From b47f1ecdf94296f8dae09ffcdaee44a183baaa10 Mon Sep 17 00:00:00 2001 From: Ronald Schaten Date: Wed, 9 Jul 2008 20:47:12 +0000 Subject: [PATCH] First CVS version --- Changelog.txt | 5 + License.txt | 346 + Makefile | 61 + USBaspLoader.2008-02-05.tar.gz | Bin 0 -> 84901 bytes bootloader/Makefile | 78 + bootloader/bootloaderconfig.h | 190 + bootloader/main.c | 299 + bootloader/usbconfig.h | 214 + bootloader/usbdrv/Changelog.txt | 200 + bootloader/usbdrv/CommercialLicense.txt | 155 + bootloader/usbdrv/License.txt | 359 + bootloader/usbdrv/Readme.txt | 154 + bootloader/usbdrv/USBID-License.txt | 143 + bootloader/usbdrv/asmcommon.inc | 178 + bootloader/usbdrv/iarcompat.h | 70 + bootloader/usbdrv/oddebug.c | 50 + bootloader/usbdrv/oddebug.h | 126 + bootloader/usbdrv/usbconfig-prototype.h | 286 + bootloader/usbdrv/usbdrv.c | 563 + bootloader/usbdrv/usbdrv.h | 678 + bootloader/usbdrv/usbdrvasm.S | 223 + bootloader/usbdrv/usbdrvasm.asm | 21 + bootloader/usbdrv/usbdrvasm12.inc | 427 + bootloader/usbdrv/usbdrvasm15.inc | 418 + bootloader/usbdrv/usbdrvasm16.inc | 337 + bootloader/usbdrv/usbdrvasm165.inc | 447 + circuit/dulcimer-drc.rpt | 8 + circuit/dulcimer.brd | 33855 ++++++++++++++++++++++ circuit/dulcimer.cache.lib | 348 + circuit/dulcimer.cmp | 206 + circuit/dulcimer.erc | 5 + circuit/dulcimer.lst | 67 + circuit/dulcimer.net | 270 + circuit/dulcimer.pro | 147 + circuit/dulcimer.sch | 1139 + circuit/dulcimer.stf | 24 + firmware/Makefile | 59 + firmware/checksize | 35 + firmware/keycodes.h | 137 + firmware/main.c | 785 + firmware/project.doxygen | 1252 + firmware/usbconfig.h | 224 + firmware/usbdrv/Changelog.txt | 135 + firmware/usbdrv/CommercialLicense.txt | 155 + firmware/usbdrv/License.txt | 359 + firmware/usbdrv/Readme.txt | 84 + firmware/usbdrv/USBID-License.txt | 143 + firmware/usbdrv/iarcompat.h | 70 + firmware/usbdrv/oddebug.c | 50 + firmware/usbdrv/oddebug.h | 126 + firmware/usbdrv/usbconfig-prototype.h | 246 + firmware/usbdrv/usbdrv.c | 552 + firmware/usbdrv/usbdrv.h | 633 + firmware/usbdrv/usbdrvasm.S | 710 + firmware/usbdrv/usbdrvasm.asm | 21 + 55 files changed, 47873 insertions(+) create mode 100644 Changelog.txt create mode 100644 License.txt create mode 100644 Makefile create mode 100644 USBaspLoader.2008-02-05.tar.gz create mode 100644 bootloader/Makefile create mode 100644 bootloader/bootloaderconfig.h create mode 100644 bootloader/main.c create mode 100644 bootloader/usbconfig.h create mode 100644 bootloader/usbdrv/Changelog.txt create mode 100644 bootloader/usbdrv/CommercialLicense.txt create mode 100644 bootloader/usbdrv/License.txt create mode 100644 bootloader/usbdrv/Readme.txt create mode 100644 bootloader/usbdrv/USBID-License.txt create mode 100644 bootloader/usbdrv/asmcommon.inc create mode 100644 bootloader/usbdrv/iarcompat.h create mode 100644 bootloader/usbdrv/oddebug.c create mode 100644 bootloader/usbdrv/oddebug.h create mode 100644 bootloader/usbdrv/usbconfig-prototype.h create mode 100644 bootloader/usbdrv/usbdrv.c create mode 100644 bootloader/usbdrv/usbdrv.h create mode 100644 bootloader/usbdrv/usbdrvasm.S create mode 100644 bootloader/usbdrv/usbdrvasm.asm create mode 100644 bootloader/usbdrv/usbdrvasm12.inc create mode 100644 bootloader/usbdrv/usbdrvasm15.inc create mode 100644 bootloader/usbdrv/usbdrvasm16.inc create mode 100644 bootloader/usbdrv/usbdrvasm165.inc create mode 100644 circuit/dulcimer-drc.rpt create mode 100644 circuit/dulcimer.brd create mode 100644 circuit/dulcimer.cache.lib create mode 100644 circuit/dulcimer.cmp create mode 100644 circuit/dulcimer.erc create mode 100644 circuit/dulcimer.lst create mode 100644 circuit/dulcimer.net create mode 100644 circuit/dulcimer.pro create mode 100644 circuit/dulcimer.sch create mode 100644 circuit/dulcimer.stf create mode 100644 firmware/Makefile create mode 100644 firmware/checksize create mode 100644 firmware/keycodes.h create mode 100644 firmware/main.c create mode 100644 firmware/project.doxygen create mode 100644 firmware/usbconfig.h create mode 100644 firmware/usbdrv/Changelog.txt create mode 100644 firmware/usbdrv/CommercialLicense.txt create mode 100644 firmware/usbdrv/License.txt create mode 100644 firmware/usbdrv/Readme.txt create mode 100644 firmware/usbdrv/USBID-License.txt create mode 100644 firmware/usbdrv/iarcompat.h create mode 100644 firmware/usbdrv/oddebug.c create mode 100644 firmware/usbdrv/oddebug.h create mode 100644 firmware/usbdrv/usbconfig-prototype.h create mode 100644 firmware/usbdrv/usbdrv.c create mode 100644 firmware/usbdrv/usbdrv.h create mode 100644 firmware/usbdrv/usbdrvasm.S create mode 100644 firmware/usbdrv/usbdrvasm.asm diff --git a/Changelog.txt b/Changelog.txt new file mode 100644 index 0000000..cb74b1a --- /dev/null +++ b/Changelog.txt @@ -0,0 +1,5 @@ +$Id: Changelog.txt,v 1.1 2008/07/09 20:47:11 rschaten Exp $ + +* Release 080709 + +- initial release diff --git a/License.txt b/License.txt new file mode 100644 index 0000000..9ba8721 --- /dev/null +++ b/License.txt @@ -0,0 +1,346 @@ +The following license applies to all but the firmware/usbdrv directories. For +that directory, please refer to the firmware/usbdrv/License.txt file for +additional license restrictions. + +------------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b439c30 --- /dev/null +++ b/Makefile @@ -0,0 +1,61 @@ +# $Id: Makefile,v 1.1 2008/07/09 20:47:11 rschaten Exp $ +# +# Creates documentation and tarball for shipping. + +TODAY=`date "+%y%m%d"` +DIR=`basename \`pwd\`` +PACKETNAME=$(DIR)_$(TODAY) + +all: usage + +usage: + @echo "Usage of this makefile:" + @echo "make docs create documentation" + @echo "make tarball packs a tarball for shipping" + @echo + @echo "For further information, consult the documentation in Readme.txt." + +# doc generation +docs: readme pdf + @echo "documentation created" + +readme: doxygen + echo "This file is auto-generated from the content of firmware/main.c" > Readme.txt + echo "You'll have more fun if you read the HTML-content in htmldoc or the PDF." >> Readme.txt + echo >> Readme.txt + lynx -dump htmldoc/main.html >> Readme.txt + +pdf: doxygen + make -C latexdoc + mv latexdoc/refman.pdf . + rm -rf latexdoc + +doxygen: + doxygen firmware/project.doxygen + +clean: + rm -rf htmldoc latexdoc Readme.txt refman.pdf + rm -f $(PACKETNAME).tar.gz + make -C firmware clean + make -C bootloader clean + +fw: + make -C firmware + mv -v firmware/main.hex firmware/main_$(TODAY).hex + +tarball: fw clean docs + @echo + @echo + @echo "I assume you updated the Changelog...? Press Enter to continue..." + @read + [ -e "firmware/main_$(TODAY).hex" ] || exit + rm --force $(PACKETNAME).tar.gz; \ + tar --directory=.. \ + --exclude=$(DIR)/Makefile \ + --exclude=CVS \ + --exclude=*.ps \ + --create \ + --gzip \ + --verbose \ + --file ../$(PACKETNAME).tar.gz $(DIR) + diff --git a/USBaspLoader.2008-02-05.tar.gz b/USBaspLoader.2008-02-05.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..1651a9bd600657e638dc4f111a5f6a6fd0a701b9 GIT binary patch literal 84901 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

H6h6pui6e-u-(n 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

m8 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 sjsH4 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≫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 YeqMfFeqMfFe$>zZ1C;3$jsPeH06Cz7c>n+a literal 0 HcmV?d00001 diff --git a/bootloader/Makefile b/bootloader/Makefile new file mode 100644 index 0000000..04fd23c --- /dev/null +++ b/bootloader/Makefile @@ -0,0 +1,78 @@ +# Configure the following variables according to your AVR. +# Program the device with +# make fuse # to set the clock generator, boot section size etc. +# make flash # to load the boot loader into flash +# make lock # to protect the boot loader from overwriting + +F_CPU = 12000000 +DEVICE = atmega32 +BOOTLOADER_ADDRESS = 0x7000 +FUSEOPT = -U lfuse:w:0xCF:m -U hfuse:w:0xD8:m +LOCKOPT = -U lock:w:0x2f:m + +# reminder, if something gets broken: default lock-byte for a mega32 is 0x3f + +PROGRAMMER = -c avrispv2 -P usb -B 10 +# PROGRAMMER contains AVRDUDE options to address your programmer + +############################################################################### + +# Tools: +AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE) +CC = avr-gcc + +# Options: +DEFINES = #-DDEBUG_LEVEL=2 +CFLAGS = -Wall -Os -I. -mmcu=$(DEVICE) -DF_CPU=$(F_CPU) $(DEFINES) +LDFLAGS = -Wl,--section-start=.text=$(BOOTLOADER_ADDRESS) + +OBJECTS = usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o + +# symbolic targets: +all: main.hex + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + +.S.o: + $(CC) $(CFLAGS) -x assembler-with-cpp -c $< -o $@ +# "-x assembler-with-cpp" should not be necessary since this is the default +# file type for the .S (with capital S) extension. However, upper case +# characters are not always preserved on Windows. To ensure WinAVR +# compatibility define the file type manually. + +.c.s: + $(CC) $(CFLAGS) -S $< -o $@ + +flash: all + $(AVRDUDE) -U flash:w:main.hex:i + +readflash: + $(AVRDUDE) -U flash:r:read.hex:i + +fuse: + $(AVRDUDE) $(FUSEOPT) + +lock: + $(AVRDUDE) $(LOCKOPT) + +read_fuses: + $(UISP) --rd_fuses + +clean: + rm -f main.hex main.bin *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s + +# file targets: +main.bin: $(OBJECTS) + $(CC) $(CFLAGS) -o main.bin $(OBJECTS) $(LDFLAGS) + +main.hex: main.bin + rm -f main.hex main.eep.hex + avr-objcopy -j .text -j .data -O ihex main.bin main.hex + avr-size main.hex + +disasm: main.bin + avr-objdump -d main.bin + +cpp: + $(CC) $(CFLAGS) -E main.c diff --git a/bootloader/bootloaderconfig.h b/bootloader/bootloaderconfig.h new file mode 100644 index 0000000..ec0618e --- /dev/null +++ b/bootloader/bootloaderconfig.h @@ -0,0 +1,190 @@ +/** + * \file bootloader/bootloaderconfig.h + * \brief This file (together with some settings in Makefile) configures the + * boot loader according to the hardware. + * + * This file contains (besides the hardware configuration normally found in + * usbconfig.h) two functions or macros: bootLoaderInit() and + * bootLoaderCondition(). Whether you implement them as macros or as static + * inline functions is up to you, decide based on code size and convenience. + * + * bootLoaderInit() is called as one of the first actions after reset. It should + * be a minimum initialization of the hardware so that the boot loader condition + * can be read. This will usually consist of activating a pull-up resistor for an + * external jumper which selects boot loader mode. + * + * bootLoaderCondition() is called immediately after initialization and in each + * main loop iteration. If it returns TRUE, the boot loader will be active. If it + * returns FALSE, the boot loader jumps to address 0 (the loaded application) + * immediately. + * + * For compatibility with Thomas Fischl's avrusbboot, we also support the macro + * names BOOTLOADER_INIT and BOOTLOADER_CONDITION for this functionality. If + * these macros are defined, the boot loader usees them. + * + * \author Ronald Schaten + * \version $Id: bootloaderconfig.h,v 1.1 2008/07/09 20:47:11 rschaten Exp $ + * + * License: GNU GPL v2 (see License.txt) + */ + +#ifndef __bootloaderconfig_h_included__ +#define __bootloaderconfig_h_included__ + +/* ---------------------------- Hardware Config ---------------------------- */ + +/** This is the port where the USB bus is connected. When you configure it to + * "B", the registers PORTB, PINB and DDRB will be used. + */ +#define USB_CFG_IOPORTNAME D +/** This is the bit number in USB_CFG_IOPORT where the USB D- line is connected. + * This may be any bit in the port. + */ +#define USB_CFG_DMINUS_BIT 0 +/** This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected. + * This may be any bit in the port. Please note that D+ must also be connected + * to interrupt pin INT0! + */ +#define USB_CFG_DPLUS_BIT 2 +/** Clock rate of the AVR in MHz. Legal values are 12000, 16000 or 16500. + * The 16.5 MHz version of the code requires no crystal, it tolerates +/- 1% + * deviation from the nominal frequency. All other rates require a precision + * of 2000 ppm and thus a crystal! + * Default if not specified: 12 MHz + */ +#define USB_CFG_CLOCK_KHZ (F_CPU/1000) + +/* ----------------------- Optional Hardware Config ------------------------ */ + +/* If you connect the 1.5k pullup resistor from D- to a port pin instead of + * V+, you can connect and disconnect the device from firmware by calling + * the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h). + * This constant defines the port on which the pullup resistor is connected. + */ +/* #define USB_CFG_PULLUP_IOPORTNAME D */ +/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined + * above) where the 1.5k pullup resistor is connected. See description + * above for details. + */ +/* #define USB_CFG_PULLUP_BIT 4 */ + +/* ------------------------------------------------------------------------- */ +/* ---------------------- feature / code size options ---------------------- */ +/* ------------------------------------------------------------------------- */ + +/** If HAVE_EEPROM_PAGED_ACCESS is defined to 1, page mode access to EEPROM is + * compiled in. Whether page mode or byte mode access is used by AVRDUDE + * depends on the target device. Page mode is only used if the device supports + * it, e.g. for the ATMega88, 168 etc. You can save quite a bit of memory by + * disabling page mode EEPROM access. Costs ~ 138 bytes. + */ +#define HAVE_EEPROM_PAGED_ACCESS 1 +/** If HAVE_EEPROM_BYTE_ACCESS is defined to 1, byte mode access to EEPROM is + * compiled in. Byte mode is only used if the device (as identified by its + * signature) does not support page mode for EEPROM. It is required for + * accessing the EEPROM on the ATMega8. Costs ~54 bytes. + */ +#define HAVE_EEPROM_BYTE_ACCESS 1 +/** If this macro is defined to 1, the boot loader will exit shortly after the + * programmer closes the connection to the device. Costs ~36 bytes. + */ +#define BOOTLOADER_CAN_EXIT 1 +/** This macro defines the signature bytes returned by the emulated USBasp to + * the programmer software. They should match the actual device at least in + * memory size and features. If you don't define this, values for ATMega8, + * ATMega88, ATMega168 and ATMega328 are guessed correctly. + */ +#define SIGNATURE_BYTES 0x1e, 0x95, 0x02, 0 /* ATMega32 */ + +/* The following block guesses feature options so that the resulting code + * should fit into 2k bytes boot block with the given device and clock rate. + * Activate by passing "-DUSE_AUTOCONFIG=1" to the compiler. + * This requires gcc 3.4.6 for small enough code size! + */ +#if USE_AUTOCONFIG +# undef HAVE_EEPROM_PAGED_ACCESS +# define HAVE_EEPROM_PAGED_ACCESS (USB_CFG_CLOCK_KHZ >= 16000) +# undef HAVE_EEPROM_BYTE_ACCESS +# define HAVE_EEPROM_BYTE_ACCESS 1 +# undef BOOTLOADER_CAN_EXIT +# define BOOTLOADER_CAN_EXIT 1 +# undef SIGNATURE_BYTES +#endif /* USE_AUTOCONFIG */ + +/* ------------------------------------------------------------------------- */ + +#ifndef __ASSEMBLER__ /* assembler cannot parse function definitions */ + +uint8_t ledcounter = 0; ///< counter used to set the speed of the running light +uint8_t ledstate = 0; ///< state of the running light + +/** + * Prepare IO-ports for detection of bootloader-condition, which happens in + * bootLoaderCondition(). + */ +static inline void bootLoaderInit(void) { + // switch on leds + DDRD |= (1 << PIND4) | (1 << PIND5) | (1 << PIND6); + PORTD &= ~((1 << PIND4) | (1 << PIND5) | (1 << PIND6)); + // choose matrix position for hotkey. we use KEY_KPminus, so we set row 13 + // and later look for pin 7 + DDRA = 0x00; + PORTA = 0xff; + DDRC = (1 << DDC2); + PORTC = ~(1 << PINC2); +} + +/** + * Clean up after boot loader action. In this case: switch off all LEDs. + */ +static inline void bootLoaderExit(void) { + // switch off leds + PORTD |= (1 << PIND4) | (1 << PIND5) | (1 << PIND6); +} + +/** + * Check if conditions for boot loader are met. This function is called in an + * endless loop, so we use our spare time to display a nice running light on + * the LEDs. + * \return 1 if bootloader should be active, 0 otherwise + */ +static inline uint8_t bootLoaderCondition() { + // look for pin 7 + if (!(PINB & (1 << PINB7))) { + // boot loader active, blink leds + _delay_ms(1); + ledcounter++; + if (ledcounter == 127) { + switch (ledstate) { + case 0: + PORTD &= ~(1 << PIND6); + PORTD |= (1 << PIND4) | (1 << PIND5); + ledstate = 1; + break; + case 1: + PORTD &= ~(1 << PIND5); + PORTD |= (1 << PIND4) | (1 << PIND6); + ledstate = 2; + break; + case 2: + PORTD &= ~(1 << PIND4); + PORTD |= (1 << PIND5) | (1 << PIND6); + ledstate = 0; + break; + default: + ledstate = 0; + } + ledcounter = 0; + } + return 1; + } else { + // no boot loader + return 0; + } +} + +#endif /* __ASSEMBLER__ */ + +/* ------------------------------------------------------------------------- */ + +#endif /* __bootloader_h_included__ */ diff --git a/bootloader/main.c b/bootloader/main.c new file mode 100644 index 0000000..669448b --- /dev/null +++ b/bootloader/main.c @@ -0,0 +1,299 @@ +/* Name: main.c + * Project: USBaspLoader + * Author: Christian Starkjohann + * Creation Date: 2007-12-08 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) + * This Revision: $Id: main.c,v 1.1 2008/07/09 20:47:11 rschaten Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static void leaveBootloader() __attribute__((__noreturn__)); + +#include "bootloaderconfig.h" +#include "usbdrv/usbdrv.c" + +/* ------------------------------------------------------------------------ */ + +/* Request constants used by USBasp */ +#define USBASP_FUNC_CONNECT 1 +#define USBASP_FUNC_DISCONNECT 2 +#define USBASP_FUNC_TRANSMIT 3 +#define USBASP_FUNC_READFLASH 4 +#define USBASP_FUNC_ENABLEPROG 5 +#define USBASP_FUNC_WRITEFLASH 6 +#define USBASP_FUNC_READEEPROM 7 +#define USBASP_FUNC_WRITEEEPROM 8 +#define USBASP_FUNC_SETLONGADDRESS 9 + +/* ------------------------------------------------------------------------ */ + +#ifndef ulong +# define ulong unsigned long +#endif +#ifndef uint +# define uint unsigned int +#endif + +/* defaults if not in config file: */ +#ifndef HAVE_EEPROM_PAGED_ACCESS +# define HAVE_EEPROM_PAGED_ACCESS 0 +#endif +#ifndef HAVE_EEPROM_BYTE_ACCESS +# define HAVE_EEPROM_BYTE_ACCESS 0 +#endif +#ifndef BOOTLOADER_CAN_EXIT +# define BOOTLOADER_CAN_EXIT 0 +#endif + +/* allow compatibility with avrusbboot's bootloaderconfig.h: */ +#ifdef BOOTLOADER_INIT +# define bootLoaderInit() BOOTLOADER_INIT +# define bootLoaderExit() +#endif +#ifdef BOOTLOADER_CONDITION +# define bootLoaderCondition() BOOTLOADER_CONDITION +#endif + +/* device compatibility: */ +#ifndef GICR /* ATMega*8 don't have GICR, use MCUCR instead */ +# define GICR MCUCR +#endif + +/* ------------------------------------------------------------------------ */ + +typedef union longConverter{ + ulong l; + uint w[2]; + uchar b[4]; +}longConverter_t; + +static uchar requestBootLoaderExit; +static longConverter_t currentAddress; /* in bytes */ +static uchar bytesRemaining; +static uchar isLastPage; +#if HAVE_EEPROM_PAGED_ACCESS +static uchar currentRequest; +#else +static const uchar currentRequest = 0; +#endif + +static const uchar signatureBytes[4] = { +#ifdef SIGNATURE_BYTES + SIGNATURE_BYTES +#elif defined (__AVR_ATmega8__) || defined (__AVR_ATmega8HVA__) + 0x1e, 0x93, 0x07, 0 +#elif defined (__AVR_ATmega48__) || defined (__AVR_ATmega48P__) + 0x1e, 0x92, 0x05, 0 +#elif defined (__AVR_ATmega88__) || defined (__AVR_ATmega88P__) + 0x1e, 0x93, 0x0a, 0 +#elif defined (__AVR_ATmega168__) || defined (__AVR_ATmega168P__) + 0x1e, 0x94, 0x06, 0 +#elif defined (__AVR_ATmega328P__) + 0x1e, 0x95, 0x0f, 0 +#elif defined (__AVR_ATmega32__) + 0x1e, 0x95, 0x02, 0 +#else +# error "Device signature is not known, please edit main.c!" +#endif +}; + +#if (FLASHEND) > 0xffff /* we need long addressing */ +# define CURRENT_ADDRESS currentAddress.l +#else +# define CURRENT_ADDRESS currentAddress.w[0] +#endif + +/* ------------------------------------------------------------------------ */ + +static void (*nullVector)(void) __attribute__((__noreturn__)); + +static void leaveBootloader() +{ + DBG1(0x01, 0, 0); + bootLoaderExit(); + cli(); + GICR = (1 << IVCE); /* enable change of interrupt vectors */ + GICR = (0 << IVSEL); /* move interrupts to application flash section */ +/* We must go through a global function pointer variable instead of writing + * ((void (*)(void))0)(); + * because the compiler optimizes a constant 0 to "rcall 0" which is not + * handled correctly by the assembler. + */ + nullVector(); +} + +/* ------------------------------------------------------------------------ */ + +uchar usbFunctionSetup(uchar data[8]) +{ +usbRequest_t *rq = (void *)data; +uchar len = 0; +static uchar replyBuffer[4]; + + usbMsgPtr = replyBuffer; + if(rq->bRequest == USBASP_FUNC_TRANSMIT){ /* emulate parts of ISP protocol */ + uchar rval = 0; + usbWord_t address; + address.bytes[1] = rq->wValue.bytes[1]; + address.bytes[0] = rq->wIndex.bytes[0]; + if(rq->wValue.bytes[0] == 0x30){ /* read signature */ + rval = rq->wIndex.bytes[0] & 3; + rval = signatureBytes[rval]; +#if HAVE_EEPROM_BYTE_ACCESS + }else if(rq->wValue.bytes[0] == 0xa0){ /* read EEPROM byte */ + rval = eeprom_read_byte((void *)address.word); + }else if(rq->wValue.bytes[0] == 0xc0){ /* write EEPROM byte */ + eeprom_write_byte((void *)address.word, rq->wIndex.bytes[1]); +#endif + }else{ + /* ignore all others, return default value == 0 */ + } + replyBuffer[3] = rval; + len = 4; + }else if(rq->bRequest == USBASP_FUNC_ENABLEPROG){ + /* replyBuffer[0] = 0; is never touched and thus always 0 which means success */ + len = 1; + }else if(rq->bRequest >= USBASP_FUNC_READFLASH && rq->bRequest <= USBASP_FUNC_SETLONGADDRESS){ + currentAddress.w[0] = rq->wValue.word; + if(rq->bRequest == USBASP_FUNC_SETLONGADDRESS){ +#if (FLASHEND) > 0xffff + currentAddress.w[1] = rq->wIndex.word; +#endif + }else{ + bytesRemaining = rq->wLength.bytes[0]; + /* if(rq->bRequest == USBASP_FUNC_WRITEFLASH) only evaluated during writeFlash anyway */ + isLastPage = rq->wIndex.bytes[1] & 0x02; +#if HAVE_EEPROM_PAGED_ACCESS + currentRequest = rq->bRequest; +#endif + len = 0xff; /* hand over to usbFunctionRead() / usbFunctionWrite() */ + } +#if BOOTLOADER_CAN_EXIT + }else if(rq->bRequest == USBASP_FUNC_DISCONNECT){ + requestBootLoaderExit = 1; /* allow proper shutdown/close of connection */ +#endif + }else{ + /* ignore: USBASP_FUNC_CONNECT */ + } + return len; +} + +uchar usbFunctionWrite(uchar *data, uchar len) +{ +uchar isLastWrite; + + DBG1(0x31, (void *)¤tAddress.l, 4); + if(len > bytesRemaining) + len = bytesRemaining; + bytesRemaining -= len; + isLastWrite = bytesRemaining == 0; + if(currentRequest >= USBASP_FUNC_READEEPROM){ + eeprom_write_block(data, (void *)currentAddress.w[0], len); + currentAddress.w[0] += len; + }else{ + char i = len; + while(i > 0){ + i -= 2; + if((currentAddress.w[0] & (SPM_PAGESIZE - 1)) == 0){ /* if page start: erase */ + DBG1(0x33, 0, 0); +#ifndef NO_FLASH_WRITE + cli(); + boot_page_erase(CURRENT_ADDRESS); /* erase page */ + sei(); + boot_spm_busy_wait(); /* wait until page is erased */ +#endif + } + DBG1(0x32, 0, 0); + cli(); + boot_page_fill(CURRENT_ADDRESS, *(short *)data); + sei(); + CURRENT_ADDRESS += 2; + data += 2; + /* write page when we cross page boundary or we have the last partial page */ + if((currentAddress.w[0] & (SPM_PAGESIZE - 1)) == 0 || (i <= 0 && isLastWrite && isLastPage)){ + DBG1(0x34, 0, 0); +#ifndef NO_FLASH_WRITE + cli(); + boot_page_write(CURRENT_ADDRESS - 2); + sei(); + boot_spm_busy_wait(); + cli(); + boot_rww_enable(); + sei(); +#endif + } + } + DBG1(0x35, (void *)¤tAddress.l, 4); + } + return isLastWrite; +} + +uchar usbFunctionRead(uchar *data, uchar len) +{ + if(len > bytesRemaining) + len = bytesRemaining; + bytesRemaining -= len; + if(currentRequest >= USBASP_FUNC_READEEPROM){ + eeprom_read_block(data, (void *)currentAddress.w[0], len); + }else{ + memcpy_P(data, (PGM_VOID_P)CURRENT_ADDRESS, len); + } + CURRENT_ADDRESS += len; + return len; +} + +/* ------------------------------------------------------------------------ */ + +static void initForUsbConnectivity(void) +{ +uchar i = 0; + + usbInit(); + /* enforce USB re-enumerate: */ + usbDeviceDisconnect(); /* do this while interrupts are disabled */ + while(--i){ /* fake USB disconnect for > 250 ms */ + wdt_reset(); + _delay_ms(1); + } + usbDeviceConnect(); + sei(); +} + +int main(void) +{ + /* initialize */ + bootLoaderInit(); + odDebugInit(); + DBG1(0x00, 0, 0); +#ifndef NO_FLASH_WRITE + GICR = (1 << IVCE); /* enable change of interrupt vectors */ + GICR = (1 << IVSEL); /* move interrupts to boot flash section */ +#endif + if(bootLoaderCondition()){ + uint i = 0; + initForUsbConnectivity(); + do{ + usbPoll(); +#if BOOTLOADER_CAN_EXIT + if(requestBootLoaderExit){ + if(--i == 0) + break; + } +#endif + }while(bootLoaderCondition()); /* main event loop */ + } + leaveBootloader(); + return 0; +} + +/* ------------------------------------------------------------------------ */ diff --git a/bootloader/usbconfig.h b/bootloader/usbconfig.h new file mode 100644 index 0000000..5d98d6d --- /dev/null +++ b/bootloader/usbconfig.h @@ -0,0 +1,214 @@ +/* Name: usbconfig.h + * Project: AVR USB driver + * Author: Christian Starkjohann + * Creation Date: 2007-12-08 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) + * This Revision: $Id: usbconfig.h,v 1.1 2008/07/09 20:47:11 rschaten Exp $ + */ + +#ifndef __usbconfig_h_included__ +#define __usbconfig_h_included__ + +/* +General Description: +This file contains the configuration options for the USB driver. + +Please note that the usbdrv contains a usbconfig-prototype.h file now. We +recommend that you use that file as a template because it will always list +the newest features and options. +*/ + +/* Fetch the hardware configuration from bootloaderconfig.h so that we have a + * single file where hardware settings are stored. + * Do not edit the functional settings below. + */ +#include "bootloaderconfig.h" + +#define USB_PUBLIC static +/* Use the define above if you #include usbdrv.c instead of linking against it. + * This technique saves a couple of bytes in flash memory. + */ + +/* --------------------------- Functional Range ---------------------------- */ + +#define USB_CFG_HAVE_INTRIN_ENDPOINT 0 +/* Define this to 1 if you want to compile a version with two endpoints: The + * default control endpoint 0 and an interrupt-in endpoint 1. + */ +#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0 +/* Define this to 1 if you want to compile a version with three endpoints: The + * default control endpoint 0, an interrupt-in endpoint 1 and an interrupt-in + * endpoint 3. You must also enable endpoint 1 above. + */ +#define USB_CFG_IMPLEMENT_HALT 0 +/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature + * for endpoint 1 (interrupt endpoint). Although you may not need this feature, + * it is required by the standard. We have made it a config option because it + * bloats the code considerably. + */ +#define USB_CFG_INTR_POLL_INTERVAL 200 +/* If you compile a version with endpoint 1 (interrupt-in), this is the poll + * interval. The value is in milliseconds and must not be less than 10 ms for + * low speed devices. + */ +#define USB_CFG_IS_SELF_POWERED 0 +/* Define this to 1 if the device has its own power supply. Set it to 0 if the + * device is powered from the USB bus. + */ +#define USB_CFG_MAX_BUS_POWER 100 +/* Set this variable to the maximum USB bus power consumption of your device. + * The value is in milliamperes. [It will be divided by two since USB + * communicates power requirements in units of 2 mA.] + */ +#define USB_CFG_IMPLEMENT_FN_WRITE 1 +/* Set this to 1 if you want usbFunctionWrite() to be called for control-out + * transfers. Set it to 0 if you don't need it and want to save a couple of + * bytes. + */ +#define USB_CFG_IMPLEMENT_FN_READ 1 +/* Set this to 1 if you need to send control replies which are generated + * "on the fly" when usbFunctionRead() is called. If you only want to send + * data from a static buffer, set it to 0 and return the data from + * usbFunctionSetup(). This saves a couple of bytes. + */ +#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0 +/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoint 1. + * You must implement the function usbFunctionWriteOut() which receives all + * interrupt/bulk data sent to endpoint 1. + */ +#define USB_CFG_HAVE_FLOWCONTROL 0 +/* Define this to 1 if you want flowcontrol over USB data. See the definition + * of the macros usbDisableAllRequests() and usbEnableAllRequests() in + * usbdrv.h. + */ + +/* -------------------------- Device Description --------------------------- */ + +#define USB_CFG_VENDOR_ID 0xc0, 0x16 /* 5824 in dec, stands for VOTI */ +/* USB vendor ID for the device, low byte first. If you have registered your + * own Vendor ID, define it here. Otherwise you use obdev's free shared + * VID/PID pair. Be sure to read USBID-License.txt for rules! + */ +#define USB_CFG_DEVICE_ID 0xdc, 0x05 /* 1500 in dec, obdev's free PID */ +/* This is the ID of the product, low byte first. It is interpreted in the + * scope of the vendor ID. If you have registered your own VID with usb.org + * or if you have licensed a PID from somebody else, define it here. Otherwise + * you use obdev's free shared VID/PID pair. Be sure to read the rules in + * USBID-License.txt! + */ +#define USB_CFG_DEVICE_VERSION 0x02, 0x01 +/* Version number of the device: Minor number first, then major number. + */ +#define USB_CFG_VENDOR_NAME 'w', 'w', 'w', '.', 'f', 'i', 's', 'c', 'h', 'l', '.', 'd', 'e' +#define USB_CFG_VENDOR_NAME_LEN 13 +/* These two values define the vendor name returned by the USB device. The name + * must be given as a list of characters under single quotes. The characters + * are interpreted as Unicode (UTF-16) entities. + * If you don't want a vendor name string, undefine these macros. + * ALWAYS define a vendor name containing your Internet domain name if you use + * obdev's free shared VID/PID pair. See the file USBID-License.txt for + * details. + */ +#define USB_CFG_DEVICE_NAME 'U', 'S', 'B', 'a', 's', 'p' +#define USB_CFG_DEVICE_NAME_LEN 6 +/* Same as above for the device name. If you don't want a device name, undefine + * the macros. See the file USBID-License.txt before you assign a name. + */ +/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */ +/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */ +/* Same as above for the serial number. If you don't want a serial number, + * undefine the macros. + * It may be useful to provide the serial number through other means than at + * compile time. See the section about descriptor properties below for how + * to fine tune control over USB descriptors such as the string descriptor + * for the serial number. + */ +#define USB_CFG_DEVICE_CLASS 0xff +#define USB_CFG_DEVICE_SUBCLASS 0 +/* See USB specification if you want to conform to an existing device class. + */ +#define USB_CFG_INTERFACE_CLASS 0 +#define USB_CFG_INTERFACE_SUBCLASS 0 +#define USB_CFG_INTERFACE_PROTOCOL 0 +/* See USB specification if you want to conform to an existing device class or + * protocol. + */ +#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 0 /* total length of report descriptor */ +/* Define this to the length of the HID report descriptor, if you implement + * an HID device. Otherwise don't define it or define it to 0. + */ + +/* ------------------- Fine Control over USB Descriptors ------------------- */ +/* If you don't want to use the driver's default USB descriptors, you can + * provide our own. These can be provided as (1) fixed length static data in + * flash memory, (2) fixed length static data in RAM or (3) dynamically at + * runtime in the function usbFunctionDescriptor(). See usbdrv.h for more + * information about this function. + * Descriptor handling is configured through the descriptor's properties. If + * no properties are defined or if they are 0, the default descriptor is used. + * Possible properties are: + * + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched + * at runtime via usbFunctionDescriptor(). + * + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found + * in static memory is in RAM, not in flash memory. + * + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash), + * the driver must know the descriptor's length. The descriptor itself is + * found at the address of a well known identifier (see below). + * List of static descriptor names (must be declared PROGMEM if in flash): + * char usbDescriptorDevice[]; + * char usbDescriptorConfiguration[]; + * char usbDescriptorHidReport[]; + * char usbDescriptorString0[]; + * int usbDescriptorStringVendor[]; + * int usbDescriptorStringDevice[]; + * int usbDescriptorStringSerialNumber[]; + * Other descriptors can't be provided statically, they must be provided + * dynamically at runtime. + * + * Descriptor properties are or-ed or added together, e.g.: + * #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18)) + * + * The following descriptors are defined: + * USB_CFG_DESCR_PROPS_DEVICE + * USB_CFG_DESCR_PROPS_CONFIGURATION + * USB_CFG_DESCR_PROPS_STRINGS + * USB_CFG_DESCR_PROPS_STRING_0 + * USB_CFG_DESCR_PROPS_STRING_VENDOR + * USB_CFG_DESCR_PROPS_STRING_PRODUCT + * USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER + * USB_CFG_DESCR_PROPS_HID + * USB_CFG_DESCR_PROPS_HID_REPORT + * USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver) + * + */ + +#define USB_CFG_DESCR_PROPS_DEVICE 0 +#define USB_CFG_DESCR_PROPS_CONFIGURATION 0 +#define USB_CFG_DESCR_PROPS_STRINGS 0 +#define USB_CFG_DESCR_PROPS_STRING_0 0 +#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 +#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 +#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 +#define USB_CFG_DESCR_PROPS_HID 0 +#define USB_CFG_DESCR_PROPS_HID_REPORT 0 +#define USB_CFG_DESCR_PROPS_UNKNOWN 0 + +/* ----------------------- Optional MCU Description ------------------------ */ + +/* The following configurations have working defaults in usbdrv.h. You + * usually don't need to set them explicitly. Only if you want to run + * the driver on a device which is not yet supported or with a compiler + * which is not fully supported (such as IAR C) or if you use a differnt + * interrupt than INT0, you may have to define some of these. + */ +/* #define USB_INTR_CFG MCUCR */ +/* #define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) */ +/* #define USB_INTR_CFG_CLR 0 */ +/* #define USB_INTR_ENABLE GIMSK */ +/* #define USB_INTR_ENABLE_BIT INT0 */ +/* #define USB_INTR_PENDING GIFR */ +/* #define USB_INTR_PENDING_BIT INTF0 */ + +#endif /* __usbconfig_h_included__ */ diff --git a/bootloader/usbdrv/Changelog.txt b/bootloader/usbdrv/Changelog.txt new file mode 100644 index 0000000..f04d08e --- /dev/null +++ b/bootloader/usbdrv/Changelog.txt @@ -0,0 +1,200 @@ +This file documents changes in the firmware-only USB driver for atmel's AVR +microcontrollers. New entries are always appended to the end of the file. +Scroll down to the bottom to see the most recent changes. + +2005-04-01: + - Implemented endpoint 1 as interrupt-in endpoint. + - Moved all configuration options to usbconfig.h which is not part of the + driver. + - Changed interface for usbVendorSetup(). + - Fixed compatibility with ATMega8 device. + - Various minor optimizations. + +2005-04-11: + - Changed interface to application: Use usbFunctionSetup(), usbFunctionRead() + and usbFunctionWrite() now. Added configuration options to choose which + of these functions to compile in. + - Assembler module delivers receive data non-inverted now. + - Made register and bit names compatible with more AVR devices. + +2005-05-03: + - Allow address of usbRxBuf on any memory page as long as the buffer does + not cross 256 byte page boundaries. + - Better device compatibility: works with Mega88 now. + - Code optimization in debugging module. + - Documentation updates. + +2006-01-02: + - Added (free) default Vendor- and Product-IDs bought from voti.nl. + - Added USBID-License.txt file which defines the rules for using the free + shared VID/PID pair. + - Added Readme.txt to the usbdrv directory which clarifies administrative + issues. + +2006-01-25: + - Added "configured state" to become more standards compliant. + - Added "HALT" state for interrupt endpoint. + - Driver passes the "USB Command Verifier" test from usb.org now. + - Made "serial number" a configuration option. + - Minor optimizations, we now recommend compiler option "-Os" for best + results. + - Added a version number to usbdrv.h + +2006-02-03: + - New configuration variable USB_BUFFER_SECTION for the memory section where + the USB rx buffer will go. This defaults to ".bss" if not defined. Since + this buffer MUST NOT cross 256 byte pages (not even touch a page at the + end), the user may want to pass a linker option similar to + "-Wl,--section-start=.mybuffer=0x800060". + - Provide structure for usbRequest_t. + - New defines for USB constants. + - Prepared for HID implementations. + - Increased data size limit for interrupt transfers to 8 bytes. + - New macro usbInterruptIsReady() to query interrupt buffer state. + +2006-02-18: + - Ensure that the data token which is sent as an ack to an OUT transfer is + always zero sized. This fixes a bug where the host reports an error after + sending an out transfer to the device, although all data arrived at the + device. + - Updated docs in usbdrv.h to reflect changed API in usbFunctionWrite(). + +* Release 2006-02-20 + + - Give a compiler warning when compiling with debugging turned on. + - Added Oleg Semyonov's changes for IAR-cc compatibility. + - Added new (optional) functions usbDeviceConnect() and usbDeviceDisconnect() + (also thanks to Oleg!). + - Rearranged tests in usbPoll() to save a couple of instructions in the most + likely case that no actions are pending. + - We need a delay between the SET ADDRESS request until the new address + becomes active. This delay was handled in usbPoll() until now. Since the + spec says that the delay must not exceed 2ms, previous versions required + aggressive polling during the enumeration phase. We have now moved the + handling of the delay into the interrupt routine. + - We must not reply with NAK to a SETUP transaction. We can only achieve this + by making sure that the rx buffer is empty when SETUP tokens are expected. + We therefore don't pass zero sized data packets from the status phase of + a transfer to usbPoll(). This change MAY cause troubles if you rely on + receiving a less than 8 bytes long packet in usbFunctionWrite() to + identify the end of a transfer. usbFunctionWrite() will NEVER be called + with a zero length. + +* Release 2006-03-14 + + - Improved IAR C support: tiny memory model, more devices + - Added template usbconfig.h file under the name usbconfig-prototype.h + +* Release 2006-03-26 + + - Added provision for one more interrupt-in endpoint (endpoint 3). + - Added provision for one interrupt-out endpoint (endpoint 1). + - Added flowcontrol macros for USB. + - Added provision for custom configuration descriptor. + - Allow ANY two port bits for D+ and D-. + - Merged (optional) receive endpoint number into global usbRxToken variable. + - Use USB_CFG_IOPORTNAME instead of USB_CFG_IOPORT. We now construct the + variable name from the single port letter instead of computing the address + of related ports from the output-port address. + +* Release 2006-06-26 + + - Updated documentation in usbdrv.h and usbconfig-prototype.h to reflect the + new features. + - Removed "#warning" directives because IAR does not understand them. Use + unused static variables instead to generate a warning. + - Do not include when compiling with IAR. + - Introduced USB_CFG_DESCR_PROPS_* in usbconfig.h to configure how each + USB descriptor should be handled. It is now possible to provide descriptor + data in Flash, RAM or dynamically at runtime. + - STALL is now a status in usbTxLen* instead of a message. We can now conform + to the spec and leave the stall status pending until it is cleared. + - Made usbTxPacketCnt1 and usbTxPacketCnt3 public. This allows the + application code to reset data toggling on interrupt pipes. + +* Release 2006-07-18 + + - Added an #if !defined __ASSEMBLER__ to the warning in usbdrv.h. This fixes + an assembler error. + - usbDeviceDisconnect() takes pull-up resistor to high impedance now. + +* Release 2007-02-01 + + - Merged in some code size improvements from usbtiny (thanks to Dick + Streefland for these optimizations!) + - Special alignment requirement for usbRxBuf not required any more. Thanks + again to Dick Streefland for this hint! + - Reverted to "#warning" instead of unused static variables -- new versions + of IAR CC should handle this directive. + - Changed Open Source license to GNU GPL v2 in order to make linking against + other free libraries easier. We no longer require publication of the + circuit diagrams, but we STRONGLY encourage it. If you improve the driver + itself, PLEASE grant us a royalty free license to your changes for our + commercial license. + +* Release 2007-03-29 + + - New configuration option "USB_PUBLIC" in usbconfig.h. + - Set USB version number to 1.10 instead of 1.01. + - Code used USB_CFG_DESCR_PROPS_STRING_DEVICE and + USB_CFG_DESCR_PROPS_STRING_PRODUCT inconsistently. Changed all occurrences + to USB_CFG_DESCR_PROPS_STRING_PRODUCT. + - New assembler module for 16.5 MHz RC oscillator clock with PLL in receiver + code. + - New assembler module for 16 MHz crystal. + - usbdrvasm.S contains common code only, clock-specific parts have been moved + to usbdrvasm12.S, usbdrvasm16.S and usbdrvasm165.S respectively. + +* Release 2007-06-25 + + - 16 MHz module: Do SE0 check in stuffed bits as well. + +* Release 2007-07-07 + + - Define hi8(x) for IAR compiler to limit result to 8 bits. This is necessary + for negative values. + - Added 15 MHz module contributed by V. Bosch. + - Interrupt vector name can now be configured. This is useful if somebody + wants to use a different hardware interrupt than INT0. + +* Release 2007-08-07 + + - Moved handleIn3 routine in usbdrvasm16.S so that relative jump range is + not exceeded. + - More config options: USB_RX_USER_HOOK(), USB_INITIAL_DATATOKEN, + USB_COUNT_SOF + - USB_INTR_PENDING can now be a memory address, not just I/O + +* Release 2007-09-19 + + - Split out common parts of assembler modules into separate include file + - Made endpoint numbers configurable so that given interface definitions + can be matched. See USB_CFG_EP3_NUMBER in usbconfig-prototype.h. + - Store endpoint number for interrupt/bulk-out so that usbFunctionWriteOut() + can handle any number of endpoints. + - Define usbDeviceConnect() and usbDeviceDisconnect() even if no + USB_CFG_PULLUP_IOPORTNAME is defined. Directly set D+ and D- to 0 in this + case. + +* Release 2007-12-01 + + - Optimize usbDeviceConnect() and usbDeviceDisconnect() for less code size + when USB_CFG_PULLUP_IOPORTNAME is not defined. + +* Release 2007-12-13 + + - Renamed all include-only assembler modules from *.S to *.inc so that + people don't add them to their project sources. + - Distribute leap bits in tx loop more evenly for 16 MHz module. + - Use "macro" and "endm" instead of ".macro" and ".endm" for IAR + - Avoid compiler warnings for constant expr range by casting some values in + USB descriptors. + +* Release 2008-01-21 + + - Fixed bug in 15 and 16 MHz module where the new address set with + SET_ADDRESS was already accepted at the next NAK or ACK we send, not at + the next data packet we send. This caused problems when the host polled + too fast. + +* Release 2008-02-05 diff --git a/bootloader/usbdrv/CommercialLicense.txt b/bootloader/usbdrv/CommercialLicense.txt new file mode 100644 index 0000000..240e45f --- /dev/null +++ b/bootloader/usbdrv/CommercialLicense.txt @@ -0,0 +1,155 @@ +AVR-USB Driver Software License Agreement +Version 2006-07-24 + +THIS LICENSE AGREEMENT GRANTS YOU CERTAIN RIGHTS IN A SOFTWARE. YOU CAN +ENTER INTO THIS AGREEMENT AND ACQUIRE THE RIGHTS OUTLINED BELOW BY PAYING +THE AMOUNT ACCORDING TO SECTION 4 ("PAYMENT") TO OBJECTIVE DEVELOPMENT. + + +1 DEFINITIONS + +1.1 "OBJECTIVE DEVELOPMENT" shall mean OBJECTIVE DEVELOPMENT Software GmbH, +Grosse Schiffgasse 1A/7, 1020 Wien, AUSTRIA. + +1.2 "You" shall mean the Licensee. + +1.3 "AVR-USB" shall mean the firmware-only USB device implementation for +Atmel AVR microcontrollers distributed by OBJECTIVE DEVELOPMENT and +consisting of the files usbdrv.c, usbdrv.h, usbdrvasm.S, oddebug.c, +oddebug.h, usbdrvasm.asm, iarcompat.h and usbconfig-prototype.h. + + +2 LICENSE GRANTS + +2.1 Source Code. OBJECTIVE DEVELOPMENT shall furnish you with the source +code of AVR-USB. + +2.2 Distribution and Use. OBJECTIVE DEVELOPMENT grants you the +non-exclusive right to use and distribute AVR-USB with your hardware +product(s), restricted by the limitations in section 3 below. + +2.3 Modifications. OBJECTIVE DEVELOPMENT grants you the right to modify +your copy of AVR-USB according to your needs. + +2.4 USB IDs. OBJECTIVE DEVELOPMENT grants you the exclusive rights to use +USB Product ID(s) sent to you in e-mail after receiving your payment in +conjunction with USB Vendor ID 5824. OBJECTIVE DEVELOPMENT has acquired an +exclusive license for this pair of USB identifiers from Wouter van Ooijen +(www.voti.nl), who has licensed the VID from the USB Implementers Forum, +Inc. (www.usb.org). + + +3 LICENSE RESTRICTIONS + +3.1 Number of Units. Only one of the following three definitions is +applicable. Which one is determined by the amount you pay to OBJECTIVE +DEVELOPMENT, see section 4 ("Payment") below. + +Hobby License: You may use AVR-USB according to section 2 above in no more +than 5 hardware units. These units must not be sold for profit. + +Entry Level License: You may use AVR-USB according to section 2 above in no +more than 150 hardware units. + +Professional License: You may use AVR-USB according to section 2 above in +any number of hardware units, except for large scale production ("unlimited +fair use"). Quantities below 10,000 units are not considered large scale +production. If your reach quantities which are obviously large scale +production, you must pay a license fee of 0.10 EUR per unit for all units +above 10,000. + +3.2 Rental. You may not rent, lease, or lend AVR-USB or otherwise encumber +any copy of AVR-USB, or any of the rights granted herein. + +3.3 Transfer. You may not transfer your rights under this Agreement to +another party without OBJECTIVE DEVELOPMENT's prior written consent. If +such consent is obtained, you may permanently transfer this License to +another party. The recipient of such transfer must agree to all terms and +conditions of this Agreement. + +3.4 Reservation of Rights. OBJECTIVE DEVELOPMENT retains all rights not +expressly granted. + +3.5 Non-Exclusive Rights. Your license rights under this Agreement are +non-exclusive. + +3.6 Third Party Rights. This Agreement cannot grant you rights controlled +by third parties. In particular, you are not allowed to use the USB logo or +other trademarks owned by the USB Implementers Forum, Inc. without their +consent. Since such consent depends on USB certification, it should be +noted that AVR-USB will not pass certification because it does not +implement checksum verification and the microcontroller ports do not meet +the electrical specifications. + + +4 PAYMENT + +The payment amount depends on the variation of this agreement (according to +section 3.1) into which you want to enter. Concrete prices are listed on +OBJECTIVE DEVELOPMENT's web site, usually at +http://www.obdev.at/avrusb/license.html. You agree to pay the amount listed +there to OBJECTIVE DEVELOPMENT or OBJECTIVE DEVELOPMENT's payment processor +or reseller. + + +5 COPYRIGHT AND OWNERSHIP + +AVR-USB is protected by copyright laws and international copyright +treaties, as well as other intellectual property laws and treaties. AVR-USB +is licensed, not sold. + + +6 TERM AND TERMINATION + +6.1 Term. This Agreement shall continue indefinitely. However, OBJECTIVE +DEVELOPMENT may terminate this Agreement and revoke the granted license and +USB-IDs if you fail to comply with any of its terms and conditions. + +6.2 Survival of Terms. All provisions regarding secrecy, confidentiality +and limitation of liability shall survive termination of this agreement. + + +7 DISCLAIMER OF WARRANTY AND LIABILITY + +LIMITED WARRANTY. AVR-USB IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, OBJECTIVE +DEVELOPMENT AND ITS SUPPLIERS HEREBY DISCLAIM ALL WARRANTIES, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND +NON-INFRINGEMENT, WITH REGARD TO AVR-USB, AND THE PROVISION OF OR FAILURE +TO PROVIDE SUPPORT SERVICES. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL +RIGHTS. YOU MAY HAVE OTHERS, WHICH VARY FROM STATE/JURISDICTION TO +STATE/JURISDICTION. + +LIMITATION OF LIABILITY. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, +IN NO EVENT SHALL OBJECTIVE DEVELOPMENT OR ITS SUPPLIERS BE LIABLE FOR ANY +SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER +(INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, +BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY +LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE AVR-USB OR THE +PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF OBJECTIVE +DEVELOPMENT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY +CASE, OBJECTIVE DEVELOPMENT'S ENTIRE LIABILITY UNDER ANY PROVISION OF THIS +AGREEMENT SHALL BE LIMITED TO THE AMOUNT ACTUALLY PAID BY YOU FOR AVR-USB. + + +8 MISCELLANEOUS TERMS + +8.1 Marketing. OBJECTIVE DEVELOPMENT has the right to mention for marketing +purposes that you entered into this agreement. + +8.2 Entire Agreement. This document represents the entire agreement between +OBJECTIVE DEVELOPMENT and you. It may only be modified in writing signed by +an authorized representative of both, OBJECTIVE DEVELOPMENT and you. + +8.3 Severability. In case a provision of these terms and conditions should +be or become partly or entirely invalid, ineffective, or not executable, +the validity of all other provisions shall not be affected. + +8.4 Applicable Law. This agreement is governed by the laws of the Republic +of Austria. + +8.5 Responsible Courts. The responsible courts in Vienna/Austria will have +exclusive jurisdiction regarding all disputes in connection with this +agreement. + diff --git a/bootloader/usbdrv/License.txt b/bootloader/usbdrv/License.txt new file mode 100644 index 0000000..f6a232e --- /dev/null +++ b/bootloader/usbdrv/License.txt @@ -0,0 +1,359 @@ +OBJECTIVE DEVELOPMENT GmbH's AVR-USB driver software is distributed under the +terms and conditions of the GNU GPL version 2, see the text below. In addition +to the requirements in the GPL, we STRONGLY ENCOURAGE you to do the following: + +(1) Publish your entire project on a web site and drop us a note with the URL. +Use the form at http://www.obdev.at/avrusb/feedback.html for your submission. + +(2) Adhere to minimum publication standards. Please include AT LEAST: + - a circuit diagram in PDF, PNG or GIF format + - full source code for the host software + - a Readme.txt file in ASCII format which describes the purpose of the + project and what can be found in which directories and which files + - a reference to http://www.obdev.at/avrusb/ + +(3) If you improve the driver firmware itself, please give us a free license +to your modifications for our commercial license offerings. + + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/bootloader/usbdrv/Readme.txt b/bootloader/usbdrv/Readme.txt new file mode 100644 index 0000000..6fb9902 --- /dev/null +++ b/bootloader/usbdrv/Readme.txt @@ -0,0 +1,154 @@ +This is the Readme file to Objective Development's firmware-only USB driver +for Atmel AVR microcontrollers. For more information please visit +http://www.obdev.at/avrusb/ + +This directory contains the USB firmware only. Copy it as-is to your own +project and add your own version of "usbconfig.h". A template for your own +"usbconfig.h" can be found in "usbconfig-prototype.h" in this directory. + + +TECHNICAL DOCUMENTATION +======================= +The technical documentation (API) for the firmware driver is contained in the +file "usbdrv.h". Please read all of it carefully! Configuration options are +documented in "usbconfig-prototype.h". + +The driver consists of the following files: + Readme.txt ............. The file you are currently reading. + Changelog.txt .......... Release notes for all versions of the driver. + usbdrv.h ............... Driver interface definitions and technical docs. +* usbdrv.c ............... High level language part of the driver. Link this + module to your code! +* usbdrvasm.S ............ Assembler part of the driver. This module is mostly + a stub and includes one of the usbdrvasm*.S files + depending on processor clock. Link this module to + your code! + usbdrvasm*.inc ......... Assembler routines for particular clock frequencies. + Included by usbdrvasm.S, don't link it directly! + asmcommon.inc .......... Common assembler routines. Included by + usbdrvasm*.inc, don't link it directly! + usbconfig-prototype.h .. Prototype for your own usbdrv.h file. +* oddebug.c .............. Debug functions. Only used when DEBUG_LEVEL is + defined to a value greater than 0. Link this module + to your code! + oddebug.h .............. Interface definitions of the debug module. + iarcompat.h ............ Compatibility definitions for IAR C-compiler. + usbdrvasm.asm .......... Compatibility stub for IAR-C-compiler. Use this + module instead of usbdrvasm.S when you assembler + with IAR's tools. + License.txt ............ Open Source license for this driver. + CommercialLicense.txt .. Optional commercial license for this driver. + USBID-License.txt ...... Terms and conditions for using particular USB ID + values for particular purposes. + +(*) ... These files should be linked to your project. + + +CPU CORE CLOCK FREQUENCY +======================== +We supply assembler modules for clock frequencies of 12 MHz, 15 MHz, 16 MHz and +16.5 MHz. Other clock rates are not supported. The actual clock rate must be +configured in usbdrv.h unless you use the default 12 MHz. + +12 MHz Clock +This is the traditional clock rate of AVR-USB because it's the lowest clock +rate where the timing constraints of the USB spec can be met. + +15 MHz Clock +Similar to 12 MHz, but some NOPs inserted. On the other hand, the higher clock +rate allows for some loops which make the resulting code size somewhat smaller +than the 12 MHz version. + +16 MHz Clock +This clock rate has been added for users of the Arduino board and other +ready-made boards which come with a fixed 16 MHz crystal. It's also an option +if you need the slightly higher clock rate for performance reasons. Since +16 MHz is not divisible by the USB low speed bit clock of 1.5 MHz, the code +is somewhat tricky and has to insert a leap cycle every third byte. + +16.5 MHz Clock +The assembler module for this clock rate differs from the other modules because +it has been built for an RC oscillator with only 1% precision. The receiver +code inserts leap cycles to compensate for clock deviations. 1% is also the +precision which can be achieved by calibrating the internal RC oscillator of +the AVR. Please note that only AVRs with internal 64 MHz PLL oscillator can be +used since the 8 MHz RC oscillator cannot be trimmed up to 16.5 MHz. This +includes the very popular ATTiny25, ATTiny45, ATTiny85 series as well as the +ATTiny26. + +We recommend that you obtain appropriate calibration values for 16.5 MHz core +clock at programming time and store it in flash or EEPROM or compute the value +from a reference clock at run time. Atmel's 8 MHz calibration is much more +precise than the guaranteed 10% and it's therefore often possible to work with +a fixed offset from this value, but it may be out of range. + + +USB IDENTIFIERS +=============== +Every USB device needs a vendor- and a product-identifier (VID and PID). VIDs +are obtained from usb.org for a price of 1,500 USD. Once you have a VID, you +can assign PIDs at will. + +Since an entry level cost of 1,500 USD is too high for most small companies +and hobbyists, we provide a single VID/PID pair for free. If you want to use +your own VID and PID instead of our's, define the macros "USB_CFG_VENDOR_ID" +and "USB_CFG_DEVICE_ID" accordingly in "usbconfig.h". + +To use our predefined VID/PID pair, you MUST conform to a couple of +requirements. See the file "USBID-License.txt" for details. + +Objective Development also has some offerings which include product IDs. See +http://www.obdev.at/avrusb/ for details. + + +HOST DRIVER +=========== +You have received this driver together with an example device implementation +and an example host driver. The host driver is based on libusb and compiles +on various Unix flavors (Linux, BSD, Mac OS X). It also compiles natively on +Windows using MinGW (see www.mingw.org) and libusb-win32 (see +libusb-win32.sourceforge.net). The "Automator" project contains a native +Windows host driver (not based on libusb) for Human Interface Devices. + + +DEVELOPMENT SYSTEM +================== +This driver has been developed and optimized for the GNU compiler version 3 +(gcc 3). It does work well with gcc 4, but with bigger code size. We recommend +that you use the GNU compiler suite because it is freely available. AVR-USB +has also been ported to the IAR compiler and assembler. It has been tested +with IAR 4.10B/W32 and 4.12A/W32 on an ATmega8 with the "small" and "tiny" +memory model. Not every release is tested with IAR CC and the driver may +therefore fail to compile with IAR. Please note that gcc is more efficient for +usbdrv.c because this module has been deliberately optimized for gcc. + + +USING AVR-USB FOR FREE +====================== +The AVR firmware driver is published under the GNU General Public License +Version 2 (GPL2). See the file "License.txt" for details. + +If you decide for the free GPL2, we STRONGLY ENCOURAGE you to do the following +things IN ADDITION to the obligations from the GPL2: + +(1) Publish your entire project on a web site and drop us a note with the URL. +Use the form at http://www.obdev.at/avrusb/feedback.html for your submission. + +(2) Adhere to minimum publication standards. Please include AT LEAST: + - a circuit diagram in PDF, PNG or GIF format + - full source code for the host software + - a Readme.txt file in ASCII format which describes the purpose of the + project and what can be found in which directories and which files + - a reference to http://www.obdev.at/avrusb/ + +(3) If you improve the driver firmware itself, please give us a free license +to your modifications for our commercial license offerings. + + +COMMERCIAL LICENSES FOR AVR-USB +=============================== +If you don't want to publish your source code under the terms of the GPL2, +you can simply pay money for AVR-USB. As an additional benefit you get +USB PIDs for free, licensed exclusively to you. See the file +"CommercialLicense.txt" for details. + diff --git a/bootloader/usbdrv/USBID-License.txt b/bootloader/usbdrv/USBID-License.txt new file mode 100644 index 0000000..4739a57 --- /dev/null +++ b/bootloader/usbdrv/USBID-License.txt @@ -0,0 +1,143 @@ +Royalty-Free Non-Exclusive License USB Product-ID +================================================= + +Version 2006-06-19 + +OBJECTIVE DEVELOPMENT Software GmbH hereby grants you the non-exclusive +right to use three USB.org vendor-ID (VID) / product-ID (PID) pairs with +products based on Objective Development's firmware-only USB driver for +Atmel AVR microcontrollers: + + * VID = 5824 (=0x16c0) / PID = 1500 (=0x5dc) for devices implementing no + USB device class (vendor-class devices with USB class = 0xff). Devices + using this pair will be referred to as "VENDOR CLASS" devices. + + * VID = 5824 (=0x16c0) / PID = 1503 (=0x5df) for HID class devices + (excluding mice and keyboards). Devices using this pair will be referred + to as "HID CLASS" devices. + + * VID = 5824 (=0x16c0) / PID = 1505 (=0x5e1) for CDC class modem devices + Devices using this pair will be referred to as "CDC-ACM CLASS" devices. + +Since the granted right is non-exclusive, the same VID/PID pairs may be +used by many companies and individuals for different products. To avoid +conflicts, your device and host driver software MUST adhere to the rules +outlined below. + +OBJECTIVE DEVELOPMENT Software GmbH has licensed these VID/PID pairs from +Wouter van Ooijen (see www.voti.nl), who has licensed the VID from the USB +Implementers Forum, Inc. (see www.usb.org). The VID is registered for the +company name "Van Ooijen Technische Informatica". + + +RULES AND RESTRICTIONS +====================== + +(1) The USB device MUST provide a textual representation of the +manufacturer and product identification. The manufacturer identification +MUST be available at least in USB language 0x0409 (English/US). + +(2) The textual manufacturer identification MUST contain either an Internet +domain name (e.g. "mycompany.com") registered and owned by you, or an +e-mail address under your control (e.g. "myname@gmx.net"). You can embed +the domain name or e-mail address in any string you like, e.g. "Objective +Development http://www.obdev.at/avrusb/". + +(3) You are responsible for retaining ownership of the domain or e-mail +address for as long as any of your products are in use. + +(4) You may choose any string for the textual product identification, as +long as this string is unique within the scope of your textual manufacturer +identification. + +(5) Matching of device-specific drivers MUST be based on the textual +manufacturer and product identification in addition to the usual VID/PID +matching. This means that operating system features which are based on +VID/PID matching only (e.g. Windows kernel level drivers, automatic actions +when the device is plugged in etc) MUST NOT be used. The driver matching +MUST be a comparison of the entire strings, NOT a sub-string match. For +CDC-ACM CLASS devices, a generic class driver should be used and the +matching is based on the USB device class. + +(6) The extent to which VID/PID matching is allowed for non device-specific +drivers or features depends on the operating system and particular VID/PID +pair used: + + * Mac OS X, Linux, FreeBSD and other Unixes: No VID/PID matching is + required and hence no VID/PID-only matching is allowed at all. + + * Windows: The operating system performs VID/PID matching for the kernel + level driver. You are REQUIRED to use libusb-win32 (see + http://libusb-win32.sourceforge.net/) as the kernel level driver for + VENDOR CLASS devices. HID CLASS devices all use the generic HID class + driver shipped with Windows, except mice and keyboards. You therefore + MUST NOT use any of the shared VID/PID pairs for mice or keyboards. + CDC-ACM CLASS devices require a ".inf" file which matches on the VID/PID + pair. This ".inf" file MUST load the "usbser" driver to configure the + device as modem (COM-port). + +(7) OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any +problems which are caused by the shared use of these VID/PID pairs. You +have been warned that the sharing of VID/PID pairs may cause problems. If +you want to avoid them, get your own VID/PID pair for exclusive use. + + +HOW TO IMPLEMENT THESE RULES +============================ + +The following rules are for VENDOR CLASS and HID CLASS devices. CDC-ACM +CLASS devices use the operating system's class driver and don't need a +custom driver. + +The host driver MUST iterate over all devices with the given VID/PID +numbers in their device descriptors and query the string representation for +the manufacturer name in USB language 0x0409 (English/US). It MUST compare +the ENTIRE string with your textual manufacturer identification chosen in +(2) above. A substring search for your domain or e-mail address is NOT +acceptable. The driver MUST NOT touch the device (other than querying the +descriptors) unless the strings match. + +For all USB devices with matching VID/PID and textual manufacturer +identification, the host driver must query the textual product +identification and string-compare it with the name of the product it can +control. It may only initialize the device if the product matches exactly. + +Objective Development provides examples for these matching rules with the +"PowerSwitch" project (using libusb) and with the "Automator" project +(using Windows calls on Windows and libusb on Unix). + + +Technical Notes: +================ + +Sharing the same VID/PID pair among devices is possible as long as ALL +drivers which match the VID/PID also perform matching on the textual +identification strings. This is easy on all operating systems except +Windows, since Windows establishes a static connection between the VID/PID +pair and a kernel level driver. All devices with the same VID/PID pair must +therefore use THE SAME kernel level driver. + +We therefore demand that you use libusb-win32 for VENDOR CLASS devices. +This is a generic kernel level driver which allows all types of USB access +for user space applications. This is only a partial solution of the +problem, though, because different device drivers may come with different +versions of libusb-win32 and they may not work with the libusb version of +the respective other driver. You are therefore encouraged to test your +driver against a broad range of libusb-win32 versions. Do not use new +features in new versions, or check for their existence before you use them. +When a new libusb-win32 becomes available, make sure that your driver is +compatible with it. + +For HID CLASS devices it is necessary that all those devices bind to the +same kernel driver: Microsoft's generic USB HID driver. This is true for +all HID devices except those with a specialized driver. Currently, the only +HIDs with specialized drivers are mice and keyboards. You therefore MUST +NOT use a shared VID/PID with mouse and keyboard devices. + +Sharing the same VID/PID among different products is unusual and probably +violates the USB specification. If you do it, you do it at your own risk. + +To avoid possible incompatibilities, we highly recommend that you get your +own VID/PID pair if you intend to sell your product. Objective +Development's commercial licenses for AVR-USB include a PID for +unrestricted exclusive use. diff --git a/bootloader/usbdrv/asmcommon.inc b/bootloader/usbdrv/asmcommon.inc new file mode 100644 index 0000000..a61e157 --- /dev/null +++ b/bootloader/usbdrv/asmcommon.inc @@ -0,0 +1,178 @@ +/* Name: asmcommon.inc + * Project: AVR USB driver + * Author: Christian Starkjohann + * Creation Date: 2007-11-05 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) + * Revision: $Id: asmcommon.inc,v 1.1 2008/07/09 20:47:11 rschaten Exp $ + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file contains assembler code which is shared among the USB driver +implementations for different CPU cocks. Since the code must be inserted +in the middle of the module, it's split out into this file and #included. + +Jump destinations called from outside: + sofError: Called when no start sequence was found. + se0: Called when a package has been successfully received. + overflow: Called when receive buffer overflows. + doReturn: Called after sending data. + +Outside jump destinations used by this module: + waitForJ: Called to receive an already arriving packet. + sendAckAndReti: + sendNakAndReti: + sendCntAndReti: + usbSendAndReti: + +The following macros must be defined before this file is included: + .macro POP_STANDARD + .endm + .macro POP_RETI + .endm +*/ + +#define token x1 + +overflow: + ldi x2, 1< +#ifndef __IAR_SYSTEMS_ASM__ +# include +#endif + +#define __attribute__(arg) +#define IAR_SECTION(section) @ section + +#ifndef USB_BUFFER_SECTION +# define USB_BUFFER_SECTION "TINY_Z" /* if user has not selected a named section */ +#endif + +#ifdef __IAR_SYSTEMS_ASM__ +# define __ASSEMBLER__ +#endif + +#ifdef __HAS_ELPM__ +# define PROGMEM __farflash +#else +# define PROGMEM __flash +#endif + +#define PRG_RDB(addr) (*(PROGMEM char *)(addr)) + +/* The following definitions are not needed by the driver, but may be of some + * help if you port a gcc based project to IAR. + */ +#define cli() __disable_interrupt() +#define sei() __enable_interrupt() +#define wdt_reset() __watchdog_reset() + +/* Depending on the device you use, you may get problems with the way usbdrv.h + * handles the differences between devices. Since IAR does not use #defines + * for MCU registers, we can't check for the existence of a particular + * register with an #ifdef. If the autodetection mechanism fails, include + * definitions for the required USB_INTR_* macros in your usbconfig.h. See + * usbconfig-prototype.h and usbdrv.h for details. + */ + +#endif /* defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ */ +#endif /* __iarcompat_h_INCLUDED__ */ diff --git a/bootloader/usbdrv/oddebug.c b/bootloader/usbdrv/oddebug.c new file mode 100644 index 0000000..85c2d7a --- /dev/null +++ b/bootloader/usbdrv/oddebug.c @@ -0,0 +1,50 @@ +/* Name: oddebug.c + * Project: AVR library + * Author: Christian Starkjohann + * Creation Date: 2005-01-16 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) + * This Revision: $Id: oddebug.c,v 1.1 2008/07/09 20:47:11 rschaten Exp $ + */ + +#include "oddebug.h" + +#if DEBUG_LEVEL > 0 + +#warning "Never compile production devices with debugging enabled" + +static void uartPutc(char c) +{ + while(!(ODDBG_USR & (1 << ODDBG_UDRE))); /* wait for data register empty */ + ODDBG_UDR = c; +} + +static uchar hexAscii(uchar h) +{ + h &= 0xf; + if(h >= 10) + h += 'a' - (uchar)10 - '0'; + h += '0'; + return h; +} + +static void printHex(uchar c) +{ + uartPutc(hexAscii(c >> 4)); + uartPutc(hexAscii(c)); +} + +void odDebug(uchar prefix, uchar *data, uchar len) +{ + printHex(prefix); + uartPutc(':'); + while(len--){ + uartPutc(' '); + printHex(*data++); + } + uartPutc('\r'); + uartPutc('\n'); +} + +#endif diff --git a/bootloader/usbdrv/oddebug.h b/bootloader/usbdrv/oddebug.h new file mode 100644 index 0000000..8c87dd2 --- /dev/null +++ b/bootloader/usbdrv/oddebug.h @@ -0,0 +1,126 @@ +/* Name: oddebug.h + * Project: AVR library + * Author: Christian Starkjohann + * Creation Date: 2005-01-16 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) + * This Revision: $Id: oddebug.h,v 1.1 2008/07/09 20:47:11 rschaten Exp $ + */ + +#ifndef __oddebug_h_included__ +#define __oddebug_h_included__ + +/* +General Description: +This module implements a function for debug logs on the serial line of the +AVR microcontroller. Debugging can be configured with the define +'DEBUG_LEVEL'. If this macro is not defined or defined to 0, all debugging +calls are no-ops. If it is 1, DBG1 logs will appear, but not DBG2. If it is +2, DBG1 and DBG2 logs will be printed. + +A debug log consists of a label ('prefix') to indicate which debug log created +the output and a memory block to dump in hex ('data' and 'len'). +*/ + + +#ifndef F_CPU +# define F_CPU 12000000 /* 12 MHz */ +#endif + +/* make sure we have the UART defines: */ +#include "iarcompat.h" +#ifndef __IAR_SYSTEMS_ICC__ +# include +#endif + +#ifndef uchar +# define uchar unsigned char +#endif + +#if DEBUG_LEVEL > 0 && !(defined TXEN || defined TXEN0) /* no UART in device */ +# warning "Debugging disabled because device has no UART" +# undef DEBUG_LEVEL +#endif + +#ifndef DEBUG_LEVEL +# define DEBUG_LEVEL 0 +#endif + +/* ------------------------------------------------------------------------- */ + +#if DEBUG_LEVEL > 0 +# define DBG1(prefix, data, len) odDebug(prefix, data, len) +#else +# define DBG1(prefix, data, len) +#endif + +#if DEBUG_LEVEL > 1 +# define DBG2(prefix, data, len) odDebug(prefix, data, len) +#else +# define DBG2(prefix, data, len) +#endif + +/* ------------------------------------------------------------------------- */ + +#if DEBUG_LEVEL > 0 +extern void odDebug(uchar prefix, uchar *data, uchar len); + +/* Try to find our control registers; ATMEL likes to rename these */ + +#if defined UBRR +# define ODDBG_UBRR UBRR +#elif defined UBRRL +# define ODDBG_UBRR UBRRL +#elif defined UBRR0 +# define ODDBG_UBRR UBRR0 +#elif defined UBRR0L +# define ODDBG_UBRR UBRR0L +#endif + +#if defined UCR +# define ODDBG_UCR UCR +#elif defined UCSRB +# define ODDBG_UCR UCSRB +#elif defined UCSR0B +# define ODDBG_UCR UCSR0B +#endif + +#if defined TXEN +# define ODDBG_TXEN TXEN +#else +# define ODDBG_TXEN TXEN0 +#endif + +#if defined USR +# define ODDBG_USR USR +#elif defined UCSRA +# define ODDBG_USR UCSRA +#elif defined UCSR0A +# define ODDBG_USR UCSR0A +#endif + +#if defined UDRE +# define ODDBG_UDRE UDRE +#else +# define ODDBG_UDRE UDRE0 +#endif + +#if defined UDR +# define ODDBG_UDR UDR +#elif defined UDR0 +# define ODDBG_UDR UDR0 +#endif + +static inline void odDebugInit(void) +{ + ODDBG_UCR |= (1< +# include +#endif +#include "usbdrv.h" +#include "oddebug.h" + +/* +General Description: +This module implements the C-part of the USB driver. See usbdrv.h for a +documentation of the entire driver. +*/ + +#ifndef IAR_SECTION +#define IAR_SECTION(arg) +#define __no_init +#endif +/* The macro IAR_SECTION is a hack to allow IAR-cc compatibility. On gcc, it + * is defined to nothing. __no_init is required on IAR. + */ + +/* ------------------------------------------------------------------------- */ + +/* raw USB registers / interface to assembler code: */ +uchar usbRxBuf[2*USB_BUFSIZE]; /* raw RX buffer: PID, 8 bytes data, 2 bytes CRC */ +uchar usbInputBufOffset; /* offset in usbRxBuf used for low level receiving */ +uchar usbDeviceAddr; /* assigned during enumeration, defaults to 0 */ +uchar usbNewDeviceAddr; /* device ID which should be set after status phase */ +uchar usbConfiguration; /* currently selected configuration. Administered by driver, but not used */ +volatile schar usbRxLen; /* = 0; number of bytes in usbRxBuf; 0 means free, -1 for flow control */ +uchar usbCurrentTok; /* last token received or endpoint number for last OUT token if != 0 */ +uchar usbRxToken; /* token for data we received; or endpont number for last OUT */ +uchar usbMsgLen = 0xff; /* remaining number of bytes, no msg to send if -1 (see usbMsgPtr) */ +volatile uchar usbTxLen = USBPID_NAK; /* number of bytes to transmit with next IN token or handshake token */ +uchar usbTxBuf[USB_BUFSIZE];/* data to transmit with next IN, free if usbTxLen contains handshake token */ +# if USB_COUNT_SOF +volatile uchar usbSofCount; /* incremented by assembler module every SOF */ +# endif +#if USB_CFG_HAVE_INTRIN_ENDPOINT +volatile uchar usbTxLen1 = USBPID_NAK; /* TX count for endpoint 1 */ +uchar usbTxBuf1[USB_BUFSIZE]; /* TX data for endpoint 1 */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 +volatile uchar usbTxLen3 = USBPID_NAK; /* TX count for endpoint 3 */ +uchar usbTxBuf3[USB_BUFSIZE]; /* TX data for endpoint 3 */ +#endif +#endif + +/* USB status registers / not shared with asm code */ +uchar *usbMsgPtr; /* data to transmit next -- ROM or RAM address */ +static uchar usbMsgFlags; /* flag values see below */ + +#define USB_FLG_TX_PACKET (1<<0) +/* Leave free 6 bits after TX_PACKET. This way we can increment usbMsgFlags to toggle TX_PACKET */ +#define USB_FLG_MSGPTR_IS_ROM (1<<6) +#define USB_FLG_USE_DEFAULT_RW (1<<7) + +/* +optimizing hints: +- do not post/pre inc/dec integer values in operations +- assign value of PRG_RDB() to register variables and don't use side effects in arg +- use narrow scope for variables which should be in X/Y/Z register +- assign char sized expressions to variables to force 8 bit arithmetics +*/ + +/* ------------------------------------------------------------------------- */ + +#if USB_CFG_DESCR_PROPS_STRINGS == 0 + +#if USB_CFG_DESCR_PROPS_STRING_0 == 0 +#undef USB_CFG_DESCR_PROPS_STRING_0 +#define USB_CFG_DESCR_PROPS_STRING_0 sizeof(usbDescriptorString0) +PROGMEM char usbDescriptorString0[] = { /* language descriptor */ + 4, /* sizeof(usbDescriptorString0): length of descriptor in bytes */ + 3, /* descriptor type */ + 0x09, 0x04, /* language index (0x0409 = US-English) */ +}; +#endif + +#if USB_CFG_DESCR_PROPS_STRING_VENDOR == 0 && USB_CFG_VENDOR_NAME_LEN +#undef USB_CFG_DESCR_PROPS_STRING_VENDOR +#define USB_CFG_DESCR_PROPS_STRING_VENDOR sizeof(usbDescriptorStringVendor) +PROGMEM int usbDescriptorStringVendor[] = { + USB_STRING_DESCRIPTOR_HEADER(USB_CFG_VENDOR_NAME_LEN), + USB_CFG_VENDOR_NAME +}; +#endif + +#if USB_CFG_DESCR_PROPS_STRING_PRODUCT == 0 && USB_CFG_DEVICE_NAME_LEN +#undef USB_CFG_DESCR_PROPS_STRING_PRODUCT +#define USB_CFG_DESCR_PROPS_STRING_PRODUCT sizeof(usbDescriptorStringDevice) +PROGMEM int usbDescriptorStringDevice[] = { + USB_STRING_DESCRIPTOR_HEADER(USB_CFG_DEVICE_NAME_LEN), + USB_CFG_DEVICE_NAME +}; +#endif + +#if USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER == 0 && USB_CFG_SERIAL_NUMBER_LEN +#undef USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER +#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER sizeof(usbDescriptorStringSerialNumber) +PROGMEM int usbDescriptorStringSerialNumber[] = { + USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LEN), + USB_CFG_SERIAL_NUMBER +}; +#endif + +#endif /* USB_CFG_DESCR_PROPS_STRINGS == 0 */ + +#if USB_CFG_DESCR_PROPS_DEVICE == 0 +#undef USB_CFG_DESCR_PROPS_DEVICE +#define USB_CFG_DESCR_PROPS_DEVICE sizeof(usbDescriptorDevice) +PROGMEM char usbDescriptorDevice[] = { /* USB device descriptor */ + 18, /* sizeof(usbDescriptorDevice): length of descriptor in bytes */ + USBDESCR_DEVICE, /* descriptor type */ + 0x10, 0x01, /* USB version supported */ + USB_CFG_DEVICE_CLASS, + USB_CFG_DEVICE_SUBCLASS, + 0, /* protocol */ + 8, /* max packet size */ + /* the following two casts affect the first byte of the constant only, but + * that's sufficient to avoid a warning with the default values. + */ + (char)USB_CFG_VENDOR_ID,/* 2 bytes */ + (char)USB_CFG_DEVICE_ID,/* 2 bytes */ + USB_CFG_DEVICE_VERSION, /* 2 bytes */ + USB_CFG_DESCR_PROPS_STRING_VENDOR != 0 ? 1 : 0, /* manufacturer string index */ + USB_CFG_DESCR_PROPS_STRING_PRODUCT != 0 ? 2 : 0, /* product string index */ + USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER != 0 ? 3 : 0, /* serial number string index */ + 1, /* number of configurations */ +}; +#endif + +#if USB_CFG_DESCR_PROPS_HID_REPORT != 0 && USB_CFG_DESCR_PROPS_HID == 0 +#undef USB_CFG_DESCR_PROPS_HID +#define USB_CFG_DESCR_PROPS_HID 9 /* length of HID descriptor in config descriptor below */ +#endif + +#if USB_CFG_DESCR_PROPS_CONFIGURATION == 0 +#undef USB_CFG_DESCR_PROPS_CONFIGURATION +#define USB_CFG_DESCR_PROPS_CONFIGURATION sizeof(usbDescriptorConfiguration) +PROGMEM char usbDescriptorConfiguration[] = { /* USB configuration descriptor */ + 9, /* sizeof(usbDescriptorConfiguration): length of descriptor in bytes */ + USBDESCR_CONFIG, /* descriptor type */ + 18 + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT + (USB_CFG_DESCR_PROPS_HID & 0xff), 0, + /* total length of data returned (including inlined descriptors) */ + 1, /* number of interfaces in this configuration */ + 1, /* index of this configuration */ + 0, /* configuration name string index */ +#if USB_CFG_IS_SELF_POWERED + USBATTR_SELFPOWER, /* attributes */ +#else + (char)USBATTR_BUSPOWER, /* attributes */ +#endif + USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */ +/* interface descriptor follows inline: */ + 9, /* sizeof(usbDescrInterface): length of descriptor in bytes */ + USBDESCR_INTERFACE, /* descriptor type */ + 0, /* index of this interface */ + 0, /* alternate setting for this interface */ + USB_CFG_HAVE_INTRIN_ENDPOINT, /* endpoints excl 0: number of endpoint descriptors to follow */ + USB_CFG_INTERFACE_CLASS, + USB_CFG_INTERFACE_SUBCLASS, + USB_CFG_INTERFACE_PROTOCOL, + 0, /* string index for interface */ +#if (USB_CFG_DESCR_PROPS_HID & 0xff) /* HID descriptor */ + 9, /* sizeof(usbDescrHID): length of descriptor in bytes */ + USBDESCR_HID, /* descriptor type: HID */ + 0x01, 0x01, /* BCD representation of HID version */ + 0x00, /* target country code */ + 0x01, /* number of HID Report (or other HID class) Descriptor infos to follow */ + 0x22, /* descriptor type: report */ + USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH, 0, /* total length of report descriptor */ +#endif +#if USB_CFG_HAVE_INTRIN_ENDPOINT /* endpoint descriptor for endpoint 1 */ + 7, /* sizeof(usbDescrEndpoint) */ + USBDESCR_ENDPOINT, /* descriptor type = endpoint */ + (char)0x81, /* IN endpoint number 1 */ + 0x03, /* attrib: Interrupt endpoint */ + 8, 0, /* maximum packet size */ + USB_CFG_INTR_POLL_INTERVAL, /* in ms */ +#endif +}; +#endif + +/* We don't use prog_int or prog_int16_t for compatibility with various libc + * versions. Here's an other compatibility hack: + */ +#ifndef PRG_RDB +#define PRG_RDB(addr) pgm_read_byte(addr) +#endif + +typedef union{ + unsigned word; + uchar *ptr; + uchar bytes[2]; +}converter_t; +/* We use this union to do type conversions. This is better optimized than + * type casts in gcc 3.4.3 and much better than using bit shifts to build + * ints from chars. Byte ordering is not a problem on an 8 bit platform. + */ + +/* ------------------------------------------------------------------------- */ + +#if USB_CFG_HAVE_INTRIN_ENDPOINT +USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len) +{ +uchar *p, i; + +#if USB_CFG_IMPLEMENT_HALT + if(usbTxLen1 == USBPID_STALL) + return; +#endif +#if 0 /* No runtime checks! Caller is responsible for valid data! */ + if(len > 8) /* interrupt transfers are limited to 8 bytes */ + len = 8; +#endif + if(usbTxLen1 & 0x10){ /* packet buffer was empty */ + usbTxBuf1[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ + }else{ + usbTxLen1 = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ + } + p = usbTxBuf1 + 1; + for(i=len;i--;) + *p++ = *data++; + usbCrc16Append(&usbTxBuf1[1], len); + usbTxLen1 = len + 4; /* len must be given including sync byte */ + DBG2(0x21, usbTxBuf1, len + 3); +} +#endif + +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 +USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len) +{ +uchar *p, i; + + if(usbTxLen3 & 0x10){ /* packet buffer was empty */ + usbTxBuf3[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ + }else{ + usbTxLen3 = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ + } + p = usbTxBuf3 + 1; + for(i=len;i--;) + *p++ = *data++; + usbCrc16Append(&usbTxBuf3[1], len); + usbTxLen3 = len + 4; /* len must be given including sync byte */ + DBG2(0x23, usbTxBuf3, len + 3); +} +#endif + + +static uchar usbRead(uchar *data, uchar len) +{ +#if USB_CFG_IMPLEMENT_FN_READ + if(usbMsgFlags & USB_FLG_USE_DEFAULT_RW){ +#endif + uchar i = len, *r = usbMsgPtr; + if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){ /* ROM data */ + while(i--){ + uchar c = PRG_RDB(r); /* assign to char size variable to enforce byte ops */ + *data++ = c; + r++; + } + }else{ /* RAM data */ + while(i--) + *data++ = *r++; + } + usbMsgPtr = r; + return len; +#if USB_CFG_IMPLEMENT_FN_READ + }else{ + if(len != 0) /* don't bother app with 0 sized reads */ + return usbFunctionRead(data, len); + return 0; + } +#endif +} + + +#define GET_DESCRIPTOR(cfgProp, staticName) \ + if(cfgProp){ \ + if((cfgProp) & USB_PROP_IS_RAM) \ + flags &= ~USB_FLG_MSGPTR_IS_ROM; \ + if((cfgProp) & USB_PROP_IS_DYNAMIC){ \ + replyLen = usbFunctionDescriptor(rq); \ + }else{ \ + replyData = (uchar *)(staticName); \ + SET_REPLY_LEN((cfgProp) & 0xff); \ + } \ + } +/* We use if() instead of #if in the macro above because #if can't be used + * in macros and the compiler optimizes constant conditions anyway. + */ + + +/* Don't make this function static to avoid inlining. + * The entire function would become too large and exceed the range of + * relative jumps. + * 2006-02-25: Either gcc 3.4.3 is better than the gcc used when the comment + * above was written, or other parts of the code have changed. We now get + * better results with an inlined function. Test condition: PowerSwitch code. + */ +static void usbProcessRx(uchar *data, uchar len) +{ +usbRequest_t *rq = (void *)data; +uchar replyLen = 0, flags = USB_FLG_USE_DEFAULT_RW; +/* We use if() cascades because the compare is done byte-wise while switch() + * is int-based. The if() cascades are therefore more efficient. + */ +/* usbRxToken can be: + * 0x2d 00101101 (USBPID_SETUP for endpoint 0) + * 0xe1 11100001 (USBPID_OUT for endpoint 0) + * 0xff 11111111 (USBPID_OUT for endpoint 1) + */ + DBG2(0x10 + ((usbRxToken >> 1) & 3), data, len); /* SETUP0=12; OUT0=10; OUT1=13 */ +#ifdef USB_RX_USER_HOOK + USB_RX_USER_HOOK(data, len) +#endif +#if USB_CFG_IMPLEMENT_FN_WRITEOUT + if(usbRxToken < 0x10){ /* endpoint number in usbRxToken */ + usbFunctionWriteOut(data, len); + return; /* no reply expected, hence no usbMsgPtr, usbMsgFlags, usbMsgLen set */ + } +#endif + if(usbRxToken == (uchar)USBPID_SETUP){ + usbTxLen = USBPID_NAK; /* abort pending transmit */ + if(len == 8){ /* Setup size must be always 8 bytes. Ignore otherwise. */ + uchar type = rq->bmRequestType & USBRQ_TYPE_MASK; + if(type == USBRQ_TYPE_STANDARD){ + #define SET_REPLY_LEN(len) replyLen = (len); usbMsgPtr = replyData + /* This macro ensures that replyLen and usbMsgPtr are always set in the same way. + * That allows optimization of common code in if() branches */ + uchar *replyData = usbTxBuf + 9; /* there is 3 bytes free space at the end of the buffer */ + replyData[0] = 0; /* common to USBRQ_GET_STATUS and USBRQ_GET_INTERFACE */ + if(rq->bRequest == USBRQ_GET_STATUS){ /* 0 */ + uchar __attribute__((__unused__)) recipient = rq->bmRequestType & USBRQ_RCPT_MASK; /* assign arith ops to variables to enforce byte size */ +#if USB_CFG_IS_SELF_POWERED + if(recipient == USBRQ_RCPT_DEVICE) + replyData[0] = USB_CFG_IS_SELF_POWERED; +#endif +#if USB_CFG_HAVE_INTRIN_ENDPOINT && USB_CFG_IMPLEMENT_HALT + if(recipient == USBRQ_RCPT_ENDPOINT && rq->wIndex.bytes[0] == 0x81) /* request status for endpoint 1 */ + replyData[0] = usbTxLen1 == USBPID_STALL; +#endif + replyData[1] = 0; + SET_REPLY_LEN(2); + }else if(rq->bRequest == USBRQ_SET_ADDRESS){ /* 5 */ + usbNewDeviceAddr = rq->wValue.bytes[0]; + }else if(rq->bRequest == USBRQ_GET_DESCRIPTOR){ /* 6 */ + flags = USB_FLG_MSGPTR_IS_ROM | USB_FLG_USE_DEFAULT_RW; + if(rq->wValue.bytes[1] == USBDESCR_DEVICE){ /* 1 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_DEVICE, usbDescriptorDevice) + }else if(rq->wValue.bytes[1] == USBDESCR_CONFIG){ /* 2 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_CONFIGURATION, usbDescriptorConfiguration) + }else if(rq->wValue.bytes[1] == USBDESCR_STRING){ /* 3 */ +#if USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC + if(USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_RAM) + flags &= ~USB_FLG_MSGPTR_IS_ROM; + replyLen = usbFunctionDescriptor(rq); +#else /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ + if(rq->wValue.bytes[0] == 0){ /* descriptor index */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_0, usbDescriptorString0) + }else if(rq->wValue.bytes[0] == 1){ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_VENDOR, usbDescriptorStringVendor) + }else if(rq->wValue.bytes[0] == 2){ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_PRODUCT, usbDescriptorStringDevice) + }else if(rq->wValue.bytes[0] == 3){ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER, usbDescriptorStringSerialNumber) + }else if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ + replyLen = usbFunctionDescriptor(rq); + } +#endif /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ +#if USB_CFG_DESCR_PROPS_HID_REPORT /* only support HID descriptors if enabled */ + }else if(rq->wValue.bytes[1] == USBDESCR_HID){ /* 0x21 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID, usbDescriptorConfiguration + 18) + }else if(rq->wValue.bytes[1] == USBDESCR_HID_REPORT){ /* 0x22 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID_REPORT, usbDescriptorHidReport) +#endif /* USB_CFG_DESCR_PROPS_HID_REPORT */ + }else if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ + replyLen = usbFunctionDescriptor(rq); + } + }else if(rq->bRequest == USBRQ_GET_CONFIGURATION){ /* 8 */ + replyData = &usbConfiguration; /* send current configuration value */ + SET_REPLY_LEN(1); + }else if(rq->bRequest == USBRQ_SET_CONFIGURATION){ /* 9 */ + usbConfiguration = rq->wValue.bytes[0]; +#if USB_CFG_IMPLEMENT_HALT + usbTxLen1 = USBPID_NAK; +#endif + }else if(rq->bRequest == USBRQ_GET_INTERFACE){ /* 10 */ + SET_REPLY_LEN(1); +#if USB_CFG_HAVE_INTRIN_ENDPOINT + }else if(rq->bRequest == USBRQ_SET_INTERFACE){ /* 11 */ + USB_SET_DATATOKEN1(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ +# if USB_CFG_HAVE_INTRIN_ENDPOINT3 + USB_SET_DATATOKEN3(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ +# endif +# if USB_CFG_IMPLEMENT_HALT + usbTxLen1 = USBPID_NAK; + }else if(rq->bRequest == USBRQ_CLEAR_FEATURE || rq->bRequest == USBRQ_SET_FEATURE){ /* 1|3 */ + if(rq->wValue.bytes[0] == 0 && rq->wIndex.bytes[0] == 0x81){ /* feature 0 == HALT for endpoint == 1 */ + usbTxLen1 = rq->bRequest == USBRQ_CLEAR_FEATURE ? USBPID_NAK : USBPID_STALL; + USB_SET_DATATOKEN1(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ +# if USB_CFG_HAVE_INTRIN_ENDPOINT3 + USB_SET_DATATOKEN3(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ +# endif + } +# endif +#endif + }else{ + /* the following requests can be ignored, send default reply */ + /* 1: CLEAR_FEATURE, 3: SET_FEATURE, 7: SET_DESCRIPTOR */ + /* 12: SYNCH_FRAME */ + } + #undef SET_REPLY_LEN + }else{ /* not a standard request -- must be vendor or class request */ + replyLen = usbFunctionSetup(data); + } +#if USB_CFG_IMPLEMENT_FN_READ || USB_CFG_IMPLEMENT_FN_WRITE + if(replyLen == 0xff){ /* use user-supplied read/write function */ + if((rq->bmRequestType & USBRQ_DIR_MASK) == USBRQ_DIR_DEVICE_TO_HOST){ + replyLen = rq->wLength.bytes[0]; /* IN transfers only */ + } + flags &= ~USB_FLG_USE_DEFAULT_RW; /* we have no valid msg, use user supplied read/write functions */ + }else /* The 'else' prevents that we limit a replyLen of 0xff to the maximum transfer len. */ +#endif + if(!rq->wLength.bytes[1] && replyLen > rq->wLength.bytes[0]) /* limit length to max */ + replyLen = rq->wLength.bytes[0]; + } + /* make sure that data packets which are sent as ACK to an OUT transfer are always zero sized */ + }else{ /* DATA packet from out request */ +#if USB_CFG_IMPLEMENT_FN_WRITE + if(!(usbMsgFlags & USB_FLG_USE_DEFAULT_RW)){ + uchar rval = usbFunctionWrite(data, len); + replyLen = 0xff; + if(rval == 0xff){ /* an error occurred */ + usbMsgLen = 0xff; /* cancel potentially pending data packet for ACK */ + usbTxLen = USBPID_STALL; + }else if(rval != 0){ /* This was the final package */ + replyLen = 0; /* answer with a zero-sized data packet */ + } + flags = 0; /* start with a DATA1 package, stay with user supplied write() function */ + } +#endif + } + usbMsgFlags = flags; + usbMsgLen = replyLen; +} + +/* ------------------------------------------------------------------------- */ + +static void usbBuildTxBlock(void) +{ +uchar wantLen, len, txLen, token; + + wantLen = usbMsgLen; + if(wantLen > 8) + wantLen = 8; + usbMsgLen -= wantLen; + token = USBPID_DATA1; + if(usbMsgFlags & USB_FLG_TX_PACKET) + token = USBPID_DATA0; + usbMsgFlags++; + len = usbRead(usbTxBuf + 1, wantLen); + if(len <= 8){ /* valid data packet */ + usbCrc16Append(&usbTxBuf[1], len); + txLen = len + 4; /* length including sync byte */ + if(len < 8) /* a partial package identifies end of message */ + usbMsgLen = 0xff; + }else{ + txLen = USBPID_STALL; /* stall the endpoint */ + usbMsgLen = 0xff; + } + usbTxBuf[0] = token; + usbTxLen = txLen; + DBG2(0x20, usbTxBuf, txLen-1); +} + +static inline uchar isNotSE0(void) +{ +uchar rval; +/* We want to do + * return (USBIN & USBMASK); + * here, but the compiler does int-expansion acrobatics. + * We can avoid this by assigning to a char-sized variable. + */ + rval = USBIN & USBMASK; + return rval; +} + +/* ------------------------------------------------------------------------- */ + +USB_PUBLIC void usbPoll(void) +{ +schar len; +uchar i; + + if((len = usbRxLen) > 0){ +/* We could check CRC16 here -- but ACK has already been sent anyway. If you + * need data integrity checks with this driver, check the CRC in your app + * code and report errors back to the host. Since the ACK was already sent, + * retries must be handled on application level. + * unsigned crc = usbCrc16(buffer + 1, usbRxLen - 3); + */ + usbProcessRx(usbRxBuf + USB_BUFSIZE + 1 - usbInputBufOffset, len - 3); +#if USB_CFG_HAVE_FLOWCONTROL + if(usbRxLen > 0) /* only mark as available if not inactivated */ + usbRxLen = 0; +#else + usbRxLen = 0; /* mark rx buffer as available */ +#endif + } + if(usbTxLen & 0x10){ /* transmit system idle */ + if(usbMsgLen != 0xff){ /* transmit data pending? */ + usbBuildTxBlock(); + } + } + for(i = 10; i > 0; i--){ + if(isNotSE0()) + break; + } + if(i == 0){ /* RESET condition, called multiple times during reset */ + usbNewDeviceAddr = 0; + usbDeviceAddr = 0; +#if USB_CFG_IMPLEMENT_HALT + usbTxLen1 = USBPID_NAK; +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 + usbTxLen3 = USBPID_NAK; +#endif +#endif + DBG1(0xff, 0, 0); + } +} + +/* ------------------------------------------------------------------------- */ + +USB_PUBLIC void usbInit(void) +{ +#if USB_INTR_CFG_SET != 0 + USB_INTR_CFG |= USB_INTR_CFG_SET; +#endif +#if USB_INTR_CFG_CLR != 0 + USB_INTR_CFG &= ~(USB_INTR_CFG_CLR); +#endif + USB_INTR_ENABLE |= (1 << USB_INTR_ENABLE_BIT); +#if USB_CFG_HAVE_INTRIN_ENDPOINT + USB_SET_DATATOKEN1(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ +# if USB_CFG_HAVE_INTRIN_ENDPOINT3 + USB_SET_DATATOKEN3(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ +# endif +#endif +} + +/* ------------------------------------------------------------------------- */ diff --git a/bootloader/usbdrv/usbdrv.h b/bootloader/usbdrv/usbdrv.h new file mode 100644 index 0000000..261281d --- /dev/null +++ b/bootloader/usbdrv/usbdrv.h @@ -0,0 +1,678 @@ +/* Name: usbdrv.h + * Project: AVR USB driver + * Author: Christian Starkjohann + * Creation Date: 2004-12-29 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) + * This Revision: $Id: usbdrv.h,v 1.1 2008/07/09 20:47:11 rschaten Exp $ + */ + +#ifndef __usbdrv_h_included__ +#define __usbdrv_h_included__ +#include "usbconfig.h" +#include "iarcompat.h" + +/* +Hardware Prerequisites: +======================= +USB lines D+ and D- MUST be wired to the same I/O port. We recommend that D+ +triggers the interrupt (best achieved by using INT0 for D+), but it is also +possible to trigger the interrupt from D-. If D- is used, interrupts are also +triggered by SOF packets. D- requires a pull-up of 1.5k to +3.5V (and the +device must be powered at 3.5V) to identify as low-speed USB device. A +pull-down or pull-up of 1M SHOULD be connected from D+ to +3.5V to prevent +interference when no USB master is connected. If you use Zener diodes to limit +the voltage on D+ and D-, you MUST use a pull-down resistor, not a pull-up. +We use D+ as interrupt source and not D- because it does not trigger on +keep-alive and RESET states. If you want to count keep-alive events with +USB_COUNT_SOF, you MUST use D- as an interrupt source. + +As a compile time option, the 1.5k pull-up resistor on D- can be made +switchable to allow the device to disconnect at will. See the definition of +usbDeviceConnect() and usbDeviceDisconnect() further down in this file. + +Please adapt the values in usbconfig.h according to your hardware! + +The device MUST be clocked at exactly 12 MHz, 15 MHz or 16 MHz +or at 16.5 MHz +/- 1%. See usbconfig-prototype.h for details. + + +Limitations: +============ +Robustness with respect to communication errors: +The driver assumes error-free communication. It DOES check for errors in +the PID, but does NOT check bit stuffing errors, SE0 in middle of a byte, +token CRC (5 bit) and data CRC (16 bit). CRC checks can not be performed due +to timing constraints: We must start sending a reply within 7 bit times. +Bit stuffing and misplaced SE0 would have to be checked in real-time, but CPU +performance does not permit that. The driver does not check Data0/Data1 +toggling, but application software can implement the check. + +Input characteristics: +Since no differential receiver circuit is used, electrical interference +robustness may suffer. The driver samples only one of the data lines with +an ordinary I/O pin's input characteristics. However, since this is only a +low speed USB implementation and the specification allows for 8 times the +bit rate over the same hardware, we should be on the safe side. Even the spec +requires detection of asymmetric states at high bit rate for SE0 detection. + +Number of endpoints: +The driver supports the following endpoints: + +- Endpoint 0, the default control endpoint. +- Any number of interrupt- or bulk-out endpoints. The data is sent to + usbFunctionWriteOut() and USB_CFG_IMPLEMENT_FN_WRITEOUT must be defined + to 1 to activate this feature. The endpoint number can be found in the + global variable 'usbRxToken'. +- One default interrupt- or bulk-in endpoint. This endpoint is used for + interrupt- or bulk-in transfers which are not handled by any other endpoint. + You must define USB_CFG_HAVE_INTRIN_ENDPOINT in order to activate this + feature and call usbSetInterrupt() to send interrupt/bulk data. +- One additional interrupt- or bulk-in endpoint. This was endpoint 3 in + previous versions of this driver but can now be configured to any endpoint + number. You must define USB_CFG_HAVE_INTRIN_ENDPOINT3 in order to activate + this feature and call usbSetInterrupt3() to send interrupt/bulk data. The + endpoint number can be set with USB_CFG_EP3_NUMBER. + +Please note that the USB standard forbids bulk endpoints for low speed devices! +Most operating systems allow them anyway, but the AVR will spend 90% of the CPU +time in the USB interrupt polling for bulk data. + +Maximum data payload: +Data payload of control in and out transfers may be up to 254 bytes. In order +to accept payload data of out transfers, you need to implement +'usbFunctionWrite()'. + +USB Suspend Mode supply current: +The USB standard limits power consumption to 500uA when the bus is in suspend +mode. This is not a problem for self-powered devices since they don't need +bus power anyway. Bus-powered devices can achieve this only by putting the +CPU in sleep mode. The driver does not implement suspend handling by itself. +However, the application may implement activity monitoring and wakeup from +sleep. The host sends regular SE0 states on the bus to keep it active. These +SE0 states can be detected by using D- as the interrupt source. Define +USB_COUNT_SOF to 1 and use the global variable usbSofCount to check for bus +activity. + +Operation without an USB master: +The driver behaves neutral without connection to an USB master if D- reads +as 1. To avoid spurious interrupts, we recommend a high impedance (e.g. 1M) +pull-down or pull-up resistor on D+ (interrupt). If Zener diodes are used, +use a pull-down. If D- becomes statically 0, the driver may block in the +interrupt routine. + +Interrupt latency: +The application must ensure that the USB interrupt is not disabled for more +than 25 cycles (this is for 12 MHz, faster clocks allow longer latency). +This implies that all interrupt routines must either be declared as "INTERRUPT" +instead of "SIGNAL" (see "avr/signal.h") or that they are written in assembler +with "sei" as the first instruction. + +Maximum interrupt duration / CPU cycle consumption: +The driver handles all USB communication during the interrupt service +routine. The routine will not return before an entire USB message is received +and the reply is sent. This may be up to ca. 1200 cycles @ 12 MHz (= 100us) if +the host conforms to the standard. The driver will consume CPU cycles for all +USB messages, even if they address another (low-speed) device on the same bus. + +*/ + +/* ------------------------------------------------------------------------- */ +/* --------------------------- Module Interface ---------------------------- */ +/* ------------------------------------------------------------------------- */ + +#define USBDRV_VERSION 20080205 +/* This define uniquely identifies a driver version. It is a decimal number + * constructed from the driver's release date in the form YYYYMMDD. If the + * driver's behavior or interface changes, you can use this constant to + * distinguish versions. If it is not defined, the driver's release date is + * older than 2006-01-25. + */ + + +#ifndef USB_PUBLIC +#define USB_PUBLIC +#endif +/* USB_PUBLIC is used as declaration attribute for all functions exported by + * the USB driver. The default is no attribute (see above). You may define it + * to static either in usbconfig.h or from the command line if you include + * usbdrv.c instead of linking against it. Including the C module of the driver + * directly in your code saves a couple of bytes in flash memory. + */ + +#ifndef __ASSEMBLER__ +#ifndef uchar +#define uchar unsigned char +#endif +#ifndef schar +#define schar signed char +#endif +/* shortcuts for well defined 8 bit integer types */ + +struct usbRequest; /* forward declaration */ + +USB_PUBLIC void usbInit(void); +/* This function must be called before interrupts are enabled and the main + * loop is entered. + */ +USB_PUBLIC void usbPoll(void); +/* This function must be called at regular intervals from the main loop. + * Maximum delay between calls is somewhat less than 50ms (USB timeout for + * accepting a Setup message). Otherwise the device will not be recognized. + * Please note that debug outputs through the UART take ~ 0.5ms per byte + * at 19200 bps. + */ +extern uchar *usbMsgPtr; +/* This variable may be used to pass transmit data to the driver from the + * implementation of usbFunctionWrite(). It is also used internally by the + * driver for standard control requests. + */ +USB_PUBLIC uchar usbFunctionSetup(uchar data[8]); +/* This function is called when the driver receives a SETUP transaction from + * the host which is not answered by the driver itself (in practice: class and + * vendor requests). All control transfers start with a SETUP transaction where + * the host communicates the parameters of the following (optional) data + * transfer. The SETUP data is available in the 'data' parameter which can + * (and should) be casted to 'usbRequest_t *' for a more user-friendly access + * to parameters. + * + * If the SETUP indicates a control-in transfer, you should provide the + * requested data to the driver. There are two ways to transfer this data: + * (1) Set the global pointer 'usbMsgPtr' to the base of the static RAM data + * block and return the length of the data in 'usbFunctionSetup()'. The driver + * will handle the rest. Or (2) return 0xff in 'usbFunctionSetup()'. The driver + * will then call 'usbFunctionRead()' when data is needed. See the + * documentation for usbFunctionRead() for details. + * + * If the SETUP indicates a control-out transfer, the only way to receive the + * data from the host is through the 'usbFunctionWrite()' call. If you + * implement this function, you must return 0xff in 'usbFunctionSetup()' to + * indicate that 'usbFunctionWrite()' should be used. See the documentation of + * this function for more information. If you just want to ignore the data sent + * by the host, return 0 in 'usbFunctionSetup()'. + * + * Note that calls to the functions usbFunctionRead() and usbFunctionWrite() + * are only done if enabled by the configuration in usbconfig.h. + */ +USB_PUBLIC uchar usbFunctionDescriptor(struct usbRequest *rq); +/* You need to implement this function ONLY if you provide USB descriptors at + * runtime (which is an expert feature). It is very similar to + * usbFunctionSetup() above, but it is called only to request USB descriptor + * data. See the documentation of usbFunctionSetup() above for more info. + */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT +USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len); +/* This function sets the message which will be sent during the next interrupt + * IN transfer. The message is copied to an internal buffer and must not exceed + * a length of 8 bytes. The message may be 0 bytes long just to indicate the + * interrupt status to the host. + * If you need to transfer more bytes, use a control read after the interrupt. + */ +extern volatile uchar usbTxLen1; +#define usbInterruptIsReady() (usbTxLen1 & 0x10) +/* This macro indicates whether the last interrupt message has already been + * sent. If you set a new interrupt message before the old was sent, the + * message already buffered will be lost. + */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 +USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len); +extern volatile uchar usbTxLen3; +#define usbInterruptIsReady3() (usbTxLen3 & 0x10) +/* Same as above for endpoint 3 */ +#endif +#endif /* USB_CFG_HAVE_INTRIN_ENDPOINT */ +#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH /* simplified interface for backward compatibility */ +#define usbHidReportDescriptor usbDescriptorHidReport +/* should be declared as: PROGMEM char usbHidReportDescriptor[]; */ +/* If you implement an HID device, you need to provide a report descriptor. + * The HID report descriptor syntax is a bit complex. If you understand how + * report descriptors are constructed, we recommend that you use the HID + * Descriptor Tool from usb.org, see http://www.usb.org/developers/hidpage/. + * Otherwise you should probably start with a working example. + */ +#endif /* USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH */ +#if USB_CFG_IMPLEMENT_FN_WRITE +USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len); +/* This function is called by the driver to provide a control transfer's + * payload data (control-out). It is called in chunks of up to 8 bytes. The + * total count provided in the current control transfer can be obtained from + * the 'length' property in the setup data. If an error occurred during + * processing, return 0xff (== -1). The driver will answer the entire transfer + * with a STALL token in this case. If you have received the entire payload + * successfully, return 1. If you expect more data, return 0. If you don't + * know whether the host will send more data (you should know, the total is + * provided in the usbFunctionSetup() call!), return 1. + * NOTE: If you return 0xff for STALL, 'usbFunctionWrite()' may still be called + * for the remaining data. You must continue to return 0xff for STALL in these + * calls. + * In order to get usbFunctionWrite() called, define USB_CFG_IMPLEMENT_FN_WRITE + * to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. + */ +#endif /* USB_CFG_IMPLEMENT_FN_WRITE */ +#if USB_CFG_IMPLEMENT_FN_READ +USB_PUBLIC uchar usbFunctionRead(uchar *data, uchar len); +/* This function is called by the driver to ask the application for a control + * transfer's payload data (control-in). It is called in chunks of up to 8 + * bytes each. You should copy the data to the location given by 'data' and + * return the actual number of bytes copied. If you return less than requested, + * the control-in transfer is terminated. If you return 0xff, the driver aborts + * the transfer with a STALL token. + * In order to get usbFunctionRead() called, define USB_CFG_IMPLEMENT_FN_READ + * to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. + */ +#endif /* USB_CFG_IMPLEMENT_FN_READ */ +#if USB_CFG_IMPLEMENT_FN_WRITEOUT +USB_PUBLIC void usbFunctionWriteOut(uchar *data, uchar len); +/* This function is called by the driver when data on interrupt-out or bulk- + * out endpoint 1 is received. You must define USB_CFG_IMPLEMENT_FN_WRITEOUT + * to 1 in usbconfig.h to get this function called. + */ +#endif /* USB_CFG_IMPLEMENT_FN_WRITEOUT */ +#ifdef USB_CFG_PULLUP_IOPORTNAME +#define usbDeviceConnect() ((USB_PULLUP_DDR |= (1<device, 1=device->host + * t ..... type: 0=standard, 1=class, 2=vendor, 3=reserved + * r ..... recipient: 0=device, 1=interface, 2=endpoint, 3=other + */ + +/* USB setup recipient values */ +#define USBRQ_RCPT_MASK 0x1f +#define USBRQ_RCPT_DEVICE 0 +#define USBRQ_RCPT_INTERFACE 1 +#define USBRQ_RCPT_ENDPOINT 2 + +/* USB request type values */ +#define USBRQ_TYPE_MASK 0x60 +#define USBRQ_TYPE_STANDARD (0<<5) +#define USBRQ_TYPE_CLASS (1<<5) +#define USBRQ_TYPE_VENDOR (2<<5) + +/* USB direction values: */ +#define USBRQ_DIR_MASK 0x80 +#define USBRQ_DIR_HOST_TO_DEVICE (0<<7) +#define USBRQ_DIR_DEVICE_TO_HOST (1<<7) + +/* USB Standard Requests */ +#define USBRQ_GET_STATUS 0 +#define USBRQ_CLEAR_FEATURE 1 +#define USBRQ_SET_FEATURE 3 +#define USBRQ_SET_ADDRESS 5 +#define USBRQ_GET_DESCRIPTOR 6 +#define USBRQ_SET_DESCRIPTOR 7 +#define USBRQ_GET_CONFIGURATION 8 +#define USBRQ_SET_CONFIGURATION 9 +#define USBRQ_GET_INTERFACE 10 +#define USBRQ_SET_INTERFACE 11 +#define USBRQ_SYNCH_FRAME 12 + +/* USB descriptor constants */ +#define USBDESCR_DEVICE 1 +#define USBDESCR_CONFIG 2 +#define USBDESCR_STRING 3 +#define USBDESCR_INTERFACE 4 +#define USBDESCR_ENDPOINT 5 +#define USBDESCR_HID 0x21 +#define USBDESCR_HID_REPORT 0x22 +#define USBDESCR_HID_PHYS 0x23 + +#define USBATTR_BUSPOWER 0x80 +#define USBATTR_SELFPOWER 0x40 +#define USBATTR_REMOTEWAKE 0x20 + +/* USB HID Requests */ +#define USBRQ_HID_GET_REPORT 0x01 +#define USBRQ_HID_GET_IDLE 0x02 +#define USBRQ_HID_GET_PROTOCOL 0x03 +#define USBRQ_HID_SET_REPORT 0x09 +#define USBRQ_HID_SET_IDLE 0x0a +#define USBRQ_HID_SET_PROTOCOL 0x0b + +/* ------------------------------------------------------------------------- */ + +#endif /* __usbdrv_h_included__ */ diff --git a/bootloader/usbdrv/usbdrvasm.S b/bootloader/usbdrv/usbdrvasm.S new file mode 100644 index 0000000..d9d637e --- /dev/null +++ b/bootloader/usbdrv/usbdrvasm.S @@ -0,0 +1,223 @@ +/* Name: usbdrvasm.S + * Project: AVR USB driver + * Author: Christian Starkjohann + * Creation Date: 2007-06-13 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) + * Revision: $Id: usbdrvasm.S,v 1.1 2008/07/09 20:47:11 rschaten Exp $ + */ + +/* +General Description: +This module is the assembler part of the USB driver. This file contains +general code (preprocessor acrobatics and CRC computation) and then includes +the file appropriate for the given clock rate. +*/ + +#include "iarcompat.h" +#ifndef __IAR_SYSTEMS_ASM__ + /* configs for io.h */ +# define __SFR_OFFSET 0 +# define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ +# include /* for CPU I/O register definitions and vectors */ +# define macro .macro /* GNU Assembler macro definition */ +# define endm .endm /* End of GNU Assembler macro definition */ +#endif /* __IAR_SYSTEMS_ASM__ */ +#include "usbdrv.h" /* for common defs */ + +/* register names */ +#define x1 r16 +#define x2 r17 +#define shift r18 +#define cnt r19 +#define x3 r20 +#define x4 r21 +#define bitcnt r22 +#define phase x4 +#define leap x4 + +/* Some assembler dependent definitions and declarations: */ + +#ifdef __IAR_SYSTEMS_ASM__ + +# define nop2 rjmp $+2 /* jump to next instruction */ +# define XL r26 +# define XH r27 +# define YL r28 +# define YH r29 +# define ZL r30 +# define ZH r31 +# define lo8(x) LOW(x) +# define hi8(x) (((x)>>8) & 0xff) /* not HIGH to allow XLINK to make a proper range check */ + + extern usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBufOffset + extern usbCurrentTok, usbRxLen, usbRxToken, usbTxLen + extern usbTxBuf, usbMsgLen, usbTxLen1, usbTxBuf1, usbTxLen3, usbTxBuf3 +# if USB_COUNT_SOF + extern usbSofCount +# endif + public usbCrc16 + public usbCrc16Append + + COMMON INTVEC +# ifndef USB_INTR_VECTOR + ORG INT0_vect +# else /* USB_INTR_VECTOR */ + ORG USB_INTR_VECTOR +# undef USB_INTR_VECTOR +# endif /* USB_INTR_VECTOR */ +# define USB_INTR_VECTOR usbInterruptHandler + rjmp USB_INTR_VECTOR + RSEG CODE + +#else /* __IAR_SYSTEMS_ASM__ */ + +# define nop2 rjmp .+0 /* jump to next instruction */ + +# ifndef USB_INTR_VECTOR /* default to hardware interrupt INT0 */ +# define USB_INTR_VECTOR SIG_INTERRUPT0 +# endif + .text + .global USB_INTR_VECTOR + .type USB_INTR_VECTOR, @function + .global usbCrc16 + .global usbCrc16Append +#endif /* __IAR_SYSTEMS_ASM__ */ + + +#if USB_INTR_PENDING < 0x40 /* This is an I/O address, use in and out */ +# define USB_LOAD_PENDING(reg) in reg, USB_INTR_PENDING +# define USB_STORE_PENDING(reg) out USB_INTR_PENDING, reg +#else /* It's a memory address, use lds and sts */ +# define USB_LOAD_PENDING(reg) lds reg, USB_INTR_PENDING +# define USB_STORE_PENDING(reg) sts USB_INTR_PENDING, reg +#endif + + +;---------------------------------------------------------------------------- +; Utility functions +;---------------------------------------------------------------------------- + +#ifdef __IAR_SYSTEMS_ASM__ +/* Register assignments for usbCrc16 on IAR cc */ +/* Calling conventions on IAR: + * First parameter passed in r16/r17, second in r18/r19 and so on. + * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) + * Result is passed in r16/r17 + * In case of the "tiny" memory model, pointers are only 8 bit with no + * padding. We therefore pass argument 1 as "16 bit unsigned". + */ +RTMODEL "__rt_version", "3" +/* The line above will generate an error if cc calling conventions change. + * The value "3" above is valid for IAR 4.10B/W32 + */ +# define argLen r18 /* argument 2 */ +# define argPtrL r16 /* argument 1 */ +# define argPtrH r17 /* argument 1 */ + +# define resCrcL r16 /* result */ +# define resCrcH r17 /* result */ + +# define ptrL ZL +# define ptrH ZH +# define ptr Z +# define byte r22 +# define bitCnt r19 +# define polyL r20 +# define polyH r21 +# define scratch r23 + +#else /* __IAR_SYSTEMS_ASM__ */ +/* Register assignments for usbCrc16 on gcc */ +/* Calling conventions on gcc: + * First parameter passed in r24/r25, second in r22/23 and so on. + * Callee must preserve r1-r17, r28/r29 + * Result is passed in r24/r25 + */ +# define argLen r22 /* argument 2 */ +# define argPtrL r24 /* argument 1 */ +# define argPtrH r25 /* argument 1 */ + +# define resCrcL r24 /* result */ +# define resCrcH r25 /* result */ + +# define ptrL XL +# define ptrH XH +# define ptr x +# define byte r18 +# define bitCnt r19 +# define polyL r20 +# define polyH r21 +# define scratch r23 + +#endif + +; extern unsigned usbCrc16(unsigned char *data, unsigned char len); +; data: r24/25 +; len: r22 +; temp variables: +; r18: data byte +; r19: bit counter +; r20/21: polynomial +; r23: scratch +; r24/25: crc-sum +; r26/27=X: ptr +usbCrc16: + mov ptrL, argPtrL + mov ptrH, argPtrH + ldi resCrcL, 0xff + ldi resCrcH, 0xff + ldi polyL, lo8(0xa001) + ldi polyH, hi8(0xa001) +crcByteLoop: + subi argLen, 1 + brcs crcReady + ld byte, ptr+ + ldi bitCnt, 8 +crcBitLoop: + mov scratch, byte + eor scratch, resCrcL + lsr resCrcH + ror resCrcL + lsr byte + sbrs scratch, 0 + rjmp crcNoXor + eor resCrcL, polyL + eor resCrcH, polyH +crcNoXor: + dec bitCnt + brne crcBitLoop + rjmp crcByteLoop +crcReady: + com resCrcL + com resCrcH + ret + +; extern unsigned usbCrc16Append(unsigned char *data, unsigned char len); +usbCrc16Append: + rcall usbCrc16 + st ptr+, resCrcL + st ptr+, resCrcH + ret + + +;---------------------------------------------------------------------------- +; Now include the clock rate specific code +;---------------------------------------------------------------------------- + +#ifndef USB_CFG_CLOCK_KHZ +# define USB_CFG_CLOCK_KHZ 12000 +#endif + +#if USB_CFG_CLOCK_KHZ == 12000 +# include "usbdrvasm12.inc" +#elif USB_CFG_CLOCK_KHZ == 15000 +# include "usbdrvasm15.inc" +#elif USB_CFG_CLOCK_KHZ == 16000 +# include "usbdrvasm16.inc" +#elif USB_CFG_CLOCK_KHZ == 16500 +# include "usbdrvasm165.inc" +#else +# error "USB_CFG_CLOCK_KHZ is not one of the supported rates!" +#endif diff --git a/bootloader/usbdrv/usbdrvasm.asm b/bootloader/usbdrv/usbdrvasm.asm new file mode 100644 index 0000000..c675f7e --- /dev/null +++ b/bootloader/usbdrv/usbdrvasm.asm @@ -0,0 +1,21 @@ +/* Name: usbdrvasm.asm + * Project: AVR USB driver + * Author: Christian Starkjohann + * Creation Date: 2006-03-01 + * Tabsize: 4 + * Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) + * This Revision: $Id: usbdrvasm.asm,v 1.1 2008/07/09 20:47:11 rschaten Exp $ + */ + +/* +General Description: +The IAR compiler/assembler system prefers assembler files with file extension +".asm". We simply provide this file as an alias for usbdrvasm.S. + +Thanks to Oleg Semyonov for his help with the IAR tools port! +*/ + +#include "usbdrvasm.S" + +end diff --git a/bootloader/usbdrv/usbdrvasm12.inc b/bootloader/usbdrv/usbdrvasm12.inc new file mode 100644 index 0000000..e48f353 --- /dev/null +++ b/bootloader/usbdrv/usbdrvasm12.inc @@ -0,0 +1,427 @@ +/* Name: usbdrvasm12.inc + * Project: AVR USB driver + * Author: Christian Starkjohann + * Creation Date: 2004-12-29 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) + * This Revision: $Id: usbdrvasm12.inc,v 1.1 2008/07/09 20:47:11 rschaten Exp $ + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file is the 12 MHz version of the asssembler part of the USB driver. It +requires a 12 MHz crystal (not a ceramic resonator and not a calibrated RC +oscillator). + +See usbdrv.h for a description of the entire driver. + +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! + + +Timing constraints according to spec (in bit times): +timing subject min max CPUcycles +--------------------------------------------------------------------------- +EOP of OUT/SETUP to sync pattern of DATA0 (both rx) 2 16 16-128 +EOP of IN to sync pattern of DATA0 (rx, then tx) 2 7.5 16-60 +DATAx (rx) to ACK/NAK/STALL (tx) 2 7.5 16-60 +*/ + +;Software-receiver engine. Strict timing! Don't change unless you can preserve timing! +;interrupt response time: 4 cycles + insn running = 7 max if interrupts always enabled +;max allowable interrupt latency: 34 cycles -> max 25 cycles interrupt disable +;max stack usage: [ret(2), YL, SREG, YH, shift, x1, x2, x3, cnt, x4] = 11 bytes +;Numbers in brackets are maximum cycles since SOF. +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt + push YL ;2 [35] push only what is necessary to sync with edge ASAP + in YL, SREG ;1 [37] + push YL ;2 [39] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;first part has no timeout because it waits for IDLE or SE1 (== disconnected) +waitForJ: + sbis USBIN, USBMINUS ;1 [40] wait for D- == 1 + rjmp waitForJ ;2 +waitForK: +;The following code results in a sampling window of 1/4 bit which meets the spec. + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ + rjmp sofError +foundK: +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push YH ;2 [2] + lds YL, usbInputBufOffset;2 [4] + clr YH ;1 [5] + subi YL, lo8(-(usbRxBuf));1 [6] + sbci YH, hi8(-(usbRxBuf));1 [7] + + sbis USBIN, USBMINUS ;1 [8] we want two bits K [sample 1 cycle too early] + rjmp haveTwoBitsK ;2 [10] + pop YH ;2 [11] undo the push from before + rjmp waitForK ;2 [13] this was not the end of sync, retry +haveTwoBitsK: +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- + push shift ;2 [16] + push x1 ;2 [12] + push x2 ;2 [14] + + in x1, USBIN ;1 [17] <-- sample bit 0 + ldi shift, 0xff ;1 [18] + bst x1, USBMINUS ;1 [19] + bld shift, 0 ;1 [20] + push x3 ;2 [22] + push cnt ;2 [24] + + in x2, USBIN ;1 [25] <-- sample bit 1 + ser x3 ;1 [26] [inserted init instruction] + eor x1, x2 ;1 [27] + bst x1, USBMINUS ;1 [28] + bld shift, 1 ;1 [29] + ldi cnt, USB_BUFSIZE;1 [30] [inserted init instruction] + rjmp rxbit2 ;2 [32] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- + +unstuff0: ;1 (branch taken) + andi x3, ~0x01 ;1 [15] + mov x1, x2 ;1 [16] x2 contains last sampled (stuffed) bit + in x2, USBIN ;1 [17] <-- sample bit 1 again + ori shift, 0x01 ;1 [18] + rjmp didUnstuff0 ;2 [20] + +unstuff1: ;1 (branch taken) + mov x2, x1 ;1 [21] x1 contains last sampled (stuffed) bit + andi x3, ~0x02 ;1 [22] + ori shift, 0x02 ;1 [23] + nop ;1 [24] + in x1, USBIN ;1 [25] <-- sample bit 2 again + rjmp didUnstuff1 ;2 [27] + +unstuff2: ;1 (branch taken) + andi x3, ~0x04 ;1 [29] + ori shift, 0x04 ;1 [30] + mov x1, x2 ;1 [31] x2 contains last sampled (stuffed) bit + nop ;1 [32] + in x2, USBIN ;1 [33] <-- sample bit 3 + rjmp didUnstuff2 ;2 [35] + +unstuff3: ;1 (branch taken) + in x2, USBIN ;1 [34] <-- sample stuffed bit 3 [one cycle too late] + andi x3, ~0x08 ;1 [35] + ori shift, 0x08 ;1 [36] + rjmp didUnstuff3 ;2 [38] + +unstuff4: ;1 (branch taken) + andi x3, ~0x10 ;1 [40] + in x1, USBIN ;1 [41] <-- sample stuffed bit 4 + ori shift, 0x10 ;1 [42] + rjmp didUnstuff4 ;2 [44] + +unstuff5: ;1 (branch taken) + andi x3, ~0x20 ;1 [48] + in x2, USBIN ;1 [49] <-- sample stuffed bit 5 + ori shift, 0x20 ;1 [50] + rjmp didUnstuff5 ;2 [52] + +unstuff6: ;1 (branch taken) + andi x3, ~0x40 ;1 [56] + in x1, USBIN ;1 [57] <-- sample stuffed bit 6 + ori shift, 0x40 ;1 [58] + rjmp didUnstuff6 ;2 [60] + +; extra jobs done during bit interval: +; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs] +; bit 1: se0 check +; bit 2: overflow check +; bit 3: recovery from delay [bit 0 tasks took too long] +; bit 4: none +; bit 5: none +; bit 6: none +; bit 7: jump, eor +rxLoop: + eor x3, shift ;1 [0] reconstruct: x3 is 0 at bit locations we changed, 1 at others + in x1, USBIN ;1 [1] <-- sample bit 0 + st y+, x3 ;2 [3] store data + ser x3 ;1 [4] + nop ;1 [5] + eor x2, x1 ;1 [6] + bst x2, USBMINUS;1 [7] + bld shift, 0 ;1 [8] + in x2, USBIN ;1 [9] <-- sample bit 1 (or possibly bit 0 stuffed) + andi x2, USBMASK ;1 [10] + breq se0 ;1 [11] SE0 check for bit 1 + andi shift, 0xf9 ;1 [12] +didUnstuff0: + breq unstuff0 ;1 [13] + eor x1, x2 ;1 [14] + bst x1, USBMINUS;1 [15] + bld shift, 1 ;1 [16] +rxbit2: + in x1, USBIN ;1 [17] <-- sample bit 2 (or possibly bit 1 stuffed) + andi shift, 0xf3 ;1 [18] + breq unstuff1 ;1 [19] do remaining work for bit 1 +didUnstuff1: + subi cnt, 1 ;1 [20] + brcs overflow ;1 [21] loop control + eor x2, x1 ;1 [22] + bst x2, USBMINUS;1 [23] + bld shift, 2 ;1 [24] + in x2, USBIN ;1 [25] <-- sample bit 3 (or possibly bit 2 stuffed) + andi shift, 0xe7 ;1 [26] + breq unstuff2 ;1 [27] +didUnstuff2: + eor x1, x2 ;1 [28] + bst x1, USBMINUS;1 [29] + bld shift, 3 ;1 [30] +didUnstuff3: + andi shift, 0xcf ;1 [31] + breq unstuff3 ;1 [32] + in x1, USBIN ;1 [33] <-- sample bit 4 + eor x2, x1 ;1 [34] + bst x2, USBMINUS;1 [35] + bld shift, 4 ;1 [36] +didUnstuff4: + andi shift, 0x9f ;1 [37] + breq unstuff4 ;1 [38] + nop2 ;2 [40] + in x2, USBIN ;1 [41] <-- sample bit 5 + eor x1, x2 ;1 [42] + bst x1, USBMINUS;1 [43] + bld shift, 5 ;1 [44] +didUnstuff5: + andi shift, 0x3f ;1 [45] + breq unstuff5 ;1 [46] + nop2 ;2 [48] + in x1, USBIN ;1 [49] <-- sample bit 6 + eor x2, x1 ;1 [50] + bst x2, USBMINUS;1 [51] + bld shift, 6 ;1 [52] +didUnstuff6: + cpi shift, 0x02 ;1 [53] + brlo unstuff6 ;1 [54] + nop2 ;2 [56] + in x2, USBIN ;1 [57] <-- sample bit 7 + eor x1, x2 ;1 [58] + bst x1, USBMINUS;1 [59] + bld shift, 7 ;1 [60] +didUnstuff7: + cpi shift, 0x04 ;1 [61] + brsh rxLoop ;2 [63] loop control +unstuff7: + andi x3, ~0x80 ;1 [63] + ori shift, 0x80 ;1 [64] + in x2, USBIN ;1 [65] <-- sample stuffed bit 7 + nop ;1 [66] + rjmp didUnstuff7 ;2 [68] + +macro POP_STANDARD ; 12 cycles + pop cnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH +endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL +endm + +#include "asmcommon.inc" + +;---------------------------------------------------------------------------- +; Transmitting data +;---------------------------------------------------------------------------- + +bitstuff0: ;1 (for branch taken) + eor x1, x4 ;1 + ldi x2, 0 ;1 + out USBOUT, x1 ;1 <-- out + rjmp didStuff0 ;2 branch back 2 cycles earlier +bitstuff1: ;1 (for branch taken) + eor x1, x4 ;1 + rjmp didStuff1 ;2 we know that C is clear, jump back to do OUT and ror 0 into x2 +bitstuff2: ;1 (for branch taken) + eor x1, x4 ;1 + rjmp didStuff2 ;2 jump back 4 cycles earlier and do out and ror 0 into x2 +bitstuff3: ;1 (for branch taken) + eor x1, x4 ;1 + rjmp didStuff3 ;2 jump back earlier and ror 0 into x2 +bitstuff4: ;1 (for branch taken) + eor x1, x4 ;1 + ldi x2, 0 ;1 + out USBOUT, x1 ;1 <-- out + rjmp didStuff4 ;2 jump back 2 cycles earlier + +sendNakAndReti: ;0 [-19] 19 cycles until SOP + ldi x3, USBPID_NAK ;1 [-18] + rjmp usbSendX3 ;2 [-16] +sendAckAndReti: ;0 [-19] 19 cycles until SOP + ldi x3, USBPID_ACK ;1 [-18] + rjmp usbSendX3 ;2 [-16] +sendCntAndReti: ;0 [-17] 17 cycles until SOP + mov x3, cnt ;1 [-16] +usbSendX3: ;0 [-16] + ldi YL, 20 ;1 [-15] 'x3' is R20 + ldi YH, 0 ;1 [-14] + ldi cnt, 2 ;1 [-13] +; rjmp usbSendAndReti fallthrough + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 +; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 +; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte +;uses: x1...x4, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +usbSendAndReti: ;0 [-13] timing: 13 cycles until SOP + in x2, USBDDR ;1 [-12] + ori x2, USBMASK ;1 [-11] + sbi USBOUT, USBMINUS;2 [-9] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;1 [-8] port mirror for tx loop + out USBDDR, x2 ;1 [-7] <- acquire bus +; need not init x2 (bitstuff history) because sync starts with 0 + push x4 ;2 [-5] + ldi x4, USBMASK ;1 [-4] exor mask + ldi shift, 0x80 ;1 [-3] sync byte is first byte sent +txLoop: ; [62] + sbrs shift, 0 ;1 [-2] [62] + eor x1, x4 ;1 [-1] [63] + out USBOUT, x1 ;1 [0] <-- out bit 0 + ror shift ;1 [1] + ror x2 ;1 [2] +didStuff0: + cpi x2, 0xfc ;1 [3] + brsh bitstuff0 ;1 [4] + sbrs shift, 0 ;1 [5] + eor x1, x4 ;1 [6] + ror shift ;1 [7] +didStuff1: + out USBOUT, x1 ;1 [8] <-- out bit 1 + ror x2 ;1 [9] + cpi x2, 0xfc ;1 [10] + brsh bitstuff1 ;1 [11] + sbrs shift, 0 ;1 [12] + eor x1, x4 ;1 [13] + ror shift ;1 [14] +didStuff2: + ror x2 ;1 [15] + out USBOUT, x1 ;1 [16] <-- out bit 2 + cpi x2, 0xfc ;1 [17] + brsh bitstuff2 ;1 [18] + sbrs shift, 0 ;1 [19] + eor x1, x4 ;1 [20] + ror shift ;1 [21] +didStuff3: + ror x2 ;1 [22] + cpi x2, 0xfc ;1 [23] + out USBOUT, x1 ;1 [24] <-- out bit 3 + brsh bitstuff3 ;1 [25] + nop2 ;2 [27] + ld x3, y+ ;2 [29] + sbrs shift, 0 ;1 [30] + eor x1, x4 ;1 [31] + out USBOUT, x1 ;1 [32] <-- out bit 4 + ror shift ;1 [33] + ror x2 ;1 [34] +didStuff4: + cpi x2, 0xfc ;1 [35] + brsh bitstuff4 ;1 [36] + sbrs shift, 0 ;1 [37] + eor x1, x4 ;1 [38] + ror shift ;1 [39] +didStuff5: + out USBOUT, x1 ;1 [40] <-- out bit 5 + ror x2 ;1 [41] + cpi x2, 0xfc ;1 [42] + brsh bitstuff5 ;1 [43] + sbrs shift, 0 ;1 [44] + eor x1, x4 ;1 [45] + ror shift ;1 [46] +didStuff6: + ror x2 ;1 [47] + out USBOUT, x1 ;1 [48] <-- out bit 6 + cpi x2, 0xfc ;1 [49] + brsh bitstuff6 ;1 [50] + sbrs shift, 0 ;1 [51] + eor x1, x4 ;1 [52] + ror shift ;1 [53] +didStuff7: + ror x2 ;1 [54] + cpi x2, 0xfc ;1 [55] + out USBOUT, x1 ;1 [56] <-- out bit 7 + brsh bitstuff7 ;1 [57] + mov shift, x3 ;1 [58] + dec cnt ;1 [59] + brne txLoop ;1/2 [60/61] +;make SE0: + cbr x1, USBMASK ;1 [61] prepare SE0 [spec says EOP may be 15 to 18 cycles] + pop x4 ;2 [63] +;brackets are cycles from start of SE0 now + out USBOUT, x1 ;1 [0] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + lds x2, usbNewDeviceAddr;2 [2] + lsl x2; ;1 [3] we compare with left shifted address + subi YL, 20 + 2 ;1 [4] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;1 [5] + breq skipAddrAssign ;2 [7] + sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< 10.0 cycles per bit, 80.0 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + +;---------------------------------------------------------------------------- +; order of registers pushed: +; YL, SREG [sofError] YH, shift, x1, x2, x3, bitcnt, cnt, x4 +;---------------------------------------------------------------------------- +USB_INTR_VECTOR: + push YL ;2 push only what is necessary to sync with edge ASAP + in YL, SREG ;1 + push YL ;2 +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +; +; sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +; sync up with J to K edge during sync pattern -- use fastest possible loops +; first part has no timeout because it waits for IDLE or SE1 (== disconnected) +;------------------------------------------------------------------------------- +waitForJ: ;- + sbis USBIN, USBMINUS ;1 <-- sample: wait for D- == 1 + rjmp waitForJ ;2 +;------------------------------------------------------------------------------- +; The following code results in a sampling window of < 1/4 bit +; which meets the spec. +;------------------------------------------------------------------------------- +waitForK: ;- + sbis USBIN, USBMINUS ;1 [00] <-- sample + rjmp foundK ;2 [01] + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ + rjmp sofError +;------------------------------------------------------------------------------ +; {3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for +; center sampling] +; we have 1 bit time for setup purposes, then sample again. +; Numbers in brackets are cycles from center of first sync (double K) +; bit after the instruction +;------------------------------------------------------------------------------ +foundK: ;- [02] + lds YL, usbInputBufOffset;2 [03+04] tx loop + push YH ;2 [05+06] + clr YH ;1 [07] + subi YL, lo8(-(usbRxBuf)) ;1 [08] [rx loop init] + sbci YH, hi8(-(usbRxBuf)) ;1 [09] [rx loop init] + push shift ;2 [10+11] + ser shift ;1 [12] + sbis USBIN, USBMINUS ;1 [-1] [13] <--sample:we want two bits K (sample 1 cycle too early) + rjmp haveTwoBitsK ;2 [00] [14] + pop shift ;2 [15+16] undo the push from before + pop YH ;2 [17+18] undo the push from before + rjmp waitForK ;2 [19+20] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 20 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: ;- [01] + push x1 ;2 [02+03] + push x2 ;2 [04+05] + push x3 ;2 [06+07] + push bitcnt ;2 [08+09] + in x1, USBIN ;1 [00] [10] <-- sample bit 0 + bst x1, USBMINUS ;1 [01] + bld shift, 0 ;1 [02] + push cnt ;2 [03+04] + ldi cnt, USB_BUFSIZE ;1 [05] + push x4 ;2 [06+07] tx loop + rjmp rxLoop ;2 [08] +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- +unstuff0: ;- [07] (branch taken) + andi x3, ~0x01 ;1 [08] + mov x1, x2 ;1 [09] x2 contains last sampled (stuffed) bit + in x2, USBIN ;1 [00] [10] <-- sample bit 1 again + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 1 + ori shift, 0x01 ;1 [03] 0b00000001 + nop ;1 [04] + rjmp didUnstuff0 ;2 [05] +;----------------------------------------------------- +unstuff1: ;- [05] (branch taken) + mov x2, x1 ;1 [06] x1 contains last sampled (stuffed) bit + andi x3, ~0x02 ;1 [07] + ori shift, 0x02 ;1 [08] 0b00000010 + nop ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 2 again + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 2 + rjmp didUnstuff1 ;2 [03] +;----------------------------------------------------- +unstuff2: ;- [05] (branch taken) + andi x3, ~0x04 ;1 [06] + ori shift, 0x04 ;1 [07] 0b00000100 + mov x1, x2 ;1 [08] x2 contains last sampled (stuffed) bit + nop ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample bit 3 + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 3 + rjmp didUnstuff2 ;2 [03] +;----------------------------------------------------- +unstuff3: ;- [00] [10] (branch taken) + in x2, USBIN ;1 [01] [11] <-- sample stuffed bit 3 one cycle too late + andi x2, USBMASK ;1 [02] + breq se0Hop ;1 [03] SE0 check for stuffed bit 3 + andi x3, ~0x08 ;1 [04] + ori shift, 0x08 ;1 [05] 0b00001000 + rjmp didUnstuff3 ;2 [06] +;---------------------------------------------------------------------------- +; extra jobs done during bit interval: +; +; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs], +; overflow check, jump to the head of rxLoop +; bit 1: SE0 check +; bit 2: SE0 check, recovery from delay [bit 0 tasks took too long] +; bit 3: SE0 check, recovery from delay [bit 0 tasks took too long] +; bit 4: SE0 check, none +; bit 5: SE0 check, none +; bit 6: SE0 check, none +; bit 7: SE0 check, reconstruct: x3 is 0 at bit locations we changed, 1 at others +;---------------------------------------------------------------------------- +rxLoop: ;- [09] + in x2, USBIN ;1 [00] [10] <-- sample bit 1 (or possibly bit 0 stuffed) + andi x2, USBMASK ;1 [01] + brne SkipSe0Hop ;1 [02] +se0Hop: ;- [02] + rjmp se0 ;2 [03] SE0 check for bit 1 +SkipSe0Hop: ;- [03] + ser x3 ;1 [04] + andi shift, 0xf9 ;1 [05] 0b11111001 + breq unstuff0 ;1 [06] +didUnstuff0: ;- [06] + eor x1, x2 ;1 [07] + bst x1, USBMINUS ;1 [08] + bld shift, 1 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 2 (or possibly bit 1 stuffed) + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 2 + andi shift, 0xf3 ;1 [03] 0b11110011 + breq unstuff1 ;1 [04] do remaining work for bit 1 +didUnstuff1: ;- [04] + eor x2, x1 ;1 [05] + bst x2, USBMINUS ;1 [06] + bld shift, 2 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 3 (or possibly bit 2 stuffed) + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 3 + andi shift, 0xe7 ;1 [03] 0b11100111 + breq unstuff2 ;1 [04] +didUnstuff2: ;- [04] + eor x1, x2 ;1 [05] + bst x1, USBMINUS ;1 [06] + bld shift, 3 ;1 [07] +didUnstuff3: ;- [07] + andi shift, 0xcf ;1 [08] 0b11001111 + breq unstuff3 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 4 + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 4 + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 4 ;1 [05] +didUnstuff4: ;- [05] + andi shift, 0x9f ;1 [06] 0b10011111 + breq unstuff4 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 5 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 5 + eor x1, x2 ;1 [03] + bst x1, USBMINUS ;1 [04] + bld shift, 5 ;1 [05] +didUnstuff5: ;- [05] + andi shift, 0x3f ;1 [06] 0b00111111 + breq unstuff5 ;1 [07] + nop2 ;2 [08+09] + in x1, USBIN ;1 [00] [10] <-- sample bit 6 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 6 + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 6 ;1 [05] +didUnstuff6: ;- [05] + cpi shift, 0x02 ;1 [06] 0b00000010 + brlo unstuff6 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 7 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 7 + eor x1, x2 ;1 [03] + bst x1, USBMINUS ;1 [04] + bld shift, 7 ;1 [05] +didUnstuff7: ;- [05] + cpi shift, 0x04 ;1 [06] 0b00000100 + brlo unstuff7 ;1 [07] + eor x3, shift ;1 [08] reconstruct: x3 is 0 at bit locations we changed, 1 at others + nop ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 0 + st y+, x3 ;2 [01+02] store data + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 0 ;1 [05] + subi cnt, 1 ;1 [06] + brcs overflow ;1 [07] + rjmp rxLoop ;2 [08] +;----------------------------------------------------- +unstuff4: ;- [08] + andi x3, ~0x10 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 4 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 4 + ori shift, 0x10 ;1 [03] + rjmp didUnstuff4 ;2 [04] +;----------------------------------------------------- +unstuff5: ;- [08] + ori shift, 0x20 ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 5 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 5 + andi x3, ~0x20 ;1 [03] + rjmp didUnstuff5 ;2 [04] +;----------------------------------------------------- +unstuff6: ;- [08] + andi x3, ~0x40 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 6 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 6 + ori shift, 0x40 ;1 [03] + rjmp didUnstuff6 ;2 [04] +;----------------------------------------------------- +unstuff7: ;- [08] + andi x3, ~0x80 ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 7 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 7 + ori shift, 0x80 ;1 [03] + rjmp didUnstuff7 ;2 [04] + +macro POP_STANDARD ; 16 cycles + pop x4 + pop cnt + pop bitcnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH +endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL +endm + +#include "asmcommon.inc" + +;--------------------------------------------------------------------------- +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies +;--------------------------------------------------------------------------- +bitstuffN: ;- [04] + eor x1, x4 ;1 [05] + clr x2 ;1 [06] + nop ;1 [07] + rjmp didStuffN ;1 [08] +;--------------------------------------------------------------------------- +bitstuff6: ;- [04] + eor x1, x4 ;1 [05] + clr x2 ;1 [06] + rjmp didStuff6 ;1 [07] +;--------------------------------------------------------------------------- +bitstuff7: ;- [02] + eor x1, x4 ;1 [03] + clr x2 ;1 [06] + nop ;1 [05] + rjmp didStuff7 ;1 [06] +;--------------------------------------------------------------------------- +sendNakAndReti: ;- [-19] + ldi x3, USBPID_NAK ;1 [-18] + rjmp sendX3AndReti ;1 [-17] +;--------------------------------------------------------------------------- +sendAckAndReti: ;- [-17] + ldi cnt, USBPID_ACK ;1 [-16] +sendCntAndReti: ;- [-16] + mov x3, cnt ;1 [-15] +sendX3AndReti: ;- [-15] + ldi YL, 20 ;1 [-14] x3==r20 address is 20 + ldi YH, 0 ;1 [-13] + ldi cnt, 2 ;1 [-12] +; rjmp usbSendAndReti fallthrough +;--------------------------------------------------------------------------- +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +;We need not to match the transfer rate exactly because the spec demands +;only 1.5% precision anyway. +usbSendAndReti: ;- [-13] 13 cycles until SOP + in x2, USBDDR ;1 [-12] + ori x2, USBMASK ;1 [-11] + sbi USBOUT, USBMINUS ;2 [-09-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;1 [-08] port mirror for tx loop + out USBDDR, x2 ;1 [-07] <- acquire bus + ; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;1 [-06] exor mask + ldi shift, 0x80 ;1 [-05] sync byte is first byte sent + ldi bitcnt, 6 ;1 [-04] +txBitLoop: ;- [-04] [06] + sbrs shift, 0 ;1 [-03] [07] + eor x1, x4 ;1 [-02] [08] + ror shift ;1 [-01] [09] +didStuffN: ;- [09] + out USBOUT, x1 ;1 [00] [10] <-- out N + ror x2 ;1 [01] + cpi x2, 0xfc ;1 [02] + brcc bitstuffN ;1 [03] + dec bitcnt ;1 [04] + brne txBitLoop ;1 [05] + sbrs shift, 0 ;1 [06] + eor x1, x4 ;1 [07] + ror shift ;1 [08] +didStuff6: ;- [08] + nop ;1 [09] + out USBOUT, x1 ;1 [00] [10] <-- out 6 + ror x2 ;1 [01] + cpi x2, 0xfc ;1 [02] + brcc bitstuff6 ;1 [03] + sbrs shift, 0 ;1 [04] + eor x1, x4 ;1 [05] + ror shift ;1 [06] + ror x2 ;1 [07] +didStuff7: ;- [07] + ldi bitcnt, 6 ;1 [08] + cpi x2, 0xfc ;1 [09] + out USBOUT, x1 ;1 [00] [10] <-- out 7 + brcc bitstuff7 ;1 [01] + ld shift, y+ ;2 [02+03] + dec cnt ;1 [04] + brne txBitLoop ;1 [05] +makeSE0: + cbr x1, USBMASK ;1 [06] prepare SE0 [spec says EOP may be 19 to 23 cycles] + lds x2, usbNewDeviceAddr;2 [07+08] + lsl x2 ;1 [09] we compare with left shifted address +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + out USBOUT, x1 ;1 [00] [10] <-- out SE0-- from now 2 bits==20 cycl. until bus idle + subi YL, 20 + 2 ;1 [01] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;1 [02] + breq skipAddrAssign ;1 [03] + sts usbDeviceAddr, x2 ;2 [04+05] if not skipped: SE0 is one cycle longer +;---------------------------------------------------------------------------- +;end of usbDeviceAddress transfer +skipAddrAssign: ;- [03/04] + ldi x2, 1< 10.6666666 cycles per bit, 85.333333333 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt + push YL ;[-25] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-23] + push YL ;[-22] + push YH ;[-20] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;first part has no timeout because it waits for IDLE or SE1 (== disconnected) +waitForJ: + sbis USBIN, USBMINUS ;[-18] wait for D- == 1 + rjmp waitForJ +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-15] + rjmp foundK ;[-14] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ + rjmp sofError +foundK: ;[-12] +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push bitcnt ;[-12] +; [---] ;[-11] + lds YL, usbInputBufOffset;[-10] +; [---] ;[-9] + clr YH ;[-8] + subi YL, lo8(-(usbRxBuf));[-7] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-6] [rx loop init] + push shift ;[-5] +; [---] ;[-4] + ldi bitcnt, 0x55 ;[-3] [rx loop init] + sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) + rjmp haveTwoBitsK ;[-1] + pop shift ;[0] undo the push from before + pop bitcnt ;[2] undo the push from before + rjmp waitForK ;[4] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 21 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: + push x1 ;[1] + push x2 ;[3] + push x3 ;[5] + ldi shift, 0 ;[7] + ldi x3, 1<<4 ;[8] [rx loop init] first sample is inverse bit, compensate that + push x4 ;[9] == leap + + in x1, USBIN ;[11] <-- sample bit 0 + andi x1, USBMASK ;[12] + bst x1, USBMINUS ;[13] + bld shift, 7 ;[14] + push cnt ;[15] + ldi leap, 0 ;[17] [rx loop init] + ldi cnt, USB_BUFSIZE;[18] [rx loop init] + rjmp rxbit1 ;[19] arrives at [21] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- + +unstuff6: + andi x2, USBMASK ;[03] + ori x3, 1<<6 ;[04] will not be shifted any more + andi shift, ~0x80;[05] + mov x1, x2 ;[06] sampled bit 7 is actually re-sampled bit 6 + subi leap, 3 ;[07] since this is a short (10 cycle) bit, enforce leap bit + rjmp didUnstuff6 ;[08] + +unstuff7: + ori x3, 1<<7 ;[09] will not be shifted any more + in x2, USBIN ;[00] [10] re-sample bit 7 + andi x2, USBMASK ;[01] + andi shift, ~0x80;[02] + subi leap, 3 ;[03] since this is a short (10 cycle) bit, enforce leap bit + rjmp didUnstuff7 ;[04] + +unstuffEven: + ori x3, 1<<6 ;[09] will be shifted right 6 times for bit 0 + in x1, USBIN ;[00] [10] + andi shift, ~0x80;[01] + andi x1, USBMASK ;[02] + breq se0 ;[03] + subi leap, 3 ;[04] since this is a short (10 cycle) bit, enforce leap bit + nop ;[05] + rjmp didUnstuffE ;[06] + +unstuffOdd: + ori x3, 1<<5 ;[09] will be shifted right 4 times for bit 1 + in x2, USBIN ;[00] [10] + andi shift, ~0x80;[01] + andi x2, USBMASK ;[02] + breq se0 ;[03] + subi leap, 3 ;[04] since this is a short (10 cycle) bit, enforce leap bit + nop ;[05] + rjmp didUnstuffO ;[06] + +rxByteLoop: + andi x1, USBMASK ;[03] + eor x2, x1 ;[04] + subi leap, 1 ;[05] + brpl skipLeap ;[06] + subi leap, -3 ;1 one leap cycle every 3rd byte -> 85 + 1/3 cycles per byte + nop ;1 +skipLeap: + subi x2, 1 ;[08] + ror shift ;[09] +didUnstuff6: + cpi shift, 0xfc ;[10] + in x2, USBIN ;[00] [11] <-- sample bit 7 + brcc unstuff6 ;[01] + andi x2, USBMASK ;[02] + eor x1, x2 ;[03] + subi x1, 1 ;[04] + ror shift ;[05] +didUnstuff7: + cpi shift, 0xfc ;[06] + brcc unstuff7 ;[07] + eor x3, shift ;[08] reconstruct: x3 is 1 at bit locations we changed, 0 at others + st y+, x3 ;[09] store data +rxBitLoop: + in x1, USBIN ;[00] [11] <-- sample bit 0/2/4 + andi x1, USBMASK ;[01] + eor x2, x1 ;[02] + andi x3, 0x3f ;[03] topmost two bits reserved for 6 and 7 + subi x2, 1 ;[04] + ror shift ;[05] + cpi shift, 0xfc ;[06] + brcc unstuffEven ;[07] +didUnstuffE: + lsr x3 ;[08] + lsr x3 ;[09] +rxbit1: + in x2, USBIN ;[00] [10] <-- sample bit 1/3/5 + andi x2, USBMASK ;[01] + breq se0 ;[02] + eor x1, x2 ;[03] + subi x1, 1 ;[04] + ror shift ;[05] + cpi shift, 0xfc ;[06] + brcc unstuffOdd ;[07] +didUnstuffO: + subi bitcnt, 0xab;[08] == addi 0x55, 0x55 = 0x100/3 + brcs rxBitLoop ;[09] + + subi cnt, 1 ;[10] + in x1, USBIN ;[00] [11] <-- sample bit 6 + brcc rxByteLoop ;[01] + rjmp overflow + +macro POP_STANDARD ; 14 cycles + pop cnt + pop x4 + pop x3 + pop x2 + pop x1 + pop shift + pop bitcnt +endm +macro POP_RETI ; 7 cycles + pop YH + pop YL + out SREG, YL + pop YL +endm + +#include "asmcommon.inc" + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies + +bitstuffN: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] + nop2 ;[7] + nop ;[9] + out USBOUT, x1 ;[10] <-- out + rjmp didStuffN ;[0] + +bitstuff6: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] Carry is zero due to brcc + rol shift ;[7] compensate for ror shift at branch destination + rjmp didStuff6 ;[8] + +bitstuff7: + ldi x2, 0 ;[2] Carry is zero due to brcc + rjmp didStuff7 ;[3] + + +sendNakAndReti: + ldi x3, USBPID_NAK ;[-18] + rjmp sendX3AndReti ;[-17] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov x3, cnt ;[-16] +sendX3AndReti: + ldi YL, 20 ;[-15] x3==r20 address is 20 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +;We don't match the transfer rate exactly (don't insert leap cycles every third +;byte) because the spec demands only 1.5% precision anyway. +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-7] <- acquire bus +; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-6] exor mask + ldi shift, 0x80 ;[-5] sync byte is first byte sent +txByteLoop: + ldi bitcnt, 0x35 ;[-4] [6] binary 0011 0101 +txBitLoop: + sbrs shift, 0 ;[-3] [7] + eor x1, x4 ;[-2] [8] + out USBOUT, x1 ;[-1] [9] <-- out N + ror shift ;[0] [10] + ror x2 ;[1] +didStuffN: + cpi x2, 0xfc ;[2] + brcc bitstuffN ;[3] + lsr bitcnt ;[4] + brcc txBitLoop ;[5] + brne txBitLoop ;[6] + + sbrs shift, 0 ;[7] + eor x1, x4 ;[8] +didStuff6: + out USBOUT, x1 ;[-1] [9] <-- out 6 + ror shift ;[0] [10] + ror x2 ;[1] + cpi x2, 0xfc ;[2] + brcc bitstuff6 ;[3] + ror shift ;[4] +didStuff7: + ror x2 ;[5] + sbrs x2, 7 ;[6] + eor x1, x4 ;[7] + nop ;[8] + cpi x2, 0xfc ;[9] + out USBOUT, x1 ;[-1][10] <-- out 7 + brcc bitstuff7 ;[0] [11] + ld shift, y+ ;[1] + dec cnt ;[3] + brne txByteLoop ;[4] +;make SE0: + cbr x1, USBMASK ;[5] prepare SE0 [spec says EOP may be 21 to 25 cycles] + lds x2, usbNewDeviceAddr;[6] + lsl x2 ;[8] we compare with left shifted address + subi YL, 20 + 2 ;[9] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;[10] + out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[0] + sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< max 52 cycles interrupt disable +;max stack usage: [ret(2), r0, SREG, YL, YH, shift, x1, x2, x3, x4, cnt] = 12 bytes +;nominal frequency: 16.5 MHz -> 11 cycles per bit +; 16.3125 MHz < F_CPU < 16.6875 MHz (+/- 1.1%) +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + + +USB_INTR_VECTOR: +;order of registers pushed: r0, SREG [sofError], YL, YH, shift, x1, x2, x3, x4, cnt + push r0 ;[-23] push only what is necessary to sync with edge ASAP + in r0, SREG ;[-21] + push r0 ;[-20] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;first part has no timeout because it waits for IDLE or SE1 (== disconnected) +waitForJ: + sbis USBIN, USBMINUS ;[-18] wait for D- == 1 + rjmp waitForJ +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-15] + rjmp foundK ;[-14] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ + rjmp sofError +foundK: ;[-12] +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push YL ;[-12] +; [---] ;[-11] + push YH ;[-10] +; [---] ;[-9] + lds YL, usbInputBufOffset;[-8] +; [---] ;[-7] + clr YH ;[-6] + subi YL, lo8(-(usbRxBuf));[-5] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-4] [rx loop init] + mov r0, x2 ;[-3] [rx loop init] + sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) + rjmp haveTwoBitsK ;[-1] + pop YH ;[0] undo the pushes from before + pop YL ;[2] + rjmp waitForK ;[4] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 22 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: ;[1] + push shift ;[1] + push x1 ;[3] + push x2 ;[5] + push x3 ;[7] + ldi shift, 0xff ;[9] [rx loop init] + ori x3, 0xff ;[10] [rx loop init] == ser x3, clear zero flag + + in x1, USBIN ;[11] <-- sample bit 0 + bst x1, USBMINUS ;[12] + bld shift, 0 ;[13] + push x4 ;[14] == phase +; [---] ;[15] + push cnt ;[16] +; [---] ;[17] + ldi phase, 0 ;[18] [rx loop init] + ldi cnt, USB_BUFSIZE;[19] [rx loop init] + rjmp rxbit1 ;[20] +; [---] ;[21] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- +/* +byte oriented operations done during loop: +bit 0: store data +bit 1: SE0 check +bit 2: overflow check +bit 3: catch up +bit 4: rjmp to achieve conditional jump range +bit 5: PLL +bit 6: catch up +bit 7: jump, fixup bitstuff +; 87 [+ 2] cycles +------------------------------------------------------------------ +*/ +continueWithBit5: + in x2, USBIN ;[055] <-- bit 5 + eor r0, x2 ;[056] + or phase, r0 ;[057] + sbrc phase, USBMINUS ;[058] + lpm ;[059] optional nop3; modifies r0 + in phase, USBIN ;[060] <-- phase + eor x1, x2 ;[061] + bst x1, USBMINUS ;[062] + bld shift, 5 ;[063] + andi shift, 0x3f ;[064] + in x1, USBIN ;[065] <-- bit 6 + breq unstuff5 ;[066] *** unstuff escape + eor phase, x1 ;[067] + eor x2, x1 ;[068] + bst x2, USBMINUS ;[069] + bld shift, 6 ;[070] +didUnstuff6: ;[ ] + in r0, USBIN ;[071] <-- phase + cpi shift, 0x02 ;[072] + brlo unstuff6 ;[073] *** unstuff escape +didUnstuff5: ;[ ] + nop2 ;[074] +; [---] ;[075] + in x2, USBIN ;[076] <-- bit 7 + eor x1, x2 ;[077] + bst x1, USBMINUS ;[078] + bld shift, 7 ;[079] +didUnstuff7: ;[ ] + eor r0, x2 ;[080] + or phase, r0 ;[081] + in r0, USBIN ;[082] <-- phase + cpi shift, 0x04 ;[083] + brsh rxLoop ;[084] +; [---] ;[085] +unstuff7: ;[ ] + andi x3, ~0x80 ;[085] + ori shift, 0x80 ;[086] + in x2, USBIN ;[087] <-- sample stuffed bit 7 + nop ;[088] + rjmp didUnstuff7 ;[089] +; [---] ;[090] + ;[080] + +unstuff5: ;[067] + eor phase, x1 ;[068] + andi x3, ~0x20 ;[069] + ori shift, 0x20 ;[070] + in r0, USBIN ;[071] <-- phase + mov x2, x1 ;[072] + nop ;[073] + nop2 ;[074] +; [---] ;[075] + in x1, USBIN ;[076] <-- bit 6 + eor r0, x1 ;[077] + or phase, r0 ;[078] + eor x2, x1 ;[079] + bst x2, USBMINUS ;[080] + bld shift, 6 ;[081] no need to check bitstuffing, we just had one + in r0, USBIN ;[082] <-- phase + rjmp didUnstuff5 ;[083] +; [---] ;[084] + ;[074] + +unstuff6: ;[074] + andi x3, ~0x40 ;[075] + in x1, USBIN ;[076] <-- bit 6 again + ori shift, 0x40 ;[077] + nop2 ;[078] +; [---] ;[079] + rjmp didUnstuff6 ;[080] +; [---] ;[081] + ;[071] + +unstuff0: ;[013] + eor r0, x2 ;[014] + or phase, r0 ;[015] + andi x2, USBMASK ;[016] check for SE0 + in r0, USBIN ;[017] <-- phase + breq didUnstuff0 ;[018] direct jump to se0 would be too long + andi x3, ~0x01 ;[019] + ori shift, 0x01 ;[020] + mov x1, x2 ;[021] mov existing sample + in x2, USBIN ;[022] <-- bit 1 again + rjmp didUnstuff0 ;[023] +; [---] ;[024] + ;[014] + +unstuff1: ;[024] + eor r0, x1 ;[025] + or phase, r0 ;[026] + andi x3, ~0x02 ;[027] + in r0, USBIN ;[028] <-- phase + ori shift, 0x02 ;[029] + mov x2, x1 ;[030] + rjmp didUnstuff1 ;[031] +; [---] ;[032] + ;[022] + +unstuff2: ;[035] + eor r0, x2 ;[036] + or phase, r0 ;[037] + andi x3, ~0x04 ;[038] + in r0, USBIN ;[039] <-- phase + ori shift, 0x04 ;[040] + mov x1, x2 ;[041] + rjmp didUnstuff2 ;[042] +; [---] ;[043] + ;[033] + +unstuff3: ;[043] + in x2, USBIN ;[044] <-- bit 3 again + eor r0, x2 ;[045] + or phase, r0 ;[046] + andi x3, ~0x08 ;[047] + ori shift, 0x08 ;[048] + nop ;[049] + in r0, USBIN ;[050] <-- phase + rjmp didUnstuff3 ;[051] +; [---] ;[052] + ;[042] + +unstuff4: ;[053] + andi x3, ~0x10 ;[054] + in x1, USBIN ;[055] <-- bit 4 again + ori shift, 0x10 ;[056] + rjmp didUnstuff4 ;[057] +; [---] ;[058] + ;[048] + +rxLoop: ;[085] + eor x3, shift ;[086] reconstruct: x3 is 0 at bit locations we changed, 1 at others + in x1, USBIN ;[000] <-- bit 0 + st y+, x3 ;[001] +; [---] ;[002] + eor r0, x1 ;[003] + or phase, r0 ;[004] + eor x2, x1 ;[005] + in r0, USBIN ;[006] <-- phase + ser x3 ;[007] + bst x2, USBMINUS ;[008] + bld shift, 0 ;[009] + andi shift, 0xf9 ;[010] +rxbit1: ;[ ] + in x2, USBIN ;[011] <-- bit 1 + breq unstuff0 ;[012] *** unstuff escape + andi x2, USBMASK ;[013] SE0 check for bit 1 +didUnstuff0: ;[ ] Z only set if we detected SE0 in bitstuff + breq se0 ;[014] + eor r0, x2 ;[015] + or phase, r0 ;[016] + in r0, USBIN ;[017] <-- phase + eor x1, x2 ;[018] + bst x1, USBMINUS ;[019] + bld shift, 1 ;[020] + andi shift, 0xf3 ;[021] +didUnstuff1: ;[ ] + in x1, USBIN ;[022] <-- bit 2 + breq unstuff1 ;[023] *** unstuff escape + eor r0, x1 ;[024] + or phase, r0 ;[025] + subi cnt, 1 ;[026] overflow check + brcs overflow ;[027] + in r0, USBIN ;[028] <-- phase + eor x2, x1 ;[029] + bst x2, USBMINUS ;[030] + bld shift, 2 ;[031] + andi shift, 0xe7 ;[032] +didUnstuff2: ;[ ] + in x2, USBIN ;[033] <-- bit 3 + breq unstuff2 ;[034] *** unstuff escape + eor r0, x2 ;[035] + or phase, r0 ;[036] + eor x1, x2 ;[037] + bst x1, USBMINUS ;[038] + in r0, USBIN ;[039] <-- phase + bld shift, 3 ;[040] + andi shift, 0xcf ;[041] +didUnstuff3: ;[ ] + breq unstuff3 ;[042] *** unstuff escape + nop ;[043] + in x1, USBIN ;[044] <-- bit 4 + eor x2, x1 ;[045] + bst x2, USBMINUS ;[046] + bld shift, 4 ;[047] +didUnstuff4: ;[ ] + eor r0, x1 ;[048] + or phase, r0 ;[049] + in r0, USBIN ;[050] <-- phase + andi shift, 0x9f ;[051] + breq unstuff4 ;[052] *** unstuff escape + rjmp continueWithBit5;[053] +; [---] ;[054] + +macro POP_STANDARD ; 16 cycles + pop cnt + pop x4 + pop x3 + pop x2 + pop x1 + pop shift + pop YH + pop YL +endm +macro POP_RETI ; 5 cycles + pop r0 + out SREG, r0 + pop r0 +endm + +#include "asmcommon.inc" + + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies + +bitstuff7: + eor x1, x4 ;[4] + ldi x2, 0 ;[5] + nop2 ;[6] C is zero (brcc) + rjmp didStuff7 ;[8] + +bitstuffN: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] + lpm ;[7] 3 cycle NOP, modifies r0 + out USBOUT, x1 ;[10] <-- out + rjmp didStuffN ;[0] + +#define bitStatus x3 + +sendNakAndReti: + ldi cnt, USBPID_NAK ;[-19] + rjmp sendCntAndReti ;[-18] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov r0, cnt ;[-16] + ldi YL, 0 ;[-15] R0 address is 0 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-7] <- acquire bus +; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-6] exor mask + ldi shift, 0x80 ;[-5] sync byte is first byte sent + ldi bitStatus, 0xff ;[-4] init bit loop counter, works for up to 12 bytes +byteloop: +bitloop: + sbrs shift, 0 ;[8] [-3] + eor x1, x4 ;[9] [-2] + out USBOUT, x1 ;[10] [-1] <-- out + ror shift ;[0] + ror x2 ;[1] +didStuffN: + cpi x2, 0xfc ;[2] + brcc bitstuffN ;[3] + nop ;[4] + subi bitStatus, 37 ;[5] 256 / 7 ~=~ 37 + brcc bitloop ;[6] when we leave the loop, bitStatus has almost the initial value + sbrs shift, 0 ;[7] + eor x1, x4 ;[8] + ror shift ;[9] +didStuff7: + out USBOUT, x1 ;[10] <-- out + ror x2 ;[0] + cpi x2, 0xfc ;[1] + brcc bitstuff7 ;[2] + ld shift, y+ ;[3] + dec cnt ;[5] + brne byteloop ;[6] +;make SE0: + cbr x1, USBMASK ;[7] prepare SE0 [spec says EOP may be 21 to 25 cycles] + lds x2, usbNewDeviceAddr;[8] + lsl x2 ;[10] we compare with left shifted address + out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + subi YL, 2 ;[0] Only assign address on data packets, not ACK/NAK in r0 + sbci YH, 0 ;[1] + breq skipAddrAssign ;[2] + sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1<> Errors ERC: 0 diff --git a/circuit/dulcimer.lst b/circuit/dulcimer.lst new file mode 100644 index 0000000..8f6bc7f --- /dev/null +++ b/circuit/dulcimer.lst @@ -0,0 +1,67 @@ +EESchema (20080320-r918) >> Creation date: 18/4/2008-19:05:17 + +#Cmp ( order = Reference ) +| C1 100n +| C2 10u +| C3 22p +| C4 22p +| D1 3.6V +| D2 3.6V +| IC1 MEGA32-P +| J1 USB +| JP1 PINHD-1X16M +| JP10 PINHD-1X4 +| JP11 PINHD-1X3 +| JP12 PINHD-1X3 +| JP13 PINHD-1X4 +| JP2 PINHD-1X8M +| JP3 PINHD-1X4M +| JP4 JUMPER +| JP5 JUMPER +| JP6 JUMPER +| JP7 PINHD-2X5M +| P1 CONN_2 +| R1 10k +| R2 2k2 +| R3 470 +| R4 470 +| R5 470 +| R6 4k7 +| R7 68 +| R8 68 +| X1 12MHz +#End Cmp + +#Cmp ( order = Value ) +| 100n C1 +| 10k R1 +| 10u C2 +| 12MHz X1 +| 22p C3 +| 22p C4 +| 2k2 R2 +| 3.6V D1 +| 3.6V D2 +| 470 R3 +| 470 R4 +| 470 R5 +| 4k7 R6 +| 68 R7 +| 68 R8 +| CONN_2 P1 +| JUMPER JP4 +| JUMPER JP5 +| JUMPER JP6 +| MEGA32-P IC1 +| PINHD-1X16M JP1 +| PINHD-1X3 JP11 +| PINHD-1X3 JP12 +| PINHD-1X4 JP10 +| PINHD-1X4 JP13 +| PINHD-1X4M JP3 +| PINHD-1X8M JP2 +| PINHD-2X5M JP7 +| USB J1 +#End Cmp + +#End List diff --git a/circuit/dulcimer.net b/circuit/dulcimer.net new file mode 100644 index 0000000..0b94da2 --- /dev/null +++ b/circuit/dulcimer.net @@ -0,0 +1,270 @@ +# EESchema Netlist Version 1.1 created 18/4/2008-19:20:15 +( + ( /48044756 C1 C1 100n + ( 1 GND ) + ( 2 VCC ) + ) + ( /48044769 C1V7 C2 10u + ( 1 GND ) + ( 2 VCC ) + ) + ( /48044750 C1 C3 22p + ( 1 N-000050 ) + ( 2 GND ) + ) + ( /48044754 C1 C4 22p + ( 1 N-000049 ) + ( 2 GND ) + ) + ( /48044791 D3 D1 3.6V + ( 1 GND ) + ( 2 N-000020 ) + ) + ( /48044798 D3 D2 3.6V + ( 1 GND ) + ( 2 N-000018 ) + ) + ( /480447FC atmel-DIL40 IC1 MEGA32-P + ( 1 /C0 ) + ( 2 /C1 ) + ( 3 /C2 ) + ( 4 /C3 ) + ( 5 /C4 ) + ( 6 /MOSI ) + ( 7 /MISO ) + ( 8 /SCK ) + ( 9 /Reset ) + ( 10 VCC ) + ( 11 GND ) + ( 12 N-000050 ) + ( 13 N-000049 ) + ( 14 N-000005 ) + ( 15 N-000007 ) + ( 16 N-000004 ) + ( 17 N-000016 ) + ( 18 N-000013 ) + ( 19 N-000011 ) + ( 20 N-000012 ) + ( 21 N-000006 ) + ( 22 /R15 ) + ( 23 /R14 ) + ( 24 /R13 ) + ( 25 /R12 ) + ( 26 /R11 ) + ( 27 /R10 ) + ( 28 /R9 ) + ( 29 /R8 ) + ( 30 ? ) + ( 31 ? ) + ( 32 ? ) + ( 33 /R7 ) + ( 34 /R6 ) + ( 35 /R5 ) + ( 36 /R4 ) + ( 37 /R3 ) + ( 38 /R2 ) + ( 39 /R1 ) + ( 40 /R0 ) + ) + ( /480446AA CONN_USB-B J1 USB + ( 1 VCC ) + ( 2 N-000003 ) + ( 3 N-000017 ) + ( 4 GND ) + ( 5 GND ) + ( 6 GND ) + ) + ( /48044A02 pinhead-1X16 JP1 PINHD-1X16M + ( 1 /R0 ) + ( 2 /R1 ) + ( 3 /R2 ) + ( 4 /R3 ) + ( 5 /R4 ) + ( 6 /R5 ) + ( 7 /R6 ) + ( 8 /R7 ) + ( 9 /R8 ) + ( 10 /R9 ) + ( 11 /R10 ) + ( 12 /R11 ) + ( 13 /R12 ) + ( 14 /R13 ) + ( 15 /R14 ) + ( 16 /R15 ) + ) + ( /48044A0E pinhead-1X08 JP2 PINHD-1X8M + ( 1 /C0 ) + ( 2 /C1 ) + ( 3 /C2 ) + ( 4 /C3 ) + ( 5 /C4 ) + ( 6 /MOSI ) + ( 7 /MISO ) + ( 8 /SCK ) + ) + ( /48044A1E pinhead-1X04 JP3 PINHD-1X4M + ( 1 N-000010 ) + ( 2 GND ) + ( 3 N-000014 ) + ( 4 N-000009 ) + ) + ( /480486B7 pinhead-1X02 JP4 JUMPER + ( 1 N-000007 ) + ( 2 GND ) + ) + ( /480486CC pinhead-1X02 JP5 JUMPER + ( 1 N-000016 ) + ( 2 GND ) + ) + ( /480486D0 pinhead-1X02 JP6 JUMPER + ( 1 N-000006 ) + ( 2 GND ) + ) + ( /48044A4D atmel-AVR-ISP-10 JP7 PINHD-2X5M + ( 1 /MOSI ) + ( 2 VCC ) + ( 3 ? ) + ( 4 GND ) + ( 5 /Reset ) + ( 6 GND ) + ( 7 /SCK ) + ( 8 GND ) + ( 9 /MISO ) + ( 10 GND ) + ) + ( /4808C81B pinhead-1X04 JP10 PINHD-1X4 + ( 1 VCC ) + ( 2 N-000002 ) + ( 3 N-000019 ) + ( 4 GND ) + ) + ( /4808E9AB pinhead-1X03 JP11 PINHD-1X3 + ( 1 N-000019 ) + ( 2 N-000017 ) + ( 3 N-000018 ) + ) + ( /4808E9BA pinhead-1X03 JP12 PINHD-1X3 + ( 1 N-000002 ) + ( 2 N-000003 ) + ( 3 N-000020 ) + ) + ( /4808E9EA pinhead-1X04 JP13 PINHD-1X4 + ( 1 VCC ) + ( 2 N-000020 ) + ( 3 N-000018 ) + ( 4 GND ) + ) + ( /48061210 connect-CLAMP-02 P1 CONN_2 + ( 1 GND ) + ( 2 GND ) + ) + ( /4804473F R3 R1 10k + ( 1 /Reset ) + ( 2 VCC ) + ) + ( /4804473B R3 R2 2k2 + ( 1 VCC ) + ( 2 N-000020 ) + ) + ( /48044733 R3 R3 470 + ( 1 N-000010 ) + ( 2 N-000012 ) + ) + ( /48044731 R3 R4 470 + ( 1 N-000014 ) + ( 2 N-000011 ) + ) + ( /4804472B R3 R5 470 + ( 1 N-000009 ) + ( 2 N-000013 ) + ) + ( /48044739 R3 R6 4k7 + ( 1 N-000020 ) + ( 2 GND ) + ) + ( /48044741 R3 R7 68 + ( 1 N-000018 ) + ( 2 N-000004 ) + ) + ( /48044743 R3 R8 68 + ( 1 N-000020 ) + ( 2 N-000005 ) + ) + ( /4804477C HC-18UV X1 12MHz + ( 1 N-000050 ) + ( 2 N-000049 ) + ) +) +* +{ Allowed footprints by component: +$component C1 + SM* + C? + C1-1 +$endlist +$component C2 + CP* + SM* +$endlist +$component C3 + SM* + C? + C1-1 +$endlist +$component C4 + SM* + C? + C1-1 +$endlist +$component D1 + D? + SO* + SM* +$endlist +$component D2 + D? + SO* + SM* +$endlist +$component R1 + R? + SM0603 + SM0805 +$endlist +$component R2 + R? + SM0603 + SM0805 +$endlist +$component R3 + R? + SM0603 + SM0805 +$endlist +$component R4 + R? + SM0603 + SM0805 +$endlist +$component R5 + R? + SM0603 + SM0805 +$endlist +$component R6 + R? + SM0603 + SM0805 +$endlist +$component R7 + R? + SM0603 + SM0805 +$endlist +$component R8 + R? + SM0603 + SM0805 +$endlist +$endfootprintlist +} diff --git a/circuit/dulcimer.pro b/circuit/dulcimer.pro new file mode 100644 index 0000000..4b8987d --- /dev/null +++ b/circuit/dulcimer.pro @@ -0,0 +1,147 @@ +update=16/4/2008-14:31:58 +last_client=pcbnew +[general] +version=1 +RootSch=dulcimer.sch +BoardNm=dulcimer.brd +[common] +NetDir= +[eeschema] +version=1 +LibDir= +NetFmt=1 +HPGLSpd=20 +HPGLDm=15 +HPGLNum=1 +offX_A4=0 +offY_A4=0 +offX_A3=0 +offY_A3=0 +offX_A2=0 +offY_A2=0 +offX_A1=0 +offY_A1=0 +offX_A0=0 +offY_A0=0 +offX_A=0 +offY_A=0 +offX_B=0 +offY_B=0 +offX_C=0 +offY_C=0 +offX_D=0 +offY_D=0 +offX_E=0 +offY_E=0 +RptD_X=0 +RptD_Y=100 +RptLab=1 +SimCmd= +UseNetN=0 +LabSize=60 +[eeschema/libraries] +LibName1=power +LibName2=pinhead +LibName3=device +LibName4=conn +LibName5=linear +LibName6=regul +LibName7=74xx +LibName8=cmos4000 +LibName9=adc-dac +LibName10=memory +LibName11=xilinx +LibName12=special +LibName13=microcontrollers +LibName14=dsp +LibName15=microchip +LibName16=analog_switches +LibName17=motorola +LibName18=texas +LibName19=intel +LibName20=audio +LibName21=interface +LibName22=digital-audio +LibName23=philips +LibName24=display +LibName25=cypress +LibName26=siliconi +LibName27=contrib +LibName28=valves +[cvpcb] +version=1 +NetITyp=0 +NetIExt=.net +PkgIExt=.pkg +NetType=0 +[cvpcb/libraries] +EquName1=devcms +[pcbnew] +version=1 +PadDrlX=320 +PadDimH=600 +PadDimV=600 +PadForm=1 +PadMask=14745599 +ViaDiam=450 +ViaDril=250 +MViaDia=200 +MViaDrl=80 +Isol=60 +Countlayer=2 +Lpiste=170 +RouteTo=15 +RouteBo=0 +TypeVia=3 +Segm45=1 +Racc45=1 +Unite=0 +SegFill=1 +SegAffG=0 +NewAffG=1 +PadFill=1 +PadAffG=1 +PadSNum=1 +ModAffC=0 +ModAffT=0 +PcbAffT=0 +SgPcb45=1 +TxtPcbV=800 +TxtPcbH=600 +TxtModV=600 +TxtModH=600 +TxtModW=120 +HPGLnum=1 +HPGdiam=15 +HPGLSpd=20 +HPGLrec=2 +HPGLorg=0 +GERBmin=15 +VEgarde=100 +DrawLar=150 +EdgeLar=150 +TxtLar=120 +MSegLar=150 +ForPlot=1 +WpenSer=10 +UserGrX=1 +UserGrY=1 +UserGrU=1 +DivGrPc=1 +TimeOut=600 +MaxLnkS=3 +ShowRat=0 +ShowMRa=1 +[pcbnew/libraries] +LibDir= +LibName1=supports +LibName2=pl_empreinte +LibName3=pinhead +LibName4=atmel +LibName5=connect +LibName6=discret +LibName7=pin_array +LibName8=divers +LibName9=libcms +LibName10=display +LibName11=valves diff --git a/circuit/dulcimer.sch b/circuit/dulcimer.sch new file mode 100644 index 0000000..4991f70 --- /dev/null +++ b/circuit/dulcimer.sch @@ -0,0 +1,1139 @@ +EESchema Schematic File Version 2 +LIBS:power,pinhead,device,conn,linear,regul,74xx,cmos4000,adc-dac,memory,xilinx,special,microcontrollers,dsp,microchip,analog_switches,motorola,texas,intel,audio,interface,digital-audio,philips,display,cypress,siliconi,contrib,valves,./dulcimer.cache +EELAYER 24 0 +EELAYER END +$Descr A4 11700 8267 +Sheet 1 1 +Title "Dulcimer - USB Keyboard Controller" +Date "18 apr 2008" +Rev "1" +Comp "Ronald Schaten" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +Wire Wire Line + 3850 5650 3850 5900 +Wire Wire Line + 2750 5650 2750 5900 +Connection ~ 3750 6600 +Wire Wire Line + 3750 5650 3750 6600 +Wire Wire Line + 2550 5650 2550 6050 +Wire Wire Line + 2550 6050 2850 6050 +Wire Wire Line + 2850 6050 2850 6300 +Wire Wire Line + 3050 6750 5450 6750 +Wire Wire Line + 3050 6750 3050 6300 +Wire Wire Line + 2950 6300 2950 6750 +Wire Wire Line + 2950 6750 1750 6750 +Wire Wire Line + 5250 5900 5250 4500 +Wire Wire Line + 5250 4500 4400 4500 +Wire Wire Line + 5450 5900 5450 4700 +Wire Wire Line + 5450 4700 4400 4700 +Wire Wire Line + 2050 4200 2050 4450 +Wire Wire Line + 1000 6850 1000 7100 +Wire Wire Line + 6100 5500 6100 5750 +Wire Wire Line + 6450 4900 6450 4000 +Wire Wire Line + 6450 4000 4400 4000 +Wire Wire Line + 5750 4900 5750 4600 +Wire Wire Line + 5750 4600 4400 4600 +Wire Wire Line + 9450 5950 8900 5950 +Wire Wire Line + 4400 1300 5350 1300 +Wire Wire Line + 9250 1300 9950 1300 +Wire Wire Line + 9950 3950 8850 3950 +Wire Wire Line + 9950 3750 8850 3750 +Wire Wire Line + 9950 3550 8850 3550 +Wire Wire Line + 9950 3350 8850 3350 +Wire Wire Line + 9950 2800 9250 2800 +Wire Wire Line + 9950 2600 9250 2600 +Wire Wire Line + 9950 2400 9250 2400 +Wire Wire Line + 9950 2200 9250 2200 +Wire Wire Line + 9950 2000 9250 2000 +Wire Wire Line + 9950 1800 9250 1800 +Wire Wire Line + 9950 1600 9250 1600 +Wire Wire Line + 9950 1400 9250 1400 +Wire Wire Line + 5700 2900 4400 2900 +Wire Wire Line + 5700 2700 4400 2700 +Wire Wire Line + 5700 2500 4400 2500 +Wire Wire Line + 4400 2300 5700 2300 +Wire Wire Line + 5350 3800 4400 3800 +Wire Wire Line + 5350 3600 4400 3600 +Wire Wire Line + 5350 3400 4400 3400 +Wire Wire Line + 5350 3200 4400 3200 +Wire Wire Line + 5350 2000 4400 2000 +Wire Wire Line + 5350 1800 4400 1800 +Wire Wire Line + 5350 1600 4400 1600 +Wire Wire Line + 5350 1400 4400 1400 +Wire Bus Line + 8750 3850 8750 1750 +Wire Bus Line + 8750 1750 5800 1750 +Wire Bus Line + 5800 1750 5800 2800 +Connection ~ 10150 6350 +Wire Wire Line + 10150 6350 9750 6350 +Connection ~ 10150 6150 +Wire Wire Line + 9750 6150 10150 6150 +Wire Wire Line + 10150 5800 10150 5950 +Wire Wire Line + 10150 5950 9750 5950 +Wire Wire Line + 9950 4700 9450 4700 +Wire Wire Line + 9450 4700 9450 4800 +Wire Wire Line + 9450 4800 9050 4800 +Wire Wire Line + 9050 4400 9950 4400 +Wire Wire Line + 8550 4600 8150 4600 +Wire Wire Line + 8150 4600 8150 4200 +Wire Wire Line + 8150 4200 4400 4200 +Connection ~ 5000 6600 +Wire Wire Line + 5000 6600 5000 7000 +Wire Wire Line + 4300 5950 4300 5850 +Connection ~ 4300 6750 +Wire Wire Line + 4650 7400 4650 7550 +Wire Wire Line + 1000 6750 800 6750 +Wire Wire Line + 800 6750 800 7100 +Wire Wire Line + 3300 5000 3300 5150 +Wire Wire Line + 1550 3600 1550 3800 +Wire Wire Line + 1000 3600 1000 3800 +Wire Wire Line + 1550 2300 2200 2300 +Wire Wire Line + 1550 1700 2400 1700 +Wire Wire Line + 850 1700 1150 1700 +Wire Wire Line + 1900 1300 2400 1300 +Wire Wire Line + 1150 1100 1150 1300 +Wire Wire Line + 1150 1300 1400 1300 +Wire Wire Line + 3300 1000 3300 800 +Wire Wire Line + 2400 2100 2200 2100 +Wire Wire Line + 2200 2100 2200 2300 +Wire Wire Line + 1150 2300 850 2300 +Wire Wire Line + 1000 4200 1000 4450 +Wire Wire Line + 1550 4200 1550 4450 +Wire Wire Line + 800 6350 800 6600 +Wire Wire Line + 800 6600 1000 6600 +Wire Wire Line + 4300 7550 4300 7400 +Wire Wire Line + 5000 7550 5000 7400 +Wire Wire Line + 4300 6900 4300 6450 +Wire Wire Line + 4650 7000 4650 6750 +Connection ~ 4650 6750 +Wire Wire Line + 4400 4100 8250 4100 +Wire Wire Line + 8250 4100 8250 4400 +Wire Wire Line + 8250 4400 8550 4400 +Wire Wire Line + 4400 4300 8050 4300 +Wire Wire Line + 8050 4300 8050 4800 +Wire Wire Line + 8050 4800 8550 4800 +Wire Wire Line + 9050 4600 9950 4600 +Wire Wire Line + 9450 4500 9950 4500 +Connection ~ 1900 1700 +Connection ~ 1900 2300 +Wire Wire Line + 9750 6050 10150 6050 +Wire Wire Line + 10150 6050 10150 6550 +Wire Wire Line + 10150 6250 9750 6250 +Connection ~ 10150 6250 +Wire Bus Line + 5450 3700 5450 850 +Wire Bus Line + 5450 850 9150 850 +Wire Bus Line + 9150 850 9150 2700 +Wire Wire Line + 4400 1500 5350 1500 +Wire Wire Line + 4400 1700 5350 1700 +Wire Wire Line + 4400 1900 5350 1900 +Wire Wire Line + 4400 3100 5350 3100 +Wire Wire Line + 4400 3300 5350 3300 +Wire Wire Line + 4400 3500 5350 3500 +Wire Wire Line + 4400 3700 5350 3700 +Wire Wire Line + 5700 2200 4400 2200 +Wire Wire Line + 5700 2400 4400 2400 +Wire Wire Line + 4400 2600 5700 2600 +Wire Wire Line + 4400 2800 5700 2800 +Wire Wire Line + 9250 1500 9950 1500 +Wire Wire Line + 9250 1700 9950 1700 +Wire Wire Line + 9250 1900 9950 1900 +Wire Wire Line + 9250 2100 9950 2100 +Wire Wire Line + 9250 2300 9950 2300 +Wire Wire Line + 9250 2500 9950 2500 +Wire Wire Line + 9250 2700 9950 2700 +Wire Wire Line + 8850 3250 9950 3250 +Wire Wire Line + 8850 3450 9950 3450 +Wire Wire Line + 8850 3650 9950 3650 +Wire Wire Line + 8850 3850 9950 3850 +Wire Wire Line + 9450 6150 8900 6150 +Wire Wire Line + 9450 6250 8900 6250 +Wire Wire Line + 9450 6350 8900 6350 +Wire Wire Line + 4400 4400 6100 4400 +Wire Wire Line + 6100 4400 6100 4900 +Wire Wire Line + 5750 5500 5750 5750 +Wire Wire Line + 6450 5500 6450 5750 +Wire Wire Line + 1750 6850 1750 7100 +Wire Wire Line + 2250 4200 2250 4450 +Wire Wire Line + 5450 6750 5450 6400 +Wire Wire Line + 5250 6400 5250 6600 +Wire Wire Line + 1750 6600 2300 6600 +Wire Wire Line + 2300 6600 2300 6300 +Wire Wire Line + 2400 6300 2400 6600 +Wire Wire Line + 2400 6600 5250 6600 +Wire Wire Line + 2200 6300 2200 5950 +Wire Wire Line + 2200 5950 2650 5950 +Wire Wire Line + 2650 5950 2650 5650 +Wire Wire Line + 3650 5650 3650 6750 +Connection ~ 3650 6750 +Wire Wire Line + 2200 5650 2200 5850 +Wire Wire Line + 2200 5850 2450 5850 +Wire Wire Line + 2450 5850 2450 5650 +Wire Wire Line + 3300 5650 3300 5850 +Wire Wire Line + 3300 5850 3550 5850 +Wire Wire Line + 3550 5850 3550 5650 +$Comp +L GND #PWR01 +U 1 1 4808EF19 +P 3850 5900 +AR Path="/4808EF19" Ref="#PWR01" +F 0 "#PWR01" H 3850 5900 30 0001 C C +F 1 "GND" H 3850 5830 30 0001 C C + 1 3850 5900 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR02 +U 1 1 4808EF14 +P 2750 5900 +AR Path="/4808EF14" Ref="#PWR02" +F 0 "#PWR02" H 2750 5900 30 0001 C C +F 1 "GND" H 2750 5830 30 0001 C C + 1 2750 5900 + 1 0 0 -1 +$EndComp +$Comp +L VCC #PWR03 +U 1 1 4808EF05 +P 3300 5650 +AR Path="/4808EF05" Ref="#PWR03" +F 0 "#PWR03" H 3300 5750 30 0001 C C +F 1 "VCC" H 3300 5750 30 0000 C C + 1 3300 5650 + 1 0 0 -1 +$EndComp +$Comp +L VCC #PWR04 +U 1 1 4808EF01 +P 2200 5650 +AR Path="/4808EF01" Ref="#PWR04" +F 0 "#PWR04" H 2200 5750 30 0001 C C +F 1 "VCC" H 2200 5750 30 0000 C C + 1 2200 5650 + 1 0 0 -1 +$EndComp +Text Notes 3300 5450 0 60 ~ +USB AUX IN +$Comp +L PINHD-1X4 JP13 +U 1 1 4808E9EA +P 3750 5550 +AR Path="/4808E9EA" Ref="JP13" +F 0 "JP13" H 3500 5875 50 0000 L B +F 1 "PINHD-1X4" H 3500 5250 50 0000 L B +F 2 "pinhead-1X04" H 3750 5700 50 0001 C C + 1 3750 5550 + 0 -1 -1 0 +$EndComp +$Comp +L PINHD-1X3 JP12 +U 1 1 4808E9BA +P 2950 6200 +AR Path="/4808E9BA" Ref="JP12" +F 0 "JP12" H 2700 6425 50 0000 L B +F 1 "PINHD-1X3" H 2700 5900 50 0000 L B +F 2 "pinhead-1X03" H 2950 6350 50 0001 C C + 1 2950 6200 + 0 -1 -1 0 +$EndComp +$Comp +L PINHD-1X3 JP11 +U 1 1 4808E9AB +P 2300 6200 +AR Path="/4808E9AB" Ref="JP11" +F 0 "JP11" H 2050 6425 50 0000 L B +F 1 "PINHD-1X3" H 2050 5900 50 0000 L B +F 2 "pinhead-1X03" H 2300 6350 50 0001 C C + 1 2300 6200 + 0 -1 -1 0 +$EndComp +Text Notes 2150 5450 0 60 ~ +USB AUX OUT +$Comp +L PINHD-1X4 JP10 +U 1 1 4808C81B +P 2650 5550 +AR Path="/4808C81B" Ref="JP10" +F 0 "JP10" H 2400 5875 50 0000 L B +F 1 "PINHD-1X4" H 2400 5250 50 0000 L B +F 2 "pinhead-1X04" H 2650 5700 50 0001 C C + 1 2650 5550 + 0 -1 -1 0 +$EndComp +Text Notes 2050 3700 0 60 ~ +GND +$Comp +L CONN_2 P1 +U 1 1 48061210 +P 2150 3850 +AR Path="/48061210" Ref="P1" +F 0 "P1" V 2100 3850 40 0000 C C +F 1 "CONN_2" V 2200 3850 40 0000 C C + 1 2150 3850 + 0 -1 -1 0 +$EndComp +$Comp +L GND #PWR05 +U 1 1 480611EF +P 2050 4450 +AR Path="/480611EF" Ref="#PWR05" +F 0 "#PWR05" H 2050 4450 30 0001 C C +F 1 "GND" H 2050 4380 30 0001 C C + 1 2050 4450 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR06 +U 1 1 480611ED +P 2250 4450 +AR Path="/480611ED" Ref="#PWR06" +F 0 "#PWR06" H 2250 4450 30 0001 C C +F 1 "GND" H 2250 4380 30 0001 C C + 1 2250 4450 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR07 +U 1 1 480611E8 +P 1750 7100 +AR Path="/480611E8" Ref="#PWR07" +F 0 "#PWR07" H 1750 7100 30 0001 C C +F 1 "GND" H 1750 7030 30 0001 C C + 1 1750 7100 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR08 +U 1 1 480611E3 +P 1000 7100 +AR Path="/480611E3" Ref="#PWR08" +F 0 "#PWR08" H 1000 7100 30 0001 C C +F 1 "GND" H 1000 7030 30 0001 C C + 1 1000 7100 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR09 +U 1 1 48048721 +P 6450 5750 +AR Path="/48048721" Ref="#PWR09" +F 0 "#PWR09" H 6450 5750 30 0001 C C +F 1 "GND" H 6450 5680 30 0001 C C + 1 6450 5750 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR010 +U 1 1 4804871F +P 6100 5750 +AR Path="/4804871F" Ref="#PWR010" +F 0 "#PWR010" H 6100 5750 30 0001 C C +F 1 "GND" H 6100 5680 30 0001 C C + 1 6100 5750 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR011 +U 1 1 48048718 +P 5750 5750 +AR Path="/48048718" Ref="#PWR011" +F 0 "#PWR011" H 5750 5750 30 0001 C C +F 1 "GND" H 5750 5680 30 0001 C C + 1 5750 5750 + 1 0 0 -1 +$EndComp +$Comp +L JUMPER JP6 +U 1 1 480486D0 +P 6450 5200 +AR Path="/480486D0" Ref="JP6" +F 0 "JP6" H 6450 5350 60 0000 C C +F 1 "JUMPER" H 6450 5120 40 0000 C C + 1 6450 5200 + 0 1 1 0 +$EndComp +$Comp +L JUMPER JP5 +U 1 1 480486CC +P 6100 5200 +AR Path="/480486CC" Ref="JP5" +F 0 "JP5" H 6100 5350 60 0000 C C +F 1 "JUMPER" H 6100 5120 40 0000 C C + 1 6100 5200 + 0 1 1 0 +$EndComp +$Comp +L JUMPER JP4 +U 1 1 480486B7 +P 5750 5200 +AR Path="/480486B7" Ref="JP4" +F 0 "JP4" H 5750 5350 60 0000 C C +F 1 "JUMPER" H 5750 5120 40 0000 C C + 1 5750 5200 + 0 1 1 0 +$EndComp +Text Notes 10600 2450 1 60 ~ +Keyboard Rows +Text Notes 10600 4150 1 60 ~ +Keyboard Columns +Text Notes 10600 4700 1 60 ~ +LEDs +Text Notes 9500 5700 0 60 ~ +ISP +Text Label 4900 2300 0 60 ~ +MISO +Text Label 8900 6350 0 60 ~ +MISO +Text Label 4900 2200 0 60 ~ +SCK +Text Label 8900 6250 0 60 ~ +SCK +Text Label 8900 5950 0 60 ~ +MOSI +Text Label 4900 2400 0 60 ~ +MOSI +Text Label 8900 6150 0 60 ~ +Reset +Text Label 2000 1300 0 60 ~ +Reset +Text Label 9450 2800 0 60 ~ +R15 +Text Label 9450 2700 0 60 ~ +R14 +Text Label 9450 2600 0 60 ~ +R13 +Text Label 9450 2500 0 60 ~ +R12 +Text Label 9450 2400 0 60 ~ +R11 +Text Label 9450 2300 0 60 ~ +R10 +Text Label 9450 2200 0 60 ~ +R9 +Text Label 9450 2100 0 60 ~ +R8 +Text Label 9450 2000 0 60 ~ +R7 +Text Label 9450 1900 0 60 ~ +R6 +Text Label 9450 1800 0 60 ~ +R5 +Text Label 9450 1700 0 60 ~ +R4 +Text Label 9450 1600 0 60 ~ +R3 +Text Label 9450 1500 0 60 ~ +R2 +Text Label 9450 1400 0 60 ~ +R1 +Text Label 9450 1300 0 60 ~ +R0 +Text Label 4600 3800 0 60 ~ +R15 +Text Label 4600 3700 0 60 ~ +R14 +Text Label 4600 3600 0 60 ~ +R13 +Text Label 4600 3500 0 60 ~ +R12 +Text Label 4600 3400 0 60 ~ +R11 +Text Label 4600 3300 0 60 ~ +R10 +Text Label 4600 3200 0 60 ~ +R9 +Text Label 4600 3100 0 60 ~ +R8 +Text Label 4600 1300 0 60 ~ +R7 +Text Label 4600 1400 0 60 ~ +R6 +Text Label 4600 1500 0 60 ~ +R5 +Text Label 4600 1600 0 60 ~ +R4 +Text Label 4600 1700 0 60 ~ +R3 +Text Label 4600 1800 0 60 ~ +R2 +Text Label 4600 1900 0 60 ~ +R1 +Text Label 4600 2000 0 60 ~ +R0 +Text Label 4600 2200 0 60 ~ +C7 +Text Label 4600 2300 0 60 ~ +C6 +Text Label 4600 2400 0 60 ~ +C5 +Text Label 4600 2500 0 60 ~ +C4 +Text Label 4600 2600 0 60 ~ +C3 +Text Label 4600 2700 0 60 ~ +C2 +Text Label 4600 2800 0 60 ~ +C1 +Text Label 4600 2900 0 60 ~ +C0 +Text Label 9450 3950 0 60 ~ +C7 +Text Label 9450 3850 0 60 ~ +C6 +Text Label 9450 3750 0 60 ~ +C5 +Text Label 9450 3650 0 60 ~ +C4 +Text Label 9450 3550 0 60 ~ +C3 +Text Label 9450 3450 0 60 ~ +C2 +Text Label 9450 3350 0 60 ~ +C1 +Text Label 9450 3250 0 60 ~ +C0 +Entry Wire Line + 9150 1200 9250 1300 +Entry Wire Line + 9150 1300 9250 1400 +Entry Wire Line + 9150 1400 9250 1500 +Entry Wire Line + 9150 1500 9250 1600 +Entry Wire Line + 9150 1600 9250 1700 +Entry Wire Line + 9150 1700 9250 1800 +Entry Wire Line + 9150 1800 9250 1900 +Entry Wire Line + 9150 1900 9250 2000 +Entry Wire Line + 9150 2000 9250 2100 +Entry Wire Line + 9150 2100 9250 2200 +Entry Wire Line + 9150 2200 9250 2300 +Entry Wire Line + 9150 2300 9250 2400 +Entry Wire Line + 9150 2400 9250 2500 +Entry Wire Line + 9150 2500 9250 2600 +Entry Wire Line + 9150 2600 9250 2700 +Entry Wire Line + 9150 2700 9250 2800 +Entry Wire Line + 8750 3150 8850 3250 +Entry Wire Line + 8750 3250 8850 3350 +Entry Wire Line + 8750 3350 8850 3450 +Entry Wire Line + 8750 3450 8850 3550 +Entry Wire Line + 8750 3550 8850 3650 +Entry Wire Line + 8750 3650 8850 3750 +Entry Wire Line + 8750 3750 8850 3850 +Entry Wire Line + 8750 3850 8850 3950 +Entry Wire Line + 5700 2900 5800 2800 +Entry Wire Line + 5700 2800 5800 2700 +Entry Wire Line + 5700 2700 5800 2600 +Entry Wire Line + 5700 2600 5800 2500 +Entry Wire Line + 5700 2500 5800 2400 +Entry Wire Line + 5700 2400 5800 2300 +Entry Wire Line + 5700 2300 5800 2200 +Entry Wire Line + 5700 2200 5800 2100 +Entry Wire Line + 5350 3800 5450 3700 +Entry Wire Line + 5350 3700 5450 3600 +Entry Wire Line + 5350 3600 5450 3500 +Entry Wire Line + 5350 3500 5450 3400 +Entry Wire Line + 5350 3400 5450 3300 +Entry Wire Line + 5350 3300 5450 3200 +Entry Wire Line + 5350 3200 5450 3100 +Entry Wire Line + 5350 3100 5450 3000 +Entry Wire Line + 5350 2000 5450 1900 +Entry Wire Line + 5350 1900 5450 1800 +Entry Wire Line + 5350 1800 5450 1700 +Entry Wire Line + 5350 1700 5450 1600 +Entry Wire Line + 5350 1600 5450 1500 +Entry Wire Line + 5350 1500 5450 1400 +Entry Wire Line + 5350 1400 5450 1300 +Entry Wire Line + 5350 1300 5450 1200 +$Comp +L GND #PWR012 +U 1 1 4804562B +P 10150 6550 +AR Path="/4804562B" Ref="#PWR012" +F 0 "#PWR012" H 10150 6550 30 0001 C C +F 1 "GND" H 10150 6480 30 0001 C C + 1 10150 6550 + 1 0 0 -1 +$EndComp +$Comp +L VCC #PWR013 +U 1 1 4804561C +P 10150 5800 +AR Path="/4804561C" Ref="#PWR013" +F 0 "#PWR013" H 10150 5900 30 0001 C C +F 1 "VCC" H 10150 5900 30 0000 C C + 1 10150 5800 + 1 0 0 -1 +$EndComp +NoConn ~ 9450 6050 +NoConn ~ 3400 5000 +NoConn ~ 2400 2500 +NoConn ~ 3400 1000 +$Comp +L VCC #PWR014 +U 1 1 480450B7 +P 4300 5850 +AR Path="/480450B7" Ref="#PWR014" +F 0 "#PWR014" H 4300 5950 30 0001 C C +F 1 "VCC" H 4300 5950 30 0000 C C + 1 4300 5850 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR015 +U 1 1 4804509C +P 9450 4500 +AR Path="/4804509C" Ref="#PWR015" +F 0 "#PWR015" H 9450 4500 30 0001 C C +F 1 "GND" H 9450 4430 30 0001 C C + 1 9450 4500 + 0 1 1 0 +$EndComp +$Comp +L VCC #PWR016 +U 1 1 48044F99 +P 1150 1100 +AR Path="/48044F99" Ref="#PWR016" +F 0 "#PWR016" H 1150 1200 30 0001 C C +F 1 "VCC" H 1150 1200 30 0000 C C + 1 1150 1100 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR017 +U 1 1 48044DD0 +P 800 7100 +AR Path="/48044DD0" Ref="#PWR017" +F 0 "#PWR017" H 800 7100 30 0001 C C +F 1 "GND" H 800 7030 30 0001 C C + 1 800 7100 + 1 0 0 -1 +$EndComp +$Comp +L VCC #PWR018 +U 1 1 48044DC6 +P 800 6350 +AR Path="/48044DC6" Ref="#PWR018" +F 0 "#PWR018" H 800 6450 30 0001 C C +F 1 "VCC" H 800 6450 30 0000 C C + 1 800 6350 + 1 0 0 -1 +$EndComp +$Comp +L VCC #PWR019 +U 1 1 48044DB8 +P 1550 3600 +AR Path="/48044DB8" Ref="#PWR019" +F 0 "#PWR019" H 1550 3700 30 0001 C C +F 1 "VCC" H 1550 3700 30 0000 C C + 1 1550 3600 + 1 0 0 -1 +$EndComp +$Comp +L VCC #PWR020 +U 1 1 48044DB5 +P 1000 3600 +AR Path="/48044DB5" Ref="#PWR020" +F 0 "#PWR020" H 1000 3700 30 0001 C C +F 1 "VCC" H 1000 3700 30 0000 C C + 1 1000 3600 + 1 0 0 -1 +$EndComp +$Comp +L VCC #PWR021 +U 1 1 48044D21 +P 3300 800 +AR Path="/48044D21" Ref="#PWR021" +F 0 "#PWR021" H 3300 900 30 0001 C C +F 1 "VCC" H 3300 900 30 0000 C C + 1 3300 800 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR022 +U 1 1 48044A9D +P 5000 7550 +AR Path="/48044A9D" Ref="#PWR022" +F 0 "#PWR022" H 5000 7550 30 0001 C C +F 1 "GND" H 5000 7480 30 0001 C C + 1 5000 7550 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR023 +U 1 1 48044A97 +P 4650 7550 +AR Path="/48044A97" Ref="#PWR023" +F 0 "#PWR023" H 4650 7550 30 0001 C C +F 1 "GND" H 4650 7480 30 0001 C C + 1 4650 7550 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR024 +U 1 1 48044A95 +P 4300 7550 +AR Path="/48044A95" Ref="#PWR024" +F 0 "#PWR024" H 4300 7550 30 0001 C C +F 1 "GND" H 4300 7480 30 0001 C C + 1 4300 7550 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR025 +U 1 1 48044A93 +P 1550 4450 +AR Path="/48044A93" Ref="#PWR025" +F 0 "#PWR025" H 1550 4450 30 0001 C C +F 1 "GND" H 1550 4380 30 0001 C C + 1 1550 4450 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR026 +U 1 1 48044A8D +P 1000 4450 +AR Path="/48044A8D" Ref="#PWR026" +F 0 "#PWR026" H 1000 4450 30 0001 C C +F 1 "GND" H 1000 4380 30 0001 C C + 1 1000 4450 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR027 +U 1 1 48044A8A +P 3300 5150 +AR Path="/48044A8A" Ref="#PWR027" +F 0 "#PWR027" H 3300 5150 30 0001 C C +F 1 "GND" H 3300 5080 30 0001 C C + 1 3300 5150 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR028 +U 1 1 48044A88 +P 850 1700 +AR Path="/48044A88" Ref="#PWR028" +F 0 "#PWR028" H 850 1700 30 0001 C C +F 1 "GND" H 850 1630 30 0001 C C + 1 850 1700 + 0 1 1 0 +$EndComp +$Comp +L GND #PWR029 +U 1 1 48044A82 +P 850 2300 +AR Path="/48044A82" Ref="#PWR029" +F 0 "#PWR029" H 850 2300 30 0001 C C +F 1 "GND" H 850 2230 30 0001 C C + 1 850 2300 + 0 1 1 0 +$EndComp +$Comp +L PINHD-2X5M JP7 +U 1 1 48044A4D +P 9550 6150 +AR Path="/48044A4D" Ref="JP7" +F 0 "JP7" H 9300 6475 50 0000 L B +F 1 "PINHD-2X5M" H 9300 5750 50 0000 L B +F 2 "pinhead-2X05M" H 9550 6300 50 0001 C C + 1 9550 6150 + 1 0 0 -1 +$EndComp +$Comp +L PINHD-1X4M JP3 +U 1 1 48044A1E +P 10050 4600 +AR Path="/48044A1E" Ref="JP3" +F 0 "JP3" H 9800 4925 50 0000 L B +F 1 "PINHD-1X4M" H 9800 4300 50 0000 L B +F 2 "pinhead-1X04M" H 10050 4750 50 0001 C C + 1 10050 4600 + 1 0 0 -1 +$EndComp +$Comp +L PINHD-1X8M JP2 +U 1 1 48044A0E +P 10050 3650 +AR Path="/48044A0E" Ref="JP2" +F 0 "JP2" H 9800 4175 50 0000 L B +F 1 "PINHD-1X8M" H 9800 3150 50 0000 L B +F 2 "pinhead-1X08M" H 10050 3800 50 0001 C C + 1 10050 3650 + 1 0 0 -1 +$EndComp +$Comp +L PINHD-1X16M JP1 +U 1 1 48044A02 +P 10050 2000 +AR Path="/48044A02" Ref="JP1" +F 0 "JP1" H 10100 2050 50 0000 L B +F 1 "PINHD-1X16M" H 9800 1000 50 0000 L B +F 2 "pinhead-1X16M" H 10050 2150 50 0001 C C + 1 10050 2000 + 1 0 0 -1 +$EndComp +$Comp +L MEGA32-P IC1 +U 1 1 480447FC +P 3400 3000 +AR Path="/480447FC" Ref="IC1" +F 0 "IC1" H 3450 3050 50 0000 L B +F 1 "MEGA32-P" H 3600 1000 50 0000 L B +F 2 "atmel-DIL40" H 3400 3150 50 0001 C C + 1 3400 3000 + 1 0 0 -1 +$EndComp +$Comp +L ZENER D2 +U 1 1 48044798 +P 5000 7200 +AR Path="/48044798" Ref="D2" +F 0 "D2" H 5000 7300 50 0000 C C +F 1 "3.6V" H 5000 7100 40 0000 C C + 1 5000 7200 + 0 -1 -1 0 +$EndComp +$Comp +L ZENER D1 +U 1 1 48044791 +P 4650 7200 +AR Path="/48044791" Ref="D1" +F 0 "D1" H 4650 7300 50 0000 C C +F 1 "3.6V" H 4650 7100 40 0000 C C + 1 4650 7200 + 0 -1 -1 0 +$EndComp +$Comp +L CRYSTAL X1 +U 1 1 4804477C +P 1900 2000 +AR Path="/4804477C" Ref="X1" +F 0 "X1" H 1900 2150 60 0000 C C +F 1 "12MHz" H 1900 1850 60 0000 C C + 1 1900 2000 + 0 1 1 0 +$EndComp +$Comp +L CP C2 +U 1 1 48044769 +P 1550 4000 +AR Path="/48044769" Ref="C2" +F 0 "C2" H 1600 4100 50 0000 L C +F 1 "10u" H 1600 3900 50 0000 L C + 1 1550 4000 + 1 0 0 1 +$EndComp +$Comp +L C C1 +U 1 1 48044756 +P 1000 4000 +AR Path="/48044756" Ref="C1" +F 0 "C1" H 1050 4100 50 0000 L C +F 1 "100n" H 1050 3900 50 0000 L C + 1 1000 4000 + 1 0 0 1 +$EndComp +$Comp +L C C4 +U 1 1 48044754 +P 1350 2300 +AR Path="/48044754" Ref="C4" +F 0 "C4" H 1400 2400 50 0000 L C +F 1 "22p" H 1400 2200 50 0000 L C + 1 1350 2300 + 0 1 1 0 +$EndComp +$Comp +L C C3 +U 1 1 48044750 +P 1350 1700 +AR Path="/48044750" Ref="C3" +F 0 "C3" H 1400 1800 50 0000 L C +F 1 "22p" H 1400 1600 50 0000 L C + 1 1350 1700 + 0 1 1 0 +$EndComp +$Comp +L R R8 +U 1 1 48044743 +P 5450 6150 +AR Path="/48044743" Ref="R8" +F 0 "R8" V 5530 6150 50 0000 C C +F 1 "68" V 5450 6150 50 0000 C C + 1 5450 6150 + -1 0 0 1 +$EndComp +$Comp +L R R7 +U 1 1 48044741 +P 5250 6150 +AR Path="/48044741" Ref="R7" +F 0 "R7" V 5330 6150 50 0000 C C +F 1 "68" V 5250 6150 50 0000 C C + 1 5250 6150 + -1 0 0 1 +$EndComp +$Comp +L R R1 +U 1 1 4804473F +P 1650 1300 +AR Path="/4804473F" Ref="R1" +F 0 "R1" V 1730 1300 50 0000 C C +F 1 "10k" V 1650 1300 50 0000 C C + 1 1650 1300 + 0 1 1 0 +$EndComp +$Comp +L R R2 +U 1 1 4804473B +P 4300 6200 +AR Path="/4804473B" Ref="R2" +F 0 "R2" V 4380 6200 50 0000 C C +F 1 "2k2" V 4300 6200 50 0000 C C + 1 4300 6200 + 1 0 0 -1 +$EndComp +$Comp +L R R6 +U 1 1 48044739 +P 4300 7150 +AR Path="/48044739" Ref="R6" +F 0 "R6" V 4380 7150 50 0000 C C +F 1 "4k7" V 4300 7150 50 0000 C C + 1 4300 7150 + 1 0 0 -1 +$EndComp +$Comp +L R R3 +U 1 1 48044733 +P 8800 4400 +AR Path="/48044733" Ref="R3" +F 0 "R3" V 8880 4400 50 0000 C C +F 1 "470" V 8800 4400 50 0000 C C + 1 8800 4400 + 0 1 1 0 +$EndComp +$Comp +L R R4 +U 1 1 48044731 +P 8800 4600 +AR Path="/48044731" Ref="R4" +F 0 "R4" V 8880 4600 50 0000 C C +F 1 "470" V 8800 4600 50 0000 C C + 1 8800 4600 + 0 1 1 0 +$EndComp +$Comp +L R R5 +U 1 1 4804472B +P 8800 4800 +AR Path="/4804472B" Ref="R5" +F 0 "R5" V 8880 4800 50 0000 C C +F 1 "470" V 8800 4800 50 0000 C C + 1 8800 4800 + 0 1 1 0 +$EndComp +$Comp +L USB J1 +U 1 1 480446AA +P 1400 6400 +AR Path="/480446AA" Ref="J1" +F 0 "J1" H 1350 6800 60 0000 C C +F 1 "USB" V 1150 6550 60 0000 C C + 1 1400 6400 + 1 0 0 -1 +$EndComp +$EndSCHEMATC diff --git a/circuit/dulcimer.stf b/circuit/dulcimer.stf new file mode 100644 index 0000000..113f9ea --- /dev/null +++ b/circuit/dulcimer.stf @@ -0,0 +1,24 @@ +comp = "C1" module = "C1" +comp = "C2" module = "CP36V" +comp = "C3" module = "C1" +comp = "C4" module = "C1" +comp = "D1" module = "D3" +comp = "D2" module = "D3" +comp = "IC1" module = "atmel-DIL40" +comp = "J1" module = "PIN_ARRAY_2X2" +comp = "JP1" module = "pinhead-1X16M" +comp = "JP2" module = "pinhead-1X08M" +comp = "JP3" module = "pinhead-1X04M" +comp = "JP4" module = "PIN_ARRAY_2X1" +comp = "JP5" module = "PIN_ARRAY_2X1" +comp = "JP6" module = "PIN_ARRAY_2X1" +comp = "JP7" module = "pinhead-2X05M" +comp = "R1" module = "R3" +comp = "R2" module = "R3" +comp = "R3" module = "R3" +comp = "R4" module = "R3" +comp = "R5" module = "R3" +comp = "R6" module = "R3" +comp = "R7" module = "R3" +comp = "R8" module = "R3" +comp = "X1" module = "2PIN_6mm" diff --git a/firmware/Makefile b/firmware/Makefile new file mode 100644 index 0000000..85049b4 --- /dev/null +++ b/firmware/Makefile @@ -0,0 +1,59 @@ +# Name: Makefile +# Project: HIDKeys +# Author: Christian Starkjohann +# Creation Date: 2006-02-02 +# Tabsize: 4 +# Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH +# License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) +# This Revision: $Id: Makefile,v 1.1 2008/07/09 20:47:12 rschaten Exp $ + +AVRDUDE = avrdude -p atmega32 -c usbasp + +COMPILE = avr-gcc -Wall -Os -Iusbdrv -I. -mmcu=atmega32 #-DDEBUG_LEVEL=1 + +OBJECTS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o + + +# symbolic targets: +all: main.hex + +.c.o: + $(COMPILE) -std=c99 -c $< -o $@ + +.S.o: + $(COMPILE) -x assembler-with-cpp -c $< -o $@ +# "-x assembler-with-cpp" should not be necessary since this is the default +# file type for the .S (with capital S) extension. However, upper case +# characters are not always preserved on Windows. To ensure WinAVR +# compatibility define the file type manually. + +.c.s: + $(COMPILE) -S $< -o $@ + +program: all + $(AVRDUDE) -U flash:w:main.hex + +fuses: + # - enable crystal + # - disable JTAG, so we can fully use PORTC + $(AVRDUDE) -U lfuse:w:0xCF:m -U hfuse:w:0xD9:m + +clean: + rm -f main.hex main.lst main.obj main.cof main.list main.map main.eep.hex main.bin *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s + +# file targets: +main.bin: $(OBJECTS) + $(COMPILE) -o main.bin $(OBJECTS) + +main.hex: main.bin + rm -f main.hex main.eep.hex + avr-objcopy -j .text -j .data -O ihex main.bin main.hex + ./checksize main.bin +# do the checksize script as our last action to allow successful compilation +# on Windows with WinAVR where the Unix commands will fail. + +disasm: main.bin + avr-objdump -d main.bin + +cpp: + $(COMPILE) -E main.c diff --git a/firmware/checksize b/firmware/checksize new file mode 100644 index 0000000..ffaafc2 --- /dev/null +++ b/firmware/checksize @@ -0,0 +1,35 @@ +#!/bin/sh +# Name: checksize +# Project: AVR-USB +# Author: Christian Starkjohann +# Creation Date: 2004-12-29 +# Tabsize: 4 +# Copyright: (c) 2005 OBJECTIVE DEVELOPMENT Software GmbH. +# Revision: $Id: checksize,v 1.1 2008/07/09 20:47:12 rschaten Exp $ + +error=0 +codelimit=32768 +datalimit=1920 # leave 128 bytes for stack + +if [ $# -gt 1 ]; then + codelimit="$2" +fi +if [ $# -gt 2 ]; then + datalimit="$3" +fi + +set -- `avr-size -d "$1" | awk '/[0-9]/ {print $1 + $2, $2 + $3, $2}'` +if [ $1 -gt $codelimit ]; then + echo "*** code size $1 exceeds limit of $codelimit" + error=1 +else + echo "ROM: $1 bytes (data=$3)" +fi +if [ $2 -gt $datalimit ]; then + echo "*** data size $2 exceeds limit of $datalimit" + error=1 +else + echo "RAM: $2 bytes" +fi + +exit $error diff --git a/firmware/keycodes.h b/firmware/keycodes.h new file mode 100644 index 0000000..288d830 --- /dev/null +++ b/firmware/keycodes.h @@ -0,0 +1,137 @@ +/** + * \file firmware/keycodes.h + * \brief This file contains modifier- and keycode definitions according to the + * USB-specifications for human interface devices. + * + * See usb.org's HID-usage-tables document, chapter 10 Keyboard/Keypad Page for + * more codes: http://www.usb.org/developers/devclass_docs/Hut1_12.pdf + * \author Ronald Schaten + * \version $Id: keycodes.h,v 1.1 2008/07/09 20:47:12 rschaten Exp $ + * + * License: GNU GPL v2 (see License.txt) + */ + +#ifndef __keycodes_h_included__ +#define __keycodes_h_included__ + +/// Codes for modifier-keys. +enum modifiers { + MOD_NONE = 0, + MOD_CONTROL_LEFT = (1 << 0), + MOD_SHIFT_LEFT = (1 << 1), + MOD_ALT_LEFT = (1 << 2), + MOD_GUI_LEFT = (1 << 3), + MOD_CONTROL_RIGHT = (1 << 4), + MOD_SHIFT_RIGHT = (1 << 5), + MOD_ALT_RIGHT = (1 << 6), + MOD_GUI_RIGHT = (1 << 7), +}; + +/// Codes for non-modifier-keys. +enum keycodes { + KEY_Reserved = 0, // unused + KEY_ErrorRollOver, + KEY_POSTFail, + KEY_ErrorUndefined, + KEY_A, // 0x04 + KEY_B, + KEY_C, + KEY_D, + KEY_E, + KEY_F, + KEY_G, + KEY_H, + KEY_I, + KEY_J, + KEY_K, + KEY_L, + KEY_M, // 0x10 + KEY_N, + KEY_O, + KEY_P, + KEY_Q, + KEY_R, + KEY_S, + KEY_T, + KEY_U, + KEY_V, + KEY_W, + KEY_X, + KEY_Y, + KEY_Z, + KEY_1, // 1 and ! + KEY_2, // 2 and @ + KEY_3, // 0x20 3 and # + KEY_4, // 4 and $ + KEY_5, // 5 and % + KEY_6, // 6 and ^ + KEY_7, // 7 and & + KEY_8, // 8 and * + KEY_9, // 9 and ( + KEY_0, // 0x27 0 and ) + KEY_Return, // 0x28 enter + KEY_ESCAPE, // 0x29 + KEY_DELETE, // 0x2A backspace + KEY_Tab, // 0x2B + KEY_Spacebar, // 0x2C + KEY_minus, // 0x2D - and _ + KEY_equals, // 0x2E = and + + KEY_lbracket, // 0x2F [ and { + KEY_rbracket, // 0x30 ] and } + KEY_backslash, // 0x31 \ and | + KEY_hash, // 0x32 non-US # and ~ + KEY_semicolon, // 0x33 ; and : + KEY_apostroph, // 0x34 ' and " + KEY_grave, // 0x35 grave accent and tilde + KEY_comma, // 0x36 , and < + KEY_dot, // 0x37 . and > + KEY_slash, // 0x38 / and ? + KEY_capslock, // 0x39 + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, // 0x40 + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, + KEY_PrintScreen, + KEY_ScrollLock, + KEY_Pause, // Break + KEY_Insert, + KEY_Home, + KEY_PageUp, + KEY_DeleteForward, + KEY_End, + KEY_PageDown, + KEY_RightArrow, + KEY_LeftArrow, // 0x50 + KEY_DownArrow, + KEY_UpArrow, + KEY_NumLock, // Clear + KEY_KPslash, + KEY_KPasterisk, + KEY_KPminus, + KEY_KPplus, + KEY_KPenter, + KEY_KP1, // End + KEY_KP2, // Down Arrow + KEY_KP3, // Page Down + KEY_KP4, // Left Arrow + KEY_KP5, + KEY_KP6, // Right Arrow + KEY_KP7, // Home + KEY_KP8, // 0x60 Up Arrow + KEY_KP9, // Page Up + KEY_KP0, // Insert + KEY_KPcomma, // Delete + KEY_Euro, // non-US \ and | + KEY_Application, +}; + +#endif /* __keycodes_h_included__ */ + diff --git a/firmware/main.c b/firmware/main.c new file mode 100644 index 0000000..e27639c --- /dev/null +++ b/firmware/main.c @@ -0,0 +1,785 @@ +/** + * \file firmware/main.c + * \brief Main functions for USB-keyboard + * \author Ronald Schaten + * \version $Id: main.c,v 1.1 2008/07/09 20:47:12 rschaten Exp $ + * + * License: GNU GPL v2 (see License.txt) + */ + +/** + * \mainpage Dulcimer + * + * \section sec_intro Introduction + * + * A computer keyboard can be a very personal utensil. Especially if it is an + * extraordinary well built one, like for example the IBM Model M. The Model M + * design dates back to 1984, but it still has many fans even nowadays. It came + * with the usual keyboard connectors. First the old 5-pin one, later a PS/2 + * plug. Unfortunately is that, at least to my knowledge, they never released a + * version with USB. + * + * A friend of mine knew that I already had built other USB-devices, and one of + * them even acted as a keyboard (it isn't really a keyboard, but that's a + * different story... ;-) ). He is a big fan of the Model M, so he asked if I + * could put new life in one of his old keyboards, which had a broken circuit + * inside. And this is the result... + * + * \subsection sec_technique Hard- and Software + * + * The main part of a computer keyboard circuit is the key matrix. You can + * imagine it as a number of keys, placed on a raster of horizontal (rows) and + * vertical (columns) wires. In the case of a Model M keyboard, we have a + * matrix of 8x16 lines. Eight columns in 16 rows, or the other way around, + * depending on how you look at it. Each key is connected to one column and one + * row. If you press the key, it will connect the column and the row on it's + * crossing of the lines. + * + * Connected to this matrix is a keyboard controller. That's a chip with a + * number of I/O-lines to detect the state of the matrix, and on the other side + * an interface that enables it to talk to the computer. Oh, and not to forget: + * it also has three output lines to drive the LEDs for Num-, Caps- and + * Scroll-lock. + * + * What I did in this project is, that I dumped the keyboard controller chip + * and its circuit, and replaced it by an ATmega32 and my own circuit. The + * ATmega scans the matrix for keyboard activity, controls the LEDs and talks + * to the computer. + * + * For further convenience, I added a boot-loader. With that, it is possible to + * update the keyboard's firmware without disassembling it, and without the + * need for a dedicated programmer. + * + * \subsection sec_hardware Other hardware? + * + * As mentioned, the controller in this project is just connected to an + * ordinary keyboard matrix. You can find this kind of matrix in all kinds of + * keyboards, from key-telephones over good old hardware like the Commodore + * C=64 or the Schneider CPC, keyboards with non-PC-connectors like those made + * by Sun, to modern hardware that could need a few more features. + * + * Till now, I just made a PCB layout for the IBM Model M, but I intend to + * modify at least a Sun keyboard. In order to do that, I expect having to + * refactor the key-scanning, since the key-matrix is not 16x8. The positions + * of the keys on the matrix will be different, I'll have to re-engineer that. + * And of course, I'll have to make another PCB. + * + * \subsection sec_features Features + * + * At the moment, the keyboard should be able to do everything that the average + * off-the-shelf-keyboard can do. But there are many features that are + * possible, regarding the fact that the ATmega32 is absolutely bored till now. + * You can think of 'magic keystrokes' that turn some hidden features on or + * off, like for example: + * - send complete phrases on one keystroke + * - 'autofire' feature on keys that don't repeat normally, for example Alt+F4 + * - change keyboard layout without reconfiguring the computer + * - turn bouncing keys on or off, to annoy other people using your computer + * - random caps lock function + * - use arrow keys as mouse, without having to include a special driver in + * the OS. + * + * With a little tweaking on the hardware side, there should be even more + * possibilities: + * - turn the oldtimer-keyboard into a supermodern wireless bluetooth one + * - implement keylogger-funktionality, using for example an SD-card + * - include an USB-hub into the keyboard + * + * If you are just a little like me, it won't take you much brainstorming to + * come up with one useful -- or even better: useless -- ideas. ;-) + * + * \section sec_install Building and installing + * + * Both, the bootloader and firmware are simply built with "make". You may need + * to customize both makefiles to fit to your system. If you don't want to add + * new features, you don't need to build the software yourself. You can use the + * hex-files included in this package. + * + * \subsection sec_boot Bootloader + * + * I used the USBaspLoader from Objective Development, the same guys that wrote + * the AVR-USB-driver: http://www.obdev.at/products/avrusb/usbasploader.html + * + * The reason why I chose this over some other available USB-bootloaders is, + * that this one emulates a common ISP-programmer that is supported by avrdude. + * In this way, the same program can be used to program the chip that is used + * without a bootloader. + * + * To prepare the ATmega32, you have to connect it to your computer with the + * ISP-programmer of your choice and modify the makefile according to that. + * Then you enter the bootloader-directory and enter the following line: + * + * \code + * make fuse && make flash && make lock + * \endcode + * + * With 'fuse' you prepare the fuse-bits of your AVR, 'flash' transfers the + * bootloader to the device and 'lock' prevents you from overwriting the + * bootloader. Don't fear the locking: you can always reset it with your + * ordinary programmer. In fact, it is disabled in the moment you use your + * ordinary programmer to reflash the device, even without any special + * parameters. The locking only affects the bootloader behavior. + * + * Afterwards you can put the programmer back into the toolbox, you won't need + * it from here on. + * + * When you plug in the device while holding the minus-key on the number-keypad + * pressed, the keyboard indicates that it would like to get a new firmware by + * showing a running light on the LEDs. That firmware will be flashed over the + * normal USB-cable that the keyboard is connected with. + * + * \subsection sec_fw Firmware + * + * If you intend to recompile the firmware yourself, you will need avr-gcc and + * avr-libc (a C-library for the AVR controller). Please read the instructions + * at http://www.nongnu.org/avr-libc/user-manual/install_tools.html for how to + * install the GNU toolchain (avr-gcc, assembler, linker etc.) and avr-libc. + * + * Once you have the GNU toolchain for AVR microcontrollers installed, you can + * run "make" in the subdirectory "firmware". + * + * Afterwards -- or if you decided not to compile the firmware yourself -- you + * can flash it to the device: + * + * \code + * make program + * \endcode + * + * Remember that you have to start the bootloader at first: unplug the + * keyboard, hold the minus-key on the number-keypad pressed and replug it. If + * the modified keyboard is the only one within reach: good luck! ;-) + * + * \section sec_usage Usage + * + * Connect the keyboard to the USB-port. All LED should flash up to indicate + * that the device is initialized. + * + * Then you can use the keyboard as always. If additional features get + * implemented, you will be able to use them in their respective ways. + * + * \section sec_drawbacks Drawbacks + * + * I don't know if and how keyboard manufacturers face the problem of ghost + * keys, I didn't take special measurements for this. I hope that the engineers + * at IBM distributed the keys on the matrix in a way that minimizes this + * problem. + * + * \section sec_files Files in the distribution + * + * - \e Readme.txt: Documentation, created from the htmldoc-directory. + * - \e firmware: Source code of the controller firmware. + * - \e firmware/usbdrv: USB driver -- See Readme.txt in this directory for + * info. + * - \e bootloader: The USBaspLoader, properly configured for this project. I + * only modified the bootloaderconfig.h and the Makefile. + * - \e USBaspLoader.2008-02-05.tar.gz: The unmodified bootloader sources, for + * reference. + * - \e circuit: Circuit diagrams in PDF and KiCAD format. KiCAD is a free + * schematic- and layout-tool, you can learn more about it at its homepage: + * http://www.lis.inpg.fr/realise_au_lis/kicad/ + * - \e License.txt: Public license for all contents of this project, except + * for the USB driver. Look in firmware/usbdrv/License.txt for further info. + * - \e Changelog.txt: Logfile documenting changes in soft-, firm- and + * hardware. + * - \e refman.pdf: Full documentation of the software. + * + * \section sec_thanks Thanks! + * + * I'd like to thank Objective Development for the possibility to use + * their driver for my project. In fact, this project wouldn't exist without + * the driver. + * + * \section sec_license About the license + * + * My work - all contents except for the USB driver - is licensed under the GNU + * General Public License (GPL). A copy of the GPL is included in License.txt. + * The driver itself is licensed under a special license by Objective + * Development. See firmware/usbdrv/License.txt for further info. + * + * (c) 2008 by Ronald Schaten - http://www.schatenseite.de + */ + +#define F_CPU 12000000L ///< we use a 12MHz crystal + +#include +#include +#include +#include +#include +#include +#include + +#include "usbdrv.h" +#include "keycodes.h" + +/* ----------------------- hardware I/O abstraction ------------------------ */ + + +#define PORTCOLUMNS PORTB ///< port on which we read the state of the columns +#define PINCOLUMNS PINB ///< port on which we read the state of the columns +#define DDRCOLUMNS DDRB ///< port on which we read the state of the columns +#define PORTROWS1 PORTA ///< first port connected to the matrix rows +#define PINROWS1 PINA ///< first port connected to the matrix rows +#define DDRROWS1 DDRA ///< first port connected to the matrix rows +#define PORTROWS2 PORTC ///< second port connected to the matrix rows +#define PINROWS2 PINC ///< second port connected to the matrix rows +#define DDRROWS2 DDRC ///< second port connected to the matrix rows + +#define PORTLEDS PORTD ///< port on which the LEDs are connected +#define PINLEDS PIND ///< port on which the LEDs are connected +#define DDRLEDS DDRD ///< port on which the LEDs are connected +#define LEDSCROLL PIND4 ///< address of the scroll-lock LED +#define LEDCAPS PIND5 ///< address of the caps-lock LED +#define LEDNUM PIND6 ///< address of the num-lock LED + +#define PORTJUMPERS PORTD ///< port for additional jumpers +#define PINJUMPERS PIND ///< port for additional jumpers +#define DDRJUMPERS DDRD ///< port for additional jumpers +#define JUMPER0 PD1 ///< address for jumper 0 +#define JUMPER1 PD3 ///< address for jumper 1 +#define JUMPER2 PD7 ///< address for jumper 2 + +/** + * Initialize hardware. Configure ports as inputs and outputs, set USB reset + * condition, start timer and blink LEDs. + */ +static void hardwareInit(void) { + // column-port is input + PORTCOLUMNS = 0xff; + DDRCOLUMNS = 0x00; + + // row-ports are output + PORTROWS1 = 0xff; + DDRROWS1 = 0x00; + PORTROWS2 = 0xff; + DDRROWS2 = 0x00; + + // port D contains USB (D0, D2), + // LEDs (D4, D5, D6) + // and Jumpers (D1, D3, D7), + // so we call it PORTD instead of PORTJUMPERS or PORTLEDS + PORTD = 0xfa; // 1000 1010: activate pull-ups except on USB- and LED-lines + DDRD = 0x75; // 0111 0101: all pins input except USB (-> USB reset) and LED-pins + // USB Reset by device only required on Watchdog Reset + _delay_us(11); // delay >10ms for USB reset + DDRD = 0x70; // 0111 0000 bin: remove USB reset condition + + // configure timer 0 for a rate of 12M/(1024 * 256) = 45.78Hz (~22ms) + TCCR0 = 5; // timer 0 prescaler: 1024 + + // blink, to indicate power-on + PORTLEDS &= ~((1 << LEDNUM) | (1 << LEDCAPS) | (1 << LEDSCROLL)); + _delay_ms(50); + PORTLEDS |= ((1 << LEDNUM) | (1 << LEDCAPS) | (1 << LEDSCROLL)); +} + +/* ------------------------------------------------------------------------- */ +/* ----------------------------- USB interface ----------------------------- */ +/* ------------------------------------------------------------------------- */ + +static uint8_t reportBuffer[8]; ///< buffer for HID reports +static uint8_t idleRate; ///< in 4ms units +static uint8_t protocolVer = 1; ///< 0 = boot protocol, 1 = report protocol +uint8_t expectReport = 0; ///< flag to indicate if we expect an USB-report + +#define LED_NUM 0x01 ///< num LED on a boot-protocol keyboard +#define LED_CAPS 0x02 ///< caps LED on a boot-protocol keyboard +#define LED_SCROLL 0x04 ///< scroll LED on a boot-protocol keyboard +#define LED_COMPOSE 0x08 ///< compose LED on a boot-protocol keyboard +#define LED_KANA 0x10 ///< kana LED on a boot-protocol keyboard +uint8_t LEDstate = 0; ///< current state of the LEDs + +/** USB report descriptor (length is defined in usbconfig.h). The report + * descriptor has been created with usb.org's "HID Descriptor Tool" which can + * be downloaded from http://www.usb.org/developers/hidpage/ (it's an .exe, but + * it even runs under Wine). + */ +char PROGMEM usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x06, // USAGE (Keyboard) + 0xa1, 0x01, // COLLECTION (Application) + 0x05, 0x07, // USAGE_PAGE (Keyboard) + 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) + 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x08, // REPORT_COUNT (8) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x08, // REPORT_SIZE (8) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) + 0x95, 0x05, // REPORT_COUNT (5) + 0x75, 0x01, // REPORT_SIZE (1) + 0x05, 0x08, // USAGE_PAGE (LEDs) + 0x19, 0x01, // USAGE_MINIMUM (Num Lock) + 0x29, 0x05, // USAGE_MAXIMUM (Kana) + 0x91, 0x02, // OUTPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x03, // REPORT_SIZE (3) + 0x91, 0x03, // OUTPUT (Cnst,Var,Abs) + 0x95, 0x06, // REPORT_COUNT (6) + 0x75, 0x08, // REPORT_SIZE (8) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x65, // LOGICAL_MAXIMUM (101) + 0x05, 0x07, // USAGE_PAGE (Keyboard) + 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) + 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) + 0x81, 0x00, // INPUT (Data,Ary,Abs) + 0xc0 // END_COLLECTION +}; + +/** + * This function is called whenever we receive a setup request via USB. + * \param data[8] eight bytes of data we received + * \return number of bytes to use, or 0xff if usbFunctionWrite() should be + * called + */ +uint8_t usbFunctionSetup(uint8_t data[8]) { + usbRequest_t *rq = (void *)data; + usbMsgPtr = reportBuffer; + if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) { + // class request type + if (rq->bRequest == USBRQ_HID_GET_REPORT) { + // wValue: ReportType (highbyte), ReportID (lowbyte) + // we only have one report type, so don't look at wValue + return sizeof(reportBuffer); + } else if (rq->bRequest == USBRQ_HID_SET_REPORT) { + if (rq->wLength.word == 1) { + // We expect one byte reports + expectReport = 1; + return 0xff; // Call usbFunctionWrite with data + } + } else if (rq->bRequest == USBRQ_HID_GET_IDLE) { + usbMsgPtr = &idleRate; + return 1; + } else if (rq->bRequest == USBRQ_HID_SET_IDLE) { + idleRate = rq->wValue.bytes[1]; + } else if (rq->bRequest == USBRQ_HID_GET_PROTOCOL) { + if (rq->wValue.bytes[1] < 1) { + protocolVer = rq->wValue.bytes[1]; + } + } else if(rq->bRequest == USBRQ_HID_SET_PROTOCOL) { + usbMsgPtr = &protocolVer; + return 1; + } + } else { + // no vendor specific requests implemented + } + return 0; +} + +/** + * The write function is called when LEDs should be set. Normally, we get only + * one byte that contains info about the LED states. + * \param data pointer to received data + * \param len number ob bytes received + * \return 0x01 + */ +uint8_t usbFunctionWrite(uchar *data, uchar len) { + if (expectReport && (len == 1)) { + LEDstate = data[0]; // Get the state of all 5 LEDs + if (LEDstate & LED_NUM) { // light up caps lock + PORTLEDS &= ~(1 << LEDNUM); + } else { + PORTLEDS |= (1 << LEDNUM); + } + if (LEDstate & LED_CAPS) { // light up caps lock + PORTLEDS &= ~(1 << LEDCAPS); + } else { + PORTLEDS |= (1 << LEDCAPS); + } + if (LEDstate & LED_SCROLL) { // light up caps lock + PORTLEDS &= ~(1 << LEDSCROLL); + } else { + PORTLEDS |= (1 << LEDSCROLL); + } + } + expectReport = 0; + return 0x01; +} + +/** + * Send a single report to the computer. This function is not used during + * normal typing, it is only used to send non-pressed keys to simulate input. + * \param mode modifier-byte + * \param key key-code + */ +void usbSendReport(uint8_t mode, uint8_t key) { + reportBuffer[0] = mode; + reportBuffer[1] = key; + while (!usbInterruptIsReady()); // wait + usbSetInterrupt(reportBuffer, sizeof(reportBuffer)); // send +} + +/* ------------------------------------------------------------------------- */ + +uint8_t curmatrix[16]; ///< contains current state of the keyboard + +/** + * The keymatrix-array contains positions of keys in the matrix. Here you can + * see which row is connected to which column when a key is pressed. This array + * probably has to be modified if this firmware is ported to a different + * keyboard. + * \sa modmatrix + */ +const uint8_t PROGMEM keymatrix[16][8] = { + // 0 1 2 3 4 5 6 7 + {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved }, // 0 + {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved }, // 1 + {KEY_ESCAPE, KEY_Tab, KEY_grave, KEY_1, KEY_Q, KEY_A, KEY_Z, KEY_Reserved }, // 2 + {KEY_backslash, KEY_capslock, KEY_F1, KEY_2, KEY_W, KEY_S, KEY_X, KEY_Reserved }, // 3 + {KEY_F4, KEY_F3, KEY_F2, KEY_3, KEY_E, KEY_D, KEY_C, KEY_Reserved }, // 4 + {KEY_G, KEY_T, KEY_5, KEY_4, KEY_R, KEY_F, KEY_V, KEY_B }, // 5 + {KEY_F5, KEY_DELETE, KEY_F9, KEY_F10, KEY_Reserved, KEY_Reserved, KEY_Return, KEY_Spacebar }, // 6 + {KEY_H, KEY_Y, KEY_6, KEY_7, KEY_U, KEY_J, KEY_M, KEY_N }, // 7 + {KEY_F6, KEY_rbracket, KEY_equals, KEY_8, KEY_I, KEY_K, KEY_comma, KEY_Reserved }, // 8 + {KEY_Reserved, KEY_F7, KEY_F8, KEY_9, KEY_O, KEY_L, KEY_dot, KEY_Reserved }, // 9 + {KEY_apostroph, KEY_lbracket, KEY_minus, KEY_0, KEY_P, KEY_semicolon, KEY_hash, KEY_slash }, // 10 + {KEY_Reserved, KEY_KP4, KEY_DeleteForward, KEY_F11, KEY_KP7, KEY_KP1, KEY_NumLock, KEY_DownArrow }, // 11 + {KEY_KP0, KEY_KP5, KEY_Insert, KEY_F12, KEY_KP8, KEY_KP2, KEY_KPslash, KEY_RightArrow }, // 12 + {KEY_KPcomma, KEY_KP6, KEY_PageUp, KEY_PageDown, KEY_KP9, KEY_KP3, KEY_KPasterisk, KEY_KPminus }, // 13 + {KEY_UpArrow, KEY_Reserved, KEY_Home, KEY_End, KEY_KPplus, KEY_KPenter, KEY_Pause, KEY_LeftArrow }, // 14 + {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_PrintScreen, KEY_ScrollLock, KEY_Reserved, KEY_Reserved, KEY_Reserved }, // 15 +}; + +/** + * The modmatrix-array contains positions of the modifier-keys in the matrix. + * It is built in the same way as the keymatrix-array. + * \sa keymatrix + */ +const uint8_t PROGMEM modmatrix[16][8] = { // contains positions of modifiers in the matrix + // 0 1 2 3 4 5 6 7 + {MOD_NONE, MOD_NONE, MOD_CONTROL_LEFT, MOD_NONE, MOD_NONE, MOD_NONE, MOD_CONTROL_RIGHT, MOD_NONE }, // 0 + {MOD_NONE, MOD_SHIFT_LEFT, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_SHIFT_RIGHT, MOD_NONE }, // 1 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 2 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 3 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 4 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 5 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 6 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 7 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 8 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 9 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 10 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 11 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 12 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 13 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 14 + {MOD_ALT_LEFT, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_ALT_RIGHT}, // 15 +}; + +/** + * This structure can be used as a container for a single 'key'. It consists of + * the key-code and the modifier-code. + */ +typedef struct { + uint8_t mode; + uint8_t key; +} Key; + +/** + * Convert an ASCII-character to the corresponding key-code and modifier-code + * combination. + * \parm character ASCII-character to convert + * \return structure containing the combination + */ +Key charToKey(char character) { + Key key; + // initialize with reserved values + key.mode = MOD_NONE; + key.key = KEY_Reserved; + if ((character >= 'a') && (character <= 'z')) { + // a..z + key.key = (character - 'a') + 0x04; + } else if ((character >= 'A') && (character <= 'Z')) { + // A..Z + key.mode = MOD_SHIFT_LEFT; + key.key = (character - 'A') + 0x04; + } else if ((character >= '1') && (character <= '9')) { + // 1..9 + key.key = (character - '1') + 0x1E; + } + // we can't map the other characters directly, so we switch... + switch (character) { + case '0': + key.key = KEY_0; break; + case '!': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_1; break; + /* + case '@': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_2; break; + case '#': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_3; break; + */ + case '$': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_4; break; + case '%': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_5; break; + case '^': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_6; break; + case '&': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_7; break; + case '*': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_8; break; + case '(': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_9; break; + case ')': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_0; break; + case ' ': + key.key = KEY_Spacebar; break; + case '-': + key.key = KEY_minus; break; + case '_': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_minus; break; + case '=': + key.key = KEY_equals; break; + case '+': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_equals; break; + case '[': + key.key = KEY_lbracket; break; + case '{': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_lbracket; break; + case ']': + key.key = KEY_rbracket; break; + case '}': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_rbracket; break; + case '\\': + key.key = KEY_backslash; break; + case '|': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_backslash; break; + case '#': + key.key = KEY_hash; break; + case '@': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_hash; break; + case ';': + key.key = KEY_semicolon; break; + case ':': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_semicolon; break; + case '\'': + key.key = KEY_apostroph; break; + case '"': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_apostroph; break; + case '`': + key.key = KEY_grave; break; + case '~': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_grave; break; + case ',': + key.key = KEY_comma; break; + case '<': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_comma; break; + case '.': + key.key = KEY_dot; break; + case '>': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_dot; break; + case '/': + key.key = KEY_slash; break; + case '?': + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_slash; break; + } + if (key.key == KEY_Reserved) { + // still reserved? WTF? return question mark... + key.mode = MOD_SHIFT_LEFT; + key.key = KEY_slash; + } + return key; +} + +/** + * Send a key to the computer, followed by the release of all keys. This can be + * used repetitively to send a string. + * \param keytosend key structure to send + */ +void sendKey(Key keytosend) { + usbSendReport(keytosend.mode, keytosend.key); + usbSendReport(0, 0); +} + +/** + * Send a string to the computer. This function converts each character of an + * ASCII-string to a key-structure and uses sendKey() to send it. + * \param string string to send + */ +void sendString(char* string) { + for (uint8_t i = 0; i < strlen(string); i++) { + Key key = charToKey(string[i]); + sendKey(key); + } +} + +/** + * Print the current state of the keyboard in a readable form. This function + * is used for debug-purposes only. + */ +void printMatrix(void) { + for (uint8_t i = 0; i <= 15; i++) { + char buffer[10]; + /* + sprintf(buffer, "%d%d%d%d%d%d%d%d.", + (curmatrix[i] & (1 << 0) ? 1 : 0), + (curmatrix[i] & (1 << 1) ? 1 : 0), + (curmatrix[i] & (1 << 2) ? 1 : 0), + (curmatrix[i] & (1 << 3) ? 1 : 0), + (curmatrix[i] & (1 << 4) ? 1 : 0), + (curmatrix[i] & (1 << 5) ? 1 : 0), + (curmatrix[i] & (1 << 6) ? 1 : 0), + (curmatrix[i] & (1 << 7) ? 1 : 0)); + */ + sprintf(buffer, "%2x", curmatrix[i]); + sendString(buffer); + if (i == 7) { + sendString(":"); + } else { + sendString("."); + } + } + sendString("---"); +} + +/** + * Scan and debounce keypresses. This is the main worker function for normal + * keyboard operation, the code contains lot of comments. Basically, it first + * scans the keyboard state. If a change is detected, it initializes a counter + * that is decreased each time this function is called. If the counter reaches + * 1, that means that the same scan result has been scanned ten times in a row, + * so we can be pretty sure that the keys are in a certain state (as in: not + * bouncing). Then, the codes for keys and modifiers are searched from the two + * arrays, the USB-message to send the state is prepared. The return value of + * this function indicates if the message has to be sent. + * \return flag to indicate whether something has changed + */ +uint8_t scankeys(void) { + static uint8_t debounce = 5; + uint8_t retval = 0; + for (uint8_t row = 0; row <= 15; row++) { + if (row <= 7) { + DDRROWS1 = (1 << row); + PORTROWS1 = ~(1 << row); + DDRROWS2 = 0x00; + PORTROWS2 = 0xff; + } else { + DDRROWS1 = 0x00; + PORTROWS1 = 0xff; + // (15 - row) looks a bit weird, you would expect (row - 8) here. + // This is because pins on PORTC are ordered in the other direction + // than on PORTA. With (15 - row), we have the bytes in the + // resulting matrix matching the pins of the keyboard connector. + DDRROWS2 = (1 << (15 - row)); + PORTROWS2 = ~(1 << (15 - row)); + } + _delay_us(30); + uint8_t data = ~PINCOLUMNS; + if (data != curmatrix[row]) { + // if a change was detected + debounce = 10; // activate debounce counter + curmatrix[row] = data; // and store the result + } + } + if (debounce) { + // Count down, but avoid underflow + debounce--; + } + if (debounce == 1) { + // debounce counter expired, create report + uint8_t reportIndex = 2; // reportBuffer[0] contains modifiers + memset(reportBuffer, 0, sizeof(reportBuffer)); // clear report buffer + for (uint8_t row = 0; row <= 15; row++) { // process all rows for key-codes + uint8_t data = curmatrix[row]; // restore buffer + if (data != 0xff) { // anything on this row? - optimization + for (uint8_t col = 0; col <= 7; col++) { // check every bit on this row + uint8_t key, modifier; + if (data & (1 << col)) { + key = pgm_read_byte(&keymatrix[row][col]); + modifier = pgm_read_byte(&modmatrix[row][col]); + } else { + key = KEY_Reserved; + modifier = MOD_NONE; + } + if (key != KEY_Reserved) { // keycode should be added to report + if (reportIndex >= sizeof(reportBuffer)) { // too many keycodes + if (!retval & 0x02) { // Only fill buffer once + memset(reportBuffer+2, KEY_ErrorRollOver, sizeof(reportBuffer)-2); + retval |= 0x02; // continue decoding to get modifiers + } + } else { + reportBuffer[reportIndex] = key; // set next available entry + reportIndex++; + } + } + if (modifier != MOD_NONE) { // modifier should be added to report + reportBuffer[0] |= modifier; + } + } + } + } + retval |= 0x01; // must have been a change at some point, since debounce is done + } + return retval; +} + +/* ------------------------------------------------------------------------- */ + +/** + * Main function, containing the main loop that manages timer- and + * USB-functionality. + * /return the obligatory integer that nobody cares about... + */ +int main(void) { + uint8_t updateNeeded = 0; + uint8_t idleCounter = 0; + wdt_enable(WDTO_2S); + hardwareInit(); + usbInit(); + sei(); + + scankeys(); + while (1) { + // main event loop + wdt_reset(); + usbPoll(); + + updateNeeded = scankeys(); // changes? + + // check timer if we need periodic reports + if (TIFR & (1 << TOV0)) { + TIFR = (1 << TOV0); // reset flag + if (idleRate != 0) { // do we need periodic reports? + if(idleCounter > 4){ // yes, but not yet + idleCounter -= 5; // 22ms in units of 4ms + } else { // yes, it is time now + updateNeeded = 1; + idleCounter = idleRate; + } + } + } + // if an update is needed, send the report + if (updateNeeded && usbInterruptIsReady()) { + updateNeeded = 0; + usbSetInterrupt(reportBuffer, sizeof(reportBuffer)); + } + } + return 0; +} + +/* ------------------------------------------------------------------------- */ diff --git a/firmware/project.doxygen b/firmware/project.doxygen new file mode 100644 index 0000000..b0ce063 --- /dev/null +++ b/firmware/project.doxygen @@ -0,0 +1,1252 @@ +# Doxyfile 1.4.7 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = "Dulcimer" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = firmware/usbdrv bootloader/usbdrv + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = htmldoc + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latexdoc + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtfdoc + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = YES + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a caller dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/firmware/usbconfig.h b/firmware/usbconfig.h new file mode 100644 index 0000000..713174f --- /dev/null +++ b/firmware/usbconfig.h @@ -0,0 +1,224 @@ +/** + * \file firmware/usbconfig.h + * \brief Configuration options for the USB-driver. + * + * This file is almost identical to the original usbconfig.h by Christian + * Starkjohann, in structure and content. + * + * It contains parts of the USB driver which can be configured and can or must + * be adapted to your hardware. + * \author Ronald Schaten + * \version $Id: usbconfig.h,v 1.1 2008/07/09 20:47:12 rschaten Exp $ + * + * License: GNU GPL v2 (see License.txt) + */ + +#ifndef __usbconfig_h_included__ +#define __usbconfig_h_included__ + +/* ---------------------------- Hardware Config ---------------------------- */ + +/** This is the port where the USB bus is connected. When you configure it to + * "B", the registers PORTB, PINB and DDRB will be used. + */ +#define USB_CFG_IOPORTNAME D +/** This is the bit number in USB_CFG_IOPORT where the USB D- line is connected. + * This may be any bit in the port. + */ +#define USB_CFG_DMINUS_BIT 0 +/** This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected. + * This may be any bit in the port. Please note that D+ must also be connected + * to interrupt pin INT0! + */ +#define USB_CFG_DPLUS_BIT 2 + +/* ----------------------- Optional Hardware Config ------------------------ */ + +/* If you connect the 1.5k pullup resistor from D- to a port pin instead of + * V+, you can connect and disconnect the device from firmware by calling + * the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h). + * This constant defines the port on which the pullup resistor is connected. + */ +/* #define USB_CFG_PULLUP_IOPORTNAME D */ +/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined + * above) where the 1.5k pullup resistor is connected. See description + * above for details. + */ +/* #define USB_CFG_PULLUP_BIT 4 */ + +/* --------------------------- Functional Range ---------------------------- */ + +/** Define this to 1 if you want to compile a version with two endpoints: The + * default control endpoint 0 and an interrupt-in endpoint 1. + */ +#define USB_CFG_HAVE_INTRIN_ENDPOINT 1 +/** Define this to 1 if you want to compile a version with three endpoints: The + * default control endpoint 0, an interrupt-in endpoint 1 and an interrupt-in + * endpoint 3. You must also enable endpoint 1 above. + */ +#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0 +/** Define this to 1 if you also want to implement the ENDPOINT_HALT feature + * for endpoint 1 (interrupt endpoint). Although you may not need this feature, + * it is required by the standard. We have made it a config option because it + * bloats the code considerably. + */ +#define USB_CFG_IMPLEMENT_HALT 0 +/** If you compile a version with endpoint 1 (interrupt-in), this is the poll + * interval. The value is in milliseconds and must not be less than 10 ms for + * low speed devices. + */ +#define USB_CFG_INTR_POLL_INTERVAL 10 +/** Define this to 1 if the device has its own power supply. Set it to 0 if the + * device is powered from the USB bus. + */ +#define USB_CFG_IS_SELF_POWERED 0 +/** Set this variable to the maximum USB bus power consumption of your device. + * The value is in milliamperes. [It will be divided by two since USB + * communicates power requirements in units of 2 mA.] + */ +#define USB_CFG_MAX_BUS_POWER 100 +/** Set this to 1 if you want usbFunctionWrite() to be called for control-out + * transfers. Set it to 0 if you don't need it and want to save a couple of + * bytes. + */ +#define USB_CFG_IMPLEMENT_FN_WRITE 1 +/** Set this to 1 if you need to send control replies which are generated + * "on the fly" when usbFunctionRead() is called. If you only want to send + * data from a static buffer, set it to 0 and return the data from + * usbFunctionSetup(). This saves a couple of bytes. + */ +#define USB_CFG_IMPLEMENT_FN_READ 0 +/** Define this to 1 if you want to use interrupt-out (or bulk out) endpoint 1. + * You must implement the function usbFunctionWriteOut() which receives all + * interrupt/bulk data sent to endpoint 1. + */ +#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0 +/** Define this to 1 if you want flowcontrol over USB data. See the definition + * of the macros usbDisableAllRequests() and usbEnableAllRequests() in + * usbdrv.h. + */ +#define USB_CFG_HAVE_FLOWCONTROL 0 + +/* -------------------------- Device Description --------------------------- */ + +/** We cannot use Obdev's free shared VID/PID pair because this is a HID. + * We use John Hyde's VID (author of the book "USB Design By Example") for + * this example instead. John has offered this VID for use by students for + * non-commercial devices. Well... This example is for demonstration and + * education only... DO NOT LET DEVICES WITH THIS VID ESCAPE YOUR LAB! + * The Product-ID is a random number. + * + * USB vendor ID for the device, low byte first. If you have registered your + * own Vendor ID, define it here. Otherwise you use obdev's free shared + * VID/PID pair. Be sure to read USBID-License.txt for rules! + */ +#define USB_CFG_VENDOR_ID 0x42, 0x42 +/** This is the ID of the product, low byte first. It is interpreted in the + * scope of the vendor ID. If you have registered your own VID with usb.org + * or if you have licensed a PID from somebody else, define it here. Otherwise + * you use obdev's free shared VID/PID pair. Be sure to read the rules in + * USBID-License.txt! + */ +#define USB_CFG_DEVICE_ID 0x31, 0xe1 +/** Version number of the device: Minor number first, then major number. + */ +#define USB_CFG_DEVICE_VERSION 0x00, 0x01 +/** These two values define the vendor name returned by the USB device. The name + * must be given as a list of characters under single quotes. The characters + * are interpreted as Unicode (UTF-16) entities. + * If you don't want a vendor name string, undefine these macros. + * ALWAYS define a vendor name containing your Internet domain name if you use + * obdev's free shared VID/PID pair. See the file USBID-License.txt for + * details. + */ +#define USB_CFG_VENDOR_NAME 'w', 'w', 'w', '.', 's', 'c', 'h', 'a', 't', 'e', 'n', 's', 'e', 'i', 't', 'e', '.', 'd', 'e' +/** Length of USB_CFG_DEVICE_VERSION + */ +#define USB_CFG_VENDOR_NAME_LEN 19 +/** Same as above for the device name. If you don't want a device name, undefine + * the macros. See the file USBID-License.txt before you assign a name. + */ +#define USB_CFG_DEVICE_NAME 'D', 'u', 'l', 'c', 'i', 'm', 'e', 'r' +/** Length of USB_CFG_DEVICE_NAME + */ +#define USB_CFG_DEVICE_NAME_LEN 8 +/** See USB specification if you want to conform to an existing device class. + * This setting means to specify the class at the interface level. + */ +#define USB_CFG_DEVICE_CLASS 0 +/** See USB specification if you want to conform to an existing device subclass. + */ +#define USB_CFG_DEVICE_SUBCLASS 0 +/** See USB specification if you want to conform to an existing device class or + * protocol. This is HID class. + */ +#define USB_CFG_INTERFACE_CLASS 0x03 +/** See USB specification if you want to conform to an existing device class or + * protocol. This is a boot device. + */ +#define USB_CFG_INTERFACE_SUBCLASS 0x01 +/** See USB specification if you want to conform to an existing device class or + * protocol. This is keyboard protocol. + */ +#define USB_CFG_INTERFACE_PROTOCOL 0x01 +/** Define this to the length of the HID report descriptor, if you implement + * an HID device. Otherwise don't define it or define it to 0. + */ +#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 63 + +/* ------------------- Fine Control over USB Descriptors ------------------- */ +/* If you don't want to use the driver's default USB descriptors, you can + * provide our own. These can be provided as (1) fixed length static data in + * flash memory, (2) fixed length static data in RAM or (3) dynamically at + * runtime in the function usbFunctionDescriptor(). See usbdrv.h for more + * information about this function. + * Descriptor handling is configured through the descriptor's properties. If + * no properties are defined or if they are 0, the default descriptor is used. + * Possible properties are: + * + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched + * at runtime via usbFunctionDescriptor(). + * + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found + * in static memory is in RAM, not in flash memory. + * + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash), + * the driver must know the descriptor's length. The descriptor itself is + * found at the address of a well known identifier (see below). + * List of static descriptor names (must be declared PROGMEM if in flash): + * char usbDescriptorDevice[]; + * char usbDescriptorConfiguration[]; + * char usbDescriptorHidReport[]; + * char usbDescriptorString0[]; + * int usbDescriptorStringVendor[]; + * int usbDescriptorStringDevice[]; + * int usbDescriptorStringSerialNumber[]; + * Other descriptors can't be provided statically, they must be provided + * dynamically at runtime. + * + * Descriptor properties are or-ed or added together, e.g.: + * #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18)) + * + * The following descriptors are defined: + * USB_CFG_DESCR_PROPS_DEVICE + * USB_CFG_DESCR_PROPS_CONFIGURATION + * USB_CFG_DESCR_PROPS_STRINGS + * USB_CFG_DESCR_PROPS_STRING_0 + * USB_CFG_DESCR_PROPS_STRING_VENDOR + * USB_CFG_DESCR_PROPS_STRING_PRODUCT + * USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER + * USB_CFG_DESCR_PROPS_HID + * USB_CFG_DESCR_PROPS_HID_REPORT + * USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver) + * + */ + +#define USB_CFG_DESCR_PROPS_DEVICE 0 +#define USB_CFG_DESCR_PROPS_CONFIGURATION 0 +#define USB_CFG_DESCR_PROPS_STRINGS 0 +#define USB_CFG_DESCR_PROPS_STRING_0 0 +#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 +#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 +#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 +#define USB_CFG_DESCR_PROPS_HID 0 +#define USB_CFG_DESCR_PROPS_HID_REPORT 0 +#define USB_CFG_DESCR_PROPS_UNKNOWN 0 + +#endif /* __usbconfig_h_included__ */ diff --git a/firmware/usbdrv/Changelog.txt b/firmware/usbdrv/Changelog.txt new file mode 100644 index 0000000..a92d2d0 --- /dev/null +++ b/firmware/usbdrv/Changelog.txt @@ -0,0 +1,135 @@ +This file documents changes in the firmware-only USB driver for atmel's AVR +microcontrollers. New entries are always appended to the end of the file. +Scroll down to the bottom to see the most recent changes. + +2005-04-01: + - Implemented endpoint 1 as interrupt-in endpoint. + - Moved all configuration options to usbconfig.h which is not part of the + driver. + - Changed interface for usbVendorSetup(). + - Fixed compatibility with ATMega8 device. + - Various minor optimizations. + +2005-04-11: + - Changed interface to application: Use usbFunctionSetup(), usbFunctionRead() + and usbFunctionWrite() now. Added configuration options to choose which + of these functions to compile in. + - Assembler module delivers receive data non-inverted now. + - Made register and bit names compatible with more AVR devices. + +2005-05-03: + - Allow address of usbRxBuf on any memory page as long as the buffer does + not cross 256 byte page boundaries. + - Better device compatibility: works with Mega88 now. + - Code optimization in debugging module. + - Documentation updates. + +2006-01-02: + - Added (free) default Vendor- and Product-IDs bought from voti.nl. + - Added USBID-License.txt file which defines the rules for using the free + shared VID/PID pair. + - Added Readme.txt to the usbdrv directory which clarifies administrative + issues. + +2006-01-25: + - Added "configured state" to become more standards compliant. + - Added "HALT" state for interrupt endpoint. + - Driver passes the "USB Command Verifier" test from usb.org now. + - Made "serial number" a configuration option. + - Minor optimizations, we now recommend compiler option "-Os" for best + results. + - Added a version number to usbdrv.h + +2006-02-03: + - New configuration variable USB_BUFFER_SECTION for the memory section where + the USB rx buffer will go. This defaults to ".bss" if not defined. Since + this buffer MUST NOT cross 256 byte pages (not even touch a page at the + end), the user may want to pass a linker option similar to + "-Wl,--section-start=.mybuffer=0x800060". + - Provide structure for usbRequest_t. + - New defines for USB constants. + - Prepared for HID implementations. + - Increased data size limit for interrupt transfers to 8 bytes. + - New macro usbInterruptIsReady() to query interrupt buffer state. + +2006-02-18: + - Ensure that the data token which is sent as an ack to an OUT transfer is + always zero sized. This fixes a bug where the host reports an error after + sending an out transfer to the device, although all data arrived at the + device. + - Updated docs in usbdrv.h to reflect changed API in usbFunctionWrite(). + +* Release 2006-02-20 + + - Give a compiler warning when compiling with debugging turned on. + - Added Oleg Semyonov's changes for IAR-cc compatibility. + - Added new (optional) functions usbDeviceConnect() and usbDeviceDisconnect() + (also thanks to Oleg!). + - Rearranged tests in usbPoll() to save a couple of instructions in the most + likely case that no actions are pending. + - We need a delay between the SET ADDRESS request until the new address + becomes active. This delay was handled in usbPoll() until now. Since the + spec says that the delay must not exceed 2ms, previous versions required + aggressive polling during the enumeration phase. We have now moved the + handling of the delay into the interrupt routine. + - We must not reply with NAK to a SETUP transaction. We can only achieve this + by making sure that the rx buffer is empty when SETUP tokens are expected. + We therefore don't pass zero sized data packets from the status phase of + a transfer to usbPoll(). This change MAY cause troubles if you rely on + receiving a less than 8 bytes long packet in usbFunctionWrite() to + identify the end of a transfer. usbFunctionWrite() will NEVER be called + with a zero length. + +* Release 2006-03-14 + + - Improved IAR C support: tiny memory model, more devices + - Added template usbconfig.h file under the name usbconfig-prototype.h + +* Release 2006-03-26 + + - Added provision for one more interrupt-in endpoint (endpoint 3). + - Added provision for one interrupt-out endpoint (endpoint 1). + - Added flowcontrol macros for USB. + - Added provision for custom configuration descriptor. + - Allow ANY two port bits for D+ and D-. + - Merged (optional) receive endpoint number into global usbRxToken variable. + - Use USB_CFG_IOPORTNAME instead of USB_CFG_IOPORT. We now construct the + variable name from the single port letter instead of computing the address + of related ports from the output-port address. + +* Release 2006-06-26 + + - Updated documentation in usbdrv.h and usbconfig-prototype.h to reflect the + new features. + - Removed "#warning" directives because IAR does not understand them. Use + unused static variables instead to generate a warning. + - Do not include when compiling with IAR. + - Introduced USB_CFG_DESCR_PROPS_* in usbconfig.h to configure how each + USB descriptor should be handled. It is now possible to provide descriptor + data in Flash, RAM or dynamically at runtime. + - STALL is now a status in usbTxLen* instead of a message. We can now conform + to the spec and leave the stall status pending until it is cleared. + - Made usbTxPacketCnt1 and usbTxPacketCnt3 public. This allows the + application code to reset data toggling on interrupt pipes. + +* Release 2006-07-18 + + - Added an #if !defined __ASSEMBLER__ to the warning in usbdrv.h. This fixes + an assembler error. + - usbDeviceDisconnect() takes pull-up resistor to high impedance now. + +* Release 2007-02-01 + + - Merged in some code size improvements from usbtiny (thanks to Dick + Streefland for these optimizations!) + - Special alignment requirement for usbRxBuf not required any more. Thanks + again to Dick Streefland for this hint! + - Reverted to "#warning" instead of unused static variables -- new versions + of IAR CC should handle this directive. + - Changed Open Source license to GNU GPL v2 in order to make linking against + other free libraries easier. We no longer require publication of the + circuit diagrams, but we STRONGLY encourage it. If you improve the driver + itself, PLEASE grant us a royalty free license to your changes for our + commercial license. + +* Release 2007-03-29 diff --git a/firmware/usbdrv/CommercialLicense.txt b/firmware/usbdrv/CommercialLicense.txt new file mode 100644 index 0000000..240e45f --- /dev/null +++ b/firmware/usbdrv/CommercialLicense.txt @@ -0,0 +1,155 @@ +AVR-USB Driver Software License Agreement +Version 2006-07-24 + +THIS LICENSE AGREEMENT GRANTS YOU CERTAIN RIGHTS IN A SOFTWARE. YOU CAN +ENTER INTO THIS AGREEMENT AND ACQUIRE THE RIGHTS OUTLINED BELOW BY PAYING +THE AMOUNT ACCORDING TO SECTION 4 ("PAYMENT") TO OBJECTIVE DEVELOPMENT. + + +1 DEFINITIONS + +1.1 "OBJECTIVE DEVELOPMENT" shall mean OBJECTIVE DEVELOPMENT Software GmbH, +Grosse Schiffgasse 1A/7, 1020 Wien, AUSTRIA. + +1.2 "You" shall mean the Licensee. + +1.3 "AVR-USB" shall mean the firmware-only USB device implementation for +Atmel AVR microcontrollers distributed by OBJECTIVE DEVELOPMENT and +consisting of the files usbdrv.c, usbdrv.h, usbdrvasm.S, oddebug.c, +oddebug.h, usbdrvasm.asm, iarcompat.h and usbconfig-prototype.h. + + +2 LICENSE GRANTS + +2.1 Source Code. OBJECTIVE DEVELOPMENT shall furnish you with the source +code of AVR-USB. + +2.2 Distribution and Use. OBJECTIVE DEVELOPMENT grants you the +non-exclusive right to use and distribute AVR-USB with your hardware +product(s), restricted by the limitations in section 3 below. + +2.3 Modifications. OBJECTIVE DEVELOPMENT grants you the right to modify +your copy of AVR-USB according to your needs. + +2.4 USB IDs. OBJECTIVE DEVELOPMENT grants you the exclusive rights to use +USB Product ID(s) sent to you in e-mail after receiving your payment in +conjunction with USB Vendor ID 5824. OBJECTIVE DEVELOPMENT has acquired an +exclusive license for this pair of USB identifiers from Wouter van Ooijen +(www.voti.nl), who has licensed the VID from the USB Implementers Forum, +Inc. (www.usb.org). + + +3 LICENSE RESTRICTIONS + +3.1 Number of Units. Only one of the following three definitions is +applicable. Which one is determined by the amount you pay to OBJECTIVE +DEVELOPMENT, see section 4 ("Payment") below. + +Hobby License: You may use AVR-USB according to section 2 above in no more +than 5 hardware units. These units must not be sold for profit. + +Entry Level License: You may use AVR-USB according to section 2 above in no +more than 150 hardware units. + +Professional License: You may use AVR-USB according to section 2 above in +any number of hardware units, except for large scale production ("unlimited +fair use"). Quantities below 10,000 units are not considered large scale +production. If your reach quantities which are obviously large scale +production, you must pay a license fee of 0.10 EUR per unit for all units +above 10,000. + +3.2 Rental. You may not rent, lease, or lend AVR-USB or otherwise encumber +any copy of AVR-USB, or any of the rights granted herein. + +3.3 Transfer. You may not transfer your rights under this Agreement to +another party without OBJECTIVE DEVELOPMENT's prior written consent. If +such consent is obtained, you may permanently transfer this License to +another party. The recipient of such transfer must agree to all terms and +conditions of this Agreement. + +3.4 Reservation of Rights. OBJECTIVE DEVELOPMENT retains all rights not +expressly granted. + +3.5 Non-Exclusive Rights. Your license rights under this Agreement are +non-exclusive. + +3.6 Third Party Rights. This Agreement cannot grant you rights controlled +by third parties. In particular, you are not allowed to use the USB logo or +other trademarks owned by the USB Implementers Forum, Inc. without their +consent. Since such consent depends on USB certification, it should be +noted that AVR-USB will not pass certification because it does not +implement checksum verification and the microcontroller ports do not meet +the electrical specifications. + + +4 PAYMENT + +The payment amount depends on the variation of this agreement (according to +section 3.1) into which you want to enter. Concrete prices are listed on +OBJECTIVE DEVELOPMENT's web site, usually at +http://www.obdev.at/avrusb/license.html. You agree to pay the amount listed +there to OBJECTIVE DEVELOPMENT or OBJECTIVE DEVELOPMENT's payment processor +or reseller. + + +5 COPYRIGHT AND OWNERSHIP + +AVR-USB is protected by copyright laws and international copyright +treaties, as well as other intellectual property laws and treaties. AVR-USB +is licensed, not sold. + + +6 TERM AND TERMINATION + +6.1 Term. This Agreement shall continue indefinitely. However, OBJECTIVE +DEVELOPMENT may terminate this Agreement and revoke the granted license and +USB-IDs if you fail to comply with any of its terms and conditions. + +6.2 Survival of Terms. All provisions regarding secrecy, confidentiality +and limitation of liability shall survive termination of this agreement. + + +7 DISCLAIMER OF WARRANTY AND LIABILITY + +LIMITED WARRANTY. AVR-USB IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, OBJECTIVE +DEVELOPMENT AND ITS SUPPLIERS HEREBY DISCLAIM ALL WARRANTIES, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND +NON-INFRINGEMENT, WITH REGARD TO AVR-USB, AND THE PROVISION OF OR FAILURE +TO PROVIDE SUPPORT SERVICES. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL +RIGHTS. YOU MAY HAVE OTHERS, WHICH VARY FROM STATE/JURISDICTION TO +STATE/JURISDICTION. + +LIMITATION OF LIABILITY. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, +IN NO EVENT SHALL OBJECTIVE DEVELOPMENT OR ITS SUPPLIERS BE LIABLE FOR ANY +SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER +(INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, +BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY +LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE AVR-USB OR THE +PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF OBJECTIVE +DEVELOPMENT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY +CASE, OBJECTIVE DEVELOPMENT'S ENTIRE LIABILITY UNDER ANY PROVISION OF THIS +AGREEMENT SHALL BE LIMITED TO THE AMOUNT ACTUALLY PAID BY YOU FOR AVR-USB. + + +8 MISCELLANEOUS TERMS + +8.1 Marketing. OBJECTIVE DEVELOPMENT has the right to mention for marketing +purposes that you entered into this agreement. + +8.2 Entire Agreement. This document represents the entire agreement between +OBJECTIVE DEVELOPMENT and you. It may only be modified in writing signed by +an authorized representative of both, OBJECTIVE DEVELOPMENT and you. + +8.3 Severability. In case a provision of these terms and conditions should +be or become partly or entirely invalid, ineffective, or not executable, +the validity of all other provisions shall not be affected. + +8.4 Applicable Law. This agreement is governed by the laws of the Republic +of Austria. + +8.5 Responsible Courts. The responsible courts in Vienna/Austria will have +exclusive jurisdiction regarding all disputes in connection with this +agreement. + diff --git a/firmware/usbdrv/License.txt b/firmware/usbdrv/License.txt new file mode 100644 index 0000000..f6a232e --- /dev/null +++ b/firmware/usbdrv/License.txt @@ -0,0 +1,359 @@ +OBJECTIVE DEVELOPMENT GmbH's AVR-USB driver software is distributed under the +terms and conditions of the GNU GPL version 2, see the text below. In addition +to the requirements in the GPL, we STRONGLY ENCOURAGE you to do the following: + +(1) Publish your entire project on a web site and drop us a note with the URL. +Use the form at http://www.obdev.at/avrusb/feedback.html for your submission. + +(2) Adhere to minimum publication standards. Please include AT LEAST: + - a circuit diagram in PDF, PNG or GIF format + - full source code for the host software + - a Readme.txt file in ASCII format which describes the purpose of the + project and what can be found in which directories and which files + - a reference to http://www.obdev.at/avrusb/ + +(3) If you improve the driver firmware itself, please give us a free license +to your modifications for our commercial license offerings. + + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/firmware/usbdrv/Readme.txt b/firmware/usbdrv/Readme.txt new file mode 100644 index 0000000..345f090 --- /dev/null +++ b/firmware/usbdrv/Readme.txt @@ -0,0 +1,84 @@ +This is the Readme file to Objective Development's firmware-only USB driver +for Atmel AVR microcontrollers. For more information please visit +http://www.obdev.at/avrusb/ + +This directory contains the USB firmware only. Copy it as-is to your own +project and add your own version of "usbconfig.h". A template for your own +"usbconfig.h" can be found in "usbconfig-prototype.h" in this directory. + + +TECHNICAL DOCUMENTATION +======================= +The technical documentation for the firmware driver is contained in the file +"usbdrv.h". Please read all of it carefully! + + +USB IDENTIFIERS +=============== +Every USB device needs a vendor- and a product-identifier (VID and PID). VIDs +are obtained from usb.org for a price of 1,500 USD. Once you have a VID, you +can assign PIDs at will. + +Since an entry level cost of 1,500 USD is too high for most small companies +and hobbyists, we provide a single VID/PID pair for free. If you want to use +your own VID and PID instead of our's, define the macros "USB_CFG_VENDOR_ID" +and "USB_CFG_DEVICE_ID" accordingly in "usbconfig.h". + +To use our predefined VID/PID pair, you MUST conform to a couple of +requirements. See the file "USBID-License.txt" for details. + +Objective Development also has some offerings which include product IDs. See +http://www.obdev.at/avrusb/ for details. + + +HOST DRIVER +=========== +You have received this driver together with an example device implementation +and an example host driver. The host driver is based on libusb and compiles +on various Unix flavors (Linux, BSD, Mac OS X). It also compiles natively on +Windows using MinGW (see www.mingw.org) and libusb-win32 (see +libusb-win32.sourceforge.net). The "Automator" project contains a native +Windows host driver (not based on libusb) for Human Interface Devices. + + +DEVELOPMENT SYSTEM +================== +This driver has been developed and optimized for the GNU compiler version 3 +(gcc 3). It does work well with gcc 4 and future versions will probably be +optimized for gcc 4. We recommend that you use the GNU compiler suite because +it is freely available. AVR-USB has also been ported to the IAR compiler and +assembler. It has been tested with IAR 4.10B/W32 and 4.12A/W32 on an ATmega8 +with the "small" and "tiny" memory model. Please note that gcc is more +efficient for usbdrv.c because this module has been deliberately optimized +for gcc. + + +USING AVR-USB FOR FREE +====================== +The AVR firmware driver is published under the GNU General Public License +Version 2 (GPL2). See the file "License.txt" for details. + +If you decide for the free GPL2, we STRONGLY ENCOURAGE you to do the following +things IN ADDITION to the obligations from the GPL2: + +(1) Publish your entire project on a web site and drop us a note with the URL. +Use the form at http://www.obdev.at/avrusb/feedback.html for your submission. + +(2) Adhere to minimum publication standards. Please include AT LEAST: + - a circuit diagram in PDF, PNG or GIF format + - full source code for the host software + - a Readme.txt file in ASCII format which describes the purpose of the + project and what can be found in which directories and which files + - a reference to http://www.obdev.at/avrusb/ + +(3) If you improve the driver firmware itself, please give us a free license +to your modifications for our commercial license offerings. + + +COMMERCIAL LICENSES FOR AVR-USB +=============================== +If you don't want to publish your source code under the terms of the GPL2, +you can simply pay money for AVR-USB. As an additional benefit you get +USB PIDs for free, licensed exclusively to you. See the file +"CommercialLicense.txt" for details. + diff --git a/firmware/usbdrv/USBID-License.txt b/firmware/usbdrv/USBID-License.txt new file mode 100644 index 0000000..4739a57 --- /dev/null +++ b/firmware/usbdrv/USBID-License.txt @@ -0,0 +1,143 @@ +Royalty-Free Non-Exclusive License USB Product-ID +================================================= + +Version 2006-06-19 + +OBJECTIVE DEVELOPMENT Software GmbH hereby grants you the non-exclusive +right to use three USB.org vendor-ID (VID) / product-ID (PID) pairs with +products based on Objective Development's firmware-only USB driver for +Atmel AVR microcontrollers: + + * VID = 5824 (=0x16c0) / PID = 1500 (=0x5dc) for devices implementing no + USB device class (vendor-class devices with USB class = 0xff). Devices + using this pair will be referred to as "VENDOR CLASS" devices. + + * VID = 5824 (=0x16c0) / PID = 1503 (=0x5df) for HID class devices + (excluding mice and keyboards). Devices using this pair will be referred + to as "HID CLASS" devices. + + * VID = 5824 (=0x16c0) / PID = 1505 (=0x5e1) for CDC class modem devices + Devices using this pair will be referred to as "CDC-ACM CLASS" devices. + +Since the granted right is non-exclusive, the same VID/PID pairs may be +used by many companies and individuals for different products. To avoid +conflicts, your device and host driver software MUST adhere to the rules +outlined below. + +OBJECTIVE DEVELOPMENT Software GmbH has licensed these VID/PID pairs from +Wouter van Ooijen (see www.voti.nl), who has licensed the VID from the USB +Implementers Forum, Inc. (see www.usb.org). The VID is registered for the +company name "Van Ooijen Technische Informatica". + + +RULES AND RESTRICTIONS +====================== + +(1) The USB device MUST provide a textual representation of the +manufacturer and product identification. The manufacturer identification +MUST be available at least in USB language 0x0409 (English/US). + +(2) The textual manufacturer identification MUST contain either an Internet +domain name (e.g. "mycompany.com") registered and owned by you, or an +e-mail address under your control (e.g. "myname@gmx.net"). You can embed +the domain name or e-mail address in any string you like, e.g. "Objective +Development http://www.obdev.at/avrusb/". + +(3) You are responsible for retaining ownership of the domain or e-mail +address for as long as any of your products are in use. + +(4) You may choose any string for the textual product identification, as +long as this string is unique within the scope of your textual manufacturer +identification. + +(5) Matching of device-specific drivers MUST be based on the textual +manufacturer and product identification in addition to the usual VID/PID +matching. This means that operating system features which are based on +VID/PID matching only (e.g. Windows kernel level drivers, automatic actions +when the device is plugged in etc) MUST NOT be used. The driver matching +MUST be a comparison of the entire strings, NOT a sub-string match. For +CDC-ACM CLASS devices, a generic class driver should be used and the +matching is based on the USB device class. + +(6) The extent to which VID/PID matching is allowed for non device-specific +drivers or features depends on the operating system and particular VID/PID +pair used: + + * Mac OS X, Linux, FreeBSD and other Unixes: No VID/PID matching is + required and hence no VID/PID-only matching is allowed at all. + + * Windows: The operating system performs VID/PID matching for the kernel + level driver. You are REQUIRED to use libusb-win32 (see + http://libusb-win32.sourceforge.net/) as the kernel level driver for + VENDOR CLASS devices. HID CLASS devices all use the generic HID class + driver shipped with Windows, except mice and keyboards. You therefore + MUST NOT use any of the shared VID/PID pairs for mice or keyboards. + CDC-ACM CLASS devices require a ".inf" file which matches on the VID/PID + pair. This ".inf" file MUST load the "usbser" driver to configure the + device as modem (COM-port). + +(7) OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any +problems which are caused by the shared use of these VID/PID pairs. You +have been warned that the sharing of VID/PID pairs may cause problems. If +you want to avoid them, get your own VID/PID pair for exclusive use. + + +HOW TO IMPLEMENT THESE RULES +============================ + +The following rules are for VENDOR CLASS and HID CLASS devices. CDC-ACM +CLASS devices use the operating system's class driver and don't need a +custom driver. + +The host driver MUST iterate over all devices with the given VID/PID +numbers in their device descriptors and query the string representation for +the manufacturer name in USB language 0x0409 (English/US). It MUST compare +the ENTIRE string with your textual manufacturer identification chosen in +(2) above. A substring search for your domain or e-mail address is NOT +acceptable. The driver MUST NOT touch the device (other than querying the +descriptors) unless the strings match. + +For all USB devices with matching VID/PID and textual manufacturer +identification, the host driver must query the textual product +identification and string-compare it with the name of the product it can +control. It may only initialize the device if the product matches exactly. + +Objective Development provides examples for these matching rules with the +"PowerSwitch" project (using libusb) and with the "Automator" project +(using Windows calls on Windows and libusb on Unix). + + +Technical Notes: +================ + +Sharing the same VID/PID pair among devices is possible as long as ALL +drivers which match the VID/PID also perform matching on the textual +identification strings. This is easy on all operating systems except +Windows, since Windows establishes a static connection between the VID/PID +pair and a kernel level driver. All devices with the same VID/PID pair must +therefore use THE SAME kernel level driver. + +We therefore demand that you use libusb-win32 for VENDOR CLASS devices. +This is a generic kernel level driver which allows all types of USB access +for user space applications. This is only a partial solution of the +problem, though, because different device drivers may come with different +versions of libusb-win32 and they may not work with the libusb version of +the respective other driver. You are therefore encouraged to test your +driver against a broad range of libusb-win32 versions. Do not use new +features in new versions, or check for their existence before you use them. +When a new libusb-win32 becomes available, make sure that your driver is +compatible with it. + +For HID CLASS devices it is necessary that all those devices bind to the +same kernel driver: Microsoft's generic USB HID driver. This is true for +all HID devices except those with a specialized driver. Currently, the only +HIDs with specialized drivers are mice and keyboards. You therefore MUST +NOT use a shared VID/PID with mouse and keyboard devices. + +Sharing the same VID/PID among different products is unusual and probably +violates the USB specification. If you do it, you do it at your own risk. + +To avoid possible incompatibilities, we highly recommend that you get your +own VID/PID pair if you intend to sell your product. Objective +Development's commercial licenses for AVR-USB include a PID for +unrestricted exclusive use. diff --git a/firmware/usbdrv/iarcompat.h b/firmware/usbdrv/iarcompat.h new file mode 100644 index 0000000..a3f65a9 --- /dev/null +++ b/firmware/usbdrv/iarcompat.h @@ -0,0 +1,70 @@ +/* Name: iarcompat.h + * Project: AVR USB driver + * Author: Christian Starkjohann + * Creation Date: 2006-03-01 + * Tabsize: 4 + * Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) + * This Revision: $Id: iarcompat.h,v 1.1 2008/07/09 20:47:12 rschaten Exp $ + */ + +/* +General Description: +This header is included when we compile with the IAR C-compiler and assembler. +It defines macros for cross compatibility between gcc and IAR-cc. + +Thanks to Oleg Semyonov for his help with the IAR tools port! +*/ + +#ifndef __iarcompat_h_INCLUDED__ +#define __iarcompat_h_INCLUDED__ + +#if defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ + +/* Enable bit definitions */ +#ifndef ENABLE_BIT_DEFINITIONS +# define ENABLE_BIT_DEFINITIONS 1 +#endif + +/* Include IAR headers */ +#include +#ifndef __IAR_SYSTEMS_ASM__ +# include +#endif + +#define __attribute__(arg) +#define IAR_SECTION(section) @ section + +#ifndef USB_BUFFER_SECTION +# define USB_BUFFER_SECTION "TINY_Z" /* if user has not selected a named section */ +#endif + +#ifdef __IAR_SYSTEMS_ASM__ +# define __ASSEMBLER__ +#endif + +#ifdef __HAS_ELPM__ +# define PROGMEM __farflash +#else +# define PROGMEM __flash +#endif + +#define PRG_RDB(addr) (*(PROGMEM char *)(addr)) + +/* The following definitions are not needed by the driver, but may be of some + * help if you port a gcc based project to IAR. + */ +#define cli() __disable_interrupt() +#define sei() __enable_interrupt() +#define wdt_reset() __watchdog_reset() + +/* Depending on the device you use, you may get problems with the way usbdrv.h + * handles the differences between devices. Since IAR does not use #defines + * for MCU registers, we can't check for the existence of a particular + * register with an #ifdef. If the autodetection mechanism fails, include + * definitions for the required USB_INTR_* macros in your usbconfig.h. See + * usbconfig-prototype.h and usbdrv.h for details. + */ + +#endif /* defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ */ +#endif /* __iarcompat_h_INCLUDED__ */ diff --git a/firmware/usbdrv/oddebug.c b/firmware/usbdrv/oddebug.c new file mode 100644 index 0000000..93301f1 --- /dev/null +++ b/firmware/usbdrv/oddebug.c @@ -0,0 +1,50 @@ +/* Name: oddebug.c + * Project: AVR library + * Author: Christian Starkjohann + * Creation Date: 2005-01-16 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) + * This Revision: $Id: oddebug.c,v 1.1 2008/07/09 20:47:12 rschaten Exp $ + */ + +#include "oddebug.h" + +#if DEBUG_LEVEL > 0 + +#warning "Never compile production devices with debugging enabled" + +static void uartPutc(char c) +{ + while(!(ODDBG_USR & (1 << ODDBG_UDRE))); /* wait for data register empty */ + ODDBG_UDR = c; +} + +static uchar hexAscii(uchar h) +{ + h &= 0xf; + if(h >= 10) + h += 'a' - (uchar)10 - '0'; + h += '0'; + return h; +} + +static void printHex(uchar c) +{ + uartPutc(hexAscii(c >> 4)); + uartPutc(hexAscii(c)); +} + +void odDebug(uchar prefix, uchar *data, uchar len) +{ + printHex(prefix); + uartPutc(':'); + while(len--){ + uartPutc(' '); + printHex(*data++); + } + uartPutc('\r'); + uartPutc('\n'); +} + +#endif diff --git a/firmware/usbdrv/oddebug.h b/firmware/usbdrv/oddebug.h new file mode 100644 index 0000000..489890a --- /dev/null +++ b/firmware/usbdrv/oddebug.h @@ -0,0 +1,126 @@ +/* Name: oddebug.h + * Project: AVR library + * Author: Christian Starkjohann + * Creation Date: 2005-01-16 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) + * This Revision: $Id: oddebug.h,v 1.1 2008/07/09 20:47:12 rschaten Exp $ + */ + +#ifndef __oddebug_h_included__ +#define __oddebug_h_included__ + +/* +General Description: +This module implements a function for debug logs on the serial line of the +AVR microcontroller. Debugging can be configured with the define +'DEBUG_LEVEL'. If this macro is not defined or defined to 0, all debugging +calls are no-ops. If it is 1, DBG1 logs will appear, but not DBG2. If it is +2, DBG1 and DBG2 logs will be printed. + +A debug log consists of a label ('prefix') to indicate which debug log created +the output and a memory block to dump in hex ('data' and 'len'). +*/ + + +#ifndef F_CPU +# define F_CPU 12000000 /* 12 MHz */ +#endif + +/* make sure we have the UART defines: */ +#include "iarcompat.h" +#ifndef __IAR_SYSTEMS_ICC__ +# include +#endif + +#ifndef uchar +# define uchar unsigned char +#endif + +#if DEBUG_LEVEL > 0 && !(defined TXEN || defined TXEN0) /* no UART in device */ +# warning "Debugging disabled because device has no UART" +# undef DEBUG_LEVEL +#endif + +#ifndef DEBUG_LEVEL +# define DEBUG_LEVEL 0 +#endif + +/* ------------------------------------------------------------------------- */ + +#if DEBUG_LEVEL > 0 +# define DBG1(prefix, data, len) odDebug(prefix, data, len) +#else +# define DBG1(prefix, data, len) +#endif + +#if DEBUG_LEVEL > 1 +# define DBG2(prefix, data, len) odDebug(prefix, data, len) +#else +# define DBG2(prefix, data, len) +#endif + +/* ------------------------------------------------------------------------- */ + +#if DEBUG_LEVEL > 0 +extern void odDebug(uchar prefix, uchar *data, uchar len); + +/* Try to find our control registers; ATMEL likes to rename these */ + +#if defined UBRR +# define ODDBG_UBRR UBRR +#elif defined UBRRL +# define ODDBG_UBRR UBRRL +#elif defined UBRR0 +# define ODDBG_UBRR UBRR0 +#elif defined UBRR0L +# define ODDBG_UBRR UBRR0L +#endif + +#if defined UCR +# define ODDBG_UCR UCR +#elif defined UCSRB +# define ODDBG_UCR UCSRB +#elif defined UCSR0B +# define ODDBG_UCR UCSR0B +#endif + +#if defined TXEN +# define ODDBG_TXEN TXEN +#else +# define ODDBG_TXEN TXEN0 +#endif + +#if defined USR +# define ODDBG_USR USR +#elif defined UCSRA +# define ODDBG_USR UCSRA +#elif defined UCSR0A +# define ODDBG_USR UCSR0A +#endif + +#if defined UDRE +# define ODDBG_UDRE UDRE +#else +# define ODDBG_UDRE UDRE0 +#endif + +#if defined UDR +# define ODDBG_UDR UDR +#elif defined UDR0 +# define ODDBG_UDR UDR0 +#endif + +static inline void odDebugInit(void) +{ + ODDBG_UCR |= (1< +# include +#endif +#include "usbdrv.h" +#include "oddebug.h" + +/* +General Description: +This module implements the C-part of the USB driver. See usbdrv.h for a +documentation of the entire driver. +*/ + +#ifndef IAR_SECTION +#define IAR_SECTION(arg) +#define __no_init +#endif +/* The macro IAR_SECTION is a hack to allow IAR-cc compatibility. On gcc, it + * is defined to nothing. __no_init is required on IAR. + */ + +/* ------------------------------------------------------------------------- */ + +/* raw USB registers / interface to assembler code: */ +uchar usbRxBuf[2*USB_BUFSIZE]; /* raw RX buffer: PID, 8 bytes data, 2 bytes CRC */ +uchar usbInputBufOffset; /* offset in usbRxBuf used for low level receiving */ +uchar usbDeviceAddr; /* assigned during enumeration, defaults to 0 */ +uchar usbNewDeviceAddr; /* device ID which should be set after status phase */ +uchar usbConfiguration; /* currently selected configuration. Administered by driver, but not used */ +volatile schar usbRxLen; /* = 0; number of bytes in usbRxBuf; 0 means free, -1 for flow control */ +uchar usbCurrentTok; /* last token received, if more than 1 rx endpoint: MSb=endpoint */ +uchar usbRxToken; /* token for data we received; if more than 1 rx endpoint: MSb=endpoint */ +uchar usbMsgLen = 0xff; /* remaining number of bytes, no msg to send if -1 (see usbMsgPtr) */ +volatile uchar usbTxLen = USBPID_NAK; /* number of bytes to transmit with next IN token or handshake token */ +uchar usbTxBuf[USB_BUFSIZE];/* data to transmit with next IN, free if usbTxLen contains handshake token */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT +volatile uchar usbTxLen1 = USBPID_NAK; /* TX count for endpoint 1 */ +uchar usbTxBuf1[USB_BUFSIZE]; /* TX data for endpoint 1 */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 +volatile uchar usbTxLen3 = USBPID_NAK; /* TX count for endpoint 1 */ +uchar usbTxBuf3[USB_BUFSIZE]; /* TX data for endpoint 1 */ +#endif +#endif + +/* USB status registers / not shared with asm code */ +uchar *usbMsgPtr; /* data to transmit next -- ROM or RAM address */ +static uchar usbMsgFlags; /* flag values see below */ + +#define USB_FLG_TX_PACKET (1<<0) +/* Leave free 6 bits after TX_PACKET. This way we can increment usbMsgFlags to toggle TX_PACKET */ +#define USB_FLG_MSGPTR_IS_ROM (1<<6) +#define USB_FLG_USE_DEFAULT_RW (1<<7) + +/* +optimizing hints: +- do not post/pre inc/dec integer values in operations +- assign value of PRG_RDB() to register variables and don't use side effects in arg +- use narrow scope for variables which should be in X/Y/Z register +- assign char sized expressions to variables to force 8 bit arithmetics +*/ + +/* ------------------------------------------------------------------------- */ + +#if USB_CFG_DESCR_PROPS_STRINGS == 0 + +#if USB_CFG_DESCR_PROPS_STRING_0 == 0 +#undef USB_CFG_DESCR_PROPS_STRING_0 +#define USB_CFG_DESCR_PROPS_STRING_0 sizeof(usbDescriptorString0) +PROGMEM char usbDescriptorString0[] = { /* language descriptor */ + 4, /* sizeof(usbDescriptorString0): length of descriptor in bytes */ + 3, /* descriptor type */ + 0x09, 0x04, /* language index (0x0409 = US-English) */ +}; +#endif + +#if USB_CFG_DESCR_PROPS_STRING_VENDOR == 0 && USB_CFG_VENDOR_NAME_LEN +#undef USB_CFG_DESCR_PROPS_STRING_VENDOR +#define USB_CFG_DESCR_PROPS_STRING_VENDOR sizeof(usbDescriptorStringVendor) +PROGMEM int usbDescriptorStringVendor[] = { + USB_STRING_DESCRIPTOR_HEADER(USB_CFG_VENDOR_NAME_LEN), + USB_CFG_VENDOR_NAME +}; +#endif + +#if USB_CFG_DESCR_PROPS_STRING_DEVICE == 0 && USB_CFG_DEVICE_NAME_LEN +#undef USB_CFG_DESCR_PROPS_STRING_DEVICE +#define USB_CFG_DESCR_PROPS_STRING_DEVICE sizeof(usbDescriptorStringDevice) +PROGMEM int usbDescriptorStringDevice[] = { + USB_STRING_DESCRIPTOR_HEADER(USB_CFG_DEVICE_NAME_LEN), + USB_CFG_DEVICE_NAME +}; +#endif + +#if USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER == 0 && USB_CFG_SERIAL_NUMBER_LEN +#undef USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER +#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER sizeof(usbDescriptorStringSerialNumber) +PROGMEM int usbDescriptorStringSerialNumber[] = { + USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LEN), + USB_CFG_SERIAL_NUMBER +}; +#endif + +#endif /* USB_CFG_DESCR_PROPS_STRINGS == 0 */ + +#if USB_CFG_DESCR_PROPS_DEVICE == 0 +#undef USB_CFG_DESCR_PROPS_DEVICE +#define USB_CFG_DESCR_PROPS_DEVICE sizeof(usbDescriptorDevice) +PROGMEM char usbDescriptorDevice[] = { /* USB device descriptor */ + 18, /* sizeof(usbDescriptorDevice): length of descriptor in bytes */ + USBDESCR_DEVICE, /* descriptor type */ + 0x01, 0x01, /* USB version supported */ + USB_CFG_DEVICE_CLASS, + USB_CFG_DEVICE_SUBCLASS, + 0, /* protocol */ + 8, /* max packet size */ + USB_CFG_VENDOR_ID, /* 2 bytes */ + USB_CFG_DEVICE_ID, /* 2 bytes */ + USB_CFG_DEVICE_VERSION, /* 2 bytes */ + USB_CFG_DESCR_PROPS_STRING_VENDOR != 0 ? 1 : 0, /* manufacturer string index */ + USB_CFG_DESCR_PROPS_STRING_DEVICE != 0 ? 2 : 0, /* product string index */ + USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER != 0 ? 3 : 0, /* serial number string index */ + 1, /* number of configurations */ +}; +#endif + +#if USB_CFG_DESCR_PROPS_HID_REPORT != 0 && USB_CFG_DESCR_PROPS_HID == 0 +#undef USB_CFG_DESCR_PROPS_HID +#define USB_CFG_DESCR_PROPS_HID 9 /* length of HID descriptor in config descriptor below */ +#endif + +#if USB_CFG_DESCR_PROPS_CONFIGURATION == 0 +#undef USB_CFG_DESCR_PROPS_CONFIGURATION +#define USB_CFG_DESCR_PROPS_CONFIGURATION sizeof(usbDescriptorConfiguration) +PROGMEM char usbDescriptorConfiguration[] = { /* USB configuration descriptor */ + 9, /* sizeof(usbDescriptorConfiguration): length of descriptor in bytes */ + USBDESCR_CONFIG, /* descriptor type */ + 18 + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT + (USB_CFG_DESCR_PROPS_HID & 0xff), 0, + /* total length of data returned (including inlined descriptors) */ + 1, /* number of interfaces in this configuration */ + 1, /* index of this configuration */ + 0, /* configuration name string index */ +#if USB_CFG_IS_SELF_POWERED + USBATTR_SELFPOWER, /* attributes */ +#else + USBATTR_BUSPOWER, /* attributes */ +#endif + USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */ +/* interface descriptor follows inline: */ + 9, /* sizeof(usbDescrInterface): length of descriptor in bytes */ + USBDESCR_INTERFACE, /* descriptor type */ + 0, /* index of this interface */ + 0, /* alternate setting for this interface */ + USB_CFG_HAVE_INTRIN_ENDPOINT, /* endpoints excl 0: number of endpoint descriptors to follow */ + USB_CFG_INTERFACE_CLASS, + USB_CFG_INTERFACE_SUBCLASS, + USB_CFG_INTERFACE_PROTOCOL, + 0, /* string index for interface */ +#if (USB_CFG_DESCR_PROPS_HID & 0xff) /* HID descriptor */ + 9, /* sizeof(usbDescrHID): length of descriptor in bytes */ + USBDESCR_HID, /* descriptor type: HID */ + 0x01, 0x01, /* BCD representation of HID version */ + 0x00, /* target country code */ + 0x01, /* number of HID Report (or other HID class) Descriptor infos to follow */ + 0x22, /* descriptor type: report */ + USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH, 0, /* total length of report descriptor */ +#endif +#if USB_CFG_HAVE_INTRIN_ENDPOINT /* endpoint descriptor for endpoint 1 */ + 7, /* sizeof(usbDescrEndpoint) */ + USBDESCR_ENDPOINT, /* descriptor type = endpoint */ + 0x81, /* IN endpoint number 1 */ + 0x03, /* attrib: Interrupt endpoint */ + 8, 0, /* maximum packet size */ + USB_CFG_INTR_POLL_INTERVAL, /* in ms */ +#endif +}; +#endif + +/* We don't use prog_int or prog_int16_t for compatibility with various libc + * versions. Here's an other compatibility hack: + */ +#ifndef PRG_RDB +#define PRG_RDB(addr) pgm_read_byte(addr) +#endif + +typedef union{ + unsigned word; + uchar *ptr; + uchar bytes[2]; +}converter_t; +/* We use this union to do type conversions. This is better optimized than + * type casts in gcc 3.4.3 and much better than using bit shifts to build + * ints from chars. Byte ordering is not a problem on an 8 bit platform. + */ + +/* ------------------------------------------------------------------------- */ + +#if USB_CFG_HAVE_INTRIN_ENDPOINT +void usbSetInterrupt(uchar *data, uchar len) +{ +uchar *p, i; + +#if USB_CFG_IMPLEMENT_HALT + if(usbTxLen1 == USBPID_STALL) + return; +#endif +#if 0 /* No runtime checks! Caller is responsible for valid data! */ + if(len > 8) /* interrupt transfers are limited to 8 bytes */ + len = 8; +#endif + if(usbTxLen1 & 0x10){ /* packet buffer was empty */ + usbTxBuf1[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ + }else{ + usbTxLen1 = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ + } + p = usbTxBuf1 + 1; + for(i=len;i--;) + *p++ = *data++; + usbCrc16Append(&usbTxBuf1[1], len); + usbTxLen1 = len + 4; /* len must be given including sync byte */ + DBG2(0x21, usbTxBuf1, len + 3); +} +#endif + +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 +void usbSetInterrupt3(uchar *data, uchar len) +{ +uchar *p, i; + + if(usbTxLen3 & 0x10){ /* packet buffer was empty */ + usbTxBuf3[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ + }else{ + usbTxLen3 = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ + } + p = usbTxBuf3 + 1; + for(i=len;i--;) + *p++ = *data++; + usbCrc16Append(&usbTxBuf3[1], len); + usbTxLen3 = len + 4; /* len must be given including sync byte */ + DBG2(0x23, usbTxBuf3, len + 3); +} +#endif + + +static uchar usbRead(uchar *data, uchar len) +{ +#if USB_CFG_IMPLEMENT_FN_READ + if(usbMsgFlags & USB_FLG_USE_DEFAULT_RW){ +#endif + uchar i = len, *r = usbMsgPtr; + if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){ /* ROM data */ + while(i--){ + uchar c = PRG_RDB(r); /* assign to char size variable to enforce byte ops */ + *data++ = c; + r++; + } + }else{ /* RAM data */ + while(i--) + *data++ = *r++; + } + usbMsgPtr = r; + return len; +#if USB_CFG_IMPLEMENT_FN_READ + }else{ + if(len != 0) /* don't bother app with 0 sized reads */ + return usbFunctionRead(data, len); + return 0; + } +#endif +} + + +#define GET_DESCRIPTOR(cfgProp, staticName) \ + if(cfgProp){ \ + if((cfgProp) & USB_PROP_IS_RAM) \ + flags &= ~USB_FLG_MSGPTR_IS_ROM; \ + if((cfgProp) & USB_PROP_IS_DYNAMIC){ \ + replyLen = usbFunctionDescriptor(rq); \ + }else{ \ + replyData = (uchar *)(staticName); \ + SET_REPLY_LEN((cfgProp) & 0xff); \ + } \ + } +/* We use if() instead of #if in the macro above because #if can't be used + * in macros and the compiler optimizes constant conditions anyway. + */ + + +/* Don't make this function static to avoid inlining. + * The entire function would become too large and exceed the range of + * relative jumps. + * 2006-02-25: Either gcc 3.4.3 is better than the gcc used when the comment + * above was written, or other parts of the code have changed. We now get + * better results with an inlined function. Test condition: PowerSwitch code. + */ +static void usbProcessRx(uchar *data, uchar len) +{ +usbRequest_t *rq = (void *)data; +uchar replyLen = 0, flags = USB_FLG_USE_DEFAULT_RW; +/* We use if() cascades because the compare is done byte-wise while switch() + * is int-based. The if() cascades are therefore more efficient. + */ +/* usbRxToken can be: + * 0x2d 00101101 (USBPID_SETUP for endpoint 0) + * 0xe1 11100001 (USBPID_OUT for endpoint 0) + * 0xff 11111111 (USBPID_OUT for endpoint 1) + */ + DBG2(0x10 + ((usbRxToken >> 1) & 3), data, len); /* SETUP0=12; OUT0=10; OUT1=13 */ +#if USB_CFG_IMPLEMENT_FN_WRITEOUT + if(usbRxToken == 0xff){ + usbFunctionWriteOut(data, len); + return; /* no reply expected, hence no usbMsgPtr, usbMsgFlags, usbMsgLen set */ + } +#endif + if(usbRxToken == (uchar)USBPID_SETUP){ + usbTxLen = USBPID_NAK; /* abort pending transmit */ + if(len == 8){ /* Setup size must be always 8 bytes. Ignore otherwise. */ + uchar type = rq->bmRequestType & USBRQ_TYPE_MASK; + if(type == USBRQ_TYPE_STANDARD){ + #define SET_REPLY_LEN(len) replyLen = (len); usbMsgPtr = replyData + /* This macro ensures that replyLen and usbMsgPtr are always set in the same way. + * That allows optimization of common code in if() branches */ + uchar *replyData = usbTxBuf + 9; /* there is 3 bytes free space at the end of the buffer */ + replyData[0] = 0; /* common to USBRQ_GET_STATUS and USBRQ_GET_INTERFACE */ + if(rq->bRequest == USBRQ_GET_STATUS){ /* 0 */ + uchar __attribute__((__unused__)) recipient = rq->bmRequestType & USBRQ_RCPT_MASK; /* assign arith ops to variables to enforce byte size */ +#if USB_CFG_IS_SELF_POWERED + if(recipient == USBRQ_RCPT_DEVICE) + replyData[0] = USB_CFG_IS_SELF_POWERED; +#endif +#if USB_CFG_HAVE_INTRIN_ENDPOINT && USB_CFG_IMPLEMENT_HALT + if(recipient == USBRQ_RCPT_ENDPOINT && rq->wIndex.bytes[0] == 0x81) /* request status for endpoint 1 */ + replyData[0] = usbTxLen1 == USBPID_STALL; +#endif + replyData[1] = 0; + SET_REPLY_LEN(2); + }else if(rq->bRequest == USBRQ_SET_ADDRESS){ /* 5 */ + usbNewDeviceAddr = rq->wValue.bytes[0]; + }else if(rq->bRequest == USBRQ_GET_DESCRIPTOR){ /* 6 */ + flags = USB_FLG_MSGPTR_IS_ROM | USB_FLG_USE_DEFAULT_RW; + if(rq->wValue.bytes[1] == USBDESCR_DEVICE){ /* 1 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_DEVICE, usbDescriptorDevice) + }else if(rq->wValue.bytes[1] == USBDESCR_CONFIG){ /* 2 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_CONFIGURATION, usbDescriptorConfiguration) + }else if(rq->wValue.bytes[1] == USBDESCR_STRING){ /* 3 */ +#if USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC + if(USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_RAM) + flags &= ~USB_FLG_MSGPTR_IS_ROM; + replyLen = usbFunctionDescriptor(rq); +#else /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ + if(rq->wValue.bytes[0] == 0){ /* descriptor index */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_0, usbDescriptorString0) + }else if(rq->wValue.bytes[0] == 1){ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_VENDOR, usbDescriptorStringVendor) + }else if(rq->wValue.bytes[0] == 2){ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_DEVICE, usbDescriptorStringDevice) + }else if(rq->wValue.bytes[0] == 3){ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER, usbDescriptorStringSerialNumber) + }else if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ + replyLen = usbFunctionDescriptor(rq); + } +#endif /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ + }else if(rq->wValue.bytes[1] == USBDESCR_HID){ /* 0x21 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID, usbDescriptorConfiguration + 18) + }else if(rq->wValue.bytes[1] == USBDESCR_HID_REPORT){ /* 0x22 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID_REPORT, usbDescriptorHidReport) + }else if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ + replyLen = usbFunctionDescriptor(rq); + } + }else if(rq->bRequest == USBRQ_GET_CONFIGURATION){ /* 8 */ + replyData = &usbConfiguration; /* send current configuration value */ + SET_REPLY_LEN(1); + }else if(rq->bRequest == USBRQ_SET_CONFIGURATION){ /* 9 */ + usbConfiguration = rq->wValue.bytes[0]; +#if USB_CFG_IMPLEMENT_HALT + usbTxLen1 = USBPID_NAK; +#endif + }else if(rq->bRequest == USBRQ_GET_INTERFACE){ /* 10 */ + SET_REPLY_LEN(1); +#if USB_CFG_HAVE_INTRIN_ENDPOINT + }else if(rq->bRequest == USBRQ_SET_INTERFACE){ /* 11 */ + USB_SET_DATATOKEN1(USBPID_DATA0); /* reset data toggling for interrupt endpoint */ +# if USB_CFG_HAVE_INTRIN_ENDPOINT3 + USB_SET_DATATOKEN3(USBPID_DATA0); /* reset data toggling for interrupt endpoint */ +# endif +# if USB_CFG_IMPLEMENT_HALT + usbTxLen1 = USBPID_NAK; + }else if(rq->bRequest == USBRQ_CLEAR_FEATURE || rq->bRequest == USBRQ_SET_FEATURE){ /* 1|3 */ + if(rq->wValue.bytes[0] == 0 && rq->wIndex.bytes[0] == 0x81){ /* feature 0 == HALT for endpoint == 1 */ + usbTxLen1 = rq->bRequest == USBRQ_CLEAR_FEATURE ? USBPID_NAK : USBPID_STALL; + USB_SET_DATATOKEN1(USBPID_DATA0); /* reset data toggling for interrupt endpoint */ +# if USB_CFG_HAVE_INTRIN_ENDPOINT3 + USB_SET_DATATOKEN3(USBPID_DATA0); /* reset data toggling for interrupt endpoint */ +# endif + } +# endif +#endif + }else{ + /* the following requests can be ignored, send default reply */ + /* 1: CLEAR_FEATURE, 3: SET_FEATURE, 7: SET_DESCRIPTOR */ + /* 12: SYNCH_FRAME */ + } + #undef SET_REPLY_LEN + }else{ /* not a standard request -- must be vendor or class request */ + replyLen = usbFunctionSetup(data); + } +#if USB_CFG_IMPLEMENT_FN_READ || USB_CFG_IMPLEMENT_FN_WRITE + if(replyLen == 0xff){ /* use user-supplied read/write function */ + if((rq->bmRequestType & USBRQ_DIR_MASK) == USBRQ_DIR_DEVICE_TO_HOST){ + replyLen = rq->wLength.bytes[0]; /* IN transfers only */ + } + flags &= ~USB_FLG_USE_DEFAULT_RW; /* we have no valid msg, use user supplied read/write functions */ + }else /* The 'else' prevents that we limit a replyLen of 0xff to the maximum transfer len. */ +#endif + if(!rq->wLength.bytes[1] && replyLen > rq->wLength.bytes[0]) /* limit length to max */ + replyLen = rq->wLength.bytes[0]; + } + /* make sure that data packets which are sent as ACK to an OUT transfer are always zero sized */ + }else{ /* DATA packet from out request */ +#if USB_CFG_IMPLEMENT_FN_WRITE + if(!(usbMsgFlags & USB_FLG_USE_DEFAULT_RW)){ + uchar rval = usbFunctionWrite(data, len); + replyLen = 0xff; + if(rval == 0xff){ /* an error occurred */ + usbMsgLen = 0xff; /* cancel potentially pending data packet for ACK */ + usbTxLen = USBPID_STALL; + }else if(rval != 0){ /* This was the final package */ + replyLen = 0; /* answer with a zero-sized data packet */ + } + flags = 0; /* start with a DATA1 package, stay with user supplied write() function */ + } +#endif + } + usbMsgFlags = flags; + usbMsgLen = replyLen; +} + +/* ------------------------------------------------------------------------- */ + +static void usbBuildTxBlock(void) +{ +uchar wantLen, len, txLen, token; + + wantLen = usbMsgLen; + if(wantLen > 8) + wantLen = 8; + usbMsgLen -= wantLen; + token = USBPID_DATA1; + if(usbMsgFlags & USB_FLG_TX_PACKET) + token = USBPID_DATA0; + usbMsgFlags++; + len = usbRead(usbTxBuf + 1, wantLen); + if(len <= 8){ /* valid data packet */ + usbCrc16Append(&usbTxBuf[1], len); + txLen = len + 4; /* length including sync byte */ + if(len < 8) /* a partial package identifies end of message */ + usbMsgLen = 0xff; + }else{ + txLen = USBPID_STALL; /* stall the endpoint */ + usbMsgLen = 0xff; + } + usbTxBuf[0] = token; + usbTxLen = txLen; + DBG2(0x20, usbTxBuf, txLen-1); +} + +static inline uchar isNotSE0(void) +{ +uchar rval; +/* We want to do + * return (USBIN & USBMASK); + * here, but the compiler does int-expansion acrobatics. + * We can avoid this by assigning to a char-sized variable. + */ + rval = USBIN & USBMASK; + return rval; +} + +/* ------------------------------------------------------------------------- */ + +void usbPoll(void) +{ +schar len; +uchar i; + + if((len = usbRxLen) > 0){ +/* We could check CRC16 here -- but ACK has already been sent anyway. If you + * need data integrity checks with this driver, check the CRC in your app + * code and report errors back to the host. Since the ACK was already sent, + * retries must be handled on application level. + * unsigned crc = usbCrc16(buffer + 1, usbRxLen - 3); + */ + usbProcessRx(usbRxBuf + USB_BUFSIZE + 1 - usbInputBufOffset, len - 3); +#if USB_CFG_HAVE_FLOWCONTROL + if(usbRxLen > 0) /* only mark as available if not inactivated */ + usbRxLen = 0; +#else + usbRxLen = 0; /* mark rx buffer as available */ +#endif + } + if(usbTxLen & 0x10){ /* transmit system idle */ + if(usbMsgLen != 0xff){ /* transmit data pending? */ + usbBuildTxBlock(); + } + } + for(i = 10; i > 0; i--){ + if(isNotSE0()) + break; + } + if(i == 0){ /* RESET condition, called multiple times during reset */ + usbNewDeviceAddr = 0; + usbDeviceAddr = 0; +#if USB_CFG_IMPLEMENT_HALT + usbTxLen1 = USBPID_NAK; +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 + usbTxLen3 = USBPID_NAK; +#endif +#endif + DBG1(0xff, 0, 0); + } +} + +/* ------------------------------------------------------------------------- */ + +void usbInit(void) +{ +#if USB_INTR_CFG_SET != 0 + USB_INTR_CFG |= USB_INTR_CFG_SET; +#endif +#if USB_INTR_CFG_CLR != 0 + USB_INTR_CFG &= ~(USB_INTR_CFG_CLR); +#endif + USB_INTR_ENABLE |= (1 << USB_INTR_ENABLE_BIT); +#if USB_CFG_HAVE_INTRIN_ENDPOINT + USB_SET_DATATOKEN1(USBPID_DATA0); /* reset data toggling for interrupt endpoint */ +# if USB_CFG_HAVE_INTRIN_ENDPOINT3 + USB_SET_DATATOKEN3(USBPID_DATA0); /* reset data toggling for interrupt endpoint */ +# endif +#endif +} + +/* ------------------------------------------------------------------------- */ diff --git a/firmware/usbdrv/usbdrv.h b/firmware/usbdrv/usbdrv.h new file mode 100644 index 0000000..c3adb85 --- /dev/null +++ b/firmware/usbdrv/usbdrv.h @@ -0,0 +1,633 @@ +/* Name: usbdrv.h + * Project: AVR USB driver + * Author: Christian Starkjohann + * Creation Date: 2004-12-29 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) + * This Revision: $Id: usbdrv.h,v 1.1 2008/07/09 20:47:12 rschaten Exp $ + */ + +#ifndef __usbdrv_h_included__ +#define __usbdrv_h_included__ +#include "usbconfig.h" +#include "iarcompat.h" + +/* +Hardware Prerequisites: +======================= +USB lines D+ and D- MUST be wired to the same I/O port. D+ must (also) be +connected to INT0. D- requires a pullup of 1.5k to +3.5V (and the device +must be powered at 3.5V) to identify as low-speed USB device. A pullup of +1M SHOULD be connected from D+ to +3.5V to prevent interference when no USB +master is connected. We use D+ as interrupt source and not D- because it +does not trigger on keep-alive and RESET states. + +As a compile time option, the 1.5k pullup resistor on D- can be made +switchable to allow the device to disconnect at will. See the definition of +usbDeviceConnect() and usbDeviceDisconnect() further down in this file. + +Please adapt the values in usbconfig.h according to your hardware! + +The device MUST be clocked at 12 MHz. This is more than the 10 MHz allowed by +an AT90S2313 powered at 4.5V. However, if the supply voltage to maximum clock +relation is interpolated linearly, an ATtiny2313 meets the requirement by +specification. In practice, the AT90S2313 can be overclocked and works well. + + +Limitations: +============ +Robustness with respect to communication errors: +The driver assumes error-free communication. It DOES check for errors in +the PID, but does NOT check bit stuffing errors, SE0 in middle of a byte, +token CRC (5 bit) and data CRC (16 bit). CRC checks can not be performed due +to timing constraints: We must start sending a reply within 7 bit times. +Bit stuffing and misplaced SE0 would have to be checked in real-time, but CPU +performance does not permit that. The driver does not check Data0/Data1 +toggling, but application software can implement the check. + +Input characteristics: +Since no differential receiver circuit is used, electrical interference +robustness may suffer. The driver samples only one of the data lines with +an ordinary I/O pin's input characteristics. However, since this is only a +low speed USB implementation and the specification allows for 8 times the +bit rate over the same hardware, we should be on the safe side. Even the spec +requires detection of asymmetric states at high bit rate for SE0 detection. + +Number of endpoints: +The driver supports up to four endpoints: One control endpoint (endpoint 0), +two interrupt-in (or bulk-in) endpoints (endpoint 1 and 3) and one +interrupt-out (or bulk-out) endpoint (endpoint 1). Please note that the USB +standard forbids bulk endpoints for low speed devices! Most operating systems +allow them anyway, but the AVR will spend 90% of the CPU time in the USB +interrupt polling for bulk data. +By default, only the control endpoint 0 is enabled. To get the other endpoints, +define USB_CFG_HAVE_INTRIN_ENDPOINT, USB_CFG_HAVE_INTRIN_ENDPOINT3 and/or +USB_CFG_IMPLEMENT_FN_WRITEOUT respectively (see usbconfig-prototype.h for +details). + +Maximum data payload: +Data payload of control in and out transfers may be up to 254 bytes. In order +to accept payload data of out transfers, you need to implement +'usbFunctionWrite()'. + +USB Suspend Mode supply current: +The USB standard limits power consumption to 500uA when the bus is in suspend +mode. This is not a problem for self-powered devices since they don't need +bus power anyway. Bus-powered devices can achieve this only by putting the +CPU in sleep mode. The driver does not implement suspend handling by itself. +However, the application may implement activity monitoring and wakeup from +sleep. The host sends regular SE0 states on the bus to keep it active. These +SE0 states can be detected by wiring the INT1 pin to D-. It is not necessary +to enable the interrupt, checking the interrupt pending flag should suffice. +Before entering sleep mode, the application should enable INT1 for a wakeup +on the next bus activity. + +Operation without an USB master: +The driver behaves neutral without connection to an USB master if D- reads +as 1. To avoid spurious interrupts, we recommend a high impedance (e.g. 1M) +pullup resistor on D+. If D- becomes statically 0, the driver may block in +the interrupt routine. + +Interrupt latency: +The application must ensure that the USB interrupt is not disabled for more +than 20 cycles. This implies that all interrupt routines must either be +declared as "INTERRUPT" instead of "SIGNAL" (see "avr/signal.h") or that they +are written in assembler with "sei" as the first instruction. + +Maximum interrupt duration / CPU cycle consumption: +The driver handles all USB communication during the interrupt service +routine. The routine will not return before an entire USB message is received +and the reply is sent. This may be up to ca. 1200 cycles = 100us if the host +conforms to the standard. The driver will consume CPU cycles for all USB +messages, even if they address another (low-speed) device on the same bus. + +*/ + +/* ------------------------------------------------------------------------- */ +/* --------------------------- Module Interface ---------------------------- */ +/* ------------------------------------------------------------------------- */ + +#define USBDRV_VERSION 20070329 +/* This define uniquely identifies a driver version. It is a decimal number + * constructed from the driver's release date in the form YYYYMMDD. If the + * driver's behavior or interface changes, you can use this constant to + * distinguish versions. If it is not defined, the driver's release date is + * older than 2006-01-25. + */ + +#ifndef __ASSEMBLER__ + +#ifndef uchar +#define uchar unsigned char +#endif +#ifndef schar +#define schar signed char +#endif +/* shortcuts for well defined 8 bit integer types */ + +struct usbRequest; /* forward declaration */ + +extern void usbInit(void); +/* This function must be called before interrupts are enabled and the main + * loop is entered. + */ +extern void usbPoll(void); +/* This function must be called at regular intervals from the main loop. + * Maximum delay between calls is somewhat less than 50ms (USB timeout for + * accepting a Setup message). Otherwise the device will not be recognized. + * Please note that debug outputs through the UART take ~ 0.5ms per byte + * at 19200 bps. + */ +extern uchar *usbMsgPtr; +/* This variable may be used to pass transmit data to the driver from the + * implementation of usbFunctionWrite(). It is also used internally by the + * driver for standard control requests. + */ +extern uchar usbFunctionSetup(uchar data[8]); +/* This function is called when the driver receives a SETUP transaction from + * the host which is not answered by the driver itself (in practice: class and + * vendor requests). All control transfers start with a SETUP transaction where + * the host communicates the parameters of the following (optional) data + * transfer. The SETUP data is available in the 'data' parameter which can + * (and should) be casted to 'usbRequest_t *' for a more user-friendly access + * to parameters. + * + * If the SETUP indicates a control-in transfer, you should provide the + * requested data to the driver. There are two ways to transfer this data: + * (1) Set the global pointer 'usbMsgPtr' to the base of the static RAM data + * block and return the length of the data in 'usbFunctionSetup()'. The driver + * will handle the rest. Or (2) return 0xff in 'usbFunctionSetup()'. The driver + * will then call 'usbFunctionRead()' when data is needed. See the + * documentation for usbFunctionRead() for details. + * + * If the SETUP indicates a control-out transfer, the only way to receive the + * data from the host is through the 'usbFunctionWrite()' call. If you + * implement this function, you must return 0xff in 'usbFunctionSetup()' to + * indicate that 'usbFunctionWrite()' should be used. See the documentation of + * this function for more information. If you just want to ignore the data sent + * by the host, return 0 in 'usbFunctionSetup()'. + * + * Note that calls to the functions usbFunctionRead() and usbFunctionWrite() + * are only done if enabled by the configuration in usbconfig.h. + */ +extern uchar usbFunctionDescriptor(struct usbRequest *rq); +/* You need to implement this function ONLY if you provide USB descriptors at + * runtime (which is an expert feature). It is very similar to + * usbFunctionSetup() above, but it is called only to request USB descriptor + * data. See the documentation of usbFunctionSetup() above for more info. + */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT +void usbSetInterrupt(uchar *data, uchar len); +/* This function sets the message which will be sent during the next interrupt + * IN transfer. The message is copied to an internal buffer and must not exceed + * a length of 8 bytes. The message may be 0 bytes long just to indicate the + * interrupt status to the host. + * If you need to transfer more bytes, use a control read after the interrupt. + */ +extern volatile uchar usbTxLen1; +#define usbInterruptIsReady() (usbTxLen1 & 0x10) +/* This macro indicates whether the last interrupt message has already been + * sent. If you set a new interrupt message before the old was sent, the + * message already buffered will be lost. + */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 +void usbSetInterrupt3(uchar *data, uchar len); +extern volatile uchar usbTxLen3; +#define usbInterruptIsReady3() (usbTxLen3 & 0x10) +/* Same as above for endpoint 3 */ +#endif +#endif /* USB_CFG_HAVE_INTRIN_ENDPOINT */ +#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH /* simplified interface for backward compatibility */ +#define usbHidReportDescriptor usbDescriptorHidReport +/* should be declared as: PROGMEM char usbHidReportDescriptor[]; */ +/* If you implement an HID device, you need to provide a report descriptor. + * The HID report descriptor syntax is a bit complex. If you understand how + * report descriptors are constructed, we recommend that you use the HID + * Descriptor Tool from usb.org, see http://www.usb.org/developers/hidpage/. + * Otherwise you should probably start with a working example. + */ +#endif /* USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH */ +#if USB_CFG_IMPLEMENT_FN_WRITE +extern uchar usbFunctionWrite(uchar *data, uchar len); +/* This function is called by the driver to provide a control transfer's + * payload data (control-out). It is called in chunks of up to 8 bytes. The + * total count provided in the current control transfer can be obtained from + * the 'length' property in the setup data. If an error occurred during + * processing, return 0xff (== -1). The driver will answer the entire transfer + * with a STALL token in this case. If you have received the entire payload + * successfully, return 1. If you expect more data, return 0. If you don't + * know whether the host will send more data (you should know, the total is + * provided in the usbFunctionSetup() call!), return 1. + * NOTE: If you return 0xff for STALL, 'usbFunctionWrite()' may still be called + * for the remaining data. You must continue to return 0xff for STALL in these + * calls. + * In order to get usbFunctionWrite() called, define USB_CFG_IMPLEMENT_FN_WRITE + * to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. + */ +#endif /* USB_CFG_IMPLEMENT_FN_WRITE */ +#if USB_CFG_IMPLEMENT_FN_READ +extern uchar usbFunctionRead(uchar *data, uchar len); +/* This function is called by the driver to ask the application for a control + * transfer's payload data (control-in). It is called in chunks of up to 8 + * bytes each. You should copy the data to the location given by 'data' and + * return the actual number of bytes copied. If you return less than requested, + * the control-in transfer is terminated. If you return 0xff, the driver aborts + * the transfer with a STALL token. + * In order to get usbFunctionRead() called, define USB_CFG_IMPLEMENT_FN_READ + * to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. + */ +#endif /* USB_CFG_IMPLEMENT_FN_READ */ +#if USB_CFG_IMPLEMENT_FN_WRITEOUT +extern void usbFunctionWriteOut(uchar *data, uchar len); +/* This function is called by the driver when data on interrupt-out or bulk- + * out endpoint 1 is received. You must define USB_CFG_IMPLEMENT_FN_WRITEOUT + * to 1 in usbconfig.h to get this function called. + */ +#endif /* USB_CFG_IMPLEMENT_FN_WRITEOUT */ +#ifdef USB_CFG_PULLUP_IOPORTNAME +#define usbDeviceConnect() ((USB_PULLUP_DDR |= (1<device, 1=device->host + * t ..... type: 0=standard, 1=class, 2=vendor, 3=reserved + * r ..... recipient: 0=device, 1=interface, 2=endpoint, 3=other + */ + +/* USB setup recipient values */ +#define USBRQ_RCPT_MASK 0x1f +#define USBRQ_RCPT_DEVICE 0 +#define USBRQ_RCPT_INTERFACE 1 +#define USBRQ_RCPT_ENDPOINT 2 + +/* USB request type values */ +#define USBRQ_TYPE_MASK 0x60 +#define USBRQ_TYPE_STANDARD (0<<5) +#define USBRQ_TYPE_CLASS (1<<5) +#define USBRQ_TYPE_VENDOR (2<<5) + +/* USB direction values: */ +#define USBRQ_DIR_MASK 0x80 +#define USBRQ_DIR_HOST_TO_DEVICE (0<<7) +#define USBRQ_DIR_DEVICE_TO_HOST (1<<7) + +/* USB Standard Requests */ +#define USBRQ_GET_STATUS 0 +#define USBRQ_CLEAR_FEATURE 1 +#define USBRQ_SET_FEATURE 3 +#define USBRQ_SET_ADDRESS 5 +#define USBRQ_GET_DESCRIPTOR 6 +#define USBRQ_SET_DESCRIPTOR 7 +#define USBRQ_GET_CONFIGURATION 8 +#define USBRQ_SET_CONFIGURATION 9 +#define USBRQ_GET_INTERFACE 10 +#define USBRQ_SET_INTERFACE 11 +#define USBRQ_SYNCH_FRAME 12 + +/* USB descriptor constants */ +#define USBDESCR_DEVICE 1 +#define USBDESCR_CONFIG 2 +#define USBDESCR_STRING 3 +#define USBDESCR_INTERFACE 4 +#define USBDESCR_ENDPOINT 5 +#define USBDESCR_HID 0x21 +#define USBDESCR_HID_REPORT 0x22 +#define USBDESCR_HID_PHYS 0x23 + +#define USBATTR_BUSPOWER 0x80 +#define USBATTR_SELFPOWER 0x40 +#define USBATTR_REMOTEWAKE 0x20 + +/* USB HID Requests */ +#define USBRQ_HID_GET_REPORT 0x01 +#define USBRQ_HID_GET_IDLE 0x02 +#define USBRQ_HID_GET_PROTOCOL 0x03 +#define USBRQ_HID_SET_REPORT 0x09 +#define USBRQ_HID_SET_IDLE 0x0a +#define USBRQ_HID_SET_PROTOCOL 0x0b + +/* ------------------------------------------------------------------------- */ + +#endif /* __usbdrv_h_included__ */ diff --git a/firmware/usbdrv/usbdrvasm.S b/firmware/usbdrv/usbdrvasm.S new file mode 100644 index 0000000..7ba4b4b --- /dev/null +++ b/firmware/usbdrv/usbdrvasm.S @@ -0,0 +1,710 @@ +/* Name: usbdrvasm.S + * Project: AVR USB driver + * Author: Christian Starkjohann + * Creation Date: 2004-12-29 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) + * This Revision: $Id: usbdrvasm.S,v 1.1 2008/07/09 20:47:12 rschaten Exp $ + */ + +/* +General Description: +This module implements the assembler part of the USB driver. See usbdrv.h +for a description of the entire driver. +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! + + +Timing constraints according to spec (in bit times): +timing subject min max CPUcycles +--------------------------------------------------------------------------- +EOP of OUT/SETUP to sync pattern of DATA0 (both rx) 2 16 16-128 +EOP of IN to sync pattern of DATA0 (rx, then tx) 2 7.5 16-60 +DATAx (rx) to ACK/NAK/STALL (tx) 2 7.5 16-60 +*/ + +#include "iarcompat.h" +#ifndef __IAR_SYSTEMS_ASM__ + /* configs for io.h */ +# define __SFR_OFFSET 0 +# define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ +# include /* for CPU I/O register definitions and vectors */ +#endif /* __IAR_SYSTEMS_ASM__ */ +#include "usbdrv.h" /* for common defs */ + + +/* register names */ +#define x1 r16 +#define x2 r17 +#define shift r18 +#define cnt r19 +#define x3 r20 +#define x4 r21 + +/* Some assembler dependent definitions and declarations: */ + +#ifdef __IAR_SYSTEMS_ASM__ + +# define nop2 rjmp $+2 /* jump to next instruction */ +# define XL r26 +# define XH r27 +# define YL r28 +# define YH r29 +# define ZL r30 +# define ZH r31 +# define lo8(x) LOW(x) +# define hi8(x) ((x)>>8) /* not HIGH to allow XLINK to make a proper range check */ + + extern usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBufOffset + extern usbCurrentTok, usbRxLen, usbRxToken, usbTxLen + extern usbTxBuf, usbMsgLen, usbTxLen1, usbTxBuf1, usbTxLen3, usbTxBuf3 + public usbCrc16 + public usbCrc16Append + + COMMON INTVEC + ORG INT0_vect + rjmp SIG_INTERRUPT0 + RSEG CODE + +#else /* __IAR_SYSTEMS_ASM__ */ + +# define nop2 rjmp .+0 /* jump to next instruction */ + + .text + .global SIG_INTERRUPT0 + .type SIG_INTERRUPT0, @function + .global usbCrc16 + .global usbCrc16Append + +#endif /* __IAR_SYSTEMS_ASM__ */ + + +;Software-receiver engine. Strict timing! Don't change unless you can preserve timing! +;interrupt response time: 4 cycles + insn running = 7 max if interrupts always enabled +;max allowable interrupt latency: 34 cycles -> max 25 cycles interrupt disable +;max stack usage: [ret(2), YL, SREG, YH, shift, x1, x2, x3, cnt, x4] = 11 bytes +;Numbers in brackets are maximum cycles since SOF. +SIG_INTERRUPT0: +;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt + push YL ;2 [35] push only what is necessary to sync with edge ASAP + in YL, SREG ;1 [37] + push YL ;2 [39] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;first part has no timeout because it waits for IDLE or SE1 (== disconnected) +waitForJ: + sbis USBIN, USBMINUS ;1 [40] wait for D- == 1 + rjmp waitForJ ;2 +waitForK: +;The following code results in a sampling window of 1/4 bit which meets the spec. + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + rjmp sofError +foundK: +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push YH ;2 [2] + lds YL, usbInputBufOffset;2 [4] + clr YH ;1 [5] + subi YL, lo8(-(usbRxBuf));1 [6] + sbci YH, hi8(-(usbRxBuf));1 [7] + + sbis USBIN, USBMINUS ;1 [8] we want two bits K [sample 1 cycle too early] + rjmp haveTwoBitsK ;2 [10] + pop YH ; undo the push from before + rjmp waitForK ; this was not the end of sync, retry +haveTwoBitsK: +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- + push shift ;2 [16] + push x1 ;2 [12] + push x2 ;2 [14] + + in x1, USBIN ;1 [17] <-- sample bit 0 + ldi shift, 0xff ;1 [18] + bst x1, USBMINUS ;1 [19] + bld shift, 0 ;1 [20] + push x3 ;2 [22] + push cnt ;2 [24] + + in x2, USBIN ;1 [25] <-- sample bit 1 + ser x3 ;1 [26] [inserted init instruction] + eor x1, x2 ;1 [27] + bst x1, USBMINUS ;1 [28] + bld shift, 1 ;1 [29] + ldi cnt, USB_BUFSIZE;1 [30] [inserted init instruction] + rjmp rxbit2 ;2 [32] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- + +unstuff0: ;1 (branch taken) + andi x3, ~0x01 ;1 [15] + mov x1, x2 ;1 [16] x2 contains last sampled (stuffed) bit + in x2, USBIN ;1 [17] <-- sample bit 1 again + ori shift, 0x01 ;1 [18] + rjmp didUnstuff0 ;2 [20] + +unstuff1: ;1 (branch taken) + mov x2, x1 ;1 [21] x1 contains last sampled (stuffed) bit + andi x3, ~0x02 ;1 [22] + ori shift, 0x02 ;1 [23] + nop ;1 [24] + in x1, USBIN ;1 [25] <-- sample bit 2 again + rjmp didUnstuff1 ;2 [27] + +unstuff2: ;1 (branch taken) + andi x3, ~0x04 ;1 [29] + ori shift, 0x04 ;1 [30] + mov x1, x2 ;1 [31] x2 contains last sampled (stuffed) bit + nop ;1 [32] + in x2, USBIN ;1 [33] <-- sample bit 3 + rjmp didUnstuff2 ;2 [35] + +unstuff3: ;1 (branch taken) + in x2, USBIN ;1 [34] <-- sample stuffed bit 3 [one cycle too late] + andi x3, ~0x08 ;1 [35] + ori shift, 0x08 ;1 [36] + rjmp didUnstuff3 ;2 [38] + +unstuff4: ;1 (branch taken) + andi x3, ~0x10 ;1 [40] + in x1, USBIN ;1 [41] <-- sample stuffed bit 4 + ori shift, 0x10 ;1 [42] + rjmp didUnstuff4 ;2 [44] + +unstuff5: ;1 (branch taken) + andi x3, ~0x20 ;1 [48] + in x2, USBIN ;1 [49] <-- sample stuffed bit 5 + ori shift, 0x20 ;1 [50] + rjmp didUnstuff5 ;2 [52] + +unstuff6: ;1 (branch taken) + andi x3, ~0x40 ;1 [56] + in x1, USBIN ;1 [57] <-- sample stuffed bit 6 + ori shift, 0x40 ;1 [58] + rjmp didUnstuff6 ;2 [60] + +; extra jobs done during bit interval: +; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs] +; bit 1: se0 check +; bit 2: overflow check +; bit 3: recovery from delay [bit 0 tasks took too long] +; bit 4: none +; bit 5: none +; bit 6: none +; bit 7: jump, eor +rxLoop: + eor x3, shift ;1 [0] reconstruct: x3 is 0 at bit locations we changed, 1 at others + in x1, USBIN ;1 [1] <-- sample bit 0 + st y+, x3 ;2 [3] store data + ser x3 ;1 [4] + nop ;1 [5] + eor x2, x1 ;1 [6] + bst x2, USBMINUS;1 [7] + bld shift, 0 ;1 [8] + in x2, USBIN ;1 [9] <-- sample bit 1 (or possibly bit 0 stuffed) + andi x2, USBMASK ;1 [10] + breq se0 ;1 [11] SE0 check for bit 1 + andi shift, 0xf9 ;1 [12] +didUnstuff0: + breq unstuff0 ;1 [13] + eor x1, x2 ;1 [14] + bst x1, USBMINUS;1 [15] + bld shift, 1 ;1 [16] +rxbit2: + in x1, USBIN ;1 [17] <-- sample bit 2 (or possibly bit 1 stuffed) + andi shift, 0xf3 ;1 [18] + breq unstuff1 ;1 [19] do remaining work for bit 1 +didUnstuff1: + subi cnt, 1 ;1 [20] + brcs overflow ;1 [21] loop control + eor x2, x1 ;1 [22] + bst x2, USBMINUS;1 [23] + bld shift, 2 ;1 [24] + in x2, USBIN ;1 [25] <-- sample bit 3 (or possibly bit 2 stuffed) + andi shift, 0xe7 ;1 [26] + breq unstuff2 ;1 [27] +didUnstuff2: + eor x1, x2 ;1 [28] + bst x1, USBMINUS;1 [29] + bld shift, 3 ;1 [30] +didUnstuff3: + andi shift, 0xcf ;1 [31] + breq unstuff3 ;1 [32] + in x1, USBIN ;1 [33] <-- sample bit 4 + eor x2, x1 ;1 [34] + bst x2, USBMINUS;1 [35] + bld shift, 4 ;1 [36] +didUnstuff4: + andi shift, 0x9f ;1 [37] + breq unstuff4 ;1 [38] + nop2 ;2 [40] + in x2, USBIN ;1 [41] <-- sample bit 5 + eor x1, x2 ;1 [42] + bst x1, USBMINUS;1 [43] + bld shift, 5 ;1 [44] +didUnstuff5: + andi shift, 0x3f ;1 [45] + breq unstuff5 ;1 [46] + nop2 ;2 [48] + in x1, USBIN ;1 [49] <-- sample bit 6 + eor x2, x1 ;1 [50] + bst x2, USBMINUS;1 [51] + bld shift, 6 ;1 [52] +didUnstuff6: + cpi shift, 0x02 ;1 [53] + brlo unstuff6 ;1 [54] + nop2 ;2 [56] + in x2, USBIN ;1 [57] <-- sample bit 7 + eor x1, x2 ;1 [58] + bst x1, USBMINUS;1 [59] + bld shift, 7 ;1 [60] +didUnstuff7: + cpi shift, 0x04 ;1 [61] + brsh rxLoop ;2 [63] loop control +unstuff7: + andi x3, ~0x80 ;1 [63] + ori shift, 0x80 ;1 [64] + in x2, USBIN ;1 [65] <-- sample stuffed bit 7 + nop ;1 [66] + rjmp didUnstuff7 ;2 [68] + + +;---------------------------------------------------------------------------- +; Processing of received packet (numbers in brackets are cycles after end of SE0) +;---------------------------------------------------------------------------- +;This is the only non-error exit point for the software receiver loop +;we don't check any CRCs here because there is no time left. +#define token x1 +se0: ; [0] + subi cnt, USB_BUFSIZE ;1 [1] + neg cnt ;1 [2] + cpi cnt, 3 ;1 [3] + ldi x2, 1<