Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
559a403836 | |||
baf5a895b5 | |||
f4952aaa63 | |||
9f3781a3eb | |||
df56c54697 | |||
c5f5a74783 | |||
964c933bf3 | |||
5bdbb430e7 | |||
0416a2f536 | |||
cde9bf0a5b | |||
05a4533cd2 | |||
7e7e50ad84 | |||
1ff13ca69e | |||
fc61d39288 | |||
b60a0fe735 | |||
eee017dedf | |||
1418971648 | |||
5ba6367a37 | |||
f4ef7a7b1e | |||
dd4ca2cb33 | |||
|
f58adea5d6 | ||
bc5d45359a | |||
31d65c76d7 | |||
|
89b3251dde | ||
df9fb6c077 | |||
5e4b562608 | |||
ca2a0a99ae |
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,4 +3,5 @@
|
|||||||
*.bin
|
*.bin
|
||||||
*.hex
|
*.hex
|
||||||
*.lst
|
*.lst
|
||||||
|
*.lss
|
||||||
*.map
|
*.map
|
||||||
|
262
LICENSE
Normal file
262
LICENSE
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
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.
|
||||||
|
|
||||||
|
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
|
132
Makefile
132
Makefile
@ -1,65 +1,103 @@
|
|||||||
PRG = twiboot
|
CC := avr-gcc
|
||||||
OBJ = main.o
|
LD := avr-ld
|
||||||
MCU_TARGET = atmega88
|
OBJCOPY := avr-objcopy
|
||||||
OPTIMIZE = -Os
|
OBJDUMP := avr-objdump
|
||||||
|
SIZE := avr-size
|
||||||
|
|
||||||
ifeq ($(MCU_TARGET), atmega8)
|
TARGET = twiboot
|
||||||
BOOTLOADER_START=0x1C00
|
SOURCE = $(wildcard *.c)
|
||||||
|
|
||||||
|
# select MCU
|
||||||
|
MCU = attiny85
|
||||||
|
|
||||||
|
AVRDUDE_PROG := -c avr910 -b 115200 -P /dev/ttyUSB0
|
||||||
|
#AVRDUDE_PROG := -c dragon_isp -P usb
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(MCU), atmega8)
|
||||||
|
# atmega8:
|
||||||
|
# Fuse L: 0x84 (8Mhz internal RC-Osz., 2.7V BOD)
|
||||||
|
# Fuse H: 0xda (512 words bootloader)
|
||||||
AVRDUDE_MCU=m8
|
AVRDUDE_MCU=m8
|
||||||
endif
|
AVRDUDE_FUSES=lfuse:w:0x84:m hfuse:w:0xda:m
|
||||||
ifeq ($(MCU_TARGET), atmega88)
|
|
||||||
BOOTLOADER_START=0x1C00
|
BOOTLOADER_START=0x1C00
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(MCU), atmega88)
|
||||||
|
# atmega88:
|
||||||
|
# Fuse L: 0xc2 (8Mhz internal RC-Osz.)
|
||||||
|
# Fuse H: 0xdd (2.7V BOD)
|
||||||
|
# Fuse E: 0xfa (512 words bootloader)
|
||||||
AVRDUDE_MCU=m88
|
AVRDUDE_MCU=m88
|
||||||
|
AVRDUDE_FUSES=lfuse:w:0xc2:m hfuse:w:0xdd:m efuse:w:0xfa:m
|
||||||
|
|
||||||
|
BOOTLOADER_START=0x1C00
|
||||||
endif
|
endif
|
||||||
ifeq ($(MCU_TARGET), atmega168)
|
|
||||||
|
ifeq ($(MCU), atmega168)
|
||||||
|
# atmega168:
|
||||||
|
# Fuse L: 0xc2 (8Mhz internal RC-Osz.)
|
||||||
|
# Fuse H: 0xdd (2.7V BOD)
|
||||||
|
# Fuse E: 0xfa (512 words bootloader)
|
||||||
|
AVRDUDE_MCU=m168 -F
|
||||||
|
AVRDUDE_FUSES=lfuse:w:0xc2:m hfuse:w:0xdd:m efuse:w:0xfa:m
|
||||||
|
|
||||||
BOOTLOADER_START=0x3C00
|
BOOTLOADER_START=0x3C00
|
||||||
AVRDUDE_MCU=m168
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
DEFS = -DAPP_END=$(BOOTLOADER_START)
|
ifeq ($(MCU), atmega328p)
|
||||||
LIBS =
|
# atmega328p:
|
||||||
|
# Fuse L: 0xc2 (8Mhz internal RC-Osz.)
|
||||||
|
# Fuse H: 0xdc (512 words bootloader)
|
||||||
|
# Fuse E: 0xfd (2.7V BOD)
|
||||||
|
AVRDUDE_MCU=m328p -F
|
||||||
|
AVRDUDE_FUSES=lfuse:w:0xc2:m hfuse:w:0xdc:m efuse:w:0xfd:m
|
||||||
|
|
||||||
# Override is only needed by avr-lib build system.
|
BOOTLOADER_START=0x7C00
|
||||||
override CFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS)
|
endif
|
||||||
override LDFLAGS = -Wl,-Map,$(PRG).map,--section-start=.text=$(BOOTLOADER_START)
|
|
||||||
|
|
||||||
CC = avr-gcc
|
ifeq ($(MCU), attiny85)
|
||||||
OBJCOPY = avr-objcopy
|
# attiny85:
|
||||||
OBJDUMP = avr-objdump
|
# Fuse L: 0xe2 (8Mhz internal RC-Osz.)
|
||||||
SIZE = avr-size
|
# Fuse H: 0xdd (2.7V BOD)
|
||||||
|
# Fuse E: 0xfe (self programming enable)
|
||||||
|
AVRDUDE_MCU=t85
|
||||||
|
AVRDUDE_FUSES=lfuse:w:0xe2:m hfuse:w:0xdd:m efuse:w:0xfe:m
|
||||||
|
|
||||||
all: $(PRG).elf lst text
|
BOOTLOADER_START=0x1C00
|
||||||
$(SIZE) -x -A $(PRG).elf
|
CFLAGS_TARGET=-DUSE_CLOCKSTRETCH=1 -DVIRTUAL_BOOT_SECTION=1
|
||||||
|
endif
|
||||||
|
|
||||||
$(PRG).elf: $(OBJ)
|
# ---------------------------------------------------------------------------
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
|
|
||||||
|
CFLAGS = -pipe -g -Os -mmcu=$(MCU) -Wall -fdata-sections -ffunction-sections
|
||||||
|
CFLAGS += -Wa,-adhlns=$(*F).lst -DBOOTLOADER_START=$(BOOTLOADER_START) $(CFLAGS_TARGET)
|
||||||
|
LDFLAGS = -Wl,-Map,$(@:.elf=.map),--cref,--relax,--gc-sections,--section-start=.text=$(BOOTLOADER_START)
|
||||||
|
LDFLAGS += -nostartfiles
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
$(TARGET): $(TARGET).elf
|
||||||
|
@$(SIZE) -B -x --mcu=$(MCU) $<
|
||||||
|
|
||||||
|
$(TARGET).elf: $(SOURCE:.c=.o)
|
||||||
|
@echo " Linking file: $@"
|
||||||
|
@$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
|
||||||
|
@$(OBJDUMP) -h -S $@ > $(@:.elf=.lss)
|
||||||
|
@$(OBJCOPY) -j .text -j .data -O ihex $@ $(@:.elf=.hex)
|
||||||
|
@$(OBJCOPY) -j .text -j .data -O binary $@ $(@:.elf=.bin)
|
||||||
|
|
||||||
%.o: %.c $(MAKEFILE_LIST)
|
%.o: %.c $(MAKEFILE_LIST)
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
@echo " Building file: $<"
|
||||||
|
@$(CC) $(CFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf *.o $(PRG).lst $(PRG).map $(PRG).elf $(PRG).hex $(PRG).bin
|
rm -rf $(SOURCE:.c=.o) $(SOURCE:.c=.lst) $(addprefix $(TARGET), .elf .map .lss .hex .bin)
|
||||||
|
|
||||||
lst: $(PRG).lst
|
install: $(TARGET).elf
|
||||||
|
avrdude $(AVRDUDE_PROG) -p $(AVRDUDE_MCU) -U flash:w:$(<:.elf=.hex)
|
||||||
|
|
||||||
%.lst: %.elf
|
fuses:
|
||||||
$(OBJDUMP) -h -S $< > $@
|
avrdude $(AVRDUDE_PROG) -p $(AVRDUDE_MCU) $(patsubst %,-U %, $(AVRDUDE_FUSES))
|
||||||
|
|
||||||
text: hex bin
|
|
||||||
|
|
||||||
hex: $(PRG).hex
|
|
||||||
bin: $(PRG).bin
|
|
||||||
|
|
||||||
%.hex: %.elf
|
|
||||||
$(OBJCOPY) -j .text -j .data -O ihex $< $@
|
|
||||||
|
|
||||||
%.bin: %.elf
|
|
||||||
$(OBJCOPY) -j .text -j .data -O binary $< $@
|
|
||||||
|
|
||||||
install: text
|
|
||||||
avrdude -c dragon_isp -P usb -p $(AVRDUDE_MCU) -U flash:w:$(PRG).hex
|
|
||||||
|
|
||||||
#fuses:
|
|
||||||
# avrdude -c dragon_isp -P usb -p $(AVRDUDE_MCU) -U lfuse:w:0xc2:m
|
|
||||||
# avrdude -c dragon_isp -P usb -p $(AVRDUDE_MCU) -U hfuse:w:0xdd:m
|
|
||||||
# avrdude -c dragon_isp -P usb -p $(AVRDUDE_MCU) -U efuse:w:0xfa:m
|
|
||||||
|
119
README.md
Normal file
119
README.md
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
# twiboot - a TWI / I2C bootloader for AVR MCUs ##
|
||||||
|
twiboot is a simple/small bootloader for AVR MCUs written in C. It uses the integrated TWI or USI peripheral of the controller to implement a I2C slave.
|
||||||
|
It was originally created to update I2C controlled BLMCs (Brushless Motor Controller) without an AVR ISP adapter.
|
||||||
|
|
||||||
|
twiboot acts as a slave device on a TWI/I2C bus and allows reading/writing of the internal flash memory.
|
||||||
|
As a compile time option (EEPROM_SUPPORT) twiboot also allows reading/writing of the whole internal EEPROM memory.
|
||||||
|
The bootloader is not able to update itself (only application flash memory region accessible).
|
||||||
|
|
||||||
|
Currently the following AVR MCUs are supported:
|
||||||
|
|
||||||
|
AVR MCU | Flash bytes used (.text + .data) | Bootloader region size
|
||||||
|
--- | --- | ---
|
||||||
|
attiny85 | 954 (0x3BA) | 512 words
|
||||||
|
atmega8 | 786 (0x312) | 512 words
|
||||||
|
atmega88 | 810 (0x32A) | 512 words
|
||||||
|
atmega168 | 810 (0x32A) | 512 words
|
||||||
|
atmega328p | 810 (0x32A) | 512 words
|
||||||
|
|
||||||
|
(Compiled on Ubuntu 18.04 LTS (gcc 5.4.0 / avr-libc 2.0.0) with EEPROM and LED support)
|
||||||
|
|
||||||
|
|
||||||
|
## Operation ##
|
||||||
|
twiboot is installed in the bootloader section and executed directly after reset (BOOTRST fuse is programmed).
|
||||||
|
For MCUs without bootloader section see [Virtual bootloader section](#virtual-bootloader-section) below.
|
||||||
|
|
||||||
|
While running, twiboot configures the TWI/USI peripheral as slave device and waits for valid protocol messages
|
||||||
|
directed to its address on the TWI/I2C bus. The slave address is configured during compile time of twiboot.
|
||||||
|
When receiving no messages for 1000ms after reset, the bootloader exits and executes the main application at address 0x0000.
|
||||||
|
|
||||||
|
A TWI/I2C master can use the protocol to
|
||||||
|
- abort the boot timeout
|
||||||
|
- query information about the device (bootloader version, AVR signature bytes, flash/eeprom size, flash page size)
|
||||||
|
- read internal flash / eeprom memory (byte wise)
|
||||||
|
- write the internal flash (page wise)
|
||||||
|
- write the internal eeprom (byte wise)
|
||||||
|
- exit the bootloader and start the application
|
||||||
|
|
||||||
|
As a compile time option (LED_SUPPORT) twiboot can output its state with two LEDs.
|
||||||
|
One LED will flash with a frequency of 20Hz while twiboot is active (including boot wait time).
|
||||||
|
A second LED will flash when the bootloader is addressed on the TWI/I2C bus.
|
||||||
|
|
||||||
|
|
||||||
|
### Virtual Bootloader Section ###
|
||||||
|
For MCUs without bootloader section twiboot will patch the vector table on the fly during flash programming to stay active.
|
||||||
|
The reset vector is patched to execute twiboot instead of the application code.
|
||||||
|
|
||||||
|
Another vector entry will be patched to store the original entry point of the application.
|
||||||
|
This vector entry is overridden and MUST NOT be used by the application.
|
||||||
|
twiboot uses this vector to start the application after the initial timeout.
|
||||||
|
|
||||||
|
This live patching changes the content of the vector table, which would result in a verification error after programming.
|
||||||
|
To counter this kind of error, twiboot caches the original vector table entries in RAM and return those on a read command.
|
||||||
|
The real content of the vector table is only returned after a reset.
|
||||||
|
|
||||||
|
|
||||||
|
## Build and install twiboot ##
|
||||||
|
twiboot uses gcc, avr-libc and GNU Make for building, avrdude is used for flashing the MCU.
|
||||||
|
The build and install procedures are only tested under linux.
|
||||||
|
|
||||||
|
The selection of the target MCU and the programming interface can be found in the Makefile,
|
||||||
|
TWI/I2C slave address and optional components (EEPROM / LED support) are configured
|
||||||
|
in the main.c source.
|
||||||
|
|
||||||
|
To build twiboot for the selected target:
|
||||||
|
``` shell
|
||||||
|
$ make
|
||||||
|
```
|
||||||
|
|
||||||
|
To install (flash download) twiboot with avrdude on the target:
|
||||||
|
``` shell
|
||||||
|
$ make install
|
||||||
|
```
|
||||||
|
|
||||||
|
Set AVR fuses with avrdude on the target (internal RC-Osz, enable BOD, enable BOOTRST):
|
||||||
|
``` shell
|
||||||
|
$ make fuses
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## TWI/I2C Protocol ##
|
||||||
|
A TWI/I2C master can use the following protocol for accessing the bootloader.
|
||||||
|
|
||||||
|
Function | TWI/I2C data | Comment
|
||||||
|
--- | --- | ---
|
||||||
|
Abort boot timeout | **SLA+W**, 0x00, **STO** |
|
||||||
|
Show bootloader version | **SLA+W**, 0x01, **SLA+R**, {16 bytes}, **STO** | ASCII, not null terminated
|
||||||
|
Start application | **SLA+W**, 0x01, 0x80, **STO** |
|
||||||
|
Read chip info | **SLA+W**, 0x02, 0x00, 0x00, 0x00, **SLA+R**, {8 bytes}, **STO** | 3byte signature, 1byte page size, 2byte flash size, 2byte eeprom size
|
||||||
|
Read 1+ flash bytes | **SLA+W**, 0x02, 0x01, addrh, addrl, **SLA+R**, {* bytes}, **STO** |
|
||||||
|
Read 1+ eeprom bytes | **SLA+W**, 0x02, 0x02, addrh, addrl, **SLA+R**, {* bytes}, **STO** |
|
||||||
|
Write one flash page | **SLA+W**, 0x02, 0x01, addrh, addrl, {* bytes}, **STO** | page size as indicated in chip info
|
||||||
|
Write 1+ eeprom bytes | **SLA+W**, 0x02, 0x02, addrh, addrl, {* bytes}, **STO** | write 0 < n < page size bytes at once
|
||||||
|
|
||||||
|
**SLA+R** means Start Condition, Slave Address, Read Access
|
||||||
|
|
||||||
|
**SLA+W** means Start Condition, Slave Address, Write Access
|
||||||
|
|
||||||
|
**STO** means Stop Condition
|
||||||
|
|
||||||
|
A flash page / eeprom write is only triggered after the Stop Condition.
|
||||||
|
During the write process twiboot will NOT acknowledge its slave address.
|
||||||
|
|
||||||
|
The multiboot_tool repository contains a simple linux application that uses
|
||||||
|
this protocol to access the bootloader over linux i2c device.
|
||||||
|
|
||||||
|
The ispprog programming adapter can also be used as a avr910/butterfly to twiboot protocol bridge.
|
||||||
|
|
||||||
|
|
||||||
|
## TWI/I2C Clockstretching ##
|
||||||
|
While a write is in progress twiboot will not respond on the TWI/I2C bus and the
|
||||||
|
TWI/I2C master needs to retry/poll the slave address until the write has completed.
|
||||||
|
|
||||||
|
As a compile time option (USE_CLOCKSTRETCH) the previous behavior of twiboot can be restored:
|
||||||
|
TWI/I2C Clockstretching is then used to inform the master of the duration of the write.
|
||||||
|
Please note that there are some TWI/I2C masters that do not support clockstretching.
|
||||||
|
|
||||||
|
|
||||||
|
## Development ##
|
||||||
|
Issue reports, feature requests, patches or simply success stories are much appreciated.
|
3
linux/.gitignore
vendored
3
linux/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
*.o
|
|
||||||
*.d
|
|
||||||
twiboot
|
|
@ -1,22 +0,0 @@
|
|||||||
TARGET = twiboot
|
|
||||||
|
|
||||||
CFLAGS = -Wall -Wno-unused-result -O2 -MMD -MP -MF $(*F).d
|
|
||||||
|
|
||||||
# ------
|
|
||||||
|
|
||||||
SRC := $(wildcard *.c)
|
|
||||||
|
|
||||||
all: $(TARGET)
|
|
||||||
|
|
||||||
$(TARGET): $(SRC:.c=.o)
|
|
||||||
@echo " Linking file: $@"
|
|
||||||
@$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) > /dev/null
|
|
||||||
|
|
||||||
%.o: %.c
|
|
||||||
@echo " Building file: $<"
|
|
||||||
@$(CC) -c $(CFLAGS) $< -o $@
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf $(TARGET) *.o *.d
|
|
||||||
|
|
||||||
-include $(shell find . -name \*.d 2> /dev/null)
|
|
423
linux/filedata.c
423
linux/filedata.c
@ -1,423 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
* Copyright (C) 10/2010 by Olaf Rempel *
|
|
||||||
* razzor@kopf-tisch.de *
|
|
||||||
* *
|
|
||||||
* 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; version 2 of the License, *
|
|
||||||
* *
|
|
||||||
* This program is distributed in the hope that it will be useful, *
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
||||||
* GNU General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU General Public License *
|
|
||||||
* along with this program; if not, write to the *
|
|
||||||
* Free Software Foundation, Inc., *
|
|
||||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
||||||
***************************************************************************/
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
|
|
||||||
#include "filedata.h"
|
|
||||||
|
|
||||||
#define FILETYPE_UNKNOWN 0
|
|
||||||
#define FILETYPE_BINARY 1
|
|
||||||
#define FILETYPE_INTELHEX 2
|
|
||||||
|
|
||||||
int dbuf_alloc(struct databuf **dbuf, uint32_t size)
|
|
||||||
{
|
|
||||||
*dbuf = malloc(sizeof(struct databuf) + size);
|
|
||||||
if (*dbuf == NULL) {
|
|
||||||
perror("dbuf_alloc");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset((*dbuf)->data, 0xFF, size);
|
|
||||||
(*dbuf)->size = size;
|
|
||||||
(*dbuf)->length = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dbuf_free(struct databuf *dbuf)
|
|
||||||
{
|
|
||||||
free(dbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dbuf_dump(struct databuf *dbuf)
|
|
||||||
{
|
|
||||||
int pos = 0, oldskip = 0;
|
|
||||||
|
|
||||||
while (pos < dbuf->length) {
|
|
||||||
char buf[128];
|
|
||||||
int j, i = 0;
|
|
||||||
|
|
||||||
int skip = 1;
|
|
||||||
for (j = 0; j < 16; j++) {
|
|
||||||
if (pos + j < dbuf->length)
|
|
||||||
i += sprintf(buf + i, "%02X", dbuf->data[pos + j]);
|
|
||||||
else
|
|
||||||
i += sprintf(buf + i, " ");
|
|
||||||
|
|
||||||
if (j % 2)
|
|
||||||
buf[i++] = ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < 16; j++) {
|
|
||||||
if (pos + j < dbuf->length) {
|
|
||||||
unsigned char val = dbuf->data[pos + j];
|
|
||||||
if (val >= 0x20 && val < 0x7F)
|
|
||||||
buf[i++] = val;
|
|
||||||
else
|
|
||||||
buf[i++] = '.';
|
|
||||||
|
|
||||||
if (val != 0xFF)
|
|
||||||
skip = 0;
|
|
||||||
} else {
|
|
||||||
buf[i++] = ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pos == 0 || (pos + 16) >= dbuf->length || skip == 0) {
|
|
||||||
buf[i++] = '\0';
|
|
||||||
printf("%04X: %s\r\n", pos, buf);
|
|
||||||
oldskip = 0;
|
|
||||||
|
|
||||||
} else if (skip == 1 && oldskip == 0) {
|
|
||||||
printf("****\n");
|
|
||||||
oldskip = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int binfile_getsize(const char *filename, uint32_t *size)
|
|
||||||
{
|
|
||||||
int fd = open(filename, O_RDONLY);
|
|
||||||
if (fd < 0) {
|
|
||||||
perror("binfile_getsize(): open()");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct stat filestat;
|
|
||||||
if (fstat(fd, &filestat) < 0) {
|
|
||||||
perror("binfile_getsize(): fstat()");
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*size = filestat.st_size;
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int binfile_read(const char *filename, struct databuf *dbuf)
|
|
||||||
{
|
|
||||||
int fd = open(filename, O_RDONLY);
|
|
||||||
if (fd < 0) {
|
|
||||||
perror("binfile_read(): open()");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t readsize = read(fd, dbuf->data, dbuf->size);
|
|
||||||
if (readsize <= 0) {
|
|
||||||
perror("binfile_read(): read()");
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
dbuf->length = readsize;
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int binfile_write(const char *filename, struct databuf *dbuf)
|
|
||||||
{
|
|
||||||
int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
|
|
||||||
if (fd < 0) {
|
|
||||||
perror("binfile_write(): open()");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t writesize = write(fd, dbuf->data, dbuf->length);
|
|
||||||
if (writesize != dbuf->length) {
|
|
||||||
perror("binfile_write(): write()");
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ihex_record {
|
|
||||||
uint8_t byte_count;
|
|
||||||
uint16_t address;
|
|
||||||
uint8_t type;
|
|
||||||
|
|
||||||
uint8_t *data;
|
|
||||||
uint8_t chksum;
|
|
||||||
};
|
|
||||||
|
|
||||||
static uint8_t hex2byte(const char *ptr)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
uint8_t result = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < 2; i++) {
|
|
||||||
result <<= 4;
|
|
||||||
result |= (ptr[i] >= '0' && ptr[i] <= '9') ? (ptr[i] - '0') :
|
|
||||||
(((ptr[i] & 0xDF) >= 'A' && (ptr[i] & 0xDF) <= 'F') ? (ptr[i] - 'A' + 0x0A) :
|
|
||||||
0x00);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hexfile_getrecord(FILE *stream, struct ihex_record *record)
|
|
||||||
{
|
|
||||||
char *hexline = NULL;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
ssize_t length = getline(&hexline, &size, stream);
|
|
||||||
if (length == -1) {
|
|
||||||
if (!feof(stream)) {
|
|
||||||
perror("hexfile_getrecord(): getline()");
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (length < 12) {
|
|
||||||
fprintf(stderr, "record too short (%d)\n", length);
|
|
||||||
free(hexline);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int pos = 0;
|
|
||||||
if (hexline[pos] != ':') {
|
|
||||||
fprintf(stderr, "invalid startcode\n");
|
|
||||||
free(hexline);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
pos++;
|
|
||||||
|
|
||||||
uint8_t chksum = 0x00;
|
|
||||||
|
|
||||||
record->byte_count = hex2byte(&hexline[pos]);
|
|
||||||
chksum += record->byte_count;
|
|
||||||
pos += 2;
|
|
||||||
|
|
||||||
if (record->byte_count > 0) {
|
|
||||||
record->data = malloc(record->byte_count);
|
|
||||||
if (record->data == NULL) {
|
|
||||||
perror("hexfile_getrecord(): malloc()");
|
|
||||||
free(hexline);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t hiaddr = hex2byte(&hexline[pos]);
|
|
||||||
uint8_t loaddr = hex2byte(&hexline[pos +2]);
|
|
||||||
record->address = (hiaddr << 8) + loaddr;
|
|
||||||
chksum += hiaddr + loaddr;
|
|
||||||
pos += 4;
|
|
||||||
|
|
||||||
record->type = hex2byte(&hexline[pos]);
|
|
||||||
chksum += record->type;
|
|
||||||
pos += 2;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < record->byte_count; i++) {
|
|
||||||
record->data[i] = hex2byte(&hexline[pos]);
|
|
||||||
chksum += record->data[i];
|
|
||||||
pos += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
record->chksum = hex2byte(&hexline[pos]);
|
|
||||||
chksum += record->chksum;
|
|
||||||
pos += 2;
|
|
||||||
|
|
||||||
if (chksum != 0x00) {
|
|
||||||
fprintf(stderr, "invalid checksum (0x%02X)\n", chksum);
|
|
||||||
if (record->byte_count > 0)
|
|
||||||
free(record->data);
|
|
||||||
free(hexline);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(hexline);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hexfile_putrecord(FILE *stream, struct ihex_record *record)
|
|
||||||
{
|
|
||||||
uint8_t chksum = record->byte_count;
|
|
||||||
chksum += (record->address >> 8) & 0xFF;
|
|
||||||
chksum += (record->address & 0xFF);
|
|
||||||
chksum += record->type;
|
|
||||||
|
|
||||||
int i, len = 0;
|
|
||||||
char buf[64];
|
|
||||||
|
|
||||||
buf[0] = '\0';
|
|
||||||
for (i = 0; i < record->byte_count; i++) {
|
|
||||||
len += snprintf(buf + len, sizeof(buf) - len, "%02X", record->data[i]);
|
|
||||||
chksum += record->data[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stream, ":%02X%04X%02X%s%02X\n", record->byte_count, record->address, record->type, buf, (uint8_t)(0x100 - chksum));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hexfile_getsize(const char *filename, uint32_t *size)
|
|
||||||
{
|
|
||||||
*size = 0x10000;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hexfile_read(const char *filename, struct databuf *dbuf)
|
|
||||||
{
|
|
||||||
FILE *stream = fopen(filename, "r");
|
|
||||||
if (stream == NULL) {
|
|
||||||
perror("hexfile_read(): fopen()");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
struct ihex_record record;
|
|
||||||
memset(&record, 0x00, sizeof(struct ihex_record));
|
|
||||||
|
|
||||||
int result = hexfile_getrecord(stream, &record);
|
|
||||||
if (result == -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (record.type == 0x00) {
|
|
||||||
if (record.address > dbuf->size || record.address + record.byte_count > dbuf->size) {
|
|
||||||
fprintf(stderr, "hexfile_read(): data out of bounds\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&dbuf->data[record.address], record.data, record.byte_count);
|
|
||||||
dbuf->length = record.address + record.byte_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(stream);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hexfile_write(const char *filename, struct databuf *dbuf)
|
|
||||||
{
|
|
||||||
FILE *stream = fopen(filename, "w");
|
|
||||||
if (stream == NULL) {
|
|
||||||
perror("hexfile_write(): fopen()");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i;
|
|
||||||
int addr_min = dbuf->length;
|
|
||||||
int addr_max = 0;
|
|
||||||
for (i = 0; i < dbuf->length; i++) {
|
|
||||||
if (dbuf->data[i] == 0xFF)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (addr_min > i)
|
|
||||||
addr_min = i;
|
|
||||||
|
|
||||||
if (addr_max < i)
|
|
||||||
addr_max = i;
|
|
||||||
}
|
|
||||||
addr_min = addr_min & ~0x0F;
|
|
||||||
addr_max = (addr_max + 0x0F) & ~0x0F;
|
|
||||||
|
|
||||||
struct ihex_record record;
|
|
||||||
for (i = addr_min; i < addr_max; i += 0x10) {
|
|
||||||
record.byte_count = 0x10;
|
|
||||||
record.address = i;
|
|
||||||
record.type = 0x00;
|
|
||||||
record.data = &dbuf->data[i];
|
|
||||||
|
|
||||||
hexfile_putrecord(stream, &record);
|
|
||||||
}
|
|
||||||
|
|
||||||
record.byte_count = 0x00;
|
|
||||||
record.address = addr_min;
|
|
||||||
record.type = 0x01;
|
|
||||||
record.data = NULL;
|
|
||||||
hexfile_putrecord(stream, &record);
|
|
||||||
|
|
||||||
fclose(stream);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_filetype(const char *filename)
|
|
||||||
{
|
|
||||||
const char *ext = filename + (strlen(filename) -4);
|
|
||||||
|
|
||||||
if (ext < filename)
|
|
||||||
return FILETYPE_UNKNOWN;
|
|
||||||
|
|
||||||
if (strncmp(ext, ".bin", 4) == 0)
|
|
||||||
return FILETYPE_BINARY;
|
|
||||||
|
|
||||||
if (strncmp(ext, ".hex", 4) == 0)
|
|
||||||
return FILETYPE_INTELHEX;
|
|
||||||
|
|
||||||
return FILETYPE_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
int file_getsize(const char *filename, uint32_t *size)
|
|
||||||
{
|
|
||||||
switch (get_filetype(filename)) {
|
|
||||||
case FILETYPE_BINARY:
|
|
||||||
return binfile_getsize(filename, size);
|
|
||||||
|
|
||||||
case FILETYPE_INTELHEX:
|
|
||||||
return hexfile_getsize(filename, size);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int file_read(const char *filename, struct databuf *dbuf)
|
|
||||||
{
|
|
||||||
switch (get_filetype(filename)) {
|
|
||||||
case FILETYPE_BINARY:
|
|
||||||
return binfile_read(filename, dbuf);
|
|
||||||
|
|
||||||
case FILETYPE_INTELHEX:
|
|
||||||
return hexfile_read(filename, dbuf);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int file_write(const char *filename, struct databuf *dbuf)
|
|
||||||
{
|
|
||||||
if (strncmp(filename, "-", 1) == 0) {
|
|
||||||
dbuf_dump(dbuf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (get_filetype(filename)) {
|
|
||||||
case FILETYPE_BINARY:
|
|
||||||
return binfile_write(filename, dbuf);
|
|
||||||
|
|
||||||
case FILETYPE_INTELHEX:
|
|
||||||
return hexfile_write(filename, dbuf);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
#ifndef _FILEDATA_H_
|
|
||||||
#define _FILEDATA_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
struct databuf {
|
|
||||||
uint32_t size; // allocation size
|
|
||||||
uint32_t length; // used size
|
|
||||||
uint8_t data[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
int dbuf_alloc(struct databuf **dbuf, uint32_t size);
|
|
||||||
void dbuf_free(struct databuf *dbuf);
|
|
||||||
|
|
||||||
int file_getsize(const char *filename, uint32_t *size);
|
|
||||||
int file_read(const char *filename, struct databuf *dbuf);
|
|
||||||
int file_write(const char *filename, struct databuf *dbuf);
|
|
||||||
|
|
||||||
#endif /* _FILEDATA_H_ */
|
|
268
linux/list.h
268
linux/list.h
@ -1,268 +0,0 @@
|
|||||||
#ifndef _LIST_H_
|
|
||||||
#define _LIST_H_
|
|
||||||
|
|
||||||
/*
|
|
||||||
* stolen from linux kernel 2.6.11 (http://kernel.org/)
|
|
||||||
* linux/include/linux/stddef.h (offsetoff)
|
|
||||||
* linux/include/linux/kernel.h (container_of)
|
|
||||||
* linux/include/linux/list.h (*list*)
|
|
||||||
* linux/include/linux/netfilter_ipv4/listhelp.h (LIST_FIND)
|
|
||||||
*
|
|
||||||
* modified by Olaf Rempel <razzor@kopf-tisch.de>
|
|
||||||
*/
|
|
||||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
|
||||||
|
|
||||||
#define container_of(ptr, type, member) ({ \
|
|
||||||
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
|
||||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
|
||||||
|
|
||||||
struct list_head {
|
|
||||||
struct list_head *next, *prev;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
|
||||||
|
|
||||||
#define LIST_HEAD(name) \
|
|
||||||
struct list_head name = LIST_HEAD_INIT(name)
|
|
||||||
|
|
||||||
#define INIT_LIST_HEAD(ptr) do { \
|
|
||||||
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Insert a new entry between two known consecutive entries.
|
|
||||||
*
|
|
||||||
* This is only for internal list manipulation where we know
|
|
||||||
* the prev/next entries already!
|
|
||||||
*/
|
|
||||||
static inline void __list_add(struct list_head *new,
|
|
||||||
struct list_head *prev,
|
|
||||||
struct list_head *next)
|
|
||||||
{
|
|
||||||
next->prev = new;
|
|
||||||
new->next = next;
|
|
||||||
new->prev = prev;
|
|
||||||
prev->next = new;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_add - add a new entry
|
|
||||||
* @new: new entry to be added
|
|
||||||
* @head: list head to add it after
|
|
||||||
*
|
|
||||||
* Insert a new entry after the specified head.
|
|
||||||
* This is good for implementing stacks.
|
|
||||||
*/
|
|
||||||
static inline void list_add(struct list_head *new, struct list_head *head)
|
|
||||||
{
|
|
||||||
__list_add(new, head, head->next);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_add_tail - add a new entry
|
|
||||||
* @new: new entry to be added
|
|
||||||
* @head: list head to add it before
|
|
||||||
*
|
|
||||||
* Insert a new entry before the specified head.
|
|
||||||
* This is useful for implementing queues.
|
|
||||||
*/
|
|
||||||
static inline void list_add_tail(struct list_head *new, struct list_head *head)
|
|
||||||
{
|
|
||||||
__list_add(new, head->prev, head);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Delete a list entry by making the prev/next entries
|
|
||||||
* point to each other.
|
|
||||||
*
|
|
||||||
* This is only for internal list manipulation where we know
|
|
||||||
* the prev/next entries already!
|
|
||||||
*/
|
|
||||||
static inline void __list_del(struct list_head * prev, struct list_head * next)
|
|
||||||
{
|
|
||||||
next->prev = prev;
|
|
||||||
prev->next = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_del - deletes entry from list.
|
|
||||||
* @entry: the element to delete from the list.
|
|
||||||
* Note: list_empty on entry does not return true after this, the entry is
|
|
||||||
* in an undefined state.
|
|
||||||
*/
|
|
||||||
static inline void list_del(struct list_head *entry)
|
|
||||||
{
|
|
||||||
__list_del(entry->prev, entry->next);
|
|
||||||
entry->next = NULL;
|
|
||||||
entry->prev = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_del_init - deletes entry from list and reinitialize it.
|
|
||||||
* entry: the element to delete from the list.
|
|
||||||
*/
|
|
||||||
static inline void list_del_init(struct list_head *entry)
|
|
||||||
{
|
|
||||||
__list_del(entry->prev, entry->next);
|
|
||||||
INIT_LIST_HEAD(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_move - delete from one list and add as another's head
|
|
||||||
* @list: the entry to move
|
|
||||||
* @head: the head that will precede our entry
|
|
||||||
*/
|
|
||||||
static inline void list_move(struct list_head *list, struct list_head *head)
|
|
||||||
{
|
|
||||||
__list_del(list->prev, list->next);
|
|
||||||
list_add(list, head);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_move_tail - delete from one list and add as another's tail
|
|
||||||
* @list: the entry to move
|
|
||||||
* @head: the head that will follow our entry
|
|
||||||
*/
|
|
||||||
static inline void list_move_tail(struct list_head *list,
|
|
||||||
struct list_head *head)
|
|
||||||
{
|
|
||||||
__list_del(list->prev, list->next);
|
|
||||||
list_add_tail(list, head);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_empty - tests whether a list is empty
|
|
||||||
* @head: the list to test.
|
|
||||||
*/
|
|
||||||
static inline int list_empty(const struct list_head *head)
|
|
||||||
{
|
|
||||||
return head->next == head;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void __list_splice(struct list_head *list,
|
|
||||||
struct list_head *head)
|
|
||||||
{
|
|
||||||
struct list_head *first = list->next;
|
|
||||||
struct list_head *last = list->prev;
|
|
||||||
struct list_head *at = head->next;
|
|
||||||
|
|
||||||
first->prev = head;
|
|
||||||
head->next = first;
|
|
||||||
|
|
||||||
last->next = at;
|
|
||||||
at->prev = last;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_splice - join two lists
|
|
||||||
* @list: the new list to add.
|
|
||||||
* @head: the place to add it in the first list.
|
|
||||||
*/
|
|
||||||
static inline void list_splice(struct list_head *list, struct list_head *head)
|
|
||||||
{
|
|
||||||
if (!list_empty(list))
|
|
||||||
__list_splice(list, head);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_splice_init - join two lists and reinitialise the emptied list.
|
|
||||||
* @list: the new list to add.
|
|
||||||
* @head: the place to add it in the first list.
|
|
||||||
*
|
|
||||||
* The list at @list is reinitialised
|
|
||||||
*/
|
|
||||||
static inline void list_splice_init(struct list_head *list,
|
|
||||||
struct list_head *head)
|
|
||||||
{
|
|
||||||
if (!list_empty(list)) {
|
|
||||||
__list_splice(list, head);
|
|
||||||
INIT_LIST_HEAD(list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_entry - get the struct for this entry
|
|
||||||
* @ptr: the &struct list_head pointer.
|
|
||||||
* @type: the type of the struct this is embedded in.
|
|
||||||
* @member: the name of the list_struct within the struct.
|
|
||||||
*/
|
|
||||||
#define list_entry(ptr, type, member) \
|
|
||||||
container_of(ptr, type, member)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_for_each - iterate over a list
|
|
||||||
* @pos: the &struct list_head to use as a loop counter.
|
|
||||||
* @head: the head for your list.
|
|
||||||
*/
|
|
||||||
#define list_for_each(pos, head) \
|
|
||||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_for_each_prev - iterate over a list backwards
|
|
||||||
* @pos: the &struct list_head to use as a loop counter.
|
|
||||||
* @head: the head for your list.
|
|
||||||
*/
|
|
||||||
#define list_for_each_prev(pos, head) \
|
|
||||||
for (pos = (head)->prev; pos != (head); pos = pos->prev)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_for_each_safe - iterate over a list safe against removal of list entry
|
|
||||||
* @pos: the &struct list_head to use as a loop counter.
|
|
||||||
* @n: another &struct list_head to use as temporary storage
|
|
||||||
* @head: the head for your list.
|
|
||||||
*/
|
|
||||||
#define list_for_each_safe(pos, n, head) \
|
|
||||||
for (pos = (head)->next, n = pos->next; pos != (head); \
|
|
||||||
pos = n, n = pos->next)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_for_each_entry - iterate over list of given type
|
|
||||||
* @pos: the type * to use as a loop counter.
|
|
||||||
* @head: the head for your list.
|
|
||||||
* @member: the name of the list_struct within the struct.
|
|
||||||
*/
|
|
||||||
#define list_for_each_entry(pos, head, member) \
|
|
||||||
for (pos = list_entry((head)->next, typeof(*pos), member); \
|
|
||||||
&pos->member != (head); \
|
|
||||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_for_each_entry_reverse - iterate backwards over list of given type.
|
|
||||||
* @pos: the type * to use as a loop counter.
|
|
||||||
* @head: the head for your list.
|
|
||||||
* @member: the name of the list_struct within the struct.
|
|
||||||
*/
|
|
||||||
#define list_for_each_entry_reverse(pos, head, member) \
|
|
||||||
for (pos = list_entry((head)->prev, typeof(*pos), member); \
|
|
||||||
&pos->member != (head); \
|
|
||||||
pos = list_entry(pos->member.prev, typeof(*pos), member))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
|
||||||
* @pos: the type * to use as a loop counter.
|
|
||||||
* @n: another type * to use as temporary storage
|
|
||||||
* @head: the head for your list.
|
|
||||||
* @member: the name of the list_struct within the struct.
|
|
||||||
*/
|
|
||||||
#define list_for_each_entry_safe(pos, n, head, member) \
|
|
||||||
for (pos = list_entry((head)->next, typeof(*pos), member), \
|
|
||||||
n = list_entry(pos->member.next, typeof(*pos), member); \
|
|
||||||
&pos->member != (head); \
|
|
||||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
|
||||||
|
|
||||||
|
|
||||||
/* Return pointer to first true entry, if any, or NULL. A macro
|
|
||||||
required to allow inlining of cmpfn. */
|
|
||||||
#define LIST_FIND(head, cmpfn, type, args...) \
|
|
||||||
({ \
|
|
||||||
const struct list_head *__i, *__j = NULL; \
|
|
||||||
\
|
|
||||||
list_for_each(__i, (head)) \
|
|
||||||
if (cmpfn((const type)__i , ## args)) { \
|
|
||||||
__j = __i; \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
(type)__j; \
|
|
||||||
})
|
|
||||||
|
|
||||||
#endif /* _LIST_H_ */
|
|
329
linux/twb.c
329
linux/twb.c
@ -1,329 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
* Copyright (C) 10/2010 by Olaf Rempel *
|
|
||||||
* razzor@kopf-tisch.de *
|
|
||||||
* *
|
|
||||||
* 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; version 2 of the License, *
|
|
||||||
* *
|
|
||||||
* This program is distributed in the hope that it will be useful, *
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
||||||
* GNU General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU General Public License *
|
|
||||||
* along with this program; if not, write to the *
|
|
||||||
* Free Software Foundation, Inc., *
|
|
||||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
||||||
***************************************************************************/
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
|
|
||||||
#include <linux/i2c.h>
|
|
||||||
#include <linux/i2c-dev.h>
|
|
||||||
|
|
||||||
#include "filedata.h"
|
|
||||||
#include "list.h"
|
|
||||||
#include "twb.h"
|
|
||||||
|
|
||||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
||||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
|
|
||||||
|
|
||||||
#define READ_BLOCK_SIZE 128 /* bytes in one flash/eeprom read request */
|
|
||||||
#define WRITE_BLOCK_SIZE 16 /* bytes in one eeprom write request */
|
|
||||||
|
|
||||||
/* SLA+R */
|
|
||||||
#define CMD_WAIT 0x00
|
|
||||||
#define CMD_READ_VERSION 0x01
|
|
||||||
#define CMD_READ_MEMORY 0x02
|
|
||||||
|
|
||||||
/* SLA+W */
|
|
||||||
#define CMD_SWITCH_APPLICATION CMD_READ_VERSION
|
|
||||||
#define CMD_WRITE_MEMORY CMD_READ_MEMORY
|
|
||||||
|
|
||||||
/* CMD_SWITCH_APPLICATION parameter */
|
|
||||||
#define BOOTTYPE_BOOTLOADER 0x00 /* only in APP */
|
|
||||||
#define BOOTTYPE_APPLICATION 0x80
|
|
||||||
|
|
||||||
/* CMD_{READ|WRITE}_* parameter */
|
|
||||||
#define MEMTYPE_CHIPINFO 0x00
|
|
||||||
#define MEMTYPE_FLASH 0x01
|
|
||||||
#define MEMTYPE_EEPROM 0x02
|
|
||||||
#define MEMTYPE_PARAMETERS 0x03 /* only in APP */
|
|
||||||
|
|
||||||
struct chipinfo {
|
|
||||||
uint8_t sig[3];
|
|
||||||
const char name[16];
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct chipinfo chips[] = {
|
|
||||||
{ { 0x1E, 0x93, 0x07 }, "AVR Mega 8" },
|
|
||||||
{ { 0x1E, 0x93, 0x0A }, "AVR Mega 88" },
|
|
||||||
{ { 0x1E, 0x94, 0x06 }, "AVR Mega 168" },
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char * twb_get_chipname(uint8_t *sig)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < ARRAY_SIZE(chips); i++) {
|
|
||||||
struct chipinfo *chip = &chips[i];
|
|
||||||
if (chip->sig[0] == sig[0] && chip->sig[1] == sig[1] && chip->sig[2] == sig[2])
|
|
||||||
return chip->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
static int twb_switch_application(struct twiboot *twb, uint8_t application)
|
|
||||||
{
|
|
||||||
uint8_t cmd[] = { CMD_SWITCH_APPLICATION, application };
|
|
||||||
|
|
||||||
return (write(twb->fd, cmd, sizeof(cmd)) != sizeof(cmd));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int twb_read_version(struct twiboot *twb)
|
|
||||||
{
|
|
||||||
uint8_t cmd[] = { CMD_READ_VERSION };
|
|
||||||
|
|
||||||
if (write(twb->fd, cmd, sizeof(cmd)) != sizeof(cmd))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
memset(twb->version, 0, sizeof(twb->version));
|
|
||||||
if (read(twb->fd, twb->version, sizeof(twb->version)) != sizeof(twb->version))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < sizeof(twb->version); i++)
|
|
||||||
twb->version[i] &= ~0x80;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int twb_read_memory(struct twiboot *twb, uint8_t *buffer, uint8_t size, uint8_t memtype, uint16_t address)
|
|
||||||
{
|
|
||||||
uint8_t cmd[] = { CMD_READ_MEMORY, memtype, (address >> 8) & 0xFF, (address & 0xFF) };
|
|
||||||
if (write(twb->fd, cmd, sizeof(cmd)) != sizeof(cmd))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return (read(twb->fd, buffer, size) != size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int twb_write_memory(struct twiboot *twb, uint8_t *buffer, uint8_t size, uint8_t memtype, uint16_t address)
|
|
||||||
{
|
|
||||||
int bufsize;
|
|
||||||
if (memtype == MEMTYPE_FLASH) {
|
|
||||||
if ((address & (twb->pagesize -1)) != 0x00) {
|
|
||||||
fprintf(stderr, "twb_write_memory(): address 0x%04x not aligned to pagesize 0x%02x\n", address, twb->pagesize);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
bufsize = 4 + twb->pagesize;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
bufsize = 4 + size;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *cmd = malloc(bufsize);
|
|
||||||
if (cmd == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
cmd[0] = CMD_WRITE_MEMORY;
|
|
||||||
cmd[1] = memtype;
|
|
||||||
cmd[2] = (address >> 8) & 0xFF;
|
|
||||||
cmd[3] = (address & 0xFF);
|
|
||||||
memcpy(cmd +4, buffer, size);
|
|
||||||
|
|
||||||
if (memtype == MEMTYPE_FLASH) {
|
|
||||||
memset(cmd +4 +size, 0xFF, twb->pagesize - size);
|
|
||||||
}
|
|
||||||
|
|
||||||
int result = write(twb->fd, cmd, bufsize);
|
|
||||||
free(cmd);
|
|
||||||
|
|
||||||
return (result != bufsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void twb_close_device(struct twiboot *twb)
|
|
||||||
{
|
|
||||||
if (twb->connected)
|
|
||||||
close(twb->fd);
|
|
||||||
|
|
||||||
if (twb->device != NULL)
|
|
||||||
free(twb->device);
|
|
||||||
|
|
||||||
twb->device = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int twb_open_device(struct twiboot *twb)
|
|
||||||
{
|
|
||||||
twb->fd = open(twb->device, O_RDWR);
|
|
||||||
if (twb->fd < 0) {
|
|
||||||
fprintf(stderr, "failed to open '%s': %s\n", twb->device, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long funcs;
|
|
||||||
if (ioctl(twb->fd, I2C_FUNCS, &funcs)) {
|
|
||||||
perror("ioctl(I2C_FUNCS)");
|
|
||||||
close(twb->fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(funcs & I2C_FUNC_I2C)) {
|
|
||||||
fprintf(stderr, "I2C_FUNC_I2C not supported on '%s'!\n", twb->device);
|
|
||||||
close(twb->fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ioctl(twb->fd, I2C_SLAVE, twb->address) < 0) {
|
|
||||||
fprintf(stderr, "failed to select slave address '%d': %s\n", twb->address, strerror(errno));
|
|
||||||
close(twb->fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
twb->connected = 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int twb_close(struct twiboot *twb)
|
|
||||||
{
|
|
||||||
if (twb->connected)
|
|
||||||
twb_switch_application(twb, BOOTTYPE_APPLICATION);
|
|
||||||
|
|
||||||
twb_close_device(twb);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int twb_open(struct twiboot *twb)
|
|
||||||
{
|
|
||||||
if (twb_open_device(twb) != 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (twb_switch_application(twb, BOOTTYPE_BOOTLOADER)) {
|
|
||||||
fprintf(stderr, "failed to switch to bootloader (invalid address?): %s\n", strerror(errno));
|
|
||||||
twb_close(twb);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* wait for watchdog and startup time */
|
|
||||||
usleep(100000);
|
|
||||||
|
|
||||||
if (twb_read_version(twb)) {
|
|
||||||
fprintf(stderr, "failed to get bootloader version: %s\n", strerror(errno));
|
|
||||||
twb_close(twb);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t chipinfo[8];
|
|
||||||
if (twb_read_memory(twb, chipinfo, sizeof(chipinfo), MEMTYPE_CHIPINFO, 0x0000)) {
|
|
||||||
fprintf(stderr, "failed to get chipinfo: %s\n", strerror(errno));
|
|
||||||
twb_close(twb);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(twb->signature, chipinfo, sizeof(twb->signature));
|
|
||||||
twb->chipname = twb_get_chipname(twb->signature);
|
|
||||||
|
|
||||||
twb->pagesize = chipinfo[3];
|
|
||||||
twb->flashsize = (chipinfo[4] << 8) + chipinfo[5];
|
|
||||||
twb->eepromsize = (chipinfo[6] << 8) + chipinfo[7];
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int twb_read(struct twiboot *twb, struct databuf *dbuf, int memtype)
|
|
||||||
{
|
|
||||||
int pos = 0;
|
|
||||||
int size = (memtype == MEMTYPE_FLASH) ? twb->flashsize : twb->eepromsize;
|
|
||||||
|
|
||||||
while (pos < size) {
|
|
||||||
if (twb->progress_cb)
|
|
||||||
twb->progress_cb(twb->progress_msg, pos, size);
|
|
||||||
|
|
||||||
int len = MIN(READ_BLOCK_SIZE, size - pos);
|
|
||||||
if (twb_read_memory(twb, dbuf->data + pos, len, memtype, pos)) {
|
|
||||||
if (twb->progress_cb)
|
|
||||||
twb->progress_cb(twb->progress_msg, -1, -1);
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (twb->progress_cb)
|
|
||||||
twb->progress_cb(twb->progress_msg, pos, size);
|
|
||||||
|
|
||||||
dbuf->length = pos;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int twb_write(struct twiboot *twb, struct databuf *dbuf, int memtype)
|
|
||||||
{
|
|
||||||
int pos = 0;
|
|
||||||
|
|
||||||
while (pos < dbuf->length) {
|
|
||||||
if (twb->progress_cb)
|
|
||||||
twb->progress_cb(twb->progress_msg, pos, dbuf->length);
|
|
||||||
|
|
||||||
int len = (memtype == MEMTYPE_FLASH) ? twb->pagesize : WRITE_BLOCK_SIZE;
|
|
||||||
|
|
||||||
len = MIN(len, dbuf->length - pos);
|
|
||||||
if (twb_write_memory(twb, dbuf->data + pos, len, memtype, pos)) {
|
|
||||||
if (twb->progress_cb)
|
|
||||||
twb->progress_cb(twb->progress_msg, -1, -1);
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (twb->progress_cb)
|
|
||||||
twb->progress_cb(twb->progress_msg, pos, dbuf->length);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int twb_verify(struct twiboot *twb, struct databuf *dbuf, int memtype)
|
|
||||||
{
|
|
||||||
int pos = 0;
|
|
||||||
uint8_t comp[READ_BLOCK_SIZE];
|
|
||||||
|
|
||||||
while (pos < dbuf->length) {
|
|
||||||
if (twb->progress_cb)
|
|
||||||
twb->progress_cb(twb->progress_msg, pos, dbuf->length);
|
|
||||||
|
|
||||||
int len = MIN(READ_BLOCK_SIZE, dbuf->length - pos);
|
|
||||||
if (twb_read_memory(twb, comp, len, memtype, pos)) {
|
|
||||||
if (twb->progress_cb)
|
|
||||||
twb->progress_cb(twb->progress_msg, -1, -1);
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (memcmp(comp, dbuf->data + pos, len) != 0x00) {
|
|
||||||
if (twb->progress_cb)
|
|
||||||
twb->progress_cb(twb->progress_msg, -1, -1);
|
|
||||||
|
|
||||||
fprintf(stderr, "verify failed at page 0x%04x!!\n", pos);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (twb->progress_cb)
|
|
||||||
twb->progress_cb(twb->progress_msg, pos, dbuf->length);
|
|
||||||
|
|
||||||
dbuf->length = pos;
|
|
||||||
return 0;
|
|
||||||
}
|
|
31
linux/twb.h
31
linux/twb.h
@ -1,31 +0,0 @@
|
|||||||
#ifndef _TWB_H_
|
|
||||||
#define _TWB_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
struct twiboot {
|
|
||||||
char *device;
|
|
||||||
uint8_t address;
|
|
||||||
int fd;
|
|
||||||
int connected;
|
|
||||||
|
|
||||||
char version[16];
|
|
||||||
uint8_t signature[3];
|
|
||||||
const char *chipname;
|
|
||||||
|
|
||||||
uint8_t pagesize;
|
|
||||||
uint16_t flashsize;
|
|
||||||
uint16_t eepromsize;
|
|
||||||
|
|
||||||
void (* progress_cb)(const char *msg, int pos, int max);
|
|
||||||
char *progress_msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
int twb_open(struct twiboot *twb);
|
|
||||||
int twb_close(struct twiboot *twb);
|
|
||||||
|
|
||||||
int twb_read(struct twiboot *twb, struct databuf *dbuf, int memtype);
|
|
||||||
int twb_verify(struct twiboot *twb, struct databuf *dbuf, int memtype);
|
|
||||||
int twb_write(struct twiboot *twb, struct databuf *dbuf, int memtype);
|
|
||||||
|
|
||||||
#endif /* _TWIBOOT_H_ */
|
|
365
linux/twiboot.c
365
linux/twiboot.c
@ -1,365 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
* Copyright (C) 10/2010 by Olaf Rempel *
|
|
||||||
* razzor@kopf-tisch.de *
|
|
||||||
* *
|
|
||||||
* 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; version 2 of the License, *
|
|
||||||
* *
|
|
||||||
* This program is distributed in the hope that it will be useful, *
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
||||||
* GNU General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU General Public License *
|
|
||||||
* along with this program; if not, write to the *
|
|
||||||
* Free Software Foundation, Inc., *
|
|
||||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
||||||
***************************************************************************/
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <getopt.h>
|
|
||||||
|
|
||||||
#include "filedata.h"
|
|
||||||
#include "list.h"
|
|
||||||
#include "twb.h"
|
|
||||||
|
|
||||||
#define OP_MODE_READ 0x01
|
|
||||||
#define OP_MODE_WRITE 0x02
|
|
||||||
#define OP_TYPE_FLASH 0x01
|
|
||||||
#define OP_TYPE_EEPROM 0x02
|
|
||||||
|
|
||||||
struct operation {
|
|
||||||
struct list_head list;
|
|
||||||
|
|
||||||
char *filename;
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
int mode;
|
|
||||||
int memtype;
|
|
||||||
};
|
|
||||||
|
|
||||||
static LIST_HEAD(operation_list);
|
|
||||||
|
|
||||||
static struct option opts[] = {
|
|
||||||
{"address", 1, 0, 'a'}, // -a <addr>
|
|
||||||
{"device", 1, 0, 'd'}, // [ -d <device> ]
|
|
||||||
{"help", 0, 0, 'h'}, // [ -h ]
|
|
||||||
{"progress", 1, 0, 'p'}, // [ -p <0|1|2> ]
|
|
||||||
{"read", 1, 0, 'r'}, // [ -r <flash|eeprom>:<file.hex> ]
|
|
||||||
{"write", 1, 0, 'w'}, // [ -w <flash|eeprom>:<file.hex> ]
|
|
||||||
{"no-verify", 0, 0, 'n'}, // [ -n ]
|
|
||||||
{0, 0, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct operation * alloc_operation(const char *arg)
|
|
||||||
{
|
|
||||||
struct operation *op = malloc(sizeof(struct operation));
|
|
||||||
if (op == NULL) {
|
|
||||||
perror("malloc()");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strncmp(arg, "flash:", 6) == 0) {
|
|
||||||
op->memtype = OP_TYPE_FLASH;
|
|
||||||
op->filename = strdup(arg + 6);
|
|
||||||
|
|
||||||
} else if (strncmp(arg, "eeprom:", 7) == 0) {
|
|
||||||
op->memtype = OP_TYPE_EEPROM;
|
|
||||||
op->filename = strdup(arg + 7);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "invalid memtype: '%s'\n", arg);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void progress_mode1_cb(const char *msg, int pos, int size)
|
|
||||||
{
|
|
||||||
if (pos != -1 && size != -1) {
|
|
||||||
char stars[50];
|
|
||||||
|
|
||||||
int i;
|
|
||||||
int count = (pos * sizeof(stars) / size);
|
|
||||||
for (i = 0; i < sizeof(stars); i++)
|
|
||||||
stars[i] = (i < count) ? '*' : ' ';
|
|
||||||
|
|
||||||
printf("%-15s: [%s] (%d)\r", msg, stars, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pos == size)
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void progress_mode2_cb(const char *msg, int pos, int size)
|
|
||||||
{
|
|
||||||
static int old_count;
|
|
||||||
|
|
||||||
if (pos != -1 && size != -1) {
|
|
||||||
if (pos == 0) {
|
|
||||||
old_count = 0;
|
|
||||||
printf("%-15s: [", msg);
|
|
||||||
|
|
||||||
} else if (pos <=size) {
|
|
||||||
int i;
|
|
||||||
int count = (pos * 50 / size);
|
|
||||||
for (i = old_count; i < count; i++)
|
|
||||||
printf("*");
|
|
||||||
|
|
||||||
old_count = count;
|
|
||||||
|
|
||||||
if (pos == size) {
|
|
||||||
printf("] (%d)\n", pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
struct twiboot twb;
|
|
||||||
int verify = 1, progress = 1;
|
|
||||||
|
|
||||||
memset(&twb, 0, sizeof(struct twiboot));
|
|
||||||
|
|
||||||
int arg = 0, code = 0, abort = 0;
|
|
||||||
while (code != -1) {
|
|
||||||
code = getopt_long(argc, argv, "a:d:hnp:r:w:", opts, &arg);
|
|
||||||
|
|
||||||
switch (code) {
|
|
||||||
case 'a': /* address */
|
|
||||||
{
|
|
||||||
char *endptr;
|
|
||||||
twb.address = strtol(optarg, &endptr, 16);
|
|
||||||
if (*endptr != '\0' || twb.address < 0x01 || twb.address > 0x7F) {
|
|
||||||
fprintf(stderr, "invalid address: '%s'\n", optarg);
|
|
||||||
abort = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'd': /* device */
|
|
||||||
if (twb.device != NULL) {
|
|
||||||
fprintf(stderr, "invalid device: '%s'\n", optarg);
|
|
||||||
abort = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
twb.device = strdup(optarg);
|
|
||||||
if (twb.device == NULL) {
|
|
||||||
perror("strdup()");
|
|
||||||
abort = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'r': /* read */
|
|
||||||
{
|
|
||||||
struct operation *op = alloc_operation(optarg);
|
|
||||||
if (op != NULL) {
|
|
||||||
op->mode = OP_MODE_READ;
|
|
||||||
list_add_tail(&op->list, &operation_list);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
abort = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'w': /* write */
|
|
||||||
{
|
|
||||||
struct operation *op = alloc_operation(optarg);
|
|
||||||
if (op != NULL) {
|
|
||||||
op->mode = OP_MODE_WRITE;
|
|
||||||
list_add_tail(&op->list, &operation_list);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
abort = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'n': /* no verify */
|
|
||||||
verify = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'p': /* progress bar mode */
|
|
||||||
{
|
|
||||||
if (*optarg >= '0' && *optarg <= '2') {
|
|
||||||
progress = *optarg - '0';
|
|
||||||
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "invalid progress bar mode: '%s'\n", optarg);
|
|
||||||
abort = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
progress = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'h':
|
|
||||||
case '?': /* error */
|
|
||||||
fprintf(stderr, "Usage: twiboot [options]\n"
|
|
||||||
" -a <address> - selects i2c address (0x01 - 0x7F)\n"
|
|
||||||
" -d <device> - selects i2c device (default: /dev/i2c-0)\n"
|
|
||||||
" -r <flash|eeprom>:<file> - reads flash/eeprom to file (.bin | .hex | -)\n"
|
|
||||||
" -w <flash|eeprom>:<file> - write flash/eeprom from file (.bin | .hex)\n"
|
|
||||||
" -n - disable verify after write\n"
|
|
||||||
" -p <0|1|2> - progress bar mode\n"
|
|
||||||
"\n"
|
|
||||||
"Example: twiboot -a 0x22 -w flash:blmc.hex -w flash:blmc_eeprom.hex\n"
|
|
||||||
"\n");
|
|
||||||
abort = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: /* unknown / all options parsed */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (twb.address == 0) {
|
|
||||||
fprintf(stderr, "abort: no address given\n");
|
|
||||||
abort = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (twb.device == NULL) {
|
|
||||||
twb.device = strdup("/dev/i2c-0");
|
|
||||||
if (twb.device == NULL) {
|
|
||||||
perror("strdup()");
|
|
||||||
abort = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!abort) {
|
|
||||||
if (twb_open(&twb) != 0x00)
|
|
||||||
abort = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!abort) {
|
|
||||||
printf("device : %-16s (address: 0x%02X)\n", twb.device, twb.address);
|
|
||||||
printf("version : %-16s (sig: 0x%02x 0x%02x 0x%02x => %s)\n", twb.version, twb.signature[0], twb.signature[1], twb.signature[2], twb.chipname);
|
|
||||||
printf("flash size : 0x%04x / %5d (0x%02x bytes/page)\n", twb.flashsize, twb.flashsize, twb.pagesize);
|
|
||||||
printf("eeprom size : 0x%04x / %5d\n", twb.eepromsize, twb.eepromsize);
|
|
||||||
|
|
||||||
if (progress) {
|
|
||||||
setbuf(stdout, NULL);
|
|
||||||
twb.progress_cb = (progress == 1) ? progress_mode1_cb : progress_mode2_cb;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct operation *op;
|
|
||||||
list_for_each_entry(op, &operation_list, list) {
|
|
||||||
abort = 1;
|
|
||||||
if (op->mode == OP_MODE_READ) {
|
|
||||||
struct databuf *dbuf;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
if (op->memtype == OP_TYPE_FLASH) {
|
|
||||||
twb.progress_msg = "reading flash";
|
|
||||||
result = dbuf_alloc(&dbuf, twb.flashsize);
|
|
||||||
} else if (op->memtype == OP_TYPE_EEPROM) {
|
|
||||||
twb.progress_msg = "reading eeprom";
|
|
||||||
result = dbuf_alloc(&dbuf, twb.eepromsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result != 0x00)
|
|
||||||
break;
|
|
||||||
|
|
||||||
result = twb_read(&twb, dbuf, op->memtype);
|
|
||||||
if (result != 0x00) {
|
|
||||||
fprintf(stderr, "failed to read from device\n");
|
|
||||||
dbuf_free(dbuf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = file_write(op->filename, dbuf);
|
|
||||||
if (result != 0x00) {
|
|
||||||
fprintf(stderr, "failed to write file '%s'\n", op->filename);
|
|
||||||
dbuf_free(dbuf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
dbuf_free(dbuf);
|
|
||||||
|
|
||||||
} else if (op->mode == OP_MODE_WRITE) {
|
|
||||||
struct databuf *dbuf;
|
|
||||||
unsigned int size;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
result = file_getsize(op->filename, &size);
|
|
||||||
if (result != 0x00)
|
|
||||||
break;
|
|
||||||
|
|
||||||
result = dbuf_alloc(&dbuf, size);
|
|
||||||
if (result != 0x00)
|
|
||||||
break;
|
|
||||||
|
|
||||||
result = file_read(op->filename, dbuf);
|
|
||||||
if (result != 0x00) {
|
|
||||||
fprintf(stderr, "failed to read file '%s'\n", op->filename);
|
|
||||||
dbuf_free(dbuf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (op->memtype == OP_TYPE_FLASH) {
|
|
||||||
twb.progress_msg = "writing flash";
|
|
||||||
|
|
||||||
if (dbuf->length > twb.flashsize) {
|
|
||||||
fprintf(stderr, "invalid flash size: 0x%04x > 0x%04x\n", dbuf->length, twb.flashsize);
|
|
||||||
dbuf_free(dbuf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (op->memtype == OP_TYPE_EEPROM) {
|
|
||||||
twb.progress_msg = "writing eeprom";
|
|
||||||
|
|
||||||
if (dbuf->length > twb.eepromsize) {
|
|
||||||
fprintf(stderr, "invalid eeprom size: 0x%04x > 0x%04x\n", dbuf->length, twb.eepromsize);
|
|
||||||
dbuf_free(dbuf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result = twb_write(&twb, dbuf, op->memtype);
|
|
||||||
if (result != 0x00) {
|
|
||||||
fprintf(stderr, "failed to write to device\n");
|
|
||||||
dbuf_free(dbuf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verify) {
|
|
||||||
if (op->memtype == OP_TYPE_FLASH) {
|
|
||||||
twb.progress_msg = "verifing flash";
|
|
||||||
} else if (op->memtype == OP_TYPE_EEPROM) {
|
|
||||||
twb.progress_msg = "verifing eeprom";
|
|
||||||
}
|
|
||||||
|
|
||||||
result = twb_verify(&twb, dbuf, op->memtype);
|
|
||||||
if (result != 0) {
|
|
||||||
fprintf(stderr, "failed to verify\n");
|
|
||||||
dbuf_free(dbuf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dbuf_free(dbuf);
|
|
||||||
}
|
|
||||||
abort = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct operation *op, *tmp;
|
|
||||||
list_for_each_entry_safe(op, tmp, &operation_list, list) {
|
|
||||||
free(op->filename);
|
|
||||||
free(op);
|
|
||||||
}
|
|
||||||
|
|
||||||
twb_close(&twb);
|
|
||||||
|
|
||||||
return abort;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user