From f311e91a86b9009271ee27d9bb78e33fe8bfd4d1 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 25 Jan 2008 12:25:17 +0000 Subject: [PATCH] Created 0.2.4 branch for release preparations --- libburn/branches/ZeroFourTwo/AUTHORS | 3 + libburn/branches/ZeroFourTwo/CONTRIBUTORS | 6 + libburn/branches/ZeroFourTwo/COPYING | 280 + libburn/branches/ZeroFourTwo/COPYRIGHT | 19 + libburn/branches/ZeroFourTwo/ChangeLog | 1 + libburn/branches/ZeroFourTwo/INSTALL | 234 + libburn/branches/ZeroFourTwo/Makefile.am | 202 + libburn/branches/ZeroFourTwo/NEWS | 1 + libburn/branches/ZeroFourTwo/README | 262 + libburn/branches/ZeroFourTwo/acinclude.m4 | 22 + libburn/branches/ZeroFourTwo/bootstrap | 10 + libburn/branches/ZeroFourTwo/cdrskin/README | 458 + .../cdrskin/add_ts_changes_to_libburn_0_4_0 | 243 + .../cdrskin/add_ts_changes_to_libburn_0_4_1 | 245 + .../ZeroFourTwo/cdrskin/cdrecord_spy.sh | 37 + .../branches/ZeroFourTwo/cdrskin/cdrfifo.c | 1253 +++ .../branches/ZeroFourTwo/cdrskin/cdrfifo.h | 171 + .../branches/ZeroFourTwo/cdrskin/cdrskin.1 | 1236 +++ .../branches/ZeroFourTwo/cdrskin/cdrskin.c | 7954 +++++++++++++++++ .../ZeroFourTwo/cdrskin/cdrskin_eng.html | 461 + .../ZeroFourTwo/cdrskin/cdrskin_timestamp.h | 1 + .../ZeroFourTwo/cdrskin/changelog.txt | 4873 ++++++++++ .../branches/ZeroFourTwo/cdrskin/cleanup.c | 215 + .../branches/ZeroFourTwo/cdrskin/cleanup.h | 34 + .../ZeroFourTwo/cdrskin/compile_cdrskin.sh | 216 + .../cdrskin/convert_man_to_html.sh | 78 + .../ZeroFourTwo/cdrskin/doener_150x200_tr.png | Bin 0 -> 10595 bytes .../cdrskin/doener_150x200_tr_octx.png | Bin 0 -> 9433 bytes .../ZeroFourTwo/cdrskin/make_timestamp.sh | 9 + .../ZeroFourTwo/cdrskin/wiki_plain.txt | 293 + libburn/branches/ZeroFourTwo/configure.ac | 174 + libburn/branches/ZeroFourTwo/doc/Makefile | 4 + libburn/branches/ZeroFourTwo/doc/comments | 161 + libburn/branches/ZeroFourTwo/doc/cookbook.txt | 1108 +++ libburn/branches/ZeroFourTwo/doc/ddlp.txt | 388 + .../branches/ZeroFourTwo/doc/doxygen.conf.in | 1300 +++ libburn/branches/ZeroFourTwo/libburn-5.pc.in | 12 + libburn/branches/ZeroFourTwo/libburn/Makefile | 4 + .../branches/ZeroFourTwo/libburn/Makefile.am | 65 + .../branches/ZeroFourTwo/libburn/asserts.txt | 792 ++ libburn/branches/ZeroFourTwo/libburn/async.c | 537 ++ libburn/branches/ZeroFourTwo/libburn/async.h | 14 + .../branches/ZeroFourTwo/libburn/back_hacks.h | 54 + .../branches/ZeroFourTwo/libburn/cleanup.c | 215 + .../branches/ZeroFourTwo/libburn/cleanup.h | 34 + libburn/branches/ZeroFourTwo/libburn/crc.c | 122 + libburn/branches/ZeroFourTwo/libburn/crc.h | 9 + libburn/branches/ZeroFourTwo/libburn/ddlpa.c | 614 ++ libburn/branches/ZeroFourTwo/libburn/ddlpa.h | 107 + libburn/branches/ZeroFourTwo/libburn/debug.c | 35 + libburn/branches/ZeroFourTwo/libburn/debug.h | 8 + libburn/branches/ZeroFourTwo/libburn/drive.c | 2446 +++++ libburn/branches/ZeroFourTwo/libburn/drive.h | 128 + libburn/branches/ZeroFourTwo/libburn/error.h | 8 + libburn/branches/ZeroFourTwo/libburn/file.c | 542 ++ libburn/branches/ZeroFourTwo/libburn/file.h | 62 + libburn/branches/ZeroFourTwo/libburn/init.c | 408 + libburn/branches/ZeroFourTwo/libburn/init.h | 21 + libburn/branches/ZeroFourTwo/libburn/lec.c | 451 + libburn/branches/ZeroFourTwo/libburn/lec.h | 12 + .../branches/ZeroFourTwo/libburn/libburn.h | 2254 +++++ .../ZeroFourTwo/libburn/libdax_audioxtr.c | 326 + .../ZeroFourTwo/libburn/libdax_audioxtr.h | 229 + .../ZeroFourTwo/libburn/libdax_msgs.c | 431 + .../ZeroFourTwo/libburn/libdax_msgs.h | 582 ++ .../libburn/libdax_msgs_to_xyz_msgs.sh | 37 + libburn/branches/ZeroFourTwo/libburn/mmc.c | 3137 +++++++ libburn/branches/ZeroFourTwo/libburn/mmc.h | 80 + libburn/branches/ZeroFourTwo/libburn/null.c | 31 + libburn/branches/ZeroFourTwo/libburn/null.h | 10 + .../branches/ZeroFourTwo/libburn/options.c | 436 + .../branches/ZeroFourTwo/libburn/options.h | 95 + .../branches/ZeroFourTwo/libburn/os-freebsd.h | 61 + .../branches/ZeroFourTwo/libburn/os-linux.h | 73 + libburn/branches/ZeroFourTwo/libburn/os.h | 34 + libburn/branches/ZeroFourTwo/libburn/read.c | 465 + libburn/branches/ZeroFourTwo/libburn/read.h | 14 + libburn/branches/ZeroFourTwo/libburn/sbc.c | 114 + libburn/branches/ZeroFourTwo/libburn/sbc.h | 18 + libburn/branches/ZeroFourTwo/libburn/sector.c | 846 ++ libburn/branches/ZeroFourTwo/libburn/sector.h | 35 + .../ZeroFourTwo/libburn/sg-freebsd-port.c | 631 ++ .../branches/ZeroFourTwo/libburn/sg-freebsd.c | 673 ++ .../branches/ZeroFourTwo/libburn/sg-linux.c | 1422 +++ libburn/branches/ZeroFourTwo/libburn/sg.c | 17 + libburn/branches/ZeroFourTwo/libburn/sg.h | 36 + libburn/branches/ZeroFourTwo/libburn/source.c | 55 + libburn/branches/ZeroFourTwo/libburn/source.h | 10 + libburn/branches/ZeroFourTwo/libburn/spc.c | 997 +++ libburn/branches/ZeroFourTwo/libburn/spc.h | 62 + .../branches/ZeroFourTwo/libburn/structure.c | 517 ++ .../branches/ZeroFourTwo/libburn/structure.h | 112 + libburn/branches/ZeroFourTwo/libburn/toc.c | 141 + libburn/branches/ZeroFourTwo/libburn/toc.h | 48 + .../branches/ZeroFourTwo/libburn/transport.h | 351 + libburn/branches/ZeroFourTwo/libburn/util.c | 53 + libburn/branches/ZeroFourTwo/libburn/util.h | 8 + libburn/branches/ZeroFourTwo/libburn/write.c | 2282 +++++ libburn/branches/ZeroFourTwo/libburn/write.h | 48 + libburn/branches/ZeroFourTwo/libcevap/cgen.c | 1503 ++++ libburn/branches/ZeroFourTwo/libcevap/cgen.h | 35 + .../branches/ZeroFourTwo/libcevap/cgen.txt | 222 + libburn/branches/ZeroFourTwo/libcevap/ctyp.c | 364 + libburn/branches/ZeroFourTwo/libcevap/ctyp.h | 41 + .../libcevap/extract_cgen_input.sh | 30 + .../ZeroFourTwo/libcevap/libcevap_gen.sh | 16 + .../ZeroFourTwo/libcevap/libdax_equip.gif | Bin 0 -> 10751 bytes .../ZeroFourTwo/libcevap/libdax_job.gif | Bin 0 -> 10532 bytes .../ZeroFourTwo/libcevap/libdax_model.txt | 944 ++ .../ZeroFourTwo/libcevap/libdax_overview.gif | Bin 0 -> 12521 bytes libburn/branches/ZeroFourTwo/libcevap/main.c | 39 + libburn/branches/ZeroFourTwo/libcevap/smem.c | 445 + libburn/branches/ZeroFourTwo/libcevap/smem.h | 165 + libburn/branches/ZeroFourTwo/test/Makefile | 4 + libburn/branches/ZeroFourTwo/test/dewav.c | 215 + libburn/branches/ZeroFourTwo/test/fake_au.c | 164 + libburn/branches/ZeroFourTwo/test/libburner.c | 773 ++ .../branches/ZeroFourTwo/test/open-cd-excl.c | 133 + libburn/branches/ZeroFourTwo/test/poll.c | 78 + libburn/branches/ZeroFourTwo/test/structest.c | 48 + libburn/branches/ZeroFourTwo/test/telltoc.c | 921 ++ libburn/branches/ZeroFourTwo/version.h.in | 3 + 122 files changed, 51566 insertions(+) create mode 100644 libburn/branches/ZeroFourTwo/AUTHORS create mode 100644 libburn/branches/ZeroFourTwo/CONTRIBUTORS create mode 100644 libburn/branches/ZeroFourTwo/COPYING create mode 100644 libburn/branches/ZeroFourTwo/COPYRIGHT create mode 100644 libburn/branches/ZeroFourTwo/ChangeLog create mode 100644 libburn/branches/ZeroFourTwo/INSTALL create mode 100644 libburn/branches/ZeroFourTwo/Makefile.am create mode 100644 libburn/branches/ZeroFourTwo/NEWS create mode 100644 libburn/branches/ZeroFourTwo/README create mode 100644 libburn/branches/ZeroFourTwo/acinclude.m4 create mode 100755 libburn/branches/ZeroFourTwo/bootstrap create mode 100644 libburn/branches/ZeroFourTwo/cdrskin/README create mode 100755 libburn/branches/ZeroFourTwo/cdrskin/add_ts_changes_to_libburn_0_4_0 create mode 100755 libburn/branches/ZeroFourTwo/cdrskin/add_ts_changes_to_libburn_0_4_1 create mode 100755 libburn/branches/ZeroFourTwo/cdrskin/cdrecord_spy.sh create mode 100644 libburn/branches/ZeroFourTwo/cdrskin/cdrfifo.c create mode 100644 libburn/branches/ZeroFourTwo/cdrskin/cdrfifo.h create mode 100644 libburn/branches/ZeroFourTwo/cdrskin/cdrskin.1 create mode 100644 libburn/branches/ZeroFourTwo/cdrskin/cdrskin.c create mode 100644 libburn/branches/ZeroFourTwo/cdrskin/cdrskin_eng.html create mode 100644 libburn/branches/ZeroFourTwo/cdrskin/cdrskin_timestamp.h create mode 100644 libburn/branches/ZeroFourTwo/cdrskin/changelog.txt create mode 100644 libburn/branches/ZeroFourTwo/cdrskin/cleanup.c create mode 100644 libburn/branches/ZeroFourTwo/cdrskin/cleanup.h create mode 100755 libburn/branches/ZeroFourTwo/cdrskin/compile_cdrskin.sh create mode 100755 libburn/branches/ZeroFourTwo/cdrskin/convert_man_to_html.sh create mode 100644 libburn/branches/ZeroFourTwo/cdrskin/doener_150x200_tr.png create mode 100644 libburn/branches/ZeroFourTwo/cdrskin/doener_150x200_tr_octx.png create mode 100755 libburn/branches/ZeroFourTwo/cdrskin/make_timestamp.sh create mode 100644 libburn/branches/ZeroFourTwo/cdrskin/wiki_plain.txt create mode 100644 libburn/branches/ZeroFourTwo/configure.ac create mode 100644 libburn/branches/ZeroFourTwo/doc/Makefile create mode 100644 libburn/branches/ZeroFourTwo/doc/comments create mode 100644 libburn/branches/ZeroFourTwo/doc/cookbook.txt create mode 100644 libburn/branches/ZeroFourTwo/doc/ddlp.txt create mode 100644 libburn/branches/ZeroFourTwo/doc/doxygen.conf.in create mode 100644 libburn/branches/ZeroFourTwo/libburn-5.pc.in create mode 100644 libburn/branches/ZeroFourTwo/libburn/Makefile create mode 100644 libburn/branches/ZeroFourTwo/libburn/Makefile.am create mode 100644 libburn/branches/ZeroFourTwo/libburn/asserts.txt create mode 100644 libburn/branches/ZeroFourTwo/libburn/async.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/async.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/back_hacks.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/cleanup.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/cleanup.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/crc.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/crc.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/ddlpa.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/ddlpa.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/debug.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/debug.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/drive.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/drive.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/error.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/file.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/file.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/init.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/init.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/lec.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/lec.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/libburn.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/libdax_audioxtr.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/libdax_audioxtr.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/libdax_msgs.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/libdax_msgs.h create mode 100755 libburn/branches/ZeroFourTwo/libburn/libdax_msgs_to_xyz_msgs.sh create mode 100644 libburn/branches/ZeroFourTwo/libburn/mmc.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/mmc.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/null.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/null.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/options.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/options.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/os-freebsd.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/os-linux.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/os.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/read.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/read.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/sbc.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/sbc.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/sector.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/sector.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/sg-freebsd-port.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/sg-freebsd.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/sg-linux.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/sg.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/sg.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/source.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/source.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/spc.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/spc.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/structure.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/structure.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/toc.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/toc.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/transport.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/util.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/util.h create mode 100644 libburn/branches/ZeroFourTwo/libburn/write.c create mode 100644 libburn/branches/ZeroFourTwo/libburn/write.h create mode 100644 libburn/branches/ZeroFourTwo/libcevap/cgen.c create mode 100644 libburn/branches/ZeroFourTwo/libcevap/cgen.h create mode 100644 libburn/branches/ZeroFourTwo/libcevap/cgen.txt create mode 100644 libburn/branches/ZeroFourTwo/libcevap/ctyp.c create mode 100644 libburn/branches/ZeroFourTwo/libcevap/ctyp.h create mode 100755 libburn/branches/ZeroFourTwo/libcevap/extract_cgen_input.sh create mode 100755 libburn/branches/ZeroFourTwo/libcevap/libcevap_gen.sh create mode 100644 libburn/branches/ZeroFourTwo/libcevap/libdax_equip.gif create mode 100644 libburn/branches/ZeroFourTwo/libcevap/libdax_job.gif create mode 100644 libburn/branches/ZeroFourTwo/libcevap/libdax_model.txt create mode 100644 libburn/branches/ZeroFourTwo/libcevap/libdax_overview.gif create mode 100644 libburn/branches/ZeroFourTwo/libcevap/main.c create mode 100644 libburn/branches/ZeroFourTwo/libcevap/smem.c create mode 100644 libburn/branches/ZeroFourTwo/libcevap/smem.h create mode 100644 libburn/branches/ZeroFourTwo/test/Makefile create mode 100644 libburn/branches/ZeroFourTwo/test/dewav.c create mode 100644 libburn/branches/ZeroFourTwo/test/fake_au.c create mode 100644 libburn/branches/ZeroFourTwo/test/libburner.c create mode 100644 libburn/branches/ZeroFourTwo/test/open-cd-excl.c create mode 100644 libburn/branches/ZeroFourTwo/test/poll.c create mode 100644 libburn/branches/ZeroFourTwo/test/structest.c create mode 100644 libburn/branches/ZeroFourTwo/test/telltoc.c create mode 100644 libburn/branches/ZeroFourTwo/version.h.in diff --git a/libburn/branches/ZeroFourTwo/AUTHORS b/libburn/branches/ZeroFourTwo/AUTHORS new file mode 100644 index 00000000..6a964a5c --- /dev/null +++ b/libburn/branches/ZeroFourTwo/AUTHORS @@ -0,0 +1,3 @@ +Mario Danic +Thomas Schmitt + diff --git a/libburn/branches/ZeroFourTwo/CONTRIBUTORS b/libburn/branches/ZeroFourTwo/CONTRIBUTORS new file mode 100644 index 00000000..31b77c58 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/CONTRIBUTORS @@ -0,0 +1,6 @@ +Joe Neeman +Philippe Rouquier +Gabriel Craciunescu +George Danchev +Jean-Francois Wauthy +Lorenzo Taylor diff --git a/libburn/branches/ZeroFourTwo/COPYING b/libburn/branches/ZeroFourTwo/COPYING new file mode 100644 index 00000000..5a965fbc --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/COPYRIGHT b/libburn/branches/ZeroFourTwo/COPYRIGHT new file mode 100644 index 00000000..2199a9e8 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/ChangeLog b/libburn/branches/ZeroFourTwo/ChangeLog new file mode 100644 index 00000000..7001d0f2 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/ChangeLog @@ -0,0 +1 @@ +nothing here now diff --git a/libburn/branches/ZeroFourTwo/INSTALL b/libburn/branches/ZeroFourTwo/INSTALL new file mode 100644 index 00000000..5458714e --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/Makefile.am b/libburn/branches/ZeroFourTwo/Makefile.am new file mode 100644 index 00000000..752298a8 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/Makefile.am @@ -0,0 +1,202 @@ +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 - A71025 +cdrskin_cdrskin_CPPFLAGS = -Ilibburn +cdrskin_cdrskin_CFLAGS = -DCdrskin_libburn_0_4_1 + +# cdrskin_cdrskin_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +# ts A80123, change proposed by Simon Huggins to cause dynamic libburn linking +cdrskin_cdrskin_LDADD = libburn/libburn.la $(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-project.org +copyright holders and then libburnia-project.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-project.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://svn.libburnia-project.org/libburn/trunk libburn +go into directory libburn and apply autotools by + ./bootstrap + +Alternatively you may unpack a release tarball for which you do not need +autotools installed. + +To build a libburnia-project.org subproject it should be sufficient to go +into its toplevel directory (here: "libburn") and execute + ./configure --prefix=/usr + 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://svn.libburnia-project.org/libisofs/trunk libisofs +See README file there. + + +------------------------------------------------------------------------------ + + + Overview of libburnia-project.org + +libburnia-project.org is an open-source software project for reading, mastering +and writing optical discs. +For now this means only CD media and all single layer DVD media except DVD+R. + +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 CD and most single layer DVD. +The burn API is quite comprehensively documented and can be used to build a +presentable application. +We 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. Its code is + independent of cdrecord. Its DVD capabilities are learned from + studying the code of dvd+rw-tools and MMC-5 specs. No code but only + the pure SCSI knowledge has been taken from dvd+rw-tools, though. + +- libisofs is the library to pack up hard disk files and directories into a + ISO 9660 disk image. This may then be brought to media 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. Additionally it + provides libburn's DVD capabilities, where only -sao is compatible + with 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 and man cdrskin/cdrskin.1 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 July 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 . + +- 16th January 2007 release of libburn-0.3.0 and cdrskin-0.3.0 . Now the scope + is widened to a first class of DVD media: overwriteable single layer types + DVD-RAM, DVD+RW, DVD-RW. This is not a cdrecord emulation but rather inspired + by dvd+rw-tools' "poor man" writing facility for this class of media. + Taking a bow towards Andy Polyakov. + +- 11th February 2007 version 0.3.2 covers sequential DVD-RW and DVD-R with + multi-session and with DAO. + +- 12th March 2007 version 0.3.4 supports DVD+R and thus covers all single layer + DVD media. Code for double layer DVD+/-R is implemented but awaits a tester + yet. + +- 23th April 2007 version 0.3.6 follows the unanimous opinion of Linux kernel + people that one should not use /dev/sg on kernel 2.6. + +- 31st July 2007 version 0.3.8 marks the first anniversary of libburn revival. + We look back on improved stability, a substantially extended list of media + and write modes, and better protection against typical user mishaps. + +- 24th October 2007 version 0.4.0 is the foundation of new library libisoburn + and an upcomming integrated application for manipulating and writing + ISO 9660 + Rock Ridge images. cdrskin-0.4.0 got capabilities like growisofs + by these enhancements: growing of overwriteable media and disk files. + Taking again a bow towards Andy Polyakov. + + +------------------------------------------------------------------------------ + + 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. To be exact: version 2 of that License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +------------------------------------------------------------------------------ +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/branches/ZeroFourTwo/acinclude.m4 b/libburn/branches/ZeroFourTwo/acinclude.m4 new file mode 100644 index 00000000..861847bb --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/bootstrap b/libburn/branches/ZeroFourTwo/bootstrap new file mode 100755 index 00000000..86709bfc --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/cdrskin/README b/libburn/branches/ZeroFourTwo/cdrskin/README new file mode 100644 index 00000000..4c86c02e --- /dev/null +++ b/libburn/branches/ZeroFourTwo/cdrskin/README @@ -0,0 +1,458 @@ +------------------------------------------------------------------------------ + libburnia-project.org scdbackup.sourceforge.net/cdrskin_eng.html +------------------------------------------------------------------------------ +cdrskin. By Thomas Schmitt +Integrated sub project of libburnia-project.org but also published via: +http://scdbackup.sourceforge.net/cdrskin_eng.html +http://scdbackup.sourceforge.net/cdrskin-0.4.1.tar.gz +Copyright (C) 2006-2008 Thomas Schmitt, provided under GPL version 2. +------------------------------------------------------------------------------ + + +cdrskin is a limited cdrecord compatibility wrapper which allows to use +most of the libburn features from the command line. + +Currently it is only supported on Linux with kernels >= 2.4. + +By using this software you agree to the disclaimer at the end of this text +"This software is provided as is. There is no warranty implied and ..." + + + Compilation, First Glimpse, Installation + +Obtain cdrskin-0.4.1.tar.gz, take it to a directory of your choice and do: + + tar xzf cdrskin-0.4.1.tar.gz + cd cdrskin-0.4.1 + +Within that directory execute: + + ./configure --prefix=/usr + make + +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 + +Note: The content of the cdrskin tarball is essentially the complete libburn + of the same version number. You may thus perform above steps in a local + SVN copy of libburn or in a unpacked libburn tarball as well. + + + 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/srM or /dev/hdX. + +The output of cdrskin --devices might look like + + 0 dev='/dev/sr0' rwrwr- : '_NEC' 'DVD_RW ND-4570A' + 1 dev='/dev/sr1' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B' + +So full and insecure enabling of both for everybody would look like + + chmod a+rw /dev/sr0 /dev/sr1 + +This is equivalent to the traditional setup chmod a+x,u+s cdrecord. + +I strongly discourage to run cdrskin with setuid root or via sudo ! +It is not checked for the necessary degree of hacker safety. + +Consider to put all authorized users into group "floppy", to chgrp the +device file to that group and to disallow w-access to others. + +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. +For finding /dev/sg1 from /dev/sr0, the program needs rw-access to both files. + + + Usage examples + +For options and recordable media classes see + man 1 cdrskin + +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 dev=/dev/sr0 +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 + +Make used CD-RW or used unformatted DVD-RW writeable again + cdrskin -v dev=0,1,0 blank=fast -eject + +Format DVD-RW to avoid need for blanking before re-use + cdrskin -v dev=0,1,0 blank=format_overwrite + +De-format DVD-RW to make it capable of multi-session again + cdrskin -v dev=/dev/sr0 blank=deformat_sequential + +Burn image file my_image.iso to media + cdrskin -v dev=0,1,0 speed=12 fs=8m driveropts=burnfree padsize=300k \ + -eject my_image.iso + +Write multi-session to the same CD , DVD-R[W] or DVD+R + cdrskin dev=/dev/hdc padsize=300k -multi 1.iso + cdrskin dev=/dev/hdc padsize=300k -multi -tao 2.iso + cdrskin dev=/dev/hdc padsize=300k -multi -tao 3.iso + cdrskin dev=/dev/hdc padsize=300k -tao 4.iso + +Get multi-session info for option -C of program mkisofs: + c_values=$(cdrskin dev=/dev/hdc -msinfo 2>/dev/null) + mkisofs ... -C "$c_values" ... + +Burn a compressed afio archive to media 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 (not to any DVD). +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 + + + Restrictions + +Several advanced CD related options of cdrecord are still unsupported. +See output of command + cdrskin --list_ignored_options +If you have use cases for them, please report your wishes and expectations. + +DVD support is restricted to single layer DVD for now. Double layer media +are implemented but untested. +On the other hand, the capability of multi-session and of writing streams +of unpredicted lenght surpass the current DVD capabilities of cdrecord. + + + Inspiration and Standard + +cdrskin combines the command line interface standard set by cdrecord with +libburn, which is a control software for optical drives according to standard +MMC-5. For particular CD legacy commands, standards MMC-3 and MMC-1 apply. + +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. +I have the hope that Joerg feels more flattered than annoyed by cdrskin. + +Many thanks to Andy Polyakov for his dvd+rw-tools + http://fy.chalmers.se/~appro/linux/DVD+RW/tools +which provide me with examples and pointers into MMC specs for DVD writing. + + + 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 + +# 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. + +Program growisofs can append to an ISO filesystem on DVD+RW by additionally +manipulating the first session. Meanwhile cdrskin can do the same. +Option --grow_overwriteable_iso allows -multi (although unneeded), enables +-msinfo and -toc, and makes blank=fast an invalidator for ISO filesystems +on overwriteable media. + +Initial session (equivalent to growisofs -Z): + mkisofs ... | cdrskin --grow_overwriteable_iso blank=fast ... + +Add-on session (equivalent to growisofs -M): + cparms=$(cdrskin dev=/dev/sr0 --grow_overwriteable_iso -msinfo) + mkisofs -C "$cparms" -M /dev/sr0 ... | \ + cdrskin dev=/dev/sr0 --grow_overwriteable_iso ... - + + + DVD-RW and DVD-R + +DVD-RW are usable if formatted to state "Restricted Overwrite" or if in state +"Sequential Recording". DVD-R are always in sequential state. + +"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. +The according cdrskin option is blank=deformat_sequential . +If "Incremental Streaming" is available, then sequential media are capable +of multi-session like CD-R[W]. (But not capable of -audio recording.) +This means they need option -multi to stay appendable, need to be blanked +to be writeable from start, return useable info with -toc and -msinfo, +eventually perform appending automatically. +Without Incremental Streaming offered by the drive, only write mode DAO is +available with sequential DVD-R[W]. It only works with blank media, allows only +one single track, no -multi, and demands a fixely predicted track size. +(growisofs uses it with DVD-R[W] if option -dvd-compat is given.) + +Overwriteable DVD-RW behave much like DVD+RW. "Restricted" refers only to the +granularity of random access and block size which have always to be aligned to +full 32 kB. Sequential DVD-RW are converted into overwriteable DVD-RW by + cdrskin dev=... -v blank=format_overwrite +(Command dvd+rw-format -force can achieve Restricted Overwrite, too.) + +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, dvd+rw-format or cdrskin. Perils of DVD-RW. + +There are three DVD-RW formatting variants with cdrskin currently: + +blank=format_overwrite uses "DVD-RW Quick" formatting (MMC-type 15h) +and writes a first session of 128 MiB. 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. DVD-ROM drives might show ill behavior with them. + +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. + +De-formatting options are available to make overwriteable DVD-RW sequential: + +blank=deformat_sequential performs thorough blanking of all states of DVD-RW. +blank=all and blank=fast perform the same thorough blanking, but refuse to do +this with overwriteable DVD-RW, thus preserving their formatting. The specs +allow minimal blanking but the resulting media on my drives offer no +Incremental Streaming afterwards. So blank=fast will do full blanking. + +blank=deformat_sequential_quickest is faster but might yield DAO-only media. + + + DVD+R + +From the view of cdrskin they behave much like DVD-R. Each track gets wrapped +into an own session, though. + + + Emulated Drives + +cdrskin can use filesystem objects as emulated drives. Regular files or block +devices appear similar to DVD-RAM. Other file types resemble blank DVD-R. +Necessary precondition is option --allow_emulated_drives which is not accepted +if cdrskin took another user identity because of the setuid bit of its access +permissions. +Addresses of emulated drives begin with prefix "stdio:". E.g. + dev=stdio:/tmp/my_pseudo_drive + +For safety reasons the superuser is only allowed to use /dev/null as emulated +drive. See man page section FILES for a way to lift that ban. + + +------------------------------------------------------------------------------ + + 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. + + +------------------------------------------------------------------------------ + Project aspects and legal stuff +------------------------------------------------------------------------------ + +Important Disclaimer : + +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. + +Actually, in case of severe trouble, nearly always the drive and the media +are the cause. Any mistake of the burn program is supposed to be caught +by the drive's firmware and to lead to mere misburns. +The worst mishaps which hit the author implied the need to reboot the +system because of drives gnawing endlessly on ill media. Permanent hardware +damage did not occur in 1.5 years of development. + +------------------------------------------------------------------------------ + +Interested users 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. + +------------------------------------------------------------------------------ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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 + +------------------------------------------------------------------------------ +Based on and sub project of: +libburnia-project.org +By Mario Danic and Thomas Schmitt +Copyright (C) 2006-2008 Mario Danic, Thomas Schmitt + +libburnia-project.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-project.org. + + +------------------------------------------------------------------------------ +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/branches/ZeroFourTwo/cdrskin/add_ts_changes_to_libburn_0_4_0 b/libburn/branches/ZeroFourTwo/cdrskin/add_ts_changes_to_libburn_0_4_0 new file mode 100755 index 00000000..6913b78b --- /dev/null +++ b/libburn/branches/ZeroFourTwo/cdrskin/add_ts_changes_to_libburn_0_4_0 @@ -0,0 +1,243 @@ +#!/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. +# + +# The top level directory in the SVN snapshot is named +intermediate="./libburn_pykix" + +# libburn source used: http://libburnia.pykix.org +# Downloaded by: +# $ svn co http://libburnia-svn.pykix.org/libburn/tags/... $intermediate +# packed up in a tarball just to save it from inadverted changes by +# $ tar czf libburn_svn.tgz $intermediate +original="./libburn_svn_release.tgz" +# Historic moments: +# original="./libburn_svn_A60815.tgz" +# original="./libburn_cdrskin_A60819.tgz" + + +# My changes are in $changes , mainly in $changes/cdrskin +changes="./libburn-release" + +skin_release="0.4.0" +patch_level=".pl00" +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" + +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 vim.swp and binaries +rm "$cdrskin_target"/.*.swp +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 eventual SVN stuff from cdrskin directory +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 "$cdrskin_target"/doener_*.png + +# Remove automatically generated HTML man page +rm "$cdrskin_target"/man_1_cdrskin.html + +# Remove all add_ts_changes_to_libburn besides this one +for i in "$cdrskin_target"/add_ts_changes_to_libburn* +do + if test $(basename "$0") = $(basename "$i") + then + dummy=dummy + else + rm $i + fi +done + +# Remove libcevap +rm -rf "$target"/libcevap + + +# 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 + + +# 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 + +# Repair non-portable shell code output of ./bootstrap +( + cd "$compile_dir" || exit 1 + sed -e 's/^for ac_header in$/test -z 1 \&\& for ac_header in dummy/' \ + < ./configure > ./configure-repaired + if test "$?" = 0 + then + echo "$0: Empty 'for ac_header in' found in configure." >&2 + fi + mv ./configure-repaired ./configure + chmod a+rx,go-w,u+w ./configure +) + +# 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" -O2 -do_strip + cp "$compile_result" "../$bintarget_dynamic" + if test -n "$compile_static_opts" + then + "$compile_cmd" $compile_static_opts -O2 -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/branches/ZeroFourTwo/cdrskin/add_ts_changes_to_libburn_0_4_1 b/libburn/branches/ZeroFourTwo/cdrskin/add_ts_changes_to_libburn_0_4_1 new file mode 100755 index 00000000..81a90d63 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/cdrskin/add_ts_changes_to_libburn_0_4_1 @@ -0,0 +1,245 @@ +#!/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. +# + +# The top level directory in the SVN snapshot is named +intermediate="./libburn_pykix" + +# libburn source used: http://libburnia-project.org +# Downloaded by: +# $ svn co http://libburnia-project.org/libburn/tags/... $intermediate +# packed up in a tarball just to save it from inadverted changes by +# $ tar czf libburn_svn.tgz $intermediate +original="./libburn_svn.tgz" +# Historic moments: +# original="./libburn_svn_A60815.tgz" +# original="./libburn_cdrskin_A60819.tgz" + + +# My changes are in $changes , mainly in $changes/cdrskin +changes="./libburn-develop" + +skin_release="0.4.1" +patch_level="" +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" + +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 vim.swp and binaries +rm "$cdrskin_target"/.*.swp +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 eventual SVN stuff from cdrskin directory +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 "$cdrskin_target"/doener_*.png + +# Remove automatically generated HTML man page +rm "$cdrskin_target"/man_1_cdrskin.html + +# Remove libcevap +rm -rf "$target"/libcevap + +# Remove all add_ts_changes_to_libburn besides this one +for i in "$cdrskin_target"/add_ts_changes_to_libburn* +do + if test $(basename "$0") = $(basename "$i") + then + dummy=dummy + else + rm $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 + + +# 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 + +# Repair non-portable shell code output of ./bootstrap +( + cd "$compile_dir" || exit 1 + sed -e 's/^for ac_header in$/test -z 1 \&\& for ac_header in dummy/' \ + < ./configure > ./configure-repaired + if test "$?" = 0 + then + echo "$0: Empty 'for ac_header in' found in configure." >&2 + fi + mv ./configure-repaired ./configure + chmod a+rx,go-w,u+w ./configure +) + + +# 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" -libburn_svn -O2 -do_strip + cp "$compile_result" "../$bintarget_dynamic" + if test -n "$compile_static_opts" + then + "$compile_cmd" $compile_static_opts -libburn_svn -O2 -do_strip + cp "$compile_result" "../$bintarget_static" + fi +# "$compile_cmd" -libburn_svn -O2 -do_diet -do_strip +# cp "$compile_result" "../$bintarget_dynamic"_diet + "$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/branches/ZeroFourTwo/cdrskin/cdrecord_spy.sh b/libburn/branches/ZeroFourTwo/cdrskin/cdrecord_spy.sh new file mode 100755 index 00000000..54d7c344 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/cdrskin/cdrfifo.c b/libburn/branches/ZeroFourTwo/cdrskin/cdrfifo.c new file mode 100644 index 00000000..e61c533e --- /dev/null +++ b/libburn/branches/ZeroFourTwo/cdrskin/cdrfifo.c @@ -0,0 +1,1253 @@ +/* + 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 + 2= report each +*/ +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; + + /* eventual ISO-9660 image size obtained from first 64k of input */ + double iso_fs_size; + char *iso_fs_descr; /* eventually block 16 to 31 of input */ + + /* (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]; + /* if follow_up_eop[i]==buffer_size : read_idx was 0 when this was set */ + int follow_up_was_full_buffer[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; + + /* rank in peer chain */ + int chain_idx; +}; + + +/** 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; + o->iso_fs_size= -1.0; + o->iso_fs_descr= NULL; + 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_was_full_buffer[i]= 0; + 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->chain_idx= 0; + 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->iso_fs_descr!=NULL) + free((char *) o->iso_fs_descr); + 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) +{ + int idx; + struct CdrfifO *s; + + for(s= o;s->prev!=NULL;s= s->prev); /* determine start of o-chain */ + 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; + for(idx= 0;s!=NULL;s= s->next) + s->chain_idx= idx++; + 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); +} + + +int Cdrfifo_get_iso_fs_size(struct CdrfifO *o, double *size_in_bytes, int flag) +{ + *size_in_bytes= o->iso_fs_size; + return(o->iso_fs_size>=2048); +} + + +int Cdrfifo_adopt_iso_fs_descr(struct CdrfifO *o, char **pt, int flag) +{ + *pt= o->iso_fs_descr; + o->iso_fs_descr= NULL; + return(*pt!=NULL); +} + + +/** 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; + if(o->follow_up_eop[i]buffer_size || o->read_idx>0) { + valid_fill= o->follow_up_eop[i]-o->read_idx; + o->follow_up_was_full_buffer[i]= 0; + } else { + /* + If an input fd change hit exactly the buffer end then follow_up_eop + points to buffer_size and not to 0. So it is time to switch output + pipes unless this is immediately after follow_up_eop was set and + read_idx was 0 (... if this is possible at all while write_idx is 0). + follow_up_was_full_buffer was set in this case and gets invalid as + soon as a non-0 read_idx is detected (see above). + */ + if(o->follow_up_was_full_buffer[i]) + valid_fill= o->buffer_size; + else + valid_fill= 0; /* the current pipe is completely served */ + } + if(valid_fill==0) + *eop_idx= i; + else if(valid_fillchunk_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, eop_reached_counter= 0; + 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]; + if(Cdrfifo_debuG) + fprintf(stderr,"\ncdrfifo %d: new fifo destination fd : %d\n", + o->chain_idx,o->dest_fd); + o->read_idx= o->follow_up_sod[eop_reached]; + o->follow_up_eop[eop_reached]= -1; + eop_is_near= 0; + eop_reached= -1; + eop_reached_counter= 0; + goto setup_try; + } else { + /* work is really done */ + if((!was_closed) && ((flag&1)||Cdrfifo_debuG)) + fprintf(stderr, + "\ncdrfifo %d: w=%d r=%d | b=%d s=%d | i=%.f o=%.f (done)\n", + o->chain_idx,o->write_idx,o->read_idx,buffer_fill,buffer_space, + o->in_counter,o->out_counter); + return(2); + } + } else if(eop_reached>=0) + eop_reached_counter++; + 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 %d: on write: errno=%d , \"%s\"\n", + o->chain_idx,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 %d: on read: errno=%d , \"%s\"\n", + o->chain_idx,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 %d: on read(%d,buffer,%d): eof\n", + o->chain_idx,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; + + /* A70304 : can this happen ? */ + o->follow_up_was_full_buffer[idx]= (o->read_idx==0); + + if(Cdrfifo_debuG || (flag&1)) + fprintf(stderr,"\ncdrfifo %d: write_idx 0 on eop: read_idx= %d\n", + o->chain_idx,o->read_idx); + + } 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,"\ncdrfifo %d: new fifo source fd : %d\n", + o->chain_idx,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>=2) { + fprintf(stderr,"\n"); + for(ff= o; ff!=NULL; ff= ff->next) { + buffer_space= Cdrfifo_tell_buffer_space(ff,0); + fprintf(stderr, + "cdrfifo %d: w=%d r=%d | b=%d s=%d | i=%.f o=%.f\n", + ff->chain_idx,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; + } + +#ifndef Cdrfifo_standalonE + if(fill>=32*2048) { + int Scan_for_iso_size(unsigned char data[2048], double *size_in_bytes, + int flag); + int bs= 16*2048; + double size; + + /* memorize blocks 16 to 31 */ + if(o->iso_fs_descr!=NULL) + free((char *) o->iso_fs_descr); + o->iso_fs_descr= TSOB_FELD(char,bs); + if(o->iso_fs_descr==NULL) + return(-1); + memcpy(o->iso_fs_descr,o->buffer+bs,bs); + + /* try to obtain ISO-9660 file system size from block 16 */ + ret= Scan_for_iso_size((unsigned char *) (o->buffer+bs), &size, 0); + if(ret>0) + o->iso_fs_size= size; + } +#endif + + 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 + one runs with block size 2048 and feeds the other which runs with 2352. + Both fifos have the same number of follow_up pipes (tracks) which shall + be connected 1-to-1. +*/ +int Test_mixed_bs(char **paths, int path_count, + int fs_size, double speed_limit, double interval, int flag) +/* + bit0= debugging verbousity +*/ +{ + int fd_in[100],fd_out[100],ret,pipe_fds[100][2],real_out[100]; + int i,iv,stall_counter= 0,cycle_counter= 0.0; + char buf[10240], target_path[80]; + double in_counter, out_counter, prev_in= -1.0, prev_out= -1.0; + struct CdrfifO *ff_in= NULL, *ff_out= NULL; + + if(path_count<1) + return(2); + Cdrfifo_new(&ff_in,fd_in[0],fd_out[0],2048,fs_size,0); + for(i= 0; ichain_idx,ret); + if(ret<0) + return(-7); + break; + } + cycle_counter++; + Cdrfifo_get_counters(ff_in, &in_counter, &out_counter, 0); + if(prev_in == in_counter && prev_out == out_counter) + stall_counter++; + prev_in= in_counter; + prev_out= out_counter; + } + return(1); +} + + +/* 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 %d: fifo ended work with ret=%d\n", + ff1->chain_idx,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],"-mixed_bs_test")==0) { + + ret= Test_mixed_bs(argv+i+1,argc-i-1, + (int) fs_value,speed_limit,interval,(verbous>=2)); + fprintf(stderr,"Test_mixed_bs(): ret= %d\n",ret); + exit(ret<0); + + } 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,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/branches/ZeroFourTwo/cdrskin/cdrfifo.h b/libburn/branches/ZeroFourTwo/cdrskin/cdrfifo.h new file mode 100644 index 00000000..1237d20a --- /dev/null +++ b/libburn/branches/ZeroFourTwo/cdrskin/cdrfifo.h @@ -0,0 +1,171 @@ + +/* + 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); + +/** Inquire the eventually detected size of an eventual ISO-9660 file system + @return 0=no ISO resp. size detected, 1=size_in_bytes is valid +*/ +int Cdrfifo_get_iso_fs_size(struct CdrfifO *o, double *size_in_bytes,int flag); + + +/** Take over the eventually memorized blocks 16 to 31 of input (2 kB each). + The fifo forgets the blocks by this call. I.e. a second one will return 0. + After this call it is the responsibility of the caller to dispose the + retrieved memory via call free(). + @param pt Will be filled either with NULL or a pointer to 32 kB of data + @return 0=nothing is buffered, 1=pt points to valid freeable data +*/ +int Cdrfifo_adopt_iso_fs_descr(struct CdrfifO *o, char **pt, 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/branches/ZeroFourTwo/cdrskin/cdrskin.1 b/libburn/branches/ZeroFourTwo/cdrskin/cdrskin.1 new file mode 100644 index 00000000..588d717c --- /dev/null +++ b/libburn/branches/ZeroFourTwo/cdrskin/cdrskin.1 @@ -0,0 +1,1236 @@ +.\" 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 "September 26, 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[W], DVD-R[W], DVD+R[W], 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. +You do not need to be superuser for its daily usage. +.SS +.B Overview of features: +.br +Blanking of CD-RW and DVD-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 +or on DVD-R[W] (in Incremental mode) or on DVD+R. +.br +Single session on DVD-RW or DVD-R (Disk-at-once). +.br +Single session or emulated ISO-9660 multi-session +.br +on overwriteable DVD+RW, DVD-RW, DVD-RAM, +.br +or on data file or block device. +.br +Bus scan, burnfree, speed options, retrieving media info, padding, fifo. +.br +See section EXAMPLES at the end of this text. +.SS +.B General information paragraphs: +.br +Track recording model +.br +Write mode selection +.br +Recordable CD Media +.br +Sequentially Recordable DVD Media +.br +Overwriteable DVD Media +.br +Drive preparation and addressing +.br +Emulated drives +.SS +.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 +Some media types 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. With DVD there is only type data. +.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. +.SS +.B Write mode selection: +.br +In general there are two approaches for writing media: +.br +A permissive mode depicted by option +.B -tao +which needs no predicted track size and allows to make use of +eventual multi-session capabilities. +.br +A more restrictive mode +.B -sao +(alias -dao) which usually demands a predictable track size and is not +necessarily capable of multi-session. It may have advantages for some +readers resp. players of the recorded tracks. +.br +If none of the options -dao, -tao or -sao is given then the program will +try to choose a write mode which matches the defined recording job, +the capabilities of the drive and the state of the present media. +.br +So the mentioning of write modes in the following paragraphs and in the +examples is not so much a demand that the user shall choose one explicitely, +but rather an illustration of what to expect with particular media types. +.SS +.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. Closing is done automatically unless option +.B -multi +is given which keeps the media appendable. +.br +Write mode +-tao allows to use track sources of unpredictable length (like stdin) and +allows to write further sessions to appendable media. +-sao produces audio sessions with seamless tracks but needs predicted track +sizes and cannot append sessions to media. +.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. +.SS +.B Sequentially Recordable DVD Media: +.br +Currently DVD-RW, DVD-R and DVD+R can be used for the Sequential recording +model. +.br +DVD-RW must be in state "Sequential Recording". +The media must be either blank or appendable. +Newly purchased DVD-RW and DVD-R media are in this state. +Used DVD-RW get into blank sequential state by option +.B blank=deformat_sequential . +.br +With DVD-R[W] two write modes may be available: +.br +Mode DAO has many restrictions. It does not work with +appendable media, allows no -multi and only a single track. The size of the +track needs to be known in advance. So either its source has to be a disk file +of recognizable size or the size has to be announced explicitely by options +.B tsize= +or +.B tao_to_sao_tsize= . +.br +DAO is the only mode for media which do not offer feature 21h Incremental +Streaming. DAO may also be selected explicitely by option +.B -sao . +Program growisofs uses DAO on sequential DVD-R[W] media for maximum +DVD-ROM/-Video compatibility. +.br +The other mode, Incremental Streaming, is the default write mode if +it is available and if the restrictions of DAO would prevent the job. +Incremental Streaming may be selected explicitely by option +.B -tao +as it resembles much CD TAO by allowing track sources of +unpredicted length and to keep media appendable by option +.B -multi . +The only restriction towards CD-R[W] is the lack of support for -audio tracks. +Multiple tracks per session are permissible. +.br +The write modes for DVD+R resemble those with DVD-R except that with DVD+R +each track gets wrapped in an own session. There is no -dummy writing with +DVD+R. +.br +Quite deliberately write mode -sao insists in the tradition of a predicted +track size and blank media, whereas -tao writes the tracks open ended and +allows appendable media. +.br +.B Note: +Option -multi might make DVD media unreadable in some DVD-ROM drives. +Best reader compatibility is achieved without it +(i.e. by single session media). +.SS +.B Overwriteable DVD Media: +.br +Currently types DVD+RW, DVD-RW and DVD-RAM can be overwritten via cdrskin. +.br +DVD+RW and DVD-RAM media need no special initial formatting. They offer a +single continuous data area for blockwise random access. +.br +Option -audio is not allowed. Only one track is allowed. +Option -multi cannot mark a recognizeable end of overwriteable media. +Therefore -multi is banned unless ISO-9660 images shall be expandable by help +of option +.B --grow_overwriteable_iso . +Without this option or without an ISO-9660 filesystem image present +on media, -toc does not return information about the media content and +media get treated as blank regardless wether they hold data or not. +.br +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 are sold in state "Sequential Recording". To become suitable for the +Overwriteable DVD recording model they need to get formatted to state +"Restricted Overwrite". Then they behave much like DVD+RW. This formatting +can be done by option +.B blank=format_overwrite . +.br +Several programs like dvd+rw-format, cdrecord, wodim, or cdrskin +can bring a DVD-RW out of overwriteable state so +that it has to be formatted again. If in doubt, just give it a try. +.SS +.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 +.B superuser +who is able to get this list without further +precautions. +.br +It is consensus that \fBchmod a+rw /dev/sr0\fP or \fBchmod a+rw /dev/hdc\fP +is less security sensitive than chmod u+s,a+x /usr/bin/cdrskin. The risk for +the drive is somewhat higher but the overall system is much less at stake. +Consider to restrict rw-access to a single group which bundles the users who +are allowed to use the burner drive (like group "floppy"). +.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/sg0). +.br +.SS +.B Emulated drives: +.br +Option +.B --allow_emulated_drives +enables addressing of pseudo-drives +which get emulated on top of filesystem objects. Regular data files and +block devices result in pseudo-drives which behave much like DVD-RAM. +If the given address does not exist yet but its directory exists, then +it gets created as regular file. +Other file types like character devices or pipes result in pseudo-drives +which behave much like blank DVD-R. +The target file address is given after prefix "stdio:". +.br +E.g.: dev=stdio:/tmp/my_pseudo_drive +.br +Addresses of the form "stdio:/dev/fd/" are treated special. The +number is read literally and used as open file descriptor. With +dev="stdio:/dev/fd/1" the normal standard output of the program is +redirected to stderr and the stream data of a burn run will appear on stdout. +.br +Not good for terminals ! Redirect it. +.br +Pseudo-drives allow -dummy. Their reply with --tell_media_space can be utopic. +-dummy burn runs touch the file but do not modify its data content. +.br +Note: --allow_emulated_drives is restricted to stdio:/dev/null if cdrskin +is run by the +.B superuser +or if it has changed user identity via the +.B setuid +bit of its access permissions. The ban for the superuser can be lifted by a +skillfully created file. See section FILES below. +.br +.SH OPTIONS +.TP +.BI \-\-help +Show non-cdrecord compatible options. +.TP +.BI \-help +Show cdrecord compatible options. +.br +Note that some of the help texts are quite wrong - for cdrecord as well as +for cdrskin (e.g. -format, blank=, -load). They are, nevertheless, traditional +indicators for the availability of the listed options. Some frontend programs +make decisions after reading them. +.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. +.br +Option -audio may be used only with CD media and not with DVD. +.TP +.BI blank= type +Blank a CD-RW, a DVD-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-RW or an unformatted DVD-RW. +(See also --prodvd_cli_compatible, --grow_overwriteable_iso) +.TP +fast +Minimally blank an entire CD-RW or blank an unformatted DVD-RW. +(See also --prodvd_cli_compatible, --grow_overwriteable_iso) +.TP +format_overwrite +Format a DVD-RW to "Restricted Overwrite". The user should bring some patience. +.br +(Note: blank=format_overwrite* are not original cdrecord options.) +.TP +format_overwrite_quickest +Like format_overwrite without creating a 128 MiB 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 MiB. +Most traditional formatting is attempted. No data get written. +Much patience is required. +.br +This option treats already formatted media even if not option -force is given. +.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. +.TP +deformat_sequential +Like blank=all but with the additional ability to blank overwriteable DVD-RW. +This will destroy their formatting and make them sequentially recordable. +Another peculiarity is the ability to blank media which appear already blank. +This is similar to option -force but does not try to blank media other than +recognizable CD-RW and DVD-RW. +.br +(Note: blank=deformat_sequential* are not original cdrecord options.) +.TP +deformat_sequential_quickest +Like blank=deformat_sequential but blanking DVD-RW only minimally. +This is faster than full blanking but may yield media incapable of +Incremental Streaming (-tao). +.RE +.TP +.BI \-checkdrive +Retrieve some info about the addressed drive and then exit. +Exits with non-zero value if the drive cannot be found and opened. +.TP +.BI \-dao +Alias for option -sao. Write CD in Session at Once mode +or DVD-R[W] in Disc-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=noburnfree" to disable the drive's eventual protection +mechanism against temporary lack of source data (i.e. buffer underrun). +A drive that announces no such capabilities will not get them enabled anyway, +even if attempted explicitely via "driveropts=burnfree". +.TP +.BI \-dummy +Try to perform the drive operations without actually affecting the inserted +media. There is no warranty that this will work with a particular combination +of drive, media, and write mode. Blanking is prevented reliably, though. +To avoid inadverted real burning, -dummy refuses burn runs on anything but +CD-R[W], DVD-R[W], or emulated stdio-drives. +.TP +.BI \-eject +Eject the disc 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 to enforce blanking or re-formatting of media +which appear to be in the desired blank or format state already. +.br +This option enables a burn run with option -dummy even if libburn believes +that drive and media will not simulate the write mode but will write for real. +.br +.B Caution: +Use this only when in urgent need. +.TP +.BI \-format +Same as blank=format_overwrite_full -force but restricted to DVD+RW. +.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 -immed +Equivalent to: +.br +modesty_on_drive=1:min_percent=75:max_percent=95 +.br +The name of this cdrecord option stems from the "Immed" bit which can make some +long running drive commands asynchronous and thus eases the load on some +wiring hardware types. Regardless of option -immed, cdrskin uses asynchronous +commands where possible and appropriate. +.TP +.BI -inq +Print the identification of the drive and then exit. +.TP +.BI -isosize +The next track following this option will try to obtain its source size from +the header information out of the first few blocks of the source data. +If these blocks indicate an ISO-9660 filesystem then its declared size +will be used under the assumption that it is a single session filesystem. +.br +If not, then the burn run will be aborted. +.br +The range of -isosize is exactly one track. Further tracks may be preceeded +by further -isosize options, though. At least 15 blocks of padding will be +added to each -isosize track. But be advised to rather use padsize=300k. +.br +This option can be performed on track sources which are regular files or block +devices. For the first track of the session it can be performed on any type +of source if there is a fifo of at least 64 kiB. See option fs= . +.TP +.BI -load +Load the media and exit. Exit value is 0 if any kind of media was found, non +zero else. Note: Option -eject will unload the media even if -load is given. +.TP +.BI -lock +Like option -load but leave the drive's eject button disabled if there is any +media found and not option -eject is given. +.br +Use program "eject" or cdrskin -eject to get the tray out of the drive. +Runs of programs like cdrecord, growisofs, wodim, cdrskin will not be hampered +and normally enable the drive's eject button when they are done. +.TP +.BI minbuf= percentage +Equivalent to: +.br +modesty_on_drive=1:min_percent=:max_percent=95 +.br +Percentage is permissible between 25 and 95. +.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 except the one of option +--tell_media_space and 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. +.br +To have a chance for working on overwriteable media, this option has to be +accompanied by option --grow_overwriteable_iso. +.TP +.BI \-multi +This option keeps the CD or unformatted DVD-R[W] appendable after the current +session has been written. +Without it the disc gets closed and may not be written any more - unless it +is a -RW and gets blanked which causes loss of its content. +.br +The following sessions can only be written in -tao mode. -multi is prohibited +with DVD-R[W] DAO write mode. Option --prodvd_cli_compatible eventually makes +-multi tolerable but cannot make it work. +.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. +This lba is the address of the 2048 byte block where the archive begins. +.br +With overwriteable DVD media, -multi cannot mark the end of the session. +So when adding a new session this end has to be determined from the payload. +Currently only ISO-9660 filesystems can be used that way. See option +.B \--grow_overwriteable_iso +for lifting the ban on -multi. +.br +Note: -multi might make DVD media unreadable in some DVD-ROM drives. +.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 kiB 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 CD 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 CD in Session At Once mode, a sequential DVD-R[W] in Disc-at-once +(DAO) mode, or a DVD+R. +.br +With CD this mode is able to put several audio tracks on media without +producing audible gaps between them. +.br +With DVD-R[W] this mode can only write a single track. +No -multi is allowed with DVD-R[W] -sao. +.br +-sao is permissible with overwriteable DVD and with DVD+R but actually only +imposes restrictions without providing known advantages. +.br +-sao 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=. +.br +-sao cannot be used on appendable media. +.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,000 bytes/second. With DVD, 1x = 1,385,000 bytes/second. +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. +Special speed settings are: +.br +0 = minimal speed , -1 = maximal speed (default), text "any" = like -1. +.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 CD in Track At Once (TAO) mode, sequential DVD-R[W] in Incremental +Streaming mode, or DVD+R without traditional -sao restrictions. +This mode also applies pro-forma to overwriteable DVD media. +.br +Mode -tao 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 media which already hold data. With unformatted DVD-R[W] it is +the only mode which allows -multi. +.TP +.BI \-toc +Print the table of content (TOC) which describes the tracks recorded on disc. +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 media 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. +.TP +.BI \-waiti +Wait until input data is available at stdin or EOF occurs at stdin. +Only then begin to access any drives. +.br +One should use this if cdrskin is working at the end of a pipe where the +feeder process reads from the drive before it starts writing its output into +cdrskin. Example: +.br +mkisofs ... -C 0,12800 -M /dev/sr0 | \\ +.br +cdrskin dev=/dev/sr0 ... -waiti - +.br +This option works even if stdin is not among the track sources. If no process +is piping in, then the Enter key of your terminal will act as trigger for +cdrskin. Note that this input line will not be consumed by cdrskin if stdin +is not among the track sources. It will end up as shell command, usually. +.PP +Alphabetical list of options which are genuine to cdrskin and intended for +normal use: +.TP +.BI \--adjust_speed_to_drive +Curb explicitely given speed= values to the maximum which is announced by the +drive for the loaded media. By default, such an adjustment is only made with +pseudo-speeds 0 and -1 whereas speed settings > 0 are sent unchanged to the +drive which will then choose an appropriate speed on its own. +.TP +.BI \--allow_emulated_drives +Enable drive addresses of the form dev=stdio:. See above, paragraph +"Drive preparation and addressing". +.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 assert_write_lba= block_number | byte_address +Abort if the write address given with this option is not the same as predicted +immediately before the write session starts. This option can ensure that a +start address which was presumed by a formatter like mkisofs -C is really used +by the drive for writing. +assert_write_lba=0 effectively demands blank media and excludes appendables. +.br +Block numbering is peculiar: If the last character of the option string is +a letter [a-zA-Z] then the usual unit scaling by "s", "k", "m", etc. applies +and the result is divided by 2048. Else the number value of the string is +taken as plain block number with block size 2048 byte. +(E.g ...=1000 or ...=1000s means block 1000, ...=1m means block +512, ...=4096b means block number 2) +.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 direct_write_amount= size +Do not write a session with tracks but rather make an appropriate number of +direct write operations with no preparations. Flushing the drive buffer will +be the only finalization. It is advised to eject the media afterwards because +the write operations circumvent the usual system i/o with its caches and +buffers. By ejecting, those invalid memory copies get surely discarded. +.br +Only few media can be written this way: DVD-RAM, RVD+RW and overwriteable +DVD-RW. Writing is restricted to the already formatted area of the media. +.br +Writing starts at byte 0 of the media or at the address given by option +.B write_start_address= . +Only the first track source is used as input for the write operations. +The fifo (fs=) is disabled. +.br +Parameter +.B size +controls the amount of data to be written. Size 0 means that the track source +shall be used up until EOF. In this case, the last write transaction gets +padded up to the necessary size by zeros. Size -1 revokes direct writing +and switches back to normal session oriented writing. +.br +Both, write_start_address and direct_write_amount size must be aligned to a +media dependend transaction size. With DVD-RAM and DVD+RW this is 2k, with +overwriteable DVD-RW it is 32k. +.TP +.BI fallback_program= command +Set a command name to be executed if cdrskin encounters a known cdrecord +option which it does not yet support. If a non-empty command is given with +fallback_program=, and if no essential options are given which are specific +to cdrskin, then cdrskin will delegate the job to said command. +.br +The natural commands to be given are cdrecord or wodim but one may well submit +the address of an own program. +.br +The fallback programm will get all arguments of cdrskin which do not match +the shell patterns --?* or *_*=* . This eventually suppresses path names of +track sources which happen to match those patterns. The options from the +startup files are not handed to the fallback program. +.br +Fallback program execution is disabled if cdrskin is run setuid and not +option --allow_setuid is given. In general, the drive's device files and the +involved programs should be set up so that each program runs under its advised +conditions. (E.g. cdrskin as member of group floppy, cdrecord setuid root.) +.br +Two alias names for cdrskin are predefined with default fallback programs: +.br +.B unicord +implies fallback_program=cdrecord +.br +.B codim +implies fallback_program=wodim +.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 +is enabled automatically if not driveropts=noburnfree is given. +.TP +.BI \--grow_overwriteable_iso +Enable emulation of multi-session writing on overwriteable media which +contain an ISO-9660 filesystem. This emulation is learned from growisofs -M +but adapted to the usage model of +.br +.B cdrskin -msinfo +.br +.B mkisofs -C -M | cdrskin -waiti [-multi] - +.br +--grow_overwriteable_iso does not hamper the use of true multi-session media. +I.e. it is possible to use the same cdrskin options with both kinds of media +and to achieve similar results if ISO-9660 filesystem images are to be written. +This option implies option -isosize and therefore demands that the track +source is a ISO-9660 filesystem image. +.br +With overwriteable media and no option blank=fast|all present it expands an +eventual ISO-9660 filesystem on media. It is assumed that this image's inner +size description points to the end of the valuable data. +Overwriteable media with a recognizeable ISO-9660 size will be regarded as +appendable rather than as blank. I.e. options -msinfo and -toc will work. +-toc will always show a single session with its size increasing with +every added mkisofs image. +.br +If not overriden by option write_start_address=, the track with the new image +will be placed behind the end of the old one. One may use option +assert_write_lba= to make sure that media state and mkisofs job do match. +.br +--grow_overwriteable_iso causes option blank=fast|all to invalidate an +eventual ISO-9660 image by altering the first few bytes of block 16 on +overwriteable media. +Option -multi is tolerated in order not to hamper true multi-session media. +.br +An equivalent of growisofs -Z for overwriteable media is: +.br +.B mkisofs | cdrskin --grow_overwriteable_iso blank=fast [-multi] - +.br +With multi-session DVD, blank=fast will act like dvd+rw-format -blank=full . +.br +growisofs -dvd-compat is roughly equivalent to cdrskin without option -multi. +.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 \--prodvd_cli_compatible +Activates behavior modifications with some DVD situations which bring cdrskin +nearer to the behavior of cdrecord-ProDVD: +.br +Option -multi with unsuitable media is not an error but simply has no effect. +.br +Options blank=fast and blank=all deformat overwriteable DVD-RW media. +.br +Option blank=fast does indeed minmal blanking with DVD-RW. This may yield media +which can only do DAO but not Incremental Streaming. +.TP +.BI \--single_track +Accept only the last argument of the command line as track source address. +.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 and an +exact track size prediction is demanded by the write mode. +.br +This was the fallback from bad old times when cdrskin was unable to burn +in mode -tao . It came back with minimally blanked DVD-RW which allow no +Incremental Streaming (-tao) resp. with explicitly selected write mode -sao +for best DVD-ROM compatibility. +.br +If the track source delivers less bytes than announced then the missing ones +will be filled with zeros. +.TP +.BI --tell_media_space +Prepare a recording session, do not perform it but rather inquire the +maximum number of 2048 byte data blocks which may be written in +the current state of media with the prepared setup. So this option disables +recording of data. It does allow blanking, though, and will measure space +afterwards. +.br +It is not mandatory to give track sources but their nature may influence +the available capacity. So for most realistic results one may set up +the full burn session and add --tell_media_space. But if one has to expect +a cdrskin version prior to 0.3.3 no track source should be given in order +not to start an involuntary burn session. +In this case set at least -sao or -tao explicitely. +.br +The result gets printed to standard output. It is 0 or empty if no writing +is possible with the given options. +This option redirects to stderr all message output except its own result +string and eventual output of -msinfo. +.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 kiB blocks, but better is 32 kiB. +With DVD-RW 32 kiB 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 \--allow_untested_media +Enable the use of media profiles which have been implemented but not yet +tested. Currently this applies to : +.br +Profile 0015h , DVD-R/DL Sequential (will not allow -multi). +.br +Profile 002Bh , DVD+R/DL. +.br +If you really test such media, then please report the outcome on +libburn-hackers@pykix.org +.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/sr1 dev_translation=+ATA:1,1,0+/dev/sr2 +.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_f_setlk +Linux specific: Try to get exclusive lock on drive device file via fcntl(2). +.TP +.BI \--drive_not_exclusive +Linux specific: Combine --drive_not_f_setlk and --drive_not_o_excl. +.TP +.BI \--drive_not_f_setlk +Linux specific: Do not try to get exclusive lock on drive device file via +fcntl(2). +.TP +.BI \--drive_not_o_excl +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_dev_family= sr | scd | sg +Linux specific: Select a SCSI device file family to be used for drive command +transactions. Normally this is /dev/sgN on kernel versions < 2.6 and /dev/srN +on kernels >= 2.6 . This option allows to explicitely override that default +in order to meet other programs at a common device file for each drive. +On kernel 2.4 families sr and scd will find no drives. +.br +Device file family /dev/hdX on kernel >= 2.6 is not affected by this setting. +.TP +.BI \--drive_scsi_exclusive +Linux specific: +Try to exclusively reserve device files /dev/srN, /dev/scdM, /dev/sgK of drives. +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 \--fill_up_media +Expand the last track of the session to occupy all remaining free space on +the media. +.br +This option overrides option -multi. It will not fill up media if option -sao +is given with CD media. +.br +.B Caution: +With multi-session media this option might increase readatibility on DVD-ROM +drives but with some DVD recorders and media types it might also fail to +produce readable media at all. "Your mileage may vary". +.br +You can expect the best possible read compatibility if you do not use -multi at +all. +.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 modesty_on_drive= [:min_percent=][:max_percent=] +Mode 1 keeps the program from trying to write to the burner drive while its +buffer is in danger to be filled by more than max_percent. If this filling is +exceeded then the program will wait until the filling is at most min_percent. +.br +This can ease the load on operating system and drive controller and thus help +with achieving better input bandwidth if disk and burner are not on independent +controllers (like hda and hdb). Unsufficient input bandwidth is indicated by +output "(fifo xy%)" of option -v if xy is lower than 90 for some time. +modesty_on_drive= might hamper output bandwidth and cause buffer underruns. +.br +To have max_percent larger than the burner's best actual +buffer fill has the same effect as min_percent==max_percent. Some burners +do not use their full buffer with all media types. Watch output "[buf xy%]" +of option -v to get an impression of the actual buffer usage. Some burners +are not suitable because they report buffer fill with granularity too large +in size or time. +.br +Mode 0 disables this feature. Mode -1 keeps it unchanged. Default is: +.br +modesty_on_drive=0:min_percent=65:max_percent=95 +.br +Percentages are permissible in the range of 25 to 100. +.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 or DVD-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/sg0 using the same SCSI address as /dev/sr0.) +.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. +.br +.SH EXAMPLES +.SS +.B Get an overview of drives and their addresses: +.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 -v -atip +.br +cdrskin dev=/dev/hdc -toc +.SS +.B Make used CD-RW or used unformatted DVD-RW writable again: +.br +cdrskin -v dev=/dev/sg1 blank=fast -eject +.br +cdrskin -v dev=/dev/dvd blank=all -eject +.SS +.B Format DVD-RW to avoid need for blanking before re-use: +.br +cdrskin -v dev=/dev/sr0 blank=format_overwrite +.SS +.B De-format DVD-RW to make it capable of multi-session again: +.br +cdrskin -v dev=/dev/sr0 blank=deformat_sequential +.SS +.B Write ISO-9660 filesystem image as only one to blank or formatted media: +.br +cdrskin -v dev=/dev/hdc speed=12 fs=8m \\ +.br +-sao -eject padsize=300k my_image.iso +.SS +.B Write compressed afio archive on-the-fly (not possible with minimally blanked DVD-RW): +.br +find . | afio -oZ - | \\ +.br +cdrskin -v dev=0,1,0 fs=32m speed=8 \\ +.br +-tao padsize=300k - +.SS +.B Write multi-session to the same CD, DVD-R[W] or DVD+R: +.br +cdrskin dev=/dev/hdc -v padsize=300k -multi -tao 1.iso +.br +cdrskin dev=/dev/hdc -v padsize=300k -multi -tao 2.iso +.br +cdrskin dev=/dev/hdc -v padsize=300k -multi -tao 3.iso +.br +cdrskin dev=/dev/hdc -v padsize=300k -tao 4.iso +.SS +.B Get multi-session info for option -C of program mkisofs: +.br +c_values=$(cdrskin dev=/dev/hdc -msinfo 2>/dev/null) +.br +mkisofs ... -C "$c_values" ... +.SS +.B Inquire free space on media for a -tao -multi run: +.br +x=$(cdrskin dev=/dev/sr0 -tao -multi \\ +.br +--tell_media_space 2>/dev/null) +.br +echo "Available: $x blocks of 2048 data bytes" +.SS +.B Write audio tracks to CD: +.br +cdrskin -v dev=ATA:1,0,0 speed=48 -sao \\ +.br +track1.wav track2.au -audio -swab track3.raw +.br +.SH FILES +.SS +Startup files: +.br +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. +.br +Example content of a startup file: +.br +# This is the default device +.br +dev=0,1,0 +.br +# Some more options +.br +fifo_start_at=0 +.br +fs=16m +.br +.SS +Disabling superuser safety precautions: +The superuser is normally banned from using any other emulated drive but +/dev/null. This ban can be lifted by the existence of file +.PP +.B /root/cdrskin_permissions/allow_emulated_drives +.PP +where the directory must be owned by the superuser and must not offer +w-permissions for group or others. +.br +Warning: Superusers must take care not to spoil their hard disk via its raw +block device (like stdio:/dev/hda or stdio:/dev/sd0). + +.SH SEE ALSO +.TP +Formatting data 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 (also tutor of libburn's DVD capabilities): +.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/branches/ZeroFourTwo/cdrskin/cdrskin.c b/libburn/branches/ZeroFourTwo/cdrskin/cdrskin.c new file mode 100644 index 00000000..75bc0bc8 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/cdrskin/cdrskin.c @@ -0,0 +1,7954 @@ + +/* + cdrskin.c , Copyright 2006-2007 Thomas Schmitt +Provided under GPL version 2. 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 or DVD, 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. + +------------------------------------------------------------------------------ + +For a more comprehensive example of the advised way to write an application +of libburn see test/libburner.c . + +------------------------------------------------------------------------------ +This program is currently copyright Thomas Schmitt only. +The copyrights of several components of libburnia-project.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 fork 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-project.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='...' \ + -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1 \ + -o cdrskin cdrskin.c cdrfifo.c cleanup.c \ + -L../libburn/.libs -lburn -lpthread + +or + + cd .. + cc -g -I. -DCdrskin_build_timestamP='...' \ + -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1 \ + -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/read.o \ + libburn/libdax_audioxtr.o libburn/libdax_msgs.o \ + -lpthread + +*/ + + +/** The official program version */ +#ifndef Cdrskin_prog_versioN +#define Cdrskin_prog_versioN "0.4.1" +#endif + +/** The official libburn interface revision to use. + (May get changed further below) +*/ +#ifndef Cdrskin_libburn_majoR +#define Cdrskin_libburn_majoR 0 +#endif +#ifndef Cdrskin_libburn_minoR +#define Cdrskin_libburn_minoR 4 +#endif +#ifndef Cdrskin_libburn_micrO +#define Cdrskin_libburn_micrO 1 +#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_4_0 +#define Cdrskin_libburn_versioN "0.4.0" +#define Cdrskin_libburn_from_pykix_svN 1 +#endif /* Cdrskin_libburn_0_4_0 */ + +#ifdef Cdrskin_libburn_0_4_1 +#define Cdrskin_libburn_versioN "0.4.1" +#define Cdrskin_libburn_from_pykix_svN 1 + +/* Place novelty switch macros here. + Move them down to Cdrskin_libburn_from_pykix_svN on version leap +*/ + +/* there are no novelties in 0.4.1 yet */ + +#endif /* Cdrskin_libburn_0_4_1 */ + +#ifndef Cdrskin_libburn_versioN +#define Cdrskin_libburn_0_4_0 +#define Cdrskin_libburn_versioN "0.4.0" +#define Cdrskin_libburn_from_pykix_svN 1 +#endif + +#ifdef Cdrskin_libburn_0_4_0 +#undef Cdrskin_libburn_majoR +#undef Cdrskin_libburn_minoR +#undef Cdrskin_libburn_micrO +#define Cdrskin_libburn_majoR 0 +#define Cdrskin_libburn_minoR 4 +#define Cdrskin_libburn_micrO 0 +#endif +#ifdef Cdrskin_libburn_0_4_1 +#undef Cdrskin_libburn_majoR +#undef Cdrskin_libburn_minoR +#undef Cdrskin_libburn_micrO +#define Cdrskin_libburn_majoR 0 +#define Cdrskin_libburn_minoR 4 +#define Cdrskin_libburn_micrO 1 +#endif + + + +#ifdef Cdrskin_libburn_from_pykix_svN +#ifndef Cdrskin_oldfashioned_api_usE + +/* 0.2.2 */ +#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 + +/* 0.2.4 */ +#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 + +/* 0.2.6 */ +#define Cdrskin_libburn_has_pretend_fulL 1 +#define Cdrskin_libburn_has_multI 1 +#define Cdrskin_libburn_has_buffer_min_filL 1 + +/* 0.3.0 */ +#define Cdrskin_atip_speed_is_oK 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 + +/* 0.3.2 */ +#define Cdrskin_libburn_has_get_msc1 1 +#define Cdrskin_libburn_has_toc_entry_extensionS 1 +#define Cdrskin_libburn_has_get_multi_capS 1 + +/* 0.3.4 */ +#define Cdrskin_libburn_has_set_filluP 1 +#define Cdrskin_libburn_has_get_spacE 1 +#define Cdrskin_libburn_write_mode_ruleS 1 +#define Cdrskin_libburn_has_allow_untested_profileS 1 +#define Cdrskin_libburn_has_set_forcE 1 + +/* 0.3.6 */ +#define Cdrskin_libburn_preset_device_familY 1 +#define Cdrskin_libburn_has_track_set_sizE 1 + +/* 0.3.8 */ +#define Cdrskin_libburn_has_set_waitinG 1 +#define Cdrskin_libburn_has_get_best_speeD 1 + +/* 0.4.0 */ +#define Cdrskin_libburn_has_random_access_rW 1 +#define Cdrskin_libburn_has_get_drive_rolE 1 +#define Cdrskin_libburn_has_drive_equals_adR 1 + + +#ifdef Cdrskin_new_api_tesT + +/* put macros under test caveat here */ + +#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-project.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 + +#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 + + +/** If tsize= sets a value smaller than media capacity divided by this + number then there will be a warning and gracetime set at least to 15 */ +#define Cdrskin_minimum_tsize_quotienT 2048.0 + + +/* --------------------------------------------------------------------- */ + +/* 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; imaxl) + 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 */ + + +/* -------------------------- other misc functions ----------------------- */ + + +/* Learned from reading growisofs.c , + watching mkisofs, and viewing its results via od -c */ +/* @return 0=no size found , 1=*size_in_bytes is valid */ +int Scan_for_iso_size(unsigned char data[2048], double *size_in_bytes, + int flag) +{ + double sectors= 0.0; + + if(data[0]!=1) + return(0); + if(strncmp((char *) (data+1),"CD001",5)!=0) + return(0); + sectors= data[80] | (data[81]<<8) | (data[82]<<16) | (data[83]<<24); + *size_in_bytes= sectors*2048.0; + return(1); +} + + +int Set_descr_iso_size(unsigned char data[2048], double size_in_bytes, + int flag) +{ + unsigned int sectors, i; + + sectors= size_in_bytes/2048.0; + if(size_in_bytes>((double) sectors) * 2048.0) + sectors++; + for(i=0;i<4;i++) + data[87-i]= data[80+i]= (sectors >> (8*i)) & 0xff; + return(1); +} + + +int Wait_for_input(int fd, int microsec, int flag) +{ + struct timeval wt; + fd_set rds,wts,exs; + int ready; + + FD_ZERO(&rds); + FD_ZERO(&wts); + FD_ZERO(&exs); + FD_SET(fd,&rds); + FD_SET(fd,&exs); + wt.tv_sec= microsec/1000000; + wt.tv_usec= microsec%1000000; + ready= select(fd+1,&rds,&wts,&exs,&wt); + if(ready<=0) + return(0); + if(FD_ISSET(fd,&exs)) + return(-1); + if(FD_ISSET(fd,&rds)) + return(1); + return(0); +} + + +/* --------------------------------------------------------------------- */ + +/** 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->original_source_path[0]= 0; + o->source_fd= -1; + o->is_from_stdin= !!(flag&2); + o->fixed_size= 0.0; + o->tao_to_sao_tsize= 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->data_image_size= -1.0; + o->iso_fs_descr= NULL; + o->use_data_image_size= 0; + o->extracting_container= 0; + o->fifo_enabled= 0; + o->fifo= NULL; + o->fifo_outlet_fd= -1; + o->fifo_size= 0; + o->ff_fifo= NULL; + o->ff_idx= -1; + o->libburn_track= NULL; + + ret= Cdrskin_get_source(boss,o->source_path,&(o->fixed_size), + &(o->tao_to_sao_tsize),&(o->use_data_image_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; + strcpy(o->original_source_path,o->source_path); + if(o->fixed_size>0.0) + o->extracting_container= 1; + 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), + &fifo_start_at,0); + if(ret<=0) + goto failed; +#endif /* ! Cdrskin_extra_leaN */ + + 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); + if(track->iso_fs_descr!=NULL) + free((char *) track->iso_fs_descr); + 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. + bit1= size returns fixed_size, padding returns tao_to_sao_tsize +*/ +int Cdrtrack_get_size(struct CdrtracK *track, double *size, double *padding, + double *sector_size, int *use_data_image_size, int flag) +{ + + *size= track->fixed_size; + *padding= track->padding; + *use_data_image_size= track->use_data_image_size; +#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; + } else if(flag&2) + *padding= track->tao_to_sao_tsize; + +#endif + *sector_size= track->sector_size; + return(1); +} + + +int Cdrtrack_get_iso_fs_descr(struct CdrtracK *track, + char **descr, double *size, int flag) +{ + *descr= track->iso_fs_descr; + *size= track->data_image_size; + return(*descr != NULL && *size > 0.0); +} + + +int Cdrtrack_get_source_path(struct CdrtracK *track, + char **source_path, int *source_fd, int *is_from_stdin, int flag) +{ + *source_path= track->original_source_path; + *source_fd= track->source_fd; + *is_from_stdin= track->is_from_stdin; + return(1); +} + + +int Cdrtrack_get_fifo(struct CdrtracK *track, struct CdrfifO **fifo, int flag) +{ + *fifo= track->fifo; + return(1); +} + + +/** Try whether 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 +} + + +/* @param flag bit0=set *size_used as the detected data image size +*/ +int Cdrtrack_activate_image_size(struct CdrtracK *track, double *size_used, + int flag) +{ + if(flag&1) + track->data_image_size= *size_used; + else + *size_used= track->data_image_size; + if(track->use_data_image_size!=1) + return(2); + if(*size_used<=0) + return(0); + track->fixed_size= *size_used; + track->use_data_image_size= 2; + if(track->libburn_track!=NULL) { +#ifdef Cdrskin_libburn_has_track_set_sizE + burn_track_set_size(track->libburn_track, (off_t) *size_used); +#else + fprintf(stderr, + "cdrskin: SORRY : libburn version is too old for -isosize\n"); + return(0); +#endif + } + /* man cdrecord prescribes automatic -pad with -isosize. + cdrskin obeys only if the current padding is less than that. */ + if(track->padding<15*2048) { + track->padding= 15*2048; + track->set_by_padsize= 0; + } + track->extracting_container= 1; + +#ifndef Cdrskin_extra_leaN + if(track->ff_fifo!=NULL) + Cdrfifo_set_fd_in_limit(track->ff_fifo,track->fixed_size,track->ff_idx,0); +#endif + + return(1); +} + + +int Cdrtrack_seek_isosize(struct CdrtracK *track, int fd, int flag) +{ + struct stat stbuf; + char secbuf[2048]; + int ret,got,i; + double size; + + if(fstat(fd,&stbuf)==-1) + return(0); + if((stbuf.st_mode&S_IFMT)!=S_IFREG && (stbuf.st_mode&S_IFMT)!=S_IFBLK) + return(2); + + if(track->iso_fs_descr!=NULL) + free((char *) track->iso_fs_descr); + track->iso_fs_descr= TSOB_FELD(char,16*2048); + if(track->iso_fs_descr==NULL) + return(-1); + for(i=0;i<32 && track->data_image_size<=0;i++) { + for(got= 0; got<2048;got+= ret) { + ret= read(fd, secbuf+got, 2048-got); + if(ret<=0) + return(0); + } + if(i<16) + continue; + memcpy(track->iso_fs_descr+(i-16)*2048,secbuf,2048); + if(i>16) + continue; + ret= Scan_for_iso_size((unsigned char *) secbuf, &size, 0); + if(ret<=0) + break; + track->data_image_size= size; + if(track->use_data_image_size) { + Cdrtrack_activate_image_size(track,&size,1); + track->fixed_size= size; + track->use_data_image_size= 2; + } + } + ret= lseek(fd, (off_t) 0, SEEK_SET); + if(ret!=0) { + fprintf(stderr, + "cdrskin: FATAL : Cannot lseek() to 0 after -isosize determination\n"); + if(errno!=0) + fprintf(stderr, "cdrskin: errno=%d : %s\n", errno, strerror(errno)); + return(-1); + } + return(track->data_image_size>0); +} + + +/** Deliver an open file descriptor corresponding to the source path of track. + @param flag Bitfield for control purposes: + bit0=debugging verbosity + bit1=open as source for direct write: + no audio extract, no minimum track size + @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, ret; + off_t xtr_size= 0; + struct stat stbuf; +#ifdef Cdrskin_libburn_has_convert_fs_adR + char *device_adr,*raw_adr; + int no_convert_fs_adr; + int Cdrskin_get_device_adr(struct CdrskiN *skin, + char **device_adr, char **raw_adr, int *no_convert_fs_adr,int flag); + int Cdrskin_get_drive(struct CdrskiN *skin, struct burn_drive **drive, + int flag); +#ifdef Cdrskin_libburn_has_drive_equals_adR + struct burn_drive *drive; +#else + char adr[BURN_DRIVE_ADR_LEN]; +#endif +#endif /* Cdrskin_libburn_has_convert_fs_adR */ + + 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; + +#ifdef Cdrskin_libburn_has_convert_fs_adR + + Cdrskin_get_device_adr(track->boss,&device_adr,&raw_adr, + &no_convert_fs_adr,0); +/* + fprintf(stderr, + "cdrskin: DEBUG : device_adr='%s' , raw_adr='%s' , ncfs=%d\n", + device_adr, raw_adr, no_convert_fs_adr); +*/ + if(!no_convert_fs_adr) { + if(flag&1) + ClN(fprintf(stderr, + "cdrskin_debug: checking track source for identity with drive\n")); + +#ifdef Cdrskin_libburn_has_drive_equals_adR + + ret= Cdrskin_get_drive(track->boss,&drive,0); + if(ret<=0) { + fprintf(stderr, + "cdrskin: FATAL : Program error. Cannot determine libburn drive.\n"); + return(0); + } + if(burn_drive_equals_adr(drive,track->source_path,2)>0) { + + { + +#else /* Cdrskin_libburn_has_drive_equals_adR */ + + if(burn_drive_convert_fs_adr(track->source_path,adr)>0) { + +/* + fprintf(stderr,"cdrskin: DEBUG : track source '%s' -> adr='%s'\n", + track->source_path,adr); +*/ + if(strcmp(device_adr,adr)==0) { + +#endif /* ! Cdrskin_libburn_has_drive_equals_adR */ + + fprintf(stderr, + "cdrskin: FATAL : track source address leads to burner drive\n"); + fprintf(stderr, + "cdrskin: : dev='%s' -> '%s' <- track source '%s'\n", + raw_adr, device_adr, track->source_path); + return(0); + } + } + } +/* + fprintf(stderr,"cdrskin: EXPERIMENTAL : Deliberate abort\n"); + return(0); +*/ + +#endif /* Cdrskin_libburn_has_convert_fs_adR */ + + if(!(flag&2)) + 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->use_data_image_size==1 && xtr_size<=0) { + ret= Cdrtrack_seek_isosize(track,*fd,0); + if(ret==-1) + return(-1); + } else if(track->fixed_size<=0) { + + /* >>> ??? is it intentional that tsize overrides .wav header ? */ + if(xtr_size>0) { + + track->fixed_size= xtr_size; + if(track->use_data_image_size==1) + track->use_data_image_size= 2; /* count this as image size found */ + 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) && !(flag&2)) { + +#else + + if(track->fixed_size < Cdrtrack_minimum_sizE * track->sector_size && + !(flag&2)) { + +#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,flag&1); + 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 fifo_start_at, int flag) +{ + int ret,buffer_fill,buffer_space; + double data_image_size; + + if(track->fifo==NULL || fifo_start_at==0) + return(2); + if(fifo_start_at>0 && fifo_start_atfifo_size) + printf( + "cdrskin: NOTE : Input buffer will be initially filled up to %d bytes\n", + fifo_start_at); + printf("Waiting for reader process to fill input buffer ... "); + fflush(stdout); + ret= Cdrfifo_fill(track->fifo,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); + } + } + ret= Cdrfifo_get_iso_fs_size(track->fifo,&data_image_size,0); + if(ret>0) + track->data_image_size= data_image_size; + if(track->iso_fs_descr!=NULL) + free((char *) track->iso_fs_descr); + Cdrfifo_adopt_iso_fs_descr(track->fifo,&(track->iso_fs_descr),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,(flag&1)); + 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_default_size(tr, (off_t) track->tao_to_sao_tsize); + 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_libburn_write_mode_ruleS + +int Cdrtrack_activate_tao_tsize(struct CdrtracK *track, int flag) +{ + if(track->fixed_size<=0.0) + track->fixed_size= track->tao_to_sao_tsize; + return(track->fixed_size>0.0); +} + +#endif /* ! Cdrskin_libburn_write_mode_ruleS */ + + +int Cdrtrack_get_sectors(struct CdrtracK *track, int flag) +{ + return(burn_track_get_sectors(track->libburn_track)); +} + + +#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) +{ + int ready,ret; + char buf[2]; + + if(track->fifo_outlet_fd<=0) + return(0); + ready= Wait_for_input(track->fifo_outlet_fd, 0, 0); + 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]; + + /** Whether to wait for available standard input data before touching drives*/ + int do_waiti; + + /** 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; + + /** Whether to allow getuid()!=geteuid() */ + int allow_setuid; + + /** Whether to allow user provided addresses like #4 */ + int allow_fd_source; + + /** Whether to support media types which are implemented but yet untested */ + int allow_untested_media; + + /** Whether to allow libburn pseudo-drives "stdio:" . + 0=forbidden, 1=seems ok, + 2=potentially forbidden (depends on uid, euid, file type) + */ + int allow_emulated_drives; + + /** Whether an option is given which needs a full bus scan */ + int no_whitelist; + + /** Whether the translated device address shall not follow softlinks, device + clones and SCSI addresses */ + int no_convert_fs_adr; + + /** Whether 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; + + /** Whether bus scans shall exit!=0 if no drive was found */ + int scan_demands_drive; + + /** Whether to abort when a busy drive is encountered during bus scan */ + int abort_on_busy_drive; + + /** Linux specific : Whether to try to avoid collisions when opening drives */ + int drive_exclusive; + + /** Linux specific : Whether to obtain an exclusive drive lock via fcntl() */ + int drive_fcntl_f_setlk; + + /** Linux specific : Device file address family to use : + 0=default , 1=sr , 2=scd , 4=sg */ + int drive_scsi_dev_family; + + + /** Whether 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 */ + + /* The eventual name of a program to be executed if demands_cdrecord_caps + is >0 and demands_cdrskin_caps is <=0 + */ + char fallback_program[Cdrskin_strleN]; + int demands_cdrecord_caps; + int demands_cdrskin_caps; + + int result_fd; + +}; + + +/** 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->do_waiti= 0; + 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->allow_untested_media= 0; + o->allow_emulated_drives= 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_fcntl_f_setlk= 1; + o->drive_scsi_dev_family= 0; + 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 */ + + o->fallback_program[0]= 0; + o->demands_cdrecord_caps= 0; + o->demands_cdrskin_caps= 0; + o->result_fd = -1; + 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(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, major, minor, micro; + + ret= burn_initialize(); + if(ret==0) { + fprintf(stderr,"cdrskin: FATAL : Initialization of libburn failed\n"); + return(0); + } + burn_version(&major, &minor, µ); + + /* <<< for testing only */ + /* major= 0; minor= 3; micro= 6; */ + + if(majorqueue_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); +} + + +/** Evaluate whether the user would be allowed in any case to use device_adr + as pseudo-drive */ +int Cdrpreskin__allows_emulated_drives(char *device_adr, char reason[4096], + int flag) +{ + struct stat stbuf; + + reason[0]= 0; + if(device_adr[0]) { + if(strcmp(device_adr,"/dev/null")==0) + return(1); + strcat(reason,"File object is not /dev/null. "); + } + + if(getuid()!=geteuid()) { + strcat(reason,"UID and EUID differ"); + return(0); + } + if(getuid()!=0) + return(1); + + strcat(reason,"UID is 0. "); + /* Directory must be owned by root and write protected against any others*/ + if(lstat("/root/cdrskin_permissions",&stbuf)==-1 || !S_ISDIR(stbuf.st_mode)) { + strcat(reason, "No directory /root/cdrskin_permissions exists"); + return(0); + } + if(stbuf.st_uid!=0) { + strcat(reason, "Directory /root/cdrskin_permissions not owned by UID 0"); + return(0); + } + if(stbuf.st_mode & (S_IWGRP | S_IWOTH)) { + strcat(reason, + "Directory /root/cdrskin_permissions has w-permission for group or others"); + return(0); + } + if(stat("/root/cdrskin_permissions/allow_emulated_drives",&stbuf)==-1) { + strcat(reason, + "No file /root/cdrskin_permissions/allow_emulated_drives exists"); + return(0); + } + reason[0]= 0; + return(1); +} + + +int Cdrpreskin_consider_normal_user(int flag) +{ + fprintf(stderr, + "cdrskin: HINT : Consider to allow rw-access to the writer devices and\n"); + fprintf(stderr, + "cdrskin: HINT : to run cdrskin under your normal user identity.\n"); + return(1); +} + + +/* Start the fallback program as replacement of the cdrskin run. + @param flag bit0=do not report start command +*/ +int Cdrpreskin_fallback(struct CdrpreskiN *preskin, int argc, char **argv, + int flag) +{ + char **hargv= NULL; + int i, wp= 1; + char *ept, *upt; + + if(getuid()!=geteuid() && !preskin->allow_setuid) { + fprintf(stderr, + "cdrskin: SORRY : uid and euid differ. Will not start external fallback program.\n"); + Cdrpreskin_consider_normal_user(0); + fprintf(stderr, + "cdrskin: HINT : Option --allow_setuid disables this safety check.\n"); + goto failure; + } + if(!(flag&1)) { + fprintf(stderr,"cdrskin: --------------------------------------------------------------------\n"); + fprintf(stderr,"cdrskin: Starting fallback program:\n"); + } + hargv= TSOB_FELD(char *,argc+1); + if(hargv==NULL) + goto failure; + hargv[0]= strdup(preskin->fallback_program); + if(argv[0]==NULL) + goto failure; + if(!(flag&1)) + fprintf(stderr," %s", hargv[0]); + for(i= 1; i 0 ? strerror(errno) : "unidentified error")); + exit(15); +} + + +/** 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); + if(strncmp(adr,"stdio:",6)==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); +} + + +/** Set the eventual output fd for the result of Cdrskin_msinfo() +*/ +int Cdrpreskin_set_result_fd(struct CdrpreskiN *o, int result_fd, int flag) +{ + o->result_fd= result_fd; + 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; + + filenames_v= TSOB_FELD(char *, o->rc_filename_count+1); + if(filenames_v==NULL) + return(-1); + 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); + free((char *) filenames_v); + 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, reason[4096]; + +#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); + } + + /* The two predefined fallback personalities are triggered by the progname */ + value_pt= strrchr(argv[0],'/'); + if(value_pt==NULL) + value_pt= argv[0]; + else + value_pt++; + if(strcmp(value_pt,"unicord")==0) + strcpy(o->fallback_program,"cdrecord"); + else if(strcmp(value_pt,"codim")==0) + strcpy(o->fallback_program,"wodim"); + + for (i= 1;iabort_handler= 3; + + } else if(strcmp(argv[i],"--allow_emulated_drives")==0) { + if(Cdrpreskin__allows_emulated_drives("",reason,0)<=0) { + fprintf(stderr,"cdrskin: WARNING : %s.\n",reason); + fprintf(stderr, + "cdrskin: WARNING : Only /dev/null will be available with \"stdio:\".\n"); + Cdrpreskin_consider_normal_user(0); + o->allow_emulated_drives= 2; + } else + o->allow_emulated_drives= 1; + + } else if(strcmp(argv[i],"--allow_setuid")==0) { + o->allow_setuid= 1; + + } else if(strcmp(argv[i],"--allow_untested_media")==0) { + o->allow_untested_media= 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"); + fprintf(stderr, + "\tdeformat_sequential\tfully blank, even formatted DVD-RW\n"); + fprintf(stderr, + "\tdeformat_sequential_quickest\tminimally blank, even 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; + o->demands_cdrskin_caps= 1; + + } else if(strcmp(argv[i],"--devices")==0) { +#ifndef Cdrskin_extra_leaN + 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"); +#endif /* ! Cdrskin_extra_leaN */ + + o->no_whitelist= 1; + o->demands_cdrskin_caps= 1; + + } else if(strncmp(argv[i],"dev_translation=",16)==0) { + o->demands_cdrskin_caps= 1; + +#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("\nSupported 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"); + } + if(o->allow_emulated_drives) { + fprintf(stderr,"\nTransport name:\t\tlibburn on standard i/o\n"); + if(o->allow_emulated_drives==2) + fprintf(stderr, "Transport descr.:\troot or setuid may only write into /dev/null\n"); + else + fprintf(stderr, "Transport descr.:\twrite into file objects\n"); + fprintf(stderr,"Transp. layer ind.:\tstdio:\n"); + fprintf(stderr,"Target specifier:\tpath\n"); + fprintf(stderr,"Target example:\t\tstdio:/tmp/pseudo_drive\n"); + fprintf(stderr,"SCSI Bus scanning:\tnot supported\n"); + fprintf(stderr,"Open via UNIX device:\tnot supported\n"); + } else { + if(Cdrpreskin__allows_emulated_drives("",reason,0)>0) + printf("\ncdrskin: NOTE : Option --allow_emulated_drives would allow dev=stdio:\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_f_setlk")==0) { + o->drive_fcntl_f_setlk= 1; + + } else if(strcmp(argv[i],"--drive_not_exclusive")==0) { + o->drive_exclusive= 0; + o->drive_fcntl_f_setlk= 0; + + } else if(strcmp(argv[i],"--drive_not_f_setlk")==0) { + o->drive_fcntl_f_setlk= 0; + + } else if(strcmp(argv[i],"--drive_not_o_excl")==0) { + o->drive_exclusive= 0; + + } else if(strncmp(argv[i],"drive_scsi_dev_family=",22)==0) { + value_pt= argv[i]+22; + if(strcmp(value_pt,"sr")==0) + o->drive_scsi_dev_family= 1; + else if(strcmp(value_pt,"scd")==0) + o->drive_scsi_dev_family= 2; + else if(strcmp(value_pt,"sg")==0) + o->drive_scsi_dev_family= 4; + else + o->drive_scsi_dev_family= 0; + } else if(strcmp(argv[i],"--drive_scsi_exclusive")==0) { + o->drive_exclusive= 2; + o->demands_cdrskin_caps= 1; + + } 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"); +#ifdef Cdrskin_libburn_has_get_best_speeD + printf( + " --adjust_speed_to_drive set only speeds offered by drive and media\n"); +#endif + printf(" --allow_emulated_drives dev=stdio: on file objects\n"); + printf( + " --allow_setuid disable setuid warning (setuid is insecure !)\n"); + printf( + " --allow_untested_media enable implemented untested media types\n"); + printf( + " --any_track allow source_addresses to match '^-.' or '='\n"); + printf( + " assert_write_lba= abort if not next write address == lba\n"); + printf( + " direct_write_amount= write random access to media like DVD+RW\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_f_setlk obtain exclusive lock via fcntl.\n"); + printf(" --drive_not_exclusive combined not_o_excl and not_f_setlk.\n"); + printf(" --drive_not_f_setlk do not obtain exclusive lock via fcntl.\n"); + printf(" --drive_not_o_excl do not ask kernel to prevent opening\n"); + printf(" busy drives. Effect is kernel dependend.\n"); + printf( + " drive_scsi_dev_family= select Linux device\n"); + printf(" file family to be used for (pseudo-)SCSI.\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( + " fallback_program= use external program for exotic CD jobs\n"); + 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"); +#ifdef Cdrskin_libburn_has_set_filluP + printf(" --fill_up_media cause the last track to have maximum size\n"); +#endif + printf( + " grab_drive_and_wait= grab drive, wait given number of\n"); + printf( + " seconds, release drive, and do normal work\n"); + printf( + " --grow_overwriteable_iso emulate multi-session on media like DVD+RW\n"); + printf( + " --ignore_signals try to ignore any signals rather than to abort\n"); + printf(" --list_ignored_options list all ignored cdrecord options.\n"); +#ifdef Cdrskin_libburn_has_set_waitinG + printf(" modesty_on_drive= no writing into full drive buffer\n"); +#endif + 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(" --prodvd_cli_compatible react on some DVD types more like\n"); + printf(" cdrecord-ProDVD with blank= and -multi\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 + +#ifdef Cdrskin_libburn_has_get_spacE + printf( + " --tell_media_space prints maximum number of writeable data blocks\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-project.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-inq\t\tdo an inquiry for the drive and exit\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,"\t-format\t\tformat a CD-RW/DVD-RW/DVD+RW disc\n"); + fprintf(stderr, + "\tfs=#\t\tSet fifo size to # (0 to disable, default is 4 MB)\n"); + fprintf(stderr, + "\t-load\t\tload the disk and exit (works only with tray loader)\n"); + fprintf(stderr, + "\t-lock\t\tload and lock the disk and exit (works only with tray loader)\n"); + fprintf(stderr, + "\t-eject\t\teject the disk after doing the work\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_set_waitinG + fprintf(stderr, + "\tminbuf=percent\tset lower limit for drive buffer modesty\n"); +#endif +#ifdef Cdrskin_libburn_has_multI + fprintf(stderr, + "\t-multi\t\tgenerate a TOC that allows multi session\n"); +#endif + fprintf(stderr, + "\t-waiti\t\twait until input is available before opening SCSI\n"); + fprintf(stderr, + "\t-immed\t\tTry to use the SCSI IMMED flag with certain long lasting commands\n"); + 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-isosize\tUse iso9660 file system size for next data track\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(strncmp(argv[i],"fallback_program=",17)==0) { + strcpy(o->fallback_program,argv[i]+17); + + } 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; + o->demands_cdrskin_caps= 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)++; + ClN(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) { + int major, minor, micro; + + printf( +"Cdrecord 2.01-Emulation Copyright (C) 2006-2007, see libburnia-project.org\n"); + printf("libburn interface : %s\n",Cdrskin_libburn_versioN); + burn_version(&major, &minor, µ); + printf("libburn in use : %d.%d.%d\n", major, minor, micro); + +#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); + if(o->fallback_program[0]) { + char *hargv[2]; + + printf("Fallback program : %s\n",o->fallback_program); + printf("Fallback version :\n"); + hargv[0]= argv[0]; + hargv[1]= "-version"; + Cdrpreskin_fallback(o,2,hargv,1); /* dirty never come back */ + } + {ret= 2; goto ex;} + + } else if(strcmp(argv[i],"-waiti")==0) { + o->do_waiti= 1; + + } + + } + ret= 1; +final_checks:; + if(flag&1) + goto ex; + + if(o->do_waiti) { + fprintf(stderr, + "cdrskin: Option -waiti pauses program until input appears at stdin\n"); + printf("Waiting for data on stdin...\n"); + for(ret= 0; ret==0; ) + ret= Wait_for_input(0,1000000,0); + if(ret<0 || feof(stdin)) + fprintf(stderr, + "cdrskin: NOTE : stdin produces exception rather than data\n"); + fprintf(stderr,"cdrskin: Option -waiti pausing is done.\n"); + } + +#ifndef Cdrskin_libburn_no_burn_preset_device_opeN + burn_preset_device_open(o->drive_exclusive +#ifdef Cdrskin_libburn_preset_device_familY + | (o->drive_scsi_dev_family<<2) + | ((!!o->drive_fcntl_f_setlk)<<5) +#endif + , + o->drive_blocking, + o->abort_on_busy_drive); +#endif /* ! Cdrskin_libburn_no_burn_preset_device_opeN */ + + if(strlen(o->raw_device_adr)>0 && !o->no_whitelist) { + int driveno,hret; + char *adr,buf[Cdrskin_adrleN]; + + if(strcmp(o->raw_device_adr,"stdio:-")==0) { + fprintf(stderr, + "cdrskin: SORRY : Cannot accept drive address \"stdio:-\".\n"); + fprintf(stderr, + "cdrskin: HINT : Use \"stdio:/dev/fd/1\" if you really want to write to stdout.\n"); + {ret= 0; goto ex;} + } + 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", + (int) 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 */ + + } + +#ifdef Cdrskin_libburn_has_allow_untested_profileS + burn_allow_untested_profiles(!!o->allow_untested_media); +#endif + + /* A60927 : note to myself : no "ret= 1;" here. It breaks --help , -version */ + +ex:; + /* Eventually replace current stdout by dup(1) from start of program */ + if(strcmp(o->device_adr,"stdio:/dev/fd/1")==0 && o->result_fd >= 0) + sprintf(o->device_adr,"stdio:/dev/fd/%d",o->result_fd); + +#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 +*/ + + +/** <<< Hopefully obsolete: + 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 +*/ +#define Cdrskin_tracksize_maX 1024.0*1024.0*1024.0*1024.0 + + +/* 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 adjust_speed_to_drive; + int gracetime; + int dummy_mode; + int force_is_set; + int single_track; + int prodvd_cli_compatible; + + int do_devices; + + int do_scanbus; + + int do_load; + + int do_checkdrive; + + int do_msinfo; + 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=deformat_sequential (blank_fast might matter) + 3=format (= format_overwrite restricted to DVD+RW) + */ + double blank_format_size; /* to be used with burn_disc_format() */ + + int do_direct_write; + int do_burn; + int tell_media_space; /* actually do not burn but tell the available space */ + 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; + int modesty_on_drive; + int min_buffer_percent; + int max_buffer_percent; + + double write_start_address; + double direct_write_amount; + int assert_write_lba; + + 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 smallest_tsize; + int has_open_ended_track; + double padding; + int set_by_padsize; + int fill_up_media; + + /** 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; + /** The persistent drive address of that drive */ + char device_adr[Cdrskin_adrleN]; + + + /** Progress state info: whether 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; /* Whether drive was told to do something cancel-worthy */ + struct burn_drive *grabbed_drive; + +#ifndef Cdrskin_extra_leaN + /** Abort test facility */ + double abort_after_bytecount; +#endif /* ! Cdrskin_extra_leaN */ + + + /** Some intermediate option info which is stored until setup finalization */ + double tao_to_sao_tsize; + int stdin_source_used; + + /* Info about media capabilities */ + int media_does_multi; + int media_is_overwriteable; + + /* For option -isosize and --grow_overwriteable_iso */ + int use_data_image_size; + + /* For growisofs stunt : + 0=disabled, + 1=do stunt, fabricate toc, allow multi, + 2=overwriteable_iso_head is valid + 3=initial session (mostly to appease -multi on overwriteables) + */ + int grow_overwriteable_iso; + /* New image head buffer for --grow_overwriteable_iso */ + char overwriteable_iso_head[32*2048]; /* block 0 to 31 of target */ + +}; + +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); +int Cdrskin_report_disc_status(struct CdrskiN *skin, enum burn_disc_status s, + 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->adjust_speed_to_drive= 0; + o->gracetime= 0; + o->dummy_mode= 0; + o->force_is_set= 0; + o->single_track= 0; + o->prodvd_cli_compatible= 0; + o->do_devices= 0; + o->do_scanbus= 0; + o->do_load= 0; + o->do_checkdrive= 0; + o->do_msinfo= 0; + 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_direct_write= 0; + o->do_burn= 0; + o->tell_media_space= 0; + o->write_type= BURN_WRITE_SAO; + o->block_type= BURN_BLOCK_SAO; + o->multi= 0; + o->modesty_on_drive= 0; + o->min_buffer_percent= 65; + o->max_buffer_percent= 95; + o->write_start_address= -1.0; + o->direct_write_amount= -1.0; + o->assert_write_lba= -1; + o->burnfree= 1; + o->do_eject= 0; + o->eject_device[0]= 0; + o->source_path[0]= 0; + o->fixed_size= 0.0; + o->smallest_tsize= -1.0; + o->has_open_ended_track= 0; + o->padding= 0.0; + o->set_by_padsize= 0; + o->fill_up_media= 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->device_adr[0]= 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; +#ifndef Cdrskin_extra_leaN + o->abort_after_bytecount= -1.0; +#endif /* ! Cdrskin_extra_leaN */ + o->tao_to_sao_tsize= 0.0; + o->stdin_source_used= 0; + o->use_data_image_size= 0; + o->media_does_multi= 0; + o->media_is_overwriteable= 0; + o->grow_overwriteable_iso= 0; + memset(o->overwriteable_iso_head,0,sizeof(o->overwriteable_iso_head)); + +#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); +} + + +/** Return the addresses of the drive. device_adr is the libburn persistent + address of the drive, raw_adr is the address as given by the user. +*/ +int Cdrskin_get_device_adr(struct CdrskiN *skin, + char **device_adr, char **raw_adr, int *no_convert_fs_adr, int flag) +{ + burn_drive_get_adr(&skin->drives[skin->driveno],skin->device_adr); + *device_adr= skin->device_adr; + *raw_adr= skin->preskin->raw_device_adr; + *no_convert_fs_adr= skin->preskin->no_convert_fs_adr; + return(1); +} + + +int Cdrskin_get_drive(struct CdrskiN *skin, struct burn_drive **drive,int flag) +{ + if(skin->driveno<0 || skin->driveno >= skin->n_drives) + return(0); + *drive= skin->drives[skin->driveno].drive; + return ((*drive) != NULL); +} + + +/** Return information about current track source */ +int Cdrskin_get_source(struct CdrskiN *skin, char *source_path, + double *fixed_size, double *tao_to_sao_tsize, + int *use_data_image_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; + *tao_to_sao_tsize = skin->tao_to_sao_tsize; + *use_data_image_size= skin->use_data_image_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],skin->fifo_start_at,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, modesty= 0; + + 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. */ +#ifdef Cdrskin_libburn_has_get_best_speeD + k_speed= -1; +#else + k_speed= Cdrskin_libburn_speed_factoR+Cdrskin_libburn_speed_addoN; +#endif + } 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)); + +#ifdef Cdrskin_libburn_has_get_best_speeD + if(skin->adjust_speed_to_drive && !skin->force_is_set) { + struct burn_speed_descriptor *best_descr; + burn_drive_get_best_speed(skin->drives[skin->driveno].drive,k_speed, + &best_descr,0); + if(best_descr!=NULL) { + k_speed= best_descr->write_speed; + skin->x_speed = ((double) k_speed) / Cdrskin_libburn_speed_factoR; + } + } +#endif /* Cdrskin_libburn_has_get_best_speeD */ + + burn_drive_set_speed(skin->drives[skin->driveno].drive,k_speed,k_speed); + +#ifdef Cdrskin_libburn_has_set_waitinG + modesty= skin->modesty_on_drive; + burn_drive_set_buffer_waiting(skin->drives[skin->driveno].drive, + modesty, -1, -1, -1, + skin->min_buffer_percent, + skin->max_buffer_percent); +#endif + return(1); +} + + +int Cdrskin_determine_media_caps(struct CdrskiN *skin, int flag) +{ +#ifdef Cdrskin_libburn_has_get_multi_capS + int ret; + struct burn_multi_caps *caps = NULL; + + skin->media_is_overwriteable= skin->media_does_multi= 0; + ret= burn_disc_get_multi_caps(skin->grabbed_drive,BURN_WRITE_NONE,&caps,0); + if(ret<=0) + return(0); + skin->media_is_overwriteable= !!caps->start_adr; + skin->media_does_multi= !!caps->multi_session; + return(1); +#else /* Cdrskin_libburn_has_get_multi_capS */ + return(-1); +#endif +} + + +/** 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 + bit3= demand and evtl. report media, return 0 if none to see + bit4= grab drive with unsuitable media even if fallback program + @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]; + enum burn_disc_status s; +#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; + + s= burn_disc_get_status(drive); + if((flag&8)) { + if(skin->verbosity>=Cdrskin_verbose_progresS) + Cdrskin_report_disc_status(skin,s,1); + if(s==BURN_DISC_EMPTY) { + Cdrskin_release_drive(skin,0); + fprintf(stderr,"cdrskin: SORRY : No media found in drive\n"); + ret= 0; goto ex; + } + } + + 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 || + strstr(profile_name,"stdio")==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 */ + if(skin->preskin->fallback_program[0] && s==BURN_DISC_UNSUITABLE && + skin->preskin->demands_cdrskin_caps<=0 && !(flag&16)) { + skin->preskin->demands_cdrecord_caps= 1; + fprintf(stderr, + "cdrskin: NOTE : Will delegate job to fallback program '%s'.\n", + skin->preskin->fallback_program); + Cdrskin_release_drive(skin,0); + ret= 0; goto ex; + } + + Cdrskin_determine_media_caps(skin,0); + + 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 + bit1= leave tray locked (eventually overrides bit0) +*/ +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); + } + if(flag&2) + burn_drive_leave_locked(skin->grabbed_drive,0); + else + 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(strncmp(adr, "stdio:", 6)==0) { + if(skin->n_drives<=0) + goto wrong_devno; + *driveno= 0; + return(1); + } else 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) + ClN(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) { + ClN(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, 3 stdio, + 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,k_start; + 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_get_drive_rolE + ret= burn_drive_get_drive_role(skin->drives[driveno].drive); + if(ret!=1) { + sprintf(btldev,"stdio:%s",adr); + {ret= 2; goto adr_translation;} + } +#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 + + k_start= 0; + if(strncmp(loc,"/dev/sg",7)==0 || strncmp(loc,"/dev/sr",7)==0) + k_start= 7; + if(strncmp(loc,"/dev/scd",8)==0) + k_start= 8; + if(k_start>0) { + for(k= k_start;loc[k]!=0;k++) + if(loc[k]<'0' || loc[k]>'9') + break; + if(loc[k]==0 && k>k_start) { + sprintf(btldev,"1,%s,0",loc+k_start); + {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 whether 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); +} + + +/** Read and buffer the start of an existing ISO-9660 image from + overwriteable target media. +*/ +int Cdrskin_overwriteable_iso_size(struct CdrskiN *skin, int *size, int flag) +{ +#ifdef Cdrskin_libburn_has_random_access_rW + int ret; + off_t data_count= 0; + double size_in_bytes; + char *buf; + + buf= skin->overwriteable_iso_head; + if(!skin->media_is_overwriteable) + {ret= 0; goto ex;} + /* Read first 64 kB */ + ret= burn_read_data(skin->grabbed_drive,(off_t) 0,buf,32*2048,&data_count,0); + if(ret<=0) + {ret= 0; goto ex;} + ret= Scan_for_iso_size((unsigned char *) (buf+16*2048), &size_in_bytes,0); + if(ret<=0) { + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin_debug: No detectable ISO-9660 size on media\n")); + {ret= 0; goto ex;} + } + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin_debug: detected ISO-9660 size : %.f (%fs)\n", + size_in_bytes, size_in_bytes/2048.0)); + if(size_in_bytes/2048.0>2147483647-1-16) { + fprintf(stderr, + "cdrskin: FATAL : ISO-9660 filesystem in terabyte size detected\n"); + {ret= 0; goto ex;} + } + *size= size_in_bytes/2048.0; + if(size_in_bytes-((double) *size)*2048.0>0.0) + (*size)++; + if((*size)%16) + *size+= 16-((*size)%16); + if(skin->grow_overwriteable_iso==1) + skin->grow_overwriteable_iso= 2; + ret= 1; +ex:; + return(ret); + +#else /* Cdrskin_libburn_has_random_access_rW */ + return(-1); +#endif +} + + +int Cdrskin_invalidate_iso_head(struct CdrskiN *skin, int flag) +{ + int ret; + int size; + + fprintf(stderr, + "cdrskin: blank=... : invalidating ISO-9660 head on overwriteable media\n"); + ret= Cdrskin_overwriteable_iso_size(skin,&size,0); + if(ret<=0) { + fprintf(stderr, + "cdrskin: NOTE : Not an ISO-9660 file system. Left unaltered.\n"); + return(2); + } + skin->overwriteable_iso_head[16*2048]= + skin->overwriteable_iso_head[16*2048+3]= + skin->overwriteable_iso_head[16*2048+4]= 'x'; + ret= burn_random_access_write(skin->grabbed_drive,(off_t) 16*2048, + skin->overwriteable_iso_head+16*2048, + (off_t) 16*2048,1); + return(ret); +} + + +/** Report media status s to the user + @param flag Bitfield for control purposes: + bit0= permission to check for overwriteable ISO image + bit1= do not report media profile +*/ +int Cdrskin_report_disc_status(struct CdrskiN *skin, enum burn_disc_status s, + int flag) +{ + int ret, iso_size; + + if(flag&1) { + if(skin->media_is_overwriteable && skin->grow_overwriteable_iso>0) { + if(skin->grow_overwriteable_iso==2) + ret= 1; + else + ret= Cdrskin_overwriteable_iso_size(skin,&iso_size,0); + if(ret>0) + s= BURN_DISC_APPENDABLE; + } + } + + 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"); + + if(flag&2) + return(1); + +#ifdef Cdrskin_libburn_has_get_profilE + if((s==BURN_DISC_FULL || s==BURN_DISC_APPENDABLE || s==BURN_DISC_BLANK || + s==BURN_DISC_UNSUITABLE) && skin->driveno>=0) { + char profile_name[80]; + int profile_number; + + printf("Current: "); + ret= burn_disc_get_profile(skin->drives[skin->driveno].drive, + &profile_number,profile_name); + if(ret>0 && profile_name[0]!=0) + printf("%s\n", profile_name); + else if(ret>0) + printf("UNSUITABLE MEDIA (Profile %4.4Xh)\n",profile_number); + else + printf("-unidentified-\n"); + } else if(s==BURN_DISC_EMPTY) { + printf("Current: none\n"); + } +#endif + + 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)) { + if(flag&2) + ClN(printf("cdrskin: pseudo-inquiry on drive %d\n",skin->driveno)); + else + ClN(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 : "); +#ifdef Cdrskin_libburn_has_get_drive_rolE + ret= burn_drive_get_drive_role(drive_info->drive); + if(ret==0) + printf("%s\n","Emulated (null-drive)"); + else if(ret==2) + printf("%s\n","Emulated (stdio-drive, 2k random read-write)"); + else if(ret==3) + printf("%s\n","Emulated (stdio-drive, sequential write-only)"); + else if(ret!=1) + printf("%s\n","Emulated (stdio-drive)"); + else +#endif + printf("%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); + + if(flag&2) + {ret= 1; goto ex;} + + printf("Driver flags : %s\n","BURNFREE"); +#ifdef Cdrskin_allow_libburn_taO + + printf("Supported modes:"); + if((drive_info->tao_block_types & (BURN_BLOCK_MODE1)) + == (BURN_BLOCK_MODE1)) + 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); +} + + +/** Predict address block number where the next write will go to + @param flag Bitfield for control purposes: + bit0= do not return nwa from eventual write_start_address + @return <=0 error, 1 nwa from drive , 2 nwa from write_start_address +*/ +int Cdrskin_obtain_nwa(struct CdrskiN *skin, int *nwa, int flag) +{ + int ret,lba; + struct burn_drive *drive; + struct burn_write_opts *o= NULL; + + if(skin->write_start_address>=0 && !(flag&1)) { + /* (There is no sense in combining random addressing with audio) */ + *nwa= skin->write_start_address/2048; + return(2); + } + + /* 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); +} + + +/** 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, pmin, psec, pframe, ret; + 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) { + if(skin->grow_overwriteable_iso>0) { + ret= Cdrskin_overwriteable_iso_size(skin,&lba,0); + if(ret>0) { + printf( +"first: 1 last 1 (fabricated from ISO-9660 image on overwriteable media)\n"); + printf( +"track: 1 lba: 0 ( 0) 00:02:00 adr: 1 control: 4 mode: 1\n"); + burn_lba_to_msf(lba, &pmin, &psec, &pframe); + printf("track:lout lba: %9d (%9d) %2.2d:%2.2d:%2.2d", + lba,4*lba,pmin,psec,pframe); + printf(" adr: 1 control: 4 mode: -1\n"); + return(1); + } + } + 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; + s= burn_disc_get_status(drive); + Cdrskin_report_disc_status(skin,s,1|2); + 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;} + } +#ifdef Cdrskin_libburn_has_allow_untested_profileS + burn_allow_untested_profiles(!!skin->preskin->allow_untested_media); +#endif + 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;} + } +#ifdef Cdrskin_libburn_has_allow_untested_profileS + burn_allow_untested_profiles(!!skin->preskin->allow_untested_media); +#endif + 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 + +/* A70324: proposal by Eduard Bloch */ +int Cdrskin_warn_of_mini_tsize(struct CdrskiN *skin, int flag) +{ + off_t media_space= 0; + enum burn_disc_status s; + struct burn_drive *drive; + +#ifdef Cdrskin_libburn_has_get_spacE + if(skin->multi || skin->has_open_ended_track || skin->smallest_tsize<0) + return(1); + drive= skin->drives[skin->driveno].drive; + s= burn_disc_get_status(drive); + if(s!=BURN_DISC_BLANK) + return(1); + media_space= burn_disc_available_space(drive, NULL); + if(media_space<=0 || + skin->smallest_tsize >= media_space / Cdrskin_minimum_tsize_quotienT) + return(1); + fprintf(stderr,"\n"); + fprintf(stderr,"\n"); + fprintf(stderr, + "cdrskin: WARNING: Very small track size set by option tsize=\n"); + fprintf(stderr, + "cdrskin: Track size %.1f MB <-> media capacity %.1f MB\n", + skin->smallest_tsize/1024.0/1024.0, + ((double) media_space)/1024.0/1024.0); + fprintf(stderr,"\n"); + fprintf(stderr, + "cdrskin: Will wait at least 15 seconds until real burning starts\n"); + fprintf(stderr,"\n"); + if(skin->gracetime<15) + skin->gracetime= 15; + +#endif /* Cdrskin_libburn_has_get_spacE */ + return(1); +} + + +/** 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; + + Cdrskin_warn_of_mini_tsize(skin,0); + + 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= "blank", *presperf="blanking", *fmt_text= "..."; + char profile_name[80]; + static char fmtp[][40]= { + "format_default", "format_overwrite", "deformat_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; + s= burn_disc_get_status(drive); + 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,1); + do_format= skin->blank_format_type & 0xff; + if(do_format == 1 || do_format == 3) { + verb= "format"; + presperf= "formatting"; + } + +#ifdef Cdrskin_libburn_has_pretend_fulL + if(s==BURN_DISC_UNSUITABLE) { + if(skin->force_is_set) { + ClN(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==2) { + /* Forceful blanking to Sequential Recording for DVD-R[W] and CD-RW */ + + if(!(profile_number == 0x14 || profile_number == 0x13 || + profile_number == 0x0a)) { + if(skin->grow_overwriteable_iso>0 && skin->media_is_overwriteable) + goto pseudo_blank_ov; + else + goto unsupported_format_type; + } + + } else if(do_format==1 || do_format==3) { + /* Formatting to become overwriteable for DVD-RW and DVD+RW */ + + if(do_format==3 && profile_number != 0x1a) { + fprintf(stderr, "cdrskin: SORRY : -format does DVD+RW only\n"); + if(profile_number==0x14) + fprintf(stderr, + "cdrskin: HINT : blank=format_overwrite would format this media\n"); + {ret= 0; goto ex;} + } + + if(profile_number == 0x14) { /* DVD-RW sequential */ + /* ok */; + } else if(profile_number == 0x13) { /* DVD-RW restricted overwrite */ + if(!(skin->force_is_set || ((skin->blank_format_type>>8)&4))) { + 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"); + {ret= 2; goto ex;} + } + } else if(profile_number == 0x1a) { /* DVD+RW */ + 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"); + {ret= 2; goto ex;} + } + } else { + fprintf(stderr, + "cdrskin: SORRY : blank=%s for now does DVD+/-RW only\n",fmt_text); + {ret= 0; goto ex;} + } + if(s==BURN_DISC_UNSUITABLE) + fprintf(stderr, + "cdrskin: NOTE : blank=%s accepted not yet suitable media\n", + fmt_text); + + } else if(do_format==0) { + /* Classical blanking of erasable media */ + + if(skin->grow_overwriteable_iso>0 && skin->media_is_overwriteable) { +pseudo_blank_ov:; + if(skin->dummy_mode) { + fprintf(stderr, + "cdrskin: would have begun to pseudo-blank disc if not in -dummy mode\n"); + goto blanking_done; + } + skin->grow_overwriteable_iso= 3; + ret= Cdrskin_invalidate_iso_head(skin, 0); + if(ret<=0) + goto ex; + goto blanking_done; + } else if(s!=BURN_DISC_FULL && + (s!=BURN_DISC_APPENDABLE || skin->no_blank_appendable) && + (profile_number!=0x13 || !skin->prodvd_cli_compatible) && + (s!=BURN_DISC_BLANK || !skin->force_is_set)) { + Cdrskin_release_drive(skin,0); + if(s==BURN_DISC_BLANK) { + fprintf(stderr, + "cdrskin: NOTE : blank=... : media was already blank (and still is)\n"); + {ret= 2; goto ex;} + } 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"); + } + {ret= 0; goto ex;} + } + if(!burn_disc_erasable(drive)) { + fprintf(stderr,"cdrskin: FATAL : blank=... : media is not erasable\n"); + {ret= 0; goto ex;} + } + if((profile_number == 0x14 || profile_number == 0x13) && + !skin->prodvd_cli_compatible) + skin->blank_fast= 0; /* only with deformat_sequential_quickest */ + + } else { +unsupported_format_type:; + fprintf(stderr, + "cdrskin: SORRY : blank=%s is unsupported with media type %s\n", + fmt_text, profile_name); + {ret= 0; goto ex;} + } + + 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==1 || do_format==3)); +#endif /* ! Cdrskin_extra_leaN */ + + skin->drive_is_busy= 1; + if(do_format==0 || do_format==2) { + burn_disc_erase(drive,skin->blank_fast); + +#ifdef Cdrskin_libburn_has_burn_disc_formaT + } else if(do_format==1 || do_format==3) { + 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==1 || do_format==3?"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; + if(skin->drive_is_grabbed) + 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,use_data_image_size; + 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,&use_data_image_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,&use_data_image_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 || (skin->fill_up_media && + skin->supposed_track_idx==skin->track_counter-1)) { + sprintf(mb_text,"%4d of %4d",(int) (written_total_bytes/1024.0/1024.0), + (int) ((double) Cdrtrack_get_sectors( + skin->tracklist[skin->supposed_track_idx],0)* + sector_size/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); +} + + +#ifdef Cdrskin_libburn_write_mode_ruleS + +/** After everything else about burn_write_opts and burn_disc is set up, this + call determines the effective write mode and checks whether the drive + promises to support it. +*/ +int Cdrskin_activate_write_mode(struct CdrskiN *skin, + struct burn_write_opts *opts, + struct burn_disc *disc, + int flag) +{ + int profile_number= -1, current_is_cd= 1, ret, was_still_default= 0; + char profile_name[80], reasons[BURN_REASONS_LEN]; + enum burn_disc_status s= BURN_DISC_UNGRABBED; + enum burn_write_types wt; + + profile_name[0]= 0; + if(skin->grabbed_drive) { + burn_disc_get_profile(skin->grabbed_drive,&profile_number,profile_name); + s= burn_disc_get_status(skin->grabbed_drive); + } + if(profile_number!=0x09 && profile_number!=0x0a) + current_is_cd= 0; + if(strcmp(skin->preskin->write_mode_name,"DEFAULT")==0) { + was_still_default= 1; + wt= burn_write_opts_auto_write_type(opts, disc, reasons, 0); + if(wt==BURN_WRITE_NONE) { + if(strncmp(reasons,"MEDIA: ",7)==0) + ret= -1; + else + ret= 0; + goto report_failure; + } + skin->write_type= wt; + if(wt==BURN_WRITE_RAW) + strcpy(skin->preskin->write_mode_name,"RAW/RAW96R"); + else if(wt==BURN_WRITE_TAO) + strcpy(skin->preskin->write_mode_name,"TAO"); + else if(wt==BURN_WRITE_SAO) + strcpy(skin->preskin->write_mode_name,"SAO"); + else + sprintf(skin->preskin->write_mode_name,"LIBBURN/%d", (int) wt); + } + if(strcmp(skin->preskin->write_mode_name,"RAW/RAW96R")==0) { + skin->write_type= BURN_WRITE_RAW; + skin->block_type= BURN_BLOCK_RAW96R; + } else if(strcmp(skin->preskin->write_mode_name,"TAO")==0) { + skin->write_type= BURN_WRITE_TAO; + skin->block_type= BURN_BLOCK_MODE1; + } else if(strncmp(skin->preskin->write_mode_name,"LIBBURN/",8)==0) { + skin->block_type= BURN_BLOCK_MODE1; + } else { + strcpy(skin->preskin->write_mode_name,"SAO"); + skin->write_type= BURN_WRITE_SAO; + skin->block_type= BURN_BLOCK_SAO; + } + if(!was_still_default) + burn_write_opts_set_write_type(opts,skin->write_type,skin->block_type); + ret = burn_precheck_write(opts,disc,reasons,0); + if(ret<=0) { +report_failure:; + if(ret!=-1) + fprintf(stderr,"cdrskin: Reason: %s\n",reasons); + fprintf(stderr,"cdrskin: Media : %s%s\n", + s==BURN_DISC_BLANK?"blank ": + s==BURN_DISC_APPENDABLE?"appendable ": + s==BURN_DISC_FULL?"** closed ** ":"", + profile_name[0]?profile_name: + s==BURN_DISC_EMPTY?"no media":"unknown media"); + return(0); + } + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: Write type : %s\n", skin->preskin->write_mode_name); + return(1); +} + +#else /* Cdrskin_libburn_write_mode_ruleS */ + +/** Determines the effective write mode and checks whether 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, track_type_1= 0, mixed_mode= 0, unpredicted_size= 0; + int might_do_tao= 0, might_do_sao= 1, allows_multi= 1, ret, current_is_cd= 1; + int use_data_image_size, current_is_overwriteable= 0; + struct burn_drive_info *drive_info = NULL; + char profile_name[80]; + double fixed_size= 0.0, tao_to_sao_tsize= 0.0, dummy; +#ifdef Cdrskin_libburn_has_get_multi_capS + struct burn_multi_caps *caps = NULL; +#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); + if(profile_number!=0x09 && profile_number!=0x0a) + current_is_cd= 0; +#endif + +#ifdef Cdrskin_allow_libburn_taO + might_do_tao= 1; +#endif +#ifdef Cdrskin_libburn_has_get_multi_capS + ret = burn_disc_get_multi_caps(skin->grabbed_drive,BURN_WRITE_NONE,&caps,0); + if (ret<0) { + fprintf(stderr, + "cdrskin: FATAL : Cannot obtain write mode capabilities of drive\n"); + return(0); + } else if(ret==0) { + fprintf(stderr, + "cdrskin: SORRY : Cannot find any suitable write mode for this media\n"); + burn_disc_free_multi_caps(&caps); + return(0); + } + might_do_tao= caps->might_do_tao; + might_do_sao= caps->might_do_sao; + burn_disc_free_multi_caps(&caps); +#endif + + for(i=0;itrack_counter;i++) { + Cdrtrack_get_track_type(skin->tracklist[i],&track_type,§or_size,0); + if(i==0) + track_type_1= track_type; + else if(track_type_1!=track_type) + mixed_mode= 1; + Cdrtrack_get_size(skin->tracklist[i],&fixed_size, + &tao_to_sao_tsize,&dummy,&use_data_image_size,2); + + /* <<< until CD-SAO does fill-up: filluped last CD track length undefined */ + if(fixed_size<=0 && + !(current_is_cd==0 && skin->fill_up_media && i==skin->track_counter-1)) + unpredicted_size= 1+(tao_to_sao_tsize<=0); + } + + if(strcmp(skin->preskin->write_mode_name,"DEFAULT")==0) { + was_still_default= 1; + + if((s==BURN_DISC_APPENDABLE || mixed_mode || + (current_is_cd && skin->fill_up_media) ) && might_do_tao) { + strcpy(skin->preskin->write_mode_name,"TAO"); + was_still_default= 2; /* prevents trying of SAO if drive dislikes TAO*/ + } else if(unpredicted_size && might_do_tao) { + strcpy(skin->preskin->write_mode_name,"TAO"); + if(unpredicted_size>1) + was_still_default= 2; /* prevents trying of SAO */ + } else if(s==BURN_DISC_BLANK && skin->track_counter==1 && + skin->fill_up_media && might_do_sao && !current_is_cd) { + /* to avoid problems on my NEC with blank DVD-RW and TAO fill_up_media */ + strcpy(skin->preskin->write_mode_name,"SAO"); + } else if((profile_number==0x1a || profile_number==0x13 || + profile_number==0x12 || + profile_number==0x11 || profile_number==0x14 || + profile_number==0x15 || + profile_number==0x1b || profile_number==0x2b) + && might_do_tao) { + /* DVD+RW, DVD-RW Restricted Overwrite, DVD-RAM, + DVD-R[W][/DL] Sequential Recording, DVD+R[/DL] */ + 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) { + 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 whether 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= 0; + if(skin->write_type==BURN_WRITE_SAO && might_do_sao) + ok= 1; + if(skin->write_type==BURN_WRITE_TAO && might_do_tao) + 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 && !mixed_mode) + 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(skin->write_type==BURN_WRITE_SAO && mixed_mode) { + fprintf(stderr, + "cdrskin: FATAL : Cannot write mix of data and audio in SAO mode\n"); + if(might_do_tao) + fprintf(stderr, + "cdrskin: HINT : Try with option -tao resp. without -sao\n"); + return(0); + } + if(skin->write_type==BURN_WRITE_SAO && unpredicted_size>1) { + fprintf(stderr, + "cdrskin: FATAL : At least one track has no predictable size.\n"); + fprintf(stderr, + "cdrskin: HINT : Use tsize= or tao_to_sao_tsize= to announce the track size\n"); + if(might_do_tao) + fprintf(stderr, + "cdrskin: HINT : or try with option -tao resp. without -sao\n"); + return(0); + } + 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) { + was_still_default= 2; /* do not try more than once */ + if((skin->write_type==BURN_WRITE_RAW || + skin->write_type==BURN_WRITE_SAO) && might_do_tao) { + skin->write_type= BURN_WRITE_TAO; + skin->block_type= BURN_BLOCK_MODE1; + strcpy(skin->preskin->write_mode_name,"TAO"); + goto check_with_drive; + } else if (might_do_sao) { + skin->write_type= BURN_WRITE_SAO; + skin->block_type= BURN_BLOCK_SAO; + strcpy(skin->preskin->write_mode_name,"SAO"); + goto check_with_drive; + } + } + fprintf(stderr,"cdrskin: HINT : If you are certain that the drive will do, try option -force\n"); + return(0); + } + } + +#ifdef Cdrskin_libburn_has_get_multi_capS + ret = burn_disc_get_multi_caps(skin->grabbed_drive,skin->write_type,&caps,0); + if (ret>0) { + current_is_overwriteable= caps->start_adr; + allows_multi= caps->multi_session || current_is_overwriteable; + } + burn_disc_free_multi_caps(&caps); +#endif + if(skin->multi) { + if(!allows_multi) { + if(skin->prodvd_cli_compatible) { + skin->multi= 0; + if(skin->verbosity>=Cdrskin_verbose_progresS) + fprintf(stderr, "cdrskin: NOTE : Ignored option -multi.\n"); + } else { + fprintf(stderr, + "cdrskin: SORRY : Cannot keep this media appendable after write by -multi\n"); + return(0); + } else if(current_is_overwriteable) { + skin->multi= 0; + if(!skin->use_data_image_size) + if(skin->verbosity>=Cdrskin_verbose_progresS) + fprintf(stderr, "cdrskin: NOTE : -multi cannot leave a recognizeable end mark on this media.\n"); + } + } + +it_is_done:; + if(skin->write_type==BURN_WRITE_SAO && unpredicted_size==1) + for(i= 0; itrack_counter; i++) { + Cdrtrack_get_size(skin->tracklist[i],&fixed_size, + &tao_to_sao_tsize,&dummy,2); + if(fixed_size<=0.0 && tao_to_sao_tsize>0.0) { + printf( + "cdrskin: NOTE : augmenting non-tao write mode by tao_to_sao_tsize\n"); + printf("cdrskin: NOTE : fixed size : %.f\n",tao_to_sao_tsize); + Cdrtrack_activate_tao_tsize(skin->tracklist[i],0); + } + } + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: write type : %s\n", skin->preskin->write_mode_name); + return(1); +} + +#endif /* ! Cdrskin_libburn_write_mode_ruleS */ + + +#ifndef Cdrskin_extra_leaN + +int Cdrskin_announce_tracks(struct CdrskiN *skin, int flag) +{ + int i,mb,use_data_image_size; + double size,padding,sector_size= 2048.0; + double sectors; + + if(skin->verbosity>=Cdrskin_verbose_progresS) { + for(i=0;itrack_counter;i++) { + Cdrtrack_get_size(skin->tracklist[i],&size,&padding,§or_size, + &use_data_image_size,0); + if(size<=0) { + printf("Track %-2.2d: %s unknown length", + i+1,(sector_size==2048?"data ":"audio")); + } else { + mb= size/1024.0/1024.0; + printf("Track %-2.2d: %s %4d MB ", + i+1,(sector_size==2048?"data ":"audio"),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); + } + } + return(1); +} + +#endif /* ! Cdrskin_extra_leaN */ + + +#ifdef Cdrskin_libburn_has_random_access_rW + +int Cdrskin_direct_write(struct CdrskiN *skin, int flag) +{ + off_t byte_address, data_count, chunksize, i, alignment, fill; + int ret, max_chunksize= 64*1024, source_fd= -1, is_from_stdin, eof_sensed= 0; + char *buf= NULL, *source_path, amount_text[81]; + struct burn_multi_caps *caps= NULL; + + ret= Cdrskin_grab_drive(skin,0); + if(ret<=0) + goto ex; + + ret= burn_disc_get_multi_caps(skin->grabbed_drive,BURN_WRITE_NONE,&caps,0); + if(ret<=0) + goto ex; + if(caps->start_adr==0) { + fprintf(stderr, + "cdrskin: SORRY : Direct writing is not supported by drive and media\n"); + {ret= 0; goto ex;} + } + alignment= caps->start_alignment; + if(alignment>0 && (((off_t) skin->direct_write_amount) % alignment)!=0) { + fprintf(stderr, + "cdrskin: SORRY : direct_write_amount=%.f not aligned to blocks of %dk\n", + skin->direct_write_amount,(int) alignment/1024); + {ret= 0; goto ex;} + } + + if(skin->track_counter<=0) { + fprintf(stderr, + "cdrskin: SORRY : No track source given for direct writing\n"); + {ret= 0; goto ex;} + } + Cdrtrack_get_source_path(skin->tracklist[0], + &source_path,&source_fd,&is_from_stdin,0); + if(source_fd==-1) { + ret= Cdrtrack_open_source_path(skin->tracklist[0],&source_fd, + 2|(skin->verbosity>=Cdrskin_verbose_debuG)); + if(ret<=0) + goto ex; + } + buf= malloc(max_chunksize); + if(buf==NULL) { + fprintf(stderr, + "cdrskin: FATAL : Cannot allocate %d bytes of read buffer.\n", + max_chunksize); + {ret= -1; goto ex;} + } + byte_address= skin->write_start_address; + if(byte_address<0) + byte_address= 0; + data_count= skin->direct_write_amount; + if(data_count>0) + sprintf(amount_text,"%.fk",(double) (data_count/1024)); + else + strcpy(amount_text,"0=open_ended"); + fprintf(stderr,"Beginning direct write (start=%.fk,amount=%s) ...\n", + (double) (byte_address/1024),amount_text); + for(i= 0; i 0 ? alignment : 2048); + else + chunksize= data_count-i; + if(chunksize>max_chunksize) + chunksize= max_chunksize; + + /* read buffer from first track */ + for(fill= 0; fill0) + fprintf(stderr,"cdrskin: %s (errno=%d)\n", strerror(errno), errno); + ret= 0; goto ex; + } else if(ret==0) { + eof_sensed= 1; + if(data_count==0) { + memset(buf+fill,0,(size_t) (chunksize-fill)); + break; + } else { + fprintf(stderr, + "cdrskin: FATAL : Premature EOF while reading from '%s'\n", + source_path); + ret= 0; goto ex; + } + } + } + ret= burn_random_access_write(skin->grabbed_drive,byte_address, + buf,chunksize,0); + if(ret<=0) + goto ex; + if(eof_sensed) + break; + byte_address+= chunksize; + fprintf(stderr,"\r%9.fk written ",((double) (i+chunksize))/1024.0); + } + fprintf(stderr,"\r%9.fk written \n",((double) i)/1024.0); + /* flush drive buffer */ + fprintf(stderr,"syncing cache ...\n"); + ret = burn_random_access_write(skin->grabbed_drive,byte_address,buf,0,1); + if(ret<=0) + goto ex; + ret= 1; +ex:; + if(caps!=NULL) + burn_disc_free_multi_caps(&caps); + if(skin->drive_is_grabbed) + Cdrskin_release_drive(skin,0); + if(buf!=NULL) + free(buf); + if(ret>0) + fprintf(stderr,"writing done\n"); + else + fprintf(stderr,"writing failed\n"); + return(ret); +} + + +int Cdrskin_grow_overwriteable_iso(struct CdrskiN *skin, int flag) +{ + int ret, i, went_well= 1; + char *track_descr,*td,*md; + double track_size, media_size; + + ret= Cdrtrack_get_iso_fs_descr(skin->tracklist[0],&track_descr,&track_size,0); + if(ret<=0) { + fprintf(stderr,"cdrskin: SORRY : Saw no ISO-9660 filesystem in track 0\n"); + return(ret); + } + if(skin->grow_overwriteable_iso==3) /* initial session */ + return(1); + if(skin->grow_overwriteable_iso!=2) { + fprintf(stderr, + "cdrskin: SORRY : Could not read ISO-9660 descriptors from media\n"); + return(0); + } + ret= Scan_for_iso_size((unsigned char *) skin->overwriteable_iso_head+16*2048, + &media_size, 0); + if(ret<=0) { + fprintf(stderr,"cdrskin: SORRY : No recognizable ISO-9660 on media\n"); + return(0); + } + if(skin->write_start_address>=0.0) + media_size= skin->write_start_address; + + /* Write new sum into media descr 0 */ + md= skin->overwriteable_iso_head+16*2048; + memcpy(md,track_descr,2048); + Set_descr_iso_size((unsigned char *) md,track_size+media_size,0); + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin_debug: new ISO-9660 size : %.f (%fs)\n", + track_size+media_size, (track_size+media_size)/2048)); + + /* Copy type 255 CD001 descriptors from track to media descriptor buffer + and adjust their size entries */ + for(i=1; i<16; i++) { + td= track_descr+i*2048; + md= skin->overwriteable_iso_head+(16+i)*2048; + if(td[0] != -1) + break; + /* demand media descrN[0] == track descrN[0] */ + if(td[0] != md[0]) { + fprintf(stderr, + "cdrskin: SORRY : Type mismatch of ISO volume descriptor #%d (%u <-> %u)\n", + i, ((unsigned int) td[0]) & 0xff, ((unsigned int) md[0])&0xff); + went_well= 0; + } + memcpy(md,td,2048); + Set_descr_iso_size((unsigned char *) md,track_size+media_size,0); + } + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin_debug: copied %d secondary ISO descriptors\n", + i-1)); + + /* write block 16 to 31 to media */ + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin_debug: writing to media: blocks 16 to 31\n")); + ret= burn_random_access_write(skin->grabbed_drive, (off_t) (16*2048), + skin->overwriteable_iso_head+16*2048, + (off_t) (16*2048), 1); + if(ret<=0) + return(ret); + + return(went_well); +} + + +#endif /* Cdrskin_libburn_has_random_access_rW */ + + +/** 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,min_buffer_fill= 101; + int use_data_image_size, needs_early_fifo_fill= 0,iso_size= -1; + 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; + char *doing; + char *source_path; + int source_fd, is_from_stdin; + + if(skin->tell_media_space) + doing= "estimating"; + else + doing= "burning"; + printf("cdrskin: beginning to %s disc\n", + skin->tell_media_space?"estimate":"burn"); + if(skin->fill_up_media && skin->multi) { + ClN(fprintf(stderr, + "cdrskin: NOTE : Option --fill_up_media disabled option -multi\n")); + skin->multi= 0; + } + ret= Cdrskin_grab_drive(skin,0); + if(ret<=0) + goto burn_failed; + drive= skin->drives[skin->driveno].drive; + s= burn_disc_get_status(drive); + if(skin->verbosity>=Cdrskin_verbose_progresS) + Cdrskin_report_disc_status(skin,s,1); + + +#ifndef Cdrskin_libburn_write_mode_ruleS + +#ifdef Cdrskin_allow_libburn_taO + if (s!=BURN_DISC_APPENDABLE && 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"); + goto burn_failed; + } + + ret= Cdrskin_activate_write_mode(skin,s,0); + if(ret<=0) { + fprintf(stderr, + "cdrskin: FATAL : Cannot activate the desired write mode\n"); + goto burn_failed; + } + +#endif /* ! Cdrskin_libburn_write_mode_ruleS */ + + + 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"); +burn_failed:; + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("cdrskin: %s failed\n", doing); + fprintf(stderr,"cdrskin: FATAL : %s failed.\n", doing); + 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); + goto burn_failed; + } + Cdrtrack_get_size(skin->tracklist[i],&size,&padding,§or_size, + &use_data_image_size,0); + if(use_data_image_size==1) { /* still unfulfilled -isosize demand pending */ + needs_early_fifo_fill= 1; + } else if(size>0) + skin->fixed_size+= size+padding; + else + skin->has_open_ended_track= 1; + } + +#ifndef Cdrskin_libburn_write_mode_ruleS + 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"); + goto burn_failed; + } +#endif /* ! Cdrskin_allow_sao_for_appendablE */ + } +#endif /* ! Cdrskin_libburn_write_mode_ruleS */ + +#ifndef Cdrskin_extra_leaN + /* Final decision on track size has to be made after eventual -isosize + determination via fifo content. + */ + if(needs_early_fifo_fill && !skin->tell_media_space) { + int start_memorized; + + start_memorized= skin->fifo_start_at; + /* try ISO-9660 size recognition via fifo */ + if(32*2048<=skin->fifo_size) + skin->fifo_start_at= 32*2048; + else + skin->fifo_start_at= skin->fifo_size; + ret= Cdrskin_fill_fifo(skin,0); + if(ret<=0) + goto fifo_filling_failed; + if((start_memorized>skin->fifo_start_at || start_memorized<=0) && + skin->fifo_start_atfifo_size) + needs_early_fifo_fill= 2; /* continue filling fifo at normal stage */ + skin->fifo_start_at= start_memorized; + } +#endif /* Cdrskin_extra_leaN */ + + skin->fixed_size= 0.0; + skin->has_open_ended_track= 0; + for(i=0;itrack_counter;i++) { + Cdrtrack_get_size(skin->tracklist[i],&size,&padding,§or_size, + &use_data_image_size,0); + if(use_data_image_size==1 && size<=0 && skin->tell_media_space) + size= 1024*1024; /* a dummy size */ + ret= Cdrtrack_activate_image_size(skin->tracklist[i],&size, + !!skin->tell_media_space); + if(ret<=0) { + Cdrtrack_get_source_path(skin->tracklist[i], + &source_path,&source_fd,&is_from_stdin,0); + fprintf(stderr, + "cdrskin: FATAL : Cannot determine -isosize of track source\n"); + fprintf(stderr, + "cdrskin: '%s'\n", source_path); + {ret= 0; goto ex;} + } + Cdrtrack_get_size(skin->tracklist[i],&size,&padding,§or_size, + &use_data_image_size,0); + if(use_data_image_size==2 && skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr, + "cdrskin: DEBUG: track %2.2d : activated -isosize %.fs (= %.fb)\n", + i+1, size/2048.0,size)); + if(size>0) + skin->fixed_size+= size+padding; + else + skin->has_open_ended_track= 1; + } + + o= burn_write_opts_new(drive); + burn_write_opts_set_perform_opc(o, 0); + +#ifdef Cdrskin_libburn_has_set_start_bytE + +/* growisofs stunt: assessment of media and start for next session */ + if((skin->grow_overwriteable_iso==1 || skin->grow_overwriteable_iso==2) && + skin->media_is_overwriteable) { + /* Obtain ISO size from media, keep 64 kB head in memory */ + ret= Cdrskin_overwriteable_iso_size(skin,&iso_size,0); + if(ret<0) + goto ex; + if(ret>0 && skin->write_start_address<0) { + skin->write_start_address= ((double) iso_size)*2048.0; + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(printf( + "cdrskin: write start address by --grow_overwriteable_iso : %ds\n", + iso_size)); + } else if(ret==0) + skin->grow_overwriteable_iso= 3; /* do not patch ISO header later on */ + } + + burn_write_opts_set_start_byte(o, skin->write_start_address); + +#endif /* Cdrskin_libburn_has_set_start_bytE */ + +#ifdef Cdrskin_libburn_has_multI + if(skin->media_is_overwriteable && skin->multi) { + if(skin->grow_overwriteable_iso<=0) { + fprintf(stderr, "cdrskin: FATAL : -multi cannot leave a recognizeable end mark on this media.\n"); + fprintf(stderr, "cdrskin: HINT : For ISO-9660 images try --grow_overwriteable_iso -multi\n"); + {ret= 0; goto ex;} + } + skin->multi= 0; + } + if(skin->multi && !skin->media_does_multi) { + if(skin->prodvd_cli_compatible) { + skin->multi= 0; + if(skin->verbosity>=Cdrskin_verbose_progresS) + fprintf(stderr, "cdrskin: NOTE : Ignored option -multi.\n"); + } + } + burn_write_opts_set_multi(o,skin->multi); +#endif +#ifdef Cdrskin_libburn_has_set_filluP + burn_write_opts_set_fillup(o, skin->fill_up_media); +#endif +#ifdef Cdrskin_libburn_has_set_forcE + burn_write_opts_set_force(o, !!skin->force_is_set); +#endif + + 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); + +#ifdef Cdrskin_libburn_write_mode_ruleS + ret= Cdrskin_activate_write_mode(skin,o,disc,0); + if(ret<=0) + goto burn_failed; +#else /* Cdrskin_libburn_write_mode_ruleS */ + burn_write_opts_set_write_type(o,skin->write_type,skin->block_type); +#endif + + ret= Cdrskin_obtain_nwa(skin, &nwa,0); + if(ret<=0) + nwa= -1; + if(skin->assert_write_lba>=0 && nwa!=skin->assert_write_lba) { + fprintf(stderr, + "cdrskin: FATAL : Option assert_write_lba= demands block number %10d\n", + skin->assert_write_lba); + fprintf(stderr, + "cdrskin: FATAL : but predicted actual write start address is %10d\n", + nwa); + {ret= 0; goto ex;} + } + +#ifndef Cdrskin_extra_leaN + Cdrskin_announce_tracks(skin,0); +#endif + + if(skin->tell_media_space || skin->track_counter<=0) { + /* write capacity estimation and return without actual burning */ + +#ifdef Cdrskin_libburn_has_get_spacE + { + off_t free_space; + char msg[80]; + + free_space= burn_disc_available_space(drive,o); + sprintf(msg,"%d\n",(int) (free_space/(off_t) 2048)); + if(skin->preskin->result_fd>=0) { + write(skin->preskin->result_fd,msg,strlen(msg)); + } else + printf("%s",msg); + } +#endif /* Cdrskin_libburn_has_get_spacE */ + + if(skin->track_counter>0) + fprintf(stderr, + "cdrskin: NOTE : %s burn run suppressed by option --tell_media_space\n", + skin->preskin->write_mode_name); + {ret= 1; goto ex;} + } + + Cdrskin_adjust_speed(skin,0); + +#ifndef Cdrskin_extra_leaN + Cdrskin_wait_before_action(skin,0); + if(needs_early_fifo_fill==1) + ret= 1; + else + ret= Cdrskin_fill_fifo(skin,0); + if(ret<=0) { +fifo_filling_failed:; + fprintf(stderr,"cdrskin: FATAL : Filling of fifo failed\n"); + goto ex; + } + +#endif /* ! Cdrskin_extra_leaN */ + + if(skin->verbosity>=Cdrskin_verbose_progresS && nwa>=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); + + if(max_tracksupposed_track_idx) + max_track= skin->supposed_track_idx; + + +#ifndef Cdrskin_extra_leaN + + /* <<< 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(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 + +#ifdef Cdrskin_libburn_has_random_access_rW + if(skin->media_is_overwriteable && skin->grow_overwriteable_iso>0 && + wrote_well) { + /* growisofs final stunt : update volume descriptors at start of media */ + ret= Cdrskin_grow_overwriteable_iso(skin,0); + if(ret<=0) + wrote_well= 0; + } +#endif /* Cdrskin_libburn_has_random_access_rW */ + + 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, + &use_data_image_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 + ret= 0; +ex:; + if(ret<=0) { + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("cdrskin: %s failed\n",doing); + fprintf(stderr,"cdrskin: FATAL : %s failed.\n",doing); + } + 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); +} + + +/** 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= NULL; + 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; + s= burn_disc_get_status(drive); + if(s!=BURN_DISC_APPENDABLE) { + if(skin->grow_overwriteable_iso==1 || skin->grow_overwriteable_iso==2) { + lba= 0; + ret= Cdrskin_overwriteable_iso_size(skin,&nwa,0); + if(ret>0) { + s= BURN_DISC_APPENDABLE; + goto put_out; + } + } + Cdrskin_report_disc_status(skin,s,0); + fprintf(stderr,"cdrskin: FATAL : -msinfo can only operate on appendable (i.e. -multi) discs\n"); + if(skin->grow_overwriteable_iso>0) + fprintf(stderr,"cdrskin: or on overwriteables with existing ISO-9660 file system.\n"); + {ret= 0; goto ex;} + } + disc= burn_drive_get_disc(drive); + if(disc==NULL) { + +#ifdef Cdrskin_libburn_has_get_msc1 + /* No TOC available. Try to inquire directly. */ + ret= burn_disc_get_msc1(drive,&lba); + if(ret>0) + goto obtain_nwa; +#endif /* Cdrskin_libburn_has_get_msc1 */ + + 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; + } + +put_out:; + if(skin->preskin->result_fd>=0) { + sprintf(msg,"%d,%d\n",lba,nwa); + write(skin->preskin->result_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); +} + + +/** 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) + ClN(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|16)>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"); + Cdrpreskin_consider_normal_user(0); + 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,l,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", + "-reset", "-abort", "-overburn", "-ignsize", "-useinfo", + "-fix", "-nofix", + "-raw", "-raw96p", "-raw16", + "-clone", "-text", "-mode2", "-xa", "-xa1", "-xa2", "-xamix", + "-cdi", "-preemp", "-nopreemp", "-copy", "-nocopy", + "-scms", "-shorttrack", "-noshorttrack", "-packet", "-noclose", + "-media-info", "-minfo", + "" + }; + + /* 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;ipreskin->demands_cdrecord_caps= 1; + if(skin->preskin->fallback_program[0]) + fprintf(stderr,"cdrskin: NOTE : Unimplemented option: '%s'\n",argv[i]); + else + fprintf(stderr,"cdrskin: NOTE : ignoring unimplemented option : '%s'\n", + argv[i]); + fprintf(stderr, + "cdrskin: NOTE : option is waiting for a volunteer to implement it.\n"); + continue; + } + +#ifndef Cdrskin_extra_leaN + if(strncmp(argv[i],"abort_after_bytecount=",22)==0) { + skin->abort_after_bytecount= Scanf_io_size(argv[i]+22,0); + fprintf(stderr, + "cdrskin: NOTE : will perform synthetic abort after %.f bytes\n", + skin->abort_after_bytecount); + + } else if(strcmp(argv[i],"--abort_handler")==0) { +#else /* ! Cdrskin_extra_leaN */ + if(strcmp(argv[i],"--abort_handler")==0) { +#endif + /* 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); + } + +#ifdef Cdrskin_libburn_has_get_best_speeD + } else if(strcmp(argv[i],"--adjust_speed_to_drive")==0) { + skin->adjust_speed_to_drive= 1; +#endif + + } else if(strcmp(argv[i],"--allow_emulated_drives")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--allow_setuid")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--allow_untested_media")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--any_track")==0) { + skin->single_track= -1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(printf( + "cdrskin: --any_track : will accept any unknown option as track source\n")); + + } else if(strncmp(argv[i],"assert_write_lba=",17)==0) { + value_pt= argv[i]+17; + value= Scanf_io_size(value_pt,0); + l= strlen(value_pt); + if(l>1) if(isalpha(value_pt[l-1])) + value/= 2048.0; + skin->assert_write_lba= value; + + } else if(strcmp(argv[i],"-atip")==0) { + if(skin->do_atip<1) + skin->do_atip= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(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; + skin->preskin->demands_cdrskin_caps= 1; + } else if(strcmp(cpt,"format_overwrite_full")==0) { + skin->do_blank= 1; + skin->blank_format_type= 1|(1<<10); + skin->blank_format_size= 0; + skin->preskin->demands_cdrskin_caps= 1; + } else if(strcmp(cpt,"format_overwrite_quickest")==0) { + skin->do_blank= 1; + skin->blank_format_type= 1; + skin->blank_format_size= 0; + skin->preskin->demands_cdrskin_caps= 1; + } else if(strcmp(cpt,"deformat_sequential")==0) { + skin->do_blank= 1; + skin->blank_format_type= 2; + skin->blank_fast= 0; + skin->preskin->demands_cdrskin_caps= 1; + } else if(strcmp(cpt,"deformat_sequential_quickest")==0) { + skin->do_blank= 1; + skin->blank_format_type= 2; + skin->blank_fast= 1; + skin->preskin->demands_cdrskin_caps= 1; + } 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) + ClN(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(strncmp(argv[i],"direct_write_amount=",20)==0) { + skin->direct_write_amount= Scanf_io_size(argv[i]+20,0); + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(printf("cdrskin: amount for direct writing : %.f\n", + skin->direct_write_amount)); + if(skin->direct_write_amount>=0.0) { + skin->do_direct_write= 1; + printf("cdrskin: NOTE : Direct writing will only use first track source and no fifo.\n"); + skin->preskin->demands_cdrskin_caps= 1; + } else + skin->do_direct_write= 0; + + } 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 || + strcmp(argv[i],"--drive_not_f_setlk")==0 || + strcmp(argv[i],"--drive_not_o_excl")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strncmp(argv[i],"drive_scsi_dev_family=",22)==0) { + /* is handled in Cdrpreskin_setup() */; + if(skin->verbosity>=Cdrskin_verbose_debuG && + skin->preskin->drive_scsi_dev_family!=0) + ClN(fprintf(stderr,"cdrskin_debug: drive_scsi_dev_family= %d\n", + skin->preskin->drive_scsi_dev_family)); + + } else if(strcmp(argv[i],"--drive_scsi_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) + ClN(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) + ClN(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) + ClN(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", + (int) sizeof(skin->eject_device)-1,(int) 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 + ClN(printf("cdrskin: eject_device : %s\n",skin->eject_device)); +#else + ClN(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) + ClN(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; + +#endif /* ! Cdrskin_extra_leaN */ + +#ifdef Cdrskin_libburn_has_set_filluP + } else if(strcmp(argv[i],"--fill_up_media")==0) { + skin->fill_up_media= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(printf( + "cdrskin: will fill up last track to full free media space\n")); +#endif + + } else if(strcmp(argv[i],"-force")==0) { + skin->force_is_set= 1; + + } else if(strcmp(argv[i],"-format")==0) { + skin->do_blank= 1; + skin->blank_format_type= 3|(1<<10); + skin->blank_format_size= 0; + skin->force_is_set= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(printf( + "cdrskin: will format DVD+RW by blank=format_overwrite_full -force\n")); + +#ifndef Cdrskin_extra_leaN + + } 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); + skin->preskin->demands_cdrskin_caps= 1; + + } 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)); + +#ifdef Cdrskin_libburn_has_get_multi_capS +#ifdef Cdrskin_libburn_has_random_access_rW + } else if(strncmp(argv[i],"--grow_overwriteable_iso",24)==0) { + skin->grow_overwriteable_iso= 1; + skin->use_data_image_size= 1; + skin->preskin->demands_cdrskin_caps= 1; +#endif /* Cdrskin_libburn_has_random_access_rW */ +#endif /* Cdrskin_libburn_has_get_multi_capS */ + + +#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],"-immed")==0) { +#ifdef Cdrskin_libburn_has_set_waitinG + skin->modesty_on_drive= 1; + skin->min_buffer_percent= 75; + skin->max_buffer_percent= 95; +#else + ; +#endif /* Cdrskin_libburn_has_set_waitinG */ + + } else if(strcmp(argv[i],"-inq")==0) { + skin->do_checkdrive= 2; + + } else if(strcmp(argv[i],"-isosize")==0) { + skin->use_data_image_size= 1; + + } 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(strncmp(argv[i],"fallback_program=",17)==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"-load")==0) { + skin->do_load= 1; + + } else if(strcmp(argv[i],"-lock")==0) { + skin->do_load= 2; + + } else if(strncmp(argv[i],"-minbuf=",8)==0) { + value_pt= argv[i]+8; + goto minbuf_equals; + } else if(strncmp(argv[i],"minbuf=",7)==0) { + value_pt= argv[i]+7; +minbuf_equals:; +#ifdef Cdrskin_libburn_has_set_waitinG + skin->modesty_on_drive= 1; + sscanf(value_pt,"%lf",&value); + if (value<25 || value>95) { + fprintf(stderr, + "cdrskin: FATAL : minbuf= value must be between 25 and 95\n"); + return(0); + } + skin->min_buffer_percent= value; + skin->max_buffer_percent= 95; + ClN(printf("cdrskin: minbuf=%d percent desired buffer fill\n", + skin->min_buffer_percent)); +#else + fprintf(stderr, + "cdrskin: SORRY : Option minbuf= is not available yet.\n"); + return(0); +#endif + + } else if(strncmp(argv[i],"modesty_on_drive=",17)==0) { +#ifdef Cdrskin_libburn_has_set_waitinG + value_pt= argv[i]+17; + if(*value_pt=='0') { + skin->modesty_on_drive= 0; + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(printf( + "cdrskin: modesty_on_drive=0 : buffer waiting by os driver\n")); + } else if(*value_pt=='1') { + skin->modesty_on_drive= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(printf( + "cdrskin: modesty_on_drive=1 : buffer waiting by libburn\n")); + } else if(*value_pt=='-' && argv[i][18]=='1') { + skin->modesty_on_drive= -1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(printf( + "cdrskin: modesty_on_drive=-1 : buffer waiting as libburn defaults\n")); + } else { + fprintf(stderr, + "cdrskin: FATAL : modesty_on_drive= must be -1, 0 or 1\n"); + return(0); + } + while(1) { + value_pt= strchr(value_pt,':'); + if(value_pt==NULL) + break; + value_pt++; + if(strncmp(value_pt,"min_percent=",12)==0) { + sscanf(value_pt+12,"%lf",&value); + if (value<25 || value>100) { + fprintf(stderr, + "cdrskin: FATAL : modest_on_drive= min_percent value must be between 25 and 100\n"); + return(0); + } + skin->min_buffer_percent= value; + ClN(printf("cdrskin: modesty_on_drive : %d percent min buffer fill\n", + skin->min_buffer_percent)); + } else if(strncmp(value_pt,"max_percent=",12)==0) { + sscanf(value_pt+12,"%lf",&value); + if (value<25 || value>100) { + fprintf(stderr, + "cdrskin: FATAL : modest_on_drive= max_percent value must be between 25 and 100\n"); + return(0); + } + skin->max_buffer_percent= value; + ClN(printf("cdrskin: modesty_on_drive : %d percent max buffer fill\n", + skin->max_buffer_percent)); + } else { + fprintf(stderr, + "cdrskin: SORRY : modest_on_drive= unknown option code : %s\n", + value_pt); + } + } + skin->preskin->demands_cdrskin_caps= 1; +#else + fprintf(stderr, + "cdrskin: SORRY : Option modesty_on_drive= is not available yet.\n"); + return(0); +#endif + + } 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", + (int) sizeof(skin->msifile)-1,(int) 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) + ClN(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) + ClN(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) + ClN(printf("cdrskin: padding : %.f\n",skin->padding)); + + } else if(strcmp(argv[i],"--prodvd_cli_compatible")==0) { + skin->prodvd_cli_compatible= 1; + + } 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) + ClN(printf( +"cdrskin: --single_track : will only accept last argument as track source\n")); + skin->preskin->demands_cdrskin_caps= 1; + + } 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:; + if(strcmp(value_pt,"any")==0) + skin->x_speed= -1; + else + 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); + } + if(skin->x_speed<0) + skin->preskin->demands_cdrskin_caps= 1; + + /* >>> cdrecord speed=0 -> minimum speed , libburn -> maximum speed */; + + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(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); + } + ClN(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; + skin->preskin->demands_cdrskin_caps= 1; + +#ifndef Cdrskin_extra_leaN + 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); +#endif /* ! Cdrskin_extra_leaN */ + +#ifdef Cdrskin_libburn_has_get_spacE + } else if(strcmp(argv[i],"--tell_media_space")==0) { + skin->tell_media_space= 1; + skin->preskin->demands_cdrskin_caps= 1; +#endif + + } else if(strcmp(argv[i],"-toc")==0) { + skin->do_atip= 2; + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(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) + ClN(printf("cdrskin: fixed track size : %.f\n",skin->fixed_size)); + if(skin->smallest_tsize<0 || skin->smallest_tsize>skin->fixed_size) + skin->smallest_tsize= 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(strcmp(argv[i],"-version")==0) { + /* is handled in Cdrpreskin_setup() and should really not get here */; + + } else if(strcmp(argv[i],"-waiti")==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) + ClN(printf("cdrskin: write start address : %.f\n", + skin->write_start_address)); + skin->preskin->demands_cdrskin_caps= 1; + + } 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", + (int) sizeof(skin->source_path)-1,(int) 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(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++; + skin->use_data_image_size= 0; + 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:; + if(skin->preskin->fallback_program[0]) + fprintf(stderr,"cdrskin: NOTE : Unknown option : '%s'\n",argv[i]); + else + fprintf(stderr,"cdrskin: NOTE : ignoring unknown option : '%s'\n", + argv[i]); + skin->preskin->demands_cdrecord_caps= 1; + } + } + + if(flag&1) /* no finalizing yet */ + return(1); + + if(skin->preskin->fallback_program[0] && + skin->preskin->demands_cdrecord_caps>0 && + skin->preskin->demands_cdrskin_caps<=0) { + fprintf(stderr,"cdrskin: NOTE : Unsupported options found.\n"); + fprintf(stderr, + "cdrskin: NOTE : Will delegate job to fallback program '%s'.\n", + skin->preskin->fallback_program); + return(0); + } + +#ifndef Cdrskin_extra_leaN + 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"); + } +#endif /* ! Cdrskin_extra_leaN */ + + 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,16); + for(k= 0; ktrack_counter>0) { + skin->do_burn= 1; + +#ifndef Cdrskin_extra_leaN + if(!skin->do_direct_write) { + 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 whether 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, stdio_drive= 0; + struct CdrskiN *skin; + char reason[4096]; + + *o= NULL; + *exit_value= 0; + + if(strlen((*preskin)->device_adr)>0) { /* disable scan for all others */ + ClN(printf( + "cdrskin: NOTE : greying out all drives besides given dev='%s'\n", + (*preskin)->device_adr)); + burn_drive_add_whitelist((*preskin)->device_adr); + if(strncmp((*preskin)->device_adr, "stdio:", 6)==0) { + ret= Cdrpreskin__allows_emulated_drives((*preskin)->device_adr+6,reason,0); + if((*preskin)->allow_emulated_drives && ret>0) { + stdio_drive= 1; + (*preskin)->demands_cdrskin_caps= 1; + } else if((*preskin)->allow_emulated_drives) { + fprintf(stderr,"cdrskin: SORRY : dev=stdio:... rejected despite --allow_emulated_drives\n"); + fprintf(stderr,"cdrskin: SORRY : Reason: %s.\n", reason); + } else { + fprintf(stderr,"cdrskin: SORRY : dev=stdio:... works only with option --allow_emulated_drives\n"); + if(ret<=0) { + fprintf(stderr,"cdrskin: SORRY : but: %s.\n", reason); + fprintf(stderr, + "cdrskin: SORRY : So this option would not help anyway.\n"); + } + } + if(!stdio_drive) { + Cdrpreskin_consider_normal_user(0); + {*exit_value= 2; goto ex;} + } + } + } + + 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 testing it from time to time */ + Cdrpreskin_queue_msgs(skin->preskin,1); + + +#ifndef Cdrskin_oldfashioned_api_usE + if(stdio_drive) { + ret= burn_drive_scan_and_grab(&(skin->drives),skin->preskin->device_adr,0); + if(ret<=0) { + fprintf(stderr,"cdrskin: FATAL : Failed to grab emulated stdio-drive\n"); + {*exit_value= 2; goto ex;} + } + skin->n_drives= 1; + burn_drive_release(skin->drives[0].drive, 0); + } else { +#else + { +#endif /* Cdrskin_oldfashioned_api_usE */ + + 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_load) { + ret= Cdrskin_grab_drive(skin,8); + if(ret>0) { + if(skin->do_load==2 && !skin->do_eject) { + printf( + "cdrskin: NOTE : Option -lock orders program to exit with locked tray.\n"); + printf( + "cdrskin: HINT : Run cdrskin with option -eject to unlock the drive tray.\n"); + } else if(!skin->do_eject) + printf( + "cdrskin: NOTE : option -load orders program to exit after loading tray.\n"); + Cdrskin_release_drive(skin,(skin->do_load==2)<<1); + } + {*exit_value= 14*(ret<=0); goto ex;} + } + if(skin->do_checkdrive) { + ret= Cdrskin_checkdrive(skin,"",(skin->do_checkdrive==2)<<1); + {*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;} + } + +#ifdef Cdrskin_libburn_has_random_access_rW + if(skin->do_direct_write) { + skin->do_burn= 0; + ret= Cdrskin_direct_write(skin,0); + if(ret<=0) + {*exit_value= 13; goto ex;} + } +#endif /* Cdrskin_libburn_has_random_access_rW */ + + if(skin->do_burn || skin->tell_media_space) { + 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, *h_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"); + } + } + + ret= Cdrskin_setup(skin,argc,argv,0); + if(ret<=0) + {exit_value= 3; goto ex;} + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(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(preskin!=NULL) + h_preskin= preskin; + else if(skin!=NULL) + h_preskin= skin->preskin; + if(h_preskin!=NULL) { + if(skin!=NULL) + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr, + "cdrskin_debug: demands_cdrecord_caps= %d , demands_cdrskin_caps= %d\n", + h_preskin->demands_cdrecord_caps, h_preskin->demands_cdrskin_caps)); + + if(exit_value && h_preskin->demands_cdrecord_caps>0 && + h_preskin->demands_cdrskin_caps<=0) { /* prepare fallback */ + /* detach preskin from managers which would destroy it */ + preskin= NULL; + if(skin!=NULL) + skin->preskin= NULL; + } else + h_preskin= NULL; /* prevent fallback */ + } + if(skin!=NULL) { + Cleanup_set_handlers(NULL,NULL,1); + if(skin->preskin!=NULL) + Cdrskin_eject(skin,0); + Cdrskin_destroy(&skin,0); + } + Cdrpreskin_destroy(&preskin,0); + if(lib_initialized) + burn_finish(); + if(h_preskin!=NULL) + Cdrpreskin_fallback(h_preskin,argc,argv,0); /* never come back */ + exit(exit_value); +} diff --git a/libburn/branches/ZeroFourTwo/cdrskin/cdrskin_eng.html b/libburn/branches/ZeroFourTwo/cdrskin/cdrskin_eng.html new file mode 100644 index 00000000..a3cf4047 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/cdrskin/cdrskin_eng.html @@ -0,0 +1,461 @@ + + + + + + +cdrskin homepage english + + + + + +
+ +cdrskin logo: Doener mit Scharf + +

Homepage of

+

cdrskin

+ + +

Limited cdrecord compatibility wrapper for libburn

+
+ +

+

Purpose:

+Burns preformatted data to CD and single layer DVD media:
+CD-R, DVD-R, DVD+R, CD-RW, DVD-RW, DVD-RAM, DVD+RW +

+

+ +


+ +

+

Hardware requirements:

+A CD/DVD recorder suitable for +http://libburnia-project.org
+(SCSI , ATA , USB , or SATA writers compliant to standard MMC-3 for CD +and to MMC-5 for DVD). +
+

+ +

+

Software requirements :

+
+
Linux with kernel 2.4 or higher (and libc, of course) :
+
With kernel 2.4 an ATA 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.4.0
+
(founded by Derek Foreman and Ben Jansens, +furthered by team of libburnia-project.org)
+
transfers data to CD and DVD
+
+

+ +

+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 most common options of cdrecord for data and audio on CD media +are provided in a compatible way.
+On single layer DVD media cdrskin is able to perform any recording job +which is possible with cdrecord. +Other than with cdrecord, options -multi and -tao are supported with +certain DVD types. +
+

+
Get an overview of drives and their addresses
+
# cdrskin -scanbus
+
# cdrskin dev=ATA -scanbus
+
# cdrskin --devices
+
Being superuser avoids permission problems with /dev/srN resp. /dev/hdX . +
+
Ordinary users should then get granted rw access to the /dev files +as listed by option --devices.
+
 
+ +
Get info about a particular drive or loaded media:
+
$ cdrskin dev=0,1,0 -checkdrive
+
$ cdrskin dev=ATA:1,0,0 -v -atip
+
$ cdrskin dev=/dev/hdc -toc
+ +
Make used CD-RW or used unformatted DVD-RW writable again:
+
$ cdrskin -v dev=/dev/sg1 blank=fast -eject
+
$ cdrskin -v dev=/dev/dvd blank=all -eject
+ +
Format DVD-RW to avoid need for blanking before re-use:
+
$ cdrskin -v dev=/dev/sr0 blank=format_overwrite
+ +
De-format DVD-RW to make it capable of multi-session again:
+
$ cdrskin -v dev=/dev/sr0 blank=deformat_sequential
+ +
Write ISO-9660 filesystem image as only one to blank or formatted media: +
+
$ cdrskin -v dev=/dev/hdc speed=12 fs=8m \
+
  -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 \
+
  -tao padsize=300k -
+ +
Write several sessions to the same CD, DVD-R[W] or DVD+R:
+
$ cdrskin dev=/dev/hdc -v padsize=300k -multi -tao 1.iso +
+
$ cdrskin dev=/dev/hdc -v padsize=300k -multi -tao 2.iso +
+
$ cdrskin dev=/dev/hdc -v padsize=300k -multi -tao 3.iso +
+
$ cdrskin dev=/dev/hdc -v padsize=300k -tao 4.iso
+ +
Get multi-session info for option -C of program mkisofs:
+
$ c_values=$(cdrskin dev=/dev/sr0 -msinfo 2>/dev/null)
+
$ mkisofs ... -C "$c_values" ...
+ +
Inquire free space on media for a -tao -multi run:
+
$ x=$(cdrskin dev=/dev/sr0 -tao -multi \
+
  --tell_media_space 2>/dev/null)
+
$ echo "Available: $x blocks of 2048 data bytes"
+ +
Write audio tracks to CD:
+
$ cdrskin -v dev=ATA:1,0,0 speed=48 -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.) +
+
+ +Testers wanted who are willing to risk some double layer DVD media. + +

+ +
+ +

+

+
Download as source code (see README):
+
cdrskin-0.4.0.pl00.tar.gz +(680 KB). +
+
+The cdrskin tarballs are source code identical with libburn releases +of the same version number. +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.4.0.pl00-x86-suse9_0.tar.gz, (106 KB), +
+
runs on SuSE 9.0 (2.4.21) , RIP-14.4 (2.6.14) , + Gentoo (2.6.15 x86_64 Athlon).
+
+
+cdrskin_0.4.0.pl00-x86-suse9_0-static.tar.gz, (310 KB), -static compiled, +
+
runs on SuSE 7.2 (2.4.4), and on the systems above.
+
+
+
+
Documentation:
+
README an 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.3.8: +

    +
  • New option direct_write_amount=
  • +
  • New option --grow_overwriteable_iso
  • +
  • New option --allow_emulated_drives dev=stdio:<path>
  • +
  • More cdrecord options supported: + -format, -inq, -load, -lock, -immed, -waiti
  • +
  • New option fallback_program=
  • +
+ + + +

+ +
+ +

+

+

Development snapshot, version 0.4.1 :

+
Enhancements towards stable version 0.4.0.pl00: +
    +
  • none yet
  • + +
+
+
 
+
README 0.4.1 +
cdrskin_0.4.1 --help
+
cdrskin_0.4.1 -help
+
man cdrskin (as of 0.4.1)
+
 
+
Maintainers of cdrskin unstable packages please use SVN of + libburnia-project.org
+
Download: svn co http://svn.libburnia-project.org/libburn/trunk libburn +
+
Build: cd libburn ; ./bootstrap ; ./configure --prefix /usr ; 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.4.1.tar.gz +(680 KB). +
+
Binary (untar and move to /usr/bin/cdrskin):
+
+cdrskin_0.4.1-x86-suse9_0.tar.gz, (105 KB). +
+
+cdrskin_0.4.1-x86-suse9_0-static.tar.gz, (310 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 +
+Very special thanks to Andy Polyakov whose +dvd+rw-tools +provide libburn with invaluable examples on how to deal with DVD media. +

+ +
+ + +

+

+
Example for a setup of device permissions. To be done by the superuser:
+
(CD devices which offer no rw-permission are invisible to normal users.) +
+
# cdrskin --devices
+
...
+
0  dev='/dev/sr0'  rwrwr- :  'TEAC' 'CD-ROM CD-532S'
+
1  dev='/dev/hdc'  rwrw-- :  'LITE-ON' 'LTR-48125S'
+
# chmod a+rw /dev/sr0 /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"
+
$ 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.3.8 : 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 and DVD 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 +
+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/branches/ZeroFourTwo/cdrskin/cdrskin_timestamp.h b/libburn/branches/ZeroFourTwo/cdrskin/cdrskin_timestamp.h new file mode 100644 index 00000000..aa04920e --- /dev/null +++ b/libburn/branches/ZeroFourTwo/cdrskin/cdrskin_timestamp.h @@ -0,0 +1 @@ +#define Cdrskin_timestamP "2008.01.23.213607" diff --git a/libburn/branches/ZeroFourTwo/cdrskin/changelog.txt b/libburn/branches/ZeroFourTwo/cdrskin/changelog.txt new file mode 100644 index 00000000..77525865 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/cdrskin/changelog.txt @@ -0,0 +1,4873 @@ +------------------------------------------------------------------------------ + libburnia-project.org scdbackup.sourceforge.net/cdrskin +------------------------------------------------------------------------------ + +Deliberate deviations of cdrskin from cdrecord compatibility: + ++ cdrskin does drive operations (on its drive 0) without a dev= option + (Note: cdrecord meanwhile has a default for missing 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) + ++ driveropts=burnfree is default if the drive supports buffer underrun + protection. If desired, use driveropts=noburnfree to disable this feature. + ++ premature end of source is not an error and leads to full announced tracksize + ++ -msinfo pushes all other messages to stderr. It works independently of + other options which would prevent it with cdrecord (-atip, -scanbus, ...) + ++ DVD track sources get not concateneated to a single track. In general DVD + writing is quite different from cdrecord-ProDVD: + DVD-R[W] "Disc-at-once" (-sao) is nearest to cdrecord-ProDVD's methods. + DVD-R[W] "Incremental Streaming" (-tao) on unformatted media allows + multi-session and track sources of unpredictable size. + Writing DVD-RAM, DVD+RW and "Restricted Overwrite" DVD-RW is like single + track -tao on blank CD. Formatting is done via cdrskin-specific + blank=format_overwrite and not with option -format. + ++ DVD-RW get blanked fast only with option blank=deformat_sequential_quickest . + Option blank=fast is the same as blank=all in order to achieve media which + are capable of Incremental Streaming. + ++ It has not been evaluated how far -isosize is compatible with the original + cdrecord option. man cdrecord forbids stdin as source, cdrskin allows it + if a fifo is used. + + +------------------------------------------------------------------------------ + 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] +libburn/write.c +Disallowed multi flag with DVD+RW (nurses wrong hopes for now) + +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 cdrkit/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 2007 [576] +cdrskin/changelog.txt +Next cdrskin-0.2.7 cycle + +2 Jan 2007 [577] +cdrskin/wiki_plain.txt +cdrskin/README +Some DVD-RW statements + +2 Jan 2007 [578] +test/libburner.c +doc/comments +Made use of formatting of DVD-RW + +2 Jan 2007 [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 + +2007.01.10.152350 [600] +libburn/libburn.h +libburn/mmc.c +libburn/drive.c +libburn/async.c +cdrskin/cdrskin.c +Option -force enables re-formatting + +2007.01.10.152520 [601] +libburn/mmc.c +Switched full formatting from type 10h to 00h which includes lead-out + +2007.01.10.152812 [602] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +Removed writing of dummy data with blank=format_overwrite_full + +2007.01.10.204839 [603] +libburn/mmc.c +libburn/async.c +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Enabled explicit full formatting of DVD+RW ("de-icing") + +11 Jan 2007 [604] +cdrskin/README +Removed outdated option from startup file example + +2007.01.11.131106 [605] +libburn/mmc.c +With full formatting prefer format 10h over 13h or 15h + +2007.01.11.131302 [606] +libburn/os-linux.h +libburn/os-freebsd.h +libburn/cleanup.c +cdrskin/cleanup.c +Kept SIGWINCH from spoiling a burn. + +2007.01.11.131615 [607] +libburn/init.c +Sketched better handling of self-inflicted SIGs + +2007.01.11.131716 [608] +libburn/drive.c +Removed surplus newlines from messages + +2007.01.12.162239 [609] +libburn/write.c +libburn/spc.c +libburn/mmc.c +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Enabled writing to DVD-RAM + +2007.01.13.140812 [610] [611] +libburn/sg-linux.c +Implemented debugging messages for ATA enumeration + +13 Jan 2007 [612] +cdrskin/cdrskin_eng.html +cdrskin/README +Documentation updates about DVD-RAM + +2007.01.13.211425 [613] +libburn/transport.h +libburn/mmc.c +Load array of format capacities into struct burn_drive + +2007.01.13.211639 [614] +libburn/libburn.h +libburn/drive.c +libburn/async.c +Introduced API for inspection and selection of format capacities + +13 Jan 2007 [615] +test/telltoc.c +Added printing of list of available formats + +13 Jan 2007 [616] +test/libburner.c +Mentioned DVD-RAM where appropriate + +2007.01.13.214259 [617] +cdrskin/cdrskin.c +Shifted fifo reporting to new 4-times -v verbosity level + +2007.01.14.101742 [618] +cdrskin/cdrskin.c +Corrected bug with debug messages for fifo + +2007.01.14.115347 [619] +libburn/write.c +Added missing cache sync in case of aborted DVD-RW burns + +2007.01.14.133951 [620] +libburn/transport.h +libburn/mmc.c +libburn/write.c +Avoided closing of 0x13-DVD-RW sessions which are not intermediate + +15 Jan 2007 [621] +cdrskin/wiki_plain.txt +Updated about overwriteable DVD and pointer to dvd+rw-tools + + +------------------------------------ cycle - cdrskin-0.2.7 - 2007.01.15.131357 +* Burning to DVD-RAM + + +15 Jan 2007 [623] +cdrskin/changelog.txt +Next cdrskin-0.2.7 cycle + +2007.01.16.120001 [tag 624] +Makefile.am +configure.ac +README +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/changelog.txt +Made version number transition to 0.3.0 + +15 Jan 2007 [tag 625] +- 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_3_0 +Updated cdrskin tarball generator + +16 Jan 2007 [tag 626] +README +Corrected statement about restriction to CD + +16 Jan 2007 [tag 627] +cdrskin/cdrskin.c +Silenced a compiler warning + +16 Jan 2007 [tag 628] +cdrskin/README +Corrected name of tarball + +16 Jan 2007 [tag 632] +test/telltoc.c +Corrected old libburn.pykix.org URL + +16 Jan 2007 [tag 634] +cdrskin/cdrskin_eng.html +Updated web page + + +------------------------------- cycle - cdrskin-0.3.0.pl00 - 2007.01.16.120001 +* Improved recognition of unsuitable media types +* Replaced ban of chmod u+s by loud warning +* detailed man page for cdrskin +* Burning of DVD+RW and DVD-RAM media as single-track TAO-like initial session +* Formatting and then burning to DVD-RW like to DVD+RW +* New option -msifile=path from cdrkit/wodim + + +16 Jan 2007 [629] +- 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_3_0 ++ cdrskin/add_ts_changes_to_libburn_0_3_1 +Updated cdrskin tarball generator + +2007.01.16.151041 [630] +Makefile.am +configure.ac +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +Made version number transition to 0.3.1 + +16 Jan 2007 [631] +test/telltoc.c +Corrected old libburn.pykix.org URL + +16 Jan 2007 [633] +cdrskin/cdrskin_eng.html +Corrected typo + + +------------------------------------ cycle - cdrskin-0.3.1 - 2007.01.16.152345 + + +16 Jan 2007 [635] +cdrskin/changelog.txt +Next cdrskin-0.3.1 cycle + +17 Jan 2007 [636] +cdrskin/wiki_plain.txt +Removed paragraph about obsoleted tao_to_sao_tsize= + +18 Jan 2007 [637] ++ doc/cookbook.txt +Wrote down what i learned from implementing support for overwriteable DVD + +18 Jan 2007 [638] +doc/cookbook.txt +More info about Current/Maximum Capacity Descriptor + +18 Jan 2007 [639] +doc/cookbook.txt +Made clarification about formatting state recognition + +2007.01.18.211740 [640] +libburn/transport.h +libburn/mmc.c +libburn/write.c +Kept DVD+RW from stopping BG formatting if it was not started at all + +2007.01.19.110510 [641] +libburn/mmc.c +Removed forgotten debug message printed to stderr + +19 Jan 2007 [642] +doc/cookbook.txt +Began to describe TAO multi-session CD writing + +20 Jan 2007 [643] +doc/cookbook.txt +Changes with CD part. Especially explanation of TOC. + +2007.01.21.190928 [644] +cdrskin/cdrskin.c +Removed unnecessary after-grab loops + +2007.01.21.191058 [645] +libburn/write.c +Comments learned from studying MMC-3 and MMC-5 for SAO CD cookbook + +21 Jan 2007 [646] +doc/cookbook.txt +Clarification about TAO and mode page 05h per track + +2007.01.22.114245 [647] +libburn/libburn.h +libburn/write.c +libburn/async.c +libburn/libdax_msgs.h +cdrskin/cdrskin.c +Bug fix: Banned mixed mode SAO (because broken in libburn and unclear in MMC) + +23 Jan 2007 [648] +doc/cookbook.txt +Added SAO CD Cookbook + +23 Jan 2007 [649] +doc/cookbook.txt +Corrections of typos and text debris + +2007.01.25.145846 [652] +libburn/structure.c +Bug fix: DVD tracks of defined size >=2GB suffered 32-bit integer wraparound + +2007.01.25.185214 [655] +libburn/libburn.h +libburn/write.c +libburn/structure.h +libburn/structure.c +libburn/file.h +libburn/file.c +Enforce minimum track length with SAO + +2007.01.25.180001 [tag 656] +libburn_0_3_0_1/libburn/structure.c +libburn_0_3_0_1/cdrskin/cdrskin_timestamp.h +libburn_0_3_0_1/cdrskin/README +Bug fix: DVD tracks of defined size >=2GB suffered 32-bit integer wraparound + +26 Jan 2007 [tag 657] +libburn_0_3_0_1/cdrskin/cdrskin_eng.html +Mentioned new cdrskin patch level 01 + +26 Jan 2007 [658] +cdrskin/cdrskin_eng.html +Now offering cdrskin-0.3.0.pl01 for download + + +------------------------------------ cycle - cdrskin-0.3.1 - 2007.01.26.111920 + +2007.01.26.173236 [659] +libburn/file.h +libburn/file.c +Unified burn_file_source and burn_fd_source + +27 Jan 2007 [660] +libburn/null.c +Initialized member set_size of burn_source within burn_null_source_new() + +27 Jan 2007 [tag 661] +libburn_0_3_0_1/libburn/file.c +Removed 1.3 GB curbs for sources created by burn_file_source_new() + +29 Jan 2007 [662] +doc/cookbook.txt +Added a statement about blanking of DVD-RW + +2007.01.29.175822 [663] +libburn/transport.h +libburn/drive.c +libburn/write.c +libburn/mmc.c +Experiments about list of features and profiles + +2007.01.30.165317 [664] +libburn/write.c +libburn/mmc.c +Preparations for DVD-R[W] Sequential Recording + +2007.01.30.191740 [665] +libburn/write.c +libburn/mmc.c +First successful multi-session write to a sequential DVD-RW + +30 Jan 2007 [666] +doc/cookbook.txt +Added snapshot of emerging sequential DVD-R[W] cookbook + +2007.01.30.220220 [667] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Enabled Burnfree buffer underrun protection by default + +2007.01.31.130100 [668] +libburn/async.c +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Blank sequential DVD-RW, deformat overwriteable DVD-RW + +2007.01.31.173611 [669] +libburn/libburn.h +libburn/transport.h +libburn/drive.c +libburn/mmc.c +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Provisorily obtain multi-session -C parameters (violates MMC specs but works) + +2007.02.01.161634 [670] +libburn/libburn.h +libburn/transport.h +libburn/mmc.c +test/telltoc.c +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Obtain TOC from non-CD via 52h READ TRACK INFORMATION + +2007.02.01.163511 [671] +cdrskin/cdrskin.c +Reacted on justified compiler warning about unitialized sessions variable + +2007.02.01.191638 [672] +libburn/mmc.c +cdrskin/cdrskin.c +Allow blanking of DVD-RW which offer no Incremental Streaming + +1 Feb 2007 [673] +cdrskin/cdrskin_eng.html +cdrskin/README +cdrskin/cdrskin.1 +Prepared next cdrskin-0.3.1 cycle + +1 Feb 2007 [674] +cdrskin/changelog.txt +Prepared next cdrskin-0.3.1 cycle + +1 Feb 2007 [675] [676] +cdrskin/cdrskin.1 +Mentioned DVD-RW multi-session in overview of features + +------------------------------------ cycle - cdrskin-0.3.1 - 2007.02.01.203057 +* Burnfree enabled by default +* Multi-session recording on sequential DVD-RW, including -toc, -msinfo + + +2 Feb 2007 [677] +cdrskin/convert_man_to_html.sh +Some sed expressions for beautification + +2 Feb 2007 [678] +doc/cookbook.txt +Updated about DVD-R[W] blanking, multi-session info and TOC + +2007.02.02.151327 [679] +libburn/mmc.c +test/telltoc.c +Make mmc_read_multi_session_c1 use TOC if available + +2007.02.02.173345 [680] +libburn/mmc.c +cdrskin/cdrskin.c +Improved classification and TOC of finalized DVD-R[W] media + +------------------------------------ cycle - cdrskin-0.3.1 - 2007.02.02.183755 + +3 Feb 2007 [681] +cdrskin/cdrskin.1 +cdrskin/wiki_plain.txt +Mentioned DVD-RW multi-session + +2007.02.03.205526 (comitted 4 Feb 2007) [682] +libburn/libburn.h +libburn/mmc.c +libburn/drive.c +libburn/async.c +test/telltoc.c +New in API : struct burn_multi_caps and burn_disc_get_multi_caps() + +2007.02.05.132335 [683] +libburn/transport.h +libburn/mmc.c +Preparations for DVD-R[W] DAO + +2007.02.06.130410 [684] +libburn/libburn.h +libburn/mmc.c +libburn/write.c +libburn/drive.c +libburn/libdax_msgs.h +Implemented DVD-R[W] DAO as BURN_WRITE_SAO + +2007.02.06.170621 [685] +libburn/write.c +libburn/mmc.c +Beautification of debugging messages + +2007.02.06.174320 [686] +cdrskin/cdrskin.c +tao_to_sao_tsize= for DVD-R[W] DAO + +2007.02.06.185534 [687] +libburn/async.c +cdrskin/cdrskin.c +Enabled fast blank for DVD-R[W] + +6 Feb 2007 [688] +cdrskin/cdrskin.1 +Clarified CD and DVD peculiarities + +6 Feb 2007 [689] +doc/cookbook.txt +Described DVD-R[W] DAO mode + +------------------------------------ cycle - cdrskin-0.3.1 - 2007.02.06.200802 +* DVD-R[W] Disk-at-once writing + +6 Feb 2007 [690] +cdrskin/README +cdrskin/changelog.txt +cdrskin/cdrskin_eng.html +Next cdrskin-0.3.1 cycle + +2007.02.07.162836 [691] +libburn/libburn.h +libburn/drive.h +libburn/drive.c +libburn/options.c +libburn/libdax_msgs.h +New API function burn_write_opts_auto_write_type() + +7 Feb 2007 [692] +test/libburner.c +Made use of burn_write_opts_auto_write_type() + +7 Feb 2007 [693] +test/libburner.c +doc/comments +Updated documentation aspects + +8 Feb 2007 [694] +README +doc/comments +cdrskin/cdrskin.1 +cdrskin/README +Finally made tests with DVD-R. They burn indeed like DVD-RW. + +2007.02.08.210744 [695] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New option --prodvd_cli_compatible + +8 Feb 2007 [696] +cdrskin/wiki_plain.txt +cdrskin/cdrskin_eng.html +Mentioned DVD-R + +2007.02.08.225208 [697] +cdrskin/cdrskin.c +Silenced compiler warning + + +------------------------------------ cycle - cdrskin-0.3.1 - 2007.02.09.074058 + +9 Feb 2006 [698] +cdrskin/cdrskin_eng.html +Added special thanks towards Andy Polyakov + +9 Feb 2006 [699] +cdrskin/cdrskin.1 +doc/cookbook.txt +Small corrections in documentation + +10 Feb 2007 [tag 701] [705] +- cdrskin/add_ts_changes_to_libburn_0_3_0 +- cdrskin/add_ts_changes_to_libburn_0_3_1 ++ cdrskin/add_ts_changes_to_libburn_0_3_2 ++ cdrskin/add_ts_changes_to_libburn_0_3_3 +Updated cdrskin tarball generator + +2007.02.10.120001 [tag 702] [704] +Makefile.am +configure.ac +README +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/wiki_plain.txt +cdrskin/cdrskin_eng.html +Made number transition and activated development documentation + +10 Feb 2007 [tag 703] [707] +cdrskin/changelog.txt +Documented changes and 0.3.2 release timestamp + + +----------------------------- release - cdrskin-0.3.2.pl00 - 2007.02.10.120001 +* Burnfree enabled by default +* Multi-session recording on sequential DVD-R[W], including -toc, -msinfo +* DVD-R[W] Disk-at-once recording + + +10 Feb 2007 [706] +libburn/mmc.c +Added a comment about DVD-R + + +------------------------------------ cycle - cdrskin-0.3.3 - 2007.02.10.190528 + + +12 Feb 2007 [708] +cdrskin/cdrskin.1 +Being exacting about on-the-fly and DVD-RW + + +12 Feb 2007 [709] +cdrskin/cdrskin_eng.html +Updated list of keywords + +2007.02.12.142245 [710] +libburn/mmc.c +Made profile 0010h DVD-ROM suitable,full,not erasable. So it delivers a TOC. + + +2007.02.13.115459 [711] +libburn/spc.c +Set a suitable page 05h after spc_probe_write_modes() + +2007.02.13.143718 [712] +libburn/libburn.h +libburn/transport.h +libburn/mmc.c +libburn/spc.c +libburn/drive.c +Mew API function burn_disc_available_space() + +13 Feb 2007 [713] +test/libburner.c +Removed or updated outdated restriction statements + +13 Feb 2007 [714] +test/telltoc.c +Applied new API function burn_disc_available_space() + +2007.02.14.120213 [715] +libburn/spc.c +Removed outdated ifdef + +14 Feb 2007 [716] +test/telltoc.c +Set the advised write mode before inquiring media space + +2007.02.14.121440 [717] +libburn/libdax_msgs.h +libburn/mmc.c +Handle eventual ridiculously high d->last_track_no + +2007.02.14.122218 [718] +libburn/mmc.h +Forgotten part of revision 718 + +2007.02.14.202944 [719] +libburn/libburn.h +libburn/options.h +libburn/options.c +libburn/structure.h +libburn/structure.c +libburn/write.c +libburn/drive.c +Optional padding up to full media size when closing (incomplete yet) + +2007.02.14.203635 [720] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New options --fill_up_media and --tell_media_space + +2007.02.15.201506 [722] +libburn/options.c +libburn/drive.c +cdrskin/cdrskin.c +libburn/write.c +Took fill_up_media into respect with automatic write mode decisions + +2007.02.15.201651 [723] +libburn/transport.h +libburn/mmc.c +libburn/libdax_msgs.h +Installed a guardian for predicted track end + +2007.02.15.201757 [724] +libburn/structure.c +Corrected bug about open_ended filluped tracks + +15 Feb 2007 [725] +libburn/libburn.h +cdrskin/cdrskin.1 +Clarifications about current state of fillup + +2007.02.15.203448 [726] +libburn/write.c +Repaired debugging message spoiled by uninitialized variable + +2007.02.16.111941 [728] +libburn/write.c +Corrected CD TAO bug introduced with DVD bug fix 724 and CD SAO change 655 + +2007.02.17.085118 [729] +libburn/structure.c +Another bug fix for revision 724 + +2007.02.17.085533 [730] +libburn/async.c +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +test/libburner.c +Allowed forceful blanking of blank media in burn_disc_erase() + +17 Feb 2007 [731] +test/libburner.c +Removed unprecise unnecessary comment + + +------------------------------------ cycle - cdrskin-0.3.3 - 2007.02.17.121238 +* New option --tell_media_space tells the maximum size for the next burn + + +2007.02.18.094414 [732] +libburn/libburn.h +Clarified usage comment with burn_drive_info_free() (see ticket 98) + +18 Feb 2007 [733] +cdrskin/cdrskin.1 +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Next cdrskin-0.3.3 cycle + + +2007.02.18.094858 [734] +libburn/mmc.h +Adjusted maximum realistic number of tracks to MMC specs + +2007.02.19.184132 [735] +cdrskin/cdrskin.c +Repaired slightly broken pacifier track size display with -audio + +2007.02.19.225102 [736] +libburn/libburn.h +libburn/async.c +libburn/structure.h +libburn/structure.c +libburn/write.h +libburn/write.c +libburn/drive.h +libburn/drive.c +libburn/options.c +libburn/libdax_msgs.h +Re-arranged checking and defaulting of write parameters += +New API function burn_track_set_default_size() +New API function burn_precheck_write() +Make wide use of burn_disc_write_mode_demands() + +2007.02.21.205244 [737] +libburn/libburn.h +libburn/async.c +libburn/drive.c +libburn/options.c +libburn/structure.c +libburn/write.c +libburn/libdax_msgs.h +cdrskin/cdrskin.c +Moved tao_to_sao_tsize into libburn, let cdrskin use auto_write_type and precheck + +21 Feb 2007 [738] +cdrskin/add_ts_changes_to_libburn_0_3_3 +cdrskin/add_ts_changes_to_libburn_0_3_2 +Added -O2 to binary production + +2007.02.22.072700 [739] +libburn/libburn.h +libburn/drive.c +libburn/options.c +Re-enabled overwriteable pseudo-pseudo-SAO with unpredicted track size + +2007.02.22.073157 [740] +libburn/mmc.c +Disabled debugging messages about format descriptors + +2007.02.22.094939 [741] +libburn/libburn.h +libburn/options.c +libburn/write.c +libburn/async.c +test/libburner.c +cdrskin/cdrskin.c +Macro for length of rejection reasons string (old size is still safe) + +2007.02.22.113016 [742] +libburn/libburn.h +libburn/drive.c +Made burn_disc_available_space() take into respect burn_write_opts_set_start_byte() + +2007.02.23.190937 [743] +libburn/libburn.h +libburn/drive.c +libburn/mmc.c +libburn/write.c +doc/cookbook.txt +Enabled DVD-R/DL Sequential via burn_allow_untested_profiles() + +2007.02.23.191117 [744] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Enabled DVD-R/DL Sequential via --allow_untested_media_types + +2007.02.23.193427 [745] +libburn/init.c +Forgotten source file for revision 743 + +------------------------------------ cycle - cdrskin-0.3.3 - 2007.02.24.102731 + + +2007.02.25.112733 [746] +libburn/write.h +libburn/write.c +libburn/drive.c +cdrskin/cdrskin.1 +Took into respect deliberate lack of DVD-R/DL multi session capability + +2007.03.01.120945 [747] +libburn/drive.c +libburn/mmc.c +libburn/write.c +cdrskin/cdrskin.c +doc/cookbook.txt +Preparations for supporting DVD+R[/DL] + +3 Mar 2007 [748] +cdrskin/cdrskin.1 +Updated DVD-R[W] write mode description + +2007.03.03.141240 [749] +libburn/transport.h +libburn/mmc.c +Determine physical interface SCSI,ATA,SATA,USB,... (for future use) + +2007.03.03.141435 [750] +libburn/libburn.h +libburn/write.c +libburn/options.h +libburn/options.c +cdrskin/cdrskin.c +Re-enabled -force with write modes which seem unavailable + +2007.03.03.151812 [751] +libburn/options.c +Fixed bug introduced with rev 736ff which prevented audio CD burning + +2007.03.04.184720 [752] +cdrskin/cdrskin.c +cdrskin/cdrfifo.c +Fifo got stuck if sum of processed track sizes was exactly aligned to fifo size + +------------------------------------ cycle - cdrskin-0.3.3 - 2007.03.04.185202 +* Bug fix: Multi-track runs with fifo could stall in rare cases + +5 Mar 2007 [753] +cdrskin/cdrskin_eng.html +cdrskin/add_ts_changes_to_libburn_0_3_2 +Released cdrskin-0.3.2.pl01 + +2007.03.06.195203 [754] +libburn/mmc.c +libburn/write.c +Enabled DVD+R, DVD+R DL via --allow_untested_media_types, always -multi for now + +2007.03.06.205312 [755] +libburn/mmc.c +cdrskin/cdrskin.1 +doc/cookbook.txt +doc/comments +Enabled DVD+R as tested media (-multi is still always on) + +2007.03.07.151514 [756] +libburn/write.c +cdrskin/cdrskin.1 +cdrskin/README +doc/cookbook.txt +Some adjustments for DVD+R recording + +2007.03.07.151514 [756] +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Next cdrskin-0.3.3 cycle + +------------------------------------ cycle - cdrskin-0.3.3 - 2007.03.07.155750 +* Multi-session burning to DVD+R + + +8 Mar 2007 [757] +cdrskin/cdrskin.1 +cdrskin/convert_man_to_html.sh +cdrskin/wiki_plain.txt +Polished documentation + +2007.03.09.134622 [758] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New option assert_write_lba= + +10 Mar 2007 [759] +cdrskin/cdrskin.1 +cdrskin/wiki_plain.txt +Polished documentation + + +------------------------------------ cycle - cdrskin-0.3.3 - 2007.03.10. +* New option assert_write_lba= + +2007.03.12.110001 [tag 761] +Makefile.am +configure.ac +README +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.3.4 and activated development documentation + +12 Mar 2007 [tag 762] +- cdrskin/add_ts_changes_to_libburn_0_3_2 +- cdrskin/add_ts_changes_to_libburn_0_3_3 ++ cdrskin/add_ts_changes_to_libburn_0_3_4 ++ cdrskin/add_ts_changes_to_libburn_0_3_5 +Updated cdrskin tarball generator + +12 Mar 2007 [tag 763] +cdrskin/changelog.txt +Documented most recent changes + +12 Mar 2007 [tag 764] +README +Removed redundant sentence + +----------------------------- release - cdrskin-0.3.4.pl00 - 2007.03.12.110001 +* Multi-session burning to DVD+R +* New option --tell_media_space tells the maximum size for the next burn +* New option assert_write_lba= +* Bug fix: Multi-track runs with fifo could stall in rare cases + + +2007.03.12.155600 [765] +Makefile.am +configure.ac +README +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.3.5 + +12 Mar 2007 [766] +- cdrskin/add_ts_changes_to_libburn_0_3_2 +- cdrskin/add_ts_changes_to_libburn_0_3_3 ++ cdrskin/add_ts_changes_to_libburn_0_3_4 ++ cdrskin/add_ts_changes_to_libburn_0_3_5 +Updated cdrskin tarball generator + + +------------------------------------ cycle - cdrskin-0.3.5 - 2007.03.12.160658 + +14 Mar 2007 [767] +cdrskin/cdrskin_eng.html +Corrected truncated sentence and file sizes + +14 Mar 2007 [768] +cdrskin/changelog.txt +Next cdrskin-0.3.5 cycle + +2007.03.14.133618 [769] +libburn/libburn.h +libburn/init.c +libburn/sg-linux.c +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New option drive_scsi_dev_family=sr|scd|sg + +2007.03.15.194531 [770] +libburn/drive.c +React properly on drive stating that it cannot write any media + +2007.03.15.195005 [771] +libburn/spc.h +libburn/spc.c +libburn/sbc.c +After loading tray wait for unit to become ready or to report some clear error + +2007.03.15.195251 [772] +cdrskin/cdrskin.c +Moved manual device family decision to a sufficiently early stage + +2007.03.15.195428 [773] +libburn/mmc.c +Kept mmc_get_configuration() from believing the announcement of 1 GB reply + +2007.03.15.195534 [774] +libburn/sg-linux.c +Trying to recognize kernel >= 2.6 and use /dev/sr by default + +15 Mar 2007 [775] +cdrskin/cdrskin.1 +Updated drive_scsi_dev_family= + +15 Mar 2007 [776] +cdrskin/changelog.txt +Next cdrskin-0.3.5 cycle + +------------------------------------ cycle - cdrskin-0.3.5 - 2007.03.16.001311 +* Usage of /dev/srN rather than /dev/sgN on Linux >= 2.6 +* New option drive_scsi_dev_family=sr|scd|sg + + +18 Mar 2007 [777] +cdrskin/cdrskin_eng.html +Mentioned new sr behavior + +2007.03.24.093238 [785] +libburn/drive.c +Fixed bug with burn_disc_available_space(...,NULL) + +2007.03.24.093623 [786] +cdrskin/cdrskin.c +Warning of very small tsize= settings. (Proposal by Eduard Bloch) + +2007.03.27.213543 [787] +cdrskin/cdrskin.c +cdrskin/cdrfifo.h +cdrskin/cdrfifo.c +Preparations for option -isosize via fifo (only a debug message yet) + +2007.03.28.100934 [788] +libburn/libburn.h +libburn/structure.c +cdrskin/cdrskin.c +Enabled -isosize for first track by help of fifo and without seeking + +2007.03.28.111739 [789] +cdrskin/cdrskin.c +Silenced error condition about -sao with stdin and -isosize + +2007.03.28.160503 [790] +cdrskin/cdrskin.c +Enabled -isosize with S_IFREG or S_IFBLK files and without fifo + +2007.03.28.182419 [791] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Made fifo based -isosize read 64k first and the rest only at normal stage + +2007.03.28.202802 [792] +cdrskin/cdrskin.c +Silenced error message if tsize= is smaller than source is willing to deliver + + +------------------------------------ cycle - cdrskin-0.3.5 - 2007.03.28.212322 +* Option -isosize is supported now + + +29 Mar 2007 [793] +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Next cdrskin-0.3.5 cycle + +2007.03.30.201034 [794] +libburn/write.c +cdrskin/cdrskin.1 +Allowed finalizing of DVD+R + +2007.03.30.214657 [795] +libburn/write.c +Avoided unconditional finalizing of DVD+R + +2007.04.03.140356 [796] +libburn/sg-linux.c +Added fcntl() locking to O_EXCL locking + +2007.04.03.145637 [797] +cdrskin/cdrskin.c +Make --old_pseudo_scsi_adr -scanbus work with any drive_scsi_dev_family= + +2007.04.03.145806 [798] +libburn/libdax_msgs.h +Added fcntl() locking to O_EXCL locking + +2007.04.04.184341 [799] +libburn/libburn.h +libburn/init.c +libburn/sg-linux.c +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New cdrskin options --drive_not_f_setlk and --drive_not_o_excl + +6 Apr 2007 [801] +test/libburner.c +Updated media list in introduction comment + +2007.04.09.105543 [802] +libburn/transport.h +libburn/os-linux.h +libburn/sg-linux.c +cdrskin/cdrskin.1 +Cleaned up scsi sibling management, sketched grafting of DDLP + +2007.04.09.111215 [803] +libburn/sg-linux.c +Reacted on compiler warning about last_rdev, fixed fresh typo bug + + +------------------------------------ cycle - cdrskin-0.3.5 - 2007.04.09.112127 +* DVD+R now get finalized (if not -multi is given) + + +10 Apr 2007 [804] +cdrskin/changelog.txt +Next cdrskin-0.3.5 cycle + +2007.04.10.081855 [805] +libburn/init.c +Fixed bug with drive_scsi_dev_family= introduced by revision 796 (fcntl lock) + +2007.04.10.082229 [806] +libburn/sg-linux.c +Used O_EXCL|O_RDWR and fcntl() even within sg_obtain_scsi_adr() + +2007.04.10.083119 [807] +libburn/sg-linux.c +Fixed bug in sg_open_scsi_siblings() introduced with revision 802 + +2007.04.10.144840 [808] +libburn/spc.c +libburn/libdax_msgs.h +Avoided SIGSEGV with an old SCSI CD-ROM drive and its wild replies + +12 Apr 2007 [810] +README +Clarified license. People who object shall please come forward now. + +2007.04.13.171347 [815] +libburn/sg-linux.c +libburn/libdax_msgs.h +Switched from O_NONBLOCK to O_NDELAY (see http://lkml.org/lkml/2007/4/11/141) + +2007.04.13.173008 [816] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Made use of fcntl(F_SETLK) switchable (and thus became more free with default) + +15 Apr 2007 [819] ++ libburn/ddlpa.h ++ libburn/ddlpa.c +Began test implementation of DDLP-A + +15 Apr 2007 [820] +libburn/ddlpa.h +libburn/ddlpa.c +Implemented ddlpa_lock_btl() + +16 Apr 2007 [821] +doc/cookbook.txt +Finalized DVD+R cookbook + +16 Apr 2007 [822] ++ doc/ddlp.txt +Emerging description of DDLP + +16 Apr 2007 [823] +doc/ddlp.txt +libburn/ddlpa.h +libburn/ddlpa.c +Polished txt and finally threw out getter functions + +16 Apr 2007 [824] +libburn/ddlpa.h +Corrected description of return values + +18 Apr 2007 [825] ++ test/open-cd-excl.c +Program for probing access to device files. By Ted Ts'o with modifications by me. + +18 Apr 2007 [826] +doc/ddlp.txt +Allowed for Friendly Programs: O_EXCL | O_RDONLY + +18 Apr 2007 [827] +libburn/ddlpa.h +libburn/ddlpa.c +Progress due to tests with test/open-cd-excl + +2007.04.18.103734 [828] +libburn/mmc.c +Updated comments about DVD+R + +18 Apr 2007 [829] +test/open-cd-excl.c +libburn/ddlpa.c +doc/ddlp.txt +Polished messages, comments and description of DDLP-A + +18 Apr 2007 [830] +test/open-cd-excl.c +doc/ddlp.txt +Adaptations to new test results and discussions + +18 Apr 2007 [831] +doc/ddlp.txt +Corrected a list of standard paths + +19 Apr 2007 [832] [833] +doc/ddlp.txt +Clarifications about motivation and duties of the participants + +20 Apr 2007 [834] +doc/ddlp.txt +Beginning to develop DDLP-B + +21 Apr 2007 [835] +doc/ddlp.txt +Declared failure of DDLP to entirely solve the concurrency problem + +------------------------------------ cycle - cdrskin-0.3.5 - 2007.04.22.112236 + +22 Apr 2007 [836] +cdrskin/changelog.txt +Next cdrskin-0.3.5 cycle + +22 Apr 2007 [837] +cdrskin/add_ts_changes_to_libburn_0_3_5 +cdrskin/add_ts_changes_to_libburn_0_3_4 +Repaired autotools bootstrap bug by help of sed + +22 Apr 2007 [838] +cdrskin/cdrskin.1 +Changed many /dev/sg to /dev/sr + + +2007.04.23.130001 [tag 841] +Makefile.am +configure.ac +README +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/wiki_plain.txt +cdrskin/cdrskin_eng.html +doc/comments +Made number transition and activated development documentation + +23 Apr 2007 [tag 842] +- cdrskin/add_ts_changes_to_libburn_0_3_4 +- cdrskin/add_ts_changes_to_libburn_0_3_5 ++ cdrskin/add_ts_changes_to_libburn_0_3_6 ++ cdrskin/add_ts_changes_to_libburn_0_3_7 +Updated cdrskin tarball generator + +23 Apr 2007 [tag 844] +cdrskin/README +Corrected false info about outdated addressing method + +2007.04.23.154600 [843] +Makefile.am +configure.ac +README +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/wiki_plain.txt +cdrskin/cdrskin_eng.html +doc/comments +Made number transition and activated development documentation + +23 Apr 2007 [845] +- cdrskin/add_ts_changes_to_libburn_0_3_4 +- cdrskin/add_ts_changes_to_libburn_0_3_5 ++ cdrskin/add_ts_changes_to_libburn_0_3_6 ++ cdrskin/add_ts_changes_to_libburn_0_3_7 +Updated cdrskin tarball generator + + +----------------------------- release - cdrskin-0.3.6.pl00 - 2007.04.23.130001 +* Usage of /dev/srN rather than /dev/sgN on Linux >= 2.6 +* New option drive_scsi_dev_family=sr|scd|sg +* Option -isosize is supported now +* DVD+R now get finalized (if not -multi is given) + + +2007.04.23.171735 [846] +cdrskin/changelog.txt +Next cdrskin-0.3.7 cycle + + +------------------------------------ cycle - cdrskin-0.3.7 - 2007.04.24.113310 + +2007.05.21.184334 [853] +libburn/sg-linux.c +Prepared fflushing and stderr output of SCSI command log + +2007.05.21.185450 [854] +libburn/sbc.c +libburn/spc.h +libburn/spc.c +libburn/mmc.c +libburn/toc.c +libburn/transport.h +For Linux 2.4, USB : Carefully avoided to inquire more data than available + +2007.05.21.185644 [855] +libburn/sector.c +libburn/write.c +For Linux 2.4, USB audio : Reduced CD output buffer size to 32 kiB + +21 May 2007 [856] +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Next cdrskin-0.3.7 cycle + + +------------------------------------ cycle - cdrskin-0.3.7 - 2007.05.21.201501 +* Now able to cope with the peculiarities of Linux 2.4 USB + + +2007.05.22.154407 [857] +libburn/sg-linux.c +Report eventual sg_io_hdr_t host_status,driver_status as debug messages + +2007.05.22.154504 [858] +cdrskin/cdrskin.c +Disabled macro Cdrskin_debug_libdax_msgS. Thus getting unqueued error messages. + +2007.05.22.164850 [859] +libburn/sg-linux.c +Added SCSI opcode to output of revision 857 + +2007.05.28.132412 [860] +libburn/os-linux.h +libburn/write.c +Moved general 32 kiB buffer restriction from write.c to os-linux.h + +2007.05.28.165630 [861] +libburn/libburn.h +libburn/drive.c +test/telltoc.c +Extended struct burn_multi_caps by .might_simulate + +28 May 2007 [862] +libburn/libdax_msgs.h +Forgotten update of error list with revison 857 + +2007.05.28.170243 [863] +libburn/options.c +cdrskin/cdrskin.1 +Added check for .might_simulate to burn_write_opts_auto_write_type() + +2007.05.28.192421 [864] +libburn/sector.c +Fixed low transaction size introduced by cooperation of revisions 855 and 860 + + +------------------------------------ cycle - cdrskin-0.3.7 - 2007.05.28.192853 +* Refusal to perform -dummy runs on media which cannot simulate burning + +29 May 2007 [865] +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Next cdrskin-0.3.7 cycle + +8 Jun 2007 [873] +- cdrskin/doener_150x200_tr.gif +- cdrskin/doener_150x200_tr_octx.gif ++ cdrskin/doener_150x200_tr.png ++ cdrskin/doener_150x200_tr_octx.png +cdrskin/cdrskin_eng.html +Changed logo graphics format from GIF to PNG + +8 Jun 2007 [874] +cdrskin/wiki_plain.txt +cdrskin/add_ts_changes_to_libburn_0_3_6 +cdrskin/add_ts_changes_to_libburn_0_3_7 +Took into respect change of logo graphics format + +8 Jun 2007 [875] +cdrskin/cdrskin.1 +Prevented macro interpretation of text snippet ".wav" + +10 Jun 2007 [876] +cdrskin/README +cdrskin/cdrskin.1 +Clarified MB to MiB if compatibility allows it (ticket 100) + +10 Jun 2007 [877] +cdrskin/cdrskin.1 +doc/cookbook.txt +test/libburner.c +Changed "KB" to "kiB" + +------------------------------------ cycle - cdrskin-0.3.7 - 2007.06.10.081025 + + +2007.07.12.162856 [882] +libburn/transport.h +libburn/mmc.c +libburn/write.c +libburn/libdax_msgs.h +Preparations to avoid writing which will not fit in drive buffer + +2007.07.12.171727 [883] +libburn/libburn.h +libburn/drive.c +New API-Function burn_drive_set_buffer_waiting() + +2007.07.12.171832 [884] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New options modesty_on_drive= and minbuf= + +2007.07.14.111614 [885] +libburn/libburn.h +libburn/drive.c +New API function burn_drive_get_best_speed() + +2007.07.14.112029 [886] +libburn/mmc.c +Only set realistic maximum DVD speeds (for my LG GSA which fails otherwise) + +2007.07.14.112326 [887] +cdrskin/cdrskin.c +Experimental option --adjust_speed_to_drive. Caution: May vanish soon. + +17 Jul 2007 [888] +cdrskin/cdrskin.1 +Clarification on option speed= + +2007.07.17.085823 [889] +libburn/libburn.h +libburn/drive.c +libburn/mmc.c +cdrskin/cdrskin.c +Implemented minimum speed in burn_drive_set_speed() + +2007.07.19.072434 [890] +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Removed ban against speed 0 with burn_drive_set_buffer_waiting() + +2007.07.19.143139 [891] +cdrskin/cdrskin.c +Trying to prevent usage of burn drive as track source + +2007.07.19.171947 [892] +cdrskin/cdrskin.c +Avoided new track-drive test with option --no_convert_fs_adr + +19 Jul 2007 [893] +cdrskin/cdrskin.1 +Documented option --adjust_speed_to_drive (i.e. it will stay) + +19 Jul 2007 [894] +cdrskin/changelog.txt +Next cdrskin-0.3.7 cycle + +19 Jul 2007 [895] +cdrskin/cdrskin_eng.html +Next cdrskin-0.3.7 cycle + +------------------------------------ cycle - cdrskin-0.3.7 - 2007.07.19.174859 +* New option modesty_on_drive= may help with hda -> hdb burns +* New option minbuf= , cdrecord compatible frontend of modesty_on_drive= +* New option --adjust_speed_to_drive +* Precautions against using the burner drive as track source + +2007.07.20.120001 [branch 897] +Makefile.am +configure.ac +README +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.3.8 + +20 Jul 2007 [branch 898] +- cdrskin/add_ts_changes_to_libburn_0_3_6 +- cdrskin/add_ts_changes_to_libburn_0_3_7 ++ cdrskin/add_ts_changes_to_libburn_0_3_8 ++ cdrskin/add_ts_changes_to_libburn_0_3_9 +Updated cdrskin tarball generators + +20 Jul 2007 [branch 899] +cdrskin/changelog.txt +Documented changes and release timestamp + +2007.07.20.210001 [900] +Makefile.am +configure.ac +README +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.3.9 + +20 Jul 2007 [901] +- cdrskin/add_ts_changes_to_libburn_0_3_6 +- cdrskin/add_ts_changes_to_libburn_0_3_7 ++ cdrskin/add_ts_changes_to_libburn_0_3_8 ++ cdrskin/add_ts_changes_to_libburn_0_3_9 +Updated cdrskin tarball generators + +20 Jul 2007 [902] +cdrskin/changelog.txt +Documented changes + +----------------------------- release - cdrskin-0.3.8.pl00 - 2007.07.20.120001 +* Now able to cope with the peculiarities of Linux 2.4 USB +* Refusal to perform -dummy runs on media which cannot simulate burning +* New option modesty_on_drive= may help with hda -> hdb burns +* New option minbuf= , cdrecord compatible frontend of modesty_on_drive= +* New option --adjust_speed_to_drive +* Precautions against using the burner drive as track source + +------------------------------------ cycle - cdrskin-0.3.9 - 2007.07.20.200422 + +2 Aug 2007 [917] +cdrskin/cdrskin_eng.html +Corrected a harmless typo + +2 Aug 2007 [918] +doc/cookbook.txt +cdrskin/cdrskin.1 +Took into respect new info from Andy Polyakov about closing DVD+-R + +9 Aug 2007 [920] +cdrskin/cdrskin_eng.html +Corrected a dead link + +2007.08.09.133137 [921] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Allowed speed=any + +2007.08.09.133259 [922] +libburn/transport.h +Changed "unsigned" to "unsigned int" + +2007.08.09.133420 [923] +libburn/libdax_msgs.h +Corrected a typo + +9 Aug 2007 [924] ++ doc/libdax_model.txt ++ doc/libdax_overview.gif ++ doc/libdax_equip.gif ++ doc/libdax_job.gif +Obscure backup of my unripe model ideas about libcevap (former libdax) + +10 Aug 2007 [936] +doc/libdax_model.txt +Fiddled on the model attributes + +2007.08.10.201450 [937] +libburn/libburn.h +Updated comments about supported profiles and media types + +2007.08.10.203040 [938] +configure.ac +Changed "libburn-1.pc" to "libburn-5.pc" to re-enable ./bootstrap ; ./configure + +2007.08.11.075330 [941] +cdrskin/README +cdrskin/add_ts_changes_to_libburn_0_3_9 +cdrskin/cdrskin.c +cdrskin/cdrskin_eng.html +cdrskin/wiki_plain.txt +doc/comments +doc/cookbook.txt +test/libburner.c +test/telltoc.c +Reflected recent URL changes to libburnia-project.org + +2007.08.11.202027 [942] +libburn/libburn.h +libburn/write.c +libburn/libdax_msgs.h +New API function burn_random_access_write() + +2007.08.11.202627 [943] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New option direct_write_amount= using new API call burn_random_access_write() + +12 Aug 2007 [944] +libburn/libburn.h +Clarifications about burn_random_access_write() + +2007.08.12.095446 [945] +libburn/write.c +libburn/libdax_msgs.h +Checked in burn_random_access_write() wether drive is grabbed + +2007.08.12.095623 [946] +cdrskin/cdrskin.c +Debug message explaining why burn_drive_convert_fs_adr() acts on track source + +2007.08.12.152937 [947] +libburn/libburn.h +libburn/transport.h +libburn/mmc.h +libburn/mmc.c +libburn/write.c +libburn/read.c +libburn/libdax_msgs.h +New API function burn_read_data() + +12 Aug 2007 [948] +test/telltoc.c +Testing burn_read_data() by option --read_and_print + +------------------------------------ cycle - cdrskin-0.3.9 - 2007.08.12.161808 +* New option direct_write_amount= + +13 Aug 2007 [949] +cdrskin/changelog.txt +Next cdrskin-0.3.9 cycle + +13 Aug 2007 [950] +cdrskin/cdrskin.1 +cdrskin/cdrskin_eng.html +Some polishing about option direct_write_amount= + +13 Aug 2007 [951] +doc/libdax_model.txt +Beautified implementation names and added some more attributes + +15 Aug 2007 [952] +doc/libdax_model.txt +Added more attributes and distinguished read-write, read-only, private ones + +2007.08.17.081901 [953] +libburn/transport.h +Corrected harmless type declaration flaws + +17 Aug 2007 [954] +doc/libdax_model.txt +Added more attributes and comments + +19 Aug 2007 [955] ++ libcevap/ ++ libcevap/cgen.c ++ libcevap/cgen.h ++ libcevap/ctyp.c ++ libcevap/ctyp.h ++ libcevap/smem.c ++ libcevap/smem.h ++ libcevap/cgen.txt +The C code generator mentioned in doc/libdax_model.txt. See there. + +19 Aug 2007 [956] +doc/libdax_model.txt +More comments, new capabilities of C code generator + +19 Aug 2007 [957] ++ libcevap/libcevap_gen.sh ++ libcevap/extract_cgen_input.sh ++ libcevap/main.c +doc/libdax_model.txt +Generator frontend scripts (./libcevap_gen.sh to be run in libcevap/) + +19 Aug 2007 [958] +doc/libdax_model.txt +Corrected description of compiling and generating + +20 Aug 2007 [959] +libcevap/cgen.c +Corrected a bug about inclusion of cevap*.h files + +21 Aug 2007 [960] +libcevap/smem.h +Added some function type declarations + +2007.08.22.134731 [961] +libburn/mmc.c +Corrected dangerous typo with error message production of mmc_read_10() + +22 Aug 2007 [962] +test/telltoc.c +Retrieving my old backups which are hit by the Linux Read-Ahead-Bug + +22 Aug 2007 [963] +libburn/read.c +test/telltoc.c +Avoiding libburn read-ahead-bug + +22 Aug 2007 [964] +test/telltoc.c +Avoided locked drive with interrupted telltoc read. (eject unlocks anyway) + +2007.08.22.173459 [965] +libburn/libburn.h +libburn/drive.c +libburn/read.c +libburn/write.c +Taking synchronous read/write into respect with abort handling + +2007.08.23.150423 [966] +libburn/libburn.h +libburn/mmc.c +libburn/read.c +Allowed to suppress error message from failed burn_read_data() + +23 Aug 2007 [967] +test/telltoc.c +Try to read last 2 blocks of CD track without eventual error message + +2007.08.25.085709 [968] +libburn/libburn.h +libburn/mmc.c +libburn/file.c +libburn/source.c +libburn/structure.c +Corrected memory management flaws found by Joris Dobbelsteen + +25 Aug 2007 [969] +test/telltoc.c +Reacted on false compiler warning about potentially unused variable + +2007.08.25.155931 [970] +libburn/mmc.c +libburn/file.c +More memory management changes proposed by Joris Dobbelsteen + +2007.08.26.200829 [971] +cdrskin/cdrskin.c +cdrskin/cdrfifo.h +cdrskin/cdrfifo.c +cdrskin/cdrskin.1 +New cdrskin option --grow_overwriteable_iso + +2007.08.28.143057 [974] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Made program behavior with --grow_overwriteable_iso more consistent + +28 Aug 2007 [975] +cdrskin/README +Mentioned --grow_overwriteable_iso + +29 Aug 2007 [976] +doc/libdax_model.txt +Work goes on + +29 Aug 2007 [977] +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Next cdrskin-0.3.9 cycle + +------------------------------------ cycle - cdrskin-0.3.9 - 2007.08.29.124057 +* New option --grow_overwriteable_iso + +29 Aug 2007 [978] +cdrskin/compile_cdrskin.sh +Added missing file to link list: read.o + +29 Aug 2007 [979] +- doc/libdax_model.txt +- doc/libdax_overview.gif +- doc/libdax_job.gif +- doc/libdax_equip.gif ++ libcevap/libdax_model.txt ++ libcevap/libdax_overview.gif ++ libcevap/libdax_job.gif ++ libcevap/libdax_equip.gif +Moved libdax-libcevap model stuff to libcevap/ + +29 Aug 2007 [980] +libcevap/libcevap_gen.sh +Adapted C code generator script to new address of libdax_model.txt + +2007.09.01.182319 [984] +libburn/libburn.h +libburn/options.c +New API function burn_write_opts_get_drive() + +2007.09.04.224905 [987] +libburn/libburn.h +libburn/transport.h +libburn/async.c +libburn/drive.c +libburn/write.c +libburn/read.c +libburn/options.c +libburn/spc.c +libburn/libdax_msgs.h +New API calls burn_drive_grab_dummy(), burn_drive_get_drive_role() + +5 Sep 2007 [988] +test/telltoc.c +test/libburner.c +Testing new API functions via --drive stdio: + +2007.09.04.225558 [989] +libburn/write.c +libburn/read.c +Reacted on compiler warnings + +2007.09.05.194124 [991] +libburn/libburn.h +libburn/drive.c +test/libburner.c +test/telltoc.c +burn_drive_grab_dummy() becomes invisible part of burn_drive_scan_and_grab() + +2007.09.05.195248 [992] +libburn/write.c +Fixed a bug with failed opening of pseudo-drive + +2007.09.06.094402 [995] +libburn/drive.c +Added forgotten handling of pseudo-drives in burn_drive_grab() + +2007.09.06.095954 [996] +libburn/write.c +Added forgotten read/write counters in burn_stdio_write_track() + +2007.09.06.100100 [997] +cdrskin/cdrskin.c +Removed obstacles for use of stdio-drives + +2007.09.06.120844 [999] +libburn/libburn.h +libburn/drive.c +Promoted burn_drive_raw_get_adr() to API function burn_drive_d_get_adr() + +2007.09.07.102728 [1002] +libburn/write.c +Corrected write counter in burn_stdio_write_track() + +2007.09.07.123748 [1003] +libburn/libburn.h +libburn/async.c +libburn/drive.c +libburn/libdax_msgs.h +Made burn_drive_info_free() only delete the drive of its parameter + +7 Sep 2007 [1004] +test/telltoc.c +Reacted on changed media profile of stdio-drives + +2007.09.07.154951 [1005] +libburn/drive.h +libburn/drive.c +libburn/async.c +libburn/init.c +Made burn_drive_scan() refuse work on non-empty drive list. + +2007.09.07.163949 [1006] +libburn/async.c +libburn/drive.c +Corrected memory leak introduced by revision 1005 + +2007.09.07.164532 [1007] +libburn/drive.h +Forgotten file for revision 1006 + +2007.09.07.184631 [1008] +libburn/init.c +libburn/libdax_msgs.h +Avoided locked tray after failed burn_finish() because of busy drive + +7 Sep 2007 [1009] +test/telltoc.c +Lowered report severity to LIBDAX_MSGS_SEV_WARNING. + +2007.09.07.190916 [1010] +libburn/libburn.h +libburn/drive.c +libburn/libdax_msgs.h +Made burn_drive_scan_and_grab() extend the drive list rather than replacing it + +2007.09.07.234122 [1011] +libburn/drive.c +cdrskin/cdrskin.c +Report media profile in cdrskin blank, format, burn runs + +2007.09.07.234704 [1012] +libburn/drive.c +Bug fix about stdio: + +2007.09.08.102151 [1013] +libburn/drive.c +Fixed memory leak and possible SIGSEGV with pseudo-drives + +2007.09.08.102620 [1014] +cdrskin/cdrskin.c +Made cdrskin work with null-drive (which it mistook for something like ATA:) + +2007.09.08.132058 [1015] +libburn/drive.c +libburn/write.c +Allowed -dummy burns with stdio-drives (because /dev/null is no block device) + +2007.09.08.132206 [1016] +cdrskin/cdrskin.c +Changed speed measurement of stdio-drives to DVD 1x units + +8 Sep 2007 [1017] +libburn/libburn.h +Documentation of stdio-drives + +2007.09.08.164924 [1018] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/convert_man_to_html.sh +New option --allow_emulated_drives + +2007.09.08.174757 [1019] +libburn/libburn.h +libburn/async.c +libburn/write.c +Made Libburn_precheck_write_ruleS unconditional code + +8 Sep 2007 [1020] +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Next cdrskin-0.3.9 cycle + +------------------------------------ cycle - cdrskin-0.3.9 - 2007.09.08.212130 +* New option --allow_emulated_drives dev=stdio: + + +2007.09.09.093535 [1021] +libburn/drive.c +libburn/libdax_msgs.h +cdrskin/cdrskin.1 +Called statvfs() for size estimation of regular stdio-files. + +2007.09.09.093637 [1022] +cdrskin/cdrskin.c +Fixed bug with dev=stdio: where path contains a digit + +2007.09.09.133136 [1023] +libburn/init.c +libburn/sg.h +libburn/sg-linux.c +libburn/sg-freebsd-port.c +libburn/sg-freebsd.c +Enabled os dependend stdio size estimation + +2007.09.09.133922 [1024] +libburn/drive.c +Made use of os dependend stdio size estimation + +2007.09.09.182827 [1026] +libburn/async.c +libburn/mmc.c +Ended falsely alleged erasability of DVD-RAM and DVD+RW + +2007.09.10.110050 [1028] +libburn/drive.c +Added forgotten return 0 to an error case + +2007.09.12.104626 [1032] +libburn/transport.h +libburn/libdax_msgs.h +libburn/write.c +libburn/drive.c +libburn/spc.c +libburn/sbc.c +libburn/mmc.c +libburn/sg-linux.c +libburn/sg-freebsd.c +Brought burn_stdio_write_track() onto sector_data() for outmost realism + +2007.09.12.115850 [1033] +libburn/mmc.c +libburn/spc.c +Reacted on compiler -O2 warnings + +2007.09.12.115946 [1034] +libburn/write.c +Implemented realistic speed simulation with stdio-drives + +2007.09.12.195206 [1039] +libburn/write.c +Implemented cache syncing for stdio-drives in burn_random_access_write() + +2007.09.12.200106 [1040] +cdrskin/cdrskin.c +Fixed bug with direct_write_amount=0 + +2007.09.14.122437 [1063] +libburn/write.c +Took into respect time granularity with stdio speed control + +2007.09.14.122531 [1064] +libburn/libburn.h +Documented burn_write_opts_set_multi @param opts + +14 Sep 2007 [1065] +src/burn_wrap.c +Changed include form of libisofs.h and libburn.h + +2007.09.15.112311 [1066] +libburn/libdax_msgs.h +libburn/libdax_msgs.c +Imported Range "vreixo" into libburn/libdax_msgs.h + +2007.09.15.171844 [1067] +libburn/libdax_msgs.h +Prepared for neat sed translation. Explained concept of libdax_msgs variants. + +15 Sep 2007 [1068] +libburn/libdax_msgs_to_xyz_msgs.sh +A sed converter which creates libiso_msgs.[ch] from libdax_msgs.[ch] + +2007.09.15.172141 [1069] +libburn/libburn.h +libburn/init.c +New API function burn_set_messenger() + +2007.09.15.204320 [1070] +libburn/libburn.h +libburn/libdax_msgs.h +libburn/libdax_msgs.c +libburn/init.c +Equipped libdax_msgs with reference counter. Completed its mutex protection. + +------------------------------------ cycle - cdrskin-0.3.9 - 2007.09.15.205752 + +16 Sep 2007 [1071] +cdrskin/changelog.txt +Next cdrskin-0.3.9 cycle + +2007.09.17.163735 [1072] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Implemented emulation for cdrecord options -inq , -format , -load + +2007.09.18.072234 [1073] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Learned helptexts for -inq, -format, -load from cdrecord (they are wrong, btw) + +2007.09.18.090713 [1074] +libburn/libburn.h +libburn/drive.c +New API function burn_drive_leave_locked() + +2007.09.18.090839 [1075] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Implemented emulation for cdrecord option -lock + +2007.09.18.130344 [1076] +libburn/mmc.c +Made use of Immed bit with 5Bh CLOSE TRACK/SESSION + +2007.09.18.200343 [1077] +libburn/mmc.c +libburn/spc.h +libburn/spc.c +libburn/sbc.c +libburn/libdax_msgs.h +Made use of Immed bit with 1Bh START STOP UNIT and 35h SYNCHRONIZE CACHE + +2007.09.18.200454 [1078] +cdrskin/cdrskin.c +Corrected an outdated HINT text + +2007.09.18.201556 [1079] +libburn/sbc.c +libburn/spc.c +Changed some comments, reacted on harmless compiler warning + +2007.09.18.204043 [1080] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Implemented emulation for cdrecord option -immed + +2007.09.19.094046 [1081] +cdrskin/cdrskin.c +Implemented emulation for cdrecord option -waiti + +19 Sep 2007 [1082] +cdrskin/cdrskin.1 +Did a little overhaul of general paragraphs, mentioned new option -waiti + + +------------------------------------ cycle - cdrskin-0.3.9 - 2007.09.19.112330 +* More cdrecord options supported: -format, -inq, -load, -lock, -immed, -waiti + +19 Sep 2007 [1083] +cdrskin/changelog.txt +cdrskin/cdrskin_eng.html +Next cdrskin-0.3.9 cycle + +2007.09.19.141650 [1084] +cdrskin/cdrskin.c +Made cdrskin/compile_cdrskin.sh -do_diet work again + +19 Sep 2007 [1085] +cdrskin/cdrskin.1 +Made minor corrections + +2007.09.19.212659 [1086] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New option fallback_program= + +2007.09.20.125854 [1087] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Triggered fallback by unsuitable media, made -version report fallback program + +2007.09.21.120333 [1088] +libburn/sbc.c +libburn/spc.c +Had to revoke Immed bit on load command. LG GSA-4082B : premature "no media" + +2007.09.22.140613 [1096] +cdrskin/cdrskin.c +Updated list of unsupported cdrecord and wodim options + +2007.09.22.151712 [1097] +libburn/libburn.h +libburn/init.c +New API function burn_msgs_submit() + +2007.09.22.151712 [1101] +libburn/sbc.c +Reacted on compiler warning + +2007.09.23.163301 [1107] +libburn/libburn.h +libburn/drive.c +New API function burn_drive_equals_adr() + +2007.09.23.163419 [1108] +cdrskin/cdrskin.c +Made use of burn_drive_equals_adr() + +2007.09.23.163529 [1109] +libburn/sbc.c +Updated a comment about Immed and a debug message with tray loading + +2007.09.24.062354 [1110] +libburn/drive.c +Added forgotten handling of "sdtio:" with burn_drive_equals_adr() + +2007.09.24.135440 [1113] +libburn/libburn.h +libburn/transport.h +libburn/drive.h +libburn/drive.c +libburn/read.c +libburn/write.c +Implemented drive role 3, sequential write-only stdio drives (e.g. stdout) + +2007.09.24.135845 [1114] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Took into respect new drive role 3 + +2007.09.24.181340 [1115] +libburn/sg-linux.c +libburn/drive.c +Made stdio-drives work on readonly CD block devices + +24 Sep 2007 [1116] +test/libburner.c +Blocked against file descriptor drives. Too dangerous for a demo. + +2007.09.26.155301 [1123] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Disabled --allow_emulated_drives in setuid runs + +26 Sep 2007 [1124] +cdrskin/README +Streamlined and moved legal stuff to end of text + +2007.09.26.173840 [1125] +cdrskin/compile_cdrskin.sh +Made -O2 default if not -g is given + +2007.09.27.083351 [1126] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/convert_man_to_html.sh +cdrskin/README +Disallowed emulated drives for superuser, allowed stdio:/dev/null for all + +2007.09.27.093129 [1127] +cdrskin/cdrskin.c +Corrected announcement with dev=help about stdio: "Open via UNIX device" + + +------------------------------------ cycle - cdrskin-0.3.9 - 2007.09.27.093301 +* New option fallback_program= + + +27 Sep 2007 [1128] +cdrskin/changelog.txt +Next cdrskin-0.3.9 cycle + +28 Sep 2007 [1129] libisofs +libisofs/libiso_msgs.h +Removed apostrophes which my compiler does not like + +2007.09.29.185007 [1131] +libburn/transport.h +libburn/init.h +libburn/init.c +libburn/drive.h +libburn/drive.c +libburn/async.c +Trying to catch signals from within the writer thread + +2007.09.29.191558 [1132] +libburn/init.c +Added forgotten handling of non-writer-non-control threads + +2007.09.30.212517 [1135] +libburn/libburn.h +libburn/async.h +libburn/async.c +libburn/file.h +libburn/file.c +Implemented a simple fifo to decouple from burn_source signals + +30 Sep 2007 [1136] +test/telltoc.c +Disallowed --read_and_print raw:- , allowed to write to chardev+pipe+socket + +2007.10.02.120659 [1145] +libburn/write.c +Moved minimum tracksize padding out of TAO track closing. Now done before sync. + +2007.10.02.135538 [1146] +libburn/libburn.h +Clarified role of burn_source + +2007.10.02.180003 [1148] +libburn/write.c +Corrected error with revision 1145 + +2007.10.03.084206 [1150] +libburn/libburn.h +More documentation for burn_source + +2007.10.03.112547 [1151] +libburn/transport.h +libburn/mmc.c +libburn/write.c +libburn/drive.c +Ensured synchronize cache before release + +2007.10.03.115550 [1153] +libburn/libburn.h +More documentation for burn_source + +2007.10.03.223649 [1155] +libburn/file.h +libburn/file.c +libburn/async.c +Implemented the ring buffer of burn_fifo_source_new() object + +2007.10.03.223851 [1156] +libburn/libburn.h +More documentation for burn_source, new API call burn_fifo_inquire_status() + +3 Oct 2007 [1157] +test/libburner.c +Made use of 4 MB fifo + +2007.10.04.200221 [1158] +libburn/libburn.h +libburn/file.h +libburn/file.c +libburn/libdax_msgs.h +Inserted the necessary error messages and magic numbers + +4 Oct 2007 [1159] +test/libburner.c +Adjusted pacifier messages and change with burn_fifo_inquire_status() + +2007.10.04.210245 [1160] +libburn/libburn.h +Minor adjustments with comment text + +2007.10.04.213107 [1161] +libburn/drive.c +Corrected abort preventing bug introduced with revision 1131 + +2007.10.05.085929 [1162] +libburn/libburn.h +libburn/file.c +Revoked urge to have a magic[4] in burn_source (free_data is magic enough) + +2007.10.05.231712 [1163] +libburn/file.c +Fixed data spoiling bug with ring buffer introduced with rev 1155 + +2007.10.07.110506 [1164] +libburn/file.c +Corrected status reply for unstarted fifo + +2007.10.07.110644 [1165] +libburn/file.c +Corrected status reply for unstarted fifo (2nd try) + +7 Oct 2007 [1166] +test/libburner.c +Minor changes with waiting for drive and fifo status display + +2007.10.15.115448 [1179] +cdrskin/cdrskin.c +Corrected 4-byte buffer overflow (which did no detectable harm) + +2007.10.15.115728 [1180] +libburn/read.c +Made possible to silence error message about missing pseudo drive + +2007.10.15.115851 [1181] +libburn/drive.c +Corrected SIGSEGV with changing from one drive to the other + +2007.10.15.144050 [1182] +libburn/drive.c +Activated re-usal of disposed global drive_array slots + +2007.10.16.212205 [1189] +libburn/libburn.h +libburn/init.c +New API function burn_text_to_sev() + +18 Oct 2007 [1209] +test/telltoc.c +Calmed down hyperactive sleep interval with drive scanning + +2007.10.18.200336 [1210] +libburn/init.c +Prevented SIGSEGV with burn_msgs_obtain() on non-initialized library + +2007.10.19.115326 [1215] +libburn/async.c +libburn/libdax_msgs.h +Starting threads detached, providing two alternatives. But zombies still there. + +2007.10.19.132821 [1216] +libburn/init.c +Small change with debug verbosity of abort handler + +2007.10.19.133310 [1217] +libburn/async.c +Removed useless alternative after zombies turned out to be caused by gdb + +------------------------------------ cycle - cdrskin-0.3.9 - 2007.10.23.150436 + +2007.10.23.150436 [] +cdrskin/changelog.txt +Next cdrskin-0.3.9 cycle + + +----------------------------- release - cdrskin-0.4.0.pl00 - 2007.10. +* New option direct_write_amount= +* New option --grow_overwriteable_iso +* New option --allow_emulated_drives dev=stdio: +* More cdrecord options supported: -format, -inq, -load, -lock, -immed, -waiti +* New option fallback_program= + + +=============================================================================== + TODO +=============================================================================== + +Refuse writing if track size exceeds free media space + +problem with telltoc: double descriptor list from before load and after load + + +What about cdrskin rc files ? Forward with fallback runs ? + +cdrskin/README +Thin out + + + [] +open(,O_LARGEFILE) ? + + [] +Emulate -dummy on overwriteables ? + + [] +Emulate -dummy on DVD+R ? + + + [] +libcevap/main.c +Preparations for lowercase class and function names + + [] +libcevap/libdax_model.txt +Work goes on +>>> struct burn_drive { int stdio_fd } + + + [] +libcevap/cgen.c +libcevap/ctyp.c +Fixed a bug about arrays + + + +------------------------------------ cycle - cdrskin-0.3.9 - +cdrskin/changelog.txt +Next cdrskin-0.3.9 cycle + + + + +What about minimum track sizes ? (POWER OFF/ON , BUS RESET ?) + + +After cooking: review of -do_diet ? + +growisofs.c : _LINUX_CAPABILITY_VERSION CAP_SYS_RAWIO SYS_capset ? + +Questions to post: + A70211_to_cdwrite_growisofs_question_finalizing + + + +Format DVD-RAM ? +Disable error checking with DVD-RAM. + + +Rectify mmc_read_atip speed interpretation. 12x media are reported as 10x. I never heard of 6x media. + + +----------------------------------------- long term intentions: + + [] +-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 + + + [] +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) + + +=============================================================================== + This is the dirty end of the todo list. + The recent changelog entries are above the headline "TODO". +=============================================================================== diff --git a/libburn/branches/ZeroFourTwo/cdrskin/cleanup.c b/libburn/branches/ZeroFourTwo/cdrskin/cleanup.c new file mode 100644 index 00000000..fc923830 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/cdrskin/cleanup.h b/libburn/branches/ZeroFourTwo/cdrskin/cleanup.h new file mode 100644 index 00000000..a9d35519 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/cdrskin/compile_cdrskin.sh b/libburn/branches/ZeroFourTwo/cdrskin/compile_cdrskin.sh new file mode 100755 index 00000000..8fac9c1f --- /dev/null +++ b/libburn/branches/ZeroFourTwo/cdrskin/compile_cdrskin.sh @@ -0,0 +1,216 @@ +#!/bin/sh + +# compile_cdrskin.sh +# Copyright 2005 - 2007 Thomas Schmitt, scdbackup@gmx.net, GPL +# to be executed within ./libburn-* resp ./cdrskin-* + +debug_opts="-O2" +def_opts= +largefile_opts="-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1" +libvers="-DCdrskin_libburn_0_4_1" +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_4_0" + then + libvers="-DCdrskin_libburn_0_4_0" + 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_4_1" + 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="-g" + 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_4_0 set macro to match libburn-0.4.0." + 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 produce debuggable programm." + 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 \ + libburn/read.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/branches/ZeroFourTwo/cdrskin/convert_man_to_html.sh b/libburn/branches/ZeroFourTwo/cdrskin/convert_man_to_html.sh new file mode 100755 index 00000000..db909ba9 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/cdrskin/convert_man_to_html.sh @@ -0,0 +1,78 @@ +#!/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>General information paragraphs:<\/b>/\ <BR><b>General information paragraphs:<\/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>Write mode selection:<\/b>/\ <BR><b>Write mode selection:<\/b>/' \ + -e 's/<b>Recordable CD Media:<\/b>/\ <BR><b>Recordable CD Media:<\/b>/' \ + -e 's/<b>Overwriteable DVD Media:<\/b>/\ <BR><b>Overwriteable DVD Media:<\/b>/' \ + -e 's/<b>Sequentially Recordable DVD Media:<\/b>/\ <BR><b>Sequentially Recordable DVD Media:<\/b>/' \ + -e 's/^The write modes for DVD+R/\ <BR>The write modes for DVD+R/' \ + -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/<b>Emulated drives:<\/b>/\ <BR><b>Emulated drives:<\/b>/' \ + -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 FILES/See section <A HREF="#FILES">FILES<\/A>/' \ + -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 -lL "$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/branches/ZeroFourTwo/cdrskin/doener_150x200_tr.png b/libburn/branches/ZeroFourTwo/cdrskin/doener_150x200_tr.png new file mode 100644 index 0000000000000000000000000000000000000000..bffad014dc4a2ca621d33980eff597c922397f76 GIT binary patch literal 10595 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 literal 0 HcmV?d00001 diff --git a/libburn/branches/ZeroFourTwo/cdrskin/doener_150x200_tr_octx.png b/libburn/branches/ZeroFourTwo/cdrskin/doener_150x200_tr_octx.png new file mode 100644 index 0000000000000000000000000000000000000000..7f8e9838896ac345a085b314c83f913a9c1d222f GIT binary patch literal 9433 zcmd^F19K%zu-(|UvEhxa&BnH^8{5`K8{4*TY}?p4xk)y*oox8N_x{G4sxzmitGars zdrtLC)kG>QN}(VUB7Xb!4Mj#;T=hRr`wx}yQ2))J_nZ~qzCnFcmRFY`#>2+LM581j z6e1(0p&;dBC1s$YVrK$yvorH^b1<@T<NeTK6%a@NX(Av+kE`I$A)(By>d0g5L8$2< zD#)+QMCZcEV)~s(LC{4)R8UQYS=WWlQIp--k=w|KP+nR}RZ(7DMNvmnT}D|$PR~MC z-BH6{QQgwf#8BT_Ny^N`$kj=~#?r#s!Oq>)&fC+&#?=!|Jpop?2;DXb!@UBaoW$yy z!0J%Tre8|uJFKdas_K|5W>aZrlx*)5YZX|;mEy%(=3t+|>`_Re)a2>(Tiqkl-Ko>b zEW^-w)CVV`jypDnvmAt)I*(bg09kfK7d1^+3YHF!a1T#(Pp;Cf%X4p-(W;np9QmEh z&R9!N+`vqi!Au&b#1!jJ7~{;`Dn{C(LQ&=nh*4LJvDF9)@K3aHuCa8faMKuPB;WF} zS+KX+cembkx7)HdniM9BFbnVWi`WbdPKv>fk7f+3qpeM1ZUCXxG%yzw6U4=+hKBgY z#oNa<t0mM~RVT<5XFJwcIMvlEH_Txd&(NfdD>VF(&R^A-Yd~G7Wjkv`I&Z^2sewH2 zqPeU5ao5H%nnt-9;j@}(H(94}l_h<Xt9Y4Zc^v6{U8r_bs(TBPzpXOAZSq*m*8DSz zJiW;94~%=ch;caxxEkku8l!zZf&YB`c76=EctXCkC^IwWa5X7@Gb?qwAal2-bv>i{ zu;lP~=6$zs6BQAj7#EYA9GjV*7M_qAon4Vrnx0nOkzUnOn3n@eiw9+Px1`jR6lFJb z#8j4-RaCV$)YUdN)^&EYgBn^>n?@>g2S62L<sBo9)k7`qBR!*CO{*oDlPR?WGhJh& z-3x2&)AQZn<Ib)7lG)w0h~KLjkrT1$^A+h6?G^ofJ@Yl7>yC(x!o0Qak=@Sm**|H6 z!=1CE4Nr5i&+|3UXTP6s!(T4aUvFx_=PhGnqZ5m(^Wzf>OS5aMt5bhwC;lF99)Rag zp3cC3UJs_;PFElA=1-1~Paoc2AI`qMz8<bm|I@-{0GGx8O2SQ5QSCqD|KIU#4UFvk zUnU@ca#G@7rEqs6|1A)lq;*{XW9k2c<b>_dZ{NtRWW+_(z1A;tkhHP1*2l!sSSV^3 zl#Bm*Wt-h#nj{J9)!>--G~4Of+M{gLwxrspb}sLgZ85a2>l(l$F6_aWEYncx9%ixX z%csUo{4xegY`{QNGPu|uP%@!o=3NVNK$v<8DI_@f_gcooPsm#wrGLE%U3>qb;vjy5 z0`vB9_XjqRl7m{e;4rG0Rv*ZLDkk3cXt0{)_sN^x!Fp)Wnp&|lr#gm)r^Z>fJKB$t zv)UcrBYn7OGnqqq-Q`o(8dsGIc}57;3}6B(`ob-1gR9D5Bc_N6F2$HPtg{@=!(2f9 zr>?g9o8#1SW<tUR_ELAS+83s{UXPq_k0Z#ZyxsjuM!FltdWMPD{6lB9ej~}A`T8}W zo*yeI7ac2yyk-*eM$>54X>KhEmNDH|+xKd?^8=)%gS>Sn5Bz24UnZwWSfS4hbYI@U zZ*lM??M|&P7qSwfS3$R3Q@1!T?)V=5PHD6JJ)ak@!S(qnRPe*U0oKHwy75Qu8_9rN zZ*kKnrnbR2?W3ph2o2T6rR%)qq-rA#TaX44(B9RU&+xj{irKfCreQJdBF24(HFO?h z>9vnR&K1k!+4R!n;7nHDDYR+JfH}S43@a%KE2qbuoZ3MNl89Mv!NS~nYxa9w3PkHh zNH=`XVB*P{t-fGPm-hG`f}&vqi8No%ulCMP{^T!aw62V2i1AhM^Mc^Sqx%~cF;c(1 z!{i|iE)lU?OzrTH)c#!2$J_IJE<Am^K6>N*H=^cQLFef#Xm|$O&)N@%NiKBItl81O z-QsX?2(2AyIb*LjXtO@sU&LROxC{0~n^CVQ+odC}rJSt#XJJ`213`TpDL5AQ3jBi& z)ZDE8?-m35sCKp_w7vMmM1RsA1pSQpDcl_fLQeSQ{=h13>$#~&Syxb?-a!GQLizq( zN)2_4M24?(W0#4JC&@r?PPiUFdWntjQjsr8cXD?~3Qb{}fvyL?arb2Ruied>h7{Vf z-p>JVeKL8_>)(@v@rBmp@O{*JAm6%3KcEx8k%T<{VyovZA_TD>6TDvE+oy!etvQ3S z)J3keeilz2s~X2|8iu3aUGd)UO<*I1NgM*4Avqi+2ZEE(IC>Zw%0_j{H*9e9k^kKs zPoX`Vr%*+7=+O_FgFfi#<IJ#E1J_bgmX=shNjGml+gT54V$VHG0ur-a;50GE&8tN^ z2ATeJ>5Ke|McZ!mkv$tAIoDz_{K5ZTGrxPz&2B@VuY*TQ`qSrjZ{&QH%<Ob0_M)a* zO1E^Fx>L=!BTB;+dVn}GTCKc<Sig_oZ#t=TDy>HQpTk1XLvwTdfZkE`q{7(ZBKuQc z7ZBXo2MrxsXBfyzZT?KEX@ZaO^aZJH@J7-AQNjk1?b2Hds-TP4zjXlWzTRGL2;rBD z+k`o62mD|%y`CJ8nv`A7sY8L+?)g-MTb}>!yLlSL3|zjHl&?Hrg|?gI$#zsXfC0UX zkB|2djI}v#Ji*=I8Vh#W6FE^!N~URPX4=|LO_!Fd50|ugRH<bco+@g&p-jQ<yPU-> z|FNpnb*&+2uhk4NQZXbY=(z5u==Z^00`!8AKfakzOEA|}benLer)JKkr{s8Fv!Kta zvkC_b7|e71SJcY$A+dV`sxl1?4Nc9IF{$1zq5YRuG;TH>!^(H`diEzaVCD=BmaqZ6 z(xhF*8=arO&7_0p5fQTqIBA;@%bZi7Z5=SjK-!*!&v;O?IfvFJg1pW2)mycq5377! ztNu;h)zww)=xC%GYm+TqPFq7R^++y#DqvJYBXY0(dHB|zjGqxr8}5mzuA-`@M^o43 z6Pa!qS{_9w6AlyW>uyxfVVXz3zYzxz6&UJqz9DDcqNvYP)o+xT7&+D!(iRoXatvs* zSK5fK=+`)&O`+vb*2yH0iS&KG)N8SsScnrDKAW>)@qac|itjldBkOy6S~wJd)KTg6 z+YY+?Bb%seQb<Ou^a?7P)c5E;ke6`L#DY)F%uH$aH1a#B(x|T0SCME&B~0T2Y}<7_ zTrxphT%U@VwH138R~1pZTsw0+9>O~Oi2zkVYC))rVPOL3*{rv#TteNBrcW1-T`ZfC zG1D}%<Kv7x{w=ggH<F5bOvyA>P2Rl>$I}k#e*tUv`nRI0qL2TmuPkx#EVJ=Tq}BPL zQRFi4dDfPc9Hb0N^jrlkPpM|=G&}Zv+>ZTZryY3mkMRLo=iN2QHl4beE2ybqL8HB^ z2f$3Y#ZmkhKU3b^5~ns7rVB2O;vwz|Znryh5aDu#Ns5uar#E3l^#D6hLK!8ZvO7nk zvgDu}fs5n*Zv8z+q`z_Ux+Itart!Hnn~_jjQ$54c;nE5Qv={9?=@A{&nU|S^y&XP` z4xt3T6lZMP6iTCbMy<4AYPkv7nViJuY%2V_zOf?9af)5<<GT@K1c3=zq+l*hj^x9k zC@``5S4eYs)l+tPVd3siT42$N%jNG8Rg2cAht@X28)u4N?tl^yOr9QH^2DGdC{V0C z7yq!f{rpOMIzmlvqi-9@Lb95OiAT3qq)A&OFWVjn1m4hxnx9%-o?f<OT(4u-QZ07@ zbr6nv42>_=4K+PFKJH4V(plIpk7}~pKs|!!gQ#E~Sp-ve=B=%j^^J{1UGc@{ObhO+ znh!n=`wE7R|6;MH;7y{Bv{m?fp5qdR5&uIXtXSSy2~faeqhMv#l4{ch2?~-ND~TQb zm}zc0{(~1ZQDsfbDDn0I1iPq6e#^P;H8nv;Nj@+9+wI+P0Yrkgu(#(?8Kvy}uplaG z7A{aIE4nZL!{K(Um@ni>nl&-psB<M0;gNgW2o;49KF|Rp_GFIqz>alrKiT=3FU(%u z0`>V}W#6ZJ>)*%hRufsKz$pq~&y|V{MzB5k&@)V1^obmJy&BWo{%^+{jw~YnAxY@= z{DH5s5(WlG&z7@#wE<rr;6VM8lh3300gf%hhN7azDi*8RS)gz5$ce-@izWk^ZO`;N zB7{yxn;tAd<H1E{R90taRuspv!m`FK!*(8<=L*kXyu3UD;v(HvAfH~|=f(=)c;`7< z!qxj9iN)Np$BffFv<RPTDP>13^S>sBayj|s)Z3}_%3!={pR4dAM~hkHJZ`9dN+GdB zzstVDBJn&!X7SrvZ7Qi>1bQSPiuPbGEDq~-)!sJLf<25BuwRSc;pDRg^q-u64`Eki zpPaTwDaOMsdw*Y|0>A52!{FV+LQKg8&y!dYhmNO;x(6J||1(4fxU$6fP?rj8=wS4@ zgxN?s-AwCjzuydn9a}KL=lwJt-9Vr4|3X~RWx$bZe~vZZy?>_yRt-UZz$2C|T2Agi zukaQqrsssuX=;F?^#A4A=-vcpHswKe^k#C|Iyl`RAJnH#?KAY&fjXT9Ox-h_k?~yW z-$okGK6+?m#MiTV<YX1aZl0*kEx4(!WBXi=h^z8!3gJQ84ZuTo&6@JQe}(@B$t-5{ z(UD<&eZh^XQ_rb^nmWrorZ$&!*m6!W$ETCp{#qc?FqKZH`J&adIeRUwn)UP)h~>Y% z(!coOAfRiQh2i(p>-4!zby<_nl|cFtwy^ALvKdce<SJAw&7Rt6y`Sqv67eo<Qy)Mb z7|i>H)h#~`KlO^k^6O_B5rP;%rE7&<kPLbj&<|F9N*&&oIjQ<ByPU+O5?#ye-7?kc z_usAgEDpeX4bRsrKbr4J?|mB1{tI4W)3INp`+1gvmVhn5xUpZ;Q(oKH0tIXBa4LhG zvf~$$ypZca@QyX|>SXHeZK_5~4ap^+?F!k(>qJhW-*mL^wzdaMe!SX4IGkD>sgH+j z#BO<-t)0rO#RXA;=6i1{!#AV@;APlS)_DZwU{&}0Uy<;i?9Yy4ab>ln0CZD*7Oh?b z7f+CfEL{$jfb0Ib&}_EQ*Tijrtf}7E%v0}0RQ=Os2wg9VzI1&@lZ7CPK_UVryVl5Z zK0jl}egF?nYaWj)Nco2~bbhf@(3xRN_ZI7WizjZcLTb<5U2-y+FQZk4IE=fClpaG> z?hM}RfvAsyRPanL-#x|)$K`WXUG2)(L$V6P<1MO>-Pr*x>GQyFF<uWxaC8Uekgth} z?og5t3n|ANK~D$g#RRz{S%Y@yt_rrBr&SG;bp7F!7Nf6HX%?I5dh<41cvx62fX>>{ zwn@1o6n|*Az0Nb?c?iwqr2=gG`A&z1r40)qw^LakIt%W6jDED`Rw-lmuTZDN7@B8o zP_N~hYpJOr5h{H8Q>~@9?y%KuZQYiFqf#OYz>VCIz(k<gx&KAK^AQ5qHcN0u{j`^L zMOKY>j2O{$#G)4eaoK^Fmw_p=uq@f5`FkHz7_0ERX}b^wwdnT(jL%RZ)TuD5j(l-S zYBT;2O{z@JfXRE7_scoF>hLHhJd(h=(`uE0bZKli`?ab2<37$0&#j?RxHG!WEbau| z%xSVQXG#80O0&<Xk$QPRa-}qsmCSi+=ym;sT7DK!;MTra7%)=1pF*Gip9dNUmEY!N za22oDo{_=J+6LWgYgLKyDS5I;I_md8co}^ZV5z!@vUwdo7b35J@5}~?aRmUc9~|j# zQalzM{2OI}CRV%I3R-Jtf%7)6T=GPB`8?47K32tL!3;6`3aOY-9Q}eCc6$e!Le0jV zpTxWXJxQ^8(dLj{6mUC)Z#p1Hh1s|L^T)8%4-?Us>_GoZ9!M6Tay`G3EE(%@vQlep zU?7%`x`G78iB$k2Nl18u$?#eqGV7H#N+7f!OQD(Q%WI9EkdDBH8kI{WcMp%Iz_y%R zpc@^I{MW<1nrJh&y<{&Xl{B-PpZLxaI-)L|pooO3g)SfwR_J!jek_hekatCn5u-g# z;X1N(4BIcb>f<h+CHOc$iyOS*SwtDmu6IHyONLSOi+7J#E}ZV86uMBEPP!_hww60V z7PwhoV7+LRB^3C%sq($hZOF@SzrfhqJH_v1ur#Iem&|=UjKY(c^AJK<8e>T`WAlir zSwXZ_abj6?RP8)&Mks-_L8v?;I&kz>B)R-2n_Yx7GWCK}+;j^|{$DkwxL<Kz{_|^= zF8z0|v~RC`qG6a&#CW2T=s_-F#9RbHVzb86qv9FXy<1y-Bzxa8S#56fx4j>DGA5|2 ze1SSteh8&x9LHGo-CODk0OhLAMS09o%YgBV_IrSKjH#g?3)w^o*Ew2I<0#UQ!Mp5c zPrZ-z<;{HO*?BlB*#>M=mOhk$!XaG=yy81Yn?OjgrfQg?QuT0hNxX2}b^xRMhcd;{ z;q@LZ!Sj#BO&j%|&vnenyGJCJ*?b=Nth#IQ@g298poKGRbS~~9I`euRs4??KVW!Dj z8eK9R-Yo&EJ2&Y&mRS;dLiz(F80LV~=dI(z7N3s{u(D_hPT%B__kmZXerb9CA*cK~ zeVK?PQ;Cf=l0tS2cBRHkvGatZ-`xKAy~xj>FVZYOk(bg21{ZEy9@1(m1-OrJQ*o8x z{)7Z3aAyqNg#M5%)v#_+QK4Wr^JxgJWF3}=O_uf}4S3n5*!#s*9gdnRtzVsCFvx!A z6}mf`{-@{WJw>=?z)E=btSL1#l3bPmlcukU(H}L0Gx#1x^tN59?1z8{F9^%GQ&Xrt zcv&+7X_)<NkAdY+Hu1qnL@=i&o(i8!Jx=y9HRBe^Zz+Iezm=lx$iAO{zL#HyFsF4A zG@jT0x<062+xHs>w>>bpvMrUzG{M)9j^bcRBv#ES<7XjM3LSWk-|%8osh-dYQh8@h zMnVSu6Sn3bwj3;xxbAMUDlD(Ip27j4g2APZ-CVvN!s@*f(v!|KsiL}J;&r7lupyOM ztxIV-^0<Tm!eR`UZd_S51blf)On7K~+<J@I94^}}pAR0$)k(P3dLI~|>1R&Om@1ls z7Y)^__6+i}y#Kn<3t!CdXO^%csX~U5!wj?c6wCNhVg<ogcM#suq3*vO!ZZVF^rP*U zySeSphrxI6f&DSKqceqy&&6Om=)I6$`ct_gWxgRR83K7$c1f7mS4H2uQamHSU8Rj} zG6C#`5ZioEdxUvmJ%*h0Yijzj5SKf<ULm^}S4z<`4UP9-2i%lV7Y%6jpBZFoS&Di5 zj$8X^(e?AJ1g?*+0_p#q*r6l|Bq*1JV+?Gop`}24wZh3cA&CNH!i6}J;gPsRpUr~; z^6Tx_7wJ((pD`5Mjwc;=B5Q4a%h`1<J-S7(er8D@{|QkgM%2r(J4KV^!DB+aG-kcW z<$A-{_XkxJ=Cs_Ec_T6<g^vR;y(}g>pi+1wKc8c(4|#>W7+}PJxr;S$UkP`NOmcd- zKD~^>Slz%n=tTlp@?H!)7GP$@lZS!I7iAtQMTTZReeM>DNsrRM110{R_oMU*c<ui8 zM+yIw1ITN1&YoIrWI-V&{YCe>SFOYz!c|pbhCyX*woo<zKY>WM6Q2lZ%-GdI&5Gmu zA;f2u5x-rbGSs7A)3VvWaBt=|n-!~WEwbj7lex~y)u0VX#l%3Ah=AmJ$nG!2wD2>7 zvw?_N%rNSItCm9i%i2AqO;Tcv>0Rt}m9l+q&2L0KGy-Z;-o%GLDtF`_Z6_JSK^(sY zW}N*YW*C0?VRO@Pfz7wqLd}Qwb(>W8{DO`tDtSa__EB0P&gd+!nQ8b<MgdNyMmqe> z8xBVB3TiQ0O3?gDUvv+UrfKsc_i_Q&iz(YPCLT&?dEhi>Kcg3mW0#%cPE9MiLs@pi zh>1pol$4lH2>1VO4AEp6kI(GVu;79=6|rmC_cO)E=`xtL3K0MoShfOXqFxvUPXbUv zDzpU@r62~<&(?mYG=r>A%Kw|t=?>~YbO8&3xQl_qX}aSWEI;{XJc}@KCa5N`?=i>p zjfOu(*ivj|dLY=P3$9-(?~nfE3Swg2LH_Mz9MS-xB0N5w?{-Ki&j~$cw6SmmQOiJ4 zv66G*#~4|s`j{rp7nCy<=%cDly?Jxc+WJYxk=_z*_Dn$=j5H#`n9Z&~^Z0>Sm^9f_ zD>F`md6}t+J47HdMQO~B!mWTved1#MI>lObBwYF@iXKi-Q<5?gfcy0DJl_CWcpa;0 zEy!=OhW>(yGMI6+bW+lVO44C#?ewl+^#L);<E|Yg&S+Cxk}`Nb81Da4)ANIijER@L zV4bw$5EVS;;1A+|lW@wVOkwjkMgWS&Bb>MX>nC5sth*-|c~@p$ZS@y?ON}>tL~ZzT zM!8Xn2%=l!-~PE#NMtLRn^WmT&q5Lw{QjddUYa02{BuirF=8x+@U6nuvIp+B1b**U zUgu*7Ob7NO4nI`n8tV&F5C*&g+xtgmQ6^Ixc~pBk`h9Nw?hbK~@X6J(SFP!3*j*j+ zs*$(z*3xZ!sIZQmFoNh=#<?7^7}|6jg-~chf_bGd*|zn)@y-CNQe+pNeMg-6vK+kq z?CFA5nHWkCW~h8QOOY`)<X{e6hR!*%1gxOOOpbV}3^jfVT5s=9QM;tMMTMcR5c9>e z^lpqSCDn377YYVGfNG^sdrl=8Hbw$9{E<E?=w?U)l1`q@?%-rK{H@DhqLfH$a~Ehc zm4l)ujDp!oSeXn3<*O?<P03E{MjfCUc!KmQnhB2;DOu+on6fJ7uoQS@YVD2hE%+9K zS4Q`UT}mq>i^jHS>t3Qb+6tf1F1p9VZaJh;u=H}Azr|`%^6UId4w!kfi<Ev6ZRGFn zlHf>A`1nwg1`SA>4*TNcGebonOgg|7)Q~=d-!3SaP(mJ(jJbIy8(<+2PwxMU9dYFj zaBJnYD<Y!-(@D9G5F`3f@gs<eSy8m|t9E%8W2d5v8Iy9mEM4&YM*3G|gjuaiM@3^N z{c&)*!<zJZcK@eO-{)-zF)*c>T&sYP(l579Ug8z$gVY;0X+=44ZZWkE9~nOySCOzm zob+|Do3>ox>EqqZ14kRlMhKFSij`iS4?3}0LnVI<H)&ni=z2nFCYw*S`KCqcpM#~E zXT5Hy=xGI*=#WA!IJiV$NpDU{RaxgS2{w!S?D53!hl~vlk*SAb@F)CA@Wew_i>)N0 zm#SywW+#;v12M&hm(%5Rzc9GdU>+i1uNTe#)dntwhU#4LikK-a0$Pl9yJG{jSIFhS zDQ_2V#AZ>EP*4)%X#sN3Q^9T80#c!OSec^OIxHm<O2&!thoFD1K6CmD`yo_DK&bHf z=kmLQLH}l1YMsuPy!6Q|8qa81jhH3ne^(BI$va2Kh+snWya`W>kyxEh3|Kp(>u~?~ zm2M&2?{H3ReZD+~^@K&-%wE9B^8enIi=KPaV+?cw0eio4^PBb`OGr-Hk~JMioiiw3 z6@zqs?m8%Nd}BnHCo4kU#K3Z;^x#nUW3Tv_0}J=Du?ft>;bHduBB2GYT*kyE#oazW zJ&}m!kgWNmZT2ci9J}uyomjbFl=JcjA)0J9%23e$6l~-$Uc?R4ujMV!yynH|LDl== z6VdmyF6=nE!<@!cT=`~}CE4VfEPup8(xdXe;yhmHC}H~(<>iMk73O|sgY~@id_(q% zzg+|B`zX#8kW0!kf6mOBQ=x`4wafWfDtnm>qw&Uo@Uc95pBsZ+;^a0XCL?)1>-A(B zf&=9ngQ=FTyu5C6ClRUVh)r!iuXo61<G%QMZEXISAHw}tyvK+YC!39e53;el$5(j0 zoiiKZW9^B-X$9i!W@>H?L_e0Zj+93L30AUwljo>-d9Hdj7lDpV|Jm$I*tOGKJdP_n zgF<etq&ByLTNaH#Yb76z0K?IJZkHnG+AmHfCdu5ql?+k?IPI)jh_gcnbgryX(CBD# z!(Y1J4Q%tLr_KatAiKvn12!<9rtjuNv%%TL1?U3bJ^k+%_C|^zE3;<<yXgWE5mD2U zbEbkjUEUd}*-k-IH%l`2bAH$(yG*1Q;^M)|oz}?~sA%=cp>5zyOVrdz*tEdez*Z0~ zSra)sIV-zbVq42VyVcCaX~j-NwCF)%b!9gh5xZPr`k%+sQ49z)ja49R`oz35xo|P* zU<X~Jm@!1N*}OVZVi}}qjnXhb-*5_Mn`eXS3jF=X^WA3lqSdmBNSP{#S*}NJG2tVO zM(@L%w8C99Hm%&<Q>@Q!dZA>@0y)Bl{8_pYESv&+z&gjsKx5zvnX~Ps`hhVR1%tyi zGbd--*za!L0M!j1?|1l{f|0v&Vr2ur%#89wdesg9l@6up<Ekab=~JM>gBD}+OFL6O zCIJ`=KC^4#uP!WPd%IV_22D8F+5EaHu%4BTo^U%4et8>f<QW^bVb4a_R1@QdzCTA= ztvgouVcWJ^%Hy$--I!(fmp}pPH{r`iTNcroU8_ZZBY0=^agNf}d23kBL(c+h5}p;7 z6OA+AIc$VmkB7Lsp`lAdM;S4Bz3J&HAOm8X)Ed1$W1q)!qh?YKPl^5>_F7Xb<X4!R z^%TPl10iR{K$pmN<wAWn8u<zscnIaP-7lak(r_1~Ui~1VUR_>6{=C{Lvi(=ry5UZ% z3v27^ubo6WsW@X7_4Nh0D$16<HryjK=<C{1_ss|dhiuy)+2HAvB#t(C*6^fT<4;nl zCD(jfx(v0Et8pYzK=R1n$5K7A;3K7A)qnlkg3Ay&#Yij=i@+L$t~NK_O2aHg@!<3& z&cDd0kNWDt%GCW)<5fL4wU~T-{BY>ca759m$DQcROA3t3z1i{VM+>KwVsXH_qsuw} zq_w}m7|}-vO}x2j=1p@muD9fkyWx&V{nN2wURLu1HNJq)V>2XL#+ky7I0&~*Lr;%! zT9Le-+x83fl~gAa=joHSXPP4%i62>6a?MHcODnyFn7J$#{r*reF85)*#*kP<%JJ)G z2GcwiQJ3X<a>z!%-n60*h`*q@z&APqMikOo5eZ(F5*@t;L<S9+6N=(4rooSK)PAu= zNA+L~u^<bCWP7a;rJWlfeIc&Zp6W%S50_)R3L6u|=4Yt2$8dT9Bq|Tb6p{eeR9Ud* zo(x5W0Xqr2;X|}^aSCT%j_7K+Jt+I+%ier~9>GK04a-~AHwRumF1(@3fE}k@7^>() zugG$E`=Rp7dQD^Mc}g_2C+bg9eGKqud#l!{=_V#(*N%^ulc1T08!ws#o@!SloUf#i zm2IsU)5A&!@g{TlUpY2rOvTAv_-0+wa4|4FDqQ)@%}+OU2zaDZcsfRm6RLVLecGLV z6a0<lNns|&U?#-adcp8`wVsnIR8+0Kc!WntRJiX5%;~Fx#HeF)sN`PyapftLyE}f? zsDpQA^;XPM>qjHI1KF@R%_kXD-{F16zw7DIk;2*HIQ?+<4>*S?eDpxV@)xw5SsS%! z6Y!o=jKqh4XK=|u)j=?X7S_ZF*F!=3N9Tu(n?A*}hL7Am7NExzHAOMU4p$gEvh2dk zN9R%z#El!|$`h<mt%JnjD~8s?e>L2wXI+E8r!=TY3d9y?ajDm83OC(TpD|xl<?;zc z#$C*<DTpJ*jK_Z{Q?Ac6U3V|Qy+{ma{!VUYmi>*5H%sAc061X6XjMl7Dal0Nba=T^ z9VRxQ_~Nf^g{R8Asyah@#mJunmH6-cBvQR@S=5Qo&|VsEVLfiZQJPqqiDXO+E;1tg zwQL#K(g^3lNsqY%xcMak(w)}D)^p9*EYsw5K!q;C+iIP*v2Xe&kc8b({4A%dHv($o z^|#pt>FToteFGuT(OD2fkbSoCEiF(w8bC2rkJDjD%@260BK4H46l%H~p>O1btVO$e z<5L_Ka+-X@x(WNCl)BRjVREg{^3&FvIojOCn9qk#>sPNcP@|xB2q&1=1<k3f2VK<m zx)7#f!v%VDOF$Bpz|?RWs%c%%i<rPC;%8bKn;yINr(OIpz-R86ZcdCP%0Dp&oOUD5 zf5Ypt<eE{J*K+gY0DdZ%!|3!oI2!<_gT%6(H;u+OAhvu=FDhuUp~lZclU2@G<h8{J zUz=NYq%)O_V2TTWvfYd5Z2%Ke|LP4^nRe9;GC^$Gm%w#~E_hPqEwiF1P00UFtTn?S c@M>h@r+qEWf&ua0=YwxD5{lxrqQ=4h1IEa__W%F@ literal 0 HcmV?d00001 diff --git a/libburn/branches/ZeroFourTwo/cdrskin/make_timestamp.sh b/libburn/branches/ZeroFourTwo/cdrskin/make_timestamp.sh new file mode 100755 index 00000000..957a2c4e --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/cdrskin/wiki_plain.txt b/libburn/branches/ZeroFourTwo/cdrskin/wiki_plain.txt new file mode 100644 index 00000000..ae809679 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/cdrskin/wiki_plain.txt @@ -0,0 +1,293 @@ +-------------------------------------------------------------------------- + cdrskin Wiki - plain text copy +-------------------------------------------------------------------------- +[[Image(source:/libburn/trunk/cdrskin/doener_150x200_tr.png)]] [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 this way. +Overwriteable media DVD-RAM, DVD+RW and DVD-RW are handled differently than +with cdrecord-ProDVD in order to offer TAO-like single track recording. +Sequential DVD-R[W] and DVD+R are handled like CD-R[W] with TAO and +multi-session. Additionally cdrskin offers cdrecord-ProDVD-like mode DAO +with DVD-R[W]. + +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-project.org/browser/libburn/trunk/cdrskin/README?format=txt cdrskin/README]. + +About libburn API for burning CD and DVD: http://api.libburnia-project.org + +-------------------------------------------------------------------------- + +For dual layer DVD types see the advice 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/sr0' 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. + +-------------------------------------------------------------------------- + +cdrskin does not only read from and write to optical drives which comply +to the MMC standard but also does the same with regular files or block +devices other than optical drives. + +Because the power to alter a disk file might be a bad surprise for a +traditional user of cdrecord, it is necessary to give option +--allow_emulated_drives before an emulated drive may be addressed. +Eventually one of the startup files would be a good place for it. +See man page, section FILES. + +The addresses of emulated drives begin with the prefix "stdio:". +{{{ +dev=stdio:/tmp/pseudo_drive +dev=stdio:/dev/usbstick +}}} + +Regular files and block devices behave much like DVD-RAM. + +Other file types may be valid targets for write-only operations. +This includes standard output, named pipes, character devices +{{{ +dev=stdio:/dev/fd/1 +dev=stdio:/tmp/named_pipe +dev=stdio:/dev/ptyxy +}}} + +These files behave much like blank DVD-R. + +All files used as pseudo-drives have to offer rw-permission. + + +-------------------------------------------------------------------------- + +The DVD capabilities of cdrskin differ from those of cdrecord-ProDVD. cdrskin +offers TAO-like multi-session with DVD-R[W], DVD+R and TAO-like single session +with overwriteable DVD media. It also offers DAO on DVD-R[W] which is probably +the same as the traditional cdrecord-ProDVD write mode. + +Non-cdrecord blank mode blank=format_overwrite brings a DVD-RW +disc from its initial profile "Sequential Recording" into profile state +"Restricted Overwrite". +{{{ +cdrskin dev=/dev/sr0 -v blank=format_overwrite +}}} + +DVD-RAM, DVD+RW and overwriteable 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 ... +}}} + +Non-cdrecord blank mode blank=deformat_sequential brings an overwriteable +DVD-RW back into state "Sequential Recording" with the capability of doing +multi-session, if the drive is capable of "Incremental Streaming" +(MMC feature 21h). + +Used sequential DVD-RW media may be blanked by blank=fast or blank=all which +normally both do full blanking. Thus sequential DVD-RW behave much like large +CD-RW with possibly more than 99 tracks. + +blank=deformat_sequential does minimal blanking of DVD-RW which usually yields +media incapable of "Incremental Streaming". + +Option --prodvd_cli_compatible activates blank=fast and blank=all for +overwriteable DVD-RW which normally ignore those two options. It also makes +option -multi tolerable with media and write modes which are not suitable for +multi-session. (The default behavior of cdrskin deems me to be preferrable.) + +Option --grow_overwriteable_iso gives cdrskin ISO pseudo-multi-session +capabilities on DVD-RAM and DVD+RW similar to growisofs. +Associated options blank=, -multi, -msinfo and -toc are available in this case. +They either pretend a blank media (if there is no ISO 9660 image) or appendable +media with a single session and track on it. blank= invalidates ISO images. + + +-------------------------------------------------------------------------- + +assert_write_lba=<lba> allows to ensure that the start block address which +was used with the formatter program (e.g. mkisofs -C) matches the start block +address which will be used by the upcoming burn. + +E.g. cdrskin aborts with an error message if +{{{ +assert_write_lba=0 +}}} +is given but an appendable media is to be burned which would start at +block 68432. + + +An ISO-9660 file system image must be prepared according to a particular +block address on media. If the prepared address and the real address on media +do not match then the filesystem will not be mountable or may even cause system +trouble. + +A sequential archive format like afio or star will not necessarily need such +a coordination of addresses. It might nevertheless be confusing to a reader +if the archive does not start at block 0. + +-------------------------------------------------------------------------- + +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 + +-------------------------------------------------------------------------- + +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 other than DVD-RAM, DVD+RW, DVD+R, DVD-RW, DVD-R, +the cdrskin project currently advises to use Andy Polyakov's dvd+rw-tools +which despite their historic name are capable of all the media above +and also do dual layer and even BD discs. + +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). + +A special feature of dvd+rw-tools is growing of ISO-9660 filesystems on +overwriteable media. This is not the same as multi-session writing of cdrskin +with CD media, but retrieves additional information from the existing ISO +image and finally manipulates the start sectors of this existing image. + +So, inspired by growisofs, cdrskin can offer DVD multi-session not only with +sequential DVD-R[W] and with DVD+R, but also with DVD-RAM, DVD+RW and +even regular disk files or block devices other than CD/DVD writers. +This is enabled by option --grow_overwriteable_iso. + +-------------------------------------------------------------------------- + diff --git a/libburn/branches/ZeroFourTwo/configure.ac b/libburn/branches/ZeroFourTwo/configure.ac new file mode 100644 index 00000000..3e1863a7 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/configure.ac @@ -0,0 +1,174 @@ +AC_INIT([libburn], [0.4.1], [http://libburnia-project.org]) +AC_PREREQ([2.50]) +dnl AC_CONFIG_HEADER([config.h]) + +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +AM_INIT_AUTOMAKE([subdir-objects]) + +dnl Note by ts A71207: +dnl +dnl BURN_MICRO_VERSION was _not_ the third component Z of libburn-X.Y.Z +dnl but an eternal counter which leads to CURRENT. This misunderstanding +dnl caused a messed up sequence of CURRENTs. +dnl +dnl CURRENT and AGE describe the binary compatibility interval of a +dnl dynamic library. +dnl See also http://www.gnu.org/software/libtool/manual.html#Interfaces +dnl +dnl The name of the library will be libburn.so.$CURRENT-$AGE.$AGE.$REV +dnl In the terminology of this file: +dnl CURRENT = LT_CURRENT +dnl REV = LT_REVISION +dnle AGE = LT_AGE +dnl +dnl In the past the following CURRENTs of libburn have been released. +dnl All with AGE=0. +dnl 0.2.2 = 2 , 0.2.3 = 3 , 0.2.6 = 6 +dnl 0.3.0 = 0 , 0.3.2 = 2 , 0.3.4 = 4 . 0.3.6 = 6 , 0.3.8 = 4 +dnl 0.4.0 = 0 +dnl +dnl Beginning with libburn-0.4.1 a rectified counting was introduced as +dnl CURRENT=10, REV=1, AGE=6 +dnl This rectification declared the current version +dnl to be binary compatible down to libburn-0.3.4. +dnl Real backward compatibility is given down to libburn-0.3.2. +dnl Beware of libburn-0.2.6 which had CURRENT=6. +dnl +dnl CURRENT=10, REV=0, AGE=6 would be appropriate for libburn-0.4.0 +dnl but was not issued, regreattably. +dnl +dnl If libburn-0.4.0 gets patched, its CURRENT will become 10, AGE 6, and +dnl REV will be set to one higher than the highest REV of CURRENT 10. +dnl If libburn-0.4.1 is still on CURRENT 10 then its REV gets set one higher +dnl than that. +dnl As soon as 0.4.1 becomes upwardly incompatible, it shall become 11,0,7. +dnl From then on it counts REV independently of libburn-0.4.0. +dnl Whether 0.4.1 alters independent REV at all, is not decided yet. +dnl It may well stay 0 until a release version joins CURRENT 11. +dnl +dnl Under the preconditions +dnl CURRENT= $BURN_MICRO_VERSION - $BURN_INTERFACE_AGE +dnl REV= $BURN_INTERFACE_AGE +dnl AGE= $BURN_BINARY_AGE +dnl the following old instructions were reasonable. Their drawback +dnl is that they cause large jumps in CURRENT, when BURN_INTERFACE_AGE gets +dnl reset to 0 from a high value. So this is outdated now: +dnl --------------------------------------------------------------------------- +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 --------------------------------------------------------------------------- +dnl +dnl Instead, LT_CURRENT, LT_REVISION and LT_AGE get set directly now. +dnl +dnl SONAME of the emerging library is LT_CURRENT - LT_AGE. +dnl The linker will do no finer checks. Especially no age range check for +dnl the cdrskin binary. If SONAME matches, then the couple starts. +dnl +dnl Therefore a run time check is provided by libburn function burn_version(). +dnl It returns the major, minor and micro revision of the library. This means +dnl BURN_*_VERSION kept its second job which does not comply to the usual ways +dnl of configure.ac . I.e. now *officially* this is the source code release +dnl version as announced to the public. It has no conection to SONAME or +dnl libtool version numbering. +dnl It rather feeds the API function burn_version(). +dnl +dnl If BURN_*_VERSION changes, be sure to change AC_INIT above to match. +dnl +BURN_MAJOR_VERSION=0 +BURN_MINOR_VERSION=4 +BURN_MICRO_VERSION=1 +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_VERSION) + +dnl Libtool versioning +LT_RELEASE=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION.$BURN_MICRO_VERSION +# SONAME = 10 - 6 = 4 . Library name = libburn.4.6.1 +LT_CURRENT=10 +LT_REVISION=1 +LT_AGE=6 +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) + +# ts A71207: This is done only not to break any old components +BURN_INTERFACE_AGE=$LT_REVISION +BURN_BINARY_AGE=`expr $LT_AGE + $BURN_INTERFACE_AGE` +AC_SUBST(BURN_INTERFACE_AGE) +AC_SUBST(BURN_BINARY_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() + +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-5.pc + ]) +AC_OUTPUT diff --git a/libburn/branches/ZeroFourTwo/doc/Makefile b/libburn/branches/ZeroFourTwo/doc/Makefile new file mode 100644 index 00000000..062350dd --- /dev/null +++ b/libburn/branches/ZeroFourTwo/doc/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/libburn/branches/ZeroFourTwo/doc/comments b/libburn/branches/ZeroFourTwo/doc/comments new file mode 100644 index 00000000..3c630470 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/doc/comments @@ -0,0 +1,161 @@ +/** + @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 CD-R, CD-RW, DVD-RAM, DVD+RW, DVD+R, DVD-RW, DVD-R. + +Not supported yet are dual layer media, HD-DVD, BD (blue ray). Testers for +dual layer DVD+/-R are wanted, though. + +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 and many DVD types. +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/srM or /dev/hdX (e.g. on kernel 2.6). + libburn is the foundation of our cdrecord emulation. Its code is + independent of cdrecord. Its DVD capabilities are learned from + studying the code of dvd+rw-tools and MMC-5 specs. No code but only + the pure SCSI knowledge has been taken from dvd+rw-tools, though. + +- 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. + +- libisoburn is an add-on to libburn and libisofs which allows to grow + ISO-9660 filesystem images on multi-session media as well as + on overwriteable media via the same API. All media peculiarities + are handled automatically. + +- 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. Additionally it + provides libburn's DVD capabilities, where only -sao is compatible + with 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. + +- xorriso is an upcomming application of all three libraries which creates, + loads, manipulates and writes ISO 9660 filesystem images with + Rock Ridge extensions. Manipulation will not only be adding or + overwriting of files but also deletion, renaming, and attribute + changing. + See SVN of libisoburn, man test/xorriso.1, test/compile_xorriso.sh + +- "test" is a collection of application gestures and examples given by the + authors of the library features. The burn API example of libburn + is named test/libburner.c . The API for media information inquiry is + demonstrated in test/telltoc.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://libburnia-project.org . +It can list the available devices, can blank a CD-RW or DVD-RW and +can burn to recordable CD and recordable single layer DVD. + +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|--format_overwrite] + [--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 +Blank a used DVD-RW (is combinable with burning in one run): + test/libburner --drive /dev/hdc --blank_full +Format a DVD-RW to avoid need for blanking before re-use: + test/libburner --drive /dev/hdc --format_overwrite +Burn two audio tracks (to CD only): + 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* media via: afio -tvZ /dev/hdc +</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 media, and also is able to provide the necessary +multi-session information for program mkisofs option -C. Especially helpful +are its predictions with "Write multi" and "Write modes" where availability +of "TAO" indicates that tracks of unpredicted length can be written. +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/branches/ZeroFourTwo/doc/cookbook.txt b/libburn/branches/ZeroFourTwo/doc/cookbook.txt new file mode 100644 index 00000000..2143d659 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/doc/cookbook.txt @@ -0,0 +1,1108 @@ +libburnia-project.org Optical Media Rotisserie Recipes as of August 2007 + +Content: +- TAO Multi-Session CD Cookbook (CD-R, CD-RW) +- SAO CD Cookbook (CD-R, CD-RW, pure audio or pure data only) +- Overwriteable DVD Cookbook (DVD-RAM, DVD+RW, DVD-RW) +- Sequential DVD-R[W] Cookbook +- DVD+R Cookbook + +------------------------------------------------------------------------------- + TAO Multi-Session CD Cookbook +------------------------------------------------------------------------------- +Guided by reading mmc-r10a.pdf , O.8 "Write a Track" + from http://www.t10.org/ftp/t10/drafts/mmc/ +backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/ +by reading spc3r23.pdf from http://www.t10.org/ftp/t10/drafts/spc3/ +by reading libburn/* from http://icculus.org/burn +and by experiments with drives NEC ND-4570A, LG GSA-4082B, LITE-ON LTR48125S +which used in part code from http://icculus.org/burn. + +For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> + +------------------------------------------------------------------------------- + +Media type can be recognized by Current Profile from 46h GET CONFIGURATION. +(mmc5r03c.pdf 6.6.2.1) + +CD-R 0009h +CD-RW 000ah + +The following topics are covered in this text: +- About blank, appendable and finalized CD media +- Writing a session to CD in TAO mode +- Obtaining CD multi-session info for extending ISO-9660 filesystems +- Obtaining a Table Of Content from CD + + +------------------------------------------------------------------------------- +About blank, appendable and finalized CD media : + +CD media have to be blank or appendable in order to be writeable in TAO mode. +The according status may be inquired by 51h READ DISC INFORMATION requesting +Data Type 000b Standard Disc Information, where reply value Disc Status +indicates: + 00b blank + 01b appendable + 10b finalized + 11b others (unsuitable for this recipe) +(mmc5r03c.pdf 6.22.3.1.4) + +CD-RW which are finalized or appendable may be blanked by command A1h BLANK +with blanking types 000b "Blank the disc" or 001b "Minimally blank the disc". +The Start Address/Track Number will be ignored so it may well be 0. + +Because the operation is long running it is advised to set the Immed bit and to +watch the progress by commands 00h TEST UNIT READY and 03h REQUEST SENSE +with DESC bit set to 0 for fixed format reply. +It is done when 00h succeeds and 03h reports 0 in PROGRESS INDICATION +(byte 1+2 in Table 22 = byte 16+17 SENSE KEY SPECIFIC in table 26). +(mmc5r03c.pdf 6.2 BLANK) +(spc3r23.pdf 4.5.2.4.4 table 22, 4.5.3 table 26, + 6.27 REQUEST SENSE, 6.33 TEST UNIT READY) + + +------------------------------------------------------------------------------- +Writing a session to CD in TAO mode : + +The writing method for blank or appendable media is the same. A new session +will get created automatically by the first track when it is written. If the +media is blank then the new session will be the first and only one in the +table of content. If the media is appendable then a new session will be +appended to the existing sessions. In any case the new track will be the +first one in the new session. + +Speed may be set by BBh SET CD SPEED parameter Drive Write Speed. Note that +kbytes/sec means 1000 bytes/sec and not 1024/sec. Rotational control should +be set to 00b. 1x CD speed is 176.4 kbytes/sec. Speed is usually set to the +next lower possible value by the drive. So it is helpful to add a few +kbytes/sec just in case the drive has rounding problems. +(mmc5r03c.pdf 6.37) + +Before writing can occur, a Write Parameters mode page 05h has to be composed +and transmitted via 55h MODE SELECT. Mode page 05h describes several burn +parameters: + BUFE Buffer Underrun protection 0=off, 1=on + Test Write -dummy mode for writing 0=off, 1=on + Write Type Packet/TAO/SAO/RAW 01h = TAO + Multi-session Wether to keep appendable 00b = finalize + 11b = keep appendable + Track Mode Describes frame type 4 for data , 0 for audio + Data Block Type Layout of payload blocks 8 for 2048 byte data blocks + 0 for 2352 byte audio blocks + Audio Pause Length 150 = 2 seconds +Any other parameters may be set to 0. +Mode page data as of MMC-5 table 644 are preceded by a Mode Parameter Header +as of SPC-3 table 240. This 8-byte header may be filled with zeros. +(mmc5r03c.pdf 7.5.4 The Mode Page, 4.2.3.4 Table 17 CONTROL = Track Mode) +(spc3r23.pdf 6.8 MODE SELECT, 7.4.3 Mode parameter header formats) + + +Writing has to begin at the address returned by 52h READ TRACK INFORMATION +with Address/Number Type set to 01b and Logical Block Address/Track/Session +Number set to FFh. The Next Writeable Address as of table 500 is the number +to start writing with. +(mmc5r03c.pdf 6.27 ) + +Writing is performed by one or more 2Ah WRITE transactions with the Logical +Block Address counted up from the initial number in sync with the number of +blocks written. I.e the Transfer Length of the previous 2Ah WRITE has to be +added to the Logical Block Address for the next 2Ah WRITE. Only full blocks +can be written. +(mmc5r03c.pdf, 6.44) +When writing is done, it is mandatory to force the drive's buffer to media by +35h SYNCHRONIZE CACHE. +(mmc5r03c.pdf, 6.41) + +A track must at least contain 300 payload blocks: 4 seconds of audio or +600 KiB of data. +(mmc5r03c.pdf 6.3.3.1.2) + +The track has to be closed by 5Bh CLOSE TRACK SESSION Close Function 001b. +Older MMC specifies a valid Logical Track Number FFh to depict the open track. +MMC-5 is quite silent about this. FFh works for my drives. +(mmc5r03c.pdf 6.3.3.1.2) + +After that, a new track may be written beginning with sending the mode page 05h +again. It is not tested wether 05h can be omitted if Track Mode and Data Block +Type are the same as with the previous track. +The new track will be added to the session which was opened by the first track. + +After the last track of a session, 5Bh CLOSE TRACK SESSION Close Function 010b +with Logical Track Number 0 closes the session. It depends on the Multi-Session +value in mode page 05h wether the disc is finalized or stays appendable. +(mmc5r03c.pdf 6.3.3.1.3) + + +------------------------------------------------------------------------------- +Obtaining CD multi-session info for extending ISO-9660 filesystems : + +Program mkisofs expects two numbers with its option -C which describe the +situation on an appendable CD which already contains a ISO-9660 filesystem +in the first track of the last session. + +The first number is the Logical Block Address of that track containing the +existing ISO-9660 filesystem image. This number is needed for mkisofs option +-M to connect to the existing image. The new image will refer to files in +the previously existing image. mkisofs option -M needs read access to the +CD or a blockwise copy of it on hard disk. +The number is gained by 43h READ TOC/PMA/ATIP. +(mmc5r03c.pdf 6.26) +Untested is Format 0001b which in table 478 promises quick access via +Start Address Of First Track In Last Session. +(mmc5r03c.pdf 6.26.2.5 table 478, 6.26.3.3.1) +libburn gets the number from its Table Of Content model which is obtained +by 43h READ TOC/PMA/ATIP, Format 0010b. See below. + +The second number is an exact prediction of the Logical Block Address of the +new track which will contain the newly generated ISO-9660 image. +Even without mkisofs option -M this second number is still needed to make the +inner block address pointers of the image match the Logical Block Addresses +on CD. For that one may inquire 52h READ TRACK INFORMATION with +Address/Number Type set to 01b and Logical Block Address/Track/Session +Number set to FFh. The Next Writeable Address as of table 500 is the number +to use. +(mmc5r03c.pdf 6.27 ) + + +------------------------------------------------------------------------------- +Obtaining a Table Of Content from CD : + +The structure of a CD is comprised of sessions. Each session contains one or +more tracks and is followed by a lead-out. A track has an address and a length. + +Table of content information is gained by 43h READ TOC/PMA/ATIP, Format 0010b. +(mmc5r03c.pdf 6.26.2.5 table 478) + +The number of sessions is given by Last Complete Session Number. +The number of TOC Track descriptors is: (TOC Data Length - 2)/11 . + +Each TOC Track Descriptor contains a Session Number. + +If POINT is >= 1 and <= 99 (63h) then the descriptor is about the track of +which POINT tells the number. +The start address of this track can be read from PMIN, PSEC, PFRAME where +it is encoded in MSF format: +blocks = frames - 150, 75 frames = 1 sec , 60 sec = 1 min. +The length of the track is given by MIN,SEC,FRAME in the same format. + +If POINT = A0h then the descriptor tells in PMIN the first track number of its +session. +POINT = A1h tells in PMIN the last track number of its session. +POINT = A2h describes in PMIN, PSEC, PFRAME the lead-out of a session, i.e the +first address after the session's end. (Next writeable address typically is +lead-out + 11400 after the first session, lead-out + 6900 after further +sessions.) +POINT = B0h tells in MIN,SEC,FRAME this next writeable address or FFh,FFh,FFh +for finalized disc. +(mmc5r03c.pdf 6.26.3.4 table 489, 4.2.3.7 Mode-1 Q, Mode-5 Q) + + +In libburn the address of the first track in the last session is obtained from +the last session's POINT = A0h and from the track descriptor with the POINT +value matching the PMIN value of the A0h descriptor. +Untested is wether POINT = B0h and 52h READ TRACK INFORMATION are always in +sync. libburn uses the info provided by 52h READ TRACK INFORMATION. + + +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- + SAO CD Cookbook +------------------------------------------------------------------------------- +Guided by reading libburn/* from http://icculus.org/burn +backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/ +and by experiments with drives NEC ND-4570A, LG GSA-4082B, LITE-ON LTR48125S +which used in part code from http://icculus.org/burn. + +For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> + +------------------------------------------------------------------------------- + +Recognition of media type and state (blank, appendable, finalized) is as +described in the TAO Multi-Session CD Cookbook. See there. + +The MMC specs do not give much hint about the combination of SAO and +multi-session. My drives refused not only on a few experiments which i did +in libburn but also failed with cdrecord -sao on an appendable CD. +So for now only blank CD seem to be suitable for SAO writing. + +Different from TAO mode, the whole session layout is announced to the drive by +sending a Cue Sheet. This implies that the sizes of the tracks have to be known +in advance, which is a heavy drawback when dealing with track data sources like +stdin, named pipes or sockets. +Nevertheless, SAO seems to be best writing mode for audio purposes, as our +audio expert Lorenzo Taylor found out. + +A SAO session in libburn may either consist entirely of audio tracks or +entirely of data tracks. For mixed sessions, only TAO is usable yet. + +- Composing a SAO CD Cue Sheet (either audio or data, but not mixed) +- Writing the prepared SAO CD session +- What is known about mixed mode sessions + + +------------------------------------------------------------------------------- +Composing a Cue Sheet (either audio or data, but not mixed) : + +The Cue Sheet will get submitted to the drive by 5Dh SEND CUE SHEET. +Each entry of the sheet is of 8 bytes size. Its fields are named +CTL|ADR, TNO, INDEX, DATA FORM, SCMS, MIN, SEC, FRAME . +(mmc5r03c.pdf 6.33) + +CTL is 40h for data and 00h for audio. +(mmc5r03c.pdf 6.33.3.4) +ADR is always 01h. +TNO is the track number (1 to 99). +INDEX is a subaddress within tracks. This recipe uses only INDEX 01h within +tracks. +(mmc5r03c.pdf 4.2.3.5.2) +DATA FORM is 00h for audio payload , 10h for data. (01h for audio pause is not +used in libburn). +(mmc5r03c.pdf 6.33.3.11 CD-DA Data Form, 6.33.3.12 CD-ROM mode 1 Form) +SCMS is always 00h. +MIN, SEC, FRAME give the MSF address where the described data entity starts. +LBA = frames - 150, 75 frames = 1 sec , 60 sec = 1 min. +This address must increase from entry to entry (or at least stay equal). + + +The first entry describes the Lead-in. Its content is +(CTL|ADR ,00h,00h,01h,00h,00h,00h,00h) +With the CTL|ADR for the first track: 41h for data, 01h for audio. + +The LBA for the first write is negative: -150. This corresponds to MSF address +00h:00h:00h. All addresses are to be given in MSF format. +The first information track on disc is preceded by a pause encoding of 2 sec: +(CTL|ADR,01h,00h, DATA FORM ,00h,00h,00h,00h) +with DATA FORM = 00h for audio and 10h for data. By those 2 seconds the MSF +address increases to 00h:02h:00h = LBA 0. + +Each track is represented by an entry +(CTL|ADR, TNO ,01h,DATA FORM,00h, MIN , SEC , FRAME) +TNO gives the track number. MIN, SEC, FRAME give the MSF address which becomes +the start address of the track. The MSF address is then increased by the size +of the track (to be used with next track or with lead-out). + +A track must at least contain 300 payload blocks: 4 seconds of audio or +600 KiB of data. +(mmc5r03c.pdf 6.33.3.6) + +At the end of the session there is a lead-out entry +(CTL|ADR,AAh,01h,01h,00h,MIN,SEC,FRAME) +marking the end of the last track. (With libburn CTL is as of the last track.) + + +------------------------------------------------------------------------------- +Writing the prepared session : + +Speed may be set by BBh SET CD SPEED parameter Drive Write Speed. Note that +kbytes/sec means 1000 bytes/sec and not 1024/sec. Rotational control should +be set to 00b. 1x CD speed is 176.4 kbytes/sec. Speed is usually set to the +next lower possible value by the drive. So it is helpful to add a few +kbytes/sec just in case the drive has rounding problems. +(mmc5r03c.pdf 6.37) + +A Write Parameters mode page 05h has to be composed and transmitted via +55h MODE SELECT. This page describes the following parameters: + BUFE Buffer Underrun protection 0=off, 1=on + Test Write -dummy mode for writing 0=off, 1=on + Write Type Packet/TAO/SAO/RAW 02h = SAO + Multi-session Wether to keep appendable 00b = finalize + 11b = keep appendable + Track Mode Describes frame type 0 (is ignored) + Data Block Type Layout of payload blocks 0 (is ignored) + Audio Pause Length 150 = 2 seconds (ignored ?) +Any other parameters may be set to 0. +Mode page data as of MMC-5 table 644 are preceded by a Mode Parameter Header +as of SPC-3 table 240. This 8-byte header may be filled with zeros. +(mmc5r03c.pdf 7.5.4 The Mode Page, 4.2.3.4 Table 17 CONTROL = Track Mode) +(spc3r23.pdf 6.8 MODE SELECT, 7.4.3 Mode parameter header formats) + +The Cue Sheet is submitted to the drive by 5Dh SEND CUE SHEET. Cue Sheet Size +is 8 times the number of entries. +(mmc5r03c.pdf 6.33) + +Writing is performed by multiple 2Ah WRITE transactions with the Logical +Block Address counted up from the initial number in sync with the number of +blocks written. I.e the Transfer Length of the previous 2Ah WRITE has to be +added to the Logical Block Address for the next 2Ah WRITE. Only full blocks +can be written. +(mmc5r03c.pdf, 6.44) +Writing begins at LBA -150 which is to be transmitted as 4-byte, Big-endian, +two's-complement. E.g: -150 = FFh FFh FFh 6Ah. This is the natural form found +with about any 32-bit processor, so only the endianness has to be taken into +respect when converting a 32-bit integer into a LBA for command 2Ah WRITE. + + +At first the mandatory pause preceding the first track has to be written as +150 blocks of the matching sector size: 2048 for data, 2352 for audio. +By this, the LBA increases from -150 to 0. + +Next the tracks' payload is sent. For each track exactly the number of blocks +has to be transmitted as is announced in the Cue Sheet by the difference +of the track's own start address and the start address of the next entry in +the Cue Sheet. After each write the LBA for the next write has to be increased +by the number of blocks transmitted. Just like with TAO writing. + +There is no separator between the tracks of a pure mode SAO session. +(If the session was mixed mode, there would be extended Pre-gaps and Post-gaps +between data mode tracks and audio mode tracks.) +(libburn sends its own buffer to the drive at the end of each track but does +not sync the drive's chache. It is unclear wether this separation of tracks +on the level of 2Ah WRITE is necessary with a pure mode session. It does not +harm in any case and would probably be unavoidable if audio and data tracks +were mixed.) + +When writing of all tracks is done, it is mandatory to force the drive's buffer +to media by 35h SYNCHRONIZE CACHE. +(mmc5r03c.pdf, 6.41) + +No further finalization is necessary. (I.e. no 5Bh CLOSE TRACK SESSION.) + + +---------------------------------------------------------------------------- +What is known about mixed mode sessions : + +For now, SAO sessions with a mix of data and audio are not supported in +libburn. Here are the reasons why. + +Obviously the code of http://icculus.org/burn is incomplete in this aspect. +In mmc5r03c.pdf comparison of table 555 and 6.33.3.18 seems self-contradicting. +(The second Pre-gap in table 555 does not match any of the criteria of +6.33.3.18. Also, there is no Post-gap shown in table 555 although 6.33.3.19 +would prescribe some.) + +If a data track follows an audio track then the data track gets a preceding +extended Pre-gap: +(CTL|ADR, TNO ,01h,DATA FORM,00h, MIN , SEC , FRAME) +with TNO already the number of the data track. The MSF address is to be +increased by 3 seconds. The first second of the extended Pre-gap needs to be +written in the audio track's mode and the other 2 seconds are to be written +in the data track's mode. +(libburn compares DATA FORM rather than burn_track.mode . Wrong ?) +(libburn currently does only 2 seconds and the second part of Pre-gap. There is +an issue with burn_track.pregap1 about this. Seems libburn mistakes the pause +preceding track 1 for a part 2 of an extended Pre-gap.) + +If a data track is followed by an audio track then it gets a Post-gap of at +least two seconds. +No example of Post-gap is given for Cue Sheet. Maybe it is to be added to the +track, or maybe it gets an own Cue Sheet entry ... who knows ? +(libburn contains write code for pregap1, pregap2 and postgap. But only +pregap2 ever gets activated. Once hackingly for the first 2 second pause, once +incompletely for a change of DATA FORM.) + +Seems nobody ever tested this. Libburnia simply knows no use case where the +correctness of Pre-gap and Post-gap would become evident. + + +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- + Overwriteable DVD Cookbook +------------------------------------------------------------------------------- +Inspired by Andy Polyakov's http://fy.chalmers.se/~appro/linux/DVD+RW/tools , +backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/ +and by experiments with drives NEC ND-4570A and LG GSA-4082B. + +For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> + +------------------------------------------------------------------------------- + +Media type can be recognized by Current Profile from 46h GET CONFIGURATION. +(mmc5r03c.pdf 6.6.2.1) + +DVD-RAM 0012h +DVD-RW Restricted Overwrite 0013h +DVD-RW Sequential Recording 0014h (i.e. unformatted) +DVD+RW 001Ah + +A short compilation of the write model: +- Overwriting in general + +The recipes described here are depending on formatting state: +- DVD-RAM, fully formatted DVD+RW or DVD-RW +- Unformatted DVD+RW +- Partly formatted DVD+RW +- Unformatted DVD-RW +- Partly formatted DVD-RW +- Intermediate state DVD-RW + + +------------------------------------------------------------------------------- +Overwriting in general : + +Depending on media type, some kind of formatting has to have happened before +data can be written. Formatting may happen separately from writing or +simultaneously. See the particular recipes below. + +No Write Parameters mode page 05h is to be sent. Speed can be influenced by +B6h SET STREAMING , speed capabilities can be inquired by ACh GET PERFORMANCE. +It is advised to set only speeds and sizes which are returned by ACh. +(mmc5r03c.pdf 6.39 SET STREAMING, 6.8 GET PERFORMANCE) +Optimal performance is promised without any speed setting. But my experiments +showed that SET STREAMING values persist after media change. + +In the formatted area of the media, coarse random access is possible. +For DVD-RAM and DVD+RW write addresses and data size need to be aligned +to 2 KiB. For DVD-RW alignment has to be 32 KiB. Within these limitations +the write address is at the discretion of the sending program. +Just use 2Ah WRITE to send data. +(mmc5r03c.pdf, 6.44) +When writing is done, it is mandatory to force the drive's buffer to media by +35h SYNCHRONIZE CACHE. +(mmc5r03c.pdf, 6.41) + +The size of the formatted area can be inquired by 23h READ FORMAT CAPACITIES. +The Number Of Blocks value in the Current/Maximum Capacity Descriptor gives +this size in 2 KiB blocks. But this is true only if Descriptor Type is 10b +("Formatted Media"). +(mmc5r03c.pdf, 6.24.3.2.1, 6.24.3.2.3) + +Not yet formatted areas may be completely forbidden or they may be allowed for +sequential writing (DVD-RW Intermediate state) or they may be allowed for +random access only after the necessary waiting time for formatting to reach +the desired address (DVD+RW with background formatting active). + +Already written areas can be overwritten without special precaution. +Blanking a DVD-RW actually destroys its formatting. + + +Most of the concepts usually expressed in Write Parameters mode page 05h do not +apply to the recipes here: Test-Write, Buffer Underrun protection, +Multi-session, Write Type, Block Type, Track Mode, ... +There are hints for multi-session formats with DVD-RW but both of my drives +do not offer "Add Session" Format Types 12h or 14h. +(mmc5r03c.pdf 6.5.4.2.7 , 6.5.4.2.9) + + +Caution: Drive and media compatibility seems still to be quite an adventure. +If you experience problems, especially problems with readability, then try +different drives and media brands. Failure does not necessarily mean that the +software did anything wrong. + + +------------------------------------------------------------------------------- +DVD-RAM, fully formatted DVD+RW or DVD-RW : + +Full format is the natural state of DVD-RAM. + +DVD+RW reaches this state if Background Formatting is allowed to finish without +being stopped by 5Bh CLOSE TRACK SESSION. +(mmc5r03c.pdf, 6.5 FORMAT UNIT, 6.5.4.2.14 Format Type = 26h) +The formatting state of a DVD+RW may be inquired by 51h READ DISC INFORMATION +requesting Data Type 000b "Standard Disc Information". In the reply, +BG Format 3 indicates fully formatted media. +(mmc5r03c.pdf 6.22.3.1.13) + +DVD-RW reaches this state either by Format Type 00h (or 10h) with maximum +size given as Number Of Blocks, or by writing sequentially until the disc is +completely full into an intermediate session opened by format 15h resp. 13h. +(mmc5r03c.pdf, 6.5 FORMAT UNIT, 6.5.4.2.1, 6.5.4.2.10, 6.5.4.2.8) +A fully formatted DVD-RW can be recognized by 23h READ FORMAT CAPACITIES. The +Descriptor Type of the Current/Maximum Capacity Descriptor is 10b ("Formatted +Media") and 0 blocks are offered with Format Types 13h or 11h. +(mmc5r03c.pdf, 6.24.3.2.1, 6.24.3.3) +See also discussion of unformatted or partially formatted DVD-RW below. + + +In fully formatted state there is no need for any formatting before writing nor +for any finalizing other than forcing the drive's buffer to media by +35h SYNCHRONIZE CACHE (which is mandatory for writing, anyway). +(mmc5r03c.pdf, 6.41) + +(It seems to do no harm to send to DVD+RW or DVD-RW a 5Bh CLOSE TRACK SESSION +with Close Function 010b despite there is no session open in this scenario.) + + +------------------------------------------------------------------------------- +Unformatted DVD+RW + +This is the state of previously unused DVD+RW media. + +The formatting state of a DVD+RW may be inquired by 51h READ DISC INFORMATION +requiring Data Type 000b "Standard Disc Information". +In the reply, BG Format 0 indicates unformatted media (or unsuitable media). +(mmc5r03c.pdf 6.22.3.1.13) + +Formatting has to be started by command 04h FORMAT UNIT, Format Type 26h. +Different from other format types, 26h allows to send a fantasy size of +0xffffffff blocks and does not require the caller to know the exact maximum +size offered with that format. +(mmc5r03c.pdf, 6.5 FORMAT UNIT, 6.5.4.2.14 Format Type = 26h) + +As its name suggests, one has not to wait for background formatting to end +but may very soon start writing as on formatted media. Random access to +yet unformatted areas can last long, though. + +If backup formatting has been started at the beginning of the session, then +it may get stopped after the final cache sync by 5Bh CLOSE TRACK SESSION +with Close Function 010b. +(mmc5r03c.pdf 6.3.3.6) + +Formatting of DVD+RW is called "de-icing" because unformatted areas offer +no hold for random access addressing and are thus slippery like ice. One can +also see a color change from shiny unformatted to more dull formatted media. + + +------------------------------------------------------------------------------- +Partly formatted DVD+RW : + +This state is achieved by stopping background formatting before the media +was completely formmatted. + +The formatting state of a DVD+RW is obtained by 51h READ DISC INFORMATION +requiring Data Type 000b "Standard Disc Information". +In the reply, BG Format 1 indicates partly formatted media. +(mmc5r03c.pdf 6.22.3.1.13) + +If the data of the session surely fit into the formatted area, then it would +be unnecessary to restart background formatting. +But in order to make the DVD+RW surely accept its maximum number of bytes, +formatting may be restarted by command 04h FORMAT UNIT, Format Type 26h, +with the Restart bit set and Number of Blocks set to 0xffffffff. +(mmc5r03c.pdf, 6.5 FORMAT UNIT, 6.5.4.2.14 Format Type = 26h) + +From then on, the same rules apply as for previously unformatted DVD+RW. + + +------------------------------------------------------------------------------- +Unformatted DVD-RW (media profile is 0014h) : + +This state is present with previously unused media. It is also present with +media blanked by programs cdrecord, wodim or dvd+rw-format and with media which +were sequentially written from blank state. +Profile transition from formatted 0013h to unformatted 0014h is done by +A1h BLANK. +(mmc5r03c.pdf, 6.2) +Experiments on my drives indicate that only Blanking Type 000b "Blank the disc" +achieves neat media. Media blanked via type 001b offer no feature 0021h and +stall cdrecord or libburn already when those media get examined. growisofs can +burn them - but only via DAO (feature 002Fh which prescribes Write Type 2). +(mmc5r03c.pdf 5.3.11, 5.3.25) + +For becoming overwriteable such media need to be treated by command 04h FORMAT +UNIT. +(mmc5r03c.pdf, 6.5) +The Format Type has to be chosen from the list replied by 23h READ FORMAT +CAPACITIES. Suitable are Format Types 00h, 10h, 15h. +(mmc5r03c.pdf 6.24) + +Format Types 00h and 10h provide a writeable area of a size given by Number of +Blocks. Type 00h seems to be the most traditional and complete one. It needs +no closing of a session at the end of writing. +The Number Of Blocks may be at most the value reported by 23h READ FORMAT +CAPACITIES in the entry for the desired format type. Full format is achieved +by sending exactly the reported value. +(mmc5r03c.pdf, 6.5.4.2.1 Format Type = 00h, 6.5.4.2.5 Format Type = 10h) + +Format Type 15h provides a writeable area of given size too, but this area can +be expanded by sequential writing and afterwards marked as overwriteable by +closing the session. It is even allowed to format with size 0 and to leave +the size claim entirely to a sequential write process beginning at LBA 0. +(mmc5r03c.pdf, 6.5.4.2.10 Format Type = 15h) +When writing is done and cache is synced, one should send 5Bh CLOSE TRACK +SESSION with Close Function 010b in order to bring the session out of +Intermediate state. +(mmc5r03c.pdf 6.3.3.2.3) +If not written up to the last 32 KiB block, the DVD-RW is only partly formatted +after that. + + +------------------------------------------------------------------------------- +Partly formatted DVD-RW (media profile is 0013h) : + +This state is achieved by formatting a DVD-RW with a number of blocks which +is less than offered for the Format Type by the drive's reply to 23h READ +FORMAT CAPACITIES. If the media was most recently formatted by Format Types +015h or 013h then it must have got written some bytes and afterwards treated +by 5Bh CLOSE TRACK SESSION, 010b in order to be partly formatted. +(mmc5r03c.pdf 6.3.3.2.3 CLOSE TRACK SESSION 010b, 6.24 READ FORMAT CAPACITIES) +Elsewise the media is in Intermediate state. See below. + +A partly formatted DVD-RW can be recognized by 23h READ FORMAT CAPACITIES. The +Descriptor Type of the Current/Maximum Capacity Descriptor is 10b ("Formatted +Media") and the Number Of Blocks with formats 00h, 10h or 15h is larger than the +currently formatted size, resp. more than 0 blocks are offered with Format +Types 13h or 11h. +(mmc5r03c.pdf, 6.24.3.2.1, 6.24.3.3) + +If the data of the session surely fit into the formatted area, then it would +be unnecessary to do any further formatting. +But in order to make the DVD-RW surely accept its maximum number of bytes, +partial formatting may be expanded by command 04h FORMAT UNIT, Format Type 13h, +which is supposed to be offered by the drive in this state. This brings the +session again into Intermediate state and thus allows expansion by sequential +writing. As with Format Type 15h it is ok to set Number Of Blocks to 0, so that +no fixed size formatting work is done and writing can begin soon after. +(mmc5r03c.pdf, 6.5.4.2.8 Format Type = 13h) + +When writing is done and cache is synced, one should send 5Bh CLOSE TRACK +SESSION with Close Function 010b in order to bring the session out of +Intermediate state. +(mmc5r03c.pdf 6.3.3.2.3) +If not written up to the last 32 KiB block, the DVD-RW is only partly formatted +after that. + +Format Type 13h has been tested only with expanding sessions formatted by 15h. +Nevertheless it is offered with sessions from 00h and 10h, too. +According to the specs, Format Type 11h would expand a session by a fixed +size. This has not been tested yet because it is less appealing than 13h. +(mmc5r03c.pdf, 6.5.4.2.6 Format Type = 11h) + + +------------------------------------------------------------------------------- +Intermediate state DVD-RW (media profile is 0013h) : + +This state is achieved by formatting a DVD-RW with Format Type 15h or 13h +without subsequentially writing data and sending 5Bh CLOSE TRACK SESSION +with Close Function 010b. +Such media behave very unpleasing with my DVD-ROM drive under Linux 2.4 ide-cd. +One should therefore better avoid to release media in this state. + +This state can be recognized by 23h READ FORMAT CAPACITIES. The Descriptor Type +of the Current/Maximum Capacity Descriptor is 11b ("Unknown Capacity") and +no formats 13h or 11h are offered. +(mmc5r03c.pdf, 6.24.3.2.1, 6.24.3.3) + +One may treat such media as if Format Type 15h or 13h had been freshly applied. +I.e. sequential writing from LBA 0. After cache sync bring the session out +of Intermediate state by 5Bh CLOSE TRACK SESSION with Close Function 010b. +(mmc5r03c.pdf 6.3.3.2.3) + + +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- + Sequential DVD-R[W] Cookbook +------------------------------------------------------------------------------- +Inspired by Andy Polyakov's http://fy.chalmers.se/~appro/linux/DVD+RW/tools , +backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/ +and by experiments with drives NEC ND-4570A and LG GSA-4082B. + +For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> + +------------------------------------------------------------------------------- + +Media type can be recognized by Current Profile from 46h GET CONFIGURATION. +(mmc5r03c.pdf 6.6.2.1) +DVD-R 0011h +DVD-RW Restricted Overwrite 0013h +DVD-RW Sequential Recording 0014h +(DVD-R/DL Sequential Recording 0015h untested, might be single-session only) + +There are two approaches for writing to sequential DVD-R[W]: DAO and +Incremental. Not all media and drives offer Incremental which allows +multi-session as with CD media and does not demand a predicted track size. +DAO seems to be the older method. It allows only one single session and +track and it demands an exactly predicted track size. + +- About overwriteable, blank, appendable and finalized DVD-R[W] media +- Incremental writing +- DAO writing +- Obtaining DVD-R[W] multi-session info for extending ISO-9660 filesystems +- Obtaining a Table Of Content from DVD-R[W] +- Hearsay about DVD-R/DL (Dual Layer) + + +------------------------------------------------------------------------------- +About overwriteable, blank, appendable and finalized DVD-R[W] media : + +DVD-RW can be either in formatted state Restricted Overwrite or in unformatted +state Sequential Recording. Sequential media can be either blank, appendable +or finalized. + +Only blank and appendable media are sequentially writeable. For overwriteable +DVD-RW see the Overwriteable DVD Cookbook. + +Overwriteable DVD-RW can be detected by their profile number 0013h in contrast +to profile number 0014h for sequential DVD-RW. +The status of sequential media can be inquired like with CD by 51h READ DISC +INFORMATION requesting Data Type 000b Standard Disc Information, where reply +value Disc Status indicates: + 00b blank + 01b appendable + 10b finalized + 11b others (unsuitable for this recipe) +(mmc5r03c.pdf 6.22.3.1.4) + +Finalized, appendable or overwriteable DVD-RW can be brought into blank +sequential state by command A1h BLANK with blanking type 000b "Blank the disc". +See TAO Multi-Session CD Cookbook for details about blanking. + +After minimal blanking (type 001b) DVD-RW my two drives do not offer the +Incremental Streaming feature 0021h the media any more. Full blanking (000b) +brings back this feature. +(mmc5r03c.pdf 6.2) + + +------------------------------------------------------------------------------- +Incremental writing : + +Incremental writing allows to produce multi-session DVDs. It is indicated +by feature 0021h being marked current in the reply of 46h GET CONFIGURATION. +growisofs inquires 0021h by setting Starting Feature Number to 0x21 and +Allocation Length to 16 in order to get only this one. The feature descriptor +begins at byte 8 of the reply. Its availability is indicated by the Current +Bit. libburn obtains the full feature list for this and other info. +(mmc5r03c.pdf 5.2.2. Feature Descriptor format, 5.3.11 Feature 0021h, + 6.2 46h GET CONFIGURATION, ) +In mode page 05h this method is selected by Write Type 00h. + +Speed can be influenced by B6h SET STREAMING , speed capabilities can be +inquired by ACh GET PERFORMANCE. It is advised to set only speeds and sizes +which are returned by ACh. +(mmc5r03c.pdf 6.39 SET STREAMING, 6.8 GET PERFORMANCE) + +growisofs fetches a mode page 05h template by MODE SENSE and inserts its own +parameters. It sets Multi-session to 11b, unless dvd_compat is nonzero. +libburn composes its mode page 05h from zero and allows control of +Multi-Session by the application. + BUFE Buffer Underrun protection 0=off, 1=on + LS_V Link size valid 1=true + Test Write -dummy mode for writing 0=off, 1=on + Write Type Packet/TAO/SAO/RAW 00h = Incremental (Packet) + Multi-session Wether to keep appendable 00b = finalize + 11b = keep appendable + Track Mode Describes frame type 5 [*1] + Data Block Type Layout of payload blocks 8 [*2] + Link Size ??? 16 [*3] + FP Fixed Packet Size Bit 1 + Packet Size 16 [*4] +(mmc5r03c.pdf 7.5.4 The Mode Page, 4.2.3.4 Table 17 CONTROL = Track Mode) +(spc3r23.pdf 6.8 MODE SELECT, 7.4.3 Mode parameter header formats) +[*1:] +growisofs takes the Track Mode from 52h READ TRACK INFORMATION, Address/Number +Type 1, Track 1, Track Information Block byte 5 & 0xf. +(mmc5r03.pdf 6.27) +The specs predict that this will be Track Mode 4 (6.27.3.8) and also state that +default is 5 (7.5.4.12). 4 means: uninterrupted, do not copy. 5 means +increment, do not copy. +[*2:] +8 means: 2048 byte data blocks. growisofs sets this value if Data Mode from +above 52h READ TRACK INFORMATION is 1 or Fh, which is predicted by the specs +to be always true. +(mmc5r03.pdf 6.27.3.10) +[*3:] +growisofs (transport.hxx) sets Link Size to 16 for profiles 0011h and 0014h. +libburn now records the first link size from feature 0021h in its burn_drive +structure. If another link size item is 16, then 16 is used. +[*4:] +growisofs takes Packet Size from 52h. Specs predict it will be 16 (= 32 kiB). +(mmc5r03.pdf 7.5.4.16) + +The writing process is much like in "Writing a session to CD in TAO mode" : +Next Writeable Address is fetched from the reply of 52h READ TRACK INFORMATION. +libburn writes full 32 kiB buffers via 2Ah WRITE. +(mmc5r03c.pdf, 6.27 READ TRACK INFORMATION, 6.44 WRITE) +When writing is done, it is mandatory to force the drive's buffer to media by +35h SYNCHRONIZE CACHE. +(mmc5r03c.pdf, 6.41) + +The track has to be closed by 5Bh CLOSE TRACK SESSION Close Function 001b. +growisofs uses the logical track number for that and not FFh like libburn +does with TAO CD. So libburn obtains the Last Track Number in Last Session +from the reply of 51h READ DISC INFORMATION requesting Data Type 000b +"Standard Disc Information". +(mmc5r03c.pdf 6.3.3.2.2 CLOSE TRACK, 6.22.3.1.) + +Multiple tracks are permissible in a single session. After all of them have +been written, 5Bh CLOSE TRACK SESSION Close Function 010b with Logical Track +Number 0 closes the session. It depends on the Multi-Session value in mode +page 05h wether the disc is finalized or stays appendable. +(mmc5r03c.pdf 6.3.3.2.3) + + +------------------------------------------------------------------------------- +DAO writing : + +DAO is the mode described by feature 002Fh. This feature also gives information +about capabilities for Burnfree (BUF), Test Write and DVD-RW. +(mmc5r03c.pdf 5.3.25) +Experiments with growisofs showed that the track size needs to be predicted +and may not be exceeded during the write process. (growisofs ran into SCSI +errors with piped non-ISO-9660 images and with piped ISO-9660 which have +trailing data.) + +Speed can be influenced by B6h SET STREAMING , speed capabilities can be +inquired by ACh GET PERFORMANCE. It is advised to set only speeds and sizes +which are returned by ACh. +(mmc5r03c.pdf 6.39 SET STREAMING, 6.8 GET PERFORMANCE) + +The mode page 05h to be sent : + BUFE Buffer Underrun protection 0=off, 1=on + LS_V Link size valid 0=false [*3] + Test Write -dummy mode for writing 0=off, 1=on + Write Type Packet/TAO/SAO/RAW 02h = DAO (same code as SAO) + Multi-session Wether to keep appendable 00b = finalize + Track Mode Describes frame type 5 [*1] + Data Block Type Layout of payload blocks 8 [*2] + Link Size ??? 0 [*3] + FP Fixed Packet Size Bit 0 [*3] + Packet Size 0 [*3] +(mmc5r03c.pdf 7.5.4 The Mode Page, 4.2.3.4 Table 17 CONTROL = Track Mode) +(spc3r23.pdf 6.8 MODE SELECT, 7.4.3 Mode parameter header formats) +[*1:] +growisofs takes the Track Mode from 52h READ TRACK INFORMATION, Address/Number +Type 1, Track 1, Track Information Block byte 5 & 0xf. +(mmc5r03.pdf 6.27) +[*2:] +8 means: 2048 byte data blocks. growisofs sets this value if Data Mode from +above 52h READ TRACK INFORMATION is 1 or Fh, which is predicted by the specs +to be always true. (If not: growisofs aborts.) +(mmc5r03.pdf 6.27.3.10) +[*3:] +Link Size, Packet Size and their companions only apply to Write Type 00h. + +The session layout must be described by 53h RESERVE TRACK, RMZ=ARSV=0. +Reservation size should better already be aligned to 32 KiB. It has not been +tested yet, what happens if not enough data get written. +(mmc5r03c.pdf 6.31) + +Next Writeable Address is fetched from the reply of 52h READ TRACK INFORMATION. +The reply is supposed to be 0. libburn writes full 32 kiB buffers via +2Ah WRITE. +(mmc5r03c.pdf, 6.27 READ TRACK INFORMATION, 6.44 WRITE) +If the track source delivers less than the announced size then libburn pads up +by zeros. +When writing is done, it is mandatory to force the drive's buffer to media by +35h SYNCHRONIZE CACHE. +(mmc5r03c.pdf, 6.41) + +No further finalization is necessary. (I.e. no 5Bh CLOSE TRACK SESSION.) + + +------------------------------------------------------------------------------- +Obtaining DVD-R[W] multi-session info for extending ISO-9660 filesystems : +(valid for DVD+R too) + +Like with CD it is necessary to obtain the two numbers for mkisofs option -C +in order to prepare a ISO-9660 filesystem image which by its inner pointers +matches the block addresses of the future location on media. +These are the start address of the first track in the last complete session +and the predicted start address of the track which will host the new image. +See TAO Multi-Session CD Cookbook for some more info about mkisofs aspects. + + +The first number may be gained by 43h READ TOC/PMA/ATIP Format 0001b which in +table 478 promises quick access via Start Address Of First Track In Last +Session. +(mmc5r03c.pdf 6.26.2.5 table 478, 6.26.3.3.1) +Regrettably the MMC-5 specs still define a useless reply for non-CD media +which obviously stems from MMC-3 times when no multi-session was possible +with non-CD. +(mmc5r03c.pdf 6.26.3.3.3) +Both my drives do give a useful reply with the correct number for appendable +DVD-RW. But not being backed by the specs this method appears unappealing . + +Another approach would be a formatted Table of Content, obtained by 43h READ +TOC/PMA/ATIP Format 0000b. The specs do not totally outrule that this returns +useful data with non-CD but they define a crippled TOC for multi-session. +(mmc5r03c.pdf 6.26.3.2.4) +My LG drive returns a more detailed TOC, my NEC drive stays with the rather +suboptimal specs. So one would get different TOCs on different drives. +Nevertheless, the MMC-5 compliant TOC would return the desired number in +the Track Start address of the track with the highest number before AAh. + +Most stable seems the approach to obtain the desired number from the reply +of 52h READ TRACK INFORMATION, Address/Number Type 01b. The field Logical Block +Address/Track/Session has to bear the track number of the first track in the +last complete session. To determine this number one has to determine the +number of the last session and the number of the last track from 51h READ DISC +INFORMATION and to iterate over the tracknumber by 52h READ TRACK INFORMATION +until the first track with the desired session number appears and reveils +its start address. +(mmc5r03c.pdf 6.22 51h DISC, 6.27 52h TRACK) +This method is very near to fabricating an own TOC. So libburn does this +when inspecting the media. If the first number for -C is needed, libburn +inquires its TOC model for the address of the first track in the last +complete session. See below for a detailed description of TOC fabrication. + + +The second -C number is the exact prediction of future track start address. It +is gained like with CD by 52h READ TRACK INFORMATION Type 01b. Different from +CD one may not use track number FFh but has to use the Last Track in Last +Session from 51h READ DISC INFORMATION. +(mmc5r03c.pdf 6.22 51h DISC, 6.27 52h TRACK) + + +------------------------------------------------------------------------------- +Obtaining a Table Of Content from DVD-R[W]: +(valid for DVD+R too) + +The raw TOC entries from 43h READ TOC/PMA/ATIP Format 0010b as described with +CD media are not available with non-CD. +There is a Format 0000b "Formatted TOC" but this is with non-CD a fictional +information much at the discretion of the drive. Two drives with the same disc +may well return different Formatted TOC. They are supposed to be consistent +only about the last complete session and even there the MMC-5 specification +6.26.3.2.5 seems to prescribe a structure which does not match the true +structure of incremental writing to sequential DVD-R[W]. +(mmc5r03c.pdf 6.26.3.2) +So i prefer not to use this method of getting a TOC. + + +The alternative is to produce an own TOC from information gained by 51h READ +DISC INFORMATION and by 52h READ TRACK INFORMATION which reveil a CD-like +structure of sessions and 1:n related tracks. + +51h READ DISC INFORMATION Data Type 000b, fields Number of Sessions (Least +Significant Byte) and Number of Sessions (Most Significant Byte) give the +number of sessions. The last complete session number of an appendable disc +is one less because there is an incomplete session at its end. libburn only +records complete sessions in its TOC model. +libburn uses Last Track in Last Session as a hint for the range of track +numbers. +(mmc5r03c.pdf 6.22) + +Next step is to iterate from 1 up to the last track number and to obtain +the according track info by 52h READ TRACK INFORMATION. Each track tells its +Session Number (LSB at byte 2, MSB at 33), its Logical Track Start Address, +its Logical Track Size, and much more which is not needed for a fake CD TOC. +One may analyze the track info more finely but for this special purpose +it is enough to discard the tracks which do not belong to complete sessions. +(mmc5r03c.pdf 6.27) + +At the end of each session libburn inserts fake leadout entries into its TOC +model. Their start address is computed from the start and size of the last +track of the session. + + +------------------------------------------------------------------------------- +Hearsay about DVD-R/DL (Dual Layer) : + +DVD-R/DL can assume profile 0015h DVD-R Dual Layer Sequential which is supposed +to behave like DVD-R or 0016h DVD-R Dual Layer Jump which has no counterpart +with DVD-R. + +A half-sentence in mmc5r03c.pdf 6.3.3.3.3 might indicate that closing a session +by 5Bh CLOSE TRACK SESSION Close Function 010b overrides the multi-session bits +in mode page 05h. +growisofs applies this function in case of not DAO, though. A comment in +growisofs_mmc.cpp states: "// DVD-R DL Seq has no notion of multi-session". +I am not reading this from the specs - but not explicitely the contrary either. + +For now libburn will close the session but there is a macro prepared in +libburn/write.c Libburn_dvd_r_dl_multi_no_close_sessioN which will suppress +close session if multi-session is demanded. + + +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- + DVD+R Cookbook +------------------------------------------------------------------------------- +Inspired by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/ +backed by Andy Polyakov's http://fy.chalmers.se/~appro/linux/DVD+RW/tools , + +For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> + +------------------------------------------------------------------------------- + +Media type can be recognized by Current Profile from 46h GET CONFIGURATION. +(mmc5r03c.pdf 6.6.2.1) +DVD+R 001bh +DVD+R/DL 002bh + +- About empty, appendable and finalized DVD+R +- Writing a Pseudo Session to DVD+R +- >>> Hearsay about DVD+R/DL (Dual Layer) : + +The following two chapters of the Sequential DVD-R[W] Cookbook are valid for +DVD+R media too: +- Obtaining DVD-R[W] multi-session info for extending ISO-9660 filesystems +- Obtaining a Table Of Content from DVD-R[W] + +------------------------------------------------------------------------------- +About blank, appendable and finalized DVD+R : + +In the beginning a DVD+R holds an empty session and the Incomplete Fragment. +From these one may spawn reserved fragments or one may write directly to +the incomplete fragment. As soon as this is done the empty session becomes the +open session which finally needs to get closed. By closing fragments and +session a new empty session with empty Incomplete Fragment gets spawned. +So the disc stays appendable. + +A DVD+R may hold 153 closed sessions with a single track each. +The open session may hold up to 15 open fragments. But on closure of the +session those fragments together form a single logical track. So one will +usually only use a single fragment for sequential writing. +(mmc5r03c.pdf 4.3.6.2) + +The disc may get finalized by another close command so that no more data can +be written. +(mmc5r03c.pdf 6.3.3.4.4) + + +------------------------------------------------------------------------------- +Writing a Pseudo Session to DVD+R : + +Session writing has to be pseudo because only one logical track per session +can be distinguished. So actually there have to be written multiple sessions +to mark multiple tracks. The pseudo session cannot get marked on disc and thus +the tracks of a pseudo session cannot be grouped accordingly in a TOC. + +Speed can be influenced by B6h SET STREAMING , speed capabilities can be +inquired by ACh GET PERFORMANCE. It is advised to set only speeds and sizes +which are returned by ACh. +(mmc5r03c.pdf 6.39 SET STREAMING, 6.8 GET PERFORMANCE) + +No mode page 05h is to be sent. +growisofs sends a page but the specs clearly state that one shall not do. +(mmc5r03c.pdf 7.5.3) + +It is optional wether a track size is reserved in advance or not. Eventually +this is done by 53h RESERVE TRACK, RMZ=ARSV=0. Reservation size should better +already be aligned to 32 KiB. +(mmc5r03c.pdf 6.31) +The specs promise to pad up the track if not enough data get written. +(mmc5r03c.pdf 6.3.3.4.2) + +Next Writeable Address is fetched from the reply of 52h READ TRACK INFORMATION +with track number FFh. +(mmc5r03c.pdf 6.27) +Since the fixely set write type is 16-block packet, full 32 kiB buffers have +to be transmitted via 2Ah WRITE. +(mmc5r03c.pdf 4.3.6.2.2) + +When writing is done, it is mandatory to force the drive's buffer to media by +35h SYNCHRONIZE CACHE. +(mmc5r03c.pdf 6.41) + +The written fragment (i.e. track-to-be) has to be closed by 5Bh CLOSE TRACK +SESSION Close Function 001b. +(mmc5r03c.pdf 6.3.3.4.2) +libburn obtains the necessary logical track number from Last Track Number in +Last Session from the reply of 51h READ DISC INFORMATION requesting +Data Type 000b. +(mmc5r03c.pdf 6.22) + +After each track 5Bh CLOSE TRACK SESSION Close Function 010b with Logical Track +Number 0 closes the DVD+R session but keeps the media appendable. +(mmc5r03c.pdf 6.3.3.4.3) +If the media shall not stay appendable then the last DVD+R session is to be +closed by Close Function 101b rather than 010b. This finalizes the media +"with minimal radius". +(mmc5r03c.pdf 6.3.3.4.4) + +Note: growisofs has code for that gesture but explicitly avoids to use it, if +the media was appendable before writing began. Instead it recommends to fill +up the media with zeros. This gesture nevertheless caused error replies from +the drives in my own experiments. +The reason given by Andy Polyakov is that some DVD-ROM drives get mislead by +the lead-out information of (formerly) appendable media unless the media is +fully written. +(http://fy.chalmers.se/~appro/linux/DVD+RW/ , "Compatibility: caveat lector") + +Own experiments showed no such problems with PC attached PATA DVD-ROM drives. +For best DVD-ROM compatibility one should avoid appendable media at all +by closing them already after the first session. + + +------------------------------------------------------------------------------- +Hearsay about DVD+R/DL (Dual Layer) : + +>>> + +------------------------------------------------------------------------------- + diff --git a/libburn/branches/ZeroFourTwo/doc/ddlp.txt b/libburn/branches/ZeroFourTwo/doc/ddlp.txt new file mode 100644 index 00000000..9211ea57 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/doc/ddlp.txt @@ -0,0 +1,388 @@ +------------------------------------------------------------------------------- + +Users of modern desktop Linux installations report misburns with CD/DVD +recording due to concurrency problems. + +This text describes two locking protocols which have been developed by our +best possible effort. But finally they rather serve as repelling example of +what would be needed in user space to achieve an insufficient partial solution. + +Ted Ts'o was so friendly to help as critic with his own use cases. It turned +out that we cannot imagine a way in user space how to cover reliably the needs +of callers of libblkid and the needs of our burn programs. + +------------------------------------------------------------------------------- +Content: + +The "Delicate Device Locking Protocol" shall demonstrate our sincere +consideration of the problem. + +"What are the Stumble Stones ?" lists reasons why the effort finally failed. + +----------------------------------------------------------------------------- + + + Delicate Device Locking Protocol + (a joint sub project of cdrkit and libburnia) + (contact: scdbackup@gmx.net ) + +Our projects provide programs which allow recording of data on CD or DVD. +We encounter an increasing number of bug reports about spoiled burn runs and +wasted media which obviously have one common cause: interference by other +programs which access the drive's device files. +There is some riddling about which gestures exactly are dangerous for +ongoing recordings or can cause weirdly misformatted drive replies to MMC +commands. +We do know, nevertheless, that these effects do not occur if no other program +accesses a device file of the drive while our programs use it. + +DDLP shall help to avoid collisions between programs in the process of +recording to a CD or DVD drive and other programs which access that drive. +The protocol intends to provide advisory locking. So any good-willing program +has to take some extra precautions to participate. + +If a program does not feel vulnerable to disturbance, then the precautions +impose much less effort than if the program feels the need for protection. + +Two locking strategies are specified: +DDLP-A operates on device files only. It is very Linux specific. +DDLP-B adds proxy lock files, inspired by FHS /var/lock standard. + + + DDLP-A + +This protocol relies on the hardly documented feature open(O_EXCL | O_RDWR) +with Linux device files and on POSIX compliant fcntl(F_SETLK). + +Other than the original meaning of O_EXCL with creating regular files, the +effect on device files is mutual exclusion of access. I.e. if one +filedescriptor is open on that combination of major-minor device number, then +no other open(O_EXCL) will succeed. But open() without O_EXCL would succeed. +So this is advisory and exclusive locking. +With kernel 2.6 it seems to work on all device drivers which might get used +to access a CD/DVD drive. + +The vulnerable programs shall not start their operation before they occupied a +wide collection of drive representations. +Non-vulnerable programs shall take care to detect the occupation of _one_ such +representation. + + So for Friendly Programs + +A program which does not feel vulnerable to disturbance is urged to access +CD/DVD drives by opening a file descriptor which will uphold the lock +as long as it does not get closed. There are two alternative ways to achieve +this. +Very reliable is + + open( some_path , O_EXCL | ...) + +But O_EXCL imposes restrictions and interferences: +- O_EXCL | O_RDONLY does not succeed with /dev/sg* ! +- O_EXCL cannot provide shared locks for programs which only want to lock + against burn programs but not against their own peers. +- O_EXCL keeps from obtaining information by harmless activities. +- O_EXCL already has a meaning with devices which are mounted as filesystems. + This priority meaning is more liberal than the one needed for CD/DV recording + protection. + +So it may be necessary to use a cautious open() without O_EXCL and to aquire +a POSIX lock via fcntl(). "Cautious" means to add O_NDELAY to the flags of +open(), because this is declared to avoid side effects within open(). + +With this gesture it is important to use the paths expected by our burn +programs: /dev/sr[0..255] /dev/scd[0..255] /dev/sg[0..255] /dev/hd[a..z] +because fcntl(F_SETLK) does not lock the device but only a device-inode. + + std_path = one of the standard device files: + /dev/sr[0..255] /dev/scd[0..255] /dev/sg[0..255] /dev/hd[a..z] + or a symbolic link pointing to one of them. + open( std_path , ... | O_NDELAY) + fcntl(F_SETLK) and close() on failure + ... eventually disable O_NDELAY by fcntl(F_SETFL) ... + +There is a pitfall mentioned in man 2 fcntl : + "locks are automatically released [...] if it closes any file descriptor + referring to a file on which locks are held. This is bad [...]" +So you may have to re-lock after some temporary fd got closed. + + + Vulnerable Programs + +For programs which do feel vulnerable, O_EXCL would suffice for the /dev/hd* +device file family and their driver. But USB and SATA recorders appear with +at least two different major-minor combinations simultaneously. +One as /dev/sr* alias /dev/scd*, the other as /dev/sg*. +The same is true for ide-scsi or recorders attached to SCSI controllers. + +So, in order to lock any access to the recorder, one has to open(O_EXCL) +not only the device file that is intended for accessing the recorder but also +a device file of any other major-minor representation of the recorder. +This is done via the SCSI address parameter vector (Host,Channel,Id,Lun) +and a search on standard device file paths /dev/sr* /dev/scd* /dev/sg*. +In this text the alternative device representations are called "siblings". + +For finding them, it is necessary to apply open() to many device files which +might be occupied by delicate operations. On the other hand it is very +important to occupy all reasonable representations of the drive. +So the reading of the (Host,Channel,Id,Lun) parameters demands an +open(O_RDONLY | O_NDELAY) _without_ fcntl() in order to find the outmost +number of representations among the standard device files. Only ioctls +SCSI_IOCTL_GET_IDLUN and SCSI_IOCTL_GET_BUS_NUMBER are applied. +Hopefully this gesture is unable to cause harmful side effects on kernel 2.6. + +At least one file of each class sr, scd and sg should be found to regard +the occupation as satisfying. Thus corresponding sr-scd-sg triplets should have +matching ownerships and access permissions. +One will have to help the sysadmins to find those triplets. + +A spicy detail is that sr and scd may be distinct device files for the same +major-minor combination. In this case fcntl() locks on both are needed +but O_EXCL can only be applied to one of them. + + +An open and free implementation ddlpa.[ch] is provided as + http://libburnia.pykix.org/browser/libburn/trunk/libburn/ddlpa.h?format=txt + http://libburnia.pykix.org/browser/libburn/trunk/libburn/ddlpa.c?format=txt +The current version of this text is + http://libburnia.pykix.org/browser/libburn/trunk/doc/ddlp.txt?format=txt + +Put ddlpa.h and ddlpa.c into the same directory and compile as test program by + cc -g -Wall -DDDLPA_C_STANDALONE -o ddlpa ddlpa.c + +Use it to occupy a drive's representations for a given number of seconds + ./ddlpa /dev/sr0 300 + +It should do no harm to any of your running activities. +If it does: Please, please alert us. + +Your own programs should not be able to circumvent the occupation if they +obey above rules for Friendly Programs. +Of course ./ddlpa should be unable to circumvent itself. + +A successfull occupation looks like + DDLPA_DEBUG: ddlpa_std_by_rdev("/dev/scd0") = "/dev/sr0" + DDLPA_DEBUG: ddlpa_collect_siblings() found "/dev/sr0" + DDLPA_DEBUG: ddlpa_collect_siblings() found "/dev/scd0" + DDLPA_DEBUG: ddlpa_collect_siblings() found "/dev/sg0" + DDLPA_DEBUG: ddlpa_occupy() : '/dev/scd0' + DDLPA_DEBUG: ddlpa_occupy() O_EXCL : '/dev/sg0' + DDLPA_DEBUG: ddlpa_occupy() O_EXCL : '/dev/sr0' + ---------------------------------------------- Lock gained + ddlpa: opened /dev/sr0 + ddlpa: opened siblings: /dev/scd0 /dev/sg0 + slept 1 seconds of 300 + +Now an attempt via device file alias /dev/NEC must fail: + DDLPA_DEBUG: ddlpa_std_by_rdev("/dev/NEC") = "/dev/sg0" + DDLPA_DEBUG: ddlpa_collect_siblings() found "/dev/sr0" + DDLPA_DEBUG: ddlpa_collect_siblings() found "/dev/scd0" + DDLPA_DEBUG: ddlpa_collect_siblings() found "/dev/sg0" + Cannot exclusively open '/dev/sg0' + Reason given : Failed to open O_RDWR | O_NDELAY | O_EXCL : '/dev/sr0' + Error condition : 16 'Device or resource busy' + +With hdc, of course, things are trivial + DDLPA_DEBUG: ddlpa_std_by_rdev("/dev/hdc") = "/dev/hdc" + DDLPA_DEBUG: ddlpa_occupy() O_EXCL : '/dev/hdc' + ---------------------------------------------- Lock gained + ddlpa: opened /dev/hdc + slept 1 seconds of 1 + + +Ted Ts'o provided program open-cd-excl which allows to explore open(2) on +device files with combinations of read-write, O_EXCL, and fcntl(). +(This does not mean that Ted endorsed our project yet. He helps exploring.) + +Friendly in the sense of DDLP-A would be any run which uses at least one of +the options -e (i.e. O_EXCL) or -f (i.e. F_SETLK, applied to a file +descriptor which was obtained from a standard device file path). +The code is available under GPL at + http://libburnia.pykix.org/browser/libburn/trunk/test/open-cd-excl.c?format=txt +To be compiled by + cc -g -Wall -o open-cd-excl open-cd-excl.c + +Options: + -e : open O_EXCL + -f : aquire lock by fcntl(F_SETLK) after sucessful open + -i : do not wait in case of success but exit 0 immediately + -r : open O_RDONLY , with -f use F_RDLCK + -w : open O_RDWR , with -f use F_WRLCK + plus the path of the devce file to open. + +Friendly Programs would use gestures like: + ./open-cd-excl -e -r /dev/sr0 + ./open-cd-excl -e -w /dev/sg1 + ./open-cd-excl -e -w /dev/black-drive + ./open-cd-excl -f -r /dev/sg1 + ./open-cd-excl -e -f -w /dev/sr0 + +Ignorant programs would use and cause potential trouble by: + ./open-cd-excl -r /dev/sr0 + ./open-cd-excl -w /dev/sg1 + ./open-cd-excl -f -w /dev/black-drive +where "/dev/black-drive" is _not_ a symbolic link to +any of /dev/sr* /dev/scd* /dev/sg* /dev/hd*, but has an own inode. + +Prone to failure without further reason is: + ./open-cd-excl -e -r /dev/sg1 + + +---------------------------------------------------------------------------- + + DDLP-B + +This protocol relies on proxy lock files in some filesystem directory. It can +be embedded into DDLP-A or it can be used be used standalone, outside DDLP-A. + +DDLP-A shall be kept by DDLP-B from trying to access any device file which +might already be in use. There is a problematic gesture in DDLP-A when SCSI +address parameters are to be retrieved. For now this gesture seems to be +harmless. But one never knows. +Vice versa DDLP-B may get from DDLP-A the service to search for SCSI device +file siblings. So they are best as a couple. + +But they are not perfect. Not even as couple. fcntl() locking is flawed. + + +There is a proxy file locking protocol described in FHS: + http://www.pathname.com/fhs/pub/fhs-2.3.html#VARLOCKLOCKFILES + +But it has shortcommings (see below). Decisive obstacle for its usage are the +possibility for stale locks and the lack of shared locks. + +DDLP-B rather defines a "path prefix" which is advised to be + /tmp/ddlpb-lock- +This prefix will get appended "device specific suffixes" and then form the path +of a "lockfile". +Not the existence of a lockfile but its occupation by an fcntl(F_SETLK) will +constitute a lock. Lockfiles may get prepared by the sysadmin in directories +where normal users are not allowed to create new files. Their rw-permissions +then act as additional access restriction to the device files. +The use of fcntl(F_SETLK) will prevent any stale locks after the process ended. +It will also allow to obtain shared locks as well as exclusive locks. + +There are two classes of device specific suffixes: + +- Device file path suffix. Absolute paths only. "/" gets replaced by "_-". + Eventual "_-" in path gets replaced by "_-_-". The leading group of "_-" + is always interpreted as a group of "/", though. E.g.: + /dev/sr0 <-> "_-dev_-sr0" + /mydevs/burner/nec <-> "_-mydevs_-burners_-nec" + /dev/rare_-name <-> "_-dev_-rare_-_-name" + ///strange/dev/x <-> "_-_-_-strange_-dev_-x" + +- st_rdev suffix. A hex representation of struct stat.st_rdev. Capital letters. + The number of characters is pare with at most one leading 0. I.e. bytewise + printf("%2.2X") beginning with the highest order byte that is not zero. + E.g. : "0B01", "2200", "01000000000004001" + +If a lockfile does not exist and cannot be created then this shall not keep +a program from working on a device. But if a lockfile exists and if permissions +or locking state do not allow to obtain a lock of the appropirate type, then +this shall prevent any opening of device file in question resp. shall cause +immediate close(2) of an already opened device file. + +The vulnerable programs shall not start their operation before they locked a +wide collection of drive representations. + +Non-vulnerable programs shall take care to lock the suffix resulting from the +path they will be using and the suffix from the st_rdev from that path. +The latter is to be obtained by call stat(2). + +Locks get upheld as long as their file descriptor is not closed or no other +incident as described in man 2 fcntl releases the lock. + +So with shared locks there are no imandatory further activities after they +have been obtained. + +In case of exclusive locks, the file has to have been opened for writing and +must be truncated to 0 bytes length immediately after obtaining the lock. +When releasing an exclusive lock it is a nice gesture to +already do this truncation. +Then a /var/lock/ compatible first line has to be written. +E.g. by: printf("%10u\n",(unsigned) getpid()) yielding " 1230\n". + +Any further lines are optional. They shall have the form Name=Value and must +be printable cleartext. If such further lines exist, then the last one must +have the name "endmark". +Defined Names are: + hostid =hostname of the machine where the process number of line 1 is valid + start =start time of lock in seconds since 1970. E.g: 1177147634.592410 + program =self chosen name of the program which obtained the lock + argv0 =argv[0] of that program + mainpath =device file path which will be used for operations by that program + path =device file path which lead to the lock + st_rdev =st_rdev suffix which is associated with path + scsi_hcil=eventual SCSI parameters Host,Channel,Id,Lun + scsi_bus =eventual SCSI parameter Bus + endmark =declares the info as complete. +Any undefined name or a line without "=" shall be handled as comment. +"=" in the value is allowed. Any line beginning with an "=" character is an +extension of the previous value. + +If programs encounter an exclusive lock, they are invited to read the content +of the lockfile anyway. But they should be aware that the info might be in the +progress of emerging. There is a race condition possible in the short time +between obtaining the exclusive lock and erasing the file content. +If it is not crucial to obtain most accurate info then one may take the newline +of the first line as indicator of a valid process number and the "endmark" +name as indicator that the preceding lines are valid. +Very cautious readers should obtain the info twice with a decent waiting period +inbetween. Only if both results are identical they should be considered valid. + + +There is no implementation of DDLP-B yet. + + + +---------------------------------------------------------------------------- +What are the Stumble Stones ? +---------------------------------------------------------------------------- + +Any of the considered locking mechanisms has decisive shortcommings +which keeps it from being the solution to all known legitimate use cases. + +The attempt has failed to compose a waterproof locking mechanism from means of +POSIX, FHS and from hardly documented Linux open(O_EXCL) on device files. +The resulting mechanisms would need about 1000 lines of code and still do +not close all gaps resp. cover the well motivated use cases. +This attempt you see above: DDLP-A and DDLP-B. + + +Summary of the reasons why the established locking mechanisms do not suffice: + +None of the mechanisms can take care of the double device driver identity +sr versus sg. To deduce the one device file from the other involves the need +to open many other (possibly unrelated) device files with the risk to disturb +them. +This hard to solve problem is aggravated by the following facts. + +Shortcommings of Linux specific open(O_EXCL) : + +- O_EXCL | O_RDONLY does not succeed with /dev/sg* +- O_EXCL cannot provide shared locks for programs which only want to lock + against burn programs but not against their own peers. +- O_EXCL keeps from obtaining information by harmless activities. +- O_EXCL already has a meaning with devices which are mounted as filesystems. + This priority meaning is more liberal than the one needed for CD/DV recording + protection. + +Shortcommings of POSIX fcntl(F_SETLK) : + +- fcntl() demands an open file descriptor. open(2) might have side effects. +- fcntl() locks can be released inadvertedly by submodules which just open and + close the same file (inode ?) without refering to fcntl locks in any way. + See man 2 fcntl "This is bad:". + Stacking of software modules is a widely used design pattern. But fcntl() + cannot cope with that. + +Shortcommings of FHS /var/lock/ : + +- Stale locks are possible. +- It is necessary to create a file (using the _old_ meaning of O_EXCL flag ?) + but /var/lock/ might not be available early during system start and it often + has restrictive permission settings. +- There is no way to indicate a difference between exclusive and shared locks. +- The FHS prescription relies entirely on the basename of the device file path. + diff --git a/libburn/branches/ZeroFourTwo/doc/doxygen.conf.in b/libburn/branches/ZeroFourTwo/doc/doxygen.conf.in new file mode 100644 index 00000000..e36d6010 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/doc/doxygen.conf.in @@ -0,0 +1,1300 @@ +# Doxyfile 1.5.3 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file that +# follow. The default is UTF-8 which is also the encoding used for all text before +# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into +# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of +# possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = @PACKAGE_NAME@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @PACKAGE_VERSION@ + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, +# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, +# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, +# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = @top_srcdir@ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = YES + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be extracted +# and appear in the documentation as a namespace called 'anonymous_namespace{file}', +# where file will be replaced with the base name of the file that contains the anonymous +# namespace. By default anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text " + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = libburn \ + doc \ + test + +# This tag can be used to specify the character encoding of the source files that +# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default +# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. +# See http://www.gnu.org/software/libiconv for the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = libburn.h \ + comments \ + libburner.c + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the output. +# The symbol name can be a fully qualified name, a word, or if the wildcard * is used, +# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = test + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH +# then you must also enable this option. If you don't then doxygen will produce +# a warning and turn it on anyway + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = YES + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = OB \ + OTK \ + _ + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = doc/html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 200 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = letter + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = DOXYGEN + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to +# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to +# specify the directory where the mscgen tool resides. If left empty the tool is assumed to +# be found in the default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will +# generate a caller dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = NO + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the number +# of direct children of the root node in a graph is already larger than +# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/libburn/branches/ZeroFourTwo/libburn-5.pc.in b/libburn/branches/ZeroFourTwo/libburn-5.pc.in new file mode 100644 index 00000000..38f4530e --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn-5.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/branches/ZeroFourTwo/libburn/Makefile b/libburn/branches/ZeroFourTwo/libburn/Makefile new file mode 100644 index 00000000..062350dd --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/libburn/branches/ZeroFourTwo/libburn/Makefile.am b/libburn/branches/ZeroFourTwo/libburn/Makefile.am new file mode 100644 index 00000000..144ece71 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/asserts.txt b/libburn/branches/ZeroFourTwo/libburn/asserts.txt new file mode 100644 index 00000000..b0791582 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/async.c b/libburn/branches/ZeroFourTwo/libburn/async.c new file mode 100644 index 00000000..ff67865d --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/async.c @@ -0,0 +1,537 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + + +/* ts A71019 */ + +/* Standard measure should be: Threads are created detached. + According to the man pages they should then care for disposing themselves. + + >>> ??? It is yet unclear why the threads vanish from the process list + even if joinable and even if never joined. + + To be activated after release of libburn-0.4.0 +*/ +#define Libburn_create_detached_threadS 1 + +/* Alternative : Threads are created joinable. + Threads get detached in remove_worker() and thus should dispose themselves. + +#define Libburn_detach_done_workeR 1 +*/ + + +#include "libburn.h" +#include "transport.h" +#include "drive.h" +#include "write.h" +#include "options.h" +#include "async.h" +#include "init.h" +#include "file.h" +#include "back_hacks.h" + +#include <pthread.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.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 fifo_opts +{ + struct burn_source *source; + int flag; +}; + + +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; + struct fifo_opts fifo; + } u; +}; + +static struct w_list *workers = NULL; + + +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; + pthread_attr_t *attr_pt = NULL; + +#ifdef Libburn_create_detached_threadS + pthread_attr_t attr; +#endif + + 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 != NULL) + d->busy = BURN_DRIVE_SPAWNING; + +#ifdef Libburn_create_detached_threadS + /* ts A71019 : + Trying to start the threads detached to get rid of the zombies + which do neither react on pthread_join() nor on pthread_detach(). + */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + attr_pt= &attr; + libdax_msgs_submit(libdax_messenger, -1, 0x00020158, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_LOW, + "add_worker(): Creating detached thread.", 0, 0); +#endif + + if (pthread_create(&a->thread, attr_pt, f, a)) { + free(a); + workers = tmp; + return; + } +} + + +static void remove_worker(pthread_t th) +{ + struct w_list *a, *l = NULL; + +#ifdef Libburn_detach_done_workeR + int ret; + char msg[80]; +#endif + + for (a = workers; a; l = a, a = a->next) + if (a->thread == th) { + if (l) + l->next = a->next; + else + workers = a->next; + +#ifdef Libburn_detach_done_workeR + /* ts A71019 : burry dead puppy before forgetting it */ + /* Alternative : threads get detached and thus should + dispose themselves. + */ + ret = pthread_detach(th); + sprintf(msg, + "remove_workers(): pid= %lu pthread_detach(%lu)= %d", + (unsigned long) getpid(), (unsigned long) th, ret); + libdax_msgs_submit(libdax_messenger, -1, 0x00020158, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_LOW, + msg, 0, 0); + +#endif /* Libburn_detach_done_workeR */ + + 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) +{ + int ret; + + ret = burn_drive_scan_sync(w->u.scan.drives, w->u.scan.n_drives, 1); + if (ret <= 0) + w->u.scan.done = -1; + else + 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 == NULL) { + /* start it */ + + /* ts A61007 : test moved up from burn_drive_scan_sync() + was burn_wait_all() */ + /* ts A70907 : now demanding freed drives, not only released */ + if (!burn_drives_are_clear(1)) + 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; + + /* 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; + } + /* A70103 : will be set to 0 by burn_disc_erase_sync() */ + drive->cancel = 1; + + /* ts A70103 moved up from burn_disc_erase_sync() */ + /* ts A60825 : allow on parole to blank appendable CDs */ + /* ts A70131 : allow blanking of overwriteable DVD-RW (profile 0x13) */ + /* ts A70216 : allow blanking of CD-RW or DVD-RW in any regular state + and of any kind of full media */ + /* ts A70909 : the willingness to burn any BURN_DISC_FULL media is + inappropriate. One would rather need a -force option + Note: keep this in sync with mmc_read_disc_info() */ + if ((drive->current_profile != 0x0a && + drive->current_profile != 0x13 && + drive->current_profile != 0x14 && + drive->status != BURN_DISC_FULL) + || + (drive->status != BURN_DISC_FULL && + drive->status != BURN_DISC_APPENDABLE && + drive->status != BURN_DISC_BLANK) + || + (drive->drive_role != 1) + ) { + 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 (drive->drive_role != 1) { + libdax_msgs_submit(libdax_messenger, drive->global_index, + 0x00020146, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Drive is a virtual placeholder", 0, 0); + drive->cancel = 1; + 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) +{ + struct burn_drive *d = w->u.write.drive; + + d->thread_pid = getpid(); + d->thread_pid_valid= 1; + burn_disc_write_sync(w->u.write.opts, w->u.write.disc); + d->thread_pid_valid= 0; + d->thread_pid = 0; + + /* 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; + char reasons[BURN_REASONS_LEN+80]; + + /* 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; + } + + /* For the next lines any return indicates failure */ + opts->drive->cancel = 1; + + /* ts A70203 : people have been warned in API specs */ + if (opts->write_type == BURN_WRITE_NONE) + return; + + if (opts->drive->drive_role == 0) { + libdax_msgs_submit(libdax_messenger, opts->drive->global_index, + 0x00020146, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Drive is a virtual placeholder (null-drive)", 0, 0); + return; + } + + /* ts A61007 : obsolete Assert in spc_select_write_params() */ + if (opts->drive->drive_role == 1 && !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 A70219 : intended to replace all further tests here and many + tests in burn_*_write_sync() + */ + strcpy(reasons, "Write job parameters are unsuitable:\n"); + if (burn_precheck_write(opts, disc, reasons + strlen(reasons), 1) + == BURN_WRITE_NONE) { + libdax_msgs_submit(libdax_messenger, + opts->drive->global_index, 0x00020139, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + reasons, 0, 0); + return; + } + + opts->drive->cancel = 0; /* End of the return = failure area */ + + o.drive = opts->drive; + o.opts = opts; + o.disc = disc; + + opts->refcount++; + + add_worker(opts->drive, (WorkerFunc) write_disc_worker_func, &o); +} + + +static void *fifo_worker_func(struct w_list *w) +{ + burn_fifo_source_shoveller(w->u.fifo.source, w->u.fifo.flag); + remove_worker(pthread_self()); + return NULL; +} + + +int burn_fifo_start(struct burn_source *source, int flag) +{ + struct fifo_opts o; + struct burn_source_fifo *fs = source->data; + + fs->is_started = -1; + + /* create and set up ring buffer */; + fs->buf = calloc(fs->chunksize, fs->chunks); + if (fs->buf == NULL) { + /* >>> could not start ring buffer */; + return -1; + } + + o.source = source; + o.flag = flag; + add_worker(NULL, (WorkerFunc) fifo_worker_func, &o); + fs->is_started = 1; + return 1; +} + + +#ifdef Libburn_has_burn_async_join_alL + +/* ts A71019 : never used */ +void burn_async_join_all(void) +{ + void *ret; + + while (workers) + pthread_join(workers->thread, &ret); +} + +#endif /* Libburn_has_burn_async_join_alL */ + + diff --git a/libburn/branches/ZeroFourTwo/libburn/async.h b/libburn/branches/ZeroFourTwo/libburn/async.h new file mode 100644 index 00000000..9a6b5141 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/async.h @@ -0,0 +1,14 @@ +/* -*- 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; + +/* ts A70930 */ +/* To be called when the first read() call comes to a fifo */ +int burn_fifo_start(struct burn_source *source, int flag); + + +#endif /* BURN__ASYNC_H */ diff --git a/libburn/branches/ZeroFourTwo/libburn/back_hacks.h b/libburn/branches/ZeroFourTwo/libburn/back_hacks.h new file mode 100644 index 00000000..b086620a --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/cleanup.c b/libburn/branches/ZeroFourTwo/libburn/cleanup.c new file mode 100644 index 00000000..fc923830 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/cleanup.h b/libburn/branches/ZeroFourTwo/libburn/cleanup.h new file mode 100644 index 00000000..a9d35519 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/crc.c b/libburn/branches/ZeroFourTwo/libburn/crc.c new file mode 100644 index 00000000..fddc5b4f --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/crc.h b/libburn/branches/ZeroFourTwo/libburn/crc.h new file mode 100644 index 00000000..a4846a33 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/ddlpa.c b/libburn/branches/ZeroFourTwo/libburn/ddlpa.c new file mode 100644 index 00000000..b58e021e --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/ddlpa.c @@ -0,0 +1,614 @@ + +/* ddlpa + Implementation of Delicate Device Locking Protocol level A. + Copyright (C) 2007 Thomas Schmitt <scdbackup@gmx.net> + Provided under any of the following licenses: GPL, LGPL, BSD. Choose one. + + + Compile as test program: + + cc -g -Wall \ + -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1 -D_LARGEFILE64_SOURCE \ + -DDDLPA_C_STANDALONE -o ddlpa ddlpa.c + + The system macros enable 64-bit off_t and open(2) flag O_LARGEFILE, which + are not absolutely necessary but explicitely take into respect that + our devices can offer more than 2 GB of addressable data. + + Run test program: + + ./ddlpa /dev/sr0 15 + ./ddlpa 0,0,0 15 + +*/ + +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <scsi/scsi.h> + + +/* All callers of ddlpa must do this */ +#include "ddlpa.h" + + +/* 1 = Enable progress message on stderr, 0 = normal silent operation */ +static int ddlpa_debug_mode = 1; + + +/* #define _GNU_SOURCE or _LARGEFILE64_SOURCE to get real O_LARGEFILE */ +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + + +/* ----------------------- private -------------------- */ + + +static int ddlpa_new(struct ddlpa_lock **lck, int o_flags, int ddlpa_flags) +{ + int i; + struct ddlpa_lock *o; + + o = *lck = (struct ddlpa_lock *) malloc(sizeof(struct ddlpa_lock)); + if (o == NULL) + return ENOMEM; + for (i = 0; i < sizeof(struct ddlpa_lock); i++) + ((char *) o)[i] = 0; + o->path = NULL; + o->fd = -1; + for (i = 0; i < DDLPA_MAX_SIBLINGS; i++) + o->sibling_fds[i] = -1; + o->errmsg = NULL; + + o->o_flags = o_flags; + o->ddlpa_flags = ddlpa_flags; + return 0; +} + + +static int ddlpa_enumerate(struct ddlpa_lock *o, int *idx, + char path[DDLPA_MAX_STD_LEN + 1]) +{ + if (*idx < 0) + *idx = 0; + + if (*idx < 26) + sprintf(path, "/dev/hd%c", 'a' + *idx); + else if (*idx < 256 + 26) + sprintf(path, "/dev/sr%d", *idx - 26); + else if (*idx < 2 * 256 + 26) + sprintf(path, "/dev/scd%d", *idx - 256 - 26); + else if (*idx < 3 * 256 + 26) + sprintf(path, "/dev/sg%d", *idx - 2 * 256 - 26); + else + return 1; + (*idx)++; + return 0; +} + + +static int ddlpa_std_by_rdev(struct ddlpa_lock *o) +{ + int idx = 0; + char try_path[DDLPA_MAX_STD_LEN+1]; + struct stat path_stbuf, try_stbuf; + + if (!o->path_is_valid) + return EFAULT; + if (stat(o->path, &path_stbuf) == -1) + return errno; + + while (ddlpa_enumerate(o, &idx, try_path) == 0) { + if (stat(try_path, &try_stbuf) == -1) + continue; + if (path_stbuf.st_rdev != try_stbuf.st_rdev) + continue; + strcpy(o->std_path, try_path); + + if (ddlpa_debug_mode) + fprintf(stderr, + "DDLPA_DEBUG: ddlpa_std_by_rdev(\"%s\") = \"%s\"\n", + o->path, o->std_path); + + return 0; + } + return ENOENT; +} + + +/* Caution : these tests are valid only with standard paths */ + +static int ddlpa_is_scsi(struct ddlpa_lock *o, char *path) +{ + return (strncmp(path, "/dev/s", 6) == 0); +} + +static int ddlpa_is_sg(struct ddlpa_lock *o, char *path) +{ + return (strncmp(path, "/dev/sg", 7) == 0); +} + +static int ddlpa_is_sr(struct ddlpa_lock *o, char *path) +{ + return (strncmp(path, "/dev/sr", 7) == 0); +} + +static int ddlpa_is_scd(struct ddlpa_lock *o, char *path) +{ + return (strncmp(path, "/dev/scd", 8) == 0); +} + + +static int ddlpa_fcntl_lock(struct ddlpa_lock *o, int fd, int l_type) +{ + struct flock lockthing; + int ret; + + memset(&lockthing, 0, sizeof(lockthing)); + lockthing.l_type = l_type; + lockthing.l_whence = SEEK_SET; + lockthing.l_start = 0; + lockthing.l_len = 0; + ret = fcntl(fd, F_SETLK, &lockthing); + if (ret == -1) + return EBUSY; + return 0; +} + + +static int ddlpa_occupy(struct ddlpa_lock *o, char *path, int *fd, + int no_o_excl) +{ + int ret, o_flags, o_rw, l_type; + char *o_rwtext; + + o_flags = o->o_flags | O_NDELAY; + if(!no_o_excl) + o_flags |= O_EXCL; + o_rw = (o_flags) & (O_RDONLY | O_WRONLY | O_RDWR); + o_rwtext = (o_rw == O_RDONLY ? "O_RDONLY" : + (o_rw == O_WRONLY ? "O_WRONLY" : + (o_rw == O_RDWR ? "O_RDWR " : "O_?rw-mode?"))); + + *fd = open(path, o_flags); + if (*fd == -1) { + o->errmsg = malloc(strlen(path)+160); + if (o->errmsg) + sprintf(o->errmsg, + "Failed to open %s | O_NDELAY %s: '%s'", + o_rwtext, + (o_flags & O_EXCL ? "| O_EXCL " : ""), path); + return (errno ? errno : EBUSY); + } + if (o_rw == O_RDWR || o_rw == O_WRONLY) + l_type = F_WRLCK; + else + l_type = F_RDLCK; + ret = ddlpa_fcntl_lock(o, *fd, l_type); + if (ret) { + o->errmsg = malloc(strlen(path)+160); + if (o->errmsg) + sprintf(o->errmsg, + "Failed to lock fcntl(F_WRLCK) : '%s'",path); + close(*fd); + *fd = -1; + return ret; + } + if (ddlpa_debug_mode) + fprintf(stderr, "DDLPA_DEBUG: ddlpa_occupy() %s %s: '%s'\n", + o_rwtext, + (no_o_excl ? " " : "O_EXCL "), path); + return 0; +} + + +static int ddlpa_obtain_scsi_adr(struct ddlpa_lock *o, char *path, + int *bus, int *host, int *channel, int *id, int *lun) +{ + int fd, ret, open_mode = O_RDONLY | O_NDELAY; + struct my_scsi_idlun { + int x; + int host_unique_id; + }; + struct my_scsi_idlun idlun; + + fd = open(path, open_mode); + if (fd == -1) + return (errno ? errno : EBUSY); + if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, bus) == -1) + *bus = -1; + ret = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun); + close(fd); + if (ret == -1) + return (errno ? errno : EIO); + *host = (idlun.x >> 24) & 255; + *channel = (idlun.x >> 16) & 255; + *id = (idlun.x) & 255; + *lun = (idlun.x >> 8 ) & 255; + return 0; +} + + +static int ddlpa_collect_siblings(struct ddlpa_lock *o) +{ + int idx = 0, ret, have_sg = 0, have_sr = 0, have_scd = 0; + dev_t path_dev; + ino_t path_inode; + struct stat stbuf; + char *path, try_path[DDLPA_MAX_STD_LEN+1]; + int t_bus, t_host, t_channel, t_id, t_lun; + + if (o->ddlpa_flags & DDLPA_OPEN_GIVEN_PATH) + path = o->path; + else + path = o->std_path; + if (path[0] == 0 || o->num_siblings != 0) + return EFAULT; + if (!ddlpa_is_scsi(o, o->std_path)) + return EFAULT; + + if (stat(path, &stbuf) == -1) + return errno; + path_inode = stbuf.st_ino; + path_dev = stbuf.st_dev; + o->rdev = stbuf.st_rdev; + o->dev = stbuf.st_dev; + o->ino = stbuf.st_ino; + ret = ddlpa_obtain_scsi_adr(o, path, + &(o->bus), &(o->host), &(o->channel), + &(o->id), &(o->lun)); + if (ret) { + o->errmsg = strdup( + "Cannot obtain SCSI parameters host,channel,id,lun"); + return ret; + } + o->hcilb_is_valid = 1; + + while (ddlpa_enumerate(o, &idx, try_path) == 0) { + if (!ddlpa_is_scsi(o, try_path)) + continue; + if (stat(try_path, &stbuf) == -1) + continue; + ret = ddlpa_obtain_scsi_adr(o, try_path, + &t_bus, &t_host, &t_channel, &t_id, &t_lun); + if (ret) { + + /* >>> interpret error, memorize busy, no permission */ + + continue; + } + if (t_host != o->host || t_channel != o->channel || + t_id != o->id || t_lun != o->lun) + continue; + + if (o->num_siblings >= DDLPA_MAX_SIBLINGS) { + o->errmsg = + strdup("Too many matching device files found"); + return ERANGE; + } + if (ddlpa_is_sg(o, try_path)) + have_sg = 1; + else if (ddlpa_is_sr(o, try_path)) + have_sr = 1; + else if (ddlpa_is_scd(o, try_path)) + have_scd = 1; + strcpy(o->sibling_paths[o->num_siblings], try_path); + o->sibling_rdevs[o->num_siblings] = stbuf.st_rdev; + o->sibling_devs[o->num_siblings] = stbuf.st_dev; + o->sibling_inodes[o->num_siblings] = stbuf.st_ino; + + if (ddlpa_debug_mode) + fprintf(stderr, + "DDLPA_DEBUG: ddlpa_collect_siblings() found \"%s\"\n", + try_path); + + (o->num_siblings)++; + } + if (have_sg && have_sr && have_scd) + return 0; + if (o->ddlpa_flags & DDLPA_ALLOW_MISSING_SGRCD) + return 0; + + o->errmsg = strdup("Did not find enough siblings"); + + /* >>> add more info about busy and forbidden paths */ + + return EBUSY; +} + + +static int ddlpa_std_by_btl(struct ddlpa_lock *o) +{ + int idx = 0, ret; + char try_path[DDLPA_MAX_STD_LEN+1]; + int t_bus, t_host, t_channel, t_id, t_lun; + + if (!o->inbtl_is_valid) + return EFAULT; + + while (ddlpa_enumerate(o, &idx, try_path) == 0) { + if (!ddlpa_is_sr(o, try_path)) + continue; + ret = ddlpa_obtain_scsi_adr(o, try_path, + &t_bus, &t_host, &t_channel, &t_id, &t_lun); + if (ret) { + + /* >>> interpret error, memorize busy, no permission */ + + continue; + } + if (t_bus != o->in_bus || t_id != o->in_target || + t_lun != o->in_lun) + continue; + strcpy(o->std_path, try_path); + + if (ddlpa_debug_mode) + fprintf(stderr, + "DDLPA_DEBUG: ddlpa_std_by_btl(%d,%d,%d) = \"%s\"\n", + t_bus, t_id, t_lun, o->std_path); + + return 0; + } + + /* >>> add more info about busy and forbidden paths */ + + return ENOENT; +} + + +static int ddlpa_open_all(struct ddlpa_lock *o) +{ + int i, j, ret, no_o_excl; + + if (ddlpa_is_scsi(o, o->std_path)) { + ret = ddlpa_collect_siblings(o); + if (ret) + return ret; + for (i = 0; i < o->num_siblings; i++) { + + /* Watch out for the main personality of the drive. */ + /* No need to occupy identical path or softlink path */ + if (o->sibling_devs[i] == o->dev && + o->sibling_inodes[i] == o->ino) + continue; + /* There may be the same rdev but different inode. */ + no_o_excl = (o->sibling_rdevs[i] == o->rdev); + + /* Look for multiply registered device drivers with + distinct inodes. */ + for (j = 0; j < i; j++) { + if (o->sibling_devs[j] == o->sibling_devs[i] && + o->sibling_inodes[j] == o->sibling_inodes[i]) + break; + if (o->sibling_rdevs[j] == o->sibling_rdevs[i]) + no_o_excl = 1; + } + if (j < i) + continue; /* inode is already occupied */ + + ret = ddlpa_occupy(o, o->sibling_paths[i], + &(o->sibling_fds[i]), no_o_excl); + if (ret) + return ret; + } + } + + if (o->ddlpa_flags & DDLPA_OPEN_GIVEN_PATH) + ret = ddlpa_occupy(o, o->path, &(o->fd), 0); + else + ret = ddlpa_occupy(o, o->std_path, &(o->fd), 0); + if (ret) + return ret; + + /* >>> use fcntl() to adjust O_NONBLOCK */; + + return 0; +} + + +/* ----------------------- public -------------------- */ + + +int ddlpa_destroy(struct ddlpa_lock **lockbundle) +{ + struct ddlpa_lock *o; + int i; + + o= *lockbundle; + if (o == NULL) + return 0; + for (i = 0; i < o->num_siblings; i++) + if (o->sibling_fds[i] != -1) + close(o->sibling_fds[i]); + if(o->fd != -1) + close(o->fd); + if (o->path != NULL) + free(o->path); + if (o->errmsg != NULL) + free(o->errmsg); + free((char *) o); + *lockbundle = NULL; + return 0; +} + + +int ddlpa_lock_path(char *path, int o_flags, int ddlpa_flags, + struct ddlpa_lock **lockbundle, char **errmsg) +{ + struct ddlpa_lock *o; + int ret; + + *errmsg = NULL; + if (ddlpa_new(&o, o_flags, ddlpa_flags)) + return ENOMEM; + *lockbundle = o; + + o->path = strdup(path); + if (o->path == NULL) + return ENOMEM; + o->path_is_valid = 1; + + ret = ddlpa_std_by_rdev(o); + if (ret) { + *errmsg = strdup( + "Cannot find equivalent of given path among standard paths"); + return ret; + } + ret = ddlpa_open_all(o); + if (ret) { + *errmsg = o->errmsg; + o->errmsg = NULL; + ddlpa_destroy(&o); + } + return ret; +} + + +int ddlpa_lock_btl(int bus, int target, int lun, + int o_flags, int ddlpa_flags, + struct ddlpa_lock **lockbundle, char **errmsg) +{ + struct ddlpa_lock *o; + int ret; + + *errmsg = NULL; + ddlpa_flags &= ~DDLPA_OPEN_GIVEN_PATH; + if (ddlpa_new(&o, o_flags, ddlpa_flags)) + return ENOMEM; + *lockbundle = o; + + o->in_bus = bus; + o->in_target = target; + o->in_lun = lun; + o->inbtl_is_valid = 1; + ret = ddlpa_std_by_btl(o); + if (ret) { + *errmsg = strdup( + "Cannot find /dev/sr* with given Bus,Target,Lun"); + return ret; + } + ret = ddlpa_open_all(o); + if (ret) { + *errmsg = o->errmsg; + o->errmsg = NULL; + ddlpa_destroy(&o); + return ret; + } + return 0; +} + + +#ifdef DDLPA_C_STANDALONE + +/* ----------------------------- Test / Demo -------------------------- */ + + +int main(int argc, char **argv) +{ + struct ddlpa_lock *lck = NULL; + char *errmsg = NULL, *opened_path = NULL, *my_path = NULL; + int i, ret, fd = -1, duration = -1, bus = -1, target = -1, lun = -1; + + if (argc < 3) { +usage:; + fprintf(stderr, "usage: %s device_path duration\n", argv[0]); + exit(1); + } + my_path = argv[1]; + sscanf(argv[2], "%d", &duration); + if (duration < 0) + goto usage; + + + /* For our purpose, only O_RDWR is a suitable access mode. + But in order to allow experiments, o_flags are freely adjustable. + + Warning: Do _not_ set an own O_EXCL flag with the following calls ! + + (This freedom to fail may get removed in a final version.) + */ + if (my_path[0] != '/' && my_path[0] != '.' && + strchr(my_path, ',') != NULL) { + /* + cdrecord style dev=Bus,Target,Lun + */ + + sscanf(my_path, "%d,%d,%d", &bus, &target, &lun); + ret = ddlpa_lock_btl(bus, target, lun, O_RDWR | O_LARGEFILE, + 0, &lck, &errmsg); + } else { + /* + This substitutes for: + fd = open(my_path, O_RDWR | O_EXCL | O_LARGEFILE); + + */ + + ret = ddlpa_lock_path(my_path, O_RDWR | O_LARGEFILE, + 0, &lck, &errmsg); + } + if (ret) { + fprintf(stderr, "Cannot exclusively open '%s'\n", my_path); + if (errmsg != NULL) + fprintf(stderr, "Reason given : %s\n", + errmsg); + free(errmsg); + fprintf(stderr, "Error condition : %d '%s'\n", + ret, strerror(ret)); + exit(2); + } + fd = lck->fd; + + printf("---------------------------------------------- Lock gained\n"); + + + /* Use fd for the usual operations on the device depicted by my_path. + */ + + + /* This prints an overview of the impact of the lock */ + if (lck->ddlpa_flags & DDLPA_OPEN_GIVEN_PATH) + opened_path = lck->path; + else + opened_path = lck->std_path; + printf("ddlpa: opened %s", opened_path); + + if (strcmp(opened_path, lck->std_path) != 0) + printf(" (an alias of '%s')", lck->std_path); + printf("\n"); + if (lck->num_siblings > 0) { + printf("ddlpa: opened siblings:"); + for (i = 0; i < lck->num_siblings; i++) + if (lck->sibling_fds[i] != -1) + printf(" %s", lck->sibling_paths[i]); + printf("\n"); + } + + + /* This example waits a while. So other lock candidates can collide. */ + for (i = 0; i < duration; i++) { + sleep(1); + fprintf(stderr, "\rslept %d seconds of %d", i + 1, duration); + } + fprintf(stderr, "\n"); + + + /* When finally done with the drive, this substitutes for: + close(fd); + */ + if (ddlpa_destroy(&lck)) { + /* Well, man 2 close says it can fail. */ + exit(3); + } + exit(0); +} + + +#endif /* DDLPA_C_STANDALONE */ + diff --git a/libburn/branches/ZeroFourTwo/libburn/ddlpa.h b/libburn/branches/ZeroFourTwo/libburn/ddlpa.h new file mode 100644 index 00000000..6b057c9f --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/ddlpa.h @@ -0,0 +1,107 @@ + +/* ddlpa + Implementation of Delicate Device Locking Protocol level A. + Copyright (C) 2007 Thomas Schmitt <scdbackup@gmx.net> + Provided under any of the following licenses: GPL, LGPL, BSD. Choose one. + + See ../doc/ddlp.txt for a description of the protocol. +*/ + +#ifndef DDLPA_H_INCLUDED +#define DDLPA_H_INCLUDED 1 + + +/* An upper limit for the length of standard paths and sibling paths */ +#define DDLPA_MAX_STD_LEN 15 + +/* An upper limit for the number of siblings */ +#define DDLPA_MAX_SIBLINGS 5 + +struct ddlpa_lock { + + /* Recorded input parameters of locking call */ + char *path; + int path_is_valid; + int in_bus, in_target, in_lun; + int inbtl_is_valid; + int ddlpa_flags; + int o_flags; + + /* Result of locking call */ + char std_path[DDLPA_MAX_STD_LEN + 1]; + int fd; + dev_t rdev; + dev_t dev; + ino_t ino; + int host, channel, id, lun, bus; + int hcilb_is_valid; + int num_siblings; + char sibling_paths[DDLPA_MAX_SIBLINGS][DDLPA_MAX_STD_LEN + 1]; + int sibling_fds[DDLPA_MAX_SIBLINGS]; + dev_t sibling_rdevs[DDLPA_MAX_SIBLINGS]; + dev_t sibling_devs[DDLPA_MAX_SIBLINGS]; + ino_t sibling_inodes[DDLPA_MAX_SIBLINGS]; + + /* Is NULL if all goes well. Else it may contain a text message. */ + char *errmsg; +}; + + + +/** Lock a recorder by naming a device file path. Allocate a new container. + @param path Gives the file system path of the recorder + as known to the calling program. + @param o_flags flags for open(2). Do not use O_EXCL here because this + is done automatically whenever appropriate. + Advised is O_RDWR | O_LARGEFILE, eventually | O_NDELAY. + @param ddlpa_flags 0 = default behavior: the standard path will be opened + and treated by fcntl(F_SETLK) + DDLPA_OPEN_GIVEN_PATH causes the input parameter "path" + to be used with open(2) and fcntl(2). + DDLPA_ALLOW_MISSING_SGRCD allows to grant a lock + although not all three, a sg, a sr and a scd device + file have been found during sibling search. Normally + this is counted as failure due to EBUSY. + @param lockbundle gets allocated and then represents the locking state + @param errmsg if *errmsg is not NULL after the call, it contains an + error message. Then to be released by free(3). + It is NULL in case of success or lack of memory. + @return 0=success , else an errno compatible error number +*/ +int ddlpa_lock_path(char *path, int o_flags, int ddlpa_flags, + struct ddlpa_lock **lockbundle, char **errmsg); + + +/** Lock a recorder by naming a Bus,Target,Lun number triple. + Allocate a new container. + @param bus parameter to match ioctl(SCSI_IOCTL_GET_BUS_NUMBER) + @param target parameter to match ioctl(SCSI_IOCTL_GET_IDLUN) &0xff + @param lun parameter to match ioctl(SCSI_IOCTL_GET_IDLUN) &0xff00 + @param o_flags see ddlpa_lock_path(). + @param ddlpa_flags see ddlpa_lock_path(). Flag DDLPA_OPEN_GIVEN_PATH + will be ignored. + @param lockbundle see ddlpa_lock_path(). + @param errmsg see ddlpa_lock_path(). + @return 0=success , else an errno compatible error number +*/ +int ddlpa_lock_btl(int bus, int target, int lun, + int o_flags, int ddlpa_flags, + struct ddlpa_lock **lockbundle, char **errmsg); + + +/** Release the lock by closing all filedescriptors and freeing memory. + @param lockbundle the lock which is to be released. + *lockbundle will be set to NULL by this call. + @return 0=success , 1=failure +*/ +int ddlpa_destroy(struct ddlpa_lock **lockbundle); + + + +/** Definitions of macros used in above functions */ + +#define DDLPA_OPEN_GIVEN_PATH 1 +#define DDLPA_ALLOW_MISSING_SGRCD 2 + + +#endif /* DDLPA_H_INCLUDED */ diff --git a/libburn/branches/ZeroFourTwo/libburn/debug.c b/libburn/branches/ZeroFourTwo/libburn/debug.c new file mode 100644 index 00000000..b4abab77 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/debug.h b/libburn/branches/ZeroFourTwo/libburn/debug.h new file mode 100644 index 00000000..b566de0e --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/drive.c b/libburn/branches/ZeroFourTwo/libburn/drive.c new file mode 100644 index 00000000..ab46eb71 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/drive.c @@ -0,0 +1,2446 @@ +/* -*- 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 <errno.h> +#include "libburn.h" +#include "init.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" + +/* ts A70219 : for burn_disc_get_write_mode_demands() */ +#include "options.h" + +/* A70225 : to learn about eventual Libburn_dvd_r_dl_multi_no_close_sessioN */ +#include "write.h" + +/* A70903 : for burn_scsi_setup_drive() */ +#include "spc.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->idata = NULL; + d->mdata = NULL; + d->toc_entry = NULL; + d->released = 1; + d->stdio_fd = -1; + d->status = BURN_DISC_UNREADY; + return 1; +} + + +/* ts A70903 */ +void burn_drive_free_subs(struct burn_drive *d) +{ + if (d->idata != NULL) + free((void *) d->idata); + d->idata = NULL; + if (d->mdata != NULL) { + burn_mdata_free_subs(d->mdata); + free((void *) d->mdata); + } + d->mdata = NULL; + if(d->toc_entry != NULL) + free((void *) d->toc_entry); + d->toc_entry = NULL; + if (d->devname != NULL) + free(d->devname); + d->devname = NULL; + if (d->stdio_fd >= 0) + close (d->stdio_fd); + d->stdio_fd = -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 (d->drive_role == 1) + if (burn_drive_is_open(d)) { + d->unlock(d); + d->release(d); + } + burn_drive_free_subs(d); + 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) +{ + if (d->drive_role != 1) + return (d->stdio_fd >= 0); + /* 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 + 2 = drive is grabbed, synchronous read/write interrupted + 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_SYNC || + d->busy == BURN_DRIVE_WRITING_SYNC) + return 2; + if(d->busy == BURN_DRIVE_WRITING) { + + /* ts A70928 */ + /* >>> how do i learn whether the writer thread is still + alive ? */; + /* >>> what to do if writer is dead ? + At least sync disc ?*/; + return 50; + } + if(d->busy == BURN_DRIVE_READING) { + 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 */ + + /* ts A71128 : run read_disc_info() for any recognizeable profile */ + if (d->current_profile > 0 || + 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); + + /* ts A70314 */ + d->status = BURN_DISC_UNSUITABLE; + } + 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; + } + if(d->drive_role != 1) { + d->released = 0; + if(d->drive_role == 2 || d->drive_role == 3) { + d->status = BURN_DISC_BLANK; + d->current_profile = 0xffff; + } else { + d->status = BURN_DISC_EMPTY; + d->current_profile = 0; + } + d->busy = BURN_DRIVE_IDLE; + return 1; + } + + 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; +} + + +/* ts A71015 */ +#define Libburn_ticket_62_re_register_is_possiblE 1 + +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->thread_pid = 0; + d->thread_pid_valid = 0; + 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 */ + /* ts A71015 : xorriso dialog imposes a use case now */ + + /* 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(struct burn_drive *d, char * text); + + d->drive_role = 1; /* MMC drive */ + + t = burn_drive_register(d); + + /* ts A60821 */ + mmc_function_spy(NULL, "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(NULL, "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->current_has_feat21h = 0; + d->current_feat2fh_byte4 = -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; +} + + +/* ts A70918 : outsourced from burn_drive_release() and enhanced */ +/** @param flag bit0-2 = mode : 0=unlock , 1=unlock+eject , 2=leave locked +*/ +int burn_drive_release_fl(struct burn_drive *d, int flag) +{ + 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 0; + } + + /* 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 0; + } + + if (d->drive_role == 1) { + if (d->needs_sync_cache) + d->sync_cache(d); + if ((flag & 7) != 2) + d->unlock(d); + if ((flag & 7) == 1) + d->eject(d); + d->release(d); + } + + d->needs_sync_cache = 0; /* just to be sure */ + d->released = 1; + + /* ts A61125 : outsourced model aspects */ + burn_drive_mark_unready(d); + return 1; +} + + +/* API */ +void burn_drive_release(struct burn_drive *d, int le) +{ + burn_drive_release_fl(d, !!le); +} + + +/* ts A70918 */ +/* API */ +int burn_drive_leave_locked(struct burn_drive *d, int flag) +{ + return burn_drive_release_fl(d, 2); +} + + +/* ts A61007 : former void burn_wait_all() */ +/* @param flag bit0= demand freed drives (else released drives) */ +int burn_drives_are_clear(int flag) +{ + 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 && !(flag & 1)) + 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); + if (d->drive_role == 1) + 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; + if (d->drive_role != 1) + return 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) +{ + /* ts A70928 : inform control thread of signal in sub-threads */ + if (burn_global_abort_level > 0) + burn_global_abort_level++; + if (burn_global_abort_level > 5) { + if (burn_global_signal_handler == NULL) + kill(getpid(), burn_global_abort_signum); + else + (*burn_global_signal_handler) + (burn_global_signal_handle, + burn_global_abort_signum, 0); + burn_global_abort_level = -1; + } + + if (p != NULL) { + 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; +} + + +/* ts A70907 : added parameter flag */ +/* @param flag bit0= reset global drive list */ +int burn_drive_scan_sync(struct burn_drive_info *drives[], + unsigned int *n_drives, int flag) +{ + /* ts A70907 : + There seems to have been a misunderstanding about the role of + burn_drive_scan_sync(). It needs no static state because it + is only started once during an asynchronous scan operation. + Its starter, burn_drive_scan(), is the one which ends immediately + and gets called repeatedly. It acts on start of scanning by + calling burn_drive_scan_sync(), returns idle while scanning is + not done and finally removes the worker object which represented + burn_drive_scan_sync(). + The scanning itself is not parallel but enumerates sequentially + drive by drive (within scsi_enumerate_drives()). + + I will use "scanned" for marking drives found by previous runs. + It will not be static any more. + */ + /* ts A71015 : this makes only trouble : static int scanning = 0; */ + /* ts A70907 : + These variables are too small anyway. We got up to 255 drives. + static int scanned = 0, found = 0; + Variable "found" was only set but never read. + */ + unsigned char scanned[32]; + unsigned count = 0; + int i; + + /* ts A61007 : moved up to burn_drive_scan() */ + /* a ssert(burn_running); */ + + + /* 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 */ + + *n_drives = 0; + + /* ts A70907 : wether to scan from scratch or to extend */ + for (i = 0; i < sizeof(scanned); i++) + scanned[i] = 0; + if (flag & 1) { + burn_drive_free_all(); + } else { + for (i = 0; i <= drivetop; i++) + if (drive_array[i].global_index >= 0) + scanned[i / 8] |= (1 << (i % 8)); + } + + /* refresh the lib's drives */ + + /* ts A61115 : formerly sg_enumerate(); ata_enumerate(); */ + scsi_enumerate_drives(); + + count = burn_drive_count(); + if (count) { + /* ts A70907 : + Extra array element marks end of array. */ + *drives = calloc(count + 1, + sizeof(struct burn_drive_info)); + if (*drives == NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00000003, + LIBDAX_MSGS_SEV_FATAL, + LIBDAX_MSGS_PRIO_HIGH, + "Out of virtual memory", 0, 0); + return -1; + } else + for (i = 0; i <= count; i++) /* invalidate */ + (*drives)[i].drive = NULL; + } else + *drives = NULL; + + for (i = 0; i < count; ++i) { + if (scanned[i / 8] & (1 << (i % 8))) + continue; /* device already scanned by previous run */ + if (drive_array[i].global_index < 0) + continue; /* invalid device */ + + while (!drive_getcaps(&drive_array[i], + &(*drives)[*n_drives])) { + sleep(1); + } + (*n_drives)++; + scanned[i / 8] |= 1 << (i % 8); + } + + return(1); +} + +/* 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); +} + + +void burn_drive_info_free(struct burn_drive_info drive_infos[]) +{ + int i; + +/* ts A60904 : ticket 62, contribution by elmom */ +/* clarifying the meaning and the identity of the victim */ + + if(drive_infos == NULL) + return; + +#ifndef Libburn_free_all_drives_on_infO + +#ifdef Not_yeT + int new_drivetop; + + /* ts A71015: compute reduced drivetop counter */ + new_drivetop = drivetop; + for (i = 0; drive_infos[i].drive != NULL; i++) + if (drive_infos[i].global_index == new_drivetop + && new_drivetop >= 0) { + new_drivetop--; + i = 0; + } +#endif /* Not_yeT */ + + /* ts A70907 : Solution for wrong behavior below */ + for (i = 0; drive_infos[i].drive != NULL; i++) + burn_drive_free(drive_infos[i].drive); + +#ifdef Not_yeT + drivetop = new_drivetop; +#endif /* Not_yeT */ + +#endif /* ! Libburn_free_all_drives_on_infO */ + + /* ts A60904 : This looks a bit weird. [ts A70907 : not any more] + 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. */ + free((void *) drive_infos); + +#ifdef Libburn_free_all_drives_on_infO + /* ts A70903 : THIS IS WRONG ! (disabled now) + It endangers multi drive usage. + This call is not entitled to delete all drives, only the + ones of the array which it recieves a parmeter. + + Problem: It was unclear how many items are listed in drive_infos + Solution: Added a end marker element to any burn_drive_info array + The mark can be recognized by having drive == NULL + */ + burn_drive_free_all(); +#endif +} + + +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->nominal_write_speed = w; + if(d->drive_role != 1) + return; + d->set_speed(d, r, w); +} + + +/* ts A70711 API function */ +int burn_drive_set_buffer_waiting(struct burn_drive *d, int enable, + int min_usec, int max_usec, int timeout_sec, + int min_percent, int max_percent) +{ + + if (enable >= 0) + d->wait_for_buffer_free = !!enable; + if (min_usec >= 0) + d->wfb_min_usec = min_usec; + if (max_usec >= 0) + d->wfb_max_usec = max_usec; + if (timeout_sec >= 0) + d->wfb_timeout_sec = timeout_sec; + if (min_percent >= 0) { + if (min_percent < 25 || min_percent > 100) + return 0; + d->wfb_min_percent = min_percent; + } + if (max_percent >= 0) { + if (max_percent < 25 || max_percent > 100) + return 0; + d->wfb_max_percent = max_percent; + } + return 1; +} + + +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) +{ + if(!d->mdata->valid) + return 0; + return d->mdata->max_read_speed; +} + +int burn_drive_get_write_speed(struct burn_drive *d) +{ + if(!d->mdata->valid) + return 0; + return d->mdata->max_write_speed; +} + +/* ts A61021 : New API function */ +int burn_drive_get_min_write_speed(struct burn_drive *d) +{ + if(!d->mdata->valid) + return 0; + 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 A70924 */ +int burn_drive__fd_from_special_adr(char *adr) +{ + int fd = -1, i; + + if (strcmp(adr, "-") == 0) + fd = 1; + if(strncmp(adr, "/dev/fd/", 8) == 0) { + for (i = 8; adr[i]; i++) + if (!isdigit(adr[i])) + break; + if (i> 8 && adr[i] == 0) + fd = atoi(adr + 8); + } + return fd; +} + + +/* ts A70903 : Implements adquiration of pseudo drives */ +int burn_drive_grab_dummy(struct burn_drive_info *drive_infos[], char *fname) +{ + int ret = -1, fd = -1, role = 0; + /* divided by 512 it needs to fit into a signed long integer */ + off_t size = ((off_t) (512 * 1024 * 1024 - 1) * (off_t) 2048); + struct burn_drive *d= NULL, *regd_d; + struct stat stbuf; + + static int allow_role_3 = 1; + + if (fname[0] != 0) { + memset(&stbuf, 0, sizeof(stbuf)); + fd = burn_drive__fd_from_special_adr(fname); + if (fd >= 0) + ret = fstat(fd, &stbuf); + else + ret = stat(fname, &stbuf); + if (ret == -1 || S_ISBLK(stbuf.st_mode) || + S_ISREG(stbuf.st_mode)) { + ret = burn_os_stdio_capacity(fname, &size); + if (ret == -1) { + libdax_msgs_submit(libdax_messenger, -1, + 0x00020009, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Neither stdio-path nor its directory exist", + 0, 0); + return 0; + } else if (ret == -2) { + libdax_msgs_submit(libdax_messenger, -1, + 0x00020005, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Failed to open device (a pseudo-drive)", + errno, 0); + return 0; + } + if (fname[0] != 0) + role = 2; + else + role = 0; + } else { + if(S_ISDIR(stbuf.st_mode) || !allow_role_3) { + libdax_msgs_submit(libdax_messenger, -1, + 0x00020149, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Unsuitable filetype for pseudo-drive", 0, 0); + return 0; + } + role = 3; + } + } + d= (struct burn_drive *) calloc(1, sizeof(struct burn_drive)); + if (d == NULL) + return 0; + burn_setup_drive(d, fname); + d->status = BURN_DISC_EMPTY; + + d->drive_role = role; + ret = burn_scsi_setup_drive(d, -1, -1, -1, -1, -1, 0); + if (ret <= 0) + goto ex; + regd_d = burn_drive_register(d); + if (regd_d == NULL) { + ret = -1; + goto ex; + } + free((char *) d); /* all sub pointers have been copied to *regd_d */ + d = regd_d; + if (d->drive_role == 2 || d->drive_role == 3) { + d->status = BURN_DISC_BLANK; + d->current_profile = 0xffff; /* MMC for non-compliant drive */ + strcpy(d->current_profile_text,"stdio file"); + d->current_is_cd_profile = 0; + d->current_is_supported_profile = 1; + d->block_types[BURN_WRITE_TAO] = BURN_BLOCK_MODE1; + d->block_types[BURN_WRITE_SAO] = BURN_BLOCK_SAO; + d->media_capacity_remaining = size; + + /* >>> ? open file for a test ? (>>> beware of "-" = stdin) */; + + } else + d->current_profile = 0; /* Drives return this if empty */ + + *drive_infos = calloc(2, sizeof(struct burn_drive_info)); + if (*drive_infos == NULL) + goto ex; + (*drive_infos)[0].drive = d; + (*drive_infos)[1].drive = NULL; /* End-Of-List mark */ + (*drive_infos)[0].tao_block_types = d->block_types[BURN_WRITE_TAO]; + (*drive_infos)[0].sao_block_types = d->block_types[BURN_WRITE_SAO]; + if (d->drive_role == 2) { + strcpy((*drive_infos)[0].vendor,"YOYODYNE"); + strcpy((*drive_infos)[0].product,"WARP DRIVE"); + strcpy((*drive_infos)[0].revision,"FX01"); + } else if (d->drive_role == 3) { + strcpy((*drive_infos)[0].vendor,"YOYODYNE"); + strcpy((*drive_infos)[0].product,"BLACKHOLE"); + strcpy((*drive_infos)[0].revision,"FX02"); + } else { + strcpy((*drive_infos)[0].vendor,"FERENGI"); + strcpy((*drive_infos)[0].product,"VAPORWARE"); + strcpy((*drive_infos)[0].revision,"0000"); + } + d->released = 0; + ret = 1; +ex:; + if (ret <= 0 && d != NULL) { + burn_drive_free_subs(d); + free((char *) d); + } + return ret; +} + + +/* 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, i; + + /* check wether drive adress is already registered */ + for (i = 0; i <= drivetop; i++) + if (drive_array[i].global_index >= 0) + if (strcmp(drive_array[i].devname, adr) == 0) + break; + if (i <= drivetop) { + libdax_msgs_submit(libdax_messenger, i, + 0x0002014b, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Drive is already registered resp. scanned", + 0, 0); + return -1; + } + + if (strncmp(adr, "stdio:", 6) == 0) { + ret = burn_drive_grab_dummy(drive_infos, adr + 6); + return ret; + } + + burn_drive_clear_whitelist(); + burn_drive_add_whitelist(adr); +/* + fprintf(stderr,"libburn: experimental: burn_drive_scan_and_grab(%s)\n", + adr); +*/ + + /* ts A70907 : now calling synchronously rather than looping */ + ret = burn_drive_scan_sync(drive_infos, &n_drives, 0); + if (ret < 0) + return -1; + + if (n_drives <= 0) + return 0; +/* + fprintf(stderr, "libburn: experimental: n_drives %d , drivetop %d\n", + n_drives, drivetop); + if (n_drives > 0) + fprintf(stderr, "libburn: experimental: global_index %d\n", + drive_infos[0]->drive->global_index); +*/ + + 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 */ /* ts A70906 : promoted to API */ +/** Inquire the persistent address of the given drive. */ +int burn_drive_d_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 */ /* A70906 : Now legacy API call */ +/** 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_d_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 */ +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_d_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 (strncmp(path, "stdio:", 6) == 0 || + 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; + + /* >>> ts A71002 : are there any threads at work ? + If not, then one can force abort because the drives will not + change status on their own. + */ + + 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->drive_role == 0) + return 0; + 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->drive_role == 0) + return 0; + 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->drive_role != 1) + return 0; + 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; + } + *lba = *nwa = 0; + if (d->drive_role != 1) + return 0; + if (o != NULL) + d->send_write_parameters(d, o); + ret = d->get_nwa(d, trackno, lba, nwa); + return ret; +} + + +/* ts A70131 : new API function */ +int burn_disc_get_msc1(struct burn_drive *d, int *start) +{ + int ret, trackno; + + 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; + } + *start = 0; + if (d->drive_role != 1) + return 0; + ret = d->read_multi_session_c1(d, &trackno, start); + return ret; +} + + +/* ts A70213 : new API function */ +off_t burn_disc_available_space(struct burn_drive *d, + struct burn_write_opts *o) +{ + int lba, nwa; + + if (burn_drive_is_released(d)) + return 0; + if (d->busy != BURN_DRIVE_IDLE) + return 0; + if (d->drive_role == 0) + return 0; + if (d->drive_role != 1) { + if (d->media_capacity_remaining <= 0) + d->media_capacity_remaining = + ((off_t) (512 * 1024 * 1024 - 1) * (off_t) 2048); + } else { + if (o != NULL) + d->send_write_parameters(d, o); + d->get_nwa(d, -1, &lba, &nwa); + } + if (o != NULL) { + if (o->start_byte > 0) { + if (o->start_byte > d->media_capacity_remaining) + return 0; + return d->media_capacity_remaining - o->start_byte; + } + } + return d->media_capacity_remaining; +} + + +/* 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) +{ + if(!m->valid) + return 0; + 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; + if(!d->mdata->valid) + return 0; + 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 A70713 : API function */ +int burn_drive_get_best_speed(struct burn_drive *d, int speed_goal, + struct burn_speed_descriptor **best_descr, int flag) +{ + struct burn_speed_descriptor *sd; + int best_speed = 0, best_lba = 0, source= 2, speed; + + if (flag & 2) + source = -1; + if (speed_goal < 0) + best_speed = 2000000000; + *best_descr = NULL; + if(!d->mdata->valid) + return 0; + for (sd = d->mdata->speed_descriptors; sd != NULL; sd = sd->next) { + if (flag & 1) + speed = sd->read_speed; + else + speed = sd->write_speed; + if ((source >= 0 && sd->source != source) || + speed <= 0) + continue; + if (speed_goal < 0) { + if (speed < best_speed) { + best_speed = speed; + *best_descr = sd; + } + } else if (speed_goal == 0) { + if ((source == 2 && sd->end_lba > best_lba) || + ((source !=2 || sd->end_lba == best_lba) && + speed > best_speed)) { + best_lba = sd->end_lba; + best_speed = speed; + *best_descr = sd; + } + } else if (speed <= speed_goal) { + if (speed > best_speed) { + best_speed = speed; + *best_descr = sd; + } + } + } + if (d->current_is_cd_profile && *best_descr == NULL && ! (flag & 2)) + /* Mode page 2Ah is deprecated in MMC-5 although all known + burners still support it with CD media. */ + return burn_drive_get_best_speed(d, speed_goal, best_descr, + flag | 2); + return (*best_descr != NULL); +} + + +/* ts A61226 : API function */ +int burn_drive_free_speedlist(struct burn_speed_descriptor **speed_list) +{ + return burn_speed_descriptor_destroy(speed_list, 1); +} + + +/* ts A70203 : API function */ +int burn_disc_get_multi_caps(struct burn_drive *d, enum burn_write_types wt, + struct burn_multi_caps **caps, int flag) +{ + enum burn_disc_status s; + struct burn_multi_caps *o; + int status, num_formats, ret, type, i; + off_t size; + unsigned dummy; + + *caps = NULL; + s = burn_disc_get_status(d); + if(s == BURN_DISC_UNGRABBED) + return -1; + *caps = o = (struct burn_multi_caps *) + malloc(sizeof(struct burn_multi_caps)); + if(*caps == NULL) + return -1; + /* Default says nothing is available */ + o->multi_session = o->multi_track = 0; + o-> start_adr = 0; + o->start_alignment = o->start_range_low = o->start_range_high = 0; + o->might_do_tao = o->might_do_sao = o->might_do_raw = 0; + o->advised_write_mode = BURN_WRITE_NONE; + o->selected_write_mode = wt; + o->current_profile = d->current_profile; + o->current_is_cd_profile = d->current_is_cd_profile; + o->might_simulate = 0; + + if (d->drive_role == 0) + return 0; + if (d->drive_role == 2) { + /* stdio file drive : random access read-write */ + o->start_adr = 1; + size = d->media_capacity_remaining; + burn_os_stdio_capacity(d->devname, &size); + d->media_capacity_remaining = size; + o->start_range_high = size; + o->start_alignment = 2048; /* imposting a drive, not a file */ + o->might_do_sao = 4; + o->might_do_tao = 2; + o->advised_write_mode = BURN_WRITE_TAO; + o->might_simulate = 1; + } else if (d->drive_role != 1) { + /* stdio file drive : sequential access write-only */ + o->might_do_sao = 4; + o->might_do_tao = 2; + o->advised_write_mode = BURN_WRITE_TAO; + o->might_simulate = 1; + } else if (s != BURN_DISC_BLANK && s != BURN_DISC_APPENDABLE) { + return 0; + } else if (s == BURN_DISC_APPENDABLE && + (wt == BURN_WRITE_SAO || wt == BURN_WRITE_RAW)) { + return 0; + } else if (wt == BURN_WRITE_RAW && !d->current_is_cd_profile) { + return 0; + } else if (d->current_profile == 0x09 || d->current_profile == 0x0a) { + /* CD-R , CD-RW */ + if (d->block_types[BURN_WRITE_TAO]) { + o->multi_session = o->multi_track = 1; + o->might_do_tao = 2; + if (o->advised_write_mode == BURN_WRITE_NONE) + o->advised_write_mode = BURN_WRITE_TAO; + } + if (d->block_types[BURN_WRITE_SAO]) { + o->multi_session = o->multi_track = 1; + o->might_do_sao = 1; + if (o->advised_write_mode == BURN_WRITE_NONE) + o->advised_write_mode = BURN_WRITE_SAO; + } + if (d->block_types[BURN_WRITE_RAW]) { + o->might_do_raw = 1; + if (o->advised_write_mode == BURN_WRITE_NONE) + o->advised_write_mode = BURN_WRITE_RAW; + } + if (wt == BURN_WRITE_RAW) + o->multi_session = o->multi_track = 0; + else if(wt == BURN_WRITE_NONE || wt == BURN_WRITE_SAO || + wt == BURN_WRITE_TAO) + o->might_simulate = !!d->mdata->simulate; + } else if (d->current_profile == 0x11 || d->current_profile == 0x14 || + d->current_profile == 0x15) { + /* DVD-R , sequential DVD-RW , DVD-R/DL Sequential */ + if (s == BURN_DISC_BLANK) { + o->might_do_sao = 1; + o->advised_write_mode = BURN_WRITE_SAO; + } + if (d->current_has_feat21h) { +#ifndef Libburn_dvd_r_dl_multi_no_close_sessioN + if (d->current_profile != 0x15) +#endif + o->multi_session = 1; + o->multi_track = 1; + o->might_do_tao = 2; + o->advised_write_mode = BURN_WRITE_TAO; + } + if (wt == BURN_WRITE_SAO) + o->multi_session = o->multi_track = 0; + if (wt == BURN_WRITE_NONE || wt == BURN_WRITE_SAO || + wt == BURN_WRITE_TAO) + o->might_simulate = 1; + } else if (d->current_profile == 0x12 || d->current_profile == 0x13 || + d->current_profile == 0x1a) { + /* DVD-RAM, overwriteable DVD-RW, DVD+RW */ + o->start_adr = 1; + ret = burn_disc_get_formats(d, &status, &size, &dummy, + &num_formats); + if (ret == 1) { + if (status == BURN_FORMAT_IS_FORMATTED) + o->start_range_high = size; + if (d->current_profile == 0x13) { + o->start_alignment = 32 * 1024; + for (i = 0; i < num_formats; i++) { + ret = burn_disc_get_format_descr(d, i, + &type, &size, &dummy); + if (ret <= 0) + continue; + if (type == 0x13) /* expandable */ + break; + } + if (i >= num_formats) /* not expandable */ + o->start_range_high -= 32 * 1024; + } else { + o->start_alignment = 2 * 1024; + if (d->best_format_size - 2048 > + o->start_range_high) + o->start_range_high = + d->best_format_size - 2048; + } + } + o->might_do_sao = 4; + o->might_do_tao = 2; + o->advised_write_mode = BURN_WRITE_TAO; + } else if (d->current_profile == 0x1b || d->current_profile == 0x2b) { + /* DVD+R , DVD+R/DL */ + o->multi_session = o->multi_track = 1; + o->might_do_tao = 2; + o->might_do_sao = 1; + o->advised_write_mode = BURN_WRITE_TAO; + } else /* unknown media */ + return 0; + + if (s == BURN_DISC_APPENDABLE) + o->might_do_sao = o->might_do_raw = 0; + + if (wt == BURN_WRITE_TAO && !o->might_do_tao) + return 0; + else if (wt == BURN_WRITE_SAO && !o->might_do_sao) + return 0; + else if (wt == BURN_WRITE_RAW && !o->might_do_raw) + return 0; + return 1; +} + + +/* ts A70203 : API function */ +int burn_disc_free_multi_caps(struct burn_multi_caps **caps) +{ + if (*caps == NULL) + return 0; + free((char *) *caps); + *caps = NULL; + return 1; +} + + +/* ts A70207 : evaluate write mode related peculiarities of a disc + @param flag bit0= fill_up_media is active +*/ +int burn_disc_get_write_mode_demands(struct burn_disc *disc, + struct burn_write_opts *opts, + struct burn_disc_mode_demands *result, int flag) +{ + struct burn_session *session; + struct burn_track *track; + int i, j, mode, unknown_track_sizes = 0, last_track_is_unknown = 0; + enum burn_disc_status s; + + + memset((char *) result, 0, sizeof(struct burn_disc_mode_demands)); + if (disc == NULL) + return 2; + s = burn_disc_get_status(opts->drive); + if (s == BURN_DISC_APPENDABLE || disc->sessions > 1) + result->will_append = 1; + if (disc->sessions > 1) + result->multi_session = 1; + for (i = 0; i < disc->sessions; i++) { + session = disc->session[i]; + if (session->tracks <= 0) + continue; + mode = session->track[0]->mode; + if (session->tracks > 1) + result->multi_track = 1; + for (j = 0; j < session->tracks; j++) { + track = session->track[j]; + if (burn_track_is_open_ended(track)) { + if (burn_track_get_default_size(track) > 0) { + if (result->unknown_track_size == 0) + result->unknown_track_size = 2; + } else + result->unknown_track_size = 1; + unknown_track_sizes++; + last_track_is_unknown = 1; + } else + last_track_is_unknown = 0; + if (mode != track->mode) + result->mixed_mode = 1; + if (track->mode == BURN_MODE1) { + result->block_types |= BURN_BLOCK_MODE1; + } else if (track->mode == BURN_AUDIO) { + result->audio = 1; + result->block_types |= BURN_BLOCK_RAW0; + result->exotic_track = 1; + } else { + result->block_types |= opts->block_type; + result->exotic_track = 1; + } + } + } + if (flag&1) {/* fill_up_media will define the size of the last track */ + if (unknown_track_sizes == 1 && last_track_is_unknown) + result->unknown_track_size = 0; + } + return (disc->sessions > 0); +} + + +/* ts A70903 : API */ +int burn_drive_get_drive_role(struct burn_drive *d) +{ + return d->drive_role; +} + + +/* ts A70923 + Hands out pointers *dpt to directory path and *npt to basename. + Caution: the last '/' in adr gets replaced by a 0. +*/ +static int burn__split_path(char *adr, char **dpt, char **npt) +{ + *dpt = adr; + *npt = strrchr(*dpt, '/'); + if (*npt == NULL) { + *npt = *dpt; + *dpt = "."; + return 1; + } + **npt = 0; + if(*npt == *dpt) + *dpt = "/"; + (*npt)++; + return 2; +} + + +/* ts A70923 : API */ +int burn_drive_equals_adr(struct burn_drive *d1, char *adr2_in, int role2) +{ + struct stat stbuf1, stbuf2; + char adr1[BURN_DRIVE_ADR_LEN], *adr2 = adr2_in; + char conv_adr1[BURN_DRIVE_ADR_LEN], conv_adr2[BURN_DRIVE_ADR_LEN]; + char *npt1, *dpt1, *npt2, *dpt2; + int role1, stat_ret1, stat_ret2, conv_ret2; + + role1 = burn_drive_get_drive_role(d1); + burn_drive_d_get_adr(d1, adr1); + stat_ret1 = stat(adr1, &stbuf1); + + if (strncmp(adr2, "stdio:", 6) == 0) { + adr2+= 6; + role2 = (!!adr2[0]) * 2; + } + if (strlen(adr2) >= BURN_DRIVE_ADR_LEN) + return -1; + stat_ret2 = stat(adr2, &stbuf2); + conv_ret2 = burn_drive_convert_fs_adr(adr2, conv_adr2); + + /* roles 2 and 3 have the same name space and object interpretation */ + if (role1 == 3) + role1 = 2; + if (role2 == 3) + role2 = 2; + + if (strcmp(adr1, adr2) == 0 && role1 == role2) + return(1); /* equal role and address */ + if (role1 == 1 && role2 == 1) { + /* MMC drive meets wannabe MMC drive */ + if (conv_ret2 <= 0) + return 0; /* no MMC drive at adr2 */ + if (strcmp(adr1, conv_adr2) == 0) + return 1; /* equal real MMC drives */ + return 0; + + } else if (role1 == 0 || role2 == 0) + return 0; /* one null-drive, one not */ + + else if (role1 != 1 && role2 != 1) { + /* pseudo-drive meets file object */ + + if (stat_ret1 == -1 || stat_ret2 == -1) { + if (stat_ret1 != -1 || stat_ret2 != -1) + return 0; /* one adress existing, one not */ + + /* Two non-existing file objects */ + + strcpy(conv_adr1, adr1); + burn__split_path(conv_adr1, &dpt1, &npt1); + strcpy(conv_adr2, adr2); + burn__split_path(conv_adr2, &dpt2, &npt2); + if (strcmp(npt1, npt2)) + return 0; /* basenames differ */ + stat_ret1= stat(adr1, &stbuf1); + stat_ret2= stat(adr2, &stbuf2); + if (stat_ret1 != stat_ret2) + return 0; /* one dir existing, one not */ + + /* Both directories exist. The basenames are equal. + So the adresses are equal if the directories are + equal.*/ + } + if (stbuf1.st_ino == stbuf2.st_ino && + stbuf1.st_dev == stbuf2.st_dev) + return 1; /* same filesystem object */ + + if (S_ISBLK(stbuf1.st_mode) && S_ISBLK(stbuf2.st_mode) && + stbuf1.st_rdev == stbuf2.st_rdev) + return 1; /* same major,minor device number */ + + /* Are both filesystem objects related to the same MMC drive */ + if (conv_ret2 <= 0) + return 0; /* no MMC drive at adr2 */ + if (burn_drive_convert_fs_adr(adr1, conv_adr1) <= 0) + return 0; /* no MMC drive at adr1 */ + if (strcmp(conv_adr1, conv_adr2) == 0) + return 1; /* same MMC drive */ + + return 0; /* all filesystem disguises are checked */ + + } else if (role1 == 1 && role2 != 1) { + /* MMC drive meets file object */ + + if (conv_ret2 <= 0) + return 0; /* no MMC drive at adr2 */ + if (strcmp(adr1, conv_adr2) == 0) + return 1; /* same MMC drive */ + return 0; + + } else if (role1 != 1 && role2 == 1) { + /* stdio-drive meets wannabe MMC drive */ + + if (conv_ret2 <= 0) + return 0; /* no MMC drive at adr2 */ + if (burn_drive_convert_fs_adr(adr1, conv_adr1) <= 0) + return 0; /* no MMC drive at adr1 */ + if (strcmp(conv_adr1, conv_adr2) == 0) + return 1; /* same MMC drive */ + return 0; + + } + return 0; /* now i believe they are really not equal */ +} + + +int burn_drive_find_by_thread_pid(struct burn_drive **d, pid_t pid) +{ + int i; + + for (i = 0; i < drivetop + 1; i++) { + +/* + if (drive_array[i].thread_pid_valid) + fprintf(stderr, "libburn_EXPERIMENTAL : drive %d , thread_pid %d\n", i, drive_array[i].thread_pid); +*/ + + if (drive_array[i].thread_pid_valid && + drive_array[i].thread_pid == pid) { + *d = &(drive_array[i]); + return 1; + } + } + return 0; +} + + diff --git a/libburn/branches/ZeroFourTwo/libburn/drive.h b/libburn/branches/ZeroFourTwo/libburn/drive.h new file mode 100644 index 00000000..84412939 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/drive.h @@ -0,0 +1,128 @@ +/* -*- 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); */ +/* @param flag bit0= demand freed drives (else released drives) */ +int burn_drives_are_clear(int flag); + +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); + +/* @param flag bit0= reset global drive list */ +int burn_drive_scan_sync(struct burn_drive_info *drives[], + unsigned int *n_drives, int flag); + +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); + + +/* ts A70207 : evaluate write mode related peculiarities of a disc */ +struct burn_disc_mode_demands { + int multi_session; + int multi_track; + int unknown_track_size; /* 0=known, 1=unknown, 2=unknown+defaulted */ + int mixed_mode; + int audio; + int exotic_track; + int block_types; + int will_append; /* because of media state or multi session disc */ +}; +int burn_disc_get_write_mode_demands(struct burn_disc *disc, + struct burn_write_opts *opts, + struct burn_disc_mode_demands *result, int flag); + + +/* ts A70924 : convert a special stdio address into fd number. + @return >0 is a valid fd , -1 indicates unsuitable address string. +*/ +int burn_drive__fd_from_special_adr(char *adr); + + +/* ts A70929 : Find the drive which is being worked on by pid */ +int burn_drive_find_by_thread_pid(struct burn_drive **d, pid_t pid); + + +#endif /* __DRIVE */ diff --git a/libburn/branches/ZeroFourTwo/libburn/error.h b/libburn/branches/ZeroFourTwo/libburn/error.h new file mode 100644 index 00000000..74d4f68d --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/file.c b/libburn/branches/ZeroFourTwo/libburn/file.c new file mode 100644 index 00000000..ffa61fd3 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/file.c @@ -0,0 +1,542 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include <stdlib.h> +#include <sys/types.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include "source.h" +#include "libburn.h" +#include "file.h" +#include "async.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + + +/* 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_file *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 (fs->fixed_size > 0) + return fs->fixed_size; + if (fstat(fs->datafd, &buf) == -1) + return (off_t) 0; + return (off_t) buf.st_size; +} + + +/* ts A70125 */ +static int file_set_size(struct burn_source *source, off_t size) +{ + struct burn_source_file *fs = source->data; + + fs->fixed_size = size; + return 1; +} + + +struct burn_source *burn_file_source_new(const char *path, const char *subpath) +{ + struct burn_source_file *fs; + struct burn_source *src; + int fd1 = -1, fd2 = -1; + + if (!path) + return NULL; + fd1 = open(path, O_RDONLY); + if (fd1 == -1) + return NULL; + if (subpath != NULL) { + fd2 = open(subpath, O_RDONLY); + if (fd2 == -1) { + close(fd1); + return NULL; + } + } + fs = malloc(sizeof(struct burn_source_file)); + + /* ts A70825 */ + if (fs == NULL) { +failure:; + close(fd1); + if (fd2 >= 0) + close(fd2); + return NULL; + } + + fs->datafd = fd1; + fs->subfd = fd2; + + /* ts A70125 */ + fs->fixed_size = 0; + + src = burn_source_new(); + + /* ts A70825 */ + if (src == NULL) { + free((char *) fs); + goto failure; + } + + src->read = file_read; + if (subpath) + src->read_sub = file_read_sub; + + src->get_size = file_size; + src->set_size = file_set_size; + src->free_data = file_free; + src->data = fs; + return src; +} + + +/* ts A70126 : removed class burn_source_fd in favor of burn_source_file */ + +struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size) +{ + struct burn_source_file *fs; + struct burn_source *src; + + if (datafd == -1) + return NULL; + fs = malloc(sizeof(struct burn_source_file)); + if (fs == NULL) /* ts A70825 */ + return NULL; + fs->datafd = datafd; + fs->subfd = subfd; + fs->fixed_size = size; + + src = burn_source_new(); + + /* ts A70825 */ + if (src == NULL) { + free((char *) fs); + return NULL; + } + + src->read = file_read; + if(subfd != -1) + src->read_sub = file_read_sub; + src->get_size = file_size; + src->set_size = file_set_size; + src->free_data = file_free; + src->data = fs; + return src; +} + + +/* ts A71003 */ +/* ------------------------------ fifo --------------------------- */ + +/* The fifo mechanism consists of a burn_source proxy which is here, + a thread management team which is located in async.c, + and a synchronous shoveller which is here. +*/ + +static int fifo_sleep(int flag) +{ + static struct timespec sleeptime = { 0, 50000000}; /* 50 ms */ + + return nanosleep(&sleeptime, NULL); +} + + +static int fifo_read(struct burn_source *source, + unsigned char *buffer, + int size) +{ + struct burn_source_fifo *fs = source->data; + int ret, todo, rpos, bufsize, diff; + + if (fs->end_of_consumption) { + /* ??? msg: reading has been ended already */; + return 0; + } + if (fs->is_started == 0) { + ret = burn_fifo_start(source, 0); + if (ret <= 0) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020152, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Cannot start fifo thread", 0, 0); + fs->end_of_consumption = 1; + return -1; + } + fs->is_started = 1; + } + if (size == 0) + return 0; + + /* Reading from the ring buffer */ + + /* This needs no mutex because each volatile variable has one thread + which may write and the other which only reads and is aware of + volatility. + The feeder of the ringbuffer is in burn_fifo_source_shoveller(). + */ + todo = size; + bufsize = fs->chunksize * fs->chunks; + while (todo > 0) { + /* readpos is not volatile here , writepos is volatile */ + rpos = fs->buf_readpos; + while (rpos == fs->buf_writepos) { + if (fs->end_of_input) + break; + if (fs->input_error) { + if (todo < size) /* deliver partial buffer */ + break; + fs->end_of_consumption = 1; + libdax_msgs_submit(libdax_messenger, -1, + 0x00020154, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, + "Forwarded input error ends output", 0, 0); + return -1; + } + fifo_sleep(0); + } + diff = fs->buf_writepos - rpos; /* read volatile only once */ + if (diff == 0) + break; + if (diff > 0) + /* diff bytes are available */; + else + /* at least (bufsize - rpos) bytes are available */ + diff = bufsize - rpos; + if (diff > todo) + diff = todo; + memcpy(buffer, fs->buf+(size-todo)+rpos, diff); + fs->buf_readpos += diff; + if (fs->buf_readpos >= bufsize) + fs->buf_readpos = 0; + todo -= diff; + } + if (size - todo <= 0) + fs->end_of_consumption = 1; + else + fs->out_counter += size - todo; + +/* + fprintf(stderr, + "libburn_EXPERIMENTAL: read= %d , pos= %d , out_count= %.f\n", + (size - todo), fs->buf_readpos, (double) fs->out_counter); +*/ + + return (size - todo); +} + + +static off_t fifo_get_size(struct burn_source *source) +{ + struct burn_source_fifo *fs = source->data; + + return fs->inp->get_size(fs->inp); +} + + +static int fifo_set_size(struct burn_source *source, off_t size) +{ + struct burn_source_fifo *fs = source->data; + + return fs->inp->set_size(fs->inp, size); +} + + +static void fifo_free(struct burn_source *source) +{ + struct burn_source_fifo *fs = source->data; + + if (fs->inp != NULL) + burn_source_free(fs->inp); + if (fs->buf != NULL) + free(fs->buf); + free((char *) fs); +} + + +int burn_fifo_source_shoveller(struct burn_source *source, int flag) +{ + struct burn_source_fifo *fs = source->data; + int ret, bufsize, diff, wpos, rpos, trans_end, free_bytes; + char *bufpt; + + fs->thread_pid = getpid(); + fs->thread_pid_valid = 1; + + bufsize = fs->chunksize * fs->chunks; + while (!fs->end_of_consumption) { + + /* wait for enough buffer space available */ + wpos = fs->buf_writepos; + while (1) { + rpos = fs->buf_readpos; + diff = rpos - wpos; + trans_end = 0; + if (diff == 0) + free_bytes = bufsize - 1; + else if (diff > 0) + free_bytes = diff - 1; + else { + free_bytes = (bufsize - wpos) + rpos - 1; + if (bufsize - wpos < fs->chunksize) + trans_end = 1; + } + if (free_bytes >= fs->chunksize) + break; + fifo_sleep(0); + } + + /* prepare the receiving memory */ + bufpt = fs->buf + wpos; + if (trans_end) { + bufpt = calloc(fs->chunksize, 1); + if (bufpt == NULL) { + libdax_msgs_submit(libdax_messenger, -1, + 0x00000003, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Out of virtual memory", 0, 0); + fs->input_error = ENOMEM; + break; + } + } + + /* Obtain next chunk */ + if (fs->inp->read != NULL) + ret = fs->inp->read(fs->inp, + (unsigned char *) bufpt, fs->chunksize); + else + ret = fs->inp->read_xt( fs->inp, + (unsigned char *) bufpt, fs->chunksize); + if (ret > 0) + fs->in_counter += ret; + else if (ret == 0) + break; /* EOF */ + else { + libdax_msgs_submit(libdax_messenger, -1, 0x00020153, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Read error on fifo input", errno, 0); + fs->input_error = errno; + if(errno == 0) + fs->input_error = EIO; + break; + } + + /* activate read chunk */ + if (ret > fs->chunksize) /* beware of ill custom burn_source */ + ret = fs->chunksize; + if (trans_end) { + /* copy to end of buffer */ + memcpy(fs->buf + wpos, bufpt, bufsize - wpos); + /* copy to start of buffer */ + memcpy(fs->buf, bufpt + (bufsize - wpos), + fs->chunksize - (bufsize - wpos)); + free(bufpt); + if (ret >= bufsize - wpos) + fs->buf_writepos = ret - (bufsize - wpos); + else + fs->buf_writepos += ret; + } else if (fs->buf_writepos + ret == bufsize) + fs->buf_writepos = 0; + else + fs->buf_writepos += ret; + +/* + fprintf(stderr, "[%2.2d%%] ", + (int) (100.0 - 100.0 * ((double) free_bytes) / + (double) bufsize)); + fprintf(stderr, + "libburn_EXPERIMENTAL: writepos= %d ,in_count = %.f\n", + fs->buf_writepos, (double) fs->in_counter); +*/ + } + if (!fs->end_of_consumption) + fs->end_of_input = 1; + + /* wait for end of reading by consumer */; + while (fs->buf_readpos != fs->buf_writepos && !fs->end_of_consumption) + fifo_sleep(0); + + /* destroy ring buffer */; + if (!fs->end_of_consumption) + fs->end_of_consumption = 2; /* Claim stop of consumption */ + + /* This is not prone to race conditions because either the consumer + indicated hangup by fs->end_of_consumption = 1 or the consumer set + fs->buf_readpos to a value indicating the buffer is empty. + So in both cases the consumer is aware that reading is futile + or even fatal. + */ + free(fs->buf); /* Give up fifo buffer. Next fifo might start soon. */ + fs->buf = NULL; + + return (fs->input_error == 0); +} + + +int burn_fifo_cancel(struct burn_source *source) +{ + struct burn_source_fifo *fs = source->data; + + burn_source_cancel(fs->inp); + return(1); +} + + +struct burn_source *burn_fifo_source_new(struct burn_source *inp, + int chunksize, int chunks, int flag) +{ + struct burn_source_fifo *fs; + struct burn_source *src; + + if (((double) chunksize) * ((double) chunks) > 1024.0*1024.0*1024.0) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020155, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Desired fifo buffer too large (> 1GB)", 0, 0); + return NULL; + } + if (chunksize < 1 || chunks < 2) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020156, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Desired fifo buffer too small", 0, 0); + return NULL; + } + fs = malloc(sizeof(struct burn_source_fifo)); + if (fs == NULL) + return NULL; + fs->is_started = 0; + fs->thread_pid = 0; + fs->thread_pid_valid = 0; + fs->inp = NULL; /* set later */ + fs->chunksize = chunksize; + fs->chunks = chunks; + fs->buf = NULL; + fs->buf_writepos = fs->buf_readpos = 0; + fs->end_of_input = 0; + fs->input_error = 0; + fs->end_of_consumption = 0; + fs->in_counter = fs->out_counter = 0; + + src = burn_source_new(); + if (src == NULL) { + free((char *) fs); + return NULL; + } + src->read = NULL; + src->read_sub = NULL; + src->get_size = fifo_get_size; + src->set_size = fifo_set_size; + src->free_data = fifo_free; + src->data = fs; + src->version= 1; + src->read_xt = fifo_read; + src->cancel= burn_fifo_cancel; + fs->inp = inp; + inp->refcount++; /* make sure inp lives longer than src */ + + return src; +} + + +/* ts A71003 : API */ +int burn_fifo_inquire_status(struct burn_source *source, + int *size, int *free_bytes, char **status_text) +{ + struct burn_source_fifo *fs = source->data; + int ret = 0, diff, wpos, rpos; + static char *(states[8]) = { + "standby", "active", "ending", "failing", + "unused", "abandoned", "ended", "aborted"}; + + *status_text = NULL; + *size = 0; + + if (source->free_data != fifo_free) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020157, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "burn_source is not a fifo object", 0, 0); + return -1; + } + *size = fs->chunksize * fs->chunks; + rpos = fs->buf_readpos; + wpos = fs->buf_writepos; + diff = rpos - wpos; + if (diff == 0) + *free_bytes = *size - 1; + else if (diff > 0) + *free_bytes = diff - 1; + else + *free_bytes = (*size - wpos) + rpos - 1; + ret = 0; + if (fs->end_of_consumption > 0) + ret |= 4; + if (fs->input_error) + ret |= 3; + else if (fs->end_of_input) + ret |= 2; + else if(fs->buf != NULL) + ret |= 1; + *status_text = states[ret]; + return ret; +} + diff --git a/libburn/branches/ZeroFourTwo/libburn/file.h b/libburn/branches/ZeroFourTwo/libburn/file.h new file mode 100644 index 00000000..7d99c643 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/file.h @@ -0,0 +1,62 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__FILE_H +#define BURN__FILE_H + +struct burn_source_file +{ + char magic[4]; + + int datafd; + int subfd; + off_t fixed_size; +}; + +/* ts A70126 : burn_source_file obsoleted burn_source_fd */ + + +/* ts A70930 */ +struct burn_source_fifo { + char magic[4]; + + /* The fifo stays inactive and unequipped with eventual resources + until its read() method is called for the first time. + Only then burn_fifo_start() gets called, allocates the complete + resources, starts a thread with burn_fifo_source_shuffler() + which shuffles data and finally destroys the resources. + This late start is to stay modest in case of multiple tracks + in one disc. + */ + int is_started; + + int thread_pid; + int thread_pid_valid; + + /* the burn_source for which this fifo is acting as proxy */ + struct burn_source *inp; + + /* <<< up to now it was only a pipe. This is on its way out. */ + int outlet[2]; + + /* The ring buffer mechanism */ + int chunksize; + int chunks; + char *buf; + volatile int buf_writepos; + volatile int buf_readpos; + volatile int end_of_input; + volatile int input_error; + volatile int end_of_consumption; + + off_t in_counter; + off_t out_counter; +}; + + +/** The worker behind the fifo thread. + Gets started from burn_fifo_start() in async.c +*/ +int burn_fifo_source_shoveller(struct burn_source *source, int flag); + + +#endif /* LIBBURN__FILE_H */ diff --git a/libburn/branches/ZeroFourTwo/libburn/init.c b/libburn/branches/ZeroFourTwo/libburn/init.c new file mode 100644 index 00000000..0a4c6c0d --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/init.c @@ -0,0 +1,408 @@ +/* -*- 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 <stdlib.h> + +/* ts A70928 : init.h is for others, not for init .c +#include "init.h" +*/ + + +#include "sg.h" +#include "error.h" +#include "libburn.h" +#include "drive.h" +#include "transport.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 : Linux: wether to use O_EXCL on open() of device files */ +int burn_sg_open_o_excl = 1; + +/* ts A70403 : Linux: wether to use fcntl(,F_SETLK,) + after open() of device files */ +int burn_sg_fcntl_f_setlk = 1; + +/* ts A70314 : Linux: what device family to use : + 0= default family + 1= sr + 2= scd + (3= st) + 4= sg +*/ +int burn_sg_use_family = 0; + +/* 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; +volatile int burn_global_abort_level= 0; +int burn_global_abort_signum= 0; +void *burn_global_signal_handle = NULL; +burn_abort_handler_t burn_global_signal_handler = NULL; + + +/* ts A70223 : wether implemented untested profiles are supported */ +int burn_support_untested_profiles = 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; + burn_support_untested_profiles = 0; + 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(0)) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020107, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + "A drive is still busy on shutdown of library", 0, 0); + usleep(1000001); + burn_abort(4440, burn_abort_pacifier, abort_message_prefix); + } + + /* 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 & 3; + burn_sg_fcntl_f_setlk = !!(exclusive & 32); + burn_sg_use_family = (exclusive >> 2) & 7; + 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; + if (libdax_messenger == NULL) + 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; +} + + +/* ts A70922 : API */ +int burn_msgs_submit(int error_code, char msg_text[], int os_errno, + char severity[], struct burn_drive *d) +{ + int ret, sevno, global_index = -1; + + ret = libdax_msgs__text_to_sev(severity, &sevno, 0); + if (ret <= 0) + sevno = LIBDAX_MSGS_SEV_FATAL; + if (error_code <= 0) { + switch(sevno) { + case LIBDAX_MSGS_SEV_ABORT: error_code = 0x00040000; + break; case LIBDAX_MSGS_SEV_FATAL: error_code = 0x00040001; + break; case LIBDAX_MSGS_SEV_SORRY: error_code = 0x00040002; + break; case LIBDAX_MSGS_SEV_WARNING: error_code = 0x00040003; + break; case LIBDAX_MSGS_SEV_HINT: error_code = 0x00040004; + break; case LIBDAX_MSGS_SEV_NOTE: error_code = 0x00040005; + break; case LIBDAX_MSGS_SEV_UPDATE: error_code = 0x00040006; + break; case LIBDAX_MSGS_SEV_DEBUG: error_code = 0x00040007; + break; default: error_code = 0x00040001; + } + } + if (d != NULL) + global_index = d->global_index; + ret = libdax_msgs_submit(libdax_messenger, global_index, error_code, + sevno, LIBDAX_MSGS_PRIO_HIGH, msg_text, os_errno, 0); + return ret; +} + + +/* ts A71016 API */ +int burn_text_to_sev(char *severity_name, int *sevno, int flag) +{ + int ret; + + ret = libdax_msgs__text_to_sev(severity_name, sevno, 0); + if (ret <= 0) + *sevno = LIBDAX_MSGS_SEV_FATAL; + return ret; +} + + +int burn_builtin_abort_handler(void *handle, int signum, int flag) +{ + +#define Libburn_new_thread_signal_handleR 1 +/* +#define Libburn_signal_handler_verbouS 1 +*/ + int ret; + struct burn_drive *d; + +#ifdef Libburn_signal_handler_verbouS + fprintf(stderr, + "libburn_ABORT: pid = %d , abort_control_pid = %d , sig= %d\n", + getpid(), abort_control_pid, signum); +#endif + + /* ts A70928: + Must be quick. Allowed to coincide with other thread and to share + the increment with that one. It must not decrease, though, and + yield at least 1 if any thread calls this function. + */ + burn_global_abort_level++; + burn_global_abort_signum= signum; + + if(getpid() != abort_control_pid) { + +#ifdef Libburn_new_thread_signal_handleR + + ret = burn_drive_find_by_thread_pid(&d, getpid()); + if (ret > 0 && d->busy == BURN_DRIVE_WRITING) { + /* This is an active writer thread */ + +#ifdef Libburn_signal_handler_verbouS + fprintf(stderr, "libburn_ABORT: pid %d found drive busy with writing, (level= %d)\n", (int) getpid(), burn_global_abort_level); +#endif + + d->sync_cache(d); + + /* >>> perform a more qualified end of burn process */; + + d->busy = BURN_DRIVE_IDLE; + + if (burn_global_abort_level > 0) { + /* control process did not show up yet */ +#ifdef Libburn_signal_handler_verbouS + fprintf(stderr, "libburn_ABORT: pid %d sending signum %d to pid %d\n", (int) getpid(), (int) signum, (int) abort_control_pid); +#endif + kill(abort_control_pid, signum); + } + +#ifdef Libburn_signal_handler_verbouS + fprintf(stderr, "libburn_ABORT: pid %d signum %d returning -2\n", (int) getpid(), (int) signum); +#endif + + return -2; + } else { + usleep(1000000); /* calm down */ + return -2; + } + +#else + usleep(1000000); /* calm down */ + return -2; +#endif /* ! Libburn_new_thread_signal_handleR */ + + } + burn_global_abort_level = -1; + 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); + burn_global_abort_level = -2; + 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); + burn_global_signal_handle = handle; + burn_global_signal_handler = handler; +} + + +/* ts A70223 : API */ +void burn_allow_untested_profiles(int yes) +{ + burn_support_untested_profiles = !!yes; +} + + +/* ts A70915 : API */ +int burn_set_messenger(void *messenger) +{ + struct libdax_msgs *pt; + + if (libdax_msgs_refer(&pt, messenger, 0) <= 0) + return 0; + libdax_msgs_destroy(&libdax_messenger, 0); + libdax_messenger = (struct libdax_msgs *) pt; + return 1; +} + diff --git a/libburn/branches/ZeroFourTwo/libburn/init.h b/libburn/branches/ZeroFourTwo/libburn/init.h new file mode 100644 index 00000000..ba2fb4f9 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/init.h @@ -0,0 +1,21 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__INIT_H +#define BURN__INIT_H + +extern int burn_running; + +/** Indicator for burn_drive_get_status() wether a signal hit parts of the + thread team. + 0= all works well , + 1 to 5 = waiting for eventual signal on control thread + > 5 = do abort now + -1 = control thread has been informed +*/ +extern volatile int burn_global_abort_level; +extern int burn_global_abort_signum; +extern void *burn_global_signal_handle; +extern burn_abort_handler_t burn_global_signal_handler; + + +#endif /* BURN__INIT_H */ diff --git a/libburn/branches/ZeroFourTwo/libburn/lec.c b/libburn/branches/ZeroFourTwo/libburn/lec.c new file mode 100644 index 00000000..9141593a --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/lec.h b/libburn/branches/ZeroFourTwo/libburn/lec.h new file mode 100644 index 00000000..f6980301 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/libburn.h b/libburn/branches/ZeroFourTwo/libburn/libburn.h new file mode 100644 index 00000000..cd085a89 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/libburn.h @@ -0,0 +1,2254 @@ +/* -*- 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, (for DVD Incremental Streaming use TAO) + */ + BURN_WRITE_PACKET, + + /** With CD: Track At Once recording + 2s gaps between tracks, no fonky lead-ins + + With sequential DVD-R[W]: Incremental Streaming + With DVD-RAM/+RW: Random Writeable (used sequentially) + With overwriteable DVD-RW: Rigid Restricted Overwrite + */ + BURN_WRITE_TAO, + + /** With CD: Session At Once + Block type MUST be BURN_BLOCK_SAO + ts A70122: Currently not capable of mixing data and audio tracks. + + With sequential DVD-R[W]: Disc-at-once, DAO + Single session, single track, fixed size mandatory, (-dvd-compat) + */ + BURN_WRITE_SAO, + + /** With CD: Raw disc at once recording. + all subcodes must be provided by lib or user + only raw block types are supported + */ + BURN_WRITE_RAW, + + /** In replies this indicates that not any writing will work. + As parameter for inquiries it indicates that no particular write + mode shall is specified. + Do not use for setting a write mode for burning. It won't work. + */ + BURN_WRITE_NONE +}; + +/** 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, + + /* ts A70822 */ + /** The drive is busy in synchronous read (if you see this then it + has been interrupted) */ + BURN_DRIVE_READING_SYNC, + /** The drive is busy in synchronous write (if you see this then it + has been interrupted) */ + BURN_DRIVE_WRITING_SYNC + +}; + + +/** 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. + + CAUTION : This structure is prone to future extension ! + + Do not restrict your application to unsigned char with any counter like + "session", "point", "pmin", ... + Do not rely on the current size of a burn_toc_entry. + + ts A70201 : DVD extension, see below +*/ +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; + + /* Indicates whether extension data are valid and eventually override + older elements in this structure: + bit0= DVD extension is valid + */ + unsigned char extensions_valid; + + /* ts A70201 : DVD extension. + If invalid the members are guaranteed to be 0. */ + /* Tracks and session numbers are 16 bit. Here are the high bytes. */ + unsigned char session_msb; + unsigned char point_msb; + /* pmin, psec, and pframe may be too small if DVD extension is valid */ + int start_lba; + /* min, sec, and frame may be too small if DVD extension is valid */ + int track_blocks; + +}; + + +/** Data source interface for tracks. + This allows to use arbitrary program code as provider of track input data. + + Objects compliant to this interface are either provided by the application + or by API calls of libburn: burn_fd_source_new() , burn_file_source_new(), + and burn_fifo_source_new(). + + The API calls allow to use any file object as data source. Consider to feed + an eventual custom data stream asynchronously into a pipe(2) and to let + libburn handle the rest. + In this case the following rule applies: + Call burn_source_free() exactly once for every source obtained from + libburn API. You MUST NOT otherwise use or manipulate its components. + + In general, burn_source objects can be freed as soon as they are attached + to track objects. The track objects will keep them alive and dispose them + when they are no longer needed. With a fifo burn_source it makes sense to + keep the own reference for inquiring its state while burning is in + progress. + + --- + + The following description of burn_source applies only to application + implemented burn_source objects. You need not to know it for API provided + ones. + + If you really implement an own passive data producer by this interface, + then beware: it can do anything and it can spoil everything. + + In this case the functions (*read), (*get_size), (*set_size), (*free_data) + MUST be implemented by the application and attached to the object at + creation time. + Function (*read_sub) is allowed to be NULL or it MUST be implemented and + attached. + + burn_source.refcount MUST be handled properly: If not exactly as many + references are freed as have been obtained, then either memory leaks or + corrupted memory are the consequence. + All objects which are referred to by *data must be kept existent until + (*free_data) is called via burn_source_free() by the last referer. +*/ +struct burn_source { + + /** Reference count for the data source. MUST be 1 when a new source + is created and thus the first reference is handed out. Increment + it to take more references for yourself. Use burn_source_free() + to destroy your references to it. */ + int refcount; + + + /** Read data from the source. Semantics like with read(2), but MUST + either deliver the full buffer as defined by size or MUST deliver + EOF (return 0) or failure (return -1) at this call or at the + next following call. I.e. the only incomplete buffer may be the + last one from that source. + libburn will read a single sector by each call to (*read). + The size of a sector depends on BURN_MODE_*. The known range is + 2048 to 2352. + + IMPORTANT: + If this function pointer is NULL, then the struct burn_source is of + version >= 1 and the job of .(*read)() is done by .(*read_xt)(). + See below, member .version. + */ + int (*read)(struct burn_source *, unsigned char *buffer, int size); + + + /** Read subchannel data from the source (NULL if lib generated) + WARNING: This is an obscure feature with CD raw write modes. + Unless you checked the libburn code for correctness in that aspect + you should not rely on raw writing with own subchannels. + ADVICE: Set this pointer to NULL. + */ + int (*read_sub)(struct burn_source *, unsigned char *buffer, int size); + + + /** Get the size of the source's data. Return 0 means unpredictable + size. If application provided (*get_size) allows return 0, then + the application MUST provide a fully functional (*set_size). + */ + off_t (*get_size)(struct burn_source *); + + + /* ts A70125 : BROKE BINARY BACKWARD COMPATIBILITY AT libburn-0.3.1. */ + /** Program the reply of (*get_size) to a fixed value. It is advised + to implement this by a attribute off_t fixed_size; in *data . + The read() function does not have to take into respect this fake + setting. It is rather a note of libburn to itself. Eventually + necessary truncation or padding is done in libburn. Truncation + is usually considered a misburn. Padding is considered ok. + + libburn is supposed to work even if (*get_size) ignores the + setting by (*set_size). But your application will not be able to + enforce fixed track sizes by burn_track_set_size() and possibly + even padding might be left out. + */ + int (*set_size)(struct burn_source *source, off_t size); + + + /** Clean up the source specific data. This function will be called + once by burn_source_free() when the last referer disposes the + source. + */ + void (*free_data)(struct burn_source *); + + + /** Next source, for when a source runs dry and padding is disabled + WARNING: This is an obscure feature. Set to NULL at creation and + from then on leave untouched and uninterpreted. + */ + struct burn_source *next; + + + /** Source specific data. Here the various source classes express their + specific properties and the instance objects store their individual + management data. + E.g. data could point to a struct like this: + struct app_burn_source + { + struct my_app *app_handle; + ... other individual source parameters ... + off_t fixed_size; + }; + + Function (*free_data) has to be prepared to clean up and free + the struct. + */ + void *data; + + + /* ts A71222 : Supposed to be binary backwards compatible extension. */ + + /** Valid only if above member .(*read)() is NULL. This indicates a + version of struct burn_source younger than 0. + From then on, member .version tells which further members exist + in the memory layout of struct burn_source. libburn will only touch + those announced extensions. + + Versions: + 0 has .(*read)() != NULL, not even .version is present. + 1 has .version, .(*read_xt)(), .(*cancel)() + */ + int version; + + /** This substitutes for (*read)() in versions above 0. */ + int (*read_xt)(struct burn_source *, unsigned char *buffer, int size); + + /** Informs the burn_source that the consumer of data prematurely + ended reading. This call may or may not be issued by libburn + before (*free_data)() is called. + */ + int (*cancel)(struct burn_source *source); +}; + + +/** 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]; + + /** Invalid: Was: "Location of the drive in the filesystem." */ + /** This string has no meaning any more. Once it stored the persistent + drive address. Now always use function burn_drive_d_get_adr() to + inquire a persistent address. ^^^^^^ ALWAYS ^^^^^^^^ */ + char location[17]; + + /** 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 bytes stored in buffer during write. + (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 Linux only: + 0 = no attempt to make drive access 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 . + One may select a device SCSI file family by adding + 0 = default family + 4 = /dev/sr%d + 8 = /dev/scd%d + 16 = /dev/sg%d + Do not use other values ! + Add 32 to demand an exclusive lock by fcntl(,F_SETLK,) + after open() has succeeded. + @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 A70223 */ +/** Allows the use of media types which are implemented in libburn but not yet + tested. The list of those untested profiles is subject to change. + Currently it contains: 0x15 "DVD-R/DL sequential recording", + 0x2b "DVD+R/DL" + If you really test such media, then please report the outcome on + libburn-hackers@pykix.org + If ever then this call should be done soon after burn_initialize() before + any drive scanning. + @param yes 1=allow all implemented profiles, 0=only tested media (default) +*/ +void burn_allow_untested_profiles(int yes); + + +/* 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_d_get_adr(drive_infos[driveno].drive,adr). + Another way is to drop the unwanted drives by burn_drive_info_forget(). + + Operating on multiple drives: + + Different than with burn_drive_scan() it is allowed to call + burn_drive_scan_and_grab() without giving up any other scanned drives. So + this call can be used to get a collection of more than one aquired drives. + The attempt to aquire the same drive twice will fail, though. + + Pseudo-drives: + + burn_drive_scan_and_grab() is able to aquire virtual drives which will + accept options much like a MMC burner drive. Many of those options will not + cause any effect, though. The address of a pseudo-drive begins with + prefix "stdio:" followed by a path. + Examples: "stdio:/tmp/pseudo_drive" , "stdio:/dev/null" , "stdio:-" + + If the path is empty, the result is a null-drive = drive role 0. + It pretends to have loaded no media and supports no reading or writing. + + If the path leads to an existing regular file, or to a not yet existing + file, or to an existing block device, then the result is a random access + stdio-drive capable of reading and writing = drive role 2. + + If the path leads to an existing file of any type other than directory, + then the result is a sequential write-only stdio-drive = drive role 3. + + The special address form "stdio:/dev/fd/<number>" is interpreted literally + as reference to open file descriptor <number>. This address form coincides + with real files on some systems, but it is in fact hardcoded in libburn. + Special address "stdio:-" means stdout = "stdio:/dev/fd/1". + The role of such a drive is determined by the file type obtained via + fstat(<number>). + + Roles 2 and 3 perform all their eventual data transfer activities on a file + via standard i/o functions open(2), lseek(2), read(2), write(2), close(2). + The media profile is reported as 0xffff. Write space information from those + media is not necessarily realistic. + + The capabilities of role 2 resemble DVD-RAM but it can simulate writing. + If the path does not exist in the filesystem yet, it is attempted to create + it as a regular file as soon as write operations are started. + + The capabilities of role 3 resemble a blank DVD-R. + + One may distinguish pseudo-drives from MMC drives by call + burn_drive_get_drive_role(). + + @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 once + obtained by burn_drive_d_get_adr() or composed skillfully by + application resp. its user. E.g. "/dev/sr0". + Consider to preprocess it by burn_drive_convert_fs_adr(). + @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. + In case of re-scanning: + All pointers to struct burn_drive and all struct burn_drive_info arrays + are invalidated by using this function. Do NOT store drive pointers across + calls to this function ! + To avoid invalid pointers one MUST free all burn_drive_info arrays + by burn_drive_info_free() before calling burn_drive_scan() a second time. + If there are drives left, then burn_drive_scan() will refuse to work. + + 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); + + +/** When no longer needed, free a whole burn_drive_info array which was + returned by burn_drive_scan(). + For freeing single drive array elements use burn_drive_info_forget(). +*/ +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 The drive to inquire. + @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_d_get_adr(struct burn_drive *drive, char adr[]); + +/** Inquire the persistent address of a drive via a given drive_info object. + (Note: This is a legacy call.) + @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 whether 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_d_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). + Linux: The drive device file is not reserved afterwards. (O_EXCL, F_SETLK). + @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); + + +/* ts A70918 */ +/** Like burn_drive_release() but keeping the drive tray closed and its + eject button disabled. This physically locked drive state will last until + the drive is grabbed again and released via burn_drive_release(). + Programs like eject, cdrecord, growisofs will break that ban too. + @param drive The drive to release and leave locked. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 means success, <=0 means failure +*/ +int burn_drive_leave_locked(struct burn_drive *d, int flag); + + +/** 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 A70131 */ +/** Read start lba of the first track in the last complete session. + This is the first parameter of mkisofs option -C. The second parameter + is nwa as obtained by burn_disc_track_lba_nwa() with trackno 0. + @param d The drive to query. + @param start_lba returns the start address of that track + @return <= 0 : failure, 1 = ok +*/ +int burn_disc_get_msc1(struct burn_drive *d, int *start_lba); + + +/* ts A70213 */ +/** Return the best possible estimation of the currently available capacity of + the media. This might depend on particular write option settings. For + inquiring the space with such a set of options, the drive has to be + grabbed and BURN_DRIVE_IDLE. If not, then one will only get a canned value + from the most recent automatic inquiry (e.g. during last drive grabbing). + An eventual start address from burn_write_opts_set_start_byte() will be + subtracted from the obtained capacity estimation. Negative results get + defaulted to 0. + @param d The drive to query. + @param o If not NULL: write parameters to be set on drive before query + @return number of most probably available free bytes +*/ +off_t burn_disc_available_space(struct burn_drive *d, + struct burn_write_opts *o); + + +/* 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", + 0x11 "DVD-R sequential recording", 0x12 "DVD-RAM", + 0x13 "DVD-RW restricted overwrite", 0x14 "DVD-RW sequential recording", + 0x1a "DVD+RW", 0x1b "DVD+R". + If enabled by burn_allow_untested_profiles() it also writes to profiles + 0x15 "DVD-R/DL sequential recording", 0x2b "DVD+R/DL". + Writeable stdio-drives return this profile + 0xffff "stdio file" + @param d The drive where the media is inserted. + @param pno Profile Number as of mmc5r03c.pdf, table 89 + @param name Profile Name (see above list, 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); + + +/* ts A70901 */ +/** Inquires the drive associated with a burn_write_opts object. + @param opts object to inquire + @return pointer to drive +*/ +struct burn_drive *burn_write_opts_get_drive(struct burn_write_opts *opts); + + +/** 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. + With DVD-RW, fast blanking yields media capable only of DAO. +*/ +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); + + + +/* ts A70222 */ +/** The length of a rejection reasons string for burn_precheck_write() and + burn_write_opts_auto_write_type() . +*/ +#define BURN_REASONS_LEN 4096 + + +/* ts A70219 */ +/** Examines a completed setup for burn_disc_write() whether it is permissible + with drive and media. This function is called by burn_disc_write() but + an application might be interested in this check in advance. + @param o The options for the writing operation. + @param disc The descrition of the disc to be created + @param reasons Eventually returns a list of rejection reason statements + @param silent 1= do not issue error messages , 0= report problems + @return 1 ok, -1= no recordable media detected, 0= other failure +*/ +int burn_precheck_write(struct burn_write_opts *o, struct burn_disc *disc, + char reasons[BURN_REASONS_LEN], int silent); + + +/** 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 ot BURN_DISC_APPENDABLE before calling this function. + Note: write_type BURN_WRITE_SAO is currently not capable of writing a mix + of data and audio tracks. You must use BURN_WRITE_TAO for such sessions. + To be set by burn_write_opts_set_write_type(). + Note: This function is not suitable for overwriting data in the middle of + a valid data area because it is allowed to append trailing data. + For exact random access overwriting use burn_random_access_write(). + @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 whether 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 + @return Pointer to a burn_disc object or NULL on failure. +*/ +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 + @return Pointer to a burn_session object or NULL on failure. + */ +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 whether 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); + + +/* ts A70218 */ +/** Set a default track size to be used only if the track turns out to be of + unpredictable length and if the effective write type demands a fixed size. + This can be useful to enable write types CD SAO or DVD DAO together with + a track source like stdin. If the track source delivers fewer bytes than + announced then the track will be padded up with zeros. + @param t The track to change + @param size The size to set + @return 0=failure 1=sucess +*/ +int burn_track_set_default_size(struct burn_track *t, off_t size); + +/** 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) + @param path The file address for the main channel payload. + @param subpath Eventual address for subchannel data. Only used in exotic + raw write modes. Submit NULL for normal tasks. + @return Pointer to a burn_source object, NULL indicates failure +*/ +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 of subchannel data. Only used in exotic + raw write modes. Submit -1 for normal tasks. + @param size The eventual fixed size of eventually both fds. + If this value is 0, the size will be determined from datafd. + @return Pointer to a burn_source object, NULL indicates failure +*/ +struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size); + + +/* ts A70930 */ +/** Creates a fifo which acts as proxy for an already existing data source. + The fifo provides a ring buffer which shall smoothen the data stream + between burn_source and writer thread. Each fifo serves only for one + data source and gets attached to one track as its only data source + by burn_track_set_source(). + A fifo starts its life in "standby" mode with no buffer space allocated. + As soon as its track requires bytes, the fifo establishes a worker thread + and allocates its buffer. After input has ended and all buffer content is + consumed, the buffer space gets freed and the worker thread ends. + This happens asynchronously. So expect two buffers and worker threads to + exist for a short time between tracks. Be modest in your size demands if + multiple tracks are to be expected. + @param inp The burn_source for which the fifo shall act as proxy. + It can be disposed by burn_source_free() immediately + after this call. + @param chunksize The size in bytes of a chunk. Use 2048 for sources + suitable for BURN_BLOCK_MODE1 and 2352 for sources + which deliver for BURN_BLOCK_AUDIO. + Some variations of burn_source might work only with + a particular chunksize. E.g. libisofs demands 2048. + @param chunks The number of chunks to be allocated in ring buffer. + This value must be >= 2. + @param flag Bitfield for control purposes (unused yet, submit 0). + @return A pointer to the newly created burn_source. + Later both burn_sources, inp and the returned fifo, have + to be disposed by calling burn_source_free() for each. + inp can be freed immediately, the returned fifo may be + kept as handle for burn_fifo_inquire_status(). +*/ +struct burn_source *burn_fifo_source_new(struct burn_source *inp, + int chunksize, int chunks, int flag); + +/* ts A71003 */ +/** Inquires state and fill parameters of a fifo burn_source which was created + by burn_fifo_source_new() . Do not use with other burn_source variants. + @param fifo The fifo object to inquire + @param size The total size of the fifo + @param free_bytes The current free capacity of the fifo + @param status_text Returns a pointer to a constant text, see below + @return <0 reply invalid, >=0 fifo status code: + bit0+1=input status, bit2=consumption status, i.e: + 0="standby" : data processing not started yet + 1="active" : input and consumption are active + 2="ending" : input has ended without error + 3="failing" : input had error and ended, + 4="unused" : ( consumption has ended before processing start ) + 5="abandoned" : consumption has ended prematurely + 6="ended" : consumption has ended without input error + 7="aborted" : consumption has ended after input error +*/ +int burn_fifo_inquire_status(struct burn_source *fifo, int *size, + int *free_bytes, char **status_text); + + +/* ts A70328 */ +/** Sets a fixed track size after the data source object has already been + created. + @param t The track to operate on + @param size the number of bytes to use as track size + @return <=0 indicates failure , >0 success +*/ +int burn_track_set_size(struct burn_track *t, 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 + Note: "k" is 1000, not 1024. 1xCD = 176.4 k/s, 1xDVD = 1385 k/s. + Fractional speeds should be rounded up. Like 4xCD = 706. + @param d The drive to set speed for + @param read Read speed in k/s (0 is max, -1 is min). + @param write Write speed in k/s (0 is max, -1 is min). +*/ +void burn_drive_set_speed(struct burn_drive *d, int read, int write); + + +/* ts A70711 */ +/** Controls the behavior with writing when the drive buffer is suspected to + be full. To check and wait for enough free buffer space before writing + will move the task of waiting from the operating system's device driver + to libburn. While writing is going on and waiting is enabled, any write + operation will be checked whether it will fill the drive buffer up to + more than max_percent. If so, then waiting will happen until the buffer + fill is predicted with at most min_percent. + Thus: if min_percent < max_percent then transfer rate will oscillate. + This may allow the driver to operate on other devices, e.g. a disk from + which to read the input for writing. On the other hand, this checking might + reduce maximum throughput to the drive or even get misled by faulty buffer + fill replies from the drive. + If a setting parameter is < 0, then this setting will stay unchanged + by the call. + Known burner or media specific pitfalls: + To have max_percent larger than the burner's best reported buffer fill has + the same effect as min_percent==max_percent. Some burners do not report + their full buffer with all media types. Some are not suitable because + they report their buffer fill with delay. + @param d The drive to control + @param enable 0= disable , 1= enable waiting , (-1 = do not change setting) + @param min_usec Shortest possible sleeping period (given in micro seconds) + @param max_usec Longest possible sleeping period (given in micro seconds) + @param timeout_sec If a single write has to wait longer than this number + of seconds, then waiting gets disabled and mindless + writing starts. A value of 0 disables this timeout. + @param min_percent Minimum of desired buffer oscillation: 25 to 100 + @param max_percent Maximum of desired buffer oscillation: 25 to 100 + @return 1=success , 0=failure +*/ +int burn_drive_set_buffer_waiting(struct burn_drive *d, int enable, + int min_usec, int max_usec, int timeout_sec, + int min_percent, int max_percent); + + +/* 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. + Note: write_type BURN_WRITE_SAO is currently not capable of writing a mix + of data and audio tracks. You must use BURN_WRITE_TAO for such sessions. + @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); + + +/* ts A70207 */ +/** As an alternative to burn_write_opts_set_write_type() this function tries + to find a suitable write type and block type for a given write job + described by opts and disc. To be used after all other setups have been + made, i.e. immediately before burn_disc_write(). + @param opts The nearly complete write opts to change + @param disc The already composed session and track model + @param reasons This text string collects reasons for decision resp. failure + @param flag Bitfield for control purposes: + bit0= do not choose type but check the one that is already set + bit1= do not issue error messages via burn_msgs queue + (is automatically set with bit0) + @return Chosen write type. BURN_WRITE_NONE on failure. +*/ +enum burn_write_types burn_write_opts_auto_write_type( + struct burn_write_opts *opts, struct burn_disc *disc, + char reasons[BURN_REASONS_LEN], int flag); + + +/** 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 . + This corresponds to the Test Write bit in MMC mode page 05h. Several media + types do not support this. See struct burn_multi_caps.might_simulate for + actual availability of this feature. + If the media is suitable, the drive will perform burn_write_disc() as a + simulation instead of effective write operations. This means that the + media content and burn_disc_get_status() stay unchanged. + Note: With stdio-drives, the target file gets eventually created, opened, + lseeked, and closed, but not written. So there are effects on it. + Warning: Call burn_random_access_write() will never do simulation because + it does not get any burn_write_opts. + @param opts The write opts to change + @param sim Non-zero enables simulation, 0 enables real writing + @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 opts The option object to be manipulated + @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 (for now: DVD+RW, DVD-RAM, formatted DVD-RW). + 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 supposed to be safe with DVD media. + Call burn_disc_get_multi_caps() can obtain the necessary media info. See + resulting struct burn_multi_caps elements .start_adr , .start_alignment , + .start_range_low , .start_range_high . + @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); + + +/* ts A70213 */ +/** Caution: still immature and likely to change. Problems arose with + sequential DVD-RW on one drive. + + Controls whether the whole available space of the media shall be filled up + by the last track of the last session. + @param opts The write opts to change + @param fill_up_media If 1 : fill up by last track, if 0 = do not fill up +*/ +void burn_write_opts_set_fillup(struct burn_write_opts *opts, + int fill_up_media); + + +/* ts A70303 */ +/** Eventually makes libburn ignore the failure of some conformance checks: + - the check whether CD write+block type is supported by the drive + - the check whether the media profile supports simulated burning + @param opts The write opts to change + @param use_force 1=ignore above checks, 0=refuse work on failed check +*/ +void burn_write_opts_set_force(struct burn_write_opts *opts, int use_force); + + +/** 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 A70713 */ +/** Look up the fastest speed descriptor which is not faster than the given + speed_goal. If it is 0, then the fastest one is chosen among the + descriptors with the highest end_lba. If it is -1 then the slowest speed + descriptor is chosen regardless of end_lba. Parameter flag decides whether + the speed goal means write speed or read speed. + @param d Drive to query + @param speed_goal Upper limit for speed, + 0=search for maximum speed , -1 search for minimum speed + @param best_descr Result of the search, NULL if no match + @param flag Bitfield for control purposes + bit0= look for best read speed rather than write speed + bit1= look for any source type (else look for source==2 first + and for any other source type only with CD media) + @return >0 indicates a valid best_descr, 0 = no valid best_descr +*/ +int burn_drive_get_best_speed(struct burn_drive *d, int speed_goal, + struct burn_speed_descriptor **best_descr, int flag); + + +/* 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); + + +/* ts A70203 */ +/** The reply structure for burn_disc_get_multi_caps() +*/ +struct burn_multi_caps { + + /* Multi-session capability allows to keep the media appendable after + writing a session. It also guarantees that the drive will be able + to predict and use the appropriate Next Writeable Address to place + the next session on the media without overwriting the existing ones. + It does not guarantee that the selected write type is able to do + an appending session after the next session. (E.g. CD SAO is capable + of multi-session by keeping a disc appendable. But .might_do_sao + will be 0 afterwards, when checking the appendable media.) + 1= media may be kept appendable by burn_write_opts_set_multi(o,1) + 0= media will not be appendable + */ + int multi_session; + + /* Multi-track capability allows to write more than one track source + during a single session. The written tracks can later be found in + libburn's TOC model with their start addresses and sizes. + 1= multiple tracks per session are allowed + 0= only one track per session allowed + */ + int multi_track; + + /* Start-address capability allows to set a non-zero address with + burn_write_opts_set_start_byte(). Eventually this has to respect + .start_alignment and .start_range_low, .start_range_high in this + structure. + 1= non-zero start address is allowed + 0= only start address 0 is allowed (to depict the drive's own idea + about the appropriate write start) + */ + int start_adr; + + /** The alignment for start addresses. + ( start_address % start_alignment ) must be 0. + */ + off_t start_alignment; + + /** The lowest permissible start address. + */ + off_t start_range_low; + + /** The highest addressable start address. + */ + off_t start_range_high; + + /** Potential availability of write modes + 4= needs no size prediction, not to be chosen automatically + 3= needs size prediction, not to be chosen automatically + 2= available, no size prediction necessary + 1= available, needs exact size prediction + 0= not available + With CD media (profiles 0x09 and 0x0a) check also the elements + *_block_types of the according write mode. + */ + int might_do_tao; + int might_do_sao; + int might_do_raw; + + /** Generally advised write mode. + Not necessarily the one chosen by burn_write_opts_auto_write_type() + because the burn_disc structure might impose particular demands. + */ + enum burn_write_types advised_write_mode; + + /** Write mode as given by parameter wt of burn_disc_get_multi_caps(). + */ + enum burn_write_types selected_write_mode; + + /** Profile number which was current when the reply was generated */ + int current_profile; + + /** Wether the current profile indicates CD media. 1=yes, 0=no */ + int current_is_cd_profile; + + /* ts A70528, added to version 0.3.7 */ + /** Wether the current profile is able to perform simulated write */ + int might_simulate; +}; + +/** Allocates a struct burn_multi_caps (see above) and fills it with values + which are appropriate for the drive and the loaded media. The drive + must be grabbed for this call. The returned structure has to be disposed + via burn_disc_free_multi_caps() when no longer needed. + @param d The drive to inquire + @param wt With BURN_WRITE_NONE the best capabilities of all write modes + get returned. If set to a write mode like BURN_WRITE_SAO the + capabilities with that particular mode are returned and the + return value is 0 if the desired mode is not possible. + @param caps returns the info structure + @param flag Bitfield for control purposes (unused yet, submit 0) + @return < 0 : error , 0 : writing seems impossible , 1 : writing possible +*/ +int burn_disc_get_multi_caps(struct burn_drive *d, enum burn_write_types wt, + struct burn_multi_caps **caps, int flag); + +/** Removes from memory a multi session info structure which was returned by + burn_disc_get_multi_caps(). The pointer *caps gets set to NULL. + @param caps the info structure to dispose (note: pointer to pointer) + @return 0 : *caps was already NULL, 1 : memory object was disposed +*/ +int burn_disc_free_multi_caps(struct burn_multi_caps **caps); + + +/** 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", "ABORT", "FATAL", "FAILURE", "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", "ABORT", "FATAL", + "FAILURE", "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 A70922 */ +/** Submit a message to the libburn queueing system. It will be queued or + printed as if it was generated by libburn itself. + @param error_code The unique error code of your message. + Submit 0 if you do not have reserved error codes within + the libburnia project. + @param msg_text Not more than BURN_MSGS_MESSAGE_LEN characters of + message text. + @param os_errno Eventual errno related to the message. Submit 0 if + the message is not related to a operating system error. + @param severity One of "ABORT", "FATAL", "FAILURE", "SORRY", "WARNING", + "HINT", "NOTE", "UPDATE", "DEBUG". Defaults to "FATAL". + @param d An eventual drive to which the message shall be related. + Submit NULL if the message is not specific to a + particular drive object. + @return 1 if message was delivered, <=0 if failure +*/ +int burn_msgs_submit(int error_code, char msg_text[], int os_errno, + char severity[], struct burn_drive *d); + + +/* ts A71016 */ +/** Convert a severity name into a severity number, which gives the severity + rank of the name. + @param severity_name A name as with burn_msgs_submit(), e.g. "SORRY". + @param severity_number The rank number: the higher, the more severe. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return >0 success, <=0 failure +*/ +int burn_text_to_sev(char *severity_name, int *severity_number, int flag); + + +/* ts A70915 */ +/** Replace the messenger object handle of libburn by a compatible handle + obtained from a related library. + See also: libisofs, API function iso_get_messenger(). + @param messenger The foreign but compatible message handle. + @return 1 : success, <=0 : failure +*/ +int burn_set_messenger(void *messenger); + + +/* 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); + + +/* ts A70811 */ +/** Write data in random access mode. + The drive must be grabbed successfully before calling this function which + circumvents usual libburn session processing and rather writes data without + preparations or finalizing. This will work only with overwriteable media + which are also suitable for burn_write_opts_set_start_byte(). The same + address alignment restrictions as with this function apply. I.e. for DVD + it is best to align to 32 KiB blocks (= 16 LBA units). The amount of data + to be written is subject to the same media dependent alignment rules. + Again, 32 KiB is most safe. + Call burn_disc_get_multi_caps() can obtain the necessary media info. See + resulting struct burn_multi_caps elements .start_adr , .start_alignment , + .start_range_low , .start_range_high . + Other than burn_disc_write() this is a synchronous call which returns + only after the write transaction has ended (sucessfully or not). So it is + wise not to transfer giant amounts of data in a single call. + Important: Data have to fit into the already formatted area of the media. + @param d The drive to which to write + @param byte_address The start address of the write in byte + (1 LBA unit = 2048 bytes) (do respect media alignment) + @param data The bytes to be written + @param data_count The number of those bytes (do respect media alignment) + data_count == 0 is permitted (e.g. to flush the + drive buffer without further data transfer). + @param flag Bitfield for control purposes: + bit0 = flush the drive buffer after eventual writing + @return 1=sucessful , <=0 : number of tranfered bytes * -1 +*/ +int burn_random_access_write(struct burn_drive *d, off_t byte_address, + char *data, off_t data_count, int flag); + + +/* ts A70812 */ +/** Read data in random access mode. + The drive must be grabbed successfully before calling this function. + With all currently supported drives and media the byte_address has to + be aligned to 2048 bytes. Only data tracks with 2048 bytes per sector + can be read this way. I.e. not CD-audio, not CD-video-stream ... + This is a synchronous call which returns only after the full read job + has ended (sucessfully or not). So it is wise not to read giant amounts + of data in a single call. + @param d The drive to which to write + @param byte_address The start address of the read in byte (aligned to 2048) + @param data A memory buffer capable of taking data_size bytes + @param data_size The amount of data to be read. This does not have to + be aligned to any block size. + @param data_count The amount of data actually read (interesting on error) + @param flag Bitfield for control purposes: + bit0= - reserved - + bit1= do not submit error message if read error + @return 1=sucessful , <=0 an error occured +*/ +int burn_read_data(struct burn_drive *d, off_t byte_address, + char data[], off_t data_size, off_t *data_count, int flag); + + +/* A70904 */ +/** Inquire whether the drive object is a real MMC drive or a pseudo-drive + created by a stdio: address. + @param d The drive to inquire + @return 0= null-drive + 1= real MMC drive + 2= stdio-drive, random access, read-write + 3= stdio-drive, sequential, write-only +*/ +int burn_drive_get_drive_role(struct burn_drive *d); + + +/* ts A70923 */ +/** Find out whether a given address string would lead to the given drive + object. This should be done in advance for track source addresses + with parameter drive_role set to 2. + Although a real MMC drive should hardly exist as two drive objects at + the same time, this can easily happen with stdio-drives. So if more than + one drive is used by the application, then this gesture is advised: + burn_drive_d_get_adr(d2, adr2); + if (burn_drive_equals_adr(d1, adr2, burn_drive_get_drive_role(d2))) + ... Both drive objects point to the same storage facility ... + + @param d1 Existing drive object + @param adr2 Address string to be tested. Prefix "stdio:" overrides + parameter drive_role2 by either 0 or 2 as appropriate. + The string must be shorter than BURN_DRIVE_ADR_LEN. + @param drive_role2 Role as burn_drive_get_drive_role() would attribute + to adr2 if it was a drive. Use value 2 for checking track + sources resp. pseudo-drive addresses without "stdio:". + Use 1 for checking drive addresses including those with + prefix "stdio:". + @return 1= adr2 leads to d1 , 0= adr2 seems not to lead to d1, + -1 = adr2 is bad +*/ +int burn_drive_equals_adr(struct burn_drive *d1, char *adr2, int drive_role2); + + + +#ifndef DOXYGEN + +BURN_END_DECLS + +#endif + +#endif /*LIBBURN_H*/ diff --git a/libburn/branches/ZeroFourTwo/libburn/libdax_audioxtr.c b/libburn/branches/ZeroFourTwo/libburn/libdax_audioxtr.c new file mode 100644 index 00000000..888218ec --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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-1]= 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/branches/ZeroFourTwo/libburn/libdax_audioxtr.h b/libburn/branches/ZeroFourTwo/libburn/libdax_audioxtr.h new file mode 100644 index 00000000..72b62bf3 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/libdax_msgs.c b/libburn/branches/ZeroFourTwo/libburn/libdax_msgs.c new file mode 100644 index 00000000..6c2a1a3b --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/libdax_msgs.c @@ -0,0 +1,431 @@ + +/* libdax_msgs + Message handling facility of libdax. + Copyright (C) 2006 - 2008 Thomas Schmitt <scdbackup@gmx.net>, + provided under GPL version 2 +*/ + +#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->origin= -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 *origin, + int flag) +{ + *timestamp= item->timestamp; + *process_id= item->process_id; + *origin= item->origin; + 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->refcount= 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); +} + + +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_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); + if(o->refcount > 1) { + if(libdax_msgs_lock(*m,0)<=0) + return(-1); + o->refcount--; + libdax_msgs_unlock(*m,0); + *m= NULL; + return(1); + } + +#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_refer(struct libdax_msgs **pt, struct libdax_msgs *m, int flag) +{ + if(libdax_msgs_lock(m,0)<=0) + return(0); + m->refcount++; + *pt= m; + libdax_msgs_unlock(m,0); + return(1); +} + + +int libdax_msgs_set_severities(struct libdax_msgs *m, int queue_severity, + int print_severity, char *print_id, int flag) +{ + if(libdax_msgs_lock(m,0)<=0) + return(0); + m->queue_severity= queue_severity; + m->print_severity= print_severity; + strncpy(m->print_id,print_id,80); + m->print_id[80]= 0; + libdax_msgs_unlock(m,0); + 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,"FAILURE",7)==0) + *severity= LIBDAX_MSGS_SEV_FAILURE; + 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\nFAILURE\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_FAILURE) + *severity_name= "FAILURE"; + 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 origin, 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->origin= origin; + 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/branches/ZeroFourTwo/libburn/libdax_msgs.h b/libburn/branches/ZeroFourTwo/libburn/libdax_msgs.h new file mode 100644 index 00000000..bac0f64d --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/libdax_msgs.h @@ -0,0 +1,582 @@ + +/* libdax_msgs + Message handling facility of libdax. + Copyright (C) 2006-2008 Thomas Schmitt <scdbackup@gmx.net>, + provided under GPL version 2 +*/ + + +/* + *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 origin; + + 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 { + + int refcount; + + 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 + + + /* Architectural aspects */ +/* + libdax_msgs is designed to serve in libraries which want to offer their + applications a way to control the output of library messages. It shall be + incorporated by an owner, i.e. a software entity which encloses the code + of the .c file. + + Owner of libdax_msgs is libburn. A fully compatible variant named libiso_msgs + is owned by libisofs and can get generated by a script of the libburn + project: libburn/libdax_msgs_to_xyz_msgs.sh . + + Reason: One cannot link two owners of the same variant together because + both would offer the same functions to the linker. For that situation one + has to create a compatible variant as it is done for libisofs. + + Compatible variants may get plugged together by call combinations like + burn_set_messenger(iso_get_messenger()); + A new variant would demand a _set_messenger() function if it has to work + with libisofs. If only libburn is planned as link partner then a simple + _get_messenger() does suffice. + Take care to shutdown libburn before its provider of the *_msgs object + gets shut down. + +*/ + + /* 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 the owner of libdax_msgs. +*/ + +/** 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 important parts of an action + failed but processing may go on if one accepts deviations from the + desired result. + + E.g.: One of several libisofs input files cannot be found. + A speed setting cannot be made. + + After SORRY a function should try to go on if that makes any sense + and if no threshold prescribes abort on SORRY. The function should + nevertheless indicate some failure in its return value. + It should - but it does not have to. +*/ +#define LIBDAX_MSGS_SEV_SORRY 0x60000000 + + +/** Non-fatal error indicating that a complete action failed and that + only a thorough new setup of preconditions will give hope for success. + + E.g.: No media is inserted in the output drive. + No write mode can be found for inserted media. + All libisofs input files are inaccessible. + + After FAILURE a function should end very soon with a return value + indicating failure. +*/ +#define LIBDAX_MSGS_SEV_FAILURE 0x68000000 + + +/** An error message which puts the whole operation of the program in question + + E.g.: Not enough memory for essential temporary objects. + Irregular errors from resources. + Programming errors (soft assert). + + After FATAL a function should end very soon with a return value + indicating severe failure. +*/ +#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 selected by the programmers and not by the user. */ + +#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 + + +/* Origin numbers of libburn drives may range from 0 to 1048575 */ +#define LIBDAX_MSGS_ORIGIN_DRIVE_BASE 0 +#define LIBDAX_MSGS_ORIGIN_DRIVE_TOP 0xfffff + +/* Origin numbers of libisofs images may range from 1048575 to 2097152 */ +#define LIBDAX_MSGS_ORIGIN_IMAGE_BASE 0x100000 +#define LIBDAX_MSGS_ORIGIN_IMAGE_TOP 0x1fffff + + + + /* Public Functions */ + + /* Calls initiated from inside the direct owner (e.g. from libburn) */ + + +/** Create new empty message handling facility with queue and issue a first + official reference to it. + @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. + Actually only the last destroy call of all offical references to the object + will really dispose it. All others just decrement the reference counter. + Call this function only with official reference pointers obtained by + libdax_msgs_new() or libdax_msgs_refer(), and only once per such pointer. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 for success, 0 for pointer to NULL, -1 for fatal error +*/ +int libdax_msgs_destroy(struct libdax_msgs **m, int flag); + + +/** Create an official reference to an existing libdax_msgs object. The + references keep the object alive at least until it is released by + a matching number of destroy calls. So each reference MUST be revoked + by exactly one call to libdax_msgs_destroy(). + @param pt The pointer to be set and registered + @param m A pointer to the existing object + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 for success, 0 for failure +*/ +int libdax_msgs_refer(struct libdax_msgs **pt, struct libdax_msgs *o, int flag); + + +/** Submit a message to a message handling facility. + @param origin program specific identification number of the originator of + a message. E.g. drive number. Programs should have an own + range of origin numbers. See above LIBDAX_MSGS_ORIGIN_*_BASE + 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_msgs 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 origin, int error_code, + int severity, int priority, char *msg_text, + int os_errno, int flag); + + + + /* Calls from applications (to be forwarded by direct owner) */ + + +/** 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 *origin, + 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 associated, 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 + 0x00000003 (FATAL,HIGH) = Out of virtual memory + + +------------------------------------------------------------------------------ +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 (SORRY,HIGH) = Failed to open device + 0x00020006 (FATAL,HIGH) = Too many scsi siblings + 0x00020007 (NOTE,HIGH) = Closed O_EXCL scsi siblings + 0x00020008 (SORRY,HIGH) = Device busy. Failed to fcntl-lock + 0x00020009 (SORRY,HIGH) = Neither stdio-path nor its directory exist + + 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 (WARNING,HIGH) = A drive is still busy on shutdown of 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 + 0x0002012a (FATAL,HIGH) = Cannot inquire write mode capabilities + 0x0002012b (FATAL,HIGH) = Drive offers no suitable write mode with this job + 0x0002012c (SORRY,HIGH) = Too many logical tracks recorded + 0x0002012d (FATAL,HIGH) = Exceeding range of permissible write addresses + 0x0002012e (NOTE,HIGH) = Activated track default size + 0x0002012f (SORRY,HIGH) = SAO is restricted to single fixed size session + 0x00020130 (SORRY,HIGH) = Drive and media state unsuitable for blanking + 0x00020131 (SORRY,HIGH) = No suitable formatting type offered by drive + 0x00020132 (SORRY,HIGH) = Selected format is not suitable for libburn + 0x00020133 (SORRY,HIGH) = Cannot mix data and audio in SAO mode + 0x00020134 (NOTE,HIGH) = Defaulted TAO to DAO + 0x00020135 (SORRY,HIGH) = Cannot perform TAO, job unsuitable for DAO + 0x00020136 (SORRY,HIGH) = DAO burning restricted to single fixed size track + 0x00020137 (HINT,HIGH) = TAO would be possible + 0x00020138 (FATAL,HIGH) = Cannot reserve track + 0x00020139 (SORRY,HIGH) = Write job parameters are unsuitable + 0x0002013a (FATAL,HIGH) = No suitable media detected + 0x0002013b (DEBUG,HIGH) = SCSI command indicates host or driver error + 0x0002013c (SORRY,HIGH) = Malformed capabilities page 2Ah received + 0x0002013d (DEBUG,LOW) = Waiting for free buffer space takes long time + 0x0002013e (SORRY,HIGH) = Timeout with waiting for free buffer. Now disabled + 0x0002013f (DEBUG,LOW) = Reporting total time spent with waiting for buffer + 0x00020140 (FATAL,HIGH) = Drive is busy on attempt to write random access + 0x00020141 (SORRY,HIGH) = Write data count not properly aligned + 0x00020142 (FATAL,HIGH) = Drive is not grabbed on random access write + 0x00020143 (SORRY,HIGH) = Read start address not properly aligned + 0x00020144 (SORRY,HIGH) = SCSI error on read + 0x00020145 (FATAL,HIGH) = Drive is busy on attempt to read data + 0x00020146 (FATAL,HIGH) = Drive is a virtual placeholder + 0x00020147 (SORRY,HIGH) = Cannot address start byte + 0x00020148 (SORRY,HIGH) = Cannot write desired amount of data + 0x00020149 (SORRY,HIGH) = Unsuitable filetype for pseudo-drive + 0x0002014a (SORRY,HIGH) = Cannot read desired amount of data + 0x0002014b (SORRY,HIGH) = Drive is already registered resp. scanned + 0x0002014c (FATAL,HIGH) = Emulated drive caught in SCSI function + 0x0002014d (SORRY,HIGH) = Asynchromous SCSI error + 0x0002014f (SORRY,HIGH) = Timeout with asynchromous SCSI command + 0x00020150 (DEBUG,LOW) = Reporting asynchronous waiting time + 0x00020151 (FATAL,HIGH) = Read attempt on write-only drive + 0x00020152 (FATAL,HIGH) = Cannot start fifo thread + 0x00020153 (SORRY,HIGH) = Read error on fifo input + 0x00020154 (NOTE,HIGH) = Forwarded input error ends output + 0x00020155 (SORRY,HIGH) = Desired fifo buffer too large + 0x00020156 (SORRY,HIGH) = Desired fifo buffer too small + 0x00020157 (FATAL,HIGH) = burn_source is not a fifo object + 0x00020158 (DEBUG,LOW) = Reporting thread disposal precautions + 0x00020159 (DEBUG,HIGH) = TOC Format 0 returns inconsistent data + + 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 + + + +------------------------------------------------------------------------------ +Range "vreixo" : 0x00030000 to 0x0003ffff + + General: + 0x00031001 (SORRY,HIGH) = Cannot read file (ignored) + 0x00031002 (FATAL,HIGH) = Cannot read file (operation canceled) + + Image reading: + 0x00031000 (FATAL,HIGH) = Unsupported ISO-9660 image + 0x00031001 (HINT,MEDIUM) = Unsupported Vol Desc that will be ignored + 0x00031002 (FATAL,HIGH) = Damaged ISO-9660 image + 0x00031003 (SORRY,HIGH) = Cannot read previous image file + + Rock-Ridge: + 0x00030101 (HINT,MEDIUM) = Unsupported SUSP entry that will be ignored + 0x00030102 (SORRY,HIGH) = Wrong/damaged SUSP entry + 0x00030103 (WARNING,MEDIUM)= Multiple SUSP ER entries where found + 0x00030111 (SORRY,HIGH) = Unsupported RR feature + 0x00030112 (SORRY,HIGH) = Error in a Rock Ridge entry + + El-Torito: + 0x00030201 (HINT,MEDIUM) = Unsupported Boot Vol Desc that will be ignored + 0x00030202 (SORRY,HIGH) = Wrong El-Torito catalog + 0x00030203 (HINT,MEDIUM) = Unsupported El-Torito feature + 0x00030204 (SORRY,HIGH) = Invalid file to be an El-Torito image + 0x00030205 (WARNING,MEDIUM)= Cannot properly patch isolinux image + 0x00030206 (WARNING,MEDIUM)= Copying El-Torito from a previous image without + enought info about it + + Joliet: + 0x00030301 (NOTE,MEDIUM) = Unsupported file type for Joliet tree + + +------------------------------------------------------------------------------ +Range "application" : 0x00040000 to 0x0004ffff + + 0x00040000 (ABORT,HIGH) : Application supplied message + 0x00040001 (FATAL,HIGH) : Application supplied message + 0x00040002 (SORRY,HIGH) : Application supplied message + 0x00040003 (WARNING,HIGH) : Application supplied message + 0x00040004 (HINT,HIGH) : Application supplied message + 0x00040005 (NOTE,HIGH) : Application supplied message + 0x00040006 (UPDATE,HIGH) : Application supplied message + 0x00040007 (DEBUG,HIGH) : Application supplied message + + +------------------------------------------------------------------------------ + +#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/branches/ZeroFourTwo/libburn/libdax_msgs_to_xyz_msgs.sh b/libburn/branches/ZeroFourTwo/libburn/libdax_msgs_to_xyz_msgs.sh new file mode 100755 index 00000000..46f68b14 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/libdax_msgs_to_xyz_msgs.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +# libdax_msgs_to_iso_msgs.sh +# generates ${xyz}_msgs.[ch] from libdax_msgs.[ch] +# To be executed within ./libburn-* resp ./cdrskin-* + +# The module name for the generated sourcecode in several +# uppercase-lowercase forms +xyz="libiso" +Xyz="Libiso" +XYZ="LIBISO" + +# The project name for which the generated code shall serve +project="libisofs" + + +for i in libburn/libdax_msgs.[ch] +do + target_adr=$(echo "$i" | sed -e "s/libdax_/${xyz}_/") + + echo "$target_adr" + + sed \ + -e "s/^\/\* libdax_msgs/\/* ${xyz}_msgs (generated from XYZ_msgs : $(date))/" \ + -e "s/Message handling facility of libdax/Message handling facility of ${project}/" \ + -e "s/libdax_/${xyz}_/g" \ + -e "s/libdax:/${xyz}:/g" \ + -e "s/Libdax_/${Xyz}_/g" \ + -e "s/LIBDAX_/${XYZ}_/g" \ + -e "s/generated from XYZ_msgs/generated from libdax_msgs/" \ + -e "s/${xyz}_msgs is designed to serve in libraries/libdax_msgs is designed to serve in libraries/" \ + -e "s/Owner of ${xyz}_msgs is libburn/Owner of libdax_msgs is libburn/" \ + \ + <"$i" >"$target_adr" + +done + diff --git a/libburn/branches/ZeroFourTwo/libburn/mmc.c b/libburn/branches/ZeroFourTwo/libburn/mmc.c new file mode 100644 index 00000000..1a9b6235 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/mmc.c @@ -0,0 +1,3137 @@ +/* -*- 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 <sys/time.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" + + +/* ts A70223 : in init.c */ +extern int burn_support_untested_profiles; + + +#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 + +/* ts A70129 */ +#define Libburn_support_dvd_r_seQ 1 + +/* ts A70306 */ +#define Libburn_support_dvd_plus_R 1 + +/* DVD progress report: + 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. + ts A70130 : Burned a first non-multi sequential DVD-RW. Feature 0021h + Incremental Recording vanishes after that and media thus gets + not recognized as suitable any more. + After a run with -multi another disc still offers 0021h . + dvd+rw-mediainfo shows two tracks. The second, an afio archive + is readable by afio. Third and forth veryfy too. Suddenly + dvd+rw-mediainfo sees lba 0 with track 2. But #2 still verifies + if one knows its address. + ts A70203 : DVD-RW need to get blanked fully. Then feature 0021h persists. + Meanwhile Incremental streaming is supported like CD TAO: + with unpredicted size, multi-track, multi-session. + ts A70205 : Beginning to implement DVD-R[W] DAO : single track and session, + size prediction mandatory. + ts A70208 : Finally made tests with DVD-R. Worked exactly as new DVD-RW. + ts A70306 : Implemented DVD+R (always -multi for now) + ts A70330 : Allowed finalizing of DVD+R. +*/ + +/* ts A70519 : With MMC commands of data direction FROM_DRIVE: + Made struct command.dxfer_len equal to Allocation Length + of MMC commands. Made sure that not more bytes are allowed + for transfer than there are available. +*/ + + +/* ts A70711 Trying to keep writing from clogging the SCSI driver due to + full buffer at burner drive: 0=waiting disabled, 1=enabled + These are only defaults which can be overwritten by + burn_drive_set_buffer_waiting() +*/ +#define Libburn_wait_for_buffer_freE 0 +#define Libburn_wait_for_buffer_min_useC 10000 +#define Libburn_wait_for_buffer_max_useC 100000 +#define Libburn_wait_for_buffer_tio_seC 120 +#define Libburn_wait_for_buffer_min_perC 65 +#define Libburn_wait_for_buffer_max_perC 95 + + +static unsigned char MMC_GET_MSINFO[] = + { 0x43, 0, 1, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_GET_TOC[] = { 0x43, 2, 2, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_GET_TOC_FMT0[] = { 0x43, 0, 0, 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}; + +/* ts A70205 : To describe the layout of a DVD-R[W] DAO session */ +static unsigned char MMC_RESERVE_TRACK[] = + { 0x53, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +/* ts A70812 : Read data sectors (for types with 2048 bytes/sector only) */ +static unsigned char MMC_READ_10[] = + { 0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + +static int mmc_function_spy_do_tell = 0; + +int mmc_function_spy(struct burn_drive *d, char * text) +{ + if (mmc_function_spy_do_tell) + fprintf(stderr,"libburn: experimental: mmc_function_spy: %s\n", + text); + if (d == NULL) + return 1; + if (d->drive_role != 1) { + char msg[4096]; + + sprintf(msg, "Emulated drive caught in SCSI adapter \"%s\"", + text); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002014c, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + d->cancel = 1; + return 0; + } + return 1; +} + +int mmc_function_spy_ctrl(int do_tell) +{ + mmc_function_spy_do_tell= !!do_tell; + return 1; +} + + +/* ts A70201 */ +int mmc_four_char_to_int(unsigned char *data) +{ + return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; +} + + +/* ts A70201 */ +int mmc_int_to_four_char(unsigned char *data, int num) +{ + data[0] = (num >> 24) & 0xff; + data[1] = (num >> 16) & 0xff; + data[2] = (num >> 8) & 0xff; + data[3] = num & 0xff; + return 1; +} + + +void mmc_send_cue_sheet(struct burn_drive *d, struct cue_sheet *s) +{ + struct buffer buf; + struct command c; + + + if (mmc_function_spy(d, "mmc_send_cue_sheet") <= 0) + return; + + scsi_init_command(&c, MMC_SEND_CUE_SHEET, sizeof(MMC_SEND_CUE_SHEET)); +/* + c.oplen = sizeof(MMC_SEND_CUE_SHEET); + memcpy(c.opcode, MMC_SEND_CUE_SHEET, sizeof(MMC_SEND_CUE_SHEET)); +*/ + c.retry = 1; + 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 A70205 : Announce size of a DVD-R[W] DAO session. + @param size The size in bytes to be announced to the drive. + It will get rounded up to align to 32 KiB. +*/ +int mmc_reserve_track(struct burn_drive *d, off_t size) +{ + struct command c; + int lba; + char msg[80]; + + if (mmc_function_spy(d, "mmc_reserve_track") <= 0) + return 0; + + scsi_init_command(&c, MMC_RESERVE_TRACK, sizeof(MMC_RESERVE_TRACK)); +/* + c.oplen = sizeof(MMC_RESERVE_TRACK); + memcpy(c.opcode, MMC_RESERVE_TRACK, sizeof(MMC_RESERVE_TRACK)); +*/ + c.retry = 1; + /* Round to 32 KiB and divide by 2048 + (by nice binary rounding trick learned from dvd+rw-tools) */ + lba = ((size + (off_t) 0x7fff) >> 11) & ~0xf; + mmc_int_to_four_char(c.opcode+5, lba); + + sprintf(msg, "reserving track of %d blocks", lba); + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); + return !c.error; +} + + +/* ts A70201 : + Common track info fetcher for mmc_get_nwa() and mmc_fake_toc() +*/ +int mmc_read_track_info(struct burn_drive *d, int trackno, struct buffer *buf, + int alloc_len) +{ + struct command c; + + if (mmc_function_spy(d, "mmc_read_track_info") <= 0) + return 0; + + scsi_init_command(&c, MMC_TRACK_INFO, sizeof(MMC_TRACK_INFO)); +/* + c.oplen = sizeof(MMC_TRACK_INFO); + memcpy(c.opcode, MMC_TRACK_INFO, sizeof(MMC_TRACK_INFO)); +*/ + c.dxfer_len = alloc_len; + c.opcode[7] = (c.dxfer_len >> 8) & 0xff; + c.opcode[8] = c.dxfer_len & 0xff; + c.retry = 1; + 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 */ + trackno = 1; + else if (d->current_profile == 0x10 || + d->current_profile == 0x11 || + d->current_profile == 0x14 || + d->current_profile == 0x15) + /* DVD-ROM , DVD-R[W] Sequential */ + trackno = d->last_track_no; + else /* mmc5r03c.pdf: valid only for CD, DVD+R, DVD+R DL */ + trackno = 0xFF; + } + mmc_int_to_four_char(c.opcode + 2, trackno); + c.page = buf; + memset(buf->data, 0, BUFFER_SIZE); + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + if (c.error) + return 0; + return 1; +} + + +/* ts A61110 : added parameters trackno, lba, nwa. Redefined return value. + @return 1=nwa is valid , 0=nwa is not valid , -1=error */ +/* ts A70201 : outsourced 52h READ TRACK INFO command */ +int mmc_get_nwa(struct burn_drive *d, int trackno, int *lba, int *nwa) +{ + struct buffer buf; + int ret, num, alloc_len = 20; + unsigned char *data; + + if (mmc_function_spy(d, "mmc_get_nwa") <= 0) + return -1; + + ret = mmc_read_track_info(d, trackno, &buf, alloc_len); + if (ret <= 0) + return ret; + data = buf.data; + *lba = mmc_four_char_to_int(data + 8); + *nwa = mmc_four_char_to_int(data + 12); + num = mmc_four_char_to_int(data + 16); + if (d->current_profile == 0x1a || d->current_profile == 0x13 || + d->current_profile == 0x12) { + /* overwriteable */ + *lba = *nwa = num = 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; + } + if (num > 0) { + d->media_capacity_remaining = ((off_t) num) * ((off_t) 2048); + d->media_lba_limit = *nwa + num; + } else + d->media_lba_limit = 0; + +/* + fprintf(stderr, "LIBBURN_DEBUG: media_lba_limit= %d\n", + d->media_lba_limit); +*/ + + 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 = o->drive; + + if (mmc_function_spy(d, "mmc_close_disc") <= 0) + return; + + 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); */ + + 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 = o->drive; + + if (mmc_function_spy(d, "mmc_close_session") <= 0) + return; + + 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); */ + + o->multi = 3; + spc_select_write_params(d, o); + mmc_close(d, 1, 0); +} + +/* ts A70227 : extended meaning of session to address all possible values + of 5Bh CLOSE TRACK SESSION to address any Close Function. + @param session contains the two high bits of Close Function + @param track if not 0: sets the lowest bit of Close Function +*/ +void mmc_close(struct burn_drive *d, int session, int track) +{ + struct command c; + + if (mmc_function_spy(d, "mmc_close") <= 0) + return; + + scsi_init_command(&c, MMC_CLOSE, sizeof(MMC_CLOSE)); +/* + c.oplen = sizeof(MMC_CLOSE); + memcpy(c.opcode, MMC_CLOSE, sizeof(MMC_CLOSE)); +*/ + c.retry = 1; + + c.opcode[1] |= 1; /* ts A70918 : Immed */ + + /* (ts A61030 : shifted !!session rather than or-ing plain session ) */ + c.opcode[2] = ((session & 3) << 1) | !!track; + c.opcode[4] = track >> 8; + c.opcode[5] = track & 0xFF; + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); + + /* ts A70918 : Immed : wait for drive to complete command */ + if (c.error) { + d->cancel = 1; + return; + } + if (spc_wait_unit_attention(d, 3600, "CLOSE TRACK SESSION", 0) <= 0) + d->cancel = 1; +} + +void mmc_get_event(struct burn_drive *d) +{ + struct buffer buf; + struct command c; + int alloc_len= 8; + + if (mmc_function_spy(d, "mmc_get_event") <= 0) + return; + + scsi_init_command(&c, MMC_GET_EVENT, sizeof(MMC_GET_EVENT)); +/* + c.oplen = sizeof(MMC_GET_EVENT); + memcpy(c.opcode, MMC_GET_EVENT, sizeof(MMC_GET_EVENT)); +*/ + c.dxfer_len = alloc_len; + c.opcode[7] = (c.dxfer_len >> 8) & 0xff; + c.opcode[8] = c.dxfer_len & 0xff; + c.retry = 1; + 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]); +} + + +/* ts A70711 + This has become a little monster because of the creative buffer reports of + my LG GSA-4082B : Belated, possibly statistically dampened. But only with + DVD media. With CD it is ok. +*/ +static int mmc_wait_for_buffer_free(struct burn_drive *d, struct buffer *buf) +{ + int usec= 0, need, reported_3s = 0, first_wait = 1; + struct timeval t0,tnow; + struct timezone dummy_tz; + double max_fac, min_fac, waiting; + +/* Enable to get reported waiting activities and total time. +#define Libburn_mmc_wfb_debuG 1 +*/ +#ifdef Libburn_mmc_wfb_debuG + char sleeplist[32768]; + static int buffer_still_invalid = 1; +#endif + + max_fac = ((double) d->wfb_max_percent) / 100.0; + + /* Buffer info from the drive is valid only after writing has begun. + Caring for buffer space makes sense mostly after max_percent of the + buffer was transmitted. */ + if (d->progress.buffered_bytes <= 0 || + d->progress.buffer_capacity <= 0 || + d->progress.buffered_bytes + buf->bytes <= + d->progress.buffer_capacity * max_fac) + return 2; + +#ifdef Libburn_mmc_wfb_debuG + if (buffer_still_invalid) + fprintf(stderr, + "\nLIBBURN_DEBUG: Buffer considered valid now\n"); + buffer_still_invalid = 0; +#endif + + /* The pessimistic counter does not assume any buffer consumption */ + if (d->pessimistic_buffer_free - buf->bytes >= + ( 1.0 - max_fac) * d->progress.buffer_capacity) + return 1; + + /* There is need to inquire the buffer fill */ + d->pessimistic_writes++; + min_fac = ((double) d->wfb_min_percent) / 100.0; + gettimeofday(&t0, &dummy_tz); +#ifdef Libburn_mmc_wfb_debuG + sleeplist[0]= 0; + sprintf(sleeplist,"(%d%s %d)", + (int) (d->pessimistic_buffer_free - buf->bytes), + (d->pbf_altered ? "? -" : " -"), + (int) ((1.0 - max_fac) * d->progress.buffer_capacity)); +#endif + + while (1) { + if ((!first_wait) || d->pbf_altered) { + d->pbf_altered = 1; + mmc_read_buffer_capacity(d); + } +#ifdef Libburn_mmc_wfb_debuG + if(strlen(sleeplist) < sizeof(sleeplist) - 80) + sprintf(sleeplist+strlen(sleeplist)," (%d%s %d)", + (int) (d->pessimistic_buffer_free - buf->bytes), + (d->pbf_altered ? "? -" : " -"), + (int) ((1.0 - min_fac) * d->progress.buffer_capacity)); +#endif + gettimeofday(&tnow,&dummy_tz); + waiting = (tnow.tv_sec - t0.tv_sec) + + ((double) (tnow.tv_usec - t0.tv_usec)) / 1.0e6; + if (d->pessimistic_buffer_free - buf->bytes >= + (1.0 - min_fac) * d->progress.buffer_capacity) { +#ifdef Libburn_mmc_wfb_debuG + if(strlen(sleeplist) >= sizeof(sleeplist) - 80) + strcat(sleeplist," ..."); + sprintf(sleeplist+strlen(sleeplist)," -> %d [%.6f]", + (int) ( + d->pessimistic_buffer_free - buf->bytes - + (1.0 - min_fac) * d->progress.buffer_capacity + ), waiting); + fprintf(stderr, + "\nLIBBURN_DEBUG: sleeplist= %s\n",sleeplist); +#endif + return 1; + } + + /* Waiting is needed */ + if (waiting >= 3 && !reported_3s) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002013d, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_LOW, + "Waiting for free buffer takes more than 3 seconds", + 0,0); + reported_3s = 1; + } else if (d->wfb_timeout_sec > 0 && + waiting > d->wfb_timeout_sec) { + d->wait_for_buffer_free = 0; + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002013d, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Timeout with waiting for free buffer. Now disabled.", + 0,0); + break; + } + + need = (1.0 - min_fac) * d->progress.buffer_capacity + + buf->bytes - d->pessimistic_buffer_free; + usec = 0; + if (d->nominal_write_speed > 0) + usec = ((double) need) / 1000.0 / + ((double) d->nominal_write_speed) * 1.0e6; + else + usec = d->wfb_min_usec * 2; + + /* >>> learn about buffer progress and adjust usec */ + + if (usec < d->wfb_min_usec) + usec = d->wfb_min_usec; + else if (usec > d->wfb_max_usec) + usec = d->wfb_max_usec; + usleep(usec); + if (d->waited_usec < 0xf0000000) + d->waited_usec += usec; + d->waited_tries++; + if(first_wait) + d->waited_writes++; +#ifdef Libburn_mmc_wfb_debuG + if(strlen(sleeplist) < sizeof(sleeplist) - 80) + sprintf(sleeplist+strlen(sleeplist)," %d", usec); +#endif + first_wait = 0; + } + return 0; +} + + +void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf) +{ + struct command c; + int len; + + if (mmc_function_spy(d, "mmc_write_12") <= 0) + return; + + 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); + + scsi_init_command(&c, MMC_WRITE_12, sizeof(MMC_WRITE_12)); +/* + memcpy(c.opcode, MMC_WRITE_12, sizeof(MMC_WRITE_12)); + c.oplen = sizeof(MMC_WRITE_12); +*/ + c.retry = 1; + mmc_int_to_four_char(c.opcode + 2, start); + mmc_int_to_four_char(c.opcode + 6, len); + c.page = buf; + c.dir = TO_DRIVE; + + d->issue_command(d, &c); + + /* ts A70711 */ + d->pessimistic_buffer_free -= buf->bytes; + d->pbf_altered = 1; +} + +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 */ + + if (mmc_function_spy(d, "mmc_write") <= 0) + return BE_CANCELLED; + + cancelled = d->cancel; + if (cancelled) + return BE_CANCELLED; + + /* ts A70215 */ + if (d->media_lba_limit > 0 && start >= d->media_lba_limit) { + char msg[160]; + + sprintf(msg, + "Exceeding range of permissible write addresses (%d >= %d)", + start, d->media_lba_limit); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002012d, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + d->cancel = 1; /* No need for mutexing because atomic */ + 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); + + /* ts A70711 */ + if(d->wait_for_buffer_free) + mmc_wait_for_buffer_free(d, buf); + + scsi_init_command(&c, MMC_WRITE_10, sizeof(MMC_WRITE_10)); +/* + memcpy(c.opcode, MMC_WRITE_10, sizeof(MMC_WRITE_10)); + c.oplen = sizeof(MMC_WRITE_10); +*/ + c.retry = 1; + mmc_int_to_four_char(c.opcode + 2, start); + 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 A70711 */ + d->pessimistic_buffer_free -= buf->bytes; + d->pbf_altered = 1; + + /* 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); + } + d->cancel = 1; + return BE_CANCELLED; + } + + return 0; +} + + +/* ts A70201 : Set up an entry for mmc_fake_toc() */ +int mmc_fake_toc_entry(struct burn_toc_entry *entry, int session_number, + int track_number, + unsigned char *size_data, unsigned char *start_data) +{ + int min, sec, frames, num; + + /* mark DVD extensions as valid */ + entry->extensions_valid |= 1; + + /* defaults are as of mmc5r03.pdf 6.26.3.2.4 Fabricated TOC */ + entry->session = session_number & 0xff; + entry->session_msb = (session_number >> 8) & 0xff; + entry->adr = 1; + entry->control = 4; + entry->tno = 0; + entry->point = track_number & 0xff; + entry->point_msb = (track_number >> 8) & 0xff; + num = mmc_four_char_to_int(size_data); + entry->track_blocks = num; + burn_lba_to_msf(num, &min, &sec, &frames); + if (min > 255) { + min = 255; + sec = 255; + frames = 255; + } + entry->min = min; + entry->sec = sec; + entry->frame = frames; + entry->zero = 0; + num = mmc_four_char_to_int(start_data); + entry->start_lba = num; + burn_lba_to_msf(num, &min, &sec, &frames); + if (min > 255) { + min = 255; + sec = 255; + frames = 255; + } + entry->pmin = min; + entry->psec = sec; + entry->pframe = frames; + return 1; +} + + +/* ts A71128 : for DVD-ROM drives which offer no reliable track information */ +static int mmc_read_toc_fmt0_al(struct burn_drive *d, int *alloc_len) +{ + struct burn_track *track; + struct burn_session *session; + struct burn_toc_entry *entry; + struct buffer buf; + struct command c; + int dlen, i, old_alloc_len, session_number, prev_session = -1; + int lba, size; + unsigned char *tdata, size_data[4], start_data[4]; + + if (*alloc_len < 4) + return 0; + + scsi_init_command(&c, MMC_GET_TOC_FMT0, sizeof(MMC_GET_TOC_FMT0)); + c.dxfer_len = *alloc_len; + c.opcode[7] = (c.dxfer_len >> 8) & 0xff; + c.opcode[8] = c.dxfer_len & 0xff; + c.retry = 1; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + if (c.error) { +err_ex:; + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002010d, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + "Could not inquire TOC", 0,0); + d->status = BURN_DISC_UNSUITABLE; + d->toc_entries = 0; + /* Prefering memory leaks over fandangos */ + d->toc_entry = calloc(1, sizeof(struct burn_toc_entry)); + return 0; + } + dlen = c.page->data[0] * 256 + c.page->data[1]; + old_alloc_len = *alloc_len; + *alloc_len = dlen + 2; + if (old_alloc_len < 12) + return 1; + if (dlen + 2 > old_alloc_len) + dlen = old_alloc_len - 2; + d->complete_sessions = 1 + c.page->data[3] - c.page->data[2]; + d->last_track_no = d->complete_sessions; + if (dlen - 2 < (d->last_track_no + 1) * 8) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020159, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + "TOC Format 0 returns inconsistent data", 0,0); + goto err_ex; + } + + d->toc_entries = d->last_track_no + d->complete_sessions; + if (d->toc_entries < 1) + return 0; + d->toc_entry = calloc(d->toc_entries, sizeof(struct burn_toc_entry)); + if(d->toc_entry == NULL) + return 0; + + d->disc = burn_disc_create(); + if (d->disc == NULL) + return 0; + for (i = 0; i < d->complete_sessions; i++) { + session = burn_session_create(); + if (session == NULL) + return 0; + burn_disc_add_session(d->disc, session, BURN_POS_END); + burn_session_free(session); + } + + + for (i = 0; i < d->last_track_no; i++) { + tdata = c.page->data + 4 + i * 8; + session_number = i + 1; + if (session_number != prev_session && prev_session > 0) { + /* leadout entry previous session */ + entry = &(d->toc_entry[(i - 1) + prev_session]); + lba = mmc_four_char_to_int(start_data) + + mmc_four_char_to_int(size_data); + mmc_int_to_four_char(start_data, lba); + mmc_int_to_four_char(size_data, 0); + mmc_fake_toc_entry(entry, prev_session, 0xA2, + size_data, start_data); + entry->min= entry->sec= entry->frame= 0; + d->disc->session[prev_session - 1]->leadout_entry = + entry; + } + + /* ??? >>> d->media_capacity_remaining , d->media_lba_limit + as of mmc_fake_toc() + */ + + entry = &(d->toc_entry[i + session_number - 1]); + track = burn_track_create(); + if (track == NULL) + return -1; + burn_session_add_track( + d->disc->session[session_number - 1], + track, BURN_POS_END); + track->entry = entry; + burn_track_free(track); + + memcpy(start_data, tdata + 4, 4); + /* size_data are estimated from next track start */ + memcpy(size_data, tdata + 8 + 4, 4); + size = mmc_four_char_to_int(size_data) - + mmc_four_char_to_int(start_data); + mmc_int_to_four_char(size_data, size); + mmc_fake_toc_entry(entry, session_number, i + 1, + size_data, start_data); + if (prev_session != session_number) + d->disc->session[session_number - 1]->firsttrack = i+1; + d->disc->session[session_number - 1]->lasttrack = i+1; + prev_session = session_number; + } + if (prev_session > 0 && prev_session <= d->disc->sessions) { + /* leadout entry of last session of closed disc */ + tdata = c.page->data + 4 + d->last_track_no * 8; + entry = &(d->toc_entry[(d->last_track_no - 1) + prev_session]); + memcpy(start_data, tdata + 4, 4); + mmc_int_to_four_char(size_data, 0); + mmc_fake_toc_entry(entry, prev_session, 0xA2, + size_data, start_data); + entry->min= entry->sec= entry->frame= 0; + d->disc->session[prev_session - 1]->leadout_entry = entry; + } + return 1; +} + + +/* ts A71128 : for DVD-ROM drives which offer no reliable track information */ +static int mmc_read_toc_fmt0(struct burn_drive *d) +{ + int alloc_len = 4, ret; + + if (mmc_function_spy(d, "mmc_read_toc_fmt0") <= 0) + return -1; + ret = mmc_read_toc_fmt0_al(d, &alloc_len); + if (alloc_len >= 12) + ret = mmc_read_toc_fmt0_al(d, &alloc_len); + return ret; +} + + +/* ts A70131 : compose a disc TOC structure from d->complete_sessions + and 52h READ TRACK INFORMATION */ +int mmc_fake_toc(struct burn_drive *d) +{ + struct burn_track *track; + struct burn_session *session; + struct burn_toc_entry *entry; + struct buffer buf; + int i, session_number, prev_session = -1, ret, lba, alloc_len = 34; + unsigned char *tdata, size_data[4], start_data[4]; + char msg[160]; + + if (mmc_function_spy(d, "mmc_fake_toc") <= 0) + return -1; + + if (d->last_track_no <= 0 || d->complete_sessions <= 0 || + d->status == BURN_DISC_BLANK) + return 2; + if (d->last_track_no > BURN_MMC_FAKE_TOC_MAX_SIZE) { + sprintf(msg, + "Too many logical tracks recorded (%d , max. %d)\n", + d->last_track_no, BURN_MMC_FAKE_TOC_MAX_SIZE); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002012c, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + return 0; + } + /* ts A71128 : My DVD-ROM drive issues no reliable track info. + One has to try 43h READ TOC/PMA/ATIP Form 0. */ + if (d->current_profile == 0x10 && d->last_track_no <= 1) { + ret = mmc_read_toc_fmt0(d); + return ret; + } + d->disc = burn_disc_create(); + if (d->disc == NULL) + return -1; + d->toc_entries = d->last_track_no + d->complete_sessions; + d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry)); + if (d->toc_entry == NULL) + return -1; + memset(d->toc_entry, 0,d->toc_entries * sizeof(struct burn_toc_entry)); + for (i = 0; i < d->complete_sessions; i++) { + session = burn_session_create(); + if (session == NULL) + return -1; + burn_disc_add_session(d->disc, session, BURN_POS_END); + burn_session_free(session); + } + memset(size_data, 0, 4); + memset(start_data, 0, 4); + + + /* Entry Layout : + session 1 track 1 entry 0 + ... + session 1 track N entry N-1 + leadout 1 entry N + session 2 track N+1 entry N+1 + ... + session 2 track M+1 entry M+1 + leadout 2 entry M+2 + session X track K entry (K-1)+(X-1) + ... + session X track i+1 entry i+(X-1) + leadout X entry i+X + */ + for (i = 0; i < d->last_track_no; i++) { + ret = mmc_read_track_info(d, i+1, &buf, alloc_len); + if (ret <= 0) + return ret; + tdata = buf.data; + session_number = (tdata[33] << 8) | tdata[3]; + if (session_number <= 0) + continue; + + if (session_number != prev_session && prev_session > 0) { + /* leadout entry previous session */ + entry = &(d->toc_entry[(i - 1) + prev_session]); + lba = mmc_four_char_to_int(start_data) + + mmc_four_char_to_int(size_data); + mmc_int_to_four_char(start_data, lba); + mmc_int_to_four_char(size_data, 0); + mmc_fake_toc_entry(entry, prev_session, 0xA2, + size_data, start_data); + entry->min= entry->sec= entry->frame= 0; + d->disc->session[prev_session - 1]->leadout_entry = + entry; + } + + if (session_number > d->disc->sessions) { + if (i == d->last_track_no - 1) { + /* ts A70212 : Last track field Free Blocks */ + d->media_capacity_remaining = + ((off_t) mmc_four_char_to_int(tdata + 16)) * + ((off_t) 2048); + d->media_lba_limit = 0; + } + continue; + } + + entry = &(d->toc_entry[i + session_number - 1]); + track = burn_track_create(); + if (track == NULL) + return -1; + burn_session_add_track( + d->disc->session[session_number - 1], + track, BURN_POS_END); + track->entry = entry; + burn_track_free(track); + + memcpy(size_data, tdata + 24, 4); + memcpy(start_data, tdata + 8, 4); + mmc_fake_toc_entry(entry, session_number, i + 1, + size_data, start_data); + + if (prev_session != session_number) + d->disc->session[session_number - 1]->firsttrack = i+1; + d->disc->session[session_number - 1]->lasttrack = i+1; + prev_session = session_number; + } + if (prev_session > 0 && prev_session <= d->disc->sessions) { + /* leadout entry of last session of closed disc */ + entry = &(d->toc_entry[(d->last_track_no - 1) + prev_session]); + lba = mmc_four_char_to_int(start_data) + + mmc_four_char_to_int(size_data); + mmc_int_to_four_char(start_data, lba); + mmc_int_to_four_char(size_data, 0); + mmc_fake_toc_entry(entry, prev_session, 0xA2, + size_data, start_data); + entry->min= entry->sec= entry->frame= 0; + d->disc->session[prev_session - 1]->leadout_entry = entry; + } + return 1; +} + + +static int mmc_read_toc_al(struct burn_drive *d, int *alloc_len) +{ +/* read full toc, all sessions, in m/s/f form, 4k buffer */ +/* ts A70201 : or fake a toc from track information */ + struct burn_track *track; + struct burn_session *session; + struct buffer buf; + struct command c; + int dlen; + int i, bpl= 12, old_alloc_len; + unsigned char *tdata; + + if (*alloc_len < 4) + return 0; + + if (!(d->current_profile == -1 || d->current_is_cd_profile)) { + /* ts A70131 : MMC_GET_TOC uses Response Format 2 + For DVD this fails with 5,24,00 */ + /* mmc_read_toc_fmt0() uses + Response Format 0: mmc5r03.pdf 6.26.3.2 + which does not yield the same result with the same disc + on different drives. + */ + /* ts A70201 : + This uses the session count from 51h READ DISC INFORMATION + and the track records from 52h READ TRACK INFORMATION. + mmc_read_toc_fmt0() is used as fallback for dull DVD-ROM. + */ + mmc_fake_toc(d); + + if (d->status == BURN_DISC_UNREADY) + d->status = BURN_DISC_FULL; + return 1; + } + scsi_init_command(&c, MMC_GET_TOC, sizeof(MMC_GET_TOC)); +/* + memcpy(c.opcode, MMC_GET_TOC, sizeof(MMC_GET_TOC)); + c.oplen = sizeof(MMC_GET_TOC); +*/ + c.dxfer_len = *alloc_len; + c.opcode[7] = (c.dxfer_len >> 8) & 0xff; + c.opcode[8] = c.dxfer_len & 0xff; + c.retry = 1; + 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", 0,0); + d->status = BURN_DISC_UNSUITABLE; + d->toc_entries = 0; + /* Prefering memory leaks over fandangos */ + d->toc_entry = calloc(1, sizeof(struct burn_toc_entry)); + return 0; + } + + dlen = c.page->data[0] * 256 + c.page->data[1]; + old_alloc_len = *alloc_len; + *alloc_len = dlen + 2; + if (old_alloc_len < 15) + return 1; + if (dlen + 2 > old_alloc_len) + dlen = old_alloc_len - 2; + d->toc_entries = (dlen - 2) / 11; + if (d->toc_entries < 1) + return 0; +/* + some drives fail this check. + + ts A61007 : if re-enabled then not via Assert. + a ssert(((dlen - 2) % 11) == 0); +*/ + d->toc_entry = calloc(d->toc_entries, sizeof(struct burn_toc_entry)); + if(d->toc_entry == NULL) /* ts A70825 */ + return 0; + tdata = c.page->data + 4; + + burn_print(12, "TOC:\n"); + + d->disc = burn_disc_create(); + if (d->disc == NULL) /* ts A70825 */ + return 0; + + for (i = 0; i < c.page->data[3]; i++) { + session = burn_session_create(); + if (session == NULL) /* ts A70825 */ + return 0; + 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"); + + /* ts A70131 : was (d->status != BURN_DISC_BLANK) */ + if (d->status == BURN_DISC_UNREADY) + d->status = BURN_DISC_FULL; + toc_find_modes(d); + return 1; +} + + +void mmc_read_toc(struct burn_drive *d) +{ + int alloc_len = 4, ret; + + if (mmc_function_spy(d, "mmc_read_toc") <= 0) + return; + + ret = mmc_read_toc_al(d, &alloc_len); +/* + fprintf(stderr, + "LIBBURN_DEBUG: 43h READ TOC alloc_len = %d , ret = %d\n", + alloc_len, ret); +*/ + if (alloc_len >= 15) + ret = mmc_read_toc_al(d, &alloc_len); +} + + +/* ts A70131 : This tries to get the start of the last complete session */ +/* 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. +*/ +int mmc_read_multi_session_c1(struct burn_drive *d, int *trackno, int *start) +{ + struct buffer buf; + struct command c; + unsigned char *tdata; + int num_sessions, session_no, num_tracks, alloc_len = 12; + struct burn_disc *disc; + struct burn_session **sessions; + struct burn_track **tracks; + struct burn_toc_entry toc_entry; + + if (mmc_function_spy(d, "mmc_read_multi_session_c1") <= 0) + return 0; + + /* First try to evaluate the eventually loaded TOC before issueing + a MMC command. This search obtains the first track of the last + complete session which has a track. + */ + *trackno = 0; + disc = burn_drive_get_disc(d); + if (disc == NULL) + goto inquire_drive; + 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); + if (toc_entry.extensions_valid & 1) { /* DVD extension valid */ + *start = toc_entry.start_lba; + *trackno = (toc_entry.point_msb << 8)| toc_entry.point; + } else { + *start = burn_msf_to_lba(toc_entry.pmin, + toc_entry.psec, toc_entry.pframe); + *trackno = toc_entry.point; + } + } + burn_disc_free(disc); + if(*trackno > 0) + return 1; + +inquire_drive:; + /* mmc5r03.pdf 6.26.3.3.3 states that with non-CD this would + be a useless fake always starting at track 1, lba 0. + My drives return useful data, though. + MMC-3 states that DVD had no tracks. So maybe this mandatory fake + is a forgotten legacy ? + */ + scsi_init_command(&c, MMC_GET_MSINFO, sizeof(MMC_GET_MSINFO)); +/* + memcpy(c.opcode, MMC_GET_MSINFO, sizeof(MMC_GET_MSINFO)); + c.oplen = sizeof(MMC_GET_MSINFO); +*/ + c.dxfer_len = alloc_len; + c.opcode[7]= (c.dxfer_len >> 8) & 0xff; + c.opcode[8]= c.dxfer_len & 0xff; + c.retry = 1; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + if (c.error) + return 0; + + tdata = c.page->data + 4; + *trackno = tdata[2]; + *start = mmc_four_char_to_int(tdata + 4); + return 1; +} + + +static int mmc_read_disc_info_al(struct burn_drive *d, int *alloc_len) +{ + struct buffer buf; + unsigned char *data; + struct command c; + char msg[160]; + /* ts A70131 : had to move mmc_read_toc() to end of function */ + int do_read_toc = 0, session_state, disc_status, len, old_alloc_len; + + /* ts A61020 */ + d->start_lba = d->end_lba = -2000000000; + d->erasable = 0; + d->last_track_no = 1; + + /* ts A70212 - A70215 */ + d->media_capacity_remaining = 0; + d->media_lba_limit = 0; + + /* ts A61202 */ + d->toc_entries = 0; + if (d->status == BURN_DISC_EMPTY) + return 1; + + mmc_get_configuration(d); + +/* ts A70910 : found this as condition for mmc_function_spy() which went up + if (*alloc_len < 2) +*/ + + scsi_init_command(&c, MMC_GET_DISC_INFO, sizeof(MMC_GET_DISC_INFO)); +/* + memcpy(c.opcode, MMC_GET_DISC_INFO, sizeof(MMC_GET_DISC_INFO)); + c.oplen = sizeof(MMC_GET_DISC_INFO); +*/ + c.dxfer_len = *alloc_len; + c.opcode[7]= (c.dxfer_len >> 8) & 0xff; + c.opcode[8]= c.dxfer_len & 0xff; + c.retry = 1; + 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 0; + } + + data = c.page->data; + len = (data[0] << 8) | data[1]; + old_alloc_len = *alloc_len; + *alloc_len = len + 2; + if (old_alloc_len < 34) + return 1; + if (*alloc_len < 24) /* data[23] is the last byte used here */ + return 0; + if (len + 2 > old_alloc_len) + len = old_alloc_len - 2; + + d->erasable = !!(data[2] & 16); + + disc_status = data[2] & 3; + if (d->current_profile == 0x10) { /* DVD-ROM */ + disc_status = 2; /* always full and finalized */ + d->erasable = 0; /* never erasable */ + } + switch (disc_status) { + 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 (disc_status == 2) + d->status = BURN_DISC_FULL; + do_read_toc = 1; + break; + } + + 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 0; + } + + /* >>> 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; + + if (d->status == BURN_DISC_BLANK) { + d->last_track_no = 1; /* The "incomplete track" */ + d->complete_sessions = 0; + } else { + /* ts A70131 : number of non-empty sessions */ + d->complete_sessions = (data[9] << 8) | data[4]; + session_state = (data[2] >> 2) & 3; + /* mmc5r03c.pdf 6.22.3.1.3 State of Last Session: 3=complete */ + if (session_state != 3 && d->complete_sessions >= 1) + d->complete_sessions--; + + /* ts A70129 : mmc5r03c.pdf 6.22.3.1.7 + This includes the "incomplete track" if the disk is + appendable. I.e number of complete tracks + 1. */ + d->last_track_no = (data[11] << 8) | data[6]; + } + if (d->current_profile != 0x0a && d->current_profile != 0x13 && + d->current_profile != 0x14 && d->status != BURN_DISC_FULL) + d->erasable = 0; /* stay in sync with burn_disc_erase() */ + + if (do_read_toc) + mmc_read_toc(d); + return 1; +} + + +void mmc_read_disc_info(struct burn_drive *d) +{ + int alloc_len = 34, ret; + + if (mmc_function_spy(d, "mmc_read_disc_info") <= 0) + return; + + ret = mmc_read_disc_info_al(d, &alloc_len); +/* + fprintf(stderr,"LIBBURN_DEBUG: 51h alloc_len = %d , ret = %d\n", + alloc_len, ret); +*/ + /* for now there is no need to inquire the variable lenght part */ +} + + +void mmc_read_atip(struct burn_drive *d) +{ + struct buffer buf; + struct command c; + int alloc_len = 28; + + /* 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, -, -, -, - */ + + if (mmc_function_spy(d, "mmc_read_atip") <= 0) + return; + + scsi_init_command(&c, MMC_GET_ATIP, sizeof(MMC_GET_ATIP)); +/* + memcpy(c.opcode, MMC_GET_ATIP, sizeof(MMC_GET_ATIP)); + c.oplen = sizeof(MMC_GET_ATIP); +*/ + c.dxfer_len = alloc_len; + c.opcode[7]= (c.dxfer_len >> 8) & 0xff; + c.opcode[8]= c.dxfer_len & 0xff; + c.retry = 1; + 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; + + if (mmc_function_spy(d, "mmc_read_sectors") <= 0) + return; + + /* 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); + + scsi_init_command(&c, MMC_READ_CD, sizeof(MMC_READ_CD)); +/* + memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD)); + c.oplen = sizeof(MMC_READ_CD); +*/ + c.retry = 1; + 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; + + if (mmc_function_spy(d, "mmc_erase") <= 0) + return; + + scsi_init_command(&c, MMC_BLANK, sizeof(MMC_BLANK)); +/* + memcpy(c.opcode, MMC_BLANK, sizeof(MMC_BLANK)); + c.oplen = sizeof(MMC_BLANK); +*/ + c.opcode[1] = 16; /* IMMED set to 1 */ + c.opcode[1] |= !!fast; + c.retry = 1; + 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; + + if (mmc_function_spy(d, "mmc_read_lead_in") <= 0) + return; + + len = buf->sectors; + scsi_init_command(&c, MMC_READ_CD, sizeof(MMC_READ_CD)); +/* + memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD)); + c.oplen = sizeof(MMC_READ_CD); +*/ + c.retry = 1; + 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; + + if (mmc_function_spy(d, "mmc_perform_opc") <= 0) + return; + + scsi_init_command(&c, MMC_SEND_OPC, sizeof(MMC_SEND_OPC)); +/* + memcpy(c.opcode, MMC_SEND_OPC, sizeof(MMC_SEND_OPC)); + c.oplen = sizeof(MMC_SEND_OPC); +*/ + c.retry = 1; + 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 + ts A70712 : That leaner solution does not suffice for my LG GSA-4082B. + Meanwhile there is a speed descriptor list anyway. +*/ +int mmc_set_streaming(struct burn_drive *d, + int r_speed, int w_speed, int end_lba) +{ + struct buffer buf; + struct command c; + int b, eff_end_lba; + char msg[160]; + unsigned char *pd; + + if (mmc_function_spy(d, "mmc_set_streaming") <= 0) + return 0; + + scsi_init_command(&c, MMC_SET_STREAMING, sizeof(MMC_SET_STREAMING)); +/* + c.oplen = sizeof(MMC_SET_STREAMING); + memcpy(c.opcode, MMC_SET_STREAMING, sizeof(MMC_SET_STREAMING)); +*/ + c.retry = 1; + 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; + + pd[0] = 0; /* WRC=0 (Default Rotation Control), RDD=Exact=RA=0 */ + + if (w_speed == 0) + w_speed = 0x10000000; /* ~ 2 TB/s */ + else if (w_speed < 0) + w_speed = 177; /* 1x CD */ + if (r_speed == 0) + r_speed = 0x10000000; /* ~ 2 TB/s */ + else if (r_speed < 0) + r_speed = 177; /* 1x CD */ + if (end_lba == 0) { + /* Default computed from 4.7e9 */ + eff_end_lba = 2294921 - 1; + if (d->mdata->max_end_lba > 0) + eff_end_lba = d->mdata->max_end_lba - 1; + } else + eff_end_lba = end_lba; + + 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, end_lba = 0; + struct burn_speed_descriptor *best_sd = NULL; + + if (mmc_function_spy(d, "mmc_set_speed") <= 0) + return; + + if (r <= 0 || w <= 0) { + /* ts A70712 : now searching for best speed descriptor */ + if (w > 0 && r <= 0) + burn_drive_get_best_speed(d, r, &best_sd, 1); + else + burn_drive_get_best_speed(d, w, &best_sd, 0); + if (best_sd != NULL) { + w = best_sd->write_speed; + d->nominal_write_speed = w; + r = best_sd->read_speed; + end_lba = best_sd->end_lba; + } + } + + /* A70711 */ + d->nominal_write_speed = w; + + /* 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, end_lba); + if (ret != 0) + return; /* success or really fatal failure */ + } + + /* ts A61112 : MMC standards prescribe FFFFh as max speed. + But libburn.h prescribes 0. + ts A70715 : <0 now means minimum speed */ + if (r == 0 || r > 0xffff) + r = 0xffff; + else if (r < 0) + r = 177; /* 1x CD */ + if (w == 0 || w > 0xffff) + w = 0xffff; + else if (w < 0) + w = 177; /* 1x CD */ + + scsi_init_command(&c, MMC_SET_SPEED, sizeof(MMC_SET_SPEED)); +/* + memcpy(c.opcode, MMC_SET_SPEED, sizeof(MMC_SET_SPEED)); + c.oplen = sizeof(MMC_SET_SPEED); +*/ + c.retry = 1; + 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 + */ +static int mmc_get_configuration_al(struct burn_drive *d, int *alloc_len) +{ + struct buffer buf; + int len, cp, descr_len = 0, feature_code, prf_number, only_current = 1; + int old_alloc_len; + unsigned char *descr, *prf, *up_to, *prf_end; + struct command c; + int phys_if_std = 0; + char *phys_name = ""; + + if (*alloc_len < 8) + return 0; + + d->current_profile = 0; + d->current_profile_text[0] = 0; + d->current_is_cd_profile = 0; + d->current_is_supported_profile = 0; + d->current_has_feat21h = 0; + d->current_feat21h_link_size = -1; + d->current_feat2fh_byte4 = -1; + + scsi_init_command(&c, MMC_GET_CONFIGURATION, + sizeof(MMC_GET_CONFIGURATION)); +/* + memcpy(c.opcode, MMC_GET_CONFIGURATION, sizeof(MMC_GET_CONFIGURATION)); + c.oplen = sizeof(MMC_GET_CONFIGURATION); +*/ + c.dxfer_len= *alloc_len; + c.retry = 1; + c.opcode[7] = (c.dxfer_len >> 8) & 0xff; + c.opcode[8] = c.dxfer_len & 0xff; + 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; + old_alloc_len = *alloc_len; + *alloc_len = len = mmc_four_char_to_int(c.page->data); + if (len > old_alloc_len) + len = old_alloc_len; + if (len < 8 || len > 4096) + return 0; + 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 +#ifdef Libburn_support_dvd_r_seQ + if (cp == 0x10 || cp == 0x11 || cp == 0x14) /* DVD-ROM,DVD-R,DVD-RW */ + d->current_is_supported_profile = 1; + if (cp == 0x15 && burn_support_untested_profiles) /* DVD-R/DL */ + d->current_is_supported_profile = 1; +#endif +#ifdef Libburn_support_dvd_plus_R + if (cp == 0x1b || (cp == 0x2b && burn_support_untested_profiles)) + /* DVD+R , DVD+R/DL */ + d->current_is_supported_profile = 1; +#endif + +/* Enable this to get loud and repeated reports about the feature set : +#define Libburn_print_feature_descriptorS 1 +*/ + /* ts A70127 : Interpret list of profile and feature descriptors. + see mmc5r03c.pdf 5.2 + >>> Ouch: What to do if list is larger than buffer size. + Specs state that the call has to be repeated. + */ + up_to = c.page->data + (len < BUFFER_SIZE ? len : BUFFER_SIZE); + +#ifdef Libburn_print_feature_descriptorS + fprintf(stderr, + "-----------------------------------------------------------------\n"); + fprintf(stderr, + "LIBBURN_EXPERIMENTAL : feature list length = %d , shown = %d\n", + len, up_to - c.page->data); +#endif /* Libburn_print_feature_descriptorS */ + + for (descr = c.page->data + 8; descr + 3 < up_to; descr += descr_len) { + descr_len = 4 + descr[3]; + feature_code = (descr[0] << 8) | descr[1]; + if (only_current && !(descr[2] & 1)) + continue; + +#ifdef Libburn_print_feature_descriptorS + fprintf(stderr, + "LIBBURN_EXPERIMENTAL : %s feature %4.4Xh\n", + descr[2] & 1 ? "+" : "-", + feature_code); +#endif /* Libburn_print_feature_descriptorS */ + + if (feature_code == 0x0) { + prf_end = descr + 4 + descr[3]; + for (prf = descr + 4; prf + 2 < prf_end; prf += 4) { + if (only_current && !(prf[2] & 1)) + continue; + prf_number = (prf[0] << 8) | prf[1]; + +#ifdef Libburn_print_feature_descriptorS + fprintf(stderr, + "LIBBURN_EXPERIMENTAL : %s profile %4.4Xh \"%s\"\n", + prf[2] & 1 ? "+" : "-", + prf_number, + mmc_obtain_profile_name(prf_number)); +#endif /* Libburn_print_feature_descriptorS */ + + } + + } else if (feature_code == 0x21) { + int i; + + d->current_has_feat21h = (descr[2] & 1); + for (i = 0; i < descr[7]; i++) { + if (i == 0 || descr[8 + i] == 16) + d->current_feat21h_link_size = + descr[8 + i]; + +#ifdef Libburn_print_feature_descriptorS + fprintf(stderr, + "LIBBURN_EXPERIMENTAL : + Link Size = %d\n", + descr[8 + i]); +#endif /* Libburn_print_feature_descriptorS */ + + } + + } else if (feature_code == 0x2F) { + if (descr[2] & 1) + d->current_feat2fh_byte4 = descr[4]; + +#ifdef Libburn_print_feature_descriptorS + fprintf(stderr, "LIBBURN_EXPERIMENTAL : BUF = %d , Test Write = %d , DVD-RW = %d\n", + !!(descr[4] & 64), !!(descr[4] & 4), + !!(descr[4] & 2)); +#endif /* Libburn_print_feature_descriptorS */ + + } else if (feature_code == 0x01) { + phys_if_std = (descr[4] << 24) | (descr[5] << 16) | + (descr[6] << 8) | descr[9]; + if (phys_if_std == 1) + phys_name = "SCSI Family"; + else if(phys_if_std == 2) + phys_name = "ATAPI"; + else if(phys_if_std == 3 || phys_if_std == 4 || + phys_if_std == 6) + phys_name = "IEEE 1394 FireWire"; + else if(phys_if_std == 7) + phys_name = "Serial ATAPI"; + else if(phys_if_std == 8) + phys_name = "USB"; + + d->phys_if_std = phys_if_std; + strcpy(d->phys_if_name, phys_name); + +#ifdef Libburn_print_feature_descriptorS + + fprintf(stderr, + "LIBBURN_EXPERIMENTAL : Phys. Interface Standard %Xh \"%s\"\n", + phys_if_std, phys_name); + + } else if (feature_code == 0x107) { + + fprintf(stderr, "LIBBURN_EXPERIMENTAL : CD SPEED = %d , page 2Ah = %d , SET STREAMING = %d\n", + !!(descr[4] & 8), !!(descr[4] & 4), + !!(descr[4] & 2)); + + } else if (feature_code == 0x108 || feature_code == 0x10c) { + int i, c_limit; + + fprintf(stderr, "LIBBURN_EXPERIMENTAL : %s = ", + feature_code == 0x108 ? + "Drive Serial Number" : "Drive Firmware Date"); + c_limit = descr[3] - 2 * (feature_code == 0x10c); + for (i = 0; i < c_limit; i++) + if (descr[4 + i] < 0x20 || descr[4 + i] > 0x7e + || descr[4 + i] == '\\') + fprintf(stderr,"\\%2.2X",descr[4 + i]); + else + fprintf(stderr, "%c", descr[4 + i]); + fprintf(stderr, "\n"); + +#endif /* Libburn_print_feature_descriptorS */ + + } + } + return 1; +} + + +void mmc_get_configuration(struct burn_drive *d) +{ + int alloc_len = 8, ret; + + if (mmc_function_spy(d, "mmc_get_configuration") <= 0) + return; + + /* first command execution to learn Allocation Length */ + ret = mmc_get_configuration_al(d, &alloc_len); +/* + fprintf(stderr,"LIBBURN_DEBUG: 46h alloc_len = %d , ret = %d\n", + alloc_len, ret); +*/ + if (alloc_len > 8 && ret > 0) + /* second execution with announced length */ + mmc_get_configuration_al(d, &alloc_len); +} + + +/* ts A70108 */ +/* mmc5r03c.pdf 6.24 */ +static int mmc_read_format_capacities_al(struct burn_drive *d, + int *alloc_len, int top_wanted) +{ + struct buffer buf; + int len, type, score, num_descr, max_score = -2000000000, i, sign = 1; + int old_alloc_len; + off_t size, num_blocks; + struct command c; + unsigned char *dpt; +/* <<< + char msg[160]; +*/ + + if (*alloc_len < 4) + return 0; + + 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; + + scsi_init_command(&c, MMC_READ_FORMAT_CAPACITIES, + sizeof(MMC_READ_FORMAT_CAPACITIES)); +/* + memcpy(c.opcode, MMC_READ_FORMAT_CAPACITIES, + sizeof(MMC_READ_FORMAT_CAPACITIES)); + c.oplen = sizeof(MMC_READ_FORMAT_CAPACITIES); +*/ + c.dxfer_len = *alloc_len; + c.retry = 1; + c.opcode[7]= (c.dxfer_len >> 8) & 0xff; + c.opcode[8]= c.dxfer_len & 0xff; + 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]; + old_alloc_len = *alloc_len; + *alloc_len = len + 4; + if (old_alloc_len < 12) + return 1; + if (len + 4 > old_alloc_len) + len = old_alloc_len - 4; + 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; + num_blocks = mmc_four_char_to_int(dpt); + size = num_blocks * (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; + if(d->current_profile == 0x12 && + d->media_capacity_remaining == 0) { + d->media_capacity_remaining = size; + d->media_lba_limit = num_blocks; + } + } 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; + if(d->current_profile == 0x13) { + d->media_capacity_remaining = size; + d->media_lba_limit = num_blocks; + } + } else if(type == 0x26) { /* DVD+RW */ + score = 1 * sign; + d->media_capacity_remaining = size; + d->media_lba_limit = num_blocks; + } 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; +} + + +int mmc_read_format_capacities(struct burn_drive *d, int top_wanted) +{ + int alloc_len = 4, ret; + + if (mmc_function_spy(d, "mmc_read_format_capacities") <= 0) + return 0; + + ret = mmc_read_format_capacities_al(d, &alloc_len, top_wanted); +/* + fprintf(stderr,"LIBBURN_DEBUG: 23h alloc_len = %d , ret = %d\n", + alloc_len, ret); +*/ + if (alloc_len >= 12 && ret > 0) + ret = mmc_read_format_capacities_al(d, &alloc_len, top_wanted); + + return ret; +} + + +void mmc_sync_cache(struct burn_drive *d) +{ + struct command c; + + if (mmc_function_spy(d, "mmc_sync_cache") <= 0) + return; + + scsi_init_command(&c, MMC_SYNC_CACHE, sizeof(MMC_SYNC_CACHE)); +/* + memcpy(c.opcode, MMC_SYNC_CACHE, sizeof(MMC_SYNC_CACHE)); + c.oplen = sizeof(MMC_SYNC_CACHE); +*/ + c.retry = 1; + + c.opcode[1] |= 2; /* ts A70918 : Immed */ + + c.page = NULL; + c.dir = NO_TRANSFER; + + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + "syncing cache", 0, 0); + if(d->wait_for_buffer_free) { + char msg[80]; + + sprintf(msg, + "Checked buffer %u times. Waited %u+%u times = %.3f s", + d->pessimistic_writes, d->waited_writes, + d->waited_tries - d->waited_writes, + ((double) d->waited_usec) / 1.0e6); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002013f, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_LOW, + msg, 0,0); + } + + d->issue_command(d, &c); + + /* ts A70918 */ + if (c.error) { + d->cancel = 1; + return; + } + if (spc_wait_unit_attention(d, 3600, "SYNCHRONIZE CACHE", 0) <= 0) + d->cancel = 1; + else + d->needs_sync_cache = 0; +} + + +/* 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; + int alloc_len = 12; + + if (mmc_function_spy(d, "mmc_read_buffer_capacity") <= 0) + return 0; + + scsi_init_command(&c, MMC_READ_BUFFER_CAPACITY, + sizeof(MMC_READ_BUFFER_CAPACITY)); +/* + memcpy(c.opcode, MMC_READ_BUFFER_CAPACITY, + sizeof(MMC_READ_BUFFER_CAPACITY)); + c.oplen = sizeof(MMC_READ_BUFFER_CAPACITY); +*/ + c.dxfer_len = alloc_len; + c.opcode[7] = (c.dxfer_len >> 8) & 0xff; + c.opcode[8] = c.dxfer_len & 0xff; + c.retry = 1; + c.page = &buf; + memset(c.page->data, 0, alloc_len); + c.page->bytes = 0; + c.page->sectors = 0; + + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + /* >>> ??? error diagnostics */ + if (c.error) + return 0; + + 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]; + d->pessimistic_buffer_free = d->progress.buffer_available; + d->pbf_altered = 0; + 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 ?) */ + + if (mmc_function_spy(d, "mmc_format_unit") <= 0) + return 0; + + scsi_init_command(&c, MMC_FORMAT_UNIT, sizeof(MMC_FORMAT_UNIT)); +/* + c.oplen = sizeof(MMC_FORMAT_UNIT); + memcpy(c.opcode, MMC_FORMAT_UNIT, sizeof(MMC_FORMAT_UNIT)); +*/ + c.retry = 1; + 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; + mmc_int_to_four_char(c.page->data + 4, num_of_blocks); + + 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; + mmc_int_to_four_char(c.page->data + 4, num_of_blocks); + } + 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->needs_close_session = 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) + mmc_int_to_four_char(c.page->data + 4, + num_of_blocks); + } + /* 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; + mmc_int_to_four_char(c.page->data + 4, + num_of_blocks); + } + + } 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->needs_close_session = 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 */ +static int mmc_get_write_performance_al(struct burn_drive *d, + int *alloc_len, int *max_descr) +{ + struct buffer buf; + int len, i, b, num_descr, ret, old_alloc_len; + 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; + + if (d->current_profile <= 0) + mmc_get_configuration(d); + + if (*alloc_len < 8) + return 0; + + scsi_init_command(&c, MMC_GET_PERFORMANCE, + sizeof(MMC_GET_PERFORMANCE)); +/* + memcpy(c.opcode, MMC_GET_PERFORMANCE, sizeof(MMC_GET_PERFORMANCE)); + c.oplen = sizeof(MMC_GET_PERFORMANCE); +*/ +/* ts A70519 : now controlled externally + 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.dxfer_len = *alloc_len; + + c.opcode[8] = ( *max_descr >> 8 ) & 0xff; + c.opcode[9] = ( *max_descr >> 0 ) & 0xff; + c.opcode[10] = 3; + c.retry = 1; + 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 = mmc_four_char_to_int(c.page->data); + old_alloc_len = *alloc_len; + *alloc_len = len + 4; + if (len + 4 > old_alloc_len) + len = old_alloc_len - 4; + num_descr = ( *alloc_len - 8 ) / 16; + if (*max_descr == 0) { + *max_descr = num_descr; + return 1; + } + if (old_alloc_len < 16) + return 1; + if (len < 12) + return 0; + + pd = c.page->data; + 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; +} + + +int mmc_get_write_performance(struct burn_drive *d) +{ + int alloc_len = 8, max_descr = 0, ret; + + if (mmc_function_spy(d, "mmc_get_write_performance") <= 0) + return 0; + + /* first command execution to learn number of descriptors and + dxfer_len */ + ret = mmc_get_write_performance_al(d, &alloc_len, &max_descr); +/* + fprintf(stderr,"LIBBURN_DEBUG: ACh alloc_len = %d , ret = %d\n", + alloc_len, ret); +*/ + if (max_descr > 0 && ret > 0) + /* second execution with announced length */ + ret = mmc_get_write_performance_al(d, &alloc_len, &max_descr); + return ret; +} + + +/* 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; + + if (d->current_profile == 0x13) { + /* A61229 : 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 if ((d->current_profile == 0x14 || d->current_profile == 0x11 || + d->current_profile == 0x15) + && o->write_type == BURN_WRITE_SAO) { + /* ts A70205 : DVD-R[W][/DL] : Disc-at-once, DAO */ + /* Learned from dvd+rw-tools and mmc5r03c.pdf . + See doc/cookbook.txt for more detailed references. */ + + /* BUFE , LS_V = 0, Test Write, Write Type = 2 SAO (DAO) */ + pd[2] = ((!!o->underrun_proof) << 6) + | ((!!o->simulate) << 4) + | 2; + /* No multi-session , FP = 0 , Track Mode = 5 */ + pd[3] = 5; + /* Data Block Type = 8 */ + pd[4] = 8; + + } else if (d->current_profile == 0x14 || d->current_profile == 0x11 || + d->current_profile == 0x15) { + /* ts A70128 : DVD-R[W][/DL] Incremental Streaming */ + /* Learned from transport.hxx : page05_setup() + and mmc5r03c.pdf 7.5, 4.2.3.4 Table 17 + and spc3r23.pdf 6.8, 7.4.3 */ + + /* BUFE , LS_V = 1, Test Write, + Write Type = 0 Packet/Incremental */ + pd[2] = ((!!o->underrun_proof) << 6) + | (1 << 5) + | ((!!o->simulate) << 4); + /* Multi-session , FP = 1 , Track Mode = 5 */ + pd[3] = ((3 * !!o->multi) << 6) | (1 << 5) | 5; + /* Data Block Type = 8 */ + pd[4] = 8; + /* Link Size */ + if (d->current_feat21h_link_size >= 0) + pd[5] = d->current_feat21h_link_size; + else + pd[5] = 16; + if (d->current_feat21h_link_size != 16) { + char msg[80]; + + sprintf(msg, + "Feature 21h Link Size = %d (expected 16)\n", + d->current_feat21h_link_size); + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + } + /* Packet Size */ + pd[13] = 16; + + } else if (d->current_profile == 0x1a || d->current_profile == 0x1b || + d->current_profile == 0x2b || d->current_profile == 0x12) { + /* not with DVD+R[W][/DL] or DVD-RAM */; + return 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; +} + + +/* A70812 ts */ +int mmc_read_10(struct burn_drive *d, int start,int amount, struct buffer *buf) +{ + struct command c; + + if (mmc_function_spy(d, "mmc_read_10") <= 0) + return -1; +; + if (amount > BUFFER_SIZE / 2048) + return -1; + + scsi_init_command(&c, MMC_READ_10, sizeof(MMC_READ_10)); + c.dxfer_len = amount * 2048; + c.retry = 1; + mmc_int_to_four_char(c.opcode + 2, start); + c.opcode[7] = (amount >> 8) & 0xFF; + c.opcode[8] = amount & 0xFF; + c.page = buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + if (c.error) { + char msg[160]; + sprintf(msg, + "SCSI error on read_10(%d,%d): key=%X asc=%2.2Xh ascq=%2.2Xh", + start, amount, + c.sense[2],c.sense[12],c.sense[13]); + if(!d->silent_on_scsi_error) + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020144, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return BE_CANCELLED; + } + + buf->sectors = amount; + buf->bytes = amount * 2048; + return 0; +} + + +/* 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->reserve_track = mmc_reserve_track; + d->sync_cache = mmc_sync_cache; + d->get_nwa = mmc_get_nwa; + d->read_multi_session_c1 = mmc_read_multi_session_c1; + 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; + d->read_10 = mmc_read_10; + + + /* ts A70302 */ + d->phys_if_std = -1; + d->phys_if_name[0] = 0; + + /* ts A61020 */ + d->start_lba = -2000000000; + d->end_lba = -2000000000; + + /* ts A61201 - A70223*/ + 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->current_has_feat21h = 0; + d->current_feat21h_link_size = -1; + d->current_feat2fh_byte4 = -1; + d->needs_close_session = 0; + d->needs_sync_cache = 0; + d->bg_format_status = -1; + d->num_format_descr = 0; + d->complete_sessions = 0; + d->last_track_no = 1; + d->media_capacity_remaining = 0; + d->media_lba_limit = 0; + d->pessimistic_buffer_free = 0; + d->pbf_altered = 0; + d->wait_for_buffer_free = Libburn_wait_for_buffer_freE; + d->nominal_write_speed = 0; + d->pessimistic_writes = 0; + d->waited_writes = 0; + d->waited_tries = 0; + d->waited_usec = 0; + d->wfb_min_usec = Libburn_wait_for_buffer_min_useC; + d->wfb_max_usec = Libburn_wait_for_buffer_max_useC; + d->wfb_timeout_sec = Libburn_wait_for_buffer_tio_seC; + d->wfb_min_percent = Libburn_wait_for_buffer_min_perC; + d->wfb_max_percent = Libburn_wait_for_buffer_max_perC; + + return 1; +} + + diff --git a/libburn/branches/ZeroFourTwo/libburn/mmc.h b/libburn/branches/ZeroFourTwo/libburn/mmc.h new file mode 100644 index 00000000..9b075976 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/mmc.h @@ -0,0 +1,80 @@ +/* -*- 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); + +/* ts A70812 : return 0 = ok , return BE_CANCELLED = error occured */ +int mmc_read_10(struct burn_drive *d, int start, int amount, + struct buffer *buf); + + + +/* mmc5r03c.pdf 4.3.4.4.1 d) "The maximum number of RZones is 2 302." */ +#define BURN_MMC_FAKE_TOC_MAX_SIZE 2302 + +#endif /*__MMC*/ diff --git a/libburn/branches/ZeroFourTwo/libburn/null.c b/libburn/branches/ZeroFourTwo/libburn/null.c new file mode 100644 index 00000000..38403396 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/null.c @@ -0,0 +1,31 @@ +/* -*- 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; + + /* ts A70126 */ + src->set_size = NULL; + + src->free_data = NULL; + src->data = NULL; + return src; +} diff --git a/libburn/branches/ZeroFourTwo/libburn/null.h b/libburn/branches/ZeroFourTwo/libburn/null.h new file mode 100644 index 00000000..1a7aae33 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/options.c b/libburn/branches/ZeroFourTwo/libburn/options.c new file mode 100644 index 00000000..4dc1ef34 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/options.c @@ -0,0 +1,436 @@ +#include "libburn.h" +#include "options.h" +#include "drive.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->fill_up_media = 0; + opts->force_is_set = 0; + 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) +{ +/* <<< ts A70529 : + One cannot predict the ability to simulate from page 05h + information alone. This check is now done later in + function burn_write_opts_auto_write_type(). + + if (opts->drive->mdata->simulate) { + opts->simulate = sim; + return 1; + } + return 0; +*/ + opts->simulate = !!sim; + return 1; +} + +int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts, + int underrun_proof) +{ + if (!opts->drive->mdata->valid) + return 0; + 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; +} + + +/* ts A70207 API */ +/** @param flag Bitfield for control purposes: + bit0= do not choose type but check the one that is already set + bit1= do not issue error messages via burn_msgs queue +*/ +enum burn_write_types burn_write_opts_auto_write_type( + struct burn_write_opts *opts, struct burn_disc *disc, + char reasons[BURN_REASONS_LEN], int flag) +{ + struct burn_multi_caps *caps = NULL; + struct burn_drive *d = opts->drive; + struct burn_disc_mode_demands demands; + enum burn_write_types wt; + int ret, would_do_sao = 0; + char *reason_pt; + + reasons[0] = 0; + + if (d->status != BURN_DISC_BLANK && + d->status != BURN_DISC_APPENDABLE){ + if (d->status == BURN_DISC_FULL) + strcat(reasons, "MEDIA: closed or not recordable, "); + else + strcat(reasons,"MEDIA: no writeable media detected, "); + if (!(flag & 3)) + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002013a, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "No suitable media detected", 0, 0); + return BURN_WRITE_NONE; + } + ret = burn_disc_get_write_mode_demands(disc, opts, &demands, + !!opts->fill_up_media); + if (ret <= 0) { + strcat(reasons, "cannot recognize job demands, "); + {wt = BURN_WRITE_NONE; goto ex;} + } + if (demands.exotic_track && !d->current_is_cd_profile) { + if (demands.audio) + strcat(reasons, "audio track prohibited by non-CD, "); + else + strcat(reasons, "exotic track prohibited by non-CD, "); + {wt = BURN_WRITE_NONE; goto ex;} + } + if ((flag & 1) && opts->write_type != BURN_WRITE_SAO) + goto try_tao; + reason_pt = reasons + strlen(reasons); + strcat(reasons, "SAO: "); + if (d->status != BURN_DISC_BLANK) { + strcat(reasons, "write type SAO works only on blank media, "); + goto try_tao; + } + burn_disc_free_multi_caps(&caps); + ret = burn_disc_get_multi_caps(d, BURN_WRITE_SAO, &caps, 0); + if (ret < 0) { +no_caps:; + strcat(reasons, "cannot inquire write mode capabilities, "); + {wt = BURN_WRITE_NONE; goto ex;} + } else if (ret == 0) { + strcat(reasons, "no SAO offered by drive and media, "); + goto no_sao; + } + if ((opts->multi || demands.multi_session) && + !caps->multi_session) + strcat(reasons, "multi session capability lacking, "); + if (demands.will_append) + strcat(reasons, "appended session capability lacking, "); + if (demands.multi_track && !caps->multi_track) + strcat(reasons, "multi track capability lacking, "); + if (demands.unknown_track_size == 1 && + (caps->might_do_sao == 1 || caps->might_do_sao == 3)) + strcat(reasons, "track size unpredictable, "); + if (demands.mixed_mode) + strcat(reasons, "tracks of different modes mixed, "); + if (demands.exotic_track && !d->current_is_cd_profile) + strcat(reasons, "non-data track on non-cd, "); + else if (d->current_is_cd_profile) + if ((d->block_types[BURN_WRITE_TAO] & demands.block_types) != + demands.block_types) + strcat(reasons, "drive dislikes block type, "); + if (d->current_is_cd_profile && opts->fill_up_media) + strcat(reasons, "cd sao cannot do media fill up yet, "); + if (strcmp(reason_pt, "SAO: ") != 0) + goto no_sao; + would_do_sao = 1; + if (demands.unknown_track_size == 2 && (!(flag & 1)) && + (caps->might_do_sao == 1 || caps->might_do_sao == 3)) { + strcat(reasons, "would have to use default track sizes, "); + goto no_sao; + } else if (caps->might_do_sao >= 3 && !(flag & 1)) + goto try_tao; +do_sao:; + if (caps->might_simulate == 0 && opts->simulate && !opts->force_is_set) + goto no_simulate; + if (!(flag & 1)) + burn_write_opts_set_write_type( + opts, BURN_WRITE_SAO, BURN_BLOCK_SAO); + {wt = BURN_WRITE_SAO; goto ex;} +no_sao:; +try_tao:; + if ((flag & 1) && opts->write_type != BURN_WRITE_TAO) + goto try_raw; + reason_pt = reasons + strlen(reasons); + strcat(reasons, "TAO: "); + burn_disc_free_multi_caps(&caps); + ret = burn_disc_get_multi_caps(d, BURN_WRITE_TAO, &caps, 0); + if (ret < 0) + goto no_caps; + if (ret == 0) { + strcat(reasons, "no TAO offered by drive and media, "); + goto no_tao; + } + if ((opts->multi || demands.multi_session) && !caps->multi_session) + strcat(reasons, "multi session capability lacking, "); + if (demands.multi_track && !caps->multi_track) + strcat(reasons, "multi track capability lacking, "); + if (demands.exotic_track && !d->current_is_cd_profile) + strcat(reasons, "non-data track on non-cd, "); + if (d->current_is_cd_profile && !opts->force_is_set) + if ((d->block_types[BURN_WRITE_TAO] & demands.block_types) != + demands.block_types) + strcat(reasons, "drive dislikes block type, "); + if (strcmp(reason_pt, "TAO: ") != 0) + goto no_tao; + /* ( TAO data/audio block size will be handled automatically ) */ + if (caps->might_simulate == 0 && opts->simulate && !opts->force_is_set) + goto no_simulate; + if (!(flag & 1)) + burn_write_opts_set_write_type( + opts, BURN_WRITE_TAO, BURN_BLOCK_MODE1); + {wt = BURN_WRITE_TAO; goto ex;} +no_tao:; + if (would_do_sao && !(flag & 1)) + goto do_sao; + if (!d->current_is_cd_profile) + goto no_write_mode; +try_raw:; + if ((flag & 1) && opts->write_type != BURN_WRITE_RAW) + goto no_write_mode; + + if (!(flag & 1)) /* For now: no automatic raw write modes */ + goto no_write_mode; + + reason_pt = reasons + strlen(reasons); + strcat(reasons, "RAW: "); + if (!d->current_is_cd_profile) + strcat(reasons, "write type RAW prohibited by non-cd, "); + else if (d->status != BURN_DISC_BLANK) + strcat(reasons, "write type RAW works only on blank media, "); + else if ((d->block_types[BURN_WRITE_TAO] & demands.block_types) != + demands.block_types) + strcat(reasons, "drive dislikes block type, "); + if (strcmp(reason_pt, "RAW: ") != 0) + goto no_write_mode; + if (!opts->force_is_set) + goto no_simulate; + + /* For now: no setting of raw write modes */ + + {wt = BURN_WRITE_RAW; goto ex;} + +no_write_mode:; + {wt = BURN_WRITE_NONE; goto ex;} + +no_simulate:; + strcat(reasons, + "simulation of write job not supported by drive and media, "); + {wt = BURN_WRITE_NONE; goto ex;} + +ex:; + burn_disc_free_multi_caps(&caps); + if (wt == BURN_WRITE_NONE && !(flag & 3)) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002012b, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Drive offers no suitable write mode with this job", + 0, 0); + } + return wt; +} + + +/* ts A70213 : new API function */ +void burn_write_opts_set_fillup(struct burn_write_opts *opts,int fill_up_media) +{ + opts->fill_up_media = !!fill_up_media; + return; +} + + +/* ts A70303: API */ +void burn_write_opts_set_force(struct burn_write_opts *opts, int use_force) +{ + opts->force_is_set = !!use_force; +} + + +/* ts A70901: API */ +struct burn_drive *burn_write_opts_get_drive(struct burn_write_opts *opts) +{ + return opts->drive; +} + + +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/branches/ZeroFourTwo/libburn/options.h b/libburn/branches/ZeroFourTwo/libburn/options.h new file mode 100644 index 00000000..13d91c31 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/options.h @@ -0,0 +1,95 @@ +#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; + + /* ts A70213 : Wether to fill up the available space on media */ + int fill_up_media; + + /* ts A70303 : Wether to override conformance checks: + - the check wether CD write+block type is supported by the drive + */ + int force_is_set; + + /** 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/branches/ZeroFourTwo/libburn/os-freebsd.h b/libburn/branches/ZeroFourTwo/libburn/os-freebsd.h new file mode 100644 index 00000000..29b87710 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/os-freebsd.h @@ -0,0 +1,61 @@ + +/* 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 +*/ + +/** 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; \ +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; + diff --git a/libburn/branches/ZeroFourTwo/libburn/os-linux.h b/libburn/branches/ZeroFourTwo/libburn/os-linux.h new file mode 100644 index 00000000..bd91a343 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/os-linux.h @@ -0,0 +1,73 @@ + +/* 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 ! */ +/* ts A70523 : >32k seems not good with kernel 2.4 USB drivers and audio +#define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 +*/ +#define BURN_OS_TRANSPORT_BUFFER_SIZE 32768 + + +/* 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; + + +/* Parameters for sibling list. See sibling_fds, sibling_fnames */ +#define BURN_OS_SG_MAX_SIBLINGS 5 +#define BURN_OS_SG_MAX_NAMELEN 16 + +/* 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[BURN_OS_SG_MAX_SIBLINGS]; \ +/* ts A70409 : DDLP */ \ +char sibling_fnames[BURN_OS_SG_MAX_SIBLINGS][BURN_OS_SG_MAX_NAMELEN]; + diff --git a/libburn/branches/ZeroFourTwo/libburn/os.h b/libburn/branches/ZeroFourTwo/libburn/os.h new file mode 100644 index 00000000..3b7a0f4d --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/read.c b/libburn/branches/ZeroFourTwo/libburn/read.c new file mode 100644 index 00000000..a54017d7 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/read.c @@ -0,0 +1,465 @@ +/* -*- 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 <fcntl.h> +#include <errno.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" + +/* ts A70812 */ +#include "error.h" +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + + +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); +} +*/ + + +/* ts A70904 */ +/** @param flag bit0=be silent on data shortage */ +int burn_stdio_read(int fd, char *buf, int bufsize, struct burn_drive *d, + int flag) +{ + int todo, count = 0; + + for(todo = bufsize; todo > 0; ) { + count = read(fd, buf + (bufsize - todo), todo); + if(count <= 0) + break; + todo -= count; + } + if(todo > 0 && !(flag & 1)) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002014a, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Cannot read desired amount of data", errno, 0); + } + if (count < 0) + return -1; + return (bufsize - todo); +} + + +/* ts A70812 : API function */ +int burn_read_data(struct burn_drive *d, off_t byte_address, + char data[], off_t data_size, off_t *data_count, int flag) +{ + int alignment = 2048, start, upto, chunksize = 1, err, cpy_size, i; + int sose_mem = 0, fd = -1, ret; + char msg[81], *wpt; + struct buffer buf; + + *data_count = 0; + sose_mem = d->silent_on_scsi_error; + + if (d->released) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020142, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Drive is not grabbed on random access read", 0, 0); + return 0; + } + if (d->drive_role == 0) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020146, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Drive is a virtual placeholder (null-drive)", 0, 0); + return 0; + } else if (d->drive_role == 3) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020151, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Read attempt on write-only drive", 0, 0); + return 0; + } + if ((byte_address % alignment) != 0) { + sprintf(msg, + "Read start address not properly aligned (%d bytes)", + alignment); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020143, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return 0; + } + + if (d->busy != BURN_DRIVE_IDLE) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020145, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Drive is busy on attempt to read data", 0, 0); + return 0; + } + + if (d->drive_role != 1) { + +/* <<< We need _LARGEFILE64_SOURCE defined by the build system. +*/ +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + + fd = open(d->devname, O_RDONLY | O_LARGEFILE); + if (fd == -1) { + if (errno != ENOENT || !(flag & 2)) + libdax_msgs_submit(libdax_messenger, + d->global_index, + 0x00020005, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Failed to open device (a pseudo-drive) for reading", + errno, 0); + ret = 0; goto ex; + } + if (lseek(fd, byte_address, SEEK_SET) == -1) { + if (!(flag & 2)) + libdax_msgs_submit(libdax_messenger, + d->global_index, + 0x00020147, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Cannot address start byte", errno, 0); + ret = 0; goto ex; + } + } + + d->busy = BURN_DRIVE_READING_SYNC; + d->buffer = &buf; + + start = byte_address / 2048; + upto = start + data_size / 2048; + if (data_size % 2048) + upto++; + wpt = data; + for (; start < upto; start += chunksize) { + chunksize = upto - start; + if (chunksize > 16) { + chunksize = 16; + cpy_size = 16 * 2048; + } else + cpy_size = data_size - *data_count; + if (flag & 2) + d->silent_on_scsi_error = 1; + if (d->drive_role == 1) { + err = d->read_10(d, start, chunksize, d->buffer); + } else { + ret = burn_stdio_read(fd, (char *) d->buffer->data, + cpy_size, d, !!(flag & 2)); + err = 0; + if (ret <= 0) + err = BE_CANCELLED; + } + if (flag & 2) + d->silent_on_scsi_error = sose_mem; + if (err == BE_CANCELLED) { + /* Try to read a smaller part of the chunk */ + for (i = 0; i < chunksize - 1; i++) { + if (flag & 2) + d->silent_on_scsi_error = 1; + if (d->drive_role == 1) { + err = d->read_10(d, start + i, 1, + d->buffer); + } else { + ret = burn_stdio_read(fd, + (char *) d->buffer->data, + 2048, d, 1); + if (ret <= 0) + err = BE_CANCELLED; + } + if (flag & 2) + d->silent_on_scsi_error = sose_mem; + if (err == BE_CANCELLED) + break; + memcpy(wpt, d->buffer->data, 2048); + wpt += 2048; + *data_count += 2048; + } + ret = 0; goto ex; + } + memcpy(wpt, d->buffer->data, cpy_size); + wpt += cpy_size; + *data_count += cpy_size; + } + + ret = 1; +ex:; + if (fd != -1) + close(fd); + d->buffer = NULL; + d->busy = BURN_DRIVE_IDLE; + return ret; +} diff --git a/libburn/branches/ZeroFourTwo/libburn/read.h b/libburn/branches/ZeroFourTwo/libburn/read.h new file mode 100644 index 00000000..fc079583 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/sbc.c b/libburn/branches/ZeroFourTwo/libburn/sbc.c new file mode 100644 index 00000000..b7726bed --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/sbc.c @@ -0,0 +1,114 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* scsi block commands */ + +#include <string.h> +#include <unistd.h> + +#include "transport.h" +#include "sbc.h" +#include "spc.h" +#include "options.h" + + +/* ts A70910 + debug: for tracing calls which might use open drive fds + or for catching SCSI usage of emulated drives. */ +int mmc_function_spy(struct burn_drive *d, char * text); + + +/* spc command set */ +static unsigned char SBC_LOAD[] = { 0x1b, 0, 0, 0, 3, 0 }; +static unsigned char SBC_UNLOAD[] = { 0x1b, 0, 0, 0, 2, 0 }; +static unsigned char SBC_START_UNIT[] = { 0x1b, 0, 0, 0, 1, 0 }; + +void sbc_load(struct burn_drive *d) +{ + struct command c; + + if (mmc_function_spy(d, "load") <= 0) + return; + + scsi_init_command(&c, SBC_LOAD, sizeof(SBC_LOAD)); +/* + memcpy(c.opcode, SBC_LOAD, sizeof(SBC_LOAD)); + c.oplen = sizeof(SBC_LOAD); + c.page = NULL; +*/ + c.retry = 1; + + /* ts A70921 : Had to revoke Immed because of LG GSA-4082B */ + /* c.opcode[1] |= 1; / * ts A70918 : Immed */ + + c.dir = NO_TRANSFER; + d->issue_command(d, &c); + if (c.error) + return; + /* ts A70923 : Needed regardless of Immed bit. Was once 1 minute, now + 5 minutes for loading. If this does not suffice then other commands + shall fail righteously. */ + spc_wait_unit_attention(d, 300, "waiting after START UNIT (+ LOAD)",0); +} + +void sbc_eject(struct burn_drive *d) +{ + struct command c; + + if (mmc_function_spy(d, "eject") <= 0) + return; + + scsi_init_command(&c, SBC_UNLOAD, sizeof(SBC_UNLOAD)); +/* + memcpy(c.opcode, SBC_UNLOAD, sizeof(SBC_UNLOAD)); + c.oplen = sizeof(SBC_UNLOAD); + c.page = NULL; +*/ + + c.opcode[1] |= 1; /* ts A70918 : Immed */ + + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); + if (c.error) + return; + /* ts A70918 : Wait long. A late eject could surprise or hurt user. */ + spc_wait_unit_attention(d, 1800, "STOP UNIT (+ EJECT)", 0); +} + +/* 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; + + if (mmc_function_spy(d, "start_unit") <= 0) + return 0; + + scsi_init_command(&c, SBC_START_UNIT, sizeof(SBC_START_UNIT)); +/* + memcpy(c.opcode, SBC_START_UNIT, sizeof(SBC_START_UNIT)); + c.oplen = sizeof(SBC_START_UNIT); + c.page = NULL; +*/ + c.retry = 1; + + c.opcode[1] |= 1; /* ts A70918 : Immed */ + + c.dir = NO_TRANSFER; + d->issue_command(d, &c); + if (c.error) + return 0; + /* ts A70918 : now asynchronous */ + return spc_wait_unit_attention(d, 1800, "START UNIT", 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/branches/ZeroFourTwo/libburn/sbc.h b/libburn/branches/ZeroFourTwo/libburn/sbc.h new file mode 100644 index 00000000..dc7a991b --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/sector.c b/libburn/branches/ZeroFourTwo/libburn/sector.c new file mode 100644 index 00000000..35513144 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/sector.c @@ -0,0 +1,846 @@ +/* -*- 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) { + if (track->source->read != NULL) + valid = track->source->read(track->source, + data + curr, count - curr); + else + valid = track->source->read_xt(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 || + (opts->obs > 0 && out->bytes + seclen > 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/branches/ZeroFourTwo/libburn/sector.h b/libburn/branches/ZeroFourTwo/libburn/sector.h new file mode 100644 index 00000000..ecb609a4 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/sg-freebsd-port.c b/libburn/branches/ZeroFourTwo/libburn/sg-freebsd-port.c new file mode 100644 index 00000000..e7e8168d --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/sg-freebsd-port.c @@ -0,0 +1,631 @@ +/* -*- 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. + +burn_os_stdio_capacity() estimates the emulated media space of stdio-drives. + + +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 */ + + +/* ts A70909 : >>> untestet yet wether this compiles */ +#include <sys/statvfs.h> + + +/** 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); +} + + +/* ts A70909 */ +/** Estimate the potential payload capacity of a file address. + @param path The address of the file to be examined. If it does not + exist yet, then the directory will be inquired. + @param bytes This value gets modified if an estimation is possible + @return -2 = cannot perform necessary operations on file object + -1 = neither path nor dirname of path exist + 0 = could not estimate size capacity of file object + 1 = estimation has been made, bytes was set +*/ +int burn_os_stdio_capacity(char *path, off_t *bytes) +{ + struct stat stbuf; + struct statvfs vfsbuf; + char testpath[4096], *cpt; + long blocks; + int open_mode = O_RDWR, fd, ret; + off_t add_size = 0; + + testpath[0] = 0; + blocks = *bytes / 512; + if (stat(path, &stbuf) == -1) { + strcpy(testpath, path); + cpt = strrchr(testpath, '/'); + if(cpt == NULL) + strcpy(testpath, "."); + else if(cpt == testpath) + testpath[1] = 0; + else + *cpt = 0; + if (stat(testpath, &stbuf) == -1) + return -1; + +#ifdef Libburn_if_this_was_linuX + + } else if(S_ISBLK(stbuf.st_mode)) { + if(burn_sg_open_o_excl) + open_mode |= O_EXCL; + fd = open(path, open_mode); + if (fd == -1) + return -2; + ret = ioctl(fd, BLKGETSIZE, &blocks); + close(fd); + if (ret == -1) + return -2; + *bytes = ((off_t) blocks) * (off_t) 512; + +#endif /* Libburn_if_this_was_linuX */ + + + } else if(S_ISREG(stbuf.st_mode)) { + add_size = stbuf.st_blocks * (off_t) 512; + strcpy(testpath, path); + } else + return 0; + + if (testpath[0]) { + if (statvfs(testpath, &vfsbuf) == -1) + return -2; + *bytes = add_size + ((off_t) vfsbuf.f_bsize) * + (off_t) vfsbuf.f_bavail; + } + return 1; +} + diff --git a/libburn/branches/ZeroFourTwo/libburn/sg-freebsd.c b/libburn/branches/ZeroFourTwo/libburn/sg-freebsd.c new file mode 100644 index 00000000..67e2993c --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/sg-freebsd.c @@ -0,0 +1,673 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#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 */ + + +/* ts A70909 : >>> untestet yet wether this compiles */ +#include <sys/statvfs.h> + + +#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; + +struct burn_drive_enumeration_state { + int fd; + union ccb ccb; + unsigned int i; + int skip_device; +}; + +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 + or for catching SCSI usage of emulated drives. */ +int mmc_function_spy(struct burn_drive *d, char * text); + + +/* 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_) +{ + struct burn_drive_enumeration_state *idx; + int bufsize; + + idx = malloc(sizeof(*idx)); + if (idx == NULL) { + warnx("can't malloc memory for enumerator"); + return -1; + } + 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; + bufsize = sizeof(struct dev_match_result) * 100; + idx->ccb.cdm.match_buf_len = bufsize; + idx->ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); + if (idx->ccb.cdm.matches == NULL) { + warnx("can't malloc memory for matches"); + close(idx->fd); + free(idx); + 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; + + *idx_ = idx; + + return 1; +} + +static void sg_destroy_enumerator(burn_drive_enumerator_t *idx_) +{ + struct burn_drive_enumeration_state *idx = *idx_; + + if(idx->fd != -1) + close(idx->fd); + + free(idx->ccb.cdm.matches); + free(idx); + + *idx_ = NULL; +} + +static int sg_next_enumeration_buffer(burn_drive_enumerator_t *idx_) +{ + struct burn_drive_enumeration_state *idx = *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) +{ + struct burn_drive_enumeration_state *idx; + int ret; + + if (initialize == 1) { + ret = sg_init_enumerator(idx_); + if (ret<=0) + return ret; + } else if (initialize == -1) { + sg_destroy_enumerator(idx_); + return 0; + } + + idx = *idx_; + + do { + if (idx->i >= idx->ccb.cdm.num_matches) { + ret = sg_next_enumeration_buffer(idx_); + if (ret<=0) + return -1; + idx->i = 0; + } else + (idx->i)++; + + while (idx->i < idx->ccb.cdm.num_matches) { + 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[idx->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; + + result = &(idx->ccb.cdm.matches[idx->i].result.periph_result); + if (idx->skip_device || + strcmp(result->periph_name, "pass") == 0) + break; + ret = snprintf(adr, adr_size, "/dev/%s%d", + result->periph_name, result->unit_number); + if(ret >= adr_size) + return -1; + + /* Found next enumerable address */ + return 1; + + } + default: + /* printf(stderr, "unknown match type\n"); */ + break; + } + (idx->i)++; + } + } while ((idx->ccb.ccb_h.status == CAM_REQ_CMP) + && (idx->ccb.cdm.status == CAM_DEV_MATCH_MORE)); + + return 0; +} + + +int sg_is_enumerable_adr(char* adr) +{ + burn_drive_enumerator_t idx; + int ret; + char buf[64]; + + ret = sg_init_enumerator(&idx); + if (ret <= 0) + return 0; + while(1) { + ret = sg_give_next_adr(&idx, buf, sizeof(buf), 0); + if (ret <= 0) + break; + if (strcmp(adr, buf) == 0) { + sg_destroy_enumerator(&idx); + return 1; + } + } + sg_destroy_enumerator(&idx); + 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 ret; + char buf[64]; + struct periph_match_result* result; + + ret = sg_init_enumerator(&idx); + if (ret <= 0) + return 0; + while(1) { + ret = sg_give_next_adr(&idx, buf, sizeof(buf), 0); + if (ret <= 0) + break; + if (strcmp(path, buf) == 0) { + result = &(idx->ccb.cdm.matches[idx->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_destroy_enumerator(&idx); + return 1; + } + } + sg_destroy_enumerator(&idx); + return (0); +} + + +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) +{ + burn_drive_enumerator_t idx; + int ret; + char buf[64]; + struct periph_match_result* result; + + ret = sg_init_enumerator(&idx); + if (ret <= 0) + return 0; + while(1) { + ret = sg_give_next_adr(&idx, buf, sizeof(buf), 0); + if (ret <= 0) + break; + if (burn_drive_is_banned(buf)) + continue; + result = &idx->ccb.cdm.matches[idx->i].result.periph_result; + enumerate_common(buf, result->path_id, result->path_id, + 0, result->target_id, + result->target_lun); + } + sg_destroy_enumerator(&idx); + + return 1; +} + + +#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(NULL, "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(NULL, "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; + + if (mmc_function_spy(d, "sg_grab") <= 0) + return 0; + + if (burn_drive_is_open(d)) { + d->released = 0; + return 1; + } + + 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) +{ + if (mmc_function_spy(d, "sg_release") <= 0) + return 0; + + if (d->cam == NULL) { + burn_print(1, "release an ungrabbed drive. die\n"); + return 0; + } + + mmc_function_spy(NULL, "sg_release ----------- closing."); + + sg_close_drive(d); + d->released = 1; + 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(NULL, 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; +} + + +/* ts A70909 */ +/** Estimate the potential payload capacity of a file address. + @param path The address of the file to be examined. If it does not + exist yet, then the directory will be inquired. + @param bytes This value gets modified if an estimation is possible + @return -2 = cannot perform necessary operations on file object + -1 = neither path nor dirname of path exist + 0 = could not estimate size capacity of file object + 1 = estimation has been made, bytes was set +*/ +int burn_os_stdio_capacity(char *path, off_t *bytes) +{ + struct stat stbuf; + struct statvfs vfsbuf; + char testpath[4096], *cpt; + long blocks; + int open_mode = O_RDWR, fd, ret; + off_t add_size = 0; + + testpath[0] = 0; + blocks = *bytes / 512; + if (stat(path, &stbuf) == -1) { + strcpy(testpath, path); + cpt = strrchr(testpath, '/'); + if(cpt == NULL) + strcpy(testpath, "."); + else if(cpt == testpath) + testpath[1] = 0; + else + *cpt = 0; + if (stat(testpath, &stbuf) == -1) + return -1; + +#ifdef Libburn_if_this_was_linuX + + } else if(S_ISBLK(stbuf.st_mode)) { + if(burn_sg_open_o_excl) + open_mode |= O_EXCL; + fd = open(path, open_mode); + if (fd == -1) + return -2; + ret = ioctl(fd, BLKGETSIZE, &blocks); + close(fd); + if (ret == -1) + return -2; + *bytes = ((off_t) blocks) * (off_t) 512; + +#endif /* Libburn_if_this_was_linuX */ + + + } else if(S_ISREG(stbuf.st_mode)) { + add_size = stbuf.st_blocks * (off_t) 512; + strcpy(testpath, path); + } else + return 0; + + if (testpath[0]) { + if (statvfs(testpath, &vfsbuf) == -1) + return -2; + *bytes = add_size + ((off_t) vfsbuf.f_bsize) * + (off_t) vfsbuf.f_bavail; + } + return 1; +} + diff --git a/libburn/branches/ZeroFourTwo/libburn/sg-linux.c b/libburn/branches/ZeroFourTwo/libburn/sg-linux.c new file mode 100644 index 00000000..4a3b2362 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/sg-linux.c @@ -0,0 +1,1422 @@ +/* -*- 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. + +burn_os_stdio_capacity() estimates the emulated media space of stdio-drives. + + +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 <sys/utsname.h> +#include <scsi/scsi.h> +#include <sys/statvfs.h> + +/* for ioctl(BLKGETSIZE) */ +#include <linux/fs.h> + + +#include <scsi/sg.h> +/* Values within sg_io_hdr_t indicating success after ioctl(SG_IO) : */ +/* .host_status : from http://tldp.org/HOWTO/SCSI-Generic-HOWTO/x291.html */ +#define Libburn_sg_host_oK 0 +/* .driver_status : from http://tldp.org/HOWTO/SCSI-Generic-HOWTO/x322.html */ +#define Libburn_sg_driver_oK 0 + + +/* ts A61211 : to eventually recognize CD devices on /dev/sr* */ +#include <limits.h> +#include <linux/cdrom.h> + + +/** Indication of the Linux kernel this software is running on */ +/* -1 = not evaluated , 0 = unrecognizable , 1 = 2.4 , 2 = 2.6 */ +static int sg_kernel_age = -1; + + +/** 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" + sr%d is supposed to map only CD-ROM style devices. Additionally a test + with ioctl(CDROM_DRIVE_STATUS) is made to assert that it is such a drive, + + This initial setting may be overridden in sg_select_device_family() by + settings made via burn_preset_device_open(). +*/ +static char linux_sg_device_family[80] = {"/dev/sg%d"}; + +/* Set this to 1 if you want the default linux_sg_device_family chosen + depending on kernel release: sg for <2.6 , sr for >=2.6 +*/ +static int linux_sg_auto_family = 1; + + +/* Set this to 1 in order to accept any TYPE_* (see scsi/scsi.h) */ +/* But 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 A60813 : storage objects are in libburn/init.c + wether to use O_EXCL with open(2) of devices + wether to use fcntl(,F_SETLK,) after open(2) of devices + what device family to use : 0=default, 1=sr, 2=scd, (3=st), 4=sg + 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_fcntl_f_setlk; +extern int burn_sg_use_family; +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 + or for catching SCSI usage of emulated drives. */ +int mmc_function_spy(struct burn_drive *d, char * text); + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Private functions. Port only if needed by public functions */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + +/* ts A70413 */ +/* This finds out wether the software is running on kernel >= 2.6 +*/ +static void sg_evaluate_kernel(void) +{ + struct utsname buf; + if (sg_kernel_age >= 0) + return; + + sg_kernel_age = 0; + if (uname(&buf) == -1) + return; + sg_kernel_age = 1; + if (strcmp(buf.release, "2.6") >= 0) + sg_kernel_age = 2; +} + + +/* ts A70314 */ +/* This installs the device file family if one was chosen explicitely + by burn_preset_device_open() +*/ +static void sg_select_device_family(void) +{ + + /* >>> ??? do we need a mutex here ? */ + /* >>> (It might be concurrent but is supposed to have always + the same effect. Any race condition should be harmless.) */ + + if (burn_sg_use_family == 1) + strcpy(linux_sg_device_family, "/dev/sr%d"); + else if (burn_sg_use_family == 2) + strcpy(linux_sg_device_family, "/dev/scd%d"); + else if (burn_sg_use_family == 3) + strcpy(linux_sg_device_family, "/dev/st%d"); + else if (burn_sg_use_family == 4) + strcpy(linux_sg_device_family, "/dev/sg%d"); + else if (linux_sg_auto_family) { + sg_evaluate_kernel(); + if (sg_kernel_age >= 2) + strcpy(linux_sg_device_family, "/dev/sr%d"); + else + strcpy(linux_sg_device_family, "/dev/sg%d"); + linux_sg_auto_family = 0; + } +} + + +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 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) { + /* ts A70409 : DDLP-B */ + /* >>> release single lock on fname */ + return 1; + } + os_errno= errno; + + sprintf(msg, "Encountered error when closing drive '%s'", fname); + 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 A70401 : + fcntl() has the unappealing property to work only after open(). + So libburn will by default use open(O_EXCL) first and afterwards + as second assertion will use fcntl(F_SETLK). One lock more should not harm. +*/ +static int sg_fcntl_lock(int *fd, char *fd_name, int l_type, int verbous) +{ + struct flock lockthing; + char msg[81]; + int ret; + + if (!burn_sg_fcntl_f_setlk) + return 1; + + memset(&lockthing, 0, sizeof(lockthing)); + lockthing.l_type = l_type; + lockthing.l_whence = SEEK_SET; + lockthing.l_start = 0; + lockthing.l_len = 0; +/* + fprintf(stderr,"LIBBURN_EXPERIMENTAL: fcntl(%d, F_SETLK, %s)\n", + *fd, l_type == F_WRLCK ? "F_WRLCK" : "F_RDLCK"); +*/ + + ret = fcntl(*fd, F_SETLK, &lockthing); + if (ret == -1) { + if (verbous) { + sprintf(msg, "Device busy. Failed to fcntl-lock '%s'", + fd_name); + libdax_msgs_submit(libdax_messenger, -1, 0x00020008, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, errno, 0); + } + close(*fd); + *fd = -1; + + /* ts A70409 : DDLP-B */ + /* >>> release single lock on fd_name */ + + return(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 A70409 : DDLP-B */ + /* >>> obtain single lock on fname */ + + /* 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. + ts A70411 + Switched to O_NDELAY for LKML statement 2007/4/11/141 by Alan Cox: + "open() has side effects. The CD layer allows you to open + with O_NDELAY if you want to avoid them." + */ + if(burn_sg_open_o_nonblock) + open_mode |= O_NDELAY; + +/* <<< 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_NDELAY= %d\n", + !!(open_mode&O_EXCL),!!(open_mode&O_NDELAY)); +*/ + + 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_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, errno, 0); + return -1; + } + sg_fcntl_lock(&fd, fname, F_WRLCK, 1); + return fd; +} + + +/* ts A60926 */ +static int sg_release_siblings(int sibling_fds[], + char sibling_fnames[][BURN_OS_SG_MAX_NAMELEN], + int *sibling_count) +{ + int i; + char msg[81]; + + for(i= 0; i < *sibling_count; i++) + sg_close_drive_fd(sibling_fnames[i], -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_fnames, + &(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[], + char sibling_fnames[][BURN_OS_SG_MAX_NAMELEN], + 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]; + struct stat stbuf; + dev_t last_rdev = 0, path_rdev; + + static char tldev[][81]= {"/dev/sr%d", "/dev/scd%d", "/dev/sg%d", ""}; + /* ts A70609: removed "/dev/st%d" */ + + if(stat(path, &stbuf) == -1) + return 0; + path_rdev = stbuf.st_rdev; + + sg_select_device_family(); + 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_fnames, + 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); + if(stat(fname, &stbuf) == -1) + continue; + if (path_rdev == stbuf.st_rdev) + continue; + if (*sibling_count > 0 && last_rdev == stbuf.st_rdev) + continue; + 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>=BURN_OS_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; + strcpy(sibling_fnames[*sibling_count], fname); + (*sibling_count)++; + last_rdev= stbuf.st_rdev; + } + } + return 1; +failed:; + sg_release_siblings(sibling_fds, sibling_fnames, 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[BURN_OS_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]; + char sibling_fnames[BURN_OS_SG_MAX_SIBLINGS][BURN_OS_SG_MAX_NAMELEN]; + + sg_select_device_family(); + + 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)); + + if (sgio_test(fd) == -1) { + if (linux_sg_enumerate_debug) + fprintf(stderr, + "FATAL: sgio_test() failed: errno=%d '%s'", + errno, strerror(errno)); + + sg_close_drive_fd(fname, -1, &fd, 0); + continue; + } + +#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_fnames, + &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_fnames, + &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<BURN_OS_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; + + sg_select_device_family(); + 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 */ + if (mmc_function_spy(d, "sg_grab") <= 0) + return 0; + + + /* 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. + ts A70411 + Switched to O_NDELAY for LKML statement 2007/4/11/141 + */ + if(burn_sg_open_o_nonblock) + open_mode |= O_NDELAY; + + /* 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(NULL, "sg_grab ----------- opening"); + + /* ts A70409 : DDLP-B */ + /* >>> obtain single lock on d->devname */ + + /* 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_fnames,&(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; + if (fd >= 0) { + sg_fcntl_lock(&fd, d->devname, F_WRLCK, 1); + if (fd < 0) + goto drive_is_in_use; + } + } 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 */ + if (mmc_function_spy(d, "sg_release") <= 0) + return 0; + + 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(NULL, "sg_release ----------- closing"); + + sg_close_drive(d); + return 0; +} + + +/** ts A70518: + Debugging log facility. Controlled by existence of macros: + Libburn_log_sg_commandS enables logging to file + /tmp/libburn_sg_command_log + Libburn_fflush_log_sg_commandS enables fflush after each output line + Libburn_log_sg_command_stderR enables additional log to stderr +*/ +/* +#define Libburn_log_sg_commandS 1 +#define Libburn_fflush_log_sg_commandS 1 +#define Libburn_log_sg_command_stderR 1 +*/ + +#ifdef Libburn_log_sg_commandS + +/** Logs command (before execution) */ +static int sg_log_cmd(struct command *c, FILE *fp, int flag) +{ + int i; + + if (fp != NULL) { + for(i = 0; i < 16 && i < c->oplen; i++) + fprintf(fp,"%2.2x ", c->opcode[i]); + fprintf(fp, "\n"); +#ifdef Libburn_fflush_log_sg_commandS + fflush(fp); +#endif + } + if (fp == stderr) + return 1; +#ifdef Libburn_log_sg_command_stderR + sg_log_cmd(c, stderr, flag); +#endif + return 1; +} + + +/** logs outcome of a sg command. flag&1 causes an error message */ +static int sg_log_err(struct command *c, FILE *fp, + sg_io_hdr_t *s, + int flag) +{ + if(fp!=NULL) { + if(flag & 1) + fprintf(fp, + "+++ key=%X asc=%2.2Xh ascq=%2.2Xh (%6d ms)\n", + s->sbp[2], s->sbp[12], s->sbp[13],s->duration); + else + fprintf(fp,"%6d ms\n", s->duration); +#ifdef Libburn_fflush_log_sg_commandS + fflush(fp); +#endif + } + if (fp == stderr) + return 1; +#ifdef Libburn_log_sg_command_stderR + sg_log_err(c, stderr, s, flag); +#endif + return 1; +} + + +#endif /* Libburn_log_sg_commandS */ + + +/** 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; + +#ifdef Libburn_log_sg_commandS + /* ts A61030 */ + static FILE *fp= NULL; +#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(NULL, buf); + +#ifdef Libburn_log_sg_commandS + /* ts A61030 */ + if(fp==NULL) { + fp= fopen("/tmp/libburn_sg_command_log","a"); + fprintf(fp,"\n-----------------------------------------\n"); + } + sg_log_cmd(c,fp,0); +#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) { + + /* ts A70519 : kernel 2.4 usb-storage seems to + expect exact dxfer_len for data + fetching commands. + */ + if (c->dxfer_len >= 0) + s.dxfer_len = c->dxfer_len; + else + 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); + } else if (s.host_status != Libburn_sg_host_oK || + s.driver_status != Libburn_sg_driver_oK) { + char msg[161]; + + sprintf(msg, + "SCSI command %2.2Xh indicates host or driver error:", + (unsigned int) c->opcode[0]); + sprintf(msg+strlen(msg), + " host_status= %xh , driver_status= %xh", + (unsigned int) s.host_status, + (unsigned int) s.driver_status); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002013b, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + } + +#ifdef Libburn_log_sg_commandS + sg_log_err(c, fp, &s, c->error != 0); +#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, open_mode = O_RDONLY; + 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 */ + + /* ts A70409 : DDLP-B */ + /* >>> obtain single lock on path */ + + if(burn_sg_open_o_nonblock) + open_mode |= O_NDELAY; + if(burn_sg_open_o_excl) { + /* O_EXCL | O_RDONLY does not work with /dev/sg* on + SuSE 9.0 (kernel 2.4) and SuSE 9.3 (kernel 2.6) */ + /* so skip it for now */; + } + fd = open(path, open_mode); + if(fd < 0) + return 0; + sg_fcntl_lock(&fd, path, F_RDLCK, 0); + 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); +} + + +/* ts A70909 */ +/** Estimate the potential payload capacity of a file address. + @param path The address of the file to be examined. If it does not + exist yet, then the directory will be inquired. + @param bytes The pointed value gets modified, but only if an estimation is + possible. + @return -2 = cannot perform necessary operations on file object + -1 = neither path nor dirname of path exist + 0 = could not estimate size capacity of file object + 1 = estimation has been made, bytes was set +*/ +int burn_os_stdio_capacity(char *path, off_t *bytes) +{ + struct stat stbuf; + struct statvfs vfsbuf; + char testpath[4096], *cpt; + long blocks; + int open_mode = O_RDONLY, fd, ret; + off_t add_size = 0; + + testpath[0] = 0; + blocks = *bytes / 512; + if (stat(path, &stbuf) == -1) { + strcpy(testpath, path); + cpt = strrchr(testpath, '/'); + if(cpt == NULL) + strcpy(testpath, "."); + else if(cpt == testpath) + testpath[1] = 0; + else + *cpt = 0; + if (stat(testpath, &stbuf) == -1) + return -1; + } else if(S_ISBLK(stbuf.st_mode)) { + fd = open(path, open_mode); + if (fd == -1) + return -2; + ret = ioctl(fd, BLKGETSIZE, &blocks); + close(fd); + if (ret == -1) + return -2; + *bytes = ((off_t) blocks) * (off_t) 512; + } else if(S_ISREG(stbuf.st_mode)) { + add_size = stbuf.st_blocks * (off_t) 512; + strcpy(testpath, path); + } else + return 0; + + if (testpath[0]) { + if (statvfs(testpath, &vfsbuf) == -1) + return -2; + *bytes = add_size + ((off_t) vfsbuf.f_bsize) * + (off_t) vfsbuf.f_bavail; + } + return 1; +} + diff --git a/libburn/branches/ZeroFourTwo/libburn/sg.c b/libburn/branches/ZeroFourTwo/libburn/sg.c new file mode 100644 index 00000000..79b78d96 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/sg.h b/libburn/branches/ZeroFourTwo/libburn/sg.h new file mode 100644 index 00000000..f1de7a2b --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/sg.h @@ -0,0 +1,36 @@ +/* -*- 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); + +int burn_os_stdio_capacity(char *path, off_t *bytes); + +#endif /* __SG */ diff --git a/libburn/branches/ZeroFourTwo/libburn/source.c b/libburn/branches/ZeroFourTwo/libburn/source.c new file mode 100644 index 00000000..17ecee4a --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/source.c @@ -0,0 +1,55 @@ +/* -*- 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) +{ + 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)); + + /* ts A70825 */ + if (out == NULL) + return NULL; + memset((char *) out, 0, sizeof(struct burn_source)); + + out->refcount = 1; + return out; +} + + +/* ts A71223 */ +int burn_source_cancel(struct burn_source *src) +{ + if(src->read == NULL) + if(src->version > 0) + if(src->cancel != NULL) + src->cancel(src); + return 1; +} + diff --git a/libburn/branches/ZeroFourTwo/libburn/source.h b/libburn/branches/ZeroFourTwo/libburn/source.h new file mode 100644 index 00000000..1f31527c --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/source.h @@ -0,0 +1,10 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SOURCE +#define __SOURCE + +struct burn_source *burn_source_new(void); + +int burn_source_cancel(struct burn_source *src); + +#endif /*__SOURCE*/ diff --git a/libburn/branches/ZeroFourTwo/libburn/spc.c b/libburn/branches/ZeroFourTwo/libburn/spc.c new file mode 100644 index 00000000..107abacb --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/spc.c @@ -0,0 +1,997 @@ +/* -*- 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; + +/* ts A70910 + debug: for tracing calls which might use open drive fds + or for catching SCSI usage of emulated drives. */ +int mmc_function_spy(struct burn_drive *d, char * text); + + +/* spc command set */ +/* ts A70519 : allocation length byte 3+4 was 0,255 */ +static unsigned char SPC_INQUIRY[] = { 0x12, 0, 0, 0, 36, 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 }; + + +/* ts A70519 : An initializer for the abstract SCSI command structure */ +int scsi_init_command(struct command *c, unsigned char *opcode, int oplen) +{ + if (oplen > 16) + return 0; + memcpy(c->opcode, opcode, oplen); + c->oplen = oplen; + c->dir = NO_TRANSFER; + c->dxfer_len = -1; + memset(c->sense, 0, sizeof(c->sense)); + c->error = 0; + c->retry = 0; + c->page = NULL; + return 1; +} + + +int spc_test_unit_ready_r(struct burn_drive *d, int *key, int *asc, int *ascq) +{ + struct command c; + + if (mmc_function_spy(d, "test_unit_ready") <= 0) + return 0; + + scsi_init_command(&c, SPC_TEST_UNIT_READY,sizeof(SPC_TEST_UNIT_READY)); +/* + c.oplen = sizeof(SPC_TEST_UNIT_READY); + memcpy(c.opcode, SPC_TEST_UNIT_READY, sizeof(SPC_TEST_UNIT_READY)); + c.page = NULL; +*/ + c.retry = 0; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); + if (c.error) { + *key= c.sense[2]; + *asc= c.sense[12]; + *ascq= c.sense[13]; + return (c.sense[2] & 0xF) == 0; + } + return 1; +} + +int spc_test_unit_ready(struct burn_drive *d) +{ + int key,asc,ascq; + + return spc_test_unit_ready_r(d, &key, &asc, &ascq); +} + + +/* ts A70315 */ +/** @param flag bit0=do not wait 0.1 seconds before first test unit ready */ +/** Wait until the drive state becomes clear or until max_usec elapsed */ +int spc_wait_unit_attention(struct burn_drive *d, int max_sec, char *cmd_text, + int flag) +{ + int i, ret = 1, key = 0, asc = 0, ascq = 0; + char msg[160]; + + if (!(flag & 1)) + usleep(100000); + for(i = !(flag & 1); i < max_sec * 10; i++) { + ret = spc_test_unit_ready_r(d, &key, &asc, &ascq); + +/* <<< + fprintf(stderr, +"libburn_EXPERIMENTAL: i= %d ret= %d key= %X asc= %2.2X ascq= %2.2X\n", + i, ret, (unsigned) key, (unsigned) asc, (unsigned) ascq); +*/ + + if(ret > 0) /* ready */ + break; + if(key!=0x2 || asc!=0x4) { + if (key == 0x2 && asc == 0x3A) { + ret = 1; /* medium not present = ok */ +/* <<< + ts A70912 : + My LG GSA-4082B on asynchronous load: + first it reports no media 2,3A,00, + then it reports not ready 2,04,00, + further media inquiry retrieves wrong data + + if(i<=100) + goto slumber; +*/ + break; + } + if (key == 0x6 && asc == 0x28 && ascq == 0x00) + /* media change notice = try again */ + goto slumber; + + sprintf(msg, + "Asynchromous SCSI error on %s: key=%X asc=%2.2Xh ascq=%2.2Xh", + cmd_text, (unsigned) key, (unsigned) asc, + (unsigned) ascq); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002014d, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + d->cancel = 1; + break; + } +slumber:; + usleep(100000); + } + + sprintf(msg, "Async %s %s after %d.%d seconds", + cmd_text, (ret > 0 ? "succeeded" : "failed"), i / 10, i % 10); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020150, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_LOW, msg, 0, 0); + + if (i < max_sec * 10) + return (ret > 0); + + sprintf(msg, "Timeout (%d s) with asynchronous SCSI command %s\n", + max_sec, cmd_text); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002014f, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); + return 0; +} + + +void spc_request_sense(struct burn_drive *d, struct buffer *buf) +{ + struct command c; + + if (mmc_function_spy(d, "request_sense") <= 0) + return; + + scsi_init_command(&c, SPC_REQUEST_SENSE, sizeof(SPC_REQUEST_SENSE)); + c.retry = 0; +/* + c.oplen = sizeof(SPC_REQUEST_SENSE); + memcpy(c.opcode, SPC_REQUEST_SENSE, sizeof(SPC_REQUEST_SENSE)); +*/ + c.dxfer_len= c.opcode[4]; + c.retry = 0; + 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; + + if (mmc_function_spy(d, "get_erase_progress") <= 0) + return 0; + + 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; + + if (mmc_function_spy(d, "inquiry") <= 0) + return; + + scsi_init_command(&c, SPC_INQUIRY, sizeof(SPC_INQUIRY)); +/* + memcpy(c.opcode, SPC_INQUIRY, sizeof(SPC_INQUIRY)); + c.oplen = sizeof(SPC_INQUIRY); +*/ + c.dxfer_len= (c.opcode[3] << 8) | c.opcode[4]; + c.retry = 1; + 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; + memset(id->vendor, 0, 9); + memset(id->product, 0, 17); + memset(id->revision, 0, 5); + if (c.error) { + id->valid = -1; + return; + } + 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; + + if (mmc_function_spy(d, "prevent") <= 0) + return; + + scsi_init_command(&c, SPC_PREVENT, sizeof(SPC_PREVENT)); +/* + memcpy(c.opcode, SPC_PREVENT, sizeof(SPC_PREVENT)); + c.oplen = sizeof(SPC_PREVENT); + c.page = NULL; +*/ + c.retry = 1; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void spc_allow(struct burn_drive *d) +{ + struct command c; + + if (mmc_function_spy(d, "allow") <= 0) + return; + + scsi_init_command(&c, SPC_ALLOW, sizeof(SPC_ALLOW)); +/* + memcpy(c.opcode, SPC_ALLOW, sizeof(SPC_ALLOW)); + c.oplen = sizeof(SPC_ALLOW); + c.page = NULL; +*/ + c.retry = 1; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +/* +ts A70518 : Do not call with *alloc_len < 8 +*/ +/** flag&1= do only inquire alloc_len */ +static int spc_sense_caps_al(struct burn_drive *d, int *alloc_len, int flag) +{ + struct buffer buf; + struct scsi_mode_data *m; + int size, page_length, num_write_speeds = 0, i, speed, ret; + int old_alloc_len, was_error = 0; + 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; + + if (*alloc_len < 8) + return 0; + + memset(&buf, 0, sizeof(buf)); + scsi_init_command(&c, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); +/* + memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); + c.oplen = sizeof(SPC_MODE_SENSE); +*/ + c.dxfer_len = *alloc_len; + c.opcode[7] = (c.dxfer_len >> 8) & 0xff; + c.opcode[8] = c.dxfer_len & 0xff; + c.retry = 1; + c.opcode[2] = 0x2A; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + if (c.error) { + memset(&buf, 0, sizeof(buf)); + d->mdata->valid = -1; + was_error = 1; + } + + 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]; + old_alloc_len = *alloc_len; + *alloc_len = page_length + 8; + if (flag & 1) + return !was_error; + if (page_length + 8 > old_alloc_len) + page_length = old_alloc_len - 8; + if (page_length < 22) + return 0; + + 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; + + if (32 + 4 * num_write_speeds > page_length + 2) { + char msg[161]; + + sprintf(msg, "Malformed capabilities page 2Ah received (len=%d, #speeds=%d)", page_length, num_write_speeds); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002013c, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return 0; + } + + 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); + return !was_error; +} + + +void spc_sense_caps(struct burn_drive *d) +{ + int alloc_len, start_len = 22, ret; + + if (mmc_function_spy(d, "sense_caps") <= 0) + return; + + /* first command execution to learn Allocation Length */ + alloc_len = start_len; + ret = spc_sense_caps_al(d, &alloc_len, 1); +/* + fprintf(stderr,"LIBBURN_DEBUG: 5Ah alloc_len = %d , ret = %d\n", + alloc_len, ret); +*/ + if (alloc_len >= start_len && ret > 0) + /* second execution with announced length */ + spc_sense_caps_al(d, &alloc_len, 0); +} + + +void spc_sense_error_params(struct burn_drive *d) +{ + struct buffer buf; + struct scsi_mode_data *m; + int size, alloc_len = 12 ; + unsigned char *page; + struct command c; + + if (mmc_function_spy(d, "sense_error_params") <= 0) + return; + + scsi_init_command(&c, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); +/* + memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); + c.oplen = sizeof(SPC_MODE_SENSE); +*/ + c.dxfer_len = alloc_len; + c.opcode[7] = (c.dxfer_len >> 8) & 0xff; + c.opcode[8] = c.dxfer_len & 0xff; + c.retry = 1; + 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; + + if (mmc_function_spy(d, "select_error_params") <= 0) + return; + + scsi_init_command(&c, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); +/* + memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); + c.oplen = sizeof(SPC_MODE_SELECT); +*/ + c.retry = 1; + 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, dummy, alloc_len = 10; + unsigned char *page; + struct command c; + + if (mmc_function_spy(d, "sense_write_params") <= 0) + return; + + /* 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); */ + + scsi_init_command(&c, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); +/* + memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); + c.oplen = sizeof(SPC_MODE_SENSE); +*/ + c.dxfer_len = alloc_len; + c.opcode[7] = (c.dxfer_len >> 8) & 0xff; + c.opcode[8] = c.dxfer_len & 0xff; + c.retry = 1; + c.opcode[2] = 0x05; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + /* ts A71128 : do not interpret reply if error */ + m = d->mdata; + if(!c.error) { + size = c.page->data[0] * 256 + c.page->data[1]; + 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; + } else + m->write_page_valid = 0; + mmc_read_disc_info(d); + + /* ts A70212 : try to setup d->media_capacity_remaining */ + if (d->current_profile == 0x1a || d->current_profile == 0x13 || + d->current_profile == 0x12) + d->read_format_capacities(d, -1); + else if (d->status == BURN_DISC_BLANK || + (d->current_is_cd_profile && d->status == BURN_DISC_APPENDABLE)) { + d->get_nwa(d, -1, &dummy, &dummy); + } + /* others are hopefully up to date from mmc_read_disc_info() */ + +/* + fprintf(stderr, "LIBBURN_DEBUG: media_capacity_remaining = %.f\n", + (double) d->media_capacity_remaining); +*/ + +} + + +/* 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 is done by mmc_compose_mode_page_5(). +*/ +void spc_select_write_params(struct burn_drive *d, + const struct burn_write_opts *o) +{ + struct buffer buf; + struct command c; + + if (mmc_function_spy(d, "select_write_params") <= 0) + return; + + /* 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)); + */ + + scsi_init_command(&c, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); +/* + memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); + c.oplen = sizeof(SPC_MODE_SELECT); +*/ + c.retry = 1; + 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); + + /* ts A61229 */ + if (mmc_compose_mode_page_5(d, o, c.page->data + 8) <= 0) + return; + + c.dir = TO_DRIVE; + d->issue_command(d, &c); +} + +void spc_getcaps(struct burn_drive *d) +{ + if (mmc_function_spy(d, "getcaps") <= 0) + return; + + spc_inquiry(d); + spc_sense_caps(d); + spc_sense_error_params(d); +} + +/* +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, useable_write_type = -1, useable_block_type = -1; + int last_try = 0; + struct command c; + + if (mmc_function_spy(d, "spc_probe_write_modes") <= 0) + return; + + /* ts A70213 : added pseudo try_write_type 4 to set a suitable mode */ + while (try_write_type != 5) { + burn_print(9, "trying %d, %d\n", try_write_type, + try_block_type); + + /* ts A70213 */ + if (try_write_type == 4) { + /* Pseudo write type NONE . Set a useable write mode */ + if (useable_write_type == -1) + break; + try_write_type = useable_write_type; + try_block_type = useable_block_type; + last_try= 1; + } + scsi_init_command(&c, SPC_MODE_SELECT,sizeof(SPC_MODE_SELECT)); +/* + memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); + c.oplen = sizeof(SPC_MODE_SELECT); +*/ + c.retry = 1; + 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; + + if (last_try) + break; + + 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; + + /* ts A70213 */ + if ((useable_write_type < 0 && try_write_type > 0) || + (try_write_type == 1 && try_block_type == 8)) { + /* Packet is not supported yet. + Prefer TAO MODE_1. */ + useable_write_type = try_write_type; + useable_block_type = 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 = calloc(1, sizeof(struct burn_scsi_inquiry_data)); + d->mdata = calloc(1, sizeof(struct scsi_mode_data)); + + /* 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; + } + d->idata->valid = 0; + d->mdata->valid = 0; + d->mdata->speed_descriptors = NULL; + 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/branches/ZeroFourTwo/libburn/spc.h b/libburn/branches/ZeroFourTwo/libburn/spc.h new file mode 100644 index 00000000..81d2d469 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/spc.h @@ -0,0 +1,62 @@ +/* -*- 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); + +/* ts A70315 : test_unit_ready with result parameters */ +int spc_test_unit_ready_r(struct burn_drive *d, int *key, int *asc, int *ascq); + +int spc_test_unit_ready(struct burn_drive *d); + +/* ts A70315 */ +/** Wait until the drive state becomes clear in or until max_sec elapsed */ +int spc_wait_unit_attention(struct burn_drive *d, int max_sec, char *cmd_text, + int flag); + +/* 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); + +/* ts A70519 */ +int scsi_init_command(struct command *c, unsigned char *opcode, int oplen); + +#endif /*__SPC*/ diff --git a/libburn/branches/ZeroFourTwo/libburn/structure.c b/libburn/branches/ZeroFourTwo/libburn/structure.c new file mode 100644 index 00000000..40897b94 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/structure.c @@ -0,0 +1,517 @@ + +/* 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)); + if (d == NULL) /* ts A70825 */ + return NULL; + 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)); + if (s == NULL) /* ts A70825 */ + return NULL; + 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)); + if (t == NULL) /* ts A70825 */ + return NULL; + 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; + + /* ts A70213 */ + t->fill_up_media = 0; + /* ts A70218 */ + t->default_size = 0; + + 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) +{ + /* ts A70125 : was int */ + off_t 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 A70125 */ +int burn_track_set_sectors(struct burn_track *t, int sectors) +{ + off_t size, seclen; + int ret; + + seclen = burn_sector_length(t->mode); + size = seclen * (off_t) sectors - (off_t) t->offset - (off_t) t->tail; + if (size < 0) + return 0; + ret = t->source->set_size(t->source, size); + t->open_ended = (t->source->get_size(t->source) <= 0); + return ret; +} + + +/* ts A70218 , API since A70328 */ +int burn_track_set_size(struct burn_track *t, off_t size) +{ + if (t->source == NULL) + return 0; + if (t->source->set_size == NULL) + return 0; + t->open_ended = (size <= 0); + return t->source->set_size(t->source, size); +} + + +/* ts A70213 */ +int burn_track_set_fillup(struct burn_track *t, int fill_up_media) +{ + t->fill_up_media = fill_up_media; + if (fill_up_media) + t->open_ended = 0; + return 1; +} + + +/* ts A70213 */ +/** + @param flag bit0= force new size even if existing track size is larger +*/ +int burn_track_apply_fillup(struct burn_track *t, off_t max_size, int flag) +{ + int max_sectors, ret = 2; + char msg[160]; + + if (t->fill_up_media <= 0) + return 2; + max_sectors = max_size / 2048; + if (burn_track_get_sectors(t) < max_sectors || (flag & 1)) { + sprintf(msg, "Setting total track size to %ds (payload %ds)\n", + max_sectors, (int) (t->source->get_size(t->source)/2048)); + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + ret = burn_track_set_sectors(t, max_sectors); + t->open_ended = 0; + } + return ret; +} + + +/* ts A61031 */ +int burn_track_is_open_ended(struct burn_track *t) +{ + return !!t->open_ended; +} + + +/* ts A70218 : API */ +int burn_track_set_default_size(struct burn_track *t, off_t size) +{ + t->default_size = size; + return 1; +} + + +/* ts A70218 */ +off_t burn_track_get_default_size(struct burn_track *t) +{ + return t->default_size; +} + + +/* 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/branches/ZeroFourTwo/libburn/structure.h b/libburn/branches/ZeroFourTwo/libburn/structure.h new file mode 100644 index 00000000..89f25d5e --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/structure.h @@ -0,0 +1,112 @@ +#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; + + /* ts A70213 : wether to expand this track to full available media */ + int fill_up_media; + + /* ts A70218 : a track size to use if it is mandarory to have some */ + off_t default_size; + + /** 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); + +/* ts A70125 : sets overall sectors of a track: offset+payload+padding */ +int burn_track_set_sectors(struct burn_track *t, int sectors); + +/* ts A70218 : sets the payload size alone */ +int burn_track_set_size(struct burn_track *t, off_t size); + +/* ts A70213 */ +int burn_track_set_fillup(struct burn_track *t, int fill_up_media); +int burn_track_apply_fillup(struct burn_track *t, off_t max_size, int flag); + +/* ts A70218 */ +off_t burn_track_get_default_size(struct burn_track *t); + + +#endif /* BURN__STRUCTURE_H */ diff --git a/libburn/branches/ZeroFourTwo/libburn/toc.c b/libburn/branches/ZeroFourTwo/libburn/toc.c new file mode 100644 index 00000000..7a9bbbc3 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/toc.c @@ -0,0 +1,141 @@ +/* -*- 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 { + + t->mode = BURN_MODE1; +/* ts A70519 : this does not work with Linux 2.4 USB because one cannot + predict the exact dxfer_size without knowing the sector type. + mem.sectors = 1; + d->read_sectors(d, lba, mem.sectors, &o, &mem); + t->mode = sector_identify(mem.data); +*/ + } + } +} diff --git a/libburn/branches/ZeroFourTwo/libburn/toc.h b/libburn/branches/ZeroFourTwo/libburn/toc.h new file mode 100644 index 00000000..1d804e75 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/transport.h b/libburn/branches/ZeroFourTwo/libburn/transport.h new file mode 100644 index 00000000..3b1a33cd --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/transport.h @@ -0,0 +1,351 @@ +/* -*- 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; + int dxfer_len; + 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 int tdp; +}; + + +/** Gets initialized in enumerate_common() and burn_drive_register() */ +struct burn_drive +{ + /* ts A70902: + 0=null-emulation + 1=MMC drive , + 2=stdio random read-write + 3=stdio sequential write-only + */ + int drive_role; + + int bus_no; + int host; + int id; + int channel; + int lun; + char *devname; + + /* ts A70302: mmc5r03c.pdf 5.3.2 Physical Interface Standard */ + int phys_if_std; /* 1=SCSI, 2=ATAPI, 3,4,6=FireWire, 7=SATA, 8=USB */ + char phys_if_name[80]; /* MMC-5 5.3.2 table 91 , e.g. "SCSI Family" */ + + /* 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 A70128 : MMC-to-MMC feature info from 46h for DVD-RW. + Quite internal. Regard as opaque :) + */ + /* 1 = incremental recording available, 0 = not available */ + int current_has_feat21h; + + /* Link Size item number 0 from feature 0021h descriptor */ + int current_feat21h_link_size; + + /* Flags from feature 002Fh feature descriptor mmc5r03c.pdf 5.3.25 : + bit1= DVD-RW supported + bit2= Test Write available + bit3= DVD-R DL supported + bit6= Buffer Under-run Free recording available (page 05h BUFE) + Value -1 indicates that no 002Fh was current in the features list. + */ + int current_feat2fh_byte4; + + /* ts A70114 : whether a DVD-RW media holds an incomplete session + (which could need closing after write) */ + int needs_close_session; + /* ts A71003 : whether a random write operation was done and no + synchronize chache has happened yet */ + int needs_sync_cache; + + /* ts A61218 from 51h READ DISC INFORMATION */ + 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 int format_curr_blsas; /* dito */ + 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 stdio_fd; + + int nwa; /* next writeable address */ + int alba; /* absolute lba */ + int rlba; /* relative lba in section */ + int start_lba; + int end_lba; + + + /* ts A70131 : from 51h READ DISC INFORMATION Number of Sessions (-1)*/ + int complete_sessions; + /* ts A70129 : + from 51h READ DISC INFORMATION Last Track Number in Last Session */ + int last_track_no; + /* ts A70212 : from various sources : free space on media (in bytes) + With CD this might change after particular write + parameters have been set and nwa has been inquired. + (e.g. by d->send_write_parameters() ; d->get_nwa()). + */ + off_t media_capacity_remaining; + /* ts A70215 : if > 0 : first lba on media that is too high for write*/ + int media_lba_limit; + + + int toc_temp; + struct burn_disc *disc; /* disc structure */ + int block_types[4]; + struct buffer *buffer; + struct burn_progress progress; + + /* ts A70711 : keeping an eye on the drive buffer */ + off_t pessimistic_buffer_free; + int pbf_altered; + int wait_for_buffer_free; + int nominal_write_speed; + unsigned int wfb_min_usec; + unsigned int wfb_max_usec; + unsigned int wfb_timeout_sec; + unsigned int wfb_min_percent; + unsigned int wfb_max_percent; + unsigned int pessimistic_writes; + unsigned int waited_writes; + unsigned int waited_tries; + unsigned int waited_usec; + + volatile int cancel; + volatile enum burn_drive_status busy; + + /* ts A70929 */ + pid_t thread_pid; + int thread_pid_valid; + + +/* 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 *); + + /* ts A70205 : Announce size of a DVD-R[W] DAO session. */ + int (*reserve_track) (struct burn_drive *d, off_t size); + + 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 A70131 : obtain (possibly fake) TOC number and start lba of + first track in last complete session */ + int (*read_multi_session_c1)(struct burn_drive *d, + int *trackno, int *start); + + /* 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); + + /* ts A70812 */ + /* mmc5r03c.pdf 6.15 : read data sectors (start and amount in LBA) */ + int (*read_10) (struct burn_drive *d, int start, int amount, + struct buffer *buf); + +}; + +/* end of generic 'drive' data structures */ + +#endif /* __TRANSPORT */ diff --git a/libburn/branches/ZeroFourTwo/libburn/util.c b/libburn/branches/ZeroFourTwo/libburn/util.c new file mode 100644 index 00000000..b759dc37 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/util.h b/libburn/branches/ZeroFourTwo/libburn/util.h new file mode 100644 index 00000000..4e74895d --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/libburn/write.c b/libburn/branches/ZeroFourTwo/libburn/write.c new file mode 100644 index 00000000..e6715fdb --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/write.c @@ -0,0 +1,2282 @@ +/* -*- 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 <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/time.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 "structure.h" +#include "source.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; +} + + +/* ts A71002 : outsourced from burn_write_flush() : no sync cache here */ +int burn_write_flush_buffer(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; + } + return 1; +} + + +int burn_write_flush(struct burn_write_opts *o, struct burn_track *track) +{ + int ret; + struct burn_drive *d = o->drive; + + ret = burn_write_flush_buffer(o, track); + if (ret <= 0) + return ret; + d->sync_cache(d); + return 1; +} + + +/* ts A71002 : outsourced from burn_write_close_track() */ +int burn_write_track_minsize(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; + + 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; + } + 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; + + /* ts A61106 */ +#ifdef Libburn_experimental_no_close_tracK + return 1; +#endif + + d = o->drive; + t = s->track[tnum]; + + 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. */ + /* ts A70129 + Probably the right value for appendables is d->last_track_no + */ + d->close_track_session(o->drive, 0, 0xff); + + 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, track_length; + 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; + + /* ts A70121 : The pause before the first track is not a Pre-gap. + To count it as part 2 of a Pre-gap is a dirty hack. It also seems + to have caused confusion in dealing with part 1 of an eventual + real Pre-gap. mmc5r03c.pdf 6.33.3.2, 6.33.3.18 . + */ + tar[0]->pregap2 = 1; + + pform = form; + for (i = 0; i < ntr; i++) { + type_to_form(tar[i]->mode, &ctladr, &form); + + + /* ts A70121 : This seems to be thw wrong test. Correct would + be to compare tar[]->mode or bit2 of ctladr. + */ + + 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; +*/ +/* ts A70121 : it is unclear why (form & BURN_AUDIO) should prevent pregap1. + I believe, correct would be: + runtime += 75; + tar[i]->pregap1 = 1; + + The test for pform != form is wrong anyway. + + Next one has to care for Post-gap: table 555 in mmc5r03c.pdf does not + show any although 6.33.3.19 would prescribe some. + + Nobody seems to have ever tested this situation, up to now. + It is banned for now in burn_disc_write(). + Warning have been placed in libburn.h . +*/ + + 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; + + /* ts A70125 : + Still not understanding the sense behind linking tracks, + i decided to at least enforce the MMC specs' minimum + track length. + */ + track_length = burn_track_get_sectors(tar[i]); + if (track_length < 300 && !burn_track_is_open_ended(tar[i])) { + track_length = 300; + if (!tar[i]->pad) + tar[i]->pad = 1; + burn_track_set_sectors(tar[i], track_length); + } + runtime += track_length; + +/* 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; + /* ts A70121 : Removed pseudo suicidal initializer + = s->track[tnum - 1]; + */ + + if (tnum == 0) { + + /* ts A70121 : This is not possible because + track 1 cannot have a pregap at all. + MMC-5 6.33.3.2 precribes a mandatory pause + prior to any track 1. Pre-gap is prescribed + for mode changes like audio-to-data. + To set burn_track.pregap1 for track 1 is + kindof a dirty hack. + */ + + printf("first track should not have a pregap1\n"); + pt = t; + } else + pt = s->track[tnum - 1]; /* ts A70121 */ + 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); + + /* ts A70213: CD-TAO: eventually expand size of track to max */ + burn_track_apply_fillup(t, d->media_capacity_remaining, 0); + + /* <<< */ + sprintf(msg, + "TAO pre-track %2.2d : get_nwa(%d)=%d, d=%d , demand=%.f , cap=%.f\n", + tnum+1, nwa, ret, d->nwa, (double) burn_track_get_sectors(t) * 2048.0, + (double) d->media_capacity_remaining); + 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 (d->cancel) + burn_source_cancel(t->source); + if (o->write_type == BURN_WRITE_TAO) { + + /* ts A71002 */ + if (!burn_write_flush_buffer(o, t)) + ret = 0; + + /* Ensure that at least 600 kB get written */ + burn_write_track_minsize(o, s, tnum); + d->sync_cache(d); + + /* ts A61030 */ + if (burn_write_close_track(o, s, tnum) <= 0) + ret = 0; + } + return ret; +} + +/* ts A61009 */ +/* @param flag bit1 = do not libdax_msgs_submit() */ +int burn_disc_write_is_ok(struct burn_write_opts *o, struct burn_disc *disc, + int flag) +{ + 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); + if (!(flag & 2)) + 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; + struct burn_track *t = NULL; + int sx, tx; + + 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; + + /* ts A70711 */ + d->pessimistic_buffer_free = 0; + d->pbf_altered = 0; + d->pessimistic_writes = 0; + d->waited_writes = 0; + d->waited_tries = 0; + d->waited_usec = 0; + + /* Set eventual media fill up for last track only */ + for (sx = 0; sx < disc->sessions; sx++) + for (tx = 0 ; tx < disc->session[sx]->tracks; tx++) { + t = disc->session[sx]->track[tx]; + burn_track_set_fillup(t, 0); + } + if (o->fill_up_media && t != NULL) + burn_track_set_fillup(t, 1); + + d->busy = BURN_DRIVE_WRITING; + + return 1; +} + + +/* ts A70219 : API */ +int burn_precheck_write(struct burn_write_opts *o, struct burn_disc *disc, + char reasons[BURN_REASONS_LEN], int silent) +{ + enum burn_write_types wt; + struct burn_drive *d = o->drive; + char msg[160], *reason_pt; + int no_media = 0; + + reason_pt= reasons; + reasons[0] = 0; + + if (d->drive_role == 0) { + sprintf(reasons, + "DRIVE: is a virtual placeholder (null-drive)"); + no_media = 1; + goto ex; + } + + /* check write mode against write job */ + wt = burn_write_opts_auto_write_type(o, disc, reasons, 1); + if (wt == BURN_WRITE_NONE) { + if (strncmp(reasons, "MEDIA: ", 7)==0) + no_media = 1; + goto ex; + } + + sprintf(reasons, "%s: ", d->current_profile_text); + reason_pt= reasons + strlen(reasons); + if (d->status == BURN_DISC_UNSUITABLE) + goto unsuitable_profile; + if (d->drive_role == 2 || + d->current_profile == 0x1a || d->current_profile == 0x12) { + /* DVD+RW , DVD-RAM , emulated drive on stdio file */ + if (o->start_byte >= 0 && (o->start_byte % 2048)) + strcat(reasons, + "write start address not properly aligned to 2048, "); + } else if (d->current_profile == 0x09 || d->current_profile == 0x0a) { + /* CD-R , CD-RW */ + if (!burn_disc_write_is_ok(o, disc, (!!silent) << 1)) + strcat(reasons, "unsuitable track mode found, "); + if (o->start_byte >= 0) + strcat(reasons, "write start address not supported, "); + } else if (d->current_profile == 0x13) { + /* DVD-RW Restricted Overwrite */ + if (o->start_byte >= 0 && (o->start_byte % 32768)) + strcat(reasons, + "write start address not properly aligned to 32k, "); + } else if (d->drive_role == 3 || + d->current_profile == 0x11 || d->current_profile == 0x14 || + d->current_profile == 0x15 || + d->current_profile == 0x1b || d->current_profile == 0x2b ) { + /* DVD-R* Sequential , DVD+R[/DL] , sequential stdio "drive" */ + if (o->start_byte >= 0) + strcat(reasons, "write start address not supported, "); + } else { +unsuitable_profile:; + sprintf(msg, "Unsuitable media detected. Profile %4.4Xh %s", + d->current_profile, d->current_profile_text); + if (!silent) + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002011e, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + strcat(reasons, "no suitable media profile detected, "); + return 0; + } +ex:; + if (reason_pt[0]) { + if (no_media) { + if (!silent) + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x0002013a, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "No suitable media detected", 0, 0); + return -1; + } + if (!silent) + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020139, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Write job parameters are unsuitable", 0, 0); + return 0; + } + return 1; +} + + +/* ts A70129 : learned much from dvd+rw-tools-7.0/growisofs_mmc.cpp */ +int burn_disc_open_track_dvd_minus_r(struct burn_write_opts *o, + struct burn_session *s, int tnum) +{ + struct burn_drive *d = o->drive; + char msg[160]; + int ret, lba, nwa; + off_t size; + + d->send_write_parameters(d, o); + ret = d->get_nwa(d, -1, &lba, &nwa); + sprintf(msg, + "DVD pre-track %2.2d : get_nwa(%d), ret= %d , d->nwa= %d", + 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; + /* ts A70214 : eventually adjust already expanded size of track */ + burn_track_apply_fillup(s->track[tnum], d->media_capacity_remaining,1); + + if (o->write_type == BURN_WRITE_SAO) { /* DAO */ + /* Round track size up to 32 KiB and reserve track */ + size = ((off_t) burn_track_get_sectors(s->track[tnum])) + * (off_t) 2048; + size = (size + (off_t) 0x7fff) & ~((off_t) 0x7fff); + ret = d->reserve_track(d, size); + if (ret <= 0) { + sprintf(msg, "Cannot reserve track of %.f bytes", + (double) size); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020138, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return 0; + } + } + return 1; +} + + +/* ts A70226 */ +int burn_disc_open_track_dvd_plus_r(struct burn_write_opts *o, + struct burn_session *s, int tnum) +{ + struct burn_drive *d = o->drive; + char msg[160]; + int ret, lba, nwa; + off_t size; + + ret = d->get_nwa(d, -1, &lba, &nwa); + sprintf(msg, + "DVD+R pre-track %2.2d : get_nwa(%d), ret= %d , d->nwa= %d", + 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; + /* ts A70214 : eventually adjust already expanded size of track */ + burn_track_apply_fillup(s->track[tnum], d->media_capacity_remaining,1); + + if (o->write_type == BURN_WRITE_SAO && + ! burn_track_is_open_ended(s->track[tnum])) { + /* Round track size up to 32 KiB and reserve track */ + size = ((off_t) burn_track_get_sectors(s->track[tnum])) + * (off_t) 2048; + size = (size + (off_t) 0x7fff) & ~((off_t) 0x7fff); + ret = d->reserve_track(d, size); + if (ret <= 0) { + sprintf(msg, "Cannot reserve track of %.f bytes", + (double) size); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020138, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return 0; + } + } + return 1; +} + + +/* ts A70129 */ +int burn_disc_close_track_dvd_minus_r(struct burn_write_opts *o, + struct burn_session *s, int tnum) +{ + struct burn_drive *d = o->drive; + char msg[80]; + + /* only with Incremental writing */ + if (o->write_type != BURN_WRITE_TAO) + return 2; + + sprintf(msg, "Closing track %2.2d (absolute track number %d)", + tnum + 1, d->last_track_no); + libdax_msgs_submit(libdax_messenger, o->drive->global_index,0x00020119, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0); + + d->busy = BURN_DRIVE_CLOSING_SESSION; + /* Ignoring tnum here and hoping that d->last_track_no is correct */ + d->close_track_session(d, 0, d->last_track_no); /* CLOSE TRACK, 001b */ + d->busy = BURN_DRIVE_WRITING; + d->last_track_no++; + return 1; +} + + +/* ts A70229 */ +int burn_disc_finalize_dvd_plus_r(struct burn_write_opts *o) +{ + struct burn_drive *d = o->drive; + + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + "Finalizing DVD+R ...", 0, 0); + + /* CLOSE SESSION, 101b, Finalize with minimal radius */ + d->close_track_session(d, 2, 1); /* (2<<1)|1 = 5 */ + + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + "... finalizing DVD+R done ", 0, 0); + + return 1; +} + + +/* ts A70226 */ +int burn_disc_close_track_dvd_plus_r(struct burn_write_opts *o, + struct burn_session *s, int tnum, int is_last_track) +{ + struct burn_drive *d = o->drive; + char msg[80]; + + sprintf(msg, + "Closing track %2.2d (absolute track and session number %d)", + tnum + 1, d->last_track_no); + libdax_msgs_submit(libdax_messenger, o->drive->global_index,0x00020119, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0); + + d->busy = BURN_DRIVE_CLOSING_SESSION; + d->close_track_session(d, 0, d->last_track_no); /* CLOSE TRACK, 001b */ + + /* Each session becomes a single logical track. So to distinguish them, + it is mandatory to close the session together with each track. */ + + if (is_last_track && !o->multi) + burn_disc_finalize_dvd_plus_r(o); + else + d->close_track_session(d, 1, 0); /* CLOSE SESSION, 010b */ + d->busy = BURN_DRIVE_WRITING; + d->last_track_no++; + return 1; +} + + +/* ts A61218 - A70129 */ +int burn_dvd_write_track(struct burn_write_opts *o, + struct burn_session *s, int tnum, int is_last_track) +{ + 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, is_flushed = 0; + + /* ts A70213 : eventually expand size of track to max */ + burn_track_apply_fillup(t, d->media_capacity_remaining, 0); + + if (d->current_profile == 0x11 || d->current_profile == 0x14 || + d->current_profile == 0x15) { + /* DVD-R, DVD-RW Sequential, DVD-R/DL Sequential */ + ret = burn_disc_open_track_dvd_minus_r(o, s, tnum); + if (ret <= 0) + goto ex; + } else if (d->current_profile == 0x1b || d->current_profile == 0x2b) { + /* DVD+R , DVD+R/DL */ + ret = burn_disc_open_track_dvd_plus_r(o, s, tnum); + if (ret <= 0) + goto ex; + } + + sectors = burn_track_get_sectors(t); + open_ended = burn_track_is_open_ended(t); + /* <<< */ + { + char msg[160]; + + sprintf(msg, + "DVD pre-track %2.2d : demand=%.f%s, cap=%.f\n", + tnum+1, (double) sectors * 2048.0, + (open_ended ? " (open ended)" : ""), + (double) d->media_capacity_remaining); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + } + + + /* (offset padding is done within sector_data()) */ + + 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++; + } + + /* (tail padding is done in sector_data()) */ + + /* Pad up buffer to next full o->obs (usually 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; + is_flushed = 1; + + /* Eventually finalize track */ + if (d->current_profile == 0x11 || d->current_profile == 0x14 || + d->current_profile == 0x15) { + /* DVD-R, DVD-RW Sequential, DVD-R/DL Sequential */ + ret = burn_disc_close_track_dvd_minus_r(o, s, tnum); + if (ret <= 0) + goto ex; + } else if (d->current_profile == 0x1b || d->current_profile == 0x2b) { + /* DVD+R , DVD+R/DL */ + ret = burn_disc_close_track_dvd_plus_r(o, s, tnum, + is_last_track); + if (ret <= 0) + goto ex; + } + ret = 1; +ex:; + if (d->cancel) + burn_source_cancel(t->source); + if (!is_flushed) + 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 A70129 : for profile 0x11 DVD-R, 0x14 DVD-RW Seq, 0x15 DVD-R/DL Seq */ +int burn_disc_close_session_dvd_minus_r(struct burn_write_opts *o, + struct burn_session *s) +{ + struct burn_drive *d = o->drive; + + /* only for Incremental writing */ + if (o->write_type != BURN_WRITE_TAO) + return 2; + +#ifdef Libburn_dvd_r_dl_multi_no_close_sessioN + if (d->current_profile == 0x15 && o->multi) + return 2; +#endif + + libdax_msgs_submit(libdax_messenger, o->drive->global_index,0x00020119, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + "Closing session", 0, 0); + + d->busy = BURN_DRIVE_CLOSING_SESSION; + d->close_track_session(d, 1, 0); /* CLOSE SESSION, 010b */ + d->busy = BURN_DRIVE_WRITING; + return 1; +} + + +/* ts A61218 */ +int burn_dvd_write_session(struct burn_write_opts *o, + struct burn_session *s, int is_last_session) +{ + int i,ret; + struct burn_drive *d = o->drive; + + /* >>> open_session ? */ + + for (i = 0; i < s->tracks; i++) { + ret = burn_dvd_write_track(o, s, i, + is_last_session && i == (s->tracks - 1)); + if (ret <= 0) + break; + } + if (d->current_profile == 0x11 || d->current_profile == 0x14 || + d->current_profile == 0x15) { + /* DVD-R , DVD-RW Sequential, DVD-R/DL Sequential */ + ret = burn_disc_close_session_dvd_minus_r(o, s); + if (ret <= 0) + return 0; + } else if (d->current_profile == 0x12) { + /* DVD-RAM */ + /* ??? any finalization needed ? */; + } else if (d->current_profile == 0x13) { + /* DVD-RW restricted overwrite */ + if (d->needs_close_session) { + ret = burn_disc_close_session_dvd_minus_rw(o, s); + if (ret <= 0) + return 0; + } + } else if (d->current_profile == 0x1a) { + /* DVD+RW */ + if (d->needs_close_session) { + ret = burn_disc_close_session_dvd_plus_rw(o, s); + if (ret <= 0) + return 0; + } + } else if (d->current_profile == 0x1b || d->current_profile == 0x2b) { + /* DVD+R , DVD+R/DL do each track as an own session */; + } + 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->needs_close_session = 1; + } + 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 */ + } + + + /* ??? 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; + + /* >>> perform OPC if needed */; + + return 1; +} + + +/* ts A70129 : for DVD-R[W] Sequential Recoding */ +int burn_disc_setup_dvd_minus_r(struct burn_write_opts *o, + struct burn_disc *disc) +{ + struct burn_drive *d = o->drive; + + /* most setup is in burn_disc_setup_track_dvd_minus_r() */; + + d->nwa = 0; + return 1; +} + + +/* ts A70226 : for DVD+R , DVD+R/DL */ +int burn_disc_setup_dvd_plus_r(struct burn_write_opts *o, + struct burn_disc *disc) +{ + struct burn_drive *d = o->drive; + + /* most setup is in burn_disc_setup_track_dvd_plus_r() */; + + d->nwa = 0; + return 1; +} + + +/* ts A61218 - A70129 */ +int burn_dvd_write_sync(struct burn_write_opts *o, + struct burn_disc *disc) +{ + int i, ret, o_end; + off_t default_size = 0; + struct burn_drive *d = o->drive; + struct burn_track *t; + char msg[160]; + + d->needs_close_session = 0; + + if (d->current_profile == 0x1a || d->current_profile == 0x12) { + /* DVD+RW , DVD-RAM */ + 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 Restricted Overwrite */ + 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; + } + + /* _Rigid_ Restricted Overwrite demands this */ + o->obs_pad = 1; /* fill-up track's last 32k buffer */ + + } else if (d->current_profile == 0x11 || d->current_profile == 0x14 || + d->current_profile == 0x15) { + /* DVD-R , DVD-RW Sequential , DVD-R/DL Sequential */ + t = disc->session[0]->track[0]; + o_end = ( burn_track_is_open_ended(t) && !o->fill_up_media ); + default_size = burn_track_get_default_size(t); + if (o->write_type == BURN_WRITE_SAO && o_end) { + sprintf(msg, "Activated track default size %.f", + (double) default_size); + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x0002012e, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + burn_track_set_size(t, default_size); + } + ret = burn_disc_setup_dvd_minus_r(o, disc); + if (ret <= 0) { + sprintf(msg, + "Write preparation setup failed for DVD-R[W]"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020121, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + goto early_failure; + } + /* ??? padding needed ??? cowardly doing it for now */ + o->obs_pad = 1; /* fill-up track's last 32k buffer */ + + } else if (d->current_profile == 0x1b || d->current_profile == 0x2b) { + /* DVD+R , DVD+R/DL */ + t = disc->session[0]->track[0]; + o_end = ( burn_track_is_open_ended(t) && !o->fill_up_media ); + default_size = burn_track_get_default_size(t); + if (o->write_type == BURN_WRITE_SAO && o_end) { + sprintf(msg, "Activated track default size %.f", + (double) default_size); + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x0002012e, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + burn_track_set_size(t, default_size); + } + ret = burn_disc_setup_dvd_plus_r(o, disc); + if (ret <= 0) { + sprintf(msg, + "Write preparation setup failed for DVD+R"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020121, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + goto early_failure; + } + /* ??? padding needed ??? cowardly doing it for now */ + o->obs_pad = 1; /* fill-up track's last 32k buffer */ + } + 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], + i == (disc->sessions - 1)); + 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; + } + 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; +} + + +/* ts A70904 */ +int burn_stdio_open_write(struct burn_drive *d, off_t start_byte, + int sector_size, int flag) +{ + +/* <<< We need _LARGEFILE64_SOURCE defined by the build system. +*/ +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + + int fd = -1; + int mode = O_RDWR | O_CREAT | O_LARGEFILE; + char msg[160]; + + if (d->devname[0] == 0) /* null drives should not come here */ + return -1; + fd = burn_drive__fd_from_special_adr(d->devname); + if (fd >= 0) + fd = dup(fd); /* check validity and make closeable */ + else + fd = open(d->devname, mode, S_IRUSR | S_IWUSR); + if (fd == -1) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020005, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Failed to open device (a pseudo-drive)", errno, 0); + d->cancel = 1; + return -1; + } + if (start_byte < 0) + start_byte = 0; + if (d->drive_role == 2) + if (lseek(fd, start_byte, SEEK_SET)==-1) { + sprintf(msg, "Cannot address start byte %.f", + (double) start_byte); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020147, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, errno, 0); + close(fd); + d->cancel = 1; + fd = -1; + } + d->nwa = start_byte / sector_size; + return fd; +} + + +/* ts A70904 */ +int burn_stdio_read_source(struct burn_source *source, char *buf, int bufsize, + struct burn_write_opts *o, int flag) +{ + int count= 0, todo; + + for(todo = bufsize; todo > 0; todo -= count) { + if(source->read!=NULL) + count = source->read(source, + (unsigned char *) (buf + (bufsize - todo)), todo); + else + count = source->read_xt(source, + (unsigned char *) (buf + (bufsize - todo)), todo); + if (count <= 0) + break; + } + return (bufsize - todo); +} + + +/* ts A70904 */ +int burn_stdio_write(int fd, char *buf, int count, struct burn_drive *d, + int flag) +{ + if (write(fd, buf, count) != count) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020148, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Cannot write desired amount of data", errno, 0); + d->cancel = 1; + return 0; + } + return count; +} + + +/* ts A70910 : to be used as burn_drive.write(), emulating mmc_write() */ +int burn_stdio_mmc_write(struct burn_drive *d, int start, struct buffer *buf) +{ + int ret; + off_t start_byte; + + if (d->cancel) + return BE_CANCELLED; + if (d->stdio_fd < 0) { + + /* >>> program error */; + + d->cancel = 1; + return BE_CANCELLED; + } + if (start != d->nwa) { + char msg[80]; + + start_byte = ((off_t) start) * + (off_t) (buf->bytes / buf->sectors); + if (lseek(d->stdio_fd, start_byte, SEEK_SET)==-1) { + sprintf(msg, "Cannot address start byte %.f", + (double) start_byte); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020147, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, errno, 0); + d->cancel = 1; + return BE_CANCELLED; + } + d->nwa = start; + } + ret = burn_stdio_write(d->stdio_fd,(char *)buf->data, buf->bytes, d,0); + if (ret <= 0) + return BE_CANCELLED; + d->nwa += buf->sectors; + return 0; +} + + +/* ts A70910 : to be used as burn_drive.write(), + emulating mmc_write() with simulated writing. */ +int burn_stdio_mmc_dummy_write(struct burn_drive *d, int start, + struct buffer *buf) +{ + if (d->cancel) + return BE_CANCELLED; + d->nwa = start + buf->sectors; + return 0; +} + + +/* ts A70911 */ +/* Flush stdio system buffer to physical device. + @param flag bit0= do not report debug message (intermediate sync) +*/ +int burn_stdio_sync_cache(int fd, struct burn_drive *d, int flag) +{ + if (fd < 0) { + + /* >>> program error */; + + d->cancel = 1; + return 0; + } + d->needs_sync_cache = 0; + if (!(flag & 1)) + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + "syncing cache (stdio fsync)", 0, 0); + if (fsync(fd) != 0) { + if (errno == EINVAL) /* E.g. /dev/null cannot fsync */ + return 1; + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020148, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Cannot write desired amount of data", errno, 0); + d->cancel = 1; + return 0; + } + return 1; +} + + +/* ts A70911 : to be used as burn_drive.sync_cache(), + emulating mmc_sync_cache() */ +void burn_stdio_mmc_sync_cache(struct burn_drive *d) +{ + burn_stdio_sync_cache(d->stdio_fd, d, 0); +} + + +/* ts A70912 */ +/* Enforces eventual nominal write speed. + @param flag bit0= initialize *prev_time */ +int burn_stdio_slowdown(struct burn_drive *d, struct timeval *prev_time, + int amount, int flag) +{ + struct timeval tnow; + struct timezone dummy_tz; + double to_wait; + + if (flag & 1) { + gettimeofday(prev_time, &dummy_tz); + return 1; + } + if(d->nominal_write_speed <= 0) + return 2; + gettimeofday(&tnow, &dummy_tz); + to_wait = ( ((double) amount) / (double) d->nominal_write_speed ) - + (double) ( tnow.tv_sec - prev_time->tv_sec ) - + (double) ( tnow.tv_usec - prev_time->tv_usec ) / 1.0e6 + - 0.001; /* best would be 1 / kernel granularity HZ */ + if (to_wait >= 0.0001) { + usleep((int) (to_wait * 1000000.0)); + } + gettimeofday(prev_time, &dummy_tz); + return 1; +} + + +/* ts A70904 */ +int burn_stdio_write_track(struct burn_write_opts *o, struct burn_session *s, + int tnum, int flag) +{ + int open_ended, bufsize, ret, sectors, fd; + struct burn_track *t = s->track[tnum]; + struct burn_drive *d = o->drive; + char buf[16*2048]; + int i, prev_sync_sector = 0; + struct buffer *out = d->buffer; + struct timeval prev_time; + + bufsize = sizeof(buf); + fd = d->stdio_fd; + + sectors = burn_track_get_sectors(t); + burn_disc_init_track_status(o, s, tnum, sectors); + open_ended = burn_track_is_open_ended(t); + + /* attach stdio emulators for mmc_*() functions */ + if (o->simulate) + d->write = burn_stdio_mmc_dummy_write; + else + d->write = burn_stdio_mmc_write; + d->sync_cache = burn_stdio_mmc_sync_cache; + + burn_stdio_slowdown(d, &prev_time, 0, 1); /* initialize */ + for (i = 0; open_ended || i < sectors; i++) { + /* transact a (CD sized) sector */ + if (!sector_data(o, t, 0)) + {ret= 0; goto ex;} + if (open_ended) { + d->progress.sectors = sectors = d->progress.sector; + if (burn_track_is_data_done(t)) + break; + } + d->progress.sector++; + /* Flush to disk after each full MB */ + if (d->progress.sector - prev_sync_sector >= 512) { + prev_sync_sector = d->progress.sector; + if (!o->simulate) + burn_stdio_sync_cache(d->stdio_fd, d, 1); + burn_stdio_slowdown(d, &prev_time, 512 * 2, 0); + } + } + + /* Pad up buffer to next full o->obs (usually 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); + ret= 1; +ex:; + if (d->cancel) + burn_source_cancel(t->source); + return ret; +} + + +/* ts A70904 */ +int burn_stdio_write_sync(struct burn_write_opts *o, + struct burn_disc *disc) +{ + int ret; + struct burn_drive *d = o->drive; + + d->needs_close_session = 0; + o->obs_pad = 0; /* no filling-up of track's last 32k buffer */ + o->obs = 32*1024; /* buffer size */ + + if (disc->sessions != 1) + {ret= 0 ; goto ex;} + if (disc->session[0]->tracks != 1) + {ret= 0 ; goto ex;} + + /* update progress */ + d->progress.session = 0; + d->progress.tracks = 1; + + /* >>> adjust sector size (2048) to eventual audio or even raw */ + /* open target file */ + if (d->stdio_fd >= 0) + close(d->stdio_fd); + d->stdio_fd = burn_stdio_open_write(d, o->start_byte, 2048, 0); + if (d->stdio_fd == -1) + {ret = 0; goto ex;} + + ret = burn_stdio_write_track(o, disc->session[0], 0, 0); + 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; + ret = 1; +ex:; + if (d->stdio_fd >= 0) + close(d->stdio_fd); + d->stdio_fd = -1; + + /* update media state records */ + burn_drive_mark_unready(d); + + d->busy = BURN_DRIVE_IDLE; + return ret; +} + + +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, *t; + int first = 1, i, ret, lba, nwa = 0; + off_t default_size; + 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 A70904 */ + if (d->drive_role != 1) { + ret = burn_stdio_write_sync(o, disc); + if (ret <= 0) + goto fail_wo_sync; + return; + } + /* ts A61218 */ + if (! d->current_is_cd_profile) { + ret = burn_dvd_write_sync(o, disc); + if (ret <= 0) + goto fail_wo_sync; + return; + } + + /* ts A70218 */ + if (o->write_type == BURN_WRITE_SAO) { + for (i = 0 ; i < disc->session[0]->tracks; i++) { + t = disc->session[0]->track[i]; + if (burn_track_is_open_ended(t)) { + default_size = burn_track_get_default_size(t); + sprintf(msg, + "Activated track default size %.f", + (double) default_size); + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x0002012e, + LIBDAX_MSGS_SEV_NOTE, + LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); + burn_track_set_size(t, default_size); + } + } + } + + 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, + "SAO|RAW: Inquired nwa: %d , ret= %d , cap=%.f\n", + nwa, ret, (double) d->media_capacity_remaining); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg,0, 0); + + /* >>> ts A70212 : CD-DAO/SAO : eventually expand size of last track to maximum */; + + } + + 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; +} + +/* ts A70811 : API function */ +int burn_random_access_write(struct burn_drive *d, off_t byte_address, + char *data, off_t data_count, int flag) +{ + int alignment = 0, start, upto, chunksize, err, fd = -1, ret; + char msg[81], *rpt; + struct buffer buf; + + if (d->released) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020142, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Drive is not grabbed on random access write", 0, 0); + return 0; + } + if(d->drive_role == 0) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020146, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Drive is a virtual placeholder (null-drive)", 0, 0); + return 0; + } + + if(d->drive_role == 2) + alignment = 2 * 1024; + if (d->current_profile == 0x12) /* DVD-RAM */ + alignment = 2 * 1024; + if (d->current_profile == 0x13) /* DVD-RW restricted overwrite */ + alignment = 32 * 1024; + if (d->current_profile == 0x1a) /* DVD+RW */ + alignment = 2 * 1024; + if (alignment == 0) { + sprintf(msg, "Write start address not supported"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020125, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Write start address not supported", 0, 0); + return 0; + } + if ((byte_address % alignment) != 0) { + sprintf(msg, + "Write start address not properly aligned (%d bytes)", + alignment); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020126, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return 0; + } + if ((data_count % alignment) != 0) { + sprintf(msg, + "Write data count not properly aligned (%ld bytes)", + (long) alignment); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020141, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return 0; + } + if (d->busy != BURN_DRIVE_IDLE) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020140, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Drive is busy on attempt to write random access",0,0); + return 0; + } + if(d->drive_role != 1) { + fd = burn_stdio_open_write(d, byte_address, 2048, 0); + if (fd == -1) + return 0; + } + d->busy = BURN_DRIVE_WRITING_SYNC; + d->buffer = &buf; + + start = byte_address / 2048; + upto = start + data_count / 2048; + rpt = data; + for (; start < upto; start += 16) { + chunksize = upto - start; + if (chunksize > 16) + chunksize = 16; + d->buffer->bytes = chunksize * 2048; + memcpy(d->buffer->data, rpt, d->buffer->bytes); + rpt += d->buffer->bytes; + d->buffer->sectors = chunksize; + d->nwa = start; + if(d->drive_role == 1) { + err = d->write(d, d->nwa, d->buffer); + } else { + ret = burn_stdio_write(fd, (char *) d->buffer->data, + d->buffer->bytes, d, 0); + err = 0; + if (ret <= 0) + err = BE_CANCELLED; + } + if (err == BE_CANCELLED) { + d->busy = BURN_DRIVE_IDLE; + if(fd >= 0) + close(fd); + return (-(start * 2048 - byte_address)); + } + } + + if(d->drive_role == 1) + d->needs_sync_cache = 1; + if(flag & 1) { + if(d->drive_role == 1) + d->sync_cache(d); + else + burn_stdio_sync_cache(fd, d, 0); + d->needs_sync_cache = 0; + } + + if(fd >= 0) + close(fd); + d->buffer = NULL; + d->busy = BURN_DRIVE_IDLE; + return 1; +} + diff --git a/libburn/branches/ZeroFourTwo/libburn/write.h b/libburn/branches/ZeroFourTwo/libburn/write.h new file mode 100644 index 00000000..d5b1b32d --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libburn/write.h @@ -0,0 +1,48 @@ +/* -*- 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, + int flag); + +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); + + + +/* mmc5r03c.pdf 6.3.3.3.3: DVD-R DL: Close Function 010b: Close Session + "When the recording mode is Incremental Recording, + the disc is single session." + Enable this macro to get away from growisofs which uses Close Session + but also states "// DVD-R DL Seq has no notion of multi-session". + + #define Libburn_dvd_r_dl_multi_no_close_sessioN 1 + +*/ + +#endif /* BURN__WRITE_H */ diff --git a/libburn/branches/ZeroFourTwo/libcevap/cgen.c b/libburn/branches/ZeroFourTwo/libcevap/cgen.c new file mode 100644 index 00000000..81718f76 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libcevap/cgen.c @@ -0,0 +1,1503 @@ + +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/stat.h> + + +#include "smem.h" + +char *Sfile_fgets(); +int Sregex_string(); +int Sregex_trimline(); + + +#include "ctyp.h" + +#include "cgen.h" + + + +/* ----------------------------- CgeN ------------------------- */ + +int Cgen_new(cgen,flag) +struct CgeN **cgen; +int flag; +{ + int ret; + struct CgeN *c; + + *cgen= c= TSOB_FELD(struct CgeN,1); + if(c==NULL) { + fprintf(stderr,"+++ Cannot create cgen object : %s\n",strerror(errno)); + return(-1); + } + c->classname= NULL; + c->structname= NULL; + c->functname= NULL; + c->is_managed_list= 0; + c->is_bossless_list= 0; + c->gen_for_stic= 1; + c->make_ansi= 0; + c->make_lowercase= 0; + c->global_include_file[0]= 0; + c->global_include_fp= NULL; + c->elements= NULL; + c->last_element= NULL; + c->may_overwrite= 0; + c->fp= NULL; + c->filename[0]= 0; + c->ptt_fp= NULL; + c->ptt_filename[0]= 0; + c->msg[0]= 0; + return(1); +} + + +int Cgen_destroy(cgen,flag) +struct CgeN **cgen; +int flag; +{ + struct CgeN *c; + struct CtyP *ct,*next_ct; + + c= *cgen; + if(c==NULL) + return(0); + + if(c->fp!=NULL) + fclose(c->fp); + if(c->ptt_fp!=NULL) + fclose(c->ptt_fp); + Sregex_string(&(c->classname),NULL,0); + Sregex_string(&(c->structname),NULL,0); + Sregex_string(&(c->functname),NULL,0); + for(ct= c->elements; ct!=NULL; ct= next_ct) { + next_ct= ct->next; + Ctyp_destroy(&ct,0); + } + + free((char *) c); + *cgen= NULL; + return(1); +} + + +int Cgen_make_names(cgen,flag) +struct CgeN *cgen; +int flag; +{ + int l; + + if(Sregex_string(&(cgen->structname),cgen->classname,0)<=0) + return(-1); + if(Sregex_string(&(cgen->functname),cgen->classname,0)<=0) + return(-1); + if(!cgen->make_lowercase) { + cgen->structname[0]= toupper(cgen->structname[0]); + l= strlen(cgen->structname); + cgen->structname[l-1]= toupper(cgen->structname[l-1]); + cgen->functname[0]= toupper(cgen->functname[0]); + } + return(1); +} + + +int Cgen_read_fp(cgen,fp,flag) +struct CgeN *cgen; +FILE *fp; +int flag; +/* + bit0= return 0 if eof at classname +*/ +{ + char line[4096],*cpt,*bpt; + int ret; + + line[0]= 0; + while(1) { + printf("[-list] classname ?\n"); + if(Sfile_fgets(line,sizeof(line)-1,fp)==NULL) { + if(!(flag&1)) + return(2); +no_name:; + sprintf(cgen->msg,"No classname given."); + return(0); + } + printf("%s\n",line); + if(strcmp(line,"@@@")==0) + return(2); + + if(line[0]==0 || line[0]=='#') { + + /* >>> record class comments */; + + } else + break; + } + cpt= line; + while(cpt[0]=='-') { + /* look for management specifiers: + -l* listable by prev-next chain + */ + if(cpt[1]=='l' || cpt[1]=='L') { + cgen->is_managed_list= 1; + } else if(cpt[1]=='b' || cpt[1]=='B') { + cgen->is_bossless_list= 1; + } + while(*cpt!=0 && !isspace(*cpt)) cpt++; + while(*cpt!=0 && isspace(*cpt)) cpt++; + if(*cpt==0) + goto no_name; + } + if(Sregex_string(&(cgen->classname),cpt,0)<=0) + return(-1); + ret= Cgen_make_names(cgen,0); + if(ret<=0) + return(ret); + + while(1) { + ret= Ctyp_read_fp(&(cgen->last_element),fp,cgen->msg, + !!cgen->make_lowercase); + if(ret<=0) + return(ret); + if(ret==2) + break; + if(cgen->elements==NULL) + cgen->elements= cgen->last_element; + } + if(cgen->is_managed_list) { + sprintf(line,"-c struct %s *prev",cgen->structname); + ret= Ctyp_new_from_line(&(cgen->last_element),cgen->last_element, + line,cgen->msg,0); + if(ret<=0) + return(ret); + if(cgen->elements==NULL) + cgen->elements= cgen->last_element; + sprintf(line,"-c struct %s *next",cgen->structname); + ret= Ctyp_new_from_line(&(cgen->last_element),cgen->last_element, + line,cgen->msg,0); + if(ret<=0) + return(ret); + } + return(1); +} + + +int Cgen_open_wfile(cgen,flag) +struct CgeN *cgen; +int flag; +/* + bit0-3: modes + 0= open cgen->fp + 1= open cgen->ptt_fp + 2= open cgen->global_include_fp +*/ +{ + struct stat stbuf; + int ret, mode; + char *name, fmode[4]; + FILE *fp; + + mode= flag&15; + strcpy(fmode,"w"); + if(mode==0) { + name= cgen->filename; + fp= cgen->fp; + cgen->fp= NULL; + } else if(mode==1) { + name= cgen->ptt_filename; + fp= cgen->ptt_fp; + cgen->ptt_fp= NULL; + } else if(mode==2) { + strcpy(fmode,"a"); + name= cgen->global_include_file; + fp= cgen->global_include_fp; + cgen->global_include_fp= NULL; + } else { + fprintf(stderr,"+++ Cgen_open_wfile : program error : unknown mode %d\n", + mode); + ret= -1; goto ex; + } + if(fmode[0]=='w' && stat(name,&stbuf)!=-1 && !cgen->may_overwrite) { + sprintf(cgen->msg,"File '%s' already existing.",name); + ret= 0; goto ex; + } + if(fp!=NULL) + {fclose(fp); fp= NULL;} + fp= fopen(name,fmode); + if(fp==NULL) { + sprintf(cgen->msg,"Cannot open file '%s' in %s-mode. %s", + name,fmode,strerror(errno)); + ret= 0; goto ex; + } + ret= 1; +ex:; + if(mode==0) + cgen->fp= fp; + else if(mode==1) + cgen->ptt_fp= fp; + else if(mode==2) + cgen->global_include_fp= fp; + return(ret); +} + + +int Cgen_write_datestr(cgen,flag) +struct CgeN *cgen; +int flag; +/* + bit0= operate on ptt (= ANSI prototype) file rather than on internal header +*/ +{ + time_t t0; + char timetext[81]; + FILE *fp; + + if(flag&1) + fp= cgen->ptt_fp; + else + fp= cgen->fp; + t0= time(0); + strftime(timetext,sizeof(timetext),"%a, %d %b %Y %H:%M:%S GMT", + gmtime(&t0)); + fprintf(fp,"/* ( derived from stub generated by CgeN on %s ) */\n", + timetext); + return(1); +} + + +int Cgen_write_h(cgen,flag) +struct CgeN *cgen; +int flag; +{ + int ret,i,pointer_level; + FILE *fp= NULL; + struct CtyP *ct; + char pvt[16],macro_name[4096],*cpt; + + if(cgen->make_ansi) { + sprintf(cgen->filename,"%s_private.h",cgen->classname); + strcpy(pvt,"_private"); + } else { + sprintf(cgen->filename,"%s.h",cgen->classname); + strcpy(pvt,""); + } + + ret= Cgen_open_wfile(cgen,0); + if(ret<=0) + goto ex; + sprintf(macro_name,"%s%s_includeD",cgen->functname,pvt); + macro_name[0]= toupper(macro_name[0]); + fp= cgen->fp; + + /* >>> print class comments */; + + fprintf(fp,"\n"); + fprintf(fp,"#ifndef %s\n",macro_name); + fprintf(fp,"#define %s\n",macro_name); + fprintf(fp,"\n"); + if(strlen(cgen->global_include_file)!=0) { + fprintf(fp,"#include \"%s\"\n",cgen->global_include_file); + fprintf(fp,"\n\n"); + } + if(cgen->make_ansi) + fprintf(fp,"/* For function prototypes see file %s.h */\n",cgen->classname); + fprintf(fp,"\n\n"); + fprintf(fp,"struct %s {\n",cgen->structname); + fprintf(fp,"\n"); + ct= cgen->elements; + for(ct= cgen->elements;ct!=NULL;ct= ct->next) { + + if(ct->is_comment) { + if(ct->name[0]==0) { + fprintf(fp,"\n"); + continue; + } + fprintf(fp," /* "); + for(cpt= ct->name; *cpt!=0; cpt++) { + fprintf(fp,"%c",*cpt); + if(cpt[0]=='*' && cpt[1]=='/') + fprintf(fp," "); + } + fprintf(fp," */\n"); + continue; + } + + if(ct->is_volatile) + fprintf(fp," volatile"); + if(Ctyp_is_struct(ct,0)) + fprintf(fp," struct"); + else if(ct->is_unsigned) + fprintf(fp," unsigned"); + fprintf(fp," %s ",ct->dtype); + pointer_level= Ctyp_get_pointer_level(ct,0); + for(i=0;i<pointer_level;i++) + fprintf(fp,"*"); + fprintf(fp,"%s",ct->name); + if(ct->array_size>0) + fprintf(fp,"[%lu]",ct->array_size); + fprintf(fp,";\n"); + } + fprintf(fp,"\n"); + fprintf(fp,"};\n"); + fprintf(fp,"\n"); + fprintf(fp,"\n"); + fprintf(fp,"#endif /* %s */\n",macro_name); + fprintf(fp,"\n"); + Cgen_write_datestr(cgen,0); + + /* Eventually write start of ANSI prototype include file */ + if(!cgen->make_ansi) + goto after_ansi_h; + sprintf(cgen->ptt_filename,"%s.h",cgen->classname); + ret= Cgen_open_wfile(cgen,1); + if(ret<=0) + goto ex; + sprintf(macro_name,"%s_includeD",cgen->functname); + macro_name[0]= toupper(macro_name[0]); + fp= cgen->ptt_fp; + + /* >>> print class comments */; + + fprintf(fp,"\n"); + fprintf(fp,"#ifndef %s\n",macro_name); + fprintf(fp,"#define %s\n",macro_name); + fprintf(fp,"\n\n"); + if(strlen(cgen->global_include_file)!=0) { + fprintf(fp,"#include \"%s\"\n",cgen->global_include_file); + } else { + fprintf(fp,"struct %s;\n",cgen->structname); + } + fprintf(fp,"\n\n"); + fprintf(fp,"/* For inner details see file %s_private.h */\n",cgen->classname); + fprintf(fp,"\n\n"); + +after_ansi_h:; + if(strlen(cgen->global_include_file)==0) + goto after_global_include; + ret= Cgen_open_wfile(cgen,2); + if(ret<=0) + goto ex; + fprintf(cgen->global_include_fp,"struct %s;\n",cgen->structname); + +after_global_include:; + ret= 1; +ex:; + if(cgen->fp!=NULL) + {fclose(cgen->fp); cgen->fp= NULL;} + /* ( note: cgen->ptt_fp stays open ) */ + if(cgen->global_include_fp!=NULL) + {fclose(cgen->global_include_fp); cgen->global_include_fp= NULL;} + return(ret); +} + + +int Cgen_write_to_ptt(cgen,ptt,flag) +struct CgeN *cgen; +char *ptt; +int flag; +{ + if(cgen->ptt_fp==NULL) + return(-1); + fprintf(cgen->ptt_fp,"%s;\n",ptt); + return(1); +} + + +int Cgen_finish_public_h(cgen,flag) +struct CgeN *cgen; +int flag; +{ + char macro_name[4096]; + + if(cgen->ptt_fp==NULL) + return(-1); + fprintf(cgen->ptt_fp,"\n"); + fprintf(cgen->ptt_fp,"\n"); + sprintf(macro_name,"%s_includeD",cgen->functname); + macro_name[0]= toupper(macro_name[0]); + fprintf(cgen->ptt_fp,"#endif /* %s */\n",macro_name); + fprintf(cgen->ptt_fp,"\n"); + Cgen_write_datestr(cgen,1); + if(cgen->ptt_fp!=NULL) + {fclose(cgen->ptt_fp); cgen->ptt_fp= NULL;} + return(1); +} + + +int Cgen_write_c_head(cgen,flag) +struct CgeN *cgen; +int flag; +{ + int ret,is_pointer,is_struct,array_size; + FILE *fp= NULL; + struct CtyP *ct,*hct; + char *dtype= NULL,*name= NULL; + + fp= cgen->fp; + fprintf(fp,"\n"); + fprintf(fp,"/*\n"); + fprintf(fp," cc -g -c %s.c\n",cgen->classname); + fprintf(fp,"*/\n"); + Cgen_write_datestr(cgen,0); + fprintf(fp,"\n"); + fprintf(fp,"#include <sys/types.h>\n"); + fprintf(fp,"#include <stdlib.h>\n"); + fprintf(fp,"#include <stdio.h>\n"); + fprintf(fp,"#include <string.h>\n"); + fprintf(fp,"#include <errno.h>\n"); + fprintf(fp,"\n"); + fprintf(fp,"#include \"%s.h\"\n",cgen->classname); + if(cgen->make_ansi) { + fprintf(fp,"#include \"%s_private.h\"\n",cgen->classname); + } + fprintf(fp,"\n"); + for(ct= cgen->elements; ct!=NULL; ct= ct->next) { + if(ct->is_comment) + continue; + Ctyp_get_dtype(ct,&dtype,0); + Ctyp_get_type_mod(ct,&is_pointer,&is_struct,&array_size,0); +/* + fprintf(stderr,"DEBUG: %s %s\n",(is_struct?"struct ":""),dtype); +*/ + /* already included ? */ + if(strcmp(dtype,cgen->structname)==0) + continue; + for(hct= cgen->elements; hct!=NULL && hct!=ct; hct= hct->next) { + if(hct->is_comment) + continue; + if(hct->dtype!=NULL) + if(strcmp(hct->dtype,dtype)==0) + break; + } + if(hct!=ct && hct!=NULL) + continue; + + if(is_struct && (isupper(dtype[0]) && isupper(dtype[strlen(dtype)-1]))) { + dtype[0]= tolower(dtype[0]); + dtype[strlen(dtype)-1]= tolower(dtype[strlen(dtype)-1]); + fprintf(fp,"#include \"%s.h\"\n",dtype); + } + } + fprintf(fp,"\n"); + if(cgen->gen_for_stic==1) { + fprintf(fp,"#include \"../s_tools/smem.h\"\n"); + fprintf(fp,"#include \"../s_tools/sfile.h\"\n"); + fprintf(fp,"#include \"../s_tools/sregex.h\"\n"); + fprintf(fp,"\n"); + } else if(cgen->gen_for_stic==2) { + fprintf(fp,"#include \"smem.h\"\n"); + fprintf(fp,"\n"); + } + fprintf(fp,"\n"); + fprintf(fp,"/* -------------------------- %s ----------------------- */\n", + cgen->structname); + fprintf(fp,"\n"); + + if(dtype!=NULL) + Sregex_string(&dtype,NULL,0); + if(name!=NULL) + Sregex_string(&name,NULL,0); + return(1); +} + + +int Cgen_write_c_new(cgen,flag) +struct CgeN *cgen; +int flag; +{ + int ret,pointer_level,management,boss_parm= 0; + unsigned long array_size; + FILE *fp= NULL; + struct CtyP *ct; + char ptt[4096]; + + fp= cgen->fp; + + if(!cgen->is_bossless_list) { + if(cgen->elements!=NULL) + if(strcmp(cgen->elements->name,"boss")==0 && cgen->elements->is_struct && + cgen->elements->is_pointer==1 && cgen->elements->no_initializer==0) + boss_parm= 1; + if(cgen->is_managed_list && boss_parm==0) + fprintf(stderr, + "+++ Warning: -l %s without -v struct ... *boss as first attribute\n", + cgen->classname); + } + fprintf(fp,"\n"); + if(cgen->make_ansi) { + sprintf(ptt,"int %s_new(struct %s **objpt, ", + cgen->functname,cgen->structname); + if(boss_parm) + sprintf(ptt+strlen(ptt),"struct %s *boss, ",cgen->elements->dtype); + sprintf(ptt+strlen(ptt),"int flag)"); + fprintf(fp,"%s\n",ptt); + ret= Cgen_write_to_ptt(cgen, ptt, 0); + if(ret<=0) + return(ret); + } else { + fprintf(fp,"int %s_new(objpt,\n",cgen->functname); + if(boss_parm) + fprintf(fp,"boss,"); + fprintf(fp,"flag)\n"); + fprintf(fp,"struct %s **objpt;\n",cgen->structname); + if(boss_parm) + fprintf(fp,"struct %s *boss;",cgen->elements->dtype); + fprintf(fp,"int flag;\n"); + } + fprintf(fp,"{\n"); + fprintf(fp," struct %s *o;\n",cgen->structname); + + /* Is an array index i needed ? */ + for(ct= cgen->elements; ct!=NULL; ct= ct->next) { + if(ct->is_comment || ct->no_initializer) + continue; + if(ct->array_size>0) + if(strcmp(ct->dtype,"char")!=0) { + fprintf(fp," int i;\n"); + break; + } + } + + fprintf(fp,"\n"); + if(cgen->gen_for_stic) + fprintf(fp," *objpt= o= TSOB_FELD(struct %s,1);\n",cgen->structname); + else + fprintf(fp," *objpt= o= (struct %s *) malloc(sizeof(struct %s));\n", + cgen->structname, cgen->structname); + fprintf(fp," if(o==NULL)\n"); + fprintf(fp," return(-1);\n"); + fprintf(fp,"\n"); + for(ct= cgen->elements; ct!=NULL; ct= ct->next) { + if(ct->is_comment || ct->no_initializer) + continue; + array_size= Ctyp_get_array_size(ct,0); + pointer_level= Ctyp_get_pointer_level(ct,0); + if(ct==cgen->elements && boss_parm) { + fprintf(fp," o->boss= boss;\n"); + } else if(array_size>0) { + if(strcmp(ct->dtype,"char")==0) { + fprintf(fp," o->%s[0]= 0;\n;",ct->name); + } else if(pointer_level>0) { + fprintf(fp," for(i=0;i<%lu;i++)\n",array_size); + fprintf(fp," o->%s[i]= NULL;\n",ct->name); + } else { + fprintf(fp," for(i=0;i<%lu;i++)\n",array_size); + fprintf(fp," o->%s[i]= 0;\n",ct->name); + } + } else if(pointer_level>0) { + fprintf(fp," o->%s= NULL;\n",ct->name); + } else + fprintf(fp," o->%s= 0;\n",ct->name); + } + fprintf(fp,"\n"); + fprintf(fp," return(1);\n"); + fprintf(fp,"/*\n"); + fprintf(fp,"failed:;\n"); + fprintf(fp," %s_destroy(objpt,0);\n",cgen->functname); + fprintf(fp," return(-1);\n"); + fprintf(fp,"*/\n"); + fprintf(fp,"}\n"); + fprintf(fp,"\n"); + for(ct= cgen->elements; ct!=NULL; ct= ct->next) { + if(ct->is_comment) + continue; + management= Ctyp_get_management(ct,0); + if(management==4) { + if(ct->next==NULL) { +no_last_pt:; + sprintf(cgen->msg, + "Lonely -l found. A -v of same type must follow.\nName is : %s", + ct->name); + return(0); + } + if(strcmp(ct->next->dtype,ct->dtype)!=0 + || ct->next->is_pointer!=ct->is_pointer) + goto no_last_pt; + ct->next->with_getter= ct->next->with_setter= 0; + ret= Cgen_write_c_new_type(cgen,ct,ct->next,0); + if(ret<=0) + return(ret); + } + } + return(1); +} + + +int Cgen_write_c_new_type(cgen,ct_first,ct_last,flag) +struct CgeN *cgen; +struct CtyP *ct_first,*ct_last; +int flag; +{ + int ret,l,management,pointer_level,i; + FILE *fp= NULL; + char funct[4096],classname[4096],*npt,ptt[4096]; + + strcpy(funct,ct_first->dtype); + strcpy(classname,funct); + l= strlen(funct); + if(l>0) { + if(cgen->make_lowercase) + funct[0]= tolower(funct[0]); + else + funct[0]= toupper(funct[0]); + funct[l-1]= tolower(funct[l-1]); + classname[0]= tolower(funct[0]); + classname[l-1]= funct[l-1]; + } + fp= cgen->fp; + fprintf(fp,"\n"); + if(cgen->make_ansi) { + sprintf(ptt, "int %s_new_%s(struct %s *o, int flag)", + cgen->functname,ct_first->name,cgen->structname); + fprintf(fp,"%s\n",ptt); + if(ct_first->with_setter) { + ret= Cgen_write_to_ptt(cgen, ptt, 0); + if(ret<=0) + return(ret); + } + } else { + fprintf(fp,"int %s_new_%s(o,flag)\n",cgen->functname,ct_first->name); + fprintf(fp,"struct %s *o;\n",cgen->structname); + fprintf(fp,"int flag;\n"); + } + fprintf(fp,"{\n"); + fprintf(fp," int ret;\n"); + fprintf(fp," struct %s *c= NULL;\n",ct_first->dtype); + fprintf(fp,"\n"); + if(ct_first->bossless_list) + fprintf(fp," ret= %s_new(&c,0);\n",funct); + else + fprintf(fp," ret= %s_new(&c,o,0);\n",funct); + fprintf(fp," if(ret<=0)\n"); + fprintf(fp," return(ret);\n"); + fprintf(fp," %s_link(c,o->%s,0);\n",funct,ct_last->name); + fprintf(fp," o->%s= c;\n",ct_last->name); + fprintf(fp," if(o->%s==NULL)\n",ct_first->name); + fprintf(fp," o->%s= c;\n",ct_first->name); + fprintf(fp," return(1);\n"); + fprintf(fp,"}\n"); + fprintf(fp,"\n"); + ret= 1; +ex:; + return(ret); +} + + +int Cgen_write_c_destroy(cgen,flag) +struct CgeN *cgen; +int flag; +{ + int ret,l,management,pointer_level,i; + FILE *fp= NULL; + struct CtyP *ct,*next; + char funct[4096],*npt,ptt[4096]; + + fp= cgen->fp; + fprintf(fp,"\n"); + if(cgen->make_ansi) { + sprintf(ptt, "int %s_destroy(struct %s **objpt, int flag)", + cgen->functname,cgen->structname); + fprintf(fp,"%s\n",ptt); + ret= Cgen_write_to_ptt(cgen, ptt, 0); + if(ret<=0) + return(ret); + } else { + fprintf(fp,"int %s_destroy(objpt,flag)\n",cgen->functname); + fprintf(fp,"struct %s **objpt;\n",cgen->structname); + fprintf(fp,"int flag;\n"); + } + fprintf(fp,"{\n"); + fprintf(fp," struct %s *o;\n",cgen->structname); + fprintf(fp,"\n"); + fprintf(fp," o= *objpt;\n"); + fprintf(fp," if(o==NULL)\n"); + fprintf(fp," return(0);\n"); + fprintf(fp,"\n"); + for(ct= cgen->elements; ct!=NULL; ct= ct->next) { + if(ct->is_comment) + continue; + management= Ctyp_get_management(ct,0); + if(management==1 || management==4) { + strcpy(funct,ct->dtype); + l= strlen(funct); + if(l>0) { + if(cgen->make_lowercase) + funct[0]= tolower(funct[0]); + else + funct[0]= toupper(funct[0]); + funct[l-1]= tolower(funct[l-1]); + } + if(strcmp(ct->dtype,"char")==0) { + if(cgen->gen_for_stic==1) + fprintf(fp," Sregex_string("); + else if(cgen->gen_for_stic==2) + fprintf(fp," Smem_freE((char *) "); + else + fprintf(fp," free("); + } else if(strcmp(ct->dtype,"LstrinG")==0 || management==4) + fprintf(fp," %s_destroy_all(",funct); + else + fprintf(fp," %s_destroy(",funct); + + pointer_level= Ctyp_get_pointer_level(ct,0)-2; + for(i=0; i>pointer_level; i--) + fprintf(fp,"&"); + for(i=0; i<pointer_level; i++) + fprintf(fp,"*"); + fprintf(fp,"(o->%s)",ct->name); + if(strcmp(ct->dtype,"char")==0) { + if(cgen->gen_for_stic==1) + fprintf(fp,",NULL,0);\n"); + else + fprintf(fp,");\n"); + } else + fprintf(fp,",0);\n"); + } else if(management==2) { + next= ct->next; + if(next==NULL) { +broken_chain:; + sprintf(cgen->msg, + "Lonely -c found. They have to appear in pairs.\nName is : %s", + ct->name); + ret= 0; goto ex; + } + if(next->management!=3) + goto broken_chain; + fprintf(fp," if(o->%s!=NULL)\n",ct->name); + fprintf(fp," o->%s->%s= o->%s;\n",ct->name,next->name,next->name); + fprintf(fp," if(o->%s!=NULL)\n",next->name); + fprintf(fp," o->%s->%s= o->%s;\n",next->name,ct->name,ct->name); + ct= next; + } + } + fprintf(fp,"\n"); + if(cgen->gen_for_stic) + fprintf(fp," Smem_freE((char *) o);\n"); + else + fprintf(fp," free((char *) o);\n"); + fprintf(fp," *objpt= NULL;\n"); + fprintf(fp," return(1);\n"); + fprintf(fp,"}\n"); + fprintf(fp,"\n"); + if(cgen->is_managed_list){ + ret= Cgen_write_c_destroy_all(cgen,0); + if(ret<=0) + goto ex; + } + ret= 1; +ex:; + return(ret); +} + + +int Cgen_write_c_destroy_all(cgen,flag) +struct CgeN *cgen; +int flag; +{ + int ret,l,management,pointer_level,i; + FILE *fp= NULL; + struct CtyP *ct; + char ptt[4096]; + + fp= cgen->fp; + fprintf(fp,"\n"); + if(cgen->make_ansi) { + sprintf(ptt, "int %s_destroy_all(struct %s **objpt, int flag)", + cgen->functname, cgen->structname); + fprintf(fp,"%s\n",ptt); + ret= Cgen_write_to_ptt(cgen, ptt, 0); + if(ret<=0) + return(ret); + } else { + fprintf(fp,"int %s_destroy_all(objpt,flag)\n",cgen->functname); + fprintf(fp,"struct %s **objpt;\n",cgen->structname); + fprintf(fp,"int flag;\n"); + } + fprintf(fp,"{\n"); + fprintf(fp," struct %s *o,*n;\n",cgen->structname); + fprintf(fp,"\n"); + fprintf(fp," o= *objpt;\n"); + fprintf(fp," if(o==NULL)\n"); + fprintf(fp," return(0);\n"); + fprintf(fp," for(;o->prev!=NULL;o= o->prev);\n"); + fprintf(fp," for(;o!=NULL;o= n) {\n"); + fprintf(fp," n= o->next;\n"); + fprintf(fp," %s_destroy(&o,0);\n",cgen->functname); + fprintf(fp," }\n"); + fprintf(fp," *objpt= NULL;\n"); + fprintf(fp," return(1);\n"); + fprintf(fp,"}\n"); + fprintf(fp,"\n"); + ret= 1; +ex:; + return(ret); +} + + +int Cgen_write_c_access(cgen,flag) +struct CgeN *cgen; +int flag; +{ + int ret,l,mgt,pointer_level,i; + FILE *fp= NULL; + struct CtyP *ct; + char funct[4096],*npt,ptt[4096]; + + fp= cgen->fp; + for(ct= cgen->elements; ct!=NULL; ct= ct->next) { + if(ct->is_comment) + continue; + pointer_level= Ctyp_get_pointer_level(ct,0); + if(Ctyp_get_with_getter(ct,0)<=0) + goto after_getter; + fprintf(fp,"\n"); + if(cgen->make_ansi) { + sprintf(ptt, "int %s_get_%s(struct %s *o, ", + cgen->functname,ct->name,cgen->structname); + if(Ctyp_is_struct(ct,0)) + strcat(ptt,"struct "); + strcat(ptt,ct->dtype); + strcat(ptt," "); + for(i=0; i<pointer_level+1; i++) + strcat(ptt,"*"); + if(Ctyp_get_array_size(ct,0)>0) + strcat(ptt,"*"); + strcat(ptt,"pt"); + if(ct->management==4) + strcat(ptt,", int idx"); + strcat(ptt,", int flag)"); + fprintf(fp,"%s\n",ptt); + ret= Cgen_write_to_ptt(cgen, ptt, 0); + if(ret<=0) + return(ret); + } else { + fprintf(fp,"int %s_get_%s(o,pt",cgen->functname,ct->name); + if(ct->management==4) + fprintf(fp,",idx"); + fprintf(fp,",flag)\n"); + fprintf(fp,"struct %s *o;\n",cgen->structname); + if(Ctyp_is_struct(ct,0)) + fprintf(fp,"struct "); + fprintf(fp,"%s ",ct->dtype); + for(i=0; i<pointer_level+1; i++) + fprintf(fp,"*"); + if(Ctyp_get_array_size(ct,0)>0) + fprintf(fp,"*"); + fprintf(fp,"pt;\n"); + if(ct->management==4) + fprintf(fp,"int idx;\n"); + fprintf(fp,"int flag;\n"); + } + if(ct->management==4) + fprintf(fp,"/* Note: idx==-1 fetches the last item of the list */\n"); + fprintf(fp,"{\n"); + if(ct->management==4) { + strcpy(funct,ct->dtype); + l= strlen(funct); + if(cgen->make_lowercase) + funct[0]= tolower(funct[0]); + if(l>1) + funct[l-1]= tolower(funct[l-1]); + fprintf(fp," if(idx==-1) {\n"); + fprintf(fp," *pt= o->%s;\n",ct->next->name); + fprintf(fp," return(*pt!=NULL);\n"); + fprintf(fp," }\n"); + fprintf(fp," return(%s_by_idx(o->%s,(flag&1?1:idx),pt,flag&1));\n", + funct,ct->name); + } else { + fprintf(fp," *pt= o->%s;\n",ct->name); + fprintf(fp," return(1);\n"); + } + fprintf(fp,"}\n"); + fprintf(fp,"\n"); +after_getter:; + + if(Ctyp_get_with_setter(ct,0)<=0) + goto after_setter; + + /* <<< provisory : develop a setter for arrays */ + if(Ctyp_get_array_size(ct,0)>0) + goto after_setter; + + mgt= Ctyp_get_management(ct,0); + if(mgt==0 || + (mgt==1 && pointer_level==1)) { + /* -value or -managed pointers */ + /* was: -value or -managed char * */ + /* (mgt==1 && strcmp(ct->dtype,"char")==0 && pointer_level==1)) { */ + + fprintf(fp,"\n"); + if(cgen->make_ansi) { + sprintf(ptt, "int %s_set_%s(struct %s *o, ", + cgen->functname,ct->name,cgen->structname); + if(Ctyp_is_struct(ct,0)) + strcat(ptt,"struct "); + strcat(ptt,ct->dtype); + strcat(ptt," "); + for(i=0; i<pointer_level; i++) + strcat(ptt,"*"); + strcat(ptt,"value, int flag)"); + fprintf(fp,"%s\n",ptt); + ret= Cgen_write_to_ptt(cgen, ptt, 0); + if(ret<=0) + return(ret); + } else { + fprintf(fp,"int %s_set_%s(o,value,flag)\n",cgen->functname,ct->name); + fprintf(fp,"struct %s *o;\n",cgen->structname); + if(Ctyp_is_struct(ct,0)) + fprintf(fp,"struct "); + fprintf(fp,"%s ",ct->dtype); + for(i=0; i<pointer_level; i++) + fprintf(fp,"*"); + fprintf(fp,"value;\n"); + fprintf(fp,"int flag;\n"); + } + fprintf(fp,"{\n"); + if(mgt==1 && strcmp(ct->dtype,"char")==0) { + if(cgen->gen_for_stic==1) { + fprintf(fp," if(Sregex_string(&(o->%s),value,0)<=0)\n",ct->name); + fprintf(fp," return(-1);\n"); + } else if(cgen->gen_for_stic==2) { + fprintf(fp," if(Smem_clone_string(&(o->%s),value)<=0)\n",ct->name); + fprintf(fp," return(-1);\n"); + } else { + fprintf(fp," char *cpt;\n"); + fprintf(fp,"\n"); + fprintf(fp," cpt= malloc(strlen(value)+1);\n"); + fprintf(fp," if(cpt==NULL)\n"); + fprintf(fp," return(-1);\n"); + fprintf(fp," o->%s= cpt;\n",ct->name); + fprintf(fp," \n"); + } + } else { + fprintf(fp," o->%s= value;\n",ct->name); + } + fprintf(fp," return(1);\n"); + fprintf(fp,"}\n"); + fprintf(fp,"\n"); + } + +after_setter:; + } + + if(cgen->is_managed_list) { + fprintf(fp,"\n"); + if(cgen->make_ansi) { + sprintf(ptt,"int %s_link(struct %s *o, struct %s *link, int flag)", + cgen->functname,cgen->structname,cgen->structname); + fprintf(fp,"%s\n",ptt); +/* if(cgen->readonly) */ + { + ret= Cgen_write_to_ptt(cgen, ptt, 0); + if(ret<=0) + return(ret); + } + } else { + fprintf(fp,"int %s_link(o,link,flag)\n",cgen->functname); + fprintf(fp,"struct %s *o;\n",cgen->structname); + fprintf(fp,"struct %s *link;\n",cgen->structname); + fprintf(fp,"int flag;\n"); + } + fprintf(fp,"/*\n"); + fprintf(fp," bit0= insert as link->prev rather than as link->next\n"); + fprintf(fp,"*/\n"); + fprintf(fp,"{\n"); + fprintf(fp," if(o->prev!=NULL)\n"); + fprintf(fp," o->prev->next= o->next;\n"); + fprintf(fp," if(o->next!=NULL)\n"); + fprintf(fp," o->next->prev= o->prev;\n"); + fprintf(fp," o->prev= o->next= NULL;\n"); + fprintf(fp," if(link==NULL)\n"); + fprintf(fp," return(1);\n"); + fprintf(fp," if(flag&1) {\n"); + fprintf(fp," o->next= link;\n"); + fprintf(fp," o->prev= link->prev;\n"); + fprintf(fp," if(o->prev!=NULL)\n"); + fprintf(fp," o->prev->next= o;\n"); + fprintf(fp," link->prev= o;\n"); + fprintf(fp," } else {\n"); + fprintf(fp," o->prev= link;\n"); + fprintf(fp," o->next= link->next;\n"); + fprintf(fp," if(o->next!=NULL)\n"); + fprintf(fp," o->next->prev= o;\n"); + fprintf(fp," link->next= o;\n"); + fprintf(fp," }\n"); + fprintf(fp," return(1);\n"); + fprintf(fp,"}\n"); + fprintf(fp,"\n"); + + fprintf(fp,"\n"); + if(cgen->make_ansi) { + sprintf(ptt,"int %s_count(struct %s *o, int flag)", + cgen->functname,cgen->structname); + fprintf(fp,"%s\n",ptt); + ret= Cgen_write_to_ptt(cgen, ptt, 0); + if(ret<=0) + return(ret); + } else { + fprintf(fp,"int %s_count(o,flag)\n",cgen->functname); + fprintf(fp,"struct %s *o;\n",cgen->structname); + fprintf(fp,"int flag;\n"); + } + fprintf(fp,"/* flag: bit1= count from start of list */\n"); + fprintf(fp,"{\n"); + fprintf(fp," int counter= 0;\n"); + fprintf(fp,"\n"); + fprintf(fp," if(flag&2)\n"); + fprintf(fp," for(;o->prev!=NULL;o= o->prev);\n"); + fprintf(fp," for(;o!=NULL;o= o->next)\n"); + fprintf(fp," counter++;\n"); + fprintf(fp," return(counter);\n"); + fprintf(fp,"}\n"); + fprintf(fp,"\n"); + + fprintf(fp,"\n"); + if(cgen->make_ansi) { + sprintf(ptt, + "int %s_by_idx(struct %s *o, int idx, struct %s **pt, int flag)", + cgen->functname,cgen->structname,cgen->structname); + fprintf(fp,"%s\n",ptt); + ret= Cgen_write_to_ptt(cgen, ptt, 0); + if(ret<=0) + return(ret); + } else { + fprintf(fp,"int %s_count(o,idx,pt,flag)\n",cgen->functname); + fprintf(fp,"struct %s *o;\n",cgen->structname); + fprintf(fp,"int idx;\n"); + fprintf(fp,"struct %s **pt;\n",cgen->structname); + fprintf(fp,"int flag;\n"); + } + fprintf(fp, + "/* flag: bit0= fetch first (idx<0) or last (idx>0) item in list\n"); + fprintf(fp, + " bit1= address from start of list */\n"); + fprintf(fp,"{\n"); + fprintf(fp," int i,abs_idx;\n"); + fprintf(fp," struct %s *npt;\n",cgen->structname); + fprintf(fp,"\n"); + fprintf(fp," if(flag&2)\n"); + fprintf(fp," for(;o->prev!=NULL;o= o->prev);\n"); + fprintf(fp," abs_idx= (idx>0?idx:-idx);\n"); + fprintf(fp," *pt= o;\n"); + fprintf(fp," for(i= 0;(i<abs_idx || (flag&1)) && *pt!=NULL;i++) {\n"); + fprintf(fp," if(idx>0)\n"); + fprintf(fp," npt= o->next;\n"); + fprintf(fp," else\n"); + fprintf(fp," npt= o->prev;\n"); + fprintf(fp," if(npt==NULL && (flag&1))\n"); + fprintf(fp," break;\n"); + fprintf(fp," *pt= npt;\n"); + fprintf(fp," }\n"); + fprintf(fp," return(*pt!=NULL);\n"); + fprintf(fp,"}\n"); + fprintf(fp,"\n"); + } + + return(1); +} + + +int Cgen_write_c_method_include(cgen,flag) +struct CgeN *cgen; +int flag; +{ + FILE *fp= NULL; + char filename[4096],line[4096]; + struct stat stbuf; + time_t t0; + + sprintf(filename,"%s.c.methods",cgen->classname); + if(stat(filename,&stbuf)!=-1) + goto write_include; + fp= fopen(filename,"w"); + if(fp==NULL) { + sprintf(cgen->msg,"Cannot open file '%s' in %s-mode. %s", + filename,"w",strerror(errno)); + return(0); + } + fprintf(fp,"\n"); + fprintf(fp,"/* File %s */\n",filename); + fprintf(fp,"/* Manually provided C code for class %s */\n", + cgen->classname); + fprintf(fp,"/* This file gets copied to the end of %s.c */\n", + cgen->classname); + fprintf(fp,"\n"); + fclose(fp); fp= NULL; + +write_include:; + fp= fopen(filename,"r"); + if(fp==NULL) { + sprintf(cgen->msg,"Cannot open file '%s' in %s-mode. %s", + filename,"r",strerror(errno)); + return(0); + } + fprintf(cgen->fp,"\n"); + fprintf(cgen->fp, +"/* -------------- end of automatically regenerated code -------------- */\n"); + fprintf(cgen->fp,"\n"); + while(1) { + if(Sfile_fgets(line,sizeof(line)-1,fp)==NULL) + break; + fprintf(cgen->fp,"%s\n",line); + } + fclose(fp); fp= NULL; + return(1); +} + +int Cgen_write_c(cgen,flag) +struct CgeN *cgen; +int flag; +/* + bit0= also write access functions *_set_* *_get_* [*_link_*] +*/ +{ + int ret; + + sprintf(cgen->filename,"%s.c",cgen->classname); + ret= Cgen_open_wfile(cgen,0); + if(ret<=0) + goto ex; + ret= Cgen_write_c_head(cgen,0); + if(ret<=0) + goto ex; + ret= Cgen_write_c_new(cgen,0); + if(ret<=0) + goto ex; + ret= Cgen_write_c_destroy(cgen,0); + if(ret<=0) + goto ex; + if(flag&1) { + ret= Cgen_write_c_access(cgen,0); + if(ret<=0) + goto ex; + } + ret= Cgen_write_c_method_include(cgen,0); + if(ret<=0) + goto ex; + + if(cgen->make_ansi) { /* public .h file collected ANSI prototypes */ + ret= Cgen_finish_public_h(cgen,0); + if(ret<=0) + goto ex; + } + + ret= 1; +ex:; + if(cgen->fp!=NULL) + {fclose(cgen->fp); cgen->fp= NULL;} + return(ret); +} + + +int Cgen__write_global_include(global_include_file,flag) +char *global_include_file; +int flag; +/* + bit0= write footer rather than header + bit1= allow overwriting of existing file +*/ +{ + FILE *fp= NULL; + int ret; + char fmode[4],timetext[81],macro_name[4096],*cpt; + time_t t0; + struct stat stbuf; + + strcpy(macro_name,global_include_file); + for(cpt= macro_name; *cpt!=0; cpt++) { + if(*cpt>='A' && *cpt<='Z') + *cpt= tolower(*cpt); + else if((*cpt>='a' && *cpt<='z') || (*cpt>='0' && *cpt<='9') || *cpt=='_') + ; + else + *cpt= '_'; + } + macro_name[0]= toupper(macro_name[0]); + strcat(macro_name,"_includeD"); + + strcpy(fmode,"w"); + if(flag&1) { + strcpy(fmode,"a"); + } else { + if(stat(global_include_file,&stbuf)!=-1 && !(flag&2)) { + fprintf(stderr,"+++ File '%s' already existing.",global_include_file); + ret= 0; goto ex; + } + } + fp= fopen(global_include_file,fmode); + if(fp==NULL) { + fprintf(stderr,"+++ Cannot open file '%s' in %s-mode. %s", + global_include_file,fmode,strerror(errno)); + ret= 0; goto ex; + } + if(flag&1) { + fprintf(fp,"\n"); + fprintf(fp,"#endif /* %s */\n\n",macro_name); + t0= time(0); + strftime(timetext,sizeof(timetext),"%a, %d %b %Y %H:%M:%S GMT", + gmtime(&t0)); + fprintf(fp,"/* ( derived from stub generated by CgeN on %s ) */\n", + timetext); + + } else { + fprintf(fp,"\n"); + fprintf(fp,"#ifndef %s\n",macro_name); + fprintf(fp,"#define %s\n",macro_name); + fprintf(fp,"\n"); + } + +ex:; + if(fp!=NULL) + fclose(fp); + return(ret); +} + + +/* ---------------- Sfile and Sregex Emancipation copies ---------------- */ + + +char *Sfile_fgets(line,maxl,fp) +char *line; +int maxl; +FILE *fp; +{ +int l; +char *ret; + ret= fgets(line,maxl,fp); + if(ret==NULL) + return(NULL); + l= strlen(line); + if(l>0) 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); +} + + +int Sregex_string_cut(handle,text,len,flag) +char **handle; +char *text; +int len; +int flag; +/* + bit0= append (text!=NULL) + bit1= prepend (text!=NULL) +*/ +{ + int l=0; + char *old_handle; + + if((flag&(1|2))&&*handle!=NULL) + l+= strlen(*handle); + old_handle= *handle; + if(text!=NULL) { + l+= len; + *handle= TSOB_FELD(char,l+1); + if(*handle==NULL) { + *handle= old_handle; + return(0); + } + if((flag&2) && old_handle!=NULL) { + strncpy(*handle,text,len); + strcpy((*handle)+len,old_handle); + } else { + if((flag&1) && old_handle!=NULL) + strcpy(*handle,old_handle); + else + (*handle)[0]= 0; + if(len>0) + strncat(*handle,text,len); + } + } else { + *handle= NULL; + } + if(old_handle!=NULL) + Smem_freE(old_handle); + return(1); +} + + +int Sregex_string(handle,text,flag) +char **handle; +char *text; +int flag; +/* + bit0= append (text!=NULL) + bit1= prepend (text!=NULL) +*/ +{ + int ret,l=0; + + if(text!=NULL) + l= strlen(text); + +/* #define Sregex_looking_for_contenT 1 */ +#ifdef Sregex_looking_for_contenT + /* a debugging point if a certain text content has to be caught */ + if(text!=NULL) + if(strcmp(text,"clear")==0) + ret= 0; +#endif + + ret= Sregex_string_cut(handle,text,l,flag&(1|2)); + return(ret); +} + + +int Sregex_trimline(line,flag) +/* + removes line endings as well as leading and trailing blanks +*/ +char *line; +int flag; +/* + bit0= do not remove line end (protects trailing blanks if line end is present) + bit1= do not remove leading blanks + bit2= do not remove trailing blanks + bit3= remove surrounding quotation marks (after removing line end) +*/ +{ + char *cpt,*wpt; + int l; + + if(!(flag&1)){ + l= strlen(line); + if(l>0) 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; + } + if(flag&3){ + l= strlen(line); + if(l>1) if(line[0]==34 && line[l-1]==34) { + wpt= line; + cpt= wpt+1; + while(*cpt!=0) + *(wpt++)= *(cpt++); + line[l-2]= 0; + } + } + if(!(flag&2)){ + wpt= cpt= line; + while(*(cpt)!=0) { + if(!isspace(*cpt)) + break; + cpt++; + } + while(*(cpt)!=0) + *(wpt++)= *(cpt++); + *wpt= 0; + } + if(!(flag&4)){ + l= strlen(line); + if(l<=0) + return(1); + cpt= line+l; + while(cpt-->=line){ + if(!isspace(*cpt)) + break; + *(cpt)= 0; + } + } + return(1); +} + + +/* -------------------------------------------------------------- */ + + +main(argc,argv) +int argc; +char **argv; +{ + struct CgeN *cgen= NULL; + int ret, msg_printed= 0,first=1,gen_for_stic= 1, make_ansi= 0, i; + int make_lowercase= 0, may_overwrite= 0; + char global_include_file[4096]; + + global_include_file[0]= 0; + + for(i= 1; i<argc; i++) { + if(strcmp(argv[i],"-no_stic")==0) + gen_for_stic= 0; + else if(strcmp(argv[i],"-smem_local")==0) + gen_for_stic= 2; + else if(strcmp(argv[i],"-ansi")==0) + make_ansi= 1; + else if(strcmp(argv[i],"-global_include")==0) { + if(i+1>=argc) + strcpy(global_include_file,"global_include.h"); + else { + i++; + strcpy(global_include_file,argv[i]); + } + } else if(strcmp(argv[i],"-lowercase")==0) { + make_lowercase= 1; + } else if(strcmp(argv[i],"-overwrite")==0) { + may_overwrite= 1; + } else { + fprintf(stderr,"+++ %s: Unrecognized option: %s\n",argv[0],argv[i]); + {ret= 0; goto ex;} + } + } + + if(strlen(global_include_file)>0) { + /* begin */ + ret= Cgen__write_global_include(global_include_file,(!!may_overwrite)<<1); + if(ret<=0) + goto ex; + } + while(!feof(stdin)) { + ret= Cgen_new(&cgen,0); + if(ret<=0) + goto ex; + + /* <<< can be done neater */ + cgen->gen_for_stic= gen_for_stic; + cgen->make_ansi= make_ansi; + strcpy(cgen->global_include_file,global_include_file); + cgen->make_lowercase= make_lowercase; + cgen->may_overwrite= may_overwrite; + + ret= Cgen_read_fp(cgen,stdin,first); + if(ret<=0) + goto ex; + if(ret==2) + break; + first= 0; + ret= Cgen_write_h(cgen,0); + if(ret<=0) + goto ex; + ret= Cgen_write_c(cgen,1); + if(ret<=0) + goto ex; + } + if(strlen(global_include_file)>0) { + /* finalize */ + ret= Cgen__write_global_include(global_include_file,1); + if(ret<=0) + goto ex; + } + ret= 1; +ex: + if(cgen!=NULL) + if(cgen->msg[0]!=0) { + fprintf(stderr,"+++ %s\n",cgen->msg); + msg_printed= 1; + } + if(ret<=0 &&!msg_printed) { + if(errno>0) + fprintf(stderr,"+++ Error : %s\n",strerror(errno)); + else if(ret==-1) + fprintf(stderr, + "+++ Program run failed (probably due to lack of memory)\n"); + else + fprintf(stderr,"+++ Program run failed\n"); + } + Cgen_destroy(&cgen,0); + exit(1-ret); +} diff --git a/libburn/branches/ZeroFourTwo/libcevap/cgen.h b/libburn/branches/ZeroFourTwo/libcevap/cgen.h new file mode 100644 index 00000000..5b464a0f --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libcevap/cgen.h @@ -0,0 +1,35 @@ + +#ifndef Cgen_includeD +#define Cgen_includeD Yes + + + +struct CgeN { + + char *classname; + char *structname; + char *functname; + + int is_managed_list; + int is_bossless_list; + int gen_for_stic; /* 0=no smem,srgex,sfile , 1=all three, 2=smem only */ + int make_ansi; + int make_lowercase; + char global_include_file[4096]; + FILE *global_include_fp; + + struct CtyP *elements; + struct CtyP *last_element; + + int may_overwrite; + FILE *fp; + char filename[4096]; + FILE *ptt_fp; + char ptt_filename[4096]; + + char msg[8192]; +}; + + +#endif /* Cgen_includeD */ + diff --git a/libburn/branches/ZeroFourTwo/libcevap/cgen.txt b/libburn/branches/ZeroFourTwo/libcevap/cgen.txt new file mode 100644 index 00000000..d260af95 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libcevap/cgen.txt @@ -0,0 +1,222 @@ + + +Description of the helper program stic*/bin/cgen + +cgen is copyright 2001 to 2007, Thomas Schmitt <stic-source@gmx.net> +and provided under BSD license. + +Compilation: + cc -g -o cgen cgen.c ctyp.c smem.c + + +cgen produces a class stub in C programming language. The data structure of +the class is described by some lines which get read from stdin. The stub will +consist of four files which emerge in the current working directory: + <classname>.h public header file of the class + <classname>.c automatically generated C code of the class + plus a copy of <classname>.c.methods + <classname>_private.h private header file of the class + <classname>.c.methods safe storage for manually created functions. + From here they get copied into the generated stub. + If such a file is missing, a dummy gets created. +It will define a struct <ClassnamE> for representing the class data aspects, +construtor <Classname>_new(), destructor <Classname>_destroy(), +getter <Classname>_<element>_get() for each structure element. +Some more functions get added for particular class and element roles. + +cgen normally refuses to overwrite existing files because it supposes that +those contain code added by the human programmer. +Human programmer enhancements may be explanatory comments, class specific +methods, initial element values and other special precautions within the +generated functions. +As long as the modelling phase is going on, one may store such human code +in <classname>.c.methods and may use command line option -overwrite for +modelling development cycles. + +At some point of model matureness one may decide to give up cgen and the +.c.method files and to go on only with _private.h , .h and .c files. + + +Command line options + + -no_stic prevents usage of stic_dir/s_tools/*.[ch] + + -ansi generates ANSI C function heads and makes file <classname>.h hold + only public definitions: an opaque declaration of the class struct + and a list of function prototypes. The definiton of the class + struct is then in <classname>_private.h . + -global_include filename + sets the name of a file which will contain globally necessary + declarations. Currently it lists the existence of all class + structs. + -lowercase generate struct <classname> rather than struct <ClassnamE> and + function names <classname>_func() rather than <Classname>_func() . + -overwrite allows to overwrite files <classname>_private.h, <classname>.h + and <classname>.c, but not <classname>.c.methods. + + +Input line format: + +There are two states of input: class level and element level. +Exampes are shown below with class roles and element roles. + +Input starts at class level. A class level line may be one of + +- Comment. A line which begins with '#' is ignored on class level. + +- Empty. A line with no characters is a comment with empty text (i.e. ignored). + +- Class. Options which begin with '-' and finally a word in lowercase letters + which defines the <classname>. The classname leads to a struct ClassnamE + and some class methods implemented as C functions <Classnname>_<func>(). + +- End of input. Line "@@@" or EOF at stdin end the program run. + +After a class line, input switches to element level where a line may be: + +- Comment. A line which after some white space begins with '#' is considered + a comment. The preceeding white space is ignored and the text after '#' is + eventuellay trimmed by a single blank at both ends. This text will be part + of the class struct definition within file <classname_private>.h as a single + C comment line /* ... */. The sequence of elements and comments is preserved. + An empty comment text leads to an empty line in <classname_private>.h. + +- Empty. A line with no characters is a comment with empty text. + +- Element. Options which begin with '-', eventual C keywords "unsigned" or + "volatile", type or "struct <NamE>", element name. This leads to a struct + element which is taken into respect in some class methods. Depending on the + options in this line, some element methods <Classnname>_<func>_<element>() + may get generated. + +- End of class. A single '@' marks the end of the element list and brings + input back to class level. I.e. next is expected another class name or + "@@@" or EOF at stdin. + +Input semantics: + +A class can have one of two roles: + +- Standalone class. + Input example: + my_class + +- Listable class, which has pointers to peer instances: .prev and .next + Such classes get a list destructor <Classname>_destroy_all() which destroys + all members of a list (which is given by any of the list members). + Such a class should have a pointer *boss as first element in which case + the constructor will look like + <Classname>_new(struct <ClassnamE> **o,struct <Some_clasS> *boss,int flag); + There is a function <Classname>_link() which inserts an instance into a list + and a function <Classname>_count() which tells the number of list members. + For pseudo random access there is function <Classname>_by_idx(). + Input example: + -l my_class + + +A modifier is defined for classes: + +- Bossless. Disables a special boss-subordinate relationship which is created + if the first element of a class is a struct pointer with the name "boss". + Like + -l <classname> + -v struct Some_clasS *boss + Normally such a parameter *boss becomes part of the constructor method + <Classname>_new(struct <ClassnamE> **o, struct Some_clasS *boss, int flag); + This relationship is typical for a listable class and a single class which + is designed to host instances of that listable class. Therefore one gets a + warning if a listable class does not begin with a struct pointer *boss. + But if -b is given, then CgeN ill not include a parameter *boss into the + constructor. It will rather look normal: + <Classname>_new(struct <ClassnamE> **o, int flag); + It will not warn if the first element of a listable class is not struct + pointer *boss. + + +Elements have one of the following roles: + +- Value. It provides only storage for a C data type (which may be a C pointer + despite the role name "value"), a getter method <Classname>_<element>_get(), + and a setter method <Classname>_<element>_set(). + Input examples: + -v int i + -v int a[100] + -v char *cpt + -v struct xyz x + -v struct xyz *xpt + +- Managed. This has to be a pointer to a struct <XyZ> or to char. It will not + get attached to an object by the stub's code but its destructor + <Xyz>_destroy() will be called by <Classname>_destruct(). In case of (char *) + it is supposed that a non-NULL value has been allocated by malloc(). + Managed (char *) types get a setter function <Classname>_<element>_set() + which allocates memory and copies the textstring from its parameter. + Input examples: + -m struct XyZ *xyzpt + -m char *textstring + +- Chainlink. A pair of prev-next-style pointers to the own class struct. + Function <Classname>_destruct() will unlink the affected instance and + put together its link partners. + Input example (there must always be two consequtive -c lines): + -c struct My_clasS *up + -c struct My_clasS *down + +- List. A pair of pointers to the struct <XyZ> of a listable class. The first + one <ls> holds the start of the list, the second one <eol> holds the end. + The getter function has an additional argument idx: + <Classname>_get_<ls>(struct <ClassnamE> *o, int idx, struct <XyZ> **pt, + int flag) + idx == 0 is the start of the list, idx=1 the next element, ... + idx == -1 retrieves the last element of the list. + For insertion of list items there is provided method <Classname>_new_<ls>(). + The inserted item is reachable via the getter function with idx == -1 + <Classname>_destroy() instance calls <Xyz>_destroy_all(). Note that the end + pointer is always generated as private element (-p). + Input example (there must always be a -l and a -v line): + -l struct XyZ *list_start + -v struct XyZ *list_end + +The availability of getter method <Classname>_get_<element>(), and setter +method <Classname>_set_<element>_set() can be controled by two modifiers: + +- Readonly. Only a getter method. + Input example + -r -v int broadcasted_status + +- Private. Neither getter nor setter method. + Input example + -p -v int private_value + +- Bossless listable. This marks elements which are listable objects but do not + expect a boss pointer in their constructor. See above: Listable class and + the bossless modifier for classes. + Input example + -b -l struct XyZ *list + -v struct XyZ *last_in_list + +- Initialization free. The class constructor will not initialize this element. + This modifier has to be used if neither NULL nor 0 are suitable + initialization values. + +Example run: + + rm class_x.c class_x.h class_y.c class_y.h + bin/cgen <<+ +-l class_x +-r -v struct Boss_clasS *boss +-v int x +-r -v struct stat stbuf +-m struct Class_Y *y +-m char *text +-c struct Class_X *master +-c struct Class_X *slave +-b -l struct Class_X *provider +-p -v struct Class_X *last_provider +@ +-b -l class_y +-r -v char providername[80] +@ ++ + + diff --git a/libburn/branches/ZeroFourTwo/libcevap/ctyp.c b/libburn/branches/ZeroFourTwo/libcevap/ctyp.c new file mode 100644 index 00000000..2b275756 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libcevap/ctyp.c @@ -0,0 +1,364 @@ + +/* + cc -g -o ctyp.c +*/ + +#include <sys/types.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "smem.h" +extern char *Sfile_fgets(); +extern int Sregex_string(); +extern int Sregex_trimline(); + +#include "ctyp.h" + + +/* -------------------------- CtyP ----------------------- */ + + +int Ctyp_new(objpt,link,flag) +struct CtyP **objpt; +struct CtyP *link; +int flag; +{ + struct CtyP *o; + int ret; + + *objpt= o= TSOB_FELD(struct CtyP,1); + if(o==NULL) + return(-1); + + o->is_comment= 0; + o->is_pointer= 0; + o->is_struct= 0; + o->is_unsigned= 0; + o->is_volatile= 0; + o->array_size= 0; + o->management= 0; + o->with_getter= 1; + o->with_setter= 1; + o->bossless_list= 0; + o->no_initializer= 0; + o->dtype= NULL; + o->name= NULL; + o->prev= NULL; + o->next= NULL; + + if(link!=NULL) + link->next= o; + o->prev= link; + + return(1); +failed:; + Ctyp_destroy(objpt,0); + return(-1); +} + + +int Ctyp_destroy(objpt,flag) +struct CtyP **objpt; +int flag; +{ + struct CtyP *o; + + o= *objpt; + if(o==NULL) + return(0); + + if(o->prev!=NULL) + o->prev->next= o->next; + if(o->next!=NULL) + o->next->prev= o->prev; + Sregex_string(&(o->dtype),NULL,0); + Sregex_string(&(o->name),NULL,0); + + free((char *) o); + *objpt= NULL; + return(1); +} + + +int Ctyp_get_pointer_level(ct,flag) +struct CtyP *ct; +int flag; +{ + return(ct->is_pointer); +} + + +int Ctyp_is_struct(ct,flag) +struct CtyP *ct; +int flag; +{ + return(ct->is_struct); +} + + +int Ctyp_get_array_size(ct,flag) +struct CtyP *ct; +int flag; +{ + return(ct->array_size); +} + + +int Ctyp_get_management(ct,flag) +struct CtyP *ct; +int flag; +{ + return(ct->management); +} + + +int Ctyp_get_with_getter(ct,flag) +struct CtyP *ct; +int flag; +{ + return(ct->with_getter); +} + + +int Ctyp_get_with_setter(ct,flag) +struct CtyP *ct; +int flag; +{ + return(ct->with_setter); +} + + +int Ctyp_get_dtype(ct,text,flag) +struct CtyP *ct; +char **text; /* must point to NULL of freeable memory */ +int flag; +/* + bit0=eventually prepend "struct " +*/ +{ + if((flag&1) && ct->is_struct) { + if(Sregex_string(text,"struct ",0)<=0) + return(-1); + } else { + if(Sregex_string(text,"",0)<=0) + return(-1); + } + if(Sregex_string(text,ct->dtype,1)<=0) + return(-1); + return(1); +} + + +int Ctyp_get_name(ct,text,flag) +struct CtyP *ct; +char **text; /* must point to NULL of freeable memory */ +int flag; +{ + if(Sregex_string(text,ct->name,0)<=0) + return(-1); + return(1); +} + + +int Ctyp_get_type_mod(ct,is_spointer,is_struct,array_size,flag) +struct CtyP *ct; +int *is_spointer,*is_struct,*array_size; +int flag; +{ + *is_spointer= ct->is_pointer; + *is_struct= ct->is_struct; + *array_size= ct->array_size; +} + +int Ctyp_new_from_line(ct,link,line,msg,flag) +struct CtyP **ct; +struct CtyP *link; +char *line; +char *msg; +int flag; +/* + bit0= make struct ClassnamE to struct classname +*/ +{ + struct CtyP *o; + char *cpt,*bpt; + int ret,l; + char orig_line[4096]; + + ret= Ctyp_new(ct,*ct,0); + if(ret<=0) { + sprintf(msg,"Failed to create CtyP object (due to lack of memory ?)"); + goto ex; + } + o= *ct; + + strcpy(orig_line,line); + cpt= line; + while(*cpt!=0 && isspace(*cpt)) cpt++; + if(cpt[0]=='#') { + cpt++; + if(cpt[1]==' ') + cpt++; + l= strlen(cpt); + if(cpt[0]==' ') + cpt++; + if(l>1) + if(cpt[l-1]==' ') + cpt[l-1]= 0; + if(Sregex_string(&(o->name),cpt,0)<=0) + {ret= -1; goto ex;} + o->is_comment= 1; + {ret= 1; goto ex;} + } else if(cpt[0]==0) { + if(Sregex_string(&(o->name),cpt,0)<=0) + {ret= -1; goto ex;} + o->is_comment= 1; + {ret= 1; goto ex;} + } else if(cpt[0]=='/' && cpt[1]=='*') { + sprintf(msg, + "C-style multi line comments (/* ... */) not supported yet. Use #."); + goto ex; + + /* >>> */ + + } + cpt= line; + while(cpt[0]=='-') { + /* look for management specifiers: + -v* just a value + -m* allocated memory which needs to be freed + -c* mutual link (like prev+next) + -l* list of -m chained by mutual links prev and next + + -r* read-only : no setter function + -p* private : neither setter nor getter function + -b* bossless_list : Class_new(o,flag), not Class_new(o,boss,flag) + -i* no_initializer : do not initialize element in <Class>_new() + #... line is a comment + */ + if(cpt[1]=='v' || cpt[1]=='V') { + o->management= 0; + } else if(cpt[1]=='m' || cpt[1]=='M') { + o->management= 1; + } else if(cpt[1]=='c' || cpt[1]=='C') { + o->management= 2; + if(o->prev!=NULL) + if(o->prev->management==2) + o->management= 3; + } else if(cpt[1]=='l' || cpt[1]=='L') { + o->management= 4; + } else if(cpt[1]=='r' || cpt[1]=='R') { + o->with_setter= 0; + } else if(cpt[1]=='p' || cpt[1]=='P') { + o->with_setter= 0; + o->with_getter= 0; + } else if(cpt[1]=='b' || cpt[1]=='B') { + o->bossless_list= 1; + } else if(cpt[1]=='i' || cpt[1]=='I') { + o->no_initializer= 1; + } + while(*cpt!=0 && !isspace(*cpt)) cpt++; + while(*cpt!=0 && isspace(*cpt)) cpt++; + if(*cpt==0) + goto no_name; + } + + if(strncmp(cpt,"struct ",7)==0) { + o->is_struct= 1; + cpt+= 7; + } else if(strncmp(cpt,"unsigned ",9)==0) { + o->is_unsigned= 1; + cpt+= 9; + } else if(strncmp(cpt,"volatile ",9)==0) { + o->is_volatile= 1; + cpt+= 9; + if(strncmp(cpt,"unsigned ",9)==0) { + o->is_unsigned= 1; + cpt+= 9; + } + } + if(*cpt==0) + goto no_name; + while(*cpt!=0 && isspace(*cpt)) cpt++; + bpt= cpt; + while(*bpt!=0 && !isspace(*bpt)) bpt++; + if(*bpt==0) + goto no_name; + if(*bpt==0) { +no_name:; + sprintf(msg,"No name found after type description : %s",orig_line); + ret= 0; goto ex; + } + *bpt= 0; + if(Sregex_string(&(o->dtype),cpt,0)<=0) + {ret= -1; goto ex;} + if((flag&1) && o->is_struct && strlen(o->dtype)>=3) + if(isupper(o->dtype[0]) && islower(o->dtype[1]) && + isupper(o->dtype[strlen(o->dtype)-1])) { + o->dtype[0]= tolower(o->dtype[0]); + o->dtype[strlen(o->dtype)-1]= tolower(o->dtype[strlen(o->dtype)-1]); + } + cpt= bpt+1; + while(*cpt!=0 && isspace(*cpt)) cpt++; + if(*cpt==0) + goto no_name; + for(;*cpt=='*';cpt++) + o->is_pointer++; + if(*cpt==0) + goto no_name; + bpt= strchr(cpt,'['); + if(bpt!=NULL) { + if(strchr(bpt,']')!=NULL) + *strchr(bpt,']')= 0; + sscanf(bpt+1,"%lu",&(o->array_size)); + *bpt= 0; + } + if(Sregex_string(&(o->name),cpt,0)<=0) + {ret= -1; goto ex;} + if(o->management==1) { + if((!(o->is_pointer>=1 && o->is_pointer<=2)) || + ((!o->is_struct) && strcmp(o->dtype,"char")!=0 && + (strcmp(o->dtype,"unsigned char")!=0))) { + sprintf(msg,"-m can only be applied to pointers of struct or char : %s", + orig_line); + ret= 0; goto ex; + } + } + ret= 1; +ex:; + return(ret); +} + + +int Ctyp_read_fp(ct,fp,msg,flag) +struct CtyP **ct; +FILE *fp; +char msg[]; /* at least [4096+256] */ +int flag; +/* + bit0= make struct ClassnamE to struct classname +*/ +{ + int ret; + char line[4096]; + struct CtyP *o; + + line[0]= 0; + printf( + "[-value|-managed|-chain|-list] class element ? (e.g.: -l struct XyZ)\n"); + if(Sfile_fgets(line,sizeof(line)-1,fp)==NULL) + {ret= 2; goto ex;} + printf("%s\n",line); + Sregex_trimline(line,0); + if(strcmp(line,"@")==0) + {ret= 2; goto ex;} + ret= Ctyp_new_from_line(ct,*ct,line,msg,flag&1); + if(ret<=0) + goto ex; + ret= 1; +ex:; + return(ret); +} + diff --git a/libburn/branches/ZeroFourTwo/libcevap/ctyp.h b/libburn/branches/ZeroFourTwo/libcevap/ctyp.h new file mode 100644 index 00000000..13657b03 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libcevap/ctyp.h @@ -0,0 +1,41 @@ + +#ifndef Ctyp_includeD +#define Ctyp_includeD + + + +struct CtyP { + + /* if 1 : .name contains comment text, all other elements are invalid */ + int is_comment; + + int is_pointer; /* number of asterisks */ + int is_struct; + int is_unsigned; + int is_volatile; + unsigned long array_size; + + int management; /* + -v 0= just a value + -m 1= allocated memory which needs to be freed + -c 2= mutual link with the next element + -c 3= mutual link with the prev element + -l 4= list of -m , chained by -c pair named 'prev','next' + supposed to be followed by a -v of the same type + which will mark the end of the list + */ + int with_getter; + int with_setter; + int bossless_list; + int no_initializer; + + char *dtype; + char *name; + + struct CtyP *prev; + struct CtyP *next; +}; + + +#endif /* Ctyp_includeD */ + diff --git a/libburn/branches/ZeroFourTwo/libcevap/extract_cgen_input.sh b/libburn/branches/ZeroFourTwo/libcevap/extract_cgen_input.sh new file mode 100755 index 00000000..3ca096fb --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libcevap/extract_cgen_input.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +copy_mode=0 + +while true +do + read line + if test "$copy_mode" = "0" + then + if echo " $line" | grep '^ Cgen=' >/dev/null 2>&1 + then + copy_mode=1 + if echo " $line" | grep '^ Cgen=..' >/dev/null 2>&1 + then + echo " $line" | sed -e 's/^ Cgen=//' + fi + elif echo " $line" | grep '^ =end Model=' >/dev/null 2>&1 + then +break + fi + else + if test " $line" = " @" + then + copy_mode=0 + echo "@" + else + echo " $line" | sed -e 's/^ //' + fi + fi +done diff --git a/libburn/branches/ZeroFourTwo/libcevap/libcevap_gen.sh b/libburn/branches/ZeroFourTwo/libcevap/libcevap_gen.sh new file mode 100755 index 00000000..48ed7862 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libcevap/libcevap_gen.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +test_dir=. +model_file=./libdax_model.txt +xtr_dir=. +cgen_dir=. +# cgen_dir=~/projekte/cdrskin_dir/libburn-develop/libcevap + +cd "$test_dir" || exit 1 +test -e smem.h || exit 1 + +cat "$model_file" | \ + "$xtr_dir"/extract_cgen_input.sh | \ + "$cgen_dir"/cgen -smem_local -ansi -global_include cevap_global.h \ + -overwrite "$@" + diff --git a/libburn/branches/ZeroFourTwo/libcevap/libdax_equip.gif b/libburn/branches/ZeroFourTwo/libcevap/libdax_equip.gif new file mode 100644 index 0000000000000000000000000000000000000000..3df86aee659d6d21ff64a771e46b0272c777cad2 GIT binary patch literal 10751 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 OoZ)B!*5d~N{Qm<)=B3*J literal 0 HcmV?d00001 diff --git a/libburn/branches/ZeroFourTwo/libcevap/libdax_job.gif b/libburn/branches/ZeroFourTwo/libcevap/libdax_job.gif new file mode 100644 index 0000000000000000000000000000000000000000..8138ad25c020c2bd196c53d1558849ab283f249e GIT binary patch literal 10532 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 M1-lnG7=XzC16(KeYXATM literal 0 HcmV?d00001 diff --git a/libburn/branches/ZeroFourTwo/libcevap/libdax_model.txt b/libburn/branches/ZeroFourTwo/libcevap/libdax_model.txt new file mode 100644 index 00000000..5b5c3c76 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libcevap/libdax_model.txt @@ -0,0 +1,944 @@ + +# +# libdax is the early working name for the successor software of libburn, +# a library for writing information onto optical media, i.e. CD and DVD. +# The current working name is libcevap, refering to various kinds of roasts in +# Europe and Asia which share the property to be structured in smaller pieces. +# +# The reason for the replacement is the unclear copyright situation as well +# as libburn's sketchy original state and the subsequential evolutionary +# damages done by us in libburn code. +# This does not mean libburn is shaky. Its current state just reflects the +# virtual conflict of at least two programmer personalities and their goals. +# + +# +# Please: Nobody shall take the development of libcevap as a reason for not +# programming an application which uses libburn. +# libburn works now. libcevap is planned to work in future. +# +# libcevap will replace libburn in a controlled, application friendly way. +# The first application of libcevap will be a libburn API wrapper which will +# allow to perform all API calls of libburn which are proveable to work in +# the current implementation. (Some CD stuff is not understood by us yet. +# We will have to learn.) +# +# The libburn API will be frozen when libcevap has closed up to its current +# capabilities. Nevertheless it may take profit from some of the future +# progress in libcevap (e.g. new media types). +# We hope that finally libcevap will have capabilities superior to libburn. +# This will then be a reason to port applications to the libcevap API. +# +# Application programmers are advised to encapsulate their libburn API calls +# in an own abstraction layer. The semantic concepts of burning will be +# compatible between libburn and libcevap. I.e you will have a library object +# to be started up, drives to be found and grabbed, media and their states +# to be identified, sessions, tracks, burn options to be set, blanking, +# formatting, and so on. +# Data types, function calls, and quirks will be incompatible between both +# APIs, though. +# + +# ------------------------------------------------------------------------- + +# Originally this was a backup of text input clicketitoggled into ArgoUML +# Meanwhile it becomes an intermediate storage for attributes and +# class interconnections in the notation of my C stub generator CgeN +# (see also end of this text) + +# next : work on completeness : cevapformat + +# Open questions: +# - how to connect to GESTURES ? Globally ? + + +Model=libdax + +ClassDiagram=Overview + +Class=API +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=11.03.2007 +Documentation=\ +The API is the only layer visible to the applications. It exposes MMC concepts +which it reflects and augments by its own architectural concepts. +Subordinates=EQUIP,JOB,AUX +Cgen=\ +cevapi +-r -m struct CevapequiP *equip +-r -m struct CevapjoB *job +-r -m struct CevapauX *aux +-r -m struct CevapgestureS *gestures +@ +=end Class + +Class=EQUIP +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=11.03.2007 +Documentation=\ +EQUIP represents the physical and logical equipment in reach of libdax. +This includes the system, drives, media, and their current states. +PeerToPeer=GESTURES +Boss=API +Cgen=\ +cevapequip +-r -v struct CevapI *boss +-r -m struct CevapsysteM *sys +-v struct CevapgestureS *gestures +@ +=end Class + +Class=JOB +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=11.03.2007 +Documentation=\ +JOB models the tasks to be performed via libdax. +This includes disc, session, track, source, fifo, dewav, burn options. +PeerToPeer=GESTURES +Boss=API +Cgen=\ +cevapjob +-r -v struct CevapI *boss +-r -m struct CevaptodO *todo +-v struct CevapgestureS *gestures + +# >>> + +@ +=end Class + +Class=AUX +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=11.03.2007 +Documentation=\ +AUX bundles any models which are neither EQUIP nor JOB. +This includes abort handler and message system. +PeerToPeer=GESTURES +Boss=API +Cgen=\ +cevapaux +-r -v struct CevapI *boss +-v struct CevapgestureS *gestures + +# >>> + +@ +=end Class + +Class=GESTURES +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=11.03.2007 +Documentation=\ +GESTURES ist the procedural repertoire which interconnects EQUIP, JOB, and AUX +and also provides to them the services from the SCSI oriented layers. +PeerToPeer=EQUIP,JOB,AUX +Subordinates=SCSI_CMD +Cgen=\ +cevapgestures +-r -v struct CevapI *boss +-v struct CevapequiP *equip +-v struct CevapjoB *job +-v struct CevapauX *aux +-r -m struct CevapscmD *scsi_cmd + +# >>> + +@ +=end Class + +Class=SCSI_CMD +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=11.03.2007 +Documentation=\ +SCSI_CMD represents the semantic part of SCSI (i.e. mainly MMC) specs. +This layer models each SCSI command that is used by libdax. It knows about +its parameters and constraints with particular equipment and jobs. +Boss=GESTURES +Subordinates=Classes with SCSI_EXEC Interface +Cgen=\ +cevapscmd +-r -v struct CevapgestureS *boss +-r -m struct CevapsexeC *scsi_exec + +# >>> + +@ +=end Class + +Interface=SCSI_EXEC +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=16.03.2007 +Documentation=\ +SCSI_EXEC hides the difference between the implementation principle of +SCSI format+transport and the principle of SCSI service. +Boss=SCSI_CMD +Implementations=SCSI_FORMAT,SCSI_SERVICE +Cgen=\ +cevapsexec +-r -v struct CevapscmD *boss +-p -v struct CevapsforM *scsi_format +-p -v struct CevapsservicE *scsi_service +-v int silent_on_scsi_error + +# >>> + +@ +=end Interface + + +Class=OSDriveAspect +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=18.08.2007 +Documentation=\ +OSDriveAspect encapsulates operating system specific properties of an +individual drive. It shall be handed out by SCSI_EXEC via the GESTURES layer +to EquipDrive where it forms the link between cevap drive model and operating +system driver. +This class description models the implementation specific to Linux. +Cgen=\ +cevaposdrv +-r -v int fd + +# >>> ??? implement the sibling stuff which never worked properly ? + +@ +=end Class + + +Class=SCSI_FORMAT +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=11.03.2007 +Documentation=\ +SCSI_FORMAT translates parameters of SCSI commands into CDBs, takes care for +transport and decodes the reply into parameters. +Boss=SCSI_CMD via SCSI_EXEC +Subordinates=SCSI_TRANSPORT +Cgen=\ +cevapsform +-r -v struct CevapsexeC *boss +-p -v struct CevapstransP *scsi_transport + +# former struct command +-v unsigned char opcode[16] +-v int oplen +-v int dir +-v int dxfer_len +-v unsigned char sense[128] +-v int error +-v int retry +-v struct CevapbuffeR *page + +# >>> + +@ +=end Class + +Class=SCSI_TRANSPORT +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=11.03.2007 +Documentation=\ +SCSI_TRANSPORT takes a formatted CDB from SCSI_FORMAT and makes the operating +system perform a SCSI transaction. It then returns the reply data in raw form. +Boss=SCSI_FORMAT +Os_specific=yes +Cgen=\ +cevapstransp +-r -v struct CevapsforM *boss + +# >>> +@ +=end Class + +Class=SCSI_SERVICE +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=11.03.2007 +Documentation=\ +SCSI_SERVICE provides the combined services of SCSI_FORMAT and SCSI_TRANSPORT +via a set of parametrized functions which abstract SCSI command transactions. +Boss=SCSI_CMD via SCSI_EXEC +Os_specific=yes +Cgen=\ +cevapsservice +-r -v struct CevapsexeC *boss + +# >>> +@ +=end Class + +=end ClassDiagram=Overview + + + +ClassDiagram=Equip_overview + +Class=EquipSystem +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=16.03.2007 +Documentation=\ +EquipSystem is the inner root class of EQUIP. It describes the system on +which libdax is working. This includes the operating system, the system +adapter classes, the drives. +Boss=EQUIP +Subordinates=EquipDrive*N +Cgen=\ +cevapsystem +-r -v struct CevapequiP *boss +-r -m char *infotext +-r -l struct CevapdrivE *drive +-p -v struct CevapdrivE *eol_drive + +# >>> be boss of SCSI_CMD ? (Rather than GESTURES) + +# >>> +@ +=end Class + + +Class=EquipDrive +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=16.03.2007 +Documentation=\ +EquipDrive represents a drive, including its capabilities, its processing +status, the media loaded. +Subordinates=EquipMedia +Boss=EquipSystem +Cgen=\ +-l cevapdrive +-r -v struct CevapsysteM *boss + +# Drive number +-r -v int global_index + +# Persistent system drive address +-r -m char *devname + +# Traditional SCSI address parameters (-1 if not applicable) +-r -v int bus_no +-r -v int host +-r -v int id +-r -v int channel +-r -v int lun + +# (former struct burn_scsi_inquiry_data idata) +# From 12h INQUIRY , spc3r23.pdf , 6.4.2 , Table 81 +-r -v char vendor[9] +-r -v char product[17] +-r -v char revision[5] +# 1= above elements contain valid information +-r -v int idata_valid + +# mc5r03c.pdf 5.3.2 Physical Interface Standard +# 1=SCSI, 2=ATAPI, 3,4,6=FireWire, 7=SATA, 8=USB +-r -v int phys_if_std +# MMC-5 5.3.2 table 91 , e.g. "SCSI Family" +-r -m char *phys_if_name + +# System despendent aspect of the drive (e.g. int fd;) +-r -v struct CevaposdrV *system_dep_drive_info + +# Result of the CD write mode x block type tests: +# Index is for write mode : 0=packet , 1=TAO , 2=SAO , 3=raw +# Bits are for block type +# Numbering as in mc5r03c.pdf 7.5.4.13 Data Block Type, Table 668 : +# 0=RAW0 (2352, Raw data) +# 1=RAW16 (2368, Raw data with P and Q Sub-channel +# 2=RAW96P (2448, Raw data with P-W Sub-channel appended) +# 3=RAW96R (2448, Raw data with raw P-W Sub-channel appended) +# 8=MODE1 (2048, ISO/IEC 10149) +# 9=MODE2R (2336, Mode 2 formless) +# 10=MODE2F1 (2048, CD-ROM XA, form 1) +# 11=MODE2F1X (2056, CD-ROM XA, form 1 plus 8 byte subheader) +# 12=MODE2F2 (2324, CD-ROM XA, form 2) +# 13=MODE2MIX (2332, CD-ROM XA, form 1, form 2, or mixed form) +-r -v int block_types[4] + +# (former struct scsi_mode_data) +# Information about the drive's capabilities, obtained via 5Ah MODE SENSE +# from mode page 2Ah , mmc3r10g.pdf , 6.3.11 , Table 361 +# (which is deprecated in MMC-5 E.11) +-p -v int mdata_buffer_size +-p -v int mdata_dvdram_read +-p -v int mdata_dvdram_write +-p -v int mdata_dvdr_read +-p -v int mdata_dvdr_write +-p -v int mdata_dvdrom_read +-p -v int mdata_cdrw_read +-p -v int mdata_cdrw_write +-p -v int mdata_cdr_read +-p -v int mdata_cdr_write +-p -v int mdata_max_read_speed +-p -v int mdata_max_write_speed +-p -v int madata_min_write_speed +-p -v int mdata_cur_read_speed +-p -v int mdata_cur_write_speed +-p -v int mdata_simulate +-p -v int mdata_c2_pointers +-r -v int mdata_underrun_proof + +# Results from ACh GET PERFORMANCE, Type 03h +# (Speed values go into *_*_speed) +# (speed_descriptors became cevapperf which is under cevapmedia) +-p -v int min_end_lba +-p -v int max_end_lba + +# from mode page 01h , mmc5r03c.pdf , 7.3.2.1 , Table 657 +-p -v int mdata_retry_page_length +-p -v int mdata_retry_page_valid + +# from mode page 05h , mmc5r03c.pdf , 7.5.4.1 , Table 664 +-p -v int mdata_write_page_length +-p -v int mdata_write_page_valid + +# 1= above elements contain valid information +-p -v int mdata_valid + +# The mutex shall be used to coordinate access to the drive in situations +# where multi-threaded race conditions could disturb operations. +# E.g. lock, read busy state, interpret, set busy state, unlock +# A mere reader of the busy state does not have to lock because +# reading of the state itself is atomar. +-i -v pthread_mutex_t access_lock + +# Flags from feature 002Fh feature descriptor mmc5r03c.pdf 5.3.25 : +# bit1= DVD-RW supported +# bit2= Test Write available +# bit3= DVD-R DL supported +# bit6= Buffer Under-run Free recording available (page 05h BUFE) +# Value -1 indicates that no 002Fh was current in the features list. +-r -v int current_feat2fh_byte4 + +# 0= drive is grabbed, 1= drive is not grabbed +-v volatile int released + +# File descriptor of an eventual emulated drive +-v int stdio_fd + +# >>> ??? +# (do we need a drive owned buffer to carry data from call to call or what ?) +-v struct CevapbuffeR *buffer + +# List of profiles as reported by the drive +-r -l struct CevapprofilE *profile +-p -v struct CevapprofilE *eol_profile + +# Media currently loaded in the drive +-r -m struct CevapmediA *media + + +# >>> transport.h : toc_temp (what is this ? It belongs to BURN_WRITE_RAW) +# >>> + +@ +=end Class + + +Class=EquipMedia +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=16.03.2007 +Documentation=\ +EquipMedia represents an optical disc, including its type, its writeability, +its formatting, its available formats and performances. +Subordinates=\ +EquipProfile*N,EquipFormat*N,EquipPerformance*N,EquipStatus,EquipMulticaps +Boss=EquipDrive +Cgen=\ +cevapmedia +-r -v struct CevapdrivE *boss + +# Volatile and/or public properties of the media +-r -m struct CevapstatuS *status + +# MMC-to-MMC feature info from 46h for DVD-RW. +# Quite internal. Regard as opaque :) +# 1 = incremental recording available, 0 = not available +-r -v int current_has_feat21h + +# Link Size item number 0 from feature 0021h descriptor +-r -v int current_feat21h_link_size + +# Wether a DVD-RW media holds an incomplete session +# (which could need closing after write) +-v int needs_close_session + +# From 51h READ DISC INFORMATION +# 0=needs format start, 1=needs format restart +-r -v int bg_format_status + +# From 23h READ FORMAT CAPACITY mmc5r03c.pdf 6.24 +# 1=unformatted, 2=formatted, 3=unclear +-r -v int format_descr_type +# meaning depends on format_descr_type +-r -v off_t format_curr_max_size +# dito +-r -v unsigned int format_curr_blsas +-r -v int best_format_type +-r -v off_t best_format_size +-r -l struct CevapformaT *format_descriptor +-p -v struct CevapformaT *eol_format_descriptor + +# The specific capabilities and restrictions of the media +-r -m struct CevapmcapS *multicaps + +# Results from ACh GET PERFORMANCE, Type 03h +# (Speed values go into drive.mdata_*_*_speed) +-r -l struct CevapperF *speed_descriptor +-p -v struct CevapperF *eol_speed_descriptor + +# >>> + +@ +=end Class + + +Class=EquipProfile +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=16.03.2007 +Documentation=\ +EquipProfile maps a MMC profile into libdax (See mmc5r03c.pdf chapter 5). +A profile describes a set of features and may be either current, possible, +disabled, or unavailable. +Subordinates=EquipFeature*N +Boss=EquipMedia +Cgen=\ +-l cevapprofile +-r -v struct CevapdrivE *boss +-r -v int is_current +-r -v int profile_code +-r -v char *profile_text +-r -v int is_cd_profile +-r -v int is_supported_profile +-r -l struct CevapfeaturE *feature +-p -v struct CevapfeaturE *eol_feature +@ +=end Class + +Class=EquipFeature +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=16.03.2007 +Documentation=\ +EquipFeature maps a MMC feature into libdax (See mmc5r03c.pdf chapter 5). +A feature describes a set of SCSI commands and (implicitely) of use cases. +Boss=EquipProfile +Cgen=\ +-l cevapfeature +-r -v struct CevapprofilE *boss + +# >>> + +@ +=end Class + +Class=EquipFormat +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since= +Documentation=\ +EquipFormat represents a single Formattable Capacity Descriptor +as of mmc5r03c.pdf 6.24.3.3 . +Boss=EquipMedia +Cgen=\ +-l cevapformat +-r -v struct CevapmediA *boss + +# format type: e.g 0x00 is "Full", 0x15 is "Quick" +-r -v int type + +# the size in bytes derived from Number of Blocks +-r -v off_t size + +# the Type Dependent Parameter (usually the write alignment size) +-r -v unsigned int tdp + +# >>> + +@ +=end Class + +Class=EquipPerformance +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since= +Documentation=\ +>>> EquipPerformance +Boss=EquipMedia +Cgen=\ +-l cevapperf +-r -v struct CevapmediA *boss + +# >>> + +@ +=end Class + +Class=EquipStatus +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=17.3.2007 +Documentation=\ +EquipStatus represents the status of media and drive. This includes +blank/appendable/closed, progress indicator. +Boss=EquipMedia +Cgen=\ +cevapstatus +-r -v struct CevapmediA *boss +-v int status +-m char *status_text +-v volatile int busy + +# From various sources : free space on media (in bytes) +# With CD this might change after particular write +# parameters have been set and nwa has been inquired. +-v off_t media_capacity_remaining + +# Current write address during write jobs. (Next address to be written) +# <<< does this belong to JOB ? +-r -v int nwa + +# if > 0 : first lba on media that is too high for write +-v int media_lba_limit + +-v struct CevapprogresS *progress + +# >>> + +@ +=end Class + +Class=EquipMulticaps +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=14.8.2007 +Documentation=\ +EquipMulticaps represents media dependent properties and media states which +are either volatile or especially interesting to several other modules. This +includes eventually existing sessions, closure status, profile dependent +capabilities. +Boss=EquipMedia +Cgen=\ +cevapmcaps + +# The current profile out of the drive profile list +-v struct CevapprofilE *current_profile + +# Wether the media is erasable (or overwriteable) +-v int erasable + +# A description of the existing media content structure +-r -m struct CevapdisC *disc + +# Start and end addresses out of ATIP. +# From 43h READ TOC/PMA/ATIP , mmc5r03c.pdf , 6.26 +-r -v int start_lba +-r -v int end_lba + +# From 51h READ DISC INFORMATION Number of Sessions (-1) +-v int complete_sessions + +# From 51h READ DISC INFORMATION Last Track Number in Last Session +-v int last_track_no + +# >>> libburn.h:struct burn_multi_caps + +@ +=end Class + +Class=EquipTocItem +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=14.8.2007 +Boss= +Cgen=\ +-l cevaptocitem +-r -v struct CevapdisC *boss +-v int session +-v int valid +-v int control + +# obscure info from CD TOC : possibly length of track +-v unsigned char point +-v unsigned char min +-v unsigned char sec +-v unsigned char frame + +-v int pmin +-v int psec +-v int pframe + +-v int start_lba +-v int track_blocks +@ +=end Class + + + +=end ClassDiagram=Equip_overview + + +ClassDiagram=Job_overview + +Class=JobTodo +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=18.3.2007 +Documentation=\ +JobTodo records what is to be done during a job. This includes peripheral +actions like tray load/eject and central actions like blank, format, burn. +Subordinates=JobDisc,JobOptions +Cgen=\ +cevaptodo +-v volatile int cancel + +# >>> + +@ +=end Class + +Class=JobDisc +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=18.3.2007 +Documentation=\ +JobDisc models a disc structure. Either one which already exists or +one which is to be created in a job run. +Subordinates=JobSession*N +Boss=JobTodo +Cgen=\ +cevapdisc +-l struct CevapsessioN *session +-p -v struct CevapsessioN *eol_session +-l struct CevaptociteM *toc_entry +-p -v struct CevaptociteM *eol_toc_entry + +# >>> take over services of struct burn_disc + +@ +=end Class + +Class=JobSession +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=18.3.2007 +Documentation=\ +JobSession represents a recording session. A session usually bundles +several tracks. Traditionally the last session of a disc is recognized +by operating systems as the thing to be mounted. +Subordinates=JobTrack*N,JobFifo +Boss=JobDisc +Cgen=\ +-l cevapsession +-r -v struct CevapdisC *boss + +# >>> + +-l struct CevaptracK *track +-p -v struct CevaptracK *eol_track + +# >>> + +@ +=end Class + +Class=JobTrack +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=18.3.2007 +Documentation=\ +JobTrack represents a track to be recorded. A track mainly is associated with +a data source but in many cases it also becomes a recognizable entity on the +target media. +Subordinates=JobBlock*N,JobTrackFilter,JobSource +Boss=JobSession +Cgen=\ +-l cevaptrack +-r -v struct CevapsessioN *boss + +# >>> + +@ +=end Class + +Class=JobBlock +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=18.3.2007 +Documentation=\ +JobBlock represents a single output data transaction unit. On CD this is +the same as an addressable media block resp. sector. On DVD this might be +an addressable block od 2k or a packet of e.g. 32k. +Boss=JobTrack +Cgen=\ +cevapblock +-v int alba +-v int rlba + +# >>> + +@ +=end Class + +Class=JobSource +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=8.4.2007 +Documentation=\ +JobSource represents a data source for a track. Typically this is a disk +file or a stream file descriptor like stdin. +Subordinates=JobSourceBlock*N +Boss=JobTrack +=end Class + +Class=JobSourceBlock +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=8.4.2007 +Documentation=\ +JobSourceBlock represents a single input data transaction unit. +Boss=JobSource +=end Class + +Class=JobFifo +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=8.4.2007 +Documentation=\ +JobFifo reads data via JobTrackFilter and buffers them until JobBlock can +accept them. +Boss=JobSession +=end Class + +Class=JobTrackFilter +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=8.4.2007 +Documentation=\ +JobTrackFilter reads data from JobSourceBlock, processes them and presents +them to JobFifo or JobBlock. This includes stripping of .wav headers. +Boss=JobTrack +=end Class + +Class=JobOptions +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=18.3.2007 +Documentation=\ +JobOptions bundles the adjustable parameters of a job. This includes dummy +mode, speed, appendability, blank mode, format selection, write mode, +underrun protection, random access addressing. +Boss=JobTodo +Cgen=\ +cevapjobopts + +# >>> + +# Keeping an eye on the drive buffer +-v int wait_for_buffer_free +-v unsigned int wfb_min_usec +-v unsigned int wfb_max_usec +-v unsigned int wfb_timeout_sec +-v unsigned int wfb_min_percent +-v unsigned int wfb_max_percent + +# >>> -m struct params params (used by disabled read cd funtionality) + +@ +=end Class + +Class=JobBuffer +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=13.8.2007 +Documentation=\ +JobBuffer is an intermediate storage for the content of several JobBlock +or JobSourceBlock. +Cgen=\ +cevapbuffer +-r -m unsigned char *data +-v int sectors +-v int bytes +@ +=end Class + +Class=JobProgress +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since=13.8.2007 +Documentation=\ +JobProgress reflects the state and parts of the history of a job +Cgen=\ +cevapprogress + +# Keeping an eye on the drive buffer +-v int nominal_write_speed +-v off_t pessimistic_buffer_free +-v int pbf_altered +-v unsigned int pessimistic_writes +-v unsigned int waited_writes +-v unsigned int waited_tries +-v unsigned int waited_usec + +# >>> the info provided by struct burn_progress + +# >>> +@ +=end Class + +Class= +Author=Thomas Schmitt <scdbackup@gmx.net> +Version=1.0 +Since= +Documentation=\ +=end Class + +=end ClassDiagram=Equip_overview + + +ClassDiagram=Gestures_overview + +# >>> + +=end ClassDiagram=Gestures_overview + + + +=end Model=libdax + +---------------------------------------------------------------------------- +Notes: +---------------------------------------------------------------------------- + + Compile cgen: + ( cd libcevap && cc -g -o cgen cgen.c ctyp.c smem.c ) + + Generate C stubs: + ( cd libcevap && ./libcevap_gen.sh ) + Option -lowercase would generate all lowercase struct and function names + + Compile test: + ( cd libcevap && ( rm a.out ; cc -g main.c cevap*.c smem.c ) ) + Option -DCevap_lowercasE would tell main.c that -lowercase was used above. + + +---------------------------------------------------------------------------- + +For a description of CgeN see libcevap/cgen.txt + +The generated code uses smem.[ch] out of one of my BSD licensed projects. +For a description see end of libcevap/smem.h . + +------------------------------------------------------------------------ diff --git a/libburn/branches/ZeroFourTwo/libcevap/libdax_overview.gif b/libburn/branches/ZeroFourTwo/libcevap/libdax_overview.gif new file mode 100644 index 0000000000000000000000000000000000000000..f4e5715cf9359fb244032880551ea6d24a2dfc7c GIT binary patch literal 12521 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 literal 0 HcmV?d00001 diff --git a/libburn/branches/ZeroFourTwo/libcevap/main.c b/libburn/branches/ZeroFourTwo/libcevap/main.c new file mode 100644 index 00000000..b537b5f8 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libcevap/main.c @@ -0,0 +1,39 @@ + +/* + cc -g -DCevap_lowercasE -c main.c +*/ +#include <stdio.h> +#include <stdlib.h> + +#include "cevapi.h" + +#include "smem.h" + +int main(int argc, char **argv) +{ +#ifdef Cevap_lowercasE + struct cevapi *cevap= NULL; +#else + struct CevapI *cevap= NULL; +#endif + int ret; + + /* full memory supervision */ + Smem_set_record_items(1); + + /* one short trip for testing */ +#ifdef Cevap_lowercasE + ret= cevapi_new(&cevap,0); + if(ret>0) + cevapi_destroy(&cevap,0); +#else /* Cevap_lowercasE */ + ret= Cevapi_new(&cevap,0); + if(ret>0) + Cevapi_destroy(&cevap,0); +#endif /* ! Cevap_lowercasE */ + + /* report any leaked memory */ + Smem_stderr(1|2); + + exit(ret<=0); +} diff --git a/libburn/branches/ZeroFourTwo/libcevap/smem.c b/libburn/branches/ZeroFourTwo/libcevap/smem.c new file mode 100644 index 00000000..94aed82f --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libcevap/smem.c @@ -0,0 +1,445 @@ + +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + + +#define Smem_included_by_smem_C +#include "smem.h" + + + +/* ------------------------------ SmemiteM ----------------------------- */ + + +int Smemitem_new(item,data,size,next,hash_start,flag) +struct SmemiteM **item; +char *data; +size_t size; +struct SmemiteM *next; +struct SmemiteM **hash_start; +int flag; +{ + struct SmemiteM *t; + + *item= t= (struct SmemiteM *) malloc(sizeof(struct SmemiteM)); + if(t==NULL) + return(-1); + t->data= data; + t->size= size; + t->prev= NULL; + t->next= next; + +#ifdef Smem_with_hasH + t->hash_next= NULL; + t->hash_prev= NULL; +#endif /* Smem_with_hasH */ + + if(next!=NULL) { + if(next->prev!=NULL) { + t->prev= next->prev; + next->prev->next= t; + } + next->prev= t; + } + +#ifdef Smem_with_hasH + if(hash_start!=NULL) { + t->hash_next= *hash_start; + if(t->hash_next!=NULL) { + t->hash_next->hash_prev= t; + } + *hash_start= t; + } +#endif /* Smem_with_hasH */ + + return(1); +} + + +int Smemitem_destroy(in_item,hash_start,flag) +struct SmemiteM **in_item; +struct SmemiteM **hash_start; +int flag; +{ + struct SmemiteM *item; + + item= *in_item; + if(item==NULL) + return(0); + if(item==Smem_start_iteM) + Smem_start_iteM= item->next; + if(item->prev!=NULL) + item->prev->next= item->next; + if(item->next!=NULL) + item->next->prev= item->prev; + +#ifdef Smem_with_hasH + if(hash_start!=NULL) { + if(item==*hash_start) + *hash_start= item->hash_next; + if(item->hash_prev!=NULL) + item->hash_prev->hash_next= item->hash_next; + if(item->hash_next!=NULL) + item->hash_next->hash_prev= item->hash_prev; + } +#endif /* Smem_with_hasH */ + + free((char *) item); + *in_item= NULL; + return(1); +} + + +int Smemitem_report(item,line,flag) +struct SmemiteM *item; +char line[1024]; +int flag; +{ + char *cpt; + int i,upto; + + sprintf(line,"%4lu bytes at %8.8lx ",(unsigned long) item->size, + (unsigned long) item->data); + cpt= line+strlen(line); + if(item->size<=256) + upto= item->size; + else + upto= 256; + if(item->data!=NULL) { + strcpy(cpt,"= \""); + cpt+= 3; + for(i=0;i<upto;i++){ + if(item->data[i]<32 || item->data[i]>=127 || item->data[i]=='\\') { + sprintf(cpt,"\\%2.2X",(unsigned char) item->data[i]); + cpt+= 3; + } else { + *(cpt++)= item->data[i]; + } + } + if(i<item->size) { + sprintf(cpt,"\" [truncated]"); + } else { + *(cpt++)= '"'; + *cpt= 0; + } + } + return(1); +} + + +int Smemitem_stderr(item,flag) +struct SmemiteM *item; +int flag; +{ + char line[1024]; + Smemitem_report(item,line,0); + fprintf(stderr,"%s\n",line); + return(1); +} + + + +/* -------------------------------- Smem ------------------------------ */ + + +int Smem_protest(line,flag) +char *line; +int flag; +{ + fprintf(stderr,"%s\n",line); + return(1); +} + + +int Smem_hashindex(ptr,flag) +char *ptr; +int flag; +{ + unsigned long idx; + + idx= (unsigned long) ptr; + return((idx>>Smem_hashshifT)%(Smem_hashsizE)); +} + + +/* find a certain memory item */ +struct SmemiteM *Smem_find_item(ptr,flag) +char *ptr; +int flag; +{ + int misscount= 0,idx; + struct SmemiteM *current; + +#ifdef Smem_with_hasH + + idx= Smem_hashindex(ptr,0); + for(current= Smem_hasH[idx];current!=NULL;current= current->hash_next) { + if(current->data==ptr) + return(current); + misscount++; + } + +#else /* Smem_with_hasH */ + + for(current= Smem_start_iteM;current!=NULL;current= current->next) { + if(current->data==ptr) + return(current); + misscount++; + } + +#endif /* ! Smem_with_hasH */ + + return(NULL); +} + + +int Smem_search_and_delete(ptr,flag) +char *ptr; +int flag; +/* + bit0= revoke registration : decrement counters +*/ +{ + int idx; + struct SmemiteM *current; + + current= Smem_find_item(ptr,0); + if(current==NULL) + return(0); + Smem_record_counT--; + Smem_record_byteS-= current->size; + idx= Smem_hashindex(ptr,0); + Smemitem_destroy(¤t,&(Smem_hasH[idx]),0); + Smem_hash_counteR[idx]-= 1.0; + if(flag&1) { + Smem_malloc_counT--; + Smem_pending_counT--; + } + return(1); +} + + +char *Smem_malloc(size) +size_t size; +{ + int idx; + char *cpt; + + if(size==0) { + Smem_protest("########### smem.c : malloc(0) caught",0); + return(NULL); + } + + /* if(size==1032) + cpt= NULL; / * set breakpoint here to find requests of certain size */ + + cpt= (char *) malloc(size); + if(cpt==NULL) { + char text[161]; + sprintf(text,"########### smem.c : malloc( %lu ) returned NULL", + (unsigned long) size); + Smem_protest(text,0); + return(NULL); + } + /* if(cpt==0x080a1e20) + cpt= NULL; / * set breakpoint here to find origin of certain address */ + + Smem_malloc_counT++; + Smem_pending_counT++; + if(Smem_record_itemS) { + idx= Smem_hashindex(cpt,0); + Smem_hash_counteR[idx]+= 1.0; + if(Smemitem_new(&Smem_start_iteM,cpt,size,Smem_start_iteM, + &(Smem_hasH[idx]),0)<=0) { + Smem_protest( + "########### smem.c : malloc( sizeof(SmemiteM) ) returned NULL",0); + return(NULL); + } + Smem_record_counT++; + Smem_record_byteS+= size; + } + return(cpt); +} + + +int Smem_free(ptr) +char *ptr; +{ + if(ptr==NULL) { + Smem_protest("########### smem.c : free() of NULL pointer caught",0); + return(0); + } + if(Smem_record_itemS) { + if(Smem_search_and_delete(ptr,0)<=0) { + Smem_protest("########### smem.c : free() of unrecorded pointer caught",0); + return(0); + } + } + Smem_free_counT++; + Smem_pending_counT--; + free(ptr); + return(1); +} + + +int Smem_report(line,flag) +char line[1024]; +int flag; +{ + sprintf(line,"malloc= %.f , free= %.f , pending= %.f", + Smem_malloc_counT,Smem_free_counT,Smem_pending_counT); + if(Smem_record_itemS) { + sprintf(line+strlen(line)," , bytes=%.f , records= %.f", + Smem_record_byteS,Smem_record_counT); + } + return(1); +} + + +int Smem_stderr(flag) +int flag; +/* + bit0= report 50 youngest pending items too + bit1= do not report if nothing is pending +*/ +{ + struct SmemiteM *current; + char line[1024]; + int i= 0; + + if(flag&2) + if(Smem_pending_counT==0.0 + && Smem_record_counT==0.0 + && Smem_record_byteS==0.0) + return(2); + Smem_report(line,0); + fprintf(stderr,"%s\n",line); + if(flag&1) { + for(current= Smem_start_iteM;current!=NULL;current= current->next) { + Smemitem_stderr(current,0); + if(++i>=50) + break; + } + if(current!=NULL) + if(current->next!=NULL) + fprintf(stderr,"[list truncated]\n"); + } + return(1); +} + + +int Smem_set_record_items(value) +int value; +{ + int i; + + if(!Smem_hash_initializeD) { + for(i=0;i<Smem_hashsizE;i++) { + Smem_hasH[i]= NULL; + Smem_hash_counteR[i]= 0.0; + } + Smem_hash_initializeD= 1; + } + Smem_record_itemS= value; + return(1); +} + + +int Smem_is_recorded(ptr,flag) +char *ptr; +int flag; +/* + bit0= complain if return(0) +*/ +{ + if(Smem_record_itemS==0) + return(2); + if(Smem_find_item(ptr,0)!=NULL) + return(1); + if(flag&1) + Smem_protest("########### smem.c : free() of unrecorded pointer caught",0); + return(0); +} + + +/* A simple C string cloner */ +int Smem_clone_string(ptr,text) +char **ptr; +char *text; +{ + *ptr= Smem_malloC(strlen(text)+1); + if(*ptr==NULL) + return(-1); + strcpy(*ptr,text); + return(1); +} + + +/* ----------------- for usage via debugger commands --------------------- */ + + +/* find a certain memory item */ +struct SmemiteM *Smem_find_data(ptr) +char *ptr; +{ + return(Smem_find_item(ptr,0)); +} + + +/* browsing the list */ +struct SmemiteM *Smem_fetch_item(step,flag) +int step; +int flag; +/* + bit0= reset cursor (and therefore address absolutely) +*/ +{ + static struct SmemiteM *current= NULL; + + if((flag&1)||current==NULL) + current= Smem_start_iteM; + if(step>0) { + for(;current!=NULL;current= current->next) { + if(step==0) + return(current); + step--; + } + } else if(step<0) { + for(;current!=NULL;current= current->prev) { + if(step==0) + return(current); + step++; + } + } else { + return(current); + } + return(NULL); +} + + +int Smem_print_hash_counter() { + int i; + + for(i=0;i<Smem_hashsizE;i++) + printf("%4d : %10.f\n",i,Smem_hash_counteR[i]); + return(1); +} + + +/* delete all recorded memory items */ +int Smem_delete_all_items() +{ + int ret; + + while(Smem_start_iteM!=NULL) { + ret= Smem_free(Smem_start_iteM->data); + if(ret<=0) + return(0); + } + return(1); +} + + diff --git a/libburn/branches/ZeroFourTwo/libcevap/smem.h b/libburn/branches/ZeroFourTwo/libcevap/smem.h new file mode 100644 index 00000000..28b5e98e --- /dev/null +++ b/libburn/branches/ZeroFourTwo/libcevap/smem.h @@ -0,0 +1,165 @@ + +#ifndef Smem_includeD +#define Smem_includeD + + +/* compile time adjustable parameters : */ + +/* if not defined, flat malloc() and free() is used */ +#define Smem_own_functionS +#ifdef Smem_no_own_functionS +#undef Smem_own_functionS +#endif /* Smem_no_own_functionS */ + +/* if not defined, the record items will be smaller by 8 byte + but deletion of items may be much slower */ +#define Smem_with_hasH + + + +struct SmemiteM { + + char *data; + size_t size; + + struct SmemiteM *prev,*next; + + struct SmemiteM *hash_prev,*hash_next; + +}; + + + + +#ifdef Smem_own_functionS + +char *Smem_malloc(); +int Smem_free(); + +#define TSOB_FELD(typ,anz) (typ *) Smem_malloc((anz)*sizeof(typ)); +#define Smem_malloC Smem_malloc +#define Smem_freE Smem_free + +#else /* Smem_own_functionS */ + +#define TSOB_FELD(typ,anz) (typ *) malloc((anz)*sizeof(typ)); +#define Smem_malloC malloc +#define Smem_freE free + +#endif /* ! Smem_own_functionS */ + +int Smem_set_record_items(); +int Smem_stderr(); +int Smem_clone_string(); + + +#define Smem_hashsizE 251 +#define Smem_hashshifT 8 + +#ifdef Smem_included_by_smem_C + +double Smem_malloc_counT= 0.0; +double Smem_free_counT= 0.0; +double Smem_pending_counT= 0.0; +struct SmemiteM *Smem_start_iteM= NULL; +struct SmemiteM *Smem_hasH[Smem_hashsizE]; +double Smem_hash_counteR[Smem_hashsizE]; + +/* these both init values are essential, since setting Smem_record_itemS=1 + by use of Smem_set_record_items() initializes the hash array + (i do not really trust the compiler producers to have read K&R) */ +int Smem_hash_initializeD= 0; +int Smem_record_itemS= 0; + +double Smem_record_counT= 0.0; +double Smem_record_byteS= 0.0; + +#else /* Smem_included_by_smem_C */ + +extern double Smem_malloc_counT; +extern double Smem_free_counT; +extern double Smem_pending_counT; +extern struct SmemiteM *Smem_start_iteM; +extern struct SmemiteM *Smem_hasH[Smem_hashsizE]; +extern double Smem_hash_counteR[Smem_hashsizE]; +extern int Smem_hash_initializeD; +extern int Smem_record_itemS; +extern double Smem_record_counT; +extern double Smem_record_byteS; + +#endif /* ! Smem_included_by_smem_C */ + + + +#endif /* ! Smem_includeD */ + + +/* + + smem + + Functions to replace malloc() and free() in order to get more control + over memory leaks or spurious errors caused by faulty usage of malloc() + and free(). + + + Sourcecode provisions: + + Use only the following macros for memory management: + TSOB_FELD(type,count) creates an array of items of given type + Smem_malloC() analogue of malloc() + Smem_freE() analogue of free() + One may #define malloc Smem_malloC resp. #define free Smem_freE + but better would be to review (and often to streamline) the sourcecode + in respect to those two functions. + + + Speed versus control: + + In production versions, where maximum speed is required, one may undefine + the macro Smem_own_functionS in smem.h . + This causes the above macros to directly invoke malloc() and free() without + any speed reduction (and without any additional use). + Undefinitio can be done globaly by modifying smem.h or locally by defining + Smem_no_own_functionS before including smem.h . + + If Smem_own_functionS remains defined, then the functions + Smem_malloc() + Smem_free() + are used rather than malloc() and free(). + They count the number of calls to maintain a rough overview of memory usage. + Smem_malloc() additionally checks for 0 size and Smem_free() checks for + NULL pointers, which they both report to stderr. Eventually one should set + a breakpoint in function Smem_protest() to learn about the origin of such + messages. + A status line may be obtained by Smem_report() or printed by Smem_stderr(). + + As long as the variable Smem_record_itemS is set to 0, there is not very much + overhead compared with malloc() and free(). + If the variable is set to 1 by Smem_set_record_items() then all malloc() + results are kept in a list where they will be deleted by their corresponding + Smem_free() calls. If a pointer is to be freed, which is not recorded in the + list then an error message will be printed to stderr. The memory will not + be freed ! + This mode not only may be very slow, it also consumes at least 16 byte per + piece of data which was obtained by malloc as long as it has not been freed. + Due to the current nature of the list, large numbers of memory items are freed + much faster in the reverse order of their creation. If there is a list of + 100000 strings to delete, it is very rewarding to free the youngest ones first. + A shortcut via hashing is available but consumes 24 bytes rather than 16. + (see above Smem_with_hasH ) + + The function Smem_is_recorded() can be used to check wether a pointer is + valid according to the list. It returns : + 0 = is not in list , 1 = is in list , 2 = recording is off + + If one decides to start recording malloc() results in the midst of a program + run, one has to be aware of false protests of Smem_free() if a memory piece + has been allocated before recording started. This will also cause those pieces + to be memory leaks because Smem_free() refuses to delete them. (Freeing memory + that was not obtained by malloc or was already freed previously can result in + deferred SIGSEGV or similar trouble, depending on OS and library.) + Also in that case one should stop recording before ending the program, to + avoid a lot of false complaints about longliving memory objects. + +*/ diff --git a/libburn/branches/ZeroFourTwo/test/Makefile b/libburn/branches/ZeroFourTwo/test/Makefile new file mode 100644 index 00000000..062350dd --- /dev/null +++ b/libburn/branches/ZeroFourTwo/test/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/libburn/branches/ZeroFourTwo/test/dewav.c b/libburn/branches/ZeroFourTwo/test/dewav.c new file mode 100644 index 00000000..6166bc3d --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/test/fake_au.c b/libburn/branches/ZeroFourTwo/test/fake_au.c new file mode 100644 index 00000000..01d7a416 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/test/libburner.c b/libburn/branches/ZeroFourTwo/test/libburner.c new file mode 100644 index 00000000..cc7769be --- /dev/null +++ b/libburn/branches/ZeroFourTwo/test/libburner.c @@ -0,0 +1,773 @@ + +/* test/libburner.c , API illustration of burning data or audio tracks to CD */ +/* Copyright (C) 2005 - 2007 Thomas Schmitt <scdbackup@gmx.net> */ +/* Provided under GPLv2,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-project.org . It can list the available devices, can + blank a CD-RW or DVD-RW, can format a DVD-RW, can burn to CD-R, CD-RW, DVD-R, + DVD+R, DVD+RW, DVD-RAM or DVD-RW. Not supported yet: double layer media. + + 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]; + + /* Some not-so-harmless drive addresses get blocked in this demo */ + if (strncmp(drive_adr, "stdio:/dev/fd/", 14) == 0 || + strcmp(drive_adr, "stdio:-") == 0) { + fprintf(stderr, "Will not work with pseudo-drive '%s'\n", + drive_adr); + return 0; + } + + /* 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 0; + } + 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. + @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(100002); + 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; + double percent = 1.0; + + disc_state = burn_disc_get_status(drive); + printf( + "Drive media status: %d (see libburn/libburn.h BURN_DISC_*)\n", + disc_state); + if (current_profile == 0x13) { + ; /* formatted DVD-RW will get blanked to sequential state */ + } else 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 libburner 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 ( %.1f%% done )\n", percent); + sleep(1); + } + printf("Done\n"); + return 1; +} + + +/** Persistently changes DVD-RW profile 0014h "Sequential Recording" to + profile 0013h "Restricted Overwrite" which needs no blanking for re-use + but is not capable of multi-session. + + Expect a behavior similar to blanking with unusual noises from the drive. +*/ +int libburner_format_row(struct burn_drive *drive) +{ + struct burn_progress p; + double percent = 1.0; + + 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 ( %.1f%% 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 kiB of padding to the (usualy single) track. + Audio tracks get padded to complete their last sector. + A fifo of 4 MB is installed between each track and its data source. + Each of the 4 MB buffers gets allocated automatically as soon as a track + begins to be processed and it gets freed as soon as the track is done. + The fifos do not wait for buffer fill but writing starts immediately. + + 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. + + For simplicity, this function allows memory leaks in case of failure. + In apps which do not abort immediately, one should clean up better. +*/ +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, *fifo_src[99]; + 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, unpredicted_size = 0, fd; + int fifo_chunksize = 2352, fifo_chunks = 1783; /* ~ 4 MB fifo */ + off_t fixed_size; + char *adr, reasons[BURN_REASONS_LEN]; + struct stat stbuf; + + if (all_tracks_type != BURN_AUDIO) { + all_tracks_type = BURN_MODE1; + /* a padding of 300 kiB helps to avoid the read-ahead bug */ + padding = 300*1024; + fifo_chunksize = 2048; + fifo_chunks = 2048; /* 4 MB fifo */ + } + + 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); + + /* Open file descriptor to source of track data */ + 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) + unpredicted_size = 1; + + /* Convert this filedescriptor into a burn_source object */ + 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; + } + /* Install a fifo object on top of that data source object */ + fifo_src[trackno] = burn_fifo_source_new(data_src, + fifo_chunksize, fifo_chunks, 0); + if (fifo_src[trackno] == NULL) { + fprintf(stderr, + "FATAL: Could not create fifo object of 4 MB\n"); + return 0; + } + + /* Use the fifo object as data source for the track */ + if (burn_track_set_source(track, fifo_src[trackno]) + != 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); + + /* Give up local reference to the data burn_source object */ + burn_source_free(data_src); + + } /* trackno loop end */ + + /* Evaluate drive and media */ + disc_state = burn_disc_get_status(drive); + if (disc_state != BURN_DISC_BLANK && + disc_state != BURN_DISC_APPENDABLE) { + 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(simulate_burn) + printf("\n*** Will TRY to SIMULATE burning ***\n\n"); + burn_write_opts_set_simulate(burn_options, simulate_burn); + burn_drive_set_speed(drive, 0, 0); + burn_write_opts_set_underrun_proof(burn_options, 1); + if (burn_write_opts_auto_write_type(burn_options, target_disc, + reasons, 0) == BURN_WRITE_NONE) { + fprintf(stderr, "FATAL: Failed to find a suitable write mode with this media.\n"); + fprintf(stderr, "Reasons given:\n%s\n", reasons); + return 0; + } + + 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(100002); + while (burn_drive_get_status(drive, &progress) != BURN_DRIVE_IDLE) { + if (progress.sectors <= 0 || + (progress.sector >= progress.sectors - 1 && + !unpredicted_size) || + (unpredicted_size && progress.sector == last_sector)) + printf( + "Thank you for being patient since %d seconds.", + (int) (time(0) - start_time)); + else if(unpredicted_size) + printf("Track %d : sector %d", progress.track+1, + progress.sector); + else + printf("Track %d : sector %d of %d",progress.track+1, + progress.sector, progress.sectors); + last_sector = progress.sector; + if (progress.track >= 0 && progress.track < source_adr_count) { + int size, free_bytes, ret; + char *status_text; + + ret = burn_fifo_inquire_status( + fifo_src[progress.track], &size, &free_bytes, + &status_text); + if (ret >= 0 ) + printf(" [fifo %s, %2d%% fill]", status_text, + (int) (100.0 - 100.0 * + ((double) free_bytes) / + (double) size)); + } + printf("\n"); + sleep(1); + } + printf("\n"); + + for (trackno = 0 ; trackno < source_adr_count; trackno++) { + burn_source_free(fifo_src[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. +*/ +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("Blank a used DVD-RW (is combinable with burning in one run):\n"); + printf(" %s --drive /dev/hdc --blank_full\n",argv[0]); + printf("Format a DVD-RW to avoid need for blanking before re-use:\n"); + printf(" %s --drive /dev/hdc --format_overwrite\n", argv[0]); + printf("Burn two audio tracks (to CD only):\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-project.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/branches/ZeroFourTwo/test/open-cd-excl.c b/libburn/branches/ZeroFourTwo/test/open-cd-excl.c new file mode 100644 index 00000000..fffe4d0d --- /dev/null +++ b/libburn/branches/ZeroFourTwo/test/open-cd-excl.c @@ -0,0 +1,133 @@ +/* + * open-cd-excl.c --- This program tries to open a block device + * by various exclusive and non-exclusive gestures in order to explore + * their impact on running CD/DVD recordings. + * + * Copyright 2007, by Theodore Ts'o. + * + * Detail modifications 2007, by Thomas Schmitt. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#define _GNU_SOURCE /* for O_LARGEFILE *//*ts A70417: or _LARGEFILE64_SOURCE */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <getopt.h> + +const char *progname; + +static void usage(void) +{ + fprintf(stderr, "Usage: %s [-feirw] device\n", progname); + exit(1); +} + +/* ts A70417: added parameter do_rdwr */ +static void init_flock(struct flock *fl, int do_rdwr) +{ + memset(fl, 0, sizeof(struct flock)); + if (do_rdwr) + fl->l_type = F_WRLCK; + else + fl->l_type = F_RDLCK; + fl->l_whence = SEEK_SET; + fl->l_start = 0; + fl->l_len = 0; +} + +int main(int argc, char **argv) +{ + struct flock fl; + char *device_name; + int fd, c, f_opt = 0, do_rdwr = 0, end_immediately = 0; + int flags = O_NONBLOCK|O_LARGEFILE; + + progname = argv[0]; + + /* ts A70417: added -w , -r , -i */ + while ((c = getopt (argc, argv, "feirw")) != EOF) { + switch (c) { + case 'e': + flags |= O_EXCL; + break; + case 'f': + f_opt++; + break; + case 'i': + end_immediately = 1; + break; + case 'r': + do_rdwr = 0; + break; + case 'w': + do_rdwr = 1; + break; + case '?': + usage(); + exit(1); + } + } + + if (optind == argc) + usage(); + device_name = argv[optind++]; + + /* ts A70417 : made read-write adjustable independently of f_opt */ + if (do_rdwr) { + flags |= O_RDWR; + printf("Using O_RDWR\n"); + } else { + flags |= O_RDONLY; + printf("Using O_RDONLY\n"); + } + + if (flags & O_EXCL) + printf("Trying to open %s with O_EXCL ...\n", device_name); + fd = open(device_name, flags, 0); + if (fd < 0) { + perror("open"); + printf("failed\n"); + exit(1); + } + if (flags & O_EXCL) + printf("succeeded\n"); + + if (f_opt) { + init_flock(&fl, do_rdwr); + if (fcntl(fd, F_GETLK, &fl) < 0) { + perror("fcntl: F_GETLK: "); + exit(1); + } + printf("fcntl lock apparently %sLOCKED\n", + (fl.l_type == F_UNLCK) ? "NOT " : ""); + + init_flock(&fl, do_rdwr); + printf("Trying to grab fcntl lock...\n"); + if (fcntl(fd, F_SETLK, &fl) < 0) { + perror("fcntl: F_SETLK: "); + printf("failed\n"); + exit(1); + } + printf("succeeded\n"); + } + + /* ts A70417: added end_immediately */ + printf("Holding %s open.\n", device_name); + usleep(100000); + if (end_immediately) + exit(0); + printf("Press ^C to exit.\n"); + while (1) { + sleep(300); + } + /* NOTREACHED */ + return 0; +} diff --git a/libburn/branches/ZeroFourTwo/test/poll.c b/libburn/branches/ZeroFourTwo/test/poll.c new file mode 100644 index 00000000..cd22f158 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/test/structest.c b/libburn/branches/ZeroFourTwo/test/structest.c new file mode 100644 index 00000000..defa274e --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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/branches/ZeroFourTwo/test/telltoc.c b/libburn/branches/ZeroFourTwo/test/telltoc.c new file mode 100644 index 00000000..63f40894 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/test/telltoc.c @@ -0,0 +1,921 @@ + +/* test/telltoc.c , API illustration of obtaining media status info */ +/* Copyright (C) 2006 - 2007 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 */ + +/** Overview + + telltoc is a minimal demo application for the library libburn as provided + on http://libburnia-project.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 + telltoc_read_and_print() reads from data CD or from DVD and prints 7-bit + to stdout (encodings 0,2) or 8-bit to file (encoding 1) + 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); + + +/* A message from --toc to --read_and_print (CD tracksize is a bit tricky) */ +static int last_track_start = 0, last_track_size = -1; +static int media_is_cd_profile = 0; + + +/* ------------------------------- 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. + Special drive addresses stdio:<path> direct output to a hard disk file + which will behave much like a DVD-RAM. +*/ +int telltoc_aquire_by_adr(char *drive_adr) +{ + int ret; + char libburn_drive_adr[BURN_DRIVE_ADR_LEN]; + + /* <<< ts A70907 FOR TESTING ONLY ! + struct burn_drive_info *test_drive_list; + */ + + /* 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 0; + } + + /* <<< ts A70907 FOR TESTING ONLY ! + ret = burn_drive_scan_and_grab(&test_drive_list, "/dev/sg2", 1); + */ + + 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; + } + + /* <<< ts A70907 FOR TESTING ONLY ! + burn_drive_info_free(test_drive_list); + */ + + 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(100002); + 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 its tray was loaded and it then was inquired + for Next Writeable Address. + The noise then still lasts 20 seconds. Same with cdrecord -toc, btw. + This 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; + off_t available = 0; + enum burn_disc_status s; + char profile_name[80], speed_unit[40]; + struct burn_multi_caps *caps; + struct burn_write_opts *o = NULL; + + 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_disc_get_multi_caps(drive, BURN_WRITE_NONE, &caps, 0); + if (ret > 0) { + /* Media appears writeable */ + printf("Write multi : "); + printf("%s multi-session , ", + caps->multi_session == 1 ? "allows" : "prohibits"); + if (caps->multi_track) + printf("allows multiple tracks\n"); + else + printf("enforces single track\n"); + printf("Write start : "); + if (caps->start_adr == 1) + printf( + "allows addresses [%.f , %.f]s , alignment=%.fs\n", + (double) caps->start_range_low / 2048 , + (double) caps->start_range_high / 2048 , + (double) caps->start_alignment / 2048 ); + else + printf("prohibits write start addressing\n"); + printf("Write modes : "); + if (caps->might_do_tao) + printf("TAO%s", + caps->advised_write_mode == BURN_WRITE_TAO ? + " (advised)" : ""); + if (caps->might_do_sao) + printf("%sSAO%s", + caps->might_do_tao ? " , " : "", + caps->advised_write_mode == BURN_WRITE_SAO ? + " (advised)" : ""); + if (caps->might_do_raw) + printf("%sRAW%s", + caps->might_do_tao | caps->might_do_sao ? + " , " : "", + caps->advised_write_mode == BURN_WRITE_RAW ? + " (advised)" : ""); + printf("\n"); + printf("Write dummy : "); + if (caps->might_simulate) + printf("supposed to work with non-RAW modes\n"); + else + printf("will not work\n"); + o= burn_write_opts_new(drive); + if (o != NULL) { + burn_write_opts_set_perform_opc(o, 0); + if(caps->advised_write_mode == BURN_WRITE_TAO) + burn_write_opts_set_write_type(o, + BURN_WRITE_TAO, BURN_BLOCK_MODE1); + else if (caps->advised_write_mode == BURN_WRITE_SAO) + burn_write_opts_set_write_type(o, + BURN_WRITE_SAO, BURN_BLOCK_SAO); + else { + burn_write_opts_free(o); + o = NULL; + } + } + available = burn_disc_available_space(drive, o); + printf("Write space : %.1f MiB (%.fs)\n", + ((double) available) / 1024.0 / 1024.0, + ((double) available) / 2048.0); + burn_disc_free_multi_caps(&caps); + if (o != NULL) + burn_write_opts_free(o); + } + + 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, pmin, psec, pframe; + int track_count = 0, pno; + int session_no, track_no; + char profile_name[80]; + 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); + if (toc_entry.extensions_valid & 1) { + /* DVD extension valid */ + lba = toc_entry.start_lba; + burn_lba_to_msf(lba, &pmin, &psec, &pframe); + } else { + pmin = toc_entry.pmin; + psec = toc_entry.psec; + pframe = toc_entry.pframe; + lba= burn_msf_to_lba(pmin, psec, pframe); + } + printf("Media content: session %2d ", session_no+1); + printf("track %2d %s lba: %9d %4.2d:%2.2d:%2.2d\n", + track_count, + ((toc_entry.control&7)<4?"audio":"data "), + lba, pmin, psec, pframe); + last_track_start = lba; + } + burn_session_get_leadout_entry(sessions[session_no], + &toc_entry); + if (toc_entry.extensions_valid & 1) { + lba = toc_entry.start_lba; + burn_lba_to_msf(lba, &pmin, &psec, &pframe); + } else { + pmin = toc_entry.pmin; + psec = toc_entry.psec; + pframe = toc_entry.pframe; + lba= burn_msf_to_lba(pmin, psec, pframe); + } + printf("Media content: session %2d ", session_no+1); + printf("leadout lba: %9d %4.2d:%2.2d:%2.2d\n", + lba, pmin, psec, pframe); + last_track_size = lba - last_track_start; + if (burn_disc_get_profile(drive, &pno, profile_name) > 0) + if (pno == 0x09 || pno == 0x0a) + media_is_cd_profile = 1; + + } + if (disc!=NULL) + burn_disc_free(disc); + return 1; +} + + +int telltoc_msinfo(struct burn_drive *drive, + int msinfo_explicit, int msinfo_alone) +{ + int ret, lba, nwa = -123456789, aux_lba; + enum burn_disc_status s; + 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. + */ + ret = burn_disc_get_msc1(drive, &lba); + if (ret <= 0) { + fprintf(stderr, + "SORRY: Cannot obtain start address of last session\n"); + { ret = 0; goto ex; } + } + + /* man mkisofs , option -C : + The second number is the starting sector number of the new session. + */ + /* Set some roughly suitable write opts to be sent to 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, + 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, + "SORRY: Cannot obtain next writeable address\n"); + { ret = 0; goto ex; } + } + + if (!msinfo_alone) + printf("Media msinfo : mkisofs ... -C "); + printf("%d,%d\n",lba,nwa); + ret = 1; +ex:; + if (o != NULL) + burn_write_opts_free(o); + return ret; +} + + +/** + @param encoding determins how to format output on stdout: + 0 = default , 1 = raw 8 bit (dangerous for tty) , 2 = hex +*/ +int telltoc_read_and_print(struct burn_drive *drive, + int start_sector, int sector_count, char *raw_file, int encoding) +{ + int j, i, request = 16, done, lbas = 0, final_cd_try = -1, todo; + int ret = 0; + char buf[16 * 2048], line[81]; + off_t data_count, total_count= 0, last_reported_count= 0; + struct stat stbuf; + FILE *raw_fp = NULL; + + if (start_sector == -1) + start_sector = last_track_start; + if (sector_count == -1) { + sector_count = last_track_start + last_track_size + - start_sector; + if (media_is_cd_profile) /* In case it is a TAO track */ + final_cd_try = 0; /* allow it (-1 is denial) */ + } + if (start_sector < 0) + start_sector = 0; + if (sector_count <= 0) + sector_count = 2147483632; + + if (sector_count <= 0) + return -1; + if (encoding == 1) { + if (stat(raw_file,&stbuf) != -1) { + if (!(S_ISCHR(stbuf.st_mode) || S_ISFIFO(stbuf.st_mode) + || (stbuf.st_mode & S_IFMT) == S_IFSOCK )) { + fprintf(stderr, + "SORRY: target file '%s' already existing\n", + raw_file); + return 1; + } + } + raw_fp = fopen(raw_file,"w"); + if (raw_fp == NULL) { + fprintf(stderr,"SORRY: cannot open target file '%s' (%s)\n", raw_file, strerror(errno)); + return 1; + } + printf( + "Data : start=%ds , count=%ds , read=0s , encoding=%d:'%s'\n", + start_sector, sector_count, encoding, raw_file); + } else + printf( + "Data : start=%ds , count=%ds , read=0 , encoding=%d\n", + start_sector, sector_count, encoding); + todo = sector_count - 2*(final_cd_try > -1); + for (done = 0; done < todo && final_cd_try != 1; done += request) { + if (todo - done > 16) + request = 16; + else + request = todo - done; + ret = burn_read_data(drive, + ((off_t) start_sector + done) * (off_t) 2048, + buf, (off_t) (request * 2048), &data_count, 1); + +print_result:; + total_count += data_count; + if (encoding == 1) { + if (data_count > 0) + fwrite(buf, data_count, 1, raw_fp); + } else for (i = 0; i < data_count; i += 16) { + if (encoding == 0) { + sprintf(line, "%8ds + %4d : ", + start_sector + done + i / 2048, + i % 2048); + lbas = strlen(line); + } + for (j = 0; j < 16 && i + j < data_count; j++) { + if (buf[i + j] >= ' ' && buf[i + j] <= 126 && + encoding != 2) + sprintf(line + lbas + 3 * j, " %c ", + (int) buf[i + j]); + else + sprintf(line + lbas + 3 * j, "%2.2X ", + (unsigned char) buf[i + j]); + } + line[lbas + 3 * (j - 1) + 2] = 0; + printf("%s\n",line); + } + if (encoding == 1 && + total_count - last_reported_count >= 1000 * 2048) { + fprintf(stderr, + "\rReading data : start=%ds , count=%ds , read=%ds ", + start_sector, sector_count, + (int) (total_count / (off_t) 2048)); + last_reported_count = total_count; + } + if (ret <= 0) { + fprintf(stderr, "SORRY : Reading failed.\n"); + break; + } + } + if (ret > 0 && media_is_cd_profile && final_cd_try == 0) { + /* In a SAO track the last 2 frames should be data too */ + final_cd_try = 1; + burn_read_data(drive, + ((off_t) start_sector + todo) * (off_t) 2048, + buf, (off_t) (2 * 2048), &data_count, 2); + if (data_count < 2 * 2048) + fprintf(stderr, "\rNOTE : Last two frames of CD track unreadable. This is normal if TAO track.\n"); + if (data_count > 0) + goto print_result; + } + if (last_reported_count > 0) + fprintf(stderr, +"\r \r"); + printf("End Of Data : start=%ds , count=%ds , read=%ds\n", + start_sector, sector_count,(int) (total_count / (off_t) 2048)); + + 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; +static int read_start = -2, read_count = -2, print_encoding = 0; +static char print_raw_file[4096] = {""}; + + +/** 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], "--read_and_print")) { + i+= 3; + if (i >= argc) { + fprintf(stderr,"--read_and_print requires three arguments: start count encoding(try 0, not 1)\n"); + return 1; + } + sscanf(argv[i-2], "%d", &read_start); + sscanf(argv[i-1], "%d", &read_count); + print_encoding = 0; + if(strncmp(argv[i], "raw:", 4) == 0 || strcmp(argv[i],"1:") == 0) { + print_encoding = 1; + strcpy(print_raw_file, strchr(argv[i], ':') + 1); + if (strcmp(print_raw_file, "-") == 0) { + fprintf(stderr, + "--read_and_print does not write to \"-\" as stdout.\n"); + return 1; + } + } else if(strcmp(argv[i], "hex") == 0 || strcmp(argv[i], "2") == 0) + print_encoding = 2; + + } 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(" [--read_and_print <start> <count> \"0\"|\"hex\"|\"raw\":<path>]\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]); + printf("View blocks 16 to 19 of data CD or DVD in human readable form\n"); + printf(" %s --drive /dev/sr1 --read_and_print 16 4 0 | less\n", + argv[0]); + printf("Copy last data track from CD to file /tmp/data\n"); + printf(" %s --drive /dev/sr1 --toc --read_and_print -1 -1 raw:/tmp/data\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 && + (read_start < 0 || read_count <= 0) && driveno!=-1) { + if(print_help) + exit(0); + full_default = do_media = do_msinfo = do_capacities= do_toc= 1; + } + + fprintf(stderr, "Initializing libburnia-project.org ...\n"); + if (burn_initialize()) + fprintf(stderr, "Done\n"); + else { + fprintf(stderr,"\nFATAL: Failed to initialize.\n"); + exit(33); + } + + /* Print messages of severity WARNING or more directly to stderr */ + burn_msgs_set_severities("NEVER", "WARNING", "telltoc : "); + + /* Activate the default signal handler */ + burn_set_signal_handling("telltoc : ", NULL, 0); + + /** 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; } + } + if (read_start >= -1 && (read_count > 0 || read_count == -1)) { + ret = telltoc_read_and_print(drive_list[driveno].drive, + read_start, read_count, print_raw_file, + print_encoding); + if (ret<=0) + {ret = 40; 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/branches/ZeroFourTwo/version.h.in b/libburn/branches/ZeroFourTwo/version.h.in new file mode 100644 index 00000000..13ada991 --- /dev/null +++ b/libburn/branches/ZeroFourTwo/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@