Browse Source

Import upstream version 0.0~svn5

Evgeni Golov 15 years ago
commit
957bed6bdd
27 changed files with 4507 additions and 0 deletions
  1. 1 0
      AUTHORS
  2. 339 0
      COPYING
  3. 33 0
      README
  4. 7 0
      TODO
  5. 579 0
      doc/commands.txt
  6. 35 0
      doc/u3-tool.1
  7. 40 0
      src/Makefile
  8. 48 0
      src/Makefile.win
  9. 25 0
      src/display_progress.c
  10. 14 0
      src/display_progress.h
  11. 578 0
      src/main.c
  12. 460 0
      src/md5.c
  13. 121 0
      src/md5.h
  14. 44 0
      src/secure_input.c
  15. 16 0
      src/secure_input.h
  16. 111 0
      src/sg_err.h
  17. 48 0
      src/u3.h
  18. 551 0
      src/u3_commands.c
  19. 319 0
      src/u3_commands.h
  20. 57 0
      src/u3_error.c
  21. 42 0
      src/u3_error.h
  22. 88 0
      src/u3_scsi.h
  23. 111 0
      src/u3_scsi_debug.c
  24. 165 0
      src/u3_scsi_sg.c
  25. 134 0
      src/u3_scsi_spt.c
  26. 352 0
      src/u3_scsi_usb.c
  27. 189 0
      src/u3_tool.dev

+ 1 - 0
AUTHORS

@@ -0,0 +1 @@
+daviedev, <daviedev@users.sourceforge.net>

+ 339 - 0
COPYING

@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.

+ 33 - 0
README

@@ -0,0 +1,33 @@
+Description:
+-----------
+u3_tool is a tool for unlocking and configuring U3 smart USB Flash devices.
+
+Supported device:
+----------------
+In general all U3 USB flash devices should be supported. A list of U3 compatible devices can be obtained from http://www.U3.com.
+
+The software is currently tested with the following drives:
+* Sandisk Cruzer Micro U3 512Mb
+* Verbatim Store 'N Go 1Gb
+
+Selecting a subsystem:
+---------------------
+U3-Tool can utilize a number of subsystems to access the USB drive. Which one to use depends on you Operating system and version.
+
+- sg
+The sg subsystem uses the Linux SCSI Generic interface to communicate with the device. The big advantage of this subsystem is that the u3_tool can issue commands to the device while the device is under control of the usb-storage Linux subsystem. Big disadvantage is that the sg system is somewhat limited, and thus doesn't allow all commands to be executed.
+
+- libusb
+The libusb subsystem uses libusb to send raw USB commands to the device. An advantag of this subsystem is that libusb should work on all Linux kernels >= 2.4, and should even work other operating systems.
+However...  For libusb to be able send commands to the USB device it needs to claim the device for its self to prevent total chaos in the communication. This requires the Linux usb-storage system, which makes the device available as disk to the end-user, to release the device. So effectively this means that you can't use the device as disk and use U3-tool at the same time.
+
+- spt
+The spt subsystem uses MS Windows SCSI pass through interface to communicate with the device. This is similar to the sg subsystem, with the difference that this is Windows specific. Als contrary to the sg subsystem, this works well!
+
+Compilation:
+------------
+If you have downloaded the source:
+ - enter the src/ directory
+ - On Linux type 'make u3_tool_sg' if you want to use the 'sg' subsystem, 'make u3_tool' for the 'libusb' subsystem or 'make all' for both.
+ - On Windows you need the mingw32 compiler. Type 'make -f Makefile.win' or open the u3_tool.dev file with Bloodshed's Dev-C++(http://www.bloodshed.net/devcpp.html).
+

+ 7 - 0
TODO

@@ -0,0 +1,7 @@
+Major:
+ - Wipe all password/hash buffers after usage
+ - Documentation
+ - Further documentation of U3 features
+
+minor:
+ - ETA when loading new CD image

+ 579 - 0
doc/commands.txt

@@ -0,0 +1,579 @@
+
+---
+
+>> 0x00
+description:
+ Read a page with some sort of device info
+
+type: request
+
+command:
+
+ CBWCB = 0xFF 0x00 0x00 0x03 0x00 0x27 0x00 0x00 0x00 0x00 0x00 0x00
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0	0xFF		SCSI opcode
+ 1-2	0x0000		command
+ 3-4	0x0003		Page number
+ 5-6	0x27		Request data length
+ 9	0x0		??? sofware update tool also uses 0x01 here
+
+ valid page numbers according to bCSWStatus:
+  verbatime: 0x3, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x13, 0x14
+
+data:
+ 6+ bytes
+
+ 0000  03 00 01 00 27 00                                 |....'.          |
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0-1	0x0003		page number
+ 2-3	0x0001		??? mostly 0x01, once 0x03
+ 4-5	0x0027		total usefull data length (including 6 byte header)
+
+
+page 0x03:
+
+ 0000  03 00 01 00 27 00 77 00  00 00 03 81 07 06 54 30  |....'.w.......T0|
+ 0016  30 30 30 31 31 41 31 41  41 31 31 41 41 41 31 81  |00011A1AA11AAA1.|
+ 0032  07 06 54 91 4e 0f 00                              |..T.N..|
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0-5			header(see above)
+ 6			Real size of full record...????
+ 7-11			??? same for both verbatime and sandisk
+ 11-14	0x54060781	??? same as @ byte 31 (actual value different on verbatim)
+ 15-30	"000011A1AA11AAA1"	Serial number
+ 31-34	0x54060781	??? same as @ byte 11
+ 35-38	0x000f4e91	Device size in 512-byte blocks
+
+page 0x0c:
+
+ 0000  0c 00 01 00 0a 00 10 27  00 00                    |.......'..      |
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0-5			header(see above)
+ 6-9	0x00002710	Maximum wrong password try for secure zone.
+
+---
+
+>> 0x20
+description:
+ Round CD size to a value the device likes
+
+type: action
+
+command:
+ CBWCB = 0xFF 0x20 0x00 0x02 0xFF 0x03 0x00 0x00 0x00 0x00 0x00 0x00
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0	0xFF		scsi opcode
+ 1-2	0x0020		command
+ 3	0x02		??? can atleast be 0x02 and 0x03 but not 0, 1, 4, is
+			this some sort of domain id to select the partition,
+			like in 0x21, byte 3??? the verbatim is less picky and
+			accepts all values...
+ 4-7	0x3FF		Value to round (in 512-byte sectors)
+ 8	0x0		Direction to round(0x00 = down, 0x01 = up)
+
+data:
+ 4 bytes
+
+ 0000  00 02 00 00                                      |....|
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0-3	0x200		Rounded value
+
+---
+
+>> 0x21
+description:
+ get information about the partition configuration
+
+type: request
+
+command:
+ CBWCB = 0xFF 0x21 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+data:
+
+ 0000  02 02 00 00 00 91 1e 0f  00 03 01 00 00 00 30 00  |..............0.|
+ can be read in any multiple of 8 all dat beyond the above data is zero(0)
+
+ u3-remover uses the following data(IIRC), (0x0f4e91 == full drive size):
+ 0000  01 02 00 00 00 91 4e 0f  00                       |.........       |
+
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0	02		amount of available records, where 1 record = 8 byte???
+ 1	02		??? some sort of domain id???
+ 3-4	00 00 00	??
+ 5-8	0x000F1E91	size of data partition in 512-byte sectors
+ 9	0x03		?? some sort of domain id????
+ 10	0x01		?? WARNING: If set to 0 on Sandisk cruzer, cd drive
+			will show up as direct-access, but can't be used, also
+			drive doesn't react to command 0x00, page 3 and you
+			won't be able to re-partition device!!!!
+ 11-12	00 00		??
+ 13-15	0x003000	size of cdrom partitoin in 512-byte sectors
+
+---
+
+>> 0x22
+description:
+ Repartition device
+
+type: action
+
+command:
+ CBWCB = 0xFF 0x22 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+data:
+ 0000  02 02 00 00 00 91 1e 0f  00 03 01 00  00 00 30 00 |..............0.|
+ 0016  00                                                |.               |
+
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0	0x02		amount of dword of data(+1byte+1dword=packet_size)
+ 1-4	0x00000002	???
+ 5-8	0x000f1e91	Size of data partition in 512-byte sectors
+ 9-12	0x00000103	??? 0x0003 make's it a direct access partition.(but can't partition afterwards, and page 3 of command 0x0000 isn't accessible anymore...)
+ 13-16	0x00003000	Size of CD partition in 512-byte sectors
+
+---
+
+>> 0x42
+description:
+ Write block of data to CD-rom partition
+
+type: action
+
+command:
+ CBWCB = 0xFF 0x42 0x00 0x01 0x00 0x00 0x01 0x1D 0x00 0x00 0x00 0x01 
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0	0xFF		scsi opcode
+ 1-2	0x42		command
+ 3	0x01		???
+ 4-7	0x0000011D	Block Address (Big Endian!!!!!!!)
+ 8-11	0x01		??? (Big Endian?)
+
+data:
+ A 2048 byte block
+
+---
+
+>> 0x61
+description:
+ read out hidden data/config storage. Looks the same as with mDrive.
+
+type: request
+
+command:
+ CBWCB = 0xFF 0x61 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+data:
+
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+---
+
+>> 0x63
+description:
+ read out hidden data/config storage. Looks the similar as with mDrive.
+
+type: request
+
+command:
+ CBWCB = 0xFF 0x63 0x00 0x00 0x00 0x55 0x33 0x49 0x4E 0x50 0x52 0x50
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+data:
+
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+---
+
+>> 0xA0
+description:
+ get some sort of data partition information
+
+type: request
+
+command:
+
+ CBWCB = 0xFF 0xA0 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+data:
+ 16 byte
+
+ 0000  40 ab 1d 00 00 00 00 00  00 00 00 00 00 00 00 00  |@...............|
+
+ 0000  40 ab 1d 00 40 ab 1d 00  01 00 00 00 00 00 00 00  |@...@...........| (secured)
+
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0-3	0x001dab40	Total data partition size
+ 4-7	0x001dab40	Amount of data partition encrypted????
+ 8-11	0x00000001	Lock(=0) or Unlocked(=1)
+ 12-15	0x00000000	Wrong password try counter
+
+---
+
+>> 0xA1
+description:
+ FUZED
+
+type: Request?
+
+command:
+
+ CBWCB = 0xFF 0xA1 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0			scsi opcode
+ 1-2			command
+ 3	0		Failes on Sandisk if != 0
+ 4-			changing these doesn't seem to have any effect
+
+data:
+
+ data is random, and changes due to executing commands
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+
+---
+
+>> 0xA2
+description:
+ Secure data partition
+
+ Password hash is a md5 sum of the unicode password including the terminating
+ null. So for a password of 'a' the following byte stream is fead to the md5
+ function: 0x61 0x00 0x00 0x00 == UNICODE "a\0"
+
+ It seems that if the whole of the data partition is made secure zone, then
+ the data currently on the data partition is accessible in the secure zone.
+ If only a part of the data partition is made secure zone than the first part
+ of the data on the partition is retained and the rest isn't accessible. In
+ this case the secure zone will contain garbage(the data on that was on that
+ part of the data partition but decrypted with an other key).
+
+ If the device is already secured and this command is issued again, the current
+ data on the device is lost(if secure zone == 100%).
+
+type: action
+
+command:
+
+ CBWCB = 0xFF 0xA2 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+data:
+ 20 byte
+
+ 0000  40 ab 1d 00 33 2c e7 85  e9 73 57 4a 1c 5f da f3  |@...3,...sWJ._..|
+ 0016  ee e3 f0 83                                       |....|
+
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0-3	0x001dab40	Size of private zone????
+ 4-19	...		Password hash ( pass='a')
+
+
+---
+
+>> 0xA3
+description:
+ value rounding for data partition securing
+
+type: request
+
+command:
+ CBWCB = 0xFF 0xA3 0x00 0x00 0x40 0xAB 0x1D 0x00 0x01 0x00 0x00 0x00
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0	0xFF		scsi opcode
+ 1-2	0x00A3		Command
+ 3	0x00		???
+ 4-7	0x001DAB40	Value to round (in 512-byte sectors)
+ 8	0x01		Direction to round(0x00 = down, 0x01 = up)
+ 
+
+data:
+ 4 byte
+
+ 0000  40 ab 1d 00                                      |@...|
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0-3	0x001DAB40	Rounded value
+
+---
+
+>> 0xA4
+description:
+ unlock device
+
+ Password hash is a md5 sum of the unicode password including the terminating
+ null. So for a password of 'a' the following byte stream is fead to the md5
+ function: 0x61 0x00 0x00 0x00 == UNICODE "a\0"
+
+type: action
+
+command:
+
+ CBWCB = 0xFF 0xA4 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+data:
+ 16 byte
+
+ 0000  33 2c e7 85 e9 73 57 4a  1c 5f da f3 ee e3 f0 83  |3,...sWJ._......|
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0-15	...		password hash (pass='a')
+
+---
+
+>> 0xA6
+description:
+ change password
+
+ Password hash is a md5 sum of the unicode password including the terminating
+ null. So for a password of 'a' the following byte stream is fead to the md5
+ function: 0x61 0x00 0x00 0x00 == UNICODE "a\0"
+
+type: action
+
+command:
+
+ CBWCB = 0xFF 0xA6 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+data:
+
+ 0000  33 2c e7 85 e9 73 57 4a  1c 5f da f3 ee e3 f0 83  |3,...sWJ._......|
+ 0016  c0 51 c1 bb 98 b7 1c cb  15 b0 cf 9c 67 d1 43 ee  |.Q..........g.C.|
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0-15	...		Old password hash ( pass='a')
+ 16-31	...		New password hash ( pass='b')
+
+---
+
+>> 0xA7
+description:
+ Remove security
+
+ Password hash is a md5 sum of the unicode password including the terminating
+ null. So for a password of 'a' the following byte stream is fead to the md5
+ function: 0x61 0x00 0x00 0x00 == UNICODE "a\0"
+
+ hmm... if security zone size != size of data partition, then this fails!!!
+ it returns a failed status but doesn't increase the password try counter,
+ even if password was incorrect.... to remove the secure zone if it doesn't
+ fully occupy the data partition, recreate the secure zone with maximum size.
+ > Possible cause: If this command is issued the secure zone becomes the public
+ zone, and thus all data on the disk will be retained. It is suspected that all
+ partitions/zones are stored encrypted on the flash device(Yes, even the public
+ zone). So, if this command is issued the secure zone key is decrypted(if
+ encrypted at all) and the zone is marked as public. Logically this would not
+ work if there is a public and secure zone. Then you would end up with two
+ public zone's with different encryptions keys.
+
+ Byte 3 of command does something... but still doesn't allow for removing half
+ secure zones.
+
+type: action
+
+command:
+
+ CBWCB = 0xFF 0xA7 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+data:
+
+ 0000  c0 51 c1 bb 98 b7 1c cb  15 b0 cf 9c 67 d1 43 ee  |.Q..........g.C.|
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0-15	...		Password hash (in this case 'b')
+
+---
+
+>> 0x100
+description:
+ seen used after a 0xA4(with some normal scsi stuff in between...).
+ generate reset some sort of reset or insert condition on data disk. Linux old 2.4
+ usb-storage sees it as a disconnect of the drive.
+
+type: 
+
+command:
+
+ CBWCB = 0xFF 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+data:
+ No data....
+
+
+---
+
+>> 0x101
+description:
+ disconnect's and possibly reconnects device
+
+type: action
+
+command:
+
+ CBWCB = 0xFF 0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+data:
+  12 bytes
+
+  0000  50 00 00 00 40 9c 00 00  01 00 00 00              |P...@.......|
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 8	0x01		If 1 reconnect after disconnect, else not
+ all other byte's dont seem to have any effect...
+
+---
+
+>> 0x103
+description:
+Get chip maker and version
+
+type: request
+
+command:
+
+ CBWCB = 0xFF 0x03 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+data:
+ 24 bytes
+
+ 0000  33 2e 32 31 00 00 00 00  53 61 6e 44 69 73 6b 20  |3.21....SanDisk |
+ 0016  00 00 00 00 00 00 00 00                           |........|
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+ 0-7	"3.21"		Chip version
+ 8-23	"SanDisk"	Chip maker
+
+
+
+
+
+
+
+possible read commands:
+0x20
+0x21
+0x61
+0x63
+0x68
+0x6b 512?
+0x81 128-byte
+0x84 64-byte
+0x85 64-byte
+0x88 64-byte
+0xa1 4-byte
+0xc1
+0xe2 = read random?, 64-byte
+
+0x102 512-byte
+
+Write:
+0x01 -> 0x1f
+0x22 -> 0x40
+0x42
+0x60
+0x62
+0x6a
+0x6c
+0x6d
+0x6e
+0x82 128 byte
+0x83 64 byte?
+0x86
+0x87
+0xc0
+0xc2
+
+
+
+
+---
+
+>>
+description:
+
+type: 
+
+command:
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+data:
+
+
+ byte	data		description
+ ------------------------------------------------------------------------------
+
+

+ 35 - 0
doc/u3-tool.1

@@ -0,0 +1,35 @@
+.TH "u3-tool" 1
+.SH NAME
+u3-tool \- Tool for controlling the special features of an U3 USB Flash disk. 
+.SH SYNOPSIS
+.B u3-tool [-cdDehiuvV] [-l
+.I cd image
+.B ] [-p
+.I cd size
+.B ]
+.I device
+.SH DESCRIPTION
+This tool can be used to control some of the special features of U3 Flash disks.
+.SH OPTIONS
+.IP -c
+Change password
+.IP -d
+Disable device security
+.IP -D
+Dump all raw info(for debug)
+.IP -e
+Enable device security
+.IP -h
+Print this help message
+.IP -i
+Display device info
+.IP "-l <cd image>"
+Load CD image into device
+.IP "-p <cd size>"
+Repartition device
+.IP -u
+Unlock device
+.IP -v
+Use verbose output
+.IP -V
+Print version information

+ 40 - 0
src/Makefile

@@ -0,0 +1,40 @@
+OBJ = main.o secure_input.o display_progress.o md5.o u3_commands.o u3_error.o
+OBJ_SG = u3_scsi_sg.o
+OBJ_USB = u3_scsi_usb.o
+OBJ_DEBUG = u3_scsi_debug.o
+
+CFLAGS += -g -Wall
+
+.PHONY: all clean
+
+all: u3_tool u3_tool_sg
+
+u3_tool: $(OBJ) $(OBJ_USB)
+	$(CC) -o u3_tool $(OBJ) $(OBJ_USB) $(LDFLAGS) -lusb 
+
+u3_tool_sg: $(OBJ) $(OBJ_SG)
+	$(CC) -o u3_tool_sg $(OBJ) $(OBJ_SG) $(LDFLAGS)
+
+u3_tool_debug: $(OBJ) $(OBJ_DEBUG)
+	$(CC) -o u3_tool_debug $(OBJ) $(OBJ_DEBUG) $(LDFLAGS)
+
+main.o: main.c u3.h
+
+secure_input.o: secure_input.c secure_input.h
+
+display_progress.o: display_progress.c display_progress.h
+
+md5.o: md5.c md5.h
+
+u3_commands.o: u3_commands.c u3_commands.h u3.h
+
+u3_error.o: u3_error.c u3_error.h u3.h
+
+u3_scsi_sg.o: u3_scsi_sg.c u3_scsi.h sg_err.h u3.h
+
+u3_scsi_usb.o: u3_scsi_usb.c u3_scsi.h u3.h
+
+u3_scsi_debug.o: u3_scsi_debug.c u3_scsi.h u3.h
+
+clean:
+	rm -f u3_tool u3_tool_sg u3_tool_debug $(OBJ) $(OBJ_SG) $(OBJ_USB) $(OBJ_DEBUG)

+ 48 - 0
src/Makefile.win

@@ -0,0 +1,48 @@
+# Project: u3_tool
+# Makefile created by Dev-C++ 4.9.9.2
+
+CPP  = g++.exe
+CC   = gcc.exe
+WINDRES = windres.exe
+RES  = 
+OBJ  = display_progress.o main.o md5.o secure_input.o u3_commands.o u3_error.o u3_scsi_spt.o $(RES)
+LINKOBJ  = display_progress.o main.o md5.o secure_input.o u3_commands.o u3_error.o u3_scsi_spt.o $(RES)
+LIBS =  -L"C:/Dev-Cpp/lib"  
+INCS =  -I"C:/Dev-Cpp/include" 
+CXXINCS =  -I"C:/Dev-Cpp/lib/gcc/mingw32/3.4.2/include"  -I"C:/Dev-Cpp/include/c++/3.4.2/backward"  -I"C:/Dev-Cpp/include/c++/3.4.2/mingw32"  -I"C:/Dev-Cpp/include/c++/3.4.2"  -I"C:/Dev-Cpp/include" 
+BIN  = u3_tool.exe
+CXXFLAGS = $(CXXINCS)  
+CFLAGS = $(INCS)  
+RM = rm -f
+
+.PHONY: all all-before all-after clean clean-custom
+
+all: all-before u3_tool.exe all-after
+
+
+clean: clean-custom
+	${RM} $(OBJ) $(BIN)
+
+$(BIN): $(OBJ)
+	$(CPP) $(LINKOBJ) -o "u3_tool.exe" $(LIBS)
+
+display_progress.o: display_progress.c
+	$(CPP) -c display_progress.c -o display_progress.o $(CXXFLAGS)
+
+main.o: main.c
+	$(CPP) -c main.c -o main.o $(CXXFLAGS)
+
+md5.o: md5.c
+	$(CPP) -c md5.c -o md5.o $(CXXFLAGS)
+
+secure_input.o: secure_input.c
+	$(CPP) -c secure_input.c -o secure_input.o $(CXXFLAGS)
+
+u3_commands.o: u3_commands.c
+	$(CPP) -c u3_commands.c -o u3_commands.o $(CXXFLAGS)
+
+u3_error.o: u3_error.c
+	$(CPP) -c u3_error.c -o u3_error.o $(CXXFLAGS)
+
+u3_scsi_spt.o: u3_scsi_spt.c
+	$(CPP) -c u3_scsi_spt.c -o u3_scsi_spt.o $(CXXFLAGS)

+ 25 - 0
src/display_progress.c

@@ -0,0 +1,25 @@
+#include "display_progress.h"
+#include <stdio.h>
+
+void display_progress(unsigned int cur, unsigned int total) {
+        unsigned int procent;
+	unsigned int bar_len;
+	int i;
+
+	if (total == 0) return;
+
+	procent = (cur * 100) / total;
+
+	putchar('|');
+	bar_len = (cur * PROGRESS_BAR_WIDTH) / total;
+	for (i =0; i < bar_len; i++) {
+		putchar('*');
+	}
+	for (i = bar_len; i < PROGRESS_BAR_WIDTH; i++) {
+		putchar(' ');
+	}
+	putchar('|');
+
+	printf(" %d%%\r", procent);
+	fflush(stdout);
+}

+ 14 - 0
src/display_progress.h

@@ -0,0 +1,14 @@
+#ifndef __DISPLAY_PROGRESS_H__
+#define __DISPLAY_PROGRESS_H__
+
+#define PROGRESS_BAR_WIDTH 50 // width of the progress bar on screen
+
+/**
+ * Display progress of an operation
+ *
+ * @param cur		Number of current itteration
+ * @param total		Total number of iterations
+ */
+void display_progress(unsigned int cur, unsigned int total);
+ 
+#endif // __DISPLAY_PROGRESS_H__

+ 578 - 0
src/main.c

@@ -0,0 +1,578 @@
+/**
+ * u3_tool - U3 USB stick manager
+ * Copyright (C) 2007 Daviedev, daviedev@users.sourceforge.net
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "u3.h"
+#include "u3_commands.h"
+#include "u3_scsi.h"
+#include "u3_error.h"
+
+#include "secure_input.h"
+#include "display_progress.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define MAX_SIZE_STRING_LENGTH 100
+#define MAX_FILENAME_STRING_LENGTH 1024
+#define MAX_PASSWORD_LENGTH 1024
+
+char *version = "0.1";
+
+int debug = 0;
+int batch_mode = 0;
+
+enum action_t { unknown, load, partition, dump, unlock, change_password,
+		enable_security, disable_security };
+
+/**
+ * Ask confirmation of user.
+ *
+ * @returns	TRUE if user wants to continue else FALSE to abort.
+ */
+int confirm() {
+	char c;
+	int retval;
+	int done;
+
+	if (batch_mode != 0)
+		return TRUE;
+
+	retval = FALSE;
+	done = FALSE;
+	do {
+		printf("\nAre you sure you want to continue? [yn] ");
+
+		c = fgetc(stdin);
+
+		if (c == 'y' || c == 'Y') {
+			retval = TRUE;
+			done = TRUE;
+		} else if (c == 'n' || c == 'N') {
+			retval = FALSE;
+			done = TRUE;
+		}
+
+		// drop any extra chars and the new line
+		while (c != '\n' && c != EOF) {
+			c = fgetc(stdin);
+		}
+	} while (!done);
+
+	return retval;
+}
+
+/********************************** Actions ***********************************/
+
+int do_load(u3_handle_t *device, char *iso_filename) {
+	struct stat file_stat;
+	struct part_info pinfo;
+	uint32_t cd_size;
+	FILE *fp;
+	uint8_t	buffer[U3_BLOCK_SIZE];
+	unsigned int bytes_read=0;
+	unsigned int block_num=0;
+	unsigned int block_cnt=0;
+
+	// determine file size
+	if (stat(iso_filename, &file_stat) == -1) {
+		perror("Failed stating iso file");
+		return EXIT_FAILURE;
+	}
+	if (file_stat.st_size == 0) {
+		fprintf(stderr, "ISO file is empty\n");
+		return EXIT_FAILURE;
+	}
+
+	cd_size = file_stat.st_size / U3_SECTOR_SIZE;
+	if (file_stat.st_size % U3_SECTOR_SIZE)
+		cd_size++;
+	block_cnt = file_stat.st_size / U3_BLOCK_SIZE;
+	if (file_stat.st_size % U3_BLOCK_SIZE)
+		block_cnt++;
+
+	// check partition size
+	if (u3_partition_info(device, &pinfo) != U3_SUCCESS) {
+		fprintf(stderr, "u3_partition_info() failed: %s\n",
+			u3_error_msg(device));
+		return EXIT_FAILURE;
+	}
+
+	if (cd_size > pinfo.cd_size) {
+		fprintf(stderr, "CD image(%lu byte) is to big for current cd "
+			"partition(%u byte), please repartition device.\n",
+			file_stat.st_size,
+			pinfo.cd_size * U3_SECTOR_SIZE);
+		return EXIT_FAILURE;
+	}
+
+	// open file
+	if ((fp = fopen(iso_filename, "rb")) == NULL) {
+		perror("Failed opening iso file");
+		return EXIT_FAILURE;
+	}
+
+	// write file to device
+	block_num = 0;
+	do {
+		display_progress(block_num, block_cnt);
+
+		bytes_read = fread(buffer, sizeof(uint8_t), U3_BLOCK_SIZE, fp);
+		if (bytes_read != U3_BLOCK_SIZE) {
+			if (ferror(fp)) {
+				fclose(fp);
+				perror("\nFailed reading iso file");
+				return EXIT_FAILURE;
+			} else if (bytes_read == 0) {
+				continue;
+			} else if (bytes_read > U3_BLOCK_SIZE) {
+				fprintf(stderr, "\nRead more bytes then requested, impossible");
+				return EXIT_FAILURE;
+			} else {
+				// zeroize rest of buffer to prevent writing garbage
+				memset(buffer+bytes_read, 0, U3_BLOCK_SIZE-bytes_read);
+			}
+		} 
+		
+		if (u3_cd_write(device, block_num, buffer) != U3_SUCCESS) {
+			fclose(fp);
+			fprintf(stderr, "\nu3_cd_write() failed: %s\n", u3_error_msg(device));
+			return EXIT_FAILURE;
+		}
+
+		block_num++;
+	} while (!feof(fp));
+	display_progress(block_num, block_cnt);
+	putchar('\n');
+
+	fclose(fp);
+
+	printf("OK\n");
+	return EXIT_SUCCESS;
+}
+
+int do_partition(u3_handle_t *device, char *size_string) {
+	uint64_t size;
+	uint32_t cd_sectors;
+
+	// determine file size
+	size = strtoll(size_string, NULL, 0);
+	cd_sectors = size / U3_SECTOR_SIZE;
+	if (size % U3_SECTOR_SIZE)
+		cd_sectors++;
+
+	// repartition
+	if (u3_partition(device, cd_sectors) != U3_SUCCESS) {
+		fprintf(stderr, "u3_partition() failed: %s\n", u3_error_msg(device));
+		return EXIT_FAILURE;
+	}
+
+	// reset device to make partitioning active
+	if (u3_reset(device) != U3_SUCCESS) {
+		fprintf(stderr, "u3_reset() failed: %s\n", u3_error_msg(device));
+		return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
+}
+
+int do_unlock(u3_handle_t *device, char *password) {
+	int result=0;
+
+	if (u3_unlock(device, password, &result) != U3_SUCCESS) {
+		fprintf(stderr, "u3_unlock() failed: %s\n", u3_error_msg(device));
+		return EXIT_FAILURE;
+	}
+
+	if (result) {
+		printf("OK\n");
+		return EXIT_SUCCESS;
+	} else {
+		printf("Password incorrect\n");
+		return EXIT_FAILURE;
+	}
+}
+
+int do_change_password(u3_handle_t *device, char *password,
+	char *new_password)
+{
+	int result=0;
+
+	if (u3_change_password(device, password, new_password, &result)
+		!= U3_SUCCESS)
+	{
+		fprintf(stderr, "u3_change_password() failed: %s\n", u3_error_msg(device));
+		return EXIT_FAILURE;
+	}
+
+	if (result) {
+		printf("OK\n");
+		return EXIT_SUCCESS;
+	} else {
+		printf("Password incorrect\n");
+		return EXIT_FAILURE;
+	}
+}
+
+int do_enable_security(u3_handle_t *device, char *password) {
+	if (u3_enable_security(device, password) != U3_SUCCESS) {
+		fprintf(stderr, "u3_enable_security() failed: %s\n",
+			u3_error_msg(device));
+		return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
+}
+
+int do_disable_security(u3_handle_t *device, char *password) {
+	int result=0;
+
+	if (u3_disable_security(device, password, &result) != U3_SUCCESS) {
+		fprintf(stderr, "u3_disable_security() failed: %s\n",
+			u3_error_msg(device));
+		return EXIT_FAILURE;
+	}
+
+	if (result) {
+		printf("OK\n");
+		return EXIT_SUCCESS;
+	} else {
+		printf("Password incorrect\n");
+		return EXIT_FAILURE;
+	}
+}
+
+int do_dump(u3_handle_t *device) {
+	int retval = EXIT_SUCCESS;
+
+	struct part_info pinfo;
+	struct dpart_info dpinfo;
+	struct chip_info cinfo;
+	struct property_03 device_properties;
+	struct property_0C security_properties;
+	
+	if (u3_partition_info(device, &pinfo) != U3_SUCCESS) {
+		fprintf(stderr, "u3_partition_info() failed: %s\n",
+			u3_error_msg(device));
+		retval = EXIT_FAILURE;
+	} else {
+		printf("Partition info:\n");
+		printf(" - Partition count: 0x%.2x\n", pinfo.partition_count);
+		printf(" - Data partition size: %d byte(0x%.8x)\n",
+			pinfo.data_size * U3_SECTOR_SIZE, pinfo.data_size);
+		printf(" - Unknown1: 0x%.8x\n", pinfo.unknown1);
+		printf(" - CD size: %d byte(0x%.8x)\n", pinfo.cd_size*U3_SECTOR_SIZE,
+			pinfo.cd_size);
+		printf(" - Unknown2: 0x%.8x\n", pinfo.unknown2);
+		printf("\n");
+	}
+
+	if (u3_data_partition_info(device, &dpinfo) != U3_SUCCESS) {
+		fprintf(stderr, "u3_data_partition_info() failed: %s\n",
+			u3_error_msg(device));
+		retval = EXIT_FAILURE;
+	} else {
+		printf("Data partition info:\n");
+		printf(" - Data partition size: %d byte(0x%.8x)\n",
+			dpinfo.total_size * U3_SECTOR_SIZE, dpinfo.total_size);
+		printf(" - Secured zone size: 0x%.8x\n", dpinfo.secured_size);
+		printf(" - Unlocked: 0x%.8x\n", dpinfo.unlocked);
+		printf(" - Password try: 0x%.8x\n", dpinfo.pass_try);
+		printf("\n");
+	}
+
+	if (u3_chip_info(device, &cinfo) != U3_SUCCESS) {
+		fprintf(stderr, "u3_chip_info() failed: %s\n",
+			u3_error_msg(device));
+		retval = EXIT_FAILURE;
+	} else {
+		printf("Chip info:\n");
+		printf(" - Manufacturer: %.*s\n", U3_MAX_CHIP_MANUFACTURER_LEN,
+			cinfo.manufacturer);
+		printf(" - Revision: %.*s\n", U3_MAX_CHIP_REVISION_LEN,
+			cinfo.revision);
+		printf("\n");
+	}
+
+	if (u3_read_device_property(device, 0x03,
+				(uint8_t *) &device_properties,
+				sizeof(device_properties)
+				) != U3_SUCCESS)
+	{
+		fprintf(stderr, "u3_read_device_property() failed for "
+			"property 0x03: %s\n", u3_error_msg(device));
+		retval = EXIT_FAILURE;
+	} else {
+		if (device_properties.hdr.length !=
+				sizeof(device_properties))
+		{
+			fprintf(stderr, "Length of property 0x03 is not the "
+				"expected length. (len=%u)\n",
+				device_properties.hdr.length);
+			retval = EXIT_FAILURE;
+		} else {
+			printf("Property page 0x03:\n");
+			printf(" - Device size: 0x%.8x\n",
+				device_properties.device_size);
+			printf(" - Device serial: %.*s\n",U3_MAX_SERIAL_LEN,
+				device_properties.serial);
+			printf(" - Full record length: 0x%.8x\n",
+				device_properties.full_length);
+			printf(" - Unknown1: 0x%.2x\n",
+				device_properties.unknown1);
+			printf(" - Unknown2: 0x%.8x\n",
+				device_properties.unknown2);
+			printf(" - Unknown3: 0x%.8x\n",
+				device_properties.unknown3);
+			printf("\n");
+		}
+	}
+
+
+
+	if (u3_read_device_property(device, 0x0C,
+				(uint8_t *) &security_properties,
+				sizeof(security_properties)
+				) != U3_SUCCESS)
+	{
+		fprintf(stderr, "u3_read_device_property() failed for "
+			"property 0x0C: %s\n", u3_error_msg(device));
+		retval = EXIT_FAILURE;
+	} else {
+		if (security_properties.hdr.length !=
+				sizeof(security_properties))
+		{
+			fprintf(stderr, "Length of property 0x0C is not the "
+				"expected length. (len=%u)\n",
+				security_properties.hdr.length);
+			retval = EXIT_FAILURE;
+		} else {
+			printf("Property page 0x0C:\n");
+			printf(" - Max. pass. try: %u",
+				security_properties.max_pass_try);
+			printf("\n");
+		}
+	}
+
+	return EXIT_SUCCESS;
+}
+
+/************************************ Main ************************************/
+
+void usage(const char *name) {
+	printf("u3_tool %s\n", version);
+	printf("U3 USB stick manager\n");
+	printf("\n");
+	printf("Usage: %s [options] <device name>\n", name);
+	printf("\n");
+	printf("Options:\n");
+	printf("\t-c                Change password\n");
+	printf("\t-d                Disable device security\n");
+	printf("\t-D                Dump all raw info(for debug)\n");
+	printf("\t-e                Enable device security\n");
+	printf("\t-h                Print this help message\n");
+//TODO:	printf("\t-i                Display device info\n");
+	printf("\t-l <cd image>     Load CD image into device\n");
+	printf("\t-p <cd size>      Repartition device\n");
+	printf("\t-u                Unlock device\n");
+	printf("\t-v                Use verbose output\n");
+	printf("\t-V                Print version information\n");
+	printf("\n");
+	printf("The device name depends on the used subsystem.\n");
+	printf("Examples: '/dev/sda0'(sg), 'scan'(USB),'vid:pid'(USB), "
+			"'e'(Windows)\n");
+}
+
+void print_version() {
+	printf("u3_tool %s\n", version);
+	printf("\n");
+	printf("Copyright (C) 2007\n");
+	printf("This is free software; see the source for copying "
+		"conditions. There is NO\n warranty; not even for "
+		"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
+}
+
+int main(int argc, char *argv[]) {
+	u3_handle_t device;
+
+	int c;
+	enum action_t action = unknown;
+	char *device_name;
+
+	char	filename_string[MAX_FILENAME_STRING_LENGTH+1];
+	char	size_string[MAX_SIZE_STRING_LENGTH+1];
+
+	int	ask_password = TRUE;
+	char	password[MAX_PASSWORD_LENGTH+1];
+
+	int	ask_new_password = TRUE;
+	char	new_password[MAX_PASSWORD_LENGTH+1];
+
+	int retval = EXIT_SUCCESS;
+
+	//
+	// parse options
+	//
+	while ((c = getopt(argc, argv, "cdDehl:p:uvVz")) != -1) {
+		switch (c) {
+			case 'c':
+				action = change_password;
+				break;
+			case 'd':
+				action = disable_security;
+				break;
+			case 'e':
+				action = enable_security;
+				break;
+			case 'l':
+				action = load;
+				strncpy(filename_string, optarg, MAX_FILENAME_STRING_LENGTH);
+				filename_string[MAX_FILENAME_STRING_LENGTH] = '\0';
+				break;
+			case 'p':
+				action = partition;
+				strncpy(size_string, optarg, MAX_SIZE_STRING_LENGTH);
+				size_string[MAX_SIZE_STRING_LENGTH] = '\0';
+				break;
+			case 'u':
+				action = unlock;
+				break;
+			case 'v':
+				debug = 1;
+				break;
+			case 'V':
+				print_version();
+				exit(EXIT_SUCCESS);
+				break;
+			case 'D':
+				action = dump;
+				break;
+			case 'h':
+			default:
+				usage(argv[0]);
+				exit(EXIT_FAILURE);
+				break;
+		}
+	}
+
+	//
+	// parse arguments
+	//
+	if (argc-optind < 1) {
+		fprintf(stderr, "Not enough arguments\n");
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+	device_name = argv[optind];
+		
+	if ((action == unlock || action == change_password
+	     || action == disable_security)
+	     && ask_password)
+	{
+		int proceed = 0;
+		do {
+			fprintf(stderr, "Enter password: ");
+			secure_input(password, sizeof(password));
+			if (strlen(password) == 0) {
+				fprintf(stderr, "Password Empty\n");
+			} else {
+				proceed = 1;
+			}
+		} while (!proceed);
+	}
+
+	if ((action == change_password || action == enable_security)
+		&& ask_new_password)
+	{
+		int proceed = 0;
+		char validate_password[MAX_PASSWORD_LENGTH+1];
+		do {
+			fprintf(stderr, "Enter new password: ");
+			secure_input(new_password, sizeof(new_password));
+			if (strlen(new_password) == 0) {
+				fprintf(stderr, "Password Empty\n");
+				continue;
+			}
+
+			fprintf(stderr, "Reenter new password: ");
+			secure_input(validate_password, sizeof(validate_password));
+			if (strcmp(new_password, validate_password) == 0) {
+				proceed = 1;
+			} else {
+				fprintf(stderr, "Passwords don't match\n");
+			}
+		} while (!proceed);
+	}
+
+	//
+	// open the device
+	// 
+	if (u3_open(&device, device_name)) {
+		fprintf(stderr, "Error opening device: %s\n", u3_error_msg(&device));
+		exit(EXIT_FAILURE);
+	}
+
+	//
+	// preform action
+	//
+	switch (action) {
+		case load:
+			retval = do_load(&device, filename_string);
+			break;
+		case partition:
+			printf("\n");
+			printf("WARNING: Loading a new cd image causes the ");
+			printf("whole device to be whiped. This INCLUDES\n ");
+			printf("the data partition.\n");
+			printf("I repeat: ANY EXCISTING DATA WILL BE LOST!\n");
+			if (confirm())
+				retval = do_partition(&device, size_string);
+			break;
+		case dump:
+			retval = do_dump(&device);
+			break;
+		case unlock:
+			retval = do_unlock(&device, password);
+			break;
+		case change_password:
+			retval = do_change_password(&device, password, new_password);
+			break;
+		case enable_security:
+			retval = do_enable_security(&device, new_password);
+			break;
+		case disable_security:
+			retval = do_disable_security(&device, password);
+			break;
+		default:
+			printf("Don't know what to do... please specify an action\n");
+			break;
+	}
+
+	//
+	// clean up
+	//
+	u3_close(&device);
+
+	return retval;
+}

+ 460 - 0
src/md5.c

@@ -0,0 +1,460 @@
+/*
+ *  RFC 1321 compliant MD5 implementation
+ *
+ *  Copyright (C) 2006-2007  Christophe Devine
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License, version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+/*
+ *  The MD5 algorithm was designed by Ron Rivest in 1991.
+ *
+ *  http://www.ietf.org/rfc/rfc1321.txt
+ */
+
+#ifndef _CRT_SECURE_NO_DEPRECATE
+#define _CRT_SECURE_NO_DEPRECATE 1
+#endif
+
+#include <string.h>
+#include <stdio.h>
+
+#include "md5.h"
+
+/*
+ * 32-bit integer manipulation macros (little endian)
+ */
+#ifndef GET_UINT32_LE
+#define GET_UINT32_LE(n,b,i)                            \
+{                                                       \
+    (n) = ( (unsigned long) (b)[(i)    ]       )        \
+        | ( (unsigned long) (b)[(i) + 1] <<  8 )        \
+        | ( (unsigned long) (b)[(i) + 2] << 16 )        \
+        | ( (unsigned long) (b)[(i) + 3] << 24 );       \
+}
+#endif
+
+#ifndef PUT_UINT32_LE
+#define PUT_UINT32_LE(n,b,i)                            \
+{                                                       \
+    (b)[(i)    ] = (unsigned char) ( (n)       );       \
+    (b)[(i) + 1] = (unsigned char) ( (n) >>  8 );       \
+    (b)[(i) + 2] = (unsigned char) ( (n) >> 16 );       \
+    (b)[(i) + 3] = (unsigned char) ( (n) >> 24 );       \
+}
+#endif
+
+/*
+ * MD5 context setup
+ */
+void md5_starts( md5_context *ctx )
+{
+    ctx->total[0] = 0;
+    ctx->total[1] = 0;
+
+    ctx->state[0] = 0x67452301;
+    ctx->state[1] = 0xEFCDAB89;
+    ctx->state[2] = 0x98BADCFE;
+    ctx->state[3] = 0x10325476;
+}
+
+static void md5_process( md5_context *ctx, unsigned char data[64] )
+{
+    unsigned long X[16], A, B, C, D;
+
+    GET_UINT32_LE( X[ 0], data,  0 );
+    GET_UINT32_LE( X[ 1], data,  4 );
+    GET_UINT32_LE( X[ 2], data,  8 );
+    GET_UINT32_LE( X[ 3], data, 12 );
+    GET_UINT32_LE( X[ 4], data, 16 );
+    GET_UINT32_LE( X[ 5], data, 20 );
+    GET_UINT32_LE( X[ 6], data, 24 );
+    GET_UINT32_LE( X[ 7], data, 28 );
+    GET_UINT32_LE( X[ 8], data, 32 );
+    GET_UINT32_LE( X[ 9], data, 36 );
+    GET_UINT32_LE( X[10], data, 40 );
+    GET_UINT32_LE( X[11], data, 44 );
+    GET_UINT32_LE( X[12], data, 48 );
+    GET_UINT32_LE( X[13], data, 52 );
+    GET_UINT32_LE( X[14], data, 56 );
+    GET_UINT32_LE( X[15], data, 60 );
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define P(a,b,c,d,k,s,t)                                \
+{                                                       \
+    a += F(b,c,d) + X[k] + t; a = S(a,s) + b;           \
+}
+
+    A = ctx->state[0];
+    B = ctx->state[1];
+    C = ctx->state[2];
+    D = ctx->state[3];
+
+#define F(x,y,z) (z ^ (x & (y ^ z)))
+
+    P( A, B, C, D,  0,  7, 0xD76AA478 );
+    P( D, A, B, C,  1, 12, 0xE8C7B756 );
+    P( C, D, A, B,  2, 17, 0x242070DB );
+    P( B, C, D, A,  3, 22, 0xC1BDCEEE );
+    P( A, B, C, D,  4,  7, 0xF57C0FAF );
+    P( D, A, B, C,  5, 12, 0x4787C62A );
+    P( C, D, A, B,  6, 17, 0xA8304613 );
+    P( B, C, D, A,  7, 22, 0xFD469501 );
+    P( A, B, C, D,  8,  7, 0x698098D8 );
+    P( D, A, B, C,  9, 12, 0x8B44F7AF );
+    P( C, D, A, B, 10, 17, 0xFFFF5BB1 );
+    P( B, C, D, A, 11, 22, 0x895CD7BE );
+    P( A, B, C, D, 12,  7, 0x6B901122 );
+    P( D, A, B, C, 13, 12, 0xFD987193 );
+    P( C, D, A, B, 14, 17, 0xA679438E );
+    P( B, C, D, A, 15, 22, 0x49B40821 );
+
+#undef F
+
+#define F(x,y,z) (y ^ (z & (x ^ y)))
+
+    P( A, B, C, D,  1,  5, 0xF61E2562 );
+    P( D, A, B, C,  6,  9, 0xC040B340 );
+    P( C, D, A, B, 11, 14, 0x265E5A51 );
+    P( B, C, D, A,  0, 20, 0xE9B6C7AA );
+    P( A, B, C, D,  5,  5, 0xD62F105D );
+    P( D, A, B, C, 10,  9, 0x02441453 );
+    P( C, D, A, B, 15, 14, 0xD8A1E681 );
+    P( B, C, D, A,  4, 20, 0xE7D3FBC8 );
+    P( A, B, C, D,  9,  5, 0x21E1CDE6 );
+    P( D, A, B, C, 14,  9, 0xC33707D6 );
+    P( C, D, A, B,  3, 14, 0xF4D50D87 );
+    P( B, C, D, A,  8, 20, 0x455A14ED );
+    P( A, B, C, D, 13,  5, 0xA9E3E905 );
+    P( D, A, B, C,  2,  9, 0xFCEFA3F8 );
+    P( C, D, A, B,  7, 14, 0x676F02D9 );
+    P( B, C, D, A, 12, 20, 0x8D2A4C8A );
+
+#undef F
+    
+#define F(x,y,z) (x ^ y ^ z)
+
+    P( A, B, C, D,  5,  4, 0xFFFA3942 );
+    P( D, A, B, C,  8, 11, 0x8771F681 );
+    P( C, D, A, B, 11, 16, 0x6D9D6122 );
+    P( B, C, D, A, 14, 23, 0xFDE5380C );
+    P( A, B, C, D,  1,  4, 0xA4BEEA44 );
+    P( D, A, B, C,  4, 11, 0x4BDECFA9 );
+    P( C, D, A, B,  7, 16, 0xF6BB4B60 );
+    P( B, C, D, A, 10, 23, 0xBEBFBC70 );
+    P( A, B, C, D, 13,  4, 0x289B7EC6 );
+    P( D, A, B, C,  0, 11, 0xEAA127FA );
+    P( C, D, A, B,  3, 16, 0xD4EF3085 );
+    P( B, C, D, A,  6, 23, 0x04881D05 );
+    P( A, B, C, D,  9,  4, 0xD9D4D039 );
+    P( D, A, B, C, 12, 11, 0xE6DB99E5 );
+    P( C, D, A, B, 15, 16, 0x1FA27CF8 );
+    P( B, C, D, A,  2, 23, 0xC4AC5665 );
+
+#undef F
+
+#define F(x,y,z) (y ^ (x | ~z))
+
+    P( A, B, C, D,  0,  6, 0xF4292244 );
+    P( D, A, B, C,  7, 10, 0x432AFF97 );
+    P( C, D, A, B, 14, 15, 0xAB9423A7 );
+    P( B, C, D, A,  5, 21, 0xFC93A039 );
+    P( A, B, C, D, 12,  6, 0x655B59C3 );
+    P( D, A, B, C,  3, 10, 0x8F0CCC92 );
+    P( C, D, A, B, 10, 15, 0xFFEFF47D );
+    P( B, C, D, A,  1, 21, 0x85845DD1 );
+    P( A, B, C, D,  8,  6, 0x6FA87E4F );
+    P( D, A, B, C, 15, 10, 0xFE2CE6E0 );
+    P( C, D, A, B,  6, 15, 0xA3014314 );
+    P( B, C, D, A, 13, 21, 0x4E0811A1 );
+    P( A, B, C, D,  4,  6, 0xF7537E82 );
+    P( D, A, B, C, 11, 10, 0xBD3AF235 );
+    P( C, D, A, B,  2, 15, 0x2AD7D2BB );
+    P( B, C, D, A,  9, 21, 0xEB86D391 );
+
+#undef F
+
+    ctx->state[0] += A;
+    ctx->state[1] += B;
+    ctx->state[2] += C;
+    ctx->state[3] += D;
+}
+
+/*
+ * MD5 process buffer
+ */
+void md5_update( md5_context *ctx, unsigned char *input, int ilen )
+{
+    int fill;
+    unsigned long left;
+
+    if( ilen <= 0 )
+        return;
+
+    left = ctx->total[0] & 0x3F;
+    fill = 64 - left;
+
+    ctx->total[0] += ilen;
+    ctx->total[0] &= 0xFFFFFFFF;
+
+    if( ctx->total[0] < (unsigned long) ilen )
+        ctx->total[1]++;
+
+    if( left && ilen >= fill )
+    {
+        memcpy( (void *) (ctx->buffer + left),
+                (void *) input, fill );
+        md5_process( ctx, ctx->buffer );
+        input += fill;
+        ilen  -= fill;
+        left = 0;
+    }
+
+    while( ilen >= 64 )
+    {
+        md5_process( ctx, input );
+        input += 64;
+        ilen  -= 64;
+    }
+
+    if( ilen > 0 )
+    {
+        memcpy( (void *) (ctx->buffer + left),
+                (void *) input, ilen );
+    }
+}
+
+static const unsigned char md5_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * MD5 final digest
+ */
+void md5_finish( md5_context *ctx, unsigned char *output )
+{
+    unsigned long last, padn;
+    unsigned long high, low;
+    unsigned char msglen[8];
+
+    high = ( ctx->total[0] >> 29 )
+         | ( ctx->total[1] <<  3 );
+    low  = ( ctx->total[0] <<  3 );
+
+    PUT_UINT32_LE( low,  msglen, 0 );
+    PUT_UINT32_LE( high, msglen, 4 );
+
+    last = ctx->total[0] & 0x3F;
+    padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
+
+    md5_update( ctx, (unsigned char *) md5_padding, padn );
+    md5_update( ctx, msglen, 8 );
+
+    PUT_UINT32_LE( ctx->state[0], output,  0 );
+    PUT_UINT32_LE( ctx->state[1], output,  4 );
+    PUT_UINT32_LE( ctx->state[2], output,  8 );
+    PUT_UINT32_LE( ctx->state[3], output, 12 );
+}
+
+/*
+ * Output = MD5( input buffer )
+ */
+void md5( unsigned char *input, int ilen,
+          unsigned char *output )
+{
+    md5_context ctx;
+
+    md5_starts( &ctx );
+    md5_update( &ctx, input, ilen );
+    md5_finish( &ctx, output );
+
+    memset( &ctx, 0, sizeof( md5_context ) );
+}
+
+/*
+ * Output = MD5( file contents )
+ */
+int md5_file( char *path, unsigned char *output )
+{
+    FILE *f;
+    size_t n;
+    md5_context ctx;
+    unsigned char buf[1024];
+
+    if( ( f = fopen( path, "rb" ) ) == NULL )
+        return( 1 );
+
+    md5_starts( &ctx );
+
+    while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 )
+        md5_update( &ctx, buf, (int) n );
+
+    md5_finish( &ctx, output );
+
+    memset( &ctx, 0, sizeof( md5_context ) );
+
+    if( ferror( f ) != 0 )
+    {
+        fclose( f );
+        return( 2 );
+    }
+
+    fclose( f );
+    return( 0 );
+}
+
+/*
+ * MD5 HMAC context setup
+ */
+void md5_hmac_starts( md5_context *ctx,
+                      unsigned char *key, int keylen )
+{
+    int i;
+
+    memset( ctx->ipad, 0x36, 64 );
+    memset( ctx->opad, 0x5C, 64 );
+
+    for( i = 0; i < keylen; i++ )
+    {
+        if( i >= 64 ) break;
+
+        ctx->ipad[i] ^= key[i];
+        ctx->opad[i] ^= key[i];
+    }
+
+    md5_starts( ctx );
+    md5_update( ctx, ctx->ipad, 64 );
+}
+
+/*
+ * MD5 HMAC process buffer
+ */
+void md5_hmac_update( md5_context *ctx,
+                      unsigned char *input, int ilen )
+{
+    md5_update( ctx, input, ilen );
+}
+
+/*
+ * MD5 HMAC final digest
+ */
+void md5_hmac_finish( md5_context *ctx, unsigned char *output )
+{
+    unsigned char tmpbuf[16];
+
+    md5_finish( ctx, tmpbuf );
+    md5_starts( ctx );
+    md5_update( ctx, ctx->opad, 64 );
+    md5_update( ctx, tmpbuf, 16 );
+    md5_finish( ctx, output );
+
+    memset( tmpbuf, 0, sizeof( tmpbuf ) );
+}
+
+/*
+ * Output = HMAC-MD5( hmac key, input buffer )
+ */
+void md5_hmac( unsigned char *key, int keylen,
+               unsigned char *input, int ilen,
+               unsigned char *output )
+{
+    md5_context ctx;
+
+    md5_hmac_starts( &ctx, key, keylen );
+    md5_hmac_update( &ctx, input, ilen );
+    md5_hmac_finish( &ctx, output );
+
+    memset( &ctx, 0, sizeof( md5_context ) );
+}
+
+static const char _md5_src[] = "_md5_src";
+
+#if defined(SELF_TEST)
+/*
+ * RFC 1321 test vectors
+ */
+static const char md5_test_str[7][81] =
+{
+    { "" }, 
+    { "a" },
+    { "abc" },
+    { "message digest" },
+    { "abcdefghijklmnopqrstuvwxyz" },
+    { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" },
+    { "12345678901234567890123456789012345678901234567890123456789012" \
+      "345678901234567890" }
+};
+
+static const unsigned char md5_test_sum[7][16] =
+{
+    { 0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04,
+      0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E },
+    { 0x0C, 0xC1, 0x75, 0xB9, 0xC0, 0xF1, 0xB6, 0xA8,
+      0x31, 0xC3, 0x99, 0xE2, 0x69, 0x77, 0x26, 0x61 },
+    { 0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0,
+      0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1, 0x7F, 0x72 },
+    { 0xF9, 0x6B, 0x69, 0x7D, 0x7C, 0xB7, 0x93, 0x8D,
+      0x52, 0x5A, 0x2F, 0x31, 0xAA, 0xF1, 0x61, 0xD0 },
+    { 0xC3, 0xFC, 0xD3, 0xD7, 0x61, 0x92, 0xE4, 0x00,
+      0x7D, 0xFB, 0x49, 0x6C, 0xCA, 0x67, 0xE1, 0x3B },
+    { 0xD1, 0x74, 0xAB, 0x98, 0xD2, 0x77, 0xD9, 0xF5,
+      0xA5, 0x61, 0x1C, 0x2C, 0x9F, 0x41, 0x9D, 0x9F },
+    { 0x57, 0xED, 0xF4, 0xA2, 0x2B, 0xE3, 0xC9, 0x55,
+      0xAC, 0x49, 0xDA, 0x2E, 0x21, 0x07, 0xB6, 0x7A }
+};
+
+/*
+ * Checkup routine
+ */
+int md5_self_test( int verbose )
+{
+    int i;
+    unsigned char md5sum[16];
+
+    for( i = 0; i < 7; i++ )
+    {
+        if( verbose != 0 )
+            printf( "  MD5 test #%d: ", i + 1 );
+
+        md5( (unsigned char *) md5_test_str[i],
+             strlen( md5_test_str[i] ), md5sum );
+
+        if( memcmp( md5sum, md5_test_sum[i], 16 ) != 0 )
+        {
+            if( verbose != 0 )
+                printf( "failed\n" );
+
+            return( 1 );
+        }
+
+        if( verbose != 0 )
+            printf( "passed\n" );
+    }
+
+    if( verbose != 0 )
+        printf( "\n" );
+
+    return( 0 );
+}
+#else
+int md5_self_test( int verbose )
+{
+    return( 0 );
+}
+#endif

+ 121 - 0
src/md5.h

@@ -0,0 +1,121 @@
+/**
+ * \file md5.h
+ */
+#ifndef _MD5_H
+#define _MD5_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief          MD5 context structure
+ */
+typedef struct
+{
+    unsigned long total[2];     /*!< number of bytes processed  */
+    unsigned long state[4];     /*!< intermediate digest state  */
+    unsigned char buffer[64];   /*!< data block being processed */
+    unsigned char ipad[64];     /*!< HMAC: inner padding        */
+    unsigned char opad[64];     /*!< HMAC: outer padding        */
+}
+md5_context;
+
+/**
+ * \brief          MD5 context setup
+ *
+ * \param ctx      context to be initialized
+ */
+void md5_starts( md5_context *ctx );
+
+/**
+ * \brief          MD5 process buffer
+ *
+ * \param ctx      MD5 context
+ * \param input    buffer holding the  data
+ * \param ilen     length of the input data
+ */
+void md5_update( md5_context *ctx, unsigned char *input, int ilen );
+
+/**
+ * \brief          MD5 final digest
+ *
+ * \param ctx      MD5 context
+ * \param output   MD5 checksum result
+ */
+void md5_finish( md5_context *ctx, unsigned char *output );
+
+/**
+ * \brief          Output = MD5( input buffer )
+ *
+ * \param input    buffer holding the  data
+ * \param ilen     length of the input data
+ * \param output   MD5 checksum result
+ */
+void md5( unsigned char *input, int ilen,
+          unsigned char *output );
+
+/**
+ * \brief          Output = MD5( file contents )
+ *
+ * \param path     input file name
+ * \param output   MD5 checksum result
+ *
+ * \return         0 if successful, 1 if fopen failed,
+ *                 or 2 if fread failed
+ */
+int md5_file( char *path, unsigned char *output );
+
+/**
+ * \brief          MD5 HMAC context setup
+ *
+ * \param ctx      HMAC context to be initialized
+ * \param key      HMAC secret key
+ * \param keylen   length of the HMAC key
+ */
+void md5_hmac_starts( md5_context *ctx,
+                      unsigned char *key, int keylen );
+
+/**
+ * \brief          MD5 HMAC process buffer
+ *
+ * \param ctx      HMAC context
+ * \param input    buffer holding the  data
+ * \param ilen     length of the input data
+ */
+void md5_hmac_update( md5_context *ctx,
+                      unsigned char *input, int ilen );
+
+/**
+ * \brief          MD5 HMAC final digest
+ *
+ * \param ctx      HMAC context
+ * \param output   MD5 HMAC checksum result
+ */
+void md5_hmac_finish( md5_context *ctx, unsigned char *output );
+
+/**
+ * \brief          Output = HMAC-MD5( hmac key, input buffer )
+ *
+ * \param key      HMAC secret key
+ * \param keylen   length of the HMAC key
+ * \param input    buffer holding the  data
+ * \param ilen     length of the input data
+ * \param output   HMAC-MD5 result
+ */
+void md5_hmac( unsigned char *key, int keylen,
+               unsigned char *input, int ilen,
+               unsigned char *output );
+
+/**
+ * \brief          Checkup routine
+ *
+ * \return         0 if successful, or 1 if the test failed
+ */
+int md5_self_test( int verbose );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* md5.h */

+ 44 - 0
src/secure_input.c

@@ -0,0 +1,44 @@
+#include "secure_input.h"
+#include <stdio.h>
+#include <unistd.h>
+#ifndef WIN32
+# include <termios.h>
+# include <sys/ioctl.h>
+#endif
+
+void
+secure_input(char *buf, size_t buf_size)
+{
+#ifndef WIN32
+	struct termio ttymode;
+#endif
+	int pos;
+	int ch;
+
+#ifndef WIN32
+	// hide input
+	ioctl(STDIN_FILENO, TCGETA, &ttymode);
+	ttymode.c_lflag &= ~( ECHO | ECHOE | ECHONL );
+	ioctl(STDIN_FILENO, TCSETAF, &ttymode);
+#endif
+
+	pos=0;
+	ch=fgetc(stdin);
+	while (pos < buf_size-1 && ch != '\n' && ch != EOF) {
+		buf[pos] = ch;
+		pos++;
+		ch=fgetc(stdin);
+	} 
+	buf[pos] = '\0';
+
+	// flush the stdin buffer of remaining unused characters
+	while (ch != '\n' && ch != EOF)
+		ch = fgetc(stdin);
+
+#ifndef WIN32
+	// unhide input
+	ttymode.c_lflag |= ECHO | ECHOE | ECHONL;
+	ioctl(STDIN_FILENO, TCSETAF, &ttymode);
+	fputc('\n', stdout);
+#endif
+}

+ 16 - 0
src/secure_input.h

@@ -0,0 +1,16 @@
+#ifndef __SECURE_INPUT_H__
+#define __SECURE_INPUT_H__
+
+#include <sys/types.h>
+
+/**
+ * Read string from standard input while hiding the input. The read
+ * input is returned in 'buf' as a \0 terminated string. The maximum string
+ * length will be 'buf_size' - 1.
+ *
+ * @param buf		target buffer
+ * @param buf_size	Size of the target buffer
+ */
+void secure_input(char *buf, size_t buf_size);
+
+#endif // __SECURE_INPUT_H__

+ 111 - 0
src/sg_err.h

@@ -0,0 +1,111 @@
+#ifndef SG_ERR_H
+#define SG_ERR_H
+
+/* Borrowed for the u3_tool project from: sg3_utils
+ * see (http://sg.torque.net/sg/)
+ */
+
+/* Feel free to copy and modify this GPL-ed code into your applications. */
+
+/* Version 0.84 (20010115) 
+	- all output now sent to stderr rather than stdout
+	- remove header files included in this file
+*/
+
+
+/* Some of the following error/status codes are exchanged between the
+   various layers of the SCSI sub-system in Linux and should never
+   reach the user. They are placed here for completeness. What appears
+   here is copied from drivers/scsi/scsi.h which is not visible in
+   the user space. */
+
+/* The following are 'host_status' codes */
+#ifndef DID_OK
+#define DID_OK 0x00
+#endif
+#ifndef DID_NO_CONNECT
+#define DID_NO_CONNECT 0x01     /* Unable to connect before timeout */
+#define DID_BUS_BUSY 0x02       /* Bus remain busy until timeout */
+#define DID_TIME_OUT 0x03       /* Timed out for some other reason */
+#define DID_BAD_TARGET 0x04     /* Bad target (id?) */
+#define DID_ABORT 0x05          /* Told to abort for some other reason */
+#define DID_PARITY 0x06         /* Parity error (on SCSI bus) */
+#define DID_ERROR 0x07          /* Internal error */
+#define DID_RESET 0x08          /* Reset by somebody */
+#define DID_BAD_INTR 0x09       /* Received an unexpected interrupt */
+#define DID_PASSTHROUGH 0x0a    /* Force command past mid-level */
+#define DID_SOFT_ERROR 0x0b     /* The low-level driver wants a retry */
+#endif
+
+/* These defines are to isolate applictaions from kernel define changes */
+#define SG_ERR_DID_OK           DID_OK
+#define SG_ERR_DID_NO_CONNECT   DID_NO_CONNECT
+#define SG_ERR_DID_BUS_BUSY     DID_BUS_BUSY
+#define SG_ERR_DID_TIME_OUT     DID_TIME_OUT
+#define SG_ERR_DID_BAD_TARGET   DID_BAD_TARGET
+#define SG_ERR_DID_ABORT        DID_ABORT
+#define SG_ERR_DID_PARITY       DID_PARITY
+#define SG_ERR_DID_ERROR        DID_ERROR
+#define SG_ERR_DID_RESET        DID_RESET
+#define SG_ERR_DID_BAD_INTR     DID_BAD_INTR
+#define SG_ERR_DID_PASSTHROUGH  DID_PASSTHROUGH
+#define SG_ERR_DID_SOFT_ERROR   DID_SOFT_ERROR
+
+/* The following are 'driver_status' codes */
+#ifndef DRIVER_OK
+#define DRIVER_OK 0x00
+#endif
+#ifndef DRIVER_BUSY
+#define DRIVER_BUSY 0x01
+#define DRIVER_SOFT 0x02
+#define DRIVER_MEDIA 0x03
+#define DRIVER_ERROR 0x04
+#define DRIVER_INVALID 0x05
+#define DRIVER_TIMEOUT 0x06
+#define DRIVER_HARD 0x07
+#define DRIVER_SENSE 0x08       /* Sense_buffer has been set */
+
+/* Following "suggests" are "or-ed" with one of previous 8 entries */
+#define SUGGEST_RETRY 0x10
+#define SUGGEST_ABORT 0x20
+#define SUGGEST_REMAP 0x30
+#define SUGGEST_DIE 0x40
+#define SUGGEST_SENSE 0x80
+#define SUGGEST_IS_OK 0xff
+#endif
+#ifndef DRIVER_MASK
+#define DRIVER_MASK 0x0f
+#endif
+#ifndef SUGGEST_MASK
+#define SUGGEST_MASK 0xf0
+#endif
+
+/* These defines are to isolate applictaions from kernel define changes */
+#define SG_ERR_DRIVER_OK        DRIVER_OK
+#define SG_ERR_DRIVER_BUSY      DRIVER_BUSY
+#define SG_ERR_DRIVER_SOFT      DRIVER_SOFT
+#define SG_ERR_DRIVER_MEDIA     DRIVER_MEDIA
+#define SG_ERR_DRIVER_ERROR     DRIVER_ERROR
+#define SG_ERR_DRIVER_INVALID   DRIVER_INVALID
+#define SG_ERR_DRIVER_TIMEOUT   DRIVER_TIMEOUT
+#define SG_ERR_DRIVER_HARD      DRIVER_HARD
+#define SG_ERR_DRIVER_SENSE     DRIVER_SENSE
+#define SG_ERR_SUGGEST_RETRY    SUGGEST_RETRY
+#define SG_ERR_SUGGEST_ABORT    SUGGEST_ABORT
+#define SG_ERR_SUGGEST_REMAP    SUGGEST_REMAP
+#define SG_ERR_SUGGEST_DIE      SUGGEST_DIE
+#define SG_ERR_SUGGEST_SENSE    SUGGEST_SENSE
+#define SG_ERR_SUGGEST_IS_OK    SUGGEST_IS_OK
+#define SG_ERR_DRIVER_MASK      DRIVER_MASK
+#define SG_ERR_SUGGEST_MASK     SUGGEST_MASK
+
+/* The following "category" function returns one of the following */
+#define SG_ERR_CAT_CLEAN 0      /* No errors or other information */
+#define SG_ERR_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */
+#define SG_ERR_CAT_RESET 2      /* interpreted from sense buffer */
+#define SG_ERR_CAT_TIMEOUT 3
+#define SG_ERR_CAT_RECOVERED 4  /* Successful command after recovered err */
+#define SG_ERR_CAT_SENSE 98     /* Something else is in the sense buffer */
+#define SG_ERR_CAT_OTHER 99     /* Some other error/warning has occurred */
+
+#endif

+ 48 - 0
src/u3.h

@@ -0,0 +1,48 @@
+/**
+ * u3_tool - U3 USB stick manager
+ * Copyright (C) 2007 Daviedev, daviedev@users.sourceforge.net
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */ 
+#ifndef __U3_H__
+#define __U3_H__
+
+#include <stdint.h>
+
+#define U3_MAX_ERROR_LEN	1024	// Max. length of error msg.
+#define U3_SECTOR_SIZE		512	// size of one sector in the u3 system
+#define U3_BLOCK_SIZE		2048	// size of one block in the u3 system
+
+extern int debug;
+
+/**
+ * Handle for a U3 device
+ */
+struct u3_handle {
+	void *dev;		/* Raw SCSI interface handle, This is a void *
+				 * to be independent of the raw SCSI interface
+				 * used(eg. sg, usb, etc.) */
+	char err_msg[U3_MAX_ERROR_LEN];
+};
+typedef struct u3_handle u3_handle_t;
+
+/**
+ * Return values of U3 functions.
+ */
+enum {
+	U3_SUCCESS = 0,
+	U3_FAILURE = -1
+};
+#endif // __U3_H__

+ 551 - 0
src/u3_commands.c

@@ -0,0 +1,551 @@
+/**
+ * u3_tool - U3 USB stick manager
+ * Copyright (C) 2007 Daviedev, daviedev@users.sourceforge.net
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */ 
+
+#include "u3_commands.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "u3_scsi.h"
+#include "u3_error.h"
+#include "md5.h"
+
+#ifdef WIN32
+# include <windows.h>
+# define sleep Sleep
+// Barf, winsock... NOT
+# define htonl(A)  ((((uint32_t)(A) & 0xff000000) >> 24) | \
+                   (((uint32_t)(A) & 0x00ff0000) >> 8)  | \
+                   (((uint32_t)(A) & 0x0000ff00) << 8)  | \
+                   (((uint32_t)(A) & 0x000000ff) << 24))
+#else
+# include <arpa/inet.h> // for htonl()
+#endif
+
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+# error "This tool only runs on Little Endian machines"
+#endif
+
+#define U3_PASSWORD_HASH_LEN 16
+
+/**
+ * Convert textual password to hash.
+ *
+ * @param pass	The password string, null terminated.
+ * @param hash	Buffer to write hash to, should be atleast
+ * 		U3_PASSWORD_HASH_LEN long.
+ */
+void u3_pass_to_hash(const char *password, uint8_t *hash) {
+	unsigned int passlen;
+	unsigned int unicode_passlen;
+	uint8_t *unicode_pass;
+	int i;
+
+	passlen = strlen(password);
+	unicode_passlen = (passlen+1)*2;
+	unicode_pass = (uint8_t *) calloc(passlen+1, 2);
+
+	for (i = 0; i < passlen + 1; i++) {
+		unicode_pass[i*2] = password[i];
+	}
+	
+	md5(unicode_pass, unicode_passlen, hash);
+
+	memset(unicode_pass, 0xFF, unicode_passlen);
+	memset(unicode_pass, 0, unicode_passlen);
+	free(unicode_pass);
+}
+
+/**** public function ***/
+
+int u3_read_device_property(u3_handle_t *device, uint16_t property_id,
+		uint8_t *buffer, uint16_t buffer_length)
+{
+	uint8_t status;
+	uint8_t cmd[U3_CMD_LEN] = {
+		0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00
+	};
+	struct _write_cmd_t {
+		uint8_t	 scsi_opcode;
+		uint16_t command;
+		uint16_t id;
+		uint16_t length;
+		uint16_t unknown1;
+		uint8_t unknown2;
+	} __attribute__ ((packed)) *property_command;
+	struct property_header *hdr;
+
+
+	if (buffer_length < 6) {
+		u3_set_error(device, "buffer supplied to "
+				"u3_read_device_property() is to small");
+		return U3_FAILURE;
+	}
+
+	// First read header to prevent reading to much data
+	hdr = (struct property_header *) buffer;
+	property_command = (struct _write_cmd_t *) &cmd;
+	property_command->id = property_id;
+	property_command->length = sizeof(struct property_header);
+
+	if (u3_send_cmd(device, cmd, U3_DATA_FROM_DEV, property_command->length,
+		buffer, &status) != U3_SUCCESS)
+	{
+		return U3_FAILURE;
+	}
+
+	if (status != 0) {
+		u3_set_error(device, "Header of property 0x%.4X could not be "
+			"read.", property_id);
+		return U3_FAILURE;
+	}
+
+	if (hdr->length < sizeof(struct property_header)) {
+		u3_set_error(device, "Header of property 0x%.4X indicates a "
+			"length of smaller then the header size. (len = %u)"
+			, property_id, hdr->length);
+		return U3_FAILURE;
+	}
+
+	// Read full property
+	if (hdr->length > buffer_length) {
+		property_command->length = buffer_length;
+	} else {
+		property_command->length = hdr->length;
+	}
+
+	if (u3_send_cmd(device, cmd, U3_DATA_FROM_DEV, property_command->length,
+		buffer, &status) != U3_SUCCESS)
+	{
+		return U3_FAILURE;
+	}
+
+	if (status != 0) {
+		u3_set_error(device, "Property 0x%.4X could not be read.",
+			property_id);
+		return U3_FAILURE;
+	}
+
+	return U3_SUCCESS;
+}
+
+int u3_partition(u3_handle_t *device, uint32_t cd_size) {
+	uint8_t status;
+	uint8_t cmd[U3_CMD_LEN] = {
+		0xff, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00
+	};
+	struct part_info data;
+	struct property_03 device_properties;
+	uint32_t data_size;
+
+	// calculate data partition size
+	if (cd_size != 0) {
+		if (u3_partition_sector_round(device, round_up, &cd_size) != U3_SUCCESS) {
+			u3_prepend_error(device, "Failed rounding partition sectors");
+			return U3_FAILURE;
+		}
+	}
+
+	if (u3_read_device_property(device, 3, (uint8_t *) &device_properties,
+			sizeof(device_properties)) != U3_SUCCESS)
+	{
+		u3_prepend_error(device, "Failed reading device property 0x03");
+		return U3_FAILURE;
+	}
+
+	if (device_properties.hdr.length != sizeof(device_properties)) {
+		u3_set_error(device, "Unexpected device property(0x03) length"
+			", won't risk breaking device. Aborting! (len=%X)", 
+			device_properties.hdr.length);
+		return U3_FAILURE;
+	}
+	if (cd_size > device_properties.device_size) {
+		u3_set_error(device, "Requested CD size is to big for device");
+		return U3_FAILURE;
+	}
+
+	data_size = device_properties.device_size - cd_size;
+	if (u3_partition_sector_round(device, round_down, &data_size) != U3_SUCCESS) {
+		u3_prepend_error(device, "Failed rounding partition sectors");
+		return U3_FAILURE;
+	}
+	
+	// fill command data
+	memset(&data, 0, sizeof(data));
+	if (cd_size == 0) {
+		data.partition_count = 1;
+		data.unknown1 = 0x02;
+		data.data_size = data_size;
+		data.unknown2 = 0;
+		data.cd_size = 0;
+	} else {
+		data.partition_count = 2;
+		data.unknown1 = 0x02;
+		data.data_size = data_size;
+		data.unknown2 = 0x103;
+		data.cd_size = cd_size;
+	}
+
+	if (u3_send_cmd(device, cmd, U3_DATA_TO_DEV, sizeof(data),
+		(uint8_t *) &data, &status) != U3_SUCCESS)
+	{
+		return U3_FAILURE;
+	}
+
+	if (status != 0) {
+		u3_set_error(device, "Device reported command failed: status %d", status);
+		return U3_FAILURE;
+	}
+
+	return U3_SUCCESS;
+}
+
+int u3_cd_write(u3_handle_t *device, uint32_t block_num, uint8_t *block) {
+	uint8_t status;
+	uint8_t cmd[U3_CMD_LEN] = {
+		0xff, 0x42, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x01
+	};
+	struct _write_cmd_t {
+		uint8_t	 scsi_opcode;
+		uint16_t command;
+		uint8_t  unknown1;
+		uint32_t block_num;
+		uint32_t unknown2;
+	} __attribute__ ((packed)) *write_command;
+
+	// fill command data
+	write_command = (struct _write_cmd_t *) &cmd;
+	write_command->block_num = htonl(block_num);
+
+	if (u3_send_cmd(device, cmd, U3_DATA_TO_DEV, 2048,
+		block, &status) != U3_SUCCESS)
+	{
+		return U3_FAILURE;
+	}
+
+	if (status != 0) {
+		u3_set_error(device, "Device reported command failed: status %d", status);
+		return U3_FAILURE;
+	}
+
+	return U3_SUCCESS;
+}
+
+
+int u3_partition_sector_round(u3_handle_t *device,
+		enum round_dir direction, uint32_t *size)
+{
+	uint8_t status;
+	uint8_t cmd[U3_CMD_LEN] = {
+		0xff, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00
+	};
+	uint32_t rounded_size;
+	
+	// fill command data
+	*((uint32_t *)(cmd+4)) = *size;
+	cmd[8] = direction;
+
+	if (u3_send_cmd(device, cmd, U3_DATA_FROM_DEV, sizeof(rounded_size),
+		(uint8_t *) &rounded_size, &status) != U3_SUCCESS)
+	{
+		return U3_FAILURE;
+	}
+
+	if (status != 0) {
+		u3_set_error(device,
+			"Device reported command failed: status %d", status);
+		return U3_FAILURE;
+	}
+
+	*size = rounded_size;
+
+	return U3_SUCCESS;
+}
+
+int u3_security_sector_round(u3_handle_t *device,
+		enum round_dir direction, uint32_t *size)
+{
+	uint8_t status;
+	uint8_t cmd[U3_CMD_LEN] = {
+		0xff, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00
+	};
+	uint32_t rounded_size;
+	
+	// fill command data
+	*((uint32_t *)(cmd+4)) = *size;
+	cmd[8] = direction;
+
+	if (u3_send_cmd(device, cmd, U3_DATA_FROM_DEV, sizeof(rounded_size),
+		(uint8_t *) &rounded_size, &status) != U3_SUCCESS)
+	{
+		return U3_FAILURE;
+	}
+
+	if (status != 0) {
+		u3_set_error(device,
+			"Device reported command failed: status %d", status);
+		return U3_FAILURE;
+	}
+
+	*size = rounded_size;
+
+	return U3_SUCCESS;
+}
+
+int u3_partition_info(u3_handle_t *device, struct part_info *info) {
+	uint8_t status;
+	uint8_t cmd[U3_CMD_LEN] = {
+		0xff, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00
+	};
+	
+	memset(info, 0, sizeof(struct part_info));
+	
+	if (u3_send_cmd(device, cmd, U3_DATA_FROM_DEV, 9,
+		(uint8_t *)info, &status) != U3_SUCCESS)
+	{
+		return U3_FAILURE;
+	}
+
+//TODO: Find out if it is possible to define more then 2 partition. if so, make
+//this more dynamic
+	if (info->partition_count == 2) {
+		if (u3_send_cmd(device, cmd, U3_DATA_FROM_DEV, 16,
+			(uint8_t *)info, &status) != U3_SUCCESS)
+		{
+			return U3_FAILURE;
+		}
+	}
+
+	if (status != 0) {
+		u3_set_error(device, "Device reported command failed: status %d", status);
+		return U3_FAILURE;
+	}
+
+	return U3_SUCCESS;
+}
+
+int u3_data_partition_info(u3_handle_t *device, struct dpart_info *info) {
+	uint8_t status;
+	uint8_t cmd[U3_CMD_LEN] = {
+		0xff, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00
+	};
+	
+	memset(info, 0, sizeof(struct dpart_info));
+	
+	if (u3_send_cmd(device, cmd, U3_DATA_FROM_DEV, sizeof(struct dpart_info),
+		(uint8_t *)info, &status) != U3_SUCCESS)
+	{
+		return U3_FAILURE;
+	}
+
+	if (status != 0) {
+		u3_set_error(device, "Device reported command failed: status %d", status);
+		return U3_FAILURE;
+	}
+
+	return U3_SUCCESS;
+}
+
+int u3_chip_info(u3_handle_t *device, struct chip_info *info) {
+	uint8_t status;
+	uint8_t cmd[U3_CMD_LEN] = {
+		0xff, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00
+	};
+	
+	memset(info, 0, sizeof(struct chip_info));
+	
+	if (u3_send_cmd(device, cmd, U3_DATA_FROM_DEV, sizeof(struct chip_info),
+		(uint8_t *)info, &status) != U3_SUCCESS)
+	{
+		return U3_FAILURE;
+	}
+
+	if (status != 0) {
+		u3_set_error(device, "Device reported command failed: status %d", status);
+		return U3_FAILURE;
+	}
+
+	return U3_SUCCESS;
+}
+
+int u3_reset(u3_handle_t *device) {
+	uint8_t status;
+	uint8_t cmd[U3_CMD_LEN] = {
+		0xff, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00
+	};
+	uint8_t data[12] = { // the magic numbers
+		0x50, 0x00, 0x00, 0x00, 0x40, 0x9c, 0x00, 0x00,
+		0x01, 0x00, 0x00, 0x00
+	};
+	
+	if (u3_send_cmd(device, cmd, U3_DATA_TO_DEV, sizeof(data),
+		data, &status) != U3_SUCCESS)
+	{
+		return U3_FAILURE;
+	}
+
+	if (status != 0) {
+		u3_set_error(device, "Device reported command failed: status %d", status);
+		return U3_FAILURE;
+	}
+
+	
+	sleep(2); // wait for device to reset
+
+//	u3_reopen(device);
+
+	return U3_SUCCESS;
+}
+
+int u3_enable_security(u3_handle_t *device, const char *password) {
+	uint8_t status;
+	uint8_t cmd[U3_CMD_LEN] = {
+		0xff, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00
+	};
+	struct {
+		uint32_t size;
+		uint8_t  hash[U3_PASSWORD_HASH_LEN];
+	} __attribute__ ((packed)) data;
+	struct dpart_info dp_info;
+	uint32_t secure_zone_size;
+
+	// determine size
+//TODO: allow user to determine secure zone size... However currently we don't
+// understand why disable security only works on a fully secured partition...
+	if (u3_data_partition_info(device, &dp_info) != U3_SUCCESS) {
+		u3_prepend_error(device, "Failed reading data partition info");
+		return U3_FAILURE;
+	}
+	secure_zone_size = dp_info.total_size;
+
+	if (secure_zone_size != 0) {
+		if (u3_security_sector_round(device, round_up,
+				&secure_zone_size) != U3_SUCCESS)
+		{
+			u3_prepend_error(device,
+				"Failed rounding secure zone sectors");
+			return U3_FAILURE;
+		}
+	}
+
+	// fill command data
+	data.size = secure_zone_size;
+	u3_pass_to_hash(password, data.hash);
+
+	if (u3_send_cmd(device, cmd, U3_DATA_TO_DEV, sizeof(data),
+		(uint8_t *) &data, &status) != U3_SUCCESS)
+	{
+		return U3_FAILURE;
+	}
+
+	if (status != 0) {
+		u3_set_error(device, "Device reported command failed: status %d", status);
+		return U3_FAILURE;
+	}
+
+	return U3_SUCCESS;
+}
+
+int u3_disable_security(u3_handle_t *device, const char *password,
+		int * result)
+{
+	uint8_t status;
+	uint8_t cmd[U3_CMD_LEN] = {
+		0xff, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00
+	};
+        uint8_t passhash_buf[U3_PASSWORD_HASH_LEN];
+
+	*result = 0;
+	u3_pass_to_hash(password, passhash_buf);
+	
+	if (u3_send_cmd(device, cmd, U3_DATA_TO_DEV, sizeof(passhash_buf),
+		passhash_buf, &status) != U3_SUCCESS)
+	{
+		return U3_FAILURE;
+	}
+
+	if (status == 0) {
+		*result = 1;
+	}
+
+	return U3_SUCCESS;
+}
+
+int u3_unlock(u3_handle_t *device, const char *password, int *result) {
+	uint8_t status;
+	uint8_t cmd[U3_CMD_LEN] = {
+		0xff, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00
+	};
+        uint8_t passhash_buf[U3_PASSWORD_HASH_LEN];
+
+	*result = 0;
+	u3_pass_to_hash(password, passhash_buf);
+	
+	if (u3_send_cmd(device, cmd, U3_DATA_TO_DEV, sizeof(passhash_buf),
+		passhash_buf, &status) != U3_SUCCESS)
+	{
+		return U3_FAILURE;
+	}
+
+	if (status == 0) {
+		*result = 1;
+	}
+
+	return U3_SUCCESS;
+}
+
+int u3_change_password(u3_handle_t *device, const char *old_password,
+		const char *new_password, int *result)
+{
+	uint8_t status;
+	uint8_t cmd[U3_CMD_LEN] = {
+		0xff, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00
+	};
+        uint8_t passhash_buf[U3_PASSWORD_HASH_LEN * 2];
+
+	*result = 0;
+	u3_pass_to_hash(old_password, passhash_buf);
+	u3_pass_to_hash(new_password, passhash_buf+U3_PASSWORD_HASH_LEN);
+	
+	if (u3_send_cmd(device, cmd, U3_DATA_TO_DEV, sizeof(passhash_buf),
+		(uint8_t *) passhash_buf, &status) != U3_SUCCESS)
+	{
+		return U3_FAILURE;
+	}
+
+	if (status == 0) {
+		*result = 1;
+	}
+
+	return U3_SUCCESS;
+}
+

+ 319 - 0
src/u3_commands.h

@@ -0,0 +1,319 @@
+/**
+ * u3_tool - U3 USB stick manager
+ * Copyright (C) 2007 Daviedev, daviedev@users.sourceforge.net
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */ 
+#ifndef __U3_COMMAND__
+#define __U3_COMMAND__
+/**
+ * @file	u3_commands.h
+ *
+ *		This file declares the functions for controlling the
+ *		U3 device.
+ */
+
+#include "u3.h"
+
+/********************************* structures *********************************/
+
+/**
+ * Device property header structure
+ */
+struct property_header {
+	uint16_t id;		/* Property ID */
+	uint16_t unknown1;
+	uint16_t length;	/* Length of property including header */
+} __attribute__ ((packed));
+
+/**
+ * Device property 0x03 structure
+ */
+#define U3_MAX_SERIAL_LEN	16
+struct property_03 {
+	struct property_header hdr;	/* property header */
+	uint32_t full_length;		/* Actual Full length of property???? */
+	uint8_t  unknown1;
+	uint32_t unknown2;
+	char	 serial[U3_MAX_SERIAL_LEN];	/* Device Serial number */
+	uint32_t unknown3;
+	uint32_t device_size;		/* Device size in sectors */
+} __attribute__ ((packed));
+
+/**
+ * Device property 0x0C structure
+ */
+struct property_0C {
+	struct property_header hdr;	/* property header */
+	uint32_t max_pass_try;		/* Maximum wrong password try */
+} __attribute__ ((packed));
+
+/**
+ * partition information structure
+ */
+struct part_info {
+	uint8_t  partition_count;
+	uint32_t unknown1;
+	uint32_t data_size;		// in sectors
+	uint32_t unknown2;
+	uint32_t cd_size;		// in sectors
+} __attribute__ ((packed));
+
+/**
+ * Data partition information structure
+ */
+struct dpart_info {
+	uint32_t total_size;	// size of data partition in sectors
+	uint32_t secured_size;	// size of secured zone in sectors
+	uint32_t unlocked;	// 1 = unlocked, 0 = locked
+	uint32_t pass_try;	// number of times incorrect password tries
+} __attribute__ ((packed));
+
+/**
+ * Chip information structure
+ */
+#define U3_MAX_CHIP_REVISION_LEN	8
+#define U3_MAX_CHIP_MANUFACTURER_LEN	16
+struct chip_info {
+	char revision[U3_MAX_CHIP_REVISION_LEN];
+	char manufacturer[U3_MAX_CHIP_MANUFACTURER_LEN];
+} __attribute__ ((packed));
+
+
+/********************************** functions *********************************/
+/**
+ * Read device property page
+ *
+ * This reads a page of device properties from the device. A requested page is
+ * stored, including there page header, in 'buffer'. If the buffer size, as
+ * specified by 'buffer_length', is to small to store the whole page, only the
+ * first 'buffer_length' bytes are stored. The caller should check if the full
+ * page fitted into the buffer, by comparing the page length in the header with
+ * the buffer length. If the function returns successfull then atleast 
+ * sizeof(struct property_header) bytes will be valid in the buffer, therefore
+ * 'buffer_length should be atleast 6 byte. If a property page does not excist,
+ * this function will fail.
+ *
+ * @param device	U3 device handle
+ * @param property_id	The index of the requested property page
+ * @param buffer	The buffer to return the requested property page in.
+ * @param buffer_length	The allocated length of 'buffer' in bytes.
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ *
+ * @see 'struct property_header', 'struct property_*'
+ */
+int u3_read_device_property(u3_handle_t *device, uint16_t property_id,
+		uint8_t *buffer, uint16_t buffer_length);
+
+/**
+ * Repartiton device
+ *
+ * This repatitions the device into a CD and a data partition. All current data
+ * will be lost. Creating a CD partition of size 0 will make this device an
+ * ordenary USB stick.
+ *
+ * @param device	U3 device handle
+ * @param cd_size	The size of the CD partition in 512 byte sectors.
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ */
+int u3_partition(u3_handle_t *device, uint32_t cd_size);
+
+/**
+ * Write CD block
+ *
+ * This function write's a block to the CD partition. Blocks are 2048 bytes in
+ * size.
+ *
+ * @param device	U3 device handle
+ * @param block_num	The block number to write
+ * @param block		A pointer to buffer containing block
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ */
+int u3_cd_write(u3_handle_t *device, uint32_t block_num, uint8_t *block);
+
+/**
+ * Direction to round sector count.
+ * 
+ * @see 'u3_sector_round()'
+ */
+enum round_dir { round_down=0, round_up=1 };
+
+/**
+ * Round partition sector count to prevered value
+ *
+ * Round a number of sectors to a sector count that is allowed for partitioning
+ * the device.
+ *
+ * @param device	U3 device handle
+ * @param direction	direction to round(up or down).
+ * @param size		Size value to round. Rounded value is writen back to
+ *			this variable.
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ *
+ * @see 'u3_partition()'
+ */
+int u3_partition_sector_round(u3_handle_t *device,
+		enum round_dir direction, uint32_t *size);
+
+/**
+ * Round secure zone sector count to prevered value
+ *
+ * Round a number of sectors to a sector count that is allowed for a secure
+ * zone on the data partition.
+ *
+ * @param device	U3 device handle
+ * @param direction	direction to round(up or down).
+ * @param size		Size value to round. Rounded value is writen back to
+ *			this variable.
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ *
+ * @see 'u3_enable_security()'
+ */
+int u3_security_sector_round(u3_handle_t *device,
+		enum round_dir direction, uint32_t *size);
+
+
+/**
+ * Request partitioning information
+ *
+ * This request some information about the partition sizes
+ *
+ * @param device	U3 device handle
+ * @param info		Pointer to structure used to return requested info.
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ *
+ * @see 'struct part_info'
+ */
+int u3_partition_info(u3_handle_t *device, struct part_info *info);
+
+/**
+ * Request data partition information
+ *
+ * This request some information about the data partition
+ *
+ * @param device	U3 device handle
+ * @param info		Pointer to structure used to return requested info.
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ *
+ * @see 'struct dpart_info'
+ */
+int u3_data_partition_info(u3_handle_t *device, struct dpart_info *info);
+
+/**
+ * Request chip information
+ *
+ * This request some information about the USB chip used on the device.
+ *
+ * @param device	U3 device handle
+ * @param info		Pointer to structure used to return requested info.
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ *
+ * @see 'struct chip_info'
+ */
+int u3_chip_info(u3_handle_t *device, struct chip_info *info);
+
+/**
+ * Reset device
+ *
+ * This function tell's the device to reconnect. When the function returns
+ * the device should have been reset and reconnected.
+ * The exact working of this action is still vague
+ *
+ * @param device	U3 device handle
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ */
+int u3_reset(u3_handle_t *device);
+
+/**
+ * Enable device security
+ *
+ * This sets a password on the data partition.
+ *
+ * @param device	U3 device handle
+ * @param password      The password for the private zone
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ */
+int u3_enable_security(u3_handle_t *device, const char *password);
+
+/**
+ * Disable device security
+ *
+ * This removes the password from the data partition.
+ *
+ * @param device	U3 device handle
+ * @param password      The password for the private zone
+ * @param result        Return variable for result True if unlocking succeeded,
+ *                      else false
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ */
+int u3_disable_security(u3_handle_t *device, const char *password,
+		int * result);
+
+/**
+ * Unlock data partition
+ *
+ * This unlocks the data partition if the device is secured.
+ *
+ * @param device	U3 device handle
+ * @param password      The password for the private zone
+ * @param result        Return variable for result True if unlocking succeeded,
+ *                      else false
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ */
+int u3_unlock(u3_handle_t *device, const char *password, int *result);
+
+/**
+ * Change password of data partition
+ *
+ * This changes the password of the data partition.
+ *
+ * @param device	U3 device handle
+ * @param old_password  The current password of the private zone
+ * @param new_password  The new password of the private zone
+ * @param result        Return variable for result: True if password changed,
+ *                      else False if old password incorrect.
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ */
+int u3_change_password(u3_handle_t *device, const char *old_password,
+		const char *new_password, int *result);
+
+
+#endif // __U3_COMMAND__

+ 57 - 0
src/u3_error.c

@@ -0,0 +1,57 @@
+/**
+ * u3_tool - U3 USB stick manager
+ * Copyright (C) 2007 Daviedev, daviedev@users.sourceforge.net
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */ 
+#include "u3_error.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+char *u3_error_msg(u3_handle_t *device) {
+	return device->err_msg;
+}
+
+void u3_set_error(u3_handle_t *device, const char *fmt, ...) {
+	va_list ap;
+
+	va_start(ap, fmt);
+	vsnprintf(device->err_msg, U3_MAX_ERROR_LEN, fmt, ap);
+	device->err_msg[U3_MAX_ERROR_LEN-1] = '\0';
+	va_end(ap);
+
+}
+
+void u3_prepend_error(u3_handle_t *device, const char *fmt, ...) {
+	va_list ap;
+	char *old_msg;
+
+	old_msg = strdup(device->err_msg);
+	if (old_msg == NULL) return;
+
+	va_start(ap, fmt);
+	vsnprintf(device->err_msg, U3_MAX_ERROR_LEN, fmt, ap);
+	device->err_msg[U3_MAX_ERROR_LEN-1] = '\0';
+	va_end(ap);
+
+	strncat(device->err_msg, ": ",
+		U3_MAX_ERROR_LEN - strlen(device->err_msg) - 1);
+	strncat(device->err_msg, old_msg,
+		U3_MAX_ERROR_LEN - strlen(device->err_msg) - 1);
+
+	free(old_msg);
+}

+ 42 - 0
src/u3_error.h

@@ -0,0 +1,42 @@
+/**
+ * u3_tool - U3 USB stick manager
+ * Copyright (C) 2007 Daviedev, daviedev@users.sourceforge.net
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */ 
+#ifndef __U3_ERROR_H__
+#define __U3_ERROR_H__
+
+#include "u3.h"
+
+/**
+ * Get string error message of last error
+ *
+ * This function returns a pointer to the internal error buffer of the U3
+ * device handle.
+ *
+ * @param device	U3 device handle
+ *
+ * @return		error string of last error
+ */
+char* u3_error_msg(u3_handle_t *device);
+
+/* set a new u3 error */
+void u3_set_error(u3_handle_t *device, const char *fmt, ...);
+
+/* prepend a message to the current u3 error */
+void u3_prepend_error(u3_handle_t *device, const char *fmt, ...);
+
+#endif // __U3_ERROR_H__

+ 88 - 0
src/u3_scsi.h

@@ -0,0 +1,88 @@
+/**
+ * u3_tool - U3 USB stick manager
+ * Copyright (C) 2007 Daviedev, daviedev@users.sourceforge.net
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */ 
+#ifndef __U3_SCSI_H__
+#define __U3_SCSI_H__
+/**
+ * @file	u3_scsi.h
+ *
+ * @description	Declare some helper functions to provide a subsystem
+ * 		independent way of sending SCSI commands to a U3 device.
+ */
+
+#include "u3.h"
+
+#define U3_CMD_LEN		12
+
+/**
+ * Open U3 device
+ *
+ * This opens the device addressed by the variable 'which'.
+ *
+ * @param device	pointer to U3 handle that will be used to access the
+ * 			newly openned device.
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ */
+int u3_open(u3_handle_t *device, const char *which);
+
+/**
+ * Close U3 device
+ *
+ * This closes the u3 device handle pointed to by 'device'
+ *
+ * @param device	U3 handle
+ */
+void u3_close(u3_handle_t *device);
+
+
+/**
+ * dxfer_direction values as used by u3_send_cmd()
+ *
+ * @see 'u3_send_cmd()'
+ */
+enum {
+	U3_DATA_NONE = 0,		// dont transfer extra data
+	U3_DATA_TO_DEV = 1,		// send data to device
+	U3_DATA_FROM_DEV = 2,	// read data from device
+};
+
+/**
+ * Execute a scsi command at device
+ *
+ * This send the given SCSI CDB in 'cmd' to the device given by 'device'.
+ * Optional data is transfered from or to the device. The SCSI status as
+ * returned by the device is placed in 'status'.
+ *
+ * @param device		U3 handle
+ * @param cmd			SCSI CDB
+ * @param dxfer_direction	Direction of extra data, given by on of the
+ * 				U3_DATA_* enum values
+ * @param dxfer_len		Length of extra data
+ * @param dxfer_data		Buffer with extra data
+ * @param status		Pointer to variable to return SCSI status in
+ *
+ * @returns		U3_SUCCESS if successful, else U3_FAILURE and
+ * 			an error string can be obtained using u3_error()
+ */
+int u3_send_cmd(u3_handle_t *device, uint8_t cmd[U3_CMD_LEN],
+		int dxfer_direction, int dxfer_length, uint8_t *dxfer_data,
+		uint8_t *status);
+
+#endif // __U3_SCSI_H__

+ 111 - 0
src/u3_scsi_debug.c

@@ -0,0 +1,111 @@
+/**
+ * u3_tool - U3 USB stick manager
+ * Copyright (C) 2007 Daviedev, daviedev@users.sourceforge.net
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */ 
+#include "u3_scsi.h"
+#include "u3_error.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <stdio.h>
+
+#include "sg_err.h"
+
+#define U3_TIMEOUT 2000	//2000 millisecs == 2 seconds 
+
+int u3_open(u3_handle_t *device, const char *which) 
+{
+	FILE *fp;
+
+	u3_set_error(device, "");
+	device->dev = NULL;
+
+	if (strcmp(which, "stdout") == 0) {
+		fp = stdout;
+	} else if (strcmp(which, "stderr") == 0) {
+		fp = stderr;
+	} else {
+		u3_set_error(device, "unknown output '%s', 'stdout' and "
+			"'stderr' only supported", which);
+		return U3_FAILURE;
+	}
+		
+	device->dev = fp;
+	return U3_SUCCESS;
+}
+
+void u3_close(u3_handle_t *device) 
+{
+	FILE *fp = (FILE *)(device->dev);
+//	if (fp != stdout && fp != stderr) {
+//		fclose(fp);
+//	}
+}
+
+int u3_send_cmd(u3_handle_t *device, uint8_t cmd[U3_CMD_LEN],
+		int dxfer_direction, int dxfer_length, uint8_t *dxfer_data,
+		uint8_t *status)
+{
+	FILE *fp = (FILE *)device->dev;
+	int i;
+
+	fprintf(fp, "---------------------------------------------------------"
+			"-----------------------\n");
+
+	fprintf(fp, "Command block:\n");
+	for (i=0; i < U3_CMD_LEN; i++) {
+		fprintf(fp, "%.2X ", cmd[i]);
+	}
+	fprintf(fp, "\n");
+
+	fprintf(fp, "\n");
+	switch (dxfer_direction) {
+		case U3_DATA_NONE:
+			fprintf(fp, "No data\n");
+			break;
+		case U3_DATA_TO_DEV:
+			fprintf(fp, "Sending %d bytes of data to device\n", dxfer_length);
+			
+			fprintf(fp, "Data:");
+			for (i=0; i < dxfer_length; i++) {
+				if (i % 8 == 0) fprintf(fp, "\n%.4x\t", i);
+				fprintf(fp, "%.2x ", dxfer_data[i]);
+			}
+			fprintf(fp, "\n");
+			break;
+		case U3_DATA_FROM_DEV:
+			fprintf(fp, "Reading %d bytes of data from device\n", dxfer_length);
+			memset(dxfer_data, 0, dxfer_length);
+			break;
+	}
+
+	fprintf(fp, "---------------------------------------------------------"
+			"-----------------------\n");
+
+	*status = 0;
+
+	return U3_SUCCESS;
+}
+
+

+ 165 - 0
src/u3_scsi_sg.c

@@ -0,0 +1,165 @@
+/**
+ * u3_tool - U3 USB stick manager
+ * Copyright (C) 2007 Daviedev, daviedev@users.sourceforge.net
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */ 
+#include "u3_scsi.h"
+#include "u3_error.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <linux/cdrom.h>
+#include <scsi/sg.h>
+
+#include "sg_err.h"
+
+#define U3_TIMEOUT 2000	//2000 millisecs == 2 seconds 
+
+int u3_open(u3_handle_t *device, const char *which) 
+{
+	int k;
+	int sg_fd;
+
+	u3_set_error(device, "");
+	device->dev = NULL;
+
+	if ((sg_fd = open(which, O_RDWR)) < 0) {
+		// Nautilus sends a eject ioctl to the device when unmounting.
+		// Just have to close the tray ;-)
+		if (errno == ENOMEDIUM) {
+			if ((sg_fd = open(which, O_RDONLY | O_NONBLOCK)) < 0) {
+				u3_set_error(device, "%s", strerror(errno));
+				return U3_FAILURE;
+			}
+			ioctl(sg_fd, CDROMCLOSETRAY);
+			close(sg_fd);
+			if ((sg_fd = open(which, O_RDWR)) < 0) {
+				u3_set_error(device, "%s", strerror(errno));
+				return U3_FAILURE;
+			}
+		} else {
+			u3_set_error(device, "%s", strerror(errno));
+			return U3_FAILURE;
+		}
+	}
+
+	// It is prudent to check we have a sg device by trying an ioctl
+	if (ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) {
+		close(sg_fd);
+		if (k < 30000) {
+			u3_set_error(device, "device is not an SG device");
+		} else {
+			u3_set_error(device, "SG driver to old");
+		}
+		return U3_FAILURE;
+	}
+
+
+	if ((device->dev = malloc(sizeof(int))) == NULL) {
+		close(sg_fd);
+		u3_set_error(device, "Failed allocating memory for file descriptor");
+		return U3_FAILURE;
+	}
+		
+	*((int *)device->dev) = sg_fd;
+	return U3_SUCCESS;
+}
+
+void u3_close(u3_handle_t *device) 
+{
+	int *sg_fd = (int *)device->dev;
+	close(*sg_fd);
+}
+
+int u3_send_cmd(u3_handle_t *device, uint8_t cmd[U3_CMD_LEN],
+		int dxfer_direction, int dxfer_length, uint8_t *dxfer_data,
+		uint8_t *status)
+{
+	sg_io_hdr_t io_hdr;
+	unsigned char sense_buf[32];
+	int *sg_fd = (int *)device->dev;
+
+	// translate dxfer_direction
+	switch (dxfer_direction) {
+		case U3_DATA_NONE:
+			dxfer_direction = SG_DXFER_NONE;
+			break;
+		case U3_DATA_TO_DEV:
+			dxfer_direction = SG_DXFER_TO_DEV;
+			break;
+		case U3_DATA_FROM_DEV:
+			dxfer_direction = SG_DXFER_FROM_DEV;
+			break;
+	}
+
+	// Prepare command
+	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+	io_hdr.interface_id = 'S';			// fixed
+	io_hdr.cmd_len = U3_CMD_LEN;			// length of command in bytes
+	// io_hdr.iovec_count = 0;   			// don't use iovector stuff
+	io_hdr.mx_sb_len = sizeof(sense_buf);		// sense buffer size. do we use this???
+	io_hdr.dxfer_direction = dxfer_direction;	// send data to device
+	io_hdr.dxfer_len = dxfer_length;		// Size of data transfered
+	io_hdr.dxferp = dxfer_data;			// Data buffer to transfer
+	io_hdr.cmdp = cmd;				// Command buffer to execute
+	io_hdr.sbp = sense_buf;				// Sense buffer
+	io_hdr.timeout = U3_TIMEOUT;			// timeout
+	// io_hdr.flags = 0;	 			// take defaults: indirect IO, etc 
+	// io_hdr.pack_id = 0;				// internal packet. used by te program to recognize packets
+	// io_hdr.usr_ptr = NULL;			// user data
+
+	// preform ioctl on device
+	if (ioctl(*sg_fd, SG_IO, &io_hdr) < 0) {
+		u3_set_error(device, "Failed executing scsi command: "
+				"SG_IO ioctl Failed");
+		return U3_FAILURE;
+	}
+
+	// evaluate result
+	if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) {
+		if (io_hdr.host_status == SG_ERR_DID_OK &&
+		    (io_hdr.driver_status & SG_ERR_DRIVER_SENSE))
+		{
+			// 
+			// The usb-storage driver automatically request sense
+			// data if a command fails. So this state isn't really
+			// a error but only indicates that the sense data in
+			// the buffer is fresh. Currently we aren't don't use
+			// this sense data, we only wanna know if a command
+			// failed or not. so ignore this...
+			//
+
+		} else {
+			u3_set_error(device, "Failed executing scsi command: "
+				"Status (S:0x%x,H:0x%x,D:0x%x)", io_hdr.status,
+				io_hdr.host_status, io_hdr.driver_status);
+			return U3_FAILURE;
+		}
+	}
+
+	*status = io_hdr.masked_status;
+
+	return U3_SUCCESS;
+}
+
+

+ 134 - 0
src/u3_scsi_spt.c

@@ -0,0 +1,134 @@
+/**
+ * u3_tool - U3 USB stick manager
+ * Copyright (C) 2007 Daviedev, daviedev@users.sourceforge.net
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */ 
+#include "u3_scsi.h"
+#include "u3_error.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <windows.h>
+#include <ddk/ntddscsi.h>
+
+#define U3_TIMEOUT 2	// 2 seconds 
+
+int u3_open(u3_handle_t *device, const char *which) 
+{
+	HANDLE hDevice;
+    CHAR lpszDeviceName[7];
+    DWORD dwBytesReturned;
+    DWORD dwError;
+
+    u3_set_error(device, "");
+    device->dev = NULL;
+
+    // check parameter
+    if (strlen(which) != 1 || ! isalpha(which[0])) {
+        u3_set_error(device, "Unknown drive name '%s', Expecting a "
+            "drive letter", which);
+        return U3_FAILURE;
+    }
+
+    //take the drive letter and put it in the format used in CreateFile
+    memcpy(lpszDeviceName, (char *) "\\\\.\\*:", 7);
+    lpszDeviceName[4] = which[0];
+
+    //get a handle to the device, the parameters used here must be used in order for this to work
+    hDevice=CreateFile(lpszDeviceName,
+						GENERIC_READ|GENERIC_WRITE,
+						FILE_SHARE_READ|FILE_SHARE_WRITE,
+						NULL,
+						OPEN_EXISTING,
+						FILE_ATTRIBUTE_NORMAL,
+						NULL);
+
+    //if for some reason we couldn't get a handle to the device we will try again using slightly different parameters for CreateFile
+    if (hDevice==INVALID_HANDLE_VALUE)
+    {
+		u3_set_error(device, "Failed openning handle for %s: Error %d\n", which, GetLastError());
+		return U3_FAILURE;
+	}
+
+	
+	device->dev = hDevice;
+	return U3_SUCCESS;
+}
+
+void u3_close(u3_handle_t *device) 
+{
+	HANDLE hDevice = (HANDLE)device->dev;
+	CloseHandle(hDevice);
+}
+
+int u3_send_cmd(u3_handle_t *device, uint8_t cmd[U3_CMD_LEN],
+		int dxfer_direction, int dxfer_length, uint8_t *dxfer_data,
+		uint8_t *status)
+{
+	HANDLE hDevice = (HANDLE)device->dev;
+	SCSI_PASS_THROUGH_DIRECT sptd;
+	DWORD returned;
+	BOOL err;
+
+	// translate dxfer_direction
+	switch (dxfer_direction) {
+		case U3_DATA_NONE:
+			dxfer_direction = SCSI_IOCTL_DATA_UNSPECIFIED;
+			break;
+		case U3_DATA_TO_DEV:
+			dxfer_direction = SCSI_IOCTL_DATA_OUT;
+			break;
+		case U3_DATA_FROM_DEV:
+			dxfer_direction = SCSI_IOCTL_DATA_IN;
+			break;
+	}
+
+	// Prepare command
+    memset(&sptd, 0, sizeof(SCSI_PASS_THROUGH_DIRECT));
+    sptd.Length             = sizeof(SCSI_PASS_THROUGH_DIRECT);// fixed
+    sptd.CdbLength          = U3_CMD_LEN;						// length of command in bytes
+    sptd.SenseInfoLength    = 0;								// don't use this currently...
+    sptd.DataIn             = dxfer_direction;					// data direction
+    sptd.DataTransferLength = dxfer_length;					// Size of data transfered
+    sptd.TimeOutValue       = U3_TIMEOUT;						// timeout in seconds
+    sptd.DataBuffer         = dxfer_data;						// data buffer
+    //sptd.SenseInfoOffset    = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, sbuf);
+    memcpy(sptd.Cdb, cmd, U3_CMD_LEN);
+
+	// preform ioctl on device
+    err = DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptd,
+						sizeof(sptd), &sptd, sizeof(sptd), &returned, NULL);
+
+	// evaluate result
+	if (!err) {
+		u3_set_error(device, "Failed executing scsi command: "
+			"Error %d", GetLastError());
+		return U3_FAILURE;
+	}
+
+	*status = sptd.ScsiStatus;
+
+	return U3_SUCCESS;
+}
+
+

+ 352 - 0
src/u3_scsi_usb.c

@@ -0,0 +1,352 @@
+/**
+ * u3_tool - U3 USB stick manager
+ * Copyright (C) 2007 Daviedev, daviedev@users.sourceforge.net
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */ 
+#include "u3_scsi.h"
+#include "u3_error.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <usb.h>
+#include <errno.h>
+#include <regex.h>
+
+// USB maximum packet length
+#define USB_MAX_PACKET_LEN 256
+
+// USB command block wrapper length
+#define U3_CBWCB_LEN	12
+
+// USB MSC command/status wrapper signatures
+#define CBW_SIGNATURE		0x43425355
+#define CSW_SIGNATURE		0x53425355
+
+// USB MSC command block wrapper data direction bits
+#define CBW_DATA_IN		0x80
+#define CBW_DATA_OUT		0x00
+#define CBW_DATA_NONE		0x00
+#define CBW_FLAG_DIRECTION	0x80
+
+#define U3_DEVICE_TIMEOUT 20000	//2000 millisecs == 2 seconds 
+
+#define U3_DEV_LIST_LENGTH 2
+uint16_t u3_dev_list[U3_DEV_LIST_LENGTH][2] = {
+	{ 0x08ec, 0x0020 },
+	{ 0x0781, 0x5406 },
+};
+
+struct u3_usb_handle {
+	usb_dev_handle *handle;
+	int interface_num;
+	int ep_out;
+	int ep_in;
+};
+typedef struct u3_usb_handle u3_usb_handle_t;
+
+struct usb_msc_cbw {
+	uint32_t dCBWSignature;
+	uint32_t dCBWTag;
+	uint32_t dCBWDataTransferLength;
+	uint8_t bmCBWFlags;
+	uint8_t bCBWLUN;
+	uint8_t bCBWCBLength;
+	uint8_t CBWCB[16];
+} __attribute__ ((packed));
+
+struct usb_msc_csw {
+	uint32_t dCSWSignature;
+	uint32_t dCSWTag;
+	uint32_t dCSWDataResidue;
+	uint8_t bCSWStatus;
+} __attribute__ ((packed));
+
+
+struct usb_device *
+locate_u3_device(uint16_t vid, uint16_t pid)
+{
+	struct usb_bus *bus;
+	struct usb_device *dev;
+
+	// rescan busses and devices
+	usb_find_busses();
+	usb_find_devices();
+
+	for (bus = usb_get_busses(); bus; bus = bus->next) {
+		for (dev = bus->devices; dev; dev = dev->next) {
+			if (dev->descriptor.idVendor == vid &&
+			    (dev->descriptor.idProduct == pid))
+			{
+				return dev;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+int u3_open(u3_handle_t *device, const char *which) 
+{
+	uint16_t vid, pid;
+	regex_t vid_pid_regex;
+
+        struct usb_device *u3_device;
+	u3_usb_handle_t *handle_wrapper;
+
+	struct usb_config_descriptor *config_desc;
+	struct usb_interface_descriptor *interface_desc;
+	struct usb_endpoint_descriptor *endpoint_desc;
+
+	int err;
+	char errbuf[U3_MAX_ERROR_LEN];
+	int i;
+
+	// init
+	u3_set_error(device, "");
+
+	err = regcomp(&vid_pid_regex, "[::xdigit::]*:[::xdigit::]*", 0);
+	if (err != 0) {
+		regerror(err, &vid_pid_regex, errbuf, U3_MAX_ERROR_LEN);
+		u3_set_error(device, "Failed constructing 'vid:pid' regular "
+				"expression: %s", errbuf);
+		return U3_FAILURE;
+	}
+
+	usb_init();
+	if (debug)
+		usb_set_debug(255);
+
+	// Find device
+	if (regexec(&vid_pid_regex, which, 0, 0, 0) == 0) {
+		char *seperator=0;
+		vid = strtoul(which, &seperator, 16);
+		pid = strtoul(seperator+1, &seperator, 16);
+		u3_device = locate_u3_device(vid, pid);
+	} else if (strcmp(which, "scan") == 0) {
+		i = 0;
+		u3_device = NULL;
+		while (u3_device == NULL && i < U3_DEV_LIST_LENGTH) {
+			u3_device = locate_u3_device(u3_dev_list[i][0],
+						u3_dev_list[i][1]);
+			i++;
+		}
+//	} else if (regexec(&serial_regex, which, 0, 0, 0) == 0) {
+//TODO: search for a specific serial number
+	} else {
+		u3_set_error(device, "Unknown device name '%s', try 'scan' "
+			"for first available device", which);
+		return U3_FAILURE;
+	}
+
+	regfree(&vid_pid_regex);
+
+	if (u3_device == NULL) {
+		u3_set_error(device, "Could not locate the U3 device '%s', "
+				"try 'scan' for first available device", which);
+		return U3_FAILURE;
+	}
+
+	// Open device
+	handle_wrapper = (u3_usb_handle_t *) malloc(sizeof(u3_usb_handle_t));
+	if (handle_wrapper == NULL) {
+		u3_set_error(device, "Failed allocate memory!!");
+		return U3_FAILURE;
+	}
+
+	handle_wrapper->handle = usb_open(u3_device);
+
+	// Set configuration
+	if (u3_device->descriptor.bNumConfigurations != 1) {
+		u3_set_error(device,
+			"Multiple USB configuration not supported");
+		goto open_fail;
+	} 
+	config_desc = u3_device->config;
+	usb_set_configuration(handle_wrapper->handle,
+				config_desc->bConfigurationValue);
+
+	// Claim device
+	if (config_desc->bNumInterfaces != 1) {
+		u3_set_error(device, "Multiple USB configuration interfaces "
+			"not supported");
+		goto open_fail;
+	}
+	if (config_desc->interface->num_altsetting != 1) {
+		u3_set_error(device, "Multiple USB configuration interface "
+			" alt. settings not supported");
+		goto open_fail;
+	}
+	interface_desc = config_desc->interface->altsetting;
+
+	handle_wrapper->interface_num = interface_desc->bInterfaceNumber;
+	if ((err=usb_claim_interface(handle_wrapper->handle,
+				handle_wrapper->interface_num)) != 0)
+	{
+		if (err == -EBUSY) {
+			// Don't try to detach driver on Linux, this can cause
+			// data loss and leaves the device in a unspecified
+			// state. The user should 'unbind' the driver through
+			// sysfs, or unload the usb-storage driver.
+			u3_set_error(device, "Failed to claim device: Device is busy");
+			goto open_fail;
+		} else {
+			u3_set_error(device, "Failed to claim device: %s", usb_strerror());
+			goto open_fail;
+		}
+	}
+
+	// Set alt. setting
+	usb_set_altinterface(handle_wrapper->handle,
+			interface_desc->bAlternateSetting);
+
+	// Find the correct endpoints
+	if (interface_desc->bNumEndpoints != 2) {
+		u3_set_error(device, "Not exactly two usb endpoints defined,"
+			" unsupported.");
+		goto claimed_fail;
+	}
+	endpoint_desc = interface_desc->endpoint;
+
+	if (endpoint_desc[0].bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+		handle_wrapper->ep_in  = endpoint_desc[0].bEndpointAddress;
+		handle_wrapper->ep_out = endpoint_desc[1].bEndpointAddress;
+	} else {
+		handle_wrapper->ep_out = endpoint_desc[0].bEndpointAddress;
+		handle_wrapper->ep_in  = endpoint_desc[1].bEndpointAddress;
+	}
+
+	if (!((handle_wrapper->ep_in & USB_ENDPOINT_DIR_MASK) ^
+	     (handle_wrapper->ep_out & USB_ENDPOINT_DIR_MASK)))
+	{
+		u3_set_error(device, "Both usb endpoints in same direction!");
+		goto claimed_fail;
+	}
+
+	device->dev = handle_wrapper;
+	return U3_SUCCESS;
+claimed_fail:
+        usb_release_interface(handle_wrapper->handle,
+				handle_wrapper->interface_num);
+open_fail:
+	usb_close(handle_wrapper->handle);
+	free(handle_wrapper);
+	return U3_FAILURE;
+
+}
+
+void u3_close(u3_handle_t *device) 
+{
+	u3_usb_handle_t *handle_wrapper = (u3_usb_handle_t *) device->dev;
+
+        usb_release_interface(handle_wrapper->handle,
+				handle_wrapper->interface_num);
+        usb_close(handle_wrapper->handle);
+	free(handle_wrapper);
+}
+
+int u3_send_cmd(u3_handle_t *device, uint8_t cmd[U3_CMD_LEN],
+		int dxfer_direction, int dxfer_length, uint8_t *dxfer_data,
+		uint8_t *status)
+{
+	u3_usb_handle_t *handle_wrapper = (u3_usb_handle_t *) device->dev;
+        struct usb_msc_cbw cbw;
+        struct usb_msc_csw csw;
+
+	// translate dxfer_direction
+	switch (dxfer_direction) {
+		case U3_DATA_TO_DEV:
+			dxfer_direction = CBW_DATA_OUT;
+			break;
+		case U3_DATA_FROM_DEV:
+			dxfer_direction = CBW_DATA_IN;
+			break;
+		case U3_DATA_NONE:
+		default:
+			dxfer_direction = CBW_DATA_NONE;
+			dxfer_length = 0;
+			break;
+	}
+
+	// Prepare command
+        memset(&cbw, 0, sizeof(cbw));
+        cbw.dCBWSignature = CBW_SIGNATURE;
+	cbw.dCBWDataTransferLength = dxfer_length;
+	cbw.bmCBWFlags |= (dxfer_direction & CBW_FLAG_DIRECTION);
+        cbw.bCBWCBLength = U3_CBWCB_LEN;
+        memcpy(&(cbw.CBWCB), cmd, U3_CBWCB_LEN);
+
+	// send command
+        if (usb_bulk_write(handle_wrapper->handle, handle_wrapper->ep_out,
+			(char *) &cbw, sizeof(cbw), U3_DEVICE_TIMEOUT)
+                        != sizeof(cbw))
+        {
+		u3_set_error(device, "Failed executing scsi command: "
+			"Could not write command block to device");
+		return U3_FAILURE;
+        }
+
+	// read/write extra command data
+	if (dxfer_direction == CBW_DATA_OUT) {
+		int bytes_send = 0;
+		while (bytes_send < dxfer_length) {
+			int plen;
+
+			plen = dxfer_length - bytes_send;
+			if (plen > USB_MAX_PACKET_LEN)
+				plen = USB_MAX_PACKET_LEN;
+
+			if (usb_bulk_write(handle_wrapper->handle,
+					handle_wrapper->ep_out,
+					(char *) dxfer_data+bytes_send, plen,
+					U3_DEVICE_TIMEOUT) != plen)
+			{
+				u3_set_error(device, "Failed executing scsi command: "
+					"Could not write data to device");
+				return U3_FAILURE;
+			}
+
+			bytes_send += plen;
+		}
+	} else if (dxfer_direction == CBW_DATA_IN) {
+		if (usb_bulk_read(handle_wrapper->handle, handle_wrapper->ep_in,
+				(char *) dxfer_data, dxfer_length,
+				U3_DEVICE_TIMEOUT) != dxfer_length)
+		{
+			u3_set_error(device, "Failed executing scsi command: "
+				"Could not read data from device");
+			return U3_FAILURE;
+		}
+	}
+
+	// read command status
+        if (usb_bulk_read(handle_wrapper->handle, handle_wrapper->ep_in,
+			(char *) &csw, sizeof(csw), U3_DEVICE_TIMEOUT)
+                        != sizeof(csw))
+        {
+		u3_set_error(device, "Failed executing scsi command: "
+			"Could not read command status from device");
+		return U3_FAILURE;
+        }
+
+        *status = csw.bCSWStatus;
+
+	return U3_SUCCESS;
+}
+

+ 189 - 0
src/u3_tool.dev

@@ -0,0 +1,189 @@
+[Project]
+FileName=u3_tool.dev
+Name=u3_tool
+UnitCount=14
+Type=1
+Ver=1
+ObjFiles=
+Includes=
+Libs=
+PrivateResource=
+ResourceIncludes=
+MakeIncludes=
+Compiler=
+CppCompiler=
+Linker=
+IsCpp=1
+Icon=
+ExeOutput=
+ObjectOutput=
+OverrideOutput=0
+OverrideOutputName=u3_tool.exe
+HostApplication=
+Folders=
+CommandLine=
+UseCustomMakefile=0
+CustomMakefile=
+IncludeVersionInfo=0
+SupportXPThemes=0
+CompilerSet=0
+CompilerSettings=0000000000000000000000
+
+[Unit1]
+FileName=display_progress.c
+CompileCpp=1
+Folder=u3_tool
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit2]
+FileName=display_progress.h
+CompileCpp=1
+Folder=u3_tool
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit3]
+FileName=main.c
+CompileCpp=1
+Folder=u3_tool
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit4]
+FileName=md5.c
+CompileCpp=1
+Folder=u3_tool
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit5]
+FileName=md5.h
+CompileCpp=1
+Folder=u3_tool
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit6]
+FileName=secure_input.c
+CompileCpp=1
+Folder=u3_tool
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit7]
+FileName=secure_input.h
+CompileCpp=1
+Folder=u3_tool
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit8]
+FileName=u3.h
+CompileCpp=1
+Folder=u3_tool
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit9]
+FileName=u3_commands.c
+CompileCpp=1
+Folder=u3_tool
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit10]
+FileName=u3_commands.h
+CompileCpp=1
+Folder=u3_tool
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit11]
+FileName=u3_error.c
+CompileCpp=1
+Folder=u3_tool
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit12]
+FileName=u3_error.h
+CompileCpp=1
+Folder=u3_tool
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit13]
+FileName=u3_scsi.h
+CompileCpp=1
+Folder=u3_tool
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit14]
+FileName=u3_scsi_spt.c
+CompileCpp=1
+Folder=u3_tool
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[VersionInfo]
+Major=0
+Minor=1
+Release=1
+Build=1
+LanguageID=1033
+CharsetID=1252
+CompanyName=
+FileVersion=
+FileDescription=Developed using the Dev-C++ IDE
+InternalName=
+LegalCopyright=
+LegalTrademarks=
+OriginalFilename=
+ProductName=
+ProductVersion=
+AutoIncBuildNr=0
+