From: edouard@lafargue.name Date: Thu, 9 Apr 2009 06:43:20 +0000 (+0000) Subject: Initial commit for the firmware. Used the 20090306_ela version as baseline. X-Git-Tag: v1.0.0~615 X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/commitdiff_plain/6658905f18a1eebc148836f26c731dea9c1377dc Initial commit for the firmware. Used the 20090306_ela version as baseline. It is identical to the popular 20081211, with the doob addition (20090301), a linux client, and two additional commands for LF analysis. Let me know if you find issues here! --- diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..8094a4ac --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,281 @@ + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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 diff --git a/LOG.txt b/LOG.txt new file mode 100644 index 00000000..43d215c1 --- /dev/null +++ b/LOG.txt @@ -0,0 +1,3 @@ + +MAY 2008: Added ISO14443 type A support, Gerhard de Koning Gans + diff --git a/README-gj.txt b/README-gj.txt new file mode 100644 index 00000000..9ce27983 --- /dev/null +++ b/README-gj.txt @@ -0,0 +1,28 @@ +Modifications to 20081211 release by d18c7db on proxmark.org + +This compiles fine under the pre-built windows compile environment ProxSpace + +I make no apologies for the utterly cr@p coding. It's rubbish, you've been warned. + +Changes made to armsrc and winsrc, no changed to fpga code. Works fine with the bootloader and fpga images that you will build using the 20081211 release. + + +Extra functionality includes: + +ISO1443a support +================ + +i) Support for cascade 2 select (used for UID's longer than 4 bytes) +ii) Hard-coded (some) responses in for DESfire + + +ISO15563 support +================ + +i) demodulation all moved onto the arm +ii) Addition of a command, hi15reader (a reader simulator) +iii) Addition of a command, hi15sim (a tag simulator) - not working too well + + + +greg.jones@digitalassurance.com \ No newline at end of file diff --git a/README.txt b/README.txt new file mode 100644 index 00000000..541365c2 --- /dev/null +++ b/README.txt @@ -0,0 +1,156 @@ +INTRO: + +This file contains enough software, logic (for the FPGA), and design +documentation for the hardware that you could, at least in theory, +do something useful with a proxmark3. It has commands to: + + * read any kind of 125 kHz unidirectional tag + * simulate any kind of 125 kHz unidirectional tag + +(This is enough to perform all of the silly cloning attacks, like the +ones that I did at the Capitol in Sacramento, or anything involving +a Verichip. From a technical standpoint, these are not that exciting, +although the `software radio' architecture of the proxmark3 makes it +easy and fun to support new formats.) + +As a bonus, I include some code to use the 13.56 MHz hardware, so you can: + + * do anything that a (medium-range) ISO 15693 reader could + * read an ISO 14443 tag, if you know the higher-layer protocol + * pretend to be an ISO 14443 tag, if you know the higher-layer protocol + * snoop on an ISO 14443 transaction + +I am not actively developing any of this. I have other projects that +seem to be more useful. + +USING THE PACKAGE: + +The software tools required to build include: + + * cygwin or other unix-like tools for Windows + * the Microsoft Visual C++ compiler (I use Version 6) + * arm-elf-gcc; I use WinterMute's build, from http://www.devkitpro.org/ + * Xilinx's WebPack tools + * Modelsim (for test only) + * perl + +It is not necessary to build the FPGA image yourself; a pre-compiled +image is provided, as armsrc/fpgaimg.c. This is a generated file, +though, and you can rebuild it by running fpga/go.bat. + +Documentation is minimal, but see the doc/ directory for what exists. A +previous familiarity with the ARM, with digital signal processing, +and with embedded programming in general is assumed. + +The device is used through a specialized command line interface; for +example, to clone a Verichip, you might type: + + loread ; this reads the tag, and stores the + ; raw samples in memory on the ARM + + losamples ; then we download the samples to + ; the PC + + vchdemod clone ; demodulate the ID, and then put it + ; back in a format that we can replay + + losim ; and then replay it + +To read an ISO 15693 tag, you might type: + + hiread ; read the tag; this involves sending a + ; particular command, and then getting + ; the response (which is stored as raw + ; samples in memory on the ARM) + + hisamples ; then download those samples to the PC + + hi15demod ; and demod them to bits (and check the + ; CRC etc. at the same time) + +Notice that in both cases the signal processing mostly happened on the PC +side; that is of course not practical for a real reader, but it is easier +to initially write your code and debug on the PC side than on the ARM. As +long as you use integer math (and I do), it's trivial to port it over +when you're done. + +The USB driver and bootloader are documented (and available separately +for download, if you wish to use them in another project) at + + http://cq.cx/trivia.pl + + +OBTAINING HARDWARE: + +Most of the ultra-low-volume contract assemblers that have sprung up +(Screaming Circuits, the various cheap Asian suppliers, etc.) could put +something like this together with a reasonable yield. A run of around +a dozen units is probably cost-effective. The BOM includes (possibly- +outdated) component pricing, and everything is available from Digikey +and the usual distributors. + +If you've never assembled a modern circuit board by hand, then this is +not a good place to start. Some of the components (e.g. the crystals) +must not be assembled with a soldering iron, and require hot air. + +The schematics are included; the component values given are not +necessarily correct for all situations, but it should be possible to do +nearly anything you would want with appropriate population options. + +The printed circuit board artwork is also available, as Gerbers and an +Excellon drill file. + + +FUTURE PLANS, ENHANCEMENTS THAT YOU COULD MAKE: + +At some point I should write software involving a proper real-time +operating system for the ARM. I would then provide interrupt-driven +drivers for many of the peripherals that are polled now (the USB, +the data stream from the FPGA), which would make it easier to develop +complex applications. + +It would not be all that hard to implement the ISO 15693 reader properly +(with anticollision, all the commands supported, and so on)--the signal +processing is already written, so it is all straightforward applications +work. + +I have basic support for ISO 14443 as well: a sniffer, a simulated +tag, and a reader. It won't do anything useful unless you fill in the +high-layer protocol. + +Nicer (i.e., closer-to-optimal) implementations of all kinds of signal +processing would be useful as well. + +A practical implementation of the learning-the-tag's-ID-from-what-the- +reader-broadcasts-during-anticollision attacks would be relatively +straightforward. This would involve some signal processing on the FPGA, +but not much else after that. + +It would be neat to write a driver that could stream samples from the A/Ds +over USB to the PC, using the full available bandwidth of USB. I am not +yet sure what that would be good for, but surely something. This would +require a kernel-mode driver under Windows, though, which is more work. + + +LICENSING: + +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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Jonathan Westhues +user jwesthues, at host cq.cx + +May 2007, Cambridge MA + diff --git a/armsrc/LCD.c b/armsrc/LCD.c new file mode 100644 index 00000000..02c2f696 --- /dev/null +++ b/armsrc/LCD.c @@ -0,0 +1,123 @@ +#include +#include "apps.h" +#include "LCD.h" + +void LCDSend(unsigned int data) +{ + // 9th bit set for data, clear for command + while ((SPI_STATUS & SPI_STATUS_TX_EMPTY) == 0); // wait for the transfer to complete + // For clarity's sake we pass data with 9th bit clear and commands with 9th + // bit set since they're implemented as defines, se we need to invert bit + SPI_TX_DATA = data^0x100; // Send the data/command +} + +void LCDSetXY(unsigned char x, unsigned char y) +{ + LCDSend(PPASET); // page start/end ram + LCDSend(y); // Start Page to display to + LCDSend(131); // End Page to display to + + LCDSend(PCASET); // column start/end ram + LCDSend(x); // Start Column to display to + LCDSend(131); // End Column to display to +} + +void LCDSetPixel(unsigned char x, unsigned char y, unsigned char color) +{ + LCDSetXY(x,y); // Set position + LCDSend(PRAMWR); // Now write the pixel to the display + LCDSend(color); // Write the data in the specified Color +} + +void LCDFill (unsigned char xs,unsigned char ys,unsigned char width,unsigned char height, unsigned char color) +{ + unsigned char i,j; + + for (i=0;i < height;i++) // Number of horizontal lines + { + LCDSetXY(xs,ys+i); // Goto start of fill area (Top Left) + LCDSend(PRAMWR); // Write to display + + for (j=0;j < width;j++) // pixels per line + LCDSend(color); + } +} + +void LCDString (char *lcd_string, const char *font_style,unsigned char x, unsigned char y, unsigned char fcolor, unsigned char bcolor) +{ + unsigned int i; + unsigned char mask=0, px, py, xme, yme, offset; + const char *data; + + data = font_style; // point to the start of the font table + + xme = *data; // get font x width + data++; + yme = *data; // get font y length + data++; + offset = *data; // get data bytes per font + + do + { + // point to data in table to be loaded + data = (font_style + offset) + (offset * (int)(*lcd_string - 32)); + + for (i=0;i < yme;i++) { + mask |=0x80; + + for (px=x; px < (x + xme); px++) { + py= y + i; + + if (*data & mask) LCDSetPixel (px,py,fcolor); + else LCDSetPixel (px,py,bcolor); + + mask>>=1; + } + data++; + } + x+=xme; + + lcd_string++; // next character in string + + } while(*lcd_string !='\0'); // keep spitting chars out until end of string +} + +void LCDReset(void) +{ + LED_A_ON(); + SetupSpi(SPI_LCD_MODE); + LCD_RESET_LOW(); + SpinDelay(100); + + LCD_RESET_HIGH(); + SpinDelay(100); + LED_A_OFF(); +} + +void LCDInit(void) +{ + int i; + + LCDReset(); + + LCDSend(PSWRESET); // software reset + SpinDelay(100); + LCDSend(PSLEEPOUT); // exit sleep mode + LCDSend(PBSTRON); // booster on + LCDSend(PDISPON); // display on + LCDSend(PNORON); // normal on + LCDSend(PMADCTL); // rotate display 180 deg + LCDSend(0xC0); + + LCDSend(PCOLMOD); // color mode + LCDSend(0x02); // 8bpp color mode + + LCDSend(PSETCON); // set contrast + LCDSend(0xDC); + + // clear display + LCDSetXY(0,0); + LCDSend(PRAMWR); // Write to display + i=LCD_XRES*LCD_YRES; + while(i--) LCDSend(WHITE); +} diff --git a/armsrc/LCD.h b/armsrc/LCD.h new file mode 100644 index 00000000..27971eba --- /dev/null +++ b/armsrc/LCD.h @@ -0,0 +1,120 @@ +#ifndef __LCD +#define __LCD + +#define LCD_RESET_HIGH() PIO_OUTPUT_DATA_SET |= (1< +#include "apps.h" +#include "fonts.h" +#include "LCD.h" + +// The large multi-purpose buffer, typically used to hold A/D samples, +// maybe pre-processed in some way. +DWORD BigBuf[16000]; + +//============================================================================= +// A buffer where we can queue things up to be sent through the FPGA, for +// any purpose (fake tag, as reader, whatever). We go MSB first, since that +// is the order in which they go out on the wire. +//============================================================================= + +BYTE ToSend[256]; +int ToSendMax; +static int ToSendBit; + +void ToSendReset(void) +{ + ToSendMax = -1; + ToSendBit = 8; +} + +void ToSendStuffBit(int b) +{ + if(ToSendBit >= 8) { + ToSendMax++; + ToSend[ToSendMax] = 0; + ToSendBit = 0; + } + + if(b) { + ToSend[ToSendMax] |= (1 << (7 - ToSendBit)); + } + + ToSendBit++; + + if(ToSendBit >= sizeof(ToSend)) { + ToSendBit = 0; + DbpString("ToSendStuffBit overflowed!"); + } +} + +//============================================================================= +// Debug print functions, to go out over USB, to the usual PC-side client. +//============================================================================= + +void DbpString(char *str) +{ + UsbCommand c; + c.cmd = CMD_DEBUG_PRINT_STRING; + c.ext1 = strlen(str); + memcpy(c.d.asBytes, str, c.ext1); + + UsbSendPacket((BYTE *)&c, sizeof(c)); + // TODO fix USB so stupid things like this aren't req'd + SpinDelay(50); +} + +void DbpIntegers(int x1, int x2, int x3) +{ + UsbCommand c; + c.cmd = CMD_DEBUG_PRINT_INTEGERS; + c.ext1 = x1; + c.ext2 = x2; + c.ext3 = x3; + + UsbSendPacket((BYTE *)&c, sizeof(c)); + // XXX + SpinDelay(50); +} + +void AcquireRawAdcSamples125k(BOOL at134khz) +{ + BYTE *dest = (BYTE *)BigBuf; + int n = sizeof(BigBuf); + int i; + + memset(dest,0,n); + + if(at134khz) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_READER_USE_134_KHZ); + } else { + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_READER_USE_125_KHZ); + } + + // Connect the A/D to the peak-detected low-frequency path. + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + + // Give it a bit of time for the resonant antenna to settle. + SpinDelay(50); + + // Now set up the SSC to get the ADC samples that are now streaming at us. + FpgaSetupSsc(); + + i = 0; + for(;;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = 0x43; + LED_D_ON(); + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + dest[i] = (BYTE)SSC_RECEIVE_HOLDING; + i++; + LED_D_OFF(); + if(i >= n) { + break; + } + } + } + DbpIntegers(dest[0], dest[1], at134khz); +} + +//----------------------------------------------------------------------------- +// Read an ADC channel and block till it completes, then return the result +// in ADC units (0 to 1023). Also a routine to average sixteen samples and +// return that. +//----------------------------------------------------------------------------- +static int ReadAdc(int ch) +{ + DWORD d; + + ADC_CONTROL = ADC_CONTROL_RESET; + ADC_MODE = ADC_MODE_PRESCALE(32) | ADC_MODE_STARTUP_TIME(16) | + ADC_MODE_SAMPLE_HOLD_TIME(8); + ADC_CHANNEL_ENABLE = ADC_CHANNEL(ch); + + ADC_CONTROL = ADC_CONTROL_START; + while(!(ADC_STATUS & ADC_END_OF_CONVERSION(ch))) + ; + d = ADC_CHANNEL_DATA(ch); + + return d; +} + +static int AvgAdc(int ch) +{ + int i; + int a = 0; + + for(i = 0; i < 32; i++) { + a += ReadAdc(ch); + } + + return (a + 15) >> 5; +} + +void MeasureAntennaTuning(void) +{ +// Impedances are Zc = 1/(j*omega*C), in ohms +#define LF_TUNING_CAP_Z 1273 // 1 nF @ 125 kHz +#define HF_TUNING_CAP_Z 235 // 50 pF @ 13.56 MHz + + int vLf125, vLf134, vHf; // in mV + + UsbCommand c; + + // Let the FPGA drive the low-frequency antenna around 125 kHz. + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_READER_USE_125_KHZ); + SpinDelay(20); + vLf125 = AvgAdc(4); + // Vref = 3.3V, and a 10000:240 voltage divider on the input + // can measure voltages up to 137500 mV + vLf125 = (137500 * vLf125) >> 10; + + // Let the FPGA drive the low-frequency antenna around 134 kHz. + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_READER_USE_134_KHZ); + SpinDelay(20); + vLf134 = AvgAdc(4); + // Vref = 3.3V, and a 10000:240 voltage divider on the input + // can measure voltages up to 137500 mV + vLf134 = (137500 * vLf134) >> 10; + + // Let the FPGA drive the high-frequency antenna around 13.56 MHz. + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + SpinDelay(20); + vHf = AvgAdc(5); + // Vref = 3300mV, and an 10:1 voltage divider on the input + // can measure voltages up to 33000 mV + vHf = (33000 * vHf) >> 10; + + c.cmd = CMD_MEASURED_ANTENNA_TUNING; + c.ext1 = (vLf125 << 0) | (vLf134 << 16); + c.ext2 = vHf; + c.ext3 = (LF_TUNING_CAP_Z << 0) | (HF_TUNING_CAP_Z << 16); + UsbSendPacket((BYTE *)&c, sizeof(c)); +} + +void SimulateTagLowFrequency(int period) +{ + int i; + BYTE *tab = (BYTE *)BigBuf; + + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_SIMULATOR); + + PIO_ENABLE = (1 << GPIO_SSC_DOUT) | (1 << GPIO_SSC_CLK); + + PIO_OUTPUT_ENABLE = (1 << GPIO_SSC_DOUT); + PIO_OUTPUT_DISABLE = (1 << GPIO_SSC_CLK); + +#define SHORT_COIL() LOW(GPIO_SSC_DOUT) +#define OPEN_COIL() HIGH(GPIO_SSC_DOUT) + + i = 0; + for(;;) { + while(!(PIO_PIN_DATA_STATUS & (1<0xFFF) { + DbpString("Tags can only have 44 bits."); + return; + } + fc(0,&n); + // special start of frame marker containing invalid bit sequences + fc(8, &n); fc(8, &n); // invalid + fc(8, &n); fc(10, &n); // logical 0 + fc(10, &n); fc(10, &n); // invalid + fc(8, &n); fc(10, &n); // logical 0 + + WDT_HIT(); + // manchester encode bits 43 to 32 + for (i=11; i>=0; i--) { + if ((i%4)==3) fc(0,&n); + if ((hi>>i)&1) { + fc(10, &n); fc(8, &n); // low-high transition + } else { + fc(8, &n); fc(10, &n); // high-low transition + } + } + + WDT_HIT(); + // manchester encode bits 31 to 0 + for (i=31; i>=0; i--) { + if ((i%4)==3) fc(0,&n); + if ((lo>>i)&1) { + fc(10, &n); fc(8, &n); // low-high transition + } else { + fc(8, &n); fc(10, &n); // high-low transition + } + } + + LED_A_ON(); + SimulateTagLowFrequency(n); + LED_A_OFF(); +} + +// loop to capture raw HID waveform then FSK demodulate the TAG ID from it +static void CmdHIDdemodFSK(void) +{ + BYTE *dest = (BYTE *)BigBuf; + int m=0, n=0, i=0, idx=0, found=0, lastval=0; + DWORD hi=0, lo=0; + + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_READER_USE_125_KHZ); + + // Connect the A/D to the peak-detected low-frequency path. + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + + // Give it a bit of time for the resonant antenna to settle. + SpinDelay(50); + + // Now set up the SSC to get the ADC samples that are now streaming at us. + FpgaSetupSsc(); + + for(;;) { + WDT_HIT(); + LED_A_ON(); + if(BUTTON_PRESS()) { + LED_A_OFF(); + return; + } + + i = 0; + m = sizeof(BigBuf); + memset(dest,128,m); + for(;;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = 0x43; + LED_D_ON(); + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + dest[i] = (BYTE)SSC_RECEIVE_HOLDING; + // we don't care about actual value, only if it's more or less than a + // threshold essentially we capture zero crossings for later analysis + if(dest[i] < 127) dest[i] = 0; else dest[i] = 1; + i++; + LED_D_OFF(); + if(i >= m) { + break; + } + } + } + + // FSK demodulator + + // sync to first lo-hi transition + for( idx=1; idx>1)&0xffff); + hi=0; + lo=0; + found=0; + } + } + if (found) { + if (dest[idx] && (!dest[idx+1]) ) { + hi=(hi<<1)|(lo>>31); + lo=(lo<<1)|0; + } else if ( (!dest[idx]) && dest[idx+1]) { + hi=(hi<<1)|(lo>>31); + lo=(lo<<1)|1; + } else { + found=0; + hi=0; + lo=0; + } + idx++; + } + if ( dest[idx] && dest[idx+1] && dest[idx+2] && (!dest[idx+3]) && (!dest[idx+4]) && (!dest[idx+5]) ) + { + found=1; + idx+=6; + if (found && (hi|lo)) { + DbpString("TAG ID"); + DbpIntegers(hi, lo, (lo>>1)&0xffff); + hi=0; + lo=0; + found=0; + } + } + } + WDT_HIT(); + } +} + +void SimulateTagHfListen(void) +{ + BYTE *dest = (BYTE *)BigBuf; + int n = sizeof(BigBuf); + BYTE v = 0; + int i; + int p = 0; + + // We're using this mode just so that I can test it out; the simulated + // tag mode would work just as well and be simpler. + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_SNOOP); + + // We need to listen to the high-frequency, peak-detected path. + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + FpgaSetupSsc(); + + i = 0; + for(;;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = 0xff; + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + BYTE r = (BYTE)SSC_RECEIVE_HOLDING; + + v <<= 1; + if(r & 1) { + v |= 1; + } + p++; + + if(p >= 8) { + dest[i] = v; + v = 0; + p = 0; + i++; + + if(i >= n) { + break; + } + } + } + } + DbpString("simulate tag (now type bitsamples)"); +} + +void UsbPacketReceived(BYTE *packet, int len) +{ + UsbCommand *c = (UsbCommand *)packet; + + switch(c->cmd) { + case CMD_ACQUIRE_RAW_ADC_SAMPLES_125K: + AcquireRawAdcSamples125k(c->ext1); + break; + + case CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693: + AcquireRawAdcSamplesIso15693(); + break; + + case CMD_READER_ISO_15693: + ReaderIso15693(c->ext1); + break; + + case CMD_SIMTAG_ISO_15693: + SimTagIso15693(c->ext1); + break; + + + case CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443: + AcquireRawAdcSamplesIso14443(c->ext1); + break; + + case CMD_READER_ISO_14443a: + ReaderIso14443a(c->ext1); + break; + + case CMD_SNOOP_ISO_14443: + SnoopIso14443(); + break; + + case CMD_SNOOP_ISO_14443a: + SnoopIso14443a(); + break; + + case CMD_SIMULATE_TAG_HF_LISTEN: + SimulateTagHfListen(); + break; + + case CMD_SIMULATE_TAG_ISO_14443: + SimulateIso14443Tag(); + break; + + case CMD_SIMULATE_TAG_ISO_14443a: + SimulateIso14443aTag(c->ext1, c->ext2); // ## Simulate iso14443a tag - pass tag type & UID + break; + + case CMD_MEASURE_ANTENNA_TUNING: + MeasureAntennaTuning(); + break; + + case CMD_HID_DEMOD_FSK: + CmdHIDdemodFSK(); // Demodulate HID tag + break; + + case CMD_HID_SIM_TAG: + CmdHIDsimTAG(c->ext1, c->ext2); // Simulate HID tag by ID + break; + + case CMD_FPGA_MAJOR_MODE_OFF: // ## FPGA Control + LED_C_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(200); + LED_C_OFF(); + break; + + case CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K: + case CMD_DOWNLOAD_RAW_BITS_TI_TYPE: { + UsbCommand n; + if(c->cmd == CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K) { + n.cmd = CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K; + } else { + n.cmd = CMD_DOWNLOADED_RAW_BITS_TI_TYPE; + } + n.ext1 = c->ext1; + memcpy(n.d.asDwords, BigBuf+c->ext1, 12*sizeof(DWORD)); + UsbSendPacket((BYTE *)&n, sizeof(n)); + break; + } + case CMD_DOWNLOADED_SIM_SAMPLES_125K: { + BYTE *b = (BYTE *)BigBuf; + memcpy(b+c->ext1, c->d.asBytes, 48); + break; + } + case CMD_SIMULATE_TAG_125K: + LED_A_ON(); + SimulateTagLowFrequency(c->ext1); + LED_A_OFF(); + break; + + case CMD_LCD_RESET: + LCDReset(); + break; + + case CMD_LCD: + LCDSend(c->ext1); + break; + + case CMD_SETUP_WRITE: + case CMD_FINISH_WRITE: + USB_D_PLUS_PULLUP_OFF(); + SpinDelay(1000); + SpinDelay(1000); + RSTC_CONTROL = RST_CONTROL_KEY | RST_CONTROL_PROCESSOR_RESET; + for(;;) { + // We're going to reset, and the bootrom will take control. + } + break; + + default: + DbpString("unknown command"); + break; + } +} + +void AppMain(void) +{ + memset(BigBuf,0,sizeof(BigBuf)); + SpinDelay(100); + + LED_D_OFF(); + LED_C_OFF(); + LED_B_OFF(); + LED_A_OFF(); + + UsbStart(); + + // The FPGA gets its clock from us from PCK0 output, so set that up. + PIO_PERIPHERAL_B_SEL = (1 << GPIO_PCK0); + PIO_DISABLE = (1 << GPIO_PCK0); + PMC_SYS_CLK_ENABLE = PMC_SYS_CLK_PROGRAMMABLE_CLK_0; + // PCK0 is PLL clock / 4 = 96Mhz / 4 = 24Mhz + PMC_PROGRAMMABLE_CLK_0 = PMC_CLK_SELECTION_PLL_CLOCK | + PMC_CLK_PRESCALE_DIV_4; + PIO_OUTPUT_ENABLE = (1 << GPIO_PCK0); + + // Reset SPI + SPI_CONTROL = SPI_CONTROL_RESET; + // Reset SSC + SSC_CONTROL = SSC_CONTROL_RESET; + + // Load the FPGA image, which we have stored in our flash. + FpgaDownloadAndGo(); + + LCDInit(); + + // test text on different colored backgrounds + LCDString(" The quick brown fox ", &FONT6x8,1,1+8*0,WHITE ,BLACK ); + LCDString(" jumped over the ", &FONT6x8,1,1+8*1,BLACK ,WHITE ); + LCDString(" lazy dog. ", &FONT6x8,1,1+8*2,YELLOW ,RED ); + LCDString(" AaBbCcDdEeFfGgHhIiJj ", &FONT6x8,1,1+8*3,RED ,GREEN ); + LCDString(" KkLlMmNnOoPpQqRrSsTt ", &FONT6x8,1,1+8*4,MAGENTA,BLUE ); + LCDString("UuVvWwXxYyZz0123456789", &FONT6x8,1,1+8*5,BLUE ,YELLOW); + LCDString("`-=[]_;',./~!@#$%^&*()", &FONT6x8,1,1+8*6,BLACK ,CYAN ); + LCDString(" _+{}|:\\\"<>? ",&FONT6x8,1,1+8*7,BLUE ,MAGENTA); + + // color bands + LCDFill(0, 1+8* 8, 132, 8, BLACK); + LCDFill(0, 1+8* 9, 132, 8, WHITE); + LCDFill(0, 1+8*10, 132, 8, RED); + LCDFill(0, 1+8*11, 132, 8, GREEN); + LCDFill(0, 1+8*12, 132, 8, BLUE); + LCDFill(0, 1+8*13, 132, 8, YELLOW); + LCDFill(0, 1+8*14, 132, 8, CYAN); + LCDFill(0, 1+8*15, 132, 8, MAGENTA); + + for(;;) { + UsbPoll(FALSE); + WDT_HIT(); + } +} + +void SpinDelay(int ms) +{ + int ticks = (48000*ms) >> 10; + + // Borrow a PWM unit for my real-time clock + PWM_ENABLE = PWM_CHANNEL(0); + // 48 MHz / 1024 gives 46.875 kHz + PWM_CH_MODE(0) = PWM_CH_MODE_PRESCALER(10); + PWM_CH_DUTY_CYCLE(0) = 0; + PWM_CH_PERIOD(0) = 0xffff; + + WORD start = (WORD)PWM_CH_COUNTER(0); + + for(;;) { + WORD now = (WORD)PWM_CH_COUNTER(0); + if(now == (WORD)(start + ticks)) { + return; + } + WDT_HIT(); + } +} diff --git a/armsrc/apps.h b/armsrc/apps.h new file mode 100644 index 00000000..e09c8357 --- /dev/null +++ b/armsrc/apps.h @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// Definitions internal to the app source. +// Jonathan Westhues, Aug 2005 +// Added ISO14443-A support by Gerhard de Koning Gans, April 2008 +//----------------------------------------------------------------------------- + +#ifndef __APPS_H +#define __APPS_H + +/// appmain.c +void AppMain(void); +void DbpIntegers(int a, int b, int c); +void DbpString(char *str); +void SpinDelay(int ms); +void ToSendStuffBit(int b); +void ToSendReset(void); +extern int ToSendMax; +extern BYTE ToSend[]; +extern DWORD BigBuf[]; + +/// fpga.c +void FpgaWriteConfWord(BYTE v); +void FpgaDownloadAndGo(void); +void FpgaSetupSsc(void); +void SetupSpi(int mode); +void FpgaSetupSscDma(BYTE *buf, int len); +void SetAdcMuxFor(int whichGpio); + +// Definitions for the FPGA configuration word. +#define FPGA_MAJOR_MODE_LF_READER (0<<5) +#define FPGA_MAJOR_MODE_LF_SIMULATOR (1<<5) +#define FPGA_MAJOR_MODE_HF_READER_TX (2<<5) +#define FPGA_MAJOR_MODE_HF_READER_RX_XCORR (3<<5) +#define FPGA_MAJOR_MODE_HF_SIMULATOR (4<<5) +#define FPGA_MAJOR_MODE_HF_ISO14443A (5<<5) +#define FPGA_MAJOR_MODE_UNUSED (6<<5) +#define FPGA_MAJOR_MODE_OFF (7<<5) +// Options for the LF reader +#define FPGA_LF_READER_USE_125_KHZ (1<<3) +#define FPGA_LF_READER_USE_134_KHZ (0<<3) +// Options for the HF reader, tx to tag +#define FPGA_HF_READER_TX_SHALLOW_MOD (1<<0) +// Options for the HF reader, correlating against rx from tag +#define FPGA_HF_READER_RX_XCORR_848_KHZ (1<<0) +#define FPGA_HF_READER_RX_XCORR_SNOOP (1<<1) +// Options for the HF simulated tag, how to modulate +#define FPGA_HF_SIMULATOR_NO_MODULATION (0<<0) +#define FPGA_HF_SIMULATOR_MODULATE_BPSK (1<<0) +// Options for ISO14443A +#define FPGA_HF_ISO14443A_SNIFFER (0<<0) +#define FPGA_HF_ISO14443A_TAGSIM_LISTEN (1<<0) +#define FPGA_HF_ISO14443A_TAGSIM_MOD (2<<0) +#define FPGA_HF_ISO14443A_READER_LISTEN (3<<0) +#define FPGA_HF_ISO14443A_READER_MOD (4<<0) + +/// iso14443.h +void SimulateIso14443Tag(void); +void AcquireRawAdcSamplesIso14443(DWORD parameter); +void SnoopIso14443(void); + +/// iso14443a.h +void SnoopIso14443a(void); +void SimulateIso14443aTag(int tagType, int TagUid); // ## simulate iso14443a tag +void ReaderIso14443a(DWORD parameter); + +/// iso15693.h +void AcquireRawAdcSamplesIso15693(void); +void ReaderIso15693(DWORD parameter); // Simulate an ISO15693 reader - greg +void SimTagIso15693(DWORD parameter); // simulate an ISO15693 tag - greg + +/// util.h +int strlen(char *str); +void *memcpy(void *dest, const void *src, int len); +void *memset(void *dest, int c, int len); +int memcmp(const void *av, const void *bv, int len); + +#endif diff --git a/armsrc/example_lcd.c b/armsrc/example_lcd.c new file mode 100644 index 00000000..a2267ba9 --- /dev/null +++ b/armsrc/example_lcd.c @@ -0,0 +1,269 @@ +unsigned char somestring[25]; + +//********************************************************************* +//******************** SYSTERM HEARTBEAT @ 10 ms ********************* +//********************************************************************* +void InitSPI (void) +{ + //set functionalite to pins: + //port0.11 -> NPCS0 + //port0.12 -> MISO + //port0.13 -> MOSI + //port0.14 -> SPCK + PIOA_PDR = BIT11 | BIT12 | BIT13 | BIT14; + PIOA_ASR = BIT11 | BIT12 | BIT13 | BIT14; + PIOA_BSR = 0; + + + PMC_PCER |= 1 << 5; // Enable SPI timer clock. + + /**** Fixed mode ****/ + SPI_CR = 0x81; //SPI Enable, Sowtware reset + SPI_CR = 0x01; //SPI Enable + + + + SPI_MR = 0x000E0011; //Master mode + SPI_CSR0 = 0x01010B11; //9 bit + +} + +//********************************************************************* +//*************************** Task 1 ******************************** +//********************************************************************* +void Task_1(void *p) +{ + char beat=0; // just flash the onboard LED for Heatbeat + + while(1) + { + if(beat) + { + PIOA_SODR = BIT18; + beat=0; + } + else + { + PIOA_CODR = BIT18; + beat=1; + } + + ctl_timeout_wait(ctl_get_current_time()+ 150); + + } +} +//********************************************************************* +//*************************** Task 2 ******************************** +//********************************************************************* +void Task_2(void *p) +{ + unsigned long z; + unsigned int x,y; + unsigned char a,b,c,d,e; + + char seconds,minutes,hours; + + unsigned int nowold,tenths; + + + InitLCD(); + + +/******* Put smiley face up in 4096 color mode *******/ + LCD_Fill(0,0,132,132,Black); + + LCD_Set_Resolution(HIGH_RES); // set 4096 color mode + +// ShowImage_4096(0,0,smiley); + LCD_Set_Resolution(LOW_RES); // set 256 color mode + + ctl_timeout_wait(ctl_get_current_time()+ 4000); // wait 4 seconds to view it + +/******* Do some static on screen *******/ + + LCD_Fill(0,0,132,132,Black); + + for(z=0;z<100000;z++) + { + while( (a = rand()) > 132); + while( (b = rand()) > 132); + c = rand(); + LCD_PixelPut(a,b,c); + } + +/******* Do some lines on screen *******/ + LCD_Fill(0,0,132,132,Black); + + for(z=1;z<300;z++) + { + while( (a = rand()) > 132); + while( (b = rand()) > 132); + while( (c = rand()) > 132); + while( (d = rand()) > 132); + e = rand(); // pick color + + LCD_Line(a,b,c,d,e); + ctl_timeout_wait(ctl_get_current_time()+ 10); + } + +/******* Do some Boxes on screen *******/ + LCD_Fill(0,0,132,132,Black); + + for(z=0;z<300;z++) + { + + while( (a = rand()) > 132); + while( (b = rand()) > 132); + while( (c = rand()) > 132); + while( (d = rand()) > 132); + + e = rand(); // pick color + LCD_Box(a,b,c,d,e); + + ctl_timeout_wait(ctl_get_current_time()+ 10); + } +/******* Do some Circles on screen *******/ + LCD_Fill(0,0,132,132,Black); + + for(z=0;z<100;z++) + { + + while( (a = rand()) > 132); + while( (b = rand()) > 132); + while( (c = rand()) > 127); // diameter + + d = rand(); // pick color + LCD_Circle(a,b,c,d); + + ctl_timeout_wait(ctl_get_current_time()+ 10); + } + +/******* Do some Thick Circles on screen *******/ + LCD_Fill(0,0,132,132,Black); + + for(z=0;z<25;z++) + { + while( (a = rand()) > 132); + while( (b = rand()) > 132); + while( (c = rand()) > 40); // diameter + while( (d = rand()) > 10); // wall thicknes + e = rand(); // pick color + LCD_Thick_Circle(a,b,c,d,e); + + ctl_timeout_wait(ctl_get_current_time()+ 1); + } + +/******* Do something funky to wipe screen *******/ + b=0; + + for(a=0;a<131;a++) + { + LCD_Line(a,b,65,65,0x62); + } + for(b=0;b<131;b++) + { + LCD_Line(a,b,65,65,0x62); + } + for(;a>1;a--) + { + LCD_Line(a,b,65,65,0x62); + } + for(;b>1;b--) + { + LCD_Line(a,b,65,65,0x62); + } + + ctl_timeout_wait(ctl_get_current_time()+ 1000); + +/******* Show Image scrolling *******/ + LCD_Fill(0,0,132,132,Black); + + ShowImage(0,50,sparkfun); + + sprintf(somestring,"Thanks SparkFun"); + LCD_String(somestring,&FONT8x8F[0][0],5,10,LightGreen,Black); + + ctl_timeout_wait(ctl_get_current_time()+ 2000); // hold sparkfun image for a bit + + for(y=50;y<140;y++) + { + LCD_Line(0,y-1,132,y-1,Black); // wipe the white line as it moves down + ShowImage(0,y,sparkfun); // move image to Y location + ctl_timeout_wait(ctl_get_current_time()+ 25); // wait a bit + } + +/******* Run radar in loop with example fonts displayed *******/ + LCD_Fill(0,0,132,132,Black); + + LCD_Thick_Circle(66,66,30,2,DarkBlue); + + y=0; + + while (1) + { + LCD_Circle_Line(66,66,28,0,y,LightGreen); + + ctl_timeout_wait(ctl_get_current_time()+ 1); + + tenths = ctl_current_time / 1000; + + if(tenths != nowold) + { + nowold = tenths; + + if(++seconds == 60) + { + seconds = 0; + + if(++minutes == 60) + { + minutes=0; + hours++; + } + } + } + + + printf("a=%6lu - b=%6lu - c=%6lu - d=%6lu : Time=%lu\r\n",a,b,c,d,ctl_current_time); + + sprintf(somestring,"%05lu",y); + LCD_String(somestring,&FONT6x8[0][0],52,25,White,Black); + + sprintf(somestring,"Time:%02u:%02u:%02u",hours,minutes,seconds); + LCD_String(somestring,&FONT8x8F[0][0],14,10,DarkRed,Black); + + sprintf(somestring,"Time:%02u:%02u:%02u",hours,minutes,seconds); + LCD_String(somestring,&FONT8x16[0][0],14,115,LightGreen,Black); + + LCD_Circle_Line(66,66,28,0,y,Black); + + if(++y==360) + { + y=0; + } + + ctl_timeout_wait(ctl_get_current_time()+ 10); + + } +} + +/************************************************************************* + ********************* Main Module ************************* + ********************* ************************* + ********************* Initialize Program ************************* + ********************* Sequences ************************* + ********************* ************************* + *************************************************************************/ +int main(void) +{ + BoardInit(); + + InitSPI(); + + while (1) + { + Idle(); + } + + return 0; +} diff --git a/armsrc/fonts.c b/armsrc/fonts.c new file mode 100644 index 00000000..81e99ce5 --- /dev/null +++ b/armsrc/fonts.c @@ -0,0 +1,300 @@ +const char FONT6x8[97][8] = { + {0x06,0x08,0x08,0x00,0x00,0x00,0x00,0x00}, // columns, rows, bytes per char + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // space + {0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00}, // ! + {0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00}, // " + {0x50,0x50,0xF8,0x50,0xF8,0x50,0x50,0x00}, // # + {0x20,0x78,0xA0,0x70,0x28,0xF0,0x20,0x00}, // $ + {0xC0,0xC8,0x10,0x20,0x40,0x98,0x18,0x00}, // % + {0x40,0xA0,0xA0,0x40,0xA8,0x90,0x68,0x00}, // & + {0x30,0x30,0x20,0x40,0x00,0x00,0x00,0x00}, // ' + {0x10,0x20,0x40,0x40,0x40,0x20,0x10,0x00}, // ( + {0x40,0x20,0x10,0x10,0x10,0x20,0x40,0x00}, // ) + {0x00,0x20,0xA8,0x70,0x70,0xA8,0x20,0x00}, // * + {0x00,0x20,0x20,0xF8,0x20,0x20,0x00,0x00}, // + + {0x00,0x00,0x00,0x00,0x30,0x30,0x20,0x40}, // , + {0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00}, // - + {0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00}, // . + {0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00}, // / + {0x70,0x88,0x88,0xA8,0x88,0x88,0x70,0x00}, // 0 + {0x20,0x60,0x20,0x20,0x20,0x20,0x70,0x00}, // 1 + {0x70,0x88,0x08,0x70,0x80,0x80,0xF8,0x00}, // 2 + {0xF8,0x08,0x10,0x30,0x08,0x88,0x70,0x00}, // 3 + {0x10,0x30,0x50,0x90,0xF8,0x10,0x10,0x00}, // 4 + {0xF8,0x80,0xF0,0x08,0x08,0x88,0x70,0x00}, // 5 + {0x38,0x40,0x80,0xF0,0x88,0x88,0x70,0x00}, // 6 + {0xF8,0x08,0x08,0x10,0x20,0x40,0x80,0x00}, // 7 + {0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00}, // 8 + {0x70,0x88,0x88,0x78,0x08,0x10,0xE0,0x00}, // 9 + {0x00,0x00,0x20,0x00,0x20,0x00,0x00,0x00}, // : + {0x00,0x00,0x20,0x00,0x20,0x20,0x40,0x00}, // ; + {0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x00}, // < + {0x00,0x00,0xF8,0x00,0xF8,0x00,0x00,0x00}, // = + {0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00}, // > + {0x70,0x88,0x08,0x30,0x20,0x00,0x20,0x00}, // ? + {0x70,0x88,0xA8,0xB8,0xB0,0x80,0x78,0x00}, // @ + {0x20,0x50,0x88,0x88,0xF8,0x88,0x88,0x00}, // A + {0xF0,0x88,0x88,0xF0,0x88,0x88,0xF0,0x00}, // B + {0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00}, // C + {0xF0,0x88,0x88,0x88,0x88,0x88,0xF0,0x00}, // D + {0xF8,0x80,0x80,0xF0,0x80,0x80,0xF8,0x00}, // E + {0xF8,0x80,0x80,0xF0,0x80,0x80,0x80,0x00}, // F + {0x78,0x88,0x80,0x80,0x98,0x88,0x78,0x00}, // G + {0x88,0x88,0x88,0xF8,0x88,0x88,0x88,0x00}, // H + {0x70,0x20,0x20,0x20,0x20,0x20,0x70,0x00}, // I + {0x38,0x10,0x10,0x10,0x10,0x90,0x60,0x00}, // J + {0x88,0x90,0xA0,0xC0,0xA0,0x90,0x88,0x00}, // K + {0x80,0x80,0x80,0x80,0x80,0x80,0xF8,0x00}, // L + {0x88,0xD8,0xA8,0xA8,0xA8,0x88,0x88,0x00}, // M + {0x88,0x88,0xC8,0xA8,0x98,0x88,0x88,0x00}, // N + {0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00}, // O + {0xF0,0x88,0x88,0xF0,0x80,0x80,0x80,0x00}, // P + {0x70,0x88,0x88,0x88,0xA8,0x90,0x68,0x00}, // Q + {0xF0,0x88,0x88,0xF0,0xA0,0x90,0x88,0x00}, // R + {0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00}, // S + {0xF8,0xA8,0x20,0x20,0x20,0x20,0x20,0x00}, // T + {0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00}, // U + {0x88,0x88,0x88,0x88,0x88,0x50,0x20,0x00}, // V + {0x88,0x88,0x88,0xA8,0xA8,0xA8,0x50,0x00}, // W + {0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00}, // X + {0x88,0x88,0x50,0x20,0x20,0x20,0x20,0x00}, // Y + {0xF8,0x08,0x10,0x70,0x40,0x80,0xF8,0x00}, // Z + {0x78,0x40,0x40,0x40,0x40,0x40,0x78,0x00}, // [ + {0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00}, // backslash + {0x78,0x08,0x08,0x08,0x08,0x08,0x78,0x00}, // ] + {0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00}, // ^ + {0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x00}, // _ + {0x60,0x60,0x20,0x10,0x00,0x00,0x00,0x00}, // ` + {0x00,0x00,0x60,0x10,0x70,0x90,0x78,0x00}, // a + {0x80,0x80,0xB0,0xC8,0x88,0xC8,0xB0,0x00}, // b + {0x00,0x00,0x70,0x88,0x80,0x88,0x70,0x00}, // c + {0x08,0x08,0x68,0x98,0x88,0x98,0x68,0x00}, // d + {0x00,0x00,0x70,0x88,0xF8,0x80,0x70,0x00}, // e + {0x10,0x28,0x20,0x70,0x20,0x20,0x20,0x00}, // f + {0x00,0x00,0x70,0x98,0x98,0x68,0x08,0x70}, // g + {0x80,0x80,0xB0,0xC8,0x88,0x88,0x88,0x00}, // h + {0x20,0x00,0x60,0x20,0x20,0x20,0x70,0x00}, // i + {0x10,0x00,0x10,0x10,0x10,0x90,0x60,0x00}, // j + {0x80,0x80,0x90,0xA0,0xC0,0xA0,0x90,0x00}, // k + {0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00}, // l + {0x00,0x00,0xD0,0xA8,0xA8,0xA8,0xA8,0x00}, // m + {0x00,0x00,0xB0,0xC8,0x88,0x88,0x88,0x00}, // n + {0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00}, // o + {0x00,0x00,0xB0,0xC8,0xC8,0xB0,0x80,0x80}, // p + {0x00,0x00,0x68,0x98,0x98,0x68,0x08,0x08}, // q + {0x00,0x00,0xB0,0xC8,0x80,0x80,0x80,0x00}, // r + {0x00,0x00,0x78,0x80,0x70,0x08,0xF0,0x00}, // s + {0x20,0x20,0xF8,0x20,0x20,0x28,0x10,0x00}, // t + {0x00,0x00,0x88,0x88,0x88,0x98,0x68,0x00}, // u + {0x00,0x00,0x88,0x88,0x88,0x50,0x20,0x00}, // v + {0x00,0x00,0x88,0x88,0xA8,0xA8,0x50,0x00}, // w + {0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00}, // x + {0x00,0x00,0x88,0x88,0x78,0x08,0x88,0x70}, // y + {0x00,0x00,0xF8,0x10,0x20,0x40,0xF8,0x00}, // z + {0x10,0x20,0x20,0x40,0x20,0x20,0x10,0x00}, // { + {0x20,0x20,0x20,0x00,0x20,0x20,0x20,0x00}, // | + {0x40,0x20,0x20,0x10,0x20,0x20,0x40,0x00}, // } + {0x40,0xA8,0x10,0x00,0x00,0x00,0x00,0x00}, // ~ + {0x70,0xD8,0xD8,0x70,0x00,0x00,0x00,0x00} // DEL +}; +/* +const char FONT8x8F[97][8] = { + {0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00}, // columns, rows, bytes per char + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // space + {0x30,0x78,0x78,0x30,0x30,0x00,0x30,0x00}, // ! + {0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00}, // " + {0x6C,0x6C,0xFE,0x6C,0xFE,0x6C,0x6C,0x00}, // # + {0x18,0x3E,0x60,0x3C,0x06,0x7C,0x18,0x00}, // $ + {0x00,0x63,0x66,0x0C,0x18,0x33,0x63,0x00}, // % + {0x1C,0x36,0x1C,0x3B,0x6E,0x66,0x3B,0x00}, // & + {0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00}, // ' + {0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00}, // ( + {0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00}, // ) + {0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00}, // * + {0x00,0x30,0x30,0xFC,0x30,0x30,0x00,0x00}, // + + {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30}, // , + {0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00}, // - + {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00}, // . + {0x03,0x06,0x0C,0x18,0x30,0x60,0x40,0x00}, // / + {0x3E,0x63,0x63,0x6B,0x63,0x63,0x3E,0x00}, // 0 + {0x18,0x38,0x58,0x18,0x18,0x18,0x7E,0x00}, // 1 + {0x3C,0x66,0x06,0x1C,0x30,0x66,0x7E,0x00}, // 2 + {0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00}, // 3 + {0x0E,0x1E,0x36,0x66,0x7F,0x06,0x0F,0x00}, // 4 + {0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00}, // 5 + {0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00}, // 6 + {0x7E,0x66,0x06,0x0C,0x18,0x18,0x18,0x00}, // 7 + {0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00}, // 8 + {0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00}, // 9 + {0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00}, // : + {0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x30}, // ; + {0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x00}, // < + {0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00}, // = + {0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x00}, // > + {0x3C,0x66,0x06,0x0C,0x18,0x00,0x18,0x00}, // ? + {0x3E,0x63,0x6F,0x69,0x6F,0x60,0x3E,0x00}, // @ + {0x18,0x3C,0x66,0x66,0x7E,0x66,0x66,0x00}, // A + {0x7E,0x33,0x33,0x3E,0x33,0x33,0x7E,0x00}, // B + {0x1E,0x33,0x60,0x60,0x60,0x33,0x1E,0x00}, // C + {0x7C,0x36,0x33,0x33,0x33,0x36,0x7C,0x00}, // D + {0x7F,0x31,0x34,0x3C,0x34,0x31,0x7F,0x00}, // E + {0x7F,0x31,0x34,0x3C,0x34,0x30,0x78,0x00}, // F + {0x1E,0x33,0x60,0x60,0x67,0x33,0x1F,0x00}, // G + {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, // H + {0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00}, // I + {0x0F,0x06,0x06,0x06,0x66,0x66,0x3C,0x00}, // J + {0x73,0x33,0x36,0x3C,0x36,0x33,0x73,0x00}, // K + {0x78,0x30,0x30,0x30,0x31,0x33,0x7F,0x00}, // L + {0x63,0x77,0x7F,0x7F,0x6B,0x63,0x63,0x00}, // M + {0x63,0x73,0x7B,0x6F,0x67,0x63,0x63,0x00}, // N + {0x3E,0x63,0x63,0x63,0x63,0x63,0x3E,0x00}, // O + {0x7E,0x33,0x33,0x3E,0x30,0x30,0x78,0x00}, // P + {0x3C,0x66,0x66,0x66,0x6E,0x3C,0x0E,0x00}, // Q + {0x7E,0x33,0x33,0x3E,0x36,0x33,0x73,0x00}, // R + {0x3C,0x66,0x30,0x18,0x0C,0x66,0x3C,0x00}, // S + {0x7E,0x5A,0x18,0x18,0x18,0x18,0x3C,0x00}, // T + {0x66,0x66,0x66,0x66,0x66,0x66,0x7E,0x00}, // U + {0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00}, // V + {0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00}, // W + {0x63,0x63,0x36,0x1C,0x1C,0x36,0x63,0x00}, // X + {0x66,0x66,0x66,0x3C,0x18,0x18,0x3C,0x00}, // Y + {0x7F,0x63,0x46,0x0C,0x19,0x33,0x7F,0x00}, // Z + {0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00}, // [ + {0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x00}, // backslash + {0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00}, // ] + {0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00}, // ^ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF}, // _ + {0x18,0x18,0x0C,0x00,0x00,0x00,0x00,0x00}, // ` + {0x00,0x00,0x3C,0x06,0x3E,0x66,0x3B,0x00}, // a + {0x70,0x30,0x3E,0x33,0x33,0x33,0x6E,0x00}, // b + {0x00,0x00,0x3C,0x66,0x60,0x66,0x3C,0x00}, // c + {0x0E,0x06,0x3E,0x66,0x66,0x66,0x3B,0x00}, // d + {0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00}, // e + {0x1C,0x36,0x30,0x78,0x30,0x30,0x78,0x00}, // f + {0x00,0x00,0x3B,0x66,0x66,0x3E,0x06,0x7C}, // g + {0x70,0x30,0x36,0x3B,0x33,0x33,0x73,0x00}, // h + {0x18,0x00,0x38,0x18,0x18,0x18,0x3C,0x00}, // i + {0x06,0x00,0x06,0x06,0x06,0x66,0x66,0x3C}, // j + {0x70,0x30,0x33,0x36,0x3C,0x36,0x73,0x00}, // k + {0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00}, // l + {0x00,0x00,0x66,0x7F,0x7F,0x6B,0x63,0x00}, // m + {0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x00}, // n + {0x00,0x00,0x3C,0x66,0x66,0x66,0x3C,0x00}, // o + {0x00,0x00,0x6E,0x33,0x33,0x3E,0x30,0x78}, // p + {0x00,0x00,0x3B,0x66,0x66,0x3E,0x06,0x0F}, // q + {0x00,0x00,0x6E,0x3B,0x33,0x30,0x78,0x00}, // r + {0x00,0x00,0x3E,0x60,0x3C,0x06,0x7C,0x00}, // s + {0x08,0x18,0x3E,0x18,0x18,0x1A,0x0C,0x00}, // t + {0x00,0x00,0x66,0x66,0x66,0x66,0x3B,0x00}, // u + {0x00,0x00,0x66,0x66,0x66,0x3C,0x18,0x00}, // v + {0x00,0x00,0x63,0x6B,0x7F,0x7F,0x36,0x00}, // w + {0x00,0x00,0x63,0x36,0x1C,0x36,0x63,0x00}, // x + {0x00,0x00,0x66,0x66,0x66,0x3E,0x06,0x7C}, // y + {0x00,0x00,0x7E,0x4C,0x18,0x32,0x7E,0x00}, // z + {0x0E,0x18,0x18,0x70,0x18,0x18,0x0E,0x00}, // { + {0x0C,0x0C,0x0C,0x00,0x0C,0x0C,0x0C,0x00}, // | + {0x70,0x18,0x18,0x0E,0x18,0x18,0x70,0x00}, // } + {0x3B,0x6E,0x00,0x00,0x00,0x00,0x00,0x00}, // ~ + {0x1C,0x36,0x36,0x1C,0x00,0x00,0x00,0x00} // DEL +}; + +const char FONT8x16[97][16] = { + {0x08,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // columns, rows, bytes per char + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // space + {0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00}, // ! + {0x00,0x63,0x63,0x63,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // " + {0x00,0x00,0x00,0x36,0x36,0x7F,0x36,0x36,0x36,0x7F,0x36,0x36,0x00,0x00,0x00,0x00}, // # + {0x0C,0x0C,0x3E,0x63,0x61,0x60,0x3E,0x03,0x03,0x43,0x63,0x3E,0x0C,0x0C,0x00,0x00}, // $ + {0x00,0x00,0x00,0x00,0x00,0x61,0x63,0x06,0x0C,0x18,0x33,0x63,0x00,0x00,0x00,0x00}, // % + {0x00,0x00,0x00,0x1C,0x36,0x36,0x1C,0x3B,0x6E,0x66,0x66,0x3B,0x00,0x00,0x00,0x00}, // & + {0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // ' + {0x00,0x00,0x0C,0x18,0x18,0x30,0x30,0x30,0x30,0x18,0x18,0x0C,0x00,0x00,0x00,0x00}, // ( + {0x00,0x00,0x18,0x0C,0x0C,0x06,0x06,0x06,0x06,0x0C,0x0C,0x18,0x00,0x00,0x00,0x00}, // ) + {0x00,0x00,0x00,0x00,0x42,0x66,0x3C,0xFF,0x3C,0x66,0x42,0x00,0x00,0x00,0x00,0x00}, // * + {0x00,0x00,0x00,0x00,0x18,0x18,0x18,0xFF,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00}, // + + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00}, // , + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // - + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00}, // . + {0x00,0x00,0x01,0x03,0x07,0x0E,0x1C,0x38,0x70,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00}, // / + {0x00,0x00,0x3E,0x63,0x63,0x63,0x6B,0x6B,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00}, // 0 + {0x00,0x00,0x0C,0x1C,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3F,0x00,0x00,0x00,0x00}, // 1 + {0x00,0x00,0x3E,0x63,0x03,0x06,0x0C,0x18,0x30,0x61,0x63,0x7F,0x00,0x00,0x00,0x00}, // 2 + {0x00,0x00,0x3E,0x63,0x03,0x03,0x1E,0x03,0x03,0x03,0x63,0x3E,0x00,0x00,0x00,0x00}, // 3 + {0x00,0x00,0x06,0x0E,0x1E,0x36,0x66,0x66,0x7F,0x06,0x06,0x0F,0x00,0x00,0x00,0x00}, // 4 + {0x00,0x00,0x7F,0x60,0x60,0x60,0x7E,0x03,0x03,0x63,0x73,0x3E,0x00,0x00,0x00,0x00}, // 5 + {0x00,0x00,0x1C,0x30,0x60,0x60,0x7E,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00}, // 6 + {0x00,0x00,0x7F,0x63,0x03,0x06,0x06,0x0C,0x0C,0x18,0x18,0x18,0x00,0x00,0x00,0x00}, // 7 + {0x00,0x00,0x3E,0x63,0x63,0x63,0x3E,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00}, // 8 + {0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x3F,0x03,0x03,0x06,0x3C,0x00,0x00,0x00,0x00}, // 9 + {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00}, // : + {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00}, // ; + {0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00}, // < + {0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00}, // = + {0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00}, // > + {0x00,0x00,0x3E,0x63,0x63,0x06,0x0C,0x0C,0x0C,0x00,0x0C,0x0C,0x00,0x00,0x00,0x00}, // ? + {0x00,0x00,0x3E,0x63,0x63,0x6F,0x6B,0x6B,0x6E,0x60,0x60,0x3E,0x00,0x00,0x00,0x00}, // @ + {0x00,0x00,0x08,0x1C,0x36,0x63,0x63,0x63,0x7F,0x63,0x63,0x63,0x00,0x00,0x00,0x00}, // A + {0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x33,0x33,0x33,0x33,0x7E,0x00,0x00,0x00,0x00}, // B + {0x00,0x00,0x1E,0x33,0x61,0x60,0x60,0x60,0x60,0x61,0x33,0x1E,0x00,0x00,0x00,0x00}, // C + {0x00,0x00,0x7C,0x36,0x33,0x33,0x33,0x33,0x33,0x33,0x36,0x7C,0x00,0x00,0x00,0x00}, // D + {0x00,0x00,0x7F,0x33,0x31,0x34,0x3C,0x34,0x30,0x31,0x33,0x7F,0x00,0x00,0x00,0x00}, // E + {0x00,0x00,0x7F,0x33,0x31,0x34,0x3C,0x34,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00}, // F + {0x00,0x00,0x1E,0x33,0x61,0x60,0x60,0x6F,0x63,0x63,0x37,0x1D,0x00,0x00,0x00,0x00}, // G + {0x00,0x00,0x63,0x63,0x63,0x63,0x7F,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00}, // H + {0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}, // I + {0x00,0x00,0x0F,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00,0x00,0x00,0x00}, // J + {0x00,0x00,0x73,0x33,0x36,0x36,0x3C,0x36,0x36,0x33,0x33,0x73,0x00,0x00,0x00,0x00}, // K + {0x00,0x00,0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x31,0x33,0x7F,0x00,0x00,0x00,0x00}, // L + {0x00,0x00,0x63,0x77,0x7F,0x6B,0x63,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00}, // M + {0x00,0x00,0x63,0x63,0x73,0x7B,0x7F,0x6F,0x67,0x63,0x63,0x63,0x00,0x00,0x00,0x00}, // N + {0x00,0x00,0x1C,0x36,0x63,0x63,0x63,0x63,0x63,0x63,0x36,0x1C,0x00,0x00,0x00,0x00}, // O + {0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00}, // P + {0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x63,0x63,0x6B,0x6F,0x3E,0x06,0x07,0x00,0x00}, // Q + {0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x36,0x36,0x33,0x33,0x73,0x00,0x00,0x00,0x00}, // R + {0x00,0x00,0x3E,0x63,0x63,0x30,0x1C,0x06,0x03,0x63,0x63,0x3E,0x00,0x00,0x00,0x00}, // S + {0x00,0x00,0xFF,0xDB,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}, // T + {0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00}, // U + {0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x36,0x1C,0x08,0x00,0x00,0x00,0x00}, // V + {0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x36,0x36,0x00,0x00,0x00,0x00}, // W + {0x00,0x00,0xC3,0xC3,0x66,0x3C,0x18,0x18,0x3C,0x66,0xC3,0xC3,0x00,0x00,0x00,0x00}, // X + {0x00,0x00,0xC3,0xC3,0xC3,0x66,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}, // Y + {0x00,0x00,0x7F,0x63,0x43,0x06,0x0C,0x18,0x30,0x61,0x63,0x7F,0x00,0x00,0x00,0x00}, // Z + {0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00}, // [ + {0x00,0x00,0x80,0xC0,0xE0,0x70,0x38,0x1C,0x0E,0x07,0x03,0x01,0x00,0x00,0x00,0x00}, // backslash + {0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00}, // ] + {0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // ^ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00}, // _ + {0x18,0x18,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // ` + {0x00,0x00,0x00,0x00,0x00,0x3C,0x46,0x06,0x3E,0x66,0x66,0x3B,0x00,0x00,0x00,0x00}, // a + {0x00,0x00,0x70,0x30,0x30,0x3C,0x36,0x33,0x33,0x33,0x33,0x6E,0x00,0x00,0x00,0x00}, // b + {0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x60,0x60,0x60,0x63,0x3E,0x00,0x00,0x00,0x00}, // c + {0x00,0x00,0x0E,0x06,0x06,0x1E,0x36,0x66,0x66,0x66,0x66,0x3B,0x00,0x00,0x00,0x00}, // d + {0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x63,0x7E,0x60,0x63,0x3E,0x00,0x00,0x00,0x00}, // e + {0x00,0x00,0x1C,0x36,0x32,0x30,0x7C,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00}, // f + {0x00,0x00,0x00,0x00,0x00,0x3B,0x66,0x66,0x66,0x66,0x3E,0x06,0x66,0x3C,0x00,0x00}, // g + {0x00,0x00,0x70,0x30,0x30,0x36,0x3B,0x33,0x33,0x33,0x33,0x73,0x00,0x00,0x00,0x00}, // h + {0x00,0x00,0x0C,0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00}, // i + {0x00,0x00,0x06,0x06,0x00,0x0E,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00,0x00}, // j + {0x00,0x00,0x70,0x30,0x30,0x33,0x33,0x36,0x3C,0x36,0x33,0x73,0x00,0x00,0x00,0x00}, // k + {0x00,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00}, // l + {0x00,0x00,0x00,0x00,0x00,0x6E,0x7F,0x6B,0x6B,0x6B,0x6B,0x6B,0x00,0x00,0x00,0x00}, // m + {0x00,0x00,0x00,0x00,0x00,0x6E,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x00}, // n + {0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00}, // o + {0x00,0x00,0x00,0x00,0x00,0x6E,0x33,0x33,0x33,0x33,0x3E,0x30,0x30,0x78,0x00,0x00}, // p + {0x00,0x00,0x00,0x00,0x00,0x3B,0x66,0x66,0x66,0x66,0x3E,0x06,0x06,0x0F,0x00,0x00}, // q + {0x00,0x00,0x00,0x00,0x00,0x6E,0x3B,0x33,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00}, // r + {0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x38,0x0E,0x03,0x63,0x3E,0x00,0x00,0x00,0x00}, // s + {0x00,0x00,0x08,0x18,0x18,0x7E,0x18,0x18,0x18,0x18,0x1B,0x0E,0x00,0x00,0x00,0x00}, // t + {0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x3B,0x00,0x00,0x00,0x00}, // u + {0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x36,0x36,0x1C,0x1C,0x08,0x00,0x00,0x00,0x00}, // v + {0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x36,0x00,0x00,0x00,0x00}, // w + {0x00,0x00,0x00,0x00,0x00,0x63,0x36,0x1C,0x1C,0x1C,0x36,0x63,0x00,0x00,0x00,0x00}, // x + {0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x3F,0x03,0x06,0x3C,0x00,0x00}, // y + {0x00,0x00,0x00,0x00,0x00,0x7F,0x66,0x0C,0x18,0x30,0x63,0x7F,0x00,0x00,0x00,0x00}, // z + {0x00,0x00,0x0E,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00}, // { + {0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00}, // | + {0x00,0x00,0x70,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00}, // } + {0x00,0x00,0x3B,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // ~ + {0x00,0x70,0xD8,0xD8,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} // DEL +}; +*/ diff --git a/armsrc/fonts.h b/armsrc/fonts.h new file mode 100644 index 00000000..621a7f56 --- /dev/null +++ b/armsrc/fonts.h @@ -0,0 +1,6 @@ +#ifndef __FONTS_H +#define __FONTS_H +extern const char FONT6x8[97][8]; +extern const char FONT8x8F[97][8]; +extern const char FONT8x16[97][16]; +#endif diff --git a/armsrc/fpga.c b/armsrc/fpga.c new file mode 100644 index 00000000..2bcade28 --- /dev/null +++ b/armsrc/fpga.c @@ -0,0 +1,222 @@ +//----------------------------------------------------------------------------- +// Routines to load the FPGA image, and then to configure the FPGA's major +// mode once it is configured. +// +// Jonathan Westhues, April 2006 +//----------------------------------------------------------------------------- +#include +#include "apps.h" + +//----------------------------------------------------------------------------- +// Set up the Serial Peripheral Interface as master +// Used to write the FPGA config word +// May also be used to write to other SPI attached devices like an LCD +//----------------------------------------------------------------------------- +void SetupSpi(int mode) +{ + // PA10 -> SPI_NCS2 chip select (LCD) + // PA11 -> SPI_NCS0 chip select (FPGA) + // PA12 -> SPI_MISO Master-In Slave-Out + // PA13 -> SPI_MOSI Master-Out Slave-In + // PA14 -> SPI_SPCK Serial Clock + + // Disable PIO control of the following pins, allows use by the SPI peripheral + PIO_DISABLE = (1 << GPIO_NCS0) | + (1 << GPIO_NCS2) | + (1 << GPIO_MISO) | + (1 << GPIO_MOSI) | + (1 << GPIO_SPCK); + + PIO_PERIPHERAL_A_SEL = (1 << GPIO_NCS0) | + (1 << GPIO_MISO) | + (1 << GPIO_MOSI) | + (1 << GPIO_SPCK); + + PIO_PERIPHERAL_B_SEL = (1 << GPIO_NCS2); + + //enable the SPI Peripheral clock + PMC_PERIPHERAL_CLK_ENABLE = (1< +#include "apps.h" +#include "..\common\iso14443_crc.c" + + +//static void GetSamplesFor14443(BOOL weTx, int n); + +#define DMA_BUFFER_SIZE 256 + +//============================================================================= +// An ISO 14443 Type B tag. We listen for commands from the reader, using +// a UART kind of thing that's implemented in software. When we get a +// frame (i.e., a group of bytes between SOF and EOF), we check the CRC. +// If it's good, then we can do something appropriate with it, and send +// a response. +//============================================================================= + +//----------------------------------------------------------------------------- +// Code up a string of octets at layer 2 (including CRC, we don't generate +// that here) so that they can be transmitted to the reader. Doesn't transmit +// them yet, just leaves them ready to send in ToSend[]. +//----------------------------------------------------------------------------- +static void CodeIso14443bAsTag(const BYTE *cmd, int len) +{ + int i; + + ToSendReset(); + + // Transmit a burst of ones, as the initial thing that lets the + // reader get phase sync. This (TR1) must be > 80/fs, per spec, + // but tag that I've tried (a Paypass) exceeds that by a fair bit, + // so I will too. + for(i = 0; i < 20; i++) { + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + } + + // Send SOF. + for(i = 0; i < 10; i++) { + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + } + for(i = 0; i < 2; i++) { + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + } + + for(i = 0; i < len; i++) { + int j; + BYTE b = cmd[i]; + + // Start bit + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + + // Data bits + for(j = 0; j < 8; j++) { + if(b & 1) { + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + } else { + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + } + b >>= 1; + } + + // Stop bit + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + } + + // Send SOF. + for(i = 0; i < 10; i++) { + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + } + for(i = 0; i < 10; i++) { + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + } + + // Convert from last byte pos to length + ToSendMax++; + + // Add a few more for slop + ToSendMax += 2; +} + +//----------------------------------------------------------------------------- +// The software UART that receives commands from the reader, and its state +// variables. +//----------------------------------------------------------------------------- +static struct { + enum { + STATE_UNSYNCD, + STATE_GOT_FALLING_EDGE_OF_SOF, + STATE_AWAITING_START_BIT, + STATE_RECEIVING_DATA, + STATE_ERROR_WAIT + } state; + WORD shiftReg; + int bitCnt; + int byteCnt; + int byteCntMax; + int posCnt; + BYTE *output; +} Uart; + +static BOOL Handle14443UartBit(int bit) +{ + switch(Uart.state) { + case STATE_UNSYNCD: + if(!bit) { + // we went low, so this could be the beginning + // of an SOF + Uart.state = STATE_GOT_FALLING_EDGE_OF_SOF; + Uart.posCnt = 0; + Uart.bitCnt = 0; + } + break; + + case STATE_GOT_FALLING_EDGE_OF_SOF: + Uart.posCnt++; + if(Uart.posCnt == 2) { + if(bit) { + if(Uart.bitCnt >= 10) { + // we've seen enough consecutive + // zeros that it's a valid SOF + Uart.posCnt = 0; + Uart.byteCnt = 0; + Uart.state = STATE_AWAITING_START_BIT; + } else { + // didn't stay down long enough + // before going high, error + Uart.state = STATE_ERROR_WAIT; + } + } else { + // do nothing, keep waiting + } + Uart.bitCnt++; + } + if(Uart.posCnt >= 4) Uart.posCnt = 0; + if(Uart.bitCnt > 14) { + // Give up if we see too many zeros without + // a one, too. + Uart.state = STATE_ERROR_WAIT; + } + break; + + case STATE_AWAITING_START_BIT: + Uart.posCnt++; + if(bit) { + if(Uart.posCnt > 25) { + // stayed high for too long between + // characters, error + Uart.state = STATE_ERROR_WAIT; + } + } else { + // falling edge, this starts the data byte + Uart.posCnt = 0; + Uart.bitCnt = 0; + Uart.shiftReg = 0; + Uart.state = STATE_RECEIVING_DATA; + } + break; + + case STATE_RECEIVING_DATA: + Uart.posCnt++; + if(Uart.posCnt == 2) { + // time to sample a bit + Uart.shiftReg >>= 1; + if(bit) { + Uart.shiftReg |= 0x200; + } + Uart.bitCnt++; + } + if(Uart.posCnt >= 4) { + Uart.posCnt = 0; + } + if(Uart.bitCnt == 10) { + if((Uart.shiftReg & 0x200) && !(Uart.shiftReg & 0x001)) + { + // this is a data byte, with correct + // start and stop bits + Uart.output[Uart.byteCnt] = (Uart.shiftReg >> 1) & 0xff; + Uart.byteCnt++; + + if(Uart.byteCnt >= Uart.byteCntMax) { + // Buffer overflowed, give up + Uart.posCnt = 0; + Uart.state = STATE_ERROR_WAIT; + } else { + // so get the next byte now + Uart.posCnt = 0; + Uart.state = STATE_AWAITING_START_BIT; + } + } else if(Uart.shiftReg == 0x000) { + // this is an EOF byte + return TRUE; + } else { + // this is an error + Uart.posCnt = 0; + Uart.state = STATE_ERROR_WAIT; + } + } + break; + + case STATE_ERROR_WAIT: + // We're all screwed up, so wait a little while + // for whatever went wrong to finish, and then + // start over. + Uart.posCnt++; + if(Uart.posCnt > 10) { + Uart.state = STATE_UNSYNCD; + } + break; + + default: + Uart.state = STATE_UNSYNCD; + break; + } + + return FALSE; +} + +//----------------------------------------------------------------------------- +// Receive a command (from the reader to us, where we are the simulated tag), +// and store it in the given buffer, up to the given maximum length. Keeps +// spinning, waiting for a well-framed command, until either we get one +// (returns TRUE) or someone presses the pushbutton on the board (FALSE). +// +// Assume that we're called with the SSC (to the FPGA) and ADC path set +// correctly. +//----------------------------------------------------------------------------- +static BOOL GetIso14443CommandFromReader(BYTE *received, int *len, int maxLen) +{ + BYTE mask; + int i, bit; + + // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen + // only, since we are receiving, not transmitting). + FpgaWriteConfWord( + FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + + + // Now run a `software UART' on the stream of incoming samples. + Uart.output = received; + Uart.byteCntMax = maxLen; + Uart.state = STATE_UNSYNCD; + + for(;;) { + WDT_HIT(); + + if(BUTTON_PRESS()) return FALSE; + + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = 0x00; + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + BYTE b = (BYTE)SSC_RECEIVE_HOLDING; + + mask = 0x80; + for(i = 0; i < 8; i++, mask >>= 1) { + bit = (b & mask); + if(Handle14443UartBit(bit)) { + *len = Uart.byteCnt; + return TRUE; + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Main loop of simulated tag: receive commands from reader, decide what +// response to send, and send it. +//----------------------------------------------------------------------------- +void SimulateIso14443Tag(void) +{ + static const BYTE cmd1[] = { 0x05, 0x00, 0x08, 0x39, 0x73 }; + static const BYTE response1[] = { + 0x50, 0x82, 0x0d, 0xe1, 0x74, 0x20, 0x38, 0x19, 0x22, + 0x00, 0x21, 0x85, 0x5e, 0xd7 + }; + + BYTE *resp; + int respLen; + + BYTE *resp1 = (((BYTE *)BigBuf) + 800); + int resp1Len; + + BYTE *receivedCmd = (BYTE *)BigBuf; + int len; + + int i; + + int cmdsRecvd = 0; + + memset(receivedCmd, 0x44, 400); + + CodeIso14443bAsTag(response1, sizeof(response1)); + memcpy(resp1, ToSend, ToSendMax); resp1Len = ToSendMax; + + // We need to listen to the high-frequency, peak-detected path. + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaSetupSsc(); + + cmdsRecvd = 0; + + for(;;) { + BYTE b1, b2; + + if(!GetIso14443CommandFromReader(receivedCmd, &len, 100)) { + DbpIntegers(cmdsRecvd, 0, 0); + DbpString("button press"); + break; + } + + // Good, look at the command now. + + if(len == sizeof(cmd1) && memcmp(receivedCmd, cmd1, len)==0) { + resp = resp1; respLen = resp1Len; + } else { + DbpString("new cmd from reader:"); + DbpIntegers(len, 0x1234, cmdsRecvd); + // And print whether the CRC fails, just for good measure + ComputeCrc14443(CRC_14443_B, receivedCmd, len-2, &b1, &b2); + if(b1 != receivedCmd[len-2] || b2 != receivedCmd[len-1]) { + // Not so good, try again. + DbpString("+++CRC fail"); + } else { + DbpString("CRC passes"); + } + break; + } + + memset(receivedCmd, 0x44, 32); + + cmdsRecvd++; + + if(cmdsRecvd > 0x30) { + DbpString("many commands later..."); + break; + } + + if(respLen <= 0) continue; + + // Modulate BPSK + FpgaWriteConfWord( + FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK); + SSC_TRANSMIT_HOLDING = 0xff; + FpgaSetupSsc(); + + // Transmit the response. + i = 0; + for(;;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + BYTE b = resp[i]; + + SSC_TRANSMIT_HOLDING = b; + + i++; + if(i > respLen) { + break; + } + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + volatile BYTE b = (BYTE)SSC_RECEIVE_HOLDING; + (void)b; + } + } + } +} + +//============================================================================= +// An ISO 14443 Type B reader. We take layer two commands, code them +// appropriately, and then send them to the tag. We then listen for the +// tag's response, which we leave in the buffer to be demodulated on the +// PC side. +//============================================================================= + +static struct { + enum { + DEMOD_UNSYNCD, + DEMOD_PHASE_REF_TRAINING, + DEMOD_AWAITING_FALLING_EDGE_OF_SOF, + DEMOD_GOT_FALLING_EDGE_OF_SOF, + DEMOD_AWAITING_START_BIT, + DEMOD_RECEIVING_DATA, + DEMOD_ERROR_WAIT + } state; + int bitCount; + int posCount; + int thisBit; + int metric; + int metricN; + WORD shiftReg; + BYTE *output; + int len; + int sumI; + int sumQ; +} Demod; + +static BOOL Handle14443SamplesDemod(int ci, int cq) +{ + int v; + + // The soft decision on the bit uses an estimate of just the + // quadrant of the reference angle, not the exact angle. +#define MAKE_SOFT_DECISION() { \ + if(Demod.sumI > 0) { \ + v = ci; \ + } else { \ + v = -ci; \ + } \ + if(Demod.sumQ > 0) { \ + v += cq; \ + } else { \ + v -= cq; \ + } \ + } + + switch(Demod.state) { + case DEMOD_UNSYNCD: + v = ci; + if(v < 0) v = -v; + if(cq > 0) { + v += cq; + } else { + v -= cq; + } + if(v > 40) { + Demod.posCount = 0; + Demod.state = DEMOD_PHASE_REF_TRAINING; + Demod.sumI = 0; + Demod.sumQ = 0; + } + break; + + case DEMOD_PHASE_REF_TRAINING: + if(Demod.posCount < 8) { + Demod.sumI += ci; + Demod.sumQ += cq; + } else if(Demod.posCount > 100) { + // error, waited too long + Demod.state = DEMOD_UNSYNCD; + } else { + MAKE_SOFT_DECISION(); + if(v < 0) { + Demod.state = DEMOD_AWAITING_FALLING_EDGE_OF_SOF; + Demod.posCount = 0; + } + } + Demod.posCount++; + break; + + case DEMOD_AWAITING_FALLING_EDGE_OF_SOF: + MAKE_SOFT_DECISION(); + if(v < 0) { + Demod.state = DEMOD_GOT_FALLING_EDGE_OF_SOF; + Demod.posCount = 0; + } else { + if(Demod.posCount > 100) { + Demod.state = DEMOD_UNSYNCD; + } + } + Demod.posCount++; + break; + + case DEMOD_GOT_FALLING_EDGE_OF_SOF: + MAKE_SOFT_DECISION(); + if(v > 0) { + if(Demod.posCount < 12) { + Demod.state = DEMOD_UNSYNCD; + } else { + Demod.state = DEMOD_AWAITING_START_BIT; + Demod.posCount = 0; + Demod.len = 0; + Demod.metricN = 0; + Demod.metric = 0; + } + } else { + if(Demod.posCount > 100) { + Demod.state = DEMOD_UNSYNCD; + } + } + Demod.posCount++; + break; + + case DEMOD_AWAITING_START_BIT: + MAKE_SOFT_DECISION(); + if(v > 0) { + if(Demod.posCount > 10) { + Demod.state = DEMOD_UNSYNCD; + } + } else { + Demod.bitCount = 0; + Demod.posCount = 1; + Demod.thisBit = v; + Demod.shiftReg = 0; + Demod.state = DEMOD_RECEIVING_DATA; + } + break; + + case DEMOD_RECEIVING_DATA: + MAKE_SOFT_DECISION(); + if(Demod.posCount == 0) { + Demod.thisBit = v; + Demod.posCount = 1; + } else { + Demod.thisBit += v; + + if(Demod.thisBit > 0) { + Demod.metric += Demod.thisBit; + } else { + Demod.metric -= Demod.thisBit; + } + (Demod.metricN)++; + + Demod.shiftReg >>= 1; + if(Demod.thisBit > 0) { + Demod.shiftReg |= 0x200; + } + + Demod.bitCount++; + if(Demod.bitCount == 10) { + WORD s = Demod.shiftReg; + if((s & 0x200) && !(s & 0x001)) { + BYTE b = (s >> 1); + Demod.output[Demod.len] = b; + Demod.len++; + Demod.state = DEMOD_AWAITING_START_BIT; + } else if(s == 0x000) { + // This is EOF + return TRUE; + Demod.state = DEMOD_UNSYNCD; + } else { + Demod.state = DEMOD_UNSYNCD; + } + } + Demod.posCount = 0; + } + break; + + default: + Demod.state = DEMOD_UNSYNCD; + break; + } + + return FALSE; +} + +static void GetSamplesFor14443Demod(BOOL weTx, int n) +{ + int max = 0; + BOOL gotFrame = FALSE; + +//# define DMA_BUFFER_SIZE 8 + SBYTE *dmaBuf; + + int lastRxCounter; + SBYTE *upTo; + + int ci, cq; + + int samples = 0; + + // Clear out the state of the "UART" that receives from the tag. + memset(BigBuf, 0x44, 400); + Demod.output = (BYTE *)BigBuf; + Demod.len = 0; + Demod.state = DEMOD_UNSYNCD; + + // And the UART that receives from the reader + Uart.output = (((BYTE *)BigBuf) + 1024); + Uart.byteCntMax = 100; + Uart.state = STATE_UNSYNCD; + + // Setup for the DMA. + dmaBuf = (SBYTE *)(BigBuf + 32); + upTo = dmaBuf; + lastRxCounter = DMA_BUFFER_SIZE; + FpgaSetupSscDma((BYTE *)dmaBuf, DMA_BUFFER_SIZE); + + // And put the FPGA in the appropriate mode + FpgaWriteConfWord( + FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | + (weTx ? 0 : FPGA_HF_READER_RX_XCORR_SNOOP)); + + for(;;) { + int behindBy = lastRxCounter - PDC_RX_COUNTER(SSC_BASE); + if(behindBy > max) max = behindBy; + + LED_D_ON(); + while(((lastRxCounter-PDC_RX_COUNTER(SSC_BASE)) & (DMA_BUFFER_SIZE-1)) + > 2) + { + ci = upTo[0]; + cq = upTo[1]; + upTo += 2; + if(upTo - dmaBuf > DMA_BUFFER_SIZE) { + upTo -= DMA_BUFFER_SIZE; + PDC_RX_NEXT_POINTER(SSC_BASE) = (DWORD)upTo; + PDC_RX_NEXT_COUNTER(SSC_BASE) = DMA_BUFFER_SIZE; + } + lastRxCounter -= 2; + if(lastRxCounter <= 0) { + lastRxCounter += DMA_BUFFER_SIZE; + } + + samples += 2; + + Handle14443UartBit(1); + Handle14443UartBit(1); + + if(Handle14443SamplesDemod(ci, cq)) { + gotFrame = 1; + } + } + LED_D_OFF(); + + if(samples > 2000) { + break; + } + } + PDC_CONTROL(SSC_BASE) = PDC_RX_DISABLE; + DbpIntegers(max, gotFrame, -1); +} + +//----------------------------------------------------------------------------- +// Read the tag's response. We just receive a stream of slightly-processed +// samples from the FPGA, which we will later do some signal processing on, +// to get the bits. +//----------------------------------------------------------------------------- +/*static void GetSamplesFor14443(BOOL weTx, int n) +{ + BYTE *dest = (BYTE *)BigBuf; + int c; + + FpgaWriteConfWord( + FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | + (weTx ? 0 : FPGA_HF_READER_RX_XCORR_SNOOP)); + + c = 0; + for(;;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = 0x43; + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + SBYTE b; + b = (SBYTE)SSC_RECEIVE_HOLDING; + + dest[c++] = (BYTE)b; + + if(c >= n) { + break; + } + } + } +}*/ + +//----------------------------------------------------------------------------- +// Transmit the command (to the tag) that was placed in ToSend[]. +//----------------------------------------------------------------------------- +static void TransmitFor14443(void) +{ + int c; + + FpgaSetupSsc(); + + while(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = 0xff; + } + + FpgaWriteConfWord( + FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); + + for(c = 0; c < 10;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = 0xff; + c++; + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + volatile DWORD r = SSC_RECEIVE_HOLDING; + (void)r; + } + WDT_HIT(); + } + + c = 0; + for(;;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = ToSend[c]; + c++; + if(c >= ToSendMax) { + break; + } + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + volatile DWORD r = SSC_RECEIVE_HOLDING; + (void)r; + } + WDT_HIT(); + } +} + +//----------------------------------------------------------------------------- +// Code a layer 2 command (string of octets, including CRC) into ToSend[], +// so that it is ready to transmit to the tag using TransmitFor14443(). +//----------------------------------------------------------------------------- +void CodeIso14443bAsReader(const BYTE *cmd, int len) +{ + int i, j; + BYTE b; + + ToSendReset(); + + // Establish initial reference level + for(i = 0; i < 40; i++) { + ToSendStuffBit(1); + } + // Send SOF + for(i = 0; i < 10; i++) { + ToSendStuffBit(0); + } + + for(i = 0; i < len; i++) { + // Stop bits/EGT + ToSendStuffBit(1); + ToSendStuffBit(1); + // Start bit + ToSendStuffBit(0); + // Data bits + b = cmd[i]; + for(j = 0; j < 8; j++) { + if(b & 1) { + ToSendStuffBit(1); + } else { + ToSendStuffBit(0); + } + b >>= 1; + } + } + // Send EOF + ToSendStuffBit(1); + for(i = 0; i < 10; i++) { + ToSendStuffBit(0); + } + for(i = 0; i < 8; i++) { + ToSendStuffBit(1); + } + + // And then a little more, to make sure that the last character makes + // it out before we switch to rx mode. + for(i = 0; i < 24; i++) { + ToSendStuffBit(1); + } + + // Convert from last character reference to length + ToSendMax++; +} + +//----------------------------------------------------------------------------- +// Read an ISO 14443 tag. We send it some set of commands, and record the +// responses. +//----------------------------------------------------------------------------- +void AcquireRawAdcSamplesIso14443(DWORD parameter) +{ +// BYTE cmd1[] = { 0x05, 0x00, 0x00, 0x71, 0xff }; + BYTE cmd1[] = { 0x05, 0x00, 0x08, 0x39, 0x73 }; + + // Make sure that we start from off, since the tags are stateful; + // confusing things will happen if we don't reset them between reads. + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(200); + + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaSetupSsc(); + + // Now give it time to spin up. + FpgaWriteConfWord( + FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); + SpinDelay(200); + + CodeIso14443bAsReader(cmd1, sizeof(cmd1)); + TransmitFor14443(); + LED_A_ON(); + GetSamplesFor14443Demod(TRUE, 2000); + LED_A_OFF(); +} + +//============================================================================= +// Finally, the `sniffer' combines elements from both the reader and +// simulated tag, to show both sides of the conversation. +//============================================================================= + +//----------------------------------------------------------------------------- +// Record the sequence of commands sent by the reader to the tag, with +// triggering so that we start recording at the point that the tag is moved +// near the reader. +//----------------------------------------------------------------------------- +void SnoopIso14443(void) +{ + // We won't start recording the frames that we acquire until we trigger; + // a good trigger condition to get started is probably when we see a + // response from the tag. + BOOL triggered = FALSE; + + // The command (reader -> tag) that we're working on receiving. + BYTE *receivedCmd = (((BYTE *)BigBuf) + 1024); + // The response (tag -> reader) that we're working on receiving. + BYTE *receivedResponse = (((BYTE *)BigBuf) + 1536); + + // As we receive stuff, we copy it from receivedCmd or receivedResponse + // into trace, along with its length and other annotations. + BYTE *trace = (BYTE *)BigBuf; + int traceLen = 0; + + // The DMA buffer, used to stream samples from the FPGA. +//# define DMA_BUFFER_SIZE 256 + SBYTE *dmaBuf = ((SBYTE *)BigBuf) + 2048; + int lastRxCounter; + SBYTE *upTo; + int ci, cq; + int maxBehindBy = 0; + + // Count of samples received so far, so that we can include timing + // information in the trace buffer. + int samples = 0; + + memset(trace, 0x44, 1000); + + // Set up the demodulator for tag -> reader responses. + Demod.output = receivedResponse; + Demod.len = 0; + Demod.state = DEMOD_UNSYNCD; + + // And the reader -> tag commands + memset(&Uart, 0, sizeof(Uart)); + Uart.output = receivedCmd; + Uart.byteCntMax = 100; + Uart.state = STATE_UNSYNCD; + + // And put the FPGA in the appropriate mode + FpgaWriteConfWord( + FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | + FPGA_HF_READER_RX_XCORR_SNOOP); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // Setup for the DMA. + FpgaSetupSsc(); + upTo = dmaBuf; + lastRxCounter = DMA_BUFFER_SIZE; + FpgaSetupSscDma((BYTE *)dmaBuf, DMA_BUFFER_SIZE); + + LED_A_ON(); + + // And now we loop, receiving samples. + for(;;) { + int behindBy = (lastRxCounter - PDC_RX_COUNTER(SSC_BASE)) & + (DMA_BUFFER_SIZE-1); + if(behindBy > maxBehindBy) { + maxBehindBy = behindBy; + if(behindBy > 100) { + DbpString("blew circular buffer!"); + goto done; + } + } + if(behindBy < 2) continue; + + ci = upTo[0]; + cq = upTo[1]; + upTo += 2; + lastRxCounter -= 2; + if(upTo - dmaBuf > DMA_BUFFER_SIZE) { + upTo -= DMA_BUFFER_SIZE; + lastRxCounter += DMA_BUFFER_SIZE; + PDC_RX_NEXT_POINTER(SSC_BASE) = (DWORD)upTo; + PDC_RX_NEXT_COUNTER(SSC_BASE) = DMA_BUFFER_SIZE; + } + + samples += 2; + +#define HANDLE_BIT_IF_BODY \ + if(triggered) { \ + trace[traceLen++] = ((samples >> 0) & 0xff); \ + trace[traceLen++] = ((samples >> 8) & 0xff); \ + trace[traceLen++] = ((samples >> 16) & 0xff); \ + trace[traceLen++] = ((samples >> 24) & 0xff); \ + trace[traceLen++] = 0; \ + trace[traceLen++] = 0; \ + trace[traceLen++] = 0; \ + trace[traceLen++] = 0; \ + trace[traceLen++] = Uart.byteCnt; \ + memcpy(trace+traceLen, receivedCmd, Uart.byteCnt); \ + traceLen += Uart.byteCnt; \ + if(traceLen > 1000) break; \ + } \ + /* And ready to receive another command. */ \ + memset(&Uart, 0, sizeof(Uart)); \ + Uart.output = receivedCmd; \ + Uart.byteCntMax = 100; \ + Uart.state = STATE_UNSYNCD; \ + /* And also reset the demod code, which might have been */ \ + /* false-triggered by the commands from the reader. */ \ + memset(&Demod, 0, sizeof(Demod)); \ + Demod.output = receivedResponse; \ + Demod.state = DEMOD_UNSYNCD; \ + + if(Handle14443UartBit(ci & 1)) { + HANDLE_BIT_IF_BODY + } + if(Handle14443UartBit(cq & 1)) { + HANDLE_BIT_IF_BODY + } + + if(Handle14443SamplesDemod(ci, cq)) { + // timestamp, as a count of samples + trace[traceLen++] = ((samples >> 0) & 0xff); + trace[traceLen++] = ((samples >> 8) & 0xff); + trace[traceLen++] = ((samples >> 16) & 0xff); + trace[traceLen++] = 0x80 | ((samples >> 24) & 0xff); + // correlation metric (~signal strength estimate) + if(Demod.metricN != 0) { + Demod.metric /= Demod.metricN; + } + trace[traceLen++] = ((Demod.metric >> 0) & 0xff); + trace[traceLen++] = ((Demod.metric >> 8) & 0xff); + trace[traceLen++] = ((Demod.metric >> 16) & 0xff); + trace[traceLen++] = ((Demod.metric >> 24) & 0xff); + // length + trace[traceLen++] = Demod.len; + memcpy(trace+traceLen, receivedResponse, Demod.len); + traceLen += Demod.len; + if(traceLen > 1000) break; + + triggered = TRUE; + LED_A_OFF(); + LED_B_ON(); + + // And ready to receive another response. + memset(&Demod, 0, sizeof(Demod)); + Demod.output = receivedResponse; + Demod.state = DEMOD_UNSYNCD; + } + + if(BUTTON_PRESS()) { + DbpString("cancelled"); + goto done; + } + } + + DbpString("in done pt"); + + DbpIntegers(maxBehindBy, Uart.state, Uart.byteCnt); + DbpIntegers(Uart.byteCntMax, traceLen, 0x23); + +done: + PDC_CONTROL(SSC_BASE) = PDC_RX_DISABLE; + LED_A_OFF(); + LED_B_OFF(); +} diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c new file mode 100644 index 00000000..a687d877 --- /dev/null +++ b/armsrc/iso14443a.c @@ -0,0 +1,1815 @@ +//----------------------------------------------------------------------------- +// Routines to support ISO 14443 type A. +// +// Gerhard de Koning Gans - May 2008 +//----------------------------------------------------------------------------- +#include +#include "apps.h" +#include "..\common\iso14443_crc.c" + +typedef enum { + SEC_D = 1, + SEC_E = 2, + SEC_F = 3, + SEC_X = 4, + SEC_Y = 5, + SEC_Z = 6 +} SecType; + +//----------------------------------------------------------------------------- +// The software UART that receives commands from the reader, and its state +// variables. +//----------------------------------------------------------------------------- +static struct { + enum { + STATE_UNSYNCD, + STATE_START_OF_COMMUNICATION, + STATE_MILLER_X, + STATE_MILLER_Y, + STATE_MILLER_Z, + STATE_ERROR_WAIT + } state; + WORD shiftReg; + int bitCnt; + int byteCnt; + int byteCntMax; + int posCnt; + int syncBit; + int parityBits; + int samples; + int highCnt; + int bitBuffer; + enum { + DROP_NONE, + DROP_FIRST_HALF, + DROP_SECOND_HALF + } drop; + BYTE *output; +} Uart; + +static BOOL MillerDecoding(int bit) +{ + int error = 0; + int bitright; + + if(!Uart.bitBuffer) { + Uart.bitBuffer = bit ^ 0xFF0; + return FALSE; + } + else { + Uart.bitBuffer <<= 4; + Uart.bitBuffer ^= bit; + } + + BOOL EOC = FALSE; + + if(Uart.state != STATE_UNSYNCD) { + Uart.posCnt++; + + if((Uart.bitBuffer & Uart.syncBit) ^ Uart.syncBit) { + bit = 0x00; + } + else { + bit = 0x01; + } + if(((Uart.bitBuffer << 1) & Uart.syncBit) ^ Uart.syncBit) { + bitright = 0x00; + } + else { + bitright = 0x01; + } + if(bit != bitright) { bit = bitright; } + + if(Uart.posCnt == 1) { + // measurement first half bitperiod + if(!bit) { + Uart.drop = DROP_FIRST_HALF; + } + } + else { + // measurement second half bitperiod + if(!bit & (Uart.drop == DROP_NONE)) { + Uart.drop = DROP_SECOND_HALF; + } + else if(!bit) { + // measured a drop in first and second half + // which should not be possible + Uart.state = STATE_ERROR_WAIT; + error = 0x01; + } + + Uart.posCnt = 0; + + switch(Uart.state) { + case STATE_START_OF_COMMUNICATION: + Uart.shiftReg = 0; + if(Uart.drop == DROP_SECOND_HALF) { + // error, should not happen in SOC + Uart.state = STATE_ERROR_WAIT; + error = 0x02; + } + else { + // correct SOC + Uart.state = STATE_MILLER_Z; + } + break; + + case STATE_MILLER_Z: + Uart.bitCnt++; + Uart.shiftReg >>= 1; + if(Uart.drop == DROP_NONE) { + // logic '0' followed by sequence Y + // end of communication + Uart.state = STATE_UNSYNCD; + EOC = TRUE; + } + // if(Uart.drop == DROP_FIRST_HALF) { + // Uart.state = STATE_MILLER_Z; stay the same + // we see a logic '0' } + if(Uart.drop == DROP_SECOND_HALF) { + // we see a logic '1' + Uart.shiftReg |= 0x100; + Uart.state = STATE_MILLER_X; + } + break; + + case STATE_MILLER_X: + Uart.shiftReg >>= 1; + if(Uart.drop == DROP_NONE) { + // sequence Y, we see a '0' + Uart.state = STATE_MILLER_Y; + Uart.bitCnt++; + } + if(Uart.drop == DROP_FIRST_HALF) { + // Would be STATE_MILLER_Z + // but Z does not follow X, so error + Uart.state = STATE_ERROR_WAIT; + error = 0x03; + } + if(Uart.drop == DROP_SECOND_HALF) { + // We see a '1' and stay in state X + Uart.shiftReg |= 0x100; + Uart.bitCnt++; + } + break; + + case STATE_MILLER_Y: + Uart.bitCnt++; + Uart.shiftReg >>= 1; + if(Uart.drop == DROP_NONE) { + // logic '0' followed by sequence Y + // end of communication + Uart.state = STATE_UNSYNCD; + EOC = TRUE; + } + if(Uart.drop == DROP_FIRST_HALF) { + // we see a '0' + Uart.state = STATE_MILLER_Z; + } + if(Uart.drop == DROP_SECOND_HALF) { + // We see a '1' and go to state X + Uart.shiftReg |= 0x100; + Uart.state = STATE_MILLER_X; + } + break; + + case STATE_ERROR_WAIT: + // That went wrong. Now wait for at least two bit periods + // and try to sync again + if(Uart.drop == DROP_NONE) { + Uart.highCnt = 6; + Uart.state = STATE_UNSYNCD; + } + break; + + default: + Uart.state = STATE_UNSYNCD; + Uart.highCnt = 0; + break; + } + + Uart.drop = DROP_NONE; + + // should have received at least one whole byte... + if((Uart.bitCnt == 2) && EOC && (Uart.byteCnt > 0)) { + return TRUE; + } + + if(Uart.bitCnt == 9) { + Uart.output[Uart.byteCnt] = (Uart.shiftReg & 0xff); + Uart.byteCnt++; + + Uart.parityBits <<= 1; + Uart.parityBits ^= ((Uart.shiftReg >> 8) & 0x01); + + if(EOC) { + // when End of Communication received and + // all data bits processed.. + return TRUE; + } + Uart.bitCnt = 0; + } + + /*if(error) { + Uart.output[Uart.byteCnt] = 0xAA; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = error & 0xFF; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = 0xAA; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = (Uart.bitBuffer >> 8) & 0xFF; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = (Uart.syncBit >> 3) & 0xFF; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = 0xAA; + Uart.byteCnt++; + return TRUE; + }*/ + } + + } + else { + bit = Uart.bitBuffer & 0xf0; + bit >>= 4; + bit ^= 0x0F; + if(bit) { + // should have been high or at least (4 * 128) / fc + // according to ISO this should be at least (9 * 128 + 20) / fc + if(Uart.highCnt == 8) { + // we went low, so this could be start of communication + // it turns out to be safer to choose a less significant + // syncbit... so we check whether the neighbour also represents the drop + Uart.posCnt = 1; // apparently we are busy with our first half bit period + Uart.syncBit = bit & 8; + Uart.samples = 3; + if(!Uart.syncBit) { Uart.syncBit = bit & 4; Uart.samples = 2; } + else if(bit & 4) { Uart.syncBit = bit & 4; Uart.samples = 2; bit <<= 2; } + if(!Uart.syncBit) { Uart.syncBit = bit & 2; Uart.samples = 1; } + else if(bit & 2) { Uart.syncBit = bit & 2; Uart.samples = 1; bit <<= 1; } + if(!Uart.syncBit) { Uart.syncBit = bit & 1; Uart.samples = 0; + if(Uart.syncBit & (Uart.bitBuffer & 8)) { + Uart.syncBit = 8; + + // the first half bit period is expected in next sample + Uart.posCnt = 0; + Uart.samples = 3; + } + } + else if(bit & 1) { Uart.syncBit = bit & 1; Uart.samples = 0; } + + Uart.syncBit <<= 4; + Uart.state = STATE_START_OF_COMMUNICATION; + Uart.drop = DROP_FIRST_HALF; + Uart.bitCnt = 0; + Uart.byteCnt = 0; + Uart.parityBits = 0; + error = 0; + } + else { + Uart.highCnt = 0; + } + } + else { + if(Uart.highCnt < 8) { + Uart.highCnt++; + } + } + } + + return FALSE; +} + +//============================================================================= +// ISO 14443 Type A - Manchester +//============================================================================= + +static struct { + enum { + DEMOD_UNSYNCD, + DEMOD_START_OF_COMMUNICATION, + DEMOD_MANCHESTER_D, + DEMOD_MANCHESTER_E, + DEMOD_MANCHESTER_F, + DEMOD_ERROR_WAIT + } state; + int bitCount; + int posCount; + int syncBit; + int parityBits; + WORD shiftReg; + int buffer; + int buff; + int samples; + int len; + enum { + SUB_NONE, + SUB_FIRST_HALF, + SUB_SECOND_HALF + } sub; + BYTE *output; +} Demod; + +static BOOL ManchesterDecoding(int v) +{ + int bit; + int modulation; + int error = 0; + + if(!Demod.buff) { + Demod.buff = 1; + Demod.buffer = v; + return FALSE; + } + else { + bit = Demod.buffer; + Demod.buffer = v; + } + + if(Demod.state==DEMOD_UNSYNCD) { + Demod.output[Demod.len] = 0xfa; + Demod.syncBit = 0; + //Demod.samples = 0; + Demod.posCount = 1; // This is the first half bit period, so after syncing handle the second part + if(bit & 0x08) { Demod.syncBit = 0x08; } + if(!Demod.syncBit) { + if(bit & 0x04) { Demod.syncBit = 0x04; } + } + else if(bit & 0x04) { Demod.syncBit = 0x04; bit <<= 4; } + if(!Demod.syncBit) { + if(bit & 0x02) { Demod.syncBit = 0x02; } + } + else if(bit & 0x02) { Demod.syncBit = 0x02; bit <<= 4; } + if(!Demod.syncBit) { + if(bit & 0x01) { Demod.syncBit = 0x01; } + + if(Demod.syncBit & (Demod.buffer & 0x08)) { + Demod.syncBit = 0x08; + + // The first half bitperiod is expected in next sample + Demod.posCount = 0; + Demod.output[Demod.len] = 0xfb; + } + } + else if(bit & 0x01) { Demod.syncBit = 0x01; } + + if(Demod.syncBit) { + Demod.len = 0; + Demod.state = DEMOD_START_OF_COMMUNICATION; + Demod.sub = SUB_FIRST_HALF; + Demod.bitCount = 0; + Demod.shiftReg = 0; + Demod.parityBits = 0; + Demod.samples = 0; + if(Demod.posCount) { + switch(Demod.syncBit) { + case 0x08: Demod.samples = 3; break; + case 0x04: Demod.samples = 2; break; + case 0x02: Demod.samples = 1; break; + case 0x01: Demod.samples = 0; break; + } + } + error = 0; + } + } + else { + //modulation = bit & Demod.syncBit; + modulation = ((bit << 1) ^ ((Demod.buffer & 0x08) >> 3)) & Demod.syncBit; + + Demod.samples += 4; + + if(Demod.posCount==0) { + Demod.posCount = 1; + if(modulation) { + Demod.sub = SUB_FIRST_HALF; + } + else { + Demod.sub = SUB_NONE; + } + } + else { + Demod.posCount = 0; + if(modulation && (Demod.sub == SUB_FIRST_HALF)) { + if(Demod.state!=DEMOD_ERROR_WAIT) { + Demod.state = DEMOD_ERROR_WAIT; + Demod.output[Demod.len] = 0xaa; + error = 0x01; + } + } + else if(modulation) { + Demod.sub = SUB_SECOND_HALF; + } + + switch(Demod.state) { + case DEMOD_START_OF_COMMUNICATION: + if(Demod.sub == SUB_FIRST_HALF) { + Demod.state = DEMOD_MANCHESTER_D; + } + else { + Demod.output[Demod.len] = 0xab; + Demod.state = DEMOD_ERROR_WAIT; + error = 0x02; + } + break; + + case DEMOD_MANCHESTER_D: + case DEMOD_MANCHESTER_E: + if(Demod.sub == SUB_FIRST_HALF) { + Demod.bitCount++; + Demod.shiftReg = (Demod.shiftReg >> 1) ^ 0x100; + Demod.state = DEMOD_MANCHESTER_D; + } + else if(Demod.sub == SUB_SECOND_HALF) { + Demod.bitCount++; + Demod.shiftReg >>= 1; + Demod.state = DEMOD_MANCHESTER_E; + } + else { + Demod.state = DEMOD_MANCHESTER_F; + } + break; + + case DEMOD_MANCHESTER_F: + // Tag response does not need to be a complete byte! + if(Demod.len > 0 || Demod.bitCount > 0) { + if(Demod.bitCount > 0) { + Demod.shiftReg >>= (9 - Demod.bitCount); + Demod.output[Demod.len] = Demod.shiftReg & 0xff; + Demod.len++; + // No parity bit, so just shift a 0 + Demod.parityBits <<= 1; + } + + Demod.state = DEMOD_UNSYNCD; + return TRUE; + } + else { + Demod.output[Demod.len] = 0xad; + Demod.state = DEMOD_ERROR_WAIT; + error = 0x03; + } + break; + + case DEMOD_ERROR_WAIT: + Demod.state = DEMOD_UNSYNCD; + break; + + default: + Demod.output[Demod.len] = 0xdd; + Demod.state = DEMOD_UNSYNCD; + break; + } + + if(Demod.bitCount>=9) { + Demod.output[Demod.len] = Demod.shiftReg & 0xff; + Demod.len++; + + Demod.parityBits <<= 1; + Demod.parityBits ^= ((Demod.shiftReg >> 8) & 0x01); + + Demod.bitCount = 0; + Demod.shiftReg = 0; + } + + /*if(error) { + Demod.output[Demod.len] = 0xBB; + Demod.len++; + Demod.output[Demod.len] = error & 0xFF; + Demod.len++; + Demod.output[Demod.len] = 0xBB; + Demod.len++; + Demod.output[Demod.len] = bit & 0xFF; + Demod.len++; + Demod.output[Demod.len] = Demod.buffer & 0xFF; + Demod.len++; + Demod.output[Demod.len] = Demod.syncBit & 0xFF; + Demod.len++; + Demod.output[Demod.len] = 0xBB; + Demod.len++; + return TRUE; + }*/ + + } + + } // end (state != UNSYNCED) + + return FALSE; +} + +//============================================================================= +// Finally, a `sniffer' for ISO 14443 Type A +// Both sides of communication! +//============================================================================= + +//----------------------------------------------------------------------------- +// Record the sequence of commands sent by the reader to the tag, with +// triggering so that we start recording at the point that the tag is moved +// near the reader. +//----------------------------------------------------------------------------- +void SnoopIso14443a(void) +{ + + // BIG CHANGE - UNDERSTAND THIS BEFORE WE COMMIT + + #define RECV_CMD_OFFSET 3032 + #define RECV_RES_OFFSET 3096 + #define DMA_BUFFER_OFFSET 3160 + #define DMA_BUFFER_SIZE 4096 + #define TRACE_LENGTH 3000 + +// #define RECV_CMD_OFFSET 2032 // original (working as of 21/2/09) values +// #define RECV_RES_OFFSET 2096 // original (working as of 21/2/09) values +// #define DMA_BUFFER_OFFSET 2160 // original (working as of 21/2/09) values +// #define DMA_BUFFER_SIZE 4096 // original (working as of 21/2/09) values +// #define TRACE_LENGTH 2000 // original (working as of 21/2/09) values + + // We won't start recording the frames that we acquire until we trigger; + // a good trigger condition to get started is probably when we see a + // response from the tag. + BOOL triggered = TRUE; // FALSE to wait first for card + + // The command (reader -> tag) that we're receiving. + // The length of a received command will in most cases be no more than 18 bytes. + // So 32 should be enough! + BYTE *receivedCmd = (((BYTE *)BigBuf) + RECV_CMD_OFFSET); + // The response (tag -> reader) that we're receiving. + BYTE *receivedResponse = (((BYTE *)BigBuf) + RECV_RES_OFFSET); + + // As we receive stuff, we copy it from receivedCmd or receivedResponse + // into trace, along with its length and other annotations. + BYTE *trace = (BYTE *)BigBuf; + int traceLen = 0; + + // The DMA buffer, used to stream samples from the FPGA + SBYTE *dmaBuf = ((SBYTE *)BigBuf) + DMA_BUFFER_OFFSET; + int lastRxCounter; + SBYTE *upTo; + int smpl; + int maxBehindBy = 0; + + // Count of samples received so far, so that we can include timing + // information in the trace buffer. + int samples = 0; + int rsamples = 0; + + memset(trace, 0x44, RECV_CMD_OFFSET); + + // Set up the demodulator for tag -> reader responses. + Demod.output = receivedResponse; + Demod.len = 0; + Demod.state = DEMOD_UNSYNCD; + + // And the reader -> tag commands + memset(&Uart, 0, sizeof(Uart)); + Uart.output = receivedCmd; + Uart.byteCntMax = 32; // was 100 (greg)//////////////////////////////////////////////////////////////////////// + Uart.state = STATE_UNSYNCD; + + // And put the FPGA in the appropriate mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_SNIFFER); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // Setup for the DMA. + FpgaSetupSsc(); + upTo = dmaBuf; + lastRxCounter = DMA_BUFFER_SIZE; + FpgaSetupSscDma((BYTE *)dmaBuf, DMA_BUFFER_SIZE); + + LED_A_ON(); + + // And now we loop, receiving samples. + for(;;) { + WDT_HIT(); + int behindBy = (lastRxCounter - PDC_RX_COUNTER(SSC_BASE)) & + (DMA_BUFFER_SIZE-1); + if(behindBy > maxBehindBy) { + maxBehindBy = behindBy; + if(behindBy > 400) { + DbpString("blew circular buffer!"); + goto done; + } + } + if(behindBy < 1) continue; + + smpl = upTo[0]; + upTo++; + lastRxCounter -= 1; + if(upTo - dmaBuf > DMA_BUFFER_SIZE) { + upTo -= DMA_BUFFER_SIZE; + lastRxCounter += DMA_BUFFER_SIZE; + PDC_RX_NEXT_POINTER(SSC_BASE) = (DWORD)upTo; + PDC_RX_NEXT_COUNTER(SSC_BASE) = DMA_BUFFER_SIZE; + } + + samples += 4; +#define HANDLE_BIT_IF_BODY \ + LED_C_ON(); \ + if(triggered) { \ + trace[traceLen++] = ((rsamples >> 0) & 0xff); \ + trace[traceLen++] = ((rsamples >> 8) & 0xff); \ + trace[traceLen++] = ((rsamples >> 16) & 0xff); \ + trace[traceLen++] = ((rsamples >> 24) & 0xff); \ + trace[traceLen++] = ((Uart.parityBits >> 0) & 0xff); \ + trace[traceLen++] = ((Uart.parityBits >> 8) & 0xff); \ + trace[traceLen++] = ((Uart.parityBits >> 16) & 0xff); \ + trace[traceLen++] = ((Uart.parityBits >> 24) & 0xff); \ + trace[traceLen++] = Uart.byteCnt; \ + memcpy(trace+traceLen, receivedCmd, Uart.byteCnt); \ + traceLen += Uart.byteCnt; \ + if(traceLen > TRACE_LENGTH) break; \ + } \ + /* And ready to receive another command. */ \ + Uart.state = STATE_UNSYNCD; \ + /* And also reset the demod code, which might have been */ \ + /* false-triggered by the commands from the reader. */ \ + Demod.state = DEMOD_UNSYNCD; \ + LED_B_OFF(); \ + + if(MillerDecoding((smpl & 0xF0) >> 4)) { + rsamples = samples - Uart.samples; + HANDLE_BIT_IF_BODY + } + if(ManchesterDecoding(smpl & 0x0F)) { + rsamples = samples - Demod.samples; + LED_B_ON(); + + // timestamp, as a count of samples + trace[traceLen++] = ((rsamples >> 0) & 0xff); + trace[traceLen++] = ((rsamples >> 8) & 0xff); + trace[traceLen++] = ((rsamples >> 16) & 0xff); + trace[traceLen++] = 0x80 | ((rsamples >> 24) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 0) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 8) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 16) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 24) & 0xff); + // length + trace[traceLen++] = Demod.len; + memcpy(trace+traceLen, receivedResponse, Demod.len); + traceLen += Demod.len; + if(traceLen > TRACE_LENGTH) break; + + triggered = TRUE; + + // And ready to receive another response. + memset(&Demod, 0, sizeof(Demod)); + Demod.output = receivedResponse; + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + } + + if(BUTTON_PRESS()) { + DbpString("cancelled_a"); + goto done; + } + } + + DbpString("COMMAND FINISHED"); + + DbpIntegers(maxBehindBy, Uart.state, Uart.byteCnt); + DbpIntegers(Uart.byteCntMax, traceLen, (int)Uart.output[0]); + +done: + PDC_CONTROL(SSC_BASE) = PDC_RX_DISABLE; + DbpIntegers(maxBehindBy, Uart.state, Uart.byteCnt); + DbpIntegers(Uart.byteCntMax, traceLen, (int)Uart.output[0]); + LED_A_OFF(); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); +} + +// Prepare communication bits to send to FPGA +void Sequence(SecType seq) +{ + ToSendMax++; + switch(seq) { + // CARD TO READER + case SEC_D: + // Sequence D: 11110000 + // modulation with subcarrier during first half + ToSend[ToSendMax] = 0xf0; + break; + case SEC_E: + // Sequence E: 00001111 + // modulation with subcarrier during second half + ToSend[ToSendMax] = 0x0f; + break; + case SEC_F: + // Sequence F: 00000000 + // no modulation with subcarrier + ToSend[ToSendMax] = 0x00; + break; + // READER TO CARD + case SEC_X: + // Sequence X: 00001100 + // drop after half a period + ToSend[ToSendMax] = 0x0c; + break; + case SEC_Y: + default: + // Sequence Y: 00000000 + // no drop + ToSend[ToSendMax] = 0x00; + break; + case SEC_Z: + // Sequence Z: 11000000 + // drop at start + ToSend[ToSendMax] = 0xc0; + break; + } +} + +//----------------------------------------------------------------------------- +// Prepare tag messages +//----------------------------------------------------------------------------- +static void CodeIso14443aAsTag(const BYTE *cmd, int len) +{ + int i; + int oddparity; + + ToSendReset(); + + // Correction bit, might be removed when not needed + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(1); // 1 + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + + // Send startbit + Sequence(SEC_D); + + for(i = 0; i < len; i++) { + int j; + BYTE b = cmd[i]; + + // Data bits + oddparity = 0x01; + for(j = 0; j < 8; j++) { + oddparity ^= (b & 1); + if(b & 1) { + Sequence(SEC_D); + } else { + Sequence(SEC_E); + } + b >>= 1; + } + + // Parity bit + if(oddparity) { + Sequence(SEC_D); + } else { + Sequence(SEC_E); + } + } + + // Send stopbit + Sequence(SEC_F); + + // Flush the buffer in FPGA!! + for(i = 0; i < 5; i++) { + Sequence(SEC_F); + } + + // Convert from last byte pos to length + ToSendMax++; + + // Add a few more for slop + ToSend[ToSendMax++] = 0x00; + ToSend[ToSendMax++] = 0x00; + //ToSendMax += 2; +} + +//----------------------------------------------------------------------------- +// This is to send a NACK kind of answer, its only 3 bits, I know it should be 4 +//----------------------------------------------------------------------------- +static void CodeStrangeAnswer() +{ + int i; + + ToSendReset(); + + // Correction bit, might be removed when not needed + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(1); // 1 + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + + // Send startbit + Sequence(SEC_D); + + // 0 + Sequence(SEC_E); + + // 0 + Sequence(SEC_E); + + // 1 + Sequence(SEC_D); + + // Send stopbit + Sequence(SEC_F); + + // Flush the buffer in FPGA!! + for(i = 0; i < 5; i++) { + Sequence(SEC_F); + } + + // Convert from last byte pos to length + ToSendMax++; + + // Add a few more for slop + ToSend[ToSendMax++] = 0x00; + ToSend[ToSendMax++] = 0x00; + //ToSendMax += 2; +} + +//----------------------------------------------------------------------------- +// Wait for commands from reader +// Stop when button is pressed +// Or return TRUE when command is captured +//----------------------------------------------------------------------------- +static BOOL GetIso14443aCommandFromReader(BYTE *received, int *len, int maxLen) +{ + // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen + // only, since we are receiving, not transmitting). + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); + + // Now run a `software UART' on the stream of incoming samples. + Uart.output = received; + Uart.byteCntMax = maxLen; + Uart.state = STATE_UNSYNCD; + + for(;;) { + WDT_HIT(); + + if(BUTTON_PRESS()) return FALSE; + + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = 0x00; + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + BYTE b = (BYTE)SSC_RECEIVE_HOLDING; + if(MillerDecoding((b & 0xf0) >> 4)) { + *len = Uart.byteCnt; + return TRUE; + } + if(MillerDecoding(b & 0x0f)) { + *len = Uart.byteCnt; + return TRUE; + } + } + } +} + +//----------------------------------------------------------------------------- +// Main loop of simulated tag: receive commands from reader, decide what +// response to send, and send it. +//----------------------------------------------------------------------------- +void SimulateIso14443aTag(int tagType, int TagUid) +{ + // This function contains the tag emulation + + // Prepare protocol messages + // static const BYTE cmd1[] = { 0x26 }; +// static const BYTE response1[] = { 0x02, 0x00 }; // Says: I am Mifare 4k - original line - greg +// + static const BYTE response1[] = { 0x44, 0x03 }; // Says: I am a DESFire Tag, ph33r me +// static const BYTE response1[] = { 0x44, 0x00 }; // Says: I am a ULTRALITE Tag, 0wn me + + // UID response + // static const BYTE cmd2[] = { 0x93, 0x20 }; + //static const BYTE response2[] = { 0x9a, 0xe5, 0xe4, 0x43, 0xd8 }; // original value - greg + + + +// my desfire + static const BYTE response2[] = { 0x88, 0x04, 0x21, 0x3f, 0x4d }; // known uid - note cascade (0x88), 2nd byte (0x04) = NXP/Phillips + + +// When reader selects us during cascade1 it will send cmd3 +//BYTE response3[] = { 0x04, 0x00, 0x00 }; // SAK Select (cascade1) successful response (ULTRALITE) +BYTE response3[] = { 0x24, 0x00, 0x00 }; // SAK Select (cascade1) successful response (DESFire) +ComputeCrc14443(CRC_14443_A, response3, 1, &response3[1], &response3[2]); + +// send cascade2 2nd half of UID +static const BYTE response2a[] = { 0x51, 0x48, 0x1d, 0x80, 0x84 }; // uid - cascade2 - 2nd half (4 bytes) of UID+ BCCheck +// NOTE : THE CRC on the above may be wrong as I have obfuscated the actual UID + + +// When reader selects us during cascade2 it will send cmd3a +//BYTE response3a[] = { 0x00, 0x00, 0x00 }; // SAK Select (cascade2) successful response (ULTRALITE) +BYTE response3a[] = { 0x20, 0x00, 0x00 }; // SAK Select (cascade2) successful response (DESFire) +ComputeCrc14443(CRC_14443_A, response3a, 1, &response3a[1], &response3a[2]); + +// When reader tries to authenticate + // static const BYTE cmd5[] = { 0x60, 0x00, 0xf5, 0x7b }; + static const BYTE response5[] = { 0x00, 0x00, 0x00, 0x00 }; // Very random tag nonce + + BYTE *resp; + int respLen; + + // Longest possible response will be 16 bytes + 2 CRC = 18 bytes + // This will need + // 144 data bits (18 * 8) + // 18 parity bits + // 2 Start and stop + // 1 Correction bit (Answer in 1172 or 1236 periods, see FPGA) + // 1 just for the case + // ----------- + + // 166 + // + // 166 bytes, since every bit that needs to be send costs us a byte + // + + + // Respond with card type + BYTE *resp1 = (((BYTE *)BigBuf) + 800); + int resp1Len; + + // Anticollision cascade1 - respond with uid + BYTE *resp2 = (((BYTE *)BigBuf) + 970); + int resp2Len; + + // Anticollision cascade2 - respond with 2nd half of uid if asked + // we're only going to be asked if we set the 1st byte of the UID (during cascade1) to 0x88 + BYTE *resp2a = (((BYTE *)BigBuf) + 1140); + int resp2aLen; + + // Acknowledge select - cascade 1 + BYTE *resp3 = (((BYTE *)BigBuf) + 1310); + int resp3Len; + + // Acknowledge select - cascade 2 + BYTE *resp3a = (((BYTE *)BigBuf) + 1480); + int resp3aLen; + + // Response to a read request - not implemented atm + BYTE *resp4 = (((BYTE *)BigBuf) + 1550); + int resp4Len; + + // Authenticate response - nonce + BYTE *resp5 = (((BYTE *)BigBuf) + 1720); + int resp5Len; + + BYTE *receivedCmd = (BYTE *)BigBuf; + int len; + + int i; + int u; + BYTE b; + + // To control where we are in the protocol + int order = 0; + int lastorder; + + // Just to allow some checks + int happened = 0; + int happened2 = 0; + + int cmdsRecvd = 0; + + BOOL fdt_indicator; + + memset(receivedCmd, 0x44, 400); + + // Prepare the responses of the anticollision phase + // there will be not enough time to do this at the moment the reader sends it REQA + + // Answer to request + CodeIso14443aAsTag(response1, sizeof(response1)); + memcpy(resp1, ToSend, ToSendMax); resp1Len = ToSendMax; + + // Send our UID (cascade 1) + CodeIso14443aAsTag(response2, sizeof(response2)); + memcpy(resp2, ToSend, ToSendMax); resp2Len = ToSendMax; + + // Answer to select (cascade1) + CodeIso14443aAsTag(response3, sizeof(response3)); + memcpy(resp3, ToSend, ToSendMax); resp3Len = ToSendMax; + + // Send the cascade 2 2nd part of the uid + CodeIso14443aAsTag(response2a, sizeof(response2a)); + memcpy(resp2a, ToSend, ToSendMax); resp2aLen = ToSendMax; + + // Answer to select (cascade 2) + CodeIso14443aAsTag(response3a, sizeof(response3a)); + memcpy(resp3a, ToSend, ToSendMax); resp3aLen = ToSendMax; + + // Strange answer is an example of rare message size (3 bits) + CodeStrangeAnswer(); + memcpy(resp4, ToSend, ToSendMax); resp4Len = ToSendMax; + + // Authentication answer (random nonce) + CodeIso14443aAsTag(response5, sizeof(response5)); + memcpy(resp5, ToSend, ToSendMax); resp5Len = ToSendMax; + + // We need to listen to the high-frequency, peak-detected path. + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaSetupSsc(); + + cmdsRecvd = 0; + + LED_A_ON(); + for(;;) { + + if(!GetIso14443aCommandFromReader(receivedCmd, &len, 100)) { + DbpString("button press"); + break; + } + // doob - added loads of debug strings so we can see what the reader is saying to us during the sim as hi14alist is not populated + // Okay, look at the command now. + lastorder = order; + i = 1; // first byte transmitted + if(receivedCmd[0] == 0x26) { + // Received a REQUEST + resp = resp1; respLen = resp1Len; order = 1; + //DbpString("Hello request from reader:"); + } else if(receivedCmd[0] == 0x52) { + // Received a WAKEUP + resp = resp1; respLen = resp1Len; order = 6; +// //DbpString("Wakeup request from reader:"); + + } else if(receivedCmd[1] == 0x20 && receivedCmd[0] == 0x93) { // greg - cascade 1 anti-collision + // Received request for UID (cascade 1) + resp = resp2; respLen = resp2Len; order = 2; +// DbpString("UID (cascade 1) request from reader:"); +// DbpIntegers(receivedCmd[0], receivedCmd[1], receivedCmd[2]); + + + } else if(receivedCmd[1] == 0x20 && receivedCmd[0] ==0x95) { // greg - cascade 2 anti-collision + // Received request for UID (cascade 2) + resp = resp2a; respLen = resp2aLen; order = 20; +// DbpString("UID (cascade 2) request from reader:"); +// DbpIntegers(receivedCmd[0], receivedCmd[1], receivedCmd[2]); + + + } else if(receivedCmd[1] == 0x70 && receivedCmd[0] ==0x93) { // greg - cascade 1 select + // Received a SELECT + resp = resp3; respLen = resp3Len; order = 3; +// DbpString("Select (cascade 1) request from reader:"); +// DbpIntegers(receivedCmd[0], receivedCmd[1], receivedCmd[2]); + + + } else if(receivedCmd[1] == 0x70 && receivedCmd[0] ==0x95) { // greg - cascade 2 select + // Received a SELECT + resp = resp3a; respLen = resp3aLen; order = 30; +// DbpString("Select (cascade 2) request from reader:"); +// DbpIntegers(receivedCmd[0], receivedCmd[1], receivedCmd[2]); + + + } else if(receivedCmd[0] == 0x30) { + // Received a READ + resp = resp4; respLen = resp4Len; order = 4; // Do nothing + DbpString("Read request from reader:"); + DbpIntegers(receivedCmd[0], receivedCmd[1], receivedCmd[2]); + + + } else if(receivedCmd[0] == 0x50) { + // Received a HALT + resp = resp1; respLen = 0; order = 5; // Do nothing + DbpString("Reader requested we HALT!:"); + + } else if(receivedCmd[0] == 0x60) { + // Received an authentication request + resp = resp5; respLen = resp5Len; order = 7; + DbpString("Authenticate request from reader:"); + DbpIntegers(receivedCmd[0], receivedCmd[1], receivedCmd[2]); + + } else if(receivedCmd[0] == 0xE0) { + // Received a RATS request + resp = resp1; respLen = 0;order = 70; + DbpString("RATS request from reader:"); + DbpIntegers(receivedCmd[0], receivedCmd[1], receivedCmd[2]); + } else { + // Never seen this command before + DbpString("Unknown command received from reader:"); + DbpIntegers(receivedCmd[0], receivedCmd[1], receivedCmd[2]); + DbpIntegers(receivedCmd[3], receivedCmd[4], receivedCmd[5]); + DbpIntegers(receivedCmd[6], receivedCmd[7], receivedCmd[8]); + + // Do not respond + resp = resp1; respLen = 0; order = 0; + } + + // Count number of wakeups received after a halt + if(order == 6 && lastorder == 5) { happened++; } + + // Count number of other messages after a halt + if(order != 6 && lastorder == 5) { happened2++; } + + // Look at last parity bit to determine timing of answer + if((Uart.parityBits & 0x01) || receivedCmd[0] == 0x52) { + // 1236, so correction bit needed + i = 0; + } + + memset(receivedCmd, 0x44, 32); + + if(cmdsRecvd > 999) { + DbpString("1000 commands later..."); + break; + } + else { + cmdsRecvd++; + } + + if(respLen <= 0) continue; + + // Modulate Manchester + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_MOD); + SSC_TRANSMIT_HOLDING = 0x00; + FpgaSetupSsc(); + + // ### Transmit the response ### + u = 0; + b = 0x00; + fdt_indicator = FALSE; + for(;;) { + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + volatile BYTE b = (BYTE)SSC_RECEIVE_HOLDING; + (void)b; + } + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + if(i > respLen) { + b = 0x00; + u++; + } else { + b = resp[i]; + i++; + } + SSC_TRANSMIT_HOLDING = b; + + if(u > 4) { + break; + } + } + if(BUTTON_PRESS()) { + break; + } + } + + } + + DbpIntegers(happened, happened2, cmdsRecvd); + LED_A_OFF(); +} + +//----------------------------------------------------------------------------- +// Transmit the command (to the tag) that was placed in ToSend[]. +//----------------------------------------------------------------------------- +static void TransmitFor14443a(const BYTE *cmd, int len, int *samples, int *wait) +{ + int c; + + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + + if(*wait < 10) { *wait = 10; } + + for(c = 0; c < *wait;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = 0x00; // For exact timing! + c++; + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + volatile DWORD r = SSC_RECEIVE_HOLDING; + (void)r; + } + WDT_HIT(); + } + + c = 0; + for(;;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = cmd[c]; + c++; + if(c >= len) { + break; + } + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + volatile DWORD r = SSC_RECEIVE_HOLDING; + (void)r; + } + WDT_HIT(); + } + *samples = (c + *wait) << 3; +} + +//----------------------------------------------------------------------------- +// To generate an arbitrary stream from reader +// +//----------------------------------------------------------------------------- +void ArbitraryFromReader(const BYTE *cmd, int parity, int len) +{ + int i; + int j; + int last; + BYTE b; + + ToSendReset(); + + // Start of Communication (Seq. Z) + Sequence(SEC_Z); + last = 0; + + for(i = 0; i < len; i++) { + // Data bits + b = cmd[i]; + for(j = 0; j < 8; j++) { + if(b & 1) { + // Sequence X + Sequence(SEC_X); + last = 1; + } else { + if(last == 0) { + // Sequence Z + Sequence(SEC_Z); + } + else { + // Sequence Y + Sequence(SEC_Y); + last = 0; + } + } + b >>= 1; + + } + + // Predefined parity bit, the flipper flips when needed, because of flips in byte sent + if(((parity >> (len - i - 1)) & 1)) { + // Sequence X + Sequence(SEC_X); + last = 1; + } else { + if(last == 0) { + // Sequence Z + Sequence(SEC_Z); + } + else { + // Sequence Y + Sequence(SEC_Y); + last = 0; + } + } + } + + // End of Communication + if(last == 0) { + // Sequence Z + Sequence(SEC_Z); + } + else { + // Sequence Y + Sequence(SEC_Y); + last = 0; + } + // Sequence Y + Sequence(SEC_Y); + + // Just to be sure! + Sequence(SEC_Y); + Sequence(SEC_Y); + Sequence(SEC_Y); + + // Convert from last character reference to length + ToSendMax++; +} + +//----------------------------------------------------------------------------- +// Code a 7-bit command without parity bit +// This is especially for 0x26 and 0x52 (REQA and WUPA) +//----------------------------------------------------------------------------- +void ShortFrameFromReader(const BYTE *cmd) +{ + int j; + int last; + BYTE b; + + ToSendReset(); + + // Start of Communication (Seq. Z) + Sequence(SEC_Z); + last = 0; + + b = cmd[0]; + for(j = 0; j < 7; j++) { + if(b & 1) { + // Sequence X + Sequence(SEC_X); + last = 1; + } else { + if(last == 0) { + // Sequence Z + Sequence(SEC_Z); + } + else { + // Sequence Y + Sequence(SEC_Y); + last = 0; + } + } + b >>= 1; + } + + // End of Communication + if(last == 0) { + // Sequence Z + Sequence(SEC_Z); + } + else { + // Sequence Y + Sequence(SEC_Y); + last = 0; + } + // Sequence Y + Sequence(SEC_Y); + + // Just to be sure! + Sequence(SEC_Y); + Sequence(SEC_Y); + Sequence(SEC_Y); + + // Convert from last character reference to length + ToSendMax++; +} + +//----------------------------------------------------------------------------- +// Prepare reader command to send to FPGA +// +//----------------------------------------------------------------------------- +void CodeIso14443aAsReader(const BYTE *cmd, int len) +{ + int i, j; + int last; + int oddparity; + BYTE b; + + ToSendReset(); + + // Start of Communication (Seq. Z) + Sequence(SEC_Z); + last = 0; + + for(i = 0; i < len; i++) { + // Data bits + b = cmd[i]; + oddparity = 0x01; + for(j = 0; j < 8; j++) { + oddparity ^= (b & 1); + if(b & 1) { + // Sequence X + Sequence(SEC_X); + last = 1; + } else { + if(last == 0) { + // Sequence Z + Sequence(SEC_Z); + } + else { + // Sequence Y + Sequence(SEC_Y); + last = 0; + } + } + b >>= 1; + } + + // Parity bit + if(oddparity) { + // Sequence X + Sequence(SEC_X); + last = 1; + } else { + if(last == 0) { + // Sequence Z + Sequence(SEC_Z); + } + else { + // Sequence Y + Sequence(SEC_Y); + last = 0; + } + } + } + + // End of Communication + if(last == 0) { + // Sequence Z + Sequence(SEC_Z); + } + else { + // Sequence Y + Sequence(SEC_Y); + last = 0; + } + // Sequence Y + Sequence(SEC_Y); + + // Just to be sure! + Sequence(SEC_Y); + Sequence(SEC_Y); + Sequence(SEC_Y); + + // Convert from last character reference to length + ToSendMax++; +} + + +//----------------------------------------------------------------------------- +// Wait a certain time for tag response +// If a response is captured return TRUE +// If it takes to long return FALSE +//----------------------------------------------------------------------------- +static BOOL GetIso14443aAnswerFromTag(BYTE *receivedResponse, int maxLen, int *samples, int *elapsed) //BYTE *buffer +{ + // buffer needs to be 512 bytes + int c; + + // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen + // only, since we are receiving, not transmitting). + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_LISTEN); + + // Now get the answer from the card + Demod.output = receivedResponse; + Demod.len = 0; + Demod.state = DEMOD_UNSYNCD; + + BYTE b; + *elapsed = 0; + + c = 0; + for(;;) { + WDT_HIT(); + + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = 0x00; // To make use of exact timing of next command from reader!! + (*elapsed)++; + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + if(c < 512) { c++; } else { return FALSE; } + b = (BYTE)SSC_RECEIVE_HOLDING; + if(ManchesterDecoding((b & 0xf0) >> 4)) { + *samples = ((c - 1) << 3) + 4; + return TRUE; + } + if(ManchesterDecoding(b & 0x0f)) { + *samples = c << 3; + return TRUE; + } + } + } +} + +//----------------------------------------------------------------------------- +// Read an ISO 14443a tag. Send out commands and store answers. +// +//----------------------------------------------------------------------------- +void ReaderIso14443a(DWORD parameter) +{ + // Anticollision + static const BYTE cmd1[] = { 0x52 }; // or 0x26 + static const BYTE cmd2[] = { 0x93,0x20 }; + // UID = 0x2a,0x69,0x8d,0x43,0x8d, last two bytes are CRC bytes + BYTE cmd3[] = { 0x93,0x70,0x2a,0x69,0x8d,0x43,0x8d,0x52,0x55 }; + + // For Ultralight add an extra anticollission layer -> 95 20 and then 95 70 + + // greg - here we will add our cascade level 2 anticolission and select functions to deal with ultralight // and 7-byte UIDs in generall... + BYTE cmd4[] = {0x95,0x20}; // ask for cascade 2 select + // 95 20 + //BYTE cmd3a[] = { 0x95,0x70,0x2a,0x69,0x8d,0x43,0x8d,0x52,0x55 }; + // 95 70 + + // cascade 2 select + BYTE cmd5[] = { 0x95,0x70,0x2a,0x69,0x8d,0x43,0x8d,0x52,0x55 }; + + + // RATS (request for answer to select) + //BYTE cmd6[] = { 0xe0,0x50,0xbc,0xa5 }; // original RATS + BYTE cmd6[] = { 0xe0,0x21,0xb2,0xc7 }; // Desfire RATS + + int reqaddr = 2024; // was 2024 - tied to other size changes + int reqsize = 60; + + BYTE *req1 = (((BYTE *)BigBuf) + reqaddr); + int req1Len; + + BYTE *req2 = (((BYTE *)BigBuf) + reqaddr + reqsize); + int req2Len; + + BYTE *req3 = (((BYTE *)BigBuf) + reqaddr + (reqsize * 2)); + int req3Len; + +// greg added req 4 & 5 to deal with cascade 2 section + BYTE *req4 = (((BYTE *)BigBuf) + reqaddr + (reqsize * 3)); + int req4Len; + + BYTE *req5 = (((BYTE *)BigBuf) + reqaddr + (reqsize * 4)); + int req5Len; + + BYTE *req6 = (((BYTE *)BigBuf) + reqaddr + (reqsize * 5)); + int req6Len; + + //BYTE *req7 = (((BYTE *)BigBuf) + reqaddr + (reqsize * 6)); + //int req7Len; + + BYTE *receivedAnswer = (((BYTE *)BigBuf) + 3560); // was 3560 - tied to other size changes + + BYTE *trace = (BYTE *)BigBuf; + int traceLen = 0; + int rsamples = 0; + + memset(trace, 0x44, 2000); // was 2000 - tied to oter size chnages + // setting it to 3000 causes no tag responses to be detected (2900 is ok) + // setting it to 1000 causes no tag responses to be detected + + // Prepare some commands! + ShortFrameFromReader(cmd1); + memcpy(req1, ToSend, ToSendMax); req1Len = ToSendMax; + + CodeIso14443aAsReader(cmd2, sizeof(cmd2)); + memcpy(req2, ToSend, ToSendMax); req2Len = ToSendMax; + + CodeIso14443aAsReader(cmd3, sizeof(cmd3)); + memcpy(req3, ToSend, ToSendMax); req3Len = ToSendMax; + + + CodeIso14443aAsReader(cmd4, sizeof(cmd4)); // 4 is cascade 2 request + memcpy(req4, ToSend, ToSendMax); req4Len = ToSendMax; + + + CodeIso14443aAsReader(cmd5, sizeof(cmd5)); // 5 is cascade 2 select + memcpy(req5, ToSend, ToSendMax); req5Len = ToSendMax; + + + CodeIso14443aAsReader(cmd6, sizeof(cmd6)); + memcpy(req6, ToSend, ToSendMax); req6Len = ToSendMax; + + // Setup SSC + FpgaSetupSsc(); + + // Start from off (no field generated) + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(200); + + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaSetupSsc(); + + // Now give it time to spin up. + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + SpinDelay(200); + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + + int samples = 0; + int tsamples = 0; + int wait = 0; + int elapsed = 0; + + for(;;) { + // Send WUPA (or REQA) + TransmitFor14443a(req1, req1Len, &tsamples, &wait); + // Store answer in buffer + trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; + trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; + trace[traceLen++] = 1; + memcpy(trace+traceLen, cmd1, 1); + traceLen += 1; + if(traceLen > TRACE_LENGTH) goto done; + + while(!GetIso14443aAnswerFromTag(receivedAnswer, 100, &samples, &elapsed)) { + if(BUTTON_PRESS()) goto done; + + // No answer, just continue polling + TransmitFor14443a(req1, req1Len, &tsamples, &wait); + // Store answer in buffer + trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; + trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; + trace[traceLen++] = 1; + memcpy(trace+traceLen, cmd1, 1); + traceLen += 1; + if(traceLen > TRACE_LENGTH) goto done; + } + + // Store answer in buffer + rsamples = rsamples + (samples - Demod.samples); + trace[traceLen++] = ((rsamples >> 0) & 0xff); + trace[traceLen++] = ((rsamples >> 8) & 0xff); + trace[traceLen++] = ((rsamples >> 16) & 0xff); + trace[traceLen++] = 0x80 | ((rsamples >> 24) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 0) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 8) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 16) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 24) & 0xff); + trace[traceLen++] = Demod.len; + memcpy(trace+traceLen, receivedAnswer, Demod.len); + traceLen += Demod.len; + if(traceLen > TRACE_LENGTH) goto done; + + // Ask for card UID + TransmitFor14443a(req2, req2Len, &tsamples, &wait); + // Store answer in buffer + trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; + trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; + trace[traceLen++] = 2; + memcpy(trace+traceLen, cmd2, 2); + traceLen += 2; + if(traceLen > TRACE_LENGTH) goto done; + + if(!GetIso14443aAnswerFromTag(receivedAnswer, 100, &samples, &elapsed)) { + continue; + } + + // Store answer in buffer + rsamples = rsamples + (samples - Demod.samples); + trace[traceLen++] = ((rsamples >> 0) & 0xff); + trace[traceLen++] = ((rsamples >> 8) & 0xff); + trace[traceLen++] = ((rsamples >> 16) & 0xff); + trace[traceLen++] = 0x80 | ((rsamples >> 24) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 0) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 8) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 16) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 24) & 0xff); + trace[traceLen++] = Demod.len; + memcpy(trace+traceLen, receivedAnswer, Demod.len); + traceLen += Demod.len; + if(traceLen > TRACE_LENGTH) goto done; + + // Construct SELECT UID command + // First copy the 5 bytes (Mifare Classic) after the 93 70 + memcpy(cmd3+2,receivedAnswer,5); + // Secondly compute the two CRC bytes at the end + ComputeCrc14443(CRC_14443_A, cmd3, 7, &cmd3[7], &cmd3[8]); + // Prepare the bit sequence to modulate the subcarrier + // Store answer in buffer + trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; + trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; + trace[traceLen++] = 9; + memcpy(trace+traceLen, cmd3, 9); + traceLen += 9; + if(traceLen > TRACE_LENGTH) goto done; + CodeIso14443aAsReader(cmd3, sizeof(cmd3)); + memcpy(req3, ToSend, ToSendMax); req3Len = ToSendMax; + + // Select the card + TransmitFor14443a(req3, req3Len, &samples, &wait); + if(!GetIso14443aAnswerFromTag(receivedAnswer, 100, &samples, &elapsed)) { + continue; + } + + // Store answer in buffer + rsamples = rsamples + (samples - Demod.samples); + trace[traceLen++] = ((rsamples >> 0) & 0xff); + trace[traceLen++] = ((rsamples >> 8) & 0xff); + trace[traceLen++] = ((rsamples >> 16) & 0xff); + trace[traceLen++] = 0x80 | ((rsamples >> 24) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 0) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 8) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 16) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 24) & 0xff); + trace[traceLen++] = Demod.len; + memcpy(trace+traceLen, receivedAnswer, Demod.len); + traceLen += Demod.len; + if(traceLen > TRACE_LENGTH) goto done; + +// OK we have selected at least at cascade 1, lets see if first byte of UID was 0x88 in +// which case we need to make a cascade 2 request and select - this is a long UID + if (receivedAnswer[0] = 0x88) + { + // Do cascade level 2 stuff + /////////////////////////////////////////////////////////////////// + // First issue a '95 20' identify request + // Ask for card UID (part 2) + TransmitFor14443a(req4, req4Len, &tsamples, &wait); + // Store answer in buffer + trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; + trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; + trace[traceLen++] = 2; + memcpy(trace+traceLen, cmd4, 2); + traceLen += 2; + if(traceLen > TRACE_LENGTH) { + DbpString("Bugging out, just popped tracelength"); + goto done;} + + if(!GetIso14443aAnswerFromTag(receivedAnswer, 100, &samples, &elapsed)) { + continue; + } + // Store answer in buffer + rsamples = rsamples + (samples - Demod.samples); + trace[traceLen++] = ((rsamples >> 0) & 0xff); + trace[traceLen++] = ((rsamples >> 8) & 0xff); + trace[traceLen++] = ((rsamples >> 16) & 0xff); + trace[traceLen++] = 0x80 | ((rsamples >> 24) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 0) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 8) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 16) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 24) & 0xff); + trace[traceLen++] = Demod.len; + memcpy(trace+traceLen, receivedAnswer, Demod.len); + traceLen += Demod.len; + if(traceLen > TRACE_LENGTH) goto done; + ////////////////////////////////////////////////////////////////// + // Then Construct SELECT UID (cascasde 2) command + DbpString("Just about to copy the UID out of the cascade 2 id req"); + // First copy the 5 bytes (Mifare Classic) after the 95 70 + memcpy(cmd5+2,receivedAnswer,5); + // Secondly compute the two CRC bytes at the end + ComputeCrc14443(CRC_14443_A, cmd4, 7, &cmd5[7], &cmd5[8]); + // Prepare the bit sequence to modulate the subcarrier + // Store answer in buffer + trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; + trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; + trace[traceLen++] = 9; + memcpy(trace+traceLen, cmd5, 9); + traceLen += 9; + if(traceLen > TRACE_LENGTH) goto done; + CodeIso14443aAsReader(cmd5, sizeof(cmd5)); + memcpy(req5, ToSend, ToSendMax); req5Len = ToSendMax; + + // Select the card + TransmitFor14443a(req4, req4Len, &samples, &wait); + if(!GetIso14443aAnswerFromTag(receivedAnswer, 100, &samples, &elapsed)) { + continue; + } + + // Store answer in buffer + rsamples = rsamples + (samples - Demod.samples); + trace[traceLen++] = ((rsamples >> 0) & 0xff); + trace[traceLen++] = ((rsamples >> 8) & 0xff); + trace[traceLen++] = ((rsamples >> 16) & 0xff); + trace[traceLen++] = 0x80 | ((rsamples >> 24) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 0) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 8) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 16) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 24) & 0xff); + trace[traceLen++] = Demod.len; + memcpy(trace+traceLen, receivedAnswer, Demod.len); + traceLen += Demod.len; + if(traceLen > TRACE_LENGTH) goto done; + + + + + + + } + + + + // Secondly compute the two CRC bytes at the end + ComputeCrc14443(CRC_14443_A, cmd5, 2, &cmd5[2], &cmd5[3]); + // Send authentication request (Mifare Classic) + TransmitFor14443a(req5, req5Len, &samples, &wait); + trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; + trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; trace[traceLen++] = 0; + trace[traceLen++] = 4; + memcpy(trace+traceLen, cmd5, 4); + traceLen += 4; + if(traceLen > TRACE_LENGTH) goto done; + if(GetIso14443aAnswerFromTag(receivedAnswer, 100, &samples, &elapsed)) { + rsamples++; + // We received probably a random, continue and trace! + } + else { + // Received nothing + continue; + } + + // Trace the random, i'm curious + rsamples = rsamples + (samples - Demod.samples); + trace[traceLen++] = ((rsamples >> 0) & 0xff); + trace[traceLen++] = ((rsamples >> 8) & 0xff); + trace[traceLen++] = ((rsamples >> 16) & 0xff); + trace[traceLen++] = 0x80 | ((rsamples >> 24) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 0) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 8) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 16) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 24) & 0xff); + trace[traceLen++] = Demod.len; + memcpy(trace+traceLen, receivedAnswer, Demod.len); + traceLen += Demod.len; + if(traceLen > TRACE_LENGTH) goto done; + + // Thats it... + } + +done: + LED_A_OFF(); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + DbpIntegers(rsamples, 0xCC, 0xCC); + DbpString("ready.."); +} diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c new file mode 100644 index 00000000..2592cbfe --- /dev/null +++ b/armsrc/iso15693.c @@ -0,0 +1,1226 @@ +//----------------------------------------------------------------------------- +// Routines to support ISO 15693. This includes both the reader software and +// the `fake tag' modes, but at the moment I've implemented only the reader +// stuff, and that barely. +// Jonathan Westhues, split Nov 2006 + +// Modified by Greg Jones, Jan 2009 to perform modulation onboard in arm rather than on PC +// Also added additional reader commands (SELECT, READ etc.) + +//----------------------------------------------------------------------------- +#include +#include "apps.h" +#include +#include + +// FROM winsrc\prox.h ////////////////////////////////// +#define arraylen(x) (sizeof(x)/sizeof((x)[0])) + +//----------------------------------------------------------------------------- +// Map a sequence of octets (~layer 2 command) into the set of bits to feed +// to the FPGA, to transmit that command to the tag. +//----------------------------------------------------------------------------- + + + + + // The sampling rate is 106.353 ksps/s, for T = 18.8 us + + // SOF defined as + // 1) Unmodulated time of 56.64us + // 2) 24 pulses of 423.75khz + // 3) logic '1' (unmodulated for 18.88us followed by 8 pulses of 423.75khz) + + static const int FrameSOF[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + -1, -1, -1, -1, + -1, -1, -1, -1, + 1, 1, 1, 1, + 1, 1, 1, 1 + }; + static const int Logic0[] = { + 1, 1, 1, 1, + 1, 1, 1, 1, + -1, -1, -1, -1, + -1, -1, -1, -1 + }; + static const int Logic1[] = { + -1, -1, -1, -1, + -1, -1, -1, -1, + 1, 1, 1, 1, + 1, 1, 1, 1 + }; + + // EOF defined as + // 1) logic '0' (8 pulses of 423.75khz followed by unmodulated for 18.88us) + // 2) 24 pulses of 423.75khz + // 3) Unmodulated time of 56.64us + + static const int FrameEOF[] = { + 1, 1, 1, 1, + 1, 1, 1, 1, + -1, -1, -1, -1, + -1, -1, -1, -1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + + + +static void CodeIso15693AsReader(BYTE *cmd, int n) +{ + int i, j; + + ToSendReset(); + + // Give it a bit of slack at the beginning + for(i = 0; i < 24; i++) { + ToSendStuffBit(1); + } + + ToSendStuffBit(0); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(0); + ToSendStuffBit(1); + ToSendStuffBit(1); + for(i = 0; i < n; i++) { + for(j = 0; j < 8; j += 2) { + int these = (cmd[i] >> j) & 3; + switch(these) { + case 0: + ToSendStuffBit(1); + ToSendStuffBit(0); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + break; + case 1: + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(0); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + break; + case 2: + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(0); + ToSendStuffBit(1); + ToSendStuffBit(1); + break; + case 3: + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(0); + break; + } + } + } + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(0); + ToSendStuffBit(1); + + // And slack at the end, too. + for(i = 0; i < 24; i++) { + ToSendStuffBit(1); + } +} + +//----------------------------------------------------------------------------- +// The CRC used by ISO 15693. +//----------------------------------------------------------------------------- +static WORD Crc(BYTE *v, int n) +{ + DWORD reg; + int i, j; + + reg = 0xffff; + for(i = 0; i < n; i++) { + reg = reg ^ ((DWORD)v[i]); + for (j = 0; j < 8; j++) { + if (reg & 0x0001) { + reg = (reg >> 1) ^ 0x8408; + } else { + reg = (reg >> 1); + } + } + } + + return ~reg; +} + +////////////////////////////////////////// code to do 'itoa' + + + +/* reverse: reverse string s in place */ +void reverse(char s[]) +{ + int c, i, j; + + for (i = 0, j = strlen(s)-1; i 0); /* delete it */ + if (sign < 0) + s[i++] = '-'; + s[i] = '\0'; + reverse(s); +} + +//////////////////////////////////////// END 'itoa' CODE + + +//----------------------------------------------------------------------------- +// Encode (into the ToSend buffers) an identify request, which is the first +// thing that you must send to a tag to get a response. +//----------------------------------------------------------------------------- +static void BuildIdentifyRequest(void) +{ + BYTE cmd[5]; + + WORD crc; + // one sub-carrier, inventory, 1 slot, fast rate + // AFI is at bit 5 (1<<4) when doing an INVENTORY + cmd[0] = (1 << 2) | (1 << 5) | (1 << 1); + // inventory command code + cmd[1] = 0x01; + // no mask + cmd[2] = 0x00; + //Now the CRC + crc = Crc(cmd, 3); + cmd[3] = crc & 0xff; + cmd[4] = crc >> 8; + + CodeIso15693AsReader(cmd, sizeof(cmd)); +} + +static void BuildSysInfoRequest(BYTE *uid) +{ + BYTE cmd[12]; + + WORD crc; + // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block + // followed by teh block data + // one sub-carrier, inventory, 1 slot, fast rate + cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit + // System Information command code + cmd[1] = 0x2B; + // UID may be optionally specified here + // 64-bit UID + cmd[2] = 0x32; + cmd[3]= 0x4b; + cmd[4] = 0x03; + cmd[5] = 0x01; + cmd[6] = 0x00; + cmd[7] = 0x10; + cmd[8] = 0x05; + cmd[9]= 0xe0; // always e0 (not exactly unique) + //Now the CRC + crc = Crc(cmd, 10); // the crc needs to be calculated over 2 bytes + cmd[10] = crc & 0xff; + cmd[11] = crc >> 8; + + CodeIso15693AsReader(cmd, sizeof(cmd)); +} + +static void BuildSelectRequest( BYTE uid[]) +{ + +// uid[6]=0x31; // this is getting ignored - the uid array is not happening... + BYTE cmd[12]; + + WORD crc; + // one sub-carrier, inventory, 1 slot, fast rate + //cmd[0] = (1 << 2) | (1 << 5) | (1 << 1); // INVENTROY FLAGS + cmd[0] = (1 << 4) | (1 << 5) | (1 << 1); // Select and addressed FLAGS + // SELECT command code + cmd[1] = 0x25; + // 64-bit UID +// cmd[2] = uid[0];//0x32; +// cmd[3]= uid[1];//0x4b; +// cmd[4] = uid[2];//0x03; +// cmd[5] = uid[3];//0x01; +// cmd[6] = uid[4];//0x00; +// cmd[7] = uid[5];//0x10; +// cmd[8] = uid[6];//0x05; + cmd[2] = 0x32;// + cmd[3]= 0x4b; + cmd[4] = 0x03; + cmd[5] = 0x01; + cmd[6] = 0x00; + cmd[7] = 0x10; + cmd[8] = 0x05; // infineon? + + cmd[9]= 0xe0; // always e0 (not exactly unique) + +// DbpIntegers(cmd[8],cmd[7],cmd[6]); + // Now the CRC + crc = Crc(cmd, 10); // the crc needs to be calculated over 10 bytes + cmd[10] = crc & 0xff; + cmd[11] = crc >> 8; + + CodeIso15693AsReader(cmd, sizeof(cmd)); +} + +static void BuildReadBlockRequest(BYTE *uid, BYTE blockNumber ) +{ + BYTE cmd[13]; + + WORD crc; + // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block + // followed by teh block data + // one sub-carrier, inventory, 1 slot, fast rate + cmd[0] = (1 << 6)| (1 << 5) | (1 << 1); // no SELECT bit + // READ BLOCK command code + cmd[1] = 0x20; + // UID may be optionally specified here + // 64-bit UID + cmd[2] = 0x32; + cmd[3]= 0x4b; + cmd[4] = 0x03; + cmd[5] = 0x01; + cmd[6] = 0x00; + cmd[7] = 0x10; + cmd[8] = 0x05; + cmd[9]= 0xe0; // always e0 (not exactly unique) + // Block number to read + cmd[10] = blockNumber;//0x00; + //Now the CRC + crc = Crc(cmd, 11); // the crc needs to be calculated over 2 bytes + cmd[11] = crc & 0xff; + cmd[12] = crc >> 8; + + CodeIso15693AsReader(cmd, sizeof(cmd)); +} + + +static void BuildReadMultiBlockRequest(BYTE *uid) +{ + BYTE cmd[14]; + + WORD crc; + // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block + // followed by teh block data + // one sub-carrier, inventory, 1 slot, fast rate + cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit + // READ Multi BLOCK command code + cmd[1] = 0x23; + // UID may be optionally specified here + // 64-bit UID + cmd[2] = 0x32; + cmd[3]= 0x4b; + cmd[4] = 0x03; + cmd[5] = 0x01; + cmd[6] = 0x00; + cmd[7] = 0x10; + cmd[8] = 0x05; + cmd[9]= 0xe0; // always e0 (not exactly unique) + // First Block number to read + cmd[10] = 0x00; + // Number of Blocks to read + cmd[11] = 0x2f; // read quite a few + //Now the CRC + crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes + cmd[12] = crc & 0xff; + cmd[13] = crc >> 8; + + CodeIso15693AsReader(cmd, sizeof(cmd)); +} + +static void BuildArbitraryRequest(BYTE *uid,BYTE CmdCode) +{ + BYTE cmd[14]; + + WORD crc; + // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block + // followed by teh block data + // one sub-carrier, inventory, 1 slot, fast rate + cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit + // READ BLOCK command code + cmd[1] = CmdCode; + // UID may be optionally specified here + // 64-bit UID + cmd[2] = 0x32; + cmd[3]= 0x4b; + cmd[4] = 0x03; + cmd[5] = 0x01; + cmd[6] = 0x00; + cmd[7] = 0x10; + cmd[8] = 0x05; + cmd[9]= 0xe0; // always e0 (not exactly unique) + // Parameter + cmd[10] = 0x00; + cmd[11] = 0x0a; + +// cmd[12] = 0x00; +// cmd[13] = 0x00; //Now the CRC + crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes + cmd[12] = crc & 0xff; + cmd[13] = crc >> 8; + + CodeIso15693AsReader(cmd, sizeof(cmd)); +} + +static void BuildArbitraryCustomRequest(BYTE *uid,BYTE CmdCode) +{ + BYTE cmd[14]; + + WORD crc; + // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block + // followed by teh block data + // one sub-carrier, inventory, 1 slot, fast rate + cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit + // READ BLOCK command code + cmd[1] = CmdCode; + // UID may be optionally specified here + // 64-bit UID + cmd[2] = 0x32; + cmd[3]= 0x4b; + cmd[4] = 0x03; + cmd[5] = 0x01; + cmd[6] = 0x00; + cmd[7] = 0x10; + cmd[8] = 0x05; + cmd[9]= 0xe0; // always e0 (not exactly unique) + // Parameter + cmd[10] = 0x05; // for custom codes this must be manufcturer code + cmd[11] = 0x00; + +// cmd[12] = 0x00; +// cmd[13] = 0x00; //Now the CRC + crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes + cmd[12] = crc & 0xff; + cmd[13] = crc >> 8; + + CodeIso15693AsReader(cmd, sizeof(cmd)); +} + +///////////////////////////////////////////////////////////////////////// +// Now the VICC>VCD responses when we are simulating a tag +//////////////////////////////////////////////////////////////////// + + static void BuildInventoryResponse(void) +{ + BYTE cmd[12]; + + WORD crc; + // one sub-carrier, inventory, 1 slot, fast rate + // AFI is at bit 5 (1<<4) when doing an INVENTORY + cmd[0] = 0; //(1 << 2) | (1 << 5) | (1 << 1); + cmd[1] = 0; + // 64-bit UID + cmd[2] = 0x32; + cmd[3]= 0x4b; + cmd[4] = 0x03; + cmd[5] = 0x01; + cmd[6] = 0x00; + cmd[7] = 0x10; + cmd[8] = 0x05; + cmd[9]= 0xe0; + //Now the CRC + crc = Crc(cmd, 10); + cmd[10] = crc & 0xff; + cmd[11] = crc >> 8; + + CodeIso15693AsReader(cmd, sizeof(cmd)); +} + + +//----------------------------------------------------------------------------- +// Transmit the command (to the tag) that was placed in ToSend[]. +//----------------------------------------------------------------------------- +static void TransmitTo15693Tag(const BYTE *cmd, int len, int *samples, int *wait) +{ + int c; + +// FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + if(*wait < 10) { *wait = 10; } + +// for(c = 0; c < *wait;) { +// if(SSC_STATUS & (SSC_STATUS_TX_READY)) { +// SSC_TRANSMIT_HOLDING = 0x00; // For exact timing! +// c++; +// } +// if(SSC_STATUS & (SSC_STATUS_RX_READY)) { +// volatile DWORD r = SSC_RECEIVE_HOLDING; +// (void)r; +// } +// WDT_HIT(); +// } + + c = 0; + for(;;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = cmd[c]; + c++; + if(c >= len) { + break; + } + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + volatile DWORD r = SSC_RECEIVE_HOLDING; + (void)r; + } + WDT_HIT(); + } + *samples = (c + *wait) << 3; +} + + +//----------------------------------------------------------------------------- +// Transmit the command (to the reader) that was placed in ToSend[]. +//----------------------------------------------------------------------------- +static void TransmitTo15693Reader(const BYTE *cmd, int len, int *samples, int *wait) +{ + int c; + +// FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR); // No requirement to energise my coils + if(*wait < 10) { *wait = 10; } + + c = 0; + for(;;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = cmd[c]; + c++; + if(c >= len) { + break; + } + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + volatile DWORD r = SSC_RECEIVE_HOLDING; + (void)r; + } + WDT_HIT(); + } + *samples = (c + *wait) << 3; +} + + + + + + +static int GetIso15693AnswerFromTag(BYTE *receivedResponse, int maxLen, int *samples, int *elapsed) +{ + int c = 0; + BYTE *dest = (BYTE *)BigBuf; + int getNext = 0; + + + SBYTE prev = 0; + +// NOW READ RESPONSE + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads + c = 0; + getNext = FALSE; + for(;;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = 0x43; + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + SBYTE b; + b = (SBYTE)SSC_RECEIVE_HOLDING; + + // The samples are correlations against I and Q versions of the + // tone that the tag AM-modulates, so every other sample is I, + // every other is Q. We just want power, so abs(I) + abs(Q) is + // close to what we want. + if(getNext) { + SBYTE r; + + if(b < 0) { + r = -b; + } else { + r = b; + } + if(prev < 0) { + r -= prev; + } else { + r += prev; + } + + dest[c++] = (BYTE)r; + + if(c >= 2000) { + break; + } + } else { + prev = b; + } + + getNext = !getNext; + } + } + +////////////////////////////////////////// +/////////// DEMODULATE /////////////////// +////////////////////////////////////////// + + int i, j; + int max = 0, maxPos; + + int skip = 4; + + +// if(GraphTraceLen < 1000) return; // THIS CHECKS FOR A BUFFER TO SMALL + + // First, correlate for SOF + for(i = 0; i < 100; i++) { + int corr = 0; + for(j = 0; j < arraylen(FrameSOF); j += skip) { + corr += FrameSOF[j]*dest[i+(j/skip)]; + } + if(corr > max) { + max = corr; + maxPos = i; + } + } +// DbpString("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip)); + + int k = 0; // this will be our return value + + // greg - If correlation is less than 1 then there's little point in continuing + if ((max/(arraylen(FrameSOF)/skip)) >= 1) + { + + i = maxPos + arraylen(FrameSOF)/skip; + + BYTE outBuf[20]; + memset(outBuf, 0, sizeof(outBuf)); + BYTE mask = 0x01; + for(;;) { + int corr0 = 0, corr1 = 0, corrEOF = 0; + for(j = 0; j < arraylen(Logic0); j += skip) { + corr0 += Logic0[j]*dest[i+(j/skip)]; + } + for(j = 0; j < arraylen(Logic1); j += skip) { + corr1 += Logic1[j]*dest[i+(j/skip)]; + } + for(j = 0; j < arraylen(FrameEOF); j += skip) { + corrEOF += FrameEOF[j]*dest[i+(j/skip)]; + } + // Even things out by the length of the target waveform. + corr0 *= 4; + corr1 *= 4; + + if(corrEOF > corr1 && corrEOF > corr0) { +// DbpString("EOF at %d", i); + break; + } else if(corr1 > corr0) { + i += arraylen(Logic1)/skip; + outBuf[k] |= mask; + } else { + i += arraylen(Logic0)/skip; + } + mask <<= 1; + if(mask == 0) { + k++; + mask = 0x01; + } + if((i+(int)arraylen(FrameEOF)) >= 2000) { + DbpString("ran off end!"); + break; + } + } + if(mask != 0x01) { + DbpString("error, uneven octet! (discard extra bits!)"); +/// DbpString(" mask=%02x", mask); + } +// BYTE str1 [8]; +// itoa(k,str1); +// strcat(str1," octets read"); + +// DbpString( str1); // DbpString("%d octets", k); + +// for(i = 0; i < k; i+=3) { +// //DbpString("# %2d: %02x ", i, outBuf[i]); +// DbpIntegers(outBuf[i],outBuf[i+1],outBuf[i+2]); +// } + + for(i = 0; i < k; i++) { + receivedResponse[i] = outBuf[i]; + } + } // "end if correlation > 0" (max/(arraylen(FrameSOF)/skip)) + return k; // return the number of bytes demodulated + +/// DbpString("CRC=%04x", Iso15693Crc(outBuf, k-2)); + + +} + +// Now the GetISO15693 message from sniffing command +static int GetIso15693AnswerFromSniff(BYTE *receivedResponse, int maxLen, int *samples, int *elapsed) +{ + int c = 0; + BYTE *dest = (BYTE *)BigBuf; + int getNext = 0; + + + SBYTE prev = 0; + +// NOW READ RESPONSE + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads + c = 0; + getNext = FALSE; + for(;;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = 0x43; + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + SBYTE b; + b = (SBYTE)SSC_RECEIVE_HOLDING; + + // The samples are correlations against I and Q versions of the + // tone that the tag AM-modulates, so every other sample is I, + // every other is Q. We just want power, so abs(I) + abs(Q) is + // close to what we want. + if(getNext) { + SBYTE r; + + if(b < 0) { + r = -b; + } else { + r = b; + } + if(prev < 0) { + r -= prev; + } else { + r += prev; + } + + dest[c++] = (BYTE)r; + + if(c >= 20000) { + break; + } + } else { + prev = b; + } + + getNext = !getNext; + } + } + +////////////////////////////////////////// +/////////// DEMODULATE /////////////////// +////////////////////////////////////////// + + int i, j; + int max = 0, maxPos; + + int skip = 4; + + +// if(GraphTraceLen < 1000) return; // THIS CHECKS FOR A BUFFER TO SMALL + + // First, correlate for SOF + for(i = 0; i < 19000; i++) { + int corr = 0; + for(j = 0; j < arraylen(FrameSOF); j += skip) { + corr += FrameSOF[j]*dest[i+(j/skip)]; + } + if(corr > max) { + max = corr; + maxPos = i; + } + } +// DbpString("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip)); + + int k = 0; // this will be our return value + + // greg - If correlation is less than 1 then there's little point in continuing + if ((max/(arraylen(FrameSOF)/skip)) >= 1) // THIS SHOULD BE 1 + { + + i = maxPos + arraylen(FrameSOF)/skip; + + BYTE outBuf[20]; + memset(outBuf, 0, sizeof(outBuf)); + BYTE mask = 0x01; + for(;;) { + int corr0 = 0, corr1 = 0, corrEOF = 0; + for(j = 0; j < arraylen(Logic0); j += skip) { + corr0 += Logic0[j]*dest[i+(j/skip)]; + } + for(j = 0; j < arraylen(Logic1); j += skip) { + corr1 += Logic1[j]*dest[i+(j/skip)]; + } + for(j = 0; j < arraylen(FrameEOF); j += skip) { + corrEOF += FrameEOF[j]*dest[i+(j/skip)]; + } + // Even things out by the length of the target waveform. + corr0 *= 4; + corr1 *= 4; + + if(corrEOF > corr1 && corrEOF > corr0) { +// DbpString("EOF at %d", i); + break; + } else if(corr1 > corr0) { + i += arraylen(Logic1)/skip; + outBuf[k] |= mask; + } else { + i += arraylen(Logic0)/skip; + } + mask <<= 1; + if(mask == 0) { + k++; + mask = 0x01; + } + if((i+(int)arraylen(FrameEOF)) >= 2000) { + DbpString("ran off end!"); + break; + } + } + if(mask != 0x01) { + DbpString("error, uneven octet! (discard extra bits!)"); +/// DbpString(" mask=%02x", mask); + } +// BYTE str1 [8]; +// itoa(k,str1); +// strcat(str1," octets read"); + +// DbpString( str1); // DbpString("%d octets", k); + +// for(i = 0; i < k; i+=3) { +// //DbpString("# %2d: %02x ", i, outBuf[i]); +// DbpIntegers(outBuf[i],outBuf[i+1],outBuf[i+2]); +// } + + for(i = 0; i < k; i++) { + receivedResponse[i] = outBuf[i]; + } + } // "end if correlation > 0" (max/(arraylen(FrameSOF)/skip)) + return k; // return the number of bytes demodulated + +/// DbpString("CRC=%04x", Iso15693Crc(outBuf, k-2)); + + +} + + + +//----------------------------------------------------------------------------- +// Start to read an ISO 15693 tag. We send an identify request, then wait +// for the response. The response is not demodulated, just left in the buffer +// so that it can be downloaded to a PC and processed there. +//----------------------------------------------------------------------------- +void AcquireRawAdcSamplesIso15693(void) +{ + int c = 0; + BYTE *dest = (BYTE *)BigBuf; + int getNext = 0; + + SBYTE prev = 0; + + BuildIdentifyRequest(); + + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // Give the tags time to energize + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + SpinDelay(100); + + // Now send the command + FpgaSetupSsc(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + + c = 0; + for(;;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = ToSend[c]; + c++; + if(c == ToSendMax+3) { + break; + } + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + volatile DWORD r = SSC_RECEIVE_HOLDING; + (void)r; + } + WDT_HIT(); + } + + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + + c = 0; + getNext = FALSE; + for(;;) { + if(SSC_STATUS & (SSC_STATUS_TX_READY)) { + SSC_TRANSMIT_HOLDING = 0x43; + } + if(SSC_STATUS & (SSC_STATUS_RX_READY)) { + SBYTE b; + b = (SBYTE)SSC_RECEIVE_HOLDING; + + // The samples are correlations against I and Q versions of the + // tone that the tag AM-modulates, so every other sample is I, + // every other is Q. We just want power, so abs(I) + abs(Q) is + // close to what we want. + if(getNext) { + SBYTE r; + + if(b < 0) { + r = -b; + } else { + r = b; + } + if(prev < 0) { + r -= prev; + } else { + r += prev; + } + + dest[c++] = (BYTE)r; + + if(c >= 2000) { + break; + } + } else { + prev = b; + } + + getNext = !getNext; + } + } +} + + + +//----------------------------------------------------------------------------- +// Simulate an ISO15693 reader, perform anti-collision and then attempt to read a sector +// all demodulation performed in arm rather than host. - greg +//----------------------------------------------------------------------------- +void ReaderIso15693(DWORD parameter) +{ + LED_A_ON(); + LED_B_ON(); + LED_C_OFF(); + LED_D_OFF(); + + +//DbpString(parameter); + + BYTE *receivedAnswer0 = (((BYTE *)BigBuf) + 3560); // allow 100 bytes per reponse (way too much) + BYTE *receivedAnswer1 = (((BYTE *)BigBuf) + 3660); // + BYTE *receivedAnswer2 = (((BYTE *)BigBuf) + 3760); + BYTE *receivedAnswer3 = (((BYTE *)BigBuf) + 3860); + //BYTE *TagUID= (((BYTE *)BigBuf) + 3960); // where we hold the uid for hi15reader + int responseLen0 = 0; + int responseLen1 = 0; + int responseLen2 = 0; + int responseLen3 = 0; + + // Blank arrays + int j; + for(j = 0; j < 100; j++) { + receivedAnswer3[j] = 0; + receivedAnswer2[j] =0; + receivedAnswer1[j] = 0; + receivedAnswer0[j] = 0; + } + + // Setup SSC + FpgaSetupSsc(); + + // Start from off (no field generated) + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(200); + + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaSetupSsc(); + + // Give the tags time to energize + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + SpinDelay(200); + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + + int samples = 0; + int tsamples = 0; + int wait = 0; + int elapsed = 0; + + // FIRST WE RUN AN INVENTORY TO GET THE TAG UID + // THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME + BYTE TagUID[7]; // where we hold the uid for hi15reader + + +// BuildIdentifyRequest(); +// //TransmitTo15693Tag(ToSend,ToSendMax+3,&tsamples, &wait); +// TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // No longer ToSendMax+3 +// // Now wait for a response +// responseLen0 = GetIso15693AnswerFromTag(receivedAnswer0, 100, &samples, &elapsed) ; +// if (responseLen0 >=12) // we should do a better check than this +// { +// // really we should check it is a valid mesg +// // but for now just grab what we think is the uid +// TagUID[0] = receivedAnswer0[2]; +// TagUID[1] = receivedAnswer0[3]; +// TagUID[2] = receivedAnswer0[4]; +// TagUID[3] = receivedAnswer0[5]; +// TagUID[4] = receivedAnswer0[6]; +// TagUID[5] = receivedAnswer0[7]; +// TagUID[6] = receivedAnswer0[8]; // IC Manufacturer code +// DbpIntegers(TagUID[6],TagUID[5],TagUID[4]); +//} + + // Now send the IDENTIFY command + BuildIdentifyRequest(); + //TransmitTo15693Tag(ToSend,ToSendMax+3,&tsamples, &wait); + TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // No longer ToSendMax+3 + // Now wait for a response + responseLen1 = GetIso15693AnswerFromTag(receivedAnswer1, 100, &samples, &elapsed) ; + + if (responseLen1 >=12) // we should do a better check than this + { + + TagUID[0] = receivedAnswer1[2]; + TagUID[1] = receivedAnswer1[3]; + TagUID[2] = receivedAnswer1[4]; + TagUID[3] = receivedAnswer1[5]; + TagUID[4] = receivedAnswer1[6]; + TagUID[5] = receivedAnswer1[7]; + TagUID[6] = receivedAnswer1[8]; // IC Manufacturer code + + // Now send the SELECT command + BuildSelectRequest(*TagUID); + TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // No longer ToSendMax+3 + // Now wait for a response + responseLen2 = GetIso15693AnswerFromTag(receivedAnswer2, 100, &samples, &elapsed); + + // Now send the MULTI READ command +// BuildArbitraryRequest(*TagUID,parameter); + BuildArbitraryCustomRequest(*TagUID,parameter); +// BuildReadBlockRequest(*TagUID,parameter); +// BuildSysInfoRequest(*TagUID); + //TransmitTo15693Tag(ToSend,ToSendMax+3,&tsamples, &wait); + TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // No longer ToSendMax+3 + // Now wait for a response + responseLen3 = GetIso15693AnswerFromTag(receivedAnswer3, 100, &samples, &elapsed) ; + + } + + + + BYTE str1 [4]; + //char str2 [200]; + int i; + + itoa(responseLen1,str1); + strcat(str1," octets read from IDENTIFY request"); + DbpString(str1); + for(i = 0; i < responseLen1; i+=3) { + DbpIntegers(receivedAnswer1[i],receivedAnswer1[i+1],receivedAnswer1[i+2]); + } + + itoa(responseLen2,str1); + strcat(str1," octets read from SELECT request"); + DbpString(str1); + for(i = 0; i < responseLen2; i+=3) { + DbpIntegers(receivedAnswer2[i],receivedAnswer2[i+1],receivedAnswer2[i+2]); + } + + itoa(responseLen3,str1); + strcat(str1," octets read from XXX request"); + DbpString(str1); + for(i = 0; i < responseLen3; i+=3) { + DbpIntegers(receivedAnswer3[i],receivedAnswer3[i+1],receivedAnswer3[i+2]); + } + + +// str2[0]=0; +// for(i = 0; i < responseLen3; i++) { +// itoa(str1,receivedAnswer3[i]); +// strcat(str2,str1); +// } +// DbpString(str2); + + LED_A_OFF(); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + + +} + + + +//----------------------------------------------------------------------------- +// Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands +// all demodulation performed in arm rather than host. - greg +//----------------------------------------------------------------------------- +void SimTagIso15693(DWORD parameter) +{ + LED_A_ON(); + LED_B_ON(); + LED_C_OFF(); + LED_D_OFF(); + + +//DbpString(parameter); + + BYTE *receivedAnswer0 = (((BYTE *)BigBuf) + 3560); // allow 100 bytes per reponse (way too much) + BYTE *receivedAnswer1 = (((BYTE *)BigBuf) + 3660); // + BYTE *receivedAnswer2 = (((BYTE *)BigBuf) + 3760); + BYTE *receivedAnswer3 = (((BYTE *)BigBuf) + 3860); + //BYTE *TagUID= (((BYTE *)BigBuf) + 3960); // where we hold the uid for hi15reader + int responseLen0 = 0; + int responseLen1 = 0; + int responseLen2 = 0; + int responseLen3 = 0; + + // Blank arrays + int j; + for(j = 0; j < 100; j++) { + receivedAnswer3[j] = 0; + receivedAnswer2[j] =0; + receivedAnswer1[j] = 0; + receivedAnswer0[j] = 0; + } + + // Setup SSC + FpgaSetupSsc(); + + // Start from off (no field generated) + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(200); + + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaSetupSsc(); + + // Give the tags time to energize +// FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // NO GOOD FOR SIM TAG!!!! + SpinDelay(200); + + LED_A_OFF(); + LED_B_OFF(); + LED_C_ON(); + LED_D_OFF(); + + int samples = 0; + int tsamples = 0; + int wait = 0; + int elapsed = 0; + + // FIRST WE RUN AN INVENTORY TO GET THE TAG UID + // THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME + BYTE TagUID[7]; // where we hold the uid for hi15reader + + + + // Now send the IDENTIFY command +// BuildIdentifyRequest(); +// TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // No longer ToSendMax+3 + + + // Now wait for a command from the reader + responseLen1=0; +// while(responseLen1=0) { +// if(BUTTON_PRESS()) break; + responseLen1 = GetIso15693AnswerFromSniff(receivedAnswer1, 100, &samples, &elapsed) ; +// } + + + if (responseLen1 >=1) // we should do a better check than this + { + // Build a suitable reponse to the reader INVENTORY cocmmand + BuildInventoryResponse; + TransmitTo15693Reader(ToSend,ToSendMax,&tsamples, &wait); + + // Now wait for a command from the reader +// responseLen2 = GetIso15693AnswerFromTag(receivedAnswer2, 100, &samples, &elapsed); + + + // Now wait for a command from the reader +// responseLen3 = GetIso15693AnswerFromTag(receivedAnswer3, 100, &samples, &elapsed) ; + + } + + + + BYTE str1 [4]; + //char str2 [200]; + int i; + + itoa(responseLen1,str1); + strcat(str1," octets read from reader command"); + DbpString(str1); + for(i = 0; i < responseLen1; i+=3) { + DbpIntegers(receivedAnswer1[i],receivedAnswer1[i+1],receivedAnswer1[i+2]); + } + +// itoa(responseLen2,str1); +// strcat(str1," octets read from SELECT request"); +// DbpString(str1); +// for(i = 0; i < responseLen2; i+=3) { +// DbpIntegers(receivedAnswer2[i],receivedAnswer2[i+1],receivedAnswer2[i+2]); +// } +// +// itoa(responseLen3,str1); +// strcat(str1," octets read from XXX request"); +// DbpString(str1); +// for(i = 0; i < responseLen3; i+=3) { +// DbpIntegers(receivedAnswer3[i],receivedAnswer3[i+1],receivedAnswer3[i+2]); +// } + + +// str2[0]=0; +// for(i = 0; i < responseLen3; i++) { +// itoa(str1,receivedAnswer3[i]); +// strcat(str2,str1); +// } +// DbpString(str2); + + LED_A_OFF(); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + + +} \ No newline at end of file diff --git a/armsrc/ldscript b/armsrc/ldscript new file mode 100644 index 00000000..ac0fe2bd --- /dev/null +++ b/armsrc/ldscript @@ -0,0 +1,11 @@ +SECTIONS +{ + . = 0x00010000; + .text : { obj/start.o(.text) *(.text) } + .rodata : { *(.rodata) } + . = 0x00200000; + .data : { *(.data) } + __bss_start__ = .; + .bss : { *(.bss) } + __bss_end__ = .; +} diff --git a/armsrc/ldscript-fpga b/armsrc/ldscript-fpga new file mode 100644 index 00000000..da8b1a21 --- /dev/null +++ b/armsrc/ldscript-fpga @@ -0,0 +1,11 @@ +SECTIONS +{ + . = 0x00002000; + .text : { obj/fpgaimg.o(.text) *(.text) } + .rodata : { *(.rodata) } + . = 0x00200000; + .data : { *(.data) } + __bss_start__ = .; + .bss : { *(.bss) } + __bss_end__ = .; +} diff --git a/armsrc/start.c b/armsrc/start.c new file mode 100644 index 00000000..52296fc2 --- /dev/null +++ b/armsrc/start.c @@ -0,0 +1,12 @@ +//----------------------------------------------------------------------------- +// Just vector to AppMain(). This is in its own file so that I can place it +// with the linker script. +// Jonathan Westhues, Mar 2006 +//----------------------------------------------------------------------------- +#include +#include "apps.h" + +void Vector(void) +{ + AppMain(); +} diff --git a/armsrc/util.c b/armsrc/util.c new file mode 100644 index 00000000..b3f0e76e --- /dev/null +++ b/armsrc/util.c @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Utility functions used in many places, not specific to any piece of code. +// Jonathan Westhues, Sept 2005 +//----------------------------------------------------------------------------- +#include +#include "apps.h" + +void *memcpy(void *dest, const void *src, int len) +{ + BYTE *d = dest; + const BYTE *s = src; + while((len--) > 0) { + *d = *s; + d++; + s++; + } + return dest; +} + +void *memset(void *dest, int c, int len) +{ + BYTE *d = dest; + while((len--) > 0) { + *d = c; + d++; + } + return dest; +} + +int memcmp(const void *av, const void *bv, int len) +{ + const BYTE *a = av; + const BYTE *b = bv; + + while((len--) > 0) { + if(*a != *b) { + return *a - *b; + } + a++; + b++; + } + return 0; +} + +int strlen(char *str) +{ + int l = 0; + while(*str) { + l++; + str++; + } + return l; +} diff --git a/bootrom/Makefile b/bootrom/Makefile new file mode 100644 index 00000000..e18737c3 --- /dev/null +++ b/bootrom/Makefile @@ -0,0 +1,58 @@ +CC = arm-elf-gcc +AS = arm-elf-as +LD = arm-elf-ld +OBJCOPY = arm-elf-objcopy + +OBJDIR = obj + +INCLUDE = -I../include + +INCLUDES = ../include/proxmark3.h ../include/at91sam7s128.h ../include/config_gpio.h ../include/usb_cmd.h + +CFLAGS = -g -c $(INCLUDE) -Wall + +OBJJTAG = $(OBJDIR)/bootrom.o $(OBJDIR)/ram-reset.o $(OBJDIR)/usb.o + +OBJFLASH = $(OBJDIR)/flash-reset.o $(OBJDIR)/fromflash.o + +all: bootrom.s19 + +bootrom.s19: $(OBJDIR)/bootrom.s19 $(OBJDIR)/bootrom-forjtag.s19 + @echo bootrom.s19 + @perl ..\tools\merge-srec.pl $(OBJDIR)\bootrom.s19 $(OBJDIR)\bootrom-forjtag.s19 > $(OBJDIR)\bootrom-merged.s19 + @perl ..\tools\srecswap.pl $(OBJDIR)\bootrom-forjtag.s19 > $(OBJDIR)\bootrom-forjtag-swapped.s19 + +$(OBJDIR)/bootrom.s19: $(OBJFLASH) + @echo obj/bootrom.s19 + @$(LD) -g -Tldscript-flash --oformat elf32-littlearm -o $(OBJDIR)/bootrom.elf $(OBJFLASH) + @$(OBJCOPY) -Osrec --srec-forceS3 $(OBJDIR)/bootrom.elf $(OBJDIR)/bootrom.s19 + +$(OBJDIR)/bootrom-forjtag.s19: $(OBJJTAG) + @echo obj/bootrom-forjtag.s19 + @$(LD) -g -Tldscript-ram-jtag --oformat elf32-littlearm -o $(OBJDIR)/bootrom-forjtag.elf $(OBJJTAG) + @$(OBJCOPY) -Osrec --srec-forceS3 $(OBJDIR)/bootrom-forjtag.elf $(OBJDIR)/bootrom-forjtag.s19 + +$(OBJDIR)/bootrom.o: bootrom.c $(INCLUDES) + @echo $(@B).c + @$(CC) $(CFLAGS) -mthumb -mthumb-interwork bootrom.c -o $(OBJDIR)/bootrom.o + +$(OBJDIR)/fromflash.o: fromflash.c $(INCLUDES) + @echo $(@B).c + @$(CC) $(CFLAGS) -mthumb -mthumb-interwork fromflash.c -o $(OBJDIR)/fromflash.o + +$(OBJDIR)/usb.o: ../common/usb.c $(INCLUDES) + @echo $(@B).c + @$(CC) $(CFLAGS) -mthumb -mthumb-interwork ../common/usb.c -o $(OBJDIR)/usb.o + +$(OBJDIR)/ram-reset.o: ram-reset.s + @echo $(@B).s + @$(CC) $(CFLAGS) -mthumb-interwork -o $(OBJDIR)/ram-reset.o ram-reset.s + +$(OBJDIR)/flash-reset.o: flash-reset.s + @echo $(@B).s + @$(CC) $(CFLAGS) -mthumb-interwork -o $(OBJDIR)/flash-reset.o flash-reset.s + +clean: + del /q obj\*.o + del /q obj\*.elf + del /q obj\*.s19 diff --git a/bootrom/bootrom.c b/bootrom/bootrom.c new file mode 100644 index 00000000..c654db29 --- /dev/null +++ b/bootrom/bootrom.c @@ -0,0 +1,190 @@ +#include + +static void ConfigClocks(void) +{ + // we are using a 16 MHz crystal as the basis for everything + // slow clock runs at 32Khz typical regardless of crystal + + // enable system clock and USB clock + PMC_SYS_CLK_ENABLE = PMC_SYS_CLK_PROCESSOR_CLK | PMC_SYS_CLK_UDP_CLK; + + // enable the clock to the following peripherals + PMC_PERIPHERAL_CLK_ENABLE = + (1<cmd) { + case CMD_DEVICE_INFO: + break; + + case CMD_SETUP_WRITE: + p = (volatile DWORD *)0; + for(i = 0; i < 12; i++) { + p[i+c->ext1] = c->d.asDwords[i]; + } + break; + + case CMD_FINISH_WRITE: + p = (volatile DWORD *)0; + for(i = 0; i < 4; i++) { + p[i+60] = c->d.asDwords[i]; + } + + MC_FLASH_COMMAND = MC_FLASH_COMMAND_KEY | + MC_FLASH_COMMAND_PAGEN(c->ext1/FLASH_PAGE_SIZE_BYTES) | + FCMD_WRITE_PAGE; + while(!(MC_FLASH_STATUS & MC_FLASH_STATUS_READY)) + ; + break; + + case CMD_HARDWARE_RESET: + break; + + default: + Fatal(); + break; + } + + c->cmd = CMD_ACK; + UsbSendPacket(packet, len); +} + +void BootROM(void) +{ + //------------ + // First set up all the I/O pins; GPIOs configured directly, other ones + // just need to be assigned to the appropriate peripheral. + + // Kill all the pullups, especially the one on USB D+; leave them for + // the unused pins, though. + PIO_NO_PULL_UP_ENABLE = (1 << GPIO_USB_PU) | + (1 << GPIO_LED_A) | + (1 << GPIO_LED_B) | + (1 << GPIO_LED_C) | + (1 << GPIO_LED_D) | + (1 << GPIO_FPGA_DIN) | + (1 << GPIO_FPGA_DOUT) | + (1 << GPIO_FPGA_CCLK) | + (1 << GPIO_FPGA_NINIT) | + (1 << GPIO_FPGA_NPROGRAM) | + (1 << GPIO_FPGA_DONE) | + (1 << GPIO_MUXSEL_HIPKD) | + (1 << GPIO_MUXSEL_HIRAW) | + (1 << GPIO_MUXSEL_LOPKD) | + (1 << GPIO_MUXSEL_LORAW) | + (1 << GPIO_RELAY) | + (1 << GPIO_NVDD_ON); + // (and add GPIO_FPGA_ON) + // These pins are outputs + PIO_OUTPUT_ENABLE = (1 << GPIO_LED_A) | + (1 << GPIO_LED_B) | + (1 << GPIO_LED_C) | + (1 << GPIO_LED_D) | + (1 << GPIO_RELAY) | + (1 << GPIO_NVDD_ON); + // PIO controls the following pins + PIO_ENABLE = (1 << GPIO_USB_PU) | + (1 << GPIO_LED_A) | + (1 << GPIO_LED_B) | + (1 << GPIO_LED_C) | + (1 << GPIO_LED_D); + + USB_D_PLUS_PULLUP_OFF(); + LED_D_OFF(); + LED_C_ON(); + LED_B_OFF(); + LED_A_OFF(); + + // if 512K FLASH part - TODO make some defines :) + if ((DBGU_CIDR | 0xf00) == 0xa00) { + MC_FLASH_MODE0 = MC_FLASH_MODE_FLASH_WAIT_STATES(1) | + MC_FLASH_MODE_MASTER_CLK_IN_MHZ(0x48); + MC_FLASH_MODE1 = MC_FLASH_MODE_FLASH_WAIT_STATES(1) | + MC_FLASH_MODE_MASTER_CLK_IN_MHZ(0x48); + } else { + MC_FLASH_MODE0 = MC_FLASH_MODE_FLASH_WAIT_STATES(0) | + MC_FLASH_MODE_MASTER_CLK_IN_MHZ(48); + } + + // Initialize all system clocks + ConfigClocks(); + + LED_A_ON(); + + if(BUTTON_PRESS()) { + UsbStart(); + } + + for(;;) { + WDT_HIT(); + + UsbPoll(TRUE); + + if(!BUTTON_PRESS()) { + USB_D_PLUS_PULLUP_OFF(); + LED_B_ON(); + + // jump to RAM address 0x10000 (LSBit set for thumb mode) + asm("ldr r3, = 0x10001\n"); + asm("bx r3\n"); + } + } +} diff --git a/bootrom/flash-reset.s b/bootrom/flash-reset.s new file mode 100644 index 00000000..afb658a4 --- /dev/null +++ b/bootrom/flash-reset.s @@ -0,0 +1,38 @@ +.extern CopyBootToRAM + +.text +.code 32 +.align 0 + +.global start +start: + b Reset + b UndefinedInstruction + b SoftwareInterrupt + b PrefetchAbort + b DataAbort + b Reserved + b Irq + b Fiq + +Reset: + ldr sp, = 0x0020FFF8 @ initialize stack pointer to top of RAM + bl CopyBootToRAM @ copy bootloader to RAM (in case the + @ user re-flashes the bootloader) + ldr r3, = 0x00200000 @ start address of RAM bootloader + bx r3 @ jump to it + +Fiq: + b Fiq +UndefinedInstruction: + b UndefinedInstruction +SoftwareInterrupt: + b SoftwareInterrupt +PrefetchAbort: + b PrefetchAbort +DataAbort: + b DataAbort +Reserved: + b Reserved +Irq: + b Irq diff --git a/bootrom/fromflash.c b/bootrom/fromflash.c new file mode 100644 index 00000000..e6868092 --- /dev/null +++ b/bootrom/fromflash.c @@ -0,0 +1,11 @@ +#include + +void CopyBootToRAM(void) +{ + int i; + + volatile DWORD *s = (volatile DWORD *)0x200; + volatile DWORD *d = (volatile DWORD *)0x200000; + + for(i = 0; i < 1024; i++) *d++ = *s++; +} diff --git a/bootrom/ldscript-flash b/bootrom/ldscript-flash new file mode 100644 index 00000000..0d5d7325 --- /dev/null +++ b/bootrom/ldscript-flash @@ -0,0 +1,11 @@ +SECTIONS +{ + . = 0x00000000; + .text : { obj/flash-reset.o(.text) *(.text) } + .rodata : { *(.rodata) } + . = 0x00200000; + .data : { *(.data) } + __bss_start__ = .; + .bss : { *(.bss) } + __bss_end__ = .; +} diff --git a/bootrom/ldscript-ram-jtag b/bootrom/ldscript-ram-jtag new file mode 100644 index 00000000..5dd57061 --- /dev/null +++ b/bootrom/ldscript-ram-jtag @@ -0,0 +1,10 @@ +SECTIONS +{ + . = 0x00200000; + .text : { obj/ram-reset.o(.text) *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + __bss_start__ = .; + .bss : { *(.bss) } + __bss_end__ = .; +} diff --git a/bootrom/ram-reset.s b/bootrom/ram-reset.s new file mode 100644 index 00000000..56bf36e3 --- /dev/null +++ b/bootrom/ram-reset.s @@ -0,0 +1,10 @@ +.extern BootROM + +.text +.code 32 +.align 0 + +.global start +start: + ldr sp, = 0x0020FFF8 + bl BootROM diff --git a/cockpit/0setpath.bat b/cockpit/0setpath.bat new file mode 100644 index 00000000..5010957b --- /dev/null +++ b/cockpit/0setpath.bat @@ -0,0 +1,5 @@ +@echo off +set PATH=..\..\devkitARM\bin;..\..\devkitWIN\bin;%PATH% +set INCLUDE=..\..\devkitWIN\include +set LIB=..\..\devkitWIN\lib +cmd.exe \ No newline at end of file diff --git a/cockpit/1makearm.bat b/cockpit/1makearm.bat new file mode 100644 index 00000000..a8ecb6e4 --- /dev/null +++ b/cockpit/1makearm.bat @@ -0,0 +1,5 @@ +@echo off +cd ..\armsrc +rem nmake clean +nmake +cd ..\cockpit diff --git a/cockpit/2makeboot.bat b/cockpit/2makeboot.bat new file mode 100644 index 00000000..a56fcfe0 --- /dev/null +++ b/cockpit/2makeboot.bat @@ -0,0 +1,5 @@ +@echo off +cd ..\bootrom +rem nmake clean +nmake +cd ..\cockpit diff --git a/cockpit/3makewin.bat b/cockpit/3makewin.bat new file mode 100644 index 00000000..82228e32 --- /dev/null +++ b/cockpit/3makewin.bat @@ -0,0 +1,5 @@ +@echo off +cd ..\winsrc +rem nmake clean +nmake +cd ..\cockpit diff --git a/cockpit/4flashos.bat b/cockpit/4flashos.bat new file mode 100644 index 00000000..6f226184 --- /dev/null +++ b/cockpit/4flashos.bat @@ -0,0 +1,3 @@ +@echo off +..\winsrc\prox.exe load ..\armsrc\obj\osimage.s19 +..\winsrc\prox.exe load ..\armsrc\obj\osimage.s19 diff --git a/cockpit/5makeall.bat b/cockpit/5makeall.bat new file mode 100644 index 00000000..072393d9 --- /dev/null +++ b/cockpit/5makeall.bat @@ -0,0 +1,3 @@ +call 1makearm.bat +call 2makeboot.bat +call 3makewin.bat diff --git a/cockpit/prox.bat b/cockpit/prox.bat new file mode 100644 index 00000000..06b24edc --- /dev/null +++ b/cockpit/prox.bat @@ -0,0 +1,3 @@ +@echo off +cd ..\winsrc +call prox gui \ No newline at end of file diff --git a/common/iso14443_crc.c b/common/iso14443_crc.c new file mode 100644 index 00000000..cf29d0e0 --- /dev/null +++ b/common/iso14443_crc.c @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Routines to compute the CRCs (two different flavours, just for confusion) +// required for ISO 14443, swiped directly from the spec. +//----------------------------------------------------------------------------- + +#define CRC_14443_A 0x6363 /* ITU-V.41 */ +#define CRC_14443_B 0xFFFF /* ISO/IEC 13239 (formerly ISO/IEC 3309) */ + +static unsigned short UpdateCrc14443(unsigned char ch, unsigned short *lpwCrc) +{ + ch = (ch ^ (unsigned char) ((*lpwCrc) & 0x00FF)); + ch = (ch ^ (ch << 4)); + *lpwCrc = (*lpwCrc >> 8) ^ ((unsigned short) ch << 8) ^ + ((unsigned short) ch << 3) ^ ((unsigned short) ch >> 4); + return (*lpwCrc); +} + +static void ComputeCrc14443(int CrcType, BYTE *Data, int Length, + BYTE *TransmitFirst, BYTE *TransmitSecond) +{ + unsigned char chBlock; + unsigned short wCrc=CrcType; + + do { + chBlock = *Data++; + UpdateCrc14443(chBlock, &wCrc); + } while (--Length); + + if (CrcType == CRC_14443_B) + wCrc = ~wCrc; /* ISO/IEC 13239 (formerly ISO/IEC 3309) */ + + *TransmitFirst = (BYTE) (wCrc & 0xFF); + *TransmitSecond = (BYTE) ((wCrc >> 8) & 0xFF); + return; +} diff --git a/common/usb.c b/common/usb.c new file mode 100644 index 00000000..f0b95291 --- /dev/null +++ b/common/usb.c @@ -0,0 +1,472 @@ +//----------------------------------------------------------------------------- +// My USB driver. This has to be common, because it exists in both the +// bootrom and the application. +// Jonathan Westhues, split Aug 14 2005 +//----------------------------------------------------------------------------- +#include + +#define min(a, b) (((a) > (b)) ? (b) : (a)) + +#define USB_REPORT_PACKET_SIZE 64 + +typedef struct PACKED { + BYTE bmRequestType; + BYTE bRequest; + WORD wValue; + WORD wIndex; + WORD wLength; +} UsbSetupData; + +#define USB_REQUEST_GET_STATUS 0 +#define USB_REQUEST_CLEAR_FEATURE 1 +#define USB_REQUEST_SET_FEATURE 3 +#define USB_REQUEST_SET_ADDRESS 5 +#define USB_REQUEST_GET_DESCRIPTOR 6 +#define USB_REQUEST_SET_DESCRIPTOR 7 +#define USB_REQUEST_GET_CONFIGURATION 8 +#define USB_REQUEST_SET_CONFIGURATION 9 +#define USB_REQUEST_GET_INTERFACE 10 +#define USB_REQUEST_SET_INTERFACE 11 +#define USB_REQUEST_SYNC_FRAME 12 + +#define USB_DESCRIPTOR_TYPE_DEVICE 1 +#define USB_DESCRIPTOR_TYPE_CONFIGURATION 2 +#define USB_DESCRIPTOR_TYPE_STRING 3 +#define USB_DESCRIPTOR_TYPE_INTERFACE 4 +#define USB_DESCRIPTOR_TYPE_ENDPOINT 5 +#define USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER 6 +#define USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONF 7 +#define USB_DESCRIPTOR_TYPE_INTERFACE_POWER 8 +#define USB_DESCRIPTOR_TYPE_HID 0x21 +#define USB_DESCRIPTOR_TYPE_HID_REPORT 0x22 + +#define USB_DEVICE_CLASS_HID 0x03 + +static const BYTE HidReportDescriptor[] = { + 0x06,0xA0,0xFF, // Usage Page (vendor defined) FFA0 + 0x09,0x01, // Usage (vendor defined) + 0xA1,0x01, // Collection (Application) + 0x09,0x02, // Usage (vendor defined) + 0xA1,0x00, // Collection (Physical) + 0x06,0xA1,0xFF, // Usage Page (vendor defined) + + //The,input report + 0x09,0x03, // usage - vendor defined + 0x09,0x04, // usage - vendor defined + 0x15,0x80, // Logical Minimum (-128) + 0x25,0x7F, // Logical Maximum (127) + 0x35,0x00, // Physical Minimum (0) + 0x45,0xFF, // Physical Maximum (255) + 0x75,0x08, // Report Size (8) (bits) + 0x95,0x40, // Report Count (64) (fields) + 0x81,0x02, // Input (Data,Variable,Absolute) + + //The,output report + 0x09,0x05, // usage - vendor defined + 0x09,0x06, // usage - vendor defined + 0x15,0x80, // Logical Minimum (-128) + 0x25,0x7F, // Logical Maximum (127) + 0x35,0x00, // Physical Minimum (0) + 0x45,0xFF, // Physical Maximum (255) + 0x75,0x08, // Report Size (8) (bits) + 0x95,0x40, // Report Count (64) (fields) + 0x91,0x02, // Output (Data,Variable,Absolute) + + 0xC0, // End Collection + + 0xC0, // End Collection +}; + +static const BYTE DeviceDescriptor[] = { + 0x12, // Descriptor length (18 bytes) + 0x01, // Descriptor type (Device) + 0x10,0x01, // Complies with USB Spec. Release (0110h = release 1.10) + 0x00, // Class code (0) + 0x00, // Subclass code (0) + 0x00, // Protocol (No specific protocol) + 0x08, // Maximum packet size for Endpoint 0 (8 bytes) + 0xc4,0x9a, // Vendor ID (random numbers) + 0x8f,0x4b, // Product ID (random numbers) + 0x01,0x00, // Device release number (0001) + 0x01, // Manufacturer string descriptor index + 0x02, // Product string descriptor index + 0x00, // Serial Number string descriptor index (None) + 0x01, // Number of possible configurations (1) +}; + +static const BYTE ConfigurationDescriptor[] = { + 0x09, // Descriptor length (9 bytes) + 0x02, // Descriptor type (Configuration) + 0x29,0x00, // Total data length (41 bytes) + 0x01, // Interface supported (1) + 0x01, // Configuration value (1) + 0x00, // Index of string descriptor (None) + 0x80, // Configuration (Bus powered) + 250, // Maximum power consumption (500mA) + + //interface + 0x09, // Descriptor length (9 bytes) + 0x04, // Descriptor type (Interface) + 0x00, // Number of interface (0) + 0x00, // Alternate setting (0) + 0x02, // Number of interface endpoint (2) + 0x03, // Class code (HID) + 0x00, // Subclass code () + 0x00, // Protocol code () + 0x00, // Index of string() + + // class + 0x09, // Descriptor length (9 bytes) + 0x21, // Descriptor type (HID) + 0x00,0x01, // HID class release number (1.00) + 0x00, // Localized country code (None) + 0x01, // # of HID class dscrptr to follow (1) + 0x22, // Report descriptor type (HID) + // Total length of report descriptor + sizeof(HidReportDescriptor),0x00, + + // endpoint 1 + 0x07, // Descriptor length (7 bytes) + 0x05, // Descriptor type (Endpoint) + 0x01, // Encoded address (Respond to OUT) + 0x03, // Endpoint attribute (Interrupt transfer) + 0x08,0x00, // Maximum packet size (8 bytes) + 0x01, // Polling interval (1 ms) + + // endpoint 2 + 0x07, // Descriptor length (7 bytes) + 0x05, // Descriptor type (Endpoint) + 0x82, // Encoded address (Respond to IN) + 0x03, // Endpoint attribute (Interrupt transfer) + 0x08,0x00, // Maximum packet size (8 bytes) + 0x01, // Polling interval (1 ms) +}; + +static const BYTE StringDescriptor0[] = { + 0x04, // Length + 0x03, // Type is string + 0x09, // English + 0x04, // US +}; + +static const BYTE StringDescriptor1[] = { + 24, // Length + 0x03, // Type is string + 'J', 0x00, + '.', 0x00, + ' ', 0x00, + 'W', 0x00, + 'e', 0x00, + 's', 0x00, + 't', 0x00, + 'h', 0x00, + 'u', 0x00, + 'e', 0x00, + 's', 0x00, +}; + +static const BYTE StringDescriptor2[] = { + 54, // Length + 0x03, // Type is string + 'P', 0x00, + 'r', 0x00, + 'o', 0x00, + 'x', 0x00, + 'M', 0x00, + 'a', 0x00, + 'r', 0x00, + 'k', 0x00, + '-', 0x00, + '3', 0x00, + ' ', 0x00, + 'R', 0x00, + 'F', 0x00, + 'I', 0x00, + 'D', 0x00, + ' ', 0x00, + 'I', 0x00, + 'n', 0x00, + 's', 0x00, + 't', 0x00, + 'r', 0x00, + 'u', 0x00, + 'm', 0x00, + 'e', 0x00, + 'n', 0x00, + 't', 0x00, +}; + +static const BYTE * const StringDescriptors[] = { + StringDescriptor0, + StringDescriptor1, + StringDescriptor2, +}; + + +static BYTE UsbBuffer[64]; +static int UsbSoFarCount; + +static BYTE CurrentConfiguration; + +static void UsbSendEp0(const BYTE *data, int len) +{ + int thisTime, i; + + do { + thisTime = min(len, 8); + len -= thisTime; + + for(i = 0; i < thisTime; i++) { + UDP_ENDPOINT_FIFO(0) = *data; + data++; + } + + if(UDP_ENDPOINT_CSR(0) & UDP_CSR_TX_PACKET_ACKED) { + UDP_ENDPOINT_CSR(0) &= ~UDP_CSR_TX_PACKET_ACKED; + while(UDP_ENDPOINT_CSR(0) & UDP_CSR_TX_PACKET_ACKED) + ; + } + + UDP_ENDPOINT_CSR(0) |= UDP_CSR_TX_PACKET; + + do { + if(UDP_ENDPOINT_CSR(0) & UDP_CSR_RX_PACKET_RECEIVED_BANK_0) { + // This means that the host is trying to write to us, so + // abandon our write to them. + UDP_ENDPOINT_CSR(0) &= ~UDP_CSR_RX_PACKET_RECEIVED_BANK_0; + return; + } + } while(!(UDP_ENDPOINT_CSR(0) & UDP_CSR_TX_PACKET_ACKED)); + } while(len > 0); + + if(UDP_ENDPOINT_CSR(0) & UDP_CSR_TX_PACKET_ACKED) { + UDP_ENDPOINT_CSR(0) &= ~UDP_CSR_TX_PACKET_ACKED; + while(UDP_ENDPOINT_CSR(0) & UDP_CSR_TX_PACKET_ACKED) + ; + } +} + +static void UsbSendZeroLength(void) +{ + UDP_ENDPOINT_CSR(0) |= UDP_CSR_TX_PACKET; + + while(!(UDP_ENDPOINT_CSR(0) & UDP_CSR_TX_PACKET_ACKED)) + ; + + UDP_ENDPOINT_CSR(0) &= ~UDP_CSR_TX_PACKET_ACKED; + + while(UDP_ENDPOINT_CSR(0) & UDP_CSR_TX_PACKET_ACKED) + ; +} + +static void HandleRxdSetupData(void) +{ + int i; + UsbSetupData usd; + + for(i = 0; i < sizeof(usd); i++) { + ((BYTE *)&usd)[i] = UDP_ENDPOINT_FIFO(0); + } + + if(usd.bmRequestType & 0x80) { + UDP_ENDPOINT_CSR(0) |= UDP_CSR_CONTROL_DATA_DIR; + while(!(UDP_ENDPOINT_CSR(0) & UDP_CSR_CONTROL_DATA_DIR)) + ; + } + + UDP_ENDPOINT_CSR(0) &= ~UDP_CSR_RX_HAVE_READ_SETUP_DATA; + while(UDP_ENDPOINT_CSR(0) & UDP_CSR_RX_HAVE_READ_SETUP_DATA) + ; + + switch(usd.bRequest) { + case USB_REQUEST_GET_DESCRIPTOR: + if((usd.wValue >> 8) == USB_DESCRIPTOR_TYPE_DEVICE) { + UsbSendEp0((BYTE *)&DeviceDescriptor, + min(sizeof(DeviceDescriptor), usd.wLength)); + } else if((usd.wValue >> 8) == USB_DESCRIPTOR_TYPE_CONFIGURATION) { + UsbSendEp0((BYTE *)&ConfigurationDescriptor, + min(sizeof(ConfigurationDescriptor), usd.wLength)); + } else if((usd.wValue >> 8) == USB_DESCRIPTOR_TYPE_STRING) { + const BYTE *s = StringDescriptors[usd.wValue & 0xff]; + UsbSendEp0(s, min(s[0], usd.wLength)); + } else if((usd.wValue >> 8) == USB_DESCRIPTOR_TYPE_HID_REPORT) { + UsbSendEp0((BYTE *)&HidReportDescriptor, + min(sizeof(HidReportDescriptor), usd.wLength)); + } else { + *((DWORD *)0x00200000) = usd.wValue; + } + break; + + case USB_REQUEST_SET_ADDRESS: + UsbSendZeroLength(); + UDP_FUNCTION_ADDR = UDP_FUNCTION_ADDR_ENABLED | usd.wValue ; + if(usd.wValue != 0) { + UDP_GLOBAL_STATE = UDP_GLOBAL_STATE_ADDRESSED; + } else { + UDP_GLOBAL_STATE = 0; + } + break; + + case USB_REQUEST_GET_CONFIGURATION: + UsbSendEp0(&CurrentConfiguration, sizeof(CurrentConfiguration)); + break; + + case USB_REQUEST_GET_STATUS: { + if(usd.bmRequestType & 0x80) { + WORD w = 0; + UsbSendEp0((BYTE *)&w, sizeof(w)); + } + break; + } + case USB_REQUEST_SET_CONFIGURATION: + CurrentConfiguration = usd.wValue; + if(CurrentConfiguration) { + UDP_GLOBAL_STATE = UDP_GLOBAL_STATE_CONFIGURED; + UDP_ENDPOINT_CSR(1) = UDP_CSR_ENABLE_EP | + UDP_CSR_EPTYPE_INTERRUPT_OUT; + UDP_ENDPOINT_CSR(2) = UDP_CSR_ENABLE_EP | + UDP_CSR_EPTYPE_INTERRUPT_IN; + } else { + UDP_GLOBAL_STATE = UDP_GLOBAL_STATE_ADDRESSED; + UDP_ENDPOINT_CSR(1) = 0; + UDP_ENDPOINT_CSR(2) = 0; + } + UsbSendZeroLength(); + break; + + case USB_REQUEST_GET_INTERFACE: { + BYTE b = 0; + UsbSendEp0(&b, sizeof(b)); + break; + } + + case USB_REQUEST_SET_INTERFACE: + UsbSendZeroLength(); + break; + + case USB_REQUEST_CLEAR_FEATURE: + case USB_REQUEST_SET_FEATURE: + case USB_REQUEST_SET_DESCRIPTOR: + case USB_REQUEST_SYNC_FRAME: + default: + break; + } +} + +void UsbSendPacket(BYTE *packet, int len) +{ + int i, thisTime; + + while(len > 0) { + thisTime = min(len, 8); + + for(i = 0; i < thisTime; i++) { + UDP_ENDPOINT_FIFO(2) = packet[i]; + } + UDP_ENDPOINT_CSR(2) |= UDP_CSR_TX_PACKET; + + while(!(UDP_ENDPOINT_CSR(2) & UDP_CSR_TX_PACKET_ACKED)) + ; + UDP_ENDPOINT_CSR(2) &= ~UDP_CSR_TX_PACKET_ACKED; + + while(UDP_ENDPOINT_CSR(2) & UDP_CSR_TX_PACKET_ACKED) + ; + + len -= thisTime; + packet += thisTime; + } +} + +static void HandleRxdData(void) +{ + int i, len; + + if(UDP_ENDPOINT_CSR(1) & UDP_CSR_RX_PACKET_RECEIVED_BANK_0) { + len = UDP_CSR_BYTES_RECEIVED(UDP_ENDPOINT_CSR(1)); + + for(i = 0; i < len; i++) { + UsbBuffer[UsbSoFarCount] = UDP_ENDPOINT_FIFO(1); + UsbSoFarCount++; + } + + UDP_ENDPOINT_CSR(1) &= ~UDP_CSR_RX_PACKET_RECEIVED_BANK_0; + while(UDP_ENDPOINT_CSR(1) & UDP_CSR_RX_PACKET_RECEIVED_BANK_0) + ; + + if(UsbSoFarCount >= 64) { + UsbPacketReceived(UsbBuffer, UsbSoFarCount); + UsbSoFarCount = 0; + } + } + + if(UDP_ENDPOINT_CSR(1) & UDP_CSR_RX_PACKET_RECEIVED_BANK_1) { + len = UDP_CSR_BYTES_RECEIVED(UDP_ENDPOINT_CSR(1)); + + for(i = 0; i < len; i++) { + UsbBuffer[UsbSoFarCount] = UDP_ENDPOINT_FIFO(1); + UsbSoFarCount++; + } + + UDP_ENDPOINT_CSR(1) &= ~UDP_CSR_RX_PACKET_RECEIVED_BANK_1; + while(UDP_ENDPOINT_CSR(1) & UDP_CSR_RX_PACKET_RECEIVED_BANK_1) + ; + + if(UsbSoFarCount >= 64) { + UsbPacketReceived(UsbBuffer, UsbSoFarCount); + UsbSoFarCount = 0; + } + } +} + +void UsbStart(void) +{ + volatile int i; + + UsbSoFarCount = 0; + + USB_D_PLUS_PULLUP_OFF(); + + for(i = 0; i < 1000000; i++) + ; + + USB_D_PLUS_PULLUP_ON(); + + if(UDP_INTERRUPT_STATUS & UDP_INTERRUPT_END_OF_BUS_RESET) { + UDP_INTERRUPT_CLEAR = UDP_INTERRUPT_END_OF_BUS_RESET; + } +} + +BOOL UsbPoll(BOOL blinkLeds) +{ + BOOL ret = FALSE; + + if(UDP_INTERRUPT_STATUS & UDP_INTERRUPT_END_OF_BUS_RESET) { + UDP_INTERRUPT_CLEAR = UDP_INTERRUPT_END_OF_BUS_RESET; + + // following a reset we should be ready to receive a setup packet + UDP_RESET_ENDPOINT = 0xf; + UDP_RESET_ENDPOINT = 0; + + UDP_FUNCTION_ADDR = UDP_FUNCTION_ADDR_ENABLED; + + UDP_ENDPOINT_CSR(0) = UDP_CSR_EPTYPE_CONTROL | UDP_CSR_ENABLE_EP; + + CurrentConfiguration = 0; + + ret = TRUE; + } + + if(UDP_INTERRUPT_STATUS & UDP_INTERRUPT_ENDPOINT(0)) { + if(UDP_ENDPOINT_CSR(0) & UDP_CSR_RX_HAVE_READ_SETUP_DATA) { + HandleRxdSetupData(); + ret = TRUE; + } + } + + if(UDP_INTERRUPT_STATUS & UDP_INTERRUPT_ENDPOINT(1)) { + HandleRxdData(); + ret = TRUE; + } + + return ret; +} diff --git a/doc/CHANGES.TXT b/doc/CHANGES.TXT new file mode 100644 index 00000000..b209cc03 --- /dev/null +++ b/doc/CHANGES.TXT @@ -0,0 +1,155 @@ +################ +## 2009/03/28 ## +################ +winsrc/command.cpp + Added two new LF commands for tag exploration : + + - askdemod: takes 2 arguments, one is the clock rate, one is the modulation + convention (high mod is 1 or high mod is zero) + + This command demodulates the stream into a binary stream into + the trace buffer (0's and 1's) + + - mandemod: manchester decoding of a bitstream: takes a binary stream from + the trace buffer (see askdemod) and attempts to do manchester decoding + to it. One argument: clock rate. Outputs the bitstream to the scrollback buffer. + + Those two helped me to validate that the unknown tag I had was indeed an EM4100 type of tag + + +################ +## 2008/12/11 ## +################ +bootrom/bootrom.c + Significant changes to bootloader. Use of Chip ID register to detect if running on a SAM7S512 then configure FLASH + waitstates as per SummoningDark's suggestion for a SAM7S512 or SAM7S256. + Deleted idle loops waiting blindly for clocks to settle and now using status registers to detect when clocks are stable. + + ************************* + * IMPORTANT INFORMATION * + ************************************************************************************************************************** + * With this boot code, the device can now only be flashed if button is held down after power on or a software reset. + * The flash procedure is this: + * Hold down button. Either plug in USB or software reset it. _While_holding_down_button_ (red and yellow LEDs are lit) you can + * issue one or more of the "prox bootrom " "prox fpga " "prox load ", be sure to hold button down for the + * entire duration of the flash process. Only release the button when flashing is complete and you want to let the board boot. + * This process may be less convenient but it's safer and avoids "unintentional" flashing of the board. + ************************************************************************************************************************** + LED boot sequence now changed, C (red) lights up when boot code jumps from flash to RAM boot code, A (yellow) lights up after + clocks have been initialized, B (green) lights up when jumping from boot code to main code, then D (red led away from the others) + lights up while code is being downloaded to FPGA, then all leds turn off and board is ready for action. + + With these changes the board now boots and is ready to use in about 3 seconds. Also since the USB bus is not initialized + twice (once during boot, then again when the main code runs) unless the button is held down at boot, this seems to avoid + the double USB connect and "USB device not recognized" when device is connected to the USB bus or software reset. + +################ +## 2008/12/06 ## +################ +armsrc/fpga.c + Implemented function SetupSpi() to initialize the Serial Peripheral Interface (SPI) in preparation to adding an LCD to the board. + Changed FpgaWriteConfWord() to use the SPI communication now instead of bit banging the serial data to the FPGA. + +fpga/fpga.v + The FPGA config word serializer required non standard SPI communication (ie for shifting in a 8 bit word, it required a 9th clock + cycle with NCS high to load the word from the shift register to the conf register). This was OK for manually bitbanging it but not + suitable for using SPI comms. The serializer was fixed to load the conf word from the shift register on a NCS lo-hi transition and + not require additional clocking. + +armsrc/fpgaimg.c + Recompiled FPGA code after changes above. + +armsrc/LCD.c + LCD driver for PCF8833 based LCDs like those found on Nokia models 2600,2650,3100,3120,5140,6030,6100,6610,7210,7250 maybe + others. These color LCDs have a resolution of 132x132 and a serial interface. They are very cheap like even down to $2/pc + This LCD driver is a straight rip of that found at http://www.sparkfun.com/datasheets/LCD/Jimbo-Nokia-SAM7-Example.zip with + very small changes, mainly to integrate it and make it compile with our codebase. Also comented out the circle subroutines + to keep the code to integer math only. + +armsrc/fonts.c + Font definition for LCD driver + +armsrc/appmain.c + Fixed a small bug in CmdHIDdemodFSK (added case 4) which prevented reading some tags. When a logic 0 is immediately followed + by the start of the next transmisson (special pattern) a pattern of 4 bit duration lengths is created. + +################ +## 2008/11/27 ## +################ +armsrc/appmain.c + Implemented an HID tag FSK demodulator (CmdHIDdemodFSK) to obtain the tag ID code from the raw sampled waveform. + Implemented CmdHIDsimTAG which takes a 44bit HID tag ID as a hex number then creates the waveform and simulates the tag + +winsrc/command.cpp + Added command "hidfskdemod" that calls CmdHIDdemodFSK, the ARM FSK demodulator for HID tags. + +include/usb-cmd.h + New defines CMD_HID_DEMOD_FSK and CMD_HID_SIM_TAG + +2008/11/25 +common/iso14443_crc.c + Moved CRC calculation code into this file as it's common to both ARM and Windows side. This file is now included as needed. + +################ +## 2008/11/21 ## +################ +armsrc/Makefile + Changes to split up the compilation of the ARM and produce separate S files for the FPGA code and the ARM code. + +armsrc/appmain.c + Replaced some of the hex value params in FpgaWriteConfWord with more explanatory defines. + Changes to the Tune command as it assumes wrong HF capacitor value (130pF) and produces wrong voltage readings. + Combined some of the integer arithmetic statements to improve accuracy slightly, since the voltage divider ratio is not an integer. + Voltage divider resistor network is 10M/240k = ratio of 41.6666 + + Originally the calculation was rounding the ratio down to 41 + 3300 (mV) * 41 * sample_value / 1024 + New calculation without rounding error is + 3300 (mV) * 41.66666 * sample_value / 1024 => 137500 * sample_value / 1024 + + New define BUTTON_PRESS() returns status of button + +armsrc/fpga.c + The current board can only take a X2S30 as there is no larger FPGA in PQFP100 package and + the smaller X2S15 FPGA can't fit the current code. The X2S30 FPGA config is fixed at 336,768 bits + The FPGA code base address and length is hard coded to occupy FLASH region 0x2000 - 0xC470. + +armsrc/ldscript-fpga + New file to place the FPGA code at FLASH address 0x2000 + +bootrom/Makefile + Slight changes, commented out the generation of byteswapped S file, the other S files are generated in the same section of the makefile now. + +bootrom/bootrom.c + Changed some thumb code with a one line ARM code which is clearer and more explicit. Processor runs in ARM mode at reset anyway. + Changed jump to RAM address, used to jump to 0x2000 (now FPGA area), now jumps to 0x10000. + +bootrom/flash-reset.s + Changed name of CMain to CopyBootToRAM. Streamlined reset code, fixed up stack pointer initialization. + +bootrom/fromflash.c + Removed the whole section of initializing clocks, this is redundant as it's being done once we jump to boot code in RAM + All fromflash.c does now is copy the boot code to ram and jumps to it. + +bootrom/ram-reset.s + Fixed up stack pointer initialization that caused crash when using "loread" + +include/at91sam7s128.h + New defines for debug register, lets you identify what processor flavour the code runs on, RAM and FLASH sizes, etc. + +include/proxmark3.h + New useful defines for relay and button + +winsrc/Makefile + Added new define /D_CRT_SECURE_NO_WARNINGS to elliminate a _whole bunch_ of bogus compilation warnings + +winsrc/command.cpp + Changed CmdLosamples to take a numeric argument (number of samples x4 to retrieve from buffer) + New command Quit to exit the program from the GUI command prompt. + +winsrc/gui.cpp + Fixup compilation warnings. + +winsrc/prox.cpp + Tidy up printing to stdout, flashing progress now updates on the same line instead of scrolling up. + New command line parameter to load FPGA image to FLASH. diff --git a/doc/README.TXT b/doc/README.TXT new file mode 100644 index 00000000..f3b49c77 --- /dev/null +++ b/doc/README.TXT @@ -0,0 +1,39 @@ +This is a bare minimum compile environment for the proxmark3 sources. + +CONTENTS + +This bundle contains the ARM cross compiler in devkitARM and a _tiny_ subset +of the Visual C++ 2008 Express Edition in devkitWIN which is the bare minimum +required for compilation of this current source. + +If you plan on further source code development you are strongly encouraged +to download the full Visual C++ 2008 available for free download from +http://www.microsoft.com/express/download/ + +CAVEATS + +There is no provision in this environment for compiling the FPGA source. To +do that you need to download the free (registration required) ISE WebPack +from Xilinx at http://www.xilinx.com/ise/logic_design_prod/webpack.htm +Be warned, the pack is huge, 2Gb download and >4Gb installed. + +USAGE + +First of all run the .msi file in devkitWIN\vcredist_x86 to install the VC++ +redistributables, without these, nmake, cl and link won't run. + +Get a command prompts in the cockpit directory and pretty much run the batch +files in the order they appear: + +0setpath.bat - sets the environment vars for the compile environment +1makearm.bat - compiles the files in armsrc, output files in armsrc\obj +2makeboot.bat - compiles the files in bootrom, output files in bootrom\obj +3makewin.bat - compiles the files in winsrc, output files in winsrc\obj +4flashos.bat - attempts to upload the OS image to the proxmark3 board + +ACKNOWLEDGMENTS + +Thanks to J Westhues for the original proxmark, Roel and the proxmark.org +community. This pack may contain F/OSS or free but copyrighted software +from Xilinx, Microsoft and others. All trademarks are the property of +their respective owners. All rights reserved. diff --git a/doc/component-placement.bmp b/doc/component-placement.bmp new file mode 100644 index 00000000..d3854310 Binary files /dev/null and b/doc/component-placement.bmp differ diff --git a/doc/proxmark3.pdf b/doc/proxmark3.pdf new file mode 100644 index 00000000..32f34770 Binary files /dev/null and b/doc/proxmark3.pdf differ diff --git a/doc/proxmark3.xls b/doc/proxmark3.xls new file mode 100644 index 00000000..c13169cb Binary files /dev/null and b/doc/proxmark3.xls differ diff --git a/doc/schematics.pdf b/doc/schematics.pdf new file mode 100644 index 00000000..8ed8470b Binary files /dev/null and b/doc/schematics.pdf differ diff --git a/doc/system.txt b/doc/system.txt new file mode 100644 index 00000000..a3ae8605 --- /dev/null +++ b/doc/system.txt @@ -0,0 +1,276 @@ + +This is outdated. + +--- + +INTRODUCTION TO THE proxmark3 +============================= + +The proxmark3 device is designed to manipulate RFID tags in a number of +different ways. For example, a proxmark3 can: + + * read a low-frequency (~100 kHz) or high-frequency (13.56 MHz) tag, + including the ISO-standard tags; standards that require + bidirectional communication between the reader and the tag are + not a problem + + * emulate a low- or high-frequency tag, in a way very similar to the + way that a real tag behaves (e.g., it derives its timing from the + incident carrier) + + * eavesdrop on the signals exchanged between another reader and tag + + * measure the resonant frequency of an antenna, to a certain extent + (this is a convenience when building a test setup for the previous + three functions) + +The proxmark3 may be thought of as a direct-sampling software radio. +There is some complication, though, because of the usual dynamic range +issue in dealing with signals in RFID systems (large signal due to +the reader, small signal due to the tag). Some analog processing is +therefore used to fix this before the signal is digitized. (Although, +it is possible to digitize the signal from the antenna directly, with +appropriate population options. It is just not usually a good idea.) + +SYSTEM ARCHITECTURE +=================== + +The ANTENNA sends and receives signals over the air. It is external to +the board; it connects through SV2. Separate pins on the connector are +used for the low- and high-frequency antennas, and the analog receive +paths are separate. The antennas are inductive loops, which are resonated +by on-board capacitors. + +On the transmit side, the antennas are excited by large numbers of +paralleled bus driver buffers. By tri-stating some of the buffers, it +is possible to vary the transmit strength. This may be used to generate +a modulated carrier. The buffers are driven by signals from the FPGA, +as are the output enables. The antennas are excited as series circuits, +which permits a large input power for a relatively small input voltage. + +By driving all of the buffers low, it is possible to make the antenna +look to the receive path like a parallel LC circuit; this provides a +high-voltage output signal. This is typically what will be done when we +are not actively transmitting a carrier (i.e., behaving as a reader). + +On the receive side, there are two possibilities, which are selected by +RLY1. A mechanical relay is used, because the signal from the antenna is +likely to be more positive or negative than the highest or lowest supply +voltages on-board. In the usual case (PEAK-DETECTED mode), the received +signal is peak-detected by an analog circuit, then filtered slightly, +and then digitized by the ADC. This is the case for both the low- and +high-frequency paths, although the details of the circuits for the +two cases are somewhat different. This receive path would typically +be selected when the device is behaving as a reader, or when it is +eavesdropping at close range. + +It is also possible to digitize the signal from the antenna directly (RAW +mode), after passing it through a gain stage. This is more likely to be +useful in reading signals at long range, but the available dynamic range +will be poor, since it is limited by the 8-bit A/D. These modes would be +very appropriate, for example, for the heavily-discussed attacks in which +a tag's ID is learned from the data broadcast by a reader performing an +anticollision loop, because there is no dynamic range problem there. It +would also be possible to program the proxmark3 to receive broadcast AM +radio, with certain changes in component values. + +In either case, an analog signal is digitized by the ADC (IC8), and +from there goes in to the FPGA (IC1). The FPGA is big enough that it +can perform DSP operations itself. For some high-frequency standards, +the subcarriers are fast enough that it would be inconvenient to do all +the math on a general-purpose CPU. The FPGA can therefore correlate for +the desired signal itself, and simply report the total to the ARM. For +low-frequency tags, it probably makes sense just to pass data straight +through to the ARM. + +The FPGA communicates with the ARM through either its SPI port (the ARM +is the master) or its generic synchronous serial port (again, the ARM +is the master). The ARM connects to the outside world over USB. + +DETAILS: POWER DISTRIBUTION +=========================== + +I make a half-hearted attempt to meet the USB power specs; this adds a +bit of complexity. I have not made measurements to determine how close +I come to succeeding, but I think that the suspend current might turn +out to be a pain. + +The +3V3 rail is always powered, whenever we are plugged in to USB. This +is generated by an LDO, which burns a quiescent current of 150 uA +(typical) already. The only thing powered from the +3V3 rail is the ARM, +which can presumably do smart power control when we are in suspend. + +The ARM generates two signals to switch power to the rest of the board: +FPGA_ON, and NVDD_ON. When NVDD_ON goes low, the Vdd rail comes up to +about five volts (the filtered-but-unregulated USB voltage). This powers +most of the analog circuitry, including the ADC and all of the opamps +and comparators in the receive path, and the coil drivers as well. Vdd +also feeds the +3V3-FPGA and +2v5 regulators, which power only the +FPGA. These regulators are enabled by FPGA_ON, so the FPGA is powered +only when NVDD_ON is asserted low, and FPGA_ON is asserted high. + +DETAILS: FPGA +============= + +The FPGA is a Spartan-II. This is a little bit old, but it is widely +available, inexpensive, and five-volt tolerant. For development, the FPGA +is configured over JTAG (SV5). In operation, the FPGA is configured in +slave serial mode by the ARM, from a bitstream stored in the ARM's flash. + +Power to the FPGA is managed by regulators IC13 and IC12, both of which +have shutdown. These generate the FPGA's VCCO (+3v3) and VCCINT (+2v5) +supplies. I am a little bit worried about the power-on surge, since we +run off USB. At the very minimum, the FPGA should not get power until +we have enumerated and requested the full 500 mA available from USB. The +large electrolytic capacitors C37 and C38 will presumably help with this. + +The logic is written in Verilog, of course for webpack. I have structured +the FPGA in terms of `major modes:' the FPGA's `major mode' determines +which of several modules is connected to the FPGA's I/O pins. A separate +module is used for each of the FPGA's function; for example, there is +now a module to read a 125 kHz tag, simulate a 125 kHz tag, transmit to +an ISO 15693 tag, and receive from an ISO 15693 tag. + +DETAILS: ANALOG RECEIVE PATH +============================ + +For `slow' signals, I use an MCP6294 opamp. This has a GBW of 10 MHz, +which is more than enough for the low-frequency stuff, and enough for +all of the subcarrier frequencies that I know of at high frequency. In +practice, the `slow' signals are all the signals following the peak +detector. These signals are usually centred around the generated +voltage Vmid. + +For `fast' signals, I use an AD8052. This is a very fast voltage-feedback +amplifier (~100 MHz GBW). I use it immediately after the antenna for +both the low- and high-frequency cases, as a sort of an ugly LNA. It is +not optimal, but it certainly made the design easy. + +An ordinary CD4066 is used to multiplex the four possible signals +(low/high frequency paths, RAW/PEAK-DETECTED). There is a potential +problem at startup, when the ARM is in reset; there are pull-ups on the +lines that control the mux, so all of the switches turn on. This shorts +the four opamp outputs together through the on-resistance of the switch. +All four outputs float to the same DC voltage with no signal, however, +and the on-resistance of the switches is fairly large, so I don't think +that will be a problem in practice. + +Comparators are used to generate clock signals when the device is +emulating a tag. These clock signals are generated from the signal on the +antenna, and therefore from the signal transmitted by the reader. This +allows us to clock ourselves off the reader, just like a real tag would. +These signals go in to the FPGA. There is a potential problem when the +FPGA is powered down; these outputs might go high and try to power the +FPGA through the protection diodes. My present solution to this is a +couple of resistors, which is not very elegeant. + +The high-frequency peak-detected receive path contains population options +for many features that I do not currently use. A lot of these are just +me guessing that if I provide options for different series and shunt +passives, perhaps it will come in handy in some way. The Zener diodes D10 +and D11 are optional, but may protect the front end from an overvoltage +(which will fry the peak detector diodes) when the `simulated tag' +is read by a powerful reader. + +DETAILS: ANALOG TRANSMIT PATH +============================= + +The coil drivers are just ACT244 bus buffers. I parallel eight of them +for each antenna (eight for the high-frequency antenna, eight for the +low-frequency antenna). This should easily provide a hundred milliamps +coil drive or so, which is more than enough for anything that I imagine +doing with the device. The drivers hit the coil with a square wave +voltage, however, which means that it is only the bandpass filter effect +of a resonant antenna that suppresses the odd harmonics. In practice it +would probably take heroic efforts (high antenna Q) to meet the FCC/CE +harmonic specs; and in practice no one cares. + +The tx strength, given good antenna tuning, is determined by the series +resistors. Choose the ratios to stay within the rated current of the +buffers, and to achieve the desired power ratios by enabling or disabling +nOEs for the desired modulation index. It is useful to populate one of the +resistors as a high value (~10k) for the simulated tag modes; this allows +us to look at the incident carrier without loading the reader very much. + +DETAILS: ARM +============ + +Atmel makes a number of pin-compatible ARMs, with slightly different +peripherals, and different amounts of flash and RAM. It is necessary +to choose a device with enough flash not just for the ARM's program, +but also for the FPGA image (which is loaded by the ARM). + +The ARM is responsible for programming the FPGA. It also supplies a +clock to the FPGA (although the FPGA clock can also run off the 13.56 +MHz clock not used for anything else, which is obviously asynchronous +to anything in the ARM). + +It is necessary to use JTAG to bring the ARM for the first time; at +that point you can load a bootrom, and subsequently load new software +over USB. It might be possible to use the ARM's pre-loaded bootloader +(see datasheet) instead of JTAG, but I wanted the JTAG anyways for +debugging, so I did not bother. I used a Wiggler clone, with Macraigor's +OCD Commander. More expensive tools would work as well. + +USB SOFTWARE +============ + +At present I enumerate as an HID device. This saves me writing a driver, +but it forces me to do interrupt transfers for everything. This limits +speed and is not very elegant. A real USB driver would be nice, maybe +even one that could do stuff like going isochronous to stream samples +from the A/D for processing on the PC. + +PRETENDING TO BE A TAG +====================== + +It is not possible, with the given topology, to open-circuit the antenna +entirely and still look at the signal received on it. The simulated tag +modes must therefore switch between slight loading and heavy loading, +not open- and short-circuts across the antenna, evening though they do +not depend upon the incident carrier for power (just timing information). + +RECEIVING SIGNAL STRAIGHT FROM THE ANTENNAS +=========================================== + +There is a path straight from the antenna to the A/D, bypassing the peak +detector assembly. This goes through a gain stage (just a fast voltage +feedback opamp), and from there straight in to the mux. + +It is necessary to energize the relay to connect these paths. If the +coil is driven (as if to excite and read a tag) while these paths are +connected, then damage will probably result. Most likely the opamp +will fry. + +READING A TAG +============= + +The tag is excited by a carrier transmitted by the reader. This is +generated by IC9 and IC10, using some combination of buffers. The transmit +power is determined by selecting the right combination of PWR_OEx pins; +drive more of them low for more power. This can be used to modulate the +transmitted signal, and thus send information to the tag. + +The received signal from the antenna is first peak-detected, and then +high-pass filtered to reject the unmodulated carrier. The signal is +amplified a bit, and goes in to the A/D mux from there. The A/D is +controlled by the FPGA. For 13.56 MHz tags, it is easiest to do everything +synchronous to the 13.56 MHz carrier. + +INTERFACE FROM THE ARM TO THE FPGA +================================== + +The FPGA and the ARM can communicate in two main ways: using the ARM's +general-purpose synchronous serial port (the SSP), or using the ARM's +SPI port. The SPI port is used to configure the FPGA. The ARM writes a +configuration word to the FPGA, which determines what operation will +be performed (e.g. read 13.56 MHz vs. read 125 kHz vs. read 134 kHz +vs...). The SPI is used exclusively for configuration. + +The SSP is used for actual data sent over the air. The ARM's SSP can +work in slave mode, which means that we can send the data using clocks +generated by the FPGA (either from the PCK0 clock, which the ARM itself +supplies, or from the 13.56 MHz clock, which is certainly not going to +be synchronous to anything in the ARM), which saves synchronizing logic +in the FPGA. The SSP is bi-directional and full-duplex. + diff --git a/fpga/fpga.mpf b/fpga/fpga.mpf new file mode 100644 index 00000000..4a281bca --- /dev/null +++ b/fpga/fpga.mpf @@ -0,0 +1,239 @@ +; +; Copyright Model Technology, a Mentor Graphics +; Corporation company 2003, - All rights reserved. +; +[Library] +std = $MODEL_TECH/../std +ieee = $MODEL_TECH/../ieee +verilog = $MODEL_TECH/../verilog +vital2000 = $MODEL_TECH/../vital2000 +std_developerskit = $MODEL_TECH/../std_developerskit +synopsys = $MODEL_TECH/../synopsys +modelsim_lib = $MODEL_TECH/../modelsim_lib + + +; VHDL Section +unisim = $MODEL_TECH/../xilinx/vhdl/unisim +simprim = $MODEL_TECH/../xilinx/vhdl/simprim +xilinxcorelib = $MODEL_TECH/../xilinx/vhdl/xilinxcorelib +aim = $MODEL_TECH/../xilinx/vhdl/aim +pls = $MODEL_TECH/../xilinx/vhdl/pls +cpld = $MODEL_TECH/../xilinx/vhdl/cpld + +; Verilog Section +unisims_ver = $MODEL_TECH/../xilinx/verilog/unisims_ver +uni9000_ver = $MODEL_TECH/../xilinx/verilog/uni9000_ver +simprims_ver = $MODEL_TECH/../xilinx/verilog/simprims_ver +xilinxcorelib_ver = $MODEL_TECH/../xilinx/verilog/xilinxcorelib_ver +aim_ver = $MODEL_TECH/../xilinx/verilog/aim_ver +cpld_ver = $MODEL_TECH/../xilinx/verilog/cpld_ver + +work = work +[vcom] +; Turn on VHDL-1993 as the default. Normally is off. +VHDL93 = 1 + +; Show source line containing error. Default is off. +; Show_source = 1 + +; Turn off unbound-component warnings. Default is on. +; Show_Warning1 = 0 + +; Turn off process-without-a-wait-statement warnings. Default is on. +; Show_Warning2 = 0 + +; Turn off null-range warnings. Default is on. +; Show_Warning3 = 0 + +; Turn off no-space-in-time-literal warnings. Default is on. +; Show_Warning4 = 0 + +; Turn off multiple-drivers-on-unresolved-signal warnings. Default is on. +; Show_Warning5 = 0 + +; Turn off optimization for IEEE std_logic_1164 package. Default is on. +; Optimize_1164 = 0 + +; Turn on resolving of ambiguous function overloading in favor of the +; "explicit" function declaration (not the one automatically created by +; the compiler for each type declaration). Default is off. + Explicit = 1 + +; Turn off VITAL compliance checking. Default is checking on. +; NoVitalCheck = 1 + +; Ignore VITAL compliance checking errors. Default is to not ignore. +; IgnoreVitalErrors = 1 + +; Turn off VITAL compliance checking warnings. Default is to show warnings. +; Show_VitalChecksWarnings = false + +; Turn off "loading..." messages. Default is messages on. +; Quiet = 1 + +; Turn on some limited synthesis rule compliance checking. Checks only: +; -- signals used (read) by a process must be in the sensitivity list +; CheckSynthesis = 1 + +[vlog] + +; Turn off "loading..." messages. Default is messages on. +; Quiet = 1 + +; Turn on Verilog hazard checking (order-dependent accessing of global vars). +; Default is off. +; Hazard = 1 + +; Turn on converting regular Verilog identifiers to uppercase. Allows case +; insensitivity for module names. Default is no conversion. +; UpCase = 1 + +; Turns on incremental compilation of modules +; Incremental = 1 + +[vsim] +; Simulator resolution +; Set to fs, ps, ns, us, ms, or sec with optional prefix of 1, 10, or 100. +Resolution = ps + +; User time unit for run commands +; Set to default, fs, ps, ns, us, ms, or sec. The default is to use the +; unit specified for Resolution. For example, if Resolution is 100ps, +; then UserTimeUnit defaults to ps. +UserTimeUnit = default + +; Default run length +RunLength = 100 + +; Maximum iterations that can be run without advancing simulation time +IterationLimit = 5000 + +; Directive to license manager: +; vhdl Immediately reserve a VHDL license +; vlog Immediately reserve a Verilog license +; plus Immediately reserve a VHDL and Verilog license +; nomgc Do not look for Mentor Graphics Licenses +; nomti Do not look for Model Technology Licenses +; noqueue Do not wait in the license queue when a license isn't available +; License = plus + +; Stop the simulator after an assertion message +; 0 = Note 1 = Warning 2 = Error 3 = Failure 4 = Fatal +BreakOnAssertion = 3 + +; Assertion Message Format +; %S - Severity Level +; %R - Report Message +; %T - Time of assertion +; %D - Delta +; %I - Instance or Region pathname (if available) +; %% - print '%' character +; AssertionFormat = "** %S: %R\n Timf: %T Iteration: %D%I\n" + +; Assertion File - alternate file for storing assertion messages +; AssertFile = assert.log + +; Default radix for all windows and commands... +; Set to symbolic, ascii, binary, octal, decimal, hex, unsigned +DefaultRadix = symbolic + +; VSIM Startup command +; Startup = do startup.do + +; File for saving command transcript +TranscriptFile = transcript + +; File for saving command history +;CommandHistory = cmdhist.log + +; Specify whether paths in simulator commands should be described +; in VHDL or Verilog format. For VHDL, PathSeparator = / +; for Verilog, PathSeparator = . +PathSeparator = / + +; Specify the dataset separator for fully rooted contexts. +; The default is ':'. For example, sim:/top +; Must not be the same character as PathSeparator. +DatasetSeparator = : + +; Disable assertion messages +; IgnoreNote = 1 +; IgnoreWarning = 1 +; IgnoreError = 1 +; IgnoreFailure = 1 + +; Default force kind. May be freeze, drive, or deposit +; or in other terms, fixed, wired or charged. +; DefaultForceKind = freeze + +; If zero, open files when elaborated +; else open files on first read or write +; DelayFileOpen = 0 + +; Control VHDL files opened for write +; 0 = Buffered, 1 = Unbuffered +UnbufferedOutput = 0 + +; Control number of VHDL files open concurrently +; This number should always be less then the +; current ulimit setting for max file descriptors +; 0 = unlimited +ConcurrentFileLimit = 40 + +; This controls the number of hierarchical regions displayed as +; part of a signal name shown in the waveform window. The default +; value or a value of zero tells VSIM to display the full name. +; WaveSignalNameWidth = 0 + +; Turn off warnings from the std_logic_arith, std_logic_unsigned +; and std_logic_signed packages. +; StdArithNoWarnings = 1 + +; Turn off warnings from the IEEE numeric_std and numeric_bit +; packages. +; NumericStdNoWarnings = 1 + +; Control the format of a generate statement label. Don't quote it. +; GenerateFormat = %s__%d + +; Specify whether checkpoint files should be compressed. +; The default is to be compressed. +; CheckpointCompressMode = 0 + +; List of dynamically loaded objects for Verilog PLI applications +; Veriuser = veriuser.sl + +[lmc] +[Project] +Project_Version = 5 +Project_DefaultLib = work +Project_SortMethod = unused +Project_Files_Count = 13 +Project_File_0 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/fpga_tb.v +Project_File_P_0 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1179836462 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 2 dont_compile 0 +Project_File_1 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/hi_simulate.v +Project_File_P_1 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1225963633 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 compile_to work vlog_upper 0 vlog_options {} compile_order 6 dont_compile 0 +Project_File_2 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/testbed_hi_simulate.v +Project_File_P_2 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1225964050 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 compile_to work vlog_upper 0 vlog_options {} compile_order 12 dont_compile 0 +Project_File_3 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/fpga.v +Project_File_P_3 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1207888760 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 1 dont_compile 0 +Project_File_4 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/hi_read_tx.v +Project_File_P_4 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1225960972 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 5 dont_compile 0 +Project_File_5 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/testbed_hi_read_tx.v +Project_File_P_5 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1225962515 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 11 dont_compile 0 +Project_File_6 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/hi_iso14443a.v +Project_File_P_6 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1207889732 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 3 dont_compile 0 +Project_File_7 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/lo_simulate.v +Project_File_P_7 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1179836462 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 8 dont_compile 0 +Project_File_8 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/lo_read.v +Project_File_P_8 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1225797126 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 7 dont_compile 0 +Project_File_9 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/util.v +Project_File_P_9 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1179836462 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 0 dont_compile 0 +Project_File_10 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/testbed_lo_read.v +Project_File_P_10 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1225960239 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 9 dont_compile 0 +Project_File_11 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/testbed_lo_simulate.v +Project_File_P_11 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1225960231 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 10 dont_compile 0 +Project_File_12 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/hi_read_rx_xcorr.v +Project_File_P_12 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1179836462 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 4 dont_compile 0 +Project_Sim_Count = 0 +Project_Folder_Count = 0 diff --git a/fpga/fpga.ucf b/fpga/fpga.ucf new file mode 100644 index 00000000..bf0d40bc --- /dev/null +++ b/fpga/fpga.ucf @@ -0,0 +1,41 @@ +# See the schematic for the pin assignment. + +NET "adc_d<0>" LOC = "P62" ; +NET "adc_d<1>" LOC = "P60" ; +NET "adc_d<2>" LOC = "P58" ; +NET "adc_d<3>" LOC = "P57" ; +NET "adc_d<4>" LOC = "P56" ; +NET "adc_d<5>" LOC = "P55" ; +NET "adc_d<6>" LOC = "P54" ; +NET "adc_d<7>" LOC = "P53" ; +#NET "cross_hi" LOC = "P88" ; +#NET "miso" LOC = "P40" ; +#PACE: Start of Constraints generated by PACE + +#PACE: Start of PACE I/O Pin Assignments +NET "adc_clk" LOC = "P46" ; +NET "adc_noe" LOC = "P47" ; +NET "ck_1356meg" LOC = "P91" ; +NET "ck_1356megb" LOC = "P93" ; +NET "cross_lo" LOC = "P87" ; +NET "dbg" LOC = "P22" ; +NET "mosi" LOC = "P43" ; +NET "ncs" LOC = "P44" ; +NET "pck0" LOC = "P36" ; +NET "pwr_hi" LOC = "P80" ; +NET "pwr_lo" LOC = "P81" ; +NET "pwr_oe1" LOC = "P82" ; +NET "pwr_oe2" LOC = "P83" ; +NET "pwr_oe3" LOC = "P84" ; +NET "pwr_oe4" LOC = "P86" ; +NET "spck" LOC = "P39" ; +NET "ssp_clk" LOC = "P71" ; +NET "ssp_din" LOC = "P32" ; +NET "ssp_dout" LOC = "P34" ; +NET "ssp_frame" LOC = "P31" ; + +#PACE: Start of PACE Area Constraints + +#PACE: Start of PACE Prohibit Constraints + +#PACE: End of Constraints generated by PACE diff --git a/fpga/fpga.v b/fpga/fpga.v new file mode 100644 index 00000000..cbebc395 --- /dev/null +++ b/fpga/fpga.v @@ -0,0 +1,190 @@ +//----------------------------------------------------------------------------- +// The FPGA is responsible for interfacing between the A/D, the coil drivers, +// and the ARM. In the low-frequency modes it passes the data straight +// through, so that the ARM gets raw A/D samples over the SSP. In the high- +// frequency modes, the FPGA might perform some demodulation first, to +// reduce the amount of data that we must send to the ARM. +// +// I am not really an FPGA/ASIC designer, so I am sure that a lot of this +// could be improved. +// +// Jonathan Westhues, March 2006 +// Added ISO14443-A support by Gerhard de Koning Gans, April 2008 +//----------------------------------------------------------------------------- + +`include "lo_read.v" +`include "lo_simulate.v" +`include "hi_read_tx.v" +`include "hi_read_rx_xcorr.v" +`include "hi_simulate.v" +`include "hi_iso14443a.v" +`include "util.v" + +module fpga( + spck, miso, mosi, ncs, + pck0i, ck_1356meg, ck_1356megb, + pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, + adc_d, adc_clk, adc_noe, + ssp_frame, ssp_din, ssp_dout, ssp_clk, + cross_hi, cross_lo, + dbg +); + input spck, mosi, ncs; + output miso; + input pck0i, ck_1356meg, ck_1356megb; + output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; + input [7:0] adc_d; + output adc_clk, adc_noe; + input ssp_dout; + output ssp_frame, ssp_din, ssp_clk; + input cross_hi, cross_lo; + output dbg; + + IBUFG #(.IOSTANDARD("DEFAULT") ) pck0b( + .O(pck0), + .I(pck0i) + ); +//assign pck0 = pck0i; +//----------------------------------------------------------------------------- +// The SPI receiver. This sets up the configuration word, which the rest of +// the logic looks at to determine how to connect the A/D and the coil +// drivers (i.e., which section gets it). Also assign some symbolic names +// to the configuration bits, for use below. +//----------------------------------------------------------------------------- + +reg [7:0] conf_word_shift; +reg [7:0] conf_word; + +// We switch modes between transmitting to the 13.56 MHz tag and receiving +// from it, which means that we must make sure that we can do so without +// glitching, or else we will glitch the transmitted carrier. +always @(posedge ncs) +begin + conf_word <= conf_word_shift; +end + +always @(posedge spck) +begin + if(~ncs) + begin + conf_word_shift[7:1] <= conf_word_shift[6:0]; + conf_word_shift[0] <= mosi; + end +end + +wire [2:0] major_mode; +assign major_mode = conf_word[7:5]; + +// For the low-frequency configuration: +wire lo_is_125khz; +assign lo_is_125khz = conf_word[3]; + +// For the high-frequency transmit configuration: modulation depth, either +// 100% (just quite driving antenna, steady LOW), or shallower (tri-state +// some fraction of the buffers) +wire hi_read_tx_shallow_modulation; +assign hi_read_tx_shallow_modulation = conf_word[0]; + +// For the high-frequency receive correlator: frequency against which to +// correlate. +wire hi_read_rx_xcorr_848; +assign hi_read_rx_xcorr_848 = conf_word[0]; +// and whether to drive the coil (reader) or just short it (snooper) +wire hi_read_rx_xcorr_snoop; +assign hi_read_rx_xcorr_snoop = conf_word[1]; + +// For the high-frequency simulated tag: what kind of modulation to use. +wire [2:0] hi_simulate_mod_type; +assign hi_simulate_mod_type = conf_word[2:0]; + +//----------------------------------------------------------------------------- +// And then we instantiate the modules corresponding to each of the FPGA's +// major modes, and use muxes to connect the outputs of the active mode to +// the output pins. +//----------------------------------------------------------------------------- + +lo_read lr( + pck0, ck_1356meg, ck_1356megb, + lr_pwr_lo, lr_pwr_hi, lr_pwr_oe1, lr_pwr_oe2, lr_pwr_oe3, lr_pwr_oe4, + adc_d, lr_adc_clk, + lr_ssp_frame, lr_ssp_din, ssp_dout, lr_ssp_clk, + cross_hi, cross_lo, + lr_dbg, + lo_is_125khz +); + +lo_simulate ls( + pck0, ck_1356meg, ck_1356megb, + ls_pwr_lo, ls_pwr_hi, ls_pwr_oe1, ls_pwr_oe2, ls_pwr_oe3, ls_pwr_oe4, + adc_d, ls_adc_clk, + ls_ssp_frame, ls_ssp_din, ssp_dout, ls_ssp_clk, + cross_hi, cross_lo, + ls_dbg +); + +hi_read_tx ht( + pck0, ck_1356meg, ck_1356megb, + ht_pwr_lo, ht_pwr_hi, ht_pwr_oe1, ht_pwr_oe2, ht_pwr_oe3, ht_pwr_oe4, + adc_d, ht_adc_clk, + ht_ssp_frame, ht_ssp_din, ssp_dout, ht_ssp_clk, + cross_hi, cross_lo, + ht_dbg, + hi_read_tx_shallow_modulation +); + +hi_read_rx_xcorr hrxc( + pck0, ck_1356meg, ck_1356megb, + hrxc_pwr_lo, hrxc_pwr_hi, hrxc_pwr_oe1, hrxc_pwr_oe2, hrxc_pwr_oe3, hrxc_pwr_oe4, + adc_d, hrxc_adc_clk, + hrxc_ssp_frame, hrxc_ssp_din, ssp_dout, hrxc_ssp_clk, + cross_hi, cross_lo, + hrxc_dbg, + hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop +); + +hi_simulate hs( + pck0, ck_1356meg, ck_1356megb, + hs_pwr_lo, hs_pwr_hi, hs_pwr_oe1, hs_pwr_oe2, hs_pwr_oe3, hs_pwr_oe4, + adc_d, hs_adc_clk, + hs_ssp_frame, hs_ssp_din, ssp_dout, hs_ssp_clk, + cross_hi, cross_lo, + hs_dbg, + hi_simulate_mod_type +); + +hi_iso14443a hisn( + pck0, ck_1356meg, ck_1356megb, + hisn_pwr_lo, hisn_pwr_hi, hisn_pwr_oe1, hisn_pwr_oe2, hisn_pwr_oe3, hisn_pwr_oe4, + adc_d, hisn_adc_clk, + hisn_ssp_frame, hisn_ssp_din, ssp_dout, hisn_ssp_clk, + cross_hi, cross_lo, + hisn_dbg, + hi_simulate_mod_type +); + +// Major modes: +// 000 -- LF reader (generic) +// 001 -- LF simulated tag (generic) +// 010 -- HF reader, transmitting to tag; modulation depth selectable +// 011 -- HF reader, receiving from tag, correlating as it goes; frequency selectable +// 100 -- HF simulated tag +// 101 -- HF ISO14443-A +// 110 -- unused +// 111 -- everything off + +mux8 mux_ssp_clk (major_mode, ssp_clk, lr_ssp_clk, ls_ssp_clk, ht_ssp_clk, hrxc_ssp_clk, hs_ssp_clk, hisn_ssp_clk, 1'b0, 1'b0); +mux8 mux_ssp_din (major_mode, ssp_din, lr_ssp_din, ls_ssp_din, ht_ssp_din, hrxc_ssp_din, hs_ssp_din, hisn_ssp_din, 1'b0, 1'b0); +mux8 mux_ssp_frame (major_mode, ssp_frame, lr_ssp_frame, ls_ssp_frame, ht_ssp_frame, hrxc_ssp_frame, hs_ssp_frame, hisn_ssp_frame, 1'b0, 1'b0); +mux8 mux_pwr_oe1 (major_mode, pwr_oe1, lr_pwr_oe1, ls_pwr_oe1, ht_pwr_oe1, hrxc_pwr_oe1, hs_pwr_oe1, hisn_pwr_oe1, 1'b0, 1'b0); +mux8 mux_pwr_oe2 (major_mode, pwr_oe2, lr_pwr_oe2, ls_pwr_oe2, ht_pwr_oe2, hrxc_pwr_oe2, hs_pwr_oe2, hisn_pwr_oe2, 1'b0, 1'b0); +mux8 mux_pwr_oe3 (major_mode, pwr_oe3, lr_pwr_oe3, ls_pwr_oe3, ht_pwr_oe3, hrxc_pwr_oe3, hs_pwr_oe3, hisn_pwr_oe3, 1'b0, 1'b0); +mux8 mux_pwr_oe4 (major_mode, pwr_oe4, lr_pwr_oe4, ls_pwr_oe4, ht_pwr_oe4, hrxc_pwr_oe4, hs_pwr_oe4, hisn_pwr_oe4, 1'b0, 1'b0); +mux8 mux_pwr_lo (major_mode, pwr_lo, lr_pwr_lo, ls_pwr_lo, ht_pwr_lo, hrxc_pwr_lo, hs_pwr_lo, hisn_pwr_lo, 1'b0, 1'b0); +mux8 mux_pwr_hi (major_mode, pwr_hi, lr_pwr_hi, ls_pwr_hi, ht_pwr_hi, hrxc_pwr_hi, hs_pwr_hi, hisn_pwr_hi, 1'b0, 1'b0); +mux8 mux_adc_clk (major_mode, adc_clk, lr_adc_clk, ls_adc_clk, ht_adc_clk, hrxc_adc_clk, hs_adc_clk, hisn_adc_clk, 1'b0, 1'b0); +mux8 mux_dbg (major_mode, dbg, lr_dbg, ls_dbg, ht_dbg, hrxc_dbg, hs_dbg, hisn_dbg, 1'b0, 1'b0); + +// In all modes, let the ADC's outputs be enabled. +assign adc_noe = 1'b0; + +endmodule diff --git a/fpga/go.bat b/fpga/go.bat new file mode 100644 index 00000000..8600d3cd --- /dev/null +++ b/fpga/go.bat @@ -0,0 +1,38 @@ +@echo off + +rmdir/s/q xst + +del fpga.ngc +xst -ifn xst.scr +if errorlevel 0 goto ok1 +goto done +:ok1 + +del fpga.ngd +ngdbuild -aul -p xc2s30-6vq100 -nt timestamp -uc fpga.ucf fpga.ngc fpga.ngd +if errorlevel 0 goto ok2 +goto done +:ok2 + +del fpga.ncd +map -p xc2s30-6vq100 fpga.ngd +if errorlevel 0 goto ok3 +goto done +:ok3 + +del fpga-placed.ncd +par fpga.ncd fpga-placed.ncd +if errorlevel 0 goto ok4 +goto done +:ok4 + +del fpga.bit fpga.drc fpga.rbt +bitgen -b fpga-placed.ncd fpga.bit +if errorlevel 0 goto ok5 +goto done +:ok5 + +echo okay +perl ..\tools\rbt2c.pl fpga.rbt > ..\armsrc\fpgaimg.c + +:done diff --git a/fpga/hi_iso14443a.v b/fpga/hi_iso14443a.v new file mode 100644 index 00000000..eb03fa23 --- /dev/null +++ b/fpga/hi_iso14443a.v @@ -0,0 +1,360 @@ +//----------------------------------------------------------------------------- +// ISO14443-A support for the Proxmark III +// Gerhard de Koning Gans, April 2008 +//----------------------------------------------------------------------------- + +module hi_iso14443a( + pck0, ck_1356meg, ck_1356megb, + pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, + adc_d, adc_clk, + ssp_frame, ssp_din, ssp_dout, ssp_clk, + cross_hi, cross_lo, + dbg, + mod_type +); + input pck0, ck_1356meg, ck_1356megb; + output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; + input [7:0] adc_d; + output adc_clk; + input ssp_dout; + output ssp_frame, ssp_din, ssp_clk; + input cross_hi, cross_lo; + output dbg; + input [2:0] mod_type; + +reg ssp_clk; +reg ssp_frame; + +reg fc_div_2; +always @(posedge ck_1356meg) + fc_div_2 = ~fc_div_2; + +wire adc_clk; +assign adc_clk = ck_1356meg; + +reg after_hysteresis, after_hysteresis_prev1, after_hysteresis_prev2, after_hysteresis_prev3; +reg [11:0] has_been_low_for; +reg [8:0] saw_deep_modulation; +reg [2:0] deep_counter; +reg deep_modulation; +always @(negedge adc_clk) +begin + if(& adc_d[7:6]) after_hysteresis <= 1'b1; + else if(~(| adc_d[7:4])) after_hysteresis <= 1'b0; + + if(~(| adc_d[7:0])) + begin + if(deep_counter == 3'd7) + begin + deep_modulation <= 1'b1; + saw_deep_modulation <= 8'd0; + end + else + deep_counter <= deep_counter + 1; + end + else + begin + deep_counter <= 3'd0; + if(saw_deep_modulation == 8'd255) + deep_modulation <= 1'b0; + else + saw_deep_modulation <= saw_deep_modulation + 1; + end + + if(after_hysteresis) + begin + has_been_low_for <= 7'b0; + end + else + begin + if(has_been_low_for == 12'd4095) + begin + has_been_low_for <= 12'd0; + after_hysteresis <= 1'b1; + end + else + has_been_low_for <= has_been_low_for + 1; + end +end + +// Report every 4 subcarrier cycles +// 64 periods of carrier frequency => 6-bit counter [negedge_cnt] +reg [5:0] negedge_cnt; +reg bit1, bit2, bit3; +reg [3:0] count_ones; +reg [3:0] count_zeros; +wire [7:0] avg; +reg [7:0] lavg; +reg signed [12:0] step1; +reg signed [12:0] step2; +reg [7:0] stepsize; +reg curbit; +reg [12:0] average; +wire signed [9:0] dif; + +// A register to send the results to the arm +reg signed [7:0] to_arm; + +assign avg[7:0] = average[11:4]; +assign dif = lavg - avg; + +reg bit_to_arm; +reg fdt_indicator, fdt_elapsed; +reg [10:0] fdt_counter; +reg [47:0] mod_sig_buf; +wire mod_sig_buf_empty; +reg [5:0] mod_sig_ptr; +reg [3:0] mod_sig_flip; +reg mod_sig, mod_sig_coil; +reg temp_buffer_reset; +reg sendbit; + +assign mod_sig_buf_empty = ~(|mod_sig_buf[47:0]); +reg [2:0] ssp_frame_counter; + +// ADC data appears on the rising edge, so sample it on the falling edge +always @(negedge adc_clk) +begin + + // last bit = 0 then fdt = 1172, in case of 0x26 (7-bit command, LSB first!) + // last bit = 1 then fdt = 1236, in case of 0x52 (7-bit command, LSB first!) + if(fdt_counter == 11'd740) fdt_indicator = 1'b1; + + if(fdt_counter == 11'd1148) + begin + if(fdt_elapsed) + begin + if(negedge_cnt[3:0] == mod_sig_flip[3:0]) mod_sig_coil <= mod_sig; + end + else + begin + mod_sig_flip[3:0] <= negedge_cnt[3:0]; + mod_sig_coil <= mod_sig; + fdt_elapsed = 1'b1; + fdt_indicator = 1'b0; + + if(~(| mod_sig_ptr[5:0])) mod_sig_ptr <= 6'b001001; + else temp_buffer_reset = 1'b1; // fix position of the buffer pointer + end + end + else + begin + fdt_counter <= fdt_counter + 1; + end + + if(& negedge_cnt[3:0]) + begin + // When there is a dip in the signal and not in reader mode + if(~after_hysteresis && mod_sig_buf_empty && ~((mod_type == 3'b100) || (mod_type == 3'b011) || (mod_type == 3'b010))) // last condition to prevent reset + begin + fdt_counter <= 11'd0; + fdt_elapsed = 1'b0; + fdt_indicator = 1'b0; + temp_buffer_reset = 1'b0; + mod_sig_ptr <= 6'b000000; + end + + lavg <= avg; + + if(stepsize<16) stepsize = 8'd16; + + if(dif>0) + begin + step1 = dif*3; + step2 = stepsize*2; // 3:2 + if(step1>step2) + begin + curbit = 1'b0; + stepsize = dif; + end + end + else + begin + step1 = dif*3; + step1 = -step1; + step2 = stepsize*2; + if(step1>step2) + begin + curbit = 1'b1; + stepsize = -dif; + end + end + + if(curbit) + begin + count_zeros <= 4'd0; + if(& count_ones[3:2]) + begin + curbit = 1'b0; // suppressed signal + stepsize = 8'd24; // just a fine number + end + else + begin + count_ones <= count_ones + 1; + end + end + else + begin + count_ones <= 4'd0; + if(& count_zeros[3:0]) + begin + stepsize = 8'd24; + end + else + begin + count_zeros <= count_zeros + 1; + end + end + + // What do we communicate to the ARM + if(mod_type == 3'b001) sendbit = after_hysteresis; + else if(mod_type == 3'b010) + begin + if(fdt_counter > 11'd772) sendbit = mod_sig_coil; + else sendbit = fdt_indicator; + end + else if(mod_type == 3'b011) sendbit = curbit; + else sendbit = 1'b0; + + end + + if(~(| negedge_cnt[3:0])) average <= adc_d; + else average <= average + adc_d; + + if(negedge_cnt == 7'd63) + begin + if(deep_modulation) + begin + to_arm <= {after_hysteresis_prev1,after_hysteresis_prev2,after_hysteresis_prev3,after_hysteresis,1'b0,1'b0,1'b0,1'b0}; + end + else + begin + to_arm <= {after_hysteresis_prev1,after_hysteresis_prev2,after_hysteresis_prev3,after_hysteresis,bit1,bit2,bit3,curbit}; + end + + negedge_cnt <= 0; + + end + else + begin + negedge_cnt <= negedge_cnt + 1; + end + + if(negedge_cnt == 6'd15) + begin + after_hysteresis_prev1 <= after_hysteresis; + bit1 <= curbit; + end + if(negedge_cnt == 6'd31) + begin + after_hysteresis_prev2 <= after_hysteresis; + bit2 <= curbit; + end + if(negedge_cnt == 6'd47) + begin + after_hysteresis_prev3 <= after_hysteresis; + bit3 <= curbit; + end + + + if(mod_type != 3'b000) + begin + if(negedge_cnt[3:0] == 4'b1000) + begin + // The modulation signal of the tag + mod_sig_buf[47:0] <= {mod_sig_buf[46:1], ssp_dout, 1'b0}; + if((ssp_dout || (| mod_sig_ptr[5:0])) && ~fdt_elapsed) + if(mod_sig_ptr == 6'b101110) + begin + mod_sig_ptr <= 6'b000000; + end + else mod_sig_ptr <= mod_sig_ptr + 1; + else if(fdt_elapsed && ~temp_buffer_reset) + begin + if(ssp_dout) temp_buffer_reset = 1'b1; + if(mod_sig_ptr == 6'b000010) mod_sig_ptr <= 6'b001001; + else mod_sig_ptr <= mod_sig_ptr - 1; + end + else + begin + // side effect: when ptr = 1 it will cancel the first 1 of every block of ones + if(~mod_sig_buf[mod_sig_ptr-1] && ~mod_sig_buf[mod_sig_ptr+1]) mod_sig = 1'b0; + else mod_sig = mod_sig_buf[mod_sig_ptr] & fdt_elapsed; // & fdt_elapsed was for direct relay to oe4 + end + end + end + + // SSP Clock and data + if(mod_type == 3'b000) + begin + if(negedge_cnt[2:0] == 3'b100) + ssp_clk <= 1'b0; + + if(negedge_cnt[2:0] == 3'b000) + begin + ssp_clk <= 1'b1; + // Don't shift if we just loaded new data, obviously. + if(negedge_cnt != 7'd0) + begin + to_arm[7:1] <= to_arm[6:0]; + end + end + + if(negedge_cnt[5:4] == 2'b00) + ssp_frame = 1'b1; + else + ssp_frame = 1'b0; + + bit_to_arm = to_arm[7]; + end + else + begin + if(negedge_cnt[3:0] == 4'b1000) ssp_clk <= 1'b0; + + if(negedge_cnt[3:0] == 4'b0111) + begin + if(ssp_frame_counter == 3'd7) ssp_frame_counter <= 3'd0; + else ssp_frame_counter <= ssp_frame_counter + 1; + end + + if(negedge_cnt[3:0] == 4'b0000) + begin + ssp_clk <= 1'b1; + end + + ssp_frame = (ssp_frame_counter == 3'd7); + + bit_to_arm = sendbit; + end + +end + +assign ssp_din = bit_to_arm; + +// Modulating carrier frequency is fc/16 +wire modulating_carrier; +assign modulating_carrier = (mod_sig_coil & negedge_cnt[3] & (mod_type == 3'b010)); +assign pwr_hi = (ck_1356megb & (((mod_type == 3'b100) & ~mod_sig_coil) || (mod_type == 3'b011))); + +// This one is all LF, so doesn't matter +//assign pwr_oe2 = modulating_carrier; +assign pwr_oe2 = 1'b0; + +// Toggle only one of these, since we are already producing much deeper +// modulation than a real tag would. +//assign pwr_oe1 = modulating_carrier; +assign pwr_oe1 = 1'b0; +assign pwr_oe4 = modulating_carrier; +//assign pwr_oe4 = 1'b0; + +// This one is always on, so that we can watch the carrier. +//assign pwr_oe3 = modulating_carrier; +assign pwr_oe3 = 1'b0; + + +assign dbg = negedge_cnt[3]; + +// Unused. +assign pwr_lo = 1'b0; + +endmodule diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v new file mode 100644 index 00000000..253f5080 --- /dev/null +++ b/fpga/hi_read_rx_xcorr.v @@ -0,0 +1,165 @@ +//----------------------------------------------------------------------------- +// +// Jonathan Westhues, April 2006 +//----------------------------------------------------------------------------- + +module hi_read_rx_xcorr( + pck0, ck_1356meg, ck_1356megb, + pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, + adc_d, adc_clk, + ssp_frame, ssp_din, ssp_dout, ssp_clk, + cross_hi, cross_lo, + dbg, + xcorr_is_848, snoop +); + input pck0, ck_1356meg, ck_1356megb; + output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; + input [7:0] adc_d; + output adc_clk; + input ssp_dout; + output ssp_frame, ssp_din, ssp_clk; + input cross_hi, cross_lo; + output dbg; + input xcorr_is_848, snoop; + +// Carrier is steady on through this, unless we're snooping. +assign pwr_hi = ck_1356megb & (~snoop); +assign pwr_oe1 = 1'b0; +assign pwr_oe2 = 1'b0; +assign pwr_oe3 = 1'b0; +assign pwr_oe4 = 1'b0; + +reg ssp_clk; +reg ssp_frame; + +reg fc_div_2; +always @(posedge ck_1356meg) + fc_div_2 = ~fc_div_2; + +reg adc_clk; + +always @(xcorr_is_848 or fc_div_2 or ck_1356meg) + if(xcorr_is_848) + // The subcarrier frequency is fc/16; we will sample at fc, so that + // means the subcarrier is 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 ... + adc_clk <= ck_1356meg; + else + // The subcarrier frequency is fc/32; we will sample at fc/2, and + // the subcarrier will look identical. + adc_clk <= fc_div_2; + +// When we're a reader, we just need to do the BPSK demod; but when we're an +// eavesdropper, we also need to pick out the commands sent by the reader, +// using AM. Do this the same way that we do it for the simulated tag. +reg after_hysteresis, after_hysteresis_prev; +reg [11:0] has_been_low_for; +always @(negedge adc_clk) +begin + if(& adc_d[7:0]) after_hysteresis <= 1'b1; + else if(~(| adc_d[7:0])) after_hysteresis <= 1'b0; + + if(after_hysteresis) + begin + has_been_low_for <= 7'b0; + end + else + begin + if(has_been_low_for == 12'd4095) + begin + has_been_low_for <= 12'd0; + after_hysteresis <= 1'b1; + end + else + has_been_low_for <= has_been_low_for + 1; + end +end + +// Let us report a correlation every 4 subcarrier cycles, or 4*16 samples, +// so we need a 6-bit counter. +reg [5:0] corr_i_cnt; +reg [5:0] corr_q_cnt; +// And a couple of registers in which to accumulate the correlations. +reg signed [15:0] corr_i_accum; +reg signed [15:0] corr_q_accum; +reg signed [7:0] corr_i_out; +reg signed [7:0] corr_q_out; + +// ADC data appears on the rising edge, so sample it on the falling edge +always @(negedge adc_clk) +begin + // These are the correlators: we correlate against in-phase and quadrature + // versions of our reference signal, and keep the (signed) result to + // send out later over the SSP. + if(corr_i_cnt == 7'd63) + begin + if(snoop) + begin + corr_i_out <= {corr_i_accum[12:6], after_hysteresis_prev}; + corr_q_out <= {corr_q_accum[12:6], after_hysteresis}; + end + else + begin + // Only correlations need to be delivered. + corr_i_out <= corr_i_accum[13:6]; + corr_q_out <= corr_q_accum[13:6]; + end + + corr_i_accum <= adc_d; + corr_q_accum <= adc_d; + corr_q_cnt <= 4; + corr_i_cnt <= 0; + end + else + begin + if(corr_i_cnt[3]) + corr_i_accum <= corr_i_accum - adc_d; + else + corr_i_accum <= corr_i_accum + adc_d; + + if(corr_q_cnt[3]) + corr_q_accum <= corr_q_accum - adc_d; + else + corr_q_accum <= corr_q_accum + adc_d; + + corr_i_cnt <= corr_i_cnt + 1; + corr_q_cnt <= corr_q_cnt + 1; + end + + // The logic in hi_simulate.v reports 4 samples per bit. We report two + // (I, Q) pairs per bit, so we should do 2 samples per pair. + if(corr_i_cnt == 6'd31) + after_hysteresis_prev <= after_hysteresis; + + // Then the result from last time is serialized and send out to the ARM. + // We get one report each cycle, and each report is 16 bits, so the + // ssp_clk should be the adc_clk divided by 64/16 = 4. + + if(corr_i_cnt[1:0] == 2'b10) + ssp_clk <= 1'b0; + + if(corr_i_cnt[1:0] == 2'b00) + begin + ssp_clk <= 1'b1; + // Don't shift if we just loaded new data, obviously. + if(corr_i_cnt != 7'd0) + begin + corr_i_out[7:0] <= {corr_i_out[6:0], corr_q_out[7]}; + corr_q_out[7:1] <= corr_q_out[6:0]; + end + end + + if(corr_i_cnt[5:2] == 4'b000 || corr_i_cnt[5:2] == 4'b1000) + ssp_frame = 1'b1; + else + ssp_frame = 1'b0; + +end + +assign ssp_din = corr_i_out[7]; + +assign dbg = corr_i_cnt[3]; + +// Unused. +assign pwr_lo = 1'b0; + +endmodule diff --git a/fpga/hi_read_tx.v b/fpga/hi_read_tx.v new file mode 100644 index 00000000..c2cec3ab --- /dev/null +++ b/fpga/hi_read_tx.v @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// The way that we connect things when transmitting a command to an ISO +// 15693 tag, using 100% modulation only for now. +// +// Jonathan Westhues, April 2006 +//----------------------------------------------------------------------------- + +module hi_read_tx( + pck0, ck_1356meg, ck_1356megb, + pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, + adc_d, adc_clk, + ssp_frame, ssp_din, ssp_dout, ssp_clk, + cross_hi, cross_lo, + dbg, + shallow_modulation +); + input pck0, ck_1356meg, ck_1356megb; + output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; + input [7:0] adc_d; + output adc_clk; + input ssp_dout; + output ssp_frame, ssp_din, ssp_clk; + input cross_hi, cross_lo; + output dbg; + input shallow_modulation; + +// The high-frequency stuff. For now, for testing, just bring out the carrier, +// and allow the ARM to modulate it over the SSP. +reg pwr_hi; +reg pwr_oe1; +reg pwr_oe2; +reg pwr_oe3; +reg pwr_oe4; +always @(ck_1356megb or ssp_dout or shallow_modulation) +begin + if(shallow_modulation) + begin + pwr_hi <= ck_1356megb; + pwr_oe1 <= ~ssp_dout; + pwr_oe2 <= ~ssp_dout; + pwr_oe3 <= ~ssp_dout; + pwr_oe4 <= 1'b0; + end + else + begin + pwr_hi <= ck_1356megb & ssp_dout; + pwr_oe1 <= 1'b0; + pwr_oe2 <= 1'b0; + pwr_oe3 <= 1'b0; + pwr_oe4 <= 1'b0; + end +end + +// Then just divide the 13.56 MHz clock down to produce appropriate clocks +// for the synchronous serial port. + +reg [6:0] hi_div_by_128; + +always @(posedge ck_1356meg) + hi_div_by_128 <= hi_div_by_128 + 1; + +assign ssp_clk = hi_div_by_128[6]; + +reg [2:0] hi_byte_div; + +always @(negedge ssp_clk) + hi_byte_div <= hi_byte_div + 1; + +assign ssp_frame = (hi_byte_div == 3'b000); + +assign ssp_din = 1'b0; + +assign pwr_lo = 1'b0; +assign dbg = ssp_frame; + +endmodule diff --git a/fpga/hi_simulate.v b/fpga/hi_simulate.v new file mode 100644 index 00000000..d0a71176 --- /dev/null +++ b/fpga/hi_simulate.v @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------------- +// Pretend to be an ISO 14443 tag. We will do this by alternately short- +// circuiting and open-circuiting the antenna coil, with the tri-state +// pins. +// +// We communicate over the SSP, as a bitstream (i.e., might as well be +// unframed, though we still generate the word sync signal). The output +// (ARM -> FPGA) tells us whether to modulate or not. The input (FPGA +// -> ARM) is us using the A/D as a fancy comparator; this is with +// (software-added) hysteresis, to undo the high-pass filter. +// +// At this point only Type A is implemented. This means that we are using a +// bit rate of 106 kbit/s, or fc/128. Oversample by 4, which ought to make +// things practical for the ARM (fc/32, 423.8 kbits/s, ~50 kbytes/s) +// +// Jonathan Westhues, October 2006 +//----------------------------------------------------------------------------- + +module hi_simulate( + pck0, ck_1356meg, ck_1356megb, + pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, + adc_d, adc_clk, + ssp_frame, ssp_din, ssp_dout, ssp_clk, + cross_hi, cross_lo, + dbg, + mod_type +); + input pck0, ck_1356meg, ck_1356megb; + output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; + input [7:0] adc_d; + output adc_clk; + input ssp_dout; + output ssp_frame, ssp_din, ssp_clk; + input cross_hi, cross_lo; + output dbg; + input [2:0] mod_type; + +// Power amp goes between LOW and tri-state, so pwr_hi (and pwr_lo) can +// always be low. +assign pwr_hi = 1'b0; +assign pwr_lo = 1'b0; + +// The comparator with hysteresis on the output from the peak detector. +reg after_hysteresis; +assign adc_clk = ck_1356meg; + +always @(negedge adc_clk) +begin + if(& adc_d[7:5]) after_hysteresis = 1'b1; + else if(~(| adc_d[7:5])) after_hysteresis = 1'b0; +end + +// Divide 13.56 MHz by 32 to produce the SSP_CLK +reg [4:0] ssp_clk_divider; +always @(posedge adc_clk) + ssp_clk_divider <= (ssp_clk_divider + 1); +assign ssp_clk = ssp_clk_divider[4]; + +// Divide SSP_CLK by 8 to produce the byte framing signal; the phase of +// this is arbitrary, because it's just a bitstream. +// One nasty issue, though: I can't make it work with both rx and tx at +// once. The phase wrt ssp_clk must be changed. TODO to find out why +// that is and make a better fix. +reg [2:0] ssp_frame_divider_to_arm; +always @(posedge ssp_clk) + ssp_frame_divider_to_arm <= (ssp_frame_divider_to_arm + 1); +reg [2:0] ssp_frame_divider_from_arm; +always @(negedge ssp_clk) + ssp_frame_divider_from_arm <= (ssp_frame_divider_from_arm + 1); + +reg ssp_frame; +always @(ssp_frame_divider_to_arm or ssp_frame_divider_from_arm or mod_type) + if(mod_type == 3'b000) // not modulating, so listening, to ARM + ssp_frame = (ssp_frame_divider_to_arm == 3'b000); + else + ssp_frame = (ssp_frame_divider_from_arm == 3'b000); + +// Synchronize up the after-hysteresis signal, to produce DIN. +reg ssp_din; +always @(posedge ssp_clk) + ssp_din = after_hysteresis; + +// Modulating carrier frequency is fc/16, reuse ssp_clk divider for that +reg modulating_carrier; +always @(mod_type or ssp_clk or ssp_dout) + if(mod_type == 3'b000) + modulating_carrier <= 1'b0; // no modulation + else if(mod_type == 3'b001) + modulating_carrier <= ssp_dout ^ ssp_clk_divider[3]; // XOR means BPSK + else + modulating_carrier <= 1'b0; // yet unused + +// This one is all LF, so doesn't matter +assign pwr_oe2 = modulating_carrier; + +// Toggle only one of these, since we are already producing much deeper +// modulation than a real tag would. +assign pwr_oe1 = modulating_carrier; +assign pwr_oe4 = modulating_carrier; + +// This one is always on, so that we can watch the carrier. +assign pwr_oe3 = 1'b0; + +assign dbg = after_hysteresis; + +endmodule diff --git a/fpga/lo_read.v b/fpga/lo_read.v new file mode 100644 index 00000000..9c3edb22 --- /dev/null +++ b/fpga/lo_read.v @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// The way that we connect things in low-frequency read mode. In this case +// we are generating the 134 kHz or 125 kHz carrier, and running the +// unmodulated carrier at that frequency. The A/D samples at that same rate, +// and the result is serialized. +// +// Jonathan Westhues, April 2006 +//----------------------------------------------------------------------------- + +module lo_read( + pck0, ck_1356meg, ck_1356megb, + pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, + adc_d, adc_clk, + ssp_frame, ssp_din, ssp_dout, ssp_clk, + cross_hi, cross_lo, + dbg, + lo_is_125khz +); + input pck0, ck_1356meg, ck_1356megb; + output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; + input [7:0] adc_d; + output adc_clk; + input ssp_dout; + output ssp_frame, ssp_din, ssp_clk; + input cross_hi, cross_lo; + output dbg; + input lo_is_125khz; + +// The low-frequency RFID stuff. This is relatively simple, because most +// of the work happens on the ARM, and we just pass samples through. The +// PCK0 must be divided down to generate the A/D clock, and from there by +// a factor of 8 to generate the carrier (that we apply to the coil drivers). +// +// This is also where we decode the received synchronous serial port words, +// to determine how to drive the output enables. + +// PCK0 will run at (PLL clock) / 4, or 24 MHz. That means that we can do +// 125 kHz by dividing by a further factor of (8*12*2), or ~134 kHz by +// dividing by a factor of (8*11*2) (for 136 kHz, ~2% error, tolerable). + +reg [3:0] pck_divider; +reg clk_lo; + +always @(posedge pck0) +begin + if(lo_is_125khz) + begin + if(pck_divider == 4'd11) + begin + pck_divider <= 4'd0; + clk_lo = !clk_lo; + end + else + pck_divider <= pck_divider + 1; + end + else + begin + if(pck_divider == 4'd10) + begin + pck_divider <= 4'd0; + clk_lo = !clk_lo; + end + else + pck_divider <= pck_divider + 1; + end +end + +reg [2:0] carrier_divider_lo; + +always @(posedge clk_lo) +begin + carrier_divider_lo <= carrier_divider_lo + 1; +end + +assign pwr_lo = carrier_divider_lo[2]; + +// This serializes the values returned from the A/D, and sends them out +// over the SSP. + +reg [7:0] to_arm_shiftreg; + +always @(posedge clk_lo) +begin + if(carrier_divider_lo == 3'b000) + to_arm_shiftreg <= adc_d; + else + to_arm_shiftreg[7:1] <= to_arm_shiftreg[6:0]; +end + +assign ssp_clk = clk_lo; +assign ssp_frame = (carrier_divider_lo == 3'b001); +assign ssp_din = to_arm_shiftreg[7]; + +// The ADC converts on the falling edge, and our serializer loads when +// carrier_divider_lo == 3'b000. +assign adc_clk = ~carrier_divider_lo[2]; + +assign pwr_hi = 1'b0; + +assign dbg = adc_clk; + +endmodule diff --git a/fpga/lo_simulate.v b/fpga/lo_simulate.v new file mode 100644 index 00000000..7eb910ba --- /dev/null +++ b/fpga/lo_simulate.v @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// The way that we connect things in low-frequency simulation mode. In this +// case just pass everything through to the ARM, which can bit-bang this +// (because it is so slow). +// +// Jonathan Westhues, April 2006 +//----------------------------------------------------------------------------- + +module lo_simulate( + pck0, ck_1356meg, ck_1356megb, + pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, + adc_d, adc_clk, + ssp_frame, ssp_din, ssp_dout, ssp_clk, + cross_hi, cross_lo, + dbg +); + input pck0, ck_1356meg, ck_1356megb; + output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; + input [7:0] adc_d; + output adc_clk; + input ssp_dout; + output ssp_frame, ssp_din, ssp_clk; + input cross_hi, cross_lo; + output dbg; + +// No logic, straight through. +assign pwr_oe3 = 1'b0; +assign pwr_oe1 = ssp_dout; +assign pwr_oe2 = ssp_dout; +assign pwr_oe4 = ssp_dout; +assign ssp_clk = cross_lo; +assign pwr_lo = 1'b0; +assign adc_clk = 1'b0; +assign pwr_hi = 1'b0; +assign dbg = cross_lo; + +endmodule diff --git a/fpga/sim.tcl b/fpga/sim.tcl new file mode 100644 index 00000000..477acd1d --- /dev/null +++ b/fpga/sim.tcl @@ -0,0 +1,27 @@ +#------------------------------------------------------------------------------ +# Run the simulation testbench in ModelSim: recompile both Verilog source +# files, then start the simulation, add a lot of signals to the waveform +# viewer, and run. I should (TODO) fix the absolute paths at some point. +# +# Jonathan Westhues, Mar 2006 +#------------------------------------------------------------------------------ + +vlog -work work -O0 C:/depot/proximity/mark3/fpga/fpga.v +vlog -work work -O0 C:/depot/proximity/mark3/fpga/fpga_tb.v + +vsim work.fpga_tb + +add wave sim:/fpga_tb/adc_clk +add wave sim:/fpga_tb/adc_d +add wave sim:/fpga_tb/pwr_lo +add wave sim:/fpga_tb/ssp_clk +add wave sim:/fpga_tb/ssp_frame +add wave sim:/fpga_tb/ssp_din +add wave sim:/fpga_tb/ssp_dout + +add wave sim:/fpga_tb/dut/clk_lo +add wave sim:/fpga_tb/dut/pck_divider +add wave sim:/fpga_tb/dut/carrier_divider_lo +add wave sim:/fpga_tb/dut/conf_word + +run 30000 diff --git a/fpga/testbed_fpga.v b/fpga/testbed_fpga.v new file mode 100644 index 00000000..3ef2766a --- /dev/null +++ b/fpga/testbed_fpga.v @@ -0,0 +1,50 @@ +`include "fpga.v" + +module testbed_fpga; + reg spck, mosi, ncs; + wire miso; + reg pck0i, ck_1356meg, ck_1356megb; + wire pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; + reg [7:0] adc_d; + wire adc_clk, adc_noe; + reg ssp_dout; + wire ssp_frame, ssp_din, ssp_clk; + + fpga dut( + spck, miso, mosi, ncs, + pck0i, ck_1356meg, ck_1356megb, + pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, + adc_d, adc_clk, adc_noe, + ssp_frame, ssp_din, ssp_dout, ssp_clk + ); + + integer i; + + initial begin + + // init inputs + #5 ncs=1; + #5 spck = 1; + #5 mosi = 1; + + #50 ncs=0; + for (i = 0 ; i < 8 ; i = i + 1) begin + #5 mosi = $random; + #5 spck = 0; + #5 spck = 1; + end + #5 ncs=1; + + #50 ncs=0; + for (i = 0 ; i < 8 ; i = i + 1) begin + #5 mosi = $random; + #5 spck = 0; + #5 spck = 1; + end + #5 ncs=1; + + #50 mosi=1; + $finish; + end + +endmodule // main diff --git a/fpga/testbed_hi_read_tx.v b/fpga/testbed_hi_read_tx.v new file mode 100644 index 00000000..0d600a1f --- /dev/null +++ b/fpga/testbed_hi_read_tx.v @@ -0,0 +1,109 @@ +`include "hi_read_tx.v" + +/* + pck0 - input main 24Mhz clock (PLL / 4) + [7:0] adc_d - input data from A/D converter + shallow_modulation - modulation type + + pwr_lo - output to coil drivers (ssp_clk / 8) + adc_clk - output A/D clock signal + ssp_frame - output SSS frame indicator (goes high while the 8 bits are shifted) + ssp_din - output SSP data to ARM (shifts 8 bit A/D value serially to ARM MSB first) + ssp_clk - output SSP clock signal + + ck_1356meg - input unused + ck_1356megb - input unused + ssp_dout - input unused + cross_hi - input unused + cross_lo - input unused + + pwr_hi - output unused, tied low + pwr_oe1 - output unused, undefined + pwr_oe2 - output unused, undefined + pwr_oe3 - output unused, undefined + pwr_oe4 - output unused, undefined + dbg - output alias for adc_clk +*/ + +module testbed_hi_read_tx; + reg pck0; + reg [7:0] adc_d; + reg shallow_modulation; + + wire pwr_lo; + wire adc_clk; + reg ck_1356meg; + reg ck_1356megb; + wire ssp_frame; + wire ssp_din; + wire ssp_clk; + reg ssp_dout; + wire pwr_hi; + wire pwr_oe1; + wire pwr_oe2; + wire pwr_oe3; + wire pwr_oe4; + wire cross_lo; + wire cross_hi; + wire dbg; + + hi_read_tx #(5,200) dut( + .pck0(pck0), + .ck_1356meg(ck_1356meg), + .ck_1356megb(ck_1356megb), + .pwr_lo(pwr_lo), + .pwr_hi(pwr_hi), + .pwr_oe1(pwr_oe1), + .pwr_oe2(pwr_oe2), + .pwr_oe3(pwr_oe3), + .pwr_oe4(pwr_oe4), + .adc_d(adc_d), + .adc_clk(adc_clk), + .ssp_frame(ssp_frame), + .ssp_din(ssp_din), + .ssp_dout(ssp_dout), + .ssp_clk(ssp_clk), + .cross_hi(cross_hi), + .cross_lo(cross_lo), + .dbg(dbg), + .shallow_modulation(shallow_modulation) + ); + + integer idx, i; + + // main clock + always #5 begin + ck_1356megb = !ck_1356megb; + ck_1356meg = ck_1356megb; + end + + //crank DUT + task crank_dut; + begin + @(posedge ssp_clk) ; + ssp_dout = $random; + end + endtask + + initial begin + + // init inputs + ck_1356megb = 0; + adc_d = 0; + ssp_dout=0; + + // shallow modulation off + shallow_modulation=0; + for (i = 0 ; i < 16 ; i = i + 1) begin + crank_dut; + end + + // shallow modulation on + shallow_modulation=1; + for (i = 0 ; i < 16 ; i = i + 1) begin + crank_dut; + end + $finish; + end + +endmodule // main diff --git a/fpga/testbed_hi_simulate.v b/fpga/testbed_hi_simulate.v new file mode 100644 index 00000000..6dc30f0b --- /dev/null +++ b/fpga/testbed_hi_simulate.v @@ -0,0 +1,116 @@ +`include "hi_simulate.v" + +/* + pck0 - input main 24Mhz clock (PLL / 4) + [7:0] adc_d - input data from A/D converter + mod_type - modulation type + + pwr_lo - output to coil drivers (ssp_clk / 8) + adc_clk - output A/D clock signal + ssp_frame - output SSS frame indicator (goes high while the 8 bits are shifted) + ssp_din - output SSP data to ARM (shifts 8 bit A/D value serially to ARM MSB first) + ssp_clk - output SSP clock signal + + ck_1356meg - input unused + ck_1356megb - input unused + ssp_dout - input unused + cross_hi - input unused + cross_lo - input unused + + pwr_hi - output unused, tied low + pwr_oe1 - output unused, undefined + pwr_oe2 - output unused, undefined + pwr_oe3 - output unused, undefined + pwr_oe4 - output unused, undefined + dbg - output alias for adc_clk +*/ + +module testbed_hi_simulate; + reg pck0; + reg [7:0] adc_d; + reg mod_type; + + wire pwr_lo; + wire adc_clk; + reg ck_1356meg; + reg ck_1356megb; + wire ssp_frame; + wire ssp_din; + wire ssp_clk; + reg ssp_dout; + wire pwr_hi; + wire pwr_oe1; + wire pwr_oe2; + wire pwr_oe3; + wire pwr_oe4; + wire cross_lo; + wire cross_hi; + wire dbg; + + hi_simulate #(5,200) dut( + .pck0(pck0), + .ck_1356meg(ck_1356meg), + .ck_1356megb(ck_1356megb), + .pwr_lo(pwr_lo), + .pwr_hi(pwr_hi), + .pwr_oe1(pwr_oe1), + .pwr_oe2(pwr_oe2), + .pwr_oe3(pwr_oe3), + .pwr_oe4(pwr_oe4), + .adc_d(adc_d), + .adc_clk(adc_clk), + .ssp_frame(ssp_frame), + .ssp_din(ssp_din), + .ssp_dout(ssp_dout), + .ssp_clk(ssp_clk), + .cross_hi(cross_hi), + .cross_lo(cross_lo), + .dbg(dbg), + .mod_type(mod_type) + ); + + integer idx, i; + + // main clock + always #5 begin + ck_1356megb = !ck_1356megb; + ck_1356meg = ck_1356megb; + end + + always begin + @(negedge adc_clk) ; + adc_d = $random; + end + + //crank DUT + task crank_dut; + begin + @(negedge ssp_clk) ; + ssp_dout = $random; + end + endtask + + initial begin + + // init inputs + ck_1356megb = 0; + // random values + adc_d = 0; + ssp_dout=1; + + // shallow modulation off + mod_type=0; + for (i = 0 ; i < 16 ; i = i + 1) begin + crank_dut; + end + + // shallow modulation on + mod_type=1; + for (i = 0 ; i < 16 ; i = i + 1) begin + crank_dut; + end + $finish; + end + +endmodule // main + diff --git a/fpga/testbed_lo_read.v b/fpga/testbed_lo_read.v new file mode 100644 index 00000000..11908d77 --- /dev/null +++ b/fpga/testbed_lo_read.v @@ -0,0 +1,105 @@ +`include "lo_read.v" + +/* + pck0 - input main 24Mhz clock (PLL / 4) + [7:0] adc_d - input data from A/D converter + lo_is_125khz - input freq selector (1=125Khz, 0=136Khz) + + pwr_lo - output to coil drivers (ssp_clk / 8) + adc_clk - output A/D clock signal + ssp_frame - output SSS frame indicator (goes high while the 8 bits are shifted) + ssp_din - output SSP data to ARM (shifts 8 bit A/D value serially to ARM MSB first) + ssp_clk - output SSP clock signal 1Mhz/1.09Mhz (pck0 / 2*(11+lo_is_125khz) ) + + ck_1356meg - input unused + ck_1356megb - input unused + ssp_dout - input unused + cross_hi - input unused + cross_lo - input unused + + pwr_hi - output unused, tied low + pwr_oe1 - output unused, undefined + pwr_oe2 - output unused, undefined + pwr_oe3 - output unused, undefined + pwr_oe4 - output unused, undefined + dbg - output alias for adc_clk +*/ + +module testbed_lo_read; + reg pck0; + reg [7:0] adc_d; + reg lo_is_125khz; + + wire pwr_lo; + wire adc_clk; + wire ck_1356meg; + wire ck_1356megb; + wire ssp_frame; + wire ssp_din; + wire ssp_clk; + wire ssp_dout; + wire pwr_hi; + wire pwr_oe1; + wire pwr_oe2; + wire pwr_oe3; + wire pwr_oe4; + wire cross_lo; + wire cross_hi; + wire dbg; + + lo_read #(5,200) dut( + .pck0(pck0), + .ck_1356meg(ck_1356meg), + .ck_1356megb(ck_1356megb), + .pwr_lo(pwr_lo), + .pwr_hi(pwr_hi), + .pwr_oe1(pwr_oe1), + .pwr_oe2(pwr_oe2), + .pwr_oe3(pwr_oe3), + .pwr_oe4(pwr_oe4), + .adc_d(adc_d), + .adc_clk(adc_clk), + .ssp_frame(ssp_frame), + .ssp_din(ssp_din), + .ssp_dout(ssp_dout), + .ssp_clk(ssp_clk), + .cross_hi(cross_hi), + .cross_lo(cross_lo), + .dbg(dbg), + .lo_is_125khz(lo_is_125khz) + ); + + integer idx, i; + + // main clock + always #5 pck0 = !pck0; + + //new A/D value available from ADC on positive edge + task crank_dut; + begin + @(posedge adc_clk) ; + adc_d = $random; + end + endtask + + initial begin + + // init inputs + pck0 = 0; + adc_d = 0; + + // simulate 4 A/D cycles at 134Khz + lo_is_125khz=0; + for (i = 0 ; i < 4 ; i = i + 1) begin + crank_dut; + end + + // simulate 4 A/D cycles at 125Khz + lo_is_125khz=1; + for (i = 0 ; i < 4 ; i = i + 1) begin + crank_dut; + end + $finish; + end + +endmodule // main diff --git a/fpga/testbed_lo_simulate.v b/fpga/testbed_lo_simulate.v new file mode 100644 index 00000000..d30f822d --- /dev/null +++ b/fpga/testbed_lo_simulate.v @@ -0,0 +1,101 @@ +`include "lo_simulate.v" + +/* + pck0 - input main 24Mhz clock (PLL / 4) + [7:0] adc_d - input data from A/D converter + + + pwr_lo - output to coil drivers (ssp_clk / 8) + adc_clk - output A/D clock signal + ssp_frame - output SSS frame indicator (goes high while the 8 bits are shifted) + ssp_din - output SSP data to ARM (shifts 8 bit A/D value serially to ARM MSB first) + ssp_clk - output SSP clock signal + + ck_1356meg - input unused + ck_1356megb - input unused + ssp_dout - input unused + cross_hi - input unused + cross_lo - input unused + + pwr_hi - output unused, tied low + pwr_oe1 - output unused, undefined + pwr_oe2 - output unused, undefined + pwr_oe3 - output unused, undefined + pwr_oe4 - output unused, undefined + dbg - output alias for adc_clk +*/ + +module testbed_lo_simulate; + reg pck0; + reg [7:0] adc_d; + + + wire pwr_lo; + wire adc_clk; + wire ck_1356meg; + wire ck_1356megb; + wire ssp_frame; + wire ssp_din; + wire ssp_clk; + reg ssp_dout; + wire pwr_hi; + wire pwr_oe1; + wire pwr_oe2; + wire pwr_oe3; + wire pwr_oe4; + reg cross_lo; + wire cross_hi; + wire dbg; + + lo_simulate #(5,200) dut( + .pck0(pck0), + .ck_1356meg(ck_1356meg), + .ck_1356megb(ck_1356megb), + .pwr_lo(pwr_lo), + .pwr_hi(pwr_hi), + .pwr_oe1(pwr_oe1), + .pwr_oe2(pwr_oe2), + .pwr_oe3(pwr_oe3), + .pwr_oe4(pwr_oe4), + .adc_d(adc_d), + .adc_clk(adc_clk), + .ssp_frame(ssp_frame), + .ssp_din(ssp_din), + .ssp_dout(ssp_dout), + .ssp_clk(ssp_clk), + .cross_hi(cross_hi), + .cross_lo(cross_lo), + .dbg(dbg) + ); + + + integer i, counter=0; + + // main clock + always #5 pck0 = !pck0; + + //cross_lo is not really synced to pck0 but it's roughly pck0/192 (24Mhz/192=125Khz) + task crank_dut; + begin + @(posedge pck0) ; + counter = counter + 1; + if (counter == 192) begin + counter = 0; + ssp_dout = $random; + cross_lo = 1; + end else begin + cross_lo = 0; + end + + end + endtask + + initial begin + pck0 = 0; + for (i = 0 ; i < 4096 ; i = i + 1) begin + crank_dut; + end + $finish; + end + +endmodule // main diff --git a/fpga/util.v b/fpga/util.v new file mode 100644 index 00000000..c500edb4 --- /dev/null +++ b/fpga/util.v @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------------- +// General-purpose miscellany. +// +// Jonathan Westhues, April 2006. +//----------------------------------------------------------------------------- + +module mux8(sel, y, x0, x1, x2, x3, x4, x5, x6, x7); + input [2:0] sel; + input x0, x1, x2, x3, x4, x5, x6, x7; + output y; + reg y; + +always @(x0 or x1 or x2 or x3 or x4 or x5 or x6 or x7 or sel) +begin + case (sel) + 3'b000: y = x0; + 3'b001: y = x1; + 3'b010: y = x2; + 3'b011: y = x3; + 3'b100: y = x4; + 3'b101: y = x5; + 3'b110: y = x6; + 3'b111: y = x7; + endcase +end + +endmodule diff --git a/fpga/xst.scr b/fpga/xst.scr new file mode 100644 index 00000000..365db39a --- /dev/null +++ b/fpga/xst.scr @@ -0,0 +1 @@ +run -ifn fpga.v -ifmt Verilog -ofn fpga.ngc -ofmt NGC -p xc2s30-6vq100 -opt_mode Speed -opt_level 1 -ent fpga diff --git a/include/at91sam7s128.h b/include/at91sam7s128.h new file mode 100644 index 00000000..1a5fe1c4 --- /dev/null +++ b/include/at91sam7s128.h @@ -0,0 +1,461 @@ +//----------------------------------------------------------------------------- +// Incomplete register definitions for the AT91SAM7S128 chip. +// Jonathan Westhues, Jul 2005 +//----------------------------------------------------------------------------- + +#ifndef __AT91SAM7S128_H +#define __AT91SAM7S128_H + +#define REG(x) (*(volatile unsigned long *)(x)) + +//------------- +// Peripheral IDs + +#define PERIPH_AIC_FIQ 0 +#define PERIPH_SYSIRQ 1 +#define PERIPH_PIOA 2 +#define PERIPH_ADC 4 +#define PERIPH_SPI 5 +#define PERIPH_US0 6 +#define PERIPH_US1 7 +#define PERIPH_SSC 8 +#define PERIPH_TWI 9 +#define PERIPH_PWMC 10 +#define PERIPH_UDP 11 +#define PERIPH_TC0 12 +#define PERIPH_TC1 13 +#define PERIPH_TC2 14 +#define PERIPH_AIC_IRQ0 30 +#define PERIPH_AIC_IRQ1 31 + +//------------- +// Reset Controller + +#define RSTC_BASE (0xfffffd00) + +#define RSTC_CONTROL REG(RSTC_BASE+0x00) + +#define RST_CONTROL_KEY (0xa5<<24) +#define RST_CONTROL_PROCESSOR_RESET (1<<0) + +//------------- +// PWM Controller + +#define PWM_BASE (0xfffcc000) + +#define PWM_MODE REG(PWM_BASE+0x00) +#define PWM_ENABLE REG(PWM_BASE+0x04) +#define PWM_DISABLE REG(PWM_BASE+0x08) +#define PWM_STATUS REG(PWM_BASE+0x0c) +#define PWM_INTERRUPT_ENABLE REG(PWM_BASE+0x10) +#define PWM_INTERRUPT_DISABLE REG(PWM_BASE+0x14) +#define PWM_INTERRUPT_MASK REG(PWM_BASE+0x18) +#define PWM_INTERRUPT_STATUS REG(PWM_BASE+0x1c) +#define PWM_CH_MODE(x) REG(PWM_BASE+0x200+((x)*0x20)) +#define PWM_CH_DUTY_CYCLE(x) REG(PWM_BASE+0x204+((x)*0x20)) +#define PWM_CH_PERIOD(x) REG(PWM_BASE+0x208+((x)*0x20)) +#define PWM_CH_COUNTER(x) REG(PWM_BASE+0x20c+((x)*0x20)) +#define PWM_CH_UPDATE(x) REG(PWM_BASE+0x210+((x)*0x20)) + +#define PWM_MODE_DIVA(x) ((x)<<0) +#define PWM_MODE_PREA(x) ((x)<<8) +#define PWM_MODE_DIVB(x) ((x)<<16) +#define PWM_MODE_PREB(x) ((x)<<24) + +#define PWM_CHANNEL(x) (1<<(x)) + +#define PWM_CH_MODE_PRESCALER(x) ((x)<<0) +#define PWM_CH_MODE_PERIOD_CENTER_ALIGNED (1<<8) +#define PWM_CH_MODE_POLARITY_STARTS_HIGH (1<<9) +#define PWM_CH_MODE_UPDATE_UPDATES_PERIOD (1<<10) + +//------------- +// Debug Unit + +#define DBG_BASE (0xfffff200) + +#define DBGU_CR REG(DBG_BASE+0x0000) +#define DBGU_MR REG(DBG_BASE+0x0004) +#define DBGU_IER REG(DBG_BASE+0x0008) +#define DBGU_IDR REG(DBG_BASE+0x000C) +#define DBGU_IMR REG(DBG_BASE+0x0010) +#define DBGU_SR REG(DBG_BASE+0x0014) +#define DBGU_RHR REG(DBG_BASE+0x0018) +#define DBGU_THR REG(DBG_BASE+0x001C) +#define DBGU_BRGR REG(DBG_BASE+0x0020) +#define DBGU_CIDR REG(DBG_BASE+0x0040) +#define DBGU_EXID REG(DBG_BASE+0x0044) +#define DBGU_FNR REG(DBG_BASE+0x0048) + +//------------- +// Embedded Flash Controller + +#define MC_BASE (0xffffff00) + +#define MC_FLASH_MODE0 REG(MC_BASE+0x60) +#define MC_FLASH_COMMAND REG(MC_BASE+0x64) +#define MC_FLASH_STATUS REG(MC_BASE+0x68) +#define MC_FLASH_MODE1 REG(MC_BASE+0x70) + +#define MC_FLASH_MODE_READY_INTERRUPT_ENABLE (1<<0) +#define MC_FLASH_MODE_LOCK_INTERRUPT_ENABLE (1<<2) +#define MC_FLASH_MODE_PROG_ERROR_INTERRUPT_ENABLE (1<<3) +#define MC_FLASH_MODE_NO_ERASE_BEFORE_PROGRAMMING (1<<7) +#define MC_FLASH_MODE_FLASH_WAIT_STATES(x) ((x)<<8) +#define MC_FLASH_MODE_MASTER_CLK_IN_MHZ(x) ((x)<<16) + +#define MC_FLASH_COMMAND_FCMD(x) ((x)<<0) +#define MC_FLASH_COMMAND_PAGEN(x) ((x)<<8) +#define MC_FLASH_COMMAND_KEY ((0x5a)<<24) + +#define FCMD_NOP 0x0 +#define FCMD_WRITE_PAGE 0x1 +#define FCMD_SET_LOCK_BIT 0x2 +#define FCMD_WRITE_PAGE_LOCK 0x3 +#define FCMD_CLEAR_LOCK_BIT 0x4 +#define FCMD_ERASE_ALL 0x8 +#define FCMD_SET_GP_NVM_BIT 0xb +#define FCMD_SET_SECURITY_BIT 0xf + +#define MC_FLASH_STATUS_READY (1<<0) +#define MC_FLASH_STATUS_LOCK_ERROR (1<<2) +#define MC_FLASH_STATUS_PROGRAMMING_ERROR (1<<3) +#define MC_FLASH_STATUS_SECURITY_BIT_ACTIVE (1<<4) +#define MC_FLASH_STATUS_GP_NVM_ACTIVE_0 (1<<8) +#define MC_FLASH_STATUS_GP_NVM_ACTIVE_1 (1<<9) +#define MC_FLASH_STATUS_LOCK_ACTIVE(x) (1<<((x)+16)) + +#define FLASH_PAGE_SIZE_BYTES 256 +#define FLASH_PAGE_COUNT 512 + +//------------- +// Watchdog Timer - 12 bit down counter, uses slow clock divided by 128 as source + +#define WDT_BASE (0xfffffd40) + +#define WDT_CONTROL REG(WDT_BASE+0x00) +#define WDT_MODE REG(WDT_BASE+0x04) +#define WDT_STATUS REG(WDT_BASE+0x08) + +#define WDT_HIT() WDT_CONTROL = 0xa5000001 + +#define WDT_MODE_COUNT(x) ((x)<<0) +#define WDT_MODE_INTERRUPT_ON_EVENT (1<<12) +#define WDT_MODE_RESET_ON_EVENT_ENABLE (1<<13) +#define WDT_MODE_RESET_ON_EVENT (1<<14) +#define WDT_MODE_WATCHDOG_DELTA(x) ((x)<<16) +#define WDT_MODE_HALT_IN_DEBUG_MODE (1<<28) +#define WDT_MODE_HALT_IN_IDLE_MODE (1<<29) +#define WDT_MODE_DISABLE (1<<15) + +//------------- +// Parallel Input/Output Controller + +#define PIO_BASE (0xfffff400) + +#define PIO_ENABLE REG(PIO_BASE+0x000) +#define PIO_DISABLE REG(PIO_BASE+0x004) +#define PIO_STATUS REG(PIO_BASE+0x008) +#define PIO_OUTPUT_ENABLE REG(PIO_BASE+0x010) +#define PIO_OUTPUT_DISABLE REG(PIO_BASE+0x014) +#define PIO_OUTPUT_STATUS REG(PIO_BASE+0x018) +#define PIO_GLITCH_ENABLE REG(PIO_BASE+0x020) +#define PIO_GLITCH_DISABLE REG(PIO_BASE+0x024) +#define PIO_GLITCH_STATUS REG(PIO_BASE+0x028) +#define PIO_OUTPUT_DATA_SET REG(PIO_BASE+0x030) +#define PIO_OUTPUT_DATA_CLEAR REG(PIO_BASE+0x034) +#define PIO_OUTPUT_DATA_STATUS REG(PIO_BASE+0x038) +#define PIO_PIN_DATA_STATUS REG(PIO_BASE+0x03c) +#define PIO_OPEN_DRAIN_ENABLE REG(PIO_BASE+0x050) +#define PIO_OPEN_DRAIN_DISABLE REG(PIO_BASE+0x054) +#define PIO_OPEN_DRAIN_STATUS REG(PIO_BASE+0x058) +#define PIO_NO_PULL_UP_ENABLE REG(PIO_BASE+0x060) +#define PIO_NO_PULL_UP_DISABLE REG(PIO_BASE+0x064) +#define PIO_NO_PULL_UP_STATUS REG(PIO_BASE+0x068) +#define PIO_PERIPHERAL_A_SEL REG(PIO_BASE+0x070) +#define PIO_PERIPHERAL_B_SEL REG(PIO_BASE+0x074) +#define PIO_PERIPHERAL_WHICH REG(PIO_BASE+0x078) +#define PIO_OUT_WRITE_ENABLE REG(PIO_BASE+0x0a0) +#define PIO_OUT_WRITE_DISABLE REG(PIO_BASE+0x0a4) +#define PIO_OUT_WRITE_STATUS REG(PIO_BASE+0x0a8) + +//------------- +// USB Device Port + +#define UDP_BASE (0xfffb0000) + +#define UDP_FRAME_NUMBER REG(UDP_BASE+0x0000) +#define UDP_GLOBAL_STATE REG(UDP_BASE+0x0004) +#define UDP_FUNCTION_ADDR REG(UDP_BASE+0x0008) +#define UDP_INTERRUPT_ENABLE REG(UDP_BASE+0x0010) +#define UDP_INTERRUPT_DISABLE REG(UDP_BASE+0x0014) +#define UDP_INTERRUPT_MASK REG(UDP_BASE+0x0018) +#define UDP_INTERRUPT_STATUS REG(UDP_BASE+0x001c) +#define UDP_INTERRUPT_CLEAR REG(UDP_BASE+0x0020) +#define UDP_RESET_ENDPOINT REG(UDP_BASE+0x0028) +#define UDP_ENDPOINT_CSR(x) REG(UDP_BASE+0x0030+((x)*4)) +#define UDP_ENDPOINT_FIFO(x) REG(UDP_BASE+0x0050+((x)*4)) +#define UDP_TRANSCEIVER_CTRL REG(UDP_BASE+0x0074) + +#define UDP_GLOBAL_STATE_ADDRESSED (1<<0) +#define UDP_GLOBAL_STATE_CONFIGURED (1<<1) +#define UDP_GLOBAL_STATE_SEND_RESUME_ENABLED (1<<2) +#define UDP_GLOBAL_STATE_RESUME_RECEIVED (1<<3) +#define UDP_GLOBAL_STATE_REMOTE_WAKE_UP_ENABLED (1<<4) + +#define UDP_FUNCTION_ADDR_ENABLED (1<<8) + +#define UDP_INTERRUPT_ENDPOINT(x) (1<<(x)) +#define UDP_INTERRUPT_SUSPEND (1<<8) +#define UDP_INTERRUPT_RESUME (1<<9) +#define UDP_INTERRUPT_EXTERNAL_RESUME (1<<10) +#define UDP_INTERRUPT_SOF (1<<11) +#define UDP_INTERRUPT_END_OF_BUS_RESET (1<<12) +#define UDP_INTERRUPT_WAKEUP (1<<13) + +#define UDP_RESET_ENDPOINT_NUMBER(x) (1<<(x)) + +#define UDP_CSR_TX_PACKET_ACKED (1<<0) +#define UDP_CSR_RX_PACKET_RECEIVED_BANK_0 (1<<1) +#define UDP_CSR_RX_HAVE_READ_SETUP_DATA (1<<2) +#define UDP_CSR_STALL_SENT (1<<3) +#define UDP_CSR_TX_PACKET (1<<4) +#define UDP_CSR_FORCE_STALL (1<<5) +#define UDP_CSR_RX_PACKET_RECEIVED_BANK_1 (1<<6) +#define UDP_CSR_CONTROL_DATA_DIR (1<<7) +#define UDP_CSR_EPTYPE_CONTROL (0<<8) +#define UDP_CSR_EPTYPE_ISOCHRON_OUT (1<<8) +#define UDP_CSR_EPTYPE_ISOCHRON_IN (5<<8) +#define UDP_CSR_EPTYPE_BULK_OUT (2<<8) +#define UDP_CSR_EPTYPE_BULK_IN (6<<8) +#define UDP_CSR_EPTYPE_INTERRUPT_OUT (3<<8) +#define UDP_CSR_EPTYPE_INTERRUPT_IN (7<<8) +#define UDP_CSR_IS_DATA1 (1<<11) +#define UDP_CSR_ENABLE_EP (1<<15) +#define UDP_CSR_BYTES_RECEIVED(x) (((x) >> 16) & 0x7ff) + +#define UDP_TRANSCEIVER_CTRL_DISABLE (1<<8) + +//------------- +// Power Management Controller + +#define PMC_BASE (0xfffffc00) + +#define PMC_SYS_CLK_ENABLE REG(PMC_BASE+0x0000) +#define PMC_SYS_CLK_DISABLE REG(PMC_BASE+0x0004) +#define PMC_SYS_CLK_STATUS REG(PMC_BASE+0x0008) +#define PMC_PERIPHERAL_CLK_ENABLE REG(PMC_BASE+0x0010) +#define PMC_PERIPHERAL_CLK_DISABLE REG(PMC_BASE+0x0014) +#define PMC_PERIPHERAL_CLK_STATUS REG(PMC_BASE+0x0018) +#define PMC_MAIN_OSCILLATOR REG(PMC_BASE+0x0020) +#define PMC_MAIN_CLK_FREQUENCY REG(PMC_BASE+0x0024) +#define PMC_PLL REG(PMC_BASE+0x002c) +#define PMC_MASTER_CLK REG(PMC_BASE+0x0030) +#define PMC_PROGRAMMABLE_CLK_0 REG(PMC_BASE+0x0040) +#define PMC_PROGRAMMABLE_CLK_1 REG(PMC_BASE+0x0044) +#define PMC_INTERRUPT_ENABLE REG(PMC_BASE+0x0060) +#define PMC_INTERRUPT_DISABLE REG(PMC_BASE+0x0064) +#define PMC_INTERRUPT_STATUS REG(PMC_BASE+0x0068) +#define PMC_INTERRUPT_MASK REG(PMC_BASE+0x006c) + +#define PMC_SYS_CLK_PROCESSOR_CLK (1<<0) +#define PMC_SYS_CLK_UDP_CLK (1<<7) +#define PMC_SYS_CLK_PROGRAMMABLE_CLK_0 (1<<8) +#define PMC_SYS_CLK_PROGRAMMABLE_CLK_1 (1<<9) +#define PMC_SYS_CLK_PROGRAMMABLE_CLK_2 (1<<10) + +#define PMC_MAIN_OSCILLATOR_STABILIZED (1<<0) +#define PMC_MAIN_OSCILLATOR_PLL_LOCK (1<<2) +#define PMC_MAIN_OSCILLATOR_MCK_READY (1<<3) +#define PMC_MAIN_OSCILLATOR_ENABLE (1<<0) +#define PMC_MAIN_OSCILLATOR_BYPASS (1<<1) +#define PMC_MAIN_OSCILLATOR_STARTUP_DELAY(x) ((x)<<8) + +#define PMC_PLL_DIVISOR(x) (x) +#define PMC_PLL_COUNT_BEFORE_LOCK(x) ((x)<<8) +#define PMC_PLL_FREQUENCY_RANGE(x) ((x)<<14) +#define PMC_PLL_MULTIPLIER(x) (((x)-1)<<16) +#define PMC_PLL_USB_DIVISOR(x) ((x)<<28) + +#define PMC_CLK_SELECTION_PLL_CLOCK (3<<0) +#define PMC_CLK_SELECTION_MAIN_CLOCK (1<<0) +#define PMC_CLK_SELECTION_SLOW_CLOCK (0<<0) +#define PMC_CLK_PRESCALE_DIV_1 (0<<2) +#define PMC_CLK_PRESCALE_DIV_2 (1<<2) +#define PMC_CLK_PRESCALE_DIV_4 (2<<2) +#define PMC_CLK_PRESCALE_DIV_8 (3<<2) +#define PMC_CLK_PRESCALE_DIV_16 (4<<2) +#define PMC_CLK_PRESCALE_DIV_32 (5<<2) +#define PMC_CLK_PRESCALE_DIV_64 (6<<2) + +//------------- +// Serial Peripheral Interface (SPI) + +#define SPI_BASE (0xfffe0000) + +#define SPI_CONTROL REG(SPI_BASE+0x00) +#define SPI_MODE REG(SPI_BASE+0x04) +#define SPI_RX_DATA REG(SPI_BASE+0x08) +#define SPI_TX_DATA REG(SPI_BASE+0x0c) +#define SPI_STATUS REG(SPI_BASE+0x10) +#define SPI_INTERRUPT_ENABLE REG(SPI_BASE+0x14) +#define SPI_INTERRUPT_DISABLE REG(SPI_BASE+0x18) +#define SPI_INTERRUPT_MASK REG(SPI_BASE+0x1c) +#define SPI_FOR_CHIPSEL_0 REG(SPI_BASE+0x30) +#define SPI_FOR_CHIPSEL_1 REG(SPI_BASE+0x34) +#define SPI_FOR_CHIPSEL_2 REG(SPI_BASE+0x38) +#define SPI_FOR_CHIPSEL_3 REG(SPI_BASE+0x3c) + +#define SPI_CONTROL_ENABLE (1<<0) +#define SPI_CONTROL_DISABLE (1<<1) +#define SPI_CONTROL_RESET (1<<7) +#define SPI_CONTROL_LAST_TRANSFER (1<<24) + +#define SPI_MODE_MASTER (1<<0) +#define SPI_MODE_VARIABLE_CHIPSEL (1<<1) +#define SPI_MODE_CHIPSELS_DECODED (1<<2) +#define SPI_MODE_USE_DIVIDED_CLOCK (1<<3) +#define SPI_MODE_MODE_FAULT_DETECTION_OFF (1<<4) +#define SPI_MODE_LOOPBACK (1<<7) +#define SPI_MODE_CHIPSEL(x) ((x)<<16) +#define SPI_MODE_DELAY_BETWEEN_CHIPSELS(x) ((x)<<24) + +#define SPI_RX_DATA_CHIPSEL(x) (((x)>>16)&0xf) + +#define SPI_TX_DATA_CHIPSEL(x) ((x)<<16) +#define SPI_TX_DATA_LAST_TRANSFER (1<<24) + +#define SPI_STATUS_RECEIVE_FULL (1<<0) +#define SPI_STATUS_TRANSMIT_EMPTY (1<<1) +#define SPI_STATUS_MODE_FAULT (1<<2) +#define SPI_STATUS_OVERRUN (1<<3) +#define SPI_STATUS_END_OF_RX_BUFFER (1<<4) +#define SPI_STATUS_END_OF_TX_BUFFER (1<<5) +#define SPI_STATUS_RX_BUFFER_FULL (1<<6) +#define SPI_STATUS_TX_BUFFER_EMPTY (1<<7) +#define SPI_STATUS_NSS_RISING_DETECTED (1<<8) +#define SPI_STATUS_TX_EMPTY (1<<9) +#define SPI_STATUS_SPI_ENABLED (1<<16) + +#define SPI_FOR_CHIPSEL_INACTIVE_CLK_1 (1<<0) +#define SPI_FOR_CHIPSEL_PHASE (1<<1) +#define SPI_FOR_CHIPSEL_LEAVE_CHIPSEL_LOW (1<<3) +#define SPI_FOR_CHIPSEL_BITS_IN_WORD(x) ((x)<<4) +#define SPI_FOR_CHIPSEL_DIVISOR(x) ((x)<<8) +#define SPI_FOR_CHIPSEL_DELAY_BEFORE_CLK(x) ((x)<<16) +#define SPI_FOR_CHIPSEL_INTERWORD_DELAY(x) ((x)<<24) + +//------------- +// Analog to Digital Converter + +#define ADC_BASE (0xfffd8000) + +#define ADC_CONTROL REG(ADC_BASE+0x00) +#define ADC_MODE REG(ADC_BASE+0x04) +#define ADC_CHANNEL_ENABLE REG(ADC_BASE+0x10) +#define ADC_CHANNEL_DISABLE REG(ADC_BASE+0x14) +#define ADC_CHANNEL_STATUS REG(ADC_BASE+0x18) +#define ADC_STATUS REG(ADC_BASE+0x1c) +#define ADC_LAST_CONVERTED_DATA REG(ADC_BASE+0x20) +#define ADC_INTERRUPT_ENABLE REG(ADC_BASE+0x24) +#define ADC_INTERRUPT_DISABLE REG(ADC_BASE+0x28) +#define ADC_INTERRUPT_MASK REG(ADC_BASE+0x2c) +#define ADC_CHANNEL_DATA(x) REG(ADC_BASE+0x30+(4*(x))) + +#define ADC_CONTROL_RESET (1<<0) +#define ADC_CONTROL_START (1<<1) + +#define ADC_MODE_HW_TRIGGERS_ENABLED (1<<0) +#define ADC_MODE_8_BIT_RESOLUTION (1<<4) +#define ADC_MODE_SLEEP (1<<5) +#define ADC_MODE_PRESCALE(x) ((x)<<8) +#define ADC_MODE_STARTUP_TIME(x) ((x)<<16) +#define ADC_MODE_SAMPLE_HOLD_TIME(x) ((x)<<24) + +#define ADC_CHANNEL(x) (1<<(x)) + +#define ADC_END_OF_CONVERSION(x) (1<<(x)) +#define ADC_OVERRUN_ERROR(x) (1<<(8+(x))) +#define ADC_DATA_READY (1<<16) +#define ADC_GENERAL_OVERRUN (1<<17) +#define ADC_END_OF_RX_BUFFER (1<<18) +#define ADC_RX_BUFFER_FULL (1<<19) + +//------------- +// Synchronous Serial Controller + +#define SSC_BASE (0xfffd4000) + +#define SSC_CONTROL REG(SSC_BASE+0x00) +#define SSC_CLOCK_DIVISOR REG(SSC_BASE+0x04) +#define SSC_RECEIVE_CLOCK_MODE REG(SSC_BASE+0x10) +#define SSC_RECEIVE_FRAME_MODE REG(SSC_BASE+0x14) +#define SSC_TRANSMIT_CLOCK_MODE REG(SSC_BASE+0x18) +#define SSC_TRANSMIT_FRAME_MODE REG(SSC_BASE+0x1c) +#define SSC_RECEIVE_HOLDING REG(SSC_BASE+0x20) +#define SSC_TRANSMIT_HOLDING REG(SSC_BASE+0x24) +#define SSC_RECEIVE_SYNC_HOLDING REG(SSC_BASE+0x30) +#define SSC_TRANSMIT_SYNC_HOLDING REG(SSC_BASE+0x34) +#define SSC_STATUS REG(SSC_BASE+0x40) +#define SSC_INTERRUPT_ENABLE REG(SSC_BASE+0x44) +#define SSC_INTERRUPT_DISABLE REG(SSC_BASE+0x48) +#define SSC_INTERRUPT_MASK REG(SSC_BASE+0x4c) + +#define SSC_CONTROL_RX_ENABLE (1<<0) +#define SSC_CONTROL_RX_DISABLE (1<<1) +#define SSC_CONTROL_TX_ENABLE (1<<8) +#define SSC_CONTROL_TX_DISABLE (1<<9) +#define SSC_CONTROL_RESET (1<<15) + +#define SSC_CLOCK_MODE_SELECT(x) ((x)<<0) +#define SSC_CLOCK_MODE_OUTPUT(x) ((x)<<2) +#define SSC_CLOCK_MODE_INVERT (1<<5) +#define SSC_CLOCK_MODE_START(x) ((x)<<8) +#define SSC_CLOCK_MODE_START_DELAY(x) ((x)<<16) +#define SSC_CLOCK_MODE_FRAME_PERIOD(x) ((x)<<24) + +#define SSC_FRAME_MODE_BITS_IN_WORD(x) (((x)-1)<<0) +#define SSC_FRAME_MODE_LOOPBACK (1<<5) // for RX +#define SSC_FRAME_MODE_DEFAULT_IS_1 (1<<5) // for TX +#define SSC_FRAME_MODE_MSB_FIRST (1<<7) +#define SSC_FRAME_MODE_WORDS_PER_TRANSFER(x) ((x)<<8) +#define SSC_FRAME_MODE_FRAME_SYNC_LEN(x) ((x)<<16) +#define SSC_FRAME_MODE_FRAME_SYNC_TYPE(x) ((x)<<20) +#define SSC_FRAME_MODE_SYNC_DATA_ENABLE (1<<23) // for TX only +#define SSC_FRAME_MODE_NEGATIVE_EDGE (1<<24) + +#define SSC_STATUS_TX_READY (1<<0) +#define SSC_STATUS_TX_EMPTY (1<<1) +#define SSC_STATUS_TX_ENDED (1<<2) +#define SSC_STATUS_TX_BUF_EMPTY (1<<3) +#define SSC_STATUS_RX_READY (1<<4) +#define SSC_STATUS_RX_OVERRUN (1<<5) +#define SSC_STATUS_RX_ENDED (1<<6) +#define SSC_STATUS_RX_BUF_FULL (1<<7) +#define SSC_STATUS_TX_SYNC_OCCURRED (1<<10) +#define SSC_STATUS_RX_SYNC_OCCURRED (1<<11) +#define SSC_STATUS_TX_ENABLED (1<<16) +#define SSC_STATUS_RX_ENABLED (1<<17) + +//------------- +// Peripheral DMA Controller +// +// There is one set of registers for every peripheral that supports DMA. + +#define PDC_RX_POINTER(x) REG((x)+0x100) +#define PDC_RX_COUNTER(x) REG((x)+0x104) +#define PDC_TX_POINTER(x) REG((x)+0x108) +#define PDC_TX_COUNTER(x) REG((x)+0x10c) +#define PDC_RX_NEXT_POINTER(x) REG((x)+0x110) +#define PDC_RX_NEXT_COUNTER(x) REG((x)+0x114) +#define PDC_TX_NEXT_POINTER(x) REG((x)+0x118) +#define PDC_TX_NEXT_COUNTER(x) REG((x)+0x11c) +#define PDC_CONTROL(x) REG((x)+0x120) +#define PDC_STATUS(x) REG((x)+0x124) + +#define PDC_RX_ENABLE (1<<0) +#define PDC_RX_DISABLE (1<<1) +#define PDC_TX_ENABLE (1<<8) +#define PDC_TX_DISABLE (1<<9) + +#endif diff --git a/include/config_gpio.h b/include/config_gpio.h new file mode 100644 index 00000000..1a189a9e --- /dev/null +++ b/include/config_gpio.h @@ -0,0 +1,40 @@ +#ifndef __CONFIG_GPIO_H +#define __CONFIG_GPIO_H + +#define GPIO_LED_A 0 +#define GPIO_PA1 1 +#define GPIO_LED_D 2 +#define GPIO_NVDD_ON 3 +#define GPIO_FPGA_NINIT 4 +#define GPIO_PA5 5 +#define GPIO_PCK0 6 +#define GPIO_LRST 7 +#define GPIO_LED_B 8 +#define GPIO_LED_C 9 +#define GPIO_NCS2 10 +#define GPIO_NCS0 11 +#define GPIO_MISO 12 +#define GPIO_MOSI 13 +#define GPIO_SPCK 14 +#define GPIO_SSC_FRAME 15 +#define GPIO_SSC_CLK 16 +#define GPIO_SSC_DOUT 17 +#define GPIO_SSC_DIN 18 +#define GPIO_MUXSEL_HIPKD 19 +#define GPIO_MUXSEL_LOPKD 20 +#define GPIO_MUXSEL_HIRAW 21 +#define GPIO_MUXSEL_LORAW 22 +#define GPIO_BUTTON 23 +#define GPIO_USB_PU 24 +#define GPIO_RELAY 25 +#define GPIO_FPGA_ON 26 +#define GPIO_FPGA_DONE 27 +#define GPIO_FPGA_NPROGRAM 28 +#define GPIO_FPGA_CCLK 29 +#define GPIO_FPGA_DIN 30 +#define GPIO_FPGA_DOUT 31 + +#define ANIN_AMPL_LO 4 +#define ANIN_AMPL_HI 5 + +#endif diff --git a/include/proxmark3.h b/include/proxmark3.h new file mode 100644 index 00000000..a94435e8 --- /dev/null +++ b/include/proxmark3.h @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Definitions of interest to most of the software for this project. +// Jonathan Westhues, Mar 2006 +//----------------------------------------------------------------------------- + +#ifndef __PROXMARK3_H +#define __PROXMARK3_H + +// Might as well have the hardware-specific defines everywhere. +#include + +#include +#define LOW(x) PIO_OUTPUT_DATA_CLEAR = (1 << (x)) +#define HIGH(x) PIO_OUTPUT_DATA_SET = (1 << (x)) + +#define SPI_FPGA_MODE 0 +#define SPI_LCD_MODE 1 + +typedef unsigned long DWORD; +typedef signed long SDWORD; +typedef unsigned long long QWORD; +typedef int BOOL; +typedef unsigned char BYTE; +typedef signed char SBYTE; +typedef unsigned short WORD; +typedef signed short SWORD; +#define TRUE 1 +#define FALSE 0 + +#include + +#define PACKED __attribute__((__packed__)) + +#define USB_D_PLUS_PULLUP_ON() { \ + PIO_OUTPUT_DATA_SET = (1</dev/null) -Wall +QTLDFLAGS = $(shell pkg-config --libs QtCore QtGui 2>/dev/null) + +ifneq ($(QTLDFLAGS),) +QTGUI = proxgui.o proxguiqt.o proxguiqt.moc.o +CFLAGS += -DHAVE_GUI +MOC = $(shell type moc-qt4 >/dev/null 2>&1 && echo moc-qt4 || echo moc) +LINK.o = $(LINK.cpp) +else +QTGUI = guidummy.o +endif + +all: proxmark3 snooper + +proxmark3: LDFLAGS+=$(QTLDFLAGS) +proxmark3: proxmark3.o gui.o command.o usb.o $(QTGUI) + +snooper: snooper.o gui.o command.o usb.o guidummy.o + +proxguiqt.moc.cpp: proxguiqt.h + $(MOC) -o$@ $^ + +clean: + rm -f proxmark3 snooper *.o *.moc.cpp + +.PHONY: all clean diff --git a/linux/command.c b/linux/command.c new file mode 100644 index 00000000..902045c6 --- /dev/null +++ b/linux/command.c @@ -0,0 +1,2 @@ +#include "translate.h" +#include "../winsrc/command.cpp" diff --git a/linux/flasher.c b/linux/flasher.c new file mode 100644 index 00000000..05f31249 --- /dev/null +++ b/linux/flasher.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "translate.h" +#include "../winsrc/prox.h" +#include "proxmark3.h" + +static DWORD ExpectedAddr; +static BYTE QueuedToSend[256]; +static BOOL AllWritten; + +static void FlushPrevious(void) +{ + UsbCommand c; + memset(&c, 0, sizeof(c)); + + printf("expected = %08x flush, ", ExpectedAddr); + + int i; + for(i = 0; i < 240; i += 48) { + c.cmd = CMD_SETUP_WRITE; + memcpy(c.d.asBytes, QueuedToSend+i, 48); + c.ext1 = (i/4); + SendCommand(&c, TRUE); + } + + c.cmd = CMD_FINISH_WRITE; + c.ext1 = (ExpectedAddr-1) & (~255); + printf("c.ext1 = %08x\r", c.ext1); + memcpy(c.d.asBytes, QueuedToSend+240, 16); + SendCommand(&c, TRUE); + + AllWritten = TRUE; +} + +static void GotByte(DWORD where, BYTE which) +{ + AllWritten = FALSE; + + if(where != ExpectedAddr) { + printf("bad: got at %08x, expected at %08x\n", where, ExpectedAddr); + exit(-1); + } + QueuedToSend[where & 255] = which; + ExpectedAddr++; + + if((where & 255) == 255) { + // we have completed a full page + FlushPrevious(); + } +} + +static int HexVal(int c) +{ + c = tolower(c); + if(c >= '0' && c <= '9') { + return c - '0'; + } else if(c >= 'a' && c <= 'f') { + return (c - 'a') + 10; + } else { + printf("bad hex digit '%c'\n", c); + exit(-1); + } +} + +static BYTE HexByte(char *s) +{ + return (HexVal(s[0]) << 4) | HexVal(s[1]); +} + +static void LoadFlashFromSRecords(char *file, int addr) +{ + ExpectedAddr = addr; + + FILE *f = fopen(file, "r"); + if(!f) { + printf("couldn't open file\n"); + exit(-1); + } + + char line[512]; + while(fgets(line, sizeof(line), f)) { + if(memcmp(line, "S3", 2)==0) { + char *s = line + 2; + int len = HexByte(s) - 5; + s += 2; + + char addrStr[9]; + memcpy(addrStr, s, 8); + addrStr[8] = '\0'; + DWORD addr; + sscanf(addrStr, "%x", &addr); + s += 8; + + int i; + for(i = 0; i < len; i++) { + while((addr+i) > ExpectedAddr) { + GotByte(ExpectedAddr, 0xff); + } + GotByte(addr+i, HexByte(s)); + s += 2; + } + } + } + + if(!AllWritten) FlushPrevious(); + + fclose(f); + printf("\ndone.\n"); +} + +int main(int argc, char **argv) { + unsigned int addr = 0; + UsbCommand c; + + if (argc != 3) { + fprintf(stderr,"Usage: %s {bootrom|os|fpga} image.s19\n", argv[0]); + exit(EXIT_FAILURE); + } + + if (!strcmp(argv[1],"bootrom")) { + addr = 0; + } else if (!strcmp(argv[1],"os")) { + addr = 0x10000; + } else if (!strcmp(argv[1],"fpga")) { + addr = 0x2000; + } else { + fprintf(stderr,"Unknown action '%s'!\n", argv[1]); + exit(EXIT_FAILURE); + } + + usb_init(); + + fprintf(stderr,"Waiting for Proxmark to appear on USB...\n"); + while(!(devh=OpenProxmark(0))) { sleep(1); } + fprintf(stderr,"Found...\n"); + + fprintf(stderr,"Entering flash-mode...\n"); + bzero(&c, sizeof(c)); + c.cmd = CMD_START_FLASH; + SendCommand(&c, FALSE); + CloseProxmark(); + sleep(1); + + fprintf(stderr,"Waiting for Proxmark to reappear on USB...\n"); + while(!(devh=OpenProxmark(0))) { sleep(1); } + fprintf(stderr,"Found...\n"); + + LoadFlashFromSRecords(argv[2], addr); + + bzero(&c, sizeof(c)); + c.cmd = CMD_HARDWARE_RESET; + SendCommand(&c, FALSE); + + CloseProxmark(); + + fprintf(stderr,"Have a nice day!\n"); + + return 0; +} diff --git a/linux/gui.c b/linux/gui.c new file mode 100644 index 00000000..6d442f0e --- /dev/null +++ b/linux/gui.c @@ -0,0 +1,54 @@ +#include +#include +#include + +#include "proxgui.h" +#include "translate.h" +#include "../winsrc/prox.h" + +int GraphBuffer[MAX_GRAPH_TRACE_LEN]; +int GraphTraceLen; +double CursorScaleFactor; +int CommandFinished; + +static char *logfilename = "proxmark3.log"; + +void PrintToScrollback(char *fmt, ...) { + va_list argptr; + static FILE *logfile = NULL; + static int logging=1; + + if (logging && !logfile) { + logfile=fopen(logfilename, "a"); + if (!logfile) { + fprintf(stderr, "Can't open logfile, logging disabled!\n"); + logging=0; + } + } + + va_start(argptr, fmt); + vprintf(fmt, argptr); + printf("\n"); + if (logging && logfile) { +#if 0 + char zeit[25]; + time_t jetzt_t; + struct tm *jetzt; + + jetzt_t = time(NULL); + jetzt = localtime(&jetzt_t); + strftime(zeit, 25, "%b %e %T", jetzt); + + fprintf(logfile,"%s ", zeit); +#endif + vfprintf(logfile, fmt, argptr); + fprintf(logfile,"\n"); + fflush(logfile); + } + va_end(argptr); +} + +void setlogfilename(char *fn) +{ + logfilename = fn; +} diff --git a/linux/guidummy.c b/linux/guidummy.c new file mode 100644 index 00000000..39bcc756 --- /dev/null +++ b/linux/guidummy.c @@ -0,0 +1,17 @@ +#include + +void ShowGraphWindow(void) +{ + static int warned = 0; + + if (!warned) { + printf("No GUI in this build!\n"); + warned = 1; + } +} + +void HideGraphWindow(void) {} +void RepaintGraphWindow(void) {} +void MainGraphics() {} +void InitGraphics(int argc, char **argv) {} +void ExitGraphics(void) {} diff --git a/linux/proxgui.cpp b/linux/proxgui.cpp new file mode 100644 index 00000000..7e87b582 --- /dev/null +++ b/linux/proxgui.cpp @@ -0,0 +1,58 @@ +#include "proxgui.h" +#include "proxguiqt.h" + +static ProxGuiQT *gui = NULL; + +extern "C" void ShowGraphWindow(void) +{ + if (!gui) + return; + + gui->ShowGraphWindow(); +} + +extern "C" void HideGraphWindow(void) +{ + if (!gui) + return; + + gui->HideGraphWindow(); +} + +extern "C" void RepaintGraphWindow(void) +{ + if (!gui) + return; + + gui->RepaintGraphWindow(); +} + +extern "C" void MainGraphics(void) +{ + if (!gui) + return; + + gui->MainLoop(); +} + +extern "C" void InitGraphics(int argc, char **argv) +{ +#ifdef Q_WS_X11 + bool useGUI = getenv("DISPLAY") != 0; +#else + bool useGUI = true; +#endif + if (!useGUI) + return; + + gui = new ProxGuiQT(argc, argv); +} + +extern "C" void ExitGraphics(void) +{ + if (!gui) + return; + + delete gui; + gui = NULL; +} diff --git a/linux/proxgui.h b/linux/proxgui.h new file mode 100644 index 00000000..c1e9198f --- /dev/null +++ b/linux/proxgui.h @@ -0,0 +1,20 @@ +#ifdef __cplusplus +extern "C" { +#endif + +void ShowGraphWindow(void); +void HideGraphWindow(void); +void RepaintGraphWindow(void); +void MainGraphics(void); +void InitGraphics(int argc, char **argv); +void ExitGraphics(void); + +#define MAX_GRAPH_TRACE_LEN (1024*128) +extern int GraphBuffer[MAX_GRAPH_TRACE_LEN]; +extern int GraphTraceLen; +extern double CursorScaleFactor; +extern int CommandFinished; + +#ifdef __cplusplus +} +#endif diff --git a/linux/proxguiqt.cpp b/linux/proxguiqt.cpp new file mode 100644 index 00000000..773d74b8 --- /dev/null +++ b/linux/proxguiqt.cpp @@ -0,0 +1,309 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "proxguiqt.h" +#include "proxgui.h" + +void ProxGuiQT::ShowGraphWindow(void) +{ + emit ShowGraphWindowSignal(); +} + +void ProxGuiQT::RepaintGraphWindow(void) +{ + emit RepaintGraphWindowSignal(); +} + +void ProxGuiQT::HideGraphWindow(void) +{ + emit HideGraphWindowSignal(); +} + +void ProxGuiQT::_ShowGraphWindow(void) +{ + if(!plotapp) + return; + + if (!plotwidget) + plotwidget = new ProxWidget(); + + plotwidget->show(); +} + +void ProxGuiQT::_RepaintGraphWindow(void) +{ + if (!plotapp || !plotwidget) + return; + + plotwidget->update(); +} + +void ProxGuiQT::_HideGraphWindow(void) +{ + if (!plotapp || !plotwidget) + return; + + plotwidget->hide(); +} + +void ProxGuiQT::MainLoop() +{ + plotapp = new QApplication(argc, argv); + + connect(this, SIGNAL(ShowGraphWindowSignal()), this, SLOT(_ShowGraphWindow())); + connect(this, SIGNAL(RepaintGraphWindowSignal()), this, SLOT(_RepaintGraphWindow())); + connect(this, SIGNAL(HideGraphWindowSignal()), this, SLOT(_HideGraphWindow())); + + plotapp->exec(); +} + +ProxGuiQT::ProxGuiQT(int argc, char **argv) : plotapp(NULL), plotwidget(NULL), + argc(argc), argv(argv) +{ +} + +ProxGuiQT::~ProxGuiQT(void) +{ + if (plotwidget) { + delete plotwidget; + plotwidget = NULL; + } + + if (plotapp) { + plotapp->quit(); + delete plotapp; + plotapp = NULL; + } +} + +void ProxWidget::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + QPainterPath penPath, whitePath, greyPath, cursorAPath, cursorBPath; + QRect r; + QBrush brush(QColor(100, 255, 100)); + QPen pen(QColor(100, 255, 100)); + + painter.setFont(QFont("Arial", 10)); + + if(GraphStart < 0) { + GraphStart = 0; + } + + r = rect(); + + painter.fillRect(r, QColor(0, 0, 0)); + + whitePath.moveTo(r.left() + 40, r.top()); + whitePath.lineTo(r.left() + 40, r.bottom()); + + int zeroHeight = r.top() + (r.bottom() - r.top()) / 2; + + greyPath.moveTo(r.left(), zeroHeight); + greyPath.lineTo(r.right(), zeroHeight); + painter.setPen(QColor(100, 100, 100)); + painter.drawPath(greyPath); + + int startMax = + (GraphTraceLen - (int)((r.right() - r.left() - 40) / GraphPixelsPerPoint)); + if(startMax < 0) { + startMax = 0; + } + if(GraphStart > startMax) { + GraphStart = startMax; + } + + int absYMax = 1; + + int i; + for(i = GraphStart; ; i++) { + if(i >= GraphTraceLen) { + break; + } + if(fabs((double)GraphBuffer[i]) > absYMax) { + absYMax = (int)fabs((double)GraphBuffer[i]); + } + int x = 40 + (int)((i - GraphStart)*GraphPixelsPerPoint); + if(x > r.right()) { + break; + } + } + + absYMax = (int)(absYMax*1.2 + 1); + + // number of points that will be plotted + int span = (int)((r.right() - r.left()) / GraphPixelsPerPoint); + // one label every 100 pixels, let us say + int labels = (r.right() - r.left() - 40) / 100; + if(labels <= 0) labels = 1; + int pointsPerLabel = span / labels; + if(pointsPerLabel <= 0) pointsPerLabel = 1; + + int yMin = INT_MAX; + int yMax = INT_MIN; + int yMean = 0; + int n = 0; + + for(i = GraphStart; ; i++) { + if(i >= GraphTraceLen) { + break; + } + int x = 40 + (int)((i - GraphStart)*GraphPixelsPerPoint); + if(x > r.right() + GraphPixelsPerPoint) { + break; + } + + int y = GraphBuffer[i]; + if(y < yMin) { + yMin = y; + } + if(y > yMax) { + yMax = y; + } + yMean += y; + n++; + + y = (y * (r.top() - r.bottom()) / (2*absYMax)) + zeroHeight; + if(i == GraphStart) { + penPath.moveTo(x, y); + } else { + penPath.lineTo(x, y); + } + + if(GraphPixelsPerPoint > 10) { + QRect f(QPoint(x - 3, y - 3),QPoint(x + 3, y + 3)); + painter.fillRect(f, brush); + } + + if(((i - GraphStart) % pointsPerLabel == 0) && i != GraphStart) { + whitePath.moveTo(x, zeroHeight - 3); + whitePath.lineTo(x, zeroHeight + 3); + + char str[100]; + sprintf(str, "+%d", (i - GraphStart)); + + painter.setPen(QColor(255, 255, 255)); + QRect size; + QFontMetrics metrics(painter.font()); + size = metrics.boundingRect(str); + painter.drawText(x - (size.right() - size.left()), zeroHeight + 9, str); + + penPath.moveTo(x,y); + } + + if(i == CursorAPos || i == CursorBPos) { + QPainterPath *cursorPath; + + if(i == CursorAPos) { + cursorPath = &cursorAPath; + } else { + cursorPath = &cursorBPath; + } + cursorPath->moveTo(x, r.top()); + cursorPath->lineTo(x, r.bottom()); + penPath.moveTo(x, y); + } + } + + if(n != 0) { + yMean /= n; + } + + painter.setPen(QColor(255, 255, 255)); + painter.drawPath(whitePath); + painter.setPen(pen); + painter.drawPath(penPath); + painter.setPen(QColor(255, 255, 0)); + painter.drawPath(cursorAPath); + painter.setPen(QColor(255, 0, 255)); + painter.drawPath(cursorBPath); + + char str[100]; + sprintf(str, "@%d max=%d min=%d mean=%d n=%d/%d dt=%d [%.3f]", + GraphStart, yMax, yMin, yMean, n, GraphTraceLen, + CursorBPos - CursorAPos, (CursorBPos - CursorAPos)/CursorScaleFactor); + + painter.setPen(QColor(255, 255, 255)); + painter.drawText(50, r.bottom() - 20, str); +} + +ProxWidget::ProxWidget(QWidget *parent) : QWidget(parent), GraphStart(0), GraphPixelsPerPoint(1) +{ + resize(600, 500); + + QPalette palette(QColor(0,0,0,0)); + palette.setColor(QPalette::WindowText, QColor(255,255,255)); + palette.setColor(QPalette::Text, QColor(255,255,255)); + palette.setColor(QPalette::Button, QColor(100, 100, 100)); + setPalette(palette); + setAutoFillBackground(true); +} + +void ProxWidget::closeEvent(QCloseEvent *event) +{ + event->ignore(); + this->hide(); +} + +void ProxWidget::mouseMoveEvent(QMouseEvent *event) +{ + int x = event->x(); + x -= 40; + x = (int)(x / GraphPixelsPerPoint); + x += GraphStart; + if((event->buttons() & Qt::LeftButton)) { + CursorAPos = x; + } else if (event->buttons() & Qt::RightButton) { + CursorBPos = x; + } + + + this->update(); +} + +void ProxWidget::keyPressEvent(QKeyEvent *event) +{ + switch(event->key()) { + case Qt::Key_Down: + if(GraphPixelsPerPoint <= 50) { + GraphPixelsPerPoint *= 2; + } + break; + + case Qt::Key_Up: + if(GraphPixelsPerPoint >= 0.02) { + GraphPixelsPerPoint /= 2; + } + break; + + case Qt::Key_Right: + if(GraphPixelsPerPoint < 20) { + GraphStart += (int)(20 / GraphPixelsPerPoint); + } else { + GraphStart++; + } + break; + + case Qt::Key_Left: + if(GraphPixelsPerPoint < 20) { + GraphStart -= (int)(20 / GraphPixelsPerPoint); + } else { + GraphStart--; + } + break; + + default: + QWidget::keyPressEvent(event); + return; + break; + } + + this->update(); +} diff --git a/linux/proxguiqt.h b/linux/proxguiqt.h new file mode 100644 index 00000000..58ff8326 --- /dev/null +++ b/linux/proxguiqt.h @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include + +class ProxWidget : public QWidget +{ + Q_OBJECT; + + private: + int GraphStart; + double GraphPixelsPerPoint; + int CursorAPos; + int CursorBPos; + + public: + ProxWidget(QWidget *parent = 0); + + protected: + void paintEvent(QPaintEvent *event); + void closeEvent(QCloseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event) { mouseMoveEvent(event); } + void keyPressEvent(QKeyEvent *event); +}; + +class ProxGuiQT : public QObject +{ + Q_OBJECT; + + private: + QApplication *plotapp; + ProxWidget *plotwidget; + int argc; + char **argv; + void (*main_func)(void); + + public: + ProxGuiQT(int argc, char **argv); + ~ProxGuiQT(void); + void ShowGraphWindow(void); + void RepaintGraphWindow(void); + void HideGraphWindow(void); + void MainLoop(void); + + private slots: + void _ShowGraphWindow(void); + void _RepaintGraphWindow(void); + void _HideGraphWindow(void); + + signals: + void ShowGraphWindowSignal(void); + void RepaintGraphWindowSignal(void); + void HideGraphWindowSignal(void); +}; diff --git a/linux/proxmark3.c b/linux/proxmark3.c new file mode 100644 index 00000000..5a3d6f2d --- /dev/null +++ b/linux/proxmark3.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "translate.h" +#include "../winsrc/prox.h" +#include "proxmark3.h" +#include "proxgui.h" + +struct usb_receiver_arg { + int run; +}; + +static void *usb_receiver(void *targ) { + struct usb_receiver_arg *arg = (struct usb_receiver_arg*)targ; + UsbCommand cmdbuf; + + while(arg->run) { + if (ReceiveCommandP(&cmdbuf) > 0) { + int i; + + for (i=0; i " + +extern usb_dev_handle *devh; +extern unsigned char return_on_error; +extern unsigned char error_occured; + +int ReceiveCommandP(UsbCommand *c); +usb_dev_handle* OpenProxmark(int); +void CloseProxmark(void); + +void setlogfilename(char *fn); diff --git a/linux/snooper.c b/linux/snooper.c new file mode 100644 index 00000000..63fa406f --- /dev/null +++ b/linux/snooper.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "translate.h" +#include "../winsrc/prox.h" +#include "proxmark3.h" + +#define HANDLE_ERROR if (error_occured) { \ + error_occured = 0;\ + break;\ +} + +int main() +{ + usb_init(); + setlogfilename("snooper.log"); + + return_on_error = 1; + + while(1) { + while(!(devh=OpenProxmark(0))) { sleep(1); } + + while(1) { + UsbCommand cmdbuf; + int i; + + CommandReceived("hi14asnoop"); + HANDLE_ERROR + + ReceiveCommand(&cmdbuf); + HANDLE_ERROR + for (i=0; i<5; i++) { + ReceiveCommandP(&cmdbuf); + } + HANDLE_ERROR + + CommandReceived("hi14alist"); + HANDLE_ERROR + } + } + + CloseProxmark(); + return 0; +} diff --git a/linux/translate.h b/linux/translate.h new file mode 100644 index 00000000..145268a0 --- /dev/null +++ b/linux/translate.h @@ -0,0 +1,9 @@ +#define BYTE unsigned char +#define WORD unsigned short +#define DWORD unsigned int +#define TRUE 1 +#define FALSE 0 +#define BOOL int + +#define max(a,b) (((a)>(b))?(a):(b)) +#define min(a,b) (((a)>(b))?(b):(a)) diff --git a/linux/unbind-proxmark b/linux/unbind-proxmark new file mode 100755 index 00000000..986c0015 --- /dev/null +++ b/linux/unbind-proxmark @@ -0,0 +1,16 @@ +#!/bin/sh + +for i in /sys/bus/usb/devices/*; do + if grep "9ac4" "${i}/idVendor" >/dev/null 2>&1; then + echo "Found Proxmark..." + dev=`basename "${i}"` + + for j in /sys/bus/usb/drivers/usbhid/*; do + if basename "${j}"|grep "^${dev}" >/dev/null; then + bound="`basename "${j}"`" + echo "Unbinding ${bound}..." + echo -n "${bound}" >/sys/bus/usb/drivers/usbhid/unbind + fi + done + fi +done diff --git a/linux/usb.c b/linux/usb.c new file mode 100644 index 00000000..8145b64a --- /dev/null +++ b/linux/usb.c @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include +#include + +#include "translate.h" +#include "../winsrc/prox.h" +#include "proxmark3.h" + +usb_dev_handle *devh = NULL; +static unsigned int claimed_iface = 0; +unsigned char return_on_error = 0; +unsigned char error_occured = 0; + +void SendCommand(UsbCommand *c, BOOL wantAck) { + int ret; + +#if 0 + printf("Sending %d bytes\n", sizeof(UsbCommand)); +#endif + ret = usb_bulk_write(devh, 0x01, (char*)c, sizeof(UsbCommand), 1000); + if (ret<0) { + error_occured = 1; + if (return_on_error) + return; + + fprintf(stderr, "write failed: %s!\nTrying to reopen device...\n", + usb_strerror()); + + if (devh) { + usb_close(devh); + devh = NULL; + } + while(!(devh=OpenProxmark(0))) { sleep(1); } + printf(PROXPROMPT); + fflush(NULL); + + return; + } + + if(wantAck) { + UsbCommand ack; + ReceiveCommand(&ack); + if(ack.cmd != CMD_ACK) { + printf("bad ACK\n"); + exit(-1); + } + } +} + +int ReceiveCommandP(UsbCommand *c) { + int ret; + + bzero(c, sizeof(UsbCommand)); + ret = usb_bulk_read(devh, 0x82, (char*)c, sizeof(UsbCommand), 500); + if (ret<0) { + if (ret != -ETIMEDOUT) { + error_occured = 1; + if (return_on_error) + return 0; + + fprintf(stderr, "read failed: %s(%d)!\nTrying to reopen device...\n", + usb_strerror(), ret); + + if (devh) { + usb_close(devh); + devh = NULL; + } + while(!(devh=OpenProxmark(0))) { sleep(1); } + printf(PROXPROMPT); + fflush(NULL); + + return 0; + } + } else { + if (ret && (ret < sizeof(UsbCommand))) { + fprintf(stderr, "Read only %d instead of requested %d bytes!\n", + ret, (int)sizeof(UsbCommand)); + } + +#if 0 + { + int i; + + printf("Read %d bytes\n", ret); + for (i = 0; i < ret; i++) { + printf("0x%02X ", ((unsigned char*)c)[i]); + if (!((i+1)%8)) + printf("\n"); + } + printf("\n"); + } +#endif + } + + return ret; +} + +void ReceiveCommand(UsbCommand *c) { + while(ReceiveCommandP(c)<0) {} +} + +usb_dev_handle* findProxmark(int verbose, unsigned int *iface) { + struct usb_bus *busses, *bus; + usb_dev_handle *handle = NULL; + + usb_find_busses(); + usb_find_devices(); + + busses = usb_get_busses(); + + for (bus = busses; bus; bus = bus->next) { + struct usb_device *dev; + + for (dev = bus->devices; dev; dev = dev->next) { + struct usb_device_descriptor *desc = &(dev->descriptor); + + if ((desc->idProduct == 0x4b8f) && (desc->idVendor == 0x9ac4)) { + handle = usb_open(dev); + if (!handle) { + if (verbose) + fprintf(stderr, "open failed: %s!\n", usb_strerror()); + return NULL; + } + + *iface = dev->config[0].interface[0].altsetting[0].bInterfaceNumber; + + return handle; + } + } + } + + return NULL; +} + +usb_dev_handle* OpenProxmark(int verbose) { + int ret; + usb_dev_handle *handle = NULL; + unsigned int iface; + +#ifndef __APPLE__ + handle = findProxmark(verbose, &iface); + if (!handle) + return NULL; + + /* Whatever... */ + usb_reset(handle); +#endif + + handle = findProxmark(verbose, &iface); + if (!handle) + return NULL; + + ret = usb_claim_interface(handle, iface); + if (ret<0) { + if (verbose) + fprintf(stderr, "claim failed: %s!\n", usb_strerror()); + return NULL; + } + + claimed_iface = iface; + devh = handle; + return handle; +} + +void CloseProxmark(void) { + usb_release_interface(devh, claimed_iface); + usb_close(devh); +} diff --git a/linux/windows.h b/linux/windows.h new file mode 100644 index 00000000..e69de29b diff --git a/winsrc/Makefile b/winsrc/Makefile new file mode 100644 index 00000000..b398c2c1 --- /dev/null +++ b/winsrc/Makefile @@ -0,0 +1,31 @@ +BASE_DEFS = /D_WIN32_WINNT=0x501 /DISOLATION_AWARE_ENABLED /D_WIN32_IE=0x600 /DWIN32_LEAN_AND_MEAN /DWIN32 /D_MT /D_CRT_SECURE_NO_WARNINGS +BASE_CFLAGS = /W3 /nologo + +DEFINES = $(BASE_DEFS) +CFLAGS = $(BASE_CFLAGS) /Zi /MT + +OBJDIR = obj + +OBJS = $(OBJDIR)\prox.obj \ + $(OBJDIR)\gui.obj \ + $(OBJDIR)\command.obj + +LIBS = kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib setupapi.lib + +HEADERS = prox.h + +all: $(OBJDIR)/prox.exe + copy $(OBJDIR)\prox.exe . + +clean: + del /q obj\*.obj + del /q obj\*.ilk + del /q obj\*.exe + del /q obj\*.pdb + del /q *.pdb + +$(OBJDIR)/prox.exe: $(OBJS) + $(CC) $(DEFINES) $(CFLAGS) -Fe$(OBJDIR)/prox.exe $(OBJS) $(LIBS) + +$(OBJS): $(@B).cpp $(HEADERS) + $(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj $(@B).cpp diff --git a/winsrc/command.cpp b/winsrc/command.cpp new file mode 100644 index 00000000..1da1e1c2 --- /dev/null +++ b/winsrc/command.cpp @@ -0,0 +1,1812 @@ +//----------------------------------------------------------------------------- +// The actual command interpeter for what the user types at the command line. +// Jonathan Westhues, Sept 2005 +// Edits by Gerhard de Koning Gans, Sep 2007 (##) +//----------------------------------------------------------------------------- +#include +#include +#include +#include +#include +#include + +#include "prox.h" +#include "../common/iso14443_crc.c" + +#define arraylen(x) (sizeof(x)/sizeof((x)[0])) + +static int CmdHisamplest(char *str, int nrlow); + +static void GetFromBigBuf(BYTE *dest, int bytes) +{ + int n = bytes/4; + + if(n % 48 != 0) { + PrintToScrollback("bad len in GetFromBigBuf"); + return; + } + + int i; + for(i = 0; i < n; i += 12) { + UsbCommand c; + c.cmd = CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K; + c.ext1 = i; + SendCommand(&c, FALSE); + ReceiveCommand(&c); + if(c.cmd != CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) { + PrintToScrollback("bad resp\n"); + return; + } + + memcpy(dest+(i*4), c.d.asBytes, 48); + } +} + +static void CmdQuit(char *str) +{ + exit(0); +} + +static void CmdHIDdemodFSK(char *str) +{ + UsbCommand c; + c.cmd = CMD_HID_DEMOD_FSK; + SendCommand(&c, FALSE); +} + +static void CmdTune(char *str) +{ + UsbCommand c; + c.cmd = CMD_MEASURE_ANTENNA_TUNING; + SendCommand(&c, FALSE); +} + +static void CmdHi15read(char *str) +{ + UsbCommand c; + c.cmd = CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693; + SendCommand(&c, FALSE); +} + +static void CmdHi14read(char *str) +{ + UsbCommand c; + c.cmd = CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443; + c.ext1 = atoi(str); + SendCommand(&c, FALSE); +} + +// ## New command +static void CmdHi14areader(char *str) +{ + UsbCommand c; + c.cmd = CMD_READER_ISO_14443a; + c.ext1 = atoi(str); + SendCommand(&c, FALSE); +} + +// ## New command +static void CmdHi15reader(char *str) +{ + UsbCommand c; + c.cmd = CMD_READER_ISO_15693; + c.ext1 = atoi(str); + SendCommand(&c, FALSE); +} + +// ## New command +static void CmdHi15tag(char *str) +{ + UsbCommand c; + c.cmd = CMD_SIMTAG_ISO_15693; + c.ext1 = atoi(str); + SendCommand(&c, FALSE); +} + +static void CmdHi14read_sim(char *str) +{ + UsbCommand c; + c.cmd = CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443_SIM; + c.ext1 = atoi(str); + SendCommand(&c, FALSE); +} + +static void CmdHi14readt(char *str) +{ + UsbCommand c; + c.cmd = CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443; + c.ext1 = atoi(str); + SendCommand(&c, FALSE); + + //CmdHisamplest(str); + while(CmdHisamplest(str,atoi(str))==0) { + c.cmd = CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443; + c.ext1 = atoi(str); + SendCommand(&c, FALSE); + } + RepaintGraphWindow(); +} + +static void CmdHisimlisten(char *str) +{ + UsbCommand c; + c.cmd = CMD_SIMULATE_TAG_HF_LISTEN; + SendCommand(&c, FALSE); +} + +static void CmdHi14sim(char *str) +{ + UsbCommand c; + c.cmd = CMD_SIMULATE_TAG_ISO_14443; + SendCommand(&c, FALSE); +} + + + +static void CmdHi14asim(char *str) // ## simulate iso14443a tag +{ // ## greg - added ability to specify tag UID + + unsigned int hi=0, lo=0; + int n=0, i=0; + UsbCommand c; + + while (sscanf(&str[i++], "%1x", &n ) == 1) { + hi=(hi<<4)|(lo>>28); + lo=(lo<<4)|(n&0xf); + } + + c.cmd = CMD_SIMULATE_TAG_ISO_14443a; + // c.ext should be set to *str or convert *str to the correct format for a uid + c.ext1 = hi; + c.ext2 = lo; + PrintToScrollback("Emulating 14443A TAG with UID %x%16x", hi, lo); + SendCommand(&c, FALSE); +} + +static void CmdHi14snoop(char *str) +{ + UsbCommand c; + c.cmd = CMD_SNOOP_ISO_14443; + SendCommand(&c, FALSE); +} + +static void CmdHi14asnoop(char *str) +{ + UsbCommand c; + c.cmd = CMD_SNOOP_ISO_14443a; + SendCommand(&c, FALSE); +} + +static void CmdFPGAOff(char *str) // ## FPGA Control +{ + UsbCommand c; + c.cmd = CMD_FPGA_MAJOR_MODE_OFF; + SendCommand(&c, FALSE); +} + +static void CmdLosim(char *str) +{ + int i; + + for(i = 0; i < GraphTraceLen; i += 48) { + UsbCommand c; + int j; + for(j = 0; j < 48; j++) { + c.d.asBytes[j] = GraphBuffer[i+j]; + } + c.cmd = CMD_DOWNLOADED_SIM_SAMPLES_125K; + c.ext1 = i; + SendCommand(&c, FALSE); + } + + UsbCommand c; + c.cmd = CMD_SIMULATE_TAG_125K; + c.ext1 = GraphTraceLen; + SendCommand(&c, FALSE); +} + +static void CmdLoread(char *str) +{ + UsbCommand c; + // 'h' means higher-low-frequency, 134 kHz + if(*str == 'h') { + c.ext1 = 1; + } else if (*str == '\0') { + c.ext1 = 0; + } else { + PrintToScrollback("use 'loread' or 'loread h'"); + return; + } + c.cmd = CMD_ACQUIRE_RAW_ADC_SAMPLES_125K; + SendCommand(&c, FALSE); +} + +static void CmdLosamples(char *str) +{ + int cnt = 0; + int i; + int n; + + n=atoi(str); + if (n==0) n=128; + if (n>16000) n=16000; + + for(i = 0; i < n; i += 12) { + UsbCommand c; + c.cmd = CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K; + c.ext1 = i; + SendCommand(&c, FALSE); + ReceiveCommand(&c); + if(c.cmd != CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) { + PrintToScrollback("bad resp\n"); + return; + } + int j; + for(j = 0; j < 48; j++) { + GraphBuffer[cnt++] = ((int)c.d.asBytes[j]) - 128; + } + } + GraphTraceLen = n*4; + RepaintGraphWindow(); +} + +static void CmdBitsamples(char *str) +{ + int cnt = 0; + int i; + int n; + + n = 3072; + for(i = 0; i < n; i += 12) { + UsbCommand c; + c.cmd = CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K; + c.ext1 = i; + SendCommand(&c, FALSE); + ReceiveCommand(&c); + if(c.cmd != CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) { + PrintToScrollback("bad resp\n"); + return; + } + int j, k; + for(j = 0; j < 48; j++) { + for(k = 0; k < 8; k++) { + if(c.d.asBytes[j] & (1 << (7 - k))) { + GraphBuffer[cnt++] = 1; + } else { + GraphBuffer[cnt++] = 0; + } + } + } + } + GraphTraceLen = cnt; + RepaintGraphWindow(); +} + +static void CmdHisamples(char *str) +{ + int cnt = 0; + int i; + int n; + n = 1000; + for(i = 0; i < n; i += 12) { + UsbCommand c; + c.cmd = CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K; + c.ext1 = i; + SendCommand(&c, FALSE); + ReceiveCommand(&c); + if(c.cmd != CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) { + PrintToScrollback("bad resp\n"); + return; + } + int j; + for(j = 0; j < 48; j++) { + GraphBuffer[cnt++] = (int)((BYTE)c.d.asBytes[j]); + } + } + GraphTraceLen = n*4; + + RepaintGraphWindow(); +} + + +static int CmdHisamplest(char *str, int nrlow) +{ + int cnt = 0; + int t1, t2; + int i; + int n; + int hasbeennull; + int show; + + + n = 1000; + hasbeennull = 0; + for(i = 0; i < n; i += 12) { + UsbCommand c; + c.cmd = CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K; + c.ext1 = i; + SendCommand(&c, FALSE); + ReceiveCommand(&c); + if(c.cmd != CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) { + PrintToScrollback("bad resp\n"); + return 0; + } + int j; + for(j = 0; j < 48; j++) { + t2 = (int)((BYTE)c.d.asBytes[j]); + if((t2 ^ 0xC0) & 0xC0) { hasbeennull++; } + + show = 0; + switch(show) { + case 0: + // combined + t1 = (t2 & 0x80) ^ (t2 & 0x20); + t2 = ((t2 << 1) & 0x80) ^ ((t2 << 1) & 0x20); + break; + + case 1: + // only reader + t1 = (t2 & 0x80); + t2 = ((t2 << 1) & 0x80); + break; + + case 2: + // only tag + t1 = (t2 & 0x20); + t2 = ((t2 << 1) & 0x20); + break; + + case 3: + // both, but tag with other algorithm + t1 = (t2 & 0x80) ^ (t2 & 0x08); + t2 = ((t2 << 1) & 0x80) ^ ((t2 << 1) & 0x08); + break; + } + + GraphBuffer[cnt++] = t1; + GraphBuffer[cnt++] = t2; + } + } + GraphTraceLen = n*4; +// 1130 + if(hasbeennull>nrlow || nrlow==0) { + PrintToScrollback("hasbeennull=%d", hasbeennull); + return 1; + } + else { + return 0; + } +} + + +static void CmdHexsamples(char *str) +{ + int i; + int n; + + if(atoi(str) == 0) { + n = 12; + } else { + n = atoi(str)/4; + } + + for(i = 0; i < n; i += 12) { + UsbCommand c; + c.cmd = CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K; + c.ext1 = i; + SendCommand(&c, FALSE); + ReceiveCommand(&c); + if(c.cmd != CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) { + PrintToScrollback("bad resp\n"); + return; + } + int j; + for(j = 0; j < 48; j += 8) { + PrintToScrollback("%02x %02x %02x %02x %02x %02x %02x %02x", + c.d.asBytes[j+0], + c.d.asBytes[j+1], + c.d.asBytes[j+2], + c.d.asBytes[j+3], + c.d.asBytes[j+4], + c.d.asBytes[j+5], + c.d.asBytes[j+6], + c.d.asBytes[j+7], + c.d.asBytes[j+8] + ); + } + } +} + +static void CmdHisampless(char *str) +{ + int cnt = 0; + int i; + int n; + + if(atoi(str) == 0) { + n = 1000; + } else { + n = atoi(str)/4; + } + + for(i = 0; i < n; i += 12) { + UsbCommand c; + c.cmd = CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K; + c.ext1 = i; + SendCommand(&c, FALSE); + ReceiveCommand(&c); + if(c.cmd != CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) { + PrintToScrollback("bad resp\n"); + return; + } + int j; + for(j = 0; j < 48; j++) { + GraphBuffer[cnt++] = (int)((signed char)c.d.asBytes[j]); + } + } + GraphTraceLen = cnt; + + RepaintGraphWindow(); +} + +static WORD Iso15693Crc(BYTE *v, int n) +{ + DWORD reg; + int i, j; + + reg = 0xffff; + for(i = 0; i < n; i++) { + reg = reg ^ ((DWORD)v[i]); + for (j = 0; j < 8; j++) { + if (reg & 0x0001) { + reg = (reg >> 1) ^ 0x8408; + } else { + reg = (reg >> 1); + } + } + } + + return (WORD)~reg; +} + +static void CmdHi14bdemod(char *str) +{ + int i, j, iold; + int isum, qsum; + int outOfWeakAt; + BOOL negateI, negateQ; + + BYTE data[256]; + int dataLen=0; + + // As received, the samples are pairs, correlations against I and Q + // square waves. So estimate angle of initial carrier (or just + // quadrant, actually), and then do the demod. + + // First, estimate where the tag starts modulating. + for(i = 0; i < GraphTraceLen; i += 2) { + if(abs(GraphBuffer[i]) + abs(GraphBuffer[i+1]) > 40) { + break; + } + } + if(i >= GraphTraceLen) { + PrintToScrollback("too weak to sync"); + return; + } + PrintToScrollback("out of weak at %d", i); + outOfWeakAt = i; + + // Now, estimate the phase in the initial modulation of the tag + isum = 0; + qsum = 0; + for(; i < (outOfWeakAt + 16); i += 2) { + isum += GraphBuffer[i+0]; + qsum += GraphBuffer[i+1]; + } + negateI = (isum < 0); + negateQ = (qsum < 0); + + // Turn the correlation pairs into soft decisions on the bit. + j = 0; + for(i = 0; i < GraphTraceLen/2; i++) { + int si = GraphBuffer[j]; + int sq = GraphBuffer[j+1]; + if(negateI) si = -si; + if(negateQ) sq = -sq; + GraphBuffer[i] = si + sq; + j += 2; + } + GraphTraceLen = i; + + i = outOfWeakAt/2; + while(GraphBuffer[i] > 0 && i < GraphTraceLen) + i++; + if(i >= GraphTraceLen) goto demodError; + + iold = i; + while(GraphBuffer[i] < 0 && i < GraphTraceLen) + i++; + if(i >= GraphTraceLen) goto demodError; + if((i - iold) > 23) goto demodError; + + PrintToScrollback("make it to demod loop"); + + for(;;) { + iold = i; + while(GraphBuffer[i] >= 0 && i < GraphTraceLen) + i++; + if(i >= GraphTraceLen) goto demodError; + if((i - iold) > 6) goto demodError; + + WORD shiftReg = 0; + if(i + 20 >= GraphTraceLen) goto demodError; + + for(j = 0; j < 10; j++) { + int soft = GraphBuffer[i] + GraphBuffer[i+1]; + + if(abs(soft) < ((abs(isum) + abs(qsum))/20)) { + PrintToScrollback("weak bit"); + } + + shiftReg >>= 1; + if(GraphBuffer[i] + GraphBuffer[i+1] >= 0) { + shiftReg |= 0x200; + } + + i+= 2; + } + + if( (shiftReg & 0x200) && + !(shiftReg & 0x001)) + { + // valid data byte, start and stop bits okay + PrintToScrollback(" %02x", (shiftReg >> 1) & 0xff); + data[dataLen++] = (shiftReg >> 1) & 0xff; + if(dataLen >= sizeof(data)) { + return; + } + } else if(shiftReg == 0x000) { + // this is EOF + break; + } else { + goto demodError; + } + } + + BYTE first, second; + ComputeCrc14443(CRC_14443_B, data, dataLen-2, &first, &second); + PrintToScrollback("CRC: %02x %02x (%s)\n", first, second, + (first == data[dataLen-2] && second == data[dataLen-1]) ? + "ok" : "****FAIL****"); + + RepaintGraphWindow(); + return; + +demodError: + PrintToScrollback("demod error"); + RepaintGraphWindow(); +} + +static void CmdHi14list(char *str) +{ + BYTE got[960]; + GetFromBigBuf(got, sizeof(got)); + + PrintToScrollback("recorded activity:"); + PrintToScrollback(" time :rssi: who bytes"); + PrintToScrollback("---------+----+----+-----------"); + + int i = 0; + int prev = -1; + + for(;;) { + if(i >= 900) { + break; + } + + BOOL isResponse; + int timestamp = *((DWORD *)(got+i)); + if(timestamp & 0x80000000) { + timestamp &= 0x7fffffff; + isResponse = 1; + } else { + isResponse = 0; + } + int metric = *((DWORD *)(got+i+4)); + + int len = got[i+8]; + + if(len > 100) { + break; + } + if(i + len >= 900) { + break; + } + + BYTE *frame = (got+i+9); + + char line[1000] = ""; + int j; + for(j = 0; j < len; j++) { + sprintf(line+(j*3), "%02x ", frame[j]); + } + + char *crc; + if(len > 2) { + BYTE b1, b2; + ComputeCrc14443(CRC_14443_B, frame, len-2, &b1, &b2); + if(b1 != frame[len-2] || b2 != frame[len-1]) { + crc = "**FAIL CRC**"; + } else { + crc = ""; + } + } else { + crc = "(SHORT)"; + } + + char metricString[100]; + if(isResponse) { + sprintf(metricString, "%3d", metric); + } else { + strcpy(metricString, " "); + } + + PrintToScrollback(" +%7d: %s: %s %s %s", + (prev < 0 ? 0 : timestamp - prev), + metricString, + (isResponse ? "TAG" : " "), line, crc); + + prev = timestamp; + i += (len + 9); + } +} + +static void CmdHi14alist(char *str) +{ + BYTE got[1920]; + GetFromBigBuf(got, sizeof(got)); + + PrintToScrollback("recorded activity:"); + PrintToScrollback(" ETU :rssi: who bytes"); + PrintToScrollback("---------+----+----+-----------"); + + int i = 0; + int prev = -1; + + for(;;) { + if(i >= 1900) { + break; + } + + BOOL isResponse; + int timestamp = *((DWORD *)(got+i)); + if(timestamp & 0x80000000) { + timestamp &= 0x7fffffff; + isResponse = 1; + } else { + isResponse = 0; + } + + int metric = 0; + int parityBits = *((DWORD *)(got+i+4)); + // 4 bytes of additional information... + // maximum of 32 additional parity bit information + // + // TODO: + // at each quarter bit period we can send power level (16 levels) + // or each half bit period in 256 levels. + + + int len = got[i+8]; + + if(len > 100) { + break; + } + if(i + len >= 1900) { + break; + } + + BYTE *frame = (got+i+9); + + // Break and stick with current result if buffer was not completely full + if(frame[0] == 0x44 && frame[1] == 0x44 && frame[3] == 0x44) { break; } + + char line[1000] = ""; + int j; + for(j = 0; j < len; j++) { + int oddparity = 0x01; + int k; + + for(k=0;k<8;k++) { + oddparity ^= (((frame[j] & 0xFF) >> k) & 0x01); + } + + //if((parityBits >> (len - j - 1)) & 0x01) { + if(isResponse && (oddparity != ((parityBits >> (len - j - 1)) & 0x01))) { + sprintf(line+(j*4), "%02x! ", frame[j]); + } + else { + sprintf(line+(j*4), "%02x ", frame[j]); + } + } + + char *crc; + crc = ""; + if(len > 2) { + BYTE b1, b2; + for(j = 0; j < (len - 1); j++) { + // gives problems... search for the reason.. + /*if(frame[j] == 0xAA) { + switch(frame[j+1]) { + case 0x01: + crc = "[1] Two drops close after each other"; + break; + case 0x02: + crc = "[2] Potential SOC with a drop in second half of bitperiod"; + break; + case 0x03: + crc = "[3] Segment Z after segment X is not possible"; + break; + case 0x04: + crc = "[4] Parity bit of a fully received byte was wrong"; + break; + default: + crc = "[?] Unknown error"; + break; + } + break; + }*/ + } + + if(strlen(crc)==0) { + ComputeCrc14443(CRC_14443_A, frame, len-2, &b1, &b2); + if(b1 != frame[len-2] || b2 != frame[len-1]) { + crc = (isResponse & (len < 6)) ? "" : " !crc"; + } else { + crc = ""; + } + } + } else { + crc = ""; // SHORT + } + + char metricString[100]; + if(isResponse) { + sprintf(metricString, "%3d", metric); + } else { + strcpy(metricString, " "); + } + + PrintToScrollback(" +%7d: %s: %s %s %s", + (prev < 0 ? 0 : (timestamp - prev)), + metricString, + (isResponse ? "TAG" : " "), line, crc); + + prev = timestamp; + i += (len + 9); + } + CommandFinished = 1; +} + +static void CmdHi15demod(char *str) +{ + // The sampling rate is 106.353 ksps/s, for T = 18.8 us + + // SOF defined as + // 1) Unmodulated time of 56.64us + // 2) 24 pulses of 423.75khz + // 3) logic '1' (unmodulated for 18.88us followed by 8 pulses of 423.75khz) + + static const int FrameSOF[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + -1, -1, -1, -1, + -1, -1, -1, -1, + 1, 1, 1, 1, + 1, 1, 1, 1 + }; + static const int Logic0[] = { + 1, 1, 1, 1, + 1, 1, 1, 1, + -1, -1, -1, -1, + -1, -1, -1, -1 + }; + static const int Logic1[] = { + -1, -1, -1, -1, + -1, -1, -1, -1, + 1, 1, 1, 1, + 1, 1, 1, 1 + }; + + // EOF defined as + // 1) logic '0' (8 pulses of 423.75khz followed by unmodulated for 18.88us) + // 2) 24 pulses of 423.75khz + // 3) Unmodulated time of 56.64us + + static const int FrameEOF[] = { + 1, 1, 1, 1, + 1, 1, 1, 1, + -1, -1, -1, -1, + -1, -1, -1, -1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + int i, j; + int max = 0, maxPos; + + int skip = 4; + + if(GraphTraceLen < 1000) return; + + // First, correlate for SOF + for(i = 0; i < 100; i++) { + int corr = 0; + for(j = 0; j < arraylen(FrameSOF); j += skip) { + corr += FrameSOF[j]*GraphBuffer[i+(j/skip)]; + } + if(corr > max) { + max = corr; + maxPos = i; + } + } + PrintToScrollback("SOF at %d, correlation %d", maxPos, + max/(arraylen(FrameSOF)/skip)); + + i = maxPos + arraylen(FrameSOF)/skip; + int k = 0; + BYTE outBuf[20]; + memset(outBuf, 0, sizeof(outBuf)); + BYTE mask = 0x01; + for(;;) { + int corr0 = 0, corr1 = 0, corrEOF = 0; + for(j = 0; j < arraylen(Logic0); j += skip) { + corr0 += Logic0[j]*GraphBuffer[i+(j/skip)]; + } + for(j = 0; j < arraylen(Logic1); j += skip) { + corr1 += Logic1[j]*GraphBuffer[i+(j/skip)]; + } + for(j = 0; j < arraylen(FrameEOF); j += skip) { + corrEOF += FrameEOF[j]*GraphBuffer[i+(j/skip)]; + } + // Even things out by the length of the target waveform. + corr0 *= 4; + corr1 *= 4; + + if(corrEOF > corr1 && corrEOF > corr0) { + PrintToScrollback("EOF at %d", i); + break; + } else if(corr1 > corr0) { + i += arraylen(Logic1)/skip; + outBuf[k] |= mask; + } else { + i += arraylen(Logic0)/skip; + } + mask <<= 1; + if(mask == 0) { + k++; + mask = 0x01; + } + if((i+(int)arraylen(FrameEOF)) >= GraphTraceLen) { + PrintToScrollback("ran off end!"); + break; + } + } + if(mask != 0x01) { + PrintToScrollback("error, uneven octet! (discard extra bits!)"); + PrintToScrollback(" mask=%02x", mask); + } + PrintToScrollback("%d octets", k); + + for(i = 0; i < k; i++) { + PrintToScrollback("# %2d: %02x ", i, outBuf[i]); + } + PrintToScrollback("CRC=%04x", Iso15693Crc(outBuf, k-2)); +} + +static void CmdTiread(char *str) +{ + UsbCommand c; + c.cmd = CMD_ACQUIRE_RAW_BITS_TI_TYPE; + SendCommand(&c, FALSE); +} + +static void CmdTibits(char *str) +{ + int cnt = 0; + int i; + for(i = 0; i < 1536; i += 12) { + UsbCommand c; + c.cmd = CMD_DOWNLOAD_RAW_BITS_TI_TYPE; + c.ext1 = i; + SendCommand(&c, FALSE); + ReceiveCommand(&c); + if(c.cmd != CMD_DOWNLOADED_RAW_BITS_TI_TYPE) { + PrintToScrollback("bad resp\n"); + return; + } + int j; + for(j = 0; j < 12; j++) { + int k; + for(k = 31; k >= 0; k--) { + if(c.d.asDwords[j] & (1 << k)) { + GraphBuffer[cnt++] = 1; + } else { + GraphBuffer[cnt++] = -1; + } + } + } + } + GraphTraceLen = 1536*32; + RepaintGraphWindow(); +} + +static void CmdTidemod(char *cmdline) +{ + /* MATLAB as follows: +f_s = 2000000; % sampling frequency +f_l = 123200; % low FSK tone +f_h = 134200; % high FSK tone + +T_l = 119e-6; % low bit duration +T_h = 130e-6; % high bit duration + +l = 2*pi*ones(1, floor(f_s*T_l))*(f_l/f_s); +h = 2*pi*ones(1, floor(f_s*T_h))*(f_h/f_s); + +l = sign(sin(cumsum(l))); +h = sign(sin(cumsum(h))); + */ + static const int LowTone[] = { + 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, + 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, + -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, + 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, + -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, + }; + static const int HighTone[] = { + 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, + 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, + 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, + }; + + int convLen = max(arraylen(HighTone), arraylen(LowTone)); + + int i; + for(i = 0; i < GraphTraceLen - convLen; i++) { + int j; + int lowSum = 0, highSum = 0;; + int lowLen = arraylen(LowTone); + int highLen = arraylen(HighTone); + + for(j = 0; j < lowLen; j++) { + lowSum += LowTone[j]*GraphBuffer[i+j]; + } + for(j = 0; j < highLen; j++) { + highSum += HighTone[j]*GraphBuffer[i+j]; + } + lowSum = abs((100*lowSum) / lowLen); + highSum = abs((100*highSum) / highLen); + GraphBuffer[i] = (highSum << 16) | lowSum; + } + + for(i = 0; i < GraphTraceLen - convLen - 16; i++) { + int j; + int lowTot = 0, highTot = 0; + // 16 and 15 are f_s divided by f_l and f_h, rounded + for(j = 0; j < 16; j++) { + lowTot += (GraphBuffer[i+j] & 0xffff); + } + for(j = 0; j < 15; j++) { + highTot += (GraphBuffer[i+j] >> 16); + } + GraphBuffer[i] = lowTot - highTot; + } + + GraphTraceLen -= (convLen + 16); + + RepaintGraphWindow(); + + // Okay, so now we have unsliced soft decisions; find bit-sync, and then + // get some bits. + + int max = 0, maxPos = 0; + for(i = 0; i < 6000; i++) { + int j; + int dec = 0; + for(j = 0; j < 8*arraylen(LowTone); j++) { + dec -= GraphBuffer[i+j]; + } + for(; j < 8*arraylen(LowTone) + 8*arraylen(HighTone); j++) { + dec += GraphBuffer[i+j]; + } + if(dec > max) { + max = dec; + maxPos = i; + } + } + GraphBuffer[maxPos] = 800; + GraphBuffer[maxPos+1] = -800; + + maxPos += 8*arraylen(LowTone); + GraphBuffer[maxPos] = 800; + GraphBuffer[maxPos+1] = -800; + maxPos += 8*arraylen(HighTone); + + GraphBuffer[maxPos] = 800; + GraphBuffer[maxPos+1] = -800; + + PrintToScrollback("actual data bits start at sample %d", maxPos); + + PrintToScrollback("length %d/%d", arraylen(HighTone), arraylen(LowTone)); + + GraphBuffer[maxPos] = 800; + GraphBuffer[maxPos+1] = -800; + + BYTE bits[64+16+8+1]; + bits[sizeof(bits)-1] = '\0'; + + for(i = 0; i < arraylen(bits); i++) { + int high = 0; + int low = 0; + int j; + for(j = 0; j < arraylen(LowTone); j++) { + low -= GraphBuffer[maxPos+j]; + } + for(j = 0; j < arraylen(HighTone); j++) { + high += GraphBuffer[maxPos+j]; + } + if(high > low) { + bits[i] = '1'; + maxPos += arraylen(HighTone); + } else { + bits[i] = '.'; + maxPos += arraylen(LowTone); + } + GraphBuffer[maxPos] = 800; + GraphBuffer[maxPos+1] = -800; + } + PrintToScrollback("bits: '%s'", bits); + + DWORD h = 0, l = 0; + for(i = 0; i < 32; i++) { + if(bits[i] == '1') { + l |= (1< max) { + max = GraphBuffer[i]; + } + if(GraphBuffer[i] < min) { + min = GraphBuffer[i]; + } + } + if(max != min) { + for(i = 0; i < GraphTraceLen; i++) { + GraphBuffer[i] = (GraphBuffer[i] - ((max + min)/2))*1000/ + (max - min); + } + } + RepaintGraphWindow(); +} + +static void CmdDec(char *str) +{ + int i; + for(i = 0; i < (GraphTraceLen/2); i++) { + GraphBuffer[i] = GraphBuffer[i*2]; + } + GraphTraceLen /= 2; + PrintToScrollback("decimated by 2"); + RepaintGraphWindow(); +} + +static void CmdHpf(char *str) +{ + int i; + int accum = 0; + for(i = 10; i < GraphTraceLen; i++) { + accum += GraphBuffer[i]; + } + accum /= (GraphTraceLen - 10); + for(i = 0; i < GraphTraceLen; i++) { + GraphBuffer[i] -= accum; + } + + RepaintGraphWindow(); +} + +static void CmdZerocrossings(char *str) +{ + int i; + // Zero-crossings aren't meaningful unless the signal is zero-mean. + CmdHpf(""); + + int sign = 1; + int zc = 0; + int lastZc = 0; + for(i = 0; i < GraphTraceLen; i++) { + if(GraphBuffer[i]*sign >= 0) { + // No change in sign, reproduce the previous sample count. + zc++; + GraphBuffer[i] = lastZc; + } else { + // Change in sign, reset the sample count. + sign = -sign; + GraphBuffer[i] = lastZc; + if(sign > 0) { + lastZc = zc; + zc = 0; + } + } + } + + RepaintGraphWindow(); +} + +static void CmdLtrim(char *str) +{ + int i; + int ds = atoi(str); + + for(i = ds; i < GraphTraceLen; i++) { + GraphBuffer[i-ds] = GraphBuffer[i]; + } + GraphTraceLen -= ds; + + RepaintGraphWindow(); +} + +static void CmdAutoCorr(char *str) +{ + static int CorrelBuffer[MAX_GRAPH_TRACE_LEN]; + + int window = atoi(str); + + if(window == 0) { + PrintToScrollback("needs a window"); + return; + } + + if(window >= GraphTraceLen) { + PrintToScrollback("window must be smaller than trace (%d samples)", + GraphTraceLen); + return; + } + + PrintToScrollback("performing %d correlations", GraphTraceLen - window); + + int i; + for(i = 0; i < GraphTraceLen - window; i++) { + int sum = 0; + int j; + for(j = 0; j < window; j++) { + sum += (GraphBuffer[j]*GraphBuffer[i+j]) / 256; + } + CorrelBuffer[i] = sum; + } + GraphTraceLen = GraphTraceLen - window; + memcpy(GraphBuffer, CorrelBuffer, GraphTraceLen*sizeof(int)); + + RepaintGraphWindow(); +} + +static void CmdVchdemod(char *str) +{ + // Is this the entire sync pattern, or does this also include some + // data bits that happen to be the same everywhere? That would be + // lovely to know. + static const int SyncPattern[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + // So first, we correlate for the sync pattern, and mark that. + int bestCorrel = 0, bestPos = 0; + int i; + // It does us no good to find the sync pattern, with fewer than + // 2048 samples after it... + for(i = 0; i < (GraphTraceLen-2048); i++) { + int sum = 0; + int j; + for(j = 0; j < arraylen(SyncPattern); j++) { + sum += GraphBuffer[i+j]*SyncPattern[j]; + } + if(sum > bestCorrel) { + bestCorrel = sum; + bestPos = i; + } + } + PrintToScrollback("best sync at %d [metric %d]", bestPos, bestCorrel); + + char bits[257]; + bits[256] = '\0'; + + int worst = INT_MAX; + int worstPos; + + for(i = 0; i < 2048; i += 8) { + int sum = 0; + int j; + for(j = 0; j < 8; j++) { + sum += GraphBuffer[bestPos+i+j]; + } + if(sum < 0) { + bits[i/8] = '.'; + } else { + bits[i/8] = '1'; + } + if(abs(sum) < worst) { + worst = abs(sum); + worstPos = i; + } + } + PrintToScrollback("bits:"); + PrintToScrollback("%s", bits); + PrintToScrollback("worst metric: %d at pos %d", worst, worstPos); + + if(strcmp(str, "clone")==0) { + GraphTraceLen = 0; + char *s; + for(s = bits; *s; s++) { + int j; + for(j = 0; j < 16; j++) { + GraphBuffer[GraphTraceLen++] = (*s == '1') ? 1 : 0; + } + } + RepaintGraphWindow(); + } +} + +static void CmdFlexdemod(char *str) +{ + int i; + for(i = 0; i < GraphTraceLen; i++) { + if(GraphBuffer[i] < 0) { + GraphBuffer[i] = -1; + } else { + GraphBuffer[i] = 1; + } + } + +#define LONG_WAIT 100 + int start; + for(start = 0; start < GraphTraceLen - LONG_WAIT; start++) { + int first = GraphBuffer[start]; + for(i = start; i < start + LONG_WAIT; i++) { + if(GraphBuffer[i] != first) { + break; + } + } + if(i == (start + LONG_WAIT)) { + break; + } + } + if(start == GraphTraceLen - LONG_WAIT) { + PrintToScrollback("nothing to wait for"); + return; + } + + GraphBuffer[start] = 2; + GraphBuffer[start+1] = -2; + + BYTE bits[64]; + + int bit; + i = start; + for(bit = 0; bit < 64; bit++) { + int j; + int sum = 0; + for(j = 0; j < 16; j++) { + sum += GraphBuffer[i++]; + } + if(sum > 0) { + bits[bit] = 1; + } else { + bits[bit] = 0; + } + PrintToScrollback("bit %d sum %d", bit, sum); + } + + for(bit = 0; bit < 64; bit++) { + int j; + int sum = 0; + for(j = 0; j < 16; j++) { + sum += GraphBuffer[i++]; + } + if(sum > 0 && bits[bit] != 1) { + PrintToScrollback("oops1 at %d", bit); + } + if(sum < 0 && bits[bit] != 0) { + PrintToScrollback("oops2 at %d", bit); + } + } + + GraphTraceLen = 32*64; + i = 0; + int phase = 0; + for(bit = 0; bit < 64; bit++) { + if(bits[bit] == 0) { + phase = 0; + } else { + phase = 1; + } + int j; + for(j = 0; j < 32; j++) { + GraphBuffer[i++] = phase; + phase = !phase; + } + } + + RepaintGraphWindow(); +} + +/* + * Generic command to demodulate ASK. bit length in argument. + * Giving the bit length helps discriminate ripple effects + * upon zero crossing for noisy traces. + * + * Second is convention: positive or negative (High mod means zero + * or high mod means one) + * + * Updates the Graph trace with 0/1 values + * + * Arguments: + * sl : bit length in terms of number of samples per bit + * (use yellow/purple markers to compute). + * c : 0 or 1 + */ + +static void Cmdaskdemod(char *str) { + int i; + int sign = 1; + int n = 0; + int c = 0; + int t1 = 0; + + // TODO: complain if we do not give 2 arguments here ! + sscanf(str, "%i %i", &n, &c); + if (c == 0) { + c = 1 ; + } else { + c = -1; + } + + if (GraphBuffer[0]*c > 0) { + GraphBuffer[0] = 1; + } else { + GraphBuffer[0] = 0; + } + for(i=1;i n/4 ) { + sign = -sign; + t1=i; + if (GraphBuffer[i]*c > 0){ + GraphBuffer[i]=1; + } else { + GraphBuffer[i]=0; + } + } else { + /* This is a ripple, set the current sample value + to the same as previous */ + GraphBuffer[i] = GraphBuffer[i-1]; + } + } else { + GraphBuffer[i] = GraphBuffer[i-1]; + } + } + RepaintGraphWindow(); +} + + +/* + * Manchester demodulate a bitstream. The bitstream needs to be already in + * the GraphBuffer as 0 and 1 values + * + * Give the clock rate as argument in order to help the sync - the algorithm + * resyncs at each pulse anyway. + * + * Not optimized by any means, this is the 1st time I'm writing this type of + * routine, feel free to improve... + * + * 1st argument: clock rate (as number of samples per clock rate) + */ +static void Cmdmanchesterdemod(char *str) { + int i; + int clock; + int grouping=16; + int lastval; + int lc = 0; + int bitidx = 0; + int bitidx2; + + + sscanf(str, "%i", &clock); + + int tolerance = clock/4; + /* Holds the decoded bitstream. */ + int BitStream[MAX_GRAPH_TRACE_LEN*2]; + int BitStream2[MAX_GRAPH_TRACE_LEN]; + + /* Detect first transition */ + /* Lo-Hi (arbitrary) */ + for(i=1;i>28); + lo=(lo<<4)|(n&0xf); + } + + PrintToScrollback("Emulating tag with ID %x%16x", hi, lo); + + c.cmd = CMD_HID_SIM_TAG; + c.ext1 = hi; + c.ext2 = lo; + SendCommand(&c, FALSE); +} + +static void CmdLcdReset(char *str) +{ + UsbCommand c; + c.cmd = CMD_LCD_RESET; + c.ext1 = atoi(str); + SendCommand(&c, FALSE); +} + +static void CmdLcd(char *str) +{ + int i, j; + UsbCommand c; + c.cmd = CMD_LCD; + sscanf(str, "%x %d", &i, &j); + while (j--) { + c.ext1 = i&0x1ff; + SendCommand(&c, FALSE); + } +} + +static void CmdTest(char *str) +{ +} + +typedef void HandlerFunction(char *cmdline); + +static struct { + char *name; + HandlerFunction *handler; + char *docString; +} CommandTable[] = { + "tune", CmdTune, "measure antenna tuning", + "tiread", CmdTiread, "read a TI-type 134 kHz tag", + "tibits", CmdTibits, "get raw bits for TI-type LF tag", + "tidemod", CmdTidemod, "demod raw bits for TI-type LF tag", + "vchdemod", CmdVchdemod, "demod samples for VeriChip", + "plot", CmdPlot, "show graph window", + "hide", CmdHide, "hide graph window", + "losim", CmdLosim, "simulate LF tag", + "loread", CmdLoread, "read (125/134 kHz) LF ID-only tag", + "losamples", CmdLosamples, "get raw samples for LF tag", + "hisamples", CmdHisamples, "get raw samples for HF tag", + "hisampless", CmdHisampless, "get signed raw samples, HF tag", + "hisamplest", CmdHi14readt, "get samples HF, for testing", + "higet", CmdHi14read_sim, "get samples HF, 'analog'", + "bitsamples", CmdBitsamples, "get raw samples as bitstring", + "hexsamples", CmdHexsamples, "dump big buffer as hex bytes", + "hi15read", CmdHi15read, "read HF tag (ISO 15693)", + "hi15reader", CmdHi15reader, "act like an ISO15693 reader", // new command greg + "hi15sim", CmdHi15tag, "fake an ISO15693 tag", // new command greg + "hi14read", CmdHi14read, "read HF tag (ISO 14443)", + "hi14areader", CmdHi14areader, "act like an ISO14443 Type A reader", // ## New reader command + "hi15demod", CmdHi15demod, "demod ISO15693 from tag", + "hi14bdemod", CmdHi14bdemod, "demod ISO14443 Type B from tag", + "autocorr", CmdAutoCorr, "autocorrelation over window", + "norm", CmdNorm, "normalize max/min to +/-500", + "dec", CmdDec, "decimate", + "hpf", CmdHpf, "remove DC offset from trace", + "zerocrossings", CmdZerocrossings, "count time between zero-crossings", + "ltrim", CmdLtrim, "trim from left of trace", + "scale", CmdScale, "set cursor display scale", + "flexdemod", CmdFlexdemod, "demod samples for FlexPass", + "save", CmdSave, "save trace (from graph window)", + "load", CmdLoad, "load trace (to graph window", + "hisimlisten", CmdHisimlisten, "get HF samples as fake tag", + "hi14sim", CmdHi14sim, "fake ISO 14443 tag", + "hi14asim", CmdHi14asim, "fake ISO 14443a tag", // ## Simulate 14443a tag + "hi14snoop", CmdHi14snoop, "eavesdrop ISO 14443", + "hi14asnoop", CmdHi14asnoop, "eavesdrop ISO 14443 Type A", // ## New snoop command + "hi14list", CmdHi14list, "list ISO 14443 history", + "hi14alist", CmdHi14alist, "list ISO 14443a history", // ## New list command + "hiddemod", CmdHiddemod, "HID Prox Card II (not optimal)", + "hidfskdemod", CmdHIDdemodFSK, "HID FSK demodulator", + "askdemod", Cmdaskdemod, "Attempt to demodulate simple ASK tags", + "hidsimtag", CmdHIDsimTAG, "HID tag simulator", + "mandemod", Cmdmanchesterdemod, "Try a Manchester demodulation on a binary stream", + "fpgaoff", CmdFPGAOff, "set FPGA off", // ## FPGA Control + "lcdreset", CmdLcdReset, "Hardware reset LCD", + "lcd", CmdLcd, "Send command/data to LCD", + "test", CmdTest, "Placeholder command for testing new code", + "quit", CmdQuit, "quit program" +}; + +//----------------------------------------------------------------------------- +// Entry point into our code: called whenever the user types a command and +// then presses Enter, which the full command line that they typed. +//----------------------------------------------------------------------------- +void CommandReceived(char *cmd) +{ + int i; + + PrintToScrollback("> %s", cmd); + + if(strcmp(cmd, "help")==0) { + PrintToScrollback("\r\nAvailable commands:"); + for(i = 0; i < sizeof(CommandTable) / sizeof(CommandTable[0]); i++) { + char line[256]; + memset(line, ' ', sizeof(line)); + strcpy(line+2, CommandTable[i].name); + line[strlen(line)] = ' '; + sprintf(line+15, " -- %s", CommandTable[i].docString); + PrintToScrollback("%s", line); + } + PrintToScrollback(""); + PrintToScrollback("and also: help, cls"); + return; + } + + for(i = 0; i < sizeof(CommandTable) / sizeof(CommandTable[0]); i++) { + char *name = CommandTable[i].name; + if(memcmp(cmd, name, strlen(name))==0 && + (cmd[strlen(name)] == ' ' || cmd[strlen(name)] == '\0')) + { + cmd += strlen(name); + while(*cmd == ' ') { + cmd++; + } + (CommandTable[i].handler)(cmd); + return; + } + } + PrintToScrollback(">> bad command '%s'", cmd); +} + +//----------------------------------------------------------------------------- +// Entry point into our code: called whenever we received a packet over USB +// that we weren't necessarily expecting, for example a debug print. +//----------------------------------------------------------------------------- +void UsbCommandReceived(UsbCommand *c) +{ + switch(c->cmd) { + case CMD_DEBUG_PRINT_STRING: { + char s[100]; + if(c->ext1 > 70 || c->ext1 < 0) { + c->ext1 = 0; + } + memcpy(s, c->d.asBytes, c->ext1); + s[c->ext1] = '\0'; + PrintToScrollback("#db# %s", s); + break; + } + + case CMD_DEBUG_PRINT_INTEGERS: + PrintToScrollback("#db# %08x, %08x, %08x\r\n", c->ext1, c->ext2, c->ext3); + break; + + case CMD_MEASURED_ANTENNA_TUNING: { + int zLf, zHf; + int vLf125, vLf134, vHf; + vLf125 = c->ext1 & 0xffff; + vLf134 = c->ext1 >> 16; + vHf = c->ext2; + zLf = c->ext3 & 0xffff; + zHf = c->ext3 >> 16; + PrintToScrollback("# LF antenna @ %3d mA / %5d mV [%d ohms] 125Khz", + vLf125/zLf, vLf125, zLf); + PrintToScrollback("# LF antenna @ %3d mA / %5d mV [%d ohms] 134Khz", + vLf134/((zLf*125)/134), vLf134, (zLf*125)/134); + PrintToScrollback("# HF antenna @ %3d mA / %5d mV [%d ohms] 13.56Mhz", + vHf/zHf, vHf, zHf); + break; + } + default: + PrintToScrollback("unrecognized command %08x\n", c->cmd); + break; + } +} diff --git a/winsrc/gui.cpp b/winsrc/gui.cpp new file mode 100644 index 00000000..e4e530b3 --- /dev/null +++ b/winsrc/gui.cpp @@ -0,0 +1,510 @@ +//----------------------------------------------------------------------------- +// Routines for the user interface when doing interactive things with prox +// cards; this is basically a command line thing, in one window, and then +// another window to do the graphs. +// Jonathan Westhues, Sept 2005 +//----------------------------------------------------------------------------- +#include +#include +#include +#include +#include +#include + +#include "prox.h" + +#define oops() do { \ + char line[100]; \ + sprintf(line, "Internal error at line %d file '%s'", __LINE__, \ + __FILE__); \ + MessageBox(NULL, line, "Error", MB_ICONERROR); \ + exit(-1); \ +} while(0) + +void dbp(char *str, ...) +{ + va_list f; + char buf[1024]; + va_start(f, str); + vsprintf(buf, str, f); + OutputDebugString(buf); + OutputDebugString("\n"); +} + +int GraphBuffer[MAX_GRAPH_TRACE_LEN]; +int GraphTraceLen; + +HPEN GreyPen, GreenPen, WhitePen, YellowPen; +HBRUSH GreenBrush, YellowBrush; + +static int GraphStart = 0; +static double GraphPixelsPerPoint = 1; + +static int CursorAPos; +static int CursorBPos; +double CursorScaleFactor = 1.0; +static HPEN CursorAPen; +static HPEN CursorBPen; + +static HWND CommandWindow; +static HWND GraphWindow; +static HWND ScrollbackEdit; +static HWND CommandEdit; + +#define COMMAND_HISTORY_MAX 16 +static char CommandHistory[COMMAND_HISTORY_MAX][256]; +static int CommandHistoryPos = -1; +static int CommandHistoryNext; + +static HFONT MyFixedFont; +#define FixedFont(x) SendMessage((x), WM_SETFONT, (WPARAM)MyFixedFont, TRUE) + +void ExecCmd(char *cmd) +{ + +} +int CommandFinished; + +static void ResizeCommandWindow(void) +{ + int w, h; + RECT r; + GetClientRect(CommandWindow, &r); + w = r.right - r.left; + h = r.bottom - r.top; + MoveWindow(ScrollbackEdit, 10, 10, w - 20, h - 50, TRUE); + MoveWindow(CommandEdit, 10, h - 29, w - 20, 22, TRUE); +} + +void RepaintGraphWindow(void) +{ + InvalidateRect(GraphWindow, NULL, TRUE); +} + +static LRESULT CALLBACK + CommandWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_DESTROY: + case WM_QUIT: + exit(0); + return 0; + + case WM_SIZE: + ResizeCommandWindow(); + return 0; + + case WM_SETFOCUS: + SetFocus(CommandEdit); + break; + + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + + return 1; +} + +static void PaintGraph(HDC hdc) +{ + HBRUSH brush; + HPEN pen; + + brush = GreenBrush; + pen = GreenPen; + + if(GraphStart < 0) { + GraphStart = 0; + } + + RECT r; + GetClientRect(GraphWindow, &r); + + SelectObject(hdc, WhitePen); + + MoveToEx(hdc, r.left + 40, r.top, NULL); + LineTo(hdc, r.left + 40, r.bottom); + + int zeroHeight = r.top + (r.bottom - r.top) / 2; + SelectObject(hdc, GreyPen); + MoveToEx(hdc, r.left, zeroHeight, NULL); + LineTo(hdc, r.right, zeroHeight); + + int startMax = + (GraphTraceLen - (int)((r.right - r.left - 40) / GraphPixelsPerPoint)); + if(startMax < 0) { + startMax = 0; + } + if(GraphStart > startMax) { + GraphStart = startMax; + } + + int absYMax = 1; + + SelectObject(hdc, pen); + + int i; + for(i = GraphStart; ; i++) { + if(i >= GraphTraceLen) { + break; + } + if(fabs((double)GraphBuffer[i]) > absYMax) { + absYMax = (int)fabs((double)GraphBuffer[i]); + } + int x = 40 + (int)((i - GraphStart)*GraphPixelsPerPoint); + if(x > r.right) { + break; + } + } + + absYMax = (int)(absYMax*1.2 + 1); + SelectObject(hdc, MyFixedFont); + SetTextColor(hdc, RGB(255, 255, 255)); + SetBkColor(hdc, RGB(0, 0, 0)); + + // number of points that will be plotted + int span = (int)((r.right - r.left) / GraphPixelsPerPoint); + // one label every 100 pixels, let us say + int labels = (r.right - r.left - 40) / 100; + if(labels <= 0) labels = 1; + int pointsPerLabel = span / labels; + if(pointsPerLabel <= 0) pointsPerLabel = 1; + + int yMin = INT_MAX; + int yMax = INT_MIN; + int yMean = 0; + int n = 0; + + for(i = GraphStart; ; i++) { + if(i >= GraphTraceLen) { + break; + } + int x = 40 + (int)((i - GraphStart)*GraphPixelsPerPoint); + if(x > r.right + GraphPixelsPerPoint) { + break; + } + + int y = GraphBuffer[i]; + if(y < yMin) { + yMin = y; + } + if(y > yMax) { + yMax = y; + } + yMean += y; + n++; + + y = (y * (r.top - r.bottom) / (2*absYMax)) + zeroHeight; + if(i == GraphStart) { + MoveToEx(hdc, x, y, NULL); + } else { + LineTo(hdc, x, y); + } + + if(GraphPixelsPerPoint > 10) { + RECT f; + f.left = x - 3; + f.top = y - 3; + f.right = x + 3; + f.bottom = y + 3; + FillRect(hdc, &f, brush); + } + + if(((i - GraphStart) % pointsPerLabel == 0) && i != GraphStart) { + SelectObject(hdc, WhitePen); + MoveToEx(hdc, x, zeroHeight - 3, NULL); + LineTo(hdc, x, zeroHeight + 3); + + char str[100]; + sprintf(str, "+%d", (i - GraphStart)); + SIZE size; + GetTextExtentPoint32(hdc, str, strlen(str), &size); + TextOut(hdc, x - size.cx, zeroHeight + 8, str, strlen(str)); + + SelectObject(hdc, pen); + MoveToEx(hdc, x, y, NULL); + } + + if(i == CursorAPos || i == CursorBPos) { + if(i == CursorAPos) { + SelectObject(hdc, CursorAPen); + } else { + SelectObject(hdc, CursorBPen); + } + MoveToEx(hdc, x, r.top, NULL); + LineTo(hdc, x, r.bottom); + + SelectObject(hdc, pen); + MoveToEx(hdc, x, y, NULL); + } + } + + if(n != 0) { + yMean /= n; + } + + char str[100]; + sprintf(str, "@%d max=%d min=%d mean=%d n=%d/%d dt=%d [%.3f]", + GraphStart, yMax, yMin, yMean, n, GraphTraceLen, + CursorBPos - CursorAPos, (CursorBPos - CursorAPos)/CursorScaleFactor); + TextOut(hdc, 50, r.bottom - 20, str, strlen(str)); +} + +static LRESULT CALLBACK + GraphWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_DESTROY: + case WM_QUIT: + GraphWindow = NULL; + return DefWindowProc(hwnd, msg, wParam, lParam); + + case WM_SIZE: + RepaintGraphWindow(); + return 0; + + case WM_PAINT: { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + if(GraphStart < 0) { + GraphStart = 0; + } + // This draws the trace. + PaintGraph(hdc); + EndPaint(hwnd, &ps); + break; + } + case WM_KEYDOWN: + switch(wParam) { + case VK_DOWN: + if(GraphPixelsPerPoint <= 50) { + GraphPixelsPerPoint *= 2; + } + break; + + case VK_UP: + if(GraphPixelsPerPoint >= 0.02) { + GraphPixelsPerPoint /= 2; + } + break; + + case VK_RIGHT: + if(GraphPixelsPerPoint < 20) { + GraphStart += (int)(20 / GraphPixelsPerPoint); + } else { + GraphStart++; + } + break; + + case VK_LEFT: + if(GraphPixelsPerPoint < 20) { + GraphStart -= (int)(20 / GraphPixelsPerPoint); + } else { + GraphStart--; + } + break; + + default: + goto nopaint; + } + RepaintGraphWindow(); +nopaint: + break; + + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: { + int x = LOWORD(lParam); + x -= 40; + x = (int)(x / GraphPixelsPerPoint); + x += GraphStart; + if(msg == WM_LBUTTONDOWN) { + CursorAPos = x; + } else { + CursorBPos = x; + } + RepaintGraphWindow(); + break; + } + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + + return 1; +} + +void PrintToScrollback(char *fmt, ...) +{ + va_list f; + char str[1024]; + strcpy(str, "\r\n"); + va_start(f, fmt); + vsprintf(str+2, fmt, f); + + static char TextBuf[1024*32]; + SendMessage(ScrollbackEdit, WM_GETTEXT, (WPARAM)sizeof(TextBuf), + (LPARAM)TextBuf); + + if(strlen(TextBuf) + strlen(str) + 1 <= sizeof(TextBuf)) { + strcat(TextBuf, str); + } else { + lstrcpyn(TextBuf, str, sizeof(TextBuf)); + } + + SendMessage(ScrollbackEdit, WM_SETTEXT, 0, (LPARAM)TextBuf); + SendMessage(ScrollbackEdit, EM_LINESCROLL, 0, (LPARAM)INT_MAX); +} + +void ShowGraphWindow(void) +{ + if(GraphWindow) return; + + GraphWindow = CreateWindowEx(0, "Graph", "graphed", + WS_OVERLAPPED | WS_BORDER | WS_MINIMIZEBOX | WS_SYSMENU | + WS_SIZEBOX | WS_VISIBLE, 200, 150, 600, 500, NULL, NULL, NULL, + NULL); + if(!GraphWindow) oops(); +} + +void HideGraphWindow(void) +{ + if(GraphWindow) { + DestroyWindow(GraphWindow); + GraphWindow = NULL; + } +} + +static void SetCommandEditTo(char *str) +{ + SendMessage(CommandEdit, WM_SETTEXT, 0, (LPARAM)str); + SendMessage(CommandEdit, EM_SETSEL, strlen(str), strlen(str)); +} + +void ShowGui(void) +{ + WNDCLASSEX wc; + memset(&wc, 0, sizeof(wc)); + wc.cbSize = sizeof(wc); + + wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC; + wc.lpfnWndProc = (WNDPROC)CommandWindowProc; + wc.hInstance = NULL; + wc.hbrBackground = (HBRUSH)(COLOR_BTNSHADOW); + wc.lpszClassName = "Command"; + wc.lpszMenuName = NULL; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + + if(!RegisterClassEx(&wc)) oops(); + + wc.lpszClassName = "Graph"; + wc.lpfnWndProc = (WNDPROC)GraphWindowProc; + wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + + if(!RegisterClassEx(&wc)) oops(); + + CommandWindow = CreateWindowEx(0, "Command", "prox", + WS_OVERLAPPED | WS_BORDER | WS_MINIMIZEBOX | WS_SYSMENU | + WS_SIZEBOX | WS_VISIBLE, 20, 20, 500, 400, NULL, NULL, NULL, + NULL); + if(!CommandWindow) oops(); + + ScrollbackEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "edit", "", + WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | ES_MULTILINE | + ES_AUTOVSCROLL | WS_VSCROLL, 0, 0, 0, 0, CommandWindow, NULL, + NULL, NULL); + + CommandEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "edit", "", + WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | WS_VISIBLE | + ES_AUTOHSCROLL, 0, 0, 0, 0, CommandWindow, NULL, NULL, NULL); + + MyFixedFont = CreateFont(14, 0, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, + ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, + FF_DONTCARE, "Lucida Console"); + if(!MyFixedFont) + MyFixedFont = (HFONT)GetStockObject(SYSTEM_FONT); + + FixedFont(ScrollbackEdit); + FixedFont(CommandEdit); + + ResizeCommandWindow(); + SetFocus(CommandEdit); + + PrintToScrollback(">> Started prox, built " __DATE__ " " __TIME__); + PrintToScrollback(">> Connected to device"); + + GreyPen = CreatePen(PS_SOLID, 1, RGB(100, 100, 100)); + GreenPen = CreatePen(PS_SOLID, 1, RGB(100, 255, 100)); + YellowPen = CreatePen(PS_SOLID, 1, RGB(255, 255, 0)); + GreenBrush = CreateSolidBrush(RGB(100, 255, 100)); + YellowBrush = CreateSolidBrush(RGB(255, 255, 0)); + WhitePen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + + CursorAPen = CreatePen(PS_DASH, 1, RGB(255, 255, 0)); + CursorBPen = CreatePen(PS_DASH, 1, RGB(255, 0, 255)); + + MSG msg; + for(;;) { + if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if(msg.message == WM_KEYDOWN && msg.wParam == VK_RETURN) { + char got[1024]; + SendMessage(CommandEdit, WM_GETTEXT, (WPARAM)sizeof(got), + (LPARAM)got); + + if(strcmp(got, "cls")==0) { + SendMessage(ScrollbackEdit, WM_SETTEXT, 0, (LPARAM)""); + } else { + CommandReceived(got); + } + SendMessage(CommandEdit, WM_SETTEXT, 0, (LPARAM)""); + + // Insert it into the command history, unless it is + // identical to the previous command in the history. + int prev = CommandHistoryNext - 1; + if(prev < 0) prev += COMMAND_HISTORY_MAX; + if(strcmp(CommandHistory[prev], got) != 0) { + strcpy(CommandHistory[CommandHistoryNext], got); + CommandHistoryNext++; + if(CommandHistoryNext == COMMAND_HISTORY_MAX) { + CommandHistoryNext = 0; + } + } + CommandHistoryPos = -1; + } else if(msg.message == WM_KEYDOWN && msg.wParam == VK_UP && + msg.hwnd == CommandEdit) + { + if(CommandHistoryPos == -1) { + CommandHistoryPos = CommandHistoryNext; + } + CommandHistoryPos--; + if(CommandHistoryPos < 0) { + CommandHistoryPos = COMMAND_HISTORY_MAX-1; + } + SetCommandEditTo(CommandHistory[CommandHistoryPos]); + } else if(msg.message == WM_KEYDOWN && msg.wParam == VK_DOWN && + msg.hwnd == CommandEdit) + { + CommandHistoryPos++; + if(CommandHistoryPos >= COMMAND_HISTORY_MAX) { + CommandHistoryPos = 0; + } + SetCommandEditTo(CommandHistory[CommandHistoryPos]); + } else if(msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE && + msg.hwnd == CommandEdit) + { + SendMessage(CommandEdit, WM_SETTEXT, 0, (LPARAM)""); + } else { + if(msg.message == WM_KEYDOWN) { + CommandHistoryPos = -1; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + UsbCommand c; + if(ReceiveCommandPoll(&c)) { + UsbCommandReceived(&c); + } + + Sleep(10); + } +} diff --git a/winsrc/include/hidpi.h b/winsrc/include/hidpi.h new file mode 100644 index 00000000..e9816cf4 --- /dev/null +++ b/winsrc/include/hidpi.h @@ -0,0 +1,1787 @@ +/*++ + +Copyright (c) 1996-1998 Microsoft Corporation + +Module Name: + + HIDPI.H + +Abstract: + + Public Interface to the HID parsing library. + +Environment: + + Kernel & user mode + +--*/ + +#ifndef __HIDPI_H__ +#define __HIDPI_H__ + +#include + +// Please include "hidsdi.h" to use the user space (dll / parser) +// Please include "hidpddi.h" to use the kernel space parser + +// +// Special Link collection values for using the query functions +// +// Root collection references the collection at the base of the link +// collection tree. +// Unspecifies, references all collections in the link collection tree. +// +#define HIDP_LINK_COLLECTION_ROOT ((USHORT) -1) +#define HIDP_LINK_COLLECTION_UNSPECIFIED ((USHORT) 0) + + +typedef enum _HIDP_REPORT_TYPE +{ + HidP_Input, + HidP_Output, + HidP_Feature +} HIDP_REPORT_TYPE; + +typedef struct _USAGE_AND_PAGE +{ + USAGE Usage; + USAGE UsagePage; +} USAGE_AND_PAGE, *PUSAGE_AND_PAGE; + +#define HidP_IsSameUsageAndPage(u1, u2) ((* (PULONG) &u1) == (* (PULONG) &u2)) + +typedef struct _HIDP_BUTTON_CAPS +{ + USAGE UsagePage; + UCHAR ReportID; + BOOLEAN IsAlias; + + USHORT BitField; + USHORT LinkCollection; // A unique internal index pointer + + USAGE LinkUsage; + USAGE LinkUsagePage; + + BOOLEAN IsRange; + BOOLEAN IsStringRange; + BOOLEAN IsDesignatorRange; + BOOLEAN IsAbsolute; + + ULONG Reserved[10]; + union { + struct { + USAGE UsageMin, UsageMax; + USHORT StringMin, StringMax; + USHORT DesignatorMin, DesignatorMax; + USHORT DataIndexMin, DataIndexMax; + } Range; + struct { + USAGE Usage, Reserved1; + USHORT StringIndex, Reserved2; + USHORT DesignatorIndex, Reserved3; + USHORT DataIndex, Reserved4; + } NotRange; + }; + +} HIDP_BUTTON_CAPS, *PHIDP_BUTTON_CAPS; + + +typedef struct _HIDP_VALUE_CAPS +{ + USAGE UsagePage; + UCHAR ReportID; + BOOLEAN IsAlias; + + USHORT BitField; + USHORT LinkCollection; // A unique internal index pointer + + USAGE LinkUsage; + USAGE LinkUsagePage; + + BOOLEAN IsRange; + BOOLEAN IsStringRange; + BOOLEAN IsDesignatorRange; + BOOLEAN IsAbsolute; + + BOOLEAN HasNull; // Does this channel have a null report union + UCHAR Reserved; + USHORT BitSize; // How many bits are devoted to this value? + + USHORT ReportCount; // See Note below. Usually set to 1. + USHORT Reserved2[5]; + + ULONG UnitsExp; + ULONG Units; + + LONG LogicalMin, LogicalMax; + LONG PhysicalMin, PhysicalMax; + + union { + struct { + USAGE UsageMin, UsageMax; + USHORT StringMin, StringMax; + USHORT DesignatorMin, DesignatorMax; + USHORT DataIndexMin, DataIndexMax; + } Range; + + struct { + USAGE Usage, Reserved1; + USHORT StringIndex, Reserved2; + USHORT DesignatorIndex, Reserved3; + USHORT DataIndex, Reserved4; + } NotRange; + }; +} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS; + +// +// Notes: +// +// ReportCount: When a report descriptor declares an Input, Output, or +// Feature main item with fewer usage declarations than the report count, then +// the last usage applies to all remaining unspecified count in that main item. +// (As an example you might have data that required many fields to describe, +// possibly buffered bytes.) In this case, only one value cap structure is +// allocated for these associtated fields, all with the same usage, and Report +// Count reflects the number of fields involved. Normally ReportCount is 1. +// To access all of the fields in such a value structure would require using +// HidP_GetUsageValueArray and HidP_SetUsageValueArray. HidP_GetUsageValue/ +// HidP_SetScaledUsageValue will also work, however, these functions will only +// work with the first field of the structure. +// + +// +// The link collection tree consists of an array of LINK_COLLECTION_NODES +// where the index into this array is the same as the collection number. +// +// Given a collection A which contains a subcollection B, A is defined to be +// the parent B, and B is defined to be the child. +// +// Given collections A, B, and C where B and C are children of A, and B was +// encountered before C in the report descriptor, B is defined as a sibling of +// C. (This implies, of course, that if B is a sibling of C, then C is NOT a +// sibling of B). +// +// B is defined as the NextSibling of C if and only if there exists NO +// child collection of A, call it D, such that B is a sibling of D and D +// is a sibling of C. +// +// E is defined to be the FirstChild of A if and only if for all children of A, +// F, that are not equivalent to E, F is a sibling of E. +// (This implies, of course, that the does not exist a child of A, call it G, +// where E is a sibling of G). In other words the first sibling is the last +// link collection found in the list. +// +// In other words, if a collection B is defined within the definition of another +// collection A, B becomes a child of A. All collections with the same parent +// are considered siblings. The FirstChild of the parent collection, A, will be +// last collection defined that has A as a parent. The order of sibling pointers +// is similarly determined. When a collection B is defined, it becomes the +// FirstChild of it's parent collection. The previously defined FirstChild of the +// parent collection becomes the NextSibling of the new collection. As new +// collections with the same parent are discovered, the chain of sibling is built. +// +// With that in mind, the following describes conclusively a data structure +// that provides direct traversal up, down, and accross the link collection +// tree. +// +// +typedef struct _HIDP_LINK_COLLECTION_NODE +{ + USAGE LinkUsage; + USAGE LinkUsagePage; + USHORT Parent; + USHORT NumberOfChildren; + USHORT NextSibling; + USHORT FirstChild; + ULONG CollectionType: 8; // As defined in 6.2.2.6 of HID spec + ULONG IsAlias : 1; // This link node is an allias of the next link node. + ULONG Reserved: 23; + PVOID UserContext; // The user can hang his coat here. +} HIDP_LINK_COLLECTION_NODE, *PHIDP_LINK_COLLECTION_NODE; + +// +// When a link collection is described by a delimiter, alias link collection +// nodes are created. (One for each usage within the delimiter). +// The parser assigns each capability description listed above only one +// link collection. +// +// If a control is defined within a collection defined by +// delimited usages, then that control is said to be within multiple link +// collections, one for each usage within the open and close delimiter tokens. +// Such multiple link collecions are said to be aliases. The first N-1 such +// collections, listed in the link collection node array, have their IsAlias +// bit set. The last such link collection is the link collection index used +// in the capabilities described above. +// Clients wishing to set a control in an aliased collection, should walk the +// collection array once for each time they see the IsAlias flag set, and use +// the last link collection as the index for the below accessor functions. +// +// NB: if IsAlias is set, then NextSibling should be one more than the current +// link collection node index. +// + +typedef PUCHAR PHIDP_REPORT_DESCRIPTOR; +typedef struct _HIDP_PREPARSED_DATA * PHIDP_PREPARSED_DATA; + +typedef struct _HIDP_CAPS +{ + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + + USHORT NumberLinkCollectionNodes; + + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +} HIDP_CAPS, *PHIDP_CAPS; + +typedef struct _HIDP_DATA +{ + USHORT DataIndex; + USHORT Reserved; + union { + ULONG RawValue; // for values + BOOLEAN On; // for buttons MUST BE TRUE for buttons. + }; +} HIDP_DATA, *PHIDP_DATA; +// +// The HIDP_DATA structure is used with HidP_GetData and HidP_SetData +// functions. +// +// The parser contiguously assigns every control (button or value) in a hid +// device a unique data index from zero to NumberXXXDataIndices -1 , inclusive. +// This value is found in the HIDP_BUTTON_CAPS and HIDP_VALUE_CAPS structures. +// +// Most clients will find the Get/Set Buttons / Value accessor functions +// sufficient to their needs, as they will allow the clients to access the +// data known to them while ignoring the other controls. +// +// More complex clients, which actually read the Button / Value Caps, and which +// do a value add service to these routines (EG Direct Input), will need to +// access all the data in the device without interest in the individual usage +// or link collection location. These are the clients that will find +// HidP_Data useful. +// + +typedef struct _HIDP_UNKNOWN_TOKEN +{ + UCHAR Token; + UCHAR Reserved[3]; + ULONG BitField; +} HIDP_UNKNOWN_TOKEN, *PHIDP_UNKNOWN_TOKEN; + +typedef struct _HIDP_EXTENDED_ATTRIBUTES +{ + UCHAR NumGlobalUnknowns; + UCHAR Reserved [3]; + PHIDP_UNKNOWN_TOKEN GlobalUnknowns; + // ... Additional attributes + ULONG Data [1]; // variableLength DO NOT ACCESS THIS FIELD +} HIDP_EXTENDED_ATTRIBUTES, *PHIDP_EXTENDED_ATTRIBUTES; + +NTSTATUS __stdcall +HidP_GetCaps ( + IN PHIDP_PREPARSED_DATA PreparsedData, + OUT PHIDP_CAPS Capabilities + ); +/*++ +Routine Description: + Returns a list of capabilities of a given hid device as described by its + preparsed data. + +Arguments: + PreparsedData The preparsed data returned from HIDCLASS. + Capabilities a HIDP_CAPS structure + +Return Value: +· HIDP_STATUS_SUCCESS +· HIDP_STATUS_INVALID_PREPARSED_DATA +--*/ + +NTSTATUS __stdcall +HidP_GetLinkCollectionNodes ( + OUT PHIDP_LINK_COLLECTION_NODE LinkCollectionNodes, + IN OUT PULONG LinkCollectionNodesLength, + IN PHIDP_PREPARSED_DATA PreparsedData + ); +/*++ +Routine Description: + Return a list of PHIDP_LINK_COLLECTION_NODEs used to describe the link + collection tree of this hid device. See the above description of + struct _HIDP_LINK_COLLECTION_NODE. + +Arguments: + LinkCollectionNodes - a caller allocated array into which + HidP_GetLinkCollectionNodes will store the information + + LinKCollectionNodesLength - the caller sets this value to the length of the + the array in terms of number of elements. + HidP_GetLinkCollectionNodes sets this value to the actual + number of elements set. The total number of nodes required to + describe this HID device can be found in the + NumberLinkCollectionNodes field in the HIDP_CAPS structure. + +--*/ + +NTSTATUS __stdcall +HidP_GetButtonCaps ( + IN HIDP_REPORT_TYPE ReportType, + OUT PHIDP_BUTTON_CAPS ButtonCaps, + IN OUT PUSHORT ButtonCapsLength, + IN PHIDP_PREPARSED_DATA PreparsedData +); +#define HidP_GetButtonCaps(_Type_, _Caps_, _Len_, _Data_) \ + HidP_GetSpecificButtonCaps (_Type_, 0, 0, 0, _Caps_, _Len_, _Data_) +NTSTATUS __stdcall +HidP_GetSpecificButtonCaps ( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, // Optional (0 => ignore) + IN USHORT LinkCollection, // Optional (0 => ignore) + IN USAGE Usage, // Optional (0 => ignore) + OUT PHIDP_BUTTON_CAPS ButtonCaps, + IN OUT PUSHORT ButtonCapsLength, + IN PHIDP_PREPARSED_DATA PreparsedData + ); +/*++ +Description: + HidP_GetButtonCaps returns all the buttons (binary values) that are a part + of the given report type for the Hid device represented by the given + preparsed data. + +Parameters: + ReportType One of HidP_Input, HidP_Output, or HidP_Feature. + + UsagePage A usage page value used to limit the button caps returned to + those on a given usage page. If set to 0, this parameter is + ignored. Can be used with LinkCollection and Usage parameters + to further limit the number of button caps structures returned. + + LinkCollection HIDP_LINK_COLLECTION node array index used to limit the + button caps returned to those buttons in a given link + collection. If set to 0, this parameter is + ignored. Can be used with UsagePage and Usage parameters + to further limit the number of button caps structures + returned. + + Usage A usage value used to limit the button caps returned to those + with the specified usage value. If set to 0, this parameter + is ignored. Can be used with LinkCollection and UsagePage + parameters to further limit the number of button caps + structures returned. + + ButtonCaps A _HIDP_BUTTON_CAPS array containing information about all the + binary values in the given report. This buffer is provided by + the caller. + + ButtonLength As input, this parameter specifies the length of the + ButtonCaps parameter (array) in number of array elements. + As output, this value is set to indicate how many of those + array elements were filled in by the function. The maximum number of + button caps that can be returned is found in the HIDP_CAPS + structure. If HIDP_STATUS_BUFFER_TOO_SMALL is returned, + this value contains the number of array elements needed to + successfully complete the request. + + PreparsedData The preparsed data returned from HIDCLASS. + + +Return Value +HidP_GetSpecificButtonCaps returns the following error codes: +· HIDP_STATUS_SUCCESS. +· HIDP_STATUS_INVALID_REPORT_TYPE +· HIDP_STATUS_INVALID_PREPARSED_DATA +· HIDP_STATUS_BUFFER_TOO_SMALL (all given entries however have been filled in) +· HIDP_STATUS_USAGE_NOT_FOUND +--*/ + +NTSTATUS __stdcall +HidP_GetValueCaps ( + IN HIDP_REPORT_TYPE ReportType, + OUT PHIDP_VALUE_CAPS ValueCaps, + IN OUT PUSHORT ValueCapsLength, + IN PHIDP_PREPARSED_DATA PreparsedData +); +#define HidP_GetValueCaps(_Type_, _Caps_, _Len_, _Data_) \ + HidP_GetSpecificValueCaps (_Type_, 0, 0, 0, _Caps_, _Len_, _Data_) +NTSTATUS __stdcall +HidP_GetSpecificValueCaps ( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, // Optional (0 => ignore) + IN USHORT LinkCollection, // Optional (0 => ignore) + IN USAGE Usage, // Optional (0 => ignore) + OUT PHIDP_VALUE_CAPS ValueCaps, + IN OUT PUSHORT ValueCapsLength, + IN PHIDP_PREPARSED_DATA PreparsedData + ); +/*++ +Description: + HidP_GetValueCaps returns all the values (non-binary) that are a part + of the given report type for the Hid device represented by the given + preparsed data. + +Parameters: + ReportType One of HidP_Input, HidP_Output, or HidP_Feature. + + UsagePage A usage page value used to limit the value caps returned to + those on a given usage page. If set to 0, this parameter is + ignored. Can be used with LinkCollection and Usage parameters + to further limit the number of value caps structures returned. + + LinkCollection HIDP_LINK_COLLECTION node array index used to limit the + value caps returned to those buttons in a given link + collection. If set to 0, this parameter is + ignored. Can be used with UsagePage and Usage parameters + to further limit the number of value caps structures + returned. + + Usage A usage value used to limit the value caps returned to those + with the specified usage value. If set to 0, this parameter + is ignored. Can be used with LinkCollection and UsagePage + parameters to further limit the number of value caps + structures returned. + + ValueCaps A _HIDP_VALUE_CAPS array containing information about all the + non-binary values in the given report. This buffer is provided + by the caller. + + ValueLength As input, this parameter specifies the length of the ValueCaps + parameter (array) in number of array elements. As output, + this value is set to indicate how many of those array elements + were filled in by the function. The maximum number of + value caps that can be returned is found in the HIDP_CAPS + structure. If HIDP_STATUS_BUFFER_TOO_SMALL is returned, + this value contains the number of array elements needed to + successfully complete the request. + + PreparsedData The preparsed data returned from HIDCLASS. + + +Return Value +HidP_GetValueCaps returns the following error codes: +· HIDP_STATUS_SUCCESS. +· HIDP_STATUS_INVALID_REPORT_TYPE +· HIDP_STATUS_INVALID_PREPARSED_DATA +· HIDP_STATUS_BUFFER_TOO_SMALL (all given entries however have been filled in) +· HIDP_STATUS_USAGE_NOT_FOUND + +--*/ + +NTSTATUS __stdcall +HidP_GetExtendedAttributes ( + IN HIDP_REPORT_TYPE ReportType, + IN USHORT DataIndex, + IN PHIDP_PREPARSED_DATA PreparsedData, + OUT PHIDP_EXTENDED_ATTRIBUTES Attributes, + IN OUT PULONG LengthAttributes + ); +/*++ +Description: + Given a data index from the value or button capabilities of a given control + return any extended attributes for the control if any exist. + +Parameters: + ReportType One of HidP_Input, HidP_Output, or HidP_Feature. + + DataIndex The data index for the given control, found in the capabilities + structure for that control + + PreparsedData The preparsed data returned from HIDCLASS. + + Attributes Pointer to a buffer into which the extended attribute data will + be copied. + + LengthAttributes Length of the given buffer in bytes. + +Return Value + HIDP_STATUS_SUCCESS + HIDP_STATUS_DATA_INDEX_NOT_FOUND +--*/ + +NTSTATUS __stdcall +HidP_InitializeReportForID ( + IN HIDP_REPORT_TYPE ReportType, + IN UCHAR ReportID, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength + ); +/*++ + +Routine Description: + + Initialize a report based on the given report ID. + +Parameters: + + ReportType One of HidP_Input, HidP_Output, or HidP_Feature. + + PreparasedData Preparsed data structure returned by HIDCLASS + + Report Buffer which to set the data into. + + ReportLength Length of Report...Report should be at least as long as the + value indicated in the HIDP_CAPS structure for the device and + the corresponding ReportType + +Return Value + +· HIDP_STATUS_INVALID_REPORT_TYPE -- if ReportType is not valid. +· HIDP_STATUS_INVALID_PREPARSED_DATA -- if PreparsedData is not valid +· HIDP_STATUS_INVALID_REPORT_LENGTH -- the length of the report packet is not equal + to the length specified in HIDP_CAPS + structure for the given ReportType +· HIDP_STATUS_REPORT_DOES_NOT_EXIST -- if there are no reports on this device + for the given ReportType + +--*/ + +NTSTATUS __stdcall +HidP_SetData ( + IN HIDP_REPORT_TYPE ReportType, + IN PHIDP_DATA DataList, + IN OUT PULONG DataLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength + ); +/*++ + +Routine Description: + + Please Note: Since usage value arrays deal with multiple fields for + for one usage value, they cannot be used with HidP_SetData + and HidP_GetData. In this case, + HIDP_STATUS_IS_USAGE_VALUE_ARRAY will be returned. + +Parameters: + + ReportType One of HidP_Input, HidP_Output, or HidP_Feature. + + DataList Array of HIDP_DATA structures that contains the data values + that are to be set into the given report + + DataLength As input, length in array elements of DataList. As output, + contains the number of data elements set on successful + completion or an index into the DataList array to identify + the faulting HIDP_DATA value if an error code is returned. + + PreparasedData Preparsed data structure returned by HIDCLASS + + Report Buffer which to set the data into. + + ReportLength Length of Report...Report should be at least as long as the + value indicated in the HIDP_CAPS structure for the device and + the corresponding ReportType + +Return Value + HidP_SetData returns the following error codes. The report packet will + have all the data set up until the HIDP_DATA structure that caused the + error. DataLength, in the error case, will return this problem index. + +· HIDP_STATUS_SUCCESS -- upon successful insertion of all data + into the report packet. +· HIDP_STATUS_INVALID_REPORT_TYPE -- if ReportType is not valid. +· HIDP_STATUS_INVALID_PREPARSED_DATA -- if PreparsedData is not valid +· HIDP_STATUS_DATA_INDEX_NOT_FOUND -- if a HIDP_DATA structure referenced a + data index that does not exist for this + device's ReportType +· HIDP_STATUS_INVALID_REPORT_LENGTH -- the length of the report packet is not equal + to the length specified in HIDP_CAPS + structure for the given ReportType +· HIDP_STATUS_REPORT_DOES_NOT_EXIST -- if there are no reports on this device + for the given ReportType +· HIDP_STATUS_IS_USAGE_VALUE_ARRAY -- if one of the HIDP_DATA structures + references a usage value array. + DataLength will contain the index into + the array that was invalid +· HIDP_STATUS_BUTTON_NOT_PRESSED -- if a HIDP_DATA structure attempted + to unset a button that was not already + set in the Report +· HIDP_STATUS_INCOMPATIBLE_REPORT_ID -- a HIDP_DATA structure was found with + a valid index value but is contained + in a different report than the one + currently being processed +· HIDP_STATUS_BUFFER_TOO_SMALL -- if there are not enough entries in + a given Main Array Item to report all + buttons that have been requested to be + set +--*/ + +NTSTATUS __stdcall +HidP_GetData ( + IN HIDP_REPORT_TYPE ReportType, + OUT PHIDP_DATA DataList, + IN OUT PULONG DataLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength + ); +/*++ + +Routine Description: + + Please Note: For obvious reasons HidP_SetData and HidP_GetData will not + access UsageValueArrays. + +Parameters: + ReportType One of HidP_Input, HidP_Output, or HidP_Feature. + + DataList Array of HIDP_DATA structures that will receive the data + values that are set in the given report + + DataLength As input, length in array elements of DataList. As output, + contains the number of data elements that were successfully + set by HidP_GetData. The maximum size necessary for DataList + can be determined by calling HidP_MaxDataListLength + + PreparasedData Preparsed data structure returned by HIDCLASS + + Report Buffer which to set the data into. + + ReportLength Length of Report...Report should be at least as long as the + value indicated in the HIDP_CAPS structure for the device and + the corresponding ReportType + +Return Value + HidP_GetData returns the following error codes. + +· HIDP_STATUS_SUCCESS -- upon successful retrieval of all data + from the report packet. +· HIDP_STATUS_INVALID_REPORT_TYPE -- if ReportType is not valid. +· HIDP_STATUS_INVALID_PREPARSED_DATA -- if PreparsedData is not valid +· HIDP_STATUS_INVALID_REPORT_LENGTH -- the length of the report packet is not equal + to the length specified in HIDP_CAPS + structure for the given ReportType +· HIDP_STATUS_REPORT_DOES_NOT_EXIST -- if there are no reports on this device + for the given ReportType +· HIDP_STATUS_BUFFER_TOO_SMALL -- if there are not enough array entries in + DataList to store all the indice values + in the given report. DataLength will + contain the number of array entries + required to hold all data +--*/ + +ULONG __stdcall +HidP_MaxDataListLength ( + IN HIDP_REPORT_TYPE ReportType, + IN PHIDP_PREPARSED_DATA PreparsedData + ); +/*++ +Routine Description: + + This function returns the maximum length of HIDP_DATA elements that + HidP_GetData could return for the given report type. + +Parameters: + + ReportType One of HidP_Input, HidP_Output or HidP_Feature. + + PreparsedData Preparsed data structure returned by HIDCLASS + +Return Value: + + The length of the data list array required for the HidP_GetData function + call. If an error occurs (either HIDP_STATUS_INVALID_REPORT_TYPE or + HIDP_STATUS_INVALID_PREPARSED_DATA), this function returns 0. + +--*/ + +#define HidP_SetButtons(Rty, Up, Lco, ULi, ULe, Ppd, Rep, Rle) \ + HidP_SetUsages(Rty, Up, Lco, ULi, ULe, Ppd, Rep, Rle) + +NTSTATUS __stdcall +HidP_SetUsages ( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, // Optional + IN PUSAGE UsageList, + IN OUT PULONG UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength + ); +/*++ + +Routine Description: + This function sets binary values (buttons) in a report. Given an + initialized packet of correct length, it modifies the report packet so that + each element in the given list of usages has been set in the report packet. + For example, in an output report with 5 LED’s, each with a given usage, + an application could turn on any subset of these lights by placing their + usages in any order into the usage array (UsageList). HidP_SetUsages would, + in turn, set the appropriate bit or add the corresponding byte into the + HID Main Array Item. + + A properly initialized Report packet is one of the correct byte length, + and all zeros. + + NOTE: A packet that has already been set with a call to a HidP_Set routine + can also be passed in. This routine then sets processes the UsageList + in the same fashion but verifies that the ReportID already set in + Report matches the report ID for the given usages. + +Parameters: + ReportType One of HidP_Input, HidP_Output or HidP_Feature. + + UsagePage All of the usages in the usage array, which HidP_SetUsages will + set in the report, refer to this same usage page. + If a client wishes to set usages in a report for multiple + usage pages then that client needs to make multiple calls to + HidP_SetUsages for each of the usage pages. + + UsageList A usage array containing the usages that HidP_SetUsages will set in + the report packet. + + UsageLength The length of the given usage array in array elements. + The parser will set this value to the position in the usage + array where it stopped processing. If successful, UsageLength + will be unchanged. In any error condition, this parameter + reflects how many of the usages in the usage list have + actually been set by the parser. This is useful for finding + the usage in the list which caused the error. + + PreparsedData The preparsed data recevied from HIDCLASS + + Report The report packet. + + ReportLength Length of the given report packet...Must be equal to the + value reported in the HIDP_CAPS structure for the device + and corresponding report type. + +Return Value + HidP_SetUsages returns the following error codes. On error, the report packet + will be correct up until the usage element that caused the error. + +· HIDP_STATUS_SUCCESS -- upon successful insertion of all usages + into the report packet. +· HIDP_STATUS_INVALID_REPORT_TYPE -- if ReportType is not valid. +· HIDP_STATUS_INVALID_PREPARSED_DATA -- if PreparsedData is not valid +· HIDP_STATUS_INVALID_REPORT_LENGTH -- the length of the report packet is not + equal to the length specified in + the HIDP_CAPS structure for the given + ReportType +· HIDP_STATUS_REPORT_DOES_NOT_EXIST -- if there are no reports on this device + for the given ReportType +· HIDP_STATUS_INCOMPATIBLE_REPORT_ID -- if a usage was found that exists in a + different report. If the report is + zero-initialized on entry the first + usage in the list will determine which + report ID is used. Otherwise, the + parser will verify that usage matches + the passed in report's ID +· HIDP_STATUS_USAGE_NOT_FOUND -- if the usage does not exist for any + report (no matter what the report ID) + for the given report type. +· HIDP_STATUS_BUFFER_TOO_SMALL -- if there are not enough entries in a + given Main Array Item to list all of + the given usages. The caller needs + to split his request into more than + one call +--*/ + +#define HidP_UnsetButtons(Rty, Up, Lco, ULi, ULe, Ppd, Rep, Rle) \ + HidP_UnsetUsages(Rty, Up, Lco, ULi, ULe, Ppd, Rep, Rle) + +NTSTATUS __stdcall +HidP_UnsetUsages ( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, // Optional + IN PUSAGE UsageList, + IN OUT PULONG UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength + ); +/*++ + +Routine Description: + This function unsets (turns off) binary values (buttons) in the report. Given + an initialized packet of correct length, it modifies the report packet so + that each element in the given list of usages has been unset in the + report packet. + + This function is the "undo" operation for SetUsages. If the given usage + is not already set in the Report, it will return an error code of + HIDP_STATUS_BUTTON_NOT_PRESSED. If the button is pressed, HidP_UnsetUsages + will unset the appropriate bit or remove the corresponding index value from + the HID Main Array Item. + + A properly initialized Report packet is one of the correct byte length, + and all zeros.. + + NOTE: A packet that has already been set with a call to a HidP_Set routine + can also be passed in. This routine then processes the UsageList + in the same fashion but verifies that the ReportID already set in + Report matches the report ID for the given usages. + +Parameters: + ReportType One of HidP_Input, HidP_Output or HidP_Feature. + + UsagePage All of the usages in the usage array, which HidP_UnsetUsages will + unset in the report, refer to this same usage page. + If a client wishes to unset usages in a report for multiple + usage pages then that client needs to make multiple calls to + HidP_UnsetUsages for each of the usage pages. + + UsageList A usage array containing the usages that HidP_UnsetUsages will + unset in the report packet. + + UsageLength The length of the given usage array in array elements. + The parser will set this value to the position in the usage + array where it stopped processing. If successful, UsageLength + will be unchanged. In any error condition, this parameter + reflects how many of the usages in the usage list have + actually been unset by the parser. This is useful for finding + the usage in the list which caused the error. + + PreparsedData The preparsed data recevied from HIDCLASS + + Report The report packet. + + ReportLength Length of the given report packet...Must be equal to the + value reported in the HIDP_CAPS structure for the device + and corresponding report type. + +Return Value + HidP_UnsetUsages returns the following error codes. On error, the report + packet will be correct up until the usage element that caused the error. + +· HIDP_STATUS_SUCCESS -- upon successful "unsetting" of all usages + in the report packet. +· HIDP_STATUS_INVALID_REPORT_TYPE -- if ReportType is not valid. +· HIDP_STATUS_INVALID_PREPARSED_DATA -- if PreparsedData is not valid +· HIDP_STATUS_INVALID_REPORT_LENGTH -- the length of the report packet is not + equal to the length specified in + the HIDP_CAPS structure for the given + ReportType +· HIDP_STATUS_REPORT_DOES_NOT_EXIST -- if there are no reports on this device + for the given ReportType +· HIDP_STATUS_INCOMPATIBLE_REPORT_ID -- if a usage was found that exists in a + different report. If the report is + zero-initialized on entry the first + usage in the list will determine which + report ID is used. Otherwise, the + parser will verify that usage matches + the passed in report's ID +· HIDP_STATUS_USAGE_NOT_FOUND -- if the usage does not exist for any + report (no matter what the report ID) + for the given report type. +· HIDP_STATUS_BUTTON_NOT_PRESSED -- if a usage corresponds to a button that + is not already set in the given report +--*/ + +#define HidP_GetButtons(Rty, UPa, LCo, ULi, ULe, Ppd, Rep, RLe) \ + HidP_GetUsages(Rty, UPa, LCo, ULi, ULe, Ppd, Rep, RLe) + +NTSTATUS __stdcall +HidP_GetUsages ( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, // Optional + OUT USAGE * UsageList, + IN OUT ULONG * UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength + ); +/*++ + +Routine Description: + This function returns the binary values (buttons) that are set in a HID + report. Given a report packet of correct length, it searches the report + packet for each usage for the given usage page and returns them in the + usage list. + +Parameters: + ReportType One of HidP_Input, HidP_Output or HidP_Feature. + + UsagePage All of the usages in the usage list, which HidP_GetUsages will + retrieve in the report, refer to this same usage page. + If the client wishes to get usages in a packet for multiple + usage pages then that client needs to make multiple calls + to HidP_GetUsages. + + LinkCollection An optional value which can limit which usages are returned + in the UsageList to those usages that exist in a specific + LinkCollection. A non-zero value indicates the index into + the HIDP_LINK_COLLECITON_NODE list returned by + HidP_GetLinkCollectionNodes of the link collection the + usage should belong to. A value of 0 indicates this + should value be ignored. + + UsageList The usage array that will contain all the usages found in + the report packet. + + UsageLength The length of the given usage array in array elements. + On input, this value describes the length of the usage list. + On output, HidP_GetUsages sets this value to the number of + usages that was found. Use HidP_MaxUsageListLength to + determine the maximum length needed to return all the usages + that a given report packet may contain. + + PreparsedData Preparsed data structure returned by HIDCLASS + + Report The report packet. + + ReportLength Length (in bytes) of the given report packet + + +Return Value + HidP_GetUsages returns the following error codes: + +· HIDP_STATUS_SUCCESS -- upon successfully retrieving all the + usages from the report packet +· HIDP_STATUS_INVALID_REPORT_TYPE -- if ReportType is not valid. +· HIDP_STATUS_INVALID_PREPARSED_DATA -- if PreparsedData is not valid +· HIDP_STATUS_INVALID_REPORT_LENGTH -- the length of the report packet is not + equal to the length specified in + the HIDP_CAPS structure for the given + ReportType +· HIDP_STATUS_REPORT_DOES_NOT_EXIST -- if there are no reports on this device + for the given ReportType +· HIDP_STATUS_BUFFER_TOO_SMALL -- if the UsageList is not big enough to + hold all the usages found in the report + packet. If this is returned, the buffer + will contain UsageLength number of + usages. Use HidP_MaxUsageListLength to + find the maximum length needed +· HIDP_STATUS_INCOMPATIBLE_REPORT_ID -- if no usages were found but usages + that match the UsagePage and + LinkCollection specified could be found + in a report with a different report ID +· HIDP_STATUS_USAGE_NOT_FOUND -- if there are no usages in a reports for + the device and ReportType that match the + UsagePage and LinkCollection that were + specified +--*/ + +#define HidP_GetButtonsEx(Rty, LCo, BLi, ULe, Ppd, Rep, RLe) \ + HidP_GetUsagesEx(Rty, LCo, BLi, ULe, Ppd, Rep, RLe) + +NTSTATUS __stdcall +HidP_GetUsagesEx ( + IN HIDP_REPORT_TYPE ReportType, + IN USHORT LinkCollection, // Optional + OUT PUSAGE_AND_PAGE ButtonList, + IN OUT ULONG * UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength + ); + +/*++ + +Routine Description: + This function returns the binary values (buttons) in a HID report. + Given a report packet of correct length, it searches the report packet + for all buttons and returns the UsagePage and Usage for each of the buttons + it finds. + +Parameters: + ReportType One of HidP_Input, HidP_Output or HidP_Feature. + + LinkCollection An optional value which can limit which usages are returned + in the ButtonList to those usages that exist in a specific + LinkCollection. A non-zero value indicates the index into + the HIDP_LINK_COLLECITON_NODE list returned by + HidP_GetLinkCollectionNodes of the link collection the + usage should belong to. A value of 0 indicates this + should value be ignored. + + ButtonList An array of USAGE_AND_PAGE structures describing all the + buttons currently ``down'' in the device. + + UsageLength The length of the given array in terms of elements. + On input, this value describes the length of the list. On + output, HidP_GetUsagesEx sets this value to the number of + usages that were found. Use HidP_MaxUsageListLength to + determine the maximum length needed to return all the usages + that a given report packet may contain. + + PreparsedData Preparsed data returned by HIDCLASS + + Report The report packet. + + ReportLength Length (in bytes) of the given report packet. + + +Return Value + HidP_GetUsagesEx returns the following error codes: + +· HIDP_STATUS_SUCCESS -- upon successfully retrieving all the + usages from the report packet +· HIDP_STATUS_INVALID_REPORT_TYPE -- if ReportType is not valid. +· HIDP_STATUS_INVALID_PREPARSED_DATA -- if PreparsedData is not valid +· HIDP_STATUS_INVALID_REPORT_LENGTH -- the length of the report packet is not + equal to the length specified in + the HIDP_CAPS structure for the given + ReportType +· HIDP_STATUS_REPORT_DOES_NOT_EXIST -- if there are no reports on this device + for the given ReportType +· HIDP_STATUS_BUFFER_TOO_SMALL -- if ButtonList is not big enough to + hold all the usages found in the report + packet. If this is returned, the buffer + will contain UsageLength number of + usages. Use HidP_MaxUsageListLength to + find the maximum length needed +· HIDP_STATUS_INCOMPATIBLE_REPORT_ID -- if no usages were found but usages + that match the specified LinkCollection + exist in report with a different report + ID. +· HIDP_STATUS_USAGE_NOT_FOUND -- if there are no usages in any reports that + match the LinkCollection parameter +--*/ + +#define HidP_GetButtonListLength(RTy, UPa, Ppd) \ + HidP_GetUsageListLength(Rty, UPa, Ppd) + +ULONG __stdcall +HidP_MaxUsageListLength ( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, // Optional + IN PHIDP_PREPARSED_DATA PreparsedData + ); +/*++ +Routine Description: + This function returns the maximum number of usages that a call to + HidP_GetUsages or HidP_GetUsagesEx could return for a given HID report. + If calling for number of usages returned by HidP_GetUsagesEx, use 0 as + the UsagePage value. + +Parameters: + ReportType One of HidP_Input, HidP_Output or HidP_Feature. + + UsagePage Specifies the optional UsagePage to query for. If 0, will + return all the maximum number of usage values that could be + returned for a given ReportType. If non-zero, will return + the maximum number of usages that would be returned for the + ReportType with the given UsagePage. + + PreparsedData Preparsed data returned from HIDCLASS + +Return Value: + The length of the usage list array required for the HidP_GetUsages or + HidP_GetUsagesEx function call. If an error occurs (such as + HIDP_STATUS_INVALID_REPORT_TYPE or HIDP_INVALID_PREPARSED_DATA, this + returns 0. +--*/ + +NTSTATUS __stdcall +HidP_SetUsageValue ( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, // Optional + IN USAGE Usage, + IN ULONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength + ); +/*++ +Description: + HidP_SetUsageValue inserts a value into the HID Report Packet in the field + corresponding to the given usage page and usage. HidP_SetUsageValue + casts this value to the appropriate bit length. If a report packet + contains two different fields with the same Usage and UsagePage, + they can be distinguished with the optional LinkCollection field value. + Using this function sets the raw value into the report packet with + no checking done as to whether it actually falls within the logical + minimum/logical maximum range. Use HidP_SetScaledUsageValue for this... + + NOTE: Although the UsageValue parameter is a ULONG, any casting that is + done will preserve or sign-extend the value. The value being set + should be considered a LONG value and will be treated as such by + this function. + +Parameters: + + ReportType One of HidP_Output or HidP_Feature. + + UsagePage The usage page to which the given usage refers. + + LinkCollection (Optional) This value can be used to differentiate + between two fields that may have the same + UsagePage and Usage but exist in different + collections. If the link collection value + is zero, this function will set the first field + it finds that matches the usage page and + usage. + + Usage The usage whose value HidP_SetUsageValue will set. + + UsageValue The raw value to set in the report buffer. This value must be within + the logical range or if a NULL value this value should be the + most negative value that can be represented by the number of bits + for this field. + + PreparsedData The preparsed data returned for HIDCLASS + + Report The report packet. + + ReportLength Length (in bytes) of the given report packet. + + +Return Value: + HidP_SetUsageValue returns the following error codes: + +· HIDP_STATUS_SUCCESS -- upon successfully setting the value + in the report packet +· HIDP_STATUS_INVALID_REPORT_TYPE -- if ReportType is not valid. +· HIDP_STATUS_INVALID_PREPARSED_DATA -- if PreparsedData is not valid +· HIDP_STATUS_INVALID_REPORT_LENGTH -- the length of the report packet is not + equal to the length specified in + the HIDP_CAPS structure for the given + ReportType +· HIDP_STATUS_REPORT_DOES_NOT_EXIST -- if there are no reports on this device + for the given ReportType +· HIDP_STATUS_INCOMPATIBLE_REPORT_ID -- the specified usage page, usage and + link collection exist but exists in + a report with a different report ID + than the report being passed in. To + set this value, call HidP_SetUsageValue + again with a zero-initizialed report + packet +· HIDP_STATUS_USAGE_NOT_FOUND -- if the usage page, usage, and link + collection combination does not exist + in any reports for this ReportType +--*/ + +NTSTATUS __stdcall +HidP_SetScaledUsageValue ( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, // Optional + IN USAGE Usage, + IN LONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength + ); + +/*++ +Description: + HidP_SetScaledUsageValue inserts the UsageValue into the HID report packet + in the field corresponding to the given usage page and usage. If a report + packet contains two different fields with the same Usage and UsagePage, + they can be distinguished with the optional LinkCollection field value. + + If the specified field has a defined physical range, this function converts + the physical value specified to the corresponding logical value for the + report. If a physical value does not exist, the function will verify that + the value specified falls within the logical range and set according. + + If the range checking fails but the field has NULL values, the function will + set the field to the defined NULL value (most negative number possible) and + return HIDP_STATUS_NULL. In other words, use this function to set NULL + values for a given field by passing in a value that falls outside the + physical range if it is defined or the logical range otherwise. + + If the field does not support NULL values, an out of range error will be + returned instead. + +Parameters: + + ReportType One of HidP_Output or HidP_Feature. + + UsagePage The usage page to which the given usage refers. + + LinkCollection (Optional) This value can be used to differentiate + between two fields that may have the same + UsagePage and Usage but exist in different + collections. If the link collection value + is zero, this function will set the first field + it finds that matches the usage page and + usage. + + Usage The usage whose value HidP_SetScaledUsageValue will set. + + UsageValue The value to set in the report buffer. See the routine + description above for the different interpretations of this + value + + PreparsedData The preparsed data returned from HIDCLASS + + Report The report packet. + + ReportLength Length (in bytes) of the given report packet. + + +Return Value: + HidP_SetScaledUsageValue returns the following error codes: + +· HIDP_STATUS_SUCCESS -- upon successfully setting the value + in the report packet +· HIDP_STATUS_NULL -- upon successfully setting the value + in the report packet as a NULL value +· HIDP_STATUS_INVALID_REPORT_TYPE -- if ReportType is not valid. +· HIDP_STATUS_INVALID_PREPARSED_DATA -- if PreparsedData is not valid +· HIDP_STATUS_INVALID_REPORT_LENGTH -- the length of the report packet is not + equal to the length specified in + the HIDP_CAPS structure for the given + ReportType +· HIDP_STATUS_VALUE_OUT_OF_RANGE -- if the value specified failed to fall + within the physical range if it exists + or within the logical range otherwise + and the field specified by the usage + does not allow NULL values +· HIDP_STATUS_BAD_LOG_PHY_VALUES -- if the field has a physical range but + either the logical range is invalid + (max <= min) or the physical range is + invalid +· HIDP_STATUS_INCOMPATIBLE_REPORT_ID -- the specified usage page, usage and + link collection exist but exists in + a report with a different report ID + than the report being passed in. To + set this value, call + HidP_SetScaledUsageValue again with + a zero-initialized report packet +· HIDP_STATUS_USAGE_NOT_FOUND -- if the usage page, usage, and link + collection combination does not exist + in any reports for this ReportType +--*/ + +NTSTATUS __stdcall +HidP_SetUsageValueArray ( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, // Optional + IN USAGE Usage, + IN PCHAR UsageValue, + IN USHORT UsageValueByteLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + OUT PCHAR Report, + IN ULONG ReportLength + ); + +/*++ +Routine Descripton: + A usage value array occurs when the last usage in the list of usages + describing a main item must be repeated because there are less usages defined + than there are report counts declared for the given main item. In this case + a single value cap is allocated for that usage and the report count of that + value cap is set to reflect the number of fields to which that usage refers. + + HidP_SetUsageValueArray sets the raw bits for that usage which spans + more than one field in a report. + + NOTE: This function currently does not support value arrays where the + ReportSize for each of the fields in the array is not a multiple + of 8 bits. + + The UsageValue buffer should have the values set as they would appear + in the report buffer. If this function supported non 8-bit multiples + for the ReportSize then caller should format the input buffer so that + each new value begins at the bit immediately following the last bit + of the previous value + +Parameters: + + ReportType One of HidP_Output or HidP_Feature. + + UsagePage The usage page to which the given usage refers. + + LinkCollection (Optional) This value can be used to differentiate + between two fields that may have the same + UsagePage and Usage but exist in different + collections. If the link collection value + is zero, this function will set the first field + it finds that matches the usage page and + usage. + + Usage The usage whose value array HidP_SetUsageValueArray will set. + + UsageValue The buffer with the values to set into the value array. + The number of BITS required is found by multiplying the + BitSize and ReportCount fields of the Value Cap for this + control. The least significant bit of this control found in the + given report will be placed in the least significan bit location + of the array given (little-endian format), regardless of whether + or not the field is byte alligned or if the BitSize is a multiple + of sizeof (CHAR). + + See the above note for current implementation limitations. + + UsageValueByteLength Length of the UsageValue buffer (in bytes) + + PreparsedData The preparsed data returned from HIDCLASS + + Report The report packet. + + ReportLength Length (in bytes) of the given report packet. + + +Return Value: +· HIDP_STATUS_SUCCESS -- upon successfully setting the value + array in the report packet +· HIDP_STATUS_INVALID_REPORT_TYPE -- if ReportType is not valid. +· HIDP_STATUS_INVALID_PREPARSED_DATA -- if PreparsedData is not valid +· HIDP_STATUS_INVALID_REPORT_LENGTH -- the length of the report packet is not + equal to the length specified in + the HIDP_CAPS structure for the given + ReportType +· HIDP_STATUS_REPORT_DOES_NOT_EXIST -- if there are no reports on this device + for the given ReportType +· HIDP_STATUS_NOT_VALUE_ARRAY -- if the control specified is not a + value array -- a value array will have + a ReportCount field in the + HIDP_VALUE_CAPS structure that is > 1 + Use HidP_SetUsageValue instead +· HIDP_STATUS_BUFFER_TOO_SMALL -- if the size of the passed in buffer with + the values to set is too small (ie. has + fewer values than the number of fields in + the array +· HIDP_STATUS_NOT_IMPLEMENTED -- if the usage value array has field sizes + that are not multiples of 8 bits, this + error code is returned since the function + currently does not handle setting into + such arrays. +· HIDP_STATUS_INCOMPATIBLE_REPORT_ID -- the specified usage page, usage and + link collection exist but exists in + a report with a different report ID + than the report being passed in. To + set this value, call + HidP_SetUsageValueArray again with + a zero-initialized report packet +· HIDP_STATUS_USAGE_NOT_FOUND -- if the usage page, usage, and link + collection combination does not exist + in any reports for this ReportType +--*/ + + +NTSTATUS __stdcall +HidP_GetUsageValue ( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, // Optional + IN USAGE Usage, + OUT PULONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength + ); + +/* +Description + HidP_GetUsageValue retrieves the value from the HID Report for the usage + specified by the combination of usage page, usage and link collection. + If a report packet contains two different fields with the same + Usage and UsagePage, they can be distinguished with the optional + LinkCollection field value. + +Parameters: + + ReportType One of HidP_Input or HidP_Feature. + + UsagePage The usage page to which the given usage refers. + + LinkCollection (Optional) This value can be used to differentiate + between two fields that may have the same + UsagePage and Usage but exist in different + collections. If the link collection value + is zero, this function will set the first field + it finds that matches the usage page and + usage. + + Usage The usage whose value HidP_GetUsageValue will retrieve + + UsageValue The raw value that is set for the specified field in the report + buffer. This value will either fall within the logical range + or if NULL values are allowed, a number outside the range to + indicate a NULL + + PreparsedData The preparsed data returned for HIDCLASS + + Report The report packet. + + ReportLength Length (in bytes) of the given report packet. + + +Return Value: + HidP_GetUsageValue returns the following error codes: + +· HIDP_STATUS_SUCCESS -- upon successfully retrieving the value + from the report packet +· HIDP_STATUS_INVALID_REPORT_TYPE -- if ReportType is not valid. +· HIDP_STATUS_INVALID_PREPARSED_DATA -- if PreparsedData is not valid +· HIDP_STATUS_INVALID_REPORT_LENGTH -- the length of the report packet is not + equal to the length specified in + the HIDP_CAPS structure for the given + ReportType +· HIDP_STATUS_REPORT_DOES_NOT_EXIST -- if there are no reports on this device + for the given ReportType +· HIDP_STATUS_INCOMPATIBLE_REPORT_ID -- the specified usage page, usage and + link collection exist but exists in + a report with a different report ID + than the report being passed in. To + set this value, call HidP_GetUsageValue + again with a different report packet +· HIDP_STATUS_USAGE_NOT_FOUND -- if the usage page, usage, and link + collection combination does not exist + in any reports for this ReportType +--*/ + + +NTSTATUS __stdcall +HidP_GetScaledUsageValue ( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, // Optional + IN USAGE Usage, + OUT PLONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength + ); + +/*++ +Description + HidP_GetScaledUsageValue retrieves a UsageValue from the HID report packet + in the field corresponding to the given usage page and usage. If a report + packet contains two different fields with the same Usage and UsagePage, + they can be distinguished with the optional LinkCollection field value. + + If the specified field has a defined physical range, this function converts + the logical value that exists in the report packet to the corresponding + physical value. If a physical range does not exist, the function will + return the logical value. This function will check to verify that the + logical value in the report falls within the declared logical range. + + When doing the conversion between logical and physical values, this + function assumes a linear extrapolation between the physical max/min and + the logical max/min. (Where logical is the values reported by the device + and physical is the value returned by this function). If the data field + size is less than 32 bits, then HidP_GetScaledUsageValue will sign extend + the value to 32 bits. + + If the range checking fails but the field has NULL values, the function + will set UsageValue to 0 and return HIDP_STATUS_NULL. Otherwise, it + returns a HIDP_STATUS_OUT_OF_RANGE error. + +Parameters: + + ReportType One of HidP_Output or HidP_Feature. + + UsagePage The usage page to which the given usage refers. + + LinkCollection (Optional) This value can be used to differentiate + between two fields that may have the same + UsagePage and Usage but exist in different + collections. If the link collection value + is zero, this function will retrieve the first + field it finds that matches the usage page + and usage. + + Usage The usage whose value HidP_GetScaledUsageValue will retrieve + + UsageValue The value retrieved from the report buffer. See the routine + description above for the different interpretations of this + value + + PreparsedData The preparsed data returned from HIDCLASS + + Report The report packet. + + ReportLength Length (in bytes) of the given report packet. + + +Return Value: + HidP_GetScaledUsageValue returns the following error codes: + +· HIDP_STATUS_SUCCESS -- upon successfully retrieving the value + from the report packet +· HIDP_STATUS_NULL -- if the report packet had a NULL value + set +· HIDP_STATUS_INVALID_REPORT_TYPE -- if ReportType is not valid. +· HIDP_STATUS_INVALID_PREPARSED_DATA -- if PreparsedData is not valid +· HIDP_STATUS_INVALID_REPORT_LENGTH -- the length of the report packet is not + equal to the length specified in + the HIDP_CAPS structure for the given + ReportType +· HIDP_STATUS_VALUE_OUT_OF_RANGE -- if the value retrieved from the packet + falls outside the logical range and + the field does not support NULL values +· HIDP_STATUS_BAD_LOG_PHY_VALUES -- if the field has a physical range but + either the logical range is invalid + (max <= min) or the physical range is + invalid +· HIDP_STATUS_INCOMPATIBLE_REPORT_ID -- the specified usage page, usage and + link collection exist but exists in + a report with a different report ID + than the report being passed in. To + set this value, call + HidP_GetScaledUsageValue with a + different report packet +· HIDP_STATUS_USAGE_NOT_FOUND -- if the usage page, usage, and link + collection combination does not exist + in any reports for this ReportType +--*/ + +NTSTATUS __stdcall +HidP_GetUsageValueArray ( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, // Optional + IN USAGE Usage, + OUT PCHAR UsageValue, + IN USHORT UsageValueByteLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength + ); + +/*++ +Routine Descripton: + A usage value array occurs when the last usage in the list of usages + describing a main item must be repeated because there are less usages defined + than there are report counts declared for the given main item. In this case + a single value cap is allocated for that usage and the report count of that + value cap is set to reflect the number of fields to which that usage refers. + + HidP_GetUsageValueArray returns the raw bits for that usage which spans + more than one field in a report. + + NOTE: This function currently does not support value arrays where the + ReportSize for each of the fields in the array is not a multiple + of 8 bits. + + The UsageValue buffer will have the raw values as they are set + in the report packet. + +Parameters: + + ReportType One of HidP_Input, HidP_Output or HidP_Feature. + + UsagePage The usage page to which the given usage refers. + + LinkCollection (Optional) This value can be used to differentiate + between two fields that may have the same + UsagePage and Usage but exist in different + collections. If the link collection value + is zero, this function will set the first field + it finds that matches the usage page and + usage. + + Usage The usage whose value HidP_GetUsageValueArray will retreive. + + UsageValue A pointer to an array of characters where the value will be + placed. The number of BITS required is found by multiplying the + BitSize and ReportCount fields of the Value Cap for this + control. The least significant bit of this control found in the + given report will be placed in the least significant bit location + of the buffer (little-endian format), regardless of whether + or not the field is byte aligned or if the BitSize is a multiple + of sizeof (CHAR). + + See note above about current implementation limitations + + UsageValueByteLength + the length of the given UsageValue buffer. + + PreparsedData The preparsed data returned by the HIDCLASS + + Report The report packet. + + ReportLength Length of the given report packet. + +Return Value: + +· HIDP_STATUS_SUCCESS -- upon successfully retrieving the value + from the report packet +· HIDP_STATUS_INVALID_REPORT_TYPE -- if ReportType is not valid. +· HIDP_STATUS_INVALID_PREPARSED_DATA -- if PreparsedData is not valid +· HIDP_STATUS_INVALID_REPORT_LENGTH -- the length of the report packet is not + equal to the length specified in + the HIDP_CAPS structure for the given + ReportType +· HIDP_STATUS_NOT_VALUE_ARRAY -- if the control specified is not a + value array -- a value array will have + a ReportCount field in the + HIDP_VALUE_CAPS structure that is > 1 + Use HidP_GetUsageValue instead +· HIDP_STATUS_BUFFER_TOO_SMALL -- if the size of the passed in buffer in + which to return the array is too small + (ie. has fewer values than the number of + fields in the array +· HIDP_STATUS_NOT_IMPLEMENTED -- if the usage value array has field sizes + that are not multiples of 8 bits, this + error code is returned since the function + currently does not handle getting values + from such arrays. +· HIDP_STATUS_INCOMPATIBLE_REPORT_ID -- the specified usage page, usage and + link collection exist but exists in + a report with a different report ID + than the report being passed in. To + set this value, call + HidP_GetUsageValueArray with a + different report packet +· HIDP_STATUS_USAGE_NOT_FOUND -- if the usage page, usage, and link + collection combination does not exist + in any reports for this ReportType +--*/ + +NTSTATUS __stdcall +HidP_UsageListDifference ( + IN PUSAGE PreviousUsageList, + IN PUSAGE CurrentUsageList, + OUT PUSAGE BreakUsageList, + OUT PUSAGE MakeUsageList, + IN ULONG UsageListLength + ); +/*++ +Routine Description: + This function will return the difference between a two lists of usages + (as might be returned from HidP_GetUsages), In other words, it will return + return a list of usages that are in the current list but not the previous + list as well as a list of usages that are in the previous list but not + the current list. + +Parameters: + + PreviousUsageList The list of usages before. + CurrentUsageList The list of usages now. + BreakUsageList Previous - Current. + MakeUsageList Current - Previous. + UsageListLength Represents the length of the usage lists in array + elements. If comparing two lists with a differing + number of array elements, this value should be + the size of the larger of the two lists. Any + zero found with a list indicates an early termination + of the list and any usages found after the first zero + will be ignored. +--*/ + +NTSTATUS __stdcall +HidP_UsageAndPageListDifference ( + IN PUSAGE_AND_PAGE PreviousUsageList, + IN PUSAGE_AND_PAGE CurrentUsageList, + OUT PUSAGE_AND_PAGE BreakUsageList, + OUT PUSAGE_AND_PAGE MakeUsageList, + IN ULONG UsageListLength + ); + +// +// Produce Make or Break Codes +// +typedef enum _HIDP_KEYBOARD_DIRECTION { + HidP_Keyboard_Break, + HidP_Keyboard_Make +} HIDP_KEYBOARD_DIRECTION; + +// +// A bitmap of the current shift state of the keyboard when using the +// below keyboard usages to i8042 translation function. +// +typedef struct _HIDP_KEYBOARD_MODIFIER_STATE { + union { + struct { + ULONG LeftControl: 1; + ULONG LeftShift: 1; + ULONG LeftAlt: 1; + ULONG LeftGUI: 1; + ULONG RightControl: 1; + ULONG RightShift: 1; + ULONG RightAlt: 1; + ULONG RigthGUI: 1; + ULONG CapsLock: 1; + ULONG ScollLock: 1; + ULONG NumLock: 1; + ULONG Reserved: 21; + }; + ULONG ul; + }; + +} HIDP_KEYBOARD_MODIFIER_STATE, * PHIDP_KEYBOARD_MODIFIER_STATE; + +// +// A call back function to give the i8042 scan codes to the caller of +// the below translation function. +// +typedef BOOLEAN (* PHIDP_INSERT_SCANCODES) ( + IN PVOID Context, // Some caller supplied context. + IN PCHAR NewScanCodes, // A list of i8042 scan codes. + IN ULONG Length // the length of the scan codes. + ); + +NTSTATUS __stdcall +HidP_TranslateUsageAndPagesToI8042ScanCodes ( + IN PUSAGE_AND_PAGE ChangedUsageList, + IN ULONG UsageListLength, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext + ); +/*++ +Routine Description: +Parameters: +--*/ + +NTSTATUS __stdcall +HidP_TranslateUsagesToI8042ScanCodes ( + IN PUSAGE ChangedUsageList, + IN ULONG UsageListLength, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext + ); +/*++ +Routine Description: +Parameters: +--*/ + + + +// +// Define NT Status codes with Facility Code of FACILITY_HID_ERROR_CODE +// + +// FACILITY_HID_ERROR_CODE defined in ntstatus.h +#ifndef FACILITY_HID_ERROR_CODE +#define FACILITY_HID_ERROR_CODE 0x11 +#endif + +#define HIDP_ERROR_CODES(SEV, CODE) \ + ((NTSTATUS) (((SEV) << 28) | (FACILITY_HID_ERROR_CODE << 16) | (CODE))) + +#define HIDP_STATUS_SUCCESS (HIDP_ERROR_CODES(0x0,0)) +#define HIDP_STATUS_NULL (HIDP_ERROR_CODES(0x8,1)) +#define HIDP_STATUS_INVALID_PREPARSED_DATA (HIDP_ERROR_CODES(0xC,1)) +#define HIDP_STATUS_INVALID_REPORT_TYPE (HIDP_ERROR_CODES(0xC,2)) +#define HIDP_STATUS_INVALID_REPORT_LENGTH (HIDP_ERROR_CODES(0xC,3)) +#define HIDP_STATUS_USAGE_NOT_FOUND (HIDP_ERROR_CODES(0xC,4)) +#define HIDP_STATUS_VALUE_OUT_OF_RANGE (HIDP_ERROR_CODES(0xC,5)) +#define HIDP_STATUS_BAD_LOG_PHY_VALUES (HIDP_ERROR_CODES(0xC,6)) +#define HIDP_STATUS_BUFFER_TOO_SMALL (HIDP_ERROR_CODES(0xC,7)) +#define HIDP_STATUS_INTERNAL_ERROR (HIDP_ERROR_CODES(0xC,8)) +#define HIDP_STATUS_I8042_TRANS_UNKNOWN (HIDP_ERROR_CODES(0xC,9)) +#define HIDP_STATUS_INCOMPATIBLE_REPORT_ID (HIDP_ERROR_CODES(0xC,0xA)) +#define HIDP_STATUS_NOT_VALUE_ARRAY (HIDP_ERROR_CODES(0xC,0xB)) +#define HIDP_STATUS_IS_VALUE_ARRAY (HIDP_ERROR_CODES(0xC,0xC)) +#define HIDP_STATUS_DATA_INDEX_NOT_FOUND (HIDP_ERROR_CODES(0xC,0xD)) +#define HIDP_STATUS_DATA_INDEX_OUT_OF_RANGE (HIDP_ERROR_CODES(0xC,0xE)) +#define HIDP_STATUS_BUTTON_NOT_PRESSED (HIDP_ERROR_CODES(0xC,0xF)) +#define HIDP_STATUS_REPORT_DOES_NOT_EXIST (HIDP_ERROR_CODES(0xC,0x10)) +#define HIDP_STATUS_NOT_IMPLEMENTED (HIDP_ERROR_CODES(0xC,0x20)) + +// +// We blundered this status code. +// +#define HIDP_STATUS_I8242_TRANS_UNKNOWN HIDP_STATUS_I8042_TRANS_UNKNOWN + +#include + +#endif diff --git a/winsrc/include/hidsdi.h b/winsrc/include/hidsdi.h new file mode 100644 index 00000000..d0db806b --- /dev/null +++ b/winsrc/include/hidsdi.h @@ -0,0 +1,412 @@ +/*++ + +Copyright (c) 1996 Microsoft Corporation + +Module Name: + + HIDSDI.H + +Abstract: + + This module contains the PUBLIC definitions for the + code that implements the HID dll. + +Environment: + + Kernel & user mode + +--*/ + + +#ifndef _HIDSDI_H +#define _HIDSDI_H + +#include + +//#include "wtypes.h" + +//#include +//#include +//#include + +typedef LONG NTSTATUS; +#include "hidusage.h" +#include "hidpi.h" + +typedef struct _HIDD_CONFIGURATION { + PVOID cookie; + ULONG size; + ULONG RingBufferSize; +} HIDD_CONFIGURATION, *PHIDD_CONFIGURATION; + +typedef struct _HIDD_ATTRIBUTES { + ULONG Size; // = sizeof (struct _HIDD_ATTRIBUTES) + + // + // Vendor ids of this hid device + // + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; + + // + // Additional fields will be added to the end of this structure. + // +} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + + +BOOLEAN __stdcall +HidD_GetAttributes ( + IN HANDLE HidDeviceObject, + OUT PHIDD_ATTRIBUTES Attributes + ); +/*++ +Routine Description: + Fill in the given HIDD_ATTRIBUTES structure with the attributes of the + given hid device. + +--*/ + + +void __stdcall +HidD_GetHidGuid ( + OUT LPGUID HidGuid + ); + +BOOLEAN __stdcall +HidD_GetPreparsedData ( + IN HANDLE HidDeviceObject, + OUT PHIDP_PREPARSED_DATA * PreparsedData + ); +/*++ +Routine Description: + Given a handle to a valid Hid Class Device Object, retrieve the preparsed + data for the device. This routine will allocate the appropriately + sized buffer to hold this preparsed data. It is up to client to call + HidP_FreePreparsedData to free the memory allocated to this structure when + it is no longer needed. + +Arguments: + HidDeviceObject A handle to a Hid Device that the client obtains using + a call to CreateFile on a valid Hid device string name. + The string name can be obtained using standard PnP calls. + + PreparsedData An opaque data structure used by other functions in this + library to retrieve information about a given device. + +Return Value: + TRUE if successful. + FALSE otherwise -- Use GetLastError() to get extended error information +--*/ + +BOOLEAN __stdcall +HidD_FreePreparsedData ( + IN PHIDP_PREPARSED_DATA PreparsedData + ); + +BOOLEAN __stdcall +HidD_FlushQueue ( + IN HANDLE HidDeviceObject + ); +/*++ +Routine Description: + Flush the input queue for the given HID device. + +Arguments: + HidDeviceObject A handle to a Hid Device that the client obtains using + a call to CreateFile on a valid Hid device string name. + The string name can be obtained using standard PnP calls. + +Return Value: + TRUE if successful + FALSE otherwise -- Use GetLastError() to get extended error information +--*/ + +BOOLEAN __stdcall +HidD_GetConfiguration ( + IN HANDLE HidDeviceObject, + OUT PHIDD_CONFIGURATION Configuration, + IN ULONG ConfigurationLength + ); +/*++ +Routine Description: + Get the configuration information for this Hid device + +Arguments: + HidDeviceObject A handle to a Hid Device Object. + + Configuration A configuration structure. HidD_GetConfiguration MUST + be called before the configuration can be modified and + set using HidD_SetConfiguration + + ConfigurationLength That is ``sizeof (HIDD_CONFIGURATION)''. Using this + parameter, we can later increase the length of the + configuration array and not break older apps. + +Return Value: + TRUE if successful + FALSE otherwise -- Use GetLastError() to get extended error information +--*/ + +BOOLEAN __stdcall +HidD_SetConfiguration ( + IN HANDLE HidDeviceObject, + IN PHIDD_CONFIGURATION Configuration, + IN ULONG ConfigurationLength + ); +/*++ +Routine Description: + Set the configuration information for this Hid device... + + NOTE: HidD_GetConfiguration must be called to retrieve the current + configuration information before this information can be modified + and set. + +Arguments: + HidDeviceObject A handle to a Hid Device Object. + + Configuration A configuration structure. HidD_GetConfiguration MUST + be called before the configuration can be modified and + set using HidD_SetConfiguration + + ConfigurationLength That is ``sizeof (HIDD_CONFIGURATION)''. Using this + parameter, we can later increase the length of the + configuration array and not break older apps. + +Return Value: + TRUE if successful + FALSE otherwise -- Use GetLastError() to get extended error information +--*/ + +BOOLEAN __stdcall +HidD_GetFeature ( + IN HANDLE HidDeviceObject, + OUT PVOID ReportBuffer, + IN ULONG ReportBufferLength + ); +/*++ +Routine Description: + Retrieve a feature report from a HID device. + +Arguments: + HidDeviceObject A handle to a Hid Device Object. + + ReportBuffer The buffer that the feature report should be placed + into. The first byte of the buffer should be set to + the report ID of the desired report + + ReportBufferLength The size (in bytes) of ReportBuffer. This value + should be greater than or equal to the + FeatureReportByteLength field as specified in the + HIDP_CAPS structure for the device +Return Value: + TRUE if successful + FALSE otherwise -- Use GetLastError() to get extended error information +--*/ + +BOOLEAN __stdcall +HidD_SetFeature ( + IN HANDLE HidDeviceObject, + IN PVOID ReportBuffer, + IN ULONG ReportBufferLength + ); +/*++ +Routine Description: + Send a feature report to a HID device. + +Arguments: + HidDeviceObject A handle to a Hid Device Object. + + ReportBuffer The buffer of the feature report to send to the device + + ReportBufferLength The size (in bytes) of ReportBuffer. This value + should be greater than or equal to the + FeatureReportByteLength field as specified in the + HIDP_CAPS structure for the device +Return Value: + TRUE if successful + FALSE otherwise -- Use GetLastError() to get extended error information +--*/ + +BOOLEAN __stdcall +HidD_GetNumInputBuffers ( + IN HANDLE HidDeviceObject, + OUT PULONG NumberBuffers + ); +/*++ +Routine Description: + This function returns the number of input buffers used by the specified + file handle to the Hid device. Each file object has a number of buffers + associated with it to queue reports read from the device but which have + not yet been read by the user-mode app with a handle to that device. + +Arguments: + HidDeviceObject A handle to a Hid Device Object. + + NumberBuffers Number of buffers currently being used for this file + handle to the Hid device + +Return Value: + TRUE if successful + FALSE otherwise -- Use GetLastError() to get extended error information +--*/ + +BOOLEAN __stdcall +HidD_SetNumInputBuffers ( + IN HANDLE HidDeviceObject, + OUT ULONG NumberBuffers + ); +/*++ + +Routine Description: + This function sets the number of input buffers used by the specified + file handle to the Hid device. Each file object has a number of buffers + associated with it to queue reports read from the device but which have + not yet been read by the user-mode app with a handle to that device. + +Arguments: + HidDeviceObject A handle to a Hid Device Object. + + NumberBuffers New number of buffers to use for this file handle to + the Hid device + +Return Value: + TRUE if successful + FALSE otherwise -- Use GetLastError() to get extended error information +--*/ + +BOOLEAN __stdcall +HidD_GetPhysicalDescriptor ( + IN HANDLE HidDeviceObject, + OUT PVOID Buffer, + IN ULONG BufferLength + ); +/*++ +Routine Description: + This function retrieves the raw physical descriptor for the specified + Hid device. + +Arguments: + HidDeviceObject A handle to a Hid Device Object. + + Buffer Buffer which on return will contain the physical + descriptor if one exists for the specified device + handle + + BufferLength Length of buffer (in bytes) + + +Return Value: + TRUE if successful + FALSE otherwise -- Use GetLastError() to get extended error information +--*/ + +BOOLEAN __stdcall +HidD_GetManufacturerString ( + IN HANDLE HidDeviceObject, + OUT PVOID Buffer, + IN ULONG BufferLength + ); +/*++ +Routine Description: + This function retrieves the manufacturer string from the specified + Hid device. + +Arguments: + HidDeviceObject A handle to a Hid Device Object. + + Buffer Buffer which on return will contain the manufacturer + string returned from the device. This string is a + wide-character string + + BufferLength Length of Buffer (in bytes) + + +Return Value: + TRUE if successful + FALSE otherwise -- Use GetLastError() to get extended error information +--*/ + +BOOLEAN __stdcall +HidD_GetProductString ( + IN HANDLE HidDeviceObject, + OUT PVOID Buffer, + IN ULONG BufferLength + ); +/*++ +Routine Description: + This function retrieves the product string from the specified + Hid device. + +Arguments: + HidDeviceObject A handle to a Hid Device Object. + + Buffer Buffer which on return will contain the product + string returned from the device. This string is a + wide-character string + + BufferLength Length of Buffer (in bytes) + + +Return Value: + TRUE if successful + FALSE otherwise -- Use GetLastError() to get extended error information +--*/ + +BOOLEAN __stdcall +HidD_GetIndexedString ( + IN HANDLE HidDeviceObject, + IN ULONG StringIndex, + OUT PVOID Buffer, + IN ULONG BufferLength + ); +/*++ +Routine Description: + This function retrieves a string from the specified Hid device that is + specified with a certain string index. + +Arguments: + HidDeviceObject A handle to a Hid Device Object. + + StringIndex Index of the string to retrieve + + Buffer Buffer which on return will contain the product + string returned from the device. This string is a + wide-character string + + BufferLength Length of Buffer (in bytes) + +Return Value: + TRUE if successful + FALSE otherwise -- Use GetLastError() to get extended error information +--*/ + +BOOLEAN __stdcall +HidD_GetSerialNumberString ( + IN HANDLE HidDeviceObject, + OUT PVOID Buffer, + IN ULONG BufferLength + ); +/*++ +Routine Description: + This function retrieves the serial number string from the specified + Hid device. + +Arguments: + HidDeviceObject A handle to a Hid Device Object. + + Buffer Buffer which on return will contain the serial number + string returned from the device. This string is a + wide-character string + + BufferLength Length of Buffer (in bytes) + +Return Value: + TRUE if successful + FALSE otherwise -- Use GetLastError() to get extended error information +--*/ + + +#include + +#endif diff --git a/winsrc/include/hidusage.h b/winsrc/include/hidusage.h new file mode 100644 index 00000000..4ea9dd12 --- /dev/null +++ b/winsrc/include/hidusage.h @@ -0,0 +1,263 @@ +/*++ + +Copyright (c) 1996, 1997 Microsoft Corporation + +Module Name: + + HIDUSAGE.H + +Abstract: + + Public Definitions of HID USAGES. + +Environment: + + Kernel & user mode + +--*/ + +#ifndef __HIDUSAGE_H__ +#define __HIDUSAGE_H__ + +// +// Usage Pages +// + +typedef USHORT USAGE, *PUSAGE; + +#define HID_USAGE_PAGE_GENERIC ((USAGE) 0x01) +#define HID_USAGE_PAGE_SIMULATION ((USAGE) 0x02) +#define HID_USAGE_PAGE_VR ((USAGE) 0x03) +#define HID_USAGE_PAGE_SPORT ((USAGE) 0x04) +#define HID_USAGE_PAGE_GAME ((USAGE) 0x05) +#define HID_USAGE_PAGE_KEYBOARD ((USAGE) 0x07) +#define HID_USAGE_PAGE_LED ((USAGE) 0x08) +#define HID_USAGE_PAGE_BUTTON ((USAGE) 0x09) +#define HID_USAGE_PAGE_ORDINAL ((USAGE) 0x0A) +#define HID_USAGE_PAGE_TELEPHONY ((USAGE) 0x0B) +#define HID_USAGE_PAGE_CONSUMER ((USAGE) 0x0C) +#define HID_USAGE_PAGE_DIGITIZER ((USAGE) 0x0D) +#define HID_USAGE_PAGE_UNICODE ((USAGE) 0x10) +#define HID_USAGE_PAGE_ALPHANUMERIC ((USAGE) 0x14) + + +// +// Usages from Generic Desktop Page (0x01) +// + +#define HID_USAGE_GENERIC_POINTER ((USAGE) 0x01) +#define HID_USAGE_GENERIC_MOUSE ((USAGE) 0x02) +#define HID_USAGE_GENERIC_JOYSTICK ((USAGE) 0x04) +#define HID_USAGE_GENERIC_GAMEPAD ((USAGE) 0x05) +#define HID_USAGE_GENERIC_KEYBOARD ((USAGE) 0x06) +#define HID_USAGE_GENERIC_KEYPAD ((USAGE) 0x07) +#define HID_USAGE_GENERIC_SYSTEM_CTL ((USAGE) 0x80) + +#define HID_USAGE_GENERIC_X ((USAGE) 0x30) +#define HID_USAGE_GENERIC_Y ((USAGE) 0x31) +#define HID_USAGE_GENERIC_Z ((USAGE) 0x32) +#define HID_USAGE_GENERIC_RX ((USAGE) 0x33) +#define HID_USAGE_GENERIC_RY ((USAGE) 0x34) +#define HID_USAGE_GENERIC_RZ ((USAGE) 0x35) +#define HID_USAGE_GENERIC_SLIDER ((USAGE) 0x36) +#define HID_USAGE_GENERIC_DIAL ((USAGE) 0x37) +#define HID_USAGE_GENERIC_WHEEL ((USAGE) 0x38) +#define HID_USAGE_GENERIC_HATSWITCH ((USAGE) 0x39) +#define HID_USAGE_GENERIC_COUNTED_BUFFER ((USAGE) 0x3A) +#define HID_USAGE_GENERIC_BYTE_COUNT ((USAGE) 0x3B) +#define HID_USAGE_GENERIC_MOTION_WAKEUP ((USAGE) 0x3C) +#define HID_USAGE_GENERIC_VX ((USAGE) 0x40) +#define HID_USAGE_GENERIC_VY ((USAGE) 0x41) +#define HID_USAGE_GENERIC_VZ ((USAGE) 0x42) +#define HID_USAGE_GENERIC_VBRX ((USAGE) 0x43) +#define HID_USAGE_GENERIC_VBRY ((USAGE) 0x44) +#define HID_USAGE_GENERIC_VBRZ ((USAGE) 0x45) +#define HID_USAGE_GENERIC_VNO ((USAGE) 0x46) +#define HID_USAGE_GENERIC_SYSCTL_POWER ((USAGE) 0x81) +#define HID_USAGE_GENERIC_SYSCTL_SLEEP ((USAGE) 0x82) +#define HID_USAGE_GENERIC_SYSCTL_WAKE ((USAGE) 0x83) +#define HID_USAGE_GENERIC_SYSCTL_CONTEXT_MENU ((USAGE) 0x84) +#define HID_USAGE_GENERIC_SYSCTL_MAIN_MENU ((USAGE) 0x85) +#define HID_USAGE_GENERIC_SYSCTL_APP_MENU ((USAGE) 0x86) +#define HID_USAGE_GENERIC_SYSCTL_HELP_MENU ((USAGE) 0x87) +#define HID_USAGE_GENERIC_SYSCTL_MENU_EXIT ((USAGE) 0x88) +#define HID_USAGE_GENERIC_SYSCTL_MENU_SELECT ((USAGE) 0x89) +#define HID_USAGE_GENERIC_SYSCTL_MENU_RIGHT ((USAGE) 0x8A) +#define HID_USAGE_GENERIC_SYSCTL_MENU_LEFT ((USAGE) 0x8B) +#define HID_USAGE_GENERIC_SYSCTL_MENU_UP ((USAGE) 0x8C) +#define HID_USAGE_GENERIC_SYSCTL_MENU_DOWN ((USAGE) 0x8D) + +// +// Usages from Simulation Controls Page (0x02) +// + +#define HID_USAGE_SIMULATION_RUDDER ((USAGE) 0xBA) +#define HID_USAGE_SIMULATION_THROTTLE ((USAGE) 0xBB) + +// +// Virtual Reality Controls Page (0x03) +// + + +// +// Sport Controls Page (0x04) +// + + +// +// Game Controls Page (0x05) +// + + +// +// Keyboard/Keypad Page (0x07) +// + + // Error "keys" +#define HID_USAGE_KEYBOARD_NOEVENT ((USAGE) 0x00) +#define HID_USAGE_KEYBOARD_ROLLOVER ((USAGE) 0x01) +#define HID_USAGE_KEYBOARD_POSTFAIL ((USAGE) 0x02) +#define HID_USAGE_KEYBOARD_UNDEFINED ((USAGE) 0x03) + + // Letters +#define HID_USAGE_KEYBOARD_aA ((USAGE) 0x04) +#define HID_USAGE_KEYBOARD_zZ ((USAGE) 0x1D) + // Numbers +#define HID_USAGE_KEYBOARD_ONE ((USAGE) 0x1E) +#define HID_USAGE_KEYBOARD_ZERO ((USAGE) 0x27) + // Modifier Keys +#define HID_USAGE_KEYBOARD_LCTRL ((USAGE) 0xE0) +#define HID_USAGE_KEYBOARD_LSHFT ((USAGE) 0xE1) +#define HID_USAGE_KEYBOARD_LALT ((USAGE) 0xE2) +#define HID_USAGE_KEYBOARD_LGUI ((USAGE) 0xE3) +#define HID_USAGE_KEYBOARD_RCTRL ((USAGE) 0xE4) +#define HID_USAGE_KEYBOARD_RSHFT ((USAGE) 0xE5) +#define HID_USAGE_KEYBOARD_RALT ((USAGE) 0xE6) +#define HID_USAGE_KEYBOARD_RGUI ((USAGE) 0xE7) +#define HID_USAGE_KEYBOARD_SCROLL_LOCK ((USAGE) 0x47) +#define HID_USAGE_KEYBOARD_NUM_LOCK ((USAGE) 0x53) +#define HID_USAGE_KEYBOARD_CAPS_LOCK ((USAGE) 0x39) + // Funtion keys +#define HID_USAGE_KEYBOARD_F1 ((USAGE) 0x3A) +#define HID_USAGE_KEYBOARD_F12 ((USAGE) 0x45) + +#define HID_USAGE_KEYBOARD_RETURN ((USAGE) 0x28) +#define HID_USAGE_KEYBOARD_ESCAPE ((USAGE) 0x29) +#define HID_USAGE_KEYBOARD_DELETE ((USAGE) 0x2A) + +#define HID_USAGE_KEYBOARD_PRINT_SCREEN ((USAGE) 0x46) + +// and hundreds more... + +// +// LED Page (0x08) +// + +#define HID_USAGE_LED_NUM_LOCK ((USAGE) 0x01) +#define HID_USAGE_LED_CAPS_LOCK ((USAGE) 0x02) +#define HID_USAGE_LED_SCROLL_LOCK ((USAGE) 0x03) +#define HID_USAGE_LED_COMPOSE ((USAGE) 0x04) +#define HID_USAGE_LED_KANA ((USAGE) 0x05) +#define HID_USAGE_LED_POWER ((USAGE) 0x06) +#define HID_USAGE_LED_SHIFT ((USAGE) 0x07) +#define HID_USAGE_LED_DO_NOT_DISTURB ((USAGE) 0x08) +#define HID_USAGE_LED_MUTE ((USAGE) 0x09) +#define HID_USAGE_LED_TONE_ENABLE ((USAGE) 0x0A) +#define HID_USAGE_LED_HIGH_CUT_FILTER ((USAGE) 0x0B) +#define HID_USAGE_LED_LOW_CUT_FILTER ((USAGE) 0x0C) +#define HID_USAGE_LED_EQUALIZER_ENABLE ((USAGE) 0x0D) +#define HID_USAGE_LED_SOUND_FIELD_ON ((USAGE) 0x0E) +#define HID_USAGE_LED_SURROUND_FIELD_ON ((USAGE) 0x0F) +#define HID_USAGE_LED_REPEAT ((USAGE) 0x10) +#define HID_USAGE_LED_STEREO ((USAGE) 0x11) +#define HID_USAGE_LED_SAMPLING_RATE_DETECT ((USAGE) 0x12) +#define HID_USAGE_LED_SPINNING ((USAGE) 0x13) +#define HID_USAGE_LED_CAV ((USAGE) 0x14) +#define HID_USAGE_LED_CLV ((USAGE) 0x15) +#define HID_USAGE_LED_RECORDING_FORMAT_DET ((USAGE) 0x16) +#define HID_USAGE_LED_OFF_HOOK ((USAGE) 0x17) +#define HID_USAGE_LED_RING ((USAGE) 0x18) +#define HID_USAGE_LED_MESSAGE_WAITING ((USAGE) 0x19) +#define HID_USAGE_LED_DATA_MODE ((USAGE) 0x1A) +#define HID_USAGE_LED_BATTERY_OPERATION ((USAGE) 0x1B) +#define HID_USAGE_LED_BATTERY_OK ((USAGE) 0x1C) +#define HID_USAGE_LED_BATTERY_LOW ((USAGE) 0x1D) +#define HID_USAGE_LED_SPEAKER ((USAGE) 0x1E) +#define HID_USAGE_LED_HEAD_SET ((USAGE) 0x1F) +#define HID_USAGE_LED_HOLD ((USAGE) 0x20) +#define HID_USAGE_LED_MICROPHONE ((USAGE) 0x21) +#define HID_USAGE_LED_COVERAGE ((USAGE) 0x22) +#define HID_USAGE_LED_NIGHT_MODE ((USAGE) 0x23) +#define HID_USAGE_LED_SEND_CALLS ((USAGE) 0x24) +#define HID_USAGE_LED_CALL_PICKUP ((USAGE) 0x25) +#define HID_USAGE_LED_CONFERENCE ((USAGE) 0x26) +#define HID_USAGE_LED_STAND_BY ((USAGE) 0x27) +#define HID_USAGE_LED_CAMERA_ON ((USAGE) 0x28) +#define HID_USAGE_LED_CAMERA_OFF ((USAGE) 0x29) +#define HID_USAGE_LED_ON_LINE ((USAGE) 0x2A) +#define HID_USAGE_LED_OFF_LINE ((USAGE) 0x2B) +#define HID_USAGE_LED_BUSY ((USAGE) 0x2C) +#define HID_USAGE_LED_READY ((USAGE) 0x2D) +#define HID_USAGE_LED_PAPER_OUT ((USAGE) 0x2E) +#define HID_USAGE_LED_PAPER_JAM ((USAGE) 0x2F) +#define HID_USAGE_LED_REMOTE ((USAGE) 0x30) +#define HID_USAGE_LED_FORWARD ((USAGE) 0x31) +#define HID_USAGE_LED_REVERSE ((USAGE) 0x32) +#define HID_USAGE_LED_STOP ((USAGE) 0x33) +#define HID_USAGE_LED_REWIND ((USAGE) 0x34) +#define HID_USAGE_LED_FAST_FORWARD ((USAGE) 0x35) +#define HID_USAGE_LED_PLAY ((USAGE) 0x36) +#define HID_USAGE_LED_PAUSE ((USAGE) 0x37) +#define HID_USAGE_LED_RECORD ((USAGE) 0x38) +#define HID_USAGE_LED_ERROR ((USAGE) 0x39) +#define HID_USAGE_LED_SELECTED_INDICATOR ((USAGE) 0x3A) +#define HID_USAGE_LED_IN_USE_INDICATOR ((USAGE) 0x3B) +#define HID_USAGE_LED_MULTI_MODE_INDICATOR ((USAGE) 0x3C) +#define HID_USAGE_LED_INDICATOR_ON ((USAGE) 0x3D) +#define HID_USAGE_LED_INDICATOR_FLASH ((USAGE) 0x3E) +#define HID_USAGE_LED_INDICATOR_SLOW_BLINK ((USAGE) 0x3F) +#define HID_USAGE_LED_INDICATOR_FAST_BLINK ((USAGE) 0x40) +#define HID_USAGE_LED_INDICATOR_OFF ((USAGE) 0x41) +#define HID_USAGE_LED_FLASH_ON_TIME ((USAGE) 0x42) +#define HID_USAGE_LED_SLOW_BLINK_ON_TIME ((USAGE) 0x43) +#define HID_USAGE_LED_SLOW_BLINK_OFF_TIME ((USAGE) 0x44) +#define HID_USAGE_LED_FAST_BLINK_ON_TIME ((USAGE) 0x45) +#define HID_USAGE_LED_FAST_BLINK_OFF_TIME ((USAGE) 0x46) +#define HID_USAGE_LED_INDICATOR_COLOR ((USAGE) 0x47) +#define HID_USAGE_LED_RED ((USAGE) 0x48) +#define HID_USAGE_LED_GREEN ((USAGE) 0x49) +#define HID_USAGE_LED_AMBER ((USAGE) 0x4A) +#define HID_USAGE_LED_GENERIC_INDICATOR ((USAGE) 0x3B) + +// +// Button Page (0x09) +// +// There is no need to label these usages. +// + + +// +// Ordinal Page (0x0A) +// +// There is no need to label these usages. +// + + +// +// Telephony Device Page (0x0B) +// + +#define HID_USAGE_TELEPHONY_PHONE ((USAGE) 0x01) +#define HID_USAGE_TELEPHONY_ANSWERING_MACHINE ((USAGE) 0x02) +#define HID_USAGE_TELEPHONY_MESSAGE_CONTROLS ((USAGE) 0x03) +#define HID_USAGE_TELEPHONY_HANDSET ((USAGE) 0x04) +#define HID_USAGE_TELEPHONY_HEADSET ((USAGE) 0x05) +#define HID_USAGE_TELEPHONY_KEYPAD ((USAGE) 0x06) +#define HID_USAGE_TELEPHONY_PROGRAMMABLE_BUTTON ((USAGE) 0x07) + +// +// and others... +// + + +#endif diff --git a/winsrc/prox.cpp b/winsrc/prox.cpp new file mode 100644 index 00000000..e95a4aff --- /dev/null +++ b/winsrc/prox.cpp @@ -0,0 +1,379 @@ +#include +#include +#include +#include +#include +extern "C" { +#include "include/hidsdi.h" +#include "include/hidpi.h" +} + +#include "prox.h" + +#define OUR_VID 0x9ac4 +#define OUR_PID 0x4b8f + +HANDLE UsbHandle; + +static void ShowError(void) +{ + char buf[1024]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, + buf, sizeof(buf), NULL); + printf("ERROR: %s", buf); +} + +static BOOL UsbConnect(void) +{ + typedef void (__stdcall *GetGuidProc)(GUID *); + typedef BOOLEAN (__stdcall *GetAttrProc)(HANDLE, HIDD_ATTRIBUTES *); + typedef BOOLEAN (__stdcall *GetPreparsedProc)(HANDLE, + PHIDP_PREPARSED_DATA *); + typedef NTSTATUS (__stdcall *GetCapsProc)(PHIDP_PREPARSED_DATA, PHIDP_CAPS); + GetGuidProc getGuid; + GetAttrProc getAttr; + GetPreparsedProc getPreparsed; + GetCapsProc getCaps; + + HMODULE h = LoadLibrary("hid.dll"); + getGuid = (GetGuidProc)GetProcAddress(h, "HidD_GetHidGuid"); + getAttr = (GetAttrProc)GetProcAddress(h, "HidD_GetAttributes"); + getPreparsed = (GetPreparsedProc)GetProcAddress(h, "HidD_GetPreparsedData"); + getCaps = (GetCapsProc)GetProcAddress(h, "HidP_GetCaps"); + + GUID hidGuid; + getGuid(&hidGuid); + + HDEVINFO devInfo; + devInfo = SetupDiGetClassDevs(&hidGuid, NULL, NULL, + DIGCF_PRESENT | DIGCF_INTERFACEDEVICE); + + SP_DEVICE_INTERFACE_DATA devInfoData; + devInfoData.cbSize = sizeof(devInfoData); + + int i; + for(i = 0;; i++) { + if(!SetupDiEnumDeviceInterfaces(devInfo, 0, &hidGuid, i, &devInfoData)) + { + if(GetLastError() != ERROR_NO_MORE_ITEMS) { +// printf("SetupDiEnumDeviceInterfaces failed\n"); + } +// printf("done list\n"); + SetupDiDestroyDeviceInfoList(devInfo); + return FALSE; + } + +// printf("item %d:\n", i); + + DWORD sizeReqd = 0; + if(!SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, + NULL, 0, &sizeReqd, NULL)) + { + if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) { +// printf("SetupDiGetDeviceInterfaceDetail (0) failed\n"); + continue; + } + } + + SP_DEVICE_INTERFACE_DETAIL_DATA *devInfoDetailData = + (SP_DEVICE_INTERFACE_DETAIL_DATA *)malloc(sizeReqd); + devInfoDetailData->cbSize = sizeof(*devInfoDetailData); + + if(!SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, + devInfoDetailData, 87, NULL, NULL)) + { +// printf("SetupDiGetDeviceInterfaceDetail (1) failed\n"); + continue; + } + + char *path = devInfoDetailData->DevicePath; + + UsbHandle = CreateFile(path, /*GENERIC_READ |*/ GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + + if(UsbHandle == INVALID_HANDLE_VALUE) { + ShowError(); +// printf("CreateFile failed: for '%s'\n", path); + continue; + } + + HIDD_ATTRIBUTES attr; + attr.Size = sizeof(attr); + if(!getAttr(UsbHandle, &attr)) { + ShowError(); +// printf("HidD_GetAttributes failed\n"); + continue; + } + +// printf("VID: %04x PID %04x\n", attr.VendorID, attr.ProductID); + + if(attr.VendorID != OUR_VID || attr.ProductID != OUR_PID) { + CloseHandle(UsbHandle); +// printf(" nope, not us\n"); + continue; + } + +// printf ("got it!\n"); + CloseHandle(UsbHandle); + + UsbHandle = CreateFile(path, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + + if(UsbHandle == INVALID_HANDLE_VALUE) { + ShowError(); +// printf("Error, couldn't open our own handle as desired.\n"); + return FALSE; + } + + PHIDP_PREPARSED_DATA pp; + getPreparsed(UsbHandle, &pp); + HIDP_CAPS caps; + + if(getCaps(pp, &caps) != HIDP_STATUS_SUCCESS) { +// printf("getcaps failed\n"); + return FALSE; + } + +// printf("input/out report %d/%d\n", caps.InputReportByteLength, +// caps.OutputReportByteLength); + + + return TRUE; + } + return FALSE; +} + +BOOL ReceiveCommandPoll(UsbCommand *c) +{ + static BOOL ReadInProgress = FALSE; + static OVERLAPPED Ov; + static BYTE Buf[65]; + static DWORD HaveRead; + + if(!ReadInProgress) { + memset(&Ov, 0, sizeof(Ov)); + ReadFile(UsbHandle, Buf, 65, &HaveRead, &Ov); + if(GetLastError() != ERROR_IO_PENDING) { + ShowError(); + exit(-1); + } + ReadInProgress = TRUE; + } + + if(HasOverlappedIoCompleted(&Ov)) { + ReadInProgress = FALSE; + + if(!GetOverlappedResult(UsbHandle, &Ov, &HaveRead, FALSE)) { + ShowError(); + exit(-1); + } + + memcpy(c, Buf+1, 64); + + return TRUE; + } else { + return FALSE; + } +} + +void ReceiveCommand(UsbCommand *c) +{ + while(!ReceiveCommandPoll(c)) { + Sleep(0); + } +} + +void SendCommand(UsbCommand *c, BOOL wantAck) +{ + BYTE buf[65]; + buf[0] = 0; + memcpy(buf+1, c, 64); + + DWORD written; + OVERLAPPED ov; + memset(&ov, 0, sizeof(ov)); + WriteFile(UsbHandle, buf, 65, &written, &ov); + if(GetLastError() != ERROR_IO_PENDING) { + ShowError(); + exit(-1); + } + + while(!HasOverlappedIoCompleted(&ov)) { + Sleep(0); + } + + if(!GetOverlappedResult(UsbHandle, &ov, &written, FALSE)) { + ShowError(); + exit(-1); + } + + if(wantAck) { + UsbCommand ack; + ReceiveCommand(&ack); + if(ack.cmd != CMD_ACK) { + printf("bad ACK\n"); + exit(-1); + } + } +} + +static DWORD ExpectedAddr; +static BYTE QueuedToSend[256]; +static BOOL AllWritten; + +static void FlushPrevious(void) +{ + UsbCommand c; + memset(&c, 0, sizeof(c)); + + printf("expected = %08x flush, ", ExpectedAddr); + + int i; + for(i = 0; i < 240; i += 48) { + c.cmd = CMD_SETUP_WRITE; + memcpy(c.d.asBytes, QueuedToSend+i, 48); + c.ext1 = (i/4); + SendCommand(&c, TRUE); + } + + c.cmd = CMD_FINISH_WRITE; + c.ext1 = (ExpectedAddr-1) & (~255); + printf("c.ext1 = %08x\r", c.ext1); + memcpy(c.d.asBytes, QueuedToSend+240, 16); + SendCommand(&c, TRUE); + + AllWritten = TRUE; +} + +static void GotByte(DWORD where, BYTE which) +{ + AllWritten = FALSE; + + if(where != ExpectedAddr) { + printf("bad: got at %08x, expected at %08x\n", where, ExpectedAddr); + exit(-1); + } + QueuedToSend[where & 255] = which; + ExpectedAddr++; + + if((where & 255) == 255) { + // we have completed a full page + FlushPrevious(); + } +} + +static int HexVal(int c) +{ + c = tolower(c); + if(c >= '0' && c <= '9') { + return c - '0'; + } else if(c >= 'a' && c <= 'f') { + return (c - 'a') + 10; + } else { + printf("bad hex digit '%c'\n", c); + exit(-1); + } +} + +static BYTE HexByte(char *s) +{ + return (HexVal(s[0]) << 4) | HexVal(s[1]); +} + +static void LoadFlashFromSRecords(char *file, int addr) +{ + ExpectedAddr = addr; + + FILE *f = fopen(file, "r"); + if(!f) { + printf("couldn't open file\n"); + exit(-1); + } + + char line[512]; + while(fgets(line, sizeof(line), f)) { + if(memcmp(line, "S3", 2)==0) { + char *s = line + 2; + int len = HexByte(s) - 5; + s += 2; + + char addrStr[9]; + memcpy(addrStr, s, 8); + addrStr[8] = '\0'; + DWORD addr; + sscanf(addrStr, "%x", &addr); + s += 8; + + int i; + for(i = 0; i < len; i++) { + while((addr+i) > ExpectedAddr) { + GotByte(ExpectedAddr, 0xff); + } + GotByte(addr+i, HexByte(s)); + s += 2; + } + } + } + + if(!AllWritten) FlushPrevious(); + + fclose(f); + printf("\ndone.\n"); +} + +int main(int argc, char **argv) +{ + int i = 0; + + if(argc < 2) { + printf("Usage: %s bootrom file.s19\n", argv[0]); + printf(" %s load osimage.s19\n", argv[0]); + printf(" %s fpga fpgaimg.s19\n", argv[0]); + printf(" %s gui\n", argv[0]); + return -1; + } + + for(;;) { + if(UsbConnect()) { + break; + } + if(i == 0) { + printf("...no device connected, polling for it now\n"); + } + if(i > 50000) { + printf("Could not connect to USB device; exiting.\n"); + return -1; + } + i++; + Sleep(5); + } + + if(strcmp(argv[1], "bootrom")==0 || strcmp(argv[1], "load")==0 || strcmp(argv[1], "fpga")==0) { + if(argc != 3) { + printf("Need filename.\n"); + return -1; + } + if(strcmp(argv[1], "bootrom")==0) { + LoadFlashFromSRecords(argv[2], 0); + } else if(strcmp(argv[1], "fpga")==0) { + LoadFlashFromSRecords(argv[2], 0x2000); + } else { + LoadFlashFromSRecords(argv[2], 0x10000); + } + } else if(strcmp(argv[1], "gui")==0) { + ShowGui(); + } else if(strcmp(argv[1], "cmd")==0) { + if(argc != 3) { + printf("Need command.\n"); + return -1; + } + ExecCmd(argv[2]); + } else { + printf("Command '%s' not recognized.\n", argv[1]); + return -1; + } + + return 0; +} diff --git a/winsrc/prox.h b/winsrc/prox.h new file mode 100644 index 00000000..f3be604c --- /dev/null +++ b/winsrc/prox.h @@ -0,0 +1,32 @@ +#ifndef __PROX_H +#define __PROX_H + +#include "../include/usb_cmd.h" + +// prox.cpp +void ReceiveCommand(UsbCommand *c); +BOOL ReceiveCommandPoll(UsbCommand *c); +void SendCommand(UsbCommand *c, BOOL wantAck); + +// gui.cpp +void ShowGui(void); +void HideGraphWindow(void); +void ShowGraphWindow(void); +void RepaintGraphWindow(void); +void PrintToScrollback(char *fmt, ...); +#define MAX_GRAPH_TRACE_LEN (1024*128) +extern int GraphBuffer[MAX_GRAPH_TRACE_LEN]; +extern int GraphTraceLen; +extern double CursorScaleFactor; +extern int CommandFinished; + +// command.cpp +void CommandReceived(char *cmd); +void UsbCommandReceived(UsbCommand *c); + +// cmdline.cpp +void ShowCommandline(void); +void ExecCmd(char *cmd); +//void PrintToScrollback(char *fmt, ...); + +#endif diff --git a/winsrc/vc90.pdb b/winsrc/vc90.pdb new file mode 100644 index 00000000..2451797c Binary files /dev/null and b/winsrc/vc90.pdb differ