summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml186
-rw-r--r--.gitignore39
-rw-r--r--COPYING502
-rw-r--r--LICENSE674
-rw-r--r--Makefile37
-rw-r--r--Makefile.am14
-rw-r--r--NEWS124
-rw-r--r--README22
-rw-r--r--README.md258
-rw-r--r--TODO15
-rwxr-xr-xautogen.sh26
-rw-r--r--configure.ac230
-rwxr-xr-xgit-version-gen20
-rw-r--r--include/Makefile.am1
-rw-r--r--include/libirecovery.h240
-rw-r--r--m4/as-compiler-flag.m462
-rw-r--r--scripts/boot.irs5
-rw-r--r--scripts/test.irs21
-rw-r--r--src/Makefile.am24
-rw-r--r--src/irecovery.c355
-rw-r--r--src/libirecovery-1.0.pc.in11
-rw-r--r--src/libirecovery.c3828
-rw-r--r--tools/Makefile.am13
-rw-r--r--tools/irecovery.c707
-rw-r--r--udev/39-libirecovery.rules.in8
-rw-r--r--udev/Makefile.am21
26 files changed, 5979 insertions, 1464 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..9d36f2a
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,186 @@
1name: build
2
3on:
4 push:
5 schedule:
6 - cron: '0 0 1 * *'
7
8jobs:
9 build-linux-ubuntu:
10 runs-on: ubuntu-latest
11 steps:
12 - name: install dependencies
13 run: |
14 sudo apt-get update
15 sudo apt-get install libusb-1.0-0-dev
16 - name: prepare environment
17 run: |
18 echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV
19 - name: fetch libplist
20 uses: dawidd6/action-download-artifact@v3
21 with:
22 github_token: ${{secrets.GITHUB_TOKEN}}
23 workflow: build.yml
24 name: libplist-latest_${{env.target_triplet}}
25 repo: libimobiledevice/libplist
26 - name: fetch libimobiledevice-glue
27 uses: dawidd6/action-download-artifact@v3
28 with:
29 github_token: ${{secrets.GITHUB_TOKEN}}
30 workflow: build.yml
31 name: libimobiledevice-glue-latest_${{env.target_triplet}}
32 repo: libimobiledevice/libimobiledevice-glue
33 - name: install external dependencies
34 run: |
35 mkdir extract
36 for I in *.tar; do
37 tar -C extract -xvf $I
38 done
39 sudo cp -r extract/* /
40 sudo ldconfig
41 - uses: actions/checkout@v4
42 - name: autogen
43 run: ./autogen.sh PKG_CONFIG_PATH=/usr/local/lib/pkgconfig LDFLAGS="-Wl,-rpath=/usr/local/lib"
44 - name: make
45 run: make
46 - name: make install
47 run: sudo make install
48 - name: prepare artifact
49 run: |
50 mkdir -p dest
51 DESTDIR=`pwd`/dest make install
52 tar -C dest -cf libirecovery.tar lib usr
53 - name: publish artifact
54 uses: actions/upload-artifact@v4
55 with:
56 name: libirecovery-latest_${{env.target_triplet}}
57 path: libirecovery.tar
58 build-macOS:
59 runs-on: macOS-latest
60 steps:
61 - name: install dependencies
62 run: |
63 if test -x "`which port`"; then
64 sudo port install libtool autoconf automake pkgconfig
65 else
66 brew install libtool autoconf automake pkgconfig
67 fi
68 shell: bash
69 - name: fetch libplist
70 uses: dawidd6/action-download-artifact@v3
71 with:
72 github_token: ${{secrets.GITHUB_TOKEN}}
73 workflow: build.yml
74 name: libplist-latest_macOS
75 repo: libimobiledevice/libplist
76 - name: fetch libimobiledevice-glue
77 uses: dawidd6/action-download-artifact@v3
78 with:
79 github_token: ${{secrets.GITHUB_TOKEN}}
80 workflow: build.yml
81 name: libimobiledevice-glue-latest_macOS
82 repo: libimobiledevice/libimobiledevice-glue
83 - name: install external dependencies
84 run: |
85 mkdir extract
86 for I in *.tar; do
87 tar -C extract -xvf $I
88 done
89 sudo cp -r extract/* /
90 - uses: actions/checkout@v4
91 - name: autogen
92 run: |
93 SDKDIR=`xcrun --sdk macosx --show-sdk-path`
94 TESTARCHS="arm64 x86_64"
95 USEARCHS=
96 for ARCH in $TESTARCHS; do
97 if echo "int main(int argc, char **argv) { return 0; }" |clang -arch $ARCH -o /dev/null -isysroot $SDKDIR -x c - 2>/dev/null; then
98 USEARCHS="$USEARCHS -arch $ARCH"
99 fi
100 done
101 export CFLAGS="$USEARCHS -isysroot $SDKDIR"
102 echo "Using CFLAGS: $CFLAGS"
103 ./autogen.sh PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
104 - name: make
105 run: make
106 - name: make install
107 run: sudo make install
108 - name: prepare artifact
109 run: |
110 mkdir -p dest
111 DESTDIR=`pwd`/dest make install
112 tar -C dest -cf libirecovery.tar usr
113 - name: publish artifact
114 uses: actions/upload-artifact@v4
115 with:
116 name: libirecovery-latest_macOS
117 path: libirecovery.tar
118 build-windows:
119 runs-on: windows-2019
120 defaults:
121 run:
122 shell: msys2 {0}
123 strategy:
124 fail-fast: false
125 matrix:
126 include: [
127 { msystem: MINGW64, arch: x86_64 },
128 { msystem: MINGW32, arch: i686 }
129 ]
130 steps:
131 - uses: msys2/setup-msys2@v2
132 with:
133 msystem: ${{ matrix.msystem }}
134 release: false
135 update: false
136 install: >-
137 base-devel
138 git
139 mingw-w64-${{ matrix.arch }}-gcc
140 make
141 libtool
142 autoconf
143 automake-wrapper
144 - name: prepare environment
145 run: |
146 dest=`echo ${{ matrix.msystem }} |tr [:upper:] [:lower:]`
147 echo "dest=$dest" >> $GITHUB_ENV
148 echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV
149 - name: fetch libplist
150 uses: dawidd6/action-download-artifact@v3
151 with:
152 github_token: ${{secrets.GITHUB_TOKEN}}
153 workflow: build.yml
154 name: libplist-latest_${{ matrix.arch }}-${{ env.dest }}
155 repo: libimobiledevice/libplist
156 - name: fetch libimobiledevice-glue
157 uses: dawidd6/action-download-artifact@v3
158 with:
159 github_token: ${{secrets.GITHUB_TOKEN}}
160 workflow: build.yml
161 name: libimobiledevice-glue-latest_${{ matrix.arch }}-${{ env.dest }}
162 repo: libimobiledevice/libimobiledevice-glue
163 - name: install external dependencies
164 run: |
165 mkdir extract
166 for I in *.tar; do
167 tar -C extract -xvf $I
168 done
169 cp -r extract/* /
170 - uses: actions/checkout@v4
171 - name: autogen
172 run: ./autogen.sh CC=gcc CXX=g++
173 - name: make
174 run: make
175 - name: make install
176 run: make install
177 - name: prepare artifact
178 run: |
179 mkdir -p dest
180 DESTDIR=`pwd`/dest make install
181 tar -C dest -cf libirecovery.tar ${{ env.dest }}
182 - name: publish artifact
183 uses: actions/upload-artifact@v4
184 with:
185 name: libirecovery-latest_${{ matrix.arch }}-${{ env.dest }}
186 path: libirecovery.tar
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1b7c628
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,39 @@
1*.[oa]
2*~
3*.po
4*.lo
5*.la
6autom4te.cache/*
7*.in
8*/.deps/*
9m4/*
10*.dll
11*.so
12*.dylib
13*.patch
14aclocal.m4
15config.h
16config.log
17config.sub
18config.guess
19config.status
20configure
21depcomp
22install-sh
23compile
24main
25ltmain.sh
26missing
27mkinstalldirs
28libtool
29*Makefile
30stamp-h1
31src/.libs
32*.pc
33tools/.libs/*
34tools/irecovery
35.irecovery
36udev/39-libirecovery.rules
37.idea
38.vscode
39.DS_Store
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..f166cc5
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,502 @@
1 GNU LESSER GENERAL PUBLIC LICENSE
2 Version 2.1, February 1999
3
4 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
5 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 Everyone is permitted to copy and distribute verbatim copies
7 of this license document, but changing it is not allowed.
8
9[This is the first released version of the Lesser GPL. It also counts
10 as the successor of the GNU Library Public License, version 2, hence
11 the version number 2.1.]
12
13 Preamble
14
15 The licenses for most software are designed to take away your
16freedom to share and change it. By contrast, the GNU General Public
17Licenses are intended to guarantee your freedom to share and change
18free software--to make sure the software is free for all its users.
19
20 This license, the Lesser General Public License, applies to some
21specially designated software packages--typically libraries--of the
22Free Software Foundation and other authors who decide to use it. You
23can use it too, but we suggest you first think carefully about whether
24this license or the ordinary General Public License is the better
25strategy to use in any particular case, based on the explanations below.
26
27 When we speak of free software, we are referring to freedom of use,
28not price. Our General Public Licenses are designed to make sure that
29you have the freedom to distribute copies of free software (and charge
30for this service if you wish); that you receive source code or can get
31it if you want it; that you can change the software and use pieces of
32it in new free programs; and that you are informed that you can do
33these things.
34
35 To protect your rights, we need to make restrictions that forbid
36distributors to deny you these rights or to ask you to surrender these
37rights. These restrictions translate to certain responsibilities for
38you if you distribute copies of the library or if you modify it.
39
40 For example, if you distribute copies of the library, whether gratis
41or for a fee, you must give the recipients all the rights that we gave
42you. You must make sure that they, too, receive or can get the source
43code. If you link other code with the library, you must provide
44complete object files to the recipients, so that they can relink them
45with the library after making changes to the library and recompiling
46it. And you must show them these terms so they know their rights.
47
48 We protect your rights with a two-step method: (1) we copyright the
49library, and (2) we offer you this license, which gives you legal
50permission to copy, distribute and/or modify the library.
51
52 To protect each distributor, we want to make it very clear that
53there is no warranty for the free library. Also, if the library is
54modified by someone else and passed on, the recipients should know
55that what they have is not the original version, so that the original
56author's reputation will not be affected by problems that might be
57introduced by others.
58
59 Finally, software patents pose a constant threat to the existence of
60any free program. We wish to make sure that a company cannot
61effectively restrict the users of a free program by obtaining a
62restrictive license from a patent holder. Therefore, we insist that
63any patent license obtained for a version of the library must be
64consistent with the full freedom of use specified in this license.
65
66 Most GNU software, including some libraries, is covered by the
67ordinary GNU General Public License. This license, the GNU Lesser
68General Public License, applies to certain designated libraries, and
69is quite different from the ordinary General Public License. We use
70this license for certain libraries in order to permit linking those
71libraries into non-free programs.
72
73 When a program is linked with a library, whether statically or using
74a shared library, the combination of the two is legally speaking a
75combined work, a derivative of the original library. The ordinary
76General Public License therefore permits such linking only if the
77entire combination fits its criteria of freedom. The Lesser General
78Public License permits more lax criteria for linking other code with
79the library.
80
81 We call this license the "Lesser" General Public License because it
82does Less to protect the user's freedom than the ordinary General
83Public License. It also provides other free software developers Less
84of an advantage over competing non-free programs. These disadvantages
85are the reason we use the ordinary General Public License for many
86libraries. However, the Lesser license provides advantages in certain
87special circumstances.
88
89 For example, on rare occasions, there may be a special need to
90encourage the widest possible use of a certain library, so that it becomes
91a de-facto standard. To achieve this, non-free programs must be
92allowed to use the library. A more frequent case is that a free
93library does the same job as widely used non-free libraries. In this
94case, there is little to gain by limiting the free library to free
95software only, so we use the Lesser General Public License.
96
97 In other cases, permission to use a particular library in non-free
98programs enables a greater number of people to use a large body of
99free software. For example, permission to use the GNU C Library in
100non-free programs enables many more people to use the whole GNU
101operating system, as well as its variant, the GNU/Linux operating
102system.
103
104 Although the Lesser General Public License is Less protective of the
105users' freedom, it does ensure that the user of a program that is
106linked with the Library has the freedom and the wherewithal to run
107that program using a modified version of the Library.
108
109 The precise terms and conditions for copying, distribution and
110modification follow. Pay close attention to the difference between a
111"work based on the library" and a "work that uses the library". The
112former contains code derived from the library, whereas the latter must
113be combined with the library in order to run.
114
115 GNU LESSER GENERAL PUBLIC LICENSE
116 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
117
118 0. This License Agreement applies to any software library or other
119program which contains a notice placed by the copyright holder or
120other authorized party saying it may be distributed under the terms of
121this Lesser General Public License (also called "this License").
122Each licensee is addressed as "you".
123
124 A "library" means a collection of software functions and/or data
125prepared so as to be conveniently linked with application programs
126(which use some of those functions and data) to form executables.
127
128 The "Library", below, refers to any such software library or work
129which has been distributed under these terms. A "work based on the
130Library" means either the Library or any derivative work under
131copyright law: that is to say, a work containing the Library or a
132portion of it, either verbatim or with modifications and/or translated
133straightforwardly into another language. (Hereinafter, translation is
134included without limitation in the term "modification".)
135
136 "Source code" for a work means the preferred form of the work for
137making modifications to it. For a library, complete source code means
138all the source code for all modules it contains, plus any associated
139interface definition files, plus the scripts used to control compilation
140and installation of the library.
141
142 Activities other than copying, distribution and modification are not
143covered by this License; they are outside its scope. The act of
144running a program using the Library is not restricted, and output from
145such a program is covered only if its contents constitute a work based
146on the Library (independent of the use of the Library in a tool for
147writing it). Whether that is true depends on what the Library does
148and what the program that uses the Library does.
149
150 1. You may copy and distribute verbatim copies of the Library's
151complete source code as you receive it, in any medium, provided that
152you conspicuously and appropriately publish on each copy an
153appropriate copyright notice and disclaimer of warranty; keep intact
154all the notices that refer to this License and to the absence of any
155warranty; and distribute a copy of this License along with the
156Library.
157
158 You may charge a fee for the physical act of transferring a copy,
159and you may at your option offer warranty protection in exchange for a
160fee.
161
162 2. You may modify your copy or copies of the Library or any portion
163of it, thus forming a work based on the Library, and copy and
164distribute such modifications or work under the terms of Section 1
165above, provided that you also meet all of these conditions:
166
167 a) The modified work must itself be a software library.
168
169 b) You must cause the files modified to carry prominent notices
170 stating that you changed the files and the date of any change.
171
172 c) You must cause the whole of the work to be licensed at no
173 charge to all third parties under the terms of this License.
174
175 d) If a facility in the modified Library refers to a function or a
176 table of data to be supplied by an application program that uses
177 the facility, other than as an argument passed when the facility
178 is invoked, then you must make a good faith effort to ensure that,
179 in the event an application does not supply such function or
180 table, the facility still operates, and performs whatever part of
181 its purpose remains meaningful.
182
183 (For example, a function in a library to compute square roots has
184 a purpose that is entirely well-defined independent of the
185 application. Therefore, Subsection 2d requires that any
186 application-supplied function or table used by this function must
187 be optional: if the application does not supply it, the square
188 root function must still compute square roots.)
189
190These requirements apply to the modified work as a whole. If
191identifiable sections of that work are not derived from the Library,
192and can be reasonably considered independent and separate works in
193themselves, then this License, and its terms, do not apply to those
194sections when you distribute them as separate works. But when you
195distribute the same sections as part of a whole which is a work based
196on the Library, the distribution of the whole must be on the terms of
197this License, whose permissions for other licensees extend to the
198entire whole, and thus to each and every part regardless of who wrote
199it.
200
201Thus, it is not the intent of this section to claim rights or contest
202your rights to work written entirely by you; rather, the intent is to
203exercise the right to control the distribution of derivative or
204collective works based on the Library.
205
206In addition, mere aggregation of another work not based on the Library
207with the Library (or with a work based on the Library) on a volume of
208a storage or distribution medium does not bring the other work under
209the scope of this License.
210
211 3. You may opt to apply the terms of the ordinary GNU General Public
212License instead of this License to a given copy of the Library. To do
213this, you must alter all the notices that refer to this License, so
214that they refer to the ordinary GNU General Public License, version 2,
215instead of to this License. (If a newer version than version 2 of the
216ordinary GNU General Public License has appeared, then you can specify
217that version instead if you wish.) Do not make any other change in
218these notices.
219
220 Once this change is made in a given copy, it is irreversible for
221that copy, so the ordinary GNU General Public License applies to all
222subsequent copies and derivative works made from that copy.
223
224 This option is useful when you wish to copy part of the code of
225the Library into a program that is not a library.
226
227 4. You may copy and distribute the Library (or a portion or
228derivative of it, under Section 2) in object code or executable form
229under the terms of Sections 1 and 2 above provided that you accompany
230it with the complete corresponding machine-readable source code, which
231must be distributed under the terms of Sections 1 and 2 above on a
232medium customarily used for software interchange.
233
234 If distribution of object code is made by offering access to copy
235from a designated place, then offering equivalent access to copy the
236source code from the same place satisfies the requirement to
237distribute the source code, even though third parties are not
238compelled to copy the source along with the object code.
239
240 5. A program that contains no derivative of any portion of the
241Library, but is designed to work with the Library by being compiled or
242linked with it, is called a "work that uses the Library". Such a
243work, in isolation, is not a derivative work of the Library, and
244therefore falls outside the scope of this License.
245
246 However, linking a "work that uses the Library" with the Library
247creates an executable that is a derivative of the Library (because it
248contains portions of the Library), rather than a "work that uses the
249library". The executable is therefore covered by this License.
250Section 6 states terms for distribution of such executables.
251
252 When a "work that uses the Library" uses material from a header file
253that is part of the Library, the object code for the work may be a
254derivative work of the Library even though the source code is not.
255Whether this is true is especially significant if the work can be
256linked without the Library, or if the work is itself a library. The
257threshold for this to be true is not precisely defined by law.
258
259 If such an object file uses only numerical parameters, data
260structure layouts and accessors, and small macros and small inline
261functions (ten lines or less in length), then the use of the object
262file is unrestricted, regardless of whether it is legally a derivative
263work. (Executables containing this object code plus portions of the
264Library will still fall under Section 6.)
265
266 Otherwise, if the work is a derivative of the Library, you may
267distribute the object code for the work under the terms of Section 6.
268Any executables containing that work also fall under Section 6,
269whether or not they are linked directly with the Library itself.
270
271 6. As an exception to the Sections above, you may also combine or
272link a "work that uses the Library" with the Library to produce a
273work containing portions of the Library, and distribute that work
274under terms of your choice, provided that the terms permit
275modification of the work for the customer's own use and reverse
276engineering for debugging such modifications.
277
278 You must give prominent notice with each copy of the work that the
279Library is used in it and that the Library and its use are covered by
280this License. You must supply a copy of this License. If the work
281during execution displays copyright notices, you must include the
282copyright notice for the Library among them, as well as a reference
283directing the user to the copy of this License. Also, you must do one
284of these things:
285
286 a) Accompany the work with the complete corresponding
287 machine-readable source code for the Library including whatever
288 changes were used in the work (which must be distributed under
289 Sections 1 and 2 above); and, if the work is an executable linked
290 with the Library, with the complete machine-readable "work that
291 uses the Library", as object code and/or source code, so that the
292 user can modify the Library and then relink to produce a modified
293 executable containing the modified Library. (It is understood
294 that the user who changes the contents of definitions files in the
295 Library will not necessarily be able to recompile the application
296 to use the modified definitions.)
297
298 b) Use a suitable shared library mechanism for linking with the
299 Library. A suitable mechanism is one that (1) uses at run time a
300 copy of the library already present on the user's computer system,
301 rather than copying library functions into the executable, and (2)
302 will operate properly with a modified version of the library, if
303 the user installs one, as long as the modified version is
304 interface-compatible with the version that the work was made with.
305
306 c) Accompany the work with a written offer, valid for at
307 least three years, to give the same user the materials
308 specified in Subsection 6a, above, for a charge no more
309 than the cost of performing this distribution.
310
311 d) If distribution of the work is made by offering access to copy
312 from a designated place, offer equivalent access to copy the above
313 specified materials from the same place.
314
315 e) Verify that the user has already received a copy of these
316 materials or that you have already sent this user a copy.
317
318 For an executable, the required form of the "work that uses the
319Library" must include any data and utility programs needed for
320reproducing the executable from it. However, as a special exception,
321the materials to be distributed need not include anything that is
322normally distributed (in either source or binary form) with the major
323components (compiler, kernel, and so on) of the operating system on
324which the executable runs, unless that component itself accompanies
325the executable.
326
327 It may happen that this requirement contradicts the license
328restrictions of other proprietary libraries that do not normally
329accompany the operating system. Such a contradiction means you cannot
330use both them and the Library together in an executable that you
331distribute.
332
333 7. You may place library facilities that are a work based on the
334Library side-by-side in a single library together with other library
335facilities not covered by this License, and distribute such a combined
336library, provided that the separate distribution of the work based on
337the Library and of the other library facilities is otherwise
338permitted, and provided that you do these two things:
339
340 a) Accompany the combined library with a copy of the same work
341 based on the Library, uncombined with any other library
342 facilities. This must be distributed under the terms of the
343 Sections above.
344
345 b) Give prominent notice with the combined library of the fact
346 that part of it is a work based on the Library, and explaining
347 where to find the accompanying uncombined form of the same work.
348
349 8. You may not copy, modify, sublicense, link with, or distribute
350the Library except as expressly provided under this License. Any
351attempt otherwise to copy, modify, sublicense, link with, or
352distribute the Library is void, and will automatically terminate your
353rights under this License. However, parties who have received copies,
354or rights, from you under this License will not have their licenses
355terminated so long as such parties remain in full compliance.
356
357 9. You are not required to accept this License, since you have not
358signed it. However, nothing else grants you permission to modify or
359distribute the Library or its derivative works. These actions are
360prohibited by law if you do not accept this License. Therefore, by
361modifying or distributing the Library (or any work based on the
362Library), you indicate your acceptance of this License to do so, and
363all its terms and conditions for copying, distributing or modifying
364the Library or works based on it.
365
366 10. Each time you redistribute the Library (or any work based on the
367Library), the recipient automatically receives a license from the
368original licensor to copy, distribute, link with or modify the Library
369subject to these terms and conditions. You may not impose any further
370restrictions on the recipients' exercise of the rights granted herein.
371You are not responsible for enforcing compliance by third parties with
372this License.
373
374 11. If, as a consequence of a court judgment or allegation of patent
375infringement or for any other reason (not limited to patent issues),
376conditions are imposed on you (whether by court order, agreement or
377otherwise) that contradict the conditions of this License, they do not
378excuse you from the conditions of this License. If you cannot
379distribute so as to satisfy simultaneously your obligations under this
380License and any other pertinent obligations, then as a consequence you
381may not distribute the Library at all. For example, if a patent
382license would not permit royalty-free redistribution of the Library by
383all those who receive copies directly or indirectly through you, then
384the only way you could satisfy both it and this License would be to
385refrain entirely from distribution of the Library.
386
387If any portion of this section is held invalid or unenforceable under any
388particular circumstance, the balance of the section is intended to apply,
389and the section as a whole is intended to apply in other circumstances.
390
391It is not the purpose of this section to induce you to infringe any
392patents or other property right claims or to contest validity of any
393such claims; this section has the sole purpose of protecting the
394integrity of the free software distribution system which is
395implemented by public license practices. Many people have made
396generous contributions to the wide range of software distributed
397through that system in reliance on consistent application of that
398system; it is up to the author/donor to decide if he or she is willing
399to distribute software through any other system and a licensee cannot
400impose that choice.
401
402This section is intended to make thoroughly clear what is believed to
403be a consequence of the rest of this License.
404
405 12. If the distribution and/or use of the Library is restricted in
406certain countries either by patents or by copyrighted interfaces, the
407original copyright holder who places the Library under this License may add
408an explicit geographical distribution limitation excluding those countries,
409so that distribution is permitted only in or among countries not thus
410excluded. In such case, this License incorporates the limitation as if
411written in the body of this License.
412
413 13. The Free Software Foundation may publish revised and/or new
414versions of the Lesser General Public License from time to time.
415Such new versions will be similar in spirit to the present version,
416but may differ in detail to address new problems or concerns.
417
418Each version is given a distinguishing version number. If the Library
419specifies a version number of this License which applies to it and
420"any later version", you have the option of following the terms and
421conditions either of that version or of any later version published by
422the Free Software Foundation. If the Library does not specify a
423license version number, you may choose any version ever published by
424the Free Software Foundation.
425
426 14. If you wish to incorporate parts of the Library into other free
427programs whose distribution conditions are incompatible with these,
428write to the author to ask for permission. For software which is
429copyrighted by the Free Software Foundation, write to the Free
430Software Foundation; we sometimes make exceptions for this. Our
431decision will be guided by the two goals of preserving the free status
432of all derivatives of our free software and of promoting the sharing
433and reuse of software generally.
434
435 NO WARRANTY
436
437 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
438WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
439EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
440OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
441KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
442IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
443PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
444LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
445THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
446
447 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
448WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
449AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
450FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
451CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
452LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
453RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
454FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
455SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
456DAMAGES.
457
458 END OF TERMS AND CONDITIONS
459
460 How to Apply These Terms to Your New Libraries
461
462 If you develop a new library, and you want it to be of the greatest
463possible use to the public, we recommend making it free software that
464everyone can redistribute and change. You can do so by permitting
465redistribution under these terms (or, alternatively, under the terms of the
466ordinary General Public License).
467
468 To apply these terms, attach the following notices to the library. It is
469safest to attach them to the start of each source file to most effectively
470convey the exclusion of warranty; and each file should have at least the
471"copyright" line and a pointer to where the full notice is found.
472
473 <one line to give the library's name and a brief idea of what it does.>
474 Copyright (C) <year> <name of author>
475
476 This library is free software; you can redistribute it and/or
477 modify it under the terms of the GNU Lesser General Public
478 License as published by the Free Software Foundation; either
479 version 2.1 of the License, or (at your option) any later version.
480
481 This library is distributed in the hope that it will be useful,
482 but WITHOUT ANY WARRANTY; without even the implied warranty of
483 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
484 Lesser General Public License for more details.
485
486 You should have received a copy of the GNU Lesser General Public
487 License along with this library; if not, write to the Free Software
488 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
489
490Also add information on how to contact you by electronic and paper mail.
491
492You should also get your employer (if you work as a programmer) or your
493school, if any, to sign a "copyright disclaimer" for the library, if
494necessary. Here is a sample; alter the names:
495
496 Yoyodyne, Inc., hereby disclaims all copyright interest in the
497 library `Frob' (a library for tweaking knobs) written by James Random Hacker.
498
499 <signature of Ty Coon>, 1 April 1990
500 Ty Coon, President of Vice
501
502That's all there is to it! \ No newline at end of file
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 20d40b6..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,674 +0,0 @@
1 GNU GENERAL PUBLIC LICENSE
2 Version 3, 29 June 2007
3
4 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5 Everyone is permitted to copy and distribute verbatim copies
6 of this license document, but changing it is not allowed.
7
8 Preamble
9
10 The GNU General Public License is a free, copyleft license for
11software and other kinds of works.
12
13 The licenses for most software and other practical works are designed
14to take away your freedom to share and change the works. By contrast,
15the GNU General Public License is intended to guarantee your freedom to
16share and change all versions of a program--to make sure it remains free
17software for all its users. We, the Free Software Foundation, use the
18GNU General Public License for most of our software; it applies also to
19any other work released this way by its authors. You can apply it to
20your programs, too.
21
22 When we speak of free software, we are referring to freedom, not
23price. Our General Public Licenses are designed to make sure that you
24have the freedom to distribute copies of free software (and charge for
25them if you wish), that you receive source code or can get it if you
26want it, that you can change the software or use pieces of it in new
27free programs, and that you know you can do these things.
28
29 To protect your rights, we need to prevent others from denying you
30these rights or asking you to surrender the rights. Therefore, you have
31certain responsibilities if you distribute copies of the software, or if
32you modify it: responsibilities to respect the freedom of others.
33
34 For example, if you distribute copies of such a program, whether
35gratis or for a fee, you must pass on to the recipients the same
36freedoms that you received. You must make sure that they, too, receive
37or can get the source code. And you must show them these terms so they
38know their rights.
39
40 Developers that use the GNU GPL protect your rights with two steps:
41(1) assert copyright on the software, and (2) offer you this License
42giving you legal permission to copy, distribute and/or modify it.
43
44 For the developers' and authors' protection, the GPL clearly explains
45that there is no warranty for this free software. For both users' and
46authors' sake, the GPL requires that modified versions be marked as
47changed, so that their problems will not be attributed erroneously to
48authors of previous versions.
49
50 Some devices are designed to deny users access to install or run
51modified versions of the software inside them, although the manufacturer
52can do so. This is fundamentally incompatible with the aim of
53protecting users' freedom to change the software. The systematic
54pattern of such abuse occurs in the area of products for individuals to
55use, which is precisely where it is most unacceptable. Therefore, we
56have designed this version of the GPL to prohibit the practice for those
57products. If such problems arise substantially in other domains, we
58stand ready to extend this provision to those domains in future versions
59of the GPL, as needed to protect the freedom of users.
60
61 Finally, every program is threatened constantly by software patents.
62States should not allow patents to restrict development and use of
63software on general-purpose computers, but in those that do, we wish to
64avoid the special danger that patents applied to a free program could
65make it effectively proprietary. To prevent this, the GPL assures that
66patents cannot be used to render the program non-free.
67
68 The precise terms and conditions for copying, distribution and
69modification follow.
70
71 TERMS AND CONDITIONS
72
73 0. Definitions.
74
75 "This License" refers to version 3 of the GNU General Public License.
76
77 "Copyright" also means copyright-like laws that apply to other kinds of
78works, such as semiconductor masks.
79
80 "The Program" refers to any copyrightable work licensed under this
81License. Each licensee is addressed as "you". "Licensees" and
82"recipients" may be individuals or organizations.
83
84 To "modify" a work means to copy from or adapt all or part of the work
85in a fashion requiring copyright permission, other than the making of an
86exact copy. The resulting work is called a "modified version" of the
87earlier work or a work "based on" the earlier work.
88
89 A "covered work" means either the unmodified Program or a work based
90on the Program.
91
92 To "propagate" a work means to do anything with it that, without
93permission, would make you directly or secondarily liable for
94infringement under applicable copyright law, except executing it on a
95computer or modifying a private copy. Propagation includes copying,
96distribution (with or without modification), making available to the
97public, and in some countries other activities as well.
98
99 To "convey" a work means any kind of propagation that enables other
100parties to make or receive copies. Mere interaction with a user through
101a computer network, with no transfer of a copy, is not conveying.
102
103 An interactive user interface displays "Appropriate Legal Notices"
104to the extent that it includes a convenient and prominently visible
105feature that (1) displays an appropriate copyright notice, and (2)
106tells the user that there is no warranty for the work (except to the
107extent that warranties are provided), that licensees may convey the
108work under this License, and how to view a copy of this License. If
109the interface presents a list of user commands or options, such as a
110menu, a prominent item in the list meets this criterion.
111
112 1. Source Code.
113
114 The "source code" for a work means the preferred form of the work
115for making modifications to it. "Object code" means any non-source
116form of a work.
117
118 A "Standard Interface" means an interface that either is an official
119standard defined by a recognized standards body, or, in the case of
120interfaces specified for a particular programming language, one that
121is widely used among developers working in that language.
122
123 The "System Libraries" of an executable work include anything, other
124than the work as a whole, that (a) is included in the normal form of
125packaging a Major Component, but which is not part of that Major
126Component, and (b) serves only to enable use of the work with that
127Major Component, or to implement a Standard Interface for which an
128implementation is available to the public in source code form. A
129"Major Component", in this context, means a major essential component
130(kernel, window system, and so on) of the specific operating system
131(if any) on which the executable work runs, or a compiler used to
132produce the work, or an object code interpreter used to run it.
133
134 The "Corresponding Source" for a work in object code form means all
135the source code needed to generate, install, and (for an executable
136work) run the object code and to modify the work, including scripts to
137control those activities. However, it does not include the work's
138System Libraries, or general-purpose tools or generally available free
139programs which are used unmodified in performing those activities but
140which are not part of the work. For example, Corresponding Source
141includes interface definition files associated with source files for
142the work, and the source code for shared libraries and dynamically
143linked subprograms that the work is specifically designed to require,
144such as by intimate data communication or control flow between those
145subprograms and other parts of the work.
146
147 The Corresponding Source need not include anything that users
148can regenerate automatically from other parts of the Corresponding
149Source.
150
151 The Corresponding Source for a work in source code form is that
152same work.
153
154 2. Basic Permissions.
155
156 All rights granted under this License are granted for the term of
157copyright on the Program, and are irrevocable provided the stated
158conditions are met. This License explicitly affirms your unlimited
159permission to run the unmodified Program. The output from running a
160covered work is covered by this License only if the output, given its
161content, constitutes a covered work. This License acknowledges your
162rights of fair use or other equivalent, as provided by copyright law.
163
164 You may make, run and propagate covered works that you do not
165convey, without conditions so long as your license otherwise remains
166in force. You may convey covered works to others for the sole purpose
167of having them make modifications exclusively for you, or provide you
168with facilities for running those works, provided that you comply with
169the terms of this License in conveying all material for which you do
170not control copyright. Those thus making or running the covered works
171for you must do so exclusively on your behalf, under your direction
172and control, on terms that prohibit them from making any copies of
173your copyrighted material outside their relationship with you.
174
175 Conveying under any other circumstances is permitted solely under
176the conditions stated below. Sublicensing is not allowed; section 10
177makes it unnecessary.
178
179 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
181 No covered work shall be deemed part of an effective technological
182measure under any applicable law fulfilling obligations under article
18311 of the WIPO copyright treaty adopted on 20 December 1996, or
184similar laws prohibiting or restricting circumvention of such
185measures.
186
187 When you convey a covered work, you waive any legal power to forbid
188circumvention of technological measures to the extent such circumvention
189is effected by exercising rights under this License with respect to
190the covered work, and you disclaim any intention to limit operation or
191modification of the work as a means of enforcing, against the work's
192users, your or third parties' legal rights to forbid circumvention of
193technological measures.
194
195 4. Conveying Verbatim Copies.
196
197 You may convey verbatim copies of the Program's source code as you
198receive it, in any medium, provided that you conspicuously and
199appropriately publish on each copy an appropriate copyright notice;
200keep intact all notices stating that this License and any
201non-permissive terms added in accord with section 7 apply to the code;
202keep intact all notices of the absence of any warranty; and give all
203recipients a copy of this License along with the Program.
204
205 You may charge any price or no price for each copy that you convey,
206and you may offer support or warranty protection for a fee.
207
208 5. Conveying Modified Source Versions.
209
210 You may convey a work based on the Program, or the modifications to
211produce it from the Program, in the form of source code under the
212terms of section 4, provided that you also meet all of these conditions:
213
214 a) The work must carry prominent notices stating that you modified
215 it, and giving a relevant date.
216
217 b) The work must carry prominent notices stating that it is
218 released under this License and any conditions added under section
219 7. This requirement modifies the requirement in section 4 to
220 "keep intact all notices".
221
222 c) You must license the entire work, as a whole, under this
223 License to anyone who comes into possession of a copy. This
224 License will therefore apply, along with any applicable section 7
225 additional terms, to the whole of the work, and all its parts,
226 regardless of how they are packaged. This License gives no
227 permission to license the work in any other way, but it does not
228 invalidate such permission if you have separately received it.
229
230 d) If the work has interactive user interfaces, each must display
231 Appropriate Legal Notices; however, if the Program has interactive
232 interfaces that do not display Appropriate Legal Notices, your
233 work need not make them do so.
234
235 A compilation of a covered work with other separate and independent
236works, which are not by their nature extensions of the covered work,
237and which are not combined with it such as to form a larger program,
238in or on a volume of a storage or distribution medium, is called an
239"aggregate" if the compilation and its resulting copyright are not
240used to limit the access or legal rights of the compilation's users
241beyond what the individual works permit. Inclusion of a covered work
242in an aggregate does not cause this License to apply to the other
243parts of the aggregate.
244
245 6. Conveying Non-Source Forms.
246
247 You may convey a covered work in object code form under the terms
248of sections 4 and 5, provided that you also convey the
249machine-readable Corresponding Source under the terms of this License,
250in one of these ways:
251
252 a) Convey the object code in, or embodied in, a physical product
253 (including a physical distribution medium), accompanied by the
254 Corresponding Source fixed on a durable physical medium
255 customarily used for software interchange.
256
257 b) Convey the object code in, or embodied in, a physical product
258 (including a physical distribution medium), accompanied by a
259 written offer, valid for at least three years and valid for as
260 long as you offer spare parts or customer support for that product
261 model, to give anyone who possesses the object code either (1) a
262 copy of the Corresponding Source for all the software in the
263 product that is covered by this License, on a durable physical
264 medium customarily used for software interchange, for a price no
265 more than your reasonable cost of physically performing this
266 conveying of source, or (2) access to copy the
267 Corresponding Source from a network server at no charge.
268
269 c) Convey individual copies of the object code with a copy of the
270 written offer to provide the Corresponding Source. This
271 alternative is allowed only occasionally and noncommercially, and
272 only if you received the object code with such an offer, in accord
273 with subsection 6b.
274
275 d) Convey the object code by offering access from a designated
276 place (gratis or for a charge), and offer equivalent access to the
277 Corresponding Source in the same way through the same place at no
278 further charge. You need not require recipients to copy the
279 Corresponding Source along with the object code. If the place to
280 copy the object code is a network server, the Corresponding Source
281 may be on a different server (operated by you or a third party)
282 that supports equivalent copying facilities, provided you maintain
283 clear directions next to the object code saying where to find the
284 Corresponding Source. Regardless of what server hosts the
285 Corresponding Source, you remain obligated to ensure that it is
286 available for as long as needed to satisfy these requirements.
287
288 e) Convey the object code using peer-to-peer transmission, provided
289 you inform other peers where the object code and Corresponding
290 Source of the work are being offered to the general public at no
291 charge under subsection 6d.
292
293 A separable portion of the object code, whose source code is excluded
294from the Corresponding Source as a System Library, need not be
295included in conveying the object code work.
296
297 A "User Product" is either (1) a "consumer product", which means any
298tangible personal property which is normally used for personal, family,
299or household purposes, or (2) anything designed or sold for incorporation
300into a dwelling. In determining whether a product is a consumer product,
301doubtful cases shall be resolved in favor of coverage. For a particular
302product received by a particular user, "normally used" refers to a
303typical or common use of that class of product, regardless of the status
304of the particular user or of the way in which the particular user
305actually uses, or expects or is expected to use, the product. A product
306is a consumer product regardless of whether the product has substantial
307commercial, industrial or non-consumer uses, unless such uses represent
308the only significant mode of use of the product.
309
310 "Installation Information" for a User Product means any methods,
311procedures, authorization keys, or other information required to install
312and execute modified versions of a covered work in that User Product from
313a modified version of its Corresponding Source. The information must
314suffice to ensure that the continued functioning of the modified object
315code is in no case prevented or interfered with solely because
316modification has been made.
317
318 If you convey an object code work under this section in, or with, or
319specifically for use in, a User Product, and the conveying occurs as
320part of a transaction in which the right of possession and use of the
321User Product is transferred to the recipient in perpetuity or for a
322fixed term (regardless of how the transaction is characterized), the
323Corresponding Source conveyed under this section must be accompanied
324by the Installation Information. But this requirement does not apply
325if neither you nor any third party retains the ability to install
326modified object code on the User Product (for example, the work has
327been installed in ROM).
328
329 The requirement to provide Installation Information does not include a
330requirement to continue to provide support service, warranty, or updates
331for a work that has been modified or installed by the recipient, or for
332the User Product in which it has been modified or installed. Access to a
333network may be denied when the modification itself materially and
334adversely affects the operation of the network or violates the rules and
335protocols for communication across the network.
336
337 Corresponding Source conveyed, and Installation Information provided,
338in accord with this section must be in a format that is publicly
339documented (and with an implementation available to the public in
340source code form), and must require no special password or key for
341unpacking, reading or copying.
342
343 7. Additional Terms.
344
345 "Additional permissions" are terms that supplement the terms of this
346License by making exceptions from one or more of its conditions.
347Additional permissions that are applicable to the entire Program shall
348be treated as though they were included in this License, to the extent
349that they are valid under applicable law. If additional permissions
350apply only to part of the Program, that part may be used separately
351under those permissions, but the entire Program remains governed by
352this License without regard to the additional permissions.
353
354 When you convey a copy of a covered work, you may at your option
355remove any additional permissions from that copy, or from any part of
356it. (Additional permissions may be written to require their own
357removal in certain cases when you modify the work.) You may place
358additional permissions on material, added by you to a covered work,
359for which you have or can give appropriate copyright permission.
360
361 Notwithstanding any other provision of this License, for material you
362add to a covered work, you may (if authorized by the copyright holders of
363that material) supplement the terms of this License with terms:
364
365 a) Disclaiming warranty or limiting liability differently from the
366 terms of sections 15 and 16 of this License; or
367
368 b) Requiring preservation of specified reasonable legal notices or
369 author attributions in that material or in the Appropriate Legal
370 Notices displayed by works containing it; or
371
372 c) Prohibiting misrepresentation of the origin of that material, or
373 requiring that modified versions of such material be marked in
374 reasonable ways as different from the original version; or
375
376 d) Limiting the use for publicity purposes of names of licensors or
377 authors of the material; or
378
379 e) Declining to grant rights under trademark law for use of some
380 trade names, trademarks, or service marks; or
381
382 f) Requiring indemnification of licensors and authors of that
383 material by anyone who conveys the material (or modified versions of
384 it) with contractual assumptions of liability to the recipient, for
385 any liability that these contractual assumptions directly impose on
386 those licensors and authors.
387
388 All other non-permissive additional terms are considered "further
389restrictions" within the meaning of section 10. If the Program as you
390received it, or any part of it, contains a notice stating that it is
391governed by this License along with a term that is a further
392restriction, you may remove that term. If a license document contains
393a further restriction but permits relicensing or conveying under this
394License, you may add to a covered work material governed by the terms
395of that license document, provided that the further restriction does
396not survive such relicensing or conveying.
397
398 If you add terms to a covered work in accord with this section, you
399must place, in the relevant source files, a statement of the
400additional terms that apply to those files, or a notice indicating
401where to find the applicable terms.
402
403 Additional terms, permissive or non-permissive, may be stated in the
404form of a separately written license, or stated as exceptions;
405the above requirements apply either way.
406
407 8. Termination.
408
409 You may not propagate or modify a covered work except as expressly
410provided under this License. Any attempt otherwise to propagate or
411modify it is void, and will automatically terminate your rights under
412this License (including any patent licenses granted under the third
413paragraph of section 11).
414
415 However, if you cease all violation of this License, then your
416license from a particular copyright holder is reinstated (a)
417provisionally, unless and until the copyright holder explicitly and
418finally terminates your license, and (b) permanently, if the copyright
419holder fails to notify you of the violation by some reasonable means
420prior to 60 days after the cessation.
421
422 Moreover, your license from a particular copyright holder is
423reinstated permanently if the copyright holder notifies you of the
424violation by some reasonable means, this is the first time you have
425received notice of violation of this License (for any work) from that
426copyright holder, and you cure the violation prior to 30 days after
427your receipt of the notice.
428
429 Termination of your rights under this section does not terminate the
430licenses of parties who have received copies or rights from you under
431this License. If your rights have been terminated and not permanently
432reinstated, you do not qualify to receive new licenses for the same
433material under section 10.
434
435 9. Acceptance Not Required for Having Copies.
436
437 You are not required to accept this License in order to receive or
438run a copy of the Program. Ancillary propagation of a covered work
439occurring solely as a consequence of using peer-to-peer transmission
440to receive a copy likewise does not require acceptance. However,
441nothing other than this License grants you permission to propagate or
442modify any covered work. These actions infringe copyright if you do
443not accept this License. Therefore, by modifying or propagating a
444covered work, you indicate your acceptance of this License to do so.
445
446 10. Automatic Licensing of Downstream Recipients.
447
448 Each time you convey a covered work, the recipient automatically
449receives a license from the original licensors, to run, modify and
450propagate that work, subject to this License. You are not responsible
451for enforcing compliance by third parties with this License.
452
453 An "entity transaction" is a transaction transferring control of an
454organization, or substantially all assets of one, or subdividing an
455organization, or merging organizations. If propagation of a covered
456work results from an entity transaction, each party to that
457transaction who receives a copy of the work also receives whatever
458licenses to the work the party's predecessor in interest had or could
459give under the previous paragraph, plus a right to possession of the
460Corresponding Source of the work from the predecessor in interest, if
461the predecessor has it or can get it with reasonable efforts.
462
463 You may not impose any further restrictions on the exercise of the
464rights granted or affirmed under this License. For example, you may
465not impose a license fee, royalty, or other charge for exercise of
466rights granted under this License, and you may not initiate litigation
467(including a cross-claim or counterclaim in a lawsuit) alleging that
468any patent claim is infringed by making, using, selling, offering for
469sale, or importing the Program or any portion of it.
470
471 11. Patents.
472
473 A "contributor" is a copyright holder who authorizes use under this
474License of the Program or a work on which the Program is based. The
475work thus licensed is called the contributor's "contributor version".
476
477 A contributor's "essential patent claims" are all patent claims
478owned or controlled by the contributor, whether already acquired or
479hereafter acquired, that would be infringed by some manner, permitted
480by this License, of making, using, or selling its contributor version,
481but do not include claims that would be infringed only as a
482consequence of further modification of the contributor version. For
483purposes of this definition, "control" includes the right to grant
484patent sublicenses in a manner consistent with the requirements of
485this License.
486
487 Each contributor grants you a non-exclusive, worldwide, royalty-free
488patent license under the contributor's essential patent claims, to
489make, use, sell, offer for sale, import and otherwise run, modify and
490propagate the contents of its contributor version.
491
492 In the following three paragraphs, a "patent license" is any express
493agreement or commitment, however denominated, not to enforce a patent
494(such as an express permission to practice a patent or covenant not to
495sue for patent infringement). To "grant" such a patent license to a
496party means to make such an agreement or commitment not to enforce a
497patent against the party.
498
499 If you convey a covered work, knowingly relying on a patent license,
500and the Corresponding Source of the work is not available for anyone
501to copy, free of charge and under the terms of this License, through a
502publicly available network server or other readily accessible means,
503then you must either (1) cause the Corresponding Source to be so
504available, or (2) arrange to deprive yourself of the benefit of the
505patent license for this particular work, or (3) arrange, in a manner
506consistent with the requirements of this License, to extend the patent
507license to downstream recipients. "Knowingly relying" means you have
508actual knowledge that, but for the patent license, your conveying the
509covered work in a country, or your recipient's use of the covered work
510in a country, would infringe one or more identifiable patents in that
511country that you have reason to believe are valid.
512
513 If, pursuant to or in connection with a single transaction or
514arrangement, you convey, or propagate by procuring conveyance of, a
515covered work, and grant a patent license to some of the parties
516receiving the covered work authorizing them to use, propagate, modify
517or convey a specific copy of the covered work, then the patent license
518you grant is automatically extended to all recipients of the covered
519work and works based on it.
520
521 A patent license is "discriminatory" if it does not include within
522the scope of its coverage, prohibits the exercise of, or is
523conditioned on the non-exercise of one or more of the rights that are
524specifically granted under this License. You may not convey a covered
525work if you are a party to an arrangement with a third party that is
526in the business of distributing software, under which you make payment
527to the third party based on the extent of your activity of conveying
528the work, and under which the third party grants, to any of the
529parties who would receive the covered work from you, a discriminatory
530patent license (a) in connection with copies of the covered work
531conveyed by you (or copies made from those copies), or (b) primarily
532for and in connection with specific products or compilations that
533contain the covered work, unless you entered into that arrangement,
534or that patent license was granted, prior to 28 March 2007.
535
536 Nothing in this License shall be construed as excluding or limiting
537any implied license or other defenses to infringement that may
538otherwise be available to you under applicable patent law.
539
540 12. No Surrender of Others' Freedom.
541
542 If conditions are imposed on you (whether by court order, agreement or
543otherwise) that contradict the conditions of this License, they do not
544excuse you from the conditions of this License. If you cannot convey a
545covered work so as to satisfy simultaneously your obligations under this
546License and any other pertinent obligations, then as a consequence you may
547not convey it at all. For example, if you agree to terms that obligate you
548to collect a royalty for further conveying from those to whom you convey
549the Program, the only way you could satisfy both those terms and this
550License would be to refrain entirely from conveying the Program.
551
552 13. Use with the GNU Affero General Public License.
553
554 Notwithstanding any other provision of this License, you have
555permission to link or combine any covered work with a work licensed
556under version 3 of the GNU Affero General Public License into a single
557combined work, and to convey the resulting work. The terms of this
558License will continue to apply to the part which is the covered work,
559but the special requirements of the GNU Affero General Public License,
560section 13, concerning interaction through a network will apply to the
561combination as such.
562
563 14. Revised Versions of this License.
564
565 The Free Software Foundation may publish revised and/or new versions of
566the GNU General Public License from time to time. Such new versions will
567be similar in spirit to the present version, but may differ in detail to
568address new problems or concerns.
569
570 Each version is given a distinguishing version number. If the
571Program specifies that a certain numbered version of the GNU General
572Public License "or any later version" applies to it, you have the
573option of following the terms and conditions either of that numbered
574version or of any later version published by the Free Software
575Foundation. If the Program does not specify a version number of the
576GNU General Public License, you may choose any version ever published
577by the Free Software Foundation.
578
579 If the Program specifies that a proxy can decide which future
580versions of the GNU General Public License can be used, that proxy's
581public statement of acceptance of a version permanently authorizes you
582to choose that version for the Program.
583
584 Later license versions may give you additional or different
585permissions. However, no additional obligations are imposed on any
586author or copyright holder as a result of your choosing to follow a
587later version.
588
589 15. Disclaimer of Warranty.
590
591 THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
600 16. Limitation of Liability.
601
602 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610SUCH DAMAGES.
611
612 17. Interpretation of Sections 15 and 16.
613
614 If the disclaimer of warranty and limitation of liability provided
615above cannot be given local legal effect according to their terms,
616reviewing courts shall apply local law that most closely approximates
617an absolute waiver of all civil liability in connection with the
618Program, unless a warranty or assumption of liability accompanies a
619copy of the Program in return for a fee.
620
621 END OF TERMS AND CONDITIONS
622
623 How to Apply These Terms to Your New Programs
624
625 If you develop a new program, and you want it to be of the greatest
626possible use to the public, the best way to achieve this is to make it
627free software which everyone can redistribute and change under these terms.
628
629 To do so, attach the following notices to the program. It is safest
630to attach them to the start of each source file to most effectively
631state the exclusion of warranty; and each file should have at least
632the "copyright" line and a pointer to where the full notice is found.
633
634 <one line to give the program's name and a brief idea of what it does.>
635 Copyright (C) <year> <name of author>
636
637 This program is free software: you can redistribute it and/or modify
638 it under the terms of the GNU General Public License as published by
639 the Free Software Foundation, either version 3 of the License, or
640 (at your option) any later version.
641
642 This program is distributed in the hope that it will be useful,
643 but WITHOUT ANY WARRANTY; without even the implied warranty of
644 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 GNU General Public License for more details.
646
647 You should have received a copy of the GNU General Public License
648 along with this program. If not, see <http://www.gnu.org/licenses/>.
649
650Also add information on how to contact you by electronic and paper mail.
651
652 If the program does terminal interaction, make it output a short
653notice like this when it starts in an interactive mode:
654
655 <program> Copyright (C) <year> <name of author>
656 This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 This is free software, and you are welcome to redistribute it
658 under certain conditions; type `show c' for details.
659
660The hypothetical commands `show w' and `show c' should show the appropriate
661parts of the General Public License. Of course, your program's commands
662might be different; for a GUI interface, you would use an "about box".
663
664 You should also get your employer (if you work as a programmer) or school,
665if any, to sign a "copyright disclaimer" for the program, if necessary.
666For more information on this, and how to apply and follow the GNU GPL, see
667<http://www.gnu.org/licenses/>.
668
669 The GNU General Public License does not permit incorporating your program
670into proprietary programs. If your program is a subroutine library, you
671may consider it more useful to permit linking proprietary applications with
672the library. If this is what you want to do, use the GNU Lesser General
673Public License instead of this License. But first, please read
674<http://www.gnu.org/philosophy/why-not-lgpl.html>. \ No newline at end of file
diff --git a/Makefile b/Makefile
deleted file mode 100644
index b7f3522..0000000
--- a/Makefile
+++ /dev/null
@@ -1,37 +0,0 @@
1all: linux
2 @echo "Please choose either macosx, linux, or windows"
3
4static:
5 gcc -o libirecovery.o -c src/libirecovery.c -g -I./include
6 ar rs libirecovery.a libirecovery.o
7 gcc -o irecovery src/irecovery.c -g -I./include -L. -lirecovery -lreadline -lusb-1.0
8
9linux:
10 gcc -o libirecovery.o -c src/libirecovery.c -g -I./include -lreadline -fPIC
11 gcc -o libirecovery.so libirecovery.o -g -shared -Wl,-soname,libirecovery.so -lusb-1.0
12 gcc -o irecovery src/irecovery.c -g -I./include -L. -lirecovery -lreadline
13
14macosx:
15 gcc -o libirecovery.dylib -c src/libirecovery.c -dynamiclib
16 gcc -o irecovery src/irecovery.c -I./include -L. -lirecovery -lreadline -lusb-1.0
17
18windows:
19 gcc -o libirecovery.dll -c src/libirecovery.c -I. -lusb-1.0 -lreadline -shared -fPIC
20 gcc -o irecovery irecovery.c -I. -lirecovery -lreadline
21
22install:
23 cp libirecovery.so /usr/local/lib/libirecovery.so
24 cp include/libirecovery.h /usr/local/include/libirecovery.h
25 cp irecovery /usr/local/bin/irecovery
26 ldconfig
27
28uninstall:
29 rm -rf /usr/local/lib/libirecovery.so
30 rm -rf /usr/local/include/libirecovery.h
31 rm -rf /usr/local/bin/irecovery
32
33clean:
34 rm -rf irecovery libirecovery.o libirecovery.so libirecovery.a
35
36
37
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..80b0eae
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,14 @@
1AUTOMAKE_OPTIONS = foreign
2ACLOCAL_AMFLAGS = -I m4
3SUBDIRS = src include tools udev
4
5EXTRA_DIST = \
6 README.md \
7 git-version-gen
8
9dist-hook:
10 @if ! git diff --quiet; then echo "Uncommitted changes present; not releasing"; exit 1; fi
11 echo $(VERSION) > $(distdir)/.tarball-version
12
13DISTCHECK_CONFIGURE_FLAGS = \
14 --with-udevrulesdir=$$dc_install_base/$(udevrulesdir)
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..cb06ea6
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,124 @@
1Version 1.2.0
2~~~~~~~~~~~~~
3
4* Changes:
5 - Allow building --without-tools
6 - Add KIS (aka Debug USB) support for macOS, Linux, and Windows
7 (Windows note: requires up-to-date AppleMobileDeviceSupport64.msi package installed)
8 - Add Port DFU support (restore procedure is handled externally)
9 - irecovery: Print detailed mode for -q and -m commands
10 - Overall code cleanup and de-duplication
11 - Windows-specific code cleanup and improvements
12
13* Device database changes:
14 - Add Mac Pro, Mac Studio (M2) and MacBook Air (M2) models
15 - Add iPhone 15 family
16 - Add Apple Watch Series 9 and Ultra 2 models
17 - Add November 2023 iMac and MacBook Pro models
18 - Add support for Apple Vision Pro (RealityDevice14,1)
19
20* Bug Fixes:
21 - A few minor Windows-specific bug fixes
22
23Version 1.1.0
24~~~~~~~~~~~~~
25
26* Changes:
27 - Remove duplicated thread/collection code and use new libimobiledevice-glue instead
28 - Add new `irecv_send_command_breq` (for M1 restoring)
29 - Add new `setenvnp` command
30 - Add support for iPad 8 and iPad Air 4 models
31 - Add all current Apple Watch models (device lookup)
32 - Add support for HomePod and HomePod mini (device lookup)
33 - Add support for Apple Silicon/M1 Macs (device lookup) and remaining T2/iBridge devices
34 - Add iMac 24-inch M1 models
35 - Add iPad Pro 11-inch (3rd gen) and iPad Pro 12.9-inch (5th gen) devices
36 - Add Apple TV 4K (2nd gen)
37 - Add iPhone 13 family
38 - Add 9th gen iPad devices
39 - Add 6th gen iPad mini
40 - Add Apple Watch Series 7
41 - Add MacBook Pro 2021 models (device lookup)
42 - Add iPad Air (5th gen)
43 - Add iPhone SE (3rd gen)
44 - Add Mac Studio
45 - Add Studio Display (device lookup)
46 - Add device ID for macOS 12 Apple Silicon VMs
47 - Add M2 MacBook models
48 - Add iPhone 14 family
49 - Add Apple Watch SE 2, Series 8 and Ultra family
50 - Add iPad (10th gen)
51 - Add iPad Pro 11-inch (4th gen) and iPad Pro 12.9-inch (6th gen)
52 - Add Apple TV 4K 3rd gen
53 - Add January 2023 Macs and 2nd generation HomePod models
54 - [Windows] Add support for newer drivers
55 - irecovery: Add new "--devices" option to list internal device data
56 - irecovery: Output product, model and marketing name for device information
57
58* Bug Fixes:
59 - Send a ZLP in recovery mode if the buffer size is a multiple of 512
60 - Make sure DEVICE_ADD events are sent to additional event listeners
61 - [Windows] Use ANSI versions for SetupDI and CreateFile API to prevent errors when compiling with unicode support
62 - irecovery: Fix sending certain commands
63
64Version 1.0.0
65~~~~~~~~~~~~~
66
67* Changes:
68 - Output basic device information after connecting
69 - Remove obsolete "in-tree" copy of libusb-1.0
70 - Improve source code directory structure
71 - Clean up and update of build system files
72 - Major code refactoring
73 - Add getters to retrieve device model information
74 - Change exploit related wording to more accurate limera1n
75 - Various improvements/fixes for win32 build
76 - Add support for latest device models
77 - Fix some memory leaks
78 - Add requirement for autoconf 2.64
79 - Support IOKit on OSX (removes dependency on libusb)
80 - Add DFU mode error handling
81 - Add udev rules to allow non-root device access
82 - Support ECID in hex or decimal format
83 - Fix various compiler warnings
84 - Add device add/remove event subscription interface
85 - Convert README to markdown
86 - Print PWND string if present
87 - Add support for Apple T2 processors
88 - Allow compiling without USB functionality
89 - Support checkra1n DFU mode devices
90 - Allow toggling debug level using "LIBIRECOVERY_DEBUG_LEVEL" environment
91 variable
92 - Add long argument name variants to irecovery
93 - Add new "--version" argument to irecovery
94 - Add support for Apple Watch 1st gen devices
95 - Add support for missing iPad4,3 model and fix wrong device information
96 iPad7 variants
97 - Improve README.md with project description, installation, contributing and
98 usage sections
99 - Rename library and all related files by adding an API version resulting
100 in "libirecovery-1.0"
101
102Version 0.1.1
103~~~~~~~~~~~~~
104
105* Changes:
106 - Add serial number and imei getters
107 - Improve USB communication stability
108 - Add support for WTF mode
109 - Add option to target device by ECID
110 - Add nonce getter
111 - Improve win32 device detection and mingw compatibility
112 - Add support for new device models
113 - Switch to autotools build system instead of plain Makefile
114 - Expose control and bulk transfer methods in public interface
115 - Improve maintainability of device model information
116 - Change license to LGPL 2.1
117
118Version 0.1.0
119~~~~~~~~~~~~~
120
121* Changes:
122 - Implement initial interface and device communication
123 - Add basic irecovery tool
124 - Setup build system
diff --git a/README b/README
deleted file mode 100644
index 50baf0d..0000000
--- a/README
+++ /dev/null
@@ -1,22 +0,0 @@
1== What is iRecovery? ==
2
3iRecovery is a libusb-based commandline utility for Mac OS X, Windows, and Linux. It is able to talk to iBoot/iBSS in Apple's iPhone/iPod touch via USB.
4
5It's completely open-source, the source code is released under the terms of the GNU General Public License v3.
6The full license text can be found in the LICENSE file.
7
8Here's its usage:
9
10irecovery [args]
11 -v Start irecovery in verbose mode.
12 -c <cmd> Send command to client.
13 -f <file> Send file to client.
14 -k [payload] Send usb exploit to client.
15 -h Show this help.
16 -r Reset client.
17 -s Start interactive shell.
18 -e <script> Executes recovery shell script.
19
20You can get info on the shell commands here:
21http://code.google.com/p/chronicdev/wiki/iBootCommands
22
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ac49f5d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,258 @@
1# libirecovery
2
3*The libirecovery library allows communication with iBoot/iBSS of iOS devices
4via USB.*
5
6![](https://github.com/libimobiledevice/libirecovery/workflows/build/badge.svg)
7
8## Table of Contents
9- [Features](#features)
10- [Building](#building)
11 - [Prerequisites](#prerequisites)
12 - [Linux (Debian/Ubuntu based)](#linux-debianubuntu-based)
13 - [macOS](#macos)
14 - [Windows](#windows)
15 - [Configuring the source tree](#configuring-the-source-tree)
16 - [Building and installation](#building-and-installation)
17- [Usage](#usage)
18- [Contributing](#contributing)
19- [Links](#links)
20- [License](#license)
21- [Credits](#credits)
22
23## Features
24
25libirecovery is a cross-platform library which implements communication to
26iBoot/iBSS found on Apple's iOS devices via USB. A command-line utility named
27`irecovery` is also provided.
28
29This is a fork of an older version from former openjailbreak.org and is meant to
30be used with [idevicerestore](https://github.com/libimobiledevice/idevicerestore.git/) from the [libimobiledevice](https://github.com/libimobiledevice/) project.
31
32## Building
33
34### Prerequisites
35
36You need to have a working compiler (gcc/clang) and development environent
37available. This project uses autotools for the build process, allowing to
38have common build steps across different platforms.
39Only the prerequisites differ and they are described in this section.
40
41libirecovery requires [libimobiledevice-glue](https://github.com/libimobiledevice/libimobiledevice-glue).
42Check the [Building](https://github.com/libimobiledevice/libimobiledevice-glue?tab=readme-ov-file#building)
43section of the README on how to build it. Note that some platforms might have it as a package.
44
45#### Linux (Debian/Ubuntu based)
46
47* Install all required dependencies and build tools:
48 ```shell
49 sudo apt-get install \
50 build-essential \
51 pkg-config \
52 checkinstall \
53 git \
54 autoconf \
55 automake \
56 libtool-bin \
57 libimobiledevice-glue-dev \
58 libreadline-dev \
59 libusb-1.0-0-dev
60 ```
61
62 In case libimobiledevice-glue-dev is not available, you can manually build and install it. See note above.
63
64#### macOS
65
66* Make sure the Xcode command line tools are installed. Then, use either [MacPorts](https://www.macports.org/)
67 or [Homebrew](https://brew.sh/) to install `automake`, `autoconf`, `libtool`, etc.
68
69 Using MacPorts:
70 ```shell
71 sudo port install libtool autoconf automake pkgconfig
72 ```
73
74 Using Homebrew:
75 ```shell
76 brew install libtool autoconf automake pkg-config
77 ```
78
79#### Windows
80
81* Using [MSYS2](https://www.msys2.org/) is the official way of compiling this project on Windows. Download the MSYS2 installer
82 and follow the installation steps.
83
84 It is recommended to use the _MSYS2 MinGW 64-bit_ shell. Run it and make sure the required dependencies are installed:
85
86 ```shell
87 pacman -S base-devel \
88 git \
89 mingw-w64-x86_64-gcc \
90 make \
91 libtool \
92 autoconf \
93 automake-wrapper \
94 pkg-config
95 ```
96 NOTE: You can use a different shell and different compiler according to your needs. Adapt the above command accordingly.
97
98### Configuring the source tree
99
100You can build the source code from a git checkout, or from a `.tar.bz2` release tarball from [Releases](https://github.com/libimobiledevice/libirecovery/releases).
101Before we can build it, the source tree has to be configured for building. The steps depend on where you got the source from.
102
103Since libirecovery depends on other packages, you should set the pkg-config environment variable `PKG_CONFIG_PATH`
104accordingly. Make sure to use a path with the same prefix as the dependencies. If they are installed in `/usr/local` you would do
105
106```shell
107export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
108```
109
110* **From git**
111
112 If you haven't done already, clone the actual project repository and change into the directory.
113 ```shell
114 git clone https://github.com/libimobiledevice/libirecovery
115 cd libirecovery
116 ```
117
118 Configure the source tree for building:
119 ```shell
120 ./autogen.sh
121 ```
122
123* **From release tarball (.tar.bz2)**
124
125 When using an official [release tarball](https://github.com/libimobiledevice/libirecovery/releases) (`libirecovery-x.y.z.tar.bz2`)
126 the procedure is slightly different.
127
128 Extract the tarball:
129 ```shell
130 tar xjf libirecovery-x.y.z.tar.bz2
131 cd libirecovery-x.y.z
132 ```
133
134 Configure the source tree for building:
135 ```shell
136 ./configure
137 ```
138
139Both `./configure` and `./autogen.sh` (which generates and calls `configure`) accept a few options, for example `--prefix` to allow
140building for a different target folder. You can simply pass them like this:
141
142```shell
143./autogen.sh --prefix=/usr/local
144```
145or
146```shell
147./configure --prefix=/usr/local
148```
149
150Once the command is successful, the last few lines of output will look like this:
151```
152[...]
153config.status: creating config.h
154config.status: executing depfiles commands
155config.status: executing libtool commands
156
157Configuration for libirecovery 1.2.0:
158-------------------------------------------
159
160 Install prefix: .........: /usr/local
161 USB backend: ............: IOKit
162
163 Now type 'make' to build libirecovery 1.2.0,
164 and then 'make install' for installation.
165```
166
167### Building and installation
168
169If you followed all the steps successfully, and `autogen.sh` or `configure` did not print any errors,
170you are ready to build the project. This is simply done with
171
172```shell
173make
174```
175
176If no errors are emitted you are ready for installation. Depending on whether
177the current user has permissions to write to the destination directory or not,
178you would either run
179```shell
180make install
181```
182_OR_
183```shell
184sudo make install
185```
186
187If you are on Linux, you want to run `sudo ldconfig` after installation to
188make sure the installed libraries are made available.
189
190## Usage
191
192First of all attach your device to your machine. Make sure your device is not
193in normal mode. You can use the `ideviceenterrecovery` application from
194[libimobiledevice](https://github.com/libimobiledevice/libimobiledevice.git/)
195to let your device boot into recovery mode if you need it.
196
197Then simply run:
198```shell
199irecovery --shell
200```
201
202This connects to your device and opens a simple shell to interact with the
203device.
204
205For instance to make your device boot into normal mode again use:
206```shell
207setenv auto-boot true
208saveenv
209reboot
210```
211
212Please consult the usage information or manual page for a full documentation of
213available command line options:
214```shell
215irecovery --help
216man irecovery
217```
218
219## Contributing
220
221We welcome contributions from anyone and are grateful for every pull request!
222
223If you'd like to contribute, please fork the `master` branch, change, commit and
224send a pull request for review. Once approved it can be merged into the main
225code base.
226
227If you plan to contribute larger changes or a major refactoring, please create a
228ticket first to discuss the idea upfront to ensure less effort for everyone.
229
230Please make sure your contribution adheres to:
231* Try to follow the code style of the project
232* Commit messages should describe the change well without being too short
233* Try to split larger changes into individual commits of a common domain
234* Use your real name and a valid email address for your commits
235
236## Links
237
238* Homepage: https://libimobiledevice.org/
239* Repository: https://git.libimobiledevice.org/libirecovery.git
240* Repository (Mirror): https://github.com/libimobiledevice/libirecovery.git
241* Issue Tracker: https://github.com/libimobiledevice/libirecovery/issues
242* Mailing List: https://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel
243* Twitter: https://twitter.com/libimobiledev
244
245## License
246
247This project is licensed under the [GNU Lesser General Public License v2.1](https://www.gnu.org/licenses/lgpl-2.1.en.html),
248also included in the repository in the `COPYING` file.
249
250## Credits
251
252Apple, iPhone, iPad, iPod, iPod Touch, Apple TV, Apple Watch, Mac, iOS,
253iPadOS, tvOS, watchOS, and macOS are trademarks of Apple Inc.
254
255This project is an independent software library and has not been authorized,
256sponsored, or otherwise approved by Apple Inc.
257
258README Updated on: 2024-03-23
diff --git a/TODO b/TODO
deleted file mode 100644
index 411f6e6..0000000
--- a/TODO
+++ /dev/null
@@ -1,15 +0,0 @@
1TODO List
2------------------------------------------------
3
4o) Need to implement irecv_saveenv()
5o) Need to implement irecv_bootx()
6o) Neex to implement irecv_go()
7o) Need to implement irecv_bgcolor()
8o) Need to implememt irecv_setpicture()
9o) Need to impelemnt irecv_reboot()
10o) Should figure out a better place to store callbacks so the CONNECTED callback can actually be used
11o) would be nice to change to use asyncronous connections
12o) could add a function to identify whether we're connected to iBoot/iBEC/iBSS or DFU
13o) could add a function to identify which version we're connected to
14o) could add a function to return the device serial number
15o) fix command parsing to strip quotes \ No newline at end of file
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..5a0ec43
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,26 @@
1#!/bin/sh
2
3olddir=`pwd`
4srcdir=`dirname $0`
5test -z "$srcdir" && srcdir=.
6
7(
8 cd "$srcdir"
9
10 gprefix=`which glibtoolize 2>&1 >/dev/null`
11 if [ $? -eq 0 ]; then
12 glibtoolize --force
13 else
14 libtoolize --force
15 fi
16 aclocal -I m4
17 autoheader
18 automake --add-missing
19 autoconf
20
21 cd "$olddir"
22)
23
24if [ -z "$NOCONFIGURE" ]; then
25 $srcdir/configure "$@"
26fi
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..94bb793
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,230 @@
1# -*- Autoconf -*-
2# Process this file with autoconf to produce a configure script.
3
4AC_PREREQ(2.68)
5AC_INIT([libirecovery], [m4_esyscmd(./git-version-gen $RELEASE_VERSION)], [https://github.com/libimobiledevice/libirecovery/issues], [], [https://libimobiledevice.org])
6AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip check-news])
7m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES])
8AC_CONFIG_SRCDIR([src/])
9AC_CONFIG_HEADERS([config.h])
10AC_CONFIG_MACRO_DIR([m4])
11
12dnl libtool versioning
13# +1 : 0 : +1 == adds new functions to the interface
14# +1 : 0 : 0 == changes or removes functions (changes include both
15# changes to the signature and the semantic)
16# ? :+1 : ? == just internal changes
17# CURRENT : REVISION : AGE
18LIBIRECOVERY_SO_VERSION=5:0:0
19
20dnl Minimum package versions
21LIBUSB_VERSION=1.0.3
22LIMD_GLUE_VERSION=1.2.0
23
24AC_SUBST(LIBIRECOVERY_SO_VERSION)
25AC_SUBST(LIMD_GLUE_VERSION)
26
27# Checks for programs.
28AC_PROG_CC
29#AC_PROG_CXX
30AM_PROG_CC_C_O
31LT_INIT
32
33# Checks for libraries.
34PKG_CHECK_MODULES(limd_glue, libimobiledevice-glue-1.0 >= $LIMD_GLUE_VERSION)
35
36# Checks for header files.
37AC_CHECK_HEADERS([stdint.h stdlib.h string.h])
38
39# Checks for typedefs, structures, and compiler characteristics.
40AC_C_CONST
41AC_TYPE_SIZE_T
42AC_TYPE_SSIZE_T
43AC_TYPE_UINT16_T
44AC_TYPE_UINT32_T
45AC_TYPE_UINT8_T
46
47# Checks for library functions.
48AC_CHECK_FUNCS([strdup strerror strcasecmp strndup malloc realloc calloc])
49
50# Check additional platform flags
51AC_MSG_CHECKING([for platform-specific build settings])
52case ${host_os} in
53 darwin*)
54 AC_MSG_RESULT([${host_os}])
55 AC_CHECK_HEADER(CoreFoundation/CoreFoundation.h, [
56 AC_CHECK_HEADER(IOKit/usb/IOUSBLib.h, [
57 GLOBAL_LDFLAGS+=" -framework IOKit -framework CoreFoundation"
58 have_iokit=yes
59 ], [])
60 ], [])
61 ;;
62 mingw32*)
63 AC_MSG_RESULT([${host_os}])
64 GLOBAL_LDFLAGS+=" -static-libgcc -lkernel32 -lsetupapi"
65 win32=true
66 ;;
67 cygwin*)
68 AC_MSG_RESULT([${host_os}])
69 CC=gcc-3
70 CFLAGS+=" -mno-cygwin"
71 GLOBAL_LDFLAGS+=" -static-libgcc -lkernel32 -lsetupapi"
72 win32=true
73 ;;
74 *)
75 AC_MSG_RESULT([${host_os}])
76 ;;
77esac
78AM_CONDITIONAL(WIN32, test x$win32 = xtrue)
79
80# Check if the C compiler supports __attribute__((constructor))
81AC_CACHE_CHECK([wether the C compiler supports constructor/destructor attributes],
82 ac_cv_attribute_constructor, [
83 ac_cv_attribute_constructor=no
84 AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
85 [[
86 static void __attribute__((constructor)) test_constructor(void) {
87 }
88 static void __attribute__((destructor)) test_destructor(void) {
89 }
90 ]], [])],
91 [ac_cv_attribute_constructor=yes]
92 )]
93)
94if test "$ac_cv_attribute_constructor" = "yes"; then
95 AC_DEFINE(HAVE_ATTRIBUTE_CONSTRUCTOR, 1, [Define if the C compiler supports constructor/destructor attributes])
96fi
97
98AC_ARG_WITH([tools],
99 [AS_HELP_STRING([--with-tools], [Build irecovery tools. (requires readline) [default=yes]])],
100 [],
101 [with_tools=yes])
102
103AS_IF([test "x$with_tools" = "xyes"], [
104 AC_DEFINE(BUILD_TOOLS, 1, [Define if we are building irecovery tools])
105 AC_CHECK_HEADERS([readline/readline.h], [],
106 [AC_MSG_ERROR([Please install readline development headers])]
107 )]
108)
109AM_CONDITIONAL(BUILD_TOOLS, test "x$with_tools" = "xyes")
110
111AC_ARG_WITH([dummy],
112 [AS_HELP_STRING([--with-dummy], [Use no USB driver at all [default=no]. This is only useful if you just want to query the device list by product type or hardware model. All other operations are no-ops or will return IRECV_E_UNSUPPORTED.])],
113 [],
114 [with_dummy=no])
115
116AS_IF([test "x$have_iokit" = "xyes"], [
117 AC_ARG_WITH([iokit],
118 [AS_HELP_STRING([--with-iokit], [Use IOKit instead of libusb on OS X [default=yes]])],
119 [],
120 [with_iokit=yes])
121 ]
122)
123
124AS_IF([test "x$with_dummy" = "xyes"], [
125 AC_DEFINE(USE_DUMMY, 1, [Define if we are using dummy USB driver])
126 USB_BACKEND="dummy"
127], [
128 AS_IF([test "x$with_iokit" = "xyes" && test "x$have_iokit" = "xyes"], [
129 AC_DEFINE(HAVE_IOKIT, 1, [Define if we have IOKit])
130 USB_BACKEND="IOKit"
131 ], [
132 AS_IF([test "x$win32" = "xtrue"], [
133 USB_BACKEND="win32 native (setupapi)"
134 ], [
135 PKG_CHECK_MODULES(libusb, libusb-1.0 >= $LIBUSB_VERSION)
136 USB_BACKEND="libusb `$PKG_CONFIG --modversion libusb-1.0`"
137 LIBUSB_REQUIRED="libusb-1.0 >= $LIBUSB_VERSION"
138 AC_SUBST(LIBUSB_REQUIRED)
139 ])
140 ])
141])
142
143AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter -fvisibility=hidden")
144
145if test "x$enable_static" = "xyes" -a "x$enable_shared" = "xno"; then
146 GLOBAL_CFLAGS+=" -DIRECV_STATIC"
147fi
148
149AC_SUBST(GLOBAL_CFLAGS)
150AC_SUBST(GLOBAL_LDFLAGS)
151
152# check for large file support
153AC_SYS_LARGEFILE
154
155AC_ARG_WITH([udev],
156 AS_HELP_STRING([--with-udev],
157 [Configure and install udev rules file for DFU/Recovery mode devices]),
158 [],
159 [if $($PKG_CONFIG --exists udev); then with_udev=yes; else with_udev=no; fi])
160
161AC_ARG_WITH([udevrulesdir],
162 AS_HELP_STRING([--with-udevrulesdir=DIR],
163 [Directory for udev rules (implies --with-udev)]),
164 [with_udev=yes],
165 [with_udevrulesdir=auto])
166
167AC_ARG_WITH([udevrule],
168 AS_HELP_STRING([--with-udevrule="RULE"],
169 [udev activation rule (implies --with-udev)]),
170 [with_udev=yes],
171 [with_udevrule=auto])
172
173if test "x$with_udev" = "xyes"; then
174 if test "x$with_udevrule" = "xauto"; then
175 for I in plugdev storage disk staff; do
176 if grep $I /etc/group >/dev/null; then
177 USEGROUP=$I
178 break
179 fi
180 done
181 if test "x$USEGROUP" != "x"; then
182 if ! groups |grep $USEGROUP >/dev/null; then
183 AC_MSG_WARN([The group '$USEGROUP' was determined to be used for the udev rule, but the current user is not member of this group.])
184 fi
185 else
186 AC_MSG_ERROR([Could not determine an appropriate user group for the udev activation rule.
187 Please manually specify a udev activation rule using --with-udevrule=<RULE>
188 Example: --with-udevrule="OWNER=\\"root\\", GROUP=\\"myusergroup\\", MODE=\\"0660\\""])
189 fi
190 with_udevrule="OWNER=\"root\", GROUP=\"$USEGROUP\", MODE=\"0660\""
191 fi
192
193 if test "x$with_udevrulesdir" = "xauto"; then
194 udevdir=$($PKG_CONFIG --silence-errors --variable=udevdir udev)
195 if test "x$udevdir" != "x"; then
196 with_udevrulesdir=$udevdir"/rules.d"
197 else
198 with_udevrulesdir="\${prefix}/lib/udev/rules.d"
199 AC_MSG_WARN([Could not determine default udev rules directory. Using $with_udevrulesdir.])
200 fi
201 fi
202
203 AC_SUBST([udev_activation_rule], [$with_udevrule])
204 AC_SUBST([udevrulesdir], [$with_udevrulesdir])
205fi
206AM_CONDITIONAL(WITH_UDEV, test "x$with_udev" = "xyes")
207
208m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
209
210AC_CONFIG_FILES([
211Makefile
212src/Makefile
213src/libirecovery-1.0.pc
214udev/39-libirecovery.rules
215include/Makefile
216tools/Makefile
217udev/Makefile
218])
219AC_OUTPUT
220
221echo "
222Configuration for $PACKAGE $VERSION:
223-------------------------------------------
224
225 Install prefix: .........: $prefix
226 USB backend: ............: $USB_BACKEND
227
228 Now type 'make' to build $PACKAGE $VERSION,
229 and then 'make install' for installation.
230"
diff --git a/git-version-gen b/git-version-gen
new file mode 100755
index 0000000..d868952
--- /dev/null
+++ b/git-version-gen
@@ -0,0 +1,20 @@
1#!/bin/sh
2SRCDIR=`dirname $0`
3if test -n "$1"; then
4 VER=$1
5else
6 if test -r "${SRCDIR}/.git" && test -x "`which git`" ; then
7 git update-index -q --refresh
8 if ! VER=`git describe --tags --dirty 2>/dev/null`; then
9 COMMIT=`git rev-parse --short HEAD`
10 DIRTY=`git diff --quiet HEAD || echo "-dirty"`
11 VER=`sed -n '1,/RE/s/Version \(.*\)/\1/p' ${SRCDIR}/NEWS`-git-${COMMIT}${DIRTY}
12 fi
13 else
14 if test -f "${SRCDIR}/.tarball-version"; then
15 VER=`cat "${SRCDIR}/.tarball-version"`
16 fi
17 fi
18fi
19VER=`printf %s "$VER" | head -n1`
20printf %s "$VER"
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 0000000..aa885aa
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1 @@
nobase_dist_include_HEADERS = libirecovery.h \ No newline at end of file
diff --git a/include/libirecovery.h b/include/libirecovery.h
index c459984..ec9255b 100644
--- a/include/libirecovery.h
+++ b/include/libirecovery.h
@@ -1,20 +1,22 @@
1/** 1/*
2 * iRecovery - Utility for DFU 2.0, WTF and Recovery Mode 2 * libirecovery.h
3 * Copyright (C) 2008 - 2009 westbaer 3 * Communication to iBoot/iBSS on Apple iOS devices via USB
4 * 4 *
5 * This program is free software: you can redistribute it and/or modify 5 * Copyright (c) 2012-2023 Nikias Bassen <nikias@gmx.li>
6 * it under the terms of the GNU General Public License as published by 6 * Copyright (c) 2012-2013 Martin Szulecki <m.szulecki@libimobiledevice.org>
7 * the Free Software Foundation, either version 3 of the License, or 7 * Copyright (c) 2010 Chronic-Dev Team
8 * (at your option) any later version. 8 * Copyright (c) 2010 Joshua Hill
9 * 9 *
10 * This program is distributed in the hope that it will be useful, 10 * All rights reserved. This program and the accompanying materials
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * are made available under the terms of the GNU Lesser General Public License
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * (LGPL) version 2.1 which accompanies this distribution, and is available at
13 * GNU General Public License for more details. 13 * http://www.gnu.org/licenses/lgpl-2.1.html
14 * 14 *
15 * You should have received a copy of the GNU General Public License 15 * This library is distributed in the hope that it will be useful,
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **/ 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 */
18 20
19#ifndef LIBIRECOVERY_H 21#ifndef LIBIRECOVERY_H
20#define LIBIRECOVERY_H 22#define LIBIRECOVERY_H
@@ -23,93 +25,171 @@
23extern "C" { 25extern "C" {
24#endif 26#endif
25 27
26#include <libusb-1.0/libusb.h> 28#include <stdint.h>
27 29
28#define APPLE_VENDOR_ID 0x05AC 30#ifndef IRECV_API
31 #ifdef IRECV_STATIC
32 #define IRECV_API
33 #elif defined(_WIN32)
34 #define IRECV_API __declspec(dllimport)
35 #else
36 #define IRECV_API
37 #endif
38#endif
29 39
30enum { 40enum irecv_mode {
31 kRecoveryMode1 = 0x1280, 41 IRECV_K_RECOVERY_MODE_1 = 0x1280,
32 kRecoveryMode2 = 0x1281, 42 IRECV_K_RECOVERY_MODE_2 = 0x1281,
33 kRecoveryMode3 = 0x1282, 43 IRECV_K_RECOVERY_MODE_3 = 0x1282,
34 kRecoveryMode4 = 0x1283, 44 IRECV_K_RECOVERY_MODE_4 = 0x1283,
35 kDfuMode = 0x1227 45 IRECV_K_WTF_MODE = 0x1222,
46 IRECV_K_DFU_MODE = 0x1227,
47 IRECV_K_PORT_DFU_MODE = 0xf014
36}; 48};
37 49
38typedef enum { 50typedef enum {
39 IRECV_E_SUCCESS = 0, 51 IRECV_E_SUCCESS = 0,
40 IRECV_E_NO_DEVICE = -1, 52 IRECV_E_NO_DEVICE = -1,
41 IRECV_E_OUT_OF_MEMORY = -2, 53 IRECV_E_OUT_OF_MEMORY = -2,
42 IRECV_E_UNABLE_TO_CONNECT = -3, 54 IRECV_E_UNABLE_TO_CONNECT = -3,
43 IRECV_E_INVALID_INPUT = -4, 55 IRECV_E_INVALID_INPUT = -4,
44 IRECV_E_FILE_NOT_FOUND = -5, 56 IRECV_E_FILE_NOT_FOUND = -5,
45 IRECV_E_USB_UPLOAD = -6, 57 IRECV_E_USB_UPLOAD = -6,
46 IRECV_E_USB_STATUS = -7, 58 IRECV_E_USB_STATUS = -7,
47 IRECV_E_USB_INTERFACE = -8, 59 IRECV_E_USB_INTERFACE = -8,
48 IRECV_E_USB_CONFIGURATION = -9, 60 IRECV_E_USB_CONFIGURATION = -9,
49 IRECV_E_PIPE = -10, 61 IRECV_E_PIPE = -10,
50 IRECV_E_TIMEOUT = -11, 62 IRECV_E_TIMEOUT = -11,
51 IRECV_E_UNKNOWN_ERROR = -255 63 IRECV_E_UNSUPPORTED = -254,
64 IRECV_E_UNKNOWN_ERROR = -255
52} irecv_error_t; 65} irecv_error_t;
53 66
54typedef enum { 67typedef enum {
55 IRECV_RECEIVED = 1, 68 IRECV_RECEIVED = 1,
56 IRECV_PRECOMMAND = 2, 69 IRECV_PRECOMMAND = 2,
57 IRECV_POSTCOMMAND = 3, 70 IRECV_POSTCOMMAND = 3,
58 IRECV_CONNECTED = 4, 71 IRECV_CONNECTED = 4,
59 IRECV_DISCONNECTED = 5, 72 IRECV_DISCONNECTED = 5,
60 IRECV_PROGRESS = 6 73 IRECV_PROGRESS = 6
61} irecv_event_type; 74} irecv_event_type;
62 75
63typedef struct { 76typedef struct {
64 int size; 77 int size;
65 char* data; 78 const char* data;
66 double progress; 79 double progress;
67 irecv_event_type type; 80 irecv_event_type type;
68} irecv_event_t; 81} irecv_event_t;
69 82
70struct irecv_client; 83struct irecv_device {
71typedef struct irecv_client* irecv_client_t; 84 const char* product_type;
72typedef int(*irecv_event_cb_t)(irecv_client_t client, const irecv_event_t* event); 85 const char* hardware_model;
86 unsigned int board_id;
87 unsigned int chip_id;
88 const char* display_name;
89};
90typedef struct irecv_device* irecv_device_t;
91
92struct irecv_device_info {
93 unsigned int cpid;
94 unsigned int cprv;
95 unsigned int cpfm;
96 unsigned int scep;
97 unsigned int bdid;
98 uint64_t ecid;
99 unsigned int ibfl;
100 char* srnm;
101 char* imei;
102 char* srtg;
103 char* serial_string;
104 unsigned char* ap_nonce;
105 unsigned int ap_nonce_size;
106 unsigned char* sep_nonce;
107 unsigned int sep_nonce_size;
108 uint16_t pid;
109};
73 110
74struct irecv_client { 111typedef enum {
75 int debug; 112 IRECV_DEVICE_ADD = 1,
76 int config; 113 IRECV_DEVICE_REMOVE = 2
77 int interface; 114} irecv_device_event_type;
78 int alt_interface; 115
79 unsigned short mode; 116typedef struct {
80 char serial[256]; 117 irecv_device_event_type type;
81 libusb_device_handle* handle; 118 enum irecv_mode mode;
82 irecv_event_cb_t progress_callback; 119 struct irecv_device_info *device_info;
83 irecv_event_cb_t received_callback; 120} irecv_device_event_t;
84 irecv_event_cb_t connected_callback; 121
85 irecv_event_cb_t precommand_callback; 122typedef struct irecv_client_private irecv_client_private;
86 irecv_event_cb_t postcommand_callback; 123typedef irecv_client_private* irecv_client_t;
87 irecv_event_cb_t disconnected_callback; 124
125enum {
126 IRECV_SEND_OPT_NONE = 0,
127 IRECV_SEND_OPT_DFU_NOTIFY_FINISH = (1 << 0),
128 IRECV_SEND_OPT_DFU_FORCE_ZLP = (1 << 1),
129 IRECV_SEND_OPT_DFU_SMALL_PKT = (1 << 2)
88}; 130};
89 131
90void irecv_set_debug_level(int level); 132/* library */
91const char* irecv_strerror(irecv_error_t error); 133IRECV_API void irecv_set_debug_level(int level);
92irecv_error_t irecv_open(irecv_client_t* client); 134IRECV_API const char* irecv_strerror(irecv_error_t error);
93irecv_error_t irecv_reset(irecv_client_t client); 135IRECV_API void irecv_init(void); /* deprecated: libirecovery has constructor now */
94irecv_error_t irecv_close(irecv_client_t client); 136IRECV_API void irecv_exit(void); /* deprecated: libirecovery has destructor now */
95irecv_error_t irecv_receive(irecv_client_t client); 137
96irecv_error_t irecv_send_exploit(irecv_client_t client); 138IRECV_API const char* irecv_version();
97irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename); 139
98irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration); 140/* device connectivity */
99 141IRECV_API irecv_error_t irecv_open_with_ecid(irecv_client_t* client, uint64_t ecid);
100irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void *user_data); 142IRECV_API irecv_error_t irecv_open_with_ecid_and_attempts(irecv_client_t* pclient, uint64_t ecid, int attempts);
101irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type); 143IRECV_API irecv_error_t irecv_reset(irecv_client_t client);
102 144IRECV_API irecv_error_t irecv_close(irecv_client_t client);
103irecv_error_t irecv_send_file(irecv_client_t client, const char* filename); 145IRECV_API irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause);
104irecv_error_t irecv_send_command(irecv_client_t client, unsigned char* command); 146
105irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length); 147/* misc */
106 148IRECV_API irecv_error_t irecv_receive(irecv_client_t client);
107irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value); 149IRECV_API irecv_error_t irecv_execute_script(irecv_client_t client, const char* script);
108irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value); 150IRECV_API irecv_error_t irecv_reset_counters(irecv_client_t client);
109irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface); 151IRECV_API irecv_error_t irecv_finish_transfer(irecv_client_t client);
110irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid); 152IRECV_API irecv_error_t irecv_trigger_limera1n_exploit(irecv_client_t client);
111irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid); 153
112irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid); 154/* usb helpers */
155IRECV_API irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int configuration);
156IRECV_API irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface);
157IRECV_API int irecv_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, unsigned int timeout);
158IRECV_API int irecv_usb_bulk_transfer(irecv_client_t client, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout);
159
160/* events */
161typedef void(*irecv_device_event_cb_t)(const irecv_device_event_t* event, void *user_data);
162typedef struct irecv_device_event_context* irecv_device_event_context_t;
163IRECV_API irecv_error_t irecv_device_event_subscribe(irecv_device_event_context_t *context, irecv_device_event_cb_t callback, void *user_data);
164IRECV_API irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t context);
165typedef int(*irecv_event_cb_t)(irecv_client_t client, const irecv_event_t* event);
166IRECV_API irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void *user_data);
167IRECV_API irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type);
168
169/* I/O */
170IRECV_API irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, unsigned int options);
171IRECV_API irecv_error_t irecv_send_command(irecv_client_t client, const char* command);
172IRECV_API irecv_error_t irecv_send_command_breq(irecv_client_t client, const char* command, uint8_t b_request);
173IRECV_API irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, unsigned int options);
174IRECV_API irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length);
175
176/* commands */
177IRECV_API irecv_error_t irecv_saveenv(irecv_client_t client);
178IRECV_API irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value);
179IRECV_API irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value);
180IRECV_API irecv_error_t irecv_setenv_np(irecv_client_t client, const char* variable, const char* value);
181IRECV_API irecv_error_t irecv_reboot(irecv_client_t client);
182IRECV_API irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value);
183
184/* device information */
185IRECV_API irecv_error_t irecv_get_mode(irecv_client_t client, int* mode);
186IRECV_API const struct irecv_device_info* irecv_get_device_info(irecv_client_t client);
187
188/* device database queries */
189IRECV_API irecv_device_t irecv_devices_get_all(void);
190IRECV_API irecv_error_t irecv_devices_get_device_by_client(irecv_client_t client, irecv_device_t* device);
191IRECV_API irecv_error_t irecv_devices_get_device_by_product_type(const char* product_type, irecv_device_t* device);
192IRECV_API irecv_error_t irecv_devices_get_device_by_hardware_model(const char* hardware_model, irecv_device_t* device);
113 193
114#ifdef __cplusplus 194#ifdef __cplusplus
115} 195}
diff --git a/m4/as-compiler-flag.m4 b/m4/as-compiler-flag.m4
new file mode 100644
index 0000000..baab5d9
--- /dev/null
+++ b/m4/as-compiler-flag.m4
@@ -0,0 +1,62 @@
1dnl as-compiler-flag.m4 0.1.0
2
3dnl autostars m4 macro for detection of compiler flags
4
5dnl David Schleef <ds@schleef.org>
6
7dnl $Id: as-compiler-flag.m4,v 1.1 2005/12/15 23:35:19 ds Exp $
8
9dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED])
10dnl Tries to compile with the given CFLAGS.
11dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags,
12dnl and ACTION-IF-NOT-ACCEPTED otherwise.
13
14AC_DEFUN([AS_COMPILER_FLAG],
15[
16 AC_MSG_CHECKING([to see if compiler understands $1])
17
18 save_CFLAGS="$CFLAGS"
19 CFLAGS="$CFLAGS $1"
20
21 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [flag_ok=yes], [flag_ok=no])
22 CFLAGS="$save_CFLAGS"
23
24 if test "X$flag_ok" = Xyes ; then
25 m4_ifvaln([$2],[$2])
26 true
27 else
28 m4_ifvaln([$3],[$3])
29 true
30 fi
31 AC_MSG_RESULT([$flag_ok])
32])
33
34dnl AS_COMPILER_FLAGS(VAR, FLAGS)
35dnl Tries to compile with the given CFLAGS.
36
37AC_DEFUN([AS_COMPILER_FLAGS],
38[
39 list=$2
40 flags_supported=""
41 flags_unsupported=""
42 AC_MSG_CHECKING([for supported compiler flags])
43 for each in $list
44 do
45 save_CFLAGS="$CFLAGS"
46 CFLAGS="$CFLAGS $each"
47 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [flag_ok=yes], [flag_ok=no])
48 CFLAGS="$save_CFLAGS"
49
50 if test "X$flag_ok" = Xyes ; then
51 flags_supported="$flags_supported $each"
52 else
53 flags_unsupported="$flags_unsupported $each"
54 fi
55 done
56 AC_MSG_RESULT([$flags_supported])
57 if test "X$flags_unsupported" != X ; then
58 AC_MSG_WARN([unsupported compiler flags: $flags_unsupported])
59 fi
60 $1="$$1 $flags_supported"
61])
62
diff --git a/scripts/boot.irs b/scripts/boot.irs
deleted file mode 100644
index f989328..0000000
--- a/scripts/boot.irs
+++ /dev/null
@@ -1,5 +0,0 @@
1# Simple script to kick you out of recovery mode
2setenv auto-boot true
3saveenv
4reboot
5
diff --git a/scripts/test.irs b/scripts/test.irs
deleted file mode 100644
index 77f959d..0000000
--- a/scripts/test.irs
+++ /dev/null
@@ -1,21 +0,0 @@
1# This small script should make the device flash
2# Red, Green, Blue a few times then reboot.
3bgcolor 255 0 0
4bgcolor 0 255 0
5bgcolor 0 0 255
6bgcolor 255 0 0
7bgcolor 0 255 0
8bgcolor 0 0 255
9bgcolor 255 0 0
10bgcolor 0 255 0
11bgcolor 0 0 255
12bgcolor 255 0 0
13bgcolor 0 255 0
14bgcolor 0 0 255
15bgcolor 255 0 0
16bgcolor 0 255 0
17bgcolor 0 0 255
18bgcolor 255 0 0
19bgcolor 0 255 0
20bgcolor 0 0 255
21reboot
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..f80ffa7
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,24 @@
1AM_CPPFLAGS = -I$(top_srcdir)/include
2
3AM_CFLAGS = \
4 $(GLOBAL_CFLAGS) \
5 $(LFS_CFLAGS) \
6 $(limd_glue_CFLAGS) \
7 $(libusb_CFLAGS)
8
9AM_LDFLAGS = \
10 $(GLOBAL_LDFLAGS) \
11 $(limd_glue_LIBS) \
12 $(libusb_LIBS)
13
14lib_LTLIBRARIES = libirecovery-1.0.la
15libirecovery_1_0_la_CFLAGS = $(AM_CFLAGS)
16libirecovery_1_0_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBIRECOVERY_SO_VERSION) -no-undefined
17libirecovery_1_0_la_SOURCES = libirecovery.c
18
19if WIN32
20libirecovery_1_0_la_LDFLAGS += -avoid-version
21endif
22
23pkgconfigdir = $(libdir)/pkgconfig
24pkgconfig_DATA = libirecovery-1.0.pc
diff --git a/src/irecovery.c b/src/irecovery.c
deleted file mode 100644
index 5a59cd0..0000000
--- a/src/irecovery.c
+++ /dev/null
@@ -1,355 +0,0 @@
1/**
2 * iRecovery - Utility for DFU 2.0, WTF and Recovery Mode
3 * Copyright (C) 2008 - 2009 westbaer
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 **/
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <unistd.h>
22#include <libirecovery.h>
23#include <readline/readline.h>
24#include <readline/history.h>
25
26#define FILE_HISTORY_PATH ".irecovery"
27#define debug(...) if(verbose) fprintf(stderr, __VA_ARGS__)
28
29enum {
30 kResetDevice, kStartShell, kSendCommand, kSendFile, kSendExploit, kSendScript
31};
32
33static unsigned int quit = 0;
34static unsigned int verbose = 0;
35
36void print_progress_bar(double progress);
37int received_cb(irecv_client_t client, const irecv_event_t* event);
38int progress_cb(irecv_client_t client, const irecv_event_t* event);
39int precommand_cb(irecv_client_t client, const irecv_event_t* event);
40int postcommand_cb(irecv_client_t client, const irecv_event_t* event);
41
42void shell_usage() {
43 printf("Usage:\n");
44 printf("\t/upload <file>\tSend file to client.\n");
45 printf("\t/exploit [file]\tSend usb exploit with optional payload\n");
46 printf("\t/help\t\tShow this help.\n");
47 printf("\t/exit\t\tExit interactive shell.\n");
48}
49
50void parse_command(irecv_client_t client, unsigned char* command, unsigned int size) {
51 irecv_error_t error = 0;
52 char* cmd = strdup(command);
53 char* action = strtok(cmd, " ");
54 debug("Executing %s\n", action);
55 if (!strcmp(cmd, "/exit")) {
56 quit = 1;
57 } else
58
59 if (!strcmp(cmd, "/help")) {
60 shell_usage();
61 } else
62
63 if (!strcmp(cmd, "/upload")) {
64 char* filename = strtok(NULL, " ");
65 debug("Uploading files %s\n", filename);
66 if (filename != NULL) {
67 error = irecv_send_file(client, filename);
68 debug("%s\n", irecv_strerror(error));
69 }
70 } else
71
72 if (!strcmp(cmd, "/exploit")) {
73 char* filename = strtok(NULL, " ");
74 debug("Sending exploit %s\n", filename);
75 if (filename != NULL) {
76 error = irecv_send_file(client, filename);
77 debug("%s\n", irecv_strerror(error));
78 }
79 irecv_send_exploit(client);
80 } else
81
82 if (!strcmp(cmd, "/execute")) {
83 char* filename = strtok(NULL, " ");
84 debug("Executing script %s\n", filename);
85 if (filename != NULL) {
86 irecv_execute_script(client, filename);
87 }
88 }
89
90
91 free(action);
92}
93
94void load_command_history() {
95 read_history(FILE_HISTORY_PATH);
96}
97
98void append_command_to_history(char* cmd) {
99 add_history(cmd);
100 write_history(FILE_HISTORY_PATH);
101}
102
103void init_shell(irecv_client_t client) {
104 irecv_error_t error = 0;
105 load_command_history();
106 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
107 irecv_event_subscribe(client, IRECV_RECEIVED, &received_cb, NULL);
108 irecv_event_subscribe(client, IRECV_PRECOMMAND, &precommand_cb, NULL);
109 irecv_event_subscribe(client, IRECV_POSTCOMMAND, &postcommand_cb, NULL);
110
111 error = irecv_receive(client);
112 if (error != IRECV_E_SUCCESS) {
113 debug("%s\n", irecv_strerror(error));
114 }
115
116 while (!quit) {
117 char* cmd = readline("> ");
118 if (cmd && *cmd) {
119 error = irecv_send_command(client, cmd);
120 if (error != IRECV_E_SUCCESS) {
121 quit = 1;
122 }
123
124 append_command_to_history(cmd);
125 free(cmd);
126 }
127 }
128}
129
130int received_cb(irecv_client_t client, const irecv_event_t* event) {
131 if (event->type == IRECV_RECEIVED) {
132 int i = 0;
133 int size = event->size;
134 char* data = event->data;
135 for (i = 0; i < size; i++) {
136 printf("%c", data[i]);
137 }
138 }
139 return 0;
140}
141
142int precommand_cb(irecv_client_t client, const irecv_event_t* event) {
143 char* value = NULL;
144 char* action = NULL;
145 char* command = NULL;
146 char* argument = NULL;
147 irecv_error_t error = IRECV_E_SUCCESS;
148
149 if (event->type == IRECV_PRECOMMAND) {
150 if (event->data[0] == '/') {
151 parse_command(client, event->data, event->size);
152 return -1;
153 }
154
155 command = strdup(event->data);
156 action = strtok(command, " ");
157
158 if (!strcmp(action, "getenv")) {
159 argument = strtok(NULL, " ");
160 error = irecv_getenv(client, argument, &value);
161 if (error != IRECV_E_SUCCESS) {
162 debug("%s\n", irecv_strerror(error));
163 free(command);
164 return error;
165 }
166 if (value) {
167 printf("%s\n", value);
168 free(value);
169 }
170 return 1;
171 }
172 }
173 return 0;
174}
175
176int postcommand_cb(irecv_client_t client, const irecv_event_t* event) {
177 char* action = NULL;
178 char* command = NULL;
179
180 if (event->type == IRECV_POSTCOMMAND) {
181 command = strdup(event->data);
182 action = strtok(command, " ");
183
184 if (!strcmp(action, "reboot")) {
185 quit = 1;
186 }
187 }
188
189 if (command) free(command);
190 return 0;
191}
192
193int progress_cb(irecv_client_t client, const irecv_event_t* event) {
194 if (event->type == IRECV_PROGRESS) {
195 print_progress_bar(event->progress);
196 }
197 return 0;
198}
199
200void print_progress_bar(double progress) {
201 int i = 0;
202 if(progress < 0) {
203 return;
204 }
205
206 if(progress > 100) {
207 progress = 100;
208 }
209
210 printf("\r[");
211 for(i = 0; i < 50; i++) {
212 if(i < progress / 2) {
213 printf("=");
214 } else {
215 printf(" ");
216 }
217 }
218
219 printf("] %3.1f%%", progress);
220 fflush(stdout);
221 if(progress == 100) {
222 printf("\n");
223 }
224}
225
226void print_usage() {
227 printf("iRecovery - iDevice Recovery Utility\n");
228 printf("Usage: ./irecovery [args]\n");
229 printf("\t-v\t\tStart irecovery in verbose mode.\n");
230 printf("\t-c <cmd>\tSend command to client.\n");
231 printf("\t-f <file>\tSend file to client.\n");
232 printf("\t-k [payload]\tSend usb exploit to client.\n");
233 printf("\t-h\t\tShow this help.\n");
234 printf("\t-r\t\tReset client.\n");
235 printf("\t-s\t\tStart interactive shell.\n");
236 printf("\t-e <script>\tExecutes recovery shell script.\n");
237 exit(1);
238}
239
240int main(int argc, char** argv) {
241 int i = 0;
242 int opt = 0;
243 int action = 0;
244 char* argument = NULL;
245 irecv_error_t error = 0;
246 if (argc == 1) print_usage();
247 while ((opt = getopt(argc, argv, "vhrsc:f:e:k::")) > 0) {
248 switch (opt) {
249 case 'v':
250 verbose += 1;
251 break;
252
253 case 'h':
254 print_usage();
255 break;
256
257 case 'r':
258 action = kResetDevice;
259 break;
260
261 case 's':
262 action = kStartShell;
263 break;
264
265 case 'f':
266 action = kSendFile;
267 argument = optarg;
268 break;
269
270 case 'c':
271 action = kSendCommand;
272 argument = optarg;
273 break;
274
275 case 'k':
276 action = kSendExploit;
277 argument = optarg;
278 break;
279
280 case 'e':
281 action = kSendScript;
282 argument = optarg;
283 break;
284
285 default:
286 fprintf(stderr, "Unknown argument\n");
287 return -1;
288 }
289 }
290
291 if (verbose) irecv_set_debug_level(verbose);
292
293 irecv_client_t client = NULL;
294 for (i = 0; i <= 5; i++) {
295 debug("Attempting to connect... \n");
296
297 if (irecv_open(&client) != IRECV_E_SUCCESS)
298 sleep(1);
299 else
300 break;
301
302 if (i == 5) {
303 return -1;
304 }
305 }
306
307 switch (action) {
308 case kResetDevice:
309 irecv_reset(client);
310 break;
311
312 case kSendFile:
313 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
314 error = irecv_send_file(client, argument);
315 debug("%s\n", irecv_strerror(error));
316 break;
317
318 case kSendCommand:
319 error = irecv_send_command(client, argument);
320 debug("%s\n", irecv_strerror(error));
321 break;
322
323 case kSendExploit:
324 if (argument != NULL) {
325 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
326 error = irecv_send_file(client, argument);
327 if (error != IRECV_E_SUCCESS) {
328 debug("%s\n", irecv_strerror(error));
329 break;
330 }
331 }
332 error = irecv_send_exploit(client);
333 debug("%s\n", irecv_strerror(error));
334 break;
335
336 case kStartShell:
337 init_shell(client);
338 break;
339
340 case kSendScript:
341 error = irecv_execute_script(client, argument);
342 if(error != IRECV_E_SUCCESS) {
343 debug("%s\n", irecv_strerror(error));
344 }
345 break;
346
347 default:
348 fprintf(stderr, "Unknown action\n");
349 break;
350 }
351
352 irecv_close(client);
353 return 0;
354}
355
diff --git a/src/libirecovery-1.0.pc.in b/src/libirecovery-1.0.pc.in
new file mode 100644
index 0000000..e6c94a3
--- /dev/null
+++ b/src/libirecovery-1.0.pc.in
@@ -0,0 +1,11 @@
1prefix=@prefix@
2exec_prefix=@exec_prefix@
3libdir=@libdir@
4includedir=@includedir@
5
6Name: @PACKAGE_NAME@
7Description: A library to communicate with iBoot/iBSS on iOS devices via USB
8Version: @PACKAGE_VERSION@
9Libs: -L${libdir} -lirecovery-1.0
10Cflags: -I${includedir}
11Requires.private: libimobiledevice-glue-1.0 >= @LIMD_GLUE_VERSION@ @LIBUSB_REQUIRED@
diff --git a/src/libirecovery.c b/src/libirecovery.c
index 38e0fc3..6b110f1 100644
--- a/src/libirecovery.c
+++ b/src/libirecovery.c
@@ -1,117 +1,1905 @@
1/** 1/*
2 * iRecovery - Utility for DFU 2.0, WTF and Recovery Mode 2 * libirecovery.c
3 * Copyright (C) 2008 - 2009 westbaer 3 * Communication to iBoot/iBSS on Apple iOS devices via USB
4 * 4 *
5 * This program is free software: you can redistribute it and/or modify 5 * Copyright (c) 2011-2023 Nikias Bassen <nikias@gmx.li>
6 * it under the terms of the GNU General Public License as published by 6 * Copyright (c) 2012-2020 Martin Szulecki <martin.szulecki@libimobiledevice.org>
7 * the Free Software Foundation, either version 3 of the License, or 7 * Copyright (c) 2010 Chronic-Dev Team
8 * (at your option) any later version. 8 * Copyright (c) 2010 Joshua Hill
9 * Copyright (c) 2008-2011 Nicolas Haunold
9 * 10 *
10 * This program is distributed in the hope that it will be useful, 11 * All rights reserved. This program and the accompanying materials
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * are made available under the terms of the GNU Lesser General Public License
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * (LGPL) version 2.1 which accompanies this distribution, and is available at
13 * GNU General Public License for more details. 14 * http://www.gnu.org/licenses/lgpl-2.1.html
14 * 15 *
15 * You should have received a copy of the GNU General Public License 16 * This library is distributed in the hope that it will be useful,
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **/ 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
18 25
19#include <stdio.h> 26#include <stdio.h>
27#include <stdint.h>
20#include <stdlib.h> 28#include <stdlib.h>
21#include <string.h> 29#include <string.h>
30#include <inttypes.h>
31#include <ctype.h>
22#include <unistd.h> 32#include <unistd.h>
23#include <libusb-1.0/libusb.h> 33#include <sys/stat.h>
34
35#include <libimobiledevice-glue/collection.h>
36#include <libimobiledevice-glue/thread.h>
37
38#ifndef USE_DUMMY
39#ifndef WIN32
40#ifndef HAVE_IOKIT
41#include <libusb.h>
42#if (defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)) || (defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000102))
43#define HAVE_LIBUSB_HOTPLUG_API 1
44#endif
45#else
46#include <CoreFoundation/CoreFoundation.h>
47#include <IOKit/usb/IOUSBLib.h>
48#include <IOKit/IOCFPlugIn.h>
49#endif
50#else
51#define WIN32_LEAN_AND_MEAN
52#include <windows.h>
53#include <setupapi.h>
54#ifndef sleep
55#define sleep(n) Sleep(1000 * n)
56#endif
57#endif
58#endif
59
60#ifdef IRECV_STATIC
61 #define IRECV_API
62#elif defined(_WIN32)
63 #define IRECV_API __declspec( dllexport )
64#else
65 #if __GNUC__ >= 4
66 #define IRECV_API __attribute__((visibility("default")))
67 #else
68 #define IRECV_API
69 #endif
70#endif
24 71
25#include "libirecovery.h" 72#include "libirecovery.h"
26 73
74struct irecv_client_private {
75 int debug;
76 int usb_config;
77 int usb_interface;
78 int usb_alt_interface;
79 unsigned int mode;
80 int isKIS;
81 struct irecv_device_info device_info;
82#ifndef USE_DUMMY
83#ifndef WIN32
84#ifndef HAVE_IOKIT
85 libusb_device_handle* handle;
86#else
87 IOUSBDeviceInterface320 **handle;
88 IOUSBInterfaceInterface300 **usbInterface;
89#endif
90#else
91 HANDLE handle;
92#endif
93 irecv_event_cb_t progress_callback;
94 irecv_event_cb_t received_callback;
95 irecv_event_cb_t connected_callback;
96 irecv_event_cb_t precommand_callback;
97 irecv_event_cb_t postcommand_callback;
98 irecv_event_cb_t disconnected_callback;
99#endif
100};
101
102#define USB_TIMEOUT 10000
103#define APPLE_VENDOR_ID 0x05AC
104
105// KIS
106#define KIS_PRODUCT_ID 0x1881
107
108#define KIS_PORTAL_CONFIG 0x01
109#define KIS_PORTAL_RSM 0x10
110
111#define KIS_INDEX_UPLOAD 0x0D
112#define KIS_INDEX_ENABLE_A 0x0A // macOS writes to this
113#define KIS_INDEX_ENABLE_B 0x14 // macOS writes to this
114#define KIS_INDEX_GET_INFO 0x100
115#define KIS_INDEX_BOOT_IMG 0x103
116
117#define KIS_ENABLE_A_VAL 0x21 // Value to write to KIS_INDEX_ENABLE_A
118#define KIS_ENABLE_B_VAL 0x01 // Value to write to KIS_INDEX_ENABLE_B
119
27#define BUFFER_SIZE 0x1000 120#define BUFFER_SIZE 0x1000
28#define debug(...) if(libirecovery_debug) fprintf(stderr, __VA_ARGS__) 121#define debug(...) if (libirecovery_debug) fprintf(stderr, __VA_ARGS__)
29 122
30static int libirecovery_debug = 0; 123static int libirecovery_debug = 0;
124#ifndef USE_DUMMY
125#ifndef WIN32
126#ifndef HAVE_IOKIT
31static libusb_context* libirecovery_context = NULL; 127static libusb_context* libirecovery_context = NULL;
128#endif
129#endif
130#endif
131
132static struct irecv_device irecv_devices[] = {
133 /* iPhone */
134 { "iPhone1,1", "m68ap", 0x00, 0x8900, "iPhone 2G" },
135 { "iPhone1,2", "n82ap", 0x04, 0x8900, "iPhone 3G" },
136 { "iPhone2,1", "n88ap", 0x00, 0x8920, "iPhone 3Gs" },
137 { "iPhone3,1", "n90ap", 0x00, 0x8930, "iPhone 4 (GSM)" },
138 { "iPhone3,2", "n90bap", 0x04, 0x8930, "iPhone 4 (GSM) R2 2012" },
139 { "iPhone3,3", "n92ap", 0x06, 0x8930, "iPhone 4 (CDMA)" },
140 { "iPhone4,1", "n94ap", 0x08, 0x8940, "iPhone 4s" },
141 { "iPhone5,1", "n41ap", 0x00, 0x8950, "iPhone 5 (GSM)" },
142 { "iPhone5,2", "n42ap", 0x02, 0x8950, "iPhone 5 (Global)" },
143 { "iPhone5,3", "n48ap", 0x0a, 0x8950, "iPhone 5c (GSM)" },
144 { "iPhone5,4", "n49ap", 0x0e, 0x8950, "iPhone 5c (Global)" },
145 { "iPhone6,1", "n51ap", 0x00, 0x8960, "iPhone 5s (GSM)" },
146 { "iPhone6,2", "n53ap", 0x02, 0x8960, "iPhone 5s (Global)" },
147 { "iPhone7,1", "n56ap", 0x04, 0x7000, "iPhone 6 Plus" },
148 { "iPhone7,2", "n61ap", 0x06, 0x7000, "iPhone 6" },
149 { "iPhone8,1", "n71ap", 0x04, 0x8000, "iPhone 6s" },
150 { "iPhone8,1", "n71map", 0x04, 0x8003, "iPhone 6s" },
151 { "iPhone8,2", "n66ap", 0x06, 0x8000, "iPhone 6s Plus" },
152 { "iPhone8,2", "n66map", 0x06, 0x8003, "iPhone 6s Plus" },
153 { "iPhone8,4", "n69ap", 0x02, 0x8003, "iPhone SE (1st gen)" },
154 { "iPhone8,4", "n69uap", 0x02, 0x8000, "iPhone SE (1st gen)" },
155 { "iPhone9,1", "d10ap", 0x08, 0x8010, "iPhone 7 (Global)" },
156 { "iPhone9,2", "d11ap", 0x0a, 0x8010, "iPhone 7 Plus (Global)" },
157 { "iPhone9,3", "d101ap", 0x0c, 0x8010, "iPhone 7 (GSM)" },
158 { "iPhone9,4", "d111ap", 0x0e, 0x8010, "iPhone 7 Plus (GSM)" },
159 { "iPhone10,1", "d20ap", 0x02, 0x8015, "iPhone 8 (Global)" },
160 { "iPhone10,2", "d21ap", 0x04, 0x8015, "iPhone 8 Plus (Global)" },
161 { "iPhone10,3", "d22ap", 0x06, 0x8015, "iPhone X (Global)" },
162 { "iPhone10,4", "d201ap", 0x0a, 0x8015, "iPhone 8 (GSM)" },
163 { "iPhone10,5", "d211ap", 0x0c, 0x8015, "iPhone 8 Plus (GSM)" },
164 { "iPhone10,6", "d221ap", 0x0e, 0x8015, "iPhone X (GSM)" },
165 { "iPhone11,2", "d321ap", 0x0e, 0x8020, "iPhone XS" },
166 { "iPhone11,4", "d331ap", 0x0a, 0x8020, "iPhone XS Max (China)" },
167 { "iPhone11,6", "d331pap", 0x1a, 0x8020, "iPhone XS Max" },
168 { "iPhone11,8", "n841ap", 0x0c, 0x8020, "iPhone XR" },
169 { "iPhone12,1", "n104ap", 0x04, 0x8030, "iPhone 11" },
170 { "iPhone12,3", "d421ap", 0x06, 0x8030, "iPhone 11 Pro" },
171 { "iPhone12,5", "d431ap", 0x02, 0x8030, "iPhone 11 Pro Max" },
172 { "iPhone12,8", "d79ap", 0x10, 0x8030, "iPhone SE (2nd gen)" },
173 { "iPhone13,1", "d52gap", 0x0A, 0x8101, "iPhone 12 mini" },
174 { "iPhone13,2", "d53gap", 0x0C, 0x8101, "iPhone 12" },
175 { "iPhone13,3", "d53pap", 0x0E, 0x8101, "iPhone 12 Pro" },
176 { "iPhone13,4", "d54pap", 0x08, 0x8101, "iPhone 12 Pro Max" },
177 { "iPhone14,2", "d63ap", 0x0C, 0x8110, "iPhone 13 Pro" },
178 { "iPhone14,3", "d64ap", 0x0E, 0x8110, "iPhone 13 Pro Max" },
179 { "iPhone14,4", "d16ap", 0x08, 0x8110, "iPhone 13 mini" },
180 { "iPhone14,5", "d17ap", 0x0A, 0x8110, "iPhone 13" },
181 { "iPhone14,6", "d49ap", 0x10, 0x8110, "iPhone SE (3rd gen)" },
182 { "iPhone14,7", "d27ap", 0x18, 0x8110, "iPhone 14" },
183 { "iPhone14,8", "d28ap", 0x1A, 0x8110, "iPhone 14 Plus" },
184 { "iPhone15,2", "d73ap", 0x0C, 0x8120, "iPhone 14 Pro" },
185 { "iPhone15,3", "d74ap", 0x0E, 0x8120, "iPhone 14 Pro Max" },
186 { "iPhone15,4", "d37ap", 0x08, 0x8120, "iPhone 15" },
187 { "iPhone15,5", "d38ap", 0x0A, 0x8120, "iPhone 15 Plus" },
188 { "iPhone16,1", "d83ap", 0x04, 0x8130, "iPhone 15 Pro" },
189 { "iPhone16,2", "d84ap", 0x06, 0x8130, "iPhone 15 Pro Max" },
190 /* iPod */
191 { "iPod1,1", "n45ap", 0x02, 0x8900, "iPod Touch (1st gen)" },
192 { "iPod2,1", "n72ap", 0x00, 0x8720, "iPod Touch (2nd gen)" },
193 { "iPod3,1", "n18ap", 0x02, 0x8922, "iPod Touch (3rd gen)" },
194 { "iPod4,1", "n81ap", 0x08, 0x8930, "iPod Touch (4th gen)" },
195 { "iPod5,1", "n78ap", 0x00, 0x8942, "iPod Touch (5th gen)" },
196 { "iPod7,1", "n102ap", 0x10, 0x7000, "iPod Touch (6th gen)" },
197 { "iPod9,1", "n112ap", 0x16, 0x8010, "iPod Touch (7th gen)" },
198 /* iPad */
199 { "iPad1,1", "k48ap", 0x02, 0x8930, "iPad" },
200 { "iPad2,1", "k93ap", 0x04, 0x8940, "iPad 2 (WiFi)" },
201 { "iPad2,2", "k94ap", 0x06, 0x8940, "iPad 2 (GSM)" },
202 { "iPad2,3", "k95ap", 0x02, 0x8940, "iPad 2 (CDMA)" },
203 { "iPad2,4", "k93aap", 0x06, 0x8942, "iPad 2 (WiFi) R2 2012" },
204 { "iPad2,5", "p105ap", 0x0a, 0x8942, "iPad mini (WiFi)" },
205 { "iPad2,6", "p106ap", 0x0c, 0x8942, "iPad mini (GSM)" },
206 { "iPad2,7", "p107ap", 0x0e, 0x8942, "iPad mini (Global)" },
207 { "iPad3,1", "j1ap", 0x00, 0x8945, "iPad (3rd gen, WiFi)" },
208 { "iPad3,2", "j2ap", 0x02, 0x8945, "iPad (3rd gen, CDMA)" },
209 { "iPad3,3", "j2aap", 0x04, 0x8945, "iPad (3rd gen, GSM)" },
210 { "iPad3,4", "p101ap", 0x00, 0x8955, "iPad (4th gen, WiFi)" },
211 { "iPad3,5", "p102ap", 0x02, 0x8955, "iPad (4th gen, GSM)" },
212 { "iPad3,6", "p103ap", 0x04, 0x8955, "iPad (4th gen, Global)" },
213 { "iPad4,1", "j71ap", 0x10, 0x8960, "iPad Air (WiFi)" },
214 { "iPad4,2", "j72ap", 0x12, 0x8960, "iPad Air (Cellular)" },
215 { "iPad4,3", "j73ap", 0x14, 0x8960, "iPad Air (China)" },
216 { "iPad4,4", "j85ap", 0x0a, 0x8960, "iPad mini 2 (WiFi)" },
217 { "iPad4,5", "j86ap", 0x0c, 0x8960, "iPad mini 2 (Cellular)" },
218 { "iPad4,6", "j87ap", 0x0e, 0x8960, "iPad mini 2 (China)" },
219 { "iPad4,7", "j85map", 0x32, 0x8960, "iPad mini 3 (WiFi)" },
220 { "iPad4,8", "j86map", 0x34, 0x8960, "iPad mini 3 (Cellular)" },
221 { "iPad4,9", "j87map", 0x36, 0x8960, "iPad mini 3 (China)" },
222 { "iPad5,1", "j96ap", 0x08, 0x7000, "iPad mini 4 (WiFi)" },
223 { "iPad5,2", "j97ap", 0x0A, 0x7000, "iPad mini 4 (Cellular)" },
224 { "iPad5,3", "j81ap", 0x06, 0x7001, "iPad Air 2 (WiFi)" },
225 { "iPad5,4", "j82ap", 0x02, 0x7001, "iPad Air 2 (Cellular)" },
226 { "iPad6,3", "j127ap", 0x08, 0x8001, "iPad Pro 9.7-inch (WiFi)" },
227 { "iPad6,4", "j128ap", 0x0a, 0x8001, "iPad Pro 9.7-inch (Cellular)" },
228 { "iPad6,7", "j98aap", 0x10, 0x8001, "iPad Pro 12.9-inch (1st gen, WiFi)" },
229 { "iPad6,8", "j99aap", 0x12, 0x8001, "iPad Pro 12.9-inch (1st gen, Cellular)" },
230 { "iPad6,11", "j71sap", 0x10, 0x8000, "iPad (5th gen, WiFi)" },
231 { "iPad6,11", "j71tap", 0x10, 0x8003, "iPad (5th gen, WiFi)" },
232 { "iPad6,12", "j72sap", 0x12, 0x8000, "iPad (5th gen, Cellular)" },
233 { "iPad6,12", "j72tap", 0x12, 0x8003, "iPad (5th gen, Cellular)" },
234 { "iPad7,1", "j120ap", 0x0C, 0x8011, "iPad Pro 12.9-inch (2nd gen, WiFi)" },
235 { "iPad7,2", "j121ap", 0x0E, 0x8011, "iPad Pro 12.9-inch (2nd gen, Cellular)" },
236 { "iPad7,3", "j207ap", 0x04, 0x8011, "iPad Pro 10.5-inch (WiFi)" },
237 { "iPad7,4", "j208ap", 0x06, 0x8011, "iPad Pro 10.5-inch (Cellular)" },
238 { "iPad7,5", "j71bap", 0x18, 0x8010, "iPad (6th gen, WiFi)" },
239 { "iPad7,6", "j72bap", 0x1A, 0x8010, "iPad (6th gen, Cellular)" },
240 { "iPad7,11", "j171ap", 0x1C, 0x8010, "iPad (7th gen, WiFi)" },
241 { "iPad7,12", "j172ap", 0x1E, 0x8010, "iPad (7th gen, Cellular)" },
242 { "iPad8,1", "j317ap", 0x0C, 0x8027, "iPad Pro 11-inch (1st gen, WiFi)" },
243 { "iPad8,2", "j317xap", 0x1C, 0x8027, "iPad Pro 11-inch (1st gen, WiFi, 1TB)" },
244 { "iPad8,3", "j318ap", 0x0E, 0x8027, "iPad Pro 11-inch (1st gen, Cellular)" },
245 { "iPad8,4", "j318xap", 0x1E, 0x8027, "iPad Pro 11-inch (1st gen, Cellular, 1TB)" },
246 { "iPad8,5", "j320ap", 0x08, 0x8027, "iPad Pro 12.9-inch (3rd gen, WiFi)" },
247 { "iPad8,6", "j320xap", 0x18, 0x8027, "iPad Pro 12.9-inch (3rd gen, WiFi, 1TB)" },
248 { "iPad8,7", "j321ap", 0x0A, 0x8027, "iPad Pro 12.9-inch (3rd gen, Cellular)" },
249 { "iPad8,8", "j321xap", 0x1A, 0x8027, "iPad Pro 12.9-inch (3rd gen, Cellular, 1TB)" },
250 { "iPad8,9", "j417ap", 0x3C, 0x8027, "iPad Pro 11-inch (2nd gen, WiFi)" },
251 { "iPad8,10", "j418ap", 0x3E, 0x8027, "iPad Pro 11-inch (2nd gen, Cellular)" },
252 { "iPad8,11", "j420ap", 0x38, 0x8027, "iPad Pro 12.9-inch (4th gen, WiFi)" },
253 { "iPad8,12", "j421ap", 0x3A, 0x8027, "iPad Pro 12.9-inch (4th gen, Cellular)" },
254 { "iPad11,1", "j210ap", 0x14, 0x8020, "iPad mini (5th gen, WiFi)" },
255 { "iPad11,2", "j211ap", 0x16, 0x8020, "iPad mini (5th gen, Cellular)" },
256 { "iPad11,3", "j217ap", 0x1C, 0x8020, "iPad Air (3rd gen, WiFi)" },
257 { "iPad11,4", "j218ap", 0x1E, 0x8020, "iPad Air (3rd gen, Cellular)" },
258 { "iPad11,6", "j171aap", 0x24, 0x8020, "iPad (8th gen, WiFi)" },
259 { "iPad11,7", "j172aap", 0x26, 0x8020, "iPad (8th gen, Cellular)" },
260 { "iPad12,1", "j181ap", 0x18, 0x8030, "iPad (9th gen, WiFi)" },
261 { "iPad12,2", "j182ap", 0x1A, 0x8030, "iPad (9th gen, Cellular)" },
262 { "iPad13,1", "j307ap", 0x04, 0x8101, "iPad Air (4th gen, WiFi)" },
263 { "iPad13,2", "j308ap", 0x06, 0x8101, "iPad Air (4th gen, Cellular)" },
264 { "iPad13,4", "j517ap", 0x08, 0x8103, "iPad Pro 11-inch (3rd gen, WiFi)" },
265 { "iPad13,5", "j517xap", 0x0A, 0x8103, "iPad Pro 11-inch (3rd gen, WiFi, 2TB)" },
266 { "iPad13,6", "j518ap", 0x0C, 0x8103, "iPad Pro 11-inch (3rd gen, Cellular)" },
267 { "iPad13,7", "j518xap", 0x0E, 0x8103, "iPad Pro 11-inch (3rd gen, Cellular, 2TB)" },
268 { "iPad13,8", "j522ap", 0x18, 0x8103, "iPad Pro 12.9-inch (5th gen, WiFi)" },
269 { "iPad13,9", "j522xap", 0x1A, 0x8103, "iPad Pro 12.9-inch (5th gen, WiFi, 2TB)" },
270 { "iPad13,10", "j523ap", 0x1C, 0x8103, "iPad Pro 12.9-inch (5th gen, Cellular)" },
271 { "iPad13,11", "j523xap", 0x1E, 0x8103, "iPad Pro 12.9-inch (5th gen, Cellular, 2TB)" },
272 { "iPad13,16", "j407ap", 0x10, 0x8103, "iPad Air (5th gen, WiFi)" },
273 { "iPad13,17", "j408ap", 0x12, 0x8103, "iPad Air (5th gen, Cellular)" },
274 { "iPad13,18", "j271ap", 0x14, 0x8101, "iPad (10th gen, WiFi)" },
275 { "iPad13,19", "j272ap", 0x16, 0x8101, "iPad (10th gen, Cellular)" },
276 { "iPad14,1", "j310ap", 0x04, 0x8110, "iPad mini (6th gen, WiFi)" },
277 { "iPad14,2", "j311ap", 0x06, 0x8110, "iPad mini (6th gen, Cellular)" },
278 { "iPad14,3", "j617ap", 0x08, 0x8112, "iPad Pro 11-inch (4th gen, WiFi)" },
279 { "iPad14,4", "j618ap", 0x0A, 0x8112, "iPad Pro 11-inch (4th gen, Cellular)" },
280 { "iPad14,5", "j620ap", 0x0C, 0x8112, "iPad Pro 12.9-inch (6th gen, WiFi)" },
281 { "iPad14,6", "j621ap", 0x0E, 0x8112, "iPad Pro 12.9-inch (6th gen, Cellular)" },
282 { "iPad14,8", "j507ap", 0x10, 0x8112, "iPad Air 11-inch (M2, WiFi)" },
283 { "iPad14,9", "j508ap", 0x12, 0x8112, "iPad Air 11-inch (M2, Cellular)" },
284 { "iPad14,10", "j537ap", 0x14, 0x8112, "iPad Air 13-inch (M2, WiFi)" },
285 { "iPad14,11", "j538ap", 0x16, 0x8112, "iPad Air 13-inch (M2, Cellular)" },
286 { "iPad16,3", "j717ap", 0x08, 0x8132, "iPad Pro 11-inch (M4, WiFi)" },
287 { "iPad16,4", "j718ap", 0x0A, 0x8132, "iPad Pro 11-inch (M4, Cellular)" },
288 { "iPad16,5", "j720ap", 0x0C, 0x8132, "iPad Pro 13-inch (M4, WiFi)" },
289 { "iPad16,6", "j721ap", 0x0E, 0x8132, "iPad Pro 13-inch (M4, Cellular)" },
290 /* Apple TV */
291 { "AppleTV2,1", "k66ap", 0x10, 0x8930, "Apple TV 2" },
292 { "AppleTV3,1", "j33ap", 0x08, 0x8942, "Apple TV 3" },
293 { "AppleTV3,2", "j33iap", 0x00, 0x8947, "Apple TV 3 (2013)" },
294 { "AppleTV5,3", "j42dap", 0x34, 0x7000, "Apple TV 4" },
295 { "AppleTV6,2", "j105aap", 0x02, 0x8011, "Apple TV 4K" },
296 { "AppleTV11,1", "j305ap", 0x08, 0x8020, "Apple TV 4K (2nd gen)" },
297 { "AppleTV14,1", "j255ap", 0x02, 0x8110, "Apple TV 4K (3rd gen)" },
298 /* HomePod */
299 { "AudioAccessory1,1", "b238aap", 0x38, 0x7000, "HomePod (1st gen)" },
300 { "AudioAccessory1,2", "b238ap", 0x1A, 0x7000, "HomePod (1st gen)" },
301 { "AudioAccessory5,1", "b520ap", 0x22, 0x8006, "HomePod mini" },
302 { "AudioAccessory6,1", "b620ap", 0x18, 0x8301, "HomePod (2nd gen)" },
303 /* Apple Watch */
304 { "Watch1,1", "n27aap", 0x02, 0x7002, "Apple Watch 38mm (1st gen)" },
305 { "Watch1,2", "n28aap", 0x04, 0x7002, "Apple Watch 42mm (1st gen)" },
306 { "Watch2,6", "n27dap", 0x02, 0x8002, "Apple Watch Series 1 (38mm)" },
307 { "Watch2,7", "n28dap", 0x04, 0x8002, "Apple Watch Series 1 (42mm)" },
308 { "Watch2,3", "n74ap", 0x0C, 0x8002, "Apple Watch Series 2 (38mm)" },
309 { "Watch2,4", "n75ap", 0x0E, 0x8002, "Apple Watch Series 2 (42mm)" },
310 { "Watch3,1", "n111sap", 0x1C, 0x8004, "Apple Watch Series 3 (38mm Cellular)" },
311 { "Watch3,2", "n111bap", 0x1E, 0x8004, "Apple Watch Series 3 (42mm Cellular)" },
312 { "Watch3,3", "n121sap", 0x18, 0x8004, "Apple Watch Series 3 (38mm)" },
313 { "Watch3,4", "n121bap", 0x1A, 0x8004, "Apple Watch Series 3 (42mm)" },
314 { "Watch4,1", "n131sap", 0x08, 0x8006, "Apple Watch Series 4 (40mm)" },
315 { "Watch4,2", "n131bap", 0x0A, 0x8006, "Apple Watch Series 4 (44mm)" },
316 { "Watch4,3", "n141sap", 0x0C, 0x8006, "Apple Watch Series 4 (40mm Cellular)" },
317 { "Watch4,4", "n141bap", 0x0E, 0x8006, "Apple Watch Series 4 (44mm Cellular)" },
318 { "Watch5,1", "n144sap", 0x10, 0x8006, "Apple Watch Series 5 (40mm)" },
319 { "Watch5,2", "n144bap", 0x12, 0x8006, "Apple Watch Series 5 (44mm)" },
320 { "Watch5,3", "n146sap", 0x14, 0x8006, "Apple Watch Series 5 (40mm Cellular)" },
321 { "Watch5,4", "n146bap", 0x16, 0x8006, "Apple Watch Series 5 (44mm Cellular)" },
322 { "Watch5,9", "n140sap", 0x28, 0x8006, "Apple Watch SE (40mm)" },
323 { "Watch5,10", "n140bap", 0x2A, 0x8006, "Apple Watch SE (44mm)" },
324 { "Watch5,11", "n142sap", 0x2C, 0x8006, "Apple Watch SE (40mm Cellular)" },
325 { "Watch5,12", "n142bap", 0x2E, 0x8006, "Apple Watch SE (44mm Cellular)" },
326 { "Watch6,1", "n157sap", 0x08, 0x8301, "Apple Watch Series 6 (40mm)" },
327 { "Watch6,2", "n157bap", 0x0A, 0x8301, "Apple Watch Series 6 (44mm)" },
328 { "Watch6,3", "n158sap", 0x0C, 0x8301, "Apple Watch Series 6 (40mm Cellular)" },
329 { "Watch6,4", "n158bap", 0x0E, 0x8301, "Apple Watch Series 6 (44mm Cellular)" },
330 { "Watch6,6", "n187sap", 0x10, 0x8301, "Apple Watch Series 7 (41mm)" },
331 { "Watch6,7", "n187bap", 0x12, 0x8301, "Apple Watch Series 7 (45mm)" },
332 { "Watch6,8", "n188sap", 0x14, 0x8301, "Apple Watch Series 7 (41mm Cellular)" },
333 { "Watch6,9", "n188bap", 0x16, 0x8301, "Apple Watch Series 7 (45mm Cellular)" },
334 { "Watch6,10", "n143sap", 0x28, 0x8301, "Apple Watch SE 2 (40mm)" },
335 { "Watch6,11", "n143bap", 0x2A, 0x8301, "Apple Watch SE 2 (44mm)" },
336 { "Watch6,12", "n149sap", 0x2C, 0x8301, "Apple Watch SE 2 (40mm Cellular)" },
337 { "Watch6,13", "n149bap", 0x2E, 0x8301, "Apple Watch SE 2 (44mm Cellular)" },
338 { "Watch6,14", "n197sap", 0x30, 0x8301, "Apple Watch Series 8 (41mm)" },
339 { "Watch6,15", "n197bap", 0x32, 0x8301, "Apple Watch Series 8 (45mm)" },
340 { "Watch6,16", "n198sap", 0x34, 0x8301, "Apple Watch Series 8 (41mm Cellular)" },
341 { "Watch6,17", "n198bap", 0x36, 0x8301, "Apple Watch Series 8 (45mm Cellular)" },
342 { "Watch6,18", "n199ap", 0x26, 0x8301, "Apple Watch Ultra" },
343 { "Watch7,1", "n207sap", 0x08, 0x8310, "Apple Watch Series 9 (41mm)" },
344 { "Watch7,2", "n207bap", 0x0A, 0x8310, "Apple Watch Series 9 (45mm)" },
345 { "Watch7,3", "n208sap", 0x0C, 0x8310, "Apple Watch Series 9 (41mm Cellular)" },
346 { "Watch7,4", "n208bap", 0x0E, 0x8310, "Apple Watch Series 9 (45mm Cellular)" },
347 { "Watch7,5", "n210ap", 0x02, 0x8310, "Apple Watch Ultra 2" },
348 /* Apple Silicon Macs */
349 { "ADP3,2", "j273aap", 0x42, 0x8027, "Developer Transition Kit (2020)" },
350 { "Macmini9,1", "j274ap", 0x22, 0x8103, "Mac mini (M1, 2020)" },
351 { "MacBookPro17,1", "j293ap", 0x24, 0x8103, "MacBook Pro (M1, 13-inch, 2020)" },
352 { "MacBookPro18,1", "j316sap", 0x0A, 0x6000, "MacBook Pro (M1 Pro, 16-inch, 2021)" },
353 { "MacBookPro18,2", "j316cap", 0x0A, 0x6001, "MacBook Pro (M1 Max, 16-inch, 2021)" },
354 { "MacBookPro18,3", "j314sap", 0x08, 0x6000, "MacBook Pro (M1 Pro, 14-inch, 2021)" },
355 { "MacBookPro18,4", "j314cap", 0x08, 0x6001, "MacBook Pro (M1 Max, 14-inch, 2021)" },
356 { "MacBookAir10,1", "j313ap", 0x26, 0x8103, "MacBook Air (M1, 2020)" },
357 { "iMac21,1", "j456ap", 0x28, 0x8103, "iMac 24-inch (M1, Two Ports, 2021)" },
358 { "iMac21,2", "j457ap", 0x2A, 0x8103, "iMac 24-inch (M1, Four Ports, 2021)" },
359 { "Mac13,1", "j375cap", 0x04, 0x6001, "Mac Studio (M1 Max, 2022)" },
360 { "Mac13,2", "j375dap", 0x0C, 0x6002, "Mac Studio (M1 Ultra, 2022)" },
361 { "Mac14,2", "j413ap", 0x28, 0x8112, "MacBook Air (M2, 2022)" },
362 { "Mac14,7", "j493ap", 0x2A, 0x8112, "MacBook Pro (M2, 13-inch, 2022)" },
363 { "Mac14,3", "j473ap", 0x24, 0x8112, "Mac mini (M2, 2023)" },
364 { "Mac14,5", "j414cap", 0x04, 0x6021, "MacBook Pro (14-inch, M2 Max, 2023)" },
365 { "Mac14,6", "j416cap", 0x06, 0x6021, "MacBook Pro (16-inch, M2 Max, 2023)" },
366 { "Mac14,8", "j180dap", 0x08, 0x6022, "Mac Pro (2023)" },
367 { "Mac14,9", "j414sap", 0x04, 0x6020, "MacBook Pro (14-inch, M2 Pro, 2023)" },
368 { "Mac14,10", "j416sap", 0x06, 0x6020, "MacBook Pro (16-inch, M2 Pro, 2023)" },
369 { "Mac14,12", "j474sap", 0x02, 0x6020, "Mac mini (M2 Pro, 2023)" },
370 { "Mac14,13", "j475cap", 0x0A, 0x6021, "Mac Studio (M2 Max, 2023)" },
371 { "Mac14,14", "j475dap", 0x0A, 0x6022, "Mac Studio (M2 Ultra, 2023)" },
372 { "Mac14,15", "j415ap", 0x2E, 0x8112, "MacBook Air (M2, 15-inch, 2023)" },
373 { "Mac15,3", "j504ap", 0x22, 0x8122, "MacBook Pro (14-inch, M3, Nov 2023)" },
374 { "Mac15,4", "j433ap", 0x28, 0x8122, "iMac 24-inch (M3, Two Ports, 2023)" },
375 { "Mac15,5", "j434ap", 0x2A, 0x8122, "iMac 24-inch (M3, Four Ports, 2023)" },
376 { "Mac15,6", "j514sap", 0x04, 0x6030, "MacBook Pro (14-inch, M3 Pro, Nov 2023)" },
377 { "Mac15,7", "j516sap", 0x06, 0x6030, "MacBook Pro (16-inch, M3 Pro, Nov 2023)" },
378 { "Mac15,8", "j514cap", 0x44, 0x6031, "MacBook Pro (14-inch, M3 Max, Nov 2023)" },
379 { "Mac15,9", "j516cap", 0x46, 0x6031, "MacBook Pro (16-inch, M3 Max, Nov 2023)" },
380 { "Mac15,10", "j514map", 0x44, 0x6034, "MacBook Pro (14-inch, M3 Max, Nov 2023)" },
381 { "Mac15,11", "j516map", 0x46, 0x6034, "MacBook Pro (16-inch, M3 Max, Nov 2023)" },
382 { "Mac15,12", "j613ap", 0x30, 0x8122, "MacBook Air (13-inch, M3, 2024)" },
383 { "Mac15,13", "j615ap", 0x32, 0x8122, "MacBook Air (15-inch, M3, 2024)" },
384 /* Apple Silicon VMs (supported by Virtualization.framework on macOS 12) */
385 { "VirtualMac2,1", "vma2macosap", 0x20, 0xFE00, "Apple Virtual Machine 1" },
386 /* Apple T2 Coprocessor */
387 { "iBridge2,1", "j137ap", 0x0A, 0x8012, "Apple T2 iMacPro1,1 (j137)" },
388 { "iBridge2,3", "j680ap", 0x0B, 0x8012, "Apple T2 MacBookPro15,1 (j680)" },
389 { "iBridge2,4", "j132ap", 0x0C, 0x8012, "Apple T2 MacBookPro15,2 (j132)" },
390 { "iBridge2,5", "j174ap", 0x0E, 0x8012, "Apple T2 Macmini8,1 (j174)" },
391 { "iBridge2,6", "j160ap", 0x0F, 0x8012, "Apple T2 MacPro7,1 (j160)" },
392 { "iBridge2,7", "j780ap", 0x07, 0x8012, "Apple T2 MacBookPro15,3 (j780)" },
393 { "iBridge2,8", "j140kap", 0x17, 0x8012, "Apple T2 MacBookAir8,1 (j140k)" },
394 { "iBridge2,10", "j213ap", 0x18, 0x8012, "Apple T2 MacBookPro15,4 (j213)" },
395 { "iBridge2,12", "j140aap", 0x37, 0x8012, "Apple T2 MacBookAir8,2 (j140a)" },
396 { "iBridge2,14", "j152fap", 0x3A, 0x8012, "Apple T2 MacBookPro16,1 (j152f)" },
397 { "iBridge2,15", "j230kap", 0x3F, 0x8012, "Apple T2 MacBookAir9,1 (j230k)" },
398 { "iBridge2,16", "j214kap", 0x3E, 0x8012, "Apple T2 MacBookPro16,2 (j214k)" },
399 { "iBridge2,19", "j185ap", 0x22, 0x8012, "Apple T2 iMac20,1 (j185)" },
400 { "iBridge2,20", "j185fap", 0x23, 0x8012, "Apple T2 iMac20,2 (j185f)" },
401 { "iBridge2,21", "j223ap", 0x3B, 0x8012, "Apple T2 MacBookPro16,3 (j223)" },
402 { "iBridge2,22", "j215ap", 0x38, 0x8012, "Apple T2 MacBookPro16,4 (j215)" },
403 /* Apple Displays */
404 { "AppleDisplay2,1", "j327ap", 0x22, 0x8030, "Studio Display" },
405 /* Apple Vision Pro */
406 { "RealityDevice14,1", "n301ap", 0x42, 0x8112, "Apple Vision Pro" },
407 { NULL, NULL, -1, -1, NULL }
408};
409
410#ifndef USE_DUMMY
411static unsigned int crc32_lookup_t1[256] = {
412 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
413 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
414 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
415 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
416 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
417 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
418 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
419 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
420 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
421 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
422 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
423 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
424 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
425 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
426 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
427 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
428 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
429 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
430 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
431 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
432 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
433 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
434 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
435 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
436 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
437 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
438 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
439 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
440 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
441 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
442 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
443 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
444 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
445 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
446 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
447 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
448 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
449 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
450 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
451 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
452 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
453 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
454 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
455 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
456 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
457 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
458 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
459 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
460 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
461 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
462 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
463 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
464 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
465 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
466 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
467 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
468 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
469 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
470 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
471 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
472 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
473 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
474 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
475 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
476};
477
478#define crc32_step(a,b) \
479 a = (crc32_lookup_t1[(a & 0xFF) ^ ((unsigned char)b)] ^ (a >> 8))
480
481#ifdef WIN32
482#pragma pack(1)
483typedef struct {
484 uint16_t vid;
485 uint16_t pid;
486 uint32_t unk;
487 char nonces[255];
488 char serial[255];
489 char manufacturer[255];
490 char product[255];
491} KIS_device_info;
492
493typedef struct {
494 uint8_t data[0x4000];
495 uint32_t size;
496 uint32_t unused;
497 uint64_t address;
498} KIS_upload_chunk;
499#pragma pack()
500#else
501#pragma pack(1)
502typedef struct {
503 uint16_t sequence; // A sequence number
504 uint8_t version; // Protocol version
505 uint8_t portal; // The "portal" to connect to
506 uint8_t argCount; // Number of arguments
507 uint8_t indexLo; // An index
508 uint8_t indexHiRplSizeLo; // High 2 bits of index + low 6 bytes of reply size
509 uint8_t rplSizeHi; // Reply size high bits, number of words the device should send
510 uint32_t reqSize; // Size of the complete request, including the arguments and payload, excluding the header
511 // Followed by arguments and payload data
512} KIS_req_header;
513
514typedef struct {
515 KIS_req_header hdr;
516 uint32_t value;
517} KIS_config_wr32;
518
519typedef struct {
520 uint8_t bLength ; ///< Size of this descriptor in bytes.
521 uint8_t bDescriptorType ; ///< DEVICE Descriptor Type.
522 uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). This field identifies the release of the USB Specification with which the device and its descriptors are compliant.
523
524 uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF). \li If this field is reset to zero, each interface within a configuration specifies its own class information and the various interfaces operate independently. \li If this field is set to a value between 1 and FEH, the device supports different class specifications on different interfaces and the interfaces may not operate independently. This value identifies the class definition used for the aggregate interfaces. \li If this field is set to FFH, the device class is vendor-specific.
525 uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass field. \li If the bDeviceClass field is reset to zero, this field must also be reset to zero. \li If the bDeviceClass field is not set to FFH, all values are reserved for assignment by the USB-IF.
526 uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass and the bDeviceSubClass fields. If a device supports class-specific protocols on a device basis as opposed to an interface basis, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use class-specific protocols on a device basis. However, it may use classspecific protocols on an interface basis. \li If this field is set to FFH, the device uses a vendor-specific protocol on a device basis.
527 uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64.
528
529 uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF).
530 uint16_t idProduct ; ///< Product ID (assigned by the manufacturer).
531 uint16_t bcdDevice ; ///< Device release number in binary-coded decimal.
532 uint8_t iManufacturer ; ///< Index of string descriptor describing manufacturer.
533 uint8_t iProduct ; ///< Index of string descriptor describing product.
534 uint8_t iSerialNumber ; ///< Index of string descriptor describing the device's serial number.
535
536 uint8_t bNumConfigurations ; ///< Number of possible configurations.
537} usb_device_descriptor;
538
539typedef struct {
540 KIS_req_header hdr;
541 union {
542 struct {
543 uint32_t tag;
544 uint32_t unk1;
545 uint32_t maxUploadSize;
546 uint32_t maxDownloadSize; // maybe???
547 uint64_t rambase;
548 uint32_t nonceOffset;
549 uint32_t pad;
550 uint8_t unkpad[0x20];
551 usb_device_descriptor deviceDescriptor;
552 };
553 uint8_t deviceInfo[0x300];
554 };
555 uint32_t rspsize;
556 uint32_t statuscode;
557} KIS_device_info;
558
559typedef struct {
560 KIS_req_header hdr;
561 uint64_t address;
562 uint32_t size;
563 uint8_t data[0x4000];
564} KIS_upload_chunk;
565
566typedef struct {
567 KIS_req_header hdr;
568 uint32_t size; // Number of bytes read/written
569 uint32_t status;
570} KIS_generic_reply;
571#pragma pack()
572#endif
573
574static THREAD_T th_event_handler = THREAD_T_NULL;
575struct collection listeners;
576static mutex_t listener_mutex;
577struct collection devices;
578static mutex_t device_mutex;
579#ifndef WIN32
580#ifdef HAVE_IOKIT
581static CFRunLoopRef iokit_runloop = NULL;
582#else
583static libusb_context* irecv_hotplug_ctx = NULL;
584#endif
585#endif
586
587static void _irecv_init(void)
588{
589 char* dbglvl = getenv("LIBIRECOVERY_DEBUG_LEVEL");
590 if (dbglvl) {
591 libirecovery_debug = strtol(dbglvl, NULL, 0);
592 irecv_set_debug_level(libirecovery_debug);
593 }
594#ifndef USE_DUMMY
595#ifndef WIN32
596#ifndef HAVE_IOKIT
597 libusb_init(&libirecovery_context);
598#endif
599#endif
600 collection_init(&listeners);
601 mutex_init(&listener_mutex);
602#endif
603}
604
605static void _irecv_deinit(void)
606{
607#ifndef USE_DUMMY
608#ifndef WIN32
609#ifndef HAVE_IOKIT
610 if (libirecovery_context != NULL) {
611 libusb_exit(libirecovery_context);
612 libirecovery_context = NULL;
613 }
614#endif
615#endif
616 collection_free(&listeners);
617 mutex_destroy(&listener_mutex);
618#endif
619}
620
621static thread_once_t init_once = THREAD_ONCE_INIT;
622static thread_once_t deinit_once = THREAD_ONCE_INIT;
623
624#ifndef HAVE_ATTRIBUTE_CONSTRUCTOR
625 #if defined(__llvm__) || defined(__GNUC__)
626 #define HAVE_ATTRIBUTE_CONSTRUCTOR
627 #endif
628#endif
629
630#ifdef HAVE_ATTRIBUTE_CONSTRUCTOR
631static void __attribute__((constructor)) libirecovery_initialize(void)
632{
633 thread_once(&init_once, _irecv_init);
634}
635
636static void __attribute__((destructor)) libirecovery_deinitialize(void)
637{
638 thread_once(&deinit_once, _irecv_deinit);
639}
640#elif defined(WIN32)
641BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved)
642{
643 switch (dwReason) {
644 case DLL_PROCESS_ATTACH:
645 thread_once(&init_once, _irecv_init);
646 break;
647 case DLL_PROCESS_DETACH:
648 thread_once(&deinit_once, _irecv_deinit);
649 break;
650 default:
651 break;
652 }
653 return 1;
654}
655#else
656#warning No compiler support for constructor/destructor attributes, some features might not be available.
657#endif
658
659#ifdef HAVE_IOKIT
660static int iokit_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size)
661{
662 IOReturn result;
663 IOUSBDevRequest request;
664 unsigned char descriptor[256];
665
666 request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
667 request.bRequest = kUSBRqGetDescriptor;
668 request.wValue = (kUSBStringDesc << 8); // | desc_index;
669 request.wIndex = 0; // All languages 0x409; // language
670 request.wLength = sizeof(descriptor) - 1;
671 request.pData = descriptor;
672 request.wLenDone = 0;
673
674 result = (*client->handle)->DeviceRequest(client->handle, &request);
675 if (result == kIOReturnNoDevice)
676 return IRECV_E_NO_DEVICE;
677 if (result == kIOReturnNotOpen)
678 return IRECV_E_USB_STATUS;
679 if (result != kIOReturnSuccess)
680 return IRECV_E_UNKNOWN_ERROR;
681
682 if (descriptor[0] >= 4) { // && descriptor[2] == 0x9 && descriptor[3] == 0x4) {
683
684 request.wValue = (kUSBStringDesc << 8) | desc_index;
685 request.wIndex = descriptor[2] + (descriptor[3] << 8);
686 request.wLenDone = 0;
687 result = (*client->handle)->DeviceRequest(client->handle, &request);
688
689 if (result == kIOReturnNoDevice)
690 return IRECV_E_NO_DEVICE;
691 if (result == kIOReturnNotOpen)
692 return IRECV_E_USB_STATUS;
693 if (result != kIOReturnSuccess)
694 return IRECV_E_UNKNOWN_ERROR;
695
696 int i = 2, j = 0;
697 for ( ; i < descriptor[0]; i += 2, j += 1) {
698 buffer[j] = descriptor[i];
699 }
700 buffer[j] = 0;
701
702 return request.wLenDone;
703 }
704 return IRECV_E_UNKNOWN_ERROR;
705}
706#endif
707
708static int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size)
709{
710#ifndef WIN32
711#ifdef HAVE_IOKIT
712 return iokit_get_string_descriptor_ascii(client, desc_index, buffer, size);
713#else
714 return libusb_get_string_descriptor_ascii(client->handle, desc_index, buffer, size);
715#endif
716#else
717 irecv_error_t ret;
718 unsigned short langid = 0;
719 unsigned char data[256];
720 int di, si;
721 memset(data, 0, 256);
722 memset(buffer, 0, size);
723
724 ret = irecv_usb_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, 255, USB_TIMEOUT);
725
726 if (ret < 0) return ret;
727 if (data[1] != 0x03) return IRECV_E_UNKNOWN_ERROR;
728 if (data[0] > ret) return IRECV_E_UNKNOWN_ERROR;
729
730 for (di = 0, si = 2; si < data[0]; si += 2) {
731 if (di >= (size - 1)) break;
732 if (data[si + 1]) {
733 /* high byte */
734 buffer[di++] = '?';
735 } else {
736 buffer[di++] = data[si];
737 }
738 }
739 buffer[di] = 0;
740
741 return di;
742#endif
743}
744
745static void irecv_load_device_info_from_iboot_string(irecv_client_t client, const char* iboot_string)
746{
747 if (!client || !iboot_string) {
748 return;
749 }
750
751 memset(&client->device_info, '\0', sizeof(struct irecv_device_info));
752
753 client->device_info.serial_string = strdup(iboot_string);
754
755 char* ptr;
756
757 ptr = strstr(iboot_string, "CPID:");
758 if (ptr != NULL) {
759 sscanf(ptr, "CPID:%x", &client->device_info.cpid);
760 }
761
762 ptr = strstr(iboot_string, "CPRV:");
763 if (ptr != NULL) {
764 sscanf(ptr, "CPRV:%x", &client->device_info.cprv);
765 }
766
767 ptr = strstr(iboot_string, "CPFM:");
768 if (ptr != NULL) {
769 sscanf(ptr, "CPFM:%x", &client->device_info.cpfm);
770 }
771
772 ptr = strstr(iboot_string, "SCEP:");
773 if (ptr != NULL) {
774 sscanf(ptr, "SCEP:%x", &client->device_info.scep);
775 }
32 776
33int irecv_write_file(const char* filename, const void* data, size_t size); 777 ptr = strstr(iboot_string, "BDID:");
34int irecv_read_file(const char* filename, char** data, uint32_t* size); 778 if (ptr != NULL) {
779 uint64_t bdid = 0;
780 sscanf(ptr, "BDID:%" SCNx64, &bdid);
781 client->device_info.bdid = (unsigned int)bdid;
782 }
783
784 ptr = strstr(iboot_string, "ECID:");
785 if (ptr != NULL) {
786 sscanf(ptr, "ECID:%" SCNx64, &client->device_info.ecid);
787 }
788
789 ptr = strstr(iboot_string, "IBFL:");
790 if (ptr != NULL) {
791 sscanf(ptr, "IBFL:%x", &client->device_info.ibfl);
792 }
793
794 char tmp[256];
795 tmp[0] = '\0';
796 ptr = strstr(iboot_string, "SRNM:[");
797 if (ptr != NULL) {
798 sscanf(ptr, "SRNM:[%s]", tmp);
799 ptr = strrchr(tmp, ']');
800 if (ptr != NULL) {
801 *ptr = '\0';
802 }
803 client->device_info.srnm = strdup(tmp);
804 }
805
806 tmp[0] = '\0';
807 ptr = strstr(iboot_string, "IMEI:[");
808 if (ptr != NULL) {
809 sscanf(ptr, "IMEI:[%s]", tmp);
810 ptr = strrchr(tmp, ']');
811 if (ptr != NULL) {
812 *ptr = '\0';
813 }
814 client->device_info.imei = strdup(tmp);
815 }
816
817 tmp[0] = '\0';
818 ptr = strstr(iboot_string, "SRTG:[");
819 if (ptr != NULL) {
820 sscanf(ptr, "SRTG:[%s]", tmp);
821 ptr = strrchr(tmp, ']');
822 if (ptr != NULL) {
823 *ptr = '\0';
824 }
825 client->device_info.srtg = strdup(tmp);
826 }
827
828 client->device_info.pid = client->mode;
829 if (client->isKIS) {
830 client->device_info.pid = KIS_PRODUCT_ID;
831 }
832}
833
834static void irecv_copy_nonce_with_tag_from_buffer(const char* tag, unsigned char** nonce, unsigned int* nonce_size, const char *buf)
835{
836 int taglen = strlen(tag);
837 int nlen = 0;
838 const char* nonce_string = NULL;
839 const char* p = buf;
840 char* colon = NULL;
841 do {
842 colon = strchr(p, ':');
843 if (!colon)
844 break;
845 if (colon-taglen < p) {
846 break;
847 }
848 char *space = strchr(colon, ' ');
849 if (strncmp(colon-taglen, tag, taglen) == 0) {
850 p = colon+1;
851 if (!space) {
852 nlen = strlen(p);
853 } else {
854 nlen = space-p;
855 }
856 nonce_string = p;
857 nlen/=2;
858 break;
859 } else {
860 if (!space) {
861 break;
862 } else {
863 p = space+1;
864 }
865 }
866 } while (colon);
867
868 if (nlen == 0) {
869 debug("%s: WARNING: couldn't find tag %s in string %s\n", __func__, tag, buf);
870 return;
871 }
872
873 unsigned char *nn = malloc(nlen);
874 if (!nn) {
875 return;
876 }
35 877
36irecv_error_t irecv_open(irecv_client_t* pclient) {
37 int i = 0; 878 int i = 0;
38 char serial[256]; 879 for (i = 0; i < nlen; i++) {
880 int val = 0;
881 if (sscanf(nonce_string+(i*2), "%02X", &val) == 1) {
882 nn[i] = (unsigned char)val;
883 } else {
884 debug("%s: ERROR: unexpected data in nonce result (%2s)\n", __func__, nonce_string+(i*2));
885 break;
886 }
887 }
888
889 if (i != nlen) {
890 debug("%s: ERROR: unable to parse nonce\n", __func__);
891 free(nn);
892 return;
893 }
894
895 *nonce = nn;
896 *nonce_size = nlen;
897}
898
899static void irecv_copy_nonce_with_tag(irecv_client_t client, const char* tag, unsigned char** nonce, unsigned int* nonce_size)
900{
901 if (!client || !tag) {
902 return;
903 }
904
905 char buf[256];
906 int len = 0;
907
908 *nonce = NULL;
909 *nonce_size = 0;
910
911 memset(buf, 0, 256);
912 len = irecv_get_string_descriptor_ascii(client, 1, (unsigned char*) buf, 255);
913 if (len < 0) {
914 debug("%s: got length: %d\n", __func__, len);
915 return;
916 }
917
918 buf[len] = 0;
919
920 irecv_copy_nonce_with_tag_from_buffer(tag,nonce,nonce_size,buf);
921}
922
923#ifndef WIN32
924static irecv_error_t irecv_kis_request_init(KIS_req_header *hdr, uint8_t portal, uint16_t index, size_t argCount, size_t payloadSize, size_t rplWords)
925{
926 if (argCount > UINT8_MAX) {
927 return IRECV_E_INVALID_INPUT;
928 }
929
930 if (index >= (1 << 10)) {
931 return IRECV_E_INVALID_INPUT;
932 }
933
934 if (rplWords >= (1 << 14)) {
935 return IRECV_E_INVALID_INPUT;
936 }
937
938 size_t reqSize = payloadSize + (argCount << 2);
939 if (reqSize > UINT32_MAX) {
940 return IRECV_E_INVALID_INPUT;
941 }
942
943 hdr->sequence = 0; // Doesn't matter
944 hdr->version = 0xA0;
945 hdr->portal = portal;
946 hdr->argCount = (uint8_t) argCount;
947 hdr->indexLo = (uint8_t) (index & 0xFF);
948 hdr->indexHiRplSizeLo = (uint8_t) (((index >> 8) & 0x3) | ((rplWords << 2) & 0xFC));
949 hdr->rplSizeHi = (uint8_t) ((rplWords >> 6) & 0xFF);
950 hdr->reqSize = (uint32_t) reqSize;
951
952 return IRECV_E_SUCCESS;
953}
954
955static irecv_error_t irecv_kis_request(irecv_client_t client, KIS_req_header *req, size_t reqSize, KIS_req_header *rpl, size_t *rplSize)
956{
957 int endpoint = 0;
958 switch (req->portal) {
959 case KIS_PORTAL_CONFIG:
960 endpoint = 1;
961 break;
962 case KIS_PORTAL_RSM:
963 endpoint = 3;
964 break;
965 default:
966 debug("Don't know which endpoint to use for portal %d\n", req->portal);
967 return IRECV_E_INVALID_INPUT;
968 }
969
970 int sent = 0;
971 irecv_error_t err = irecv_usb_bulk_transfer(client, endpoint, (unsigned char *) req, reqSize, &sent, USB_TIMEOUT);
972 if (err != IRECV_E_SUCCESS) {
973 debug("[send] irecv_usb_bulk_transfer failed, error %d\n", err);
974 return err;
975 }
976
977 if ((size_t) sent != reqSize) {
978 debug("sent != reqSize\n");
979 return IRECV_E_USB_UPLOAD;
980 }
981
982 int rcvd = 0;
983 err = irecv_usb_bulk_transfer(client, endpoint | 0x80, (unsigned char *) rpl, *rplSize, &rcvd, USB_TIMEOUT);
984 if (err != IRECV_E_SUCCESS) {
985 debug("[rcv] irecv_usb_bulk_transfer failed, error %d\n", err);
986 return err;
987 }
988
989 *rplSize = rcvd;
990
991 return IRECV_E_SUCCESS;
992}
993
994static irecv_error_t irecv_kis_config_write32(irecv_client_t client, uint8_t portal, uint16_t index, uint32_t value)
995{
996 KIS_config_wr32 req = {};
997 KIS_generic_reply rpl = {};
998 irecv_error_t err = irecv_kis_request_init(&req.hdr, portal, index, 1, 0, 1);
999 if (err != IRECV_E_SUCCESS) {
1000 debug("Failed to init KIS request, error %d\n", err);
1001 return err;
1002 }
1003
1004 req.value = value;
1005
1006 size_t rplSize = sizeof(rpl);
1007 err = irecv_kis_request(client, &req.hdr, sizeof(req), &rpl.hdr, &rplSize);
1008 if (err != IRECV_E_SUCCESS) {
1009 debug("Failed to send KIS request, error %d\n", err);
1010 return err;
1011 }
1012
1013 if (rpl.size != 4) {
1014 debug("Failed to write config, %d bytes written, status %d\n", rpl.size, rpl.status);
1015 return err;
1016 }
1017
1018 return IRECV_E_SUCCESS;
1019}
1020
1021static int irecv_kis_read_string(KIS_device_info *di, size_t off, char *buf, size_t buf_size)
1022{
1023 off *= 4;
1024
1025 size_t inputSize = sizeof(KIS_device_info) - sizeof(KIS_req_header);
1026
1027 if ((off + 2) > inputSize)
1028 return 0;
1029
1030 uint8_t len = di->deviceInfo[off];
1031 uint8_t type = di->deviceInfo[off + 1];
1032
1033 if (len & 1)
1034 return 0;
1035
1036 if (len/2 >= buf_size)
1037 return 0;
1038
1039 if ((off + 2 + len) > inputSize)
1040 return 0;
1041
1042 if (type != 3)
1043 return 0;
1044
1045 buf[len >> 1] = 0;
1046 for (size_t i = 0; i < len; i += 2) {
1047 buf[i >> 1] = di->deviceInfo[i + off + 2];
1048 }
1049
1050 return len/2;
1051}
1052#endif
1053
1054static irecv_error_t irecv_kis_init(irecv_client_t client)
1055{
1056#ifndef WIN32
1057 irecv_error_t err = irecv_kis_config_write32(client, KIS_PORTAL_CONFIG, KIS_INDEX_ENABLE_A, KIS_ENABLE_A_VAL);
1058 if (err != IRECV_E_SUCCESS) {
1059 debug("Failed to write to KIS_INDEX_ENABLE_A, error %d\n", err);
1060 return err;
1061 }
1062
1063 err = irecv_kis_config_write32(client, KIS_PORTAL_CONFIG, KIS_INDEX_ENABLE_B, KIS_ENABLE_B_VAL);
1064 if (err != IRECV_E_SUCCESS) {
1065 debug("Failed to write to KIS_INDEX_ENABLE_B, error %d\n", err);
1066 return err;
1067 }
1068#endif
1069 client->isKIS = 1;
1070
1071 return IRECV_E_SUCCESS;
1072}
1073
1074static irecv_error_t irecv_kis_load_device_info(irecv_client_t client)
1075{
1076 debug("Loading device info in KIS mode...\n");
1077#ifdef WIN32
1078 KIS_device_info kisInfo;
1079 DWORD transferred = 0;
1080 int ret = DeviceIoControl(client->handle, 0x220004, NULL, 0, &kisInfo, sizeof(kisInfo), (PDWORD)&transferred, NULL);
1081 if (ret) {
1082 debug("Serial: %s\n", kisInfo.serial);
1083 irecv_load_device_info_from_iboot_string(client, kisInfo.serial);
1084 debug("Manufacturer: %s\n", kisInfo.manufacturer);
1085 debug("Product: %s\n", kisInfo.product);
1086 debug("Nonces: %s\n", kisInfo.nonces);
1087 irecv_copy_nonce_with_tag_from_buffer("NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size, kisInfo.nonces);
1088 irecv_copy_nonce_with_tag_from_buffer("SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size, kisInfo.nonces);
1089 debug("VID: 0x%04x\n", kisInfo.vid);
1090 debug("PID: 0x%04x\n", kisInfo.pid);
1091 }
1092 client->mode = kisInfo.pid;
1093#else
1094 KIS_req_header req = {};
1095 KIS_device_info di = {};
1096 irecv_error_t err = irecv_kis_request_init(&req, KIS_PORTAL_RSM, KIS_INDEX_GET_INFO, 0, 0, sizeof(di.deviceInfo)/4);
1097 if (err != IRECV_E_SUCCESS) {
1098 debug("Failed to init KIS request, error %d\n", err);
1099 return err;
1100 }
1101
1102 size_t rcvSize = sizeof(di);
1103 err = irecv_kis_request(client, &req, sizeof(req), &di.hdr, &rcvSize);
1104 if (err != IRECV_E_SUCCESS) {
1105 debug("Failed to send KIS request, error %d\n", err);
1106 return err;
1107 }
1108
1109 char buf[0x100];
1110 int len = 0;
1111
1112 len = irecv_kis_read_string(&di, di.deviceDescriptor.iSerialNumber, buf, sizeof(buf));
1113 if (len == 0)
1114 return IRECV_E_INVALID_INPUT;
1115 debug("Serial: %s\n", buf);
1116
1117 irecv_load_device_info_from_iboot_string(client, buf);
1118
1119 len = irecv_kis_read_string(&di, di.deviceDescriptor.iManufacturer, buf, sizeof(buf));
1120 if (len == 0)
1121 return IRECV_E_INVALID_INPUT;
1122 debug("Manufacturer: %s\n", buf);
1123
1124 len = irecv_kis_read_string(&di, di.deviceDescriptor.iProduct, buf, sizeof(buf));
1125 if (len == 0)
1126 return IRECV_E_INVALID_INPUT;
1127 debug("Product: %s\n", buf);
1128
1129 len = irecv_kis_read_string(&di, di.nonceOffset, buf, sizeof(buf));
1130 if (len == 0)
1131 return IRECV_E_INVALID_INPUT;
1132 debug("Nonces: %s\n", buf);
1133
1134 irecv_copy_nonce_with_tag_from_buffer("NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size, buf);
1135 irecv_copy_nonce_with_tag_from_buffer("SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size, buf);
1136
1137 debug("VID: 0x%04x\n", di.deviceDescriptor.idVendor);
1138 debug("PID: 0x%04x\n", di.deviceDescriptor.idProduct);
1139
1140 client->mode = di.deviceDescriptor.idProduct;
1141#endif
1142 return IRECV_E_SUCCESS;
1143}
1144
1145#ifdef WIN32
1146static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}};
1147static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}};
1148static const GUID GUID_DEVINTERFACE_KIS = {0xB36F4137L, 0xF4EF, 0x4BFC, {0xA2, 0x5A, 0xC2, 0x41, 0x07, 0x68, 0xEE, 0x37}};
1149static const GUID GUID_DEVINTERFACE_PORTDFU = {0xAF633FF1L, 0x1170, 0x4CA6, {0xAE, 0x9E, 0x08, 0xD0, 0x01, 0x42, 0x1E, 0xAA}};
1150
1151typedef struct usb_control_request {
1152 uint8_t bmRequestType;
1153 uint8_t bRequest;
1154 uint16_t wValue;
1155 uint16_t wIndex;
1156 uint16_t wLength;
1157
1158 char data[];
1159} usb_control_request;
1160
1161static irecv_error_t win32_open_with_ecid(irecv_client_t* client, uint64_t ecid)
1162{
1163 int found = 0;
1164 const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_PORTDFU, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
1165 irecv_client_t _client = (irecv_client_t) malloc(sizeof(struct irecv_client_private));
1166 memset(_client, 0, sizeof(struct irecv_client_private));
1167
1168 int k;
1169 for (k = 0; !found && guids[k]; k++) {
1170 DWORD i;
1171 SP_DEVICE_INTERFACE_DATA currentInterface;
1172 HDEVINFO usbDevices = SetupDiGetClassDevs(guids[k], NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
1173 memset(&currentInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA));
1174 currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
1175 for (i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, guids[k], i, &currentInterface); i++) {
1176 _client->handle = INVALID_HANDLE_VALUE;
1177 DWORD requiredSize = 0;
1178 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details;
1179 SetupDiGetDeviceInterfaceDetailA(usbDevices, &currentInterface, NULL, 0, &requiredSize, NULL);
1180 details = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A) malloc(requiredSize);
1181 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
1182 if (!SetupDiGetDeviceInterfaceDetailA(usbDevices, &currentInterface, details, requiredSize, NULL, NULL)) {
1183 free(details);
1184 continue;
1185 }
1186
1187 unsigned int pid = 0;
1188 unsigned int vid = 0;
1189 if (sscanf(details->DevicePath, "\\\\?\\%*3s#vid_%04x&pid_%04x", &vid, &pid) != 2) {
1190 debug("%s: ERROR: failed to parse VID/PID! path: %s\n", __func__, details->DevicePath);
1191 free(details);
1192 continue;
1193 }
1194 if (vid != APPLE_VENDOR_ID) {
1195 free(details);
1196 continue;
1197 }
1198
1199 // make sure the current device is actually in the right mode for the given driver interface
1200 if ((guids[k] == &GUID_DEVINTERFACE_DFU && pid != IRECV_K_DFU_MODE && pid != IRECV_K_WTF_MODE)
1201 || (guids[k] == &GUID_DEVINTERFACE_PORTDFU && pid != IRECV_K_PORT_DFU_MODE)
1202 || (guids[k] == &GUID_DEVINTERFACE_IBOOT && (pid < IRECV_K_RECOVERY_MODE_1 || pid > IRECV_K_RECOVERY_MODE_4))
1203 || (guids[k] == &GUID_DEVINTERFACE_KIS && pid != 1)
1204 ) {
1205 free(details);
1206 continue;
1207 }
1208 if (guids[k] == &GUID_DEVINTERFACE_KIS) {
1209 pid = KIS_PRODUCT_ID;
1210 }
1211
1212 _client->handle = CreateFileA(details->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
1213 if (_client->handle == INVALID_HANDLE_VALUE) {
1214 debug("%s: Failed to open device path %s: %d\n", __func__, details->DevicePath, (int)GetLastError());
1215 free(details);
1216 continue;
1217 }
1218 _client->mode = pid;
1219
1220 if (ecid == IRECV_K_WTF_MODE) {
1221 if (_client->mode != IRECV_K_WTF_MODE) {
1222 /* special ecid case, ignore !IRECV_K_WTF_MODE */
1223 CloseHandle(_client->handle);
1224 free(details);
1225 continue;
1226 } else {
1227 ecid = 0;
1228 }
1229 }
1230
1231 if ((ecid != 0) && (_client->mode == IRECV_K_WTF_MODE)) {
1232 /* we can't get ecid in WTF mode */
1233 CloseHandle(_client->handle);
1234 free(details);
1235 continue;
1236 }
1237
1238 char serial_str[256];
1239 serial_str[0] = '\0';
1240
1241 if (_client->mode != KIS_PRODUCT_ID) {
1242 char *p = (char*)details->DevicePath;
1243 while ((p = strstr(p, "\\usb"))) {
1244 if (sscanf(p, "\\usb#vid_05ac&pid_%*04x#%s", serial_str) == 1)
1245 break;
1246 p += 4;
1247 }
1248 free(details);
1249
1250 if (serial_str[0] == '\0') {
1251 CloseHandle(_client->handle);
1252 continue;
1253 }
1254 p = strchr(serial_str, '#');
1255 if (p) {
1256 *p = '\0';
1257 }
1258
1259 unsigned int j;
1260 for (j = 0; j < strlen(serial_str); j++) {
1261 if (serial_str[j] == '_') {
1262 serial_str[j] = ' ';
1263 } else {
1264 serial_str[j] = toupper(serial_str[j]);
1265 }
1266 }
1267
1268 irecv_load_device_info_from_iboot_string(_client, serial_str);
1269 }
1270
1271 if (ecid != 0 && _client->mode != KIS_PRODUCT_ID) {
1272 if (_client->device_info.ecid != ecid) {
1273 CloseHandle(_client->handle);
1274 continue;
1275 }
1276 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)ecid);
1277 }
1278 found = 1;
1279 break;
1280 }
1281 SetupDiDestroyDeviceInfoList(usbDevices);
1282 }
1283
1284 if (!found) {
1285 irecv_close(_client);
1286 return IRECV_E_UNABLE_TO_CONNECT;
1287 }
1288
1289 *client = _client;
1290
1291 return IRECV_E_SUCCESS;
1292}
1293#endif
1294
1295#ifdef HAVE_IOKIT
1296static void iokit_cfdictionary_set_short(CFMutableDictionaryRef dict, const void *key, SInt16 value)
1297{
1298 CFNumberRef numberRef;
1299
1300 numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &value);
1301 if (numberRef) {
1302 CFDictionarySetValue(dict, key, numberRef);
1303 CFRelease(numberRef);
1304 }
1305}
1306#endif
1307
1308static int check_context(irecv_client_t client)
1309{
1310 if (client == NULL || client->handle == NULL) {
1311 return IRECV_E_NO_DEVICE;
1312 }
1313
1314 return IRECV_E_SUCCESS;
1315}
1316#endif
1317
1318void irecv_init(void)
1319{
1320#ifndef USE_DUMMY
1321 thread_once(&init_once, _irecv_init);
1322#endif
1323}
1324
1325void irecv_exit(void)
1326{
1327#ifndef USE_DUMMY
1328 thread_once(&deinit_once, _irecv_deinit);
1329#endif
1330}
1331
1332#ifndef USE_DUMMY
1333#ifdef HAVE_IOKIT
1334static int iokit_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, unsigned int timeout)
1335{
1336 IOReturn result;
1337 IOUSBDevRequestTO req;
1338
1339 bzero(&req, sizeof(req));
1340 req.bmRequestType = bm_request_type;
1341 req.bRequest = b_request;
1342 req.wValue = OSSwapLittleToHostInt16(w_value);
1343 req.wIndex = OSSwapLittleToHostInt16(w_index);
1344 req.wLength = OSSwapLittleToHostInt16(w_length);
1345 req.pData = data;
1346 req.noDataTimeout = timeout;
1347 req.completionTimeout = timeout;
1348
1349 result = (*client->handle)->DeviceRequestTO(client->handle, &req);
1350 switch (result) {
1351 case kIOReturnSuccess: return req.wLenDone;
1352 case kIOReturnTimeout: return IRECV_E_TIMEOUT;
1353 case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT;
1354 case kIOReturnNotResponding: return IRECV_E_NO_DEVICE;
1355 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
1356 default:
1357 return IRECV_E_UNKNOWN_ERROR;
1358 }
1359}
1360#else
1361#ifdef __APPLE__
1362 void dummy_callback(void) { }
1363#endif
1364#endif
1365#endif
1366
1367int irecv_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, unsigned int timeout)
1368{
1369#ifdef USE_DUMMY
1370 return IRECV_E_UNSUPPORTED;
1371#else
1372#ifndef WIN32
1373#ifdef HAVE_IOKIT
1374 return iokit_usb_control_transfer(client, bm_request_type, b_request, w_value, w_index, data, w_length, timeout);
1375#else
1376 return libusb_control_transfer(client->handle, bm_request_type, b_request, w_value, w_index, data, w_length, timeout);
1377#endif
1378#else
1379 DWORD count = 0;
1380 BOOL bRet;
1381 OVERLAPPED overlapped;
1382
1383 if (data == NULL)
1384 w_length = 0;
1385
1386 usb_control_request* packet = (usb_control_request*) malloc(sizeof(usb_control_request) + w_length);
1387 packet->bmRequestType = bm_request_type;
1388 packet->bRequest = b_request;
1389 packet->wValue = w_value;
1390 packet->wIndex = w_index;
1391 packet->wLength = w_length;
1392
1393 if (bm_request_type < 0x80 && w_length > 0) {
1394 memcpy(packet->data, data, w_length);
1395 }
1396
1397 memset(&overlapped, 0, sizeof(overlapped));
1398 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1399 DeviceIoControl(client->handle, 0x2200A0, packet, sizeof(usb_control_request) + w_length, packet, sizeof(usb_control_request) + w_length, NULL, &overlapped);
1400 WaitForSingleObject(overlapped.hEvent, timeout);
1401 bRet = GetOverlappedResult(client->handle, &overlapped, &count, FALSE);
1402 CloseHandle(overlapped.hEvent);
1403 if (!bRet) {
1404 CancelIo(client->handle);
1405 free(packet);
1406 return -1;
1407 }
1408
1409 count -= sizeof(usb_control_request);
1410 if (count > 0) {
1411 if (bm_request_type >= 0x80) {
1412 memcpy(data, packet->data, count);
1413 }
1414 }
1415 free(packet);
1416
1417 return count;
1418#endif
1419#endif
1420}
1421
1422#ifndef USE_DUMMY
1423#ifdef HAVE_IOKIT
1424static int iokit_usb_bulk_transfer(irecv_client_t client,
1425 unsigned char endpoint,
1426 unsigned char *data,
1427 int length,
1428 int *transferred,
1429 unsigned int timeout)
1430{
1431 IOReturn result;
1432 IOUSBInterfaceInterface300 **intf = client->usbInterface;
1433 UInt32 size = length;
1434 UInt8 isUSBIn = (endpoint & kUSBbEndpointDirectionMask) != 0;
1435 UInt8 numEndpoints;
1436
1437 if (!intf) return IRECV_E_USB_INTERFACE;
1438
1439 result = (*intf)->GetNumEndpoints(intf, &numEndpoints);
1440
1441 if (result != kIOReturnSuccess)
1442 return IRECV_E_USB_INTERFACE;
1443
1444 for (UInt8 pipeRef = 0; pipeRef <= numEndpoints; pipeRef++) {
1445 UInt8 direction = 0;
1446 UInt8 number = 0;
1447 UInt8 transferType = 0;
1448 UInt16 maxPacketSize = 0;
1449 UInt8 interval = 0;
1450
1451 result = (*intf)->GetPipeProperties(intf, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval);
1452 if (result != kIOReturnSuccess)
1453 continue;
1454
1455 if (direction == 3)
1456 direction = isUSBIn;
1457
1458 if (number != (endpoint & ~kUSBbEndpointDirectionMask) || direction != isUSBIn)
1459 continue;
1460
1461 // Just because
1462 result = (*intf)->GetPipeStatus(intf, pipeRef);
1463 switch (result) {
1464 case kIOReturnSuccess: break;
1465 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
1466 case kIOReturnNotOpen: return IRECV_E_UNABLE_TO_CONNECT;
1467 default: return IRECV_E_USB_STATUS;
1468 }
1469
1470 // Do the transfer
1471 if (isUSBIn) {
1472 result = (*intf)->ReadPipeTO(intf, pipeRef, data, &size, timeout, timeout);
1473 if (result != kIOReturnSuccess)
1474 return IRECV_E_PIPE;
1475 *transferred = size;
1476
1477 return IRECV_E_SUCCESS;
1478 }
1479 else {
1480 // IOUSBInterfaceClass::interfaceWritePipe (intf?, pipeRef==1, data, size=0x8000)
1481 result = (*intf)->WritePipeTO(intf, pipeRef, data, size, timeout, timeout);
1482 if (result != kIOReturnSuccess)
1483 return IRECV_E_PIPE;
1484 *transferred = size;
1485
1486 return IRECV_E_SUCCESS;
1487 }
1488 }
1489
1490 return IRECV_E_USB_INTERFACE;
1491}
1492#endif
1493#endif
1494
1495int irecv_usb_bulk_transfer(irecv_client_t client,
1496 unsigned char endpoint,
1497 unsigned char *data,
1498 int length,
1499 int *transferred,
1500 unsigned int timeout)
1501{
1502#ifdef USE_DUMMY
1503 return IRECV_E_UNSUPPORTED;
1504#else
1505 int ret;
1506
1507#ifndef WIN32
1508#ifdef HAVE_IOKIT
1509 return iokit_usb_bulk_transfer(client, endpoint, data, length, transferred, timeout);
1510#else
1511 ret = libusb_bulk_transfer(client->handle, endpoint, data, length, transferred, timeout);
1512 if (ret < 0) {
1513 libusb_clear_halt(client->handle, endpoint);
1514 }
1515#endif
1516#else
1517 if (endpoint==0x4) {
1518 ret = DeviceIoControl(client->handle, 0x2201B6, data, length, data, length, (PDWORD) transferred, NULL);
1519 } else {
1520 ret = 0;
1521 }
1522 ret = (ret==0) ? -1 : 0;
1523#endif
1524
1525 return ret;
1526#endif
1527}
1528
1529#ifndef USE_DUMMY
1530#ifdef HAVE_IOKIT
1531static irecv_error_t iokit_usb_open_service(irecv_client_t *pclient, io_service_t service)
1532{
1533 IOReturn result;
1534 irecv_client_t client;
1535 SInt32 score;
1536 UInt16 mode;
1537 UInt32 locationID;
1538 IOCFPlugInInterface **plug = NULL;
1539 CFStringRef serialString;
1540
1541 client = (irecv_client_t) calloc( 1, sizeof(struct irecv_client_private));
1542
1543 // Create the plug-in
1544 result = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plug, &score);
1545 if (result != kIOReturnSuccess) {
1546 IOObjectRelease(service);
1547 free(client);
1548 return IRECV_E_UNKNOWN_ERROR;
1549 }
1550
1551 // Cache the serial string before discarding the service. The service object
1552 // has a cached copy, so a request to the hardware device is not required.
1553 char serial_str[256];
1554 serial_str[0] = '\0';
1555 serialString = IORegistryEntryCreateCFProperty(service, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
1556 if (serialString) {
1557 CFStringGetCString(serialString, serial_str, sizeof(serial_str), kCFStringEncodingUTF8);
1558 CFRelease(serialString);
1559 }
1560 irecv_load_device_info_from_iboot_string(client, serial_str);
1561
1562 IOObjectRelease(service);
1563
1564 // Create the device interface
1565 result = (*plug)->QueryInterface(plug, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320), (LPVOID *)&(client->handle));
1566 IODestroyPlugInInterface(plug);
1567 if (result != kIOReturnSuccess) {
1568 free(client);
1569 return IRECV_E_UNKNOWN_ERROR;
1570 }
1571
1572 (*client->handle)->GetDeviceProduct(client->handle, &mode);
1573 (*client->handle)->GetLocationID(client->handle, &locationID);
1574 client->mode = mode;
1575 debug("opening device %04x:%04x @ %#010x...\n", kAppleVendorID, client->mode, locationID);
1576
1577 result = (*client->handle)->USBDeviceOpenSeize(client->handle);
1578 if (result != kIOReturnSuccess) {
1579 (*client->handle)->Release(client->handle);
1580 free(client);
1581 return IRECV_E_UNABLE_TO_CONNECT;
1582 }
1583
1584 *pclient = client;
1585 return IRECV_E_SUCCESS;
1586}
1587
1588static io_iterator_t iokit_usb_get_iterator_for_pid(UInt16 pid)
1589{
1590 IOReturn result;
1591 io_iterator_t iterator;
1592 CFMutableDictionaryRef matchingDict;
1593
1594 matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
1595 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBVendorID), kAppleVendorID);
1596 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBProductID), pid);
1597
1598 result = IOServiceGetMatchingServices(MACH_PORT_NULL, matchingDict, &iterator);
1599 if (result != kIOReturnSuccess)
1600 return IO_OBJECT_NULL;
1601
1602 return iterator;
1603}
1604
1605static irecv_error_t iokit_open_with_ecid(irecv_client_t* pclient, uint64_t ecid)
1606{
1607 io_service_t service, ret_service;
1608 io_iterator_t iterator;
1609 CFStringRef usbSerial = NULL;
1610 CFStringRef ecidString = NULL;
1611 CFRange range;
1612
1613 UInt16 wtf_pids[] = { IRECV_K_WTF_MODE, 0};
1614 UInt16 all_pids[] = { IRECV_K_WTF_MODE, IRECV_K_DFU_MODE, IRECV_K_PORT_DFU_MODE, IRECV_K_RECOVERY_MODE_1, IRECV_K_RECOVERY_MODE_2, IRECV_K_RECOVERY_MODE_3, IRECV_K_RECOVERY_MODE_4, KIS_PRODUCT_ID, 0 };
1615 UInt16 *pids = all_pids;
1616 int i;
1617
1618 if (pclient == NULL) {
1619 debug("%s: pclient parameter is null\n", __func__);
1620 return IRECV_E_INVALID_INPUT;
1621 }
1622 if (ecid == IRECV_K_WTF_MODE) {
1623 /* special ecid case, ignore !IRECV_K_WTF_MODE */
1624 pids = wtf_pids;
1625 ecid = 0;
1626 }
1627 if (ecid > 0) {
1628 ecidString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%llX"), ecid);
1629 if (ecidString == NULL) {
1630 debug("%s: failed to create ECID string\n", __func__);
1631 return IRECV_E_UNABLE_TO_CONNECT;
1632 }
1633 }
1634
1635 *pclient = NULL;
1636 ret_service = IO_OBJECT_NULL;
1637
1638 for (i = 0; (pids[i] > 0 && ret_service == IO_OBJECT_NULL) ; i++) {
1639
1640 iterator = iokit_usb_get_iterator_for_pid(pids[i]);
1641 if (iterator) {
1642 while ((service = IOIteratorNext(iterator))) {
1643
1644 if (ecid == 0) {
1645 ret_service = service;
1646 break;
1647 }
1648
1649 if (pids[i] == KIS_PRODUCT_ID) {
1650 // In KIS Mode, we have to open the device in order to get
1651 // it's ECID
1652 irecv_error_t err = iokit_usb_open_service(pclient, service);
1653 if (err != IRECV_E_SUCCESS) {
1654 debug("%s: failed to open KIS device\n", __func__);
1655 continue;
1656 }
1657
1658 if (ecidString)
1659 CFRelease(ecidString);
1660
1661 return IRECV_E_SUCCESS;
1662 }
1663
1664 usbSerial = IORegistryEntryCreateCFProperty(service, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
1665 if (usbSerial == NULL) {
1666 debug("%s: failed to create USB serial string property\n", __func__);
1667 IOObjectRelease(service);
1668 continue;
1669 }
1670
1671 range = CFStringFind(usbSerial, ecidString, kCFCompareCaseInsensitive);
1672 if (range.location == kCFNotFound) {
1673 IOObjectRelease(service);
1674 } else {
1675 ret_service = service;
1676 break;
1677 }
1678 }
1679 if (usbSerial) {
1680 CFRelease(usbSerial);
1681 usbSerial = NULL;
1682 }
1683 IOObjectRelease(iterator);
1684 }
1685 }
1686
1687 if (ecidString)
1688 CFRelease(ecidString);
1689
1690 if (ret_service == IO_OBJECT_NULL)
1691 return IRECV_E_UNABLE_TO_CONNECT;
1692
1693 return iokit_usb_open_service(pclient, ret_service);
1694}
1695#endif
1696
1697#ifndef WIN32
1698#ifndef HAVE_IOKIT
1699static irecv_error_t libusb_usb_open_handle_with_descriptor_and_ecid(irecv_client_t *pclient, struct libusb_device_handle *usb_handle, struct libusb_device_descriptor *usb_descriptor, uint64_t ecid)
1700{
1701 irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client_private));
1702 if (client == NULL) {
1703 libusb_close(usb_handle);
1704 return IRECV_E_OUT_OF_MEMORY;
1705 }
1706
1707 memset(client, '\0', sizeof(struct irecv_client_private));
1708 client->usb_interface = 0;
1709 client->handle = usb_handle;
1710 client->mode = usb_descriptor->idProduct;
1711
1712 if (client->mode != KIS_PRODUCT_ID) {
1713 char serial_str[256];
1714 memset(serial_str, 0, 256);
1715 irecv_get_string_descriptor_ascii(client, usb_descriptor->iSerialNumber, (unsigned char*)serial_str, 255);
1716 irecv_load_device_info_from_iboot_string(client, serial_str);
1717 }
1718
1719 if (ecid != 0 && client->mode != KIS_PRODUCT_ID) {
1720 if (client->device_info.ecid != ecid) {
1721 irecv_close(client);
1722 return IRECV_E_NO_DEVICE; //wrong device
1723 }
1724 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)ecid);
1725 }
1726
1727 *pclient = client;
1728 return IRECV_E_SUCCESS;
1729}
1730
1731static irecv_error_t libusb_open_with_ecid(irecv_client_t* pclient, uint64_t ecid)
1732{
1733 irecv_error_t ret = IRECV_E_UNABLE_TO_CONNECT;
1734 int i = 0;
39 struct libusb_device* usb_device = NULL; 1735 struct libusb_device* usb_device = NULL;
40 struct libusb_device** usb_device_list = NULL; 1736 struct libusb_device** usb_device_list = NULL;
41 struct libusb_device_handle* usb_handle = NULL;
42 struct libusb_device_descriptor usb_descriptor; 1737 struct libusb_device_descriptor usb_descriptor;
43 1738
44 *pclient = NULL; 1739 *pclient = NULL;
45 libusb_init(&libirecovery_context);
46 if(libirecovery_debug) {
47 irecv_set_debug_level(libirecovery_debug);
48 }
49
50 irecv_error_t error = IRECV_E_SUCCESS;
51 int usb_device_count = libusb_get_device_list(libirecovery_context, &usb_device_list); 1740 int usb_device_count = libusb_get_device_list(libirecovery_context, &usb_device_list);
52 for (i = 0; i < usb_device_count; i++) { 1741 for (i = 0; i < usb_device_count; i++) {
53 usb_device = usb_device_list[i]; 1742 usb_device = usb_device_list[i];
54 libusb_get_device_descriptor(usb_device, &usb_descriptor); 1743 libusb_get_device_descriptor(usb_device, &usb_descriptor);
55 if (usb_descriptor.idVendor == APPLE_VENDOR_ID) { 1744 if (usb_descriptor.idVendor == APPLE_VENDOR_ID) {
56 /* verify this device is in a mode we understand */ 1745 /* verify this device is in a mode we understand */
57 if (usb_descriptor.idProduct == kRecoveryMode1 || 1746 if (usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_1 ||
58 usb_descriptor.idProduct == kRecoveryMode2 || 1747 usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_2 ||
59 usb_descriptor.idProduct == kRecoveryMode3 || 1748 usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_3 ||
60 usb_descriptor.idProduct == kRecoveryMode4 || 1749 usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_4 ||
61 usb_descriptor.idProduct == kDfuMode) { 1750 usb_descriptor.idProduct == IRECV_K_WTF_MODE ||
1751 usb_descriptor.idProduct == IRECV_K_DFU_MODE ||
1752 usb_descriptor.idProduct == IRECV_K_PORT_DFU_MODE ||
1753 usb_descriptor.idProduct == KIS_PRODUCT_ID) {
1754
1755 if (ecid == IRECV_K_WTF_MODE) {
1756 if (usb_descriptor.idProduct != IRECV_K_WTF_MODE) {
1757 /* special ecid case, ignore !IRECV_K_WTF_MODE */
1758 continue;
1759 } else {
1760 ecid = 0;
1761 }
1762 }
1763
1764 if ((ecid != 0) && (usb_descriptor.idProduct == IRECV_K_WTF_MODE)) {
1765 /* we can't get ecid in WTF mode */
1766 continue;
1767 }
62 1768
63 debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct); 1769 debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct);
64 1770
65 libusb_open(usb_device, &usb_handle); 1771 struct libusb_device_handle* usb_handle = NULL;
66 if (usb_handle == NULL) { 1772 int libusb_error = libusb_open(usb_device, &usb_handle);
67 libusb_free_device_list(usb_device_list, 1); 1773 if (usb_handle == NULL || libusb_error != 0) {
1774 debug("%s: can't connect to device: %s\n", __func__, libusb_error_name(libusb_error));
1775
68 libusb_close(usb_handle); 1776 libusb_close(usb_handle);
69 libusb_exit(libirecovery_context); 1777 if (ecid != 0) {
1778 continue;
1779 }
1780 libusb_free_device_list(usb_device_list, 1);
70 return IRECV_E_UNABLE_TO_CONNECT; 1781 return IRECV_E_UNABLE_TO_CONNECT;
71 } 1782 }
72 libusb_free_device_list(usb_device_list, 1);
73 1783
74 irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client)); 1784 ret = libusb_usb_open_handle_with_descriptor_and_ecid(pclient, usb_handle, &usb_descriptor, ecid);
75 if (client == NULL) { 1785 if (ret == IRECV_E_SUCCESS) {
76 libusb_close(usb_handle); 1786 break;
77 libusb_exit(libirecovery_context);
78 return IRECV_E_OUT_OF_MEMORY;
79 } 1787 }
1788 }
1789 }
1790 }
1791 libusb_free_device_list(usb_device_list, 1);
1792 return ret;
1793}
1794#endif
1795#endif
1796#endif
1797
1798irecv_error_t irecv_open_with_ecid(irecv_client_t* pclient, uint64_t ecid)
1799{
1800#ifdef USE_DUMMY
1801 return IRECV_E_UNSUPPORTED;
1802#else
1803 irecv_error_t error = IRECV_E_UNABLE_TO_CONNECT;
1804
1805 if (libirecovery_debug) {
1806 irecv_set_debug_level(libirecovery_debug);
1807 }
1808#ifndef WIN32
1809#ifdef HAVE_IOKIT
1810 error = iokit_open_with_ecid(pclient, ecid);
1811#else
1812 error = libusb_open_with_ecid(pclient, ecid);
1813#endif
1814#else
1815 error = win32_open_with_ecid(pclient, ecid);
1816#endif
1817 irecv_client_t client = *pclient;
1818 if (error != IRECV_E_SUCCESS) {
1819 irecv_close(client);
1820 return error;
1821 }
80 1822
81 memset(client, '\0', sizeof(struct irecv_client)); 1823 error = irecv_usb_set_configuration(client, 1);
82 client->interface = 0; 1824 if (error != IRECV_E_SUCCESS) {
83 client->handle = usb_handle; 1825 debug("Failed to set configuration, error %d\n", error);
84 client->mode = usb_descriptor.idProduct; 1826 irecv_close(client);
1827 return error;
1828 }
85 1829
86 error = irecv_set_configuration(client, 1); 1830 if (client->mode == IRECV_K_DFU_MODE || client->mode == IRECV_K_PORT_DFU_MODE || client->mode == IRECV_K_WTF_MODE || client->mode == KIS_PRODUCT_ID) {
87 if (error != IRECV_E_SUCCESS) { 1831 error = irecv_usb_set_interface(client, 0, 0);
88 return error; 1832 } else {
89 } 1833 error = irecv_usb_set_interface(client, 0, 0);
1834 if (error == IRECV_E_SUCCESS && client->mode > IRECV_K_RECOVERY_MODE_2) {
1835 error = irecv_usb_set_interface(client, 1, 1);
1836 }
1837 }
90 1838
91 error = irecv_set_interface(client, 1, 1); 1839 if (error != IRECV_E_SUCCESS) {
92 if (error != IRECV_E_SUCCESS) { 1840 debug("Failed to set interface, error %d\n", error);
93 return error; 1841 irecv_close(client);
94 } 1842 return error;
1843 }
95 1844
96 /* cache usb serial */ 1845 if (client->mode == KIS_PRODUCT_ID) {
97 libusb_get_string_descriptor_ascii(client->handle, usb_descriptor.iSerialNumber, client->serial, 255); 1846 error = irecv_kis_init(client);
1847 if (error != IRECV_E_SUCCESS) {
1848 debug("irecv_kis_init failed, error %d\n", error);
1849 irecv_close(client);
1850 return error;
1851 }
98 1852
99 *pclient = client; 1853 error = irecv_kis_load_device_info(client);
100 return IRECV_E_SUCCESS; 1854 if (error != IRECV_E_SUCCESS) {
101 } 1855 debug("irecv_kis_load_device_info failed, error %d\n", error);
1856 irecv_close(client);
1857 return error;
1858 }
1859 if (ecid != 0 && client->device_info.ecid != ecid) {
1860 irecv_close(client);
1861 return IRECV_E_NO_DEVICE; //wrong device
102 } 1862 }
1863 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)client->device_info.ecid);
1864 } else {
1865 irecv_copy_nonce_with_tag(client, "NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size);
1866 irecv_copy_nonce_with_tag(client, "SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size);
103 } 1867 }
104 1868
105 return IRECV_E_UNABLE_TO_CONNECT; 1869 if (error == IRECV_E_SUCCESS) {
1870 if ((*pclient)->connected_callback != NULL) {
1871 irecv_event_t event;
1872 event.size = 0;
1873 event.data = NULL;
1874 event.progress = 0;
1875 event.type = IRECV_CONNECTED;
1876 (*pclient)->connected_callback(*pclient, &event);
1877 }
1878 }
1879 return error;
1880#endif
106} 1881}
107 1882
108irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration) { 1883irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int configuration)
109 if (client == NULL || client->handle == NULL) { 1884{
1885#ifdef USE_DUMMY
1886 return IRECV_E_UNSUPPORTED;
1887#else
1888 if (check_context(client) != IRECV_E_SUCCESS)
110 return IRECV_E_NO_DEVICE; 1889 return IRECV_E_NO_DEVICE;
111 }
112 1890
1891#ifndef WIN32
113 debug("Setting to configuration %d\n", configuration); 1892 debug("Setting to configuration %d\n", configuration);
114 1893
1894#ifdef HAVE_IOKIT
1895 IOReturn result;
1896
1897 result = (*client->handle)->SetConfiguration(client->handle, configuration);
1898 if (result != kIOReturnSuccess) {
1899 debug("error setting configuration: %#x\n", result);
1900 return IRECV_E_USB_CONFIGURATION;
1901 }
1902#else
115 int current = 0; 1903 int current = 0;
116 libusb_get_configuration(client->handle, &current); 1904 libusb_get_configuration(client->handle, &current);
117 if (current != configuration) { 1905 if (current != configuration) {
@@ -119,45 +1907,199 @@ irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration)
119 return IRECV_E_USB_CONFIGURATION; 1907 return IRECV_E_USB_CONFIGURATION;
120 } 1908 }
121 } 1909 }
1910#endif
1911 client->usb_config = configuration;
1912#endif
122 1913
123 client->config = configuration;
124 return IRECV_E_SUCCESS; 1914 return IRECV_E_SUCCESS;
1915#endif
125} 1916}
126 1917
127irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface) { 1918#ifndef USE_DUMMY
128 if (client == NULL || client->handle == NULL) { 1919#ifdef HAVE_IOKIT
129 return IRECV_E_NO_DEVICE; 1920static IOReturn iokit_usb_get_interface(IOUSBDeviceInterface320 **device, uint8_t ifc, io_service_t *usbInterfacep)
1921{
1922 IOUSBFindInterfaceRequest request;
1923 uint8_t current_interface;
1924 kern_return_t kresult;
1925 io_iterator_t interface_iterator;
1926
1927 *usbInterfacep = IO_OBJECT_NULL;
1928
1929 request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
1930 request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
1931 request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
1932 request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
1933
1934 kresult = (*device)->CreateInterfaceIterator(device, &request, &interface_iterator);
1935 if (kresult)
1936 return kresult;
1937
1938 for ( current_interface = 0 ; current_interface <= ifc ; current_interface++ ) {
1939 *usbInterfacep = IOIteratorNext(interface_iterator);
1940 if (current_interface != ifc)
1941 (void) IOObjectRelease (*usbInterfacep);
130 } 1942 }
1943 IOObjectRelease(interface_iterator);
131 1944
132 if (client->interface == interface) { 1945 return kIOReturnSuccess;
133 return IRECV_E_SUCCESS; 1946}
1947
1948static irecv_error_t iokit_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface)
1949{
1950 IOReturn result;
1951 io_service_t interface_service = IO_OBJECT_NULL;
1952 IOCFPlugInInterface **plugInInterface = NULL;
1953 SInt32 score;
1954
1955 // Close current interface
1956 if (client->usbInterface) {
1957 result = (*client->usbInterface)->USBInterfaceClose(client->usbInterface);
1958 result = (*client->usbInterface)->Release(client->usbInterface);
1959 client->usbInterface = NULL;
134 } 1960 }
135 1961
136 debug("Setting to interface %d:%d\n", interface, alt_interface); 1962 result = iokit_usb_get_interface(client->handle, usb_interface, &interface_service);
137 if (libusb_claim_interface(client->handle, interface) < 0) { 1963 if (result != kIOReturnSuccess) {
1964 debug("failed to find requested interface: %d\n", usb_interface);
138 return IRECV_E_USB_INTERFACE; 1965 return IRECV_E_USB_INTERFACE;
139 } 1966 }
140 1967
141 if (libusb_set_interface_alt_setting(client->handle, interface, alt_interface) < 0) { 1968 result = IOCreatePlugInInterfaceForService(interface_service, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);
1969 IOObjectRelease(interface_service);
1970 if (result != kIOReturnSuccess) {
1971 debug("error creating plug-in interface: %#x\n", result);
142 return IRECV_E_USB_INTERFACE; 1972 return IRECV_E_USB_INTERFACE;
143 } 1973 }
144 1974
145 client->interface = interface; 1975 result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID300), (LPVOID)&client->usbInterface);
146 client->alt_interface = alt_interface; 1976 IODestroyPlugInInterface(plugInInterface);
1977 if (result != kIOReturnSuccess) {
1978 debug("error creating interface interface: %#x\n", result);
1979 return IRECV_E_USB_INTERFACE;
1980 }
1981
1982 result = (*client->usbInterface)->USBInterfaceOpen(client->usbInterface);
1983 if (result != kIOReturnSuccess) {
1984 debug("error opening interface: %#x\n", result);
1985 return IRECV_E_USB_INTERFACE;
1986 }
1987
1988 if (usb_interface == 1) {
1989 result = (*client->usbInterface)->SetAlternateInterface(client->usbInterface, usb_alt_interface);
1990 if (result != kIOReturnSuccess) {
1991 debug("error setting alternate interface: %#x\n", result);
1992 return IRECV_E_USB_INTERFACE;
1993 }
1994 }
1995
147 return IRECV_E_SUCCESS; 1996 return IRECV_E_SUCCESS;
148} 1997}
1998#endif
1999#endif
2000
2001irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface)
2002{
2003#ifdef USE_DUMMY
2004 return IRECV_E_UNSUPPORTED;
2005#else
2006 if (check_context(client) != IRECV_E_SUCCESS)
2007 return IRECV_E_NO_DEVICE;
149 2008
150irecv_error_t irecv_reset(irecv_client_t client) { 2009 debug("Setting to interface %d:%d\n", usb_interface, usb_alt_interface);
151 if (client == NULL || client->handle == NULL) { 2010#ifndef WIN32
2011#ifdef HAVE_IOKIT
2012 if (iokit_usb_set_interface(client, usb_interface, usb_alt_interface) < 0) {
2013 return IRECV_E_USB_INTERFACE;
2014 }
2015#else
2016 if (libusb_claim_interface(client->handle, usb_interface) < 0) {
2017 return IRECV_E_USB_INTERFACE;
2018 }
2019
2020 if (usb_interface == 1) {
2021 if (libusb_set_interface_alt_setting(client->handle, usb_interface, usb_alt_interface) < 0) {
2022 return IRECV_E_USB_INTERFACE;
2023 }
2024 }
2025#endif
2026#else
2027 if (usb_interface == 1) {
2028 if (irecv_usb_control_transfer(client, 0, 0x0B, usb_alt_interface, usb_interface, NULL, 0, USB_TIMEOUT) < 0) {
2029 return IRECV_E_USB_INTERFACE;
2030 }
2031 }
2032#endif
2033 client->usb_interface = usb_interface;
2034 client->usb_alt_interface = usb_alt_interface;
2035
2036 return IRECV_E_SUCCESS;
2037#endif
2038}
2039
2040irecv_error_t irecv_reset(irecv_client_t client)
2041{
2042#ifdef USE_DUMMY
2043 return IRECV_E_UNSUPPORTED;
2044#else
2045 if (check_context(client) != IRECV_E_SUCCESS)
152 return IRECV_E_NO_DEVICE; 2046 return IRECV_E_NO_DEVICE;
2047
2048#ifndef WIN32
2049#ifdef HAVE_IOKIT
2050 IOReturn result;
2051
2052 result = (*client->handle)->ResetDevice(client->handle);
2053 if (result != kIOReturnSuccess && result != kIOReturnNotResponding) {
2054 debug("error sending device reset: %#x\n", result);
2055 return IRECV_E_UNKNOWN_ERROR;
153 } 2056 }
154 2057
2058 result = (*client->handle)->USBDeviceReEnumerate(client->handle, 0);
2059 if (result != kIOReturnSuccess && result != kIOReturnNotResponding) {
2060 debug("error re-enumerating device: %#x (ignored)\n", result);
2061 }
2062#else
155 libusb_reset_device(client->handle); 2063 libusb_reset_device(client->handle);
2064#endif
2065#else
2066 DWORD count;
2067 DeviceIoControl(client->handle, 0x22000C, NULL, 0, NULL, 0, &count, NULL);
2068#endif
156 2069
157 return IRECV_E_SUCCESS; 2070 return IRECV_E_SUCCESS;
2071#endif
158} 2072}
159 2073
160irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data) { 2074irecv_error_t irecv_open_with_ecid_and_attempts(irecv_client_t* pclient, uint64_t ecid, int attempts)
2075{
2076#ifdef USE_DUMMY
2077 return IRECV_E_UNSUPPORTED;
2078#else
2079 int i;
2080
2081 for (i = 0; i < attempts; i++) {
2082 if (*pclient) {
2083 irecv_close(*pclient);
2084 *pclient = NULL;
2085 }
2086 if (irecv_open_with_ecid(pclient, ecid) != IRECV_E_SUCCESS) {
2087 debug("Connection failed. Waiting 1 sec before retry.\n");
2088 sleep(1);
2089 } else {
2090 return IRECV_E_SUCCESS;
2091 }
2092 }
2093
2094 return IRECV_E_UNABLE_TO_CONNECT;
2095#endif
2096}
2097
2098irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data)
2099{
2100#ifdef USE_DUMMY
2101 return IRECV_E_UNSUPPORTED;
2102#else
161 switch(type) { 2103 switch(type) {
162 case IRECV_RECEIVED: 2104 case IRECV_RECEIVED:
163 client->received_callback = callback; 2105 client->received_callback = callback;
@@ -165,9 +2107,11 @@ irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type
165 2107
166 case IRECV_PROGRESS: 2108 case IRECV_PROGRESS:
167 client->progress_callback = callback; 2109 client->progress_callback = callback;
2110 break;
168 2111
169 case IRECV_CONNECTED: 2112 case IRECV_CONNECTED:
170 client->connected_callback = callback; 2113 client->connected_callback = callback;
2114 break;
171 2115
172 case IRECV_PRECOMMAND: 2116 case IRECV_PRECOMMAND:
173 client->precommand_callback = callback; 2117 client->precommand_callback = callback;
@@ -179,15 +2123,21 @@ irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type
179 2123
180 case IRECV_DISCONNECTED: 2124 case IRECV_DISCONNECTED:
181 client->disconnected_callback = callback; 2125 client->disconnected_callback = callback;
2126 break;
182 2127
183 default: 2128 default:
184 return IRECV_E_UNKNOWN_ERROR; 2129 return IRECV_E_UNKNOWN_ERROR;
185 } 2130 }
186 2131
187 return IRECV_E_SUCCESS; 2132 return IRECV_E_SUCCESS;
2133#endif
188} 2134}
189 2135
190irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type) { 2136irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type)
2137{
2138#ifdef USE_DUMMY
2139 return IRECV_E_UNSUPPORTED;
2140#else
191 switch(type) { 2141 switch(type) {
192 case IRECV_RECEIVED: 2142 case IRECV_RECEIVED:
193 client->received_callback = NULL; 2143 client->received_callback = NULL;
@@ -195,9 +2145,11 @@ irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type ty
195 2145
196 case IRECV_PROGRESS: 2146 case IRECV_PROGRESS:
197 client->progress_callback = NULL; 2147 client->progress_callback = NULL;
2148 break;
198 2149
199 case IRECV_CONNECTED: 2150 case IRECV_CONNECTED:
200 client->connected_callback = NULL; 2151 client->connected_callback = NULL;
2152 break;
201 2153
202 case IRECV_PRECOMMAND: 2154 case IRECV_PRECOMMAND:
203 client->precommand_callback = NULL; 2155 client->precommand_callback = NULL;
@@ -209,17 +2161,844 @@ irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type ty
209 2161
210 case IRECV_DISCONNECTED: 2162 case IRECV_DISCONNECTED:
211 client->disconnected_callback = NULL; 2163 client->disconnected_callback = NULL;
2164 break;
212 2165
213 default: 2166 default:
214 return IRECV_E_UNKNOWN_ERROR; 2167 return IRECV_E_UNKNOWN_ERROR;
215 } 2168 }
216 2169
217 return IRECV_E_SUCCESS; 2170 return IRECV_E_SUCCESS;
2171#endif
2172}
2173
2174#ifndef USE_DUMMY
2175struct irecv_device_event_context {
2176 irecv_device_event_cb_t callback;
2177 void *user_data;
2178};
2179
2180struct irecv_usb_device_info {
2181 struct irecv_device_info device_info;
2182 enum irecv_mode mode;
2183 uint32_t location;
2184 int alive;
2185};
2186
2187#ifdef WIN32
2188struct irecv_win_dev_ctx {
2189 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details;
2190 uint32_t location;
2191};
2192#else
2193#ifdef HAVE_IOKIT
2194struct irecv_iokit_dev_ctx {
2195 io_service_t device;
2196 IOUSBDeviceInterface **dev;
2197};
2198#endif
2199#endif
2200
2201static int _irecv_is_recovery_device(void *device)
2202{
2203 uint16_t vendor_id = 0;
2204 uint16_t product_id = 0;
2205#ifdef WIN32
2206 const char *path = (const char*)device;
2207 unsigned int vendor = 0;
2208 unsigned int product = 0;
2209 if (sscanf(path, "\\usb#vid_%04x&pid_%04x#", &vendor, &product) != 2) {
2210 return 0;
2211 }
2212 vendor_id = (uint16_t)vendor;
2213 product_id = (uint16_t)product;
2214#else
2215#ifdef HAVE_IOKIT
2216 kern_return_t kr;
2217 IOUSBDeviceInterface **dev = device;
2218 kr = (*dev)->GetDeviceVendor(dev, &vendor_id);
2219 if (kr != kIOReturnSuccess) {
2220 debug("%s: Failed to get vendor id\n", __func__);
2221 return 0;
2222 }
2223 kr = (*dev)->GetDeviceProduct(dev, &product_id);
2224 if (kr != kIOReturnSuccess) {
2225 debug("%s: Failed to get product id\n", __func__);
2226 return 0;
2227 }
2228#else
2229 libusb_device *device_ = (libusb_device*)device;
2230 struct libusb_device_descriptor devdesc;
2231 int libusb_error;
2232
2233 libusb_error = libusb_get_device_descriptor(device_, &devdesc);
2234 if (libusb_error != 0) {
2235 debug("%s: failed to get device descriptor: %s\n", __func__, libusb_error_name(libusb_error));
2236 return 0;
2237 }
2238 vendor_id = devdesc.idVendor;
2239 product_id = devdesc.idProduct;
2240#endif
2241#endif
2242
2243 if (vendor_id != APPLE_VENDOR_ID) {
2244 return 0;
2245 }
2246
2247 switch (product_id) {
2248 case IRECV_K_DFU_MODE:
2249 case IRECV_K_WTF_MODE:
2250 case IRECV_K_RECOVERY_MODE_1:
2251 case IRECV_K_RECOVERY_MODE_2:
2252 case IRECV_K_RECOVERY_MODE_3:
2253 case IRECV_K_RECOVERY_MODE_4:
2254 case IRECV_K_PORT_DFU_MODE:
2255 case KIS_PRODUCT_ID:
2256 break;
2257 default:
2258 return 0;
2259 }
2260 return 1;
2261}
2262
2263static void* _irecv_handle_device_add(void *userdata)
2264{
2265 struct irecv_client_private client_loc;
2266 char serial_str[256];
2267 uint32_t location = 0;
2268 uint16_t product_id = 0;
2269 irecv_error_t error = 0;
2270 irecv_client_t client = NULL;
2271
2272 memset(serial_str, 0, 256);
2273#ifdef WIN32
2274 struct irecv_win_dev_ctx *win_ctx = (struct irecv_win_dev_ctx*)userdata;
2275 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details = win_ctx->details;
2276 LPSTR result = (LPSTR)details->DevicePath;
2277 location = win_ctx->location;
2278
2279 unsigned int pid = 0;
2280
2281 if (strncmp(result, "\\\\?\\kis#", 8) == 0) {
2282 pid = KIS_PRODUCT_ID;
2283 } else {
2284 char *p = result;
2285 while ((p = strstr(p, "\\usb"))) {
2286 if (sscanf(p, "\\usb#vid_05ac&pid_%04x#%s", &pid, serial_str) == 2)
2287 break;
2288 p += 4;
2289 }
2290
2291 if (serial_str[0] == '\0') {
2292 debug("%s: ERROR: failed to parse DevicePath?!\n", __func__);
2293 return NULL;
2294 }
2295
2296 if (!_irecv_is_recovery_device(p)) {
2297 return NULL;
2298 }
2299 }
2300
2301 product_id = (uint16_t)pid;
2302
2303 if (product_id == KIS_PRODUCT_ID) {
2304 client = (irecv_client_t)malloc(sizeof(struct irecv_client_private));
2305 client->handle = CreateFileA(result, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
2306 if (client->handle == INVALID_HANDLE_VALUE) {
2307 debug("%s: Failed to open device path %s\n", __func__, result);
2308 free(client);
2309 return NULL;
2310 }
2311 client->mode = pid;
2312 } else {
2313 char* p = strchr(serial_str, '#');
2314 if (p) {
2315 *p = '\0';
2316 }
2317
2318 unsigned int j;
2319 for (j = 0; j < strlen(serial_str); j++) {
2320 if (serial_str[j] == '_') {
2321 serial_str[j] = ' ';
2322 } else {
2323 serial_str[j] = toupper(serial_str[j]);
2324 }
2325 }
2326 }
2327
2328#else /* !WIN32 */
2329#ifdef HAVE_IOKIT
2330 struct irecv_iokit_dev_ctx* iokit_ctx = (struct irecv_iokit_dev_ctx*)userdata;
2331 io_service_t device = iokit_ctx->device;
2332 IOUSBDeviceInterface **dev = iokit_ctx->dev;
2333
2334 if (!device) {
2335 debug("%s: ERROR: no device?!\n", __func__);
2336 return NULL;
2337 }
2338 if (!dev) {
2339 debug("%s: ERROR: no device interface?!\n", __func__);
2340 return NULL;
2341 }
2342
2343 (*dev)->GetDeviceProduct(dev, &product_id);
2344 if (!product_id) {
2345 debug("%s: ERROR: could not get product id?!\n", __func__);
2346 return NULL;
2347 }
2348 CFNumberRef locationNum = (CFNumberRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0);
2349 if (locationNum) {
2350 CFNumberGetValue(locationNum, kCFNumberSInt32Type, &location);
2351 CFRelease(locationNum);
2352 }
2353 if (!location) {
2354 debug("%s: ERROR: could not get locationID?!\n", __func__);
2355 return NULL;
2356 }
2357
2358 if (product_id == KIS_PRODUCT_ID) {
2359 IOObjectRetain(device);
2360
2361 error = iokit_usb_open_service(&client, device);
2362 if (error != IRECV_E_SUCCESS) {
2363 debug("%s: ERROR: could not open KIS device!\n", __func__);
2364 return NULL;
2365 }
2366
2367 product_id = client->mode;
2368 } else {
2369 CFStringRef serialString = (CFStringRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
2370 if (serialString) {
2371 CFStringGetCString(serialString, serial_str, sizeof(serial_str), kCFStringEncodingUTF8);
2372 CFRelease(serialString);
2373 }
2374 }
2375#else /* !HAVE_IOKIT */
2376 libusb_device *device = (libusb_device*)userdata;
2377 struct libusb_device_descriptor devdesc;
2378 struct libusb_device_handle* usb_handle = NULL;
2379 int libusb_error;
2380
2381 libusb_error = libusb_get_device_descriptor(device, &devdesc);
2382 if (libusb_error != 0) {
2383 debug("%s: ERROR: failed to get device descriptor: %s\n", __func__, libusb_error_name(libusb_error));
2384 return NULL;
2385 }
2386 product_id = devdesc.idProduct;
2387
2388 uint8_t bus = libusb_get_bus_number(device);
2389 uint8_t address = libusb_get_device_address(device);
2390 location = (bus << 16) | address;
2391
2392 libusb_error = libusb_open(device, &usb_handle);
2393 if (usb_handle == NULL || libusb_error != 0) {
2394 debug("%s: ERROR: can't connect to device: %s\n", __func__, libusb_error_name(libusb_error));
2395 libusb_close(usb_handle);
2396 return 0;
2397 }
2398
2399 if (product_id == KIS_PRODUCT_ID) {
2400 error = libusb_usb_open_handle_with_descriptor_and_ecid(&client, usb_handle, &devdesc, 0);
2401 if (error != IRECV_E_SUCCESS) {
2402 debug("%s: ERROR: could not open KIS device!\n", __func__);
2403 return NULL;
2404 }
2405
2406 product_id = client->mode;
2407 } else {
2408 libusb_error = libusb_get_string_descriptor_ascii(usb_handle, devdesc.iSerialNumber, (unsigned char*)serial_str, 255);
2409 if (libusb_error < 0) {
2410 debug("%s: Failed to get string descriptor: %s\n", __func__, libusb_error_name(libusb_error));
2411 return 0;
2412 }
2413 libusb_close(usb_handle);
2414 }
2415#endif /* !HAVE_IOKIT */
2416#endif /* !WIN32 */
2417 memset(&client_loc, '\0', sizeof(client_loc));
2418 if (product_id == KIS_PRODUCT_ID) {
2419 error = irecv_usb_set_configuration(client, 1);
2420 if (error != IRECV_E_SUCCESS) {
2421 debug("Failed to set configuration, error %d\n", error);
2422 irecv_close(client);
2423 return NULL;
2424 }
2425
2426 error = irecv_usb_set_interface(client, 0, 0);
2427 if (error != IRECV_E_SUCCESS) {
2428 debug("Failed to set interface, error %d\n", error);
2429 irecv_close(client);
2430 return NULL;
2431 }
2432
2433 error = irecv_kis_init(client);
2434 if (error != IRECV_E_SUCCESS) {
2435 debug("irecv_kis_init failed, error %d\n", error);
2436 irecv_close(client);
2437 return NULL;
2438 }
2439
2440 error = irecv_kis_load_device_info(client);
2441 if (error != IRECV_E_SUCCESS) {
2442 debug("irecv_kis_load_device_info failed, error %d\n", error);
2443 irecv_close(client);
2444 return NULL;
2445 }
2446 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)client->device_info.ecid);
2447 strncpy(serial_str, client->device_info.serial_string, 255);
2448 product_id = client->mode;
2449 client_loc.isKIS = 1;
2450 }
2451 if (client) {
2452 irecv_close(client);
2453 }
2454
2455 client_loc.mode = product_id;
2456 irecv_load_device_info_from_iboot_string(&client_loc, serial_str);
2457
2458 struct irecv_usb_device_info *usb_dev_info = (struct irecv_usb_device_info*)malloc(sizeof(struct irecv_usb_device_info));
2459 memcpy(&(usb_dev_info->device_info), &(client_loc.device_info), sizeof(struct irecv_device_info));
2460 usb_dev_info->location = location;
2461 usb_dev_info->alive = 1;
2462 usb_dev_info->mode = client_loc.mode;
2463
2464 collection_add(&devices, usb_dev_info);
2465
2466 irecv_device_event_t dev_event;
2467 dev_event.type = IRECV_DEVICE_ADD;
2468 dev_event.mode = client_loc.mode;
2469 dev_event.device_info = &(usb_dev_info->device_info);
2470
2471 mutex_lock(&listener_mutex);
2472 FOREACH(struct irecv_device_event_context* context, &listeners) {
2473 context->callback(&dev_event, context->user_data);
2474 } ENDFOREACH
2475 mutex_unlock(&listener_mutex);
2476
2477 return NULL;
2478}
2479
2480static void _irecv_handle_device_remove(struct irecv_usb_device_info *devinfo)
2481{
2482 irecv_device_event_t dev_event;
2483 dev_event.type = IRECV_DEVICE_REMOVE;
2484 dev_event.mode = 0;
2485 dev_event.device_info = &(devinfo->device_info);
2486 mutex_lock(&listener_mutex);
2487 FOREACH(struct irecv_device_event_context* context, &listeners) {
2488 context->callback(&dev_event, context->user_data);
2489 } ENDFOREACH
2490 mutex_unlock(&listener_mutex);
2491 free(devinfo->device_info.srnm);
2492 devinfo->device_info.srnm = NULL;
2493 free(devinfo->device_info.imei);
2494 devinfo->device_info.imei = NULL;
2495 free(devinfo->device_info.srtg);
2496 devinfo->device_info.srtg = NULL;
2497 free(devinfo->device_info.serial_string);
2498 devinfo->device_info.serial_string = NULL;
2499 devinfo->alive = 0;
2500 collection_remove(&devices, devinfo);
2501 free(devinfo);
2502}
2503
2504#ifndef WIN32
2505#ifdef HAVE_IOKIT
2506static void iokit_device_added(void *refcon, io_iterator_t iterator)
2507{
2508 kern_return_t kr;
2509 io_service_t device;
2510 IOCFPlugInInterface **plugInInterface = NULL;
2511 IOUSBDeviceInterface **dev = NULL;
2512 HRESULT result;
2513 SInt32 score;
2514
2515 while ((device = IOIteratorNext(iterator))) {
2516 kr = IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);
2517 if ((kIOReturnSuccess != kr) || !plugInInterface) {
2518 debug("%s: ERROR: Unable to create a plug-in (%08x)\n", __func__, kr);
2519 IOObjectRelease(device);
2520 continue;
2521 }
2522 result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320), (LPVOID *)&dev);
2523 (*plugInInterface)->Release(plugInInterface);
2524
2525 if (result || !dev) {
2526 debug("%s: ERROR: Couldn't create a device interface (%08x)\n", __func__, (int)result);
2527 IOObjectRelease(device);
2528 continue;
2529 }
2530
2531 if (!_irecv_is_recovery_device(dev)) {
2532 (void) (*dev)->Release(dev);
2533 IOObjectRelease(device);
2534 continue;
2535 }
2536
2537 struct irecv_iokit_dev_ctx idev;
2538 idev.device = device;
2539 idev.dev = dev;
2540 _irecv_handle_device_add(&idev);
2541 (void) (*dev)->Release(dev);
2542 IOObjectRelease(device);
2543 }
2544}
2545
2546static void iokit_device_removed(void *refcon, io_iterator_t iterator)
2547{
2548 io_service_t device;
2549
2550 while ((device = IOIteratorNext(iterator))) {
2551 uint32_t location = 0;
2552 CFNumberRef locationNum = (CFNumberRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0);
2553 if (locationNum) {
2554 CFNumberGetValue(locationNum, kCFNumberSInt32Type, &location);
2555 CFRelease(locationNum);
2556 }
2557 IOObjectRelease(device);
2558
2559 if (!location) {
2560 continue;
2561 }
2562
2563 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2564 if (devinfo->location == location) {
2565 _irecv_handle_device_remove(devinfo);
2566 break;
2567 }
2568 } ENDFOREACH
2569 }
2570}
2571#else /* !HAVE_IOKIT */
2572#ifdef HAVE_LIBUSB_HOTPLUG_API
2573static int _irecv_usb_hotplug_cb(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data)
2574{
2575 if (!_irecv_is_recovery_device(device)) {
2576 return 0;
2577 }
2578 if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
2579 THREAD_T th_device;
2580 if (thread_new(&th_device, _irecv_handle_device_add, device) != 0) {
2581 debug("%s: FATAL: failed to create thread to handle device add\n", __func__);
2582 return 0;
2583 }
2584 thread_detach(th_device);
2585 } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
2586 uint8_t bus = libusb_get_bus_number(device);
2587 uint8_t address = libusb_get_device_address(device);
2588 uint32_t location = (bus << 16) | address;
2589 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2590 if (devinfo->location == location) {
2591 _irecv_handle_device_remove(devinfo);
2592 break;
2593 }
2594 } ENDFOREACH
2595 }
2596
2597 return 0;
2598}
2599#endif /* HAVE_LIBUSB_HOTPLUG_API */
2600#endif /* !HAVE_IOKIT */
2601#endif /* !WIN32 */
2602
2603struct _irecv_event_handler_info {
2604 cond_t startup_cond;
2605 mutex_t startup_mutex;
2606};
2607
2608static void *_irecv_event_handler(void* data)
2609{
2610 struct _irecv_event_handler_info* info = (struct _irecv_event_handler_info*)data;
2611#ifdef WIN32
2612 struct collection newDevices;
2613 const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_PORTDFU, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
2614 int running = 1;
2615
2616 collection_init(&newDevices);
2617
2618 mutex_lock(&(info->startup_mutex));
2619 cond_signal(&(info->startup_cond));
2620 mutex_unlock(&(info->startup_mutex));
2621
2622 do {
2623 SP_DEVICE_INTERFACE_DATA currentInterface;
2624 HDEVINFO usbDevices;
2625 DWORD i;
2626 int k;
2627
2628 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2629 devinfo->alive = 0;
2630 } ENDFOREACH
2631
2632 for (k = 0; guids[k]; k++) {
2633 usbDevices = SetupDiGetClassDevs(guids[k], NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
2634 if (!usbDevices) {
2635 debug("%s: ERROR: SetupDiGetClassDevs failed\n", __func__);
2636 // cleanup/free newDevices
2637 FOREACH(struct irecv_win_dev_ctx *win_ctx, &newDevices) {
2638 free(win_ctx->details);
2639 collection_remove(&newDevices, win_ctx);
2640 free(win_ctx);
2641 } ENDFOREACH
2642 collection_free(&newDevices);
2643 return NULL;
2644 }
2645
2646
2647 memset(&currentInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA));
2648 currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
2649 for (i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, guids[k], i, &currentInterface); i++) {
2650 DWORD requiredSize = 0;
2651 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details;
2652 SetupDiGetDeviceInterfaceDetail(usbDevices, &currentInterface, NULL, 0, &requiredSize, NULL);
2653 details = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A) malloc(requiredSize);
2654 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
2655 SP_DEVINFO_DATA devinfodata;
2656 devinfodata.cbSize = sizeof(SP_DEVINFO_DATA);
2657 if (!SetupDiGetDeviceInterfaceDetailA(usbDevices, &currentInterface, details, requiredSize, NULL, &devinfodata)) {
2658 free(details);
2659 continue;
2660 }
2661
2662 DWORD sz = REG_SZ;
2663 char driver[256];
2664 driver[0] = '\0';
2665 if (!SetupDiGetDeviceRegistryPropertyA(usbDevices, &devinfodata, SPDRP_DRIVER, &sz, (PBYTE)driver, sizeof(driver), NULL)) {
2666 debug("%s: ERROR: Failed to get driver key\n", __func__);
2667 free(details);
2668 continue;
2669 }
2670
2671 char *p = strrchr(driver, '\\');
2672 if (!p) {
2673 debug("%s: ERROR: Failed to parse device location\n", __func__);
2674 free(details);
2675 continue;
2676 }
2677 p++;
2678 uint32_t location = 0;
2679 if (!*p || strlen(p) < 4) {
2680 debug("%s: ERROR: Driver location suffix too short\n", __func__);
2681 free(details);
2682 continue;
2683 }
2684 memcpy(&location, p, 4);
2685 int found = 0;
2686
2687 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2688 if (devinfo->location == location) {
2689 devinfo->alive = 1;
2690 found = 1;
2691 break;
2692 }
2693 } ENDFOREACH
2694
2695 unsigned int pid = 0;
2696 unsigned int vid = 0;
2697 if (sscanf(details->DevicePath, "\\\\?\\%*3s#vid_%04x&pid_%04x", &vid, &pid)!= 2) {
2698 debug("%s: ERROR: failed to parse VID/PID! path: %s\n", __func__, details->DevicePath);
2699 free(details);
2700 continue;
2701 }
2702 if (vid != APPLE_VENDOR_ID) {
2703 free(details);
2704 continue;
2705 }
2706
2707 // make sure the current device is actually in the right mode for the given driver interface
2708 int skip = 0;
2709 if ((guids[k] == &GUID_DEVINTERFACE_DFU && pid != IRECV_K_DFU_MODE && pid != IRECV_K_WTF_MODE)
2710 || (guids[k] == &GUID_DEVINTERFACE_PORTDFU && pid != IRECV_K_PORT_DFU_MODE)
2711 || (guids[k] == &GUID_DEVINTERFACE_IBOOT && (pid < IRECV_K_RECOVERY_MODE_1 || pid > IRECV_K_RECOVERY_MODE_4))
2712 || (guids[k] == &GUID_DEVINTERFACE_KIS && pid != 1)
2713 ) {
2714 skip = 1;
2715 }
2716
2717 if (!found && !skip) {
2718 // Add device to newDevices list, and deliver the notification later, when removed devices are first handled.
2719 struct irecv_win_dev_ctx *win_ctx = (struct irecv_win_dev_ctx*)malloc(sizeof(struct irecv_win_dev_ctx));
2720 win_ctx->details = details;
2721 win_ctx->location = location;
2722 collection_add(&newDevices, win_ctx);
2723 details = NULL;
2724 }
2725 free(details);
2726 }
2727 SetupDiDestroyDeviceInfoList(usbDevices);
2728 }
2729
2730 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2731 if (!devinfo->alive) {
2732 debug("%s: removed ecid: %016" PRIx64 ", location: %d\n",__func__, (uint64_t)devinfo->device_info.ecid, devinfo->location);
2733 _irecv_handle_device_remove(devinfo);
2734 }
2735 } ENDFOREACH
2736
2737 // handle newly added devices and remove from local list
2738 FOREACH(struct irecv_win_dev_ctx *win_ctx, &newDevices) {
2739 debug("%s: found new: %s, location: %d\n", __func__, win_ctx->details->DevicePath, win_ctx->location);
2740 _irecv_handle_device_add(win_ctx);
2741 free(win_ctx->details);
2742 collection_remove(&newDevices, win_ctx);
2743 free(win_ctx);
2744 } ENDFOREACH
2745
2746 Sleep(500);
2747 mutex_lock(&listener_mutex);
2748 if (collection_count(&listeners) == 0) {
2749 running = 0;
2750 }
2751 mutex_unlock(&listener_mutex);
2752 } while (running);
2753
2754 collection_free(&newDevices);
2755#else /* !WIN32 */
2756#ifdef HAVE_IOKIT
2757 kern_return_t kr;
2758
2759 IONotificationPortRef notifyPort = IONotificationPortCreate(MACH_PORT_NULL);
2760 CFRunLoopSourceRef runLoopSource = IONotificationPortGetRunLoopSource(notifyPort);
2761 iokit_runloop = CFRunLoopGetCurrent();
2762 CFRunLoopAddSource(iokit_runloop, runLoopSource, kCFRunLoopDefaultMode);
2763
2764 uint16_t pids[9] = { IRECV_K_WTF_MODE, IRECV_K_DFU_MODE, IRECV_K_RECOVERY_MODE_1, IRECV_K_RECOVERY_MODE_2, IRECV_K_RECOVERY_MODE_3, IRECV_K_RECOVERY_MODE_4, IRECV_K_PORT_DFU_MODE, KIS_PRODUCT_ID, 0 };
2765 int i = 0;
2766 while (pids[i] > 0) {
2767 CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
2768 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBVendorID), kAppleVendorID);
2769 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBProductID), pids[i]);
2770
2771 matchingDict = (CFMutableDictionaryRef)CFRetain(matchingDict);
2772
2773 io_iterator_t devAddedIter;
2774 kr = IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, iokit_device_added, NULL, &devAddedIter);
2775 if (kr != kIOReturnSuccess) {
2776 debug("%s: Failed to register device add notification callback\n", __func__);
2777 }
2778 iokit_device_added(NULL, devAddedIter);
2779
2780 io_iterator_t devRemovedIter;
2781 kr = IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, iokit_device_removed, NULL, &devRemovedIter);
2782 if (kr != kIOReturnSuccess) {
2783 debug("%s: Failed to register device remove notification callback\n", __func__);
2784 }
2785 iokit_device_removed(NULL, devRemovedIter);
2786
2787 i++;
2788 }
2789
2790 mutex_lock(&(info->startup_mutex));
2791 cond_signal(&(info->startup_cond));
2792 mutex_unlock(&(info->startup_mutex));
2793
2794 CFRunLoopRun();
2795
2796#else /* !HAVE_IOKIT */
2797#ifdef HAVE_LIBUSB_HOTPLUG_API
2798 static libusb_hotplug_callback_handle usb_hotplug_cb_handle;
2799 libusb_hotplug_register_callback(irecv_hotplug_ctx, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, LIBUSB_HOTPLUG_ENUMERATE, APPLE_VENDOR_ID, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, _irecv_usb_hotplug_cb, NULL, &usb_hotplug_cb_handle);
2800 int running = 1;
2801
2802 mutex_lock(&(info->startup_mutex));
2803 cond_signal(&(info->startup_cond));
2804 mutex_unlock(&(info->startup_mutex));
2805
2806 do {
2807 struct timeval tv;
2808 tv.tv_sec = tv.tv_usec = 0;
2809 libusb_handle_events_timeout(irecv_hotplug_ctx, &tv);
2810
2811 mutex_lock(&listener_mutex);
2812 if (collection_count(&listeners) == 0) {
2813 running = 0;
2814 }
2815 mutex_unlock(&listener_mutex);
2816
2817 usleep(100000);
2818 } while (running);
2819 libusb_hotplug_deregister_callback(irecv_hotplug_ctx, usb_hotplug_cb_handle);
2820#else /* !HAVE_LIBUSB_HOTPLUG_API */
2821 int i, cnt;
2822 libusb_device **devs;
2823 int running = 1;
2824
2825 mutex_lock(&(info->startup_mutex));
2826 cond_signal(&(info->startup_cond));
2827 mutex_unlock(&(info->startup_mutex));
2828
2829 do {
2830 cnt = libusb_get_device_list(irecv_hotplug_ctx, &devs);
2831 if (cnt < 0) {
2832 debug("%s: FATAL: Failed to get device list: %s\n", __func__, libusb_error_name(cnt));
2833 return NULL;
2834 }
2835
2836 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2837 devinfo->alive = 0;
2838 } ENDFOREACH
2839
2840 for (i = 0; i < cnt; i++) {
2841 libusb_device *dev = devs[i];
2842 if (!_irecv_is_recovery_device(dev)) {
2843 continue;
2844 }
2845 uint8_t bus = libusb_get_bus_number(dev);
2846 uint8_t address = libusb_get_device_address(dev);
2847 uint32_t location = (bus << 16) | address;
2848 int found = 0;
2849 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2850 if (devinfo->location == location) {
2851 devinfo->alive = 1;
2852 found = 1;
2853 break;
2854 }
2855 } ENDFOREACH
2856 if (!found) {
2857 _irecv_handle_device_add(dev);
2858 }
2859 }
2860
2861 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2862 if (!devinfo->alive) {
2863 _irecv_handle_device_remove(devinfo);
2864 }
2865 } ENDFOREACH
2866
2867 libusb_free_device_list(devs, 1);
2868
2869 mutex_lock(&listener_mutex);
2870 if (collection_count(&listeners) == 0) {
2871 running = 0;
2872 }
2873 mutex_unlock(&listener_mutex);
2874 if (!running)
2875 break;
2876 usleep(500000);
2877 } while (running);
2878#endif /* !HAVE_LIBUSB_HOTPLUG_API */
2879#endif /* !HAVE_IOKIT */
2880#endif /* !WIN32 */
2881 return NULL;
2882}
2883#endif /* !USE_DUMMY */
2884
2885irecv_error_t irecv_device_event_subscribe(irecv_device_event_context_t *context, irecv_device_event_cb_t callback, void *user_data)
2886{
2887#ifdef USE_DUMMY
2888 return IRECV_E_UNSUPPORTED;
2889#else
2890 if (!context || !callback)
2891 return IRECV_E_INVALID_INPUT;
2892
2893 struct irecv_device_event_context* _context = malloc(sizeof(struct irecv_device_event_context));
2894 if (!_context) {
2895 return IRECV_E_OUT_OF_MEMORY;
2896 }
2897
2898 _context->callback = callback;
2899 _context->user_data = user_data;
2900
2901 mutex_lock(&listener_mutex);
2902 collection_add(&listeners, _context);
2903
2904 if (th_event_handler == THREAD_T_NULL || !thread_alive(th_event_handler)) {
2905 mutex_unlock(&listener_mutex);
2906 struct _irecv_event_handler_info info;
2907 cond_init(&info.startup_cond);
2908 mutex_init(&info.startup_mutex);
2909#ifndef WIN32
2910#ifndef HAVE_IOKIT
2911 libusb_init(&irecv_hotplug_ctx);
2912#endif
2913#endif
2914 collection_init(&devices);
2915 mutex_init(&device_mutex);
2916 mutex_lock(&info.startup_mutex);
2917 if (thread_new(&th_event_handler, _irecv_event_handler, &info) == 0) {
2918 cond_wait(&info.startup_cond, &info.startup_mutex);
2919 }
2920 mutex_unlock(&info.startup_mutex);
2921 cond_destroy(&info.startup_cond);
2922 mutex_destroy(&info.startup_mutex);
2923 } else {
2924 /* send DEVICE_ADD events to the new listener */
2925 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2926 if (devinfo && devinfo->alive) {
2927 irecv_device_event_t ev;
2928 ev.type = IRECV_DEVICE_ADD;
2929 ev.mode = devinfo->mode;
2930 ev.device_info = &(devinfo->device_info);
2931 _context->callback(&ev, _context->user_data);
2932 }
2933 } ENDFOREACH
2934 mutex_unlock(&listener_mutex);
2935 }
2936
2937 *context = _context;
2938
2939 return IRECV_E_SUCCESS;
2940#endif
2941}
2942
2943irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t context)
2944{
2945#ifdef USE_DUMMY
2946 return IRECV_E_UNSUPPORTED;
2947#else
2948 if (!context)
2949 return IRECV_E_INVALID_INPUT;
2950
2951 mutex_lock(&listener_mutex);
2952 collection_remove(&listeners, context);
2953 int num = collection_count(&listeners);
2954 mutex_unlock(&listener_mutex);
2955
2956 if (num == 0 && th_event_handler != THREAD_T_NULL && thread_alive(th_event_handler)) {
2957#ifdef HAVE_IOKIT
2958 if (iokit_runloop) {
2959 CFRunLoopStop(iokit_runloop);
2960 iokit_runloop = NULL;
2961 }
2962#endif
2963 thread_join(th_event_handler);
2964 thread_free(th_event_handler);
2965 th_event_handler = THREAD_T_NULL;
2966 mutex_lock(&device_mutex);
2967 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2968 free(devinfo->device_info.srnm);
2969 devinfo->device_info.srnm = NULL;
2970 free(devinfo->device_info.imei);
2971 devinfo->device_info.imei = NULL;
2972 free(devinfo->device_info.srtg);
2973 devinfo->device_info.srtg = NULL;
2974 free(devinfo->device_info.serial_string);
2975 devinfo->device_info.serial_string = NULL;
2976 free(devinfo);
2977 } ENDFOREACH
2978 collection_free(&devices);
2979 mutex_unlock(&device_mutex);
2980 mutex_destroy(&device_mutex);
2981#ifndef WIN32
2982#ifndef HAVE_IOKIT
2983 libusb_exit(irecv_hotplug_ctx);
2984 irecv_hotplug_ctx = NULL;
2985#endif
2986#endif
2987 }
2988
2989 free(context);
2990
2991 return IRECV_E_SUCCESS;
2992#endif
218} 2993}
219 2994
220irecv_error_t irecv_close(irecv_client_t client) { 2995irecv_error_t irecv_close(irecv_client_t client)
2996{
2997#ifdef USE_DUMMY
2998 return IRECV_E_UNSUPPORTED;
2999#else
221 if (client != NULL) { 3000 if (client != NULL) {
222 if(client->disconnected_callback != NULL) { 3001 if (client->disconnected_callback != NULL) {
223 irecv_event_t event; 3002 irecv_event_t event;
224 event.size = 0; 3003 event.size = 0;
225 event.data = NULL; 3004 event.data = NULL;
@@ -227,114 +3006,164 @@ irecv_error_t irecv_close(irecv_client_t client) {
227 event.type = IRECV_DISCONNECTED; 3006 event.type = IRECV_DISCONNECTED;
228 client->disconnected_callback(client, &event); 3007 client->disconnected_callback(client, &event);
229 } 3008 }
230 3009#ifndef WIN32
3010#ifdef HAVE_IOKIT
3011 if (client->usbInterface) {
3012 (*client->usbInterface)->USBInterfaceClose(client->usbInterface);
3013 (*client->usbInterface)->Release(client->usbInterface);
3014 client->usbInterface = NULL;
3015 }
3016 if (client->handle) {
3017 (*client->handle)->USBDeviceClose(client->handle);
3018 (*client->handle)->Release(client->handle);
3019 client->handle = NULL;
3020 }
3021#else
231 if (client->handle != NULL) { 3022 if (client->handle != NULL) {
232 libusb_release_interface(client->handle, client->interface); 3023 if ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE) && (client->isKIS == 0)) {
3024 libusb_release_interface(client->handle, client->usb_interface);
3025 }
233 libusb_close(client->handle); 3026 libusb_close(client->handle);
234 client->handle = NULL; 3027 client->handle = NULL;
235 } 3028 }
236 3029#endif
237 if (libirecovery_context != NULL) { 3030#else
238 libusb_exit(libirecovery_context); 3031 CloseHandle(client->handle);
239 libirecovery_context = NULL; 3032#endif
240 } 3033 free(client->device_info.srnm);
3034 free(client->device_info.imei);
3035 free(client->device_info.srtg);
3036 free(client->device_info.serial_string);
3037 free(client->device_info.ap_nonce);
3038 free(client->device_info.sep_nonce);
241 3039
242 free(client); 3040 free(client);
243 client = NULL; 3041 client = NULL;
244 } 3042 }
245 3043
246 return IRECV_E_SUCCESS; 3044 return IRECV_E_SUCCESS;
3045#endif
247} 3046}
248 3047
249void irecv_set_debug_level(int level) { 3048void irecv_set_debug_level(int level)
3049{
250 libirecovery_debug = level; 3050 libirecovery_debug = level;
251 if(libirecovery_context) { 3051#ifndef USE_DUMMY
252 libusb_set_debug(libirecovery_context, libirecovery_debug); 3052#ifndef WIN32
3053#ifndef HAVE_IOKIT
3054 if (libirecovery_context) {
3055#if LIBUSB_API_VERSION >= 0x01000106
3056 libusb_set_option(libirecovery_context, LIBUSB_OPTION_LOG_LEVEL, libirecovery_debug > 2 ? 1: 0);
3057#else
3058 libusb_set_debug(libirecovery_context, libirecovery_debug > 2 ? 1: 0);
3059#endif
253 } 3060 }
3061#endif
3062#endif
3063#endif
254} 3064}
255 3065
256static irecv_error_t irecv_send_command_raw(irecv_client_t client, unsigned char* command) { 3066const char* irecv_version()
3067{
3068#ifndef PACKAGE_VERSION
3069#error PACKAGE_VERSION is not defined!
3070#endif
3071 return PACKAGE_VERSION;
3072}
3073
3074
3075#ifndef USE_DUMMY
3076static irecv_error_t irecv_send_command_raw(irecv_client_t client, const char* command, uint8_t b_request)
3077{
257 unsigned int length = strlen(command); 3078 unsigned int length = strlen(command);
258 if (length >= 0x100) { 3079 if (length >= 0x100) {
259 length = 0xFF; 3080 return IRECV_E_INVALID_INPUT;
260 } 3081 }
261 3082
262 if (length > 0) { 3083 if (length > 0) {
263 int ret = libusb_control_transfer(client->handle, 0x40, 0, 0, 0, command, length + 1, 100); 3084 irecv_usb_control_transfer(client, 0x40, b_request, 0, 0, (unsigned char*) command, length + 1, USB_TIMEOUT);
264 if ((ret < 0) || (ret != (length + 1))) {
265 if (ret == LIBUSB_ERROR_PIPE)
266 return IRECV_E_PIPE;
267 if (ret == LIBUSB_ERROR_TIMEOUT)
268 return IRECV_E_TIMEOUT;
269 return IRECV_E_UNKNOWN_ERROR;
270 }
271 } 3085 }
272 3086
273 return IRECV_E_SUCCESS; 3087 return IRECV_E_SUCCESS;
274} 3088}
3089#endif
275 3090
276irecv_error_t irecv_send_command(irecv_client_t client, unsigned char* command) { 3091irecv_error_t irecv_send_command_breq(irecv_client_t client, const char* command, uint8_t b_request)
3092{
3093#ifdef USE_DUMMY
3094 return IRECV_E_UNSUPPORTED;
3095#else
277 irecv_error_t error = 0; 3096 irecv_error_t error = 0;
278 3097
279 if (client == NULL || client->handle == NULL) { 3098 if (check_context(client) != IRECV_E_SUCCESS)
280 return IRECV_E_NO_DEVICE; 3099 return IRECV_E_NO_DEVICE;
281 }
282 3100
283 unsigned int length = strlen(command); 3101 unsigned int length = strlen(command);
284 if (length >= 0x100) { 3102 if (length >= 0x100) {
285 length = 0xFF; 3103 return IRECV_E_INVALID_INPUT;
286 } 3104 }
287 3105
288 irecv_event_t event; 3106 irecv_event_t event;
289 if(client->precommand_callback != NULL) { 3107 if (client->precommand_callback != NULL) {
290 event.size = length; 3108 event.size = length;
291 event.data = command; 3109 event.data = command;
292 event.type = IRECV_PRECOMMAND; 3110 event.type = IRECV_PRECOMMAND;
293 if(client->precommand_callback(client, &event)) { 3111 if (client->precommand_callback(client, &event)) {
294 return IRECV_E_SUCCESS; 3112 return IRECV_E_SUCCESS;
295 } 3113 }
296 } 3114 }
297 3115
298 error = irecv_send_command_raw(client, command); 3116 error = irecv_send_command_raw(client, command, b_request);
299 if (error != IRECV_E_SUCCESS) { 3117 if (error != IRECV_E_SUCCESS) {
300 debug("Failed to send command %s\n", command); 3118 debug("Failed to send command %s\n", command);
301 if (error != IRECV_E_PIPE) 3119 if (error != IRECV_E_PIPE)
302 return error; 3120 return error;
303 } 3121 }
304 3122
305 if(client->postcommand_callback != NULL) { 3123 if (client->postcommand_callback != NULL) {
306 event.size = length; 3124 event.size = length;
307 event.data = command; 3125 event.data = command;
308 event.type = IRECV_POSTCOMMAND; 3126 event.type = IRECV_POSTCOMMAND;
309 if(client->postcommand_callback(client, &event)) { 3127 if (client->postcommand_callback(client, &event)) {
310 return IRECV_E_SUCCESS; 3128 return IRECV_E_SUCCESS;
311 } 3129 }
312 } 3130 }
313 3131
314 return IRECV_E_SUCCESS; 3132 return IRECV_E_SUCCESS;
3133#endif
315} 3134}
316 3135
317irecv_error_t irecv_send_file(irecv_client_t client, const char* filename) { 3136irecv_error_t irecv_send_command(irecv_client_t client, const char* command)
318 if (client == NULL || client->handle == NULL) { 3137{
3138 return irecv_send_command_breq(client, command, 0);
3139}
3140
3141irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, unsigned int options)
3142{
3143#ifdef USE_DUMMY
3144 return IRECV_E_UNSUPPORTED;
3145#else
3146 if (check_context(client) != IRECV_E_SUCCESS)
319 return IRECV_E_NO_DEVICE; 3147 return IRECV_E_NO_DEVICE;
320 }
321 3148
322 FILE* file = fopen(filename, "rb"); 3149 FILE* file = fopen(filename, "rb");
323 if (file == NULL) { 3150 if (file == NULL) {
324 return IRECV_E_FILE_NOT_FOUND; 3151 return IRECV_E_FILE_NOT_FOUND;
325 } 3152 }
326 3153
327 fseek(file, 0, SEEK_END); 3154 struct stat fst;
328 long length = ftell(file); 3155 if (fstat(fileno(file), &fst) < 0) {
329 fseek(file, 0, SEEK_SET); 3156 return IRECV_E_UNKNOWN_ERROR;
3157 }
3158 size_t length = fst.st_size;
330 3159
331 unsigned char* buffer = (unsigned char*) malloc(length); 3160 char* buffer = (char*)malloc(length);
332 if (buffer == NULL) { 3161 if (buffer == NULL) {
333 fclose(file); 3162 fclose(file);
334 return IRECV_E_OUT_OF_MEMORY; 3163 return IRECV_E_OUT_OF_MEMORY;
335 } 3164 }
336 3165
337 long bytes = fread(buffer, 1, length, file); 3166 size_t bytes = fread(buffer, 1, length, file);
338 fclose(file); 3167 fclose(file);
339 3168
340 if (bytes != length) { 3169 if (bytes != length) {
@@ -342,53 +3171,179 @@ irecv_error_t irecv_send_file(irecv_client_t client, const char* filename) {
342 return IRECV_E_UNKNOWN_ERROR; 3171 return IRECV_E_UNKNOWN_ERROR;
343 } 3172 }
344 3173
345 irecv_error_t error = irecv_send_buffer(client, buffer, length); 3174 irecv_error_t error = irecv_send_buffer(client, (unsigned char*)buffer, length, options);
346 free(buffer); 3175 free(buffer);
3176
347 return error; 3177 return error;
3178#endif
348} 3179}
349 3180
350irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status) { 3181#ifndef USE_DUMMY
351 if (client == NULL || client->handle == NULL) { 3182static irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status)
3183{
3184 if (check_context(client) != IRECV_E_SUCCESS) {
352 *status = 0; 3185 *status = 0;
353 return IRECV_E_NO_DEVICE; 3186 return IRECV_E_NO_DEVICE;
354 } 3187 }
355 3188
356 unsigned char buffer[6]; 3189 unsigned char buffer[6];
357 memset(buffer, '\0', 6); 3190 memset(buffer, '\0', 6);
358 if (libusb_control_transfer(client->handle, 0xA1, 3, 0, 0, buffer, 6, 1000) != 6) { 3191 if (irecv_usb_control_transfer(client, 0xA1, 3, 0, 0, buffer, 6, USB_TIMEOUT) != 6) {
359 *status = 0; 3192 *status = 0;
360 return IRECV_E_USB_STATUS; 3193 return IRECV_E_USB_STATUS;
361 } 3194 }
362 3195
363 *status = (unsigned int) buffer[4]; 3196 *status = (unsigned int) buffer[4];
3197
3198 return IRECV_E_SUCCESS;
3199}
3200
3201static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, unsigned int options)
3202{
3203 if (client->mode != IRECV_K_DFU_MODE) {
3204 return IRECV_E_UNSUPPORTED;
3205 }
3206
3207 unsigned long origLen = length;
3208
3209 KIS_upload_chunk *chunk = calloc(1, sizeof(KIS_upload_chunk));
3210 uint64_t address = 0;
3211 while (length) {
3212 unsigned long toUpload = length;
3213 if (toUpload > 0x4000)
3214 toUpload = 0x4000;
3215
3216#ifdef WIN32
3217 memcpy(chunk->data, buffer, toUpload);
3218 chunk->size = toUpload;
3219 chunk->address = address;
3220#else
3221 irecv_error_t error = irecv_kis_request_init(&chunk->hdr, KIS_PORTAL_RSM, KIS_INDEX_UPLOAD, 3, toUpload, 0);
3222 if (error != IRECV_E_SUCCESS) {
3223 free(chunk);
3224 debug("Failed to init chunk header, error %d\n", error);
3225 return error;
3226 }
3227
3228 chunk->address = address;
3229 chunk->size = toUpload;
3230 memcpy(chunk->data, buffer, toUpload);
3231#endif
3232
3233#ifdef WIN32
3234 DWORD transferred = 0;
3235 int ret = DeviceIoControl(client->handle, 0x220008, chunk, sizeof(*chunk), NULL, 0, (PDWORD)&transferred, NULL);
3236 irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD;
3237#else
3238 KIS_generic_reply reply;
3239 size_t rcvSize = sizeof(reply);
3240 error = irecv_kis_request(client, &chunk->hdr, sizeof(*chunk) - (0x4000 - toUpload), &reply.hdr, &rcvSize);
3241#endif
3242 if (error != IRECV_E_SUCCESS) {
3243 free(chunk);
3244 debug("Failed to upload chunk, error %d\n", error);
3245 return error;
3246 }
3247
3248 address += toUpload;
3249 buffer += toUpload;
3250 length -= toUpload;
3251
3252 if (client->progress_callback != NULL) {
3253 irecv_event_t event;
3254 event.progress = ((double) (origLen - length) / (double) origLen) * 100.0;
3255 event.type = IRECV_PROGRESS;
3256 event.data = (char*)"Uploading";
3257 event.size = origLen - length;
3258 client->progress_callback(client, &event);
3259 } else {
3260 debug("Sent: %lu bytes - %lu of %lu\n", toUpload, origLen - length, origLen);
3261 }
3262 }
3263 free(chunk);
3264
3265 if (options & IRECV_SEND_OPT_DFU_NOTIFY_FINISH) {
3266#ifdef WIN32
3267 DWORD amount = (DWORD)origLen;
3268 DWORD transferred = 0;
3269 int ret = DeviceIoControl(client->handle, 0x22000C, &amount, 4, NULL, 0, (PDWORD)&transferred, NULL);
3270 irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD;
3271#else
3272 irecv_error_t error = irecv_kis_config_write32(client, KIS_PORTAL_RSM, KIS_INDEX_BOOT_IMG, origLen);
3273#endif
3274 if (error != IRECV_E_SUCCESS) {
3275 debug("Failed to boot image, error %d\n", error);
3276 return error;
3277 }
3278 }
3279
364 return IRECV_E_SUCCESS; 3280 return IRECV_E_SUCCESS;
365} 3281}
3282#endif
3283
3284irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, unsigned int options)
3285{
3286#ifdef USE_DUMMY
3287 return IRECV_E_UNSUPPORTED;
3288#else
3289 if (client->isKIS)
3290 return irecv_kis_send_buffer(client, buffer, length, options);
366 3291
367irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length) {
368 irecv_error_t error = 0; 3292 irecv_error_t error = 0;
369 int recovery_mode = (client->mode != kDfuMode); 3293 int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE));
370 3294
371 if (client == NULL || client->handle == NULL) { 3295 if (check_context(client) != IRECV_E_SUCCESS)
372 return IRECV_E_NO_DEVICE; 3296 return IRECV_E_NO_DEVICE;
373 }
374 3297
375 int packet_size = recovery_mode ? 0x4000: 0x800; 3298 unsigned int h1 = 0xFFFFFFFF;
3299 unsigned char dfu_xbuf[12] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10};
3300 int dfu_crc = 1;
3301 int packet_size = recovery_mode ? 0x8000 : 0x800;
3302 if (!recovery_mode && (options & IRECV_SEND_OPT_DFU_SMALL_PKT)) {
3303 packet_size = 0x40;
3304 dfu_crc = 0;
3305 }
376 int last = length % packet_size; 3306 int last = length % packet_size;
377 int packets = length / packet_size; 3307 int packets = length / packet_size;
3308
378 if (last != 0) { 3309 if (last != 0) {
379 packets++; 3310 packets++;
3311 } else {
3312 last = packet_size;
380 } 3313 }
381 3314
382 /* initiate transfer */ 3315 /* initiate transfer */
383 if (recovery_mode) { 3316 if (recovery_mode) {
384 error = libusb_control_transfer(client->handle, 0x41, 0, 0, 0, NULL, 0, 1000); 3317 error = irecv_usb_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, USB_TIMEOUT);
385 if (error != IRECV_E_SUCCESS) { 3318 } else {
386 return error; 3319 uint8_t state = 0;
3320 if (irecv_usb_control_transfer(client, 0xa1, 5, 0, 0, (unsigned char*)&state, 1, USB_TIMEOUT) == 1) {
3321 error = IRECV_E_SUCCESS;
3322 } else {
3323 return IRECV_E_USB_UPLOAD;
3324 }
3325 switch (state) {
3326 case 2:
3327 /* DFU IDLE */
3328 break;
3329 case 10:
3330 debug("DFU ERROR, issuing CLRSTATUS\n");
3331 irecv_usb_control_transfer(client, 0x21, 4, 0, 0, NULL, 0, USB_TIMEOUT);
3332 error = IRECV_E_USB_UPLOAD;
3333 break;
3334 default:
3335 debug("Unexpected state %d, issuing ABORT\n", state);
3336 irecv_usb_control_transfer(client, 0x21, 6, 0, 0, NULL, 0, USB_TIMEOUT);
3337 error = IRECV_E_USB_UPLOAD;
3338 break;
387 } 3339 }
388 } 3340 }
389 3341
3342 if (error != IRECV_E_SUCCESS) {
3343 return error;
3344 }
3345
390 int i = 0; 3346 int i = 0;
391 double progress = 0;
392 unsigned long count = 0; 3347 unsigned long count = 0;
393 unsigned int status = 0; 3348 unsigned int status = 0;
394 int bytes = 0; 3349 int bytes = 0;
@@ -397,9 +3352,48 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
397 3352
398 /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */ 3353 /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */
399 if (recovery_mode) { 3354 if (recovery_mode) {
400 error = libusb_bulk_transfer(client->handle, 0x04, &buffer[i * packet_size], size, &bytes, 1000); 3355 error = irecv_usb_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT);
401 } else { 3356 } else {
402 bytes = libusb_control_transfer(client->handle, 0x21, 1, 0, 0, &buffer[i * packet_size], size, 1000); 3357 if (dfu_crc) {
3358 int j;
3359 for (j = 0; j < size; j++) {
3360 crc32_step(h1, buffer[i*packet_size + j]);
3361 }
3362 }
3363 if (dfu_crc && i+1 == packets) {
3364 int j;
3365 if (size+16 > packet_size) {
3366 bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, &buffer[i * packet_size], size, USB_TIMEOUT);
3367 if (bytes != size) {
3368 return IRECV_E_USB_UPLOAD;
3369 }
3370 count += size;
3371 size = 0;
3372 }
3373 for (j = 0; j < 2; j++) {
3374 crc32_step(h1, dfu_xbuf[j*6 + 0]);
3375 crc32_step(h1, dfu_xbuf[j*6 + 1]);
3376 crc32_step(h1, dfu_xbuf[j*6 + 2]);
3377 crc32_step(h1, dfu_xbuf[j*6 + 3]);
3378 crc32_step(h1, dfu_xbuf[j*6 + 4]);
3379 crc32_step(h1, dfu_xbuf[j*6 + 5]);
3380 }
3381
3382 char* newbuf = (char*)malloc(size + 16);
3383 if (size > 0) {
3384 memcpy(newbuf, &buffer[i * packet_size], size);
3385 }
3386 memcpy(newbuf+size, dfu_xbuf, 12);
3387 newbuf[size+12] = h1 & 0xFF;
3388 newbuf[size+13] = (h1 >> 8) & 0xFF;
3389 newbuf[size+14] = (h1 >> 16) & 0xFF;
3390 newbuf[size+15] = (h1 >> 24) & 0xFF;
3391 size += 16;
3392 bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, (unsigned char*)newbuf, size, USB_TIMEOUT);
3393 free(newbuf);
3394 } else {
3395 bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, &buffer[i * packet_size], size, USB_TIMEOUT);
3396 }
403 } 3397 }
404 3398
405 if (bytes != size) { 3399 if (bytes != size) {
@@ -415,44 +3409,81 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
415 } 3409 }
416 3410
417 if (!recovery_mode && status != 5) { 3411 if (!recovery_mode && status != 5) {
418 return IRECV_E_USB_UPLOAD; 3412 int retry = 0;
3413
3414 while (retry++ < 20) {
3415 irecv_get_status(client, &status);
3416 if (status == 5) {
3417 break;
3418 }
3419 sleep(1);
3420 }
3421
3422 if (status != 5) {
3423 return IRECV_E_USB_UPLOAD;
3424 }
419 } 3425 }
420 3426
421 count += size; 3427 count += size;
422 if(client->progress_callback != NULL) { 3428 if (client->progress_callback != NULL) {
423 irecv_event_t event; 3429 irecv_event_t event;
424 event.progress = ((double) count/ (double) length) * 100.0; 3430 event.progress = ((double) count/ (double) length) * 100.0;
425 event.type = IRECV_PROGRESS; 3431 event.type = IRECV_PROGRESS;
426 event.data = "Uploading"; 3432 event.data = (char*)"Uploading";
427 event.size = count; 3433 event.size = count;
428 client->progress_callback(client, &event); 3434 client->progress_callback(client, &event);
429 } else { 3435 } else {
430 debug("Sent: %d bytes - %d of %d\n", bytes, count, length); 3436 debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length);
431 } 3437 }
432 } 3438 }
433 3439
434 if (!recovery_mode) { 3440 if (recovery_mode && length % 512 == 0) {
435 libusb_control_transfer(client->handle, 0x21, 1, 0, 0, buffer, 0, 1000); 3441 /* send a ZLP */
436 for (i = 0; i < 3; i++) { 3442 bytes = 0;
3443 irecv_usb_bulk_transfer(client, 0x04, buffer, 0, &bytes, USB_TIMEOUT);
3444 }
3445
3446 if ((options & IRECV_SEND_OPT_DFU_NOTIFY_FINISH) && !recovery_mode) {
3447 irecv_usb_control_transfer(client, 0x21, 1, packets, 0, (unsigned char*) buffer, 0, USB_TIMEOUT);
3448
3449 for (i = 0; i < 2; i++) {
437 error = irecv_get_status(client, &status); 3450 error = irecv_get_status(client, &status);
438 if (error != IRECV_E_SUCCESS) { 3451 if (error != IRECV_E_SUCCESS) {
439 return error; 3452 return error;
440 } 3453 }
441 } 3454 }
3455
3456 if ((options & IRECV_SEND_OPT_DFU_FORCE_ZLP)) {
3457 /* we send a pseudo ZLP here just in case */
3458 irecv_usb_control_transfer(client, 0x21, 1, 0, 0, 0, 0, USB_TIMEOUT);
3459 }
3460
3461 irecv_reset(client);
442 } 3462 }
443 3463
444 return IRECV_E_SUCCESS; 3464 return IRECV_E_SUCCESS;
3465#endif
445} 3466}
446 3467
447irecv_error_t irecv_receive(irecv_client_t client) { 3468irecv_error_t irecv_receive(irecv_client_t client)
448 unsigned char buffer[BUFFER_SIZE]; 3469{
3470#ifdef USE_DUMMY
3471 return IRECV_E_UNSUPPORTED;
3472#else
3473 char buffer[BUFFER_SIZE];
449 memset(buffer, '\0', BUFFER_SIZE); 3474 memset(buffer, '\0', BUFFER_SIZE);
450 if (client == NULL || client->handle == NULL) { 3475
3476 if (check_context(client) != IRECV_E_SUCCESS)
451 return IRECV_E_NO_DEVICE; 3477 return IRECV_E_NO_DEVICE;
452 }
453 3478
454 int bytes = 0; 3479 int bytes = 0;
455 while (libusb_bulk_transfer(client->handle, 0x81, buffer, BUFFER_SIZE, &bytes, 100) == 0) { 3480 while (1) {
3481 irecv_usb_set_interface(client, 1, 1);
3482 int r = irecv_usb_bulk_transfer(client, 0x81, (unsigned char*) buffer, BUFFER_SIZE, &bytes, 500);
3483 irecv_usb_set_interface(client, 0, 0);
3484 if (r != 0) {
3485 break;
3486 }
456 if (bytes > 0) { 3487 if (bytes > 0) {
457 if (client->received_callback != NULL) { 3488 if (client->received_callback != NULL) {
458 irecv_event_t event; 3489 irecv_event_t event;
@@ -460,155 +3491,288 @@ irecv_error_t irecv_receive(irecv_client_t client) {
460 event.data = buffer; 3491 event.data = buffer;
461 event.type = IRECV_RECEIVED; 3492 event.type = IRECV_RECEIVED;
462 if (client->received_callback(client, &event) != 0) { 3493 if (client->received_callback(client, &event) != 0) {
463 return IRECV_E_SUCCESS; 3494 break;
464 } 3495 }
465 } 3496 }
466 } else break; 3497 } else break;
467 } 3498 }
468
469 return IRECV_E_SUCCESS; 3499 return IRECV_E_SUCCESS;
3500#endif
470} 3501}
471 3502
472irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value) { 3503irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value)
3504{
3505#ifdef USE_DUMMY
3506 return IRECV_E_UNSUPPORTED;
3507#else
473 char command[256]; 3508 char command[256];
474 if (client == NULL || client->handle == NULL) { 3509
3510 if (check_context(client) != IRECV_E_SUCCESS)
475 return IRECV_E_NO_DEVICE; 3511 return IRECV_E_NO_DEVICE;
476 }
477 3512
478 *value = NULL; 3513 *value = NULL;
479 3514
480 if(variable == NULL) { 3515 if (variable == NULL) {
481 return IRECV_E_UNKNOWN_ERROR; 3516 return IRECV_E_INVALID_INPUT;
482 } 3517 }
483 3518
484 memset(command, '\0', sizeof(command)); 3519 memset(command, '\0', sizeof(command));
485 snprintf(command, sizeof(command)-1, "getenv %s", variable); 3520 snprintf(command, sizeof(command)-1, "getenv %s", variable);
486 irecv_error_t error = irecv_send_command_raw(client, command); 3521 irecv_error_t error = irecv_send_command_raw(client, command, 0);
487 if(error == IRECV_E_PIPE) 3522 if (error == IRECV_E_PIPE) {
488 return IRECV_E_SUCCESS; 3523 return IRECV_E_SUCCESS;
489 if(error != IRECV_E_SUCCESS) 3524 }
3525
3526 if (error != IRECV_E_SUCCESS) {
490 return error; 3527 return error;
3528 }
491 3529
492 unsigned char* response = (unsigned char*) malloc(256); 3530 char* response = (char*) malloc(256);
493 if (response == NULL) { 3531 if (response == NULL) {
494 return IRECV_E_OUT_OF_MEMORY; 3532 return IRECV_E_OUT_OF_MEMORY;
495 } 3533 }
496 3534
497 memset(response, '\0', 256); 3535 memset(response, '\0', 256);
498 int ret = libusb_control_transfer(client->handle, 0xC0, 0, 0, 0, response, 255, 500); 3536 irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, USB_TIMEOUT);
499 if (ret < 0)
500 return IRECV_E_UNKNOWN_ERROR;
501 3537
502 *value = response; 3538 *value = response;
3539
503 return IRECV_E_SUCCESS; 3540 return IRECV_E_SUCCESS;
3541#endif
504} 3542}
505 3543
506irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid) { 3544irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value)
507 if (client == NULL || client->handle == NULL) { 3545{
3546#ifdef USE_DUMMY
3547 return IRECV_E_UNSUPPORTED;
3548#else
3549 if (check_context(client) != IRECV_E_SUCCESS)
508 return IRECV_E_NO_DEVICE; 3550 return IRECV_E_NO_DEVICE;
509 }
510 3551
511 unsigned char* cpid_string = strstr(client->serial, "CPID:"); 3552 *value = 0;
512 if (cpid_string == NULL) { 3553
513 *cpid = 0; 3554 char* response = (char*) malloc(256);
514 return IRECV_E_UNKNOWN_ERROR; 3555 if (response == NULL) {
3556 return IRECV_E_OUT_OF_MEMORY;
515 } 3557 }
516 sscanf(cpid_string, "CPID:%d", cpid); 3558
3559 memset(response, '\0', 256);
3560 irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, USB_TIMEOUT);
3561
3562 *value = (unsigned int) *response;
517 3563
518 return IRECV_E_SUCCESS; 3564 return IRECV_E_SUCCESS;
3565#endif
519} 3566}
520 3567
521irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid) { 3568irecv_error_t irecv_get_mode(irecv_client_t client, int* mode)
522 if (client == NULL || client->handle == NULL) { 3569{
3570#ifdef USE_DUMMY
3571 return IRECV_E_UNSUPPORTED;
3572#else
3573 if (check_context(client) != IRECV_E_SUCCESS)
523 return IRECV_E_NO_DEVICE; 3574 return IRECV_E_NO_DEVICE;
524 }
525 3575
526 unsigned char* bdid_string = strstr(client->serial, "BDID:"); 3576 *mode = client->mode;
527 if (bdid_string == NULL) {
528 *bdid = 0;
529 return IRECV_E_UNKNOWN_ERROR;
530 }
531 sscanf(bdid_string, "BDID:%d", bdid);
532 3577
533 return IRECV_E_SUCCESS; 3578 return IRECV_E_SUCCESS;
3579#endif
534} 3580}
535 3581
536irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid) { 3582const struct irecv_device_info* irecv_get_device_info(irecv_client_t client)
537 if (client == NULL || client->handle == NULL) { 3583{
538 return IRECV_E_NO_DEVICE; 3584#ifdef USE_DUMMY
539 } 3585 return NULL;
540 3586#else
541 unsigned char* ecid_string = strstr(client->serial, "ECID:"); 3587 if (check_context(client) != IRECV_E_SUCCESS)
542 if (ecid_string == NULL) { 3588 return NULL;
543 *ecid = 0;
544 return IRECV_E_UNKNOWN_ERROR;
545 }
546 sscanf(ecid_string, "ECID:%qX", ecid);
547 3589
548 return IRECV_E_SUCCESS; 3590 return &client->device_info;
3591#endif
549} 3592}
550 3593
551irecv_error_t irecv_send_exploit(irecv_client_t client) { 3594#ifndef USE_DUMMY
552 if (client == NULL || client->handle == NULL) { 3595#ifdef HAVE_IOKIT
3596static void *iokit_limera1n_usb_submit_request(void *argv)
3597{
3598 void **args = argv;
3599 IOUSBDeviceInterface320 **dev = args[0];
3600 IOUSBDevRequest *req = args[1];
3601
3602 IOReturn result = (*dev)->DeviceRequest(dev, req);
3603 if (result != kIOReturnSuccess)
3604 debug("%s result: %#x\n", __func__, result);
3605
3606 return NULL;
3607}
3608#endif
3609#endif
3610
3611irecv_error_t irecv_trigger_limera1n_exploit(irecv_client_t client)
3612{
3613#ifdef USE_DUMMY
3614 return IRECV_E_UNSUPPORTED;
3615#else
3616 if (check_context(client) != IRECV_E_SUCCESS)
553 return IRECV_E_NO_DEVICE; 3617 return IRECV_E_NO_DEVICE;
3618
3619#ifdef HAVE_IOKIT
3620 IOReturn result;
3621 IOUSBDevRequestTO req;
3622 bzero(&req, sizeof(req));
3623
3624 req.bmRequestType = 0x21;
3625 req.bRequest = 2;
3626 req.wValue = 0;
3627 req.wIndex = 0;
3628 req.wLength = 0;
3629 req.pData = NULL;
3630 req.noDataTimeout = USB_TIMEOUT;
3631 req.completionTimeout = USB_TIMEOUT;
3632
3633 // The original version uses an async request, but we don't have an async event
3634 // source set up. The hack relies on aborting the transaction before it times out,
3635 // which can be accomplished by sending on another thread.
3636
3637 void *args[2] = { client->handle, &req };
3638 THREAD_T thread;
3639 thread_new(&thread, iokit_limera1n_usb_submit_request, args);
3640
3641 usleep(5 * 1000);
3642 result = (*client->handle)->USBDeviceAbortPipeZero(client->handle);
3643 if (result != kIOReturnSuccess)
3644 debug("USBDeviceAbortPipeZero returned %#x\n", result);
3645
3646 switch (result) {
3647 case kIOReturnSuccess: return req.wLenDone;
3648 case kIOReturnTimeout: return IRECV_E_TIMEOUT;
3649 case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT;
3650 case kIOReturnNotResponding: return IRECV_E_NO_DEVICE;
3651 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
3652 default:
3653 return IRECV_E_UNKNOWN_ERROR;
554 } 3654 }
3655#else
3656 irecv_usb_control_transfer(client, 0x21, 2, 0, 0, NULL, 0, USB_TIMEOUT);
3657#endif
555 3658
556 libusb_control_transfer(client->handle, 0x21, 2, 0, 0, NULL, 0, 100);
557 return IRECV_E_SUCCESS; 3659 return IRECV_E_SUCCESS;
3660#endif
558} 3661}
559 3662
560irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename) { 3663irecv_error_t irecv_execute_script(irecv_client_t client, const char* script)
3664{
3665#ifdef USE_DUMMY
3666 return IRECV_E_UNSUPPORTED;
3667#else
561 irecv_error_t error = IRECV_E_SUCCESS; 3668 irecv_error_t error = IRECV_E_SUCCESS;
562 if (client == NULL || client->handle == NULL) { 3669 if (check_context(client) != IRECV_E_SUCCESS)
563 return IRECV_E_NO_DEVICE; 3670 return IRECV_E_NO_DEVICE;
564 }
565 3671
566 int file_size = 0; 3672 char* body = strdup(script);
567 char* file_data = NULL; 3673 char* line = strtok(body, "\n");
568 if(irecv_read_file(filename, &file_data, &file_size) < 0) {
569 return IRECV_E_FILE_NOT_FOUND;
570 }
571 3674
572 char* line = strtok(file_data, "\n"); 3675 while (line != NULL) {
573 while(line != NULL) { 3676 if (line[0] != '#') {
574 if(line[0] != '#') {
575 error = irecv_send_command(client, line); 3677 error = irecv_send_command(client, line);
576 if(error != IRECV_E_SUCCESS) { 3678 if (error != IRECV_E_SUCCESS) {
577 return error; 3679 break;
578 } 3680 }
579 3681
580 error = irecv_receive(client); 3682 error = irecv_receive(client);
581 if(error != IRECV_E_SUCCESS) { 3683 if (error != IRECV_E_SUCCESS) {
582 return error; 3684 break;
583 } 3685 }
584 } 3686 }
585 line = strtok(NULL, "\n"); 3687 line = strtok(NULL, "\n");
586 } 3688 }
587 3689
3690 free(body);
3691
3692 return error;
3693#endif
3694}
3695
3696irecv_error_t irecv_saveenv(irecv_client_t client)
3697{
3698#ifdef USE_DUMMY
3699 return IRECV_E_UNSUPPORTED;
3700#else
3701 irecv_error_t error = irecv_send_command_raw(client, "saveenv", 0);
3702 if (error != IRECV_E_SUCCESS) {
3703 return error;
3704 }
3705
588 return IRECV_E_SUCCESS; 3706 return IRECV_E_SUCCESS;
3707#endif
589} 3708}
590 3709
591irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value) { 3710irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value)
3711{
3712#ifdef USE_DUMMY
3713 return IRECV_E_UNSUPPORTED;
3714#else
592 char command[256]; 3715 char command[256];
593 if (client == NULL || client->handle == NULL) { 3716
3717 if (check_context(client) != IRECV_E_SUCCESS)
594 return IRECV_E_NO_DEVICE; 3718 return IRECV_E_NO_DEVICE;
595 }
596 3719
597 if(variable == NULL || value == NULL) { 3720 if (variable == NULL || value == NULL) {
598 return IRECV_E_UNKNOWN_ERROR; 3721 return IRECV_E_UNKNOWN_ERROR;
599 } 3722 }
600 3723
601 memset(command, '\0', sizeof(command)); 3724 memset(command, '\0', sizeof(command));
602 snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value); 3725 snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value);
603 irecv_error_t error = irecv_send_command_raw(client, command); 3726 irecv_error_t error = irecv_send_command_raw(client, command, 0);
604 if(error != IRECV_E_SUCCESS) { 3727 if (error != IRECV_E_SUCCESS) {
3728 return error;
3729 }
3730
3731 return IRECV_E_SUCCESS;
3732#endif
3733}
3734
3735irecv_error_t irecv_setenv_np(irecv_client_t client, const char* variable, const char* value)
3736{
3737#ifdef USE_DUMMY
3738 return IRECV_E_UNSUPPORTED;
3739#else
3740 char command[256];
3741
3742 if (check_context(client) != IRECV_E_SUCCESS)
3743 return IRECV_E_NO_DEVICE;
3744
3745 if (variable == NULL || value == NULL) {
3746 return IRECV_E_UNKNOWN_ERROR;
3747 }
3748
3749 memset(command, '\0', sizeof(command));
3750 snprintf(command, sizeof(command)-1, "setenvnp %s %s", variable, value);
3751 irecv_error_t error = irecv_send_command_raw(client, command, 0);
3752 if (error != IRECV_E_SUCCESS) {
3753 return error;
3754 }
3755
3756 return IRECV_E_SUCCESS;
3757#endif
3758}
3759
3760irecv_error_t irecv_reboot(irecv_client_t client)
3761{
3762#ifdef USE_DUMMY
3763 return IRECV_E_UNSUPPORTED;
3764#else
3765 irecv_error_t error = irecv_send_command_raw(client, "reboot", 0);
3766 if (error != IRECV_E_SUCCESS) {
605 return error; 3767 return error;
606 } 3768 }
607 3769
608 return IRECV_E_SUCCESS; 3770 return IRECV_E_SUCCESS;
3771#endif
609} 3772}
610 3773
611const char* irecv_strerror(irecv_error_t error) { 3774const char* irecv_strerror(irecv_error_t error)
3775{
612 switch (error) { 3776 switch (error) {
613 case IRECV_E_SUCCESS: 3777 case IRECV_E_SUCCESS:
614 return "Command completed successfully"; 3778 return "Command completed successfully";
@@ -646,6 +3810,9 @@ const char* irecv_strerror(irecv_error_t error) {
646 case IRECV_E_TIMEOUT: 3810 case IRECV_E_TIMEOUT:
647 return "Timeout talking to device"; 3811 return "Timeout talking to device";
648 3812
3813 case IRECV_E_UNSUPPORTED:
3814 return "Operation unsupported by driver";
3815
649 default: 3816 default:
650 return "Unknown error"; 3817 return "Unknown error";
651 } 3818 }
@@ -653,64 +3820,215 @@ const char* irecv_strerror(irecv_error_t error) {
653 return NULL; 3820 return NULL;
654} 3821}
655 3822
656int irecv_write_file(const char* filename, const void* data, size_t size) { 3823irecv_error_t irecv_reset_counters(irecv_client_t client)
657 size_t bytes = 0; 3824{
658 FILE* file = NULL; 3825#ifdef USE_DUMMY
3826 return IRECV_E_UNSUPPORTED;
3827#else
3828 if (check_context(client) != IRECV_E_SUCCESS)
3829 return IRECV_E_NO_DEVICE;
659 3830
660 debug("Writing data to %s\n", filename); 3831 if ((client->mode == IRECV_K_DFU_MODE) || (client->mode == IRECV_K_PORT_DFU_MODE) || (client->mode == IRECV_K_WTF_MODE)) {
661 file = fopen(filename, "wb"); 3832 irecv_usb_control_transfer(client, 0x21, 4, 0, 0, 0, 0, USB_TIMEOUT);
662 if (file == NULL) {
663 error("read_file: Unable to open file %s\n", filename);
664 return -1;
665 } 3833 }
666 3834
667 bytes = fwrite(data, 1, size, file); 3835 return IRECV_E_SUCCESS;
668 fclose(file); 3836#endif
3837}
669 3838
670 if (bytes != size) { 3839irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length)
671 error("ERROR: Unable to write entire file: %s: %d of %d\n", filename, bytes, size); 3840{
672 return -1; 3841#ifdef USE_DUMMY
3842 return IRECV_E_UNSUPPORTED;
3843#else
3844 int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE));
3845
3846 if (check_context(client) != IRECV_E_SUCCESS)
3847 return IRECV_E_NO_DEVICE;
3848
3849 int packet_size = recovery_mode ? 0x2000: 0x800;
3850 int last = length % packet_size;
3851 int packets = length / packet_size;
3852 if (last != 0) {
3853 packets++;
3854 } else {
3855 last = packet_size;
673 } 3856 }
674 3857
675 return size; 3858 int i = 0;
3859 int bytes = 0;
3860 unsigned long count = 0;
3861 for (i = 0; i < packets; i++) {
3862 unsigned short size = (i+1) < packets ? packet_size : last;
3863 bytes = irecv_usb_control_transfer(client, 0xA1, 2, 0, 0, (unsigned char*)&buffer[i * packet_size], size, USB_TIMEOUT);
3864
3865 if (bytes != size) {
3866 return IRECV_E_USB_UPLOAD;
3867 }
3868
3869 count += size;
3870 if (client->progress_callback != NULL) {
3871 irecv_event_t event;
3872 event.progress = ((double) count/ (double) length) * 100.0;
3873 event.type = IRECV_PROGRESS;
3874 event.data = (char*)"Downloading";
3875 event.size = count;
3876 client->progress_callback(client, &event);
3877 } else {
3878 debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length);
3879 }
3880 }
3881
3882 return IRECV_E_SUCCESS;
3883#endif
676} 3884}
677 3885
678int irecv_read_file(const char* filename, char** data, uint32_t* size) { 3886irecv_error_t irecv_finish_transfer(irecv_client_t client)
679 size_t bytes = 0; 3887{
680 size_t length = 0; 3888#ifdef USE_DUMMY
681 FILE* file = NULL; 3889 return IRECV_E_UNSUPPORTED;
682 char* buffer = NULL; 3890#else
683 debug("Reading data from %s\n", filename); 3891 int i = 0;
3892 unsigned int status = 0;
684 3893
685 *size = 0; 3894 if (check_context(client) != IRECV_E_SUCCESS)
686 *data = NULL; 3895 return IRECV_E_NO_DEVICE;
687 3896
688 file = fopen(filename, "rb"); 3897 irecv_usb_control_transfer(client, 0x21, 1, 0, 0, 0, 0, USB_TIMEOUT);
689 if (file == NULL) { 3898
690 error("read_file: File %s not found\n", filename); 3899 for (i = 0; i < 3; i++){
691 return -1; 3900 irecv_get_status(client, &status);
692 } 3901 }
693 3902
694 fseek(file, 0, SEEK_END); 3903 irecv_reset(client);
695 length = ftell(file);
696 rewind(file);
697 3904
698 buffer = (char*) malloc(length); 3905 return IRECV_E_SUCCESS;
699 if(buffer == NULL) { 3906#endif
700 error("ERROR: Out of memory\n"); 3907}
701 fclose(file); 3908
702 return -1; 3909irecv_device_t irecv_devices_get_all(void)
3910{
3911 return irecv_devices;
3912}
3913
3914irecv_error_t irecv_devices_get_device_by_client(irecv_client_t client, irecv_device_t* device)
3915{
3916#ifdef USE_DUMMY
3917 return IRECV_E_UNSUPPORTED;
3918#else
3919 int i = 0;
3920
3921 if (!client || !device)
3922 return IRECV_E_INVALID_INPUT;
3923
3924 *device = NULL;
3925
3926 if (client->device_info.cpid == 0) {
3927 return IRECV_E_UNKNOWN_ERROR;
703 } 3928 }
704 bytes = fread(buffer, 1, length, file);
705 fclose(file);
706 3929
707 if(bytes != length) { 3930 unsigned int cpid_match = client->device_info.cpid;
708 error("ERROR: Unable to read entire file\n"); 3931 unsigned int bdid_match = client->device_info.bdid;
709 free(buffer); 3932 if (client->mode == IRECV_K_PORT_DFU_MODE) {
710 return -1; 3933 cpid_match = (client->device_info.bdid >> 8) & 0xFFFF;
3934 bdid_match = (client->device_info.bdid >> 24) & 0xFF;
711 } 3935 }
712 3936
713 *size = length; 3937 for (i = 0; irecv_devices[i].hardware_model != NULL; i++) {
714 *data = buffer; 3938 if (irecv_devices[i].chip_id == cpid_match && irecv_devices[i].board_id == bdid_match) {
715 return 0; 3939 *device = &irecv_devices[i];
3940 return IRECV_E_SUCCESS;
3941 }
3942 }
3943
3944 return IRECV_E_NO_DEVICE;
3945#endif
3946}
3947
3948irecv_error_t irecv_devices_get_device_by_product_type(const char* product_type, irecv_device_t* device)
3949{
3950 int i = 0;
3951
3952 if (!product_type || !device)
3953 return IRECV_E_INVALID_INPUT;
3954
3955 *device = NULL;
3956
3957 for (i = 0; irecv_devices[i].product_type != NULL; i++) {
3958 if (!strcmp(product_type, irecv_devices[i].product_type)) {
3959 *device = &irecv_devices[i];
3960 return IRECV_E_SUCCESS;
3961 }
3962 }
3963
3964 return IRECV_E_NO_DEVICE;
3965}
3966
3967irecv_error_t irecv_devices_get_device_by_hardware_model(const char* hardware_model, irecv_device_t* device)
3968{
3969 int i = 0;
3970
3971 if (!hardware_model || !device)
3972 return IRECV_E_INVALID_INPUT;
3973
3974 *device = NULL;
3975
3976 for (i = 0; irecv_devices[i].hardware_model != NULL; i++) {
3977 if (!strcasecmp(hardware_model, irecv_devices[i].hardware_model)) {
3978 *device = &irecv_devices[i];
3979 return IRECV_E_SUCCESS;
3980 }
3981 }
3982
3983 return IRECV_E_NO_DEVICE;
3984}
3985
3986irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause)
3987{
3988#ifdef USE_DUMMY
3989 return NULL;
3990#else
3991 irecv_error_t error = 0;
3992 irecv_client_t new_client = NULL;
3993 irecv_event_cb_t progress_callback = client->progress_callback;
3994 irecv_event_cb_t received_callback = client->received_callback;
3995 irecv_event_cb_t connected_callback = client->connected_callback;
3996 irecv_event_cb_t precommand_callback = client->precommand_callback;
3997 irecv_event_cb_t postcommand_callback = client->postcommand_callback;
3998 irecv_event_cb_t disconnected_callback = client->disconnected_callback;
3999
4000 uint64_t ecid = client->device_info.ecid;
4001
4002 if (check_context(client) == IRECV_E_SUCCESS) {
4003 irecv_close(client);
4004 }
4005
4006 if (initial_pause > 0) {
4007 debug("Waiting %d seconds for the device to pop up...\n", initial_pause);
4008 sleep(initial_pause);
4009 }
4010
4011 error = irecv_open_with_ecid_and_attempts(&new_client, ecid, 10);
4012 if (error != IRECV_E_SUCCESS) {
4013 return NULL;
4014 }
4015
4016 new_client->progress_callback = progress_callback;
4017 new_client->received_callback = received_callback;
4018 new_client->connected_callback = connected_callback;
4019 new_client->precommand_callback = precommand_callback;
4020 new_client->postcommand_callback = postcommand_callback;
4021 new_client->disconnected_callback = disconnected_callback;
4022
4023 if (new_client->connected_callback != NULL) {
4024 irecv_event_t event;
4025 event.size = 0;
4026 event.data = NULL;
4027 event.progress = 0;
4028 event.type = IRECV_CONNECTED;
4029 new_client->connected_callback(new_client, &event);
4030 }
4031
4032 return new_client;
4033#endif
716} 4034}
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 0000000..ebb085c
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,13 @@
1if BUILD_TOOLS
2AM_CPPFLAGS = -I$(top_srcdir)/include
3
4AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusb_CFLAGS)
5AM_LDFLAGS = $(libusb_LIBS) -lreadline
6
7bin_PROGRAMS = irecovery
8
9irecovery_SOURCES = irecovery.c
10irecovery_CFLAGS = $(AM_CFLAGS)
11irecovery_LDFLAGS = $(AM_LDFLAGS)
12irecovery_LDADD = $(top_builddir)/src/libirecovery-1.0.la
13endif
diff --git a/tools/irecovery.c b/tools/irecovery.c
new file mode 100644
index 0000000..61d053a
--- /dev/null
+++ b/tools/irecovery.c
@@ -0,0 +1,707 @@
1/*
2 * irecovery.c
3 * Software frontend for iBoot/iBSS communication with iOS devices
4 *
5 * Copyright (c) 2012-2023 Nikias Bassen <nikias@gmx.li>
6 * Copyright (c) 2012-2015 Martin Szulecki <martin.szulecki@libimobiledevice.org>
7 * Copyright (c) 2010-2011 Chronic-Dev Team
8 * Copyright (c) 2010-2011 Joshua Hill
9 * Copyright (c) 2008-2011 Nicolas Haunold
10 *
11 * All rights reserved. This program and the accompanying materials
12 * are made available under the terms of the GNU Lesser General Public License
13 * (LGPL) version 2.1 which accompanies this distribution, and is available at
14 * http://www.gnu.org/licenses/lgpl-2.1.html
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 */
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
25
26#define TOOL_NAME "irecovery"
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include <string.h>
32#include <getopt.h>
33#include <inttypes.h>
34#include <libirecovery.h>
35#include <readline/readline.h>
36#include <readline/history.h>
37
38#ifdef WIN32
39#include <windows.h>
40#ifndef sleep
41#define sleep(n) Sleep(1000 * n)
42#endif
43#endif
44
45#define FILE_HISTORY_PATH ".irecovery"
46#define debug(...) if (verbose) fprintf(stderr, __VA_ARGS__)
47
48enum {
49 kNoAction,
50 kResetDevice,
51 kStartShell,
52 kSendCommand,
53 kSendFile,
54 kSendExploit,
55 kSendScript,
56 kShowMode,
57 kRebootToNormalMode,
58 kQueryInfo,
59 kListDevices
60};
61
62static unsigned int quit = 0;
63static unsigned int verbose = 0;
64
65void print_progress_bar(double progress);
66int received_cb(irecv_client_t client, const irecv_event_t* event);
67int progress_cb(irecv_client_t client, const irecv_event_t* event);
68int precommand_cb(irecv_client_t client, const irecv_event_t* event);
69int postcommand_cb(irecv_client_t client, const irecv_event_t* event);
70
71static void shell_usage()
72{
73 printf("Usage:\n");
74 printf(" /upload FILE\t\tsend FILE to device\n");
75 printf(" /limera1n [FILE]\trun limera1n exploit and send optional payload from FILE\n");
76 printf(" /deviceinfo\t\tprint device information (ECID, IMEI, etc.)\n");
77 printf(" /help\t\t\tshow this help\n");
78 printf(" /exit\t\t\texit interactive shell\n");
79}
80
81static const char* mode_to_str(int mode)
82{
83 switch (mode) {
84 case IRECV_K_RECOVERY_MODE_1:
85 case IRECV_K_RECOVERY_MODE_2:
86 case IRECV_K_RECOVERY_MODE_3:
87 case IRECV_K_RECOVERY_MODE_4:
88 return "Recovery";
89 break;
90 case IRECV_K_DFU_MODE:
91 return "DFU";
92 break;
93 case IRECV_K_PORT_DFU_MODE:
94 return "Port DFU";
95 break;
96 case IRECV_K_WTF_MODE:
97 return "WTF";
98 break;
99 default:
100 return "Unknown";
101 break;
102 }
103}
104
105static void buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length)
106{
107 FILE *f;
108 uint64_t size;
109
110 *length = 0;
111
112 f = fopen(filename, "rb");
113 if (!f) {
114 return;
115 }
116
117 fseek(f, 0, SEEK_END);
118 size = ftell(f);
119 rewind(f);
120
121 if (size == 0) {
122 fclose(f);
123 return;
124 }
125
126 *buffer = (char*)malloc(sizeof(char)*(size+1));
127 fread(*buffer, sizeof(char), size, f);
128 fclose(f);
129
130 *length = size;
131}
132
133static void print_hex(unsigned char *buf, size_t len)
134{
135 size_t i;
136 for (i = 0; i < len; i++) {
137 printf("%02x", buf[i]);
138 }
139}
140
141static void print_device_info(irecv_client_t client)
142{
143 int ret, mode;
144 irecv_device_t device = NULL;
145 const struct irecv_device_info *devinfo = irecv_get_device_info(client);
146 if (devinfo) {
147 printf("CPID: 0x%04x\n", devinfo->cpid);
148 printf("CPRV: 0x%02x\n", devinfo->cprv);
149 printf("BDID: 0x%02x\n", devinfo->bdid);
150 printf("ECID: 0x%016" PRIx64 "\n", devinfo->ecid);
151 printf("CPFM: 0x%02x\n", devinfo->cpfm);
152 printf("SCEP: 0x%02x\n", devinfo->scep);
153 printf("IBFL: 0x%02x\n", devinfo->ibfl);
154 printf("SRTG: %s\n", (devinfo->srtg) ? devinfo->srtg : "N/A");
155 printf("SRNM: %s\n", (devinfo->srnm) ? devinfo->srnm : "N/A");
156 printf("IMEI: %s\n", (devinfo->imei) ? devinfo->imei : "N/A");
157 printf("NONC: ");
158 if (devinfo->ap_nonce) {
159 print_hex(devinfo->ap_nonce, devinfo->ap_nonce_size);
160 } else {
161 printf("N/A");
162 }
163 printf("\n");
164 printf("SNON: ");
165 if (devinfo->sep_nonce) {
166 print_hex(devinfo->sep_nonce, devinfo->sep_nonce_size);
167 } else {
168 printf("N/A");
169 }
170 printf("\n");
171 char* p = strstr(devinfo->serial_string, "PWND:[");
172 if (p) {
173 p+=6;
174 char* pend = strchr(p, ']');
175 if (pend) {
176 printf("PWND: %.*s\n", (int)(pend-p), p);
177 }
178 }
179 } else {
180 printf("Could not get device info?!\n");
181 }
182
183 ret = irecv_get_mode(client, &mode);
184 if (ret == IRECV_E_SUCCESS) {
185 switch (devinfo->pid) {
186 case 0x1881:
187 printf("MODE: DFU via Debug USB (KIS)\n");
188 break;
189 default:
190 printf("MODE: %s\n", mode_to_str(mode));
191 break;
192 }
193 }
194
195 irecv_devices_get_device_by_client(client, &device);
196 if (device) {
197 printf("PRODUCT: %s\n", device->product_type);
198 printf("MODEL: %s\n", device->hardware_model);
199 printf("NAME: %s\n", device->display_name);
200 }
201}
202
203static void print_devices()
204{
205 struct irecv_device *devices = irecv_devices_get_all();
206 struct irecv_device *device = NULL;
207 int i = 0;
208
209 for (i = 0; devices[i].product_type != NULL; i++) {
210 device = &devices[i];
211
212 printf("%s %s 0x%02x 0x%04x %s\n", device->product_type, device->hardware_model, device->board_id, device->chip_id, device->display_name);
213 }
214}
215
216static int _is_breq_command(const char* cmd)
217{
218 return (
219 !strcmp(cmd, "go")
220 || !strcmp(cmd, "bootx")
221 || !strcmp(cmd, "reboot")
222 || !strcmp(cmd, "memboot")
223 );
224}
225
226static void parse_command(irecv_client_t client, unsigned char* command, unsigned int size)
227{
228 char* cmd = strdup((char*)command);
229 char* action = strtok(cmd, " ");
230
231 if (!strcmp(cmd, "/exit")) {
232 quit = 1;
233 } else if (!strcmp(cmd, "/help")) {
234 shell_usage();
235 } else if (!strcmp(cmd, "/upload")) {
236 char* filename = strtok(NULL, " ");
237 debug("Uploading file %s\n", filename);
238 if (filename != NULL) {
239 irecv_send_file(client, filename, 0);
240 }
241 } else if (!strcmp(cmd, "/deviceinfo")) {
242 print_device_info(client);
243 } else if (!strcmp(cmd, "/limera1n")) {
244 char* filename = strtok(NULL, " ");
245 debug("Sending limera1n payload %s\n", filename);
246 if (filename != NULL) {
247 irecv_send_file(client, filename, 0);
248 }
249 irecv_trigger_limera1n_exploit(client);
250 } else if (!strcmp(cmd, "/execute")) {
251 char* filename = strtok(NULL, " ");
252 debug("Executing script %s\n", filename);
253 if (filename != NULL) {
254 char* buffer = NULL;
255 uint64_t buffer_length = 0;
256 buffer_read_from_filename(filename, &buffer, &buffer_length);
257 if (buffer) {
258 buffer[buffer_length] = '\0';
259 irecv_execute_script(client, buffer);
260 free(buffer);
261 } else {
262 printf("Could not read file '%s'\n", filename);
263 }
264 }
265 } else {
266 printf("Unsupported command %s. Use /help to get a list of available commands.\n", cmd);
267 }
268
269 free(action);
270}
271
272static void load_command_history()
273{
274 read_history(FILE_HISTORY_PATH);
275}
276
277static void append_command_to_history(char* cmd)
278{
279 add_history(cmd);
280 write_history(FILE_HISTORY_PATH);
281}
282
283static void init_shell(irecv_client_t client)
284{
285 irecv_error_t error = 0;
286 load_command_history();
287 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
288 irecv_event_subscribe(client, IRECV_RECEIVED, &received_cb, NULL);
289 irecv_event_subscribe(client, IRECV_PRECOMMAND, &precommand_cb, NULL);
290 irecv_event_subscribe(client, IRECV_POSTCOMMAND, &postcommand_cb, NULL);
291 while (!quit) {
292 error = irecv_receive(client);
293 if (error != IRECV_E_SUCCESS) {
294 debug("%s\n", irecv_strerror(error));
295 break;
296 }
297
298 char* cmd = readline("> ");
299 if (cmd && *cmd) {
300 if (_is_breq_command(cmd)) {
301 error = irecv_send_command_breq(client, cmd, 1);
302 } else {
303 error = irecv_send_command(client, cmd);
304 }
305 if (error != IRECV_E_SUCCESS) {
306 quit = 1;
307 }
308
309 append_command_to_history(cmd);
310 free(cmd);
311 }
312 }
313}
314
315int received_cb(irecv_client_t client, const irecv_event_t* event)
316{
317 if (event->type == IRECV_RECEIVED) {
318 int i = 0;
319 int size = event->size;
320 const char* data = event->data;
321 for (i = 0; i < size; i++) {
322 printf("%c", data[i]);
323 }
324 }
325
326 return 0;
327}
328
329int precommand_cb(irecv_client_t client, const irecv_event_t* event)
330{
331 if (event->type == IRECV_PRECOMMAND) {
332 if (event->data[0] == '/') {
333 parse_command(client, (unsigned char*)event->data, event->size);
334 return -1;
335 }
336 }
337
338 return 0;
339}
340
341int postcommand_cb(irecv_client_t client, const irecv_event_t* event)
342{
343 char* value = NULL;
344 char* action = NULL;
345 char* command = NULL;
346 char* argument = NULL;
347 irecv_error_t error = IRECV_E_SUCCESS;
348
349 if (event->type == IRECV_POSTCOMMAND) {
350 command = strdup(event->data);
351 action = strtok(command, " ");
352 if (!strcmp(action, "getenv")) {
353 argument = strtok(NULL, " ");
354 error = irecv_getenv(client, argument, &value);
355 if (error != IRECV_E_SUCCESS) {
356 debug("%s\n", irecv_strerror(error));
357 free(command);
358 return error;
359 }
360 printf("%s\n", value);
361 free(value);
362 }
363
364 if (!strcmp(action, "reboot")) {
365 quit = 1;
366 }
367 }
368
369 free(command);
370
371 return 0;
372}
373
374int progress_cb(irecv_client_t client, const irecv_event_t* event)
375{
376 if (event->type == IRECV_PROGRESS) {
377 print_progress_bar(event->progress);
378 }
379
380 return 0;
381}
382
383void print_progress_bar(double progress)
384{
385 int i = 0;
386
387 if (progress < 0) {
388 return;
389 }
390
391 if (progress > 100) {
392 progress = 100;
393 }
394
395 printf("\r[");
396
397 for (i = 0; i < 50; i++) {
398 if (i < progress / 2) {
399 printf("=");
400 } else {
401 printf(" ");
402 }
403 }
404
405 printf("] %3.1f%%", progress);
406
407 fflush(stdout);
408
409 if (progress == 100) {
410 printf("\n");
411 }
412}
413
414static void print_usage(int argc, char **argv)
415{
416 char *name = NULL;
417 name = strrchr(argv[0], '/');
418 printf("Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
419 printf("\n");
420 printf("Interact with an iOS device in DFU or recovery mode.\n");
421 printf("\n");
422 printf("OPTIONS:\n");
423 printf(" -i, --ecid ECID\tconnect to specific device by its ECID\n");
424 printf(" -c, --command CMD\trun CMD on device\n");
425 printf(" -m, --mode\t\tprint current device mode\n");
426 printf(" -f, --file FILE\tsend file to device\n");
427 printf(" -k, --payload FILE\tsend limera1n usb exploit payload from FILE\n");
428 printf(" -r, --reset\t\treset client\n");
429 printf(" -n, --normal\t\treboot device into normal mode (exit recovery loop)\n");
430 printf(" -e, --script FILE\texecutes recovery script from FILE\n");
431 printf(" -s, --shell\t\tstart an interactive shell\n");
432 printf(" -q, --query\t\tquery device info\n");
433 printf(" -a, --devices\t\tlist information for all known devices\n");
434 printf(" -v, --verbose\t\tenable verbose output, repeat for higher verbosity\n");
435 printf(" -h, --help\t\tprints this usage information\n");
436 printf(" -V, --version\t\tprints version information\n");
437 printf("\n");
438 printf("Homepage: <" PACKAGE_URL ">\n");
439 printf("Bug Reports: <" PACKAGE_BUGREPORT ">\n");
440}
441
442int main(int argc, char* argv[])
443{
444 static struct option longopts[] = {
445 { "ecid", required_argument, NULL, 'i' },
446 { "command", required_argument, NULL, 'c' },
447 { "mode", no_argument, NULL, 'm' },
448 { "file", required_argument, NULL, 'f' },
449 { "payload", required_argument, NULL, 'k' },
450 { "reset", no_argument, NULL, 'r' },
451 { "normal", no_argument, NULL, 'n' },
452 { "script", required_argument, NULL, 'e' },
453 { "shell", no_argument, NULL, 's' },
454 { "query", no_argument, NULL, 'q' },
455 { "devices", no_argument, NULL, 'a' },
456 { "verbose", no_argument, NULL, 'v' },
457 { "help", no_argument, NULL, 'h' },
458 { "version", no_argument, NULL, 'V' },
459 { NULL, 0, NULL, 0 }
460 };
461 int i = 0;
462 int opt = 0;
463 int action = kNoAction;
464 uint64_t ecid = 0;
465 int mode = -1;
466 char* argument = NULL;
467 irecv_error_t error = 0;
468
469 char* buffer = NULL;
470 uint64_t buffer_length = 0;
471
472 if (argc == 1) {
473 print_usage(argc, argv);
474 return 0;
475 }
476
477 while ((opt = getopt_long(argc, argv, "i:vVhrsmnc:f:e:k:qa", longopts, NULL)) > 0) {
478 switch (opt) {
479 case 'i':
480 if (optarg) {
481 char* tail = NULL;
482 ecid = strtoull(optarg, &tail, 0);
483 if (tail && (tail[0] != '\0')) {
484 ecid = 0;
485 }
486 if (ecid == 0) {
487 fprintf(stderr, "ERROR: Could not parse ECID from argument '%s'\n", optarg);
488 return -1;
489 }
490 }
491 break;
492
493 case 'v':
494 verbose += 1;
495 break;
496
497 case 'h':
498 print_usage(argc, argv);
499 return 0;
500
501 case 'm':
502 action = kShowMode;
503 break;
504
505 case 'n':
506 action = kRebootToNormalMode;
507 break;
508
509 case 'r':
510 action = kResetDevice;
511 break;
512
513 case 's':
514 action = kStartShell;
515 break;
516
517 case 'f':
518 action = kSendFile;
519 argument = optarg;
520 break;
521
522 case 'c':
523 action = kSendCommand;
524 argument = optarg;
525 break;
526
527 case 'k':
528 action = kSendExploit;
529 argument = optarg;
530 break;
531
532 case 'e':
533 action = kSendScript;
534 argument = optarg;
535 break;
536
537 case 'q':
538 action = kQueryInfo;
539 break;
540
541 case 'a':
542 action = kListDevices;
543 print_devices();
544 return 0;
545
546 case 'V':
547 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
548 return 0;
549
550 default:
551 fprintf(stderr, "Unknown argument\n");
552 return -1;
553 }
554 }
555
556 if (action == kNoAction) {
557 fprintf(stderr, "ERROR: Missing action option\n");
558 print_usage(argc, argv);
559 return -1;
560 }
561
562 if (verbose)
563 irecv_set_debug_level(verbose);
564
565 irecv_client_t client = NULL;
566 for (i = 0; i <= 5; i++) {
567 debug("Attempting to connect... \n");
568
569 irecv_error_t err = irecv_open_with_ecid(&client, ecid);
570 if (err == IRECV_E_UNSUPPORTED) {
571 fprintf(stderr, "ERROR: %s\n", irecv_strerror(err));
572 return -1;
573 }
574 else if (err != IRECV_E_SUCCESS)
575 sleep(1);
576 else
577 break;
578
579 if (i == 5) {
580 fprintf(stderr, "ERROR: %s\n", irecv_strerror(err));
581 return -1;
582 }
583 }
584
585 irecv_device_t device = NULL;
586 irecv_devices_get_device_by_client(client, &device);
587 if (device)
588 debug("Connected to %s, model %s, cpid 0x%04x, bdid 0x%02x\n", device->product_type, device->hardware_model, device->chip_id, device->board_id);
589
590 const struct irecv_device_info *devinfo = irecv_get_device_info(client);
591
592 switch (action) {
593 case kResetDevice:
594 irecv_reset(client);
595 break;
596
597 case kSendFile:
598 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
599 error = irecv_send_file(client, argument, IRECV_SEND_OPT_DFU_NOTIFY_FINISH);
600 debug("%s\n", irecv_strerror(error));
601 break;
602
603 case kSendCommand:
604 if (devinfo->pid == 0x1881) {
605 printf("Shell is not available in Debug USB (KIS) mode.\n");
606 break;
607 }
608 if (_is_breq_command(argument)) {
609 error = irecv_send_command_breq(client, argument, 1);
610 } else {
611 error = irecv_send_command(client, argument);
612 }
613 debug("%s\n", irecv_strerror(error));
614 break;
615
616 case kSendExploit:
617 if (devinfo->pid == 0x1881) {
618 printf("Shell is not available in Debug USB (KIS) mode.\n");
619 break;
620 }
621 if (argument != NULL) {
622 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
623 error = irecv_send_file(client, argument, 0);
624 if (error != IRECV_E_SUCCESS) {
625 debug("%s\n", irecv_strerror(error));
626 break;
627 }
628 }
629 error = irecv_trigger_limera1n_exploit(client);
630 debug("%s\n", irecv_strerror(error));
631 break;
632
633 case kStartShell:
634 if (devinfo->pid == 0x1881) {
635 printf("This feature is not supported in Debug USB (KIS) mode.\n");
636 break;
637 }
638 init_shell(client);
639 break;
640
641 case kSendScript:
642 if (devinfo->pid == 0x1881) {
643 printf("This feature is not supported in Debug USB (KIS) mode.\n");
644 break;
645 }
646 buffer_read_from_filename(argument, &buffer, &buffer_length);
647 if (buffer) {
648 buffer[buffer_length] = '\0';
649
650 error = irecv_execute_script(client, buffer);
651 if (error != IRECV_E_SUCCESS) {
652 debug("%s\n", irecv_strerror(error));
653 }
654
655 free(buffer);
656 } else {
657 fprintf(stderr, "Could not read file '%s'\n", argument);
658 }
659 break;
660
661 case kShowMode: {
662 irecv_get_mode(client, &mode);
663 printf("%s Mode", mode_to_str(mode));
664 if (devinfo->pid == 0x1881) {
665 printf(" via Debug USB (KIS)");
666 }
667 printf("\n");
668 break;
669 }
670 case kRebootToNormalMode:
671 if (devinfo->pid == 0x1881) {
672 printf("This feature is not supported in Debug USB (KIS) mode.\n");
673 break;
674 }
675 error = irecv_setenv(client, "auto-boot", "true");
676 if (error != IRECV_E_SUCCESS) {
677 debug("%s\n", irecv_strerror(error));
678 break;
679 }
680
681 error = irecv_saveenv(client);
682 if (error != IRECV_E_SUCCESS) {
683 debug("%s\n", irecv_strerror(error));
684 break;
685 }
686
687 error = irecv_reboot(client);
688 if (error != IRECV_E_SUCCESS) {
689 debug("%s\n", irecv_strerror(error));
690 } else {
691 debug("%s\n", irecv_strerror(error));
692 }
693 break;
694
695 case kQueryInfo:
696 print_device_info(client);
697 break;
698
699 default:
700 fprintf(stderr, "Unknown action\n");
701 break;
702 }
703
704 irecv_close(client);
705
706 return 0;
707}
diff --git a/udev/39-libirecovery.rules.in b/udev/39-libirecovery.rules.in
new file mode 100644
index 0000000..ea9f93a
--- /dev/null
+++ b/udev/39-libirecovery.rules.in
@@ -0,0 +1,8 @@
1# Handle iOS devices in DFU and Recovery mode - for use with libirecovery
2
3# Change group and permissions of iOS devices in DFU, legacy WTF, and Recovery mode
4ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="05ac", ATTR{idProduct}=="122[27]|128[0-3]", @udev_activation_rule@
5
6# Handle checkra1n DFU mode
7ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="05ac", ATTR{idProduct}=="1338", @udev_activation_rule@
8
diff --git a/udev/Makefile.am b/udev/Makefile.am
new file mode 100644
index 0000000..2a7ad98
--- /dev/null
+++ b/udev/Makefile.am
@@ -0,0 +1,21 @@
1if WITH_UDEV
2edit = \
3 $(SED) -r \
4 -e 's|@udev_activation_rule[@]|$(udev_activation_rule)|g' \
5 < $< > $@ || rm $@
6
7udevrules_DATA = \
8 39-libirecovery.rules
9
1039-libirecovery.rules: 39-libirecovery.rules.in
11 $(edit)
12
13EXTRA_DIST = \
14 39-libirecovery.rules.in
15
16MAINTAINERCLEANFILES = \
17 39-libirecovery.rules
18
19CLEANFILES = \
20 39-libirecovery.rules
21endif