├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.txt ├── Makefile ├── README.md └── ftx_prog.c /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: python-app 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: checkout repo 14 | uses: actions/checkout@v3 15 | 16 | - name: Install libusb and libfdti 17 | run: | 18 | sudo apt-get update 19 | sudo apt-get install libusb-1.0-0-dev libftdi-dev 20 | 21 | - name: build application 22 | run: make -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ftx_prog 2 | TAGS 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [v0.4] 2022-07-03 6 | 7 | * Support libfdti 1.x if `USE_LIBFTDI1` is set [#28] 8 | * Specify `-lusb-1.0` instead of `-lusb` in Makefile [#28] 9 | 10 | 11 | [Unreleased]: https://github.com/richardeoin/ftx-prog/compare/v0.4...HEAD 12 | 13 | [#172]: https://github.com/stm32-rs/stm32h7xx-hal/pull/172 14 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | GNU GENERAL PUBLIC LICENSE 3 | Version 2, June 1991 4 | 5 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 6 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | Preamble 11 | 12 | The licenses for most software are designed to take away your 13 | freedom to share and change it. By contrast, the GNU General Public 14 | License is intended to guarantee your freedom to share and change free 15 | software--to make sure the software is free for all its users. This 16 | General Public License applies to most of the Free Software 17 | Foundation's software and to any other program whose authors commit to 18 | using it. (Some other Free Software Foundation software is covered by 19 | the GNU Library General Public License instead.) You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | this service if you wish), that you receive source code or can get it 26 | if you want it, that you can change the software or use pieces of it 27 | in new free programs; and that you know you can do these things. 28 | 29 | To protect your rights, we need to make restrictions that forbid 30 | anyone to deny you these rights or to ask you to surrender the rights. 31 | These restrictions translate to certain responsibilities for you if you 32 | distribute copies of the software, or if you modify it. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must give the recipients all the rights that 36 | you have. You must make sure that they, too, receive or can get the 37 | source code. And you must show them these terms so they know their 38 | rights. 39 | 40 | We protect your rights with two steps: (1) copyright the software, and 41 | (2) offer you this license which gives you legal permission to copy, 42 | distribute and/or modify the software. 43 | 44 | Also, for each author's protection and ours, we want to make certain 45 | that everyone understands that there is no warranty for this free 46 | software. If the software is modified by someone else and passed on, we 47 | want its recipients to know that what they have is not the original, so 48 | that any problems introduced by others will not reflect on the original 49 | authors' reputations. 50 | 51 | Finally, any free program is threatened constantly by software 52 | patents. We wish to avoid the danger that redistributors of a free 53 | program will individually obtain patent licenses, in effect making the 54 | program proprietary. To prevent this, we have made it clear that any 55 | patent must be licensed for everyone's free use or not licensed at all. 56 | 57 | The precise terms and conditions for copying, distribution and 58 | modification follow. 59 | 60 | GNU GENERAL PUBLIC LICENSE 61 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 62 | 63 | 0. This License applies to any program or other work which contains 64 | a notice placed by the copyright holder saying it may be distributed 65 | under the terms of this General Public License. The "Program", below, 66 | refers to any such program or work, and a "work based on the Program" 67 | means either the Program or any derivative work under copyright law: 68 | that is to say, a work containing the Program or a portion of it, 69 | either verbatim or with modifications and/or translated into another 70 | language. (Hereinafter, translation is included without limitation in 71 | the term "modification".) Each licensee is addressed as "you". 72 | 73 | Activities other than copying, distribution and modification are not 74 | covered by this License; they are outside its scope. The act of 75 | running the Program is not restricted, and the output from the Program 76 | is covered only if its contents constitute a work based on the 77 | Program (independent of having been made by running the Program). 78 | Whether that is true depends on what the Program does. 79 | 80 | 1. You may copy and distribute verbatim copies of the Program's 81 | source code as you receive it, in any medium, provided that you 82 | conspicuously and appropriately publish on each copy an appropriate 83 | copyright notice and disclaimer of warranty; keep intact all the 84 | notices that refer to this License and to the absence of any warranty; 85 | and give any other recipients of the Program a copy of this License 86 | along with the Program. 87 | 88 | You may charge a fee for the physical act of transferring a copy, and 89 | you may at your option offer warranty protection in exchange for a fee. 90 | 91 | 2. You may modify your copy or copies of the Program or any portion 92 | of it, thus forming a work based on the Program, and copy and 93 | distribute such modifications or work under the terms of Section 1 94 | above, provided that you also meet all of these conditions: 95 | 96 | a) You must cause the modified files to carry prominent notices 97 | stating that you changed the files and the date of any change. 98 | 99 | b) You must cause any work that you distribute or publish, that in 100 | whole or in part contains or is derived from the Program or any 101 | part thereof, to be licensed as a whole at no charge to all third 102 | parties under the terms of this License. 103 | 104 | c) If the modified program normally reads commands interactively 105 | when run, you must cause it, when started running for such 106 | interactive use in the most ordinary way, to print or display an 107 | announcement including an appropriate copyright notice and a 108 | notice that there is no warranty (or else, saying that you provide 109 | a warranty) and that users may redistribute the program under 110 | these conditions, and telling the user how to view a copy of this 111 | License. (Exception: if the Program itself is interactive but 112 | does not normally print such an announcement, your work based on 113 | the Program is not required to print an announcement.) 114 | 115 | These requirements apply to the modified work as a whole. If 116 | identifiable sections of that work are not derived from the Program, 117 | and can be reasonably considered independent and separate works in 118 | themselves, then this License, and its terms, do not apply to those 119 | sections when you distribute them as separate works. But when you 120 | distribute the same sections as part of a whole which is a work based 121 | on the Program, the distribution of the whole must be on the terms of 122 | this License, whose permissions for other licensees extend to the 123 | entire whole, and thus to each and every part regardless of who wrote it. 124 | 125 | Thus, it is not the intent of this section to claim rights or contest 126 | your rights to work written entirely by you; rather, the intent is to 127 | exercise the right to control the distribution of derivative or 128 | collective works based on the Program. 129 | 130 | In addition, mere aggregation of another work not based on the Program 131 | with the Program (or with a work based on the Program) on a volume of 132 | a storage or distribution medium does not bring the other work under 133 | the scope of this License. 134 | 135 | 3. You may copy and distribute the Program (or a work based on it, 136 | under Section 2) in object code or executable form under the terms of 137 | Sections 1 and 2 above provided that you also do one of the following: 138 | 139 | a) Accompany it with the complete corresponding machine-readable 140 | source code, which must be distributed under the terms of Sections 141 | 1 and 2 above on a medium customarily used for software interchange; or, 142 | 143 | b) Accompany it with a written offer, valid for at least three 144 | years, to give any third party, for a charge no more than your 145 | cost of physically performing source distribution, a complete 146 | machine-readable copy of the corresponding source code, to be 147 | distributed under the terms of Sections 1 and 2 above on a medium 148 | customarily used for software interchange; or, 149 | 150 | c) Accompany it with the information you received as to the offer 151 | to distribute corresponding source code. (This alternative is 152 | allowed only for noncommercial distribution and only if you 153 | received the program in object code or executable form with such 154 | an offer, in accord with Subsection b above.) 155 | 156 | The source code for a work means the preferred form of the work for 157 | making modifications to it. For an executable work, complete source 158 | code means all the source code for all modules it contains, plus any 159 | associated interface definition files, plus the scripts used to 160 | control compilation and installation of the executable. However, as a 161 | special exception, the source code distributed need not include 162 | anything that is normally distributed (in either source or binary 163 | form) with the major components (compiler, kernel, and so on) of the 164 | operating system on which the executable runs, unless that component 165 | itself accompanies the executable. 166 | 167 | If distribution of executable or object code is made by offering 168 | access to copy from a designated place, then offering equivalent 169 | access to copy the source code from the same place counts as 170 | distribution of the source code, even though third parties are not 171 | compelled to copy the source along with the object code. 172 | 173 | 4. You may not copy, modify, sublicense, or distribute the Program 174 | except as expressly provided under this License. Any attempt 175 | otherwise to copy, modify, sublicense or distribute the Program is 176 | void, and will automatically terminate your rights under this License. 177 | However, parties who have received copies, or rights, from you under 178 | this License will not have their licenses terminated so long as such 179 | parties remain in full compliance. 180 | 181 | 5. You are not required to accept this License, since you have not 182 | signed it. However, nothing else grants you permission to modify or 183 | distribute the Program or its derivative works. These actions are 184 | prohibited by law if you do not accept this License. Therefore, by 185 | modifying or distributing the Program (or any work based on the 186 | Program), you indicate your acceptance of this License to do so, and 187 | all its terms and conditions for copying, distributing or modifying 188 | the Program or works based on it. 189 | 190 | 6. Each time you redistribute the Program (or any work based on the 191 | Program), the recipient automatically receives a license from the 192 | original licensor to copy, distribute or modify the Program subject to 193 | these terms and conditions. You may not impose any further 194 | restrictions on the recipients' exercise of the rights granted herein. 195 | You are not responsible for enforcing compliance by third parties to 196 | this License. 197 | 198 | 7. If, as a consequence of a court judgment or allegation of patent 199 | infringement or for any other reason (not limited to patent issues), 200 | conditions are imposed on you (whether by court order, agreement or 201 | otherwise) that contradict the conditions of this License, they do not 202 | excuse you from the conditions of this License. If you cannot 203 | distribute so as to satisfy simultaneously your obligations under this 204 | License and any other pertinent obligations, then as a consequence you 205 | may not distribute the Program at all. For example, if a patent 206 | license would not permit royalty-free redistribution of the Program by 207 | all those who receive copies directly or indirectly through you, then 208 | the only way you could satisfy both it and this License would be to 209 | refrain entirely from distribution of the Program. 210 | 211 | If any portion of this section is held invalid or unenforceable under 212 | any particular circumstance, the balance of the section is intended to 213 | apply and the section as a whole is intended to apply in other 214 | circumstances. 215 | 216 | It is not the purpose of this section to induce you to infringe any 217 | patents or other property right claims or to contest validity of any 218 | such claims; this section has the sole purpose of protecting the 219 | integrity of the free software distribution system, which is 220 | implemented by public license practices. Many people have made 221 | generous contributions to the wide range of software distributed 222 | through that system in reliance on consistent application of that 223 | system; it is up to the author/donor to decide if he or she is willing 224 | to distribute software through any other system and a licensee cannot 225 | impose that choice. 226 | 227 | This section is intended to make thoroughly clear what is believed to 228 | be a consequence of the rest of this License. 229 | 230 | 8. If the distribution and/or use of the Program is restricted in 231 | certain countries either by patents or by copyrighted interfaces, the 232 | original copyright holder who places the Program under this License 233 | may add an explicit geographical distribution limitation excluding 234 | those countries, so that distribution is permitted only in or among 235 | countries not thus excluded. In such case, this License incorporates 236 | the limitation as if written in the body of this License. 237 | 238 | 9. The Free Software Foundation may publish revised and/or new versions 239 | of the General Public License from time to time. Such new versions will 240 | be similar in spirit to the present version, but may differ in detail to 241 | address new problems or concerns. 242 | 243 | Each version is given a distinguishing version number. If the Program 244 | specifies a version number of this License which applies to it and "any 245 | later version", you have the option of following the terms and conditions 246 | either of that version or of any later version published by the Free 247 | Software Foundation. If the Program does not specify a version number of 248 | this License, you may choose any version ever published by the Free Software 249 | Foundation. 250 | 251 | 10. If you wish to incorporate parts of the Program into other free 252 | programs whose distribution conditions are different, write to the author 253 | to ask for permission. For software which is copyrighted by the Free 254 | Software Foundation, write to the Free Software Foundation; we sometimes 255 | make exceptions for this. Our decision will be guided by the two goals 256 | of preserving the free status of all derivatives of our free software and 257 | of promoting the sharing and reuse of software generally. 258 | 259 | NO WARRANTY 260 | 261 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 262 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 263 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 264 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 265 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 266 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 267 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 268 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 269 | REPAIR OR CORRECTION. 270 | 271 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 272 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 273 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 274 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 275 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 276 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 277 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 278 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 279 | POSSIBILITY OF SUCH DAMAGES. 280 | 281 | END OF TERMS AND CONDITIONS 282 | 283 | How to Apply These Terms to Your New Programs 284 | 285 | If you develop a new program, and you want it to be of the greatest 286 | possible use to the public, the best way to achieve this is to make it 287 | free software which everyone can redistribute and change under these terms. 288 | 289 | To do so, attach the following notices to the program. It is safest 290 | to attach them to the start of each source file to most effectively 291 | convey the exclusion of warranty; and each file should have at least 292 | the "copyright" line and a pointer to where the full notice is found. 293 | 294 | 295 | Copyright (C) 296 | 297 | This program is free software; you can redistribute it and/or modify 298 | it under the terms of the GNU General Public License as published by 299 | the Free Software Foundation; either version 2 of the License, or 300 | (at your option) any later version. 301 | 302 | This program is distributed in the hope that it will be useful, 303 | but WITHOUT ANY WARRANTY; without even the implied warranty of 304 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 305 | GNU General Public License for more details. 306 | 307 | You should have received a copy of the GNU General Public License 308 | along with this program; if not, write to the Free Software 309 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 310 | 311 | 312 | Also add information on how to contact you by electronic and paper mail. 313 | 314 | If the program is interactive, make it output a short notice like this 315 | when it starts in an interactive mode: 316 | 317 | Gnomovision version 69, Copyright (C) year name of author 318 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 319 | This is free software, and you are welcome to redistribute it 320 | under certain conditions; type `show c' for details. 321 | 322 | The hypothetical commands `show w' and `show c' should show the appropriate 323 | parts of the General Public License. Of course, the commands you use may 324 | be called something other than `show w' and `show c'; they could even be 325 | mouse-clicks or menu items--whatever suits your program. 326 | 327 | You should also get your employer (if you work as a programmer) or your 328 | school, if any, to sign a "copyright disclaimer" for the program, if 329 | necessary. Here is a sample; alter the names: 330 | 331 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 332 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 333 | 334 | , 1 April 1989 335 | Ty Coon, President of Vice 336 | 337 | This General Public License does not permit incorporating your program into 338 | proprietary programs. If your program is a subroutine library, you may 339 | consider it more useful to permit linking proprietary applications with the 340 | library. If this is what you want to do, use the GNU Library General 341 | Public License instead of this License. 342 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | ifeq ($(USE_LIBFTDI1),1) 3 | CFLAGS_FTDI = -DUSE_LIBFTDI1 4 | LDFLAGS_FTDI = -lftdi1 5 | else 6 | LDFLAGS_FTDI = -lftdi 7 | endif 8 | 9 | override CFLAGS += -Wall -O2 -s -pedantic $(CFLAGS_FTDI) 10 | override LDFLAGS += -lusb-1.0 $(LDFLAGS_FTDI) -s 11 | 12 | PROG = ftx_prog 13 | 14 | all: $(PROG) 15 | 16 | $(PROG): $(PROG).c 17 | $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) 18 | 19 | clean: 20 | rm -f $(PROG) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **ftx-prog** is a Linux command-line alternative to the 2 | [FTProg](http://www.ftdichip.com/Support/Utilities.htm#FT_Prog) 3 | utility for [FTDI](http://www.ftdichip.com/)'s 4 | [FT-X series](http://www.ftdichip.com/FT-X.htm) of USB interfaces. 5 | 6 | ## Installation 7 | 8 | Install some prerequisites 9 | 10 | ``` 11 | sudo apt-get install build-essential gcc make libftdi-dev 12 | ``` 13 | 14 | then 15 | [download](https://github.com/richardeoin/ftx-prog/archive/master.zip), 16 | unzip, open a terminal in the unzipped directory and 17 | 18 | ``` 19 | make 20 | ``` 21 | 22 | Don't forget to plug in your FT-X device! 23 | 24 | ## Usage 25 | 26 | ``` 27 | sudo ./ftx_prog 28 | ``` 29 | 30 | This should give you full details on all the possible options. 31 | 32 | ### Display Current Settings 33 | 34 | ``` 35 | sudo ./ftx_prog --dump 36 | ``` 37 | 38 | ### CBUS Pins 39 | 40 | ``` 41 | sudo ./ftx_prog --cbus [cbus pin number] [function] 42 | ``` 43 | 44 | Sets up configurable CBUS pins with a particular function. Have a look 45 | at `--help` to see all the possible functions, and §7.12 of 46 | [this application note](http://www.ftdichip.com/Support/Documents/AppNotes/AN_201_FT-X%20MTP%20Memory%20Configuration.pdf) 47 | to see what they all do. Don't forget that the pin numbers are zero 48 | based, so `--cbus 0` corresponds to the `CBUS0` pin. 49 | 50 | The most commonly used are: 51 | 52 | * `RxLED` *(pulses low when data is being sent from the host to the USB device)* 53 | * `TxLED` *(pulses low when data is being sent from the USB device to the host)* 54 | * `TxRxLED` *(pulses low when data is being sent either way)* 55 | * `SLEEP` *(goes low when the interface is in USB suspend mode)* 56 | 57 | ### Inverting RS232 Signals 58 | `FT230X` and `FT231X` only 59 | 60 | ``` 61 | sudo ./ftx_prog --invert [rs232 pin] 62 | ``` 63 | 64 | This will invert the polarity of this pin on the chip. So if it was 65 | Active High it'll become Active Low and vice versa. You can use 66 | `--dump` to see if the pin is in its default state or not. 67 | 68 | The possible pins are: 69 | * `txd` *The interface's data output pin* 70 | * `rxd` *The interface's data input pin* 71 | * `rts` *Request to Send output pin* 72 | * `cts` *Clear to Send input pin* 73 | * `dtr` *Data Terminal Ready output pin* 74 | * `dsr` *Data Set Ready input pin* 75 | * `dcd` *Data Carrier Detect input pin* 76 | * `ri` *Ring Indicator input pin* 77 | 78 | Taking `RI` low (or high when polarity is inverted) for > 20ms will 79 | resume the USB host controller from suspend if remote wakeup has been 80 | enabled using `--remote-wakeup on`. 81 | 82 | ### I2C 83 | `FT200XD` and `FT201X` only 84 | 85 | *Note: The functionality of these options is completely untested* 86 | 87 | ``` 88 | sudo ./ftx_prog --i2c-slave-address 89 | ``` 90 | 91 | Sets the slave address of the FT-X on the I2C bus. For instance, 92 | `--i2c-slave-address 0x22` will restore the slave address to its 93 | default value. 94 | 95 | ``` 96 | sudo ./ftx_prog --i2c-schmitt [on|off] 97 | ``` 98 | 99 | The I2C pins have schmitt triggers which can be turned on or off. 100 | 101 | 102 | ``` 103 | sudo ./ftx_prog --i2c-device-id 104 | ``` 105 | 106 | Sets the I2C device ID. 107 | 108 | ### SPI / FT1248 109 | `FT220X` and `FT221X` only 110 | 111 | *Note: The functionality of these options is completely untested* 112 | 113 | ``` 114 | sudo ./ftx_prog --ft1248-cpol [high|low] 115 | ``` 116 | 117 | The clock on the SPI / FT1248 bus can either be active `high` (Like 118 | SPI `Mode 1`) or active `low` (Like SPI `Mode 3`). 119 | 120 | ``` 121 | sudo ./ftx_prog --ft1248-bord [msb|lsb] 122 | ``` 123 | 124 | The bit order on the SPI / FT1248 bus can be either `msb` first or `lsb` first. 125 | 126 | ``` 127 | sudo ./ftx_prog --ft1248-flow-control [on|off] 128 | ``` 129 | 130 | Turns on flow control when SS_n is inactive. 131 | 132 | ### RS485 133 | 134 | *Note: The functionality of these options is completely untested* 135 | 136 | ``` 137 | sudo ./ftx_prog --rs485-echo-supp [on|off] 138 | ``` 139 | 140 | Used to enable echo supression if the interface is being used in a 141 | RS-485 system. 142 | 143 | ### Misc 144 | 145 | ``` 146 | sudo ./ftx_prog --load-vcp [on|off] 147 | ``` 148 | 149 | Controls if the Virtual COM Port (VCP) drivers are loaded. 150 | 151 | ``` 152 | sudo ./ftx_prog --remote-wakeup [on|off] 153 | ``` 154 | 155 | Allows the interface to be woken up by something other than USB. 156 | 157 | Use `sudo ./ftx_prog --help` to see details of all the command line options. 158 | 159 | *There are other configuration options that have not yet been 160 | implemented in the user interface. Support for the user configurable 161 | area in the EEPROM is also a possibility.* 162 | 163 | ## Workarounds for FT-X devices 164 | 165 | ### All 166 | 167 | Due to an error in the Silicon Revisions A, B and C of the FT-X series: 168 | 169 | > The device is put into suspend mode during a transfer of certain 170 | > data patterns most notable with binary zeros. This can halt the data 171 | > transfer in certain circumstances and will require the device to be 172 | > reenumerated to recover. NB. It is the presence of this data 173 | > pattern on the USB bus regardless of whether the data is intended 174 | > for the [FT-X] or other devices (e.g. a broadcast) on the bus that 175 | > forces the suspend state. 176 | 177 | This can be fixed by connecting any `CBUS` pin to ground on the PCB 178 | and then configuring it as `KeepAwake#`. If you were to choose `CBUS0` 179 | then `sudo ./ftx_prog --cbus 0 Keep_Awake` will do the configuration. 180 | 181 | ### `FT230X` and `FT231X` 182 | 183 | In Silicon Revisons A and B of the `FT230X` and `FT231X`: 184 | 185 | > An incorrect value for the VCP driver was programmed into the MTP on some production runs. 186 | 187 | This means the Virtual COM Port (VCP) drivers are disabled, preventing 188 | the device from appearing as a COM Port on windows. *Note: This is 189 | untested and may not be true!* 190 | 191 | To enable the VCP drivers, use `sudo ./ftx_prog --load-vcp true`. 192 | 193 | ## License 194 | 195 | GPL v2 196 | 197 | ## Contributing 198 | 199 | Bug reports and pull requests welcome! 200 | 201 | This project is passively maintained, so there may be no changes for a long 202 | time. PRs are more likely to get merged if they are well explained, well 203 | commented and match the style of the surrounding code. 204 | 205 | ## Credits 206 | 207 | Modified for the FT-X series by Richard Meadows 2012 208 | 209 | Based upon [ft232r_prog](http://rtr.ca/ft232r/), Version 1.23, by 210 | [Mark Lord](http://rtr.ca/). Copyright 2010-2012. 211 | -------------------------------------------------------------------------------- /ftx_prog.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a Linux command-line alternative to the FTDI MProg/FTProg 3 | * utilities for FTDI's FT-X series. 4 | * 5 | * Modified for the FT-X series by Richard Meadows 2012. 6 | * 7 | * Based upon: 8 | * ft232r_prog.c by Mark Lord. Copyright 2010-2012. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2, or (at your option) 13 | * any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; see the file LICENSE.txt. If not, write to 22 | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #define MYVERSION "0.4" 37 | 38 | #define CBUS_COUNT 7 39 | 40 | static struct ftdi_context ftdi; 41 | static int verbose = 0; 42 | static int erase_eeprom = 0; 43 | static int ignore_crc_error = 0; 44 | static bool use_8b_strings = false; 45 | static const char *save_path = NULL, *restore_path = NULL; 46 | 47 | /* ------------ Bit Definitions for EEPROM Decoding ------------ */ 48 | 49 | enum cbus_mode { 50 | cbus_tristate = 0, 51 | cbus_rxled = 1, 52 | cbus_txled = 2, 53 | cbus_txrxled = 3, 54 | cbus_pwren = 4, 55 | cbus_sleep = 5, 56 | cbus_drive0 = 6, 57 | cbus_drive1 = 7, 58 | cbus_gpio = 8, 59 | cbus_txden = 9, 60 | cbus_clk24 = 10, 61 | cbus_clk12 = 11, 62 | cbus_clk6 = 12, 63 | cbus_bcd_det = 13, 64 | cbus_bcd_det_i = 14, 65 | cbus_i2c_txe = 15, 66 | cbus_i2c_rxf = 16, 67 | cbus_vbus_sense = 17, 68 | cbus_bitbang_wr = 18, 69 | cbus_bitbang_rd = 19, 70 | cbus_timestamp = 20, 71 | cbus_keep_awake = 21, 72 | _cbus_mode_end 73 | }; 74 | enum misc_config { 75 | bcd_enable = 0x01, 76 | force_power_enable = 0x02, 77 | deactivate_sleep = 0x04, 78 | rs485_echo_suppress = 0x08, 79 | ext_osc = 0x10, 80 | ext_osc_feedback_en = 0x20, 81 | vbus_sense_alloc = 0x40, 82 | load_vcp = 0x80, 83 | }; 84 | enum power_config { 85 | remote_wakeup = 0x20, 86 | self_powered = 0x40, 87 | }; 88 | enum dbus_cbus_config { 89 | dbus_drive_strength = 0x03, 90 | dbus_slow_slew = 0x04, 91 | dbus_schmitt = 0x08, 92 | cbus_drive_strength = 0x30, 93 | cbus_slow_slew = 0x40, 94 | cbus_schmitt = 0x80, 95 | }; 96 | enum peripheral_config { 97 | suspend_pull_down = 0x04, 98 | serial_number_avail = 0x08, 99 | ft1248_cpol = 0x10, 100 | ft1248_bord = 0x20, 101 | ft1248_flow_control = 0x40, 102 | disable_i2c_schmitt = 0x80, 103 | invert_txd = 0x01, 104 | invert_rxd = 0x02, 105 | invert_rts = 0x04, 106 | invert_cts = 0x08, 107 | invert_dtr = 0x10, 108 | invert_dsr = 0x20, 109 | invert_dcd = 0x40, 110 | invert_ri = 0x80, 111 | }; 112 | 113 | /* ------------ Command Line Arguments ------------ */ 114 | 115 | enum arg_type { 116 | arg_help, 117 | arg_dump, 118 | arg_verbose, 119 | arg_save, 120 | arg_restore, 121 | arg_8b_strings, 122 | arg_cbus, 123 | arg_manufacturer, 124 | arg_product, 125 | arg_old_serno, 126 | arg_new_serno, 127 | arg_max_bus_power, 128 | arg_suspend_pull_down, 129 | arg_load_vcp, 130 | arg_remote_wakeup, 131 | arg_ft1248_cpol, 132 | arg_ft1248_bord, 133 | arg_ft1248_flow_control, 134 | arg_i2c_schmitt, 135 | arg_i2c_slave_address, 136 | arg_i2c_device_id, 137 | arg_rs485_echo_suppression, 138 | 139 | arg_old_vid, 140 | arg_old_pid, 141 | arg_new_vid, 142 | arg_new_pid, 143 | arg_invert, 144 | arg_self_powered, 145 | arg_ignore_crc_error, 146 | arg_erase_eeprom, 147 | arg_dbus_config, 148 | arg_cbus_config 149 | }; 150 | 151 | struct args_required_t 152 | { 153 | enum arg_type t; 154 | int number; 155 | }; 156 | 157 | const struct args_required_t req_info[] = 158 | { 159 | {arg_help, 0}, 160 | {arg_dump, 0}, 161 | {arg_verbose, 0}, 162 | {arg_save, 1}, 163 | {arg_restore, 1}, 164 | {arg_8b_strings, 0}, 165 | {arg_cbus, 2}, 166 | {arg_manufacturer, 1}, 167 | {arg_product, 1}, 168 | {arg_old_serno, 1}, 169 | {arg_new_serno, 1}, 170 | {arg_max_bus_power, 1}, 171 | {arg_suspend_pull_down, 1}, 172 | {arg_load_vcp, 1}, 173 | {arg_remote_wakeup, 1}, 174 | {arg_ft1248_cpol, 1}, 175 | {arg_ft1248_bord, 1}, 176 | {arg_ft1248_flow_control, 1}, 177 | {arg_i2c_schmitt, 1}, 178 | {arg_i2c_slave_address, 1}, 179 | {arg_i2c_device_id, 1}, 180 | {arg_rs485_echo_suppression, 1}, 181 | 182 | {arg_old_vid, 1}, 183 | {arg_old_pid, 1}, 184 | {arg_new_vid, 1}, 185 | {arg_new_pid, 1}, 186 | {arg_invert, 1}, 187 | {arg_self_powered, 1}, 188 | {arg_ignore_crc_error, 0}, 189 | {arg_erase_eeprom, 0}, 190 | {arg_cbus_config,1}, 191 | }; 192 | 193 | 194 | 195 | /* ------------ Strings for argument parsing ------------ */ 196 | 197 | static const char* arg_type_strings[] = { 198 | "--help", 199 | "--dump", 200 | "--verbose", 201 | "--save", 202 | "--restore", 203 | "--8bit-strings", 204 | "--cbus", 205 | "--manufacturer", 206 | "--product", 207 | "--old-serial-number", 208 | "--new-serial-number", 209 | "--max-bus-power", 210 | "--suspend-pull-down", 211 | "--load-vcp", 212 | "--remote-wakeup", 213 | "--ft1248-cpol", 214 | "--ft1248-bord", 215 | "--ft1248-flow-control", 216 | "--i2c-schmitt", 217 | "--i2c-slave-address", 218 | "--i2c-device-id", 219 | "--rs485-echo-supp", 220 | "--old-vid", 221 | "--old-pid", 222 | "--new-vid", 223 | "--new-pid", 224 | "--invert", 225 | "--self-powered", 226 | "--ignore-crc-error", 227 | "--erase-eeprom", 228 | "--dbus-config", 229 | "--cbus-config", 230 | NULL 231 | }; 232 | static const char* rs232_strings[] = { 233 | "txd", 234 | "rxd", 235 | "rts", 236 | "cts", 237 | "dtr", 238 | "dsr", 239 | "dcd", 240 | "ri", 241 | NULL 242 | }; 243 | static const char* cbus_strings[] = { 244 | "0", 245 | "1", 246 | "2", 247 | "3", 248 | "4", 249 | "5", 250 | "6", 251 | NULL 252 | }; 253 | static const char* cbus_mode_strings[] = { 254 | "Tristate", 255 | "RxLED", 256 | "TxLED", 257 | "TxRxLED", 258 | "PWREN", 259 | "SLEEP", 260 | "Drive_0", 261 | "Drive_1", 262 | "GPIO", 263 | "TXDEN", 264 | "CLK24MHz", 265 | "CLK12MHz", 266 | "CLK6MHz", 267 | "BCD_Charger", 268 | "BCD_Charger#", 269 | "I2C_TXE", 270 | "I2C_RXF", 271 | "VBUS_Sense", 272 | "BitBang_WR", 273 | "BitBang_RD", 274 | "Time_Stamp", 275 | "Keep_Awake", 276 | NULL 277 | }; 278 | static const char *d_cbus_config_strings [] = { 279 | "4ma", 280 | "8ma", 281 | "12ma", 282 | "16ma", 283 | "slow_slew", 284 | "fast_slew", 285 | "normal", 286 | "schmitt", 287 | NULL 288 | }; 289 | 290 | static const char *arg_type_help[] = { 291 | " # (show this help text)", 292 | " # (dump eeprom settings to stdout)", 293 | " # (show debug info and raw eeprom contents)", 294 | " # (save original eeprom contents to file)", 295 | " # (restore initial eeprom contents from file)", 296 | " # (byte strings)", 297 | "[cbus]", 298 | " # (new USB manufacturer string)", 299 | " # (new USB product name string)", 300 | " # (current serial number of device to be reprogrammed)", 301 | " # (new USB serial number string)", 302 | " # (max bus current in milli-amperes)", 303 | " [on|off] # (force I/O pins into logic low state on suspend)", 304 | " [on|off] # (controls if the VCP drivers are loaded)", 305 | " [on|off] # (allows the interface to be woken up by something other than USB)", 306 | " [high|low] # (set the clock polarity on the FT1248 interface to active high or active low)", 307 | " [msb|lsb] # (set the bit order on the FT1248 interface to msb first or lsb first)", 308 | " [on|off] # (flow control for FT1248 interface)", 309 | " [on|off] # (schmitt trigger on I2C interface)", 310 | " # (I2C slave address)", 311 | " # (I2C device ID)", 312 | " [on|off] # (enable echo supression on the RS485 bus)", 313 | " # (current vendor id of device to be reprogrammed, eg. 0x0403)", 314 | " # (current product id of device to be reprogrammed, eg. 0x6001)", 315 | " # (new/custom vendor id to be programmed)", 316 | " # (new/custom product id be programmed)", 317 | "[invert]", 318 | " [on|off] # (specify if chip is bus-powered or self-powered)", 319 | " # Ignore CRC errors and continue ", 320 | " # Erase the EEPROM and exit", 321 | "dbus_cfg", 322 | "cbus_cfg", 323 | 324 | }; 325 | 326 | static const char *bool_strings[] = { 327 | "False", 328 | "True", 329 | "false", 330 | "true", 331 | "Off", 332 | "On", 333 | "off", 334 | "on", 335 | "0", 336 | "1", 337 | "No", 338 | "Yes", 339 | "no", 340 | "yes", 341 | "disable", 342 | "enable", 343 | "low", 344 | "high", 345 | "msb", 346 | "lsb", 347 | }; 348 | 349 | struct eeprom_fields { 350 | /* Misc Config */ 351 | unsigned char bcd_enable; 352 | unsigned char force_power_enable; 353 | unsigned char deactivate_sleep; 354 | unsigned char rs485_echo_suppress; 355 | unsigned char ext_osc; 356 | unsigned char ext_osc_feedback_en; 357 | unsigned char vbus_sense_alloc; 358 | unsigned char load_vcp; 359 | 360 | /* USB VID/PID */ 361 | unsigned short usb_vid; 362 | unsigned short usb_pid; 363 | 364 | /* USB Release Number */ 365 | unsigned short usb_release_major; 366 | unsigned short usb_release_minor; 367 | 368 | /* Max Power and Config */ 369 | unsigned char remote_wakeup; 370 | unsigned char self_powered; 371 | unsigned char max_power; /* Units of 2mA */ 372 | 373 | /* Device and perhiperal control */ 374 | unsigned char suspend_pull_down; 375 | unsigned char serial_number_avail; 376 | unsigned char ft1248_cpol; 377 | unsigned char ft1248_bord; 378 | unsigned char ft1248_flow_control; 379 | unsigned char disable_i2c_schmitt; 380 | unsigned char invert_txd; 381 | unsigned char invert_rxd; 382 | unsigned char invert_rts; 383 | unsigned char invert_cts; 384 | unsigned char invert_dtr; 385 | unsigned char invert_dsr; 386 | unsigned char invert_dcd; 387 | unsigned char invert_ri; 388 | 389 | /* DBUS & CBUS Control */ 390 | unsigned char dbus_drive_strength; 391 | unsigned char dbus_slow_slew; 392 | unsigned char dbus_schmitt; 393 | unsigned char cbus_drive_strength; 394 | unsigned char cbus_slow_slew; 395 | unsigned char cbus_schmitt; 396 | 397 | /* Manufacturer, Product and Serial Number string */ 398 | char* manufacturer_string; 399 | char* product_string; 400 | char* serial_string; 401 | 402 | /* I2C */ 403 | unsigned short i2c_slave_addr; 404 | unsigned int i2c_device_id; 405 | 406 | /* CBUS */ 407 | enum cbus_mode cbus[CBUS_COUNT]; 408 | 409 | /* Other memory areas */ 410 | unsigned char user_mem[92]; /* user memory space */ 411 | unsigned char factory_config[32]; /* factory configuration values */ 412 | 413 | /* These are not actually eeprom values; here for convenience */ 414 | unsigned short old_vid; 415 | unsigned short old_pid; 416 | const char *old_serno; 417 | }; 418 | 419 | /* ------------ libftdi helpers ------------ */ 420 | 421 | static void do_deinit (void) 422 | { 423 | ftdi_deinit(&ftdi); 424 | } 425 | 426 | static void do_close (void) 427 | { 428 | ftdi_usb_close(&ftdi); 429 | } 430 | 431 | /* ------------ Printing ------------ */ 432 | 433 | /** 434 | * Prints a hex dump of a block of memory 435 | */ 436 | static void dumpmem (const char *msg, void *addr, int len) 437 | { 438 | char *data = addr, hex[3 * 16 + 1], ascii[17]; 439 | unsigned int i, offset = 0; 440 | 441 | if (msg) 442 | printf("%s:\n", msg); 443 | for (i = 0; i < len;) { 444 | unsigned int i16 = i % 16; 445 | unsigned char c = data[i]; 446 | sprintf(hex + (3 * i16), " %02x", c); 447 | ascii[i16] = (c < ' ' || c > '~') ? '.' : c; 448 | if (++i == len || i16 == 15) { 449 | ascii[i16 + 1] = '\0'; 450 | for (; i16 != 15; ++i16) 451 | strcat(hex, " "); 452 | printf("%04x:%s %s\n", offset, hex, ascii); 453 | offset = i; 454 | } 455 | } 456 | } 457 | /** 458 | * A helper that prints either true or false 459 | */ 460 | const char* print_bool(char value) 461 | { 462 | if (value) return bool_strings[1]; 463 | return bool_strings[0]; 464 | } 465 | /** 466 | * Prints out the current FT-X EEPROM Configuration 467 | */ 468 | static void ee_dump (struct eeprom_fields *ee) 469 | { 470 | unsigned int c; 471 | 472 | /* Misc Config */ 473 | printf(" Battery Charge Detect (BCD) Enabled = %s\n", 474 | print_bool(ee->bcd_enable)); 475 | printf(" Force Power Enable Signal on CBUS = %s\n", 476 | print_bool(ee->force_power_enable)); 477 | printf(" Deactivate Sleep in Battery Charge Mode = %s\n", 478 | print_bool(ee->deactivate_sleep)); 479 | 480 | printf(" External Oscillator Enabled = %s\n", print_bool(ee->ext_osc)); 481 | printf(" External Oscillator Feedback Resistor Enabled = %s\n", 482 | print_bool(ee->ext_osc_feedback_en)); 483 | printf(" CBUS pin allocated to VBUS Sense Mode = %s\n", 484 | print_bool(ee->vbus_sense_alloc)); 485 | printf(" Load Virtual COM Port (VCP) Drivers = %s\n", 486 | print_bool(ee->load_vcp)); 487 | 488 | /* USB VID/PID */ 489 | printf(" Vendor ID (VID) = 0x%04x\n", ee->usb_vid); 490 | printf(" Product ID (PID) = 0x%04x\n", ee->usb_pid); 491 | 492 | /* USB Release Number */ 493 | printf(" USB Version = USB%d.%d\n", ee->usb_release_major, 494 | ee->usb_release_minor); 495 | 496 | /* Max Power and Config */ 497 | printf(" Remote Wakeup by something other than USB = %s\n", 498 | print_bool(ee->remote_wakeup)); 499 | printf(" Self Powered = %s\n", print_bool(ee->self_powered)); 500 | printf(" Maximum Current Supported from USB = %dmA\n", 501 | 2 * ee->max_power); /* units of 2mA */ 502 | 503 | /* Device and perhiperal control */ 504 | printf(" Pins Pulled Down on USB Suspend = %s\n", 505 | print_bool(ee->suspend_pull_down)); 506 | printf(" Indicate USB Serial Number Available = %s\n", 507 | print_bool(ee->serial_number_avail)); 508 | 509 | printf(" FT1248\n"); 510 | printf("-------\n"); 511 | printf(" FT1248 Clock Polarity = %s\n", 512 | ee->ft1248_cpol ? "Active High":"Active Low"); 513 | printf(" FT1248 Bit Order = %s\n", 514 | ee->ft1248_bord ? "LSB to MSB":"MSB to LSB"); 515 | printf(" FT1248 Flow Control Enabled = %s\n", 516 | print_bool(ee->ft1248_flow_control)); 517 | 518 | printf(" RS232\n"); 519 | printf("-------\n"); 520 | printf(" Invert TXD = %s\n", print_bool(ee->invert_txd)); 521 | printf(" Invert RXD = %s\n", print_bool(ee->invert_rxd)); 522 | printf(" Invert RTS = %s\n", print_bool(ee->invert_rts)); 523 | printf(" Invert CTS = %s\n", print_bool(ee->invert_cts)); 524 | printf(" Invert DTR = %s\n", print_bool(ee->invert_dtr)); 525 | printf(" Invert DSR = %s\n", print_bool(ee->invert_dsr)); 526 | printf(" Invert DCD = %s\n", print_bool(ee->invert_dcd)); 527 | printf(" Invert RI = %s\n", print_bool(ee->invert_ri)); 528 | 529 | printf(" RS485\n"); 530 | printf("-------\n"); 531 | printf(" RS485 Echo Suppression Enabled = %s\n", 532 | print_bool(ee->rs485_echo_suppress)); 533 | 534 | /* DBUS & CBUS Control */ 535 | printf(" DBUS Drive Strength = %dmA\n", 4 * (ee->dbus_drive_strength+1)); 536 | printf(" DBUS Slow Slew Mode = %u\n", ee->dbus_slow_slew); 537 | printf(" DBUS Schmitt Trigger = %u\n", ee->dbus_schmitt); 538 | printf(" CBUS Drive Strength = %dmA\n", 4 * (ee->cbus_drive_strength+1)); 539 | printf(" CBUS Slow Slew Mode = %u\n", ee->cbus_slow_slew); 540 | printf(" CBUS Schmitt Trigger = %u\n", ee->cbus_schmitt); 541 | 542 | /* Manufacturer, Product and Serial Number string */ 543 | printf(" Manufacturer = %s\n", ee->manufacturer_string); 544 | printf(" Product = %s\n", ee->product_string); 545 | printf(" Serial Number = %s\n", ee->serial_string); 546 | 547 | /* I2C */ 548 | printf(" I2C\n"); 549 | printf("-------\n"); 550 | printf(" I2C Slave Address = %d \n", ee->i2c_slave_addr); 551 | printf(" I2C Device ID = %d \n", ee->i2c_device_id); 552 | printf(" I2C Schmitt Triggers Disabled = %s\n", 553 | print_bool(ee->disable_i2c_schmitt)); 554 | 555 | /* CBUS */ 556 | printf(" CBUS\n"); 557 | printf("-------\n"); 558 | for (c = 0; c < CBUS_COUNT; ++c) { 559 | /* Check this is a valid cbus mode */ 560 | if (ee->cbus[c] < _cbus_mode_end) { 561 | printf(" CBUS%u = %s\n", c, cbus_mode_strings[ee->cbus[c]]); 562 | } else { 563 | printf(" CBUS%u = %d\n", c, ee->cbus[c]); 564 | } 565 | } 566 | } 567 | 568 | /* ------------ Cyclic Redundancy Check ------------ */ 569 | 570 | static unsigned short calc_crc_ftx (void *addr) 571 | { 572 | unsigned int i; 573 | unsigned short crc = 0xaaaa; 574 | unsigned char* d8 = addr; 575 | 576 | /* Word Addresses 0x0 - 0x11 inclusive */ 577 | for (i = 0; i < 0x12*2; i += 2) { 578 | crc ^= d8[i] | (d8[i+1] << 8); 579 | crc = (crc << 1) | (crc >> 15); 580 | } 581 | 582 | /* Word Addresses 0x12 - 0x39 are ignored */ 583 | 584 | /* Word Addresses 0x40 - 0x7E inclusive */ 585 | for (i = 0x40*2; i < 0x7F*2; i += 2) { 586 | crc ^= d8[i] | (d8[i+1] << 8); 587 | crc = (crc << 1) | (crc >> 15); 588 | } 589 | 590 | /* Word Address 0x7E is ignored */ 591 | /* Word Address 0x7F is the checksum */ 592 | 593 | return crc; 594 | } 595 | static unsigned short verify_crc (void *addr, int len) 596 | { 597 | unsigned short crc = calc_crc_ftx(addr); 598 | unsigned char *d8 = addr; 599 | unsigned short actual = d8[len-2] | (d8[len-1] << 8); 600 | 601 | if (crc != actual) { 602 | fprintf(stderr, "Bad CRC: crc=0x%04x, actual=0x%04x\n", crc, actual); 603 | if (ignore_crc_error == 0) { 604 | exit(EINVAL); 605 | } else { 606 | fprintf(stderr, "Ignoring CRC error\n"); 607 | } 608 | } 609 | if (verbose) { printf("CRC: Okay (0x%04x)\n", crc); } 610 | return crc; 611 | } 612 | static unsigned short update_crc (void *addr, int len) 613 | { 614 | unsigned short crc = calc_crc_ftx(addr); 615 | unsigned char *d8 = addr; 616 | 617 | d8[len-2] = crc; 618 | d8[len-1] = crc >> 8; 619 | return crc; 620 | } 621 | 622 | /* ------------ EEPROM Encoding and Decoding ------------ */ 623 | 624 | /** 625 | * Checks that the strings aren't too big to fit in the string 626 | * descriptors memory. 627 | */ 628 | static int ee_check_strings(char* man, char* prod, char* ser) 629 | { 630 | /* if the strings are too long */ 631 | if (strlen(man) + strlen(prod) + strlen(ser) > 96) return 1; 632 | return 0; 633 | } 634 | /** 635 | * Inserts a string into a buffer to be written out to the eeprom 636 | */ 637 | static void ee_encode_string(char* str, unsigned char *ptr_field, 638 | unsigned char* len_field, unsigned char* eeprom, 639 | unsigned char* string_addr) 640 | { 641 | int original_length = strlen(str), length; 642 | 643 | if (use_8b_strings) { 644 | length = original_length; 645 | } else { 646 | length = strlen(str)*2 + 2; 647 | char* ftstr = malloc(length); 648 | 649 | /* Encode a FT Prog compatible string */ 650 | ftstr[0] = length; 651 | ftstr[1] = 3; 652 | 653 | int in, out; 654 | for (in = 0, out = 2; out < length; in++, out += 2) { 655 | ftstr[out] = str[in]; 656 | ftstr[out+1] = 0; 657 | } 658 | 659 | str = ftstr; 660 | } 661 | 662 | /* Copy the strings to the string area */ 663 | memcpy(eeprom + *string_addr, str, length); 664 | 665 | /* Write the the two metadata fields */ 666 | *ptr_field = *string_addr; 667 | *len_field = length; 668 | /* Move the string area address forward */ 669 | *string_addr += *len_field; 670 | } 671 | /** 672 | * Encodes an eeprom_fields object into a buffer ready to be written 673 | * out to the eeprom. 674 | */ 675 | static unsigned short ee_encode (unsigned char *eeprom, int len, 676 | struct eeprom_fields *ee) 677 | { 678 | int c; unsigned char string_desc_addr = 0xA0; 679 | 680 | memset(eeprom, 0, len); 681 | 682 | /* Misc Config */ 683 | if (ee->bcd_enable) eeprom[0x00] |= bcd_enable; 684 | if (ee->force_power_enable) eeprom[0x00] |= force_power_enable; 685 | if (ee->deactivate_sleep) eeprom[0x00] |= deactivate_sleep; 686 | if (ee->rs485_echo_suppress) eeprom[0x00] |= rs485_echo_suppress; 687 | if (ee->ext_osc) eeprom[0x00] |= ext_osc; 688 | if (ee->ext_osc_feedback_en) eeprom[0x00] |= ext_osc_feedback_en; 689 | if (ee->vbus_sense_alloc) eeprom[0x00] |= vbus_sense_alloc; 690 | if (ee->load_vcp) eeprom[0x00] |= load_vcp; 691 | 692 | /* USB VID/PID */ 693 | eeprom[0x02] = ee->usb_vid & 0xFF; 694 | eeprom[0x03] = (ee->usb_vid >> 8) & 0xFF; 695 | eeprom[0x04] = ee->usb_pid & 0xFF; 696 | eeprom[0x05] = (ee->usb_pid >> 8) & 0xFF; 697 | 698 | /* USB Release Number */ 699 | eeprom[0x07] = ee->usb_release_major; 700 | eeprom[0x06] = ee->usb_release_minor; 701 | 702 | /* Max Power and Config */ 703 | if (ee->remote_wakeup) eeprom[0x08] |= remote_wakeup; 704 | if (ee->self_powered) eeprom[0x08] |= self_powered; 705 | eeprom[0x08] |= 0x80; /* This is a reserved bit! */ 706 | eeprom[0x09] = ee->max_power; /* Units of 2mA */ 707 | 708 | /* Device and perhiperal control */ 709 | if (ee->suspend_pull_down) eeprom[0x0A] |= suspend_pull_down; 710 | if (ee->serial_number_avail) eeprom[0x0A] |= serial_number_avail; 711 | if (ee->ft1248_cpol) eeprom[0x0A] |= ft1248_cpol; 712 | if (ee->ft1248_bord) eeprom[0x0A] |= ft1248_bord; 713 | if (ee->ft1248_flow_control) eeprom[0x0A] |= ft1248_flow_control; 714 | if (ee->disable_i2c_schmitt) eeprom[0x0A] |= disable_i2c_schmitt; 715 | if (ee->invert_txd) eeprom[0x0B] |= invert_txd; 716 | if (ee->invert_rxd) eeprom[0x0B] |= invert_rxd; 717 | if (ee->invert_rts) eeprom[0x0B] |= invert_rts; 718 | if (ee->invert_cts) eeprom[0x0B] |= invert_cts; 719 | if (ee->invert_dtr) eeprom[0x0B] |= invert_dtr; 720 | if (ee->invert_dsr) eeprom[0x0B] |= invert_dsr; 721 | if (ee->invert_dcd) eeprom[0x0B] |= invert_dcd; 722 | if (ee->invert_ri) eeprom[0x0B] |= invert_ri; 723 | 724 | /* DBUS & CBUS Control */ 725 | eeprom[0x0C] |= (ee->dbus_drive_strength & dbus_drive_strength); 726 | if (ee->dbus_slow_slew) eeprom[0x0C] |= dbus_slow_slew; 727 | if (ee->dbus_schmitt) eeprom[0x0C] |= dbus_schmitt; 728 | eeprom[0x0C] |= (ee->cbus_drive_strength << 4) & cbus_drive_strength; 729 | if (ee->cbus_slow_slew) eeprom[0x0C] |= cbus_slow_slew; 730 | if (ee->cbus_schmitt) eeprom[0x0C] |= cbus_schmitt; 731 | 732 | /* eeprom[0x0D] is unused */ 733 | 734 | /* Manufacturer, Product and Serial Number string */ 735 | if (ee_check_strings(ee->manufacturer_string, ee->product_string, 736 | ee->serial_string)) { 737 | fprintf(stderr, 738 | "Failed to encode, strings too long to fit in string memory area!\n"); 739 | exit(EINVAL); 740 | } 741 | ee_encode_string(ee->manufacturer_string, &eeprom[0x0E], &eeprom[0x0F], 742 | eeprom, &string_desc_addr); 743 | ee_encode_string(ee->product_string, &eeprom[0x10], &eeprom[0x11], 744 | eeprom, &string_desc_addr); 745 | ee_encode_string(ee->serial_string, &eeprom[0x12], &eeprom[0x13], 746 | eeprom, &string_desc_addr); 747 | 748 | /* I2C */ 749 | eeprom[0x14] = ee->i2c_slave_addr & 0xFF; 750 | eeprom[0x15] = (ee->i2c_slave_addr >> 8) & 0xFF; 751 | eeprom[0x16] = ee->i2c_device_id & 0xFF; 752 | eeprom[0x17] = (ee->i2c_device_id >> 8) & 0xFF; 753 | eeprom[0x18] = (ee->i2c_device_id >> 16) & 0xFF; 754 | 755 | /* CBUS */ 756 | for (c = 0; c < CBUS_COUNT; c++) { 757 | eeprom[0x1A + c] = ee->cbus[c]; 758 | } 759 | 760 | /* User Memory Space */ 761 | memcpy(&eeprom[0x24], ee->user_mem, 92); 762 | /* Factory Configuration Values */ 763 | memcpy(&eeprom[0x80], ee->factory_config, 32); 764 | 765 | return update_crc(eeprom, len); 766 | } 767 | /** 768 | * Extracts a string from the a buffer read from eeprom 769 | */ 770 | static char* ee_decode_string(unsigned char *eeprom, unsigned char* ptr, 771 | unsigned char len) 772 | { 773 | char* str = malloc(len+1); 774 | 775 | if (str != NULL) { 776 | /* Copy the string from the EEPROM memory */ 777 | memcpy(str, ptr, len); 778 | 779 | /* Decode strings written by FT Prog correctly */ 780 | if (use_8b_strings) { 781 | str[len] = '\0'; 782 | } else { 783 | 784 | /* Pick the actual ASCII characters out of the FT Prog encoded string */ 785 | int in, out; 786 | for (in = 2, out = 0; in < len; in += 2, out++) { 787 | str[out] = str[in]; 788 | } 789 | 790 | str[out] = '\0'; 791 | } 792 | } 793 | 794 | return str; 795 | } 796 | /* 797 | * Populates an eeprom_fields object from a buffer read from eeprom 798 | */ 799 | static void ee_decode (unsigned char *eeprom, int len, struct eeprom_fields *ee) 800 | { 801 | int c; 802 | 803 | /* Misc Config */ 804 | ee->bcd_enable = (eeprom[0x00] & bcd_enable); 805 | ee->force_power_enable = (eeprom[0x00] & force_power_enable); 806 | ee->deactivate_sleep = (eeprom[0x00] & deactivate_sleep); 807 | ee->rs485_echo_suppress = (eeprom[0x00] & rs485_echo_suppress); 808 | ee->ext_osc = (eeprom[0x00] & ext_osc); 809 | ee->ext_osc_feedback_en = (eeprom[0x00] & ext_osc_feedback_en); 810 | ee->vbus_sense_alloc = (eeprom[0x00] & vbus_sense_alloc); 811 | ee->load_vcp = (eeprom[0x00] & load_vcp); 812 | 813 | /* USB VID/PID */ 814 | ee->usb_vid = eeprom[0x02] | (eeprom[0x03] << 8); 815 | ee->usb_pid = eeprom[0x04] | (eeprom[0x05] << 8); 816 | 817 | /* USB Release Number */ 818 | ee->usb_release_major = eeprom[0x07]; 819 | ee->usb_release_minor = eeprom[0x06]; 820 | 821 | /* Max Power and Config */ 822 | ee->remote_wakeup = (eeprom[0x08] & remote_wakeup); 823 | ee->self_powered = (eeprom[0x08] & self_powered); 824 | ee->max_power = eeprom[0x09]; /* Units of 2mA */ 825 | 826 | /* Device and perhiperal control */ 827 | ee->suspend_pull_down = (eeprom[0x0A] & suspend_pull_down); 828 | ee->serial_number_avail = (eeprom[0x0A] & serial_number_avail); 829 | ee->ft1248_cpol = (eeprom[0x0A] & ft1248_cpol); 830 | ee->ft1248_bord = (eeprom[0x0A] & ft1248_bord); 831 | ee->ft1248_flow_control = (eeprom[0x0A] & ft1248_flow_control); 832 | ee->disable_i2c_schmitt = (eeprom[0x0A] & disable_i2c_schmitt); 833 | ee->invert_txd = (eeprom[0x0B] & invert_txd); 834 | ee->invert_rxd = (eeprom[0x0B] & invert_rxd); 835 | ee->invert_rts = (eeprom[0x0B] & invert_rts); 836 | ee->invert_cts = (eeprom[0x0B] & invert_cts); 837 | ee->invert_dtr = (eeprom[0x0B] & invert_dtr); 838 | ee->invert_dsr = (eeprom[0x0B] & invert_dsr); 839 | ee->invert_dcd = (eeprom[0x0B] & invert_dcd); 840 | ee->invert_ri = (eeprom[0x0B] & invert_ri); 841 | 842 | /* DBUS & CBUS Control */ 843 | ee->dbus_drive_strength = (eeprom[0x0C] & dbus_drive_strength); 844 | ee->dbus_slow_slew = (eeprom[0x0C] & dbus_slow_slew); 845 | ee->dbus_schmitt = (eeprom[0x0C] & dbus_schmitt); 846 | ee->cbus_drive_strength = (eeprom[0x0C] & cbus_drive_strength) >> 4; 847 | ee->cbus_slow_slew = (eeprom[0x0C] & cbus_slow_slew); 848 | ee->cbus_schmitt = (eeprom[0x0C] & cbus_schmitt); 849 | 850 | /* eeprom[0x0D] is unused */ 851 | 852 | /* Manufacturer, Product and Serial Number string */ 853 | ee->manufacturer_string = ee_decode_string(eeprom, 854 | eeprom+eeprom[0x0E], eeprom[0x0F]); 855 | ee->product_string = ee_decode_string(eeprom, 856 | eeprom+eeprom[0x10], eeprom[0x11]); 857 | ee->serial_string = ee_decode_string(eeprom, 858 | eeprom+eeprom[0x12], eeprom[0x13]); 859 | 860 | /* I2C */ 861 | ee->i2c_slave_addr = eeprom[0x14] | (eeprom[0x15] << 8); 862 | ee->i2c_device_id = eeprom[0x16] | (eeprom[0x17] << 8) | (eeprom[0x18] << 16); 863 | 864 | /* CBUS */ 865 | for (c = 0; c < CBUS_COUNT; c++) { 866 | ee->cbus[c] = eeprom[0x1A + c]; 867 | } 868 | 869 | /* User Memory Space */ 870 | memcpy(ee->user_mem, &eeprom[0x24], 92); 871 | /* Factory Configuration Values */ 872 | memcpy(ee->factory_config, &eeprom[0x80], 32); 873 | } 874 | 875 | /* ------------ Help ------------ */ 876 | 877 | static const char *myname; 878 | 879 | /** 880 | * Prints a human-readable expression showing all the possibilities 881 | * for an option. 882 | */ 883 | static void print_options(FILE *fp, const char** options) 884 | { 885 | int j; 886 | 887 | fprintf(fp, " ["); 888 | for (j = 0; options[j];) { 889 | fprintf(fp, "%s", options[j]); 890 | if (options[++j]) 891 | fprintf(fp, "|"); 892 | } 893 | fprintf(fp, "]"); 894 | } 895 | /* 896 | * Prints a human readable help text. 897 | */ 898 | static void show_help (FILE *fp) 899 | { 900 | int i; 901 | 902 | fprintf(fp, "\nUsage: %s [ ]..\n", myname); 903 | fprintf(fp, "\nwhere must be any of:\n"); 904 | 905 | for (i = 0; arg_type_strings[i]; ++i) { /* For each argument */ 906 | /* Get the help string */ 907 | const char *val = arg_type_help[i]; 908 | /* Print its name */ 909 | fprintf(fp, " %s", arg_type_strings[i]); 910 | 911 | if (val) { /* If there is a help string */ 912 | if (strcmp(val, "[cbus]") == 0) { 913 | fprintf(fp, " [1..%d]", CBUS_COUNT); 914 | print_options(fp, cbus_mode_strings); 915 | } else if (strcmp(val, "[invert]") == 0) { 916 | print_options(fp, rs232_strings); 917 | } else if (strcmp(val, "dbus_cfg") == 0) { 918 | print_options(fp, d_cbus_config_strings); 919 | } else if (strcmp(val, "cbus_cfg") == 0) { 920 | print_options(fp, d_cbus_config_strings); 921 | } else { 922 | fprintf(fp, " %s", val); 923 | } 924 | } 925 | fputc('\n', fp); 926 | } 927 | fputc('\n', fp); 928 | } 929 | 930 | /* ------------ EEPROM Reading and Writing ------------ */ 931 | 932 | #ifdef USE_LIBFTDI1 933 | static int ee_write(unsigned char *eeprom, int len) 934 | { 935 | if (ftdi_set_eeprom_buf(&ftdi, eeprom, len) != 0) 936 | exit(EIO); 937 | 938 | if (ftdi_write_eeprom(&ftdi) != 0) 939 | exit(EIO); 940 | 941 | return 0; 942 | } 943 | #else 944 | static int ee_prepare_write(void) 945 | { 946 | unsigned short status; 947 | int ret; 948 | 949 | /* These commands were traced while running MProg */ 950 | if ((ret = ftdi_usb_reset(&ftdi)) != 0) { return ret; } 951 | if ((ret = ftdi_poll_modem_status(&ftdi, &status)) != 0) { return ret; } 952 | if ((ret = ftdi_set_latency_timer(&ftdi, 0x77)) != 0) { return ret; } 953 | 954 | return 0; 955 | } 956 | static int ee_write(unsigned char *eeprom, int len) 957 | { 958 | int i; 959 | 960 | if (ee_prepare_write()) { 961 | fprintf(stderr, "ee_prepare_write() failed: %s\n", 962 | ftdi_get_error_string(&ftdi)); 963 | exit(EIO); 964 | } 965 | 966 | for (i = 0; i < len/2; i++) { 967 | if (ftdi_write_eeprom_location(&ftdi, i, 968 | eeprom[i*2] | (eeprom[(i*2)+1] << 8))) { 969 | fprintf(stderr, "ftdi_write_eeprom_location() failed: %s\n", 970 | ftdi_get_error_string(&ftdi)); 971 | exit(EIO); 972 | } 973 | } 974 | 975 | return 0; 976 | } 977 | #endif 978 | 979 | static unsigned short ee_read_and_verify (unsigned char *eeprom, int len) 980 | { 981 | #ifdef USE_LIBFTDI1 982 | if (ftdi_read_eeprom(&ftdi) != 0) 983 | exit(EIO); 984 | 985 | if (ftdi_get_eeprom_buf(&ftdi, eeprom, len) != 0) 986 | exit(EIO); 987 | 988 | if (ftdi_eeprom_build(&ftdi) < 0) 989 | exit(EIO); 990 | #else 991 | int i; 992 | 993 | for (i = 0; i < len/2; i++) { 994 | if (ftdi_read_eeprom_location(&ftdi, i, (void*)(eeprom + (i*2)))) { 995 | fprintf(stderr, "ftdi_read_eeprom_location() failed: %s\n", 996 | ftdi_get_error_string(&ftdi)); 997 | exit(EIO); 998 | } 999 | } 1000 | #endif 1001 | 1002 | return verify_crc(eeprom, len); 1003 | } 1004 | 1005 | /* ------------ Parsing Command Line ------------ */ 1006 | 1007 | static int match_arg (const char *arg, const char **possibles) 1008 | { 1009 | int i; 1010 | 1011 | for (i = 0; possibles[i]; ++i) { 1012 | if (0 == strcasecmp(possibles[i], arg)) 1013 | return i; 1014 | } 1015 | fprintf(stderr, "unrecognized arg: \"%s\"\n", arg); 1016 | exit(EINVAL); 1017 | return -1; /* never reached */ 1018 | } 1019 | static unsigned long unsigned_val (const char *arg, unsigned long max) 1020 | { 1021 | unsigned long val; 1022 | 1023 | errno = 0; 1024 | val = strtoul(arg, NULL, 0); 1025 | if (errno || val > max) { 1026 | fprintf(stderr, "%s: bad value (max=0x%lx)\n", arg, max); 1027 | exit(EINVAL); 1028 | } 1029 | return val; 1030 | } 1031 | 1032 | 1033 | 1034 | static int process_args (int argc, char *argv[], struct eeprom_fields *ee) 1035 | { 1036 | int i; int c; 1037 | int j; 1038 | 1039 | for (i = 1; i < argc;) { 1040 | int arg; 1041 | arg = match_arg(argv[i++], arg_type_strings); 1042 | 1043 | /* detect missing arguments and handle errors */ 1044 | int expected_args = 0; 1045 | for (j = 0; j < (sizeof(req_info) / sizeof(req_info[0])); j++) { 1046 | if (req_info[j].t == arg) expected_args = req_info[j].number; 1047 | } 1048 | 1049 | int remaining_args = (argc - i); 1050 | if (remaining_args < expected_args) { 1051 | fprintf(stderr, "Missing arguments to %s. " 1052 | "Expected %i but only %i args remain\n", 1053 | argv[i - 1], expected_args, remaining_args); 1054 | fprintf(stderr, "type %s --help for more information\n", argv[0]); 1055 | return -1; 1056 | } 1057 | 1058 | switch (arg) { 1059 | case arg_help: 1060 | show_help(stdout); 1061 | exit(1); 1062 | case arg_dump: 1063 | break; 1064 | case arg_ignore_crc_error: 1065 | ignore_crc_error = 1; 1066 | break; 1067 | case arg_erase_eeprom: 1068 | erase_eeprom = 1; 1069 | break; 1070 | case arg_verbose: 1071 | verbose = 1; 1072 | break; 1073 | /* File operations */ 1074 | case arg_save: 1075 | save_path = argv[i++]; 1076 | break; 1077 | case arg_restore: 1078 | restore_path = argv[i++]; 1079 | break; 1080 | case arg_8b_strings: 1081 | use_8b_strings = true; 1082 | break; 1083 | case arg_cbus: 1084 | c = match_arg(argv[i++], cbus_strings); 1085 | ee->cbus[c] = match_arg(argv[i++], cbus_mode_strings); 1086 | break; 1087 | case arg_cbus_config: 1088 | c = match_arg(argv[i++], d_cbus_config_strings); 1089 | if(c < 4) { 1090 | ee->cbus_drive_strength = c; 1091 | } else if (4 == c) { 1092 | ee->cbus_slow_slew = 1; 1093 | } else if(6 == c) { 1094 | ee->cbus_schmitt = 1; 1095 | } 1096 | break; 1097 | case arg_dbus_config: 1098 | c = match_arg(argv[i++], d_cbus_config_strings); 1099 | if( c < 4) 1100 | ee->dbus_drive_strength = c; 1101 | else if( 4 == c){ 1102 | ee->dbus_slow_slew=1; 1103 | } else if( 6 == c) 1104 | ee->dbus_schmitt=1; 1105 | break; 1106 | case arg_invert: 1107 | switch(match_arg(argv[i++], rs232_strings)) { 1108 | case 0: ee->invert_txd = !ee->invert_txd; break; 1109 | case 1: ee->invert_rxd = !ee->invert_rxd; break; 1110 | case 2: ee->invert_rts = !ee->invert_rts; break; 1111 | case 3: ee->invert_cts = !ee->invert_cts; break; 1112 | case 4: ee->invert_dtr = !ee->invert_dtr; break; 1113 | case 5: ee->invert_dsr = !ee->invert_dsr; break; 1114 | case 6: ee->invert_dcd = !ee->invert_dcd; break; 1115 | case 7: ee->invert_ri = !ee->invert_ri; break; 1116 | } 1117 | break; 1118 | /* Strings */ 1119 | case arg_manufacturer: 1120 | ee->manufacturer_string = argv[i++]; 1121 | break; 1122 | case arg_product: 1123 | ee->product_string = argv[i++]; 1124 | break; 1125 | case arg_new_serno: 1126 | ee->serial_string = argv[i++]; 1127 | ee->serial_number_avail = strlen(ee->serial_string) > 0; 1128 | break; 1129 | case arg_max_bus_power: 1130 | ee->max_power = unsigned_val(argv[i++], 0x1ff) / 2; 1131 | break; 1132 | case arg_self_powered: 1133 | ee->self_powered = match_arg(argv[i++], bool_strings) & 1; 1134 | break; 1135 | case arg_suspend_pull_down: 1136 | ee->suspend_pull_down = match_arg(argv[i++], bool_strings) & 1; 1137 | break; 1138 | case arg_load_vcp: 1139 | ee->load_vcp = match_arg(argv[i++], bool_strings) & 1; 1140 | break; 1141 | case arg_remote_wakeup: 1142 | ee->remote_wakeup = match_arg(argv[i++], bool_strings) & 1; 1143 | break; 1144 | /* FT1248 */ 1145 | case arg_ft1248_cpol: 1146 | ee->ft1248_cpol = match_arg(argv[i++], bool_strings) & 1; 1147 | break; 1148 | case arg_ft1248_bord: 1149 | ee->ft1248_bord = match_arg(argv[i++], bool_strings) & 1; 1150 | break; 1151 | case arg_ft1248_flow_control: 1152 | ee->ft1248_flow_control = match_arg(argv[i++], bool_strings) & 1; 1153 | break; 1154 | case arg_i2c_schmitt: 1155 | /* The command line arg is enabled +ve, the eeprom is disabled +ve */ 1156 | ee->disable_i2c_schmitt = !(match_arg(argv[i++], bool_strings) & 1); 1157 | break; 1158 | /* I2C */ 1159 | case arg_i2c_slave_address: 1160 | ee->i2c_slave_addr = unsigned_val(argv[i++], 0xffff); 1161 | break; 1162 | case arg_i2c_device_id: 1163 | ee->i2c_device_id = unsigned_val(argv[i++], 0xffff); 1164 | break; 1165 | /* RS485 */ 1166 | case arg_rs485_echo_suppression: 1167 | ee->rs485_echo_suppress = match_arg(argv[i++], bool_strings) & 1; 1168 | break; 1169 | /* VID, PID, Ser No. */ 1170 | case arg_old_vid: 1171 | ee->old_vid = unsigned_val(argv[i++], 0xffff); 1172 | break; 1173 | case arg_old_pid: 1174 | ee->old_pid = unsigned_val(argv[i++], 0xffff); 1175 | break; 1176 | case arg_old_serno: 1177 | ee->old_serno = argv[i++]; 1178 | break; 1179 | case arg_new_vid: 1180 | ee->usb_vid = unsigned_val(argv[i++], 0xffff); 1181 | break; 1182 | case arg_new_pid: 1183 | ee->usb_pid = unsigned_val(argv[i++], 0xffff); 1184 | break; 1185 | } 1186 | } 1187 | 1188 | return 0; 1189 | } 1190 | 1191 | /* ------------ File Save / Restore ------------ */ 1192 | 1193 | static void save_eeprom_to_file (const char *path, void *eeprom, int len) 1194 | { 1195 | int count, fd = open(path, O_CREAT|O_WRONLY|O_TRUNC, 0644); 1196 | 1197 | if (fd == -1) { 1198 | int err = errno; 1199 | perror(path); 1200 | exit(err); 1201 | } 1202 | count = write(fd, eeprom, len); 1203 | if (count < 0) { 1204 | int err = errno; 1205 | perror(path); 1206 | exit(err); 1207 | } 1208 | close(fd); 1209 | if (count != len) { 1210 | fprintf(stderr, "%s: wrong size, wrote %d/%d bytes\n", path, count, len); 1211 | exit(EINVAL); 1212 | } 1213 | printf("%s: wrote %d bytes\n", path, count); 1214 | } 1215 | 1216 | static void restore_eeprom_from_file (const char *path, void *eeprom, int len, 1217 | int max) 1218 | { 1219 | int count, fd = open(path, O_RDONLY); 1220 | 1221 | if (fd == -1) { 1222 | int err = errno; 1223 | perror(path); 1224 | exit(err); 1225 | } 1226 | count = read(fd, eeprom, max); 1227 | if (count < 0) { 1228 | int err = errno; 1229 | perror(path); 1230 | exit(err); 1231 | } 1232 | close(fd); 1233 | if (count != len ) { 1234 | fprintf(stderr, "%s: wrong size, read %d/%d bytes\n", path, count, len); 1235 | exit(EINVAL); 1236 | } 1237 | printf("%s: read %d bytes\n", path, count); 1238 | verify_crc(eeprom, len); 1239 | } 1240 | 1241 | /* ------------ Main ------------ */ 1242 | 1243 | int main (int argc, char *argv[]) 1244 | { 1245 | const char *slash; 1246 | unsigned char old[0x100] = {0,}, new[0x100] = {0,}; 1247 | unsigned short new_crc; 1248 | struct eeprom_fields ee; 1249 | /* We only deal with the first 256 bytes and ignore the user memory space */ 1250 | unsigned int len = 0x100; 1251 | 1252 | myname = argv[0]; 1253 | slash = strrchr(myname, '/'); 1254 | if (slash) 1255 | myname = slash + 1; 1256 | 1257 | printf("\n%s: version %s\n", myname, MYVERSION); 1258 | printf("Modified for the FT-X series by Richard Meadows\n\n"); 1259 | printf("Based upon:\n"); 1260 | printf("ft232r_prog: version 1.23, by Mark Lord.\n"); 1261 | if (argc < 2) { 1262 | show_help(stdout); 1263 | exit(0); 1264 | } 1265 | 1266 | ftdi_init(&ftdi); 1267 | atexit(&do_deinit); 1268 | 1269 | memset(&ee, 0, sizeof(ee)); 1270 | ee.old_vid = 0x0403; /* default; override with --old_vid arg */ 1271 | ee.old_pid = 0x6015; /* default; override with --old_pid arg */ 1272 | if (process_args(argc, argv, &ee)) { /* handle --help and --old-* args */ 1273 | return -1; 1274 | } 1275 | 1276 | if (ftdi_usb_open_desc(&ftdi, ee.old_vid, ee.old_pid, NULL, ee.old_serno)) { 1277 | fprintf(stderr, "ftdi_usb_open() failed for %04x:%04x:%s %s\n", 1278 | ee.old_vid, ee.old_pid, 1279 | ee.old_serno ? ee.old_serno : "", ftdi_get_error_string(&ftdi)); 1280 | exit(ENODEV); 1281 | } 1282 | atexit(&do_close); 1283 | 1284 | /* First, read the original eeprom from the device */ 1285 | (void) ee_read_and_verify(old, len); 1286 | if (verbose) dumpmem("existing eeprom", old, len); 1287 | 1288 | /* Save old contents to a file, if requested (--save) */ 1289 | if (save_path) 1290 | save_eeprom_to_file(save_path, old, len); 1291 | 1292 | /* Restore contents from a file, if requested (--restore) */ 1293 | if (restore_path) { 1294 | restore_eeprom_from_file(restore_path, new, len, sizeof(new)); 1295 | if (verbose) dumpmem(restore_path, new, len); 1296 | } 1297 | 1298 | /* TODO: It'd be nice to check we can restore the EEPROM.. */ 1299 | 1300 | /* Decode eeprom contents into ee struct */ 1301 | ee_decode(old, len, &ee); 1302 | 1303 | /* process args, and dump new settings */ 1304 | process_args(argc, argv, &ee); /* Handle value-change args */ 1305 | ee_dump(&ee); 1306 | 1307 | /* Build new eeprom image */ 1308 | if (erase_eeprom == 0) { 1309 | new_crc = ee_encode(new, len, &ee); 1310 | } else { 1311 | memset(new, 0xff, 0x100); 1312 | new_crc = 0xFFFF; 1313 | } 1314 | 1315 | /* If different from original, then write it back to the device */ 1316 | if (0 == memcmp(old, new, len)) { 1317 | printf("No change from existing eeprom contents.\n"); 1318 | } else { 1319 | if (verbose) { dumpmem("new eeprom", new, len); } 1320 | 1321 | if (erase_eeprom == 0) { 1322 | printf("Rewriting eeprom with new contents.\n"); 1323 | } else { 1324 | printf("Erasing EEPROM\n"); 1325 | } 1326 | 1327 | printf("Continue? [y|n]:"); 1328 | if (getc(stdin) == 'y') { 1329 | ee_write(new, len); 1330 | 1331 | /* Read it back again, and check for differences */ 1332 | if (ee_read_and_verify(new, len) != new_crc ) { 1333 | fprintf(stderr, "Readback test failed, results may be botched\n"); 1334 | exit(EINVAL); 1335 | } 1336 | if (erase_eeprom == 1) { printf("Erase done\n"); } 1337 | 1338 | /* Reset the device to force it to load the new settings */ 1339 | ftdi_usb_reset(&ftdi); 1340 | } 1341 | } 1342 | 1343 | return 0; 1344 | } 1345 | --------------------------------------------------------------------------------