From f87a54f8dfcfb025a71de315b3102290b80fe149 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Mon, 15 Jan 2007 14:49:13 +0000 Subject: [PATCH] Tagged 0.3.0 libburn/cdrskin release --- libburn/tags/ZeroThreeZero/AUTHORS | 5 + libburn/tags/ZeroThreeZero/CONTRIBUTORS | 5 + libburn/tags/ZeroThreeZero/COPYING | 280 + libburn/tags/ZeroThreeZero/COPYRIGHT | 19 + libburn/tags/ZeroThreeZero/ChangeLog | 1 + libburn/tags/ZeroThreeZero/INSTALL | 234 + libburn/tags/ZeroThreeZero/Makefile.am | 198 + libburn/tags/ZeroThreeZero/NEWS | 1 + libburn/tags/ZeroThreeZero/README | 231 + libburn/tags/ZeroThreeZero/acinclude.m4 | 22 + libburn/tags/ZeroThreeZero/bootstrap | 10 + libburn/tags/ZeroThreeZero/cdrskin/README | 496 ++ .../cdrskin/add_ts_changes_to_libburn_0_2_6 | 206 + .../cdrskin/add_ts_changes_to_libburn_0_2_7 | 217 + .../ZeroThreeZero/cdrskin/cdrecord_spy.sh | 37 + libburn/tags/ZeroThreeZero/cdrskin/cdrfifo.c | 1083 +++ libburn/tags/ZeroThreeZero/cdrskin/cdrfifo.h | 156 + libburn/tags/ZeroThreeZero/cdrskin/cdrskin.1 | 703 ++ libburn/tags/ZeroThreeZero/cdrskin/cdrskin.c | 5967 +++++++++++++++++ .../ZeroThreeZero/cdrskin/cdrskin_eng.html | 441 ++ .../ZeroThreeZero/cdrskin/cdrskin_timestamp.h | 1 + .../tags/ZeroThreeZero/cdrskin/changelog.txt | 2519 +++++++ libburn/tags/ZeroThreeZero/cdrskin/cleanup.c | 215 + libburn/tags/ZeroThreeZero/cdrskin/cleanup.h | 34 + .../ZeroThreeZero/cdrskin/compile_cdrskin.sh | 219 + .../cdrskin/convert_man_to_html.sh | 72 + .../cdrskin/doener_150x200_tr.gif | Bin 0 -> 34821 bytes .../cdrskin/doener_150x200_tr_octx.gif | Bin 0 -> 29548 bytes .../ZeroThreeZero/cdrskin/make_timestamp.sh | 9 + .../tags/ZeroThreeZero/cdrskin/wiki_plain.txt | 230 + libburn/tags/ZeroThreeZero/configure.ac | 117 + libburn/tags/ZeroThreeZero/doc/Makefile | 4 + libburn/tags/ZeroThreeZero/doc/comments | 136 + .../tags/ZeroThreeZero/doc/doxygen.conf.in | 186 + libburn/tags/ZeroThreeZero/libburn-1.pc.in | 12 + libburn/tags/ZeroThreeZero/libburn/Makefile | 4 + .../tags/ZeroThreeZero/libburn/Makefile.am | 65 + .../tags/ZeroThreeZero/libburn/asserts.txt | 792 +++ libburn/tags/ZeroThreeZero/libburn/async.c | 365 + libburn/tags/ZeroThreeZero/libburn/async.h | 8 + .../tags/ZeroThreeZero/libburn/back_hacks.h | 54 + libburn/tags/ZeroThreeZero/libburn/cleanup.c | 215 + libburn/tags/ZeroThreeZero/libburn/cleanup.h | 34 + libburn/tags/ZeroThreeZero/libburn/crc.c | 122 + libburn/tags/ZeroThreeZero/libburn/crc.h | 9 + libburn/tags/ZeroThreeZero/libburn/debug.c | 35 + libburn/tags/ZeroThreeZero/libburn/debug.h | 8 + libburn/tags/ZeroThreeZero/libburn/drive.c | 1531 +++++ libburn/tags/ZeroThreeZero/libburn/drive.h | 98 + libburn/tags/ZeroThreeZero/libburn/error.h | 8 + libburn/tags/ZeroThreeZero/libburn/file.c | 187 + libburn/tags/ZeroThreeZero/libburn/file.h | 22 + libburn/tags/ZeroThreeZero/libburn/init.c | 276 + libburn/tags/ZeroThreeZero/libburn/init.h | 8 + libburn/tags/ZeroThreeZero/libburn/lec.c | 451 ++ libburn/tags/ZeroThreeZero/libburn/lec.h | 12 + libburn/tags/ZeroThreeZero/libburn/libburn.h | 1450 ++++ .../ZeroThreeZero/libburn/libdax_audioxtr.c | 326 + .../ZeroThreeZero/libburn/libdax_audioxtr.h | 229 + .../tags/ZeroThreeZero/libburn/libdax_msgs.c | 404 ++ .../tags/ZeroThreeZero/libburn/libdax_msgs.h | 397 ++ libburn/tags/ZeroThreeZero/libburn/mmc.c | 1731 +++++ libburn/tags/ZeroThreeZero/libburn/mmc.h | 72 + libburn/tags/ZeroThreeZero/libburn/null.c | 27 + libburn/tags/ZeroThreeZero/libburn/null.h | 10 + libburn/tags/ZeroThreeZero/libburn/options.c | 219 + libburn/tags/ZeroThreeZero/libburn/options.h | 87 + .../tags/ZeroThreeZero/libburn/os-freebsd.h | 73 + libburn/tags/ZeroThreeZero/libburn/os-linux.h | 64 + libburn/tags/ZeroThreeZero/libburn/os.h | 34 + libburn/tags/ZeroThreeZero/libburn/read.c | 282 + libburn/tags/ZeroThreeZero/libburn/read.h | 14 + libburn/tags/ZeroThreeZero/libburn/sbc.c | 65 + libburn/tags/ZeroThreeZero/libburn/sbc.h | 18 + libburn/tags/ZeroThreeZero/libburn/sector.c | 840 +++ libburn/tags/ZeroThreeZero/libburn/sector.h | 35 + .../ZeroThreeZero/libburn/sg-freebsd-port.c | 559 ++ .../tags/ZeroThreeZero/libburn/sg-freebsd.c | 697 ++ libburn/tags/ZeroThreeZero/libburn/sg-linux.c | 1104 +++ libburn/tags/ZeroThreeZero/libburn/sg.c | 17 + libburn/tags/ZeroThreeZero/libburn/sg.h | 34 + libburn/tags/ZeroThreeZero/libburn/source.c | 39 + libburn/tags/ZeroThreeZero/libburn/source.h | 8 + libburn/tags/ZeroThreeZero/libburn/spc.c | 752 +++ libburn/tags/ZeroThreeZero/libburn/spc.h | 50 + .../tags/ZeroThreeZero/libburn/structure.c | 424 ++ .../tags/ZeroThreeZero/libburn/structure.h | 92 + libburn/tags/ZeroThreeZero/libburn/toc.c | 136 + libburn/tags/ZeroThreeZero/libburn/toc.h | 48 + .../tags/ZeroThreeZero/libburn/transport.h | 266 + libburn/tags/ZeroThreeZero/libburn/util.c | 53 + libburn/tags/ZeroThreeZero/libburn/util.h | 8 + libburn/tags/ZeroThreeZero/libburn/write.c | 1350 ++++ libburn/tags/ZeroThreeZero/libburn/write.h | 35 + libburn/tags/ZeroThreeZero/test/Makefile | 4 + libburn/tags/ZeroThreeZero/test/dewav.c | 215 + libburn/tags/ZeroThreeZero/test/fake_au.c | 164 + libburn/tags/ZeroThreeZero/test/libburner.c | 718 ++ libburn/tags/ZeroThreeZero/test/poll.c | 78 + libburn/tags/ZeroThreeZero/test/structest.c | 48 + libburn/tags/ZeroThreeZero/test/telltoc.c | 684 ++ libburn/tags/ZeroThreeZero/version.h.in | 3 + 102 files changed, 32333 insertions(+) create mode 100644 libburn/tags/ZeroThreeZero/AUTHORS create mode 100644 libburn/tags/ZeroThreeZero/CONTRIBUTORS create mode 100644 libburn/tags/ZeroThreeZero/COPYING create mode 100644 libburn/tags/ZeroThreeZero/COPYRIGHT create mode 100644 libburn/tags/ZeroThreeZero/ChangeLog create mode 100644 libburn/tags/ZeroThreeZero/INSTALL create mode 100644 libburn/tags/ZeroThreeZero/Makefile.am create mode 100644 libburn/tags/ZeroThreeZero/NEWS create mode 100644 libburn/tags/ZeroThreeZero/README create mode 100644 libburn/tags/ZeroThreeZero/acinclude.m4 create mode 100755 libburn/tags/ZeroThreeZero/bootstrap create mode 100644 libburn/tags/ZeroThreeZero/cdrskin/README create mode 100755 libburn/tags/ZeroThreeZero/cdrskin/add_ts_changes_to_libburn_0_2_6 create mode 100755 libburn/tags/ZeroThreeZero/cdrskin/add_ts_changes_to_libburn_0_2_7 create mode 100755 libburn/tags/ZeroThreeZero/cdrskin/cdrecord_spy.sh create mode 100644 libburn/tags/ZeroThreeZero/cdrskin/cdrfifo.c create mode 100644 libburn/tags/ZeroThreeZero/cdrskin/cdrfifo.h create mode 100644 libburn/tags/ZeroThreeZero/cdrskin/cdrskin.1 create mode 100644 libburn/tags/ZeroThreeZero/cdrskin/cdrskin.c create mode 100644 libburn/tags/ZeroThreeZero/cdrskin/cdrskin_eng.html create mode 100644 libburn/tags/ZeroThreeZero/cdrskin/cdrskin_timestamp.h create mode 100644 libburn/tags/ZeroThreeZero/cdrskin/changelog.txt create mode 100644 libburn/tags/ZeroThreeZero/cdrskin/cleanup.c create mode 100644 libburn/tags/ZeroThreeZero/cdrskin/cleanup.h create mode 100755 libburn/tags/ZeroThreeZero/cdrskin/compile_cdrskin.sh create mode 100755 libburn/tags/ZeroThreeZero/cdrskin/convert_man_to_html.sh create mode 100644 libburn/tags/ZeroThreeZero/cdrskin/doener_150x200_tr.gif create mode 100644 libburn/tags/ZeroThreeZero/cdrskin/doener_150x200_tr_octx.gif create mode 100755 libburn/tags/ZeroThreeZero/cdrskin/make_timestamp.sh create mode 100644 libburn/tags/ZeroThreeZero/cdrskin/wiki_plain.txt create mode 100644 libburn/tags/ZeroThreeZero/configure.ac create mode 100644 libburn/tags/ZeroThreeZero/doc/Makefile create mode 100644 libburn/tags/ZeroThreeZero/doc/comments create mode 100644 libburn/tags/ZeroThreeZero/doc/doxygen.conf.in create mode 100644 libburn/tags/ZeroThreeZero/libburn-1.pc.in create mode 100644 libburn/tags/ZeroThreeZero/libburn/Makefile create mode 100644 libburn/tags/ZeroThreeZero/libburn/Makefile.am create mode 100644 libburn/tags/ZeroThreeZero/libburn/asserts.txt create mode 100644 libburn/tags/ZeroThreeZero/libburn/async.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/async.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/back_hacks.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/cleanup.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/cleanup.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/crc.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/crc.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/debug.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/debug.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/drive.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/drive.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/error.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/file.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/file.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/init.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/init.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/lec.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/lec.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/libburn.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/libdax_audioxtr.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/libdax_audioxtr.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/libdax_msgs.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/libdax_msgs.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/mmc.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/mmc.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/null.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/null.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/options.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/options.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/os-freebsd.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/os-linux.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/os.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/read.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/read.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/sbc.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/sbc.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/sector.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/sector.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/sg-freebsd-port.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/sg-freebsd.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/sg-linux.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/sg.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/sg.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/source.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/source.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/spc.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/spc.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/structure.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/structure.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/toc.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/toc.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/transport.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/util.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/util.h create mode 100644 libburn/tags/ZeroThreeZero/libburn/write.c create mode 100644 libburn/tags/ZeroThreeZero/libburn/write.h create mode 100644 libburn/tags/ZeroThreeZero/test/Makefile create mode 100644 libburn/tags/ZeroThreeZero/test/dewav.c create mode 100644 libburn/tags/ZeroThreeZero/test/fake_au.c create mode 100644 libburn/tags/ZeroThreeZero/test/libburner.c create mode 100644 libburn/tags/ZeroThreeZero/test/poll.c create mode 100644 libburn/tags/ZeroThreeZero/test/structest.c create mode 100644 libburn/tags/ZeroThreeZero/test/telltoc.c create mode 100644 libburn/tags/ZeroThreeZero/version.h.in diff --git a/libburn/tags/ZeroThreeZero/AUTHORS b/libburn/tags/ZeroThreeZero/AUTHORS new file mode 100644 index 00000000..59a18579 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/AUTHORS @@ -0,0 +1,5 @@ +Developers: + +Mario Danic +Thomas Schmitt +Lorenzo Taylor diff --git a/libburn/tags/ZeroThreeZero/CONTRIBUTORS b/libburn/tags/ZeroThreeZero/CONTRIBUTORS new file mode 100644 index 00000000..812d5be0 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/CONTRIBUTORS @@ -0,0 +1,5 @@ +Joe Neeman +Philippe Rouquier +Gabriel Craciunescu +George Danchev +Jean-Francois Wauthy diff --git a/libburn/tags/ZeroThreeZero/COPYING b/libburn/tags/ZeroThreeZero/COPYING new file mode 100644 index 00000000..5a965fbc --- /dev/null +++ b/libburn/tags/ZeroThreeZero/COPYING @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/libburn/tags/ZeroThreeZero/COPYRIGHT b/libburn/tags/ZeroThreeZero/COPYRIGHT new file mode 100644 index 00000000..2199a9e8 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/COPYRIGHT @@ -0,0 +1,19 @@ +Derek Foreman and Ben Jansens +Copyright (C) 2002-2006 Derek Foreman and Ben Jansens +Mario Danic , Thomas Schmitt +Copyright (C) 2006-2007 Mario Danic, Thomas Schmitt + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA diff --git a/libburn/tags/ZeroThreeZero/ChangeLog b/libburn/tags/ZeroThreeZero/ChangeLog new file mode 100644 index 00000000..7001d0f2 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/ChangeLog @@ -0,0 +1 @@ +nothing here now diff --git a/libburn/tags/ZeroThreeZero/INSTALL b/libburn/tags/ZeroThreeZero/INSTALL new file mode 100644 index 00000000..5458714e --- /dev/null +++ b/libburn/tags/ZeroThreeZero/INSTALL @@ -0,0 +1,234 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006 Free Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/libburn/tags/ZeroThreeZero/Makefile.am b/libburn/tags/ZeroThreeZero/Makefile.am new file mode 100644 index 00000000..dfde7680 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/Makefile.am @@ -0,0 +1,198 @@ +pkgconfigdir=$(libdir)/pkgconfig +libincludedir=$(includedir)/libburn + +lib_LTLIBRARIES = libburn/libburn.la + +## ========================================================================= ## + +# Build libraries +libburn_libburn_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) +libburn_libburn_la_LIBADD = $(LIBBURN_ARCH_LIBS) $(THREAD_LIBS) +libburn_libburn_la_SOURCES = \ + libburn/async.c \ + libburn/async.h \ + libburn/back_hacks.h \ + libburn/cleanup.c \ + libburn/cleanup.h \ + libburn/crc.c \ + libburn/crc.h \ + libburn/debug.c \ + libburn/debug.h \ + libburn/drive.c \ + libburn/drive.h \ + libburn/error.h \ + libburn/file.c \ + libburn/file.h \ + libburn/init.c \ + libburn/init.h \ + libburn/lec.c \ + libburn/lec.h \ + libburn/libburn.h \ + libburn/libdax_audioxtr.h \ + libburn/libdax_audioxtr.c \ + libburn/libdax_msgs.h \ + libburn/libdax_msgs.c \ + libburn/mmc.c \ + libburn/mmc.h \ + libburn/null.c \ + libburn/null.h \ + libburn/options.c \ + libburn/options.h \ + libburn/os.h \ + libburn/read.c \ + libburn/read.h \ + libburn/sbc.c \ + libburn/sbc.h \ + libburn/sector.c \ + libburn/sector.h \ + libburn/sg.c \ + libburn/sg.h \ + libburn/source.h \ + libburn/source.c \ + libburn/spc.c \ + libburn/spc.h \ + libburn/structure.c \ + libburn/structure.h \ + libburn/toc.c \ + libburn/toc.h \ + libburn/transport.h \ + libburn/util.c \ + libburn/util.h \ + libburn/write.c \ + libburn/write.h \ + version.h + +## libburn/sg-@ARCH@.c \ + +libinclude_HEADERS = \ + libburn/libburn.h + +## ========================================================================= ## + +## Build test applications +noinst_PROGRAMS = \ + test/libburner \ + test/telltoc \ + test/dewav \ + test/fake_au \ + test/poll \ + test/structest + +bin_PROGRAMS = \ + cdrskin/cdrskin + +test_libburner_CPPFLAGS = -Ilibburn +test_libburner_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_libburner_SOURCES = test/libburner.c +test_telltoc_CPPFLAGS = -Ilibburn +test_telltoc_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_telltoc_SOURCES = test/telltoc.c +test_dewav_CPPFLAGS = -Ilibburn +test_dewav_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_dewav_SOURCES = test/dewav.c +test_fake_au_CPPFLAGS = +test_fake_au_LDADD = +test_fake_au_SOURCES = test/fake_au.c +test_poll_CPPFLAGS = -Ilibburn +test_poll_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_poll_SOURCES = test/poll.c +test_structest_CPPFLAGS = -Ilibburn +test_structest_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_structest_SOURCES = test/structest.c + +## cdrskin construction site - ts A60816 +cdrskin_cdrskin_CPPFLAGS = -Ilibburn +cdrskin_cdrskin_CFLAGS = -DCdrskin_libburn_0_2_7 +cdrskin_cdrskin_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +cdrskin_cdrskin_SOURCES = cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cdrfifo.h cdrskin/cdrskin_timestamp.h +## +## Open questions: how to compute $timestamp and express -DX="$timestamp" +## + + + +## ========================================================================= ## + +## Build documentation (You need Doxygen for this to work) +webhost = http://libburn-api.pykix.org +webpath = / +docdir = $(DESTDIR)$(prefix)/share/doc/$(PACKAGE)-$(VERSION) + +doc: doc/html + +doc/html: doc/doxygen.conf + if [ -f ./doc/doc.lock ]; then \ + $(RM) -r doc/html; \ + doxygen doc/doxygen.conf; \ + fi + +doc-upload: doc/html + scp -r $ and Thomas Schmitt +Copyright (C) 2006-2007 Mario Danic, Thomas Schmitt + +Still containing parts of +Libburn. By Derek Foreman and + Ben Jansens +Copyright (C) 2002-2006 Derek Foreman and Ben Jansens +These parts are to be replaced by own code of above libburnia.pykix.org +copyright holders and then libburnia.pykix.org is to be their sole copyright. +This is done to achieve the right to issue the clarification and the +commitment as written at the end of this text. +The rights and merits of the Libburn-copyright holders Derek Foreman and +Ben Jansens will be duely respected. + +This libburnia.pykix.org toplevel README (C) 2006-2007 Thomas Schmitt +------------------------------------------------------------------------------ + + Build and Installation + +Our build system is based on autotools. For preparing the build of a SVN +snapshot you will need autotools of at least version 1.7. +Check out from SVN by + svn co http://libburnia-svn.pykix.org/libburn/trunk libburn_pykix +go into directory libburn_pykix and apply autotools by + ./bootstrap + +Alternatively you may unpack a release tarball for which you do not need +autotools installed. + +To build a libburnia.pykix.org subproject it should be sufficient to go +into its toplevel directory (here: "libburn_pykix") and execute + ./configure + make + +To make the libraries accessible for running resp. developing applications + make install + + +The other half of the project, libisofs, is hosted in the libburnia SVN, too: + svn co http://libburnia-svn.pykix.org/libisofs/trunk libisofs_pykix +See README file there. + + +------------------------------------------------------------------------------ + + + Overview of libburnia.pykix.org + +libburnia.pykix.org is an open-source software project for reading, mastering +and writing optical discs. For now this means only CD-R and CD-RW. + +The project comprises of several more or less interdependent parts which +together strive to be a usable foundation for application development. +These are libraries, language bindings, and middleware binaries which emulate +classical (and valuable) Linux tools. + +Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems +we would need : login on a development machine resp. a live OS on CD or DVD, +advise from a system person about the equivalent of Linux sg or FreeBSD CAM, +volunteers for testing of realistic use cases. + +We do have a workable code base for burning data CDs, though. The burn API is +quite comprehensively documented and can be used to build a presentable +application. +We do have a functional binary which emulates parts of cdrecord in order to +prove that usability, and in order to allow you to explore libburnia's scope +by help of existing cdrecord frontends. + +The project components (list subject to growth, hopefully): + +- libburn is the library by which preformatted data get onto optical media. + It uses either /dev/sgN (e.g. on kernel 2.4 with ide-scsi) or + /dev/hdX (e.g. on kernel 2.6). + libburn is the foundation of our cdrecord emulation. + +- libisofs is the library to pack up hard disk files and directories into a + ISO 9660 disk image. This may then be brought to CD via libburn. + libisofs is to be the foundation of our upcoming mkisofs emulation. + +- cdrskin is a limited cdrecord compatibility wrapper for libburn. + Cdrecord is a powerful GPL'ed burn program included in Joerg + Schilling's cdrtools. cdrskin strives to be a second source for + the services traditionally provided by cdrecord. + cdrskin does not contain any bytes copied from cdrecord's sources. + Many bytes have been copied from the message output of cdrecord + runs, though. + See cdrskin/README for more. + +- test is a collection of application gestures and examples given by the + authors of the library features. The main API example for libburn + is test/libburner.c . + Explore these examples if you look for inspiration. + +We plan to be a responsive upstream. Bear with us. We are still practicing. + + +------------------------------------------------------------------------------ +Project history as far as known to me: + +- Founded in 2002 as it seems. See mailing list archives + http://lists.freedesktop.org/archives/libburn/ + The site of this founder team is reachable and offers download of a + (somewhat outdated) tarball and from CVS : + http://icculus.org/burn/ + Copyright holders and most probably founders: + Derek Foreman and Ben Jansens. + +- I came to using libburn in 2005. Founded the cdrskin project and submitted + necessary patches which were accepted or implemented better. Except one + remaining patch which prevented cdrskin from using vanilla libburn from CVS. + The cdrskin project site is reachable and offers download of the heavily + patched (elsewise outdated) tarball under the name cdrskin-0.1.2 : + http://scdbackup.sourceforge.net/cdrskin_eng.html + It has meanwhile moved to use vanilla libburn.pykix.org , though. + Version 0.1.4 constitutes the first release of this kind. + +- In Juli 2006 our team mate Mario Danic announced a revival of libburn + which by about nearly everybody else was perceived as unfriendly fork. + Derek Foreman four days later posted a message which expressed his + discontent. + The situation first caused me to publically regret it and then - after i + got the opportunity to move in with cdrskin - gave me true reason to + personally apologize to Derek Foreman, Ben Jansens and the contibutors at + icculus.org/burn. Posted to both projects: + http://lists.freedesktop.org/archives/libburn/2006-August/000446.html + http://mailman-mail1.webfaction.com/pipermail/libburn-hackers/2006-August/000024.html + +- Mid August 2006 project cdrskin established a branch office in + libburn.pykix.org so that all maintainers of our tools have one single place + to get the current (at least slightely) usable coordinated versions of + everything. + Project cdrskin will live forth independendly for a while but it is committed + to stay in sync with libburn.pykix.org (or some successor, if ever). + cdrskin is also committed to support icculus.org/burn if the pending fork + is made reality by content changes in that project. It will cease to maintain + a patched version of icculus.org/burn though. Precondition for a new + release of cdrskin on base of icculus.org/burn would be the pending + "whitelist patch" therefore. + I would rather prefer if both projects find consense and merge, or at least + cooperate. I have not given up hope totally, yet. + I, personally, will honor any approach. + +- 2nd September 2006 the decision is made to strive for a consolidation of + copyright and a commitment to GPL in a reasonable and open minded way. + This is to avoid long term problems with code of unknown origin and + with finding consense among the not so clearly defined group of copyright + claimers and -holders. + libisofs is already claimed sole copyright Mario Danic. + cdrskin and libburner are already claimed sole copyright Thomas Schmitt. + Rewrites of other components will follow and concluded by claiming full + copyright within the group of libburn.pykix.org-copyright holders. + +- 16th September 2006 feature freeze for release of libburn-0.2.2 . + +- 20th September 2006 release of libburn-0.2.2 . + +- 26th October 2006 feature freeze for cdrskin-0.2.4 based on libburn-0.2.3 . + This version of cdrskin is much more cdrecord compatible in repect + to drive addressing and audio features. + +- 30th October 2006 release of cdrskin-0.2.4 . + +- 13th November 2006 splitting releases of libburn+cdrskin from libisofs. + +- 24th November 2006 release of libburn-0.2.6 and cdrskin-0.2.6 . cdrskin has + become suitable for unaware frontends as long as they perform only the core + of cdrecord use cases (including open-ended input streams, audio, and + multi-session). + +- 28th November 2006 the umbrella project which encloses both, libisofs and + libburn, is now called libburnia. For the origin of this name, see + http://en.wikipedia.org/wiki/Liburnians . + + +------------------------------------------------------------------------------ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +------------------------------------------------------------------------------ +Clarification in my name and in the name of Mario Danic, upcoming copyright +holders on toplevel of libburnia. To be fully in effect after the remaining +other copyrighted code has been replaced by ours and by copyright-free +contributions of our friends: +------------------------------------------------------------------------------ + +We, the copyright holders, agree on the interpretation that +dynamical linking of our libraries constitutes "use of" and +not "derivation from" our work in the sense of GPL, provided +those libraries are compiled from our unaltered code. + +Thus you may link our libraries dynamically with applications +which are not under GPL. You may distribute our libraries and +application tools in binary form, if you fulfill the usual +condition of GPL to offer a copy of the source code -altered +or unaltered- under GPL. + +We ask you politely to use our work in open source spirit +and with the due reference to the entire open source community. + +If there should really arise the case where above clarification +does not suffice to fulfill a clear and neat request in open source +spirit that would otherwise be declined for mere formal reasons, +only in that case we will duely consider to issue a special license +covering only that special case. +It is the open source idea of responsible freedom which will be +decisive and you will have to prove that you exhausted all own +means to qualify for GPL. + +For now we are firmly committed to maintain one single license: GPL. + +signed: Mario Danic, Thomas Schmitt + diff --git a/libburn/tags/ZeroThreeZero/acinclude.m4 b/libburn/tags/ZeroThreeZero/acinclude.m4 new file mode 100644 index 00000000..861847bb --- /dev/null +++ b/libburn/tags/ZeroThreeZero/acinclude.m4 @@ -0,0 +1,22 @@ +AC_DEFUN([TARGET_SHIZZLE], +[ + ARCH="" + + AC_MSG_CHECKING([target operating system]) + + case $target in + *-*-linux*) + ARCH=linux + LIBBURN_ARCH_LIBS= + ;; + *-*-freebsd*) + ARCH=freebsd + LIBBURN_ARCH_LIBS=-lcam + ;; + *) + AC_ERROR([You are attempting to compile for an unsupported platform]) + ;; + esac + + AC_MSG_RESULT([$ARCH]) +]) diff --git a/libburn/tags/ZeroThreeZero/bootstrap b/libburn/tags/ZeroThreeZero/bootstrap new file mode 100755 index 00000000..86709bfc --- /dev/null +++ b/libburn/tags/ZeroThreeZero/bootstrap @@ -0,0 +1,10 @@ +#!/bin/sh -x + +aclocal +libtoolize --copy --force +autoconf + +# ts A61101 : libburn is not prepared for config.h +# autoheader + +automake --foreign --add-missing --copy --include-deps diff --git a/libburn/tags/ZeroThreeZero/cdrskin/README b/libburn/tags/ZeroThreeZero/cdrskin/README new file mode 100644 index 00000000..2ef0f124 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/README @@ -0,0 +1,496 @@ +------------------------------------------------------------------------------ + libburnia.pykix.org scdbackup.sourceforge.net/cdrskin_eng.html +------------------------------------------------------------------------------ +Installation instructions at about line 60. First the legal stuff: +------------------------------------------------------------------------------ +This all is under GPL. +(See GPL reference, our clarification and commitment at the end of this text) +------------------------------------------------------------------------------ +Based on and sub project of: +libburnia.pykix.org +By Mario Danic and Thomas Schmitt +Copyright (C) 2006-2007 Mario Danic, Thomas Schmitt + +libburnia.pykix.org is inspired by and in other components still containing +parts of +Libburn. By Derek Foreman and + Ben Jansens +Copyright (C) 2002-2006 Derek Foreman and Ben Jansens +See toplevel README for an overview of the current copyright situation in +libburnia.pykix.org. + +------------------------------------------------------------------------------ +My thanks to the above authors (except myself, of course) for making the +following possible. + +cdrskin. By Thomas Schmitt +Integrated sub project of libburnia.pykix.org but also published via: +http://scdbackup.sourceforge.net/cdrskin_eng.html +http://scdbackup.sourceforge.net/cdrskin-0.2.7.tar.gz +Copyright (C) 2006-2007 Thomas Schmitt + +------------------------------------------------------------------------------ + +On top of libburn there is implemented cdrskin 0.2.7, a limited cdrecord +compatibility wrapper which allows to use some libburn features from +the command line. +Interested users of cdrecord are invited to participate in the development +of cdrskin. Contact: scdbackup@gmx.net or libburn-hackers@pykix.org . +We will keep copyright narrow but will of course acknowledge valuable +contributions in a due way. + + +Important : +This software is provided as is. There is no warranty implied and no +protection against possible damages. You use this on your own risk. +Don't blame me or other authors of libburn if anything goes wrong. + +I used it on my own risk with : +SuSE 7.2, kernel 2.4.4, ide-scsi emulation, LITE-ON LTR48125S CD burner, 2002 +SuSE 9.0, kernel 2.4.21, ide-scsi emulation, LG GSA-4082B CD/DVD burner, 2004 + NEC ND-4570A CD/DVD burner, 2006 +RIP-14.4, kernel 2.6.14, no ide-scsi, with all above burners + +It fails to compile or run on SuSE 6.4 (kernel 2.2.14). +It does not find the IDE CD burner on SuSE 7.2 without ide-scsi. +Other people sucessfully tested cdrskin on several kernel 2.6 based x86 Linux +systems, including 64 bit systems. (Further reports are welcome.) + + + Compilation, First Glimpse, Installation + +Obtain cdrskin-0.2.7.tar.gz, take it to a directory of your choice and do: + + tar xzf cdrskin-0.2.7.tar.gz + cd cdrskin-0.2.7 + +Or obtain a libburnia.pykix.org SVN snapshot, +go into the toplevel directory of the snapshot (e.g. cd libburn_pykix ), +and execute the autotools script ./bootstrap . Use autools version >= 1.7 . + +Within that toplevel directory of either cdrskin-0.2.7 or libburn then execute: + + ./configure + make + +(Note: there are next-level directories "libburn" and "cdrskin". Those +would be the wrong ones. Meant is the highest directory of tarball resp. +SVN download. Among others containing files "AUTHORS", "configure", +"Makefile.am", as well as directories "libburn" and "cdrskin".) + +This will already produce a cdrskin binary. But it might be necessary to +install libburn in order to use this binary. Installation of libburn is +beyond the scope of cdrskin. For this, see included libburn docs. + +In order to surely get a standalone binary, execute + + cdrskin/compile_cdrskin.sh + +Version identification and help texts available afterwards: + cdrskin/cdrskin -version + cdrskin/cdrskin --help + cdrskin/cdrskin -help + man cdrskin/cdrskin.1 + +Install (eventually as superuser) cdrskin to a directory where it can be found: +If cdrskin was already installed by a previous version, or by "make install" +in the course of this installation, then find out where: + which cdrskin +Copy your standalone binary to exactly the address which you get as reply + + cp cdrskin/cdrskin /usr/bin/cdrskin + +Check the version timestamps of the globally installed binary + cdrskin -version + +It is not necessary for the standalone cdrskin binary to have libburn +installed, since it incorporates the necessary libburn parts at compile time. +It will not collide with an installed version of libburn either. +But libpthread must be installed on the system and glibc has to match. (See +below for a way to create a statically linked binary.) + +To install the man page, you may do: echo $MANPATH and choose one of the +listed directories to copy the man-page under its ./man1 directory. Like: + cp cdrskin/cdrskin.1 /usr/share/man/man1/cdrskin.1 + + + Usage + +The user of cdrskin needs rw-permission for the CD burner device. +A list of rw-accessible drives can be obtained by + + cdrskin --devices + +CD devices which offer no rw-permission are invisible to normal users. +The superuser should be able to see any usable drive and then set the +permissions as needed. If this hangs then there is a drive with +unexpected problems (locked, busy, broken, whatever). You might have to +guess the address of your (non-broken) burner by other means, then. +On Linux 2.4 this would be some /dev/sgN and on 2.6. some /dev/hdX. + +The output of cdrskin --devices might look like + + 0 dev='/dev/sg0' rwrwr- : '_NEC' 'DVD_RW ND-4570A' + 1 dev='/dev/sg1' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B' + +So full and insecure enabling of both for everybody would look like + + chmod a+rw /dev/sg0 /dev/sg1 + +I strongly discourage to run cdrskin with setuid root or via sudo ! +It is not checked for the necessary degree of hacker safety. + + + Usage examples + +Get an overview of cdrecord style addresses of available devices + cdrskin -scanbus + cdrskin dev=ATA -scanbus + cdrskin --devices + +Adresses reported with dev=ATA need prefix "ATA:". Address examples: + dev=0,1,0 dev=ATA:1,0,0 dev=/dev/sg1 dev=/dev/hdc +See also "Drive Addressing" below. + +Obtain some info about the drive + cdrskin dev=0,1,0 -checkdrive + +Obtain some info about the drive and the inserted media + cdrskin dev=0,1,0 -atip -v + +Thoroughly blank a CD-RW + cdrskin -v dev=0,1,0 blank=all -eject + +Blank CD-RW sufficiently for making it ready for overwrite + cdrskin -v dev=0,1,0 blank=fast -eject + +Burn image file my_image.iso to CD or DVD+-RW + cdrskin -v dev=0,1,0 speed=12 fs=8m driveropts=burnfree padsize=300k \ + -eject my_image.iso + +Burn a compressed afio archive to CD or DVD+-RW on-the-fly + find . | afio -oZ - | cdrskin -v dev=0,1,0 fs=32m speed=8 -tao \ + driveropts=burnfree padsize=300k - + +Burn 6 audio tracks from files with different formats to CD. +Anything except .wav or .au files has to be converted into raw format first. +See below "Audio CD" for specifications. + ogg123 -d raw -f track01.cd /path/to/track1.ogg + oggdec -R -o track02.cd /path/to/track2.ogg + lame --decode -t /path/to/track3.mp3 track03.cd + madplay -o raw:track04.cd /path/to/track4.mp3 + mppdec --raw-le /path/to/track5.mpc track05.cd + + cdrskin -v dev=0,1,0 blank=fast -eject speed=48 -sao \ + -audio -swab track0[1-5].cd /path/to/track6.wav + + + Usage example with http://scdbackup.sourceforge.net + +Address may be a cdrecord-style "scsibus,target,lun" as listed with +cdrskin -scanbus (and hopefully as listed with cdrecord -scanbus) : + + export SCDBACKUP_SCSI_ADR="0,1,0" + +or a device file address as listed by --devices with an accessible drive : + + export SCDBACKUP_SCSI_ADR="/dev/sg1" + +Set usage of cdrskin with appropriate options rather than cdrecord : + + export SCDBACKUP_CDRECORD="cdrskin -v -v" + +Run a backup : + + scdbackup_home + + + Restrictions + +The major restrictions are lifted now: audio, TAO, multi-session do work. +Many cdrecord options are still unsupported, though. + +If you have use cases for them, please report your wishes and expectations. + +DVD support is still immature and restricted to DVD+-RW for now. + + + + Inspiration and Standard + +For the original meaning of cdrecord options see : + man cdrecord + (http://cdrecord.berlios.de/old/private/man/cdrecord-2.0.html) +Do not bother Joerg Schilling with any cdrskin problems. +(Be cursed if you install cdrskin as "cdrecord" without clearly forwarding + this "don't bother Joerg" demand.) +cdrskin does not contain any bytes copied from cdrecord's sources. Many bytes +have been copied from the message output of cdrecord runs, though. I am +thankful to Joerg Schilling for every single one of them. + +Actually i, Thomas Schmitt, am a devoted user of cdrecord via my project +scdbackup which still runs a bit better with cdrecord than with cdrskin. TAO. +I have the hope that Joerg feels more flattered than annoyed by cdrskin. + + + Drive Addressing + +Drives get addressed either via their cdrecord-style addresses as listed +with option -scanbus (see below "Pseudo-SCSI Adresses") or via the paths +of device files. +Not only device files listed by --devices may be used but also device files +which via their major,minor numbers point to the same device driver as +a listed device file. + +Helpful with Linux kernel 2.4 is a special SCSI feature: +It is possible to address a scsi(-emulated) drive via associated device files +which are not listed by option --devices but point to the same SCSI addresses +as listed device files. This addressing via e.g. /dev/sr0 or /dev/scd1 is +compatible with generic read programs like dd and with write program growisofs. + + Pseudo-SCSI Adresses + +cdrecord and cdrskin share the syntax of SCSI addresses but not necessarily +the meaning of the components. A cdrecord-style address for cdrskin + [prefix:]scsibus,target,lun +can be interpreted in two different modes. + +Standard mode tries to be compatible to original cdrecord. This should be true +with (emulated) SCSI where the /dev/sgN with is looked up with matching +scsibus,target,lun as given by the operating system. +With dev=ATA: or dev=ATAPI: the translation to /dev/hdX is purely literal +but matches the cdrecord addresses on all systems tested so far: + X = 'a' + 2 * scsibus + target +where target only may have the values 0 or 1. + +In this mode, option -scanbus will list only SCSI devices unless option +dev=ATA or dev=ATAPI are given, which will suppress SCSI devices and only +show IDE drives (i.e. /dev/hdX without ide-scsi emulation). + + +In mode --old_pseudo_scsi_adr there is a scsibus,target,lun representation +which has nothing to do with SCSI and thus is not compatible to cdrecord. +Each number triple corresponds either to a device file address or to a +libburn drive number. +Component "scsibus" indicates the translation method. Defined busses are: + 0 target is the libburn drivenumber as listed with --devices + 1 associated to device file /dev/sgN , target chooses N + 2 associated to device file /dev/hdX , target 0='a', 1='b' ..., 25='z' + +So "1,1,0" is /dev/sg1, "2,3,0" is /dev/hdd, "0,2,0" is libburn drive #2 at +some unspecified device file. +This scheme shall help to keep cdrecord-style addresses stable and exchangeable +between users without excluding drives with unexpected device addresses. +The numbering on bus 0 is prone to arbitrary changes caused by changes in +drive accessability. +Further busses may emerge as libburn evolves. "prefix" and "lun" may get +a meaning. To stay upward compatible, use addresses as printed by -scanbus. + + User Defined Device Address Translation + +Some programs or users have their own ideas about the address of their burner. +K3b 0.10 for example derives cdrecord addresses by own examination of the +devices and not by calling cdrecord -scanbus. +Standard mode will hopefully be fully compatible with their ideas. + +Old frontends which do not know dev=ATA or dev=ATAPI and which do ask their +"cdrecord" via -scanbus may be well served with option --old_pseudo_scsi_adr . + +To direct any remaining stubborn callers to the appropriate drives, cdrskin +allows to define device address aliases. Like + cdrskin dev_translation=+1,0,0+/dev/sg1 \ + dev_translation=+ATA:1,0,0+/dev/sg1 \ + dev_translation=-"cd+dvd"-0,1,0 \ + ... +Any of the addresses dev=1,0,0, dev=ATA:1,0,0, dev=cd+dvd will be mapped to +/dev/sg1 resp. to 0,1,0. +The first character after "dev_translation=" defines the character which +separates the two parts of the translation pair. (Above: "+" and "-".) + +In K3b 0.10 it is possible to employ alternative writer programs by setting +their full path (e.g. /usr/bin/cdrskin) in menu + Settings:Configure K3b...:Programs:Search Path +and to make them default in menu + Settings:Configure K3b...:Programs:Programs: +A suitable setting for "cdrecord" in menu + Settings:Configure K3b...:Programs:User Parameters +would then probably be + -v dev_translation=+1,0,0+/dev/sg1 +You will learn from button "Show Debugging Output" after a failed burn run +what cdrecord command was used with what address "dev=...". This address "..." +will be the right one to replace "1,0,0" in above example. + + + Startup Files + +If not --no_rc is the first argument then cdrskin attempts on startup to read +arguments from the following three files: + /etc/default/cdrskin + /etc/opt/cdrskin/rc + /etc/cdrskin/cdrskin.conf + $HOME/.cdrskinrc +The files are read in the sequence given above. +Each readable line is treated as one single argument. No extra blanks. +A first character '#' marks a comment, empty lines are ignored. + +Example content of a startup file: +# This is the default device +dev=0,1,0 +# To accomodate to eventual remnant cdrskin-0.2.2 addresses +dev_translation=+1,0,0+0,1,0 + +# Some more options +fifo_start_at=0 +fs=16m + + + Audio CD + +Lorenzo Taylor enabled option -audio in cdrskin (thanks !) and reports neat +results with audio data files which are : + headerless PCM (i.e. uncompressed) + 44100 Hz sampling rate + 16 bits per sample + stereo (2 channels) + little-endian byte order with option -swab, or big-endian without -swab + +Files with name extension .wav get examined wether they are in Microsoft WAVE +format with above parameters and eventually get extracted by cdrskin itself. +In the same way files with name extension .au get examined wether they are +in SUN's audio format. For both formats, track format -audio and eventual +endianness option -swab are enabled automatically. + +Any other formats are to be converted to format .wav with above parameters +or to be extracted as raw CD track data by commands like those given above +under "Usage examples". Those raw files need option -audio and in most cases +option -swab to mark them as little-endian/Intel/LSB-first 16-bit data. +Incorrect endianness setting results in random noise on CD. + +I myself am not into audio. So libburn-hackers@pykix.org might be the +best address for suggestions, requests and bug reports. + + + DVD+RW and DVD-RAM + +DVD+RW and DVD-RAM media get treated as blank media regardless wether they +hold data or not. Options -audio and -multi are not allowed. Only one track +is allowed. -toc does not return information about the media content. +Speed is counted in DVD units (i.e. 1x = 1,385,000 bytes/second). Currently +there is no difference between -sao and -tao. If ever, then -tao will be the +mode which preserves the current behavior. + +For these media, -msinfo alone would not be enough to perform appending of an +ISO filesystem. The filesystem driver will need a hint to find the start of the +most recent session. For example put an ISO filesystem at address 1 GB: + mkisofs -C 0,524288 ... | \ + cdrskin dev=/dev/sr0 -v fs=32m -eject speed=4 write_start_address=524288s - +The superuser may then do: + mount -t iso9660 -o ro,sbsector=524288 /dev/sr0 /mnt +Note: On my linux-2.4.21-215 mount works only with sbsector <= 337920 (660 MB). +To extend a filesystem already existing at address 0 + mkisofs -C 0,524288 -M /dev/sr0 ... | cdrskin dev=/dev/sr0 ... +Record the number 524288 for usage as first number with -C at the next +extension: + mkisofs -C 524288,1000000 ... | cdrskin write_start_address=1000000s ... + +Program growisofs can append to an ISO filesystem on DVD+RW by additionally +manipulating the first session. cdrskin does not want to get involved so deep +into the format of the burned data. Be advised to use growisofs for the +task of maintaining extendable ISO-Filesystems on DVD+RW. + + + DVD-RW + +DVD-RW are usable if formatted to state "Restricted Overwrite". They then +behave much like DVD+RW. See above. +DVD-RW in state "Sequential" have first to be formatted by + cdrskin dev=... -v blank=format_overwrite +"Sequential" is the state of unused media and of media previously blanked +or written by cdrecord. dvd+rw-format -blank can also achieve this state. +(Command dvd+rw-format -force can achieve "Restricted Overwrite".) + +Formatting or first use of freshly formatted DVD-RW can produce unusual noises +from the drive and last several minutes. Depending on mutual compatibility of +drive and media, formatting can yield unusable media. It seems that those die +too on blanking by cdrecord or dvd+rw-format. Perils of DVD-RW, i fear. + +There are three formatting variants with cdrskin currently: + +blank=format_overwrite uses "DVD-RW Quick" formatting (MMC-type 15h) +and writes a first session of 128 MB. This leads to media which are expandable +and random addressable by cdrskin. + +blank=format_overwrite_quickest uses "DVD-RW Quick" formatting (type 15h) too, +but leaves the media in "intermediate" state. In the first session of writing +one may only write sequentially to such a DVD. After that, it gets random +addressable by cdrskin. + +blank=format_overwrite_full uses preferrably "Full Format" (type 00h). +This formatting lasts as long as writing a full DVD. It includes writing of +lead-out which is said to be good for DVD ROM compatibility. + + + Special compilation variations + +You may get a (super fat) statically linked binary by : + cdrskin/compile_cdrskin.sh -static +if your system supports static linking, at all. This will not help with kernels +which do not properly support the necessary low-level interfaces chosen by +your compile-time libraries. + +A size reduced but fully functional binary may be produced by + cdrskin/compile_cdrskin.sh -do_strip + +An extra lean binary with reduced capabilities is created by + cdrskin/compile_cdrskin.sh -do_diet -do_strip +It will not read startup files, will abort on option dev_translation= , +will not have a fifo buffer, and will not be able to put out help texts or +debugging messages. + +------------------------------------------------------------------------------ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +------------------------------------------------------------------------------ +cdrskin is currently copyright Thomas Schmitt only. +It adopts the following commitment by the toplevel copyright holders: +------------------------------------------------------------------------------ + +We, the copyright holders, agree on the interpretation that +dynamical linking of our libraries constitutes "use of" and +not "derivation from" our work in the sense of GPL, provided +those libraries are compiled from our unaltered code. + +Thus you may link our libraries dynamically with applications +which are not under GPL. You may distribute our libraries and +application tools in binary form, if you fulfill the usual +condition of GPL to offer a copy of the source code -altered +or unaltered- under GPL. + +We ask you politely to use our work in open source spirit +and with the due reference to the entire open source community. + +If there should really arise the case where above clarification +does not suffice to fulfill a clear and neat request in open source +spirit that would otherwise be declined for mere formal reasons, +only in that case we will duely consider to issue a special license +covering only that special case. +It is the open source idea of responsible freedom which will be +decisive and you will have to prove that you exhausted all own +means to qualify for GPL. + +For now we are firmly committed to maintain one single license: GPL. + +signed for cdrskin: Thomas Schmitt diff --git a/libburn/tags/ZeroThreeZero/cdrskin/add_ts_changes_to_libburn_0_2_6 b/libburn/tags/ZeroThreeZero/cdrskin/add_ts_changes_to_libburn_0_2_6 new file mode 100755 index 00000000..bf09dd8a --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/add_ts_changes_to_libburn_0_2_6 @@ -0,0 +1,206 @@ +#!/bin/sh + +set -x + +# This script documents how this cdrskin version was derived from +# a vanilla libburn version. It is not intended nor needed for any +# use of cdrskin but included here only to show the technical +# relationship between both projects - which are close friends +# and issue roughly the same software. +# +# Package maintainers are advised to cover rather libburn than +# cdrskin unless they put only emphasis on the cdrecord emulation +# provided by cdrskin. libburn contains cdrskin - cdrskin is an +# oscillating, friendly and coordinated fork of libburn. +# +# Script results are a source tarball and two binaries +# one dynamic and one static in respect to system libs. +# Both binaries are static in respect to libburn. +# +# The script is to be run in the directory above the toplevel +# directory of libburn resp. cdrskin development. +# +# libburn version used: http://libburn.pykix.org +# Downloaded by: +# $ svn co http://libburn-svn.pykix.org/trunk libburn_pykix +# packed up in a tarball just to save it from inadverted changes by +# $ tar czf libburn_svn.tgz libburn_pykix +original="./libburn_svn.tgz" +# Historic moments: +# original="./libburn_svn_A60815.tgz" +# original="./libburn_cdrskin_A60819.tgz" + +# The top level directory in that snapshot is named +intermediate="./libburn_pykix" + +# My changes are in libburn-0.2.3.ts.develop , mainly in ./cdrskin + +changes="./libburn-0.2.3.ts.develop" +skin_release="0.2.6" +patch_level=".pl01" +skin_rev="$skin_release""$patch_level" + + +# The result directory and the name of the result tarballs +target="./cdrskin-${skin_release}" +cdrskin_tarball="./cdrskin-${skin_rev}.tar.gz" +cdrskin_tarball_svn="./cdrskin-${skin_rev}.svn.tar.gz" + +# (This once earned me an embarrassingly blooping source tarball) +# compile_dir="$changes" + +compile_dir="$target" +compile_cmd="./cdrskin/compile_cdrskin.sh" +compile_static_opts="-static" +compile_result="cdrskin/cdrskin" + +bintarget_dynamic="cdrskin_${skin_rev}-x86-suse9_0" +bintarget_static="$bintarget_dynamic"-static + +if test -d "$changes" +then + dummy=dummy +else + echo "$0 : FATAL : no directory $changes" >&2 + exit 1 +fi + +for i in "$target" "$intermediate" +do + if test -e "$i" + then + echo "$0 : FATAL : already existing $i" >&2 + exit 2 + fi +done + +if test -f "$original" +then + dummy=dummy +else + echo "$0 : FATAL : no file $original" >&2 + exit 3 +fi + + +# Unpack SVN snapshot. +tar xzf "$original" + + +# Rename the directory to the cdrskin name +mv "$intermediate" "$target" + + +# Copy the changes from the development tree +# +cdrskin_dir="$changes"/cdrskin +libburn_dir="$changes"/libburn +cdrskin_target="$target"/cdrskin +libburn_target="$target"/libburn + +# Create version timestamp +timestamp="$(date -u '+%Y.%m.%d.%H%M%S')" +echo "$timestamp" +echo '#define Cdrskin_timestamP "'"$timestamp"'"' >"$cdrskin_dir"/cdrskin_timestamp.h + +# Add the cdrskin files +if test -e "$cdrskin_target" +then + rm -rf "$cdrskin_target" +fi +cp -a "$cdrskin_dir" "$cdrskin_target" + +# Remove copied binaries +rm "$cdrskin_target"/*.o +rm "$cdrskin_target"/cdrfifo +rm "$cdrskin_target"/cdrskin +rm "$cdrskin_target"/cleanup +for i in std new make old +do + if test -e "$cdrskin_target"/cdrskin_"$i" + then + rm "$cdrskin_target"/cdrskin_"$i" + fi +done +for i in .deps .dirstamp .libs +do + if test -e "$cdrskin_target"/"$i" + then + rm -rf "$cdrskin_target"/"$i" + fi +done + +# Remove unwanted SVN stuff (TODO: avoid downloading it) +for i in "$target"/.svn "$target"/*/.svn +do + if test "$i" = "$target"'/*/.svn' + then + dummy=dummy + else + if test -e "$i" + then + rm -rf "$i" + fi + fi +done + +## No more : Add own libburn-README in toplevel +# cp -a "$changes"/README "$target" + +## No more : Add modified Makefile.am +# cp -a "$changes"/Makefile.am "$target" + + +# Make SVN state tarball for the libburn team +tar czf "$cdrskin_tarball_svn" "$target" + + +# Get over dependecy on autotools. Rely only on cc, make et. al. +# This is not the same as "make dist" but i can do it without +# having to evaluate the quality of said "make dist" +# +( cd "$target" ; ./bootstrap ) + +# Remove unwanted stuff after bootstrap +for i in "$target"/autom4te.cache +do + if echo "$i" | grep '\*' >/dev/null + then + dummy=dummy + else + if test -e "$i" + then + rm -rf "$i" + fi + fi +done + + +# Pack it up to the new libburn+cdrskin-tarball +tar czf "$cdrskin_tarball" "$target" + +# Produce a static and a dynamic binary +( + cd "$compile_dir" || exit 1 + ./configure + make + $compile_cmd -do_strip + cp "$compile_result" "../$bintarget_dynamic" + if test -n "$compile_static_opts" + then + $compile_cmd $compile_static_opts -do_strip + cp "$compile_result" "../$bintarget_static" + fi +) + +# Remove the build area +# Disable this for debugging the merge process +rm -rf "$target" + +# Show the result +./"$bintarget_dynamic" -version +./"$bintarget_static" -version +ls -l "$cdrskin_tarball" +ls -l "$bintarget_dynamic" +ls -l "$bintarget_static" + diff --git a/libburn/tags/ZeroThreeZero/cdrskin/add_ts_changes_to_libburn_0_2_7 b/libburn/tags/ZeroThreeZero/cdrskin/add_ts_changes_to_libburn_0_2_7 new file mode 100755 index 00000000..5f870b8e --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/add_ts_changes_to_libburn_0_2_7 @@ -0,0 +1,217 @@ +#!/bin/sh + +set -x + +# This script documents how this cdrskin version was derived from +# a vanilla libburn version. It is not intended nor needed for any +# use of cdrskin but included here only to show the technical +# relationship between both projects - which are close friends +# and issue roughly the same software. +# +# Package maintainers are advised to cover rather libburn than +# cdrskin unless they put only emphasis on the cdrecord emulation +# provided by cdrskin. libburn contains cdrskin - cdrskin is an +# oscillating, friendly and coordinated fork of libburn. +# +# Script results are a source tarball and two binaries +# one dynamic and one static in respect to system libs. +# Both binaries are static in respect to libburn. +# +# The script is to be run in the directory above the toplevel +# directory of libburn resp. cdrskin development. +# +# libburn version used: http://libburn.pykix.org +# Downloaded by: +# $ svn co http://libburn-svn.pykix.org/libburn/trunk libburn_pykix +# packed up in a tarball just to save it from inadverted changes by +# $ tar czf libburn_svn.tgz libburn_pykix +original="./libburn_svn.tgz" +# Historic moments: +# original="./libburn_svn_A60815.tgz" +# original="./libburn_cdrskin_A60819.tgz" + +# The top level directory in that snapshot is named +intermediate="./libburn_pykix" + +# My changes are in libburn-0.2.3.ts.develop , mainly in ./cdrskin + +changes="./libburn-0.2.3.ts.develop" +skin_rev="0.2.7" + +# The result directory and the name of the result tarballs +target="./cdrskin-${skin_rev}" +cdrskin_tarball="./cdrskin-${skin_rev}.tar.gz" +cdrskin_tarball_svn="./cdrskin-${skin_rev}.svn.tar.gz" + +# (This once earned me an embarrassingly blooping source tarball) +# compile_dir="$changes" + +compile_dir="$target" +compile_cmd="./cdrskin/compile_cdrskin.sh" +compile_static_opts="-static" +compile_result="cdrskin/cdrskin" + +man_to_html_cmd="./cdrskin/convert_man_to_html.sh" +man_page_html="cdrskin/man_1_cdrskin.html" + +bintarget_dynamic="cdrskin_${skin_rev}-x86-suse9_0" +bintarget_static="$bintarget_dynamic"-static + +if test -d "$changes" +then + dummy=dummy +else + echo "$0 : FATAL : no directory $changes" >&2 + exit 1 +fi + +for i in "$target" "$intermediate" +do + if test -e "$i" + then + echo "$0 : FATAL : already existing $i" >&2 + exit 2 + fi +done + +if test -f "$original" +then + dummy=dummy +else + echo "$0 : FATAL : no file $original" >&2 + exit 3 +fi + + +# Unpack SVN snapshot. +tar xzf "$original" + + +# Rename the directory to the cdrskin name +mv "$intermediate" "$target" + + +# Copy the changes from the development tree +# +cdrskin_dir="$changes"/cdrskin +libburn_dir="$changes"/libburn +cdrskin_target="$target"/cdrskin +libburn_target="$target"/libburn + +# Create version timestamp +timestamp="$(date -u '+%Y.%m.%d.%H%M%S')" +echo "$timestamp" +echo '#define Cdrskin_timestamP "'"$timestamp"'"' >"$cdrskin_dir"/cdrskin_timestamp.h + +# Add the cdrskin files +if test -e "$cdrskin_target" +then + rm -rf "$cdrskin_target" +fi +cp -a "$cdrskin_dir" "$cdrskin_target" + +# Remove copied binaries +rm "$cdrskin_target"/*.o +rm "$cdrskin_target"/cdrfifo +rm "$cdrskin_target"/cdrskin +rm "$cdrskin_target"/cleanup +for i in std new make old +do + if test -e "$cdrskin_target"/cdrskin_"$i" + then + rm "$cdrskin_target"/cdrskin_"$i" + fi +done + +# Remove automatically generated HTML man page +rm "$cdrskin_target"/man_1_cdrskin.html + +# Remove eventual SVN stuff from cdrskin driectory +for i in .deps .dirstamp .libs +do + if test -e "$cdrskin_target"/"$i" + then + rm -rf "$cdrskin_target"/"$i" + fi +done + +# Remove GIFs of cdrskin_eng.html +rm "$cdrskin_target"/doener_*.gif + +# Remove unwanted SVN stuff (TODO: avoid downloading it) +for i in "$target"/.svn "$target"/*/.svn +do + if test "$i" = "$target"'/*/.svn' + then + dummy=dummy + else + if test -e "$i" + then + rm -rf "$i" + fi + fi +done + +## No more : Add own libburn-README in toplevel +# cp -a "$changes"/README "$target" + +## No more : Add modified Makefile.am +# cp -a "$changes"/Makefile.am "$target" + + +# Make SVN state tarball for the libburn team +tar czf "$cdrskin_tarball_svn" "$target" + + +# Get over dependecy on autotools. Rely only on cc, make et. al. +# This is not the same as "make dist" but i can do it without +# having to evaluate the quality of said "make dist" +# +( cd "$target" ; ./bootstrap ) + +# Remove unwanted stuff after bootstrap +for i in "$target"/autom4te.cache +do + if echo "$i" | grep '\*' >/dev/null + then + dummy=dummy + else + if test -e "$i" + then + rm -rf "$i" + fi + fi +done + + +# Pack it up to the new libburn+cdrskin-tarball +tar czf "$cdrskin_tarball" "$target" + +# Produce a static and a dynamic binary, and a HTML man page +( + cd "$compile_dir" || exit 1 + ./configure + make + "$compile_cmd" -do_strip + cp "$compile_result" "../$bintarget_dynamic" + if test -n "$compile_static_opts" + then + "$compile_cmd" $compile_static_opts -do_strip + cp "$compile_result" "../$bintarget_static" + fi + "$man_to_html_cmd" + mv "$man_page_html" .. +) + +# Remove the build area +# Disable this for debugging the merge process +rm -rf "$target" + +# Show the result +./"$bintarget_dynamic" -version +./"$bintarget_static" -version +ls -l "$cdrskin_tarball" +ls -l "$bintarget_dynamic" +ls -l "$bintarget_static" +ls -l $(basename "$man_page_html") + diff --git a/libburn/tags/ZeroThreeZero/cdrskin/cdrecord_spy.sh b/libburn/tags/ZeroThreeZero/cdrskin/cdrecord_spy.sh new file mode 100755 index 00000000..54d7c344 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/cdrecord_spy.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# +# Spying on the call to cdrecord. +# +# Move $(which cdrecord) to $(dirname $(which cdrecord))/real_cdrecord . +# Install this sript instead. (Do not forget to revoke this after the test.) +# + +# The report target is set in variable rt. +# The default is this file : +rt=/tmp/cdrecord_spy_log + +# To use a bystanding xterm as target i find out the pty address by +# executing in that terminal +# sleep 12345 +# and then running in another terminal +# ps -ef | grep 'sleep 12345' +# which answers something like +# thomas 21303 30518 0 14:02 pts/23 00:00:00 sleep 12345 +# thomas 21421 30523 0 14:02 pts/24 00:00:00 grep sleep 12345 +# from which i learn that pts/23 is sleeping 12345. Now sleep can be aborted. +# +# rt=/dev/pts/23 + +echo '------------------------------------- cdrecord_spy 0.1.0 -------' >>"$rt" +date >>"$rt" +echo '----------------------------------------------------------------' >>"$rt" +echo "$0" >>"$rt" +for i in "$@" +do + echo "$i" >>"$rt" +done +echo '------------------------------------- cdrecord_spy 0.1.0 - end -' >>"$rt" + +real_cdrecord "$@" + + diff --git a/libburn/tags/ZeroThreeZero/cdrskin/cdrfifo.c b/libburn/tags/ZeroThreeZero/cdrskin/cdrfifo.c new file mode 100644 index 00000000..13e48206 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/cdrfifo.c @@ -0,0 +1,1083 @@ +/* + cdrfifo.c , Copyright 2006 Thomas Schmitt + + A fd-to-fd or fd-to-memory fifo to be used within cdrskin or independently. + By chaining of fifo objects, several fifos can be run simultaneously + in fd-to-fd mode. Modes are controlled by parameter flag of + Cdrfifo_try_to_work(). + + Provided under GPL license within cdrskin and under BSD license elsewise. +*/ + +/* + Compile as standalone tool : + cc -g -o cdrfifo -DCdrfifo_standalonE cdrfifo.c +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdrfifo.h" + + +/* Macro for creation of arrays of objects (or single objects) */ +#define TSOB_FELD(typ,anz) (typ *) malloc((anz)*sizeof(typ)); + + +#define Cdrfifo_buffer_chunK 2048 + +/** Number of follow-up fd pairs */ +#define Cdrfifo_ffd_maX 100 + + +/* 1= enable , 0= disable status messages to stderr */ +static int Cdrfifo_debuG= 0; + + +struct CdrfifO { + int chunk_size; + + int source_fd; + double in_counter; + + double fd_in_counter; + double fd_in_limit; + + char *buffer; + int buffer_size; + int buffer_is_full; + int write_idx; + int read_idx; + + int dest_fd; + double out_counter; + + struct timeval start_time; + double speed_limit; + + /* statistics */ + double interval_counter; + struct timeval interval_start_time; + double interval_start_counter; + int total_min_fill; + int interval_min_fill; + + double put_counter; + double get_counter; + double empty_counter; + double full_counter; + + + /* (sequential) fd chaining */ + /* fds: 0=source, 1=dest */ + int follow_up_fds[Cdrfifo_ffd_maX][2]; + + /* index of first byte in buffer which does not belong to predecessor fd */ + int follow_up_eop[Cdrfifo_ffd_maX]; + + /* index of first byte in buffer which belongs to [this] fd pair */ + int follow_up_sod[Cdrfifo_ffd_maX]; + + /* values for fd_in_limit */ + double follow_up_in_limits[Cdrfifo_ffd_maX]; + + /* number of defined follow-ups */ + int follow_up_fd_counter; + + /* index of currently active (i.e. reading) follow-up */ + int follow_up_fd_idx; + + + /* (simultaneous) peer chaining */ + struct CdrfifO *next; + struct CdrfifO *prev; +}; + + +/** Create a fifo object. + @param ff Returns the address of the new object. + @param source_fd Filedescriptor opened to a readable data stream. + @param dest_fd Filedescriptor opened to a writable data stream. + To work with libburn, it needs to be attached to a + struct burn_source object. + @param chunk_size Size of buffer block for a single transaction (0=default) + @param buffer_size Size of fifo buffer + @param flag Unused yet + @return 1 on success, <=0 on failure +*/ +int Cdrfifo_new(struct CdrfifO **ff, int source_fd, int dest_fd, + int chunk_size, int buffer_size, int flag) +{ + struct CdrfifO *o; + struct timezone tz; + int i; + + (*ff)= o= TSOB_FELD(struct CdrfifO,1); + if(o==NULL) + return(-1); + if(chunk_size<=0) + chunk_size= Cdrfifo_buffer_chunK; + o->chunk_size= chunk_size; + if(buffer_size%chunk_size) + buffer_size+= chunk_size-(buffer_size%chunk_size); + o->source_fd= source_fd; + o->in_counter= 0.0; + o->fd_in_counter= 0; + o->fd_in_limit= -1.0; + o->buffer= NULL; + o->buffer_is_full= 0; + o->buffer_size= buffer_size; + o->write_idx= 0; + o->read_idx= 0; + o->dest_fd= dest_fd; + o->out_counter= 0.0; + memset(&(o->start_time),0,sizeof(o->start_time)); + gettimeofday(&(o->start_time),&tz); + o->speed_limit= 0.0; + o->interval_counter= 0.0; + memset(&(o->interval_start_time),0,sizeof(o->interval_start_time)); + gettimeofday(&(o->interval_start_time),&tz); + o->interval_start_counter= 0.0; + o->total_min_fill= buffer_size; + o->interval_min_fill= buffer_size; + o->put_counter= 0.0; + o->get_counter= 0.0; + o->empty_counter= 0.0; + o->full_counter= 0.0; + for(i= 0; ifollow_up_fds[i][0]= o->follow_up_fds[i][1]= -1; + o->follow_up_eop[i]= o->follow_up_sod[i]= -1; + o->follow_up_in_limits[i]= -1.0; + } + o->follow_up_fd_counter= 0; + o->follow_up_fd_idx= -1; + o->next= o->prev= NULL; + o->buffer= TSOB_FELD(char,buffer_size); + if(o->buffer==NULL) + goto failed; + return(1); +failed:; + Cdrfifo_destroy(ff,0); + return(-1); +} + + +/** Close any output fds */ +int Cdrfifo_close(struct CdrfifO *o, int flag) +{ + int i; + + if(o->dest_fd!=-1) + close(o->dest_fd); + o->dest_fd= -1; + for(i=0; ifollow_up_fd_counter; i++) + if(o->follow_up_fds[i][1]!=-1) + close(o->follow_up_fds[i][1]); + o->follow_up_fds[i][1]= -1; + return(1); +} + + +/** Release from memory a fifo object previously created by Cdrfifo_new(). + @param ff The victim (gets returned as NULL, call can stand *ff==NULL)) + @param flag Bitfield for control purposes: + bit0= do not close destination fd +*/ +int Cdrfifo_destroy(struct CdrfifO **ff, int flag) +/* flag + bit0= do not close destination fd +*/ +{ + struct CdrfifO *o; + + o= *ff; + if(o==NULL) + return(0); + if(o->next!=NULL) + o->next->prev= o->prev; + if(o->prev!=NULL) + o->prev->next= o->next; + if(!(flag&1)) + Cdrfifo_close(o,0); + + /* eventual closing of source fds is the job of the calling application */ + + if(o->buffer!=NULL) + free((char *) o->buffer); + free((char *) o); + (*ff)= NULL; + return(1); +} + + +int Cdrfifo_get_sizes(struct CdrfifO *o, int *chunk_size, int *buffer_size, + int flag) +{ + *chunk_size= o->chunk_size; + *buffer_size= o->buffer_size; + return(1); +} + +/** Set a speed limit for buffer output. + @param o The fifo object + @param bytes_per_second >0 catch up slowdowns over the whole run time + <0 catch up slowdowns only over one interval + =0 disable speed limit +*/ +int Cdrfifo_set_speed_limit(struct CdrfifO *o, double bytes_per_second, + int flag) +{ + o->speed_limit= bytes_per_second; + return(1); +} + + +/** Set a fixed size for input in order to cut off any unwanted tail + @param o The fifo object + @param idx index for fds attached via Cdrfifo_attach_follow_up_fds(), + first attached is 0, <0 directs limit to active fd limit + (i.e. first track is -1, second track is 0, third is 1, ...) +*/ +int Cdrfifo_set_fd_in_limit(struct CdrfifO *o, double fd_in_limit, int idx, + int flag) +{ + if(idx<0) { + o->fd_in_limit= fd_in_limit; + return(1); + } + if(idx >= o->follow_up_fd_counter) + return(0); + o->follow_up_in_limits[idx]= fd_in_limit; + return(1); +} + + +int Cdrfifo_set_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag) +{ + o->source_fd= source_fd; + o->dest_fd= dest_fd; + return(1); +} + + +int Cdrfifo_get_fds(struct CdrfifO *o, int *source_fd, int *dest_fd, int flag) +{ + *source_fd= o->source_fd; + *dest_fd= o->dest_fd; + return(1); +} + + +/** Attach a further pair of input and output fd which will use the same + fifo buffer when its predecessors are exhausted. Reading will start as + soon as reading of the predecessor encounters EOF. Writing will start + as soon as all pending predecessor data are written. + @return index number of new item + 1, <=0 indicates error +*/ +int Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd, + int flag) +{ + if(o->follow_up_fd_counter>=Cdrfifo_ffd_maX) + return(0); + o->follow_up_fds[o->follow_up_fd_counter][0]= source_fd; + o->follow_up_fds[o->follow_up_fd_counter][1]= dest_fd; + o->follow_up_fd_counter++; + return(o->follow_up_fd_counter); +} + + +/** Attach a further fifo which shall be processed simultaneously with this + one by Cdrfifo_try_to_work() in fd-to-fd mode. +*/ +int Cdrfifo_attach_peer(struct CdrfifO *o, struct CdrfifO *next, int flag) +{ + for(;o->next!=NULL;o= o->next); /* determine end of o-chain */ + for(;next->prev!=NULL;next= next->prev); /* determine start of next-chain */ + next->prev= o; + o->next= next; + return(1); +} + + +static int Cdrfifo_tell_buffer_space(struct CdrfifO *o, int flag) +{ + if(o->buffer_is_full) + return(0); + if(o->write_idx>=o->read_idx) + return((o->buffer_size - o->write_idx) + o->read_idx); + return(o->read_idx - o->write_idx); +} + + +/** Obtain buffer state. + @param o The buffer object + @param fill Returns the number of pending payload bytes in the buffer + @param space Returns the number of unused buffer bytes + @param flag Unused yet + @return -1=error , 0=inactive , 1=reading and writing , + 2=reading ended (but still writing) +*/ +int Cdrfifo_get_buffer_state(struct CdrfifO *o,int *fill,int *space,int flag) +/* return : + -1=error + 0=inactive + 1=reading and writing + 2=reading ended, still writing +*/ +{ + *space= Cdrfifo_tell_buffer_space(o,0); + *fill= o->buffer_size-(*space); + if(o->dest_fd==-1) + return(0); + if(o->source_fd<0) + return(2); + return(1); +} + + +int Cdrfifo_get_counters(struct CdrfifO *o, + double *in_counter, double *out_counter, int flag) +{ + *in_counter= o->in_counter; + *out_counter= o->out_counter; + return(1); +} + + +/** reads min_fill and begins measurement interval for next min_fill */ +int Cdrfifo_next_interval(struct CdrfifO *o, int *min_fill, int flag) +{ + struct timezone tz; + + o->interval_counter++; + gettimeofday(&(o->interval_start_time),&tz); + o->interval_start_counter= o->out_counter; + *min_fill= o->interval_min_fill; + o->interval_min_fill= o->buffer_size - Cdrfifo_tell_buffer_space(o,0); + return(1); +} + + +int Cdrfifo_get_min_fill(struct CdrfifO *o, int *total_min_fill, + int *interval_min_fill, int flag) +{ + *total_min_fill= o->total_min_fill; + *interval_min_fill= o->interval_min_fill; + return(1); +} + + +/** Get counters which are mentioned by cdrecord at the end of burning. + It still has to be examined wether they mean what i believe they do. +*/ +int Cdrfifo_get_cdr_counters(struct CdrfifO *o, + double *put_counter, double *get_counter, + double *empty_counter, double *full_counter, + int flag) +{ + *put_counter= o->put_counter;; + *get_counter= o->get_counter; + *empty_counter= o->empty_counter; + *full_counter= o->full_counter; + return(1); +} + + +/** Adjust a given buffer fill value so it will not cross an eop boundary. + @param o The fifo to exploit. + @param buffer_fill The byte count to adjust. + @param eop_idx If eop boundary exactly hit: index of follow-up fd pair + @param flag Unused yet. + @return 0= nothing changed , 1= buffer_fill adjusted +*/ +int Cdrfifo_eop_adjust(struct CdrfifO *o,int *buffer_fill, int *eop_idx, + int flag) +{ + int i,eop_is_near= 0,valid_fill; + + *eop_idx= -1; + valid_fill= *buffer_fill; + for(i=0; i<=o->follow_up_fd_idx; i++) { + if(o->follow_up_eop[i]>=0 && o->follow_up_eop[i]>=o->read_idx) { + eop_is_near= 1; + valid_fill= o->follow_up_eop[i]-o->read_idx; + if(valid_fill==0) + *eop_idx= i; + else if(valid_fill<=o->chunk_size) + eop_is_near= 2; /* for debugging. to carry a break point */ + break; + } + } + if(*buffer_fill>valid_fill) + *buffer_fill= valid_fill; + return(!!eop_is_near); +} + + +/* Perform pre-select activities of Cdrfifo_try_to_work() */ +static int Cdrfifo_setup_try(struct CdrfifO *o, struct timeval start_tv, + double start_out_counter, int *still_to_wait, + int *speed_limiter, int *ready_to_write, + fd_set *rds, fd_set *wts, int *max_fd, int flag) +/* flag: + bit0= enable debug pacifier (same with Cdrfifo_debuG) + bit1= do not write, just fill buffer + bit2= fd-to-memory mode (else fd-to-fd mode): + rather than writing a chunk return it and its size in reply_* + bit3= with bit2: do not check destination fd for readiness +*/ +{ + int buffer_space,buffer_fill,eop_reached= -1,eop_is_near= 0,was_closed; + int fd_buffer_fill; + struct timeval current_tv; + struct timezone tz; + double diff_time,diff_counter,limit,min_wait_time; + +setup_try:; + buffer_space= Cdrfifo_tell_buffer_space(o,0); + fd_buffer_fill= buffer_fill= o->buffer_size - buffer_space; + +#ifdef NIX + fprintf(stderr,"cdrfifo_debug: o->write_idx=%d o->read_idx=%d o->source_fd=%d\n",o->write_idx,o->read_idx,o->source_fd); + if(buffer_fill>10) + sleep(1); +#endif + + if(o->follow_up_fd_idx>=0) + eop_is_near= Cdrfifo_eop_adjust(o,&fd_buffer_fill,&eop_reached,0); + + if(fd_buffer_fill<=0 && (o->source_fd==-1 || eop_reached>=0) ) { + was_closed= 0; + if(o->dest_fd!=-1 && !(flag&4)) + close(o->dest_fd); + if(o->dest_fd<0) + was_closed= 1; + else + o->dest_fd= -1; + + if(eop_reached>=0) { /* switch to next output fd */ + o->dest_fd= o->follow_up_fds[eop_reached][1]; + o->read_idx= o->follow_up_sod[eop_reached]; + o->follow_up_eop[eop_reached]= -1; + eop_is_near= 0; + eop_reached= -1; + goto setup_try; + } else { + /* work is really done */ + if((!was_closed) && ((flag&1)||Cdrfifo_debuG)) + fprintf(stderr, + "\ncdrfifo_debug: w=%d r=%d | b=%d s=%d | i=%.f o=%.f (done)\n", + o->write_idx,o->read_idx,buffer_fill,buffer_space, + o->in_counter,o->out_counter); + return(2); + } + } + if(o->interval_counter>0) { + if(o->total_min_fill>buffer_fill && o->source_fd>=0) + o->total_min_fill= buffer_fill; + if(o->interval_min_fill>buffer_fill) + o->interval_min_fill= buffer_fill; + } + *speed_limiter= 0; + if(o->speed_limit!=0) { + gettimeofday(¤t_tv,&tz); + if(o->speed_limit>0) { + diff_time= ((double) current_tv.tv_sec)-((double) o->start_time.tv_sec)+ + (((double) current_tv.tv_usec)-((double) o->start_time.tv_usec))*1e-6; + diff_counter= o->out_counter; + limit= o->speed_limit; + } else if(flag&4) { + if(o->interval_start_time.tv_sec==0) + o->interval_start_time= start_tv; + diff_time= ((double) current_tv.tv_sec) + - ((double) o->interval_start_time.tv_sec) + + (((double) current_tv.tv_usec) + -((double) o->interval_start_time.tv_usec))*1e-6; + diff_counter= o->out_counter - o->interval_start_counter; + limit= -o->speed_limit; + } else { + diff_time= ((double) current_tv.tv_sec) - ((double) start_tv.tv_sec) + + (((double) current_tv.tv_usec) + -((double)start_tv.tv_usec))*1e-6; + diff_counter= o->out_counter - start_out_counter; + limit= -o->speed_limit; + } + if(diff_time>0.0) + if(diff_counter/diff_time>limit) { + min_wait_time= (diff_counter/limit - diff_time)*1.0e6; + if(min_wait_time<*still_to_wait) + *still_to_wait= min_wait_time; + if(*still_to_wait>0) + *speed_limiter= 1; + } + } + if(o->source_fd>=0) { + if(buffer_space>0) { + FD_SET((o->source_fd),rds); + if(*max_fdsource_fd) + *max_fd= o->source_fd; + } else if(o->interval_counter>0) + o->full_counter++; + } + *ready_to_write= 0; + if(o->dest_fd>=0 && (!(flag&2)) && !*speed_limiter) { + if(fd_buffer_fill>=o->chunk_size || o->source_fd<0 || eop_is_near) { + if((flag&(4|8))==(4|8)) { + *still_to_wait= 0; + *ready_to_write= 1; + } else { + FD_SET((o->dest_fd),wts); + if(*max_fddest_fd) + *max_fd= o->dest_fd; + } + } else if(o->interval_counter>0) + o->empty_counter++; + } + return(1); +} + + +/* Perform post-select activities of Cdrfifo_try_to_work() */ +static int Cdrfifo_transact(struct CdrfifO *o, fd_set *rds, fd_set *wts, + char *reply_buffer, int *reply_count, int flag) +/* flag: + bit0= enable debug pacifier (same with Cdrfifo_debuG) + bit1= do not write, just fill buffer + bit2= fd-to-memory mode (else fd-to-fd mode): + rather than writing a chunk return it and its size in reply_* + bit3= with bit2: do not check destination fd for readiness +return: <0 = error , 0 = idle , 1 = did some work +*/ +{ + double buffer_space; + int can_read,can_write,ret,did_work= 0,idx,sod,eop_is_near,eop_idx; + + buffer_space= Cdrfifo_tell_buffer_space(o,0); + if(o->dest_fd>=0) if(FD_ISSET((o->dest_fd),wts)) { + can_write= o->buffer_size - buffer_space; + if(can_write>o->chunk_size) + can_write= o->chunk_size; + if(o->read_idx+can_write > o->buffer_size) + can_write= o->buffer_size - o->read_idx; + if(o->follow_up_fd_idx>=0) { + eop_is_near= Cdrfifo_eop_adjust(o,&can_write,&eop_idx,0); + if(can_write<=0) + goto after_write; + } + if(flag&4) { + memcpy(reply_buffer,o->buffer+o->read_idx,can_write); + *reply_count= ret= can_write; + } else { + ret= write(o->dest_fd,o->buffer+o->read_idx,can_write); + } + if(ret==-1) { + + /* >>> handle broken pipe */; + fprintf(stderr,"\ncdrfifo: on write: errno=%d , \"%s\"\n",errno, + errno==0?"-no error code available-":strerror(errno)); + + if(!(flag&4)) + close(o->dest_fd); + o->dest_fd= -1; + {ret= -1; goto ex;} + } + did_work= 1; + o->get_counter++; + o->out_counter+= can_write; + o->read_idx+= can_write; + if(o->read_idx>=o->buffer_size) + o->read_idx= 0; + o->buffer_is_full= 0; + } +after_write:; + if(o->source_fd>=0) if(FD_ISSET((o->source_fd),rds)) { + can_read= o->buffer_size - o->write_idx; + if(can_read>o->chunk_size) + can_read= o->chunk_size; + if(o->write_idxread_idx && o->write_idx+can_read > o->read_idx) + can_read= o->read_idx - o->write_idx; + if(o->fd_in_limit>=0.0) + if(can_read > o->fd_in_limit - o->fd_in_counter) + can_read= o->fd_in_limit - o->fd_in_counter; + ret= 0; + if(can_read>0) + ret= read(o->source_fd,o->buffer+o->write_idx,can_read); + if(ret==-1) { + + /* >>> handle input error */; + fprintf(stderr,"\ncdrfifo: on read: errno=%d , \"%s\"\n",errno, + errno==0?"-no error code available-":strerror(errno)); + + o->source_fd= -1; + } else if(ret==0) { /* eof */ + /* activate eventual follow-up source fd */ + if(Cdrfifo_debuG || (flag&1)) + fprintf(stderr,"\ncdrfifo: on read(%d,buffer,%d): eof\n", + o->source_fd,can_read); + if(o->follow_up_fd_idx+1 < o->follow_up_fd_counter) { + idx= ++(o->follow_up_fd_idx); + o->source_fd= o->follow_up_fds[idx][0]; + /* End-Of-Previous */ + if(o->write_idx==0) + o->follow_up_eop[idx]= o->buffer_size; + else + o->follow_up_eop[idx]= o->write_idx; + /* Start-Of-Data . Try to start at next full chunk */ + sod= o->write_idx; + if(o->write_idx%o->chunk_size) + sod+= o->chunk_size - (o->write_idx%o->chunk_size); + /* but do not catch up to the read pointer */ + if((o->write_idx<=o->read_idx && o->read_idx<=sod) || sod==o->read_idx) + sod= o->write_idx; + if(sod>=o->buffer_size) + sod= 0; + o->follow_up_sod[idx]= sod; + o->write_idx= sod; + o->fd_in_counter= 0; + o->fd_in_limit= o->follow_up_in_limits[idx]; + if(Cdrfifo_debuG || (flag&1)) + fprintf(stderr,"\ncdrfio: new fifo source fd : %d\n",o->source_fd); + } else { + o->source_fd= -1; + } + } else { + did_work= 1; + o->put_counter++; + o->in_counter+= ret; + o->fd_in_counter+= ret; + o->write_idx+= ret; + if(o->write_idx>=o->buffer_size) + o->write_idx= 0; + if(o->write_idx==o->read_idx) + o->buffer_is_full= 1; + } + } + ret= !!did_work; +ex:; + return(ret); +} + + +/** Check for pending data at the fifo's source file descriptor and wether the + fifo is ready to take them. Simultaneously check the buffer for existing + data and the destination fd for readiness to accept some. If so, a small + chunk of data is transfered to and/or from the fifo. + This is done for the given fifo object and all members of its next-chain. + The check and transactions are repeated until a given timespan has elapsed. + libburn applications call this function in the burn loop instead of sleep(). + It may also be used instead of read(). Then it returns as soon as an output + transaction would be performed. See flag:bit2. + @param o The fifo object + @param wait_usec The time in microseconds after which the function shall + return. + @param reply_buffer with bit2: Returns write-ready buffer chunk and must + be able to take at least chunk_size bytes + @param reply_count with bit2: Returns number of writeable bytes in reply + @param flag Bitfield for control purposes: + bit0= Enable debug pacifier (same with Cdrfifo_debuG) + bit1= Do not write, just fill buffer + bit2= fd-to-memory mode (else fd-to-fd mode): + Rather than writing a chunk return it and its size. + No simultaneous processing of chained fifos. + bit3= With bit2: do not check destination fd for readiness + @return <0 = error , 0 = idle , 1 = did some work , 2 = all work is done +*/ +int Cdrfifo_try_to_work(struct CdrfifO *o, int wait_usec, + char *reply_buffer, int *reply_count, int flag) +{ + struct timeval wt,start_tv,current_tv; + struct timezone tz; + fd_set rds,wts,exs; + int ready,ret,max_fd= -1,buffer_space,dummy,still_active= 0; + int did_work= 0,elapsed,still_to_wait,speed_limiter= 0,ready_to_write= 0; + double start_out_counter; + struct CdrfifO *ff; + + start_out_counter= o->out_counter; + gettimeofday(&start_tv,&tz); + still_to_wait= wait_usec; + if(flag&4) + *reply_count= 0; + +try_again:; + /* is there still a destination open ? */ + for(ff= o; ff!=NULL; ff= ff->next) + if(ff->dest_fd!=-1) + break; + if(ff==NULL) + return(2); + FD_ZERO(&rds); + FD_ZERO(&wts); + FD_ZERO(&exs); + + for(ff= o; ff!=NULL; ff= ff->next) { + ret= Cdrfifo_setup_try(ff,start_tv,start_out_counter, + &still_to_wait,&speed_limiter,&ready_to_write, + &rds,&wts,&max_fd,flag&15); + if(ret<=0) + return(ret); + else if(ret==2) { + /* This fifo is done */; + } else + still_active= 1; + if(flag&2) + break; + } + if(!still_active) + return(2); + + if(still_to_wait>0 || max_fd>=0) { + wt.tv_sec= still_to_wait/1000000; + wt.tv_usec= still_to_wait%1000000; + ready= select(max_fd+1,&rds,&wts,&exs,&wt); + } else + ready= 0; + if(ready<=0) { + if(!ready_to_write) + goto check_wether_done; + FD_ZERO(&rds); + } + if(ready_to_write) + FD_SET((o->dest_fd),&wts); + + for(ff= o; ff!=NULL; ff= ff->next) { + ret= Cdrfifo_transact(ff,&rds,&wts,reply_buffer,reply_count,flag&15); + if(ret<0) + goto ex; + if(ret>0) + did_work= 1; + if(flag&2) + break; + } + +check_wether_done:; + if((flag&4) && *reply_count>0) + {ret= 1; goto ex;} + gettimeofday(¤t_tv,&tz); + elapsed= (current_tv.tv_sec-start_tv.tv_sec)*1000000 + + (((int) current_tv.tv_usec) - ((int) start_tv.tv_usec)); + still_to_wait= wait_usec-elapsed; + if(still_to_wait>0) + goto try_again; + ret= !!did_work; +ex:; + if(flag&4) { + gettimeofday(¤t_tv,&tz); + elapsed= (current_tv.tv_sec - o->interval_start_time.tv_sec)*1000000 + + (((int) current_tv.tv_usec) + - ((int) o->interval_start_time.tv_usec)); + } else + elapsed= wait_usec; + if(elapsed>=wait_usec) { + if((flag&1)||Cdrfifo_debuG) { + fprintf(stderr,"\n"); + for(ff= o; ff!=NULL; ff= ff->next) { + buffer_space= Cdrfifo_tell_buffer_space(ff,0); + fprintf(stderr, + "cdrfifo_debug: w=%d r=%d | b=%d s=%d | i=%.f o=%.f\n", + ff->write_idx,ff->read_idx, + ff->buffer_size-buffer_space,buffer_space, + ff->in_counter,ff->out_counter); + } + } + if(flag&4) + Cdrfifo_next_interval(o,&dummy,0); + } + return(ret); +} + + +/** Fill the fifo as far as possible without writing to destination fd */ +int Cdrfifo_fill(struct CdrfifO *o, int size, int flag) +{ + int ret,fill= 0,space,state; + + while(1) { + state= Cdrfifo_get_buffer_state(o,&fill,&space,0); + if(state==-1) { + + /* >>> handle error */; + + return(0); + } else if(state!=1) + break; + if(space<=0) + break; + if(size>=0 && fill>=size) + break; + ret= Cdrfifo_try_to_work(o,100000,NULL,NULL,2); + if(ret<0) { + + /* >>> handle error */; + + return(0); + } + if(ret==2) + break; + } + o->total_min_fill= fill; + o->interval_min_fill= fill; + return(1); +} + + +int Cdrfifo_close_all(struct CdrfifO *o, int flag) +{ + struct CdrfifO *ff; + + if(o==NULL) + return(0); + for(ff= o; ff->prev!=NULL; ff= ff->prev); + for(; ff!=NULL; ff= ff->next) + Cdrfifo_close(ff,0); + return(1); +} + + + +#ifdef Cdrfifo_standalonE + +/* ---------------------------------------------------------------------- */ + +/** Application example. See also cdrskin.c */ + + +double Scanf_io_size(char *text, int flag) +/* + bit0= default value -1 rather than 0 +*/ +{ + int c; + double ret= 0.0; + + if(flag&1) + ret= -1.0; + if(text[0]==0) + return(ret); + sscanf(text,"%lf",&ret); + c= text[strlen(text)-1]; + if(c=='k' || c=='K') ret*= 1024.0; + if(c=='m' || c=='M') ret*= 1024.0*1024.0; + if(c=='g' || c=='G') ret*= 1024.0*1024.0*1024.0; + if(c=='t' || c=='T') ret*= 1024.0*1024.0*1024.0*1024.0; + if(c=='p' || c=='P') ret*= 1024.0*1024.0*1024.0*1024.0*1024.0; + if(c=='e' || c=='E') ret*= 1024.0*1024.0*1024.0*1024.0*1024.0*1024.0; + if(c=='s' || c=='S') ret*= 2048.0; + return(ret); +} + + +/* This is a hardcoded test mock-up for two simultaneous fifos of which the + first one simulates the cdrskin fifo feeding libburn and the second one + simulates libburn and the burner at given speed. Both have two fd pairs + (i.e. tracks). The tracks are read from /u/test/cdrskin/in_[12] and + written to /u/test/cdrskin/out_[12]. +*/ +int Test_multi(int fs_size, double speed_limit, double interval, int flag) +/* + bit0= debugging verbousity +*/ +{ + int fd_in[4],fd_out[4],ret,pipe_fds[4][2],real_out[4],pipe_idx; + int i,iv; + char buf[10240]; + struct CdrfifO *ff1= NULL,*ff2= NULL; + + /* open four pairs of fds */ + fd_in[0]= open("/u/test/cdrskin/in_1",O_RDONLY); + fd_in[1]= open("/u/test/cdrskin/in_2",O_RDONLY); + fd_out[2]= open("/u/test/cdrskin/out_1", + O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); + fd_out[3]= open("/u/test/cdrskin/out_2", + O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); + if(pipe(pipe_fds[0])==-1) + return(-3); + if(pipe(pipe_fds[1])==-1) + return(-3); + fd_out[0]= pipe_fds[0][1]; + fd_out[1]= pipe_fds[1][1]; + fd_in[2]= pipe_fds[0][0]; + fd_in[3]= pipe_fds[1][0]; + for(i=0;i<4;i++) { + if(fd_in[i]==-1) + return(-1); + if(fd_out[i]==-1) + return(-2); + } + + /* Create two fifos with two sequential fd pairs each and chain them for + simultaneous usage. */ + Cdrfifo_new(&ff1,fd_in[0],fd_out[0],2048,fs_size,0); + Cdrfifo_new(&ff2,fd_in[2],fd_out[2],2048,2*1024*1024,0); /*burner cache 2 MB*/ + if(ff1==NULL || ff2==NULL) + return(-3); + Cdrfifo_set_speed_limit(ff2,speed_limit,0); + ret= Cdrfifo_attach_follow_up_fds(ff1,fd_in[1],fd_out[1],0); + if(ret<=0) + return(-4); + ret= Cdrfifo_attach_follow_up_fds(ff2,fd_in[3],fd_out[3],0); + if(ret<=0) + return(-4); + Cdrfifo_attach_peer(ff1,ff2,0); + + /* Let the fifos work */ + iv= interval*1e6; + while(1) { + ret= Cdrfifo_try_to_work(ff1,iv,NULL,NULL,flag&1); + if(ret<0 || ret==2) { /* <0 = error , 2 = work is done */ + fprintf(stderr,"\ncdrfifo: fifo ended work with ret=%d\n",ret); + if(ret<0) + return(-7); + break; + } + } + return(1); +} + + +int main(int argc, char **argv) +{ + int i,ret,exit_value= 0,verbous= 1,fill_buffer= 0,min_fill,fifo_percent,fd; + double fs_value= 4.0*1024.0*1024.0,bs_value= 2048,in_counter,out_counter; + double interval= 1.0,speed_limit= 0.0; + char output_file[4096]; + struct CdrfifO *ff= NULL; + + strcpy(output_file,"-"); + fd= 1; + + for(i= 1; i1000.0) + interval= 1; + } else if(strncmp(argv[i],"of=",3)==0) { + if(strcmp(argv[i]+3,"-")==0 || argv[i][3]==0) + continue; + fd= open(argv[i]+3,O_WRONLY|O_CREAT); + if(fd<0) { + fprintf(stderr,"cdrfifo: FATAL : cannot open output file '%s'\n", + argv[i]+3); + fprintf(stderr,"cdrfifo: errno=%d , \"%s\"\n", + errno,errno==0?"-no error code available-":strerror(errno)); + {exit_value= 4; goto ex;} + } + } else if(strncmp(argv[i],"sl=",3)==0) { + speed_limit= Scanf_io_size(argv[i]+3,0); + } else if(strncmp(argv[i],"vb=",3)==0) { + sscanf(argv[i]+3,"%d",&verbous); + + } else if(strcmp(argv[i],"-multi_test")==0) { + + if(speed_limit==0.0) + speed_limit= 10*150*1024; + ret= Test_multi((int) fs_value,speed_limit,interval,(verbous>=2)); + fprintf(stderr,"Test_multi(): ret= %d\n",ret); + exit(ret<0); + + } else { + fprintf(stderr,"cdrfifo 0.3 : stdin-to-stdout fifo buffer.\n"); + fprintf(stderr,"usage : %s [bs=block_size] [fl=fillfirst]\n",argv[0]); + fprintf(stderr," [fs=fifo_size] [iv=interval] [of=output_file]\n"); + fprintf(stderr," [sl=bytes_per_second_limit] [vb=verbosity]\n"); + fprintf(stderr,"fl=1 reads full buffer before writing starts.\n"); + fprintf(stderr,"sl>0 allows catch up for whole run time.\n"); + fprintf(stderr,"sl<0 allows catch up for single interval.\n"); + fprintf(stderr,"vb=0 is silent, vb=2 is debug.\n"); + fprintf(stderr,"example: cdrfifo bs=8k fl=1 fs=32m iv=0.1 sl=-5400k\n"); + if(strcmp(argv[i],"-help")!=0 && strcmp(argv[i],"--help")!=0) { + fprintf(stderr,"\ncdrfifo: FATAL : option not recognized: '%s'\n", + argv[i]); + exit_value= 1; + } + goto ex; + } + } + if(verbous>=1) { + fprintf(stderr, + "cdrfifo: bs=%.lf fl=%d fs=%.lf iv=%lf of='%s' sl=%.lf vb=%d\n", + bs_value,fill_buffer,fs_value,interval,output_file,speed_limit, + verbous); + } + + ret= Cdrfifo_new(&ff,0,fd,(int) bs_value,(int) fs_value,0); + if(ret<=0) { + fprintf(stderr, + "cdrfifo: FATAL : creation of fifo object with %.lf bytes failed\n", + fs_value); + {exit_value= 3; goto ex;} + } + if(speed_limit!=0.0) + Cdrfifo_set_speed_limit(ff,speed_limit,0); + if(fill_buffer) { + ret= Cdrfifo_fill(ff,0); + if(ret<=0) { + fprintf(stderr, + "cdrfifo: FATAL : initial filling of fifo buffer failed\n"); + {exit_value= 4; goto ex;} + } + } + while(1) { + ret= Cdrfifo_try_to_work(ff,(int) (interval*1000000.0), + NULL,NULL,(verbous>=2)); + if(ret<0) { + fprintf(stderr,"\ncdrfifo: FATAL : fifo aborted. errno=%d , \"%s\"\n", + errno,errno==0?"-no error code available-":strerror(errno)); + {exit_value= 4; goto ex;} + } else if(ret==2) { + if(verbous>=1) { + double put_counter,get_counter,empty_counter,full_counter; + int total_min_fill; + Cdrfifo_get_counters(ff,&in_counter,&out_counter,0); + fprintf(stderr,"\ncdrfifo: done : %.lf bytes in , %.lf bytes out\n", + in_counter,out_counter); + Cdrfifo_get_min_fill(ff,&total_min_fill,&min_fill,0); + fifo_percent= 100.0*((double) total_min_fill)/fs_value; + if(fifo_percent==0 && total_min_fill>0) + fifo_percent= 1; + Cdrfifo_get_cdr_counters(ff,&put_counter,&get_counter, + &empty_counter,&full_counter,0); + fprintf(stderr,"cdrfifo: fifo had %.lf puts and %.lf gets.\n", + put_counter,get_counter); + fprintf(stderr, +"cdrfifo: fifo was %.lf times empty and %.lf times full, min fill was %d%%.\n", + empty_counter,full_counter,fifo_percent); + } + break; + } + Cdrfifo_next_interval(ff,&min_fill,0); + } + +ex:; + Cdrfifo_destroy(&ff,0); + exit(exit_value); +} + + +#endif /* Cdrfifo_standalonE */ + diff --git a/libburn/tags/ZeroThreeZero/cdrskin/cdrfifo.h b/libburn/tags/ZeroThreeZero/cdrskin/cdrfifo.h new file mode 100644 index 00000000..2c702ee0 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/cdrfifo.h @@ -0,0 +1,156 @@ + +/* + cdrfifo.c , Copyright 2006 Thomas Schmitt + + A fd-to-fd or fd-to-memory fifo to be used within cdrskin or independently. + By chaining of fifo objects, several fifos can be run simultaneously + in fd-to-fd mode. Modes are controlled by parameter flag of + Cdrfifo_try_to_work(). + + Provided under GPL license within cdrskin and under BSD license elsewise. +*/ + +#ifndef Cdrfifo_headerfile_includeD +#define Cdrfifo_headerfile_includeD + + +/** The fifo buffer which will smoothen the data stream from data provider + to data consumer. Although this is not a mandatory lifesaver for modern + burners any more, a fifo can speed up burning of data which is delivered + with varying bandwidths (e.g. compressed archives created on the fly + or mkisofs running at its speed limit.). + This structure is opaque to applications and may only be used via + the Cdrfifo*() methods described in cdrfifo.h . +*/ +struct CdrfifO; + + +/** Create a fifo object. + @param ff Returns the address of the new object. + @param source_fd Filedescriptor opened to a readable data stream. + @param dest_fd Filedescriptor opened to a writable data stream. + To work with libburn, it needs to be attached to a + struct burn_source object. + @param chunk_size Size of buffer block for a single transaction (0=default) + @param buffer_size Size of fifo buffer + @param flag unused yet + @return 1 on success, <=0 on failure +*/ +int Cdrfifo_new(struct CdrfifO **ff, int source_fd, int dest_fd, + int chunk_size, int buffer_size, int flag); + +/** Release from memory a fifo object previously created by Cdrfifo_new(). + @param ff The victim (gets returned as NULL, call can stand *ff==NULL) + @param flag Bitfield for control purposes: + bit0= do not close destination fd +*/ +int Cdrfifo_destroy(struct CdrfifO **ff, int flag); + +/** Close any output fds */ +int Cdrfifo_close(struct CdrfifO *o, int flag); + +/** Close any output fds of o and its chain peers */ +int Cdrfifo_close_all(struct CdrfifO *o, int flag); + +int Cdrfifo_get_sizes(struct CdrfifO *o, int *chunk_size, int *buffer_size, + int flag); + +/** Set a speed limit for buffer output. + @param o The fifo object + @param bytes_per_second >0 catch up slowdowns over the whole run time + <0 catch up slowdowns only over one interval + =0 disable speed limit +*/ +int Cdrfifo_set_speed_limit(struct CdrfifO *o, double bytes_per_second, + int flag); + +/** Set a fixed size for input in order to cut off any unwanted tail + @param o The fifo object + @param idx index for fds attached via Cdrfifo_attach_follow_up_fds(), + first attached is 0, <0 directs limit to active fd limit + (i.e. first track is -1, second track is 0, third is 1, ...) +*/ +int Cdrfifo_set_fd_in_limit(struct CdrfifO *o, double fd_in_limit, int idx, + int flag); + + +int Cdrfifo_set_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag); +int Cdrfifo_get_fds(struct CdrfifO *o, int *source_fd, int *dest_fd, int flag); + + +/** Attach a further pair of input and output fd which will use the same + fifo buffer when its predecessors are exhausted. Reading will start as + soon as reading of the predecessor encounters EOF. Writing will start + as soon as all pending predecessor data are written. + @return index number of new item + 1, <=0 indicates error +*/ +int Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd, + int flag); + +/** Attach a further fifo which shall be processed simultaneously with this + one by Cdrfifo_try_to_work() in fd-to-fd mode. +*/ +int Cdrfifo_attach_peer(struct CdrfifO *o, struct CdrfifO *next, int flag); + + +/** Obtain buffer state. + @param o The buffer object + @param fill Returns the number of pending payload bytes in the buffer + @param space Returns the number of unused buffer bytes + @param flag unused yet + @return -1=error , 0=inactive , 1=reading and writing , + 2=reading ended (but still writing) +*/ +int Cdrfifo_get_buffer_state(struct CdrfifO *o,int *fill,int *space,int flag); + +int Cdrfifo_get_counters(struct CdrfifO *o, + double *in_counter, double *out_counter, int flag); + +/** reads min_fill and begins measurement interval for next min_fill */ +int Cdrfifo_next_interval(struct CdrfifO *o, int *min_fill, int flag); + +int Cdrfifo_get_min_fill(struct CdrfifO *o, int *total_min_fill, + int *interval_min_fill, int flag); + +int Cdrfifo_get_cdr_counters(struct CdrfifO *o, + double *put_counter, double *get_counter, + double *empty_counter, double *full_counter, + int flag); + + +/** Check for pending data at the fifo's source file descriptor and wether the + fifo is ready to take them. Simultaneously check the buffer for existing + data and the destination fd for readiness to accept some. If so, a small + chunk of data is transfered to and/or from the fifo. + This is done for the given fifo object and all members of its next-chain. + The check and transactions are repeated until a given timespan has elapsed. + libburn applications call this function in the burn loop instead of sleep(). + It may also be used instead of read(). Then it returns as soon as an output + transaction would be performed. See flag:bit2. + @param o The fifo object + @param wait_usec The time in microseconds after which the function shall + return. + @param reply_buffer with bit2: Returns write-ready buffer chunk and must + be able to take at least chunk_size bytes + @param reply_count with bit2: Returns number of writeable bytes in reply_pt + @param flag Bitfield for control purposes: + bit0= Enable debug pacifier (same with Cdrfifo_debuG) + bit1= Do not write, just fill buffer + bit2= fd-to-memory mode (else fd-to-fd mode): + Rather than writing a chunk return it and its size. + No simultaneous processing of chained fifos. + bit3= With bit2: do not check destination fd for readiness + @return <0 = error , 0 = idle , 1 = did some work , 2 = all work is done +*/ +int Cdrfifo_try_to_work(struct CdrfifO *o, int wait_usec, + char *reply_buffer, int *reply_count, int flag); + +/** Fill the fifo as far as possible without writing to destination fd. + @param size if >=0 : end filling after the given number of bytes + @return 1 on success, <=0 on failure +*/ +int Cdrfifo_fill(struct CdrfifO *o, int size, int flag); + + +#endif /* Cdrfifo_headerfile_includeD */ + diff --git a/libburn/tags/ZeroThreeZero/cdrskin/cdrskin.1 b/libburn/tags/ZeroThreeZero/cdrskin/cdrskin.1 new file mode 100644 index 00000000..729d97ae --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/cdrskin.1 @@ -0,0 +1,703 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH CDRSKIN 1 "January 12, 2007" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +cdrskin \- burns preformatted data to CD-R, CD-RW, DVD+/-RW, DVD-RAM +via libburn. +.SH SYNOPSIS +.B cdrskin +.RI [ options | track_source_addresses ] +.br +.SH DESCRIPTION +.PP +.\" TeX users may be more comfortable with the \fB\fP and +.\" \fI\fP escape sequences to invode bold face and italics, +.\" respectively. +.PP +\fBcdrskin\fP is a program that provides some of cdrecord's options +in a compatible way for CD media. With DVD it has its own ways for now. +You do not need to be superuser for its daily usage. +.PP +.B Overview of features: +.br +Blanking of CD-RW. +.br +Burning of data or audio tracks to CD, +.br +either in versatile Track at Once mode (TAO) +.br +or in Session at Once mode for seamless tracks. +.br +Multi session on CD (follow-up sessions in TAO only). +.br +Bus scan, burnfree, speed options, retrieving media info, padding, fifo. +.br +Burning of a single data track to DVD+RW, DVD-RW or DVD-RAM. +.br +See section EXAMPLES at the end of this text. +.PP +.B Track recording model: +.br +The input-output entities which get processed are called tracks. +A \fBtrack\fP stores a stream of bytes. +.br +Each track is initiated by one track source address argument, which may either +be "-" for standard input or the address of a readable file. If no write mode +is given explicitely then one will be chosen which matches the peculiarities +of track sources and the state of the output media. +.PP +More than one track can be burned by a single run of cdrskin. +In the terms of the MMC standard all tracks written by the same run constitute +a \fBsession\fP. +.br +CDs can be kept appendable so that further tracks can +be written to them in subsequent runs of cdrskin (see option -multi). +Info about the addresses of burned tracks is kept in a table of +content (TOC) on media and can be retrieved via cdrskin option -toc. +These informations are also used by the operating systems' CD-ROM read drivers. +.PP +In general there are two types of tracks: data and audio. They differ in +sector size, throughput and readability via the systems' CD-ROM drivers +resp. by music CD players. +.br +If not explicitely option -audio is given, then any track is burned as type +data, unless the track source is a file with suffix ".wav" or ".au" and has a +header part which identifies it as MS-WAVE resp. SUN Audio with suitable +parameters. Such files are burned as audio tracks by default. +.PP +While audio tracks just contain a given time span of acoustic vibrations, +data tracks may have an arbitray meaning. Nevertheless, ISO-9660 filesystems +are established as a format which can represent a tree of directories and +files on all major operating systems. Such filesystem images can be +produced by programs mkisofs or genisoimage. They can also be extended by +follow-up tracks if prepared properly. See the man pages of said programs. +cdrskin is able to fulfill the needs about their option -C. +.br +Another type of data track content are archive formats which originally +have been developed for magnetic tapes. Only formats which mark a detectable +end-of-archive in their data are suitable, though. Well tested are +the archivers afio and star. Not suitable seems GNU tar. +.PP +.B Recordable CD Media: +.br +CD-R can be initially written only once and eventually extended until they +get closed (or are spoiled because they are overly full). After that they are +read-only. +.br +CD-RW media can be blanked to make them re-usable for another +round of overwriting. Usually +.B blank=fast +is the appropriate option. +Blanking damages the previous content but does not +make it completely unreadable. It is no effective privacy precaution. +Multiple cycles of blanking and overwriting with random numbers might be. +.PP +.B Recordable DVD Media: +.br +Currently only types DVD+RW, DVD-RW and DVD-RAM can be burned via cdrskin. +.br +DVD+RW and DVD-RAM media get treated as blank media regardless wether they +hold data or not. They need no special initial formatting. +Options -audio and -multi are not allowed. Only one track is allowed. +-toc does not return information about the media content. +Speed is counted in DVD units (i.e. 1x = 1,385,000 bytes/second). Currently +there is no difference between -sao and -tao. If ever, then -tao will be the +mode which preserves the current behavior. +.br +DVD-RW need to be formatted to state "Restricted Overwrite". Then they behave +much like DVD+RW. This formatting can be done by option +.B blank=format_overwrite +It is necessary for unused media, for media written or blanked by cdrecord, +for media which have been written unformatted by growisofs or blanked by +dvd+rw-format -blank. If in doubt, just give it a try. +.PP +.B Drive preparation and addressing: +.br +The drives, either CD burners or DVD burners, are accessed via addresses which +are specific to libburn and the operating system. Those addresses get listed +by a run of \fBcdrskin --devices\fP. +.br +On Linux, they are device files which traditionally do not offer +w-permissions for normal users. Because libburn needs rw-permission, +it might be only the superuser who is able to get this list without further +precautions. +.br +It is consensus that \fBchmod a+rw /dev/sg0\fP or \fBchmod a+rw /dev/hdc\fP +is less security sensitive than chmod u+s /usr/bin/cdrskin. The risk for the +drive is somewhat higher but the overall system is much less at stake. +.br +.PP +If you only got one CD capable drive then you may leave out cdrskin option +\fBdev=\fP. Else you should use this option to address the drive you want. +.br +cdrskin option dev= not only accepts the listed addresses but also +traditional cdrecord SCSI addresses which on Linux consist of three +numbers: Bus,Target,Lun. There is also a related address family "ATA" which +accesses IDE drives not under control of Linux SCSI drivers: +ATA:Bus,Target,Lun. +.br +See option -scanbus for getting a list of cdrecord style addresses. +.br +Further are accepted on Linux: links to libburn-suitable device files, +device files which have the same major and minor device number, +and device files which have the same SCSI address parameters (e.g. /dev/sr0). +.br +.SH OPTIONS +.TP +.BI \-\-help +Show non-cdrecord compatible options. +.TP +.BI \-help +Show cdrecord compatible options. +.TP +.BI \-version +Print cdrskin id line, compatibility lure line, libburn version, cdrskin +version, version timestamp, build timestamp (if available), and then exit. +.PP +Alphabetical list of options which are intended to be compatible with +original cdrecord by Joerg Schilling: +.TP +.BI \-atip +Retrieve some info about media state. With CD-RW print "Is erasable". +With DVD media print "book type:" and a media type text. +.TP +.BI \-audio +Announces that the subsequent tracks are to be burned as audio. +The source is supposed to be uncompressed headerless PCM, 44100 Hz, 16 bit, +stereo. For little-endian byte order (which is usual on PCs) use option +-swab. Unless marked explicitely by option -data, input files with suffix +.wav are examined wether they have a header in MS-WAVE format confirming +those parameters and eventually raw audio data get extracted and burned as +audio track. Same is done for suffix .au and SUN Audio. +.TP +.BI blank= type +Blank a CD-RW or format a DVD+/-RW. +This is combinable with burning in the same run of cdrskin. +The type given with blank= selects the particular behavior: +.RS +.TP +help +Print this list of blanking types. +.TP +all +Blank an entire CD. +.TP +fast +Minimally blank an entire CD. +.TP +format_overwrite +Format a DVD-RW to "Restricted Overwrite". The user should bring some patience. +(Note: format_overwrite* are not original cdrecord options.) +.TP +format_overwrite_quickest +Like format_overwrite without creating a 128 MB trailblazer session. +Leads to "intermediate" state which only allows sequential write +beginning from address 0. +The "intermediate" state ends after the first session of writing data. +.TP +format_overwrite_full +For DVD-RW this is like format_overwrite but claims full media size +rather than just 128 MB. +Most traditional formatting is attempted. No data get written. +Much patience is required. +.br +For DVD+RW this is the only supported explicit formatting type. It provides +complete "de-icing" so no reader slips on unwritten data areas. +.RE +.TP +.BI \-checkdrive +Retrieve some info about the addressed drive. +Exits with non-zero value if the drive cannot be found and opened. +.TP +.BI \-dao +Alias for option -sao. Write disk in Session at Once mode. +.TP +.BI \-data +Subsequent tracks are data tracks. This option is default and only needed +to mark the end of the range of an eventual option -audio. +.TP +.BI dev= target +Set the address of the drive to use. Valid are at least the +addresses listed with option --devices, +X,Y,Z addresses listed with option -scanbus, +ATA:X,Y,Z addresses listed with options dev=ATA -scanbus, +and volatile libburn drive numbers (numbering starts at "0"). +Other device file addresses which lead to the same drive might work too. +.br +If no dev= is given, volatile address "dev=0" is assumed. That is the first +drive found being available. Better avoid this ambiguity on systems with more +than one drive. +.br +The special target "help" lists hints about available addressing formats. +Be aware that deprecated option --old_pseudo_scsi_adr may change the meaning +of Bus,Target,Lun addresses. +.TP +.BI driveropts= opt +Set "driveropts=burnfree" to enable the drive's eventual protection mechanism +against temporary lack of source data (i.e. buffer underrun). +It is not an error to do this with a drive that has no such capabilities. +.TP +.BI \-dummy +Try to perform the drive operations without actually affecting the inserted +media. There is no guarantee that this will work with a particular drive +in a particular write mode. Blanking is prevented reliably, though. +.TP +.BI \-eject +Eject the disk after work is done. +.TP +.BI \-force +Assume that the user knows better in situations when cdrskin or libburn are +insecure about drive or media state. This includes the attempt to blank +media which are classified as unknown or unsuitable, and the attempt to use +write modes which libburn believes they are not supported by the drive. +.br +Another application is with blank=format_* to enforce re-formatting of media +which appear to be sufficiently formatted already. +.br +Use this only when in urgent need. +.TP +.BI fs= size +Set the fifo size to the given value. The value may have appended letters which +multiply the preceding number: +.br +"k" or "K" = 1024 , "m" or "M" = 1024k , "g" or "G" = 1024m , "s" or "S" = 2048 +.br +Set size to 0 in order to disable the fifo (default is "4m"). +.br +The fifo buffers an eventual temporary surplus of track source data in order to +provide the drive with a steady stream during times of temporary lack of track +source supply. +The larger the fifo, the longer periods of poor source supply can be +compensated. +But a large fifo needs substantial time to fill up if not curbed via +option fifo_start_at=size. +.TP +.BI gracetime= seconds +Set the grace time before starting to write. (Default is 0) +.TP +.BI msifile= path +Run option -msinfo and copy the result line into the file given by path. +Unlike -msinfo this option does not redirect all normal output away from +standard output. But it may be combined with -msinfo to achieve this. +.br +Note: msifile=path is actually an option of wodim and not of cdrecord. +.TP +.BI \-msinfo +Retrieve multi-session info for preparing a follow-up session by option -C +of programs mkisofs or genisoimage. Print result to standard output. +This option redirects to stderr all +message output besides its own result string, which consists of two numbers. +The result string shall be used as argument of option -C with said programs. +It gives the start address of the most recent session and the predicted +start address of the next session to be appended. The string is empty if +the most recent session was not written with option -multi. +.TP +.BI \-multi +This option keeps the CD appendable after the current session has been written. +Without it the disk gets closed and may not be written any more - unless it +is a CD-RW and gets blanked which causes loss of its content. +.br +The following sessions can only be written in -tao mode. +.br +In order to have all filesystem content accessible, the eventual ISO-9660 +filesystem of a follow-up +session needs to be prepared in a special way by the filesystem formatter +program. mkisofs and genisoimage expect particular info about the situation +which can be retrieved by cdrskin option -msinfo. +.br +To retrieve an archive file which was written as follow-up session, +you may use option -toc to learn about the "lba" of the desired track number. +.TP +.BI \-nopad +Do not add trailing zeros to the data stream. Nevertheless, since there seems +to be no use for audio tracks with incomplete last sector, this option applies +only to data tracks. There it is default. +.TP +.BI \-pad +Add 30 kB of trailing zeros to each data track. (This is not sufficient to +avoid problems with various CD-ROM read drivers.) +.TP +.BI padsize= size +Add the given amount of trailing zeros to the next data track. This option +gets reset to padsize=0 after that next track is written. It may be set +again before the next track argument. About size specifiers, see option fs=. +.TP +.BI \-raw96r +Write disk in RAW/RAW96R mode. This mode allows to put more payload bytes +into a CD sector but obviously at the cost of error correction. It can only +be used for tracks of fixely predicted size. Some drives allow this mode but +then behave strange or even go bad for the next few attempts to burn a CD. +One should use it only if inavoidable. +.TP +.BI \-sao +Write disk in Session At Once mode. This mode is able to put several audio +tracks on CD without producing audible gaps between them. It can only +be used for tracks of fixely predicted size. This implies that track arguments +which depict stdin or named pipes need to be preceeded by option tsize= or +by option tao_to_sao_tsize=. +.TP +.BI \-scanbus +Scan the system for drives. On Linux the drives at /dev/s* and at /dev/hd* +are to be scanned by two separate runs. One without dev= for /dev/s* and +one with dev=ATA for /dev/hd* devices. (Option --drives lists all available +drives in a single run.) +.br +Drives which are busy or which offer no rw-permission to the user of cdrskin +are not listed. Busy drives get reported in form of warning messages. +.br +The useful fields in a result line are: +.br +Bus,Target,Lun Number) 'Vendor' 'Mode' 'Revision' +.TP +.BI speed= number +Set speed of drive. With data CD, 1x speed corresponds to a throughput of +150 kB/s. It is not an error to set a speed higher than is suitable for drive +and media. One should stay within a realistic speed range, though. +.TP +.BI \-swab +Announce that the raw audio data source of subsequent tracks is byte swapped +versus the expectations of cdrecord. This option is suitable for audio where +the least significant byte of a 16 bit word is first (little-endian, Intel). +Most raw audio data on PC systems are available in this byte order. +Less guesswork is needed if track sources are in format MS-WAVE in a file with +suffix ".wav". +.TP +.BI \-tao +Write disk in Track At Once (TAO) mode. This mode can be used with track +sources of unpredictable size, like standard input or named pipes. It is +also the only mode that can be used for writing to appendable CD which +already hold data. +.TP +.BI \-toc +Print the table of content (TOC) which describes the tracks recorded on CD. +The output contains all info from option -atip plus lines which begin with +"track:", the track number, the word "lba:" and a number which gives the +start address of the track. Addresses are counted in CD sectors which with +SAO or TAO data tracks hold 2048 bytes each. +.RS +.TP +Example. Retrieve an afio archive from track number 2: +.br +tracknumber=2 +.br +lba=$(cdrskin dev=/dev/cdrom -toc 2>&1 | \\ +.br +grep '^track:[ ]*[ 0-9][0-9]' | \\ +.br +tail +"$tracknumber" | head -1 | \\ +.br +awk '{ print $4}' ) +.br +dd if=/dev/cdrom bs=2048 skip="$lba" | \\ +.br +afio -t - | less +.RE +.TP +.BI tsize= size +Announces the exact size of the next track source. This is necessary with any +write mode other than -tao if the track source is not a regular disk file, but +e.g. "-" (standard input) or a named pipe. +About size specifiers, see option fs=. +.br +If the track source does not deliver the predicted amount of bytes, the +remainder of the track is padded with zeros. This is not considered an error. +If on the other hand the track source delivers more than the announced bytes +then the track on CD gets truncated to the predicted size and cdrskin exits +with non-zero value. +.TP +.BI \-v +Increment verbose level by one. Startlevel is 0 with only few messages. +Level 1 prints progress report with long running operations and also causes +some extra lines to be put out with info retrieval options. +Level 2 additionally reports about option settings derived from arguments or +startup files. Level 3 is for debugging and useful mainly in conjunction with +somebody who had a look into the program sourcecode. +.PP +Alphabetical list of options which are genuine to cdrskin and intended for +normal use: +.TP +.BI \--allow_setuid +Disable the loud warning about insecure discrepance between login user and +effective user which indicates application of chmod u+s to the program binary. +One should not do this chmod u+s , but it is an old cdrecord tradition. +.TP +.BI \--any_track +Allow source_addresses to begin with "-" (plus further characters) or to +contain a "=" character. +By default such arguments are seen as misspelled options. It is nevertheless +not possible to use one of the options listed with --list_ignored_options. +.TP +.BI \--demand_a_drive +Exit with a nonzero value if no drive can be found during a bus scan. +.TP +.BI \--devices +List the device file addresses of all accessible CD drives. In order to get +listed, a drive has to offer rw-permission for the cdrskin user and it may +not be busy. The superuser should be able to see all idle drives listed and +busy drives reported as "SORRY" messages. +.br +Each available drive gets listed by a line containing the following fields: +.br +Number dev='Devicefile' rw-Permissions : 'Vendor' 'Model' +.br +Number and Devicefile can both be used with option dev=, but number is +volatile (numbering changes if drives become busy). +.TP +.BI fifo_start_at= size +Do not wait for full fifo but start burning as soon as the given number +of bytes is read. This option may be helpful to bring the average throughput +near to the maximum throughput of a drive. A large fs= and a small +fifo_start_at= combine a quick burn start and a large savings buffer to +compensate for temporary lack of source data. At the beginning of burning, +the software protection against buffer underun is as weak as the size of +fifo_start_at= . So it is best if the drive offers hardware protection which +has to be enabled by driveropts=burnfree. +.TP +.BI \--list_ignored_options +List all ignored cdrecord options. The --options cannot be used as addresses +of track sources. No track source address may begin with a text equal to an +option which ends by "=". The list is ended by an empty line. +.TP +.BI \--no_rc +Only if used as first command line argument this option prevents reading and +interpretation of eventual startup files. See section FILES below. +.TP +.BI \--single_track +Accept only the last argument of the command line as track source address. +.TP +.BI write_start_address= byte_offset +Set the address on media where to start writing the track. With DVD+RW or +DVD-RAM byte_offset must be aligned to 2 KB blocks, but better is 32 kB. +With DVD-RW 32 kB alignment is mandatory. +.br +Other media are not suitable for this option yet. +.PP +Alphabetical list of options which are only intended for very special +situations and not for normal use: +.TP +.BI \--abort_handler +Establish default signal handling not to leave a drive in busy state +but rather to shut it down and to wait until it has ended the final operations. +This option is only needed for revoking eventual --ignore_signals or +--no_abort_handler. +.TP +.BI dev_translation= +Set drive address alias. This was necessary before cdrskin-0.2.4 to manually +translate cdrecord addresses into cdrskin addresses. +.br + is a single character which may not occur in the address string +. is an address as expected to be given by the user via option +dev=. is the address to be used instead whenever is given. +More than one translation instruction can be given in one cdrskin run. +.br +E.g.: dev_translation=+ATA:1,0,0+/dev/sg1 dev_translation=+ATA:1,1,0+/dev/sg2 +.TP +.BI \--drive_abort_on_busy +Linux specific: Abort process if a busy drive is encountered. +.TP +.BI \--drive_blocking +Linux specific: Try to wait for a busy drive to become free. +This is not guaranteed to work with all drivers. Some need nonblocking i/o. +.TP +.BI \--drive_not_exclusive +Linux specific: Do not ask the operating system to prevent opening busy drives. +Wether this leads to senseful behavior depends on operating system and kernel. +.TP +.BI \--drive_scsi_exclusive +Linux specific: +Try to exclusively reserve device files /dev/srN, /dev/scdM, /dev/stK of drive. +this would be helpful to protect against collisions with program growisofs. +Regrettably on Linux kernel 2.4 with ide-scsi emulation this seems not to +work. Wether it becomes helpful with new Linux systems has to be evaluated. +.TP +.BI \--fifo_disable +Disable fifo despite any fs=. +.TP +.BI \--fifo_per_track +Use a separate fifo for each track. +.TP +.BI grab_drive_and_wait= seconds +Open the addressed drive, wait the given number of seconds, release the drive, +and do normal work as indicated by the other options used. This option helps +to explore the program behavior when faced with busy drives. Just start a +second cdrskin with option --devices while grab_drive_and_wait= is still +active. +.TP +.BI \--ignore_signals +Try to ignore any signals rather than to abort the program. This is not a +very good idea. You might end up waiting a very long time for cdrskin +to finish. +.TP +.BI \--no_abort_handler +On signals exit even if the drive is in busy state. This is not a very good +idea. You might end up with a stuck drive that refuses to hand out the media. +.TP +.BI \--no_blank_appendable +Refuse to blank appendable CD-RW. This is a feature that was once builtin with +libburn. No information available for what use case it was needed. +.TP +.BI \--no_convert_fs_adr +Do only literal translations of dev=. This prevents cdrskin from test-opening +device files in order to find one that matches the given dev= specifier. +.br +Partly Linux specific: +Such opening is needed for Bus,Target,Lun addresses unless option +--old_pseudo_scsi_adr is given. It is also needed to resolve device file +addresses which are not listed with cdrskin --devices but nevertheless point +to a usable drive. (Like /dev/sr0 using the same SCSI address as /dev/sg0.) +.TP +.BI \--old_pseudo_scsi_adr +Linux specific: +Use and report literal Bus,Target,Lun addresses rather than real SCSI and +pseudo ATA addresses. This method is outdated and was never compatible with +original cdrecord. +.TP +.BI tao_to_sao_tsize= size +Set an exact fixed size for the next track to be in effect only if the track +source cannot deliver a size prediction and no tsize= was specified. +This is the fallback from bad old times when cdrskin was unable to burn +in mode -tao. +.br +.SH EXAMPLES +.SS +.B Get an overview of drives: +.br +cdrskin -scanbus +.br +cdrskin dev=ATA -scanbus +.br +cdrskin --devices +.SS +.B Get info about a particular drive or loaded media: +.br +cdrskin dev=0,1,0 -checkdrive +.br +cdrskin dev=ATA:1,0,0 -atip +.br +cdrskin dev=/dev/hdc -toc +.SS +.B Make used CD-RW writable again: +.br +cdrskin -v dev=/dev/sg1 blank=all -eject +.br +cdrskin -v dev=/dev/dvd blank=fast -eject +.SS +.B Format DVD-RW before first use with cdrskin: +.br +cdrskin -v dev=/dev/sr0 blank=format_overwrite +.SS +.B Write ISO-9660 filesystem image: +.br +cdrskin -v dev=/dev/hdc speed=12 fs=8m \\ +.br +driveropts=burnfree -sao -eject \\ +.br +padsize=300k my_image.iso +.SS +.B Write compressed afio archive on-the-fly: +.br +find . | afio -oZ - | \\ +.br +cdrskin -v dev=0,1,0 fs=32m speed=8 driveropts=burnfree \\ +.br +padsize=300k -tao - +.SS +.B Write several sessions to the same CD: +.br +cdrskin dev=/dev/hdc padsize=300k -multi 1.iso +.br +cdrskin dev=/dev/hdc padsize=300k -multi -tao 2.afio +.br +cdrskin dev=/dev/hdc padsize=300k -multi -tao 3.afio +.br +cdrskin dev=/dev/hdc padsize=300k -tao 4.afio +.SS +.B Get CD multi-session info for option -C of program mkisofs: +.br +c_values=$(cdrskin dev=/dev/sr0 -msinfo 2>/dev/null) +.br +mkisofs ... -C "$c_values" ... +.SS +.B Write audio tracks to CD: +.br +cdrskin -v dev=ATA:1,0,0 speed=48 \\ +.br +driveropts=burnfree -sao \\ +.br +track1.wav track2.au -audio -swab track3.raw +.br +.SH FILES +If not --no_rc is given as the first argument then cdrskin attempts on +startup to read the arguments from the following files: +.PP +.br +.B /etc/default/cdrskin +.br +.B /etc/opt/cdrskin/rc +.br +.B /etc/cdrskin/cdrskin.conf +.br +.B $HOME/.cdrskinrc +.br +.PP +The files are read in the sequence given above, but none of them is +required for cdrskin to function properly. Each readable line is treated +as one single argument. No extra blanks. +A first character '#' marks a comment, empty lines are ignored. +.SS +.B Example content of a startup file: +.br +# This is the default device +.br +dev=0,1,0 +.br +# To accomodate to remnant cdrskin-0.2.2 addresses +.br +dev_translation=+1,0,0+0,1,0 +.br +# Some more options +.br +fifo_start_at=0 +.br +fs=16m +.br +.SH SEE ALSO +.TP +Formatting track sources for cdrskin: +.br +.BR mkisofs (8), +.BR genisoimage (8), +.BR afio (1), +.BR star (1) +.br +.TP +Other CD/DVD burn programs: +.br +.BR cdrecord (1), +.BR wodim (1) +.br +.TP +For DVD burning: +.br +.BR growisofs (1) +.br +.SH AUTHOR +cdrskin was written by Thomas Schmitt . +.PP +This manual page was written by George Danchev and +Thomas Schmitt, for the Debian project and for all others. + diff --git a/libburn/tags/ZeroThreeZero/cdrskin/cdrskin.c b/libburn/tags/ZeroThreeZero/cdrskin/cdrskin.c new file mode 100644 index 00000000..bd58e6fd --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/cdrskin.c @@ -0,0 +1,5967 @@ + +/* + cdrskin.c , Copyright 2006-2007 Thomas Schmitt +Provided under GPL. See future commitment below. + +A cdrecord compatible command line interface for libburn. + +This project is neither directed against original cdrecord nor does it exploit +any source code of said program. It rather tries to be an alternative method +to burn CD which is not based on the same code as cdrecord. +See also : http://scdbackup.sourceforge.net/cdrskin_eng.html + +Interested users of cdrecord are encouraged to contribute further option +implementations as they need them. Contributions will get published under GPL +but it is essential that the authors allow a future release under LGPL and/or +BSD license. + +There is a script test/cdrecord_spy.sh which may be installed between +the cdrecord command and real cdrecord in order to learn about the options +used by your favorite cdrecord frontend. Edit said script and install it +according to the instructions given inside. + +The implementation of an option would probably consist of +- necessary structure members for structs CdrpreskiN and/or CdrskiN +- code in Cdrpreskin_setup() and Cdrskin_setup() which converts + argv[i] into CdrpreskiN/CdrskiN members (or into direct actions) +- removal of option from ignore list "ignored_partial_options" resp. + "ignored_full_options" in Cdrskin_setup() +- functions which implement the option's run time functionality +- eventually calls of those functions in Cdrskin_run() +- changes to be made within Cdrskin_burn() or Cdrskin_blank() or other + existing methods +See option blank= for an example. + +------------------------------------------------------------------------------ +About compliance with *strong urge* of API towards burn_drive_scan_and_grab() + +For a more comprehensive example of the advised way to behave with libburn +see test/libburner.c . + +cdrskin was the initiator of the whitelist functionality within libburn. +Now it has problems to obviously comply with the new API best practice +presciptions literally. Therefore this explanation: + +On start it restricts the library to a single drive if it already knows the +persistent address by option dev= . This is done with a combination of +burn_drive_add_whitelist() and burn_drive_scan(). Not compliant to the +literal strong urge but in fact exactly fulfilling the reason for that +urge in the API: any scanned drive might be opened exclusively after +burn_drive_scan(). It is kernel dependent wether this behavior is on, off +or switchable. The sysdamin will want it on - but only for one drive. + +So with dev=... cdrskin complies to the spirit of the strong urge. +Without dev=... it has to leave out the whitelist in order to enable bus +scanning and implicit drive address 0. A tradition of 9 months shall not +be broken. So burns without dev= will stay possible - but harmless only +on single drive systems. + +Burns without dev= resp. with dev=number are harmless on multi-drive systems. + +This is because Cdrskin_grab_drive() either drops the unwanted drives or +it enforces a restart of the library with the desired drive's persistent +address. This restart then really uses the strongly urged function +burn_drive_scan_and_grab(). +Thus, cdrskin complies with the new spirit of API by closing down libburn +or by dropping unused drives as soon as the persistent drive address is +known and the drive is to be used with a long running operation. To my +knowlege all long running operations in cdrskin need a grabbed drive. + +This spaghetti approach seems necessary to keep small the impact of new API +urge on cdrskin's stability. cdrskin suffers from having donated the body +parts which have been transplanted to libburn in order to create + burn_drive_scan_and_grab() . The desired sysadmin friendlyness was already +achieved by most cdrskin runs. The remaining problem situations should now +be defused by releasing any short time grabbed flocks of drives during the +restart of libburn. + +------------------------------------------------------------------------------ +This program is currently copyright Thomas Schmitt only. +The copyrights of several components of libburnia.pykix.org are willfully +tangled at toplevel to form an irrevocable commitment to true open source +spirit. +We have chosen the GPL for legal compatibility and clearly express that it +shall not hamper the use of our software by non-GPL applications which show +otherwise the due respect to the open source community. +See toplevel README and cdrskin/README for that commitment. + +For a short time, this place showed a promise to release a BSD license on +mere request. I have to retract that promise now, and replace it by the +promise to make above commitment reality in a way that any BSD conformant +usage in due open source spirit will be made possible somehow and in the +particular special case. I will not raise public protest if you spawn yourself +a BSD license from an (outdated) cdrskin.c which still bears that old promise. +Note that this extended commitment is valid only for cdrskin.[ch], +cdrfifo.[ch] and cleanup.[ch], but not for libburnia.pykix.org as a whole. + +cdrskin is originally inspired by libburn-0.2/test/burniso.c : +(c) Derek Foreman and Ben Jansens + +------------------------------------------------------------------------------ + +Compilation within cdrskin-* : + + cd cdrskin + cc -g -I.. -DCdrskin_build_timestamP='...' \ + -o cdrskin cdrskin.c cdrfifo.c cleanup.c \ + -L../libburn/.libs -lburn -lpthread + +or + + cd .. + cc -g -I. -DCdrskin_build_timestamP='...' \ + -o cdrskin/cdrskin cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cleanup.c \ + libburn/async.o libburn/crc.o libburn/debug.o libburn/drive.o \ + libburn/file.o libburn/init.o libburn/lec.o \ + libburn/mmc.o libburn/options.o libburn/sbc.o libburn/sector.o \ + libburn/sg.o libburn/spc.o libburn/source.o libburn/structure.o \ + libburn/toc.o libburn/util.o libburn/write.o \ + libburn/libdax_audioxtr.o libburn/libdax_msgs.o \ + -lpthread + +*/ + + +/** The official program version */ +#ifndef Cdrskin_prog_versioN +#define Cdrskin_prog_versioN "0.2.7" +#endif + +/** The source code release timestamp */ +#include "cdrskin_timestamp.h" +#ifndef Cdrskin_timestamP +#define Cdrskin_timestamP "-none-given-" +#endif + +/** The binary build timestamp is to be set externally by the compiler */ +#ifndef Cdrskin_build_timestamP +#define Cdrskin_build_timestamP "-none-given-" +#endif + + +#ifdef Cdrskin_libburn_versioN +#undef Cdrskin_libburn_versioN +#endif + +/** use this to accomodate to the CVS version as of Feb 20, 2006 +#define Cdrskin_libburn_cvs_A60220_tS 1 +*/ +#ifdef Cdrskin_libburn_cvs_A60220_tS + +#define Cdrskin_libburn_versioN "0.2.tsA60220" +#define Cdrskin_libburn_no_burn_preset_device_opeN 1 +#ifndef Cdrskin_oldfashioned_api_usE +#define Cdrskin_oldfashioned_api_usE 1 +#endif + +#endif /* Cdrskin_libburn_cvs_A60220_tS */ + + +#ifdef Cdrskin_libburn_0_2_6 +#define Cdrskin_libburn_versioN "0.2.6" +#define Cdrskin_libburn_from_pykix_svN 1 +#endif /* Cdrskin_libburn_0_2_6 */ + +#ifdef Cdrskin_libburn_0_2_7 +#define Cdrskin_libburn_versioN "0.2.7" +#define Cdrskin_libburn_from_pykix_svN 1 +#define Cdrskin_atip_speed_is_oK 1 +#define Cdrskin_no_aftergrab_loopS 1 +#define Cdrskin_libburn_has_get_profilE 1 +#define Cdrskin_libburn_has_set_start_bytE 1 +#define Cdrskin_libburn_has_wrote_welL 1 +#define Cdrskin_libburn_has_bd_formattinG 1 +#define Cdrskin_libburn_has_burn_disc_formaT 1 +#endif /* Cdrskin_libburn_0_2_7 */ + +#ifndef Cdrskin_libburn_versioN +#define Cdrskin_libburn_versioN "0.2.6" +#define Cdrskin_libburn_from_pykix_svN 1 +#endif + +#ifdef Cdrskin_libburn_from_pykix_svN +#ifndef Cdrskin_oldfashioned_api_usE + +#define Cdrskin_libburn_does_ejecT 1 +#define Cdrskin_libburn_has_drive_get_adR 1 +#define Cdrskin_progress_track_does_worK 1 +#define Cdrskin_is_erasable_on_load_does_worK 1 +#define Cdrskin_grab_abort_does_worK 1 +#define Cdrskin_allow_libburn_taO 1 +#define Cdrskin_libburn_has_is_enumerablE 1 +#define Cdrskin_libburn_has_convert_fs_adR 1 +#define Cdrskin_libburn_has_convert_scsi_adR 1 +#define Cdrskin_libburn_has_burn_msgS 1 +#define Cdrskin_libburn_has_burn_aborT 1 +#define Cdrskin_libburn_has_cleanup_handleR 1 +#define Cdrskin_libburn_has_audioxtR 1 +#define Cdrskin_libburn_has_get_start_end_lbA 1 +#define Cdrskin_libburn_has_burn_disc_unsuitablE 1 +#define Cdrskin_libburn_has_read_atiP 1 +#define Cdrskin_libburn_has_buffer_progresS 1 +#define Cdrskin_libburn_has_pretend_fulL 1 +#define Cdrskin_libburn_has_multI 1 +#define Cdrskin_libburn_has_buffer_min_filL 1 + +#ifdef Cdrskin_new_api_tesT + +/* put macros under test caveat here */ +#define Cdrskin_allow_sao_for_appendablE 1 + +#endif /* Cdrskin_new_api_tesT */ + + +#endif /* ! Cdrskin_oldfashioned_api_usE */ +#endif /* Cdrskin_libburn_from_pykix_svN */ + + +/* These macros activate cdrskin workarounds for deficiencies resp. + problematic features of libburn which hopefully will change in + future. */ + +/** Work around the fact that neither /dev/sg0 (kernel 2.4 + ide-scsi) nor + /dev/hdc (kernel 2.6) get ejected by icculus.org/burn */ +#ifndef Cdrskin_libburn_does_ejecT +#define Cdrskin_burn_drive_eject_brokeN 1 +#endif + +/** Work around the fact that after loading media speed report is wrong */ +#ifndef Cdrskin_atip_speed_is_oK +#define Cdrskin_atip_speed_brokeN 1 +#endif + +/** Work around the fact that burn_drive_get_status() always reports to do + track 0 in icculus.org/burn */ +#ifndef Cdrskin_progress_track_does_worK +#define Cdrskin_progress_track_brokeN 1 +#endif + +/** Work around the fact that a drive interrupted at burn_drive_grab() never + leaves status BURN_DRIVE_GRABBING in icculus.org/burn */ +#ifndef Cdrskin_grab_abort_does_worK +#define Cdrskin_grab_abort_brokeN 1 +#endif + +/** Work around the fact that a freshly loaded tray with media reports + arbitrary media erasability in icculuc.org/burn */ +#ifndef Cdrskin_is_erasable_on_load_does_worK +#define Cdrskin_is_erasable_on_load_is_brokeN 1 +#endif + +/** http://libburnia.pykix.org/ticket/41 reports of big trouble without + padding any track to a full sector +*/ +#define Cdrskin_all_tracks_with_sector_paD 1 + + +/** A macro which is able to eat up a function call like printf() */ +#ifdef Cdrskin_extra_leaN +#define ClN(x) +#else +#define ClN(x) x +#endif + + +/** Verbosity level for pacifying progress messages */ +#define Cdrskin_verbose_progresS 1 + +/** Verbosity level for command recognition and execution logging */ +#define Cdrskin_verbose_cmD 2 + +/** Verbosity level for reporting of debugging messages */ +#define Cdrskin_verbose_debuG 3 + +/** Verbosity level for fifo debugging */ +#define Cdrskin_verbose_debug_fifO 4 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../libburn/libburn.h" + +#ifdef Cdrskin_libburn_has_audioxtR +#include "../libburn/libdax_audioxtr.h" +#endif + +#ifdef Cdrskin_libburn_has_cleanup_handleR +#define Cleanup_set_handlers burn_set_signal_handling +#define Cleanup_app_handler_T burn_abort_handler_t +#else +#include "cleanup.h" +#endif + + +/** The size of a string buffer for pathnames and similar texts */ +#define Cdrskin_strleN 4096 + +/** The maximum length +1 of a drive address */ +#ifndef Cdrskin_oldfashioned_api_usE +#define Cdrskin_adrleN BURN_DRIVE_ADR_LEN +#else +#define Cdrskin_adrleN 80 +#endif + + +/* --------------------------------------------------------------------- */ + +/* Imported from scdbackup-0.8.5/src/cd_backup_planer.c */ + +/** Macro for creation of arrays of objects (or single objects) */ +#define TSOB_FELD(typ,anz) (typ *) malloc((anz)*sizeof(typ)); + + +/** Convert a text so that eventual characters special to the shell are + made literal. Note: this does not make a text terminal-safe ! + @param in_text The text to be converted + @param out_text The buffer for the result. + It should have size >= strlen(in_text)*5+2 + @param flag Unused yet + @return For convenience out_text is returned +*/ +char *Text_shellsafe(char *in_text, char *out_text, int flag) +{ + int l,i,w=0; + + /* enclose everything by hard quotes */ + l= strlen(in_text); + out_text[w++]= '\''; + for(i=0;i0) if(line[l-1]=='\r') line[--l]= 0; + if(l>0) if(line[l-1]=='\n') line[--l]= 0; + if(l>0) if(line[l-1]=='\r') line[--l]= 0; + return(ret); +} + + +/** Destroy a synthetic argument array */ +int Sfile_destroy_argv(int *argc, char ***argv, int flag) +{ + int i; + + if(*argc>0 && *argv!=NULL){ + for(i=0;i<*argc;i++){ + if((*argv)[i]!=NULL) + free((*argv)[i]); + } + free((char *) *argv); + } + *argc= 0; + *argv= NULL; + return(1); +} + + +/** Read a synthetic argument array from a list of files. + @param progname The content for argv[0] + @param filenames The paths of the filex from where to read + @param filenamecount The number of paths in filenames + @param argc Returns the number of read arguments (+1 for progname) + @param argv Returns the array of synthetic arguments + @param argidx Returns source file indice of argv[] items + @param arglno Returns source file line numbers of argv[] items + @param flag Bitfield for control purposes: + bit0= read progname as first argument from line + bit1= just release argument array argv and return + bit2= tolerate failure to open file + @return 1=ok , 0=cannot open file , -1=cannot create memory objects +*/ +int Sfile_multi_read_argv(char *progname, char **filenames, int filename_count, + int *argc, char ***argv, int **argidx, int **arglno, + int flag) +{ + int ret,i,pass,maxl=0,l,argcount=0,line_no; + char buf[Cdrskin_strleN]; + FILE *fp= NULL; + + Sfile_destroy_argv(argc,argv,0); + if(flag&2) + return(1); + if((*argidx)!=NULL) + free((char *) *argidx); + if((*arglno)!=NULL) + free((char *) *arglno); + *argidx= *arglno= NULL; + + for(pass=0;pass<2;pass++) { + if(!(flag&1)){ + argcount= 1; + if(pass==0) + maxl= strlen(progname)+1; + else { + (*argv)[0]= (char *) malloc(strlen(progname)+1); + if((*argv)[0]==NULL) + {ret= -1; goto ex;} + strcpy((*argv)[0],progname); + } + } else { + argcount= 0; + if(pass==0) + maxl= 1; + } + for(i=0; i0) + fprintf(stderr,"cdrskin: DEBUG : Reading arguments from file '%s'\n", + filenames[i]); +#endif + + line_no= 0; + while(Sfile_fgets(buf,sizeof(buf)-1,fp)!=NULL) { + line_no++; + l= strlen(buf); + if(l==0 || buf[0]=='#') + continue; + if(pass==0){ + if(l>maxl) + maxl= l; + } else { + if(argcount >= *argc) + break; + (*argv)[argcount]= (char *) malloc(l+1); + if((*argv)[argcount]==NULL) + {ret= -1; goto ex;} + strcpy((*argv)[argcount],buf); + (*argidx)[argcount]= i; + (*arglno)[argcount]= line_no; + } + argcount++; + } + fclose(fp); fp= NULL; + } + if(pass==0){ + *argc= argcount; + if(argcount>0) { + *argv= (char **) malloc(argcount*sizeof(char *)); + *argidx= (int *) malloc(argcount*sizeof(int)); + *arglno= (int *) malloc(argcount*sizeof(int)); + if(*argv==NULL || *argidx==NULL || *arglno==NULL) + {ret= -1; goto ex;} + } + for(i=0;i<*argc;i++) { + (*argv)[i]= NULL; + (*argidx)[i]= -1; + (*arglno)[i]= -1; + } + } + } + + ret= 1; +ex:; + if(fp!=NULL) + fclose(fp); + return(ret); +} + + +/** Combine environment variable HOME with given filename + @param filename Address relative to $HOME + @param fileadr Resulting combined address + @param fa_size Size of array fileadr + @param flag Unused yet + @return 1=ok , 0=no HOME variable , -1=result address too long +*/ +int Sfile_home_adr_s(char *filename, char *fileadr, int fa_size, int flag) +{ + char *home; + + strcpy(fileadr,filename); + home= getenv("HOME"); + if(home==NULL) + return(0); + if(strlen(home)+strlen(filename)+1>=fa_size) + return(-1); + strcpy(fileadr,home); + if(filename[0]!=0){ + strcat(fileadr,"/"); + strcat(fileadr,filename); + } + return(1); +} + + +#endif /* ! Cdrskin_extra_leaN */ + + +/* --------------------------------------------------------------------- */ + +/** Address translation table for users/applications which do not look + for the output of -scanbus but guess a Bus,Target,Lun on their own. +*/ + +/** The maximum number of entries in the address translation table */ +#define Cdradrtrn_leN 256 + +/** The address prefix which will prevent translation */ +#define Cdrskin_no_transl_prefiX "LITERAL_ADR:" + + +struct CdradrtrN { + char *from_address[Cdradrtrn_leN]; + char *to_address[Cdradrtrn_leN]; + int fill_counter; +}; + + +#ifndef Cdrskin_extra_leaN + +/** Create a device address translator object */ +int Cdradrtrn_new(struct CdradrtrN **trn, int flag) +{ + struct CdradrtrN *o; + int i; + + (*trn)= o= TSOB_FELD(struct CdradrtrN,1); + if(o==NULL) + return(-1); + for(i= 0;ifrom_address[i]= NULL; + o->to_address[i]= NULL; + } + o->fill_counter= 0; + return(1); +} + + +/** Release from memory a device address translator object */ +int Cdradrtrn_destroy(struct CdradrtrN **o, int flag) +{ + int i; + struct CdradrtrN *trn; + + trn= *o; + if(trn==NULL) + return(0); + for(i= 0;ifill_counter;i++) { + if(trn->from_address[i]!=NULL) + free(trn->from_address[i]); + if(trn->to_address[i]!=NULL) + free(trn->to_address[i]); + } + free((char *) trn); + *o= NULL; + return(1); +} + + +/** Add a translation pair to the table + @param trn The translator which shall learn + @param from The user side address + @param to The cdrskin side address + @param flag Bitfield for control purposes: + bit0= "from" contains from+to address, to[0] contains delimiter +*/ +int Cdradrtrn_add(struct CdradrtrN *trn, char *from, char *to, int flag) +{ + char buf[2*Cdrskin_adrleN+1],*from_pt,*to_pt; + int cnt; + + cnt= trn->fill_counter; + if(cnt>=Cdradrtrn_leN) + return(-1); + if(flag&1) { + if(strlen(from)>=sizeof(buf)) + return(0); + strcpy(buf,from); + to_pt= strchr(buf,to[0]); + if(to_pt==NULL) + return(0); + *(to_pt)= 0; + from_pt= buf; + to_pt++; + } else { + from_pt= from; + to_pt= to; + } + if(strlen(from)>=Cdrskin_adrleN || strlen(to)>=Cdrskin_adrleN) + return(0); + trn->from_address[cnt]= malloc(strlen(from_pt)+1); + trn->to_address[cnt]= malloc(strlen(to_pt)+1); + if(trn->from_address[cnt]==NULL || + trn->to_address[cnt]==NULL) + return(-2); + strcpy(trn->from_address[cnt],from_pt); + strcpy(trn->to_address[cnt],to_pt); + trn->fill_counter++; + return(1); +} + + +/** Apply eventual device address translation + @param trn The translator + @param from The address from which to translate + @param driveno With backward translation only: The libburn drive number + @param to The result of the translation + @param flag Bitfield for control purposes: + bit0= translate backward + @return <=0 error, 1=no translation found, 2=translation found, + 3=collision avoided +*/ +int Cdradrtrn_translate(struct CdradrtrN *trn, char *from, int driveno, + char to[Cdrskin_adrleN], int flag) +{ + int i,ret= 1; + char *adr; + + to[0]= 0; + adr= from; + if(flag&1) + goto backward; + + if(strncmp(adr,Cdrskin_no_transl_prefiX, + strlen(Cdrskin_no_transl_prefiX))==0) { + adr= adr+strlen(Cdrskin_no_transl_prefiX); + ret= 2; + } else { + for(i=0;ifill_counter;i++) + if(strcmp(adr,trn->from_address[i])==0) + break; + if(ifill_counter) { + adr= trn->to_address[i]; + ret= 2; + } + } + if(strlen(adr)>=Cdrskin_adrleN) + return(-1); + strcpy(to,adr); + return(ret); + +backward:; + if(strlen(from)>=Cdrskin_adrleN) + sprintf(to,"%s%d",Cdrskin_no_transl_prefiX,driveno); + else + strcpy(to,from); + for(i=0;ifill_counter;i++) + if(strcmp(from,trn->to_address[i])==0 && + strlen(trn->from_address[i])fill_counter) { + ret= 2; + strcpy(to,trn->from_address[i]); + } else { + for(i=0;ifill_counter;i++) + if(strcmp(from,trn->from_address[i])==0) + break; + if(ifill_counter) + if(strlen(from)+strlen(Cdrskin_no_transl_prefiX)boss= boss; + o->trackno= trackno; + o->source_path[0]= 0; + o->source_fd= -1; + o->is_from_stdin= !!(flag&2); + o->fixed_size= 0.0; + o->padding= 0.0; + o->set_by_padsize= 0; + o->sector_pad_up= Cdrskin_all_tracks_with_sector_paD; + o->track_type= BURN_MODE1; + o->sector_size= 2048.0; + o->track_type_by_default= 1; + o->swap_audio_bytes= 0; + o->extracting_container= 0; + o->fifo_enabled= 0; + o->fifo= NULL; + o->fifo_outlet_fd= -1; + o->fifo_size= 0; + o->fifo_start_at= -1; + o->ff_fifo= NULL; + o->ff_idx= -1; + o->libburn_track= NULL; + ret= Cdrskin_get_source(boss,o->source_path,&(o->fixed_size),&(o->padding), + &(o->set_by_padsize),&(skin_track_type), + &(o->track_type_by_default),&(o->swap_audio_bytes), + 0); + if(ret<=0) + goto failed; + Cdrtrack_set_track_type(o,skin_track_type,0); + +#ifndef Cdrskin_extra_leaN + ret= Cdrskin_get_fifo_par(boss, &(o->fifo_enabled),&(o->fifo_size), + &(o->fifo_start_at),0); + if(ret<=0) + goto failed; +#endif /* ! Cdrskin_extra_leaN */ + + if(flag&1) + o->fifo_start_at= 0; + return(1); +failed:; + Cdrtrack_destroy(track,0); + return(-1); +} + + +/** Release from memory a track object previously created by Cdrtrack_new() */ +int Cdrtrack_destroy(struct CdrtracK **o, int flag) +{ + struct CdrtracK *track; + + track= *o; + if(track==NULL) + return(0); + +#ifndef Cdrskin_extra_leaN + Cdrfifo_destroy(&(track->fifo),0); +#endif + + if(track->libburn_track!=NULL) + burn_track_free(track->libburn_track); + free((char *) track); + *o= NULL; + return(1); +} + + +int Cdrtrack_set_track_type(struct CdrtracK *o, int track_type, int flag) +{ + if(track_type==BURN_AUDIO) { + o->track_type= BURN_AUDIO; + o->sector_size= 2352.0; + } else { + o->track_type= BURN_MODE1; + o->sector_size= 2048.0; + } + return(1); +} + + +int Cdrtrack_get_track_type(struct CdrtracK *o, int *track_type, + int *sector_size, int flag) +{ + *track_type= o->track_type; + *sector_size= o->sector_size; + return(1); +} + + +/** + @param flag Bitfield for control purposes: + bit0= size returns number of actually processed source bytes + rather than the predicted fixed_size (if available). + padding returns the difference from number of written + bytes. +*/ +int Cdrtrack_get_size(struct CdrtracK *track, double *size, double *padding, + double *sector_size, int flag) +{ + + *size= track->fixed_size; + *padding= track->padding; +#ifdef Cdrskin_allow_libburn_taO + if((flag&1) && track->libburn_track!=NULL) { + off_t readcounter= 0,writecounter= 0; + + burn_track_get_counters(track->libburn_track,&readcounter,&writecounter); + *size= readcounter; + *padding= writecounter-readcounter; +/* + fprintf(stderr,"cdrskin_debug: sizeof(off_t)=%d\n", + sizeof(off_t)); +*/ + } + +#endif + *sector_size= track->sector_size; + return(1); +} + + +int Cdrtrack_get_fifo(struct CdrtracK *track, struct CdrfifO **fifo, int flag) +{ + *fifo= track->fifo; + return(1); +} + + +/** Try wether automatic audio extraction is appropriate and eventually open + a file descriptor to the raw data. + @return -3 identified as .wav but with cdrecord-inappropriate parameters + -2 could not open track source, no use in retrying + -1 severe error + 0 not appropriate to extract, burn plain file content + 1 to be extracted, *fd is a filedescriptor delivering raw data +*/ +int Cdrtrack_extract_audio(struct CdrtracK *track, int *fd, off_t *xtr_size, + int flag) +{ + int l, ok= 0; +#ifdef Cdrskin_libburn_has_audioxtR + struct libdax_audioxtr *xtr= NULL; + char *fmt,*fmt_info; + int num_channels,sample_rate,bits_per_sample,msb_first,ret; +#endif + + *fd= -1; + + if(track->track_type!=BURN_AUDIO && !track->track_type_by_default) + return(0); + l= strlen(track->source_path); + if(l>=4) + if(strcmp(track->source_path+l-4,".wav")==0) + ok= 1; + if(l>=3) + if(strcmp(track->source_path+l-3,".au")==0) + ok= 1; + if(!ok) + return(0); + + if(track->track_type_by_default) { + Cdrtrack_set_track_type(track,BURN_AUDIO,0); + track->track_type_by_default= 2; + fprintf(stderr,"cdrskin: NOTE : Activated -audio for '%s'\n", + track->source_path); + } + +#ifdef Cdrskin_libburn_has_audioxtR + + ret= libdax_audioxtr_new(&xtr,track->source_path,0); + if(ret<=0) + return(ret); + libdax_audioxtr_get_id(xtr,&fmt,&fmt_info, + &num_channels,&sample_rate,&bits_per_sample,&msb_first,0); + if((strcmp(fmt,".wav")!=0 && strcmp(fmt,".au")!=0) || + num_channels!=2 || sample_rate!=44100 || bits_per_sample!=16) { + fprintf(stderr,"cdrskin: ( %s )\n",fmt_info); + fprintf(stderr,"cdrskin: FATAL : Inappropriate audio coding in '%s'.\n", + track->source_path); + {ret= -3; goto ex;} + } + libdax_audioxtr_get_size(xtr,xtr_size,0); + ret= libdax_audioxtr_detach_fd(xtr,fd,0); + if(ret<=0) + {ret= -1*!!ret; goto ex;} + track->swap_audio_bytes= !!msb_first; + track->extracting_container= 1; + fprintf(stderr,"cdrskin: NOTE : %.f %saudio bytes in '%s'\n", + (double) *xtr_size, (msb_first ? "" : "(-swab) "), + track->source_path); + ret= 1; +ex: + libdax_audioxtr_destroy(&xtr,0); + return(ret); + +#else /* Cdrskin_libburn_has_audioxtR */ + + return(0); + +#endif +} + + +/** Deliver an open file descriptor corresponding to the source path of track. + @return <=0 error, 1 success +*/ +int Cdrtrack_open_source_path(struct CdrtracK *track, int *fd, int flag) +{ + int is_wav= 0, size_from_file= 0; + off_t xtr_size= 0; + struct stat stbuf; + + if(track->source_path[0]=='-' && track->source_path[1]==0) + *fd= 0; + else if(track->source_path[0]=='#' && + (track->source_path[1]>='0' && track->source_path[1]<='9')) + *fd= atoi(track->source_path+1); + else { + *fd= -1; + is_wav= Cdrtrack_extract_audio(track,fd,&xtr_size,0); + if(is_wav==-1) + return(-1); + if(is_wav==-3) + return(0); + if(is_wav==0) + *fd= open(track->source_path,O_RDONLY); + if(*fd==-1) { + fprintf(stderr,"cdrskin: failed to open source address '%s'\n", + track->source_path); + fprintf(stderr,"cdrskin: errno=%d , \"%s\"\n",errno, + errno==0?"-no error code available-":strerror(errno)); + return(0); + } + if(track->fixed_size<=0) { + if(xtr_size>0) { + track->fixed_size= xtr_size; + size_from_file= 1; + } else { + if(fstat(*fd,&stbuf)!=-1) { + if((stbuf.st_mode&S_IFMT)==S_IFREG) { + track->fixed_size= stbuf.st_size; + size_from_file= 1; + } /* all other types are assumed of open ended size */ + } + } + } + } + +#ifdef Cdrskin_allow_libburn_taO + + if(track->fixed_size < Cdrtrack_minimum_sizE * track->sector_size + && (track->fixed_size>0 || size_from_file)) { + +#else + + if(track->fixed_size < Cdrtrack_minimum_sizE * track->sector_size) { + +#endif + + if(track->track_type == BURN_AUDIO) { + /* >>> cdrecord: We differ in automatic padding with audio: + Audio tracks must be at least 705600 bytes and a multiple of 2352. + */ + fprintf(stderr, + "cdrskin: FATAL : Audio tracks must be at least %.f bytes\n", + Cdrtrack_minimum_sizE*track->sector_size); + return(0); + } else { + fprintf(stderr, + "cdrskin: NOTE : Enforcing minimum track size of %.f bytes\n", + Cdrtrack_minimum_sizE*track->sector_size); + track->fixed_size= Cdrtrack_minimum_sizE*track->sector_size; + } + } + track->source_fd= *fd; + return(*fd>=0); +} + + +#ifndef Cdrskin_extra_leaN + +/** Install a fifo object between data source and libburn. + Its parameters are known to track. + @param outlet_fd Returns the filedescriptor of the fifo outlet. + @param previous_fifo Object address for chaining or follow-up attachment. + @param flag Bitfield for control purposes: + bit0= Debugging verbosity + bit1= Do not create and attach a new fifo + but attach new follow-up fd pair to previous_fifo + bit2= Do not enforce fixed_size if not container extraction + @return <=0 error, 1 success +*/ +int Cdrtrack_attach_fifo(struct CdrtracK *track, int *outlet_fd, + struct CdrfifO *previous_fifo, int flag) +{ + struct CdrfifO *ff; + int source_fd,pipe_fds[2],ret; + + *outlet_fd= -1; + if(track->fifo_size<=0) + return(2); + ret= Cdrtrack_open_source_path(track,&source_fd,0); + if(ret<=0) + return(ret); + if(pipe(pipe_fds)==-1) + return(0); + + Cdrfifo_destroy(&(track->fifo),0); + if(flag&2) { + ret= Cdrfifo_attach_follow_up_fds(previous_fifo,source_fd,pipe_fds[1],0); + if(ret<=0) + return(ret); + track->ff_fifo= previous_fifo; + track->ff_idx= ret-1; + } else { + + /* >>> ??? obtain track sector size and use instead of 2048 ? */ + + ret= Cdrfifo_new(&ff,source_fd,pipe_fds[1],2048,track->fifo_size,0); + if(ret<=0) + return(ret); + if(previous_fifo!=NULL) + Cdrfifo_attach_peer(previous_fifo,ff,0); + track->fifo= track->ff_fifo= ff; + track->ff_idx= -1; + } + track->fifo_outlet_fd= pipe_fds[0]; + + if((track->extracting_container || !(flag&4)) && track->fixed_size>0) + Cdrfifo_set_fd_in_limit(track->ff_fifo,track->fixed_size,track->ff_idx,0); + + if(flag&1) + printf( + "cdrskin_debug: track %d fifo replaced source_address '%s' by '#%d'\n", + track->trackno+1,track->source_path,track->fifo_outlet_fd); + sprintf(track->source_path,"#%d",track->fifo_outlet_fd); + track->source_fd= track->fifo_outlet_fd; + *outlet_fd= track->fifo_outlet_fd; + return(1); +} + + +/** Read data into the fifo until either it is full or the data source is + exhausted. + @return <=0 error, 1 success +*/ +int Cdrtrack_fill_fifo(struct CdrtracK *track, int flag) +{ + int ret,buffer_fill,buffer_space; + + if(track->fifo==NULL || track->fifo_start_at==0) + return(2); + if(track->fifo_start_at>0 && track->fifo_start_atfifo_size) + printf( + "cdrskin: NOTE : Input buffer will be initially filled up to %d bytes\n", + track->fifo_start_at); + printf("Waiting for reader process to fill input buffer ... "); + fflush(stdout); + ret= Cdrfifo_fill(track->fifo,track->fifo_start_at,0); + if(ret<=0) + return(ret); + +/** Ticket 55: check fifos for input, throw error on 0-bytes from stdin + @return <=0 abort run, 1 go on with burning +*/ + if(track->is_from_stdin) { + ret= Cdrfifo_get_buffer_state(track->fifo,&buffer_fill,&buffer_space,0); + if(ret<0 || buffer_fill<=0) { + fprintf(stderr,"\ncdrskin: FATAL : (First track) fifo did not read a single byte from stdin\n"); + return(0); + } + } + return(1); +} + +#endif /* ! Cdrskin_extra_leaN */ + + +/** Create a corresponding libburn track object and add it to the libburn + session. This may change the trackno index set by Cdrtrack_new(). +*/ +int Cdrtrack_add_to_session(struct CdrtracK *track, int trackno, + struct burn_session *session, int flag) +/* + bit0= debugging verbosity + bit1= apply padding hack (<<< should be unused for now) +*/ +{ + struct burn_track *tr; + struct burn_source *src= NULL; + double padding,lib_padding; + int ret,sector_pad_up; + double fixed_size; + int source_fd; + + track->trackno= trackno; + tr= burn_track_create(); + track->libburn_track= tr; + + /* Note: track->track_type may get set in here */ + if(track->source_fd==-1) { + ret= Cdrtrack_open_source_path(track,&source_fd,0); + if(ret<=0) + goto ex; + } + + padding= 0.0; + sector_pad_up= track->sector_pad_up; + if(track->padding>0) { + if(track->set_by_padsize || track->track_type!=BURN_AUDIO) + padding= track->padding; + else + sector_pad_up= 1; + } + if(flag&2) + lib_padding= 0.0; + else + lib_padding= padding; + if(flag&1) { + if(sector_pad_up) { + ClN(fprintf(stderr,"cdrskin_debug: track %d telling burn_track_define_data() to pad up last sector\n",trackno+1)); + } + if(lib_padding>0 || !sector_pad_up) { + ClN(fprintf(stderr, + "cdrskin_debug: track %d telling burn_track_define_data() to pad %.f bytes\n", + trackno+1,lib_padding)); + } + } + burn_track_define_data(tr,0,(int) lib_padding,sector_pad_up, + track->track_type); + burn_track_set_byte_swap(tr, + (track->track_type==BURN_AUDIO && track->swap_audio_bytes)); + fixed_size= track->fixed_size; + if((flag&2) && track->padding>0) { + if(flag&1) + ClN(fprintf(stderr,"cdrskin_debug: padding hack : %.f + %.f = %.f\n", + track->fixed_size,track->padding, + track->fixed_size+track->padding)); + fixed_size+= track->padding; + } + src= burn_fd_source_new(track->source_fd,-1,(off_t) fixed_size); + + if(src==NULL) { + fprintf(stderr, + "cdrskin: FATAL : Could not create libburn data source object\n"); + {ret= 0; goto ex;} + } + if(burn_track_set_source(tr,src)!=BURN_SOURCE_OK) { + fprintf(stderr,"cdrskin: FATAL : libburn rejects data source object\n"); + {ret= 0; goto ex;} + } + burn_session_add_track(session,tr,BURN_POS_END); + ret= 1; +ex: + if(src!=NULL) + burn_source_free(src); + return(ret); +} + + +/** Release libburn track information after a session is done */ +int Cdrtrack_cleanup(struct CdrtracK *track, int flag) +{ + if(track->libburn_track==NULL) + return(0); + burn_track_free(track->libburn_track); + track->libburn_track= NULL; + return(1); +} + + +int Cdrtrack_ensure_padding(struct CdrtracK *track, int flag) +/* +flag: + bit0= debugging verbosity +*/ +{ + if(track->track_type!=BURN_AUDIO) + return(2); + if(flag&1) + fprintf(stderr,"cdrskin_debug: enforcing -pad on last -audio track\n"); + track->sector_pad_up= 1; + return(1); +} + + +#ifndef Cdrskin_extra_leaN + +/** Try to read bytes from the track's fifo outlet and eventually discard + them. Not to be called unless the track is completely written. +*/ +int Cdrtrack_has_input_left(struct CdrtracK *track, int flag) +{ + struct timeval wt; + fd_set rds,wts,exs; + int ready,ret; + char buf[2]; + + if(track->fifo_outlet_fd<=0) + return(0); + FD_ZERO(&rds); + FD_ZERO(&wts); + FD_ZERO(&exs); + FD_SET(track->fifo_outlet_fd,&rds); + wt.tv_sec= 0; + wt.tv_usec= 0; + ready= select(track->fifo_outlet_fd+1,&rds,&wts,&exs,&wt); + if(ready<=0) + return(0); + ret= read(track->fifo_outlet_fd,buf,1); + if(ret>0) + return(1); + return(0); +} + +#endif /* ! Cdrskin_extra_leaN */ + + +/* --------------------------------------------------------------------- */ + +/** The list of startup file names */ +#define Cdrpreskin_rc_nuM 4 + +static char Cdrpreskin_sys_rc_nameS[Cdrpreskin_rc_nuM][80]= { + "/etc/default/cdrskin", + "/etc/opt/cdrskin/rc", + "/etc/cdrskin/cdrskin.conf", + "placeholder for $HOME/.cdrskinrc" +}; + + +/** A structure which bundles several parameters for creation of the CdrskiN + object. It finally becomes a managed subordinate of the CdrskiN object. +*/ +struct CdrpreskiN { + + /* to be transfered into skin */ + int verbosity; + char queue_severity[81]; + char print_severity[81]; + + /** Stores eventually given absolute device address before translation */ + char raw_device_adr[Cdrskin_adrleN]; + + /** Stores an eventually given translated absolute device address between + Cdrpreskin_setup() and Cdrskin_create() . + */ + char device_adr[Cdrskin_adrleN]; + + /** The eventual address translation table */ + struct CdradrtrN *adr_trn; + + /** Memorizes the abort handling mode from presetup to creation of + control object. Defined handling modes are: + 0= no abort handling + 1= try to cancel, release, exit (leave signal mode as set by caller) + 2= try to ignore all signals + 3= mode 1 in normal operation, mode 2 during abort handling + 4= mode 1 in normal operation, mode 0 during abort handling + -1= install abort handling 1 only in Cdrskin_burn() after burning started + */ + int abort_handler; + + /** Wether to allow getuid()!=geteuid() */ + int allow_setuid; + + /** Wether to allow user provided addresses like #4 */ + int allow_fd_source; + + /** Wether an option is given which needs a full bus scan */ + int no_whitelist; + + /** Wether the translated device address shall not follow softlinks, device + clones and SCSI addresses */ + int no_convert_fs_adr; + + /** Wether Bus,Target,Lun addresses shall be converted literally as old + Pseudo SCSI-Adresses. New default is to use (possibly system emulated) + real SCSI addresses via burn_drive_convert_scsi_adr() and literally + emulated and cdrecord-incompatible ATA: addresses. */ + int old_pseudo_scsi_adr; + + /** Wether bus scans shall exit!=0 if no drive was found */ + int scan_demands_drive; + + /** Wether to abort when a busy drive is encountered during bus scan */ + int abort_on_busy_drive; + + /** Wether to try to avoid collisions when opening drives */ + int drive_exclusive; + + /** Wether to try to wait for unwilling drives to become willing to open */ + int drive_blocking; + + /** Explicit write mode option is determined before skin processes + any track arguments */ + char write_mode_name[80]; + +#ifndef Cdrskin_extra_leaN + + /** List of startupfiles */ + char rc_filenames[Cdrpreskin_rc_nuM][Cdrskin_strleN]; + int rc_filename_count; + + /** Non-argument options from startupfiles */ + int pre_argc; + char **pre_argv; + int *pre_argidx; + int *pre_arglno; + +#endif /* ! Cdrskin_extra_leaN */ + +}; + + + +/** Create a preliminary cdrskin program run control object. It will become + part of the final control object. + @param preskin Returns pointer to resulting + @param flag Bitfield for control purposes: unused yet + @return <=0 error, 1 success +*/ +int Cdrpreskin_new(struct CdrpreskiN **preskin, int flag) +{ + struct CdrpreskiN *o; + int i; + + (*preskin)= o= TSOB_FELD(struct CdrpreskiN,1); + if(o==NULL) + return(-1); + + o->verbosity= 0; + strcpy(o->queue_severity,"NEVER"); + strcpy(o->print_severity,"SORRY"); + o->raw_device_adr[0]= 0; + o->device_adr[0]= 0; + o->adr_trn= NULL; + o->abort_handler= 3; + o->allow_setuid= 0; + o->allow_fd_source= 0; + o->no_whitelist= 0; + o->no_convert_fs_adr= 0; +#ifdef Cdrskin_libburn_has_convert_scsi_adR + o->old_pseudo_scsi_adr= 0; +#else + o->old_pseudo_scsi_adr= 1; +#endif + o->scan_demands_drive= 0; + o->abort_on_busy_drive= 0; + o->drive_exclusive= 1; + o->drive_blocking= 0; + strcpy(o->write_mode_name,"DEFAULT"); + +#ifndef Cdrskin_extra_leaN + o->rc_filename_count= Cdrpreskin_rc_nuM; + for(i=0;irc_filename_count-1;i++) + strcpy(o->rc_filenames[i],Cdrpreskin_sys_rc_nameS[i]); + o->rc_filenames[o->rc_filename_count-1][0]= 0; + o->pre_argc= 0; + o->pre_argv= NULL; + o->pre_argidx= NULL; + o->pre_arglno= NULL; +#endif /* ! Cdrskin_extra_leaN */ + + return(1); +} + + +int Cdrpreskin_destroy(struct CdrpreskiN **preskin, int flag) +{ + struct CdrpreskiN *o; + + o= *preskin; + if(o==NULL) + return(0); + +#ifndef Cdrskin_extra_leaN + if((o->pre_arglno)!=NULL) + free((char *) o->pre_arglno); + if((o->pre_argidx)!=NULL) + free((char *) o->pre_argidx); + if(o->pre_argc>0 && o->pre_argv!=NULL) + Sfile_destroy_argv(&(o->pre_argc),&(o->pre_argv),0); + Cdradrtrn_destroy(&(o->adr_trn),0); +#endif /* ! Cdrskin_extra_leaN */ + + free((char *) o); + *preskin= NULL; + return(1); +} + + +int Cdrpreskin_set_severities(struct CdrpreskiN *preskin, char *queue_severity, + char *print_severity, int flag) +{ +/* + if(preskin->verbosity>=Cdrskin_verbose_debuG) + fprintf(stderr, + "cdrskin: DEBUG : queue_severity='%s' print_severity='%s'\n", + queue_severity,print_severity); +*/ + + if(queue_severity!=NULL) + strcpy(preskin->queue_severity,queue_severity); + if(print_severity!=NULL) + strcpy(preskin->print_severity,print_severity); +#ifdef Cdrskin_libburn_has_burn_msgS + burn_msgs_set_severities(preskin->queue_severity, preskin->print_severity, + "cdrskin: "); +#endif + return(1); +} + + +int Cdrpreskin_initialize_lib(struct CdrpreskiN *preskin, int flag) +{ + int ret; + + ret= burn_initialize(); + if(ret==0) { + fprintf(stderr,"cdrskin : FATAL : Initialization of libburn failed\n"); + return(0); + } + Cdrpreskin_set_severities(preskin,NULL,NULL,0); + return(1); +} + + +/** Enable queuing of libburn messages or disable and print queue content. + @param flag Bitfield for control purposes: + bit0= enable queueing, else disable and print +*/ +int Cdrpreskin_queue_msgs(struct CdrpreskiN *o, int flag) +{ +#ifdef Cdrskin_libburn_has_burn_msgS +#ifndef Cdrskin_extra_leaN +#define Cdrskin_debug_libdax_msgS 1 +#endif +/* <<< In cdrskin there is not much sense in queueing library messages. + It is done here only for debugging */ +#ifdef Cdrskin_debug_libdax_msgS + + int ret; + static char queue_severity[81]= {"NEVER"}, print_severity[81]= {"SORRY"}; + static int queueing= 0; + char msg[BURN_MSGS_MESSAGE_LEN],msg_severity[81],filler[81]; + int error_code,os_errno,first,i; + + if(flag&1) { + if(!queueing) { + strcpy(queue_severity,o->queue_severity); + strcpy(print_severity,o->print_severity); + } + if(o->verbosity>=Cdrskin_verbose_debuG) + Cdrpreskin_set_severities(o,"DEBUG","NEVER",0); + else + Cdrpreskin_set_severities(o,"SORRY","NEVER",0); + queueing= 1; + return(1); + } + + if(queueing) + Cdrpreskin_set_severities(o,queue_severity,print_severity,0); + queueing= 0; + + for(first= 1; ; first= 0) { + ret= burn_msgs_obtain("ALL",&error_code,msg,&os_errno,msg_severity); + if(ret==0) + break; + if(ret<0) { + fprintf(stderr, + "cdrskin: NOTE : Please inform libburn-hackers@pykix.org about:\n"); + fprintf(stderr, + "cdrskin: burn_msgs_obtain() returns %d\n",ret); + break; + } + if(first) + fprintf(stderr, +"cdrskin: -------------------- Messages from Libburn ---------------------\n"); + for(i=0;msg_severity[i]!=0;i++) + filler[i]= ' '; + filler[i]= 0; + fprintf(stderr,"cdrskin: %s : %s\n",msg_severity,msg); + if(strcmp(msg_severity,"DEBUG")!=0 && os_errno!=0) + fprintf(stderr,"cdrskin: %s ( errno=%d '%s')\n", + filler,os_errno,strerror(os_errno)); + } + if(first==0) + fprintf(stderr, +"cdrskin: ----------------------------------------------------------------\n"); + +#endif /* Cdrskin_debug_libdax_msgS */ +#endif /* Cdrskin_libburn_has_burn_msgS */ + + return(1); +} + + +/** Convert a cdrecord-style device address into a libburn device address or + into a libburn drive number. It depends on the "scsibus" number of the + cdrecord-style address which kind of libburn address emerges: + bus=0 : drive number , bus=1 : /dev/sgN , bus=2 : /dev/hdX + (This call intentionally has no CdrpreskiN argument) + @param flag Bitfield for control purposes: + bit0= old_pseudo_scsi_adr + @return 1 success, 0=no recognizable format, -1=severe error, + -2 could not find scsi device, -3 address format error +*/ +int Cdrpreskin__cdrecord_to_dev(char *adr, char device_adr[Cdrskin_adrleN], + int *driveno, int flag) +{ + int comma_seen= 0,digit_seen= 0,busno= 0,k,lun_no= -1; + + *driveno= -1; + device_adr[0]= 0; + if(strlen(adr)==0) + return(0); + + /* read the trailing numeric string as device address code */ + /* accepts "1" , "0,1,0" , "ATA:0,1,0" , ... */ + for(k= strlen(adr)-1;k>=0;k--) { + if(adr[k]==',' && !comma_seen) { + sscanf(adr+k+1,"%d",&lun_no); + comma_seen= 1; + digit_seen= 0; + continue; + } + if(adr[k]<'0' || adr[k]>'9') + break; + digit_seen= 1; + } + if(!digit_seen) { + k= strlen(adr)-1; + if(adr[k]==':' || (adr[k]>='A' && adr[k]<='Z')) { /* empty prefix ? */ + *driveno= 0; + return(1); + } + return(0); + } + sscanf(adr+k+1,"%d",driveno); + + digit_seen= 0; + if(k>0) if(adr[k]==',') { + for(k--;k>=0;k--) { + if(adr[k]<'0' || adr[k]>'9') + break; + digit_seen= 1; + } + if(digit_seen) { + sscanf(adr+k+1,"%d",&busno); + if(flag&1) { + /* look for symbolic bus : 1=/dev/sgN 2=/dev/hdX */ + if(busno==1) { + sprintf(device_adr,"/dev/sg%d",*driveno); + } else if(busno==2) { + sprintf(device_adr,"/dev/hd%c",'a'+(*driveno)); + } else if(busno!=0) { + fprintf(stderr, + "cdrskin: FATAL : dev=[Prefix:]Bus,Target,Lun expects Bus out of {0,1,2}\n"); + return(-3); + } + } else { + if(busno<0) { + fprintf(stderr, + "cdrskin: FATAL : dev=[Prefix:]Bus,Target,Lun expects Bus number >= 0\n"); + return(-3); + } + if(busno>=1000) { + busno-= 1000; + goto ata_bus; + } else if((strncmp(adr,"ATA",3)==0 && (adr[3]==0 || adr[3]==':')) || + (strncmp(adr,"ATAPI",5)==0 && (adr[5]==0 || adr[5]==':'))) { +ata_bus:; + if(busno>12 || (*driveno)<0 || (*driveno)>1) { + fprintf(stderr, +"cdrskin: FATAL : dev=ATA:Bus,Target,Lun expects Bus {0..12}, Target {0,1}\n"); + return(-3); + } + sprintf(device_adr,"/dev/hd%c",'a'+(2*busno)+(*driveno)); + +#ifdef Cdrskin_libburn_has_convert_scsi_adR + } else { + int ret; + + ret= burn_drive_convert_scsi_adr(busno,-1,-1,*driveno,lun_no, + device_adr); + if(ret==0) { + fprintf(stderr, + "cdrskin: FATAL : Cannot find /dev/* with Bus,Target,Lun = %d,%d,%d\n", + busno,*driveno,lun_no); + fprintf(stderr, + "cdrskin: HINT : This drive may be in use by another program currently\n"); + return(-2); + } else if(ret<0) + return(-1); + return(1); + +#endif /* Cdrskin_libburn_has_convert_scsi_adR */ + } + } + } + } + return(1); +} + + +#ifndef Cdrskin_extra_leaN + +/** Load content startup files into preskin cache */ +int Cdrpreskin_read_rc(struct CdrpreskiN *o, char *progname, int flag) +{ + int ret,i; + char *filenames_v[3]; + + for(i=0;irc_filename_count;i++) + filenames_v[i]= o->rc_filenames[i]; + Sfile_home_adr_s(".cdrskinrc",o->rc_filenames[o->rc_filename_count-1], + Cdrskin_strleN,0); + ret= Sfile_multi_read_argv(progname,filenames_v,o->rc_filename_count, + &(o->pre_argc),&(o->pre_argv), + &(o->pre_argidx),&(o->pre_arglno),4); + return(ret); +} + +#endif /* ! Cdrskin_extra_leaN */ + + +/** Interpret those arguments which do not need libburn or which influence the + startup of libburn and/or the creation of the CdrskiN object. This is run + before libburn gets initialized and before Cdrskin_new() is called. + Options which need libburn or a CdrskiN object are processed in a different + function named Cdrskin_setup(). + @param flag Bitfield for control purposes: + bit0= do not finalize setup + bit1= do not read and interpret rc files + @return <=0 error, 1 success , 2 end program run with exit value 0 +*/ +int Cdrpreskin_setup(struct CdrpreskiN *o, int argc, char **argv, int flag) +/* +return: + <=0 error + 1 ok + 2 end program run (--help) +*/ +{ + int i,ret; + char *value_pt; + +#ifndef Cdrskin_extra_leaN + if(argc>1) { + if(strcmp(argv[1],"--no_rc")==0 || strcmp(argv[1],"-version")==0 || + strcmp(argv[1],"--help")==0 || strcmp(argv[1],"-help")==0) + flag|= 2; + } + if(!(flag&2)) { + ret= Cdrpreskin_read_rc(o,argv[0],0); + if(ret<0) + return(-1); + if(o->pre_argc>1) { + ret= Cdrpreskin_setup(o,o->pre_argc,o->pre_argv,flag|1|2); + if(ret<=0) + return(ret); + /* ??? abort on ret==2 ? */ + } + } +#endif + + if(argc==1) { + fprintf(stderr,"cdrskin: SORRY : no options given. Try option --help\n"); + return(0); + } + for (i= 1;iabort_handler= 3; + + } else if(strcmp(argv[i],"--allow_setuid")==0) { + o->allow_setuid= 1; + + } else if(strcmp(argv[i],"blank=help")==0 || + strcmp(argv[i],"-blank=help")==0) { + +#ifndef Cdrskin_extra_leaN + + fprintf(stderr,"Blanking options:\n"); + fprintf(stderr,"\tall\t\tblank the entire disk\n"); + fprintf(stderr,"\tdisc\t\tblank the entire disk\n"); + fprintf(stderr,"\tdisk\t\tblank the entire disk\n"); + fprintf(stderr,"\tfast\t\tminimally blank the entire disk\n"); + fprintf(stderr,"\tminimal\t\tminimally blank the entire disk\n"); + fprintf(stderr, + "\tformat_overwrite\tformat a DVD-RW to \"Restricted Overwrite\"\n"); + fprintf(stderr, + "\tformat_overwrite_quickest\tto \"Restricted Overwrite intermediate\"\n"); + fprintf(stderr, + "\tformat_overwrite_full\tfull-size format a DVD-RW or DVD+RW\n"); + +#else /* ! Cdrskin_extra_leaN */ + + goto see_cdrskin_eng_html; + +#endif /* ! Cdrskin_extra_leaN */ + + if(argc==2) + {ret= 2; goto final_checks;} + + } else if(strcmp(argv[i],"--bragg_with_audio")==0) { + /* OBSOLETE 0.2.3 */; + + } else if(strcmp(argv[i],"--demand_a_drive")==0) { + o->scan_demands_drive= 1; + + } else if(strcmp(argv[i],"--devices")==0) { + printf("Note: If this hangs for a while then there is a drive with\n"); + printf(" unexpected problems (e.g. ill DMA).\n"); + printf(" One may exclude such a device file by removing r- and w-\n"); + printf(" permissions for all cdrskin users.\n"); + o->no_whitelist= 1; + + } else if(strncmp(argv[i],"dev_translation=",16)==0) { + +#ifndef Cdrskin_extra_leaN + + if(o->adr_trn==NULL) { + ret= Cdradrtrn_new(&(o->adr_trn),0); + if(ret<=0) + goto no_adr_trn_mem; + } + if(argv[i][16]==0) { + fprintf(stderr, + "cdrskin: FATAL : dev_translation= : missing separator character\n"); + return(0); + } + ret= Cdradrtrn_add(o->adr_trn,argv[i]+17,argv[i]+16,1); + if(ret==-2) { +no_adr_trn_mem:; + fprintf(stderr, + "cdrskin: FATAL : address_translation= : cannot allocate memory\n"); + } else if(ret==-1) + fprintf(stderr, + "cdrskin: FATAL : address_translation= : table full (%d items)\n", + Cdradrtrn_leN); + else if(ret==0) + fprintf(stderr, + "cdrskin: FATAL : address_translation= : no address separator '%c' found\n", + argv[i][17]); + if(ret<=0) + return(0); + +#else /* ! Cdrskin_extra_leaN */ + + fprintf(stderr, + "cdrskin: FATAL : dev_translation= is not available in lean version\n"); + return(0); + +#endif /* Cdrskin_extra_leaN */ + + + } else if(strncmp(argv[i],"-dev=",5)==0) { + value_pt= argv[i]+5; + goto set_dev; + } else if(strncmp(argv[i],"dev=",4)==0) { + value_pt= argv[i]+4; +set_dev:; + if(strcmp(value_pt,"help")==0) { + +#ifndef Cdrskin_extra_leaN + + printf("Supported SCSI transports for this platform:\n"); + fflush(stdout); + if(o->old_pseudo_scsi_adr) { + fprintf(stderr,"\nTransport name:\t\tlibburn OLD_PSEUDO\n"); + fprintf(stderr, + "Transport descr.:\tBus0=DriveNum , Bus1=/dev/sgN , Bus2=/dev/hdX\n"); + } else { + fprintf(stderr,"\nTransport name:\t\tlibburn SCSI\n"); + fprintf(stderr, + "Transport descr.:\tSCSI Bus,Id,Lun as of operating system\n"); + } + fprintf(stderr,"Transp. layer ind.:\t\n"); + fprintf(stderr,"Target specifier:\tbus,target,lun\n"); + fprintf(stderr,"Target example:\t\t1,2,0\n"); + fprintf(stderr,"SCSI Bus scanning:\tsupported\n"); + fprintf(stderr,"Open via UNIX device:\tsupported\n"); + if(!o->old_pseudo_scsi_adr) { + fprintf(stderr,"\nTransport name:\t\tlibburn HD\n"); + fprintf(stderr, + "Transport descr.:\tLinux specific alias for /dev/hdX\n"); + fprintf(stderr,"Transp. layer ind.:\tATA:\n"); + fprintf(stderr,"Target specifier:\tbus,target,lun\n"); + fprintf(stderr,"Target example:\t\tATA:1,0,0\n"); + fprintf(stderr,"SCSI Bus scanning:\tsupported\n"); + fprintf(stderr,"Open via UNIX device:\tsupported\n"); + } + +#else /* ! Cdrskin_extra_leaN */ + + goto see_cdrskin_eng_html; + +#endif /* Cdrskin_extra_leaN */ + + {ret= 2; goto final_checks;} + } + if(strlen(value_pt)>=sizeof(o->raw_device_adr)) + goto dev_too_long; + strcpy(o->raw_device_adr,value_pt); + + } else if(strcmp(argv[i],"--drive_abort_on_busy")==0) { + o->abort_on_busy_drive= 1; + + } else if(strcmp(argv[i],"--drive_blocking")==0) { + o->drive_blocking= 1; + + } else if(strcmp(argv[i],"--drive_not_exclusive")==0) { + o->drive_exclusive= 0; + + } else if(strcmp(argv[i],"--drive_scsi_exclusive")==0) { + o->drive_exclusive= 2; + + } else if(strcmp(argv[i],"driveropts=help")==0 || + strcmp(argv[i],"-driveropts=help")==0) { + +#ifndef Cdrskin_extra_leaN + + fprintf(stderr,"Driver options:\n"); + fprintf(stderr,"burnfree\tPrepare writer to use BURN-Free technology\n"); + fprintf(stderr,"noburnfree\tDisable using BURN-Free technology\n"); + +#else /* ! Cdrskin_extra_leaN */ + + goto see_cdrskin_eng_html; + +#endif /* Cdrskin_extra_leaN */ + + if(argc==2 || (i==2 && argc==3 && strncmp(argv[1],"dev=",4)==0)) + {ret= 2; goto final_checks;} + + } else if(strcmp(argv[i],"--help")==0) { + +#ifndef Cdrskin_extra_leaN + + printf("\n"); + printf("Usage: %s [options|source_addresses]\n", argv[0]); + printf("Burns preformatted data to CD-R, CD-RW or DVD+RW via libburn.\n"); + printf("For the cdrecord compatible options which control the work of\n"); + printf( + "blanking and burning see output of option -help rather than --help.\n"); + printf("Non-cdrecord options:\n"); + printf(" --abort_handler do not leave the drive in busy state\n"); + printf( + " --allow_setuid disable setuid warning (setuid is insecure !)\n"); + printf( + " --any_track allow source_addresses to match '^-.' or '='\n"); + printf(" --demand_a_drive exit !=0 on bus scans with empty result\n"); + printf(" --devices list accessible devices (tells /dev/...)\n"); + printf( + " dev_translation= set input address alias\n"); + printf(" e.g.: dev_translation=+ATA:1,0,0+/dev/sg1\n"); + printf(" --drive_abort_on_busy abort process if busy drive is found\n"); + printf(" (might be triggered by a busy hard disk)\n"); + printf(" --drive_blocking try to wait for busy drive to become free\n"); + printf(" (might be stalled by a busy hard disk)\n"); + printf(" --drive_not_exclusive do not ask kernel to prevent opening\n"); + printf(" busy drives. Effect is kernel dependend.\n"); + printf( + " --drive_scsi_exclusive try to exclusively reserve device files\n"); + printf(" /dev/srN, /dev/scdM, /dev/stK of drive.\n"); +#ifdef Cdrskin_burn_drive_eject_brokeN + printf( + " eject_device= set the device address for command eject\n"); +#endif + printf(" --fifo_disable disable fifo despite any fs=...\n"); + printf(" --fifo_per_track use a separate fifo for each track\n"); + printf( + " fifo_start_at= do not wait for full fifo but start burning\n"); + printf( + " as soon as the given number of bytes is read\n"); + printf( + " grab_drive_and_wait= grab drive, wait given number of\n"); + printf( + " seconds, release drive, and do normal work\n"); + printf( + " --ignore_signals try to ignore any signals rather than to abort\n"); + printf(" --list_ignored_options list all ignored cdrecord options.\n"); + printf(" --no_abort_handler exit even if the drive is in busy state\n"); + printf(" --no_blank_appendable refuse to blank appendable CD-RW\n"); + printf(" --no_convert_fs_adr only literal translations of dev=\n"); + printf( + " --no_rc as first argument: do not read startup files\n"); + printf(" --old_pseudo_scsi_adr use and report literal Bus,Target,Lun\n"); + printf(" rather than real SCSI and pseudo ATA.\n"); + printf( + " --single_track accept only last argument as source_address\n"); + +#ifdef Cdrskin_allow_libburn_taO + printf( + " tao_to_sao_tsize= use num as fixed track size if in a\n"); + printf( + " non-TAO mode track data are read from \"-\"\n"); + printf( + " and no tsize= was specified.\n"); +#else + printf( + " tao_to_sao_tsize= substitute -tao by -sao and eventually\n"); + printf(" augment input from \"-\" by tsize=\n"); + printf(" (set tao_to_sao_tsize=0 to disable it)\n"); +#endif + + printf( + " write_start_address= write to given byte address (DVD+RW)\n"); + printf( + "Preconfigured arguments are read from the following startup files\n"); + printf( + "if they exist and are readable. The sequence is as listed here:\n"); + printf(" /etc/default/cdrskin /etc/opt/cdrskin/rc\n"); + printf(" /etc/cdrskin/cdrskin.conf $HOME/.cdrskinrc\n"); + printf("Each file line is a single argument. No whitespace.\n"); + printf( + "By default any argument that does not match grep '^-.' or '=' is\n"); + printf( + "used as track source. If it is \"-\" then stdin is used.\n"); + printf("cdrskin : http://scdbackup.sourceforge.net/cdrskin_eng.html\n"); + printf(" mailto:scdbackup@gmx.net (Thomas Schmitt)\n"); + printf("libburn : http://libburnia.pykix.org\n"); + printf("cdrecord : ftp://ftp.berlios.de/pub/cdrecord/\n"); + printf("My respect to the authors of cdrecord and libburn.\n"); + printf("scdbackup: http://scdbackup.sourceforge.net/main_eng.html\n"); + printf("\n"); + +#else /* ! Cdrskin_extra_leaN */ + +see_cdrskin_eng_html:; + printf("This is a capability reduced lean version without help texts.\n"); + printf("See http://scdbackup.sourceforge.net/cdrskin_eng.html\n"); + +#endif /* Cdrskin_extra_leaN */ + + + {ret= 2; goto final_checks;} + } else if(strcmp(argv[i],"-help")==0) { + +#ifndef Cdrskin_extra_leaN + + fprintf(stderr,"Usage: %s [options|source_addresses]\n",argv[0]); + fprintf(stderr,"Note: This is not cdrecord. See cdrskin start message on stdout. See --help.\n"); + fprintf(stderr,"Options:\n"); + fprintf(stderr,"\t-version\tprint version information and exit\n"); + fprintf(stderr, + "\tdev=target\tpseudo-SCSI target to use as CD-Recorder\n"); + fprintf(stderr, + "\tgracetime=#\tset the grace time before starting to write to #.\n"); + fprintf(stderr,"\t-v\t\tincrement verbose level by one\n"); + fprintf(stderr, + "\tdriveropts=opt\topt= one of {burnfree,noburnfree,help}\n"); + fprintf(stderr, + "\t-checkdrive\tcheck if a driver for the drive is present\n"); + fprintf(stderr,"\t-scanbus\tscan the SCSI bus and exit\n"); + fprintf(stderr,"\tspeed=#\t\tset speed of drive\n"); + fprintf(stderr,"\tblank=type\tblank a CD-RW disc (see blank=help)\n"); + fprintf(stderr, + "\tfs=#\t\tSet fifo size to # (0 to disable, default is 4 MB)\n"); + fprintf(stderr, + "\t-eject\t\teject the disk after doing the work (might be ignored)\n"); + fprintf(stderr,"\t-dummy\t\tdo everything with laser turned off\n"); +#ifdef Cdrskin_libburn_has_multI + fprintf(stderr, + "\t-msinfo\t\tretrieve multi-session info for mkisofs >= 1.10\n"); + fprintf(stderr,"\tmsifile=path\trun -msinfo and copy output to file\n"); +#endif + fprintf(stderr,"\t-toc\t\tretrieve and print TOC/PMA data\n"); + fprintf(stderr, + "\t-atip\t\tretrieve media state, print \"Is *erasable\"\n"); +#ifdef Cdrskin_libburn_has_multI + fprintf(stderr, + "\t-multi\t\tgenerate a TOC that allows multi session\n"); +#endif + fprintf(stderr, + "\t-force\t\tforce to continue on some errors to allow blanking\n"); +#ifdef Cdrskin_allow_libburn_taO + fprintf(stderr,"\t-tao\t\tWrite disk in TAO mode.\n"); +#endif + fprintf(stderr,"\t-dao\t\tWrite disk in SAO mode.\n"); + fprintf(stderr,"\t-sao\t\tWrite disk in SAO mode.\n"); + fprintf(stderr,"\t-raw96r\t\tWrite disk in RAW/RAW96R mode\n"); + fprintf(stderr,"\ttsize=#\t\tannounces exact size of source data\n"); + fprintf(stderr,"\tpadsize=#\tAmount of padding\n"); + fprintf(stderr,"\t-audio\t\tSubsequent tracks are CD-DA audio tracks\n"); + fprintf(stderr, + "\t-data\t\tSubsequent tracks are CD-ROM data mode 1 (default)\n"); + fprintf(stderr,"\t-pad\t\tpadsize=30k\n"); + fprintf(stderr, + "\t-nopad\t\tDo not pad (default, but applies only to data tracks)\n"); + fprintf(stderr, + "\t-swab\t\tAudio data source is byte-swapped (little-endian/Intel)\n"); + fprintf(stderr,"\t-help\t\tprint this text to stderr and exit\n"); + fprintf(stderr, + "Without option -data, .wav and .au files are extracted and burned as -audio.\n"); + fprintf(stderr, + "By default any argument that does not match grep '^-.' or '=' is used\n"); + fprintf(stderr, + "as track source address. Address \"-\" means stdin.\n"); + fprintf(stderr, + "cdrskin will ensure that an announced tsize= is written even if\n"); + fprintf(stderr, + "the source delivers fewer bytes. But 0 bytes from stdin with fifo\n"); + fprintf(stderr, + "enabled will lead to abort and no burn attempt at all.\n"); + +#else /* ! Cdrskin_extra_leaN */ + + fprintf(stderr,"Note: This is not cdrecord. See cdrskin start message on stdout.\n"); + fprintf(stderr, + "(writer profile: -atip retrieve, blank=type, -eject after work)\n"); + goto see_cdrskin_eng_html; + +#endif /* Cdrskin_extra_leaN */ + + {ret= 2; goto final_checks;} + + } else if(strcmp(argv[i],"--ignore_signals")==0) { + o->abort_handler= 2; + + } else if(strcmp(argv[i],"--no_abort_handler")==0) { + o->abort_handler= 0; + + } else if(strcmp(argv[i],"--no_convert_fs_adr")==0) { + o->no_convert_fs_adr= 1; + + } else if(strcmp(argv[i],"--old_pseudo_scsi_adr")==0) { + o->old_pseudo_scsi_adr= 1; + + } else if(strcmp(argv[i],"--no_rc")==0) { + if(i!=1) + fprintf(stderr, + "cdrskin: NOTE : option --no_rc would only work as first argument.\n"); + + } else if(strcmp(argv[i],"-raw96r")==0) { + strcpy(o->write_mode_name,"RAW/RAW96R"); + + } else if(strcmp(argv[i],"-sao")==0 || strcmp(argv[i],"-dao")==0) { + strcpy(o->write_mode_name,"SAO"); + + } else if(strcmp(argv[i],"-scanbus")==0) { + o->no_whitelist= 1; + + } else if(strcmp(argv[i],"-tao")==0) { + strcpy(o->write_mode_name,"TAO"); + + } else if(strcmp(argv[i],"-v")==0 || strcmp(argv[i],"-verbose")==0) { + (o->verbosity)++; + printf("cdrskin: verbosity level : %d\n",o->verbosity); +set_severities:; + if(o->verbosity>=Cdrskin_verbose_debuG) + Cdrpreskin_set_severities(o,"NEVER","DEBUG",0); + } else if(strcmp(argv[i],"-vv")==0 || strcmp(argv[i],"-vvv")==0 || + strcmp(argv[i],"-vvvv")==0) { + (o->verbosity)+= strlen(argv[i])-1; + goto set_severities; + + } else if(strcmp(argv[i],"-version")==0) { + printf( + "Cdrecord 2.01-Emulation Copyright (C) 2006-2007, see libburnia.pykix.org\n"); + printf("libburn version : %s\n",Cdrskin_libburn_versioN); + +#ifndef Cdrskin_extra_leaN + printf("cdrskin version : %s\n",Cdrskin_prog_versioN); +#else + printf("cdrskin version : %s.lean (capability reduced lean version)\n", + Cdrskin_prog_versioN); +#endif + + printf("Version timestamp : %s\n",Cdrskin_timestamP); + printf("Build timestamp : %s\n",Cdrskin_build_timestamP); + {ret= 2; goto final_checks;} + } + + } + ret= 1; +final_checks:; + if(flag&1) + goto ex; + + if(strlen(o->raw_device_adr)>0 && !o->no_whitelist) { + int driveno,hret; + char *adr,buf[Cdrskin_adrleN]; + + adr= o->raw_device_adr; + +#ifndef Cdrskin_extra_leaN + if(o->adr_trn!=NULL) { + hret= Cdradrtrn_translate(o->adr_trn,adr,-1,buf,0); + if(hret<=0) { + fprintf(stderr, + "cdrskin: FATAL : address translation failed (address too long ?) \n"); + {ret= 0; goto ex;} + } + adr= buf; + } +#endif /* ! Cdrskin_extra_leaN */ + + if(adr[0]=='/') { + if(strlen(adr)>=sizeof(o->device_adr)) { +dev_too_long:; + fprintf(stderr, + "cdrskin: FATAL : dev=... too long (max. %d characters)\n", + sizeof(o->device_adr)-1); + {ret= 0; goto ex;} + } + strcpy(o->device_adr,adr); + } else { + ret= Cdrpreskin__cdrecord_to_dev(adr,o->device_adr,&driveno, + !!o->old_pseudo_scsi_adr); + if(ret==-2 || ret==-3) + {ret= 0; goto ex;} + if(ret<0) + goto ex; + if(ret==0) { + strcpy(o->device_adr,adr); + ret= 1; + } + } + +#ifdef Cdrskin_libburn_has_convert_fs_adR + + if(strlen(o->device_adr)>0 && !o->no_convert_fs_adr) { + int lret; + char link_adr[Cdrskin_strleN+1]; + + strcpy(link_adr,o->device_adr); + lret = burn_drive_convert_fs_adr(link_adr,o->device_adr); + if(lret<0) { + fprintf(stderr, + "cdrskin: NOTE : Please inform libburn-hackers@pykix.org about:\n"); + fprintf(stderr, + "cdrskin: burn_drive_convert_fs_adr() returned %d\n",lret); + } + } + +#endif /* Cdrskin_libburn_has_convert_fs_adR */ + + } + + /* A60927 : note to myself : no "ret= 1;" here. It breaks --help , -version */ + +ex:; + +#ifndef Cdrskin_extra_leaN + if(ret<=0 || !(flag&1)) + Cdradrtrn_destroy(&(o->adr_trn),0); +#endif + + return(ret); +} + + +/* --------------------------------------------------------------------- */ + + + +/** The maximum number of tracks */ +#define Cdrskin_track_maX 99 + + +/** Work around the fact that libburn leaves the track input fds open + after the track is done. This can hide a few overflow bytes buffered + by the fifo-to-libburn pipe which would cause a broken-pipe error + if libburn would close that outlet. + This macro enables a coarse workaround which tries to read bytes from + the track inlets after burning has ended. Probably not a good idea if + libburn would close the inlet fds. +*/ +#define Cdrskin_libburn_leaves_inlet_opeN 1 + + +/** List of furter wishes towards libburn: + - write mode which does not demand a track size in advance + - obtain minimum drive speed (for cdrskin -atip) + - obtain MMC profile of inserted media (for cdrskin -v -atip) + - a possibility to implement cdrskin -multi + - a possibilty to implement cdrskin -reset +*/ + + +/** Limit to prevent int rollovers within libburn as long as not everything is + changed to 64 bit off_t : 2 GB minus 800 MB for eventual computations. */ +#define Cdrskin_tracksize_maX 1308622848 + + +/* Some constants obtained by hearsay and experiments */ + +/** The CD payload speed factor for reporting progress: 1x = 150 kB/s */ +static double Cdrskin_cd_speed_factoR= 150.0*1024.0; +/** The DVD payload speed factor for reporting progress: 1x */ +static double Cdrskin_dvd_speed_factoR= 1385000; + +/** The effective payload speed factor for reporting progress */ +static double Cdrskin_speed_factoR= 150.0*1024.0; + +/** The speed conversion factors consumer x-speed to libburn speed as used with + burn_drive_set_speed() burn_drive_get_write_speed() +*/ +static double Cdrskin_libburn_cd_speed_factoR= 176.4; +static double Cdrskin_libburn_dvd_speed_factoR= 1385.0; + +/* The effective speed conversion factor for burn_drive_set_speed() */ +static double Cdrskin_libburn_speed_factoR= 176.4; + +/** Add-on for burn_drive_set_speed() to accomodate to the slightley oversized + speed ideas of my LG DVDRAM GSA-4082B. LITE-ON LTR-48125S tolerates it. +*/ +static double Cdrskin_libburn_cd_speed_addoN= 40.0; +static double Cdrskin_libburn_dvd_speed_addoN= 1.0; /*poor accuracy with 2.4x*/ +static double Cdrskin_libburn_speed_addoN = 40.0; + + +/** The program run control object. Defaults: see Cdrskin_new(). */ +struct CdrskiN { + + /** Settings already interpreted by Cdrpreskin_setup */ + struct CdrpreskiN *preskin; + + /** Job: what to do, plus some parameters. */ + int verbosity; + double x_speed; + int gracetime; + int dummy_mode; + int force_is_set; + int single_track; + + int do_devices; + + int do_scanbus; + + int do_checkdrive; + + int do_msinfo; + int msinfo_fd; + char msifile[Cdrskin_strleN]; + + int do_atip; + + int do_blank; + int blank_fast; + int no_blank_appendable; + int blank_format_type; /* 0=blank + bit0-7: + 1=format_overwrite + bit8-15: bit0-7 of burn_disc_format(flag) + bit8 = write zeros after formatting + bit9 = insist in size 0 + bit10= format to maximum available size + 2=format_sequential (unimplemented yet) + */ + double blank_format_size; /* to be used with burn_disc_format() */ + + int do_burn; + int burnfree; + /** The write mode (like SAO or RAW96/R). See libburn. + Controled by preskin->write_mode_name */ + enum burn_write_types write_type; + int block_type; + int multi; + + double write_start_address; + + int do_eject; + char eject_device[Cdrskin_strleN]; + + + /** The current data source and its eventual parameters. + source_path may be either "-" for stdin, "#N" for open filedescriptor N + or the address of a readable file. + */ + char source_path[Cdrskin_strleN]; + double fixed_size; + double padding; + int set_by_padsize; + + /** track_type may be set to BURN_MODE1, BURN_AUDIO, etc. */ + int track_type; + int track_type_by_default; /* 0= explicit, 1=not set, 2=by file extension */ + int swap_audio_bytes; + + /** The list of tracks with their data sources and parameters */ + struct CdrtracK *tracklist[Cdrskin_track_maX]; + int track_counter; + /** a guess about what track might be processing right now */ + int supposed_track_idx; + + int fifo_enabled; + /** Optional fifo between input fd and libburn. It uses a pipe(2) to transfer + data to libburn. This fifo may be actually the start of a chain of fifos + which are to be processed simultaneously. + The fifo object knows the real input fd and the fd[1] of the pipe. + This is just a reference pointer. The fifos are managed by the tracks + which either line up their fifos or share the fifo of the first track. + */ + struct CdrfifO *fifo; + /** fd[0] of the fifo pipe. This is from where libburn reads its data. */ + int fifo_outlet_fd; + int fifo_size; + int fifo_start_at; + int fifo_per_track; + + + /** User defined address translation */ + struct CdradrtrN *adr_trn; + + + /** The drives known to libburn after scan */ + struct burn_drive_info *drives; + unsigned int n_drives; + /** The drive selected for operation by CdrskiN */ + int driveno; + + + /** Progress state info: wether libburn is actually processing payload data */ + int is_writing; + /** Previously detected drive state */ + enum burn_drive_status previous_drive_status; + + /** abort parameters */ + int abort_max_wait; + + /** Engagement info for eventual abort */ + int lib_is_initialized; + pid_t control_pid; /* pid of the thread that calls libburn */ + int drive_is_grabbed; + int drive_is_busy; /* Wether drive was told to do something cancel-worthy */ + struct burn_drive *grabbed_drive; + + /** Abort test facility */ + double abort_after_bytecount; + + + /** Some intermediate option info which is stored until setup finalization */ + double tao_to_sao_tsize; + int stdin_source_used; + +}; + +int Cdrskin_destroy(struct CdrskiN **o, int flag); +int Cdrskin_grab_drive(struct CdrskiN *skin, int flag); +int Cdrskin_release_drive(struct CdrskiN *skin, int flag); + + +/** Create a cdrskin program run control object. + @param skin Returns pointer to resulting + @param flag Bitfield for control purposes: + bit0= library is already initialized + @return <=0 error, 1 success +*/ +int Cdrskin_new(struct CdrskiN **skin, struct CdrpreskiN *preskin, int flag) +{ + struct CdrskiN *o; + int ret,i; + + (*skin)= o= TSOB_FELD(struct CdrskiN,1); + if(o==NULL) + return(-1); + o->preskin= preskin; + o->verbosity= preskin->verbosity; + o->x_speed= -1.0; + o->gracetime= 0; + o->dummy_mode= 0; + o->force_is_set= 0; + o->single_track= 0; + o->do_devices= 0; + o->do_scanbus= 0; + o->do_checkdrive= 0; + o->do_msinfo= 0; + o->msinfo_fd= -1; + o->msifile[0]= 0; + o->do_atip= 0; + o->do_blank= 0; + o->blank_fast= 0; + o->no_blank_appendable= 0; + o->blank_format_type= 0; + o->blank_format_size= 0.0; + o->do_burn= 0; + o->write_type= BURN_WRITE_SAO; + o->block_type= BURN_BLOCK_SAO; + o->multi= 0; + o->write_start_address= -1.0; + o->burnfree= 0; + o->do_eject= 0; + o->eject_device[0]= 0; + o->source_path[0]= 0; + o->fixed_size= 0.0; + o->padding= 0.0; + o->set_by_padsize= 0; + o->track_type= BURN_MODE1; + o->swap_audio_bytes= 1; /* cdrecord default is big-endian (msb_first) */ + o->track_type_by_default= 1; + for(i=0;itracklist[i]= NULL; + o->track_counter= 0; + o->supposed_track_idx= -1; + o->fifo_enabled= 1; + o->fifo= NULL; + o->fifo_outlet_fd= -1; + o->fifo_size= 4*1024*1024; + o->fifo_start_at= -1; + o->fifo_per_track= 0; + o->adr_trn= NULL; + o->drives= NULL; + o->n_drives= 0; + o->driveno= 0; + o->is_writing= 0; + o->previous_drive_status = BURN_DRIVE_IDLE; + o->abort_max_wait= 74*60; + o->lib_is_initialized= (flag&1); + o->control_pid= getpid(); + o->drive_is_grabbed= 0; + o->drive_is_busy= 0; + o->grabbed_drive= NULL; + o->abort_after_bytecount= -1.0; + o->tao_to_sao_tsize= 0.0; + o->stdin_source_used= 0; + +#ifndef Cdrskin_extra_leaN + ret= Cdradrtrn_new(&(o->adr_trn),0); + if(ret<=0) + goto failed; +#endif /* ! Cdrskin_extra_leaN */ + + return(1); +failed:; + Cdrskin_destroy(skin,0); + return(-1); +} + + +/** Release from memory a cdrskin object */ +int Cdrskin_destroy(struct CdrskiN **o, int flag) +{ + struct CdrskiN *skin; + int i; + + skin= *o; + if(skin==NULL) + return(0); + if(skin->drive_is_grabbed) + Cdrskin_release_drive(skin,0); + for(i=0;itrack_counter;i++) + Cdrtrack_destroy(&(skin->tracklist[i]),0); + +#ifndef Cdrskin_extra_leaN + Cdradrtrn_destroy(&(skin->adr_trn),0); +#endif /* ! Cdrskin_extra_leaN */ + + Cdrpreskin_destroy(&(skin->preskin),0); + if(skin->drives!=NULL) + burn_drive_info_free(skin->drives); + free((char *) skin); + *o= NULL; + return(1); +} + + +/** Set the eventual output fd for the result of Cdrskin_msinfo() +*/ +int Cdrskin_set_msinfo_fd(struct CdrskiN *skin, int result_fd, int flag) +{ + skin->msinfo_fd= result_fd; + return(1); +} + + +/** Return information about current track source */ +int Cdrskin_get_source(struct CdrskiN *skin, char *source_path, + double *fixed_size, double *padding, + int *set_by_padsize, int *track_type, + int *track_type_by_default, int *swap_audio_bytes, + int flag) +{ + strcpy(source_path,skin->source_path); + *fixed_size= skin->fixed_size; + *padding= skin->padding; + *set_by_padsize= skin->set_by_padsize; + *track_type= skin->track_type; + *track_type_by_default= skin->track_type_by_default; + *swap_audio_bytes= skin->swap_audio_bytes; + return(1); +} + + +#ifndef Cdrskin_extra_leaN + +/** Return information about current fifo setting */ +int Cdrskin_get_fifo_par(struct CdrskiN *skin, int *fifo_enabled, + int *fifo_size, int *fifo_start_at, int flag) +{ + *fifo_enabled= skin->fifo_enabled; + *fifo_size= skin->fifo_size; + *fifo_start_at= skin->fifo_start_at; + return(1); +} + + +/** Create and install fifo objects between track data sources and libburn. + The sources and parameters are known to skin. + @return <=0 error, 1 success +*/ +int Cdrskin_attach_fifo(struct CdrskiN *skin, int flag) +{ + struct CdrfifO *ff= NULL; + int ret,i,hflag; + + skin->fifo= NULL; + for(i=0;itrack_counter;i++) { + hflag= (skin->verbosity>=Cdrskin_verbose_debuG); + if(i==skin->track_counter-1) + hflag|= 4; + if(skin->verbosity>=Cdrskin_verbose_cmD) { + if(skin->fifo_per_track) + printf("cdrskin: track %d establishing fifo of %d bytes\n", + i+1,skin->fifo_size); + else if(i==0) + printf("cdrskin: establishing fifo of %d bytes\n",skin->fifo_size); + else { + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin_debug: attaching track %d to fifo\n",i+1)); + hflag|= 2; + } + } + ret= Cdrtrack_attach_fifo(skin->tracklist[i],&(skin->fifo_outlet_fd),ff, + hflag); + if(ret<=0) { + fprintf(stderr,"cdrskin: FATAL : failed to attach fifo.\n"); + return(0); + } + if(i==0 || skin->fifo_per_track) + Cdrtrack_get_fifo(skin->tracklist[i],&ff,0); + if(i==0) + skin->fifo= ff; + } + return(1); +} + + +/** Read data into the track fifos until either #1 is full or its data source + is exhausted. + @return <=0 error, 1 success +*/ +int Cdrskin_fill_fifo(struct CdrskiN *skin, int flag) +{ + int ret; + + ret= Cdrtrack_fill_fifo(skin->tracklist[0],0); + if(ret<=0) + return(ret); + printf("input buffer ready.\n"); + fflush(stdout); + return(1); +} + +#endif /* ! Cdrskin_extra_leaN */ + + +/** Inform libburn about the consumer x-speed factor of skin */ +int Cdrskin_adjust_speed(struct CdrskiN *skin, int flag) +{ + int k_speed; + + if(skin->x_speed<0) + k_speed= 0; /* libburn.h promises 0 to be max speed. */ + else if(skin->x_speed==0) /* cdrecord specifies 0 as minimum speed. */ + k_speed= Cdrskin_libburn_speed_factoR+Cdrskin_libburn_speed_addoN; + else + k_speed= skin->x_speed*Cdrskin_libburn_speed_factoR + + Cdrskin_libburn_speed_addoN; + + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin_debug: k_speed= %d\n",k_speed)); + + burn_drive_set_speed(skin->drives[skin->driveno].drive,k_speed,k_speed); + return(1); +} + + +/** Obtain access to a libburn drive for writing or information retrieval. + If libburn is not restricted to a single persistent address then the + unused drives are dropped. This might be done by shutting down and + restartiing libburn with the wanted drive only. Thus, after this call, + libburn is supposed to have open only the reserved drive. + All other drives should be free for other use. + Warning: Do not store struct burn_drive pointer over this call. + Any such pointer might be invalid afterwards. + @param flag Bitfield for control purposes: + bit0= bus is unscanned, device is known, + use burn_drive_scan_and_grab() + bit1= do not load drive tray + bit2= do not issue error message on failure + @return <=0 error, 1 success +*/ +int Cdrskin_grab_drive(struct CdrskiN *skin, int flag) +{ + int ret,i,profile_number; + struct burn_drive *drive; + char profile_name[80]; +#ifdef Cdrskin_grab_abort_brokeN + int restore_handler= 0; +#endif + + i= 0;/* as long as its use is conditional, so gcc -Wall does not complain */ + + if(skin->drive_is_grabbed) + Cdrskin_release_drive(skin,0); + + if(flag&1) { + skin->driveno= 0; + drive= NULL; + skin->grabbed_drive= drive; + } else { + drive= skin->drives[skin->driveno].drive; + skin->grabbed_drive= drive; + } + +#ifdef Cdrskin_grab_abort_brokeN + + /* There seems to be no way to get a drive out of status BURN_DRIVE_GRABBING + So try to block out signals if there is a signal handler installed */ + if(skin->preskin->abort_handler==1 || + skin->preskin->abort_handler==3 || + skin->preskin->abort_handler==4) { + Cleanup_set_handlers(NULL,NULL,2); + restore_handler= 1; + } + +#endif /* ! Cdrskin_grab_abort_brokeN */ + +#ifndef Cdrskin_oldfashioned_api_usE + + + if(flag&1) { + ret= burn_drive_scan_and_grab(&(skin->drives),skin->preskin->device_adr, + !(flag&2)); + if(ret<=0) { + if(!(flag&4)) + fprintf(stderr,"cdrskin: FATAL : unable to open drive '%s'\n", + skin->preskin->device_adr); + goto ex; + } + skin->driveno= 0; + drive= skin->drives[skin->driveno].drive; + skin->grabbed_drive= drive; + } else { + if(strlen(skin->preskin->device_adr)<=0) { + + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr, + "cdrskin_debug: Cdrskin_grab_drive() dropping unwanted drives (%d)\n", + skin->n_drives-1)); + for(i=0;in_drives;i++) { + if(i==skin->driveno) + continue; + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr, + "cdrskin_debug: Cdrskin_grab_drive() dropped drive number %d\n",i)); + ret= burn_drive_info_forget(&(skin->drives[i]), 0); + if(ret==1 || ret==2) + continue; + fprintf(stderr, + "cdrskin: NOTE : Please inform libburn-hackers@pykix.org about:\n"); + fprintf(stderr, + "cdrskin: burn_drive_info_forget() returns %d\n",ret); + } + + } + +#else + + { + +#endif /* Cdrskin_oldfashioned_api_usE */ + + ret= burn_drive_grab(drive,!(flag&2)); + if(ret==0) { + if(!(flag&4)) + fprintf(stderr,"cdrskin: FATAL : unable to open drive %d\n", + skin->driveno); + goto ex; + } + +#ifdef Cdrskin_is_erasable_on_load_is_brokeN + /* RIP-14.5 + LITE-ON 48125S produce a false status if tray was unloaded */ + /* Therefore the first grab was just for loading */ + skin->drive_is_grabbed= 1; /* message to eventual abort handler */ + burn_drive_release(drive,0); + skin->drive_is_grabbed= 0; + + /* now grab the drive for real */ + ret= burn_drive_grab(drive,!(flag&2)); + if(ret==0) { + if(!(flag&4)) + fprintf(stderr,"cdrskin: FATAL : unable to open drive %d\n", + skin->driveno); + goto ex; + } +#endif /* ! Cdrskin_is_erasable_on_load_is_brokeN */ + + } + skin->drive_is_grabbed= 1; + + Cdrskin_speed_factoR= Cdrskin_cd_speed_factoR; + Cdrskin_libburn_speed_factoR= Cdrskin_libburn_cd_speed_factoR; + Cdrskin_libburn_speed_addoN= Cdrskin_libburn_cd_speed_addoN; +#ifdef Cdrskin_libburn_has_get_profilE + ret= burn_disc_get_profile(drive,&profile_number,profile_name); + if(ret>0) { + if(strstr(profile_name,"DVD")==profile_name) { + Cdrskin_speed_factoR= Cdrskin_dvd_speed_factoR; + Cdrskin_libburn_speed_factoR= Cdrskin_libburn_dvd_speed_factoR; + Cdrskin_libburn_speed_addoN= Cdrskin_libburn_dvd_speed_addoN; + } + } +#endif /* Cdrskin_libburn_has_get_profilE */ + + ret= 1; +ex:; + +#ifdef Cdrskin_grab_abort_brokeN + if(restore_handler) { + int Cdrskin_abort_handler(struct CdrskiN *, int, int); + Cleanup_set_handlers(skin,(Cleanup_app_handler_T) Cdrskin_abort_handler,4); + } +#endif /* Cdrskin_grab_abort_brokeN */ + + if(ret<=0) { + skin->drive_is_grabbed= 0; + skin->grabbed_drive= NULL; + } + return(ret); +} + + +/** Release grabbed libburn drive + @param flag Bitfield for control purposes: + bit0= eject +*/ +int Cdrskin_release_drive(struct CdrskiN *skin, int flag) +{ + if((!skin->drive_is_grabbed) || skin->grabbed_drive==NULL) { + fprintf(stderr,"cdrskin: CAUGHT : release of non-grabbed drive.\n"); + return(0); + } + burn_drive_release(skin->grabbed_drive,(flag&1)); + skin->drive_is_grabbed= 0; + skin->grabbed_drive= NULL; + return(1); +} + + +/** Clean up resources in abort situations. To be called by Cleanup subsystem + but hardly ever by the application. The program must exit afterwards. +*/ +int Cdrskin_abort_handler(struct CdrskiN *skin, int signum, int flag) +{ + +#ifdef Cdrskin_libburn_has_burn_aborT + + int ret; + +#else + + int wait_grain= 100000,first_status= 1; + double start_time,last_time,current_time; + +#endif /* ! Cdrskin_libburn_has_burn_aborT */ + + struct burn_progress p; + enum burn_drive_status drive_status= BURN_DRIVE_GRABBING; + + if(getpid()!=skin->control_pid) { + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr, + "\ncdrskin_debug: ABORT : [%d] Thread rejected: pid=%d, signum=%d\n", + skin->control_pid,getpid(),signum)); + +#ifdef Not_yeT + /* >>> find more abstract and system independent way to determine + signals which make no sense with a return */ + if(signum==11) { + Cleanup_set_handlers(NULL,NULL,1); /* allow abort */ + return(0); /* let exit */ + } +#endif + usleep(1000000); + + return(-2); /* do only process the control thread */ + } + if(skin->preskin->abort_handler==3) + Cleanup_set_handlers(NULL,NULL,2); /* ignore all signals */ + else if(skin->preskin->abort_handler==4) + Cleanup_set_handlers(NULL,NULL,1); /* allow abort */ + fprintf(stderr, + "\ncdrskin: ABORT : Handling started. Please do not press CTRL+C now.\n"); + if(skin->preskin->abort_handler==3) + fprintf(stderr,"cdrskin: ABORT : Trying to ignore any further signals\n"); + +#ifndef Cdrskin_extra_leaN + if(skin->fifo!=NULL) + Cdrfifo_close_all(skin->fifo,0); +#endif + +#ifdef Cdrskin_libburn_has_burn_aborT + + /* Only for user info */ + if(skin->grabbed_drive!=NULL) + drive_status= burn_drive_get_status(skin->grabbed_drive,&p); + if(drive_status!=BURN_DRIVE_IDLE) { + fprintf(stderr,"cdrskin: ABORT : Abort processing depends on speed and buffer size\n"); + fprintf(stderr,"cdrskin: ABORT : Usually it is done with 4x speed after about a MINUTE\n"); + fprintf(stderr,"cdrskin: URGE : But wait at least the normal burning time before any kill -9\n"); + } + + ret= burn_abort(skin->abort_max_wait, burn_abort_pacifier, "cdrskin: "); + if(ret<=0) { + fprintf(stderr, + "\ncdrskin: ABORT : Cannot cancel burn session and release drive.\n"); + return(0); + } + fprintf(stderr,"\n"); + +#else /* Cdrskin_libburn_has_burn_aborT */ + + if(skin->grabbed_drive!=NULL) { + drive_status= burn_drive_get_status(skin->grabbed_drive,&p); + if(drive_status!=BURN_DRIVE_IDLE && !skin->drive_is_grabbed) + skin->drive_is_grabbed= 2; + if(drive_status!=BURN_DRIVE_IDLE && !skin->drive_is_busy) + skin->drive_is_busy= 2; + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin_debug: ABORT : Drive status: %d\n", + (int) drive_status)); + } + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr, + "cdrskin_debug: ABORT : drive_is_grabbed=%d , drive_is_busy=%d (%X)\n", + skin->drive_is_grabbed,skin->drive_is_busy, + (unsigned int) skin->grabbed_drive)); + + if(skin->drive_is_grabbed) { + if(skin->drive_is_busy && skin->grabbed_drive!=NULL) { + if(drive_status==BURN_DRIVE_WRITING || drive_status==BURN_DRIVE_READING) { + fprintf(stderr,"cdrskin: ABORT : Trying to cancel drive operation.\n"); + burn_drive_cancel(skin->grabbed_drive); + } else if(drive_status==BURN_DRIVE_GRABBING) { + +#ifndef Cdrskin_oldfashioned_api_usE + int ret; + + fprintf(stderr, + "cdrskin: ABORT : Trying to close drive in process of grabbing\n"); + + /* >>> ??? rather inquire driveno from + skin->grabbed_drive->global_index ? */; + + ret= burn_drive_info_forget(&(skin->drives[skin->driveno]),1); + if(ret<=0) + fprintf(stderr, + "cdrskin: ABORT : Attempt to close drive failed (ret= %d)\n",ret); + else { + skin->drive_is_grabbed= 0; + skin->grabbed_drive= NULL; + goto try_to_finish_lib; + } + +#else + /* >>> what to do in this state ? */; +#endif /* Cdrskin_oldfashioned_api_usE */ + + } else if(drive_status!=BURN_DRIVE_IDLE) { + fprintf(stderr, + "cdrskin: ABORT : Will wait for current operation to end\n"); + } + if(drive_status!=BURN_DRIVE_IDLE) { + fprintf(stderr,"cdrskin: ABORT : Abort processing depends on speed and buffer size\n"); + fprintf(stderr,"cdrskin: ABORT : Usually it is done with 4x speed after about a MINUTE\n"); + fprintf(stderr,"cdrskin: URGE : But wait at least the normal burning time before any kill -9\n"); + } + last_time= start_time= Sfile_microtime(0); + while(1) { + drive_status= burn_drive_get_status(skin->grabbed_drive,&p); + if(drive_status==BURN_DRIVE_IDLE) + break; + usleep(wait_grain); + current_time= Sfile_microtime(0); + if(current_time-last_time>=1.0) { + if(first_status) + fprintf(stderr,"\n"); + first_status= 0; + fprintf(stderr,"\rcdrskin: ABORT : Status %d. Waiting for status %d since %d seconds (%d max)", + (int) drive_status, (int) BURN_DRIVE_IDLE, + (int) (current_time-start_time),skin->abort_max_wait); + last_time= current_time; + } + if(current_time-start_time>=skin->abort_max_wait) { + fprintf(stderr, + "\ncdrskin: ABORT : Cannot cancel burn session and release drive.\n"); + return(0); + } + } + fprintf(stderr,"\ncdrskin: ABORT : Status %d.\n",(int) drive_status); + } + fprintf(stderr,"cdrskin: ABORT : Trying to release drive.\n"); + Cdrskin_release_drive(skin,0); + } + +#ifndef Cdrskin_oldfashioned_api_usE +try_to_finish_lib:; +#endif + + if(skin->lib_is_initialized) { + fprintf(stderr,"cdrskin: ABORT : Trying to finish libburn.\n"); + burn_finish(); + } + +#endif /* ! Cdrskin_libburn_has_burn_aborT */ + + fprintf(stderr, + "cdrskin: ABORT : Drive is released and library is shut down now.\n"); + fprintf(stderr, + "cdrskin: ABORT : Program done. Even if you do not see a shell prompt.\n"); + return(1); +} + + +/** Convert a libburn device address into a libburn drive number + @return <=0 error, 1 success +*/ +int Cdrskin_driveno_of_location(struct CdrskiN *skin, char *devicename, + int *driveno, int flag) +{ + int i,ret; + char adr[Cdrskin_adrleN]; + + for(i=0;in_drives;i++) { + +#ifdef Cdrskin_libburn_has_drive_get_adR + ret= burn_drive_get_adr(&(skin->drives[i]), adr); + if(ret<=0) + continue; +#else + ret= 1; /* to please gcc -Wall */ + strcpy(adr,skin->drives[i].location); +#endif + + if(strcmp(adr,devicename)==0) { + *driveno= i; + return(1); + } + } + return(0); +} + + +/** Convert a cdrskin address into a libburn drive number + @return <=0 error, 1 success +*/ +int Cdrskin_dev_to_driveno(struct CdrskiN *skin, char *in_adr, int *driveno, + int flag) +{ + int ret; + char *adr,translated_adr[Cdrskin_adrleN],synthetic_adr[Cdrskin_adrleN]; + + adr= in_adr; + +#ifndef Cdrskin_extra_leaN + /* user defined address translation */ + ret= Cdradrtrn_translate(skin->adr_trn,adr,-1,translated_adr,0); + if(ret<=0) { + fprintf(stderr, + "cdrskin: FATAL : address translation failed (address too long ?) \n"); + return(0); + } + if(skin->verbosity>=Cdrskin_verbose_cmD && strcmp(adr,translated_adr)!=0) + printf("cdrskin: dev_translation=... : dev='%s' to dev='%s'\n", + adr,translated_adr); + adr= translated_adr; +#endif /* ! Cdrskin_extra_leaN */ + + if(adr[0]=='/') { + ret= Cdrskin_driveno_of_location(skin,adr,driveno,0); + if(ret<=0) { +location_not_found:; + fprintf(stderr, + "cdrskin: FATAL : cannot find '%s' among accessible drive devices.\n", + adr); + fprintf(stderr, + "cdrskin: HINT : use option --devices for a list of drive devices.\n"); + return(0); + } + return(1); + } + ret= Cdrpreskin__cdrecord_to_dev(adr,synthetic_adr,driveno, + !!skin->preskin->old_pseudo_scsi_adr); + if(ret<=0) { +wrong_devno:; + if(skin->n_drives<=0) { + fprintf(stderr,"cdrskin: FATAL : No accessible drives.\n"); + } else { + fprintf(stderr, + "cdrskin: FATAL : Address does not lead to an accessible drive: %s\n", + in_adr); + fprintf(stderr, + "cdrskin: HINT : dev= expects /dev/xyz, Bus,Target,0 or a number [0,%d]\n", + skin->n_drives-1); + } + return(0); + } + if(strlen(synthetic_adr)>0) { + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: converted address '%s' to '%s'\n",adr,synthetic_adr); + ret= Cdrskin_driveno_of_location(skin,synthetic_adr,driveno,0); + if(ret<=0) { + fprintf(stderr, + "cdrskin: failure while using address converted from '%s'\n",adr); + adr= synthetic_adr; + goto location_not_found; + } + } + if((*driveno)>=skin->n_drives || (*driveno)<0) { + fprintf(stderr,"cdrskin: obtained drive number %d from '%s'\n", + *driveno,adr); + goto wrong_devno; + } + return(1); +} + + +/** Convert a libburn drive number into a cdrecord-style address which + represents a device address if possible and the drive number else. + @param flag Bitfield for control purposes: + bit0= do not apply user defined address translation + @return <0 error, + pseudo transport groups: + 0 volatile drive number, + 1 /dev/sgN, 2 /dev/hdX, + 1000000+busno = non-pseudo SCSI bus + 2000000+busno = pseudo-ATA|ATAPI SCSI bus (currently busno==2) +*/ +int Cdrskin_driveno_to_btldev(struct CdrskiN *skin, int driveno, + char btldev[Cdrskin_adrleN], int flag) +{ + int k,ret,still_untranslated= 1,hret; + char *loc= NULL,buf[Cdrskin_adrleN],adr[Cdrskin_adrleN]; + + if(driveno<0 || driveno>skin->n_drives) + goto fallback; + +#ifdef Cdrskin_libburn_has_drive_get_adR + ret= burn_drive_get_adr(&(skin->drives[driveno]), adr); + if(ret<=0) + goto fallback; + loc= adr; +#else + adr[0]= 0; /* to please gcc -Wall */ + loc= skin->drives[driveno].location; + if(loc==NULL) + goto fallback; +#endif + +#ifdef Cdrskin_libburn_has_convert_scsi_adR + if(!skin->preskin->old_pseudo_scsi_adr) { + int host_no= -1,channel_no= -1,target_no= -1,lun_no= -1, bus_no= -1; + + ret= burn_drive_obtain_scsi_adr(loc,&bus_no,&host_no,&channel_no, + &target_no,&lun_no); + if(ret<=0) { + if(strncmp(loc,"/dev/hd",7)==0) + if(loc[7]>='a' && loc[7]<='z') + if(loc[8]==0) { + bus_no= (loc[7]-'a')/2; + sprintf(btldev,"%d,%d,0",bus_no,(loc[7]-'a')%2); + {ret= 2000000 + bus_no; goto adr_translation;} + } + goto fallback; + } else { + sprintf(btldev,"%d,%d,%d",bus_no,target_no,lun_no); + ret= 1000000+bus_no; + goto adr_translation; + } + } +#endif + + if(strncmp(loc,"/dev/sg",7)==0) { + for(k= 7;loc[k]!=0;k++) + if(loc[k]<'0' || loc[k]>'9') + break; + if(loc[k]==0 && k>7) { + sprintf(btldev,"1,%s,0",loc+7); + {ret= 1; goto adr_translation;} + } + } + if(strncmp(loc,"/dev/hd",7)==0) + if(loc[7]>='a' && loc[7]<='z') + if(loc[8]==0) { + sprintf(btldev,"2,%d,0",loc[7]-'a'); + {ret= 2; goto adr_translation;} + } +fallback:; + if(skin->preskin->old_pseudo_scsi_adr) { + sprintf(btldev,"0,%d,0",driveno); + } else { + if(loc!=NULL) + strcpy(btldev,loc); + else + sprintf(btldev,"%d",driveno); + } + ret= 0; + +adr_translation:; +#ifndef Cdrskin_extra_leaN + /* user defined address translation */ + if(!(flag&1)) { + if(ret>0) { + /* try wether a translation points to loc */ + hret= Cdradrtrn_translate(skin->adr_trn,loc,driveno,buf,1); + if(hret==2) { + still_untranslated= 0; + strcpy(btldev,buf); + } + } + if(still_untranslated) { + Cdradrtrn_translate(skin->adr_trn,btldev,driveno,buf,1); + strcpy(btldev,buf); + } + } +#endif /* ! Cdrskin_extra_leaN */ + + return(ret); +} + + +/** Report media status s to the user */ +int Cdrskin_report_disc_status(struct CdrskiN *skin, enum burn_disc_status s, + int flag) +{ + printf("cdrskin: status %d ",s); + if(s==BURN_DISC_FULL) { + printf("burn_disc_full \"There is a disc with data on it in the drive\"\n"); + } else if(s==BURN_DISC_BLANK) { + printf("burn_disc_blank \"The drive holds a blank disc\"\n"); + } else if(s==BURN_DISC_APPENDABLE) { + printf( + "BURN_DISC_APPENDABLE \"There is an incomplete disc in the drive\"\n"); + } else if(s==BURN_DISC_EMPTY) { + printf("BURN_DISC_EMPTY \"There is no disc at all in the drive\"\n"); + } else if(s==BURN_DISC_UNREADY) { + printf("BURN_DISC_UNREADY \"The current status is not yet known\"\n"); + +#ifdef Cdrskin_libburn_has_burn_disc_unsuitablE + + } else if(s==BURN_DISC_UNGRABBED) { + printf("BURN_DISC_UNGRABBED \"API usage error: drive not grabbed\"\n"); + } else if(s==BURN_DISC_UNSUITABLE) { + printf("BURN_DISC_UNSUITABLE \"Media is not suitable\"\n"); + +#endif /* Cdrskin_libburn_has_burn_disc_unsuitablE */ + + } else + printf("-unknown status code-\n"); + return(1); +} + + +/** Perform operations -scanbus or --devices + @param flag Bitfield for control purposes: + bit0= perform --devices rather than -scanbus + @return <=0 error, 1 success +*/ +int Cdrskin_scanbus(struct CdrskiN *skin, int flag) +{ + int ret,i,busno,first_on_bus,pseudo_transport_group= 0,skipped_devices= 0; + int busmax= 16; + char shellsafe[5*Cdrskin_strleN+2],perms[40],btldev[Cdrskin_adrleN]; + char adr[Cdrskin_adrleN],*raw_dev,*drives_shown= NULL; + struct stat stbuf; + + drives_shown= malloc(skin->n_drives+1); + if(drives_shown==NULL) + {ret= -1; goto ex;} + for(i=0;in_drives;i++) + drives_shown[i]= 0; + if(flag&1) { + printf("cdrskin: Overview of accessible drives (%d found) :\n", + skin->n_drives); + printf("-----------------------------------------------------------------------------\n"); + for(i=0;in_drives;i++) { + +#ifdef Cdrskin_libburn_has_drive_get_adR + ret= burn_drive_get_adr(&(skin->drives[i]), adr); + if(ret<=0) { + /* >>> one should massively complain */; + continue; + } +#else + strcpy(adr,skin->drives[i].location); +#endif + + if(stat(adr,&stbuf)==-1) { + sprintf(perms,"errno=%d",errno); + } else { + strcpy(perms,"------"); + if(stbuf.st_mode&S_IRUSR) perms[0]= 'r'; + if(stbuf.st_mode&S_IWUSR) perms[1]= 'w'; + if(stbuf.st_mode&S_IRGRP) perms[2]= 'r'; + if(stbuf.st_mode&S_IWGRP) perms[3]= 'w'; + if(stbuf.st_mode&S_IROTH) perms[4]= 'r'; + if(stbuf.st_mode&S_IWOTH) perms[5]= 'w'; + } + if(strlen(adr)>=Cdrskin_strleN) + Text_shellsafe("failure:oversized string",shellsafe,0); + else + Text_shellsafe(adr,shellsafe,0); + printf("%d dev=%s %s : '%s' '%s'\n", + i,shellsafe,perms,skin->drives[i].vendor,skin->drives[i].product); + } + printf("-----------------------------------------------------------------------------\n"); + } else { + if(!skin->preskin->old_pseudo_scsi_adr) { + pseudo_transport_group= 1000000; + raw_dev= skin->preskin->raw_device_adr; + if(strncmp(raw_dev,"ATA",3)==0 && (raw_dev[3]==0 || raw_dev[3]==':')) + pseudo_transport_group= 2000000; + if(strncmp(raw_dev,"ATAPI",5)==0 && (raw_dev[5]==0 || raw_dev[5]==':')) + pseudo_transport_group= 2000000; + if(pseudo_transport_group==2000000) { + fprintf(stderr,"scsidev: 'ATA'\ndevname: 'ATA'\n"); + fprintf(stderr,"scsibus: -2 target: -2 lun: -2\n"); + } + } + /* >>> fprintf(stderr,"Linux sg driver version: 3.1.25\n"); */ + printf("Using libburn version '%s'.\n", Cdrskin_libburn_versioN); + if(pseudo_transport_group!=1000000) + if(skin->preskin->old_pseudo_scsi_adr) + printf("cdrskin: NOTE : The printed addresses are not cdrecord compatible !\n"); + + for(busno= 0;busno<=busmax;busno++) { + first_on_bus= 1; + for(i=0;in_drives;i++) { + ret= Cdrskin_driveno_to_btldev(skin,i,btldev,1); + if(busno==busmax && drives_shown[i]==0) { + if(ret/1000000 != pseudo_transport_group) { + skipped_devices++; + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin_debug: skipping drive '%s%s'\n", + ((ret/1000000)==2?"ATA:":""), btldev)); + continue; + } + } else if(ret != pseudo_transport_group + busno) + continue; + if(first_on_bus) + printf("scsibus%d:\n",busno); + first_on_bus= 0; + printf("\t%s\t %d) '%-8s' '%-16s' '%-4s' Removable CD-ROM\n", + btldev,i,skin->drives[i].vendor,skin->drives[i].product, + skin->drives[i].revision); + drives_shown[i]= 1; + } + } + } + if(skipped_devices>0) { + if(skipped_devices>1) + printf("cdrskin: NOTE : There were %d drives not shown.\n", + skipped_devices); + else + printf("cdrskin: NOTE : There was 1 drive not shown.\n"); + printf("cdrskin: HINT : To surely see all drives try option: --devices\n"); + if(pseudo_transport_group!=2000000) + printf("cdrskin: HINT : or try options: dev=ATA -scanbus\n"); + } + ret= 1; +ex:; + if(drives_shown!=NULL) + free((char *) drives_shown); + return(ret); +} + + +/** Perform -checkdrive . + @param flag Bitfield for control purposes: + bit0= do not print message about pseudo-checkdrive + @return <=0 error, 1 success +*/ +int Cdrskin_checkdrive(struct CdrskiN *skin, char *profile_name, int flag) +{ + struct burn_drive_info *drive_info; + int ret; + char btldev[Cdrskin_adrleN]; + + if(!(flag&1)) + printf("cdrskin: pseudo-checkdrive on drive %d\n",skin->driveno); + if(skin->driveno>=skin->n_drives || skin->driveno<0) { + fprintf(stderr,"cdrskin: FATAL : there is no drive #%d\n",skin->driveno); + {ret= 0; goto ex;} + } + drive_info= &(skin->drives[skin->driveno]); + ret= Cdrskin_driveno_to_btldev(skin,skin->driveno,btldev,0); + if(ret>=0) + fprintf(stderr,"scsidev: '%s'\n",btldev); + printf("Device type : %s\n","Removable CD-ROM"); + printf("Vendor_info : '%s'\n",drive_info->vendor); + printf("Identifikation : '%s'\n",drive_info->product); + printf("Revision : '%s'\n",drive_info->revision); + printf("Driver flags : %s\n","BURNFREE"); +#ifdef Cdrskin_allow_libburn_taO + + /* <<< */ + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr, + "cdrskin_debug: block_types: tao=%4.4X sao=%4.4X raw=%4.4X\n", + drive_info->tao_block_types,drive_info->sao_block_types, + drive_info->raw_block_types)); + + printf("Supported modes:"); + if((drive_info->tao_block_types & (BURN_BLOCK_MODE1|BURN_BLOCK_RAW0)) + == (BURN_BLOCK_MODE1|BURN_BLOCK_RAW0)) + printf(" TAO"); + if(drive_info->sao_block_types & BURN_BLOCK_SAO) + printf(" SAO"); + if((drive_info->raw_block_types & BURN_BLOCK_RAW96R) && + strstr(profile_name,"DVD")!=profile_name) + printf(" RAW/RAW96R"); + printf("\n"); + +#else + printf("Supported modes: %s\n","SAO RAW/R96R"); +#endif + ret= 1; +ex:; + return(ret); +} + + +int Cdrskin_obtain_nwa(struct CdrskiN *skin, int *nwa, int flag) +{ + int ret,lba; + struct burn_drive *drive; + struct burn_write_opts *o= NULL; + + /* Set write opts in order to provoke MODE SELECT. LG GSA-4082B needs it. */ + drive= skin->drives[skin->driveno].drive; + o= burn_write_opts_new(drive); + if(o!=NULL) { + burn_write_opts_set_perform_opc(o, 0); + burn_write_opts_set_write_type(o,skin->write_type,skin->block_type); + burn_write_opts_set_underrun_proof(o,skin->burnfree); + } +#ifdef Cdrskin_libburn_has_multI + ret= burn_disc_track_lba_nwa(drive,o,0,&lba,nwa); +#else + ret= 0; + lba= 0;/* silence gcc warning */ +#endif + if(o!=NULL) + burn_write_opts_free(o); + return(ret); +} + + +/** Print lba of first track of last session and Next Writeable Address of + the next unwritten session. +*/ +int Cdrskin_msinfo(struct CdrskiN *skin, int flag) +{ + int num_sessions, session_no, ret, num_tracks; + int nwa= -123456789, lba= -123456789, aux_lba; + char msg[80]; + enum burn_disc_status s; + struct burn_drive *drive; + struct burn_disc *disc= NULL; + struct burn_session **sessions; + struct burn_track **tracks; + struct burn_toc_entry toc_entry; + + ret= Cdrskin_grab_drive(skin,0); + if(ret<=0) + return(ret); + drive= skin->drives[skin->driveno].drive; + +#ifndef Cdrskin_no_aftergrab_loopS + while(burn_drive_get_status(drive,NULL) != BURN_DRIVE_IDLE) + usleep(100002); + while ((s= burn_disc_get_status(drive)) == BURN_DISC_UNREADY) + usleep(100002); +#else + s= burn_disc_get_status(drive); +#endif /* ! Cdrskin_no_aftergrab_loopS */ + + if(s!=BURN_DISC_APPENDABLE) { + Cdrskin_report_disc_status(skin,s,0); + fprintf(stderr, + "cdrskin: FATAL : -msinfo can only operate on appendable (i.e. -multi) CD\n"); + {ret= 0; goto ex;} + } + disc= burn_drive_get_disc(drive); + if(disc==NULL) { + fprintf(stderr,"cdrskin: FATAL : Cannot obtain info about CD content\n"); + {ret= 0; goto ex;} + } + sessions= burn_disc_get_sessions(disc,&num_sessions); + for(session_no= 0; session_no0) + nwa= aux_lba+6900; + else + nwa= aux_lba+11400; + } + if(skin->msinfo_fd>=0) { + sprintf(msg,"%d,%d\n",lba,nwa); + write(skin->msinfo_fd,msg,strlen(msg)); + } else + printf("%d,%d\n",lba,nwa); + + if(strlen(skin->msifile)) { + FILE *fp; + + fp = fopen(skin->msifile, "w"); + if(fp==NULL) { + if(errno>0) + fprintf(stderr,"cdrskin: %s (errno=%d)\n", strerror(errno), errno); + fprintf(stderr,"cdrskin: FATAL : Cannot write msinfo to file '%s'\n", + skin->msifile); + {ret= 0; goto ex;} + } + fprintf(fp,"%d,%d",lba,nwa); + fclose(fp); + } + ret= 1; +ex:; + + /* must calm down my NEC ND-4570A afterwards */ + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin_debug: doing extra release-grab cycle\n")); + Cdrskin_release_drive(skin,0); + Cdrskin_grab_drive(skin,0); + + Cdrskin_release_drive(skin,0); + return(ret); +} + + +/** Perform -toc under control of Cdrskin_atip(). + @param flag Bitfield for control purposes: + bit0= do not list sessions separately (do it cdrecord style) + @return <=0 error, 1 success +*/ +int Cdrskin_toc(struct CdrskiN *skin, int flag) +{ + int num_sessions= 0,num_tracks= 0,lba= 0,track_count= 0,total_tracks= 0; + int session_no, track_no; + struct burn_drive *drive; + struct burn_disc *disc= NULL; + struct burn_session **sessions; + struct burn_track **tracks; + struct burn_toc_entry toc_entry; + + drive= skin->drives[skin->driveno].drive; + + disc= burn_drive_get_disc(drive); + if(disc==NULL) + goto cannot_read; + sessions= burn_disc_get_sessions(disc,&num_sessions); + if(flag&1) { + for(session_no= 0; session_no>> From where does cdrecord take "mode" ? */ + + /* This is not the "mode" as printed by cdrecord : + printf(" mode: %d\n",burn_track_get_mode(tracks[track_no])); + */ + /* own guess: cdrecord says "1" on data and "0" on audio : */ + printf(" mode: %d\n",((toc_entry.control&7)<4?0:1)); + + } + if((flag&1) && session_nodriveno); + ret= Cdrskin_grab_drive(skin,0); + if(ret<=0) + return(ret); + drive= skin->drives[skin->driveno].drive; + +#ifndef Cdrskin_no_aftergrab_loopS + while(burn_drive_get_status(drive,NULL) != BURN_DRIVE_IDLE) + usleep(100002); + while ((s= burn_disc_get_status(drive)) == BURN_DISC_UNREADY) + usleep(100002); +#else + s= burn_disc_get_status(drive); +#endif + + Cdrskin_report_disc_status(skin,s,0); + if(s==BURN_DISC_APPENDABLE && skin->no_blank_appendable) { + is_not_really_erasable= 1; + } else if(s==BURN_DISC_EMPTY) { + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("Current: none\n"); + ret= 0; goto ex; + } + + +#ifdef Cdrskin_atip_speed_brokeN + + /* <<< terrible stunt to get correct media speed info */ + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr, + "cdrskin_debug: redoing startup for speed inquiry stabilization\n")); + + +#ifndef Cdrskin_oldfashioned_api_usE + + if(strlen(skin->preskin->device_adr)<=0) + burn_drive_get_adr(&(skin->drives[skin->driveno]), + skin->preskin->device_adr); + + Cdrskin_release_drive(skin,0); + burn_finish(); + if(!burn_initialize()) { + fflush(stdout); + fprintf(stderr,"cdrskin : FATAL : Re-initialization of libburn failed\n"); + {ret= 0; goto ex;} + } + ret= Cdrskin_grab_drive(skin,1); /* uses burn_drive_scan_and_grab() */ + if(ret<=0) + return(ret); + drive= skin->drives[skin->driveno].drive; + +#else /* ! Cdrskin_oldfashioned_api_usE */ + + Cdrskin_release_drive(skin,0); + burn_finish(); + if(!burn_initialize()) { + fflush(stdout); + fprintf(stderr,"cdrskin : FATAL : Re-initialization of libburn failed\n"); + {ret= 0; goto ex;} + } + if(strlen(skin->preskin->device_adr)>0) + burn_drive_add_whitelist(skin->preskin->device_adr); + while(!burn_drive_scan(&(skin->drives),&(skin->n_drives))) + usleep(1002); + ret= Cdrskin_grab_drive(skin,0); + if(ret<=0) + return(ret); + drive= skin->drives[skin->driveno].drive; + +#endif /* Cdrskin_oldfashioned_api_usE */ + +#endif /* Cdrskin_atip_speed_brokeN */ + + profile_name[0]= 0; +#ifdef Cdrskin_libburn_has_get_profilE + ret= burn_disc_get_profile(drive,&profile_number,profile_name); + if(ret<=0) { + profile_number= 0; + strcpy(profile_name, "-unidentified-"); + } +#endif /* Cdrskin_libburn_has_get_profilE */ + + ret= Cdrskin_checkdrive(skin,profile_name,1); + if(ret<=0) + return(ret); + +#ifdef Cdrskin_libburn_has_read_atiP + if(burn_disc_get_status(drive) != BURN_DISC_UNSUITABLE) { + ret= burn_disc_read_atip(drive); + if(ret>0) { + ret= burn_drive_get_min_write_speed(drive); + x_speed_min= ((double) ret)/Cdrskin_libburn_speed_factoR; + } + } +#endif + +#ifdef Cdrskin_libburn_has_burn_disc_unsuitablE + if(burn_disc_get_status(drive) == BURN_DISC_UNSUITABLE) { + if(skin->verbosity>=Cdrskin_verbose_progresS) { +#ifdef Cdrskin_libburn_has_get_profilE + if(profile_name[0]) + printf("Current: %s\n",profile_name); + else + printf("Current: UNSUITABLE MEDIA (Profile %4.4Xh)\n",profile_number); +#else + printf("Current: UNSUITABLE MEDIA\n"); +#endif + } + {ret= 0; goto ex;} + } +#endif /* Cdrskin_libburn_has_burn_disc_unsuitablE */ + + ret= burn_drive_get_write_speed(drive); + x_speed_max= ((double) ret)/Cdrskin_libburn_speed_factoR; + if(x_speed_min<0) + x_speed_min= x_speed_max; + printf("cdrskin: burn_drive_get_write_speed = %d (%.1fx)\n",ret,x_speed_max); + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(profile_name[0]) + printf("Current: %s\n",profile_name); + else if(burn_disc_erasable(drive)) + printf("Current: CD-RW\n"); + else + printf("Current: CD-R\n"); + } + if(strstr(profile_name,"DVD")==profile_name) { + /* These are dummy messages for project scdbackup, so its media recognition + gets a hint that the media is suitable and not in need of blanking. + scdbackup will learn to interpret cdrskin's DVD messages but the + current stable version needs to believe it is talking to its own + growisofs_wrapper. So this is an emulation of an emulator. + */ + printf("book type: %s (emulated booktype)\n", profile_name); + if(profile_number==0x13) /* DVD-RW */ + printf("cdrskin: message for sdvdbackup: \"(growisofs mode Restricted Overwrite)\"\n"); + } else { + printf("ATIP info from disk:\n"); + if(burn_disc_erasable(drive)) { + if(is_not_really_erasable) + printf(" Is erasable (but not while in this incomplete state)\n"); + else + printf(" Is erasable\n"); + } else { + printf(" Is not erasable\n"); + } + +#ifdef Cdrskin_libburn_has_get_start_end_lbA + { int start_lba,end_lba,min,sec,fr; + ret= burn_drive_get_start_end_lba(drive,&start_lba,&end_lba,0); + if(ret>0) { + burn_lba_to_msf(start_lba,&min,&sec,&fr); + printf(" ATIP start of lead in: %d (%-2.2d:%-2.2d/%-2.2d)\n", + start_lba,min,sec,fr); + burn_lba_to_msf(end_lba,&min,&sec,&fr); + printf(" ATIP start of lead out: %d (%-2.2d:%-2.2d/%-2.2d)\n", + end_lba,min,sec,fr); + } + } +#endif /* Cdrskin_libburn_has_get_start_end_lbA */ + + printf(" 1T speed low: %.f 1T speed high: %.f\n",x_speed_min,x_speed_max); + } + + ret= 1; + if(flag&1) + Cdrskin_toc(skin,1);/*cdrecord seems to ignore -toc errors if -atip is ok */ +ex:; + Cdrskin_release_drive(skin,0); + + /* A61227 : + A kindof race condition with -atip on filled CD-RW and following grabs + under SuSE 9.3. Waiting seems to help. I suspect the media demon. */ + usleep(200000); + + return(ret); +} + + +#ifndef Cdrskin_extra_leaN + +/** Emulate the gracetime= behavior of cdrecord + @param flag Bitfield for control purposes: + bit0= do not print message about pseudo-checkdrive +*/ +int Cdrskin_wait_before_action(struct CdrskiN *skin, int flag) +/* flag: bit0= BLANK rather than write mode + bit1= FORMAT rather than write mode +*/ +{ + int i; + + if(skin->verbosity>=Cdrskin_verbose_progresS) { + char speed_text[80]; + if(skin->x_speed<0) + strcpy(speed_text,"MAX"); + else if(skin->x_speed==0) + strcpy(speed_text,"MIN"); + else + sprintf(speed_text,"%.f",skin->x_speed); + printf( + "Starting to write CD/DVD at speed %s in %s %s mode for %s session.\n", + speed_text,(skin->dummy_mode?"dummy":"real"), + (flag&2?"FORMAT":(flag&1?"BLANK":skin->preskin->write_mode_name)), + (skin->multi?"multi":"single")); + printf("Last chance to quit, starting real write in %3d seconds.", + skin->gracetime); + fflush(stdout); + } + for(i= skin->gracetime-1;i>=0;i--) { + usleep(1000000); + if(skin->verbosity>=Cdrskin_verbose_progresS) { + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b %3d seconds.",i); + fflush(stdout); + } + } + if(skin->verbosity>=Cdrskin_verbose_progresS) + {printf(" Operation starts.\n");fflush(stdout);} + return(1); +} + +#endif /* Cdrskin_extra_leaN */ + + +/** Perform blank=[all|fast] + @return <=0 error, 1 success +*/ +int Cdrskin_blank(struct CdrskiN *skin, int flag) +{ + enum burn_disc_status s; + struct burn_progress p; + struct burn_drive *drive; + int ret,loop_counter= 0,hint_force= 0,do_format= 0, profile_number= -1; + int wrote_well= 1; + double start_time; + char *verb= "format", *presperf="blanking", *fmt_text= "format_..."; + char profile_name[80]; + static char fmtp[][40]= { + "format_default", "format_overwrite", "format_sequential"}; + static int fmtp_max= 2; + + start_time= Sfile_microtime(0); /* will be refreshed later */ + ret= Cdrskin_grab_drive(skin,0); + if(ret<=0) + return(ret); + drive= skin->drives[skin->driveno].drive; + +#ifndef Cdrskin_no_aftergrab_loopS + while(burn_drive_get_status(drive,NULL) != BURN_DRIVE_IDLE) + usleep(100002); + while ((s= burn_disc_get_status(drive)) == BURN_DISC_UNREADY) + usleep(100002); +#else + s= burn_disc_get_status(drive); +#endif + + profile_name[0]= 0; +#ifdef Cdrskin_libburn_has_get_profilE + if(skin->grabbed_drive) + burn_disc_get_profile(skin->grabbed_drive,&profile_number,profile_name); +#endif + + if(skin->verbosity>=Cdrskin_verbose_progresS) + Cdrskin_report_disc_status(skin,s,0); + do_format= skin->blank_format_type & 0xff; + if(do_format) { + verb= "format"; + presperf= "formatting"; + } + +#ifdef Cdrskin_libburn_has_pretend_fulL + if(s==BURN_DISC_UNSUITABLE) { + if(skin->force_is_set) { + fprintf(stderr, + "cdrskin: NOTE : -force blank=... : Treating unsuitable media as burn_disc_full\n"); + ret= burn_disc_pretend_full(drive); + s= burn_disc_get_status(drive); + } else + hint_force= 1; + } +#endif /* Cdrskin_libburn_has_pretend_fulL */ + + if(do_format) { + if(do_format>=0 && do_format<=fmtp_max) + fmt_text= fmtp[do_format]; + if(do_format!=1) { + + /* >>> other formats to come. At least _sequential */; + + fprintf(stderr, + "cdrskin: SORRY : only blank=%s is implemented yet\n",fmtp[1]); + return(0); + } else if(profile_number == 0x14) { /* DVD-RW sequential */ + if(do_format!=1) + goto unsupported_format_type; + } else if(profile_number == 0x13) { /* DVD-RW restricted overwrite */ + if(do_format==1 && skin->force_is_set) { + /* ok */; + } else if(do_format!=1) { +unsupported_format_type:; + fprintf(stderr, + "cdrskin: SORRY : blank=%s : unsupported format and/or media type\n", + fmt_text); + return(0); + } else { + fprintf(stderr, + "cdrskin: NOTE : blank=format_... : media is already formatted\n"); + fprintf(stderr, + "cdrskin: HINT : If you really want to re-format, add option -force\n"); + return(2); + } + } else if(profile_number == 0x1a) { /* DVD+RW */ + if(do_format!=1) + goto unsupported_format_type; + if(!((skin->blank_format_type>>8)&4)) { + fprintf(stderr, + "cdrskin: NOTE : blank=format_... : DVD+RW do not need this\n"); + fprintf(stderr, + "cdrskin: HINT : For de-icing use option blank=format_overwrite_full"); + fprintf(stderr, + "cdrskin: HINT : If you really want to re-format, add option -force\n"); + return(2); + } + } else { + fprintf(stderr, + "cdrskin: SORRY : blank=%s for now does DVD+/-RW only\n",fmt_text); + return(0); + } + if(s==BURN_DISC_UNSUITABLE) + fprintf(stderr, + "cdrskin: NOTE : blank=%s accepted not yet suitable media\n", + fmt_text); + + } else { /* do_format */ + + if(s!=BURN_DISC_FULL && + (s!=BURN_DISC_APPENDABLE || skin->no_blank_appendable)) { + Cdrskin_release_drive(skin,0); + if(s==BURN_DISC_BLANK) { + fprintf(stderr, + "cdrskin: NOTE : blank=... : media was already blank (and still is)\n"); + return(2); + } else if(s==BURN_DISC_APPENDABLE) { + fprintf(stderr, + "cdrskin: FATAL : blank=... : media is still appendable\n"); + } else { + fprintf(stderr, + "cdrskin: FATAL : blank=... : no blankworthy disc found\n"); + if(hint_force) + fprintf(stderr, + "cdrskin: HINT : If you are certain to have a CD-RW, try option -force\n"); + } + return(0); + } + if(!burn_disc_erasable(drive)) { + fprintf(stderr,"cdrskin: FATAL : blank=... : media is not erasable\n"); + return(0); + } + + } /* ! do_format */ + + if(skin->dummy_mode) { + fprintf(stderr, + "cdrskin: would have begun to %s disc if not in -dummy mode\n", + verb); + goto blanking_done; + } + fprintf(stderr,"cdrskin: beginning to %s disc\n",verb); + Cdrskin_adjust_speed(skin,0); + +#ifndef Cdrskin_extra_leaN + Cdrskin_wait_before_action(skin,1+(!!do_format)); +#endif /* ! Cdrskin_extra_leaN */ + + skin->drive_is_busy= 1; + if(do_format==0) { + burn_disc_erase(drive,skin->blank_fast); + +#ifdef Cdrskin_libburn_has_burn_disc_formaT + } else if(do_format==1) { + burn_disc_format(drive,(off_t) skin->blank_format_size, + ((skin->blank_format_type>>8)&0xff) | ((!!skin->force_is_set)<<4)); +#endif + + } else { + + /* >>> */; + + /* <<< */ + fprintf(stderr,"cdrskin: SORRY : Format type %d not implemented yet.\n", + do_format); + ret= 0; goto ex; + } + + loop_counter= 0; + start_time= Sfile_microtime(0); + while(burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) { + if(loop_counter>0) + if(skin->verbosity>=Cdrskin_verbose_progresS) { + double percent= 50.0; + + if(p.sectors>0) /* i want a display of 1 to 99 percent */ + percent= 1.0+((double) p.sector+1.0)/((double) p.sectors)*98.0; + fprintf(stderr, + "\rcdrskin: %s ( done %.1f%% , %lu seconds elapsed ) ", + presperf,percent,(unsigned long) (Sfile_microtime(0)-start_time)); + } + sleep(1); + loop_counter++; + } +blanking_done:; +#ifdef Cdrskin_libburn_has_wrote_welL + wrote_well = burn_drive_wrote_well(drive); +#endif + if(wrote_well && skin->verbosity>=Cdrskin_verbose_progresS) { + fprintf(stderr, + "\rcdrskin: %s done \n", + presperf); + printf("%s time: %.3fs\n",(do_format?"Formatting":"Blanking"), + Sfile_microtime(0)-start_time); + } + fflush(stdout); + if(!wrote_well) + fprintf(stderr, + "\rcdrskin: %s failed \n", + presperf); + ret= !!(wrote_well); +ex:; + skin->drive_is_busy= 0; + Cdrskin_release_drive(skin,0); + return(ret); +} + + +/** Report burn progress. This is done partially in cdrecord style. + Actual reporting happens only if write progress hit the next MB or if in + non-write-progress states a second has elapsed since the last report. + After an actual report a new statistics interval begins. + @param drive_status As obtained from burn_drive_get_status() + @param p Progress information from burn_drive_get_status() + @param start_time Timestamp of burn start in seconds + @param last_time Timestamp of report interval start in seconds + @param total_count Returns the total number of bytes written so far + @param total_count Returns the number of bytes written during interval + @param flag Bitfield for control purposes: + bit0= report in growisofs style rather than cdrecord style + @return <=0 error, 1 seems to be writing payload, 2 doing something else +*/ +int Cdrskin_burn_pacifier(struct CdrskiN *skin, + enum burn_drive_status drive_status, + struct burn_progress *p, + double start_time, double *last_time, + double *total_count, double *last_count, + int *min_buffer_fill, int flag) +/* + bit0= growisofs style +*/ +{ + double bytes_to_write,written_bytes= 0.0,written_total_bytes= 0.0,buffer_size; + double fixed_size,padding,sector_size,speed_factor; + double measured_total_speed,measured_speed; + double elapsed_time,elapsed_total_time,current_time; + double estim_time,estim_minutes,estim_seconds,percent; + int ret,fifo_percent,fill,space,advance_interval=0,new_mb,old_mb,time_to_tell; + int fs,bs,old_track_idx,buffer_fill,formatting= 0; + char fifo_text[80],mb_text[40]; + char *debug_mark= ""; /* use this to prepend a marker text for experiments */ + + /* for debugging */ + static double last_fifo_in= 0.0,last_fifo_out= 0.0,curr_fifo_in,curr_fifo_out; + + current_time= Sfile_microtime(0); + elapsed_total_time= current_time-start_time; + elapsed_time= current_time-*last_time; + time_to_tell= (elapsed_time>=1.0)&&(elapsed_total_time>=1.0); + written_total_bytes= *last_count; /* to be overwritten by p.sector */ + +#ifdef Cdrskin_libburn_has_bd_formattinG + if(drive_status==BURN_DRIVE_FORMATTING) + formatting= 1; +#endif + + if(drive_status==BURN_DRIVE_WRITING) { + ; + } else if(drive_status==BURN_DRIVE_WRITING_LEADIN + +#ifdef Cdrskin_allow_libburn_taO + || drive_status==BURN_DRIVE_WRITING_PREGAP +#endif + || formatting) { + if(time_to_tell || skin->is_writing) { + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(skin->is_writing) + fprintf(stderr,"\n"); + fprintf(stderr, + "\rcdrskin: %s (burning since %.f seconds) ", + (formatting?"formatting":"working pre-track"), elapsed_total_time); + } + skin->is_writing= 0; + advance_interval= 1; + } + {ret= 2; goto ex;} + } else if(drive_status==BURN_DRIVE_WRITING_LEADOUT + +#ifdef Cdrskin_allow_libburn_taO + || drive_status==BURN_DRIVE_CLOSING_TRACK + || drive_status==BURN_DRIVE_CLOSING_SESSION +#endif + + ) { + +#ifdef Cdrskin_allow_libburn_taO + if(drive_status==BURN_DRIVE_CLOSING_SESSION && + skin->previous_drive_status!=drive_status) + {printf("\nFixating...\n"); fflush(stdout);} +#endif + + if(time_to_tell || skin->is_writing) { + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(skin->is_writing) + fprintf(stderr,"\n"); + fprintf(stderr, + "\rcdrskin: working post-track (burning since %.f seconds) ", + elapsed_total_time); + } + skin->is_writing= 0; + advance_interval= 1; + } + {ret= 2; goto ex;} + } else + goto thank_you_for_patience; + + old_track_idx= skin->supposed_track_idx; +#ifdef Cdrskin_progress_track_brokeN + /* with libburn.0.2 there is always reported 0 as p->track */ + if(written_bytes<0) { /* track hop ? */ + if(skin->supposed_track_idx+1track_counter) + skin->supposed_track_idx++; + } + /* >>> ask eventual fifo about writing fd */; + if(p->track>0) + skin->supposed_track_idx= p->track; +#else /* Cdrskin_progress_track_brokeN */ + skin->supposed_track_idx= p->track; +#endif /* ! Cdrskin_progress_track_brokeN */ + + if(old_track_idx>=0 && old_track_idxsupposed_track_idx) { + Cdrtrack_get_size(skin->tracklist[old_track_idx],&fixed_size,&padding, + §or_size,1); + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("\n"); + printf("%sTrack %-2.2d: Total bytes read/written: %.f/%.f (%.f sectors).\n", + debug_mark,old_track_idx+1,fixed_size,fixed_size+padding, + (fixed_size+padding)/sector_size); + } + + sector_size= 2048.0; + if(skin->supposed_track_idx>=0 && + skin->supposed_track_idxtrack_counter) + Cdrtrack_get_size(skin->tracklist[skin->supposed_track_idx],&fixed_size, + &padding,§or_size,0); + + bytes_to_write= ((double) p->sectors)*sector_size; + written_total_bytes= ((double) p->sector)*sector_size; + written_bytes= written_total_bytes-*last_count; + if(written_total_bytes<1024*1024) { +thank_you_for_patience:; + if(time_to_tell || (skin->is_writing && elapsed_total_time>=1.0)) { + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(skin->is_writing) + fprintf(stderr,"\n"); + fprintf(stderr, + "\rcdrskin: thank you for being patient since %.f seconds ", + elapsed_total_time); + } + advance_interval= 1; + } + skin->is_writing= 0; + {ret= 2; goto ex;} + } + new_mb= written_total_bytes/(1024*1024); + old_mb= (*last_count)/(1024*1024); + if(new_mb==old_mb && !(written_total_bytes>=skin->fixed_size && + skin->fixed_size>0 && time_to_tell)) + {ret= 1; goto ex;} + + +#ifndef Cdrskin_extra_leaN + + percent= 0.0; + if(bytes_to_write>0) + percent= written_total_bytes/bytes_to_write*100.0; + measured_total_speed= 0.0; + measured_speed= 0.0; + estim_time= -1.0; + estim_minutes= -1.0; + estim_seconds= -1.0; + if(elapsed_total_time>0.0) { + measured_total_speed= written_total_bytes/elapsed_total_time; + estim_time= (bytes_to_write-written_bytes)/measured_total_speed; + if(estim_time>0.0 && estim_time<86400.0) { + estim_minutes= ((int) estim_time)/60; + estim_seconds= estim_time-estim_minutes*60.0; + if(estim_seconds<0.0) + estim_seconds= 0.0; + } + } + if(written_bytes==written_total_bytes && elapsed_total_time>0) { + measured_speed= measured_total_speed; + } else if(elapsed_time>0.0) + measured_speed= written_bytes/elapsed_time; + else if(written_bytes>0.0) + measured_speed= 99.91*Cdrskin_speed_factoR; + + if(measured_speed<=0.0 && written_total_bytes>=skin->fixed_size && + skin->fixed_size>0) { + if(!skin->is_writing) + goto thank_you_for_patience; + skin->is_writing= 0; + measured_speed= measured_total_speed; + } else + skin->is_writing= 1; + if(skin->supposed_track_idx<0) + skin->supposed_track_idx= 0; + if(*last_count<=0.0) + printf("%-78.78s\r",""); + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(flag&1) { + printf("%.f/%.f (%2.1f%%) @%1.1f, remaining %.f:%2.2d\n", + written_total_bytes,bytes_to_write,percent, + measured_speed/Cdrskin_speed_factoR, + estim_minutes,(int) estim_seconds); + } else { + fill= 0; + fifo_percent= 50; + fifo_text[0]= 0; + curr_fifo_in= last_fifo_in; + curr_fifo_out= last_fifo_out; + if(skin->fifo!=NULL) { + ret= Cdrfifo_get_buffer_state(skin->fifo,&fill,&space,0); + buffer_size= fill+space; + if(ret==2 || ret==0) { + fifo_percent= 100; + } else if(ret>0 && buffer_size>0.0) { + /* obtain minimum fill of pacifier interval */ + Cdrfifo_next_interval(skin->fifo,&fill,0); + fifo_percent= 100.0*((double) fill)/buffer_size; + if(fifo_percent<100 && fill>0) + fifo_percent++; + } + if(skin->verbosity>=Cdrskin_verbose_debuG) { + Cdrfifo_get_counters(skin->fifo,&curr_fifo_in,&curr_fifo_out,0); + Cdrfifo_get_sizes(skin->fifo,&bs,&fs,0); + } + } + if(skin->fifo_size>0) { + sprintf(fifo_text,"(fifo %3d%%) ",fifo_percent); + if(skin->verbosity>=Cdrskin_verbose_debug_fifO) { + fprintf(stderr, + "\ncdrskin_debug: fifo >= %9d / %d : %8.f in, %8.f out\n", + fill,(int) buffer_size, + curr_fifo_in-last_fifo_in,curr_fifo_out-last_fifo_out); + last_fifo_in= curr_fifo_in; + last_fifo_out= curr_fifo_out; + } + } + if(skin->supposed_track_idx >= 0 && + skin->supposed_track_idx < skin->track_counter) { + /* fixed_size,padding are fetched above via Cdrtrack_get_size() */; + } else if(skin->fixed_size!=0) { + fixed_size= skin->fixed_size; + padding= skin->padding; + } + if(fixed_size) { + sprintf(mb_text,"%4d of %4d", + (int) (written_total_bytes/1024.0/1024.0), + (int) ((fixed_size+padding)/1024.0/1024.0)); + } else + sprintf(mb_text,"%4d",(int) (written_total_bytes/1024.0/1024.0)); + speed_factor= Cdrskin_speed_factoR*sector_size/2048; + + buffer_fill= 50; +#ifdef Cdrskin_libburn_has_buffer_progresS + if(p->buffer_capacity>0) + buffer_fill= (double) (p->buffer_capacity - p->buffer_available)*100.0 + / (double) p->buffer_capacity; + +#endif /* Cdrskin_libburn_has_buffer_progresS */ + if(buffer_fill<*min_buffer_fill) + *min_buffer_fill= buffer_fill; + + printf("\r%sTrack %-2.2d: %s MB written %s[buf %3d%%] %4.1fx.", + debug_mark,skin->supposed_track_idx+1,mb_text,fifo_text, + buffer_fill,measured_speed/speed_factor); + fflush(stdout); + } + if(skin->is_writing==0) { + printf("\n"); + goto thank_you_for_patience; + } + } + +#else /* ! Cdrskin_extra_leaN */ + + if(skin->supposed_track_idx<0) + skin->supposed_track_idx= 0; + if(written_bytes<=0.0 && written_total_bytes>=skin->fixed_size && + skin->fixed_size>0) { + if(!skin->is_writing) + goto thank_you_for_patience; + skin->is_writing= 0; + } else { + if(!skin->is_writing) + printf("\n"); + skin->is_writing= 1; + } + printf("\rTrack %-2.2d: %3d MB written ", + skin->supposed_track_idx+1,(int) (written_total_bytes/1024.0/1024.0)); + fflush(stdout); + if(skin->is_writing==0) + printf("\n"); + +#endif /* Cdrskin_extra_leaN */ + + + advance_interval= 1; + ret= 1; +ex:; + if(advance_interval) { + if(written_total_bytes>0) + *last_count= written_total_bytes; + else + *last_count= 0.0; + if(*last_count>*total_count) + *total_count= *last_count; + *last_time= current_time; + } + skin->previous_drive_status= drive_status; + return(ret); +} + + +/** Determines the effective write mode and checks wether the drive promises + to support it. + @param s state of target media, obtained from burn_disc_get_status(), + submit BURN_DISC_BLANK if no real state is available +*/ +int Cdrskin_activate_write_mode(struct CdrskiN *skin, enum burn_disc_status s, + int flag) +{ + int ok, was_still_default= 0, block_type_demand,track_type,sector_size, i; + int profile_number= -1; + struct burn_drive_info *drive_info = NULL; + char profile_name[80]; + + profile_name[0]= 0; +#ifdef Cdrskin_libburn_has_get_profilE + if(skin->grabbed_drive) + burn_disc_get_profile(skin->grabbed_drive,&profile_number,profile_name); +#endif + + if(strcmp(skin->preskin->write_mode_name,"DEFAULT")==0) { + was_still_default= 1; + + if(s == BURN_DISC_APPENDABLE) { + strcpy(skin->preskin->write_mode_name,"TAO"); + was_still_default= 2; /*<<< prevents trying of SAO if drive dislikes TAO*/ + } else if(profile_number==0x1a || profile_number==0x13 || + profile_number==0x12) { + /* DVD+RW , DVD-RW Restricted Overwrite , DVD-RAM */ + strcpy(skin->preskin->write_mode_name,"TAO"); + } else { + strcpy(skin->preskin->write_mode_name,"SAO"); + } + } + if(strcmp(skin->preskin->write_mode_name,"RAW/RAW96R")==0) { + skin->write_type= BURN_WRITE_RAW; + skin->block_type= BURN_BLOCK_RAW96R; + +#ifdef Cdrskin_allow_libburn_taO + } else if(strcmp(skin->preskin->write_mode_name,"TAO")==0) { + strcpy(skin->preskin->write_mode_name,"TAO"); + skin->write_type= BURN_WRITE_TAO; + skin->block_type= BURN_BLOCK_MODE1; +#endif /* Cdrskin_allow_libburn_taO */ + + } else { + strcpy(skin->preskin->write_mode_name,"SAO"); + skin->write_type= BURN_WRITE_SAO; + skin->block_type= BURN_BLOCK_SAO; + } + + /* check wether desired type combination is available with drive */ + if(skin->driveno<0 || skin->driveno>skin->n_drives) { + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(printf("cdrskin_debug: WARNING : No drive selected with Cdrskin_activate_write_mode\n")); + goto it_is_done; + } + drive_info= skin->drives+skin->driveno; + + /* <<< this should become a libburn API function.The knowledge about TAO audio + track block type is quite inappropriate here. It refers to a habit of + spc_select_write_params() (and MMC-1 table 61). But the knowledge about + the tracklist is rather cdrskin realm. (ponder ...) + */ +check_with_drive:; + ok= 0; + if(strstr(profile_name,"DVD")==profile_name) { + + /* >>> drive_info does not reflect DVD capabilities yet */ + + ok= 1; + } else if(skin->write_type==BURN_WRITE_RAW) + ok= !!(drive_info->raw_block_types & BURN_BLOCK_RAW96R); + else if(skin->write_type==BURN_WRITE_SAO) + ok= !!(drive_info->sao_block_types & BURN_BLOCK_SAO); + else if(skin->write_type==BURN_WRITE_TAO) { + block_type_demand= 0; + for(i=0;itrack_counter;i++) { + Cdrtrack_get_track_type(skin->tracklist[i],&track_type,§or_size,0); + if(track_type==BURN_AUDIO) + block_type_demand|= BURN_BLOCK_RAW0; + else + block_type_demand|= BURN_BLOCK_MODE1; + } + ok= ((drive_info->tao_block_types & block_type_demand)==block_type_demand); + } + + if(!ok) { + fprintf(stderr, + "cdrskin: %s : Drive indicated refusal for write mode %s.\n", + (skin->force_is_set || was_still_default==1?"WARNING":"FATAL"), + skin->preskin->write_mode_name); + if(! skin->force_is_set) { + if(was_still_default==1) { + if(skin->write_type==BURN_WRITE_RAW || + skin->write_type==BURN_WRITE_SAO) { + skin->write_type= BURN_WRITE_TAO; + skin->block_type= BURN_BLOCK_MODE1; + strcpy(skin->preskin->write_mode_name,"TAO"); + } else { + skin->write_type= BURN_WRITE_SAO; + skin->block_type= BURN_BLOCK_SAO; + strcpy(skin->preskin->write_mode_name,"SAO"); + } + was_still_default= 2; /* do not try more than once */ + goto check_with_drive; + } + fprintf(stderr,"cdrskin: HINT : If you are certain that the drive will do, try option -force\n"); + return(0); + } + } +it_is_done:; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: write type : %s\n", skin->preskin->write_mode_name); + return(1); +} + + +/** Burn data via libburn according to the parameters set in skin. + @return <=0 error, 1 success +*/ +int Cdrskin_burn(struct CdrskiN *skin, int flag) +{ + struct burn_disc *disc; + struct burn_session *session; + struct burn_write_opts *o; + enum burn_disc_status s; + enum burn_drive_status drive_status; + struct burn_progress p; + struct burn_drive *drive; + int ret,loop_counter= 0,max_track= -1,i,hflag,nwa,num, wrote_well= 2; + int fifo_disabled= 0,fifo_percent,total_min_fill,mb,min_buffer_fill= 101; + double put_counter,get_counter,empty_counter,full_counter; + double start_time,last_time; + double total_count= 0.0,last_count= 0.0,size,padding,sector_size= 2048.0; + double sectors; + + printf("cdrskin: beginning to burn disk\n"); + + disc= burn_disc_create(); + session= burn_session_create(); + ret= burn_disc_add_session(disc,session,BURN_POS_END); + if(ret==0) { + fprintf(stderr,"cdrskin: FATAL : cannot add session to disc object.\n"); + return(0); + } + + skin->fixed_size= 0.0; + for(i=0;itrack_counter;i++) { + hflag= (skin->verbosity>=Cdrskin_verbose_debuG); + if(i==skin->track_counter-1) + Cdrtrack_ensure_padding(skin->tracklist[i],hflag&1); + ret= Cdrtrack_add_to_session(skin->tracklist[i],i,session,hflag); + if(ret<=0) { + fprintf(stderr,"cdrskin: FATAL : cannot add track %d to session.\n",i+1); + return(0); + } + Cdrtrack_get_size(skin->tracklist[i],&size,&padding,§or_size,0); + if(size>0) + skin->fixed_size+= size+padding; + } + + ret= Cdrskin_grab_drive(skin,0); + if(ret<=0) + return(ret); + drive= skin->drives[skin->driveno].drive; + +#ifndef Cdrskin_no_aftergrab_loopS + while(burn_drive_get_status(drive, NULL) != BURN_DRIVE_IDLE) + usleep(100002); /* >>> ??? add a timeout ? */ + while((s= burn_disc_get_status(drive)) == BURN_DISC_UNREADY) + usleep(100002); /* >>> ??? add a timeout ? */ +#else + s= burn_disc_get_status(drive); +#endif + + if(skin->verbosity>=Cdrskin_verbose_progresS) + Cdrskin_report_disc_status(skin,s,0); + + ret= Cdrskin_activate_write_mode(skin,s,0); + if(ret<=0) { + fprintf(stderr, + "cdrskin: FATAL : Cannot activate the desired write mode\n"); + ret= 0; goto ex; + } + +#ifdef Cdrskin_libburn_has_multI + if (s == BURN_DISC_APPENDABLE) { + +#ifdef Cdrskin_allow_sao_for_appendablE + ; +#else + if(skin->write_type!=BURN_WRITE_TAO) { + Cdrskin_release_drive(skin,0); + fprintf(stderr,"cdrskin: FATAL : For now only write mode -tao can be used with appendable disks\n"); + return(0); + } +#endif /* ! Cdrskin_allow_sao_for_appendablE */ + + } else if (s != BURN_DISC_BLANK) { +#else + if (s != BURN_DISC_BLANK) { +#endif + Cdrskin_release_drive(skin,0); + fprintf(stderr,"cdrskin: FATAL : no writeable media detected.\n"); + return(0); + } + + +#ifndef Cdrskin_extra_leaN + + if(skin->verbosity>=Cdrskin_verbose_progresS) { + for(i=0;itrack_counter;i++) { + Cdrtrack_get_size(skin->tracklist[i],&size,&padding,§or_size,0); + if(size<=0) { + printf("Track %-2.2d: data unknown length",i+1); + } else { + mb= size/1024.0/1024.0; + printf("Track %-2.2d: data %5d MB ",i+1,mb); + } + if(padding>0) + printf(" padsize: %.f KB\n",padding/1024.0); + else + printf("\n"); + } + if(skin->fixed_size<=0) { + printf("Total size: 0 MB (00:00.00) = 0 sectors\n"); + printf("Lout start: 0 MB (00:02/00) = 0 sectors\n"); + } else { + /* >>> This is quite a fake. Need to learn about 12:35.25 and "Lout" + ??? Is there a way to obtain the toc in advance (print_cue()) ? */ + double seconds; + int min,sec,frac; + + mb= skin->fixed_size/1024.0/1024.0; + seconds= skin->fixed_size/150.0/1024.0+2.0; + min= seconds/60.0; + sec= seconds-min*60; + frac= (seconds-min*60-sec)*100; + if(frac>99) + frac= 99; + sectors= (int) (skin->fixed_size/sector_size); + if(sectors*sector_size != skin->fixed_size) + sectors++; + printf("Total size: %5d MB (%-2.2d:%-2.2d.%-2.2d) = %d sectors\n", + mb,min,sec,frac,(int) sectors); + seconds+= 2; + min= seconds/60.0; + sec= seconds-min*60; + frac= (seconds-min*60-sec)*100; + if(frac>99) + frac= 99; + printf("Lout start: %5d MB (%-2.2d:%-2.2d/%-2.2d) = %d sectors\n", + mb,min,sec,frac,(int) sectors); + } + } + + Cdrskin_wait_before_action(skin,0); + ret= Cdrskin_fill_fifo(skin,0); + if(ret<=0) { + fprintf(stderr,"cdrskin: FATAL : filling of fifo failed\n"); + goto ex; + } + +#endif /* ! Cdrskin_extra_leaN */ + + + o= burn_write_opts_new(drive); + burn_write_opts_set_perform_opc(o, 0); + +#ifdef Cdrskin_libburn_has_multI + if(skin->multi) + fprintf(stderr, + "cdrskin: NOTE : Option -multi set. Media will be appendable.\n"); + burn_write_opts_set_multi(o,skin->multi); +#endif + +#ifdef Cdrskin_libburn_has_set_start_bytE + burn_write_opts_set_start_byte(o, skin->write_start_address); +#endif + + burn_write_opts_set_write_type(o,skin->write_type,skin->block_type); + if(skin->dummy_mode) { + fprintf(stderr, + "cdrskin: NOTE : -dummy mode will prevent actual writing\n"); + burn_write_opts_set_simulate(o, 1); + } + burn_write_opts_set_underrun_proof(o,skin->burnfree); + + Cdrskin_adjust_speed(skin,0); + if(skin->verbosity>=Cdrskin_verbose_progresS) { + ret= Cdrskin_obtain_nwa(skin, &nwa,0); + if(ret>0) + printf("Starting new track at sector: %d\n",nwa); + } + skin->drive_is_busy= 1; + burn_disc_write(o, disc); + if(skin->preskin->abort_handler==-1) + Cleanup_set_handlers(skin,(Cleanup_app_handler_T) Cdrskin_abort_handler,4); + last_time= start_time= Sfile_microtime(0); + + burn_write_opts_free(o); + + while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING) { + + /* >>> how do i learn about success or failure ? */ + + ; + } + loop_counter= 0; + while (1) { + drive_status= burn_drive_get_status(drive, &p); + if(drive_status==BURN_DRIVE_IDLE) + break; + + /* >>> how do i learn about success or failure ? */ + + if(loop_counter>0) + Cdrskin_burn_pacifier(skin,drive_status,&p,start_time,&last_time, + &total_count,&last_count,&min_buffer_fill,0); + + + /* <<< debugging : artificial abort without a previous signal */; + if(skin->abort_after_bytecount>=0.0 && + total_count>=skin->abort_after_bytecount) { + /* whatever signal handling is installed: this thread is the boss now */ + fprintf(stderr, + "cdrskin: DEVELOPMENT : synthetic abort by abort_after_bytecount=%.f\n", + skin->abort_after_bytecount); + skin->control_pid= getpid(); + ret= Cdrskin_abort_handler(skin,0,0); + fprintf(stderr,"cdrskin: done (aborted)\n"); + exit(1); + } + + + if(max_tracksupposed_track_idx) + max_track= skin->supposed_track_idx; + +#ifndef Cdrskin_extra_leaN + if(skin->fifo==NULL || fifo_disabled) { + usleep(20000); + } else { + ret= Cdrfifo_try_to_work(skin->fifo,20000,NULL,NULL,0); + if(ret<0) { + int abh; + + abh= skin->preskin->abort_handler; + if(abh!=2) + fprintf(stderr, + "\ncdrskin: FATAL : fifo encountered error during burn loop.\n"); + if(abh==0) { + ret= -1; goto ex; + } else if(abh==1 || abh==3 || abh==4 || abh==-1) { + Cdrskin_abort_handler(skin,0,0); + fprintf(stderr,"cdrskin: done (aborted)\n"); + exit(10); + } else { + if(skin->verbosity>=Cdrskin_verbose_debuG) + fprintf(stderr, + "\ncdrskin_debug: Cdrfifo_try_to_work() returns %d\n",ret); + } + } + if(ret==2) { /* <0 = error , 2 = work is done */ + if(skin->verbosity>=Cdrskin_verbose_debuG) + fprintf(stderr,"\ncdrskin_debug: fifo ended work with ret=%d\n",ret); + fifo_disabled= 1; + } + } +#else /* ! Cdrskin_extra_leaN */ + usleep(20000); +#endif /* Cdrskin_extra_leaN */ + + loop_counter++; + } + skin->drive_is_busy= 0; + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("\n"); + +#ifdef Cdrskin_libburn_has_wrote_welL + wrote_well = burn_drive_wrote_well(drive); +#endif + + if(max_track<0) { + printf("Track 01: Total bytes read/written: %.f/%.f (%.f sectors).\n", + total_count,total_count,total_count/sector_size); + } else { + Cdrtrack_get_size(skin->tracklist[max_track],&size,&padding,§or_size,1); + printf( + "Track %-2.2d: Total bytes read/written: %.f/%.f (%.f sectors).\n", + max_track+1,size,size+padding,(size+padding)/sector_size); + } + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("Writing time: %.3fs\n",Sfile_microtime(0)-start_time); + + +#ifndef Cdrskin_extra_leaN + + if(skin->fifo!=NULL && skin->fifo_size>0 && wrote_well) { + int dummy,final_fill; + Cdrfifo_get_buffer_state(skin->fifo,&final_fill,&dummy,0); + if(final_fill>0) { +fifo_full_at_end:; + fprintf(stderr, + "cdrskin: FATAL : Fifo still contains data after burning has ended.\n"); + fprintf(stderr, + "cdrskin: FATAL : %.d bytes left.\n",final_fill); + fprintf(stderr, + "cdrskin: FATAL : This indicates an overflow of the last track.\n"); + fprintf(stderr, + "cdrskin: NOTE : The media might appear ok but is probably truncated.\n"); + ret= -1; goto ex; + } + +#ifdef Cdrskin_libburn_leaves_inlet_opeN + for(i= 0;itrack_counter;i++) { + ret= Cdrtrack_has_input_left(skin->tracklist[i],0); + if(ret>0) { + fprintf(stderr, + "cdrskin: FATAL : fifo outlet of track #%d is still buffering some bytes.\n", + i+1); + goto fifo_full_at_end; + } + } +#endif /* Cdrskin_libburn_leaves_inlet_opeN */ + + } + + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(skin->fifo!=NULL && skin->fifo_size>0) { + int dummy; + + Cdrfifo_get_min_fill(skin->fifo,&total_min_fill,&dummy,0); + fifo_percent= 100.0*((double) total_min_fill)/(double) skin->fifo_size; + if(fifo_percent==0 && total_min_fill>0) + fifo_percent= 1; + Cdrfifo_get_cdr_counters(skin->fifo,&put_counter,&get_counter, + &empty_counter,&full_counter,0); + fflush(stdout); + fprintf(stderr,"Cdrskin: fifo had %.f puts and %.f gets.\n", + put_counter,get_counter); + fprintf(stderr, + "Cdrskin: fifo was %.f times empty and %.f times full, min fill was %d%%.\n", + empty_counter,full_counter,fifo_percent); + } + drive_status= burn_drive_get_status(drive, &p); + +#ifdef Cdrskin_libburn_has_buffer_min_filL + /* cdrskin recorded its own coarse min_buffer_fill. + libburn's is finer - if enough bytes were processed so it is available.*/ + if(p.buffer_min_fill<=p.buffer_capacity && p.buffer_capacity>0) { + num= 100.0 * ((double) p.buffer_min_fill)/(double) p.buffer_capacity; + if(num100) + min_buffer_fill= 50; + printf("Min drive buffer fill was %d%%\n", min_buffer_fill); + } + +#endif /* ! Cdrskin_extra_leaN */ + + ret= 1; + if(wrote_well) { + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("cdrskin: burning done\n"); + } else { + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("cdrskin: burning failed\n"); + fprintf(stderr,"cdrskin: FATAL : burning failed.\n"); + ret= 0; + } +ex:; + skin->drive_is_busy= 0; + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(printf("cdrskin_debug: do_eject= %d\n",skin->do_eject)); + Cdrskin_release_drive(skin,0); + for(i= 0;itrack_counter;i++) + Cdrtrack_cleanup(skin->tracklist[i],0); + burn_session_free(session); + burn_disc_free(disc); + return(ret); +} + + +/** Work around the failure of libburn to eject the tray. + This employs a system(2) call and is therefore an absolute no-no for any + pseudo user identities. + @return <=0 error, 1 success +*/ +int Cdrskin_eject(struct CdrskiN *skin, int flag) +{ + +#ifndef Cdrskin_burn_drive_eject_brokeN + +#ifndef Cdrskin_oldfashioned_api_usE + int i,ret,max_try= 5; + + if(!skin->do_eject) + return(1); + + if(skin->n_drives<=skin->driveno || skin->driveno < 0) + return(2); + + /* ??? A61012 : retry loop might now be obsolete + (a matching bug in burn_disc_write_sync() was removed ) */ + /* A61227 : A kindof race condition with -atip -eject on SuSE 9.3. Loop saved + me. Waiting seems to help. I suspect the media demon. */ + + for(i= 0;i0 || i>=max_try-1) + break; + if(skin->verbosity>=Cdrskin_verbose_progresS) + fprintf(stderr, + "cdrskin: NOTE : Attempt #%d of %d failed to grab drive for eject\n", + i+1,max_try); + usleep(1000000); + } + if(ret>0) { + ret= Cdrskin_release_drive(skin,1); + if(ret<=0) + goto sorry_failed_to_eject; + } else { +sorry_failed_to_eject:; + fprintf(stderr,"cdrskin: SORRY : Failed to finally eject tray.\n"); + return(0); + } + return(1); + +#else + + if(!skin->do_eject) + return(1); + if(Cdrskin_grab_drive(skin,2)>0) { + Cdrskin_release_drive(skin,1); + } else { + fprintf(stderr,"cdrskin: SORRY : Failed to finally eject tray.\n"); + return(0); + } + return(1); + +#endif + +#else /* Cdrskin_burn_drive_eject_brokeN */ + + int ret; + char adr[Cdrskin_adrleN]; + char cmd[5*Cdrskin_strleN+16],shellsafe[5*Cdrskin_strleN+2]; + + if(!skin->do_eject) + return(1); + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("cdrskin: trying to eject media\n"); + if(getuid()!=geteuid()) { + fprintf(stderr, + "cdrskin: SORRY : uid and euid differ. Will not start external eject.\n"); + fprintf(stderr, + "cdrskin: HINT : Consider to allow rw-access to the writer device and\n"); + fprintf(stderr, + "cdrskin: HINT : to run cdrskin under your normal user identity.\n"); + return(0); + } + +#ifdef Cdrskin_libburn_has_drive_get_adR + ret= burn_drive_get_adr(&(skin->drives[skin->driveno]), adr); + if(ret<=0) + adr[0]= 0; +#else + strcpy(adr,skin->drives[skin->driveno].location); +#endif + + if(strlen(skin->eject_device)>0) + sprintf(cmd,"eject %s",Text_shellsafe(skin->eject_device,shellsafe,0)); + else if(strcmp(adr,"/dev/sg0")==0) + sprintf(cmd,"eject /dev/sr0"); + else + sprintf(cmd,"eject %s",Text_shellsafe(adr,shellsafe,0)); + ret= system(cmd); + if(ret==0) + return(1); + return(0); + +#endif /* Cdrskin_burn_drive_eject_brokeN */ + +} + + +/** Interpret all arguments of the program after libburn has been initialized + and drives have been scanned. This call reports to stderr any valid + cdrecord options which are not implemented yet. + @param flag Bitfield for control purposes: + bit0= do not finalize setup + bit1= do not interpret (again) skin->preskin->pre_argv + @return <=0 error, 1 success +*/ +int Cdrskin_setup(struct CdrskiN *skin, int argc, char **argv, int flag) +{ + int i,k,ret,source_has_size=0; + double value,grab_and_wait_value= -1.0; + char *cpt,*value_pt,adr[Cdrskin_adrleN],*blank_mode= ""; + struct stat stbuf; + + /* cdrecord 2.01 options which are not scheduled for implementation, yet */ + static char ignored_partial_options[][41]= { + "timeout=", "debug=", "kdebug=", "kd=", "driver=", "ts=", + "pregap=", "defpregap=", "mcn=", "isrc=", "index=", "textfile=", + "pktsize=", "cuefile=", + "" + }; + static char ignored_full_options[][41]= { + "-d", "-Verbose", "-V", "-silent", "-s", "-setdropts", "-prcap", "-inq", + "-reset", "-abort", "-overburn", "-ignsize", "-useinfo", "-format", "-load", + "-lock", "-fix", "-nofix", "-waiti", + "-immed", "-raw", "-raw96p", "-raw16", + "-clone", "-text", "-mode2", "-xa", "-xa1", "-xa2", "-xamix", + "-cdi", "-isosize", "-preemp", "-nopreemp", "-copy", "-nocopy", + "-scms", "-shorttrack", "-noshorttrack", "-packet", "-noclose", + "" + }; + + /* are we pretending to be cdrecord ? */ + cpt= strrchr(argv[0],'/'); + if(cpt==NULL) + cpt= argv[0]; + else + cpt++; + if(strcmp(cpt,"cdrecord")==0 && !(flag&1)) { + fprintf(stderr,"\n"); + fprintf(stderr, + "Note: This is not cdrecord by Joerg Schilling. Do not bother him.\n"); + fprintf(stderr, + " See cdrskin start message on stdout. See --help. See -version.\n"); + fprintf(stderr,"\n"); + /* allow automatic -tao to -sao redirection */ + skin->tao_to_sao_tsize=650*1024*1024; + } + +#ifndef Cdrskin_extra_leaN + if(!(flag&2)) { + if(skin->preskin->pre_argc>1) { + ret= Cdrskin_setup(skin,skin->preskin->pre_argc,skin->preskin->pre_argv, + flag|1|2); + if(ret<=0) + return(ret); + } + } +#endif + + for (i= 1;iabort_after_bytecount); + + } else if(strcmp(argv[i],"--abort_handler")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strncmp(argv[i],"-abort_max_wait=",16)==0) { + value_pt= argv[i]+16; + goto set_abort_max_wait; + } else if(strncmp(argv[i],"abort_max_wait=",15)==0) { + value_pt= argv[i]+15; +set_abort_max_wait:; + value= Scanf_io_size(value_pt,0); + if(value<0 || value>86400) { + fprintf(stderr, + "cdrskin: NOTE : ignored out-of-range value: abort_max_wait=%s\n", + value_pt); + } else { + skin->abort_max_wait= value; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf( + "cdrskin: maximum waiting time with abort handling : %d seconds\n", + skin->abort_max_wait); + } + + } else if(strcmp(argv[i],"--allow_setuid")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--any_track")==0) { + skin->single_track= -1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf( + "cdrskin: --any_track : will accept any unknown option as track source\n"); + + } else if(strcmp(argv[i],"-atip")==0) { + if(skin->do_atip<1) + skin->do_atip= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: will put out some -atip style lines\n"); + + } else if(strcmp(argv[i],"-audio")==0) { + skin->track_type= BURN_AUDIO; + skin->track_type_by_default= 0; + + } else if(strncmp(argv[i],"-blank=",7)==0) { + cpt= argv[i]+7; + goto set_blank; + } else if(strncmp(argv[i],"blank=",6)==0) { + cpt= argv[i]+6; +set_blank:; + skin->blank_format_type= 0; + blank_mode= cpt; + if(strcmp(cpt,"all")==0 || strcmp(cpt,"disc")==0 + || strcmp(cpt,"disk")==0) { + skin->do_blank= 1; + skin->blank_fast= 0; + blank_mode= "all"; + } else if(strcmp(cpt,"fast")==0 || strcmp(cpt,"minimal")==0) { + skin->do_blank= 1; + skin->blank_fast= 1; + blank_mode= "fast"; + } else if(strcmp(cpt,"format_overwrite")==0) { + skin->do_blank= 1; + skin->blank_format_type= 1|(1<<8); + skin->blank_format_size= 128*1024*1024; + } else if(strcmp(cpt,"format_overwrite_full")==0) { + skin->do_blank= 1; + skin->blank_format_type= 1|(1<<10); + /* was : 1|(1<<8)|(1<<10); */ + skin->blank_format_size= 0; + /* was: 32*1024; / * write just a minimal packet */ + } else if(strcmp(cpt,"format_overwrite_quickest")==0) { + skin->do_blank= 1; + skin->blank_format_type= 1; + skin->blank_format_size= 0; + } else if(strcmp(cpt,"format_sequential")==0) {/* >>> not yet supported */ + skin->do_blank= 1; + skin->blank_format_type= 2; + } else if(strcmp(cpt,"help")==0) { + /* is handled in Cdrpreskin_setup() */; + continue; + } else { + fprintf(stderr,"cdrskin: FATAL : blank option '%s' not supported yet\n", + cpt); + return(0); + } + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: blank mode : blank=%s\n",blank_mode); + + } else if(strcmp(argv[i],"--bragg_with_audio")==0) { + /* OBSOLETE 0.2.3 : was handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"-checkdrive")==0) { + skin->do_checkdrive= 1; + + } else if(strcmp(argv[i],"-data")==0) { + + /* >>> !!! All Subsequent Tracks Option */ + + skin->track_type= BURN_MODE1; + skin->track_type_by_default= 0; + + } else if(strcmp(argv[i],"--demand_a_drive")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--devices")==0) { + skin->do_devices= 1; + + +#ifndef Cdrskin_extra_leaN + + } else if(strncmp(argv[i],"dev_translation=",16)==0) { + + if(argv[i][16]==0) { + fprintf(stderr, + "cdrskin: FATAL : dev_translation= : missing separator character\n"); + return(0); + } + ret= Cdradrtrn_add(skin->adr_trn,argv[i]+17,argv[i]+16,1); + if(ret==-2) + fprintf(stderr, + "cdrskin: FATAL : address_translation= : cannot allocate memory\n"); + else if(ret==-1) + fprintf(stderr, + "cdrskin: FATAL : address_translation= : table full (%d items)\n", + Cdradrtrn_leN); + else if(ret==0) + fprintf(stderr, + "cdrskin: FATAL : address_translation= : no address separator '%c' found\n", + argv[i][17]); + if(ret<=0) + return(0); + +#endif /* Cdrskin_extra_leaN */ + + + } else if(strncmp(argv[i],"-dev=",5)==0) { + /* is handled in Cdrpreskin_setup() */; + } else if(strncmp(argv[i],"dev=",4)==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--drive_abort_on_busy")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--drive_blocking")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--drive_not_exclusive")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strncmp(argv[i],"-driveropts=",12)==0) { + value_pt= argv[i]+12; + goto set_driveropts; + } else if(strncmp(argv[i],"driveropts=",11)==0) { + value_pt= argv[i]+11; +set_driveropts:; + if(strcmp(value_pt,"burnfree")==0 || strcmp(value_pt,"burnproof")==0) { + skin->burnfree= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: burnfree : on\n"); + } else if(strcmp(argv[i]+11,"noburnfree")==0 || + strcmp(argv[i]+11,"noburnproof")==0 ) { + skin->burnfree= 0; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: burnfree : off\n"); + } else if(strcmp(argv[i]+11,"help")==0) { + /* handled in Cdrpreskin_setup() */; + } else + goto ignore_unknown; + + } else if(strcmp(argv[i],"-dummy")==0) { + skin->dummy_mode= 1; + + } else if(strcmp(argv[i],"-eject")==0) { + skin->do_eject= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: eject after work : on\n"); + + } else if(strncmp(argv[i],"eject_device=",13)==0) { + if(strlen(argv[i]+13)>=sizeof(skin->eject_device)) { + fprintf(stderr, + "cdrskin: FATAL : eject_device=... too long. (max: %d, given: %d)\n", + sizeof(skin->eject_device)-1,strlen(argv[i]+13)); + return(0); + } + strcpy(skin->eject_device,argv[i]+13); + if(skin->verbosity>=Cdrskin_verbose_cmD) +#ifdef Cdrskin_burn_drive_eject_brokeN + printf("cdrskin: eject_device : %s\n",skin->eject_device); +#else + printf("cdrskin: ignoring obsolete eject_device=%s\n", + skin->eject_device); +#endif + + +#ifndef Cdrskin_extra_leaN + + } else if(strcmp(argv[i],"--fifo_disable")==0) { + skin->fifo_enabled= 0; + skin->fifo_size= 0; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: option fs=... disabled\n"); + + } else if(strcmp(argv[i],"--fifo_start_empty")==0) { /* obsoleted */ + skin->fifo_start_at= 0; + + } else if(strncmp(argv[i],"fifo_start_at=",14)==0) { + value= Scanf_io_size(argv[i]+14,0); + if(value>1024.0*1024.0*1024.0) + value= 1024.0*1024.0*1024.0; + else if(value<0) + value= 0; + skin->fifo_start_at= value; + + } else if(strcmp(argv[i],"--fifo_per_track")==0) { + skin->fifo_per_track= 1; + + } else if(strcmp(argv[i],"-force")==0) { + skin->force_is_set= 1; + + } else if(strncmp(argv[i],"-fs=",4)==0) { + value_pt= argv[i]+4; + goto fs_equals; + } else if(strncmp(argv[i],"fs=",3)==0) { + value_pt= argv[i]+3; +fs_equals:; + if(skin->fifo_enabled) { + value= Scanf_io_size(value_pt,0); + if(value<0.0 || value>1024.0*1024.0*1024.0) { + fprintf(stderr, + "cdrskin: FATAL : fs=N expects a size between 0 and 1g\n"); + return(0); + } + skin->fifo_size= value; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: fifo size : %d\n",skin->fifo_size); + } + + } else if(strncmp(argv[i],"grab_drive_and_wait=",20)==0) { + value_pt= argv[i]+20; + grab_and_wait_value= Scanf_io_size(value_pt,0); + + } else if(strncmp(argv[i],"-gracetime=",11)==0) { + value_pt= argv[i]+11; + goto gracetime_equals; + } else if(strncmp(argv[i],"gracetime=",10)==0) { + value_pt= argv[i]+10; +gracetime_equals:; + sscanf(value_pt,"%d",&(skin->gracetime)); + +#else /* ! Cdrskin_extra_leaN */ + + } else if( + strcmp(argv[i],"--fifo_disable")==0 || + strcmp(argv[i],"--fifo_start_empty")==0 || + strncmp(argv[i],"fifo_start_at=",14)==0 || + strcmp(argv[i],"--fifo_per_track")==0 || + strncmp(argv[i],"-fs=",4)==0 || + strncmp(argv[i],"fs=",3)==0 || + strncmp(argv[i],"-gracetime=",11)==0 || + strncmp(argv[i],"gracetime=",10)==0) { + fprintf(stderr, + "cdrskin: NOTE : lean version ignores option: '%s'\n", + argv[i]); + +#endif /* Cdrskin_extra_leaN */ + + + } else if(strcmp(argv[i],"--help")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"-help")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--ignore_signals")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--list_ignored_options")==0) { + char line[80]; + + line[0]= 0; + printf("cdrskin: List of all ignored options:\n"); + for(k=0;ignored_partial_options[k][0]!=0;k++) + printf("%s\n",ignored_partial_options[k]); + for(k=0;ignored_full_options[k][0]!=0;k++) + printf("%s\n",ignored_full_options[k]); + printf("\n"); + + } else if(strcmp(argv[i],"-multi")==0) { +#ifdef Cdrskin_libburn_has_multI + skin->multi= 1; +#else + fprintf(stderr,"cdrskin: SORRY : Option -multi is not available yet.\n"); +#endif + + } else if(strncmp(argv[i],"-msifile=",9)==0) { + value_pt= argv[i]+9; + goto msifile_equals; + } else if(strncmp(argv[i],"msifile=",8)==0) { + value_pt= argv[i]+8; +msifile_equals:; +#ifdef Cdrskin_libburn_has_multI + if(strlen(value_pt)>=sizeof(skin->msifile)) { + fprintf(stderr, + "cdrskin: FATAL : msifile=... too long. (max: %d, given: %d)\n", + sizeof(skin->msifile)-1,strlen(value_pt)); + return(0); + } + strcpy(skin->msifile, value_pt); + skin->do_msinfo= 1; +#else + fprintf(stderr, + "cdrskin: SORRY : Option msifile= is not available.\n"); + return(0); +#endif + + } else if(strcmp(argv[i],"-msinfo")==0) { +#ifdef Cdrskin_libburn_has_multI + skin->do_msinfo= 1; +#else + fprintf(stderr,"cdrskin: SORRY : Option -msinfo is not available.\n"); + return(0); +#endif + + } else if(strcmp(argv[i],"--no_abort_handler")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--no_blank_appendable")==0) { + skin->no_blank_appendable= 1; + + } else if(strcmp(argv[i],"--no_convert_fs_adr")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--no_rc")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"-nopad")==0) { + skin->padding= 0.0; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: padding : off\n"); + + } else if(strcmp(argv[i],"--old_pseudo_scsi_adr")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"-pad")==0) { + skin->padding= 15*2048; + skin->set_by_padsize= 0; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: padding : %.f\n",skin->padding); + + } else if(strncmp(argv[i],"-padsize=",9)==0) { + value_pt= argv[i]+9; + goto set_padsize; + } else if(strncmp(argv[i],"padsize=",8)==0) { + value_pt= argv[i]+8; +set_padsize:; + skin->padding= Scanf_io_size(argv[i]+8,0); + skin->set_by_padsize= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: padding : %.f\n",skin->padding); + + } else if(strcmp(argv[i],"-raw96r")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"-sao")==0 || strcmp(argv[i],"-dao")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"-scanbus")==0) { + skin->do_scanbus= 1; + + } else if(strcmp(argv[i],"--single_track")==0) { + skin->single_track= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf( + "cdrskin: --single_track : will only accept last argument as track source\n"); + + } else if(strncmp(argv[i],"-speed=",7)==0) { + value_pt= argv[i]+7; + goto set_speed; + } else if(strncmp(argv[i],"speed=",6)==0) { + value_pt= argv[i]+6; +set_speed:; + sscanf(value_pt,"%lf",&(skin->x_speed)); + if(skin->x_speed<1.0 && skin->x_speed!=0.0 && skin->x_speed!=-1) { + fprintf(stderr,"cdrskin: FATAL : speed= must be -1, 0 or at least 1\n"); + return(0); + } + + /* >>> cdrecord speed=0 -> minimum speed , libburn -> maximum speed */; + + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: speed : %f\n",skin->x_speed); + + } else if(strcmp(argv[i],"-swab")==0) { + skin->swap_audio_bytes= 0; + + } else if(strcmp(argv[i],"-tao")==0) { + /* is partly handled in Cdrpreskin_setup() */; + +#ifndef Cdrskin_allow_libburn_taO + + if(skin->tao_to_sao_tsize<=0.0) { + fprintf(stderr,"cdrskin: FATAL : libburn does not support -tao yet.\n"); + fprintf(stderr,"cdrskin: HINT : Try option tao_to_sao_tsize=650m\n"); + return(0); + } + printf("cdrskin: NOTE : substituting mode -tao by mode -sao\n"); + strcpy(skin->preskin->write_mode_name,"SAO"); + +#endif /* ! Cdrskin_allow_libburn_taO */ + + } else if(strncmp(argv[i],"tao_to_sao_tsize=",17)==0) { + skin->tao_to_sao_tsize= Scanf_io_size(argv[i]+17,0); + if(skin->tao_to_sao_tsize>Cdrskin_tracksize_maX) + goto track_too_large; + if(skin->verbosity>=Cdrskin_verbose_cmD) +#ifdef Cdrskin_allow_libburn_taO + printf("cdrskin: size default for non-tao write modes: %.f\n", +#else + printf("cdrskin: replace -tao by -sao with fixed size : %.f\n", +#endif + skin->tao_to_sao_tsize); + + } else if(strcmp(argv[i],"-toc")==0) { + skin->do_atip= 2; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: will put out some -atip style lines plus -toc\n"); + + } else if(strncmp(argv[i],"-tsize=",7)==0) { + value_pt= argv[i]+7; + goto set_tsize; + } else if(strncmp(argv[i],"tsize=",6)==0) { + value_pt= argv[i]+6; +set_tsize:; + skin->fixed_size= Scanf_io_size(value_pt,0); + if(skin->fixed_size>Cdrskin_tracksize_maX) { +track_too_large:; + fprintf(stderr,"cdrskin: FATAL : track size too large\n"); + return(0); + } + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: fixed track size : %.f\n",skin->fixed_size); + + } else if(strcmp(argv[i],"-v")==0 || strcmp(argv[i],"-verbose")==0) { + /* is handled in Cdrpreskin_setup() */; + } else if(strcmp(argv[i],"-vv")==0 || strcmp(argv[i],"-vvv")==0 || + strcmp(argv[i],"-vvvv")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strncmp(argv[i],"write_start_address=",20)==0) { + skin->write_start_address= Scanf_io_size(argv[i]+20,0); + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: fixed track size : %.f\n",skin->fixed_size); + + } else if( i==argc-1 || + (skin->single_track==0 && strchr(argv[i],'=')==NULL + && !(argv[i][0]=='-' && argv[i][1]!=0) ) || + (skin->single_track==-1)) { + if(strlen(argv[i])>=sizeof(skin->source_path)) { + fprintf(stderr, + "cdrskin: FATAL : source_address too long. (max: %d, given: %d)\n", + sizeof(skin->source_path)-1,strlen(argv[i])); + return(0); + } + source_has_size= 0; + strcpy(skin->source_path,argv[i]); + if(strcmp(skin->source_path,"-")==0) { + if(skin->stdin_source_used) { + fprintf(stderr, + "cdrskin: FATAL : \"-\" (stdin) can be used as track source only once.\n"); + return(0); + } + skin->stdin_source_used= 1; + } else if(argv[i][0]=='#' && (argv[i][1]>='0' && argv[i][1]<='9')) { + if(skin->preskin->allow_fd_source==0) { + fprintf(stderr, + "cdrskin: SORRY : '%s' is a reserved source path with cdrskin\n", + argv[i]); + fprintf(stderr, + "cdrskin: SORRY : which would use an open file descriptor as source.\n"); + fprintf(stderr, + "cdrskin: SORRY : Its usage is dangerous and disabled for now.\n"); + return(0); + } + } else { + if(stat(skin->source_path,&stbuf)!=-1) { + if((stbuf.st_mode&S_IFMT)==S_IFREG) + source_has_size= 1; + else if((stbuf.st_mode&S_IFMT)==S_IFDIR) { + fprintf(stderr, + "cdrskin: FATAL : source address is a directory: '%s'\n", + skin->source_path); + return(0); + } + } + } + if(! source_has_size) { + if(skin->fixed_size<=0.0) { + if(strcmp(skin->preskin->write_mode_name,"TAO")==0) { + /* with TAO it is ok to have an undefined track length */; + +#ifdef Cdrskin_allow_libburn_taO + } else if(strcmp(skin->preskin->write_mode_name,"DEFAULT")==0) { + strcpy(skin->preskin->write_mode_name,"TAO"); +#endif + + } else if(skin->tao_to_sao_tsize>0.0) { + skin->fixed_size= skin->tao_to_sao_tsize; + printf( + "cdrskin: NOTE : augmenting non-tao write mode by tao_to_sao_tsize\n"); + printf("cdrskin: NOTE : fixed size : %.f\n",skin->fixed_size); + } else { + fprintf(stderr, +#ifdef Cdrskin_allow_libburn_taO + "cdrskin: FATAL : Track source '%s' needs -tao or tsize= or tao_to_sao_tsize=\n", +#else + "cdrskin: FATAL : Track source '%s' needs a fixed tsize= or tao_to_sao_tsize=\n", +#endif + skin->source_path); + return(0); + } + } + } + if(skin->track_counter>=Cdrskin_track_maX) { + fprintf(stderr,"cdrskin: FATAL : too many tracks given. (max %d)\n", + Cdrskin_track_maX); + return(0); + } + ret= Cdrtrack_new(&(skin->tracklist[skin->track_counter]),skin, + skin->track_counter, + (strcmp(skin->source_path,"-")==0)<<1); + if(ret<=0) { + fprintf(stderr, + "cdrskin: FATAL : creation of track control object failed.\n"); + return(ret); + } + skin->track_counter++; + if(skin->verbosity>=Cdrskin_verbose_cmD) { + if(strcmp(skin->source_path,"-")==0) + printf("cdrskin: track %d data source : '-' (i.e. standard input)\n", + skin->track_counter); + else + printf("cdrskin: track %d data source : '%s'\n", + skin->track_counter,skin->source_path); + } + /* reset track options */ + if(skin->set_by_padsize) + skin->padding= 0; /* cdrecord-ProDVD-2.01b31 resets to 30k + the man page says padsize= is reset to 0 + Joerg Schilling will change in 2.01.01 to 0 */ + skin->fixed_size= 0; + } else { +ignore_unknown:; + fprintf(stderr,"cdrskin: NOTE : ignoring unknown option : '%s'\n", + argv[i]); + } + } + + if(flag&1) /* no finalizing yet */ + return(1); + + if(skin->verbosity>=Cdrskin_verbose_cmD) { + if(skin->preskin->abort_handler==1) + printf("cdrskin: installed abort handler.\n"); + else if(skin->preskin->abort_handler==2) + printf("cdrskin: will try to ignore any signals.\n"); + else if(skin->preskin->abort_handler==3) + printf("cdrskin: installed hard abort handler.\n"); + else if(skin->preskin->abort_handler==4) + printf("cdrskin: installed soft abort handler.\n"); + else if(skin->preskin->abort_handler==-1) + printf("cdrskin: will install abort handler in eventual burn loop.\n"); + } + + if(strlen(skin->preskin->raw_device_adr)>0 || + strlen(skin->preskin->device_adr)>0) { + if(strlen(skin->preskin->device_adr)>0) + cpt= skin->preskin->device_adr; + else + cpt= skin->preskin->raw_device_adr; + if(strcmp(cpt,"ATA")!=0 && strcmp(cpt,"ATAPI")!=0 && strcmp(cpt,"SCSI")!=0){ + ret= Cdrskin_dev_to_driveno(skin,cpt,&(skin->driveno),0); + if(ret<=0) + return(ret); + if(skin->verbosity>=Cdrskin_verbose_cmD) { + +#ifdef Cdrskin_libburn_has_drive_get_adR + ret= burn_drive_get_adr(&(skin->drives[skin->driveno]), adr); + if(ret<=0) + adr[0]= 0; +#else + strcpy(adr,skin->drives[skin->driveno].location); +#endif + + printf("cdrskin: active drive number : %d '%s'\n", + skin->driveno,adr); + } + } + } + if(grab_and_wait_value>0) { + Cdrskin_grab_drive(skin,0); + for(k= 0; ktrack_counter>0) { + skin->do_burn= 1; + +#ifndef Cdrskin_extra_leaN + ret= Cdrskin_attach_fifo(skin,0); + if(ret<=0) + return(ret); +#endif /* ! Cdrskin_extra_leaN */ + + } + return(1); +} + + +/** Initialize libburn, create a CdrskiN program run control object, + set eventual device whitelist, and obtain the list of available drives. + @param o Returns the CdrskiN object created + @param lib_initialized Returns wether libburn was initialized here + @param exit_value Returns after error the proposal for an exit value + @param flag Unused yet + @return <=0 error, 1 success +*/ +int Cdrskin_create(struct CdrskiN **o, struct CdrpreskiN **preskin, + int *exit_value, int flag) +{ + int ret; + struct CdrskiN *skin; + + *o= NULL; + *exit_value= 0; + +#ifndef Cdrskin_libburn_no_burn_preset_device_opeN + burn_preset_device_open((*preskin)->drive_exclusive, + (*preskin)->drive_blocking, + (*preskin)->abort_on_busy_drive); +#endif + + if(strlen((*preskin)->device_adr)>0) { /* disable scan for all others */ + printf("cdrskin: NOTE : greying out all drives besides given dev='%s'\n", + (*preskin)->device_adr); + burn_drive_add_whitelist((*preskin)->device_adr); + } + + ret= Cdrskin_new(&skin,*preskin,1); + if(ret<=0) { + fprintf(stderr,"cdrskin: FATAL : creation of control object failed\n"); + {*exit_value= 2; goto ex;} + } + *preskin= NULL; /* the preskin object now is under management of skin */ + *o= skin; + if(skin->preskin->abort_handler==1 || skin->preskin->abort_handler==3 || + skin->preskin->abort_handler==4) + Cleanup_set_handlers(skin,(Cleanup_app_handler_T) Cdrskin_abort_handler,4); + else if(skin->preskin->abort_handler==2) + Cleanup_set_handlers(skin,(Cleanup_app_handler_T) Cdrskin_abort_handler,2|8); + + printf("cdrskin: scanning for devices ...\n"); + fflush(stdout); + + /* In cdrskin there is not much sense in queueing library messages. + It is done here only for debugging */ + Cdrpreskin_queue_msgs(skin->preskin,1); + + while (!burn_drive_scan(&(skin->drives), &(skin->n_drives))) { + usleep(20000); + /* >>> ??? set a timeout ? */ + } + + /* This prints the eventual queued messages */ + Cdrpreskin_queue_msgs(skin->preskin,0); + + printf("cdrskin: ... scanning for devices done\n"); + fflush(stdout); +ex:; + return((*exit_value)==0); +} + + +/** Perform the activities which were ordered by setup + @param skin Knows what to do + @param exit_value Returns the proposal for an exit value + @param flag Unused yet + @return <=0 error, 1 success +*/ +int Cdrskin_run(struct CdrskiN *skin, int *exit_value, int flag) +{ + int ret; + + *exit_value= 0; + + if(skin->preskin->allow_setuid==0 && getuid()!=geteuid()) { + fprintf(stderr,"\n"); + fprintf(stderr,"cdrskin: WARNING : THIS PROGRAM WAS TREATED WITH chmod u+s WHICH IS INSECURE !\n"); + fprintf(stderr, + "cdrskin: HINT : Consider to allow rw-access to the writer device and\n"); + fprintf(stderr, + "cdrskin: HINT : to run cdrskin under your normal user identity.\n"); + fprintf(stderr, + "cdrskin: HINT : Option --allow_setuid disables this safety check.\n"); + fprintf(stderr,"\n"); + } + + if(skin->do_devices) { + if(skin->n_drives<=0 && skin->preskin->scan_demands_drive) + {*exit_value= 4; goto no_drive;} + ret= Cdrskin_scanbus(skin,1); + if(ret<=0) { + fprintf(stderr,"cdrskin: FATAL : --devices failed.\n"); + {*exit_value= 4; goto ex;} + } + } + if(skin->do_scanbus) { + if(skin->n_drives<=0 && skin->preskin->scan_demands_drive) + {*exit_value= 5; goto no_drive;} + ret= Cdrskin_scanbus(skin,0); + if(ret<=0) + fprintf(stderr,"cdrskin: FATAL : -scanbus failed.\n"); + {*exit_value= 5*(ret<=0); goto ex;} + } + if(skin->do_checkdrive) { + ret= Cdrskin_checkdrive(skin,"",0); + {*exit_value= 6*(ret<=0); goto ex;} + } + if(skin->do_msinfo) { + if(skin->n_drives<=0) + {*exit_value= 12; goto no_drive;} + ret= Cdrskin_msinfo(skin,0); + if(ret<=0) + {*exit_value= 12; goto ex;} + } + if(skin->do_atip) { + if(skin->n_drives<=0) + {*exit_value= 7; goto no_drive;} + ret= Cdrskin_atip(skin,(skin->do_atip>1)); + if(ret<=0) + {*exit_value= 7; goto ex;} + } + if(skin->do_blank) { + if(skin->n_drives<=0) + {*exit_value= 8; goto no_drive;} + ret= Cdrskin_blank(skin,0); + if(ret<=0) + {*exit_value= 8; goto ex;} + } + if(skin->do_burn) { + if(skin->n_drives<=0) + {*exit_value= 10; goto no_drive;} + ret= Cdrskin_burn(skin,0); + if(ret<=0) + {*exit_value= 10; goto ex;} + } +ex:; + return((*exit_value)==0); +no_drive:; + fprintf(stderr,"cdrskin: FATAL : This run would need an accessible drive\n"); + goto ex; +} + + +int main(int argc, char **argv) +{ + int ret,exit_value= 0,lib_initialized= 0,i,result_fd= -1; + struct CdrpreskiN *preskin= NULL; + struct CdrskiN *skin= NULL; + char *lean_id= ""; +#ifdef Cdrskin_extra_leaN + lean_id= ".lean"; +#endif + + /* For -msinfo: Redirect normal stdout to stderr */ + for(i=1; in_drives<=0) { + fprintf(stderr,"cdrskin: NOTE : No usable drive detected.\n"); + if(getuid()!=0) { + fprintf(stderr, + "cdrskin: HINT : Run this program as superuser with option --devices\n"); + fprintf(stderr, + "cdrskin: HINT : Allow rw-access to the dev='...' file of the burner.\n"); + fprintf(stderr, + "cdrskin: HINT : Busy drives are invisible. (Busy = open O_EXCL)\n"); + } + } + Cdrskin_set_msinfo_fd(skin,result_fd,0); + + ret= Cdrskin_setup(skin,argc,argv,0); + if(ret<=0) + {exit_value= 3; goto ex;} + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: called as : %s\n",argv[0]); + + if(skin->verbosity>=Cdrskin_verbose_debuG) { +#ifdef Cdrskin_oldfashioned_api_usE + ClN(fprintf(stderr,"cdrskin_debug: Compiled with option -oldfashioned\n")); +#endif +#ifdef Cdrskin_new_api_tesT + ClN(fprintf(stderr,"cdrskin_debug: Compiled with option -experimental\n")); +#endif + } + + Cdrskin_run(skin,&exit_value,0); + +ex:; + if(skin!=NULL) { + Cleanup_set_handlers(NULL,NULL,1); + Cdrskin_eject(skin,0); + Cdrskin_destroy(&skin,0); + } + Cdrpreskin_destroy(&preskin,0); + if(lib_initialized) + burn_finish(); + exit(exit_value); +} diff --git a/libburn/tags/ZeroThreeZero/cdrskin/cdrskin_eng.html b/libburn/tags/ZeroThreeZero/cdrskin/cdrskin_eng.html new file mode 100644 index 00000000..b4ae222f --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/cdrskin_eng.html @@ -0,0 +1,441 @@ + + + + + + +cdrskin homepage english + + + + + +
+ +cdrskin logo: Doener mit Scharf + +

Homepage of

+

cdrskin

+ + +

Limited cdrecord compatibility wrapper for libburn

+
+ +

+

Purpose:

+
    +
  • Burns preformatted data to CD-R or CD-RW
  • +
+

+

+ +


+ +

+

Hardware requirements:

+A CD recorder suitable for +libburnia.pykix.org +(SCSI or IDE/ATAPI writers compliant to mmc-3 standard). +
+

+ +

+

Software requirements :

+
+
Linux kernel 2.4 or higher
+
With kernel 2.4 the drive has to be under ide-scsi emulation.
+
With kernel 2.6 the drive should not be under ide-scsi.
+
libpthread
+
is supposed to be a standard system component.
+
+

+ +

+

+GPL software included:
+

+
+
libburn-0.2.6
+
(by Derek Foreman, Ben Jansens, and team of libburnia.pykix.org)
+
transfers data to CD
+
+

+ +

+This program system has been tested on Intel/AMD Linux systems only.
+Ports to other usable systems are appreciated. Reports are welcome. +

+ +
+ +

+

Special features:

+
    +
  • Source code is independent of +cdrecord +
  • +
+

+ +

+

Commands:

+
+
The goal is to provide some of cdrecord's options in a compatible way. +This has been achieved quite sufficiently for the needs of backup tool +scdbackup +and for data CD projects of K3b +(see examples). +Suitability for audio CD frontends has been improved much and is now being +evaluated.
+Further enhancements depend on people who can describe and discuss their +wishes as well as on the development of libburn.
+

+
Get an overview of drives:
+
$ cdrskin -scanbus
+
$ cdrskin dev=ATA -scanbus
+
$ cdrskin --devices
+
Get info about a particular drive or loaded media:
+
$ cdrskin dev=0,1,0 -checkdrive
+
$ cdrskin dev=ATA:1,0,0 -atip
+
$ cdrskin dev=/dev/hdc -toc
+
Make used CD-RW writable again:
+
$ cdrskin -v dev=/dev/sg1 blank=all -eject
+
$ cdrskin -v dev=/dev/dvd blank=fast -eject
+
Write ISO-9660 filesystem image:
+
$ cdrskin -v dev=/dev/hdc speed=12 fs=8m driveropts=burnfree -sao -eject padsize=300k my_image.iso
+
Write compressed afio archive on-the-fly :
+
$ find . | afio -oZ - | cdrskin -v dev=0,1,0 fs=32m speed=8 driveropts=burnfree padsize=300k -tao -
+
Write audio tracks:
+
$ cdrskin -v dev=ATA:1,0,0 speed=48 driveropts=burnfree -sao track1.wav track2.au -audio -swab track3.raw +
+
+
Get overview of the cdrecord compatible options:
+
$ cdrskin -help
+
Get overview of the non-cdrecord options:
+
$ cdrskin --help
+
Read the detailed manual page:
+
$ man cdrskin
+
Read about the standard for which cdrskin is striving:
+
+$ man cdrecord
+
Do not bother Joerg Schilling with any cdrskin problems. +(Be cursed if you install cdrskin as "cdrecord" without clearly forwarding + this "don't bother Joerg" demand.) +
+
+

+ +

+

Known deficiencies:

+
    +
    +
  • +Appending sessions to unclosed media is restricted to write mode TAO. +
  • +
  • +cdrskin -scanbus or --devices hangs for quite a while if there is +a CD drive which does not work properly (e.g. because it has individual +problems with DMA). +So if the superuser gets no result with cdrskin --devices then one should +disable DMA with the problematic CD drives +(like: hdparm -d0 /dev/hdd ) +and try again.
    +In severe cases it might be necessary to guess the device name /dev/sgN resp. +/dev/hdX of the non-ill burner if it cannot be found otherwise among its +ill peers. Alternatively one can guess the address of the ill device, remove +rw-permissions and retry the bus scan as non-superuser. +
+

+ +
+ +

+

+
Download as source code (see README):
+
cdrskin-0.2.6.pl02.tar.gz +(510 KB). +
+
(Most recent patch: backported man page from cdrskin-0.2.7)
+
+The "stable" cdrskin tarballs are source code identical with "stable" +libburn releases or with "stabilized" libburn SVN snapshots. They get +produced via a different procedure, though.
+cdrskin is part of libburn - full libburn is provided with cdrskin releases. +
+
 
+
Download as single x86 binaries (untar and move to /usr/bin/cdrskin):
+
+cdrskin_0.2.6.pl01-x86-suse9_0.tar.gz, (60 KB), +
+
runs on SuSE 9.0 (2.4.21) , RIP-14.4 (2.6.14) , + Gentoo (2.6.15 x86_64 Athlon).
+
+
+cdrskin_0.2.6.pl01-x86-suse9_0-static.tar.gz, (260 KB), -static compiled, +
+
runs on SuSE 7.2 (2.4.4), and on the systems above.
+
+
+
+
Documentation:
+
README a short introduction
+
cdrskin --help non-cdrecord options
+
cdrskin -help cdrecord compatible options
+
man cdrskin the manual page
+
 
+
+
Contact:
+
Thomas Schmitt, scdbackup@gmx.net
+
libburn development mailing list, +libburn-hackers@pykix.org
+
+
License:
+
GPL, an Open Source approved license
+
 
+
+

+ +
+ +

+Enhancements towards previous stable version cdrskin-0.2.4: +

    +
  • Option -tao is fully enabled.
    + SAO is still the preferred default but TAO is default if a track of + unpredicted size is present (stdin, named pipe, ...) or if a follow-up + session is written to an appendable CD. + (This is an intentional deviation from cdrecord defaults which themselves + have changed with the newest cdrecord versions.) +
  • +
  • Status report during blank, preparation and finalization improved.
  • +
  • Bug fixed: Trailing trash appended to .wav files caused error message + and, if exceeding fifo size, could even stall a burn. + (Workaround: disable fifo by fs=0)
  • +
  • Bug fixed: False speed with first pacifier cycle. Potential program + abort by floating point exception (NaN).
  • +
  • multi-session CDs: -multi, -msinfo, + writing to appendable CDs (for now restricted to write mode TAO).
  • +
+

+ +
+ +

+

+

Development snapshot, version 0.2.7 :

+
Enhancements towards stable version 0.2.6: +
    +
  • Improved recognition of unsuitable media types
  • +
  • Ban of chmod u+s is replaced by a loud warning
  • +
  • Detailed man page (already backported to cdrskin-0.2.6.pl02)
  • +
  • Burning to DVD+RW and DVD-RAM as non-multi, non-appending, +single-track session
  • +
  • Formatting and then burning to DVD-RW like to DVD+RW
  • +
  • Emulation of new wodim option msifile=path
  • +
+
+
 
+
README 0.2.7 +
cdrskin_0.2.7 --help
+
cdrskin_0.2.7 -help
+
man cdrskin (as of 0.2.7)
+
 
+
Maintainers of cdrskin unstable packages please use SVN of + libburnia.pykix.org
+
Download: svn co http://libburnia-svn.pykix.org/libburn/trunk libburn_pykix +
+
Build: cd libburn_pykix ; ./bootstrap ; ./configure ; make +
+
Build of SVN versions needs +autotools of at least version 1.7 installed. +But after the run of ./bootstrap, only +vanilla tools like make and gcc are needed.
+ +
 
+
The following downloads are intended for adventurous end users or +admins with full system souvereignty.
+
Source (./bootstrap is already applied, build tested, for more see +upcoming README ): +
+
+cdrskin-0.2.7.tar.gz +(550 KB). +
+
Binary (untar and move to /usr/bin/cdrskin):
+
+cdrskin_0.2.7-x86-suse9_0.tar.gz, (75 KB). +
+
+cdrskin_0.2.7-x86-suse9_0-static.tar.gz, (275 KB) +
+
+

+ +
+ +

+Many thanks to Joerg Schilling for cdrecord, +
+and to Derek Foreman and Ben Jansens for creating libburn. +
+Historic versions based on Derek's and Ben's +icculus.org/burn :
+cdrskin-0.1.2.0.2.ts.tar.gz
+cdrskin-0.1.3.0.2.ts.tar.gz +

+ +
+ + +

+

+
Example for a setup of device permissions. To be done by the superuser:
+
(CD devices which offer no r-permission are invisible to normal users.)
+
(CD devices which offer no w-permission are not useable.)
+
# cdrskin --devices
+
...
+
0  dev='/dev/sg0'  rwrwr- :  'TEAC' 'CD-ROM CD-532S'
+
1  dev='/dev/hdc'  rwrw-- :  'LITE-ON' 'LTR-48125S'
+
# chmod a+rw /dev/sg0 /dev/hdc
+
+

+ +
+ +
+

+ +Example how to setup K3b to use cdrskin for burning data CD projects. +
+(
K3b +is a GUI frontend which uses cdrecord for CD burning.) +

+ +
+ + +

+

+
Example for a test session with a cdrecord based scdbackup installation:
+
$ cdrskin -scanbus
+
...
+
    2,0,0    0)  'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM
+
$ cdrskin -scanbus dev=ATA
+
...
+
    1,0,0    1)  'LITE-ON' 'LTR-48125S' '?' Removable CD-ROM
+
$ export SCDBACKUP_SCSI_ADR="ATA:1,0,0"
+
$ export SCDBACKUP_CDRECORD="cdrskin -v -v tao_to_sao_tsize=650m"
+
$ scdbackup_home
+
+
+
Example for a permanent configuration of cdrskin based scdbackup
+
$ cd scdbackup-0.8.6/inst
+
$ export SCDBACKUP_USE_CDRSKIN=1
+
$ ./CONFIGURE_CD
+
...
+
cdrskin 0.2.6 : limited cdrecord compatibility wrapper for libburn
+
+If your system is stricken with some ill CD device then this can stall +and you will have to press Ctrl+C to abort. +In this case, you may execute +export SCDBACKUP_NO_SCANBUS=1 +and try again. +
+
+
------------------- SCSI devices. To be used like    0,0,0
+
    2,0,0    0)  'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM
+
------------------- end of SCSI device list
+
------------------- ATA devices. To be used like ATA:0,0,0 +
    1,0,0    1)  'LITE-ON' 'LTR-48125S' '?' Removable CD-ROM
+
...
+
    * Your cdrecord offers -driveropts=burnfree with your recorder.
+
...
+
scdbackup for CD 0.8.6 : First stage of installation done.
+
...
+
Now give it a try. Run : scdbackup_home
+
$ unset SCDBACKUP_USE_CDRSKIN
+
+
+
To get back to using cdrecord :
+
$ cd scdbackup-0.8.6/inst
+
$ export SCDBACKUP_USE_CDRSKIN=0
+
$ ./CONFIGURE_CD
+
...
+
$ unset SCDBACKUP_USE_CDRSKIN
+
+

+ +
+ +
+

+

About the relationship of cdrecord and cdrskin

+First of all: this relationship is single sided, as cdrskin has to be aware of +cdrecord but not vice versa. +
+
+I am a long time user of cdrecord and it works fine for me. +Especially i do appreciate its write mode -tao which allows to pipe arbitrary +data on CD and CD-RW via stdin. cdrecord is reliable, versatile and well +maintained. So for me - there would be not problem with it. +
+But the author of cdrecord and the Linux kernel people foster a very hostile +relationship. Ok, that's their business, not mine (or ours if you are with me). +One has to be aware, though, that this relationship might lead to a situation +where cdrecord is no longer available for certain Linux kernels. +
+To have my own project prepared for such a time, i began to implement its +cdrecord gestures on top of libburn. +From now on i invite other interested users of cdrecord to teach cdrskin +the gestures necessary for their cdrecord applications. +Contact me. Let's see what we can achieve. +
+
+libburn and cdrskin are now mature enough to substitute cdrecord in its +major use cases of CD burning. It is possible to foist cdrskin on various +software packages if it gets falsely named "cdrecord". +I do not encourage this approach, but of course such a replacement +opportunity is the goal of a cdrecord compatibility wrapper. +
+
+It is very important to me that this project is not perceived as hostile +towards Joerg Schilling and his ongoing work. +I owe him much. For cdrecord, for mkisofs, for star. Chapeau. +
+

+
+ +
+ + +cdrskin logo: Doener mit Scharf +

+Enjoying free Open Source hosting by www.webframe.org
+ +100 % Microsoft free
+and by sourceforge.net
+ +SourceForge Logo + +
+
+
+
Links to my other published software projects : +
+scdbackup, multi volume CD backup +
+(a second source of above)
+
Some Tools for Image Collectors +
(a second source of above)
+
+pppoem, a DSL throughput monitor (mainly for Linux kernel 2.4) +
+

+Legal statement: This website does not serve any commercial purpose.
+
+ + diff --git a/libburn/tags/ZeroThreeZero/cdrskin/cdrskin_timestamp.h b/libburn/tags/ZeroThreeZero/cdrskin/cdrskin_timestamp.h new file mode 100644 index 00000000..d3a49808 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/cdrskin_timestamp.h @@ -0,0 +1 @@ +#define Cdrskin_timestamP "2007.01.14.133951" diff --git a/libburn/tags/ZeroThreeZero/cdrskin/changelog.txt b/libburn/tags/ZeroThreeZero/cdrskin/changelog.txt new file mode 100644 index 00000000..76bd6514 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/changelog.txt @@ -0,0 +1,2519 @@ +------------------------------------------------------------------------------ + libburn.pykix.org scdbackup.sourceforge.net/cdrskin +------------------------------------------------------------------------------ + +Deliberate deviations of cdrskin from cdrecord compatibility: + ++ cdrskin does drive operations (on its drive 0) without a dev= option + ++ gracetime=0 is allowed and set by default + ++ -pad is always set for audio tracks + ++ -sao is default where possible, -tao is default where needed + (Note: cdrecord write mode defaults have changed in cdrtools-2.01.01a20) + ++ premature end of source is not an error and leads to full announced tracksize + ++ -msinfo pushes all other messages to stderr. It works independent of + other options which would prevent it with cdrecord (-atip, -scanbus, ...) + ++ DVD+RW writing is like -tao on giant blank CD-RW. + Same with DVD-RW in mode Restricted Overwrite. + ++ DVD-RW "Restricted Overwrite" writing is like DVD+RW (i.e. like -tao). + Formatting is done via cdrskin-specific blank=format_overwrite and not + with option -format. + +------------------------------------------------------------------------------ + Changelog +------------------------------------------------------------------------------ + +19 Aug 2006 [committed] +README +cdrskin/README cdrskin/cdrskin.c cdrskin/add_ts_changes_to_libburn_0_2_1 \ +cdrskin/cdrskin_timestamp.h cdrskin/compile_cdrskin.sh +cdrskin-0.1.4 "stable" released on base of August 15 2006 version of +libburn-svn.pykix.org/trunk after initial merge of libburn and cdrskin-0.1.3 . + +------------------------------------------------------------------------------ + +19 Aug 2006 [committed 16] +cdrskin/changelog.txt +cdrskin-0.1.5 development started. +Introduced this changelog. +---------------------------------- Format (still emerging): + +Day Month Year [eventual commit mark with revision number] +affected files , naked, one by line, +[eventually = internal snapshot tarball] +More ore less concise description. + +---------------------------------- End of Format + +20 Aug 2006 [committed together with next change, i fear] +libburn/sg.c +Hopefully fixed a file descriptor resource leak in sg_grab(). +All scanned drives (seem to) stay open once, the grabbed one got re-opened +and its stored first file descriptor got forgotten. Now we try to detect +and re-use the still open fd. + +21 Aug 2006 [committed] +libburn/libburn.h +libburn/sg.c +libburn/init.c += libburn_cdrskin_A60819.tgz +cdrskin/cdrskin.c [committed later as revision 11] +O_EXCL experiments imported from cdrskin-0.1.3 +In default configuration and on compliant kernels expect that a busy drive is +invisible to further cdrskin instances. The user gets hints in case of empty +bus scan result resp. busy drive given with dev=... +Wether cdrecord and cdrskin respect each other would have to be evaluated. +Options to play with: + --demand_a_drive exit !=0 on bus scans with empty result + --drive_abort_on_busy abort process if busy drive is found + (might be triggered by a busy hard disk) + --drive_blocking try to wait for busy drive to become free + (might be stalled by a busy hard disk) + --drive_not_exclusive do not ask kernel to prevent opening + busy drives. Effect is kernel dependend. + grab_drive_and_wait= grab drive, wait given number of + seconds, release drive, and do normal work + +21 Aug 2006 [committed] +libburn/write.c +Rectified non-ANSI-C comment, complained by gcc. + +21 Aug 2006 [committed 13] +cdrskin/cdrskin_eng.html +The homepage moved in from scdbackup's internal doc collection. + +21 Aug 2006 [committed with revision 11, i fear] +cdrskin/cdrskin.c +Removed flaw: +Help text of tao_to_sao_tsize= clarified. + +21 Aug 2006 [committed 12] +cdrskin/wiki_plain.txt +The initial filling of the cdrskin wiki on libburn.pykix.org . +I am not sure about the future fate of this text. I plan to keep it up to +date with the online wiki for now. The online version will be considered +the original. + +21 Aug 2006 [committed 15] +cdrskin/cdrskin_timestamp.h +Timestamping the new version. + +21 Aug 2006 [committed 14] +cdrskin/add_ts_changes_to_libburn_0_2_1 +cdrskin/compile_cdrskin.sh +Readjusted relationship glue of libburn and cdrskin. + +--------------------------------------------------------------------- cycled - + +27 Aug 2006 [40] +libburn/mmc.c +libburn/sg.c +Inserted prints to see how sg_issue_command() is called (printing is disabled now) + +21 Aug 2006 [17] +README +Reported obvious need for automake >=1.7 + +21 Aug 2006 [18] +cdrskin/cdrskin_eng.html +cdrskin/README +Reported obvious need for automake >=1.7 + +22 Aug 2006 [19] +libburn/drive.c +libburn/drive.h +New internal function burn_drive_is_open() + +23 Aug 2006 [20] +cdrskin/cdrskin.c +Implemented Lorenzo Taylor's audio patch manually by copy+paste +as i wanted to fully understand it. +Hopefully did not break it that way. + +24 Aug 2006 [21] +libburn/libburn.h +libburn/drive.c +Introduced API functions burn_drive_scan_and_grab() burn_drive_get_adr() + +24 Aug 2006 [22] +cdrskin/compile_cdrskin.sh +Experimental option cdrskin/compile_cdrskin.sh -newapi + +24 Aug 2006 [23] +cdrskin/cdrskin.c +First test of possibility to obey the self imposed rules of Revison 21 + +24 Aug 2006 [25] +cdrskin/cdrskin.c +Fixed undefined track_type introduced by revision 20. +(We broke cdrskin, but i did not break the patch. Success.) + +24 Aug 2006 [30] +libburn/libburn.h +Hopefully fixed an unintended line break in API doxygen + +25 Aug 2006 [32] +cdrskin/cdrskin.c +Installed protection against resource leak in Cdrskin_grab_drive() +Just to be sure. + +25 Aug 2006 [33] +libburn/drive.c +burn_drive_free() now closes all open drive file descriptors + +25 Aug 2006 [34] +libburn/libburn.h +Adjusted statement at API documention of burn_initialize() + +25 Aug 2006 [35] +cdrskin/cdrskin.c +Worked forth in order to make cdrskin fully newapi compliant + +26 Aug 2006 [37] +Makefile.am +libburn/back_hacks.h +libburn/drive.c +libburn/init.c +Allowed to blank appendable files and installed first back_hacks.h variable ever + +26 Aug 2006 [38] +test/burniso.c +Rewrote it to new API practice, inflated explanation comments, SAO mode + +27 Aug 2006 [39] +cdrskin/cdrskin.c +Implemented Lorenzos blank-appendable patch plus option --no_blank_appendable + +27 Aug 2006 [44] +test/blank.c +Rewrote test/blank.c to new API practice, inflated explanation comments + +27 Aug 2006 [41] +cdrskin/cdrskin.c +Fixed obscure sigsegv introduced with 35 or 39 by obeying libburn.h text +(could be a fandango starting in burn_drive_info_free) + +27 Aug 2006 [45] +test/burniso.c +Disabled inner burn_drive_info_free like in cdrskin, polished a bit + +27 Aug 2006 [43] +libburn/libburn.h +Changed some 'release' to 'close' with specs of burn_drive_scan_and_grab + +28 Aug 2006 [46] +test/burniso.c +Polished a bit more for doxygen + +28 Aug 2006 [50] +libburn/libburn.h +cdrskin/cdrskin.c +Wrote into API the imperative not to use drive.location but burn_drive_get_adr + +28 Aug 2006 [47] +test/burniso.c +Integrated functionality of test/devices.c into test/burniso.c +Proposed to rename it to test/libburner.c + +28 Aug 2006 [51] +cdrskin/cdrskin.c +Closed a pitfall with reading from '-' and no tsize= or tao_to_sao_tsize= +Ticket 55 + +28 Aug 2006 [52] +cdrskin/cdrskin.c +Added personal commitment to grant BSD license on request. Insisted in GPL for now. + +28 Aug 2006 [53] +cdrskin/cdrskin.c +Forced each track to have a minimum size of 600 kB +Ticket 55 + +29 Aug 2006 [58] +test/burniso.c +Integrated functionality of test/blank.c into test/burniso.c + +29 Aug 2006 [55] +cdrskin/cdrskin.c +Made cdrskin ready to make good use of now working libburn-eject + +29 Aug 2006 [56] +cdrskin/cdrskin.c +Avoided unwanted tray loading on eject of never grabbed drive + +29 Aug 2006 [57] +cdrskin/cdrskin.c +Disabled unconditionality of eject introduced by 55 or 56 + +30 Aug 2006 [59] +test/burniso.c +Repaired SIGSEGV caused by releasing ungrabbed drive after mere bus scan + +30 Aug 2006 [60] +test/libburner.c +Makefile.am +My proposal for new souvereign app as API doc and reference for API decisions + +31 Aug 2006 [61] +libburn/sg.c +cdrskin/cdrskin.c +Outcommented "experimental:" messages of O_EXCL development + +31 Aug 2006 [62] +test/libburner.c +Added 300 kB of padding to get rid of warning in doc, plus end sector padding + +31 Aug 2006 [63] +cdrskin/cdrskin.c +Promoted "newapi" functionality and libburn-eject from test to standard + +31 Aug 2006 [64] +cdrskin/cdrskin.c +Made cdrskin abort if fifo filling before burn yields 0 bytes (ticket 55) + +1 Sep 2006 [65] +cdrskin/README +cdrskin/cdrskin.c +cdrskin/cdrskin_eng.html +Updated cdrskin help tests and docs: -audio, obsolete eject_device= + +1 Sep 2006 [66] +libburn/write.c +cdrskin/cdrskin.c +Implemented track number patch by bonfire-app@wanadoo.fr, tickets 58 and 9 + +1 Sep 2006 [67] +cdrskin/cdrskin.c +Added clarifying URGE to ABORT texts + +1 Sep 2006 [71] +test/libburner.c +Made "read-ahead" comment sufficiently ambigous: "buffer"|"filesystem" == "" + +1 Sep 2006 [72] [73] +cdrskin/cdrskin.c +cdrskin/compile_cdrskin.sh +Rowed back from revision 64. Now #ifdef Cdrskin_fifo_abort_on_emptY + +1 Sep 2006 [74] +cdrskin/cdrskin.c +cdrskin/compile_cdrskin.sh +Rowed forth from revision 73. Now hopefully compliant to man cdrecord. + +1 Sep 2006 [78] +cdrskin/cdrskin.c +Made -pad behave cdrecord-ly on audio tracks (not tested acousticly) + +2 Sep 2006 [85] +test/libburner.c +Added upcoming clarification of copyright and license aspiration + +3 Sep 2006 [86] +README +Added upcoming clarification of copyright and license aspiration + +3 Sep 2006 [87] [88] +cdrskin/README +cdrskin/cdrskin.c +Added upcoming clarification of copyright and license aspiration + +3 Sep 2006 [91] +cdrskin/changelog.txt +cdrskin/cdrskin_timestamp.h +Opened new cdrskin-0.1.5 upload cycle. This marks a should-be-stable phase. + +---------------------------------------------------- cycled - 2006.09.03.132934 + + +3 Sep 2006 [89] +doc/comments_test_ts +Made a try to get doxygen portal page readable by html dl lists + +4 Sep 2006 [92] +cdrskin/README +Made changes as reported by Lorenzo on libburn-hackers today + +4 Sep 2006 [93] +libburn/transport.h +libburn/drive.h +libburn/drive.c +libburn/init.c +libburn/libburn.h +Integrated elmom patch proposal #3 from ticket #62 +/* ts A60904 : ticket 62, contribution by elmom */ + +4 Sep 2006 [94] +cdrskin/README +Removed reference to frontend "burn" (needs a patch to work for .mp3) + +5 Sep 2006 [95] +doc/comments_test_ts +Made a try to get doxygen portal page readable by pre tags and truncation + +5 Sep 2006 [96] +test/libburner.c +Rearranged definitions and header inclusions. Is safer so. + +5 Sep 2006 [97] +test/libburner.c +Re-inserted lost tab. + +6 Sep 2006 [trac] +closed ticket 55: burn of empty tracks from stdin is now forbidden + +6 Sep 2006 [98] +libburn/libburn.h +libburn/drive.c +Added new parameter "force" to API-experimental burn_drive_info_forget() + +6 Sep 2006 [99] +doc/comments +Made doc test portal the official doc portal + +6 Sep 2006 [100] +cdrskin/cdrfifo.c +Added an initial value on proposal by Bart Vanherck + +7 Sep 2006 [101] +test/libburner.c +Changed a macro name from Burniso_ to Libburner_ + +7 Sep 2006 [102] [103] +libburn/libburn.h +libburn/drive.c +cdrskin/cdrskin.c +Implemented first use of API-experimental burn_drive_info_forget() in cdrskin signal handler + +7 Sep 2006 [104] +cdrskin/cdrskin.c +Tried to make abort messages clearer + +7 Sep 2006 [105] +Makefile.am +test/testburner.c +Prepared test bed for burn_drive_info_forget() as regular API call + +7 Sep 2006 [106] +test/testburner.c +Removed a remnant piece of rather unhealthy test code + +7 Sep 2006 [106] +test/testburner.c +Corrected test reciepe + +7 Sep 2006 [107] +test/libburner.c +Added constraint --stdin_size >= 600k, better bus scan behavior + +8 Sep 2006 2006 [109] +libburn/drive.c +test/testburner.c +cdrskin/cdrskin.c +Hopefully ensured correct burn_disc_erasable() already after first grab + +9 Sep 2006 2006 [112] +libburn/drive.c +cdrskin/cdrskin.c +Hunted down the bug which let newapi-cdrskin fail with drive 1 + +10 Sep 2006 [113] +libburn/drive.c +test/libburner.c +test/testburner.c +cdrskin/cdrskin.c +Slowed down highspeed loops waiting for drive status changes + +10 Sep 2006 [114] +cdrskin/compile_cdrskin.sh +Aliased switch name -newapi by -experimental + +10 Sep 2006 [115] +test/libburner.c +test/testburner.c +cdrskin/cdrskin.c +Re-enabled call to burn_drive_info_free() after repair by revision 93 + +11 Sep 2006 [116] +cdrskin/compile_cdrskin.sh +Changed -newapi to -experimental in help text + +11 Sep 2006 [117] +libburn/drive.c +Removed a bug introduced with revison 93 + +11 Sep 2006 [118] +libburn/libburn.h +test/libburner.c +Officialized burn_drive_info_forget() + +11 Sep 2006 [119] [120] +Makefile.am +test/testburner.c +Deleted until next occasion: testburner + +12 Sep Sep 2006 [124] +cdrskin/cdrskin.c +Repaired regression of -eject which loaded tray again + +12 Sep 2006 [126] +cdrskin/cdrskin.c +Replaced -experimental method of closing libburn by burn_drive_info_forget() + +12 Sep 2006 [129] +cdrskin/cdrskin.c +Added automated padding to last audio track (ticket 41) + +2006.09.13.093350 [130] +cdrskin/make_timestamp.sh +Prepared for new revision timestamps to mark cdrskin test versions +From now on cdrskin/cdrskin_timestamp.h is to be part of any commit. + +15 Sep 2006 [135] +Makefile.am +Replaced a few 8-blanks by tab + +15 Sep 2006 [136] +README +Moved installation instructions in front of overview paragraph + +15 Sep 2006 [137] +Makefile.am +cdrskin/README +Made cdrskin an installable program + +2006.09.15.092509 [138] +cdrskin/cdrskin.c +cdrskin/compile_cdrskin.sh +Prepared cdrskin-build for version leap + + +----------------------------- cycled (last cdrskin-0.1.5 ?) - 2006.09.15.101326 + +2006.09.15.101326 [139] +cdrskin/README +cdrskin/changelog.txt +New upload of scdbackup.sourceforge.net/cdrskin-0.1.5.tar.gz + +2006.09.15.174748 [143] +cdrskin/cdrskin.c +Revoked change of 1 Sep 2006 revision 78 (full -nopad) (ticket 41) + +16 Sep 2006 [144] +libburn/libburn.h +Made doxygen happy with parameter of burn_drive_get_adr + +2006.09.16.194730 [145] +000_CAUTION_RELEASE_CANDIDATE +zzz_CAUTION_RELEASE_CANDIDATE +README +Makefile.am +cdrskin/README +cdrskin/cdrskin.c +cdrskin/compile_cdrskin.sh +Perfomed version leap in respect to SVN + +2006.09.19.124540 [160] [161] +cdrskin/cdrskin.c +Allowed driveropts=burnproof as alias for burnfree + +2006.09.19.140716 [162] [163] +cdrskin/cdrskin.c +Added error message in case of failed eject + +2006.09.20.134219 [175] [176] [177] +cdrskin/cdrskin.c +Fixed bug with dev=1,1,0 (ticket 75) + +21 Sep 2006 [186] +configure.ac +Makefile.am +Forwarded changes from 0.2.2 to 0.2.3 + +Sep 2006 [187] [188] [189] [190] +cdrskin/cdrskin.c +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_eng.html +cdrskin/README +cdrskin/changelog.txt +- cdrskin/add_ts_changes_to_libburn_0_2_1 ++ cdrskin/add_ts_changes_to_libburn_0_2_3 +Makefile.am +cdrskin/cdrskin_timestamp.h +Performed development version leap to cdrskin-0.2.3 + +------------------------------------ cycled - cdrskin-0.2.3 - 2006.09.21.082411 + +21 Sep 2006 [191] +README +Clarified build from SVN versus tarball + +2006.09.21.173012 [192] +cdrskin/cdrskin.c +cdrskin/compile_cdrskin.sh +Gave up compile options -tarball_0_2 , -cvs_A51208 , -libburn_0_2_1 +Now standard and therefore no longer needed as macros: +Cdrskin_libburn_p_sectoR +Cdrskin_libburn_with_fd_sourcE +Cdrskin_libburn_largefilE +Cdrskin_libburn_padding_does_worK + +2006.09.21.185623 [193] +cdrskin/cdrskin.c +cdrskin/compile_cdrskin.sh +Officialized gestures of pre-0.2.3 -experimental, introduced -oldfashioned + +2006.09.21.194006 [194] +cdrskin/cdrskin.c +Investigating failure to open drive on eject + +2006.09.22.133027 [195] +libburn/libburn.h +libburn/sg.c +libburn/sg.h +libburn/drive.c +cdrskin/cdrskin.c +Implemented resolving of softlinks (ticket 33) + +2006.09.22.170220 [196] +libburn/libburn.h +libburn/sg.c +libburn/sg.h +libburn/drive.c +cdrskin/cdrskin.c +Implemented new API function burn_drive_convert_fs_adr() + + +2006.09.22.172307 [197] +cdrskin/cdrskin.c +Fixed SIGSEGV of -eject on non-existent device address + +22 Sep 2006 [198] +libburn/sg.c +libburn/sg.h +libburn/drive.c +Implemented finding matching /dev/sgN from /dev/srM or /dev/scdK +(ioctl(): SCSI_IOCTL_GET_IDLUN) + +2006.09.22.195414 [199] +cdrskin/cdrskin.c +Removed bug prone implementation of link resolving (now only via libburn) + +2006.09.22.202631 [200] +cdrskin/cdrskin.c +Kept new address conversion from hopping on libburn drive numbers + +2006.09.22.203939 [201] +cdrskin/cdrskin.c +Fixed --no_follow_links and renamed to --no_convert_fs_adr + +2006.09.23.080015 [202] +libburn/drive.c +Restructured SCSI search, removed a potential bug with hdX + +23 Sep 2006 [203] +libburn/libburn.h +Made burn_drive_convert_scsi_adr() a new API function + +23 Sep 2006 [204] +libburn/drive.c +libburn/sg.c +Changed outdated comments + +23 Sep 2006 [205] +libburn/libburn.h +libburn/drive.c +Introduced new API function burn_drive_obtain_scsi_adr() + +2006.09.23.100001 [not in libburn-0.2.2 yet] +cdrskin-0.2.2/cdrskin/cdrskin.c +Backported bug fix of revision 197. Version timestamp : 2006.09.23.100001 + +23 Sep 2006 [206] [207] +libburn/drive.c +libburn/sg.c +Enabled unused SCSI part of struct burn_drive. Switched persistent address to burn_drive.devname + +2006.09.23.114858 [208] +cdrskin/cdrskin.c +Implemented --no_pseudo_scsi_adr + +2006.09.23.132755 [209] +cdrskin/cdrskin.c +libburn/drive.c +Removed a bug with SCSI address of scanned drives without such address + +23 Sep 2006 [210] +cdrskin/README +Explained the new addressing mode + +2006.09.23.182444 [211] +cdrskin/README +cdrskin/cdrskin.c +Bus,Target,Lun for dev=ATA and dev=ATAPI, real SCSI addressing by default + +2006.09.23.183608 [212] +cdrskin/cdrskin.c +Made small improvement with debug message + +2006.09.24.171706 [214] +Makefile.am +libburn/libburn.h +libburn/libdax_msgs.h +libburn/libdax_msgs.c +libburn/init.c +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin.c +Added an error message handling facility (ticket 74) + +2006.09.24.180836 [215] +libburn/init.c +libburn/sg.c +libburn/libdax_msgs.h +libburn/libdax_msgs.c +cdrskin/cdrskin.c +Made use of new message handling facility and removed first bugs + +24 Sep 2006 [216] +libburn/libdax_msgs.h +Recorded error_code 0x00020001 + +24 Sep 2006 [217] [218] +Makefile.am +libburn/libburn.h +libburn/write.c +libburn/drive.c +cdrskin/compile_cdrskin.sh +Obsoleted libburn/message.[ch] + +25 Sep 2006 [219] +libburn/read.c +libburn/write.c +Removed inclusion of libburn/message.h + +2006.09.25.104629 [220] +libburn/init.c +libburn/drive.c +cdrskin/cdrskin.c +Converted "libburn_experimental:" messages of address conversion into "DEBUG" + +2006.09.25.141035 [221] +libburn/libdax_msgs.h +libburn/libdax_msgs.c +libburn/drive.c +libburn/sg.h +libburn/sg.c +Implemented sg_close_drive_fd (ticket 74) + +2006.09.25.144506 [222] +libburn/libdax_msgs.c +Achieved minimum strerror thread safety (strerror_r is burned by Unix and GNU) + +2006.09.26.114552 [223] +libburn/libburn.h +libburn/init.c +libburn/libdax_msgs.c +cdrskin/cdrskin.c +Made first use of queued messages and fixed several bugs with that + +2006.09.26.142824 [224] +libburn/sg.c +libburn/libdax_msgs.h +cdrskin/cdrskin.c +Made changes with usage of queued messages + +24 Sep 2006 [225] +Makefile.am +libburn/libburn.h +libburn/write.c +libburn/drive.c +- libburn/message.c +- libburn/message.h +Removed libburn/message.[ch] + +2006.09.26.205504 [227] +libburn/drive.c +Enhanced softlink resolution + +2006.09.26.210711 [228] +libburn/drive.c +Fixed bug in enhanced softlink resolution + +2006.09.27.063147 [229] +cdrskin/cdrskin.c +Fixed bug with relative device addresses and Cdrpreskin__cdrecord_to_dev() + +2006.09.27.074910 [230] +cdrskin/cdrskin.c +Fixed broken -version and --help (second time for same mistake) + +2006.09.27.080826 [231] +cdrskin/README +cdrskin/cdrskin.c +Allowed comments and empty lines in startup files + +2006.09.27.082057 [232] +cdrskin/cdrskin.c +Prevented reading of startup files with first arg -version, -help or --help + +2006.09.27.115919 [233] +libburn/drive.c +libburn/sg.h +libburn/sg.c +libburn/libdax_msgs.h +cdrskin/cdrskin.c +Disabled but did not discarded failed attempt to lock against growisofs + +2006.09.27.120656 [234] +libburn/drive.h +libburn/init.c +libburn/transport.h +libburn/libburn.h +Disabled but did not discarded failed attempt to lock against growisofs + +2006.09.27.134332 [235] +libburn/sg.c +Kept /dev/hdX from all having SCSI address 0,0,0 + +2006.09.27.142312 [236] +libburn/drive.c +Curbed endless links to 20 hops + +2006.09.27.143843 [237] +libburn/sg.c +Removed obsolete code and comments + +2006.09.28.074434 [238] +cdrskin/cdrskin.c +Enabled optional growisofs lock attempt via --drive_scsi_exclusive + +28 Sep 2006 [239] +libburn/libburn.h +Made official exclusive==2 with burn_preset_device_open() + +------------------------------------ cycled - cdrskin-0.2.3 - 2006.09.28.100934 + +2006.09.28.115152 [240] +cdrskin/cdrskin.c +Restored vanished line at end of -help text and added a new one + +2006.10.01.104140 [243] +libburn/drive.c +cdrskin/cdrskin.c +Enhanced Cdrpreskin__cdrecord_to_dev so it warns of invisible SCSI drive. + +2006.10.02.103418 [244] +libburn/libburn.h +libburn/drive.h +libburn/drive.c +libburn/libdax_msgs.h +libburn/libdax_msgs.c +cdrskin/cdrskin.c +Implemented burn_abort() and made use of it + +2006.10.03.162719 [245] +Makefile.am +libburn/libburn.h +libburn/init.c +libburn/cleanup.h +libburn/cleanup.c +cdrskin/cleanup.h +cdrskin/cleanup.c +test/libburner.c +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin.c +Implemented new API function burn_set_signal_handling(), libburner uses it + +5 Oct 2006 [246] +libburn/drive.c +Uploaded forgotten part of revision 245 + +2006.10.05.142628 [247] +libburn/libburn.h +libburn/transport.h +libburn/sg.c +libburn/sg.h +libburn/drive.c +cdrskin/cdrskin.c +Made use of SCSI_IOCTL_GET_BUS_NUMBER in hope of cdrecord compatibility + +6 Oct 2006 [248] ++ libburn/asserts.txt +Listed findings on assert() within libburn + +7 Oct 2006 [249] +libburn/async.c +libburn/libdax_msgs.h +libburn/asserts.txt +Got rid by soft means of assert() in async.c + +2006.10.07.121234 [250] +libburn/libburn.h +libburn/async.c +libburn/drive.h +libburn/drive.c +libburn/init.c +libburn/libdax_msgs.h +libburn/sg.c +libburn/asserts.txt +Got rid of assert() in drive.c by soft means + +2006.10.07.132916 [251] +libburn/init.c +libburn/async.c +Got rid of assert() in init.c by soft means + +2006.10.07.142454 [252] +libburn/libburn.h +libburn/async.c +libburn/options.c +libburn/sector.c +libburn/spc.c +libburn/libdax_msgs.h +Got rid of assert() in options.c by soft means + +7 Oct 2006 [253] +libburn/read.c +Got rid of assert() in read.c by soft means + +2006.10.07.170913 [254] +libburn/sg.c +libburn/asserts.txt +Got rid of some assert() in sg.c by soft means + +2006.10.07.175427 [255] +libburn/spc.c +libburn/async.c +libburn/libdax_msgs.h +Got rid of assert() in spc.c by soft means + +2006.10.08.095016 [256] +libburn/structure.c +libburn/sector.c +libburn/spc.c +libburn/libdax_msgs.h +libburn/asserts.txt +Got rid of assert() in structure.c by soft means + +8 Oct 2006 [257] +libburn/toc.c +Got rid of assert() in toc.c by soft means + +8 Oct 2006 [258] +libburn/util.c +Got rid of assert() in util.c by soft means + +2006.10.09.083438 [259] +libburn/write.c +libburn/structure.c +libburn/libdax_msgs.h +libburn/asserts.txt +Got rid of assert() in write.c by soft means + +2006.10.09.123518 [260] +libburn/mmc.c +libburn/read.c +libburn/sector.c +libburn/mmc.h +libburn/transport.h +libburn/asserts.txt +Got rid of assert() in mmc.c by soft means + +2006.10.10.112545 [261] +libburn/sector.h +libburn/sector.c +libburn/async.c +libburn/write.h +libburn/write.c +libburn/libdax_msgs.h +libburn/asserts.txt +Got rid of assert() in sector.c by soft means + +2006.10.10.175444 [262] +libburn/sg.c +libburn/libdax_msgs.h +Got rid of assert() in sg.c by soft means + +10 Oct 2006 [263] +libburn/asserts.txt +Got rid of assert() in libburn + +2006.10.11.191959 [264] +cdrskin/cdrskin.c +cdrskin/README +Changed pseudo-cdrecord addresses: /dev/hdX = ATA:(X-'a')/2,(X-'a')%2,0 + +13 Oct 2006 [270] +Makefile.am +libburn/sg.c +libburn/cleanup.c +libburn/sg-linux.c +libburn/read.c +libburn/drive.c +libburn/sbc.c +libburn/transport.h +cdrskin/cleanup.c +Made libburn and cdrskin build on my Linux again + +2006.10.13.114554 [271] +cdrskin/cdrskin.c +libburn/write.c +libburn/sg-linux.c +libburn/libdax_msgs.h +Removed bug in burn_disc_write_sync(): BURN_DRIVE_IDLE, then d->sync_cache() + +2006.10.14.105224 [272] +libburn/sg.h +libburn/sg-freebsd.c +libburn/sg-linux.c +libburn/drive.c +Introduced burn_drive_enumerator_t to allow more complete sg-freebsd implementation + +2006.10.15.133035 [274] +cdrskin/cdrskin.c +Changed ambigous include statement of libburn.h + +15 Oct 2006 2006 [275] +Makefile.am +libburn/libdax_msgs.h ++ libburn/libdax_audioxtr.h ++ libburn/libdax_audioxtr.c +Implemented a first attempt of a .wav decapitator (ticket 38) + +15 Oct 2006 2006 [276] ++ test/dewav.c +Implemented a first attempt of a .wav decapitator (ticket 38) + +15 Oct 2006 2006 [277] +test/dewav.c +Fixed permissions of eventually created output file + +15 Oct 2006 2006 [278] +test/dewav.c +cdrskin/compile_cdrskin.sh +Hunting a malloc/free memory problem + +15 Oct 2006 2006 [279] +libburn/libdax_audioxtr.c +Hopefully fixed memory problem which causes sigabrt on free + +15 Oct 2006 2006 [280] +libburn/libdax_audioxtr.c +Hopefully fixed problem with stdin as audio source + +15 Oct 2006 2006 [281] +test/dewav.c +Made helptext print to stderr rather than stdout + +2006.10.17.141053 [283] +cdrskin/cdrskin.c +cdrskin/compile_cdrskin.sh +libburn/libdax_audioxtr.h +libburn/libdax_audioxtr.c +test/dewav.c +Roughly implemented automatic .wav extraction in cdrskin + +2006.10.17.164140 [284] +cdrskin/cdrskin.c +libburn/libdax_audioxtr.h +libburn/libdax_audioxtr.c +test/dewav.c +Implemented some cdrecord pickiness for .wav extraction + +18 Oct 2006 [285] +test/libburner.c +Added --audio and multi-track, removed --verbose, hid --burn_for_real + +2006.10.18.174334 [286] +cdrskin/cdrskin.c +Removed assumption BURN_DRIVE_IDLE==0 + +18 Oct 2006 [287] +cdrskin/README +Changed audio statements to reflect new situation + +18 Oct 2006 [288] +cdrskin/README +Removed a typo + +19 Oct 2006 [292] +test/libburner.c +Freed all tracks after burning and not only last one + +2006.10.19.094543 [293] +cdrskin/cdrskin.c +cdrskin/compile_cdrskin.sh +Made cdrskin use libburn/cleanup.[oh] by default (not cdrskin/cleanup.[ch]) + +------------------------------------ cycled - cdrskin-0.2.3 - 2006.10.19.105730 + +2006.10.19.162254 [294] +cdrskin/cdrskin.c +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated documentation about cdrskin-0.2.3 + +2006.10.19.164742 [295] +cdrskin/cdrskin.c +Corrected -help text + +2006.10.20.113421 [297] +libburn/libburn.h +libburn/mmc.c +libburn/sg-linux.c +libburn/sg-freebsd.c +libburn/drive.c +cdrskin/cdrskin.c +Made cdrskin produce "ATIP start of lead" (on non-blank media for now) + +20 Oct 2006 [298] +doc/comments +Updated help text of libburner + +2006.10.20.151602 [299] +libburn/libburn.h +libburn/drive.c +libburn/mmc.c +libburn/libdax_msgs.h +cdrskin/cdrskin.c +Classified media with TOC read error as unsuitable (rather than as blank) + +2006.10.21.103352 [300] +libburn/libburn.h +libburn/transport.h +libburn/drive.c +libburn/mmc.c +libburn/spc.c +libburn/sg-linux.c +libburn/sg-freebsd.c +libburn/libdax_msgs.h +cdrskin/cdrskin.c +Implemented some ATIP functionality + +2006.10.21.103653 [301] [302] +libburn/libburn.h +Clarified relation of burn_disc_read_atip() and burn_drive_get_start_end_lba() + +2006.10.21.185102 [303] +libburn/sg.h +libburn/mmc.h +libburn/mmc.c +libburn/sbc.h +libburn/sbc.c +libburn/spc.h +libburn/spc.c +libburn/drive.h +libburn/drive.c +libburn/transport.h +libburn/sg-linux.c +libburn/sg-freebsd.c +Split enumerate_common() into logic-layer, command-layer, transport-layer + +2006.10.22.130341 [304] +libburn/libburn.h +libburn/mmc.c +libburn/drive.c +libburn/cleanup.c +cdrskin/cleanup.c +cdrskin/cdrskin.c +Implemented cdrskin -toc + +2006.10.22.131452 [305] +cdrskin/cdrskin.c +Regulated coexistence of options -toc and -atip + +23 Oct 2006 [306] +libburn/mmc.c +Made clarification in remark about atip speed conversion + +2006.10.23.074430 [307] +cdrskin/cdrskin.c +Made use of burn_drive_info.revision as firmware revision text + +2006.10.23.113116 [308] +libburn/libburn.h +libburn/transport.h +libburn/mmc.h +libburn/mmc.c +libburn/write.c +cdrskin/cdrskin.c +cdrskin/cdrskin_eng.html +cdrskin/README +Made available drive buffer fill during write + +23 Oct 2006 [309] +libburn/sg-freebsd.c +Updated tangling of FreeBSD code with mmc.c :( + +2006.10.23.134719 [310] +cdrskin/cdrskin.c +Corrected -toc track counter and notified about "hidden" tracks + +2006.10.24.075039 [311] +libburn/libdax_audioxtr.h +libburn/libdax_audioxtr.c +test/dewav.c +cdrskin/cdrskin.c +Introduced extraction of .au (but not its usage within cdrskin) + +2006.10.24.102107 [312] +libburn/libburn.h +libburn/structure.h +libburn/structure.c +libburn/sector.c +cdrskin/cdrskin.c +cdrskin/README +Enabled byte swapping for audio track sources, added anti option -swab + +2006.10.24.130259 [313] +test/dewav.c +cdrskin/cdrskin.c +cdrskin/README +Enabled automatic extraction of .au + +24 Oct 2006 [314] +Makefile.am ++ test/fake_au.c +Introduced temporary test program to produce SUN .au files + +------------------------------------ cycled - cdrskin-0.2.3 - 2006.10.24.144650 + +2006.10.24.165427 [315] +libburn/sector.c +Closed some loopholes for byte swapping. + + +2006.10.24.165610 [316] +cdrskin/cdrskin.c +Enabled audio byte swapping by default (to be disabled via option -swab) + +------------------------------------ cycled - cdrskin-0.2.3 - 2006.10.24.173602 + +25 Oct 2006 [317] +cdrskin/README +cdrskin/cdrskin_eng.html +cdrskin/wiki_plain.txt +Announced full -audio compatibility with cdrecord + +------------------------------------ cycled - cdrskin-0.2.3 - 2006.10.25.160540 + +2006.10.27.114326 [319 branch] +- cdrskin/add_ts_changes_to_libburn_0_2_3 ++ cdrskin/add_ts_changes_to_libburn_0_2_4 +cdrskin/README +cdrskin/cdrskin.c +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +README +Performed cdrskin version leap to cdrskin-0.2.4 + +2006.10.28.093922 [320 branch] +cdrskin/cdrskin_timestamp.h +Set final timestamp 2006.10.28.093922 + +------------------------------------ cycled - cdrskin-0.2.4 - 2006.10.28.093922 + +2006.10.28.115213 [321 trunk] [322 trunk] +- cdrskin/add_ts_changes_to_libburn_0_2_3 ++ cdrskin/add_ts_changes_to_libburn_0_2_4 ++ cdrskin/add_ts_changes_to_libburn_0_2_5 +cdrskin/README +cdrskin/cdrskin.c +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +README +Performed cdrskin version leap to cdrskin-0.2.5 + +28 Oct 2006 [323 branch] +- cdrskin/add_ts_changes_to_libburn_0_2_3 +cdrskin/add_ts_changes_to_libburn_0_2_4 +Corrected misnaming of my development directory + +28 Oct 2006 [324 trunk] +cdrskin/add_ts_changes_to_libburn_0_2_4 +cdrskin/add_ts_changes_to_libburn_0_2_5 +Corrected misnaming of my development directory + +2006.10.28.132532 [325 branch] +cdrskin/cdrskin.c +Corrected last-minute bug which made every track from file an audio track + +2006.10.28.151521 [326 trunk] +cdrskin/cdrskin.c +Corrected bug which made every track from file an audio track + +29 Oct 2006 [327 branch] [328 trunk] ++ CONTRIBUTORS +Copied missing file from libburn-0.2.2 + +29 Oct 2006 [329 branch] [330 trunk] +Makefile.am +Added to EXTRA_DIST cdrskin/cleanup.[ch] + +29 Oct 2006 [331 branch] [332 trunk] +Makefile.am +Added to EXTRA_DIST libburn/sg-*.[ch] + +29 Oct 2006 [333 branch] [334 trunk] +Makefile.am +Deleted from to EXTRA_DIST libburn/sg-*.h + +30 Oct 2006 [337] +libburn/transport.h +libburn/mmc.c +libburn/sg-freebsd.c +Made MMC command CLOSE TRACK/SESSION available to struct burn_drive + +2006.10.31.115606 [338] +libburn/transport.h +libburn/spc.c +libburn/mmc.c +libburn/write.h +libburn/write.c +libburn/sector.c +libburn/libdax_msgs.h +libburn/sg.h +libburn/sg-linux.c +cdrskin/cdrskin.c +Made single track TAO work (sector i/o still wants fixed size, though) + +2006.10.31.184736 [339] +libburn/sector.c +libburn/source.c +libburn/structure.h +libburn/structure.c +libburn/write.c +cdrskin/cdrskin.c +Made single track TAO work without fixed size (compile -experimental) + +2006.11.01.163934 [340] +libburn/libburn.h +libburn/source.c +libburn/write.h +libburn/write.c +libburn/structure.c +libburn/structure.h +libburn/sector.c +cdrskin/cdrskin.c +cdrskin/compile_cdrskin.sh +Adapted cdrskin pacifier to possibly unknown track size + +2006.11.01.172004 [341] +configure.ac +bootstrap +test/libburner.c +Repaired broken macro settings during Linux build + +2 Nov 2006 [342] +cdrskin/README +Mentioned -tao and experimental compile + +2006.11.02.211816 [343] +libburn/libburn.h +libburn/write.c +cdrskin/cdrskin.c +Installed status communications about closing session ("Fixating") + +3 Nov 2006 [344] +test/libburner.c +Changed status report during blanking (there are no "sectors") + +2006.11.03.063307 [345] +cdrskin/cdrskin.c +Removed some obsolete debugging messages + +2006.11.03.151137 [346] +libburn/structure.h +libburn/structure.c +libburn/write.h +libburn/write.c +libburn/sector.c +libburn/libdax_msgs.h +Installed decent abort behavior with TAO + +2006.11.03.202403 [347] +libburn/write.c +Enabled TAO for multiple -data tracks (-audio still ends after 0 bytes) + +2006.11.04.092909 [348] +libburn/spc.c +libburn/sector.c +Enabled audio tracks with TAO + +2006.11.02.140329 (pl01) [351 tags/CdrskinZeroTwoFourPlZeroOne] +../cdrskin-0.2.4.patch01/configure.ac +../cdrskin-0.2.4.patch01/bootstrap +../cdrskin-0.2.4.patch01/cdrskin/README +../cdrskin-0.2.4.patch01/cdrskin/add_ts_changes_to_libburn_0_2_4_patch01 +../cdrskin-0.2.4.patch01/cdrskin/cdrskin_timestamp.h +../cdrskin-0.2.4.patch01/test/libburner.c +Revoked autotools aspect of revision 290 + +2006.11.06.073810 [352] +cdrskin/cdrskin.c +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Adapted documentation to reflect experimental TAO mode + +------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.06.085056 + +2006.11.06.114159 [353] +libburn/libburn.h +libburn/spc.c +libburn/options.c +cdrskin/cdrskin.c +New API burn_write_opts_set_multi(). (But libburn cannot burn next session yet) + +2006.11.06.121409 [354] +cdrskin/cdrskin.c +Made -toc on multiple sessions more compatible + +2006.11.06.155237 [355] +libburn/libburn.h +libburn/drive.c +libburn/write.c +libburn/mmc.c +libburn/sg-linux.c +cdrskin/cdrskin.c +Made CD with unclosed track blankable (by -force) + +2006.11.06.195743 [356] +libburn/transport.h +libburn/mmc.c +libburn/spc.c +libburn/write.c +libburn/sg-linux.c +Cared for some SCSI error conditions which were ignored up to now + +2006.11.07.114514 [357] +cdrskin/cdrskin.c +Made -tao default for single track or stdin, -sao for any other multi-track + +7 Nov 2006 [358] +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Prepared next cdrskin-0.2.5 upload cycle + +7 Nov 2006 [359] +cdrskin/changelog.txt +cdrskin/README +cdrskin/wiki_plain.txt +Updated documentation about TAO + +2006.11.07.152018 [360] +cdrskin/cdrskin.c +cdrskin/changelog.txt +Updated documentation about TAO + +------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.07.152018 +* Enabled TAO + + +2006.11.08.165648 [361] +cdrskin/cdrfifo.h +cdrskin/cdrfifo.c +cdrskin/cdrskin.c +Avoided error message and nonzero exit with trailing trash on .wav + +8 Nov 2006 [362] +cdrskin/cdrskin_eng.html +Mentioned bug fix about trailing trash + +2006.11.08.172918 [363] +libburn/write.c +Made track write counter of SAO count rather too much than too few bytes + +8 Nov 2006 [364] +cdrskin/changelog.txt +Prepared next cdrskin-0.2.5 upload cycle + +------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.08.181016 +* Bug fixed: Trailing trash appended to .wav files caused error message + and, if exceeding fifo size, could even stall a burn. + + +2006.11.09.112832 [365] +cdrskin/cdrskin.c +Check desired write and block type with burn_drive_info.*_block_types + +2006.11.09.113729 [366] +libburn/sg-linux.c +Silenced SCSI error (debugging) messages about missing media + +2006.11.09.151431 [367] +cdrskin/cdrskin.c +Corrected first speed measurement report in TAO mode (which was random) + +2006.11.09.193030 [368] +libburn/libburn.h +libburn/write.c +cdrskin/cdrskin.c +Experimentally enabled burning to BURN_DISC_APPENDABLE (tested with TAO only) + +2006.11.10.093843 [369] +cdrskin/cdrskin.c +doc/comments +Expressing more self-confidence + +2006.11.10.172212 [370] +cdrskin/cdrskin.c +Provisory -msinfo (very verbous on stderr) + +2006.11.10.184047 [371] +cdrskin/cdrskin.c +Made it work with fifos and other non-plain files as track sources + +2006.11.10.185209 [372] +cdrskin/cdrskin.c +Read -msinfo from first track of last session and not from last track + +2006.11.11.122402 [373] +libburn/libburn.h +libburn/transport.h +libburn/mmc.h +libburn/mmc.c +libburn/write.c +libburn/drive.c +libburn/libdax_msgs.h +New API function burn_disc_track_lba_nwa() + +2006.11.11.122907 [374] [375] +cdrskin/cdrskin.c +Implemented not so provisory -msinfo + +2006.11.11.124020 [376] [377] +cdrskin/cdrskin.c +Reacted on some warnings of gcc -O2 + +2006.11.11.134752 [378] +cdrskin/cdrskin.c +Implemented handling of unsuitable disk states with -msinfo + +2006.11.11.152037 [379] +cdrskin/cdrskin.c +Demanded (for now) -tao for writing to appendable CDs + +2006.11.11.152748 [380] +cdrskin/cdrskin.c +Generally enabled -multi, -msinfo and writing to appendable CDs in TAO mode + +11 Nov 2006 [381] +cdrskin/changelog.txt +Prepared next cdrskin-0.2.5 cycle + +11 Nov 2006 [382] +cdrskin/cdrskin_eng.html +cdrskin/README +Updated docs about multi-session + +------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.11.163625 +* Multi-session CDs (for now restricted to write mode TAO), -multi, -msinfo +* Bug fixed: False speed with first pacifier cycle. Potential SIGFPE by NaN. + + +2006.11.12.085808 [383] +libburn/mmc.c +Made speed 0 in burn_drive_set_speed() really maximum speed (i.e. FFFFh) + +2006.11.12.113629 [384] +cdrskin/cdrskin.c +Made SAO preferrable default write mode, kept TAO as default where needed + +2006.11.12.152723 [385] +libburn/mmc.c +libburn/libdax_msgs.h +Reacted on error condition during write operation + +2006.11.12.185342 [386] +cdrskin/cdrskin.c +Made -toc on blank CD exit with value 0 (rather than 7 with no media) + +13 Nov 2006 [390] +README +cdrskin/add_ts_changes_to_libburn_0_2_5 +cdrskin/cdrskin_eng.html +Changed SVN URLs to reflect new structure + +13 Nov 2006 [391] +README +Refered to both libburn and libisofs in SVN description, updated history + +------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.13.122418 +* Enabled named pipes and other non-plain file types as track sources + (actually already in previous cycle) +* Bug fixed: Default speed was not highest possible but announced as "MAX" +* SAO is preferred default write mode, TAO is default when needed + + +2006.11.13.122418 [392] +cdrskin/changelog.txt +Next cdrskin-0.2.5 cycle + +13 Nov 2006 [393] +cdrskin/cdrskin_eng.html +Adjusted description of write mode default + +13 Nov 2006 [394] +Makefile.am +configure.ac +Removed references to libisofs + +14 Nov 2006 [399] +README +Changed references to libisofs + +14 Nov 2006 [400] +cdrskin/README +Corrected a typo reported by George Danchev + +2006.11.14.104023 [401] +cdrskin/cdrskin.c +Implemented try to find on restricted drives a suitable write mode as default + +14 Nov 2006 [402] +libburn/libburn.h +Fixed a wrong name in API description + +15 Nov 2006 [403] +libburn/write.h +libburn/write.c +Prepared tests for eventual drive which might support SAO to appendable CD + +15 Nov 2006 [404] +libburn/sg-linux.c +Enhanced optional SCSI command logging + +2006.11.15.091329 [405] +cdrskin/cdrskin.c +Adjusted some texts to new multi-session situation + +2006.11.15.170927 [406] +libburn/sg.h +libburn/sg-linux.c +libburn/sg-freebsd.c ++ libburn/sg-freebsd-port.c +libburn/spc.h +libburn/spc.c +libburn/drive.c +Made portability clarifications + +15 Nov 2006 [407] +libburn/sg-linux.c +libburn/sg-freebsd-port.c +Narrowed system specific part of enumerate_common() + +2006.11.16.111656 [408] [409] +Makefile.am ++ libburn/os.h ++ libburn/os-linux.h ++ libburn/os-freebsd.h +libburn/sg.h +libburn/sg.c +libburn/transport.h +libburn/cleanup.c +libburn/sg-linux.c +libburn/sg-freebsd-port.c +cdrskin/cleanup.c +cdrskin/compile_cdrskin.sh +Made consolidaed operating system adapters for ease of porting + +2006.11.16.133951 [410] +libburn/sg-linux.c +libburn/sg-freebsd-port.c +Polished porting hints and self-compliance to newly established specs + +16 Nov 2006 [411] +libburn/sg-linux.c +libburn/sg-freebsd-port.c +Polished porting hints + +16 Nov 2006 [412] +test/libburner.c +Obsoleted --stdin_size by automatic TAO, cared for non-plain track files + +16 Nov 2006 [413] +doc/comments +Updated help text of libburner + +17 Nov 2006 [414] +Makefile.am ++ test/telltoc.c +Created info retrieval companion for libburner + +17 Nov 2006 [415] +test/libburner.c +Enabled multi-session with libburner + +17 Nov 2006 [416] +Makefile.am +- test/toc.c +Obsoleted old test program + +17 Nov 2006 [417] [418] +test/telltoc.c +Shifted a comma + +2006.11.18.194606 [419] +libburn/transport.h +libburn/sbc.h +libburn/sbc.c +libburn/drive.c +Test wether SCSI 1Bh START UNIT would be helpful with ticket 90 + +2006.11.19.114413 [420] +libburn/libburn.h +libburn/mmc.c +libburn/sector.c +libburn/write.c +cdrskin/cdrskin.c +Implemented libburn builtin fine granulated drive buffer min-fill recording + +2006.11.19.162900 [421] +cdrskin/cdrskin.c +Avoided self contradiction with "Min drive buffer fill" + +2006.11.19.163646 [422] +cdrskin/cdrskin.c +Fixed missing brackets from revison 421 + +2006.11.20.090207 [423] +libburn/sector.c +Silenced compiler warnings + +2006.11.20.090503 [424] +libburn/drive.c +Removed a redundant d->start_unit() of revision 419 + + +------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.20.092808 +* libisofs unbundled from libburn+cdrskin +* Hints for porting to other operating systems are now in sg-*.c + +20 Nov 2006 [425] +cdrskin/changelog.txt +Next cdrskin-0.2.5 cycle + +2006.11.20.132717 [426] +cdrskin/cdrskin.c +cdrskin/cdrfifo.h +cdrskin/cdrfifo.c +cdrskin/wiki_plain.txt +Implemented new option fifo_start_at= + +20 Nov 2006 [427] +cdrskin/wiki_plain.txt +Updated in respect to multi-session + +2006.11.22.122228 [428] +libburn/spc.h +libburn/spc.c +Coordinated scsi_notify_error() and scsi_error() + + +------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.22.142517 + +23 Nov 2006 [433] +cdrskin/changelog.txt +Last cdrskin-0.2.5 cycle + +23 Nov 2006 [434] +- test/burn.c +- test/master.c +- test/tree.py +- test/tree.pyc +- test/rip.c +Removed obsolete test programs + +2006.11.23.102340 [435] +Makefile.am +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin.c +cdrskin/cdrskin_eng.html +cdrskin/README +cdrskin/wiki_plain.txt ++ cdrskin/add_ts_changes_to_libburn_0_2_6 ++ cdrskin/add_ts_changes_to_libburn_0_2_7 +- cdrskin/add_ts_changes_to_libburn_0_2_4 +- cdrskin/add_ts_changes_to_libburn_0_2_5 +Version leap to 0.2.6 + +24 Nov 2006 +Published cdrskin-0.2.6.pl01 on cdrskin home pages + +------------------------------- cycle - cdrskin-0.2.6.pl01 - 2006.11.23.114611 + + +2006.11.24.121745 [437] +Makefile.am +configure.ac +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin.c +cdrskin/README +Version leap to 0.2.7 + +24 Nov 2006 [438] +doc/comments +Mentioned telltoc and dewav as helpers of libburner + + +------------------------------------ cycle - cdrskin-0.2.7 - 2006.11.24.125445 + +24 Nov 2006 [439] +cdrskin/changelog.txt +First cdrskin-0.2.7 cycle + +24 Nov 2006 [440] +doc/comments +Updated libburner helptext copy + +24 Nov 2006 [441] +doc/comments +Made our claim of burning CD more general + +2006.11.25.104047 [442] +cdrskin/cdrskin.c +Disabled old workaround for ticket 8, burn_disc_read_atip() fixed the problem + +25 Nov 2006 [443] +libburn/drive.h +libburn/drive.c +libburn/write.c +test/libburner.c +Ticket 91: update media state model after content change + +25 Nov 2006 [444] +test/libburner.c +test/telltoc.c +Made use of libburn device address translation (/dev/sr0, /dev/cdrom, ...) + +25 Nov 2006 [445] +test/libburner.c +Corrected two minor bugs + +2006.11.25.170456 [446] +libburn/cleanup.c +cdrskin/cleanup.c +Trying to keep signal handler from repeating messages + +2006.11.25.182841 [447] +libburn/write.c +libburn/drive.c +Prevented premature BURN_DRIVE_IDLE introduced with revision 443 + +2006.11.25.210321 [448] +cdrskin/cdrskin.c +Enabled options -vv, -vvv and -vvvv + +28 Nov 2006 [449] +README +Mentioned renaming of umbrella project to libburnia + +2006.11.29.205136 [450] +cdrskin/cdrskin.c +Added preliminary support for new cdrecord 1000+ = ATA busses (input only) + +2006.12.01.213845 [451] +libburn/transport.h +libburn/libdax_msgs.h +libburn/mmc.c +Ticket 21: media type via 46h GET CONFIGURATION , Current Profile + +2006.12.02.111701 [452] +libburn/libburn.h +libburn/mmc.c +libburn/drive.c +cdrskin/cdrskin.c +New API function to obtain media type: burn_disc_get_profile() + +2006.12.02.130631 [453] +libburn/mmc.c +Correction for drives which return empty tray as profile 0x00 rather than error + + +------------------------------------ cycle - cdrskin-0.2.7 - 2006.12.02.151257 +* Improved recognition of unsuitable media types + + +2 Dec 2006 [454] +cdrskin/changelog.txt +cdrskin/cdrskin_eng.html +Next cdrskin-0.2.7 cycle + +2006.12.02.201405 [456] +libburn/drive.c +libburn/mmc.c +Avoided unsuitable media complaint on burn_drive_grab() with load==0 + +2006.12.02.201529 [457] +cdrskin/cdrskin.c +Testing wether the after-grab status waiting loops are necessary + +3 Dec 2006 [461] +cdrskin/doener_150x200_tr.gif +cdrskin/doener_150x200_tr_octx.gif +cdrskin/cdrskin_eng.html +cdrskin is declared honoray Doenerware (burp) + +3 Dec 2006 [463] +cdrskin/cdrskin_eng.html +Have wikipedia explain doenerism ("Eat the rich" and so) + +2006.12.03.155703 [464] +README +doc/comments +test/libburner.c +cdrskin/cdrskin_eng.html +cdrskin/README +cdrskin/cdrskin.c +Changed URLs and umbrella names to libburnia + +3 Dec 2006 [465] +cdrskin/wiki_plain.txt +Changed URLs and umbrella names to libburnia + +3 Dec 2006 [466] +cdrskin/wiki_plain.txt +Added Doener logo and link + +3 Dec 2006 [468] +cdrskin/cdrskin_eng.html +cdrskin/add_ts_changes_to_libburn_0_2_7 +Excluded doener gifs from cdrskin tarballs + + +------------------------------------ cycle - cdrskin-0.2.7 - 2006.12.03.204709 + + +2006.12.03.204709 [469] +cdrskin/changelog.txt +Next cdrskin-0.2.7 cycle + +4 Dec 2006 [470] +cdrskin/cdrskin_eng.html +Removed a newline which made the Mozilla family show a "_" + +4 Dec 2006 [471] +test/telltoc.c +Added reporting of current media profile, changed "Media type" to "Media reuse" + +2006.12.09.111108 [475] +cdrskin/cdrskin.c +Replaced setuid blocker by warning. People must know themselves what they do. + + +------------------------------------ cycle - cdrskin-0.2.7 - 2006.12.09.141837 +* Replaced ban of chmod u+s by loud warning + +11 Dec 2006 [484] +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Next cdrskin-0.2.7 cycle + +2006.12.11.095230 [485] +cdrskin/cdrskin_timestamp.h +Belated timestamp for changes in cdrskin + +11 Dec 2006 [486] +Makefile.am +Unified mix of tab an blanks which looks ugly in diff + +2006.12.11.100021 [487] +libburn/sg-linux.c +Prepared experiments for new Linux SCSI adventures + +2006.12.11.101350 [488] +cdrskin/README +cdrskin/cdrskin.c +cdrskin/wiki_plain.txt +Consequences from newly introduces startup file + +11 Dec 2006 [489] +cdrskin/wiki_plain.txt +Repaired README link and planted helptext links + +2006.12.11.115802 [490] +libburn/sg-linux.c +Silenced a compiler warning. Worked further on /dev/srM test. Not done yet. + +2006.12.11.125222 [491] +libburn/mmc.c +libburn/structure.c +Prevented SIGSEGVs when using -atip with my SCSI CD-ROM (sr,sg: no matter) + +2006.12.11.134452 [492] +libburn/sg-linux.c +Enabled correct SCSI address parameter registration for /dev/srM. + +2006.12.11.145332 [493] +libburn/sg-linux.c +Removed ban on linux_sg_device_family, warning now of linux_sg_accept_any_type + +2006.12.11.161952 [494] +libburn/sg-linux.c +Reacted better on failing ioctl(SG_GET_SCSI_ID) + +2006.12.11.191826 [495] +libburn/sg-linux.c +cdrskin/cdrskin.c +Trying to identfy CD device via ioctl(CDROM_DRIVE_STATUS) + +2006.12.11.215017 [496] +libburn/libburn.h +Appeased doxygen warnings + +2006.12.13.170319 [497] +cdrskin/cdrskin.c +Updated dev=help to versions >= 0.2.4, new option --list_ignored_options + +13 Dec 2006 [498] ++ cdrskin/cdrskin.1 +Detailed man page for cdrskin. Based on work of George Danchev. + +2006.12.13.195441 [499] +Makefile.am +Trying to get new man page into release tarball and installation. + +13 Dec 2006 [500] +cdrskin/README +Took care for man page + +13 Dec 2006 [501] +cdrskin/cdrskin.1 +Clarified track content meaning. Corrected some typos and beauty flaws + +13 Dec 2006 [502] +cdrskin/README +cdrskin/cdrskin_eng.html +Took more care for man page + + +------------------------------------ cycle - cdrskin-0.2.7 - 2006.12.13.221921 +* detailed man page for cdrskin + +13 Dec 2006 [503] +cdrskin/changelog.txt +Next cdrskin-0.2.7 cycle + +13 Dec 2006 [504] +cdrskin/cdrskin.1 +Explained recordable CD media. Removed a typo. + +14 Dec 2006 [505] +cdrskin/cdrskin.1 +Unified some nomenclature. Removed an error with dev_translation= + +14 Dec 2006 [506] +CONTRIBUTORS +Mentioned George Danchev for his merits about cdrskin man page + +2006.12.14.102857 [507] +Makefile.am +cdrskin/cdrskin_eng.html ++ cdrskin/convert_man_to_html.sh +cdrskin/add_ts_changes_to_libburn_0_2_7 +Publishing cdrskin.1 as man_1_cdrskin.html + +14 Dec 2006 [508] +- doc/comments_test_ts +Removed outdated experiment + + +------------------------------------ cycle - cdrskin-0.2.7 - 2006.12.14.111807 + +2006.12.14.140001 ++ cdrskin-0.2.6/cdrskin/cdrskin.1 +cdrskin-0.2.6/cdrskin/README +cdrskin-0.2.6/cdrskin/cdrskin_eng.html +cdrskin-0.2.6/cdrskin/changelog.txt +Backported manpage to cdrskin-0.2.6 -> pl02 and libburn-0.2.6 -> 0.2.6.2 + + +14 Dec 2006 [509] +cdrskin/cdrskin_eng.html +Re-arranged examples of documentation commands + +14 Dec 2006 [510] +cdrskin/cdrskin.1 +Corrected alphabetical sorting error + +14 Dec 2006 [511] +cdrskin/changelog.txt +Next cdrskin-0.2.7 cycle + +14 Dec 2006 [512] +cdrskin/convert_man_to_html.sh +Added some meta info to the document header, changed title to "man 1 cdrskin" + +14 Dec 2006 [513] +cdrskin/wiki_plain.txt +Adapted to existence of man page + +15 Dec 2006 [514] +cdrskin/cdrskin.1 +Clarified drives and their addresses + +15 Dec 2006 [515] +cdrskin/convert_man_to_html.sh +Adapted to changes in cdrskin.1 + +15 Dec 2006 [516] +cdrskin/cdrskin.1 +Introduced term "session" + +2006.12.16.090001 [518] +cdrskin-0.2.6/Makefile.am ++ cdrskin-0.2.6/cdrskin/cdrskin.1 +cdrskin-0.2.6/cdrskin/README +cdrskin-0.2.6/cdrskin/cdrskin_eng.html +cdrskin-0.2.6/cdrskin/changelog.txt +cdrskin-0.2.6/cdrskin/cdrskin_timestamp.h +Backported manpage to libburn-0.2.6.1 -> libburn-0.2.6.2 + +18 Dec 2006 [519] +cdrskin/cdrskin.1 +Made several minor clarifications + +2006.12.18.123242 [520] +libburn/mmc.c +Noted some insight about necessity of OPC gained from growisofs_mmc.cpp + +2006.12.20.111932 [522] +libburn/transport.h +libburn/options.h +libburn/options.c +libburn/write.c +libburn/sector.c +libburn/mmc.c +libburn/libdax_msgs.h +Prepared experiments for writing to DVD (most easy: DVD+RW) + +2006.12.20.142301 [523] +libburn/write.c +libburn/libdax_msgs.h +Refuse to burn audio tracks to DVD + +2006.12.20.145222 [524] +libburn/drive.c +libburn/mmc.c +Avoid undefined 43h commands (TOC/ATIP) with non-CD + +2006.12.20.170502 [525] +libburn/mmc.c +Corrected DVD+RW track number and nwa with 52h READ TRACK INFORMATION + +2006.12.20.171230 [526] +libburn/mmc.c +Corrected bug reported by gcc -O2 + +2006.12.20.174016 [527] +yylibburn/write.c +Corrected bug reported by gcc -O2 + +2006.12.20.180214 [528] +cdrskin/cdrskin.c +With -atip report "booktype" for DVD media and no questionable ATIP info + +2006.12.20.195421 [529] +cdrskin/cdrskin.c +With -atip on DVD report no RAW/RAW96R among "Supported modes" + +2006.12.21.122301 [530] +libburn/write.c +Removed some debugging messages + +2006.12.21.122533 [531] +cdrskin/cdrskin.c +DVD speed reporting (and setting for drives which obey BBh SET CD SPEED) + +2006.12.21.200556 [532] +libburn/mmc.c +libburn/libdax_msgs.h +DVD speed setting via B6h SET STREAMING, DVD+RW now enabled in vanilla build + +2006.12.21.205702 [533] +Disallowed multi flag with DVD+RW (nurses wrong hopes for now) +libburn/write.c + +21 Dec 2006 [534] +test/libburner.c +Report media type, avoid self contradicting messages with DVD+RW --multi + +2006.12.21.214641 [535] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/cdrskin_eng.html +doc/comments +Some bragging about DVD+RW + + +------------------------------------ cycle - cdrskin-0.2.7 - 2006.12.22.120854 +* Burning of DVD+RW media as single-track TAO-like initial session + + +2006.12.23.102056 [536] +libburn/libburn.h +libburn/options.h +libburn/options.c +libburn/write.c +Adjustable write position for DVD+RW: burn_write_opts_set_start_byte() + +2006.12.23.102201 [537] +cdrskin/cdrskin.c +New option write_start_address= + +23 Dec 2006 [538] +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/convert_man_to_html.sh +Added more info about DVD+RW + +2006.12.23.141315 [539] +libburn/libburn.h +libburn/drive.c +libburn/write.c +cdrskin/cdrskin.c +New API function to inquire burn success (and avoid confusing messages) + +2006.12.23.184353 [540] +libburn/libburn.h +libburn/write.c +cdrskin/cdrskin.c +More appropriate drive status during format and close of DVD+RW + +2006.12.24.140904 [547] +cdrskin/cdrskin.c +libburn/write.c +Fixed a bug with speed measurement at end of DVD+RW burning + +2006.12.24.142512 [548] +libburn/options.h +libburn/options.c +libburn/write.c +Made DVD 32k end padding controllable + +2006.12.24.154455 [549] +cdrskin/cdrskin.c +Made DVD ignore CD write modes of drive. Made TAO default for DVD+RW. + +2006.12.24.182307 [550] +libburn/write.c +Bugfix with DVD+RW : progress indicators were initialized too late + +2006.12.24.182410 [551] +libburn/options.c +Bugfix after changes for DVD+RW: start_byte was initialized 0, but must be -1 + +24 Dec 2006 [552] +test/libburner.c +test/telltoc.c +Removed unnecessary waiting loops after grab, mentioned DVD+RW + +2006.12.25.113534 [553] +libburn/libburn.h +libburn/spc.c +libburn/drive.c +Ticket 93: write speeds from mode page 2Ah descriptors + +25 Dec 2006 [554] +test/telltoc.c +Adjusted to new (still immature) speed info semantics + +2006.12.25.185747 [555] +cdrskin/cdrskin.c +Corrected CD speed conversion factor to 2352*75/1000 = 176.4 kB/s + +2006.12.25.190055 [556] +libburn/spc.c +libburn/mmc.h +libburn/mmc.c +libburn/write.c +libburn/libdax_msgs.h +Ticket 93: write speeds from ACh GET PERFORMANCE, Type 03h, DVD media capacity + +25 Dec 2006 [557] +test/telltoc.c +Adjusted to new (more mature) speed info semantics + +2006.12.25.190811 [558] +libburn/transport.h +Completed revision 556 + +2006.12.26.170459 [559] +libburn/libburn.h +libburn/transport.h +libburn/drive.h +libburn/drive.c +libburn/mmc.c +libburn/spc.c +New API calls burn_drive_get_speedlist() , burn_drive_free_speedlist() + +26 Dec 2006 [560] +test/telltoc.c +Enabled report of speed descriptor list + +2006.12.26.184321 [561] +libburn/libburn.h +test/telltoc.c +Minor corrections to revisions 559 and 560 + +2006.12.27.125948 [562] +libburn/drive.c +Disabled obsolete drive-media-state workaround. (Spinoff of ticket 93 :)) + +2006.12.27.130125 [563] +libburn/mmc.c +Corrected kB conversion factor to 176.4 with ATIP speed codes + +2006.12.27.130239 [564] +cdrskin/cdrskin.c +Defended against a race condition on SuSE 9.3 after -atip (hald et.al. ?) + +2006.12.27.132653 [565] +libburn/mmc.c +Avoided self contradicting result of ATIP speed inquiry + +2006.12.27.162846 [566] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Emulated wodim option -msifile=path + +2006.12.27.213729 [567] +cdrskin/cdrskin.c +Followed revision 644 of wodim (msifile output without newline) + +27 Dec 2006 [568] +cdrskin/cdrskin_eng.html +cdrskin/cdrskin.1 +Updated about msifile=path + + +------------------------------------ cycle - cdrskin-0.2.7 - 2006.12.27.214424 +* New option -msifile=path from cdrskit/wodim + + +2006.12.29.143734 [569] +libburn/mmc.c +Corrected DVD-RW sequential profile name + +2006.12.30.001343 [570] +libburn/mmc.h +libburn/mmc.c +libburn/spc.c +libburn/write.c +libburn/libdax_msgs.h +cdrskin/cdrskin.c +Prepared support for DVD-RW in mode Restricted Overwrite + +29 Dec 2006 [571] +cdrskin/changelog.txt +Next cdrskin-0.2.7 cycle + +2007.01.01.170824 [572] +libburn/libburn.h +libburn/transport.h +libburn/spc.c +libburn/mmc.h +libburn/mmc.c +libburn/async.c +libburn/drive.h +libburn/drive.c +libburn/write.c +libburn/libdax_msgs.h +Prepared formatting of DVD-RW + +2007.01.01.171725 [573] +cdrskin/cdrskin.c +cdrskin/README +cdrskin/cdrskin.1 +cdrskin/wiki_plain.txt +cdrskin/cdrskin_eng.html +Made use of formatting of DVD-RW + +2 Jan 2007 [574] +cdrskin/wiki_plain.txt +Added links to cdrskin help texts + +2007.01.02.090530 [575] +COPYRIGHT +README +cdrskin/cdrskin.c +Greeting the new year + + +------------------------------------ cycle - cdrskin-0.2.7 - 2007.01.02.101027 +* Formatting and then burning to DVD-RW like to DVD+RW + +2 Jan 2006 [576] +cdrskin/changelog.txt +Next cdrskin-0.2.7 cycle + +2 Jan 2006 [577] +cdrskin/wiki_plain.txt +cdrskin/README +Some DVD-RW statements + +2 Jan 2006 [578] +test/libburner.c +doc/comments +Made use of formatting of DVD-RW + +2 Jan 2006 [579] +cdrskin/cdrskin.1 +Some DVD-RW statements + +2007.01.03.163026 [583] +libburn/mmc.c +Made formatting report progress (as good as the drive does) + +2007.01.03.164716 [584] +libburn/async.c +libburn/drive.c +Moved blanking suitability test before eventual spwaning of threads + +2007.01.05.125715 [587] +libburn/mmc.c +libburn/write.c +Comments and name changes from new findings out of reading MMC-5 + +2007.01.06.120551 [591] +libburn/libburn.h +libburn/transport.h +libburn/mmc.h +libburn/mmc.c +libburn/async.c +libburn/drive.h +libburn/drive.c +libburn/write.c +cdrskin/cdrskin.c +test/libburner.c +New formatting parameter "size". Sorry for changing API. Function is a week old. + +8 Jan 2007 [592] +libburn/os-linux.h +libburn/os-freebsd.h +Added note that buffer may not be smaller than 32768 + +2007.01.08.104222 [593] +libburn/libburn.h +libburn/drive.c +libburn/write.c +libburn/mmc.c +Introduced size parameter to DVD-RW formatting plus writing of zeros. + +2007.01.08.104407 [594] +cdrskin/cdrskin.1 +cdrskin/cdrskin.c +Using 128 MB of size plus writing of zeros with blank=format_overwrite + +2007.01.09.140302 [595] +libburn/drive.c +Fixed a SIGFPE with formatting via libburner + +9 Jan 2007 [596] +libburn/libburn.h +libburn/transport.h +libburn/libdax_msgs.h +libburn/drive.c +libburn/write.c +libburn/mmc.c +Enhanced DVD-RW formatting + +2007.01.09.211152 [597] +cdrskin/README +cdrskin/cdrskin.1 +cdrskin/cdrskin.c +Now available: "quickest" and "full" formatting for DVD-RW + + +------------------------------------ cycle - cdrskin-0.2.7 - 2007.01.10.101406 + + +10 Jan 2007 [598] +cdrskin/cdrskin_eng.html +Updated size estimation of development downloads + +10 Jan 2007 [599] +cdrskin/changelog.txt +Next cdrskin-0.2.7 cycle + + [] +libburn/libburn.h +libburn/mmc.c +libburn/drive.c +libburn/async.c +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Option -force enables re-formatting + + [] +libburn/mmc.c +Switched full formatting from type 10h to 00h which includes lead-out + + [] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +Removing writing of dummy data with blank=format_overwrite_full + + +Format DVD+RW ? + +Format and use DVD-RAM ? + +Load format parameters into struct burn_drive and offer API. +With full formatting prefer format 10h over 13h or 15h. + +Learn about multi-track with DVD-RW Restricted Overwrite. + +How to handle finalizing ? + +Check wether current_profile is kept up to date with each possible media change + +Make drive_info reflect DVD capabilities +Make proper use of reflected capabilities (e.g. in burn_drive_inquire_media()) + +Rectify mmc_read_atip speed interpretation. 12x media are reported as 10x. I never heard of 6x media. + + +Sequentially check option list for DVD compliance + + +------------------------------------ cycle - cdrskin-0.2.7 - + + + +----------------------------------------- long term intentions: + + [] +Ticket 90: failure on USB + + [] +-reset: ioctl(fd,CDROMRESET) ioctl(fd,SG_SCSI_RESET,SG_SCSI_RESET_DEVICE) +http://developer.osdl.org/dev/robustmutexes/src/fusyn.hg/Documentation/ioctl/cdrom.txt + + + [] +Open O_EXCL all devices in address resolution chain +(Might help with non-standard hdX device siblings) + + [] +Convert libburn_experimental: into LIBDAX_MSGS_SEV_DEBUG +Convert burn_print() into libdax_msgs_submit() + + [] +Test unlocking of all drives by burn_drive_info_free() +Test unlocking of single drive by burn_drive_grab(), burn_drive_release() + + [] +Clear outdated persistent read buffer after small CD image was read (ticket 57) + + [] +Enable multi-session for write modes other than TAO + + diff --git a/libburn/tags/ZeroThreeZero/cdrskin/cleanup.c b/libburn/tags/ZeroThreeZero/cdrskin/cleanup.c new file mode 100644 index 00000000..fc923830 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/cleanup.c @@ -0,0 +1,215 @@ +/* + cleanup.c , Copyright 2006 Thomas Schmitt + + A signal handler which cleans up an application and exits. + + Provided under GPL license within GPL projects, BSD license elsewise. +*/ + +/* + cc -g -o cleanup -DCleanup_standalonE cleanup.c +*/ + +#include +#include +#include +#include +#include +#include + +#include +typedef void (*sighandler_t)(int); + + +#include "cleanup.h" + + +#ifndef Cleanup_has_no_libburn_os_H + + +#include "../libburn/os.h" + +/* see os.h for name of particular os-*.h where this is defined */ +static int signal_list[]= { BURN_OS_SIGNAL_MACRO_LIST , -1}; +static char *signal_name_list[]= { BURN_OS_SIGNAL_NAME_LIST , "@"}; +static int signal_list_count= BURN_OS_SIGNAL_COUNT; +static int non_signal_list[]= { BURN_OS_NON_SIGNAL_MACRO_LIST, -1}; +static int non_signal_list_count= BURN_OS_NON_SIGNAL_COUNT; + + +#else /* ! Cleanup_has_no_libburn_os_H */ + + +/* Outdated. Linux only. For backward compatibility with pre-libburn-0.2.3 */ + +/* Signals to be caught */ +static int signal_list[]= { + SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, + SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, + SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, + SIGTTOU, + SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP, + SIGVTALRM, SIGXCPU, SIGXFSZ, -1 +}; +static char *signal_name_list[]= { + "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", + "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", + "SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", + "SIGTTOU", + "SIGBUS", "SIGPOLL", "SIGPROF", "SIGSYS", "SIGTRAP", + "SIGVTALRM", "SIGXCPU", "SIGXFSZ", "@" +}; +static int signal_list_count= 24; + +/* Signals not to be caught */ +static int non_signal_list[]= { + SIGKILL, SIGCHLD, SIGSTOP, SIGURG, SIGWINCH, -1 +}; +static int non_signal_list_count= 5; + + +#endif /* Cleanup_has_no_libburn_os_H */ + + + +/* run time dynamic part */ +static char cleanup_msg[4096]= {""}; +static int cleanup_exiting= 0; +static int cleanup_has_reported= -1234567890; + +static void *cleanup_app_handle= NULL; +static Cleanup_app_handler_T cleanup_app_handler= NULL; +static int cleanup_perform_app_handler_first= 0; + + +static int Cleanup_handler_exit(int exit_value, int signum, int flag) +{ + int ret; + + if(cleanup_msg[0]!=0 && cleanup_has_reported!=signum) { + fprintf(stderr,"\n%s\n",cleanup_msg); + cleanup_has_reported= signum; + } + if(cleanup_perform_app_handler_first) + if(cleanup_app_handler!=NULL) { + ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0); + if(ret==2 || ret==-2) + return(2); + } + if(cleanup_exiting) { + fprintf(stderr,"cleanup: ABORT : repeat by pid=%d, signum=%d\n", + getpid(),signum); + return(0); + } + cleanup_exiting= 1; + alarm(0); + if(!cleanup_perform_app_handler_first) + if(cleanup_app_handler!=NULL) { + ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0); + if(ret==2 || ret==-2) + return(2); + } + exit(exit_value); +} + + +static void Cleanup_handler_generic(int signum) +{ + int i; + + sprintf(cleanup_msg,"UNIX-SIGNAL caught: %d errno= %d",signum,errno); + for(i= 0; imax_sig) + max_sig= signal_list[i]; + if(signal_list[i]=non_signal_list_count) { + if(i==SIGABRT && (flag&8)) + signal(i,Cleanup_handler_generic); + else + signal(i,sig_handler); + } + } + return(1); +} + + +#ifdef Cleanup_standalonE + +struct Demo_apP { + char *msg; +}; + + +int Demo_app_handler(struct Demo_apP *demoapp, int signum, int flag) +{ + printf("Handling exit of demo application on signal %d. msg=\"%s\"\n", + signum,demoapp->msg); + return(1); +} + + +main() +{ + struct Demo_apP demoapp; + + demoapp.msg= "Good Bye"; + Cleanup_set_handlers(&demoapp,(Cleanup_app_handler_T) Demo_app_handler,0); + + if(1) { /* change to 0 in order to wait for external signals */ + char *cpt= NULL,c; + printf("Intentionally provoking SIGSEGV ...\n"); + c= *cpt; + } else { + printf("killme: %d\n",getpid()); + sleep(3600); + } + + Cleanup_set_handlers(NULL,NULL,1); + exit(0); +} + +#endif /* Cleanup_standalonE */ diff --git a/libburn/tags/ZeroThreeZero/cdrskin/cleanup.h b/libburn/tags/ZeroThreeZero/cdrskin/cleanup.h new file mode 100644 index 00000000..a9d35519 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/cleanup.h @@ -0,0 +1,34 @@ +/* + cleanup.c , Copyright 2006 Thomas Schmitt + + A signal handler which cleans up an application and exits. + + Provided under GPL license within GPL projects, BSD license elsewise. +*/ + +#ifndef Cleanup_includeD +#define Cleanup_includeD 1 + + +/** Layout of an application provided cleanup function using an application + provided handle as first argument and the signal number as second + argument. The third argument is a flag bit field with no defined bits yet. + If the handler returns 2 or -2 then it has delegated exit() to some other + instance and the Cleanup handler shall return rather than exit. +*/ +typedef int (*Cleanup_app_handler_T)(void *, int, int); + + +/** Establish exiting signal handlers on (hopefully) all signals that are + not ignored by default or non-catchable. + @param handle Opaque object which knows how to cleanup application + @param handler Function which uses handle to perform application cleanup + @param flag Control Bitfield + bit0= reset to default signal handling +*/ +int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler, + int flag); + + +#endif /* ! Cleanup_includeD */ + diff --git a/libburn/tags/ZeroThreeZero/cdrskin/compile_cdrskin.sh b/libburn/tags/ZeroThreeZero/cdrskin/compile_cdrskin.sh new file mode 100755 index 00000000..fa4dc357 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/compile_cdrskin.sh @@ -0,0 +1,219 @@ +#!/bin/sh + +# compile_cdrskin.sh +# Copyright 2005 - 2006 Thomas Schmitt, scdbackup@gmx.net, GPL +# to be executed within ./libburn-* resp ./cdrskin-* + +debug_opts= +def_opts= +largefile_opts="-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1" +libvers="-DCdrskin_libburn_0_2_7" +cleanup_src_or_obj="libburn/cleanup.o" +libdax_msgs_o="libburn/libdax_msgs.o" +libdax_audioxtr_o="libburn/libdax_audioxtr.o" +do_strip=0 +static_opts= +warn_opts="-Wall" +fifo_source="cdrskin/cdrfifo.c" +compile_cdrskin=1 +compile_cdrfifo=0 +compile_dewav=0 + +for i in "$@" +do + if test "$i" = "-compile_cdrfifo" + then + compile_cdrfifo=1 + elif test "$i" = "-compile_dewav" + then + compile_dewav=1 + elif test "$i" = "-cvs_A60220" + then + libvers="-DCdrskin_libburn_cvs_A60220_tS" + libdax_audioxtr_o= + libdax_msgs_o="libburn/message.o" + cleanup_src_or_obj="-DCleanup_has_no_libburn_os_H cdrskin/cleanup.c" + elif test "$i" = "-libburn_0_2_6" + then + libvers="-DCdrskin_libburn_0_2_6" + libdax_audioxtr_o="libburn/libdax_audioxtr.o" + libdax_msgs_o="libburn/libdax_msgs.o" + cleanup_src_or_obj="libburn/cleanup.o" + elif test "$i" = "-libburn_svn" + then + libvers="-DCdrskin_libburn_0_2_7" + libdax_audioxtr_o="libburn/libdax_audioxtr.o" + libdax_msgs_o="libburn/libdax_msgs.o" + cleanup_src_or_obj="libburn/cleanup.o" + elif test "$i" = "-newapi" -o "$i" = "-experimental" + then + def_opts="$def_opts -DCdrskin_new_api_tesT" + elif test "$i" = "-oldfashioned" + then + def_opts="$def_opts -DCdrskin_oldfashioned_api_usE" + cleanup_src_or_obj="-DCleanup_has_no_libburn_os_H cdrskin/cleanup.c" + elif test "$i" = "-no_largefile" + then + largefile_opts= + elif test "$i" = "-do_not_compile_cdrskin" + then + compile_cdrskin=0 + elif test "$i" = "-do_diet" + then + fifo_source= + def_opts="$def_opts -DCdrskin_extra_leaN" + warn_opts= + elif test "$i" = "-do_strip" + then + do_strip=1 + elif test "$i" = "-g" + then + debug_opts="$debug_opts -g" + elif test "$i" = "-O2" + then + debug_opts="$debug_opts -O2" + elif test "$i" = "-help" -o "$i" = "--help" -o "$i" = "-h" + then + echo "cdrskin/compile_cdrskin.sh : to be executed within top level directory" + echo "Options:" + echo " -compile_cdrfifo compile program cdrskin/cdrfifo." + echo " -compile_dewav compile program test/dewav without libburn." + echo " -cvs_A60220 set macro to match libburn-CVS of 20 Feb 2006." + echo " -libburn_0_2_6 set macro to match libburn-0.2.6." + echo " -libburn_svn set macro to match current libburn-SVN." + echo " -no_largefile do not use 64 bit off_t (must match libburn)." + echo " -do_not_compile_cdrskin omit compilation of cdrskin/cdrskin." + echo " -experimental use newly introduced libburn features." + echo " -oldfashioned use pre-0.2.2 libburn features only." + echo " -do_diet produce capability reduced lean version." + echo " -do_strip apply program strip to compiled programs." + echo " -g compile with cc option -g." + echo " -O2 compile with cc option -O2." + echo " -static compile with cc option -static." + exit 0 + elif test "$i" = "-static" + then + static_opts="-static" + fi +done + + +timestamp="$(date -u '+%Y.%m.%d.%H%M%S')" +echo "Version timestamp : $(sed -e 's/#define Cdrskin_timestamP "//' -e 's/"$//' cdrskin/cdrskin_timestamp.h)" +echo "Build timestamp : $timestamp" + +if test "$compile_cdrskin" +then + echo "compiling program cdrskin/cdrskin.c $static_opts $debug_opts $libvers $def_opts $cleanup_src_or_obj" + cc -I. \ + $warn_opts \ + $static_opts \ + $debug_opts \ + $libvers \ + $largefile_opts \ + $def_opts \ + \ + -DCdrskin_build_timestamP='"'"$timestamp"'"' \ + \ + -o cdrskin/cdrskin \ + \ + cdrskin/cdrskin.c \ + $fifo_source \ + \ + $cleanup_src_or_obj \ + \ + libburn/async.o \ + libburn/debug.o \ + libburn/drive.o \ + libburn/file.o \ + libburn/init.o \ + libburn/options.o \ + libburn/source.o \ + libburn/structure.o \ + \ + libburn/sg.o \ + libburn/write.o \ + $libdax_audioxtr_o \ + $libdax_msgs_o \ + \ + libburn/mmc.o \ + libburn/sbc.o \ + libburn/spc.o \ + libburn/util.o \ + \ + libburn/sector.o \ + libburn/toc.o \ + \ + libburn/crc.o \ + libburn/lec.o \ + \ + -lpthread + + ret=$? + if test "$ret" = 0 + then + dummy=dummy + else + echo >&2 + echo "+++ FATAL : Compilation of cdrskin failed" >&2 + echo >&2 + exit 1 + fi +fi + +if test "$compile_cdrfifo" = 1 +then + echo "compiling program cdrskin/cdrfifo.c $static_opts $debug_opts" + cc $static_opts $debug_opts \ + -DCdrfifo_standalonE \ + -o cdrskin/cdrfifo \ + cdrskin/cdrfifo.c + + ret=$? + if test "$ret" = 0 + then + dummy=dummy + else + echo >&2 + echo "+++ FATAL : Compilation of cdrfifo failed" >&2 + echo >&2 + exit 2 + fi +fi + +if test "$compile_dewav" = 1 +then + echo "compiling program test/dewav.c -DDewav_without_libburN $static_opts $debug_opts" + cc $static_opts $debug_opts \ + -DDewav_without_libburN \ + -o test/dewav \ + test/dewav.c \ + libburn/libdax_audioxtr.o \ + libburn/libdax_msgs.o \ + \ + -lpthread + + ret=$? + if test "$ret" = 0 + then + dummy=dummy + else + echo >&2 + echo "+++ FATAL : Compilation of test/dewav failed" >&2 + echo >&2 + exit 2 + fi +fi + +if test "$do_strip" = 1 +then + echo "stripping result cdrskin/cdrskin" + strip cdrskin/cdrskin + if test "$compile_cdrfifo" = 1 + then + echo "stripping result cdrskin/cdrfifo" + strip cdrskin/cdrfifo + fi +fi + +echo 'done.' diff --git a/libburn/tags/ZeroThreeZero/cdrskin/convert_man_to_html.sh b/libburn/tags/ZeroThreeZero/cdrskin/convert_man_to_html.sh new file mode 100755 index 00000000..333cb098 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/convert_man_to_html.sh @@ -0,0 +1,72 @@ +#!/bin/sh + +# +# convert_man_to_html.sh - ts A61214 +# +# Generates a HTML version of man page cdrskin.1 +# +# To be executed within the libburn toplevel directory (like ./libburn-0.2.7) +# + +# set -x + +man_dir=$(pwd)"/cdrskin" +export MANPATH="$man_dir" +manpage="cdrskin" +raw_html=$(pwd)/"cdrskin/raw_man_1_cdrskin.html" +htmlpage=$(pwd)/"cdrskin/man_1_cdrskin.html" + +if test -r "$manpage" +then + dummy=dummy +else + echo "Cannot find readable man page source $1" >&2 + exit 1 +fi + +if test -e "$man_dir"/man1 +then + dummy=dummy +else + ln -s . "$man_dir"/man1 +fi + +if test "$1" = "-work_as_filter" +then + +# set -x + + sed \ + -e 's///' \ + -e 's///' \ + -e 's/CDRSKIN<\/title>/<title>man 1 cdrskin<\/title>/' \ + -e 's/<h1 align=center>CDRSKIN<\/h1>/<h1 align=center>man 1 cdrskin<\/h1>/' \ + -e 's/<body>/<body BGCOLOR="#F5DEB3" TEXT=#000000 LINK=#0000A0 VLINK=#800000>/' \ + -e 's/<b>Overview of features:<\/b>/\ <BR><b>Overview of features:<\/b>/' \ + -e 's/<b>Track recording model:<\/b>/\ <BR><b>Track recording model:<\/b>/' \ + -e 's/In general there are two types of tracks: data and audio./\ <BR>In general there are two types of tracks: data and audio./' \ + -e 's/While audio tracks just contain a given/\ <BR>While audio tracks just contain a given/' \ + -e 's/<b>Recordable CD Media:<\/b>/\ <BR><b>Recordable CD Media:<\/b>/' \ + -e 's/<b>Recordable DVD Media:<\/b>/\ <BR><b>Recordable DVD Media:<\/b>/' \ + -e 's/<b>Drive preparation and addressing:<\/b>/\ <BR><b>Drive preparation and addressing:<\/b>/' \ + -e 's/If you only got one CD capable drive/\ <BR>If you only got one CD capable drive/' \ + -e 's/^Alphabetical list of options/\ <BR>Alphabetical list of options/' \ + -e 's/and for all others\.<\/td><\/table>/and for all others.<\/td><\/table> <BR><HR><FONT SIZE=-1><CENTER>(HTML generated from '"$manpage"'.1 on '"$(date)"' by '$(basename "$0")' )<\/CENTER><\/FONT>/' \ + -e 's/See section EXAMPLES/See section <A HREF="#EXAMPLES">EXAMPLES<\/A>/' \ + <"$2" >"$htmlpage" + + set +x + + chmod u+rw,go+r,go-w "$htmlpage" + echo "Emerged file:" + ls -l "$htmlpage" + +else + + export BROWSER='cp "%s" '"$raw_html" + man -H "$manpage" + "$0" -work_as_filter "$raw_html" + rm "$raw_html" + rm "$man_dir"/man1 + +fi diff --git a/libburn/tags/ZeroThreeZero/cdrskin/doener_150x200_tr.gif b/libburn/tags/ZeroThreeZero/cdrskin/doener_150x200_tr.gif new file mode 100644 index 0000000000000000000000000000000000000000..07f823ee26c628d44bad63181fd754da6cd1fb99 GIT binary patch literal 34821 zcmd6Q2|Seh_y03vWGrPyvb30?5+YmsX3|)qg-EFETd1^Nb)`|GCMKyQ?IKYrWT~Vq z*~(J1P~A$}NDD=YO#kzlA%qOo_xJaD-Fu%q&vriNywCfb&v~Bba}4I_Po82QisfTp zzGK6sC8Z_A6-Erzp$u1)mz$y?H&$_!mYT{$t#MN)YO88al%8&-F^x`|X+O<CS!&*T zZ9S853s+5AzG0}vO5GV#P1Kauj8}IYIc(mHHF~-;%uL5wuhCp(p|yI|L|faTa}5m^ z&Yx>;I^W8|e2&Q?<0URe=BpO1m~Za7%HC$_@&yJ?_O@%i=DEANtX}E4ey!)me>S+U z{YTt9cz|^T$zv~heT<69UX8WE8Y|ChE{#(9H+$it{R>y^o9%wu({|qquU&3iA|`}* zYn)oSB6!?}6Y>iZ|MA*xzG3HjuT(FmFq_r6n})FBChlU7kB%L*zeFOY6o2ZKQb3_n z)C0pEto1wgtlxLWI_}u|grdbUw^!wC->0Q|UU_)JIHj<0azP8!cC8=EUOg^lwp_BQ z{HfI{Z1eeSk41jIoA<b{KIgh7X5FHERqB&X?xic-pRZs3Y`y1`<+e9z6qeJDR3FxV zTej|HOYRO-^^Y5Ke($)1Sn+cS<H93lgV+n1+x`vOy&@>dEI7{XY_M_U;Z^Z5UUBD5 z5^hUI7Ac10&r7&vc)V=U?Sw(4=QZD45Usf=Q*{nsb4Bsv>FFOYYUhS3{Kwi<w#V~k z+`RXP3~P_he|O0B_0H8FPMFn3S=YtRtvlmfm$>21VT)VEVug3cetjVI?hg4~mdgA5 z$)EGaG*uBf4Oq=<p*vO7yLaXk<*j^wlU`eFP**zVWBK9_MGNciu55U-@#Ay%0M?E@ zLF|3|b{#wrx+8dh;Nh4fQ3pcLUOsRp`NXj!v7x(T52huDoI4qDIN>t;bo8m1Gbssi z=Pz7{OTBz4HX&tyV$SKKnXxf>(U)^BoV}iWDd%eLmBg}>2XBU)&n&u<mz!2veyOk| z?ZNBRC!bChKPzW#FALjwW7mO_m;*O1#bl&kEjbta;WF#diDTtyInPq_i*JQyWv3SB zCValV>r2VGFK@PgsoT-`_CQnZxd%1Ld3m`v?v$0}-zdFXTwYdIaI5&n<JbSacyPPw z^P2~^nqCw%SC=(>EU9|^y1M>bQ~etbhg1Kdnle;c{wqNk!@gsx82llhz=#~|drR|| z4^Pv#eCPCSW3UUx?BT3WLRnZs+XZJzW2B`1-Qtg&)nbB??A~@iKsHC1NR4RSC;b7m zqW+T>bQpcrOaFz|*$mbRZ|L3_Nf>5GYu%--2$GUpeiF3@WBf`UPQNVQ{AITs9>ZJ^ zUMLxDcK&gD9cv7u-fq2erZyF$1^WK)l=wpo-bS=K4#SeV*VHPG6ov`cx5B=~8l&BQ z-#t#cKfilJbsnmC#ZLnDtnoVsFpOc<`SiA17>HsFLQ|U<P0Zw)p0D#OgLgWM)5Ub% z%i0j=T4<TZv*9h<X^wUEqe0U9>04SVuGU->x0wdJzIwQdJ-G%KECFQr!A4{9ow5%U zosD%(EzM=(a%sLTm9ieuaX;%<sP^YyI*G`qgsmoQx=AHbD>6hkgA(^iXE895Boc;c z)I3dJNhHY}cFtg&^L<B?!MqqbSK!JnI8re6NxH8W!6*F7Eh(G`C|G-Y1w+%CNFat& zWB;IJovnI{B8nIooLw({ORW;xVqiEtzOd=b2SE%+Bw$!=rVJ<&Hi#A5u5C#K=MVot z(G&nIKN7+-F#GSE{lZkt55wT7$^C*YhcBE=;z~*&VhkEZM-s##VKfjI@6u8;2IN2@ z(rUrQxPR2QPa(8OR7HM)Uj#lNnAqJ<_f^0Xxx3-Vw5s1QZ)YX)Tm$#5^@1hFtwMmN zZiV46JjhI?!EcG2hQT}m#aNR;(Kt{d7S_PoErsEPtFFTTFhqQtxHz%ZA%i+eUK#Ks zrc}k3g8Dw56pV<~&I;2Y3)$2=;%LC+SvDXmmJz$9*#xMQM#J0WieaSSv2sTQh)-G$ z3t}|<k^vY-qkcF(6ChF8lqaqSc(KW@(dR0<jTgF65Y~g4Io-Q@rxd_e2l!FO?ehmp zx_5KEDJTkmbxX@qjKsC75}rt^Rd}M~2)YWp7x(nah89i{p31=RI800oVuTIo(v1fb zLLCPUgTYwYjflUHp3hy;AHb#xja=b%yg^f85JYtx@dSvbyD1_(TZ%scT@mAh%Mfrl z5Khq_LmF#tY32qAz{zYfl|}?I8!{N9(JuCld-xpKogHw~R+2xiiNh0Q0A;q*!K$~L z$`;UJ7{HgW7r_7HvEkt_K~^h_02<GrT|(q)3^+Nr<r^LR5v46f1b|CFVirGot(QmM z+nWEx9R^QlgMt^>f>pu!cffxnQ3mq*Emx7muzMdU=MWtLLHGl>+MHhrkC95a*1(7w zpLtH(31z>}vOMSIT?bMl2Ezr60g7gTqMLSt-vG;g=QMbH!7)IsM44s|uo^>v0<I2> z#m&0H8sd%vNtx6GZjZo0sr-uAP5$!>&(hfp6lw?RK*wC2M#UKCU?Wo-qax=A>$fGN zcP7;!wc31(M9yyykZuhBV4Kzo6Zj8;iR)a58jtz0W#?l!e5jnOjzQVYU*c@jeVZge zRatOW5>S^$4VNZa6@XhE@4BW2QGhgLqb@ism}(;mxGijo7WZo-UMeQ}4Z^A-?Qc2% zmsP-0m}N$wt1&a)7vc|9W9+eb0YD-+KNuB92cyO<+bW=5Ro#qX3>L>1<<jACM}ZFp zB>_%ofRn}(fD<~HB~=)sg+KU(4S!>ohXFFG5D6pJW>PfK_GAngALJ-(Diaq3o&u{v zss~x=O!!xLAxfo3-7Z5(0cLaCm>VO|e1_@(#>fR<yZw$2TmIy9ox#w<`E`zD8i)XY zwD1K_g&>@QV`B`IOE(Ob2Q30kLaIkYKVdQ&pOF!St6Dr<;eSQm0baN;!7vsCeA`Jl zjIgeM(Nv)EC+WL7W)4`D`<GPp<yJW;6CgmPf%AZiVOT{D7_3qr#=(FccD0=5F>^77 zIET~lFC6OcO~n(bKue%>n{Ulu)FDU+Rdl__ANu8mKT;x&42fpV7(|?e&lx~UPtv!% z8#LSvr4qimTJSRiLpf}9Gc5zM?C+J%B#pDj)$p=akV+5A^g}`Pd6_PTrd2=kA3i1I z^s3Ib3w<LWo=_0RMSG1<+<jHoZ=Z#=?;{awD;l`Q^sQ$V9l95U5Y&|8!HugeRsNs8 ztg^l04WXe%NZf{d`lqt_J)o@ZUVK5aUkCq`|L~Lc{GZAC_q5P<Wlq;DrQh|R{`vF& z=~-=R=&FStfB9e5f4I)_7ZudsYR|7Iqi^}o-yR`iP)Mk{X9|AOo?o5+i%a*ss}J>` zzi4@+7k;Zf-7Bx>5&oXa{%HNDC(Cn%?*SEmYhb^GV)r+#{Mr=)eB?iT8LWpgK+oRN zy@;(!^uBnAmjd+YY`;AH51#dN{?pRbNwDJ@DwirjLHwsZ|0|f-rvKZqfM4^E=z;Sa ze+gi|?EEu=c=^xor9I+LCqbt9<Ms{L{XGu$6F7g+@;~Hh-<Wn_XGh$%tbC^X3ei(H zY0r=G{EzaVyxkoo``)zr46_4q>iAyVp!TRAl+kXLb}jr20S;dW{W$;G^G=d6zD+cp z+Cehg^d(o;bwgBL?)u|`e{G{_brYJ8v|@@fb8bjshkKQYmyKJ79+~`{_H<^5ALsXP zgWvq8&_1h!66Ho49pRNLxzg9{FfKd4`Fm5tPfvBe6?Y=D{z=%^v}bKdal00;EZ(_L z(dUP=e6N38nrsYHz%Nj!=rA6N&U`5Hbo`}F*1wtm%rH9AF2>PF`p(kJ?flYos;czm z2g)_hDz0R4lyn}4?S6V{ZTon-7rGr(ZNup2FSfj!{u3t=-Oiw|l}sbDPq$tf@*kZo zX2I2^Up_oh<Q|iEYbg)XWxMb2e(9j-zRSb!2*saHLASqRP`#whqn@V!8=_=-X5Ej$ z-+Cgs>`Hw@b108y>mFtEb~QXV7w)@c-QwlPy;?WA$$!c|3?BocK92mML{QKSgCM0v zT4k+g)qGhIaDG`+tuyB#PsI23tsK_0I>M<R1#`Y+?TY{F7puXTua#E6`C-F<ZkdiW z3Q?_VJ-Bhsp$mogFU}3o;{EdP{>i)z-y>~jwbcHC1%A$dIzzh;X%ASnT25zv$mI5$ zU5OT!9BmYGt@W4JQ|DQ22;4i;=pc`}t{Uy<@)PCS+>nW_Lcgx<r>45<uE$@BO&@0O zl!#i@KGgTiRT^R>M$dhX=l~pC$1L=j5j|ib2dImi00H0bFGeB`A8i!YD&pFw?d0*d zg#H4qPT#qgOG`CeaDE%VAFp>B$y)pDO5W*xqADd`4LH4>rB@MkT`_=x867D;c%2R# z|MEsC*O7qvb!X%VoOTWMd|F%ngIqm=Ui}|({%6+#t~x#kX$Lof9H26dMQ^gRc8qgR z^%+%4Up^Qe^Z0Ci6&cV0XT4yx67LT2I~Tbe_5<yIVFPRlH|4qW7cN@mMw^_XAw`3z zA)Mf_t*S#WD0>g*m`1ES4{CU=Gy|{4-6uCAVja)<>k63sG1l+Ye}MLptVg*>UK?`l z+4a1g5=v&L45uJjql~u9H?d0bd>}dNBq-5o(9^5)LgWrAfT&RhQF3ibSEF;A%zXsu z&uLH35I|hYRo*M^F~~<$Od|+4LzK<J<-ojErV3J*)>T_i6kMQefFc4qc;P7{zfJTr zk~Fg*rN=y9ZPYNWy!$hO_vpsIlBegPzUSIGY89$4yB2c+I7P)7i3;-Io!{IMUYZ)J z_sK7hBdLUB1xTC1zPSgroJD}lzVPlk!ze`S*&mkd=kcr!8V+|Cu0S!o&Qb8q9z~-N z8i>J_736tYO><FXF3)(@k^bg+aroC@gB*B^a<!u{DzcV}!i#Ssg#$wDqh$ReR!| zPFlDdG-Y1>pN{Wq!-HCjM9f=N#uI4c)LD9qlibXjLfWNo9mbxB*2v$`tPi;DduG+A z`cK~O!Ad5`s3FylCK-hzqqeDxw=w-_MwId5@f2T9*L}bcJm;@n5kbFE@^^`<?z;Jz zyAbEO0NnFKo2M5>%|9yg^2XqZ>7!z*tfh)}9Gk|P??3g2<<T%;ge$V(-Kq3dA;4UX zEVK4}?1y=K+2~8Pr+9{nvq%V6)-~oxol)~Q>aKGQl2#vnjrFWHC+OH<aQ=XaQLLlt zw%5FBD{~5tPCGV#Khphaa4{>be^w42Z4_iQ<7R6**blh&G9GYM+w@f@!KjM@?nW9! z814VK&b9c!Ajf=c`dXJ=!zLacf7$(>(QRRs)FXO_vp8e+pS6h7NU&P_O6<^(vDXSO zKR<1*Y_K0$1E|a7l$?F#Ux-X?DX=)@)Iu)f+zg4HwsvZ4h1uA0;Q9DutLutpV;7$m zlYKMWAz<9Hl2bC8PD+c;E)M-K=|;+m!<w#_UCvo*z8*A+3mini3-|ZK`8!p6etH1b z8B0K+TyP#uo$V@kd};BK{m);mSd!wo=V8)v$Ld8Yv;9Rz$j{O{aNf-<SW*SaI(NlG zyGOZ2vQyTS9#MNFxI<4wb0-~PK~mZ1b&qV+yLsDm*F*SdbOvshjF_V5B0Tan`-rw{ z@y(5o28<gv(R0ParKR;ZOit(;8qO{8XFrr2$=NI-Yjn@uY}kYcDf`#HntLe8r7%Rf zd5BQafhqsYM-VFQ;QVd*&u@#^Hu%@WkEVXgF&yXm;>eV>uL=)>td~|lN>Lk@Vz{E* zZqxoVCX|_DysE9GR@{`-@fVBQwEpzKlokKM!S`c<(9)Ca9}z^4UZ&Z;Kce!|jhk!r z$)Vch=q>r%O&Oy(;<IF{h;6dIxU@pxfZD>76FnzJsodRhOPFB>&cC66bzJwfwX?&v zfx{=QDLy&D?3C)k6wkwUCzf1Xd337fmbAhX=G9L+gX>Sl*P6Utg_5p!V&-%ab@ijr zfx}WJhFV0ayx5T~xhyqeo+hU<+C*jEfxAx6^@EmPa=sbXl9eJHn!3K`?ukG_W5@*D zQdydR&iOmle;RTB`o-E@-PG`E(P7112&F&NdULhFfq_d7PdvC#R#(?SM9b;!%6ZC8 zG5bePcyMXYzWZ#e3%2ExGZ%t3Q!V3dR@`)|E*(8(O>XdsK8ng$wTE)mD)E%n*^8@e z(>FJ~u{t|(*P)KGURqJGeO}S%dE0}-!y+J@s^~YJd^k#Z*d*7|Fy-d`yUbLPHn$0S z+)*?>ppd+0q(RWc6<#+px-<Ci)qkQ_Eg0we?$n~gi&s6p=G*f5-C5Uj563V6q1NJ? zs|!NU?TRwr49*`l_MsX_X1h)Djof+G+Ro`0R_jJ{)j84m;kr44CQezCw7j{Y;6R+y z#Z775$=U<=@Z(VRMK@m}S1*m3ch=?HlWUuso8CQIyz0q#_jXw?t++0wyf1t(RC@w8 z)c7kJKe(jlIH0yHW!0<ur?rK++I(oYZ?M6mINNJ`m5oDD4s*q(TebEiYOk{DY)M?x zMoUMg?9)GIoxG{x^Sc_r!qts;+Te=xx?#@koyw`})UHp^E1l?C<g=pr)O>LD_=#4j z>x2(aT=CGi#VAyjmx6f!btT!Wx4lZ=8};-geMg?#9Y#j(BT&U<P_AHBSC-#-b??zd zz{2KFuiEH<Syw6e7CC?DGtMSc(en5ePw$?dE#$E;%D6lp8GJFyR5U?Et3;RLlW6d= zmzO60xyk>X*I9_<f9MW2^n;FQFE2fHnTy-a&9COI-5_Q;&b78GbDE}}^aZy|r}8}v z=9W*kJTg%)>(sk4b=w;u%8pzKOTj9yxDk$b!N3i_)Mm%}Pw&+p@)a&hf5`eSKY66V z`4LlGi}UyFQ!IL%wyF8^l#z-Hj;DLqvX63|f2rAvub-sjEY(+}`zo8|#vV*?DMd<* zJqlyaZ->&_SE|zJWyX#+<j2n49^>o0wd0iwA>H`!<m%ur^N&nkU73AVbn*FR)0P+4 zzxy;sTuzfb%BQMy$ch6T@#P=jr?UB$i7xMWc7}mrQ+Z*dJsN{vEeie7w+eqiR@?L$ zIjsg?)2h})u3jMeT>j_f6Ms?ZaZ9Id=6uUpD=kh496W79bwEmi!L&`apTB-8EB+Zc zynKOYc`l~@oz<;^x)Hv&YESfTe{{t?|3XFt8!zVU)1N1oyc|1iikvxV`JMlY)=mP; zZ{}2HMwU5*#VcQ0r*>tuuFZ6B&bN2l{wWO8bS!2AIKd&;bkZhI=zQ6YeT-E-<$QO_ zkVBQ8oCktgC%fM}vnX-mDil`VAm@je)%^M0qv%`5b+**K@Q+-SOAwQ}=*E%V2Ctc2 z+gu;^_2bL22Vtt>^Y!Bdwrrwf5qm<7OFN5z^y)A8wtO%3pSmkAV;?6izjp?><s68J zBr9u+Rg2?nX8iL$W=`Pe`JmROiW?q^vE`bMAOFkw^!k=<+6HhRkNa<Bv~3H0z8bv( zp0X|6tLd1_hHK?=Az*byx}Hyv_FC;}aT^k8aqiFrkNaFLf~K0^+&c>)D$V^)OfopX zm9|TI$_nj&ijU3Q!l|#0jNX0h+NP!#8w&<>K&j@MBDSoD3tLv(r6_0uFqSNBIhRw} zN66@_n?Dg&ebwAE4_g$0s133A4!agTOmn|?YF?z`yhGY+irEt!Ul}i%xb~HuUvp0E z(Metv+n4>Tug;+)sZ84xW|gS;XyQtq@&H1eP<o#?jO4M~Gs^!=qALExPrMcaz6AtP zu844p@!(=}P&m9`pMGJpw(FpZi=OFW6KZnR_72mY5lZk2{UA%GPV>o_lBnOB_IL65 z);zc3@{iZSdsAz#@Akl};(kl(kC^U5T5O9|iEc&U)1N_|^IsLN-g~m>zy+7bDS8Xn z4k#X^JZo?K5}|>_;1N?dfvl%?o%DQkbIQoq9sQ{FI<nL4Wr;sN`0FDCy3~IX1*w@k z9BL8%*~6!}zBuXcai7hb7bQ9Y!_ME6^mXBV5Mx#Of0uUNaTFp^>6~LP9e0c-l7>w~ zvbqdBZ1^B$b*mOTaOv6saYu=y0i-|lVUH({MylMVx#`7`Ir~uPhAd-rbA#yN(V)%D z3(R?s)SU|sO<9|wwq(>KkBa*81OY>m+uQOP#uD_KgXCsx3IhlfMRg}?#h%$X1tx|* z`0M8RR~MG;z1s6`kc<o&&(zrTE^LCwDOD>i+w^$73-v`urdWnTj`Y%4RvVJ))dh;t zyADtGTK6^>Kgw{g>(}yGq7qIwf=0|haLwIy$nZhNisu&XbRaurQ!(9#Gz*s@^k<y^ z_)HrW(n;bT`_O6s+zk2J6gBWs)Vz4R6VIKL_ohIO1ndQ=(aJ{`&RIQwC3Zw^+6Igu z!C?}|Z>TvoLKsiK83b?*o3a*UH7*|ovbMo>z-~DWl04l<gT}D<GtR&2X`qAB1<wZ& z&g=y5Col3ZCshwowLCm7x#aU$D=n}*<fm^RKq*Dx^y;;zb_9)>b`lfb`qFCVhTB^S zI0fgtAh{V8N35Rb&O7W0XnhVWkm&lcDtY8}y@F6RX@U>;b?|QD{Ld`gsoK+(fua5r ztv-wB>XgJ3{^Y!E&xX$#RuJv?jrK$d{3*7Twqt4jl#zKWmQ0-E4pj&NgS?;-3quL= z_+86py3w~{doDQc2$J(cvYwhZYR)5WO+{{ock)CZ^59nkr3EOG=o_Y<f_Yzd!S@-o z9L*)V+et&mvj$OCeYxhmZ=}KDI46|9^JwzYibqn)3x`c`EzYP;aLgN|%%N>1`Q^U1 z+DoOKG<<Mkwt@I$FDSSSRxX{m@<m0-zQuvXBh0rE2d?(25WwSdyC|ri^`EAzlf7t6 z4M*SU`?VT`cMhavzr89Njb>~`hmv{J^AhIKQq_A8PjIcMzYVemjX<If*kwqf8lQYy zUJ@w;6$0R}q;pGWZpvjFTCO4PKW(;l_$aJy7Pi&<XFGQf+b-kjdfG>(X?So%PTZQz zGvhaYe4)RSY!;9c`Fj4bwu!XW6!55JsVAYVr|q?=stU<Ede&Bwu!Pf2NyfAdRaGSd z#Rn%mNP7@j_FhU@Z1SP$8%qf+Arb0A%gI;;_v@-Y(tfvi(b=5FT6BA_VxX*jX)OJ+ z>Fn)nnZddl@`?*6SG{W=?g6zvK+dn1`og8?AQH8L!_})X1KSf1U>KoOQU7s6@v*B8 zi6%5}((D02L&v+&8nLa{L&r-IeE!_$XA7S0u*%M5)RoR6W+<c`QHxwNGFN~|r8zZH z9d>KLJg+3uK>W#-HUFhJn?-`Glf5eHt4_?$+daZO#^gbi@xe6jx`)D7a`4$Z#Hn<; zH|cpMUXVifB<Ttj3JOpp_+2l7k7^Hm+vq3#jx8oTUg_?LsXojJ`*I720VH`Eqp|jx z08w(B3t3x#klgf5pWi-6bX{;{(y9-Ym)E{Ne>81FX^?PY1746NCOgvyBP(R@AP9*m z!3g!_791Nea5V!X;BiI00I(1BpHE|SX?UsYM@~=me`6w{INmcP!8vw`rcI(Xi$q46 zlGSnonYR&#sG7uUpb9%jOVvEyL>}#vMEmUNERvFCQn;R{#deaiQMpOhi`&sAYll0L z`ruJ~Ah~JCzpJTqmUM2u>CTTYgeB!<<mM{jwuNdqK4y~>C!Ez29il*IVYtfcJH#n7 zP4a2axEl;iUYALfRI|=V&|`;buF3VDXOGj!Vh)|WX8gudr|kkFR9%K4#t`h@)w>GT zi|)CcTqXO+OkVVWs!PC}JA#4+VzOEebQv5z8|3(0F5wa@JH<7ZEj(CJOh$e3zc&jH zE!r-nX`jV}PkmyST-=b$6h7Rr)t^P8(C93A<dB+*8jZnOS)poDgi$@MRne7$`1r*) zH$U^)N5#1Z29EQb;GyqtK!AYF!ju%~L=u(8Ad+<nqEc$73M2FaLp2<-Si+paitPKU z)`>2c-7@idEHZ_QQh<PhE>ot&4hIf7H>^?O)vi#SFe&~|et534Nr72st;r;vnDx)c z+JyD7hvR3cf@#SPbRt=S4jb_GIf*(Gxfsv#Xah1;7>2w8>;?&8JWLirh-5;vuPit^ zYa8(%S(iF{0QbYrZrSl?P_$NiIu?Ms3Bt$I1c)xOqV?5xmCbjsloFmf0!oWn4-Vbi zss|#193T*=4Ah3e3DTntq(nLs!-1?rR4d9QUKPp&kd!vuj#)??qGEPzIu+;u$B^d3 zs(d6cyn*G_Tn`jbW$rOslyhU>7?U+sF?H0eozlt7DITtG!!&H1ebPvoTm6Y-c{-Cs z)}?bmPI?S2b8AkZw7RG8G?&;VlN?e`#Zxuqw-1stIW~Rc1*dJ>$@19NM!O2ykJV^? z)?zm*>ot=(>`V&_6tT)DdYl@2bJtKUr+k*FBN&`QW7*X?gsR!)1|@6XKmS<MAvZ{N ztVLlM1gR`pk20vBIHb7kU+$+sV2M+?4yDf?I@xWmT`-Ows<uQ0b9=_GJiS$Wco6c& z8QR5)9Z-`Cxa)qOeRJ<nvOd?&@Gr#wxNP_qbv6t1DkvtczVwpUvn)MPDFt&D30En= z5~t9(DCL5JRa-GjU;s%sth_>S?jccundAGC*mb3@pMQC$JDe!bUc5t|`ToVT>u0Sd zEm21d+78J!ufY>Qczq69FM~<!gj*u~!V)JAqB2Mdg7AoX0C{L7mMMfjw(P&GUF$zO zTXJ0%RnX2py0jqNWC-5DF;C4QH&ys-10KN1_3+L@w|iErdnuTth36E=3@0g@7(#>! zmN$=nKRLV>=O@DVyHmYZdw8BnNptYJ<R&{ZcA17Eq<fhsM(Q#LdwG(z@9zqWw@R`} zvg7)^ye_b-1IRg2mxm51HM_zK5TX)Xa=qXAnO4qX51kGns?uracCn#K!^6?4DXos( z>G@ax6&?3f)h2B!k-U(3V7${}uk2v?k@^JWP_$?GLF)E7sNe*1ITV<H^*Lc04jIfn z+V_h=#2n^>Lk+tAh;P|SWXJ7dvYPJM3~Uy206~aGCG~4<lwaY+pe94rkul(??#b8p z6P)wC=`!+wL^7yWrtX;lBF=cb%&Z)_$%z$(hmwUV<S1&^@uU-Y2xBNh;UJ!I`5$MG z+>RC-uAS$%jU-Rve|3ajt37S;W2{RCChG9ge6)Q;_5ik3b^w`+QWRGa+k>pSOpNS+ zs7n_lLqSU(AOMjG`8@m!1}3F2XjC}1l}Q+8INcm*pFqSD(HAxR5>+oTqP6~$BcP~& zV@YL2SnVu5sm~UYuqju$|1K=vdYmPY5eiBmDhf=%{~Vt5JS~zHKulH3kc81dvsP3? zI*aC*%OuDyJPCmfhm-2zwe<c=-@+z0hdUQYX<a=c4{1LSrFNQu2Q*xB4+#%;5e*`b zRI?1X^V9Vgk%UCmPJXLDd9u2j@bmSBmXjQ_+~1kX;|WX|aVZUkokC9N_~j{1^0g;= zT5+Ij{ipY8&kvQ?TZ@gnqsJ!6kNy0@-J&p5l+2}ld!#a(!b78DWi%a%Qe{01=38HJ zdB<Mw;Tmg+LR69a2}l7Tb!FxUQ)g`zr&!#U)nX1Bp}BOxsJ7KB{u~6pd^ny?pa#4g zsztn~yudr4@zSE$VVX-4!E3l_KX^?ehFlE6K%RpzfRd9sUTTGj$#2aOwa#E)ZX`u8 z2abv{IylweJy?i9A)&PgdJX1%L~<PE2tjhqezS&Zl9b|tLvNK#@wsX?P!u1m=y^X@ zO2rZhjRKP{t5slnyh&zdj_Q&u)&y<+!uxVEWQD3rxSXAX6)nYH)>WWMR5Jg>*-N#j z<AuuRmkWtHVh^=kM&H~we6o9fFnMGaNM@N9=4>1WbCp9wfK;IZoXcfbwDEAtV8yxR zYLithLBIBNrVQjZF@BMA_Q{HB`Bd1=%GmMG4{-g#jZd!Z@-Q_ki@R&Kvu_TR)l^^u z2LTyTX<&K8m#WtBZkZ7|qB0yc>m)Zw0|5x!1cm?`Tsk1^9IELs-3>AZ)}SS&Vul%L zc^rO3>}&le&^1uzp#1DNw{fFlAH*+F(*<Cnf4JZTuK|)`Kqdm26_uMlf%L6(3mb4C zPC)=KNYY|jnnuqbAugyyWqjfvq#e!ME4G_tBb`+(&zMCM<r6e>q!JvmJ+DH*r?Ze8 zi2vJ!?zF)j4<S&>YfaJ3;91$ijETYFQ(rD;2@E9iU6$Ze_3h@VA0l9Js{E1)Bq`7Q z<nh8X7n!m(7IVYSNsqH;(U8nwW+ss%&(ktYymSd7(&{T847Dhf(X>yTPiKX1GuSuA zXs#Q>?nIWKRmV>&`{?1lSR=)!d|^VKWzyJ54#=VK8DM9Yn`v(7Ifp#<dTm9vWr9Nv zYlyafEZSg>@OX_lHQ5DJ4|NJM8O|~tnM)v!oah<DpfUUM7@Ci2Pe)Mb*bK0iIjd)| zJKi}pCIq4=6Gc%5a4CT(sbmtb^r_N+x3s!Vn)kEkFo{y7hZD(_0%%eQDF7%MrrR2g z8V-8LrVLf;H|_IP|DnuSBD}C%MaXmKE=n3(wm>i%(m6<|h&;=4a}zp|1CpV%A7v?M z4v4TorL(;26d*)Vq1uDb7VsAr5+nXugSVp;ysvCQn3||{?%jY51C5wMqkYO%T-agf ztkt1J)rL**a4rjQ`5(DhytgJ)EbMcGN+dKPm6p+_I$?WW;{Bx1#H7pfubzePlk>yD zlSBnI$J+wqn8(Uq7@;uJhg1^5R<p@vP8geOER$eu_PG!}4u&UJ6@?Ots?rH7bA!l< zdNqQ9kf=J@#bfww;$xY_1QYpfe{}lXWrRbTsScL$3!<mr+oP)|Gs!-sA_hfwA(TpE z#Jv1KEqHbT)gG}hjV0<fZx4Rd6%-*;QC6T!2-H%O@R2da3m-GvHhombnMt2Xh#&n{ z4*Ay}N4mUv(UHlQPmIjkshq_m4|XZIA1I|tXAxUd5}*9mvJZe_tAC(XiZ^+zVxbh9 zINEFc#tU@Tc5z9u3|Ky^FB10nZcmT(pL$)Q_Vt$q=N$9TR|LyHpT&eqbG)~G0hIS( zx;j{&EtBAI)n(qZMQ?$h(%8um`7O^`shhJf%}PI*L^;f5g!^st$0_{5Y`?oPAD<~A z?0N6ZKB3)In}XnZ_VT&}@N2HmLts*}gi?7{9tVm%HX$49BEa&%VNhCynHHo6`56eM z!(ZKfVB&}GcA42<zW>|>ioeckcOTb~l&zR-)}XvoV%SVi+}KbLBT-3|>b3ejI2)Iq zP~>l80eQS$pjL{BNh2*Qckjr5rkaG?g`-uS_<>v7G5l3~+Kxt-mR=weptkI@NutDP zgIzM3hH2q~P`kBHanD>{f~sgrbe0L^Eh1!0k3}lU(hHPM@*FXE1B6_yQF3=PPB3!` zBEwZlq!WEYX@3r`S!`T>P<?hirjqhjB#1Z?*6D)s9+Ky!)_}QTnyy7BIdc-71DB#| zG+A3Q)&jBu6Bbmj2uvaIp){<-e%lUF3R;}7KezAYtj_hHvpHKWHD*T-XxOvHI4s^q z-I7JrL<XnmvN1AVP()J2JlyUC6eD1w<pUfB(Nvxu?k11N`HPe1R@99jH4BecneFAK zKcR!qYELIR@iz4;2`ryHcK)FXCHc%9YWCTz2NxQ6?gMjvZtMh2Be((<;=P_-mr}RS zW@a)-vkV50rcL*0q)`b!$<dvDf4~0I<n3>#c{Vi8*}^-YMFy2Ywtu^FJy_l$g-IT1 z;la5DwG=n<G=27g_?1uqikxs(Fn~l+$POmMOj?Ap(1dDI(`oR1Rd9C$>v};-x>Z{4 zI?cTWq4CR7S6(%dS3s5rCBjsnB{dH6sJhCWK&_cJX)yU`5>fph)5M3f#Dpbj5D~Tw z7_?L+rfpqp{vp73wWkB9VD&~)7B(<bz%We1J_Q`xB`{P}N(m-(Tz-NoCCUO(g%_O7 zfgmzO&E|HcV65JDc*f=xzRwk=D2abZi9f4V$Sc%OJSF$Wo9o%Rk~Ue)x(m+6GU^U# z%(_E*1ybjv)HpEVD~ZsPU6SPTKtVAUDt`chZTn#IFJZ*0s1BF+!-o95Y`-H*dniWe zhH9n-FT1=D%04oQo^=Yj1@~2#B)UDg;9LXs`xN(k7Zw$SYAi|ee1Ok3mVpgRm~BN4 z*d<I+wWOL*(Yn}uR4b=z0jPtWdm_9E#^Dix2jn;52hP@inavWHufK$zOq{dM0v3SD zKTP$RNa!#P>lBxkl5OY-Dn({IR86lBkYA=^GC6qG&xYd9NBVl!V<i(id;BeBUHS%- zgwe5OC+D2WXQKzxkUGI6^#lj1Xh2p>UTS6gHpUH?7b+q$auhZ5`Eu4aF-ao!Exac! z@b6=Mm_^4?a-pTn^DN>%#YyY#vNU$?S6!LyEqr)_hm@$4s%4s+A3g`NK+E_`6GVgJ z=j>uUI3zeSYunH%hp>`hd2m0Wek>^6hiXp;`-~72QJP3Bd7iZcB0m;E8zn+wk(I13 zg&~wW<g$l6&nZCc>WK>Hw<74uG^Pw$USEtz(!G6pw!v(?09uE%!v*_lRenKUwn|D+ zRIT3oi~wN(TWW)hvb822P3nM^>Tq--iNh;W08v3LCH}e!S%@UBHOj75K5MV;v5#)I zO)}ftOz;t_x*Z0;^&ja~79xX2*qHb!`0Ej-21vEoXX+9HU;@f$Bq^Xo6)2xvIh#nP zT)VW!IS=0ZmRFb_<2PG)AaUqKOG6EhZtwm}P4$r~epoDUQUN^w(({p-^TJ?3q{Rk0 z%PuoQPgF+D`V!>QV#j8v+W@EHw+GS_<knLoU&Et5vYaYIW9xi5af)Z>wS7A0KR<5q zr#9{wc<z={9`8xp6cV%NEFC7PV0p;@<sqTsBq)@eL^J@;Oav7c2QDo9I#QRul|WG_ zH*wGv5Ow_D(0<!c8v^qR@+4(gQfk)ssuqY!t68())e`~@=!snwuVf0v6%(dR0>0#o z-Uf*Y54T0khV)cH!%nl%wqMxnW3{LAJ+4eTUrx~rpikI33SL4XD_F+6;aImsrB$hW zjFPsBhr>q+X}3oiNk%LRc<WP}<>wb*_3T^w${%fG{(Tnct^U(^b>V^Ci_V%xO4Kbc zfqDI)O*Gb=A(hvKIGO?av#d3j>undK(lgxe8kvsO+tpZ3z6&q;^Fi00)$u3Bvp&G- z+|VH&ca_%HvW2fijWG?^iNGhg)%lkwnFe#FS6+wu^vKgvSFN*+N@CUq*tOJ@#r5v< zJ=TBv>-@0R_0zHXkT|(V8JdHpnx1)#cmGet;-x1}H|flfku&y5oxYG$U`f>v)Zb-2 z%01+)UOX&3a^QawL_1sM`4>0P9a~a!O%A$yyv&Wg6O7ES`JR=_;VA4^Dv!{+(AXm2 zy=qT{MJ#>i&<2%ICCe7h%YSp?`sY!-TcgLASGOcv#bT`KdHQAeaGa8E8eqL^?>)oE zsd^V2)DDHPN>tW;<77JAJ#$aN^t^S{+NgY3N9C*jvx2hzskO&yPgjTRQvYe%HFW<t zi_~?oLti$35x0%h50t#TrlD$ojO7KoyhD<qh1%v8xditd!w^-g1Sd5Ke^}~e{jR$= zZuU6%t~dQNzr;&jHaakFnX2h-yVKei-9nV~BOH?VPEfJnoVAFgUlv)XE0=CsrN1j+ z|G1-L^&;Lw*MU`cBNjz|?91){Nc~60XSZck;E=J)Vl5(vsyU1@y02s!DVei(NWnd& z#gV!5raTLxOkEdg7iC-)u;<kJFL$rH-*pR#(fisjFYbG_r=#go7biGmT7#^q>n_M> zN{k+RUrGN$Y1FXMH}9#qn$Nvd<GOOsS<C9B%I^N>_bBLL!!GYlHY`!-u*h4#g#KkF z)2+eco=&y=+LS5bKW<s7WrRhJk+iX7q-m8C$A9Q(;}DglWB#jKGFk=gvwm4{(QQxc zx&-~p?)7JW!;QO@<CiYln*YR6bbP-Ts_6n;Q!NupqXLuj@5L+v&oat0G7lg8zVV!U z$hf1irZr7<!;+OOVl6IMPw2SdUms-dPqnAB#cb0T2UtfLBU#r~m+o9M+JhWvT3s5o zc0$1Jq%Dp5QO08m?oIWnlH6r<U_KzZrt|52khR<XU!6(*m!UU~5~~;J6cvadu&&x^ z=ibpq9COumRi!r$WgC>I`^Gy(neA9<9;p|3KgT_pc%QpS)-SL4OJnvyvM$#tTlF|X zKho|rQtQ<9RZcm;O67^GpL-pZ`TXJdl*QE_8_cAQN2L42-F@6)8OPr8pPyCVpK4Dx zvW}R#_FLmQkadKPFIaw<>x(dLr%UtSLg%2+RG$`3!*hsCsq6dhv|aAik$U=}Uta#x zR9A5EylwirYR<>Z!x0{`S}J3<x4=(rP~2_Xk4yiiWIde@%ZKKzT6S$q)2X)sDIOx@ z(7K&}3X<Kt+taW5Pdm>&&qEfg^HRIM{_piO`%|Fce6USlRuFTkFY4@)|NQJxe`&H1 z_f*5l?9V<L>tD5}%}07L0^~ns3#2b7zyBZUr3W$ixI3gh4Z~7Ajne*-#k!Y_-}=u_ zbntn^o%J5QsGg58cOc-t9o_EXy*Z07JX9_DNqYY8^_S~E?ZQLaLrHhMa_SF#oXh_R z`w>~n;zpde=T)D6|M?dW0>4%5Y1d*`JD1)1KaHJy=0ATV?ddxB|Nnn!t^fR0xc+~y zg>KTGACUgd{O8AYy4fg;gY~(afAdktx9Sere)l1U=izX#9tzeSsy#e`YA!A~uCux6 zM?v;mkpp?RbU?SwGx^ScbT+Gz;%iaUzAeoi;8qhIw``hs=hx(ShY_K{>@P!1^sP{q z)H{xBb8y9Mya>fc<~bK-(2&x5mOK`~_m?biR+qFV?kI-gA3}dawytP<F3@BHUEMM= zSPmVuSlvvbZu>QXW1l^6(58;mVhJ7Ws2Bso#4dpro~C<CiV+9P+x^yZ0MX)12C0eD z;7DfBP)A{;GRjqP3=cirsF+~8@Zal4KnLA!^M&?mPpufHk&$JoL&gz&X!2XpMQ)<r zg|=xco1P?pLPGmCeQS9hy`YWK<BreV+=2iccl?@fOa0e2C^kUvWk3+8sih_wmh?f^ zh%``rk`A3iJ2XR|B+6ijW3Kygt21)AHFfU6TcDk$A??=jnOk^@wna#6sd*g-(toRO zsENJ>tyKva^nXM>BJu2oN71`Oao7MbSoA6>7D3Wd62v&3Ku~4+8wN83Q99Q!Qc+<R zo}5zOQe#kn8*VK-Gr!dz130F3nKiSwil@{+XbQ#%3BKQ(N{nb{k6*JA!iborF;`~| zvvD*={Fau_)m$xX&Z3VpthK*?g@6spxIvS@E{T?(h!lD~8&68J9;Na57A267Vl&E4 zel7v}Em{(v@4z!KM>wXY476(fy>c_M4~7kJe^---^vwO!g{An_Te}8JH9-SYDn<jq zc=8+pwU*@m;bk$nY6T-<3-rZ=X2YocBt{_G)gV!uP7|rrZRdCnb<0*%0N%5}NEwEB z_%8u=p<d{4cW48M{=yqMWrY}q!(gb~KOCWcFbtLZzU5d&4B9M?gC>!T%55O)L(l?7 z+iI042ExBRu7P26nYD^&Jg5OGwPKqZZxIe|>rRQ=B5+lVoFst}Z&x0_CV-Kuwv|=F zy(l-Kt0y9=9Jme)gJ=&8LY|}Myd;&GCKdZJ=#H3TNhH}-EYp$3u;HXSbRmT%*^yK@ zTQbXYGYPA}0<2r;7}f4AH~*0xjA44v!&XeDCVDrtu}nl=SD|$wQd!tNXl5&rQgL;# z3zi||EsT-EqY#vkvs@JCKxfM|PO>)ix4p6j+MCi!7Eti0Cp)*hG_n|1R^Kcwo`R&B z8h2t;Si{4&slIwPmXUE~OOqC~!JG$Y39%bKA!CwT*uo?)h78<;EsRkefKwIPde3GN zFgyFX=or+K6k1inE)rT703>y<HpXb&SE4X1ID8o%Bet(+2Q8#gM_{gNe1N{a7`+~K z^2KjL4Q8qB3krd=<huBQq8MtMg)t@87jFIot#py|bH%tnk9T*jbveF|2?QFqAM9f^ zcpzsoMnqr#AD>BvCgAR402!bvVd!tH2fI)gP3T*VB?36@Xz+rzs%29A5Oo}b(7sMo z;|U@){E^OP0P^iGaYq@@R1~!hhIda;=g`g#LtDNTL90&xYv4y*P@uC$g@1{oh~V%K z*Mn7=pv3JsXf}*`c%nwC+-9EiT9{K3FkIHRYL7f!06*#oPt6FOxsd`LmhV9uV>~Rh z5h)10ihWh_GD@FeT$|t3gMK@{0^_U10o@O%J${XVlc<7jwn#B>p<qzeW+ja11!m#m z<W8B`F$kXS!dONYP!48Ybw@gG#8vRC+t6Ab5cC+S18(mPiYJxalEHBsseVCCi=j_0 z7fJ+S2^rJ_6qcxiMzt6<6aFQUjdn30tQO%>OKUG7fR>}jf&rL%)d(z@0er~=AhHS} zxH(!Jht}i)z0l}f{jypqkO$H&96C{>)=yAG0N9|}0EUAJDalZ=F<>5u5j?+!PVOW| z1?&QCau5Q!q0)ov>ff6Z&<^+`jEcDjk7YKiiy>x!KK40H4Qt5=?cW(VETaY(SQrk2 zR?Ax*I5P?P6|I^=!9o-p@TBF=7kRWM&l6efcEs^(;y8Hsq%2Mkdb0BzN@t4-P#F5d zsef*%nN+~trjkC(V}el6x{TRXJl5H)ihPdhhr`Ap4`p!I`M9c0%0L?rcXB^zC#Udw zf$I2XS^8j_qDW;A{k&1b=glf6Fqey?q*=+&WaSTud#YnkVYX@tskO{?5K|l?Q(Y4@ zVNT!dOas{OE)znDhp145Sex#TUz;He@7dMPLjKHY(Ebe1v!#ohcxP>SN5fPT;bvR1 e=sw<c#Zn?0zc=mw%KPTCX+N$6R{LjQ)BYa{$MrM- literal 0 HcmV?d00001 diff --git a/libburn/tags/ZeroThreeZero/cdrskin/doener_150x200_tr_octx.gif b/libburn/tags/ZeroThreeZero/cdrskin/doener_150x200_tr_octx.gif new file mode 100644 index 0000000000000000000000000000000000000000..36c9c7a78a82f61421e8e081ddd1a7d916caaf43 GIT binary patch literal 29548 zcmd_T2|QH$|37|)!B}p_lC8xUS+k3hVyr0?kwnR!P-!8Rx<h1Si7ZJf*-|Kbdl%Uf z3T11jg|yIB$*uYRUT4NKt%mCR`!DzL_<ZiU=bSn3=lk_~zMikw`*jwJ78}pga!No> zAs-NAy0D-yKQD2Hn6CJAX^ELyDl=zG&r(yA*HlxQuc@w}swuo+m5R0@UdKs$k(`jZ zuey<i((+An*ZYatY}8*k-$GG#%N%7F$!X>bw;1U!w6avP+oHP3Ms4#Z&9!UA%uE(7 zUutG;xpb9{^<s+^ruObjtT(OLu+)0pCMSp0>z6HZby~aC$J}$B`{s?_zFWO_Z1?lr zx}DcLn%yoH?{$#RH%s2)pvu;0m5paqS7*rXs#v}ve)*<Y1J6_5YhyR~M0)(4suAa} zlDTn1w36QmiDiY`efC-Vh57ni^l?pe*j%-9D&?$ZWW=14IaA^r`Lmi>GM~xruanJa zHwmTqhQ|2Dp0+!C%s0P&W!9}tmHT4V6wb&^&sUO7RGJyJOfk||EMl`#iNVZbONq?Q z@)6ccBfM4w?%5sVxjA>;maJ_nY7__$c6v5#@a*zk|Jc|2!TPneByoys=*0lay}yGF zMhG4VR|q*PbLOB@ehzPLzEVn>XjH`V;Jv$|4s3`jvWh<IksWQCc4Sjtme1KU7WuaX z)9R(;YRvO*njCLkaVvjH(;3wl1>C*oM7ncXdM`=8J+<KNIrXXp;yudF))?>Fv*xdp zO!|&4eU-HCdD!MRC#?E1?D})e`cJ#{7y30HvANm6Q`bEEW4q9+X1-VD@~>;=y|0!T z>c&yuA-&Hzn!5?Nn-|wtZ+u;A*w?VAziIK?wv}({mk-?D`0mAyw_Tq5DWNe@5wWq6 zhYux$M#qOA$x6;Rl#qSl(COk6$C7gr4&)rZTpX91o_ZwzLd2<)nOUbx^3R?rC^&oZ z!ug#1lK8^PQ%B2kvZ_yBs4U37R(!tlO4X&p*7U=*ac9cvFI87vZfZMU*Lb=8`NaqS zrZ+rpqwH%<47(nAs4?r%_48S!|6FOz&3SWya`(isw#$`|FV-~NOen9o*ie=K{#N9N z#@r7t_I>CN9ejCcs4us@x462x>Uwi)W6kxZ+YN24tv7BqT)+SP-jnuQ-S1zt-yC{! z<G-HPcW)cJpFi&z_%t-|;(z|<e+J(4AP6Gy5sN{P&xitSBWO$@I4bhryWZBbYya8v zdFcO#8`SaUtV{m?vR$aZ?0NCh|F;q+{P|hxpQk;q<F7|n=Y7wXKM|qS-|aeGCu^U| ziKz)k-k<jY&33Q9i1dewt1nWYeX;9*3CDOUD}Hs;Cx&xa+jxb~xKSDQOw7JN4shx( zU+B$h=`R>`DTMp*ud{t(Q^mae-VDWczW~--eTx4o!=AzW9mQ1d|3^4H^nkz@$Tp;I zJHrU<oL|q|@6C|$oS$VV<o);EOr3<Is(b7iel@6Ksm6#*clD)hJ>MUU;a5MyIx)}x zBzO;B3m((UhYOd)&Fg0X#(U_@8tQ)|gE7=U{o0K4`QHKEU^#zm&^HcJOAhyqO{{sl zX{*xi&qMba(4!Ok^u>I}rphcShQ=*>Mkmiu|9p_${9+XA_9%X-CC^8U_<5-3NUG_f zfA$Ps9k%qahUxA((l9okucq;bxbX1GlH@U9N7D?$_)m@nKQ)ukOwBm^@QOJN;(_BA zhL4G#r0<!SIVV~F=mtC=1iQSMnXzfi3a>3w4No;)8NsT%%WB)Jrq(z1bd{~%Brxoi z16EQ2W4{`kDzk>3SlRPXYbjXj(xNb{QBwS~D{8vsY1dJqdUIEB&830IH;Te&47`7B zM8moaIa5aL%QQKeoyIp8<b9aA_4Cltw2D)shaN6m6e(+2P%-S(E}8YUYE=7&YL>P1 zU9VSk4j<c`I`Xq?qa^=x8y^w$=hfEufR-W-#*h?BJ(8ZPyY_Reu@S6mV$>()tw z@j-qCT`30DEIWoVADs-;iPjR@i`EOsEQz~1=9Wt*Pu)&i5~p$$jc15(s@bZ2v-#wP zr3=+t`J-K}XCG?8IgjZv|D56H6H)=zHs<5;J{sy$OP-BIM1Qk2mtY18^;+ngnJ1Sj zWf@Yh-Qf_FTJixs>CH?jz`A+<x+wy%F<c?;N@P&r31Maa<PWkC>S0Z^(6|<g&TvpI zQ0*NB(8sAboxU9w!n@}7wvQ(1VNJB)zqGQaQE~~eBh~OaUj~pI!1Ib{f_8kr{u$-} zHyc#u%}ICoF50L)tI^=U-*p>)zAKJ&n8gUSG)h+kXRZQ|n#@O=hv#!NZ2$-Mg+n_B zDD)ZjRx1lg|B_;TGmUR&f#yyalpZ6`Wp`hL%1X*26G<XIrI}U!vzDG)JGH>g3L68q zKd2S(LDO=l>wXmL%=n##^clYEIyCGS=AAiVDc*j7X1l|*A-2ZJI-LgI%35Wb1?~i3 zX^X;aA2nrowF@rDn2u%y^kkF6@@y~oPA$)zZh4WeN_|eFg2g;k>bF=K>YuR)AH5>T zJ~})tdXH-@Lb>oHD^3ou0wAuqQ_Ez0dhCSiIc8d=pjgBBGZtl-9aeLzjgtd08?IY` z?X|(*dhECEY0%n&j*h1Au4=B^lIC%;fixH>RM=?dTzxT$YV@yDP28#F>C@rt)AVVm zxq(8M(M1tetP#+|mSU)XzO~)t_B=FM2$ot7VLW(L(yi87&SJ!5G^|g1uv6DGrkeLy zff0w%4{sRlUp}1q-%JCqOti9^V7KRhxQs;#Dmik^=QcsiGn$hIXvh3OQ(@AY#(+Up z)Zt%X1%Virw{yUtMGwa``8zBdrQ0Mos(GzWEk8ihR3IST;V7OZ@$-G<<7Adm-bSaS zdQn%+5#RZmFn{U`YeuXj%hV!H5SUL_EmH@&`4~#Y$+`E-Uwz&G(f$D8V-ZDDa!V9O z{QRe!jYqW{F8d4;_gxd#-nkTle|AS4(eG-&$MUjpVdd?w{pYWs*>|R8s_XT87l7MZ z^6z9v%0VXMXrkSot1;kkYq{kIW`@bHsBJ!~wRV1l#MWnZVspJU*KfIVIzC0fdYe|b zX2>%6mAMX1&V|qFMCZ)52{^p98oUuDPm=?wB`IbL%ZJOA?|FW#{$X+&Bh~R}F{&Go zxMY}{$6nX<<OMCub-Y{PcCE$zuOnu;t25MGvv|YgE>gD{9dWLRK4G2fuxd-Q^xcvT zRmo42ik&NywWI0G0R=W$F|4kCgswM}CD9m}?;F~{rE?)!Yj$=0-4gG1V)6Rss-|q~ zeOy!L>j&*uTGN<Gkh89_IBj#dqX<LUFm3L-CpC+=mUs(K7m}-(#ZTixx-6~w8nwcB z8x!mHbRFW1J7q<ACU{sXB3XS)17Ll=VcL1`w!Gr?TNgLW7=&<1NT?bKt9e>Q49L04 zuE;juaNG5+O~YaJ^-W2tR+%qXPM@>(**z_kK}-H%@Hb$#ZF|umEO{Y02B>*heH}pU zs@ec%Z6g*t?d@4{T7UOoa%l$DYgLh))h9u<xEFUaG}}rx!`Z6}-D~4MH|^UXps?*3 z%kB$XPsgQAW(mkdae|h~$ran$tcD?uV2$Z2`D=BFNlZsxd)#RYaUGe=?xv})68LmO zmKWDnuuq$}@k#PxuS~O5Y8%=$%k<A}yg%PJkLrKefMJ>VbI;TJKNITqh|g8CN;XfE zuBV%G3~SW}drj}Q4qLnU)6wGmR@-{^YgST@>z|slW_fJnG_5U<6XaapbKjM=ebJI@ z6{;kpV18KbL=UlwRy%$#_<w1A)zA8#W!06P?_ShrYnqE`p`IUxS`**Jt~9MgbAwuj zT5xcMpewZ|>wePm8jIPco&}pFWpYIeUH{439F&2Y6LBfqSu7WuVS4CEji|vsho75s zQo1nUUZ3Qi`{3&C&+qSKugmS2<2i;k87QDQO;h9098vusXEgY;M0#R9W7ECwB`<b? zIMQ62e;cehMJUJoh{Brvw~eEwYjqTr$+0r8m&|s1mOVVSx}n27Tk|Z6b?DXIl{f7) z$j#{^c10CS(Y{18Vfi}<{zGRx&N+(gydynr?)v(;&$U&YM=$!ma4zeJTi-0D)%K*O zG4oZc^4hBh@4x6D?G!K%`e%G;o;&*x8tRMFG;@=RcmDVORqwe?Yp?8R&KiRXk!7E5 z7PQn%kD58VBUd;{!L3Uuvi<CahjqDD>!!41nkOg~mr2V+m8|_IK*}mX0nH(PSI86O z<T(AGPxZA=M`PYhtl0C;i#vDEoqc$9_q%82Bawxg^{$j28vMmPt%8g1CTsj#{OZVj z-xrQ$9j-l1Qr;a!>;HQ<dsTretqjNk<!ah@<rZ^Lnd|oGIvbD14}Amft7q0{pW6gj z|9e&+JFKppU6tHzkWRNLUVE1+l;?E!>M0;+i?r#XJg4UOy@$@C5M5eOi1o>fw?5-Z zQD66D%bl^tA#<RAtf|Y6b2EfG4XUWtmWK4jN1knTxUw6#aAa<MyQ<@b<gzr5Hx+`^ zH8rV^lZa<$s5*5yZtf7|JVJMPW&{c`2_Tv!(J~y}8%iBNBi@ASQ%S!YUnpotjMt*5 z)!L9vV<ctFlP>Le_x{yZ_UOa~yQ!UJ>VCA~_c4!oesk?*=PN~@hce7W3*D=d7Ne#w z^aII|d_GDg*t5Eb88Kq6+Y`?@l04~Fcv5p^*N3I|v^RHFgb935T7ALqRk~L5xtqrq z_*WQ4_nk0ZS+qtc{R-?^tL;>uR7_{blTilMjK$&BXE}-7peB<v>(~9C_kw^Bb@=EI z$56Y#o~IAucL^)4KJW8t;N!o-H`gq{D=d9UZG&1`N&HoJ*<G-!_Sek$!apebmu#c$ z*I!En$905IkvDz~6%lO2%9x93?6{q2nWnh`g1I^K!jb12kLmpV->1sGi_TW6I9b6u z+RrrjvVY&Fk9RrVB&xbpC5vvQ<=`iyQysN$a*X<0x~BfT|HD+bXW3dbq87)}kli`G zBHO0E<MNJoFYaWoM1%kH`&W0*Z91;=H}z9xtZ<Z#sDO&&y;L_I2W<$pMwe$dAm(AI z^7iSCRu%WUC=K<NoFl3XhOW61{i`uQoHe?(-&l2Nee>xR5V&@b`B7k~l+sewS%)h- zU*EHg6?%0P%sNmO+iGS@&3CCnYPSmCJg)n9e+TdcWXYv2K=t3Jvkxn;y&j-yS`{Ix z4k{o@r~NC3TN8@#pPZbwBEqiN6LgSnM3ANIl>l+azo*U9T9ziwSy!L<xuc~1p4P6R zPam649yq46bLd4+>~qCw^TupfRimNqo^x2$y)IEhy9E`jCR9^_?UF-G1k`uBJ>SM5 z@?UWl+a@~=ua=xUg*KaxX?Xp6dc_gV$fP;zA;#2OA`UBWXs<iGm+iH1q2Ghjz-fN3 zM6U$gI{mWS2@L0b_x!N!K-K6*q}Mj9=&{yvMuf%X+d(I@N7tW2rxX%Cm=#D02<^1j zH9<s3=Ym~o_kFH?U!QzoQ~MrOua-#rS##ZYQWZE^6d$S+Nq&Rmt?rRxfR#~}7_=0v zCCav9NQU3W{G|Fn$5k=xTN<|-o1;b?4cb%<m#T<%u;#qO>eY)6YObwK^1UmxxL$Og zb~<)(`NkP@2qeGmZp`z#B+Y{b8)iR;rPml2Mz7Ns>_#Ayew-jjN-U<jJ?pqhLA;fZ zP`l7Xbuo8qj;Oj@?Vk15+^+-jWa9dNGjc#CALL!wEO4@V3V}pfaHD#PDW0I|Hvne+ zt5n0A=ytd2tHlECf|vb>BoR`|2%uk6fkHM!kHTBk9kW&1+ztdFLCz}SWvjAx0$7vd z2EXQpf8rFqp4GLNc*oq!32b(u0#?10e3uUji}7;W{sY}k(7Mn#^gXP57foTK;92n9 zKZ)lF9O{^%qC&*toCZ38iaaR=uGa%TmkV8Qn!ELx<Erw^SBI3{>Y%2sQaqBZxv{gf zG2ehIny~oTQx^_JlV~YzzkxoWfY@A5q8sbENPC8b&692|fqp$K8zMe1ZZ>7E+cV^U ziWE&W+_&oWJvpmqy@xct4{4SKg+#?>m~`xsvI6;n(&+q#8zt_MN7Q|GzHGzH!tr^$ zY+0zykEO)T(B3wnvh`WSG>t=fi#w`v6Nl-_GjpLML7y_{NJetMY1e!eV$Ak`b}R2c zQB&X+o}gOQxg%Ecc~j=fJjV?eZyVn|>v|nL51F!X-Q5DC{pmo(ne(ZBFQZvz;S$z; zY}4bWW8I0;HFDbA4Fy0~n9jO}W7pGKqj-d2NwXT?cVf*~$R<hGWov`*4K*H|0?%%+ zOTKy;SP0o(+O!Soxp-PVmmag#I12g`2XBKKu}4MC&`ww2)W?cl3b;kVVu)_lQ)VoD zRH{{2Wq!ouV95qnTj;6m7<hE2qtD1JaqeoK01L;Qaf6?^ZqLa#tjN@AZ%$Etk6VQK z0hjOpmtY<>eZ$p*a#mGn%!A}~m29w2H`#DIS&Q{#HHsCXVO_U42#F!Oh0?KhRhg%o z+&v$&chNEj#rfMU{vyt)C=2ApVMQhoH9yT~yEZ=}<D9BK`OCY#5a6GtLuub7ycAuq zC1Jc<z(4>s9@f$J>82Z7k^`Hm1}IiP8rC^PJ+eYffy<%;GkqG8H+M-lm2COgEvim* zx-MazxwL+BG*ZEV!F7%sQR6Xrm@n4_j05%e3;k2emfiH^dh9eQV>3CS1IM&?G>xpI zpF%+S^&AwNyRNRZ=R_I`H37@1KMR6?x!=oMjcFXo>Rtsq3KyNdugEXrGiS%!bWS|4 zASpf12Pq9_0>&5Jo*z8`1Z*GZC)((lCMmcFFD~SNYW$a|nzI=W6ABBGfzC3oMW|hG z0#>1(gGV&hH2~JVG_xvTSPaVn^tMmr<{NjlcD>(Uua9}G$0It=^Hw6}F)MD0eUmM| zugv!M1Qhhnc8k07O=GPx<&ZG)*RnT$Y8=6VCy{IV^eSdLH9Tre1K(cWj+!;oxIRgp zy7^IQPoqI-l(=@5#fbvbLuxzvJBtPU!r3Ui;v@>Ck5ILjjZ25pMhKpg$c_9JU~=oX z^(T&B#bO<b)YV*XrC4n5?&E7Q6H}Iy)elB6o!3Hn2^QxQjp{_S{QJ7w3)kH|ws7-< z%U8mmHypj>_qJoPwsc(!w;(~yb+8X(NWqKqPghBKw3jk<$ATSVb1E<_cpP>P69L6! zx99n?U@}%=pO%#`*PRoy%skp;U2fQ(bH40ejF%u{!BT3jHGPk-agd4Dd8D#(6ExcL zEXwplaCmVYi<;=i*FyOu^iv!ggM{)|iR!1zIvH@r8XVV$fYSS|0rjqT<;%)Iyo7^@ z<RYosSQ!g{M}L(QSRT9b2w!#Mx|SRvMQaLPf(6B_6EH|6Nw9{{wh)$J93Ro5$OBU? zr0QH%Bif}AVie4&;q%1fZTfW{4na~d5`!SzOhnX=nfz7%CvBbgvJi#U1&MOf=E8#0 zRb6W+d`?&#Uecnd&BZh^-~CClsJgUSqKf0?M=#~ny=+A7DuOu$r0Z-8H{S0M6_ECB z;T9t52OxYB`*<K8D-uf|8E|pim)PQ?$u>WRF`5yW>-L}*5|!i#y>Uckjpq5hJ2|3L zXY!GP@e=w#JOYV~@DYP?LJD?fhmFF86rC!9`2?iRL~>UZxz}%L3}V}lmjp{AL^xT( zFc_zrTE@bTCnc)%OVv$NWaTF~HjdVOU*G>?g23M>ad#NL%BvEuAIyIL`nBqo{Eg-B z^T4x)6g~++02??I^B@8tMx}cm4K{I@28I|+!NZB@+3-L9=_HawaG3IT_P>bMi*SKF zq6npXl6ih~h723JJ2>!lYC_%3r>wH3@L4)vt5$HTO6Dun$Yu)%h(JeufPsRR&_}rm z8yE}NkkE%|0D<ukYh<8_x{IuSN}FEV5((=pvmI~Ah#>`wI1G$$JLbAQsDZ`L@+wcs z!y7MMF;!;SG1B!DX;pi1G7h)-pXD6QcPaOe@9)>g0u>E|@pRDo!T1v_Y}`W14wv(! zUMA*myiaLQDi5j%7Mbr)+VS?u^@D$j83!Z%Wu<}6lUd&VfZMEXn-VZiO7gA}k~xRZ z$j<ewiI$Kwj^rbd0uYIy%J5YsWKr#w2HQ)1)f7>xwtJ2(bWWmXaIec*qwZ2gX-{2| z33Mgs0Hx80QuTb4q8g~jj$c*ceF{kl`N62;Tl#zDN*5a;FkfwTiNEaf>{aUWrk;i( zbU)Mep93$BhFB0Aw}_I%d7sDSA>l#_hFKt86>vz041^da1>fVcz3e=MV3qtb?!-C5 z>;WD>eEy%Y|HD+bhfddG?#*y{hZ+0u7h^+t&${3DzJf|9os%QvME?syOFviy1BdWL z5(94`N5CE(!aOpfPZ8Yq+F#7jgTjgxH~dYDs1f^8BkE6=2HKq4e4KPb9$QfrWnU5D zD`3;h#>GcOb!sGz05kPl^Z*#yPwEd6Qg-nnrIRG|AVux7IC`;*T+V@&N1woCh~8i3 zLf`(f$Ej`+d46Sy^L8|0L!)qfOH1>O>TKsam%BIaYzzuP5eDNhH0jchz|jqDS4395 zfo7;A(XL_+<1h|Y&%T<*Ct<NtVrm{KK4kqMe`W%T%FIb4=d8Ny?~5m<<CNDUF>A{B z4E9Bc@SzdK62d+2a`04j=r}<S56w)FtDMUz7y;u3TWy!Bq^Sb#UM<{H!S;hHsfH1v zYOWPoe5@>3J~8}sB#=b9^h?=vG>wgJi085vMqtFYam5_7uTGHY;&TcH-Wf*V_?#=C zpaS76gMbp=^@v&*r0&&nY@v=_k)wn@tmg@6iedqYMNQ7fQjW$Q*;a2|6f1{>J^|0f zDhg%CMEXC87*+*Jyj6T~82>aCcfQ%iIZ)3LX=xP=Y<);WfCrT>Tt5cF2h9kOap|<B z#TwC;GB29Ug5&4+CiGjNRUiRJpfE#Hrn)^N7YgSvGi1Rl1{)RfXDp5s;hl9kZ$-{s zRj1<Bd7uI?Dw@@SoM1aJ@UR@9Q$VrW=E*{eQgtaLMN{yB?4_at{H`W3kZ7@3+rHl$ zP{3Ao<Cl9A6zyvUw(N_jjii$_s8Ye9JXzEWxT!eLIHJfgFo?>=HC5gM3RQ?PA>&7v zQ=}qVRbB2!aAp@Tf{GRIyuh{;2?`UmZ%S`J7*ZPQJ`xDOkFMRtBF35Fp!hZ~uiwc@ zR1_xalaYSRqfjtgp1-PBLY{L4MWfd}XNK1LS4qX{ExAYYohu<jX=I0R@ksz%@kE0l zET>KYkhBIre_Mr*Xf)Y~DOF;w+cP?6DnD5^!K;Q~eR9ELxy~+|{EZdf^`2!wMaV+| zgMF}uL#rmb=V>(-3j`Z_2(OO~7cL@;Zta&a<eX+=5w8Zz2Ldlnhkzfe`6i;N<Mn@h z_$_KyEWLBmz&3Hg`hqk=PLXr|&=faI%v)7xm*q&qOr(-1IEi7v_%eOAaN$C4PRW)! z5mozLj)s(!E_OkgrHPKJan*ZSVHI`z_uY+Tn5wpuxI#p}Tiud487-gNXq6JoM?gJ) zBSFQf>Y~PMR8ye?3^yF;8^v3Jz<KsQws|;M9a3<xSctY{T@)*=F625`S{6j+eKYj# z>-%3Ld2!U2u)$onXC(N0TW=m$b#^InU1fPgR84-aYXwCG6(O|j?|;mS27jJMSt?t& zkg{WuM`J3~e8iCg25JF1DQ0<AQ2J9q#3bf>FIFdDm}yHe#pEyJQTV2is+z&R+b38} zYzv*v*`XRl(;*r+(f>nuFjTY@L92b5K7n*GDi)T)gA*jx;GK<*&i7p)m_kODP6Ef6 z?HC;X)pgcIB;-4uT6&ZiTu~)xU$LLB@Ew})C5!<uLzO~T`(aHTUYID@m0s7FZNSba z<4w@>t!>70h~Z~>Ff%Be^F_DkTY7x2|I@sZ$M&#u*J-Fyf;sl#W;x#Dch9j`bt-W< z?*SzMx*&nFA6+}qaOwwh@yV{feBR9qitCEt`uThkK~rX$ACpx*fe8xX;&A`Xbrb`) z+uu5wR?c*3JadmCVPFgu6N!v(OpQ>quZ$AybvF~qccxbE=bP;wVGCNipA%LBtMZ*I zJnMIs=|ew2l2{Qfk!Bphi8uCcv%m!6zH=Y?=g&V+AfNe*dN}0>%~)8T(383xMn`ie z1U5)1!6JoJmRc7stmAOmzrQo&fUvSd9zGBRix+|pEdjvL{0g?EkFG(t=b1+ev$7At z!eqCHkxRNeOZe0CR!nK*pQh_Cs-D2Tl$(!0XDQ9$P&EO1C{7Tx>?gwpx&%ZIk?|6p zmEn-0$Y{3*dXhTRRS4sEqbFd*ulTF$RHEP^YBjq<LzZu5T%S{RqN;O==M@j2C7qj8 zJ|bX6SxVP`fLYdAsbE(gEUIm6T0!AZb*>{vV+<3xv39?CWkP4^X#eM-f?&n10DxnW z?_T3a8SK+D3|A}h7Zqs95m9!k@HB6S@)YhryVnP)n<nP1uDA#Y>x6QJ74!oJqXWxW za6CFWcw&Wq2v5~lR`^P27<|cmw}&A_SH9d!aVpa}`qp|8*Hou_!R<k5wDe8_+p$E& z2)n_}3+VQ!fE-+IQ-2!51vM-b1sLcEx!KKvvLDO8;3WGF6If1N{!*?Pkc|}ZztsN; zU->eGjr62y#pcc@`=W3%nK%JssQK)b?VwI2Rt$H0`Xtb94-_Ymop3@*?(LmMIf*Lv zD#W<b#wXYLCB$?rRFJ(RfPO6C-$Bpx$=xA%_kA}G*;hxeDU&dORthwl9sA1@6rG_< zDIgsoqUv0^vp%UjyA9;YU(`zmGQVA+Llz!`2a>`filT}MVxJ<!PGPFrr$c4F+rv2d zA=z1ZE3%i)ToB~1XrLJaYERH7(-$N}!xYE<&Pr}^X!7`5KC28Dl7-$Gr~n8RTFgqY zvI0we60O!5uyH9c%~HnE8MK{&lHW7ExUQ3|S}CU6?9RzqOS}j3*m7*o$SGQ&l^#9h z(e*xk6@_O05fuo=2`bjM>FYga<(y_3b+3mM7!+v7{HT+|x^{t+Y{|BEgZd?L(>0tc z2+(m*ajpn%Kh1X12yLRJfJ7PL_~cQPsnGUO_Gq6k{Srv(X0jkF(10_NQ*fRwfuKK; z(TBS|j13^>=&I<n?kZ0sYgSvq?0Lip5P2y66di6A#NU9JuS(X4m99@v+=6zajq8A= z#SZ655}nsL@c5YvJP?dw=wCCH0#tt3kQ&FU6PV%3aJGEMeM|p(A^8+4=qr#i`J7RI z>p(w>h^2A~%35cU;D!Wrg7j(KUk>P1QAmyYSQajEVq$66dXpfmAo61J>F3A7`_(kM zjLM9Zox5`8tq)2I#)_8&^GUkjXrYG>y5>i7CoP-e3BZN2@(^D8%g|G2XVu5fFpop} zVZ{}Qr@fENoD2WiL!Bh02l~0noNW}>-pJ{T=94Vvz#~R^9(v)es!q@><u{J7&0V<b zQC+IhOA%Fv@{5oNBH#v#tbPCmZvZ6%UN*o)FOW3uUH$LH^H(Mt*DIgg`^B0oO_c1* z{dz9G(+@#Qk^t0*TvLg5)bmhCRl>_LwS)fsLG)%I)GZY_K2d&IGTfZv_-p!kYl{=i zn)8R&WUl{1B;i=+V0j5n<-zR*zL28qlx$Om=?V)IdKIXHqKE#EtaXvUUbt|ckf@Gp zO=;lLV}IcU>n64&olv)j(b5kUSjD{~WAB89Mao?c=InBV#i>JuKWCRlc!Hvxq8+;6 zGlIIsqrSk%%vKfd{BR1e%RqjWio=&R>lv_M<iC{t(fVhi?){;~M}qD!g=<w+pT6JI zCD8V9tShJt7g4q^^sY~JyFpFJwTBmHPOrFOqiA35-B{qpjaPOm^=@>A<yM5)9O-~O z$3RjbmYIj-#>Fx2#JmznM#9Pl16VO=QHAk|>vwNPYf$^*V7@{(?i58QSn~_G-$=-H zDfehkdbfy|3+=Kv-hwjwuW7;CPLUpy*uS$d`7y8wc6%5u<eNqc4B=dG635rBSDBFK z5G#F(s7|{xjjm8(oi`OtQG8HZp#@cbS}NOgdwJQ2XfpP|Nr@?H7KA>%am~R<xA9N@ zQU52x(!|Lrf?Luo>E10%$?{n?1?Ty?q`aYmR-oGEIrgWraS1{h=vda-qA;&qT7$*i zODF=z>-ofk#n51%f}PUzl;8B=*?9BWE-0JQMld|UEw1TYK?*bxB%pVDeN=C>D8ju8 z(b-kuhhWw0-Cm&48*<DjY&jXHO4N@K!4ra0tU{yqVc9Kyc4P0`{?A0aJ&Y2#N>HKb zR@bR1co7x5Hm7utO5AW}f;&(W=>18wj-uTxE1!~WIGq$+6HP7)#hG&Ls7=!|U}LTL zGWf@L?<5m}@`D_~>GLnAuo!WL<F*MZ4qby*il79Eg7i}*pj;uMcTIt%a0d$c)L9!p z10MViru3z-Pm!8K@Mv?CD9f2pM2)+QVKm}bCo4Zt5Z?A$u-|D`jQGMxyLM=fK<`Wu zdS{QJlvekDf7u^274G@*SMIzW7;%i3pkwHthm_;y?^qC!_w*-M{@$T+x;@|e=ZpT& zGV%O{JJWCFVR|f6jnD9h!Kw~CkFJ|kSt|A$#-b_!)xIEQwc--h=Lc|nocsY4M}siV zzZ4>VUtz}-DU@d_i(5GoAS10v(^Xtu1Z#e1<Wqs5B8^U#snDfN!Kpg4rLG8nIm@$% z!zdKTJJZmy)H!h^e=teT-_ieh^WePm--2@dcW?*cArxbZBeYKmI?Ai_$cFNbTsma9 z+uN@nD{D!ykt_JOjGKd9-aX`BrmXN41t!t$`NACiyE2r-vu)Chr8=8aVPQXgfQ*lo z2eo$=j0`GrZraZyN}`bOWW}f2>=*0Lm&`h<LmvH75<?#O^IT|=J6P;#onx7HyG&jB zWx_GTKq`fcT9H~QY@BAOH}o%s7gkXb>2?F2e6x+x8sviRd}XRX50MsYQ2(42DYh%h ziYHorggv2W8?!cN+r}5N;TlidL=2eOrPspU=RgrfF%8d{EXy-OWiMLaI9&ZoM3@3U zscz30%?><FGfFd5U0zW$6(vQP^DNUASpc28Q!v~*)iGS?dS~mK2SlS>+c18O7b_Gj z^M>Bt_^_YJIQjuQCe{C`-o%$}n`?N1|0z29Cu2EJjU_gcRNr2q>-&p6mJ^M$Z}s<b zgj;6Ygd1M)jBki1!Y5IlP76`~HLOfI;j1C0X~?h1+16vLQ47!1Ts4jqlH2n1MdNJC zeAh7j3Xhl^yP=_l&Ug05$gV1IzHT>*3x57W>{M+0$XGwBf4&v+$#r`e+)`%uM1IZ1 zZTW@^gEAWKrCO$ngzdkxq$SHX*DjL(sr)U@9iNO(z<g{jZaX6w1~0;kU$qRkiG+!L zHRtgvqRDWZafrd0`AdXn+g!BGHP&D`PXXJd8Aei<3!2&}tV~-SDK__Mu=bwrTaiKW zN^52tC$-$S`f%DZ%`#Q!>7Pc%V9jBD>2;{w&*rrPDA07>xxyqa$2Lvm`jSH%<78K6 zCtIpMK6Ctn(HZBAtr=NUFB<O`)Yu~B6_aW-_^o@#+4I9^dnVBT>2-YC%V8>baa)gb znBCObYZNRaj8DW)^^8-nJneJTr+QO-7JA4tUio3FQ9)Cs*wfRE*}v+{Z%sD-9^E~k zh7Q{YE1_(4&M+1BilJs(W<zMjN5yB6WbXCb-;rH2e^pw`yRNyOmN~Y$uEm}|`oPQ= z_0OMSvV)u_FcHk!lwm4U8=qqoVGOU6H{6lcX&QPwEnexU!ph#h6IOHNQOt(9lOQQ| ze4Bld=YNp=bZtT8mk4vyuwLhvGL6f!MH)|-%6RdMJ3&S`7{7GJMaxK`NIT)|J49RB zH?sbW$;M$j4C)&$78#U5GwW?CuoU#b@n%)zHD70VuKd~Rj2o@X)+qRXdV6L0_CE=c zss4{|cU^ziITY)sLGb*><QSKR=*RQ(v&D67U%Ulp8~}OiZTW#$#`WB!yFHAe%E+Lu zL!01jm!77ld*>Rps<~P(<>{sEp`Rl@E+B@C@aOtJqu{CH|M-4{4@?%!?l>cNe*9OT z{%<H2-DK6MpFej#w%gY`^76x^>K_LCuKj27-5w_R=BJp}`P*K881>i$HpZ=gM$4XS z(dJ=a80P<;pMMJDxRR^YJtj;)z67EZq<-4~nneC!41R{6Z)z)$>t_xJ!>|6N+<q-R z!)4Da?abbm@2{CAQ2+eE3cq(ga8K1}0{s6emBxFvhZ)!Xxc>PR^(ra#_fGz|(VkAX z(Xz+y{J!6O=}|8Hj{XmW5q19_z49+_{A#!7cbJT+`sdFm@oz7C{)C48x%%f9yFCn? z{9081HxG@}Kj$Vo=+|Y>&EwzK|H+b^2I#*5`1Z!l5xKtkP(i5Q|3vpMJ=@UZ6-cJN zR`l^Mqqghre$`P1A4MJ+3*D$N#P$BD{uwQMhP=Z(8qXZh=jGIymD2Y(M3o+FW(ZkW zkR|1p-d^kAL<lLc5U7$Z>KjFN@(mjc3zjEBmca_gukvEl2FD2>ZUkW=cUKb;xZjOn z5yw)R6)MvlNr^a2Av39p;heFXT=e>9h=jwz&3s)U46bMdN8^780sTWpEEZ$vTbDh= zAh@5e5jbul2rJyr5a1(~atIP-;SwokXpB_$X1-aYO%re+h3=lAvFr3S4;u*n!ojlR zqXDH0cbPsmKS!N8H1H7xrn|EOUL~(yhD9)=kMR{ExIvX{9tx|{dm3AHcMpAf*BpwF zSawqBzd|qAgI<T0KDtgq*`vFg3a@A)BrF^=MBsImhdR59cn}qMS6O#=A!$}hX)3zL zh`^YY>f2JVoVKO3;hl;6v+z!)0H|ns>52Hqdq*D86JZm&tTSuS(e!vOFDD5PCyVD| zi(pyh3l75<(x<8`h+uKOt?1W&AbZ`FF*6Lpio0JiMIIr^Jc4RwjkFp1=*&XOvW?)w zE}!yE9D9vegvt#O#D^TBvPTxd(H{*`1f$+je!v0lK@i6jDBfs)bPDwsVc^}rAA;Zj z7LJ@)ww47>0ls7dW*F23{z4TMJq=$&b1z54yXHe+s@)~D-;xk}T3%gdgkVIyE9ntM zwG%<`1ek|0Km40OLNFM+fnMP^tYr8NjE^Au_tf(scmz(N{evfwbor3s?-Mw)z+0ft zSr9U-tPT#at^q3`vl2CKz^Ua!oU8~bSalr{=99q-*jRMhnufU$26MsW!c`Hx-fRpR zQ5@>gMvy=qf5j;Xd{DxSZu*sV=#Wz=_-Ei$2Lu5kce1iMa3aW{V|WOHVSn&$D+VdW z@QK4|gw*4zEL0W*Pm-u>;lLnMQnKy<msn_Ax|xU|vYiqTTDH2o!JLWcA*6(6v(lVB zL_{Qy?afgYRz#=N6lSV8dJA+nxaHBEvqv!qd&j#jDbyT8c*F#5kb@O4!yZG;Dx<I$ z1q*IDa2$uQc)aU9x$L+O7U8+vds3Md!Q3B^Kzv~IzhQKIBjCYsP~Z<fpumX0Z5b9a zLi^f}j2VImRbk;A7{Y}GNqHp0f?(iKJ!~gj#RALVX&xAevqK7D{Fn{u73#L$le`!* zc3?<U03jLb?#6*kwVp)*G2Db~Ugs@@1K+ka9YV<TH{{?~2>Q&20ER@)--vn@9Hoju z3<r9p2{31LhSKQdKM#c=Bvko64}cHJE448Q3FK{=2-p`65mMmqoD_k5Gn6q1ncWbc zG6*u_w(sXhOh}^wen5Ob!g3J}V}xb*=MGLBIaT}$7U74djCt9>QVe1S270J~K`go< zjz~08(1ZW;&<Q*U@Lc-nn`oo^3Q%FrXe9W;<cEWwP<Q;Qp8^&k;c3z0-4pu(=w|=f z5KDm9*mUg>mRm2XiDcS~x6Q3f;Ufez93xmmX|^Ab!bOv4-k@}!R0jJF4ZSh|OBK@W z0`|S?-uDS!wl@XWQ4JB~mj3ap{;#^hKV<kQCNQWw6U@O{GEl|}R-x>v!Xc!_vuOQu z5%NeXL@yD+k)jEK5G2^P7p<Fwz`y*{fz$AqFdTxk`VeU2{*HS)m~39jdsn0{=m7@d z>>f+u;5jOC0VOB;*&r~C;Q(r2)V<)A(&45Ge7XQ(6}L@MK}h7hJrxiGKI9+_ix2pR zPH712xsZ-9xG4Yu7=$vAmMPuoq!5RL=szS<5c+6n^M`WuzpxgN^Nxoj5RknbJP6|O z`MoF@10o6G7yS2w1c{xYwzor6`p%$vQK!52Ljyt<`aG~3M?#;DQDhPE9~Ov@RuVJ~ zgJ9`%hC_vLRrrU+Nu0$3zTSA88y%~;Itbm#FM>QuU8{Y86|qI12YIN?idl_r9Ho8M z0Pn;NP5`W3G7*&=_`*l1&1O)9QS>KSLeSEv?8wy4y_D6ko)>tpH`0DWDe#Ik_=xtx zcR5J<N_IndMbm#b9!>8)gxb+J_ImEdk|1-72h;w5i>`~ZPJsX89C%F}e%0GL#qcni e>I9@auZ3?fv8aL3>q7ltblBjbzy=!@(f&UQ*M1iO literal 0 HcmV?d00001 diff --git a/libburn/tags/ZeroThreeZero/cdrskin/make_timestamp.sh b/libburn/tags/ZeroThreeZero/cdrskin/make_timestamp.sh new file mode 100755 index 00000000..957a2c4e --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/make_timestamp.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# Create version timestamp cdrskin/cdrskin_timestamp.h +# to be executed within ./libburn-* resp ./cdrskin-* + +timestamp="$(date -u '+%Y.%m.%d.%H%M%S')" +echo "Version timestamp : $timestamp" +echo '#define Cdrskin_timestamP "'"$timestamp"'"' >cdrskin/cdrskin_timestamp.h + diff --git a/libburn/tags/ZeroThreeZero/cdrskin/wiki_plain.txt b/libburn/tags/ZeroThreeZero/cdrskin/wiki_plain.txt new file mode 100644 index 00000000..3a4b7284 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/cdrskin/wiki_plain.txt @@ -0,0 +1,230 @@ +-------------------------------------------------------------------------- + cdrskin Wiki - plain text copy +-------------------------------------------------------------------------- +[[Image(source:/libburn/trunk/cdrskin/doener_150x200_tr.gif)]] [http://en.wikipedia.org/wiki/D%C3%B6ner_kebab Doener] + +'''cdrskin is the cdrecord compatibility middleware of libburn.''' + +Its paragon, cdrecord, is a powerful GPL'ed burn program included in Joerg +Schilling's cdrtools. cdrskin strives to be a second source for the services +traditionally provided by cdrecord. Currently it does CD-R and CD-RW. +Its future ability to burn DVD media depends on the development of libburn. + +cdrskin does not contain any bytes copied from cdrecord's sources. +Many bytes have been copied from the message output of cdrecord +runs, though. The most comprehensive technical overview of cdrskin +can be found in [http://libburnia.pykix.org/browser/libburn/trunk/cdrskin/README?format=txt cdrskin/README]. + +About libburn API for burning CD: http://libburnia-api.pykix.org + +-------------------------------------------------------------------------- + +Appending sessions to an unclosed CD is restricted to write mode TAO. +(Users who have a burner which succeeds with a follow-up session via +cdrecord -sao : please contact us.) + +The development version of cdrskin is able to burn a single track to DVD+RW +or DVD-RW media. +For other DVD types and for appending sessions to ISO filesystems see the +advise to use dvd+rw-tools at the end of this text. + +-------------------------------------------------------------------------- + +About the command line options of cdrskin: + +They are described in detail in [http://scdbackup.sourceforge.net/man_1_cdrskin_devel.html#OPTIONS section OPTIONS] of +[http://scdbackup.sourceforge.net/man_1_cdrskin_devel.html man cdrskin] + +There are two families of options: cdrecord-compatible ones and options +which are specific to cdrskin. The latter are mostly used to configure +cdrskin for its task to emulate cdrecord. There are some, nevertheless, +which provide rather exotic unique features of cdrskin. + +The cdrecord-compatible options are listed in the output of +{{{ +cdrskin -help +}}} +where the option "help" has *one* dash. Online: [http://scdbackup.sourceforge.net/cdrskin_help_devel cdrskin -help] + +For these options you may expect program behavior that is roughly the +same as described in original man cdrecord . + +Online: http://cdrecord.berlios.de/old/private/man/cdrecord-2.0.html + +The cdrskin-specific options are listed by +{{{ +cdrskin --help +}}} +where the option "help" has *two* dashes. Online: [http://scdbackup.sourceforge.net/cdrskin__help_devel cdrskin --help] + +Some are very experimental and should only be +used in coordination with the libburnia developer team. +Some are of general user interest, though: + +-------------------------------------------------------------------------- + +--devices allows the sysadmin to scan the system for possible drives +and displays their detected properties. +The drives are listed one per line, with fields: +libburn-drive-number, sysadmin-device-file, permissions, vendor, type +{{{ +0 dev='/dev/sg0' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B' +}}} +This feature is valuable since cdrskin -scanbus will not give you +the device file name and its current permissions. +cdrskin will accept of course the proposed dev= option as address +for any usage of the drive. + +Different from cdrecord, cdrskin is intended to be run without special +privileges, i.e. no superuser setuid. It is intended that the sysadmin +controls drive accessability by rw-permissions of the drive rather than +by x-permission of the burn binary. To be usable with cdrskin, the drive +has to offer both, r- and w-permission. + +-------------------------------------------------------------------------- + +Non-cdrecord blank mode blank=format_overwrite is needed to bring a DVD-RW +disc from its initial profile "Sequential Recording" into profile state +"Restricted Overwrite". The latter is usable with cdrskin. +{{{ +cdrskin dev=/dev/sr0 -v blank=format_overwrite +}}} + +DVD-RW "Restricted Overwrite" and DVD+RW appear to cdrskin as blank media +which are capable of taking only a single track. This track may be positioned +on a 32KiB aligned address, though. +{{{ +cdrskin ... write_start_address=2412m ... +}}} + +-------------------------------------------------------------------------- + +fifo_start_at=<num> is a throughput enhancer for unsteady data streams +like they are produced by a compressing archiver program when piping to +CD on-the-fly. It makes better use of the general property of a FIFO +buffer to transport surplus bandwidth into the future. Yep. A time machine. +One-way, i fear. + +FIFO originally was introduced by cdrecord's author Joerg Schilling in order +to protect mediocre burner hardware from suffering buffer underruns +and thus producing misburns (at 1x speed on CD-R media at the price of a +DVD-RAM nowadays). This purpose would not justify a fifo any more - +given the limited life time of burners and the seamless underrun protection +of contemporary consumer drives. + +With an unsteady data stream the task of the buffer is to soak up peak +performance and to release it steadily at the drive's maximum speed. +The larger the buffer the more reserves can be built up and the longer +input drought can be compensated. + +Original cdrecord has the historical property, though, to first wait until +the buffer is completely filled. Best practice for fighting drive +underruns, of course. +With a very fat fs=# buffer (128 MB for 12x CD is not unrealistic) this +can cause a big delay until burning finally starts and takes its due time. + +fifo_start_at=<num> makes cdrskin start burning after the given number of bytes +is read rather than waiting for the FIFO to be completely full resp. the data +stream to end. It risks a few drive buffer underruns at the beginning of burn +- but modern drives stand this. + +Usage examples: +{{{ +cdrskin ... fs=128m fifo_start_at=20m ... +cdrskin ... fifo_start_at=0 ... +}}} + +Note: no FIFO can give you better average throughput than the average +throughput of the data source and the throughput of the burner. +It can be used, though, to bring the effective throughput very close +to the theoretical limit. Especially with high speed media. + +-------------------------------------------------------------------------- + +--no_rc allows you to surely ban influence from systemwide or user specific +default settings of cdrskin. Possible locations for such settings: + +/etc/default/cdrskin + +/etc/opt/cdrskin/rc + +/etc/cdrskin/cdrskin.conf + +$HOME/.cdrskinrc + +-------------------------------------------------------------------------- + +tao_to_sao_tsize=<num> allows the - actually unsupported - cdrecord option +-tao and defines a default track size to be used if - as custom with -tao - +no option tsize=# is given. + +Since -tao is supported in cdrskin-0.2.6 the TAO-to-SAO workaround has become +quite obsolete. Nevertheless, tao_to_sao_tsize= allows to preset a default +size for SAO mode which is in effect only if no track size is available. + +As in general with cdrskin tsize=# the data source does not have to provide +the full annouced amount of data. Missing data will be padded up by 0-bytes. +Surplus data is supposed to cause an error, though. The burn will then +be a failure in any way. + +-------------------------------------------------------------------------- + +dev_translation=<sep><from><sep><to> may be needed to foist cdrskin to +frontend programs of cdrecord which do *not* ask cdrecord -scanbus but +which make own assumptions and guesses about cdrecord's device addresses. + +Normally, cdrskin understands all addresses which are suitable for cdrecord +under Linux. See cdrskin/README, "Pseudo-SCSI Adresses". +This option is mainly for (yet unknown) exotic configurations or very +stubborn frontend programs. + +If a frontend refuses to work with cdrskin, look into the error protocol +of that frontend, look at the output of a run of cdrskin --devices and give +cdrskin the necessary hint. +Example: Your frontend insists in using "0,0,0" and --devices reported +dev='/dev/hdc' resp. cdrskin dev=ATA -scanbus reported "1,0,0" then this +would be the appropriate translation: +{{{ +dev_translation=+0,0,0+/dev/hdc +}}} +The "+" character is a separator to be choosen by you. +Currently i am not aware of the need to choose any other than "+" +unless you get playful with custom translations like +{{{ +dev_translation=-"cd+dvd"-1,0,0 +}}} +See http://scdbackup.sourceforge.net/k3b_on_cdrskin.html +for an illustrated example with K3b 0.10 . + +-------------------------------------------------------------------------- + +DVD advise: + +For burning of DVD media the cdrskin project currently advises to use +Andy Polyakov's dvd+rw-tools which despite their historic name burn +for me on above burner: DVD+RW, DVD+R, DVD-RW, DVD-R . + +http://fy.chalmers.se/~appro/linux/DVD+RW/tools + +They are not compatible or related to cdrecord resp. cdrecord-ProDVD +(now obsoleted by original source cdrtools cdrecord with identical +capabilities besides the license key). + +libburn and thus the cdrskin project are currently aquiring own capabilities +to burn to DVD media. For now restricted to DVD+RW and DVD-RW and to single +tracks. + +To my knowledge, Linux kernels 2.6 do write to DVD+RW via block devices as +they would write to a traditional tape device. Try old tape archiver +commands with addresses like /dev/sr0 or /dev/hdc rather than /dev/mt0 . +I have heard rumors that DVD-RW in mode "restricted overwrite" would be +block device ready, too. My burner is not a real friend of DVD-RW and +in an experiment the burn worked fine - but the result was not identical +to the stream sent to the device. I had similar failure with DVD-RAM, too. + +Beware of the impact of a slow block device on overall system i/o buffering. +It is wise to curb its input to a speed which it is able to deliver to media. +Else your i/o dedicated RAM might buffer a big amount of stream data. + +-------------------------------------------------------------------------- + diff --git a/libburn/tags/ZeroThreeZero/configure.ac b/libburn/tags/ZeroThreeZero/configure.ac new file mode 100644 index 00000000..98c9bd57 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/configure.ac @@ -0,0 +1,117 @@ +AC_INIT([libburn], [0.2.7], [http://libburnia.pykix.org]) +AC_PREREQ([2.50]) +dnl AC_CONFIG_HEADER([config.h]) + +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +AM_INIT_AUTOMAKE([subdir-objects]) + +dnl A61101 This breaks Linux build (makes 32 bit off_t) +dnl http://sourceware.org/autobook/autobook/autobook_96.html says +dnl one must include some config.h and this was a pitfall. +dnl So why dig the pit at all ? +dnl AM_CONFIG_HEADER(config.h) + +dnl Making releases: +dnl BURN_MICRO_VERSION += 1; +dnl BURN_INTERFACE_AGE += 1; +dnl BURN_BINARY_AGE += 1; +dnl if any functions have been added, set BURN_INTERFACE_AGE to 0. +dnl if backwards compatibility has been broken, +dnl set BURN_BINARY_AGE and BURN_INTERFACE_AGE to 0. +dnl +dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match +dnl +BURN_MAJOR_VERSION=0 +BURN_MINOR_VERSION=2 +BURN_MICRO_VERSION=7 +BURN_INTERFACE_AGE=0 +BURN_BINARY_AGE=0 +BURN_VERSION=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION.$BURN_MICRO_VERSION + +AC_SUBST(BURN_MAJOR_VERSION) +AC_SUBST(BURN_MINOR_VERSION) +AC_SUBST(BURN_MICRO_VERSION) +AC_SUBST(BURN_INTERFACE_AGE) +AC_SUBST(BURN_BINARY_AGE) +AC_SUBST(BURN_VERSION) + +dnl Libtool versioning +LT_RELEASE=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION +LT_CURRENT=`expr $BURN_MICRO_VERSION - $BURN_INTERFACE_AGE` +LT_REVISION=$BURN_INTERFACE_AGE +LT_AGE=`expr $BURN_BINARY_AGE - $BURN_INTERFACE_AGE` +LT_CURRENT_MINUS_AGE=`expr $LT_CURRENT - $LT_AGE` + +AC_SUBST(LT_RELEASE) +AC_SUBST(LT_CURRENT) +AC_SUBST(LT_REVISION) +AC_SUBST(LT_AGE) +AC_SUBST(LT_CURRENT_MINUS_AGE) + +AC_PREFIX_DEFAULT([/usr/local]) +test "$prefix" = "NONE" && prefix=$ac_default_prefix + +AM_MAINTAINER_MODE + +AM_PROG_CC_C_O +AC_C_CONST +AC_C_INLINE +AC_C_BIGENDIAN + +dnl Large file support +AC_SYS_LARGEFILE +AC_FUNC_FSEEKO +AC_CHECK_FUNC([fseeko]) +if test ! $ac_cv_func_fseeko; then + AC_ERROR([Libburn requires largefile support.]) +fi + +AC_PROG_LIBTOOL +AC_SUBST(LIBTOOL_DEPS) +LIBTOOL="$LIBTOOL --silent" + +AC_PROG_INSTALL + +AC_CHECK_HEADERS() + +AC_CHECK_MEMBER([struct tm.tm_gmtoff], + [AC_DEFINE(HAVE_TM_GMTOFF, 1, + [Define this if tm structure includes a tm_gmtoff entry.])], + , + [#include <time.h>]) + +THREAD_LIBS=-lpthread +AC_SUBST(THREAD_LIBS) + +TARGET_SHIZZLE +AC_SUBST(ARCH) +AC_SUBST(LIBBURN_ARCH_LIBS) + +dnl Add compiler-specific flags + +dnl See if the user wants aggressive optimizations of the code +AC_ARG_ENABLE(debug, +[ --enable-debug Disable aggressive optimizations [default=yes]], + , enable_debug=yes) +if test x$enable_debug != xyes; then + if test x$GCC = xyes; then + CFLAGS="$CFLAGS -O3" + CFLAGS="$CFLAGS -fexpensive-optimizations" + fi + CFLAGS="$CFLAGS -DNDEBUG" +else + if test x$GCC = xyes; then + CFLAGS="$CFLAGS -g -pedantic -Wall" + fi + CFLAGS="$CFLAGS -DDEBUG" +fi + +AC_CONFIG_FILES([ + Makefile + doc/doxygen.conf + version.h + libburn-1.pc + ]) +AC_OUTPUT diff --git a/libburn/tags/ZeroThreeZero/doc/Makefile b/libburn/tags/ZeroThreeZero/doc/Makefile new file mode 100644 index 00000000..062350dd --- /dev/null +++ b/libburn/tags/ZeroThreeZero/doc/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/libburn/tags/ZeroThreeZero/doc/comments b/libburn/tags/ZeroThreeZero/doc/comments new file mode 100644 index 00000000..c40ac902 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/doc/comments @@ -0,0 +1,136 @@ +/** + @author Mario Danic, Thomas Schmitt + + @mainpage Libburnia Documentation Index + + @section intro Introduction + +Libburnia is an open-source project for reading, mastering and writing +optical discs. For now this means only CD-R and CD-RW. +Support for DVD+RW and DVD-RW is emerging. + + +The project comprises of several more or less interdependent parts which +together strive to be a usable foundation for application development. +These are libraries, language bindings, and middleware binaries which emulate +classical (and valuable) Linux tools. + +Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems +we would need : login on a development machine resp. a live OS on CD or DVD, +advise from a system person about the equivalent of Linux sg or FreeBSD CAM, +volunteers for testing of realistic use cases. + +We have a workable code base for burning data and audio CDs. The burn API is +quite comprehensively documented and can be used to build a presentable +application. +We have a functional binary which emulates the core use cases of cdrecord in +order to prove that usability, and in order to allow you to explore libburn's +scope by help of existing cdrecord frontends. + +@subsection components The project components (list subject to growth, hopefully): + +- libburn is the library by which preformatted data get onto optical media. + It uses either /dev/sgN (e.g. on kernel 2.4 with ide-scsi) or + /dev/hdX (e.g. on kernel 2.6). + libburn is the foundation of our cdrecord emulation. + +- libisofs is the library to pack up hard disk files and directories into a + ISO 9660 disk image. This may then be brought to CD via libburn. + libisofs is to be the foundation of our upcoming mkisofs emulation. + +- cdrskin is a limited cdrecord compatibility wrapper for libburn. + cdrecord is a powerful GPL'ed burn program included in Joerg + Schilling's cdrtools. cdrskin strives to be a second source for + the services traditionally provided by cdrecord. + cdrskin does not contain any bytes copied from cdrecord's sources. + Many bytes have been copied from the message output of cdrecord + runs, though. + See cdrskin/README for more. + +- "test" is a collection of application gestures and examples given by the + authors of the library features. The main API example of libburn + is named test/libburner.c . + Explore these examples if you look for inspiration. + +We plan to be a responsive upstream. Bear with us. + + + @section using Using the libraries + +Our build system is based on autotools. +User experience tells us that you will need at least autotools version 1.7. + +To build libburn and its subprojects it should be sufficient to go into +its toplevel directory and execute + +- ./bootstrap (needed if you downloaded from SVN) + +- ./configure + +- make + +To make the libraries accessible for running resp. developing applications + +- make install + +Both libraries are written in C language and get built by autotools. +Thus we expect them to be useable by a wide range of Linux-implemented +languages and development tools. + + +@section libburner Libburner + +libburner is a minimal demo application for the library libburn +(see: libburn/libburn.h) as provided on http://libburn.pykix.org . +It can list the available devices, can blank a CD-RW and +can burn to CD-R or CD-RW. +New: burning to DVD+/-RW (single data track, single session only). + +It's main purpose, nevertheless, is to show you how to use libburn and also +to serve the libburnia team as reference application. libburner does indeed +define the standard way how above three gestures can be implemented and +stay upward compatible for a good while. + + @subsection libburner-help Libburner --help +<pre> +Usage: test/libburner + [--drive <address>|<driveno>|"-"] [--audio] + [--blank_fast|--blank_full] [--try_to_simulate] + [--multi] [one or more imagefiles|"-"] +Examples +A bus scan (needs rw-permissions to see a drive): + test/libburner --drive - +Burn a file to drive chosen by number, leave appendable: + test/libburner --drive 0 --multi my_image_file +Burn a file to drive chosen by persistent address, close: + test/libburner --drive /dev/hdc my_image_file +Blank a used CD-RW (is combinable with burning in one run): + test/libburner --drive /dev/hdc --blank_fast +Burn two audio tracks + lame --decode -t /path/to/track1.mp3 track1.cd + test/dewav /path/to/track2.wav -o track2.cd + test/libburner --drive /dev/hdc --audio track1.cd track2.cd +Burn a compressed afio archive on-the-fly: + ( cd my_directory ; find . -print | afio -oZ - ) | \ + test/libburner --drive /dev/hdc - +To be read from *not mounted* CD via: afio -tvZ /dev/hdc +Program tar would need a clean EOF which our padded CD cannot deliver. +</pre> +libburner has two companions, telltoc and dewav, which help to perform some +peripheral tasks of burning. + +telltoc prints a table of content (sessions, tracks and leadouts), it tells +about type and state of CD media, and also is able to provide the necessary +multi-session information for program mkisofs option -C. +See: test/telltoc --help. + +dewav extracts raw byte-swapped audio data from files of format .wav (MS WAVE) +or .au (SUN Audio). See example in libburner --help. + + @subsection libburner-source Sourceode of libburner + +Click on blue names of functions, structures, variables, etc in oder to +get to the according specs of libburn API or libburner sourcecode. + +@include libburner.c +*/ diff --git a/libburn/tags/ZeroThreeZero/doc/doxygen.conf.in b/libburn/tags/ZeroThreeZero/doc/doxygen.conf.in new file mode 100644 index 00000000..640835bc --- /dev/null +++ b/libburn/tags/ZeroThreeZero/doc/doxygen.conf.in @@ -0,0 +1,186 @@ +# Doxyfile 1.2.18 + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = @PACKAGE_NAME@ +PROJECT_NUMBER = @PACKAGE_VERSION@ +OUTPUT_DIRECTORY = +OUTPUT_LANGUAGE = English +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = @top_srcdir@ +INTERNAL_DOCS = NO +STRIP_CODE_COMMENTS = NO +CASE_SENSE_NAMES = NO +SHORT_NAMES = NO +HIDE_SCOPE_NAMES = NO +VERBATIM_HEADERS = YES +SHOW_INCLUDE_FILES = YES +JAVADOC_AUTOBRIEF = YES +MULTILINE_CPP_IS_BRIEF = YES +DETAILS_AT_TOP = YES +INHERIT_DOCS = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 4 +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ALIASES = +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +SHOW_USED_FILES = YES +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = libburn doc test +FILE_PATTERNS = libburn.h comments libburner.c +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = test +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = OB OTK _ +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = doc/html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 200 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = letter +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_SCHEMA = +XML_DTD = +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = DOXYGEN +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +GRAPHICAL_HIERARCHY = NO +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/libburn/tags/ZeroThreeZero/libburn-1.pc.in b/libburn/tags/ZeroThreeZero/libburn-1.pc.in new file mode 100644 index 00000000..38f4530e --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn-1.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libburn +Description: Disc reading/writing library +Version: @VERSION@ +Requires: +Libs: -L${libdir} -lburn +Libs.private: @THREAD_LIBS@ @LIBBURN_ARCH_LIBS@ +Cflags: -I${includedir}/libburn diff --git a/libburn/tags/ZeroThreeZero/libburn/Makefile b/libburn/tags/ZeroThreeZero/libburn/Makefile new file mode 100644 index 00000000..062350dd --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/libburn/tags/ZeroThreeZero/libburn/Makefile.am b/libburn/tags/ZeroThreeZero/libburn/Makefile.am new file mode 100644 index 00000000..144ece71 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/Makefile.am @@ -0,0 +1,65 @@ +pkgconfigdir=$(libdir)/pkgconfig +libincludedir=$(includedir)/libburn + +lib_LTLIBRARIES = libburn.la + +libburn_la_SOURCES = \ + async.c \ + async.h \ + crc.c \ + crc.h \ + debug.c \ + debug.h \ + drive.c \ + drive.h \ + file.c \ + file.h \ + init.c \ + init.h \ + lec.c \ + lec.h \ + message.c \ + message.h \ + mmc.c \ + mmc.h \ + null.c \ + null.h \ + options.c \ + options.h \ + read.c \ + read.h \ + sbc.c \ + sbc.h \ + sector.c \ + sector.h \ + sg.c \ + sg.h \ + spc.c \ + spc.h \ + source.h \ + source.c \ + structure.c \ + structure.h \ + toc.c \ + toc.h \ + transport.h \ + util.c \ + util.h \ + write.c \ + write.h + +libinclude_HEADERS = libburn.h + +## ========================================================================= ## +indent_files = $(libburn_la_SOURCES) + +indent: $(indent_files) + indent -bad -bap -nbbb -nbbo -nbc -bli0 -br -bls \ + -cdw -ce -cli0 -ncs -nbfda -i8 -l79 -lc79 \ + -lp -saf -sai -nprs -npsl -saw -sob -ss -ut \ + -sbi0 -nsc -ts8 -npcs -ncdb -fca \ + $^ + +.PHONY: indent + +## ========================================================================= ## diff --git a/libburn/tags/ZeroThreeZero/libburn/asserts.txt b/libburn/tags/ZeroThreeZero/libburn/asserts.txt new file mode 100644 index 00000000..b0791582 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/asserts.txt @@ -0,0 +1,792 @@ +List of assert() calls in libburn. 6 Oct 2006. + +Format: + +------------------------------------------------------------------------------ + + Number) grep'ed line + (++ before number means: is fully done, + means is done so far ) +function(): +Description of abort condition. + +Possible callers and their relation to the abort condition. + +: Error Evaluation +=> Consequences + +Eventual implementation timestamp + +------------------------------------------------------------------------------ + +++ 1) libburn/async.c: assert(a != NULL); /* wasn't found.. this should not be possible */ +static remove_worker(): +A thread describing structure (struct w_list) could not be found in +order to be released. + +Called by API burn_drive_scan() +Called by static erase_worker_func() , thread under API burn_disc_erase() +Called by static write_disc_worker_func(), thread under API burn_disc_write() +All three want to clean up after they are done. + +: Severe Libburn Error +=> issue LIBDAX_MSGS_SEV_WARNING + +ts A61006 + +------------------------------------------------------------------------------ + +++ 2) libburn/async.c: assert(!(workers && workers->drive)); +API burn_drive_scan(): +Before spawning a thread, the function refuses work because another +drive activity is going on. + +: Severe Application Error +=> return -1; redefine @return in API , issue LIBDAX_MSGS_SEV_SORRY + +ts A61006 + +------------------------------------------------------------------------------ + ++ 3) libburn/async.c: assert(workers == NULL); +API burn_drive_scan(): +After thread is done and remover_worker() succeeded, there is still a +worker registered. Shall probably detect roguely appeared burn or +erase runs. (I consider to install a mutex shielded function for that.) + +: Severe Libburn Error +=> Same as 1) + +ts A61006 + +------------------------------------------------------------------------------ + +++ 4) libburn/async.c: assert(drive); + libburn/async.c: assert(!SCAN_GOING()); + libburn/async.c: assert(!find_worker(drive)); +API burn_disc_erase(): +Wants to see a drive (assumes NULL == 0), wants to see no scan and +wants to see no other worker on that drive. I.e. this would tolerate +a parallel activity on another drive. + +: Severe Application Error +=> (no return value), issue LIBDAX_MSGS_SEV_SORRY + +ts A61006 + +------------------------------------------------------------------------------ + +++ 5) libburn/async.c: assert(!SCAN_GOING()); + libburn/async.c: assert(!find_worker(opts->drive)); +API burn_disc_write(): +Same as 4) + +: Severe Application Error +=> Same as 4) + +ts A61006 + +--------------------------------------------------------------------- + +++ 6) libburn/drive.c: assert(d->busy == BURN_DRIVE_IDLE); +API burn_drive_release(): +A drive is not idle on release. + +: Severe Application Error +=> Same as 4) + +ts A61007 + +------------------------------------------------------------------------------ + +++ 7) libburn/drive.c: assert(d->released); +burn_wait_all() +A drive is found grabbed. + +Called by burn_drive_scan_sync(), thread under API burn_drive_scan() +Called by API burn_finish + +: Severe Application Error +=> rename and redefine burn_wait_all() : now burn_drives_are_clear() +=> change all use of burn_wait_all() +=> Move tests up to burn_drive_scan() +=> There: return -1; issue LIBDAX_MSGS_SEV_SORRY + +ts A61007 + +------------------------------------------------------------------------------ + +++ 8) libburn/drive.c: assert(!d->released); +API burn_disc_get_status() +Attempt to read status of non-grabbed drive. + +: Severe Application Error +=> extend enum burn_disc_status by BURN_DISC_UNGRABBED +=> return BURN_DISC_UNGRABBED, issue LIBDAX_MSGS_SEV_SORRY + +ts A61007 + +------------------------------------------------------------------------------ + +++ 9) libburn/drive.c: assert( /* (write_type >= BURN_WRITE_PACKET) && */ +burn_drive_get_block_types(): +Will not work on BURN_WRITE below BURN_WRITE_RAW. + +Called by -nobody- ? + +: Severe Application Error +=> inactivate unused function + +ts A61007 + +------------------------------------------------------------------------------ + +++ 10) libburn/drive.c: assert(d->idata); + libburn/drive.c: assert(d->mdata); +static drive_getcaps(): +sg.c:enumerate_common() did not succeed in creating a proper struct burn_drive +Called by burn_drive_scan_sync() + +: Severe System Error +=> This could possibly really stay an abort() because the reason is + a plain failure of the system's memory management. +=> Detect this failure already in enumerate_common(), + issue LIBDAX_MSGS_SEV_FATAL, return + +ts A61007 + +------------------------------------------------------------------------------ + +++ 11) libburn/drive.c: assert(burn_running); +burn_drive_scan_sync(): +The library was not initialized. + +Called as thread by API burn_drive_scan() + +: Severe Application Error +=> Move this test up to burn_drive_scan() +=> There: return -1; redefine @return in API , issue LIBDAX_MSGS_SEV_FATAL + +ts A61007 + +------------------------------------------------------------------------------ + +++ 12) libburn/drive.c: assert(d->released == 1); +burn_drive_scan_sync(): +Inactivated + +: (Severe Application Error) +=> throw out inactivated code + +ts A61007 + +------------------------------------------------------------------------------ + +++ 13) libburn/drive.c: assert(strlen(d->devname) < BURN_DRIVE_ADR_LEN); +burn_drive_raw_get_adr(): +An enumerated device address is longer than the API's maximum length + +Called by API burn_drive_get_adr() +Called by API burn_drive_obtain_scsi_adr() + +: Severe Libburn Error +=> return -1; in all three functions, enhance burn_drive_get_adr @return docs +=> issue LIBDAX_MSGS_SEV_SORRY + +ts A61007 + +------------------------------------------------------------------------------ + +++ 14) libburn/drive.c: assert(drive_info->drive!=NULL); +API burn_drive_get_adr(): +Drive info has no drive attached. + +: Severe Libburn Error (unlikely, will eventually SIGSEGV on NULL) +=> delete assert + +ts A61007 + +------------------------------------------------------------------------------ + +++ 15) libburn/init.c: assert(burn_running); +API burn_finish(): +The library is not initialized + +: Severe Application Error +=> return (assume no msg system) + +ts A61007 + +------------------------------------------------------------------------------ + +++ 16) libburn/init.c: assert(burn_running); +API burn_preset_device_open(): +The library is not initialized + +: Severe Application Error +=> return (assume no msg system) + +ts A61007 + +------------------------------------------------------------------------------ + +++ 17) libburn/mmc.c: assert(o->drive == d); +mmc_close_disc(): +alias: struct burn_drive.close_disc() +Parameters struct burn_drive and struct burn_write_opts do not match + +Called by -nobody- ? + +( => Disable unused function ? ) +=> removed redundant parameter struct burn_drive + +ts A61009 + +------------------------------------------------------------------------------ + +++ 18) libburn/mmc.c: assert(o->drive == d); +mmc_close_session(): +Same as 17) +alias: struct burn_drive.close_session() + +Called by -nobody- ? + +( => Disable unused function ? ) +=> removed redundant parameter struct burn_drive + +ts A61009 + +------------------------------------------------------------------------------ + +++ 19) libburn/mmc.c: assert(buf->bytes >= buf->sectors); /* can be == at 0... */ +mmc_write_12(): +- Unclear what .bytes and .sectors mean in struct buffer - + +Called by -nobody- ? + +=> problems with filling the write buffer have to be handled by callers +=> delete assert + +ts A61009 + +------------------------------------------------------------------------------ + +++ 20) libburn/mmc.c: assert(buf->bytes >= buf->sectors); /* can be == at 0... */ +mmc_write(): +- Unclear what .bytes and .sectors mean in struct buffer - + +libburn/mmc.c: c.page->sectors = errorblock - start + 1; +mmc_read_sectors() by toc_find_modes() by mmc_read_toc() alias drive.read_toc() +by burn_drive_grab() +This seems to be unrelated to mmc_write(). + +libburn/sector.c: out->sectors++; +get_sector() +Seems to hand out sector start pointer in opts->drive->buffer +and to count reservation transactions as well as reserved bytes. +Ensures out->bytes >= out->sectors + + +libburn/mmc.c: c.page->bytes = s->count * 8; +mmc_send_cue_sheet() +Does not use mmc_write() but directly (sg_)issue_command() + +libburn/sector.c: out->bytes += seclen; +get_sector() +See above +Ensures out->bytes >= out->sectors + +libburn/spc.c: c.page->bytes = 8 + 2 + d->mdata->retry_page_length; +spc_select_error_params() +Does not use mmc_write() but directly (sg_)issue_command() + +libburn/spc.c: c.page->bytes = 8 + 2 + d->mdata->write_page_length; +spc_select_error_params() +Does not use mmc_write() but directly (sg_)issue_command() + +libburn/spc.c: c.page->bytes = 8 + 2 + 0x32; +spc_probe_write_modes() +Does not use mmc_write() but directly (sg_)issue_command() + +alias struct burn_drive.write() +Called by static get_sector, by many +Called by burn_write_flush +Called by burn_write_track + +=> problems with filling the write buffer have to be handled by callers +=> delete assert + +ts A61009 + +------------------------------------------------------------------------------ + +++ 21) libburn/mmc.c: assert(((dlen - 2) % 11) == 0); +mmc_read_toc(): +- Is defunct - + +=> :) + +ts A61009 + +------------------------------------------------------------------------------ + +++ 22) libburn/mmc.c: assert(len >= 0); +mmc_read_sectors(): +Catches a bad parameter + +alias: struct burn_drive.read_sectors() +Called by API burn_disc_read() , - is defunct -, one could catch the problem +Called by toc_find_modes(), problem cannot occur: mem.sectors = 1; + +: Severe Libburn Error +(=> in burn_disc_read() check page.sectors before d->read_sectors() ) +=> :) + +ts A61009 + +------------------------------------------------------------------------------ + +++ 23) libburn/mmc.c: assert(d->busy); +mmc_read_sectors(): +Catches use of a drive that is not marked as busy + +alias: struct burn_drive.read_sectors() +Called by API burn_disc_read() , - is defunct -, busy = BURN_DRIVE_READING; +Called by toc_find_modes(), does the same assert. To be solved there. + +: Severe Libburn Error +=> :) + +ts A61009 + +------------------------------------------------------------------------------ + +++ 24) libburn/options.c: assert(0); +API burn_write_opts_set_write_type(): +Detects unsuitable enum burn_write_types write_type and int block_type. +API promises return 0 on failure + +: Severe Application Error +=> issue LIBDAX_MSGS_SEV_SORRY +=> should also detect problem of 26) : wrong write_type,block_type combination + by calling sector_get_outmode() and checking for -1 +=> should also detect problem of 41) : unknown block_type + by spc_block_type() and checking for -1 +=> delete assert(0) + +ts A61007 + +------------------------------------------------------------------------------ + +++ 25) libburn/read.c: assert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000)); + libburn/read.c: assert(!d->busy); + libburn/read.c: assert(d->toc->valid); + libburn/read.c: assert(o->datafd != -1); +API burn_disc_read(): +- ? - + +burn_disc_read() is defunct +OPTIONS_VERSION does not occur outside this line + +( => one would return ) +( 22) => catch page.sectors<0 before d->read_sectors() ) +( 37) => catch ! d->mdata->valid ) +=> :) + +ts A61007 + +------------------------------------------------------------------------------ + +++ 26) libburn/sector.c: assert(0); /* return BURN_MODE_UNIMPLEMENTED :) */ +static get_outmode(): +burn_write_opts is wrongly programmed with .write_type and .block_type + +: Severe Application Error +=> This gets handled by burn_write_opts_set_write_type() + ts A61007 by new semi-public sector_get_outmode() +=> delete assert() + +ts A61007 + +------------------------------------------------------------------------------ + +++ 27) libburn/sector.c: assert(outlen >= inlen); + libburn/sector.c: assert(outmode & BURN_MODE_RAW); + libburn/sector.c: assert(offset != -1); +static convert_data(): +Several unacceptable settings within struct burn_write_opts + +Called by sector_toc() sector_pregap() sector_postgap() sector_lout() + sector_data() + +: Severe Application Error +=> change return type of convert_data() +=> all callers interpret return value and eventually return failure + +ts A61007 + +------------------------------------------------------------------------------ + +++ 28) libburn/sector.c: assert(0); +static char_to_isrc(): +Called by subcode_user() with data set by API burn_track_set_isrc() +Some character conversion fails on wrong input + +: Severe Application Error +=> burn_track_set_isrc() has to make sure that only good data are set +=> char_to_isrc() returns 0 as default +=> delete assert() + +ts A61008 + +------------------------------------------------------------------------------ + +++ 29) libburn/sector.c: assert(qmode == 1 || qmode == 2 || qmode == 3); +subcode_user(): +- can not happen - + +: Unknown reason of assert() +=> remove assert() + +ts A61010 + +------------------------------------------------------------------------------ + +++ 30) libburn/sector.c: assert(modebyte == 1); +sector_headers(): +Does only accept modes BURN_AUDIO, BURN_MODE1 or write_type BURN_WRITE_SAO + +Called by sector_toc() sector_pregap() sector_postgap() sector_lout() + sector_data() + +: Severe Libburn Error +=> new functions sector_headers_is_ok(), burn_disc_write_is_ok() + help to catch problem in API burn_disc_write() +=> issue LIBDAX_MSGS_SEV_FATAL + +ts A61009 + +------------------------------------------------------------------------------ + +++ 31) libburn/sector.c: assert(0); +process_q() +- defunct - + +=> :) + +ts A61009 + +------------------------------------------------------------------------------ + +++ 32) libburn/sg.c: assert("drive busy" == "non fatal"); +sg_handle_busy_device(): +Intentional abort preset by the app + +=> change to abort() + +ts A61007 + +------------------------------------------------------------------------------ + +++ 33) libburn/sg.c: assert(fd != -1337); +sg_grab(): +The drive device file could not be opened + +:Severe External Problem +=> obsolete by normal drive open failure handling + +ts A61007 + +------------------------------------------------------------------------------ + +++ 34) libburn/sg.c: assert(!c->page); +sg_issue_command(): +An SCSI command of direction NO_TRANSFER may not have a .page != NULL. + +Since it is about exposing a libburn detail towards the sg driver, i believe +it is sufficient to just not use it. + +: Libburn Error +=> enhance internal logics of sg_issue_command() + +ts A61007 + +------------------------------------------------------------------------------ + +++ 35) libburn/sg.c: assert(c->page->bytes > 0); +sg_issue_command(): +An SCSI command of direction TO_DRIVE wants to transfer 0 bytes. + +: Severe Libburn Error +=> set command.error = 1 and return 0 + +ts A61010 + +------------------------------------------------------------------------------ + +++ 36) libburn/sg.c: assert(err != -1); +sg_issue_command(): +The transfer of the command via ioctl() failed + +: Severe Transport Level Problem +=> close drive fd, set idle and released +=> set command.error = 1 and return -1 + +ts A61010 + +------------------------------------------------------------------------------ + +++ 37) libburn/spc.c: assert(d->mdata->valid); +spc_select_error_params(): +Drive was not properly programmed + +alias struct burn_drive.send_parameters() +Called by burn_disc_read, - defunct - + +: Severe Application Error +=> moved up as mangled assert to burn_disc_read() + +ts A61007 + +------------------------------------------------------------------------------ + +++ 38) libburn/spc.c: assert(d->mdata->cdr_write || d->mdata->cdrw_write || +spc_sense_write_params(): +Drive does not offer write of any known media type + +alias struct burn_drive.read_disc_info() +Called by API burn_drive_grab (assert test made there in soft) + +: Severe Command Level Problem +=> remove assert() + +ts A61007 + +------------------------------------------------------------------------------ + +++ 39) libburn/spc.c: assert(o->drive == d); +spc_select_write_params(): +Drive does not match struct burn_write_opts + +alias struct burn_drive.send_write_parameters() +Called by mmc_close_disc() (-defunct- ?), mmc_close_session() (-defunct- ?), + burn_write_track() (d = o->drive;), + burn_disc_write_sync() d = (o->drive;) + +: Severe Libburn Error +=> remove assert() + +ts A61007 + +------------------------------------------------------------------------------ + +++ 40) libburn/spc.c: assert(d->mdata->valid); +spc_select_write_params(): +Drive was not properly programmed + +Called by (see 39) + burn_write_track() by burn_write_session() by burn_disc_write_sync() + burn_disc_write_sync() indirectly by API burn_disc_write() + +: Severe Libburn Error +=> caught in burn_disc_write() now + +ts A61007 + +------------------------------------------------------------------------------ + +++ 41) libburn/spc.c: assert(0); +spc_block_type(): +Unknown value with enum burn_block_types + +Called by spc_select_write_params, uses burn_write_opts.block_type, + set by API burn_write_opts_set_write_type() + +: Severe Application Error +=> catch in API burn_write_opts_set_write_type + by calling spc_block_type() +=> delete assert + +ts A61007 + +------------------------------------------------------------------------------ + +++ 42) libburn/structure.c: assert(!(pos > BURN_POS_END));\ +macro RESIZE +An illegal list index is given by the app. + +( TO->NEW##s obviusly means to append "s" to cpp result of TO->NEW ) +Used by API burn_session_add_track() and API burn_disc_add_session() + +: Severe Application Error +=> replace assert by if-and-return-0 + +ts A61008 + +------------------------------------------------------------------------------ + +++ 43) libburn/structure.c: assert(s->track != NULL); +API burn_session_remove_track() +An application supplied pointer is NULL + +: Severe Application Error +=> replace by if-and-return-0 + +ts A61008 + +------------------------------------------------------------------------------ + +++ 44) libburn/structure.c: assert((country[i] >= '0' || country[i] < '9') && + libburn/structure.c: assert((owner[i] >= '0' || owner[i] < '9') && + libburn/structure.c: assert(year <= 99); + libburn/structure.c: assert(serial <= 99999); +API burn_track_set_isrc(): +Illegal texts supplied by application. +The logical expression is always true ! + +: Severe Application Error +=> issue LIBDAX_MSGS_SEV_SORRY and return +=> delete assert +=> delete assert 28) in char_to_isrc() + +ts A61008 + +------------------------------------------------------------------------------ + +++ 45) libburn/toc.c: assert(0); /* unhandled! find out ccd's +static write_clonecd2(): + + - defunct -, - unused - + +=> mangle assert + +ts A61008 + +------------------------------------------------------------------------------ + +++ 46) libburn/toc.c: assert(d->busy); +toc_find_modes(): +The drive to work on is not marked busy + +Called by mmc_read_toc() alias read_toc() by ... burn_drive_grab() + +: Severe Libburn Error +=> to be prevented on the higher levels +=> delete assert + +ts A61008 + +------------------------------------------------------------------------------ + +++ 47) libburn/util.c: assert(s); +burn_strdup() +Abort on NULL string which would elsewise cause a SIGSEGV + +Used once in enumerate_common() with a string that worked with open(2) before + +: Severe Libburn Error +=> delete assert + +ts A61008 + +------------------------------------------------------------------------------ + +++ 48) libburn/util.c: assert(s); +burn_strndup(): - unused - +Same as 47 + +: Severe Libburn Error +=> return NULL +=> delete assert + +ts A61008 + +------------------------------------------------------------------------------ + +++ 49) libburn/util.c: assert(n > 0); +burn_strndup(): - unused - +Prevent problems by negative copy length + +: Severe Libburn Error +=> return NULL +=> delete assert + +ts A61008 + +------------------------------------------------------------------------------ + +++ 50) libburn/write.c: assert(0); +static type_to_ctrl(): +Unsuitable mode to be converted into "ctrl" +Called by static type_to_form() finally burn_create_toc_entries() + +: Severe Application Error +=> to be caught in burn_track_define_data by calling for test type_to_form() +=> return -1; + +ts A61008 + +------------------------------------------------------------------------------ + +++ 51) libburn/write.c: assert(0); + libburn/write.c: assert(0); /* XXX someone's gonna want this sometime */ +static type_to_form(): +Does not like BURN_MODE0 or BURN_MODE2 but tolerates unknown modes + +Called by static burn_create_toc_entries() by burn_disc_write_sync() + +: Undocumented Libburn Restriction +=> set *form = -1 , *ctladr = 0xff , return +=> make function non-static +=> call for test in API burn_track_define_data() + +ts A61009 + +------------------------------------------------------------------------------ + +++ 52) libburn/write.c: assert(ptr); +static add_cue(): +realloc() failed + +Called by burn_create_toc_entries() by burn_disc_write_sync() +(burn_create_toc_entries is ignorant towards own potential memory problems) +(This could possibly really stay an abort() because the reason is + a plain failure of the system's memory management.) + +: Severe System Error +=> change return type of add_cue to int +=> react on return -1 in burn_create_toc_entries, return NULL on failure +=> abort burn_disc_write_sync() on NULL return + +ts A61009 + +------------------------------------------------------------------------------ + +++ 53) libburn/write.c: assert(d->toc_entry == NULL); +burn_create_toc_entries(): +Multiple usage of struct burn_drive.toc_entry + +Called by burn_disc_write_sync() +This will probably trigger an abort with disc->sessions > 1 +(disc->sessions is incremented in macro RESIZE() as "NEW##s") + +: Design Problem +( => ? disallow multiple sessions ? ) +=> replace assert by soft means and wait what happens + +ts A61009 + +------------------------------------------------------------------------------ + +++ 54) libburn/write.c: assert(0); +burn_sector_length(): +Only BURN_AUDIO, BURN_MODE_RAW, BURN_MODE1 are allowed + +Called by get_sector(), convert_data(), ... + +=> call burn_sector_length() for test in API burn_track_define_data() +=> replace assert by -1 + +ts A61009 + +------------------------------------------------------------------------------ + diff --git a/libburn/tags/ZeroThreeZero/libburn/async.c b/libburn/tags/ZeroThreeZero/libburn/async.c new file mode 100644 index 00000000..595c07ec --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/async.c @@ -0,0 +1,365 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "libburn.h" +#include "transport.h" +#include "drive.h" +#include "write.h" +#include "options.h" +#include "async.h" +#include "init.h" +#include "back_hacks.h" + +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> + +/* +#include <a ssert.h> +*/ +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + +#define SCAN_GOING() (workers && !workers->drive) + +typedef void *(*WorkerFunc) (void *); + +struct scan_opts +{ + struct burn_drive_info **drives; + unsigned int *n_drives; + + int done; +}; + +struct erase_opts +{ + struct burn_drive *drive; + int fast; +}; + +/* ts A61230 */ +struct format_opts +{ + struct burn_drive *drive; + off_t size; + int flag; +}; + +struct write_opts +{ + struct burn_drive *drive; + struct burn_write_opts *opts; + struct burn_disc *disc; +}; + + +struct w_list +{ + struct burn_drive *drive; + pthread_t thread; + + struct w_list *next; + + union w_list_data + { + struct scan_opts scan; + struct erase_opts erase; + struct format_opts format; + struct write_opts write; + } u; +}; + +static struct w_list *workers; + +static struct w_list *find_worker(struct burn_drive *d) +{ + struct w_list *a; + + for (a = workers; a; a = a->next) + if (a->drive == d) + return a; + return NULL; +} + +static void add_worker(struct burn_drive *d, WorkerFunc f, void *data) +{ + struct w_list *a; + struct w_list *tmp; + + a = malloc(sizeof(struct w_list)); + a->drive = d; + a->u = *(union w_list_data *)data; + + /* insert at front of the list */ + a->next = workers; + tmp = workers; + workers = a; + + if (d) + d->busy = BURN_DRIVE_SPAWNING; + + if (pthread_create(&a->thread, NULL, f, a)) { + free(a); + workers = tmp; + return; + } +} + +static void remove_worker(pthread_t th) +{ + struct w_list *a, *l = NULL; + + for (a = workers; a; l = a, a = a->next) + if (a->thread == th) { + if (l) + l->next = a->next; + else + workers = a->next; + free(a); + break; + } + + /* ts A61006 */ + /* a ssert(a != NULL);/ * wasn't found.. this should not be possible */ + if (a == NULL) + libdax_msgs_submit(libdax_messenger, -1, 0x00020101, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + "remove_worker() cannot find given worker item", 0, 0); +} + +static void *scan_worker_func(struct w_list *w) +{ + burn_drive_scan_sync(w->u.scan.drives, w->u.scan.n_drives); + w->u.scan.done = 1; + return NULL; +} + +int burn_drive_scan(struct burn_drive_info *drives[], unsigned int *n_drives) +{ + struct scan_opts o; + int ret = 0; + + /* ts A61006 : moved up from burn_drive_scan_sync , former Assert */ + if (!burn_running) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020109, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Library not running (on attempt to scan)", 0, 0); + *drives = NULL; + *n_drives = 0; + return -1; + } + + /* cant be anything working! */ + + /* ts A61006 */ + /* a ssert(!(workers && workers->drive)); */ + if (workers != NULL && workers->drive != NULL) { +drive_is_active:; + libdax_msgs_submit(libdax_messenger, -1, 0x00020102, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "A drive operation is still going on (want to scan)", + 0, 0); + *drives = NULL; + *n_drives = 0; + return -1; + } + + if (!workers) { + /* start it */ + + /* ts A61007 : test moved up from burn_drive_scan_sync() + was burn_wait_all() */ + if (!burn_drives_are_clear()) + goto drive_is_active; + *drives = NULL; + *n_drives = 0; + + o.drives = drives; + o.n_drives = n_drives; + o.done = 0; + add_worker(NULL, (WorkerFunc) scan_worker_func, &o); + } else if (workers->u.scan.done) { + /* its done */ + ret = workers->u.scan.done; + remove_worker(workers->thread); + + /* ts A61006 */ + /* a ssert(workers == NULL); */ + if (workers != NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020101, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + "After scan a drive operation is still going on", + 0, 0); + return -1; + } + + } else { + /* still going */ + } + return ret; +} + +static void *erase_worker_func(struct w_list *w) +{ + burn_disc_erase_sync(w->u.erase.drive, w->u.erase.fast); + remove_worker(pthread_self()); + return NULL; +} + +void burn_disc_erase(struct burn_drive *drive, int fast) +{ + struct erase_opts o; + + /* A70103 : will be set to 0 by burn_disc_erase_sync() */ + drive->cancel = 1; + + /* ts A61006 */ + /* a ssert(drive); */ + /* a ssert(!SCAN_GOING()); */ + /* a ssert(!find_worker(drive)); */ + if((drive == NULL)) { + libdax_msgs_submit(libdax_messenger, drive->global_index, + 0x00020104, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "NULL pointer caught in burn_disc_erase", 0, 0); + return; + } + if ((SCAN_GOING()) || find_worker(drive)) { + libdax_msgs_submit(libdax_messenger, drive->global_index, + 0x00020102, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "A drive operation is still going on (want to erase)", + 0, 0); + return; + } + + /* ts A70103 moved up from burn_disc_erase_sync() */ + /* ts A60825 : allow on parole to blank appendable CDs */ + if ( ! (drive->status == BURN_DISC_FULL || + (drive->status == BURN_DISC_APPENDABLE && + ! libburn_back_hack_42) ) ) { + libdax_msgs_submit(libdax_messenger, drive->global_index, + 0x00020130, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Drive and media state unsuitable for blanking", + 0, 0); + return; + } + + o.drive = drive; + o.fast = fast; + add_worker(drive, (WorkerFunc) erase_worker_func, &o); +} + + +/* ts A61230 */ +static void *format_worker_func(struct w_list *w) +{ + burn_disc_format_sync(w->u.format.drive, w->u.format.size, + w->u.format.flag); + remove_worker(pthread_self()); + return NULL; +} + + +/* ts A61230 */ +void burn_disc_format(struct burn_drive *drive, off_t size, int flag) +{ + struct format_opts o; + int ok = 0; + char msg[160]; + + if ((SCAN_GOING()) || find_worker(drive)) { + libdax_msgs_submit(libdax_messenger, drive->global_index, + 0x00020102, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "A drive operation is still going on (want to format)", + 0, 0); + return; + } + if (flag & 128) /* application prescribed format type */ + flag |= 16; /* enforce re-format */ + + if (drive->current_profile == 0x14) + ok = 1; /* DVD-RW sequential */ + else if (drive->current_profile == 0x13 && (flag & 16)) + ok = 1; /* DVD-RW Restricted Overwrite with force bit */ + else if (drive->current_profile == 0x1a) { + ok = 1; /* DVD+RW */ + size = 0; + flag &= ~(2|8); /* no insisting in size 0, no expansion */ + flag |= 4; /* format up to maximum size */ + } + + if (!ok) { + sprintf(msg,"Will not format media type %4.4Xh", + drive->current_profile); + libdax_msgs_submit(libdax_messenger, drive->global_index, + 0x00020129, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + drive->cancel = 1; + return; + } + o.drive = drive; + o.size = size; + o.flag = flag; + add_worker(drive, (WorkerFunc) format_worker_func, &o); +} + + +static void *write_disc_worker_func(struct w_list *w) +{ + burn_disc_write_sync(w->u.write.opts, w->u.write.disc); + + /* the options are refcounted, free out ref count which we added below + */ + burn_write_opts_free(w->u.write.opts); + + remove_worker(pthread_self()); + return NULL; +} + +void burn_disc_write(struct burn_write_opts *opts, struct burn_disc *disc) +{ + struct write_opts o; + + /* ts A61006 */ + /* a ssert(!SCAN_GOING()); */ + /* a ssert(!find_worker(opts->drive)); */ + if ((SCAN_GOING()) || find_worker(opts->drive)) { + libdax_msgs_submit(libdax_messenger, opts->drive->global_index, + 0x00020102, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "A drive operation is still going on (want to write)", + 0, 0); + return; + } + /* ts A61007 : obsolete Assert in spc_select_write_params() */ + if (!opts->drive->mdata->valid) { + libdax_msgs_submit(libdax_messenger, + opts->drive->global_index, 0x00020113, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Drive capabilities not inquired yet", 0, 0); + return; + } + /* ts A61009 : obsolete Assert in sector_headers() */ + if (! burn_disc_write_is_ok(opts, disc)) /* issues own msgs */ + return; + + o.drive = opts->drive; + o.opts = opts; + o.disc = disc; + + opts->refcount++; + + add_worker(opts->drive, (WorkerFunc) write_disc_worker_func, &o); +} + +void burn_async_join_all(void) +{ + void *ret; + + while (workers) + pthread_join(workers->thread, &ret); +} diff --git a/libburn/tags/ZeroThreeZero/libburn/async.h b/libburn/tags/ZeroThreeZero/libburn/async.h new file mode 100644 index 00000000..0e1d6677 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/async.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__ASYNC_H +#define BURN__ASYNC_H + +void burn_async_join_all(void); +struct burn_write_opts; +#endif /* BURN__ASYNC_H */ diff --git a/libburn/tags/ZeroThreeZero/libburn/back_hacks.h b/libburn/tags/ZeroThreeZero/libburn/back_hacks.h new file mode 100644 index 00000000..b086620a --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/back_hacks.h @@ -0,0 +1,54 @@ +/** + + This file bundles variables which disable changes in libburn which are + not yet completely accepted. + + The use of these variables is *strongly discouraged* unless you have sincere + reason and are willing to share your gained knowledge with the libburn + developers. + + Do *not silently rely* on these variables with your application. Tell us + that you needed one or more of them. They are subject to removal as soon + as consense has been found about correctness of the change they revoke. + + Value 0 means that the new behavior is enabled. Any other value enables + the described old time behavior. + + If you doubt one of the changes here broke your application, then do + *in your application*, *not here* : + + - #include "libburn/back_hacks.h" like you include "libburn/libburn.h" + + - Set the libburn_back_hack_* variable of your choice to 1. + In your app. Not here. + + - Then start and use libburn as usual. Watch out for results. + + - If you believe to have detected a flaw in our change, come forward + and report it to the libburn developers. Thanks in advance. :) + +*/ + +/** Do not define this macro in your application. Only libburn/init.c is + entitled to set it. +*/ +#ifdef BURN_BACK_HACKS_INIT + + +/** Corresponds to http://libburn.pykix.org/ticket/42 + Reinstates the old ban not to blank appendable CD-RW. We see no reason + for this ban yet. It appears unusual. But maybe it patches a bug. +*/ +int libburn_back_hack_42= 0; + + +#else /* BURN_BACK_HACKS_INIT */ + +/* Note: no application programmer info beyond this point */ + + +extern int libburn_back_hack_42; + +#endif /* ! BURN_BACK_HACKS_INIT */ + + diff --git a/libburn/tags/ZeroThreeZero/libburn/cleanup.c b/libburn/tags/ZeroThreeZero/libburn/cleanup.c new file mode 100644 index 00000000..fc923830 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/cleanup.c @@ -0,0 +1,215 @@ +/* + cleanup.c , Copyright 2006 Thomas Schmitt <scdbackup@gmx.net> + + A signal handler which cleans up an application and exits. + + Provided under GPL license within GPL projects, BSD license elsewise. +*/ + +/* + cc -g -o cleanup -DCleanup_standalonE cleanup.c +*/ + +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <signal.h> +typedef void (*sighandler_t)(int); + + +#include "cleanup.h" + + +#ifndef Cleanup_has_no_libburn_os_H + + +#include "../libburn/os.h" + +/* see os.h for name of particular os-*.h where this is defined */ +static int signal_list[]= { BURN_OS_SIGNAL_MACRO_LIST , -1}; +static char *signal_name_list[]= { BURN_OS_SIGNAL_NAME_LIST , "@"}; +static int signal_list_count= BURN_OS_SIGNAL_COUNT; +static int non_signal_list[]= { BURN_OS_NON_SIGNAL_MACRO_LIST, -1}; +static int non_signal_list_count= BURN_OS_NON_SIGNAL_COUNT; + + +#else /* ! Cleanup_has_no_libburn_os_H */ + + +/* Outdated. Linux only. For backward compatibility with pre-libburn-0.2.3 */ + +/* Signals to be caught */ +static int signal_list[]= { + SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, + SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, + SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, + SIGTTOU, + SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP, + SIGVTALRM, SIGXCPU, SIGXFSZ, -1 +}; +static char *signal_name_list[]= { + "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", + "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", + "SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", + "SIGTTOU", + "SIGBUS", "SIGPOLL", "SIGPROF", "SIGSYS", "SIGTRAP", + "SIGVTALRM", "SIGXCPU", "SIGXFSZ", "@" +}; +static int signal_list_count= 24; + +/* Signals not to be caught */ +static int non_signal_list[]= { + SIGKILL, SIGCHLD, SIGSTOP, SIGURG, SIGWINCH, -1 +}; +static int non_signal_list_count= 5; + + +#endif /* Cleanup_has_no_libburn_os_H */ + + + +/* run time dynamic part */ +static char cleanup_msg[4096]= {""}; +static int cleanup_exiting= 0; +static int cleanup_has_reported= -1234567890; + +static void *cleanup_app_handle= NULL; +static Cleanup_app_handler_T cleanup_app_handler= NULL; +static int cleanup_perform_app_handler_first= 0; + + +static int Cleanup_handler_exit(int exit_value, int signum, int flag) +{ + int ret; + + if(cleanup_msg[0]!=0 && cleanup_has_reported!=signum) { + fprintf(stderr,"\n%s\n",cleanup_msg); + cleanup_has_reported= signum; + } + if(cleanup_perform_app_handler_first) + if(cleanup_app_handler!=NULL) { + ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0); + if(ret==2 || ret==-2) + return(2); + } + if(cleanup_exiting) { + fprintf(stderr,"cleanup: ABORT : repeat by pid=%d, signum=%d\n", + getpid(),signum); + return(0); + } + cleanup_exiting= 1; + alarm(0); + if(!cleanup_perform_app_handler_first) + if(cleanup_app_handler!=NULL) { + ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0); + if(ret==2 || ret==-2) + return(2); + } + exit(exit_value); +} + + +static void Cleanup_handler_generic(int signum) +{ + int i; + + sprintf(cleanup_msg,"UNIX-SIGNAL caught: %d errno= %d",signum,errno); + for(i= 0; i<signal_list_count; i++) + if(signum==signal_list[i]) { + sprintf(cleanup_msg,"UNIX-SIGNAL: %s errno= %d", + signal_name_list[i],errno); + break; + } + Cleanup_handler_exit(1,signum,0); +} + + +int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler, int flag) +/* + bit0= set to default handlers + bit1= set to ignore + bit2= set cleanup_perform_app_handler_first + bit3= set SIGABRT to handler (makes sense with bits 0 or 1) +*/ +{ + int i,j,max_sig= -1,min_sig= 0x7fffffff; + sighandler_t sig_handler; + + cleanup_msg[0]= 0; + cleanup_app_handle= handle; + cleanup_app_handler= handler; + + /* <<< make cleanup_exiting thread safe to get rid of this */ + if(flag&4) + cleanup_perform_app_handler_first= 1; + + + if(flag&1) + sig_handler= SIG_DFL; + else if(flag&2) + sig_handler= SIG_IGN; + else + sig_handler= Cleanup_handler_generic; + /* set all signal numbers between the lowest and highest in the list + except those in the non-signal list */ + for(i= 0; i<signal_list_count; i++) { + if(signal_list[i]>max_sig) + max_sig= signal_list[i]; + if(signal_list[i]<min_sig) + min_sig= signal_list[i]; + } + for(i= min_sig; i<=max_sig; i++) { + for(j= 0; j<non_signal_list_count; j++) + if(i==non_signal_list[j]) + break; + if(j>=non_signal_list_count) { + if(i==SIGABRT && (flag&8)) + signal(i,Cleanup_handler_generic); + else + signal(i,sig_handler); + } + } + return(1); +} + + +#ifdef Cleanup_standalonE + +struct Demo_apP { + char *msg; +}; + + +int Demo_app_handler(struct Demo_apP *demoapp, int signum, int flag) +{ + printf("Handling exit of demo application on signal %d. msg=\"%s\"\n", + signum,demoapp->msg); + return(1); +} + + +main() +{ + struct Demo_apP demoapp; + + demoapp.msg= "Good Bye"; + Cleanup_set_handlers(&demoapp,(Cleanup_app_handler_T) Demo_app_handler,0); + + if(1) { /* change to 0 in order to wait for external signals */ + char *cpt= NULL,c; + printf("Intentionally provoking SIGSEGV ...\n"); + c= *cpt; + } else { + printf("killme: %d\n",getpid()); + sleep(3600); + } + + Cleanup_set_handlers(NULL,NULL,1); + exit(0); +} + +#endif /* Cleanup_standalonE */ diff --git a/libburn/tags/ZeroThreeZero/libburn/cleanup.h b/libburn/tags/ZeroThreeZero/libburn/cleanup.h new file mode 100644 index 00000000..a9d35519 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/cleanup.h @@ -0,0 +1,34 @@ +/* + cleanup.c , Copyright 2006 Thomas Schmitt <scdbackup@gmx.net> + + A signal handler which cleans up an application and exits. + + Provided under GPL license within GPL projects, BSD license elsewise. +*/ + +#ifndef Cleanup_includeD +#define Cleanup_includeD 1 + + +/** Layout of an application provided cleanup function using an application + provided handle as first argument and the signal number as second + argument. The third argument is a flag bit field with no defined bits yet. + If the handler returns 2 or -2 then it has delegated exit() to some other + instance and the Cleanup handler shall return rather than exit. +*/ +typedef int (*Cleanup_app_handler_T)(void *, int, int); + + +/** Establish exiting signal handlers on (hopefully) all signals that are + not ignored by default or non-catchable. + @param handle Opaque object which knows how to cleanup application + @param handler Function which uses handle to perform application cleanup + @param flag Control Bitfield + bit0= reset to default signal handling +*/ +int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler, + int flag); + + +#endif /* ! Cleanup_includeD */ + diff --git a/libburn/tags/ZeroThreeZero/libburn/crc.c b/libburn/tags/ZeroThreeZero/libburn/crc.c new file mode 100644 index 00000000..fddc5b4f --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/crc.c @@ -0,0 +1,122 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "crc.h" + +static unsigned short ccitt_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +unsigned long crc32_table[256] = { + 0x00000000L, 0x90910101L, 0x91210201L, 0x01B00300L, + 0x92410401L, 0x02D00500L, 0x03600600L, 0x93F10701L, + 0x94810801L, 0x04100900L, 0x05A00A00L, 0x95310B01L, + 0x06C00C00L, 0x96510D01L, 0x97E10E01L, 0x07700F00L, + 0x99011001L, 0x09901100L, 0x08201200L, 0x98B11301L, + 0x0B401400L, 0x9BD11501L, 0x9A611601L, 0x0AF01700L, + 0x0D801800L, 0x9D111901L, 0x9CA11A01L, 0x0C301B00L, + 0x9FC11C01L, 0x0F501D00L, 0x0EE01E00L, 0x9E711F01L, + 0x82012001L, 0x12902100L, 0x13202200L, 0x83B12301L, + 0x10402400L, 0x80D12501L, 0x81612601L, 0x11F02700L, + 0x16802800L, 0x86112901L, 0x87A12A01L, 0x17302B00L, + 0x84C12C01L, 0x14502D00L, 0x15E02E00L, 0x85712F01L, + 0x1B003000L, 0x8B913101L, 0x8A213201L, 0x1AB03300L, + 0x89413401L, 0x19D03500L, 0x18603600L, 0x88F13701L, + 0x8F813801L, 0x1F103900L, 0x1EA03A00L, 0x8E313B01L, + 0x1DC03C00L, 0x8D513D01L, 0x8CE13E01L, 0x1C703F00L, + 0xB4014001L, 0x24904100L, 0x25204200L, 0xB5B14301L, + 0x26404400L, 0xB6D14501L, 0xB7614601L, 0x27F04700L, + 0x20804800L, 0xB0114901L, 0xB1A14A01L, 0x21304B00L, + 0xB2C14C01L, 0x22504D00L, 0x23E04E00L, 0xB3714F01L, + 0x2D005000L, 0xBD915101L, 0xBC215201L, 0x2CB05300L, + 0xBF415401L, 0x2FD05500L, 0x2E605600L, 0xBEF15701L, + 0xB9815801L, 0x29105900L, 0x28A05A00L, 0xB8315B01L, + 0x2BC05C00L, 0xBB515D01L, 0xBAE15E01L, 0x2A705F00L, + 0x36006000L, 0xA6916101L, 0xA7216201L, 0x37B06300L, + 0xA4416401L, 0x34D06500L, 0x35606600L, 0xA5F16701L, + 0xA2816801L, 0x32106900L, 0x33A06A00L, 0xA3316B01L, + 0x30C06C00L, 0xA0516D01L, 0xA1E16E01L, 0x31706F00L, + 0xAF017001L, 0x3F907100L, 0x3E207200L, 0xAEB17301L, + 0x3D407400L, 0xADD17501L, 0xAC617601L, 0x3CF07700L, + 0x3B807800L, 0xAB117901L, 0xAAA17A01L, 0x3A307B00L, + 0xA9C17C01L, 0x39507D00L, 0x38E07E00L, 0xA8717F01L, + 0xD8018001L, 0x48908100L, 0x49208200L, 0xD9B18301L, + 0x4A408400L, 0xDAD18501L, 0xDB618601L, 0x4BF08700L, + 0x4C808800L, 0xDC118901L, 0xDDA18A01L, 0x4D308B00L, + 0xDEC18C01L, 0x4E508D00L, 0x4FE08E00L, 0xDF718F01L, + 0x41009000L, 0xD1919101L, 0xD0219201L, 0x40B09300L, + 0xD3419401L, 0x43D09500L, 0x42609600L, 0xD2F19701L, + 0xD5819801L, 0x45109900L, 0x44A09A00L, 0xD4319B01L, + 0x47C09C00L, 0xD7519D01L, 0xD6E19E01L, 0x46709F00L, + 0x5A00A000L, 0xCA91A101L, 0xCB21A201L, 0x5BB0A300L, + 0xC841A401L, 0x58D0A500L, 0x5960A600L, 0xC9F1A701L, + 0xCE81A801L, 0x5E10A900L, 0x5FA0AA00L, 0xCF31AB01L, + 0x5CC0AC00L, 0xCC51AD01L, 0xCDE1AE01L, 0x5D70AF00L, + 0xC301B001L, 0x5390B100L, 0x5220B200L, 0xC2B1B301L, + 0x5140B400L, 0xC1D1B501L, 0xC061B601L, 0x50F0B700L, + 0x5780B800L, 0xC711B901L, 0xC6A1BA01L, 0x5630BB00L, + 0xC5C1BC01L, 0x5550BD00L, 0x54E0BE00L, 0xC471BF01L, + 0x6C00C000L, 0xFC91C101L, 0xFD21C201L, 0x6DB0C300L, + 0xFE41C401L, 0x6ED0C500L, 0x6F60C600L, 0xFFF1C701L, + 0xF881C801L, 0x6810C900L, 0x69A0CA00L, 0xF931CB01L, + 0x6AC0CC00L, 0xFA51CD01L, 0xFBE1CE01L, 0x6B70CF00L, + 0xF501D001L, 0x6590D100L, 0x6420D200L, 0xF4B1D301L, + 0x6740D400L, 0xF7D1D501L, 0xF661D601L, 0x66F0D700L, + 0x6180D800L, 0xF111D901L, 0xF0A1DA01L, 0x6030DB00L, + 0xF3C1DC01L, 0x6350DD00L, 0x62E0DE00L, 0xF271DF01L, + 0xEE01E001L, 0x7E90E100L, 0x7F20E200L, 0xEFB1E301L, + 0x7C40E400L, 0xECD1E501L, 0xED61E601L, 0x7DF0E700L, + 0x7A80E800L, 0xEA11E901L, 0xEBA1EA01L, 0x7B30EB00L, + 0xE8C1EC01L, 0x7850ED00L, 0x79E0EE00L, 0xE971EF01L, + 0x7700F000L, 0xE791F101L, 0xE621F201L, 0x76B0F300L, + 0xE541F401L, 0x75D0F500L, 0x7460F600L, 0xE4F1F701L, + 0xE381F801L, 0x7310F900L, 0x72A0FA00L, 0xE231FB01L, + 0x71C0FC00L, 0xE151FD01L, 0xE0E1FE01L, 0x7070FF00L +}; + +unsigned short crc_ccitt(unsigned char *q, int len) +{ + unsigned short crc = 0; + + while (len-- > 0) + crc = ccitt_table[(crc >> 8 ^ *q++) & 0xff] ^ (crc << 8); + return ~crc; +} +unsigned int crc_32(unsigned char *data, int len) +{ + unsigned int crc = 0; + + while (len-- > 0) + crc = crc32_table[(crc ^ *data++) & 0xffL] ^ (crc >> 8); + return crc; +} diff --git a/libburn/tags/ZeroThreeZero/libburn/crc.h b/libburn/tags/ZeroThreeZero/libburn/crc.h new file mode 100644 index 00000000..a4846a33 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/crc.h @@ -0,0 +1,9 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__CRC_H +#define BURN__CRC_H + +unsigned short crc_ccitt(unsigned char *, int len); +unsigned int crc_32(unsigned char *, int len); + +#endif /* BURN__CRC_H */ diff --git a/libburn/tags/ZeroThreeZero/libburn/debug.c b/libburn/tags/ZeroThreeZero/libburn/debug.c new file mode 100644 index 00000000..b4abab77 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/debug.c @@ -0,0 +1,35 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifdef WIN32 +#include <windows.h> +#endif + +#include <stdarg.h> +#include <stdio.h> +#include "libburn.h" +#include "debug.h" + +static int burn_verbosity = 0; + +void burn_set_verbosity(int v) +{ + burn_verbosity = v; +} + +void burn_print(int level, const char *a, ...) +{ +#ifdef WIN32 + char debug_string_data[256]; +#endif + va_list vl; + + if (level <= burn_verbosity) { + va_start(vl, a); +#ifdef WIN32 + vsprintf(debug_string_data, a, vl); + OutputDebugString(debug_string_data); +#else + vfprintf(stderr, a, vl); +#endif + } +} diff --git a/libburn/tags/ZeroThreeZero/libburn/debug.h b/libburn/tags/ZeroThreeZero/libburn/debug.h new file mode 100644 index 00000000..b566de0e --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/debug.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__DEBUG_H +#define BURN__DEBUG_H + +void burn_print(int level, const char *a, ...); + +#endif /* BURN__DEBUG_H */ diff --git a/libburn/tags/ZeroThreeZero/libburn/drive.c b/libburn/tags/ZeroThreeZero/libburn/drive.c new file mode 100644 index 00000000..68e81c4b --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/drive.c @@ -0,0 +1,1531 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include <sys/types.h> +#include <sys/stat.h> + +/* #include <m alloc.h> ts A61013 : not in Linux man 3 malloc */ + +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> + +/* ts A61007 */ +/* #include <a ssert.h> */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <pthread.h> +#include "libburn.h" +#include "drive.h" +#include "transport.h" +#include "debug.h" +#include "init.h" +#include "toc.h" +#include "util.h" +#include "sg.h" +#include "structure.h" + +/* ts A70107 : to get BE_CANCELLED */ +#include "error.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + +static struct burn_drive drive_array[255]; +static int drivetop = -1; + +/* ts A61021 : the unspecific part of sg.c:enumerate_common() +*/ +int burn_setup_drive(struct burn_drive *d, char *fname) +{ + d->devname = burn_strdup(fname); + memset(&d->params, 0, sizeof(struct params)); + d->released = 1; + d->status = BURN_DISC_UNREADY; + return 1; +} + +/* ts A60904 : ticket 62, contribution by elmom */ +/* splitting former burn_drive_free() (which freed all, into two calls) */ +void burn_drive_free(struct burn_drive *d) +{ + if (d->global_index == -1) + return; + /* ts A60822 : close open fds before forgetting them */ + if (burn_drive_is_open(d)) + d->release(d); + free((void *) d->idata); + burn_mdata_free_subs(d->mdata); + free((void *) d->mdata); + if(d->toc_entry != NULL) + free((void *) d->toc_entry); + free(d->devname); + d->global_index = -1; +} + +void burn_drive_free_all(void) +{ + int i; + + for (i = 0; i < drivetop + 1; i++) + burn_drive_free(&(drive_array[i])); + drivetop = -1; + memset(drive_array, 0, sizeof(drive_array)); +} + + +/* ts A60822 */ +int burn_drive_is_open(struct burn_drive *d) +{ + /* ts A61021 : moved decision to sg.c */ + return d->drive_is_open(d); +} + + +/* ts A60906 */ +int burn_drive_force_idle(struct burn_drive *d) +{ + d->busy = BURN_DRIVE_IDLE; + return 1; +} + + +/* ts A60906 */ +int burn_drive_is_released(struct burn_drive *d) +{ + return !!d->released; +} + + +/* ts A60906 */ +/** Inquires drive status in respect to degree of app usage. + @param return -2 = drive is forgotten + -1 = drive is closed (i.e. released explicitely) + 0 = drive is open, not grabbed (after scan, before 1st grab) + 1 = drive is grabbed but BURN_DRIVE_IDLE + 10 = drive is grabbing (BURN_DRIVE_GRABBING) + 100 = drive is busy in cancelable state + 1000 = drive is in non-cancelable state + Expect a monotonous sequence of usage severity to emerge in future. +*/ +int burn_drive_is_occupied(struct burn_drive *d) +{ + if(d->global_index < 0) + return -2; + if(!burn_drive_is_open(d)) + return -1; + if(d->busy == BURN_DRIVE_GRABBING) + return 10; + if(d->released) + return 0; + if(d->busy == BURN_DRIVE_IDLE) + return 1; + if(d->busy == BURN_DRIVE_READING || d->busy == BURN_DRIVE_WRITING) + return 50; + return 1000; +} + + +/* +void drive_read_lead_in(int dnum) +{ + mmc_read_lead_in(&drive_array[dnum], get_4k()); +} +*/ +unsigned int burn_drive_count(void) +{ + return drivetop + 1; +} + + +/* ts A61125 : media status aspects of burn_drive_grab() */ +int burn_drive_inquire_media(struct burn_drive *d) +{ + + /* ts A61225 : after loading the tray, mode page 2Ah can change */ + d->getcaps(d); + + /* ts A61020 : d->status was set to BURN_DISC_BLANK as pure guess */ + + if (d->mdata->cdr_write || d->mdata->cdrw_write || + d->mdata->dvdr_write || d->mdata->dvdram_write) { + +#define Libburn_knows_correct_state_after_loaD 1 +#ifdef Libburn_knows_correct_state_after_loaD + + d->read_disc_info(d); + +#else + /* ts A61227 : This repeated read_disc_info seems + to be obsoleted by above d->getcaps(d). + */ + /* ts A60908 */ + /* Trying to stabilize the disc status after eventual load + without closing and re-opening the drive */ + /* This seems to work for burn_disc_erasable() . + Speed values on RIP-14 and LITE-ON 48125S are stable + and false, nevertheless. */ + int was_equal = 0, must_equal = 3, max_loop = 20; + int loop_count, old_speed = -1234567890, new_speed= -987654321; + int old_erasable = -1234567890, new_erasable = -987654321; + + fprintf(stderr,"LIBBURN_DEBUG: read_disc_info()\n"); + for (loop_count = 0; loop_count < max_loop; loop_count++){ + old_speed = new_speed; + old_erasable = new_erasable; + + d->read_disc_info(d); + if(d->status == BURN_DISC_UNSUITABLE) + break; + + new_speed = burn_drive_get_write_speed(d); + new_erasable = burn_disc_erasable(d); + if (new_speed == old_speed && + new_erasable == old_erasable) { + was_equal++; + if (was_equal >= must_equal) + break; + } else + was_equal = 0; + /* + if (loop_count >= 1 && was_equal == 0) + */ + fprintf(stderr,"LIBBURN_DEBUG: %d : speed %d:%d erasable %d:%d\n", + loop_count,old_speed,new_speed,old_erasable,new_erasable); + usleep(100000); + } +#endif /* ! Libburn_knows_correct_state_after_loaD */ + + } else { + if (d->current_profile == -1 || d->current_is_cd_profile) + d->read_toc(d); + } + return 1; +} + + +int burn_drive_grab(struct burn_drive *d, int le) +{ + int errcode; + /* ts A61125 - A61202 */ + int ret, sose; + + if (!d->released) { + burn_print(1, "can't grab - already grabbed\n"); + return 0; + } + d->status = BURN_DISC_UNREADY; + errcode = d->grab(d); + + if (errcode == 0) { + burn_print(1, "low level drive grab failed\n"); + return 0; + } + d->busy = BURN_DRIVE_GRABBING; + + if (le) + d->load(d); + + d->lock(d); + + /* ts A61118 */ + d->start_unit(d); + + /* ts A61202 : gave bit1 of le a meaning */ + sose = d->silent_on_scsi_error; + if (!le) + d->silent_on_scsi_error = 1; + /* ts A61125 : outsourced media state inquiry aspects */ + ret = burn_drive_inquire_media(d); + d->silent_on_scsi_error = sose; + d->busy = BURN_DRIVE_IDLE; + return ret; +} + +struct burn_drive *burn_drive_register(struct burn_drive *d) +{ +#ifdef Libburn_ticket_62_re_register_is_possiblE + int i; +#endif + + d->block_types[0] = 0; + d->block_types[1] = 0; + d->block_types[2] = 0; + d->block_types[3] = 0; + d->toc_temp = 0; + d->nwa = 0; + d->alba = 0; + d->rlba = 0; + d->cancel = 0; + d->busy = BURN_DRIVE_IDLE; + d->toc_entries = 0; + d->toc_entry = NULL; + d->disc = NULL; + d->erasable = 0; + +#ifdef Libburn_ticket_62_re_register_is_possiblE + /* ts A60904 : ticket 62, contribution by elmom */ + /* Not yet accepted because no use case seen yet */ + + /* This is supposed to find an already freed drive struct among + all the the ones that have been used before */ + for (i = 0; i < drivetop + 1; i++) + if (drive_array[i].global_index == -1) + break; + d->global_index = i; + memcpy(&drive_array[i], d, sizeof(struct burn_drive)); + pthread_mutex_init(&drive_array[i].access_lock, NULL); + if (drivetop < i) + drivetop = i; + return &(drive_array[i]); + +#else /* Libburn_ticket_62_re_register_is_possiblE */ + /* old A60904 : */ + /* Still active by default */ + + d->global_index = drivetop + 1; + memcpy(&drive_array[drivetop + 1], d, sizeof(struct burn_drive)); + pthread_mutex_init(&drive_array[drivetop + 1].access_lock, NULL); + return &drive_array[++drivetop]; + +#endif /* ! Libburn_ticket_62_re_register_is_possiblE */ + +} + + +/* unregister most recently registered drive */ +int burn_drive_unregister(struct burn_drive *d) +{ + if(d->global_index != drivetop) + return 0; + burn_drive_free(d); + drivetop--; + return 1; +} + + +/* ts A61021 : after-setup activities from sg.c:enumerate_common() +*/ +struct burn_drive *burn_drive_finish_enum(struct burn_drive *d) +{ + struct burn_drive *t; + /* ts A60821 + <<< debug: for tracing calls which might use open drive fds */ + int mmc_function_spy(char * text); + + t = burn_drive_register(d); + + /* ts A60821 */ + mmc_function_spy("enumerate_common : -------- doing grab"); + + /* try to get the drive info */ + if (t->grab(t)) { + burn_print(2, "getting drive info\n"); + t->getcaps(t); + t->unlock(t); + t->released = 1; + } else { + burn_print(2, "unable to grab new located drive\n"); + burn_drive_unregister(t); + t = NULL; + } + + /* ts A60821 */ + mmc_function_spy("enumerate_common : ----- would release "); + + return t; +} + + +/* ts A61125 : model aspects of burn_drive_release */ +int burn_drive_mark_unready(struct burn_drive *d) +{ + /* ts A61020 : mark media info as invalid */ + d->start_lba= -2000000000; + d->end_lba= -2000000000; + + /* ts A61202 */ + d->current_profile = -1; + d->status = BURN_DISC_UNREADY; + if (d->toc_entry != NULL) + free(d->toc_entry); + d->toc_entry = NULL; + d->toc_entries = 0; + if (d->disc != NULL) { + burn_disc_free(d->disc); + d->disc = NULL; + } + return 1; +} + + +void burn_drive_release(struct burn_drive *d, int le) +{ + if (d->released) { + /* ts A61007 */ + /* burn_print(1, "second release on drive!\n"); */ + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020105, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Drive is already released", 0, 0); + return; + } + + /* ts A61007 */ + /* ts A60906: one should not assume BURN_DRIVE_IDLE == 0 */ + /* a ssert(d->busy == BURN_DRIVE_IDLE); */ + if (d->busy != BURN_DRIVE_IDLE) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020106, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Drive is busy on attempt to close", 0, 0); + return; + } + + d->unlock(d); + if (le) + d->eject(d); + d->release(d); + d->released = 1; + + /* ts A61125 : outsourced model aspects */ + burn_drive_mark_unready(d); +} + + + +/* ts A61007 : former void burn_wait_all() */ +int burn_drives_are_clear(void) +{ + int i; + + for (i = burn_drive_count() - 1; i >= 0; --i) { + /* ts A60904 : ticket 62, contribution by elmom */ + if (drive_array[i].global_index == -1) + continue; + if (drive_array[i].released) + continue; + return 0; + } + return 1; +} + + +#if 0 +void burn_wait_all(void) +{ + unsigned int i; + int finished = 0; + struct burn_drive *d; + + while (!finished) { + finished = 1; + d = drive_array; + for (i = burn_drive_count(); i > 0; --i, ++d) { + + /* ts A60904 : ticket 62, contribution by elmom */ + if (d->global_index==-1) + continue; + + a ssert(d->released); + } + if (!finished) + sleep(1); + } +} +#endif + + +void burn_disc_erase_sync(struct burn_drive *d, int fast) +{ +/* ts A60924 : libburn/message.c gets obsoleted + burn_message_clear_queue(); +*/ + + burn_print(1, "erasing drive %s %s\n", d->idata->vendor, + d->idata->product); + + d->cancel = 0; + d->busy = BURN_DRIVE_ERASING; + d->erase(d, fast); + /* reset the progress */ + d->progress.session = 0; + d->progress.sessions = 1; + d->progress.track = 0; + d->progress.tracks = 1; + d->progress.index = 0; + d->progress.indices = 1; + d->progress.start_sector = 0; + d->progress.sectors = 0x10000; + d->progress.sector = 0; + /* read the initial 0 stage */ + while (!d->test_unit_ready(d) && d->get_erase_progress(d) == 0) + sleep(1); + while ((d->progress.sector = d->get_erase_progress(d)) > 0 || + !d->test_unit_ready(d)) + sleep(1); + d->progress.sector = 0x10000; + + /* ts A61125 : update media state records */ + burn_drive_mark_unready(d); + burn_drive_inquire_media(d); + d->busy = BURN_DRIVE_IDLE; +} + +/* + @param flag: bit0 = fill formatted size with zeros + bit1, bit2 , bit4, bit7 - bit15 are for d->format_unit() +*/ +void burn_disc_format_sync(struct burn_drive *d, off_t size, int flag) +{ + int ret, buf_secs, err, i, stages = 1, pbase, pfill, pseudo_sector; + off_t num_bufs; + char msg[80]; + struct buffer buf; + + /* reset the progress */ + d->progress.session = 0; + d->progress.sessions = 1; + d->progress.track = 0; + d->progress.tracks = 1; + d->progress.index = 0; + d->progress.indices = 1; + d->progress.start_sector = 0; + d->progress.sectors = 0x10000; + d->progress.sector = 0; + stages = 1 + ((flag & 1) && size > 1024 * 1024); + d->cancel = 0; + d->busy = BURN_DRIVE_FORMATTING; + ret = d->format_unit(d, size, flag & 0xff96); /* forward bits */ + if (ret <= 0) + d->cancel = 1; + + while (!d->test_unit_ready(d) && d->get_erase_progress(d) == 0) + sleep(1); + while ((pseudo_sector = d->get_erase_progress(d)) > 0 || + !d->test_unit_ready(d)) { + d->progress.sector = pseudo_sector / stages; + sleep(1); + } + d->sync_cache(d); + + if (size <= 0) + goto ex; + + /* update media state records */ + burn_drive_mark_unready(d); + burn_drive_inquire_media(d); + if (flag & 1) { + /* write size in zeros */; + pbase = 0x8000 + 0x7fff * (stages == 1); + pfill = 0xffff - pbase; + buf_secs = 16; /* Must not be more than 16 */ + num_bufs = size / buf_secs / 2048; + if (num_bufs > 0x7fffffff) { + d->cancel = 1; + goto ex; + } + + /* <<< */ + sprintf(msg, + "Writing %.f sectors of zeros to formatted media", + (double) num_bufs * (double) buf_secs); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + + d->buffer = &buf; + memset(d->buffer, 0, sizeof(struct buffer)); + d->buffer->bytes = buf_secs * 2048; + d->buffer->sectors = buf_secs; + d->busy = BURN_DRIVE_WRITING; + for (i = 0; i < num_bufs; i++) { + d->nwa = i * buf_secs; + err = d->write(d, d->nwa, d->buffer); + if (err == BE_CANCELLED || d->cancel) { + d->cancel = 1; + break; + } + d->progress.sector = pbase + + pfill * ((double) i / (double) num_bufs); + } + d->sync_cache(d); + if (d->current_profile == 0x13 || d->current_profile == 0x1a) { + /* DVD-RW or DVD+RW */ + d->busy = BURN_DRIVE_CLOSING_SESSION; + /* CLOSE SESSION, 010b */ + d->close_track_session(d, 1, 0); + d->busy = BURN_DRIVE_WRITING; + } + } +ex:; + d->progress.sector = 0x10000; + d->busy = BURN_DRIVE_IDLE; + d->buffer = NULL; +} + + +/* ts A70112 API */ +int burn_disc_get_formats(struct burn_drive *d, int *status, off_t *size, + unsigned *bl_sas, int *num_formats) +{ + int ret; + + *status = 0; + *size = 0; + *bl_sas = 0; + *num_formats = 0; + ret = d->read_format_capacities(d, 0x00); + if (ret <= 0) + return 0; + *status = d->format_descr_type; + *size = d->format_curr_max_size; + *bl_sas = d->format_curr_blsas; + *num_formats = d->num_format_descr; + return 1; +} + + +/* ts A70112 API */ +int burn_disc_get_format_descr(struct burn_drive *d, int index, + int *type, off_t *size, unsigned *tdp) +{ + *type = 0; + *size = 0; + *tdp = 0; + if (index < 0 || index >= d->num_format_descr) + return 0; + *type = d->format_descriptors[index].type; + *size = d->format_descriptors[index].size; + *tdp = d->format_descriptors[index].tdp; + return 1; +} + + +enum burn_disc_status burn_disc_get_status(struct burn_drive *d) +{ + /* ts A61007 */ + /* a ssert(!d->released); */ + if (d->released) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020108, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Drive is not grabbed on disc status inquiry", + 0, 0); + return BURN_DISC_UNGRABBED; + } + + return d->status; +} + +int burn_disc_erasable(struct burn_drive *d) +{ + return d->erasable; +} +enum burn_drive_status burn_drive_get_status(struct burn_drive *d, + struct burn_progress *p) +{ + if (p) { + memcpy(p, &(d->progress), sizeof(struct burn_progress)); + /* TODO: add mutex */ + } + return d->busy; +} + +void burn_drive_cancel(struct burn_drive *d) +{ + pthread_mutex_lock(&d->access_lock); + d->cancel = 1; + pthread_mutex_unlock(&d->access_lock); +} + +/* ts A61007 : defunct because unused */ +#if 0 +int burn_drive_get_block_types(struct burn_drive *d, + enum burn_write_types write_type) +{ + burn_print(12, "write type: %d\n", write_type); + a ssert( /* (write_type >= BURN_WRITE_PACKET) && */ + (write_type <= BURN_WRITE_RAW)); + return d->block_types[write_type]; +} +#endif + +static void strip_spaces(char *str) +{ + char *tmp; + + tmp = str + strlen(str) - 1; + while (isspace(*tmp)) + *(tmp--) = '\0'; + + tmp = str; + while (*tmp) { + if (isspace(*tmp) && isspace(*(tmp + 1))) { + char *tmp2; + + for (tmp2 = tmp + 1; *tmp2; ++tmp2) + *(tmp2 - 1) = *tmp2; + *(tmp2 - 1) = '\0'; + } else + ++tmp; + } +} + +static int drive_getcaps(struct burn_drive *d, struct burn_drive_info *out) +{ + struct burn_scsi_inquiry_data *id; + + /* ts A61007 : now prevented in enumerate_common() */ +#if 0 + a ssert(d->idata); + a ssert(d->mdata); +#endif + + if (!d->idata->valid || !d->mdata->valid) + return 0; + + id = (struct burn_scsi_inquiry_data *)d->idata; + + memcpy(out->vendor, id->vendor, sizeof(id->vendor)); + strip_spaces(out->vendor); + memcpy(out->product, id->product, sizeof(id->product)); + strip_spaces(out->product); + memcpy(out->revision, id->revision, sizeof(id->revision)); + strip_spaces(out->revision); + strncpy(out->location, d->devname, 16); + out->location[16] = '\0'; + out->buffer_size = d->mdata->buffer_size; + out->read_dvdram = !!d->mdata->dvdram_read; + out->read_dvdr = !!d->mdata->dvdr_read; + out->read_dvdrom = !!d->mdata->dvdrom_read; + out->read_cdr = !!d->mdata->cdr_read; + out->read_cdrw = !!d->mdata->cdrw_read; + out->write_dvdram = !!d->mdata->dvdram_write; + out->write_dvdr = !!d->mdata->dvdr_write; + out->write_cdr = !!d->mdata->cdr_write; + out->write_cdrw = !!d->mdata->cdrw_write; + out->write_simulate = !!d->mdata->simulate; + out->c2_errors = !!d->mdata->c2_pointers; + out->drive = d; + /* update available block types for burners */ + if (out->write_dvdram || out->write_dvdr || + out->write_cdrw || out->write_cdr) + d->probe_write_modes(d); + out->tao_block_types = d->block_types[BURN_WRITE_TAO]; + out->sao_block_types = d->block_types[BURN_WRITE_SAO]; + out->raw_block_types = d->block_types[BURN_WRITE_RAW]; + out->packet_block_types = d->block_types[BURN_WRITE_PACKET]; + return 1; +} + +int burn_drive_scan_sync(struct burn_drive_info *drives[], + unsigned int *n_drives) +{ + /* state vars for the scan process */ + /* ts A60904 : did set some default values to feel comfortable */ + static int scanning = 0, scanned = 0, found = 0; + static unsigned num_scanned = 0, count = 0; + unsigned int i; + + /* ts A61007 : moved up to burn_drive_scan() */ + /* a ssert(burn_running); */ + + if (!scanning) { + scanning = 1; + + /* ts A61007 : test moved up to burn_drive_scan() + burn_wait_all() is obsoleted */ +#if 0 + /* make sure the drives aren't in use */ + burn_wait_all(); /* make sure the queue cleans up + before checking for the released + state */ +#endif /* 0 */ + + /* refresh the lib's drives */ + + /* ts A61115 : formerly sg_enumerate(); ata_enumerate(); */ + scsi_enumerate_drives(); + + count = burn_drive_count(); + if (count) + *drives = + malloc(sizeof(struct burn_drive_info) * count); + else + *drives = NULL; + *n_drives = scanned = found = num_scanned = 0; + } + + for (i = 0; i < count; ++i) { + if (scanned & (1 << i)) + continue; /* already scanned the device */ + + while (!drive_getcaps(&drive_array[i], + &(*drives)[num_scanned])) { + sleep(1); + } + scanned |= 1 << i; + found |= 1 << i; + num_scanned++; + (*n_drives)++; + } + + if (num_scanned == count) { + /* done scanning */ + scanning = 0; + return 1; + } + return 0; +} + + +void burn_drive_info_free(struct burn_drive_info drive_infos[]) +{ +/* ts A60904 : ticket 62, contribution by elmom */ +/* clarifying the meaning and the identity of the victim */ + + /* ts A60904 : This looks a bit weird. + burn_drive_info is not the manager of burn_drive but only its + spokesperson. To my knowlege drive_infos from burn_drive_scan() + are not memorized globally. */ + if(drive_infos != NULL) + free((void *) drive_infos); + + burn_drive_free_all(); +} + +/* ts A61001 : internal call */ +int burn_drive_forget(struct burn_drive *d, int force) +{ + int occup; + + occup = burn_drive_is_occupied(d); +/* + fprintf(stderr, "libburn: experimental: occup == %d\n",occup); +*/ + if(occup <= -2) + return 2; + if(occup > 0) + if(force < 1) + return 0; + if(occup > 10) + return 0; + + /* >>> do any drive calming here */; + + + burn_drive_force_idle(d); + if(occup > 0 && !burn_drive_is_released(d)) + burn_drive_release(d,0); + burn_drive_free(d); + return 1; +} + +/* API call */ +int burn_drive_info_forget(struct burn_drive_info *info, int force) +{ + return burn_drive_forget(info->drive, force); +} + +struct burn_disc *burn_drive_get_disc(struct burn_drive *d) +{ + /* ts A61022: SIGSEGV on calling this function with blank media */ + if(d->disc == NULL) + return NULL; + + d->disc->refcnt++; + return d->disc; +} + +void burn_drive_set_speed(struct burn_drive *d, int r, int w) +{ + d->set_speed(d, r, w); +} + +int burn_msf_to_sectors(int m, int s, int f) +{ + return (m * 60 + s) * 75 + f; +} + +void burn_sectors_to_msf(int sectors, int *m, int *s, int *f) +{ + *m = sectors / (60 * 75); + *s = (sectors - *m * 60 * 75) / 75; + *f = sectors - *m * 60 * 75 - *s * 75; +} + +int burn_drive_get_read_speed(struct burn_drive *d) +{ + return d->mdata->max_read_speed; +} + +int burn_drive_get_write_speed(struct burn_drive *d) +{ + return d->mdata->max_write_speed; +} + +/* ts A61021 : New API function */ +int burn_drive_get_min_write_speed(struct burn_drive *d) +{ + return d->mdata->min_write_speed; +} + + +/* ts A51221 */ +static char *enumeration_whitelist[BURN_DRIVE_WHITELIST_LEN]; +static int enumeration_whitelist_top = -1; + +/** Add a device to the list of permissible drives. As soon as some entry is in + the whitelist all non-listed drives are banned from enumeration. + @return 1 success, <=0 failure +*/ +int burn_drive_add_whitelist(char *device_address) +{ + char *new_item; + if(enumeration_whitelist_top+1 >= BURN_DRIVE_WHITELIST_LEN) + return 0; + enumeration_whitelist_top++; + new_item = malloc(strlen(device_address) + 1); + if (new_item == NULL) + return -1; + strcpy(new_item, device_address); + enumeration_whitelist[enumeration_whitelist_top] = new_item; + return 1; +} + +/** Remove all drives from whitelist. This enables all possible drives. */ +void burn_drive_clear_whitelist(void) +{ + int i; + for (i = 0; i <= enumeration_whitelist_top; i++) + free(enumeration_whitelist[i]); + enumeration_whitelist_top = -1; +} + +int burn_drive_is_banned(char *device_address) +{ + int i; + if(enumeration_whitelist_top<0) + return 0; + for (i = 0; i <= enumeration_whitelist_top; i++) + if (strcmp(enumeration_whitelist[i], device_address) == 0) + return 0; + return 1; +} + +/* ts A60823 */ +/** Aquire a drive with known persistent address. +*/ +int burn_drive_scan_and_grab(struct burn_drive_info *drive_infos[], char* adr, + int load) +{ + unsigned int n_drives; + int ret; + + burn_drive_clear_whitelist(); + burn_drive_add_whitelist(adr); +/* + fprintf(stderr,"libburn: experimental: burn_drive_scan_and_grab(%s)\n", + adr); +*/ + while (1) { + ret = burn_drive_scan(drive_infos, &n_drives); + if (ret < 0) + return -1; + if (ret > 0) + break; + usleep(1002); + } + if (n_drives <= 0) + return 0; +/* + fprintf(stderr, "libburn: experimental: n_drives == %d\n",n_drives); +*/ + + ret = burn_drive_grab(drive_infos[0]->drive, load); + if (ret != 1) + return -1; + return 1; +} + +/* ts A60925 */ +/** Simple debug message frontend to libdax_msgs_submit(). + If arg is not NULL, then fmt MUST contain exactly one %s and no + other sprintf() %-formatters. +*/ +int burn_drive_adr_debug_msg(char *fmt, char *arg) +{ + int ret; + char msg[4096], *msgpt; + + msgpt= msg; + if(arg != NULL) + sprintf(msg, fmt, arg); + else + msgpt= fmt; + if(libdax_messenger == NULL) + return 0; + ret = libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msgpt, 0, 0); + return ret; +} + +/* ts A60923 */ +/** Inquire the persistent address of the given drive. */ +int burn_drive_raw_get_adr(struct burn_drive *d, char adr[]) +{ + if (strlen(d->devname) >= BURN_DRIVE_ADR_LEN) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020110, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Persistent drive address too long", 0, 0); + return -1; + } + strcpy(adr,d->devname); + return 1; +} + +/* ts A60823 - A60923 */ +/** Inquire the persistent address of the given drive. */ +int burn_drive_get_adr(struct burn_drive_info *drive_info, char adr[]) +{ + int ret; + + ret = burn_drive_raw_get_adr(drive_info->drive, adr); + return ret; +} + + +/* ts A60922 ticket 33 */ +/** Evaluate wether the given address would be enumerated by libburn */ +int burn_drive_is_enumerable_adr(char *adr) +{ + return sg_is_enumerable_adr(adr); +} + +#define BURN_DRIVE_MAX_LINK_DEPTH 20 + +/* ts A60922 ticket 33 */ +/* Try to find an enumerated address with the given stat.st_rdev number */ +int burn_drive_resolve_link(char *path, char adr[], int *recursion_count) +{ + int ret; + char link_target[4096], msg[4096+100], link_adr[4096], *adrpt; + + burn_drive_adr_debug_msg("burn_drive_resolve_link( %s )", path); + if (*recursion_count >= BURN_DRIVE_MAX_LINK_DEPTH) { + burn_drive_adr_debug_msg( + "burn_drive_resolve_link aborts because link too deep", + NULL); + return 0; + } + (*recursion_count)++; + ret = readlink(path, link_target, sizeof(link_target)); + if (ret == -1) { + burn_drive_adr_debug_msg("readlink( %s ) returns -1", path); + return 0; + } + if (ret >= sizeof(link_target) - 1) { + sprintf(msg,"readlink( %s ) returns %d (too much)", path, ret); + burn_drive_adr_debug_msg(msg, NULL); + return -1; + } + link_target[ret] = 0; + adrpt= link_target; + if (link_target[0] != '/') { + strcpy(link_adr, path); + if ((adrpt = strrchr(link_adr, '/')) != NULL) { + strcpy(adrpt + 1, link_target); + adrpt = link_adr; + } else + adrpt = link_target; + } + ret = burn_drive_convert_fs_adr_sub(adrpt, adr, recursion_count); + sprintf(msg,"burn_drive_convert_fs_adr( %s ) returns %d", + link_target, ret); + burn_drive_adr_debug_msg(msg, NULL); + return ret; +} + +/* ts A60922 - A61014 ticket 33 */ +/* Try to find an enumerated address with the given stat.st_rdev number */ +int burn_drive_find_devno(dev_t devno, char adr[]) +{ + char fname[4096], msg[4096+100]; + int ret = 0, first = 1; + struct stat stbuf; + burn_drive_enumerator_t enm; + + while (1) { + ret = sg_give_next_adr(&enm, fname, sizeof(fname), first); + if(ret <= 0) + break; + first = 0; + ret = stat(fname, &stbuf); + if(ret == -1) + continue; + if(devno != stbuf.st_rdev) + continue; + if(strlen(fname) >= BURN_DRIVE_ADR_LEN) + return -1; + + sprintf(msg, "burn_drive_find_devno( 0x%lX ) found %s", + (long) devno, fname); + burn_drive_adr_debug_msg(msg, NULL); + strcpy(adr, fname); + { ret = 1; goto ex;} + } + ret = 0; +ex:; + if (first == 0) + sg_give_next_adr(&enm, fname, sizeof(fname), -1); + return ret; +} + +/* ts A60923 */ +/** Try to obtain host,channel,target,lun from path. + @return 1 = success , 0 = failure , -1 = severe error +*/ +int burn_drive_obtain_scsi_adr(char *path, + int *bus_no, int *host_no, int *channel_no, + int *target_no, int *lun_no) +{ + int ret, i; + char adr[BURN_DRIVE_ADR_LEN]; + + /* open drives cannot be inquired by sg_obtain_scsi_adr() */ + for (i = 0; i < drivetop + 1; i++) { + if (drive_array[i].global_index < 0) + continue; + ret = burn_drive_raw_get_adr(&(drive_array[i]),adr); + if (ret < 0) + return -1; + if (ret == 0) + continue; + if (strcmp(adr, path) == 0) { + *host_no = drive_array[i].host; + *channel_no = drive_array[i].channel; + *target_no = drive_array[i].id; + *lun_no = drive_array[i].lun; + *bus_no = drive_array[i].bus_no; + if (*host_no < 0 || *channel_no < 0 || + *target_no < 0 || *lun_no < 0) + return 0; + return 1; + } + } + + ret = sg_obtain_scsi_adr(path, bus_no, host_no, channel_no, + target_no, lun_no); + return ret; +} + +/* ts A60923 */ +int burn_drive_convert_scsi_adr(int bus_no, int host_no, int channel_no, + int target_no, int lun_no, char adr[]) +{ + char fname[4096],msg[4096+100]; + int ret = 0, first = 1, i_bus_no = -1; + int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1; + burn_drive_enumerator_t enm; + + sprintf(msg,"burn_drive_convert_scsi_adr( %d,%d,%d,%d,%d )", + bus_no, host_no, channel_no, target_no, lun_no); + burn_drive_adr_debug_msg(msg, NULL); + + while (1) { + ret= sg_give_next_adr(&enm, fname, sizeof(fname), first); + if(ret <= 0) + break; + first = 0; + ret = burn_drive_obtain_scsi_adr(fname, &i_bus_no, &i_host_no, + &i_channel_no, &i_target_no, &i_lun_no); + if(ret <= 0) + continue; + if(bus_no >=0 && i_bus_no != bus_no) + continue; + if(host_no >=0 && i_host_no != host_no) + continue; + if(channel_no >= 0 && i_channel_no != channel_no) + continue; + if(target_no >= 0 && i_target_no != target_no) + continue; + if(lun_no >= 0 && i_lun_no != lun_no) + continue; + if(strlen(fname) >= BURN_DRIVE_ADR_LEN) + { ret = -1; goto ex;} + burn_drive_adr_debug_msg( + "burn_drive_convert_scsi_adr() found %s", fname); + strcpy(adr, fname); + { ret = 1; goto ex;} + } + ret = 0; +ex:; + if (first == 0) + sg_give_next_adr(&enm, fname, sizeof(fname), -1); + return ret; +} + +/* ts A60922 ticket 33 */ +/* Try to find an enumerated address with the same host,channel,target,lun + as path */ +int burn_drive_find_scsi_equiv(char *path, char adr[]) +{ + int ret = 0; + int bus_no, host_no, channel_no, target_no, lun_no; + char msg[4096]; + + ret = burn_drive_obtain_scsi_adr(path, &bus_no, &host_no, &channel_no, + &target_no, &lun_no); + if(ret <= 0) { + sprintf(msg,"burn_drive_obtain_scsi_adr( %s ) returns %d", + path, ret); + burn_drive_adr_debug_msg(msg, NULL); + return 0; + } + sprintf(msg, "burn_drive_find_scsi_equiv( %s ) : (%d),%d,%d,%d,%d", + path, bus_no, host_no, channel_no, target_no, lun_no); + burn_drive_adr_debug_msg(msg, NULL); + + ret= burn_drive_convert_scsi_adr(-1, host_no, channel_no, target_no, + lun_no, adr); + return ret; +} + + +/* ts A60922 ticket 33 */ +/** Try to convert a given existing filesystem address into a persistent drive + address. */ +int burn_drive_convert_fs_adr_sub(char *path, char adr[], int *rec_count) +{ + int ret; + struct stat stbuf; + + burn_drive_adr_debug_msg("burn_drive_convert_fs_adr( %s )", path); + if(burn_drive_is_enumerable_adr(path)) { + if(strlen(path) >= BURN_DRIVE_ADR_LEN) + return -1; + burn_drive_adr_debug_msg( + "burn_drive_is_enumerable_adr( %s ) is true", path); + strcpy(adr, path); + return 1; + } + + if(lstat(path, &stbuf) == -1) { + burn_drive_adr_debug_msg("lstat( %s ) returns -1", path); + return 0; + } + if((stbuf.st_mode & S_IFMT) == S_IFLNK) { + ret = burn_drive_resolve_link(path, adr, rec_count); + if(ret > 0) + return 1; + burn_drive_adr_debug_msg("link fallback via stat( %s )", path); + if(stat(path, &stbuf) == -1) { + burn_drive_adr_debug_msg("stat( %s ) returns -1",path); + return 0; + } + } + if((stbuf.st_mode&S_IFMT) == S_IFBLK || + (stbuf.st_mode&S_IFMT) == S_IFCHR) { + ret = burn_drive_find_devno(stbuf.st_rdev, adr); + if(ret > 0) + return 1; + ret = burn_drive_find_scsi_equiv(path, adr); + if(ret > 0) + return 1; + } + burn_drive_adr_debug_msg("Nothing found for %s", path); + return 0; +} + +/** Try to convert a given existing filesystem address into a persistent drive + address. */ +int burn_drive_convert_fs_adr(char *path, char adr[]) +{ + int ret, rec_count = 0; + + ret = burn_drive_convert_fs_adr_sub(path, adr, &rec_count); + return ret; +} + + +/** A pacifier function suitable for burn_abort. + @param handle If not NULL, a pointer to a text suitable for printf("%s") +*/ +int burn_abort_pacifier(void *handle, int patience, int elapsed) +{ + char *prefix= "libburn : "; + + if(handle!=NULL) + prefix= handle; + fprintf(stderr, + "\r%sABORT : Waiting for drive to finish ( %d s, %d max)", + (char *) prefix, elapsed, patience); + return(1); +} + + +/** Abort any running drive operation and finish libburn. + @param patience Maximum number of seconds to wait for drives to finish + @param pacifier_func Function to produce appeasing messages. See + burn_abort_pacifier() for an example. + @return 1 ok, all went well + 0 had to leave a drive in unclean state + <0 severe error, do no use libburn again +*/ +int burn_abort(int patience, + int (*pacifier_func)(void *handle, int patience, int elapsed), + void *handle) +{ + int ret, i, occup, still_not_done= 1, pacifier_off= 0, first_round= 1; + unsigned long wait_grain= 100000; + time_t start_time, current_time, pacifier_time, end_time; + + current_time = start_time = pacifier_time = time(0); + end_time = start_time + patience; + while(current_time-end_time < patience) { + still_not_done = 0; + + for(i = 0; i < drivetop + 1; i++) { + occup = burn_drive_is_occupied(&(drive_array[i])); + if(occup == -2) + continue; + if(occup <= 10) { + burn_drive_forget(&(drive_array[i]), 1); + } else if(occup <= 100) { + if(first_round) + burn_drive_cancel(&(drive_array[i])); + still_not_done++; + } else if(occup <= 1000) { + still_not_done++; + } + } + first_round = 0; + + if(still_not_done == 0) + break; + usleep(wait_grain); + current_time = time(0); + if(current_time>pacifier_time) { + if(pacifier_func != NULL && !pacifier_off) { + ret = (*pacifier_func)(handle, patience, + current_time-start_time); + pacifier_off = (ret <= 0); + } + pacifier_time = current_time; + } + } + burn_finish(); + return(still_not_done == 0); +} + + +/* ts A61020 API function */ +int burn_drive_get_start_end_lba(struct burn_drive *d, + int *start_lba, int *end_lba, int flag) +{ + if (d->start_lba == -2000000000 || d->end_lba == -2000000000) + return 0; + *start_lba = d->start_lba; + *end_lba= d->end_lba; + return 1; +} + + +/* ts A61020 API function */ +int burn_disc_pretend_blank(struct burn_drive *d) +{ + if (d->status != BURN_DISC_UNREADY && + d->status != BURN_DISC_UNSUITABLE) + return 0; + d->status = BURN_DISC_BLANK; + return 1; +} + +/* ts A61106 API function */ +int burn_disc_pretend_full(struct burn_drive *d) +{ + if (d->status != BURN_DISC_UNREADY && + d->status != BURN_DISC_UNSUITABLE) + return 0; + d->status = BURN_DISC_FULL; + return 1; +} + +/* ts A61021: new API function */ +int burn_disc_read_atip(struct burn_drive *d) +{ + if (burn_drive_is_released(d)) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x0002010e, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Attempt to read ATIP from ungrabbed drive", + 0, 0); + return -1; + } + if (d->current_profile == -1 || d->current_is_cd_profile) { + d->read_atip(d); + /* >>> some control of success would be nice :) */ + } else { + /* mmc5r03c.pdf 6.26.3.6.3 : ATIP is undefined for non-CD */; + return 0; + } + return 1; +} + +/* ts A61110 : new API function */ +int burn_disc_track_lba_nwa(struct burn_drive *d, struct burn_write_opts *o, + int trackno, int *lba, int *nwa) +{ + int ret; + + if (burn_drive_is_released(d)) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x0002011b, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Attempt to read track info from ungrabbed drive", + 0, 0); + return -1; + } + if (d->busy != BURN_DRIVE_IDLE) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x0002011c, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Attempt to read track info from busy drive", + 0, 0); + return -1; + } + if (o!=NULL) + d->send_write_parameters(d, o); + ret = d->get_nwa(d, trackno, lba, nwa); + return ret; +} + + +/* ts A61202 : New API function */ +int burn_disc_get_profile(struct burn_drive *d, int *pno, char name[80]) +{ + *pno = d->current_profile; + strcpy(name,d->current_profile_text); + return *pno >= 0; +} + + +/* ts A61223 : New API function */ +int burn_drive_wrote_well(struct burn_drive *d) +{ + return !d->cancel; +} + + +/* ts A61226 */ +int burn_speed_descriptor_new(struct burn_speed_descriptor **s, + struct burn_speed_descriptor *prev, + struct burn_speed_descriptor *next, int flag) +{ + struct burn_speed_descriptor *o; + + (*s) = o = malloc(sizeof(struct burn_speed_descriptor)); + if (o == NULL) + return -1; + o->source = 0; + o->profile_loaded = -2; + o->profile_name[0] = 0; + o->wrc = 0; + o->exact = 0; + o->mrw = 0; + o->end_lba = -1; + o->write_speed = 0; + o->read_speed = 0; + + o->prev = prev; + if (prev != NULL) { + next = prev->next; + prev->next = o; + } + o->next = next; + if (next != NULL) + next->prev = o; + return 1; +} + + +/* ts A61226 */ +/* @param flag bit0= destroy whole next-chain of descriptors */ +int burn_speed_descriptor_destroy(struct burn_speed_descriptor **s, int flag) +{ + struct burn_speed_descriptor *next, *o; + + if ((*s) == NULL) + return 0; + if (flag&1) + for (o = (*s); o->prev != NULL; o = o->prev); + else + o = (*s); + next = o->next; + if (next != NULL) + next->prev = o->prev; + if (o->prev != NULL) + o->prev->next = next; + free((char *) (*s)); + (*s) = NULL; + if (flag&1) + return burn_speed_descriptor_destroy(&next, flag&1); + return 1; +} + + +/* ts A61226 */ +int burn_speed_descriptor_copy(struct burn_speed_descriptor *from, + struct burn_speed_descriptor *to, int flag) +{ + to->source = from->source; + to->profile_loaded = from->profile_loaded; + strcpy(to->profile_name, from->profile_name); + to->wrc = from->wrc; + to->exact = from->exact; + to->mrw = from->mrw; + to->end_lba = from->end_lba; + to->write_speed = from->write_speed; + to->read_speed = from->read_speed; + return 1; +} + + +/* ts A61226 : free dynamically allocated sub data of struct scsi_mode_data */ +int burn_mdata_free_subs(struct scsi_mode_data *m) +{ + burn_speed_descriptor_destroy(&(m->speed_descriptors), 1); + return 1; +} + + +/* ts A61226 : API function */ +int burn_drive_get_speedlist(struct burn_drive *d, + struct burn_speed_descriptor **speed_list) +{ + int ret; + struct burn_speed_descriptor *sd, *csd = NULL; + + (*speed_list) = NULL; + for (sd = d->mdata->speed_descriptors; sd != NULL; sd = sd->next) { + ret = burn_speed_descriptor_new(&csd, NULL, csd, 0); + if (ret <= 0) + return -1; + burn_speed_descriptor_copy(sd, csd, 0); + } + (*speed_list) = csd; + return (csd != NULL); +} + + +/* ts A61226 : API function */ +int burn_drive_free_speedlist(struct burn_speed_descriptor **speed_list) +{ + return burn_speed_descriptor_destroy(speed_list, 1); +} + diff --git a/libburn/tags/ZeroThreeZero/libburn/drive.h b/libburn/tags/ZeroThreeZero/libburn/drive.h new file mode 100644 index 00000000..dca49d46 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/drive.h @@ -0,0 +1,98 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __DRIVE +#define __DRIVE + +#include "libburn.h" +#include "toc.h" +#include "structure.h" + +struct burn_drive; +struct command; +struct mempage; +struct scsi_mode_data; +struct burn_speed_descriptor; + +#define LEAD_IN 1 +#define GAP 2 +#define USER_DATA 3 +#define LEAD_OUT 4 +#define SYNC 5 + +#define SESSION_LEADOUT_ENTRY(d,s) (d)->toc->session[(s)].leadout_entry + +#define CURRENT_SESSION_START(d) \ + burn_msf_to_lba(d->toc->session[d->currsession].start_m, \ + d->toc->session[d->currsession].start_s, \ + d->toc->session[d->currsession].start_f) + +#define SESSION_END(d,s) \ + TOC_ENTRY_PLBA((d)->toc, SESSION_LEADOUT_ENTRY((d), (s))) + +#define PREVIOUS_SESSION_END(d) \ + TOC_ENTRY_PLBA((d)->toc, SESSION_LEADOUT_ENTRY((d), (d)->currsession-1)) + +#define LAST_SESSION_END(d) \ + TOC_ENTRY_PLBA((d)->toc, \ + SESSION_LEADOUT_ENTRY((d), (d)->toc->sessions-1)) + +struct burn_drive *burn_drive_register(struct burn_drive *); +int burn_drive_unregister(struct burn_drive *d); + +unsigned int burn_drive_count(void); + +/* ts A61007 */ +/* void burn_wait_all(void); */ +int burn_drives_are_clear(void); + +int burn_sector_length_write(struct burn_drive *d); +int burn_track_control(struct burn_drive *d, int); +void burn_write_empty_sector(int fd); +void burn_write_empty_subcode(int fd); +void burn_drive_free(struct burn_drive *d); +void burn_drive_free_all(void); + +int burn_drive_scan_sync(struct burn_drive_info *drives[], + unsigned int *n_drives); +void burn_disc_erase_sync(struct burn_drive *d, int fast); +int burn_drive_get_block_types(struct burn_drive *d, + enum burn_write_types write_type); + +int burn_drive_is_open(struct burn_drive *d); +int burn_drive_is_occupied(struct burn_drive *d); +int burn_drive_forget(struct burn_drive *d, int force); +int burn_drive_convert_fs_adr_sub(char *path, char adr[], int *rec_count); + +/* ts A61021 : the unspecific part of sg.c:enumerate_common() +*/ +int burn_setup_drive(struct burn_drive *d, char *fname); + +/* ts A61021 : after-setup activities from sg.c:enumerate_common() +*/ +struct burn_drive *burn_drive_finish_enum(struct burn_drive *d); + +/* ts A61125 : media status aspects of burn_drive_grab() */ +int burn_drive_inquire_media(struct burn_drive *d); + +/* ts A61125 : model aspects of burn_drive_release */ +int burn_drive_mark_unready(struct burn_drive *d); + + +/* ts A61226 */ +int burn_speed_descriptor_new(struct burn_speed_descriptor **s, + struct burn_speed_descriptor *prev, + struct burn_speed_descriptor *next, int flag); + +/* ts A61226 */ +/* @param flag bit0= destroy whole next-chain of descriptors */ +int burn_speed_descriptor_destroy(struct burn_speed_descriptor **s, int flag); + + +/* ts A61226 : free dynamically allocated sub data of struct scsi_mode_data */ +int burn_mdata_free_subs(struct scsi_mode_data *m); + + +/* ts A61230 */ +void burn_disc_format_sync(struct burn_drive *d, off_t size, int flag); + +#endif /* __DRIVE */ diff --git a/libburn/tags/ZeroThreeZero/libburn/error.h b/libburn/tags/ZeroThreeZero/libburn/error.h new file mode 100644 index 00000000..74d4f68d --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/error.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode; t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __ERROR_H +#define __ERROR_H + +#define BE_CANCELLED 1 + +#endif /* __ERROR_H */ diff --git a/libburn/tags/ZeroThreeZero/libburn/file.c b/libburn/tags/ZeroThreeZero/libburn/file.c new file mode 100644 index 00000000..809b8428 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/file.c @@ -0,0 +1,187 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include "source.h" +#include "libburn.h" +#include "file.h" + +/* main channel data can be padded on read, but 0 padding the subs will make +an unreadable disc */ + + +/* This is a generic OS oriented function wrapper which compensates + shortcommings of read() in respect to a guaranteed amount of return data. + See man 2 read , paragraph "RETURN VALUE". + Possibly libburn/file.c is not the right storage location for this. + To make it ready for a move, this function is not declared static. +*/ +static int read_full_buffer(int fd, unsigned char *buffer, int size) +{ + int ret,summed_ret = 0; + + /* make safe against partial buffer returns */ + while (1) { + ret = read(fd, buffer + summed_ret, size - summed_ret); + if (ret <= 0) + break; + summed_ret += ret; + if (summed_ret >= size) + break; + } + if (ret < 0) /* error encountered. abort immediately */ + return ret; + return summed_ret; +} + + +static int file_read(struct burn_source *source, + unsigned char *buffer, + int size) +{ + struct burn_source_fd *fs = source->data; + + return read_full_buffer(fs->datafd, buffer, size); +} + +static int file_read_sub(struct burn_source *source, + unsigned char *buffer, + int size) +{ + struct burn_source_file *fs = source->data; + + return read_full_buffer(fs->subfd, buffer, size); +} + +static void file_free(struct burn_source *source) +{ + struct burn_source_file *fs = source->data; + + close(fs->datafd); + if (source->read_sub) + close(fs->subfd); + free(fs); +} + +static off_t file_size(struct burn_source *source) +{ + struct stat buf; + struct burn_source_file *fs = source->data; + + if (fstat(fs->datafd, &buf) == -1) + return (off_t) 0; + /* for now we keep it compatible to the old (int) return value */ + if(buf.st_size >= 1308622848) /* 2 GB - 800 MB to prevent rollover */ + return (off_t) 1308622848; + return (off_t) buf.st_size; +} + +struct burn_source *burn_file_source_new(const char *path, const char *subpath) +{ + struct burn_source_file *fs; + struct burn_source *src; + int fd1, fd2 = 0; + + if (!path) + return NULL; + fd1 = open(path, O_RDONLY); + if (fd1 == -1) + return NULL; + if (subpath) { + fd2 = open(subpath, O_RDONLY); + if (fd2 == -1) { + close(fd1); + return NULL; + } + } + fs = malloc(sizeof(struct burn_source_file)); + fs->datafd = fd1; + + if (subpath) + fs->subfd = fd2; + + src = burn_source_new(); + src->read = file_read; + if (subpath) + src->read_sub = file_read_sub; + + src->get_size = file_size; + src->free_data = file_free; + src->data = fs; + return src; +} + + +/* ------ provisory location for the new source subclass fd --------- */ + +static off_t fd_get_size(struct burn_source *source) +{ + struct stat buf; + struct burn_source_fd *fs = source->data; + + if (fs->fixed_size > 0) + return fs->fixed_size; + if (fstat(fs->datafd, &buf) == -1) + return (off_t) 0; + /* for now we keep it compatible to the old (int) return value */ + if (buf.st_size >= 1308622848) /* 2 GB - 800 MB to prevent rollover */ + return (off_t) 1308622848; + return buf.st_size; +} + +static int fd_read(struct burn_source *source, + unsigned char *buffer, + int size) +{ + struct burn_source_fd *fs = source->data; + + return read_full_buffer(fs->datafd, buffer, size); +} + + +static int fd_read_sub(struct burn_source *source, + unsigned char *buffer, + int size) +{ + struct burn_source_fd *fs = source->data; + + return read_full_buffer(fs->subfd, buffer, size); +} + + +static void fd_free_data(struct burn_source *source) +{ + struct burn_source_fd *fs = source->data; + + close(fs->datafd); + if (source->read_sub) + close(fs->subfd); + free(fs); +} + + +struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size) +{ + struct burn_source_fd *fs; + struct burn_source *src; + + if (datafd == -1) + return NULL; + fs = malloc(sizeof(struct burn_source_fd)); + fs->datafd = datafd; + fs->subfd = subfd; + fs->fixed_size = size; + + src = burn_source_new(); + src->read = fd_read; + if(subfd != -1) + src->read = fd_read_sub; + src->get_size = fd_get_size; + src->free_data = fd_free_data; + src->data = fs; + return src; +} + diff --git a/libburn/tags/ZeroThreeZero/libburn/file.h b/libburn/tags/ZeroThreeZero/libburn/file.h new file mode 100644 index 00000000..96c1a692 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/file.h @@ -0,0 +1,22 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__FILE_H +#define BURN__FILE_H + +struct burn_source_file +{ + int datafd; + int subfd; +}; + + +/* ------ provisory location for the new source subclass fd --------- */ + +struct burn_source_fd +{ + int datafd; + int subfd; + off_t fixed_size; +}; + +#endif /* LIBBURN__FILE_H */ diff --git a/libburn/tags/ZeroThreeZero/libburn/init.c b/libburn/tags/ZeroThreeZero/libburn/init.c new file mode 100644 index 00000000..e9b33827 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/init.c @@ -0,0 +1,276 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include <unistd.h> + +/* ts A61007 */ +/* #include <a ssert.h> */ + +#include <stdio.h> +#include <signal.h> +#include <string.h> + +#include "init.h" +#include "sg.h" +#include "error.h" +#include "libburn.h" +#include "drive.h" + +/* ts A60825 : The storage location for back_hacks.h variables. */ +#define BURN_BACK_HACKS_INIT 1 +#include "back_hacks.h" + +/* ts A60924 : a new message handling facility */ +#include "libdax_msgs.h" +struct libdax_msgs *libdax_messenger= NULL; + + +int burn_running = 0; + +/* ts A60813 : wether to use O_EXCL and/or O_NONBLOCK in libburn/sg.c */ +int burn_sg_open_o_excl = 1; + +/* O_NONBLOCK was hardcoded in enumerate_ata() which i hardly use. + For enumerate_sg() it seems ok. + So it should stay default mode until enumerate_ata() without O_NONBLOCK + has been thoroughly tested. */ +int burn_sg_open_o_nonblock = 1; + +/* wether to take a busy drive as an error */ +/* Caution: this is implemented by a rough hack and eventually leads + to unconditional abort of the process */ +int burn_sg_open_abort_busy = 0; + + +/* ts A61002 */ + +#include "cleanup.h" + +/* Parameters for builtin abort handler */ +static char abort_message_prefix[81] = {"libburn : "}; +static pid_t abort_control_pid= 0; + + +/* ts A60925 : ticket 74 */ +/** Create the messenger object for libburn. */ +int burn_msgs_initialize(void) +{ + int ret; + + if(libdax_messenger == NULL) { + ret = libdax_msgs_new(&libdax_messenger,0); + if (ret <= 0) + return 0; + } + libdax_msgs_set_severities(libdax_messenger, LIBDAX_MSGS_SEV_NEVER, + LIBDAX_MSGS_SEV_FATAL, "libburn: ", 0); + return 1; +} + +/* ts A60924 : ticket 74 : Added use of global libdax_messenger */ +int burn_initialize(void) +{ + int ret; + + if (burn_running) + return 1; + ret = burn_msgs_initialize(); + if (ret <= 0) + return 0; + burn_running = 1; + return 1; +} + +void burn_finish(void) +{ + /* ts A61007 : assume no messageing system */ + /* a ssert(burn_running); */ + if (!burn_running) + return; + + /* ts A61007 */ + /* burn_wait_all(); */ + if (!burn_drives_are_clear()) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020107, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Drive is busy on attempt to shut down library", 0, 0); + return; + } + + /* ts A60904 : ticket 62, contribution by elmom : name addon "_all" */ + burn_drive_free_all(); + + /* ts A60924 : ticket 74 */ + libdax_msgs_destroy(&libdax_messenger,0); + + burn_running = 0; +} + + +/* ts A60813 */ +/** API function. See libburn.h */ +void burn_preset_device_open(int exclusive, int blocking, int abort_on_busy) +{ + /* ts A61007 */ + /* a ssert(burn_running); */ + if (!burn_running) + return; + + burn_sg_open_o_excl= exclusive; + burn_sg_open_o_nonblock= !blocking; + burn_sg_open_abort_busy= !!abort_on_busy; +} + + +/* ts A60924 : ticket 74 */ +/** Control queueing and stderr printing of messages from libburn. + Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", + "NOTE", "UPDATE", "DEBUG", "ALL". + @param queue_severity Gives the minimum limit for messages to be queued. + Default: "NEVER". If you queue messages then you + must consume them by burn_msgs_obtain(). + @param print_severity Does the same for messages to be printed directly + to stderr. + @param print_id A text prefix to be printed before the message. + @return >0 for success, <=0 for error + +*/ +int burn_msgs_set_severities(char *queue_severity, + char *print_severity, char *print_id) +{ + int ret, queue_sevno, print_sevno; + + ret = libdax_msgs__text_to_sev(queue_severity, &queue_sevno, 0); + if (ret <= 0) + return 0; + ret = libdax_msgs__text_to_sev(print_severity, &print_sevno, 0); + if (ret <= 0) + return 0; + ret = libdax_msgs_set_severities(libdax_messenger, queue_sevno, + print_sevno, print_id, 0); + if (ret <= 0) + return 0; + return 1; +} + + +/* ts A60924 : ticket 74 */ +#define BURM_MSGS_MESSAGE_LEN 4096 + +/** Obtain the oldest pending libburn message from the queue which has at + least the given minimum_severity. This message and any older message of + lower severity will get discarded from the queue and is then lost forever. + Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", + "NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER" + will discard the whole queue. + @param error_code Will become a unique error code as liste in + libburn/libdax_msgs.h + @param msg_text Must provide at least BURM_MSGS_MESSAGE_LEN bytes. + @param os_errno Will become the eventual errno related to the message + @param severity Will become the severity related to the message and + should provide at least 80 bytes. + @return 1 if a matching item was found, 0 if not, <0 for severe errors +*/ +int burn_msgs_obtain(char *minimum_severity, + int *error_code, char msg_text[], int *os_errno, + char severity[]) +{ + int ret, minimum_sevno, sevno, priority; + char *textpt, *sev_name; + struct libdax_msgs_item *item = NULL; + + ret = libdax_msgs__text_to_sev(minimum_severity, &minimum_sevno, 0); + if (ret <= 0) + return 0; + ret = libdax_msgs_obtain(libdax_messenger, &item, minimum_sevno, + LIBDAX_MSGS_PRIO_ZERO, 0); + if (ret <= 0) + goto ex; + ret = libdax_msgs_item_get_msg(item, error_code, &textpt, os_errno, 0); + if (ret <= 0) + goto ex; + strncpy(msg_text, textpt, BURM_MSGS_MESSAGE_LEN-1); + if(strlen(textpt) >= BURM_MSGS_MESSAGE_LEN) + msg_text[BURM_MSGS_MESSAGE_LEN-1] = 0; + + severity[0]= 0; + ret = libdax_msgs_item_get_rank(item, &sevno, &priority, 0); + if(ret <= 0) + goto ex; + ret = libdax_msgs__sev_to_text(sevno, &sev_name, 0); + if(ret <= 0) + goto ex; + strcpy(severity,sev_name); + + ret = 1; +ex: + libdax_msgs_destroy_item(libdax_messenger, &item, 0); + return ret; +} + +int burn_builtin_abort_handler(void *handle, int signum, int flag) +{ + if(getpid() != abort_control_pid) { + +#ifdef Not_yeT + pthread_t thread_id; + + /* >>> need better handling of self-induced SIGs + like SIGSEGV or SIGFPE. + Like bonking the control thread if it did not show up + after a short while. + */ + + /* >>> if this is a non-fatal signal : return -2 */ + + thread_id = pthread_self(); + /* >>> find thread_id in worker list of async.c */ + /* >>> if owning a drive : mark idle and canceled + (can't do anything more) */ + + usleep(1000000); /* calm down */ + + /* forward signal to control thread */ + if (abort_control_pid>1) + kill(abort_control_pid, signum); + + /* >>> ??? end thread */; + +#else + usleep(1000000); /* calm down */ + return -2; +#endif /* ! Not_yeT */ + + } + Cleanup_set_handlers(NULL, NULL, 2); + fprintf(stderr,"%sABORT : Trying to shut down drive and library\n", + abort_message_prefix); + fprintf(stderr, + "%sABORT : Wait the normal burning time before any kill -9\n", + abort_message_prefix); + close(0); /* somehow stdin as input blocks abort until EOF */ + burn_abort(4440, burn_abort_pacifier, abort_message_prefix); + fprintf(stderr, + "\n%sABORT : Program done. Even if you do not see a shell prompt.\n\n", + abort_message_prefix); + return(1); +} + +void burn_set_signal_handling(void *handle, burn_abort_handler_t handler, + int mode) +{ + if(handler == NULL && mode == 0) { + handler = burn_builtin_abort_handler; +/* + fprintf(stderr, "libburn_experimental: activated burn_builtin_abort_handler() with handle '%s'\n",(handle==NULL ? "libburn : " : (char *) handle)); +*/ + + } + strcpy(abort_message_prefix, "libburn : "); + if(handle != NULL) + strncpy(abort_message_prefix, (char *) handle, + sizeof(abort_message_prefix)-1); + abort_message_prefix[sizeof(abort_message_prefix)-1] = 0; + abort_control_pid= getpid(); + Cleanup_set_handlers(handle, (Cleanup_app_handler_T) handler, mode|4); +} + diff --git a/libburn/tags/ZeroThreeZero/libburn/init.h b/libburn/tags/ZeroThreeZero/libburn/init.h new file mode 100644 index 00000000..c3e9b9a8 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/init.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__INIT_H +#define BURN__INIT_H + +extern int burn_running; + +#endif /* BURN__INIT_H */ diff --git a/libburn/tags/ZeroThreeZero/libburn/lec.c b/libburn/tags/ZeroThreeZero/libburn/lec.c new file mode 100644 index 00000000..9141593a --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/lec.c @@ -0,0 +1,451 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* borrowed HEAVILY from cdrdao */ + +#include <string.h> +#include "lec.h" + +#define LEC_HEADER_OFFSET 12 +#define LEC_MODE1_P_PARITY_OFFSET 2076 +#define LEC_MODE1_Q_PARITY_OFFSET 2248 + +static unsigned char gf8_ilog[255] = { + 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, + 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, + 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, + 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, + 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, + 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, + 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, + 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, + 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, + 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, + 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, + 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, + 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, + 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, + 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, + 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, + 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, + 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, + 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, +}; +static unsigned char gf8_log[256] = { + 0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, + 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, + 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, + 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, + 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253, + 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, + 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, + 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, + 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, + 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, + 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, + 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, + 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, + 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, + 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, + 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, + 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, + 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, + 173, 232, 116, 214, 244, 234, 168, 80, 88, 175, +}; +static unsigned char gf8_q_coeffs[2][45] = { + {97, 251, 133, 60, 82, 160, 155, 201, 8, 112, 246, 11, 21, 42, 157, + 169, 80, 174, 232, 230, 172, 211, 241, 18, 68, 216, 44, 121, 9, 200, + 75, 103, 221, 252, 96, 176, 88, 167, 114, 76, 199, 26, 1, 0, 0}, + {190, 96, 250, 132, 59, 81, 159, 154, 200, 7, 111, 245, 10, 20, 41, + 156, 168, 79, 173, 231, 229, 171, 210, 240, 17, 67, 215, 43, 120, 8, + 199, 74, 102, 220, 251, 95, 175, 87, 166, 113, 75, 198, 25, 0, 0} +}; +static unsigned char gf8_p_coeffs[2][26] = { + {230, 172, 211, 241, 18, 68, 216, 44, 121, 9, 200, 75, 103, 221, 252, + 96, 176, 88, 167, 114, 76, 199, 26, 1, 0, 0}, + {231, 229, 171, 210, 240, 17, 67, 215, 43, 120, 8, 199, 74, 102, 220, + 251, 95, 175, 87, 166, 113, 75, 198, 25, 0, 0} +}; + +static unsigned char yellowbook_scrambler[2340] = { + 1, 128, 0, 96, 0, 40, 0, 30, 128, 8, 96, 6, 168, 2, 254, 129, 128, 96, + 96, 40, 40, 30, 158, + 136, 104, 102, 174, 170, 252, 127, 1, 224, 0, 72, 0, 54, 128, 22, 224, + 14, 200, 4, 86, 131, 126, 225, + 224, 72, 72, 54, 182, 150, 246, 238, 198, 204, 82, 213, 253, 159, 1, + 168, 0, 126, 128, 32, 96, 24, 40, + 10, 158, 135, 40, 98, 158, 169, 168, 126, 254, 160, 64, 120, 48, 34, + 148, 25, 175, 74, 252, 55, 1, 214, + 128, 94, 224, 56, 72, 18, 182, 141, 182, 229, 182, 203, 54, 215, 86, + 222, 190, 216, 112, 90, 164, 59, 59, + 83, 83, 125, 253, 225, 129, 136, 96, 102, 168, 42, 254, 159, 0, 104, 0, + 46, 128, 28, 96, 9, 232, 6, + 206, 130, 212, 97, 159, 104, 104, 46, 174, 156, 124, 105, 225, 238, + 200, 76, 86, 181, 254, 247, 0, 70, 128, + 50, 224, 21, 136, 15, 38, 132, 26, 227, 75, 9, 247, 70, 198, 178, 210, + 245, 157, 135, 41, 162, 158, 249, + 168, 66, 254, 177, 128, 116, 96, 39, 104, 26, 174, 139, 60, 103, 81, + 234, 188, 79, 49, 244, 20, 71, 79, + 114, 180, 37, 183, 91, 54, 187, 86, 243, 126, 197, 224, 83, 8, 61, 198, + 145, 146, 236, 109, 141, 237, 165, + 141, 187, 37, 179, 91, 53, 251, 87, 3, 126, 129, 224, 96, 72, 40, 54, + 158, 150, 232, 110, 206, 172, 84, + 125, 255, 97, 128, 40, 96, 30, 168, 8, 126, 134, 160, 98, 248, 41, 130, + 158, 225, 168, 72, 126, 182, 160, + 118, 248, 38, 194, 154, 209, 171, 28, 127, 73, 224, 54, 200, 22, 214, + 142, 222, 228, 88, 75, 122, 183, 99, + 54, 169, 214, 254, 222, 192, 88, 80, 58, 188, 19, 49, 205, 212, 85, + 159, 127, 40, 32, 30, 152, 8, 106, + 134, 175, 34, 252, 25, 129, 202, 224, 87, 8, 62, 134, 144, 98, 236, 41, + 141, 222, 229, 152, 75, 42, 183, + 95, 54, 184, 22, 242, 142, 197, 164, 83, 59, 125, 211, 97, 157, 232, + 105, 142, 174, 228, 124, 75, 97, 247, + 104, 70, 174, 178, 252, 117, 129, 231, 32, 74, 152, 55, 42, 150, 159, + 46, 232, 28, 78, 137, 244, 102, 199, + 106, 210, 175, 29, 188, 9, 177, 198, 244, 82, 199, 125, 146, 161, 173, + 184, 125, 178, 161, 181, 184, 119, 50, + 166, 149, 186, 239, 51, 12, 21, 197, 207, 19, 20, 13, 207, 69, 148, 51, + 47, 85, 220, 63, 25, 208, 10, + 220, 7, 25, 194, 138, 209, 167, 28, 122, 137, 227, 38, 201, 218, 214, + 219, 30, 219, 72, 91, 118, 187, 102, + 243, 106, 197, 239, 19, 12, 13, 197, 197, 147, 19, 45, 205, 221, 149, + 153, 175, 42, 252, 31, 1, 200, 0, + 86, 128, 62, 224, 16, 72, 12, 54, 133, 214, 227, 30, 201, 200, 86, 214, + 190, 222, 240, 88, 68, 58, 179, + 83, 53, 253, 215, 1, 158, 128, 104, 96, 46, 168, 28, 126, 137, 224, + 102, 200, 42, 214, 159, 30, 232, 8, + 78, 134, 180, 98, 247, 105, 134, 174, 226, 252, 73, 129, 246, 224, 70, + 200, 50, 214, 149, 158, 239, 40, 76, + 30, 181, 200, 119, 22, 166, 142, 250, 228, 67, 11, 113, 199, 100, 82, + 171, 125, 191, 97, 176, 40, 116, 30, + 167, 72, 122, 182, 163, 54, 249, 214, 194, 222, 209, 152, 92, 106, 185, + 239, 50, 204, 21, 149, 207, 47, 20, + 28, 15, 73, 196, 54, 211, 86, 221, 254, 217, 128, 90, 224, 59, 8, 19, + 70, 141, 242, 229, 133, 139, 35, + 39, 89, 218, 186, 219, 51, 27, 85, 203, 127, 23, 96, 14, 168, 4, 126, + 131, 96, 97, 232, 40, 78, 158, + 180, 104, 119, 110, 166, 172, 122, 253, 227, 1, 137, 192, 102, 208, 42, + 220, 31, 25, 200, 10, 214, 135, 30, + 226, 136, 73, 166, 182, 250, 246, 195, 6, 209, 194, 220, 81, 153, 252, + 106, 193, 239, 16, 76, 12, 53, 197, + 215, 19, 30, 141, 200, 101, 150, 171, 46, 255, 92, 64, 57, 240, 18, + 196, 13, 147, 69, 173, 243, 61, 133, + 209, 163, 28, 121, 201, 226, 214, 201, 158, 214, 232, 94, 206, 184, 84, + 114, 191, 101, 176, 43, 52, 31, 87, + 72, 62, 182, 144, 118, 236, 38, 205, 218, 213, 155, 31, 43, 72, 31, + 118, 136, 38, 230, 154, 202, 235, 23, + 15, 78, 132, 52, 99, 87, 105, 254, 174, 192, 124, 80, 33, 252, 24, 65, + 202, 176, 87, 52, 62, 151, 80, + 110, 188, 44, 113, 221, 228, 89, 139, 122, 231, 99, 10, 169, 199, 62, + 210, 144, 93, 172, 57, 189, 210, 241, + 157, 132, 105, 163, 110, 249, 236, 66, 205, 241, 149, 132, 111, 35, + 108, 25, 237, 202, 205, 151, 21, 174, 143, + 60, 100, 17, 235, 76, 79, 117, 244, 39, 7, 90, 130, 187, 33, 179, 88, + 117, 250, 167, 3, 58, 129, 211, + 32, 93, 216, 57, 154, 146, 235, 45, 143, 93, 164, 57, 187, 82, 243, + 125, 133, 225, 163, 8, 121, 198, 162, + 210, 249, 157, 130, 233, 161, 142, 248, 100, 66, 171, 113, 191, 100, + 112, 43, 100, 31, 107, 72, 47, 118, 156, + 38, 233, 218, 206, 219, 20, 91, 79, 123, 116, 35, 103, 89, 234, 186, + 207, 51, 20, 21, 207, 79, 20, 52, + 15, 87, 68, 62, 179, 80, 117, 252, 39, 1, 218, 128, 91, 32, 59, 88, 19, + 122, 141, 227, 37, 137, 219, + 38, 219, 90, 219, 123, 27, 99, 75, 105, 247, 110, 198, 172, 82, 253, + 253, 129, 129, 160, 96, 120, 40, 34, + 158, 153, 168, 106, 254, 175, 0, 124, 0, 33, 192, 24, 80, 10, 188, 7, + 49, 194, 148, 81, 175, 124, 124, + 33, 225, 216, 72, 90, 182, 187, 54, 243, 86, 197, 254, 211, 0, 93, 192, + 57, 144, 18, 236, 13, 141, 197, + 165, 147, 59, 45, 211, 93, 157, 249, 169, 130, 254, 225, 128, 72, 96, + 54, 168, 22, 254, 142, 192, 100, 80, + 43, 124, 31, 97, 200, 40, 86, 158, 190, 232, 112, 78, 164, 52, 123, 87, + 99, 126, 169, 224, 126, 200, 32, + 86, 152, 62, 234, 144, 79, 44, 52, 29, 215, 73, 158, 182, 232, 118, + 206, 166, 212, 122, 223, 99, 24, 41, + 202, 158, 215, 40, 94, 158, 184, 104, 114, 174, 165, 188, 123, 49, 227, + 84, 73, 255, 118, 192, 38, 208, 26, + 220, 11, 25, 199, 74, 210, 183, 29, 182, 137, 182, 230, 246, 202, 198, + 215, 18, 222, 141, 152, 101, 170, 171, + 63, 63, 80, 16, 60, 12, 17, 197, 204, 83, 21, 253, 207, 1, 148, 0, 111, + 64, 44, 48, 29, 212, 9, + 159, 70, 232, 50, 206, 149, 148, 111, 47, 108, 28, 45, 201, 221, 150, + 217, 174, 218, 252, 91, 1, 251, 64, + 67, 112, 49, 228, 20, 75, 79, 119, 116, 38, 167, 90, 250, 187, 3, 51, + 65, 213, 240, 95, 4, 56, 3, + 82, 129, 253, 160, 65, 184, 48, 114, 148, 37, 175, 91, 60, 59, 81, 211, + 124, 93, 225, 249, 136, 66, 230, + 177, 138, 244, 103, 7, 106, 130, 175, 33, 188, 24, 113, 202, 164, 87, + 59, 126, 147, 96, 109, 232, 45, 142, + 157, 164, 105, 187, 110, 243, 108, 69, 237, 243, 13, 133, 197, 163, 19, + 57, 205, 210, 213, 157, 159, 41, 168, + 30, 254, 136, 64, 102, 176, 42, 244, 31, 7, 72, 2, 182, 129, 182, 224, + 118, 200, 38, 214, 154, 222, 235, + 24, 79, 74, 180, 55, 55, 86, 150, 190, 238, 240, 76, 68, 53, 243, 87, + 5, 254, 131, 0, 97, 192, 40, + 80, 30, 188, 8, 113, 198, 164, 82, 251, 125, 131, 97, 161, 232, 120, + 78, 162, 180, 121, 183, 98, 246, 169, + 134, 254, 226, 192, 73, 144, 54, 236, 22, 205, 206, 213, 148, 95, 47, + 120, 28, 34, 137, 217, 166, 218, 250, + 219, 3, 27, 65, 203, 112, 87, 100, 62, 171, 80, 127, 124, 32, 33, 216, + 24, 90, 138, 187, 39, 51, 90, + 149, 251, 47, 3, 92, 1, 249, 192, 66, 208, 49, 156, 20, 105, 207, 110, + 212, 44, 95, 93, 248, 57, 130, + 146, 225, 173, 136, 125, 166, 161, 186, 248, 115, 2, 165, 193, 187, 16, + 115, 76, 37, 245, 219, 7, 27, 66, + 139, 113, 167, 100, 122, 171, 99, 63, 105, 208, 46, 220, 28, 89, 201, + 250, 214, 195, 30, 209, 200, 92, 86, + 185, 254, 242, 192, 69, 144, 51, 44, 21, 221, 207, 25, 148, 10, 239, + 71, 12, 50, 133, 213, 163, 31, 57, + 200, 18, 214, 141, 158, 229, 168, 75, 62, 183, 80, 118, 188, 38, 241, + 218, 196, 91, 19, 123, 77, 227, 117, + 137, 231, 38, 202, 154, 215, 43, 30, 159, 72, 104, 54, 174, 150, 252, + 110, 193, 236, 80, 77, 252, 53, 129, + 215, 32, 94, 152, 56, 106, 146, 175, 45, 188, 29, 177, 201, 180, 86, + 247, 126, 198, 160, 82, 248, 61, 130, + 145, 161, 172, 120, 125, 226, 161, 137, 184, 102, 242, 170, 197, 191, + 19, 48, 13, 212, 5, 159, 67, 40, 49, + 222, 148, 88, 111, 122, 172, 35, 61, 217, 209, 154, 220, 107, 25, 239, + 74, 204, 55, 21, 214, 143, 30, 228, + 8, 75, 70, 183, 114, 246, 165, 134, 251, 34, 195, 89, 145, 250, 236, + 67, 13, 241, 197, 132, 83, 35, 125, + 217, 225, 154, 200, 107, 22, 175, 78, 252, 52, 65, 215, 112, 94, 164, + 56, 123, 82, 163, 125, 185, 225, 178, + 200, 117, 150, 167, 46, 250, 156, 67, 41, 241, 222, 196, 88, 83, 122, + 189, 227, 49, 137, 212, 102, 223, 106, + 216, 47, 26, 156, 11, 41, 199, 94, 210, 184, 93, 178, 185, 181, 178, + 247, 53, 134, 151, 34, 238, 153, 140, + 106, 229, 239, 11, 12, 7, 69, 194, 179, 17, 181, 204, 119, 21, 230, + 143, 10, 228, 7, 11, 66, 135, 113, + 162, 164, 121, 187, 98, 243, 105, 133, 238, 227, 12, 73, 197, 246, 211, + 6, 221, 194, 217, 145, 154, 236, 107, + 13, 239, 69, 140, 51, 37, 213, 219, 31, 27, 72, 11, 118, 135, 102, 226, + 170, 201, 191, 22, 240, 14, 196, + 4, 83, 67, 125, 241, 225, 132, 72, 99, 118, 169, 230, 254, 202, 192, + 87, 16, 62, 140, 16, 101, 204, 43, + 21, 223, 79, 24, 52, 10, 151, 71, 46, 178, 156, 117, 169, 231, 62, 202, + 144, 87, 44, 62, 157, 208, 105, + 156, 46, 233, 220, 78, 217, 244, 90, 199, 123, 18, 163, 77, 185, 245, + 178, 199, 53, 146, 151, 45, 174, 157, + 188, 105, 177, 238, 244, 76, 71, 117, 242, 167, 5, 186, 131, 51, 33, + 213, 216, 95, 26, 184, 11, 50, 135, + 85, 162, 191, 57, 176, 18, 244, 13, 135, 69, 162, 179, 57, 181, 210, + 247, 29, 134, 137, 162, 230, 249, 138, + 194, 231, 17, 138, 140, 103, 37, 234, 155, 15, 43, 68, 31, 115, 72, 37, + 246, 155, 6, 235, 66, 207, 113, + 148, 36, 111, 91, 108, 59, 109, 211, 109, 157, 237, 169, 141, 190, 229, + 176, 75, 52, 55, 87, 86, 190, 190, + 240, 112, 68, 36, 51, 91, 85, 251, 127, 3, 96, 1, 232, 0, 78, 128, 52, + 96, 23, 104, 14, 174, 132, + 124, 99, 97, 233, 232, 78, 206, 180, 84, 119, 127, 102, 160, 42, 248, + 31, 2, 136, 1, 166, 128, 122, 224, + 35, 8, 25, 198, 138, 210, 231, 29, 138, 137, 167, 38, 250, 154, 195, + 43, 17, 223, 76, 88, 53, 250, 151, + 3, 46, 129, 220, 96, 89, 232, 58, 206, 147, 20, 109, 207, 109, 148, 45, + 175, 93, 188, 57, 177, 210, 244, + 93, 135, 121, 162, 162, 249, 185, 130, 242, 225, 133, 136, 99, 38, 169, + 218, 254, 219, 0, 91, 64, 59, 112, + 19, 100, 13, 235, 69, 143, 115, 36, 37, 219, 91, 27, 123, 75, 99, 119, + 105, 230, 174, 202, 252, 87, 1, + 254, 128, 64, 96, 48, 40, 20, 30, 143, 72, 100, 54, 171, 86, 255, 126, + 192, 32, 80, 24, 60, 10, 145, + 199, 44, 82, 157, 253, 169, 129, 190, 224, 112, 72, 36, 54, 155, 86, + 235, 126, 207, 96, 84, 40, 63, 94, + 144, 56, 108, 18, 173, 205, 189, 149, 177, 175, 52, 124, 23, 97, 206, + 168, 84, 126, 191, 96, 112, 40, 36, + 30, 155, 72, 107, 118, 175, 102, 252, 42, 193, 223, 16, 88, 12, 58, + 133, 211, 35, 29, 217, 201, 154, 214, + 235, 30, 207, 72, 84, 54, 191, 86, 240, 62, 196, 16, 83, 76, 61, 245, + 209, 135, 28, 98, 137, 233, 166, + 206, 250, 212, 67, 31, 113, 200, 36, 86, 155, 126, 235, 96, 79, 104, + 52, 46, 151, 92, 110, 185, 236, 114, + 205, 229, 149, 139, 47, 39, 92, 26, 185, 203, 50, 215, 85, 158, 191, + 40, 112, 30, 164, 8, 123, 70, 163, + 114, 249, 229, 130, 203, 33, 151, 88, 110, 186, 172, 115, 61, 229, 209, + 139, 28, 103, 73, 234, 182, 207, 54, + 212, 22, 223, 78, 216, 52, 90, 151, 123, 46, 163, 92, 121, 249, 226, + 194, 201, 145, 150, 236, 110, 205, 236, + 85, 141, 255, 37, 128, 27, 32, 11, 88, 7, 122, 130, 163, 33, 185, 216, + 114, 218, 165, 155, 59, 43, 83, + 95, 125, 248, 33, 130, 152, 97, 170, 168, 127, 62, 160, 16, 120, 12, + 34, 133, 217, 163, 26, 249, 203, 2, + 215, 65, 158, 176, 104, 116, 46, 167, 92, 122, 185, 227, 50, 201, 213, + 150, 223, 46, 216, 28, 90, 137, 251, + 38, 195, 90, 209, 251, 28, 67, 73, 241, 246, 196, 70, 211, 114, 221, + 229, 153, +}; + +void scramble(unsigned char *inout) +{ + unsigned char *r = inout + 12; + unsigned char *s = yellowbook_scrambler; + unsigned int i; + + for (i = 2340; i; i--) { + *r++ ^= *s++; + } +} + +/* Calculate the P parities for the sector. + * The 43 P vectors of length 24 are combined with the GF8_P_COEFFS. + */ +void parity_p(unsigned char *sector) +{ + int i, j; + unsigned char p0_msb, p1_msb; + unsigned char p0_lsb, p1_lsb; + unsigned char *p_msb_start, *p_lsb_start; + unsigned char *p_msb, *p_lsb; + unsigned char *coeffs0, *coeffs1; + unsigned char *p0, *p1; + unsigned char d; + unsigned short c; + + p_lsb_start = sector + LEC_HEADER_OFFSET; + p_msb_start = sector + LEC_HEADER_OFFSET + 1; + + p1 = sector + LEC_MODE1_P_PARITY_OFFSET; + p0 = sector + LEC_MODE1_P_PARITY_OFFSET + 2 * 43; + + for (i = 0; i <= 42; i++) { + p_lsb = p_lsb_start; + p_msb = p_msb_start; + + coeffs0 = gf8_p_coeffs[0]; + coeffs1 = gf8_p_coeffs[1]; + + p0_lsb = p1_lsb = p0_msb = p1_msb = 0; + + for (j = 0; j <= 23; j++) { + d = *p_lsb; + + if (d != 0) { + c = gf8_log[d] + *coeffs0; + if (c >= 255) + c -= 255; + p0_lsb ^= gf8_ilog[c]; + + c = gf8_log[d] + *coeffs1; + if (c >= 255) + c -= 255; + p1_lsb ^= gf8_ilog[c]; + } + + d = *p_msb; + + if (d != 0) { + c = gf8_log[d] + *coeffs0; + if (c >= 255) + c -= 255; + p0_msb ^= gf8_ilog[c]; + + c = gf8_log[d] + *coeffs1; + if (c >= 255) + c -= 255; + p1_msb ^= gf8_ilog[c]; + } + + coeffs0++; + coeffs1++; + + p_lsb += 2 * 43; + p_msb += 2 * 43; + } + + *p0 = p0_lsb; + *(p0 + 1) = p0_msb; + + *p1 = p1_lsb; + *(p1 + 1) = p1_msb; + + p0 += 2; + p1 += 2; + + p_lsb_start += 2; + p_msb_start += 2; + } +} + +/* Calculate the Q parities for the sector. + * The 26 Q vectors of length 43 are combined with the GF8_Q_COEFFS. + */ +void parity_q(unsigned char *sector) +{ + int i, j; + unsigned char q0_msb, q1_msb; + unsigned char q0_lsb, q1_lsb; + unsigned char *q_msb_start, *q_lsb_start; + unsigned char *q_msb, *q_lsb; + unsigned char *coeffs0, *coeffs1; + unsigned char *q0, *q1, *q_start; + unsigned char d; + unsigned short c; + + q_lsb_start = sector + LEC_HEADER_OFFSET; + q_msb_start = sector + LEC_HEADER_OFFSET + 1; + + q_start = sector + LEC_MODE1_Q_PARITY_OFFSET; + q1 = sector + LEC_MODE1_Q_PARITY_OFFSET; + q0 = sector + LEC_MODE1_Q_PARITY_OFFSET + 2 * 26; + + for (i = 0; i <= 25; i++) { + q_lsb = q_lsb_start; + q_msb = q_msb_start; + + coeffs0 = gf8_q_coeffs[0]; + coeffs1 = gf8_q_coeffs[1]; + + q0_lsb = q1_lsb = q0_msb = q1_msb = 0; + + for (j = 0; j <= 42; j++) { + d = *q_lsb; + + if (d != 0) { + c = gf8_log[d] + *coeffs0; + if (c >= 255) + c -= 255; + q0_lsb ^= gf8_ilog[c]; + + c = gf8_log[d] + *coeffs1; + if (c >= 255) + c -= 255; + q1_lsb ^= gf8_ilog[c]; + } + + d = *q_msb; + + if (d != 0) { + c = gf8_log[d] + *coeffs0; + if (c >= 255) + c -= 255; + q0_msb ^= gf8_ilog[c]; + + c = gf8_log[d] + *coeffs1; + if (c >= 255) + c -= 255; + q1_msb ^= gf8_ilog[c]; + } + + coeffs0++; + coeffs1++; + + q_lsb += 2 * 44; + q_msb += 2 * 44; + + if (q_lsb >= q_start) { + q_msb -= 2 * 1118; + q_lsb -= 2 * 1118; + } + } + + *q0 = q0_lsb; + *(q0 + 1) = q0_msb; + + *q1 = q1_lsb; + *(q1 + 1) = q1_msb; + + q0 += 2; + q1 += 2; + + q_lsb_start += 2 * 43; + q_msb_start += 2 * 43; + } +} diff --git a/libburn/tags/ZeroThreeZero/libburn/lec.h b/libburn/tags/ZeroThreeZero/libburn/lec.h new file mode 100644 index 00000000..f6980301 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/lec.h @@ -0,0 +1,12 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __LEC +#define __LEC + +#define RS_L12_BITS 8 + +void scramble(unsigned char *); +void parity_p(unsigned char *in); +void parity_q(unsigned char *in); + +#endif /* __LEC */ diff --git a/libburn/tags/ZeroThreeZero/libburn/libburn.h b/libburn/tags/ZeroThreeZero/libburn/libburn.h new file mode 100644 index 00000000..5d1b4765 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/libburn.h @@ -0,0 +1,1450 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef LIBBURN_H +#define LIBBURN_H + +/* Needed for off_t which is the (POSIX-ly) appropriate type for + expressing a file or stream size. + + XXX we should enforce 64-bitness for off_t + ts A61101 : this is usually done by the build system (if it is not broken) +*/ +#include <sys/types.h> + +#ifndef DOXYGEN + +#if defined(__cplusplus) +#define BURN_BEGIN_DECLS \ + namespace burn { \ + extern "C" { +#define BURN_END_DECLS \ + } \ + } +#else +#define BURN_BEGIN_DECLS +#define BURN_END_DECLS +#endif + +BURN_BEGIN_DECLS + +#endif + +/** References a physical drive in the system */ +struct burn_drive; + +/** References a whole disc */ +struct burn_disc; + +/** References a single session on a disc */ +struct burn_session; + +/** References a single track on a disc */ +struct burn_track; + +/* ts A61111 */ +/** References a set of write parameters */ +struct burn_write_opts; + +/** Session format for normal audio or data discs */ +#define BURN_CDROM 0 +/** Session format for obsolete CD-I discs */ +#define BURN_CDI 0x10 +/** Session format for CDROM-XA discs */ +#define BURN_CDXA 0x20 + +#define BURN_POS_END 100 + +/** Mask for mode bits */ +#define BURN_MODE_BITS 127 + +/** Track mode - mode 0 data + 0 bytes of user data. it's all 0s. mode 0. get it? HAH +*/ +#define BURN_MODE0 (1 << 0) +/** Track mode - mode "raw" - all 2352 bytes supplied by app + FOR DATA TRACKS ONLY! +*/ +#define BURN_MODE_RAW (1 << 1) +/** Track mode - mode 1 data + 2048 bytes user data, and all the LEC money can buy +*/ +#define BURN_MODE1 (1 << 2) +/** Track mode - mode 2 data + defaults to formless, 2336 bytes of user data, unprotected + | with a data form if required. +*/ +#define BURN_MODE2 (1 << 3) +/** Track mode modifier - Form 1, | with MODE2 for reasonable results + 2048 bytes of user data, 4 bytes of subheader +*/ +#define BURN_FORM1 (1 << 4) +/** Track mode modifier - Form 2, | with MODE2 for reasonable results + lots of user data. not much LEC. +*/ +#define BURN_FORM2 (1 << 5) +/** Track mode - audio + 2352 bytes per sector. may be | with 4ch or preemphasis. + NOT TO BE CONFUSED WITH BURN_MODE_RAW + Audio data must be 44100Hz 16bit stereo with no riff or other header at + beginning. Extra header data will cause pops or clicks. Audio data should + also be in little-endian byte order. Big-endian audio data causes static. +*/ +#define BURN_AUDIO (1 << 6) +/** Track mode modifier - 4 channel audio. */ +#define BURN_4CH (1 << 7) +/** Track mode modifier - Digital copy permitted, can be set on any track.*/ +#define BURN_COPY (1 << 8) +/** Track mode modifier - 50/15uS pre-emphasis */ +#define BURN_PREEMPHASIS (1 << 9) +/** Input mode modifier - subcodes present packed 16 */ +#define BURN_SUBCODE_P16 (1 << 10) +/** Input mode modifier - subcodes present packed 96 */ +#define BURN_SUBCODE_P96 (1 << 11) +/** Input mode modifier - subcodes present raw 96 */ +#define BURN_SUBCODE_R96 (1 << 12) + +/** Possible disc writing style/modes */ +enum burn_write_types +{ + /** Packet writing. + currently unsupported + */ + BURN_WRITE_PACKET, + /** Track At Once recording. + 2s gaps between tracks, no fonky lead-ins + */ + BURN_WRITE_TAO, + /** Session At Once. + block type MUST be BURN_BLOCK_SAO + */ + BURN_WRITE_SAO, + /** Raw disc at once recording. + all subcodes must be provided by lib or user + only raw block types are supported + */ + BURN_WRITE_RAW +}; + +/** Data format to send to the drive */ +enum burn_block_types +{ + /** sync, headers, edc/ecc provided by lib/user */ + BURN_BLOCK_RAW0 = 1, + /** sync, headers, edc/ecc and p/q subs provided by lib/user */ + BURN_BLOCK_RAW16 = 2, + /** sync, headers, edc/ecc and packed p-w subs provided by lib/user */ + BURN_BLOCK_RAW96P = 4, + /** sync, headers, edc/ecc and raw p-w subs provided by lib/user */ + BURN_BLOCK_RAW96R = 8, + /** only 2048 bytes of user data provided by lib/user */ + BURN_BLOCK_MODE1 = 256, + /** 2336 bytes of user data provided by lib/user */ + BURN_BLOCK_MODE2R = 512, + /** 2048 bytes of user data provided by lib/user + subheader provided in write parameters + are we ever going to support this shit? I vote no. + (supposed to be supported on all drives...) + */ + BURN_BLOCK_MODE2_PATHETIC = 1024, + /** 2048 bytes of data + 8 byte subheader provided by lib/user + hey, this is also dumb + */ + BURN_BLOCK_MODE2_LAME = 2048, + /** 2324 bytes of data provided by lib/user + subheader provided in write parameters + no sir, I don't like it. + */ + BURN_BLOCK_MODE2_OBSCURE = 4096, + /** 2332 bytes of data supplied by lib/user + 8 bytes sub header provided in write parameters + this is the second least suck mode2, and is mandatory for + all drives to support. + */ + BURN_BLOCK_MODE2_OK = 8192, + /** SAO block sizes are based on cue sheet, so use this. */ + BURN_BLOCK_SAO = 16384 +}; + +/** Possible status' of the drive in regard to the disc in it. */ +enum burn_disc_status +{ + /** The current status is not yet known */ + BURN_DISC_UNREADY, + /** The drive holds a blank disc */ + BURN_DISC_BLANK, + /** There is no disc at all in the drive */ + BURN_DISC_EMPTY, + /** There is an incomplete disc in the drive */ + BURN_DISC_APPENDABLE, + /** There is a disc with data on it in the drive */ + BURN_DISC_FULL, + + /* ts A61007 */ + /** The drive was not grabbed when the status was inquired */ + BURN_DISC_UNGRABBED, + + /* ts A61020 */ + /** The media seems not to be suitable for burning */ + BURN_DISC_UNSUITABLE +}; + + +/** Possible data source return values */ +enum burn_source_status +{ + /** The source is ok */ + BURN_SOURCE_OK, + /** The source is at end of file */ + BURN_SOURCE_EOF, + /** The source is unusable */ + BURN_SOURCE_FAILED +}; + + +/** Possible busy states for a drive */ +enum burn_drive_status +{ + /** The drive is not in an operation */ + BURN_DRIVE_IDLE, + /** The library is spawning the processes to handle a pending + operation (A read/write/etc is about to start but hasn't quite + yet) */ + BURN_DRIVE_SPAWNING, + /** The drive is reading data from a disc */ + BURN_DRIVE_READING, + /** The drive is writing data to a disc */ + BURN_DRIVE_WRITING, + /** The drive is writing Lead-In */ + BURN_DRIVE_WRITING_LEADIN, + /** The drive is writing Lead-Out */ + BURN_DRIVE_WRITING_LEADOUT, + /** The drive is erasing a disc */ + BURN_DRIVE_ERASING, + /** The drive is being grabbed */ + BURN_DRIVE_GRABBING, + + /* ts A61102 */ + /** The drive gets written zeroes before the track payload data */ + BURN_DRIVE_WRITING_PREGAP, + /** The drive is told to close a track (TAO only) */ + BURN_DRIVE_CLOSING_TRACK, + /** The drive is told to close a session (TAO only) */ + BURN_DRIVE_CLOSING_SESSION, + + /* ts A61223 */ + /** The drive is formatting media */ + BURN_DRIVE_FORMATTING + +}; + +/** Information about a track on a disc - this is from the q sub channel of the + lead-in area of a disc. The documentation here is very terse. + See a document such as mmc3 for proper information. +*/ +struct burn_toc_entry +{ + /** Session the track is in */ + unsigned char session; + /** Type of data. for this struct to be valid, it must be 1 */ + unsigned char adr; + /** Type of data in the track */ + unsigned char control; + /** Zero. Always. Really. */ + unsigned char tno; + /** Track number or special information */ + unsigned char point; + unsigned char min; + unsigned char sec; + unsigned char frame; + unsigned char zero; + /** Track start time minutes for normal tracks */ + unsigned char pmin; + /** Track start time seconds for normal tracks */ + unsigned char psec; + /** Track start time frames for normal tracks */ + unsigned char pframe; +}; + + +/** Data source for tracks */ +struct burn_source { + /** Reference count for the data source. Should be 1 when a new source + is created. Increment it to take a reference for yourself. Use + burn_source_free to destroy your reference to it. */ + int refcount; + + /** Read data from the source */ + int (*read)(struct burn_source *, + unsigned char *buffer, + int size); + + /** Read subchannel data from the source (NULL if lib generated) */ + int (*read_sub)(struct burn_source *, + unsigned char *buffer, + int size); + + /** Get the size of the source's data */ + off_t (*get_size)(struct burn_source *); + + /** Clean up the source specific data */ + void (*free_data)(struct burn_source *); + + /** Next source, for when a source runs dry and padding is disabled + THIS IS AUTOMATICALLY HANDLED, DO NOT TOUCH + */ + struct burn_source *next; + + /** Source specific data */ + void *data; +}; + + +/** Information on a drive in the system */ +struct burn_drive_info +{ + /** Name of the vendor of the drive */ + char vendor[9]; + /** Name of the drive */ + char product[17]; + /** Revision of the drive */ + char revision[5]; + /** Location of the drive in the filesystem. */ + char location[17]; + /** This is currently the string which is used as persistent + drive address. But be warned: there is NO GUARANTEE that this + will stay so. Always use function burn_drive_get_adr() to + inquire a persistent address. ^^^^^^ ALWAYS ^^^^^^ */ + + /** Can the drive read DVD-RAM discs */ + unsigned int read_dvdram:1; + /** Can the drive read DVD-R discs */ + unsigned int read_dvdr:1; + /** Can the drive read DVD-ROM discs */ + unsigned int read_dvdrom:1; + /** Can the drive read CD-R discs */ + unsigned int read_cdr:1; + /** Can the drive read CD-RW discs */ + unsigned int read_cdrw:1; + + /** Can the drive write DVD-RAM discs */ + unsigned int write_dvdram:1; + /** Can the drive write DVD-R discs */ + unsigned int write_dvdr:1; + /** Can the drive write CD-R discs */ + unsigned int write_cdr:1; + /** Can the drive write CD-RW discs */ + unsigned int write_cdrw:1; + + /** Can the drive simulate a write */ + unsigned int write_simulate:1; + + /** Can the drive report C2 errors */ + unsigned int c2_errors:1; + + /** The size of the drive's buffer (in kilobytes) */ + int buffer_size; + /** + * The supported block types in tao mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int tao_block_types; + /** + * The supported block types in sao mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int sao_block_types; + /** + * The supported block types in raw mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int raw_block_types; + /** + * The supported block types in packet mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int packet_block_types; + + /** The value by which this drive can be indexed when using functions + in the library. This is the value to pass to all libbburn functions + that operate on a drive. */ + struct burn_drive *drive; +}; + + +/** Operation progress report. All values are 0 based indices. + * */ +struct burn_progress { + /** The total number of sessions */ + int sessions; + /** Current session.*/ + int session; + /** The total number of tracks */ + int tracks; + /** Current track. */ + int track; + /** The total number of indices */ + int indices; + /** Curent index. */ + int index; + /** The starting logical block address */ + int start_sector; + /** On write: The number of sectors. + On blank: 0x10000 as upper limit for relative progress steps */ + int sectors; + /** On write: The current sector being processed. + On blank: Relative progress steps 0 to 0x10000 */ + int sector; + + /* ts A61023 */ + /** The capacity of the drive buffer */ + unsigned buffer_capacity; + /** The free space in the drive buffer (might be slightly outdated) */ + unsigned buffer_available; + + /* ts A61119 */ + /** The number of bytes sent to the drive buffer */ + off_t buffered_bytes; + /** The minimum number of buffered bytes. (Caution: Before surely + one buffer size of bytes was processed, this value is 0xffffffff.) + */ + unsigned buffer_min_fill; +}; + + +/* ts A61226 */ +/** Description of a speed capability as reported by the drive in conjunction + with eventually loaded media. There can be more than one such object per + drive. So they are chained via .next and .prev , where NULL marks the end + of the chain. This list is set up by burn_drive_scan() and gets updated + by burn_drive_grab(). + A copy may be obtained by burn_drive_get_speedlist() and disposed by + burn_drive_free_speedlist(). + For technical background info see SCSI specs MMC and SPC: + mode page 2Ah (from SPC 5Ah MODE SENSE) , mmc3r10g.pdf , 6.3.11 Table 364 + ACh GET PERFORMANCE, Type 03h , mmc5r03c.pdf , 6.8.5.3 Table 312 +*/ +struct burn_speed_descriptor { + + /** Where this info comes from : + 0 = misc , 1 = mode page 2Ah , 2 = ACh GET PERFORMANCE */ + int source; + + /** The media type that was current at the time of report + -2 = state unknown, -1 = no media was loaded , else see + burn_disc_get_profile() */ + int profile_loaded; + char profile_name[80]; + + /** The attributed capacity of appropriate media in logical block units + i.e. 2352 raw bytes or 2048 data bytes. -1 = capacity unknown. */ + int end_lba; + + /** Speed is given in 1000 bytes/s , 0 = invalid. The numbers + are supposed to be usable with burn_drive_set_speed() */ + int write_speed; + int read_speed; + + /** Expert info from ACh GET PERFORMANCE and/or mode page 2Ah. + Expect values other than 0 or 1 to get a meaning in future.*/ + /* Rotational control: 0 = CLV/default , 1 = CAV */ + int wrc; + /* 1 = drive promises reported performance over full media */ + int exact; + /* 1 = suitable for mixture of read and write */ + int mrw; + + /** List chaining. Use .next until NULL to iterate over the list */ + struct burn_speed_descriptor *prev; + struct burn_speed_descriptor *next; +}; + + +/** Initialize the library. + This must be called before using any other functions in the library. It + may be called more than once with no effect. + It is possible to 'restart' the library by shutting it down and + re-initializing it. This is necessary if you follow the older and + more general way of accessing a drive via burn_drive_scan() and + burn_drive_grab(). See burn_drive_scan_and_grab() with its strong + urges and its explanations. + @return Nonzero if the library was able to initialize; zero if + initialization failed. +*/ +int burn_initialize(void); + +/** Shutdown the library. + This should be called before exiting your application. Make sure that all + drives you have grabbed are released <i>before</i> calling this. +*/ +void burn_finish(void); + + +/* ts A61002 */ +/** Abort any running drive operation and finally call burn_finish(). + You MUST calm down the busy drive if an aborting event occurs during a + burn run. For that you may call this function either from your own signal + handling code or indirectly by activating the builtin signal handling: + burn_set_signal_handling("my_app_name : ", NULL, 0); + Else you may eventually call burn_drive_cancel() on the active drive and + wait for it to assume state BURN_DRIVE_IDLE. + @param patience Maximum number of seconds to wait for drives to finish + @param pacifier_func If not NULL: a function to produce appeasing messages. + See burn_abort_pacifier() for an example. + @param handle Opaque handle to be used with pacifier_func + @return 1 ok, all went well + 0 had to leave a drive in unclean state + <0 severe error, do no use libburn again +*/ +int burn_abort(int patience, + int (*pacifier_func)(void *handle, int patience, int elapsed), + void *handle); + +/** A pacifier function suitable for burn_abort. + @param handle If not NULL, a pointer to a text suitable for printf("%s") + @param patience Maximum number of seconds to wait + @param elapsed Elapsed number of seconds +*/ +int burn_abort_pacifier(void *handle, int patience, int elapsed); + + +/** ts A61006 : This is for development only. Not suitable for applications. + Set the verbosity level of the library. The default value is 0, which means + that nothing is output on stderr. The more you increase this, the more + debug output should be displayed on stderr for you. + @param level The verbosity level desired. 0 for nothing, higher positive + values for more information output. +*/ +void burn_set_verbosity(int level); + +/* ts A60813 */ +/** Set parameters for behavior on opening device files. To be called early + after burn_initialize() and before any bus scan. But not mandatory at all. + Parameter value 1 enables a feature, 0 disables. + Default is (1,0,0). Have a good reason before you change it. + @param exclusive 1 = Try to open only devices which are not marked as busy + and try to mark them busy if opened sucessfully. (O_EXCL) + There are kernels which simply don't care about O_EXCL. + Some have it off, some have it on, some are switchable. + 2 = in case of a SCSI device, also try to open exclusively + the matching /dev/sr, /dev/scd and /dev/st . + 0 = no attempt to make drive access exclusive. + @param blocking Try to wait for drives which do not open immediately but + also do not return an error as well. (O_NONBLOCK) + This might stall indefinitely with /dev/hdX hard disks. + @param abort_on_busy Unconditionally abort process when a non blocking + exclusive opening attempt indicates a busy drive. + Use this only after thorough tests with your app. +*/ +void burn_preset_device_open(int exclusive, int blocking, int abort_on_busy); + + +/* ts A60823 */ +/** Aquire a drive with known persistent address.This is the sysadmin friendly + way to open one drive and to leave all others untouched. It bundles + the following API calls to form a non-obtrusive way to use libburn: + burn_drive_add_whitelist() , burn_drive_scan() , burn_drive_grab() + You are *strongly urged* to use this call whenever you know the drive + address in advance. + If not, then you have to use directly above calls. In that case, you are + *strongly urged* to drop any unintended drive which will be exclusively + occupied and not closed by burn_drive_scan(). + This can be done by shutting down the library including a call to + burn_finish(). You may later start a new libburn session and should then + use the function described here with an address obtained after + burn_drive_scan() via burn_drive_get_adr(&(drive_infos[driveno]), adr) . + Another way is to drop the unwanted drives by burn_drive_info_forget(). + @param drive_infos On success returns a one element array with the drive + (cdrom/burner). Thus use with driveno 0 only. On failure + the array has no valid elements at all. + The returned array should be freed via burn_drive_info_free() + when it is no longer needed, and before calling a scan + function again. + This is a result from call burn_drive_scan(). See there. + Use with driveno 0 only. + @param adr The persistent address of the desired drive. Either obtained + by burn_drive_get_adr() or guessed skillfully by application + resp. its user. + @param load Nonzero to make the drive attempt to load a disc (close its + tray door, etc). + @return 1 = success , 0 = drive not found , -1 = other error +*/ +int burn_drive_scan_and_grab(struct burn_drive_info *drive_infos[], + char* adr, int load); + + +/* ts A51221 */ +/** Maximum number of particularly permissible drive addresses */ +#define BURN_DRIVE_WHITELIST_LEN 255 +/** Add a device to the list of permissible drives. As soon as some entry is in + the whitelist all non-listed drives are banned from scanning. + @return 1 success, <=0 failure +*/ +int burn_drive_add_whitelist(char *device_address); + +/** Remove all drives from whitelist. This enables all possible drives. */ +void burn_drive_clear_whitelist(void); + + +/** Scan for drives. This function MUST be called until it returns nonzero. + No drives may be in use when this is called. + All drive pointers are invalidated by using this function. Do NOT store + drive pointers across calls to this function or death AND pain will ensue. + After this call all drives depicted by the returned array are subject + to eventual (O_EXCL) locking. See burn_preset_device_open(). This state + ends either with burn_drive_info_forget() or with burn_drive_release(). + It is unfriendly to other processes on the system to hold drives locked + which one does not definitely plan to use soon. + @param drive_infos Returns an array of drive info items (cdroms/burners). + The returned array must be freed by burn_drive_info_free() + before burn_finish(), and also before calling this function + burn_drive_scan() again. + @param n_drives Returns the number of drive items in drive_infos. + @return 0 while scanning is not complete + >0 when it is finished sucessfully, + <0 when finished but failed. +*/ +int burn_drive_scan(struct burn_drive_info *drive_infos[], + unsigned int *n_drives); + +/* ts A60904 : ticket 62, contribution by elmom */ +/** Release memory about a single drive and any exclusive lock on it. + Become unable to inquire or grab it. Expect FATAL consequences if you try. + @param drive_info pointer to a single element out of the array + obtained from burn_drive_scan() : &(drive_infos[driveno]) + @param force controls degree of permissible drive usage at the moment this + function is called, and the amount of automatically provided + drive shutdown : + 0= drive must be ungrabbed and BURN_DRIVE_IDLE + 1= try to release drive resp. accept BURN_DRIVE_GRABBING + Use these two only. Further values are to be defined. + @return 1 on success, 2 if drive was already forgotten, + 0 if not permissible, <0 on other failures, +*/ +int burn_drive_info_forget(struct burn_drive_info *drive_info, int force); + + +/** Free a burn_drive_info array returned by burn_drive_scan +*/ +void burn_drive_info_free(struct burn_drive_info drive_infos[]); + + +/* ts A60823 */ +/** Maximum length+1 to expect with a persistent drive address string */ +#define BURN_DRIVE_ADR_LEN 1024 + +/** Inquire the persistent address of the given drive. + @param drive_info The drive to inquire. Usually some &(drive_infos[driveno]) + @param adr An application provided array of at least BURN_DRIVE_ADR_LEN + characters size. The persistent address gets copied to it. + @return >0 success , <=0 error (due to libburn internal problem) +*/ +int burn_drive_get_adr(struct burn_drive_info *drive_info, char adr[]); + +/* ts A60922 ticket 33 */ +/** Evaluate wether the given address would be a possible persistent drive + address of libburn. + @return 1 means yes, 0 means no +*/ +int burn_drive_is_enumerable_adr(char *adr); + +/* ts A60922 ticket 33 */ +/** Try to convert a given existing filesystem address into a persistent drive + address. This succeeds with symbolic links or if a hint about the drive's + system address can be read from the filesystem object and a matching drive + is found. + @param path The address of an existing file system object + @param adr An application provided array of at least BURN_DRIVE_ADR_LEN + characters size. The persistent address gets copied to it. + @return 1 = success , 0 = failure , -1 = severe error +*/ +int burn_drive_convert_fs_adr(char *path, char adr[]); + +/* ts A60923 */ +/** Try to convert a given SCSI address of bus,host,channel,target,lun into + a persistent drive address. If a SCSI address component parameter is < 0 + then it is not decisive and the first enumerated address which matches + the >= 0 parameters is taken as result. + Note: bus and (host,channel) are supposed to be redundant. + @param bus_no "Bus Number" (something like a virtual controller) + @param host_no "Host Number" (something like half a virtual controller) + @param channel_no "Channel Number" (other half of "Host Number") + @param target_no "Target Number" or "SCSI Id" (a device) + @param lun_no "Logical Unit Number" (a sub device) + @param adr An application provided array of at least BURN_DRIVE_ADR_LEN + characters size. The persistent address gets copied to it. + @return 1 = success , 0 = failure , -1 = severe error +*/ +int burn_drive_convert_scsi_adr(int bus_no, int host_no, int channel_no, + int target_no, int lun_no, char adr[]); + +/* ts A60923 - A61005 */ +/** Try to obtain bus,host,channel,target,lun from path. If there is an SCSI + address at all, then this call should succeed with a persistent + drive address obtained via burn_drive_get_adr(). It is also supposed to + succeed with any device file of a (possibly emulated) SCSI device. + @return 1 = success , 0 = failure , -1 = severe error +*/ +int burn_drive_obtain_scsi_adr(char *path, int *bus_no, int *host_no, + int *channel_no, int *target_no, int *lun_no); + +/** Grab a drive. This must be done before the drive can be used (for reading, + writing, etc). + @param drive The drive to grab. This is found in a returned + burn_drive_info struct. + @param load Nonzero to make the drive attempt to load a disc (close its + tray door, etc). + @return 1 if it was possible to grab the drive, else 0 +*/ +int burn_drive_grab(struct burn_drive *drive, int load); + + +/** Release a drive. This should not be done until the drive is no longer + busy (see burn_drive_get_status). The drive is (O_EXCL) unlocked + afterwards. + @param drive The drive to release. + @param eject Nonzero to make the drive eject the disc in it. +*/ +void burn_drive_release(struct burn_drive *drive, int eject); + + +/** Returns what kind of disc a drive is holding. This function may need to be + called more than once to get a proper status from it. See burn_disc_status + for details. + @param drive The drive to query for a disc. + @return The status of the drive, or what kind of disc is in it. + Note: BURN_DISC_UNGRABBED indicates wrong API usage +*/ +enum burn_disc_status burn_disc_get_status(struct burn_drive *drive); + + +/* ts A61020 */ +/** WARNING: This revives an old bug-like behavior that might be dangerous. + Sets the drive status to BURN_DISC_BLANK if it is BURN_DISC_UNREADY + or BURN_DISC_UNSUITABLE. Thus marking media as writable which actually + failed to declare themselves either blank or (partially) filled. + @return 1 drive status has been set , 0 = unsuitable drive status +*/ +int burn_disc_pretend_blank(struct burn_drive *drive); + + +/* ts A61106 */ +/** WARNING: This overrides the safety measures against unsuitable media. + Sets the drive status to BURN_DISC_FULL if it is BURN_DISC_UNREADY + or BURN_DISC_UNSUITABLE. Thus marking media as blankable which actually + failed to declare themselves either blank or (partially) filled. +*/ +int burn_disc_pretend_full(struct burn_drive *drive); + + +/* ts A61021 */ +/** Reads ATIP information from inserted media. To be obtained via + burn_drive_get_write_speed(), burn_drive_get_min_write_speed(), + burn_drive_get_start_end_lba(). The drive must be grabbed for this call. + @param drive The drive to query. + @return 1=sucess, 0=no valid ATIP info read, -1 severe error +*/ +int burn_disc_read_atip(struct burn_drive *drive); + + +/* ts A61020 */ +/** Returns start and end lba of the media which is currently inserted + in the given drive. The drive has to be grabbed to have hope for reply. + Shortcomming (not a feature): unless burn_disc_read_atip() was called + only blank media will return valid info. + @param drive The drive to query. + @param start_lba Returns the start lba value + @param end_lba Returns the end lba value + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 if lba values are valid , 0 if invalid +*/ +int burn_drive_get_start_end_lba(struct burn_drive *drive, + int *start_lba, int *end_lba, int flag); + +/* ts A61110 */ +/** Read start lba and Next Writeable Address of a track from media. + Usually a track lba is obtained from the result of burn_track_get_entry(). + This call retrieves an updated lba, eventual nwa, and can address the + invisible track to come. + The drive must be grabbed for this call. One may not issue this call + during ongoing burn_disc_write() or burn_disc_erase(). + @param d The drive to query. + @param o If not NULL: write parameters to be set on drive before query + @param trackno 0=next track to come, >0 number of existing track + @param lba return value: start lba + @param nwa return value: Next Writeable Address + @return 1=nwa is valid , 0=nwa is not valid , -1=error +*/ +int burn_disc_track_lba_nwa(struct burn_drive *d, struct burn_write_opts *o, + int trackno, int *lba, int *nwa); + +/* ts A61202 */ +/** Tells the MMC Profile identifier of the loaded media. The drive must be + grabbed in order to get a non-zero result. + libburn currently writes only to profiles 0x09 "CD-R", 0x0a "CD-RW", + 0x12 "DVD-RAM", 0x13 "DVD-RW restricted overwrite" or 0x1a "DVD+RW". + @param d The drive where the media is inserted. + @param pno Profile Number as of mmc5r03c.pdf, table 89 + @param name Profile Name (e.g "CD-RW", unknown profiles have empty name) + @return 1 profile is valid, 0 no profile info available +*/ +int burn_disc_get_profile(struct burn_drive *d, int *pno, char name[80]); + +/** Tells whether a disc can be erased or not + @return Non-zero means erasable +*/ +int burn_disc_erasable(struct burn_drive *d); + +/** Returns the progress and status of a drive. + @param drive The drive to query busy state for. + @param p Returns the progress of the operation, NULL if you don't care + @return the current status of the drive. See also burn_drive_status. +*/ +enum burn_drive_status burn_drive_get_status(struct burn_drive *drive, + struct burn_progress *p); + +/** Creates a write_opts struct for burning to the specified drive + must be freed with burn_write_opts_free + @param drive The drive to write with + @return The write_opts, NULL on error +*/ +struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive); + +/** Frees a write_opts struct created with burn_write_opts_new + @param opts write_opts to free +*/ +void burn_write_opts_free(struct burn_write_opts *opts); + +/** Creates a read_opts struct for reading from the specified drive + must be freed with burn_read_opts_free + @param drive The drive to read from + @return The read_opts +*/ +struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive); + +/** Frees a read_opts struct created with burn_read_opts_new + @param opts write_opts to free +*/ +void burn_read_opts_free(struct burn_read_opts *opts); + +/** Erase a disc in the drive. The drive must be grabbed successfully BEFORE + calling this functions. Always ensure that the drive reports a status of + BURN_DISC_FULL before calling this function. An erase operation is not + cancellable, as control of the operation is passed wholly to the drive and + there is no way to interrupt it safely. + @param drive The drive with which to erase a disc. + @param fast Nonzero to do a fast erase, where only the disc's headers are + erased; zero to erase the entire disc. +*/ +void burn_disc_erase(struct burn_drive *drive, int fast); + + +/* ts A70101 - A70112 */ +/** Format media for use with libburn. This currently applies to DVD-RW + in state "Sequential Recording" (profile 0014h) which get formatted to + state "Restricted Overwrite" (profile 0013h). DVD+RW can be "de-iced" + by setting bit2 of flag. Other media cannot be formatted yet. + @param drive The drive with the disc to format. + @param size The size in bytes to be used with the format command. It should + be divisible by 32*1024. The effect of this parameter may + depend on the media profile. + @param flag Bitfield for control purposes: + bit0= after formatting, write the given number of zero-bytes + to the media and eventually perform preliminary closing. + bit1= insist in size 0 even if there is a better default known + bit2= format to maximum available size + bit3= -reserved- + bit4= enforce re-format of (partly) formatted media + bit7= MMC expert application mode (else libburn tries to + choose a suitable format type): + bit8 to bit15 contain the index of the format to use. See + burn_disc_get_formats(), burn_disc_get_format_descr(). + Acceptable types are: 0x00, 0x10, 0x11, 0x13, 0x15, 0x26. + If bit7 is set, bit4 is set automatically. +*/ +void burn_disc_format(struct burn_drive *drive, off_t size, int flag); + + +/* ts A70112 */ +/** Possible formatting status values */ +#define BURN_FORMAT_IS_UNFORMATTED 1 +#define BURN_FORMAT_IS_FORMATTED 2 +#define BURN_FORMAT_IS_UNKNOWN 3 + +/** Inquire the formatting status, the associated sizes and the number of + available formats. The info is media specific and stems from MMC command + 23h READ FORMAT CAPACITY. See mmc5r03c.pdf 6.24 for background details. + Media type can be determined via burn_disc_get_profile(). + @param drive The drive with the disc to format. + @param status The current formatting status of the inserted media. + See BURN_FORMAT_IS_* macros. Note: "unknown" is the + legal status for quick formatted, yet unwritten DVD-RW. + @param size The size in bytes associated with status. + unformatted: the maximum achievable size of the media + formatted: the currently formatted capacity + unknown: maximum capacity of drive or of media + @param bl_sas Additional info "Block Length/Spare Area Size". + Expected to be constantly 2048 for non-BD media. + @param num_formats The number of available formats. To be used with + burn_disc_get_format_descr() to obtain such a format + and eventually with burn_disc_format() to select one. + @return 1 reply is valid , <=0 failure +*/ +int burn_disc_get_formats(struct burn_drive *drive, int *status, off_t *size, + unsigned *bl_sas, int *num_formats); + +/** Inquire parameters of an available media format. + @param drive The drive with the disc to format. + @param index The index of the format item. Beginning with 0 up to reply + parameter from burn_disc_get_formats() : num_formats - 1 + @param type The format type. See mmc5r03c.pdf, 6.5, 04h FORMAT UNIT. + 0x00=full, 0x10=CD-RW/DVD-RW full, 0x11=CD-RW/DVD-RW grow, + 0x15=DVD-RW quick, 0x13=DVD-RW quick grow, + 0x26=DVD+RW background + @param size The maximum size in bytes achievable with this format. + @param tdp Type Dependent Parameter. See mmc5r03c.pdf. + @return 1 reply is valid , <=0 failure +*/ +int burn_disc_get_format_descr(struct burn_drive *drive, int index, + int *type, off_t *size, unsigned *tdp); + + + +/* ts A61109 : this was and is defunct */ +/** Read a disc from the drive and write it to an fd pair. The drive must be + grabbed successfully BEFORE calling this function. Always ensure that the + drive reports a status of BURN_DISC_FULL before calling this function. + @param drive The drive from which to read a disc. + @param o The options for the read operation. +*/ +void burn_disc_read(struct burn_drive *drive, const struct burn_read_opts *o); + +/** Write a disc in the drive. The drive must be grabbed successfully before + calling this function. Always ensure that the drive reports a status of + BURN_DISC_BLANK before calling this function. + @param o The options for the writing operation. + @param disc The struct burn_disc * that described the disc to be created +*/ +void burn_disc_write(struct burn_write_opts *o, struct burn_disc *disc); + +/** Cancel an operation on a drive. + This will only work when the drive's busy state is BURN_DRIVE_READING or + BURN_DRIVE_WRITING. + @param drive The drive on which to cancel the current operation. +*/ +void burn_drive_cancel(struct burn_drive *drive); + + +/* ts A61223 */ +/** Inquire wether the most recent write run was successful. Reasons for + non-success may be: rejection of burn parameters, abort during fatal errors + during write, a call to burn_drive_cancel() by the application thread. + @param d The drive to inquire. + @return 1=burn seems to have went well, 0=burn failed +*/ +int burn_drive_wrote_well(struct burn_drive *d); + + +/** Convert a minute-second-frame (MSF) value to sector count + @param m Minute component + @param s Second component + @param f Frame component + @return The sector count +*/ +int burn_msf_to_sectors(int m, int s, int f); + +/** Convert a sector count to minute-second-frame (MSF) + @param sectors The sector count + @param m Returns the minute component + @param s Returns the second component + @param f Returns the frame component +*/ +void burn_sectors_to_msf(int sectors, int *m, int *s, int *f); + +/** Convert a minute-second-frame (MSF) value to an lba + @param m Minute component + @param s Second component + @param f Frame component + @return The lba +*/ +int burn_msf_to_lba(int m, int s, int f); + +/** Convert an lba to minute-second-frame (MSF) + @param lba The lba + @param m Returns the minute component + @param s Returns the second component + @param f Returns the frame component +*/ +void burn_lba_to_msf(int lba, int *m, int *s, int *f); + +/** Create a new disc (for DAO recording)*/ +struct burn_disc *burn_disc_create(void); + +/** Delete disc and decrease the reference count on all its sessions + @param d The disc to be freed +*/ +void burn_disc_free(struct burn_disc *d); + +/** Create a new session (For SAO at once recording, or to be added to a + disc for DAO) +*/ +struct burn_session *burn_session_create(void); + +/** Free a session (and decrease reference count on all tracks inside) + @param s Session to be freed +*/ +void burn_session_free(struct burn_session *s); + +/** Add a session to a disc at a specific position, increasing the + sessions's reference count. + @param d Disc to add the session to + @param s Session to add to the disc + @param pos position to add at (BURN_POS_END is "at the end") + @return 0 for failure, 1 for success +*/ +int burn_disc_add_session(struct burn_disc *d, struct burn_session *s, + unsigned int pos); + +/** Remove a session from a disc + @param d Disc to remove session from + @param s Session pointer to find and remove +*/ +int burn_disc_remove_session(struct burn_disc *d, struct burn_session *s); + + +/** Create a track (for TAO recording, or to put in a session) */ +struct burn_track *burn_track_create(void); + +/** Free a track + @param t Track to free +*/ +void burn_track_free(struct burn_track *t); + +/** Add a track to a session at specified position + @param s Session to add to + @param t Track to insert in session + @param pos position to add at (BURN_POS_END is "at the end") + @return 0 for failure, 1 for success +*/ +int burn_session_add_track(struct burn_session *s, struct burn_track *t, + unsigned int pos); + +/** Remove a track from a session + @param s Session to remove track from + @param t Track pointer to find and remove + @return 0 for failure, 1 for success +*/ +int burn_session_remove_track(struct burn_session *s, struct burn_track *t); + + +/** Define the data in a track + @param t the track to define + @param offset The lib will write this many 0s before start of data + @param tail The number of extra 0s to write after data + @param pad 1 means the lib should pad the last sector with 0s if the + track isn't exactly sector sized. (otherwise the lib will + begin reading from the next track) + @param mode data format (bitfield) +*/ +void burn_track_define_data(struct burn_track *t, int offset, int tail, + int pad, int mode); + + +/* ts A61024 */ +/** Define wether a track shall swap bytes of its input stream. + @param t The track to change + @param swap_source_bytes 0=do not swap, 1=swap byte pairs + @return 1=success , 0=unacceptable value +*/ +int burn_track_set_byte_swap(struct burn_track *t, int swap_source_bytes); + + +/** Set the ISRC details for a track + @param t The track to change + @param country the 2 char country code. Each character must be + only numbers or letters. + @param owner 3 char owner code. Each character must be only numbers + or letters. + @param year 2 digit year. A number in 0-99 (Yep, not Y2K friendly). + @param serial 5 digit serial number. A number in 0-99999. +*/ +void burn_track_set_isrc(struct burn_track *t, char *country, char *owner, + unsigned char year, unsigned int serial); + +/** Disable ISRC parameters for a track + @param t The track to change +*/ +void burn_track_clear_isrc(struct burn_track *t); + +/** Hide the first track in the "pre gap" of the disc + @param s session to change + @param onoff 1 to enable hiding, 0 to disable +*/ +void burn_session_hide_first_track(struct burn_session *s, int onoff); + +/** Get the drive's disc struct - free when done + @param d drive to query + @return the disc struct or NULL on failure +*/ +struct burn_disc *burn_drive_get_disc(struct burn_drive *d); + +/** Set the track's data source + @param t The track to set the data source for + @param s The data source to use for the contents of the track + @return An error code stating if the source is ready for use for + writing the track, or if an error occured + +*/ +enum burn_source_status burn_track_set_source(struct burn_track *t, + struct burn_source *s); + +/** Free a burn_source (decrease its refcount and maybe free it) + @param s Source to free +*/ +void burn_source_free(struct burn_source *s); + +/** Creates a data source for an image file (and maybe subcode file) */ +struct burn_source *burn_file_source_new(const char *path, + const char *subpath); + +/** Creates a data source for an image file (a track) from an open + readable filedescriptor, an eventually open readable subcodes file + descriptor and eventually a fixed size in bytes. + @param datafd The source of data. + @param subfd The eventual source for subcodes. Not used if -1. + @param size The eventual fixed size of eventually both fds. + If this value is 0, the size will be determined from datafd. +*/ +struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size); + +/** Tells how long a track will be on disc + >>> NOTE: Not reliable with tracks of undefined length +*/ +int burn_track_get_sectors(struct burn_track *); + + +/* ts A61101 */ +/** Tells how many source bytes have been read and how many data bytes have + been written by the track during burn */ +int burn_track_get_counters(struct burn_track *t, + off_t *read_bytes, off_t *written_bytes); + + +/** Sets drive read and write speed + @param d The drive to set speed for + @param read Read speed in k/s (0 is max) + @param write Write speed in k/s (0 is max) +*/ +void burn_drive_set_speed(struct burn_drive *d, int read, int write); + +/* these are for my debugging, they will disappear */ +void burn_structure_print_disc(struct burn_disc *d); +void burn_structure_print_session(struct burn_session *s); +void burn_structure_print_track(struct burn_track *t); + +/** Sets the write type for the write_opts struct + @param opts The write opts to change + @param write_type The write type to use + @param block_type The block type to use + @return Returns 1 on success and 0 on failure. +*/ +int burn_write_opts_set_write_type(struct burn_write_opts *opts, + enum burn_write_types write_type, + int block_type); + +/** Supplies toc entries for writing - not normally required for cd mastering + @param opts The write opts to change + @param count The number of entries + @param toc_entries +*/ +void burn_write_opts_set_toc_entries(struct burn_write_opts *opts, + int count, + struct burn_toc_entry *toc_entries); + +/** Sets the session format for a disc + @param opts The write opts to change + @param format The session format to set +*/ +void burn_write_opts_set_format(struct burn_write_opts *opts, int format); + +/** Sets the simulate value for the write_opts struct + @param opts The write opts to change + @param sim If non-zero, the drive will perform a simulation instead of a burn + @return Returns 1 on success and 0 on failure. +*/ +int burn_write_opts_set_simulate(struct burn_write_opts *opts, int sim); + +/** Controls buffer underrun prevention + @param opts The write opts to change + @param underrun_proof if non-zero, buffer underrun protection is enabled + @return Returns 1 on success and 0 on failure. +*/ +int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts, + int underrun_proof); + +/** Sets whether to use opc or not with the write_opts struct + @param opts The write opts to change + @param opc If non-zero, optical power calibration will be performed at + start of burn + +*/ +void burn_write_opts_set_perform_opc(struct burn_write_opts *opts, int opc); + +void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts, int has_mediacatalog); + +void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, unsigned char mediacatalog[13]); + + +/* ts A61106 */ +/** Sets the multi flag which eventually marks the emerging session as not + being the last one and thus creating a BURN_DISC_APPENDABLE media. + @param multi 1=media will be appendable, 0=media will be closed (default) +*/ +void burn_write_opts_set_multi(struct burn_write_opts *opts, int multi); + + +/* ts A61222 */ +/** Sets a start address for writing to media and write modes which allow to + choose this address at all (DVD+RW, DVD-RAM, DVD-RW only for now). The + address is given in bytes. If it is not -1 then a write run will fail if + choice of start address is not supported or if the block alignment of the + address is not suitable for media and write mode. (Alignment to 32 kB + blocks is advised with DVD media.) + @param opts The write opts to change + @param value The address in bytes (-1 = start at default address) +*/ +void burn_write_opts_set_start_byte(struct burn_write_opts *opts, off_t value); + + +/** Sets whether to read in raw mode or not + @param opts The read opts to change + @param raw_mode If non-zero, reading will be done in raw mode, so that everything in the data tracks on the + disc is read, including headers. +*/ +void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw_mode); + +/** Sets whether to report c2 errors or not + @param opts The read opts to change + @param c2errors If non-zero, report c2 errors. +*/ +void burn_read_opts_set_c2errors(struct burn_read_opts *opts, int c2errors); + +/** Sets whether to read subcodes from audio tracks or not + @param opts The read opts to change + @param subcodes_audio If non-zero, read subcodes from audio tracks on the disc. +*/ +void burn_read_opts_read_subcodes_audio(struct burn_read_opts *opts, + int subcodes_audio); + +/** Sets whether to read subcodes from data tracks or not + @param opts The read opts to change + @param subcodes_data If non-zero, read subcodes from data tracks on the disc. +*/ +void burn_read_opts_read_subcodes_data(struct burn_read_opts *opts, + int subcodes_data); + +/** Sets whether to recover errors if possible + @param opts The read opts to change + @param hardware_error_recovery If non-zero, attempt to recover errors if possible. +*/ +void burn_read_opts_set_hardware_error_recovery(struct burn_read_opts *opts, + int hardware_error_recovery); + +/** Sets whether to report recovered errors or not + @param opts The read opts to change + @param report_recovered_errors If non-zero, recovered errors will be reported. +*/ +void burn_read_opts_report_recovered_errors(struct burn_read_opts *opts, + int report_recovered_errors); + +/** Sets whether blocks with unrecoverable errors should be read or not + @param opts The read opts to change + @param transfer_damaged_blocks If non-zero, blocks with unrecoverable errors will still be read. +*/ +void burn_read_opts_transfer_damaged_blocks(struct burn_read_opts *opts, + int transfer_damaged_blocks); + +/** Sets the number of retries to attempt when trying to correct an error + @param opts The read opts to change + @param hardware_error_retries The number of retries to attempt when correcting an error. +*/ +void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts, + unsigned char hardware_error_retries); + +/** Gets the maximum write speed for a drive and eventually loaded media. + The return value might change by the media type of already loaded media, + again by call burn_drive_grab() and again by call burn_disc_read_atip(). + @param d Drive to query + @return Maximum write speed in K/s +*/ +int burn_drive_get_write_speed(struct burn_drive *d); + + +/* ts A61021 */ +/** Gets the minimum write speed for a drive and eventually loaded media. + The return value might change by the media type of already loaded media, + again by call burn_drive_grab() and again by call burn_disc_read_atip(). + @param d Drive to query + @return Minimum write speed in K/s +*/ +int burn_drive_get_min_write_speed(struct burn_drive *d); + + +/** Gets the maximum read speed for a drive + @param d Drive to query + @return Maximum read speed in K/s +*/ +int burn_drive_get_read_speed(struct burn_drive *d); + + +/* ts A61226 */ +/** Obtain a copy of the current speed descriptor list. The drive's list gets + updated on various occasions such as burn_drive_grab() but the copy + obtained here stays untouched. It has to be disposed via + burn_drive_free_speedlist() when it is not longer needed. Speeds + may appear several times in the list. The list content depends much on + drive and media type. It seems that .source == 1 applies mostly to CD media + whereas .source == 2 applies to any media. + @param d Drive to query + @param speed_list The copy. If empty, *speed_list gets returned as NULL. + @return 1=success , 0=list empty , <0 severe error +*/ +int burn_drive_get_speedlist(struct burn_drive *d, + struct burn_speed_descriptor **speed_list); + +/* ts A61226 */ +/** Dispose a speed descriptor list copy which was obtained by + burn_drive_get_speedlist(). + @param speed_list The list copy. *speed_list gets set to NULL. + @return 1=list disposed , 0= *speedlist was already NULL +*/ +int burn_drive_free_speedlist(struct burn_speed_descriptor **speed_list); + + +/** Gets a copy of the toc_entry structure associated with a track + @param t Track to get the entry from + @param entry Struct for the library to fill out +*/ +void burn_track_get_entry(struct burn_track *t, struct burn_toc_entry *entry); + +/** Gets a copy of the toc_entry structure associated with a session's lead out + @param s Session to get the entry from + @param entry Struct for the library to fill out +*/ +void burn_session_get_leadout_entry(struct burn_session *s, + struct burn_toc_entry *entry); + +/** Gets an array of all the sessions for the disc + THIS IS NO LONGER VALID AFTER YOU ADD OR REMOVE A SESSION + @param d Disc to get session array for + @param num Returns the number of sessions in the array + @return array of sessions +*/ +struct burn_session **burn_disc_get_sessions(struct burn_disc *d, + int *num); + +int burn_disc_get_sectors(struct burn_disc *d); + +/** Gets an array of all the tracks for a session + THIS IS NO LONGER VALID AFTER YOU ADD OR REMOVE A TRACK + @param s session to get track array for + @param num Returns the number of tracks in the array + @return array of tracks +*/ +struct burn_track **burn_session_get_tracks(struct burn_session *s, + int *num); + +int burn_session_get_sectors(struct burn_session *s); + +/** Gets the mode of a track + @param track the track to query + @return the track's mode +*/ +int burn_track_get_mode(struct burn_track *track); + +/** Returns whether the first track of a session is hidden in the pregap + @param session the session to query + @return non-zero means the first track is hidden +*/ +int burn_session_get_hidefirst(struct burn_session *session); + +/** Returns the library's version in its parts + @param major The major version number + @param minor The minor version number + @param micro The micro version number +*/ +void burn_version(int *major, int *minor, int *micro); + + +/* ts A60924 : ticket 74 */ +/** Control queueing and stderr printing of messages from libburn. + Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", + "NOTE", "UPDATE", "DEBUG", "ALL". + @param queue_severity Gives the minimum limit for messages to be queued. + Default: "NEVER". If you queue messages then you + must consume them by burn_msgs_obtain(). + @param print_severity Does the same for messages to be printed directly + to stderr. Default: "FATAL". + @param print_id A text prefix to be printed before the message. + @return >0 for success, <=0 for error + +*/ +int burn_msgs_set_severities(char *queue_severity, + char *print_severity, char *print_id); + +/* ts A60924 : ticket 74 */ +#define BURN_MSGS_MESSAGE_LEN 4096 + +/** Obtain the oldest pending libburn message from the queue which has at + least the given minimum_severity. This message and any older message of + lower severity will get discarded from the queue and is then lost forever. + @param minimum_severity may be one of "NEVER", "FATAL", "SORRY", + "WARNING", "HINT", "NOTE", "UPDATE", "DEBUG", "ALL". + To call with minimum_severity "NEVER" will discard the + whole queue. + @param error_code Will become a unique error code as liste in + libburn/libdax_msgs.h + @param msg_text Must provide at least BURN_MSGS_MESSAGE_LEN bytes. + @param os_errno Will become the eventual errno related to the message + @param severity Will become the severity related to the message and + should provide at least 80 bytes. + @return 1 if a matching item was found, 0 if not, <0 for severe errors +*/ +int burn_msgs_obtain(char *minimum_severity, + int *error_code, char msg_text[], int *os_errno, + char severity[]); + + +/* ts A61002 */ +/* The prototype of a handler function suitable for burn_set_abort_handling(). + Such a function has to return -2 if it does not want the process to + exit with value 1. +*/ +typedef int (*burn_abort_handler_t)(void *handle, int signum, int flag); + +/** Control builtin signal handling. See also burn_abort(). + @param handle Opaque handle eventually pointing to an application + provided memory object + @param handler A function to be called on signals. It will get handle as + argument. It should finally call burn_abort(). See there. + @param mode : 0 call handler(handle, signum, 0) on nearly all signals + 1 enable system default reaction on all signals + 2 try to ignore nearly all signals + 10 like mode 2 but handle SIGABRT like with mode 0 + Arguments (text, NULL, 0) activate the builtin abort handler. It will + eventually call burn_abort() and then perform exit(1). If text is not NULL + then it is used as prefix for pacifier messages of burn_abort_pacifier(). +*/ +void burn_set_signal_handling(void *handle, burn_abort_handler_t handler, + int mode); + +#ifndef DOXYGEN + +BURN_END_DECLS + +#endif + +#endif /*LIBBURN_H*/ diff --git a/libburn/tags/ZeroThreeZero/libburn/libdax_audioxtr.c b/libburn/tags/ZeroThreeZero/libburn/libdax_audioxtr.c new file mode 100644 index 00000000..5f9037df --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/libdax_audioxtr.c @@ -0,0 +1,326 @@ + +/* libdax_audioxtr + Audio track data extraction facility of libdax and libburn. + Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL +*/ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + + +/* Only this single source module is entitled to do this */ +#define LIBDAX_AUDIOXTR_H_INTERNAL 1 + +/* All clients of the extraction facility must do this */ +#include "libdax_audioxtr.h" + + +int libdax_audioxtr_new(struct libdax_audioxtr **xtr, char *path, int flag) +{ + int ret= -1; + struct libdax_audioxtr *o; + + o= *xtr= (struct libdax_audioxtr *) malloc(sizeof(struct libdax_audioxtr)); + if(o==NULL) + return(-1); + strncpy(o->path,path,LIBDAX_AUDIOXTR_STRLEN-1); + o->path[LIBDAX_AUDIOXTR_STRLEN]= 0; + o->fd= -1; + strcpy(o->fmt,"unidentified"); + o->fmt_info[0]= 0; + o->data_size= 0; + o->extract_count= 0; + + o->num_channels= 0; + o->sample_rate= 0; + o->bits_per_sample= 0; + o->msb_first= 0; + + o->wav_subchunk2_size= 0; + + o->au_data_location= 0; + o->au_data_size= 0xffffffff; + + ret= libdax_audioxtr_open(o,0); + if(ret<=0) + {ret= -2*(ret<0); goto failure;} + + return(1); +failure: + libdax_audioxtr_destroy(xtr,0); + return(ret); +} + + +int libdax_audioxtr_destroy(struct libdax_audioxtr **xtr, int flag) +{ + struct libdax_audioxtr *o; + + o= *xtr; + if(o==NULL) + return(0); + if(o->fd>=0 && strcmp(o->path,"-")!=0) + close(o->fd); + free((char *) o); + *xtr= NULL; + return(1); +} + + +static int libdax_audioxtr_open(struct libdax_audioxtr *o, int flag) +{ + int ret; + char msg[LIBDAX_AUDIOXTR_STRLEN+80]; + + if(strcmp(o->path,"-")==0) + o->fd= 0; + else + o->fd= open(o->path, O_RDONLY); + if(o->fd<0) { + sprintf(msg,"Cannot open audio source file : %s",o->path); + libdax_msgs_submit(libdax_messenger,-1,0x00020200, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, errno, 0); + return(-1); + } + ret= libdax_audioxtr_identify(o,0); + if(ret<=0) { + sprintf(msg,"Audio source file has unsuitable format : %s",o->path); + libdax_msgs_submit(libdax_messenger,-1,0x00020201, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return(0); + } + ret= libdax_audioxtr_init_reading(o,0); + if(ret<=0) { + sprintf(msg,"Failed to prepare reading of audio data : %s",o->path); + libdax_msgs_submit(libdax_messenger,-1,0x00020202, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return(0); + } + return(1); +} + + +static int libdax_audioxtr_identify_wav(struct libdax_audioxtr *o, int flag) +{ + int ret; + char buf[45]; + + /* check wether this is a MS WAVE file .wav */ + /* info used: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */ + + if(o->fd!=0) { + ret= lseek(o->fd,0,SEEK_SET); + if(ret==-1) + return(0); + } + ret= read(o->fd, buf, 44); + if(ret<44) + return(0); + buf[44]= 0; /* as stopper for any string operations */ + + if(strncmp(buf,"RIFF",4)!=0) /* ChunkID */ + return(0); + if(strncmp(buf+8,"WAVE",4)!=0) /* Format */ + return(0); + if(strncmp(buf+12,"fmt ",4)!=0) /* Subchunk1ID */ + return(0); + if(buf[16]!=16 || buf[17]!=0 || buf[18]!=0 || buf[19]!=0) /* Subchunk1Size */ + return(0); + if(buf[20]!=1 || buf[21]!=0) /* AudioFormat must be 1 (Linear quantization) */ + return(0); + + strcpy(o->fmt,".wav"); + o->msb_first= 0; + o->num_channels= libdax_audioxtr_to_int(o,(unsigned char *) buf+22,2,0); + o->sample_rate= libdax_audioxtr_to_int(o,(unsigned char *) buf+24,4,0); + o->bits_per_sample= libdax_audioxtr_to_int(o,(unsigned char *)buf+34,2,0); + sprintf(o->fmt_info, + ".wav , num_channels=%d , sample_rate=%d , bits_per_sample=%d", + o->num_channels,o->sample_rate,o->bits_per_sample); + o->wav_subchunk2_size= libdax_audioxtr_to_int(o,(unsigned char *)buf+40,4,0); + o->data_size= o->wav_subchunk2_size; + return(1); +} + + +static int libdax_audioxtr_identify_au(struct libdax_audioxtr *o, int flag) +{ + int ret,encoding; + char buf[24]; + + /* Check wether this is a Sun Audio, .au file */ + /* info used: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */ + + if(o->fd!=0) { + ret= lseek(o->fd,0,SEEK_SET); + if(ret==-1) + return(0); + } + ret= read(o->fd, buf, 24); + if(ret<24) + return(0); + + if(strncmp(buf,".snd",4)!=0) + return(0); + strcpy(o->fmt,".au"); + o->msb_first= 1; + o->au_data_location= libdax_audioxtr_to_int(o,(unsigned char *)buf+4,4,1); + o->au_data_size= libdax_audioxtr_to_int(o,(unsigned char *)buf+8,4,1); + encoding= libdax_audioxtr_to_int(o,(unsigned char *)buf+12,4,1); + if(encoding==2) + o->bits_per_sample= 8; + else if(encoding==3) + o->bits_per_sample= 16; + else if(encoding==4) + o->bits_per_sample= 24; + else if(encoding==5) + o->bits_per_sample= 32; + else + o->bits_per_sample= -encoding; + o->sample_rate= libdax_audioxtr_to_int(o,(unsigned char *)buf+16,4,1); + o->num_channels= libdax_audioxtr_to_int(o,(unsigned char *)buf+20,4,1); + if(o->au_data_size!=0xffffffff) + o->data_size= o->au_data_size; + else + o->data_size= 0; + sprintf(o->fmt_info, + ".au , num_channels=%d , sample_rate=%d , bits_per_sample=%d", + o->num_channels,o->sample_rate,o->bits_per_sample); + + /* <<< for testing only */; + return(1); + + return(o->bits_per_sample>0); /* Audio format must be linear PCM */ +} + + +static int libdax_audioxtr_identify(struct libdax_audioxtr *o, int flag) +{ + int ret; + + ret= libdax_audioxtr_identify_wav(o, 0); + if(ret!=0) + return(ret); + if(o->fd==0) /* cannot rewind stdin */ + return(0); + ret= libdax_audioxtr_identify_au(o, 0); + if(ret!=0) + return(ret); + return(0); +} + + +/* @param flag bit0=msb_first */ +static unsigned libdax_audioxtr_to_int(struct libdax_audioxtr *o, + unsigned char *bytes, int len, int flag) +{ + unsigned int ret= 0; + int i; + + if(flag&1) + for(i= 0; i<len; i++) + ret= ret*256+bytes[i]; + else + for(i= len-1; i>=0; i--) + ret= ret*256+bytes[i]; + return(ret); +} + + +static int libdax_audioxtr_init_reading(struct libdax_audioxtr *o, int flag) +{ + int ret; + + + /* currently this only works for MS WAVE files .wav and Sun .au*/; + if(o->fd==0) /* stdin: hope no read came after libdax_audioxtr_identify() */ + return(1); + + o->extract_count= 0; + if(strcmp(o->fmt,".wav")==0) + ret= lseek(o->fd,44,SEEK_SET); + else if(strcmp(o->fmt,".au")==0) + ret= lseek(o->fd,o->au_data_location,SEEK_SET); + else + ret= -1; + if(ret==-1) + return(0); + + return(1); +} + + +int libdax_audioxtr_get_id(struct libdax_audioxtr *o, + char **fmt, char **fmt_info, + int *num_channels, int *sample_rate, int *bits_per_sample, + int *msb_first, int flag) +{ + *fmt= o->fmt; + *fmt_info= o->fmt_info; + *num_channels= o->num_channels; + *sample_rate= o->sample_rate; + *bits_per_sample= o->bits_per_sample; + *msb_first= o->msb_first; + return(1); +} + + +int libdax_audioxtr_get_size(struct libdax_audioxtr *o, off_t *size, int flag) +{ + *size= o->data_size; + return(1); +} + + +int libdax_audioxtr_read(struct libdax_audioxtr *o, + char buffer[], int buffer_size, int flag) +{ + int ret; + + if(buffer_size<=0 || o->fd<0) + return(-2); + if(o->data_size>0 && !(flag&1)) + if(buffer_size > o->data_size - o->extract_count) + buffer_size= o->data_size - o->extract_count; + if(buffer_size<=0) + return(0); + ret= read(o->fd,buffer,buffer_size); + if(ret>0) + o->extract_count+= ret; + return(ret); +} + + +int libdax_audioxtr_detach_fd(struct libdax_audioxtr *o, int *fd, int flag) +{ + if(o->fd<0) + return(-1); + if(strcmp(o->fmt,".wav")!=0 && strcmp(o->fmt,".au")!=0) + return(0); + if(flag&1) { + *fd= o->fd; + } else { + *fd= dup(o->fd); + if(*fd>=0 && strcmp(o->path,"-")!=0) + close(o->fd); + } + if(*fd>=0) { + o->fd= -1; + return(1); + } + return(-1); +} + diff --git a/libburn/tags/ZeroThreeZero/libburn/libdax_audioxtr.h b/libburn/tags/ZeroThreeZero/libburn/libdax_audioxtr.h new file mode 100644 index 00000000..72b62bf3 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/libdax_audioxtr.h @@ -0,0 +1,229 @@ + +/* libdax_audioxtr + Audio track data extraction facility of libdax and libburn. + Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL +*/ + +#ifndef LIBDAX_AUDIOXTR_H_INCLUDED +#define LIBDAX_AUDIOXTR_H_INCLUDED 1 + + /* Public Macros */ + +/* Maximum size for address paths and fmt_info strings */ +#define LIBDAX_AUDIOXTR_STRLEN 4096 + + + /* Public Opaque Handles */ + +/** Extractor object encapsulating intermediate states of extraction. + The clients of libdax_audioxtr shall only allocate pointers to this + struct and get a storage object via libdax_audioxtr_new(). + Appropriate initial value for the pointer is NULL. +*/ +struct libdax_audioxtr; + + + /* Public Functions */ + + /* Calls initiated from inside libdax/libburn */ + + + /* Calls from applications (to be forwarded by libdax/libburn) */ + + +/** Open an audio file, check wether suitable, create extractor object. + @param xtr Opaque handle to extractor. Gets attached extractor object. + @param path Address of the audio file to extract. "-" is stdin (but might + be not suitable for all futurely supported formats). + @param flag Bitfield for control purposes (unused yet, submit 0) + @return >0 success + 0 unsuitable format + -1 severe error + -2 path not found +*/ +int libdax_audioxtr_new(struct libdax_audioxtr **xtr, char *path, int flag); + + +/** Obtain identification parameters of opened audio source. + @param xtr Opaque handle to extractor + @param fmt Gets pointed to the audio file format id text: ".wav" , ".au" + @param fmt_info Gets pointed to a format info text telling parameters + @param num_channels e.g. 1=mono, 2=stereo, etc + @param sample_rate e.g. 11025, 44100 + @param bits_per_sample e.g. 8= 8 bits per sample, 16= 16 bits ... + @param msb_first Byte order of samples: 0=Intel 1=Motorola + @param flag Bitfield for control purposes (unused yet, submit 0) + @return >0 success, <=0 failure +*/ +int libdax_audioxtr_get_id(struct libdax_audioxtr *xtr, + char **fmt, char **fmt_info, + int *num_channels, int *sample_rate, + int *bits_per_sample, int *msb_first, int flag); + + +/** Obtain a prediction about the extracted size based on internal information + of the formatted file. + @param xtr Opaque handle to extractor + @param size Gets filled with the predicted size + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 prediction was possible , 0 no prediction could be made +*/ +int libdax_audioxtr_get_size(struct libdax_audioxtr *o, off_t *size, int flag); + + +/** Obtain next buffer full of extracted data in desired format (only raw audio + for now). + @param xtr Opaque handle to extractor + @param buffer Gets filled with extracted data + @param buffer_size Maximum number of bytes to be filled into buffer + @param flag Bitfield for control purposes + bit0= do not stop at predicted end of data + @return >0 number of valid buffer bytes, + 0 End of file + -1 operating system reports error + -2 usage error by application +*/ +int libdax_audioxtr_read(struct libdax_audioxtr *xtr, + char buffer[], int buffer_size, int flag); + + +/** Try to obtain a file descriptor which will deliver extracted data + to normal calls of read(2). This may fail because the format is + unsuitable for that, but ".wav" is ok. If this call succeeds the xtr + object will have forgotten its file descriptor and libdax_audioxtr_read() + will return a usage error. One may use *fd after libdax_audioxtr_destroy() + and will have to close it via close(2) when done with it. + @param xtr Opaque handle to extractor + @param fd Eventually returns the file descriptor number + @param flag Bitfield for control purposes + bit0= do not dup(2) and close(2) but hand out original fd + @return 1 success, 0 cannot hand out fd , -1 severe error +*/ +int libdax_audioxtr_detach_fd(struct libdax_audioxtr *o, int *fd, int flag); + + +/** Clean up after extraction and destroy extractor object. + @param xtr Opaque handle to extractor, *xtr is allowed to be NULL, + *xtr is set to NULL by this function + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 = destroyed object, 0 = was already destroyed +*/ +int libdax_audioxtr_destroy(struct libdax_audioxtr **xtr, int flag); + + + +#ifdef LIDBAX_AUDIOXTR________________ + + +-- place documentation text here --- + + +#endif /* LIDBAX_AUDIOXTR_________________ */ + + + +/* + *Never* set this macro outside libdax_audioxtr.c ! + The entrails of this facility are not to be seen by + the other library components or the applications. +*/ +#ifdef LIBDAX_AUDIOXTR_H_INTERNAL + + /* Internal Structures */ + +/** Extractor object encapsulating intermediate states of extraction */ +struct libdax_audioxtr { + + /* Source of the encoded audio data */ + char path[LIBDAX_AUDIOXTR_STRLEN]; + + /* File descriptor to path. Anything else than 0 must be lseek-able */ + int fd; + + /* Format identifier. E.g. ".wav" */ + char fmt[80]; + + /* Format parameter info text */ + char fmt_info[LIBDAX_AUDIOXTR_STRLEN]; + + /* 1= mono, 2= stereo, etc. */ + int num_channels; + + /* 8000, 44100, etc. */ + int sample_rate; + + /* 8 bits = 8, 16 bits = 16, etc. */ + int bits_per_sample; + + /* Byte order of samples: 0=Intel 1=Motorola */ + int msb_first; + + /* Number of bytes to extract (0= unknown/unlimited) */ + off_t data_size; + + /* Number of extracted data bytes */ + off_t extract_count; + + + /* Format dependent parameters */ + + /* MS WAVE Format */ + /* info used: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */ + + /* == NumSamples * NumChannels * BitsPerSample/8 + This is the number of bytes in the data. */ + unsigned wav_subchunk2_size; + + + /* Sun Audio, .au */ + /* info used: http://www.opengroup.org/public/pubs/external/auformat.html */ + + /* Number of bytes in non-payload header part */ + unsigned au_data_location; + + /* Number of payload bytes or 0xffffffff */ + unsigned au_data_size; + +}; + + + /* Internal Functions */ + +/** Open the audio source pointed to by .path and evaluate suitability. + @return -1 failure to open, 0 unsuitable format, 1 success +*/ +static int libdax_audioxtr_open(struct libdax_audioxtr *o, int flag); + + +/** Identify format and evaluate suitability. + @return 0 unsuitable format, 1 format is suitable +*/ +static int libdax_audioxtr_identify(struct libdax_audioxtr *o, int flag); + +/** Specialized identifier for .wav */ +static int libdax_audioxtr_identify_wav(struct libdax_audioxtr *o, int flag); +/** Specialized identifier for .au */ +static int libdax_audioxtr_identify_au(struct libdax_audioxtr *o, int flag); + + +/** Convert a byte string into a number (currently only little endian) + @param flag Bitfield for control purposes + bit0=msb_first + @return The resulting number +*/ +static unsigned libdax_audioxtr_to_int(struct libdax_audioxtr *o, + unsigned char *bytes, int len, int flag); + + +/** Prepare for reading of first buffer. + @return 0 error, 1 success +*/ +static int libdax_audioxtr_init_reading(struct libdax_audioxtr *o, int flag); + + + +#endif /* LIBDAX_AUDIOXTR_H_INTERNAL */ + + +#endif /* ! LIBDAX_AUDIOXTR_H_INCLUDED */ + diff --git a/libburn/tags/ZeroThreeZero/libburn/libdax_msgs.c b/libburn/tags/ZeroThreeZero/libburn/libdax_msgs.c new file mode 100644 index 00000000..5f179ae4 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/libdax_msgs.c @@ -0,0 +1,404 @@ + +/* libdax_msgs + Message handling facility of libdax. + Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL +*/ + +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/time.h> + +/* Only this single source module is entitled to do this */ +#define LIBDAX_MSGS_H_INTERNAL 1 + +/* All participants in the messaging system must do this */ +#include "libdax_msgs.h" + + +/* ----------------------------- libdax_msgs_item ------------------------- */ + + +static int libdax_msgs_item_new(struct libdax_msgs_item **item, + struct libdax_msgs_item *link, int flag) +{ + int ret; + struct libdax_msgs_item *o; + struct timeval tv; + struct timezone tz; + + (*item)= o= + (struct libdax_msgs_item *) malloc(sizeof(struct libdax_msgs_item)); + if(o==NULL) + return(-1); + o->timestamp= 0.0; + ret= gettimeofday(&tv,&tz); + if(ret==0) + o->timestamp= tv.tv_sec+0.000001*tv.tv_usec; + o->process_id= getpid(); + o->driveno= -1; + o->severity= LIBDAX_MSGS_SEV_ALL; + o->priority= LIBDAX_MSGS_PRIO_ZERO; + o->error_code= 0; + o->msg_text= NULL; + o->os_errno= 0; + o->prev= link; + o->next= NULL; + if(link!=NULL) { + if(link->next!=NULL) { + link->next->prev= o; + o->next= link->next; + } + link->next= o; + } + return(1); +} + + +/** Detaches item from its queue and eventually readjusts start, end pointers + of the queue */ +int libdax_msgs_item_unlink(struct libdax_msgs_item *o, + struct libdax_msgs_item **chain_start, + struct libdax_msgs_item **chain_end, int flag) +{ + if(o->prev!=NULL) + o->prev->next= o->next; + if(o->next!=NULL) + o->next->prev= o->prev; + if(chain_start!=NULL) + if(*chain_start == o) + *chain_start= o->next; + if(chain_end!=NULL) + if(*chain_end == o) + *chain_end= o->prev; + o->next= o->prev= NULL; + return(1); +} + + +int libdax_msgs_item_destroy(struct libdax_msgs_item **item, + int flag) +{ + struct libdax_msgs_item *o; + + o= *item; + if(o==NULL) + return(0); + libdax_msgs_item_unlink(o,NULL,NULL,0); + if(o->msg_text!=NULL) + free((char *) o->msg_text); + free((char *) o); + *item= NULL; + return(1); +} + + +int libdax_msgs_item_get_msg(struct libdax_msgs_item *item, + int *error_code, char **msg_text, int *os_errno, + int flag) +{ + *error_code= item->error_code; + *msg_text= item->msg_text; + *os_errno= item->os_errno; + return(1); +} + + +int libdax_msgs_item_get_origin(struct libdax_msgs_item *item, + double *timestamp, pid_t *process_id, int *driveno, + int flag) +{ + *timestamp= item->timestamp; + *process_id= item->process_id; + *driveno= item->driveno; + return(1); +} + + +int libdax_msgs_item_get_rank(struct libdax_msgs_item *item, + int *severity, int *priority, int flag) +{ + *severity= item->severity; + *priority= item->priority; + return(1); +} + + +/* ------------------------------- libdax_msgs ---------------------------- */ + + +int libdax_msgs_new(struct libdax_msgs **m, int flag) +{ + struct libdax_msgs *o; + + (*m)= o= (struct libdax_msgs *) malloc(sizeof(struct libdax_msgs)); + if(o==NULL) + return(-1); + o->oldest= NULL; + o->youngest= NULL; + o->count= 0; + o->queue_severity= LIBDAX_MSGS_SEV_ALL; + o->print_severity= LIBDAX_MSGS_SEV_NEVER; + strcpy(o->print_id,"libdax: "); + +#ifndef LIBDAX_MSGS_SINGLE_THREADED + pthread_mutex_init(&(o->lock_mutex),NULL); +#endif + + return(1); +} + + +int libdax_msgs_destroy(struct libdax_msgs **m, int flag) +{ + struct libdax_msgs *o; + struct libdax_msgs_item *item, *next_item; + + o= *m; + if(o==NULL) + return(0); + +#ifndef LIBDAX_MSGS_SINGLE_THREADED + if(pthread_mutex_destroy(&(o->lock_mutex))!=0) { + pthread_mutex_unlock(&(o->lock_mutex)); + pthread_mutex_destroy(&(o->lock_mutex)); + } +#endif + + for(item= o->oldest; item!=NULL; item= next_item) { + next_item= item->next; + libdax_msgs_item_destroy(&item,0); + } + free((char *) o); + *m= NULL; + return(1); +} + + +int libdax_msgs_set_severities(struct libdax_msgs *m, int queue_severity, + int print_severity, char *print_id, int flag) +{ + m->queue_severity= queue_severity; + m->print_severity= print_severity; + strncpy(m->print_id,print_id,80); + m->print_id[80]= 0; + return(1); +} + + +static int libdax_msgs_lock(struct libdax_msgs *m, int flag) +{ + +#ifndef LIBDAX_MSGS_SINGLE_THREADED + int ret; + + ret= pthread_mutex_lock(&(m->lock_mutex)); + if(ret!=0) + return(0); +#endif + + return(1); +} + + +static int libdax_msgs_unlock(struct libdax_msgs *m, int flag) +{ + +#ifndef LIBDAX_MSGS_SINGLE_THREADED + int ret; + + ret= pthread_mutex_unlock(&(m->lock_mutex)); + if(ret!=0) + return(0); +#endif + + return(1); +} + + +int libdax_msgs__text_to_sev(char *severity_name, int *severity, + int flag) +{ + if(strncmp(severity_name,"NEVER",5)==0) + *severity= LIBDAX_MSGS_SEV_NEVER; + else if(strncmp(severity_name,"ABORT",5)==0) + *severity= LIBDAX_MSGS_SEV_ABORT; + else if(strncmp(severity_name,"FATAL",5)==0) + *severity= LIBDAX_MSGS_SEV_FATAL; + else if(strncmp(severity_name,"SORRY",5)==0) + *severity= LIBDAX_MSGS_SEV_SORRY; + else if(strncmp(severity_name,"WARNING",7)==0) + *severity= LIBDAX_MSGS_SEV_WARNING; + else if(strncmp(severity_name,"HINT",4)==0) + *severity= LIBDAX_MSGS_SEV_HINT; + else if(strncmp(severity_name,"NOTE",4)==0) + *severity= LIBDAX_MSGS_SEV_NOTE; + else if(strncmp(severity_name,"UPDATE",6)==0) + *severity= LIBDAX_MSGS_SEV_UPDATE; + else if(strncmp(severity_name,"DEBUG",5)==0) + *severity= LIBDAX_MSGS_SEV_DEBUG; + else if(strncmp(severity_name,"ALL",3)==0) + *severity= LIBDAX_MSGS_SEV_ALL; + else { + *severity= LIBDAX_MSGS_SEV_NEVER; + return(0); + } + return(1); +} + + +int libdax_msgs__sev_to_text(int severity, char **severity_name, + int flag) +{ + if(flag&1) { + *severity_name= + "NEVER\nABORT\nFATAL\nSORRY\nWARNING\nHINT\nNOTE\nUPDATE\nDEBUG\nALL"; + return(1); + } + *severity_name= ""; + if(severity>=LIBDAX_MSGS_SEV_NEVER) + *severity_name= "NEVER"; + else if(severity>=LIBDAX_MSGS_SEV_ABORT) + *severity_name= "ABORT"; + else if(severity>=LIBDAX_MSGS_SEV_FATAL) + *severity_name= "FATAL"; + else if(severity>=LIBDAX_MSGS_SEV_SORRY) + *severity_name= "SORRY"; + else if(severity>=LIBDAX_MSGS_SEV_WARNING) + *severity_name= "WARNING"; + else if(severity>=LIBDAX_MSGS_SEV_HINT) + *severity_name= "HINT"; + else if(severity>=LIBDAX_MSGS_SEV_NOTE) + *severity_name= "NOTE"; + else if(severity>=LIBDAX_MSGS_SEV_UPDATE) + *severity_name= "UPDATE"; + else if(severity>=LIBDAX_MSGS_SEV_DEBUG) + *severity_name= "DEBUG"; + else if(severity>=LIBDAX_MSGS_SEV_ALL) + *severity_name= "ALL"; + else { + *severity_name= ""; + return(0); + } + return(1); +} + + +int libdax_msgs_submit(struct libdax_msgs *m, int driveno, int error_code, + int severity, int priority, char *msg_text, + int os_errno, int flag) +{ + int ret; + char *textpt,*sev_name,sev_text[81]; + struct libdax_msgs_item *item= NULL; + + if(severity >= m->print_severity) { + if(msg_text==NULL) + textpt= ""; + else + textpt= msg_text; + sev_text[0]= 0; + ret= libdax_msgs__sev_to_text(severity,&sev_name,0); + if(ret>0) + sprintf(sev_text,"%s : ",sev_name); + + fprintf(stderr,"%s%s%s\n",m->print_id,sev_text,textpt); + if(os_errno!=0) { + ret= libdax_msgs_lock(m,0); + if(ret<=0) + return(-1); + fprintf(stderr,"%s( Most recent system error: %d '%s' )\n", + m->print_id,os_errno,strerror(os_errno)); + libdax_msgs_unlock(m,0); + } + + } + if(severity < m->queue_severity) + return(0); + + ret= libdax_msgs_lock(m,0); + if(ret<=0) + return(-1); + ret= libdax_msgs_item_new(&item,m->youngest,0); + if(ret<=0) + goto failed; + item->driveno= driveno; + item->error_code= error_code; + item->severity= severity; + item->priority= priority; + if(msg_text!=NULL) { + item->msg_text= malloc(strlen(msg_text)+1); + if(item->msg_text==NULL) + goto failed; + strcpy(item->msg_text,msg_text); + } + item->os_errno= os_errno; + if(m->oldest==NULL) + m->oldest= item; + m->youngest= item; + m->count++; + libdax_msgs_unlock(m,0); + +/* +fprintf(stderr,"libdax_experimental: message submitted to queue (now %d)\n", + m->count); +*/ + + return(1); +failed:; + libdax_msgs_item_destroy(&item,0); + libdax_msgs_unlock(m,0); + return(-1); +} + + +int libdax_msgs_obtain(struct libdax_msgs *m, struct libdax_msgs_item **item, + int severity, int priority, int flag) +{ + int ret; + struct libdax_msgs_item *im, *next_im= NULL; + + *item= NULL; + ret= libdax_msgs_lock(m,0); + if(ret<=0) + return(-1); + for(im= m->oldest; im!=NULL; im= next_im) { + for(; im!=NULL; im= next_im) { + next_im= im->next; + if(im->severity>=severity) + break; + libdax_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0); + libdax_msgs_item_destroy(&im,0); /* severity too low: delete */ + } + if(im==NULL) + break; + if(im->priority>=priority) + break; + } + if(im==NULL) + {ret= 0; goto ex;} + libdax_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0); + *item= im; + ret= 1; +ex:; + libdax_msgs_unlock(m,0); + return(ret); +} + + +int libdax_msgs_destroy_item(struct libdax_msgs *m, + struct libdax_msgs_item **item, int flag) +{ + int ret; + + ret= libdax_msgs_lock(m,0); + if(ret<=0) + return(-1); + ret= libdax_msgs_item_destroy(item,0); + libdax_msgs_unlock(m,0); + return(ret); +} + diff --git a/libburn/tags/ZeroThreeZero/libburn/libdax_msgs.h b/libburn/tags/ZeroThreeZero/libburn/libdax_msgs.h new file mode 100644 index 00000000..c81bd45b --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/libdax_msgs.h @@ -0,0 +1,397 @@ + +/* libdax_msgs + Message handling facility of libdax. + Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL +*/ + + +/* + *Never* set this macro outside libdax_msgs.c ! + The entrails of the message handling facility are not to be seen by + the other library components or the applications. +*/ +#ifdef LIBDAX_MSGS_H_INTERNAL + + +#ifndef LIBDAX_MSGS_SINGLE_THREADED +#include <pthread.h> +#endif + + +struct libdax_msgs_item { + + double timestamp; + pid_t process_id; + int driveno; + + int severity; + int priority; + + /* Apply for your developer's error code range at + libburn-hackers@pykix.org + Report introduced codes in the list below. */ + int error_code; + + char *msg_text; + int os_errno; + + struct libdax_msgs_item *prev,*next; + +}; + + +struct libdax_msgs { + + struct libdax_msgs_item *oldest; + struct libdax_msgs_item *youngest; + int count; + + int queue_severity; + int print_severity; + char print_id[81]; + +#ifndef LIBDAX_MSGS_SINGLE_THREADED + pthread_mutex_t lock_mutex; +#endif + + +}; + +#endif /* LIBDAX_MSGS_H_INTERNAL */ + + +#ifndef LIBDAX_MSGS_H_INCLUDED +#define LIBDAX_MSGS_H_INCLUDED 1 + + +#ifndef LIBDAX_MSGS_H_INTERNAL + + + /* Public Opaque Handles */ + +/** A pointer to this is a opaque handle to a message handling facility */ +struct libdax_msgs; + +/** A pointer to this is a opaque handle to a single message item */ +struct libdax_msgs_item; + +#endif /* ! LIBDAX_MSGS_H_INTERNAL */ + + + /* Public Macros */ + + +/* Registered Severities */ + +/* It is well advisable to let applications select severities via strings and + forwarded functions libdax_msgs__text_to_sev(), libdax_msgs__sev_to_text(). + These macros are for use by libdax/libburn only. +*/ + +/** Use this to get messages of any severity. Do not use for submitting. +*/ +#define LIBDAX_MSGS_SEV_ALL 0x00000000 + +/** Debugging messages not to be visible to normal users by default +*/ +#define LIBDAX_MSGS_SEV_DEBUG 0x10000000 + +/** Update of a progress report about long running actions +*/ +#define LIBDAX_MSGS_SEV_UPDATE 0x20000000 + +/** Not so usual events which were gracefully handled +*/ +#define LIBDAX_MSGS_SEV_NOTE 0x30000000 + +/** Possibilities to achieve a better result +*/ +#define LIBDAX_MSGS_SEV_HINT 0x40000000 + +/** Warnings about problems which could not be handled optimally +*/ +#define LIBDAX_MSGS_SEV_WARNING 0x50000000 + +/** Non-fatal error messages indicating that parts of the action failed + but processing will/should go on +*/ +#define LIBDAX_MSGS_SEV_SORRY 0x60000000 + +/** An error message which puts the whole operation of libdax in question +*/ +#define LIBDAX_MSGS_SEV_FATAL 0x70000000 + +/** A message from an abort handler which will finally finish libburn +*/ +#define LIBDAX_MSGS_SEV_ABORT 0x71000000 + +/** A severity to exclude resp. discard any possible message. + Do not use this severity for submitting. +*/ +#define LIBDAX_MSGS_SEV_NEVER 0x7fffffff + + +/* Registered Priorities */ + +/* Priorities are to be used by libburn/libdax only. */ + +#define LIBDAX_MSGS_PRIO_ZERO 0x00000000 +#define LIBDAX_MSGS_PRIO_LOW 0x10000000 +#define LIBDAX_MSGS_PRIO_MEDIUM 0x20000000 +#define LIBDAX_MSGS_PRIO_HIGH 0x30000000 +#define LIBDAX_MSGS_PRIO_TOP 0x7ffffffe + +/* Do not use this priority for submitting */ +#define LIBDAX_MSGS_PRIO_NEVER 0x7fffffff + + + /* Public Functions */ + + /* Calls initiated from inside libdax/libburn */ + + +/** Create new empty message handling facility with queue. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return >0 success, <=0 failure +*/ +int libdax_msgs_new(struct libdax_msgs **m, int flag); + + +/** Destroy a message handling facility and all its eventual messages. + The submitted pointer gets set to NULL. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 for success, 0 for pointer to NULL +*/ +int libdax_msgs_destroy(struct libdax_msgs **m, int flag); + + +/** Submit a message to a message handling facility. + @param driveno libdax drive number. Use -1 if no number is known. + @param error_code Unique error code. Use only registered codes. See below. + The same unique error_code may be issued at different + occasions but those should be equivalent out of the view + of a libdax application. (E.g. "cannot open ATA drive" + versus "cannot open SCSI drive" would be equivalent.) + @param severity The LIBDAX_MSGS_SEV_* of the event. + @param priority The LIBDAX_MSGS_PRIO_* number of the event. + @param msg_text Printable and human readable message text. + @param os_errno Eventual error code from operating system (0 if none) + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 on success, 0 on rejection, <0 for severe errors +*/ +int libdax_msgs_submit(struct libdax_msgs *m, int driveno, int error_code, + int severity, int priority, char *msg_text, + int os_errno, int flag); + + + /* Calls from applications (to be forwarded by libdax/libburn) */ + + +/** Convert a registered severity number into a severity name + @param flag Bitfield for control purposes: + bit0= list all severity names in a newline separated string + @return >0 success, <=0 failure +*/ +int libdax_msgs__sev_to_text(int severity, char **severity_name, + int flag); + + +/** Convert a severity name into a severity number, + @param flag Bitfield for control purposes (unused yet, submit 0) + @return >0 success, <=0 failure +*/ +int libdax_msgs__text_to_sev(char *severity_name, int *severity, + int flag); + + +/** Set minimum severity for messages to be queued (default + LIBDAX_MSGS_SEV_ALL) and for messages to be printed directly to stderr + (default LIBDAX_MSGS_SEV_NEVER). + @param print_id A text of at most 80 characters to be printed before + any eventually printed message (default is "libdax: "). + @param flag Bitfield for control purposes (unused yet, submit 0) + @return always 1 for now +*/ +int libdax_msgs_set_severities(struct libdax_msgs *m, int queue_severity, + int print_severity, char *print_id, int flag); + + +/** Obtain a message item that has at least the given severity and priority. + Usually all older messages of lower severity are discarded then. If no + item of sufficient severity was found, all others are discarded from the + queue. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 if a matching item was found, 0 if not, <0 for severe errors +*/ +int libdax_msgs_obtain(struct libdax_msgs *m, struct libdax_msgs_item **item, + int severity, int priority, int flag); + + +/** Destroy a message item obtained by libdax_msgs_obtain(). The submitted + pointer gets set to NULL. + Caution: Copy eventually obtained msg_text before destroying the item, + if you want to use it further. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 for success, 0 for pointer to NULL, <0 for severe errors +*/ +int libdax_msgs_destroy_item(struct libdax_msgs *m, + struct libdax_msgs_item **item, int flag); + + +/** Obtain from a message item the three application oriented components as + submitted with the originating call of libdax_msgs_submit(). + Caution: msg_text becomes a pointer into item, not a copy. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 on success, 0 on invalid item, <0 for servere errors +*/ +int libdax_msgs_item_get_msg(struct libdax_msgs_item *item, + int *error_code, char **msg_text, int *os_errno, + int flag); + + +/** Obtain from a message item the submitter identification submitted + with the originating call of libdax_msgs_submit(). + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 on success, 0 on invalid item, <0 for servere errors +*/ +int libdax_msgs_item_get_origin(struct libdax_msgs_item *item, + double *timestamp, pid_t *process_id, int *driveno, + int flag); + + +/** Obtain from a message item severity and priority as submitted + with the originating call of libdax_msgs_submit(). + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 on success, 0 on invalid item, <0 for servere errors +*/ +int libdax_msgs_item_get_rank(struct libdax_msgs_item *item, + int *severity, int *priority, int flag); + + +#ifdef LIDBAX_MSGS_________________ + + + /* Registered Error Codes */ + + +Format: error_code (LIBDAX_MSGS_SEV_*,LIBDAX_MSGS_PRIO_*) = explanation +If no severity or priority are fixely associates, use "(,)". + +------------------------------------------------------------------------------ +Range "libdax_msgs" : 0x00000000 to 0x0000ffff + + 0x00000000 (ALL,ZERO) = Initial setting in new libdax_msgs_item + 0x00000001 (DEBUG,ZERO) = Test error message + 0x00000002 (DEBUG,ZERO) = Debugging message + + +------------------------------------------------------------------------------ +Range "elmom" : 0x00010000 to 0x0001ffff + + + +------------------------------------------------------------------------------ +Range "scdbackup" : 0x00020000 to 0x0002ffff + + Acessing and defending drives: + + 0x00020001 (SORRY,LOW) = Cannot open busy device + 0x00020002 (SORRY,HIGH) = Encountered error when closing drive + 0x00020003 (SORRY,HIGH) = Could not grab drive + 0x00020004 (NOTE,HIGH) = Opened O_EXCL scsi sibling + 0x00020005 (FATAL,HIGH) = Failed to open device + 0x00020006 (FATAL,HIGH) = Too many scsi siblings + 0x00020007 (NOTE,HIGH) = Closed O_EXCL scsi siblings + + General library operations: + + 0x00020101 (WARNING,HIGH) = Cannot find given worker item + 0x00020102 (SORRY,HIGH) = A drive operation is still going on + 0x00020103 (WARNING,HIGH) = After scan a drive operation is still going on + 0x00020104 (SORRY,HIGH) = NULL pointer caught + 0x00020105 (SORRY,HIGH) = Drive is already released + 0x00020106 (SORRY,HIGH) = Drive is busy on attempt to close + 0x00020107 (SORRY,HIGH) = Drive is busy on attempt to shut down library + 0x00020108 (SORRY,HIGH) = Drive is not grabbed on disc status inquiry + 0x00020108 (FATAL,HIGH) = Could not allocate new drive object + 0x00020109 (FATAL,HIGH) = Library not running + 0x0002010a (FATAL,HIGH) = Unsuitable track mode + 0x0002010b (FATAL,HIGH) = Burn run failed + 0x0002010c (FATAL,HIGH) = Failed to transfer command to drive + 0x0002010d (DEBUG,HIGH) = Could not inquire TOC + 0x0002010e (FATAL,HIGH) = Attempt to read ATIP from ungrabbed drive + 0x0002010f (DEBUG,HIGH) = SCSI error condition on command + 0x00020110 (FATAL,HIGH) = Persistent drive address too long + 0x00020111 (FATAL,HIGH) = Could not allocate new auxiliary object + 0x00020112 (SORRY,HIGH) = Bad combination of write_type and block_type + 0x00020113 (FATAL,HIGH) = Drive capabilities not inquired yet + 0x00020114 (SORRY,HIGH) = Attempt to set ISRC with bad data + 0x00020115 (SORRY,HIGH) = Attempt to set track mode to unusable value + 0x00020116 (FATAL,HIGH) = Track mode has unusable value + 0x00020117 (FATAL,HIGH) = toc_entry of drive is already in use + 0x00020118 (DEBUG,HIGH) = Closing track + 0x00020119 (DEBUG,HIGH) = Closing session + 0x0002011a (NOTE,HIGH) = Padding up track to minimum size + 0x0002011b (FATAL,HIGH) = Attempt to read track info from ungrabbed drive + 0x0002011c (FATAL,HIGH) = Attempt to read track info from busy drive + 0x0002011d (FATAL,HIGH) = SCSI error on write + 0x0002011e (SORRY,HIGH) = Unsuitable media detected + 0x0002011f (SORRY,HIGH) = Burning is restricted to a single track + 0x00020120 (NOTE,HIGH) = FORMAT UNIT ignored + 0x00020121 (FATAL,HIGH) = Write preparation setup failed + 0x00020122 (FATAL,HIGH) = SCSI error on format_unit + 0x00020123 (SORRY,HIGH) = DVD Media are unsuitable for desired track type + 0x00020124 (SORRY,HIGH) = SCSI error on set_streaming + 0x00020125 (SORRY,HIGH) = Write start address not supported + 0x00020126 (SORRY,HIGH) = Write start address not properly aligned + 0x00020127 (NOTE,HIGH) = Write start address is ... + 0x00020128 (FATAL,HIGH) = Unsupported inquiry_type with mmc_get_performance + 0x00020129 (SORRY,HIGH) = Will not format media type + 0x00020130 (SORRY,HIGH) = Drive and media state unsuitable for blanking + 0x00020131 (SORRY,HIGH) = No suitable formatting type offered by drive + + + libdax_audioxtr: + 0x00020200 (SORRY,HIGH) = Cannot open audio source file + 0x00020201 (SORRY,HIGH) = Audio source file has unsuitable format + 0x00020202 (SORRY,HIGH) = Failed to prepare reading of audio data + + +------------------------------------------------------------------------------ + +#endif /* LIDBAX_MSGS_________________ */ + + + +#ifdef LIBDAX_MSGS_H_INTERNAL + + /* Internal Functions */ + + +/** Lock before doing side effect operations on m */ +static int libdax_msgs_lock(struct libdax_msgs *m, int flag); + +/** Unlock after effect operations on m are done */ +static int libdax_msgs_unlock(struct libdax_msgs *m, int flag); + + +/** Create new empty message item. + @param link Previous item in queue + @param flag Bitfield for control purposes (unused yet, submit 0) + @return >0 success, <=0 failure +*/ +static int libdax_msgs_item_new(struct libdax_msgs_item **item, + struct libdax_msgs_item *link, int flag); + +/** Destroy a message item obtained by libdax_msgs_obtain(). The submitted + pointer gets set to NULL. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 for success, 0 for pointer to NULL +*/ +static int libdax_msgs_item_destroy(struct libdax_msgs_item **item, int flag); + + +#endif /* LIBDAX_MSGS_H_INTERNAL */ + + +#endif /* ! LIBDAX_MSGS_H_INCLUDED */ diff --git a/libburn/tags/ZeroThreeZero/libburn/mmc.c b/libburn/tags/ZeroThreeZero/libburn/mmc.c new file mode 100644 index 00000000..4b02b7b6 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/mmc.c @@ -0,0 +1,1731 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* ts A61009 */ +/* #include <a ssert.h> */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <pthread.h> +#include "error.h" +#include "sector.h" +#include "libburn.h" +#include "transport.h" +#include "mmc.h" +#include "spc.h" +#include "drive.h" +#include "debug.h" +#include "toc.h" +#include "structure.h" +#include "options.h" + + +#ifdef Libburn_log_in_and_out_streaM +/* <<< ts A61031 */ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#endif /* Libburn_log_in_and_out_streaM */ + + +/* ts A61005 */ +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + + +/* ts A61219 : Based on knowlege from dvd+rw-tools-7.0 and mmc5r03c.pdf */ +#define Libburn_support_dvd_plus_rW 1 + +/* ts A61229 */ +#define Libburn_support_dvd_minusrw_overW 1 + +/* ts A70112 */ +#define Libburn_support_dvd_raM 1 + + +/* Progress report (with Libburn_support_dvd_plus_rW defined): + ts A61219 : It seems to work with a used (i.e. thoroughly formatted) DVD+RW. + Error messages of class DEBUG appear because of inability to + read TOC or track info. Nevertheless, the written images verify. + ts A61220 : Burned to a virgin DVD+RW by help of new mmc_format_unit() + (did not test wether it would work without). Burned to a + not completely formatted DVD+RW. (Had worked before without + mmc_format_unit() but i did not exceed the formatted range + as reported by dvd+rw-mediainfo.) + ts A61221 : Speed setting now works for both of my drives. The according + functions in dvd+rw-tools are a bit intimidating to the reader. + I hope it is possible to leave much of this to the drive. + And if it fails ... well, it's only speed setting. :)) + ts A61229 : Burned to several DVD-RW formatted to mode Restricted Overwrite + by dvd+rw-format. Needs Libburn_support_dvd_minusrw_overW. + ts A61230 : Other than growisofs, libburn does not send a mode page 5 for + such DVD-RW (which the MMC-5 standard does deprecate) and it + really seems to work without such a page. + ts A70101 : Formatted DVD-RW media. Success is varying with media, but + dvd+rw-format does not do better with the same media. + ts A70112 : Support for writing to DVD-RAM. + +Todo: + Determine first free lba for appending data. +*/ + + +static unsigned char MMC_GET_TOC[] = { 0x43, 2, 2, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_GET_ATIP[] = { 0x43, 2, 4, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_GET_DISC_INFO[] = + { 0x51, 0, 0, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_READ_CD[] = { 0xBE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_BLANK[] = { 0xA1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_SEND_OPC[] = { 0x54, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_SET_SPEED[] = + { 0xBB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_WRITE_12[] = + { 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_WRITE_10[] = { 0x2A, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* ts A61201 : inserted 0, before 16, */ +static unsigned char MMC_GET_CONFIGURATION[] = + { 0x46, 0, 0, 0, 0, 0, 0, 16, 0, 0 }; + +static unsigned char MMC_SYNC_CACHE[] = { 0x35, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_GET_EVENT[] = { 0x4A, 1, 0, 0, 16, 0, 0, 0, 8, 0 }; +static unsigned char MMC_CLOSE[] = { 0x5B, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_TRACK_INFO[] = { 0x52, 0, 0, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_SEND_CUE_SHEET[] = + { 0x5D, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* ts A61023 : get size and free space of drive buffer */ +static unsigned char MMC_READ_BUFFER_CAPACITY[] = + { 0x5C, 0, 0, 0, 0, 0, 0, 16, 0, 0 }; + +/* ts A61219 : format DVD+RW (and various others) */ +static unsigned char MMC_FORMAT_UNIT[] = { 0x04, 0x11, 0, 0, 0, 0 }; + +/* ts A61221 : + To set speed for DVD media (0xBB is for CD but works on my LG GSA drive) */ +static unsigned char MMC_SET_STREAMING[] = + { 0xB6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +/* ts A61225 : + To obtain write speed descriptors (command can do other things too) */ +static unsigned char MMC_GET_PERFORMANCE[] = + { 0xAC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +/* ts A70108 : To obtain info about drive and media formatting opportunities */ +static unsigned char MMC_READ_FORMAT_CAPACITIES[] = + { 0x23, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + +static int mmc_function_spy_do_tell = 0; + +int mmc_function_spy(char * text) +{ + + if (mmc_function_spy_do_tell) + fprintf(stderr,"libburn: experimental: mmc_function_spy: %s\n", + text); + return 1; +} + +int mmc_function_spy_ctrl(int do_tell) +{ + mmc_function_spy_do_tell= !!do_tell; + return 1; +} + + +void mmc_send_cue_sheet(struct burn_drive *d, struct cue_sheet *s) +{ + struct buffer buf; + struct command c; + + + mmc_function_spy("mmc_send_cue_sheet"); + c.retry = 1; + c.oplen = sizeof(MMC_SEND_CUE_SHEET); + memcpy(c.opcode, MMC_SEND_CUE_SHEET, sizeof(MMC_SEND_CUE_SHEET)); + c.page = &buf; + c.page->bytes = s->count * 8; + c.page->sectors = 0; + c.opcode[6] = (c.page->bytes >> 16) & 0xFF; + c.opcode[7] = (c.page->bytes >> 8) & 0xFF; + c.opcode[8] = c.page->bytes & 0xFF; + c.dir = TO_DRIVE; + memcpy(c.page->data, s->data, c.page->bytes); + d->issue_command(d, &c); +} + +/* ts A61110 : added parameters trackno, lba, nwa. Redefined return value. + @return 1=nwa is valid , 0=nwa is not valid , -1=error */ +int mmc_get_nwa(struct burn_drive *d, int trackno, int *lba, int *nwa) +{ + struct buffer buf; + struct command c; + unsigned char *data; + + mmc_function_spy("mmc_get_nwa"); + c.retry = 1; + c.oplen = sizeof(MMC_TRACK_INFO); + memcpy(c.opcode, MMC_TRACK_INFO, sizeof(MMC_TRACK_INFO)); + c.opcode[1] = 1; + if(trackno<=0) { + if (d->current_profile == 0x1a || d->current_profile == 0x13 || + d->current_profile == 0x12 ) + /* DVD+RW , DVD-RW restricted overwrite , DVD-RAM */ + c.opcode[5] = 1; + else /* mmc5r03c.pdf: valid only for CD, DVD+R, DVD+R DL */ + c.opcode[5] = 0xFF; + } else + c.opcode[5] = trackno; + c.page = &buf; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + data = c.page->data; + + *lba = (data[8] << 24) + (data[9] << 16) + + (data[10] << 8) + data[11]; + *nwa = (data[12] << 24) + (data[13] << 16) + + (data[14] << 8) + data[15]; + if (d->current_profile == 0x1a || d->current_profile == 0x13) { + /* DVD+RW or DVD-RW restricted overwrite */ + *lba = *nwa = 0; + } else if (!(data[7]&1)) { + /* ts A61106 : MMC-1 Table 142 : NWA_V = NWA Valid Flag */ + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + "mmc_get_nwa: Track Info Block: NWA_V == 0", 0, 0); + return 0; + } + return 1; +} + +/* ts A61009 : function is obviously unused. */ +/* void mmc_close_disc(struct burn_drive *d, struct burn_write_opts *o) */ +void mmc_close_disc(struct burn_write_opts *o) +{ + struct burn_drive *d; + + mmc_function_spy("mmc_close_disc"); + + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + "HOW THAT ? mmc_close_disc() was called", 0, 0); + + /* ts A61009 : made impossible by removing redundant parameter d */ + /* a ssert(o->drive == d); */ + d = o->drive; + + o->multi = 0; + spc_select_write_params(d, o); + mmc_close(d, 1, 0); +} + +/* ts A61009 : function is obviously unused. */ +/* void mmc_close_session(struct burn_drive *d, struct burn_write_opts *o) */ +void mmc_close_session(struct burn_write_opts *o) +{ + struct burn_drive *d; + + mmc_function_spy("mmc_close_session"); + + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + "HOW THAT ? mmc_close_session() was called", 0, 0); + + /* ts A61009 : made impossible by removing redundant parameter d */ + /* a ssert(o->drive == d); */ + d = o->drive; + + o->multi = 3; + spc_select_write_params(d, o); + mmc_close(d, 1, 0); +} + +void mmc_close(struct burn_drive *d, int session, int track) +{ + struct command c; + + mmc_function_spy("mmc_close"); + + c.retry = 1; + c.oplen = sizeof(MMC_CLOSE); + memcpy(c.opcode, MMC_CLOSE, sizeof(MMC_CLOSE)); + + /* ts A61030 : shifted !!session rather than or-ing plain session */ + c.opcode[2] = ((!!session)<<1) | !!track; + + c.opcode[4] = track >> 8; + c.opcode[5] = track & 0xFF; + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void mmc_get_event(struct burn_drive *d) +{ + struct buffer buf; + struct command c; + + mmc_function_spy("mmc_get_event"); + c.retry = 1; + c.oplen = sizeof(MMC_GET_EVENT); + memcpy(c.opcode, MMC_GET_EVENT, sizeof(MMC_GET_EVENT)); + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + burn_print(12, "0x%x:0x%x:0x%x:0x%x\n", + c.page->data[0], c.page->data[1], c.page->data[2], + c.page->data[3]); + burn_print(12, "event: %d:%d:%d:%d\n", c.page->data[4], + c.page->data[5], c.page->data[6], c.page->data[7]); +} + + +void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf) +{ + struct command c; + int len; + + mmc_function_spy("mmc_write_12"); + len = buf->sectors; + + /* ts A61009 */ + /* a ssert(buf->bytes >= buf->sectors);*/ /* can be == at 0... */ + + burn_print(100, "trying to write %d at %d\n", len, start); + memcpy(c.opcode, MMC_WRITE_12, sizeof(MMC_WRITE_12)); + c.retry = 1; + c.oplen = sizeof(MMC_WRITE_12); + c.opcode[2] = start >> 24; + c.opcode[3] = (start >> 16) & 0xFF; + c.opcode[4] = (start >> 8) & 0xFF; + c.opcode[5] = start & 0xFF; + c.opcode[6] = len >> 24; + c.opcode[7] = (len >> 16) & 0xFF; + c.opcode[8] = (len >> 8) & 0xFF; + c.opcode[9] = len & 0xFF; + c.page = buf; + c.dir = TO_DRIVE; + + d->issue_command(d, &c); +} + +int mmc_write(struct burn_drive *d, int start, struct buffer *buf) +{ + int cancelled; + struct command c; + int len; + +#ifdef Libburn_log_in_and_out_streaM + /* <<< ts A61031 */ + static int tee_fd= -1; + if(tee_fd==-1) + tee_fd= open("/tmp/libburn_sg_written", + O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR); +#endif /* Libburn_log_in_and_out_streaM */ + + mmc_function_spy("mmc_write"); + pthread_mutex_lock(&d->access_lock); + cancelled = d->cancel; + pthread_mutex_unlock(&d->access_lock); + + if (cancelled) + return BE_CANCELLED; + + len = buf->sectors; + + /* ts A61009 : buffer fill problems are to be handled by caller */ + /* a ssert(buf->bytes >= buf->sectors);*/ /* can be == at 0... */ + + burn_print(100, "trying to write %d at %d\n", len, start); + memcpy(c.opcode, MMC_WRITE_10, sizeof(MMC_WRITE_10)); + c.retry = 1; + c.oplen = sizeof(MMC_WRITE_10); + c.opcode[2] = start >> 24; + c.opcode[3] = (start >> 16) & 0xFF; + c.opcode[4] = (start >> 8) & 0xFF; + c.opcode[5] = start & 0xFF; + c.opcode[6] = 0; + c.opcode[7] = (len >> 8) & 0xFF; + c.opcode[8] = len & 0xFF; + c.page = buf; + c.dir = TO_DRIVE; +/* + burn_print(12, "%d, %d, %d, %d - ", c->opcode[2], c->opcode[3], c->opcode[4], c->opcode[5]); + burn_print(12, "%d, %d, %d, %d\n", c->opcode[6], c->opcode[7], c->opcode[8], c->opcode[9]); +*/ + +#ifdef Libburn_log_in_and_out_streaM + /* <<< ts A61031 */ + if(tee_fd!=-1) { + write(tee_fd,c.page->data,len*2048); + } +#endif /* Libburn_log_in_and_out_streaM */ + + d->issue_command(d, &c); + + /* ts A61112 : react on eventual error condition */ + if (c.error && c.sense[2]!=0) { + + /* >>> make this scsi_notify_error() when liberated */ + if (c.sense[2]!=0) { + char msg[160]; + sprintf(msg, + "SCSI error on write(%d,%d): key=%X asc=%2.2Xh ascq=%2.2Xh", + start, len, + c.sense[2],c.sense[12],c.sense[13]); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002011d, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + } + pthread_mutex_lock(&d->access_lock); + d->cancel = 1; + pthread_mutex_unlock(&d->access_lock); + return BE_CANCELLED; + } + + return 0; +} + +void mmc_read_toc(struct burn_drive *d) +{ +/* read full toc, all sessions, in m/s/f form, 4k buffer */ + struct burn_track *track; + struct burn_session *session; + struct buffer buf; + struct command c; + int dlen; + int i, bpl= 12; + unsigned char *tdata; + + mmc_function_spy("mmc_read_toc"); + memcpy(c.opcode, MMC_GET_TOC, sizeof(MMC_GET_TOC)); + c.retry = 1; + c.oplen = sizeof(MMC_GET_TOC); + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + if (c.error) { + + /* ts A61020 : this snaps on non-blank DVD media */ + /* ts A61106 : also snaps on CD with unclosed track/session */ + /* Very unsure wether this old measure is ok. + Obviously higher levels do not care about this. + outdated info: DVD+RW burns go on after passing through here. + + d->busy = BURN_DRIVE_IDLE; + */ + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002010d, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + "Could not inquire TOC (non-blank DVD media ?)", 0,0); + d->status = BURN_DISC_UNSUITABLE; + d->toc_entries = 0; + /* Prefering memory leaks over fandangos */ + d->toc_entry = malloc(sizeof(struct burn_toc_entry)); + + return; + } + + dlen = c.page->data[0] * 256 + c.page->data[1]; + d->toc_entries = (dlen - 2) / 11; +/* + some drives fail this check. + + ts A61007 : if re-enabled then not via Assert. + a ssert(((dlen - 2) % 11) == 0); +*/ + d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry)); + tdata = c.page->data + 4; + + burn_print(12, "TOC:\n"); + + d->disc = burn_disc_create(); + + for (i = 0; i < c.page->data[3]; i++) { + session = burn_session_create(); + burn_disc_add_session(d->disc, session, BURN_POS_END); + burn_session_free(session); + } + + /* ts A61022 */ + burn_print(bpl, "-----------------------------------\n"); + + for (i = 0; i < d->toc_entries; i++, tdata += 11) { + + /* ts A61022: was burn_print level 12 */ + burn_print(bpl, "S %d, PT %2.2Xh, TNO %d :", tdata[0],tdata[3], + tdata[2]); + burn_print(bpl, " MSF(%d:%d:%d)", tdata[4],tdata[5],tdata[6]); + burn_print(bpl, " PMSF(%d:%d:%d %d)", + tdata[8], tdata[9], tdata[10], + burn_msf_to_lba(tdata[8], tdata[9], tdata[10]) + ); + burn_print(bpl, " - control %d, adr %d\n", tdata[1] & 0xF, + tdata[1] >> 4); + +/* + fprintf(stderr, "libburn_experimental: toc entry #%d : %d %d %d\n",i,tdata[8], tdata[9], tdata[10]); +*/ + + if (tdata[3] == 1) { + if (burn_msf_to_lba(tdata[8], tdata[9], tdata[10])) { + d->disc->session[0]->hidefirst = 1; + track = burn_track_create(); + burn_session_add_track(d->disc-> + session[tdata[0] - 1], + track, BURN_POS_END); + burn_track_free(track); + + } + } + if (tdata[0] <= 0 || tdata[0] > d->disc->sessions) + tdata[0] = d->disc->sessions; + if (tdata[3] < 100 && tdata[0] > 0) { + track = burn_track_create(); + burn_session_add_track(d->disc->session[tdata[0] - 1], + track, BURN_POS_END); + track->entry = &d->toc_entry[i]; + burn_track_free(track); + } + d->toc_entry[i].session = tdata[0]; + d->toc_entry[i].adr = tdata[1] >> 4; + d->toc_entry[i].control = tdata[1] & 0xF; + d->toc_entry[i].tno = tdata[2]; + d->toc_entry[i].point = tdata[3]; + d->toc_entry[i].min = tdata[4]; + d->toc_entry[i].sec = tdata[5]; + d->toc_entry[i].frame = tdata[6]; + d->toc_entry[i].zero = tdata[7]; + d->toc_entry[i].pmin = tdata[8]; + d->toc_entry[i].psec = tdata[9]; + d->toc_entry[i].pframe = tdata[10]; + if (tdata[3] == 0xA0) + d->disc->session[tdata[0] - 1]->firsttrack = tdata[8]; + if (tdata[3] == 0xA1) + d->disc->session[tdata[0] - 1]->lasttrack = tdata[8]; + if (tdata[3] == 0xA2) + d->disc->session[tdata[0] - 1]->leadout_entry = + &d->toc_entry[i]; + } + + /* ts A61022 */ + burn_print(bpl, "-----------------------------------\n"); + + if (d->status != BURN_DISC_APPENDABLE) + d->status = BURN_DISC_FULL; + toc_find_modes(d); +} + +void mmc_read_disc_info(struct burn_drive *d) +{ + struct buffer buf; + unsigned char *data; + struct command c; + char msg[160]; + + /* ts A61020 */ + d->start_lba = d->end_lba = -2000000000; + d->erasable = 0; + + /* ts A61202 */ + d->toc_entries = 0; + if (d->status == BURN_DISC_EMPTY) + return; + + mmc_get_configuration(d); + if ((d->current_profile != 0 || d->status != BURN_DISC_UNREADY) + && ! d->current_is_supported_profile) { + if (!d->silent_on_scsi_error) { + sprintf(msg, + "Unsuitable media detected. Profile %4.4Xh %s", + d->current_profile, d->current_profile_text); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002011e, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + } + d->status = BURN_DISC_UNSUITABLE; + return; + } + + mmc_function_spy("mmc_read_disc_info"); + memcpy(c.opcode, MMC_GET_DISC_INFO, sizeof(MMC_GET_DISC_INFO)); + c.retry = 1; + c.oplen = sizeof(MMC_GET_DISC_INFO); + c.page = &buf; + c.page->sectors = 0; + c.page->bytes = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + if (c.error) { + d->busy = BURN_DRIVE_IDLE; + return; + } + + data = c.page->data; + d->erasable = !!(data[2] & 16); + +/* + fprintf(stderr, "libburn_experimental: data[2]= %d 0x%x\n", + (unsigned) data[2], (unsigned) data[2]); +*/ + switch (data[2] & 3) { + case 0: + d->toc_entries = 0; + d->start_lba = burn_msf_to_lba(data[17], data[18], data[19]); + d->end_lba = burn_msf_to_lba(data[21], data[22], data[23]); + +/* + fprintf(stderr, "libburn_experimental: start_lba = %d (%d %d %d) , end_lba = %d (%d %d %d)\n", + d->start_lba, data[17], data[18], data[19], + d->end_lba, data[21], data[22], data[23]); +*/ + + d->status = BURN_DISC_BLANK; + break; + case 1: + d->status = BURN_DISC_APPENDABLE; + case 2: + if (d->current_profile == -1 || d->current_is_cd_profile) + mmc_read_toc(d); + break; + } + + /* >>> ts A61217 : Note for future + growisofs performs OPC if (data[0]<<8)|data[1]<=32 + which indicates no OPC entries are attached to the + reply from the drive. + */ + + /* ts A61219 : mmc5r03c.pdf 6.22.3.1.13 BG Format Status + 0=blank (not yet started) + 1=started but neither running nor complete + 2=in progress + 3=completed + */ + d->bg_format_status = data[7] & 3; + + /* Preliminarily declare blank: + ts A61219 : DVD+RW (is not bg_format_status==0 "blank") + ts A61229 : same for DVD-RW Restricted overwrite + ts A70112 : same for DVD-RAM + */ + if (d->current_profile == 0x1a || d->current_profile == 0x13 || + d->current_profile == 0x12) + d->status = BURN_DISC_BLANK; +} + +void mmc_read_atip(struct burn_drive *d) +{ + struct buffer buf; + struct command c; + + /* ts A61021 */ + unsigned char *data; + /* Speed values from A1: + With 4 cdrecord tells "10" or "8" where MMC-1 says "8". + cdrecord "8" appear on 4xCD-RW and thus seem to be quite invalid. + My CD-R (>=24 speed) tell no A1. + The higher non-MMC-1 values are hearsay. + */ + /* 0, 2, 4, 6, 10, -, 16, -, */ + static int speed_value[16]= { 0, 353, 706, 1059, 1764, -5, 2824, -7, + 4234, 5646, 7056, 8468, -12, -13, -14, -15}; + /* 24, 32, 40, 48, -, -, -, - */ + + mmc_function_spy("mmc_read_atip"); + memcpy(c.opcode, MMC_GET_ATIP, sizeof(MMC_GET_ATIP)); + c.retry = 1; + c.oplen = sizeof(MMC_GET_ATIP); + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + burn_print(1, "atip shit for you\n"); + + + /* ts A61021 */ + data = c.page->data; + d->erasable= !!(data[6]&64); + d->start_lba= burn_msf_to_lba(data[8],data[9],data[10]); + d->end_lba= burn_msf_to_lba(data[12],data[13],data[14]); + if (data[6]&4) { + if (speed_value[(data[16]>>4)&7] > 0) { + d->mdata->min_write_speed = + speed_value[(data[16]>>4)&7]; + if (speed_value[(data[16])&15] <= 0) + d->mdata->max_write_speed = + speed_value[(data[16]>>4)&7]; + } + if (speed_value[(data[16])&15] > 0) { + d->mdata->max_write_speed = + speed_value[(data[16])&15]; + if (speed_value[(data[16]>>4)&7] <= 0) + d->mdata->min_write_speed = + speed_value[(data[16])&15]; + } + } + +#ifdef Burn_mmc_be_verbous_about_atiP + { int i; + fprintf(stderr,"libburn_experimental: Returned ATIP Data\n"); + for(i= 0; i<28; i++) + fprintf(stderr,"%3.3d (0x%2.2x)%s", + data[i],data[i],((i+1)%5 ? " ":"\n")); + fprintf(stderr,"\n"); + + fprintf(stderr, + "libburn_experimental: Indicative Target Writing Power= %d\n", + (data[4]>>4)&7); + fprintf(stderr, + "libburn_experimental: Reference speed= %d ->%d\n", + data[4]&7, speed_value[data[4]&7]); + fprintf(stderr, + "libburn_experimental: Is %sunrestricted\n", + (data[5]&64?"":"not ")); + fprintf(stderr, + "libburn_experimental: Is %serasable, sub-type %d\n", + (data[6]&64?"":"not "),(data[6]>>3)&3); + fprintf(stderr, + "libburn_experimental: lead in: %d (%-2.2d:%-2.2d/%-2.2d)\n", + burn_msf_to_lba(data[8],data[9],data[10]), + data[8],data[9],data[10]); + fprintf(stderr, + "libburn_experimental: lead out: %d (%-2.2d:%-2.2d/%-2.2d)\n", + burn_msf_to_lba(data[12],data[13],data[14]), + data[12],data[13],data[14]); + if(data[6]&4) + fprintf(stderr, + "libburn_experimental: A1 speed low %d speed high %d\n", + speed_value[(data[16]>>4)&7], speed_value[(data[16])&7]); + if(data[6]&2) + fprintf(stderr, + "libburn_experimental: A2 speed low %d speed high %d\n", + speed_value[(data[20]>>4)&7], speed_value[(data[20])&7]); + if(data[6]&1) + fprintf(stderr, + "libburn_experimental: A3 speed low %d speed high %d\n", + speed_value[(data[24]>>4)&7], speed_value[(data[24])&7]); + } + +#endif /* Burn_mmc_be_verbous_about_atiP */ + +/* ts A61020 +http://www.t10.org/ftp/t10/drafts/mmc/mmc-r10a.pdf , table 77 : + + 0 ATIP Data Length MSB + 1 ATIP Data Length LSB + 2 Reserved + 3 Reserved + 4 bit7=1, bit4-6="Indicative Target Writing Power", bit3=reserved , + bit0-2="Reference speed" + 5 bit7=0, bit6="URU" , bit0-5=reserved + 6 bit7=1, bit6="Disc Type", bit3-4="Disc Sub-Type", + bit2="A1", bit1="A2", bit0="A3" + 7 reserved + 8 ATIP Start Time of lead-in (Min) + 9 ATIP Start Time of lead-in (Sec) +10 ATIP Start Time of lead-in (Frame) +11 reserved +12 ATIP Last Possible Start Time of lead-out (Min) +13 ATIP Last Possible Start Time of lead-out (Sec) +14 ATIP Last Possible Start Time of lead-out (Frame) +15 reserved +16 bit7=0, bit4-6="Lowest Usable CLV Recording speed" + bit0-3="Highest Usable CLV Recording speed" +17 bit7=0, bit4-6="Power Multiplication Factor p", + bit1-3="Target y value of the Modulation/Power function", bit0=reserved +18 bit7=1, bit4-6="Recommended Erase/Write Power Ratio (P(inf)/W(inf))" + bit0-3=reserved +19 reserved +20-22 A2 Values +23 reserved +24-26 A3 Values +27 reserved + +Disc Type - zero indicates CD-R media; one indicates CD-RW media. + +Disc Sub-Type - shall be set to zero. + +A1 - when set to one, indicates that bytes 16-18 are valid. + +Lowest Usable CLV Recording Speed +000b Reserved +001b 2X +010b - 111b Reserved + +Highest CLV Recording Speeds +000b Reserved +001b 2X +010b 4X +011b 6X +100b 8X +101b - 111b Reserved + +MMC-3 seems to recommend MODE SENSE (5Ah) page 2Ah rather than A1, A2, A3. +This page is loaded in libburn function spc_sense_caps() . +Speed is given in kbytes/sec there. But i suspect this to be independent +of media. So one would habe to associate the speed descriptor blocks with +the ATIP media characteristics ? How ? + +*/ +} + +void mmc_read_sectors(struct burn_drive *d, + int start, + int len, + const struct burn_read_opts *o, struct buffer *buf) +{ + int temp; + int errorblock, req; + struct command c; + + mmc_function_spy("mmc_read_sectors"); + + /* ts A61009 : to be ensured by callers */ + /* a ssert(len >= 0); */ + +/* if the drive isn't busy, why the hell are we here? */ + /* ts A61006 : i second that question */ + /* a ssert(d->busy); */ + + burn_print(12, "reading %d from %d\n", len, start); + memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD)); + c.retry = 1; + c.oplen = sizeof(MMC_READ_CD); + temp = start; + c.opcode[5] = temp & 0xFF; + temp >>= 8; + c.opcode[4] = temp & 0xFF; + temp >>= 8; + c.opcode[3] = temp & 0xFF; + temp >>= 8; + c.opcode[2] = temp & 0xFF; + c.opcode[8] = len & 0xFF; + len >>= 8; + c.opcode[7] = len & 0xFF; + len >>= 8; + c.opcode[6] = len & 0xFF; + req = 0xF8; + + /* ts A61106 : LG GSA-4082B dislikes this. key=5h asc=24h ascq=00h + + if (d->busy == BURN_DRIVE_GRABBING || o->report_recovered_errors) + req |= 2; + */ + + c.opcode[10] = 0; +/* always read the subcode, throw it away later, since we don't know + what we're really reading +*/ + if (d->busy == BURN_DRIVE_GRABBING || (o->subcodes_audio) + || (o->subcodes_data)) + c.opcode[10] = 1; + + c.opcode[9] = req; + c.page = buf; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + if (c.error) { + burn_print(12, "got an error over here\n"); + burn_print(12, "%d, %d, %d, %d\n", c.sense[3], c.sense[4], + c.sense[5], c.sense[6]); + errorblock = + (c.sense[3] << 24) + (c.sense[4] << 16) + + (c.sense[5] << 8) + c.sense[6]; + c.page->sectors = errorblock - start + 1; + burn_print(1, "error on block %d\n", errorblock); + burn_print(12, "error on block %d\n", errorblock); + burn_print(12, "returning %d sectors\n", c.page->sectors); + } +} + +void mmc_erase(struct burn_drive *d, int fast) +{ + struct command c; + + mmc_function_spy("mmc_erase"); + memcpy(c.opcode, MMC_BLANK, sizeof(MMC_BLANK)); + c.opcode[1] = 16; /* IMMED set to 1 */ + c.opcode[1] |= !!fast; + c.retry = 1; + c.oplen = sizeof(MMC_BLANK); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void mmc_read_lead_in(struct burn_drive *d, struct buffer *buf) +{ + int len; + struct command c; + + mmc_function_spy("mmc_read_lead_in"); + len = buf->sectors; + memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD)); + c.retry = 1; + c.oplen = sizeof(MMC_READ_CD); + c.opcode[5] = 0; + c.opcode[4] = 0; + c.opcode[3] = 0; + c.opcode[2] = 0xF0; + c.opcode[8] = 1; + c.opcode[7] = 0; + c.opcode[6] = 0; + c.opcode[9] = 0; + c.opcode[10] = 2; + c.page = buf; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); +} + +void mmc_perform_opc(struct burn_drive *d) +{ + struct command c; + + mmc_function_spy("mmc_perform_opc"); + memcpy(c.opcode, MMC_SEND_OPC, sizeof(MMC_SEND_OPC)); + c.retry = 1; + c.oplen = sizeof(MMC_SEND_OPC); + c.opcode[1] = 1; + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + + +/* ts A61221 : Learned much from dvd+rw-tools-7.0 set_speed_B6h() but then + made own experiments on base of mmc5r03c.pdf 6.8.3 and 6.39 in the hope + to achieve a leaner solution */ +int mmc_set_streaming(struct burn_drive *d, int r_speed, int w_speed) +{ + struct buffer buf; + struct command c; + int b, end_lba; + char msg[160]; + unsigned char *pd; + + mmc_function_spy("mmc_set_streaming"); + + if (r_speed <= 0) + r_speed = 0x10000000; /* ~ 2 TB/s */ + if (w_speed <= 0) + w_speed = 0x10000000; /* ~ 2 TB/s */ + c.retry = 1; + c.oplen = sizeof(MMC_SET_STREAMING); + memcpy(c.opcode, MMC_SET_STREAMING, sizeof(MMC_SET_STREAMING)); + c.page = &buf; + c.page->bytes = 28; + c.opcode[9] = (c.page->bytes >> 8) & 0xff; + c.opcode[10] = c.page->bytes & 0xff; + c.page->sectors = 0; + c.dir = TO_DRIVE; + memset(c.page->data, 0, c.page->bytes); + pd = c.page->data; + + /* Trying to avoid inquiry of available speed descriptors but rather + to allow the drive to use the liberties of Exact==0. + */ + pd[0] = 0; /* WRC=0 (Default Rotation Control), RDD=Exact=RA=0 */ + + /* Default computed from 4.7e9 */ + end_lba = 2294921 - 1; + if (d->mdata->max_end_lba > 0) + end_lba = d->mdata->max_end_lba - 1; + + sprintf(msg, "mmc_set_streaming: end_lba=%d , r=%d , w=%d", + end_lba, r_speed, w_speed); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + + /* start_lba is 0 , 1000 = 1 second as base time for data rate */ + for (b = 0; b < 4 ; b++) { + pd[8+b] = (end_lba >> (24 - 8 * b)) & 0xff; + pd[12+b] = (r_speed >> (24 - 8 * b)) & 0xff; + pd[16+b] = (1000 >> (24 - 8 * b)) & 0xff; + pd[20+b] = (w_speed >> (24 - 8 * b)) & 0xff; + pd[24+b] = (1000 >> (24 - 8 * b)) & 0xff; + } + +/* <<< + fprintf(stderr,"LIBBURN_EXPERIMENTAL : B6h Performance descriptor:\n"); + for (b = 0; b < 28 ; b++) + fprintf(stderr, "%2.2X%c", pd[b], ((b+1)%4 ? ' ' : '\n')); +*/ + + + d->issue_command(d, &c); + if (c.error) { + if (c.sense[2]!=0 && !d->silent_on_scsi_error) { + sprintf(msg, + "SCSI error on set_streaming(%d): key=%X asc=%2.2Xh ascq=%2.2Xh", + w_speed, + c.sense[2],c.sense[12],c.sense[13]); + libdax_msgs_submit(libdax_messenger, + d->global_index, + 0x00020124, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + } + return 0; + } + return 1; +} + + +void mmc_set_speed(struct burn_drive *d, int r, int w) +{ + struct command c; + int ret; + + mmc_function_spy("mmc_set_speed"); + + /* ts A61221 : try to set DVD speed via command B6h */ + if (strstr(d->current_profile_text, "DVD") == d->current_profile_text){ + ret = mmc_set_streaming(d, r, w); + if (ret != 0) + return; /* success or really fatal failure */ + } + + /* ts A61112 : MMC standards prescribe FFFFh as max speed. + But libburn.h prescribes 0. */ + if (r<=0 || r>0xffff) + r = 0xffff; + if (w<=0 || w>0xffff) + w = 0xffff; + + memcpy(c.opcode, MMC_SET_SPEED, sizeof(MMC_SET_SPEED)); + c.retry = 1; + c.oplen = sizeof(MMC_SET_SPEED); + c.opcode[2] = r >> 8; + c.opcode[3] = r & 0xFF; + c.opcode[4] = w >> 8; + c.opcode[5] = w & 0xFF; + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + + +/* ts A61201 */ +static char *mmc_obtain_profile_name(int profile_number) +{ + static char *texts[0x53] = {NULL}; + int i, max_pno = 0x53; + + if (texts[0] == NULL) { + for (i = 0; i<max_pno; i++) + texts[i] = ""; + /* mmc5r03c.pdf , Table 89, Spelling: guessed cdrecord style */ + texts[0x01] = "Non-removable disk"; + texts[0x02] = "Removable disk"; + texts[0x03] = "MO erasable"; + texts[0x04] = "Optical write once"; + texts[0x05] = "AS-MO"; + texts[0x08] = "CD-ROM"; + texts[0x09] = "CD-R"; + texts[0x0a] = "CD-RW"; + texts[0x10] = "DVD-ROM"; + texts[0x11] = "DVD-R sequential recording"; + texts[0x12] = "DVD-RAM"; + texts[0x13] = "DVD-RW restricted overwrite"; + texts[0x14] = "DVD-RW sequential recording"; + texts[0x15] = "DVD-R/DL sequential recording"; + texts[0x16] = "DVD-R/DL layer jump recording"; + texts[0x1a] = "DVD+RW"; + texts[0x1b] = "DVD+R"; + texts[0x2a] = "DVD+RW/DL"; + texts[0x2b] = "DVD+R/DL"; + texts[0x40] = "BD-ROM"; + texts[0x41] = "BD-R sequential recording"; + texts[0x42] = "BD-R random recording"; + texts[0x43] = "BD-RE"; + texts[0x50] = "HD-DVD-ROM"; + texts[0x51] = "HD-DVD-R"; + texts[0x52] = "HD-DVD-RAM"; + } + if (profile_number<0 || profile_number>=max_pno) + return ""; + return texts[profile_number]; +} + + +/* ts A61201 : found in unfunctional state */ +void mmc_get_configuration(struct burn_drive *d) +{ + struct buffer buf; + int len, cp; + struct command c; + + d->current_profile = 0; + d->current_profile_text[0] = 0; + d->current_is_cd_profile = 0; + d->current_is_supported_profile = 0; + + mmc_function_spy("mmc_get_configuration"); + memcpy(c.opcode, MMC_GET_CONFIGURATION, sizeof(MMC_GET_CONFIGURATION)); + c.retry = 1; + c.oplen = sizeof(MMC_GET_CONFIGURATION); + c.page = &buf; + c.page->sectors = 0; + c.page->bytes = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + if (c.error) + return; + len = (c.page->data[0] << 24) + + (c.page->data[1] << 16) + + (c.page->data[2] << 8) + + c.page->data[3]; + + if (len<8) + return; + cp = (c.page->data[6]<<8) | c.page->data[7]; + d->current_profile = cp; + strcpy(d->current_profile_text, mmc_obtain_profile_name(cp)); + if (cp == 0x08 || cp == 0x09 || cp == 0x0a) + d->current_is_supported_profile = d->current_is_cd_profile = 1; + +#ifdef Libburn_support_dvd_plus_rW + if (cp == 0x1a) + d->current_is_supported_profile = 1; +#endif +#ifdef Libburn_support_dvd_minusrw_overW + if (cp == 0x13) + d->current_is_supported_profile = 1; +#endif +#ifdef Libburn_support_dvd_raM + if (cp == 0x12) + d->current_is_supported_profile = 1; +#endif + + /* >>> see mmc5r03c.pdf 5.2 + Interpret list of profile and feature descriptors. + */ + +} + + +/* ts A70108 */ +/* mmc5r03c.pdf 6.24 */ +int mmc_read_format_capacities(struct burn_drive *d, int top_wanted) +{ + struct buffer buf; + int len, type, score, num_descr, max_score = -2000000000, i, sign = 1; + off_t size; + struct command c; + unsigned char *dpt; + char msg[160]; + + mmc_function_spy("mmc_read_format_capacities"); + + d->format_descr_type = 3; + d->format_curr_max_size = 0; + d->format_curr_blsas = 0; + d->best_format_type = -1; + d->best_format_size = 0; + + memcpy(c.opcode, MMC_READ_FORMAT_CAPACITIES, + sizeof(MMC_GET_CONFIGURATION)); + c.retry = 1; + c.oplen = sizeof(MMC_READ_FORMAT_CAPACITIES); + c.opcode[7]= 0x02; + c.opcode[8]= 0x00; /* accept 512 bytes (not more than 260 possible) */ + c.page = &buf; + c.page->sectors = 0; + c.page->bytes = 0; + c.dir = FROM_DRIVE; + + d->issue_command(d, &c); + if (c.error) + return 0; + + len = c.page->data[3]; + if (len<8) + return 0; + + dpt = c.page->data + 4; + /* decode 6.24.3.2 Current/Maximum Capacity Descriptor */ + d->format_descr_type = dpt[4] & 3; + d->format_curr_max_size = (((off_t) dpt[0]) << 24) + + (dpt[1] << 16) + (dpt[2] << 8) + dpt[3]; + d->format_curr_blsas = (dpt[5] << 16) + (dpt[6] << 8) + dpt[7]; + + sprintf(msg, + "Current/Maximum Capacity Descriptor : type = %d : %.f", + d->format_descr_type, (double) d->format_curr_max_size); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + + d->format_curr_max_size *= (off_t) 2048; + + if (top_wanted == 0x00 || top_wanted == 0x10) + sign = -1; /* the caller clearly desires full format */ + + /* 6.24.3.3 Formattable Capacity Descriptors */ + num_descr = (len - 8) / 8; + for (i = 0; i < num_descr; i++) { + dpt = c.page->data + 12 + 8 * i; + size = (((off_t) dpt[0]) << 24) + + (dpt[1] << 16) + (dpt[2] << 8) + dpt[3]; + size *= (off_t) 2048; + type = dpt[4] >> 2; + + if (i < 32) { + d->format_descriptors[i].type = type; + d->format_descriptors[i].size = size; + d->format_descriptors[i].tdp = + (dpt[5] << 16) + (dpt[6] << 8) + dpt[7]; + d->num_format_descr = i + 1; + } + + sprintf(msg, "Capacity Descriptor %2.2Xh %.fs = %.1f MB",type, + ((double) size)/2048.0, ((double) size)/1024.0/1024.0); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + + /* Criterion is proximity to quick intermediate state */ + if (type == 0x00) { /* full format (with lead out) */ + score = 1 * sign; + } else if (type == 0x10) { /* DVD-RW full format */ + score = 10 * sign; + } else if(type == 0x13) { /* DVD-RW quick grow last session */ + score = 100 * sign; + } else if(type == 0x15) { /* DVD-RW Quick */ + score = 50 * sign; + } else { + continue; + } + if (type == top_wanted) + score += 1000000000; + if (score > max_score) { + d->best_format_type = type; + d->best_format_size = size; + max_score = score; + } + } + + sprintf(msg, + "best_format_type = %2.2Xh , best_format_size = %.f", + d->best_format_type, (double) d->best_format_size); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + + return 1; +} + + +void mmc_sync_cache(struct burn_drive *d) +{ + struct command c; + + mmc_function_spy("mmc_sync_cache"); + memcpy(c.opcode, MMC_SYNC_CACHE, sizeof(MMC_SYNC_CACHE)); + c.retry = 1; + c.oplen = sizeof(MMC_SYNC_CACHE); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + + +/* ts A61023 : http://libburn.pykix.org/ticket/14 + get size and free space of drive buffer +*/ +int mmc_read_buffer_capacity(struct burn_drive *d) +{ + struct buffer buf; + struct command c; + unsigned char *data; + + mmc_function_spy("mmc_read_buffer_capacity"); + memcpy(c.opcode, MMC_READ_BUFFER_CAPACITY, + sizeof(MMC_READ_BUFFER_CAPACITY)); + c.retry = 1; + c.oplen = sizeof(MMC_READ_BUFFER_CAPACITY); + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + /* >>> ??? error diagnostics */ + + data = c.page->data; + + d->progress.buffer_capacity = + (data[4]<<24)|(data[5]<<16)|(data[6]<<8)|data[7]; + d->progress.buffer_available = + (data[8]<<24)|(data[9]<<16)|(data[10]<<8)|data[11]; + if (d->progress.buffered_bytes >= d->progress.buffer_capacity){ + double fill; + + fill = d->progress.buffer_capacity + - d->progress.buffer_available; + if (fill < d->progress.buffer_min_fill && fill>=0) + d->progress.buffer_min_fill = fill; + } + return 1; +} + + +/* ts A61219 : learned much from dvd+rw-tools-7.0: plus_rw_format() + and mmc5r03c.pdf, 6.5 FORMAT UNIT */ +/* + @param size The size (in bytes) to be sent with the FORMAT comand + @param flag bit1= insist in size 0 even if there is a better default known + bit2= format to maximum available size + bit3= expand format up to at least size + bit4= enforce re-format of (partly) formatted media + bit7= bit8 to bit15 contain the index of the format to use + bit8-bit15 = see bit7 +*/ +int mmc_format_unit(struct burn_drive *d, off_t size, int flag) +{ + struct buffer buf; + struct command c; + int ret, tolerate_failure = 0, return_immediately = 0, i, format_type; + int index; + off_t num_of_blocks = 0, diff; + char msg[160],descr[80]; + int full_format_type = 0x00; /* Full Format (or 0x10 for DVD-RW ?) */ + + mmc_function_spy("mmc_format_unit"); + c.retry = 1; + c.oplen = sizeof(MMC_FORMAT_UNIT); + memcpy(c.opcode, MMC_FORMAT_UNIT, sizeof(MMC_FORMAT_UNIT)); + c.page = &buf; + c.page->bytes = 12; + c.page->sectors = 0; + c.dir = TO_DRIVE; + memset(c.page->data, 0, c.page->bytes); + + descr[0] = 0; + c.page->data[1] = 0x02; /* Immed */ + c.page->data[3] = 8; /* Format descriptor length */ + num_of_blocks = size / 2048; + for (i = 0; i < 4; i++) + c.page->data[4 + i] = (num_of_blocks >> (24 - 8 * i)) & 0xff; + if (flag & 128) { /* explicitely chosen format descriptor */ + /* use case: the app knows what to do */ + + ret = mmc_read_format_capacities(d, -1); + if (ret <= 0) + goto selected_not_suitable; + index = (flag >> 8) & 0xff; + if(index < 0 || index > d->num_format_descr) { +selected_not_suitable:; + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020132, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Selected format is not suitable for libburn", + 0, 0); + return 0; + } + if (!(d->current_profile == 0x13 || + d->current_profile == 0x14 || + d->current_profile == 0x1a)) + goto unsuitable_media; + + format_type = d->format_descriptors[index].type; + if (!(format_type == 0x00 || format_type == 0x10 || + format_type == 0x11 || format_type == 0x13 || + format_type == 0x15 || format_type == 0x26)) + goto selected_not_suitable; + if (flag & 4) { + num_of_blocks = + d->format_descriptors[index].size / 2048; + for (i = 0; i < 4; i++) + c.page->data[4 + i] = + (num_of_blocks >> (24 - 8 * i)) & 0xff; + } + if (format_type != 0x26) + for (i = 0; i < 3; i++) + c.page->data[9 + i] = + ( d->format_descriptors[index].tdp >> + (16 - 8 * i)) & 0xff; + sprintf(descr, "%s (bit7)", d->current_profile_text); + return_immediately = 1; /* caller must do the waiting */ + + } else if (d->current_profile == 0x1a) { /* DVD+RW */ + /* use case: background formatting during write !(flag&4) + de-icing as explicit formatting action (flag&4) + */ + + /* mmc5r03c.pdf , 6.5.4.2.14, DVD+RW Basic Format */ + format_type = 0x26; + + if ((size <= 0 && !(flag & 2)) || (flag & (4 | 8))) { + /* maximum capacity */ + memset(c.page->data + 4, 0xff, 4); + num_of_blocks = 0xffffffff; + } + + if(d->bg_format_status == 2 || + (d->bg_format_status == 3 && !(flag & 16))) { + sprintf(msg,"FORMAT UNIT ignored. Already %s.", + (d->bg_format_status == 2 ? "in progress" : + "completed")); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020120, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + return 2; + } + if (!(flag & 16)) /* if not re-format is desired */ + if (d->bg_format_status == 1) /* is partly formatted */ + c.page->data[11] = 1; /* Restart bit */ + sprintf(descr, "DVD+RW (fs=%d,rs=%d)", + d->bg_format_status, (c.page->data[11] == 1)); + if (flag & 4) + return_immediately = 1;/* caller must do the waiting */ + + } else if (d->current_profile == 0x13 && !(flag & 16)) { + /*DVD-RW restricted overwrite*/ + /* use case: quick grow formatting during write */ + + ret = mmc_read_format_capacities(d, 0x13); + if (ret > 0) { + if (d->best_format_type == 0x13) { + if (d->best_format_size <= 0) + return 1; + } else { + if (d->format_descr_type == 2) /* formatted */ + return 1; + if (d->format_descr_type == 3){/*intermediate*/ + d->dvd_minus_rw_incomplete = 1; + return 1; + } + /* does trying make sense at all ? */ + tolerate_failure = 1; + } + } + if (d->best_format_type == 0x13 && (flag & (4 | 8))) { + num_of_blocks = d->best_format_size / 2048; + if (flag & 8) { + /* num_of_blocks needed to reach size */ + diff = (size - d->format_curr_max_size) /32768; + if ((size - d->format_curr_max_size) % 32768) + diff++; + diff *= 16; + if (diff < num_of_blocks) + num_of_blocks = diff; + } + if (num_of_blocks > 0) + for (i = 0; i < 4; i++) + c.page->data[4 + i] = + (num_of_blocks >> (24 - 8 * i)) & 0xff; + } + /* 6.5.4.2.8 , DVD-RW Quick Grow Last Border */ + format_type = 0x13; + c.page->data[11] = 16; /* block size * 2k */ + sprintf(descr, "DVD-RW quick grow"); + + } else if (d->current_profile == 0x14 || + (d->current_profile == 0x13 && (flag & 16))) { + /* DVD-RW sequential recording (or Overwrite for re-format) */ + /* use case : transition from Sequential to Overwrite + re-formatting of Overwrite media */ + + /* To Restricted Overwrite */ + /* 6.5.4.2.10 Format Type = 15h (DVD-RW Quick) */ + /* or 6.5.4.2.1 Format Type = 00h (Full Format) */ + /* or 6.5.4.2.5 Format Type = 10h (DVD-RW Full Format) */ + mmc_read_format_capacities(d, + (flag & 4) ? full_format_type : 0x15); + if (d->best_format_type == 0x15 || + d->best_format_type == full_format_type) { + if ((flag & 4) + || d->best_format_type == full_format_type) { + num_of_blocks = d->best_format_size / 2048; + for (i = 0; i < 4; i++) + c.page->data[4 + i] = + (num_of_blocks >> (24 - 8 * i)) & 0xff; + } + + } else { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020131, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "No suitable formatting type offered by drive", + 0, 0); + return 0; + } + format_type = d->best_format_type; + c.page->data[11] = 16; /* block size * 2k */ + sprintf(descr, "DVD-RW %s", + format_type == 0x15 ? "quick" : "full"); + return_immediately = 1; /* caller must do the waiting */ + + } else { + + /* >>> other formattable types to come */ +unsuitable_media:; + sprintf(msg, "Unsuitable media detected. Profile %4.4Xh %s", + d->current_profile, d->current_profile_text); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002011e, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return 0; + } + c.page->data[8] = format_type << 2; + + sprintf(msg, "Format type %2.2Xh \"%s\", blocks = %.f\n", + format_type, descr, (double) num_of_blocks); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + + d->issue_command(d, &c); + if (c.error && !tolerate_failure) { + if (c.sense[2]!=0) { + sprintf(msg, + "SCSI error on format_unit(%s): key=%X asc=%2.2Xh ascq=%2.2Xh", + descr, + c.sense[2],c.sense[12],c.sense[13]); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020122, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + } + return 0; + } else if ((!c.error) && (format_type == 0x13 || format_type == 0x15)) + d->dvd_minus_rw_incomplete = 1; + if (return_immediately) + return 1; + usleep(1000000); /* there seems to be a little race condition */ + for (ret = 0; ret <= 0 ;) { + usleep(50000); + ret = spc_test_unit_ready(d); + } + mmc_sync_cache(d); + return 1; +} + + +/* ts A61225 */ +int mmc_get_write_performance(struct burn_drive *d) +{ + struct buffer buf; + int len, i, b, max_descr, num_descr, ret; + int exact_bit, read_speed, write_speed; + /* if this call delivers usable data then they should override + previously recorded min/max speed and not compete with them */ + int min_write_speed = 0x7fffffff, max_write_speed = 0; + int min_read_speed = 0x7fffffff, max_read_speed = 0; + struct command c; + unsigned long end_lba; + unsigned char *pd; + struct burn_speed_descriptor *sd; + + /* A61225 : 1 = report about speed descriptors */ + static int speed_debug = 0; + + mmc_function_spy("mmc_get_write_performance"); + + if (d->current_profile <= 0) + mmc_get_configuration(d); + + memcpy(c.opcode, MMC_GET_PERFORMANCE, sizeof(MMC_GET_PERFORMANCE)); + max_descr = ( BUFFER_SIZE - 8 ) / 16 - 1; + + /* >>> future: maintain a list of write descriptors + if (max_descr > d->max_write_descr - d->num_write_descr) + max_descr = d->max_write_descr; + */ + + c.opcode[8] = ( max_descr >> 8 ) & 0xff; + c.opcode[9] = ( max_descr >> 0 ) & 0xff; + c.opcode[10] = 3; + c.retry = 1; + c.oplen = sizeof(MMC_GET_PERFORMANCE); + c.page = &buf; + c.page->sectors = 0; + c.page->bytes = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + if (c.error) + return 0; + len = (c.page->data[0] << 24) + + (c.page->data[1] << 16) + + (c.page->data[2] << 8) + + c.page->data[3]; + if (len<12) + return 0; + + pd = c.page->data; + num_descr = ( len - 4 ) / 16; + if (num_descr > max_descr) + num_descr = max_descr; + for (i = 0; i < num_descr; i++) { + exact_bit = !!(pd[8 + i*16] & 2); + end_lba = read_speed = write_speed = 0; + for (b = 0; b < 4 ; b++) { + end_lba += pd[8 + i*16 + 4 + b] << (24 - 8 * b); + read_speed += pd[8 + i*16 + 8 + b] << (24 - 8 * b); + write_speed += pd[8 + i*16 + 12 + b] << (24 - 8 * b); + } + if (end_lba > 0x7ffffffe) + end_lba = 0x7ffffffe; + + if (speed_debug) + fprintf(stderr, + "LIBBURN_DEBUG: kB/s: write=%d read=%d end=%lu exact=%d\n", + write_speed, read_speed, end_lba, exact_bit); + + /* ts A61226 */ + ret = burn_speed_descriptor_new(&(d->mdata->speed_descriptors), + NULL, d->mdata->speed_descriptors, 0); + if (ret > 0) { + sd = d->mdata->speed_descriptors; + sd->source = 2; + if (d->current_profile > 0) { + sd->profile_loaded = d->current_profile; + strcpy(sd->profile_name, + d->current_profile_text); + } + sd->wrc = (pd[8 + i*16] >> 3 ) & 3; + sd->exact = exact_bit; + sd->mrw = pd[8 + i*16] & 1; + sd->end_lba = end_lba; + sd->write_speed = write_speed; + sd->read_speed = read_speed; + } + + if (end_lba > d->mdata->max_end_lba) + d->mdata->max_end_lba = end_lba; + if (end_lba < d->mdata->min_end_lba) + d->mdata->min_end_lba = end_lba; + if (write_speed < min_write_speed) + min_write_speed = write_speed; + if (write_speed > max_write_speed) + max_write_speed = write_speed; + if (read_speed < min_read_speed) + min_read_speed = read_speed; + if (read_speed > max_read_speed) + max_read_speed = read_speed; + } + if (min_write_speed < 0x7fffffff) + d->mdata->min_write_speed = min_write_speed; + if (max_write_speed > 0) + d->mdata->max_write_speed = max_write_speed; + /* there is no mdata->min_read_speed yet + if (min_read_speed < 0x7fffffff) + d->mdata->min_read_speed = min_read_speed; + */ + if (max_read_speed > 0) + d->mdata->max_read_speed = max_read_speed; + return num_descr; +} + + +/* ts A61021 : the mmc specific part of sg.c:enumerate_common() +*/ +int mmc_setup_drive(struct burn_drive *d) +{ + d->read_atip = mmc_read_atip; + d->read_toc = mmc_read_toc; + d->write = mmc_write; + d->erase = mmc_erase; + d->read_sectors = mmc_read_sectors; + d->perform_opc = mmc_perform_opc; + d->set_speed = mmc_set_speed; + d->send_cue_sheet = mmc_send_cue_sheet; + d->sync_cache = mmc_sync_cache; + d->get_nwa = mmc_get_nwa; + d->close_disc = mmc_close_disc; + d->close_session = mmc_close_session; + d->close_track_session = mmc_close; + d->read_buffer_capacity = mmc_read_buffer_capacity; + d->format_unit = mmc_format_unit; + d->read_format_capacities = mmc_read_format_capacities; + + /* ts A61020 */ + d->start_lba = -2000000000; + d->end_lba = -2000000000; + + /* ts A61201 */ + d->erasable = 0; + d->current_profile = -1; + d->current_profile_text[0] = 0; + d->current_is_cd_profile = 0; + d->current_is_supported_profile = 0; + d->dvd_minus_rw_incomplete = 0; + d->bg_format_status = -1; + d->num_format_descr = 0; + + return 1; +} + + +/* ts A61229 : outsourced from spc_select_write_params() */ +/* Note: Page data is not zeroed here to allow preset defaults. Thus + memset(pd, 0, 2 + d->mdata->write_page_length); + is the eventual duty of the caller. +*/ +int mmc_compose_mode_page_5(struct burn_drive *d, + const struct burn_write_opts *o, + unsigned char *pd) +{ + pd[0] = 5; + pd[1] = d->mdata->write_page_length; + + /* ts A61229 */ + if (d->current_profile == 0x13) { /* DVD-RW restricted overwrite */ + /* learned from transport.hxx : page05_setup() + and mmc3r10g.pdf table 347 */ + /* BUFE (burnproof), no LS_V (i.e. default Link Size, i hope), + no simulate, write type 0 = packet */ + pd[2] = (1 << 6); + /* no multi, fixed packet, track mode 5 */ + pd[3] = (1 << 5) | 5; + /* Data Block Type */ + pd[4] = 8; + /* Link size dummy */ + pd[5] = 0; + } else { + /* Traditional setup for CD */ + pd[2] = ((!!o->underrun_proof) << 6) + | ((!!o->simulate) << 4) + | (o->write_type & 0x0f); + + /* ts A61106 : MMC-1 table 110 : multi==0 or multi==3 */ + pd[3] = ((3 * !!o->multi) << 6) | (o->control & 0x0f); + + pd[4] = spc_block_type(o->block_type); + + /* ts A61104 */ + if(!(o->control&4)) /* audio (MMC-1 table 61) */ + if(o->write_type == BURN_WRITE_TAO) + pd[4] = 0; /* Data Block Type: Raw Data */ + + pd[14] = 0; /* audio pause length MSB */ + pd[15] = 150; /* audio pause length LSB */ + +/*XXX need session format! */ +/* ts A61229 : but session format (pd[8]) = 0 seems ok */ + + } + return 1; +} + diff --git a/libburn/tags/ZeroThreeZero/libburn/mmc.h b/libburn/tags/ZeroThreeZero/libburn/mmc.h new file mode 100644 index 00000000..df0938b8 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/mmc.h @@ -0,0 +1,72 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __MMC +#define __MMC + +struct burn_drive; +struct burn_write_opts; +struct command; +struct buffer; +struct cue_sheet; + +/* MMC commands */ + +void mmc_read(struct burn_drive *); + +/* ts A61009 : removed redundant parameter d in favor of o->drive */ +/* void mmc_close_session(struct burn_drive *, struct burn_write_opts *); */ +/* void mmc_close_disc(struct burn_drive *, struct burn_write_opts *); */ +void mmc_close_session(struct burn_write_opts *o); +void mmc_close_disc(struct burn_write_opts *o); + +void mmc_close(struct burn_drive *, int session, int track); +void mmc_get_event(struct burn_drive *); +int mmc_write(struct burn_drive *, int start, struct buffer *buf); +void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf); +void mmc_sync_cache(struct burn_drive *); +void mmc_load(struct burn_drive *); +void mmc_eject(struct burn_drive *); +void mmc_erase(struct burn_drive *, int); +void mmc_read_toc(struct burn_drive *); +void mmc_read_disc_info(struct burn_drive *); +void mmc_read_atip(struct burn_drive *); +void mmc_read_sectors(struct burn_drive *, + int, + int, const struct burn_read_opts *, struct buffer *); +void mmc_set_speed(struct burn_drive *, int, int); +void mmc_read_lead_in(struct burn_drive *, struct buffer *); +void mmc_perform_opc(struct burn_drive *); +void mmc_get_configuration(struct burn_drive *); + +/* ts A61110 : added parameters trackno, lba, nwa. Redefined return value. + @return 1=nwa is valid , 0=nwa is not valid , -1=error */ +int mmc_get_nwa(struct burn_drive *d, int trackno, int *lba, int *nwa); + +void mmc_send_cue_sheet(struct burn_drive *, struct cue_sheet *); + +/* ts A61023 : get size and free space of drive buffer */ +int mmc_read_buffer_capacity(struct burn_drive *d); + +/* ts A61021 : the mmc specific part of sg.c:enumerate_common() +*/ +int mmc_setup_drive(struct burn_drive *d); + +/* ts A61219 : learned much from dvd+rw-tools-7.0: plus_rw_format() + and mmc5r03c.pdf, 6.5 FORMAT UNIT */ +int mmc_format_unit(struct burn_drive *d, off_t size, int flag); + +/* ts A61225 : obtain write speed descriptors via ACh GET PERFORMANCE */ +int mmc_get_write_performance(struct burn_drive *d); + + +/* ts A61229 : outsourced from spc_select_write_params() */ +/* Note: Page data is not zeroed here to allow preset defaults. Thus + memset(pd, 0, 2 + d->mdata->write_page_length); + is the eventual duty of the caller. +*/ +int mmc_compose_mode_page_5(struct burn_drive *d, + const struct burn_write_opts *o, + unsigned char *pd); + + +#endif /*__MMC*/ diff --git a/libburn/tags/ZeroThreeZero/libburn/null.c b/libburn/tags/ZeroThreeZero/libburn/null.c new file mode 100644 index 00000000..415e0a49 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/null.c @@ -0,0 +1,27 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "null.h" +#include "libburn.h" +#include <stdlib.h> + +#include <string.h> +int null_read(struct burn_source *source, unsigned char *buffer, int size) +{ + memset(buffer, 0, size); + return size; +} + +struct burn_source *burn_null_source_new(void) +{ + struct burn_source *src; + + src = malloc(sizeof(struct burn_source)); + src->refcount = 1; + src->read = null_read; + src->read_sub = NULL; + + src->get_size = 0; + src->free_data = NULL; + src->data = NULL; + return src; +} diff --git a/libburn/tags/ZeroThreeZero/libburn/null.h b/libburn/tags/ZeroThreeZero/libburn/null.h new file mode 100644 index 00000000..1a7aae33 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/null.h @@ -0,0 +1,10 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__NULL_H +#define BURN__NULL_H + +struct burn_source; +int null_read(struct burn_source *source, unsigned char *buffer, int size); +struct burn_source *burn_null_source_new(void); + +#endif /* LIBBURN__NULL_H */ diff --git a/libburn/tags/ZeroThreeZero/libburn/options.c b/libburn/tags/ZeroThreeZero/libburn/options.c new file mode 100644 index 00000000..59c926c8 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/options.c @@ -0,0 +1,219 @@ +#include "libburn.h" +#include "options.h" +#include "transport.h" + +/* ts A61007 */ +/* #include <a ssert.h> */ + +#include <stdlib.h> +#include <string.h> + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + + +struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive) +{ + struct burn_write_opts *opts; + + opts = malloc(sizeof(struct burn_write_opts)); + if (opts == NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020111, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Could not allocate new auxiliary object", 0, 0); + return NULL; + } + opts->drive = drive; + opts->refcount = 1; + opts->write_type = BURN_WRITE_TAO; + opts->block_type = BURN_BLOCK_MODE1; + opts->toc_entry = NULL; + opts->toc_entries = 0; + opts->simulate = 0; + opts->underrun_proof = drive->mdata->underrun_proof; + opts->perform_opc = 1; + opts->obs = -1; + opts->obs_pad = 0; + opts->start_byte = -1; + opts->has_mediacatalog = 0; + opts->format = BURN_CDROM; + opts->multi = 0; + opts->control = 0; + return opts; +} + +void burn_write_opts_free(struct burn_write_opts *opts) +{ + if (--opts->refcount <= 0) + free(opts); +} + +struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive) +{ + struct burn_read_opts *opts; + + opts = malloc(sizeof(struct burn_read_opts)); + opts->drive = drive; + opts->refcount = 1; + opts->raw = 0; + opts->c2errors = 0; + opts->subcodes_audio = 0; + opts->subcodes_data = 0; + opts->hardware_error_recovery = 0; + opts->report_recovered_errors = 0; + opts->transfer_damaged_blocks = 0; + opts->hardware_error_retries = 3; + + return opts; +} + +void burn_read_opts_free(struct burn_read_opts *opts) +{ + if (--opts->refcount <= 0) + free(opts); +} + +int burn_write_opts_set_write_type(struct burn_write_opts *opts, + enum burn_write_types write_type, + int block_type) +{ + int sector_get_outmode(enum burn_write_types write_type, + enum burn_block_types block_type); + int spc_block_type(enum burn_block_types b); + + /* ts A61007 */ + if (! ( (write_type == BURN_WRITE_SAO && block_type == BURN_BLOCK_SAO) + || (opts->drive->block_types[write_type] & block_type) ) ) { +bad_combination:; + libdax_msgs_submit(libdax_messenger, -1, 0x00020112, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Bad combination of write_type and block_type", 0, 0); + return 0; + } + /* ts A61007 : obsoleting Assert in sector.c:get_outmode() */ + if (sector_get_outmode(write_type, (enum burn_block_types) block_type) + == -1) + goto bad_combination; + /* ts A61007 : obsoleting Assert in spc.c:spc_block_type() */ + if (spc_block_type((enum burn_block_types) block_type) == -1) + goto bad_combination; + + opts->write_type = write_type; + opts->block_type = block_type; + return 1; + + /* a ssert(0); */ +} + +void burn_write_opts_set_toc_entries(struct burn_write_opts *opts, int count, + struct burn_toc_entry *toc_entries) +{ + opts->toc_entries = count; + opts->toc_entry = malloc(count * sizeof(struct burn_toc_entry)); + memcpy(opts->toc_entry, &toc_entries, + sizeof(struct burn_toc_entry) * count); +} + +void burn_write_opts_set_format(struct burn_write_opts *opts, int format) +{ + opts->format = format; +} + +int burn_write_opts_set_simulate(struct burn_write_opts *opts, int sim) +{ + if (opts->drive->mdata->simulate) { + opts->simulate = sim; + return 1; + } + return 0; +} + +int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts, + int underrun_proof) +{ + if (opts->drive->mdata->underrun_proof) { + opts->underrun_proof = underrun_proof; + return 1; + } + return 0; +} + +void burn_write_opts_set_perform_opc(struct burn_write_opts *opts, int opc) +{ + opts->perform_opc = opc; +} + +void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts, + int has_mediacatalog) +{ + opts->has_mediacatalog = has_mediacatalog; +} + +void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, + unsigned char mediacatalog[13]) +{ + memcpy(opts->mediacatalog, &mediacatalog, 13); +} + + +/* ts A61106 */ +void burn_write_opts_set_multi(struct burn_write_opts *opts, int multi) +{ + opts->multi = !!multi; +} + + +/* ts A61222 */ +void burn_write_opts_set_start_byte(struct burn_write_opts *opts, off_t value) +{ + opts->start_byte = value; +} + + +void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw) +{ + opts->raw = raw; +} + +void burn_read_opts_set_c2errors(struct burn_read_opts *opts, int c2errors) +{ + opts->c2errors = c2errors; +} + +void burn_read_opts_read_subcodes_audio(struct burn_read_opts *opts, + int subcodes_audio) +{ + opts->subcodes_audio = subcodes_audio; +} + +void burn_read_opts_read_subcodes_data(struct burn_read_opts *opts, + int subcodes_data) +{ + opts->subcodes_data = subcodes_data; +} + +void burn_read_opts_set_hardware_error_recovery(struct burn_read_opts *opts, + int hardware_error_recovery) +{ + opts->hardware_error_recovery = hardware_error_recovery; +} + +void burn_read_opts_report_recovered_errors(struct burn_read_opts *opts, + int report_recovered_errors) +{ + opts->report_recovered_errors = report_recovered_errors; +} + +void burn_read_opts_transfer_damaged_blocks(struct burn_read_opts *opts, + int transfer_damaged_blocks) +{ + opts->transfer_damaged_blocks = transfer_damaged_blocks; +} + +void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts, + unsigned char + hardware_error_retries) +{ + opts->hardware_error_retries = hardware_error_retries; +} + diff --git a/libburn/tags/ZeroThreeZero/libburn/options.h b/libburn/tags/ZeroThreeZero/libburn/options.h new file mode 100644 index 00000000..2a0d1a3e --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/options.h @@ -0,0 +1,87 @@ +#ifndef BURN__OPTIONS_H +#define BURN__OPTIONS_H + +#include "libburn.h" + +/** Options for disc writing operations. This should be created with + burn_write_opts_new() and freed with burn_write_opts_free(). */ +struct burn_write_opts +{ + /** Drive the write opts are good for */ + struct burn_drive *drive; + + /** For internal use. */ + int refcount; + + /** The method/style of writing to use. */ + enum burn_write_types write_type; + /** format of the data to send to the drive */ + enum burn_block_types block_type; + + /** Number of toc entries. if this is 0, they will be auto generated*/ + int toc_entries; + /** Toc entries for the disc */ + struct burn_toc_entry *toc_entry; + + /** Simulate the write so that the disc is not actually written */ + unsigned int simulate:1; + /** If available, enable a drive feature which prevents buffer + underruns if not enough data is available to keep up with the + drive. */ + unsigned int underrun_proof:1; + /** Perform calibration of the drive's laser before beginning the + write. */ + unsigned int perform_opc:1; + + /* ts A61219 : Output block size to trigger buffer flush if hit. + -1 with CD, 32 kB with DVD */ + int obs; + int obs_pad; /* 1=pad up last block to obs */ + + /* ts A61222 : Start address for media which allow a choice */ + off_t start_byte; + + /** A disc can have a media catalog number */ + int has_mediacatalog; + unsigned char mediacatalog[13]; + /** Session format */ + int format; + /* internal use only */ + unsigned char control; + unsigned char multi; +}; + +/** Options for disc reading operations. This should be created with + burn_read_opts_new() and freed with burn_read_opts_free(). */ +struct burn_read_opts +{ + /** Drive the read opts are good for */ + struct burn_drive *drive; + + /** For internal use. */ + int refcount; + + /** Read in raw mode, so that everything in the data tracks on the + disc is read, including headers. Not needed if just reading a + filesystem off a disc, but it should usually be used when making a + disc image or copying a disc. */ + unsigned int raw:1; + /** Report c2 errors. Useful for statistics reporting */ + unsigned int c2errors:1; + /** Read subcodes from audio tracks on the disc */ + unsigned int subcodes_audio:1; + /** Read subcodes from data tracks on the disc */ + unsigned int subcodes_data:1; + /** Have the drive recover errors if possible */ + unsigned int hardware_error_recovery:1; + /** Report errors even when they were recovered from */ + unsigned int report_recovered_errors:1; + /** Read blocks even when there are unrecoverable errors in them */ + unsigned int transfer_damaged_blocks:1; + + /** The number of retries the hardware should make to correct + errors. */ + unsigned char hardware_error_retries; +}; + +#endif /* BURN__OPTIONS_H */ diff --git a/libburn/tags/ZeroThreeZero/libburn/os-freebsd.h b/libburn/tags/ZeroThreeZero/libburn/os-freebsd.h new file mode 100644 index 00000000..f3e903e5 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/os-freebsd.h @@ -0,0 +1,73 @@ + +/* os-freebsd.h + Operating system specific libburn definitions and declarations. Included + by os.h in case of compilation for + FreeBSD with CAM + + Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL +*/ + +#ifndef BURN_OS_H_INCLUDED +#define BURN_OS_H_INCLUDED 1 + + +/** List of all signals which shall be caught by signal handlers and trigger + a graceful abort of libburn. (See man 7 signal.) +*/ +/* Once as system defined macros */ +#define BURN_OS_SIGNAL_MACRO_LIST \ + SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \ + SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \ + SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, \ + SIGTTOU, \ + SIGBUS, SIGPROF, SIGSYS, SIGTRAP, \ + SIGVTALRM, SIGXCPU, SIGXFSZ + +/* Once as text 1:1 list of strings for messages and interpreters */ +#define BURN_OS_SIGNAL_NAME_LIST \ + "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", \ + "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", \ + "SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", \ + "SIGTTOU", \ + "SIGBUS", "SIGPROF", "SIGSYS", "SIGTRAP", \ + "SIGVTALRM", "SIGXCPU", "SIGXFSZ" + +/* The number of above list items */ +#define BURN_OS_SIGNAL_COUNT 23 + +/** To list all signals which shall surely not be caught */ +#define BURN_OS_NON_SIGNAL_MACRO_LIST \ +SIGKILL, SIGCHLD, SIGSTOP, SIGURG, SIGWINCH + +/* The number of above list items */ +#define BURN_OS_NON_SIGNAL_COUNT 5 + + +/* The maximum size for a (SCSI) i/o transaction */ +/* Important : MUST be at least 32768 ! */ +#define BURN_OS_TRANSPORT_BUFFER_SIZE 32768 + + +/** To hold all state information of BSD device enumeration + which are now local in sg_enumerate() . So that sg_give_next_adr() + can work in BSD and sg_enumerate() can use it. +*/ +#define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \ +struct burn_drive_enumeration_state { \ + union ccb ccb; \ + int bufsize, fd; \ + unsigned int i; \ + int skip_device; \ +}; \ +typedef struct burn_drive_enumeration_state burn_drive_enumerator_t; + + +/* The list of operating system dependent elements in struct burn_drive. + To be initialized and used within sg-*.c . +*/ +#define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \ +struct cam_device* cam; + + +#endif /* ! BURN_OS_H_INCLUDED */ + diff --git a/libburn/tags/ZeroThreeZero/libburn/os-linux.h b/libburn/tags/ZeroThreeZero/libburn/os-linux.h new file mode 100644 index 00000000..99e8bb5f --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/os-linux.h @@ -0,0 +1,64 @@ + +/* os-linux.h + Operating system specific libburn definitions and declarations. Included + by os.h in case of compilation for + Linux kernels 2.4 and 2.6 with Linux SCSI Generic (sg) + + Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL +*/ + + +/** List of all signals which shall be caught by signal handlers and trigger + a graceful abort of libburn. (See man 7 signal.) +*/ +/* Once as system defined macros */ +#define BURN_OS_SIGNAL_MACRO_LIST \ + SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \ + SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \ + SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, \ + SIGTTOU, \ + SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP, \ + SIGVTALRM, SIGXCPU, SIGXFSZ + +/* Once as text 1:1 list of strings for messages and interpreters */ +#define BURN_OS_SIGNAL_NAME_LIST \ + "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", \ + "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", \ + "SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", \ + "SIGTTOU", \ + "SIGBUS", "SIGPOLL", "SIGPROF", "SIGSYS", "SIGTRAP", \ + "SIGVTALRM", "SIGXCPU", "SIGXFSZ" + +/* The number of above list items */ +#define BURN_OS_SIGNAL_COUNT 24 + +/** To list all signals which shall surely not be caught */ +#define BURN_OS_NON_SIGNAL_MACRO_LIST \ +SIGKILL, SIGCHLD, SIGSTOP, SIGURG, SIGWINCH + +/* The number of above list items */ +#define BURN_OS_NON_SIGNAL_COUNT 5 + + +/* The maximum size for a (SCSI) i/o transaction */ +/* Important : MUST be at least 32768 ! */ +#define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 + + +/* To hold the index number of the most recently delivered address from + device enumeration. +*/ +#define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \ +typedef int burn_drive_enumerator_t; + + +/* The list of operating system dependent elements in struct burn_drive. + Usually they are initialized in sg-*.c:enumerate_common(). +*/ +#define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \ +int fd; \ + \ +/* ts A60926 : trying to lock against growisofs /dev/srN, /dev/scdN */ \ +int sibling_count; \ +int sibling_fds[LIBBURN_SG_MAX_SIBLINGS]; + diff --git a/libburn/tags/ZeroThreeZero/libburn/os.h b/libburn/tags/ZeroThreeZero/libburn/os.h new file mode 100644 index 00000000..3b7a0f4d --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/os.h @@ -0,0 +1,34 @@ + +/* os.h + Operating system specific libburn definitions and declarations. + The macros defined here are used by libburn modules in order to + avoid own system dependent case distinctions. + Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL +*/ + +#ifndef BURN_OS_H_INCLUDED +#define BURN_OS_H_INCLUDED 1 + +/* + Operating system case distinction +*/ + +#ifdef __FreeBSD__ + + +/* ----------------------------- FreeBSD with CAM -------------------------- */ +#include "os-freebsd.h" + + +#else /* operating system case distinction */ + + +/* --------- Linux kernels 2.4 and 2.6 with Linux SCSI Generic (sg) -------- */ +#include "os-linux.h" + + +#endif /* End of operating system case distinction */ + + +#endif /* ! BURN_OS_H_INCLUDED */ + diff --git a/libburn/tags/ZeroThreeZero/libburn/read.c b/libburn/tags/ZeroThreeZero/libburn/read.c new file mode 100644 index 00000000..72f60d78 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/read.c @@ -0,0 +1,282 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* #include <m alloc.h> ts A61013 : not in Linux man 3 malloc */ + +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> + +/* ts A61007 */ +/* #include <a ssert.h> */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include "sector.h" +#include "libburn.h" +#include "drive.h" +#include "transport.h" + +/* ts A60925 : obsoleted by libdax_msgs.h +#include "message.h" +*/ + +#include "crc.h" +#include "debug.h" +#include "init.h" +#include "lec.h" +#include "toc.h" +#include "util.h" +#include "sg.h" +#include "read.h" +#include "options.h" + +void burn_disc_read(struct burn_drive *d, const struct burn_read_opts *o) +{ +#if 0 + int i, end, maxsects, finish; + int seclen; + int drive_lba; + unsigned short crc; + unsigned char fakesub[96]; + struct buffer page; + int speed; + + /* ts A61007 : if this function gets revived, then these + tests have to be done more graceful */ + a ssert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000)); + a ssert(!d->busy); + a ssert(d->toc->valid); + a ssert(o->datafd != -1); + + /* moved up from spc_select_error_params alias d->send_parameters() */ + a ssert(d->mdata->valid); + +/* XXX not sure this is a good idea. copy it? */ +/* XXX also, we have duplicated data now, do we remove the fds from struct +drive, or only store a subset of the _opts structs in drives */ + + /* set the speed on the drive */ + speed = o->speed > 0 ? o->speed : d->mdata->max_read_speed; + d->set_speed(d, speed, 0); + + d->params.retries = o->hardware_error_retries; + + d->send_parameters(d, o); + + d->cancel = 0; + d->busy = BURN_DRIVE_READING; + d->currsession = 0; +/* drive_lba = 232000; + d->currtrack = 18; +*/ + d->currtrack = 0; + drive_lba = 0; +/* XXX removal of this line obviously breaks * + d->track_end = burn_track_end(d, d->currsession, d->currtrack);*/ + printf("track ends at %d\n", d->track_end); + page.sectors = 0; + page.bytes = 0; + + if (o->subfd != -1) { + memset(fakesub, 0xFF, 12); + memset(fakesub + 12, 0, 84); + fakesub[13] = 1; + fakesub[14] = 1; + fakesub[20] = 2; + fakesub[12] = (d->toc->toc_entry[0].control << 4) + + d->toc->toc_entry[0].adr; + crc = crc_ccitt(fakesub + 12, 10); + fakesub[22] = crc >> 8; + fakesub[23] = crc & 0xFF; + write(o->subfd, fakesub, 96); + } + while (1) { + seclen = burn_sector_length_read(d, o); + + burn_print(12, "received %d blocks\n", page.sectors); + for (i = 0; i < page.sectors; i++) { + burn_packet_process(d, page.data + seclen * i, o); + d->track_end--; + drive_lba++; + } + + if ((d->cancel) || (drive_lba == LAST_SESSION_END(d))) { + burn_print(1, "finished or cancelled\n"); + d->busy = BURN_DRIVE_IDLE; + if (!d->cancel) + d->toc->complete = 1; + return; + } +/* XXX: removal of this line obviously breaks * + end = burn_track_end(d, d->currsession, d->currtrack); */ + + if (drive_lba == end) { + d->currtrack++; + if (d->currtrack > + d->toc->session[d->currsession].lasttrack) { + d->currsession++; + burn_print(12, "session switch to %d\n", + d->currsession); + burn_print(12, "skipping a lead out\n"); + drive_lba = CURRENT_SESSION_START(d); + burn_print(12, "new lba %d\n", drive_lba); +/* XXX more of the same + end = burn_track_end(d, d->currsession, + d->currtrack); +*/ } + burn_print(12, "track switch to %d\n", d->currtrack); + } + + page.sectors = 0; + page.bytes = 0; + + maxsects = BUFFER_SIZE / seclen; + finish = end - drive_lba; + + d->track_end = finish; + + page.sectors = (finish < maxsects) ? finish : maxsects; + printf("reading %d sectors from %d\n", page.sectors, + drive_lba); + + /* >>> ts A61009 : ensure page.sectors >= 0 before calling */ + d->r ead_sectors(d, drive_lba, page.sectors, o, &page); + + printf("Read %d\n", page.sectors); + } +#endif +} +int burn_sector_length_read(struct burn_drive *d, + const struct burn_read_opts *o) +{ + int dlen = 2352; + int data; + +/*XXX how do we handle this crap now?*/ +/* data = d->toc->track[d->currtrack].toc_entry->control & 4;*/ + data = 1; + if (o->report_recovered_errors) + dlen += 294; + if ((o->subcodes_data) && data) + dlen += 96; + if ((o->subcodes_audio) && !data) + dlen += 96; + return dlen; +} + +static int bitcount(unsigned char *data, int n) +{ + int i, j, count = 0; + unsigned char tem; + + for (i = 0; i < n; i++) { + tem = data[i]; + for (j = 0; j < 8; j++) { + count += tem & 1; + tem >>= 1; + } + } + return count; +} + +void burn_packet_process(struct burn_drive *d, unsigned char *data, + const struct burn_read_opts *o) +{ + unsigned char sub[96]; + unsigned short crc; + int ptr = 2352, i, j, code, fb; + int audio = 1; + + if (o->c2errors) { + fb = bitcount(data + ptr, 294); + if (fb) { + burn_print(1, "%d damaged bits\n", + bitcount(data + ptr, 294)); + burn_print(1, "sending error on %s %s\n", + d->idata->vendor, d->idata->product); + /* XXX send a burn_message! burn_message_error(d, + something); */ + } + ptr += 294; + } +/* + if (d->toc->track[d->currtrack].mode == BURN_MODE_UNINITIALIZED) { + if ((d->toc->track[d->currtrack].toc_entry->control & 4) == 0) + d->toc->track[d->currtrack].mode = BURN_MODE_AUDIO; + else + switch (data[15]) { + case 0: + d->toc->track[d->currtrack].mode = BURN_MODE0; + break; + case 1: + d->toc->track[d->currtrack].mode = BURN_MODE1; + break; + case 2: + d->toc->track[d->currtrack].mode = + BURN_MODE2_FORMLESS; + break; + } + } +*/ + if ((audio && o->subcodes_audio) + || (!audio && o->subcodes_data)) { + memset(sub, 0, sizeof(sub)); + for (i = 0; i < 12; i++) { + for (j = 0; j < 8; j++) { + for (code = 0; code < 8; code++) { + sub[code * 12 + i] <<= 1; + if (data[ptr + j + i * 8] & + (1 << (7 - code))) + sub[code * 12 + i]++; + } + } + } + crc = (*(sub + 22) << 8) + *(sub + 23); + if (crc != crc_ccitt(sub + 12, 10)) { + burn_print(1, "sending error on %s %s\n", + d->idata->vendor, d->idata->product); +/* e = burn_error(); + e->drive = d; +*/ + burn_print(1, "crc mismatch in Q\n"); + } + /* else process_q(d, sub + 12); */ + /* + if (o->subfd != -1) write(o->subfd, sub, 96); */ + } +/* + if ((d->track_end <= 150) + && (drive_lba + 150 < CURRENT_SESSION_END(d)) + && (TOC_ENTRY(d->toc, d->currtrack).control == 4) + && (TOC_ENTRY(d->toc, d->currtrack + 1).control == 0)) { + burn_print(12, "pregap : %d\n", d->track_end); + write(o->binfd, zeros, 2352); + +#warning XXX WHERE ARE MY SUBCODES + } else +*//* write(o->datafd, data, 2352); */ +} + +/* so yeah, when you uncomment these, make them write zeros insted of crap +static void write_empty_sector(int fd) +{ + char sec[2352]; + + burn_print(1, "writing an 'empty' sector\n"); + write(fd, sec, 2352); +} + +static void write_empty_subcode(int fd) +{ + char sub[96]; + + write(fd, sub, 96); +} + +static void flipq(unsigned char *sub) +{ + *(sub + 12 + 10) = ~*(sub + 12 + 10); + *(sub + 12 + 11) = ~*(sub + 12 + 11); +} +*/ diff --git a/libburn/tags/ZeroThreeZero/libburn/read.h b/libburn/tags/ZeroThreeZero/libburn/read.h new file mode 100644 index 00000000..fc079583 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/read.h @@ -0,0 +1,14 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __LIBBURN_READ +#define __LIBBURN_READ + +struct burn_drive; +struct burn_read_opts; + +int burn_sector_length_read(struct burn_drive *d, + const struct burn_read_opts *o); +void burn_packet_process(struct burn_drive *d, unsigned char *data, + const struct burn_read_opts *o); + +#endif /* __LIBBURN_READ */ diff --git a/libburn/tags/ZeroThreeZero/libburn/sbc.c b/libburn/tags/ZeroThreeZero/libburn/sbc.c new file mode 100644 index 00000000..942ebf56 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/sbc.c @@ -0,0 +1,65 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* scsi block commands */ + +#include <string.h> + +#include "transport.h" +#include "sbc.h" +#include "options.h" + +/* spc command set */ +static char SBC_LOAD[] = { 0x1b, 0, 0, 0, 3, 0 }; +static char SBC_UNLOAD[] = { 0x1b, 0, 0, 0, 2, 0 }; +static char SBC_START_UNIT[] = { 0x1b, 0, 0, 0, 1, 0 }; + +void sbc_load(struct burn_drive *d) +{ + struct command c; + + memcpy(c.opcode, SBC_LOAD, sizeof(SBC_LOAD)); + c.retry = 1; + c.oplen = sizeof(SBC_LOAD); + c.dir = NO_TRANSFER; + c.page = NULL; + d->issue_command(d, &c); +} + +void sbc_eject(struct burn_drive *d) +{ + struct command c; + + c.page = NULL; + memcpy(c.opcode, SBC_UNLOAD, sizeof(SBC_UNLOAD)); + c.oplen = 1; + c.oplen = sizeof(SBC_UNLOAD); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +/* ts A61118 : is it necessary to tell the drive to get ready for use ? */ +int sbc_start_unit(struct burn_drive *d) +{ + struct command c; + + memcpy(c.opcode, SBC_START_UNIT, sizeof(SBC_START_UNIT)); + c.retry = 1; + c.oplen = sizeof(SBC_START_UNIT); + c.dir = NO_TRANSFER; + c.page = NULL; + d->issue_command(d, &c); + return (c.error==0); +} + + +/* ts A61021 : the sbc specific part of sg.c:enumerate_common() +*/ +int sbc_setup_drive(struct burn_drive *d) +{ + d->eject = sbc_eject; + d->load = sbc_load; + d->start_unit = sbc_start_unit; + return 1; +} + diff --git a/libburn/tags/ZeroThreeZero/libburn/sbc.h b/libburn/tags/ZeroThreeZero/libburn/sbc.h new file mode 100644 index 00000000..dc7a991b --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/sbc.h @@ -0,0 +1,18 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SBC +#define __SBC + +struct burn_drive; + +void sbc_load(struct burn_drive *); +void sbc_eject(struct burn_drive *); + +/* ts A61118 */ +int sbc_start_unit(struct burn_drive *); + +/* ts A61021 : the sbc specific part of sg.c:enumerate_common() +*/ +int sbc_setup_drive(struct burn_drive *d); + +#endif /* __SBC */ diff --git a/libburn/tags/ZeroThreeZero/libburn/sector.c b/libburn/tags/ZeroThreeZero/libburn/sector.c new file mode 100644 index 00000000..6fcceab1 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/sector.c @@ -0,0 +1,840 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include <stdio.h> + +/* ts A61010 */ +/* #include <a ssert.h> */ + +#include <unistd.h> +#include <string.h> +#include "error.h" +#include "options.h" +#include "transport.h" +#include "libburn.h" +#include "drive.h" +#include "sector.h" +#include "crc.h" +#include "debug.h" +#include "lec.h" +#include "toc.h" +#include "write.h" + + +#ifdef Libburn_log_in_and_out_streaM +/* <<< ts A61031 */ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#endif /* Libburn_log_in_and_out_streaM */ + + +/*static unsigned char isrc[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";*/ + +#define sector_common(X) d->alba++; d->rlba X; + +static void uncook_subs(unsigned char *dest, unsigned char *source) +{ + int i, j, code; + + memset(dest, 0, 96); + + for (i = 0; i < 12; i++) { + for (j = 0; j < 8; j++) { + for (code = 0; code < 8; code++) { + if (source[code * 12 + i] & 0x80) + dest[j + i * 8] |= (1 << (7 - code)); + source[code * 12 + i] <<= 1; + } + } + } +} + +/* @return >=0 : valid , <0 invalid */ +int sector_get_outmode(enum burn_write_types write_type, + enum burn_block_types block_type) +{ + /* ts A61103 : extended SAO condition to TAO */ + if (write_type == BURN_WRITE_SAO || write_type == BURN_WRITE_TAO) + return 0; + else + switch (block_type) { + case BURN_BLOCK_RAW0: + return BURN_MODE_RAW; + case BURN_BLOCK_RAW16: + return BURN_MODE_RAW | BURN_SUBCODE_P16; + case BURN_BLOCK_RAW96P: + return BURN_MODE_RAW | BURN_SUBCODE_P96; + case BURN_BLOCK_RAW96R: + return BURN_MODE_RAW | BURN_SUBCODE_R96; + case BURN_BLOCK_MODE1: + return BURN_MODE1; + default: + return -1; + } + + /* ts A61007 : now handled in burn_write_opts_set_write_type() */ + /* a ssert(0); */ /* return BURN_MODE_UNIMPLEMENTED :) */ +} + +/* 0 means "same as inmode" */ +static int get_outmode(struct burn_write_opts *o) +{ + /* ts A61007 */ + return sector_get_outmode(o->write_type, o->block_type); + + /* -1 is prevented by check in burn_write_opts_set_write_type() */ + /* a ssert(0); */ /* return BURN_MODE_UNIMPLEMENTED :) */ +} + + +static void get_bytes(struct burn_track *track, int count, unsigned char *data) +{ + int valid, shortage, curr, i, tr; + +#ifdef Libburn_log_in_and_out_streaM + /* <<< ts A61031 */ + static int tee_fd= -1; + if(tee_fd==-1) + tee_fd= open("/tmp/libburn_sg_readin", + O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR); +#endif /* Libburn_log_in_and_out_streaM */ + + +/* no track pointer means we're just generating 0s */ + if (!track) { + memset(data, 0, count); + return; + } + +/* first we use up any offset */ + valid = track->offset - track->offsetcount; + if (valid > count) + valid = count; + + if (valid) { + track->offsetcount += valid; + memset(data, 0, valid); + } + shortage = count - valid; + + if (!shortage) + goto ex; + +/* Next we use source data */ + curr = valid; + if (!track->eos) { + valid = track->source->read(track->source, data + curr, count - curr); + } else valid = 0; + + if (valid <= 0) { /* ts A61031 : extended from (valid == -1) */ + track->eos = 1; + valid = 0; + } + track->sourcecount += valid; + +#ifdef Libburn_log_in_and_out_streaM + /* <<< ts A61031 */ + if(tee_fd!=-1 && valid>0) { + write(tee_fd, data + curr, valid); + } +#endif /* Libburn_log_in_and_out_streaM */ + + curr += valid; + shortage = count - curr; + + if (!shortage) + goto ex; + +/* Before going to the next track, we run through any tail */ + + valid = track->tail - track->tailcount; + if (valid > count - curr) + valid = count - curr; + + if (valid) { + track->tailcount += valid; + memset(data + curr, 0, valid); + } + curr += valid; + shortage -= valid; + + if (!shortage) + goto ex; + + /* ts A61031 */ + if (shortage >= count) + track->track_data_done = 1; + if (track->open_ended) + goto ex; + +/* If we're still short, and there's a "next" pointer, we pull from that. + if that depletes, we'll just fill with 0s. +*/ + if (track->source->next) { + struct burn_source *src; + printf("pulling from next track\n"); + src = track->source->next; + valid = src->read(src, data + curr, shortage); + if (valid > 0) { + shortage -= valid; + curr += valid; + } + } +ex:; + /* ts A61024 : general finalizing processing */ + if(shortage) + memset(data + curr, 0, shortage); /* this is old icculus.org */ + if (track->swap_source_bytes == 1) { + for (i = 1; i < count; i += 2) { + tr = data[i]; + data[i] = data[i-1]; + data[i-1] = tr; + } + } +} + +/* ts A61009 : seems to hand out sector start pointer in opts->drive->buffer + and to count hand outs as well as reserved bytes */ +/* ts A61101 : added parameter track for counting written bytes */ +static unsigned char *get_sector(struct burn_write_opts *opts, + struct burn_track *track, int inmode) +{ + struct burn_drive *d = opts->drive; + struct buffer *out = d->buffer; + int outmode; + int seclen; + unsigned char *ret; + + outmode = get_outmode(opts); + if (outmode == 0) + outmode = inmode; + + /* ts A61009 : react on eventual failure of burn_sector_length() + (should not happen if API tested properly). + Ensures out->bytes >= out->sectors */ + seclen = burn_sector_length(outmode); + if (seclen <= 0) + return NULL; + seclen += burn_subcode_length(outmode); + + /* ts A61219 : opts->obs is eventually a 32k trigger for DVD */ + if (out->bytes + (seclen) > BUFFER_SIZE || out->bytes == opts->obs) { + int err; + err = d->write(d, d->nwa, out); + if (err == BE_CANCELLED) + return NULL; + + /* ts A61101 */ + if(track != NULL) { + track->writecount += out->bytes; + track->written_sectors += out->sectors; + } + /* ts A61119 */ + d->progress.buffered_bytes += out->bytes; + + d->nwa += out->sectors; + out->bytes = 0; + out->sectors = 0; + } + + ret = out->data + out->bytes; + out->bytes += seclen; + out->sectors++; + + return ret; +} + +/* ts A61031 */ +/* Revoke the counting of the most recent sector handed out by get_sector() */ +static void unget_sector(struct burn_write_opts *opts, int inmode) +{ + struct burn_drive *d = opts->drive; + struct buffer *out = d->buffer; + int outmode; + int seclen; + + outmode = get_outmode(opts); + if (outmode == 0) + outmode = inmode; + + /* ts A61009 : react on eventual failure of burn_sector_length() + (should not happen if API tested properly). + Ensures out->bytes >= out->sectors */ + seclen = burn_sector_length(outmode); + if (seclen <= 0) + return; + seclen += burn_subcode_length(outmode); + + out->bytes -= seclen; + out->sectors--; +} + + +/* either inmode == outmode, or outmode == raw. anything else is bad news */ +/* ts A61010 : changed type to int in order to propagate said bad news */ +/** @return 1 is ok, <= 0 is failure */ +static int convert_data(struct burn_write_opts *o, struct burn_track *track, + int inmode, unsigned char *data) +{ + int outlen, inlen; + int offset = -1; + int outmode; + + outmode = get_outmode(o); + if (outmode == 0) + outmode = inmode; + + outlen = burn_sector_length(outmode); + inlen = burn_sector_length(inmode); + + /* ts A61010 */ + /* a ssert(outlen >= inlen); */ + if (outlen < inlen) + return 0; + + if ((outmode & BURN_MODE_BITS) == (inmode & BURN_MODE_BITS)) { + get_bytes(track, inlen, data); + return 1; + } + + /* ts A61010 */ + /* a ssert(outmode & BURN_MODE_RAW); */ + if (!(outmode & BURN_MODE_RAW)) + return 0; + + if (inmode & BURN_MODE1) + offset = 16; + if (inmode & BURN_MODE_RAW) + offset = 0; + if (inmode & BURN_AUDIO) + offset = 0; + + /* ts A61010 */ + /* a ssert(offset != -1); */ + if (offset == -1) + return 0; + + get_bytes(track, inlen, data + offset); + return 1; +} + +static void convert_subs(struct burn_write_opts *o, int inmode, + unsigned char *subs, unsigned char *sector) +{ + unsigned char *out; + int outmode; + + outmode = get_outmode(o); + if (outmode == 0) + outmode = inmode; + sector += burn_sector_length(outmode); +/* XXX for sao with subs, we'd need something else... */ + + switch (o->block_type) { + case BURN_BLOCK_RAW96R: + uncook_subs(sector, subs); + break; + + case BURN_BLOCK_RAW16: + memcpy(sector, subs + 12, 12); + out = sector + 12; + out[0] = 0; + out[1] = 0; + out[2] = 0; +/*XXX find a better way to deal with partially damaged P channels*/ + if (subs[2] != 0) + out[3] = 0x80; + else + out[3] = 0; + out = sector + 10; + + out[0] = ~out[0]; + out[1] = ~out[1]; + break; + /* ts A61119 : to silence compiler warnings */ + default:; + } +} + +static void subcode_toc(struct burn_drive *d, int mode, unsigned char *data) +{ + unsigned char *q; + int track; + int crc; + int min, sec, frame; + + track = d->toc_temp / 3; + memset(data, 0, 96); + q = data + 12; + + burn_lba_to_msf(d->rlba, &min, &sec, &frame); +/*XXX track numbers are BCD +a0 - 1st track ctrl +a1 - last track ctrl +a2 - lout ctrl +*/ + q[0] = (d->toc_entry[track].control << 4) + 1; + q[1] = 0; + if (d->toc_entry[track].point < 100) + q[2] = dec_to_bcd(d->toc_entry[track].point); + else + q[2] = d->toc_entry[track].point; + q[3] = dec_to_bcd(min); + q[4] = dec_to_bcd(sec); + q[5] = dec_to_bcd(frame); + q[6] = 0; + q[7] = dec_to_bcd(d->toc_entry[track].pmin); + q[8] = dec_to_bcd(d->toc_entry[track].psec); + q[9] = dec_to_bcd(d->toc_entry[track].pframe); + crc = crc_ccitt(q, 10); + q[10] = crc >> 8; + q[11] = crc & 0xFF; + d->toc_temp++; + d->toc_temp %= (d->toc_entries * 3); +} + +int sector_toc(struct burn_write_opts *o, int mode) +{ + struct burn_drive *d = o->drive; + unsigned char *data; + unsigned char subs[96]; + + data = get_sector(o, NULL, mode); + if (data == NULL) + return 0; + /* ts A61010 */ + if (convert_data(o, NULL, mode, data) <= 0) + return 0; + subcode_toc(d, mode, subs); + convert_subs(o, mode, subs, data); + sector_headers(o, data, mode, 1); + sector_common(++) + return 1; +} + +int sector_pregap(struct burn_write_opts *o, + unsigned char tno, unsigned char control, int mode) +{ + struct burn_drive *d = o->drive; + unsigned char *data; + unsigned char subs[96]; + + data = get_sector(o, NULL, mode); + if (data == NULL) + return 0; + /* ts A61010 */ + if (convert_data(o, NULL, mode, data) <= 0) + return 0; + subcode_user(o, subs, tno, control, 0, NULL, 1); + convert_subs(o, mode, subs, data); + sector_headers(o, data, mode, 0); + sector_common(--) + return 1; +} + +int sector_postgap(struct burn_write_opts *o, + unsigned char tno, unsigned char control, int mode) +{ + struct burn_drive *d = o->drive; + unsigned char subs[96]; + unsigned char *data; + + data = get_sector(o, NULL, mode); + if (data == NULL) + return 0; + /* ts A61010 */ + if (convert_data(o, NULL, mode, data) <= 0) + return 0;; +/* use last index in track */ + subcode_user(o, subs, tno, control, 1, NULL, 1); + convert_subs(o, mode, subs, data); + sector_headers(o, data, mode, 0); + sector_common(++) + return 1; +} + +static void subcode_lout(struct burn_write_opts *o, unsigned char control, + unsigned char *data) +{ + struct burn_drive *d = o->drive; + unsigned char *q; + int crc; + int rmin, min, rsec, sec, rframe, frame; + + memset(data, 0, 96); + q = data + 12; + + burn_lba_to_msf(d->alba, &min, &sec, &frame); + burn_lba_to_msf(d->rlba, &rmin, &rsec, &rframe); + + if (((rmin == 0) && (rsec == 0) && (rframe == 0)) || + ((rsec >= 2) && !((rframe / 19) % 2))) + memset(data, 0xFF, 12); + q[0] = (control << 4) + 1; + q[1] = 0xAA; + q[2] = 0x01; + q[3] = dec_to_bcd(rmin); + q[4] = dec_to_bcd(rsec); + q[5] = dec_to_bcd(rframe); + q[6] = 0; + q[7] = dec_to_bcd(min); + q[8] = dec_to_bcd(sec); + q[9] = dec_to_bcd(frame); + crc = crc_ccitt(q, 10); + q[10] = crc >> 8; + q[11] = crc & 0xFF; +} + +static char char_to_isrc(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'Z') + return 0x11 + (c - 'A'); + if (c >= 'a' && c <= 'z') + return 0x11 + (c - 'a'); + + /* ts A61008 : obsoleted by test in burn_track_set_isrc() */ + /* a ssert(0); */ + return 0; +} + +void subcode_user(struct burn_write_opts *o, unsigned char *subcodes, + unsigned char tno, unsigned char control, + unsigned char indx, struct isrc *isrc, int psub) +{ + struct burn_drive *d = o->drive; + unsigned char *p, *q; + int crc; + int m, s, f, c, qmode; /* 1, 2 or 3 */ + + memset(subcodes, 0, 96); + + p = subcodes; + if ((tno == 1) && (d->rlba == -150)) + memset(p, 0xFF, 12); + + if (psub) + memset(p, 0xFF, 12); + q = subcodes + 12; + + qmode = 1; + /* every 1 in 10 we can do something different */ + if (d->rlba % 10 == 0) { + /* each of these can occur 1 in 100 */ + if ((d->rlba / 10) % 10 == 0) { + if (o->has_mediacatalog) + qmode = 2; + } else if ((d->rlba / 10) % 10 == 1) { + if (isrc && isrc->has_isrc) + qmode = 3; + } + } + + /* ts A61010 : this cannot happen. Assert for fun ? */ + /* a ssert(qmode == 1 || qmode == 2 || qmode == 3); */ + + switch (qmode) { + case 1: + q[1] = dec_to_bcd(tno); /* track number */ + q[2] = dec_to_bcd(indx); /* index XXX read this shit + from the track array */ + burn_lba_to_msf(d->rlba, &m, &s, &f); + q[3] = dec_to_bcd(m); /* rel min */ + q[4] = dec_to_bcd(s); /* rel sec */ + q[5] = dec_to_bcd(f); /* rel frame */ + q[6] = 0; /* zero */ + burn_lba_to_msf(d->alba, &m, &s, &f); + q[7] = dec_to_bcd(m); /* abs min */ + q[8] = dec_to_bcd(s); /* abs sec */ + q[9] = dec_to_bcd(f); /* abs frame */ + break; + case 2: + /* media catalog number */ + q[1] = (o->mediacatalog[0] << 4) + o->mediacatalog[1]; + q[2] = (o->mediacatalog[2] << 4) + o->mediacatalog[3]; + q[3] = (o->mediacatalog[4] << 4) + o->mediacatalog[5]; + q[4] = (o->mediacatalog[6] << 4) + o->mediacatalog[7]; + q[5] = (o->mediacatalog[8] << 4) + o->mediacatalog[9]; + q[6] = (o->mediacatalog[10] << 4) + o->mediacatalog[11]; + q[7] = o->mediacatalog[12] << 4; + + q[8] = 0; + burn_lba_to_msf(d->alba, &m, &s, &f); + q[9] = dec_to_bcd(f); /* abs frame */ + break; + case 3: + c = char_to_isrc(isrc->country[0]); + /* top 6 bits of [1] is the first country code */ + q[1] = c << 2; + c = char_to_isrc(isrc->country[1]); + /* bottom 2 bits of [1] is part of the second country code */ + q[1] += (c >> 4); + /* top 4 bits if [2] is the rest of the second country code */ + q[2] = c << 4; + + c = char_to_isrc(isrc->owner[0]); + /* bottom 4 bits of [2] is part of the first owner code */ + q[2] += (c >> 2); + /* top 2 bits of [3] is the rest of the first owner code */ + q[3] = c << 6; + c = char_to_isrc(isrc->owner[1]); + /* bottom 6 bits of [3] is the entire second owner code */ + q[3] += c; + c = char_to_isrc(isrc->owner[2]); + /* top 6 bits of [4] are the third owner code */ + q[4] = c << 2; + + /* [5] is the year in 2 BCD numbers */ + q[5] = dec_to_bcd(isrc->year % 100); + /* [6] is the first 2 digits in the serial */ + q[6] = dec_to_bcd(isrc->serial % 100); + /* [7] is the next 2 digits in the serial */ + q[7] = dec_to_bcd((isrc->serial / 100) % 100); + /* the top 4 bits of [8] is the last serial digit, the rest is + zeros */ + q[8] = dec_to_bcd((isrc->serial / 10000) % 10) << 4; + burn_lba_to_msf(d->alba, &m, &s, &f); + q[9] = dec_to_bcd(f); /* abs frame */ + break; + } + q[0] = (control << 4) + qmode; + + crc = crc_ccitt(q, 10); + q[10] = crc >> 8; + q[11] = crc & 0xff; +} + +int sector_lout(struct burn_write_opts *o, unsigned char control, int mode) +{ + struct burn_drive *d = o->drive; + unsigned char subs[96]; + unsigned char *data; + + data = get_sector(o, NULL, mode); + if (!data) + return 0; + /* ts A61010 */ + if (convert_data(o, NULL, mode, data) <= 0) + return 0; + subcode_lout(o, control, subs); + convert_subs(o, mode, subs, data); + sector_headers(o, data, mode, 0); + sector_common(++) + return 1; +} + +int sector_data(struct burn_write_opts *o, struct burn_track *t, int psub) +{ + struct burn_drive *d = o->drive; + unsigned char subs[96]; + unsigned char *data; + + data = get_sector(o, t, t->mode); + if (!data) + return 0; + /* ts A61010 */ + if (convert_data(o, t, t->mode, data) <= 0) + return 0; + + /* ts A61031 */ + if (t->open_ended && t->track_data_done) { + unget_sector(o, t->mode); + return 2; + } + + /* ts A61219 : allow track without .entry */ + if (t->entry == NULL) + ; + else if (!t->source->read_sub) + subcode_user(o, subs, t->entry->point, + t->entry->control, 1, &t->isrc, psub); + else if (!t->source->read_sub(t->source, subs, 96)) + subcode_user(o, subs, t->entry->point, + t->entry->control, 1, &t->isrc, psub); + convert_subs(o, t->mode, subs, data); + + sector_headers(o, data, t->mode, 0); + sector_common(++) + return 1; +} + +int burn_msf_to_lba(int m, int s, int f) +{ + if (m < 90) + return (m * 60 + s) * 75 + f - 150; + else + return (m * 60 + s) * 75 + f - 450150; +} + +void burn_lba_to_msf(int lba, int *m, int *s, int *f) +{ + if (lba >= -150) { + *m = (lba + 150) / (60 * 75); + *s = (lba + 150 - *m * 60 * 75) / 75; + *f = lba + 150 - *m * 60 * 75 - *s * 75; + } else { + *m = (lba + 450150) / (60 * 75); + *s = (lba + 450150 - *m * 60 * 75) / 75; + *f = lba + 450150 - *m * 60 * 75 - *s * 75; + } +} + +int dec_to_bcd(int d) +{ + int top, bottom; + + top = d / 10; + bottom = d - (top * 10); + return (top << 4) + bottom; +} + +int sector_headers_is_ok(struct burn_write_opts *o, int mode) +{ + if (mode & BURN_AUDIO) /* no headers for "audio" */ + return 1; + if (o->write_type == BURN_WRITE_SAO) + return 1; + + /* ts A61031 */ + if (o->write_type == BURN_WRITE_TAO) + return 1; + + if (mode & BURN_MODE1) + return 2; + return 0; +} + +void sector_headers(struct burn_write_opts *o, unsigned char *out, + int mode, int leadin) +{ + struct burn_drive *d = o->drive; + unsigned int crc; + int min, sec, frame; + int modebyte = -1; + + /* ts A61009 */ +#if 1 + int ret; + + ret = sector_headers_is_ok(o, mode); + if (ret != 2) + return; + modebyte = 1; + +#else + + if (mode & BURN_AUDIO) /* no headers for "audio" */ + return; + if (o->write_type == BURN_WRITE_SAO) + return; + + /* ts A61031 */ + if (o->write_type == BURN_WRITE_TAO) + return; + + if (mode & BURN_MODE1) + modebyte = 1; + +#endif + + /* ts A61009 : now ensured by burn_disc_write_is_ok() */ + /* a ssert(modebyte == 1); */ + + out[0] = 0; + memset(out + 1, 0xFF, 10); /* sync */ + out[11] = 0; + + if (leadin) { + burn_lba_to_msf(d->rlba, &min, &sec, &frame); + out[12] = dec_to_bcd(min) + 0xA0; + out[13] = dec_to_bcd(sec); + out[14] = dec_to_bcd(frame); + out[15] = modebyte; + } else { + burn_lba_to_msf(d->alba, &min, &sec, &frame); + out[12] = dec_to_bcd(min); + out[13] = dec_to_bcd(sec); + out[14] = dec_to_bcd(frame); + out[15] = modebyte; + } + if (mode & BURN_MODE1) { + crc = crc_32(out, 2064); + out[2064] = crc & 0xFF; + crc >>= 8; + out[2065] = crc & 0xFF; + crc >>= 8; + out[2066] = crc & 0xFF; + crc >>= 8; + out[2067] = crc & 0xFF; + } + if (mode & BURN_MODE1) { + memset(out + 2068, 0, 8); + parity_p(out); + parity_q(out); + } + scramble(out); +} + +#if 0 +void process_q(struct burn_drive *d, unsigned char *q) +{ + unsigned char i[5]; + int mode; + + mode = q[0] & 0xF; +/* burn_print(12, "mode: %d : ", mode);*/ + switch (mode) { + case 1: +/* burn_print(12, "tno = %d : ", q[1]); + burn_print(12, "index = %d\n", q[2]); +*/ + /* q[1] is the track number (starting at 1) q[2] is the index + number (starting at 0) */ +#warning this is totally bogus + if (q[1] - 1 > 99) + break; + if (q[2] > d->toc->track[q[1] - 1].indices) { + burn_print(12, "new index at %d\n", d->alba); + d->toc->track[q[1] - 1].index[q[2]] = d->alba; + d->toc->track[q[1] - 1].indices++; + } + break; + case 2: + /* XXX dont ignore these */ + break; + case 3: +/* burn_print(12, "ISRC data in mode 3 q\n");*/ + i[0] = isrc[(q[1] << 2) >> 2]; +/* burn_print(12, "0x%x 0x%x 0x%x 0x%x 0x%x\n", q[1], q[2], q[3], q[4], q[5]); + burn_print(12, "ISRC - %c%c%c%c%c\n", i[0], i[1], i[2], i[3], i[4]); +*/ + break; + default: + + /* ts A61009 : if reactivated then witout Assert */ + a ssert(0); + } +} +#endif + +/* this needs more info. subs in the data? control/adr? */ + +/* ts A61119 : One should not use inofficial compiler extensions. + >>> Some day this function needs to be implemented. At least for now + the result does not match the "mode" of cdrecord -toc. + */ +/* +#warning sector_identify needs to be written +*/ +int sector_identify(unsigned char *data) +{ + scramble(data); +/* +check mode byte for 1 or 2 +test parity to see if it's a valid sector +if invalid, return BURN_MODE_AUDIO; +else return mode byte (what about mode 2 formless? heh) +*/ + return BURN_MODE1; +} diff --git a/libburn/tags/ZeroThreeZero/libburn/sector.h b/libburn/tags/ZeroThreeZero/libburn/sector.h new file mode 100644 index 00000000..ecb609a4 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/sector.h @@ -0,0 +1,35 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SECTOR +#define __SECTOR + +#include "libburn.h" +#include "transport.h" + +struct burn_drive; +struct isrc; + +int dec_to_bcd(int); + +int sector_toc(struct burn_write_opts *, int mode); +int sector_pregap(struct burn_write_opts *, unsigned char tno, + unsigned char control, int mode); +int sector_postgap(struct burn_write_opts *, unsigned char tno, + unsigned char control, int mode); +int sector_lout(struct burn_write_opts *, unsigned char control, int mode); +int sector_data(struct burn_write_opts *, struct burn_track *t, int psub); + +/* ts A61009 */ +int sector_headers_is_ok(struct burn_write_opts *o, int mode); + +void sector_headers(struct burn_write_opts *, unsigned char *, + int mode, int leadin); +void subcode_user(struct burn_write_opts *, unsigned char *s, + unsigned char tno, unsigned char control, + unsigned char index, struct isrc *isrc, int psub); + +int sector_identify(unsigned char *); + +void process_q(struct burn_drive *d, unsigned char *q); + +#endif /* __SECTOR */ diff --git a/libburn/tags/ZeroThreeZero/libburn/sg-freebsd-port.c b/libburn/tags/ZeroThreeZero/libburn/sg-freebsd-port.c new file mode 100644 index 00000000..42f85096 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/sg-freebsd-port.c @@ -0,0 +1,559 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* + +This is the main operating system dependent SCSI part of libburn. It implements +the transport level aspects of SCSI control and command i/o. + +Present implementation: FreeBSD CAM (untested) + + +PORTING: + +Porting libburn typically will consist of adding a new operating system case +to the following switcher files: + os.h Operating system specific libburn definitions and declarations. + sg.c Operating system dependent transport level modules. +and of deriving the following system specific files from existing examples: + os-*.h Included by os.h. You will need some general system knowledge + about signals and knowledge about the storage object needs of your + transport level module sg-*.c. + + sg-*.c This source module. You will need special system knowledge about + how to detect all potentially available drives, how to open them, + eventually how to exclusively reserve them, how to perform + SCSI transactions, how to inquire the (pseudo-)SCSI driver. + You will not need to care about CD burning, MMC or other high-level + SCSI aspects. + +Said sg-*.c operations are defined by a public function interface, which has +to be implemented in a way that provides libburn with the desired services: + +sg_give_next_adr() iterates over the set of potentially useful drive + address strings. + +scsi_enumerate_drives() brings all available, not-whitelist-banned, and + accessible drives into libburn's list of drives. + +sg_drive_is_open() tells wether libburn has the given drive in use. + +sg_grab() opens the drive for SCSI commands and ensures + undisturbed access. + +sg_release() closes a drive opened by sg_grab() + +sg_issue_command() sends a SCSI command to the drive, receives reply, + and evaluates wether the command succeeded or shall + be retried or finally failed. + +sg_obtain_scsi_adr() tries to obtain SCSI address parameters. + + +Porting hints are marked by the text "PORTING:". +Send feedback to libburn-hackers@pykix.org . + +*/ + + +/** PORTING : ------- OS dependent headers and definitions ------ */ + +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/poll.h> +#include <camlib.h> +#include <cam/scsi/scsi_message.h> +#include <cam/scsi/scsi_pass.h> + +#include <err.h> /* XXX */ + + +/** PORTING : ------ libburn portable headers and definitions ----- */ + +#include "transport.h" +#include "drive.h" +#include "sg.h" +#include "spc.h" +#include "mmc.h" +#include "sbc.h" +#include "debug.h" +#include "toc.h" +#include "util.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + + +/* is in portable part of libburn */ +int burn_drive_is_banned(char *device_address); + + + +/* ------------------------------------------------------------------------ */ +/* ts A61115: Private functions. Port only if needed by public functions */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + + +/* Helper function for scsi_give_next_adr() */ +static int sg_init_enumerator(burn_drive_enumerator_t *idx) +{ + idx->skip_device = 0; + + if ((idx->fd = open(XPT_DEVICE, O_RDWR)) == -1) { + warn("couldn't open %s", XPT_DEVICE); + return -1; + } + + bzero(&(idx->ccb), sizeof(union ccb)); + + idx->ccb.ccb_h.path_id = CAM_XPT_PATH_ID; + idx->ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; + idx->ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; + + idx->ccb.ccb_h.func_code = XPT_DEV_MATCH; + idx->bufsize = sizeof(struct dev_match_result) * 100; + idx->ccb.cdm.match_buf_len = idx->bufsize; + idx->ccb.cdm.matches = (struct dev_match_result *)malloc(idx->bufsize); + if (idx->ccb.cdm.matches == NULL) { + warnx("can't malloc memory for matches"); + close(idx->fd); + return -1; + } + idx->ccb.cdm.num_matches = 0; + idx->i = idx->ccb.cdm.num_matches; /* to trigger buffer load */ + + /* + * We fetch all nodes, since we display most of them in the default + * case, and all in the verbose case. + */ + idx->ccb.cdm.num_patterns = 0; + idx->ccb.cdm.pattern_buf_len = 0; + + return 1; +} + + +/* Helper function for scsi_give_next_adr() */ +static int sg_next_enumeration_buffer(burn_drive_enumerator_t *idx) +{ + /* + * We do the ioctl multiple times if necessary, in case there are + * more than 100 nodes in the EDT. + */ + if (ioctl(idx->fd, CAMIOCOMMAND, &(idx->ccb)) == -1) { + warn("error sending CAMIOCOMMAND ioctl"); + return -1; + } + + if ((idx->ccb.ccb_h.status != CAM_REQ_CMP) + || ((idx->ccb.cdm.status != CAM_DEV_MATCH_LAST) + && (idx->ccb.cdm.status != CAM_DEV_MATCH_MORE))) { + warnx("got CAM error %#x, CDM error %d\n", + idx->ccb.ccb_h.status, idx->ccb.cdm.status); + return -1; + } + return 1; +} + + +static int sg_close_drive(struct burn_drive * d) +{ + if (d->cam != NULL) { + cam_close_device(d->cam); + d->cam = NULL; + } + return 0; +} + + +/* ----------------------------------------------------------------------- */ +/* PORTING: Private functions which contain publicly needed functionality. */ +/* Their portable part must be performed. So it is probably best */ +/* to replace the non-portable part and to call these functions */ +/* in your port, too. */ +/* ----------------------------------------------------------------------- */ + + +/** Wraps a detected drive into libburn structures and hands it over to + libburn drive list. +*/ +static void enumerate_common(char *fname, int bus_no, int host_no, + int channel_no, int target_no, int lun_no) +{ + int ret; + struct burn_drive out; + + /* General libburn drive setup */ + burn_setup_drive(&out, fname); + + /* This transport adapter uses SCSI-family commands and models + (seems the adapter would know better than its boss, if ever) */ + ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no, + target_no, lun_no, 0); + if (ret<=0) + return; + + /* PORTING: ------------------- non portable part --------------- */ + + /* Operating system adapter is CAM */ + /* Adapter specific handles and data */ + out.cam = NULL; + + /* PORTING: ---------------- end of non portable part ------------ */ + + /* Adapter specific functions with standardized names */ + out.grab = sg_grab; + out.release = sg_release; + out.drive_is_open = sg_drive_is_open; + out.issue_command = sg_issue_command; + /* Finally register drive and inquire drive information */ + burn_drive_finish_enum(&out); +} + + +/* ts A61115 */ +/* ------------------------------------------------------------------------ */ +/* PORTING: Public functions. These MUST be ported. */ +/* ------------------------------------------------------------------------ */ + + +/** Returns the next index number and the next enumerated drive address. + The enumeration has to cover all available and accessible drives. It is + allowed to return addresses of drives which are not available but under + some (even exotic) circumstances could be available. It is on the other + hand allowed, only to hand out addresses which can really be used right + in the moment of this call. (This implementation chooses the latter.) + @param idx An opaque handle. Make no own theories about it. + @param adr Takes the reply + @param adr_size Gives maximum size of reply including final 0 + @param initialize 1 = start new, + 0 = continue, use no other values for now + -1 = finish + @return 1 = reply is a valid address , 0 = no further address available + -1 = severe error (e.g. adr_size too small) +*/ +int sg_give_next_adr(burn_drive_enumerator_t *idx, + char adr[], int adr_size, int initialize) +{ + int ret; + + if (initialize == 1) { + ret = sg_init_enumerator(idx); + if (ret<=0) + return ret; + } else if (initialize == -1) { + if(idx->fd != -1) + close(idx->fd); + idx->fd = -1; + return 0; + } + + +try_item:; /* This spaghetti loop keeps the number of tabs small */ + + /* Loop content from old scsi_enumerate_drives() */ + + while (idx->i >= idx->ccb.cdm.num_matches) { + ret = sg_next_enumeration_buffer(idx); + if (ret<=0) + return -1; + if (!((idx->ccb.ccb_h.status == CAM_REQ_CMP) + && (idx->ccb.cdm.status == CAM_DEV_MATCH_MORE)) ) + return 0; + idx->i = 0; + } + + switch (idx->ccb.cdm.matches[idx->i].type) { + case DEV_MATCH_BUS: + break; + case DEV_MATCH_DEVICE: { + struct device_match_result* result; + + result = &(idx->ccb.cdm.matches[i].result.device_result); + if (result->flags & DEV_RESULT_UNCONFIGURED) + idx->skip_device = 1; + else + idx->skip_device = 0; + break; + } + case DEV_MATCH_PERIPH: { + struct periph_match_result* result; + char buf[64]; + + result = &(idx->ccb.cdm.matches[i].result.periph_result); + if (idx->skip_device || + strcmp(result->periph_name, "pass") == 0) + break; + snprintf(buf, sizeof (buf), "/dev/%s%d", + result->periph_name, result->unit_number); + if(adr_size <= strlen(buf) + return -1; + strcpy(adr, buf); + + /* Found next enumerable address */ + return 1; + + } + default: + /* printf(stderr, "unknown match type\n"); */ + break; + } + + (idx->i)++; + goto try_item; /* Regular function exit is return 1 above */ +} + + +/** Brings all available, not-whitelist-banned, and accessible drives into + libburn's list of drives. +*/ +int scsi_enumerate_drives(void) +{ + burn_drive_enumerator_t idx; + int initialize = 1; + char buf[64]; + + while(1) { + ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); + initialize = 0; + if (ret <= 0) + break; + if (burn_drive_is_banned(buf)) + continue; + enumerate_common(buf, idx.result->path_id, idx.result->path_id, + 0, idx.result->target_id, + idx.result->target_lun); + } + sg_give_next_adr(&idx, buf, sizeof(buf), -1); +} + + +/** Tells wether libburn has the given drive in use or exclusively reserved. + If it is "open" then libburn will eventually call sg_release() on it when + it is time to give up usage resp. reservation. +*/ +/** Published as burn_drive.drive_is_open() */ +int sg_drive_is_open(struct burn_drive * d) +{ + return (d->cam != NULL); +} + + +/** Opens the drive for SCSI commands and - if burn activities are prone + to external interference on your system - obtains an exclusive access lock + on the drive. (Note: this is not physical tray locking.) + A drive that has been opened with sg_grab() will eventually be handed + over to sg_release() for closing and unreserving. +*/ +int sg_grab(struct burn_drive *d) +{ + int count; + struct cam_device *cam; + + if(d->cam != NULL) + return 0; + + cam = cam_open_device(d->devname, O_RDWR); + if (cam == NULL) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020003, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Could not grab drive", 0/*os_errno*/, 0); + return 0; + } + d->cam = cam; + fcntl(cam->fd, F_SETOWN, getpid()); + d->released = 0; + return 1; +} + + +/** PORTING: Is mainly about the call to sg_close_drive() and wether it + implements the demanded functionality. +*/ +/** Gives up the drive for SCSI commands and releases eventual access locks. + (Note: this is not physical tray locking.) +*/ +int sg_release(struct burn_drive *d) +{ + if (d->cam == NULL) { + burn_print(1, "release an ungrabbed drive. die\n"); + return 0; + } + sg_close_drive(d); + return 0; +} + + +/** Sends a SCSI command to the drive, receives reply and evaluates wether + the command succeeded or shall be retried or finally failed. + Returned SCSI errors shall not lead to a return value indicating failure. + The callers get notified by c->error. An SCSI failure which leads not to + a retry shall be notified via scsi_notify_error(). + The Libburn_log_sg_commandS facility might be of help when problems with + a drive have to be examined. It shall stay disabled for normal use. + @return: 1 success , <=0 failure +*/ +int sg_issue_command(struct burn_drive *d, struct command *c) +{ + int done = 0; + int err; + union ccb *ccb; + + if (d->cam == NULL) { + c->error = 0; + return 0; + } + + c->error = 0; + + ccb = cam_getccb(d->cam); + cam_fill_csio(&ccb->csio, + 1, /* retries */ + NULL, /* cbfncp */ + CAM_DEV_QFRZDIS, /* flags */ + MSG_SIMPLE_Q_TAG, /* tag_action */ + NULL, /* data_ptr */ + 0, /* dxfer_len */ + sizeof (ccb->csio.sense_data), /* sense_len */ + 0, /* cdb_len */ + 30*1000); /* timeout */ + switch (c->dir) { + case TO_DRIVE: + ccb->csio.ccb_h.flags |= CAM_DIR_OUT; + break; + case FROM_DRIVE: + ccb->csio.ccb_h.flags |= CAM_DIR_IN; + break; + case NO_TRANSFER: + ccb->csio.ccb_h.flags |= CAM_DIR_NONE; + break; + } + + ccb->csio.cdb_len = c->oplen; + memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen); + + memset(&ccb->csio.sense_data, 0, sizeof (ccb->csio.sense_data)); + + if (c->page) { + ccb->csio.data_ptr = c->page->data; + if (c->dir == FROM_DRIVE) { + ccb->csio.dxfer_len = BUFFER_SIZE; +/* touch page so we can use valgrind */ + memset(c->page->data, 0, BUFFER_SIZE); + } else { + + /* ts A61115: removed a ssert() */ + if(c->page->bytes <= 0) + return 0; + + ccb->csio.dxfer_len = c->page->bytes; + } + } else { + ccb->csio.data_ptr = NULL; + ccb->csio.dxfer_len = 0; + } + + do { + err = cam_send_ccb(d->cam, ccb); + if (err == -1) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x0002010c, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Failed to transfer command to drive", + errno, 0); + cam_freeccb(ccb); + sg_close_drive(d); + d->released = 1; + d->busy = BURN_DRIVE_IDLE; + c->error = 1; + return -1; + } + /* XXX */ + memcpy(c->sense, &ccb->csio.sense_data, ccb->csio.sense_len); + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + if (!c->retry) { + c->error = 1; + cam_freeccb(ccb); + return 1; + } + switch (scsi_error(d, c->sense, 0)) { + case RETRY: + done = 0; + break; + case FAIL: + done = 1; + c->error = 1; + break; + } + } else { + done = 1; + } + } while (!done); + cam_freeccb(ccb); + return 1; +} + + +/** Tries to obtain SCSI address parameters. + @return 1 is success , 0 is failure +*/ +int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, + int *target_no, int *lun_no) +{ + burn_drive_enumerator_t idx; + int initialize = 1; + char buf[64]; + struct periph_match_result* result; + + while(1) { + ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); + initialize = 0; + if (ret <= 0) + break; + if (strcmp(adr, buf) != 0) + continue; + result = &(idx->ccb.cdm.matches[i].result.periph_result); + *bus_no = result->path_id; + *host_no = result->path_id; + *channel_no = 0; + *target_no = result->target_id + *lun_no = result->target_lun; + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return 1; + } + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return (0); +} + + +/** Tells wether a text is a persistent address as listed by the enumeration + functions. +*/ +int sg_is_enumerable_adr(char* adr) +{ + burn_drive_enumerator_t idx; + int initialize = 1; + char buf[64]; + + while(1) { + ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); + initialize = 0; + if (ret <= 0) + break; + if (strcmp(adr, buf) == 0) { + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return 1; + } + } + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return (0); +} + diff --git a/libburn/tags/ZeroThreeZero/libburn/sg-freebsd.c b/libburn/tags/ZeroThreeZero/libburn/sg-freebsd.c new file mode 100644 index 00000000..8f5978da --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/sg-freebsd.c @@ -0,0 +1,697 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + + + +/* Revives old enumerate_common(). New version delegates much work + to methods in drive, mmc, spc, and sbc . +*/ +#define Scsi_freebsd_make_own_enumeratE 1 + + +/* Revives old scsi_enumerate_drives(). New version delegates most work to + sg_give_next_adr(). +*/ +#define Scsi_freebsd_old_scsi_enumeratE 1 + + +#include <assert.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/poll.h> +#include <camlib.h> +#include <cam/scsi/scsi_message.h> +#include <cam/scsi/scsi_pass.h> + +#include <err.h> /* XXX */ + + +#include "transport.h" +#include "drive.h" +#include "sg.h" +#include "spc.h" +#include "mmc.h" +#include "sbc.h" +#include "debug.h" +#include "toc.h" +#include "util.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + +static void enumerate_common(char *fname, int bus_no, int host_no, + int channel_no, int target_no, int lun_no); + +/* ts A51221 */ +int burn_drive_is_banned(char *device_address); + + +/* ts A60821 + <<< debug: for tracing calls which might use open drive fds */ +int mmc_function_spy(char * text); + + +#ifdef Scsi_freebsd_old_scsi_enumeratE + +int sg_give_next_adr(burn_drive_enumerator_t *idx, + char adr[], int adr_size, int initialize) +{ + return (0); +} + +int sg_is_enumerable_adr(char* adr) +{ + return (0); +} + +int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, + int *target_no, int *lun_no) +{ + return (0); +} + +#else /* Scsi_freebsd_old_scsi_enumeratE */ + +/* ts A61021 : Moved most code from scsi_enumerate_drives under + sg_give_next_adr() */ +/* Some helper functions for scsi_give_next_adr() */ + +static int sg_init_enumerator(burn_drive_enumerator_t *idx) +{ + idx->skip_device = 0; + + if ((idx->fd = open(XPT_DEVICE, O_RDWR)) == -1) { + warn("couldn't open %s", XPT_DEVICE); + return -1; + } + + bzero(&(idx->ccb), sizeof(union ccb)); + + idx->ccb.ccb_h.path_id = CAM_XPT_PATH_ID; + idx->ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; + idx->ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; + + idx->ccb.ccb_h.func_code = XPT_DEV_MATCH; + idx->bufsize = sizeof(struct dev_match_result) * 100; + idx->ccb.cdm.match_buf_len = idx->bufsize; + idx->ccb.cdm.matches = (struct dev_match_result *)malloc(idx->bufsize); + if (idx->ccb.cdm.matches == NULL) { + warnx("can't malloc memory for matches"); + close(idx->fd); + return -1; + } + idx->ccb.cdm.num_matches = 0; + idx->i = idx->ccb.cdm.num_matches; /* to trigger buffer load */ + + /* + * We fetch all nodes, since we display most of them in the default + * case, and all in the verbose case. + */ + idx->ccb.cdm.num_patterns = 0; + idx->ccb.cdm.pattern_buf_len = 0; + + return 1; +} + + +static int sg_next_enumeration_buffer(burn_drive_enumerator_t *idx) +{ + /* + * We do the ioctl multiple times if necessary, in case there are + * more than 100 nodes in the EDT. + */ + if (ioctl(idx->fd, CAMIOCOMMAND, &(idx->ccb)) == -1) { + warn("error sending CAMIOCOMMAND ioctl"); + return -1; + } + + if ((idx->ccb.ccb_h.status != CAM_REQ_CMP) + || ((idx->ccb.cdm.status != CAM_DEV_MATCH_LAST) + && (idx->ccb.cdm.status != CAM_DEV_MATCH_MORE))) { + warnx("got CAM error %#x, CDM error %d\n", + idx->ccb.ccb_h.status, idx->ccb.cdm.status); + return -1; + } + return 1; +} + + +/** Returns the next index number and the next enumerated drive address. + @param idx An opaque handle. Make no own theories about it. + @param adr Takes the reply + @param adr_size Gives maximum size of reply including final 0 + @param initialize 1 = start new, + 0 = continue, use no other values for now + -1 = finish + @return 1 = reply is a valid address , 0 = no further address available + -1 = severe error (e.g. adr_size too small) +*/ +int sg_give_next_adr(burn_drive_enumerator_t *idx, + char adr[], int adr_size, int initialize) +{ + int ret; + + if (initialize == 1) { + ret = sg_init_enumerator(idx); + if (ret<=0) + return ret; + } else if (initialize == -1) { + if(idx->fd != -1) + close(idx->fd); + idx->fd = -1; + return 0; + } + + +try_item:; /* This spaghetti loop keeps the number of tabs small */ + + /* Loop content from old scsi_enumerate_drives() */ + + while (idx->i >= idx->ccb.cdm.num_matches) { + ret = sg_next_enumeration_buffer(idx); + if (ret<=0) + return -1; + if (!((idx->ccb.ccb_h.status == CAM_REQ_CMP) + && (idx->ccb.cdm.status == CAM_DEV_MATCH_MORE)) ) + return 0; + idx->i = 0; + } + + switch (idx->ccb.cdm.matches[idx->i].type) { + case DEV_MATCH_BUS: + break; + case DEV_MATCH_DEVICE: { + struct device_match_result* result; + + result = &(idx->ccb.cdm.matches[i].result.device_result); + if (result->flags & DEV_RESULT_UNCONFIGURED) + idx->skip_device = 1; + else + idx->skip_device = 0; + break; + } + case DEV_MATCH_PERIPH: { + struct periph_match_result* result; + char buf[64]; + + result = &(idx->ccb.cdm.matches[i].result.periph_result); + if (idx->skip_device || + strcmp(result->periph_name, "pass") == 0) + break; + snprintf(buf, sizeof (buf), "/dev/%s%d", + result->periph_name, result->unit_number); + if(adr_size <= strlen(buf) + return -1; + strcpy(adr, buf); + + /* Found next enumerable address */ + return 1; + + } + default: + /* printf(stderr, "unknown match type\n"); */ + break; + } + + (idx->i)++; + goto try_item; /* Regular function exit is return 1 above */ +} + + +int sg_is_enumerable_adr(char* adr) +{ + burn_drive_enumerator_t idx; + int initialize = 1; + char buf[64]; + + while(1) { + ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); + initialize = 0; + if (ret <= 0) + break; + if (strcmp(adr, buf) == 0) { + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return 1; + } + } + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return (0); +} + + +/** Try to obtain SCSI address parameters. + @return 1 is success , 0 is failure +*/ +int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, + int *target_no, int *lun_no) +{ + burn_drive_enumerator_t idx; + int initialize = 1; + char buf[64]; + struct periph_match_result* result; + + while(1) { + ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); + initialize = 0; + if (ret <= 0) + break; + if (strcmp(adr, buf) != 0) + continue; + result = &(idx->ccb.cdm.matches[i].result.periph_result); + *bus_no = result->path_id; + *host_no = result->path_id; + *channel_no = 0; + *target_no = result->target_id + *lun_no = result->target_lun; + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return 1; + } + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return (0); +} + +#endif /* ! Scsi_freebsd_old_scsi_enumeratE */ + + +int sg_close_drive(struct burn_drive * d) +{ + if (d->cam != NULL) { + cam_close_device(d->cam); + d->cam = NULL; + } + return 0; +} + +int sg_drive_is_open(struct burn_drive * d) +{ + return (d->cam != NULL); +} + +int scsi_enumerate_drives(void) +{ + +#ifdef Scsi_freebsd_old_scsi_enumeratE + + union ccb ccb; + int bufsize, fd; + unsigned int i; + int skip_device = 0; + + if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) { + warn("couldn't open %s", XPT_DEVICE); + return; + } + + bzero(&ccb, sizeof(union ccb)); + + ccb.ccb_h.path_id = CAM_XPT_PATH_ID; + ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; + ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; + + ccb.ccb_h.func_code = XPT_DEV_MATCH; + bufsize = sizeof(struct dev_match_result) * 100; + ccb.cdm.match_buf_len = bufsize; + ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); + if (ccb.cdm.matches == NULL) { + warnx("can't malloc memory for matches"); + close(fd); + return; + } + ccb.cdm.num_matches = 0; + + /* + * We fetch all nodes, since we display most of them in the default + * case, and all in the verbose case. + */ + ccb.cdm.num_patterns = 0; + ccb.cdm.pattern_buf_len = 0; + + /* + * We do the ioctl multiple times if necessary, in case there are + * more than 100 nodes in the EDT. + */ + do { + if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { + warn("error sending CAMIOCOMMAND ioctl"); + break; + } + + if ((ccb.ccb_h.status != CAM_REQ_CMP) + || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) + && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { + warnx("got CAM error %#x, CDM error %d\n", + ccb.ccb_h.status, ccb.cdm.status); + break; + } + + for (i = 0; i < ccb.cdm.num_matches; i++) { + switch (ccb.cdm.matches[i].type) { + case DEV_MATCH_BUS: + break; + case DEV_MATCH_DEVICE: { + struct device_match_result* result; + + result = &ccb.cdm.matches[i].result.device_result; + + if (result->flags & DEV_RESULT_UNCONFIGURED) + skip_device = 1; + else + skip_device = 0; + + break; + } + case DEV_MATCH_PERIPH: { + struct periph_match_result* result; + char buf[64]; + + result = &ccb.cdm.matches[i].result.periph_result; + if (skip_device || strcmp(result->periph_name, "pass") == 0) + break; + snprintf(buf, sizeof (buf), "/dev/%s%d", result->periph_name, result->unit_number); + /* ts A51221 */ + if (burn_drive_is_banned(buf)) + break; + + enumerate_common(buf, result->path_id, result->path_id, 0, + result->target_id, result->target_lun); + break; + } + default: + fprintf(stdout, "unknown match type\n"); + break; + } + } + + } while ((ccb.ccb_h.status == CAM_REQ_CMP) + && (ccb.cdm.status == CAM_DEV_MATCH_MORE)); + + close(fd); + +#else /* Scsi_freebsd_old_scsi_enumeratE */ + + burn_drive_enumerator_t idx; + int initialize = 1; + char buf[64]; + + while(1) { + ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); + initialize = 0; + if (ret <= 0) + break; + if (burn_drive_is_banned(buf)) + continue; + enumerate_common(buf, idx.result->path_id, idx.result->path_id, + 0, idx.result->target_id, + idx.result->target_lun); + } + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + +#endif /* ! Scsi_freebsd_old_scsi_enumeratE */ + +} + + +#ifdef Scsi_freebsd_make_own_enumeratE + +/* ts A61021: The old version which mixes SCSI and operating system adapter +*/ +static void enumerate_common(char *fname, int bus_no, int host_no, + int channel_no, int target_no, int lun_no) +{ + struct burn_drive *t; + struct burn_drive out; + + /* ts A60923 */ + out.bus_no = bus_no; + out.host = host_no; + out.id = target_no; + out.channel = channel_no; + out.lun = lun_no; + + out.devname = burn_strdup(fname); + out.cam = NULL; + + out.start_lba= -2000000000; + out.end_lba= -2000000000; + out.read_atip = mmc_read_atip; + + out.grab = sg_grab; + out.release = sg_release; + out.drive_is_open= sg_drive_is_open; + out.issue_command = sg_issue_command; + out.getcaps = spc_getcaps; + out.released = 1; + out.status = BURN_DISC_UNREADY; + + out.eject = sbc_eject; + out.load = sbc_load; + out.lock = spc_prevent; + out.unlock = spc_allow; + out.read_disc_info = spc_sense_write_params; + out.get_erase_progress = spc_get_erase_progress; + out.test_unit_ready = spc_test_unit_ready; + out.probe_write_modes = spc_probe_write_modes; + out.read_toc = mmc_read_toc; + out.write = mmc_write; + out.erase = mmc_erase; + out.read_sectors = mmc_read_sectors; + out.perform_opc = mmc_perform_opc; + out.set_speed = mmc_set_speed; + out.send_parameters = spc_select_error_params; + out.send_write_parameters = spc_select_write_params; + out.send_cue_sheet = mmc_send_cue_sheet; + out.sync_cache = mmc_sync_cache; + out.get_nwa = mmc_get_nwa; + out.close_disc = mmc_close_disc; + out.close_session = mmc_close_session; + out.close_track_session = mmc_close; + out.read_buffer_capacity = mmc_read_buffer_capacity; + out.idata = malloc(sizeof(struct burn_scsi_inquiry_data)); + out.idata->valid = 0; + out.mdata = malloc(sizeof(struct scsi_mode_data)); + out.mdata->valid = 0; + if (out.idata == NULL || out.mdata == NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020108, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Could not allocate new drive object", 0, 0); + return; + } + memset(&out.params, 0, sizeof(struct params)); + t = burn_drive_register(&out); + +/* ts A60821 + <<< debug: for tracing calls which might use open drive fds */ + mmc_function_spy("enumerate_common : -------- doing grab"); + +/* try to get the drive info */ + if (t->grab(t)) { + burn_print(2, "getting drive info\n"); + t->getcaps(t); + t->unlock(t); + t->released = 1; + } else { + burn_print(2, "unable to grab new located drive\n"); + } + +/* ts A60821 + <<< debug: for tracing calls which might use open drive fds */ + mmc_function_spy("enumerate_common : ----- would release "); + +} + +#else /* Scsi_freebsd_make_own_enumeratE */ + +/* The new, more concise version of enumerate_common */ +static void enumerate_common(char *fname, int bus_no, int host_no, + int channel_no, int target_no, int lun_no) +{ + int ret; + struct burn_drive out; + + /* General libburn drive setup */ + burn_setup_drive(&out, fname); + + /* This transport adapter uses SCSI-family commands and models + (seems the adapter would know better than its boss, if ever) */ + ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no, + target_no, lun_no, 0); + if (ret<=0) + return; + + /* Operating system adapter is CAM */ + /* Adapter specific handles and data */ + out.cam = NULL; + /* Adapter specific functions */ + out.grab = sg_grab; + out.release = sg_release; + out.drive_is_open = sg_drive_is_open; + out.issue_command = sg_issue_command; + + /* Finally register drive and inquire drive information */ + burn_drive_finish_enum(&out); +} + +#endif /* ! Scsi_freebsd_make_own_enumeratE */ + +/* ts A61021: do not believe this: + we use the sg reference count to decide whether we can use the + drive or not. + if refcount is not one, drive is open somewhere else. +*/ +int sg_grab(struct burn_drive *d) +{ + int count; + struct cam_device *cam; + + mmc_function_spy("sg_grab"); + + assert(d->cam == NULL); + + cam = cam_open_device(d->devname, O_RDWR); + if (cam == NULL) { + libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Could not grab drive", 0/*os_errno*/, 0); + return 0; + } +/* er = ioctl(fd, SG_GET_ACCESS_COUNT, &count);*/ + count = 1; + if (1 == count) { + d->cam = cam; + fcntl(cam->fd, F_SETOWN, getpid()); + d->released = 0; + return 1; + } + burn_print(1, "could not acquire drive - already open\n"); + sg_close_drive(d); + return 0; +} + + +/* + non zero return means you still have the drive and it's not + in a state to be released? (is that even possible?) +*/ + +int sg_release(struct burn_drive *d) +{ + mmc_function_spy("sg_release"); + + if (d->cam == NULL) { + burn_print(1, "release an ungrabbed drive. die\n"); + return 0; + } + + mmc_function_spy("sg_release ----------- closing."); + + sg_close_drive(d); + return 0; +} + +int sg_issue_command(struct burn_drive *d, struct command *c) +{ + int done = 0; + int err; + union ccb *ccb; + + char buf[161]; + snprintf(buf, sizeof (buf), "sg_issue_command d->cam=%p d->released=%d", + (void*)d->cam, d->released); + mmc_function_spy(buf); + + if (d->cam == NULL) { + c->error = 0; + return 0; + } + + c->error = 0; + + ccb = cam_getccb(d->cam); + cam_fill_csio(&ccb->csio, + 1, /* retries */ + NULL, /* cbfncp */ + CAM_DEV_QFRZDIS, /* flags */ + MSG_SIMPLE_Q_TAG, /* tag_action */ + NULL, /* data_ptr */ + 0, /* dxfer_len */ + sizeof (ccb->csio.sense_data), /* sense_len */ + 0, /* cdb_len */ + 30*1000); /* timeout */ + switch (c->dir) { + case TO_DRIVE: + ccb->csio.ccb_h.flags |= CAM_DIR_OUT; + break; + case FROM_DRIVE: + ccb->csio.ccb_h.flags |= CAM_DIR_IN; + break; + case NO_TRANSFER: + ccb->csio.ccb_h.flags |= CAM_DIR_NONE; + break; + } + + ccb->csio.cdb_len = c->oplen; + memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen); + + memset(&ccb->csio.sense_data, 0, sizeof (ccb->csio.sense_data)); + + if (c->page) { + ccb->csio.data_ptr = c->page->data; + if (c->dir == FROM_DRIVE) { + ccb->csio.dxfer_len = BUFFER_SIZE; +/* touch page so we can use valgrind */ + memset(c->page->data, 0, BUFFER_SIZE); + } else { + assert(c->page->bytes > 0); + ccb->csio.dxfer_len = c->page->bytes; + } + } else { + ccb->csio.data_ptr = NULL; + ccb->csio.dxfer_len = 0; + } + + do { + err = cam_send_ccb(d->cam, ccb); + if (err == -1) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x0002010c, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Failed to transfer command to drive", + errno, 0); + cam_freeccb(ccb); + sg_close_drive(d); + d->released = 1; + d->busy = BURN_DRIVE_IDLE; + c->error = 1; + return -1; + } + /* XXX */ + memcpy(c->sense, &ccb->csio.sense_data, ccb->csio.sense_len); + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + if (!c->retry) { + c->error = 1; + cam_freeccb(ccb); + return 1; + } + switch (scsi_error(d, c->sense, 0)) { + case RETRY: + done = 0; + break; + case FAIL: + done = 1; + c->error = 1; + break; + } + } else { + done = 1; + } + } while (!done); + cam_freeccb(ccb); + return 1; +} + diff --git a/libburn/tags/ZeroThreeZero/libburn/sg-linux.c b/libburn/tags/ZeroThreeZero/libburn/sg-linux.c new file mode 100644 index 00000000..50bde11c --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/sg-linux.c @@ -0,0 +1,1104 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* + +This is the main operating system dependent SCSI part of libburn. It implements +the transport level aspects of SCSI control and command i/o. + +Present implementation: Linux SCSI Generic (sg) + + +PORTING: + +Porting libburn typically will consist of adding a new operating system case +to the following switcher files: + os.h Operating system specific libburn definitions and declarations. + sg.c Operating system dependent transport level modules. +and of deriving the following system specific files from existing examples: + os-*.h Included by os.h. You will need some general system knowledge + about signals and knowledge about the storage object needs of your + transport level module sg-*.c. + + sg-*.c This source module. You will need special system knowledge about + how to detect all potentially available drives, how to open them, + eventually how to exclusively reserve them, how to perform + SCSI transactions, how to inquire the (pseudo-)SCSI driver. + You will not need to care about CD burning, MMC or other high-level + SCSI aspects. + +Said sg-*.c operations are defined by a public function interface, which has +to be implemented in a way that provides libburn with the desired services: + +sg_give_next_adr() iterates over the set of potentially useful drive + address strings. + +scsi_enumerate_drives() brings all available, not-whitelist-banned, and + accessible drives into libburn's list of drives. + +sg_drive_is_open() tells wether libburn has the given drive in use. + +sg_grab() opens the drive for SCSI commands and ensures + undisturbed access. + +sg_release() closes a drive opened by sg_grab() + +sg_issue_command() sends a SCSI command to the drive, receives reply, + and evaluates wether the command succeeded or shall + be retried or finally failed. + +sg_obtain_scsi_adr() tries to obtain SCSI address parameters. + + +Porting hints are marked by the text "PORTING:". +Send feedback to libburn-hackers@pykix.org . + +Hint: You should also look into sg-freebsd-port.c, which is a younger and + in some aspects more straightforward implementation of this interface. + +*/ + + +/** PORTING : ------- OS dependent headers and definitions ------ */ + +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <string.h> +#include <sys/poll.h> +#include <linux/hdreg.h> +#include <stdlib.h> +#include <scsi/sg.h> +#include <scsi/scsi.h> + +/* ts A61211 : to recognize CD devices on /dev/sr* */ +#include <linux/cdrom.h> + + +/* ts A61211 : preparing for exploration of recent Linux ATA adventures */ +/** PORTING : Device file families for bus scanning and drive access. + Both device families must support the following ioctls: + SG_IO, + SG_GET_SCSI_ID + SCSI_IOCTL_GET_BUS_NUMBER + SCSI_IOCTL_GET_IDLUN + as well as mutual exclusively locking with open(O_EXCL). + If a device family is left empty, then it will not be used. + + To avoid misunderstandings: both families are used via identical + transport methods as soon as a device file is accepted as CD drive + by the family specific function <family>_enumerate(). + One difference remains throughout usage: Host,Channel,Id,Lun and Bus + address parameters of ATA devices are considered invalid. +*/ + +/* Set this to 1 in order to get on stderr messages from sg_enumerate() */ +static int linux_sg_enumerate_debug = 0; + + +/* The device file family to use for (emulated) generic SCSI transport. + This must be a printf formatter with one single placeholder for int + in the range of 0 to 31 . The resulting addresses must provide SCSI + address parameters Host, Channel, Id, Lun and also Bus. + E.g.: "/dev/sg%d" +*/ +/* NEW INFO: If hard disks at /dev/sr allow ioctl(CDROM_DRIVE_STATUS), they + are in danger. + If you want it less dangerous: + #undef CDROM_DRIVE_STATUS + but then you might need linux_sg_accept_any_type = 1 which + is _more dangerous_. +*/ +/* !!! DO NOT SET TO sr%d UNLESS YOU PROTECTED ALL INDISPENSIBLE DEVICES + by chmod -rw . A test wether non-CD devices are properly excluded would + be well needed though. Heroic disks, scanners, etc. wanted !!! */ +static char linux_sg_device_family[80] = {"/dev/sg%d"}; + + +/* Set this to 1 in order to accept any TYPE_* (see scsi/scsi.h) */ +/* NEW INFO: Try with 0 first. There is hope via CDROM_DRIVE_STATUS. */ +/* !!! DO NOT SET TO 1 UNLESS YOU PROTECTED ALL INDISPENSIBLE DEVICES + chmod -rw !!! */ +static int linux_sg_accept_any_type = 0; + + +/* The device file family to use for SCSI transport over ATA. + This must be a printf formatter with one single placeholder for a + _single_ char in the range of 'a' to 'z'. This placeholder _must_ be + at the end of the formatter string. + E.g. "/dev/hd%c" +*/ +static char linux_ata_device_family[80] = {"/dev/hd%c"}; + +/* Set this to 1 in order to get on stderr messages from ata_enumerate() +*/ +static int linux_ata_enumerate_verbous = 0; + + + +/** PORTING : ------ libburn portable headers and definitions ----- */ + +#include "transport.h" +#include "drive.h" +#include "sg.h" +#include "spc.h" +#include "mmc.h" +#include "sbc.h" +#include "debug.h" +#include "toc.h" +#include "util.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + +/* ts A51221 */ +int burn_drive_is_banned(char *device_address); + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Private definitions. Port only if needed by public functions. */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + + +static void enumerate_common(char *fname, int bus_no, int host_no, + int channel_no, int target_no, int lun_no); + + +/* >>> ts A61115 : this needs mending. A Linux aspect shows up in cdrskin. */ +/* ts A60813 : storage objects are in libburn/init.c + wether to use O_EXCL + wether to use O_NOBLOCK with open(2) on devices + wether to take O_EXCL rejection as fatal error */ +extern int burn_sg_open_o_excl; +extern int burn_sg_open_o_nonblock; +extern int burn_sg_open_abort_busy; + + +/* ts A60821 + <<< debug: for tracing calls which might use open drive fds */ +int mmc_function_spy(char * text); + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Private functions. Port only if needed by public functions */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + + +static int sgio_test(int fd) +{ + unsigned char test_ops[] = { 0, 0, 0, 0, 0, 0 }; + sg_io_hdr_t s; + + memset(&s, 0, sizeof(sg_io_hdr_t)); + s.interface_id = 'S'; + s.dxfer_direction = SG_DXFER_NONE; + s.cmd_len = 6; + s.cmdp = test_ops; + s.timeout = 12345; + return ioctl(fd, SG_IO, &s); +} + + +/* ts A60924 */ +static int sg_handle_busy_device(char *fname, int os_errno) +{ + char msg[4096]; + + /* ts A60814 : i saw no way to do this more nicely */ + if (burn_sg_open_abort_busy) { + fprintf(stderr, + "\nlibburn: FATAL : Application triggered abort on busy device '%s'\n", + fname); + + /* ts A61007 */ + abort(); + /* a ssert("drive busy" == "non fatal"); */ + } + + /* ts A60924 : now reporting to libdax_msgs */ + sprintf(msg, "Cannot open busy device '%s'", fname); + libdax_msgs_submit(libdax_messenger, -1, 0x00020001, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_LOW, + msg, os_errno, 0); + return 1; +} + + +/* ts A60926 */ +static int sg_open_drive_fd(char *fname, int scan_mode) +{ + int open_mode = O_RDWR, fd; + char msg[81]; + + /* ts A60813 - A60927 + O_EXCL with devices is a non-POSIX feature + of Linux kernels. Possibly introduced 2002. + Mentioned in "The Linux SCSI Generic (sg) HOWTO" */ + if(burn_sg_open_o_excl) + open_mode |= O_EXCL; + /* ts A60813 + O_NONBLOCK was already hardcoded in ata_ but not in sg_. + There must be some reason for this. So O_NONBLOCK is + default mode for both now. Disable on own risk. */ + if(burn_sg_open_o_nonblock) + open_mode |= O_NONBLOCK; + +/* <<< debugging + fprintf(stderr, + "\nlibburn: experimental: o_excl= %d , o_nonblock= %d, abort_on_busy= %d\n", + burn_sg_open_o_excl,burn_sg_open_o_nonblock,burn_sg_open_abort_busy); + fprintf(stderr, + "libburn: experimental: O_EXCL= %d , O_NONBLOCK= %d\n", + !!(open_mode&O_EXCL),!!(open_mode&O_NONBLOCK)); +*/ + + fd = open(fname, open_mode); + if (fd == -1) { +/* <<< debugging + fprintf(stderr, + "\nlibburn: experimental: fname= %s , errno= %d\n", + fname,errno); +*/ + if (errno == EBUSY) { + sg_handle_busy_device(fname, errno); + return -1; + + } + if (scan_mode) + return -1; + sprintf(msg, "Failed to open device '%s'",fname); + libdax_msgs_submit(libdax_messenger, -1, 0x00020005, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, errno, 0); + return -1; + } + return fd; +} + + +/* ts A60925 : ticket 74 */ +static int sg_close_drive_fd(char *fname, int driveno, int *fd, int sorry) +{ + int ret, os_errno, sevno= LIBDAX_MSGS_SEV_DEBUG; + char msg[4096+100]; + + if(*fd < 0) + return(0); + ret = close(*fd); + *fd = -1337; + if(ret != -1) + return 1; + os_errno= errno; + + if (fname != NULL) + sprintf(msg, "Encountered error when closing drive '%s'", + fname); + else + sprintf(msg, "Encountered error when closing drive"); + + if (sorry) + sevno = LIBDAX_MSGS_SEV_SORRY; + libdax_msgs_submit(libdax_messenger, driveno, 0x00020002, + sevno, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0); + return 0; +} + + +/* ts A60926 */ +static int sg_release_siblings(int sibling_fds[], int *sibling_count) +{ + int i; + char msg[81]; + + for(i= 0; i < *sibling_count; i++) + sg_close_drive_fd(NULL, -1, &(sibling_fds[i]), 0); + if(*sibling_count > 0) { + sprintf(msg, "Closed %d O_EXCL scsi siblings", *sibling_count); + libdax_msgs_submit(libdax_messenger, -1, 0x00020007, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0); + } + *sibling_count = 0; + return 1; +} + + +/* ts A60926 */ +static int sg_close_drive(struct burn_drive *d) +{ + int ret; + + if (!burn_drive_is_open(d)) + return 0; + sg_release_siblings(d->sibling_fds, &(d->sibling_count)); + ret = sg_close_drive_fd(d->devname, d->global_index, &(d->fd), 0); + return ret; +} + + +/* ts A60926 */ +static int sg_open_scsi_siblings(char *path, int driveno, + int sibling_fds[], int *sibling_count, + int host_no, int channel_no, int id_no, int lun_no) +{ + int tld, i, ret, fd, i_bus_no = -1; + int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1; + char msg[161], fname[81]; + + static char tldev[][81]= {"/dev/sr%d", "/dev/scd%d", "/dev/st%d", + "/dev/sg%d", ""}; + + if (linux_sg_device_family[0] == 0) + return 1; + + if(host_no < 0 || id_no < 0 || channel_no < 0 || lun_no < 0) + return(2); + if(*sibling_count > 0) + sg_release_siblings(sibling_fds, sibling_count); + + for (tld = 0; tldev[tld][0] != 0; tld++) { + if (strcmp(tldev[tld], linux_sg_device_family)==0) + continue; + for (i = 0; i < 32; i++) { + sprintf(fname, tldev[tld], i); + ret = sg_obtain_scsi_adr(fname, &i_bus_no, &i_host_no, + &i_channel_no, &i_target_no, &i_lun_no); + if (ret <= 0) + continue; + if (i_host_no != host_no || i_channel_no != channel_no) + continue; + if (i_target_no != id_no || i_lun_no != lun_no) + continue; + + fd = sg_open_drive_fd(fname, 0); + if (fd < 0) + goto failed; + + if (*sibling_count>=LIBBURN_SG_MAX_SIBLINGS) { + sprintf(msg, "Too many scsi siblings of '%s'", + path); + libdax_msgs_submit(libdax_messenger, + driveno, 0x00020006, + LIBDAX_MSGS_SEV_FATAL, + LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); + goto failed; + } + sprintf(msg, "Opened O_EXCL scsi sibling '%s' of '%s'", + fname, path); + libdax_msgs_submit(libdax_messenger, driveno, + 0x00020004, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + sibling_fds[*sibling_count] = fd; + (*sibling_count)++; + } + } + return 1; +failed:; + sg_release_siblings(sibling_fds, sibling_count); + return 0; +} + + +/** Speciality of Linux: detect non-SCSI ATAPI (EIDE) which will from + then on used used via generic SCSI as is done with (emulated) SCSI drives */ +static void ata_enumerate(void) +{ + struct hd_driveid tm; + int i, fd; + char fname[10]; + + if (linux_ata_enumerate_verbous) + fprintf(stderr, "libburn_debug: linux_ata_device_family = %s\n", + linux_ata_device_family); + + if (linux_ata_device_family[0] == 0) + return; + + for (i = 0; i < 26; i++) { + sprintf(fname, linux_ata_device_family, 'a' + i); + if (linux_ata_enumerate_verbous) + fprintf(stderr, "libburn_debug: %s : ", fname); + + /* ts A51221 */ + if (burn_drive_is_banned(fname)) { + if (linux_ata_enumerate_verbous) + fprintf(stderr, "not in whitelist\n"); + continue; + } + fd = sg_open_drive_fd(fname, 1); + if (fd == -1) { + if (linux_ata_enumerate_verbous) + fprintf(stderr,"open failed, errno=%d '%s'\n", + errno, strerror(errno)); + continue; + } + + /* found a drive */ + ioctl(fd, HDIO_GET_IDENTITY, &tm); + + /* not atapi */ + if (!(tm.config & 0x8000) || (tm.config & 0x4000)) { + if (linux_ata_enumerate_verbous) + fprintf(stderr, "not marked as ATAPI\n"); + sg_close_drive_fd(fname, -1, &fd, 0); + continue; + } + + /* if SG_IO fails on an atapi device, we should stop trying to + use hd* devices */ + if (sgio_test(fd) == -1) { + if (linux_ata_enumerate_verbous) + fprintf(stderr, + "FATAL: sgio_test() failed: errno=%d '%s'\n", + errno, strerror(errno)); + sg_close_drive_fd(fname, -1, &fd, 0); + return; + } + if (sg_close_drive_fd(fname, -1, &fd, 1) <= 0) { + if (linux_ata_enumerate_verbous) + fprintf(stderr, + "cannot close properly, errno=%d '%s'\n", + errno, strerror(errno)); + continue; + } + if (linux_ata_enumerate_verbous) + fprintf(stderr, "accepting as drive without SCSI address\n"); + enumerate_common(fname, -1, -1, -1, -1, -1); + } +} + + +/** Detects (probably emulated) SCSI drives */ +static void sg_enumerate(void) +{ + struct sg_scsi_id sid; + int i, fd, sibling_fds[LIBBURN_SG_MAX_SIBLINGS], sibling_count= 0, ret; + int sid_ret = 0; + int bus_no= -1, host_no= -1, channel_no= -1, target_no= -1, lun_no= -1; + char fname[10]; + + if (linux_sg_enumerate_debug) + fprintf(stderr, "libburn_debug: linux_sg_device_family = %s\n", + linux_sg_device_family); + + if (linux_sg_device_family[0] == 0) + return; + + for (i = 0; i < 32; i++) { + sprintf(fname, linux_sg_device_family, i); + + if (linux_sg_enumerate_debug) + fprintf(stderr, "libburn_debug: %s : ", fname); + + /* ts A51221 */ + if (burn_drive_is_banned(fname)) { + if (linux_sg_enumerate_debug) + fprintf(stderr, "not in whitelist\n"); + continue; + } + + /* ts A60927 */ + fd = sg_open_drive_fd(fname, 1); + if (fd == -1) { + if (linux_sg_enumerate_debug) + fprintf(stderr, "open failed, errno=%d '%s'\n", + errno, strerror(errno)); + continue; + } + + /* found a drive */ + sid_ret = ioctl(fd, SG_GET_SCSI_ID, &sid); + if (sid_ret == -1) { + sid.scsi_id = -1; /* mark SCSI address as invalid */ + if(linux_sg_enumerate_debug) + fprintf(stderr, + "ioctl(SG_GET_SCSI_ID) failed, errno=%d '%s' , ", + errno, strerror(errno)); + +#ifdef CDROM_DRIVE_STATUS + /* ts A61211 : not widening old acceptance range */ + if (strcmp(linux_sg_device_family,"/dev/sg%d") != 0) { + /* http://developer.osdl.org/dev/robustmutexes/ + src/fusyn.hg/Documentation/ioctl/cdrom.txt */ + sid_ret = ioctl(fd, CDROM_DRIVE_STATUS, 0); + if(linux_sg_enumerate_debug) + fprintf(stderr, + "ioctl(CDROM_DRIVE_STATUS) = %d , ", + sid_ret); + if (sid_ret != -1 && sid_ret != CDS_NO_INFO) + sid.scsi_type = TYPE_ROM; + else + sid_ret = -1; + } +#endif /* CDROM_DRIVE_STATUS */ + + } + +#ifdef SCSI_IOCTL_GET_BUS_NUMBER + /* Hearsay A61005 */ + if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus_no) == -1) + bus_no = -1; +#endif + + if (sg_close_drive_fd(fname, -1, &fd, + sid.scsi_type == TYPE_ROM ) <= 0) { + if (linux_sg_enumerate_debug) + fprintf(stderr, + "cannot close properly, errno=%d '%s'\n", + errno, strerror(errno)); + continue; + } + if ( (sid_ret == -1 || sid.scsi_type != TYPE_ROM) + && !linux_sg_accept_any_type) { + if (linux_sg_enumerate_debug) + fprintf(stderr, "sid.scsi_type = %d (!= TYPE_ROM)\n", + sid.scsi_type); + continue; + } + + if (sid_ret == -1 || sid.scsi_id < 0) { + /* ts A61211 : employ a more general ioctl */ + ret = sg_obtain_scsi_adr(fname, &bus_no, &host_no, + &channel_no, &target_no, &lun_no); + if (ret>0) { + sid.host_no = host_no; + sid.channel = channel_no; + sid.scsi_id = target_no; + sid.lun = lun_no; + } else { + if (linux_sg_enumerate_debug) + fprintf(stderr, + "sg_obtain_scsi_adr() failed\n"); + continue; + } + } + + /* ts A60927 : trying to do locking with growisofs */ + if(burn_sg_open_o_excl>1) { + ret = sg_open_scsi_siblings( + fname, -1, sibling_fds, &sibling_count, + sid.host_no, sid.channel, + sid.scsi_id, sid.lun); + if (ret<=0) { + if (linux_sg_enumerate_debug) + fprintf(stderr, "cannot lock siblings\n"); + sg_handle_busy_device(fname, 0); + continue; + } + /* the final occupation will be done in sg_grab() */ + sg_release_siblings(sibling_fds, &sibling_count); + } +#ifdef SCSI_IOCTL_GET_BUS_NUMBER + if(bus_no == -1) + bus_no = 1000 * (sid.host_no + 1) + sid.channel; +#else + bus_no = sid.host_no; +#endif + if (linux_sg_enumerate_debug) + fprintf(stderr, "accepting as SCSI %d,%d,%d,%d bus=%d\n", + sid.host_no, sid.channel, sid.scsi_id, sid.lun, + bus_no); + enumerate_common(fname, bus_no, sid.host_no, sid.channel, + sid.scsi_id, sid.lun); + } +} + + +/* ts A61115 */ +/* ----------------------------------------------------------------------- */ +/* PORTING: Private functions which contain publicly needed functionality. */ +/* Their portable part must be performed. So it is probably best */ +/* to replace the non-portable part and to call these functions */ +/* in your port, too. */ +/* ----------------------------------------------------------------------- */ + + +/** Wraps a detected drive into libburn structures and hands it over to + libburn drive list. +*/ +/* ts A60923 - A61005 : introduced new SCSI parameters */ +/* ts A61021 : moved non os-specific code to spc,sbc,mmc,drive */ +static void enumerate_common(char *fname, int bus_no, int host_no, + int channel_no, int target_no, int lun_no) +{ + int ret, i; + struct burn_drive out; + + /* General libburn drive setup */ + burn_setup_drive(&out, fname); + + /* This transport adapter uses SCSI-family commands and models + (seems the adapter would know better than its boss, if ever) */ + ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no, + target_no, lun_no, 0); + if (ret<=0) + return; + + /* PORTING: ------------------- non portable part --------------- */ + + /* Operating system adapter is Linux Generic SCSI (sg) */ + /* Adapter specific handles and data */ + out.fd = -1337; + out.sibling_count = 0; + for(i= 0; i<LIBBURN_SG_MAX_SIBLINGS; i++) + out.sibling_fds[i] = -1337; + + /* PORTING: ---------------- end of non portable part ------------ */ + + /* Adapter specific functions with standardized names */ + out.grab = sg_grab; + out.release = sg_release; + out.drive_is_open= sg_drive_is_open; + out.issue_command = sg_issue_command; + + /* Finally register drive and inquire drive information */ + burn_drive_finish_enum(&out); +} + + +/* ts A61115 */ +/* ------------------------------------------------------------------------ */ +/* PORTING: Public functions. These MUST be ported. */ +/* ------------------------------------------------------------------------ */ + + +/** PORTING: + In this Linux implementation, this function mirrors the enumeration + done in sg_enumerate and ata_enumerate(). It would be better to base those + functions on this sg_give_next_adr() but the situation is not inviting. +*/ +/* ts A60922 ticket 33 : called from drive.c */ +/** Returns the next index number and the next enumerated drive address. + The enumeration has to cover all available and accessible drives. It is + allowed to return addresses of drives which are not available but under + some (even exotic) circumstances could be available. It is on the other + hand allowed, only to hand out addresses which can really be used right + in the moment of this call. (This implementation chooses the former.) + @param idx An opaque handle. Make no own theories about it. + @param adr Takes the reply + @param adr_size Gives maximum size of reply including final 0 + @param initialize 1 = start new, + 0 = continue, use no other values for now + -1 = finish + @return 1 = reply is a valid address , 0 = no further address available + -1 = severe error (e.g. adr_size too small) +*/ +int sg_give_next_adr(burn_drive_enumerator_t *idx, + char adr[], int adr_size, int initialize) +{ + /* os-linux.h : typedef int burn_drive_enumerator_t; */ + static int sg_limit = 32, ata_limit = 26; + int baseno = 0; + + if (initialize == -1) + return 0; + + if (linux_sg_device_family[0] == 0) + sg_limit = 0; + if (linux_ata_device_family[0] == 0) + ata_limit = 0; + + if (initialize == 1) + *idx = -1; + (*idx)++; + if (*idx >= sg_limit) + goto next_ata; + if (adr_size < 10) + return -1; + sprintf(adr, linux_sg_device_family, *idx); + return 1; +next_ata:; + baseno += sg_limit; + if (*idx - baseno >= ata_limit) + goto next_nothing; + if (adr_size < 9) + return -1; + sprintf(adr, linux_ata_device_family, 'a' + (*idx - baseno)); + return 1; +next_nothing:; + baseno += ata_limit; + return 0; +} + + +/** Brings all available, not-whitelist-banned, and accessible drives into + libburn's list of drives. +*/ +/** PORTING: + If not stricken with an incompletely unified situation like in Linux + one would rather implement this by a loop calling sg_give_next_adr(). + If needed with your sg_give_next_adr() results, do a test for existence + and accessability. If burn activities are prone to external interference + on your system it is also necessary to obtain exclusive access locks on + the drives. + Hand over each accepted drive to enumerate_common() resp. its replacement + within your port. + + See FreeBSD port sketch sg-freebsd-port.c for such an implementation. +*/ +/* ts A61115: replacing call to sg-implementation internals from drive.c */ +int scsi_enumerate_drives(void) +{ + sg_enumerate(); + ata_enumerate(); + return 1; +} + + +/** Tells wether libburn has the given drive in use or exclusively reserved. + If it is "open" then libburn will eventually call sg_release() on it when + it is time to give up usage resp. reservation. +*/ +/** Published as burn_drive.drive_is_open() */ +int sg_drive_is_open(struct burn_drive * d) +{ + /* a bit more detailed case distinction than needed */ + if (d->fd == -1337) + return 0; + if (d->fd < 0) + return 0; + return 1; +} + + +/** Opens the drive for SCSI commands and - if burn activities are prone + to external interference on your system - obtains an exclusive access lock + on the drive. (Note: this is not physical tray locking.) + A drive that has been opened with sg_grab() will eventually be handed + over to sg_release() for closing and unreserving. +*/ +int sg_grab(struct burn_drive *d) +{ + int fd, os_errno= 0, ret; + + /* ts A60813 */ + int open_mode = O_RDWR; + +/* ts A60821 + <<< debug: for tracing calls which might use open drive fds */ + mmc_function_spy("sg_grab"); + + + /* ts A60813 - A60927 + O_EXCL with devices is a non-POSIX feature + of Linux kernels. Possibly introduced 2002. + Mentioned in "The Linux SCSI Generic (sg) HOWTO". + */ + if(burn_sg_open_o_excl) + open_mode |= O_EXCL; + + /* ts A60813 + O_NONBLOCK was hardcoded here. So it should stay default mode. */ + if(burn_sg_open_o_nonblock) + open_mode |= O_NONBLOCK; + + /* ts A60813 - A60822 + After enumeration the drive fd is probably still open. + -1337 is the initial value of burn_drive.fd and the value after + relase of drive. Unclear why not the official error return + value -1 of open(2) war used. */ + if(! burn_drive_is_open(d)) { + + /* ts A60821 + <<< debug: for tracing calls which might use open drive fds */ + mmc_function_spy("sg_grab ----------- opening"); + + /* ts A60926 */ + if(burn_sg_open_o_excl>1) { + fd = -1; + ret = sg_open_scsi_siblings(d->devname, + d->global_index,d->sibling_fds, + &(d->sibling_count), + d->host, d->channel, d->id, d->lun); + if(ret <= 0) + goto drive_is_in_use; + } + + fd = open(d->devname, open_mode); + os_errno = errno; + } else + fd= d->fd; + + if (fd >= 0) { + d->fd = fd; + fcntl(fd, F_SETOWN, getpid()); + d->released = 0; + return 1; + } + libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Could not grab drive", os_errno, 0); + return 0; + +drive_is_in_use:; + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020003, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Could not grab drive - already in use", 0, 0); + sg_close_drive(d); + d->fd = -1337; + return 0; +} + + +/** PORTING: Is mainly about the call to sg_close_drive() and wether it + implements the demanded functionality. +*/ +/** Gives up the drive for SCSI commands and releases eventual access locks. + (Note: this is not physical tray locking.) +*/ +int sg_release(struct burn_drive *d) +{ + /* ts A60821 + <<< debug: for tracing calls which might use open drive fds */ + mmc_function_spy("sg_release"); + + if (d->fd < 1) { + burn_print(1, "release an ungrabbed drive. die\n"); + return 0; + } + + /* ts A60821 + <<< debug: for tracing calls which might use open drive fds */ + mmc_function_spy("sg_release ----------- closing"); + + sg_close_drive(d); + return 0; +} + + +/** Sends a SCSI command to the drive, receives reply and evaluates wether + the command succeeded or shall be retried or finally failed. + Returned SCSI errors shall not lead to a return value indicating failure. + The callers get notified by c->error. An SCSI failure which leads not to + a retry shall be notified via scsi_notify_error(). + The Libburn_log_sg_commandS facility might be of help when problems with + a drive have to be examined. It shall stay disabled for normal use. + @return: 1 success , <=0 failure +*/ +int sg_issue_command(struct burn_drive *d, struct command *c) +{ + int done = 0, no_c_page = 0; + int err; + sg_io_hdr_t s; +/* +#define Libburn_log_sg_commandS 1 +*/ + +#ifdef Libburn_log_sg_commandS + /* ts A61030 */ + static FILE *fp= NULL; + static int fpcount= 0; + int i; +#endif /* Libburn_log_sg_commandS */ + + /* <<< ts A60821 + debug: for tracing calls which might use open drive fds */ + char buf[161]; + sprintf(buf,"sg_issue_command d->fd= %d d->released= %d\n", + d->fd,d->released); + mmc_function_spy(buf); + +#ifdef Libburn_log_sg_commandS + /* ts A61030 */ + if(fp==NULL) { + fp= fopen("/tmp/libburn_sg_command_log","a"); + fprintf(fp,"\n-----------------------------------------\n"); + } + if(fp!=NULL) { + for(i=0;i<10;i++) + fprintf(fp,"%2.2x ", c->opcode[i]); + fprintf(fp,"\n"); + fpcount++; + } +#endif /* Libburn_log_sg_commandS */ + + + /* ts A61010 : with no fd there is no chance to send an ioctl */ + if (d->fd < 0) { + c->error = 1; + return 0; + } + + c->error = 0; + memset(&s, 0, sizeof(sg_io_hdr_t)); + + s.interface_id = 'S'; + + if (c->dir == TO_DRIVE) + s.dxfer_direction = SG_DXFER_TO_DEV; + else if (c->dir == FROM_DRIVE) + s.dxfer_direction = SG_DXFER_FROM_DEV; + else if (c->dir == NO_TRANSFER) { + s.dxfer_direction = SG_DXFER_NONE; + + /* ts A61007 */ + /* a ssert(!c->page); */ + no_c_page = 1; + } + s.cmd_len = c->oplen; + s.cmdp = c->opcode; + s.mx_sb_len = 32; + s.sbp = c->sense; + memset(c->sense, 0, sizeof(c->sense)); + s.timeout = 200000; + if (c->page && !no_c_page) { + s.dxferp = c->page->data; + if (c->dir == FROM_DRIVE) { + s.dxfer_len = BUFFER_SIZE; +/* touch page so we can use valgrind */ + memset(c->page->data, 0, BUFFER_SIZE); + } else { + + /* ts A61010 */ + /* a ssert(c->page->bytes > 0); */ + if (c->page->bytes <= 0) { + c->error = 1; + return 0; + } + + s.dxfer_len = c->page->bytes; + } + } else { + s.dxferp = NULL; + s.dxfer_len = 0; + } + s.usr_ptr = c; + + do { + err = ioctl(d->fd, SG_IO, &s); + + /* ts A61010 */ + /* a ssert(err != -1); */ + if (err == -1) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x0002010c, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Failed to transfer command to drive", + errno, 0); + sg_close_drive(d); + d->released = 1; + d->busy = BURN_DRIVE_IDLE; + c->error = 1; + return -1; + } + + if (s.sb_len_wr) { + if (!c->retry) { + c->error = 1; + + /* A61106: rather than : return 1 */ + goto ex; + } + switch (scsi_error(d, s.sbp, s.sb_len_wr)) { + case RETRY: + done = 0; + break; + case FAIL: + done = 1; + c->error = 1; + break; + } + } else { + done = 1; + } + } while (!done); + + /* ts A61106 */ +ex:; + if (c->error) { + scsi_notify_error(d, c, s.sbp, s.sb_len_wr, 0); + +#ifdef Libburn_log_sg_commandS + if(fp!=NULL) { + fprintf(fp,"+++ key=%X asc=%2.2Xh ascq=%2.2Xh\n", + s.sbp[2], s.sbp[12], s.sbp[13]); + fpcount++; + } +#endif /* Libburn_log_sg_commandS */ + + } + return 1; +} + + +/* ts A60922 */ +/** Tries to obtain SCSI address parameters. + @return 1 is success , 0 is failure +*/ +int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, + int *target_no, int *lun_no) +{ + int fd, ret, l; + struct my_scsi_idlun { + int x; + int host_unique_id; + }; + struct my_scsi_idlun idlun; + + l = strlen(linux_ata_device_family) - 2; + if (l > 0 && strncmp(path, linux_ata_device_family, l) == 0 + && path[7] >= 'a' && path[7] <= 'z' && path[8] == 0) + return 0; /* on RIP 14 all hdx return SCSI adr 0,0,0,0 */ + + fd = open(path, O_RDONLY | O_NONBLOCK); + if(fd < 0) + return 0; + +#ifdef SCSI_IOCTL_GET_BUS_NUMBER + /* Hearsay A61005 */ + if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, bus_no) == -1) + *bus_no = -1; +#endif + + /* http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/scsi_g_idlun.html */ + ret = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun); + + sg_close_drive_fd(path, -1, &fd, 0); + if (ret == -1) + return(0); + *host_no= (idlun.x>>24)&255; + *channel_no= (idlun.x>>16)&255; + *target_no= (idlun.x)&255; + *lun_no= (idlun.x>>8)&255; +#ifdef SCSI_IOCTL_GET_BUS_NUMBER + if(*bus_no == -1) + *bus_no = 1000 * (*host_no + 1) + *channel_no; +#else + *bus_no= *host_no; +#endif + return 1; +} + + +/* ts A60922 ticket 33 : called from drive.c */ +/** Tells wether a text is a persistent address as listed by the enumeration + functions. +*/ +int sg_is_enumerable_adr(char *adr) +{ + char fname[4096]; + int ret = 0, first = 1; + burn_drive_enumerator_t idx; + + while (1) { + ret= sg_give_next_adr(&idx, fname, sizeof(fname), first); + if(ret <= 0) + break; + first = 0; + if (strcmp(adr, fname) == 0) { + sg_give_next_adr(&idx, fname, sizeof(fname), -1); + return 1; + } + + } + sg_give_next_adr(&idx, fname, sizeof(fname), -1); + return(0); +} + + diff --git a/libburn/tags/ZeroThreeZero/libburn/sg.c b/libburn/tags/ZeroThreeZero/libburn/sg.c new file mode 100644 index 00000000..79b78d96 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/sg.c @@ -0,0 +1,17 @@ + +/* sg.c + Switcher for operating system dependent transport level modules of libburn. + Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL +*/ + + +#ifdef __FreeBSD__ + +#include "sg-freebsd.c" + +#else + +#include "sg-linux.c" + +#endif + diff --git a/libburn/tags/ZeroThreeZero/libburn/sg.h b/libburn/tags/ZeroThreeZero/libburn/sg.h new file mode 100644 index 00000000..abee161d --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/sg.h @@ -0,0 +1,34 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SG +#define __SG + + +#include "os.h" + + +/* see os.h for name of particular os-*.h where this is defined */ +BURN_OS_DEFINE_DRIVE_ENUMERATOR_T + + +struct burn_drive; +struct command; + + +/* ts A60922 ticket 33 */ +int sg_give_next_adr(burn_drive_enumerator_t *enm_context, + char adr[], int adr_size, int initialize); +int sg_is_enumerable_adr(char *adr); +int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, + int *target_no, int *lun_no); + +int sg_grab(struct burn_drive *); +int sg_release(struct burn_drive *); +int sg_issue_command(struct burn_drive *, struct command *); + +/* ts A61115 : formerly sg_enumerate();ata_enumerate() */ +int scsi_enumerate_drives(void); + +int sg_drive_is_open(struct burn_drive * d); + +#endif /* __SG */ diff --git a/libburn/tags/ZeroThreeZero/libburn/source.c b/libburn/tags/ZeroThreeZero/libburn/source.c new file mode 100644 index 00000000..a5ec1529 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/source.c @@ -0,0 +1,39 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include <stdlib.h> +#include <string.h> +#include "libburn.h" +#include "source.h" +#include "structure.h" + +void burn_source_free(struct burn_source *src) +{ + if (--src->refcount < 1) { + if (src->free_data) + src->free_data(src); + free(src); + } +} + +enum burn_source_status burn_track_set_source(struct burn_track *t, + struct burn_source *s) +{ + if (!s->read) + return BURN_SOURCE_FAILED; + s->refcount++; + t->source = s; + + /* ts A61031 */ + t->open_ended = (s->get_size(s) <= 0); + + return BURN_SOURCE_OK; +} + +struct burn_source *burn_source_new(void) +{ + struct burn_source *out; + + out = calloc(1, sizeof(struct burn_source)); + out->refcount = 1; + return out; +} diff --git a/libburn/tags/ZeroThreeZero/libburn/source.h b/libburn/tags/ZeroThreeZero/libburn/source.h new file mode 100644 index 00000000..e0a69aa1 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/source.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SOURCE +#define __SOURCE + +struct burn_source *burn_source_new(void); + +#endif /*__SOURCE*/ diff --git a/libburn/tags/ZeroThreeZero/libburn/spc.c b/libburn/tags/ZeroThreeZero/libburn/spc.c new file mode 100644 index 00000000..c33f1834 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/spc.c @@ -0,0 +1,752 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* scsi primary commands */ + +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <string.h> + +/* ts A61008 */ +/* #include <a ssert.h> */ + +#include <stdlib.h> + +#include "libburn.h" +#include "transport.h" +#include "spc.h" +#include "mmc.h" +#include "sbc.h" +#include "drive.h" +#include "debug.h" +#include "options.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + + +/* spc command set */ +static unsigned char SPC_INQUIRY[] = { 0x12, 0, 0, 0, 255, 0 }; + +/*static char SPC_TEST[]={0,0,0,0,0,0};*/ +static unsigned char SPC_PREVENT[] = { 0x1e, 0, 0, 0, 1, 0 }; +static unsigned char SPC_ALLOW[] = { 0x1e, 0, 0, 0, 0, 0 }; +static unsigned char SPC_MODE_SENSE[] = { 0x5a, 0, 0, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char SPC_MODE_SELECT[] = + { 0x55, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char SPC_REQUEST_SENSE[] = { 0x03, 0, 0, 0, 18, 0 }; +static unsigned char SPC_TEST_UNIT_READY[] = { 0x00, 0, 0, 0, 0, 0 }; + +int spc_test_unit_ready(struct burn_drive *d) +{ + struct command c; + + c.retry = 0; + c.oplen = sizeof(SPC_TEST_UNIT_READY); + memcpy(c.opcode, SPC_TEST_UNIT_READY, sizeof(SPC_TEST_UNIT_READY)); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); + if (c.error) + return (c.sense[2] & 0xF) == 0; + return 1; +} + +void spc_request_sense(struct burn_drive *d, struct buffer *buf) +{ + struct command c; + + c.retry = 0; + c.oplen = sizeof(SPC_REQUEST_SENSE); + memcpy(c.opcode, SPC_REQUEST_SENSE, sizeof(SPC_REQUEST_SENSE)); + c.page = buf; + c.page->sectors = 0; + c.page->bytes = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); +} + +int spc_get_erase_progress(struct burn_drive *d) +{ + struct buffer b; + + spc_request_sense(d, &b); + return (b.data[16] << 8) | b.data[17]; +} + +void spc_inquiry(struct burn_drive *d) +{ + struct buffer buf; + struct burn_scsi_inquiry_data *id; + struct command c; + + memcpy(c.opcode, SPC_INQUIRY, sizeof(SPC_INQUIRY)); + c.retry = 1; + c.oplen = sizeof(SPC_INQUIRY); + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + id = (struct burn_scsi_inquiry_data *)d->idata; + id->vendor[8] = 0; + id->product[16] = 0; + id->revision[4] = 0; + + memcpy(id->vendor, c.page->data + 8, 8); + memcpy(id->product, c.page->data + 16, 16); + memcpy(id->revision, c.page->data + 32, 4); + + id->valid = 1; + return; +} + +void spc_prevent(struct burn_drive *d) +{ + struct command c; + + memcpy(c.opcode, SPC_PREVENT, sizeof(SPC_PREVENT)); + c.retry = 1; + c.oplen = sizeof(SPC_PREVENT); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void spc_allow(struct burn_drive *d) +{ + struct command c; + + memcpy(c.opcode, SPC_ALLOW, sizeof(SPC_ALLOW)); + c.retry = 1; + c.oplen = sizeof(SPC_ALLOW); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void spc_sense_caps(struct burn_drive *d) +{ + struct buffer buf; + struct scsi_mode_data *m; + int size, page_length, num_write_speeds = 0, i, speed, ret; + unsigned char *page; + struct command c; + struct burn_speed_descriptor *sd; + + /* ts A61225 : 1 = report about post-MMC-1 speed descriptors */ + static int speed_debug = 0; + + memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SENSE); + c.opcode[2] = 0x2A; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + size = c.page->data[0] * 256 + c.page->data[1]; + m = d->mdata; + page = c.page->data + 8; + + /* ts A61225 : + Although MODE SENSE indeed belongs to SPC, the returned code page + 2Ah is part of MMC-1 to MMC-3. In MMC-1 5.2.3.4. it has 22 bytes, + in MMC-3 6.3.11 there are at least 28 bytes plus a variable length + set of speed descriptors. In MMC-5 E.11 it is declared "legacy". + */ + page_length = page[1]; + + m->valid = 0; + burn_mdata_free_subs(m); + + m->buffer_size = page[12] * 256 + page[13]; + m->dvdram_read = page[2] & 32; + m->dvdram_write = page[3] & 32; + m->dvdr_read = page[2] & 16; + m->dvdr_write = page[3] & 16; + m->dvdrom_read = page[2] & 8; + m->simulate = page[3] & 4; + m->cdrw_read = page[2] & 2; + m->cdrw_write = page[3] & 2; + m->cdr_read = page[2] & 1; + m->cdr_write = page[3] & 1; + + m->c2_pointers = page[5] & 16; + m->underrun_proof = page[4] & 128; + + /* ts A61021 : these fields are marked obsolete in MMC 3 */ + m->max_read_speed = page[8] * 256 + page[9]; + m->cur_read_speed = page[14] * 256 + page[15]; + + m->max_write_speed = page[18] * 256 + page[19]; + m->cur_write_speed = page[20] * 256 + page[21]; + + /* ts A61021 : New field to be set by atip (or following MMC-3 info) */ + m->min_write_speed = m->max_write_speed; + + /* ts A61225 : for ACh GET PERFORMANCE, Type 03h */ + m->min_end_lba = 0x7fffffff; + m->max_end_lba = 0; + + m->valid = 1; + + mmc_get_configuration(d); + + /* ts A61225 : end of MMC-1 , begin of MMC-3 */ + if (page_length < 32) /* no write speed descriptors ? */ + goto try_mmc_get_performance; + + m->cur_write_speed = page[28] * 256 + page[29]; + + if (speed_debug) + fprintf(stderr, "LIBBURN_DEBUG: cur_write_speed = %d\n", + m->cur_write_speed); + + num_write_speeds = page[30] * 256 + page[31]; + m->max_write_speed = m->min_write_speed = m->cur_write_speed; + for (i = 0; i < num_write_speeds; i++) { + speed = page[32 + 4*i + 2] * 256 + page[32 + 4*i + 3]; + + if (speed_debug) + fprintf(stderr, + "LIBBURN_DEBUG: write speed #%d = %d kB/s (rc %d)\n", + i, speed, page[32 + 4*i +1] & 7); + + /* ts A61226 */ + ret = burn_speed_descriptor_new(&(d->mdata->speed_descriptors), + NULL, d->mdata->speed_descriptors, 0); + if (ret > 0) { + sd = d->mdata->speed_descriptors; + sd->source = 1; + if (d->current_profile > 0) { + sd->profile_loaded = d->current_profile; + strcpy(sd->profile_name, + d->current_profile_text); + } + sd->wrc = (( page[32 + 4*i +1] & 7 ) == 1 ); + sd->write_speed = speed; + } + + if (speed > m->max_write_speed) + m->max_write_speed = speed; + if (speed < m->min_write_speed) + m->min_write_speed = speed; + } + + if (speed_debug) + fprintf(stderr, + "LIBBURN_DEBUG: 5Ah,2Ah min_write_speed = %d , max_write_speed = %d\n", + m->min_write_speed, m->max_write_speed); + +try_mmc_get_performance:; + ret = mmc_get_write_performance(d); + + if (ret > 0 && speed_debug) + fprintf(stderr, + "LIBBURN_DEBUG: ACh min_write_speed = %d , max_write_speed = %d\n", + m->min_write_speed, m->max_write_speed); +} + + +void spc_sense_error_params(struct burn_drive *d) +{ + struct buffer buf; + struct scsi_mode_data *m; + int size; + unsigned char *page; + struct command c; + + memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SENSE); + c.opcode[2] = 0x01; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + size = c.page->data[0] * 256 + c.page->data[1]; + m = d->mdata; + page = c.page->data + 8; + d->params.retries = page[3]; + m->retry_page_length = page[1]; + m->retry_page_valid = 1; +} + +void spc_select_error_params(struct burn_drive *d, + const struct burn_read_opts *o) +{ + struct buffer buf; + struct command c; + + memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SELECT); + c.opcode[8] = 8 + 2 + d->mdata->retry_page_length; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + + /* ts A61007 : moved up to only caller burn_disc_read() */ + /* a ssert(d->mdata->valid); */ + + memset(c.page->data, 0, 8 + 2 + d->mdata->retry_page_length); + c.page->bytes = 8 + 2 + d->mdata->retry_page_length; + c.page->data[8] = 1; + c.page->data[9] = d->mdata->retry_page_length; + if (o->transfer_damaged_blocks) + c.page->data[10] |= 32; + if (o->report_recovered_errors) + c.page->data[10] |= 4; + if (!o->hardware_error_recovery) + c.page->data[10] |= 1; +/*burn_print(1, "error parameter 0x%x\n", c->page->data[10]);*/ + c.page->data[11] = d->params.retries; + c.dir = TO_DRIVE; + d->issue_command(d, &c); +} + +void spc_sense_write_params(struct burn_drive *d) +{ + struct buffer buf; + struct scsi_mode_data *m; + int size; + unsigned char *page; + struct command c; + + /* ts A61007 : Done in soft at only caller burn_drive_grab() */ + /* a ssert(d->mdata->cdr_write || d->mdata->cdrw_write || + d->mdata->dvdr_write || d->mdata->dvdram_write); */ + + memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SENSE); + c.opcode[2] = 0x05; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + size = c.page->data[0] * 256 + c.page->data[1]; + m = d->mdata; + page = c.page->data + 8; + burn_print(1, "write page length 0x%x\n", page[1]); + m->write_page_length = page[1]; + m->write_page_valid = 1; + mmc_read_disc_info(d); +} + + +/* ts A61229 */ +#define Libburn_mmc_compose_mode_page_5 1 + + +/* remark ts A61104 : +Although command MODE SELECT is SPC, the content of the +Write Parameters Mode Page (05h) is MMC (Table 108 in MMC-1). +Thus the filling of the mode page should be done by a mmc_ function. +*/ +void spc_select_write_params(struct burn_drive *d, + const struct burn_write_opts *o) +{ + struct buffer buf; + struct command c; +#ifndef Libburn_mmc_compose_mode_page_5 + int bufe, sim; +#endif + + /* ts A61007 : All current callers are safe. */ + /* a ssert(o->drive == d); */ + + /* <<< A61030 + fprintf(stderr,"libburn_debug: write_type=%d multi=%d control=%d\n", + o->write_type,o->multi,o->control); + fprintf(stderr,"libburn_debug: block_type=%d spc_block_type=%d\n", + o->block_type,spc_block_type(o->block_type)); + */ + + memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SELECT); + c.opcode[8] = 8 + 2 + d->mdata->write_page_length; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + + /* ts A61007 : moved up to burn_disc_write() */ + /* a ssert(d->mdata->valid); */ + + memset(c.page->data, 0, 8 + 2 + d->mdata->write_page_length); + c.page->bytes = 8 + 2 + d->mdata->write_page_length; + + burn_print(12, "using write page length %d (valid %d)\n", + d->mdata->write_page_length, d->mdata->write_page_valid); + +#ifdef Libburn_mmc_compose_mode_page_5 + + /* ts A61229 */ + if (mmc_compose_mode_page_5(d, o, c.page->data + 8) <= 0) + return; + +#else + + c.page->data[8] = 5; + c.page->data[9] = d->mdata->write_page_length; + + bufe = o->underrun_proof; + sim = o->simulate; + c.page->data[10] = (bufe << 6) + + (sim << 4) + + o->write_type; + + /* ts A61106 : MMC-1 table 110 : multi==0 or multi==3 */ + c.page->data[11] = ((3 * !!o->multi) << 6) | o->control; + + c.page->data[12] = spc_block_type(o->block_type); + + /* ts A61104 */ + if(!(o->control&4)) /* audio (MMC-1 table 61) */ + if(o->write_type == BURN_WRITE_TAO) /* ??? for others too ? */ + c.page->data[12] = 0; /* Data Block Type: Raw Data */ + + c.page->data[22] = 0; + c.page->data[23] = 150; /* audio pause length */ + +/*XXX need session format! */ + +#endif /* ! Libburn_mmc_compose_mode_page_5 */ + + c.dir = TO_DRIVE; + d->issue_command(d, &c); +} + +void spc_getcaps(struct burn_drive *d) +{ + spc_inquiry(d); + spc_sense_caps(d); + spc_sense_error_params(d); + + /* <<< for debugging. >>> ??? to be fixely included here ? + mmc_read_format_capacities(d, -1); + */ +} + +/* +only called when a blank is present, so we set type to blank +(on the last pass) + +don't check totally stupid modes (raw/raw0) +some drives say they're ok, and they're not. +*/ + +void spc_probe_write_modes(struct burn_drive *d) +{ + struct buffer buf; + int try_write_type = 1; + int try_block_type = 0; + int key, asc, ascq; + struct command c; + + while (try_write_type != 4) { + burn_print(9, "trying %d, %d\n", try_write_type, + try_block_type); + memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SELECT); + c.opcode[8] = 8 + 2 + 0x32; + c.page = &buf; + + memset(c.page->data, 0, 8 + 2 + 0x32); + c.page->bytes = 8 + 2 + 0x32; + + c.page->data[8] = 5; + c.page->data[9] = 0x32; + c.page->data[10] = try_write_type; + if (try_block_type > 4) + c.page->data[11] = 4; + else + c.page->data[11] = 0; + c.page->data[12] = try_block_type; + c.page->data[23] = 150; + c.dir = TO_DRIVE; + + d->silent_on_scsi_error = 1; + d->issue_command(d, &c); + d->silent_on_scsi_error = 0; + + key = c.sense[2]; + asc = c.sense[12]; + ascq = c.sense[13]; + + if (key) + burn_print(7, "%d not supported\n", try_block_type); + else { + burn_print(7, "%d:%d SUPPORTED MODE!\n", + try_write_type, try_block_type); + if (try_write_type == 2) /* sao */ + d->block_types[try_write_type] = + BURN_BLOCK_SAO; + else + d->block_types[try_write_type] |= + 1 << try_block_type; + } + switch (try_block_type) { + case 0: + case 1: + case 2: + try_block_type++; + break; + case 3: + try_block_type = 8; + break; + case 8: + case 9: + case 10: + case 11: + case 12: + try_block_type++; + break; + case 13: + try_block_type = 0; + try_write_type++; + break; + default: + return; + } + } +} + +/* ( ts A61229 : shouldn't this go to mmc.c too ?) */ + +/** @return -1 = error */ +int spc_block_type(enum burn_block_types b) +{ + switch (b) { + case BURN_BLOCK_SAO: + return 0; /* ignored bitz */ + case BURN_BLOCK_RAW0: + return 0; + case BURN_BLOCK_RAW16: + return 1; + case BURN_BLOCK_RAW96P: + return 2; + case BURN_BLOCK_RAW96R: + return 3; + case BURN_BLOCK_MODE1: + return 8; + case BURN_BLOCK_MODE2R: + return 9; + case BURN_BLOCK_MODE2_PATHETIC: + return 10; + case BURN_BLOCK_MODE2_LAME: + return 11; + case BURN_BLOCK_MODE2_OBSCURE: + return 12; + case BURN_BLOCK_MODE2_OK: + return 13; + default: + return -1; + } + /* ts A61007 : already prevented in burn_write_opts_set_write_type() */ + /* a ssert(0); */; +} + +/* ts A61021 : the spc specific part of sg.c:enumerate_common() +*/ +int spc_setup_drive(struct burn_drive *d) +{ + d->getcaps = spc_getcaps; + d->lock = spc_prevent; + d->unlock = spc_allow; + d->read_disc_info = spc_sense_write_params; + d->get_erase_progress = spc_get_erase_progress; + d->test_unit_ready = spc_test_unit_ready; + d->probe_write_modes = spc_probe_write_modes; + d->send_parameters = spc_select_error_params; + d->send_write_parameters = spc_select_write_params; + return 1; +} + +/* ts A61021 : the general SCSI specific part of sg.c:enumerate_common() + @param flag Bitfiled for control purposes + bit0= do not setup spc/sbc/mmc +*/ +int burn_scsi_setup_drive(struct burn_drive *d, int bus_no, int host_no, + int channel_no, int target_no, int lun_no, int flag) +{ + int ret; + + /* ts A60923 */ + d->bus_no = bus_no; + d->host = host_no; + d->id = target_no; + d->channel = channel_no; + d->lun = lun_no; + + /* ts A61106 */ + d->silent_on_scsi_error = 0; + + + d->idata = malloc(sizeof(struct burn_scsi_inquiry_data)); + d->idata->valid = 0; + d->mdata = malloc(sizeof(struct scsi_mode_data)); + d->mdata->valid = 0; + d->mdata->speed_descriptors = NULL; + + /* ts A61007 : obsolete Assert in drive_getcaps() */ + if(d->idata == NULL || d->mdata == NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020108, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Could not allocate new drive object", 0, 0); + return -1; + } + if(!(flag & 1)) { + ret = spc_setup_drive(d); + if (ret<=0) + return ret; + ret = sbc_setup_drive(d); + if (ret<=0) + return ret; + ret = mmc_setup_drive(d); + if (ret<=0) + return ret; + } + return 1; +} + + +/* ts A61122 */ +enum response scsi_error_msg(struct burn_drive *d, unsigned char *sense, + int senselen, char msg[161], + int *key, int *asc, int *ascq) +{ + *key= *asc= *ascq= -1; + + if (senselen<=0 || senselen>2) + *key = sense[2]; + if (senselen<=0 || senselen>12) + *asc = sense[12]; + if (senselen<=0 || senselen>13) + *ascq = sense[13]; + + burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n", + *key, *asc, *ascq, d->idata->vendor, d->idata->product); + + switch (*asc) { + case 0: + sprintf(msg, "(no error reported by SCSI transaction)"); + return RETRY; + + case 2: + sprintf(msg, "not ready"); + return RETRY; + case 4: + sprintf(msg, + "logical unit is in the process of becoming ready"); + return RETRY; + case 0x20: + if (*key == 5) + sprintf(msg, "bad opcode"); + return FAIL; + case 0x21: + sprintf(msg, "invalid address"); + return FAIL; + case 0x24: + if (*key == 5) + sprintf(msg, "invalid field in cdb"); + else + break; + return FAIL; + case 0x26: + if (*key == 5 ) + sprintf(msg, "invalid field in parameter list" ); + return FAIL; + case 0x28: + if (*key == 6) + sprintf(msg, "Medium may have changed"); + else + break; + return RETRY; + case 0x3A: + sprintf(msg, "Medium not present"); + d->status = BURN_DISC_EMPTY; + return FAIL; + } + sprintf(msg, + "Failure. See mmc3r10g.pdf: Sense Key %X ASC %2.2X ASCQ %2.2X", + *key, *asc, *ascq); + return FAIL; +} + + +/* ts A61115 moved from sg-*.c */ +/* ts A61122 made it frontend to scsi_error_msg() */ +enum response scsi_error(struct burn_drive *d, unsigned char *sense, + int senselen) +{ + int key, asc, ascq; + char msg[160]; + enum response resp; + + resp = scsi_error_msg(d, sense, senselen, msg, &key, &asc, &ascq); + if (asc == 0 || asc == 0x3A) + burn_print(12, "%s\n", msg); + else + burn_print(1, "%s\n", msg); + return resp; +} + + +/* ts A61030 - A61115 */ +/* @param flag bit0=do report conditions which are considered not an error */ +int scsi_notify_error(struct burn_drive *d, struct command *c, + unsigned char *sense, int senselen, int flag) +{ + int key= -1, asc= -1, ascq= -1, ret; + char msg[320],scsi_msg[160]; + + if (d->silent_on_scsi_error) + return 1; + + strcpy(scsi_msg, " \""); + scsi_error_msg(d, sense, senselen, scsi_msg + strlen(scsi_msg), + &key, &asc, &ascq); + strcat(scsi_msg, "\""); + + if(!(flag & 1)) { + /* SPC : TEST UNIT READY command */ + if (c->opcode[0] == 0) + return 1; + /* MMC : READ DISC INFORMATION command */ + if (c->opcode[0] == 0x51) + if (key == 0x2 && asc == 0x3A && + ascq>=0 && ascq <= 0x02) /* MEDIUM NOT PRESENT */ + return 1; + } + + sprintf(msg,"SCSI error condition on command %2.2Xh :", c->opcode[0]); + if (key>=0) + sprintf(msg+strlen(msg), " key=%Xh", key); + if (asc>=0) + sprintf(msg+strlen(msg), " asc=%2.2Xh", asc); + if (ascq>=0) + sprintf(msg+strlen(msg), " ascq=%2.2Xh", ascq); + ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0); + if (ret < 0) + return ret; + ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + scsi_msg,0,0); + return ret; +} + diff --git a/libburn/tags/ZeroThreeZero/libburn/spc.h b/libburn/tags/ZeroThreeZero/libburn/spc.h new file mode 100644 index 00000000..e7fa0816 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/spc.h @@ -0,0 +1,50 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SPC +#define __SPC + +#include "libburn.h" + +void spc_inquiry(struct burn_drive *); +void spc_prevent(struct burn_drive *); +void spc_allow(struct burn_drive *); +void spc_sense_caps(struct burn_drive *); +void spc_sense_error_params(struct burn_drive *); +void spc_select_error_params(struct burn_drive *, + const struct burn_read_opts *); +void spc_getcaps(struct burn_drive *d); +void spc_sense_write_params(struct burn_drive *); +void spc_select_write_params(struct burn_drive *, + const struct burn_write_opts *); +void spc_probe_write_modes(struct burn_drive *); +void spc_request_sense(struct burn_drive *d, struct buffer *buf); +int spc_block_type(enum burn_block_types b); +int spc_get_erase_progress(struct burn_drive *d); +int spc_test_unit_ready(struct burn_drive *d); + +/* ts A61021 : the spc specific part of sg.c:enumerate_common() +*/ +int spc_setup_drive(struct burn_drive *d); + +/* ts A61021 : the general SCSI specific part of sg.c:enumerate_common() + @param flag Bitfield for control purposes + bit0= do not setup spc/sbc/mmc +*/ +int burn_scsi_setup_drive(struct burn_drive *d, int bus_no, int host_no, + int channel_no, int target_no, int lun_no, int flag); + +/* ts A61115 moved from sg-*.h */ +enum response { RETRY, FAIL }; +enum response scsi_error(struct burn_drive *, unsigned char *, int); + +/* ts A61122 */ +enum response scsi_error_msg(struct burn_drive *d, unsigned char *sense, + int senselen, char msg[161], + int *key, int *asc, int *ascq); + +/* ts A61030 */ +/* @param flag bit0=do report conditions which are considered not an error */ +int scsi_notify_error(struct burn_drive *, struct command *c, + unsigned char *sense, int senselen, int flag); + +#endif /*__SPC*/ diff --git a/libburn/tags/ZeroThreeZero/libburn/structure.c b/libburn/tags/ZeroThreeZero/libburn/structure.c new file mode 100644 index 00000000..d1ea5923 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/structure.c @@ -0,0 +1,424 @@ + +/* ts A61008 */ +/* #include <a ssert.h> */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "libburn.h" +#include "structure.h" +#include "write.h" +#include "debug.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + + +/* ts A61008 : replaced Assert by if and return 0 */ +/* a ssert(!(pos > BURN_POS_END)); */ + +#define RESIZE(TO, NEW, pos) {\ + void *tmp;\ +\ + if (pos > BURN_POS_END)\ + return 0;\ + if (pos == BURN_POS_END)\ + pos = TO->NEW##s;\ + if (pos > TO->NEW##s)\ + return 0;\ +\ + tmp = realloc(TO->NEW, sizeof(struct NEW *) * (TO->NEW##s + 1));\ + if (!tmp)\ + return 0;\ + TO->NEW = tmp;\ + memmove(TO->NEW + pos + 1, TO->NEW + pos,\ + sizeof(struct NEW *) * (TO->NEW##s - pos));\ + TO->NEW##s++;\ +} + +struct burn_disc *burn_disc_create(void) +{ + struct burn_disc *d; + d = calloc(1, sizeof(struct burn_disc)); + d->refcnt = 1; + d->sessions = 0; + d->session = NULL; + return d; +} + +void burn_disc_free(struct burn_disc *d) +{ + d->refcnt--; + if (d->refcnt == 0) { + /* dec refs on all elements */ + int i; + + for (i = 0; i < d->sessions; i++) + burn_session_free(d->session[i]); + free(d->session); + free(d); + } +} + +struct burn_session *burn_session_create(void) +{ + struct burn_session *s; + s = calloc(1, sizeof(struct burn_session)); + s->refcnt = 1; + s->tracks = 0; + s->track = NULL; + s->hidefirst = 0; + return s; +} + +void burn_session_hide_first_track(struct burn_session *s, int onoff) +{ + s->hidefirst = onoff; +} + +void burn_session_free(struct burn_session *s) +{ + s->refcnt--; + if (s->refcnt == 0) { + /* dec refs on all elements */ + int i; + + for (i = 0; i < s->tracks; i++) + burn_track_free(s->track[i]); + free(s->track); + free(s); + } + +} + +int burn_disc_add_session(struct burn_disc *d, struct burn_session *s, + unsigned int pos) +{ + RESIZE(d, session, pos); + d->session[pos] = s; + s->refcnt++; + return 1; +} + +struct burn_track *burn_track_create(void) +{ + struct burn_track *t; + t = calloc(1, sizeof(struct burn_track)); + t->refcnt = 1; + t->indices = 0; + t->offset = 0; + t->offsetcount = 0; + t->tail = 0; + t->tailcount = 0; + t->mode = BURN_MODE1; + t->isrc.has_isrc = 0; + t->pad = 1; + t->entry = NULL; + t->source = NULL; + t->eos = 0; + + /* ts A61101 */ + t->sourcecount = 0; + t->writecount = 0; + t->written_sectors = 0; + + /* ts A61031 */ + t->open_ended = 0; + t->track_data_done = 0; + + t->postgap = 0; + t->pregap1 = 0; + t->pregap2 = 0; + + /* ts A61024 */ + t->swap_source_bytes = 0; + return t; +} + +void burn_track_free(struct burn_track *t) +{ + t->refcnt--; + if (t->refcnt == 0) { + /* dec refs on all elements */ + if (t->source) + burn_source_free(t->source); + free(t); + } +} + +int burn_session_add_track(struct burn_session *s, struct burn_track *t, + unsigned int pos) +{ + RESIZE(s, track, pos); + s->track[pos] = t; + t->refcnt++; + return 1; +} + +int burn_session_remove_track(struct burn_session *s, struct burn_track *t) +{ + struct burn_track **tmp; + int i, pos = -1; + + /* ts A61008 */ + /* a ssert(s->track != NULL); */ + if (s->track == NULL) + return 0; + + burn_track_free(t); + + /* Find the position */ + for (i = 0; i < s->tracks; i++) { + if (t == s->track[i]) { + pos = i; + break; + } + } + + if (pos == -1) + return 0; + + /* Is it the last track? */ + if (pos != s->tracks - 1) { + memmove(&s->track[pos], &s->track[pos + 1], + sizeof(struct burn_track *) * (s->tracks - (pos + 1))); + } + + s->tracks--; + tmp = realloc(s->track, sizeof(struct burn_track *) * s->tracks); + if (tmp) + s->track = tmp; + return 1; +} + +void burn_structure_print_disc(struct burn_disc *d) +{ + int i; + + burn_print(12, "This disc has %d sessions\n", d->sessions); + for (i = 0; i < d->sessions; i++) { + burn_structure_print_session(d->session[i]); + } +} +void burn_structure_print_session(struct burn_session *s) +{ + int i; + + burn_print(12, " Session has %d tracks\n", s->tracks); + for (i = 0; i < s->tracks; i++) { + burn_structure_print_track(s->track[i]); + } +} +void burn_structure_print_track(struct burn_track *t) +{ + burn_print(12, "(%p) track size %d sectors\n", t, + burn_track_get_sectors(t)); +} + +void burn_track_define_data(struct burn_track *t, int offset, int tail, + int pad, int mode) +{ + int type_to_form(int mode, unsigned char *ctladr, int *form); + int burn_sector_length(int tracktype); + unsigned char ctladr; + int form = -1; /* unchanged form will be considered an error too */ + + type_to_form(mode, &ctladr, &form); + if (form == -1 || burn_sector_length(mode) <= 0) { + char msg[160]; + + sprintf(msg, "Attempt to set track mode to unusable value %d", + mode); + libdax_msgs_submit(libdax_messenger, -1, 0x00020115, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return; + } + + t->offset = offset; + t->pad = pad; + t->mode = mode; + t->tail = tail; +} + + +/* ts A61024 */ +int burn_track_set_byte_swap(struct burn_track *t, int swap_source_bytes) +{ + if(swap_source_bytes!=0 && swap_source_bytes!=1) + return 0; + t->swap_source_bytes = swap_source_bytes; + return 1; +} + + +void burn_track_set_isrc(struct burn_track *t, char *country, char *owner, + unsigned char year, unsigned int serial) +{ + int i; + + for (i = 0; i < 2; ++i) { + + /* ts A61008 : This is always true */ + /* a ssert((country[i] >= '0' || country[i] < '9') && + (country[i] >= 'a' || country[i] < 'z') && + (country[i] >= 'A' || country[i] < 'Z')); */ + /* ts A61008 : now coordinated with sector.c: char_to_isrc() */ + if (! ((country[i] >= '0' && country[i] <= '9') || + (country[i] >= 'a' && country[i] <= 'z') || + (country[i] >= 'A' && country[i] <= 'Z') ) ) + goto is_not_allowed; + + t->isrc.country[i] = country[i]; + } + for (i = 0; i < 3; ++i) { + + /* ts A61008 : This is always true */ + /* a ssert((owner[i] >= '0' || owner[i] < '9') && + (owner[i] >= 'a' || owner[i] < 'z') && + (owner[i] >= 'A' || owner[i] < 'Z')); */ + /* ts A61008 : now coordinated with sector.c: char_to_isrc() */ + if (! ((owner[i] >= '0' && owner[i] <= '9') || + (owner[i] >= 'a' && owner[i] <= 'z') || + (owner[i] >= 'A' && owner[i] <= 'Z') ) ) + goto is_not_allowed; + + t->isrc.owner[i] = owner[i]; + } + + /* ts A61008 */ + /* a ssert(year <= 99); */ + if (year > 99) + goto is_not_allowed; + + t->isrc.year = year; + + /* ts A61008 */ + /* a ssert(serial <= 99999); */ + if (serial > 99999) + goto is_not_allowed; + + t->isrc.serial = serial; + + /* ts A61008 */ + t->isrc.has_isrc = 1; + return; +is_not_allowed:; + libdax_msgs_submit(libdax_messenger, -1, 0x00020114, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Attempt to set ISRC with bad data", 0, 0); + return; +} + +void burn_track_clear_isrc(struct burn_track *t) +{ + t->isrc.has_isrc = 0; +} + +int burn_track_get_sectors(struct burn_track *t) +{ + int size; + int sectors, seclen; + + seclen = burn_sector_length(t->mode); + size = t->offset + t->source->get_size(t->source) + t->tail; + sectors = size / seclen; + if (size % seclen) + sectors++; + burn_print(1, "%d sectors of %d length\n", sectors, seclen); + return sectors; +} + +/* ts A61031 */ +int burn_track_is_open_ended(struct burn_track *t) +{ + return !!t->open_ended; +} + +/* ts A61101 : API function */ +int burn_track_get_counters(struct burn_track *t, + off_t *read_bytes, off_t *written_bytes) +{ +/* + fprintf(stderr, "libburn_experimental: sizeof(off_t)=%d\n", + sizeof(off_t)); +*/ + *read_bytes = t->sourcecount; + *written_bytes = t->writecount; + return 1; +} + +/* ts A61031 */ +int burn_track_is_data_done(struct burn_track *t) +{ + return !!t->track_data_done; +} + +int burn_track_get_shortage(struct burn_track *t) +{ + int size; + int seclen; + + seclen = burn_sector_length(t->mode); + size = t->offset + t->source->get_size(t->source) + t->tail; + if (size % seclen) + return seclen - size % seclen; + return 0; +} + +int burn_session_get_sectors(struct burn_session *s) +{ + int sectors = 0, i; + + for (i = 0; i < s->tracks; i++) + sectors += burn_track_get_sectors(s->track[i]); + return sectors; +} + +int burn_disc_get_sectors(struct burn_disc *d) +{ + int sectors = 0, i; + + for (i = 0; i < d->sessions; i++) + sectors += burn_session_get_sectors(d->session[i]); + return sectors; +} + +void burn_track_get_entry(struct burn_track *t, struct burn_toc_entry *entry) +{ + if (t->entry == NULL) + memset(entry, 0, sizeof(struct burn_toc_entry)); + else + memcpy(entry, t->entry, sizeof(struct burn_toc_entry)); +} + +void burn_session_get_leadout_entry(struct burn_session *s, + struct burn_toc_entry *entry) +{ + if (s->leadout_entry == NULL) + memset(entry, 0, sizeof(struct burn_toc_entry)); + else + memcpy(entry, s->leadout_entry, sizeof(struct burn_toc_entry)); +} + +struct burn_session **burn_disc_get_sessions(struct burn_disc *d, int *num) +{ + *num = d->sessions; + return d->session; +} + +struct burn_track **burn_session_get_tracks(struct burn_session *s, int *num) +{ + *num = s->tracks; + return s->track; +} + +int burn_track_get_mode(struct burn_track *track) +{ + return track->mode; +} + +int burn_session_get_hidefirst(struct burn_session *session) +{ + return session->hidefirst; +} diff --git a/libburn/tags/ZeroThreeZero/libburn/structure.h b/libburn/tags/ZeroThreeZero/libburn/structure.h new file mode 100644 index 00000000..0eb3b8c1 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/structure.h @@ -0,0 +1,92 @@ +#ifndef BURN__STRUCTURE_H +#define BURN__STRUCTURE_H + +struct isrc +{ + int has_isrc; + char country[2]; /* each must be 0-9, A-Z */ + char owner[3]; /* each must be 0-9, A-Z */ + unsigned char year; /* must be 0-99 */ + unsigned int serial; /* must be 0-99999 */ +}; + +struct burn_track +{ + int refcnt; + struct burn_toc_entry *entry; + unsigned char indices; + /* lba address of the index */ + unsigned int index[99]; + /** number of 0 bytes to write before data */ + int offset; + /** how much offset has been used */ + int offsetcount; + /** Number of zeros to write after data */ + int tail; + /** how much tail has been used */ + int tailcount; + /** 1 means Pad with zeros, 0 means start reading the next track */ + int pad; + /** Data source */ + struct burn_source *source; + /** End of Source flag */ + int eos; + + /* ts A61101 */ + off_t sourcecount; + off_t writecount; + off_t written_sectors; + + /* ts A61031 */ + /** Source is of undefined length */ + int open_ended; + /** End of open ended track flag : offset+payload+tail are delivered */ + int track_data_done; + + /** The audio/data mode for the entry. Derived from control and + possibly from reading the track's first sector. */ + int mode; + /** The track contains interval one of a pregap */ + int pregap1; + /** The track contains interval two of a pregap */ + int pregap2; + /** The track contains a postgap */ + int postgap; + struct isrc isrc; + + /* ts A61024 */ + /** Byte swapping on source data stream : 0=none , 1=pairwise */ + int swap_source_bytes; +}; + +struct burn_session +{ + unsigned char firsttrack; + unsigned char lasttrack; + int hidefirst; + unsigned char start_m; + unsigned char start_s; + unsigned char start_f; + struct burn_toc_entry *leadout_entry; + + int tracks; + struct burn_track **track; + int refcnt; +}; + +struct burn_disc +{ + int sessions; + struct burn_session **session; + int refcnt; +}; + +int burn_track_get_shortage(struct burn_track *t); + + +/* ts A61031 : might go to libburn.h */ +int burn_track_is_open_ended(struct burn_track *t); +int burn_track_is_data_done(struct burn_track *t); + + +#endif /* BURN__STRUCTURE_H */ diff --git a/libburn/tags/ZeroThreeZero/libburn/toc.c b/libburn/tags/ZeroThreeZero/libburn/toc.c new file mode 100644 index 00000000..c1c198c4 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/toc.c @@ -0,0 +1,136 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* ts A61008 */ +/* #include <a ssert.h> */ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include "toc.h" +#include "transport.h" +#include "libburn.h" +#include "sector.h" +#include "options.h" + +#if 0 +static void write_clonecd2(volatile struct toc *toc, int f); + +static void write_clonecd2(volatile struct toc *toc, int f) +{ + int i; + + /* header */ + dprintf(f, "[CloneCD]\r\n"); + dprintf(f, "Version=2\r\n"); + dprintf(f, "\r\n"); + + /* disc data */ + dprintf(f, "[Disc]\r\n"); + + dprintf(f, "TocEntries=%d\r\n", toc->toc_entries); + dprintf(f, "Sessions=%d\r\n", toc->sessions); + dprintf(f, "DataTracksScrambled=%d\r\n", toc->datatracksscrambled); + dprintf(f, "CDTextLength=%d\r\n", toc->cdtextlength); + dprintf(f, "\r\n"); + + /* session data */ + for (i = 0; i < toc->sessions; ++i) { + dprintf(f, "[Session %d]\r\n", i + 1); + + { + int m; + + switch (toc->session[i].track[0]->mode) { + case BURN_MODE_RAW_DATA: + case BURN_MODE_AUDIO: + m = 0; + break; + case BURN_MODE0: + m = 1; + break; + case BURN_MODE1: + case BURN_MODE2_FORMLESS: + case BURN_MODE2_FORM1: + case BURN_MODE2_FORM2: + case BURN_MODE_UNINITIALIZED: + + /* ts A61008 : do this softly without Assert */ + + a ssert(0); /* unhandled! find out ccd's + value for these modes! */ + } + dprintf(f, "PreGapMode=%d\r\n", m); + } + dprintf(f, "\r\n"); + } + + for (i = 0; i < toc->toc_entries; ++i) { + dprintf(f, "[Entry %d]\r\n", i); + + dprintf(f, "Session=%d\r\n", toc->toc_entry[i].session); + dprintf(f, "Point=0x%02x\r\n", toc->toc_entry[i].point); + dprintf(f, "ADR=0x%02x\r\n", toc->toc_entry[i].adr); + dprintf(f, "Control=0x%02x\r\n", toc->toc_entry[i].control); + dprintf(f, "TrackNo=%d\r\n", toc->toc_entry[i].tno); + dprintf(f, "AMin=%d\r\n", toc->toc_entry[i].min); + dprintf(f, "ASec=%d\r\n", toc->toc_entry[i].sec); + dprintf(f, "AFrame=%d\r\n", toc->toc_entry[i].frame); + dprintf(f, "ALBA=%d\r\n", + burn_msf_to_lba(toc->toc_entry[i].min, + toc->toc_entry[i].sec, + toc->toc_entry[i].frame)); + dprintf(f, "Zero=%d\r\n", toc->toc_entry[i].zero); + dprintf(f, "PMin=%d\r\n", toc->toc_entry[i].pmin); + dprintf(f, "PSec=%d\r\n", toc->toc_entry[i].psec); + dprintf(f, "PFrame=%d\r\n", toc->toc_entry[i].pframe); + dprintf(f, "PLBA=%d\r\n", + burn_msf_to_lba(toc->toc_entry[i].pmin, + toc->toc_entry[i].psec, + toc->toc_entry[i].pframe)); + dprintf(f, "\r\n"); + } +} +#endif + +void toc_find_modes(struct burn_drive *d) +{ + struct burn_read_opts o; + int lba; + int i, j; + struct buffer mem; + struct burn_toc_entry *e; + + /* ts A61008 : to be prevented on the higher levels */ + /* a ssert(d->busy); */ + + mem.bytes = 0; + mem.sectors = 1; + o.raw = 1; + o.c2errors = 0; + o.subcodes_audio = 1; + o.subcodes_data = 1; + o.hardware_error_recovery = 1; + o.report_recovered_errors = 0; + o.transfer_damaged_blocks = 1; + o.hardware_error_retries = 1; + + for (i = 0; i < d->disc->sessions; i++) + for (j = 0; j < d->disc->session[i]->tracks; j++) { + struct burn_track *t = d->disc->session[i]->track[j]; + + e = t->entry; + if (!e) + lba = 0; + else + lba = burn_msf_to_lba(e->pmin, e->psec, + e->pframe); +/* XXX | in the subcodes if appropriate! */ + if (e && !(e->control & 4)) { + t->mode = BURN_AUDIO; + } else { + mem.sectors = 1; + d->read_sectors(d, lba, mem.sectors, &o, &mem); + t->mode = sector_identify(mem.data); + } + } +} diff --git a/libburn/tags/ZeroThreeZero/libburn/toc.h b/libburn/tags/ZeroThreeZero/libburn/toc.h new file mode 100644 index 00000000..1d804e75 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/toc.h @@ -0,0 +1,48 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __TOC_H +#define __TOC_H + +struct command; + +#include "libburn.h" +#include "structure.h" + +/* return if a given entry refers to a track position */ +#define TOC_ENTRY_IS_TRACK(drive, entrynum) \ + ((drive)->toc_entry[entrynum].point < 100) + +/* return if a given entry is in audio or data format */ +#define TOC_ENTRY_IS_AUDIO(drive, entrynum) \ + (~(drive)->toc_entry[entrynum].control & 4) + +/* return the point value for a given entry number */ +#define TOC_POINT(drive, entrynum) ((drive)->toc_entry[entrynum].point) + +/* return the track struct for a given entry number */ +#define TOC_TRACK(drive, entrynum) \ + ((drive)->track[TOC_POINT(drive, entrynum) - 1]) + +/* return the lba of a toc entry */ +#define TOC_ENTRY_PLBA(drive, entrynum) \ + burn_msf_to_lba((drive)->toc_entry[(entrynum)].pmin, \ + (drive)->toc_entry[(entrynum)].psec, \ + (drive)->toc_entry[(entrynum)].pframe) + +/* flags for the q subchannel control field */ +#define TOC_CONTROL_AUDIO (0) +#define TOC_CONTROL_DATA (1 << 2) +#define TOC_CONTROL_AUDIO_TWO_CHANNELS (0) +#define TOC_CONTROL_AUDIO_FOUR_CHANNELS (1 << 3) +#define TOC_CONTROL_AUDIO_PRE_EMPHASIS (1 << 0) +#define TOC_CONTROL_DATA_RECORDED_UNINTERRUPTED (0) +#define TOC_CONTROL_DATA_RECORDED_INCREMENT (1 << 0) +#define TOC_CONTROL_COPY_PROHIBITED (0) +#define TOC_CONTROL_COPY_PERMITTED (1 << 1) + +/** read a sector from each track on disc to determine modes + @param d The drive. +*/ +void toc_find_modes(struct burn_drive *d); + +#endif /*__TOC_H*/ diff --git a/libburn/tags/ZeroThreeZero/libburn/transport.h b/libburn/tags/ZeroThreeZero/libburn/transport.h new file mode 100644 index 00000000..a906007d --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/transport.h @@ -0,0 +1,266 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __TRANSPORT +#define __TRANSPORT + +#include "libburn.h" +#include "os.h" + +#include <pthread.h> +/* sg data structures */ +#include <sys/types.h> + + +/* see os.h for name of particular os-*.h where this is defined */ +#define BUFFER_SIZE BURN_OS_TRANSPORT_BUFFER_SIZE + + +enum transfer_direction +{ TO_DRIVE, FROM_DRIVE, NO_TRANSFER }; + +/* end of sg data structures */ + +/* generic 'drive' data structures */ + +struct cue_sheet +{ + int count; + unsigned char *data; +}; + +struct params +{ + int speed; + int retries; +}; + +struct buffer +{ + /* ts A61219: + Added 4096 bytes reserve against possible buffer overflows. + (Changed in sector.c buffer flush test from >= to > BUFFER_SIZE . + This can at most cause a 1 sector overlap. Sometimes an offset + of 16 byte is applied to the output data (in some RAW mode). ) */ + unsigned char data[BUFFER_SIZE + 4096]; + int sectors; + int bytes; +}; + +struct command +{ + unsigned char opcode[16]; + int oplen; + int dir; + unsigned char sense[128]; + int error; + int retry; + struct buffer *page; +}; + +struct burn_scsi_inquiry_data +{ + char vendor[9]; + char product[17]; + char revision[5]; + int valid; +}; + + +struct scsi_mode_data +{ + int buffer_size; + int dvdram_read; + int dvdram_write; + int dvdr_read; + int dvdr_write; + int dvdrom_read; + int cdrw_read; + int cdrw_write; + int cdr_read; + int cdr_write; + int simulate; + int max_read_speed; + int max_write_speed; + + /* ts A61021 */ + int min_write_speed; + + /* ts A61225 : Results from ACh GET PERFORMANCE, Type 03h + Speed values go into *_*_speed */ + int min_end_lba; + int max_end_lba; + struct burn_speed_descriptor *speed_descriptors; + + int cur_read_speed; + int cur_write_speed; + int retry_page_length; + int retry_page_valid; + int write_page_length; + int write_page_valid; + int c2_pointers; + int valid; + int underrun_proof; +}; + + +/* ts A70112 : represents a single Formattable Capacity Descriptor as of + mmc5r03c.pdf 6.24.3.3 . There can at most be 32 of them. */ +struct burn_format_descr { + /* format type: e.g 0x00 is "Full", 0x15 is "Quick" */ + int type; + + /* the size in bytes derived from Number of Blocks */ + off_t size; + + /* the Type Dependent Parameter (usually the write alignment size) */ + unsigned tdp; +}; + + +#define LIBBURN_SG_MAX_SIBLINGS 16 + +/** Gets initialized in enumerate_common() and burn_drive_register() */ +struct burn_drive +{ + int bus_no; + int host; + int id; + int channel; + int lun; + char *devname; + + + /* see os.h for name of particular os-*.h where this is defined */ + BURN_OS_TRANSPORT_DRIVE_ELEMENTS + + + /* ts A60904 : ticket 62, contribution by elmom */ + /** + Tells the index in scanned burn_drive_info array. + -1 if fallen victim to burn_drive_info_forget() + */ + int global_index; + + pthread_mutex_t access_lock; + + enum burn_disc_status status; + int erasable; + + /* ts A61201 from 46h GET CONFIGURATION */ + int current_profile; + char current_profile_text[80]; + int current_is_cd_profile; + int current_is_supported_profile; + + /* ts A70114 : wether a DVD-RW media holds an incomplete session + (which could need closing after write) */ + int dvd_minus_rw_incomplete; + + /* ts A61218 from 46h GET CONFIGURATION */ + int bg_format_status; /* 0=needs format start, 1=needs format restart*/ + + /* ts A70108 from 23h READ FORMAT CAPACITY mmc5r03c.pdf 6.24 */ + int format_descr_type; /* 1=unformatted, 2=formatted, 3=unclear */ + off_t format_curr_max_size; /* meaning depends on format_descr_type */ + unsigned format_curr_blsas; /* meaning depends on format_descr_type */ + int best_format_type; + off_t best_format_size; + + /* The complete list of format descriptors as read with 23h */ + int num_format_descr; + struct burn_format_descr format_descriptors[32]; + + + volatile int released; + + /* ts A61106 */ + int silent_on_scsi_error; + + int nwa; /* next writeable address */ + int alba; /* absolute lba */ + int rlba; /* relative lba in section */ + int start_lba; + int end_lba; + int toc_temp; + struct burn_disc *disc; /* disc structure */ + int block_types[4]; + struct buffer *buffer; + struct burn_progress progress; + + volatile int cancel; + volatile enum burn_drive_status busy; +/* transport functions */ + int (*grab) (struct burn_drive *); + int (*release) (struct burn_drive *); + + /* ts A61021 */ + int (*drive_is_open) (struct burn_drive *); + + int (*issue_command) (struct burn_drive *, struct command *); + +/* lower level functions */ + void (*erase) (struct burn_drive *, int); + void (*getcaps) (struct burn_drive *); + + /* ts A61021 */ + void (*read_atip) (struct burn_drive *); + + int (*write) (struct burn_drive *, int, struct buffer *); + void (*read_toc) (struct burn_drive *); + void (*lock) (struct burn_drive *); + void (*unlock) (struct burn_drive *); + void (*eject) (struct burn_drive *); + void (*load) (struct burn_drive *); + int (*start_unit) (struct burn_drive *); + void (*read_disc_info) (struct burn_drive *); + void (*read_sectors) (struct burn_drive *, + int start, + int len, + const struct burn_read_opts *, struct buffer *); + void (*perform_opc) (struct burn_drive *); + void (*set_speed) (struct burn_drive *, int, int); + void (*send_parameters) (struct burn_drive *, + const struct burn_read_opts *); + void (*send_write_parameters) (struct burn_drive *, + const struct burn_write_opts *); + void (*send_cue_sheet) (struct burn_drive *, struct cue_sheet *); + void (*sync_cache) (struct burn_drive *); + int (*get_erase_progress) (struct burn_drive *); + int (*get_nwa) (struct burn_drive *, int trackno, int *lba, int *nwa); + + /* ts A61009 : removed d in favor of o->drive */ + /* void (*close_disc) (struct burn_drive * d, + struct burn_write_opts * o); + void (*close_session) (struct burn_drive * d, + struct burn_write_opts * o); + */ + void (*close_disc) (struct burn_write_opts * o); + void (*close_session) ( struct burn_write_opts * o); + + /* ts A61029 */ + void (*close_track_session) ( struct burn_drive *d, + int session, int track); + + int (*test_unit_ready) (struct burn_drive * d); + void (*probe_write_modes) (struct burn_drive * d); + struct params params; + struct burn_scsi_inquiry_data *idata; + struct scsi_mode_data *mdata; + int toc_entries; + struct burn_toc_entry *toc_entry; + + /* ts A61023 : get size and free space of drive buffer */ + int (*read_buffer_capacity) (struct burn_drive *d); + + /* ts A61220 : format media (e.g. DVD+RW) */ + int (*format_unit) (struct burn_drive *d, off_t size, int flag); + + /* ts A70108 */ + /* mmc5r03c.pdf 6.24 : get list of available formats */ + int (*read_format_capacities) (struct burn_drive *d, int top_wanted); + +}; + +/* end of generic 'drive' data structures */ + +#endif /* __TRANSPORT */ diff --git a/libburn/tags/ZeroThreeZero/libburn/util.c b/libburn/tags/ZeroThreeZero/libburn/util.c new file mode 100644 index 00000000..b759dc37 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/util.c @@ -0,0 +1,53 @@ +#include <string.h> + +/* ts A61008 */ +/* #include <a ssert.h> */ + +#include <stdlib.h> +#include "../version.h" +#include "util.h" +#include "libburn.h" + +char *burn_strdup(char *s) +{ + char *ret; + int l; + + /* ts A61008 */ + /* a ssert(s); */ + if (s == NULL) + return NULL; + + l = strlen(s) + 1; + ret = malloc(l); + memcpy(ret, s, l); + + return ret; +} + +char *burn_strndup(char *s, int n) +{ + char *ret; + int l; + + /* ts A61008 */ + /* a ssert(s); */ + /* a ssert(n > 0); */ + if (s == NULL || n <= 0) + return NULL; + + l = strlen(s); + ret = malloc(l < n ? l : n); + + memcpy(ret, s, l < n - 1 ? l : n - 1); + ret[n - 1] = '\0'; + + return ret; +} + +void burn_version(int *major, int *minor, int *micro) +{ + *major = BURN_MAJOR_VERSION; + *minor = BURN_MINOR_VERSION; + *micro = BURN_MICRO_VERSION; +} diff --git a/libburn/tags/ZeroThreeZero/libburn/util.h b/libburn/tags/ZeroThreeZero/libburn/util.h new file mode 100644 index 00000000..4e74895d --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/util.h @@ -0,0 +1,8 @@ +#ifndef __UTIL +#define __UTIL + +char *burn_strdup(char *s); + +char *burn_strndup(char *s, int n); + +#endif diff --git a/libburn/tags/ZeroThreeZero/libburn/write.c b/libburn/tags/ZeroThreeZero/libburn/write.c new file mode 100644 index 00000000..a233d0cc --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/write.c @@ -0,0 +1,1350 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include <unistd.h> +#include <signal.h> + +/* ts A61009 */ +/* #include <a ssert.h> */ + + +/* ts A61106 : Deliberate defect provocation macros + DO NOT DEFINE THESE IF YOU WANT SUCCESSFUL TAO ! +#define Libburn_experimental_no_close_tracK 1 +#define Libburn_experimental_no_close_sessioN 1 +*/ + +/* ts A61114 : Highly experimental : try to achieve SAO on appendables + THIS DOES NOT WORK YET ! +#define Libburn_sao_can_appenD 1 +*/ + + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include "error.h" +#include "sector.h" +#include "libburn.h" +#include "drive.h" +#include "transport.h" +#include "crc.h" +#include "debug.h" +#include "init.h" +#include "lec.h" +#include "toc.h" +#include "util.h" +#include "sg.h" +#include "write.h" +#include "options.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + + +static int type_to_ctrl(int mode) +{ + int ctrl = 0; + + int data = BURN_MODE2 | BURN_MODE1 | BURN_MODE0; + + if (mode & data) { + ctrl |= 4; + } else if (mode & BURN_AUDIO) { + if (mode & BURN_4CH) + ctrl |= 8; + if (mode & BURN_PREEMPHASIS) + ctrl |= 1; + } else + /* ts A61008 */ + /* a ssert(0); */ + return -1; + + if (mode & BURN_COPY) + ctrl |= 2; + + return ctrl; +} + +/* only the ctrl nibble is set here (not adr) */ +/* ts A61009 : removed "static" , reacted on type_to_ctrl() == -1 + preserved ignorance towards unknown modes (for now) */ +void type_to_form(int mode, unsigned char *ctladr, int *form) +{ + int ret; + + ret = type_to_ctrl(mode) << 4; + if (ret == -1) { + *ctladr = 0xff; + *form = -1; + return; + } + *ctladr = ret; + + if (mode & BURN_AUDIO) + *form = 0; + if (mode & BURN_MODE0) { + + /* ts A61009 */ + /* a ssert(0); */ + *form = -1; + return; + } + + if (mode & BURN_MODE1) + *form = 0x10; + if (mode & BURN_MODE2) { + + /* ts A61009 */ + /* a ssert(0); */ /* XXX someone's gonna want this sometime */ + *form = -1; + return; + } + + if (mode & BURN_MODE_RAW) + *form = 0; + if (mode & BURN_SUBCODE_P16) /* must be expanded to R96 */ + *form |= 0x40; + if (mode & BURN_SUBCODE_P96) + *form |= 0xC0; + if (mode & BURN_SUBCODE_R96) + *form |= 0x40; +} + +int burn_write_flush(struct burn_write_opts *o, struct burn_track *track) +{ + struct burn_drive *d = o->drive; + + if (d->buffer->bytes && !d->cancel) { + int err; + err = d->write(d, d->nwa, d->buffer); + if (err == BE_CANCELLED) + return 0; + /* A61101 */ + if(track != NULL) { + track->writecount += d->buffer->bytes; + track->written_sectors += d->buffer->sectors; + } + /* ts A61119 */ + d->progress.buffered_bytes += d->buffer->bytes; + + d->nwa += d->buffer->sectors; + d->buffer->bytes = 0; + d->buffer->sectors = 0; + } + d->sync_cache(d); + return 1; +} + + +/* ts A61030 */ +int burn_write_close_track(struct burn_write_opts *o, struct burn_session *s, + int tnum) +{ + char msg[81]; + struct burn_drive *d; + struct burn_track *t; + int todo, step, cancelled, seclen; + + /* ts A61106 */ +#ifdef Libburn_experimental_no_close_tracK + return 1; +#endif + + d = o->drive; + t = s->track[tnum]; + + /* ts A61103 : pad up track to minimum size of 600 sectors */ + if (t->written_sectors < 300) { + todo = 300 - t->written_sectors; + sprintf(msg,"Padding up track to minimum size (+ %d sectors)", + todo); + libdax_msgs_submit(libdax_messenger, o->drive->global_index, + 0x0002011a, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg,0,0); + step = BUFFER_SIZE / 4096; /* shall fit any sector size */ + if (step <= 0) + step = 1; + seclen = burn_sector_length(t->mode); + if (seclen <= 0) + seclen = 2048; + memset(d->buffer, 0, sizeof(struct buffer)); + cancelled = d->cancel; + for (; todo > 0; todo -= step) { + if (step > todo) + step = todo; + d->buffer->bytes = step*seclen; + d->buffer->sectors = step; + d->cancel = 0; + d->write(d, d->nwa, d->buffer); + d->nwa += d->buffer->sectors; + t->writecount += d->buffer->bytes; + t->written_sectors += d->buffer->sectors; + d->progress.buffered_bytes += d->buffer->bytes; + } + d->cancel = cancelled; + } + + /* ts A61102 */ + d->busy = BURN_DRIVE_CLOSING_TRACK; + + sprintf(msg, "Closing track %2.2d", tnum+1); + libdax_msgs_submit(libdax_messenger, o->drive->global_index,0x00020119, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0); + + /* MMC-1 mentions track number 0xFF for "the incomplete track", + MMC-3 does not. I tried both. 0xFF was in effect when other + bugs finally gave up and made way for readable tracks. */ + d->close_track_session(o->drive, 0, 0xff); /* tnum+1); */ + + /* ts A61102 */ + d->busy = BURN_DRIVE_WRITING; + + return 1; +} + + +/* ts A61030 */ +int burn_write_close_session(struct burn_write_opts *o, struct burn_session *s) +{ + + /* ts A61106 */ +#ifdef Libburn_experimental_no_close_sessioN + return 1; +#endif + + libdax_msgs_submit(libdax_messenger, o->drive->global_index,0x00020119, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + "Closing session", 0, 0); + + /* ts A61102 */ + o->drive->busy = BURN_DRIVE_CLOSING_SESSION; + + o->drive->close_track_session(o->drive, 1, 0); + + /* ts A61102 */ + o->drive->busy = BURN_DRIVE_WRITING; + + return 1; +} + + +/* ts A60819: + This is unused since about Feb 2006, icculus.org/burn CVS. + The compiler complains. We shall please our compiler. +*/ +#ifdef Libburn_write_with_function_print_cuE + +static void print_cue(struct cue_sheet *sheet) +{ + int i; + unsigned char *unit; + + printf("\n"); + printf("ctladr|trno|indx|form|scms| msf\n"); + printf("------+----+----+----+----+--------\n"); + for (i = 0; i < sheet->count; i++) { + unit = sheet->data + 8 * i; + printf(" %1X %1X | %02X | %02X | %02X | %02X |", + (unit[0] & 0xf0) >> 4, unit[0] & 0xf, unit[1], unit[2], + unit[3], unit[4]); + printf("%02X:%02X:%02X\n", unit[5], unit[6], unit[7]); + } +} + +#endif /* Libburn_write_with_print_cuE */ + + +/* ts A61009 : changed type from void to int */ +/** @return 1 = success , <=0 failure */ +static int add_cue(struct cue_sheet *sheet, unsigned char ctladr, + unsigned char tno, unsigned char indx, + unsigned char form, unsigned char scms, int lba) +{ + unsigned char *unit; + unsigned char *ptr; + int m, s, f; + + burn_lba_to_msf(lba, &m, &s, &f); + + sheet->count++; + ptr = realloc(sheet->data, sheet->count * 8); + + /* ts A61009 */ + /* a ssert(ptr); */ + if (ptr == NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020111, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Could not allocate new auxiliary object (cue_sheet->data)", + 0, 0); + return -1; + } + + sheet->data = ptr; + unit = sheet->data + (sheet->count - 1) * 8; + unit[0] = ctladr; + unit[1] = tno; + unit[2] = indx; + unit[3] = form; + unit[4] = scms; + unit[5] = m; + unit[6] = s; + unit[7] = f; + return 1; +} + +/* ts A61114: added parameter nwa */ +struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o, + struct burn_session *session, + int nwa) +{ + int i, m, s, f, form, pform, runtime = -150, ret; + unsigned char ctladr; + struct burn_drive *d; + struct burn_toc_entry *e; + struct cue_sheet *sheet; + struct burn_track **tar = session->track; + int ntr = session->tracks; + int rem = 0; + + d = o->drive; + +#ifdef Libburn_sao_can_appenD + if (d->status == BURN_DISC_APPENDABLE) + runtime = nwa-150; +#endif + + sheet = malloc(sizeof(struct cue_sheet)); + + /* ts A61009 : react on failures of malloc(), add_cue_sheet() + type_to_form() */ + if (sheet == NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020111, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Could not allocate new auxiliary object (cue_sheet)", + 0, 0); + return NULL; + } + + sheet->data = NULL; + sheet->count = 0; + type_to_form(tar[0]->mode, &ctladr, &form); + if (form == -1) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020116, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Track mode has unusable value", 0, 0); + goto failed; + } + ret = add_cue(sheet, ctladr | 1, 0, 0, 1, 0, runtime); + if (ret <= 0) + goto failed; + ret = add_cue(sheet, ctladr | 1, 1, 0, form, 0, runtime); + if (ret <= 0) + goto failed; + runtime += 150; + + burn_print(1, "toc for %d tracks:\n", ntr); + d->toc_entries = ntr + 3; + + /* ts A61009 */ + /* a ssert(d->toc_entry == NULL); */ + if (d->toc_entry != NULL) { + + /* ts A61109 : this happens with appendable CDs + >>> Open question: is the existing TOC needed ? */ + + /* ts A61109 : for non-SAO, this sheet is thrown away later */ + free((char *) d->toc_entry); + + /* + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020117, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "toc_entry of drive is already in use", 0, 0); + goto failed; + */ + } + + d->toc_entry = calloc(d->toc_entries, sizeof(struct burn_toc_entry)); + e = d->toc_entry; + e[0].point = 0xA0; + if (tar[0]->mode & BURN_AUDIO) + e[0].control = TOC_CONTROL_AUDIO; + else + e[0].control = TOC_CONTROL_DATA; + e[0].pmin = 1; + e[0].psec = o->format; + e[0].adr = 1; + e[1].point = 0xA1; + e[1].pmin = ntr; + e[1].adr = 1; + if (tar[ntr - 1]->mode & BURN_AUDIO) + e[1].control = TOC_CONTROL_AUDIO; + else + e[1].control = TOC_CONTROL_DATA; + e[2].point = 0xA2; + e[2].control = e[1].control; + e[2].adr = 1; + + tar[0]->pregap2 = 1; + pform = form; + for (i = 0; i < ntr; i++) { + type_to_form(tar[i]->mode, &ctladr, &form); + if (pform != form) { + + ret = add_cue(sheet, ctladr | 1, i + 1, 0, form, 0, + runtime); + if (ret <= 0) + goto failed; + + runtime += 150; +/* XXX fix pregap interval 1 for data tracks */ +/* ts A60813 silence righteous compiler warning about C++ style comments + This is possibly not a comment but rather a trace of Derek Foreman + experiments. Thus not to be beautified - but to be preserved rectified. +/ / if (!(form & BURN_AUDIO)) +/ / tar[i]->pregap1 = 1; +*/ + tar[i]->pregap2 = 1; + } +/* XXX HERE IS WHERE WE DO INDICES IN THE CUE SHEET */ +/* XXX and we should make sure the gaps conform to ecma-130... */ + tar[i]->entry = &e[3 + i]; + e[3 + i].point = i + 1; + burn_lba_to_msf(runtime, &m, &s, &f); + e[3 + i].pmin = m; + e[3 + i].psec = s; + e[3 + i].pframe = f; + e[3 + i].adr = 1; + e[3 + i].control = type_to_ctrl(tar[i]->mode); + burn_print(1, "track %d control %d\n", tar[i]->mode, + e[3 + i].control); + + ret = add_cue(sheet, ctladr | 1, i + 1, 1, form, 0, runtime); + if (ret <= 0) + goto failed; + + runtime += burn_track_get_sectors(tar[i]); +/* if we're padding, we'll clear any current shortage. + if we're not, we'll slip toc entries by a sector every time our + shortage is more than a sector +XXX this is untested :) +*/ + if (!tar[i]->pad) { + rem += burn_track_get_shortage(tar[i]); + + /* ts A61101 : I doubt that linking would yield a + desireable effect. With TAO it is + counterproductive in any way. + */ + if (o->write_type == BURN_WRITE_TAO) + tar[i]->source->next = NULL; + else + + if (i +1 != ntr) + tar[i]->source->next = tar[i+1]->source; + } else if (rem) { + rem = 0; + runtime++; + } + if (rem > burn_sector_length(tar[i]->mode)) { + rem -= burn_sector_length(tar[i]->mode); + runtime--; + } + pform = form; + } + burn_lba_to_msf(runtime, &m, &s, &f); + e[2].pmin = m; + e[2].psec = s; + e[2].pframe = f; + burn_print(1, "run time is %d (%d:%d:%d)\n", runtime, m, s, f); + for (i = 0; i < d->toc_entries; i++) + burn_print(1, "point %d (%02d:%02d:%02d)\n", + d->toc_entry[i].point, d->toc_entry[i].pmin, + d->toc_entry[i].psec, d->toc_entry[i].pframe); + ret = add_cue(sheet, ctladr | 1, 0xAA, 1, 1, 0, runtime); + if (ret <= 0) + goto failed; + return sheet; + +failed:; + if (sheet != NULL) + free((char *) sheet); + return NULL; +} + +int burn_sector_length(int tracktype) +{ + if (tracktype & BURN_AUDIO) + return 2352; + if (tracktype & BURN_MODE_RAW) + return 2352; + if (tracktype & BURN_MODE1) + return 2048; + /* ts A61009 */ + /* a ssert(0); */ + return -1; +} + +int burn_subcode_length(int tracktype) +{ + if (tracktype & BURN_SUBCODE_P16) + return 16; + if ((tracktype & BURN_SUBCODE_P96) || (tracktype & BURN_SUBCODE_R96)) + return 96; + return 0; +} + +int burn_write_leadin(struct burn_write_opts *o, + struct burn_session *s, int first) +{ + struct burn_drive *d = o->drive; + int count; + + d->busy = BURN_DRIVE_WRITING_LEADIN; + + burn_print(5, first ? " first leadin\n" : " leadin\n"); + + if (first) + count = 0 - d->alba - 150; + else + count = 4500; + + d->progress.start_sector = d->alba; + d->progress.sectors = count; + d->progress.sector = 0; + + while (count != 0) { + if (!sector_toc(o, s->track[0]->mode)) + return 0; + count--; + d->progress.sector++; + } + d->busy = BURN_DRIVE_WRITING; + return 1; +} + +int burn_write_leadout(struct burn_write_opts *o, + int first, unsigned char control, int mode) +{ + struct burn_drive *d = o->drive; + int count; + + d->busy = BURN_DRIVE_WRITING_LEADOUT; + + d->rlba = -150; + burn_print(5, first ? " first leadout\n" : " leadout\n"); + if (first) + count = 6750; + else + count = 2250; + d->progress.start_sector = d->alba; + d->progress.sectors = count; + d->progress.sector = 0; + + while (count != 0) { + if (!sector_lout(o, control, mode)) + return 0; + count--; + d->progress.sector++; + } + d->busy = BURN_DRIVE_WRITING; + return 1; +} + +int burn_write_session(struct burn_write_opts *o, struct burn_session *s) +{ + struct burn_drive *d = o->drive; + struct burn_track *prev = NULL, *next = NULL; + int i, ret; + + d->rlba = 0; + burn_print(1, " writing a session\n"); + for (i = 0; i < s->tracks; i++) { + if (i > 0) + prev = s->track[i - 1]; + if (i + 1 < s->tracks) + next = s->track[i + 1]; + else + next = NULL; + + if (!burn_write_track(o, s, i)) + { ret = 0; goto ex; } + } + + /* ts A61103 */ + ret = 1; +ex:; + if (o->write_type == BURN_WRITE_TAO) + burn_write_close_session(o, s); + return ret; +} + + +/* ts A61218 : outsourced from burn_write_track() */ +int burn_disc_init_track_status(struct burn_write_opts *o, + struct burn_session *s, int tnum, int sectors) +{ + struct burn_drive *d = o->drive; + + /* Update progress */ + d->progress.start_sector = d->nwa; + d->progress.sectors = sectors; + d->progress.sector = 0; + + /* ts A60831: added tnum-line, extended print message on proposal + by bonfire-app@wanadoo.fr in http://libburn.pykix.org/ticket/58 */ + d->progress.track = tnum; + + /* ts A61102 */ + d->busy = BURN_DRIVE_WRITING; + + return 1; +} + + +int burn_write_track(struct burn_write_opts *o, struct burn_session *s, + int tnum) +{ + struct burn_track *t = s->track[tnum]; + struct burn_drive *d = o->drive; + int i, tmp = 0, open_ended = 0, ret= 0, nwa, lba; + int sectors; + char msg[80]; + + d->rlba = -150; + +/* XXX for tao, we don't want the pregaps but still want post? */ + if (o->write_type != BURN_WRITE_TAO) { + + /* ts A61102 */ + d->busy = BURN_DRIVE_WRITING_PREGAP; + + if (t->pregap1) + d->rlba += 75; + if (t->pregap2) + d->rlba += 150; + + if (t->pregap1) { + struct burn_track *pt = s->track[tnum - 1]; + + if (tnum == 0) { + printf("first track should not have a pregap1\n"); + pt = t; + } + for (i = 0; i < 75; i++) + if (!sector_pregap(o, t->entry->point, + pt->entry->control, pt->mode)) + { ret = 0; goto ex; } + } + if (t->pregap2) + for (i = 0; i < 150; i++) + if (!sector_pregap(o, t->entry->point, + t->entry->control, t->mode)) + { ret = 0; goto ex; } + } else { + o->control = t->entry->control; + d->send_write_parameters(d, o); + + /* ts A61103 */ + ret = d->get_nwa(d, -1, &lba, &nwa); + sprintf(msg, + "pre-track %2.2d : get_nwa(%d), ret= %d , d->nwa= %d\n", + tnum+1, nwa, ret, d->nwa); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg,0,0); + if (nwa > d->nwa) + d->nwa = nwa; + } + +/* user data */ + + sectors = burn_track_get_sectors(t); + open_ended = burn_track_is_open_ended(t); + + burn_disc_init_track_status(o, s, tnum, sectors); + + burn_print(12, "track %d is %d sectors long\n", tnum, sectors); + + /* ts A61030 : this cannot happen. tnum is always < s->tracks */ + if (tnum == s->tracks) + tmp = sectors > 150 ? 150 : sectors; + + for (i = 0; open_ended || i < sectors - tmp; i++) { + + /* ts A61023 : http://libburn.pykix.org/ticket/14 + From time to time inquire drive buffer */ + if ((i%64)==0) + d->read_buffer_capacity(d); + + if (!sector_data(o, t, 0)) + { ret = 0; goto ex; } + + /* ts A61031 */ + if (open_ended) { + d->progress.sectors = sectors = i; + if (burn_track_is_data_done(t)) + break; + } + + /* update current progress */ + d->progress.sector++; + } + for (; i < sectors; i++) { + + /* ts A61030: program execution never gets to this point */ + fprintf(stderr,"LIBBURN_DEBUG: TNUM=%d TRACKS=%d TMP=%d\n", + tnum, s->tracks, tmp); + + burn_print(1, "last track, leadout prep\n"); + + /* ts A61023 */ + if ((i%64)==0) + d->read_buffer_capacity(d); + + if (!sector_data(o, t, 1)) + { ret = 0; goto ex; } + + /* update progress */ + d->progress.sector++; + } + + if (t->postgap) + for (i = 0; i < 150; i++) + if (!sector_postgap(o, t->entry->point, t->entry->control, + t->mode)) + { ret = 0; goto ex; } + i = t->offset; + if (o->write_type == BURN_WRITE_SAO) { + if (d->buffer->bytes) { + int err; + err = d->write(d, d->nwa, d->buffer); + if (err == BE_CANCELLED) + { ret = 0; goto ex; } + + /* A61101 : probably this is not all payload data */ + /* A61108 : but audio count is short without this */ + t->writecount += d->buffer->bytes; + t->written_sectors += d->buffer->sectors; + d->progress.buffered_bytes += d->buffer->bytes; + + d->nwa += d->buffer->sectors; + d->buffer->bytes = 0; + d->buffer->sectors = 0; + } + } + + /* ts A61103 */ + ret = 1; +ex:; + if (o->write_type == BURN_WRITE_TAO) { + + /* ts A61103 */ + /* >>> if cancelled: ensure that at least 600 kB get written */ + + if (!burn_write_flush(o, t)) + ret = 0; + + /* ts A61030 */ + if (burn_write_close_track(o, s, tnum) <= 0) + ret = 0; + } + return ret; +} + +/* ts A61009 */ +int burn_disc_write_is_ok(struct burn_write_opts *o, struct burn_disc *disc) +{ + int i, t; + char msg[80]; + + for (i = 0; i < disc->sessions; i++) + for (t = 0; t < disc->session[i]->tracks; t++) + if (!sector_headers_is_ok( + o, disc->session[i]->track[t]->mode)) + goto bad_track_mode_found; + return 1; +bad_track_mode_found:; + sprintf(msg, "Unsuitable track mode 0x%x in track %d of session %d", + disc->session[i]->track[t]->mode, i+1, t+1); + libdax_msgs_submit(libdax_messenger, -1, 0x0002010a, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return 0; +} + + +/* ts A61218 : outsourced from burn_disc_write_sync() */ +int burn_disc_init_write_status(struct burn_write_opts *o, + struct burn_disc *disc) +{ + struct burn_drive *d = o->drive; + + d->cancel = 0; + + /* init progress before showing the state */ + d->progress.session = 0; + d->progress.sessions = disc->sessions; + d->progress.track = 0; + d->progress.tracks = disc->session[0]->tracks; + /* TODO: handle indices */ + d->progress.index = 0; + d->progress.indices = disc->session[0]->track[0]->indices; + /* TODO: handle multissession discs */ + /* XXX: sectors are only set during write track */ + d->progress.start_sector = 0; + d->progress.sectors = 0; + d->progress.sector = 0; + d->progress.track = 0; + + /* ts A61023 */ + d->progress.buffer_capacity = 0; + d->progress.buffer_available = 0; + d->progress.buffered_bytes = 0; + d->progress.buffer_min_fill = 0xffffffff; + + d->busy = BURN_DRIVE_WRITING; + + return 1; +} + + +/* ts A61218 */ +int burn_dvd_write_track(struct burn_write_opts *o, + struct burn_session *s, int tnum) +{ + struct burn_track *t = s->track[tnum]; + struct burn_drive *d = o->drive; + struct buffer *out = d->buffer; + int sectors; + int i, open_ended = 0, ret= 0; + + sectors = burn_track_get_sectors(t); + open_ended = burn_track_is_open_ended(t); + + /* >>> any type specific track preparations */; + + burn_disc_init_track_status(o, s, tnum, sectors); + for (i = 0; open_ended || i < sectors; i++) { + + /* From time to time inquire drive buffer */ + if ((i%256)==0) + d->read_buffer_capacity(d); + + /* transact a (CD sized) sector */ + if (!sector_data(o, t, 0)) + { ret = 0; goto ex; } + + if (open_ended) { + d->progress.sectors = sectors = i; + if (burn_track_is_data_done(t)) + break; + } + + /* update current progress */ + d->progress.sector++; + } + + /* Pad up buffer to next full 32 kB */ + if (o->obs_pad && out->bytes > 0 && out->bytes < o->obs) { + memset(out->data + out->bytes, 0, o->obs - out->bytes); + out->sectors += (o->obs - out->bytes) / 2048; + out->bytes = o->obs; + } + ret = burn_write_flush(o, t); + if (ret <= 0) + goto ex; + + /* >>> any other normal track finalizing */; + + ret = 1; +ex:; + if (ret<=0) { + d->sync_cache(d); /* burn_write_flush() was not called */ + } + return ret; +} + + +/* ts A61219 */ +int burn_disc_close_session_dvd_plus_rw(struct burn_write_opts *o, + struct burn_session *s) +{ + struct burn_drive *d = o->drive; + + d->busy = BURN_DRIVE_CLOSING_SESSION; + /* This seems to be a quick end : "if (!dvd_compat)" */ + /* >>> Stop de-icing (ongoing background format) quickly + by mmc_close() (but with opcode[2]=0). + Wait for unit to get ready. + return 1; + */ + /* Else: end eventual background format in a "DVD-RO" compatible way */ + d->close_track_session(d, 1, 0); /* same as CLOSE SESSION for CD */ + d->busy = BURN_DRIVE_WRITING; + return 1; +} + + +/* ts A61228 */ +int burn_disc_close_session_dvd_minus_rw(struct burn_write_opts *o, + struct burn_session *s) +{ + struct burn_drive *d = o->drive; + + d->busy = BURN_DRIVE_CLOSING_SESSION; + if (d->current_profile == 0x13) { + d->close_track_session(d, 1, 0); /* CLOSE SESSION, 010b */ + + /* ??? under what circumstances to use close functiom 011b + "Finalize disc" ? */ + + } + d->busy = BURN_DRIVE_WRITING; + return 1; +} + + +/* ts A61218 */ +int burn_dvd_write_session(struct burn_write_opts *o, + struct burn_session *s) +{ + int i,ret; + struct burn_drive *d = o->drive; + + for (i = 0; i < s->tracks; i++) { + ret = burn_dvd_write_track(o, s, i); + if (ret <= 0) + break; + } + if (d->current_profile == 0x1a) { + /* DVD+RW */ + ret = burn_disc_close_session_dvd_plus_rw(o, s); + if (ret <= 0) + return 0; + } else if (d->current_profile == 0x13) { + /* DVD-RW restricted overwrite */ + if (d->dvd_minus_rw_incomplete) { + ret = burn_disc_close_session_dvd_minus_rw(o, s); + if (ret <= 0) + return 0; + } + } else if (d->current_profile == 0x12) { + /* DVD-RAM */ + /* ??? any finalization needed ? */; + } + return 1; +} + + +/* ts A61218 : learned much from dvd+rw-tools-7.0/growisofs_mmc.cpp */ +int burn_disc_setup_dvd_plus_rw(struct burn_write_opts *o, + struct burn_disc *disc) +{ + struct burn_drive *d = o->drive; + int ret; + char msg[160]; + + if (d->bg_format_status==0 || d->bg_format_status==1) { + d->busy = BURN_DRIVE_FORMATTING; + /* start or re-start dvd_plus_rw formatting */ + ret = d->format_unit(d, (off_t) 0, 0); + if (ret <= 0) + return 0; + d->busy = BURN_DRIVE_WRITING; + } + d->nwa = 0; + if (o->start_byte >= 0) { + d->nwa = o->start_byte / 2048; + + sprintf(msg, "Write start address is %d * 2048", d->nwa); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020127, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + } + + /* >>> perform OPC if needed */; + + /* >>> ? what else ? */; + + return 1; +} + + +/* ts A61228 : learned much from dvd+rw-tools-7.0/growisofs_mmc.cpp */ +int burn_disc_setup_dvd_minus_rw(struct burn_write_opts *o, + struct burn_disc *disc) +{ + struct burn_drive *d = o->drive; + char msg[160]; + int ret; + + d->nwa = 0; + if (o->start_byte >= 0) { + d->nwa = o->start_byte / 32768; /* align to 32 kB */ + + sprintf(msg, "Write start address is %d * 32768", d->nwa); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020127, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + + d->nwa *= 16; /* convert to 2048 block units */ + } + + if (d->current_profile == 0x13) { /* DVD-RW restricted overwrite */ + + /* ??? mmc5r03c.pdf 7.5.2 : + "For DVD-RW media ... If a medium is in Restricted overwrite + mode, this mode page shall not be used." + + But growisofs composes a page 5 and sends it. + mmc5r03c.pdf 5.3.16 , table 127 specifies that mode page 5 + shall be supported with feature 0026h Restricted Overwrite. + 5.3.22 describes a feature 002Ch Rigid Restrictive Overwrite + which seems to apply to DVD-RW and does not mention page 5. + + 5.4.14 finally states that profile 0013h includes feature + 002Ch rather than 0026h. + + d->send_write_parameters(d, o); + */ + + d->busy = BURN_DRIVE_FORMATTING; + + /* "quick grow" to at least byte equivalent of d->nwa */ + ret = d->format_unit(d, (off_t) d->nwa * (off_t) 2048, + (d->nwa > 0) << 3); + if (ret <= 0) + return 0; + d->busy = BURN_DRIVE_WRITING; + + } else { + sprintf(msg, "Unsuitable media detected. Profile %4.4Xh %s", + d->current_profile, d->current_profile_text); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002011e, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + return 0; + } + + /* >>> perform OPC if needed */; + + /* >>> */; + + return 1; +} + + +/* ts A61218 */ +int burn_dvd_write_sync(struct burn_write_opts *o, + struct burn_disc *disc) +{ + int i, ret, sx, tx, mode, exotic_track = 0; + struct burn_drive *d = o->drive; + char msg[160]; + + for (sx = 0; sx < disc->sessions; sx++) + for (tx = 0 ; tx < disc->session[sx]->tracks; tx++) { + mode = disc->session[sx]->track[tx]->mode; + if (disc->session[sx]->track[tx]->mode != BURN_MODE1) + exotic_track = 1; + } + if (exotic_track) { + sprintf(msg,"DVD Media are unsuitable for desired track type"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020123, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + goto early_failure; + } + + if (d->current_profile == 0x1a || d->current_profile == 0x13 || + d->current_profile == 0x12) { + /* DVD+RW , DVD-RW Restricted Overwrite , DVD-RAM */ + if (disc->sessions!=1 || disc->session[0]->tracks>1 + || o->multi ) { + sprintf(msg, + "Burning is restricted to a single track and no multi-session on %s", + d->current_profile_text); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002011f, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + goto early_failure; + } + } + if (d->current_profile == 0x1a || d->current_profile == 0x12) { + /* DVD+RW , DVD-RAM */ + if (o->start_byte >= 0 && (o->start_byte % 2048)) { + sprintf(msg, + "Write start address not properly aligned to 2048"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020125, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + goto early_failure; + } + ret = 1; + if (d->current_profile == 0x1a) + ret = burn_disc_setup_dvd_plus_rw(o, disc); + if (ret <= 0) { + sprintf(msg, + "Write preparation setup failed for DVD+RW"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020121, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + goto early_failure; + } + o->obs_pad = 0; /* no filling-up of track's last 32k buffer */ + + } else if (d->current_profile == 0x13) { /* DVD-RW Rest. Overwrite */ + if (o->start_byte >= 0 && (o->start_byte % 32768)) { + sprintf(msg, + "Write start address not properly aligned to 32K"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020125, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + goto early_failure; + } + ret = burn_disc_setup_dvd_minus_rw(o, disc); + if (ret <= 0) { + sprintf(msg, + "Write preparation setup failed for DVD-RW"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020121, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + goto early_failure; + } + + /* ??? is this necessary ? */ + o->obs_pad = 1; /* fill-up track's last 32k buffer */ + + } else { + sprintf(msg, "Unsuitable media detected. Profile %4.4Xh %s", + d->current_profile, d->current_profile_text); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002011e, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + goto early_failure; + } + o->obs = 32*1024; /* buffer flush trigger for sector.c:get_sector() */ + + for (i = 0; i < disc->sessions; i++) { + /* update progress */ + d->progress.session = i; + d->progress.tracks = disc->session[i]->tracks; + + ret = burn_dvd_write_session(o, disc->session[i]); + if (ret <= 0) + goto ex; + + /* XXX: currently signs an end of session */ + d->progress.sector = 0; + d->progress.start_sector = 0; + d->progress.sectors = 0; + } + + /* >>> eventual normal finalization measures */ + + ret = 1; +ex:; + + /* >>> eventual emergency finalization measures */ + + /* update media state records */ + burn_drive_mark_unready(d); + burn_drive_inquire_media(d); + + d->busy = BURN_DRIVE_IDLE; + return ret; +early_failure:; + return 0; +} + + +void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc) +{ + struct cue_sheet *sheet; + struct burn_drive *d = o->drive; + struct buffer buf; + struct burn_track *lt; + int first = 1, i, ret, lba, nwa = 0; + char msg[80]; + +/* ts A60924 : libburn/message.c gets obsoleted + burn_message_clear_queue(); +*/ + + /* ts A61224 */ + burn_disc_init_write_status(o, disc); /* must be done very early */ + + d->buffer = &buf; + memset(d->buffer, 0, sizeof(struct buffer)); + d->rlba = -150; + d->toc_temp = 9; + + /* ts A61218 */ + if (! d->current_is_cd_profile) { + ret = burn_dvd_write_sync(o, disc); + if (ret <= 0) + goto fail_wo_sync; + return; + } + + if (o->start_byte >= 0) { + sprintf(msg, "Write start address not supported"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020124, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + goto fail_wo_sync; + } + + burn_print(1, "sync write of %d CD sessions\n", disc->sessions); + +/* Apparently some drives require this command to be sent, and a few drives +return crap. so we send the command, then ignore the result. +*/ + /* ts A61107 : moved up send_write_parameters because LG GSA-4082B + seems to dislike get_nwa() in advance */ + d->alba = d->start_lba; /* ts A61114: this looks senseless */ + d->nwa = d->alba; + if (o->write_type == BURN_WRITE_TAO) { + nwa = 0; /* get_nwa() will be called in burn_track() */ + } else { + + d->send_write_parameters(d, o); + + ret = d->get_nwa(d, -1, &lba, &nwa); + sprintf(msg, "Inquired nwa: %d (ret=%d)", nwa, ret); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg,0,0); + } + + for (i = 0; i < disc->sessions; i++) { + /* update progress */ + d->progress.session = i; + d->progress.tracks = disc->session[i]->tracks; + + /* ts A61114: added parameter nwa */ + sheet = burn_create_toc_entries(o, disc->session[i], nwa); + + /* ts A61009 */ + if (sheet == NULL) + goto fail; + +/* print_cue(sheet);*/ + if (o->write_type == BURN_WRITE_SAO) + d->send_cue_sheet(d, sheet); + free(sheet); + + if (o->write_type == BURN_WRITE_RAW) { + if (!burn_write_leadin(o, disc->session[i], first)) + goto fail; + } else { + if (first) { + + /* ts A61030 : 0 made the burner take data. */ + /* ts A61103 : Meanwhile d->nwa is updated in + burn_write_track() */ + if(o->write_type == BURN_WRITE_TAO) { + d->nwa= d->alba = 0; + } else { + +#ifdef Libburn_sao_can_appenD + /* ts A61114: address for d->write() */ + if (d->status == BURN_DISC_APPENDABLE + && o->write_type == BURN_WRITE_SAO) { + d->nwa = d->alba = nwa-150; + + sprintf(msg, + "SAO appendable d->nwa= %d\n", d->nwa); + libdax_msgs_submit( + libdax_messenger, d->global_index, 0x000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg,0,0); + + } else { + d->nwa = -150; + d->alba = -150; + } +#else + d->nwa = -150; + d->alba = -150; +#endif /* ! Libburn_sao_can_appenD */ + + + } + + } else { + d->nwa += 4500; + d->alba += 4500; + } + } + if (!burn_write_session(o, disc->session[i])) + goto fail; + + lt = disc->session[i]->track[disc->session[i]->tracks - 1]; + if (o->write_type == BURN_WRITE_RAW) { + if (!burn_write_leadout(o, first, lt->entry->control, + lt->mode)) + goto fail; + } else { + + /* ts A61030 */ + if (o->write_type != BURN_WRITE_TAO) + + if (!burn_write_flush(o, NULL)) + goto fail; + + d->nwa += first ? 6750 : 2250; + d->alba += first ? 6750 : 2250; + } + if (first) + first = 0; + + /* XXX: currently signs an end of session */ + d->progress.sector = 0; + d->progress.start_sector = 0; + d->progress.sectors = 0; + } + + /* ts A61030: extended skipping of flush to TAO: session is closed */ + if (o->write_type != BURN_WRITE_SAO && o->write_type != BURN_WRITE_TAO) + if (!burn_write_flush(o, NULL)) + goto fail; + + sleep(1); + + /* ts A61125 : update media state records */ + burn_drive_mark_unready(d); + burn_drive_inquire_media(d); + + burn_print(1, "done\n"); + d->busy = BURN_DRIVE_IDLE; + + /* ts A61012 : This return was traditionally missing. I suspect this + to have caused Cdrskin_eject() failures */ + return; + +fail: + d->sync_cache(d); +fail_wo_sync:; + usleep(500001); /* ts A61222: to avoid a warning from remove_worker()*/ + burn_print(1, "done - failed\n"); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010b, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Burn run failed", 0, 0); + d->cancel = 1; + d->busy = BURN_DRIVE_IDLE; +} diff --git a/libburn/tags/ZeroThreeZero/libburn/write.h b/libburn/tags/ZeroThreeZero/libburn/write.h new file mode 100644 index 00000000..d2a03a0e --- /dev/null +++ b/libburn/tags/ZeroThreeZero/libburn/write.h @@ -0,0 +1,35 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__WRITE_H +#define BURN__WRITE_H + +struct cue_sheet; +struct burn_session; +struct burn_write_opts; +struct burn_disc; + +struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o, + struct burn_session *session, + int nwa); +int burn_sector_length(int trackmode); +int burn_subcode_length(int trackmode); + +/* ts A61009 */ +int burn_disc_write_is_ok(struct burn_write_opts *o, struct burn_disc *disc); + +void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc); +int burn_write_leadin(struct burn_write_opts *o, + struct burn_session *s, int first); +int burn_write_leadout(struct burn_write_opts *o, + int first, unsigned char control, int mode); +int burn_write_session(struct burn_write_opts *o, struct burn_session *s); +int burn_write_track(struct burn_write_opts *o, struct burn_session *s, + int tnum); +int burn_write_flush(struct burn_write_opts *o, struct burn_track *track); + +/* ts A61030 : necessary for TAO */ +int burn_write_close_track(struct burn_write_opts *o, struct burn_session *s, + int tnum); +int burn_write_close_session(struct burn_write_opts *o,struct burn_session *s); + +#endif /* BURN__WRITE_H */ diff --git a/libburn/tags/ZeroThreeZero/test/Makefile b/libburn/tags/ZeroThreeZero/test/Makefile new file mode 100644 index 00000000..062350dd --- /dev/null +++ b/libburn/tags/ZeroThreeZero/test/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/libburn/tags/ZeroThreeZero/test/dewav.c b/libburn/tags/ZeroThreeZero/test/dewav.c new file mode 100644 index 00000000..6166bc3d --- /dev/null +++ b/libburn/tags/ZeroThreeZero/test/dewav.c @@ -0,0 +1,215 @@ + +/* dewav + Demo of libburn extension libdax_audioxtr + Audio track data extraction facility of libdax and libburn. + Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL +*/ + +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + + +/* libdax_audioxtr is quite independent of libburn. It only needs + the messaging facility libdax_msgs. So we got two build variations: +*/ +#ifdef Dewav_without_libburN + +/* This build environment is standalone relying only on libdax components */ +#include "../libburn/libdax_msgs.h" +struct libdax_msgs *libdax_messenger= NULL; + +#else /* Dewav_without_libburN */ + +/* This build environment uses libdax_msgs via libburn */ +/* Thus the API header of libburn */ +#include "../libburn/libburn.h" + +#endif /* ! Dewav_without_libburN */ + + +/* The API for .wav extraction */ +#include "../libburn/libdax_audioxtr.h" + + +int main(int argc, char **argv) +{ + /* This program acts as filter from in_path to out_path */ + char *in_path= "", *out_path= "-"; + + /* The read-and-extract object for use with in_path */ + struct libdax_audioxtr *xtr= NULL; + /* The file descriptor eventually detached from xtr */ + int xtr_fd= -2; + + /* Default output is stdout */ + int out_fd= 1; + + /* Inquired source parameters */ + char *fmt, *fmt_info; + int num_channels, sample_rate, bits_per_sample, msb_first; + off_t data_size; + + /* Auxiliary variables */ + int ret, i, be_strict= 1, buf_count, detach_fd= 0, extract_all= 0; + char buf[2048]; + + if(argc < 2) + goto help; + for(i= 1; i<argc; i++) { + if(strcmp(argv[i],"-o")==0) { + if(i>=argc-1) { + fprintf(stderr,"%s: option -o needs a file address as argument.\n", + argv[0]); + exit(1); + } + i++; + out_path= argv[i]; + } else if(strcmp(argv[i],"--lax")==0) { + be_strict= 0; + } else if(strcmp(argv[i],"--strict")==0) { + be_strict= 1; + } else if(strcmp(argv[i],"--detach_fd")==0) { + /* Test the dirty detach method. Always --extract_all */ + detach_fd= 1; + } else if(strcmp(argv[i],"--extract_all")==0) { + /* Dirty : read all available bytes regardless of data_size */ + extract_all= 1; + } else if(strcmp(argv[i],"--help")==0) { +help:; + fprintf(stderr, + "usage: %s [-o output_path|\"-\"] [--lax|--strict] [source_path|\"-\"]\n", + argv[0]); + exit(0); + } else { + if(in_path[0]!=0) { + fprintf(stderr,"%s: only one input file is allowed.\n", argv[0]); + exit(2); + } + in_path= argv[i]; + } + } + if(in_path[0] == 0) + in_path= "-"; + + +/* Depending on wether this was built standalone or with full libburn : +*/ +#ifdef Dewav_without_libburN + + /* Initialize and set up libdax messaging system */ + ret= libdax_msgs_new(&libdax_messenger,0); + if(ret<=0) { + fprintf(stderr,"Failed to create libdax_messenger object.\n"); + exit(3); + } + libdax_msgs_set_severities(libdax_messenger, LIBDAX_MSGS_SEV_NEVER, + LIBDAX_MSGS_SEV_NOTE, "", 0); + fprintf(stderr, "dewav on libdax\n"); + +#else /* Dewav_without_libburN */ + + /* Initialize libburn and set up its messaging system */ + if(burn_initialize() == 0) { + fprintf(stderr,"Failed to initialize libburn.\n"); + exit(3); + } + /* Print messages of severity NOTE or more directly to stderr */ + burn_msgs_set_severities("NEVER", "NOTE", ""); + fprintf(stderr, "dewav on libburn\n"); + +#endif /* ! Dewav_without_libburN */ + + + /* Open audio source and create extractor object */ + ret= libdax_audioxtr_new(&xtr, in_path, 0); + if(ret<=0) + exit(4); + if(strcmp(out_path,"-")!=0) { + out_fd= open(out_path, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if(out_fd == -1) { + fprintf(stderr, "Cannot open file: %s\n", out_path); + fprintf(stderr, "Error reported: '%s' (%d)\n",strerror(errno), errno); + exit(5); + } + } + /* Obtain and print parameters of audio source */ + libdax_audioxtr_get_id(xtr, &fmt, &fmt_info, + &num_channels, &sample_rate, &bits_per_sample, &msb_first, 0); + fprintf(stderr, "Detected format: %s\n", fmt_info); + libdax_audioxtr_get_size(xtr, &data_size, 0); + fprintf(stderr, "Data size : %.f bytes\n", (double) data_size); + if((strcmp(fmt,".wav")!=0 && strcmp(fmt,".au")!=0) || + num_channels!=2 || sample_rate!=44100 || bits_per_sample!=16) { + fprintf(stderr, + "%sAudio source parameters do not comply to cdrskin/README specs\n", + (be_strict ? "" : "WARNING: ")); + if(be_strict) + exit(6); + } + if(msb_first==0) + fprintf(stderr, + "NOTE: Extracted data to be written with cdrskin option -swab\n"); + + if(detach_fd) { + /* Take over fd from xtr */; + ret= libdax_audioxtr_detach_fd(xtr, &xtr_fd, 0); + if(ret<=0) { + fprintf(stderr, "Cannot detach file descriptor from extractor\n"); + exit(8); + } + /* not needed any more */ + libdax_audioxtr_destroy(&xtr, 0); + fprintf(stderr, "Note: detached fd and freed extractor object.\n"); + } + + /* Extract and put out raw audio data */; + while(1) { + if(detach_fd) { + buf_count= read(xtr_fd, buf, sizeof(buf)); + if(buf_count==-1) + fprintf(stderr,"Error while reading from detached fd\n(%d) '%s'\n", + errno, strerror(errno)); + } else { + buf_count= libdax_audioxtr_read(xtr, buf, sizeof(buf), !!extract_all); + } + if(buf_count < 0) + exit(7); + if(buf_count == 0) + break; + + ret= write(out_fd, buf, buf_count); + if(ret == -1) { + fprintf(stderr, "Failed to write buffer of %d bytes to: %s\n", + buf_count, out_path); + fprintf(stderr, "Error reported: '%s' (%d)\n", strerror(errno), errno); + exit(5); + } + + } + + /* Shutdown */ + if(out_fd>2) + close(out_fd); + /* ( It is permissible to do this with xtr==NULL ) */ + libdax_audioxtr_destroy(&xtr, 0); + +#ifdef Dewav_without_libburN + + libdax_msgs_destroy(&libdax_messenger,0); + +#else /* Dewav_without_libburN */ + + burn_finish(); + +#endif /* ! Dewav_without_libburN */ + + exit(0); +} diff --git a/libburn/tags/ZeroThreeZero/test/fake_au.c b/libburn/tags/ZeroThreeZero/test/fake_au.c new file mode 100644 index 00000000..01d7a416 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/test/fake_au.c @@ -0,0 +1,164 @@ + +/* fake_au + Fakes a file in SUN .au format from a raw little-endian PCM audio file + (e.g. a file extracted from .wav by test/dewav). The input data are assumed + to be 16 bit, stereo, 44100 Hz. + Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL + + Info used: http://www.opengroup.org/public/pubs/external/auformat.html +*/ + + +#include <ctype.h> +#include <sys/types.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + + +int fake_write(unsigned char *buf, size_t size, FILE *fp) +{ + int ret; + + ret= fwrite(buf,size,1,fp); + if(ret==1) + return(1); + fprintf(stderr,"Error %d while writing: '%s'\n",errno,strerror(errno)); + return(0); +} + + +int main(int argc, char **argv) +{ + int ret, i; + unsigned data_size= 0,byte_count,exit_value= 0; + FILE *fp_out= stdout,*fp_in= stdin; + unsigned char buf[4]; + char out_path[4096],in_path[4096]; + struct stat stbuf; + + strcpy(out_path,"-"); + strcpy(in_path,""); + if(argc < 2) { + exit_value= 1; + goto help; + } + for(i= 1; i<argc; i++) { + if(strcmp(argv[i],"-o")==0) { + if(i>=argc-1) { + fprintf(stderr,"%s: option -o needs a file address as argument.\n", + argv[0]); + exit(1); + } + i++; + strcpy(out_path, argv[i]); + } else if(strcmp(argv[i],"--stdin_size")==0) { + if(i>=argc-1) { + fprintf(stderr,"%s: option --stdin_size needs a number as argument.\n", + argv[0]); + exit(1); + } + i++; + sscanf(argv[i],"%u",&data_size); + } else if(strcmp(argv[i],"--help")==0) { + exit_value= 0; +help:; + fprintf(stderr,"usage: %s \\\n", argv[0]); + fprintf(stderr," [-o output_path|\"-\"] [source_path | --stdin_size size]\n"); + fprintf(stderr, + "Disguises an extracted .wav stream as .au stereo, 16bit, 44100Hz\n"); + fprintf(stderr, + "stdin gets byte-swapped and appended up to the announced data_size.\n"); + exit(exit_value); + } else { + if(in_path[0]!=0) { + fprintf(stderr,"%s: only one input file is allowed.\n", argv[0]); + exit(1); + } + strcpy(in_path, argv[i]); + } + } + + if(strcmp(in_path,"-")==0 || in_path[0]==0) { + if(data_size==0) { + fprintf(stderr,"%s: input from stdin needs option --stdin_size.\n", + argv[0]); + exit(6); + } + fp_in= stdin; + } else { + fp_in= fopen(in_path,"r"); + if(stat(in_path,&stbuf)!=-1) + data_size= stbuf.st_size; + } + if(fp_in==NULL) { + fprintf(stderr,"Error %d while fopen(\"%s\",\"r\") : '%s'\n", + errno,in_path,strerror(errno)); + exit(2); + } + + if(strcmp(out_path,"-")==0) { + fp_out= stdout; + } else { + if(stat(out_path,&stbuf)!=-1) { + fprintf(stderr,"%s: file '%s' already existing\n",argv[0],out_path); + exit(4); + } + fp_out= fopen(out_path,"w"); + } + if(fp_out==NULL) { + fprintf(stderr,"Error %d while fopen(\"%s\",\"w\") : '%s'\n", + errno,out_path,strerror(errno)); + exit(2); + } + + fake_write((unsigned char *) ".snd",4,fp_out); /* magic number */ + buf[0]= buf[1]= buf[2]= 0; + buf[3]= 32; + fake_write(buf,4,fp_out); /* data_location */ + buf[0]= (data_size>>24)&0xff; + buf[1]= (data_size>>16)&0xff; + buf[2]= (data_size>>8)&0xff; + buf[3]= (data_size)&0xff; + fake_write(buf,4,fp_out); /* data_size */ + buf[0]= buf[1]= buf[2]= 0; + buf[3]= 3; + fake_write(buf,4,fp_out); /* encoding 16 Bit PCM */ + buf[0]= buf[1]= 0; + buf[2]= 172; + buf[3]= 68; + fake_write(buf,4,fp_out); /* sample rate 44100 Hz */ + buf[0]= buf[1]= buf[2]= 0; + buf[3]= 2; + fake_write(buf,4,fp_out); /* number of channels */ + buf[0]= buf[1]= buf[2]= buf[3]= 0; + fake_write(buf,4,fp_out); /* padding */ + fake_write(buf,4,fp_out); /* padding */ + + for(byte_count= 0; byte_count<data_size; byte_count+=2) { + ret= fread(buf,2,1,fp_in); + if(ret<=0) { + fprintf(stderr,"Premature end end of input\n"); + exit_value= 5; + break; + } + buf[3]= buf[0]; + buf[2]= buf[1]; + ret= fake_write(buf+2,2,fp_out); + if(ret<=0) { + exit_value= 3; + break; + } + } + if(fp_out!=stdout) + fclose(fp_out); + if(fp_in!=stdin) + fclose(fp_in); + fprintf(stderr, "Swapped and appended: %u stdin bytes\n",byte_count); + exit(exit_value); +} + diff --git a/libburn/tags/ZeroThreeZero/test/libburner.c b/libburn/tags/ZeroThreeZero/test/libburner.c new file mode 100644 index 00000000..0144fe6a --- /dev/null +++ b/libburn/tags/ZeroThreeZero/test/libburner.c @@ -0,0 +1,718 @@ + +/* test/libburner.c , API illustration of burning data or audio tracks to CD */ +/* Copyright (C) 2005 - 2006 Thomas Schmitt <scdbackup@gmx.net> */ +/* Provided under GPL, see also "License and copyright aspects" at file end */ + + +/** Overview + + libburner is a minimal demo application for the library libburn as provided + on http://libburnia.pykix.org . It can list the available devices, can + blank a CD-RW, can format a DVD-RW, and can burn to CD-R, CD-RW, DVD+RW, + DVD-RAM or DVD-RW. + It's main purpose, nevertheless, is to show you how to use libburn and also + to serve the libburnia team as reference application. libburner.c does indeed + define the standard way how above three gestures can be implemented and + stay upward compatible for a good while. + + Before you can do anything, you have to initialize libburn by + burn_initialize() + and provide some signal and abort handling, e.g. by the builtin handler, by + burn_set_signal_handling() + as it is done in main() at the end of this file. Then you aquire a + drive in an appropriate way conforming to the API. The two main + approaches are shown here in application functions: + libburner_aquire_by_adr() demonstrates usage as of cdrecord traditions + libburner_aquire_by_driveno() demonstrates a scan-and-choose approach + With that aquired drive you can blank a CD-RW + libburner_blank_disc() + or you can format a DVD-RW to profile "Restricted Overwrite" (needed once) + libburner_format_row() + With the aquired drive you can burn to CD-R, CD-RW, DVD+RW, DVD-RAM, DVD-RW + libburner_payload() + When everything is done, main() releases the drive and shuts down libburn: + burn_drive_release(); + burn_finish() + +*/ + +/** See this for the decisive API specs . libburn.h is The Original */ +/* For using the installed header file : #include <libburn/libburn.h> */ +/* This program insists in the own headerfile. */ +#include "../libburn/libburn.h" + +/* libburn is intended for Linux systems with kernel 2.4 or 2.6 for now */ +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include <errno.h> +#include <sys/stat.h> +#include <fcntl.h> + + +/** For simplicity i use global variables to represent the drives. + Drives are systemwide global, so we do not give away much of good style. +*/ + +/** This list will hold the drives known to libburn. This might be all CD + drives of the system and thus might impose severe impact on the system. +*/ +static struct burn_drive_info *drive_list; + +/** If you start a long lasting operation with drive_count > 1 then you are + not friendly to the users of other drives on those systems. Beware. */ +static unsigned int drive_count; + +/** This variable indicates wether the drive is grabbed and must be + finally released */ +static int drive_is_grabbed = 0; + +/** A number and a text describing the type of media in aquired drive */ +static int current_profile= -1; +static char current_profile_name[80]= {""}; + + +/* Some in-advance definitions to allow a more comprehensive ordering + of the functions and their explanations in here */ +int libburner_aquire_by_adr(char *drive_adr); +int libburner_aquire_by_driveno(int *drive_no); + + +/* ------------------------------- API gestures ---------------------------- */ + +/** You need to aquire a drive before burning. The API offers this as one + compact call and alternatively as application controllable gestures of + whitelisting, scanning for drives and finally grabbing one of them. + + If you have a persistent address of the drive, then the compact call is + to prefer because it only touches one drive. On modern Linux kernels, + there should be no fatal disturbance of ongoing burns of other libburn + instances with any of our approaches. We use open(O_EXCL) by default. + On /dev/hdX it should cooperate with growisofs and some cdrecord variants. + On /dev/sgN versus /dev/scdM expect it not to respect other programs. +*/ +int libburner_aquire_drive(char *drive_adr, int *driveno) +{ + int ret; + + if(drive_adr != NULL && drive_adr[0] != 0) + ret = libburner_aquire_by_adr(drive_adr); + else + ret = libburner_aquire_by_driveno(driveno); + if (ret <= 0) + return ret; + burn_disc_get_profile(drive_list[0].drive, ¤t_profile, + current_profile_name); + if (current_profile_name[0]) + printf("Detected media type: %s\n", current_profile_name); + return 1; +} + + +/** If the persistent drive address is known, then this approach is much + more un-obtrusive to the systemwide livestock of drives. Only the + given drive device will be opened during this procedure. +*/ +int libburner_aquire_by_adr(char *drive_adr) +{ + int ret; + char libburn_drive_adr[BURN_DRIVE_ADR_LEN]; + + /* This tries to resolve links or alternative device files */ + ret = burn_drive_convert_fs_adr(drive_adr, libburn_drive_adr); + if (ret<=0) { + fprintf(stderr,"Address does not lead to a CD burner: '%s'\n", + drive_adr); + return ret; + } + + printf("Aquiring drive '%s' ...\n",libburn_drive_adr); + ret = burn_drive_scan_and_grab(&drive_list,libburn_drive_adr,1); + if (ret <= 0) { + fprintf(stderr,"FAILURE with persistent drive address '%s'\n", + libburn_drive_adr); + } else { + printf("Done\n"); + drive_is_grabbed = 1; + } + return ret; +} + + +/** This method demonstrates how to use libburn without knowing a persistent + drive address in advance. It has to make sure that after assessing the list + of available drives, all unwanted drives get closed again. As long as they + are open, no other libburn instance can see them. This is an intended + locking feature. The application is responsible for giving up the locks + by either burn_drive_release() (only after burn_drive_grab() !), + burn_drive_info_forget(), burn_drive_info_free(), or burn_finish(). + @param driveno the index number in libburn's drive list. This will get + set to 0 on success and will then be the drive index to + use in the further dourse of processing. + @return 1 success , <= 0 failure +*/ +int libburner_aquire_by_driveno(int *driveno) +{ + char adr[BURN_DRIVE_ADR_LEN]; + int ret, i; + + printf("Beginning to scan for devices ...\n"); + while (!burn_drive_scan(&drive_list, &drive_count)) + usleep(1002); + if (drive_count <= 0 && *driveno >= 0) { + printf("FAILED (no drives found)\n"); + return 0; + } + printf("Done\n"); + + /* + Interactive programs may choose the drive number at this moment. + + drive[0] to drive[drive_count-1] are struct burn_drive_info + as defined in libburn/libburn.h . This structure is part of API + and thus will strive for future compatibility on source level. + Have a look at the info offered. + Caution: do not take .location for drive address. Always use + burn_drive_get_adr() or you might become incompatible + in future. + Note: bugs with struct burn_drive_info - if any - will not be + easy to fix. Please report them but also strive for + workarounds on application level. + */ + printf("\nOverview of accessible drives (%d found) :\n", + drive_count); + printf("-----------------------------------------------------------------------------\n"); + for (i = 0; i < drive_count; i++) { + if (burn_drive_get_adr(&(drive_list[i]), adr) <=0) + strcpy(adr, "-get_adr_failed-"); + printf("%d --drive '%s' : '%s' '%s'\n", + i,adr,drive_list[i].vendor,drive_list[i].product); + } + printf("-----------------------------------------------------------------------------\n\n"); + + /* + On multi-drive systems save yourself from sysadmins' revenge. + + Be aware that you hold reserved all available drives at this point. + So either make your choice quick enough not to annoy other system + users, or set free the drives for a while. + + The tested way of setting free all drives is to shutdown the library + and to restart when the choice has been made. The list of selectable + drives should also hold persistent drive addresses as obtained + above by burn_drive_get_adr(). By such an address one may use + burn_drive_scan_and_grab() to finally aquire exactly one drive. + + A not yet tested shortcut should be to call burn_drive_info_free() + and to call either burn_drive_scan() or burn_drive_scan_and_grab() + before accessing any drives again. + + In both cases you have to be aware that the desired drive might get + aquired in the meantime by another user resp. libburn process. + */ + + /* We already made our choice via command line. (default is 0) + So we just have to keep our desired drive and drop all others. + No other libburn instance will have a chance to steal our drive. + */ + if (*driveno < 0) { + printf("Pseudo-drive \"-\" given : bus scanning done.\n"); + return 2; /* the program will end after this */ + } + if (drive_count <= *driveno) { + fprintf(stderr, + "Found only %d drives. Number %d not available.\n", + drive_count, *driveno); + return 0; /* the program will end after this */ + } + + /* Drop all drives which we do not want to use */ + for (i = 0; i < drive_count; i++) { + if (i == *driveno) /* the one drive we want to keep */ + continue; + ret = burn_drive_info_forget(&(drive_list[i]),0); + if (ret != 1) + fprintf(stderr, "Cannot drop drive %d. Please report \"ret=%d\" to libburn-hackers@pykix.org\n", + i, ret); + else + printf("Dropped unwanted drive %d\n",i); + } + /* Make the one we want ready for blanking or burning */ + ret= burn_drive_grab(drive_list[*driveno].drive, 1); + if (ret != 1) + return 0; + drive_is_grabbed = 1; + return 1; +} + + +/** Makes a previously used CD-RW ready for thorough re-usal. + + To our knowledge it is hardly possible to abort an ongoing blank operation + because after start it is entirely handled by the drive. + So expect signal handling to wait the normal blanking timespan until it + can allow the process to end. External kill -9 will not help the drive. +*/ +int libburner_blank_disc(struct burn_drive *drive, int blank_fast) +{ + enum burn_disc_status disc_state; + struct burn_progress p; + int percent = 1; + + disc_state = burn_disc_get_status(drive); + printf( + "Drive media status: %d (see libburn/libburn.h BURN_DISC_*)\n", + disc_state); + if (disc_state == BURN_DISC_BLANK) { + fprintf(stderr, + "IDLE: Blank media detected. Will leave it untouched\n"); + return 2; + } else if (disc_state == BURN_DISC_FULL || + disc_state == BURN_DISC_APPENDABLE) { + ; /* this is what libburn is willing to blank */ + } else if (disc_state == BURN_DISC_EMPTY) { + fprintf(stderr,"FATAL: No media detected in drive\n"); + return 0; + } else { + fprintf(stderr, + "FATAL: Unsuitable drive and media state\n"); + return 0; + } + if(!burn_disc_erasable(drive)) { + fprintf(stderr, + "FATAL : Media is not of erasable type\n"); + return 0; + } + printf( + "Beginning to %s-blank media.\n", (blank_fast?"fast":"full")); + burn_disc_erase(drive, blank_fast); + sleep(1); + while (burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) { + if(p.sectors>0 && p.sector>=0) /* display 1 to 99 percent */ + percent = 1.0 + ((double) p.sector+1.0) + / ((double) p.sectors) * 98.0; + printf("Blanking ( %d%% done )\n", percent); + sleep(1); + } + printf("Done\n"); + return 1; +} + + +/** Persistently changes DVD-RW profile 0014h "Sequential Recording" + to profile 0013h "Restricted Overwrite" which is usable with libburner. + + Expect a behavior similar to blanking with unusual noises from the drive. +*/ +int libburner_format_row(struct burn_drive *drive) +{ + struct burn_progress p; + int percent = 1; + + if (current_profile == 0x13) { + fprintf(stderr, "IDLE: DVD-RW media is already formatted\n"); + return 2; + } else if (current_profile != 0x14) { + fprintf(stderr, "FATAL: Can only format DVD-RW\n"); + return 0; + } + printf("Beginning to format media.\n"); + burn_disc_format(drive, (off_t) 0, 0); + + sleep(1); + while (burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) { + if(p.sectors>0 && p.sector>=0) /* display 1 to 99 percent */ + percent = 1.0 + ((double) p.sector+1.0) + / ((double) p.sectors) * 98.0; + printf("Formatting ( %d%% done )\n", percent); + sleep(1); + } + burn_disc_get_profile(drive_list[0].drive, ¤t_profile, + current_profile_name); + printf("Media type now: %4.4xh \"%s\"\n", + current_profile, current_profile_name); + if (current_profile != 0x13) { + fprintf(stderr, + "FATAL: Failed to change media profile to desired value\n"); + return 0; + } + return 1; +} + + +/** Brings preformatted track images (ISO 9660, audio, ...) onto media. + To make sure a data image is fully readable on any Linux machine, this + function adds 300 kB of padding to the (usualy single) track. + Audio tracks get padded to complete their last sector. + + In case of external signals expect abort handling of an ongoing burn to + last up to a minute. Wait the normal burning timespan before any kill -9. +*/ +int libburner_payload(struct burn_drive *drive, + char source_adr[][4096], int source_adr_count, + int multi, int simulate_burn, int all_tracks_type) +{ + struct burn_source *data_src; + struct burn_disc *target_disc; + struct burn_session *session; + struct burn_write_opts *burn_options; + enum burn_disc_status disc_state; + struct burn_track *track, *tracklist[99]; + struct burn_progress progress; + time_t start_time; + int last_sector = 0, padding = 0, trackno, write_mode_tao = 0, fd; + off_t fixed_size; + char *adr; + struct stat stbuf; + + if (all_tracks_type != BURN_AUDIO) { + all_tracks_type = BURN_MODE1; + /* a padding of 300 kB helps to avoid the read-ahead bug */ + padding = 300*1024; + } + + target_disc = burn_disc_create(); + session = burn_session_create(); + burn_disc_add_session(target_disc, session, BURN_POS_END); + + for (trackno = 0 ; trackno < source_adr_count; trackno++) { + tracklist[trackno] = track = burn_track_create(); + burn_track_define_data(track, 0, padding, 1, all_tracks_type); + + adr = source_adr[trackno]; + fixed_size = 0; + if (adr[0] == '-' && adr[1] == 0) { + fd = 0; + } else { + fd = open(adr, O_RDONLY); + if (fd>=0) + if (fstat(fd,&stbuf)!=-1) + if((stbuf.st_mode&S_IFMT)==S_IFREG) + fixed_size = stbuf.st_size; + } + if (fixed_size==0) + write_mode_tao = 1; + data_src = NULL; + if (fd>=0) + data_src = burn_fd_source_new(fd, -1, fixed_size); + if (data_src == NULL) { + fprintf(stderr, + "FATAL: Could not open data source '%s'.\n",adr); + if(errno!=0) + fprintf(stderr,"(Most recent system error: %s )\n", + strerror(errno)); + return 0; + } + if (burn_track_set_source(track, data_src) != BURN_SOURCE_OK) { + printf("FATAL: Cannot attach source object to track object\n"); + return 0; + } + + burn_session_add_track(session, track, BURN_POS_END); + printf("Track %d : source is '%s'\n", trackno+1, adr); + burn_source_free(data_src); + } /* trackno loop end */ + + /* Evaluate drive and media */ + disc_state = burn_disc_get_status(drive); + if (disc_state == BURN_DISC_APPENDABLE) { + write_mode_tao = 1; + } else if (disc_state != BURN_DISC_BLANK) { + if (disc_state == BURN_DISC_FULL) { + fprintf(stderr, "FATAL: Closed media with data detected. Need blank or appendable media.\n"); + if (burn_disc_erasable(drive)) + fprintf(stderr, "HINT: Try --blank_fast\n\n"); + } else if (disc_state == BURN_DISC_EMPTY) + fprintf(stderr,"FATAL: No media detected in drive\n"); + else + fprintf(stderr, + "FATAL: Cannot recognize state of drive and media\n"); + return 0; + } + + burn_options = burn_write_opts_new(drive); + burn_write_opts_set_perform_opc(burn_options, 0); + burn_write_opts_set_multi(burn_options, !!multi); + if (write_mode_tao) + burn_write_opts_set_write_type(burn_options, + BURN_WRITE_TAO, BURN_BLOCK_MODE1); + else + burn_write_opts_set_write_type(burn_options, + BURN_WRITE_SAO, BURN_BLOCK_SAO); + if(simulate_burn) + printf("\n*** Will TRY to SIMULATE burning ***\n\n"); + burn_write_opts_set_simulate(burn_options, simulate_burn); + burn_structure_print_disc(target_disc); + burn_drive_set_speed(drive, 0, 0); + burn_write_opts_set_underrun_proof(burn_options, 1); + + printf("Burning starts. With e.g. 4x media expect up to a minute of zero progress.\n"); + start_time = time(0); + burn_disc_write(burn_options, target_disc); + + burn_write_opts_free(burn_options); + while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING) + usleep(1002); + while (burn_drive_get_status(drive, &progress) != BURN_DRIVE_IDLE) { + if( progress.sectors <= 0 || progress.sector == last_sector) + printf( + "Thank you for being patient since %d seconds.\n", + (int) (time(0) - start_time)); + else if(write_mode_tao) + printf("Track %d : sector %d\n", progress.track+1, + progress.sector); + else + printf("Track %d : sector %d of %d\n",progress.track+1, + progress.sector, progress.sectors); + last_sector = progress.sector; + sleep(1); + } + printf("\n"); + + for (trackno = 0 ; trackno < source_adr_count; trackno++) + burn_track_free(tracklist[trackno]); + burn_session_free(session); + burn_disc_free(target_disc); + if (multi && current_profile != 0x1a && current_profile != 0x13 && + current_profile != 0x12) /* not with DVD+RW, DVD-RW, DVD-RAM */ + printf("NOTE: Media left appendable.\n"); + if (simulate_burn) + printf("\n*** Did TRY to SIMULATE burning ***\n\n"); + return 1; +} + + +/** The setup parameters of libburner */ +static char drive_adr[BURN_DRIVE_ADR_LEN] = {""}; +static int driveno = 0; +static int do_blank = 0; +static char source_adr[99][4096]; +static int source_adr_count = 0; +static int do_multi = 0; +static int simulate_burn = 0; +static int all_tracks_type = BURN_MODE1; + + +/** Converts command line arguments into above setup parameters. + drive_adr[] must provide at least BURN_DRIVE_ADR_LEN bytes. + source_adr[] must provide at least 4096 bytes. +*/ +int libburner_setup(int argc, char **argv) +{ + int i, insuffient_parameters = 0, print_help = 0; + + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--audio")) { + all_tracks_type = BURN_AUDIO; + + } else if (!strcmp(argv[i], "--blank_fast")) { + do_blank = 1; + + } else if (!strcmp(argv[i], "--blank_full")) { + do_blank = 2; + + } else if (!strcmp(argv[i], "--burn_for_real")) { + simulate_burn = 0; + + } else if (!strcmp(argv[i], "--drive")) { + ++i; + if (i >= argc) { + fprintf(stderr,"--drive requires an argument\n"); + return 1; + } else if (strcmp(argv[i], "-") == 0) { + drive_adr[0] = 0; + driveno = -1; + } else if (isdigit(argv[i][0])) { + drive_adr[0] = 0; + driveno = atoi(argv[i]); + } else { + if(strlen(argv[i]) >= BURN_DRIVE_ADR_LEN) { + fprintf(stderr,"--drive address too long (max. %d)\n", + BURN_DRIVE_ADR_LEN-1); + return 2; + } + strcpy(drive_adr, argv[i]); + } + } else if (!strcmp(argv[i], "--format_overwrite")) { + do_blank = 101; + + } else if (!strcmp(argv[i], "--multi")) { + do_multi = 1; + + } else if (!strcmp(argv[i], "--stdin_size")) { /* obsoleted */ + i++; + + } else if (!strcmp(argv[i], "--try_to_simulate")) { + simulate_burn = 1; + + } else if (!strcmp(argv[i], "--help")) { + print_help = 1; + + } else if (!strncmp(argv[i], "--",2)) { + fprintf(stderr, "Unidentified option: %s\n", argv[i]); + return 7; + } else { + if(strlen(argv[i]) >= 4096) { + fprintf(stderr, "Source address too long (max. %d)\n", 4096-1); + return 5; + } + if(source_adr_count >= 99) { + fprintf(stderr, "Too many tracks (max. 99)\n"); + return 6; + } + strcpy(source_adr[source_adr_count], argv[i]); + source_adr_count++; + } + } + insuffient_parameters = 1; + if (driveno < 0) + insuffient_parameters = 0; + if (source_adr_count > 0) + insuffient_parameters = 0; + if (do_blank) + insuffient_parameters = 0; + if (print_help || insuffient_parameters ) { + printf("Usage: %s\n", argv[0]); + printf(" [--drive <address>|<driveno>|\"-\"] [--audio]\n"); + printf(" [--blank_fast|--blank_full|--format_overwrite]\n"); + printf(" [--try_to_simulate]\n"); + printf(" [--multi] [<one or more imagefiles>|\"-\"]\n"); + printf("Examples\n"); + printf("A bus scan (needs rw-permissions to see a drive):\n"); + printf(" %s --drive -\n",argv[0]); + printf("Burn a file to drive chosen by number, leave appendable:\n"); + printf(" %s --drive 0 --multi my_image_file\n", argv[0]); + printf("Burn a file to drive chosen by persistent address, close:\n"); + printf(" %s --drive /dev/hdc my_image_file\n", argv[0]); + printf("Blank a used CD-RW (is combinable with burning in one run):\n"); + printf(" %s --drive /dev/hdc --blank_fast\n",argv[0]); + printf("Format a DVD-RW once before first use with libburner:\n"); + printf(" %s --drive /dev/hdc --format_overwrite\n", argv[0]); + printf("Burn two audio tracks:\n"); + printf(" lame --decode -t /path/to/track1.mp3 track1.cd\n"); + printf(" test/dewav /path/to/track2.wav -o track2.cd\n"); + printf(" %s --drive /dev/hdc --audio track1.cd track2.cd\n", argv[0]); + printf("Burn a compressed afio archive on-the-fly:\n"); + printf(" ( cd my_directory ; find . -print | afio -oZ - ) | \\\n"); + printf(" %s --drive /dev/hdc -\n", argv[0]); + printf("To be read from *not mounted* media via: afio -tvZ /dev/hdc\n"); + if (insuffient_parameters) + return 6; + } + return 0; +} + + +int main(int argc, char **argv) +{ + int ret; + + ret = libburner_setup(argc, argv); + if (ret) + exit(ret); + + printf("Initializing libburnia.pykix.org ...\n"); + if (burn_initialize()) + printf("Done\n"); + else { + printf("FAILED\n"); + fprintf(stderr,"\nFATAL: Failed to initialize.\n"); + exit(33); + } + + /* Print messages of severity SORRY or more directly to stderr */ + burn_msgs_set_severities("NEVER", "SORRY", "libburner : "); + + /* Activate the default signal handler which eventually will try to + properly shutdown drive and library on aborting events. */ + burn_set_signal_handling("libburner : ", NULL, 0); + + /** Note: driveno might change its value in this call */ + ret = libburner_aquire_drive(drive_adr, &driveno); + if (ret<=0) { + fprintf(stderr,"\nFATAL: Failed to aquire drive.\n"); + { ret = 34; goto finish_libburn; } + } + if (ret == 2) + { ret = 0; goto release_drive; } + if (do_blank) { + if (do_blank > 100) + ret = libburner_format_row(drive_list[driveno].drive); + else + ret = libburner_blank_disc(drive_list[driveno].drive, + do_blank == 1); + if (ret<=0) + { ret = 36; goto release_drive; } + } + if (source_adr_count > 0) { + ret = libburner_payload(drive_list[driveno].drive, + source_adr, source_adr_count, + do_multi, simulate_burn, all_tracks_type); + if (ret<=0) + { ret = 38; goto release_drive; } + } + ret = 0; +release_drive:; + if (drive_is_grabbed) + burn_drive_release(drive_list[driveno].drive, 0); + +finish_libburn:; + /* This app does not bother to know about exact scan state. + Better to accept a memory leak here. We are done anyway. */ + /* burn_drive_info_free(drive_list); */ + + burn_finish(); + exit(ret); +} + + +/* License and copyright aspects: + +This all is provided under GPL. +Read. Try. Think. Play. Write yourself some code. Be free of my copyright. + +Be also invited to study the code of cdrskin/cdrskin.c et al. + + +Clarification in my name and in the name of Mario Danic, copyright holder +on toplevel of libburnia. To be fully in effect after the remaining other +copyrighted code has been replaced by ours and by copyright-free contributions +of our friends: + +We, the copyright holders, agree on the interpretation that +dynamical linking of our libraries constitutes "use of" and +not "derivation from" our work in the sense of GPL, provided +those libraries are compiled from our unaltered code. + +Thus you may link our libraries dynamically with applications +which are not under GPL. You may distribute our libraries and +application tools in binary form, if you fulfill the usual +condition of GPL to offer a copy of the source code -altered +or unaltered- under GPL. + +We ask you politely to use our work in open source spirit +and with the due reference to the entire open source community. + +If there should really arise the case where above clarification +does not suffice to fulfill a clear and neat request in open source +spirit that would otherwise be declined for mere formal reasons, +only in that case we will duely consider to issue a special license +covering only that special case. +It is the open source idea of responsible freedom which will be +decisive and you will have to prove that you exhausted all own +means to qualify for GPL. + +For now we are firmly committed to maintain one single license: GPL. + +History: +libburner is a compilation of my own contributions to test/burniso.c and +fresh code which replaced the remaining parts under copyright of +Derek Foreman. +My respect and my thanks to Derek for providing me a start back in 2005. + +*/ + diff --git a/libburn/tags/ZeroThreeZero/test/poll.c b/libburn/tags/ZeroThreeZero/test/poll.c new file mode 100644 index 00000000..cd22f158 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/test/poll.c @@ -0,0 +1,78 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "libburn/libburn.h" +#include "libburn/toc.h" +#include "libburn/mmc.h" + +#include <unistd.h> +#include <stdio.h> +#include <signal.h> +#include <assert.h> + +static struct burn_drive_info *drives; +static unsigned int n_drives; +int NEXT; + +static void catch_int () +{ + NEXT = 1; +} + +static void poll_drive(int d) +{ + enum burn_disc_status s; + + fprintf(stderr, "polling disc in %s - %s:\n", + drives[d].vendor, drives[d].product); + + if (!burn_drive_grab(drives[d].drive, 1)) { + fprintf(stderr, "Unable to open the drive!\n"); + return; + } + + while (burn_drive_get_status(drives[d].drive, NULL)) + usleep(1000); + + while ((s = burn_disc_get_status(drives[d].drive)) + == BURN_DISC_UNREADY) + usleep(1000); + + while (NEXT == 0) { + sleep(2); + mmc_get_event(drives[d].drive); + } + burn_drive_release(drives[d].drive, 0); +} + +int main() +{ + int i; + struct sigaction newact; + struct sigaction oldact; + fprintf(stderr, "Initializing library..."); + if (burn_initialize()) + fprintf(stderr, "Success\n"); + else { + printf("Failed\n"); + return 1; + } + + fprintf(stderr, "Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + fprintf(stderr, "Done\n"); + if (!drives) { + printf("No burner found\n"); + return 1; + } + + newact.sa_handler = catch_int; + sigaction(SIGINT, &newact, &oldact); + for (i = 0; i < n_drives; i++) { + NEXT=0; + poll_drive(i); + } + sigaction(SIGINT, &oldact, NULL); + burn_drive_info_free(drives); + burn_finish(); + return 0; +} diff --git a/libburn/tags/ZeroThreeZero/test/structest.c b/libburn/tags/ZeroThreeZero/test/structest.c new file mode 100644 index 00000000..defa274e --- /dev/null +++ b/libburn/tags/ZeroThreeZero/test/structest.c @@ -0,0 +1,48 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <libburn/libburn.h> + +int main(int argc, char **argv) +{ + int i; + const char *path; + struct burn_track *track; + struct burn_disc *disc; + struct burn_session *session; + struct burn_source *src; + + disc = burn_disc_create(); + session = burn_session_create(); + burn_disc_add_session(disc, session, BURN_POS_END); + + /* Define a source for all of the tracks */ + path = strdup("/etc/hosts"); + src = burn_file_source_new(path, NULL); + + /* Add ten tracks to a session */ + for (i = 0; i < 10; i++) { + track = burn_track_create(); + burn_session_add_track(session, track, 0); + if (burn_track_set_source(track, src) != BURN_SOURCE_OK) { + printf("problem with the source\n"); + return 0; + } + } + + /* Add ten tracks to a session */ + for (i = 0; i < 10; i++) { + track = burn_track_create(); + burn_session_add_track(session, track, 0); + if (burn_track_set_source(track, src) != BURN_SOURCE_OK) { + printf("problem with the source\n"); + return 0; + } + } + + /* Delete a session */ + burn_session_remove_track(session, track); + + burn_structure_print_disc(disc); + return EXIT_SUCCESS; +} diff --git a/libburn/tags/ZeroThreeZero/test/telltoc.c b/libburn/tags/ZeroThreeZero/test/telltoc.c new file mode 100644 index 00000000..483e6228 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/test/telltoc.c @@ -0,0 +1,684 @@ + +/* test/telltoc.c , API illustration of obtaining media status info */ +/* Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL */ + +/** Overview + + telltoc is a minimal demo application for the library libburn as provided + on http://libburn.pykix.org . It can list the available devices, can display + some drive properties, the type of media, eventual table of content and + multisession info for mkisofs option -C . + It's main purpose, nevertheless, is to show you how to use libburn and also + to serve the libburn team as reference application. telltoc.c does indeed + define the standard way how above gestures can be implemented and stay upward + compatible for a good while. + + Before you can do anything, you have to initialize libburn by + burn_initialize() + as it is done in main() at the end of this file. Then you aquire a + drive in an appropriate way conforming to the API. The two main + approaches are shown here in application functions: + telltoc_aquire_by_adr() demonstrates usage as of cdrecord traditions + telltoc_aquire_by_driveno() demonstrates a scan-and-choose approach + With that aquired drive you can call + telltoc_media() prints some information about the media in a drive + telltoc_toc() prints a table of content (if there is content) + telltoc_msinfo() prints parameters for mkisofs option -C + When everything is done, main() releases the drive and shuts down libburn: + burn_drive_release(); + burn_finish() + +*/ + +/** See this for the decisive API specs . libburn.h is The Original */ +/* For using the installed header file : #include <libburn/libburn.h> */ +/* This program insists in the own headerfile. */ +#include "../libburn/libburn.h" + +/* libburn is intended for Linux systems with kernel 2.4 or 2.6 for now */ +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include <errno.h> +#include <sys/stat.h> +#include <fcntl.h> + + +/** For simplicity i use global variables to represent the drives. + Drives are systemwide global, so we do not give away much of good style. +*/ + +/** This list will hold the drives known to libburn. This might be all CD + drives of the system and thus might impose severe impact on the system. +*/ +static struct burn_drive_info *drive_list; + +/** If you start a long lasting operation with drive_count > 1 then you are + not friendly to the users of other drives on those systems. Beware. */ +static unsigned int drive_count; + +/** This variable indicates wether the drive is grabbed and must be + finally released */ +static int drive_is_grabbed = 0; + + +/* Some in-advance definitions to allow a more comprehensive ordering + of the functions and their explanations in here */ +int telltoc_aquire_by_adr(char *drive_adr); +int telltoc_aquire_by_driveno(int *drive_no, int silent); + + +/* ------------------------------- API gestures ---------------------------- */ + +/** You need to aquire a drive before burning. The API offers this as one + compact call and alternatively as application controllable gestures of + whitelisting, scanning for drives and finally grabbing one of them. + + If you have a persistent address of the drive, then the compact call is + to prefer because it only touches one drive. On modern Linux kernels, + there should be no fatal disturbance of ongoing burns of other libburn + instances with any of our approaches. We use open(O_EXCL) by default. + On /dev/hdX it should cooperate with growisofs and some cdrecord variants. + On /dev/sgN versus /dev/scdM expect it not to respect other programs. +*/ +int telltoc_aquire_drive(char *drive_adr, int *driveno, int silent_drive) +{ + int ret; + + if(drive_adr != NULL && drive_adr[0] != 0) + ret = telltoc_aquire_by_adr(drive_adr); + else + ret = telltoc_aquire_by_driveno(driveno, silent_drive); + return ret; +} + + +/** If the persistent drive address is known, then this approach is much + more un-obtrusive to the systemwide livestock of drives. Only the + given drive device will be opened during this procedure. +*/ +int telltoc_aquire_by_adr(char *drive_adr) +{ + int ret; + + char libburn_drive_adr[BURN_DRIVE_ADR_LEN]; + + /* This tries to resolve links or alternative device files */ + ret = burn_drive_convert_fs_adr(drive_adr, libburn_drive_adr); + if (ret<=0) { + fprintf(stderr,"Address does not lead to a CD burner: '%s'\n", + drive_adr); + return ret; + } + fprintf(stderr,"Aquiring drive '%s' ...\n",libburn_drive_adr); + ret = burn_drive_scan_and_grab(&drive_list,libburn_drive_adr,1); + if (ret <= 0) { + fprintf(stderr,"FAILURE with persistent drive address '%s'\n", + libburn_drive_adr); + } else { + fprintf(stderr,"Done\n"); + drive_is_grabbed = 1; + } + return ret; +} + + +/** This method demonstrates how to use libburn without knowing a persistent + drive address in advance. It has to make sure that after assessing the list + of available drives, all unwanted drives get closed again. As long as they + are open, no other libburn instance can see them. This is an intended + locking feature. The application is responsible for giving up the locks + by either burn_drive_release() (only after burn_drive_grab() !), + burn_drive_info_forget(), burn_drive_info_free(), or burn_finish(). + @param driveno the index number in libburn's drive list. This will get + set to 0 on success and will then be the drive index to + use in the further dourse of processing. + @param silent_drive 1=do not print "Drive found :" line with *driveno >= 0 + @return 1 success , <= 0 failure +*/ +int telltoc_aquire_by_driveno(int *driveno, int silent_drive) +{ + char adr[BURN_DRIVE_ADR_LEN]; + int ret, i; + + fprintf(stderr, "Beginning to scan for devices ...\n"); + while (!burn_drive_scan(&drive_list, &drive_count)) + usleep(1002); + if (drive_count <= 0 && *driveno >= 0) { + fprintf(stderr, "FAILED (no drives found)\n"); + return 0; + } + fprintf(stderr, "Done\n"); + + for (i = 0; i < drive_count; i++) { + if (*driveno >= 0 && (silent_drive || *driveno != i)) + continue; + if (burn_drive_get_adr(&(drive_list[i]), adr) <=0) + strcpy(adr, "-get_adr_failed-"); + printf("Drive found : %d --drive '%s' : ", i,adr); + printf("%-8s %-16s (%4s)\n", + drive_list[i].vendor,drive_list[i].product, + drive_list[i].revision); + } + if (*driveno < 0) { + fprintf(stderr, + "Pseudo-drive \"-\" given : bus scanning done.\n"); + return 2; /* the program will end after this */ + } + + /* We already made our choice via command line. (default is 0) + So we just have to keep our desired drive and drop all others. + */ + if (drive_count <= *driveno) { + fprintf(stderr, + "Found only %d drives. Number %d not available.\n", + drive_count, *driveno); + return 0; /* the program will end after this */ + } + + /* Drop all drives which we do not want to use */ + for (i = 0; i < drive_count; i++) { + if (i == *driveno) /* the one drive we want to keep */ + continue; + ret = burn_drive_info_forget(&(drive_list[i]),0); + if (ret != 1) + fprintf(stderr, "Cannot drop drive %d. Please report \"ret=%d\" to libburn-hackers@pykix.org\n", + i, ret); + else + fprintf(stderr, "Dropped unwanted drive %d\n",i); + } + /* Make the one we want ready for inquiry */ + ret= burn_drive_grab(drive_list[*driveno].drive, 1); + if (ret != 1) + return 0; + drive_is_grabbed = 1; + return 1; +} + + + +/** This gesture is necessary to get my NEC DVD_RW ND-4570A out of a state + of noisy overexcitement after it was inquired for Next Writeable Address. + The noise then still lasts 20 seconds. Same with cdrecord -toc, btw. + It opens a small gap for losing the drive to another libburn instance. + Not a problem in telltoc. This is done as very last drive operation. + Eventually the other libburn instance will have the same sanitizing effect. +*/ +int telltoc_regrab(struct burn_drive *drive) { + int ret; + + if (drive_is_grabbed) + burn_drive_release(drive, 0); + drive_is_grabbed = 0; + ret = burn_drive_grab(drive, 0); + if (ret != 0) { + drive_is_grabbed = 1; + } + return !!ret; +} + + +int telltoc_media(struct burn_drive *drive) +{ + int ret, media_found = 0, profile_no = -1; + double max_speed = 0.0, min_speed = 0.0, speed_conv; + enum burn_disc_status s; + char profile_name[80], speed_unit[40]; + + printf("Media current: "); + ret = burn_disc_get_profile(drive, &profile_no, profile_name); + if (profile_no > 0 && ret >0) { + if (profile_name[0]) + printf("%s\n", profile_name); + else + printf("%4.4Xh\n", profile_no); + } else + printf("is not recognizable\n"); + + speed_conv = 176.4; + strcpy(speed_unit,"176.4 kB/s (CD, data speed 150 KiB/s)"); + if (strstr(profile_name, "DVD") == profile_name) { + speed_conv = 1385.0; + strcpy(speed_unit,"1385.0 kB/s (DVD)"); + } + + /* >>> libburn does not obtain full profile list yet */ + + printf("Media status : "); + s = burn_disc_get_status(drive); + if (s==BURN_DISC_FULL) { + printf("is written , is closed\n"); + media_found = 1; + } else if (s==BURN_DISC_APPENDABLE) { + printf("is written , is appendable\n"); + media_found = 1; + } else if (s==BURN_DISC_BLANK) { + printf("is blank\n"); + media_found = 1; + } else if (s==BURN_DISC_EMPTY) + printf("is not present\n"); + else + printf("is not recognizable\n"); + + printf("Media reuse : "); + if (media_found) { + if (burn_disc_erasable(drive)) + printf("is erasable\n"); + else + printf("is not erasable\n"); + } else + printf("is not recognizable\n"); + + ret= burn_drive_get_write_speed(drive); + max_speed = ((double ) ret) / speed_conv; + ret= burn_drive_get_min_write_speed(drive); + min_speed = ((double ) ret) / speed_conv; + if (!media_found) + printf("Drive speed : max=%.1f , min=%.1f\n", + max_speed, min_speed); + else + printf("Avail. speed : max=%.1f , min=%.1f\n", + max_speed, min_speed); + + ret = 0; + if (media_found) + ret= burn_disc_read_atip(drive); + if(ret>0) { + ret= burn_drive_get_min_write_speed(drive); + min_speed = ((double ) ret) / speed_conv; + ret= burn_drive_get_write_speed(drive); + max_speed = ((double ) ret) / speed_conv; + printf("Media speed : max=%.1f , min=%.1f\n", + max_speed, min_speed); + } + printf("Speed unit 1x: %s\n", speed_unit); + + return 1; +} + + +int telltoc_speedlist(struct burn_drive *drive) +{ + int ret, has_modern_entries = 0; + struct burn_speed_descriptor *speed_list, *sd; + + ret = burn_drive_get_speedlist(drive, &speed_list); + if (ret <= 0) { + fprintf(stderr, "SORRY: Cannot obtain speed list info\n"); + return 2; + } + for (sd = speed_list; sd != NULL; sd = sd->next) + if (sd->source == 2) + has_modern_entries = 1; + for (sd = speed_list; sd != NULL; sd = sd->next) { + if (has_modern_entries && sd->source < 2) + continue; + if (sd->write_speed <= 0) + continue; + printf("Speed descr. : %d kB/s", sd->write_speed); + if (sd->end_lba >= 0) + printf(", %.1f MiB", ((double) sd->end_lba) / 512.0); + if (sd->profile_name[0]) + printf(", %s", sd->profile_name); + printf("\n"); + } + burn_drive_free_speedlist(&speed_list); + return 1; +} + + +int telltoc_formatlist(struct burn_drive *drive) +{ + int ret, i, status, num_formats, profile_no, type; + off_t size; + unsigned dummy; + char status_text[80], profile_name[90]; + + ret = burn_disc_get_formats(drive, &status, &size, &dummy, + &num_formats); + if (ret <= 0) { + fprintf(stderr, "SORRY: Cannot obtain format list info\n"); + return 2; + } + if (status == BURN_FORMAT_IS_UNFORMATTED) + sprintf(status_text, "unformatted, up to %.1f MiB", + ((double) size) / 1024.0 / 1024.0); + else if(status == BURN_FORMAT_IS_FORMATTED) + sprintf(status_text, "formatted, with %.1f MiB", + ((double) size) / 1024.0 / 1024.0); + else if(status == BURN_FORMAT_IS_UNKNOWN) { + burn_disc_get_profile(drive, &profile_no, profile_name); + if (profile_no > 0) + sprintf(status_text, "intermediate or unknown"); + else + sprintf(status_text, "no media or unknown media"); + } else + sprintf(status_text, "illegal status according to MMC-5"); + printf("Format status: %s\n", status_text); + + for (i = 0; i < num_formats; i++) { + ret = burn_disc_get_format_descr(drive, i, + &type, &size, &dummy); + if (ret <= 0) + continue; + printf("Format descr.: %2.2Xh , %.1f MiB (%.fs)\n", + type, ((double) size) / 1024.0 / 1024.0, + ((double) size) / 2048.0); + } + return 1; +} + + +int telltoc_toc(struct burn_drive *drive) +{ + int num_sessions = 0 , num_tracks = 0 , lba = 0; + int track_count = 0; + int session_no, track_no; + struct burn_disc *disc= NULL; + struct burn_session **sessions; + struct burn_track **tracks; + struct burn_toc_entry toc_entry; + + disc = burn_drive_get_disc(drive); + if (disc==NULL) { + fprintf(stderr, "SORRY: Cannot obtain Table Of Content\n"); + return 2; + } + sessions = burn_disc_get_sessions(disc, &num_sessions); + for (session_no = 0; session_no<num_sessions; session_no++) { + tracks = burn_session_get_tracks(sessions[session_no], + &num_tracks); + if (tracks==NULL) + continue; + for(track_no= 0; track_no<num_tracks; track_no++) { + track_count++; + burn_track_get_entry(tracks[track_no], &toc_entry); + lba= burn_msf_to_lba(toc_entry.pmin, toc_entry.psec, + toc_entry.pframe); + printf("Media content: session %2d ", session_no+1); + printf("track %2d %s lba: %9d %2.2u:%2.2u:%2.2u\n", + track_count, + ((toc_entry.control&7)<4?"audio":"data "), + lba, + toc_entry.pmin, + toc_entry.psec, + toc_entry.pframe); + } + burn_session_get_leadout_entry(sessions[session_no], + &toc_entry); + lba = burn_msf_to_lba(toc_entry.pmin, + toc_entry.psec, toc_entry.pframe); + printf("Media content: session %2d ", session_no+1); + printf("leadout lba: %9d %2.2u:%2.2u:%2.2u\n", + lba, + toc_entry.pmin, + toc_entry.psec, + toc_entry.pframe); + } + if (disc!=NULL) + burn_disc_free(disc); + return 1; +} + + +int telltoc_msinfo(struct burn_drive *drive, + int msinfo_explicit, int msinfo_alone) +{ + int num_sessions, session_no, ret, num_tracks; + int nwa = -123456789, lba = -123456789, aux_lba, lout_lba; + enum burn_disc_status s; + struct burn_disc *disc= NULL; + struct burn_session **sessions; + struct burn_track **tracks; + struct burn_toc_entry toc_entry; + struct burn_write_opts *o= NULL; + + s = burn_disc_get_status(drive); + if (s!=BURN_DISC_APPENDABLE) { + if (!msinfo_explicit) + return 2; + fprintf(stderr, + "SORRY: --msinfo can only operate on appendable media.\n"); + return 0; + } + + /* man mkisofs , option -C : + The first number is the sector number of the first sector in + the last session of the disk that should be appended to. + */ + disc = burn_drive_get_disc(drive); + if (disc==NULL) { + fprintf(stderr,"SORRY: Cannot obtain info about CD content\n"); + return 2; + } + sessions = burn_disc_get_sessions(disc, &num_sessions); + for (session_no = 0; session_no<num_sessions; session_no++) { + tracks = burn_session_get_tracks(sessions[session_no], + &num_tracks); + if (tracks==NULL || num_tracks<=0) + continue; + burn_track_get_entry(tracks[0], &toc_entry); + lba= burn_msf_to_lba(toc_entry.pmin, toc_entry.psec, + toc_entry.pframe); + } + if(lba==-123456789) { + fprintf(stderr,"SORRY: Cannot find any track on media\n"); + { ret = 0; goto ex; } + } + /* Prepare a qualified guess as fallback for nwa inquiry */ + burn_session_get_leadout_entry(sessions[num_sessions-1], &toc_entry); + lout_lba= burn_msf_to_lba(toc_entry.pmin,toc_entry.psec, + toc_entry.pframe); + + /* man mkisofs , option -C : + The second number is the starting sector number of the new session. + */ + /* Set some write opts to be sent to drive. LG GSA-4082B needs it. */ + o= burn_write_opts_new(drive); + if(o!=NULL) { + burn_write_opts_set_perform_opc(o, 0); + burn_write_opts_set_write_type(o, + BURN_WRITE_TAO, BURN_BLOCK_MODE1); + } + /* Now try to inquire nwa from drive */ + ret= burn_disc_track_lba_nwa(drive,o,0,&aux_lba,&nwa); + telltoc_regrab(drive); /* necessary to calm down my NEC drive */ + if(ret<=0) { + fprintf(stderr, + "NOTE: Guessing next writeable address from leadout\n"); + if(num_sessions>0) + nwa= lout_lba+6900; + else + nwa= lout_lba+11400; + } + + if (!msinfo_alone) + printf("Media msinfo : mkisofs ... -C "); + printf("%d,%d\n",lba,nwa); + + ret = 1; +ex:; + if (disc!=NULL) + burn_disc_free(disc); + if (o!=NULL) + burn_write_opts_free(o); + return ret; +} + + +/** The setup parameters of telltoc */ +static char drive_adr[BURN_DRIVE_ADR_LEN] = {""}; +static int driveno = 0; +static int do_media = 0; +static int do_toc = 0; +static int do_msinfo = 0; +static int print_help = 0; +static int do_capacities = 0; + + +/** Converts command line arguments into above setup parameters. + drive_adr[] must provide at least BURN_DRIVE_ADR_LEN bytes. + source_adr[] must provide at least 4096 bytes. +*/ +int telltoc_setup(int argc, char **argv) +{ + int i; + + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--drive")) { + ++i; + if (i >= argc) { + fprintf(stderr,"--drive requires an argument\n"); + return 1; + } else if (strcmp(argv[i], "-") == 0) { + drive_adr[0] = 0; + driveno = -1; + } else if (isdigit(argv[i][0])) { + drive_adr[0] = 0; + driveno = atoi(argv[i]); + } else { + if(strlen(argv[i]) >= BURN_DRIVE_ADR_LEN) { + fprintf(stderr,"--drive address too long (max. %d)\n", + BURN_DRIVE_ADR_LEN-1); + return 2; + } + strcpy(drive_adr, argv[i]); + } + } else if (strcmp(argv[i],"--media")==0) { + do_media = 1; + + } else if (!strcmp(argv[i], "--msinfo")) { + do_msinfo = 1; + + } else if (!strcmp(argv[i], "--capacities")) { + do_capacities = 1; + + } else if (!strcmp(argv[i], "--toc")) { + do_toc = 1; + + } else if (!strcmp(argv[i], "--help")) { + print_help = 1; + + } else { + fprintf(stderr, "Unidentified option: %s\n", argv[i]); + return 7; + } + } + if (argc==1) + print_help = 1; + if (print_help) { + printf("Usage: %s\n", argv[0]); + printf(" [--drive <address>|<driveno>|\"-\"]\n"); + printf(" [--media] [--capacities] [--toc] [--msinfo]\n"); + printf("Examples\n"); + printf("A bus scan (needs rw-permissions to see a drive):\n"); + printf(" %s --drive -\n",argv[0]); + printf("Obtain info about the type of loaded media:\n"); + printf(" %s --drive /dev/hdc --media\n",argv[0]); + printf("Obtain table of content:\n"); + printf(" %s --drive /dev/hdc --toc\n",argv[0]); + printf("Obtain parameters for option -C of program mkisofs:\n"); + printf(" msinfo=$(%s --drive /dev/hdc --msinfo 2>/dev/null)\n", + argv[0]); + printf(" mkisofs ... -C \"$msinfo\" ...\n"); + printf("Obtain what is available about drive 0 and its media\n"); + printf(" %s --drive 0\n",argv[0]); + } + return 0; +} + + +int main(int argc, char **argv) +{ + int ret, toc_failed = 0, msinfo_alone = 0, msinfo_explicit = 0; + int full_default = 0; + + ret = telltoc_setup(argc, argv); + if (ret) + exit(ret); + + /* Behavior shall be different if --msinfo is only option */ + if (do_msinfo) { + msinfo_explicit = 1; + if (!(do_media || do_toc)) + msinfo_alone = 1; + } + /* Default option is to do everything if possible */ + if (do_media==0 && do_msinfo==0 && do_capacities==0 && do_toc==0 + && driveno!=-1) { + if(print_help) + exit(0); + full_default = do_media = do_msinfo = do_capacities= do_toc = 1; + } + + fprintf(stderr, "Initializing libburn.pykix.org ...\n"); + if (burn_initialize()) + fprintf(stderr, "Done\n"); + else { + fprintf(stderr,"\nFATAL: Failed to initialize.\n"); + exit(33); + } + + /* Print messages of severity SORRY or more directly to stderr */ + burn_msgs_set_severities("NEVER", "SORRY", "telltoc : "); + + /** Note: driveno might change its value in this call */ + ret = telltoc_aquire_drive(drive_adr, &driveno, !full_default); + if (ret<=0) { + fprintf(stderr,"\nFATAL: Failed to aquire drive.\n"); + { ret = 34; goto finish_libburn; } + } + if (ret == 2) + { ret = 0; goto release_drive; } + + if (do_media) { + ret = telltoc_media(drive_list[driveno].drive); + if (ret<=0) + {ret = 36; goto release_drive; } + } + if (do_capacities) { + ret = telltoc_speedlist(drive_list[driveno].drive); + if (ret<=0) + {ret = 39; goto release_drive; } + ret = telltoc_formatlist(drive_list[driveno].drive); + if (ret<=0) + {ret = 39; goto release_drive; } + } + if (do_toc) { + ret = telltoc_toc(drive_list[driveno].drive); + if (ret<=0) + {ret = 37; goto release_drive; } + if (ret==2) + toc_failed = 1; + } + if (do_msinfo) { + ret = telltoc_msinfo(drive_list[driveno].drive, + msinfo_explicit, msinfo_alone); + if (ret<=0) + {ret = 38; goto release_drive; } + } + + ret = 0; + if (toc_failed) + ret = 37; +release_drive:; + if (drive_is_grabbed) + burn_drive_release(drive_list[driveno].drive, 0); + +finish_libburn:; + /* This app does not bother to know about exact scan state. + Better to accept a memory leak here. We are done anyway. */ + /* burn_drive_info_free(drive_list); */ + + burn_finish(); + exit(ret); +} + +/* License and copyright aspects: + See libburner.c +*/ + diff --git a/libburn/tags/ZeroThreeZero/version.h.in b/libburn/tags/ZeroThreeZero/version.h.in new file mode 100644 index 00000000..13ada991 --- /dev/null +++ b/libburn/tags/ZeroThreeZero/version.h.in @@ -0,0 +1,3 @@ +#define BURN_MAJOR_VERSION @BURN_MAJOR_VERSION@ +#define BURN_MINOR_VERSION @BURN_MINOR_VERSION@ +#define BURN_MICRO_VERSION @BURN_MICRO_VERSION@