diff --git a/trunk/AUTHORS b/trunk/AUTHORS new file mode 100644 index 0000000..6a964a5 --- /dev/null +++ b/trunk/AUTHORS @@ -0,0 +1,3 @@ +Mario Danic +Thomas Schmitt + diff --git a/trunk/CONTRIBUTORS b/trunk/CONTRIBUTORS new file mode 100644 index 0000000..31b77c5 --- /dev/null +++ b/trunk/CONTRIBUTORS @@ -0,0 +1,6 @@ +Joe Neeman +Philippe Rouquier +Gabriel Craciunescu +George Danchev +Jean-Francois Wauthy +Lorenzo Taylor diff --git a/trunk/COPYING b/trunk/COPYING new file mode 100644 index 0000000..5a965fb --- /dev/null +++ b/trunk/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/trunk/COPYRIGHT b/trunk/COPYRIGHT new file mode 100644 index 0000000..8cd566d --- /dev/null +++ b/trunk/COPYRIGHT @@ -0,0 +1,18 @@ +Derek Foreman and Ben Jansens +Copyright (C) 2002-2006 Derek Foreman and Ben Jansens +Mario Danic , Thomas Schmitt +Copyright (C) 2006-2014 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 version 2 or later + 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 diff --git a/trunk/ChangeLog b/trunk/ChangeLog new file mode 100644 index 0000000..b0d1619 --- /dev/null +++ b/trunk/ChangeLog @@ -0,0 +1,441 @@ +SVN trunk (to become libburn-1.4.0 or higher) +=============================================================================== +- no novelties yet - + + +libburn-1.3.8.tar.gz Sat Jun 28 2014 +=============================================================================== +* Bug fix: Wrong stack usage caused SIGBUS on sparc when compiled by gcc -O2 +* Bug fix: Minimum drive buffer fill was measured by cdrskin before the buffer + could get full +* Bug fix: A failed MMC BLANK command did not cause error indication by libburn +* Bug fix: A final fsync(2) was performed with stdio drives, even if not + desired + +libburn-1.3.6.pl01.tar.gz Tue Mar 18 2013 +=============================================================================== +* Bug fix: CD TAO with multiple tracks could cause a buffer overrun +* Bug fix: Compilation warning for unsupported systems mutated into an error + +libburn-1.3.6.tar.gz Tue Mar 04 2013 +=============================================================================== +* New system adapter for NetBSD + +libburn-1.3.4.tar.gz Thu Dec 12 2013 +=============================================================================== +* Bug fix: Drive error reports were ignored during blanking and formatting +* Bug fix: Drive LG BH16NS40 stalls on inspection of unformatted DVD+RW +* New API call burn_disc_pretend_full_uncond() + +libburn-1.3.2.tar.gz Wed Aug 07 2013 +=============================================================================== +* Bug fix: cdrskin -msinfo on DVD and BD reported + old session start = next writable address. + Regression introduced by version 1.2.8 (rev 4956). +* Bug fix: The signal handler aborted on SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU +* New API call burn_make_input_sheet_v07t() +* API call burn_session_input_sheet_v07t(): read multiple blocks from same file +* New API calls burn_drive_extract_audio(), burn_drive_extract_audio_track() +* New cdrskin option textfile_to_v07t= +* New cdrskin options cdtext_to_textfile= and cdtext_to_v07t= +* New cdrskin options extract_audio_to= , extract_tracks= , extract_basename= , + --extract_dap +* New cdrskin option --pacifier_with_newline +* Improved granularity of SCSI log time measurement, now with timestamp +* Optional "make doc" now demands doxygen 1.8.4 + +libburn-1.3.0.pl01.tar.gz Fri May 31 2013 +=============================================================================== +* Bug fix: cdrskin -msinfo on DVD and BD reported + old session start = next writable address. + Regression introduced by version 1.2.8. + +libburn-1.3.0.tar.gz Fri May 17 2013 +=============================================================================== +* Bug fix: Full formatting of BD-RE used certification regardless of drive + capabilities +* Bug fix: DVD+R with damaged TOC were reported by -minfo with wrong end + address + +libburn-1.2.8.tar.gz Mon Mar 18 2013 +=============================================================================== +* Bug fix: All CD tracks were reported with the sizes of the tracks in the + first session. Regression introduced with version 1.2.0 (rev 4552). +* Bug fix: On some drives the request for minimum speed yielded maximum speed +* New cdrskin option --list_speeds +* -toc and -minfo now report about tracks in the incomplete session +* New API call burn_disc_get_incomplete_sessions() +* New burn_toc_entry component .track_status_bits + +libburn-1.2.6.tar.gz Tue Jan 08 2013 +=============================================================================== +* Bug fix: Speed setting had no effect on BD media +* New cdrskin option --no_load +* New API call burn_read_audio() +* New API call burn_list_sev_texts() + +libburn-1.2.4.tar.gz Fri Jul 20 2012 +=============================================================================== +* Bug fix: CD SAO sessions with data tracks started by an audio pause +* Bug fix: CD tracks were perceived 2 sectors too short. + Nice with TAO, bad with SAO. +* Bug fix: cdrskin SIGSEGV if track source was added when no drive was available +* New API call burn_write_opts_set_obs_pad(), ./configure --enable-dvd-obs-pad +* New cdrskin option --obs_pad + +libburn-1.2.2.tar.gz Mon Apr 02 2012 +=============================================================================== +* Small internal refinements + +libburn-1.2.0.tar.gz Sat Jan 28 2012 +=============================================================================== +* Bug fix: cdrskin produced a memory fault if interupted before writing began +* Bug fix: Solaris adapter mishandled write commands which failed on first try +* Bug fix: Interrupting libburn while drive tray is loading led to endless loop +* Bug fix: Progress report with blanking and formatting could be bogus +* New API calls burn_disc_get_leadin_text(), burn_write_opts_set_leadin_text() +* New API calls for composing CD-TEXT, see doc/cdtext.txt +* New API call burn_session_by_cue_file() for reading CDRWIN .cue files +* New API call burn_track_set_isrc_string() +* New API calls burn_track_set_index(), burn_track_clear_indice() +* New API calls burn_session_set_start_tno(), burn_session_get_start_tno() +* New API calls burn_track_set_pregap_size(), burn_track_set_postgap_size() +* Implemented cdrskin option textfile= +* Implemented cdrskin option combination -vv -toc for cdtext.dat production +* Implemented cdrskin options mcn= and isrc= +* Implemented cdrskin options -scms -copy -nocopy -preemp -nopreemp +* Implemented cdrskin option index= +* Partly implemented cdrskin options cuefile= and -text +* New cdrskin option input_sheet_v07t= for CD-TEXT definition +* New cdrskin options --cdtext_dummy and --cdtext_verbose +* New cdrskin options --four_channel --two_channel +* New cdrskin option cd_start_tno= +* New cdrskin options sao_pregap=, sao_postgap= + +libburn-1.1.8.tar.gz Mon Nov 21 2011 +=============================================================================== +* Bug fix: Misinterpreted mode page 2A if block descriptors are present +* Enabled recognition of QEMU DVD-ROM 0.12 +* Avoiding to intermediately close and open drive device file +* New API call burn_drive_re_assess() + +libburn-1.1.6.tar.gz Tue Sep 27 2011 +=============================================================================== +* Bug fix: stdio sizes > 4 TB - 32 kB caused integer rollover +* Worked around a collision with Linux udev which lets links vanish + +libburn-1.1.4.tar.gz Sun Aug 07 2011 +=============================================================================== +* Bug fix: Some drives return -150 as NWA of blank CD, rather than 0. + libburn forwarded this misleading information to the application. +* Bug fix: Some drives returned wrong CD sizes after having burned DVD-R +* Bug fix: Empty ROM drive was mistaken to hold an unsuitable disc +* Bug fix: Avoiding to load speed descriptor list twice +* New API call burn_lookup_device_link() +* New API call burn_disc_get_phys_format_info() +* New cdrskin option --device_links + +Release 1.1.2 was skipped to get back in sync with libisoburn. + +libburn-1.1.0.pl01.tar.gz Mon Jun 20 2011 +=============================================================================== +* Bug fix: libburn-1.1.0 compiled only on Linux, FreeBSD, and Solaris + +libburn-1.1.0.tar.gz Sat Jun 18 2011 +=============================================================================== +* Bug fix: burn_disc_format() on DVD-RW issued wrong block size with type 00h +* New API call burn_disc_next_track_is_damaged() +* New API call burn_disc_close_damaged() +* Dropped suffix .plXY from tarball name + +Release 1.0.8 was skipped to get back in sync with libisofs and libisoburn. + +libburn-1.0.6.pl00.tar.gz Sat Apr 9 2011 +=============================================================================== +* Burning DVD-R DAO with 2 kB size granularity rather than 32 kB +* New API call burn_allow_drive_role_4() + +libburn-1.0.4.pl00.tar.gz Thu Mar 3 2011 +=============================================================================== +* Bug fix: Read-only file descriptors were classified as write-only pseudo + drives + +libburn-1.0.2.pl00.tar.gz Wed Feb 23 2011 +=============================================================================== +* Removed compilation obstacles on Solaris 9. +* Improved recognition of non-seekable stdio pseudo-drives. + +libburn-1.0.0.pl00.tar.gz Sun Jan 16 2011 +=============================================================================== +* Allowed umask to create stdio-drive files with rw-permissions for all +* cdrskin now refuses to burn if the foreseeable size exceeds media capacity + +libburn-0.9.0.pl00.tar.gz Wed Dec 08 2010 +=============================================================================== +* Regression fix: SCSI reply data logging was disabled in release 0.8.6 + +libburn-0.8.8.pl00.tar.gz Wed Oct 20 2010 +=============================================================================== +* New API call burn_offst_source_new() +* New API call burn_disc_get_bd_spare_info() + +libburn-0.8.6.pl00.tar.gz Fri Sep 17 2010 +=============================================================================== +* Lifted test reservation on DVD-R DL media. +* Hiding all non-API symbols from the linker by use of --version-script +* Now with history of release notes in ./ChangeLog file. + +libburn-0.8.4.pl00.tar.gz Wed Jun 30 2010 +=============================================================================== +* General POSIX system adapters ignore SIGWINCH and SIGURG if defined +* Allowed 64 kB max output buffer size on all OSes + +libburn-0.8.2.pl00.tar.gz Fri Jun 11 2010 +=============================================================================== +* New system adapter for Solaris uscsi (tested on snv134, kernel 5.11) +* Bug fix: CD TOC was not read if the first track did not start at LBA 0 +* Bug fix: CD-ROM media got attributed random lead-in and lead-out adresses +* Bug fix: SIGSEGV of experimental libcdio system adapter if drive list is + empty + +libburn-0.8.0.pl00.tar.gz Fri Apr 09 2010 +=============================================================================== +* libburn now works with ahci driver on FreeBSD 8-STABLE. + +libburn-0.7.8.pl00.tar.gz Wed Mar 10 2010 +=============================================================================== +* Bug fix: On FreeBSD, piped input was falsely attributed a small fixed size. +* Built-in abort handling is more suitable for FreeBSD now. +cdrskin novelties: +* Bug fix: Option fs=0 led to SIGSEGV. Regression introduced by version 0.7.4 + in december 2009. +* Abort handling is more suitable for FreeBSD now. + +libburn-0.7.6.pl00.tar.gz Sat Jan 23 2010 +=============================================================================== +* Bug fix: System adapter for generic X/Open was missing in libburn release + tarball +* Bug fix: with non-Linux adapters there were 0 readable bytes on block devices +* Made FreeBSD system adapter safe from mutal burn spoiling and drive deadlock +* Enabled FreeBSD system adapter for Debian kfreebsd +* Experimental SCSI transport adapter via GNU libcdio 0.83git +cdrskin novelties: +* none + +libburn-0.7.4.pl01.tar.gz Sat Dec 26 2009 +=============================================================================== +* Bug fix: Added missing system adapter for generic X/Open to libburn release + tarball + +Libburn 0.7.4.pl00 Mon Dec 07 2009 +=============================================================================== +* Bug fix: SIGSEGV from NULL pointer with media product id inquiry on LG + GH22LS30 +* Bug fix: DVD DAO track size was rounded up much too generously +* Workaround for Pioneer DVR-216D which got stuck on DVD-R burns. + (already fixed in 0.7.2.pl01) +* Workaround for Pioneer DVR-216D refusal to eject. + (already fixed in 0.7.2.pl01) +* Configure options --enable-dvd-obs-64k, --enable-track-src-odirect +* New API calls burn_write_opts_set_dvd_obs(), + burn_write_opts_set_stdio_fsync() +* New API call burn_set_scsi_logging() +* New API calls burn_fifo_get_statistics(), burn_fifo_next_interval(), + burn_fifo_fill() +* Re-implemented ECMA-130 P-parity, Q-parity and scrambling for BURN_WRITE_RAW +cdrskin novelties: +* cdrskin option -V for logging of SCSI commands +* New cdrskin options dvd_obs= and stdio_fsync= +* New compile_cdrskin.sh option -dvd_obs_64k + +libburn-0.7.2.pl01.tar.gz Fri Nov 13 2009 +=============================================================================== +* Workaround for Pioneer DVR-216D which got stuck on DVD-R burns. +* Workaround for Pioneer DVR-216D refusal to eject. + +Libburn 0.7.2.pl00 Mon Oct 12 2009 +=============================================================================== +* Bug fix: CD TAO sessions with multiple tracks did not work in -dummy mode +* New API calls burn_get_media_product_id() , burn_guess_manufacturer() , + burn_guess_cd_manufacturer() +* New API call burn_disc_get_cd_info() +* New API call burn_track_set_cdxa_conv() +cdrskin novelties: +* Better interpretation of options -mode2, -xa, -xa1, -xa2 +* New option --xa1-ignore +* New -atip report lines "Product Id:" and "Producer:" + +libburn-0.7.0.pl00.tar.gz Thu Aug 27 2009 +=============================================================================== +* New API calls burn_drive_get_all_profiles(), burn_obtain_profile_name() allow + to inquire and process the list of supported media types. cdrskin lists all + supported profiles with option -atip -v +* New API call burn_drive_snooze() allows to calm down a drive when no i/o is + expected for a while. +* Bug fix: Some SCSI commands stalled on U3 memory sticks which appear as a hub + with a memory stick and a CD-ROM drive containing a small CD. These commands + make not much sense with a CD-ROM and are now avoided for this media + situation. + +libburn-0.6.8.pl00.tar.gz Tue Jul 14 2009 +=============================================================================== +* Bug fix: Old MMC-1 drives were rejected because of mode page 2Ah length. +* cdrskin -scanbus now works with high SCSI bus numbers. + +libburn-0.6.6.pl00.tar.gz Fri May 8 2009 +=============================================================================== +* Bug fix: Improper abort handling with broken pipe during outputto a stdio: + pseudo-drive. +* Bug fix: Device scan stalled on FreeBSD with non-burner USB device + +libburn-0.6.4.pl00.tar.gz Fri Mar 13 2009 +=============================================================================== +* New operating system adapter "dummy" for stdio on general X/Open systems +* New API function burn_drive_set_stream_recording() allows to write the + crucial start blocks of a filesystem with slow BD-RE Defect Management and to + write the bulk of data with full nominal speed. + +libburn-0.6.2.pl00.tar.gz Fri Feb 20 2009 +=============================================================================== +* Improvements with build system for FreeBSD + +libburn-0.6.0.pl01.tar.gz Wed Jan 07 2009 +=============================================================================== +* Bug fix: BD-R were not correctly finalized + +libburn-0.6.0.pl00.tar.gz Sun Jan 04 2009 +=============================================================================== +* Formatting and writing of BD-R media +* New API function burn_get_read_capacity() + +libburn-0.5.8.pl00.tar.gz Mon Dec 08 2008 +=============================================================================== +* Bug fix: A session without leadout entry on CD caused a SIGSEGV by NULL +* Improvements about BD-RE formatting + +libburn-0.5.6.pl00.tar.gz Wed Nov 12 2008 +=============================================================================== +* Bug fix: libburn fifo thread was not aborted when burn run was aborted which + could lead to use of freed memory. + +libburn-0.5.4.pl00.tar.gz Mon Oct 6 2008 +=============================================================================== +* Bug fix: On Linux 2.4 /dev/sr0 was accepted as enumerable address but then + failed to work. + +libburn-0.5.2.pl00.tar.gz Wed Aug 20 2008 +=============================================================================== +* Larger set of possibly acceptable drive device file names + +libburn-0.5.0.pl00.tar.gz Thu Jul 17 2008 +=============================================================================== +* Bug fix: cdrskin option drive_scsi_dev_family=scd lead to buffer overflow +* Ability to use /dev/scd as fallback if /dev/sr does not exist +* New API call burn_fifo_peek_data() + +libburn-0.4.8.pl00.tar.gz Sat May 17 2008 +=============================================================================== +* Bug fix: Random access addressing for DVD-RAM and BD-RE did not work. +* cdrskin: Affected were options write_start_address= and + -- grow_overwriteable_iso on DVD-RAM or BD-RE. +* xorriso: Affected were sessions on DVD-RAM or BD-RE after the first one. + +libburn-0.4.6.pl00.tar.gz Sun May 11 2008 +=============================================================================== +* Support for BD-RE media is now official +* New burn_write_opts_set_stream_recording() can speed up DVD-RAM and BD-RE +* New cdrskin option --list_formats +* New cdrskin blank types for expert formatting of DVD-RAM and BD-RE +* New cdrskin blank type blank=as_needed for automatic handling of media + +libburn-0.4.4.tar.gz Thu April 10 2008 +=============================================================================== +* Support for DVD+R/DL media is now official + +libburn-0.4.2.tar.gz Sun Feb 3 2008 +=============================================================================== +* Long term commitment to ABI libburn.so.4. +* ABI compatibility is guaranteed for any older feature set released since + libburn-0.3.2 about one year ago. +* libburn provides means for compile time and runtime checking of its version. +* Compile time check in cdrskin for proper version of libburn include file. + Required is at least 0.4.2. +* Runtime check in cdrskin prevents dynamic linking with outdated version of + libburn.so.4. Required is at least the version seen in the include file at + compile time. + +libburn-0.4.0.tar.gz Mon Oct 29 2007 +=============================================================================== +* 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= +* A lot of libburn API additions. + +libburn-0.3.8.tar.gz Tue Jul 31 2007 +=============================================================================== +* 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 +* Note: ABI has not been broken. + +libburn-0.3.6.tar.gz Thu Apr 26 2007 +=============================================================================== +* On Linux kernel 2.6, /dev/sr* gets used rather than /dev/sg*. +* DVD+R now get finalized (if not -multi is given) + +libburn-0.3.4.tar.gz Mon Mar 12 2007 +=============================================================================== +* Multi-session recording on DVD+R, including -toc, -msinfo +* Options --tell_media_space , assert_write_lba= +* Bug fix of rare multi track fifo stall + +libburn-0.3.2.tar.gz Feb 11 2007 +=============================================================================== +* Burnfree enabled by default +* Multi-session recording on sequential DVD-R[W], including -toc, -msinfo +* DVD-R[W] Disk-at-once recording + +libburn-0.3.0.1.tar.gz Tue Jan 30 2007 +=============================================================================== +* 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 +* 0.3.0.1 release notes * +* Bug fix enabling tracks >= 1.3 GB from disk file + +libburn-0.2.6.3.tar.gz Fri Dec 29 2006 +=============================================================================== +* 0.2.6 release notes (Wed Nov 22 2006) +* After a lot of time with dedication to this project, we proudly present you + libburn 0.2.6. It is the first version of cdrskin and libburn after they have + been split from genisofs and libisofs. Main new features are write mode TAO + and support for multi session. +* 0.2.6.1 release notes (Fri Nov 24 2006) +* Point release to fix misleading version numbers in messages and documentation +* 0.2.6.2 release notes (Sat Dec 16 2006) +* cdrskin man page backported from development version 0.2.7. +* 0.2.6.3 release notes (Fri Dec 29 2006) +* Point release to fix build system problems people have experienced with the + past release. + +libburn-0.2.3.snapshot02.tar.gz Thu Nov 02 2006 +=============================================================================== +* Stabilized snapshot including release 0.2.4.pl01 of cdrskin +* cdrskin 0.2.4 release notes +* Compatibility with cdrecord has been improved in respect to drive addresses, + audio extraction from .wav, -scanbus, -toc, drive buffer fill indicator. +* Note: The previous snapshot01 with the same source base is handicapped by a + broken ./configure setup. It works well on Intel compatible CPUs but is + supposed to be unusable on big-endian architectures. + +libburn-0.2.2.tar.gz Wed Sep 20 2006 +=============================================================================== +Initial release of libburnia's libburn combined with cdrskin. diff --git a/trunk/INSTALL b/trunk/INSTALL new file mode 100644 index 0000000..5458714 --- /dev/null +++ b/trunk/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/trunk/Makefile.am b/trunk/Makefile.am new file mode 100644 index 0000000..e2ee6b5 --- /dev/null +++ b/trunk/Makefile.am @@ -0,0 +1,240 @@ + +# ts A90315 : LIBBURNIA_PKGCONFDIR is defined OS specific in acinclude.m4 +# was: pkgconfigdir=$(libdir)/pkgconfig +pkgconfigdir=$(LIBBURNIA_PKGCONFDIR) + +libincludedir=$(includedir)/libburn + +lib_LTLIBRARIES = libburn/libburn.la +ACLOCAL_AMFLAGS = -I ./ + +## ========================================================================= ## + +# Build libraries +libburn_libburn_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) +# This causes undesired .o names +# configure.ac appends -D options to variable CFLAG +### libburn_libburn_la_CFLAGS = $(LIBBURN_DVD_OBS_64K) +libburn_libburn_la_LIBADD = $(LIBBURN_ARCH_LIBS) $(THREAD_LIBS) +libburn_libburn_la_SOURCES = \ + libburn/async.c \ + libburn/async.h \ + libburn/back_hacks.h \ + libburn/cdtext.c \ + 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/ecma130ab.c \ + libburn/ecma130ab.h \ + libburn/error.h \ + libburn/file.c \ + libburn/file.h \ + libburn/init.c \ + libburn/init.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 + +## libburn/sg-@ARCH@.c \ + +libinclude_HEADERS = \ + libburn/libburn.h + +install-exec-hook: + $(LIBBURNIA_LDCONFIG_CMD) "$(DESTDIR)$(libdir)" || echo 'NOTE: Explicit dynamic library configuration failed. If needed, configure manually for:' "$(DESTDIR)$(libdir)" + +## ========================================================================= ## + +## Build test applications +noinst_PROGRAMS = \ + test/libburner \ + test/offst_source \ + test/telltoc \ + test/dewav \ + test/fake_au \ + test/poll \ + test/structest + +bin_PROGRAMS = \ + cdrskin/cdrskin + +LIBBURN_EXTRALIBS = $(LIBBURN_ARCH_LIBS) $(THREAD_LIBS) + +test_libburner_CPPFLAGS = -Ilibburn +test_libburner_LDADD = $(libburn_libburn_la_OBJECTS) $(LIBBURN_EXTRALIBS) +test_libburner_SOURCES = test/libburner.c +test_offst_source_CPPFLAGS = -Ilibburn +test_offst_source_LDADD = $(libburn_libburn_la_OBJECTS) $(LIBBURN_EXTRALIBS) +test_offst_source_SOURCES = test/offst_source.c +test_telltoc_CPPFLAGS = -Ilibburn +test_telltoc_LDADD = $(libburn_libburn_la_OBJECTS) $(LIBBURN_EXTRALIBS) +test_telltoc_SOURCES = test/telltoc.c +test_dewav_CPPFLAGS = -Ilibburn +test_dewav_LDADD = $(libburn_libburn_la_OBJECTS) $(LIBBURN_EXTRALIBS) +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) $(LIBBURN_EXTRALIBS) +test_poll_SOURCES = test/poll.c +test_structest_CPPFLAGS = -Ilibburn +test_structest_LDADD = $(libburn_libburn_la_OBJECTS) $(LIBBURN_EXTRALIBS) +test_structest_SOURCES = test/structest.c + +## cdrskin construction site - ts A60816 - B40628 +cdrskin_cdrskin_CPPFLAGS = -Ilibburn +cdrskin_cdrskin_CFLAGS = -DCdrskin_libburn_1_3_9 + +# cdrskin_cdrskin_LDADD = $(libburn_libburn_la_OBJECTS) $(LIBBURN_EXTRALIBS) +# ts A80123, change proposed by Simon Huggins to cause dynamic libburn linking +cdrskin_cdrskin_LDADD = libburn/libburn.la $(LIBBURN_EXTRALIBS) + +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" +## + +# "make clean" shall remove a few stubborn .libs directories +# which George Danchev reported Dec 03 2011. +# Learned from: http://www.gnu.org/software/automake/manual/automake.html#Clean +clean-local: + -rm -rf cdrskin/.libs test/.libs + + +## ========================================================================= ## + +## 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-2014 Mario Danic, Thomas Schmitt +Still containing parts of Libburn. By Derek Foreman +and Ben Jansens +Copyright (C) 2002-2006 Derek Foreman and Ben Jansens + + http://files.libburnia-project.org/releases/libburn-1.3.8.tar.gz + +------------------------------------------------------------------------------ + + Build and Installation + + From tarball + +Obtain libburn-1.3.8.tar.gz, take it to a directory of your choice and do: + + tar xzf libburn-1.3.8.tar.gz + cd libburn-1.3.8 + ./configure --prefix=/usr + make + +To make libburn accessible for running resp. application development, +and to install the cdrecord compatibility binary cdrskin, do +(as Superuser): + + make install + +This procedure installs libburn.so.4 and cdrskin depending on it. +For a standalone cdrskin binary, see cdrskin/README. + +A behavioral conflict is known between any burn software and demons like hald +which probe CD drives. This can spoil burn runs for CD-R or CD-RW. +You may have to keep your hald away from the drive. See for example + http://www.freebsd.org/gnome/docs/halfaq.html + + + From SVN + +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. +Do in a directory of your choice: + + svn co http://svn.libburnia-project.org/libburn/trunk libburn-svn + cd libburn-svn + ./bootstrap + ./configure --prefix=/usr + make + make install + +Warning: The trunk might contain experimental features which might not + persist until next release. + + + Special ./configure options + +make install on GNU/Linux will try to run program ldconfig with the library +installation directory as only argument. Failure to do so will not abort +installation. One may disable ldconfig by ./configure option: + --disable-ldconfig-at-install + +In some situations Linux may deliver a better write performance to drives if +the track input is read with O_DIRECT (see man 2 open). The API call +burn_os_open_track_src() and the input readers of cdrskin and libburn fifo +can be told to use this peculiar read mode by: + --enable-track-src-odirect + +But often libburn call burn_write_opts_set_dvd_obs(opts, 64*1024) will yield +even better performance in such a situation. 64k can be made default at +configure time by: + --enable-dvd-obs-64k +This may be combined with above --enable-track-src-odirect . + +If it is desired that DVD DAO writing and stdio: writing get padded up to +a full write chunk of 32k resp. 64k, then use ./configure option: + --enable-dvd-obs-pad + +Alternatively the transport of SCSI commands can be done via libcdio-0.83. +You may install it and re-run libburn's ./configure with option + --enable-libcdio + +By use of a version script, the libburn.so library exposes no other function +names but those of the API definition in libburn/libburn.h. +If -Wl,--version-script=... makes problems with the local compiler, then +disable this encapsulation feature by + --disable-versioned-libs + +Make sure to re-compile all source files after running ./configure + make clean ; make + make install + + +Linux only: + +libburn tries to avoid a collision with udev's drive examination by waiting +0.1 seconds before opening the device file for a longer time, after udev +might have been alarmed by drive scanning activities. +The waiting time can be set at ./configure time with microsecond granularity. +E.g. 2 seconds: + CFLAGS="$CFLAGS -DLibburn_udev_wait_useC=2000000" + ./configure ...options... +Waiting can be disabled by zero waiting time: + CFLAGS="$CFLAGS -DLibburn_udev_wait_useC=0" +Alternatively, libburn can try to be nice by opening the device file, +closing it immediately, waiting, and only then opening it for real: + CFLAGS="$CFLAGS -DLibburn_udev_extra_open_cyclE -DLibburn_udev_wait_useC=500000" + +------------------------------------------------------------------------------ + +An important part of the project, libisofs, is hosted in a bzr repository at +launchpad.net : + bzr branch lp:libisofs + +Another part the project, libisoburn, is hosted in the libburnia SVN, too: + svn co http://svn.libburnia-project.org/libisoburn/trunk libisoburn + +See README files 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 CD media, all DVD media, all BD media. + +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. +Currently it is supported on GNU/Linux with kernels >= 2.4, +on FreeBSD with ATAPI/CAM enabled in the kernel (see man atapicam), +on OpenSolaris (tested with kernel 5.11), +on NetBSD (tested with 6.1.3). +On other X/Open compliant systems there will only be pseudo drives, but no +direct MMC operation on real CD/DVD/BD drives. + +For full 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 well tested code base for burning data and audio CDs, DVDs and BDs. +The burn API is quite comprehensively documented and can be used to build a +presentable application. +We have a functional application 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. + +ISO 9660 filesystems with Rock Ridge and Joliet extensions can be created +and manipulated quite freely. This capability together with our burn capability +makes possible a single binary application which covers all steps of image +composition, updating and writing. Quite unique in the Linux world. + +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. + An own ISO 9660 extension stores ACLs, xattr, and MD5 of file + content. + +- libisoburn is an add-on to libburn and libisofs which coordinates both and + also 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. + It also contains the methods of command oriented application + xorriso and offers them via a C language API. + +- 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 application of all three libraries which creates, loads, + manipulates and writes ISO 9660 filesystem images with + Rock Ridge extensions. Manipulation is not only adding or + overwriting of files but also deleting, renaming, attribute + changing, incremental backups, activating boot images, and + extracting of files from ISO images to disk. There is also a + sparse emulation of cdrecord and a more laborate one of mkisofs. + All features of xorriso are also available via a C language API + of libisoburn. + A static compilation of xorriso and the libraries is dedicated + to the GNU Operating System. See xorriso/README_gnu_xorriso . + +- "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 strive to be a responsive upstream. + +Our libraries are committed to maintain older feature sets in newer versions. +This applies to source code headers (API) as well as to linkable objects (ABI). +The only exception from this rule is about non-release versions x.y.*[13579] +which are allowed to introduce new features, change those new features in +any way and even may revoke such new features before the next release of +x.y.*[02468]. As soon as it is released, a feature is promised to persist. + +SONAMES: +libburn.so.4 (since 0.3.4, March 2007), +libisofs.so.6 (since 0.6.2, February 2008), +libisoburn.so.1 (since 0.1.0, February 2008). + +Applications must use 64 bit off_t. E.g. by defining +#define _LARGEFILE_SOURCE +#define _FILE_OFFSET_BITS 64 +or take special precautions to interface with the libraries by 64 bit integers +where the .h files prescribe off_t. To reduce libburn's off_t size to 32 bit +will keep it from processing tracks of more than 2 GB size. + + +------------------------------------------------------------------------------ +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 contributors 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. + +- 26th Januar 2008 version 0.4.2 rectifies the version numbering so that we + reliably release libburn.so.4 as should have been done since libburn-0.3.2. + cdrskin now is by default linked dynamically and does a runtime check + to ensure not to be started with a libburn which is older than itself. + +- 3rd Feb 2008 libisofs-0.2.x (.so.5) has been deprecated. + +- 14th Feb 2008 libisofs-0.6.2 permanently replaces the old libisofs-0.2.x. + It is the first release of new libisofs.so.6 which will guarantee future + API/ABI compatibility for its whole feature set. + +- 15th Feb 2008 libisoburn-0.1.0 (.so.1) coordinates libisofs and libburn for + the purpose of ISO image reading and writing. It emulates multi-session on + overwriteable media. Application xorriso makes use of all three libraries. + +- 8th Apr 2008 libburn-0.4.4 has proven to be capable of burning to DVD+R/DL + and read performance on disk file pseudo-drives has been improved. + +- 27th Apr 2008 libisofs-0.6.4 can now read data file content from images + and can map pieces of disk files onto image files. Image directory iteration + has been enhanced. Input data streams and extended information have been + exposed in the API to allow future development. + +- 29th Apr 2008 libisoburn-0.1.4 was made more efficient with reading of + image tree nodes. It now depends on libisofs-0.6.4 and libburn-0.4.4. + xorriso makes use of new libisofs features by performing incremental + updates of directory trees and by cutting oversized data files into + pieces. A primitive single session emulation of cdrecord and mkisofs is + provided. + +- 10th May 2008 libburn-0.4.6 supports formatting and writing of BD-RE, + full nominal speed for DVD-RAM and BD-RE. cdrskin has a unified blank + type with automatic media state recognition. + +- 17th May 2008 an old bug with DVD-RAM and now with BD-RE is fixed by + libburn-0.4.8 to allow libisoburn emulation of multisession on those media. + +- 19th May 2008 libisoburn-0.1.6 brings better table-of-content emulation + on overwriteble media and disk files. + +- 1st Jun 2008 libisofs-0.6.6 fixes some problems around device files. + +- 3rd Jun 2008 libisoburn-0.1.8 fixes a bug with overwriteable media. + +- 23rd Jun 2008 libisoburn-0.2.0 introduces extraction of files from + ISO images. + +- 16th Jul 2008 libburn-0.5.0 handles systems with no /dev/sr* but only + /dev/scd*. + +- 19th Jul 2008 libisoburn/xorriso-0.2.2 can do multi-session in mkisofs + and cdrecord style. xorriso now can serve underneath growisofs. + +- 20th Aug 2008 libburn-0.5.2 revokes the necessity that a drive must be + enumerable in order to be adressable. Enumeration is enhanced by examining + /proc/sys/dev/cdrom/info. + +- 24th Aug 2008 libisoburn/xorriso-0.2.4 introduces a media readability check + with data retrieval option. + +- 18th Sep 2008 libisofs-0.6.8 supports ISO 9660 Level 3 which allows very + large data files in the image. + +- 20th Sep 2008 libisoburn/xorriso-0.2.6 takes into respect the new Level 3 + capabilities of libisofs. + +- 6th Oct 2008 libburn-0.5.4 adjusts the changes of 0.5.2 to the needs of + Linux kernel 2.4 and introduces human readable SCSI error messages. + +- 6th Oct 2008 libisofs-0.6.10 fixes two bugs which prevented adding and + manipulation of ISOLINUX boot images. + +- 15th Oct 2008 libisoburn/xorriso-0.2.8 can activate and maintain an + ISOLINUX boot image by an EL Torito boot record. + +- 12th Nov 2008 libburn-0.5.6 fixes usage of freed memory by the fifo thread + of an aborted burn run. + +- 26th Nov 2008 libisofs-0.6.12 can produce a ISOLINUX isohybrid MBR on the fly + and allows to produce ISO images which resemble old mkisofs images. + +- 2nd Dec 2008 libisoburn-0.3.0. xorriso now is ready for exotic character + sets, for legacy FreeBSD systems which expect an outdated Rock Ridge + signature, and for producing ISO images with MBR which boot from hard disk + or USB stick. Three minor bugs were fixed. + +- 7th Dec 2008 libburn-0.5.8 prevents a SIGSEGV with wierd CD table-of-content + and improves BD-RE formatting. + +- 9th Dec 2008 Our project received a donation from Thomas Weber. + +- 2nd Jan 2009 libburn-0.6.0 allows to format BD-R and to write to either + formatted or unformatted BD-R. + +- 6th Jan 2009 libisoburn-0.3.2. xorriso can produce and execute commands for + mounting older sessions from all kinds of media. Pseudo-drives outside the + /dev/ tree can be addressed without prefix "stdio:". + +- 20th Feb 2009 libburn-0.6.2 source release now compiles out of the box + on FreeBSD. + +- 28 Feb 2009 libisofs-0.6.14 can record ACLs and Extended Attributes xattr + in its ISO images. + +- 01 Mar 2009 libisoburn-0.3.4. xorriso makes use of the ACL and xattr + capabilities provided by libisofs for xorriso backup features. + +- 11 Mar 2009 libisofs-0.6.16 of libisofs fixes two bugs which on Solaris + prevented to navigate the ISO images by ".." and to recognize the Rock Ridge + extensions of ISO images. The ban to build libisofs on operating systems + other than Linux and FreeBSD has been lifted. + +- 13 Mar 2009 libburn-0.6.4 got a dummy adapter for SCSI/MMC command transport. + It will show no drives and thus libburn will only be able to perform + operations on "stdio:" pseudo drives. Nevertheless this allowed to lift the + ban to build libburn on operating systems other than Linux and FreeBSD. + +- 16 Mar 2009 libisoburn-0.3.6: xorriso uses RRIP version 1.10 as default + in order to be mountable where mkisofs images are mountable. + +- 17 Apr 2009 libisofs-0.6.18 introduces content filtering of data files. + Built-in filters allow compression to formats gzip and zisofs. External + filter processes allow arbitrary data conversions like encryption. + +- 19 Apr 2009 libisoburn-0.3.8 makes use of the new libisofs capability to + perform content filtering of data files. + +- 08 May 2009 libburn-0.6.6 fixes a bug with aborting on broken output pipe + and a bug with device scan on FreeBSD. + +- 31 May 2009 libisofs-0.6.20 can record hard link relations in ISO images + and offers support with restoring them to disk. Current Linux kernels will + mount images with such hard links but will attribute a unique inode number + to each file. + +- 28 Jun 2009 libisoburn-0.4.0: xorriso can record and restore hard link + relations of files. Performance of data reading has been improved. Option + -find now supports logical operators with its tests. + +- 14 Jul 2009 libburn-0.6.8 fixes bugs and shortcommings with old MMC-1 drives + and with large SCSI bus numbers as handed out by Linux for USB drives. + +- 20 Jul 2009 libisoburn-0.4.0.pl01 fixes a regression in xorriso which caused + data loss in older sessions if xorriso was used underneath growisofs. + Affected are releases since libisoburn-0.3.2 in january 2009. + +- 25 Aug 2009 libisofs-0.6.22 can record MD5 checksums for the whole session + and for each single data file. Checksum tags allow to verify superblock and + directory tree before importing them. + +- 27 Aug 2009 libburn-0.7.0 allows to calm down a drive and to inquire its + supported profiles. It works around some pitfalls with U3 enhanced memory + sticks which emulate a CD-ROM. + +- 27 Aug 2009 libisoburn-0.4.0.pl00 can record MD5 checksums by which one may + verify the session or single data files in the image. When comparing image + files with files in the local filesystem, the MD5 sums avoid the need for + reading file content from the image. + +- 22 Sep 2009 libisoburn-0.4.0.pl01 fixes a bug in xorriso option -cut_out. + +- 08 Oct 2009 libisofs-0.6.24 fixes a bug which could cause the loss of blanks + in file names when a new session got added to an ISO image. With names + shorter than 251 characters this happened only to trailing blanks. + +- 08 Oct 2009 libisoburn-0.4.0.pl02 fixes bugs with xorriso option -for_backup, + with xorrisofs -help, and with xorrecord -help. + +- 12 Oct 2009 libburn-0.7.2 fixes a bug with CD TAO multi-track dummy sessions. + It can retrieve media product info and can process track input which was + prepared for CD-ROM XA Mode 2 Form 1. cdrskin now performs option -minfo. + +- 28 Oct 2009 libisoburn-0.4.4 fixes a bug with cdrecord emulation and + introduces new information options about media type and ISO image id strings. + On Linux it helps with mounting two sessions of the same media + simultaneously. + +- 12 Nov 2009 libburn-0.7.2.pl01 works around problems with Pioneer DVR-216D. + DVD-R runs made the drive stuck. Ejecting the tray did not work properly. + +- 06 Dec 2009 libburn-0.7.4 works around problems with newer DVD burners, + provides throughput enhancements with hampered busses on Linux, and new + API calls to log SCSI commands and to control the libburn fifo. + +- 09 Dec 2009 libisoburn-0.4.6 allows performance tuning of output to DVD + drives or disk files. + +- 26 Dec 2009 libburn-0.7.4.pl01 fixes the release tarball which was lacking + the files of the generic system adapter for X/Open. + +- 29 Dec 2009 Our project received a donation for purchasing a fine small + computer which shall serve as OS farm for development and support. + +- 20 Jan 2010 Version 0.6.26 of libisofs fixes minor bugs and shall enhance + portability. + +- 22 Jan 2010 libburn-0.7.6 has an improved system adapter for FreeBSD, + fixes bugs about the generic X/Open system adapter, and allows to use + libcdio >= 0.83 as SCSI transport facility. + +- 10 Feb 2010 libisofs-0.6.28 fixes a regression about bootable images which + was introduced by version 0.6.22 in August 2009. + +- 23 Feb 2010 libisoburn-0.5.0 marks the transition of the xorriso standalone + version to an official GNU project. The name changed to "GNU xorriso" and its + license is now GPLv3+. + The licenses of libburnia libraries and applications are not affected by + this change. + +- 10 Mar 2010 libburn-0.7.8 fixes bugs and improves the built-in abort handler + on FreeBSD. + +- 30 Mar 2010 Release 0.5.2 of libisoburn provides xorriso documentation in + GNU Texinfo format with embedded extra data to derive a full man page. + +- 09 Apr 2010 libburn-0.8.0 now works with ahci driver on FreeBSD 8-STABLE. + +- 03 May 2010 Version 0.6.32 of libisofs is able to create ISO images with + multiple boot images. All boot catalog parameters described in El-Torito + specs can be set and inquired. This allows to use GRUB boot images for EFI. + +- 04 May 2010 Release 0.5.6.pl00 of libisoburn makes use of the new libisofs + capabilities about boot images. + +- 11 Jun 2010 libburn-0.8.2 now works on Solaris. + +- 14 Jun 2010 By release 0.5.8.pl00 of libisoburn, xorriso becomes a public C + language API of libisoburn. The emulations of mkisofs and cdrecord have + been enhanced. + +- Tue Jun 29 2010 Version 0.6.34 of libisofs provides new features about + hiding file names from directory trees. + +- Wed Jun 30 2010 libburn-0.8.4 removes some restrictions on operating + systems other than Linux and FreeBSD. + +- Fri Jul 02 2010 Release 0.6.0.pl00 of libisoburn adds more options to the + mkisofs emulation of xorriso. It also fixes minor bugs and shortcommings. + +- Wed Sep 15 2010 Version 0.6.36 of libisofs can produce ISO images which + bear a partiton 1 with non-zero start address. They can be mounted from + USB stick via the main device file (e.g. /dev/sdb) as well as via the + partition device file (e.g. /dev/sdb1). + +- Fri Sep 17 2010 libburn-0.8.6 lifts the test reservation on DVD-R DL media. + +- Sat Sep 18 2010 Release 0.6.2.pl00 of libisoburn introduces a partition + with non-zero offset for ISO 9660 images on USB sticks, improves mkisofs + emulation, and fixes a regression which existed since version 0.4.2. + +- Wed Oct 20 2010 libburn-0.8.8 can report the used amount of BD spare blocks. + +- Sat Oct 23 2010 Version 0.6.38 of libisofs can use libjte to produce jigdo + files along with the ISO image. Further filesystem images may be appended + as MBR partitions 1 to 4. The capability was added to produce boot blocks + for computers with MIPS CPU. + +- Tue Oct 26 2010 Release 0.6.4.pl00 of libisoburn and xorriso makes use of + the new libisofs capabilities. + +- Wed Dec 08 2010 libburn-0.9.0 fixes a regression with SCSI command logging. + +- Fri Dec 10 2010 Version 0.6.40 of libisofs makes the prediction of the + emerging image size less expensive and is able to make images bootable + for SUN SPARC systems. + +- Sun Dec 12 2010 Release 0.6.6.pl00 of libisoburn and xorriso can read ISO + images which were copied to a different start address than they were prepared + for. + +- Mon Jan 17 2011 we go for release 1.0.0. This does not indicate a + technological overhaul but shall emphasize the maturity of the software. + libisofs-1.0.0 fixes a bug about the length of ECMA-119 directory names and + is ready to allow untranslated ECMA-119 names (violating the specs). + libburn-1.0.0.pl00 allows umask to create stdio-drive files with + rw-permissions for all. cdrskin now refuses to burn if the foreseeable size + exceeds media capacity + libisoburn-1.0.0.pl00 allows to create an ISO 9660:1999 directory tree, + improved the emulation fidelity of command -as mkisofs, lowered the default + abort threshold for xorriso batch mode, and increased that threshold for + xorriso dialog mode. + +- Wed Feb 23 2011 release 1.0.2: + libisofs fixes several bugs and introduces the capability to copy files + inside the ISO filesystem. + libburn removed a compilation obstacle on Solaris 9 and improved recognition + of stdio pseudo-drives. + libisoburn and xorriso fix bugs and make use of the new libisofs capability. + xorriso improves its mkisofs emulation. + +- Thu Mar 10 2011 release 1.0.4: + Several bugs were fixed in the libraries and in the mkisofs emulation of + xorriso. This emulation xorrisofs has now an own man page and info document. + +- Sat Apr 09 2011 release 1.0.6: + libburn refined its representation of emulated drives. The size alignment + of DVD DAO is now 2 kB rather than 32 kB. libisofs produces Joliet names of + up to 103 characters. xorriso fixes two bugs and makes use of the library + improvements. + +- Thu Apr 14 2011 release libisoburn-1.0.8: + A bug in the mkisofs emulation of xorriso could cause options to be ignored. + The problem was freshly introduced with libisoburn-1.0.6. + +- Fri May 13 2011 release libisofs-1.0.8: + Fixes a few rarely occurring bugs that have been found during the last month. + +- Sat Jun 18 2011 release 1.1.0: + The consumption of stack memory was reduced. Statical program analysis found + some rarely occuring memory leaks. Several small bugs were fixed. + The suffix .plXY was dropped from tarball names of libburn and libisoburn. + +- Mon Jun 20 2011 patch release libburn-1.1.0.pl01: + libburn-1.1.0 compiled only on Linux, FreeBSD, and Solaris, but not on + other X/Open compliant systems. + +- Fri Jul 08 2011 release libisofs-1.1.2 and libisoburn-1.1.2: + A severe regression was fixed in libisoburn and xorriso, which was introduced + with version 1.0.6. It caused ISO 9660 images to be unreadable if they were + written to a write-only random-access file. E.g. by: xorrisofs ... >image.iso + +- Mon Aug 08 2011 release 1.1.4: + Several bugs were fixed in libburn. The most severe of them prevented xorriso + on some drives from burning mountable ISO 9660 images to CD media. + New means to list drives by their udev symbolic links help to deal with + the non-persistent drive addresses on modern GNU/Linux. + +- Tue Sep 27 2011 release 1.1.6: + libisoburn now comes with a test suite. See releng/README. Bugs were fixed + in several rarely used features. Processing of ACL and extattr was enabled + on FreeBSD. Workarounds try to cope with vanishing udev links on GNU/Linux. + +- Mon Nov 21 2011 release libburn-1.1.8 and libisoburn-1.1.8: + libburn avoids to close and open drive device files while operating on them. + xorriso emulation mode xorrecord now has an own manual. libburn and xorriso + were prepared to operate on qemu virtio-blk-pci devices. + +- Sat Jan 28 2012 release 1.2.0: + libburn has learned to read and write CD-TEXT with CD SAO audio sessions. + It can now read CDRWIN .cue files which define pure audio or pure data + sessions. libisofs and libisoburn improved timestamp handling. Several + minor bugs were fixed. + +- Mon Apr 02 2012 release 1.2.2: + The handling of intentional deviations from ECMA-119 specifications has + been improved in libisofs. libisoburn and xorriso now make use of these + improvements. Some rarely occuring bugs have been fixed. + +- Fri Jul 20 2012 release 1.2.4: + libburn and libisofs got some rarely occuring bugs fixed. libisofs learned + to produce HFS+ metadata and Apple Partition Map. The capabilities of + isohybrid options --efi and --mac have been implemented (GPT and APM). + +- Tue Jan 08 2013 release 1.2.6: + Small improvements were made in libburn. Minor bugs were fixed in the + libraries. xorriso improved its capabilities to serve the needs of frontend + programs. A proof of concept for a GUI frontend has been implemented: + xorriso-tcltk + +- Mon Mar 18 2013 release 1.2.8: + Some rarely occuring bugs were fixed in libisofs and libburn. libburn's + handling of incomplete sessions has been improved. xorriso's mkisofs + emulation learned to set El Torito section id strings. + +- Fri May 17 2013 release 1.3.0: + Several bugs were fixed in the libraries and in xorriso. The recently + introduced new boot preparation capabilities have been tested. New + boot preparation options for GRUB2 were added. + +- Fri May 31 2013 patch release libburn-1.3.0.pl01: + cdrskin -msinfo on DVD and BD reported as old session start the same + number as the next writable address. + Regression introduced by version 1.2.8. + +- Fri Aug 07 2013 release 1.3.2: + cdrskin has aquired the capability to copy audio tracks to .wav files. + It can extract CD-TEXT in a form that is readable for humans and for + cdrskin itself. Several small bugs were fixed in xorriso. Its capabilities + to serve frontend programs in dialog mode have been improved. + +- Thu Dec 12 2013 release 1.3.4: + A long standing hidden bug was fixed, which affected inspection of + unformatted DVD+RW. + xorriso now by default puts EL Torito boot images to low block addresses. + It can report and set read speeds. Several rarely occuring bugs were fixed. + +- Tue Mar 04 2014 release 1.3.6 + libburn learned to operate optical drives and media on NetBSD. libisofs got + a bug fix about HFS+ and enhancements about character set conversion. + Minor bugs were fixed in libisoburn. xorriso can now find files with names + which cannot be represented unchanged in ECMA-119, Joliet, or HFS+. + +- Sat Jun 28 2014 release 1.3.8 + libburn got several bugs fixed. libisofs offers new API calls for inspection + of boot sectors in ISO 9660 filesystems. xorriso improved its support for + NetBSD, offers new features with command -find, and a command to extract + ISO 9660 file content onto standard output or into filter processes. + + +------------------------------------------------------------------------------ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 or later + 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 + +------------------------------------------------------------------------------ +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 or from altered code published under GPL. + +So we will not raise legal protest if you link our libraries dynamically with +applications which are not under GPL, or if you distribute our libraries +and application tools in binary form, as long as you fulfill the usual +condition of GPL to offer a copy of their 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 +Agreement joined later by: Vreixo Formoso + diff --git a/trunk/acinclude.m4 b/trunk/acinclude.m4 new file mode 100644 index 0000000..6e3e012 --- /dev/null +++ b/trunk/acinclude.m4 @@ -0,0 +1,115 @@ +AC_DEFUN([LIBBURNIA_SET_FLAGS], +[ +case $target_os in +freebsd* | netbsd*) + LDFLAGS="$LDFLAGS -L/usr/local/lib" + CPPFLAGS="$CPPFLAGS -I/usr/local/include" + ;; +esac +]) + + +AC_DEFUN([TARGET_SHIZZLE], +[ + ARCH="" + LIBBURNIA_PKGCONFDIR="$libdir"/pkgconfig + + AC_MSG_CHECKING([target operating system]) + + LIBBURNIA_LDCONFIG_CMD="echo 'No ldconfig run performed. If needed, configure manually for:'" + + case $target_os in + linux*) + ARCH=linux + LIBBURN_ARCH_LIBS= + LIBBURNIA_LDCONFIG_CMD=ldconfig + ;; + freebsd*) + ARCH=freebsd + LIBBURN_ARCH_LIBS=-lcam + LIBBURNIA_PKGCONFDIR=$(echo "$libdir" | sed 's/\/lib$/\/libdata/')/pkgconfig + ;; + kfreebsd*-gnu) + ARCH=freebsd + LIBBURN_ARCH_LIBS=-lcam + ;; + *-solaris*) + ARCH=solaris + LIBBURN_ARCH_LIBS=-lvolmgt + ;; + *) + ARCH= + LIBBURN_ARCH_LIBS= +# AC_ERROR([You are attempting to compile for an unsupported platform]) + ;; + esac + + AC_MSG_RESULT([$ARCH]) +]) + + +dnl LIBBURN_ASSERT_VERS_LIBS is by Thomas Schmitt, libburnia project +dnl It tests whether -Wl,--version-script=... works with the compiler +AC_DEFUN([LIBBURN_ASSERT_VERS_LIBS], +[ + libburnia_save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -Wl,--version-script=libburn/libburn.ver" + AC_TRY_LINK([#include ], [printf("Hello\n");], + [vers_libs_test="yes"], [vers_libs_test="no"]) + if test x$vers_libs_test = xno + then + LDFLAGS="$libburnia_save_LDFLAGS" + fi +]) + + +dnl LIBBURNIA_SET_PKGCONFIG determines the install directory for the *.pc file. +dnl Important: Must be performed _after_ TARGET_SHIZZLE +dnl +AC_DEFUN([LIBBURNIA_SET_PKGCONFIG], +[ +### for testing --enable-libdir-pkgconfig on Linux +### LIBBURNIA_PKGCONFDIR="$libdir"data/pkgconfig + +if test "x$LIBBURNIA_PKGCONFDIR" = "x$libdir"/pkgconfig +then + dummy=dummy +else + AC_ARG_ENABLE(libdir-pkgconfig, + [ --enable-libdir-pkgconfig Install to $libdir/pkgconfig on any OS, default=no], + , enable_libdir_pkgconfig="no") + AC_MSG_CHECKING([for --enable-libdir-pkgconfig]) + if test "x$enable_libdir_pkgconfig" = xyes + then + LIBBURNIA_PKGCONFDIR="$libdir"/pkgconfig + fi + AC_MSG_RESULT([$enable_libdir_pkgconfig]) +fi + +libburnia_pkgconfig_override="no" +AC_ARG_ENABLE(pkgconfig-path, +[ --enable-pkgconfig-path=DIR Absolute path of directory for libisofs-*.pc], +libburnia_pkgconfig_override="yes" , enable_pkgconfig_path="none") +AC_MSG_CHECKING([for overridden pkgconfig directory path]) +if test "x$enable_pkgconfig_path" = xno +then + libburnia_pkgconfig_override="no" +fi +if test "x$enable_pkgconfig_path" = x -o "x$enable_pkgconfig_path" = xyes +then + libburnia_pkgconfig_override="invalid argument" +fi +if test "x$libburnia_pkgconfig_override" = xyes +then + LIBBURNIA_PKGCONFDIR="$enable_pkgconfig_path" + AC_MSG_RESULT([$LIBBURNIA_PKGCONFDIR]) +else + AC_MSG_RESULT([$libburnia_pkgconfig_override]) +fi +AC_SUBST(LIBBURNIA_PKGCONFDIR) + +dnl For debugging only +### AC_MSG_RESULT([LIBBURNIA_PKGCONFDIR = $LIBBURNIA_PKGCONFDIR]) + +]) + diff --git a/trunk/bootstrap b/trunk/bootstrap new file mode 100755 index 0000000..faadaab --- /dev/null +++ b/trunk/bootstrap @@ -0,0 +1,10 @@ +#!/bin/sh -x + +aclocal -I . +libtoolize --copy --force +autoconf + +# ts A61101 : libburn is not prepared for config.h +# autoheader + +automake --foreign --add-missing --copy --include-deps diff --git a/trunk/cdrskin/README b/trunk/cdrskin/README new file mode 100644 index 0000000..f81d1d1 --- /dev/null +++ b/trunk/cdrskin/README @@ -0,0 +1,601 @@ +------------------------------------------------------------------------------ + 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-1.3.9.tar.gz + +Copyright (C) 2006-2014 Thomas Schmitt, provided under GPL version 2 or later. +------------------------------------------------------------------------------ + + +cdrskin is a limited cdrecord compatibility wrapper which allows to use +most of the libburn features from the command line. + +Currently it is fully supported on GNU/Linux with kernels >= 2.4, on FreeBSD, +on OpenSolaris, and on NetBSD. +IDE drives under Linux 2.4. need kernel module ide-scsi. +ATA and SATA drives under FreeBSD need kernel module atapicam. +On other X/Open compliant systems there will only be emulated drives, but no +direct MMC operation on real CD/DVD/BD drives. + +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-1.3.9.tar.gz, take it to a directory of your choice and do: + + tar xzf cdrskin-1.3.9.tar.gz + cd cdrskin-1.3.9 + +Within that directory execute: + + ./configure --prefix=/usr + make + +This will already produce a cdrskin binary. But it will be necessary to +install libburn in order to use this binary. + +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: +The command for global installation of both, libburn and cdrskin is + make install +If the library libburn.so.4 is not found with a test run of cdrskin, then +try whether command + ldconfig +makes it accessible. With the statically linked binary this should not matter. + +With that static binary you may as well do the few necessary actions manually. +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. +E.g.: + + 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 totally static 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' + +On Linux, full and insecure enabling of both for everybody would look like + chmod a+rw /dev/sr0 /dev/hda +This is equivalent to the traditional setup chmod a+x,u+s cdrecord. + +On FreeBSD, device rw-permissions are to be set in /etc/devfs.rules. +On Solaris, pfexec privileges may be restricted to "basic,sys_devices". +On NetBSD, rw-permission may be granted by chmod a+rw /dev/rcd?d. +See below "System Dependend Drive Permission Examples". + +I strongly discourage to run cdrskin with setuid root or via sudo ! +It is not checked for the necessary degree of hacker safety. +Better consider to grant the necessary permissions to group "floppy" +and to add users to it. + + +A behavioral conflict is known between any burn software and demons like hald +which probe CD drives. This can spoil burn runs for CD-R or CD-RW. +You may have to keep your hald away from the drive. See for example + http://www.freebsd.org/gnome/docs/halfaq.html + + +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 -minfo + +Prepare CD-RW or DVD-RW for re-use, DVD-RAM or BD-RE for first use + cdrskin -v dev=/dev/sg1 blank=as_needed -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[/DL] + cdrskin dev=/dev/hdc padsize=300k -multi 1.iso + cdrskin dev=/dev/hdc padsize=300k -multi 2.iso + cdrskin dev=/dev/hdc padsize=300k -multi 3.iso + cdrskin dev=/dev/hdc padsize=300k 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 \ + 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 + +Extract audio tracks and CD-TEXT from CD into directory /home/me/my_cd: + mkdir /home/me/my_cd + cdrskin -v dev=/dev/sr0 extract_audio_to=/home/me/my_cd \ + cdtext_to_v07t=/home/me/my_cd/cdtext.v07t + + + 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. + +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 , DVD-RAM , BD-RE + +These random access 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) or BD units +(1x = 4,495,625 bytes/second). Currently there is no difference between -sao +and -tao. If ever, then -tao will be the mode which preserves the current +behavior. + +BD-RE media need formatting before first use. cdrskin option "blank=as_needed" +recognizes unformatted BD-RE and applies a lengthy formatting run. + +During write operations DVD-RAM and BD-RE automatically apply Defect +Management. This usually slows them down to half nominal speed. If drive +and media produce flawless results anyway, then one can try to reach full +nominal speed by option "stream_recording=on". +In this case bad blocks are not detected during write and not even previously +known bad blocks are avoided. So you have to make your own readability tests +and go back to half speed as soon as the first read errors show up. +Instead of "on" one may also set a start address for stream recording. +Like "stream_recording=100m". This will write slowly to the first 100 MB of +the media and accelerate when writing to higher addresses. + +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 , DVD-R , DVD-R DL + +DVD-RW are usable if formatted to state "Restricted Overwrite" or if in state +"Sequential Recording". DVD-R are always in sequential state. DVD-R DL are +always sequential and incapable of multi-session. + +"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 , DVD+R DL , BD-R + +From the view of cdrskin they behave much like DVD-R. Each track gets wrapped +into an own session, though. + +DVD+R DL appear as extra large DVD+R. cdrskin does not allow to set the address +of the layer break where a reading drive might show some delay while switching +between both media layers. + +BD-R are sold unformatted blank. If used without initial formatting then the +drive is supposed to format them to maximum payload size with no Defect +Management (see also above with BD-RE). +If Defect Management is desired then BD-R need to be formatted before the +first attempt to write a session to them. +blank=format_if_needed will detect the situation and eventually apply + default sized Defect Management formatting. +blank=format_defectmgt_* will apply non-default parameters to formatting. + + + 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 + +All following options of ./configure and cdrskin/compile_cdrskin.sh are +combinable. After runs of ./configure do as next: + make clean ; make + +In some situations Linux may deliver a better write performance to drives if +the track input is read with O_DIRECT (see man 2 open). The API call +burn_os_open_track_src() and the input readers of cdrskin and libburn fifo +can be told to use this peculiar read mode by: + --enable-track-src-odirect + +But often cdrskin option dvd_obs=64k will yield even better performance in +such a situation. 64k can be made default at compile time by + cdrskin/compile_cdrskin.sh -dvd_obs_64k +It can also be enabled at configure time by + ./configure ... --enable-dvd-obs-64k ... + +Alternatively the transport of SCSI commands can be done via libcdio-0.83. +You may install it and re-run libburn's ./configure with option + --enable-libcdio + +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. + + +Linux only: + +libburn tries to avoid a collision with udev's drive examination by waiting +0.1 seconds before opening the device file for a longer time, after udev +might have been alarmed by drive scanning activities. +The waiting time can be set at ./configure time with microsecond granularity. +E.g. 2 seconds: + CFLAGS="$CFLAGS -DLibburn_udev_wait_useC=2000000" + ./configure ...options... +Waiting can be disabled by zero waiting time: + CFLAGS="$CFLAGS -DLibburn_udev_wait_useC=0" +Alternatively, libburn can try to be nice by opening the device file, +closing it immediately, waiting, and only then opening it for real: + CFLAGS="$CFLAGS -DLibburn_udev_extra_open_cyclE -DLibburn_udev_wait_useC=500000" + +------------------------------------------------------------------------------ + + System Dependend Drive Permission Examples + +Accessing the optical drives requires privileges which usually are granted +only to the superuser. Linux, FreeBSD, Solaris, NetBSD, offer quite different +approaches for avoiding the need for unrestricted privileges. + +First check whether some friendly system setting already allows you to +access the drives as normal user: + cdrskin --devices +Those drives of which you see address and type strings are already usable. + +If there remain drives invisible which the superuser can see by the same +command, then the following examples might help: + +--------------- +On all systems: +--------------- +Add the authorized users of CD drives to group "floppy" in /etc/group. +If missing: create this group. +Changes to /etc/group often only affect new login sessions. So log out and in +before making the first tests. + +--------- +On Linux: +--------- +Allow rw-access to the drives + chgrp floppy /dev/sr0 /dev/sr1 + chmod g+rw /dev/sr0 /dev/sr1 +It might be necessary to perform chgrp and chmod after each reboot or to +edit distro dependent device configuration files for permanent settings. + +----------- +On FreeBSD: +----------- +Edit /etc/devfs.rules and make sure to have these lines + [localrules=10] + add path 'acd*' mode 0664 group floppy + add path 'cd*' mode 0664 group floppy + add path 'pass*' mode 0664 group floppy + add path 'xpt*' mode 0664 group floppy + [localrules=5] + add path 'pass*' mode 0664 group floppy + add path 'cd*' mode 0664 group floppy + add path 'xpt*' mode 0664 group floppy + add path 'acd*' mode 0664 group floppy + +Edit /etc/rc.conf and add the following line if missing + devfs_system_ruleset="localrules" + +This gets into effect by reboot or by command + /etc/rc.d/devfs start + +----------- +On Solaris: +----------- +Run cdrskin by + pfexec cdrskin ...arguments... + +The following settings will make pfexec keep original UID and EUID and prevent +most superuser powers. Be aware that you still can manipulate all device files +if you have the file permissions for that. +Full root privileges for cdrskin can then be aquired only by command su. + +Edit /etc/security/exec_attr and add this line to the other "Media Backup" +lines: + Media Backup:solaris:cmd:::/usr/local/bin/cdrskin:privs=basic,sys_devices +Edit /etc/user_attr and add profile "Media Backup" to the user's line: + thomas::::profiles=Media Backup,Primary Administrator;roles=root +See also man privileges, man exec_attr, man user_attr. + +Then allow the group r-access to the drives + pfexec chgrp floppy /dev/rdsk/c3t0d0s2 /dev/rdsk/c4t0d0s2 + pfexec chmod g+r /dev/rdsk/c3t0d0s2 /dev/rdsk/c4t0d0s2 +The last two commands have to be executed after each boot. I do not know +the relevant device configuration files yet. + +---------- +On NetBSD: +---------- +Allow rw-access to the drives + chgrp floppy /dev/rcd[01]d + chmod g+rw /dev/rcd[01]d + +------------------------------------------------------------------------------ + 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 imposed the need to reboot the +system because of drives gnawing endlessly on ill media. Permanent hardware +damage did not occur in 3.5 years of development. But one never knows ... + +------------------------------------------------------------------------------ + +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 or later + 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-2014 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. + + diff --git a/trunk/cdrskin/add_ts_changes_to_libburn_1_3_8 b/trunk/cdrskin/add_ts_changes_to_libburn_1_3_8 new file mode 100755 index 0000000..0edc6db --- /dev/null +++ b/trunk/cdrskin/add_ts_changes_to_libburn_1_3_8 @@ -0,0 +1,246 @@ +#!/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="1.3.8" +patch_level="" +# 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_dynamic="cdrskin_${skin_rev}-amd64-suse10_2" +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"/.*.swo +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/trunk/cdrskin/add_ts_changes_to_libburn_1_3_9 b/trunk/cdrskin/add_ts_changes_to_libburn_1_3_9 new file mode 100755 index 0000000..01e9ee3 --- /dev/null +++ b/trunk/cdrskin/add_ts_changes_to_libburn_1_3_9 @@ -0,0 +1,246 @@ +#!/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="1.3.9" +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}-amd64-suse10_2" +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"/.*.swo +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/trunk/cdrskin/cdrecord_spy.sh b/trunk/cdrskin/cdrecord_spy.sh new file mode 100755 index 0000000..54d7c34 --- /dev/null +++ b/trunk/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/trunk/cdrskin/cdrfifo.c b/trunk/cdrskin/cdrfifo.c new file mode 100644 index 0000000..f2c0cf8 --- /dev/null +++ b/trunk/cdrskin/cdrfifo.c @@ -0,0 +1,1313 @@ +/* + 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 + +#ifndef Cdrfifo_standalonE +/* <<< until release of 0.7.4 : for Libburn_has_open_trac_srC */ +#include "../libburn/libburn.h" +#endif + +#include "cdrfifo.h" + + +/* Macro for creation of arrays of objects (or single objects) */ +#define TSOB_FELD(typ,anz) (typ *) calloc(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 bit0= Debugging verbosity + @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; + +#ifdef Libburn_has_open_trac_srC + o->buffer= burn_os_alloc_buffer((size_t) buffer_size, 0); +#else + o->buffer= TSOB_FELD(char,buffer_size); +#endif /* ! Libburn_has_open_trac_srC */ + + 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) +#ifdef Libburn_has_open_trac_srC + burn_os_free_buffer(o->buffer, o->buffer_size, 0); +#else + free((char *) o->buffer); +#endif /* Libburn_has_open_trac_srC */ + + 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= 0,ret,did_work= 0,idx,sod, 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) { + 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; + +#ifdef Libburn_has_open_trac_srC + + /* ts A91115 + This chunksize must be aligned to filesystem blocksize. + */ +#define Cdrfifo_o_direct_chunK 32768 + + if(o->write_idx < o->read_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; + /* Make sure to read with properly aligned size */ + if(can_read > Cdrfifo_o_direct_chunK) + can_read= Cdrfifo_o_direct_chunK; + else if(can_read < Cdrfifo_o_direct_chunK) + can_read= -1; + ret= 0; + if(can_read>0) + ret= read(o->source_fd,o->buffer+o->write_idx,can_read); + if(can_read < 0) { + /* waiting for a full Cdrfifo_o_direct_chunK to fit */ + if(can_write <= 0 && o->dest_fd >= 0) { + fd_set rds,wts,exs; + struct timeval wt; + + FD_ZERO(&rds); + FD_ZERO(&wts); + FD_ZERO(&exs); + FD_SET((o->dest_fd),&wts); + wt.tv_sec= 0; + wt.tv_usec= 10000; + select(o->dest_fd + 1,&rds, &wts, &exs, &wt); + + } + } else + +#else /* Libburn_has_open_trac_srC */ + + 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); + +#endif /* ! Libburn_has_open_trac_srC */ + + 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]; + int i,iv,stall_counter= 0,cycle_counter= 0.0; + char 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]; + int i,iv; + 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/trunk/cdrskin/cdrfifo.h b/trunk/cdrskin/cdrfifo.h new file mode 100644 index 0000000..94a291a --- /dev/null +++ b/trunk/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 bit0= Debugging verbosity + @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/trunk/cdrskin/cdrskin.1 b/trunk/cdrskin/cdrskin.1 new file mode 100644 index 0000000..30fcf72 --- /dev/null +++ b/trunk/cdrskin/cdrskin.1 @@ -0,0 +1,1773 @@ +.\" 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 "Version 1.3.9, Jun 28, 2014" +.\" 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, DVD, and BD 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 and BD 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 +Formatting of DVD-RW, DVD+RW, DVD-RAM, BD. +.br +Burning of data tracks or audio tracks with CD-TEXT 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 DVD+R[/DL] or BD-R. +.br +Single session Disk-at-once on DVD-RW, DVD-R, DVD-R DL. +.br +Single session or emulated ISO-9660 multi-session +.br +on overwriteable DVD+RW, DVD-RW, DVD-RAM, BD-RE +.br +or on data file or block device. +.br +Extraction of audio tracks and CD-TEXT to hard disk files. +.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 or BD Media +.br +Overwriteable DVD or BD 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 +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 +Normally, each track is initiated by one track source address argument, +which may either be "-" for standard input or the address of a readable file. +Alternatively, option cuefile= may be used to read a session description +from a text file and to read the session content from a single data file. +.br +If no write mode +is given explicitly then one will be chosen which matches the peculiarities +of track sources and the state of the output media. +.PP +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. +This information is 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 and BD there is only type data. +.br +If not explicitly 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 or xorriso. +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 can be used to write CD-TEXT and +it is the only one that works with option cuefile=. +.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 explicitly, +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 +needed. +.SS +.B Sequentially Recordable DVD or BD Media: +.br +Currently DVD-RW, DVD-R[DL], DVD+R[DL], and BD-R can be used for the Sequential +recording model. It resembles the model of CD media. Only DVD-RW can be +blanked and re-used from scratch. +.br +DVD-RW are sequential media if they are 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 explicitly by options +.B tsize= +or +.B tao_to_sao_tsize= . +.br +DAO is the only mode for DVD-R media which do not offer feature 21h Incremental +Streaming (e.g. DVD-R DL). DAO may also be selected explicitly 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 explicitly 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 . It does not work with DVD-R DL and minimally blanked DVD-RW. +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[/DL] and BD-R resemble those with DVD-R except that +each track gets wrapped in an own session. There is no -dummy writing +with DVD+R[/DL] or BD-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 +BD-R may be formatted before first use to enable the Defect Management which +might catch and repair some bad spots at the expense of slow speed +even with flawless 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 or BD Media: +.br +Currently types DVD+RW, DVD-RW, DVD-RAM and BD-RE can be overwritten via +cdrskin. +.br +Option -audio is not allowed. Only one track is allowed. +Option -multi cannot mark a recognizable 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. +.PP +DVD+RW and DVD-RAM media need no special initial formatting. They offer a +single continuous data area for blockwise random access. BD-RE need +explicit formatting before use. See +.B blank=as_needed +or blank=format_defectmgt . +.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, CD, DVD, or BD 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 or \fBcdrskin --device_links\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 +For drive permission examples on Linux, FreeBSD, and Solaris, +see cdrskin/README. +.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 consist of three numbers: +Bus,Target,Lun. On Linux 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: 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. With BD media +print "Mounted Media:" and 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 explicitly 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 or BD. +.TP +.BI blank= type +Blank a CD-RW, DVD-RW, or format a DVD-RW, DVD+RW, DVD-RAM, BD. +This is combinable with burning in the same run of cdrskin. +The type given with blank= selects the particular behavior: +.RS +.TP +as_needed +Try to make the media ready for writing from scratch. If it needs formatting, +then format it. If it is not blank, then try to apply blank=fast. +It is a reason to abort if the media cannot assume thoroughly writeable state, +e.g. if it is non-blank write-once. +.br +This leaves unformatted DVD-RW in unformatted blank state. To format DVD-RW use +blank=format_overwriteable. Blank unformatted BD-R stay unformatted. +.br +(Note: blank=as_needed is not an original cdrecord option.) +.TP +The following blank types are specific to particular media familes. Use them if special features are desired. +.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 +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_* 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). +.TP +format_if_needed +Format a media if it is not formatted yet, +and if cdrskin supports formatting for the media type, +and if formatting will not happen automatically during write. +This currently applies to unformatted DVD-RW, DVD-RAM, BD-RE, +and blank unformatted BD-R. +Eventually the appropriate default formatting is chosen. +If other media or states are encountered then nothing happens. +.br +The following formatting types are more specialized to particular +media families. +.TP +format_overwrite +Format a DVD-RW to "Restricted Overwrite". The user should bring some patience. +.br +(Note: blank=format_* 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 +format_defectmgt +Format DVD-RAM or BD to reserve the default amount of spare blocks for +defect management. +.br +The following format_defectmgt_* allow to submit user wishes which +nevertheless have to match one of the available formats. These formats are +offered by the drive after examining the media. +.TP +format_defectmgt_cert_off +Disable the usual media quality certification in order to save time and +format to default size. +The certification setting persists even if subsequent blank= options override +the size of the format selection. +.br +Whether formatting without certification works properly depends much on the +drive. One should check the "Format status:" from --list_formats afterwards. +.TP +format_defectmgt_cert_on +Re-enable the usual media quality certification and format to default size. +The certification setting persists like with format_defectmgt_cert_off. +.br +Whether there happens certification at all depends much on the media state +and the actually selected format descriptor. +.TP +format_defectmgt_max +Format DVD-RAM or BD to reserve a maximum number of spare blocks. +.TP +format_defectmgt_min +Format DVD-RAM or BD to reserve a minimum number of spare blocks. +It might be necessary to format format_defectmgt_none first in order to get +offered the most minmal spare blocks sizes for format_defectmgt_min. +.TP +format_defectmgt_none +Format DVD-RAM or BD-RE to the largest available payload in the hope to disable +defect management at all. This may or may not have a speed increasing effect. +Unformatted blank BD-R will be left unformatted. +.TP +format_defectmgt_payload_ +Format DVD-RAM or BD. The text after "format_defectmgt_payload_" gives a +number of bytes, eventually with suffixes "s", "k", "m". The largest number +of spare blocks will be chosen which allows at least the given payload size. +.TP +format_by_index_ +Format DVD-RW, DVD+RW, DVD-RAM or BD. +The number after "format_by_index_" is used as index to the list of available +format descriptors. This list can be obtained by option --list_formats. +The numbers after text "Format idx" are the ones to be used with +format_by_index_. Format descriptor lists are volatile. Do neither eject +nor write the media between the run of --list_formats and the run of +blank=format_by_index_ or else you may get a different format than desired. +.TP +help +Print this list of blanking types. +.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 \-copy +Create the subsequent tracks with permission for an unlimited number of copies. +.TP +.BI cuefile= path +Read a session description from a cue sheet file in CDRWIN format. +Base the tracks on a single file which is given in the sheet by command FILE. +To enable CD-TEXT from the cue sheet file, cdrskin option -text has to be +present. +.br +cdrskin currently supports TRACK datatypes AUDIO and MODE1/2048 which may +not be mixed. +Data source may be of FILE type BINARY, MOTOROLA, or WAVE. +.br +Non-CDRWIN commands ARRANGER, COMPOSER, MESSAGE are supported. +.br +Cue sheet file commands CATALOG and ISRC may be overridden by option mcn= +and by input_sheet_v07t= purpose specifiers "UPC / EAN" and "ISRC". +This does not affect their appearance in CD-TEXT, but only on Q sub-channel. +.br +The track numbers may be overridden by option cd_start_tno=. +.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 or -xa1. +.br +Options -mode2, -xa, and -xa2 get mapped to -data, not using the desired CD +sector formats and thus not taking advantage of eventual higher payload. +.BI \-xa1 +Subsequent tracks are data tracks with input suitable for CD-ROM XA mode 2 +form 1. This differs from -data input by 8 additional header bytes per block. +cdrskin will not write CD-ROM XA but rather strip the header bytes and write as +-data tracks. +.TP +.BI dev= target +Set the address of the drive to use. Valid are at least the +addresses listed with options --devices or --device_links, +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 explicitly 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 +It enables a burn run where cdrskin expects to exceed the available media +capacity. +.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 index= list +Set a comma separated list of index start address numbers for the next track. +This applies to CD SAO sessions only. +.br +The addresses count sectors from the start of the next track. The first number +is for index 1 and must be 0. The following numbers have to be larger than +their respective predecessors. Up to 99 numbers are allowed. +.br +Sector numbers are computed from Min:Sec:Frame addresses by +.br + Sector = ((Min*60)+Sec)*75+Frame +.br +E.g.: "0,7512,20408" sets index 2 to 01:40:12 and index 3 to 04:32:08. +.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 preceded +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 isrc= text +Set the ISRC for the next track source to the given text, which must be exactly +13 characters long. It must comply to the format CCOOOYYSSSSS. +.br +CC is the country code. OOO is the owner code. Both may consist of capital +letters A to Z and of decimal digits 0 to 9. YY depicts the year (00 to 99). +SSSSS is the serial number (00000 to 99999). +.br +This option does not affect CD-TEXT but only the Q sub-channel. +.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 mcn= text +Set the CD Media Catalog Number to text, which must be exactly 13 characters +long and should consist of decimal digits. +.br +This option does not affect CD-TEXT but only the Q sub-channel. +.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 -minfo +Print information about the loaded media. This includes media type, writability +state, and a quite readable table of content. +.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, genisoimage, or xorriso -as mkisofs. +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 CD, unformatted DVD-R[W], DVD+R, or BD-R 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 and on DVD-R DL media. +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 or BD 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 \-nocopy +Create subsequent tracks with permission for a single level of copies. +I.e. those copies would then be marked by -scms as offering no permission +for further copies. +.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 \-nopreemp +Indicate for subsequent tracks that they were mastered without pre-emphasis. +.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 \-preemp +Indicate for subsequent tracks that they were mastered with pre-emphasis. +.TP +.BI \-sao +Write CD in Session At Once mode or sequential DVD-R[W] in Disc-at-once +(DAO) mode. +.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, or DVD+R[/DL], or BD 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 preceded 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 \-scms +Create subsequent tracks without permission for being copied. This is usually +done for tracks which are copies of tracks that were marked with -nocopy +(but not yet with -scms). So copies of copies are prohibited. +.br +This option gets reset by option -copy. Thus the combination -copy -nocopy +means -nocopy surely without -scms. +.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. +With BD 1x = 4,495,625 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[/DL] without traditional -sao restrictions. +This mode also applies pro-forma to overwriteable 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. +.br +Mode -tao is not usable for minimally blanked DVD-RW and for DVD-R DL. +.TP +.BI \-text +Enable writing of CD-TEXT attributes read by option cuefile=. +Without option -text, cue sheet file command CDTEXTFILE will be ignored and +no CD-TEXT attributes will be read from the file. Nevertheless, CATALOG and +ISRC will have the same effect as options mcn= and isrc=. +.TP +.BI textfile= path +Read CD-TEXT packs from the file depicted by path and put them into the +Lead-in of the emerging session. This session has to be done by Session At Once +(SAO) mode and may only contain audio tracks. +.br +path must lead to a regular file, which consists of an optional header of four +bytes and one or more text packs of 18 bytes each. Suitable would be the +file 'cdtext.dat' which gets extracted from CD media by options -vv -toc +and shown in human readable form by -vvv -toc. +.br +The header, if present, must tell the file size minus 2, encoded as big-endian +16 bit word. The other two bytes must be 0. +.br +If there is no 4-byte header, then a trailing 0-byte, as of Sony specification, +is tolerated and ignored. +.br +A text pack consists of a pack type byte, a track number byte, a counter byte, +a Block Number and Character Indicator byte, 12 text characters or data bytes, +two optional CRC bytes. For details see libburn documentation file +doc/cdtext.txt. +.br +By default, the input file is checked for correct CRC bytes. If all CRC bytes +are 0, then the correct values get silently inserted. If there are non-zero +CRC bytes, then a mismatch causes the abort of the burn run. +This check can be disabled by option -force. +.br +Note that this option overrides option input_sheet_v07t= . +.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. +.br +If verbosity is set to level 2 (-v -v) then the CD-TEXT packs from the lead-in +of an audio CD get extracted and written into file 'cdtext.dat', if that file +does not yet exist. Prepended is a 4 byte header, followed by one or more +packs of 18 bytes each. +.br +Verbosity level 3 causes the CD-TEXT packs to be printed as hex numbers to +standard output. Bytes 4 to 15 of certain pack types are printed as ASCII +characters if they have values in the range of 32 to 126. +.br +See option textfile= for more information about the text pack format. +.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 verbosity 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 \-V +Enable logging of SCSI commands to stderr. This allows expert examination +of the interaction between libburn and the drive. The commands are specified +in SCSI-3 standards SPC, SBC, MMC. +.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 explicitly 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 cd_start_tno= number +Set the number which shall be written as CD track number with the first +track of the session. The following tracks will then get written with +consecutive CD track numbers. The resulting number of the last track +must not exceed 99. The lowest possible start number is 1, which is also +the default. +.br +This setting applies only to CD SAO writing. It overrides the track number +settings caused by options cuefile= or input_sheet_v07t=. +.TP +.BI cdtext_to_textfile= path +Extract the CD-TEXT packs from the lead-in of an audio CD and write them to +the file with the given path. If CD-TEXT can be retrieved, then this file +will be suitable for option textfile=. +.br +Not all drives can read CD-TEXT and not all audio CDs bear CD-TEXT. +It is not considered an error if no CD-TEXT is available. +.TP +.BI cdtext_to_v07t= path +Extract the CD-TEXT packs from the lead-in of an audio CD and write them +as human readable Sony Input Sheet Version 0.7T to the file with the +given path. If CD-TEXT can be retrieved, then this file +will be suitable for option input_sheet_v07t=. +.br +If the given path is "-", then the result is printed to standard output. +.br +Not all drives can read CD-TEXT and not all audio CDs bear CD-TEXT. +It is not considered an error if no CD-TEXT is available. +.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 \--device_links +Like --devices, but presenting the drives with addresses of symbolic links +which point to the actual device files. +.br +Modern GNU/Linux systems may shuffle drive addresses from boot to boot. +The udev daemon is supposed to create links which always point to the +same drive, regardless of its system address. +Option --device_links shows the addresses of such links if they begin +by "/dev/dvd" or "/dev/cd". +Precedence is: "dvdrw", "cdrw", "dvd", "cdrom", "cd". +.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, BD-RE, 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, BD-RE, DVD+RW this is 2k, with +overwriteable DVD-RW it is 32k. +.TP +.BI dvd_obs= default|32k|64k +Set the number of bytes to be transmitted with each write operation to DVD +or BD media. With most write types, tracks get padded up to the next multiple +of this write size (see option --obs_pad). +A number of 64 KB may improve throughput with systems +which show latency problems. The default depends on media type, option +stream_recording=, and on compile time options. +.TP +.BI extract_audio_to= directory_path +Extract tracks from an audio CD as separate WAVE audio files into the +given directory. +This directory has to already exist, but none of the track files may exist. +This option will rather fail than overwrite an existing file. +.br +By default all tracks of the CD are extracted to files with names +trackNN.wav, where NN is the track number from 01 to at most 99. +.TP +.BI extract_basename= name +Set a filename which shall be used by extract_audio_to= instead of the default +name "track". +.TP +.BI --extract_dap +Enable Digital Audio Play flaw obscuring mechanisms +like audio data mute and interpolate. +.TP +.BI extract_tracks= number[,number[,...]] +Set a list of track numbers to define which tracks shall be extracted +by extract_audio_to=. +If no extract_tracks= is given, then all audio tracks get extracted. +It is permissible to have more than one extract_tracks= option in order +to split a long list into shorter pieces. +.br +The lowest permissible track number is 1, the highest is 99. +.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 program 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 --four_channel +Indicate for subsequent tracks that they were mastered with four channels. +.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 recognizable 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 overridden 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 input_sheet_v07t= path +Read CD-TEXT definitions from a Sony Input Sheet version 0.7T. Up to eight +or seven such sheets can be read by multiple input_sheet_v07t= options. +Each will define one CD-TEXT language block. +.br +The first line of a sheet file decides whether more than one sheet +may be defined by the file. If it is +.br + Input Sheet Version = 0.7T +.br +then each further line with that text switches to the next sheet for the next block. +If it is not, then all definitions apply to a single block. +.br +The information in such a sheet is given by text lines of the following form: +.br + purpose specifier [whitespace] = [whitespace] content text +.br +[whitespace] is zero or more ASCII 32 (space) or ASCII 9 (tab) characters. +The purpose specifier tells the meaning of the content text. +Empty content text does not cause a CD-TEXT attribute to be attached. +.br +The following purpose specifiers apply to the session as a whole: +.br + Purpose specifier | Content example +.br + ------------------------------------------------------------- +.br + Text Code = 8859 +.br + Language Code = English +.br + Album Title = Joyful Nights +.br + Artist Name = United Cat Orchestra +.br + Songwriter = Various Songwriters +.br + Composer = Various Composers +.br + Arranger = Tom Cat +.br + Album Message = For all our fans +.br + Catalog Number = 1234567890 +.br + Genre Code = Classical +.br + Genre Information = Feline classic music +.br + Closed Information = This is not to be shown by CD players +.br + UPC / EAN = 1234567890123 +.br + Text Data Copy Protection = OFF +.br + First Track Number = 1 +.br + Last Track Number = 3 +.br +The following purpose specifiers apply to particular tracks: +.br + Purpose specifier | Content example +.br + ------------------------------------------------------------- +.br + Track 01 Title = Song of Joy +.br + Track 01 Artist = Felix and The Purrs +.br + Track 01 Songwriter = Friedrich Schiller +.br + Track 01 Composer = Ludwig van Beethoven +.br + Track 01 Arranger = Tom Cat +.br + Track 01 Message = Fritz and Louie once were punks +.br + ISRC 01 = XYCRR1101234 +.br +Track numbers are decimal despite the leading 0. There should be as many track +definitions as there are track source files given. +.br +See libburn's doc/cdtext.txt for a detailed definition of 0.7T and the +possible values for Text Code, Language Code, Genre Code, Text Data Copy +Protection. +.br +The Q sub-channel settings by "UPC / EAN" and "ISRC" may be overridden by +options mcn= and isrc=. This will not affect their appearance as CD-TEXT. +They may override cuefile= commands CATALOG and ISRC in the same way. +.br +If options -text cuefile= are given and if the cue sheet file defines CD-TEXT, +then only seven input_sheet_v07t= options may be given. They will then be +used as CD-TEXT language blocks 1 to 7. +.br +This option will get into effect only if no option textfile= is given. +The write mode must be SAO on CD. All tracks must be -audio tracks. +.br +The track numbers may be overridden by option cd_start_tno=. +.TP +.BI \--list_formats +List the available format descriptors as reported by the drive for the +loaded media. Each descriptor line begins with "Format idx" and the +descriptor's list index, followed by a ":", the format type, the number +of payload blocks and that same number converted to MiB. +.br +The meaning of the format types is defined by the MMC standard +with command FORMAT UNIT. A user will more be interested in the +sizes than in the types. +.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 \--list_speeds +Put out a list of speed values as reported by the output drive with +the loaded medium. This does not necessarily mean that the medium is writable +or that these speeds are actually achievable. Especially the +lists reported with empty drive or with ROM media obviously advertise +speeds for other media. +.br +It is not mandatory to use speed values out of the listed range. +The drive is supposed to choose a safe speed that is as near to the desired +speed as possible. +.br +At the end of the list, "Write speed L" and "Write speed H" +are the best guesses for lower and upper speed limit. +"Write speed l" and "Write speed h" may appear only with CD +and eventually override the list of other speed offers. +.br +Only if the drive reports contradicting speed information there will appear +"Write speed 0" or "Write speed-1", which tell the outcome of speed selection +by options speed=0 resp. speed=-1, if it deviates from "Write speed L" +resp. "Write speed H". +.TP +.BI \--long_toc +Like option -toc but marking each session start by a line "first: X last: Y" +and each session end by "track:lout ...". +.TP +.BI \--no_load +When aquiring the optical drive, do not try to load its tray. This yields the +same behavior for desktop drives with tray loader as is shown by laptop drives +which usually lack a motorized tray loader. +.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 \--pacifier_with_newline +Adds a newline character to each pacifier line that would elsewise be +overwritten by the next pacifier line. Such lines are emitted during a +run of writing, formatting, or blanking if option -v is given. +.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 stdio_sync= on|off|number +Set the number of bytes after which to force output to drives with prefix +"stdio:". This forcing keeps the memory from being clogged with lots of +pending data for slow devices. Default "on" is the same as "16m". +Forced output can be disabled by "off". +.TP +.BI stream_recording= on|off|number +By setting "on" request that compliance to the desired speed setting is +preferred over management of write errors. With DVD-RAM and BD this can +bring effective write speed near to the nominal write speed of the media. +But it will also disable the automatic use of replacement blocks +if write errors occur. It might as well be disliked or ignored by the drive. +.br +If a number is given, then error management stays enabled for all byte +addresses below that number. Any number below 16s is the same as "off". +.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 explicitly. +.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 textfile_to_v07t= path +Read a CD-TEXT pack file (e.g. cdtext.dat from a run with -v -v -toc) +and print its content in the human readable format that is described +with option input_sheet_v07t=. +.br +The program run ends immediately thereafter. +No drive scan will happen and no drive will be aquired. +.br +To avoid the cdrskin start message in the output, run: + cdrskin textfile_to_v07t=cdtext.dat | grep -v '^cdrskin' +.TP +.BI --two_channel +Indicate for subsequent tracks that they were mastered with two channels. +.TP +.BI write_start_address= byte_offset +Set the address on media where to start writing the track. With DVD+RW, DVD-RAM +or BD-RE 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 option is without effect because no media types are +under test reservation. +.br +(If you really test experimental media, then please report the outcome on +libburn-hackers@pykix.org) +.TP +.BI \--cdtext_dummy +Prepare a burn run, report the effective array of CD-TEXT packs to stdout, +and then end the program run without starting to burn the session. +A blank CD-R or CD-RW has to be present in the drive, nevertheless. +.br +The output is formatted in lines which describe 18 bytes as 2-digit hex +numbers or as single printable characters. +See libburn document doc/cdtext.txt about the format of these records. +.TP +.BI \--cdtext_verbose +Like --cdtext_dummy but without preventing the burn run. Combinable with +option -dummy to exercise a CD burn run with no persistent impact on the +medium. +.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 scanned for by +options --devices, --device_links and -scanbus. +Normally this is /dev/sgN on kernel versions < 2.6 and /dev/srN +on kernels >= 2.6 . This option allows to explicitly 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 \--obs_pad +Pad the data of last write operation of a DVD-R[W] DAO session or +stdio: pseudo-drive up to the full size of an output chunk. +This padding has to be applied automatically to the other DVD and BD media +types, where it causes e.g. ISO images to have trailing unclaimed blocks. +.br +Use this option if there is the suspicion that DAO sessions abort with +your kernel and/or DVD drive, if their size is not a multiple of 16 blocks. +.br +This option may also get enabled at compile time of libburn. +.TP +.BI \--old_pseudo_scsi_adr +Linux specific: +Use and report literal Bus,Target,Lun addresses rather than real SCSI and +pseudo ATA addresses. This method is outdated and was never compatible with +original cdrecord. +.TP +.BI sao_postgap= off|number +Define whether a post-gap shall be written at the end of the track and +how many sectors this gap shall have. A post-gap occupies the range of +an additional index of the track. It contains zeros. No bytes from the +track source will be read for writing the post-gap. +.br +This setting affects only CD SAO write runs. +.TP +.BI sao_pregap= off|number +Define whether a pre-gap shall be written before the track and how many +sectors this pre-gap shall have. A pre-gap is written in the range of track +index 0 and contains zeros resp. silence. No bytes from the track source +will be read for writing the pre-gap. +.br +This setting affects only CD SAO write runs. +.br +The first track automatically gets a pre-gap of at least 150 sectors. Its +size can only be enlarged by this call. +.TP +.BI --xa1-ignore +Silently interpret option -xa1 as -data. This may be necessary if a frontent +does not prepare -xa1 block headers but insists in using option -xa1. +.SH EXAMPLES +.SS +.B Get an overview of drives and their addresses: +.br +cdrskin -scanbus +.br +cdrskin dev=ATA -scanbus +.br +cdrskin --device_links +.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 -minfo +.SS +.B Prepare CD-RW or DVD-RW for re-use, DVD-RAM or BD-RE for first use: +.br +cdrskin -v dev=/dev/sg1 blank=as_needed -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 + blank=as_needed -eject padsize=300k my_image.iso +.SS +.B Write compressed afio archive on-the-fly (not possible with minimally blanked DVD-RW or DVD-R DL): +.br +find . | afio -oZ - | \\ +.br +cdrskin -v dev=0,1,0 fs=32m speed=8 \\ +.br + blank=as_needed padsize=300k - +.SS +.B Write multi-session to the same CD, DVD-R[W], DVD+R[/DL], or BD-R: +.br +cdrskin dev=/dev/sr0 -v padsize=300k -multi 1.iso +.br +cdrskin dev=/dev/sr0 -v padsize=300k -multi 2.iso +.br +cdrskin dev=/dev/sr0 -v padsize=300k -multi 3.iso +.br +cdrskin dev=/dev/sr0 -v padsize=300k 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 -multi run: +.br +x=$(cdrskin dev=/dev/sr0 -multi \\ +.br + --tell_media_space 2>/dev/null) +.br +echo "Available: $x blocks of 2048 data bytes" +.SS +.B Write audio tracks and CD-TEXT to CD: +.br +cdrskin -v dev=ATA:1,0,0 speed=48 -sao \\ +.br + input_sheet_v07t=cdtext.v07t \\ +.br + track1.wav track2.au -audio -swab track3.raw +.SS +.B Extract audio tracks and CD-TEXT from CD into directory /home/me/my_cd: +.br +mkdir /home/me/my_cd +.br +cdrskin -v dev=/dev/sr0 extract_audio_to=/home/me/my_cd \\ +.br + cdtext_to_v07t=/home/me/my_cd/cdtext.v07t +.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 xorriso (1), +.BR afio (1), +.BR star (1) +.br +.TP +Other CD/DVD/BD burn programs: +.br +.BR cdrecord (1), +.BR wodim (1), +.BR xorriso (1) +.br +.TP +For DVD/BD burning (also tutor of libburn's DVD/BD capabilities): +.br +.BR growisofs (1) +.br +.SH AUTHOR +cdrskin was written by Thomas Schmitt . +.PP +This manual page was started by George Danchev and +is now maintained by Thomas Schmitt. + diff --git a/trunk/cdrskin/cdrskin.c b/trunk/cdrskin/cdrskin.c new file mode 100644 index 0000000..cd28143 --- /dev/null +++ b/trunk/cdrskin/cdrskin.c @@ -0,0 +1,9729 @@ + +/* + cdrskin.c , Copyright 2006-2014 Thomas Schmitt +Provided under GPL version 2 or later. + +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, DVD, or BD, 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. + +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 "1.3.9" +#endif + +/** The official libburn interface revision to use. + (May get changed further below) +*/ +#ifndef Cdrskin_libburn_majoR +#define Cdrskin_libburn_majoR 1 +#endif +#ifndef Cdrskin_libburn_minoR +#define Cdrskin_libburn_minoR 3 +#endif +#ifndef Cdrskin_libburn_micrO +#define Cdrskin_libburn_micrO 8 +#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 + +#ifdef Cdrskin_libburn_1_3_8 +#define Cdrskin_libburn_versioN "1.3.8" +#endif + +#ifdef Cdrskin_libburn_1_3_9 +#define Cdrskin_libburn_versioN "1.3.9" +#endif + +#ifndef Cdrskin_libburn_versioN +#define Cdrskin_libburn_1_3_8 +#define Cdrskin_libburn_versioN "1.3.8" +#endif + +#ifdef Cdrskin_libburn_1_3_8 +#undef Cdrskin_libburn_majoR +#undef Cdrskin_libburn_minoR +#undef Cdrskin_libburn_micrO +#define Cdrskin_libburn_majoR 1 +#define Cdrskin_libburn_minoR 3 +#define Cdrskin_libburn_micrO 8 +#endif +#ifdef Cdrskin_libburn_1_3_9 +#undef Cdrskin_libburn_majoR +#undef Cdrskin_libburn_minoR +#undef Cdrskin_libburn_micrO +#define Cdrskin_libburn_majoR 1 +#define Cdrskin_libburn_minoR 3 +#define Cdrskin_libburn_micrO 9 +#endif + + +/* History of development macros. + As of version 1.1.8 they are now unconditional, thus removing the option + to compile a heavily restricted cdrskin for the old libburn at icculus.org. +*/ + +/* 0.2.2 */ +/* Cdrskin_libburn_does_ejecT */ +/* Cdrskin_libburn_has_drive_get_adR */ +/* Cdrskin_progress_track_does_worK */ +/* Cdrskin_is_erasable_on_load_does_worK */ +/* Cdrskin_grab_abort_does_worK */ + +/* 0.2.4 */ +/* Cdrskin_allow_libburn_taO */ +/* Cdrskin_libburn_has_is_enumerablE */ +/* Cdrskin_libburn_has_convert_fs_adR */ +/* Cdrskin_libburn_has_convert_scsi_adR */ +/* Cdrskin_libburn_has_burn_msgS */ +/* Cdrskin_libburn_has_burn_aborT */ +/* Cdrskin_libburn_has_cleanup_handleR */ +/* Cdrskin_libburn_has_audioxtR */ +/* Cdrskin_libburn_has_get_start_end_lbA */ +/* Cdrskin_libburn_has_burn_disc_unsuitablE */ +/* Cdrskin_libburn_has_read_atiP */ +/* Cdrskin_libburn_has_buffer_progresS */ + +/* 0.2.6 */ +/* Cdrskin_libburn_has_pretend_fulL */ +/* Cdrskin_libburn_has_multI */ +/* Cdrskin_libburn_has_buffer_min_filL */ + +/* 0.3.0 */ +/* Cdrskin_atip_speed_is_oK */ +/* Cdrskin_libburn_has_get_profilE */ +/* Cdrskin_libburn_has_set_start_bytE */ +/* Cdrskin_libburn_has_wrote_welL */ +/* Cdrskin_libburn_has_bd_formattinG */ +/* Cdrskin_libburn_has_burn_disc_formaT */ + +/* 0.3.2 */ +/* Cdrskin_libburn_has_get_msc1 */ +/* Cdrskin_libburn_has_toc_entry_extensionS */ +/* Cdrskin_libburn_has_get_multi_capS */ + +/* 0.3.4 */ +/* Cdrskin_libburn_has_set_filluP */ +/* Cdrskin_libburn_has_get_spacE */ +/* Cdrskin_libburn_write_mode_ruleS */ +/* Cdrskin_libburn_has_allow_untested_profileS */ +/* Cdrskin_libburn_has_set_forcE */ + +/* 0.3.6 */ +/* Cdrskin_libburn_preset_device_familY */ +/* Cdrskin_libburn_has_track_set_sizE */ + +/* 0.3.8 */ +/* Cdrskin_libburn_has_set_waitinG */ +/* Cdrskin_libburn_has_get_best_speeD */ + +/* 0.4.0 */ +/* Cdrskin_libburn_has_random_access_rW */ +/* Cdrskin_libburn_has_get_drive_rolE */ +/* Cdrskin_libburn_has_drive_equals_adR */ + +/* 0.4.2 */ +/* no novel libburn features but rather organizational changes */ + +/* 0.4.4 */ +/* novel libburn features are transparent to cdrskin */ + +/* 0.4.6 */ +/* Cdrskin_libburn_has_stream_recordinG */ + +/* 0.4.8 */ +/* Bug fix release for write_start_address=... on DVD-RAM and BD-RE */ + +/* 0.5.0 , 0.5.2 , 0.5.4 , 0.5.6 , 0.5.8 , 0.6.0 , 0.6.2 */ +/* novel libburn features are transparent to cdrskin */ + +/* 0.6.4 */ +/* Ended to mark novelties by macros. + libburnia libburn and cdrskin are fixely in sync now. + icculus libburn did not move for 30 months. +*/ + +/* 1.1.8 */ +/* The code which got enabled by novelty macros was made unconditional. +*/ + + +#ifdef Cdrskin_new_api_tesT + +/* put macros under test caveat here */ + + +#endif /* Cdrskin_new_api_tesT */ + + +/** ts A90901 + The raw write modes of libburn depend in part on code borrowed from cdrdao. + Since this code is not understood by the current developers and since CDs + written with cdrskin -raw96r seem unreadable anyway, -raw96r is given up + for now. +*/ +#define Cdrskin_disable_raw96R 1 + + +/** A macro which is able to eat up a function call like printf() */ +#ifdef Cdrskin_extra_leaN +#define ClN(x) +#define Cdrskin_no_cdrfifO 1 +#else +#define ClN(x) x +#ifdef Cdrskin_use_libburn_fifO +/* + # define Cdrskin_no_cdrfifO 1 +*/ +#endif +#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" + + +#define Cleanup_set_handlers burn_set_signal_handling +#define Cleanup_app_handler_T burn_abort_handler_t + +/* + # define Cdrskin_use_libburn_cleanuP 1 +*/ +/* May not use libburn cleanup with cdrskin fifo */ +#ifndef Cdrskin_use_libburn_fifO +#ifdef Cdrskin_use_libburn_cleanuP +#undef Cdrskin_use_libburn_cleanuP +#endif +#endif + +#ifdef Cdrskin_use_libburn_cleanuP +#define Cleanup_handler_funC NULL +#define Cleanup_handler_handlE "cdrskin: " +#define Cleanup_handler_flaG 48 +#else +#define Cleanup_handler_funC (Cleanup_app_handler_T) Cdrskin_abort_handler +#define Cleanup_handler_handlE skin +#define Cleanup_handler_flaG 4 +#endif /* ! Cdrskin_use_libburn_cleanuP */ + +/* 0= no abort going on, -1= Cdrskin_abort_handler was called +*/ +static int Cdrskin_abort_leveL= 0; + + +/** The size of a string buffer for pathnames and similar texts */ +#define Cdrskin_strleN 4096 + +/** The maximum length +1 of a drive address */ +#define Cdrskin_adrleN BURN_DRIVE_ADR_LEN + + +/** 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 *) calloc(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); +} + +#ifndef Cdrskin_extra_leaN + + +/** 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 *) calloc(1, 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 *) calloc(1, 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 **) calloc(argcount, sizeof(char *)); + *argidx= (int *) calloc(argcount, sizeof(int)); + *arglno= (int *) calloc(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((int) (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]= calloc(1, strlen(from_pt)+1); + trn->to_address[cnt]= calloc(1, 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= 1; + o->track_type= BURN_MODE1; + o->mode_modifiers= 0; + o->sector_size= 2048.0; + o->track_type_by_default= 1; + o->swap_audio_bytes= 0; + o->cdxa_conversion= 0; + o->isrc[0]= 0; + o->index_string= NULL; + o->sao_pregap= -1; + o->sao_postgap= -1; + 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; + o->libburn_track_is_own= 0; +#ifdef Cdrskin_use_libburn_fifO + o->libburn_fifo= NULL; +#endif /* Cdrskin_use_libburn_fifO */ + + if(flag & 1) + return(1); + + 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->mode_modifiers), + &(o->swap_audio_bytes), &(o->cdxa_conversion), 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_no_cdrfifO + Cdrfifo_destroy(&(track->fifo),0); +#endif + + if(track->libburn_track != NULL && track->libburn_track_is_own) + burn_track_free(track->libburn_track); + if(track->iso_fs_descr!=NULL) + free((char *) track->iso_fs_descr); + if(track->index_string != NULL) + free(track->index_string); + 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) +{ + off_t readcounter= 0,writecounter= 0; + + *size= track->fixed_size; + *padding= track->padding; + *use_data_image_size= track->use_data_image_size; + if((flag&1) && track->libburn_track!=NULL) { + burn_track_get_counters(track->libburn_track,&readcounter,&writecounter); + *size= readcounter; + *padding= writecounter-readcounter; + } else if(flag&2) + *padding= track->tao_to_sao_tsize; + *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); +} + + +#ifdef Cdrskin_use_libburn_fifO + +int Cdrtrack_get_libburn_fifo(struct CdrtracK *track, + struct burn_source **fifo, int flag) +{ + *fifo= track->libburn_fifo; + return(1); +} + + +int Cdrtrack_report_fifo(struct CdrtracK *track, int flag) +{ + int size, free_bytes, ret; + int total_min_fill, interval_min_fill, put_counter, get_counter; + int empty_counter, full_counter; + double fifo_percent; + char *status_text; + + if(track->libburn_fifo == NULL) + return(0); + + /* Check for open input or leftover bytes in liburn fifo */ + ret = burn_fifo_inquire_status(track->libburn_fifo, &size, &free_bytes, + &status_text); + if(ret >= 0 && size - free_bytes > 1) { + /* not clear why free_bytes is reduced by 1 */ + fprintf(stderr, + "cdrskin: FATAL : Fifo still contains data after burning has ended.\n"); + fprintf(stderr, + "cdrskin: FATAL : %d bytes left.\n", size - free_bytes - 1); + 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"); + return(-1); + } + + burn_fifo_get_statistics(track->libburn_fifo, &total_min_fill, + &interval_min_fill, &put_counter, &get_counter, + &empty_counter, &full_counter); + fifo_percent= 100.0*((double) total_min_fill)/(double) size; + if(fifo_percent==0 && total_min_fill>0) + fifo_percent= 1; + fflush(stdout); + fprintf(stderr,"Cdrskin: fifo had %d puts and %d gets.\n", + put_counter,get_counter); + fprintf(stderr, + "Cdrskin: fifo was %d times empty and %d times full, min fill was %.f%%.\n", + empty_counter, full_counter, fifo_percent); + return(1); +} + +#endif /* Cdrskin_use_libburn_fifO */ + + +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; + struct libdax_audioxtr *xtr= NULL; + char *fmt,*fmt_info; + int num_channels,sample_rate,bits_per_sample,msb_first,ret; + + *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); + } + + 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); +} + + +/* @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) + burn_track_set_size(track->libburn_track, (off_t) *size_used); + /* 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_no_cdrfifO + 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 + bit2=permission to use burn_os_open_track_src() (evtl O_DIRECT) + @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; + 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); + struct burn_drive *drive; + + 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; + + ret= Cdrskin_get_device_adr(track->boss,&device_adr,&raw_adr, + &no_convert_fs_adr,0); + if(ret <= 0) { + fprintf(stderr, + "cdrskin: FATAL : No drive found. Cannot prepare track.\n"); + return(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")); + + 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) { + 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); +*/ + + 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) { + if(track->track_type != BURN_MODE1 || + (track->cdxa_conversion & 0x7fffffff)) + flag&= ~4; /* Better avoid O_DIRECT with odd sectors */ + if(flag & 4) + *fd= burn_os_open_track_src(track->source_path, O_RDONLY, 0); + else + *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 */ + } + } + } + } + + if(track->fixed_size < Cdrtrack_minimum_sizE * track->sector_size + && (track->fixed_size>0 || size_from_file) && !(flag&2)) { + 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_no_cdrfifO + +/** 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) | (4 * (track->fifo_size >= 256 * 1024))); + 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, flag & 1); + 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); +} + +#endif /* ! Cdrskin_no_cdrfifO */ + + +#ifndef Cdrskin_extra_leaN + +#ifdef Cdrskin_use_libburn_fifO + +/** Read data into the eventual libburn fifo until either fifo_start_at bytes + are read (-1 = no limit), it is full or or the data source is exhausted. + @return <=0 error, 1 success +*/ +int Cdrtrack_fill_libburn_fifo(struct CdrtracK *track, int fifo_start_at, + int flag) +{ + int ret, bs= 32 * 1024; + int buffer_size, buffer_free; + double data_image_size; + char buf[64 * 1024], *buffer_text; + + if(fifo_start_at == 0) + return(2); + if(track->libburn_fifo == NULL) + 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= burn_fifo_fill(track->libburn_fifo, fifo_start_at, + (fifo_start_at == -1)); + if(ret < 0) + return(0); + +/** Ticket 55: check fifos for input, throw error on 0-bytes from stdin + @return <=0 abort run, 1 go on with burning +*/ + ret= burn_fifo_inquire_status(track->libburn_fifo, &buffer_size, + &buffer_free, &buffer_text); + if(track->is_from_stdin) { + if(ret<0 || buffer_size <= buffer_free) { + fprintf(stderr,"\ncdrskin: FATAL : (First track) fifo did not read a single byte from stdin\n"); + return(0); + } + } + + /* Try to obtain ISO 9660 Volume Descriptors and size from fifo. + Not an error if there is no ISO 9660. */ + if(track->iso_fs_descr != NULL) + free(track->iso_fs_descr); + track->iso_fs_descr = NULL; + if(buffer_size - buffer_free >= 64 * 1024) { + ret= burn_fifo_peek_data(track->libburn_fifo, buf, 64 * 1024, 0); + if(ret == 1) { + track->iso_fs_descr = calloc(1, bs); + if(track->iso_fs_descr == NULL) + return(-1); + memcpy(track->iso_fs_descr, buf + bs, bs); + ret= Scan_for_iso_size((unsigned char *) buf + bs, &data_image_size, 0); + if(ret > 0) + track->data_image_size= data_image_size; + } + } + return(1); +} + +#endif /* Cdrskin_use_libburn_fifO */ + + +#ifdef Cdrskin_no_cdrfifO + +int Cdrtrack_fill_fifo(struct CdrtracK *track, int fifo_start_at, int flag) +{ + return(Cdrtrack_fill_libburn_fifo(track, fifo_start_at, 0)); +} + +#else /* Cdrskin_no_cdrfifO */ + +int Cdrtrack_fill_fifo(struct CdrtracK *track, int fifo_start_at, int flag) +{ + int ret,buffer_fill,buffer_space; + double data_image_size; + + if(fifo_start_at==0) + return(2); + if(track->fifo==NULL) { +#ifdef Cdrskin_use_libburn_fifO + ret= Cdrtrack_fill_libburn_fifo(track, fifo_start_at, 0); + return(ret); +#else + return(2); +#endif + } + 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_no_cdrfifO */ +#endif /* ! Cdrskin_extra_leaN */ + + +int Cdrtrack_set_indice(struct CdrtracK *track, int flag) +{ + int idx= 1, adr, prev_adr= -1, ret; + char *cpt, *ept; + + if(track->sao_pregap >= 0) { + ret= burn_track_set_pregap_size(track->libburn_track, track->sao_pregap, 0); + if(ret <= 0) + return(ret); + } + if(track->sao_postgap >= 0) { + ret= burn_track_set_postgap_size(track->libburn_track, track->sao_postgap, + 0); + if(ret <= 0) + return(ret); + } + + if(track->index_string == NULL) + return(2); + + for(ept= cpt= track->index_string; ept != NULL; cpt= ept + 1) { + ept= strchr(cpt, ','); + if(ept != NULL) + *ept= 0; + adr= -1; + sscanf(cpt, "%d", &adr); + if(adr < 0) { + fprintf(stderr, "cdrskin: SORRY : Bad address number with index=\n"); + return(0); + } + if(idx == 1 && adr != 0) { + fprintf(stderr, + "cdrskin: SORRY : First address number of index= is not 0\n"); + return(0); + } + if(idx > 1 && adr < prev_adr) { + fprintf(stderr, + "cdrskin: SORRY : Backward address number with index=\n"); + return(0); + } + ret= burn_track_set_index(track->libburn_track, idx, adr, 0); + if(ret <= 0) + return(ret); + prev_adr= adr; + if(ept != NULL) + *ept= ','; + idx++; + } + + return(1); +} + +/** 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) + bit2= permission to use O_DIRECT (if enabled at compile time) +*/ +{ + struct burn_track *tr; + struct burn_source *src= NULL, *fd_src= NULL; + double padding,lib_padding; + int ret,sector_pad_up; + double fixed_size; + int source_fd; + + track->trackno= trackno; + tr= burn_track_create(); + if(tr == NULL) + {ret= -1; goto ex;} + track->libburn_track= tr; + track->libburn_track_is_own= 1; + + /* Note: track->track_type may get set in here */ + if(track->source_fd==-1) { + ret= Cdrtrack_open_source_path(track, &source_fd, flag & (4 | 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 | track->mode_modifiers); + 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)); + if(!(track->cdxa_conversion & (1 << 31))) + burn_track_set_cdxa_conv(tr, track->cdxa_conversion & 0x7fffffff); + + 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); + +#ifdef Cdrskin_use_libburn_fifO + + if(src != NULL && track->fifo == NULL) { + int fifo_enabled, fifo_size, fifo_start_at, chunksize, chunks; + int Cdrskin_get_fifo_par(struct CdrskiN *skin, int *fifo_enabled, + int *fifo_size, int *fifo_start_at, int flag); + + Cdrskin_get_fifo_par(track->boss, &fifo_enabled, &fifo_size, &fifo_start_at, + 0); + + if(track->track_type == BURN_AUDIO) + chunksize= 2352; + else if (track->cdxa_conversion == 1) + chunksize= 2056; + else + chunksize= 2048; + chunks= fifo_size / chunksize; + if(chunks > 1 && fifo_enabled) { + fd_src= src; + src= burn_fifo_source_new(fd_src, chunksize, chunks, + (chunksize * chunks >= 128 * 1024)); + if((flag & 1) || src == NULL) + fprintf(stderr, "cdrskin_DEBUG: %s libburn fifo of %d bytes\n", + src != NULL ? "installed" : "failed to install", + chunksize * chunks); + track->libburn_fifo= src; + if(src == NULL) { + src= fd_src; + fd_src= NULL; + } + } + } + +#endif /* Cdrskin_use_libburn_fifO */ + + 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;} + } + ret= Cdrtrack_set_indice(track, 0); + if(ret <= 0) + goto ex; + + burn_session_add_track(session,tr,BURN_POS_END); + ret= 1; +ex: + if(fd_src!=NULL) + burn_source_free(fd_src); + 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); + if(track->libburn_track_is_own) + 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); +} + + +int Cdrtrack_get_sectors(struct CdrtracK *track, int flag) +{ + return(burn_track_get_sectors(track->libburn_track)); +} + + +#ifndef Cdrskin_no_cdrfifO + +/** 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_no_cdrfifO */ + + +/* --------------------------------------------------------------------- */ + +/** 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 to omit bus scanning */ + int do_not_scan; + + /** 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; + o->old_pseudo_scsi_adr= 0; + o->do_not_scan= 0; + 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); + burn_msgs_set_severities(preskin->queue_severity, preskin->print_severity, + "cdrskin: "); + return(1); +} + + +int Cdrpreskin_initialize_lib(struct CdrpreskiN *preskin, int flag) +{ + int ret, major, minor, micro; + + /* Needed are at least 44 bits in signed type off_t . + This is a popular mistake in configuration or compilation. + */ + if(sizeof(off_t) < 6) { + fprintf(stderr, +"\ncdrskin: FATAL : Compile time misconfiguration. sizeof(off_t) too small.\n" + ); + return(0); + } + +/* This is the minimum requirement of cdrskin towards the libburn header + at compile time. + It gets compared against the version macros in libburn/libburn.h : + burn_header_version_major + burn_header_version_minor + burn_header_version_micro + If the header is too old then the following code shall cause failure of + cdrskin compilation rather than to allow production of a program with + unpredictable bugs or memory corruption. + The compiler message supposed to appear in this case is: + error: 'LIBBURN_MISCONFIGURATION' undeclared (first use in this function) + error: 'INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libburn_dot_h_TOO_OLD__SEE_cdrskin_dot_c' undeclared (first use in this function) + error: 'LIBBURN_MISCONFIGURATION_' undeclared (first use in this function) +*/ + +/* The indendation is an advise of man gcc to help old compilers ignoring */ + #if Cdrskin_libburn_majoR > burn_header_version_major + #define Cdrskin_libburn_dot_h_too_olD 1 + #endif + #if Cdrskin_libburn_majoR == burn_header_version_major && Cdrskin_libburn_minoR > burn_header_version_minor + #define Cdrskin_libburn_dot_h_too_olD 1 + #endif + #if Cdrskin_libburn_minoR == burn_header_version_minor && Cdrskin_libburn_micrO > burn_header_version_micro + #define Cdrskin_libburn_dot_h_too_olD 1 + #endif + +#ifdef Cdrskin_libburn_dot_h_too_olD +LIBBURN_MISCONFIGURATION = 0; +INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libburn_dot_h_TOO_OLD__SEE_cdrskin_dot_c = 0; +LIBBURN_MISCONFIGURATION_ = 0; +#endif + + ret= burn_initialize(); + if(ret==0) { + fprintf(stderr,"cdrskin: FATAL : Initialization of libburn failed\n"); + return(0); + } + + /* This is the runtime check towards eventual dynamically linked libburn. + cdrskin deliberately does not to allow the library to be older than + the header file which was seen at compile time. More liberal would be + to use here Cdrskin_libburn_* instead of burn_header_version_* . + */ + burn_version(&major, &minor, µ); + 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 */ + + 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(preskin->fallback_program[0] == 0) + return(1); + 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; ifallback_program); + fprintf(stderr,"cdrskin: errno=%d \"%s\"\n", + errno, (errno > 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)); + + } 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); + } + } + } + } + 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], *argpt; + +#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 || + strncmp(argv[1], "textfile_to_v07t=", 17) == 0 || + strncmp(argv[1], "-textfile_to_v07t=", 18) == 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;i 3) + argpt++; + + if(strcmp(argv[i],"--abort_handler")==0) { + o->abort_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(argpt, "blank=help") == 0 || + strcmp(argpt, "-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, + "\tas_needed\tblank or format media to make it ready for (re-)use\n"); + fprintf(stderr, + "\tdeformat_sequential\t\tfully blank, even formatted DVD-RW\n"); + fprintf(stderr, + "\tdeformat_sequential_quickest\tminimally blank, even DVD-RW\n"); + fprintf(stderr, + "\tformat_if_needed\t\tmake overwriteable if needed and possible\n"); + fprintf(stderr, + "\tformat_overwrite\t\tformat a DVD-RW to \"Restricted Overwrite\"\n"); + fprintf(stderr, + "\tformat_overwrite_quickest\tto \"Restricted Overwrite intermediate\"\n"); + fprintf(stderr, + "\tformat_overwrite_full\t\tfull-size format a DVD-RW or DVD+RW\n"); + fprintf(stderr, + "\tformat_defectmgt[_max|_min|_none]\tformat DVD-RAM or BD-R[E]\n"); + fprintf(stderr, + "\tformat_defectmgt[_cert_on|_cert_off]\tcertification slow|quick\n"); + fprintf(stderr, + "\tformat_defectmgt_payload_\tformat DVD-RAM or BD-R[E]\n"); + fprintf(stderr, + "\tformat_by_index_\t\tformat by index from --list_formats\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 || + strcmp(argv[i],"--device_links") == 0) { + 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(argpt, "-dev=", 5) == 0) { + value_pt= argpt + 5; + goto set_dev; + } else if(strncmp(argpt, "dev=", 4) == 0) { + value_pt= argpt + 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(argpt,"driveropts=help")==0 || + strcmp(argpt,"-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 or DVD via libburn.\n"); + printf("For the cdrecord compatible options which control the work of\n"); + printf( + "blanking and burning see output of option -help rather than --help.\n"); + printf("Non-cdrecord options:\n"); + printf(" --abort_handler do not leave the drive in busy state\n"); + printf( + " --adjust_speed_to_drive set only speeds offered by drive and media\n"); + 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( + " cd_start_tno= set number of first track in CD SAO session\n"); + printf( + " --cdtext_dummy show CD-TEXT pack array instead of writing CD\n"); + printf( + " cdtext_to_textfile= extract CD-TEXT from CD to disk file\n"); + printf( + " cdtext_to_v07t= report CD-TEXT from CD to file or stdout\n"); + printf(" --cdtext_verbose show CD-TEXT pack array before writing CD\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(" --device_links list accessible devices by (udev) links\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"); + printf(" dvd_obs=\"default\"|number\n"); + printf( + " set number of bytes per DVD/BD write: 32k or 64k\n"); + printf(" extract_audio_to=\n"); + printf( + " Copy all tracks of an audio CD as separate\n"); + printf( + " .WAV files /track.wav to hard disk\n"); + printf(" extract_basename=\n"); + printf( + " Compose track files as /.wav\n"); + printf( + " --extract_dap Enable flaw obscuring mechanisms for audio reading\n"); + printf(" extract_tracks=\n"); + printf( + " Restrict extract_audio_to= to list of tracks.\n"); + 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"); + printf(" --fill_up_media cause the last track to have maximum size\n"); + printf(" --four_channel indicate that audio tracks have 4 channels\n"); + 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(" input_sheet_v07t= read a Sony CD-TEXT definition file\n"); + printf(" --list_formats list format descriptors for loaded media.\n"); + printf(" --list_ignored_options list all ignored cdrecord options.\n"); + printf(" --list_speeds list speed descriptors for loaded media.\n"); + printf(" --long_toc print overview of media content\n"); + printf(" modesty_on_drive= no writing into full drive buffer\n"); + printf(" --no_abort_handler exit even if the drive is in busy state\n"); + printf(" --no_blank_appendable refuse to blank appendable CD-RW\n"); + printf(" --no_convert_fs_adr only literal translations of dev=\n"); + printf(" --no_load do not try to load the drive tray\n"); + printf( + " --no_rc as first argument: do not read startup files\n"); + printf(" --obs_pad pad DVD DAO to full 16 or 32 blocks\n"); + printf(" --old_pseudo_scsi_adr use and report literal Bus,Target,Lun\n"); + printf(" rather than real SCSI and pseudo ATA.\n"); + printf( + " --pacifier_with_newline do not overwrite pacifier line by next one.\n"); + printf(" --prodvd_cli_compatible react on some DVD types more like\n"); + printf(" cdrecord-ProDVD with blank= and -multi\n"); + printf(" sao_postgap=\"off\"|number\n"); + printf(" defines whether the next track will get a\n"); + printf( + " post-gap and how many sectors it shall have\n"); + printf(" sao_pregap=\"off\"|number\n"); + printf(" defines whether the next track will get a\n"); + printf( + " pre-gap and how many sectors it shall have\n"); + printf( + " --single_track accept only last argument as source_address\n"); + printf(" stream_recording=\"on\"|\"off\"|number\n"); + printf( + " \"on\" requests to prefer speed over write\n"); + printf( + " error management. A number prevents this with\n"); + printf( + " byte addresses below that number.\n"); + printf(" stdio_sync=\"default\"|\"off\"|number\n"); + printf( + " set number of bytes after which to force output\n"); + printf( + " to drives with prefix \"stdio:\".\n"); + 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"); + printf( + " --tell_media_space print maximum number of writeable data blocks\n"); + printf(" textfile_to_v07t=file_path\n"); + printf( + " print CD-TEXT file in Sony format and exit.\n"); + printf(" --two_channel indicate that audio tracks have 2 channels\n"); + printf( + " write_start_address= write to given byte address (DVD+RW)\n"); + printf( + " --xa1-ignore with -xa1 do not strip 8 byte headers\n"); + printf( + " -xamix DO NOT USE (Dummy announcement to make K3B use -xa)\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 general verbose level by one\n"); + fprintf(stderr, + "\t-V\t\tincrement SCSI command transport 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"); + fprintf(stderr,"\t-minfo\t\tretrieve and print media information/status\n"); + fprintf(stderr,"\t-media-info\tretrieve and print media information/status\n"); + 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"); + fprintf(stderr,"\t-toc\t\tretrieve and print TOC/PMA data\n"); + fprintf(stderr, + "\t-atip\t\tretrieve media state, print \"Is *erasable\"\n"); + fprintf(stderr, + "\tminbuf=percent\tset lower limit for drive buffer modesty\n"); + fprintf(stderr, + "\t-multi\t\tgenerate a TOC that allows multi session\n"); + 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"); + fprintf(stderr,"\t-tao\t\tWrite disk in TAO mode.\n"); + fprintf(stderr,"\t-dao\t\tWrite disk in SAO mode.\n"); + fprintf(stderr,"\t-sao\t\tWrite disk in SAO mode.\n"); + +#ifndef Cdrskin_disable_raw96R + fprintf(stderr,"\t-raw96r\t\tWrite disk in RAW/RAW96R mode\n"); +#endif + + fprintf(stderr,"\ttsize=#\t\tannounces exact size of source data\n"); + fprintf(stderr,"\tpadsize=#\tAmount of padding\n"); + fprintf(stderr, + "\tmcn=text\tSet the media catalog number for this CD to 'text'\n"); + fprintf(stderr, + "\tisrc=text\tSet the ISRC number for the next track to 'text'\n"); + fprintf(stderr, + "\tindex=list\tSet the index list for the next track to 'list'\n"); + fprintf(stderr, + "\t-text\t\tWrite CD-Text from information from *.cue files\n"); + fprintf(stderr, + "\ttextfile=name\tSet the file with CD-Text data to 'name'\n"); + fprintf(stderr, + "\tcuefile=name\tSet the file with CDRWIN CUE data to 'name'\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-xa1\t\tSubsequent tracks are CD-ROM XA mode 2 form 1 - 2056 bytes\n"); + fprintf(stderr, + "\t-isosize\tUse iso9660 file system size for next data track\n"); + fprintf(stderr,"\t-preemp\t\tAudio tracks are mastered with 50/15 microseconds preemphasis\n"); + fprintf(stderr,"\t-nopreemp\tAudio tracks are mastered with no preemphasis (default)\n"); + fprintf(stderr,"\t-copy\t\tAudio tracks have unlimited copy permission\n"); + fprintf(stderr,"\t-nocopy\t\tAudio tracks may only be copied once for personal use (default)\n"); + fprintf(stderr,"\t-scms\t\tAudio tracks will not have any copy permission at all\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"); + +#ifndef Cdrskin_disable_raw96R + } else if(strcmp(argpt,"-raw96r")==0) { + strcpy(o->write_mode_name,"RAW/RAW96R"); +#endif + + } else if(strcmp(argpt,"-sao")==0 || strcmp(argpt,"-dao")==0) { + strcpy(o->write_mode_name,"SAO"); + + } else if(strcmp(argpt,"-scanbus")==0) { + o->no_whitelist= 1; + + } else if(strcmp(argpt,"-tao")==0) { + strcpy(o->write_mode_name,"TAO"); + + } else if(strncmp(argpt, "-textfile_to_v07t=", 18) == 0) { + o->do_not_scan= 1; + } else if(strncmp(argpt ,"textfile_to_v07t=", 17) == 0) { + o->do_not_scan= 1; + + } else if(strcmp(argv[i],"-V")==0 || strcmp(argpt, "-Verbose") == 0) { + burn_set_scsi_logging(2 | 4); /* log SCSI to stderr */ + + } else if(strcmp(argv[i],"-v")==0 || strcmp(argpt, "-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(o->verbosity >= Cdrskin_verbose_progresS) + Cdrpreskin_set_severities(o, "NEVER", "UPDATE", 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(argpt,"-version")==0) { + int major, minor, micro; + + printf( +"Cdrecord 2.01a27 Emulation. Copyright (C) 2006-2014, see libburnia-project.org\n"); + 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 */ + } + printf("System adapter : %s\n", burn_scsi_transport_id(0)); + printf("libburn interface : %d.%d.%d\n", + burn_header_version_major, burn_header_version_minor, + burn_header_version_micro); + 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); + {ret= 2; goto ex;} + + } else if(strcmp(argpt,"-waiti")==0) { + o->do_waiti= 1; + + } else if(strcmp(argpt,"-xamix")==0) { + fprintf(stderr, + "cdrskin: FATAL : Option -xamix not implemented and data not yet convertible to other modes.\n"); + return(0); + } + + } + ret= 1; +final_checks:; + if(flag&1) + goto ex; + if(o->verbosity >= Cdrskin_verbose_debuG) + ClN(fprintf(stderr, + "cdrskin: DEBUG : Using %s\n", burn_scsi_transport_id(0))); + 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"); + } + burn_preset_device_open(o->drive_exclusive + | (o->drive_scsi_dev_family<<2) + | ((!!o->drive_fcntl_f_setlk)<<5), + o->drive_blocking, + o->abort_on_busy_drive); + + 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; + } + } + + 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); + } + } + + } + + burn_allow_untested_profiles(!!o->allow_untested_media); + + /* 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: + - a possibilty to implement cdrskin -reset +*/ + + +#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 BD payload speed factor for reporting progress: 1x */ +static double Cdrskin_bd_speed_factoR= 4495625; + +/** 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; +static double Cdrskin_libburn_bd_speed_factoR= 4495.625; + +/* 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_bd_speed_addoN= 1.0; +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; + int pacifier_with_newline; + double x_speed; + int adjust_speed_to_drive; + int gracetime; + int dummy_mode; + int force_is_set; + int stream_recording_is_set; /* see burn_write_opts_set_stream_recording() */ + int dvd_obs; /* DVD write chunk size: 0, 32k or 64k */ + int obs_pad; /* Whether to force obs end padding */ + int stdio_sync; /* stdio fsync interval: -1, 0, >=32 */ + int single_track; + int prodvd_cli_compatible; + + int do_devices; /* 1= --devices , 2= --device_links */ + + int do_scanbus; + + int do_load; /* 1= -load , 2= -lock , -1= --no_load */ + + int do_checkdrive; + + int do_msinfo; + char msifile[Cdrskin_strleN]; + + int do_atip; + int do_list_speeds; + int do_list_formats; + int do_cdtext_to_textfile; + char cdtext_to_textfile_path[Cdrskin_strleN]; + int do_cdtext_to_vt07; + char cdtext_to_vt07_path[Cdrskin_strleN]; + int do_extract_audio; + char extract_audio_dir[Cdrskin_strleN]; + char extract_basename[249]; + char extract_audio_tracks[100]; /* if [i] > 0 : extract track i */ + int extract_flags; /* bit3 = for flag bit3 of + burn_drive_extract_audio() + */ + +#ifdef Libburn_develop_quality_scaN + int do_qcheck; /* 0= no , 1=nec_optiarc_rep_err_rate */ +#endif /* Libburn_develop_quality_scaN */ + + int do_blank; + int blank_fast; + int no_blank_appendable; + int blank_format_type; /* bit0-7: job type + 0=blank + 1=format_overwrite for DVD+RW, DVD-RW + 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 + bit11= - reserved - + bit12= - reserved - + bit13= try to disable eventual defect management + bit14= - reserved - + bit15= format by index + 2=deformat_sequential (blank_fast might matter) + 3=format (= format_overwrite restricted to DVD+RW) + 4=format_defectmgt for DVD-RAM, BD-R[E] + bit8-15: bit0-7 of burn_disc_format(flag) + bit8 = write zeros after formatting + bit9+10: size mode + 0 = use parameter size as far as it makes sense + 1 = (identical to size mode 0) + 2 = without bit7: format to maximum size + with bit7 : take size from indexed format + descriptor + 3 = without bit7: format to default size + with bit7 : take size from indexed format + descriptor + bit11= - reserved - + bit12= - reserved - + bit13= try to disable defect management + bit14= - reserved - + bit15= format by index + 5=format_by_index + gets mapped to 4 with DVD-RAM and BD-RE else to 1, + bit15 should be set and bit16-23 should contain + a usable index number + 6=format_if_needed + gets mapped to default variants of specialized + formats if the media state requires formatting + before writing + 7=if_needed + gets mapped to 6 for DVD-RAM and BD-RE, + to 0 with all other non-blanks + + bit8-15: bit0-7 of burn_disc_format(flag) + depending on job type + */ + int blank_format_index; /* bit8-15 of burn_disc_format(flag) */ + double blank_format_size; /* to be used with burn_disc_format() */ + int blank_format_no_certify; + + 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 TAO). See libburn. + Controled by preskin->write_mode_name */ + enum burn_write_types write_type; + int block_type; + int multi; + int cdxa_conversion; /* bit0-30: for burn_track_set_cdxa_conv() + bit31 : ignore bits 0 to 30 + */ + 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; + int track_modemods; + + /** CDRWIN cue sheet file */ + char cuefile[Cdrskin_adrleN]; + int use_cdtext; + + /** CD-TEXT */ + unsigned char *text_packs; + int num_text_packs; + + int sheet_v07t_blocks; + char sheet_v07t_paths[8][Cdrskin_adrleN]; + + int cdtext_test; + + /* Media Catalog Number and ISRC */ + char mcn[14]; + char next_isrc[13]; + + char *index_string; + + int sao_pregap; + int sao_postgap; + + /** 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; + + /** The number of the first track in a CD SAO session */ + int cd_start_tno; + + 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; + struct burn_source *cuefile_fifo; + + /** 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; + struct burn_drive *grabbed_drive; + /* Whether drive was told to do something cancel-worthy + 0= no: directly call burn_abort + 1= yes: A worker thread is busy (e.g. writing, formatting) + 2= yes: A synchronous operation is busy (e.g. grabbing). + */ + int drive_is_busy; + +#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->pacifier_with_newline= 0; + o->x_speed= -1.0; + o->adjust_speed_to_drive= 0; + o->gracetime= 0; + o->dummy_mode= 0; + o->force_is_set= 0; + o->stream_recording_is_set= 0; + o->dvd_obs= 0; + o->obs_pad= 0; + o->stdio_sync= 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_list_speeds= 0; + o->do_list_formats= 0; + o->do_cdtext_to_textfile= 0; + o->cdtext_to_textfile_path[0]= 0; + o->do_cdtext_to_vt07= 0; + o->cdtext_to_vt07_path[0]= 0; + o->do_extract_audio= 0; + o->extract_audio_dir[0]= 0; + o->extract_basename[0]= 0; + for(i= 0; i < 100; i++) + o->extract_audio_tracks[i]= 0; + o->extract_flags= 0; + +#ifdef Libburn_develop_quality_scaN + o->do_qcheck= 0; +#endif /* Libburn_develop_quality_scaN */ + + o->do_blank= 0; + o->blank_fast= 0; + o->no_blank_appendable= 0; + o->blank_format_type= 0; + o->blank_format_index= -1; + o->blank_format_size= 0.0; + o->blank_format_no_certify= 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->cdxa_conversion= 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->cuefile[0] = 0; + o->use_cdtext = 0; + o->text_packs= NULL; + o->num_text_packs= 0; + o->sheet_v07t_blocks= 0; + for(i= 0; i < 8; i++) + memset(o->sheet_v07t_paths, 0, Cdrskin_adrleN); + o->cdtext_test= 0; + o->mcn[0]= 0; + o->next_isrc[0]= 0; + o->index_string= NULL; + o->sao_pregap= -1; + o->sao_postgap= -1; + o->track_modemods= 0; + o->track_type_by_default= 1; + for(i=0;itracklist[i]= NULL; + o->track_counter= 0; + o->supposed_track_idx= -1; + o->cd_start_tno= 0; + 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->cuefile_fifo= NULL; + 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); + if(skin->index_string != NULL) + free(skin->index_string); + +#ifndef Cdrskin_extra_leaN + Cdradrtrn_destroy(&(skin->adr_trn),0); +#endif /* ! Cdrskin_extra_leaN */ + + if(skin->cuefile_fifo != NULL) + burn_source_free(skin->cuefile_fifo); + if(skin->text_packs != NULL) + free(skin->text_packs); + Cdrpreskin_destroy(&(skin->preskin),0); + if(skin->drives!=NULL) + burn_drive_info_free(skin->drives); + free((char *) skin); + *o= NULL; + return(1); +} + + +int Cdrskin_assert_driveno(struct CdrskiN *skin, int flag) +{ + if(skin->driveno < 0 || (unsigned int) skin->driveno >= skin->n_drives) { + fprintf(stderr, + "cdrskin: FATAL : No drive found. Cannot perform desired operation.\n"); + return(0); + } + 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) +{ + if(skin->driveno < 0 || (unsigned int) skin->driveno >= skin->n_drives) + return(0); + 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 || (unsigned int) 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 *mode_mods, + int *swap_audio_bytes, int *cdxa_conversion, 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; + *mode_mods= skin->track_modemods; + *swap_audio_bytes= skin->swap_audio_bytes; + *cdxa_conversion= skin->cdxa_conversion; + 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); +} + + +#ifndef Cdrskin_no_cdrfifO + +/** 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; + +#ifdef Cdrskin_use_libburn_fifO + + int profile_number; + char profile_name[80]; + + ret= Cdrskin_assert_driveno(skin, 0); + if(ret <= 0) + return(ret); + + /* Refuse here and thus use libburn fifo only with single track, non-CD */ + ret= burn_disc_get_profile(skin->drives[skin->driveno].drive, + &profile_number, profile_name); + if(profile_number != 0x09 && profile_number != 0x0a && + skin->track_counter == 1) + return(1); + +#endif /* Cdrskin_use_libburn_fifO */ + + 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); +} + +#endif /* ! Cdrskin_no_cdrfifO */ + + +/** 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. */ + k_speed= -1; + } 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)); + + 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; + } + } + burn_drive_set_speed(skin->drives[skin->driveno].drive,k_speed,k_speed); + 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); + return(1); +} + + +int Cdrskin_determine_media_caps(struct CdrskiN *skin, int flag) +{ + 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) + goto ex; + skin->media_is_overwriteable= !!caps->start_adr; + skin->media_does_multi= !!caps->multi_session; + ret= 1; +ex:; + if(caps != NULL) + burn_disc_free_multi_caps(&caps); + return(ret); +} + + +int Cdrskin__is_aborting(int flag) +{ + if(Cdrskin_abort_leveL) + return(-1); + return(burn_is_aborting(0)); +} + + +int Cdrskin_abort(struct CdrskiN *skin, int flag) +{ + int ret; + + Cdrskin_abort_leveL= 1; + 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"); + } else { + 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"); + fprintf(stderr,"\n"); + exit(1); +} + + +/** Obtain access to a libburn drive for writing or information retrieval. + If libburn is not restricted to a single persistent address then the + unused drives are dropped. This might be done by shutting down and + restartiing libburn with the wanted drive only. Thus, after this call, + libburn is supposed to have open only the reserved drive. + All other drives should be free for other use. + Warning: Do not store struct burn_drive pointer over this call. + Any such pointer might be invalid afterwards. + @param flag Bitfield for control purposes: + bit0= bus is unscanned, device is known, + use burn_drive_scan_and_grab() + bit1= do not load drive tray + bit2= do not issue error message on failure + bit3= demand and evtl. report media, return 0 if none to see + bit4= grab drive with unsuitable media even if fallback program + bit5= do not re-assess the drive if it is already grabbed + but rather perform a release-and-grab cycle + @return <=0 error, 1 success +*/ +int Cdrskin_grab_drive(struct CdrskiN *skin, int flag) +{ + int ret,i,profile_number, mem; + struct burn_drive *drive; + char profile_name[80]; + enum burn_disc_status s; + + if(skin->drive_is_grabbed && (flag & 32)) + Cdrskin_release_drive(skin,0); + + if(!skin->drive_is_grabbed) { + if(flag&1) { + skin->driveno= 0; + drive= NULL; + skin->grabbed_drive= drive; + } else { + ret= Cdrskin_assert_driveno(skin, 0); + if(ret <= 0) + return(ret); + drive= skin->drives[skin->driveno].drive; + skin->grabbed_drive= drive; + } + } + if(skin->drive_is_grabbed) { + drive= skin->grabbed_drive; + mem= skin->drive_is_busy; + skin->drive_is_busy= 2; + ret= burn_drive_re_assess(skin->grabbed_drive, 0); + skin->drive_is_busy= mem; + if(Cdrskin__is_aborting(0)) { + fprintf(stderr,"cdrskin: ABORT : Drive re-assessment aborted\n"); + Cdrskin_abort(skin, 0); /* Never comes back */ + } + if(ret <= 0) { + if(!(flag&4)) + fprintf(stderr,"cdrskin: FATAL : unable to re-assess drive '%s'\n", + skin->preskin->device_adr); + goto ex; + } + } else if(flag&1) { + mem= skin->drive_is_busy; + skin->drive_is_busy= 2; + ret= burn_drive_scan_and_grab(&(skin->drives),skin->preskin->device_adr, + (skin->do_load != -1) && !(flag&2)); + skin->drive_is_busy= mem; + if(Cdrskin__is_aborting(0)) { +aborted:; + fprintf(stderr,"cdrskin: ABORT : Drive aquiration aborted\n"); + Cdrskin_abort(skin, 0); /* Never comes back */ + } + 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; i < (int) skin->n_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); + } + } + + mem= skin->drive_is_busy; + skin->drive_is_busy= 2; + ret= burn_drive_grab(drive,(skin->do_load != -1) && !(flag&2)); + skin->drive_is_busy= mem; + if(Cdrskin__is_aborting(0)) + goto aborted; + if(ret==0) { + if(!(flag&4)) + fprintf(stderr,"cdrskin: FATAL : unable to open drive %d\n", + skin->driveno); + goto ex; + } + } + 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; + 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; + } else if(strstr(profile_name,"BD")==profile_name) { + Cdrskin_speed_factoR= Cdrskin_bd_speed_factoR; + Cdrskin_libburn_speed_factoR= Cdrskin_libburn_bd_speed_factoR; + Cdrskin_libburn_speed_addoN= Cdrskin_libburn_bd_speed_addoN; + } + } + 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:; + 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) +{ + struct burn_progress p; + enum burn_drive_status drive_status= BURN_DRIVE_SPAWNING; + +/* + fprintf(stderr, +"cdrskin_DEBUG: Cdrskin_abort_handler: signum=%d, flag=%d, drive_is_busy=%d\n", + signum, flag, skin->drive_is_busy); +*/ + + if(skin->drive_is_busy == 0) + Cdrskin_abort(skin, 0); /* Never comes back */ + + if(getpid()!=skin->control_pid) { + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr, + "\ncdrskin_debug: ABORT : [%lu] Thread rejected: pid=%lu, signum=%lu\n", + (unsigned long int) skin->control_pid, (unsigned long int) getpid(), + (unsigned long int) 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_no_cdrfifO + if(skin->fifo!=NULL) + Cdrfifo_close_all(skin->fifo,0); +#endif + + /* 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"); + } + + Cdrskin_abort_leveL= -1; + if (!(flag & 1)) { + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin: ABORT : Calling burn_abort(-1)\n")); + burn_abort(-1, burn_abort_pacifier, "cdrskin: "); + } + fprintf(stderr, + "cdrskin: ABORT : Urged drive worker threads to do emergency halt.\n"); + return(-2); +} + + +/** 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; i < (int) skin->n_drives; i++) { + ret= burn_drive_get_adr(&(skin->drives[i]), adr); + if(ret<=0) + continue; + 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((unsigned int) (*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 > (int) skin->n_drives) + goto fallback; + + ret= burn_drive_get_adr(&(skin->drives[driveno]), adr); + if(ret<=0) + goto fallback; + loc= adr; + ret= burn_drive_get_drive_role(skin->drives[driveno].drive); + if(ret!=1) { + sprintf(btldev,"stdio:%s",adr); + {ret= 2; goto adr_translation;} + } + + 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; + } + } + + 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) +{ + 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); +} + + +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 + bit2= do not report but only check for pseudo appendable + @return 1=ok, 2=ok, is pseudo appendable, <=0 error +*/ +int Cdrskin_report_disc_status(struct CdrskiN *skin, enum burn_disc_status s, + int flag) +{ + int ret, iso_size, pseudo_appendable= 0; + + 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; + pseudo_appendable= 1; + } + } + } + if(flag&4) + return(1+pseudo_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"); + } 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"); + } else + printf("-unknown status code-\n"); + + if(flag&2) + return(1+pseudo_appendable); + + 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"); + } + + return(1+pseudo_appendable); +} + + +/** Perform operations -scanbus or --devices + @param flag Bitfield for control purposes: + bit0= perform --devices rather than -scanbus + bit1= with bit0: perform --device_links + @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, busidx, max_dev_len, pad, j; + char shellsafe[5*Cdrskin_strleN+2],perms[40],btldev[Cdrskin_adrleN]; + char adr[Cdrskin_adrleN],*raw_dev,*drives_shown= NULL; + char link_adr[BURN_DRIVE_ADR_LEN]; + int *drives_busses= NULL; + struct stat stbuf; + + drives_shown= calloc(1, skin->n_drives+1); + drives_busses= calloc((skin->n_drives+1), sizeof(int)); + if(drives_shown == NULL || drives_busses == NULL) + {ret= -1; goto ex;} + + if(flag&1) { + max_dev_len= 0; + for(i= 0; i < (int) skin->n_drives; i++) { + drives_shown[i]= 0; + ret= burn_drive_get_adr(&(skin->drives[i]), adr); + if(ret<=0) + continue; + if(flag & 2) { + ret= burn_lookup_device_link(adr, link_adr, "/dev", NULL, 0, 0); + if(ret == 1) + strcpy(adr, link_adr); + } + if(strlen(adr)>=Cdrskin_strleN) + Text_shellsafe("failure:oversized string", shellsafe, 0); + else + Text_shellsafe(adr, shellsafe,0); + if((int) strlen(shellsafe) > max_dev_len) + max_dev_len= strlen(shellsafe); + } + printf("cdrskin: Overview of accessible drives (%d found) :\n", + skin->n_drives); + printf("-----------------------------------------------------------------------------\n"); + for(i= 0; i < (int) skin->n_drives; i++) { + ret= burn_drive_get_adr(&(skin->drives[i]), adr); + if(ret<=0) { + /* >>> one should massively complain */; + continue; + } + 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(flag & 2) { + ret= burn_lookup_device_link(adr, link_adr, "/dev", NULL, 0, 0); + if(ret == 1) + strcpy(adr, link_adr); + } + if(strlen(adr)>=Cdrskin_strleN) + Text_shellsafe("failure:oversized string",shellsafe,0); + else + Text_shellsafe(adr,shellsafe,0); + printf("%d dev=%s", i, shellsafe); + pad= max_dev_len - strlen(shellsafe); + if(pad > 0) + for(j= 0; j < pad; j++) + printf(" "); + printf(" %s : '%-8.8s' '%s'\n", + 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(i= 0; i < (int) skin->n_drives; i++) { + drives_busses[i]= -1; + ret= Cdrskin_driveno_to_btldev(skin,i,btldev,1); + if(ret >= pseudo_transport_group && + ret < pseudo_transport_group + 1000000) { + drives_busses[i]= ret - pseudo_transport_group; + if(ret > pseudo_transport_group + busmax) + busmax= 1 + ret - pseudo_transport_group; + } + } + for(busidx= 0; busidx < (int) skin->n_drives + 1; busidx++) { + if(busidx < (int) skin->n_drives) + busno= drives_busses[busidx]; + else + busno= busmax; + if(busno < 0) + continue; + for(i= 0; i < busidx; i++) + if(drives_busses[i] == busno) + break; + if(i < busidx) + continue; + first_on_bus= 1; + for(i= 0; i < (int) skin->n_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); + if(drives_busses!=NULL) + free((char *) drives_busses); + 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 >= (int) 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 : "); + 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==4) + printf("%s\n","Emulated (stdio-drive, 2k random read-only)"); + else if(ret == 5) + printf("%s\n","Emulated (stdio-drive, 2k random write-only)"); + else if(ret!=1) + printf("%s\n","Emulated (stdio-drive)"); + else + 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"); + 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"); + +#ifndef Cdrskin_disable_raw96R + if((drive_info->raw_block_types & BURN_BLOCK_RAW96R) && + strstr(profile_name,"DVD")!=profile_name && + strstr(profile_name,"BD")!=profile_name) + printf(" RAW/RAW96R"); +#endif /* ! Cdrskin_disable_raw96R */ + + printf("\n"); + 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); + } + ret= burn_disc_track_lba_nwa(drive,o,0,&lba,nwa); + if(o!=NULL) + burn_write_opts_free(o); + return(ret); +} + + +/* @param flag bit0-3= source of text packs: + 0= CD Lead-in + 1= session and tracks + 2= textfile= + bit4-7= output format + 0= -vv -toc + 1= Sony CD-TEXT Input Sheet Version 0.7T +*/ +int Cdrskin_print_text_packs(struct CdrskiN *skin, unsigned char *text_packs, + int num_packs, int flag) +{ + int i, j, from, fmt, ret, char_code= 0; + char *from_text= "", *result= NULL; + unsigned char *pack; + + from= flag & 15; + fmt= (flag >> 4) & 15; + if(from == 0) + from_text= " from CD Lead-in"; + else if(from == 1) + from_text= " from session and tracks"; + else if(from == 2) + from_text= " from textfile= or cuefile= CDTEXTFILE"; + if (fmt == 1) { + ret = burn_make_input_sheet_v07t(text_packs, num_packs, 0, 0, &result, + &char_code, 0); + if(ret <= 0) + goto ex; + if(char_code == 0x80) + fprintf(stderr, + "cdrskin : WARNING : Double byte characters encountered.\n"); + for(i = 0; i < ret; i++) + printf("%c", result[i]); + } else { + printf("CD-TEXT data%s:\n", from_text); + for(i= 0; i < num_packs; i++) { + pack= text_packs + 18 * i; + printf("%4d :", i); + for(j= 0; j < 18; j++) { + if(j >= 4 && j <= 15 && pack[j] >= 32 && pack[j] <= 126 && + pack[0] != 0x88 && pack[0] != 0x89 && pack[0] != 0x8f) + printf(" %c", pack[j]); + else + printf(" %2.2x", pack[j]); + } + printf("\n"); + } + } + ret= 1; +ex:; + if(result != NULL) + free(result); + return(ret); +} + + +/* @param flag bit4= no not overwrite existing target file +*/ +int Cdrskin_store_text_packs(struct CdrskiN *skin, unsigned char *text_packs, + int num_packs, char *path, int flag) +{ + int data_length, ret; + struct stat stbuf; + FILE *fp; + unsigned char fake_head[4]; + + if(stat(path, &stbuf) != -1 && (flag & 16)) { + fprintf(stderr, "cdrskin: SORRY : Will not overwrite file '%s'\n", path); + return(0); + } + fp= fopen(path, "w"); + if(fp == NULL) { + fprintf(stderr, "cdrskin: SORRY : Cannot open file '%s' for storing extracted CD-TEXT\n", path); + fprintf(stderr, "cdrskin: %s (errno=%d)\n", strerror(errno), errno); + return(0); + } + data_length= num_packs * 18 + 2; + fake_head[0]= (data_length >> 8) & 0xff; + fake_head[1]= data_length & 0xff; + fake_head[2]= fake_head[3]= 0; + ret= fwrite(fake_head, 4, 1, fp); + if(ret != 1) { +write_failure:; + fprintf(stderr, + "cdrskin: SORRY : Cannot write all data to file '%s'\n", path); + fprintf(stderr, "cdrskin: %s (errno=%d)\n", strerror(errno), errno); + fclose(fp); + return(0); + } + ret= fwrite(text_packs, data_length - 2, 1, fp); + if(ret != 1) + goto write_failure; + fprintf(stderr, + "cdrskin: NOTE : Wrote header and %d CD-TEXT bytes to file '%s'\n", + data_length - 2, path); + fclose(fp); + return(1); +} + + +/* @param flag bit0-3= output format + 1= Sony CD-TEXT Input Sheet Version 0.7T + 15= Cdrskin_store_text_packs + bit4= do not overwrite existing the target file +*/ +int Cdrskin_cdtext_to_file(struct CdrskiN *skin, char *path, int flag) +{ + int ret, fmt, char_code= 0, to_write; + struct burn_drive *drive; + unsigned char *text_packs= NULL; + int num_packs= 0; + char *result= 0; + FILE *fp= NULL; + struct stat stbuf; + + ret= Cdrskin_grab_drive(skin, 0); + if(ret<=0) + goto ex; + + fmt= flag & 15; + drive= skin->drives[skin->driveno].drive; + ret= burn_disc_get_leadin_text(drive, &text_packs, &num_packs, 0); + if(ret <= 0 || num_packs <= 0) { + fprintf(stderr, "cdrskin: No CD-Text or CD-Text unaware drive.\n"); + ret= 2; goto ex; + } + if(fmt == 1) { + ret = burn_make_input_sheet_v07t(text_packs, num_packs, 0, 0, &result, + &char_code, 0); + if(ret <= 0) + goto ex; + to_write= ret; + if(stat(path, &stbuf) != -1 && (flag & 16)) { + fprintf(stderr, "cdrskin: SORRY : Will not overwrite file '%s'\n", path); + return(0); + } + if(strcmp(path, "-") == 0) + fp= stdout; + else + fp = fopen(path, "w"); + if(fp == NULL) { + if(errno > 0) + fprintf(stderr, "cdrskin: %s (errno=%d)\n", strerror(errno), errno); + fprintf(stderr, + "cdrskin: SORRY : Cannot write CD-TEXT list to file '%s'\n", + path); + {ret= 0; goto ex;} + } + ret = fwrite(result, to_write, 1, fp); + if(ret != 1) { + if(errno > 0) + fprintf(stderr, "cdrskin: %s (errno=%d)\n", strerror(errno), errno); + fprintf(stderr, + "cdrskin: SORRY : Cannot write all CD-TEXT to file '%s'\n", path); + ret= 0; + } + if(ret <= 0) + goto ex; + + } else if(fmt == 15) { + if(skin->verbosity >= Cdrskin_verbose_debuG) + Cdrskin_print_text_packs(skin, text_packs, num_packs, 0); + ret= Cdrskin_store_text_packs(skin, text_packs, num_packs, path, flag & 16); + if(ret <= 0) + goto ex; + printf("CD-Text len: %d\n", num_packs * 18 + 4); + + } else { + fprintf(stderr, "cdrskin: FATAL : Program error : Unknow format %d with Cdrskin_cdtext_to_file.\n", fmt); + {ret= -1; goto ex;} + } + ret= 1; +ex:; + if(result != NULL) + free(result); + if(text_packs != NULL) + free(text_packs); + if(fp != NULL && fp != stdout) + fclose(fp); + return(1); +} + + +/** 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, final_ret= 1; + int track_offset = 1, open_sessions= 0, have_real_open_session= 0; + struct burn_drive *drive; + struct burn_disc *disc= NULL; + struct burn_session **sessions; + struct burn_track **tracks; + struct burn_toc_entry toc_entry; + enum burn_disc_status s; + char profile_name[80]; + int profile_number; + + drive= skin->drives[skin->driveno].drive; + + s= burn_disc_get_status(drive); + if(s == BURN_DISC_EMPTY || s == BURN_DISC_BLANK) + goto summary; + 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"); + goto summary; + } + } + goto cannot_read; + } + sessions= burn_disc_get_sessions(disc,&num_sessions); + open_sessions= burn_disc_get_incomplete_sessions(disc); + if(num_sessions > 0) + track_offset = burn_session_get_start_tno(sessions[0], 0); + if(track_offset <= 0) + track_offset= 1; + if(flag&1) { + for(session_no= 0; session_no < num_sessions + open_sessions; + session_no++) { + tracks= burn_session_get_tracks(sessions[session_no],&num_tracks); + total_tracks+= num_tracks; + if(session_no == num_sessions + open_sessions - 1 && open_sessions > 0) { + total_tracks--; /* Do not count invisible track */ + if(num_tracks > 1) + have_real_open_session= 1; + } + } + printf("first: %d last %d\n", + track_offset, total_tracks + track_offset - 1); + } + for(session_no= 0; session_no < num_sessions + open_sessions; session_no++) { + tracks= burn_session_get_tracks(sessions[session_no],&num_tracks); + if(tracks==NULL) + continue; + if(session_no == num_sessions + open_sessions - 1 && open_sessions > 0) + num_tracks--; + if(num_tracks <= 0) + continue; + if(!(flag&1)) + printf("first: %d last: %d\n", + track_count + track_offset, + track_count + num_tracks + track_offset - 1); + for(track_no= 0; track_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_no < num_sessions + open_sessions - 1 + have_real_open_session - 1) + continue; + if(have_real_open_session) { + /* Use start of invisible track */ + burn_track_get_entry(tracks[num_tracks], &toc_entry); + } else { + burn_session_get_leadout_entry(sessions[session_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("track:lout lba: %9d (%9d) %2.2d:%2.2d:%2.2d", + lba,4*lba,pmin,psec,pframe); + printf(" adr: %d control: %d",toc_entry.adr,toc_entry.control); + printf(" mode: -1\n"); + } + + if(skin->verbosity >= Cdrskin_verbose_cmD) { + ret= Cdrskin_cdtext_to_file(skin, "cdtext.dat", 15 | 16); + if(ret <= 0 && ret < final_ret) + final_ret= ret; + } + +summary: + ret= burn_disc_get_profile(drive, &profile_number, profile_name); + if(ret <= 0) + strcpy(profile_name, "media"); + + printf("Media summary: %d sessions, %d tracks, %s %s\n", + num_sessions + open_sessions - 1 + have_real_open_session, track_count, + s==BURN_DISC_BLANK ? "blank" : + s==BURN_DISC_APPENDABLE ? "appendable" : + s==BURN_DISC_FULL ? "closed" : + s==BURN_DISC_EMPTY ? "no " : "unknown ", + profile_name); + + if(have_real_open_session) + printf("Warning : Incomplete session encountered !\n"); + + if(disc!=NULL) + burn_disc_free(disc); + if(s == BURN_DISC_EMPTY) + return(0); + return(final_ret); +cannot_read:; + fprintf(stderr,"cdrecord_emulation: Cannot read TOC header\n"); + fprintf(stderr,"cdrecord_emulation: Cannot read TOC/PMA\n"); + return(0); +} + + +/** Perform -minfo under control of Cdrskin_atip(). + @param flag Bitfield for control purposes: + @return <=0 error, 1 success +*/ +int Cdrskin_minfo(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, size= 0, nwa= 0; + int last_leadout= 0, ovwrt_full= 0, track_offset= 1, open_sessions= 0; + struct burn_drive *drive; + struct burn_disc *disc= NULL; + struct burn_session **sessions= NULL; + struct burn_track **tracks; + struct burn_toc_entry toc_entry; + enum burn_disc_status s, show_status; + char profile_name[80]; + int pno; + char media_class[80]; + int nominal_sessions= 1, ftils= 1, ltils= 1, first_track= 1, read_capacity= 0; + int app_code, cd_info_valid, lra, alloc_blocks, free_blocks; + int have_real_open_session= 0; + off_t avail, buf_count; + char disc_type[80], bar_code[9], buf[2 * 2048], *type_text; + unsigned int disc_id; + + drive= skin->drives[skin->driveno].drive; + + s= burn_disc_get_status(drive); + if(s == BURN_DISC_EMPTY) { + fprintf(stderr, "cdrecord-Emulation: No disk / Wrong disk!\n"); + return(1); + } + ret= burn_disc_get_profile(drive, &pno, profile_name); + if(ret <= 0) { + fprintf(stderr, "cdrskin: SORRY : Cannot inquire current media profile\n"); + return(1); + } + if(pno >= 0x08 && pno <= 0x0a) + strcpy(media_class, "CD"); + else if(pno >= 0x10 && pno <= 0x2f) + strcpy(media_class, "DVD"); + else if(pno >= 0x40 && pno <= 0x43) + strcpy(media_class, "BD"); + else + sprintf(media_class, "Unknown class (profile 0x%4.4X)", pno); + + printf("\n"); + printf("Mounted media class: %s\n", media_class); + printf("Mounted media type: %s\n", profile_name); + ret= burn_disc_erasable(drive); + printf("Disk Is %serasable\n", + (ret || skin->media_is_overwriteable) ? "" : "not "); + show_status= s; + ret = burn_get_read_capacity(drive, &read_capacity, 0); + if(ret <= 0) + read_capacity= 0; + if(skin->media_is_overwriteable && read_capacity > 0) + ovwrt_full= 1; + if(ovwrt_full) + show_status= BURN_DISC_FULL; + printf("disk status: %s\n", + show_status == BURN_DISC_BLANK ? "empty" : + show_status == BURN_DISC_APPENDABLE ? "incomplete/appendable" : + show_status == BURN_DISC_FULL ? "complete" : + "unusable"); + printf("session status: %s\n", + show_status == BURN_DISC_BLANK ? "empty" : + show_status == BURN_DISC_APPENDABLE ? "empty" : + show_status == BURN_DISC_FULL ? "complete" : + "unknown"); + + disc= burn_drive_get_disc(drive); + if(disc==NULL || s == BURN_DISC_BLANK) { + first_track= 1; + num_sessions= 0; + nominal_sessions= 1; + ftils= ltils= 1; + } else { + + sessions= burn_disc_get_sessions(disc, &num_sessions); + open_sessions= burn_disc_get_incomplete_sessions(disc); + if(num_sessions > 0) + track_offset= burn_session_get_start_tno(sessions[0], 0); + if(track_offset <= 0) + track_offset= 1; + first_track= track_offset; + nominal_sessions= num_sessions + open_sessions; + if(s == BURN_DISC_APPENDABLE && open_sessions == 0) + nominal_sessions++; + for(session_no= 0; session_no < num_sessions + open_sessions; + session_no++) { + ftils= total_tracks + 1; + tracks= burn_session_get_tracks(sessions[session_no],&num_tracks); + if(tracks==NULL) + continue; + total_tracks+= num_tracks; + ltils= total_tracks; + if(session_no==0 && burn_session_get_hidefirst(sessions[session_no]) + && total_tracks >= 2) + first_track= 2; + } + if(s == BURN_DISC_APPENDABLE && open_sessions == 0) + ftils= ltils= total_tracks + 1; + } + printf("first track: %d\n", first_track); + printf("number of sessions: %d\n", nominal_sessions); + printf("first track in last sess: %d\n", ftils + track_offset - 1); + printf("last track in last sess: %d\n", ltils + track_offset - 1); + + burn_disc_get_cd_info(drive, disc_type, &disc_id, bar_code, &app_code, + &cd_info_valid); + printf("Disk Is %sunrestricted\n", (cd_info_valid & 16) ? "" : "not "); + if((cd_info_valid & 8) && !(cd_info_valid & 16)) + printf("Disc application code: %d\n", app_code); + if(strcmp(media_class, "DVD") == 0 || strcmp(media_class, "BD") == 0) + printf("Disk type: DVD, HD-DVD or BD\n"); + else if(strcmp(media_class, "CD") == 0 && (cd_info_valid & 1)) + printf("Disk type: %s\n", disc_type); + else + printf("Disk type: unrecognizable\n"); + if(cd_info_valid & 2) + printf("Disk id: 0x%-X\n", disc_id); + ret= burn_disc_get_bd_spare_info(drive, &alloc_blocks, &free_blocks, 0); + if(ret == 1) { + printf("BD Spare Area consumed: %d\n", alloc_blocks - free_blocks); + printf("BD Spare Area available: %d\n", free_blocks); + } + + printf("\n"); + printf("Track Sess Type Start Addr End Addr Size\n"); + printf("==============================================\n"); + for(session_no= 0; session_no < num_sessions + open_sessions; session_no++) { + tracks= burn_session_get_tracks(sessions[session_no],&num_tracks); + if(tracks==NULL) + continue; + for(track_no= 0; track_no= 0x08 && pno <= 0x0a && ((toc_entry.control & 7) >= 4) && + lra >= 2 && size >= 2) { + /* If last two blocks not readable then assume TAO and subtract 2 + from lra and size. + */; + ret= burn_read_data(drive, (off_t) (lra - 1) * (off_t) 2048, buf, + 2 * 2048, &buf_count, 2 | 4); + if(ret <= 0) { + lra-= 2; + size-= 2; + } + } + +#ifdef Cdrskin_with_last_recorded_addresS + + /* Interesting, but obviously not what cdrecord prints as "End Addr" */ + if(toc_entry.extensions_valid & 2) { /* LRA extension valid */ + if(pno == 0x11 || pno == 0x13 || pno == 0x14 || pno == 0x15 || + pno == 0x41 || pno == 0x42 || pno == 0x51) + lra= toc_entry.last_recorded_address; + } + +#endif /* Cdrskin_with_last_recorded_addresS */ + + if(session_no < num_sessions) { + type_text= ((toc_entry.control&7)<4) ? "Audio" : "Data"; + } else { + if(track_no < num_tracks - 1) { + type_text= "Rsrvd"; + have_real_open_session = 1; + } else { + type_text= "Blank"; + } + if(toc_entry.extensions_valid & 4) { + if(toc_entry.track_status_bits & (1 << 14)) + type_text= "Blank"; + else if(toc_entry.track_status_bits & (1 << 16)) { + type_text= "Apdbl"; + have_real_open_session = 1; + } else if(toc_entry.track_status_bits & (1 << 15)) { + type_text= "Rsrvd"; + have_real_open_session = 1; + } else + type_text= "Invsb"; + } + } + printf("%5d %5d %-6s %-10d %-10d %-10d\n", + track_count + track_offset - 1, session_no + 1, + type_text, lba, lra, size); + if(session_no < num_sessions) + last_leadout= lba + size; + } + } + if(last_leadout > 0) + if(read_capacity > last_leadout) + read_capacity= last_leadout; + if(nominal_sessions > num_sessions) { + ret= burn_disc_track_lba_nwa(drive, NULL, 0, &lba, &nwa); + if(ret > 0) { + avail= burn_disc_available_space(drive, NULL); + size= avail / 2048; + if(read_capacity == 0 && skin->media_is_overwriteable) + size= 0; /* unformatted overwriteable media */ + if(nominal_sessions > num_sessions + open_sessions) { + printf("%5d %5d %-6s %-10d %-10d %-10d\n", + track_count + track_offset, nominal_sessions, + ovwrt_full ? "Data" : "Blank", + nwa, nwa + size - 1, size); + } + } + } + printf("\n"); + + if(num_sessions > 0) { + ret= burn_disc_get_msc1(drive, &lba); + if(ret > 0) + printf("Last session start address: %-10d\n", lba); + if(last_leadout > 0) + printf("Last session leadout start address: %-10d\n", last_leadout); + if(s == BURN_DISC_FULL) { + if(read_capacity > 0 && (last_leadout != read_capacity || + pno == 0x08 || pno == 0x10 || pno == 0x40 || pno == 0x50)) + printf("Read capacity: %-10d\n", read_capacity); + } + } else if(ovwrt_full) { + printf("Last session start address: %-10d\n", nwa); + printf("Last session leadout start address: %-10d\n", size); + } + if(nominal_sessions > num_sessions && !skin->media_is_overwriteable) { + printf("Next writable address: %-10d\n", nwa); + printf("Remaining writable size: %-10d\n", size); + } + + if(ovwrt_full) { + printf("\n"); + printf("cdrskin: Media is overwriteable. No blanking needed. No reliable track size.\n"); + printf("cdrskin: Above contrary statements follow cdrecord traditions.\n"); + } + + if(have_real_open_session) + printf("\nWarning: Incomplete session encountered !\n"); + + if(disc!=NULL) + burn_disc_free(disc); + if(s == BURN_DISC_EMPTY) + return(0); + return(1); +} + + +int Cdrskin_print_all_profiles(struct CdrskiN *skin, struct burn_drive *drive, + int flag) +{ + int num_profiles, profiles[64], i, ret; + char is_current[64], profile_name[80]; + + burn_drive_get_all_profiles(drive, &num_profiles, profiles, is_current); + for(i= 0; i < num_profiles; i++) { + ret= burn_obtain_profile_name(profiles[i], profile_name); + if(ret <= 0) + strcpy(profile_name, "unknown"); + printf("Profile: 0x%4.4X (%s)%s\n", (unsigned int) profiles[i], + profile_name, is_current[i] ? " (current)" : ""); + } + return(1); +} + + +/** Perform -atip . + @param flag Bitfield for control purposes: + bit0= perform -toc + bit1= perform -toc with session markers + bit2= perform -minfo + @return <=0 error, 1 success +*/ +int Cdrskin_atip(struct CdrskiN *skin, int flag) +{ + int ret,is_not_really_erasable= 0, current_is_cd= 1; + double x_speed_max= -1.0, x_speed_min= -1.0; + enum burn_disc_status s; + struct burn_drive *drive; + int profile_number= 0; + char profile_name[80], *manuf= NULL, *media_code1= NULL, *media_code2= NULL; + char *book_type= NULL, *product_id= NULL; + + ClN(printf("cdrskin: pseudo-atip on drive %d\n",skin->driveno)); + 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; + + profile_name[0]= 0; + ret= burn_disc_get_profile(drive,&profile_number,profile_name); + if(ret<=0) { + profile_number= 0; + strcpy(profile_name, "-unidentified-"); + } + if(profile_number != 0x08 && profile_number != 0x09 && profile_number != 0x0a) + current_is_cd= 0; + + ret= Cdrskin_checkdrive(skin,profile_name,1); + if(ret<=0) + return(ret); + + 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; + } + } + if(burn_disc_get_status(drive) == BURN_DISC_UNSUITABLE) { + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(profile_name[0]) + printf("Current: %s\n",profile_name); + else + printf("Current: UNSUITABLE MEDIA (Profile %4.4Xh)\n",profile_number); + } + {ret= 0; goto ex;} + } + if(burn_disc_get_status(drive) != BURN_DISC_EMPTY) { + 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(burn_disc_get_status(drive) == BURN_DISC_EMPTY) + printf("Current: none\n"); + else 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"); + Cdrskin_print_all_profiles(skin, drive, 0); + } + + if(burn_disc_get_status(drive) == BURN_DISC_EMPTY) + {ret= 0; goto ex;} + + 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. + The real book type is available meanwhile. But that one is not intended. + */ + 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 if(strstr(profile_name,"BD")==profile_name) { + printf("Mounted Media: %2.2Xh, %s\n", profile_number, profile_name); + } 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"); + } + { int start_lba,end_lba,min,sec,fr; + int m_lo, s_lo, f_lo; + + 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,&m_lo,&s_lo,&f_lo); + printf(" ATIP start of lead out: %d (%-2.2d:%-2.2d/%-2.2d)\n", + end_lba, m_lo, s_lo, f_lo); + if(current_is_cd) + manuf= burn_guess_cd_manufacturer(min, sec, fr, m_lo, s_lo, f_lo, 0); + } + } + printf(" 1T speed low: %.f 1T speed high: %.f\n",x_speed_min,x_speed_max); + } + + ret= burn_disc_get_media_id(drive, &product_id, &media_code1, &media_code2, + &book_type, 0); + if(ret > 0 && (!current_is_cd) && + manuf == NULL && media_code1 != NULL && media_code2 != NULL) { + + manuf= burn_guess_manufacturer(profile_number, media_code1, media_code2, 0); + } + if(product_id != NULL) + printf("Product Id: %s\n", product_id); + if(manuf != NULL) + printf("Producer: %s\n", manuf); + if(skin->verbosity >= Cdrskin_verbose_progresS) { + if (current_is_cd) { + if(manuf != NULL) + printf("Manufacturer: %s\n", manuf); + } else if(product_id != NULL && media_code1 != NULL && media_code2 != NULL){ + free(product_id); + free(media_code1); + free(media_code2); + if(book_type != NULL) + free(book_type); + product_id= media_code1= media_code2= book_type= NULL; + ret= burn_disc_get_media_id(drive, &product_id, &media_code1, + &media_code2, &book_type, 1); + if(ret > 0) { + if(profile_number == 0x11 || profile_number == 0x13 || + profile_number == 0x14 || profile_number == 0x15) + printf("Manufacturer: '%s'\n", media_code1); + else if(profile_number >= 0x40 && profile_number <= 0x43) { + printf("Manufacturer: '%s'\n", media_code1); + if(media_code2[0]) + printf("Media type: '%s'\n", media_code2); + } else { + printf("Manufacturer: '%s'\n", media_code1); + if(media_code2[0]) + printf("Media type: '%s'\n", media_code2); + } + } + } + } + + ret= 1; + if(flag&1) + ret= Cdrskin_toc(skin, !(flag & 2)); + /*cdrecord seems to ignore -toc errors if -atip is ok */ + if(ret > 0 && (flag & 4)) + ret= Cdrskin_minfo(skin, 0); + +ex:; + if(manuf != NULL) + free(manuf); + if(media_code1 != NULL) + free(media_code1); + if(media_code2 != NULL) + free(media_code2); + if(book_type != NULL) + free(book_type); + if(product_id != NULL) + free(product_id); + + return(ret); +} + + +/** Perform --list_formats + @param flag Bitfield for control purposes: + @return <=0 error, 1 success +*/ +int Cdrskin_list_formats(struct CdrskiN *skin, int flag) +{ + struct burn_drive *drive; + int ret, i, status, num_formats, profile_no, type, alloc_blocks, free_blocks; + off_t size; + unsigned dummy; + char status_text[80], profile_name[90]; + + ret= Cdrskin_grab_drive(skin,0); + if(ret<=0) + return(ret); + drive= skin->drives[skin->driveno].drive; + + ret = burn_disc_get_formats(drive, &status, &size, &dummy, + &num_formats); + if(ret <= 0) { + fprintf(stderr, "cdrskin: SORRY: Cannot obtain format list info\n"); + ret= 0; goto ex; + } + ret= burn_disc_get_profile(drive, &profile_no, profile_name); + printf("Media current: "); + 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"); + + 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) { + if(profile_no==0x12 || profile_no==0x13 || profile_no==0x1a || + profile_no==0x43) + sprintf(status_text, "formatted, with %.1f MiB", + ((double) size) / 1024.0 / 1024.0); + else + sprintf(status_text, "written, with %.1f MiB", + ((double) size) / 1024.0 / 1024.0); + } else if(status == BURN_FORMAT_IS_UNKNOWN) { + 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); + ret= burn_disc_get_bd_spare_info(drive, &alloc_blocks, &free_blocks, 0); + if(ret == 1) + printf("BD Spare Area: %d blocks consumed, %d blocks available\n", + alloc_blocks - free_blocks, free_blocks); + + for (i = 0; i < num_formats; i++) { + ret= burn_disc_get_format_descr(drive, i, &type, &size, &dummy); + if (ret <= 0) + continue; + printf("Format idx %-2d: %2.2Xh , %.fs , %.1f MiB\n", + i, type, ((double) size) / 2048.0, ((double) size) / 1024.0/1024.0); + } + ret= 1; +ex:; + return(ret); +} + + +/** Perform --list_speeds + @param flag Bitfield for control purposes: + @return <=0 error, 1 success +*/ +int Cdrskin_list_speeds(struct CdrskiN *skin, int flag) +{ + struct burn_drive *drive; + int ret, i, profile_no, high= -1, low= 0x7fffffff, is_cd= 0; + char profile_name[90], *speed_unit= "D"; + double speed_factor= 1385000.0, cd_factor= 75.0 * 2352; + struct burn_speed_descriptor *speed_list= NULL, *item, *other; + + ret= Cdrskin_grab_drive(skin,0); + if(ret<=0) + return(ret); + drive= skin->drives[skin->driveno].drive; + ret= burn_drive_get_speedlist(drive, &speed_list); + if(ret <= 0) { + fprintf(stderr, "cdrskin: SORRY: Cannot obtain speed list info\n"); + ret= 0; goto ex; + } + ret= burn_disc_get_profile(drive, &profile_no, profile_name); + printf("Media current: "); + 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"); + if(profile_no >= 0x08 && profile_no <= 0x0a) + is_cd= profile_no; + speed_factor= Cdrskin_libburn_speed_factoR * 1000.0; + if(Cdrskin_libburn_speed_factoR == Cdrskin_libburn_cd_speed_factoR) + speed_unit= "C"; + else if(Cdrskin_libburn_speed_factoR == Cdrskin_libburn_bd_speed_factoR) + speed_unit= "B"; + + for (item= speed_list; item != NULL; item= item->next) { + if(item->source == 1) { + /* CD mode page 2Ah : report only if not same speed by GET PERFORMANCE */ + for(other= speed_list; other != NULL; other= other->next) + if(other->source == 2 && item->write_speed == other->write_speed) + break; + if(other != NULL) + continue; + } + printf("Write speed : %5dk , %4.1fx%s\n", + item->write_speed, + ((double) item->write_speed) * 1000.0 / speed_factor, speed_unit); + if(item->write_speed > high) + high= item->write_speed; + if(item->write_speed < low) + low= item->write_speed; + } + + /* Maybe there is ATIP info */ + if(is_cd) { + ret= burn_disc_read_atip(drive); + if(ret < 0) + goto ex; + if(ret > 0) { + for(i= 0; i < 2; i++) { + if(i == 0) + ret= burn_drive_get_min_write_speed(drive); + else + ret= burn_drive_get_write_speed(drive); + if(ret > 0) { + if(ret < low || (i == 0 && ret != low)) { + printf("Write speed l: %5dk , %4.1fx%s\n", + ret, ((double) ret) * 1000.0 / cd_factor, "C"); + low= ret; + } + if(ret > high || (i == 1 && ret != high)) { + printf("Write speed h: %5dk , %4.1fx%s\n", + ret, ((double) ret) * 1000.0 / cd_factor, "C"); + high= ret; + } + } + } + } + } + if(high > -1) { + printf("Write speed L: %5dk , %4.1fx%s\n", + low, ((double) low) * 1000.0 / speed_factor, speed_unit); + printf("Write speed H: %5dk , %4.1fx%s\n", + high, ((double) high) * 1000.0 / speed_factor, speed_unit); + ret= burn_drive_get_best_speed(drive, -1, &item, 2); + if(ret > 0 && item != NULL) + if(item->write_speed != low) + printf("Write speed 0: %5dk , %4.1fx%s\n", + item->write_speed, + ((double) item->write_speed) * 1000.0 / speed_factor, speed_unit); + ret= burn_drive_get_best_speed(drive, 0, &item, 2); + if(ret > 0 && item != NULL) + if(item->write_speed != high) + printf("Write speed-1: %5dk , %4.1fx%s\n", + item->write_speed, + ((double) item->write_speed) * 1000.0 / speed_factor, speed_unit); + } else { + fprintf(stderr, + "cdrskin: SORRY : Could not get any write speed information from drive"); + } + ret= 1; +ex:; + if(speed_list != NULL) + burn_drive_free_speedlist(&speed_list); + return(ret); +} + + +int Cdrskin_read_textfile(struct CdrskiN *skin, char *path, int flag) +{ + int ret, num_packs = 0; + unsigned char *text_packs = NULL; + + ret= burn_cdtext_from_packfile(path, &text_packs, &num_packs, 0); + if(ret <= 0) + goto ex; + + if(skin->text_packs != NULL) + free(skin->text_packs); + skin->text_packs= text_packs; + skin->num_text_packs= num_packs; + ret= 1; +ex:; + if(ret <= 0 && text_packs != NULL) + free(text_packs); + return(ret); +} + + +/* + @param flag Bitfield for control purposes: + bit3= Enable DAP : "flaw obscuring mechanisms like + audio data mute and interpolate" +*/ +int Cdrskin_extract_audio_to_dir(struct CdrskiN *skin, + char *dir, char *basename, + char print_tracks[100], int flag) +{ + int num_sessions= 0, num_tracks= 0, profile_number, session_no, track_no; + int track_count= 0, existing= 0, ret, pass, not_audio= 0, pick_tracks= 0, i; + int tracks_extracted= 0, min_tno= 100, max_tno= 0; + struct burn_drive *drive; + struct burn_disc *disc= NULL; + struct burn_session **sessions; + struct burn_track **tracks; + enum burn_disc_status s; + struct burn_toc_entry toc_entry; + struct stat stbuf; + char profile_name[80], path[4096 + 256]; + + ret= Cdrskin_grab_drive(skin, 0); + if(ret<=0) + goto ex; + drive= skin->drives[skin->driveno].drive; + s= burn_disc_get_status(drive); + disc= burn_drive_get_disc(drive); + if(s == BURN_DISC_EMPTY || s == BURN_DISC_BLANK || disc == NULL) { +not_readable:; + fprintf(stderr, "cdrskin: SORRY : No audio CD in drive.\n"); + ret= 0; goto ex; + } + ret= burn_disc_get_profile(drive, &profile_number, profile_name); + if(ret <= 0 || profile_number < 0x08 || profile_number > 0x0a) { +not_audio_cd:; + fprintf(stderr, "cdrskin: SORRY : Medium in drive is not an audio CD.\n"); + ret= 0; goto ex; + } + for(i= 0; i < 99; i++) + if(print_tracks[i]) + pick_tracks= 1; + sessions= burn_disc_get_sessions(disc, &num_sessions); + if(num_sessions <= 0) + goto not_readable; + for(pass= 0; pass < 2; pass++) { + if(pass > 0) { + if(existing > 0) + {ret= 0; goto ex;} + if(not_audio == track_count) + goto not_audio_cd; + } + track_count= 0; + 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.point < min_tno) + min_tno= toc_entry.point; + if(toc_entry.point > max_tno) + max_tno= toc_entry.point; + if(pick_tracks) { + if(toc_entry.point <= 0 || toc_entry.point > 99) + continue; /* should not happen */ + if(print_tracks[toc_entry.point] == 0) + continue; + } + if((toc_entry.control & 7) >= 4) { + if(pass == 0) + fprintf(stderr, + "cdrskin: WARNING : Track %2.2d is not an audio track.\n", + toc_entry.point); + not_audio++; + continue; + } + sprintf(path, "%s/%s%2.2u.wav", dir, basename, toc_entry.point); + if(pass == 0) { + if(stat(path, &stbuf) != -1) { + fprintf(stderr, + "cdrskin: SORRY : May not overwrite existing file: '%s'\n", + path); + existing++; + } + continue; + } + if(skin->verbosity >= Cdrskin_verbose_progresS) + fprintf(stderr, "cdrskin: Writing audio track file: %s\n", path); + ret= burn_drive_extract_audio_track(drive, tracks[track_no], path, + (flag & 8) | (skin->verbosity >= Cdrskin_verbose_progresS)); + if(ret <= 0) + goto ex; + tracks_extracted++; + } + } + } + if(tracks_extracted == 0 && pick_tracks) { + fprintf(stderr, + "cdrskin: SORRY : Not a single track matched the list of extract_tracks=\n"); + if(min_tno < 100 && max_tno > 0) + fprintf(stderr, "cdrskin: HINT : Track range is %2.2d to %2.2d\n", + min_tno, max_tno); + ret= 0; goto ex; + } + ret= 1; +ex:; + 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; + int ret; + + ret= burn_drive_get_drive_role(skin->drives[skin->driveno].drive); + if(ret != 1) + return(1); + + 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; + 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(Cdrskin__is_aborting(0)) + return(0); + 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, format_flag= 0, status, num_formats; + off_t size; + unsigned dummy; + double start_time; + char *verb= "blank", *presperf="blanking", *fmt_text= "..."; + char profile_name[80]; + static char fmtp[][40]= { + "...", "format_overwrite", "deformat_sequential", + "(-format)", "format_defectmgt", "format_by_index", + "format_if_needed", "as_needed"}; + static int fmtp_max= 7; + + 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; + if(skin->grabbed_drive) + burn_disc_get_profile(skin->grabbed_drive,&profile_number,profile_name); + + ret= Cdrskin_report_disc_status(skin,s, + 1|(4*!(skin->verbosity>=Cdrskin_verbose_progresS))); + if(ret==2) + s= BURN_DISC_APPENDABLE; + do_format= skin->blank_format_type & 0xff; + 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; + } + if(do_format) + if(do_format>=0 && do_format<=fmtp_max) + fmt_text= fmtp[do_format]; + if(do_format==5) { /* format_by_index */ + if(profile_number == 0x12 || profile_number == 0x43) + do_format= 4; + else + do_format= 1; + } else if(do_format==6 || do_format==7) { /* format_if_needed , as_needed */ + /* Find out whether format is needed at all. + Eventuelly set up a suitable formatting run + */ + if(profile_number == 0x14 && do_format==6) { /* sequential DVD-RW */ + do_format= 1; + skin->blank_format_type= 1|(1<<8); + skin->blank_format_size= 128*1024*1024; + } else if(profile_number == 0x12 || + profile_number == 0x43 || + (profile_number == 0x41 && do_format==6)) { + /* DVD-RAM , BD-RE , BD-R SRM */ + ret= burn_disc_get_formats(drive, &status, &size, &dummy, &num_formats); + if((ret>0 && status!=BURN_FORMAT_IS_FORMATTED)) { + do_format= 4; + skin->blank_format_type= 4|(3<<9); /* default payload size */ + skin->blank_format_size= 0; + } + } else if(do_format==7) { /* try to blank what is not blank yet */ + if(s!=BURN_DISC_BLANK) { + do_format= 0; + skin->blank_fast= 1; + } + } + if(do_format==6 || do_format==7) { + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(fprintf(stderr, + "cdrskin: NOTE : blank=%s : no need for action detected\n", fmt_text)); + {ret= 2; goto ex;} + } + } + + if(do_format == 1 || do_format == 3 || do_format == 4) { + verb= "format"; + presperf= "formatting"; + } + 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\n"); + {ret= 2; goto ex;} + } + } else { + fprintf(stderr, + "cdrskin: SORRY : blank=%s for now does DVD-RW and 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==4) { + /* Formatting and influencing defect management of DVD-RAM , BD-RE */ + if(!(profile_number == 0x12 || profile_number == 0x41 || + profile_number == 0x43)) { + fprintf(stderr, + "cdrskin: SORRY : blank=%s for now does DVD-RAM and BD 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)) { + 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 || do_format==4)); +#endif /* ! Cdrskin_extra_leaN */ + + if(Cdrskin__is_aborting(0)) + {ret= 0; goto ex;} + skin->drive_is_busy= 1; + if(do_format==0 || do_format==2) { + burn_disc_erase(drive,skin->blank_fast); + + } else if(do_format==1 || do_format==3 || do_format==4) { + format_flag= (skin->blank_format_type>>8)&(1|2|4|32|128); + if(skin->force_is_set) + format_flag|= 16; + if(format_flag&128) + format_flag|= (skin->blank_format_index&255)<<8; + if(skin->blank_format_no_certify) + format_flag|= 64; + burn_disc_format(drive,(off_t) skin->blank_format_size,format_flag); + + } 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, + "%scdrskin: %s ( done %.1f%% , %lu seconds elapsed ) %s", + skin->pacifier_with_newline ? "" : "\r", + presperf,percent,(unsigned long) (Sfile_microtime(0)-start_time), + skin->pacifier_with_newline ? "\n" : ""); + } + sleep(1); + loop_counter++; + } +blanking_done:; + wrote_well = burn_drive_wrote_well(drive); + if(wrote_well && skin->verbosity>=Cdrskin_verbose_progresS) { + fprintf(stderr, + "%scdrskin: %s done \n", + skin->pacifier_with_newline ? "" : "\r", presperf); + printf("%s time: %.3fs\n", + (do_format==1 || do_format==3 || do_format==4 ? + "Formatting":"Blanking"), + Sfile_microtime(0)-start_time); + } + fflush(stdout); + if(!wrote_well) + fprintf(stderr, + "%scdrskin: %s failed \n", + skin->pacifier_with_newline ? "" : "\r", presperf); + ret= !!(wrote_well); +ex:; + skin->drive_is_busy= 0; + if(Cdrskin__is_aborting(0)) + Cdrskin_abort(skin, 0); /* Never comes back */ + return(ret); +} + + +int Cdrskin__libburn_fifo_status(struct burn_source *current_fifo, + char fifo_text[80], int flag) +{ + int ret, size, free_space, fill, fifo_percent; + char *status_text= ""; + + ret= burn_fifo_inquire_status(current_fifo, &size, &free_space, + &status_text); + if(ret <= 0 || ret >= 4) { + strcpy(fifo_text, "(fifo 0%) "); + } else if(ret == 1) { + burn_fifo_next_interval(current_fifo, &fill); + fifo_percent= 100.0 * ((double) fill) / (double) size; + if(fifo_percent<100 && fill>0) + fifo_percent++; + sprintf(fifo_text, "(fifo %3d%%) ", fifo_percent); + } + return(1); +} + + +/** 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, int start_tno, + 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= 0.0; + double written_bytes= 0.0,written_total_bytes= 0.0; + 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,advance_interval=0,new_mb,old_mb,time_to_tell; + int old_track_idx,buffer_fill,formatting= 0,use_data_image_size; + char fifo_text[80],mb_text[40], pending[40]; + char *debug_mark= ""; /* use this to prepend a marker text for experiments */ + +#ifndef Cdrskin_no_cdrfifO + double buffer_size; + int fs, bs, space; +#endif + +#ifdef Cdrskin_use_libburn_fifO + struct burn_source *current_fifo= NULL; + +#ifdef NIX + int size, free_space; + char *status_text= ""; +#endif + +#endif /* Cdrskin_use_libburn_fifO */ + + /* for debugging */ + static double last_fifo_in= 0.0,last_fifo_out= 0.0,curr_fifo_in,curr_fifo_out; + + if(Cdrskin__is_aborting(0)) + Cdrskin_abort(skin, 0); /* Never comes back */ + + 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 */ + + if(drive_status==BURN_DRIVE_FORMATTING) { + formatting= 1; + } else if(drive_status==BURN_DRIVE_WRITING) { + ; + } else if(drive_status==BURN_DRIVE_WRITING_LEADIN + || drive_status==BURN_DRIVE_WRITING_PREGAP + || formatting) { + if(time_to_tell || skin->is_writing) { + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(skin->is_writing) + fprintf(stderr,"\n"); + fprintf(stderr, + "%scdrskin: %s (burning since %.f seconds)%s", + skin->pacifier_with_newline ? "" : "\r", + (formatting?"formatting":"working pre-track"), elapsed_total_time, + skin->pacifier_with_newline ? "\n" : " "); + } + skin->is_writing= 0; + advance_interval= 1; + } + {ret= 2; goto ex;} + } else if(drive_status==BURN_DRIVE_WRITING_LEADOUT + || drive_status==BURN_DRIVE_CLOSING_TRACK + || drive_status==BURN_DRIVE_CLOSING_SESSION) { + + if(drive_status==BURN_DRIVE_CLOSING_SESSION && + skin->previous_drive_status!=drive_status) + {printf("\nFixating...\n"); fflush(stdout);} + if(time_to_tell || skin->is_writing) { + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(skin->is_writing && !skin->pacifier_with_newline) + fprintf(stderr,"\n"); + fprintf(stderr, + "%scdrskin: working post-track (burning since %.f seconds)%s", + skin->pacifier_with_newline ? "" : "\r", elapsed_total_time, + skin->pacifier_with_newline ? "\n" : " "); + } + skin->is_writing= 0; + advance_interval= 1; + } + {ret= 2; goto ex;} + } else + goto thank_you_for_patience; + + old_track_idx= skin->supposed_track_idx; + skin->supposed_track_idx= p->track; + + if(old_track_idx>=0 && old_track_idxsupposed_track_idx && + skin->supposed_track_idx < skin->track_counter) { + 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 + start_tno, fixed_size,fixed_size+padding, + (fixed_size+padding)/sector_size); + *last_count= 0; + } + + 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"); + } + pending[0]= 0; +/* + if(bytes_to_write > 0 && skin->verbosity >= Cdrskin_verbose_debuG) + sprintf(pending, " pnd %.f", bytes_to_write - written_total_bytes); +*/ + fprintf(stderr, + "%scdrskin: thank you for being patient for %.f seconds%21.21s%s", + skin->pacifier_with_newline ? "" : "\r", + elapsed_total_time, pending, + skin->pacifier_with_newline ? "\n" : ""); + } + 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 && !skin->pacifier_with_newline) + printf("\r%-78.78s\r", ""); /* wipe output line */ + 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->cuefile_fifo != NULL) + Cdrskin__libburn_fifo_status(skin->cuefile_fifo, fifo_text, 0); + +#ifdef Cdrskin_use_libburn_fifO + + /* Inquire fifo fill and format fifo pacifier text */ + if(skin->fifo == NULL && skin->supposed_track_idx >= 0 && + skin->supposed_track_idx < skin->track_counter && + skin->fifo_size > 0 && fifo_text[0] == 0) { + Cdrtrack_get_libburn_fifo(skin->tracklist[skin->supposed_track_idx], + ¤t_fifo, 0); + if(current_fifo != NULL) { + Cdrskin__libburn_fifo_status(current_fifo, fifo_text, 0); + } else if(skin->fifo_size > 0) { + strcpy(fifo_text, "(fifo 100%) "); + } + } + +#endif /* Cdrskin_use_libburn_fifO */ + +#ifndef Cdrskin_no_cdrfifO + + 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; + } + } + } + +#endif /* ! Cdrskin_no_cdrfifO */ + + 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; + if(p->buffer_capacity>0) + buffer_fill= (double) (p->buffer_capacity - p->buffer_available)*100.0 + / (double) p->buffer_capacity; + + if(p->buffered_bytes > p->buffer_capacity) + if(buffer_fill < *min_buffer_fill) + *min_buffer_fill= buffer_fill; + + printf("%s%sTrack %-2.2d: %s MB written %s[buf %3d%%] %4.1fx.%s", + skin->pacifier_with_newline ? "" : "\r", + debug_mark, skin->supposed_track_idx + start_tno, mb_text, + fifo_text, buffer_fill, measured_speed / speed_factor, + skin->pacifier_with_newline ? "\n" : ""); + 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) && !skin->pacifier_with_newline) + printf("\n"); + skin->is_writing= 1; + } + printf("%sTrack %-2.2d: %3d MB written %s", + skin->pacifier_with_newline ? "" : "\r", + skin->supposed_track_idx + start_tno, + (int) (written_total_bytes / 1024.0 / 1024.0), + skin->pacifier_with_newline ? "\n" : ""); + if(skin->is_writing == 0 && !skin->pacifier_with_newline) + printf("\n"); + fflush(stdout); + +#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); +} + + +/** 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, 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(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; + +#ifndef Cdrskin_disable_raw96R + if(wt==BURN_WRITE_RAW) + strcpy(skin->preskin->write_mode_name,"RAW/RAW96R"); + else +#endif /* ! Cdrskin_disable_raw96R */ + 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); + } + +#ifndef Cdrskin_disable_raw96R + if(strcmp(skin->preskin->write_mode_name,"RAW/RAW96R")==0) { + skin->write_type= BURN_WRITE_RAW; + skin->block_type= BURN_BLOCK_RAW96R; + } else +#endif /* ! Cdrskin_disable_raw96R */ + + 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; + /* cdrecord tolerates the combination of -sao and -multi. -multi wins. */ + burn_write_opts_set_write_type(opts,skin->write_type,skin->block_type); + ret = burn_precheck_write(opts,disc,reasons,0); + if(ret <= 0) { + if(strstr(reasons, "multi session capability lacking") != NULL) { + fprintf(stderr,"cdrskin: WARNING : Cannot do SAO/DAO. Reason: %s\n", + reasons); + fprintf(stderr,"cdrskin: Defaulting to TAO/Incremental.\n"); + skin->write_type= BURN_WRITE_TAO; + skin->block_type= BURN_BLOCK_MODE1; + } + } + } + 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); +} + + +#ifndef Cdrskin_extra_leaN + +int Cdrskin_announce_tracks(struct CdrskiN *skin, int start_tno, int flag) +{ + int i,mb,use_data_image_size; + double size,padding,sector_size= 2048.0; + double sectors; + + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(start_tno < 1) + start_tno= 1; + 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 + start_tno, (sector_size==2048?"data ":"audio")); + } else { + mb= size/1024.0/1024.0; + printf("Track %-2.2d: %s %4d MB ", + i + start_tno, (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; + sectors+= 150; + 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 */ + + +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) | + (4 * (skin->fifo_size >= 256 * 1024))); + if(ret<=0) + goto ex; + } + buf= calloc(1, 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, "%s%9.fk written %s", + skin->pacifier_with_newline ? "" : "\r", + ((double) (i+chunksize)) / 1024.0, + skin->pacifier_with_newline ? "\n" : ""); + } + fprintf(stderr, "%s%9.fk written \n", + skin->pacifier_with_newline ? "" : "\r", ((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(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, *md; + unsigned char *td; /* Because the representation of 255 in char is ambigous */ + 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= (unsigned char *) (track_descr + i * 2048); + md= skin->overwriteable_iso_head+(16+i)*2048; + if(td[0] != 255) + break; + /* demand media descrN[0] == track descrN[0] */ + if(((char *) 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); +} + + +int Cdrskin_cdtext_test(struct CdrskiN *skin, struct burn_write_opts *o, + struct burn_session *session, int flag) +{ + int ret, num_packs; + unsigned char *text_packs = NULL; + + if(skin->num_text_packs > 0) { + Cdrskin_print_text_packs(skin, skin->text_packs, skin->num_text_packs, 2); + ret= 1; goto ex; + } + ret= burn_cdtext_from_session(session, &text_packs, &num_packs, 0); + if(ret < 0) + goto ex; + if(ret > 0) { + Cdrskin_print_text_packs(skin, text_packs, num_packs, 1); + ret= 1; goto ex; + } + ret= 1; +ex: + if (text_packs != NULL) + free(text_packs); + return(ret); +} + + +int Cdrskin_read_input_sheet_v07t(struct CdrskiN *skin, char *path, int block, + struct burn_session *session, int flag) +{ + int ret= 0; + + ret= burn_session_input_sheet_v07t(session, path, block, 1); + return(ret); +} + + +int Cdrskin_cdrtracks_from_session(struct CdrskiN *skin, + struct burn_session *session, int flag) +{ + int ret, num_tracks, i; + struct burn_track **tracks; + struct CdrtracK *t; + double sectors; + + for(i= 0; i < skin->track_counter; i++) + Cdrtrack_destroy(&(skin->tracklist[i]), 0); + skin->track_counter= 0; + + tracks= burn_session_get_tracks(session, &num_tracks); + if(num_tracks <= 0) { + fprintf(stderr, "cdrskin: SORRY : No tracks defined for burn run.\n"); + ret= 0; goto ex; + } + for(i= 0; i < num_tracks; i++) { + /* Create Cdrtrack without reading parameters from skin */ + ret= Cdrtrack_new(&(skin->tracklist[skin->track_counter]), skin, + skin->track_counter, 1); + if(ret <= 0) { + fprintf(stderr, + "cdrskin: FATAL : Creation of track control object failed.\n"); + goto ex; + } + + /* Set essential properties */ + t= skin->tracklist[skin->track_counter]; + t->track_type= burn_track_get_mode(tracks[i]); + if(t->track_type & BURN_AUDIO) { + t->sector_size= 2352; + t->track_type= BURN_AUDIO; + } else { + t->sector_size= 2048; + t->track_type= BURN_MODE1; + } + sectors= burn_track_get_sectors(tracks[i]); + t->fixed_size= sectors * t->sector_size; + t->libburn_track= tracks[i]; + t->libburn_track_is_own= 0; + + skin->track_counter++; + } + ret= 1; +ex: + if(ret <= 0) { + for(i= 0; i < skin->track_counter; i++) + Cdrtrack_destroy(&(skin->tracklist[i]), 0); + skin->track_counter= 0; + } + return(ret); +} + + +int Cdrskin_write_result_string(struct CdrskiN *skin, char *msg, int flag) +{ + int ret; + + if(skin->preskin->result_fd < 0) { + printf("%s",msg); + return(1); + } + ret= write(skin->preskin->result_fd, msg, strlen(msg)); + if(ret != (int) strlen(msg)) + return(0); + return(1); +} + + +/** Burn data via libburn according to the parameters set in skin. + @return <=0 error, 1 success +*/ +int Cdrskin_burn(struct CdrskiN *skin, int flag) +{ + struct burn_disc *disc; + struct burn_session *session; + struct burn_write_opts *o = NULL; + struct burn_source *cuefile_fifo= NULL; + 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, min_buffer_fill= 101, length, start_tno= 1; + int use_data_image_size, needs_early_fifo_fill= 0,iso_size= -1, non_audio= 0; + 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; + unsigned char *payload; + int source_fd, is_from_stdin; + int text_flag= 4; /* Check CRCs and silently repair CRCs if all are 0 */ + unsigned char *text_packs= NULL; + int num_packs= 0, start_block, block_no; + +#ifndef Cdrskin_no_cdrfifO + double put_counter, get_counter, empty_counter, full_counter; + int total_min_fill, fifo_percent; +#endif + off_t free_space; + char msg[80]; + + 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); + + 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(cuefile_fifo != NULL) + burn_source_free(cuefile_fifo); + 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; + skin->has_open_ended_track= 0; + if(skin->cuefile[0]) { + if(skin->track_counter > 0) { + fprintf(stderr, + "cdrskin: SORRY : Option cuefile= cannot be combined with track sources\n"); + goto burn_failed; + } + if(strcmp(skin->preskin->write_mode_name, "SAO") != 0 && + strcmp(skin->preskin->write_mode_name, "DEFAULT") != 0) { + fprintf(stderr, + "cdrskin: SORRY : Option cuefile= works only with write mode SAO"); + goto burn_failed; + } + strcpy(skin->preskin->write_mode_name, "SAO"); + + ret= burn_session_by_cue_file(session, skin->cuefile, skin->fifo_size, + &cuefile_fifo, + &text_packs, &num_packs, !skin->use_cdtext); + if(ret <= 0) + goto burn_failed; + if(num_packs > 0) { + if(skin->num_text_packs > 0) { + fprintf(stderr, "cdrskin: WARNING : Option textfile= overrides CDTEXTFILE from option cuesheet=\n"); + if(text_packs != NULL) + free(text_packs); + } else { + skin->text_packs= text_packs; + skin->num_text_packs= num_packs; + } + } + ret= Cdrskin_cdrtracks_from_session(skin, session, 0); + if(ret <= 0) + goto burn_failed; + skin->cuefile_fifo= cuefile_fifo; + cuefile_fifo= NULL; + + } else { + 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); + +/* if(skin->fifo_size >= 256 * 1024) */ + + hflag|= 4; + + 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; + } + non_audio|= (skin->tracklist[i]->track_type != BURN_AUDIO); + } + } + + if(skin->sheet_v07t_blocks > 0) { + if(skin->num_text_packs > 0) { + fprintf(stderr, "cdrskin: WARNING : Option textfile= or cuefile= overrides option input_sheet_v07t=\n"); + } else if(non_audio) { + fprintf(stderr, "cdrskin: SORRY : Option input_sheet_v07t= works only if all tracks are -audio\n"); + goto burn_failed; + } else { + /* If cuefile and session has block 0, then start at block 1 */ + start_block= 0; + if(skin->cuefile[0]) { + for(i= 0x80; i < 0x8f; i++) { + ret= burn_session_get_cdtext(session, 0, i, "", &payload, &length, 0); + if(ret > 0 && length > 0) + break; + } + if(i < 0x8f) + start_block= 1; + } + block_no = start_block; + for(i= 0; i < skin->sheet_v07t_blocks && block_no < 8; i++) { + ret= Cdrskin_read_input_sheet_v07t(skin, skin->sheet_v07t_paths[i], + block_no, session, 0); + if(ret <= 0) + goto burn_failed; + block_no += ret; + } + if(i < skin->sheet_v07t_blocks) { + fprintf(stderr, "cdrskin: WARNING : Too many CD-TEXT blocks. input_sheet_v07t= files ignored: %d\n", + skin->sheet_v07t_blocks - i); + } + } + } + +#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 */ + + 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; + if(skin->tracklist[i]->isrc[0] && + skin->tracklist[i]->libburn_track != NULL) { + ret= burn_track_set_isrc_string(skin->tracklist[i]->libburn_track, + skin->tracklist[i]->isrc, 0); + if(ret <= 0) + goto ex; + } + } + + o= burn_write_opts_new(drive); + burn_write_opts_set_perform_opc(o, 0); + +/* 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); + + if(skin->media_is_overwriteable && skin->multi) { + if(skin->grow_overwriteable_iso<=0) { + fprintf(stderr, "cdrskin: FATAL : -multi cannot leave a recognizable 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); + burn_write_opts_set_fillup(o, skin->fill_up_media); + + burn_write_opts_set_force(o, !!skin->force_is_set); + burn_write_opts_set_stream_recording(o, skin->stream_recording_is_set); + +#ifdef Cdrskin_dvd_obs_default_64K + if(skin->dvd_obs == 0) + burn_write_opts_set_dvd_obs(o, 64 * 1024); + else +#endif + burn_write_opts_set_dvd_obs(o, skin->dvd_obs); + burn_write_opts_set_obs_pad(o, skin->obs_pad); + burn_write_opts_set_stdio_fsync(o, skin->stdio_sync); + + 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); + if(skin->num_text_packs > 0) { + if(non_audio) { + fprintf(stderr, "cdrskin: SORRY : Option textfile= works only if all tracks are -audio\n"); + goto burn_failed; + } + if(!!skin->force_is_set) + text_flag= 1; /* No CRC verification or repairing */ + ret= burn_write_opts_set_leadin_text(o, skin->text_packs, + skin->num_text_packs, text_flag); + if(ret <= 0) + goto burn_failed; + } + if(skin->mcn[0]) { + burn_write_opts_set_mediacatalog(o, (unsigned char *) skin->mcn); + burn_write_opts_set_has_mediacatalog(o, 1); + } + if(skin->cd_start_tno >= 1 && skin->cd_start_tno <= 99) { + ret= burn_session_set_start_tno(session, skin->cd_start_tno, 0); + if(ret <= 0) + goto burn_failed; + } + ret= Cdrskin_activate_write_mode(skin,o,disc,0); + if(ret<=0) + goto burn_failed; + + if(skin->cdtext_test) { + ret= Cdrskin_cdtext_test(skin, o, session, (skin->cdtext_test == 1)); + if(ret <= 0) + goto ex; + if(skin->cdtext_test >= 2) { + fprintf(stderr, + "cdrskin: Option --cdtext_dummy prevents actual burn run\n"); + ret= 1; goto ex; + } + } + + 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, burn_session_get_start_tno(session, 0), 0); +#endif + + if(skin->tell_media_space || skin->track_counter <= 0) { + /* write capacity estimation and return without actual burning */ + + free_space= burn_disc_available_space(drive,o); + sprintf(msg,"%d\n",(int) (free_space/(off_t) 2048)); + Cdrskin_write_result_string(skin, msg, 0); + 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;} + } + + if(skin->fixed_size > 0 && !skin->force_is_set) { + free_space= burn_disc_available_space(drive,o); + if(skin->fixed_size > free_space && free_space > 0) { + fprintf(stderr, + "cdrskin: FATAL : predicted session size %lus does not fit on media (%lus)\n", + (unsigned long) ((skin->fixed_size + 2047.0) / 2048.0), + (unsigned long) ((free_space + 2047) / 2048)); + ClN(fprintf(stderr, + "cdrskin: HINT : This test may be disabled by option -force\n");) + {ret= 0; goto ex;} + } + } + + Cdrskin_adjust_speed(skin,0); + +#ifndef Cdrskin_extra_leaN + Cdrskin_wait_before_action(skin,0); + if(burn_is_aborting(0)) + {ret= 0; goto ex;} + + if(needs_early_fifo_fill==1) + ret= 1; + else if(skin->cuefile[0] != 0) + 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 */ + + start_tno = burn_session_get_start_tno(session, 0); + if(skin->verbosity>=Cdrskin_verbose_progresS && nwa>=0) { + printf("Starting new track at sector: %d\n",nwa); + fflush(stdout); + } + if(burn_is_aborting(0)) + {ret= 0; goto ex;} + skin->drive_is_busy= 1; + burn_disc_write(o, disc); + if(skin->preskin->abort_handler==-1) + Cleanup_set_handlers(Cleanup_handler_handlE, Cleanup_handler_funC, + Cleanup_handler_flaG); + last_time= start_time= Sfile_microtime(0); + + burn_write_opts_free(o); + o= NULL; + + 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__is_aborting(0)) + Cdrskin_burn_pacifier(skin, start_tno, + 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(Cdrskin__is_aborting(0)) + fifo_disabled= 1; + if(skin->fifo==NULL || fifo_disabled) { + usleep(20000); + } else { + +#ifdef Cdrskin_no_cdrfifO + + /* Should never happen as skin->fifo should be NULL */ + usleep(20000); + +#else /* Cdrskin_no_cdrfifO */ + + 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; + } + +#endif /* ! Cdrskin_no_cdrfifO */ + + } + +#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"); + + if(Cdrskin__is_aborting(0)) + Cdrskin_abort(skin, 0); /* Never comes back */ + + wrote_well = burn_drive_wrote_well(drive); + 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; + } + if(max_track<0) { + printf("Track %-2.2d: Total bytes read/written: %.f/%.f (%.f sectors).\n", + start_tno, 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); + if (start_tno <= 0) + start_tno = 1; + printf("Track %-2.2d: Total bytes read/written: %.f/%.f (%.f sectors).\n", + max_track + start_tno, 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 + +#ifdef Cdrskin_use_libburn_fifO + + if(skin->fifo == NULL && skin->verbosity>=Cdrskin_verbose_progresS) { + /* >>> this should rather be done for each track + (for now this libburn_fifo should only be used with single track) + */ + Cdrtrack_report_fifo(skin->tracklist[skin->track_counter - 1], 0); + } + +#endif /* Cdrskin_use_libburn_fifO */ + +#ifndef Cdrskin_no_cdrfifO + + 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); + } + } + +#endif /* ! Cdrskin_no_cdrfifO */ + + if(skin->verbosity>=Cdrskin_verbose_progresS) { + drive_status= burn_drive_get_status(drive, &p); + 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)); + for(i= 0;itrack_counter;i++) + Cdrtrack_cleanup(skin->tracklist[i],0); + if(o != NULL) + burn_write_opts_free(o); + if(cuefile_fifo != NULL) + burn_source_free(cuefile_fifo); + burn_session_free(session); + burn_disc_free(disc); + return(ret); +} + + +#ifdef Libburn_develop_quality_scaN + +int Cdrskin_qcheck(struct CdrskiN *skin, int flag) +{ + struct burn_drive *drive; + int ret, rate_period, profile_number; + char profile_name[80]; + + printf("cdrskin: beginning to perform quality check on disc\n"); + ret= Cdrskin_grab_drive(skin,0); + if(ret<=0) + return(ret); + drive= skin->drives[skin->driveno].drive; + + ret= burn_disc_get_profile(drive, &profile_number, profile_name); + if(ret <= 0) + profile_number= 0; + if(profile_number != 0x08 && profile_number != 0x09 && profile_number != 0x0a) + rate_period= 8; + else + rate_period= 75; + if(skin->do_qcheck == 1) { + ret= burn_nec_optiarc_rep_err_rate(drive, 0, rate_period, 0); + if(ret<=0) + return(ret); + } + return(1); +} + +#endif /* Libburn_develop_quality_scaN */ + + +/** 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, open_sessions= 0; + 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) { + /* No TOC available. Try to inquire directly. */ + ret= burn_disc_get_msc1(drive,&lba); + if(ret>0) + goto obtain_nwa; + fprintf(stderr,"cdrskin: FATAL : Cannot obtain info about CD content\n"); + {ret= 0; goto ex;} + } + sessions= burn_disc_get_sessions(disc,&num_sessions); + open_sessions= burn_disc_get_incomplete_sessions(disc); + for(session_no= 0; session_no < num_sessions + open_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 */ + if(session_no >= num_sessions) { + if(!(toc_entry.extensions_valid & 4)) + continue; /* open session with no track status bits from libburn */ + if((toc_entry.track_status_bits & (1 << 14)) || + !((toc_entry.track_status_bits & (1 << 16)) || + ((toc_entry.track_status_bits & (1 << 17)) && + toc_entry.last_recorded_address > toc_entry.start_lba))) + continue; /* Blank or not appendable and not recorded */ + } + lba= toc_entry.start_lba; + } else { + lba= burn_msf_to_lba(toc_entry.pmin,toc_entry.psec,toc_entry.pframe); + } + } + if(lba==-123456789) { + fprintf(stderr,"cdrskin: FATAL : Cannot find any track on CD\n"); + {ret= 0; goto ex;} + } + +obtain_nwa:; + ret= Cdrskin_obtain_nwa(skin,&nwa,flag); + if(ret<=0) { + if (sessions == NULL) { + fprintf(stderr, + "cdrskin: SORRY : Cannot obtain next writeable address\n"); + {ret= 0; goto ex;} + } + ClN(fprintf(stderr, + "cdrskin: NOTE : Guessing next writeable address from leadout\n")); + burn_session_get_leadout_entry(sessions[num_sessions-1],&toc_entry); + if(toc_entry.extensions_valid&1) { /* DVD extension valid */ + aux_lba= toc_entry.start_lba; + } else { + aux_lba= burn_msf_to_lba(toc_entry.pmin,toc_entry.psec,toc_entry.pframe); + } + if(num_sessions>0) + nwa= aux_lba+6900; + else + nwa= aux_lba+11400; + } + +put_out:; + sprintf(msg,"%d,%d\n",lba,nwa); + Cdrskin_write_result_string(skin, msg, 0); + + 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:; + if(disc!=NULL) + burn_disc_free(disc); + return(ret); +} + + +/** Work around the failure of libburn to eject the tray. + This employs a system(2) call and is therefore an absolute no-no for any + pseudo user identities. + @return <=0 error, 1 success +*/ +int Cdrskin_eject(struct CdrskiN *skin, int flag) +{ + int i,ret,max_try= 5; + + if(!skin->do_eject) + return(1); + + if((int) 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); +} + + +/** 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, idx= -1, cd_start_tno; + double value,grab_and_wait_value= -1.0, num; + char *cpt,*value_pt,adr[Cdrskin_adrleN],*blank_mode= "", *argpt; + 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=", + "pktsize=", + "" + }; + static char ignored_full_options[][41]= { + "-d", "-silent", "-s", "-setdropts", "-prcap", + "-reset", "-abort", "-overburn", "-ignsize", "-useinfo", + "-fix", "-nofix", + "-raw", "-raw96p", "-raw16", "-raw96r", + "-clone", + "-cdi", + "-shorttrack", "-noshorttrack", "-packet", "-noclose", + "" + }; + + /* are we pretending to be cdrecord ? */ + cpt= strrchr(argv[0],'/'); + if(cpt==NULL) + cpt= argv[0]; + else + cpt++; + if(strcmp(cpt,"cdrecord")==0 && !(flag&1)) { + fprintf(stderr,"\n"); + fprintf(stderr, + "Note: This is not cdrecord by Joerg Schilling. Do not bother him.\n"); + fprintf(stderr, + " See cdrskin start message on stdout. See --help. See -version.\n"); + fprintf(stderr,"\n"); + /* allow automatic -tao to -sao redirection */ + skin->tao_to_sao_tsize=650*1024*1024; + } + +#ifndef Cdrskin_extra_leaN + if(!(flag&2)) { + if(skin->preskin->pre_argc>1) { + ret= Cdrskin_setup(skin,skin->preskin->pre_argc,skin->preskin->pre_argv, + flag|1|2); + if(ret<=0) + return(ret); + } + } +#endif + + for (i= 1;i 3) + argpt++; + + /* is this a known option which is not planned to be implemented ? */ + /* such an option will not be accepted as data source */ + for(k=0;ignored_partial_options[k][0]!=0;k++) { + if(argpt[0]=='-') + if(strncmp(argpt+1,ignored_partial_options[k], + strlen(ignored_partial_options[k]))==0) + goto no_volunteer; + if(strncmp(argpt,ignored_partial_options[k], + strlen(ignored_partial_options[k]))==0) + goto no_volunteer; + } + for(k=0;ignored_full_options[k][0]!=0;k++) + if(strcmp(argpt,ignored_full_options[k])==0) + goto no_volunteer; + if(0) { +no_volunteer:; + skin->preskin->demands_cdrecord_caps= 1; + if(skin->preskin->fallback_program[0]) + fprintf(stderr,"cdrskin: NOTE : Unimplemented option: '%s'\n",argpt); + else + fprintf(stderr,"cdrskin: NOTE : ignoring unimplemented option : '%s'\n", + argpt); + 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); + } + + } else if(strcmp(argv[i],"--adjust_speed_to_drive")==0) { + skin->adjust_speed_to_drive= 1; + + } 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(argpt,"-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(argpt,"-audio")==0) { + skin->track_type= BURN_AUDIO; + skin->track_type_by_default= 0; + + } else if(strncmp(argpt,"-blank=",7)==0) { + cpt= argpt + 7; + goto set_blank; + } else if(strncmp(argpt,"blank=",6)==0) { + cpt= argpt + 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_if_needed")==0) { + skin->do_blank= 1; + skin->blank_format_type= 6; + skin->preskin->demands_cdrskin_caps= 1; + } 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(strncmp(cpt,"format_defectmgt",16)==0) { + skin->do_blank= 1; + skin->blank_format_type= 4|(3<<9); /* default payload size */ + skin->blank_format_size= 0; + skin->preskin->demands_cdrskin_caps= 1; + if(cpt[16]=='_') { + cpt+= 17; + if(strcmp(cpt,"none")==0) + skin->blank_format_type= 4|(1<<13); + else if(strcmp(cpt,"max")==0) + skin->blank_format_type= 4; /* smallest payload size above 0 */ + else if(strcmp(cpt,"min")==0) + skin->blank_format_type= 4|(2<<9); /*largest payload size with mgt*/ + else if(strncmp(cpt,"payload_",8)==0) { + skin->blank_format_size= Scanf_io_size(cpt+8,0); + skin->blank_format_type= 4; + } else if(strcmp(cpt,"cert_off")==0) + skin->blank_format_no_certify= 1; + else if(strcmp(cpt,"cert_on")==0) + skin->blank_format_no_certify= 0; + else + goto unsupported_blank_option; + } + skin->preskin->demands_cdrskin_caps= 1; + } else if(strncmp(cpt,"format_by_index_",16)==0) { + sscanf(cpt+16, "%d", &idx); + if(idx<0 || idx>255) { + fprintf(stderr,"cdrskin: SORRY : blank=%s provides unusable index\n", + cpt); + return(0); + } + skin->do_blank= 1; + skin->blank_format_type= 5|(2<<9)|(1<<15); + skin->blank_format_index= idx; + 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,"as_needed")==0) { + skin->do_blank= 1; + skin->blank_format_type= 7; + } else if(strcmp(cpt,"help")==0) { + /* is handled in Cdrpreskin_setup() */; + continue; + } else { +unsupported_blank_option:; + 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(strncmp(argv[i], "-cd_start_tno=", 14) == 0) { + value_pt= argv[i] + 14; + goto set_cd_start_tno; + } else if(strncmp(argv[i], "cd_start_tno=", 13) == 0) { + value_pt= argv[i] + 13; +set_cd_start_tno:; + cd_start_tno= -1; + sscanf(value_pt, "%d", &cd_start_tno); + if(cd_start_tno < 1 || cd_start_tno > 99) { + fprintf(stderr, + "cdrskin: FATAL : cd_start_tno= gives a number outside [1 ... 99]\n"); + return(0); + } + skin->cd_start_tno= cd_start_tno; + + } else if(strcmp(argv[i],"--cdtext_dummy")==0) { + skin->cdtext_test= 2; + + } else if(strncmp(argv[i], "-cdtext_to_textfile=", 20) == 0) { + value_pt= argv[i] + 20; + goto set_cdtext_to_textfile; + } else if(strncmp(argv[i], "cdtext_to_textfile=", 19) == 0) { + value_pt= argv[i] + 19; +set_cdtext_to_textfile:; + if(strlen(value_pt) >= sizeof(skin->cdtext_to_textfile_path)) { + fprintf(stderr, + "cdrskin: FATAL : cdtext_to_textfile=... too long. (max: %d, given: %d)\n", + (int) sizeof(skin->cdtext_to_textfile_path)-1,(int) strlen(value_pt)); + return(0); + } + skin->do_cdtext_to_textfile= 1; + strcpy(skin->cdtext_to_textfile_path, value_pt); + + } else if(strncmp(argv[i], "-cdtext_to_v07t=", 16) == 0) { + value_pt= argv[i] + 16; + goto set_cdtext_to_v07t; + } else if(strncmp(argv[i], "cdtext_to_v07t=", 15) == 0) { + value_pt= argv[i] + 15; +set_cdtext_to_v07t:; + if(strlen(value_pt) >= sizeof(skin->cdtext_to_vt07_path)) { + fprintf(stderr, + "cdrskin: FATAL : cdtext_to_vt07=... too long. (max: %d, given: %d)\n", + (int) sizeof(skin->cdtext_to_vt07_path)-1,(int) strlen(value_pt)); + return(0); + } + skin->do_cdtext_to_vt07= 1; + strcpy(skin->cdtext_to_vt07_path, value_pt); + + } else if(strcmp(argv[i],"--cdtext_verbose")==0) { + skin->cdtext_test= 1; + + } else if(strcmp(argpt,"-checkdrive")==0) { + skin->do_checkdrive= 1; + + } else if(strcmp(argpt,"-copy")==0) { + skin->track_modemods|= BURN_COPY; + skin->track_modemods&= ~BURN_SCMS; + + } else if(strncmp(argpt, "-cuefile=", 9)==0) { + value_pt= argpt + 9; + goto set_cuefile; + } else if(strncmp(argpt, "cuefile=", 8)==0) { + value_pt= argpt + 8; +set_cuefile:; + if(strlen(value_pt) >= sizeof(skin->cuefile)) { + fprintf(stderr, + "cdrskin: FATAL : cuefile=... too long. (max: %d, given: %d)\n", + (int) sizeof(skin->cuefile) - 1, (int) strlen(value_pt)); + return(0); + } + strcpy(skin->cuefile, value_pt); + skin->do_burn= 1; + + } else if(strcmp(argpt,"-data")==0) { +option_data:; + /* All Subsequent Tracks Option */ + skin->cdxa_conversion= (skin->cdxa_conversion & ~0x7fffffff) | 0; + 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; + + } else if(strcmp(argv[i],"--device_links")==0) { + skin->do_devices= 2; + +#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(argpt,"-dev=",5)==0) { + /* is handled in Cdrpreskin_setup() */; + } else if(strncmp(argpt,"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(argpt, "-driveropts=", 12)==0) { + value_pt= argpt + 12; + goto set_driveropts; + } else if(strncmp(argpt, "driveropts=", 11)==0) { + value_pt= argpt + 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(argpt+11,"noburnfree")==0 || + strcmp(argpt+11,"noburnproof")==0 ) { + skin->burnfree= 0; + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(printf("cdrskin: burnfree : off\n")); + } else if(strcmp(argpt+11,"help")==0) { + /* handled in Cdrpreskin_setup() */; + } else + goto ignore_unknown; + + } else if(strcmp(argpt,"-dummy")==0) { + skin->dummy_mode= 1; + + } else if(strncmp(argv[i], "-dvd_obs=", 9)==0) { + value_pt= argv[i] + 9; + goto dvd_obs; + } else if(strncmp(argv[i], "dvd_obs=", 8)==0) { + value_pt= argv[i] + 8; +dvd_obs:; + if(strcmp(value_pt, "default") == 0) + num= 0; + else + num = Scanf_io_size(value_pt,0); + if(num != 0 && num != 32768 && num != 65536) { + fprintf(stderr, + "cdrskin: SORRY : Option dvd_obs= accepts only sizes 0, 32k, 64k\n"); + } else + skin->dvd_obs= num; + + } else if(strcmp(argpt,"-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) + ClN(printf("cdrskin: ignoring obsolete eject_device=%s\n", + skin->eject_device)); + + } else if(strncmp(argv[i],"-extract_audio_to=", 18)==0) { + value_pt= argpt + 18; + goto extract_audio_to; + } else if(strncmp(argpt, "extract_audio_to=", 17) == 0) { + value_pt= argpt + 17; +extract_audio_to:; + if(strlen(value_pt) >= sizeof(skin->extract_audio_dir)) { + fprintf(stderr, + "cdrskin: FAILURE : extract_audio_to=... is much too long.\n"); + return(0); + } + skin->do_extract_audio= 1; + strcpy(skin->extract_audio_dir, value_pt); + + } else if(strncmp(argv[i],"-extract_tracks=", 16)==0) { + value_pt= argpt + 16; + goto extract_tracks; + } else if(strncmp(argpt, "extract_tracks=", 15) == 0) { + value_pt= argpt + 15; +extract_tracks:; + value= 0.0; + for(cpt= value_pt; ; cpt++) { + if(*cpt >= '0' && *cpt <= '9') { + value= value * 10 + *cpt - '0'; + } else { + if(value >= 1.0 && value <= 99.0) { + skin->extract_audio_tracks[(int) value]= 1; + if(skin->verbosity >= Cdrskin_verbose_cmD) + fprintf(stderr, "cdrskin: Will extract track number %.f\n", + value); + } else { + fprintf(stderr, + "cdrskin: WARNING : extract_tracks= with unsuitable number: %.f\n", + value); + } + if(*cpt == 0) + break; + value= 0; + } + } + + } else if(strncmp(argv[i],"-extract_basename=", 18)==0) { + value_pt= argpt + 18; + goto extract_basename; + } else if(strncmp(argpt, "extract_basename=", 17) == 0) { + value_pt= argpt + 17; +extract_basename:; + if(strchr(value_pt, '/') != NULL) { + fprintf(stderr, + "cdrskin: FAILURE : extract_basename=... may not contain '/'\n"); + return(0); + } + if(strlen(value_pt) > 248) { + fprintf(stderr, + "cdrskin: FAILURE : Oversized extract_basename=... (Max. 248)\n"); + return(0); + } + strcpy(skin->extract_basename, value_pt); + + } else if(strcmp(argv[i],"--extract_dap") == 0) { + skin->extract_flags|= 8; + +#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 */ + + } 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")); + + } else if(strcmp(argpt,"-force")==0) { + skin->force_is_set= 1; + + } else if(strcmp(argpt,"-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")); + + } else if(strcmp(argv[i],"--four_channel")==0) { + skin->track_modemods|= BURN_4CH; + +#ifndef Cdrskin_extra_leaN + + } else if(strncmp(argpt, "-fs=", 4) == 0) { + value_pt= argpt + 4; + goto fs_equals; + } else if(strncmp(argpt, "fs=", 3) == 0) { + value_pt= argpt + 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(argpt, "-gracetime=", 11) == 0) { + value_pt= argpt + 11; + goto gracetime_equals; + } else if(strncmp(argpt, "gracetime=", 10) == 0) { + value_pt= argpt + 10; +gracetime_equals:; + sscanf(value_pt,"%d",&(skin->gracetime)); + + } 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; + +#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(argpt,"-immed")==0) { + skin->modesty_on_drive= 1; + skin->min_buffer_percent= 75; + skin->max_buffer_percent= 95; + + } else if(strncmp(argpt, "-index=", 7) == 0) { + value_pt= argpt + 7; + goto set_index; + } else if(strncmp(argpt, "index=", 6) == 0) { + value_pt= argpt + 6; +set_index:; + if(skin->index_string != NULL) + free(skin->index_string); + skin->index_string= strdup(value_pt); + if(skin->index_string == NULL) { + fprintf(stderr, "cdrskin: FATAL : Out of memory\n"); + return(-1); + } + + } else if(strncmp(argv[i], "input_sheet_v07t=", 17)==0) { + if(skin->sheet_v07t_blocks >= 8) { + fprintf(stderr, + "cdrskin: SORRY : Too many input_sheet_v07t= options. (Max. 8)\n"); + return(0); + } + if(argv[i][17] == 0) { + fprintf(stderr, + "cdrskin: SORRY : Missing file path after option input_sheet_v07t=\n"); + return(0); + } + if(strlen(argv[i] + 17) > Cdrskin_adrleN) { + fprintf(stderr, + "cdrskin: SORRY : File path too long after option input_sheet_v07t=\n"); + return(0); + } + strcpy(skin->sheet_v07t_paths[skin->sheet_v07t_blocks], argv[i] + 17); + skin->sheet_v07t_blocks++; + skin->preskin->demands_cdrskin_caps= 1; + + } else if(strcmp(argpt,"-inq")==0) { + skin->do_checkdrive= 2; + + } else if(strcmp(argpt,"-isosize")==0) { + skin->use_data_image_size= 1; + + } else if(strncmp(argpt, "-isrc=", 6) == 0) { + value_pt= argpt + 6; + goto set_isrc; + } else if(strncmp(argpt, "isrc=", 5) == 0) { + value_pt= argpt + 5; +set_isrc:; + if(strlen(value_pt) != 12) { + fprintf(stderr, + "cdrskin: SORRY : isrc=... is not exactly 12 characters long.\n"); + return(0); + } + memcpy(skin->next_isrc, value_pt, 13); + + } else if(strcmp(argv[i],"--list_formats")==0) { + skin->do_list_formats= 1; + skin->preskin->demands_cdrskin_caps= 1; + + } else if(strcmp(argv[i],"--list_ignored_options")==0) { + /* is also handled in Cdrpreskin_setup() */; + + printf("cdrskin: List of all ignored options:\n"); + for(k=0;ignored_partial_options[k][0]!=0;k++) + printf("%s\n",ignored_partial_options[k]); + for(k=0;ignored_full_options[k][0]!=0;k++) + printf("%s\n",ignored_full_options[k]); + printf("\n"); + + } else if(strcmp(argv[i],"--list_speeds")==0) { + skin->do_list_speeds= 1; + skin->preskin->demands_cdrskin_caps= 1; + + } else if(strncmp(argv[i],"fallback_program=",17)==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argpt,"-load")==0) { + skin->do_load= 1; + + } else if(strcmp(argpt,"-lock")==0) { + skin->do_load= 2; + + } else if(strcmp(argv[i],"--long_toc")==0) { + skin->do_atip= 3; + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(printf("cdrskin: will put out some -atip style lines plus -toc\n")); + + } else if(strncmp(argpt,"-mcn=", 5) == 0) { + value_pt= argpt + 5; + goto set_mcn; + } else if(strncmp(argpt,"mcn=", 4) == 0) { + value_pt= argpt + 4; +set_mcn:; + if(strlen(value_pt) != 13) { + fprintf(stderr, + "cdrskin: SORRY : mcn=... is not exactly 13 characters long.\n"); + return(0); + } + memcpy(skin->mcn, value_pt, 14); + + } else if(strncmp(argpt, "-minbuf=", 8) == 0) { + value_pt= argpt + 8; + goto minbuf_equals; + } else if(strncmp(argpt, "minbuf=", 7) == 0) { + value_pt= argpt + 7; +minbuf_equals:; + 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 if(strcmp(argpt,"-minfo") == 0 || + strcmp(argpt,"-media-info") == 0) { + skin->do_atip= 4; + + } else if(strcmp(argpt, "-mode2") == 0) { + fprintf(stderr, + "cdrskin: NOTE : defaulting option -mode2 to option -data\n"); + goto option_data; + + } else if(strncmp(argv[i],"modesty_on_drive=",17)==0) { + 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 if(strcmp(argpt,"-multi")==0) { + skin->multi= 1; + + } else if(strncmp(argpt, "-msifile=", 9) == 0) { + value_pt= argpt + 9; + goto msifile_equals; + } else if(strncmp(argpt, "msifile=", 8) == 0) { + value_pt= argpt + 8; +msifile_equals:; + 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 if(strcmp(argpt,"-msinfo")==0) { + skin->do_msinfo= 1; + +#ifdef Libburn_develop_quality_scaN + + } else if(strcmp(argv[i],"--nec_optiarc_qcheck")==0) { + skin->do_qcheck= 1; + +#endif /* Libburn_develop_quality_scaN */ + + } 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_load")==0) { + skin->do_load= -1; + + } else if(strcmp(argv[i],"--no_rc")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argpt,"-nocopy")==0) { + skin->track_modemods&= ~BURN_COPY; + + } else if(strcmp(argpt,"-nopad")==0) { + skin->padding= 0.0; + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(printf("cdrskin: padding : off\n")); + + } else if(strcmp(argpt,"-nopreemp")==0) { + skin->track_modemods&= ~BURN_PREEMPHASIS; + + } else if(strcmp(argv[i],"--obs_pad")==0) { + skin->obs_pad= 1; + + } else if(strcmp(argv[i],"--old_pseudo_scsi_adr")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i], "--pacifier_with_newline") == 0) { + skin->pacifier_with_newline= 1; + + } else if(strcmp(argpt,"-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(argpt, "-padsize=", 9) == 0) { + value_pt= argpt + 9; + goto set_padsize; + } else if(strncmp(argpt, "padsize=", 8) == 0) { + value_pt= argpt + 8; +set_padsize:; + skin->padding= Scanf_io_size(value_pt, 0); + skin->set_by_padsize= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + ClN(printf("cdrskin: padding : %.f\n",skin->padding)); + + } else if(strcmp(argpt,"-preemp")==0) { + skin->track_modemods|= BURN_PREEMPHASIS; + + } else if(strcmp(argv[i],"--prodvd_cli_compatible")==0) { + skin->prodvd_cli_compatible= 1; + + } else if(strcmp(argpt,"-sao")==0 || strcmp(argpt,"-dao")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argpt,"-scanbus")==0) { + skin->do_scanbus= 1; + + } else if(strcmp(argpt,"-scms")==0) { + skin->track_modemods|= BURN_SCMS; + + } 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], "-sao_postgap=", 13) == 0) { + value_pt= argv[i] + 13; + goto set_sao_postgap; + } else if(strncmp(argv[i], "sao_postgap=", 12) == 0) { + value_pt= argv[i] + 12; +set_sao_postgap:; + skin->sao_postgap= -1; + if(strcmp(value_pt, "off") == 0) + skin->sao_postgap = -1; + else + sscanf(value_pt, "%d", &(skin->sao_postgap)); + if(skin->sao_postgap < 0) { + fprintf(stderr, + "cdrskin: SORRY : sao_postgap must be \"off\" or a number >= 0\n"); + return(0); + } + + } else if(strncmp(argv[i], "-sao_pregap=", 12) == 0) { + value_pt= argv[i] + 12; + goto set_sao_pregap; + } else if(strncmp(argv[i], "sao_pregap=", 11) == 0) { + value_pt= argv[i] + 11; +set_sao_pregap:; + skin->sao_pregap= -1; + if(strcmp(value_pt, "off") == 0) + skin->sao_pregap = -1; + else + sscanf(value_pt, "%d", &(skin->sao_pregap)); + if(skin->sao_pregap < 0) { + fprintf(stderr, + "cdrskin: SORRY : sao_pregap must be \"off\" or a number >= 0\n"); + return(0); + } + + } else if(strncmp(argpt, "-speed=", 7) == 0) { + value_pt= argpt + 7; + goto set_speed; + } else if(strncmp(argpt, "speed=", 6) == 0) { + value_pt= argpt + 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(strncmp(argv[i], "-stdio_sync=", 12)==0) { + value_pt= argv[i] + 12; + goto stdio_sync; + } else if(strncmp(argv[i], "stdio_sync=", 11)==0) { + value_pt= argv[i] + 11; +stdio_sync:; + if(strcmp(value_pt, "default") == 0 || strcmp(value_pt, "on") == 0) + num= 0; + else if(strcmp(value_pt, "off") == 0) + num= -1; + else + num = Scanf_io_size(value_pt,0); + if(num > 0) + num/= 2048; + if(num != -1 && num != 0 && (num < 32 || num > 512 * 1024)) { + fprintf(stderr, + "cdrskin: SORRY : Option stdio_sync= accepts only sizes -1, 0, 32k ... 1g\n"); + } else + skin->stdio_sync= num; + + } else if(strncmp(argv[i],"-stream_recording=",18)==0) { + value_pt= argv[i]+18; + goto set_stream_recording; + } else if(strncmp(argv[i],"stream_recording=",17)==0) { + value_pt= argv[i]+17; +set_stream_recording:; + if(strcmp(value_pt, "on")==0) + skin->stream_recording_is_set= 1; + else if(value_pt[0] >= '0' && value_pt[0] <= '9') { + num= Scanf_io_size(value_pt, 0); + num/= 2048.0; + if(num >= 16 && num <= 0x7FFFFFFF) + skin->stream_recording_is_set= num; + else + skin->stream_recording_is_set= 0; + } else + skin->stream_recording_is_set= 0; + } else if(strcmp(argpt,"-swab")==0) { + skin->swap_audio_bytes= 0; + + } else if(strcmp(argpt,"-tao")==0) { + /* is handled in Cdrpreskin_setup() */; + + } 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) + printf("cdrskin: size default for non-tao write modes: %.f\n", + skin->tao_to_sao_tsize); +#endif /* ! Cdrskin_extra_leaN */ + + } else if(strcmp(argv[i],"--tell_media_space")==0) { + skin->tell_media_space= 1; + skin->preskin->demands_cdrskin_caps= 1; + + } else if(strcmp(argpt, "-text") == 0) { + skin->use_cdtext= 1; + + } else if(strncmp(argpt, "-textfile=", 10) == 0) { + value_pt= argpt + 10; + goto set_textfile; + } else if(strncmp(argpt ,"textfile=", 9) == 0) { + value_pt= argpt + 9; +set_textfile:; + ret= Cdrskin_read_textfile(skin, value_pt, 0); + if(ret <= 0) + return(ret); + + } else if(strncmp(argpt, "-textfile_to_v07t=", 18) == 0) { + value_pt= argpt + 18; + goto textfile_to_v07t; + } else if(strncmp(argpt ,"textfile_to_v07t=", 17) == 0) { + value_pt= argpt + 17; +textfile_to_v07t:; + ret= Cdrskin_read_textfile(skin, value_pt, 0); + if(ret <= 0) + return(ret); + ret= Cdrskin_print_text_packs(skin, skin->text_packs, + skin->num_text_packs, (1 << 4) | 2); + if(ret <= 0) + return(ret); + if(i != 1 || argc != 2) + fprintf(stderr, "cdrskin: WARNING : Program run ended by option textfile_to_v07t=. Other options may have been ignored.\n"); + return(2); + + } else if(strcmp(argpt,"-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(argpt, "-tsize=", 7) == 0) { + value_pt= argpt + 7; + goto set_tsize; + } else if(strncmp(argpt, "tsize=", 6) == 0) { + value_pt= argpt + 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],"--two_channel")==0) { + skin->track_modemods&= ~BURN_4CH; + + } else if(strcmp(argv[i],"-V")==0 || strcmp(argpt, "-Verbose")==0) { + /* is handled in Cdrpreskin_setup() */; + } else if(strcmp(argv[i],"-v")==0 || strcmp(argpt,"-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(argpt,"-version")==0) { + /* is handled in Cdrpreskin_setup() and should really not get here */; + + } else if(strcmp(argpt,"-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(strcmp(argpt, "-xa") == 0) { + fprintf(stderr,"cdrskin: NOTE : defaulting option -xa to option -data\n"); + goto option_data; + + } else if(strcmp(argpt, "-xa1") == 0) { + /* All Subsequent Tracks Option */ + skin->cdxa_conversion= (skin->cdxa_conversion & ~0x7fffffff) | 1; + skin->track_type= BURN_MODE1; + skin->track_type_by_default= 0; + + } else if(strcmp(argpt, "-xa2") == 0) { + fprintf(stderr, + "cdrskin: NOTE : defaulting option -xa2 to option -data\n"); + goto option_data; + + } else if(strcmp(argv[i], "--xa1-ignore") == 0) { + skin->cdxa_conversion|= (1 << 31); + + } 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); + } + 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) + ; + 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); + } + if(skin->next_isrc[0]) + memcpy(skin->tracklist[skin->track_counter]->isrc, skin->next_isrc, 13); + skin->tracklist[skin->track_counter]->index_string= skin->index_string; + skin->tracklist[skin->track_counter]->sao_pregap= skin->sao_pregap; + skin->tracklist[skin->track_counter]->sao_postgap= skin->sao_postgap; + skin->index_string= NULL; + skin->sao_postgap= skin->sao_pregap= -1; + 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; + skin->next_isrc[0]= 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) { + ret= burn_drive_get_adr(&(skin->drives[skin->driveno]), adr); + if(ret<=0) + adr[0]= 0; + 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; kpacifier_with_newline ? "" : "\r", k, + skin->pacifier_with_newline ? "\n" : " "); + usleep(1000000); + } + fprintf(stderr, + "%scdrskin: held drive grabbed for %d seconds \n", + skin->pacifier_with_newline ? "" : "\r", k); + Cdrskin_release_drive(skin,0); + } + + if(skin->track_counter>0) { + skin->do_burn= 1; + +#ifndef Cdrskin_no_cdrfifO + if(!skin->do_direct_write) { + ret= Cdrskin_attach_fifo(skin,0); + if(ret<=0) + return(ret); + } +#endif /* ! Cdrskin_no_cdrfifO */ + + } + 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 bit0= do not scan for devices + @return <=0 error, 1 success +*/ +int Cdrskin_create(struct CdrskiN **o, struct CdrpreskiN **preskin, + int *exit_value, int flag) +{ + int ret, stdio_drive= 0, mem; + 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(Cleanup_handler_handlE, Cleanup_handler_funC, + Cleanup_handler_flaG); + else if(skin->preskin->abort_handler==2) + Cleanup_set_handlers(Cleanup_handler_handlE, Cleanup_handler_funC, + 2 | 8); + + if(!(flag & 1)) + printf("cdrskin: scanning for devices ...\n"); + fflush(stdout); + + if(skin->preskin->verbositypreskin,1); + + if(stdio_drive) { + mem= skin->drive_is_busy; + skin->drive_is_busy= 1; + ret= burn_drive_scan_and_grab(&(skin->drives),skin->preskin->device_adr,0); + skin->drive_is_busy= mem; + if(Cdrskin__is_aborting(0)) { + fprintf(stderr,"cdrskin: ABORT : Startup aborted\n"); + Cdrskin_abort(skin, 0); /* Never comes back */ + } + if(ret <= 0) { + fprintf(stderr,"cdrskin: FATAL : Failed to grab emulated stdio-drive\n"); + {*exit_value= 2; goto ex;} + } + skin->n_drives= 1; + skin->driveno= 0; + burn_drive_release(skin->drives[0].drive, 0); + } else if(flag & 1){ + skin->n_drives= 0; + skin->driveno= 0; + } else { + while (!burn_drive_scan(&(skin->drives), &(skin->n_drives))) { + usleep(20000); + /* >>> ??? set a timeout ? */ + } + if(skin->n_drives <= 0) + skin->driveno= -1; + } + + burn_msgs_set_severities(skin->preskin->queue_severity, + skin->preskin->print_severity, "cdrskin: "); + + /* This prints the eventual queued messages */ + Cdrpreskin_queue_msgs(skin->preskin,0); + + if(!(flag & 1)) + 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;} + if(Cdrskin__is_aborting(0)) + goto ex; + ret= Cdrskin_scanbus(skin, 1 | (2 * (skin->do_devices == 2))); + 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;} + if(Cdrskin__is_aborting(0)) + goto ex; + 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 > 0) { + if(Cdrskin__is_aborting(0)) + goto ex; + 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) { + if(Cdrskin__is_aborting(0)) + goto ex; + 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;} + if(Cdrskin__is_aborting(0)) + goto ex; + 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;} + if(Cdrskin__is_aborting(0)) + goto ex; + ret= Cdrskin_atip(skin, skin->do_atip == 4 ? 4 : + (skin->do_atip>1) | (2 * (skin->do_atip > 2))); + if(ret<=0) + {*exit_value= 7; goto ex;} + } + if(skin->do_cdtext_to_textfile) { + ret= Cdrskin_cdtext_to_file(skin, skin->cdtext_to_textfile_path, 15); + if(ret<=0) + {*exit_value= 18; goto ex;} + } + if(skin->do_cdtext_to_vt07) { + ret= Cdrskin_cdtext_to_file(skin, skin->cdtext_to_vt07_path, 1); + if(ret<=0) + {*exit_value= 19; goto ex;} + } + if(skin->do_extract_audio) { + ret= Cdrskin_extract_audio_to_dir(skin, skin->extract_audio_dir, + skin->extract_basename, + skin->extract_audio_tracks, + skin->extract_flags & 8); + if(ret<=0) + {*exit_value= 19; goto ex;} + } + if(skin->do_list_speeds) { + if(skin->n_drives<=0) + {*exit_value= 17; goto no_drive;} + if(Cdrskin__is_aborting(0)) + goto ex; + ret= Cdrskin_list_speeds(skin, 0); + if(ret<=0) + {*exit_value= 17; goto ex;} + } + if(skin->do_list_formats) { + if(skin->n_drives<=0) + {*exit_value= 16; goto no_drive;} + if(Cdrskin__is_aborting(0)) + goto ex; + ret= Cdrskin_list_formats(skin, 0); + if(ret<=0) + {*exit_value= 16; goto ex;} + } + if(skin->do_blank) { + if(skin->n_drives<=0) + {*exit_value= 8; goto no_drive;} + if(Cdrskin__is_aborting(0)) + goto ex; + ret= Cdrskin_blank(skin,0); + if(ret<=0) + {*exit_value= 8; goto ex;} + } + if(skin->do_direct_write) { + skin->do_burn= 0; + if(Cdrskin__is_aborting(0)) + goto ex; + ret= Cdrskin_direct_write(skin,0); + if(ret<=0) + {*exit_value= 13; goto ex;} + } + if(skin->do_burn || skin->tell_media_space) { + if(skin->n_drives<=0) + {*exit_value= 10; goto no_drive;} + if(Cdrskin__is_aborting(0)) + goto ex; + ret= Cdrskin_burn(skin,0); + if(ret<=0) + {*exit_value= 10; goto ex;} + } + +#ifdef Libburn_develop_quality_scaN + + if(skin->do_qcheck) { + ret= Cdrskin_qcheck(skin, 0); + if(ret<=0) + {*exit_value= 15; goto ex;} + } + +#endif /* Libburn_develop_quality_scaN */ + +ex:; + if(Cdrskin__is_aborting(0)) + Cdrskin_abort(skin, 0); /* Never comes back */ + 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, do_not_scan; + 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; ido_not_scan; /* preskin will vanish in Cdrskin_create */ + ret= Cdrskin_create(&skin,&preskin,&exit_value, preskin->do_not_scan); + if(ret<=0) + {exit_value= 2; goto ex;} + if(skin->n_drives<=0 && !do_not_scan) { + 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_new_api_tesT + ClN(fprintf(stderr,"cdrskin_debug: Compiled with option -experimental\n")); +#endif + } + + if(!Cdrskin__is_aborting(0)) + Cdrskin_run(skin,&exit_value,0); + +ex:; + if(Cdrskin__is_aborting(0)) + Cdrskin_abort(skin, 0); /* Never comes back */ + + 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/trunk/cdrskin/cdrskin_eng.html b/trunk/cdrskin/cdrskin_eng.html new file mode 100644 index 0000000..bb8d330 --- /dev/null +++ b/trunk/cdrskin/cdrskin_eng.html @@ -0,0 +1,562 @@ + + + + + + +cdrskin homepage english + + + + + +
+ +cdrskin logo: Doener mit Scharf + +

Homepage of

+

cdrskin

+ +

Limited cdrecord compatibility wrapper for libburn

+
+ +

+

Purpose:

+Burns preformatted data to CD, DVD, and BD media:
+CD-R, DVD-R, DVD-R DL, DVD+R, DVD+R DL, BD-R, CD-RW, +DVD-RW, DVD-RAM, DVD+RW, BD-RE +

+

+ +


+ +Direct hop to download links -> + +

+

Hardware requirements:

+About any CD, DVD, or BD recorder produced in the recent ten years. +
+libburn +supports recorders which are compliant to standards MMC-1 for CD and +MMC-5 for DVD or BD. Linux, FreeBSD, Solaris, and NetBSD allow to access drives +connected via SCSI, PATA (aka IDE, ATA), USB, or SATA. +
+

+ +

+

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 or higher the drive should not be under ide-scsi.
+
or FreeBSD (with libc, of course) :
+
ATA and SATA drives need atapicam running.
+
libcam has to be installed.
+
or Solaris (with libc, of course) :
+
Tested on kernel 5.11, hopefully suitable for older ones too.
+
or NetBSD (with libc, of course) :
+
Tested on 6.1.2 and 6.1.3
+
libpthread
+
is supposed to be a standard system component.
+
+

+ +

+

+GPL software included:
+

+
+
libburn-1.3.8
+
(founded by Derek Foreman and Ben Jansens, +developed and maintained since August 2006 by +Thomas Schmitt from team of libburnia-project.org) +
+
transfers data to CD, DVD, BD
+
+

+ +

+This program system has been tested on Intel/AMD with Linux, FreeBSD, +OpenSolaris, and NetBSD based operating systems.
+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 all DVD media, cdrskin is able to perform any recording job +which is possible with cdrecord. +Other than with cdrecord, option -multi is supported with many DVD types and +BD-R. Write mode -tao works with anything but quickly blanked DVD-RW and +DVD-R DL, which both support no -multi. +
+

+
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 access to the /dev files +as listed by option --devices. Linux, FreeBSD, and NetBSD demand rw-permission. +On Solaris it is r-permission and privileges "basic,sys_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 -minfo
+ +
Prepare CD-RW or DVD-RW for re-use, DVD-RAM or BD-RE for first use:
+
$ cdrskin -v dev=/dev/sg1 blank=as_needed -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 \
+
  blank=as_needed -eject padsize=300k my_image.iso
+ +
Write compressed afio archive on-the-fly +(not DVD-R DL or minimally blanked DVD-RW):
+
$ find . | afio -oZ - | \
+
  cdrskin -v dev=0,1,0 fs=32m speed=8 \
+
  blank=as_needed padsize=300k -
+ +
Write several sessions to the same CD, DVD-R[W] or DVD+R[/DL]:
+
$ cdrskin dev=/dev/hdc -v padsize=300k -multi 1.iso +
+
$ cdrskin dev=/dev/hdc -v padsize=300k -multi 2.iso +
+
$ cdrskin dev=/dev/hdc -v padsize=300k -multi 3.iso +
+
$ cdrskin dev=/dev/hdc -v padsize=300k 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 -multi run:
+
$ x=$(cdrskin dev=/dev/sr0 -multi \
+
  --tell_media_space 2>/dev/null)
+
$ echo "Available: $x blocks of 2048 data bytes"
+ +
Accelerate BD-RE writing to full nominal speed after the first 250 MB
+
$ cdrskin ... stream_recording=250m ... + +
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.) +
+
+
+
Learn to know a more versatile way to burn ISO 9660 formatted data
+
+Standalone ISO 9660 multi-session CD/DVD/BD tool +xorriso. +
+
+ +

+ +
+ + + +

+

+
Download as source code (see README):
+
cdrskin-1.3.8.tar.gz +(975 KB). +
+
cdrskin-1.3.8.tar.gz.sig
+
+(detached GPG signature for verification by +gpg --verify cdrskin-1.3.8.tar.gz.sig cdrskin-1.3.8.tar.gz +
+after gpg --keyserver keys.gnupg.net --recv-keys ABC0A854). +
+
+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. +
+ + + + +
+
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-1.3.6.pl01: +

    +
  • none
  • + +
+ +Bug fixes towards cdrskin-1.3.6.pl01: +
    +
  • Minimum drive buffer fill was measured by cdrskin before the buffer could get full
  • +
  • Wrong stack usage caused SIGBUS on sparc when compiled by gcc -O2
  • +
  • A failed blank run did not cause an error indication
  • +
  • A final fsync(2) was performed with stdio drives, even if not desired
  • + +
+ +
+ +

+

+

Development snapshot, version 1.3.9 :

+
Enhancements towards current stable version 1.3.8: +
    +
  • none yet
  • + +
+
+ +
Bug fixes towards cdrskin-1.3.8: +
    +
  • none yet
  • + +
+
+ +
 
+
README 1.3.9 +
cdrskin-1.3.9 --help
+
cdrskin-1.3.9 -help
+
man cdrskin (as of 1.3.9)
+
 
+
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 ; cdrskin/compile_cdrskin.sh +
+
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 download is intended for adventurous end users or +admins with full system sovereignty.
+
Source (./bootstrap is already applied, build tested, for more see +upcoming README ): +
+
+cdrskin-1.3.9.tar.gz +(975 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 and BD media. +

+ +
+ + +

+

+
Example for a setup of device permissions.
+
+Newer Linux distros enable rw-access for the desktop user automatically. +So try as normal user whether all your drives are found. +CD devices which offer no rw-permission will stay invisible. +
+
$ cdrskin --devices
+
If not all desired drives show up, become superuser and do again:
+ +
# cdrskin --devices
+
...
+
0  dev='/dev/sr0'  rwr-r- :  'TEAC' 'CD-ROM CD-532S'
+
1  dev='/dev/hdc'  rwrw-- :  'LITE-ON' 'LTR-48125S'
+
Most simple and most insecure is this equivalent +of the usual cdrecord permissions u+s,a+x:
+
# chmod a+rw /dev/sr0 /dev/hdc
+
+More secure is to put the permitted users into a group like +"floppy", to assign /dev/sr0 /dev/hdc to this group, +and to allow rw-access only to group members. +
+
# vi /etc/group
+
...
+
floppy:x:19:thomas,scdbackup
+
...
+
# chgrp floppy /dev/sr0 /dev/hdc
+
# chmod g+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.) +

+ + + +
+ + +

+

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 was a long time user of cdrecord and it worked 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 no problem with using it for +burning CDs. +
+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 : +
+xorriso, a standalone ISO 9660 multi-session CD/DVD/BD burn tool. +No mkisofs needed. +
+
+ +(a second source of above) +
+
+
+
+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/trunk/cdrskin/cdrskin_timestamp.h b/trunk/cdrskin/cdrskin_timestamp.h new file mode 100644 index 0000000..e55fcbd --- /dev/null +++ b/trunk/cdrskin/cdrskin_timestamp.h @@ -0,0 +1 @@ +#define Cdrskin_timestamP "2015.05.17.083420" diff --git a/trunk/cdrskin/changelog.txt b/trunk/cdrskin/changelog.txt new file mode 100644 index 0000000..9f1273c --- /dev/null +++ b/trunk/cdrskin/changelog.txt @@ -0,0 +1,12820 @@ +------------------------------------------------------------------------------ + 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 +* Enabled TAO +* Bug fixed: Trailing trash appended to .wav files caused error message + and, if exceeding fifo size, could even stall a burn. +* Multi-session CDs (for now restricted to write mode TAO), -multi, -msinfo +* Bug fixed: False speed with first pacifier cycle. Potential SIGFPE by NaN. +* 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 +* libisofs unbundled from libburn+cdrskin +* Hints for porting to other operating systems are now in sg-*.c + + +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 [1240] +cdrskin/changelog.txt +Next cdrskin-0.3.9 cycle + +23 Oct 2007 [1241] +libcevap/main.c +Preparations for lowercase class and function names + +23 Oct 2007 [1242] +libcevap/libdax_model.txt +Work goes on + +23 Oct 2007 [1243] +libcevap/cgen.c +libcevap/ctyp.c +Fixed a bug about arrays + +2007.10.24.184233 [1248] branch/ZeroFourZero +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 to 0.4.0 + +25 Oct 2007 [1249] branch/ZeroFourZero +- cdrskin/add_ts_changes_to_libburn_0_3_8 +- cdrskin/add_ts_changes_to_libburn_0_3_9 ++ cdrskin/add_ts_changes_to_libburn_0_4_0 ++ cdrskin/add_ts_changes_to_libburn_0_4_1 +Updated cdrskin tarball generator + +2007.10.25.091106 [1250] +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 to 0.4.1 + +25 Oct 2007 [1251] +- cdrskin/add_ts_changes_to_libburn_0_3_8 +- cdrskin/add_ts_changes_to_libburn_0_3_9 ++ cdrskin/add_ts_changes_to_libburn_0_4_0 ++ cdrskin/add_ts_changes_to_libburn_0_4_1 +Updated cdrskin tarball generator + +25 Oct 2007 [1252] branch/ZeroFourZero +cdrskin/cdrskin.c +Added forgotten help text lines + +2007.10.25.131841 [1253] +cdrskin/cdrskin.c +Added forgotten help text lines + +2007.10.27.090421 [1254] [1256] branch/ZeroFourZero +libburn/sg-linux.c +Reacted on cdwrite@ message about INT_MAX in cdrom.h of kernel 2.6.23 + +2007.10.27.075309 [1255] +libburn/sg-linux.c +Reacted on cdwrite@ message about INT_MAX in cdrom.h of kernel 2.6.23 + +29 Oct 2007 [1272] +- cdrskin/add_ts_changes_to_libburn_0_3_9 ++ cdrskin/add_ts_changes_to_libburn_0_4_0 +Updated cdrskin release generator scripts + +----------------------------- release - cdrskin-0.4.0.pl00 - 2007.10.27.090421 +* 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= + +------------------------------------ cycle - cdrskin-0.4.1 - 2007.10.27.114207 + +2007.11.18.093952 [1307] +libburn/libburn.h +Marked loss of binary backward compatibility back in rev 655, libburn-0.3.1 + +18 Nov 2007 [1308] +cdrskin/cdrskin.1 +Corrected a typo in cdrskin man page + +2007.11.18.094209 [1309] +cdrskin/cdrskin.c +Reacted on build warnings on a 64 Bit system + +2007.11.26.154817 [1310] +libburn/libdax_audioxtr.c +Reacted on build warnings on another system + +------------------------------------ cycle - cdrskin-0.4.1 - 2007.11.27.214003 + +2007.11.29.185342 [1312] +libburn/drive.c +libburn/mmc.c +libburn/spc.c +libburn/libdax_msgs.h +Enabled reading of TOC from ROM drives (direly needed for xorriso) + +29 Nov 2007 [1313] +test/telltoc.c +Adjusted meaning of --read_and_print count= -1 + +2007.12.07.185030 [1323] +configure.ac +An attempt to rectify .so numbering: SONAME=10, REV=1, AGE=6 + +2007.12.07.185206 [1324] +libburn/async.c +Made postponed change in thread management + +8 Dec 2007 [1325] +configure.ac +Some changes in the comments + +24 Dec 2008 [1338] +libburn/libburn.h +libburn/source.h +libburn/source.c +libburn/file.c +libburn/write.c +libburn/sector.c +Implemented burn_source.cancel() in a binary backwards compatible way + +2008.01.17.185051 [1383] +libburn/libdax_msgs.h +libburn/libdax_msgs.c +Changed meaning of .driveno to .origin, introduced LIBDAX_MSGS_ORIGIN_* macros + +17 Jan 2008 [1384] +cdrskin/README +Removed a reference to future GPL versions + +2008.01.19.201702 [1396] +libburn/read.c +Fixed small bug about error messages with burn_read_data + +2008.01.23.193345 [1405] +libburn/read.c +Made burn_read_data() issue messages about hopeless drive access errors + +2008.01.23.193843 [1406] +libburn/libburn.h +libburn/libdax_msgs.h +libburn/libdax_msgs.c +Introduced message severity "FAILURE" + +2008.01.23.211731 [1408] +cdrskin/cdrskin.c +configure.ac +Implemented run time check of libburn version. + +2008.01.23.213607 [1409] +Makefile.am +Dynamic cdrskin linking patch by Simon Huggins. + +26 Jan 2008 [1420] +cdrskin/convert_man_to_html.sh +Adapted to man -H on my new system + +2008.01.26.123054 [1421] +libburn/libdax_msgs.h +Ported change in vreixo message range from isoburn_msgs + +2008.01.26.131519 [1422] +libburn/drive.c +Made a sudden end to all stdio drives in burn_abort() + +2008.01.26.180241 [1426] [branch 1427] +libburn/async.c +Disabled debugging messages about thread properties + +2008.01.26.200001 [branch 1428] +Makefile.am +configure.ac +libburn-5.pc.in +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 to 0.4.2 , libburn.so.4.7.0 + +27 Jan [branch 1429] +cdrskin/cdrskin.c +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Adjustments after first round of testing + +27 Jan [branch 1430] +cdrskin/README +cdrskin/cdrskin_eng.html +Adjustments after testing + +2008.01.27.143022 [1431] +Makefile.am +configure.ac +libburn-5.pc.in +README +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/wiki_plain.txt +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Made number transition to 0.4.3 , still libburn.so.4.7.0 + +27 Jan 2008 [branch 1433] [1432] +- cdrskin/add_ts_changes_to_libburn_0_4_0 +- cdrskin/add_ts_changes_to_libburn_0_4_1 ++ cdrskin/add_ts_changes_to_libburn_0_4_2 ++ cdrskin/add_ts_changes_to_libburn_0_4_3 +Updated cdrskin tarball generator + +2008.01.29.210821 [1442] +configure.ac +libburn/libburn.h +Moving the major.minor.micro definition from configure.ac to libburn.h + +2008.01.28.213001 [branch 1444] +libburn/libburn.h +Introduced copy of major.minor.micro definition in libburn.h of version 0.4.2 + +29 Jan 2008 [branch 1445] [branch 1447] +Corrected description of major.minor.micro definition in libburn.h of version 0.4.2 + +2008.01.29.214110 [1446] +libburn/libburn.h +Corrected description of major.minor.micro definition in libburn.h + +2008.01.31.111057 [1448] +cdrskin/cdrskin.c +Introduced compile time check of libburn header version + +2008.02.01.100302 [1451] [branch 1453] +- libburn-5.pc.in ++ libburn-1.pc.in +configure.ac +Makefile.am +Renamed libburn-5.pc to libburn-1.pc + +2008.02.01.100530 [1452] +cdrskin/cdrskin.c +Changed "libburn interface :" version message to libburn.h macros + + [branch ] +cdrskin/cdrskin_timestamp.h +cdrskin/changelog.txt +Updated changelog before release + +----------------------------- release - cdrskin-0.4.2.pl00 - 2008.02.01.100001 +* Safe dynamic linking possible with libburn.so.4 + +------------------------------------ cycle - cdrskin-0.4.3 - 2008.02.01.225039 + +2008.02.03.092013 [1463] +libburn/libdax_msgs.h +Registered error code range "libisofs-xorriso" + +2008.02.03.092134 [1464] +configure.ac +Incremented LT_* to get libburn.so.4.8.0 (forgot to do 4.7.1 anyway) + +2008.02.03.092509 [1465] +libburn/libburn.h +libburn/init.c +New API call burn_sev_to_text() + +4 Feb 2008 [1473] +README +Announced deprecation of libisofs-0.2.x, temporory employment of libisofs-0.6.1 + +2008.02.04.175209 [1474] +libburn/libdax_msgs.h +Registered range "libisoburn" 0x00060000 to 0x00006ffff + +2008.02.06.174009 [1482] +libburn/libdax_msgs.h +Re-instated range "vreixo" with old and new codes, adjusted severity definitions + +2008.02.06.182222 [1483] +libburn/libburn.h +Copied usage discussion about *_header_version_* from libisoburn + +2008.02.06.230041 [1488] +libburn/read.c +Installed a simple address logger in burn_read_data + +2008.02.07.232820 [1492] +libburn/read.c +Added debug message in case of burn_read_data() return 0 + +2008.02.08.073414 [1493] +libburn/mmc.c +Allowed DVD +/- DL for reading + +2008.02.11.190802 [1518] +libburn/libdax_msgs.h +libburn/libdax_msgs.c +Introduced LIBDAX_MSGS_SEV_MISHAP + +2008.02.14.074108 [1522] +libburn/libburn.h +Micro corrections in comment text + +14 Feb 2008 [1523] +COPYRIGHT +Updated year + +14 Feb 2008 [1524] +cdrskin/README +Pointed to ldconfig + +2008.02.16.121102 [1541] +configure.ac +Corrected typo in comment + +16 Feb 2008 [1542] +doc/comments +README +cdrskin/wiki_plain.txt +Updated project interrelations + +2008.02.21.200956 [1554] +libburn/sg-linux.c +Testwise inquiry of ioctl(CDROM_MEDIA_CHANGED) (disabled by default) + +21 Feb 2008 [1555] +test/libburner.c +Directed error message to proper output channel + +2008.02.21.201238 [1556] +libburn/libdax_msgs.h +libburn/libdax_msgs.c +Introduced severity ERRFILE + +2008.02.21.202216 [1557] +libburn/libdax_msgs.c +Mapped unknown severity text to ALL rather than NEVER + +2008.02.21.215250 [1560] +libburn/libdax_msgs.h +Weakened demand to print file path in following message + +2008.02.22.150939 [1562] +libburn/libdax_msgs.h +libburn/init.c +New error code 0x00040008 + +2008.02.22.213527 [1563] +libburn/libdax_msgs.h +libburn/libdax_msgs.c +Imported changes from libiso_msgs.h + +2008.02.22.213726 [1564] +libburn/init.c +Changed meaning of unrecognized severity texts from FATAL to ALL + +2008.02.28.123152 [1565] +libburn/libburn.h +libburn/spc.c +libburn/mmc.c +libburn/write.c +cdrskin/cdrskin.1 +Made support for DVD+R DL official, enabled untested support for BD-RE + +2008.02.28.132325 [1586] +libburn/mmc.c +cdrskin/README +cdrskin/cdrskin.c +cdrskin/cdrskin_eng.html +cdrskin/wiki_plain.txt +test/libburner.c +Mentioned support for DVD+R/DL. + +------------------------------------ cycle - cdrskin-0.4.3 - 2008.03.01.154319 +* Support for DVD+R/DL media is now official +* Experimental code for BD-RE with --allow_untested_media + +1 Mar 2008 [1595] +cdrskin/changelog.txt +Updated changelog + +2008.03.03.202930 [1598] +libburn/libburn.h +libburn/drive.c +libburn/read.c +Got rid of a cumbersome open-close cycle with burn_read_data() on stdio: + +2 Apr 2008 [1654] +COPYRIGHT +Corrected GPL version + +------------------------------------ cycle - cdrskin-0.4.3 - 2008.04.07.152331 +* libburn: Improved read performance from stdio: pseudo-drives + +2008.04.08.100001 [ZeroFourFour 1669] +Makefile.am +configure.ac +README +libburn/libburn.h (burn_header_version_*) +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 + +8 Apr 2008 [ZeroFourFour 1670] +- cdrskin/add_ts_changes_to_libburn_0_4_2 +- cdrskin/add_ts_changes_to_libburn_0_4_3 ++ cdrskin/add_ts_changes_to_libburn_0_4_4 ++ cdrskin/add_ts_changes_to_libburn_0_4_5 +Updated cdrskin tarball generator + +8 Apr 2008 [ZeroFourFour 1671] +cdrskin/changelog.txt +Documented changes and release timestamp + +2008.04.08.100001 [ZeroFourFour 1672] +libburn/util.c +Switched from configure.ac versioning to libburn.h versioning + +8 Apr 2008 [ZeroFourFour 1673] +cdrskin/changelog.txt +Documented last minute changes + +----------------------------- release - cdrskin-0.4.4.pl00 - 2008.04.08.100001 +* Support for DVD+R/DL media is now official +* Experimental code for BD-RE with --allow_untested_media +* libburn: Improved read performance from stdio: pseudo-drives + + +2008.04.08.132344 [1674] +Makefile.am +configure.ac +README +libburn/libburn.h (burn_header_version_*) +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/wiki_plain.txt +cdrskin/cdrskin_eng.html +doc/comments +cdrskin/changelog.txt +Made number transition + +8 Apr 2008 [1675] +- cdrskin/add_ts_changes_to_libburn_0_4_2 +- cdrskin/add_ts_changes_to_libburn_0_4_3 ++ cdrskin/add_ts_changes_to_libburn_0_4_4 ++ cdrskin/add_ts_changes_to_libburn_0_4_5 +Updated cdrskin tarball generator + +2008.04.08.133452 [1676] +libburn/util.c +Switched from configure.ac versioning to libburn.h versioning + +------------------------------------ cycle - cdrskin-0.4.5 - 2008.04.08.134413 + +2008.04.10.211529 [1681] +libburn/mmc.c +libburn/drive.c +cdrskin/cdrskin.c +Trying to fix bugs about BD-RE, macro for simulating BD-RE on DVD-RAM + + +------------------------------------ cycle - cdrskin-0.4.5 - 2008.04.10.211529 + +2008.04.12.164244 [1683] +libburn/libburn.h +libburn/transport.h +libburn/options.h +libburn/options.c +libburn/write.c +libburn/drive.c +libburn/mmc.c +New API call burn_write_opts_set_stream_recording() + +2008.04.12.164606 [1684] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/cdrskin_eng.html +New option stream_recording=on|off + +------------------------------------ cycle - cdrskin-0.4.5 - 2008.04.12.164930 +* New option stream_recording=on can speed up DVD-RAM + + +2008.04.15.094133 [1685] +libburn/os-linux.h +libburn/write.c +Enforced tail padding with stream_recording, enlarged transport buffer + +15 Apr 2008 [1686] +cdrskin/cdrskin.1 +Some adjustments of cdrskin man page + +------------------------------------ cycle - cdrskin-0.4.5 - 2008.04.15.094545 + +2008.04.16.082208 [1687] +libburn/read.c +Made burn_read_data() obey its flag bit1 + +2008.04.18.092715 [1688] +libburn/libburn.h +libburn/async.c +libburn/drive.c +libburn/mmc.c +Began to implement formatting of DVD-RAM and experimentally of BD-RE + +2008.04.18.092816 [1689] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Began to implement formatting of DVD-RAM and experimentally of BD-RE + +2008.04.18.194602 [1691] +libburn/mmc.c +Adjustments with DVD-RAM formatting + +2008.04.22.161139 [1695] +libburn/libburn.h +libburn/transport.h +libburn/mmc.c +libburn/drive.c +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Adjustments with DVD-RAM formatting + +2008.04.22.200949 [1696] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New option --list_formats + +2008.04.23.110116 [1697] +libburn/mmc.c +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New blank type blank=format_by_index_ + +24 Apr 2008 [1698] +doc/cookbook.txt +Wrote down what was learned about DVD-RAM formatting + +24 Apr 2008 [1699] +cdrskin/cdrskin_eng.html +Mentioned new features + +2008.04.25.131531 [1700] +libburn/mmc.c +Preparations for formatting BD-RE + +2008.04.25.132353 [1701] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Documented experimental support for BD-RE formatting + +25 Apr 2008 [1702] +configure.ac +Incremented LT_CURRENT and LT_AGE to reflect API enhancements + +25 Apr 2008 [1703] +cdrskin/cdrskin_eng.html +Documented newest enhancements + +------------------------------------ cycle - cdrskin-0.4.5 - 2008.04.25.134438 +* New blank type blank=format_defectmgt for DVD-RAM +* New option --list_formats +* New blank type blank=format_by_index_ +* Experimental support for formatting BD-RE + + +2008.04.26.150646 [1704] +libburn/libburn.h +libburn/mmc.c +libburn/drive.c +doc/cookbook.txt +Enabled quick formatting with DVD-RAM, made slow formatting default with BD-RE + +2008.04.26.150945 [1705] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New blank subtypes format_defectmgt_cert_[on|off], on is default + +2008.04.27.084704 [1706] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New blank type format_if_needed + +2008.04.27.140144 [1707] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New blank type as_needed + +27 Apr 2008 [1708] +cdrskin/cdrskin_eng.html +Documented newest enhancements + +------------------------------------ cycle - cdrskin-0.4.5 - 2008.04.27.163625 +* New blank type blank=as_needed for automatic handling of media type and state + + +3 May 2008 [1723] +README +Updated project history + +2008.05.06.082429 [1729] +libburn/drive.c +Avoided to report negative burn_multi_caps.start_range_high with DVD-RW + +2008.05.06.084156 [1730] +libburn/mmc.c +Mapped undefined size to 0 with burn_disc_get_formats() and DVD-RW + +2008.05.06.180813 [1733] +libburn/libburn.h +libburn/drive.c +libburn/mmc.c +doc/cookbook.txt +doc/comments +test/libburner.c +Declared BD-RE to be supported + +2008.05.06.181100 [1734] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/wiki_plain.txt +Declared BD-RE to be supported + +2008.05.06.181723 [1735] +libburn/mmc.c +Declared BD-RE to be supported + +6 May 2008 [1736] +cdrskin/cdrskin_eng.html +cdrskin/cdrskin.1 +Declared BD-RE to be supported + +------------------------------------ cycle - cdrskin-0.4.5 - 2008.05.06.183436 +* Support for BD-RE media is now official + +2008.05.09.143130 [1748] +libburn/libburn.h +Documented read-only profiles CD-ROM and DVD-ROM + +2008.05.09.145205 [1749] +libburn/mmc.c +Allowed BD-ROM and BD-R for read-only purposes + +9 May 2008 [1750] +cdrskin/cdrskin.1 +Changed blank examples to blank=as_needed + + +------------------------------------ cycle - cdrskin-0.4.5 - 2008.05.09.151327 + +2008.05.10.080001 [1754] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/wiki_plain.txt +cdrskin/cdrskin_eng.html +doc/comments +cdrskin/cdrskin.1 +Made number transition to 0.4.6 and activated development documentation + +10 May 2008 [1755] +cdrskin/README +Made number transition to 0.4.6 and activated development documentation + +10 May 2008 [1756] +- cdrskin/add_ts_changes_to_libburn_0_4_4 +- cdrskin/add_ts_changes_to_libburn_0_4_5 ++ cdrskin/add_ts_changes_to_libburn_0_4_6 ++ cdrskin/add_ts_changes_to_libburn_0_4_7 +Updated cdrskin tarball generator + +10 May 2008 [1757] +cdrskin/changelog.txt +Documented changes and release timestamp + + +----------------------------- release - cdrskin-0.4.6.pl00 - 2008.05.10.080001 +* Support for BD-RE media is now official +* New option stream_recording=on can speed up DVD-RAM and BD-RE +* New option --list_formats +* New blank types for expert formatting of DVD-RAM and BD-RE +* New blank type blank=as_needed for automatic handling of media type and state + + +2008.05.10.132543 [1758] +Makefile.am +configure.ac +README +libburn/libburn.h (burn_header_version_*) +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/wiki_plain.txt +cdrskin/cdrskin_eng.html +doc/comments +cdrskin/cdrskin.1 +Made number transition to 0.4.7 + +10 May 2008 [1759] +- cdrskin/add_ts_changes_to_libburn_0_4_4 +- cdrskin/add_ts_changes_to_libburn_0_4_5 ++ cdrskin/add_ts_changes_to_libburn_0_4_6 ++ cdrskin/add_ts_changes_to_libburn_0_4_7 +Updated cdrskin tarball generator + +10 May 2008 [1760] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-0.4.7 - 2008.05.10.132543 + +2008.05.14.165025 [1776] +libburn/write.c +Bug fix: random access addressing for DVD-RAM and BD-RE did not work + +2008.05.14.165157 [1777] +libburn/libburn.h +Added format types 0x30 and 0x32 to list in API comments + +2008.05.14.165258 [1778] +cdrskin/cdrskin.c +Made inability to get format list a reason to abort the program + +------------------------------------ cycle - cdrskin-0.4.7 - 2008.05.14.165258 +* Bug fix: random access addressing for DVD-RAM and BD-RE did not work + + +2008.05.17.080001 [1788] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +cdrskin/cdrskin.1 +Made number transition to 0.4.8 and activated development documentation + +17 May 2008 [1789] +- cdrskin/add_ts_changes_to_libburn_0_4_6 +- cdrskin/add_ts_changes_to_libburn_0_4_7 ++ cdrskin/add_ts_changes_to_libburn_0_4_8 ++ cdrskin/add_ts_changes_to_libburn_0_4_9 +Updated cdrskin tarball generator + +17 May 2008 [1792] +cdrskin/changelog.txt +Documented changes and release timestamp + +----------------------------- release - cdrskin-0.4.8.pl00 - 2008.05.17.080001 +* Bug fix: random access addressing for DVD-RAM and BD-RE did not work + + +2008.05.17.115434 [1790] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +cdrskin/cdrskin.1 +Made number transition to 0.4.9 + +17 May 2008 [1791] +- cdrskin/add_ts_changes_to_libburn_0_4_6 +- cdrskin/add_ts_changes_to_libburn_0_4_7 ++ cdrskin/add_ts_changes_to_libburn_0_4_8 ++ cdrskin/add_ts_changes_to_libburn_0_4_9 +Updated cdrskin tarball generator + +17 May 2008 [1793] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-0.4.9 - 2008.05.17.121250 + +14 Jun 2008 [1852] +README +Updated release history + +2008.06.14.140711 [1853] +libburn/libburn.h +Inserted @since tags for all functions older than 0.2.0 + +2008.07.02.093933 [1882] +libburn/sg-linux.c +With auto device family: scd is now fallback if sr does not exist + +------------------------------------ cycle - cdrskin-0.4.9 - 2008.07.12.164045 +* Ability to use /dev/scd as fallback if /dev/sr does not exist +* Bug fix: option drive_scsi_dev_family=scd lead to buffer overflow + + +2008.07.14.112935 [1914] +libburn/sg-linux.c +libburn/libdax_msgs.h +Trying to avoid SORRY messages when hitting busy hard disk /dev/hdX + +2008.07.14.113050 [1915] +cdrskin/cdrskin.c +Making visible the new NOTE and HINT about busy alleged hard disks + +2008.07.14.113152 [1916] +configure.ac +Did LT_CURRENT++, LT_AGE++ because of new API call + +2008.07.14.113903 [1917] +libburn/libburn.h +libburn/file.c +libburn/async.c +New API call burn_fifo_peek_data() + +2008.07.14.164528 [1922] +libburn/sg-linux.c +Followed hint of Giulio Orsero to recognize disk by /proc/ide/hdX/media + +------------------------------------ cycle - cdrskin-0.4.9 - 2008.07.14.164906 +* New API call burn_fifo_peek_data() + +2008.07.16.070001 [1927] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.5.0 and activated development documentation + +16 Jul 2008 [1928] +- cdrskin/add_ts_changes_to_libburn_0_4_8 +- cdrskin/add_ts_changes_to_libburn_0_4_9 ++ cdrskin/add_ts_changes_to_libburn_0_5_0 ++ cdrskin/add_ts_changes_to_libburn_0_5_1 +Updated cdrskin tarball generator + +16 Jul 2008 [1929] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.5.0.pl00 - 2008.07.16.070001 +* Ability to use /dev/scd as fallback if /dev/sr does not exist +* Bug fix: option drive_scsi_dev_family=scd lead to buffer overflow +* New API call burn_fifo_peek_data() + + +2008.07.16.090816 [1930] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.5.1 and activated development documentation + +16 Jul 2008 [1931] +- cdrskin/add_ts_changes_to_libburn_0_4_8 +- cdrskin/add_ts_changes_to_libburn_0_4_9 ++ cdrskin/add_ts_changes_to_libburn_0_5_0 ++ cdrskin/add_ts_changes_to_libburn_0_5_1 +Updated cdrskin tarball generator + +16 Jul 2008 [1932] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-0.5.1 - 2008.07.14.164528 + + +2008.08.01.101053 [1954] +libburn/drive.h +libburn/drive.c +libburn/sg-linux.c +Avoiding drive scan if single drive is given + +2008.08.05.175930 [1963] +libburn/os-linux.h +libburn/drive.h +libburn/drive.c +libburn/sg-linux.c +libburn/libdax_msgs.h +cdrskin/cdrskin.1 +Taking into respect drive list from /proc/sys/dev/cdrom/info + +5 Aug 2008 [1964] +cdrskin/cdrskin_eng.html +Updated for next 0.5.1 cycle + +------------------------------------ cycle - cdrskin-0.5.1 - +* Larger set of possibly acceptable drive device file names + + +2008.08.09.071742 [1975] +libburn/mmc.c +libburn/structure.h +libburn/structure.c +CD burn_toc_entries now bear extension_valid data + +2008.08.09.071854 [1976] +libburn/libburn.h +libburn/read.c +New flag bit2 with burn_read_data() + +2008.08.19.122535 [1991] +libburn/libburn.h +API clarification about CD burn_toc_entries + +2008.08.19.123513 [1992] +libburn/structure.c +Reacted on harmless compiler warning + +------------------------------------ cycle - cdrskin-0.5.1 - 2008.08.19.123513 + +2008.08.20.080001 [1994] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.5.2 and activated development documentation + +20 Aug 2008 [1995] +- cdrskin/add_ts_changes_to_libburn_0_5_0 +- cdrskin/add_ts_changes_to_libburn_0_5_1 ++ cdrskin/add_ts_changes_to_libburn_0_5_2 ++ cdrskin/add_ts_changes_to_libburn_0_5_3 +Updated cdrskin tarball generator + +20 Aug 2008 [1996] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.5.2.pl00 - 2008.08.20.080001 +* Larger set of possibly acceptable drive device file names + +2008.08.20.100045 [1997] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.5.3 + +20 Aug 2008 [1998] +- cdrskin/add_ts_changes_to_libburn_0_5_0 +- cdrskin/add_ts_changes_to_libburn_0_5_1 ++ cdrskin/add_ts_changes_to_libburn_0_5_2 ++ cdrskin/add_ts_changes_to_libburn_0_5_3 +Updated cdrskin tarball generator + +20 Aug 2008 [1999] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-0.5.3 - 2008.08.20.110457 + +30 Aug 2008 [2023] +README +Mentioned release of libisoburn-0.2.4 + +2008.08.30.104339 [2024] +libburn/mmc.c +libburn/spc.c +Issueing many SCSI error messages in cleartext now + +2008.09.09.131915 [2039] +libburn/sg-linux.c +Trying to avoid unnecessary access to sibling device objects + +12 Sep 2008 [2043] +doc/cookbook.txt +Described ISO 9660 multi-session on overwriteable media + +2008.09.14.174344 [2048] +libburn/util.c +Gave up problematic and unused version.h + +2008.09.16.060250 [2052] +cdrskin/cdrskin.c +Corrected pacifier text (Ticket 141) + +24 Sep 2008 [2079] +README +Mentioned recent releases of libisofs and libisoburn + +2008.09.28.193802 [2086] +libburn/spc.c +Bug fix: Potential buffer overflow introduced with revision 2024 + +2008.09.28.211741 [2087] +libburn/sg-linux.c +Bug fix: /dev/sr0 was accepted as enumerable address on Linux 2.4 + +2008.10.04.072657 [2096] +libburn/write.c +libburn/read.c +libburn/drive.c +Prevented SIGSEGV after illegal drive operations during sync write + +2008.10.04.072657 [2097] +cdrskin/cdrskin.1 +Mentioned new xorriso capabilities in man cdrskin + +2008.10.04.073814 [2098] +configure.ac +Incremented LT_CURRENT and LT_AGE to get libburn.so.4.18.0 + +------------------------------------ cycle - cdrskin-0.5.3 - 2008.10.04.072657 +* Bug fix: /dev/sr0 was accepted as enumerable address on Linux 2.4 + + +2008.10.05.073001 [2102] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition + +5 Oct 2008 [2103] +- cdrskin/add_ts_changes_to_libburn_0_5_2 +- cdrskin/add_ts_changes_to_libburn_0_5_3 ++ cdrskin/add_ts_changes_to_libburn_0_5_4 ++ cdrskin/add_ts_changes_to_libburn_0_5_5 +Updated cdrskin tarball generator + +5 Oct 2008 [2104] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.5.4.pl00 - 2008.10.06.073001 +* Bug fix: /dev/sr0 was accepted as enumerable address on Linux 2.4 +* Issueing many SCSI error messages in cleartext now + + +2008.10.05.123737 [2106] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to next development cycle + +5 Oct 2008 [2107] +- cdrskin/add_ts_changes_to_libburn_0_5_2 +- cdrskin/add_ts_changes_to_libburn_0_5_3 ++ cdrskin/add_ts_changes_to_libburn_0_5_4 ++ cdrskin/add_ts_changes_to_libburn_0_5_5 +Updated cdrskin tarball generator + +5 Oct 2008 [2108] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-0.5.5 - 2008.10.05.130109 + + +2008.10.15.103224 [2126] +libburn/libburn.h +A clarification in comment about burn_disc_format() + +15 Oct 2008 [2127] +cdrskin/cdrskin.1 +Fixed incomplete sentence in man cdrskin + +2008.11.01.121240 [2157] +libburn/libburn.h +libburn/async.c +Bug fix: Unsuitable write modes were caught silently and later than desired + +------------------------------------ cycle - cdrskin-0.5.5 - 2008.11.01.121445 + +2008.11.08.134828 [2173] +libburn/libburn.h +Clarified behavior of burn_source with pipes + +2008.11.08.141734 [2174] +libburn/file.h +libburn/file.c +libburn/async.c +Cancelling libburn fifo thread before freeing the fifo object + +2008.11.08.202456 [2175] +libburn/async.c +Disabling the sigsegv provoking new debug message + +2008.11.12.075111 [2180] +libburn/async.h +Avoiding warning message about implicitely declared burn_fifo_abort() + +------------------------------------ cycle - cdrskin-0.5.5 - 2008.11.12.075411 +* Bug fix: libburn fifo thread was not aborted when burn run was aborted + +12 Nov 2008 [2181] +svn copy http://svn.libburnia-project.org/libburn/trunk + http://svn.libburnia-project.org/libburn/branches/ZeroFiveSix +Preparing for libburn-0.5.6 + +12 Nov 2008 [2182] +svn delete http://svn.libburnia-project.org/libburn/branches/ZeroFourSix +Removing obsolete libburn branch ZeroFourSix + +2008.11.12.120001 [2183] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.5.6 + +12 Nov 2008 [2184] +- cdrskin/add_ts_changes_to_libburn_0_5_4 +- cdrskin/add_ts_changes_to_libburn_0_5_5 ++ cdrskin/add_ts_changes_to_libburn_0_5_6 ++ cdrskin/add_ts_changes_to_libburn_0_5_7 +Updated cdrskin tarball generator + +12 Nov 2008 [2185] +cdrskin/changelog.txt +Documented changes and release timestamp + +12 Nov 2008 [2186] +cdrskin/cdrskin.c +Corrected wrong version number of cdrskin + +------------------------------ release - cdrskin-0.5.6.pl00 - 2008.11.12.120001 +* Bug fix: libburn fifo thread was not aborted when burn run was aborted +which could lead to use of freed memory + + +2008.11.12.121832 [2187] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.5.7 + +12 Nov 2008 [2188] +- cdrskin/add_ts_changes_to_libburn_0_5_4 +- cdrskin/add_ts_changes_to_libburn_0_5_5 ++ cdrskin/add_ts_changes_to_libburn_0_5_6 ++ cdrskin/add_ts_changes_to_libburn_0_5_7 +Updated cdrskin tarball generator + +12 Nov 2008 [2189] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-0.5.7 - 2008.11.12.130026 + +12 Nov 2008 [2191] +svn move \ + http://svn.libburnia-project.org/libburn/"$svn_name" \ + http://svn.libburnia-project.org/libburn/"$tag_name" +libburn release 0.5.6 is ready + +2008.11.15.220652 [2197] +libburn/sg-freebsd.c +libburn/sg-freebsd-port.c +Removed remark that use of statvfs() was untested with FreeBSD + +21 Nov 2008 [2208] +README +cdrskin/README +cdrskin/cdrskin_eng.html +Mentioned FreeBSD peculiarities in our docs + +2008.11.26.210608 [2213] +libburn/structure.c +Added tests against the SIGSEGV of ticket 146 + +2008.11.27.081027 [2214] +libburn/libdax_msgs.h +libburn/structure.c +Truncating eventually detected damaged CD table-of-content + +------------------------------------ cycle - cdrskin-0.5.7 - 2008.11.27.081027 +* Bug fix: Session without leadout entry on CD caused SIGSEGV + + +2008.11.27.172124 [2215] +libburn/libdax_msgs.h +libburn/structure.c +Changed error severity with TOC truncation to MISHAP + +2008.11.29.140115 [2217] +libburn/spc.c +Translating ASC=0x31 formatting error messages, reporting command names + +2008.11.29.140404 [2218] +libburn/mmc.c +Circumventing BD-RE Quick Certification refusal of LG GGW-H20L YL03 + +------------------------------------ cycle - cdrskin-0.5.7 - 2008.11.29.140404 + + +2008.12.03.085219 [2239] +libburn/mmc.c +libburn/structure.c +libburn/libdax_msgs.h +Defaulting sessions without leadout entry + +------------------------------------ cycle - cdrskin-0.5.7 - 2008.12.03.085219 + + +Dec 07 2008 [2248] +svn copy http://svn.libburnia-project.org/libburn/trunk + http://svn.libburnia-project.org/libburn/branches/ZeroFiveEight +Preparing for libburn-0.5.8 + +2008.12.07.140001 [2249] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.5.8 + +Dec 07 2008 [2250] +- cdrskin/add_ts_changes_to_libburn_0_5_6 +- cdrskin/add_ts_changes_to_libburn_0_5_7 ++ cdrskin/add_ts_changes_to_libburn_0_5_8 ++ cdrskin/add_ts_changes_to_libburn_0_5_9 +Updated cdrskin tarball generator + +Dec 07 2008 [2251] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.5.8.pl00 - 2008.12.07.140001 +* Bug fix: A session without leadout entry on CD caused a SIGSEGV by NULL +* Improvements about BD-RE formatting + + +2008.12.07.155219 [2252] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.5.9 + +Dec 07 2008 [2253] +- cdrskin/add_ts_changes_to_libburn_0_5_6 +- cdrskin/add_ts_changes_to_libburn_0_5_7 ++ cdrskin/add_ts_changes_to_libburn_0_5_8 ++ cdrskin/add_ts_changes_to_libburn_0_5_9 +Updated cdrskin tarball generator + +Dec 07 2008 [2254] +cdrskin/changelog.txt +Documented changes and release timestamp + +Dec 07 2008 [2255] +svn move -m "libburn release 0.5.8 is ready" \ + http://svn.libburnia-project.org/libburn/branches/ZeroFiveEigh \ + http://svn.libburnia-project.org/libburn/tags/ZeroFiveEight + +svn delete -m 'Deleted obsolete branch' \ + http://svn.libburnia-project.org/libburn/branches/"$svn_name" + + +------------------------------------ cycle - cdrskin-0.5.9 - 2008.12.07.155219 + +2008.12.09.123314 [2264] +libburn/libburn.h +libburn/drive.c +libburn/async.c +libburn/write.c +libburn/mmc.c +Beginning to implement write code for BD-R SRM without POW + +2008.12.09.123558 [2265] +cdrskin/cdrskin.c +Beginning to implement write code for BD-R SRM without POW + +2008.12.10.092535 [2271] +libburn/transport.h +libburn/async.c +libburn/mmc.h +libburn/mmc.c +Formatting of BD-R SRM to default size and by index + +2008.12.10.114241 [2273] +cdrskin/cdrskin.c +Formatting of BD-R SRM to default size and by index + +10 Dec 2008 [2274] +README +cdrskin/README +cdrskin/cdrskin.1 +cdrskin/wiki_plain.txt +Mentioning BD-R in documentation + +2008.12.11.072308 [2276] +libburn/libdax_msgs.h +libburn/async.c +Rejecting unformattable BD-R more early + +2008.12.11.092204 [2278] +libburn/libdax_msgs.h +libburn/async.c +libburn/mmc.c +cdrskin/cdrskin.1 +Catching BD-R zero spare formatting with NOTE rather than SORRY + +2008.12.12.112129 [2279] +libburn/libburn.h +libburn/async.c +libburn/mmc.c +Making format size of BD-RE and BD-R quite freely adjustable + +2008.12.12.214013 [2280] +libburn/transport.h +libburn/async.c +libburn/mmc.c +Interpreting feature 0023h for BD formatting capabilities + +2008.12.13.144909 [2284] +configure.ac +Now producing libburn.so.4.24.0 + +------------------------------------ cycle - cdrskin-0.5.9 - 2008.12.13.144909 +* Formatting and writing of BD-R media + + +14 Dec 2008 [2289] +test/libburner.c +Updated libburner to BD-R + +14 Dec 2008 [2290] +cdrskin/cdrskin_eng.html +Updated cdrskin web page + +2008.12.17.091905 [2297] +libburn/libburn.h +libburn/drive.c +New API function burn_get_read_capacity() + +17 Dec 2008 [2298] [2302] +doc/comments +Removed project overview and references to libisofs and libisoburn + +------------------------------------ cycle - cdrskin-0.5.9 - 2008.12.19.070912 +* New API function burn_get_read_capacity() + + +2008.12.19.203523 [2306] +libburn/libburn.h +Clarified blank, appendable, closed burn_disc_status + +2008.12.22.130527 [2323] +libburn/async.c +Fixed denial of fast formatting with BD-RE introduced by revision 2280 + +------------------------------------ cycle - cdrskin-0.5.9 - 2008.12.23.144853 + + +2008.12.29.105341 [2342] +libburn/libburn.h +test/libburner.c +Cosmetic changes + +Jan 02 2009 [2346] +svn copy http://svn.libburnia-project.org/libburn/trunk + http://svn.libburnia-project.org/libburn/branches/ZeroSixZero +Preparing for libburn-0.6.0 + +02 Jan 2009 [2347] +COPYRIGHT +libburn/libdax_msgs.c +libburn/libdax_msgs.h +test/libburner.c +Updated copyright claims to year 2009 + +2009.01.02.160001 [2348] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.6.0 + +02 Jan 2009 [2349] +- cdrskin/add_ts_changes_to_libburn_0_5_8 +- cdrskin/add_ts_changes_to_libburn_0_5_9 ++ cdrskin/add_ts_changes_to_libburn_0_6_0 ++ cdrskin/add_ts_changes_to_libburn_0_6_1 +Updated cdrskin tarball generator + +02 Jan 2009 [2350] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.6.0.pl00 - 2009.01.02.160001 +* Formatting and writing of BD-R media +* New API function burn_get_read_capacity() + +2009.01.04.112716 [2351] +COPYRIGHT +libburn/libdax_msgs.c +libburn/libdax_msgs.h +test/libburner.c +Updated copyright claims to year 2009 + +2009.01.04.113401 [2352] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.6.1 + +04 Jan 2009 [2353] +- cdrskin/add_ts_changes_to_libburn_0_5_8 +- cdrskin/add_ts_changes_to_libburn_0_5_9 ++ cdrskin/add_ts_changes_to_libburn_0_6_0 ++ cdrskin/add_ts_changes_to_libburn_0_6_1 +Updated cdrskin tarball generator + +03 Jan 2009 [2354] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-0.6.1 - 2009.01.04.113401 + +04 Jan 2009 [2356] +svn move +http://svn.libburnia-project.org/libburn/branches/ZeroSixZero +http://svn.libburnia-project.org/libburn/tags/ZeroSixZero +libburn release 0.6.0 is ready + +2009.01.06.122534 [2366] +libburn/spc.c +Error texts for ASC 73 : power calibration and program memory + +2009.01.06.122808 [2367] [2369] +libburn/async.c +libburn/libdax_msgs.h +Complaining and refusing more early with unformatted BD-RE + +7 Jan 2009 [2370] +cdrskin/cdrskin.1 +Clarification about one-time DVD and BD media + +2009.01.07.154414 [2371] +libburn/write.c +Bug fix: BD-R were not correctly finalized + +7 Jan 2009 [2372] +cdrskin/cdrskin_eng.html +Mentioned bug fix and pl01 + +------------------------------------ cycle - cdrskin-0.6.1 - 2009.01.07.154414 + +7 Jan 2009 [2373] +svn copy -m "Branching for libburn bugfix release 0.6.0.pl01" \ + http://svn.libburnia-project.org/libburn/ \ + http://svn.libburnia-project.org/libburn/ + +7 Jan 2009 [2374] +svn rm -m 'Removing falsly copied tag' \ + http://svn.libburnia-project.org/libburn/libburn + +7 Jan 2009 [2375] +svn copy -m "Branching for libburn bugfix release 0.6.0.pl01" \ + http://svn.libburnia-project.org/libburn/tags/ZeroSixZero \ + http://svn.libburnia-project.org/libburn/branches/ZeroSixZeroPl01 + +2009.01.07.140001 [branch 2376] +README +cdrskin/README +cdrskin/cdrskin_eng.html +cdrskin/cdrskin_timestamp.h +Mentioned bug fix and pl01 + +2009.01.07.140001 [branch 2377] +libburn/write.c +Bug fix: BD-R were not correctly finalized + +7 Jan 2009 [2378] +svn move -m 'libburn bugfix release 0.6.0.pl01 is ready' \ + http://svn.libburnia-project.org/libburn/branches/ZeroSixZeroPl01 \ + http://svn.libburnia-project.org/libburn/tags/ZeroSixZeroPl01 + +------------------------------------ cycle - cdrskin-0.6.1 - 2009.01.07.154414 + +2009.01.09.095943 [2381] +libburn/transport.h +libburn/write.c +libburn/mmc.c +libburn/libdax_msgs.h +Recognizing BD-R media spoiled by the close bug and handling them as appendable + +------------------------------------ cycle - cdrskin-0.6.1 - 2009.01.09.100210 + +2009.01.09.161704 [2383] +libburn/write.c +Preventing a possible bug with a burn run of more than one session at once + +2009.01.11.102711 [2390] +libburn/write.c +libburn/libdax_msgs.h +Prepared eventual closing of spoiled BD-R media by a pseudo write run + +11 Jan 2009 [2391] +doc/cookbook.txt +Updated cookbook about BD-R media + +27 Jan 2009 [2431] +README +libburn/libburn.h +Mentioned the need for 64 bit file i/o + +2009.02.04.102822 [2447] +Makefile.am +Linking with $LIBBURN_ARCH_LIBS to get -lcam on FreeBSD + +4 Feb 2009 [2448] +README +cdrskin/README +Mentioned hald as possibly conflicting service + +2009.02.19.192801 [2479] +libburn/spc.c +Human readable error messages with asynchronous SCSI errors + +20 Feb 2009 [2482] +svn copy http://svn.libburnia-project.org/libburn/trunk + http://svn.libburnia-project.org/libburn/branches/ZeroSixZero +Preparing for libburn-0.6.0 + +2009.02.20.090001 [2483] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.6.2 + +20 Feb 2009 [2484] +- cdrskin/add_ts_changes_to_libburn_0_6_0 +- cdrskin/add_ts_changes_to_libburn_0_6_1 ++ cdrskin/add_ts_changes_to_libburn_0_6_2 ++ cdrskin/add_ts_changes_to_libburn_0_6_3 +Updated cdrskin tarball generator + +------------------------------ release - cdrskin-0.6.2.pl00 - 2009.02.20.090001 +* Improvements with build system for FreeBSD + +2009.02.20.124909 [2485] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.6.3 + +20 Feb 2009 [2486] +- cdrskin/add_ts_changes_to_libburn_0_6_0 +- cdrskin/add_ts_changes_to_libburn_0_6_1 ++ cdrskin/add_ts_changes_to_libburn_0_6_2 ++ cdrskin/add_ts_changes_to_libburn_0_6_3 +Updated cdrskin tarball generator + +------------------------------------ cycle - cdrskin-0.6.3 - 2009.02.20.132535 + +20 Feb 2009 [2489] +svn move -m libburn release 0.6.2 is ready + http://svn.libburnia-project.org/libburn/branches/ZeroSixTwo + http://svn.libburnia-project.org/libburn/tags/ZeroSixTwo + +2009.02.27.143100 [2500] +libburn/libburn.h +libburn/transport.h +libburn/drive.c +libburn/options.c +libburn/write.c +libburn/mmc.c +New API function burn_drive_set_stream_recording() + +2009.02.27.211707 [2501] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New stream_recording mode with start number + +2009.03.02.170126 [2513] +libburn/os.h +libburn/libdax_msgs.h +libburn/sg.c ++ libburn/os-dummy.h ++ libburn/sg-dummy.c +New operating system adapter "dummy" for stdio on POSIX-like systems + +2009.03.02.193353 [2514] +libburn/drive.c +Fixed a race condition on abort with stdio writing which could cause SIGSEGV + +2009.03.02.200132 [2515] +libburn/sg.c +Added a dummy function with loud compiler warning to sg.c dummy case + +2009.03.03.092057 [2516] +configure.ac +libburn/sg-dummy.c +Making optional use of statvfs() in sg-dummy + +------------------------------------ cycle - cdrskin-0.6.3 - 2009.03.03.092057 +* New API function burn_drive_set_stream_recording() +* New stream recording mode with start number + + +2009.03.05.145309 [2520] +acinclude.m4 +Lifted the ban on operating systems other than Linux and FreeBSD + +------------------------------------ cycle - cdrskin-0.6.3 - 2009.03.05.145309 +* New operating system adapter "dummy" for stdio on POSIX-like systems + + +2009.03.08.140120 [2522] +libburn/cleanup.c +Reacted on compiler warnings of SchilliX-0.6.7 (based on Solaris 5.11) + +13 Mar 2009 [2529] +svn copy -m "Branching for libburn release 0.6.4" \ + http://svn.libburnia-project.org/libburn/trunk \ + http://svn.libburnia-project.org/libburn/branches/ZeroSixFour + +2009.03.13.080001 [2530] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made libburn number transition to 0.6.4 + +13 Mar 2009 [2531] +- cdrskin/add_ts_changes_to_libburn_0_6_2 +- cdrskin/add_ts_changes_to_libburn_0_6_3 ++ cdrskin/add_ts_changes_to_libburn_0_6_4 ++ cdrskin/add_ts_changes_to_libburn_0_6_5 +Updated cdrskin tarball generator + +13 Mar 2009 [2534] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.6.4.pl00 - 2009.03.13.080001 +* New operating system adapter "dummy" for stdio on general X/Open systems +* New API function burn_drive_set_stream_recording() +* New stream recording mode with start address + + +2009.03.13.135818 [2532] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made libburn number transition to 0.6.5 + +13 Mar 2009 [2533] +- cdrskin/add_ts_changes_to_libburn_0_6_2 +- cdrskin/add_ts_changes_to_libburn_0_6_3 ++ cdrskin/add_ts_changes_to_libburn_0_6_4 ++ cdrskin/add_ts_changes_to_libburn_0_6_5 +Updated cdrskin tarball generator + +13 Mar 2009 [2535] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-0.6.5 - 2009.03.13.162355 + +13 Mar 2009 [2538] +svn move -m "libburn release 0.6.4 is ready" \ + http://svn.libburnia-project.org/libburn/branches/ZeroSixFour \ + http://svn.libburnia-project.org/libburn/tags/ZeroSixFour + +2009.03.16.190745 [2547] +configure.ac +acinclude.m4 +Makefile.am +Get on FreeBSD pkgconfigdir=.../libdata , on Linux and others: .../lib + +2009.03.18.124558 [2553] +libburn/spc.c +Human readable error messages for asc=08 "Logical unit communication failure" + +2009.03.18.172512 [2554] +libburn/drive.c +Bug fix: burn_abort() did not work with broken output pipe (since rev 2514) + +2009.04.30.065653 [2623] +libburn/write.c +Marked alleged use of uninitialized memory reported by valgrind + +2009.04.30.065918 [2624] +libburn/write.c +Replaced 8 blanks by a tab + +2009.04.30.070813 [2625] +libburn/sg-freebsd.c +Bug fix: Device scan stalled on FreeBSD. Ticket 148 jwehle + +------------------------------------ cycle - cdrskin-0.6.5 - 2009.04.30.070813 +Bug fix: burn_abort() did not work with broken output pipe (since rev 2514) +Bug fix: Device scan stalled on FreeBSD. Ticket 148 jwehle + +7 May 2009 [2628] +svn copy -m "Branching for libburn release 0.6.6" + http://svn.libburnia-project.org/libburn/trunk + http://svn.libburnia-project.org/libburn/branches/ZeroSixSix + +7 May 2009 [2629] +cdrskin/cdrskin.1 +Mentioned BD were it was missing + +2009.05.07.100001 [2630] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition and activated development documentation + +7 May 2009 [2631] +- cdrskin/add_ts_changes_to_libburn_0_6_4 +- cdrskin/add_ts_changes_to_libburn_0_6_5 ++ cdrskin/add_ts_changes_to_libburn_0_6_6 ++ cdrskin/add_ts_changes_to_libburn_0_6_7 +Updated cdrskin tarball generator + +7 May 2009 [2632] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.6.6.pl00 - 2009.05.07.100001 +Bug fix: burn_abort() did not work with broken output pipe (since rev 2514) +Bug fix: Device scan stalled on FreeBSD. Ticket 148 jwehle + + +7 May 2009 [2633] +cdrskin/cdrskin.1 +Mentioned BD were it was missing + +2009.05.07.181034 [2634] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition and activated development documentation + +7 May 2009 [2635] +- cdrskin/add_ts_changes_to_libburn_0_6_4 +- cdrskin/add_ts_changes_to_libburn_0_6_5 ++ cdrskin/add_ts_changes_to_libburn_0_6_6 ++ cdrskin/add_ts_changes_to_libburn_0_6_7 +Updated cdrskin tarball generator + +7 May 2009 [2636] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-0.6.7 - 2009.05.07.181034 + +8 May 2009 [2637] +svn move -m libburn release 0.6.6 is ready +http://svn.libburnia-project.org/libburn/branches/ZeroSixSix +http://svn.libburnia-project.org/libburn/tags/ZeroSixSix + +2009.05.17.144304 [2645] +cdrskin/cdrskin.c +Made -scanbus work with SCSI bus numbers like 85 (USB, kernel 2.6.18.2) + +2009.06.02.162841 [2655] +libburn/drive.c +libburn/async.c +libburn/options.c +libburn/spc.c +libburn/libdax_msgs.h +Rectified use of burn_drive.mdata->valid + +2 Jun 2009 [2656] +test/libburner.c +Avoided SIGSEGV if no drives were found by scan + +2009.06.02.172201 [2657] +libburn/drive.c +Avoided to enumerate faulty drive objects + +2009.06.03.085637 [2658] +libburn/spc.c +Bug fix: Old MMC-1 drives were rejected because of mode page 2Ah length + +2009.06.03.185118 [2659] +libburn/transport.h +libburn/drive.c +libburn/spc.c +libburn/mmc.c +Bug fix: No usable media was detected with old MMC-1 drives + +2009.06.14.095004 [2669] +libburn/sg-linux.c +Retrying 3 times on EBUSY drives with generous usleep intervals + +------------------------------------ cycle - cdrskin-0.6.7 - 2009.06.14.095833 +Made -scanbus work with high SCSI bus numbers +Bug fix: Old MMC-1 drives were rejected because of mode page 2Ah length +Bug fix: No usable media was detected with old MMC-1 drives + + +7 Jul 2009 [2691] +acinclude.m4 +configure.ac +configure options --enable-libdir-pkgconfig and --enable-pkgconfig-path=DIR + +2009.07.07.191134 [2692] +Timestamp for revision 2691 + +14 Jul 2009 [2894] +svn copy -m "Branching for libburn release 0.6.8" + http://svn.libburnia-project.org/libburn/trunk + http://svn.libburnia-project.org/libburn/branches/ZeroSixEight + +2009.07.14.100001 [2695] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition and activated development documentation + +14 Jul 2009 [2696] +- cdrskin/add_ts_changes_to_libburn_0_6_6 +- cdrskin/add_ts_changes_to_libburn_0_6_7 ++ cdrskin/add_ts_changes_to_libburn_0_6_8 ++ cdrskin/add_ts_changes_to_libburn_0_6_9 +Updated cdrskin tarball generator + +14 Jul 2009 [2697] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.6.8.pl00 - 2009.07.14.100001 +Made -scanbus work with high SCSI bus numbers +Bug fix: Old MMC-1 drives were rejected because of mode page 2Ah length +Bug fix: No usable media was detected with old MMC-1 drives + + +2009.07.14.133159 [2698] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition and activated development documentation + +14 Jul 2009 [2699] +- cdrskin/add_ts_changes_to_libburn_0_6_6 +- cdrskin/add_ts_changes_to_libburn_0_6_7 ++ cdrskin/add_ts_changes_to_libburn_0_6_8 ++ cdrskin/add_ts_changes_to_libburn_0_6_9 +Updated cdrskin tarball generator + +14 Jul 2009 [2700] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-0.6.9 - 2009.07.14.133159 + +14 Jul 2009 [2702] +svn move -m libburn release 0.6.8 is ready +http://svn.libburnia-project.org/libburn/branches/ZeroSixEight +http://svn.libburnia-project.org/libburn/tags/ZeroSixEight + + +2009.08.15.133055 [2721] +libburn/write.c +Added test code about output blocks size as comment. + +2009.08.15.133332 [2722] +libburn/libburn.h +libburn/transport.h +libburn/drive.c +libburn/mmc.h +libburn/mmc.c +New API calls burn_drive_get_all_profiles(), burn_obtain_profile_name() + +2009.08.15.133744 [2723] +cdrskin/cdrskin.c +Listing all profiles with cdrskin -v -atip + +2009.08.23.130326 [2735] +libburn/drive.c +libburn/spc.c +libburn/mmc.c +Adapted to pitfalls of U3 memory sticks which appear as CD-ROM drives + +------------------------------------ cycle - cdrskin-0.6.9 - 2009.08.23.132942 +New API calls burn_drive_get_all_profiles(), burn_obtain_profile_name() +Adapted to pitfalls of U3 memory sticks which appear as CD-ROM drives + + +2009.08.24.131146 [2736] +libburn/libburn.h +libburn/transport.h +libburn/drive.c +libburn/sbc.c +New API call burn_drive_snooze() + +2009.08.24.161646 [2737] +libburn/libburn.h +libburn/drive.c +Made burn_drive_snooze() safe for emulated drives + +2009.08.24.202517 [2740] +libburn/transport.h +libburn/sbc.c +libburn/mmc.c +Implemented automatic START UNIT after STOP UNIT before any other SCSI command + +2009.08.27.123001 [2746] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition and activated development documentation + +27 Aug 2009 [2747] +- cdrskin/add_ts_changes_to_libburn_0_6_8 +- cdrskin/add_ts_changes_to_libburn_0_6_9 ++ cdrskin/add_ts_changes_to_libburn_0_7_0 ++ cdrskin/add_ts_changes_to_libburn_0_7_1 +Updated cdrskin tarball generator + +27 Aug 2009 [2748] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.7.0.pl00 - 2009.08.27.123001 +New API calls burn_drive_get_all_profiles(), burn_obtain_profile_name() +New API call burn_drive_snooze() +Adapted to pitfalls of U3 memory sticks which appear as CD-ROM drives +Listing all supported profiles with option -atip + + +2009.08.27.143351 [2749] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to development version 0.7.1 + +27 Aug 2009 [2750] +- cdrskin/add_ts_changes_to_libburn_0_6_8 +- cdrskin/add_ts_changes_to_libburn_0_6_9 ++ cdrskin/add_ts_changes_to_libburn_0_7_0 ++ cdrskin/add_ts_changes_to_libburn_0_7_1 +Updated cdrskin tarball generator + +27 Aug 2009 [2751] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-0.7.1 - 2009.08.27.143351 + +2009.09.01.082602 [2773] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Disabled write mode -raw96r + +2009.09.01.095750 [2774] [2775] +libburn/libburn.h +libburn/sector.h +libburn/sector.c +libburn/lec.h +libburn/lec.c +libburn/read.c +libburn/write.c +Disabled code using libburn/lec.c + +2009.09.01.113957 [2777] +cdrskin/cdrskin.c +Aligned the output columns of cdrskin --devices + +2009.09.01.133520 [2778] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New option --long_toc, now printing media summary at end of TOC + +2009.09.02.131514 [2779] +Makefile.am +libburn/sector.c +- libburn/lec.h +- libburn/lec.c +cdrskin/compile_cdrskin.sh +Removed lec.c from libburn + +2009.09.02.133307 [2780] +libburn/libburn.h +libburn/util.c ++ doc/mediainfo.txt +cdrskin/cdrskin.c +New API call burn_guess_cd_manufacturer() + +2009.09.04.202255 [2781] +libburn/libburn.h +libburn/drive.c +libburn/util.c +libburn/spc.c +libburn/mmc.h +libburn/mmc.c +New API calls burn_get_media_product_id() and burn_guess_manufacturer() + +2009.09.04.202357 [2782] +cdrskin/cdrskin.c +Option -atip now reports Product Id and Manufacturer for most CD, DVD, BD types + +4 Sep 2009 [2783] +doc/mediainfo.txt +Updated media info documentation + +2009.09.04.204432 [2784] +cdrskin/cdrskin.c +Adjusted column width of media info messages + +2009.09.05.072512 [2785] +cdrskin/cdrskin.c +Adjusted column width of media summary message + +------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.05.072659 +* New option --long_toc, now printing media summary at end of TOC +* New API call burn_guess_cd_manufacturer() +* New API calls burn_get_media_product_id() and burn_guess_manufacturer() + + +2009.09.05.113043 [2786] +libburn/util.h +libburn/util.c +libburn/mmc.c +Made product ID surely a single printable word + +2009.09.05.114326 [2787] +libburn/sg-linux.c +Silenced a valgrind warning caused by not recognizing side effects of a ioctl + +2009.09.05.115112 [2788] +cdrskin/cdrskin.c +Closed a small memory leak with drive inquiry + +2009.09.05.120947 [2789] +cdrskin/cdrskin.c +Made cdrskin/compile_cdrskin.sh -libburn_0_7_0 work again + +------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.05.121334 + +2009.09.05.131859 [2790] +cdrskin/cdrskin.c +Re-enabled output of product id and manufacturer + +------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.05.132208 + +2009.09.05.165127 [2791] +libburn/libburn.h +libburn/drive.c +libburn/util.c +libburn/mmc.c +Introduced flag bit0 for API call burn_get_media_product_id() + +2009.09.05.165257 [2792] +cdrskin/cdrskin.c +"Manufacturer:", "Media type:" as cdrecord, own "Product Id:" and "Producer:" + +------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.05.172818 +* New -atip report lines "Product Id:" and "Producer:" + + +2009.09.06.092330 [2793] +libburn/util.c +Made recognition of CD media codes work in burn_guess_manufacturer() + +2009.09.06.092654 [2794] +libburn/libburn.h +libburn/drive.c +cdrskin/cdrskin.c +Changed new API call burn_get_media_product_id() to burn_disc_get_media_id() + +06 Sep 2009 [2795] +doc/mediainfo.txt +Corrected misformatted manufacturer strings of DVD- media + +2009.09.06.112121 [2796] +libburn/mmc.c +doc/mediainfo.txt +Avoided to read third sixpack of manufacturer bytes with DVD-R media + +------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.06.115138 + +2009.09.09.125305 [2801] +libburn/libburn.h +libburn/transport.h +libburn/drive.c +libburn/mmc.c +New API call burn_disc_get_cd_info() + +2009.09.09.134030 [2802] +libburn/transport.h +libburn/drive.c +libburn/mmc.c +Correction with erasable bit of burn_disc_get_cd_info() + +2009.09.09.153951 [2803] +libburn/libburn.h +libburn/mmc.c +New struct burn_toc_entry extension for Last Recorded Address + +2009.09.09.173452 [2804] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Emulation of some -minfo output + +2009.09.09.174337 [2805] +cdrskin/cdrskin.c +Silenced compiler warning + +------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.09.174446 +* New API call burn_disc_get_cd_info() +* Emulation of some -minfo output + + +2009.09.10.071253 [2806] +cdrskin/cdrskin.c +Removed Media summary from -minfo because of incompatible counting rules + +------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.10.084912 + + +2009.09.11.112528 [2807] +libburn/libburn.h +libburn/transport.h +libburn/structure.h +libburn/structure.c +libburn/sector.c +New API call burn_track_set_cdxa_conv() + +2009.09.11.120959 [2808] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Interpreting options -mode2, -xa, -xa1, -xa2 but producing CD-ROM Mode 1 tracks + +12 Sep 2009 [2809] +cdrskin/cdrskin_eng.html +Updated cdrskin web page + +------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.12.134510 +* New API call burn_track_set_cdxa_conv() +* Better interpretation of options -mode2, -xa, -xa1, -xa2 +* New option --xa1-ignore + + +2009.09.13.095055 [2810] +libburn/drive.c +Evaluating read capacity with role 2 drives (regular files and block devices) + +2009.09.18.161214 [2815] +libburn/sector.c +Fixing SIGSEGV with CD SAO introduced with revision 2807 + +2009.09.18.161944 [2816] +libburn/read.c +libburn/libdax_msgs.h +Refusing to read beyond media_read_capacity + +------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.18.164446 + + +2009.09.20.111911 [2820] +libburn/libburn.h +Mentioned need for 2056 byte fifo chunks with burn_track_set_cdxa_conv() + +2009.09.22.192545 [2822] +libburn/sbc.c +Revoked instruction to retry STOP UNIT + +2009.09.22.192802 [2823] +libburn/sg-linux.c +Restricted retry to the timeout for single SCSI commands (200 seconds) + +2009.10.04.151239 [2830] +libburn/write.c +libburn/libdax_msgs.h +Fixed CD TAO multi-track -dummy bug reported by Philippe Rouquier + +------------------------------------ cycle - cdrskin-0.7.1 - 2009.10.08.174136 +* Bug fix: CD TAO sessions with multiple tracks did not work in -dummy mode + + +2009.10.09.123651 [2847] +libburn/mmc.c +Determining read capacity for DVD-RAM + +2009.10.09.123840 [2848] +cdrskin/cdrskin.c +Made -minfo stupidly report overwriteable media as "erasable" and "complete" + +2009.10.09.200411 [2849] +libburn/read.c +Made read_capacity error message of burn_read_data() depending on flag bit1 + +2009.10.09.200730 [2850] +cdrskin/cdrskin.c +Made -minfo subtract 2 from track size if "Data" and last 2 blocks unreadable + +12 Oct 2009 [2854] +svn copy -m "Branching for libburn release 0.7.2" \ + http://svn.libburnia-project.org/libburn/trunk \ + http://svn.libburnia-project.org/libburn/branches/ZeroSevenTwo + +2009.10.12.080001 [2855] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition and activated development documentation + +12 Oct 2009 [2856] +- cdrskin/add_ts_changes_to_libburn_0_7_0 +- cdrskin/add_ts_changes_to_libburn_0_7_1 ++ cdrskin/add_ts_changes_to_libburn_0_7_2 ++ cdrskin/add_ts_changes_to_libburn_0_7_3 +Updated cdrskin tarball generator + +12 Oct 2009 [2867] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.7.2.pl00 - 2009.10.12.080001 +* Bug fix: CD TAO sessions with multiple tracks did not work in -dummy mode +* Better interpretation of options -mode2, -xa, -xa1, -xa2 +* New option --xa1-ignore +* Emulation of some -minfo output +* New -atip report lines "Product Id:" and "Producer:" +* New API call burn_guess_cd_manufacturer() +* New API calls burn_get_media_product_id() and burn_guess_manufacturer() +* New API call burn_disc_get_cd_info() +* New API call burn_track_set_cdxa_conv() + + +2009.10.12.103503 [2858] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.7.3 + +12 Oct 2009 [2859] +- cdrskin/add_ts_changes_to_libburn_0_7_0 +- cdrskin/add_ts_changes_to_libburn_0_7_1 ++ cdrskin/add_ts_changes_to_libburn_0_7_2 ++ cdrskin/add_ts_changes_to_libburn_0_7_3 +Updated cdrskin tarball generator + +12 Oct 2009 [2860] +cdrskin/changelog.txt +Documented changes and release timestamp + +12 Oct 2009 [2861] +svn move -m "libburn release 0.7.2 is ready" \ + http://svn.libburnia-project.org/libburn/branches/ZeroSevenTwo \ + http://svn.libburnia-project.org/libburn/tags/ZeroSevenTwo + +------------------------------------ cycle - cdrskin-0.7.3 - 2009.10.12.105750 + + +2009.10.17.131852 [2865] [2864] +Makefile.am +libburn/libburn.h +libburn/sector.c ++ libburn/ecma130ab.h ++ libburn/ecma130ab.c +cdrskin/compile_cdrskin.sh +Re-implemented ECMA-130 P-parity, Q-parity and scrambling for BURN_WRITE_RAW + +------------------------------------ cycle - cdrskin-0.7.3 - +* Re-implemented ECMA-130 P-parity, Q-parity and scrambling for BURN_WRITE_RAW + + +2009.10.19.115722 [2867] +libburn/ecma130ab.c +Optimizations with parity computation, clarification about nature of logarithms + +2009.10.20.160131 [2868] +libburn/libburn.h +libburn/ecma130ab.c +More optimizations with parity computation + +------------------------------------ cycle - cdrskin-0.7.3 - 2009.10.20.160131 + + +2009.10.27.100637 [2871] +libburn/mmc.c +Bug fix: burn_drive->disc_id or burn_drive->disc_app_code altered by stray 0. Thanks to George Danchev. + +2009.10.27.101031 [2872] +libburn/sg-freebsd.c +Bug fix: Closed memory leak with failure to open device file under FreeBSD. Thanks to George Danchev. + +2009.10.30.134640 [2880] +libburn/libburn.h +libburn/spc.c +libburn/mmc.c +libburn/sg-linux.c +Test macros for finding reason of stall problem with Pioneer DVD-216D on DVD-R + +------------------------------------ cycle - cdrskin-0.7.3 - 2009.10.30.134640 + + +2009.11.03.184626 [2881] +libburn/libburn.h +libburn/mmc.c +Test macro for SL_V in mode page 05 + +2009.11.04.084506 [2882] +libburn/libburn.h +libburn/write.c +Test macro for SEND OPC INFORMATION before DVD-R track + +2009.11.05.170409 [2883] +libburn/libburn.h +libburn/transport.h +libburn/write.c +libburn/sbc.c +libburn/mmc.c +Test macros for double START UNIT and SET CD SPEED + +2009.11.08.120917 [2884] +libburn/mmc.c +Corrected allocation length with GET CONFIGURATION + +2009.11.08.121334 [2885] +libburn/spc.h +libburn/spc.c +libburn/mmc.h +libburn/sg-linux.c +Made SCSI command log more complete and more readable + +2009.11.10.132154 [2886] +libburn/mmc.c +Avoiding START UNIT before the drive gets released + +2009.11.10.203412 [2887] +libburn/libburn.h +libburn/write.c +libburn/spc.c +libburn/sbc.c +libburn/mmc.c +libburn/sg-linux.c +Hopefully solved the endless burn problem with Pioneer DVR-216D + +2009.11.11.100714 [2888] +libburn/write.c +Increased stdio flush interval from 1 MB to 16 MB + +2009.11.11.100822 [2889] +cdrskin/cdrskin.c +Reporting number of pending bytes while thanking for patience in -vvv mode + +2009.11.11.105028 [2890] +cdrskin/cdrskin.c +Silenced a compiler warning about potentially uninitialized variable + +------------------------------------ cycle - cdrskin-0.7.3 - 2009.11.11.105159 +* Workaround for Pioneer DVR-216D which got stuck on DVD-R burns + + +11 Nov 2009 [2891] +cdrskin/cdrskin_eng.html +Updated cdrskin home page about DVR-216D workaround + +2009.11.12.175514 [2892] +libburn/libburn.h +libburn/init.c +libburn/write.c +libburn/sg-linux.c +Made SCSI logger permanent and controllable via API call + +2009.11.12.175735 [2893] +libburn/spc.c +libburn/sbc.c +libburn/mmc.c +Workaround for Pioneer DVR-216D refusal to eject + +2009.11.12.180048 [2894] +libburn/drive.c +Macro Libburn_pioneer_dvr_216d_dummy_probe_wM for omitting write mode probe + +2009.11.12.180241 [2895] +cdrskin/cdrskin.c +Implemented option -V for logging of SCSI commands + +2009.11.12.193617 [2896] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Man page entry and help text for option -V + +------------------------------------ cycle - cdrskin-0.7.3 - 2009.11.12.194644 +* Implemented option -V for logging of SCSI commands +* Workaround for Pioneer DVR-216D refusal to eject + + +13 Nov 2009 [2898] +svn copy -m Branching for libburn bugfix release 0.7.2.pl01 \ + http://svn.libburnia-project.org/libburn/tags/ZeroSevenTwo \ + http://svn.libburnia-project.org/libburn/branches/ZeroSevenTwoPl01 + +2009.11.12.230001 [2899] +README +libburn/write.c +libburn/sbc.c +cdrskin/README +cdrskin/cdrskin_eng.html +cdrskin/cdrskin_timestamp.h +libburn-0.7.2.pl01 : Workarounds for Pioneer DVR-216D rev 1.09 + +13 Nov 2009 [2900] +svn move -m libburn bugfix release 0.7.2.pl01 is ready \ + http://svn.libburnia-project.org/libburn/branches/ZeroSevenTwoPl01 \ + http://svn.libburnia-project.org/libburn/tags/ZeroSevenTwoPl01 + +-------------------------------- patch - cdrskin-0.7.2.pl01 - 2009.11.12.230001 +* Workaround for Pioneer DVR-216D which got stuck on DVD-R burns +* Workaround for Pioneer DVR-216D refusal to eject + + +2009.11.15.115923 [2901] +libburn/file.c +Corrected an outdated remark + +2009.11.15.152541 [2902] +cdrskin/cdrskin.c +cdrskin/cdrfifo.c +cdrskin/compile_cdrskin.sh +New compile_cdrskin.sh option -o_direct (Linux only) + +2009.11.15.153140 [2903] +libburn/libburn.h +libburn/options.h +libburn/options.c +libburn/write.c +New API calls burn_write_opts_set_dvd_obs(), burn_write_opts_set_stdio_fsync() + +2009.11.15.165016 [2904] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New options dvd_obs= and stdio_fsync= + +2009.11.16.165420 [2905] +configure.ac +Makefile.am +libburn/write.c +cdrskin/cdrskin.c +cdrskin/cdrfifo.h +cdrskin/cdrfifo.c +cdrskin/cdrskin.1 +Configure options --enable-cdrskin-fifo-odirect, --enable-dvd-obs-64k + +16 Nov 2009 [2906] +cdrskin/compile_cdrskin.sh +New compile_cdrskin.sh option -dvd_obs_64k, adapted to new .o names of libburn + +16 Nov 2009 [2907] +README +cdrskin/README +cdrskin/cdrskin_eng.html +Updated build instructions of libburn and cdrskin + +------------------------------------ cycle - cdrskin-0.7.3 - 2009.11.16.180334 +* New API calls burn_write_opts_set_dvd_obs(), burn_write_opts_set_stdio_fsync() +* New options dvd_obs= and stdio_fsync= +* Configure option --enable-dvd-obs-64k +* New compile_cdrskin.sh option -dvd_obs_64k + + +2009.11.17.093602 [2908] +configure.ac +Makefile.am +cdrskin/compile_cdrskin.sh +Revoked usage of libburn_libburn_la_CFLAGS in Makefile.am (ugly .o names) + +------------------------------------ cycle - cdrskin-0.7.3 - 2009.11.17.100248 + + +17 Nov 2009 [2912] +cdrskin/compile_cdrskin.sh +Corrected help text of cdrskin static compile script + +2009.11.18.122713 [2913] +libburn/drive.c +libburn/spc.c +libburn/mmc.h +libburn/mmc.c +Split automatic drive start function from mmc_function_spy() + +2009.11.18.185733 [2914] +libburn/write.c +Reserving enough track space for 64 kB write chunks + +------------------------------------ cycle - cdrskin-0.7.3 - 2009.11.18.190403 + + +2009.11.20.134952 [2915] +libburn/libburn.h +libburn/write.c +libburn/sg-linux.c +Experiment about SG_FLAG_DIRECT_IO + +2009.11.20.175854 [2916] +cdrskin/cdrfifo.c +Avoided use of uninitialized variable + +------------------------------------ cycle - cdrskin-0.7.3 - 2009.11.21.122202 + + +2009.11.21.191516 [2917] +libburn/write.c +Longer READ BUFFER CAPACITY interval with DVD/BD writing + +2009.11.21.191717 [2918] +cdrskin/cdrskin.c +Enabled cdrskin O_DIRECT with fs=0 + +2009.11.22.115227 [2919] +libburn/write.c +Bug fix: DVD DAO track size was rounded up much too generously + +2009.11.23.185725 [2920] +configure.ac +Makefile.am +README +libburn/libburn.h +libburn/file.h +libburn/file.c +libburn/async.c +libburn/write.c +libburn/sg-linux.c +libburn/sg-freebsd.c +libburn/sg-freebsd-port.c +libburn/sg-dummy.c +New API calls burn_os_open_track_src() , burn_os_alloc_buffer() + +2009.11.23.193252 [2921] +cdrskin/cdrskin.c +cdrskin/cdrfifo.c +cdrskin/compile_cdrskin.sh +cdrskin/README +Gave up cdrskin specific O_DIRECT compile option + +2009.11.25.122233 [2920] +libburn/libburn.h +libburn/sg-linux.c +libburn/sg-freebsd.c +libburn/sg-freebsd-port.c +libburn/sg-dummy.c +Gave up call burn_os_close_track_src() introduced by rev 2920 + +2009.11.25.160153 [2923] +libburn/libburn.h +libburn/file.c +libburn/libdax_msgs.h +New API call burn_fifo_fill() + +2009.11.26.144452 [2924] +libburn/libburn.h +libburn/file.h +libburn/file.c +New API calls burn_fifo_get_statistics(), burn_fifo_next_interval() + +2009.11.26.211418 [2925] +cdrskin/cdrskin.c +cdrskin/compile_cdrskin.sh +Compiler option -use_libburn_fifo to switch non-CD from cdrfifo to libburn fifo + +2009.11.28.122436 [2926] +libburn/sg-linux.c +Using mmap() by default for allocating read buffers + +------------------------------------ cycle - cdrskin-0.7.3 - 2009.11.28.165658 +* Bug fix: DVD DAO track size was rounded up much too generously +* New API call burn_fifo_fill() +* New API calls burn_fifo_get_statistics(), burn_fifo_next_interval() +* Configure option --enable-track-src-odirect + + +2009.11.30.100152 [2929] +libburn/sbc.c +Documented meaning of START/STOP UNIT bits + +2009.12.02.103036 [2930] +libburn/write.c +Gave up CLOSE TRACK with CD TAO burn runs + +2009.12.05.111822 [2931] +libburn/mmc.c +Bug fix: SIGSEGV with LG GH22LS30 when inquiring media product id + +------------------------------------ cycle - cdrskin-0.7.3 - 2009.12.05.112623 +* Bug fix: SIGSEGV from NULL pointer with media product id inquiry on LG GH22LS30 + + +2009.12.05.142309 [2932] +libburn/libburn.h +libburn/write.c +Made effect of macro Libburn_pioneer_dvr_216d_read_buf_caP unconditional + +2009.12.05.143707 [2933] +libburn/mmc.c +libburn/sg-linux.c +Converted stderr experiment messages to DEBUG messages + +2009.12.06.073404 [2934] +libburn/mmc.c +Some clarifications about the GH22LS30 problem + +2009.12.06.074622 [2935] +libburn/write.c +Some clarifications about the Linux throughput problem + +2009.12.06.093847 [2936] +cdrskin/compile_cdrskin.sh +Now default in cdrskin: use of libburn fifo with DVD and BD single track + +06 Dec 2009 [2937] +svn copy -m Branching for libburn release 0.7.4 +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/ZeroSevenFour + +2009.12.06.160001 [2938] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.7.4 + +06 Dec 2009 [2939] +- cdrskin/add_ts_changes_to_libburn_0_7_2 +- cdrskin/add_ts_changes_to_libburn_0_7_3 ++ cdrskin/add_ts_changes_to_libburn_0_7_4 ++ cdrskin/add_ts_changes_to_libburn_0_7_5 +Updated cdrskin tarball generator + +06 Dec 2009 [2940] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.7.4.pl00 - 2009.12.06.160001 +* Configure options --enable-dvd-obs-64k, --enable-track-src-odirect +* New API calls burn_write_opts_set_dvd_obs(), burn_write_opts_set_stdio_fsync() +* New API call burn_set_scsi_logging() +* New API calls burn_fifo_get_statistics(), burn_fifo_next_interval(), burn_fifo_fill() +* Re-implemented ECMA-130 P-parity, Q-parity and scrambling for BURN_WRITE_RAW +* Workaround for Pioneer DVR-216D which got stuck on DVD-R burns +* Workaround for Pioneer DVR-216D refusal to eject +* Bug fix: SIGSEGV from NULL pointer with media product id inquiry on LG GH22LS30 +* Bug fix: DVD DAO track size was rounded up much too generously +* cdrskin option -V for logging of SCSI commands +* New cdrskin options dvd_obs= and stdio_fsync= +* New compile_cdrskin.sh option -dvd_obs_64k + + +2009.12.07.070029 [2941] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.7.5 + +07 Dec 2009 [2942] +- cdrskin/add_ts_changes_to_libburn_0_7_2 +- cdrskin/add_ts_changes_to_libburn_0_7_3 ++ cdrskin/add_ts_changes_to_libburn_0_7_4 ++ cdrskin/add_ts_changes_to_libburn_0_7_5 +Updated cdrskin tarball generator + +07 Dec 2009 [2943] +cdrskin/changelog.txt +Documented changes and release timestamp + +07 Dec 2009 [2946] +svn move -m libburn release 0.7.4 is ready +http://svn.libburnia-project.org/libburn/branches/ZeroSevenFour +http://svn.libburnia-project.org/libburn/tags/ZeroSevenFour + +------------------------------------ cycle - cdrskin-0.7.5 - 2009.12.07.083850 + + +16 Dec 2009 [2955] +doc/cookbook.txt +Mentioned in cookbook the change about TAO close track + +2009.12.19.140015 [2957] +libburn/spc.c +Corrected CDB length of command 55h MODE SELECT from 12 to 10 + +2009.12.19.142456 [2958] +libburn/spc.h +libburn/sg-linux.c +Moved sg_log_cmd() to spc.c scsi_log_cmd() + +2009.12.19.142456 [2959] +configure.ac +Makefile.am +libburn/os.h +libburn/sg.c +libburn/sg-freebsd-port.c ++ libburn/os-libcdio.h ++ libburn/sg-libcdio.c +Experimental SCSI transport adapter via GNU libcdio + +2009.12.24.170601 [2960] +configure.ac +libburn/spc.h +libburn/spc.c +libburn/sg-linux.c +libburn/sg-libcdio.c +Making use of libcdio function mmc_get_cmd_scsi_sense() + +25 Dec 2009 [2961] +cdrskin/compile_cdrskin.sh +Option -use_libcdio for cdrskin development compile script + +2009.12.25.101433 [2962] +libburn/libdax_msgs.h +Commited file forgotten with rev 2960 + +2009.12.25.143326 [2963] +libburn/sg-libcdio.c +Resolving symbolic links in libcdio drive list + +2009.12.25.144122 [2964] +configure.ac +Added PKG_CHECK_MODULES for libcdio-0.82 (must become 0.83 when released) + +2009.12.25.205704 [2965] +configure.ac +libburn/sg-libcdio.c +Adapted to libcdio-0.83 and its runtime version telling + +2009.12.25.223915 [2966] +libburn/init.c +libburn/sg.h +libburn/sg-freebsd.c +libburn/sg-freebsd-port.c +libburn/sg-libcdio.c +libburn/sg-linux.c +New internal sg-API function sg_initialize() + +2009.12.26.080301 [2967] +libburn/init.c +libburn/sg-dummy.c +libburn/sg-freebsd.c +libburn/sg-freebsd-port.c +libburn/sg-libcdio.c +libburn/sg-linux.c +New API function burn_scsi_transport_id() + +26 Dec 2009 [2968] +Makefile.am +Added os-dummy.h and sg-dummy.h to libburn tarball + +26 Dec 2009 [2969] +svn copy -m Branching for libburn bugfix release 0.7.4.pl01 +http://svn.libburnia-project.org/libburn/tags/ZeroSevenTwo +http://svn.libburnia-project.org/libburn/branches/ZeroSevenTwoPl01 + +26 Dec 2009 [2970] +svn mv -m Branching for libburn bugfix release 0.7.4.pl01 +http://svn.libburnia-project.org/libburn/branches/ZeroSevenTwoPl01 +http://svn.libburnia-project.org/libburn/branches/ZeroSevenFourPl01 + +26 Dec 2009 [2971] +svn rm -m Branching for libburn bugfix release 0.7.4.pl01 +http://svn.libburnia-project.org/libburn/branches/ZeroSevenFourPl01 + +26 Dec 2009 [2972] +svn copy -m Branching for libburn bugfix release 0.7.4.pl01 +http://svn.libburnia-project.org/libburn/tags/ZeroSevenFour +http://svn.libburnia-project.org/libburn/branches/ZeroSevenFourPl01 + +2009.12.26.110001 [2973] +README +Makefile.am +cdrskin/cdrskin_timestamp.h +Bug fix: Added missing system adapter for generic X/Open to libburn release tarball + +26 Dec 2009 [2974] +svn move -m libburn bugfix release 0.7.4.pl01 is ready +http://svn.libburnia-project.org/libburn/branches/ZeroSevenFourPl01 +http://svn.libburnia-project.org/libburn/tags/ZeroSevenFourPl01 + +2009.12.26.193707 [2975] +cdrskin/cdrskin.c +Reporting burn_scsi_transport_id() in cdrskin as debug message + +2009.12.26.222656 [2976] +libburn/libburn.h +doc/comments +Reacted on some doxygen warnings of Debian hurd build + +2009.12.27.092057 [2979] +libburn/os-libcdio.h +libburn/sg-libcdio.c +Showing libburn users drive name link targets, using in libcdio its own names + +2009.12.27.102342 [2981] +libburn/sg-libcdio.c +Shorter sg_initialize message with sg-libcdio + +2009.12.27.144620 [2982] +libburn/init.c +libburn/drive.c +libburn/sg.h +libburn/os-libcdio.h +libburn/sg-dummy.c +libburn/sg-freebsd.c +libburn/sg-freebsd-port.c +libburn/sg-libcdio.c +libburn/sg-linux.c +Extended sg-API by sg_shutdown(), sg_dispose_drive(), sg_id_string() + +2009.12.27.144733 [2983] +cdrskin/cdrskin.c +Reporting system adapter id with cdrskin -version + +2009.12.29.115717 [2984] +configure.ac +Incremented middle .so number + +2009.12.29.115854 [2985] +libburn/spc.c +Corrected a mode page size computation error which for now had no bad effect + +2009.12.29.132537 [2986] +acinclude.m4 +libburn/os.h +libburn/sg.c +Experimentally enabled FreeBSD system adapter for Debian kfreebsd + +------------------------------------ cycle - cdrskin-0.7.5 - 2009.12.29.134637 +* Experimental SCSI transport adapter via GNU libcdio +* Experimentally using FreeBSD system adapter for Debian kfreebsd +* Bug fix: System adapter for generic X/Open was missing in libburn release tarball + + +2009.12.29.224506 [2987] +acinclude.m4 +Adaptions for Debian kfreebsd requested by Petr Salinger + +2009.12.30.154140 [2988] +libburn/drive.c +libburn/sg-libcdio.c +Making use of new libcdio capability to obtain SCSI address tuple on Linux + +2009.12.30.201025 [2990] +libburn/sg-libcdio.c +Silenced libcdio warnings + +2010.01.01.124042 [2994] +libburn/drive.c +Bug fix: with non-Linux adapters there were 0 readable bytes on block devices + +2010.01.01.124415 [2995] +libburn/sg-libcdio.c +Enabled block device size recognition with sg-libcdio on Linux + +------------------------------------ cycle - cdrskin-0.7.5 - 2010.01.01.143104 +* Bug fix: with non-Linux adapters there were 0 readable bytes on block devices + + +2010.01.04.134949 [3001] +libburn/write.c +libburn/libdax_msgs.h +Avoiding stream recording on BD if not 64 kB buffer + +2010.01.04.135427 [3002] +libburn/os-libcdio.h +Enlarged buffer size of libcdio adapter on Linux to 64k + +2010.01.09.142027 [3004] +libburn/sg-libcdio.c +Forgot to forward sense reply to higher levels + +2010.01.09.142642 [3005] +libburn/spc.c +Better error message with unknown SCSI error codes + +2010.01.09.143428 [3006] +libburn/spc.c +libburn/sbc.c +Revoked asynchronous eject, as we cannot distinguish out from unready + +2010.01.12.165214 [3009] +libburn/sg-dummy.c +libburn/sg-freebsd-port.c +libburn/sg-libcdio.c +libburn/sg-linux.c +Corrected free capacity measurement of stdio: drives in regular files + +12 Jan 2010 [3010] +doc/cookbook.txt +Fixed typos in MMC cookbook + +2010.01.13.074028 [3011] +libburn/drive.c +Experimentally regard FreeBSD /dev/da[0-9] and /dev/cd[0-9] as block device + +2010.01.13.074640 [3012] +libburn/sg-freebsd.c +Adaptions after encounter with FreeBSD 8.0 + +2010.01.13.171546 [3013] +libburn/libburn.h +Carified that apps must use 64 bit off_t or the lib must be tweaked. + +14 Jan 2010 [3014] +14 Jan 2010 [3015] +test/libburner.c +Carified in libburner.c that apps must use 64 bit off_t. + +2010.01.14.160633 [3016] +libburn/libburn.h +libburn/drive.c +Giving up drive probing by mode page sending + +2010.01.14.160748 [3017] +libburn/sg-libcdio.c +Provisory rejection of FreeBSD ATAPI drives in sg-libcdio + +2010.01.15.182615 [3018] +libburn/os-freebsd.h +libburn/sg-freebsd.c +Implemented adivisory FreeBSD drive locking via flock(2) + +2010.01.16.125258 [3019] +libburn/drive.c +libburn/sg.h +libburn/sg-dummy.c +libburn/sg-freebsd.c +libburn/sg-freebsd-port.c +libburn/sg-libcdio.c +libburn/sg-linux.c +New OS adapter burn_os_is_2k_seekrw() replaces S_ISBLK() with pseudo-drives + +2010.01.18.103410 [3023] +libburn/sg-linux.c +Changed a comment in sg-linux.c + +------------------------------------ cycle - cdrskin-0.7.5 - 2010.01.18.104011 +* Made FreeBSD system adapter safe from mutal burn spoiling and drive deadlock + + +21 Jan 2010 [3028] +cdrskin/cdrskin.1 +Changed man page example from -toc to -minfo + +2010.01.21.104741 [3029] +libburn/sg-freebsd.c +libburn/sg-libcdio.c +Learned how to inquire size of disk-like FreeBSD devices + +22 Jan 2010 [3030] +svn copy -m "Branching for libburn release 0.7.6" \ + http://svn.libburnia-project.org/libburn/trunk \ + http://svn.libburnia-project.org/libburn/branches/ZeroSevenSix + +2010.01.22.130001 [] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.7.6 + +22 Jan 2010 [3032] +COPYRIGHT +cdrskin/cdrskin.c +doc/cookbook.txt +libburn/libdax_msgs.h +libburn/libdax_msgs.c +test/libburner.c +test/telltoc.c +Lifted ban to derive GPLv3, extended copyright range to 2010 + +22 Jan 2009 [3033] +- cdrskin/add_ts_changes_to_libburn_0_7_4 +- cdrskin/add_ts_changes_to_libburn_0_7_5 ++ cdrskin/add_ts_changes_to_libburn_0_7_6 ++ cdrskin/add_ts_changes_to_libburn_0_7_7 +Updated cdrskin tarball generator + +22 Jan 2009 [3034] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.7.6.pl00 - 2010.01.22.130001 +* Bug fix: System adapter for generic X/Open was missing in libburn release tarball +* Bug fix: with non-Linux adapters there were 0 readable bytes on block devices +* Made FreeBSD system adapter safe from mutal burn spoiling and drive deadlock +* Enabled FreeBSD system adapter for Debian kfreebsd +* Experimental SCSI transport adapter via GNU libcdio 0.83git + + +2010.01.23.103338 [3035] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.7.7 + +2010.01.23.104423 [3036] +COPYRIGHT +doc/cookbook.txt +libburn/libdax_msgs.h +libburn/libdax_msgs.c +test/libburner.c +test/telltoc.c +Lifted ban to derive GPLv3, extended copyright range to 2010 + +23 Jan 2010 [3037] +- cdrskin/add_ts_changes_to_libburn_0_7_4 +- cdrskin/add_ts_changes_to_libburn_0_7_5 ++ cdrskin/add_ts_changes_to_libburn_0_7_6 ++ cdrskin/add_ts_changes_to_libburn_0_7_7 +Updated cdrskin tarball generator + +------------------------------------ cycle - cdrskin-0.7.7 - 2010.01.23.104423 + + +23 Jan 2010 [3038] +svn move -m 'libburn release 0.7.6 is ready' + http://svn.libburnia-project.org/libburn/branches/ZeroSevenSix + http://svn.libburnia-project.org/libburn/tags/ZeroSevenSix + +2010.02.04.083315 [3054] +acinclude.m4 +configure.ac +Forcing use of /usr/local on FreeBSD by LDFLAGS and CPPFLAGS + +2010.02.12.173236 [3063] +libburn/os-linux.h +libburn/sg-linux.c +Changed system adapter id and some remarks from "Linux" to "GNU/Linux" + +2010.02.12.212818 [3064] +libburn/libburn.h +libburn/sg.c +libburn/mmc.c +libburn/drive.c +libburn/init.c +libburn/cleanup.c +libburn/os-linux.h +libburn/sg-linux.c +libburn/write.c +libburn/read.c +libburn/sg-libcdio.c +libburn/os-libcdio.h +libburn/os.h +libburn/toc.c +Changed docs and comments to "GNU/Linux" where appropriate + +2010.02.14.084452 [3066] +libburn/sbc.c +libburn/file.h +libburn/os-libcdio.h +libburn/os-dummy.h +libburn/cleanup.h +libburn/sector.h +libburn/libiso_msgs.c +libburn/async.c +libburn/libdax_audioxtr.h +libburn/ecma130ab.c +libburn/back_hacks.h +libburn/libdax_msgs.h +libburn/drive.h +libburn/read.c +libburn/source.c +libburn/util.h +libburn/cleanup.c +libburn/sg.c +libburn/init.c +libburn/write.c +libburn/transport.h +libburn/write.h +libburn/libburn.h +libburn/options.c +libburn/mmc.h +libburn/sg.h +libburn/sbc.h +libburn/sg-dummy.c +libburn/ecma130ab.h +libburn/null.c +libburn/structure.c +libburn/mmc.c +libburn/spc.h +libburn/drive.c +libburn/sg-linux.c +libburn/options.h +libburn/os-linux.h +libburn/sg-libcdio.c +libburn/os-freebsd.h +libburn/sg-freebsd-port.c +libburn/sector.c +libburn/debug.c +libburn/util.c +libburn/toc.h +libburn/file.c +libburn/libdax_audioxtr.c +libburn/libdax_msgs.c +libburn/toc.c +libburn/sg-freebsd.c +libburn/spc.c +libburn/structure.h +Added or adjusted copyright and license statements in single files + +2010.02.14.171833 [3069] +libburn/write.c +libburn/read.c +libburn/sector.c +libburn/crc.h +Created opportunity to omit source module libburn/crc.c + +2010.02.15.125922 [3071] +libburn/crc.h +Changed a comment in libburn/crc.h + +2010.02.16.194147 [3073] +libburn/file.c +Bug fix on FreeBSD: Piped input was falsely attributed a small fixed size + +2010.02.17.141409 [3075] +libburn/async.c +libburn/drive.c +libburn/write.c +Avoided random percentage display at start of blanking + +2010.02.22.134904 [3080] +libburn/init.c +Made burn_set_signal_handling() more suitable for cdrskin + +2010.02.25.070635 [3090] +libburn/write.c +Corrected optional speed curb for stdio: drives. Was damaged by revision 2903. + +2010.02.28.104003 [3091] +cdrskin/cdrskin.c +Added forgotten initialization of a variable + +2010.02.28.110749 [3092] +cdrskin/cdrskin.c +Bug fix: cdrskin fs=0 lead to SIGSEGV. Regression introduced by revision 2936. + +28 Feb 2010 [3093] +cdrskin/cdrskin.1 +Corrected spelling errors in cdrskin man page + +2010.03.03.140639 [3096] +libburn/drive.c +Enabled patience 0 within burn_abort() + +2010.03.03.141407 [3097] +cdrskin/cdrskin.c +cdrskin/cdrfifo.c +Adapted cdrskin abort handler to FreeBSD peculiarities + +3 Mar 2010 [3098] [3099] +cdrskin/compile_cdrskin.sh +Enabled static compile script compile_cdrskin.sh for FreeBSD + +2010.03.04.121441 [3100] +libburn/sg-linux.c +Showing more patience with temporarily busy drives on Linux + +2010.03.04.180102 [3101] +cdrskin/cdrskin.c +Changed burn_abort(0) to burn_abort(-1) + +2010.03.05.090948 [3102] +libburn/libburn.h +libburn/transport.h +libburn/drive.h +libburn/drive.c +libburn/init.h +libburn/init.c +libburn/async.c +libburn/write.c +libburn/sector.c +libburn/libdax_msgs.h +libburn/libdax_msgs.c +Introduced alternative signal handling actions + +2010.03.05.091432 [3103] +cdrskin/cdrskin.c +Enabled optional use of new signal action 2 with libburn built-in handler + +2010.03.05.111712 [3104] +libburn/init.c +libburn/drive.h +libburn/drive.c +Removed some debugging printing + +------------------------------------ cycle - cdrskin-0.7.7 - 2010.03.05.111954 +Bug fix on FreeBSD: Piped input was falsely attributed a small fixed size +Bug fix: cdrskin fs=0 led to SIGSEGV. Regression introduced by revision 2936. + + +2010.03.05.190110 [3105] +libburn/async.c +Protected blanker and formatter thread from signals + +06 Mar 2010 [3106] +test/libburner.c +Adapted libburner to new advise about signal handling + +07 Mar 2010 [3107] +libburn/libburn.h +test/libburner.c +Changed examples burn_set_signal_handling(...,48) to (...,0x30) + +2010.03.08.092608 [3108] +libburn/libburn.h +libburn/init.c +Prevented potential memory fault with burn_set_signal_handling() + +2010.03.09.140341 [3111] +libburn/libburn.h +Clarifications in API description of burn_set_signal_handling() + +10 Mar 2010 [3112] +svn copy -m "Branching for libburn release 0.7.8" \ + http://svn.libburnia-project.org/libburn/trunk \ + http://svn.libburnia-project.org/libburn/branches/ZeroSevenEight + +2010.03.10.120001 [3113] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.7.8 + +10 Mar 2010 [3114] +- cdrskin/add_ts_changes_to_libburn_0_7_6 +- cdrskin/add_ts_changes_to_libburn_0_7_7 ++ cdrskin/add_ts_changes_to_libburn_0_7_8 ++ cdrskin/add_ts_changes_to_libburn_0_7_9 +Updated cdrskin tarball generator + +10 Mar 2010 [3115] +cdrskin/cdrskin.c +Removed unused variable + +10 Mar 2010 [3116] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.7.8.pl00 - 2010.03.10.120001 +Bug fix on FreeBSD: Piped input was falsely attributed a small fixed size +Bug fix: cdrskin fs=0 led to SIGSEGV. Regression introduced by version 0.7.4. + + +2010.03.10.134802 [3117] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.7.9 + +10 Mar 2010 [3118] +- cdrskin/add_ts_changes_to_libburn_0_7_6 +- cdrskin/add_ts_changes_to_libburn_0_7_7 ++ cdrskin/add_ts_changes_to_libburn_0_7_8 ++ cdrskin/add_ts_changes_to_libburn_0_7_9 +Updated cdrskin tarball generator + +10 Mar 2010 [3119] +cdrskin/changelog.txt +Documented changes and release timestamp + +10 Mar 2010 [3120] +svn move -m 'libburn release 0.7.8 is ready' + http://svn.libburnia-project.org/libburn/branches/ZeroSevenEight + http://svn.libburnia-project.org/libburn/tags/ZeroSevenEight + +------------------------------------ cycle - cdrskin-0.7.9 - 2010.03.10.143607 + +2010.03.17.185222 [3123] +cdrskin/cdrskin.c +Small bug fix about track size with cdrskin -minfo + +17 Mar 2010 [3124] +configure.ac +Corrected initialization of configure option --enable-dvd-obs-64k + +2010.03.25.113536 [3131] +libburn/spc.c +libburn/sg-freebsd.c +Changed sg-freebsd.c to work with ahci, advise by Alexander Motin + +2010.03.26.083158 [3132] +libburn/sg-freebsd.c +Had to make ahci change conditional for now: -DLibburn_for_freebsd_ahcI + +2010.03.27.155659 [3133] +libburn/mmc.c +Avoiding to inquire NWA of unwritable media or states + +2010.03.27.172644 [3134] +libburn/os-freebsd.h +libburn/sg-freebsd.c +Trying to detect FreeBSD ahci devices and to handle others the old way + +------------------------------------ cycle - cdrskin-0.7.9 - 2010.03.27.184614 +* Now able to work with ahci driver of FreeBSD 8-STABLE + + +2010.03.29.103141 [3135] +libburn/spc.c +libburn/sg-linux.c +libburn/sg-freebsd.c +libburn/sg-libcdio.c +Adjusted libcdio system adapter to FreeBSD peculiarities + +2010.04.04.181237 [3146] +test/libburner.c +Let libburner warn programmers if they forget to set 64 bit off_t + +2010.04.09.090645 [3155] +libburn/sg-linux.c +Reporting eventual SCSI sense in sg-linux repeat loop + +9 Apr 2010 [3156] +svn copy -m "Branching for libburn release 0.8.0" \ + http://svn.libburnia-project.org/libburn/trunk \ + http://svn.libburnia-project.org/libburn/branches/ZeroEightZero + +2010.04.09.100001 [3157] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.8.0 + +09 Apr 2010 [3158] +- cdrskin/add_ts_changes_to_libburn_0_7_8 +- cdrskin/add_ts_changes_to_libburn_0_7_9 ++ cdrskin/add_ts_changes_to_libburn_0_8_0 ++ cdrskin/add_ts_changes_to_libburn_0_8_1 +Updated cdrskin tarball generator + +09 Apr 2010 [3159] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.8.0.pl00 - 2010.04.09.100001 +* Now able to work with ahci driver of FreeBSD 8-STABLE + + +2010.04.09.104907 [3160] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.8.1 + +09 Apr 2010 [3161] +- cdrskin/add_ts_changes_to_libburn_0_7_8 +- cdrskin/add_ts_changes_to_libburn_0_7_9 ++ cdrskin/add_ts_changes_to_libburn_0_8_0 ++ cdrskin/add_ts_changes_to_libburn_0_8_1 +Updated cdrskin tarball generator + +09 Apr 2010 [3162] +cdrskin/changelog.txt +Documented changes and release timestamp + +09 Apr 2009 [3163] +svn move -m libburn release 0.8.0 is ready +http://svn.libburnia-project.org/libburn/branches/ZeroEightZero +http://svn.libburnia-project.org/libburn/tags/ZeroEightZero + +------------------------------------ cycle - cdrskin-0.8.1 - 2010.04.09.110341 + + +2010.04.30.180350 [3206] +libburn/mmc.c +Avoided to create track without toc_entry from "hidden first track" on CD + +------------------------------------ cycle - cdrskin-0.8.1 - 2010.04.30.182846 +* Bug fix: CD TOC was not read if the first track did not start at LBA 0 + + +2010.05.01.082808 [3207] +libburn/util.c +libburn/mmc.c +Bug fix: CD-ROM media got attributed random lead-in and lead-out adresses + +------------------------------------ cycle - cdrskin-0.8.1 - 2010.05.01.083316 +* Bug fix: CD-ROM media got attributed random lead-in and lead-out adresses + + +2010.05.16.090624 [3219] +libburn/async.c +libburn/cleanup.c +libburn/crc.c +libburn/ddlpa.c +libburn/debug.c +libburn/drive.c +libburn/ecma130ab.c +libburn/file.c +libburn/init.c +libburn/libdax_audioxtr.c +libburn/libdax_msgs.c +libburn/mmc.c +libburn/null.c +libburn/options.c +libburn/read.c +libburn/sbc.c +libburn/sector.c +libburn/sg.c +libburn/sg-dummy.c +libburn/sg-freebsd.c +libburn/sg-freebsd-port.c +libburn/sg-libcdio.c +libburn/sg-linux.c +libburn/source.c +libburn/spc.c +libburn/structure.c +libburn/toc.c +libburn/util.c +libburn/write.c +Eventually including ../config.h generated by autotools + +2010.05.29.074318 [3237] +libburn/sg-libcdio.c +Bug fix: SIGSEGV of libcdio system adapter if drive list is empty + +2010.06.07.171552 [3239] +libburn/sg-libcdio.c +Avoiding to resolve /dev/rdsk/cXtYdZs2 drive addresses on Solaris libcdio + +2010.06.07.171706 [3240] +libburn/init.c +cdrskin/cdrskin.c +Reacted on harmless compiler warnings on Solaris + +2010.06.07.172925 [3241] +README +cdrskin/README +Makefile.am +libburn/os.h +libburn/sg.c ++ libburn/os-solaris.h ++ libburn/sg-solaris.c +libburn/libdax_msgs.h +cdrskin/cdrskin_eng.html +New system adapter for Solaris uscsi (tested on snv134, kernel 5.11) + +2010.06.08.173156 [3244] +libburn/os-solaris.h +Corrected comment + +2010.06.08.174946 [3245] +libburn/sg-solaris.c +Handled SCSI status "BUSY" on Solaris + +------------------------------------ cycle - cdrskin-0.8.1 - 2010.06.08.175125 +* Bug fix: SIGSEGV of libcdio system adapter if drive list is empty +* New system adapter for Solaris uscsi (tested on snv134, kernel 5.11) + + +2010.06.08.200313 [3246] +libburn/sg-libcdio.c +libburn/sg-solaris.c +Implemented block device capacity determination fo Solaris + +2010.06.10.170901 [3247] +libburn/sg-solaris.c +Removed obsolete development remark + +2010.06.10.171018 [3248] +README +cdrskin/README +cdrskin/cdrskin.1 +Mentioned Solaris and system dependent drive permission settings + + [] +svn copy -m "Branching for libburn release 0.8.2" \ + http://svn.libburnia-project.org/libburn/trunk \ + http://svn.libburnia-project.org/libburn/branches/ZeroEightTwo + +2010.06.11.080001 [3251] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.8.2 + +11 Jun 2010 [3252] +- cdrskin/add_ts_changes_to_libburn_0_8_0 +- cdrskin/add_ts_changes_to_libburn_0_8_1 ++ cdrskin/add_ts_changes_to_libburn_0_8_2 ++ cdrskin/add_ts_changes_to_libburn_0_8_3 +Updated cdrskin tarball generator + +11 Jun 2010 [3253] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.8.2.pl00 - 2010.06.11.080001 +* Bug fix: CD TOC was not read if the first track did not start at LBA 0 +* Bug fix: CD-ROM media got attributed random lead-in and lead-out adresses +* Bug fix: SIGSEGV of experimental libcdio system adapter if drive list is empty +* New system adapter for Solaris uscsi (tested on snv134, kernel 5.11) + + +2010.06.11.104830 [3254] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.8.3 + +11 Jun 2010 [3255] +- cdrskin/add_ts_changes_to_libburn_0_8_0 +- cdrskin/add_ts_changes_to_libburn_0_8_1 ++ cdrskin/add_ts_changes_to_libburn_0_8_2 ++ cdrskin/add_ts_changes_to_libburn_0_8_3 +Updated cdrskin tarball generator + +11 Jun 2010 [3256] +cdrskin/changelog.txt +Documented changes and release timestamp + +11 Jun 2010 [3257] +svn move -m libburn release 0.8.2 is ready +http://svn.libburnia-project.org/libburn/branches/ZeroEightTwo +http://svn.libburnia-project.org/libburn/tags/ZeroEightTwo + +------------------------------------ cycle - cdrskin-0.8.3 - 2010.06.11.104830 + + +2010.06.13.190707 [3267] +libburn/sg-solaris.c +New less obtrusive implementation of sg_is_enumerable_adr for Solaris + +2010.06.15.155423 [3272] +libburn/sg-libcdio.c +Avoided compiler warning about unused variable on non-Solaris systems + +2010.06.15.155625 [3273] +libburn/os-dummy.h +libburn/os-libcdio.h +Let general POSIX system adapters ignore SIGWINCH and SIGURG if defined + +2010.06.15.155739 [3274] +configure.ac +Incremented LT_CURRENT and LT_AGE of libburn + +2010.06.16.082457 [3277] +libburn/os-solaris.h +libburn/os-libcdio.h +libburn/os-freebsd.h +Allowed 64 kB max output buffer size on all OSes + +30 Jun 2010 [3307] +svn copy -m "Branching for libburn release 0.8.4" \ + http://svn.libburnia-project.org/libburn/trunk \ + http://svn.libburnia-project.org/libburn/branches/ZeroEightFour + +2010.06.30.100001 [3308] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.8.4 + +30 Jun 2010 [3309] +- cdrskin/add_ts_changes_to_libburn_0_8_2 +- cdrskin/add_ts_changes_to_libburn_0_8_3 ++ cdrskin/add_ts_changes_to_libburn_0_8_4 ++ cdrskin/add_ts_changes_to_libburn_0_8_5 +Updated cdrskin tarball generator + +30 Jun 2010 [3310] +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.8.4.pl00 - 2010.06.30.100001 +* Let general POSIX system adapters ignore SIGWINCH and SIGURG if defined +* Allowed 64 kB max output buffer size on all OSes + + +2010.06.30.112709 [3311] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.8.5 + +30 Jun 2010 [3312] +- cdrskin/add_ts_changes_to_libburn_0_8_2 +- cdrskin/add_ts_changes_to_libburn_0_8_3 ++ cdrskin/add_ts_changes_to_libburn_0_8_4 ++ cdrskin/add_ts_changes_to_libburn_0_8_5 +Updated cdrskin tarball generator + +30 Jun 2010 [3313] +cdrskin/changelog.txt +Documented changes and release timestamp + +30 Jun 2010 [3314] +svn move -m libburn release 0.8.4 is ready \ +http://svn.libburnia-project.org/libburn/branches/ZeroEightFour \ +http://svn.libburnia-project.org/libburn/tags/ZeroEightFour + +------------------------------------ cycle - cdrskin-0.8.5 - 2010.06.30.112709 + + +2010.07.04.131219 [3323] +libburn/libburn.h +libburn/libdax_audioxtr.h +libburn/libdax_audioxtr.c +cdrskin/cdrskin.c +test/dewav.c +Moved public part of libdax_audioxtr.h to libburn.h + +2010.07.04.170035 [3325] +configure.ac +Makefile.am ++ libburn/libburn.ver +Hiding all non-API symbols from the linker by use of --version-script + +04 Jul [3326] +README +Mentioned new configure option --disable-versioned-libs + +2010.07.06.113304 [3329] +configure.ac +acinclude.m4 +Let configure perform linker test with --version-script if enabled + +2010.07.06.113356 [3330] +libburn/libburn.h +Mentioned that public API calls must be in libisofs/libisofs.ver + +2010.07.12.193201 [3334] +cdrskin/cdrskin.c +cdrskin/cdrfifo.c +Changed all malloc() to calloc() + +2010.07.12.193644 [3335] +libburn/async.c +libburn/drive.c +libburn/file.c +libburn/libdax_audioxtr.c +libburn/libdax_msgs.c +libburn/mmc.c +libburn/null.c +libburn/options.c +libburn/read.c +libburn/sg-freebsd.c +libburn/sg-freebsd-port.c +libburn/util.c +libburn/write.c +Changed all malloc() to calloc() + +2010.07.29.083453 [3336] +libburn/spc.h +libburn/spc.c +libburn/mmc.c +libburn/sg-freebsd.c +libburn/sg-solaris.c +libburn/sg-libcdio.c +Recognizing sense data format 0x72 if given instead of 0x70 + +2010.07.29.164100 [3337] +Makefile.am +Detached make target "doc" from target "all". + +30 Jul 2010 [3341] +doc/doxygen.conf.in +Removed problematic DETAILS_AT_TOP to silence warning of Debian buildd + +2010.08.02.100630 [3345] +libburn/spc.h +libburn/spc.c +libburn/sg-linux.c +libburn/sg-freebsd.c +libburn/sg-libcdio.c +Reporting sense data with burn_set_scsi_logging() + +2010.08.02.131946 [3346] +libburn/sg-linux.c +Added error simulation code to Linux system adapter + +2010.08.02.141233 [3347] +libburn/sg-solaris.c +Committed Solaris system adapter which was forgotten with rev 3345 + +------------------------------------ cycle - cdrskin-0.8.5 - 2010.08.02.141233 +* Hiding all non-API symbols from the linker by use of --version-script + + +2010.08.03.191151 [3348] +libburn/sg-linux.c +libburn/sg-freebsd.c +libburn/sg-libcdio.c +libburn/sg-solaris.c +Obeying burn_set_scsi_logging() with errors of class RETRY + +2010.08.08.091224 [3349] +libburn/spc.h +libburn/spc.c +libburn/sg-linux.c +libburn/sg-freebsd.c +libburn/sg-libcdio.c +libburn/sg-solaris.c +New SCSI comand response "GO_ON" + +2010.08.08.091317 [3350] +libburn/libburn.h +Hopefully silenced a warning of doxygen on Debian buildd + +2010.08.13.114139 [3352] +libburn/libdax_msgs.h +libburn/libdax_audioxtr.h +Corrected typo in macro names (which shall never be defined anyway) + +2010.08.21.095456 [3356] +libburn/libburn.h +libburn/mmc.c +cdrskin/cdrskin.1 +cdrskin/cdrskin_eng.html +Lifted test reservation on DVD-R DL media. Thanks to Kevin Kieffer for testing. + +28 Aug 2010 [3358] +test/libburner.c +Clarified the meaning of 0x0 and 0x30 signal handlers + +2010.09.14.124934 [3368] +libburn/spc.h +libburn/spc.c +libburn/sg-linux.c +libburn/sg-libcdio.c +libburn/sg-freebsd.c +libburn/sg-solaris.c +Centralized interpretation of SCSI command outcome + +------------------------------------ cycle - cdrskin-0.8.5 - 2010.09.14.135405 +* Lifted test reservation on DVD-R DL media. + + +15 Sep 2010 [3369] +ChangeLog +Meaningful change log file derived by George Danchev from web site + +16 Sep 2010 [3373] +svn copy -m Branching for libburn release 0.8.6 +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/ZeroEightSix + +2010.09.16.113001 [3374] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.8.6 + +16 Sep 2010 [3375] +- cdrskin/add_ts_changes_to_libburn_0_8_4 +- cdrskin/add_ts_changes_to_libburn_0_8_5 ++ cdrskin/add_ts_changes_to_libburn_0_8_6 ++ cdrskin/add_ts_changes_to_libburn_0_8_7 +Updated cdrskin tarball generator + +16 Sep 2010 [3376] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.8.6.pl00 - 2010.09.16.113001 +* Lifted test reservation on DVD-R DL media. +* Hiding all non-API symbols from the linker by use of --version-script + + +2010.09.17.073827 [3377] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.8.7 + +17 Sep 2010 [3378] +- cdrskin/add_ts_changes_to_libburn_0_8_4 +- cdrskin/add_ts_changes_to_libburn_0_8_5 ++ cdrskin/add_ts_changes_to_libburn_0_8_6 ++ cdrskin/add_ts_changes_to_libburn_0_8_7 +Updated cdrskin tarball generator + +17 Sep 2010 [3379] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +17 Sep 2010 [3381] +svn move -m libburn release 0.8.6 is ready +http://svn.libburnia-project.org/libburn/branches/ZeroEightSix +http://svn.libburnia-project.org/libburn/tags/ZeroEightSix + +------------------------------------ cycle - cdrskin-0.8.7 - 2010.09.17.082926 + +2010.09.22.105426 [3395] +acinclude.m4 +configure.ac +Makefile.am +README +On Linux: Run ldconfig during make install,if not --disable-ldconfig-at-install + +2010.09.22.175054 [3397] +libburn/libburn.h +libburn/libburn.ver +libburn/libdax_msgs.h +libburn/file.h +libburn/file.c +libburn/source.h +libburn/source.c +New API call burn_offst_source_new() + +2010.09.22.180921 [3398] +Makefile.am ++ test/offst_source.c +Temporarily added test program for burn_offst_source_new() + +22 Sep 2010 [3399] +test/offst_source.c +Better default input file for test/offst_source.c + +2010.09.24.090631 [3401] +libburn/libburn.h +libburn/drive.c +libburn/mmc.h +libburn/mmc.c +ChangeLog +New API call burn_disc_get_bd_spare_info() + +2010.09.24.090731 [3402] +cdrskin/cdrskin.c +Displaying eventual BD spare area information with -minfo + +2010.09.24.091902 [3403] +cdrskin/cdrskin.c +Displaying eventual BD spare area information with --list_formats + +2010.09.24.092535 [3404] +libburn/libburn.ver +Making new API call available in dynamic library + +2010.09.24.100255 [3405] +cdrskin/cdrskin.c +Polished appearance of BD spare info with --list_formats + +------------------------------------ cycle - cdrskin-0.8.7 - 2010.09.24.100255 +* New API call burn_offst_source_new() +* New API call burn_disc_get_bd_spare_info() + + +2010.09.28.101043 [3408] +libburn/mmc.c +Avoiding to inquire spare area of unsuitable media + +2010.10.15.191717 [3445] +libburn/write.c +libburn/libdax_msgs.h +Issue warning after writing a BD-R with more than 300 sessions + +2010.10.19.165908 [3450] +libburn/async.c +libburn/write.c +libburn/libdax_msgs.h +Issueing messages with all cases of burn canceling + +20 Oct 2010 [3453] +svn copy -m Branching for libburn release 0.8.8 +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/ZeroEightEight + +2010.10.20.120001 [3454] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.8.8 + +20 Oct 2010 [3455] +- cdrskin/add_ts_changes_to_libburn_0_8_6 +- cdrskin/add_ts_changes_to_libburn_0_8_7 ++ cdrskin/add_ts_changes_to_libburn_0_8_8 ++ cdrskin/add_ts_changes_to_libburn_0_8_9 +Updated cdrskin tarball generator + +20 Oct 2010 [3456] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.8.8.pl00 - 2010.10.20.120001 +* New API call burn_offst_source_new() +* New API call burn_disc_get_bd_spare_info() + + +2010.10.20.125207 [3457] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.8.9 + +20 Oct 2010 [3458] +- cdrskin/add_ts_changes_to_libburn_0_8_6 +- cdrskin/add_ts_changes_to_libburn_0_8_7 ++ cdrskin/add_ts_changes_to_libburn_0_8_8 ++ cdrskin/add_ts_changes_to_libburn_0_8_9 +Updated cdrskin tarball generator + +20 Oct 2010 [3459] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +20 Oct 2010 +svn move -m libburn release 0.8.8 is ready +http://svn.libburnia-project.org/libburn/branches/ZeroEightEight +http://svn.libburnia-project.org/libburn/tags/ZeroEightEight + +------------------------------------ cycle - cdrskin-0.8.9 - 2010.10.20.134102 + + +2010.10.29.164059 [3474] +libburn/mmc.c +libburn/libdax_msgs.h +Issueing error messages if cache syncing or closing fails + +2010.10.29.174455 [3476] +libburn/spc.c +Regression fix: SCSI reply data logging was disabled in rev 3368, 0.8.6 + +2010.11.16.131221 [3483] +libburn/sg-linux.c +libburn/sg-freebsd.c +libburn/sg-libcdio.c +libburn/sg-solaris.c +Removed outdated development macros + +08 Dec 2010 [3502] +svn copy -m Branching for libburn release 0.9.0 +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/ZeroNineZero + +2010.12.08.133001 [3503] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.9.0 + +08 Dec 2010 [3504] +- cdrskin/add_ts_changes_to_libburn_0_8_8 +- cdrskin/add_ts_changes_to_lisburn_0_8_9 ++ cdrskin/add_ts_changes_to_libburn_0_9_0 ++ cdrskin/add_ts_changes_to_libburn_0_9_1 +Updated cdrskin tarball generator + +08 Dec 2010 [3505] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-0.9.0.pl00 - 2010.12.08.133001 +Regression fix: SCSI reply data logging was disabled in release 0.8.6 + + +2010.12.08.131934 [3506] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 0.9.1 + +08 Dec 2010 [3507] +- cdrskin/add_ts_changes_to_libburn_0_8_8 +- cdrskin/add_ts_changes_to_lisburn_0_8_9 ++ cdrskin/add_ts_changes_to_libburn_0_9_0 ++ cdrskin/add_ts_changes_to_libburn_0_9_1 +Updated cdrskin tarball generator + +08 Dec 2010 [3508] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +08 Dec 2010 [3509] +svn move -m libburn release 0.9.0 is ready +http://svn.libburnia-project.org/libburn/branches/ZeroNineZero +http://svn.libburnia-project.org/libburn/tags/ZeroNineZero + +------------------------------------ cycle - cdrskin-0.9.1 - 2010.12.08.154833 + +2010.12.13.075956 [3522] +configure.ac +Prepending ./configure generated options to CFLAGS rather than appending them + +23 Dec 2010 [3528] +doc/comments +Updated API introduction + +2010.12.28.071904 [3537] +libburn/write.c +Allowed umask to create stdio-drive files with rw-permissions for all + +2011.01.03.195125 [3542] +libburn/write.c +libburn/sector.c +libburn/structure.h +libburn/structure.c +Allowed stdio tracks of known size to end in TAO mode on premature EOF + +2011.01.09.135915 [3546] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Refusing to burn if foreseeable size exceeds media capacity + +------------------------------------ cycle - cdrskin-0.9.1 - 2011.01.09.140240 +* Allowed umask to create stdio-drive files with rw-permissions for all +* cdrskin now refuses to burn if the foreseeable size exceeds media capacity + + +09 Jan 2011 [3547] +cdrskin/cdrskin_eng.html +ChangeLog +Updated change log and web page + +16 Jan 2011 [3548] +svn copy -m "Branching for libburn release 1.0.0" \ + http://svn.libburnia-project.org/libburn/trunk \ + http://svn.libburnia-project.org/libburn/1.0.0" + +2011.01.16.123001 [3549] [3550] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.0.0 + +16 Jan 2011 [3551] +- cdrskin/add_ts_changes_to_libburn_0_9_0 +- cdrskin/add_ts_changes_to_libburn_0_9_1 ++ cdrskin/add_ts_changes_to_libburn_1_0_0 ++ cdrskin/add_ts_changes_to_libburn_1_0_1 +Updated cdrskin tarball generator + +16 Jan 2011 [3552] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-1.0.0.pl00 - 2011.01.16.123001 +* Allowed umask to create stdio-drive files with rw-permissions for all +* cdrskin now refuses to burn if the foreseeable size exceeds media capacity + + +2011.01.16.140456 [3553] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.0.1 + +16 Jan 2011 [3554] +- cdrskin/add_ts_changes_to_libburn_0_9_0 +- cdrskin/add_ts_changes_to_libburn_0_9_1 ++ cdrskin/add_ts_changes_to_libburn_1_0_0 ++ cdrskin/add_ts_changes_to_libburn_1_0_1 +Updated cdrskin tarball generator + +16 Jan 2011 [3555] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +16 Jan 2011 [3556] +svn move -m "libburn release 1.0.0 is ready" \ +http://svn.libburnia-project.org/libburn/branches/1.0.0 \ +http://svn.libburnia-project.org/libburn/tags/1.0.0 + + +------------------------------------ cycle - cdrskin-1.0.1 - 2011.01.16.152923 + + +2011.01.18.162859 [3570] +libburn/file.c +Using usleep() instead of nanosleep() which is not available on Solaris 9 + +2011.02.09.114311 [3586] +libburn/write.c +libburn/drive.c +Forced role 3 on drives which stem from open file descriptors without O_RDWR + +2011.02.14.085951 [3589] +libburn/drive.c +Reacted on compiler warnings about uninitialized variables + +2011.02.18.165542 [3592] +libburn/drive.c +libburn/sector.c +DEBUG message with burn_drive_cancel, FAILURE with premature end-of-input + +23 Feb 2011 [3604] +svn copy -m Branching for libburn release 1.0.2 +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/1.0.2 + +2011.02.23.130001 [3605] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.0.2 + +23 Feb 2011 [3606] +- cdrskin/add_ts_changes_to_libburn_1_0_0 +- cdrskin/add_ts_changes_to_libburn_1_0_1 ++ cdrskin/add_ts_changes_to_libburn_1_0_2 ++ cdrskin/add_ts_changes_to_libburn_1_0_3 +Updated cdrskin tarball generator + +23 Feb 2011 [3607] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-1.0.2.pl00 - 2011.02.23.130001 +* Removed compilation obstacles on Solaris 9. +* Improved recognition of non-seekable stdio pseudo-drives. + + +2011.02.23.193502 [3611] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.0.3 + +23 Feb 2011 [3612] +- cdrskin/add_ts_changes_to_libburn_1_0_0 +- cdrskin/add_ts_changes_to_libburn_1_0_1 ++ cdrskin/add_ts_changes_to_libburn_1_0_2 ++ cdrskin/add_ts_changes_to_libburn_1_0_3 +Updated cdrskin tarball generator + +23 Feb 2011 [3613] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-1.0.3 - 2011.02.23.193502 + + +23 Feb 2011 [3614] +svn move -m libburn release 1.0.2 is ready +http://svn.libburnia-project.org/libburn/branches/1.0.2 +http://svn.libburnia-project.org/libburn/tags/1.0.2 + +2011.02.23.195650 [3615] +libburn/libdax_msgs.h +Registered new error code + +2011.02.24.191718 [3619] +libburn/async.c +Corrected a flaw found by George Danchev with cpp + +2011.03.01.144625 [3625] +libburn/drive.c +Bug fix: Read-only file descriptors were classified as write-only pseudo drives + +svn copy -m Branching for libburn release 1.0.4 +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/1.0.4 + +2011.03.10.080001 [3651] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.0.4 + +10 Mar 2011 [3652] +- cdrskin/add_ts_changes_to_libburn_1_0_2 +- cdrskin/add_ts_changes_to_libburn_1_0_3 ++ cdrskin/add_ts_changes_to_libburn_1_0_4 ++ cdrskin/add_ts_changes_to_libburn_1_0_5 +Updated cdrskin tarball generator + +10 Mar 2011 [3653] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-1.0.4.pl00 - 2011.03.10.080001 +* Bug fix: Read-only file descriptors were classified as write-only pseudo drives + + +2011.03.10.132603 [3657] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.0.5 + +10 Mar 2011 [3659] +- cdrskin/add_ts_changes_to_libburn_1_0_2 +- cdrskin/add_ts_changes_to_libburn_1_0_3 ++ cdrskin/add_ts_changes_to_libburn_1_0_4 ++ cdrskin/add_ts_changes_to_libburn_1_0_5 +Updated cdrskin tarball generator + +10 Mar 2011 [3660] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +10 Mar 2011 [3658] +svn move -m libburn release 1.0.4 is ready +http://svn.libburnia-project.org/libburn/branches/1.0.4 +http://svn.libburnia-project.org/libburn/tags/1.0.4 + +------------------------------------ cycle - cdrskin-1.0.5 - 2011.03.10.132603 + + +12 Mar 2011 [3664] +COPYRIGHT +Updated copyright year + +2011.03.12.093520 [3665] +libburn/write.c +libburn/mmc.c +Burning DVD-R DAO with 2 kB size granularity rather than 32 kB + +2011.03.13.130746 [3666] +libburn/libburn.h +libburn/init.c +libburn/drive.c +libburn/async.c +libburn/write.c +libburn/libdax_msgs.h +libburn/libburn.ver +New API call burn_allow_drive_role_4() + +2011.03.13.130850 [3667] +cdrskin/cdrskin.c +Using burn_allow_drive_role_4() in cdrskin + +2011.03.13.192627 [3671] +libburn/read.c +libburn/libdax_msgs.h +Changed severity of "Read attempt on write-only drive" from FATAL to FAILURE + +2011.03.18.093128 [3672] +cdrskin/cdrskin.c +Prepared cdrskin for drive role 5 + +2011.03.18.093410 [3673] +libburn/transport.h +libburn/drive.c +libburn/async.c +libburn/write.c +libburn/read.c +libburn/libdax_msgs.h +Provisory introduction of drive role 5, random access write-only + +2011.03.18.153326 [3674] +libburn/async.c +libburn/mmc.c +Enabled BD formatting by index on Pioneer BDR-205 which offers no Cert or QCert + +2011.03.19.222152 [3675] +libburn/sector.c +Silenced an error message with input that is not aligned to 2 kB + +2011.03.21.090313 [3676] +libburn/drive.c +Corrected nwa computation for drive role 5 + +2011.03.21.090430 [3677] +libburn/write.c +Adjustments for drive role 5, random access write-only + +2011.03.21.092220 [3678] +libburn/async.c +Enabled blanking of drive with role 5 + +2011.03.22.085956 [3685] +libburn/libburn.h +libburn/init.c +libburn/drive.c +Avoiding appendable role 5 if not explicitely enabled + +2011.03.24.182148 [3687] +libburn/libburn.h +libburn/read.c +libburn/libdax_msgs.h +Better handling of read attempt on pseudo-drive without read-permission + +8 Apr 2011 [3712] +svn copy -m Branching for libburn release 1.0.6 +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/1.0.6 + +2011.04.08.180001 [3713] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.0.6 + +08 Apr 2011 [3714] +- cdrskin/add_ts_changes_to_libburn_1_0_4 +- cdrskin/add_ts_changes_to_libburn_1_0_5 ++ cdrskin/add_ts_changes_to_libburn_1_0_6 ++ cdrskin/add_ts_changes_to_libburn_1_0_7 +Updated cdrskin tarball generator + +08 Apr 2011 [3715] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------ release - cdrskin-1.0.6.pl00 - 2011.04.08.180001 +* Burning DVD-R DAO with 2 kB size granularity rather than 32 kB +* New API call burn_allow_drive_role_4() + + +2011.04.09.090001 [3719] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.0.7 + +09 Apr 2011 [3720] +- cdrskin/add_ts_changes_to_libburn_1_0_4 +- cdrskin/add_ts_changes_to_libburn_1_0_5 ++ cdrskin/add_ts_changes_to_libburn_1_0_6 ++ cdrskin/add_ts_changes_to_libburn_1_0_7 +Updated cdrskin tarball generator + +09 Apr 2011 [3721] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +09 Apr 2011 [3722] +svn move -m libburn release 1.0.6 is ready +http://svn.libburnia-project.org/libburn/branches/1.0.6 +http://svn.libburnia-project.org/libburn/tags/1.0.6 + +------------------------------------ cycle - cdrskin-1.0.7 - 2011.04.09.090001 + + +15 Apr 2011 [3735] +doc/doxygen.conf.in +Disabled HAVE_DOT in doxygen.conf + +2011.05.01.144546 [3743] +libburn/init.c +Closed tiny memory leak found by valgrind + +2011.05.12.120503 [3791] +libburn/drive.h +Including header pthread.h on request of Mats Andersson for OpenBSD + +2011.05.12.134723 [3792] +libburn/init.c +libburn/drive.c +libburn/structure.c +libburn/mmc.c +libburn/sg-linux.c +Reacted on -Wsign-compare warnings of gcc + +2011.05.12.135008 [3793] +cdrskin/cdrskin.c +Reacted on -Wsign-compare warnings of gcc + +2011.05.12.135116 [3794] +test/libburner.c +Reacted on -Wsign-compare warnings of gcc + +2011.05.12.135217 [3795] +test/telltoc. +Reacted on -Wsign-compare warnings of gcc + +12 May 2011 [3796] +test/poll.c +Reacted on -Wsign-compare warnings of gcc + +14 May 2011 [3803] +Makefile.am +doc/cookbook.txt +doc/mediainfo.txt +Added cookbook.txt and mediainfo.txt to tarball on request of George Danchev + +2011.05.15.104727 [3804] +libburn/init.h +Macros BURN_ALLOC_MEM, BURN_FREE_MEM for replaceing local variables + +2011.05.15.104926 [3805] +libburn/async.c +Replaced some large local variables by other means in libburn/async.c + +2011.05.15.181724 [3806] +libburn/init.h +Polished macro BURN_ALLOC_MEM + +2011.05.15.191420 [3807] +libburn/drive.c +Replaced some large local variables by other means in libburn/drive.c + +2011.05.15.203140 [3808] +libburn/init.h +libburn/async.c +Added forgotten return value to BURN_ALLOC_MEM + +22 May 2011 [3830] +doc/mediainfo.txt +Added a DVD+RW product id to the list + +22 May 2011 [3831] +configure.ac +Added options -Wextra -Wno-unused-parameter for gcc + +2011.05.23.154011 [3834] +libburn/mmc.c +Replaced some large local variables by other means in libburn/mmc.c + +2011.05.23.155217 [3835] +libburn/read.c +Replaced some large local variables by other means in libburn/read.c + +2011.05.23.163506 [3836] +libburn/sg-dummy.c +Replaced some large local variables by other means in libburn/sg-dummy.c + +2011.05.23.174016 [3837] +libburn/sg-freebsd.c +Replaced some large local variables by other means in libburn/sg-freebsd.c + +2011.05.23.182627 [3838] +libburn/sg-freebsd-port.c +Replaced some large local variables by other means in libburn/sg-freebsd-port.c + +2011.05.26.145626 [3839] +libburn/transport.h +libburn/libdax_msgs.h +libburn/mmc.c +Improved reaction on Damage Bit and missing NWA_V of READ TRACK INFORMATION + +2011.05.26.150020 [3840] +libburn/libburn.h +libburn/drive.c +libburn/libburn.ver +New API call burn_disc_next_track_is_damaged() + +2011.05.31.103124 [3846] +libburn/write.h +libburn/write.c +libburn/libdax_msgs.h +libburn/libburn.ver +New API call burn_disc_close_damaged() + +2011.06.02.083808 [3848] +libburn/sg-freebsd.c +libburn/sg-freebsd-port.c +Gave up use of bzero() in FreeBSD system adapters + +2011.06.02.132554 [3849] +libburn/sg-libcdio.c +Replaced some large local variables by other means in libburn/sg-libcdio.c + +2011.06.05.170431 [3850] +libburn/sg-linux.c +Replaced some large local variables by other means in libburn/sg-linux.c + +2011.06.06.104404 [3851] +libburn/sg-solaris.c +Replaced some large local variables by other means in libburn/sg-solaris.c + +2011.06.06.105052 [3852] +libburn/sg-solaris.c +Reacted on warnings of gcc about mixed sign comparison + +2011.06.06.173021 [3853] +libburn/spc.c +Replaced some large local variables by other means in libburn/spc.c + +2011.06.06.173611 [3854] +libburn/init.h +libburn/init.c +Leaner implementation of macro BURN_ALLOC_MEM + +2011.06.07.084343 [3855] +libburn/write.c +Closed a small memory leak with CD SAO found by valgrind + +2011.06.07.143930 [3856] +libburn/transport.h +libburn/spc.c +libburn/sbc.c +libburn/mmc.c +Consolidated several local struct command to a new member of struct burn_drive + +2011.06.08.081338 [3858] +libburn/structure.c +Replaced some large local variables by other means in libburn/structure.c + +2011.06.08.082201 [3859] +libburn/toc.c +Replaced some large local variables by other means in libburn/toc.c + +2011.06.08.181204 [3860] +libburn/mmc.c +Bug fix: burn_disc_format() on DVD-RW issued wrong block size with type 00h + +08 Jun 2011 [3861] +configure.ac +Makefile.am +Introduced AC_CONFIG_MACRO_DIR() and ACLOCAL_AMFLAGS on advise of George Danchev + +2011.06.08.200329 [3863] +libburn/write.c +Replaced some large local variables by other means in libburn/write.c + +09 Jun 2011 [3864] +bootstrap +Added option -I . to aclocal in bootstrap script on advise of George Danchev + +2011.06.14.152832 [3866] +libburn/spc.c +libburn/mmc.c +Reporting SCSI error if command RESERVE TRACK fails + + +Release 1.0.8 was skipped to get back in sync with libisofs and libisoburn. + + +18 Jun 2011 [3868] +svn copy -m Branching for libburn release 1.1.0 +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/1.1.1 + +2011.06.18.100001 [3869] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.1.0 + +18 Jun 2011 [3870] +- cdrskin/add_ts_changes_to_libburn_1_0_6 +- cdrskin/add_ts_changes_to_libburn_1_0_7 ++ cdrskin/add_ts_changes_to_libburn_1_1_0 ++ cdrskin/add_ts_changes_to_libburn_1_1_1 +Updated cdrskin tarball generator + +18 Jun 2011 [3871] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +----------------------------------- release - cdrskin-1.1.0 - 2011.06.18.100001 +* New API call burn_disc_next_track_is_damaged() +* New API call burn_disc_close_damaged() +* Bug fix: burn_disc_format() on DVD-RW issued wrong block size with type 00h + + +2011.06.18.154942 [3875] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.1.1 + +18 Jun 2011 [3876] +- cdrskin/add_ts_changes_to_libburn_1_0_6 +- cdrskin/add_ts_changes_to_libburn_1_0_7 ++ cdrskin/add_ts_changes_to_libburn_1_1_0 ++ cdrskin/add_ts_changes_to_libburn_1_1_1 +Updated cdrskin tarball generator + +18 Jun 2011 [3877] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +18 Jun 2011 [3878] +svn move -m libburn release 1.1.0 is ready +http://svn.libburnia-project.org/libburn/branches/1.1.0 +http://svn.libburnia-project.org/libburn/tags/1.1.0 + +------------------------------------ cycle - cdrskin-1.1.1 - 2011.06.18.154942 + +2011.06.19.203515 [3882] +libburn/sg-dummy.c +Bug fix: libburn-1.1.0 did only compile on Linux, FreeBSD, and Solaris + +20 Jun 2011 [3883] +svn copy -m Branching for libburn release 1.1.0.pl01 +http://svn.libburnia-project.org/libburn/tags/1.1.0 +http://svn.libburnia-project.org/libburn/branches/1.1.0.pl01 + +2011.06.20.110001 [3884] [3885] (branch 1.0.0.pl01) +libburn/sg-dummy.c +ChangeLog +cdrskin/cdrskin_timestamp.h +Bug fix: libburn-1.1.0 did only compile on Linux, FreeBSD, and Solaris + +20 Jun 2011 [3886] +svn move -m "libburn bugfix release 1.0.0.pl01 is ready" +http://svn.libburnia-project.org/libburn/branches/1.1.0.pl01 +http://svn.libburnia-project.org/libburn/tags/1.1.0.pl01 + +------------------------------ release - libburn-1.1.0.pl01 - 2011.06.20.110001 +* Bug fix: libburn-1.1.0 compiled only on Linux, FreeBSD, and Solaris + + +2011.06.22.093127 [3888] +libburn/sg-libcdio.c +Silenced compiler warnings about signedness + +22 Jun 2011 [3889] +releng/releng_build_os +Beginning to create a test suite for libburn and cdrskin + +2011.07.04.143922 [4041] +libburn/spc.c +Avoiding to load speed descriptor list twice + +2011.07.04.171649 [4047] +libburn/file.c +Reacted on warnings of -Wunused-but-set-variable + +2011.07.06.142532 [4071] +libburn/init.h +libburn/async.c +Reacted on warnings of -Wunused-but-set-variable + +2011.07.06.143104 [4072] +libburn/mmc.c +Reacted on warnings of -Wunused-but-set-variable + +2011.07.06.143313 [4073] +libburn/spc.c +Reacted on warnings of -Wunused-but-set-variable + +2011.07.06.143441 [4074] +libburn/toc.c +Reacted on warnings of -Wunused-but-set-variable + +2011.07.06.143536 [4075] +libburn/write.c +Reacted on warnings of -Wunused-but-set-variable + +2011.07.06.143724 [4076] +cdrskin/cdrskin.c +Reacted on warnings of -Wtype-limits and -Wunused-but-set-variable + +2011.07.06.143904 [4077] +cdrskin/cdrfifo.c +Reacted on warnings of -Wunused-but-set-variable + +2011.07.07.114911 [4083] +libburn/mmc.c +Silenced warning of cppcheck + +2011.07.07.115006 [4084] +libburn/sg-linux.c +Silenced warning of cppcheck + +2011.07.07.120719 [4085] +libburn/util.c +Reacted on warning of cppcheck + +2011.07.11.143934 [4133] +libburn/sg.c +Implemented macro Libburn_use_sg_freebsd_porT + +2011.07.12.095936 [4138] +cdrskin/cdrfifo.c +Reacted on warning of cppcheck about cdrskin/cdrfifo.c + +2011.07.12.105955 [4139] +cdrskin/cleanup.c +Reacted on warning of cppcheck about cdrskin/cleanup.c + +2011.07.12.110131 [4140] +libburn/async.c +Reacted on warning of cppcheck about libburn/async.c + +2011.07.12.110223 [4142] +libburn/cleanup.c +Reacted on warning of cppcheck about libburn/cleanup.c + +2011.07.12.110306 [4143] +libburn/drive.c +Reacted on warning of cppcheck about libburn/drive.c + +2011.07.12.110405 [4144] +libburn/read.c +Reacted on warning of cppcheck about libburn/read.c + +2011.07.12.110453 [4145] +test/poll.c +Reacted on warning of cppcheck about test/poll.c + +2011.07.12.141659 [4147] +libburn/sg.c +Improved intentional compiler warning about lack of suitable system adapter + +2011.07.12.141838 [4148] +libburn/sg-dummy.c +Reacted on warning of cppcheck about libburn/sg-dummy.c + +2011.07.12.141932 [4149] +libburn/sg-freebsd-port.c +Reacted on warning of cppcheck about libburn/sg-freebsd-port.c + +2011.07.12.142018 [4150] +libburn/sg-freebsd.c +Reacted on warning of cppcheck about libburn/sg-freebsd.c + +2011.07.12.142141 [4151] +libburn/sg-libcdio.c +Reacted on warning of cppcheck about libburn/sg-libcdio.c + +2011.07.12.155335 [4152] +cdrskin/cdrskin.c +Improved -atip and -minfo with empty drive + +2011.07.12.155417 [4153] +libburn/drive.c +Bug fix: Empty ROM drive was mistaken to hold an unsuitable disc + +2011.07.12.173727 [4154] +libburn/sg-solaris.c +Reacted on warning of cppcheck about libburn/sg-solaris.c + +2011.07.14.162150 [4164] +cdrskin/cdrskin.c +Fixed possible uninitialized variable use introduced by rev 4152 + +2011.07.15.081325 [4170] +libburn/sg-freebsd.c +Reacted on warning of cppcheck about libburn/sg-freebsd.c + +2011.07.28.191202 [4222] +libburn/libburn.h +libburn/drive.c +libburn/libburn.ver +New API call burn_lookup_device_link() + +2011.07.28.191536 [4223] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New option --device_links + +2011.07.31.080152 [4227] +libburn/transport.h +libburn/drive.h +libburn/drive.c +libburn/spc.c +libburn/mmc.c +Bug fix: Some drives returned wrong CD sizes after having burnt DVD-R + +2011.07.31.083046 [4228] +libburn/spc.c +libburn/mmc.c +Bug fix: Some drives return -150 as NWA of blank CD, rather than 0 + +2011.08.01.125405 [4232] +libburn/libburn.h +libburn/drive.c +libburn/mmc.c +libburn/libburn.ver +New API call burn_disc_get_phys_format_info() + +2011.08.01.153305 [4237] +libburn/mmc.h +Forgot to upload the header which defines mmc_get_phys_format_info + + +Release 1.1.2 was skipped to get back in sync with libisoburn. + + +07 Aug 2011 [4247] +copy -m Branching for libburn release 1.1.4 +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/1.1.4 + +2011.08.07.100001 [4249] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.1.4 + +07 Aug 2011 [4250] +- cdrskin/add_ts_changes_to_libburn_1_1_0 +- cdrskin/add_ts_changes_to_libburn_1_1_1 ++ cdrskin/add_ts_changes_to_libburn_1_1_4 ++ cdrskin/add_ts_changes_to_libburn_1_1_5 +Updated cdrskin tarball generator + +07 Aug 2011 [4251] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +07 Aug 2011 [4255] +cdrskin/cdrskin_eng.html +ChangeLog +cdrskin/changelog.txt +Forgot to mention cdrskin option --device_links + +----------------------------------- release - cdrskin-1.1.4 - 2011.08.07.100001 +* Bug fix: Some drives return -150 as NWA of blank CD, rather than 0. + libburn forwarded this misleading information to the application. +* Bug fix: Some drives returned wrong CD sizes after having burned DVD-R +* Bug fix: Empty ROM drive was mistaken to hold an unsuitable disc +* Bug fix: Avoiding to load speed descriptor list twice +* New API call burn_lookup_device_link() +* New API call burn_disc_get_phys_format_info() +* New cdrskin option --device_links + + +2011.08.08.121355 [4259] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.1.5 + +08 Aug 2011 [4260] +- cdrskin/add_ts_changes_to_libburn_1_1_0 +- cdrskin/add_ts_changes_to_libburn_1_1_1 ++ cdrskin/add_ts_changes_to_libburn_1_1_4 ++ cdrskin/add_ts_changes_to_libburn_1_1_5 +Updated cdrskin tarball generator + +08 Aug 2011 [4261] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-1.1.5 - 2011.08.08.121355 + + +08 Aug 2011 [4262] +svn move -m libburn release 1.1.4 is ready +http://svn.libburnia-project.org/libburn/branches/1.1.4 +http://svn.libburnia-project.org/libburn/tags/1.1.4 + +2011.08.17.160854 [4280] +libburn/libburn.h +libburn/transport.h +libburn/drive.c +Bug fix: stdio sizes > 4 TB - 32 kB caused integer rollover + +2011.08.17.162201 [4281] +libburn/drive.c +Reacted on compiler warning about previous revision + +2011.08.29.213339 [4302] +libburn/write.c +More generous ignoring of failure of fsync() on inappropriate fd + +2011.09.20.131602 [4306] +libburn/sg-linux.c +Simulation of large disks + +2011.09.20.131843 [4307] +libburn/sg-linux.c +Working around collision with udev by closing and re-opening device file + +2011.09.20.132438 [4308] +libburn/spc.c +Slowing down test cycle while waiting for drive to become ready + +2011.09.21.133344 [4310] +README +cdrskin/README +libburn/sg-linux.c +Improved and documented workaround for udev + +21 Sep 2011 [4311] +README +cdrskin/README +Corrected outdated sentence in documented workaround for udev + +26 Sep 2011 [4321] +svn copy -m Branching for libburn release 1.1.6 +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/1.1.6 + +2011.09.27.060001 [4322] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.1.6 + +27 Sep 2011 [4323] +- cdrskin/add_ts_changes_to_libburn_1_1_4 +- cdrskin/add_ts_changes_to_libburn_1_1_5 ++ cdrskin/add_ts_changes_to_libburn_1_1_6 ++ cdrskin/add_ts_changes_to_libburn_1_1_7 +Updated cdrskin tarball generator + +27 Sep 2011 [4324] +ChangeLog +cdrskin/changelog.txt +Updated change log + +----------------------------------- release - libburn-1.1.6 - 2011.09.27.060001 +* Bug fix: stdio sizes > 4 TB - 32 kB caused integer rollover +* Worked around a collision with Linux udev which lets links vanish + + +2011.09.27.124555 [4329] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.1.7 + +27 Sep 2011 [4330] +- cdrskin/add_ts_changes_to_libburn_1_1_4 +- cdrskin/add_ts_changes_to_libburn_1_1_5 ++ cdrskin/add_ts_changes_to_libburn_1_1_6 ++ cdrskin/add_ts_changes_to_libburn_1_1_7 +Updated cdrskin tarball generator + +27 Sep 2011 [4331] +ChangeLog +cdrskin/changelog.txt +Updated change log + +27 Sep 2011 [4332] +svn move -m libburn release 1.1.6 is ready +http://svn.libburnia-project.org/libburn/branches/1.1.6 +http://svn.libburnia-project.org/libburn/tags/1.1.6 + +------------------------------------ cycle - cdrskin-1.1.7 - 2011.09.27.124555 + + +2011.10.02.134037 [4336] +libburn/sg-linux.c +Avoided open()-close() cycles during drive scan and grab on Linux + +2011.10.02.172004 [4337] +libburn/sg-linux.c +Avoided one more open()-close() cycles during drive scan and grab on Linux + +2011.10.05.075531 [4339] +libburn/libburn.h +libburn/drive.h +libburn/drive.c +libburn/write.c +libburn/libburn.ver +New API call burn_drive_re_assess() + +2011.10.06.082649 [4344] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_stream_recordinG + +2011.10.06.082934 [4345] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_drive_equals_adR + +2011.10.06.083228 [4346] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_get_drive_rolE + +2011.10.06.084026 [4347] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_random_access_rW + +2011.10.06.085426 [4348] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_get_best_speeD + +2011.10.06.085713 [4349] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_set_waitinG + +2011.10.06.085848 [4350] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_track_set_sizE + +2011.10.06.090207 [4351] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_preset_device_familY + +2011.10.06.090422 [4352] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_set_forcE + +2011.10.06.090557 [4353] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_allow_untested_profileS + +2011.10.06.091334 [4554] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_write_mode_ruleS + +2011.10.06.091641 [4355] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_get_spacE + +2011.10.06.091949 [4356] [4357] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_set_filluP + +2011.10.06.092242 [4358] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_get_multi_capS + +2011.10.06.095055 [4359] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_toc_entry_extensionS + +2011.10.06.095322 [4360] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_get_msc1 + +2011.10.06.100025 [4361] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_burn_disc_formaT + +2011.10.06.101239 [4362] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_bd_formattinG + +2011.10.06.101442 [4363] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_wrote_welL + +2011.10.06.101918 [4364] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_set_start_bytE + +2011.10.06.102127 [4365] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_get_profilE + +2011.10.06.102557 [4366] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_atip_speed_is_oK + +2011.10.06.102816 [4367] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_buffer_min_filL + +2011.10.06.103102 [4368] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_multI + +2011.10.06.103325 [4369] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_pretend_fulL + +2011.10.06.103445 [4370] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_buffer_progresS + +2011.10.06.103619 [4371] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_read_atiP + +2011.10.06.103832 [4372] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_burn_disc_unsuitablE + +2011.10.06.104012 [4373] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_get_start_end_lbA + +2011.10.06.104233 [4374] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_audioxtR + +2011.10.06.104519 [4375] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_cleanup_handleR + +2011.10.06.104908 [4376] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_burn_aborT + +2011.10.06.105627 [4377] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_burn_msgS + +2011.10.06.105922 [4378] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_convert_scsi_adR + +2011.10.06.111115 [4379] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_convert_fs_adR + +2011.10.06.111245 [4380] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_is_enumerablE + +2011.10.06.111758 [4381] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_allow_libburn_taO + +2011.10.06.112031 [4382] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_grab_abort_does_worK + +2011.10.06.112239 [4383] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_is_erasable_on_load_does_worK + +2011.10.06.112456 [4384] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_progress_track_does_worK + +2011.10.06.112732 [4385] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_has_drive_get_adR + +2011.10.06.113103 [4386] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_does_ejecT + +2011.10.06.113315 [4387] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_all_tracks_with_sector_paD + +2011.10.06.114009 [4388] +cdrskin/cdrskin.c +Removed obsolete macro case + +2011.10.06.120014 [4389] +cdrskin/cdrskin.c +cdrskin/compile_cdrskin.sh +Removed outdated code and Cdrskin_oldfashioned_api_usE + +2011.10.06.121140 [4390] +cdrskin/cdrskin.c +Now unconditional: Cdrskin_libburn_from_pykix_svN + +2011.10.07.075117 [4391] +libburn/drive.h +libburn/drive.c +Avoided to release drive prematurely if interrupted while grabbing drive + +08 Oct 2011 [4392] +cdrskin/convert_man_to_html.sh +Replaced HTML minus by a flat ASCII minus in HTML man page generator + +2011.10.09.155415 [4394] +libburn/drive.c +Reacted on warning of cppcheck about burn_disc_get_cd_info() + +2011.10.09.162355 [4395] +libburn/drive.c +Reacted on nitpicking of cppcheck + +2011.10.10.125950 [4397] +libburn/libburn.h +Changed term "persistent address" to "device file address" + +2011.10.10.130029 [4398] +libburn/drive.c +Changed debug message which called "stdio:" addresses enumerable + +2011.10.11.141313 [4399] [4400] +cdrskin/cdrskin.c +Avoided release-grab cycle in cdrskin -msinfo + +2011.10.11.163842 [4401] +cdrskin/cdrskin.c +Avoided release-grab cycles between tasks of a cdrskin run + +2011.10.12.100050 [4402] +libburn/libburn.h +libburn/libdax_msgs.h +libburn/drive.c +libburn/write.c +libburn/structure.c +libburn/read.c +libburn/debug.c +libburn/mmc.c +libburn/spc.c +libburn/sg-solaris.c +libburn/sg-linux.c +libburn/sg-libcdio.c +libburn/sg-freebsd-port.c +libburn/sg-freebsd.c +Gave up use of burn_print() in libburn + +2011.10.12.100155 [4403] +test/structest.c +Added madatory library initialization to test/structest.c + +2011.10.27.091014 [4413] +libburn/sg-libcdio.c +Silenced a warnings in libcdio adapter + +2011.10.27.094705 [4414] +libburn/sg-libcdio.c +Enabled optional reporting of drive replies in libcdio adapter + +2011.10.27.094705 [4415] +libburn/sg-solaris.c +Enabled optional reporting of drive replies in Solaris adapter + +2011.10.31.164236 [4416] +libburn/spc.c +libburn/mmc.c +Enabled recognition of QEMU DVD-ROM 0.12 as ATAPI drive + +2011.11.01.221713 [4417] +libburn/spc.c +Small correction of rev 4416 + +2011.11.02.074334 [4418] +libburn/sg-linux.c +Avoiding on Linux to list drives from /proc if device family is non-default + +2011.11.02.130735 [4419] +libburn/mmc.c +Corrected a bug with obtaining Physical Interface Standard information + +2011.11.03.130710 [4422] +libburn/spc.c +Bug fix: Misinterpreted mode page 2A if block descriptors are present + +2011.11.09.111408 [4424] +libburn/sg-linux.c +Enabled recognition of CD drive at /dev/vda of Linux guest on qemu virtio + +2011.11.10.134432 [4430] +libburn/libburn.h +Removed remark about SCSI logging being restricted to Linux system adapter + +2011.11.10.153659 [4431] +libburn/spc.h +libburn/spc.c +libburn/sg-linux.c +Logging of early SCSI commands of Linux system adapter to stderr + +2011.11.14.170612 [4435] +libburn/sg-linux.c +Clarified a remark in libburn/sg-linux.c + +2011.11.16.094244 [4336] +libburn/libdax_msgs.h +Clarified a remark in libburn/libdax_msgs.h + +2011.11.16.094436 [4437] +libburn/write.c +Distinguished failure messages about write(2) and fsync(2) + +2011.11.18.155410 [4440] +libburn/sg-libcdio.c +Bug fix: licdio adapter reacted inconsistently on symbolic links to drives + +20 Nov 2011 [4441] +svn copy -m Branching for libburn release 1.1.8 +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/1.1.8 + +2011.11.20.100001 [4442] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.1.8 + +20 Nov 2011 [4443] +- cdrskin/add_ts_changes_to_libburn_1_1_6 +- cdrskin/add_ts_changes_to_libburn_1_1_7 ++ cdrskin/add_ts_changes_to_libburn_1_1_8 ++ cdrskin/add_ts_changes_to_libburn_1_1_9 +Updated cdrskin tarball generator + +20 Nov 2011 [4444] +ChangeLog +cdrskin/changelog.txt +Updated change log + +----------------------------------- release - libburn-1.1.8 - 2011.11.20.100001 +* New API call burn_drive_re_assess() +* Enabled recognition of QEMU DVD-ROM 0.12 as ATAPI drive +* Bug fix: Misinterpreted mode page 2A if block descriptors are present + + +2011.11.20.214751 [4448] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.1.9 + +20 Nov 2011 [4449] +- cdrskin/add_ts_changes_to_libburn_1_1_6 +- cdrskin/add_ts_changes_to_libburn_1_1_7 ++ cdrskin/add_ts_changes_to_libburn_1_1_8 ++ cdrskin/add_ts_changes_to_libburn_1_1_9 +Updated cdrskin tarball generator + +20 Nov 2011 [4450] +ChangeLog +cdrskin/changelog.txt +Updated change log + +21 Nov 2011 [4451] +svn move -m libburn release 1.1.8 is ready +http://svn.libburnia-project.org/libburn/branches/1.1.8 +http://svn.libburnia-project.org/libburn/tags/1.1.8 + +------------------------------------ cycle - cdrskin-1.1.9 - 2011.11.20.220318 + + +2011.11.22.191621 [4455] +libburn/sg-linux.c +Reacted on compiler warning of Debian buildd + +2011.11.23.104948 [4458] +libburn/libdax_msgs.h +libburn/sg-linux.c +Reporting about failure to open existing /dev/sr or /dev/scd on Linux + +2011.11.26.153142 [4460] +libburn/sg-solaris.c +Bug fix: Solaris adapter mishandled write commands which failed on first try + +2011.11.30.081130 [4462] +libburn/transport.h +libburn/libdax_msgs.h +libburn/spc.h +libburn/spc.c +libburn/sbc.c +libburn/mmc.c +libburn/sg-linux.c +libburn/sg-freebsd.c +libburn/sg-libcdio.c +libburn/sg-solaris.c +Made SCSI timeout settable at level of SPC, SBC, MMC functions + +2011.11.30.131608 [4463] +libburn/mmc.c +Improved debugging of drive feature list + +2011.12.02.100148 [4464] +libburn/drive.c +libburn/spc.c +Bug fix: Interrupting libburn while drive tray is loading led to endless loop + +2011.12.02.171436 [4466] +libburn/libburn.h +libburn/drive.c +libburn/mmc.h +libburn/mmc.c +libburn/libburn.ver +New API call burn_disc_get_leadin_text() + +2011.12.02.171710 [4467] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Storing CD-TEXT as cdtext.dat with cdrskin option -toc -vv, reporting with -vvv + +2011.12.02.204513 [4468] +libburn/libburn.h +Clarified meaning of Character Position byte of burn_disc_get_leadin_text() + +2011.12.03.113345 [4469] +libburn/mmc.c +Corrected a style deviation in code of mmc.c + +03 Dec 2011 [4470] +Makefile.am +Removing cdrskin/.libs and test/.libs witch make clean + +03 Dec 2011 [4471] +Makefile.am +Added ./bootstrap script to release tarball + +2011.12.05.203600 [4475] +libburn/libburn.h +libburn/libdax_msgs.h +libburn/options.h +libburn/options.c +libburn/mmc.c +libburn/write.c +libburn/libburn.ver +doc/cookbook.txt +New API call burn_write_opts_set_leadin_text() + +2011.12.05.203821 [4476] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Implemented cdrskin option textfile= + +2011.12.06.134651 [4477] +libburn/write.c +Reacted on compiler warning about uninitialized variable in rev 4476 + +06 Dec 2011 [4478] +doc/cookbook.txt +Documented CD-TEXT pack type 0x8f + +2011.12.07.075031 [4479] +Makefile.am +Removed version.h from list of source files because generated by configure + +08 Dec 2011 [4481] +doc/cookbook.txt +Updated documentation of CD-TEXT pack types 0x87 and 0x8f + +08 Dec 2011 [4482] +doc/cookbook.txt +Updated documentation of CD-TEXT pack types 0x86, 0x8d, 0x8e + +2011.12.09.092152 [4483] +cdrskin/cdrskin.c +Tolerating trailing zero of Sony CDTEXT files + +10 Dec 2011 [4484] +doc/cookbook.txt +Updated documentation of CD-TEXT pack types 0x88 and 0x89 + +10 Dec 2011 [4485] +doc/cookbook.txt +Some polishing in cookbook + +12 Dec 2011 [4486] +doc/cookbook.txt ++ doc/cdtext.txt +Outsourced cdtext.txt from cookbook.txt + +12 Dec 2011 [4487] +cdrskin/cdrskin.1 +Pointing from man cdrskin to doc/cdtext.txt for CD-TEXT details + +2011.12.12.092602 [4488] +libburn/libburn.h +libburn/libdax_msgs.h +libburn/init.c +libburn/options.h +libburn/options.c +libburn/structure.h +libburn/structure.c +libburn/write.h +libburn/write.c +libburn/libburn.ver +New API calls for composing CD-TEXT + +2011.12.12.164452 [4490] +libburn/libburn.h +libburn/write.c +doc/cdtext.txt +New macros for CD-TEXT genre and language names + +2011.12.12.181346 [4491] +libburn/write.c +Enabled repetition of track texts by TAB character + +2011.12.13.164329 [4492] +libburn/libburn.h +More macros for CD-TEXT genre and language names + +2011.12.14.124906 [4494] +libburn/write.c +Changed default CD-TEXT for tracks from track number text to empty text + +2011.12.14.132551 [4495] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +doc/cdtext.txt +doc/cookbook.txt +New option input_sheet_v07t= + +14 Dec 2011 [4496] +ChangeLog +cdrskin/changelog.txt +Updated change log + +------------------------------------ cycle - cdrskin-1.1.9 - 2011.12.14.132711 +* Bug fix: Solaris adapter mishandled write commands which failed on first try +* Bug fix: Interrupting libburn while drive tray is loading led to endless loop +* New API calls burn_disc_get_leadin_text(), burn_write_opts_set_leadin_text() +* New API calls for composing CD-TEXT +* Implemented cdrskin option textfile= +* Implemented cdrskin option combination -vv -toc for cdtext.dat production +* New cdrskin option input_sheet_v07t= + + +14 Dec 2011 [4497] +cdrskin/cdrskin.1 +cdrskin/cdrskin_eng.html +Improved cdrskin man page and web page + +15 Dec 2011 [4498] +Makefile.am +Included doc/cdtext.txt in libburn tarball + +2011.12.15.104259 [4499] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Refusing to burn CD-TEXT if non-audio tracks are present + +2011.12.15.154818 [4502] +Makefile.am +libburn/libburn.h +libburn/libdax_msgs.h ++ libburn/cdtext.c +libburn/libburn.ver +doc/cdtext.txt +New API call burn_session_input_sheet_v07t() + +2011.12.15.155642 [4503] +cdrskin/cdrskin.c +cdrskin/compile_cdrskin.sh +Moved reading of Sony Input Sheet v07 from cdrskin to libburn + +2011.12.15.161349 [4504] +libburn/libburn.h +Documented new API call + +2011.12.15.174914 [4505] +libburn/cdtext.c +libburn/write.c +Moved burn_cdtext_from_session() from write.c to cdtext.c + +2011.12.16.105329 [4506] +libburn/util.c +libburn/util.h +libburn/cdtext.c +Moved a function from cdtext.c to util.c + +2011.12.16.111300 [4507] +libburn/util.c +libburn/util.h +libburn/cdtext.c +Moved a function from cdtext.c to util.c + +2011.12.18.161107 [4508] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New options --cdtext_dummy and --cdtext_verbose + +2011.12.22.112138 [4509] +cdrskin/cdrskin.c +Changed libburn print threshold of cdrskin -v to severity NOTE + + +2011.12.25.103852 [4510] +libburn/libburn.h +libburn/libdax_msgs.h +libburn/structure.c +libburn/file.c +libburn/source.c +libburn/cdtext.c +libburn/libburn.ver +New API calls burn_cdtext_from_packfile() and burn_session_by_cue_file() + +2011.12.25.105032 [4511] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Partly implemented options cuefile= and -text + +2011.12.25.110440 [4512] +libburn/structure.c +libburn/cdtext.c +Reacted on compiler warnings about uninitialized variables + +26 Dec 2011 [4514] +doc/cdtext.txt +Updated doc/cdtext.txt + +2011.12.27.115645 [4515] +libburn/libburn.h +libburn/structure.h +libburn/structure.c +libburn/libburn.ver +New API call burn_track_set_isrc_string() + +2011.12.27.133733 [4516] +libburn/write.c +libburn/cdtext.c +doc/cookbook.txt +doc/cdtext.txt +cdrskin/cdrskin.1 +Writing CATALOG and ISRC into Q sub-channel of CD SAO sessions + +2011.12.28.104044 [4517] +libburn/libburn.h +libburn/transport.h +libburn/drive.c +libburn/spc.h +libburn/spc.c +libburn/mmc.h +libburn/mmc.c +doc/cookbook.txt +doc/cdtext.txt +Transmitting CATALOG by mode page 5. ISRC too, if TAO. + +2011.12.28.104536 [4518] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Implemented options mcn= and isrc= + +2011.12.28.152153 [4519] +libburn/transport.h +libburn/write.c +libburn/mmc.h +libburn/mmc.c +Improved reaction on failure of SEND CUE SHEET + +2011.12.30.142742 [4521] +libburn/spc.h +libburn/spc.c +libburn/mmc.c +Reduced waiting time between retries of WRITE commands + +2011.12.30.164755 [4522] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Coordinated option cuefile= with option input_sheet_v07t= + +2011.12.30.174416 [4523] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Corrected precedence of media catalog and ISRC options + +2011.12.31.120007 [4524] +libburn/libburn.h +libburn/write.c +doc/cookbook.txt +New track mode modifier BURN_SCMS + +2012.01.01.124121 [4525] +libburn/write.c +Corrected CTL of lead-in CUE SHEET entry + +2012.01.01.124330 [4526] +libburn/mmc.c +doc/cookbook.txt +Writing SCMS into mode page 5 if TAO + +2012.01.01.124424 [4627] +libburn/drive.c +Fixed a wrong assumption about track.mode + +2012.01.01.124645 [4628] +libburn/libburn.h +libburn/structure.c +doc/cdtext.txt +Interpreting CDRWIN command FLAGS + +2012.01.01.125539 [4529] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Implemented options -scms -copy -nocopy -preemp -nopreemp + +2012.01.01.125539 [4530] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New cdrskin options --four_channel --two_channel + +2012.01.01.173446 [4531] +libburn/structure.c +doc/cdtext.txt +cdrskin/cdrskin.1 +Implemented cue sheet file commands ARRANGER, COMPOSER, MESSAGE + +2012.01.02.143655 [4532] +libburn/libdax_msgs.h +libburn/structure.c +Reporting line number in case of cue sheet file problems + +2012.01.02.164110 [4533] +libburn/write.c +Changed debug messages about CUE SHEET MSF from hex to decimal + +2012.01.03.194322 [4534] +libburn/libburn.h +libburn/libdax_msgs.h +libburn/structure.h +libburn/structure.c +libburn/write.c +doc/cdtext.txt +cdrskin/cdrskin.1 +libburn/libburn.ver +New API calls burn_track_set_index(), burn_track_clear_indice() + +2012.01.04.131808 [4535] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Implemented option index= + +2012.01.05.115516 [4536] +libburn/drive.c +libburn/write.c +libburn/spc.h +libburn/spc.c +Bug fix: Progress report with blanking and formatting could be bogus + +2012.01.05.120246 [4537] +cdrskin/cdrskin.c +Reacted on compiler warnings about rev 4535 + +2012.01.05.123114 [4538] +ChangeLog +cdrskin/changelog.txt +cdrskin/cdrskin_eng.html +Updated change log and web page + +2012.01.05.140927 [4539] +libburn/write.c +Reacted on compiler warning about rev 4519 + +------------------------------------ cycle - cdrskin-1.1.9 - 2012.01.05.141340 +* New API call burn_session_input_sheet_v07t() +* New API calls burn_cdtext_from_packfile() and burn_session_by_cue_file() +* New API call burn_track_set_isrc_string() +* New API calls burn_track_set_index(), burn_track_clear_indice() +* New cdrskin options --cdtext_dummy and --cdtext_verbose +* New cdrskin options --four_channel --two_channel +* Implemented cdrskin options mcn= and isrc= +* Implemented cdrskin options -scms -copy -nocopy -preemp -nopreemp +* Implemented cdrskin option index= +* Partly implemented cdrskin options cuefile= and -text +* Bug fix: Progress report with blanking and formatting could be bogus + + +2012.01.06.125849 [4540] +libburn/libburn.h +libburn/structure.c +cdrskin/cdrskin.1 +doc/cdtext.txt +Implemented data extraction from cue sheet FILE type WAVE + +2012.01.07.190901 [4541] +libburn/libburn.h +libburn/libdax_msgs.h +libburn/drive.c +libburn/structure.c +libburn/write.c +libburn/spc.c +libburn/mmc.c +libburn/libburn.ver +New API call burn_session_set_start_tno() + +2012.01.08.132304 [4542] +libburn/libburn.h +libburn/structure.c +libburn/cdtext.c +doc/cdtext.txt +Implemented track number starts > 1 with .cue and v07t.txt files + +2012.01.08.140810 [4543] +libburn/libburn.h +libburn/libburn.ver +libburn/structure.c +New API call burn_session_get_start_tno() + +2012.01.08.141104 [4544] +cdrskin/cdrskin.c +While writing: Reporting track numbers with correct start number offset + +2012.01.08.154822 [4545] +cdrskin/cdrskin.c +Corrected display of track number offset with -toc and -minfo + +2012.01.08.191443 [4546] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New cdrskin option cd_start_tno= + +2012.01.10.122000 [4547] +libburn/libburn.h +libburn/libburn.ver +libburn/structure.h +libburn/structure.c +libburn/write.c +New API call burn_track_set_pregap_size() + +2012.01.10.124916 [4548] +libburn/write.c +Removed a development test contraption introduced by previous revision + +2012.01.11.111602 [4549] +libburn/write.c +Enabled pre-gap size for tracks other than the first one + +2012.01.11.122013 [4550] +libburn/write.c +Reacted on compiler warning + +2012.01.11.122158 [4551] +libburn/structure.c +doc/cdtext.txt +cdrskin/cdrskin.1 +Interpreting .cue file command PREGAP + +2012.01.11.122442 [4552] +libburn/structure.h +libburn/structure.c +libburn/mmc.h +libburn/mmc.c +Obtaining more accurate track sizes for CD with pre-gap + +2012.01.11.132847 [4553] +cdrskin/cdrskin.c +Corrected track number of blank track reported by cdrskin -minfo + +2012.01.11.132946 [4554] +libburn/libburn.h +Clarified meaning of parameter trackno with call burn_disc_track_lba_nwa() + +2012.01.12.084429 [4555] +libburn/libburn.h +libburn/libburn.ver +libburn/libdax_msgs.h +libburn/structure.h +libburn/structure.c +libburn/write.c +New API call burn_track_set_postgap_size() + +2012.01.12.120316 [4556] +libburn/libburn.h +libburn/structure.c +doc/cdtext.txt +cdrskin/cdrskin.1 +Interpreting .cue file command POSTGAP + +2012.01.12.191143 [4557] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New cdrskin options sao_pregap=, sao_postgap= + +2012.01.13.101844 [4558] +cdrskin/cdrskin.c +Avoided display of negative speed numbers at track change + +2012.01.13.122205 [4559] +libburn/write.c +libburn/sector.h +libburn/sector.c +Flushing buffer to MMC before a new track begins + +2012.01.13.141517 [4560] +cdrskin/cdrskin.c +Corrected type and sector size for .cue generated tracks + +2012.01.13.142723 [4561] +libburn/write.c +Corrected write count for tracks with post-gap + +2012.01.13.150949 [4562] +libburn/write.c +Removed development macro which sneaked in with previous revision + +2012.01.13.151126 [4563] +cdrskin/cdrskin.c +Corrected total size summary of cdrskin before burn start + +2012.01.13.170914 [4564] +libburn/write.c +libburn/sector.c +Implemented index reporting for struct burn_progress + +15 Jan 2012 [4568] +doc/cookbook.txt +Updated SAO cookbook about indice, pre-gap, post-gap + +2012.01.16.112048 [4570] +libburn/sector.c +Corrected a comment + +2012.01.16.112258 [4571] +libburn/libburn.h +libburn/write.c +Preventing CD-TEXT with sessions that are not pure audio + +2012.01.18.120429 [4573] +libburn/structure.c +Detecting address sequence errors with .cue command INDEX + +2012.01.18.141954 [4574] +libburn/spc.c +Made conversion of ASC ASCQ to text independent of key and its text + +2012.01.19.210418 [4575,4577] delayed_commit/B20119.200204 +libburn/libburn.h +libburn/structure.h +libburn/structure.c +libburn/write.c +Counting post-gap as part of track with burn_track_get_sectors() + +2012.01.20.091016 [4576,4578] delayed_commit/B20120.091044 +libburn/libburn.h +libburn/file.h +libburn/file.c +libburn/structure.c +Introduced burn_offst_source_new() flag bit0 which prevents later size changes + +2012.01.21.174103 [4579] delayed_commit/B20121.174108 +libburn/read.c +Allowing SCSI read operations > 32 kB + +2012.01.22.124929 [4580] delayed_commit/B20122.124935 +libburn/spc.c +Revoked an aspect of rev 4465 which aborted operation before drive is ready + +2012.01.22.125048 [4581] delayed_commit/B20122.125048 +libburn/async.c +Prevented SIGSEGV with testing drive for being prepared for writing + +2012.01.22.125357 [4582] delayed_commit/B20122.125357 +libburn/drive.c +Improved reaction time on interrupt during writing of lead-in + +2012.01.22.182854 [4583] delayed_commit/B20122.182858 +libburn/libburn.h +libburn/init.h +libburn/init.c +libburn/drive.c +Disabled dangerous abort handler actions while BURN_DRIVE_GRABBING + +2012.01.22.194131 [4584] delayed_commit/B20122.194131 +cdrskin/cdrskin.c +Bug fix: cdrskin produced a memory fault if interupted before writing began + +2012.01.23.102720 [4585] +libburn/libdax_msgs.h +libburn/mmc.c +libburn/options.c +libburn/sg-freebsd-port.c +libburn/crc.c +libburn/write.c +Corrected some minor differences between SVN and local development tree + +23 Jan 2012 [4590] +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +23 Jan 2012 [4591] +ChangeLog +Updated change log + +23 Jan 2012 [4592] +cdrskin/wiki_plain.txt +Updated cdrskin wiki text + +2012.01.23.164346 [4593] +cdrskin/cdrskin.c +Fixed an endless loop with cdrskin signal handling + +------------------------------------ cycle - cdrskin-1.1.9 - 2012.01.23.164529 +* Bug fix: cdrskin produced a memory fault if interupted before writing began +* New API calls burn_session_set_start_tno(), burn_session_get_start_tno() +* New API calls burn_track_set_pregap_size(), burn_track_set_postgap_size() +* New cdrskin option cd_start_tno= +* New cdrskin options sao_pregap=, sao_postgap= + + +2012.01.25.092650 [4595] +cdrskin/cdrskin.c +Removed overdone part of rev 4593 + +27 Jan 2012 [4598] +svn copy -m Branching for libburn release 1.2.0 +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/1.2.0 + +2012.01.27.103001 [4599] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.2.0 + +27 Jan 2012 [4600] +- cdrskin/add_ts_changes_to_libburn_1_1_8 +- cdrskin/add_ts_changes_to_libburn_1_1_9 ++ cdrskin/add_ts_changes_to_libburn_1_2_0 ++ cdrskin/add_ts_changes_to_libburn_1_2_1 +Updated cdrskin tarball generator + +27 Jan 2012 [4601] +ChangeLog +cdrskin/changelog.txt +Updated change log + +----------------------------------- release - libburn-1.2.0 - 2012.01.27.103001 +* Bug fix: cdrskin produced a memory fault if interupted before writing began +* Bug fix: Solaris adapter mishandled write commands which failed on first try +* Bug fix: Interrupting libburn while drive tray is loading led to endless loop +* Bug fix: Progress report with blanking and formatting could be bogus +* New API calls burn_disc_get_leadin_text(), burn_write_opts_set_leadin_text() +* New API calls for composing CD-TEXT, see doc/cdtext.txt +* New API call burn_session_by_cue_file() for reading CDRWIN .cue files +* New API call burn_track_set_isrc_string() +* New API calls burn_track_set_index(), burn_track_clear_indice() +* New API calls burn_session_set_start_tno(), burn_session_get_start_tno() +* New API calls burn_track_set_pregap_size(), burn_track_set_postgap_size() +* Implemented cdrskin option textfile= +* Implemented cdrskin option combination -vv -toc for cdtext.dat production +* Implemented cdrskin options mcn= and isrc= +* Implemented cdrskin options -scms -copy -nocopy -preemp -nopreemp +* Implemented cdrskin option index= +* Partly implemented cdrskin options cuefile= and -text +* New cdrskin option input_sheet_v07t= for CD-TEXT definition +* New cdrskin options --cdtext_dummy and --cdtext_verbose +* New cdrskin options --four_channel --two_channel +* New cdrskin option cd_start_tno= +* New cdrskin options sao_pregap=, sao_postgap= + + +2012.01.27.150951 [4605] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.2.1 + +27 Jan 2012 [4606] +- cdrskin/add_ts_changes_to_libburn_1_1_8 +- cdrskin/add_ts_changes_to_libburn_1_1_9 ++ cdrskin/add_ts_changes_to_libburn_1_2_0 ++ cdrskin/add_ts_changes_to_libburn_1_2_1 +Updated cdrskin tarball generator + +27 Jan 2012 [4607] +ChangeLog +cdrskin/changelog.txt +Updated change log + +27 Jan 2012 [4608] +svn move -m libburn release 1.2.0 is ready +http://svn.libburnia-project.org/libburn/branches/1.2.0 +http://svn.libburnia-project.org/libburn/tags/1.2.0 + +27 Jan 2012 [tag:4612] [4613] +libburn/write.c +ChangeLog +Corrected small flaws of libburn release tarball + +27 Jan 2012 [4614] [4615] +libburn/write.c +One of the flaws was phony. Corrected back. + +------------------------------------ cycle - cdrskin-1.2.1 - 2012.01.27.150951 + + +2012.02.02.190720 [4624] +libburn/write.c +libburn/spc.c +Reacted on warnings of Debian buildd + +06 Feb 2012 [4625] +doc/cdtext.txt +Small corrections in CD-TEXT documentation + +08 Feb 2012 [4626] +doc/cdtext.txt +Small corrections in CD-TEXT documentation + +2012.02.11.171228 [4627] +libburn/crc.c +Mathematical description of crc_ccitt() versus crc_11021() + +2012.02.13.102837 [4628] +libburn/spc.c +Added LG drive sense code 2 06 00 to error list (officially listed is 3 06 00) + +2012.02.19.101022 [4631] +libburn/crc.h +libburn/crc.c +Re-implemented the CRC functions under own copyright + +2012.02.22.103056 [4632] +libburn/util.c +doc/mediainfo.txt +Added to DVD manufacturer list: "UmeDisc Limited" + +2012.03.21.193320 [4671] +cdrskin/cdrskin.c +Reacted on warning of cppcheck + +2 Apr 2012 [4684] +svn copy -m Branching for libburn release 1.2.2 +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/1.2.2 + +2012.04.02.110001 [4685] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.2.2 + +02 Apr 2012 [4686] +- cdrskin/add_ts_changes_to_libburn_1_2_0 +- cdrskin/add_ts_changes_to_libburn_1_2_1 ++ cdrskin/add_ts_changes_to_libburn_1_2_2 ++ cdrskin/add_ts_changes_to_libburn_1_2_3 +Updated cdrskin tarball generator + +02 Apr 2012 [4687] +ChangeLog +cdrskin/changelog.txt +Updated change log + +----------------------------------- release - libburn-1.2.2 - 2012.04.02.110001 +* Small internal refinements + + +2012.04.02.172347 [4691] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.2.3 + +02 Apr 2012 [4692] +- cdrskin/add_ts_changes_to_libburn_1_2_0 +- cdrskin/add_ts_changes_to_libburn_1_2_1 ++ cdrskin/add_ts_changes_to_libburn_1_2_2 ++ cdrskin/add_ts_changes_to_libburn_1_2_3 +Updated cdrskin tarball generator + +02 Apr 2012 [4693] +ChangeLog +cdrskin/changelog.txt +Updated change log + +02 Apr 2012 [4694] +svn move -m libburn release 1.2.2 is ready +http://svn.libburnia-project.org/libburn/branches/1.2.2 +http://svn.libburnia-project.org/libburn/tags/1.2.2 + +------------------------------------ cycle - cdrskin-1.2.3 - 2012.04.02.172347 + + +2012.04.04.100754 [4698] +cdrskin/cdrskin.c +Bug fix: cdrskin SIGSEGV if track source was added when no drive was available + +2012.04.04.183902 [4699] +libburn/crc.c +Reacted on warning of Debian buildd + +2012.04.08.112703 [4701] +libburn/libburn.h +libburn/options.h +libburn/options.c +libburn/write.c +libburn/libdax_msgs.h +libburn/libburn.ver +New API call burn_write_opts_set_obs_pad() + +2012.04.08.112825 [4702] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New option --obs_pad + +2012.04.10.181239 [4704] +cdrskin/cdrskin.c +Now recognizing long options with double dash + +13 Apr 2012 [4706] +cdrskin/cdrskin.1 +Small correction in cdrskin man page + +2012.04.13.202654 [4707] +configure.ac +libburn/libburn.h +libburn/options.c +cdrskin/cdrskin.1 +Compile time option for obs_pad + +18 Apr 2012 [4708] +doc/cdtext.txt +Augmented CD-TEXT documentation by a complete example of packs + +18 Apr 2012 [4709] +doc/cookbook.txt +Small change in cookbook.txt about DVD-R DL + +2012.05.08.080449 [4731] +libburn/crc.c +Corrections of CRC-32 algorithm for 32 bit systems. Mentioning of start value. + +2012.05.30.202249 [4744] +libburn/write.c +libburn/mmc.c +doc/cookbook.txt +Bug fix: CD SAO sessions with data tracks started by an audio pause + +2012.06.17.173420 [4762] +libburn/sector.c +Improved reported number of missing bytes in case of track source shortage + +2012.07.08.103007 [4778] +libburn/structure.c +Bug fix: CD tracks were perceived 2 sectors too short. Nice with TAO, bad with SAO. + +20 Jul 2012 [4794] +svn copy -m "Branching for libburn release 1.2.4" +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/1.2.4 + +2012.07.20.113001 [4795] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.2.4 + +20 Jul 2012 [4796] +- cdrskin/add_ts_changes_to_libburn_1_2_2 +- cdrskin/add_ts_changes_to_libburn_1_2_3 ++ cdrskin/add_ts_changes_to_libburn_1_2_4 ++ cdrskin/add_ts_changes_to_libburn_1_2_5 +Updated cdrskin tarball generator + +20 Jul 2012 [] +ChangeLog +cdrskin/changelog.txt +Updated change log + +----------------------------------- release - libburn-1.2.4 - 2012.07.20.113001 +* New API call burn_write_opts_set_obs_pad(), ./configure --enable-dvd-obs-pad +* New cdrskin option --obs_pad +* Bug fix: CD SAO sessions with data tracks started by an audio pause +* Bug fix: CD tracks were perceived 2 sectors too short. Nice with TAO, bad with SAO. +* Bug fix: cdrskin SIGSEGV if track source was added when no drive was available + + +2012.07.20.164346 [4801] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.2.5 + +20 Jul 2012 [4802] +- cdrskin/add_ts_changes_to_libburn_1_2_2 +- cdrskin/add_ts_changes_to_libburn_1_2_3 ++ cdrskin/add_ts_changes_to_libburn_1_2_4 ++ cdrskin/add_ts_changes_to_libburn_1_2_5 +Updated cdrskin tarball generator + +20 Jul 2012 [4803] +ChangeLog +cdrskin/changelog.txt +Updated change log + +20 Jul 2012 [4806] +svn move -m libburn release 1.2.4 is ready +http://svn.libburnia-project.org/libburn/branches/1.2.4 +http://svn.libburnia-project.org/libburn/tags/1.2.4 + +------------------------------------ cycle - cdrskin-1.2.5 - 2012.07.20.164346 + + +2012.07.26.122909 [4811] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New option --no_load + +26 Jul 2012 [4813] +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +------------------------------------ cycle - cdrskin-1.2.5 - 2012.07.26.195346 +* New option --no_load + + +1 Aug 2012 [4814] +doc/cdtext.txt +Small grammatical correction in CD-TEXT documentaion + +2012.08.28.161949 [4820] +libburn/drive.c +libburn/util.h +libburn/util.c +libburn/sg-freebsd.c +Removed buggy burn_strdup() and burn_strndup(). Thanks to Rich Felker. + +2012.09.13.085623 [4828] +libburn/mmc.c +Bug fix: Speed setting had no effect on BD media. Thanks to Dennis Vshivkov. + +13 Sep 2012 [4829] +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +------------------------------------ cycle - cdrskin-1.2.5 - 2012.09.13.090310 +Bug fix: Speed setting had no effect on BD media. Thanks to Dennis Vshivkov. + + +2012.10.02.134821 [4841] +configure.ac +Configuration for use of libcdio on cygwin. Thanks Rocky Bernstein. + +2012.10.24.095725 [4850] +libburn/async.c +Reporting (still cryptic) details about refusal to blank + +2012.10.25.123837 [4852] +libburn/libburn.h +libburn/transport.h +libburn/read.c +libburn/spc.c +libburn/mmc.c +New flag bit4 of burn_read_data() for better handling of TAO end blocks + +2012.10.25.173408 [4854] +libburn/read.c +Corrected error handling which was spoiled by rev 4852 + +2012.11.18.184006 [4858] +libburn/libburn.h +libburn/read.c +libburn/spc.c +libburn/mmc.h +libburn/mmc.c +Preserving an immature sketch of media quality scanning + +2012.11.18.184114 [4859] +cdrskin/cdrskin.c +Preserving an immature sketch of media quality scanning + +2012.11.24.181347 [4860] +libburn/mmc.c +Better reaction on non-plausible ATIP info from CD-ROM + +2012.11.29.111344 [4865] +libburn/libburn.h +libburn/transport.h +libburn/libdax_msgs.h +libburn/options.h +libburn/options.c +libburn/toc.c +libburn/read.c +libburn/spc.c +libburn/mmc.h +libburn/sg-freebsd.c +libburn/libburn.ver +Beginning to create new API call burn_read_audio + +2012.11.29.112506 [4866] +libburn/mmc.c +Forgot mcc.c with the previous commit + +2012.11.29.112605 [4867] +libburn/libdax_msgs.h +libburn/mmc.c +Let mmc_format_unit issue failure message on SCSI error + +2012.11.30.193330 [4868] +libburn/libburn.h +libburn/read.c +New API call burn_read_audio + +2012.11.30.193415 [4869] +test/telltoc.c +Made telltoc ready for reading CD audio + +2012.12.14.145101 [4878] +libburn/libburn.h +libburn/init.c +libburn/libdax_msgs.h +libburn/libdax_msgs.c +libburn/libburn.ver +New API call burn_list_sev_texts() + +14 Dec 2012 [4879] +doc/cookbook.txt +Small change to burn cookbook about ISO multi-session emulation + +08 Jan 2013 [4935] +svn copy -m "Branching for libburn release 1.2.6" +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/1.2.6 + +2013.01.08.090001 [4936] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.2.6 + +08 Jan 2013 [4937] +- cdrskin/add_ts_changes_to_libburn_1_2_4 +- cdrskin/add_ts_changes_to_libburn_1_2_5 ++ cdrskin/add_ts_changes_to_libburn_1_2_6 ++ cdrskin/add_ts_changes_to_libburn_1_2_7 +Updated cdrskin tarball generator + +08 Jan 2013 [4938] +ChangeLog +cdrskin/changelog.txt +Updated change log + +----------------------------------- release - libburn-1.2.6 - 2013.01.08.090001 +Bug fix: Speed setting had no effect on BD media. Thanks to Dennis Vshivkov. +* New API call burn_read_audio() +* New API call burn_list_sev_texts() +* New cdrskin option --no_load + + +2013.01.08.144634 [] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.2.7 + +08 Jan 2013 [4947] +- cdrskin/add_ts_changes_to_libburn_1_2_4 +- cdrskin/add_ts_changes_to_libburn_1_2_5 ++ cdrskin/add_ts_changes_to_libburn_1_2_6 ++ cdrskin/add_ts_changes_to_libburn_1_2_7 +Updated cdrskin tarball generator + +08 Jan 2013 [4948] +ChangeLog +cdrskin/changelog.txt +Updated change log + +08 Jan 2013 [4949] +svn move -m libburn release 1.2.6 is ready +http://svn.libburnia-project.org/libburn/branches/1.2.6 +http://svn.libburnia-project.org/libburn/tags/1.2.6 + +------------------------------------ cycle - cdrskin-1.2.7 - 2013.01.08.150303 + + +2013.01.12.195030 [4955] +libburn/libburn.h +libburn/transport.h +libburn/structure.h +libburn/structure.c +libburn/mmc.c +libburn/libburn.ver +New API call burn_disc_get_incomplete_sessions(), new burn_toc_entry.track_status_bits + +2013.01.12.195311 [4956] +cdrskin/cdrskin.c +Made use of new API features to handle incomplete sessions + +2013.01.15.104005 [4961] +libburn/structure.c +Bug fix: All CD tracks were reported with the sizes of the tracks in the first session. Regression introduced with version 1.2.0 (rev 4552). + +16 Jan 2013 [4965] +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +2013.01.16.181124 [4966] +cdrskin/cdrskin.c +Updated cdrskin copyright message + +------------------------------------ cycle - cdrskin-1.2.7 - 2013.01.16.181124 +* New API call burn_disc_get_incomplete_sessions() +* New burn_toc_entry component .track_status_bits +* -toc and -minfo now report about tracks in the incomplete session +* Bug fix: All CD tracks were reported with the sizes of the tracks in the + first session. Regression introduced with version 1.2.0 (rev 4552). + + +2013.02.26.080127 [4972] +libburn/drive.c +Corrected wrong use of sizeof + +2013.03.04.211258 [4975] +libburn/mmc.c +Bug fix: On some drives the request for minimum speed yielded maximum speed + +2013.03.04.232436 [4976] +libburn/mmc.c +Corrected previous bug fix which caused speed descriptors to appear twice + +2013.03.05.124217 [4977] +libburn/mmc.c +Still correcting the bug fix of rev 4975 + +2013.03.05.124508 [4978] +cdrskin/cdrskin.c +New cdrskin option --list_speeds + +05 Mar 2013 [4979] +cdrskin/cdrskin.1 +Mentioned --list_speeds in manual page + +05 Mar 2013 [4980] +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +2013.03.05.185356 [4981] +libburn/mmc.c +Always considering mode page 2A when looking for min and max speed + +2013.03.05.185440 [4982] +cdrskin/cdrskin.c +Always considering mode page 2A when looking for min and max speed + +------------------------------------ cycle - cdrskin-1.2.7 - 2013.03.05.185655 +* Bug fix: On some drives the request for minimum speed yielded maximum speed +* New cdrskin option --list_speeds + + +2013.03.12.114739 [4987] +libburn/write.c +Avoiding SYNCHRONIZE CACHE if DVD track preparation has failed + +18 Mar 2013 [4994] +svn copy -m "Branching for libburn release 1.2.8" +http://svn.libburnia-project.org/libburn/trunk +http://svn.libburnia-project.org/libburn/branches/1.2.8 + +2013.03.18.080001 [4997] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.2.8 + +18 Mar 2013 [4998] +- cdrskin/add_ts_changes_to_libburn_1_2_6 +- cdrskin/add_ts_changes_to_libburn_1_2_7 ++ cdrskin/add_ts_changes_to_libburn_1_2_8 ++ cdrskin/add_ts_changes_to_libburn_1_2_9 +Updated cdrskin tarball generator + +18 Mar 2013 [4999] +ChangeLog +cdrskin/changelog.txt +Updated change log + +----------------------------------- release - libburn-1.2.8 - 2013.03.18.080001 +* -toc and -minfo now report about tracks in the incomplete session +* New API call burn_disc_get_incomplete_sessions() +* New burn_toc_entry component .track_status_bits +* Bug fix: All CD tracks were reported with the sizes of the tracks in the + first session. Regression introduced with version 1.2.0 (rev 4552). +* Bug fix: On some drives the request for minimum speed yielded maximum speed +* New cdrskin option --list_speeds + + +2013.03.18.210519 [5007] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.2.9 + +18 Mar 2013 [4996] +- cdrskin/add_ts_changes_to_libburn_1_2_6 +- cdrskin/add_ts_changes_to_libburn_1_2_7 ++ cdrskin/add_ts_changes_to_libburn_1_2_8 ++ cdrskin/add_ts_changes_to_libburn_1_2_9 +Updated cdrskin tarball generator + +18 Mar 2013 [5008] +ChangeLog +cdrskin/changelog.txt +Updated change log + +18 Mar 2013 [5009] +svn move -m libburn release 1.2.8 is ready +http://svn.libburnia-project.org/libburn/branches/1.2.8 +http://svn.libburnia-project.org/libburn/tags/1.2.8 + +------------------------------------ cycle - cdrskin-1.2.9 - 2013.03.18.211611 + + +2013.04.01.121637 [5013] +cdrskin/cdrskin.c +Forgot to increment cdrskin version number + +2013.04.01.121851 [5014] +libburn/mmc.c +libburn/libdax_msgs.h +Bug fix: Formatting of BD-RE used certification regardless of drive capabilities + +01 Apr 2013 [5015] +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +------------------------------------ cycle - cdrskin-1.2.9 - 2013.04.01.125750 +Bug fix: Formatting of BD-RE used certification regardless of drive capabilities + + +2013.05.10.064018 [5034] +cdrskin/cdrskin.c +Bug fix: DVD+R with damaged TOC were reported by -minfo with wrong end address + +2013.05.10.072542 [5035] +libburn/libdax_msgs.h +Added new item to list of error codes + +10 May 2013 [5036] +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +------------------------------------ cycle - cdrskin-1.2.9 - 2013.05.10.073212 +Bug fix: DVD+R with damaged TOC were reported by -minfo with wrong end address + + +17 May 2013 [5044] +svn copy -m Branching for libburn release 1.3.0 + +2013.05.17.090001 [5045] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.3.0 + +17 May 2013 [5046] +- cdrskin/add_ts_changes_to_libburn_1_2_8 +- cdrskin/add_ts_changes_to_libburn_1_2_9 ++ cdrskin/add_ts_changes_to_libburn_1_3_0 ++ cdrskin/add_ts_changes_to_libburn_1_3_1 +Updated cdrskin tarball generator + +17 May 2013 [5047] +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +----------------------------------- release - libburn-1.3.0 - 2013.05.17.090001 +* Bug fix: Full formatting of BD-RE used certification regardless of drive + capabilities +* Bug fix: DVD+R with damaged TOC were reported by -minfo with wrong end + address + + +17 May 2013 [5051] +- cdrskin/add_ts_changes_to_libburn_1_2_8 +- cdrskin/add_ts_changes_to_libburn_1_2_9 ++ cdrskin/add_ts_changes_to_libburn_1_3_0 ++ cdrskin/add_ts_changes_to_libburn_1_3_1 +Updated cdrskin tarball generator + +2013.05.17.180032 [5052] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.3.1 + +17 May 2013 [5053] +ChangeLog +cdrskin/changelog.txt +Updated change log + +17 May 2013 [5054] +svn move -m libburn release 1.3.0 is ready + +------------------------------------ cycle - cdrskin-1.3.1 - 2013.05.17.181442 + + +2013.05.19.114643 [5059] +libburn/libburn.h +libburn/cdtext.c +doc/cdtext.txt +libburn/libburn.ver +New API call burn_make_input_sheet_v07t() + +2013.05.19.114854 [5060] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New option textfile_to_v07t= + +2013.05.19.154838 [5061] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New options cdtext_to_textfile= and cdtext_to_v07t= + +2013.05.20.104814 [5062] +libburn/libburn.h +libburn/cdtext.c +libburn/libdax_msgs.h +API call burn_session_input_sheet_v07t(): read multiple blocks from same file + +2013.05.20.110128 [5063] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +Allowed option input_sheet_v07t= to read multiple blocks from same file + +2013.05.20.124448 [5064] +libburn/cdtext.c +Bug fixes with new API call burn_make_input_sheet_v07t() + +2013.05.20.124520 [5065] +cdrskin/cdrskin.c +Closed memory leak introduced by rev 5063 + +20 May 2013 [5066] +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +------------------------------------ cycle - cdrskin-1.3.1 - 2013.05.20.141737 +* New API call burn_make_input_sheet_v07t() +* New option textfile_to_v07t= +* New options cdtext_to_textfile= and cdtext_to_v07t= +* API call burn_session_input_sheet_v07t(): read multiple blocks from same file + + +2013.05.21.081819 [5067] +cdrskin/cdrskin.c +Defaulting -sao -multi to -tao -multi if -sao -multi is not possible + +2013.05.23.154249 [5068] +libburn/libburn.h +libburn/file.c +libburn/util.h +libburn/util.c +libburn/libdax_msgs.h +libburn/libdax_msgs.c +libburn/libburn.ver +New API calls burn_drive_extract_audio(), burn_drive_extract_audio_track() + +2013.05.23.154249 [5069] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New cdrskin options extract_audio_to= , extract_tracks= , extract_basename= , --extract_dap + +23 May 2013 [5070] +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +------------------------------------ cycle - cdrskin-1.3.1 - 2013.05.23.155617 +* New API calls burn_drive_extract_audio(), burn_drive_extract_audio_track() +* New cdrskin options extract_audio_to= , extract_tracks= , extract_basename= , + --extract_dap + + +23 May 2013 [5071] +doc/cdtext.txt +Updated documentation about CD-TEXT + +2013.05.26.185945 [5072] +cdrskin/cdrskin.c +Luring K3B into using -xa rather than -xa1 + +2013.05.30.133008 [5076] +cdrskin/cdrskin.c +Bug fix: cdrskin -msinfo on DVD and BD reported +old session start == next writable address. +Regression introduced by version 1.2.8 (rev 4956). + + [] +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +------------------------------------ cycle - cdrskin-1.3.1 - 2013.05.30.133756 +* Bug fix: cdrskin -msinfo on DVD and BD reported + old session start == next writable address. + Regression introduced by version 1.2.8 (rev 4956). + + +31 May 2013 [5079] +svn copy -m Branching for libburn bugfix release 1.3.0.pl01 +http://svn.libburnia-project.org/libburn/tags/1.3.0 +http://svn.libburnia-project.org/libburn/branches/1.3.0.pl01 + +2013.05.31.080001 [5080] +cdrskin/cdrskin.c +Bug fix: cdrskin -msinfo on DVD and BD reported +old session start = next writable address. +Regression introduced by version 1.2.8 (rev 4956). + +31 May 2013 [5081] +README +cdrskin/README +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Mentioned bug fix and pl01 + +31 May 2013 [5082] +svn move -m libburn bugfix release 1.3.0.pl01 is ready +http://svn.libburnia-project.org/libburn/branches/1.3.0.pl01 +http://svn.libburnia-project.org/libburn/tags/1.3.0.pl01 + +------------------------------ release - libburn-1.3.0.pl01 - 2013.05.31.080001 +* Bug fix: cdrskin -msinfo on DVD and BD reported + old session start = next writable address. + Regression introduced by version 1.2.8 (rev 4956). + +31 May 2013 [5083] +README +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Mentioned bug fix and pl01 + +2013.06.09.152602 [5087] +libburn/transport.h +libburn/init.h +libburn/init.c +libburn/util.h +libburn/util.c +libburn/spc.h +libburn/spc.c +libburn/sg-linux.c +libburn/sg-solaris.c +libburn/sg-libcdio.c +libburn/sg-freebsd.c +Improved granularity of SCSI log time measurement and added absolute timestamp + +2013.06.09.154237 [5088] +libburn/util.c +Prepared for optional use of clock_gettime() + +2013.06.09.163052 [5089] +libburn/util.c +libburn/spc.c +Some polishing of SCSI log time measurement + +12 Jun 2013 [5090] +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +------------------------------------ cycle - cdrskin-1.3.1 - 2013.06.12.104457 +* Improved granularity of SCSI log time measurement, now with timestamp + + +24 Jun 2013 [5091] +test/telltoc.c +Removed inactive test code from telltoc.c + +2013.06.28.104134 [5093] +cdrskin/cdrskin.c +Making sure in cdrskin that off_t is large enough before starting libburn + +2013.06.28.104316 [5094] +cdrskin/cdrskin.c +Removed an obsolete note message from cdrskin --devices + +2013.07.01.155958 [5097] +libburn/libburn.h +libburn/init.c +libburn/cleanup.c +New mode bit8 with burn_set_signal_handling() to particularly ignore SIGPIPE + +2013.07.08.145600 [5110] +libburn/cleanup.c +Corrected typo in a comment + +2013.07.08.151826 [5111] +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +New option --pacifier_with_newline + +2013.07.21.170714 [5112] +libburn/os-dummy.h +libburn/os-freebsd.h +libburn/os-libcdio.h +libburn/os-linux.h +libburn/os-solaris.h +Bug fix: The signal handler aborted on SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU + +2013.07.29.091415 [5114] +libburn/libburn.h +Reacted on warnings of Debian build service about doxygen flaws + +2013.08.04.100247 [5116] +configure.ac +libburn/write.c +cdrskin/cdrskin.1 +Changed default write chunk size for BD to 64 KiB + +2013.08.04.124449 [5122] +libburn/libburn.h +Reacted on advise from Helmut Grohne to avoid confusion of doxygen + +04 Aug 2013 [5124] +doc/doxygen.conf.in +Update of doxygen configuration for version 1.8.4 provided by George Danchev + +07 Aug 2013 [5125] +svn copy -m Branching for libburn release 1.3.2 + +2013.08.07.093001 [5126] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.3.2 + +07 Aug 2013 [5127] +- cdrskin/add_ts_changes_to_libburn_1_3_0 +- cdrskin/add_ts_changes_to_libburn_1_3_1 ++ cdrskin/add_ts_changes_to_libburn_1_3_2 ++ cdrskin/add_ts_changes_to_libburn_1_3_3 +Updated cdrskin tarball generator + +07 Aug 2013 [5128] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +----------------------------------- release - cdrskin-1.3.2 - 2013.08.07.093001 +* Bug fix: cdrskin -msinfo on DVD and BD reported + old session start = next writable address. + Regression introduced by version 1.2.8 (rev 4956). +* Bug fix: The signal handler aborted on SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU +* New API call burn_make_input_sheet_v07t() +* API call burn_session_input_sheet_v07t(): read multiple blocks from same file +* New API calls burn_drive_extract_audio(), burn_drive_extract_audio_track() +* New cdrskin option textfile_to_v07t= +* New cdrskin options cdtext_to_textfile= and cdtext_to_v07t= +* New cdrskin options extract_audio_to= , extract_tracks= , extract_basename= , + --extract_dap +* New cdrskin option --pacifier_with_newline +* Improved granularity of SCSI log time measurement, now with timestamp +* Optional "make doc" now demands doxygen 1.8.4 + + +2013.08.07.134744 [5132] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.3.3 + +07 Aug 2013 [5133] +- cdrskin/add_ts_changes_to_libburn_1_3_0 +- cdrskin/add_ts_changes_to_libburn_1_3_1 ++ cdrskin/add_ts_changes_to_libburn_1_3_2 ++ cdrskin/add_ts_changes_to_libburn_1_3_3 +Updated cdrskin tarball generator + +07 Aug 2013 [5134] +ChangeLog +cdrskin/changelog.txt +Updated change log + +07 Aug 2013 [5135] +svn move -m libburn release 1.3.2 is ready +http://svn.libburnia-project.org/libburn/branches/1.3.2 +http://svn.libburnia-project.org/libburn/tags/1.3.2 + +------------------------------------ cycle - cdrskin-1.3.3 - 2013.08.07.135823 + + +2013.09.04.105934 [5145] +libburn/mmc.c +Catching and defaulting mad responses to READ BUFFER CAPACITY + +2013.09.04.110910 [5146] [5147] +cdrskin/cdrskin.c +Closed a small memory leak with cdrskin -msinfo + +2013.09.04.141706 [5148] +cdrskin/cdrskin.c +Reacted on warnings of PLD Linux + +2013.09.05.084834 [5152] +libburn/file.c +libburn/mmc.c +Reacted on warnings of PLD Linux build log + +2013.09.16.172745 [5157] +libburn/async.c +Reacted on warning of Debian buildd with clang + +2013.10.03.100011 [5158] +cdrskin/cdrskin.c +Made -version message more acceptable for K3B. Proposal of Omegaweapon. + +2013.10.09.092306 [5161] +libburn/mmc.c +Separately determining the maximum speeds for writing and reading + +2013.10.09.134543 [5163] +libburn/mmc.c +Forgot to disable a debugging message + +2013.10.10.161931 [5164] +libburn/drive.c +libburn/spc.c +libburn/libdax_msgs.h +Bug fix: Drive error reports were ignored during blanking and formatting + +2013.10.28.104957 [5168] +libburn/libburn.h +libburn/transport.h +libburn/drive.c +libburn/options.h +libburn/options.c +libburn/write.c +libburn/mmc.c +libburn/libburn.ver +New API calls burn_drive_was_feat21_failure(), burn_write_opts_set_fail21h_sev() + +2013.11.08.095023 [5172] +libburn/write.c +Closed potential memory leak introduced by rev 5168 + +2013.11.08.095213 [5173] [5174] +libburn/mmc.c +Enforcing reasonable maximum read speeds even if the drive is lying + +2013.11.10.163403 [5175] +libburn/libburn.h +libburn/drive.c +libburn/libburn.ver +New API call burn_disc_pretend_full_uncond() + +2013.11.11.160915 [5177] +libburn/mmc.c +Bug fix: Drive LG BH16NS40 stalled on inspection of unformatted DVD+RW + +11 Nov 2013 [] +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +------------------------------------ cycle - cdrskin-1.3.3 - 2013.11.11.172016 +* Bug fix: Drive error reports were ignored during blanking and formatting +* Bug fix: Drive LG BH16NS40 stalled on inspection of unformatted DVD+RW +* New API call burn_disc_pretend_full_uncond() + + +2013.11.14.101636 [5180] +libburn/libdax_msgs.h +libburn/spc.h +libburn/spc.c +libburn/sg-linux.c +Improved reaction on Linux SG_IO transport problems + +2013.11.15.102314 [5182] +libburn/mmc.c +Enforcing reasonable minimum read speeds even if the drive is lying + +2013.11.16.170431 [5184] +libburn/options.c +Prevented a memory leak that in most cases was closed by a race condition + +2013.11.17.102351 [5185] +libburn/write.c +Resetting the drive failure status before starting random-access writing + +2013.11.17.152544 [5187] +libburn/sg-linux.c +Corrected bugs introduced with rev 5180 + +2013.11.21.092012 [5189] +libburn/drive.c +Better reaction on drive errors during burn_drive_scan_and_grab() + +12 Dec 2013 [5192] +svn copy -m Branching for libburn release 1.3.4 + http://svn.libburnia-project.org/libburn/trunk + http://svn.libburnia-project.org/libburn/branches/1.3.4 + +2013.12.12.093001 [5193] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.3.4 + +12 Dec 2013 [5194] +- cdrskin/add_ts_changes_to_libburn_1_3_2 +- cdrskin/add_ts_changes_to_libburn_1_3_3 ++ cdrskin/add_ts_changes_to_libburn_1_3_4 ++ cdrskin/add_ts_changes_to_libburn_1_3_5 +Updated cdrskin tarball generator + +12 Dec 2013 [5195] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +----------------------------------- release - cdrskin-1.3.4 - 2013.12.12.093001 +* Bug fix: Drive error reports were ignored during blanking and formatting +* Bug fix: Drive LG BH16NS40 stalled on inspection of unformatted DVD+RW +* New API call burn_disc_pretend_full_uncond() + + +2013.12.12.140531 [5199] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.3.5 + +12 Dec 2013 [5200] +- cdrskin/add_ts_changes_to_libburn_1_3_2 +- cdrskin/add_ts_changes_to_libburn_1_3_3 ++ cdrskin/add_ts_changes_to_libburn_1_3_4 ++ cdrskin/add_ts_changes_to_libburn_1_3_5 +Updated cdrskin tarball generator + +12 Dec 2013 [5201] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-1.3.5 - 2013.12.12.140531 + + +12 Dec 2013 [5202] +svn move -m libburn release 1.3.4 is ready + http://svn.libburnia-project.org/libburn/branches/1.3.4 + http://svn.libburnia-project.org/libburn/tags/1.3.4 + +2014.01.07.115938 [5215] +libburn/transport.h +libburn/drive.h +libburn/drive.c +libburn/mmc.c +Registering all drive-media feature descriptors in burn_drive + +2014.01.09.132159 [5216] +libburn/libburn.h +libburn/spc.c +libburn/mmc.h +libburn/mmc.c +Interpreting feature 0x107 when deciding from where to get speed info + +2014.01.09.214841 [5218] +libburn/sg-linux.c +Adapted Linux SG_IO adapter to scsi/sg.h of git.kernel.org + +2014.01.15.174741 [5225] +libburn/sg-linux.c +Updated copyright claim in sg-linux.c + +2014.01.15.174907 [5226] +libburn/drive.h +libburn/drive.c +libburn/write.c +Improved handling of stdio pseudo-drives after aborted burn runs + +2014.02.04.112944 [5233] +libburn/transport.h +libburn/spc.h +libburn/spc.c +Implemented a generic verification whether a SPC device is a MMC device + +2014.02.05.124803 [5234] +libburn/spc.c +Trying to better handle MMC violating replies of MODE SENSE (Block Descriptors) + +2014.02.05.185801 [5235] +libburn/spc.c +Fixed bugs introduced with previous commit + +2014.02.05.191839 [5237] +libburn/libburn.h +libburn/transport.h +libburn/drive.c +libburn/options.c +libburn/async.c +libburn/spc.c +libburn/mmc.c +libburn/sg-freebsd.c +Prepared for possible demise of mode page 2A + +2014.02.07.180650 [5238] +libburn/write.c +Avoiding to have two file descriptors open to the same stdio drive + +2014.02.10.213159 [5244] +libburn/drive.c +libburn/spc.c +libburn/mmc.c +Being more rugged towards missing MODE SENSE info + +2014.02.10.213500 [5245] +libburn/os.h +libburn/sg.c ++ libburn/os-netbsd.h ++ libburn/sg-netbsd.c +Inmplemented a system adapter for NetBSD + +10 Feb 2014 [5246] +Makefile.am +Introduced NetBSD system adapter in tarball generator + +11 Feb 2014 [5247] +configure.ac +acinclude.m4 +Silenced warnings about -Wchar-subscripts, added /usr/local for NetBSD + +12 Feb 2014 [5249] +doc/comments +cdrskin/README +Mentioned support for NetBSD + +2014.02.13.205358 [5252] +libburn/sg-netbsd.c +Corrected size determination of NetBSD block devices + +2014.02.14.200129 [5253] +libburn/drive.c +Improved workaround for missing mode page 2A + +2014.02.16.203859 [5254] +libburn/spc.c +Inquiring GET PERFORMANCE independently of existence of mode page 2A + +2014.02.19.111017 [5255] +libburn/os.h +libburn/sg.c +libburn/sg-netbsd.c +Removed Linux compilability mock-up from sg-netbsd.c + +2014.03.01.101537 [5257] +libburn/libburn.h +libburn/transport.h +libburn/drive.c +Improved emulation of mode page 2A + +04 Mar 2014 [5260] +svn copy -m Branching for libburn release 1.3.6 + http://svn.libburnia-project.org/libburn/trunk + http://svn.libburnia-project.org/libburn/branches/1.3.6 + +2014.03.04.110001 [5261] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.3.6 + +04 Mar 2014 [5262] +- cdrskin/add_ts_changes_to_libburn_1_3_4 +- cdrskin/add_ts_changes_to_libburn_1_3_5 ++ cdrskin/add_ts_changes_to_libburn_1_3_6 ++ cdrskin/add_ts_changes_to_libburn_1_3_7 +Updated cdrskin tarball generator + +04 Mar 2014 [5263] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +----------------------------------- release - cdrskin-1.3.6 - 2014.03.04.110001 +* New system adapter for NetBSD + + +2014.03.04.161835 [5269] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.3.7 + +04 Mar 2014 [5270] +- cdrskin/add_ts_changes_to_libburn_1_3_4 +- cdrskin/add_ts_changes_to_libburn_1_3_5 ++ cdrskin/add_ts_changes_to_libburn_1_3_6 ++ cdrskin/add_ts_changes_to_libburn_1_3_7 +Updated cdrskin tarball generator + +04 Mar 2014 [5271] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-1.3.7 - 2014.03.04.162814 + + +04 Mar 2014 [5272] +svn move -m libburn release 1.3.6 is ready +http://svn.libburnia-project.org/libburn/branches/1.3.6 +http://svn.libburnia-project.org/libburn/tags/1.3.6 + +2014.03.14.095413 [5281] +libburn/write.c +Bug fix: CD TAO with multiple tracks could cause a buffer overrun + +14 Mar 2013 [5282] +ChangeLog +cdrskin/cdrskin_eng.html +Updated change log + +------------------------------------ cycle - cdrskin-1.3.7 - 2014.03.14.100747 +* Bug fix: CD TAO with multiple tracks could cause a buffer overrun + + +2014.03.17.221810 [5284] +libburn/sg.c +Bug fix: Compilation warning for unsupported systems mutated into an error + +18 Mar 2014 [5285] +svn copy -m Branching for libburn bugfix release 1.3.6.pl01 + http://svn.libburnia-project.org/libburn/tags/1.3.6 + http://svn.libburnia-project.org/libburn/branches/1.3.6.pl01 + +2014.03.18.100001 [5286 tag 1.3.6.pl01] +libburn/write.c +Bug fix: CD TAO with multiple tracks could cause a buffer overrun + +2014.03.18.100001 [5287 tag 1.3.6.pl01] +libburn/sg.c +Bug fix: Compilation warning for unsupported systems mutated into an error + +18 Mar 2014 [5291] +svn move -m libburn bugfix release 1.3.6.pl01 is ready + http://svn.libburnia-project.org/libburn/branches/1.3.6.pl01 + http://svn.libburnia-project.org/libburn/tags/1.3.6.pl01 + +------------------------------ release - cdrskin-1.3.6.pl01 - 2014.03.18.100001 +* Bug fix: Compilation warning for unsupported systems mutated into an error +* Bug fix: CD TAO with multiple tracks could cause a buffer overrun + + +2014.04.06.111004 [5297] +cdrskin/cdrskin.c +Bug fix: Minimum drive buffer fill was measured before the buffer could get full + +2014.04.09.152035 [5302] +libburn/libburn.h +libburn/options.c +libburn/write.c +Bug fix: A final fsync(2) was performed with stdio drives, even if not desired + +2014.04.13.120906 [5309] +libburn/write.c +Restoring capability of burn_random_access_write() to fsync() (lost in rev 5302) + +2014.04.14.103311 [5310] +libburn/write.c +Retrying write(2) if it returns a short non-negative write count + +2014.04.19.114816 [5315] +libburn/libburn.h +libburn/read.c +Improved read retrying with DVD and BD media + +2014.04.29.061645 [5324] +libburn/async.c +Bug fix: Wrong stack usage caused SIGBUS on sparc when compiled by gcc -O2 + +2014.05.03.103448 [5328] +libburn/mmc.c +Bug fix: A failed MMC BLANK command did not cause error indication by libburn + +2014.06.09.183251 [5335] +libburn/libburn.h +libburn/drive.c +libburn/util.h +libburn/util.c +libburn/sg.h +libburn/sg-dummy.c +libburn/sg-freebsd.c +libburn/sg-freebsd-port.c +libburn/sg-libcdio.c +libburn/sg-linux.c +libburn/sg-netbsd.c +libburn/sg-solaris.c +Improved drive capacity estimation for sparse regular files + +2014.06.10.130721 [5336] +libburn/drive.c +Fixed a SIGSEGV introduced by previous revision + +2014.06.20.145224 [5346] +libburn/sg-netbsd.c +Reacted on compiler warning of gcc on NetBSD-current + +27 Jun 2014 [5348] +svn copy -m Branching for libburn release 1.3.8 + http://svn.libburnia-project.org/libburn/trunk + http://svn.libburnia-project.org/libburn/branches/1.3.8 + +2014.06.28.060001 [5349] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.3.8 + +27 Jun 2014 [5350] +- cdrskin/add_ts_changes_to_libburn_1_3_6 +- cdrskin/add_ts_changes_to_libburn_1_3_7 ++ cdrskin/add_ts_changes_to_libburn_1_3_8 ++ cdrskin/add_ts_changes_to_libburn_1_3_9 +Updated cdrskin tarball generator + +27 Jun 2014 [5351] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +----------------------------------- release - cdrskin-1.3.8 - 2014.06,28.060001 +* Bug fix: Wrong stack usage caused SIGBUS on sparc when compiled by gcc -O2 +* Bug fix: Minimum drive buffer fill was measured by cdrskin before the buffer + could get full +* Bug fix: A failed MMC BLANK command did not cause error indication by libburn +* Bug fix: A final fsync(2) was performed with stdio drives, even if not + desired + + +2014.06.28.062807 [5356] +Makefile.am +configure.ac +README +libburn/libburn.h +cdrskin/cdrskin.c +cdrskin/cdrskin.1 +cdrskin/README +cdrskin/compile_cdrskin.sh +cdrskin/cdrskin_timestamp.h +cdrskin/cdrskin_eng.html +Made number transition to 1.3.9 + +28 Jun 2014 [5357] +- cdrskin/add_ts_changes_to_libburn_1_3_6 +- cdrskin/add_ts_changes_to_libburn_1_3_7 ++ cdrskin/add_ts_changes_to_libburn_1_3_8 ++ cdrskin/add_ts_changes_to_libburn_1_3_9 +Updated cdrskin tarball generator + + [ ] +ChangeLog +cdrskin/changelog.txt +Documented changes and release timestamp + +------------------------------------ cycle - cdrskin-1.3.9 - 2014.06.28.062807 + + + [] +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +------------------------------------ cycle - cdrskin-1.3.7 - + + + [] +ChangeLog +cdrskin/cdrskin_eng.html +cdrskin/changelog.txt +Updated change log + +------------------------------------ cycle - cdrskin-1.3.7 - + + +********************************************************************** +Important: When adding a public API function then add its name to file + libburn/libburn.ver +********************************************************************** + +=============================================================================== + TODO +=============================================================================== + + +CD-TEXT: + - with SAO + - libburn: + - optionally genererate TOC pack type 0x88 with A[123] and track POINTS + - .inf files + - enforcing constraints: + - examine how drives react on wodim's cue sheet of INDEX 00 == INDEX 01 + + +qemu: + +- Re-assess benefit of BLKFLSBUF as of dvd+rw-tools/transport.hxx + +- Centralize SCSI log /tmp file opening + + +Get sg-freebsd-port.c functional. + +Expose BD type from bytes 8+4 to 10+4 out of READ DISC STRUCTURE form 0. +Expose Disc Size/Class/Version from byte 11+4. + +Better motivation of burn_set_signal_handling() in libburn.h. + +Explore MMC command GET EVENT STATUS NOTIFICATION + + +-------------------------------- Solaris ----------------------------------- + +Locking of device files. + +Unmounting. + +Could need ioctl to obtain SCSI bus,target,lun from fd rather than name + +( +Subject: Re: [osol-help] DHCP configuration with fixed addresses +pntadm -A 10.0.0.24 -f MANUAL -i 010008544255E7 -m 10.0.0.0 -y 10.0.0.0 +) + + +--------------------------------- bugs ------------------------------------- + +- handle HD DVD profiles 0x50 "HD DVD-ROM", 0x51 "HD DVD-R", 0x52 "HD DVD-RAM" + as readable. + +- Do something about drive->buffer asynchronous race conditions + and dangerous use of temporary dynamic memory. + (The various asynchronous operations use the same buffer + pointer in struct burn_drive and let it point to + their private memory. + Of course, any problem is due to faulty application + ... but then it is really hard to detect.) + +- Why are DVD+R tracks labeled "invisible" by dvd+rw-mediainfo ? + Why does the DVD drive only show the first session ? +READ TRACK INFORMATION[#5]: + Track State: invisible + Track Start Address: 954960*2KB + Free Blocks: 0*2KB + Track Size: 22784*2KB + ROM Compatibility LBA: 265696 + + +------------------------------ end of bugs --------------------------------- + +Support for BD-R SRM+POW + +? Enable profile 0x42 BD-R random recording + +Provide DVD+R DL layer break setter + +Check all SORRY and FATAL errors whether they should become FAILUREs + + +What about cdrskin rc files ? Forward with fallback runs ? + + + [] +Emulate -dummy on overwriteables ? + + [] +Emulate -dummy on DVD+R ? + + + +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 ? + + +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() + + [] +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/trunk/cdrskin/cleanup.c b/trunk/cdrskin/cleanup.c new file mode 100644 index 0000000..f524502 --- /dev/null +++ b/trunk/cdrskin/cleanup.c @@ -0,0 +1,216 @@ +/* + 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; + printf("Strange: The system ignored a SIGSEGV: c= %u\n", (unsigned int) c); + } else { + printf("killme: %d\n",getpid()); + sleep(3600); + } + + Cleanup_set_handlers(NULL,NULL,1); + exit(0); +} + +#endif /* Cleanup_standalonE */ diff --git a/trunk/cdrskin/cleanup.h b/trunk/cdrskin/cleanup.h new file mode 100644 index 0000000..a9d3551 --- /dev/null +++ b/trunk/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/trunk/cdrskin/compile_cdrskin.sh b/trunk/cdrskin/compile_cdrskin.sh new file mode 100755 index 0000000..d4c65fc --- /dev/null +++ b/trunk/cdrskin/compile_cdrskin.sh @@ -0,0 +1,245 @@ +#!/bin/sh + +# compile_cdrskin.sh +# Copyright 2005 - 2014 Thomas Schmitt, scdbackup@gmx.net, GPL v2 or later +# to be executed within ./libburn-* resp ./cdrskin-* + +debug_opts="-O2" +def_opts= +largefile_opts="-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1" +fifo_opts="" +libvers="-DCdrskin_libburn_1_3_9" + +# To be used if Makefile.am uses libburn_libburn_la_CFLAGS +# burn="libburn/libburn_libburn_la-" +burn="libburn/" + +cleanup_src_or_obj="$burn"cleanup.o +libdax_msgs_o="$burn"libdax_msgs.o +libdax_audioxtr_o="$burn"libdax_audioxtr.o +do_strip=0 +static_opts= +warn_opts="-Wall -Wextra -Wno-unused-parameter" +libcdio= +fifo_source="cdrskin/cdrfifo.c" +compile_cdrskin=1 +compile_cdrfifo=0 +compile_dewav=0 + +libcam= +os=$(uname -s) +case $os in + *FreeBSD) + libcam="-lcam" +esac + +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" = "-libburn_1_3_8" + then + libvers="-DCdrskin_libburn_1_3_8" + libdax_audioxtr_o="$burn"libdax_audioxtr.o + libdax_msgs_o="$burn"libdax_msgs.o + cleanup_src_or_obj="$burn"cleanup.o + elif test "$i" = "-libburn_svn" + then + libvers="-DCdrskin_libburn_1_3_9" + libdax_audioxtr_o="$burn"libdax_audioxtr.o + libdax_msgs_o="$burn"libdax_msgs.o + cleanup_src_or_obj="$burn"cleanup.o + elif test "$i" = "-newapi" -o "$i" = "-experimental" + then + def_opts="$def_opts -DCdrskin_new_api_tesT" + elif test "$i" = "-no_largefile" + then + largefile_opts= + elif test "$i" = "-dvd_obs_64k" + then + def_opts="$def_opts -DCdrskin_dvd_obs_default_64K" + 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" = "-use_libburn_fifo" + then + fifo_opts="-DCdrskin_use_libburn_fifO" + elif test "$i" = "-use_no_libburn_fifo" + then + fifo_opts="" + elif test "$i" = "-use_no_cdrfifo" + then + fifo_source= + fifo_opts="-DCdrskin_use_libburn_fifO -DCdrskin_no_cdrfifO" + elif test "$i" = "-use_libburn_cleanup" + then + fifo_source= + fifo_opts="-DCdrskin_use_libburn_cleanuP -DCdrskin_use_libburn_fifO -DCdrskin_no_cdrfifO" + elif test "$i" = "-use_libcdio" + then + libcdio="-lcdio" + 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 " -libburn_1_3_8 set macro to match libburn-1.3.8" + echo " -libburn_svn set macro to match current libburn-SVN." + echo " -dvd_obs_64k 64 KB default size for DVD/BD writing." + echo " -use_libcdio link with -lcdio because libburn uses it." + echo " -do_not_compile_cdrskin omit compilation of cdrskin/cdrskin." + echo " -use_no_libburn_fifo use cdrfifo even for single track non-CD" + echo " -use_no_cdrfifo always use fifo of libburn and never cdrfifo" + echo " -experimental use newly introduced libburn features." + 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 $fifo_source $static_opts $debug_opts $libvers $fifo_opts $def_opts $cleanup_src_or_obj $libcdio $libcam" + cc -I. \ + $warn_opts \ + $static_opts \ + $debug_opts \ + $libvers \ + $largefile_opts \ + $fifo_opts \ + $def_opts \ + \ + -DCdrskin_build_timestamP='"'"$timestamp"'"' \ + \ + -o cdrskin/cdrskin \ + \ + cdrskin/cdrskin.c \ + $fifo_source \ + \ + $cleanup_src_or_obj \ + \ + "$burn"async.o \ + "$burn"cdtext.o \ + "$burn"debug.o \ + "$burn"drive.o \ + "$burn"file.o \ + "$burn"init.o \ + "$burn"options.o \ + "$burn"source.o \ + "$burn"structure.o \ + \ + "$burn"sg.o \ + "$burn"write.o \ + "$burn"read.o \ + $libdax_audioxtr_o \ + $libdax_msgs_o \ + \ + "$burn"mmc.o \ + "$burn"sbc.o \ + "$burn"spc.o \ + "$burn"util.o \ + \ + "$burn"sector.o \ + "$burn"toc.o \ + \ + "$burn"crc.o \ + "$burn"ecma130ab.o \ + \ + $libcdio \ + $libcam \ + -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 \ + "$burn"libdax_audioxtr.o \ + "$burn"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/trunk/cdrskin/convert_man_to_html.sh b/trunk/cdrskin/convert_man_to_html.sh new file mode 100755 index 0000000..3f4c620 --- /dev/null +++ b/trunk/cdrskin/convert_man_to_html.sh @@ -0,0 +1,79 @@ +#!/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 "$man_dir"/"$manpage".1 +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/<\/body>/<BR><HR><FONT SIZE=-1><CENTER>(HTML generated from '"$manpage"'.1 on '"$(date)"' by '$(basename "$0")' )<\/CENTER><\/FONT><\/body>/' \ + -e 's/See section FILES/See section <A HREF="#FILES">FILES<\/A>/' \ + -e 's/See section EXAMPLES/See section <A HREF="#EXAMPLES">EXAMPLES<\/A>/' \ + -e 's/−/-/g' \ + <"$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/trunk/cdrskin/doener_150x200_tr.png b/trunk/cdrskin/doener_150x200_tr.png new file mode 100644 index 0000000..bffad01 Binary files /dev/null and b/trunk/cdrskin/doener_150x200_tr.png differ diff --git a/trunk/cdrskin/doener_150x200_tr_octx.png b/trunk/cdrskin/doener_150x200_tr_octx.png new file mode 100644 index 0000000..7f8e983 Binary files /dev/null and b/trunk/cdrskin/doener_150x200_tr_octx.png differ diff --git a/trunk/cdrskin/make_timestamp.sh b/trunk/cdrskin/make_timestamp.sh new file mode 100755 index 0000000..957a2c4 --- /dev/null +++ b/trunk/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/trunk/cdrskin/wiki_plain.txt b/trunk/cdrskin/wiki_plain.txt new file mode 100644 index 0000000..ac1c3ad --- /dev/null +++ b/trunk/cdrskin/wiki_plain.txt @@ -0,0 +1,302 @@ +-------------------------------------------------------------------------- + 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, DVD-RW, and BD-RE are handled differently +than with cdrecord-ProDVD in order to offer TAO-like single track recording. +Sequential DVD-R[W], DVD+R, DVD+R DL, BD-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, DVD, and BD: http://api.libburnia-project.org + +-------------------------------------------------------------------------- + +For yet unsupported media 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. + +-------------------------------------------------------------------------- + +blank=as_needed applies the suitable blanking or formatting to make +any supported type of media ready for writing from scratch. +If this is not possible, e.g. because the media is written and not +re-usable, then the program run fails. + +Option blank= offers several specialized blanking and formatting types, +which one may use for particular purposes on DVD-RW, DVD-RAM and BD-RE. +(See also below: blank=format_overwrite) +The drive offers a list of possible formats by cdrskin option --list_formats. +One should aquire MMC background information before making use of them. + +-------------------------------------------------------------------------- + +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[ DL] 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, BD-RE 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, DVD+RW, BD-RE 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 . + +-------------------------------------------------------------------------- + +Advanced multi-session use cases as of dvd+rw-tools: + +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 [DL], but also with DVD-RAM, DVD+RW, BD-RE +and even regular disk files or block devices other than CD/DVD writers. +This is enabled by option --grow_overwriteable_iso. + +The libburnia project provides an integrated ISO-9660 multi-session tool +named [wiki:Xorriso xorriso] which tries to go one step beyond +growisofs. It uses [wiki:Libburn libburn] , [wiki:Libisofs libisofs] +and [wiki:Libisoburn libisoburn]. + +See [http://scdbackup.sourceforge.net/man_1_xorriso.html man xorriso]. + +-------------------------------------------------------------------------- + diff --git a/trunk/configure.ac b/trunk/configure.ac new file mode 100644 index 0000000..114334a --- /dev/null +++ b/trunk/configure.ac @@ -0,0 +1,361 @@ +AC_INIT([libburn], [1.3.9], [http://libburnia-project.org]) +AC_PREREQ([2.50]) +dnl AC_CONFIG_HEADER([config.h]) + +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +LIBBURNIA_SET_FLAGS + +AM_INIT_AUTOMAKE([subdir-objects]) +AC_CONFIG_MACRO_DIR([./]) + +dnl Notes about version numbers and .so numbers: +dnl +dnl Regrettably the meaning of the various version types was misunderstood +dnl before version 0.4.1. +dnl +dnl In the past MAJOR.MINOR.MICRO versions led to the following SONAME numbers: +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 (also released as SONAME 4) +dnl +dnl Meanwhile the following schemes are maintained in parallel: +dnl +dnl BURN_MAJOR_VERSION , BURN_MINOR_VERSION , BURN_MICRO_VERSION +dnl are three small non-negative integers which describe the evolution +dnl steps of the library. +dnl Older applications are able to use younger libraries over +dnl quite a long range of such steps. Some day, nevertheless, +dnl compatibility might get terminated, after due notice. +dnl +dnl SONAME (libburn.so.4) +dnl is a small positive integer which marks a family of compatible +dnl evolution steps. Libraries with a particular SONAME allow a binary +dnl with the same SONAME to start up. Any further compatibility check is to +dnl be done by own runtime means. Especially *_version() calls in the API +dnl which return BURN_MAJOR_VERSION, BURN_MINOR_VERSION, BURN_MICRO_VERSION. +dnl See below. +dnl +dnl CURRENT, AGE, REVISION +dnl are three integers used by libtool. CURRENT is positive, the others +dnl non-negative. The use at runtime is not known yet. But libtool computes +dnl at build time SONAME = CURRENT - AGE. +dnl So this is a superspace of the SONAME version space. To avoid +dnl ill SONAME, the value of CURRENT must be larger than AGE. +dnl See also http://www.gnu.org/software/libtool/manual.html#Interfaces +dnl +dnl On Linux the name of the dynamic library will be +dnl libburn.so.$SONAME.$AGE.$REV +dnl In the terminology of this file: +dnl CURRENT = LT_CURRENT +dnl AGE = LT_AGE +dnl REVISION= LT_REVISION +dnl +dnl Beginning with libburn-0.4.1 a rectified counting was introduced as +dnl CURRENT=10, REVISION=1, AGE=6 +dnl This rectification declared that version to be binary compatible up +dnl from libburn-0.3.4. +dnl Real compatibility was given since libburn-0.3.2. +dnl Beware of libburn-0.2.6 which had SONAME=6 and is not binary compatible. +dnl Applications for libburn-0.2 to libburn-0.3.1 need recompilation but no +dnl source code changes. +dnl +dnl Neatly versioned stable releases meanwhile: +dnl 0.4.2 = libburn.so.4.7.0 +dnl 0.4.4 = libburn.so.4.9.0 +dnl 0.4.6 = libburn.so.4.11.0 +dnl 0.4.8 = libburn.so.4.13.0 +dnl 0.5.0 = libburn.so.4.15.0 +dnl 0.5.2 = libburn.so.4.17.0 +dnl 0.5.4 = libburn.so.4.19.0 +dnl 0.5.6 = libburn.so.4.21.0 +dnl 0.5.8 = libburn.so.4.23.0 +dnl 0.6.0 = libburn.so.4.25.0 +dnl 0.6.2 = libburn.so.4.27.0 +dnl 0.6.4 = libburn.so.4.29.0 +dnl 0.6.6 = libburn.so.4.31.0 +dnl 0.6.8 = libburn.so.4.33.0 +dnl 0.7.0 = libburn.so.4.35.0 +dnl 0.7.2 = libburn.so.4.37.0 +dnl 0.7.4 = libburn.so.4.39.0 +dnl 0.7.6 = libburn.so.4.41.0 +dnl 0.7.8 = libburn.so.4.43.0 +dnl 0.8.0 = libburn.so.4.45.0 +dnl 0.8.2 = libburn.so.4.47.0 +dnl 0.8.4 = libburn.so.4.49.0 +dnl 0.8.6 = libburn.so.4.51.0 +dnl 0.8.8 = libburn.so.4.53.0 +dnl 0.9.0 = libburn.so.4.55.0 +dnl 1.0.0 = libburn.so.4.57.0 +dnl 1.0.2 = libburn.so.4.59.0 +dnl 1.0.4 = libburn.so.4.61.0 +dnl 1.0.6 = libburn.so.4.63.0 +dnl 1.1.0 = libburn.so.4.65.0 +dnl 1.1.4 = libburn.so.4.67.0 +dnl 1.1.6 = libburn.so.4.69.0 +dnl 1.1.8 = libburn.so.4.71.0 +dnl 1.2.0 = libburn.so.4.73.0 +dnl 1.2.2 = libburn.so.4.75.0 +dnl 1.2.4 = libburn.so.4.77.0 +dnl 1.2.6 = libburn.so.4.79.0 +dnl 1.2.8 = libburn.so.4.81.0 +dnl 1.3.0 = libburn.so.4.83.0 +dnl 1.3.2 = libburn.so.4.85.0 +dnl 1.3.4 = libburn.so.4.87.0 +dnl 1.3.6 = libburn.so.4.89.0 +dnl 1.3.8 = libburn.so.4.91.0 +dnl +dnl So LT_CURRENT, LT_REVISION and LT_AGE get set directly here. +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 application binary. If SONAME matches, then the couple starts. +dnl +dnl Therefore at run time info is provided by libburn function burn_version(). +dnl It returns the major, minor and micro revision of the library. +dnl Before using any API feature, a program should check for age. +dnl +dnl The variables BURN_*_VERSION are mere copies for informing libtool. +dnl The true values which get issued and should be compared are macros +dnl defined in libburn/libburn.h . +dnl +dnl Normally one can allow a program to run with a library which passed the +dnl linker SONAME test and which is not older than the library it was +dnl developed for. Library2 is younger than library1 if: +dnl major2>major1 || (major2==major1 && +dnl (minor2>minor1 || (minor2==minor1 && micro2 > micro1))) +dnl +dnl If BURN_*_VERSION changes, be sure to change AC_INIT above to match. +dnl +dnl As said: Only copies. Original in libburn/libburn.h : burn_header_version_* +BURN_MAJOR_VERSION=1 +BURN_MINOR_VERSION=3 +BURN_MICRO_VERSION=9 +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 +dnl +dnl ### This is the release version libburn-1.3.8 +dnl This is the development version after above release version +dnl LT_CURRENT++, LT_AGE++ has not yet happened. +dnl ### LT_CURRENT++, LT_AGE++ has happened meanwhile. +dnl +dnl SONAME = 95 - 91 = 4 . Linux library name = libburn.so.4.91.0 +LT_CURRENT=95 +LT_AGE=91 +LT_REVISION=0 +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) + +dnl 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(LIBBURNIA_PKGCONFDIR) +AC_SUBST(LIBBURN_ARCH_LIBS) + +dnl ts A90303 +dnl Check the preconditions for using statvfs() in sg-dummy +dnl (sg-linux and sg-freebsd use statvfs() unconditionally) +STATVFS_DEF=-DLibburn_os_has_statvfS +AC_CHECK_HEADER(sys/statvfs.h, X=, STATVFS_DEF=) +AC_CHECK_FUNC([statvfs], X=, STATVFS_DEF=) +dnl If this would be done more specifically in Makefile.am +dnl via libburn_libburn_la_CFLAGS then undesired .o file names would emerge +CFLAGS="$STATVFS_DEF $CFLAGS" + +dnl ts A91122 +AC_ARG_ENABLE(track-src-odirect, +[ --enable-track-src-odirect Enable use of O_DIRECT with track input, default=no], + , enable_track_src_odirect=no) +if test x$enable_track_src_odirect = xyes; then + LIBBURN_O_DIRECT_DEF="-DLibburn_read_o_direcT" + echo "enabled use of O_DIRECT with track input" +else + LIBBURN_O_DIRECT_DEF= + echo "disabled use of O_DIRECT with track input" +fi +dnl Avoid the need for libburn_libburn_la_CFLAGS in Makefile.am (ugly .o names) +dnl ### AC_SUBST(LIBBURN_O_DIRECT_DEF) +CFLAGS="$LIBBURN_O_DIRECT_DEF $CFLAGS" + +dnl ts A91116 +AC_ARG_ENABLE(dvd-obs-64k, +[ --enable-dvd-obs-64k 64 KB default size for DVD writing, default=no], + , enable_dvd_obs_64k=no) +if test x$enable_dvd_obs_64k = xyes; then + LIBBURN_DVD_OBS_64K="-DLibburn_dvd_obs_default_64K" + echo "enabled write size default 64 KB on DVD" +else + LIBBURN_DVD_OBS_64K= + echo "disabled write size default 64 KB on DVD" +fi +CFLAGS="$LIBBURN_DVD_OBS_64K $CFLAGS" + +dnl ts B20413 +AC_ARG_ENABLE(dvd-obs-pad, +[ --enable-dvd-obs-pad pad DVD DAO sessions to 32 resp. 64 KB, default=no], + , enable_dvd_obs_pad=no) +if test x$enable_dvd_obs_pad = xyes; then + LIBBURN_DVD_OBS_PAD="-DLibburn_dvd_always_obs_paD" + echo "enabled padding of DVD DAO sessions to 32 resp. 64 KB" +else + LIBBURN_DVD_OBS_64K= + echo "disabled padding of DVD DAO sessions to 32 resp. 64 KB" +fi +CFLAGS="$LIBBURN_DVD_OBS_PAD $CFLAGS" + +dnl ts A91218 - B21002 +case $host_os in + cygwin*|mingw*) + default_libcdio=yes + ;; + *) + default_libcdio=no + ;; +esac +# Check for proper library versions if this is desired. +# (It fails too often on too many systems.) +AC_ARG_ENABLE(pkg-check-modules, +[ --enable-pkg-check-modules Enable pkg-config check for libcdio , default=no], + , enable_pkg_check_modules=no) +AC_ARG_ENABLE(libcdio, +[ --enable-libcdio Enable use of libcdio as system adapter, default=no (except on MSWindows)], + , enable_libcdio=$default_libcdio) +PKG_PROG_PKG_CONFIG +if test x$enable_libcdio = xyes; then +dnl Check whether there is libcdio-devel and libcdio-runtime. +dnl If not, erase this macro + LIBCDIO_DEF="-DLibburn_use_libcdiO" +dnl The empty yes case obviously causes -lcdio to be linked + AC_CHECK_HEADER(cdio/cdio.h, AC_CHECK_LIB(cdio, mmc_last_cmd_sense, , LIBCDIO_DEF= ), LIBCDIO_DEF= ) +else + LIBCDIO_DEF= +fi +if test x$LIBCDIO_DEF = x +then + if test x$enable_libcdio = xyes + then + echo "WARNING: could not enable use of libcdio as system adapter" + fi +else + echo "enabled use of libcdio as system adapter" + CFLAGS="$LIBCDIO_DEF $CFLAGS" + + if test x$enable_pkg_check_modules = xyes; then + LIBCDIO_REQUIRED=0.83 + PKG_CHECK_MODULES(LIBCDIO, libcdio >= $LIBCDIO_REQUIRED) + else + echo "checking for LIBCDIO... skipped, no --enable-pkg-check-modules" + fi +fi + +dnl ts B00704 +# Library versioning normally serves a complex purpose. +# Since libburn obeys strict ABI backward compatibility, it needs only the +# simple feature to declare function names "global:" or "local:". Only the +# global ones are visible to applications at library load time. +AC_ARG_ENABLE(versioned-libs, +[ --enable-versioned-libs Enable strict symbol encapsulation , default=yes], + , enable_versioned_libs=yes) +if test x$enable_versioned_libs = xyes; then + vers_libs_test=no + LIBBURN_ASSERT_VERS_LIBS + if test x$vers_libs_test = xno + then + echo "disabled strict symbol encapsulation (test failed)" + else + echo "enabled strict symbol encapsulation" + fi +else + echo "disabled strict symbol encapsulation" +fi + +AC_ARG_ENABLE(ldconfig-at-install, +[ --enable-ldconfig-at-install On GNU/Linux run ldconfig, default=yes], + , ldconfig_at_install=yes) +if test x$ldconfig_at_install = xyes; then + dummy=dummy +else + LIBBURNIA_LDCONFIG_CMD="echo 'NOTE: ldconfig is disabled. If needed, configure manually for:'" + echo "disabled run of ldconfig during installation on GNU/Linux" +fi +AC_SUBST(LIBBURNIA_LDCONFIG_CMD) + + +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="-O3 $CFLAGS" + CFLAGS="-fexpensive-optimizations $CFLAGS" + fi + CFLAGS="-DNDEBUG $CFLAGS" +else + if test x$GCC = xyes; then + CFLAGS="-g -pedantic -Wall -Wextra -Wno-unused-parameter -Wno-char-subscripts $CFLAGS" + fi + CFLAGS="-DDEBUG $CFLAGS" +fi + +dnl Determine target directory for libburn-*.pc +dnl Important: Must be performed _after_ TARGET_SHIZZLE +dnl +LIBBURNIA_SET_PKGCONFIG + + +AC_CONFIG_FILES([ + Makefile + doc/doxygen.conf + version.h + libburn-1.pc + ]) +AC_OUTPUT diff --git a/trunk/doc/Makefile b/trunk/doc/Makefile new file mode 100644 index 0000000..062350d --- /dev/null +++ b/trunk/doc/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/trunk/doc/cdtext.txt b/trunk/doc/cdtext.txt new file mode 100644 index 0000000..00efd76 --- /dev/null +++ b/trunk/doc/cdtext.txt @@ -0,0 +1,736 @@ + + Description of CD-TEXT + +Guided by Leon Merten Lohse via libcdio-devel@gnu.org +by reading mmc3r10g.pdf from http://www.t10.org/ftp/t10/drafts/mmc3/ +by docs and results of cdtext.zip from http://www.sonydadc.com/file/ +by reading http://digitalx.org/cue-sheet/syntax +by reading source of libcdio from http://www.gnu.org/s/libcdio + which quotes source of cdrecord from ftp://ftp.berlios.de/pub/cdrecord/alpha +by reading cdrecord.1 from ftp://ftp.berlios.de/pub/cdrecord/alpha + +Language codes were learned from http://tech.ebu.ch/docs/tech/tech3264.pdf +Genre codes were learned from libcdio and confirmed by +http://helpdesk.audiofile-engineering.com/index.php?pg=kb.page&id=123 + +For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> + +Content: +- CD-TEXT from the view of the user +- Content specifications of particular pack types +- Format of a CD-TEXT packs array +- Overview of libburn API calls for CD-TEXT +- Sony Text File Format (Input Sheet Version 0.7T) +- CDRWIN cue sheet files + + +------------------------------------------------------------------------------- +CD-TEXT from the view of the user: + +CD-TEXT records attributes of disc and tracks on audio CD. + +The attributes are grouped into blocks which represent particular languages. +Up to 8 blocks are possible. + +There are 13 defined attribute categories, which are called Pack Types and are +identified by a single-byte code: + 0x80 = Title + 0x81 = Names of Performers + 0x82 = Names of Songwriters + 0x83 = Names of Composers + 0x84 = Names of Arrangers + 0x85 = Messages + 0x86 = text-and-binary: Disc Identification + 0x87 = text-and-binary: Genre Identification + 0x88 = binary: Table of Content information + 0x89 = binary: Second Table of Content information + (0x8a to 0x8c are reserved.) + 0x8d = Closed Information + 0x8e = UPC/EAN code of the album and ISRC code of each track + 0x8f = binary: Size Information of the Block + +Some of these categories apply to the whole disc only: + 0x86, 0x87, 0x88, 0x89, 0x8d +Some have to be additionally attributed to each track, if they are present for +the whole disc: + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x8e +One describes the overall content of a block and in part of all other blocks: + 0x8f + +The total size of a block's attribute set is restricted by the fact that it +has to be stored in at most 253 records with 12 bytes of payload. These records +are called Text Packs. +A shortcut for repeated identical track texts is provided, so that a text +that is identical to the one of the previous track occupies only 2 or 4 bytes. + + +------------------------------------------------------------------------------- +Content specification of particular pack types: + +Pack types 0x80 to 0x85 and 0x8e contain 0-terminated cleartext. If double byte +characters are used, then two 0-bytes terminate the cleartext. +The meaning of 0x80 to 0x85 should be clear by above list. They are encoded +according to the Character Code of their block. Either as ISO-8859-1 single +byte characters, or as 7-bit ASCII single byte characters, or as MS-JIS double +byte characters. +More info to 0x8e is given below. + +Pack type 0x86 (Disc Identification) is documented by Sony as "Catalog Number: +(use ASCII Code) Catalog Number of the album". So it is not really binary +but might be non-printable, and should contain only bytes with bit7 = 0. + +Pack type 0x87 contains 2 binary bytes, followed by 0-terminated cleartext. +The two binary bytes form a big-endian index to the following list. + 0x0000 = "Not Used" (Sony prescribes to use this if no genre applies) + 0x0001 = "Not Defined" + 0x0002 = "Adult Contemporary" + 0x0003 = "Alternative Rock" + 0x0004 = "Childrens Music" + 0x0005 = "Classical" + 0x0006 = "Contemporary Christian" + 0x0007 = "Country" + 0x0008 = "Dance" + 0x0009 = "Easy Listening" + 0x000a = "Erotic" + 0x000b = "Folk" + 0x000c = "Gospel" + 0x000d = "Hip Hop" + 0x000e = "Jazz" + 0x000f = "Latin" + 0x0010 = "Musical" + 0x0011 = "New Age" + 0x0012 = "Opera" + 0x0013 = "Operetta" + 0x0014 = "Pop Music" + 0x0015 = "Rap" + 0x0016 = "Reggae" + 0x0017 = "Rock Music" + 0x0018 = "Rhythm & Blues" + 0x0019 = "Sound Effects" + 0x001a = "Spoken Word" + 0x001b = "World Music" +Sony documents the cleartext part as "Genre information that would supplement +the Genre Code, such as 'USA Rock music in the 60s'". Always ASCII encoded. + +Pack type 0x88 records information from the CD's Table of Content, as of +READ PMA/TOC/ATIP Format 0010b (mmc3r10g.pdf, table 237 TOC Track Descriptor +Format, Q Sub-channel). +See below, Format of a CD-TEXT packs array, for more details about the content +of pack type 0x88. + +Pack type 0x89 is yet quite unclear. It might be a representation of Playback +Skip Interval, Mode-5 Q sub-channel, POINT 01 to 40 (mmc3r10g.pdf 4.2.3.6.3). +If so, then this seems not to apply to write type SAO, because the CUE SHEET +format offers no way to express Mode-5 Q. +See below, Format of a CD-TEXT packs array, for an example of this pack type. + +Pack type 0x8d is documented by Sony as "Closed Information: (use 8859-1 Code) +Any information can be recorded on disc as memorandum. Information in this +field will not be read by CD TEXT players available to the public." +Always ISO-8859-1 encoded. + +Pack type 0x8e is documented by Sony as "UPC/EAN Code (POS Code) of the album. +This field typically consists of 13 characters." Always ASCII encoded. +It applies to tracks as "ISRC code [which] typically consists of 12 characters" +and is always ISO-8859-1 encoded. +MMC calls these information entities Media Catalog Number and ISRC. +The catalog number consists of 13 decimal digits. +ISRC consists of 12 characters: 2 country code [0-9A-Z], 3 owner code [0-9A-Z], +2 year digits (00 to 99), 5 serial number digits (00000 to 99999). + +Pack type 0x8f summarizes the whole list of text packs of a block. +See below, Format of a CD-TEXT packs array, for details. + + +------------------------------------------------------------------------------- +Format of a CD-TEXT packs array: + +The attributes are represented on CD as Text Packs in the sub-channel of +the Lead-in of the disc. See doc/cookbook.txt for a description how to write +the readily formatted CD-TEXT pack array to CD, and how to read CD-TEXT packs +from CD. + +The format is explained in part in MMC-3 (mmc3r10g.pdf, Annex J) and in part by +the documentation in Sony's cdtext.zip : + +Each pack consists of a 4-byte header, 12 bytes of payload, and 2 bytes of CRC. + +The first byte of each pack tells the pack type. See above for a list of types. + +The second byte tells the track number to which the first text piece in +a pack is associated. Number 0 means the whole album. Higher numbers are +valid for types 0x80 to 0x85, and 0x8e. With these types, there should be +one text for the disc and one for each track. +With types 0x88 and 0x89, the second byte bears a track number, too. +With type 0x8f, the second byte counts the record parts from 0 to 2. + +The third byte is a sequential counter. + +The fourth byte is the Block Number and Character Position Indicator. +It consists of three bit fields: + bit7 = Double Bytes Character Code (0= single byte characters) + bit4-6 = Block Number (groups text packs in language blocks) + bit0-3 = Character position. Either the number of characters which + the current text inherited from the previous pack, or + 15 if the current text started before the previous pack. + +The 12 payload bytes contain pieces of 0-terminated texts or binary data. +A text may span over several packs. Unused characters in a pack are used for +the next text of the same pack type. If no text of the same type follows, +then the remaining text bytes are set to 0. + +The CRC algorithm uses divisor 0x11021. The resulting 16-bit residue of the +polynomial division gets inverted and written as big-endian number to bytes +16 and 17 of the pack. + + +The text packs are grouped in up to 8 blocks of at most 256 packs. Each block +is in charge for one language. Sequence numbers of each block are counted +separately. All packs of block 0 come before the packs of block 1. + +The limitation of block number and sequence numbers imply that there are at +most 2048 text packs possible. (READ TOC/PMA/ATIP could retrieve 3640 packs, +as it is limited to 64 kB - 2.) + + +If a text of a track (pack types 0x80 to 0x85 and 0x8e) repeats identically +for the next track, then it may be represented by a TAB character (ASCII 9) +for single byte texts, resp. two TAB characters for double byte texts. +(This should be used because 256 * 12 bytes is few space for 99 tracks.) + +The two binary bytes of pack type 0x87 are written to the first 0x87 pack of +a block. They may or may not be repeated at the start of the follow-up packs +of type 0x87. + +The first pack of type 0x88 in a block records in its payload bytes: + 0 : PMIN of POINT A1 = First Track Number + 1 : PMIN of POINT A2 = Last Track Number + 2 : unknown, 0 in Sony example + 3 : PMIN of POINT A2 = Start position of Lead-Out + 4 : PSEC of POINT A2 = Start position of Lead-Out + 5 : PFRAME of POINT A2 = Start position of Lead-Out + 6 to 11 : unknown, 0 in Sony example +The following packs record PMIN, PSEC, PFRAME of the POINTs between the +lowest track number (min 01h) and the highest track number (max 63h). +The payload of the last pack is padded by 0s. +The Sony .TOC example: + A0 01 + A1 14 + A2 63:02:18 + 01 00:02:00 + 02 04:11:25 + 03 08:02:50 + 04 11:47:62 + ... + 13 53:24:25 + 14 57:03:25 +yields + 88 00 23 00 01 0e 00 3f 02 12 00 00 00 00 00 00 12 00 + 88 01 24 00 00 02 00 04 0b 19 08 02 32 0b 2f 3e 67 2d + ... + 88 0d 27 00 35 18 19 39 03 19 00 00 00 00 00 00 ea af + +Pack type 0x89 is yet quite unclear. Especially what the information shall +mean to the user of the CD. The time points in the Sony example are in the +time range of the tracks numbers that are given before the time points: + 01 02:41:48 01 02:52:58 + 06 23:14:25 06 23:29:60 + 07 28:30:39 07 28:42:30 + 13 55:13:26 13 55:31:50 +yields + 89 01 28 00 01 04 00 00 00 00 02 29 30 02 34 3a f3 0c + 89 06 29 00 02 04 00 00 00 00 17 0e 19 17 1d 3c 73 92 + 89 07 2a 00 03 04 00 00 00 00 1c 1e 27 1c 2a 1e 72 20 + 89 0d 2b 00 04 04 00 00 00 00 37 0d 1a 37 1f 32 0b 62 +The track numbers are stored in the track number byte of the packs. The two +time points are stored in byte 6 to 11 of the payload. Byte 0 of the payload +seems to be a sequential counter. Byte 1 always 4 ? Byte 2 to 5 always 0 ? + +Pack type 0x8f summarizes the whole list of text packs of a block. +So there is one group of three 0x8f packs per block. +Nevertheless each 0x8f group tells the highest sequence number and the +language code of all blocks. +The payload bytes of three 0x8f packs form a 36 byte record. The track number +bytes of the three packs have the values 0, 1, 2. + Byte : + 0 : Character code for pack types 0x80 to 0x85: + 0x00 = ISO-8859-1 + 0x01 = 7 bit ASCII + 0x80 = MS-JIS (japanese Kanji, double byte characters) + 1 : Number of first track + 2 : Number of last track + 3 : libcdio source states: "cd-text information copyright byte" + Probably 3 means "copyrighted", 0 means "not copyrighted". + 4 - 19 : Pack count of the various types 0x80 to 0x8f. + Byte number N tells the count of packs of type 0x80 + (N - 4). + I.e. the first byte in this field of 16 counts packs of type 0x80. + 20 - 27 : Highest sequence byte number of blocks 0 to 7. + 28 - 36 : Language code for blocks 0 to 7 (tech3264.pdf appendix 3) + Not all of these Codes have ever been seen with CD-TEXT, though. + 0x00 = Unknown + 0x01 = Albanian + 0x02 = Breton + 0x03 = Catalan + 0x04 = Croatian + 0x05 = Welsh + 0x06 = Czech + 0x07 = Danish + 0x08 = German + 0x09 = English + 0x0a = Spanish + 0x0b = Esperanto + 0x0c = Estonian + 0x0d = Basque + 0x0e = Faroese + 0x0f = French + 0x10 = Frisian + 0x11 = Irish + 0x12 = Gaelic + 0x13 = Galician + 0x14 = Icelandic + 0x15 = Italian + 0x16 = Lappish + 0x17 = Latin + 0x18 = Latvian + 0x19 = Luxembourgian + 0x1a = Lithuanian + 0x1b = Hungarian + 0x1c = Maltese + 0x1d = Dutch + 0x1e = Norwegian + 0x1f = Occitan + 0x20 = Polish + 0x21 = Portuguese + 0x22 = Romanian + 0x23 = Romansh + 0x24 = Serbian + 0x25 = Slovak + 0x26 = Slovenian + 0x27 = Finnish + 0x28 = Swedish + 0x29 = Turkish + 0x2a = Flemish + 0x2b = Wallon + 0x45 = Zulu + 0x46 = Vietnamese + 0x47 = Uzbek + 0x48 = Urdu + 0x49 = Ukrainian + 0x4a = Thai + 0x4b = Telugu + 0x4c = Tatar + 0x4d = Tamil + 0x4e = Tadzhik + 0x4f = Swahili + 0x50 = Sranan Tongo + 0x51 = Somali + 0x52 = Sinhalese + 0x53 = Shona + 0x54 = Serbo-croat + 0x55 = Ruthenian + 0x56 = Russian + 0x57 = Quechua + 0x58 = Pushtu + 0x59 = Punjabi + 0x5a = Persian + 0x5b = Papamiento + 0x5c = Oriya + 0x5d = Nepali + 0x5e = Ndebele + 0x5f = Marathi + 0x60 = Moldavian + 0x61 = Malaysian + 0x62 = Malagasay + 0x63 = Macedonian + 0x64 = Laotian + 0x65 = Korean + 0x66 = Khmer + 0x67 = Kazakh + 0x68 = Kannada + 0x69 = Japanese + 0x6a = Indonesian + 0x6b = Hindi + 0x6c = Hebrew + 0x6d = Hausa + 0x6e = Gurani + 0x6f = Gujurati + 0x70 = Greek + 0x71 = Georgian + 0x72 = Fulani + 0x73 = Dari + 0x74 = Churash + 0x75 = Chinese + 0x76 = Burmese + 0x77 = Bulgarian + 0x78 = Bengali + 0x79 = Bielorussian + 0x7a = Bambora + 0x7b = Azerbaijani + 0x7c = Assamese + 0x7d = Armenian + 0x7e = Arabic + 0x7f = Amharic +E.g. these three packs + 42 : 8f 00 2a 00 01 01 03 00 06 05 04 05 07 06 01 02 48 65 + 43 : 8f 01 2b 00 00 00 00 00 00 00 06 03 2c 00 00 00 c0 20 + 44 : 8f 02 2c 00 00 00 00 00 09 00 00 00 00 00 00 00 11 45 +decode to +Byte :Value Meaning + 0 : 01 = ASCII 7-bit + 1 : 01 = first track is 1 + 2 : 03 = last track is 3 + 3 : 00 = copyright (0 = public domain, 3 = copyrighted ?) + 4 : 06 = 6 packs of type 0x80 + 5 : 05 = 5 packs of type 0x81 + 6 : 04 = 4 packs of type 0x82 + 7 : 05 = 5 packs of type 0x83 + 8 : 07 = 7 packs of type 0x84 + 9 : 06 = 6 packs of type 0x85 + 10 : 01 = 1 pack of type 0x86 + 11 : 02 = 2 packs of type 0x87 + 12 : 00 = 0 packs of type 0x88 + 13 : 00 = 0 packs of type 0x89 + 14 : 00 00 00 00 = 0 packs of types 0x8a to 0x8d + 18 : 06 = 6 packs of type 0x8e + 19 : 03 = 3 packs of type 0x8f + 20 : 2c = last sequence for block 0 + This matches the sequence number of the last text pack (0x2c = 44) + 21 : 00 00 00 00 00 00 00 = last sequence numbers for block 1..7 (none) + 28 : 09 = language code for block 0: English + 29 : 00 00 00 00 00 00 00 = language codes for block 1..7 (none) + + +------------------------------------------------------------------------------- +Overview of libburn API calls for CD-TEXT (see libburn/libburn.h for details): + +libburn can retrieve the array of text packs from a CD: + + int burn_disc_get_leadin_text(struct burn_drive *d, + unsigned char **text_packs, int *num_packs, + int flag); + + +It can write a text pack set with a CD SAO session. + +This set may be attached as array of readily formatted text packs by: + + int burn_write_opts_set_leadin_text(struct burn_write_opts *opts, + unsigned char *text_packs, + int num_packs, int flag); + +The array of text packs may be read from a file by + + int burn_cdtext_from_packfile(char *path, unsigned char **text_packs, + int *num_packs, int flag); + + +Alternatively the pack set may be defined by attaching CD-TEXT attributes +to burn_session and burn_track: + + int burn_session_set_cdtext_par(struct burn_session *s, + int char_codes[8], int copyrights[8], + int languages[8], int flag); + + int burn_session_set_cdtext(struct burn_session *s, int block, + int pack_type, char *pack_type_name, + unsigned char *payload, int length, int flag); + + int burn_track_set_cdtext(struct burn_track *t, int block, + int pack_type, char *pack_type_name, + unsigned char *payload, int length, int flag); + +Macros list the texts for genre and language codes: + + BURN_CDTEXT_LANGUAGES_0X00 + BURN_CDTEXT_FILLER + BURN_CDTEXT_LANGUAGES_0X45 + + BURN_CDTEXT_GENRE_LIST + BURN_CDTEXT_NUM_GENRES + +There is a reader for Sony Input Sheet Version 0.7T: + + int burn_session_input_sheet_v07t(struct burn_session *session, + char *path, int block, int flag); + +and a writer which converts an array of text packs to such a Sony Input Sheet: + + int burn_make_input_sheet_v07t(unsigned char *text_packs, int num_packs, + int start_tno, int track_count, + char **result, int *char_code, int flag); + +CD-TEXT can be read from a CDRWIN cue sheet file which defines the tracks +of a session + + int burn_session_by_cue_file(struct burn_session *session, + char *path, int fifo_size, struct burn_source **fifo, + unsigned char **text_packs, int *num_packs, int flag); + + +The session and track attributes can then be converted into an array of +text packs by: + + int burn_cdtext_from_session(struct burn_session *s, + unsigned char **text_packs, int *num_packs, + int flag); + +or they can be written as array of text packs to CD when burning begins and +no array of pre-formatted packs was attached to the write options by +burn_write_opts_set_leadin_text(). + +There are calls for inspecting the attached attributes: + + int burn_session_get_cdtext_par(struct burn_session *s, + int char_codes[8], int copyrights[8], + int block_languages[8], int flag); + + int burn_session_get_cdtext(struct burn_session *s, int block, + int pack_type, char *pack_type_name, + unsigned char **payload, int *length, int flag); + + int burn_track_get_cdtext(struct burn_track *t, int block, + int pack_type, char *pack_type_name, + unsigned char **payload, int *length, int flag); + +and for removing attached attributes: + + int burn_session_dispose_cdtext(struct burn_session *s, int block); + + int burn_track_dispose_cdtext(struct burn_track *t, int block); + + +UPC/EAN and ISRC not only affect CD-TEXT but also information that is written +along with the tracks in Q sub-channel. These can be influenced by +burn_session_input_sheet_v07t(), burn_session_by_cue_file() and by + + void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, + unsigned char mediacatalog[13]); + + void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts, + int has_mediacatalog); + + void burn_track_set_isrc(struct burn_track *t, char *country, char *owner, + unsigned char year, unsigned int serial); + + int burn_track_set_isrc_string(struct burn_track *t, char isrc[13], + int flag); + + +------------------------------------------------------------------------------- +Sony Text File Format (Input Sheet Version 0.7T): + +This text file format provides comprehensive means to define the text +attributes of session and tracks for a single block. More than one +such file has to be read to form an attribute set with multiple blocks. + +The information is given by text lines of the following form: + purpose specifier [whitespace] = [whitespace] content text +[whitespace] is zero or more ASCII 32 (space) or ASCII 9 (tab) characters. +The purpose specifier tells the meaning of the content text. +Empty content text does not cause a CD-TEXT attribute to be attached. + +The following purpose specifiers apply to the session as a whole: + Specifier = Meaning + ------------------------------------------------------------------------- + Text Code = Character code for pack type 0x8f + "ASCII", "8859" + Language Code = One of the language names for pack type 0x8f + Album Title = Content of pack type 0x80 + Artist Name = Content of pack type 0x81 + Songwriter = Content of pack type 0x82 + Composer = Content of pack type 0x83 + Arranger = Content of pack type 0x84 + Album Message = Content of pack type 0x85 + Catalog Number = Content of pack type 0x86 + Genre Code = One of the genre names for pack type 0x87 + Genre Information = Cleartext part of pack type 0x87 + Closed Information = Content of pack type 0x8d + UPC / EAN = Content of pack type 0x8e + Text Data Copy Protection = Copyright value for pack type 0x8f + "ON" = 0x03, "OFF" = 0x00 + First Track Number = The lowest track number used in the file + Last Track Number = The highest track number used in the file + +The following purpose specifiers apply to particular tracks: + Track NN Title = Content of pack type 0x80 + Track NN Artist = Content of pack type 0x81 + Track NN Songwriter = Content of pack type 0x82 + Track NN Composer = Content of pack type 0x83 + Track NN Arranger = Content of pack type 0x84 + Track NN Message = Content of pack type 0x85 + ISRC NN = Content of pack type 0x8e + +The following purpose specifiers have no effect on CD-TEXT: + Remarks = Comments with no influence on CD-TEXT + Disc Information NN = Supplementary information for use by record companies. + ISO-8859-1 encoded. NN ranges from 01 to 04. + Input Sheet Version = "0.7T" + + +libburn peculiarties: + +libburn may read files of the described format by + burn_session_input_sheet_v07t() +after the burn_session has been establiched and all burn_track objects have +been added. It can convert an array of CD-TEXT packs into this format by + burn_make_input_sheet_v07t() + +The following purpose specifiers accept byte values of the form 0xXY. + Text Code , Language Code , Genre Code , Text Data Copy Protection +E.g. to indicate MS-JIS character code (of which the exact name is unknown): + Text Code = 0x80 +Genre Code is settable by 0xXY or 0xXYZT or 0xXY 0xZT. + Genre Code = 0x001b + +Purpose specifiers which have the meaning "Content of pack type 0xXY" +may be replaced by the pack type codes. E.g.: + 0x80 = Session content of pack type 0x80 + Track 02 0x80 = Track content of pack type 0x80 for track 2. +Applicable are pack types 0x80 to 0x86, 0x8d, 0x8e. + +Text Code may be specified only once. It gets speficied to "ISO-8850-1" +automatically as soon as content is defined which depends on the text +encoding of the block. I.e with pack types 0x80 to 0x85. + +If a track attribute is set, but the corresponding session attribute is not +defined or defined with empty text, then the session attribute gets attached +as empty test. (Normally empty content is ignored.) + + +Example cdrskin run with three tracks: + + $ cdrskin dev=/dev/sr0 -v input_sheet_v07t=NIGHTCATS.TXT \ + -audio -swab track_source_1 track_source_2 track_source_3 + +---------------------------------------------------------- +Content of file NIGHTCATS.TXT : +---------------------------------------------------------- +Input Sheet Version = 0.7T +Text Code = 8859 +Language Code = English +Album Title = Joyful Nights +Artist Name = United Cat Orchestra +Songwriter = Various Songwriters +Composer = Various Composers +Arranger = Tom Cat +Album Message = For all our fans +Catalog Number = 1234567890 +Genre Code = Classical +Genre Information = Feline classic music +Closed Information = This is not to be shown by CD players +UPC / EAN = 1234567890123 +Text Data Copy Protection = OFF +First Track Number = 1 +Last Track Number = 3 +Track 01 Title = Song of Joy +Track 01 Artist = Felix and The Purrs +Track 01 Songwriter = Friedrich Schiller +Track 01 Composer = Ludwig van Beethoven +Track 01 Arranger = Tom Cat +Track 01 Message = Fritz and Louie once were punks +ISRC 01 = XYBLG1101234 +Track 02 Title = Humpty Dumpty +Track 02 Artist = Catwalk Beauties +Track 02 Songwriter = Mother Goose +Track 02 Composer = unknown +Track 02 Arranger = Tom Cat +Track 02 Message = Pluck the goose +ISRC 02 = XYBLG1100005 +Track 03 Title = Mee Owwww +Track 03 Artist = Mia Kitten +Track 03 Songwriter = Mia Kitten +Track 03 Composer = Mia Kitten +Track 03 Arranger = Mia Kitten +Track 03 Message = +ISRC 03 = XYBLG1100006 +---------------------------------------------------------- + + +------------------------------------------------------------------------------- +CDRWIN cue sheet files: + +A CDRWIN cue sheet file defines the track data source (FILE), various text +attributes (CATALOG, TITLE, PERFORMER, SONGWRITER, ISRC), track block types +(TRACK), track start addresses (INDEX). +The rules for CDRWIN cue sheet files are described at + http://digitalx.org/cue-sheet/syntax/ +There are three more text attributes mentioned in man cdrecord for defining +the corresponding CD-TEXT attributes: ARRANGER, COMPOSER, MESSAGE. + + +-------------------------------------------------------- +Example of a CDRWIN cue sheet file named NIGHTCATS.CUE : +-------------------------------------------------------- + +CATALOG 1234567890123 +FILE "audiodata.bin" BINARY +TITLE "Joyful Nights" + TRACK 01 AUDIO + FLAGS DCP + TITLE "Song of Joy" + PERFORMER "Felix and The Purrs" + SONGWRITER "Friedrich Schiller" + ISRC XYBLG1101234 + INDEX 01 00:00:00 + TRACK 02 AUDIO + FLAGS DCP + TITLE "Humpty Dumpty" + PERFORMER "Catwalk Beauties" + SONGWRITER "Mother Goose" + ISRC XYBLG1100005 + INDEX 01 08:20:12 + TRACK 03 AUDIO + FLAGS DCP + TITLE "Mee Owwww" + PERFORMER "Mia Kitten" + SONGWRITER "Mia Kitten" + ISRC XYBLG1100006 + INDEX 01 13:20:33 + +By + + $ cdrskin -v dev=/dev/sr0 -text cuefile=NIGHTCATS.CUE + +this yields as text packs: + + 0 : 80 00 00 00 J o y f u l N i g h t f0 f7 + 1 : 80 00 01 0c s 00 S o n g o f J o 43 1c + 2 : 80 01 02 0a y 00 H u m p t y D u m 43 f9 + 3 : 80 02 03 0a p t y 00 M e e O w w w 24 72 + 4 : 80 03 04 08 w 00 00 00 00 00 00 00 00 00 00 00 6e af + 5 : 81 00 05 00 00 F e l i x a n d T 4d 51 + 6 : 81 01 06 0b h e P u r r s 00 C a t a7 40 + 7 : 81 02 07 03 w a l k B e a u t i e 59 80 + 8 : 81 02 08 0f s 00 M i a K i t t e n 30 c9 + 9 : 81 03 09 0a 00 00 00 00 00 00 00 00 00 00 00 00 ad 19 + 10 : 82 00 0a 00 00 F r i e d r i c h S 70 8f + 11 : 82 01 0b 0b c h i l l e r 00 M o t h 33 43 + 12 : 82 02 0c 04 e r G o o s e 00 M i a d6 f5 + 13 : 82 03 0d 03 K i t t e n 00 00 00 00 00 f5 83 + 14 : 8e 00 0e 00 1 2 3 4 5 6 7 8 9 0 1 2 92 3e + 15 : 8e 00 0f 0c 3 00 X Y B L G 1 1 0 1 2 c0 2b + 16 : 8e 01 10 0a 3 4 00 X Y B L G 1 1 0 0 bb b3 + 17 : 8e 02 11 09 0 0 5 00 X Y B L G 1 1 0 f3 bf + 18 : 8e 03 12 08 0 0 0 6 00 00 00 00 00 00 00 00 5b 5c + 19 : 8f 00 13 00 00 01 03 00 05 05 04 00 00 00 00 00 9b fe + 20 : 8f 01 14 00 00 00 00 00 00 00 05 03 15 00 00 00 11 0b + 21 : 8f 02 15 00 00 00 00 00 09 00 00 00 00 00 00 00 da 77 + +-------------------------------------- + +Some restrictions apply in the libburn call burn_session_by_cue_file(): + +Only FILE types BINARY, MOTOROLA, WAVE are allowed. +Only TRACK datatypes AUDIO, MODE1/2048 are allowed. They may not be mixed in +the same session. + +On the other hand, ARRANGER, COMPOSER, MESSAGE are supported unconditionally. + + +------------------------------------------------------------------------------- +This text is copyright 2011 - 2012 Thomas Schmitt <scdbackup@gmx.net>. +Permission is granted to copy, modify, and distribute it, as long as the +references to the original information sources are maintained. +There is NO WARRANTY, to the extent permitted by law. +------------------------------------------------------------------------------- + diff --git a/trunk/doc/comments b/trunk/doc/comments new file mode 100644 index 0000000..9cceec7 --- /dev/null +++ b/trunk/doc/comments @@ -0,0 +1,119 @@ +/** + @author Mario Danic, Thomas Schmitt + + @mainpage Libburn Documentation Index + + @section intro Introduction + +Libburnia is an open-source project for reading, mastering and writing +optical discs. This page is about its capability to handle optical media. +For now this means CD-R, CD-RW, DVD-RAM, DVD+RW, DVD+R, DVD+R/DL, DVD-RW, +DVD-R, DVD-R/DL, BD-R, BD-RE. + +Our scope is currently Linux 2.4 and 2.6, FreeBSD, OpenSolaris, or NetBSD. +For ports to other systems we would need : login on a development machine resp. +an OS that is installable on an AMD 64-bit PC, advise from a system person +about the equivalent of Linux sg or FreeBSD CAM, volunteers for testing of +realistic use cases. + +libburn is the library by which preformatted data get onto optical media. +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. + +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/BD 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. + +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. + +SONAME: +libburn.so.4 (since 0.3.4, March 2007), + + + @section using Using libburn + +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 companion applications 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 + + +@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 burn to recordable CD, DVD, or BD, +can blank a CD-RW or DVD-RW, and can format unformatted DVD-RW, BD-R, or BD-RE. + +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 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] [--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, BD-RE or BD-R: + test/libburner --drive /dev/hdc --format +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/trunk/doc/cookbook.txt b/trunk/doc/cookbook.txt new file mode 100644 index 0000000..548bb04 --- /dev/null +++ b/trunk/doc/cookbook.txt @@ -0,0 +1,1519 @@ + +------------------------------------------------------------------------------- +Note: This is about how libburn operates optical drives. Not about how to + operate libburn. The libburn API is described in libburn/libburn.h +------------------------------------------------------------------------------- + +libburnia-project.org Optical Media Rotisserie Recipes as of December 2011 + +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, BD-RE) +- Sequential DVD-R[W] Cookbook +- DVD+R[/DL] Cookbook +- BD-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 Whether to keep appendable 00b = finalize + 11b = keep appendable + Copy Whether to deny copying 1 = deny by SCMS , 0 = allow + 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 + Media Catalog Number A property of the disc 0x80 if valid + 13 decimal digits as ASCII + ISRC A property of the track 0x80 if valid + 12 letters and digits, ASCII +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) + +Up to december 2009 the track was 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 worked for my drives. +(mmc5r03c.pdf 6.3.3.1.2) +This is omitted since libburn-0.7.4, relying entirely on 35h SYNCHRONIZE CACHE. +First appeared a drive where CLOSE TRACK fails in simulation mode, later +another one produced error replies even with real burning. + +After that, a new track may be written beginning with sending the mode page 05h +again. It is not tested whether 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 whether 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: +If M is smaller than 90: LBA = (M * 60 + S) * 75 + F - 150 +Else : LBA = (M * 60 + S) * 75 + F - 450150 + +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 whether 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/ +backed by reading scms.html from + http://www.barrel-of-monkeys.com/graphics/prod/dvdplayers/ +and by experiments with drives NEC ND-4570A, LG GSA-4082B, LITE-ON LTR48125S, + Optiarc BD RW BD-5300S, LG BDDVDRW GGC-H20L + +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 comprised of four bits: + bit4 = Pre-emphasis (audio only) + bit5 = Digital copy permission: + 0 = prohibited (one-time copy is permitted if SCMS is 00h) + 1 = permitted (unlimited) + bit6 = Data track indicator (bit4 and bit7 shall be 0) + bit7 = 4-channel audio +Usually CTL is 40h for data and 00h for audio. +(mmc5r03c.pdf 6.33.3.4) +ADR is 01h for entries which define time points. It is 02h for media catalog +entries and it is 03h for track ISRC entries. +The bits of CTL and ADR are combined in the CTL|ADR byte. + +TNO is the track number. The TNO of the first track may be chosen in the range +of 1 to 99. The TNO of following tracks must be the TNO of their predecessor +plus 1. The last track must not have a TNO larger than 99. + +INDEX is a subaddress within tracks. INDEX 1 is mandatory and marks the start +of the payload area of a track. The range between INDEX 0 and 1 is called +pre-gap. It should contain zeros if it exists. Further cue sheet entries with +consecutive INDEX numbers mark ranges within the track. The range of the last +index may contain a post-gap with zeros. +(mmc5r03c.pdf 4.2.3.5.2) +A pre-gap of 2 seconds is mandatory only for the first track. Pre-gap and +post-gap may be needed with further tracks if they have neighbors with +different DATA FORM values. (Such mixing is not yet supported by libburn.) + +DATA FORM is 00h for audio payload, 01h for audio pause (Lead-in and Lead-out), +10h for data, 14h for data pause (Lead-in and Lead-out). +This shall be ored with 40h for CD-TEXT in Lead-in. +(mmc5r03c.pdf 6.33.3.11 CD-DA Data Form, 6.33.3.12 CD-ROM mode 1 Form) + +SCMS value 80h in conjunction with bit5 of CTL is an indicator for exhausted +one-time-copy permission. If this permission is still intact, then SCMS is 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 two entries in a Cue Sheet may describe the Media Catalog Number, +a string of 13 characters, also known with CD-TEXT as "UPC/EAN". +(02h, catalog characters 1 to 7) +(02h, catalog characters 8 to 13, 00h) +These two entries shall be omitted if no catalog number is given. + +The next entry (eventually being the first one) describes the Lead-in. +Its content is +(CTL|ADR ,00h,00h, DATA FORM ,00h,00h,00h,00h) +With the CTL|ADR for the first track: 41h for data, 01h for audio. +DATA FORM is pause (audio=01h, data=14h). Ored with 40h if CD-TEXT shall +be stored in Lean-in. + +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. + +Each track may be preceded by two entries describing an ISRC string of 12 +characters. +(CTL | 03h, TNO, characters 1 to 6) +(CTL | 03h, TNO, characters 7 to 12) +These entries shall be omitted if no ISRC is given for the track. +CTL shall be the same as with the track. + +The first information track on disc is preceded by a pause encoding or pre-gap +of at least 2 seconds: +(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. Optional further sectors may occupy +addresses larger than 0. This entry has to come after ISRC, if ISRC is given +for the track. INDEX has to be 0. + +Each track is represented by one or more entries, with increasing index number. +At least the entry for INDEX 1 has to exist: +(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). +There may be more entries with INDEX 2 to 99. Their MSF address tells the +sector where their range starts. This range ends at the MSF of the next entry +in the cue sheet. INDEX information is stored in the sub-channel of the sectors +but not in the Table-of-Content of the disc. + +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,DATA FORM,00h,MIN,SEC,FRAME) +marking the end of the last track. (With libburn CTL is as of the last track.) +DATA FORM is 01h for audio, 14h for data. + + +------------------------------------------------------------------------------- +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) + +If CD-TEXT shall be written into Lead-in, then it is necessary to obtain the +Start Time of Lead-in by 43h READ TOC/PMA/ATIP Format 0100b. It is an MFS +address which varies from media manufacturer to media manufacturer. +Minute will be >= 90. Therefore this conversion applies: + LBA = (M * 60 + S) * 75 + F - 450150 + +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 Whether 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 ?) + Media Catalog Number 0x80 if valid + See also Cue Sheet ADR 02h 13 decimal digits as ASCII + (With SAO, ISRC is transmitted only by the Cue Sheet.) + +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) +Block addresses may be negative for areas before the normally readable +data. Data representation of addresses is 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. + +If CD-TEXT shall be written into Lead-in, then writing begins at the start +address of Lead-in, which was obtained above. +The 18 bytes of each text pack have to be split up to 24 bytes with only the +lowest six bits used in each byte. E.g. text pack + 8F 00 2A 00 01 01 03 00 06 05 04 05 07 06 01 02 48 65 +becomes + 23 30 00 2A 00 00 04 01 00 30 00 06 01 10 10 05 01 30 18 01 00 24 21 25 +4 of these 24 byte packs form a block of DATA FORM 41h. I.e. only 96 bytes +payload per block. The whole range from Lead-in start to LBA -150 has to be +filled with blocks of this form. Therefore it is necessary to write the +list of given packs in repeated cycles. +A typical Lead-in start address is -11635 = FFh FFh D2h 8Dh. +A description of the CD-TEXT pack format is given in file doc/cdtext.txt . + +Writing without CD-TEXT begins at LBA -150 = FFh FFh FFh 6Ah. + +In both cases, 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 whether 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.) + + +------------------------------------------------------------------------------- +Obtaining CD-TEXT from Lead-in : + +Audio CDs may contain CD-TEXT information in their Lead-in. It is gained by +43h READ TOC/PMA/ATIP, Format 0101b. The reply consists of 4 bytes header, +of which the first two bytes give the number of following bytes as big-endian +16 bit number. The other two bytes are 0. +Following are text packs of 18 bytes each. +(mmc5r03c.pdf 6.26.3.7.1 table 495) + + +A description of CD-TEXT packs and of the applicable libburn API calls is +given in file doc/cdtext.txt . + + +---------------------------------------------------------------------------- +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/ +by own experiments with drives NEC ND-4570A, LG GSA-4082B, PHILIPS SPD3300L, +LG GGW H20L, and by BD-RE experiments done by Giulio Orsero on LG BE06LU10. + +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 +BD-RE 0043h + +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, DVD-RW, BD-RE +- Unformatted DVD+RW +- Partly formatted DVD+RW +- Unformatted DVD-RW +- Partly formatted DVD-RW +- Intermediate state DVD-RW +- DVD-RAM and BD-RE formatting +- DVD-RAM and BD-RE speed tuning + +Slightly off topic are +- ISO 9660 multi-session emulation on overwriteable media +- ISO 9660 based TOC emulation on overwriteable media + +------------------------------------------------------------------------------- +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, DVD+RW, BD-RE 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, DVD-RW, BD-RE : + +Full format is the natural state of DVD-RAM. + +BD-RE are sold unformatted and need to be fully formatted first. +See paragraph about DVD-RAM and BD-RE formatting below. + +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) + + +------------------------------------------------------------------------------- +DVD-RAM and BD-RE formatting : + +Although DVD-RAM usually are sold formatted, there may still arise the wish +to adjust formatting. +BD-RE are sold unformatted and need to be formatted prior to usage. + +Two format types are relevant for DVD-RAM : 00h and 01h. +00h offers the default size format and usually a maximum payload size format. +Even with that maximum size payload there is hardware defect management. +(mmc5r03c.pdf 6.5.4.2.1.2) +01h allows to convert payload capacity into spare blocks for defect +managment. There is no way to increase payload capacity by format 01h. +(mmc5r03c.pdf 6.5.4.2.2.1) + +With BD-RE there are three format types : 00h, 30h and 31h. +00h offers the default size format. This may be the only fast formatting +mode that is offered by the drive. +Feature 0023h tells whether format 31h and certain 30h subtypes are available. +(mmc5r03c.pdf 5.3.13) +30h offers several sizes with defect management. Usually there are three +sizes given: #1: default size, #2: maximum spare area, #3: minimal spare. +One may demand any spare size between maximum and minimum. There may be quick +certification and full certification. See feature 0023h. +31h offers a single size and disables defect management. This has the side +effect to speed up writing to nominal speed. +(mmc5r03c.pdf 6.5.4.2.15, 6.24.3.3, Table 472) + +Only format sizes from the list of format descriptors are permissible +for DVD-RAM. The format list can be obtained by 23h READ FORMAT CAPACITIES. +It also includes a description of the current formatting state. +(mmc5r03c.pdf 6.24, 6.24.3.2, 6.24.3.3) + +Formatting is done by command 04h FORMAT UNIT. Its data payload consists +of a Format List Header and a Format Descriptor. It is advisable to set +the Immed bit and the FOV bit in header byte number 1. The descriptor should +be a copy of a descriptor from 23h READ FORMAT CAPACITIES. +(mmc5r03c.pdf 6.5, 6.5.3.2, 6.5.3.3) +With nearly all formats Sub-type should be set to 0. But with BD-RE formats +30h and 31h the Sub-type selects the certification mode. +Usable with 30h seem 10b Full Certification and 11b Quick Certification. +Usable with 31h seem also 00b Quick Reformat and 01b No Certification. +(mmc5r03c.pdf 6.5.4.2.15.1) + +Other format types have their certification intensity controlled by +a pair of bits: CmpList and DCRT. +CmpList resides in CDB byte 1 as bit 3. DCRT resides in the payload byte 1 +as bit 5. Both together should request a quick size change without lengthy +certification but maintaining the list of known defects. +(mmc5r03c.pdf 6.5, table 249, 6.5.3.2) +With DVD-RAM on my PHILIPS SPD3300L drive they prevent any format size +change though. The TSSTcorp SH-S203B works properly. +With BD-RE format 00h, the default is specified to be Quick Reformat, +and with 00h in general certification can only be disabled not enabled. +(mmc5r03c.pdf 6.5.4.2.1.7) + + +------------------------------------------------------------------------------- +DVD-RAM and BD-RE tuning : + +A special aspect of DVD-RAM and BD-RE is their low speed with write operations, +which usually is only half than the nominal speed of media and drive. +This is blamed to the automatic checkreading which happens for managing +eventual defects. + +Defect management of BD-RE can be disabled by format type 31h. See above. +There is no drive known yet which would apply command 2Ah WRITE10 to DVD-RAM +with full speed. + +The only known way to get full speed from DVD-RAM or BD-RE with enabled defect +management is the use of AAh WRITE12 with Streaming Bit set to 1. +(mmc5r03c.pdf 6.45) +With some DVD-RAM drives this fails if a write buffer is not full 32 kB. +With the tested BD-RE one must write full 64 kB buffers, or else writing +might not get into effect at all. + +Although it seems not optimal, this is specified not only to disable the +cumbersome checkread but also to ignore known defects and to write data +to these defective addresses. +(mmc5r03c.pdf 4.8.5) +So the speed-up is only advisable as long as the media are free of +incorrectable errors. + +Caveat: +MMC-5 does not guarantee AAh WRITE12 to work on DVD-RAM or BD-RE at all. +None of the features of profiles 0012h and 0043h promises the existence of +AAh WRITE12. +(mmc5r03c.pdf 5.4.13, 6.45) +Nevertheless it worked on all tested drives if proper alignment and block +size was observed. + + +------------------------------------------------------------------------------- +ISO 9660 multi-session emulation on overwriteable media : + +Overwriteable media provide a single overwriteable track which may grow up to +the full media capacity. There is no builtin table-of-content which records +the history of write sessions. +mount -t iso9660 will use sbsector=0 as default. +The term "superblock" shall depict the first 64 KiB after the sbsector address. + +ISO 9660 multi-session depends on typical TOC information in two ways: +It needs the superblock address MSC1 of the most recently recorded session and +it needs the Next Writeable Address NWA for which to prepare the adress offset. + +The following is learned from growisofs and from ECMA-119: +http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf + +ISO 9660 filesystems provide information about the number of sectors which +is also the lowest unused block address and thus a suitable NWA. +This block address is stored in the Primary Volume Descriptor which is supposed +to be stored in block 16 (eventually relative to MSC1). +The bytes 0 to 5 of a PVD block are + 0x01 'C' 'D' '0' '0' '1' +The sector count can then be read from byte 80 to 83 + sectors= pvd[80] | (pvd[81] << 8) | (pvd[82] << 16) | (pvd[83] << 24); +(Ecma-119.pdf 8.4) +To support CD, DVD and BD media alike, it is advisable to round the NWA +to the next multiple of 32 (= 64 KiB). + +So one can use 0 as MSC1 and prepare a new ISO session for the computed NWA. +After writing the session it is necessary to copy the PVD from session start +plus 16 to LBA 16 and to adjust it to its new location. +The minimal change would be to update the number of image sectors. +It is stored in both notations LSB and MSB: + for(i= 0; i < 4; i++) + pvd[87 - i]= pvd[80 + i]= (sectors >> (8 * i)) & 0xff; + +cdrskin --grow_overwriteable_iso not only patches the sector fields of the +PVD block but also the blocks up to LBA 31 which begin with + 0xff 'C' 'D' '0' '0' '1' +libisoburn submits 64 KiB data buffer to libisofs before image generation and +afterwards writes these 64 KiB as new superblock to LBA 0. + + +------------------------------------------------------------------------------- +ISO 9660 based TOC emulation on overwriteable media : + +Above method of multi-session emulation yields a single session image after +each add-on session. No reliable session history can be read because the +sector size of the existing session gets overwritten by the new one. +A TOC with session history is nevertheless desirable with incremental backups +in order to access older backup states by mounting older superblocks at the +start addresses of older sessions. + +All usual ISO 9660 formatter programs write a complete superblock to the +start of each session. +With a uniform NWA rounding rule it is possible to compute the address of +superblock N+1 as the NWA after session N. The only problem is N=1 +because it gets overwritten by later sessions. + +libisoburn preserves the information of session 1 by writing the first session +to LBA 32 rather than LBA 0. Afterwards it writes the overall superblock to +LBA 0 (up to 31). +So with all further add-on sessions the superblock at LBA 0 will enclose the +overall image, while the superblocks of the sessions form a chain beginning +at LBA 32. Each session superblock points to the next one by its sector count +rounded up to 32. The chain end is marked by the overall image size. +This chain gives the start addresses of sessions. The sector count minus start +address gives the size of a particular session. ECMA-119 explains how to +retrieve more info from the PVD (e.g. the volume id). + +See also the multi-session example in libisofs/doc/checksums.txt. + + +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- + 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 (can only do single-session) + +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 Whether 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 whether 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 Whether 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) : + +Meanwhile confirmed by one user: + +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 indicates 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[/DL] 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 +- 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 whether 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. + + +------------------------------------------------------------------------------- +DVD+R/DL (Dual Layer) : + +libburn treats DL media just like their single layer equivalents. +This seems to work fine for DVD+R/DL, according to a report by nightmorph +in http://libburnia-project.org/ticket/13 . + +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- + BD-R Cookbook +------------------------------------------------------------------------------- +Inspired by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/ +backed by experiments iwith drive LG GGW H20L. + +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) +BD-R 0042h + +There are two basic recording modes defined: Sequential Recording Mode SRM and +Random Recording Mode RRM. The latter is optional and for now not topic of this +text. +(mmc5r03c.pdf 4.5.3.5) + + +- SRM Formatting +- Writing a session in SRM-POW +(- Pseudo-OverWrite SRM+POW) + + +------------------------------------------------------------------------------- +SRM Formatting: + +Despite being write-once media BD-R can optionally carry some formatting. + +SRM has a disc structure model with tracks and sessions. +Several tracks may be open at the same time, each having its own NWA. +(mmc5r03c.pdf 4.5.3.5.2.2) +This structure is formatted onto blank media automatically as soon as the +first serious write attempt occurs. +(mmc5r03c.pdf 4.5.3.5) +Before such a write attempt, blank media may be explicitely formatted with +spares, which provide defect management. +(mmc5r03c.pdf 4.5.3.5.3) +Tracks get created from other tracks via RESERVE TRACK splitting. +(mmc5r03c.pdf 4.5.3.5.2.5) + +On top of defect management there may be Pseudo-OverWrite SRM+POW, a costly +way to write several times to the same LBA. See below. +Fully sequential states are called SRM-POW. +(mmc5r03c.pdf 4.5.3.5.4) + +Explicite formatting is done by 04h FORMAT UNIT. Its data payload consists +of a Format List Header and a Format Descriptor. It is advisable to set +the Immed bit and the FOV bit in header byte number 1. The descriptor should +be a copy of a descriptor from 23h READ FORMAT CAPACITIES but the size may be +adjusted within a certain range. +(mmc5r03c.pdf 6.5, 6.5.3.2, 6.5.3.3) + +Format type 00h creates SRM layouts with a default number of spares (or +eventually RRM) chosen by the format sub-type: + 00b = SRM with support for POW + 01b = SRM without POW (but with some spares for defect management) + 10b = (RRM) +(mmc5r03c.pdf 6.5.4.2.1.6) + +Format type 32h uses the same sub-types but allows to allocate non-default +amounts of spares. Similar to BD-RE format 31h, three format descriptors are +offered: #1: default size, #2: maximum spare area, #3: minimal spare. +The size may be chosen within that range. + +The sense behind the Type Dependent Parameters is obscure +to me. Best will be to set ISA_V and TDMA_V to 0. +(mmc5r03c.pdf 6.5.4.2.1.17) + + +------------------------------------------------------------------------------- +Writing a session in SRM: + +The procedure and constraints for writing BD-R SRM-POW are very similar to +DVD+R. libburn flatly re-uses its DVD+R code except the Close Function for +finalizing a disc. + +In short: +If all written sessions are closed, then there is exactly one NWA. +In the beginning there is an empty session and track. A new track can be +written either with pre-announced size (by RESERVE TRACK) or open-end by +simply starting to write to the NWA. When done the track gets closed by +close function 001b. Then either session or disc gets closed depending on +the Close Function used: +- Close Function 010b closes the session and keeps the media appendable + (same as with DVD+R) +- Close Function 110b finalizes the media and makes it read-only. + (differs from libburn DVD+R procedure which uses 101b) + +------------------------------------------------------------------------------- +Pseudo-OverWrite POW: (no used yet by libburn) + +This enhancement of SRM emulates overwriting of existing data blocks. +(mmc5r03c.pdf 4.5.3.5.4) + +POW establishes a virtual vLBA space on top of the real address space rLBA. +All read and write commands deal with vLBA. It seems that track NWAs are +assumed to be identical in vLBA space and in rLBA space. +It is not clear whether one may write to vLBA blocks which are neither written +yet nor at one of the track NWAs. Probably not, or else one could make NWAs run +into vLBAs which are associated with older rLBAs. + +Replacing invalidated blocks consumes addresses in rLBA space at the NWA of +some track. I.e. no spares are consumed by POW. Nevertheless it is costly by +a special map called Orphanage. It covers rLBA which have been consumed +by differing vLBAs. It never shrinks and can grow with each write to remapped +addresses. +To avoid heavy Orphanage growth it is advised to write mostly to vLBA which +still coincide with their rLBA. E.g. those addresses which have neither been +written as rLBA nor as vLBA yet. So one should begin the vLBA of new sessions +at the NWA of a sufficiently sized track. +(mmc5r03c.pdf 4.5.3.5.4.2 , 4.5.3.6.9) + + +------------------------------------------------------------------------------- +This text is copyright 2011 - 2012 Thomas Schmitt <scdbackup@gmx.net>. +Permission is granted to copy, modify, and distribute it, as long as the +references to the original information sources are maintained. +There is NO WARRANTY, to the extent permitted by law. +------------------------------------------------------------------------------- + diff --git a/trunk/doc/ddlp.txt b/trunk/doc/ddlp.txt new file mode 100644 index 0000000..9211ea5 --- /dev/null +++ b/trunk/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/trunk/doc/doxygen.conf.in b/trunk/doc/doxygen.conf.in new file mode 100644 index 0000000..da790b4 --- /dev/null +++ b/trunk/doc/doxygen.conf.in @@ -0,0 +1,1896 @@ +# Doxyfile 1.8.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed +# in front of the TAG it is preceding . +# 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 sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +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@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# 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 = @abs_top_builddir@ + +# 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, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, +# Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, +# Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +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 = NO + +# 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. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = + +# 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 if your file system +# 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 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 = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# 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 + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# 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 makes 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 + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES (the +# default) will make doxygen replace the get and set methods by a property in +# the documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# 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 + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields or simple typedef fields will be shown +# inline in the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO (the default), structs, classes, and unions are shown on a separate +# page (for HTML and Man pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can +# be an expensive process and often the same symbol appear multiple times in +# the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too +# small doxygen will become slower. If the cache is too large, memory is wasted. +# The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid +# range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 +# symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# 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 respectively 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_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# 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 namespaces 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 FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# 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_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = 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 + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = 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 section-label ... \endif +# and \cond section-label ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro 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 macros 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 + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# 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 = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. Do not use +# file names with spaces, bibtex cannot handle them. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# 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 + +# The WARN_NO_PARAMDOC option can be enabled 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++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +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 be +# 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. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system 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. +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +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 or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +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 + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# 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. + +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, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES +# 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 +# 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 documentation. + +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. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +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 left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# 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. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# 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 compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = 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 CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# 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 + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> +# Qt Help Project / Custom Filters</a>. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> +# Qt Help Project / Filter Attributes</a>. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value 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 (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# 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 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript +# pieces of code that will be used on startup of the MathJax code. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = NO + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search +# engine library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id +# of to a relative location where the documentation can be found. +# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# 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. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +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, letter, legal and +# executive. If left blank a4 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 = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images +# or other source files which should be copied to the LaTeX output directory. +# Note that the files will be copied as-is; there are no commands or markers +# available. + +LATEX_EXTRA_FILES = + +# 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 + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# 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 style sheet 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 related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files +# that can be used to generate PDF. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK 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 docbook will be used as the default path. + +DOCBOOK_OUTPUT = docbook + +#--------------------------------------------------------------------------- +# 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 +# pointed to by INCLUDE_PATH will be searched when 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 that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these 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. For each +# tag file the location of the external documentation should be added. 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. 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 + +# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed +# in the related pages index. If set to NO, only the current project's +# pages will be listed. + +EXTERNAL_PAGES = 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 also works with HAVE_DOT disabled, but 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 = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# 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 +# 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 the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# manageable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# 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 and HAVE_DOT options 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 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 generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = NO + +# If the DIRECTORY_GRAPH 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 svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# 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 MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The 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 +# DOT_GRAPH_MAX_NODES 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, because dot on Windows does not +# seem to support this out of the box. 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 diff --git a/trunk/doc/mediainfo.txt b/trunk/doc/mediainfo.txt new file mode 100644 index 0000000..10b3a4d --- /dev/null +++ b/trunk/doc/mediainfo.txt @@ -0,0 +1,1212 @@ + +------------------------------------------------------------------------------- +Note: This is about how libburn operates optical drives. Not about how to + operate libburn. The libburn API is described in libburn/libburn.h +------------------------------------------------------------------------------- + + + Overview of class specific or individual media information + especially manufacturer and exact product type + +Inspired by Andy Polyakov's http://fy.chalmers.se/~appro/linux/DVD+RW/tools , +and by Joerg Schilling's http://cdrecord.berlios.de/private/cdrecord.html, +backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/, +ECMA-279 (DVD-R), ECMA-337 (DVD+RW), ECMA-349 (DVD+R), +and by searching the web for media manufacturer IDs (see list of URLs below). + +For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> +September 2009 + +------------------------------------------------------------------------------- +CD: + +The manufacturers vary the lead-in address in ATIP + ATIP start of lead in: -12490 (97:15/35) +Media sizes and nominal speeds often get expressed in the ATIP lead-out +address. + ATIP start of lead out: 359849 (79:59/74) +(example media labeled "LITEON CD-RW 4x-12x", by "Nan-Ya Plastics Corporation") + +This is available only with profile 0x09 (CD-R) and 0x0A (CD-RW). +CD-ROM media (0x08) cannot be inquired by command 43h READ TOC/PMA/ATIP. + +6.26 READ TOC/PMA/ATIP Format 4 with MSF bit produces these data as described +in 6.26.3.6 Response Format 0100b: ATIP. +The minute, second, and frame number of lead-in can be read from byte 4 to 6 +of the ATIP descriptor. The lead-out bytes are at position 8 to 10. The ATIP +descriptor is preceeded by a four byte header. + +I could not find out yet whether (lead-in,lead-out) is mandatorily unique for +each particular media product. + +The parameters can be obtained via libburn by : + burn_disc_read_atip(d), + burn_drive_get_start_end_lba(d, start_lba, end_lba, 0), + burn_lba_to_msf(start_lba, &m_li, &s_li, &f_li) and + burn_lba_to_msf(end_lba, &m_lo, &s_lo, &f_lo) + +There seems to be no public and free list of manufacturers and codes. +Nevertheless many people report codes and manufacturer names in the web. +Especially helpful are media compatibility lists by drive manufacturers. + +The items from the CD media list below are the foundation of libburn API call +burn_guess_cd_manufacturer() which rounds the lead-in address down to +multiples of 10 frames and then looks them up in its list. + + +------------------------------------------------------------------------------- +DVD + BD: + +dvd+rw-mediainfo uses MMC-5 6.23, ADh READ DISC STRUCTURE which returns media +type specific information if the drive supports the particular Format Code. + +With DVD+ and BD there is a 8 character Manufacturer ID, a 3 character Media +Id and a single character Media Revision. + +With DVD-R, ECMA-279 promises a Manufacturer ID of up to 18 characters. +With DVD-RW, ECMA-338 gives up the meaning of the last six-pack. +Both, DVD-RW RITEKW01 and DVD-R RITEKF1 bear unprintable characters there. + +With DVD-ROM and DVD-RAM there seem to be no such ids. + + +6.23.3.2.1 Format Code 00h: Physical Format Information + +Disk Category in bits 4 to 7 of byte 0 gives the "Book Type". +MMC-5 Table 401 gives this list + 0x0 DVD-ROM + 0x1 DVD-RAM + 0x2 DVD-R + 0x3 DVD-RW + 0x4 HD DVD-ROM + 0x5 HD DVD-RAM + 0x6 HD DVD-R + 0x9 DVD+RW + 0xA DVD+R + 0xD DVD+RW DL + 0xE DVD+R DL +Bits 0 to 3 is Part Version "[revision %d]" + + +6.23.3.1.7 Format Code FFh: Disc Structure List + +Returns a list of 4-byte list entries: +Byte0 gives an available format code. +Byte1,bit6 confirms that it is readable. +Bytes 2 and 3 predict the reply length. + + +DVD+RW and DVD+R: + +dvd+rw-mediainfo prefers this if available: + + Format code 11h : "ADIP Information" + dvd+rw-mediainfo: + printf (" Media ID: %.8s/%.3s\n",dvd._11+23,dvd._11+31), + = ECMA-337 (DVD+RW) and ECMA-349 (DVD+R) + 14.4.2 Physical format information in ADIP, Table 3 + byte 19: Disk Manufacturer ID + byte 27: Media Type ID + (byte 28: Product revision number) + MMC-5 6.23.3.2.17 , Table 426 shows 4 byte header: + MSB,LSB of Disc Structure Data Length + 2 bytes reserved + +Two of my burners do not perform this operation on DVD+ media and reply +[5 24 00] "Invalid field in cdb": + 'HL-DT-ST' 'BD-RE GGW-H20L' + 'HL-DT-ST' 'BDDVDRW GGC-H20L' +The other one does it with the same media: + 'TSSTcorp' 'CDDVDW SH-S203B' + +This refusal seems not uncommon. dvd+rw-mediainfo later does: + + Format code 00h : "Physical Format Information" + dvd+rw-mediainfo: + if (dvd_plus && !plus_mediaid_printed) + printf (" Media ID: %.8s/%.3s\n",header+23,header+31); + = ECMA-337 (DVD+RW) and ECMA-349 (DVD+R) + 14.4.2 Physical format information in ADIP, Table 3 + byte 19: Disk Manufacturer ID + byte 27: Media Type ID + (byte 28: Product revision number) + MMC-5 6.23.3.2.1 , Table 400, Table 407 + says for DVD+RW bytes 19 - 255 are a copy of bytes 19 -255 from ADIP + dvd+rw-mediainfo applies this to DVD+R too + + +DVD-R , DVD-RW, (DVD-R DL) : + +Format code 0Eh "Pre-recorded Information in Lead-in" + dvd+rw-mediainfo: + if (!dvd_plus && dvd_0E && dvd._e[4+16]==3 && dvd._e[4+24]==4) + printf (" Media ID: %6.6s%-6.6s\n",dvd._e+4+17,dvd._e+4+25); + = ECMA-279 (DVD-R) + 27.3.7 Field ID3 to Field ID5 + 28.3.2.1 RMD Field 0, Table 20 - Copy of Pre-pit Information + It appears that the reply format of MMC ADh format 0Eh is the data record + shown in Table 20 of ECMA-279 with offset 22: + Manufacturer ID at bytes 17 to 22, 25 to 30, 33 to 38. + Bytes 16, 24, 32 have content 3, 4, 5. + ECMA-338 (DVD-RW) says the same in: + 29.3.3.1.1 Format1 RMD Field0 + Table 24 - Copy of Pre-pit Information + Manufacturer ID at bytes 17 to 22, 25 to 30, but not at 33 + 6.23.3.2.14 Format Code 0Eh: Pre-recorded Information in Lead-in + Table 423 shows 4 byte header: + MSB,LSB of Disc Structure Data Length + 2 bytes reserved + + +>>> still to evaluate: + Format code 0Fh : "Unique Disc Identifier" + = 6.23.3.2.15 Format Code 0Fh: Unique Disc Identifier + + +DVD-RAM : + +dvd+rw-mediainfo lists no info about manufacturer or type. +No format code of ADh READ DISC STRUCTURE promises such info for DVD-RAM. + + +BD-R and BD-RE: + + dvd+rw-mediainfo: + if (di[4+0]=='D' && di[4+1]=='I') + printf (" Media ID: %6.6s/%-3.3s\n",di+4+100,di+4+106); + = MMC-5 6.23.3.3.1 Format Code 00h: Disc Information (DI) + Table 288 says that Media Type Code (byte 1 of CDB) for BD media is 1. + Table 446 says that Disc Information is preceeded by 4 bytes of header. + Table 448 says that bytes 0 to 1 are Disc Information Identifier "DI". + that bytes 8 to 10 are Disc Type Identifier + BDO for BD-ROM, BDW for BD-RE, + BDR for BD-R + that bytes 100 to 105 are Disc Manufactuer ID + that bytes 106 to 108 are Media Type ID + that byte 111 is Product Revision Number + + +Via libburn these manufacturer and media ids can be retrieved by API call +burn_disc_get_media_id() as a single printable word product_id and as two +printable words media_code1, media_code2. The latter can be translated into +a manufacturer company name by API call burn_guess_manufacturer(). +(Both calls work for CD, too. burn_disc_get_media_id() eventually calls + burn_disc_read_atip().) + + +=============================================================================== + + Collection of Media IDs + +Sources: + +[Gig] http://www.gigabyte.com.tw/FileList/WebPage/old_system_oddwebpage/media_support_list_r5232a.pdf +[Aopen] http://download.aopen.com.tw/Download.aspx?RecNo=11411&Model=2597&Section=1&DL=yes +[Msi] http://ru.msi.com/products/multimedia/optical/spec/recommend_disc.pdf +[Btc] http://www.btc.com.tw/english/3-2-1MediaSupport_combo5216_cdr.htm +[Mits] http://www.mitsubishielectric.com.au/assets/vis/DD16C48_Media_Compatibility.pdf +[Hij] http://hijacker.rpc1.org/toshiba/SD-R5372V/MediaList_SD-R5372V_TU15.pdf +[Teac] http://www.google.com/url?sa=t&source=web&ct=res&cd=4&url=http%3A%2F%2Fdspd.teac.de%2Ffileadmin%2Fredaktion%2Fdownloads%2Ffirmware%2Fmedialist%2Fmedialist_dvw50e_130.pdf&ei=0smfSt-TBNaosgbnm6wZ&rct=j&q=cd+%22manufacturer+id%22+97&usg=AFQjCNH4hSHb7yphONuO_NnaaWLV4g5v9A +[Blu] http://www.blu-raydisc.info/licensee_info.php +[Edig] http://www.editgrid.com/user/idrs/DVR112_MediaList_109_20070118_2final.xml + + +Found by : www.google.com : NAN-YA cmc Princo Ricoh lead-in +Try also : www.google.com : cd "manufacturer id" 97 + +------------------------------------------------------------------------------ + + CD Media IDs + +The findings from the web have been cross checked with the lists of cdrecord. +But no codes or manufacturer ids have been taken from diskid.c out of +cdrtools-2.01.01a39/cdrecord/. + +X = not in cdrecord +D = different in cdrecord +The exact lettering of manufacturer names may differ from cdrecord. +For any exact comparison use rather the minute,second,frame tuples of the ATIP +lead-in address directly. The vast majority of these tuples begin with 97. +(libburn API call burn_disc_get_media_id() encodes both tuples in its reply + parameters as e.g. "97m15s35f/79m59s74f" , "97m15s35f", "79m59s74f") + + +Chosen Name +Lead-in Lead-out Name [source] +======================================================== + +"SKC" +X +96 40 05 79 59 74 - SKC(90) 24X [Msi] + +"Ritek Corp" +X +96 43 33 79 59 74 702.83MB TRAXDATA 24X [Msi] +96 43 33 79 59 74 90 32x "Prodisc Technology Inc." [Gig] +96 43 37 79 59 73 99 48x "Ritek Corp" [Gig] +96 43 37 79 59 73 Ritek [Aopen] +96 43 37 79 59 74 702.83MB PHIL(Ritek) Multi Speed [Msi] +96 43 37 79 59 74 90 48x "Ritek Corp" [Gig] +96 43 37 79 59 74 Ritek [Aopen] +96 43 37 79 59 74 Ritek [Btc] + +"TDK / Ritek" +X +97 10 00 79 59 74 702.83MB RiDATA(Ritek) N/A 12X [Msi] +97 10 00 79 59 74 702.83MB TRAXDATA(Ritek) 10X [Msi] +97 10 00 79 59 74 702.83MB TRAXDATA(Ritek) N/A 10X [Msi] +97 10 00 79 59 74 80 12X "TDK Corporation" [Gig] +97 10 00 79 59 74 TDK(RITEK) [Aopen] +97 10 01 79 59 74 702.83MB Ritek.Co. 16X [Msi] +97 10 01 79 59 74 Ritek [Aopen] +97 10 03 79 59 74 80 32X "RITEK" [Gig] + +"TDK Corporation" +97 15 00 79 59 74 702.83MB 52X TDK Multi Speed [Msi] +97 15 00 79 59 74 TDK [Btc] +97 15 01 79 59 74 80 24X "TDK Corporation" [Gig] +97 15 05 79 59 74 80 52X "TDK Corporation" [Gig] +97 15 05 79 59 74 TDK 52X [Msi] +97 15 05 79 59 74 TDK [Aopen] +97 15 05 79 59 74 TDK [Btc] +97 15 05 91 01 48 800MB TDK 40X 800MB [Msi] +97 15 05 91 01 48 800MB TDK 40X [Msi] +97 15 05 91 01 48 90 24X "TDK Corporation" [Gig] +97 15 05 91 01 48 TDK [Aopen] + +"Ritek Corp" +97 15 12 79 59 73 702.83MB Ritek Co. 52X [Msi] +97 15 12 79 59 73 80 52x "Ritek Corp" [Gig] +97 15 12 79 59 73 Ritek,80 [Aopen] +97 15 12 79 59 73 Ritek [Btc] +97 15 12 79 59 73 Ritek CD-R 52X 702.83MB [Mits] +97 15 12 79 59 74 80 24x "Ritek Corp" [Gig] +97 15 12 79 59 74 Ritek [Btc] +97 15 17 24 15 01 Ritek [Btc] +97 15 17 74 45 01 Ritek [Btc] +97 15 17 79 59 70 702.83MB 7-plus (Ritek) Multi Speed [Msi] +97 15 17 79 59 70 702.83MB Aopen (Ritek) Multi Speed [Msi] +97 15 17 79 59 70 702.83MB PONY (Ritek) Multi Speed [Msi] +97 15 17 79 59 70 702.83MB PONY(Ritek) Multi Speed [Msi] +97 15 17 79 59 70 702.83MB Power Source(Ritek) 32X [Msi] +97 15 17 79 59 70 702.83MB Ritek 40X [Msi] +97 15 17 79 59 70 702.83MB Ritek Co. 52X [Msi] +97 15 17 79 59 70 702.83MB TDK (Ritek ) 40X [Msi] +97 15 17 79 59 70 702.83MB TDK(Ritek ) 40X [Msi] +97 15 17 79 59 70 702.83MB TDK(Ritek ) 48X [Msi] +97 15 17 79 59 70 702.83MB TDK(Ritek) 48X [Msi] +97 15 17 79 59 70 702.83MB TRAXDATA (Ritek ) 40X [Msi] +97 15 17 79 59 70 80 52x "Ritek Corp" [Gig] +97 15 17 79 59 70 Aopen CD-R Multi Speed 702.83MB [Mits] +97 15 17 79 59 70 HiCO CD-R Multi Speed 702.83MB [Mits] +97 15 17 79 59 70 PHILIPS CD-R 48X 702.83MB [Mits] +97 15 17 79 59 70 PONY CD-R Multi Speed 702.83MB [Mits] +97 15 17 79 59 70 Primdisc CD-R 52X 702.83MB [Mits] +97 15 17 79 59 70 RiDATA CD-R 40X 702.83MB [Mits] +97 15 17 79 59 70 Ritek [Aopen] +97 15 17 79 59 70 Ritek [Btc] +97 15 17 79 59 70 Ritek CD-R 40X 702.83MB [Mits] +97 15 17 79 59 70 Ritek CD-R 52X 702.83MB [Mits] +97 15 17 79 59 70 TDK CD-R 40X 702.82MB [Mits] +97 15 17 79 59 70 TDK CD-R 40X 702.83MB [Mits] +97 15 17 79 59 70 TRAXDATA CD-R 40X 702.83MB [Mits] +97 15 17 79 59 70 Victor.JVC CD-R 48X 702.82MB [Mits] +97 15 17 79 59 71 702.82MB PONY(Ritek) Multi Speed [Msi] +97 15 17 79 59 71 702.83MB TRAXDATA(Ritek) 24X [Msi] +97 15 17 79 59 71 80 24x "Ritek Corp" [Gig] +97 15 17 79 59 71 Ritek [Btc] +97 15 17 79 59 72 Ritek [Btc] +97 15 17 79 59 73 702.83MB OPTI STORAGE (Ritek) 40X [Msi] +97 15 17 79 59 73 702.83MB Ritek 48X [Msi] +97 15 17 79 59 73 702.83MB Ritek.Co (Trax) 40X [Msi] +97 15 17 79 59 73 702.83MB Samsung Digit(Ritek) 40X [Msi] +97 15 17 79 59 73 80 52x "Ritek Corp" [Gig] +97 15 17 79 59 73 Ritek [Aopen] +97 15 17 79 59 74 Ritek [Btc] +97 15 18 79 59 74 80 32x "Ritek Corp" [Gig] + +"Mitsubishi Chemical Corporation" +97 15 21 79 59 74 MCC ("Mitsubishi Chemical Corporation") [Btc] + +"Nan-Ya Plastics Corporation" +97 15 35 79 59 73 702.83MB NANYA 4-12X [Msi] +97 15 35 79 59 73 80 12X "Nan-Ya" [Gig] +97 15 35 79 59 73 NanYa [Aopen] +97 15 35 79 59 74 702.83MB Hatron(NANYA) 4-10X [Msi] +97 15 35 79 59 74 702.83MB MMore(NANYA) 4-10X [Msi] +97 15 35 79 59 74 (NanYa) [Aopen] +97 15 36 79 59 74 80 40x "Nan-Ya plastics Corporation" [Gig] +97 15 36 79 59 74 NanYa [Aopen] +97 15 36 79 59 74 NANYA [Btc] +97 15 37 79 59 73 NANYA [Btc] +97 15 37 79 59 74 702.83MB NANYA 1-52X [Msi] +97 15 37 79 59 74 702.83MB NANYA 48X [Msi] +97 15 37 79 59 74 80 52x "Nan-Ya plastics Corporation" [Gig] +97 15 37 79 59 74 Acer CD-R 48X 702.83MB [Mits] +97 15 37 79 59 74 LITEON CD-R 48X 702.83MB [Mits] +97 15 37 79 59 74 LITEON CD-R 52X 702.83MB [Mits] +97 15 37 79 59 74 NANYA [Btc] +97 15 37 79 59 74 NANYA CD-R 1-52X 702.83MB [Mits] +97 15 39 79 59 74 80 52x "Nan-Ya plastics Corporation" [Gig] +97 15 39 79 59 74 NanYa [Aopen] +97 15 39 79 59 74 NANYA [Btc] + +"Delphi" +X +97 15 57 79 59 73 Delphi [Aopen] +97 15 57 79 59 74 Delphi [Btc] + +"Shenzhen SG&SAST" +97 16 29 79 59 73 702.83MB SAST(SHENZHEN) Multi peed [Msi] +97 16 29 79 59 73 80 52x "Shenzhen SG&SAST [Gig] +97 16 29 79 59 73 SAST [Btc] +97 16 29 79 59 73 SAST(ultra [Aopen] +97 16 29 79 59 74 80 52x "Shenzhen SG&SAST" [Gig] + +"Moser Baer India Limited" +97 17 00 4x "Moser Baer India Limited" [Gig] +97 17 01 79 59 74 MBI [Aopen] +97 17 06 79 59 74 702.83MB EMTEC (Moser Bear India) 48X [Msi] +97 17 06 79 59 74 702.83MB Intenso (Moser Bear India) 1-48X [Msi] +97 17 06 79 59 74 702.83MB Moser Bear India 52X [Msi] +97 17 06 79 59 74 702.83MB YAKUMO(Moser Bear India) 52X [Msi] +97 17 06 79 59 74 80 52x "Moser Baer India Limited" [Gig] +97 17 06 79 59 74 MBI [Aopen] +97 17 06 79 59 74 Moser Bear India CD-R 52X 702.83MB [Mits] +97 17 06 79 59 74 PLATINUM CD-R 52X 702.83MB [Mits] +97 17 06 79 59 74 Silver Circle CD-R 52X 702.83MB [Mits] + +"SKY media Manufacturing SA" +X +97 17 16 79 59 74 80 48x "SKY media Manufacturing SA" [Gig] +97 17 16 79 59 74 SKY [Aopen] + +"Wing" +D "WEALTH FAIR INVESTMENT LIMITED" +97 18 17 79 59 74 Wing [Aopen] + +"DDT" +X +97 18 28 79 59 74 DDT [Aopen] + +"Taroko International Co.Ltd" +97 18 67 79 59 73 80 48x "Taroko International Co.Ltd" [Gig] +97 18 67 79 59 74 Taroko [Aopen] +97 18 67 79 59 74 TAROKO [Btc] + +"Daxon Technology Inc. / Acer" +D "Acer Media Technology, Inc." +97 22 60 4x "Daxon Technology Inc." [Gig] +97 22 60 74 41 50 74 12X "Daxon Technology Inc." [Gig] +97 22 60 74 41 50 Acer CD-RW 4X 656.20MB [Mits] +97 22 60 74 41 50 Acer US-RW 24X 656.20MB [Mits] +97 22 60 74 41 50 Daxon [Aopen] +97 22 60 79 59 74 702.83MB Maxmax(Acer) 8-10X [Msi] +97 22 60 79 59 74 80 24x "Daxon Technology Inc." [Gig] +97 22 60 79 59 74 Acer CD-RW 4X 702.83MB [Mits] +97 22 60 79 59 74 Daxon [Aopen] +97 22 60 79 59 74 Diamond Data HS-RW 4-16X 702.83MB [Mits] +97 22 60 79 59 74 Maxmax HS-RW 8-10X 702.83MB [Mits] +97 22 62 79 59 74 702.83MB Acer 4-16X [Msi] +97 22 62 79 59 74 702.83MB Acer N/A 4-16X [Msi] +97 22 62 79 59 74 Acer [Aopen] +97 22 66 79 59 74 80 52x "Daxon Technology Inc." [Gig] +97 22 66 79 59 74 Acer [Aopen] +97 22 67 79 59 74 702.83MB Acer 52X [Msi] +97 22 67 79 59 74 702.83MB BenQ(Acer) 40X [Msi] +97 22 67 79 59 74 702.83MB BenQ (Acer) 48X [Msi] +97 22 67 79 59 74 702.83MB Daxon(Acer) 48X [Msi] +97 22 67 79 59 74 702.83MB gold(Acer) 32X [Msi] +97 22 67 79 59 74 702.83MB SONY(Acer) 48X [Msi] +97 22 67 79 59 74 80 52x "Daxon Technology Inc." [Gig] +97 22 67 79 59 74 Acer [Btc] +97 22 67 79 59 74 Acer CD-R 52X 702.83MB [Mits] +97 22 67 79 59 74 Acer,DAXON [Aopen] + +"Taiyo Yuden Company Limited" +97 24 01 74 43 00 74 16x "Taiyo Yuden Company Limited" [Gig] +97 24 01 74 43 01 656.40MB Maxell(Taiyo Yuden) 32X [Msi] +97 24 01 74 43 01 656.40MB Taiyo Yuden 32X [Msi] +97 24 01 74 43 01 74 48x "Taiyo Yuden Company Limited" [Gig] +97 24 01 74 43 01 Taiyo Yuden [Btc] +97 24 01 74 43 01 TY,tuned [Aopen] +97 24 01 74 43 02 656.40MB Taiyo Yuden 40-48X [Msi] +97 24 01 74 43 02 74 52x "Taiyo Yuden Company Limited" [Gig] +97 24 01 74 43 02 FUJIFILM CD-R 48X 656.40MB [Mits] +97 24 01 74 43 02 Taiyo Yuden [Btc] +97 24 01 74 43 02 TY [Aopen] +97 24 01 79 59 72 702.83MB Taiyo Yuden 40-48X [Msi] +97 24 01 79 59 72 702.83MB Taiyo Yuden 48X [Msi] +97 24 01 79 59 72 702.83MB Taiyo Yuden Multi Speed [Msi] +97 24 01 79 59 72 80 52x "Taiyo Yuden Company Limited" [Gig] +97 24 01 79 59 72 Maxell CD-R 48X 702.83MB [Mits] +97 24 01 79 59 72 SONY CD-R 48X 702.83MB [Mits] +97 24 01 79 59 72 Taiyo Yuden [Btc] +97 24 01 79 59 72 Taiyo Yuden CD-R 52X 702.83MB [Mits] +97 24 01 79 59 72 Taiyo Yuden CD-R Multi Speed 702.83MB [Mits] +97 24 01 79 59 72 TY [Aopen] +97 24 01 79 59 73 702.83MB SONY(Taiyo Yuden) 32X [Msi] +97 24 01 79 59 73 702.83MB Taiyo Yuden 32X [Msi] +97 24 01 79 59 73 80 48x "Taiyo Yuden Company Limited" [Gig] +97 24 01 79 59 73 Taiyo Yuden [Btc] +97 24 01 79 59 73 TY,tuned [Aopen] +97 24 01 79 59 74 702.83MB FUJIFILM(Tayio Yuden) 48X [Msi] +97 24 01 79 59 74 80 16x "Taiyo Yuden Company Limited" [Gig] + +"Sony Corporation" +97 24 11 74 43 00 74 24x "Sony Corporation" [Gig] +97 24 11 79 59 11 80 24x "Sony Corporation" [Gig] +97 24 11 79 59 74 Sony [Aopen] +97 24 11 79 59 74 SONY [Btc] +97 24 12 74 43 00 74 48x "Sony Corporation" [Gig] +97 24 12 74 43 00 LeadData,Sony [Aopen] +97 24 12 79 59 74 80 32x "Sony Corporation" [Gig] +97 24 12 79 59 74 SONY [Btc] +97 24 15 74 30 00 Imation [Aopen] +97 24 15 74 43 00 654.49MB imation(Sony) 4X-10X [Msi] +97 24 15 79 59 74 702.83MB (SONY) 2-24X [Msi] +97 24 15 79 59 74 702.83MB SONY(Lead data) 48X [Msi] +97 24 15 79 59 74 80 48x "Sony Corporation" [Gig] +97 24 15 79 59 74 Sony [Aopen] +97 24 16 79 59 74 702.83MB SONY(Lead data) 52X [Msi] +97 24 16 79 59 74 80 52x "Sony Corporation" [Gig] +97 24 16 79 59 74 Sony [Aopen] +97 24 16 79 59 74 SONY [Btc] + +"Computer Support Italcard s.r.l" +97 24 26 79 59 74 80 52x "Computer Support Italcard s.r.l" [Gig] +97 24 26 79 59 74 Csita [Aopen] + +"Unitech Japan Inc." +97 24 39 79 59 74 80 52x "Unitech Japan Inc." [Gig] +97 24 39 79 59 74 RA [Aopen] + +"MPO, France" +97 25 00 79 59 74 TDK CD-R80(25) [Teac] +97 25 06 74 45 00 74 52x "MPO" [Gig] +97 25 06 74 45 00 MPO,France [Aopen] +97 25 06 79 59 74 80 52x "MPO" [Gig] +97 25 07 79 59 00 MPO,France [Aopen] + +"Hitachi Maxell Ltd." +97 25 22 79 59 74 80 16x "Hitachi Maxell Ltd." [Gig] +97 25 29 74 30 00 654.49MB Hitachi Maxell Multi Speed [Msi] +97 25 29 74 30 00 74 52x "Hitachi Maxell Ltd." [Gig] +97 25 29 74 30 00 MAXELL [Aopen] +97 25 29 74 30 00 Maxell [Btc] +97 25 29 79 59 74 702.83MB Hitachi Maxell(Acer OEM) 32X [Msi] +97 25 29 79 59 74 702.83MB Hitachi Maxell Multi Speed [Msi] +97 25 29 79 59 74 80 52x "Hitachi Maxell Ltd." [Gig] +97 25 29 79 59 74 Eigen CD-R Multi Speed 702.83MB [Mits] +97 25 29 79 59 74 Maxell [Btc] +97 25 29 79 59 74 MAXWELL [Aopen] + +"Infodisc Technology Co,Ltd." +97 25 30 24 01 05 Infodisk 8cm HS-RW 12X 210.80MB [Mits] +97 25 30 - - - 4x "Infodisc Technology Co,Ltd." [Gig] +97 25 30 74 44 07 INFODISC [Aopen] +97 25 30 79 59 72 INFODISC [Aopen] +97 25 30 79 59 73 MEMOREX [Aopen] +97 25 30 79 59 74 80 10X "Infodisc Technology Co,Ltd." [Gig] +97 25 30 79 59 74 Infodisk CD-RW 4X 702.83MB [Mits] +97 25 30 79 59 74 MEMOREX(INFODISC) [Aopen] +97 25 31 79 59 73 80 24x "Infodisc Technology Co,Ltd." [Gig] +97 25 35 79 59 74 702.83MB Infodisc 52X [Msi] +97 25 35 79 59 74 702.83MB SPEEDA(Infodisc) 40X [Msi] +97 25 35 79 59 74 80 48x "Infodisc Technology Co,Ltd." [Gig] +97 25 35 79 59 74 Lead data [Btc] + +"Xcitec" +D "Xcitec Inc." +97 25 66 79 59 73 XGITEK [Aopen] + +"Fornet International Pte Ltd" +97 26 00 4x "Fornet International Pte Ltd" [Gig] +97 26 00 79 59 74 80 12X "Fornet International Pte Ltd" [Gig] +97 26 00 79 59 74 COMPUSA,Fornet [Aopen] +97 26 01 74 56 00 74 24x "Fornet International Pte Ltd" [Gig] +97 26 01 79 59 74 80 12X "Fornet International Pte Ltd" [Gig] +97 26 06 79 59 74 80 24x "Fornet International Pte Ltd" [Gig] +97 26 07 79 59 64 90 24x "Fornet International Pte Ltd" [Gig] +97 26 07 79 59 64 Formet [Btc] +97 26 07 79 59 71 702.82MB Cdhouse (Fornet International) 52X [Msi] +97 26 07 79 59 71 80 52x "Fornet International Pte Ltd" [Gig] +97 26 07 79 59 71 Formet [Btc] +97 26 07 79 59 71 Fornet [Aopen] +97 26 07 79 59 74 Fornet [Aopen] + +"Postech Corporation" +97 26 11 22 23 60 Postech [Btc] +97 26 11 79 59 73 702.83MB Postech 52X [Msi] +97 26 11 79 59 73 80 52x "Postech Corporation" [Gig] +97 26 11 79 59 73 Postech [Aopen] +97 26 11 79 59 73 POSTECH [Aopen] +97 26 11 79 59 73 Postech [Btc] +97 26 11 79 59 73 Postech CD-R 52X 702.83MB [Mits] +97 26 11 79 59 74 702.83MB Postech corp Multi Speed [Msi] +97 26 11 79 59 74 702.83MB Postech N/A 12X [Msi] +97 26 11 79 59 74 80 12X "Postech Corporation" [Gig] +97 26 11 79 59 74 80 40x "Postech Corporation" [Gig] +97 26 11 79 59 74 Mr.Platinum [Aopen] +97 26 11 79 59 74 POSTECH [Aopen] +97 26 11 79 59 74 Postech [Btc] +97 26 15 79 59 74 80 32x "Postech Corporation" [Gig] + +"SKC Co Ltd." +97 26 21 74 59 73 74 12X "SKC Co Ltd." [Gig] +97 26 21 74 59 73 SKC74-Korea [Aopen] +97 26 21 79 59 74 80 12X "SKC Co Ltd." [Gig] +97 26 21 79 59 74 SKC80-Korea [Aopen] +97 26 26 79 59 73 702.83MB SKC 48X [Msi] +97 26 26 79 59 73 80 52x "SKC Co Ltd." [Gig] +97 26 26 79 59 73 Infinite CD-R 48X 702.83MB [Mits] +97 26 26 79 59 73 SKC [Btc] +97 26 26 79 59 73 SKC(Korea) [Aopen] + +"Fuji Photo Film Co,Ltd." +97 26 45 79 59 73 702.83MB FUJI 52X [Msi] +97 26 45 79 59 73 80 52x "Fuji Photo Film Co,Ltd." [Gig] +97 26 45 79 59 73 Fujifilm [Aopen] +97 26 45 79 59 74 80 32x "Fuji Photo Film Co,Ltd." [Gig] + +"Lead Data Inc." +97 26 50 4x "Lead Data Inc." [Gig] +97 26 50 74 59 74 SONY CD-RW 1-4X 702.83MB [Mits] +97 26 51 79 59 74 702.83MB Lead Data 12X [Msi] +97 26 51 79 59 74 80 12X "Lead Data Inc." [Gig] +97 26 51 79 59 74 Leaddata [Aopen] +97 26 52 79 59 74 80 32x "Lead Data Inc." [Gig] +97 26 53 79 59 74 702.83MB Lead Data 48X [Msi] +97 26 53 79 59 74 Gigastorage [Btc] +97 26 54 79 59 74 702.83MB Lead Data 48X [Msi] +97 26 54 79 59 74 80 48x "Lead Data Inc." [Gig] +97 26 54 79 59 74 LeadData [Aopen] +97 26 54 79 59 74 Lead data [Btc] +97 26 54 79 59 74 Lead Data CD-R 48X 702.83MB [Mits] +97 26 56 79 59 74 80 52x "Lead Data Inc." [Gig] +97 26 56 79 59 74 LeadData [Aopen] +97 26 56 79 59 74 Lead data [Btc] +97 26 57 79 59 74 80 52x "Lead Data Inc." [Gig] +97 26 57 79 59 74 MIRAGE [Aopen] + +"CMC Magnetics Corporation" +97 26 60 74 41 50 74 32x "Daxon Technology Inc." [Gig] +97 26 61 79 59 74 80 32x "CMC Magnetics Corporation" [Gig] +97 26 65 21 59 74 Verbatim 8mm CD-RW 2-4X 193.06MB [Mits] +97 26 65 24 30 00 215.04MB Memorex(CMC)8cm 4X [Msi] +97 26 65 24 30 00 Bi-Winner 8cm HS-RW 4-8X 215.04MB [Mits] +97 26 65 24 30 00 Memorex 8cm CD-RW 4X 215.04MB [Mits] +97 26 65 4x "CMC Magnetics Corporation" [Gig] +97 26 65 74 12 00 PLEXTOR [Aopen] +97 26 65 74 59 64 658.89MB CMC 16X [Msi] +97 26 65 74 59 64 CMC [Aopen] +97 26 65 75 00 00 658.89MB YAMAHA(CMC) 4-10X [Msi] +97 26 65 75 00 00 658.89MB YAMAHA(CMC) N/A 4-10X [Msi] +97 26 65 75 00 00 74 12X "CMC Magnetics Corporation" [Gig] +97 26 65 75 00 00 CMC [Aopen] +97 26 65 75 00 00 CMC CD-RW 4X 658.89MB [Mits] +97 26 65 79 59 64 CMC [Aopen] +97 26 65 79 59 74 702.83MB Melody(CMC) 10X [Msi] +97 26 65 79 59 74 702.83MB Office DEPOT(CMC) 1-4X [Msi] +97 26 65 79 59 74 702.83MB Philips(CMC) 4-12X [Msi] +97 26 65 79 59 74 702.83MB Philips(CMC) N/A 4-12X [Msi] +97 26 65 79 59 74 CMC [Aopen] +97 26 65 79 59 74 CMC CD-RW 4X 702.83MB [Mits] +97 26 65 79 59 74 Office DEPOT CD-RW 1-4X 702.83MB [Mits] +97 26 66 23 00 00 CMC [Btc] +97 26 66 23 59 74 CMC [Btc] +97 26 66 75 00 00 74 40x "CMC Magnetics Corporation" [Gig] +97 26 66 75 00 00 CMC [Aopen] +97 26 66 75 10 00 CMC [Aopen] +97 26 66 75 10 00 CMC [Btc] +97 26 66 75 10 00 CMC CD-R 52X 660.35MB [Mits] +97 26 66 79 59 71 702.83MB CMC 52X [Msi] +97 26 66 79 59 71 702.83MB eMARK(CMC) 48X [Msi] +97 26 66 79 59 71 702.83MB imation(CMC) 48X [Msi] +97 26 66 79 59 71 702.83MB imation(CMC) 48X [Msi] +97 26 66 79 59 71 80 52x "CMC Magnetics Corporation" [Gig] +97 26 66 79 59 71 CMC [Btc] +97 26 66 79 59 71 CMC CD-R 52X 702.83MB [Mits] +97 26 66 79 59 71 CMC,MEMOREX [Aopen] +97 26 66 79 59 73 702.83MB CMC 52X [Msi] +97 26 66 79 59 73 702.83MB HyperMedia(CMC) 52X [Msi] +97 26 66 79 59 73 702.83MB imation(CMC) 48X [Msi] +97 26 66 79 59 73 702.83MB imation(CMC) 48X [Msi] +97 26 66 79 59 73 702.83MB Melody (CMC) 40X [Msi] +97 26 66 79 59 73 702.83MB Melody(CMC) 40X [Msi] +97 26 66 79 59 73 702.83MB Samsung(CMC) 48X [Msi] +97 26 66 79 59 73 80 52x "CMC Magnetics Corporation" [Gig] +97 26 66 79 59 73 CMC [Aopen] +97 26 66 79 59 73 CMC [Btc] +97 26 66 79 59 73 CMC CD-R 52X 702.83MB [Mits] +97 26 66 79 59 73 Imation CD-R 48X 702.83MB [Mits] +97 26 66 79 59 73 Shintaro CD-R 40X 702.83MB [Mits] +97 26 66 79 59 73 Techworks CD-R 48X 702.83MB [Mits] +97 26 66 79 59 74 702.83MB Bi-Winner(CMC)99min 40X [Msi] +97 26 66 79 59 74 702.83MB Bi-Winner(CMC)99min 40X [Msi] +97 26 66 79 59 74 702.83MB Memorex(CMC) 48X [Msi] +97 26 66 79 59 74 702.83MB PHILIPS(CMC) 40X [Msi] +97 26 66 79 59 74 702.83MB PHILIPS(CMC) Multi Speed [Msi] +97 26 66 79 59 74 80 16x "CMC Magnetics Corporation" [Gig] +97 26 66 79 59 74 CMC [Aopen] +97 26 66 79 59 74 CMC [Btc] +97 26 67 75 00 00 658.89MB CMC 24X [Msi] +97 26 67 75 00 00 74 24x "CMC Magnetics Corporation" [Gig] +97 26 67 75 00 00 CMC US-RW 24X 658.89MB [Mits] +97 26 67 79 59 64 80 32X "CMC Magnetics Corporation" [Gig] + +"Ricoh Company Limited" +D "DIGITAL STORAGE TECHNOLOGY CO.,LTD" +97 27 00 4x "Ricoh Company Limited" [Gig] +97 27 00 74 12 00 651.86MB RICOH N/A 4-10X [Msi] +97 27 00 74 12 00 651.86MB Sony(Digital Storage) 4X-10X [Msi] +97 27 00 74 12 00 651.86MB Sony(Digital Storage) 4X-10X [Msi] +97 27 00 74 12 00 74 10X "Ricoh Company Limited" [Gig] +97 27 00 74 12 00 74 24x "Ricoh Company Limited" [Gig] +97 27 00 74 12 00 RICOH [Aopen] +97 27 00 74 12 00 Ricoh HS-RW 4X-10X 651.86MB [Mits] +97 27 00 74 12 00 Sony HS-RW 4X-10X 651.86MB [Mits] +97 27 00 75 00 00 74 32x "Digital Storage Techology" [Gig] +97 27 00 79 59 74 702.83MB Laser 48X [Msi] +97 27 00 79 59 74 80 40x "Digital Storage Techology" [Gig] +97 27 00 79 59 74 Csita [Aopen] +97 27 00 79 59 74 RICOH [Aopen] +97 27 06 79 59 72 Digital Storage CD-R 52X 53.318MB [Mits] +97 27 06 79 59 72 DST [Aopen] +97 27 06 79 59 73 702.83MB Digital Storage 32X [Msi] +97 27 06 79 59 73 702.83MB Digital Storage 52X [Msi] +97 27 06 79 59 73 DST [Btc] +97 27 06 79 59 74 80 40x "Digital Storage Techology" [Gig] +97 27 06 79 59 74 DST [Aopen] + +"Plasmon Data systems Ltd" +97 27 10 21 30 00 188.67MB Plasmon 4-10X [Msi] +97 27 10 21 30 00 Ritek 8cm HS-RW 4-10X 188.67MB [Mits] +97 27 10 4x "Ritek Corp" [Gig] +97 27 10 74 41 00 656.10MB TDK(Plasmon) 4X-10X [Msi] +97 27 10 74 41 00 74 12X "Ritek Corp" [Gig] +97 27 10 74 41 00 EMTEC CD-RW 1-4X 656.10MB [Mits] +97 27 10 74 41 00 Ritek [Aopen] +97 27 10 74 41 00 TDK HS-RW 4-10X 651.86MB [Mits] +97 27 11 74 41 00 Ritek [Aopen] +97 27 11 74 41 01 656.10MB Plasmon 16X [Msi] +97 27 12 74 41 00 656.10MB RiDATA(Plasmon) 24X [Msi] +97 27 12 74 41 00 656.10MB Riteck co. 24X [Msi] +97 27 12 74 41 00 74 24x "Ritek Corp" [Gig] +97 27 12 74 41 00 Plasmon US-RW 24X 656.10MB [Mits] +97 27 18 79 59 74 702.83MB ALPHAPET(Plasmon)() 1-40X [Msi] +97 27 18 79 59 74 702.83MB MANIA(Plasmon)() 48X [Msi] +97 27 18 79 59 74 80 52x "Plasmon Data systems Ltd" [Gig] +97 27 18 79 59 74 Plasmon [Aopen] +97 27 18 79 59 74 Plasmon [Btc] +97 27 19 79 59 74 4M [Aopen] +97 27 19 79 59 74 80 48x "Plasmon Data systems Ltd" [Gig] + +"Princo Corporation" +97 27 21 79 59 74 Princo Co CD-RW 4X 702.83MB [Mits] +97 27 28 4x "Princo Corporation" [Gig] +97 27 28 74 50 01 74 40x "Princo Corporation" [Gig] +97 27 28 74 50 01 Princo [Aopen] +97 27 28 79 59 74 702.83MB Princo 2-48X [Msi] +97 27 28 79 59 74 702.83MB Princo 52X [Msi] +97 27 28 79 59 74 80 52x "Princo Corporation" [Gig] +97 27 28 79 59 74 Princo [Aopen] +97 27 28 79 59 74 Princo [Btc] +97 27 29 79 59 74 80 12X "Princo Corporation" [Gig] +97 27 29 79 59 74 Princo [Aopen] +97 27 29 79 59 74 Princo Co HS-RW 4-12X 702.83MB [Mits] + +"Pioneer" +97 27 30 PIONEER [Hij] + +"Eastman Kodak Company" +97 27 45 74 05 01 74 32x "Eastman Kodak Company" [Gig] + +"Mitsui Chemicals Inc." +97 27 55 74 05 10 74 16x "Mitsui Chemicals Inc." [Gig] +97 27 56 74 05 11 74 16x "Mitsui Chemicals Inc." [Gig] +97 27 56 79 59 74 80 24x "Mitsui Chemicals Inc." [Gig] +97 27 57 79 59 74 80 40x "Mitsui Chemicals Inc." [Gig] +97 27 57 79 59 74 MITSUI [Aopen] +97 27 58 74 05 13 MAM-A [Aopen] +97 27 58 79 59 74 80 52x "Mitsui Chemicals Inc." [Gig] +97 27 58 79 59 74 Mitsui [Aopen] +97 27 58 79 59 74 TDK [Btc] + +"Ricoh Company Limited" +97 27 66 74 12 00 RICOH [Btc] +97 27 66 74 12 02 74 48x "Ricoh Company Limited" [Gig] +97 27 66 74 12 02 Ricoh [Aopen] +97 27 66 74 12 03 74 52x "Ricoh Company Limited" [Gig] +97 27 66 74 12 03 Ritek [Btc] +97 27 66 74 12 03 Ritek,RS [Aopen] +97 27 66 79 59 71 702.82MB RICOH 40X [Msi] +97 27 66 79 59 71 80 48x "Ricoh Company Limited" [Gig] +97 27 66 79 59 71 RICOH [Btc] +97 27 66 79 59 71 RICOH CD-R 40X 702.82MB [Mits] +97 27 66 79 59 71 Ritek,RS [Aopen] +97 27 66 79 59 72 80 48x "Ricoh Company Limited" [Gig] +97 27 66 79 59 72 Ricoh [Aopen] +97 27 66 79 59 74 RICOH [Btc] + +"Gigastorage Corporation" +97 28 12 79 59 72 702.83MB Gigastorage 52X [Msi] +97 28 12 79 59 72 80 52x "Gigastorage Corporation" [Gig] +97 28 12 79 59 72 GigaSto [Aopen] +97 28 12 79 59 74 702.83MB Gigastorage 40X [Msi] +97 28 12 79 59 74 80 48x "Gigastorage Corporation" [Gig] +97 28 12 79 59 74 GigaSto, MaxMax [Aopen] +97 28 12 79 59 74 Gigastorage [Btc] +97 28 15 4x "Gigastorage Corporation" [Gig] +97 28 15 4x "Nan-Ya" [Gig] +97 28 15 74 12 00 74 10X "Gigastorage Corporation" [Gig] +97 28 15 74 12 00 CURSOR [Aopen] +97 28 15 79 59 72 702.83MB Gigastorage 52X [Msi] +97 28 15 79 59 72 702.83MB MaxMax (Gigastorage) 40X [Msi] +97 28 15 79 59 72 GigaSto [Aopen] +97 28 15 79 59 72 Gigastorage [Btc] +97 28 15 79 59 74 702.83MB Gigastorage 40X [Msi] +97 28 15 79 59 74 80 48x "Gigastorage Corporation" [Gig] +97 28 15 79 59 74 80 52x "Gigastorage Corporation" [Gig] +97 28 15 79 59 74 GigaSto [Aopen] +97 28 15 79 59 74 Gigastorage [Btc] +97 28 15 79 59 74 Gigastorage HS-RW 4X-8X 702.83MB [Mits] + +"Multi Media Masters&Machinary SA" +97 28 22 79 59 67 80 24x "Multi Media Masters&Machinary SA" [Gig] +97 28 22 79 59 67 King [Aopen] +97 28 22 79 59 67 Multi Media CD-R 48X 702.82MB [Mits] +97 28 26 79 59 74 702.83MB Mmirex(Multi Media) 24X [Msi] +97 28 26 79 59 74 80 52x "Multi Media Masters&Machinary SA" [Gig] +97 28 26 79 59 74 KingPro [Aopen] + +"Ritek Corp" +97 31 01 74 45 00 74 24x "Ritek Corp" [Gig] +97 31 01 74 45 00 Ritek [Btc] +97 31 01 74 45 01 656.68MB Ritek Co. 40X [Msi] +97 31 01 74 45 01 656.68MB Ritek Co. 52X [Msi] +97 31 01 74 45 01 74 52x "Ritek Corp" [Gig] +97 31 01 74 45 01 Ritek,74 [Aopen] +97 31 01 74 45 01 Ritek [Btc] +97 31 07 21 15 01 Ritek [Btc] +97 31 07 74 45 00 Ritek [Btc] +97 31 07 74 45 01 656.69MB Ritek Co. 48X [Msi] +97 31 07 74 45 01 656.69MB TDK(Ritek) 40X [Msi] +97 31 07 74 45 01 74 52x "Ritek Corp" [Gig] +97 31 07 74 45 01 Ritek [Aopen] +97 31 07 74 45 01 Ritek [Btc] +97 31 07 74 45 02 Ritek [Btc] + +"Grand Advance Technology Sdn. Bhd." +97 31 35 79 59 74 GAT [Aopen] + +"TDK Corporation" +97 32 00 74 59 00 74 16x "TDK Corporation" [Gig] +97 32 01 74 59 74 74 24X "TDK Corporation" [Gig] +97 32 05 74 59 00 74 52X "TDK Corporation" [Gig] +97 32 05 74 59 00 TDK [Aopen] + +"Prodisc Technology Inc." +97 32 10 79 59 74 702.83MB Prodisc 12X [Msi] +97 32 10 79 59 74 702.83MB Smartbuy(Prodisc) 8-12X [Msi] +97 32 10 79 59 74 702.83MB Smartbuy(Prodisc) N/A 8-12X [Msi] +97 32 10 79 59 74 80 12X "Prodisc Technology Inc." [Gig] +97 32 10 79 59 74 PRODISC [Aopen] +97 32 10 79 59 74 Smartbuy HS-RW 8-12X 702.83MB [Mits] +97 32 11 22 00 00 Prodisc CD-RW 1-4X 193.07MB [Mits] +97 32 11 4x "Prodisc Technology Inc." [Gig] +97 32 11 79 59 74 Prodisc CD-RW 1X-4X 702.83MB [Mits] +97 32 12 79 59 74 80 10X "Prodisc Technology Inc." [Gig] +97 32 12 79 59 74 PRODISC [Aopen] +97 32 13 79 59 74 (PRODISC) [Aopen] +97 32 19 22 00 00 Prodisc [Btc] +97 32 19 74 30 01 74 52x "Prodisc Technology Inc." [Gig] +97 32 19 74 30 01 Prodisc [Aopen] +97 32 19 74 30 01 Prodisc [Btc] +97 32 19 79 59 71 702.83MB Mitsubishi (Prodisc) 52X [Msi] +97 32 19 79 59 71 702.83MB Prodise 52X [Msi] +97 32 19 79 59 71 80 52x "Prodisc Technology Inc." [Gig] +97 32 19 79 59 71 Prodisc [Aopen] +97 32 19 79 59 71 Prodisc [Btc] +97 32 19 79 59 72 654.49MB Prodise 48X [Msi] +97 32 19 79 59 72 654.49MB Prodise 48X [Msi] +97 32 19 79 59 72 702.83MB Digmaster(Prodisc) 40X [Msi] +97 32 19 79 59 72 702.83MB Digmaster(Prodisc) 48X [Msi] +97 32 19 79 59 72 702.83MB LG(Prodisc) 1-48X [Msi] +97 32 19 79 59 72 702.83MB Mitsubishi (Prodisc) Multi Speed [Msi] +97 32 19 79 59 72 702.83MB Prodise 48X [Msi] +97 32 19 79 59 72 80 52x "Prodisc Technology Inc." [Gig] +97 32 19 79 59 72 Mitsubishi CD-R Multi Speed 702.83MB [Mits] +97 32 19 79 59 72 Prodisc [Aopen] +97 32 19 79 59 72 Prodisc [Btc] +97 32 19 79 59 73 654.49MB Prodisc 40X [Msi] +97 32 19 79 59 73 702.83MB Media Market(Prodisc) 1-40X [Msi] +97 32 19 79 59 73 80 48x "Prodisc Technology Inc." [Gig] +97 32 19 79 59 73 Prodisc [Aopen] +97 32 19 79 59 73 Prodisc [Btc] +97 32 19 79 59 74 80 48x "Prodisc Technology Inc." [Gig] +97 32 19 79 59 74 Prodisc [Aopen] +97 32 19 79 59 74 Prodisc [Btc] + +"Mitsubishi Chemical Corporation" +97 34 21 74 43 00 74 24x "Mitsubishi Chemical Corporation" [Gig] +97 34 21 74 43 00 MCC [Btc] +97 34 21 79 59 74 702.83MB Mitsubishi Multi Speed [Msi] +97 34 21 79 59 74 MCC [Btc] +97 34 21 79 59 74 MCC(Metal [Aopen] +97 34 22 4x "Mitsubishi Chemical Corporation" [Gig] +97 34 22 74 43 00 656.40MB Mitsubishi N/A 1-4X [Msi] +97 34 22 74 43 00 74 32x "Mitsubishi Chemical Corporation" [Gig] +97 34 22 74 43 00 MCC [Btc] +97 34 22 74 43 00 Mitsubishi CD-RW 1-4X 656.40MB [Mits] +97 34 22 79 59 74 702.83MB Mitsubishi 32X [Msi] +97 34 22 79 59 74 80 32x "Mitsubishi Chemical Corporation" [Gig] +97 34 22 79 59 74 MCC [Btc] +97 34 22 79 59 74 Mitsubishi CD-RW 1-4X 702.83MB [Mits] +97 34 23 74 43 00 656.40MB Mitsubishi 4-10X [Msi] +97 34 23 74 43 00 74 10X "Mitsubishi Chemical Corporation" [Gig] +97 34 23 74 43 00 MCC [Aopen] +97 34 23 74 43 00 Mitsubishi HS-RW 4-10X 656.40MB [Mits] +97 34 23 74 43 00 Mitsubishi HS-RW 4-12X 656.40MB [Mits] +97 34 23 74 43 00 Verbatim HS-RW 4-12X 656.40MB [Mits] +97 34 23 74 43 01 656.40MB Mitsubishi 52X [Msi] +97 34 23 74 43 01 MCC [Aopen] +97 34 23 74 43 01 MCC [Btc] +97 34 23 74 43 01 Mitsubishi CD-R 52X 656.40MB [Mits] +97 34 23 79 59 73 702.83MB Mitsubishi 52X [Msi] +97 34 23 79 59 73 702.83MB Mitsubishi 52X [Msi] +97 34 23 79 59 73 702.83MB Mitsubishi [Msi] +97 34 23 79 59 73 702.83MB Verbatim(Mitsubishi) 48X [Msi] +97 34 23 79 59 73 702.83MB Verbatim(Mitsubishi) 48X [Msi] +97 34 23 79 59 73 702.83MB YAMAHA(Mitsubishi) 48X [Msi] +97 34 23 79 59 73 MCC [Aopen] +97 34 23 79 59 73 MCC [Btc] +97 34 23 79 59 73 Mitsubishi CD-R 52X 702.83MB [Mits] +97 34 23 79 59 73 Mitsubishi CD-R 52X 702.83MB [Mits] +97 34 23 79 59 74 702.83MB Mitsubishi 40X [Msi] +97 34 23 79 59 74 702.83MB Verbatim(Mitsubishi) 40X [Msi] +97 34 23 79 59 74 702.83MB Verbatim(Mitsubishi) N/A 4-10X [Msi] +97 34 23 79 59 74 80 48x "Mitsubishi Chemical Corporation" [Gig] +97 34 23 79 59 74 MCC [Aopen] +97 34 23 79 59 74 MCC [Aopen] +97 34 23 79 59 74 MCC [Btc] +97 34 23 79 59 74 Mitsubishi HS-RW 12X 702.83MB [Mits] +97 34 23 79 59 74 Verbatim HS-RW 4-10X 702.83MB [Mits] +97 34 23 79 59 76 80 52x "Mitsubishi Chemical Corporation" [Gig] +97 34 24 74 43 00 656.40MB Mitsubishi 24X [Msi] +97 34 24 74 43 00 74 24x "Mitsubishi Chemical Corporation" [Gig] +97 34 24 74 43 00 Mitsubishi US-RW 24X 656.40MB [Mits] +97 34 24 79 59 74 702.83MB Mitsubishi 24X [Msi] +97 34 24 79 59 74 Mitsubishi US-RW 24X 702.83MB [Mits] +97 34 24 79 59 74 Verbatim US-RW 24X 702.83MB [Mits] +97 34 25 74 43 00 656.40MB Mitsubishi(standard) 32X [Msi] +97 34 25 74 43 00 74 32x "Mitsubishi Chemical Corporation" [Gig] + +"Mitsui Chemicals Inc." +97 48 55 63 04 00 MCI(Mitsui) MTCDR63G [Teac] +97 48 55 63 04 00 MCI(MITSUI) MTCDR63G [Edig] + +"TDK Corporation" +97 49 00 63 52 00 TDK CD-R63S [Teac] +97 49 00 63 52 00 TDK CD-R63S [Edig] + + +------------------------------------------------------------------------------- + + DVD and BD Media IDs + +"UML" +AML 003 UME 8X [Hij] + +"BeAll Developers, Inc." +BeAll000 P40 BeAll 4X [Hij] +BeAll G40001 BeAll 4X [Hij] + +"CMC Magnetics Corporation" +CMC MAG M01 CMC 16X [Hij] +CMC MAG F01 CMC 4X [Hij] +CMC MAG R01 CMC 2.5X [Hij] +CMC MAG W01 CMC 2.5x [Hij] +CMC MAG. AE1 CMC 8X [Hij] +CMC MAG. AF1 CMC 4X [Hij] +CMC MAG. AM1 CMC 12X [Hij] +CMCW02 CMC 2x [Hij] +CMC00RG200 CMC 2X [Hij] +CMCMAG CMC 12X [Hij] +CMCMAG BA2 CMC Magnetics Corporation 1-2X HTL 25GB (12cm) [Blu] +CMCMAG BA3 CMC Magnetics Corporation 1-4X HTL 25GB (12cm) [Blu] +CMCMAG BA5 CMC Magnetics Corporation 1-6X HTL 25GB (12cm) [Blu] +CMCMAG CN2 CMC Magnetics Corporation 1-2X HTL 25GB(12cm) [Blu] + +"Daxon Technology Inc. / Acer" +DAXON 016 DAXON 8X [Hij] +DAXON AZ3 DAXON 16X [Hij] +DAXON CY3 DAXON 12X [Hij] +DAXON D42/52 (user reported DVD+RW) +Daxon R2X Daxon Technology Inc. 1-2X HTL 25GB(12cm) [Blu] +Daxon R4X Daxon Technology Inc. 1-4X HTL 25GB(12cm) [Blu] + +"Fujifilm Holdings Corporation" +FUJI Fujifilm Corporation [Blu] +FUJIFILM02 FUJIFILM 4X [Hij] +FUJIFILM03 FUJIFILM 8X [Hij] +FUJIFILM04 FUJIFILM 12X [Hij] + +"New Star Digital Co., Ltd." +INFODISC R20 NSD 8X [Hij] +INFODISC-R20 NSD 8X [Hij] + +"InfoMedia Inc." +INFOME E20 INFOMEDIA INC. 1-2X [Blu] +INFOME R30 INFOMEDIA 16X [Hij] +INFOME R20 INFOMEDIA INC. 1-2X [Blu] +INFOME R30 INFOMEDIA INC. 1-4X [Blu] + +"Info Source Multi Media Ltd." +ISMMBD R01 Info Source Multi Media Ltd. 1-4X HTL 12cm [Blu] +ISMMBD R02 Info Source Multi Media Ltd. 1-6X HTL 12cm [Blu] +ISMMBD RE1 Info Source Multi Media Ltd. 1-2X HTL 12cm [Blu] + +"JVC Limited" +JVC0VictorD7 JVC 4X [Hij] +JVC1Victord7 JVC 6X [Hij] +JVC/VictorT7 JVC 1x [Hij] +JVC_VictorW7 JVC 2x [Hij] +JVCRE1 Victor Company of Japan, Limited [Blu] + +"AMC" +KIC01RG160 AMC 12X [Hij] + +"Lead Data Inc." +LD M04 LEADDATA 8X [Hij] +LD S04 LEADDATA 8X [Hij] +LEADDATA01 LEADDATA 4X [Hij] + +"LG Electronics" +LGE04 LG 2X [Hij] +LGEBRA S04 LG Electronics Inc. 1-4X HTL 12cm (25GB) [Blu] +LGEBRA S06 LG Electronics Inc. 1-6X HTL 12cm (25GB) [Blu] +LGEBRE S01 LG Electronics, Inc. 1-2X HTL 12cm(25GB) [Blu] +LGEP16 001 LGE 8X [Hij] + +"Mitsui Advanced Media, Inc. Europe" +MAM M02 MAM-E 8X [Hij] + +"Hitachi Maxell Ltd." +MAXELL 001 HITACHI MAXELL 4X [Hij] +MAXELL 002 HITACHI MAXELL 8X [Hij] +MAXELL 003 HITACHI MAXELL 16X [Hij] +MAXELL ES1 Hitachi Maxell, Ltd. 1-2X HTL 12cm and 8cm [Blu] +MAXELL RS1 Hitachi Maxell, Ltd. 1-2X HTL 12cm and 8cm [Blu] + +"Moser Baer India Limited" +MBI E02 Moser Baer India Ltd 1-2X HTL 25GB [Blu] +MBI F01 Moser Baer India Ltd 1-2X LTH 25GB [Blu] +MBI F02 Moser Baer India Ltd 1-4X LTH 25GB [Blu] +MBI R02 Moser Baer India Ltd 1-2X HTL 25GB [Blu] +MBI R04 Moser Baer India Ltd 1-4X HTL 25GB [Blu] +MBI R06 Moser Baer India Ltd 1-6X HTL 25GB [Blu] +MBI 01RG20 Moser Bear India 4X [Hij] +MBI 01RG40 MBI 16X [Hij] +MBI 03RG30 MBI 8X [Hij] +MBI 03RG40 MBI 12X [Hij] +MBIPG101 Moser Bear India 4X [Hij] +MBIPG101 R04 MBI 8X [Hij] +MBIPG101 R05 MBI 16X [Hij] + +"Mitsubishi Chemical Corporation" +MCC 002 MKM 4X [Hij] +MCC 003 MKM 8X [Hij] +MCC 00RG200 MKM 2X [Hij] +MCC 01RG20 MKM 4X [Hij] +MCC 01RW11n9 MKM 2x [Hij] +MCC 01RW4X MKM 4X [Hij] +MCC 02RG20 MKM 8X [Hij] +MCC 03RG20 MKM 16X [Hij] +MCC A01 MKM 2.5x [Hij] + +"Mitsui Chemicals Inc." +MCI G01 Mitsui 2X [Hij] + +"Panasonic Corporation" +MEI RA1 Panasonic Corporation 1-6X HTL 12cm [Blu] +MEI RB1 Panasonic Corporation 1-6X HTL 12cm [Blu] +MEI T01 Panasonic Corporation 1-2X HTL 12cm [Blu] +MEI T02 Panasonic Corporation 1-4X HTL 12cm [Blu] +MEI 00V001 MATSUSHITA EI 2X [Hij] +MEI 00V002 MATSUSHITA EI 4X [Hij] + +"Mitsubishi Kagaku Media Co." +MKM 001 MKM 6X [Hij] +MKM 003 MKM 8X [Hij] +MKM A02 MKM 4x [Hij] +MKM 01RD30 MKM 6X [Hij] +MKM01RW6X01 MKM 6X [Hij] + +"Mitsubishi Kagaku Media Co." +MMC 001 MKM 2.5X [Hij] + +"Hitachi Maxell Ltd." +MXL RG01 MAXELL 2X [Hij] +MXL RG02 MAXELL 4X [Hij] +MXL RG03 MAXELL 8X [Hij] +MXL RG04 MAXELL 16X [Hij] + +"Nan-Ya Plastics Corporation" +NANYA ALX NANYA 8X [Hij] + +"NESA International Inc." +NSDR40 NSD 8X [Hij] + +"Optodisc Technology Corporation" +OPTODISC F16 OPTODISC 12X [Hij] +OPTODISC OP1 OPTODISC 2.5x [Hij] +OPTODISC OP1 OPTODISC 2.5X [Hij] +OPTODISC OR8 OPTODISC 8X [Hij] +OPTODISCR004 OPTODISC 4X [Hij] +OPTODISCR008 OPTODISC 8X [Hij] +OPTODISC R01 OPTODISC 16X [Hij] +OPTODISC R02 OPTODISC 4X [Hij] +OPTODISCR016 OPTODISC 8X [Hij] + +"Optodisc Technology Corporation" +OTCBDR 001 Optodisc Technology Corporation 1-4X HTL 25GB (12cm) [Blu] +OTCBDR 002 Optodisc Technology Corporation 1-6X HTL 25GB (12cm) [Blu] +OTCBRE 001 Optodisc Technology Corporation 1-2X HTL 25GB (12cm) [Blu] + +"Moser Baer India Limited" +PHILIP R02 Moser Baer India Ltd 1-2X HTL 25GB [Blu] +PHILIP R04 Moser Baer India Ltd 1-4X HTL 25GB [Blu] +PHILIP W02 Moser Baer India Ltd 1-2X HTL 25GB [Blu] + +"Philips" +PHILIPS C16 PHILIPS 12X [Hij] +PHILIPS CD2 CMC (PHILIPS) 2.5X [Hij] + +"Princo Corporation" +PRINCO PRONCO 4X [Hij] + +"Prodisc Technology Inc." +ProdiscF02 PRODISC 12X [Hij] +PRODISC R01 PRODISC 2.5X [Hij] +Prodisc R02 PRODISC 4X [Hij] +Prodisc R03 PRODISC 8X [Hij] +Prodisc R04 PRODISC 16X [Hij] +Prodisc R05 PRODISC 16X [Hij] +ProdiscS03 PRODISC 4X [Hij] +ProdiscS04 PRODISC 8X [Hij] +ProdiscS05 PRODISC 16X [Hij] +PRODISC W01 PRODISC 2.5x [Hij] + +"Pioneer" +PVC001001 PIONEER VIDEO 2X [Hij] +PVCR001002 PIONEER VIDEO 4X [Hij] +PVCW00D002K9 PVC 2x [Hij] + +"Ricoh Company Limited" +RICOHJPN D00 RICOH 2.5X [Hij] +RICOHJPN D01 RICOH 2.5X [Hij] +RICOHJPN R00 RICOHJPN 2.5X [Hij] +RICOHJPN R01 RICOH 4X [Hij] +RICOHJPN R02 RICOH 12X [Hij] +RICOHJPN R03 RICOH 16X [Hij] +RICOHJPN W01 RICOH 2.5x [Hij] +RICOHJPN W11 RICOH 4x [Hij] +RICOHJPN W21 RICOH 8X [Hij] + +"Ritek Corp" +RITEK 001 Ritek 2.5x [Hij] +RITEK BR1 RITEK CORPORATION 1-2X HTL 25GB [Blu] +RITEK BR2 RITEK CORPORATION 1-4X HTL 25GB [Blu] +RITEK BR3 RITEK CORPORATION 1-6X HTL 25GB [Blu] +RITEK D01 RITEK 2.5X [Hij] +RITEK DR2 RITEK CORPORATION 1-4X HTL 50GB [Blu] +RITEK DW1 RITEK CORPORATION 1-2X HTL 50GB [Blu] +RITEK F16 RITEK 8X [Hij] +RITEKF1 RITEK 12X [Hij] +RITEK G03 RITEK 2X [Hij] +RITEK G04 RITEK 4X [Hij] +RITEKG05 RITEK 4X [Hij] +RITEKG06 RITEK 8X [Hij] +RITEK M02 RITEK 4X [Hij] +RITEKM16 RITEK 12X [Hij] +RITEK R01 RITEK 2.5X [Hij] +RITEK R02 RITEK 4X [Hij] +RITEK R03 RITEK 8X [Hij] +RITEK R04 RITEK 12X [Hij] +RITEK R05 RITEK 16X [Hij] +RITEKW01 RiTEK 2x [Hij] +RITEKW06 RITEK 6X [Hij] + +"Sony Corporation" +SONY SONY 2X [Hij] +SONY D11 SONY 8X [Hij] +SONY D21 SONY 16X [Hij] +SONY ED4 Sony Corporation 1-2X HTL 50GB [Blu] +SONY ES1 Sony Corporation 1-2X 25GB [Blu] +SONY NO1 Sony Corporation 1-2X HTL 25GB [Blu] +SONY NS1 Sony Corporation 1-2X HTL 25GB [Blu] +SONY NS2 Sony Corporation 1-4X HTL 25GB [Blu] +SONY NN1 Sony Corporation 1-2X HTL 25GB [Blu] +SONY NN2 Sony Corporation 1-4X HTL 25GB [Blu] +SONY NN3 Sony Corporation 1-6X HTL 25GB [Blu] +SONY04D1 SONY 4X [Hij] +SONY08D1 SONY 12X [Hij] +SONY16D1 SONY 16X [Hij] + +"TDK Corporation" +TDK 001 TDK 4X [Hij] +TDK 002 TDK 8X [Hij] +TDK 003 TDK 16X [Hij] +TDKBLD RBA TDK Corporation 1-2X HTL 12cm [Blu] +TDKBLD RBB TDK Corporation 1-4X HTL 12cm [Blu] +TDKBLD RBD TDK Corporation 1-6x HTL 12cm (25GB) [Blu] +TDKBLD RDA TDK Corporation 1-2X HTL 8cm [Blu] +TDKBLD RFA TDK Corporation 1-2X HTL 12cm [Blu] +TDKBLD RFB TDK Corporation 1-4X HTL 12cm [Blu] +TDKBLD RFD TDK Corporation 1-6x HTL 12cm (50GB) [Blu] +TDKBLD WBA TDK Corporation 1-2X HTL 12cm [Blu] +TDKBLD WDA TDK Corporation 1-2X HTL 8cm [Blu] +TDKBLD Wfa TDK Corporation 1-2X HTL 12cm [Blu] +TDK502sakuM3 TDK 2x [Hij] +TDK601saku TDK 4X [Hij] +TDKG02000000 TDK 2X [Hij] + +"TDK Corporation" +TTG01 TDK 4X [Hij] +TTG02 TDK 8X [Hij] +TTH01 TDK 8X [Hij] +TTH02 TDK 12X [Hij] + +"Taiyo Yuden Company Limited" +TYG01 TAIYO YUDEN 4X [Hij] +TYG02 TAIYO YUDEN 12X [Hij] +TYG03 TAIYO YUDEN 16X [Hij] +TYG11 TAIYO YUDEN DVD-R DL 8x +TYG-BD Y01 TAIYO YUDEN Co., Ltd. 1-2X LTH [Blu] +TYG-BD Y03 TAIYO YUDEN Co., Ltd. 1-4X LTH [Blu] + +"UmeDisc Limited" +UMEDISC DL1 Elite DVD+R DL [User report feb 2012] + +"Unifino Inc." +UTJR001001 UNIFINO 4X [Hij] + +"Mitsubishi Kagaku Media Co." +VERBAT IM0 Mitsubishi Kagaku Media, Co., Ltd. 1-2X 25GB (12cm) and 7.8GB (8cm) [Blu] +VERBAT IM1 Mitsubishi kagaku Media, Co., ltd. 1-2X HTL 50GB(12cm) [Blu] +VERBAT IMa Mitsubishi Kagaku Media, Co., Ltd. 1-2X HTL 25GB (12cm) and 7.8GB (8cm) [Blu] +VERBAT IMb Mitsubishi Kagaku Media, Co., Ltd. 1-2X HTL 50GB (12cm) and 15.6GB (8cm) [Blu] +VERBAT IMc Mitsubishi Kagaku Media, Co., Ltd. 1-4X HTL 25GB (12cm) and 7.8GB (8cm) [Blu] +VERBAT IMd Mitsubishi Kagaku Media, Co., Ltd. 1-4X HTL 50GB (12cm) [Blu] +VERBAT IMe Mitsubishi Kagaku Media, Co., Ltd. 1-6X HTL 25GB (12cm) [Blu] +VERBAT IMf Mitsubishi Kagaku Media, Co., Ltd. 1-6X HTL 50GB (12cm) [Blu] +VERBAT IMu Mitsubishi Kagaku Media, Co., Ltd. 1-6X LTH 25GB (12cm) [Blu] +VERBAT IMv Mitsubishi Kagaku Media, Co., Ltd. 1-4X LTH 25GB (12cm) [Blu] +VERBAT IMw Mitsubishi Kagaku Media, Co., Ltd. 1-2X LTH 25GB (12cm) [Blu] + +"Taiyo Yuden Company Limited" +YUDEN000 T01 TAIYO YUDEN 4X [Hij] +YUDEN000 T02 TAIYO YUDEN 12x [Hij] +YUDEN000 T03 TAIYO YUDEN 16X [Hij] + diff --git a/trunk/libburn-1.pc.in b/trunk/libburn-1.pc.in new file mode 100644 index 0000000..e3bf784 --- /dev/null +++ b/trunk/libburn-1.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libburn +Description: Library to read/write optical discs +Version: @VERSION@ +Requires: +Libs: -L${libdir} -lburn +Libs.private: @THREAD_LIBS@ @LIBBURN_ARCH_LIBS@ +Cflags: -I${includedir}/libburn diff --git a/trunk/libburn/Makefile b/trunk/libburn/Makefile new file mode 100644 index 0000000..062350d --- /dev/null +++ b/trunk/libburn/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/trunk/libburn/Makefile.am b/trunk/libburn/Makefile.am new file mode 100644 index 0000000..144ece7 --- /dev/null +++ b/trunk/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/trunk/libburn/asserts.txt b/trunk/libburn/asserts.txt new file mode 100644 index 0000000..b079158 --- /dev/null +++ b/trunk/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/trunk/libburn/async.c b/trunk/libburn/async.c new file mode 100644 index 0000000..7022726 --- /dev/null +++ b/trunk/libburn/async.c @@ -0,0 +1,796 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +/* 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 +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#include "libburn.h" +#include "transport.h" +#include "drive.h" +#include "write.h" +#include "options.h" +#include "file.h" +#include "async.h" +#include "init.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 <signal.h> + +/* +#include <a ssert.h> +*/ +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + +/* ts A80714 : introduced type codes for the worker list */ +#define Burnworker_type_scaN 0 +#define Burnworker_type_erasE 1 +#define Burnworker_type_formaT 2 +#define Burnworker_type_writE 3 +#define Burnworker_type_fifO 4 + +#define SCAN_GOING() (workers != NULL && \ + workers->w_type == Burnworker_type_scaN) + +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; +}; + +union w_list_data +{ + struct scan_opts scan; + struct erase_opts erase; + struct format_opts format; + struct write_opts write; + struct fifo_opts fifo; +}; + +struct w_list +{ + /* ts A80714 */ + int w_type; /* see above define Burnworker_type_* */ + + struct burn_drive *drive; + pthread_t thread; + + struct w_list *next; + + union w_list_data 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(int w_type, struct burn_drive *d, + WorkerFunc f, union w_list_data *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 = calloc(1, sizeof(struct w_list)); + a->w_type = w_type; + a->drive = d; + + a->u = *data; +/* + memcpy(&(a->u), data, sizeof(union w_list_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; + + 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. + */ + pthread_detach(th); +/* + int ret; + char msg[80]; + + 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; +} + +static void reset_progress(struct burn_drive *d, int sessions, int tracks, + int indices, int sectors, int flag) +{ + /* reset the progress indicator */ + d->progress.session = 0; + d->progress.sessions = sessions; + d->progress.track = 0; + d->progress.tracks = tracks; + d->progress.index = 0; + d->progress.indices = indices; + d->progress.start_sector = 0; + d->progress.sectors = sectors; + d->progress.sector = 0; +} + + +int burn_drive_scan(struct burn_drive_info *drives[], unsigned int *n_drives) +{ + union w_list_data 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.scan.drives = drives; + o.scan.n_drives = n_drives; + o.scan.done = 0; + add_worker(Burnworker_type_scaN, 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) +{ + +#define Libburn_protect_erase_threaD 1 + +#ifdef Libburn_protect_erase_threaD + sigset_t sigset, oldset; + + /* Protect blank thread from being interrupted by external signals */ + sigfillset(&sigset); + sigdelset(&sigset, SIGSEGV); + sigdelset(&sigset, SIGILL); + pthread_sigmask(SIG_SETMASK, &sigset, &oldset); +#endif /* Libburn_protect_erase_threaD */ + + burn_disc_erase_sync(w->u.erase.drive, w->u.erase.fast); + remove_worker(pthread_self()); + +#ifdef Libburn_protect_erase_threaD + /* (just in case it would not end with all signals blocked) */ + pthread_sigmask(SIG_SETMASK, &oldset, NULL); +#endif /* Libburn_protect_erase_threaD */ + + return NULL; +} + +void burn_disc_erase(struct burn_drive *drive, int fast) +{ + union w_list_data o; + + /* ts A61006 */ + /* a ssert(drive); */ + /* a ssert(!SCAN_GOING()); */ + /* a ssert(!find_worker(drive)); */ + + if(drive == NULL) { + libdax_msgs_submit(libdax_messenger, -1, + 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) != NULL) { + 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; + } + + reset_progress(drive, 1, 1, 1, 0x10000, 0); + + /* 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() */ + /* ts B10321 : Allowed role 5 to be blanked */ + if ((drive->drive_role == 1 && + 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 && drive->drive_role != 5) + ) { + char msg[160]; + + sprintf(msg, "Drive and media state unsuitable for blanking. (role= %d , profile= 0x%x , status= %d)", + drive->drive_role, + (unsigned int) drive->current_profile, + drive->status); + libdax_msgs_submit(libdax_messenger, drive->global_index, + 0x00020130, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return; + } + + o.erase.drive = drive; + o.erase.fast = fast; + add_worker(Burnworker_type_erasE, drive, + (WorkerFunc) erase_worker_func, &o); +} + + +/* ts A61230 */ +static void *format_worker_func(struct w_list *w) +{ + +#define Libburn_protect_format_threaD 1 + +#ifdef Libburn_protect_format_threaD + sigset_t sigset, oldset; + + /* Protect format thread from being interrupted by external signals */ + sigfillset(&sigset); + sigdelset(&sigset, SIGSEGV); + sigdelset(&sigset, SIGILL); + pthread_sigmask(SIG_SETMASK, &sigset, &oldset); +#endif /* Libburn_protect_format_threaD */ + + burn_disc_format_sync(w->u.format.drive, w->u.format.size, + w->u.format.flag); + remove_worker(pthread_self()); + +#ifdef Libburn_protect_format_threaD + /* (just in case it would not end with all signals blocked) */ + pthread_sigmask(SIG_SETMASK, &oldset, NULL); +#endif /* Libburn_protect_format_threaD */ + + return NULL; +} + + +/* ts A61230 */ +void burn_disc_format(struct burn_drive *drive, off_t size, int flag) +{ + union w_list_data o; + int ok = 0, ret; + char msg[40]; + + reset_progress(drive, 1, 1, 1, 0x10000, 0); + + if ((SCAN_GOING()) || find_worker(drive) != NULL) { + 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 */ + } else if (drive->current_profile == 0x12) { + ok = 1; /* DVD-RAM */ + + } else if (drive->current_profile == 0x41) { + /* BD-R SRM */ + ok= 1; + ret = drive->read_format_capacities(drive, 0x00); + if (ret > 0 && + drive->format_descr_type == BURN_FORMAT_IS_FORMATTED) + ok = 0; + if (drive->status != BURN_DISC_BLANK) + ok = 0; + if (!ok) { + libdax_msgs_submit(libdax_messenger, + drive->global_index, 0x00020162, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "BD-R not unformatted blank any more. Cannot format.", + 0, 0); + drive->cancel = 1; + return; + } + if (flag & 32) { + libdax_msgs_submit(libdax_messenger, + drive->global_index, 0x00020163, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, + "Blank BD-R left unformatted for zero spare capacity.", + 0, 0); + return; + } + } else if (drive->current_profile == 0x43) { + ok = 1; /* BD-RE */ + + if ((flag & 32) && !(drive->current_feat23h_byte4 & 8)) { + libdax_msgs_submit(libdax_messenger, + drive->global_index, 0x00020164, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Drive does not format BD-RE without spares.", + 0, 0); + drive->cancel = 1; + return; + + } + } + + 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.format.drive = drive; + o.format.size = size; + o.format.flag = flag; + add_worker(Burnworker_type_formaT, drive, + (WorkerFunc) format_worker_func, &o); +} + + +static void *write_disc_worker_func(struct w_list *w) +{ + struct burn_drive *d = w->u.write.drive; + char msg[80]; + +#define Libburn_protect_write_threaD 1 + +#ifdef Libburn_protect_write_threaD + sigset_t sigset, oldset; + + /* Protect write thread from being interrupted by external signals */ + sigfillset(&sigset); + sigdelset(&sigset, SIGSEGV); + sigdelset(&sigset, SIGILL); + pthread_sigmask(SIG_SETMASK, &sigset, &oldset); +#endif /* Libburn_protect_write_threaD */ + + d->thread_pid = getpid(); + d->thread_tid = pthread_self(); + 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); + + sprintf(msg, "Write thread on drive %d ended", d->global_index); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020178, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + + remove_worker(pthread_self()); + d->busy = BURN_DRIVE_IDLE; + +#ifdef Libburn_protect_write_threaD + /* (just in case it would not end with all signals blocked) */ + pthread_sigmask(SIG_SETMASK, &oldset, NULL); +#endif /* Libburn_protect_write_threaD */ + + return NULL; +} + +void burn_disc_write(struct burn_write_opts *opts, struct burn_disc *disc) +{ + union w_list_data o; + char *reasons= NULL; + struct burn_drive *d; + int mvalid; + + d = opts->drive; + + /* ts A61006 */ + /* a ssert(!SCAN_GOING()); */ + /* a ssert(!find_worker(opts->drive)); */ + if ((SCAN_GOING()) || find_worker(opts->drive) != NULL) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020102, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "A drive operation is still going on (want to write)", + 0, 0); + return; + } + + reset_progress(d, disc->sessions, disc->session[0]->tracks, + disc->session[0]->track[0]->indices, 0, 0); + + /* For the next lines any return indicates failure */ + d->cancel = 1; + + /* ts A70203 : people have been warned in API specs */ + if (opts->write_type == BURN_WRITE_NONE) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002017c, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "No valid write type selected", 0, 0); + return; + } + + 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; + } + if (d->drive_role == 4) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020181, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Pseudo-drive is a read-only file. Cannot write.", + 0, 0); + return; + } + + /* ts A61007 : obsolete Assert in spc_select_write_params() */ + if (d->drive_role == 1) { + mvalid = 0; + if (d->mdata != NULL) + mvalid = 1; + if (!mvalid) { + libdax_msgs_submit(libdax_messenger, + d->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() + */ + + BURN_ALLOC_MEM_VOID(reasons, char, BURN_REASONS_LEN + 80); + strcpy(reasons, "Write job parameters are unsuitable:\n"); + if (burn_precheck_write(opts, disc, reasons + strlen(reasons), 1) + <= 0) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020139, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + reasons, 0, 0); + goto ex; + } + BURN_FREE_MEM(reasons); reasons= NULL; + + /* ts A90106 : early catching of unformatted BD-RE */ + if (d->current_profile == 0x43) + if (d->read_format_capacities(d, 0x00) > 0 && + d->format_descr_type != BURN_FORMAT_IS_FORMATTED) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020168, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Media not properly formatted. Cannot write.", + 0, 0); + return; + } + + d->cancel = 0; /* End of the return = failure area */ + + o.write.drive = d; + o.write.opts = opts; + o.write.disc = disc; + + opts->refcount++; + + add_worker(Burnworker_type_writE, d, + (WorkerFunc) write_disc_worker_func, &o); + +ex:; + BURN_FREE_MEM(reasons); +} + + +static void *fifo_worker_func(struct w_list *w) +{ + int old; + +#define Libburn_protect_fifo_threaD 1 + +#ifdef Libburn_protect_fifo_threaD + sigset_t sigset, oldset; + + /* Protect fifo thread from being interrupted by external signals */ + sigfillset(&sigset); + sigdelset(&sigset, SIGSEGV); + sigdelset(&sigset, SIGILL); + pthread_sigmask(SIG_SETMASK, &sigset, &oldset); +#endif /* Libburn_protect_fifo_threaD */ + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old); + /* Note: Only burn_fifo_abort() shall cancel the fifo thread */ + + burn_fifo_source_shoveller(w->u.fifo.source, w->u.fifo.flag); + remove_worker(pthread_self()); + +#ifdef Libburn_protect_fifo_threaD + /* (just in case it would not end with all signals blocked) */ + pthread_sigmask(SIG_SETMASK, &oldset, NULL); +#endif /* Libburn_protect_fifo_threaD */ + + return NULL; +} + + +int burn_fifo_start(struct burn_source *source, int flag) +{ + union w_list_data o; + struct burn_source_fifo *fs = source->data; + + fs->is_started = -1; + + /* create and set up ring buffer */; + fs->buf = burn_os_alloc_buffer( + ((size_t) fs->chunksize) * (size_t) fs->chunks, 0); + if (fs->buf == NULL) { + /* >>> could not start ring buffer */; + return -1; + } + + o.fifo.source = source; + o.fifo.flag = flag; + add_worker(Burnworker_type_fifO, NULL, + (WorkerFunc) fifo_worker_func, &o); + fs->is_started = 1; + + return 1; +} + + +int burn_fifo_abort(struct burn_source_fifo *fs, int flag) +{ + int ret; + pthread_t pt; + + if (fs->thread_is_valid <= 0 || fs->thread_handle == NULL) + return(2); + +#ifdef NIX + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + "Aborting running burn_source_fifo thread", 0, 0); +#endif /* NIX */ + + pt= *((pthread_t *) fs->thread_handle); + remove_worker(pt); + ret = pthread_cancel(pt); + return (ret == 0); +} + + +#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/trunk/libburn/async.h b/trunk/libburn/async.h new file mode 100644 index 0000000..8ebae21 --- /dev/null +++ b/trunk/libburn/async.h @@ -0,0 +1,18 @@ +/* -*- 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); + +/* ts A81108 */ +/* To abort a running fifo thread before the fifo object gets deleted */ +int burn_fifo_abort(struct burn_source_fifo *fs, int flag); + + +#endif /* BURN__ASYNC_H */ diff --git a/trunk/libburn/back_hacks.h b/trunk/libburn/back_hacks.h new file mode 100644 index 0000000..50b7de5 --- /dev/null +++ b/trunk/libburn/back_hacks.h @@ -0,0 +1,56 @@ +/** + Copyright (c) 2006 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. + + 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/trunk/libburn/cdtext.c b/trunk/libburn/cdtext.c new file mode 100755 index 0000000..6c21ced --- /dev/null +++ b/trunk/libburn/cdtext.c @@ -0,0 +1,1723 @@ + +/* Copyright (c) 2011 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> + +#include "libburn.h" +#include "init.h" +#include "structure.h" +#include "options.h" +#include "util.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + + +/* --------------------- Production of CD-TEXT packs -------------------- */ + + +struct burn_pack_cursor { + unsigned char *packs; + int num_packs; + int td_used; + int hiseq[8]; + int pack_count[16]; + int track_offset; +}; + + +/* @param flag bit0= double_byte characters +*/ +int burn_create_new_pack(int pack_type, int track_no, int double_byte, + int block, int char_pos, + struct burn_pack_cursor *crs, int flag) +{ + int idx; + + if (crs->num_packs >= Libburn_leadin_cdtext_packs_maX) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002018b, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Too many CD-TEXT packs", 0, 0); + return 0; + } + if (crs->hiseq[block] >= 255) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002018e, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Too many CD-TEXT packs in block", 0, 0); + return 0; + } + if (char_pos > 15) + char_pos = 15; + else if (char_pos < 0) + char_pos = 0; + idx = crs->num_packs * 18; + crs->packs[idx++] = pack_type; + crs->packs[idx++] = track_no; + crs->packs[idx++] = crs->hiseq[block]; + crs->packs[idx++] = ((flag & 1) << 7) | (block << 4) | char_pos; + crs->hiseq[block]++; + crs->td_used = 0; + crs->pack_count[pack_type - Libburn_pack_type_basE]++; + return 1; +} + + +/* Plain implementation of polynomial division on a Galois field, where + addition and subtraction both are binary exor. Euclidian algorithm. + Divisor is x^16 + x^12 + x^5 + 1 = 0x11021. +*/ +static int crc_11021(unsigned char *data, int count, int flag) +{ + int acc = 0, i; + + for (i = 0; i < count * 8 + 16; i++) { + acc = (acc << 1); + if (i < count * 8) + acc |= ((data[i / 8] >> (7 - (i % 8))) & 1); + if (acc & 0x10000) + acc ^= 0x11021; + } + return acc; +} + + +/* @param flag bit0= repair mismatching checksums + bit1= repair checksums if all pack CRCs are 0 + @return 0= no mismatch , >0 number of unrepaired mismatches + <0 number of repaired mismatches that were not 0 +*/ +int burn_cdtext_crc_mismatches(unsigned char *packs, int num_packs, int flag) +{ + int i, residue, count = 0, repair; + unsigned char crc[2]; + + repair = flag & 1; + if (flag & 2) { + for (i = 0; i < num_packs * 18; i += 18) + if (packs[i + 16] || packs[i + 17]) + break; + if (i == num_packs * 18) + repair = 1; + } + for (i = 0; i < num_packs * 18; i += 18) { + residue = crc_11021(packs + i, 16, 0); + crc[0] = ((residue >> 8) & 0xff) ^ 0xff; + crc[1] = ((residue ) & 0xff) ^ 0xff; + if(crc[0] != packs[i + 16] || crc[1] != packs[i + 17]) { + if (repair) { + if (packs[i + 16] || packs[i + 17]) + count--; + packs[i + 16] = crc[0]; + packs[i + 17] = crc[1]; + } else + count++; + } + + } + return count; +} + + +static int burn_finalize_text_pack(struct burn_pack_cursor *crs, int flag) +{ + int residue = 0, i, idx; + + idx = 18 * crs->num_packs; + for(i = 4 + crs->td_used; i < 16; i++) + crs->packs[idx + i] = 0; + crs->td_used = 12; + + /* MMC-3 Annex J : CRC Field consists of 2 bytes. + The polynomial is X16 + X12 + X5 + 1. All bits shall be inverted. + */ + residue = crc_11021(crs->packs + idx, 16, 0) ^ 0xffff; + + crs->packs[idx + 16] = (residue >> 8) & 0xff; + crs->packs[idx + 17] = residue & 0xff; + crs->num_packs++; + crs->td_used = 0; + return 1; +} + + +/* @param flag bit0= double_byte characters +*/ +static int burn_create_tybl_packs(unsigned char *payload, int length, + int track_no, int pack_type, int block, + struct burn_pack_cursor *crs, int flag) +{ + int i, ret, binary_part = 0, char_pos; + + if (pack_type == 0x87) + binary_part = 2; + else if ((pack_type >= 0x88 && pack_type <= 0x8c) || pack_type == 0x8f) + binary_part = length; + for(i = 0; i < length; i++) { + if (crs->td_used == 0 || crs->td_used >= 12) { + if (crs->td_used > 0) { + ret = burn_finalize_text_pack(crs, 0); + if (ret <= 0) + return ret; + } + char_pos = (i - binary_part) / (1 + (flag & 1)); + ret = burn_create_new_pack(pack_type, track_no, + (flag & 1), block, char_pos, + crs, flag & 1); + if (ret <= 0) + return ret; + } + crs->packs[crs->num_packs * 18 + 4 + crs->td_used] = + payload[i]; + crs->td_used++; + } + return 1; +} + + +/* Finalize block by 0x8f. Set bytes 20 to 27 to 0 for now. */ +static int burn_create_bl_size_packs(int block, unsigned char *char_codes, + unsigned char *copyrights, + unsigned char *languages, + int num_tracks, + struct burn_pack_cursor *crs, int flag) +{ + int i, ret; + unsigned char payload[12]; + + payload[0] = char_codes[block]; + payload[1] = crs->track_offset; + payload[2] = num_tracks + crs->track_offset - 1; + payload[3] = copyrights[block]; + for (i = 0; i < 8; i++) + payload[i + 4] = crs->pack_count[i]; + ret = burn_create_tybl_packs(payload, 12, 0, 0x8f, block, crs, 0); + if (ret <= 0) + return ret; + + for (i = 0; i < 7; i++) + payload[i] = crs->pack_count[i + 8]; + payload[7] = 3; /* always 3 packs of type 0x8f */ + for (i = 0; i < 4; i++) { + /* Will be set when all blocks are done */ + payload[i + 8] = 0; + } + ret = burn_create_tybl_packs(payload, 12, 1, 0x8f, block, crs, 0); + if (ret <= 0) + return ret; + + for (i = 0; i < 4; i++) { + /* Will be set when all blocks are done */ + payload[i] = 0; + } + for (i = 0; i < 8; i++) { + payload[i + 4] = languages[i]; + } + ret = burn_create_tybl_packs(payload, 12, 2, 0x8f, block, crs, 0); + if (ret <= 0) + return ret; + ret = burn_finalize_text_pack(crs, 0); + if (ret <= 0) + return ret; + + for (i = 0; i < 16; i++) + crs->pack_count[i] = 0; + return 1; +} + + +/* Text packs of track for type and block + @param flag bit0= write TAB, because content is identical to previous track +*/ +static int burn_create_tybl_t_packs(struct burn_track *t, int track_no, + int pack_type, int block, + struct burn_pack_cursor *crs, int flag) +{ + int ret, length = 0, idx, double_byte, flags= 0; + unsigned char *payload = NULL, dummy[8]; + struct burn_cdtext *cdt; + + cdt = t->cdtext[block]; + idx = pack_type - Libburn_pack_type_basE; + if (cdt != NULL) { + if (cdt->length[idx] > 0) { + payload = cdt->payload[idx]; + length = cdt->length[idx]; + } + flags = cdt->flags; + } + if (payload == NULL) { + dummy[0]= 0; + payload = dummy; + length = strlen((char *) dummy) + 1; + } + double_byte = !!(flags & (1 <<(pack_type - Libburn_pack_type_basE))); + if (flag & 1) { + length = 0; + dummy[length++] = 9; + if (double_byte) + dummy[length++] = 9; + dummy[length++] = 0; + if (double_byte) + dummy[length++] = 0; + payload = dummy; + } + ret = burn_create_tybl_packs(payload, length, track_no, + pack_type, block, crs, double_byte); + return ret; +} + + +/* Check whether the content is the same as in the previous pack. If so, + advise to use the TAB abbreviation. +*/ +static int burn_decide_cdtext_tab(int block, int pack_type, + struct burn_cdtext *cdt_curr, + struct burn_cdtext *cdt_prev, int flag) +{ + int length, j, idx; + + idx = pack_type - Libburn_pack_type_basE; + if (cdt_curr == NULL || cdt_prev == NULL) + return 0; + if (((cdt_curr->flags >> idx) & 1) != ((cdt_prev->flags >> idx) & 1)) + return 0; + length = cdt_curr->length[idx]; + if (length != cdt_prev->length[idx] || + length <= 1 + ((cdt_curr->flags >> idx) & 1)) + return 0; + for (j = 0; j < length; j++) + if (cdt_curr->payload[idx][j] != cdt_prev->payload[idx][j]) + break; + if (j < length) + return 0; + return 1; +} + + +/* Text packs of session and of tracks (if applicable), for type and block +*/ +static int burn_create_tybl_s_packs(struct burn_session *s, + int pack_type, int block, + struct burn_pack_cursor *crs, int flag) +{ + int i, ret, idx, double_byte, use_tab; + struct burn_cdtext *cdt; + + cdt = s->cdtext[block]; + idx = pack_type - Libburn_pack_type_basE; + if (cdt->length[idx] == 0 || cdt->payload[idx] == NULL) + return 1; + + double_byte = !!(cdt->flags & + (1 <<(pack_type - Libburn_pack_type_basE))); + ret = burn_create_tybl_packs(cdt->payload[idx], cdt->length[idx], 0, + pack_type, block, crs, double_byte); + if (ret <= 0) + return ret; + + if ((pack_type < 0x80 || pack_type > 0x85) && pack_type != 0x8e) { + ret = burn_finalize_text_pack(crs, 0); + return ret; + } + + for (i = 0; i < s->tracks; i++) { + if (i > 0) + use_tab = burn_decide_cdtext_tab(block, pack_type, + s->track[i]->cdtext[block], + s->track[i - 1]->cdtext[block], 0); + else + use_tab = 0; + ret = burn_create_tybl_t_packs(s->track[i], + i + crs->track_offset, pack_type, + block, crs, use_tab); + if (ret <= 0) + return ret; + } + /* Fill up last pack with 0s */ + ret = burn_finalize_text_pack(crs, 0); + return ret; +} + + +/* ts B11210 : API */ +/* @param flag bit0= do not return CD-TEXT packs, but return number of packs +*/ +int burn_cdtext_from_session(struct burn_session *s, + unsigned char **text_packs, int *num_packs, + int flag) +{ + int pack_type, block, ret, i, idx, j, residue; + struct burn_pack_cursor crs; + + if (text_packs == NULL || num_packs == NULL) { + flag |= 1; + } else if (!(flag & 1)) { + *text_packs = NULL; + *num_packs = 0; + } + memset(&crs, 0, sizeof(struct burn_pack_cursor)); + crs.track_offset = s->firsttrack; + BURN_ALLOC_MEM(crs.packs, unsigned char, + Libburn_leadin_cdtext_packs_maX * 18); + + for (block = 0; block < 8; block++) + if (s->cdtext[block] != NULL) + break; + if (block == 8) + {ret = 1; goto ex;} + + for (block= 0; block < 8; block++) { + if (s->cdtext[block] == NULL) + continue; + for (pack_type = 0x80; + pack_type < 0x80 + Libburn_pack_num_typeS; pack_type++) { + if (pack_type == 0x8f) + continue; + ret = burn_create_tybl_s_packs(s, + pack_type, block, &crs, 0); + if (ret <= 0) + goto ex; + } + ret = burn_create_bl_size_packs(block, + s->cdtext_char_code, s->cdtext_copyright, + s->cdtext_language, s->tracks, &crs, 0); + if (ret <= 0) + goto ex; + } + + /* Insert the highest sequence numbers of each block into + the 0x8f packs 2 and 3 (bytes 20 to 27) + */ + for (i = 0; i < crs.num_packs; i++) { + idx = i * 18; + if (crs.packs[idx] == 0x8f && crs.packs[idx + 1] == 1) { + for (j = 0; j < 4; j++) + if (crs.hiseq[j] > 0) + crs.packs[idx + 4 + 8 + j] = + crs.hiseq[j] - 1; + else + crs.packs[idx + 4 + 8 + j] = 0; + } else if (crs.packs[idx] == 0x8f && crs.packs[idx + 1] == 2) { + for (j = 0; j < 4; j++) + if (crs.hiseq[j + 4] > 0) + crs.packs[idx + 4 + j] = + crs.hiseq[j + 4] - 1; + else + crs.packs[idx + 4 + j] = 0; + } else + continue; + /* Re-compute checksum */ + residue = crc_11021(crs.packs + idx, 16, 0) ^ 0xffff; + crs.packs[idx + 16] = (residue >> 8) & 0xff; + crs.packs[idx + 17] = residue & 0xff; + } + + ret = 1; +ex:; + if (ret <= 0 || (flag & 1)) { + if (ret > 0) + ret = crs.num_packs; + else if (flag & 1) + ret = -1; + BURN_FREE_MEM(crs.packs); + } else { + if (crs.num_packs > 0) + *text_packs = crs.packs; + else + BURN_FREE_MEM(crs.packs); + *num_packs = crs.num_packs; + } + return(ret); +} + + +/* ---------------- Reader of Sony Input Sheet Version 0.7T ------------- */ + + +/* @param flag bit0= allow two byte codes 0xNNNN or 0xNN 0xNN +*/ +static int v07t_hexcode(char *payload, int flag) +{ + unsigned int x; + int lo, hi, l; + char buf[10], *cpt; + + l = strlen(payload); + if (strncmp(payload, "0x", 2) != 0) + return -1; + if ((l == 6 || l == 9) && (flag & 1)) + goto double_byte; + if (strlen(payload) != 4) + return -1; + if (!(isxdigit(payload[2]) && isxdigit(payload[3]))) + return -1; + sscanf(payload + 2, "%x", &x); + return x; + +double_byte:; + strcpy(buf, payload); + buf[4] = 0; + hi = v07t_hexcode(buf, 0); + if (strlen(payload) == 6) { + buf[4] = payload[4]; + buf[2] = '0'; + buf[3] = 'x'; + cpt = buf + 2; + } else { + if(payload[4] != 32 && payload[4] != 9) + return(-1); + cpt = buf + 5; + } + lo = v07t_hexcode(cpt, 0); + if (lo < 0 || hi < 0) + return -1; + return ((hi << 8) | lo); +} + + +static int v07t_cdtext_char_code(char *payload, int flag) +{ + int ret; + char *msg = NULL; + + ret = v07t_hexcode(payload, 0); + if (ret >= 0) + return ret; + if (strstr(payload, "8859") != NULL) + return 0x00; + else if(strstr(payload, "ASCII") != NULL) + return 0x01; + else if(strstr(payload, "JIS") != NULL) + return 0x80; + + BURN_ALLOC_MEM(msg, char, 160); + sprintf(msg, "Unknown v07t Text Code '%.80s'", payload); + libdax_msgs_submit(libdax_messenger, -1, 0x00020191, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = -1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +static int v07t_cdtext_lang_code(char *payload, int flag) +{ + int i, ret; + static char *languages[128] = { + BURN_CDTEXT_LANGUAGES_0X00, + BURN_CDTEXT_FILLER, + BURN_CDTEXT_LANGUAGES_0X45 + }; + char *msg = NULL; + + ret = v07t_hexcode(payload, 0); + if (ret >= 0) + return ret; + if (payload[0] != 0) + for(i = 0; i < 128; i++) + if(strcmp(languages[i], payload) == 0) + return i; + + BURN_ALLOC_MEM(msg, char, 160); + sprintf(msg, "Unknown v07t Language Code '%.80s'", payload); + libdax_msgs_submit(libdax_messenger, -1, 0x00020191, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = -1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +static int v07t_cdtext_genre_code(char *payload, int flag) +{ + int i, ret; + static char *genres[BURN_CDTEXT_NUM_GENRES] = { + BURN_CDTEXT_GENRE_LIST + }; + char *msg = NULL; + + ret = v07t_hexcode(payload, 1); + if(ret >= 0) + return ret; + for (i= 0; i < BURN_CDTEXT_NUM_GENRES; i++) + if (strcmp(genres[i], payload) == 0) + return i; + + BURN_ALLOC_MEM(msg, char, 160); + sprintf(msg, "Unknown v07t Genre Code '%.80s'", payload); + libdax_msgs_submit(libdax_messenger, -1, 0x00020191, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = -1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +static int v07t_cdtext_len_db(char *payload, int *char_code, + int *length, int *double_byte, int flag) +{ + if (*char_code < 0) + *char_code = 0x00; + *double_byte = (*char_code == 0x80); + *length = strlen(payload) + 1 + *double_byte; + return 1; +} + + +static int v07t_cdtext_to_session(struct burn_session *session, int block, + char *payload, int *char_code, int pack_type, + char *pack_type_name, int flag) +{ + int length, double_byte, ret; + + ret = v07t_cdtext_len_db(payload, char_code, &length, &double_byte, 0); + if (ret <= 0) + return ret; + ret = burn_session_set_cdtext(session, block, pack_type, + pack_type_name, (unsigned char *) payload, length, + double_byte); + return ret; +} + + +static int v07t_cdtext_to_track(struct burn_track *track, int block, + char *payload, int *char_code, int pack_type, + char *pack_type_name, int flag) +{ + int length, double_byte, ret; + + ret = v07t_cdtext_len_db(payload, char_code, &length, &double_byte, 0); + if (ret <= 0) + return ret; + ret = burn_track_set_cdtext(track, block, pack_type, pack_type_name, + (unsigned char *) payload, length, double_byte); + return ret; +} + + +static int v07t_apply_to_session(struct burn_session *session, int block, + int char_codes[8], int copyrights[8], int languages[8], + int session_attr_seen[16], int track_attr_seen[16], + int genre_code, char *genre_text, int flag) +{ + int i, ret, length; + char *line = NULL; + + BURN_ALLOC_MEM(line, char, 4096); + + for (i= 0x80; i <= 0x8e; i++) { + if (i > 0x85 && i != 0x8e) + continue; + if (session_attr_seen[i - 0x80] || !track_attr_seen[i - 0x80]) + continue; + ret = v07t_cdtext_to_session(session, block, "", + char_codes + block, i, NULL, 0); + if (ret <= 0) + goto ex; + } + if (genre_code >= 0 && genre_text[0]) { + line[0] = (genre_code >> 8) & 0xff; + line[1] = genre_code & 0xff; + strcpy(line + 2, genre_text); + length = 2 + strlen(line + 2) + 1; + ret = burn_session_set_cdtext(session, block, 0, "GENRE", + (unsigned char *) line, length, 0); + if (ret <= 0) + goto ex; + } + ret = burn_session_set_cdtext_par(session, char_codes, copyrights, + languages, 0); + if (ret <= 0) + goto ex; + for (i = 0; i < 8; i++) + char_codes[i] = copyrights[i] = languages[i]= -1; + for (i = 0; i < 16; i++) + session_attr_seen[i] = track_attr_seen[i] = 0; + genre_text[0] = 0; + ret = 1; +ex: + BURN_FREE_MEM(line); + return ret; +} + + +/* ts B11215 API */ +/* @param flag bit0= permission to read multiple blocks from the same sheet + bit1= do not attach CATALOG to session or ISRC to track for + writing to Q sub-channel +*/ +int burn_session_input_sheet_v07t(struct burn_session *session, + char *path, int block, int flag) +{ + int ret = 0, num_tracks, char_codes[8], copyrights[8], languages[8], i; + int genre_code = -1, track_offset = 1, pack_type, tno, tnum; + int session_attr_seen[16], track_attr_seen[16]; + int int0x00 = 0x00, int0x01 = 0x01; + int additional_blocks = -1, line_count = 0, enable_multi_block = 0; + struct stat stbuf; + FILE *fp = NULL; + char *line = NULL, *eq_pos, *payload, *genre_text = NULL, track_txt[3]; + char *msg = NULL; + struct burn_track **tracks; + + BURN_ALLOC_MEM(msg, char, 4096); + BURN_ALLOC_MEM(line, char, 4096); + BURN_ALLOC_MEM(genre_text, char, 160); + + for (i = 0; i < 8; i++) + char_codes[i] = copyrights[i] = languages[i]= -1; + for (i = 0; i < 16; i++) + session_attr_seen[i] = track_attr_seen[i] = 0; + genre_text[0] = 0; + + tracks = burn_session_get_tracks(session, &num_tracks); + if (stat(path, &stbuf) == -1) { +cannot_open:; + sprintf(msg, "Cannot open CD-TEXT input sheet v07t '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020193, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), errno, 0); + ret = 0; goto ex; + } + if (!S_ISREG(stbuf.st_mode)) { + sprintf(msg, + "File is not of usable type: CD-TEXT input sheet v07t '%s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020193, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + + fp = fopen(path, "rb"); + if (fp == NULL) + goto cannot_open; + + while (1) { + if (burn_sfile_fgets(line, 4095, fp) == NULL) { + if (!ferror(fp)) + break; + sprintf(msg, + "Cannot read all bytes from CD-TEXT input sheet v07t '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020193, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + line_count++; + if (strlen(line) == 0) + continue; + eq_pos = strchr(line, '='); + if (eq_pos == NULL) { + sprintf(msg, + "CD-TEXT v07t input sheet line without '=' : '%.4000s'", + line); + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + for (payload = eq_pos + 1; *payload == 32 || *payload == 9; + payload++); + *eq_pos = 0; + for (eq_pos--; + (*eq_pos == 32 || *eq_pos == 9) && eq_pos > line; + eq_pos--) + *eq_pos= 0; + + if (payload[0] == 0) + continue; + + if (strcmp(line, "Text Code") == 0) { + ret = v07t_cdtext_char_code(payload, 0); + if (ret < 0) + goto ex; + if (char_codes[block] >= 0 && + char_codes[block] != ret) { + libdax_msgs_submit(libdax_messenger, -1, + 0x00020192, LIBDAX_MSGS_SEV_FAILURE, + LIBDAX_MSGS_PRIO_HIGH, + "Unexpected v07t Text Code change", + 0, 0); + ret = 0; goto ex; + } + char_codes[block] = ret; + + } else if (strcmp(line, "Language Code") == 0) { + ret = v07t_cdtext_lang_code(payload, 0); + if(ret < 0) + goto ex; + languages[block] = ret; + + } else if (strcmp(line, "0x80") == 0 || + strcmp(line, "Album Title") == 0) { + ret = v07t_cdtext_to_session(session, block, payload, + char_codes + block, 0, "TITLE", 0); + if (ret <= 0) + goto ex; + session_attr_seen[0x0] = 1; + + } else if (strcmp(line, "0x81") == 0 || + strcmp(line, "Artist Name") == 0) { + ret = v07t_cdtext_to_session(session, block, payload, + char_codes + block, 0, "PERFORMER", 0); + if (ret <= 0) + goto ex; + session_attr_seen[0x1] = 1; + + } else if (strcmp(line, "0x82") == 0 || + strcmp(line, "Songwriter") == 0) { + ret = v07t_cdtext_to_session(session, block, payload, + char_codes + block, 0, "SONGWRITER", + 0); + if (ret <= 0) + goto ex; + session_attr_seen[0x2] = 1; + + } else if (strcmp(line, "0x83") == 0 || + strcmp(line, "Composer") == 0) { + ret = v07t_cdtext_to_session(session, block, payload, + char_codes + block, 0, "COMPOSER", 0); + if (ret <= 0) + goto ex; + session_attr_seen[0x3] = 1; + + } else if (strcmp(line, "0x84") == 0 || + strcmp(line, "Arranger") == 0) { + ret = v07t_cdtext_to_session(session, block, payload, + char_codes + block, 0, "ARRANGER", 0); + if (ret <= 0) + goto ex; + session_attr_seen[0x4] = 1; + + } else if (strcmp(line, "0x85") == 0 || + strcmp(line, "Album Message") == 0) { + ret = v07t_cdtext_to_session(session, block, payload, + char_codes + block, 0, "MESSAGE", 0); + if (ret <= 0) + goto ex; + session_attr_seen[0x5] = 1; + + } else if (strcmp(line, "0x86") == 0 || + strcmp(line, "Catalog Number") == 0) { + ret = v07t_cdtext_to_session(session, block, payload, + &int0x01, 0, "DISCID", 0); + if(ret <= 0) + goto ex; + + } else if (strcmp(line, "Genre Code") == 0) { + genre_code = v07t_cdtext_genre_code(payload, 0); + if (genre_code < 0) { + ret = 0; goto ex; + } + + } else if (strcmp(line, "Genre Information") == 0) { + strncpy(genre_text, payload, 159); + genre_text[159] = 0; + + } else if (strcmp(line, "0x8d") == 0 || + strcmp(line, "Closed Information") == 0) { + ret = v07t_cdtext_to_session(session, block, payload, + &int0x00, 0, "CLOSED", 0); + if (ret <= 0) + goto ex; + + } else if(strcmp(line, "0x8e") == 0 || + strcmp(line, "UPC / EAN") == 0) { + ret = v07t_cdtext_to_session(session, block, payload, + &int0x01, 0, "UPC_ISRC", 0); + if (ret <= 0) + goto ex; + if (!(flag & 2)) { + memcpy(session->mediacatalog, payload, 13); + session->mediacatalog[13] = 0; + } + session_attr_seen[0xe] = 1; + + } else if (strncmp(line, "Disc Information ", 17) == 0) { + + /* >>> ??? is this good for anything ? */; + + } else if (strcmp(line, "Input Sheet Version") == 0) { + if (strcmp(payload, "0.7T") != 0) { + sprintf(msg, + "Wrong Input Sheet Version '%.4000s'. Expected '0.7T'.", + payload); + libdax_msgs_submit(libdax_messenger, -1, + 0x00020194, LIBDAX_MSGS_SEV_FAILURE, + LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + if (flag & 1) + if (line_count == 1) + enable_multi_block = 1; + if (enable_multi_block) { + if (additional_blocks >= 0) { + if (block == 7) { + libdax_msgs_submit( + libdax_messenger, -1, 0x000201a0, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + "Maximum number of CD-TEXT blocks exceeded", + 0, 0); + break; + } + ret = v07t_apply_to_session( + session, block, char_codes, + copyrights, languages, + session_attr_seen, + track_attr_seen, + genre_code, genre_text, 0); + if (ret <= 0) + goto ex; + block++; + } + additional_blocks++; + } + + } else if (strcmp(line, "Remarks") == 0) { + ; + + } else if (strcmp(line, "Text Data Copy Protection") == 0) { + ret = v07t_hexcode(payload, 0); + if (ret >= 0) + copyrights[block] = ret; + else if (strcmp(payload, "ON") == 0) + copyrights[block] = 0x03; + else if (strcmp(payload, "OFF") == 0) + copyrights[block] = 0x00; + else { + sprintf(msg, + "Unknown v07t Text Data Copy Protection '%.4000s'", + payload); + libdax_msgs_submit(libdax_messenger, -1, + 0x00020191, LIBDAX_MSGS_SEV_FAILURE, + LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + + ret = 0; goto ex; + } + + } else if (strcmp(line, "First Track Number") == 0) { + ret = -1; + sscanf(payload, "%d", &ret); + if (ret <= 0 || ret > 99) { +bad_tno:; + sprintf(msg, + "Inappropriate v07t First Track Number '%.4000s'", + payload); + libdax_msgs_submit(libdax_messenger, -1, + 0x00020194, LIBDAX_MSGS_SEV_FAILURE, + LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } else { + track_offset = ret; + ret = burn_session_set_start_tno(session, + track_offset, 0); + if (ret <= 0) + goto ex; + } + + } else if (strcmp(line, "Last Track Number") == 0) { + ret = -1; + sscanf(payload, "%d", &ret); + if (ret < 0) { + goto bad_tno; + } else { + + /* >>> ??? Is it good for anything ? */; + + } + + } else if (strncmp(line, "Track ", 6) == 0) { + tno = -1; + sscanf(line + 6, "%d", &tno); + if (tno < 1 || tno - track_offset < 0 || + tno - track_offset >= num_tracks) { + track_txt[0] = line[6]; + track_txt[1] = line[7]; + track_txt[2] = 0; +bad_track_no:; + sprintf(msg, + "Inappropriate v07t Track number '%.3900s'", + track_txt); + sprintf(msg + strlen(msg), + " (acceptable range: %2.2d to %2.2d)", + track_offset, + num_tracks + track_offset - 1); + libdax_msgs_submit(libdax_messenger, -1, + 0x00020194, LIBDAX_MSGS_SEV_FAILURE, + LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + tnum = tno - track_offset; + + if (strcmp(line, "0x80") == 0 || + strcmp(line + 9, "Title") == 0) + pack_type = 0x80; + else if (strcmp(line + 9, "0x81") == 0 || + strcmp(line + 9, "Artist") == 0) + pack_type = 0x81; + else if (strcmp(line + 9, "0x82") == 0 || + strcmp(line + 9, "Songwriter") == 0) + pack_type = 0x82; + else if (strcmp(line + 9, "0x83") == 0 || + strcmp(line + 9, "Composer") == 0) + pack_type = 0x83; + else if (strcmp(line + 9, "0x84") == 0 || + strcmp(line + 9, "Arranger") == 0) + pack_type = 0x84; + else if (strcmp(line + 9, "0x85") == 0 || + strcmp(line + 9, "Message") == 0) + pack_type = 0x85; + else if (strcmp(line + 9, "0x8e") == 0 || + strcmp(line + 9, "ISRC") == 0) { + pack_type = 0x8e; + if (!(flag & 2)) { + ret = burn_track_set_isrc_string( + tracks[tnum], payload, 0); + if (ret <= 0) + goto ex; + } + } else { + sprintf(msg, + "Unknown v07t Track purpose specifier '%s'", + line + 9); + libdax_msgs_submit(libdax_messenger, -1, + 0x00020191, LIBDAX_MSGS_SEV_FAILURE, + LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + ret = v07t_cdtext_to_track(tracks[tnum], block, + payload, &int0x00, pack_type, "", 0); + if (ret <= 0) + goto ex; + track_attr_seen[pack_type - 0x80] = 1; + + } else if (strncmp(line, "ISRC ", 5) == 0) { + /* Track variation of UPC EAN = 0x8e */ + tno = -1; + sscanf(line + 5, "%d", &tno); + if (tno <= 0 || tno - track_offset < 0 || + tno - track_offset >= num_tracks) { + track_txt[0] = line[5]; + track_txt[1] = line[6]; + track_txt[2] = 0; + goto bad_track_no; + } + tnum = tno - track_offset; + if (!(flag & 2)) { + ret = burn_track_set_isrc_string( + tracks[tnum], payload, 0); + if (ret <= 0) + goto ex; + } + ret = v07t_cdtext_to_track(tracks[tnum], block, + payload, &int0x00, 0x8e, "", 0); + if (ret <= 0) + goto ex; + track_attr_seen[0xe] = 1; + + } else { + sprintf(msg, + "Unknown v07t purpose specifier '%.4000s'", + line); + libdax_msgs_submit(libdax_messenger, -1, 0x00020191, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + } + ret = v07t_apply_to_session(session, block, + char_codes, copyrights, languages, + session_attr_seen, track_attr_seen, + genre_code, genre_text, 0); + if (ret <= 0) + goto ex; + + ret = 1; + if (additional_blocks > 0) + ret += additional_blocks;; +ex:; + if(fp != NULL) + fclose(fp); + BURN_FREE_MEM(genre_text); + BURN_FREE_MEM(line); + BURN_FREE_MEM(msg); + return ret; +} + + +/* ts B11221 API */ +int burn_cdtext_from_packfile(char *path, unsigned char **text_packs, + int *num_packs, int flag) +{ + int ret = 0, residue = 0; + struct stat stbuf; + FILE *fp = NULL; + unsigned char head[4], tail[1]; + char *msg = NULL; + + BURN_ALLOC_MEM(msg, char, 4096); + + if (stat(path, &stbuf) == -1) { +cannot_open:; + sprintf(msg, "Cannot open CD-TEXT pack file '%.4000s'", path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020198, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), errno, 0); + ret = 0; goto ex; + } + if (!S_ISREG(stbuf.st_mode)) + goto not_a_textfile; + residue = (stbuf.st_size % 18); + if(residue != 4 && residue != 0 && residue != 1) { +not_a_textfile:; + sprintf(msg, + "File is not of usable type or content for CD-TEXT packs: '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020198, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + if (stbuf.st_size < 18) + goto not_a_textfile; + + fp = fopen(path, "rb"); + if (fp == NULL) + goto cannot_open; + if (residue == 4) { /* This is for files from cdrecord -vv -toc */ + ret = fread(head, 4, 1, fp); + if (ret != 1) { +cannot_read:; + sprintf(msg, + "Cannot read all bytes from CD-TEXT pack file '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020198, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), errno, 0); + ret = 0; goto ex; + } + if (head[0] * 256 + head[1] != stbuf.st_size - 2) + goto not_a_textfile; + } + *num_packs = (stbuf.st_size - residue) / 18; + if (*num_packs > 2048) { + /* Each block can have 256 text packs. + There are 8 blocks at most. */ + sprintf(msg, + "CD-Text pack file too large (max. 36864 bytes): '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x0002018b, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), errno, 0); + ret = 0; goto ex; + } + + BURN_ALLOC_MEM(*text_packs, unsigned char, *num_packs * 18); + ret = fread(*text_packs, *num_packs * 18, 1, fp); + if (ret != 1) + goto cannot_read; + if (residue == 1) { /* This is for Sony CDTEXT files */ + ret = fread(tail, 1, 1, fp); + if (ret != 1) + goto cannot_read; + if (tail[0] != 0) + goto not_a_textfile; + } + + ret= 1; +ex:; + if (ret <= 0) { + BURN_FREE_MEM(*text_packs); + *text_packs = NULL; + *num_packs = 0; + } + if (fp != NULL) + fclose(fp); + BURN_FREE_MEM(msg); + return ret; +} + + +/* --------------------------------- make_v07t -------------------------- */ + + +static int search_pack(unsigned char *text_packs, int num_packs, + int start_no, int pack_type, int block, + unsigned char **found_pack, int *found_no, int flag) +{ + int i; + + for (i = start_no; i < num_packs; i++) { + if (pack_type >= 0) + if (text_packs[i * 18] != pack_type) + continue; + if (block >= 0) + if (((text_packs[i * 18 + 3] >> 4) & 7) != block) + continue; + *found_pack = text_packs + i * 18; + *found_no = i; + return 1; + } + *found_pack = NULL; + *found_no = num_packs; + return 0; +} + + +static void write_v07t_line(char **respt, char *spec, char *value, int vlen, + int *result_len, int flag) +{ + int len; + + if (vlen == -1) + vlen = strlen(value); + len = strlen(spec); + if (len < 19) + len = 19; + len += 3 + vlen + 1; + if(flag & 1) { + *result_len += len; + return; + } + sprintf(*respt, "%-19s = ", spec); + if (vlen > 0) + memcpy(*respt + strlen(*respt), value, vlen); + (*respt)[len - 1] = '\n'; + (*respt)[len] = 0; + *respt+= len; +} + + +/* + @return -1 error + 0 no pack of block,pack_type found + 1 packs found, delimiter is single 0-byte + 2 packs found, delimiter is double 0-byte +*/ +static int collect_payload(unsigned char *text_packs, int num_packs, + int pack_type, int block, + unsigned char **payload, int *payload_count, + int flag) +{ + unsigned char *pack; + int pack_no, ret, double_byte = 0; + + *payload_count = 0; + for (pack_no = 0; ; pack_no++) { + ret = search_pack(text_packs, num_packs, pack_no, pack_type, + block, &pack, &pack_no, 0); + if (ret <= 0) + break; + *payload_count += 12; + } + if (*payload_count == 0) + return 0; + *payload = burn_alloc_mem(*payload_count + 1, 1, 0); + if (*payload == NULL) + return -1; + *payload_count = 0; + for (pack_no = 0; ; pack_no++) { + ret = search_pack(text_packs, num_packs, pack_no, pack_type, + block, &pack, &pack_no, 0); + if (ret <= 0) + break; + memcpy(*payload + *payload_count, pack + 4, 12); + *payload_count += 12; + if (pack[4] & 128) + double_byte = 1; + } + (*payload)[*payload_count] = 0; + return 1 + double_byte; +} + + +/* + @param flag bit0= use double 0 as delimiter +*/ +static int is_payload_text_end(unsigned char *payload, int payload_count, + int i, int flag) +{ + if (i >= payload_count) + return 1; + if (payload[i]) + return 0; + if (!(flag & 1)) + return 1; + if (i + 1 >= payload_count) + return 1; + if (payload[i + 1] == 0) + return 1; + return 0; +} + + +/* + @param flag Bitfield for control purposes. + bit0= use double 0 as delimiter + bit1= replace TAB resp. TAB TAB by text of previous tno +*/ +static int pick_payload_text(unsigned char *payload, int payload_count, + int tno, + unsigned char **text_start, int *text_len, + int flag) +{ + int i, skipped = 0, end_found = 0; + +again:; + if (tno <= 0) { + *text_start = payload; + *text_len = 0; + for (i = 0; i < payload_count; i += 1 + (flag & 1)) { + end_found = is_payload_text_end(payload, payload_count, + i, flag & 1); + if (end_found) { + *text_len = i; + break; + } + } + return 1; + } + *text_start = NULL; + *text_len = 0; + for (i = 0; i < payload_count; i += 1 + (flag & 1)) { + end_found = is_payload_text_end(payload, payload_count, + i, flag & 1); + if (end_found) { + skipped++; + if (skipped == tno) { + *text_start = payload + (i + 1 + (flag & 1)); + } else if (skipped == tno + 1) { + *text_len = i - (*text_start - payload); + goto found; + } + } + } + if (*text_start == NULL) + return 0; + *text_len = payload_count - (*text_start - payload); + +found:; + if (flag & 2) { + /* If TAB resp. TAB TAB, then look back */ + if (flag & 1) { + if (*text_len == 2) { + if ((*text_start)[0] == '\t' && + (*text_start)[1] == '\t') { + skipped = 0; + tno--; + goto again; + } + } + } else if (*text_len == 1) { + if ((*text_start)[0] == '\t') { + skipped = 0; + tno--; + goto again; + } + } + } + return 1; +} + + +static int write_v07t_textline(unsigned char *text_packs, int num_packs, + int pack_type, int block, + int tno, int first_tno, char *spec, + char **respt, int *result_len, int flag) +{ + unsigned char *payload = NULL, *text_start; + int ret, payload_count = 0, text_len, tab_flag = 0; + char msg[80]; + + if ((pack_type >= 0x80 && pack_type <= 0x85) || pack_type == 0x8e) + tab_flag = 2; + ret = collect_payload(text_packs, num_packs, pack_type, block, + &payload, &payload_count, 0); + if(ret > 0) { + ret = pick_payload_text(payload, payload_count, tno, + &text_start, &text_len, + (ret == 2) | tab_flag); + if (ret > 0) { + if (tno > 0 && strcmp(spec, "ISRC") == 0) + sprintf(msg, "%s %-2.2d", + spec, tno + first_tno - 1); + else if (tno > 0) + sprintf(msg, "Track %-2.2d %s", + tno + first_tno - 1, spec); + else + strcpy(msg, spec); + write_v07t_line(respt, msg, + (char *) text_start, text_len, + result_len, flag & 1); + ret = 1; + } + } + BURN_FREE_MEM(payload); + return ret; +} + + +static int report_track(unsigned char *text_packs, int num_packs, + int block, int tno, int first_tno, + char **respt, int *result_len, int flag) +{ + int ret, i; + static char *track_specs[6] = { + "Title", "Artist", "Songwriter", "Composer", + "Arranger", "Message" + }; + + for (i = 0; i < 6; i++) { + ret = write_v07t_textline(text_packs, num_packs, 0x80 + i, + block, tno, first_tno, + track_specs[i], respt, result_len, + flag & 1); + if (ret < 0) + return -1; + } + ret = write_v07t_textline(text_packs, num_packs, 0x8e, block, + tno, first_tno, + "ISRC", respt, result_len, flag & 1); + if (ret < 0) + return -1; + return 1; +} + + +/* + @param flag Bitfield for control purposes. + bit0= Do not store text in result but only determine + the minimum size for the result array. + It is permissible to submit result == NULL. + Submit the already occupied size as result_size. + @return > 0 tells the number of valid text bytes in result resp. + with flag bit0 the prediction of that number. + This does not include the trailing 0-byte. + = 0 indicates that the block is not present + < 0 indicates failure. +*/ +static int report_block(unsigned char *text_packs, int num_packs, + int block, int first_tno, int last_tno, int char_code, + char *result, int result_size, int flag) +{ + char *respt = NULL; + unsigned char *pack, *payload = NULL; + int result_len = 0, pack_no, ret, i, lang, payload_count = 0, genre; + char msg[80]; + static char *languages[] = { + BURN_CDTEXT_LANGUAGES_0X00, + BURN_CDTEXT_FILLER, + BURN_CDTEXT_LANGUAGES_0X45 + }; + static char *volume_specs[7] = { + "Album Title", "Artist Name", "Songwriter", "Composer", + "Arranger", "Album Message", "Catalog Number", + }; + static char *genres[BURN_CDTEXT_NUM_GENRES] = { + BURN_CDTEXT_GENRE_LIST + }; + + /* Search for any pack of the block. But do not accept 0x8f as first.*/ + ret = search_pack(text_packs, num_packs, 0, -1, block, + &pack, &pack_no, 0); + if (ret <= 0) + return 0; + if (pack[0] == 0x8f) + return 0; + + if (flag & 1) { + result_len = result_size; + } else { + respt = result + result_size; + } + write_v07t_line(&respt, "Input Sheet Version", "0.7T", -1, &result_len, + flag & 1); + sprintf(msg, "Libburn report of CD-TEXT Block %d", block); + write_v07t_line(&respt, "Remarks ", msg, -1, &result_len, + flag & 1); + write_v07t_line(&respt, "Text Code ", + char_code == 0 ? "8859" : char_code == 0x01 ? "ASCII" : "MS-JIS", + -1, &result_len, flag & 1); + + pack_no = 0; + for (i = 0; i < 3; i++) { + ret = search_pack(text_packs, num_packs, pack_no, 0x8f, -1, + &pack, &pack_no, 0); + if (ret <= 0) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002019f, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "No third CD-TEXT pack 0x8f found. No language code defined", + 0, 0); + goto failure; + } + pack_no++; + } + lang = pack[8 + block]; + if (lang > 127) { + sprintf(msg, "CD-TEXT with unknown language code %2.2x", + (unsigned int) lang); + libdax_msgs_submit(libdax_messenger, -1, 0x0002019f, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + goto failure; + } + write_v07t_line(&respt, "Language Code", languages[lang], -1, + &result_len, flag & 1); + + for (i = 0; i < 7; i++) { + ret = write_v07t_textline(text_packs, num_packs, 0x80 + i, + block, 0, 0, volume_specs[i], + &respt, &result_len, + flag & 1); + if (ret < 0) + goto failure; + } + + ret = collect_payload(text_packs, num_packs, 0x87, block, + &payload, &payload_count, 0); + if(ret > 0) { + genre = (payload[0] << 8) | payload[1]; + if (genre < BURN_CDTEXT_NUM_GENRES) + strcpy(msg, genres[genre]); + else + sprintf(msg, "0x%-4.4x", (unsigned int) genre); + write_v07t_line(&respt, "Genre Code", msg, + -1, &result_len, flag & 1); + write_v07t_line(&respt, "Genre Information", + (char *) payload + 2, + -1, &result_len, flag & 1); + BURN_FREE_MEM(payload); payload = NULL; + } + ret = collect_payload(text_packs, num_packs, 0x8d, block, + &payload, &payload_count, 0); + if(ret > 0) { + write_v07t_line(&respt, "Closed Information", (char *) payload, + -1, &result_len, flag & 1); + BURN_FREE_MEM(payload); payload = NULL; + } + ret = write_v07t_textline(text_packs, num_packs, 0x8e, block, 0, 0, + "UPC / EAN", &respt, &result_len, flag & 1); + if (ret < 0) + goto failure; + ret = search_pack(text_packs, num_packs, 0, 0x8f, -1, + &pack, &pack_no, 0); + if (ret < 0) + goto failure; + if (pack[7] == 0x00) + strcpy(msg, "OFF"); + else if (pack[7] == 0x03) + strcpy(msg, "ON"); + else + sprintf(msg, "0x%2.2x", (unsigned int) pack[7]); + write_v07t_line(&respt, "Text Data Copy Protection", msg, + -1, &result_len, flag & 1); + sprintf(msg, "%d", first_tno); + write_v07t_line(&respt, "First Track Number", msg, + -1, &result_len, flag & 1); + sprintf(msg, "%d", last_tno); + write_v07t_line(&respt, "Last Track Number", msg, + -1, &result_len, flag & 1); + + for (i = 0; i < last_tno - first_tno + 1; i++) { + ret = report_track(text_packs, num_packs, block, + i + 1, first_tno, + &respt, &result_len, flag & 1); + if (ret < 0) + goto failure; + } + + if (flag & 1) + return result_len; + return respt - result; + +failure:; + BURN_FREE_MEM(payload); + return -1; +} + + +/* + @param result A byte buffer of sufficient size. + It will be filled by the text for the v07t sheet file + plus a trailing 0-byte. (Be aware that double-byte + characters might contain 0-bytes, too.) + @param result_size The number of bytes in result. + To be determined by a run with flag bit0 set. + @param flag Bitfield for control purposes. + bit0= Do not store text in result but only determine + the minimum size for the result array. + It is permissible to submit result == NULL and + result_size == 0. + @return > 0 tells the number of valid text bytes in result resp. + with flag bit0 the prediction of that number. + This does not include the trailing 0-byte. + <= 0 indicates failure. +*/ +static int burn_make_v07t(unsigned char *text_packs, int num_packs, + int first_tno, int track_count, + char *result, int result_size, + int *char_code, int flag) +{ + int pack_no = 0, ret, block, last_tno = 0; + unsigned char *pack; + char msg[80]; + + /* >>> ??? Verify checksums ? */; + + /* Check character code, reject unknown ones */ + ret = search_pack(text_packs, num_packs, 0, 0x8f, -1, + &pack, &pack_no, 0); + if (ret <= 0) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002019f, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "No CD-TEXT pack 0x8f found. No character code defined", + 0, 0); + return 0; + } + *char_code = pack[4]; + if (*char_code != 0x00 && *char_code != 0x01 && *char_code != 0x80) { + sprintf(msg, "CD-TEXT with unknown character code %2.2x", + (unsigned int) *char_code); + libdax_msgs_submit(libdax_messenger, -1, 0x0002019f, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return 0; + } + + /* Obtain first_tno and last_tno from type 0x88 if present. */ + if (first_tno <= 0) { + if (pack[5] > 0 && pack[5] + pack[6] < 100 && + pack[5] <= pack[6]) { + first_tno = pack[5]; + last_tno = pack[6]; + } else { + sprintf(msg, + "CD-TEXT with illegal track range %d to %d", + (int) pack[5], (int) pack[6]); + libdax_msgs_submit(libdax_messenger, -1, 0x0002019f, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return 0; + } + } + if (last_tno <= 0) { + if (track_count > 0) { + last_tno = first_tno + track_count - 1; + } else { + last_tno = 99; + } + } + + /* Report content */ + result_size = 0; + for (block = 0; block < 8; block++) { + ret = report_block(text_packs, num_packs, block, + first_tno, last_tno, *char_code, + result, result_size, flag & 1); + if (ret < 0) + return ret; + if (ret == 0) + continue; + result_size = ret; + } + +#ifdef NIX + if (flag & 1) + return result_size; + return (int) strlen((char *) result); +#else /* NIX */ + + return result_size; + +#endif /* ! NIX */ +} + + +/* Convert an array of CD-TEXT packs into the text format of + Sony CD-TEXT Input Sheet Version 0.7T . + + @param text_packs Array of bytes which form CD-TEXT packs of 18 bytes + each. For a description of the format of the array, + see file doc/cdtext.txt. + No header of 4 bytes must be prepended which would + tell the number of pack bytes + 2. + This parameter may be NULL if the currently attached + array of packs shall be removed. + @param num_packs The number of 18 byte packs in text_packs. + @param start_tno The start number of track counting, if known from + CD table-of-content or orther sources. + Submit 0 to enable the attempt to read it and the + track_count from pack type 0x8f. + @param track_count The number of tracks, if known from CD table-of-content + or orther sources. + @param result Will return the buffer with Sheet text. + Dispose by free() when no longer needed. + It will be filled by the text for the v07t sheet file + plus a trailing 0-byte. (Be aware that double-byte + characters might contain 0-bytes, too.) + Each CD-TEXT language block starts by the line + "Input Sheet Version = 0.7T" + and a "Remarks" line that tells the block number. + @param char_code Returns the character code of the pack array: + 0x00 = ISO-8859-1 + 0x01 = 7 bit ASCII + 0x80 = MS-JIS (japanese Kanji, double byte characters) + The presence of a code value that is not in this list + will cause this function to fail. + @param flag Bitfield for control purposes. Unused yet. Submit 0. + @return > 0 tells the number of valid text bytes in result. + This does not include the trailing 0-byte. + <= 0 indicates failure. +*/ +int burn_make_input_sheet_v07t(unsigned char *text_packs, int num_packs, + int start_tno, int track_count, + char **result, int *char_code, int flag) +{ + int ret, result_size = 0; + + ret = burn_make_v07t(text_packs, num_packs, start_tno, track_count, + NULL, 0, char_code, 1); + if (ret <= 0) + return ret; + result_size = ret + 1; + *result = burn_alloc_mem(result_size, 1, 0); + if (*result == NULL) + return -1; + ret = burn_make_v07t(text_packs, num_packs, start_tno, track_count, + *result, result_size, char_code, 0); + if (ret <= 0) { + free(*result); + return ret; + } + return result_size - 1; +} + + diff --git a/trunk/libburn/cleanup.c b/trunk/libburn/cleanup.c new file mode 100644 index 0000000..d8b48fb --- /dev/null +++ b/trunk/libburn/cleanup.c @@ -0,0 +1,242 @@ +/* + cleanup.c , Copyright 2006 - 2011 Thomas Schmitt <scdbackup@gmx.net> + + A signal handler which cleans up an application and exits. + + Provided under GPLv2+ license within GPL projects, BSD license elsewise. +*/ + +/* + cc -g -o cleanup -DCleanup_standalonE cleanup.c +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#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. GNU/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=%.f, signum=%d\n", + (double) 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); +} + + +static char *Cleanup_signo_to_name(int signo) +{ + int i; + for(i= 0; i < signal_list_count; i++) + if(signal_list[i] == signo) + return(signal_name_list[i]); + return(""); +} + + +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) + bit8= set SIGPIPE to SIGIGN +*/ +{ + int i,j,max_sig= -1,min_sig= 0x7fffffff; + char *sig_name; + 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) { + /* Avoid to use particular SIG macros which might not be defined. + If they are defined, then their names are in the name list. + */ + if(flag & (8 | 256)) + sig_name= Cleanup_signo_to_name(i); + else + sig_name= ""; + if((flag & 8) && strcmp(sig_name, "SIGABRT") == 0) + signal(i,Cleanup_handler_generic); + else if((flag & 256) && strcmp(sig_name, "SIGPIPE") == 0) + signal(i, SIG_IGN); + 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; + printf("Strange: The system ignored a SIGSEGV: c= %u\n", (unsigned int) c); + } else { + printf("killme: %d\n",getpid()); + sleep(3600); + } + + Cleanup_set_handlers(NULL,NULL,1); + exit(0); +} + +#endif /* Cleanup_standalonE */ diff --git a/trunk/libburn/cleanup.h b/trunk/libburn/cleanup.h new file mode 100644 index 0000000..b66c731 --- /dev/null +++ b/trunk/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 GPLv2+ 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/trunk/libburn/crc.c b/trunk/libburn/crc.c new file mode 100644 index 0000000..fce2dd5 --- /dev/null +++ b/trunk/libburn/crc.c @@ -0,0 +1,642 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2012 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. + + Containing disabled code pieces from other GPL programs. + They are just quotes for reference. + + The activated code uses plain polynomial division and other primitve + algorithms to build tables of pre-computed CRC values. It then computes + the CRCs by algorithms which are derived from mathematical considerations + and from analysing the mathematical meaning of the disabled code pieces. + + The comments here are quite detailed in order to prove my own understanding + of the topic. +*/ + + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#include "crc.h" + + +/* Exploration ts B00214 : + ECMA-130, 22.3.6 "CRC field" + "This field contains the inverted parity bits. The CRC code word must be + divisible by the check polynomial. [...] + The generating polynomial shall be + G(x) = x^16 + x^12 + x^5 + 1 + " + Also known as CRC-16-CCITT, CRC-CCITT + + Used in libburn for raw write modes in sector.c. + There is also disabled code in read.c which would use it. + + ts B11222: + The same algorithm is prescribed for CD-TEXT in MMC-3 Annex J. + "CRC Field consists of 2 bytes. Initiator system may use these bytes + to check errors in the Pack. The polynomial is x^16 + x^12 + x^5 + 1. + All bits shall be inverted." + + libburn/cdtext.c uses a simple bit shifting function : crc_11021() + + + ts B20211: + Discussion why both are equivalent in respect to their result: + + Both map the bits of the given bytes to a polynomial over the finite field + of two elements "GF(2)". If bytes 0 .. M are given, then bit n of byte m + is mapped to the coefficient of x exponent (n + ((M - m) * 8) + 16). + I.e. they translate the bits into a polynomial with the highest bit + becomming the coefficient of the highest power of x. Then this polynomial + is multiplied by (x exp 16). + + The set of all such polynomials forms a commutative ring. Its addition + corresponds to bitwise exclusive or. Addition and subtraction are identical. + Multiplication with polynomials of only one single non-zero coefficient + corresponds to leftward bit shifting by the exponent of that coefficient. + The same rules apply as with elementary school arithmetics on integer + numbers, but with surprising results due to the finite nature of the + coefficient number space. + Note that multiplication is _not_ an iteration of addition here. + + Function crc_11021() performs a division with residue by the euclidian + algorithm. I.e. it splits polynomial d into quotient q(d) and residue r(d) + in respect to the polynomial p = x exp 16 + x exp 12 + x exp 5 + x exp 0 + d = p * q(d) + r(d) + where r(d) is of a polynomial degree lower than p, i.e. only x exp 15 + or lower have non-zero coefficients. + The checksum crc(D) is derived by reverse mapping (r(d) * (x exp 16)). + I.e. by mapping the coefficient of (x exp n) to bit n of the 16 bit word + crc(D). + The function result is the bit-wise complement of crc(D). + + Function crc_ccitt uses a table ccitt_table of r(d) values for the + polynomials d which represent the single byte values 0x00 to 0xff. + It computes r(d) by computing the residues of an iteratively expanded + polynomial. The expansion of the processed byte string A by the next byte B + from the input byte string happens by shifting the string 8 bits to the + left, and by oring B onto bits 0 to 7. + In the space of polynomials, the already processed polynomial "a" (image of + byte string A) gets expanded by polynomial b (the image of byte B) like this + a * X + b + where X is (x exp 8), i.e. the single coefficient polynomial of degree 8. + + The following argumentation uses algebra with commutative, associative + and distributive laws. + Valid especially with polynomials is this rule: + (1): r(a + b) = r(a) + r(b) + because r(a) and r(b) are of degree lower than degree(p) and + degree(a + b) <= max(degree(a), degree(b)) + Further valid are: + (2): r(a) = r(r(a)) + (3): r(p * a) = 0 + + The residue of this expanded polynomial can be expressed by means of the + residue r(a) which is known from the previous iteration step, and the + residue r(b) which may be looked up in ccitt_table. + r(a * X + b) + = r(p * q(a) * X + r(a) * X + p * q(b) + r(b)) + + Applying rule (1): + = r(p * q(a) * X) + r(r(a) * X) + r(p * q(b)) + r(r(b)) + + Rule (3) and rule (2): + = r(r(a) * X) + r(b) + + Be h(a) and l(a) chosen so that: r(a) = h(a) * X + l(a), + and l(a) has zero coefficients above (x exp 7), and h(a) * X has zero + coefficients below (x exp 8). (They correspond to the high and low byte + of the 16 bit word crc(A).) + So the previous statement can be written as: + = r(h(a) * X * X) + r(l(a) * X) + r(b) + + Since the degree of l(a) is lower than 8, the degree of l(a) * X is lower + than 16. Thus it cannot be divisible by p which has degree 16. + So: r(l(a) * X) = l(a) * X + This yields + = l(a) * X + r(h(a) * X * X + b) + + h(a) * X * X is the polynomial representation of the high byte of 16 bit + word crc(A). + So in the world of bit patterns the iteration step is: + + crc(byte string A expanded by byte B) + = (low_byte(crc(A)) << 8) ^ crc(high_byte(crc(A)) ^ B) + + And this is what function crc_ccitt() does, modulo swapping the exor + operants and the final bit inversion which is prescribed by ECMA-130 + and MMC-3 Annex J. + + The start value of the table driven byte shifting algorithm may be + different from the start value of an equivalent bit shifting algorithm. + This is because the final flushing by zero bits is already pre-computed + in the table. So the start value of the table driven algorithm must be + the CRC of the 0-polynomial under the start value of the bit shifting + algorithm. + This fact is not of much importance here, because the start value of + the bit shifter is 0x0000 which leads to CRC 0x0000 and thus to start + value 0x0000 with the table driven byte shifter. +*/ + + +/* Plain implementation of polynomial division on a Galois field, where + addition and subtraction both are binary exor. Euclidian algorithm. + Divisor is x^16 + x^12 + x^5 + 1 = 0x11021. + + This is about ten times slower than the table driven algorithm. +*/ +static int crc_11021(unsigned char *data, int count, int flag) +{ + int acc = 0, i; + + for (i = 0; i < count * 8 + 16; i++) { + acc = (acc << 1); + if (i < count * 8) + acc |= ((data[i / 8] >> (7 - (i % 8))) & 1); + if (acc & 0x10000) + acc ^= 0x11021; + } + return acc; +} + + +/* This is my own table driven implementation for which i claim copyright. + + Copyright (c) 2012 Thomas Schmitt <scdbackup@gmx.net> +*/ +unsigned short crc_ccitt(unsigned char *data, int count) +{ + static unsigned short crc_tab[256], tab_initialized = 0; + unsigned short acc = 0; + unsigned char b[1]; + int i; + + if (!tab_initialized) { + /* Create table of byte residues */ + for (i = 0; i < 256; i++) { + b[0] = i; + crc_tab[i] = crc_11021(b, 1, 0); + } + tab_initialized = 1; + } + /* There seems to be a speed advantage on amd64 if (acc << 8) is the + second operant of exor, and *(data++) seems faster than data[i]. + */ + for (i = 0; i < count; i++) + acc = crc_tab[(acc >> 8) ^ *(data++)] ^ (acc << 8); + + /* ECMA-130 22.3.6 and MMC-3 Annex J (CD-TEXT) want the result with + inverted bits + */ + return ~acc; +} + + +/* + This was the function inherited with libburn-0.2. + + 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 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; + } + +*/ + + +/* Exploration ts B00214 : + ECMA-130, 14.3 "EDC field" + "The EDC field shall consist of 4 bytes recorded in positions 2064 to 2067. + The error detection code shall be a 32-bit CRC applied on bytes 0 to 2063. + The least significant bit of a data byte is used first. The EDC codeword + must be divisible by the check polynomial: + P(x) = (x^16 + x^15 + x^2 + 1) . (x^16 + x^2 + x + 1) + The least significant parity bit (x^0) is stored in the most significant + bit position of byte 2067. + " + + Used for raw writing in sector.c + + + ts B20211: + Discussion why function crc_32() implements above prescription of ECMA-130. + See end of this file for the ofunction inherited with libburn-0.2. + + The mentioned polynomial product + (x^16 + x^15 + x^2 + 1) . (x^16 + x^2 + x + 1) + yields this sum of x exponents + 32 31 18 16 + 18 17 4 2 + 17 16 3 1 + 16 15 2 0 + ====================================== + 32 31 16 15 4 3 1 0 + (The number of x^18 and x^17 is divisible by two and thus 0 in GF(2).) + This yields as 33 bit number: + 0x18001801b + + If above prescription gets implemented straight forward by function + crc_18001801b(), then its results match the ones of crc_32() with all test + strings which i could invent. + + The function consists of a conventional polynomial division with reverse + input order of bits per byte. + + Further it swaps the bits in the resulting 32 bit word. That is because + sector.c:sector_headers writes the 4 bytes of crc_32() as little endian. + The ECMA-130 prescription rather demands big endianness and bit swapping + towards the normal bit order in bytes: + "The EDC field shall consist of 4 bytes recorded in positions 2064 to 2067. + [...] + The least significant parity bit (x^0) is stored in the most + significant bit position of byte 2067." + + ----------------------------------------------------------------------- +*/ + + +/* Overall bit mirroring of a 32 bit word */ +unsigned int rfl32(unsigned int acc) +{ + unsigned int inv_acc; + int i; + + inv_acc = 0; + for (i = 0; i < 32; i++) + if (acc & (1 << i)) + inv_acc |= 1 << (31 - i); + return inv_acc; +} + + +/* Plain implementation of polynomial division on a Galois field, where + addition and subtraction both are binary exor. Euclidian algorithm. + Divisor is (x^16 + x^15 + x^2 + 1) * (x^16 + x^2 + x + 1). + + This is about ten times slower than the table driven algorithm. + + @param flag bit0= do not mirror bits in input bytes and result word + (Useful for building the byte indexed CRC table) +*/ +static unsigned int crc_18001801b(unsigned char *data, int count, int flag) +{ + unsigned int acc = 0, top; + long int i; + unsigned int inv_acc; + + for (i = 0; i < count * 8 + 32; i++) { + top = acc & 0x80000000; + acc = (acc << 1); + if (i < count * 8) { + if (flag & 1) + /* Normal bit sequence of input bytes */ + acc |= ((data[i / 8] >> (7 - (i % 8))) & 1); + else + /* Bit sequence of input bytes mirrored */ + acc |= ((data[i / 8] >> (i % 8)) & 1); + } + if (top) + acc ^= 0x8001801b; + } + + if (flag & 1) + return (unsigned int) (acc & 0xffffffff); + + /* The bits of the whole 32 bit result are mirrored for ECMA-130 + output compliance and for sector.c habit to store CRC little endian + although ECMA-130 prescribes it big endian. + */ + inv_acc = rfl32((unsigned int) acc); + return inv_acc; +} + + +/* + ----------------------------------------------------------------------- + + Above discussion why crc_ccitt() and crc_11021() yield identical results + can be changed from 16 bit to 32 bit by chosing h(a) and l(a) so that: + r(a) = h(a) * X * X * X + l(a) + h(a) corresponds to the highest byte of crc(A), whereas l(a) corresponds + to the lower three bytes of crc(A). + + This yields + r(a * X + b) + = l(a) * X + r(h(a) * X * X * X * X + b) + + h(a) * X * X * X * X is the polynomial representation of the high byte of + 32 bit word crc(A). + So in the world of bit patterns we have: + + crc(byte string A expanded by byte B) + = (lowest_three_bytes(crc(A)) << 8) ^ crc(high_byte(crc(A)) ^ B) + + + Regrettably this does not yet account for the byte-internal mirroring of + bits during the conversion from bit pattern to polynomial, and during + conversion from polynomial residue to bit pattern. + + Be rfl8(D) the result of byte-internal mirroring of bit pattern D, + and mirr8(d) its corresponding polynom. + + Be now h(a) and l(a) chosen so that: r(mirr8(a)) = h(a) * X * X * X + l(a) + This corresponds to highest byte and lower three bytes of crc(A). + + r(mirr8(a) * X + mirr8(b)) + = r(h(a) * X * X * X * X) + r(l(a) * X) + r(mirr8(b)) + = l(a)) * X + r(h(a) * X * X * X * X + mirr8(b)) + + The corresponding bit pattern operation is + + crc(mirrored byte string A expanded by mirrored byte B) + = (lowest_three_bytes(crc(A)) << 8) ^ crc(high_byte(crc(A)) ^ rfl8(B)) + + This demands a final result mirroring to meet the ECMA-130 prescription. + + rfl8() can be implemented as lookup table. + + The start value of the bit shifting iteration is 0x00000000, which leads + to the same start value for the table driven byte shifting. + + The following function crc32_by_tab() yields the same results as functions + crc_18001801b() and crc_32(): + + ----------------------------------------------------------------------- +*/ + + +/* Byte-internal bit mirroring function. +*/ +unsigned int rfl8(unsigned int acc) +{ + unsigned int inv_acc; + int i, j; + + inv_acc = 0; + for (j = 0; j < 4; j++) + for (i = 0; i < 8; i++) + if (acc & (1 << (i + 8 * j))) + inv_acc |= 1 << ((7 - i) + 8 * j); + return inv_acc; +} + + +#ifdef Libburn_with_crc_illustratioN +/* Not needed for libburn. The new implementation of function crc_32() is the + one that is used. +*/ + +unsigned int crc32_by_tab(unsigned char *data, int count, int flag) +{ + static unsigned int crc_tab[256], tab_initialized = 0; + static unsigned char mirr_tab[256]; + unsigned int acc, inv_acc; + unsigned char b[1]; + int i; + + if (!tab_initialized) { + for (i = 0; i < 256; i++) { + b[0] = i; + /* Create table of non-mirrored 0x18001801b residues */ + crc_tab[i] = crc_18001801b(b, 1, 1); + /* Create table of mirrored byte values */ + mirr_tab[i] = rfl8(i); + } + tab_initialized = 1; + } + + acc = 0; + for (i = 0; i < count; i++) + acc = (acc << 8) ^ crc_tab[(acc >> 24) ^ mirr_tab[data[i]]]; + + /* The bits of the whole 32 bit result are mirrored for ECMA-130 + output compliance and for sector.c habit to store CRC little endian + although ECMA-130 prescribes it big endian. + */ + inv_acc = rfl32((unsigned int) acc); + return inv_acc; +} + +#endif /* Libburn_with_crc_illustratioN */ + + +/* + ----------------------------------------------------------------------- + + Above function yields sufficient performance, nevertheless the old function + crc_32() (see below) is faster by avoiding the additional mirror table + lookup. + A test with 10 times 650 MB on 3000 MHz amd64: + crc_18001801b : 187 s + crc32_by_tab : 27 s + crc_32 : 16 s + + So how does crc_32() avoid the application of bit mirroring to B ?. + + Inherited crc_32() performs + crc = crc32_table[(crc ^ *data++) & 0xffL] ^ (crc >> 8); + + Above function crc32_by_tab() would be + crc = crc_tab[(crc >> 24) ^ mirr_tab[*data++]] ^ (crc << 8); + + The shortcut does not change the polynomial representation of the algorithm + or the mapping from and to bit patterns. It only mirrors the bit direction + in the bytes and in the 32-bit words which are involved in the bit pattern + computation. This affects input (which is desired), intermediate state + (which is as good as unmirrored), and final output (which would be slightly + undesirable if libburn could not use the mirrored result anyway). + + Instead of the high byte (crc >> 24), the abbreviated algorithm uses + the low byte of the mirrored intermediate checksum (crc & 0xffL). + Instead of shifting the other three intermediate bytes to the left + (crc << 8), the abbreviated algorithm shifts them to the right (crc >> 8). + In both cases they overwrite the single byte that was used for computing + the table index. + + The byte indexed table of CRC values needs to hold mirrored 32 bit values. + The byte index [(crc ^ *data++) & 0xffL] would need to be mirrored, which + would eat up the gain of not mirroring the input bytes. But this mirroring + can be pre-computed into the table by exchanging each value with the value + of its mirrored index. + + So this relation exists between the CRC table crc_tab[] of crc32_by_tab() + and the table crc32_table[] of the abbreviated algorithm crc_32(): + + crc_tab[i] == rfl32(crc32_table[rfl8(i)]) + + for i={0..255}. + + I compared the generated table in crc32_by_tab() by this test + for (i = 0; i < 256; i++) { + if (rfl32(crc_tab[rfl8(i)]) != crc32_table[i] || + crc_tab[i] != rfl32(crc32_table[rfl8(i)])) { + printf("DEVIATION : i = %d\n", i); + exit(1); + } + } + No screaming abort happened. + + ----------------------------------------------------------------------- +*/ + +/* This is my own mirrored table implementation for which i claim copyright. + With gcc -O2 it shows the same efficiency as the inherited implementation + below. With -O3, -O1, or -O0 it is only slightly slower. + + Copyright (c) 2012 Thomas Schmitt <scdbackup@gmx.net> +*/ +unsigned int crc_32(unsigned char *data, int count) +{ + static unsigned int crc_tab[256], tab_initialized = 0; + unsigned int acc = 0; + unsigned char b[1]; + int i; + + if (!tab_initialized) { + /* Create table of mirrored 0x18001801b residues in + bit-mirrored index positions. + */ + for (i = 0; i < 256; i++) { + b[0] = i; + crc_tab[rfl8(i)] = rfl32(crc_18001801b(b, 1, 1)); + } + tab_initialized = 1; + } + for (i = 0; i < count; i++) + acc = (acc >> 8) ^ crc_tab[(acc & 0xff) ^ data[i]]; + + /* The bits of the whole 32 bit result stay mirrored for ECMA-130 + output 8-bit mirroring and for sector.c habit to store the CRC + little endian although ECMA-130 prescribes it big endian. + */ + return acc; +} + + +/* + ----------------------------------------------------------------------- + + This was the function inherited with libburn-0.2 which implements the + abbreviated algorithm. Its obscure existence led me to above insights. + My compliments to the (unknown) people who invented this. + + 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 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/trunk/libburn/crc.h b/trunk/libburn/crc.h new file mode 100644 index 0000000..9bf786c --- /dev/null +++ b/trunk/libburn/crc.h @@ -0,0 +1,33 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2012 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ +#ifndef BURN__CRC_H +#define BURN__CRC_H + + +#ifdef Xorriso_standalonE +/* Source module crc.c of yet unclear ancestry is excluded from GNU xorriso */ +/* ts B20219 : The functions have been re-implemented from scratch after + studying texts about CRC computation and understanding the + meaning of the underlying ECMA-130 specs. + Nevertheless, there is no need to include them into xorriso + because it does neither CD-TEXT nor raw CD writing. +*/ +#ifndef Libburn_no_crc_C +#define Libburn_no_crc_C 1 +#endif +#endif + + +#ifndef Libburn_no_crc_C + +unsigned short crc_ccitt(unsigned char *, int len); +unsigned int crc_32(unsigned char *, int len); + +#endif /* Libburn_no_crc_C */ + + +#endif /* BURN__CRC_H */ diff --git a/trunk/libburn/ddlpa.c b/trunk/libburn/ddlpa.c new file mode 100644 index 0000000..3f473ba --- /dev/null +++ b/trunk/libburn/ddlpa.c @@ -0,0 +1,621 @@ + +/* 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 + +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#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 +#ifndef O_BINARY +#define O_BINARY 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 | O_BINARY; + 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 | O_BINARY; + 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 | O_BINARY); + + */ + + 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/trunk/libburn/ddlpa.h b/trunk/libburn/ddlpa.h new file mode 100644 index 0000000..6b057c9 --- /dev/null +++ b/trunk/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/trunk/libburn/debug.c b/trunk/libburn/debug.c new file mode 100644 index 0000000..205df72 --- /dev/null +++ b/trunk/libburn/debug.c @@ -0,0 +1,28 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Provided under GPL version 2 or later. +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + + +#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; +} + + diff --git a/trunk/libburn/debug.h b/trunk/libburn/debug.h new file mode 100644 index 0000000..b566de0 --- /dev/null +++ b/trunk/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/trunk/libburn/drive.c b/trunk/libburn/drive.c new file mode 100644 index 0000000..132252a --- /dev/null +++ b/trunk/libburn/drive.c @@ -0,0 +1,3492 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <dirent.h> + +/* ts A61007 */ +/* #include <a ssert.h> */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <pthread.h> +#include <errno.h> +#include <fcntl.h> + +/* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#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" + +/* A90815 : for mmc_obtain_profile_name() */ +#include "mmc.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + +static struct burn_drive drive_array[255]; +static int drivetop = -1; + +/* ts A80410 : in init.c */ +extern int burn_support_untested_profiles; + +/* ts B10312 : in init.c */ +extern int burn_drive_role_4_allowed; + + +/* ts A61021 : the unspecific part of sg.c:enumerate_common() +*/ +int burn_setup_drive(struct burn_drive *d, char *fname) +{ + d->devname = 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; + d->erasable = 0; + d->current_profile = -1; + d->do_stream_recording = 0; + d->stream_recording_start= 0; + d->role_5_nwa = 0; + d->features = NULL; + 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; + burn_feature_descr_free(&(d->features), 0); + sg_dispose_drive(d, 0); +} + + +/* 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 || + d->busy == BURN_DRIVE_WRITING_LEADIN || + d->busy == BURN_DRIVE_WRITING_LEADOUT || + d->busy == BURN_DRIVE_WRITING_PREGAP) { + + /* 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 A80801 */ +int burn_drive_is_listed(char *path, struct burn_drive **found, int flag) +{ + int i, ret; + char *drive_adr = NULL, *off_adr = NULL; + + BURN_ALLOC_MEM(drive_adr, char, BURN_DRIVE_ADR_LEN); + BURN_ALLOC_MEM(off_adr, char, BURN_DRIVE_ADR_LEN); + + ret = burn_drive_convert_fs_adr(path, off_adr); + if (ret <= 0) + strcpy(off_adr, path); + for (i = 0; i <= drivetop; i++) { + if (drive_array[i].global_index < 0) + continue; + ret = burn_drive_d_get_adr(&(drive_array[i]), drive_adr); + if (ret <= 0) + continue; + if(strcmp(off_adr, drive_adr) == 0) { + if (found != NULL) + *found= &(drive_array[i]); + {ret= 1; goto ex;} + } + } + ret= 0; +ex:; + BURN_FREE_MEM(drive_adr); + BURN_FREE_MEM(off_adr); + return ret; +} + + +/* 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->current_is_guessed_profile || + (d->mdata->p2a_valid > 0 && + (d->mdata->cdr_write || d->mdata->cdrw_write || + d->mdata->dvdr_write || d->mdata->dvdram_write)) ) { + d->read_disc_info(d); + } else { + if (d->current_profile == -1 || d->current_is_cd_profile) + d->read_toc(d); + + /* ts A70314 , B10712 */ + if (d->status != BURN_DISC_EMPTY) + d->status = BURN_DISC_UNSUITABLE; + } + return 1; +} + +/* ts B10730 */ +/* Send a default mode page 05 to CD and DVD-R-oids */ +int burn_drive_send_default_page_05(struct burn_drive *d, int flag) +{ + struct burn_write_opts *opts; + + if (d->sent_default_page_05) + return 0; + if (!((d->status == BURN_DISC_APPENDABLE || + d->status == BURN_DISC_BLANK) && + (d->current_is_cd_profile || d->current_profile == 0x11 || + d->current_profile == 0x14 || d->current_profile == 0x15))) + return 0; + opts = burn_write_opts_new(d); + if (opts == NULL) + return -1; + if (d->status == BURN_DISC_APPENDABLE) + burn_write_opts_set_write_type(opts, + BURN_WRITE_TAO, BURN_BLOCK_MODE1); + else + burn_write_opts_set_write_type(opts, + BURN_WRITE_SAO, BURN_BLOCK_SAO); + d->send_write_parameters(d, NULL, -1, opts); + burn_write_opts_free(opts); + d->sent_default_page_05 = 1; + 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; +} + +/* @param flag bit0= accept read-only files and return 2 in this case + bit1= accept write-only files and return 3 in this case +*/ +static int burn_drive__is_rdwr(char *fname, int *stat_ret, + struct stat *stbuf_ret, + off_t *read_size_ret, int flag) +{ + int fd, is_rdwr = 1, ret, getfl_ret, st_ret, mask; + struct stat stbuf; + off_t read_size = 0; + + memset(&stbuf, 0, sizeof(struct stat)); + fd = burn_drive__fd_from_special_adr(fname); + if (fd >= 0) + st_ret = fstat(fd, &stbuf); + else + st_ret = stat(fname, &stbuf); + if (st_ret != -1) { + is_rdwr = burn_os_is_2k_seekrw(fname, 0); + ret = 1; + if (S_ISREG(stbuf.st_mode)) + read_size = stbuf.st_size; + else if (is_rdwr) + ret = burn_os_stdio_capacity(fname, 0, &read_size); + if (ret <= 0 || + read_size / (off_t) 2048 >= (off_t) 0x7ffffff0) + read_size = (off_t) 0x7ffffff0 * (off_t) 2048; + } + + if (is_rdwr && fd >= 0) { + getfl_ret = fcntl(fd, F_GETFL); + +/* +fprintf(stderr, "LIBBURN_DEBUG: burn_drive__is_rdwr: getfl_ret = %lX , O_RDWR = %lX , & = %lX , O_RDONLY = %lX\n", (unsigned long) getfl_ret, (unsigned long) O_RDWR, (unsigned long) (getfl_ret & O_RDWR), (unsigned long) O_RDONLY); +*/ + + mask = O_RDWR | O_WRONLY | O_RDONLY; + + if (getfl_ret == -1 || (getfl_ret & mask) != O_RDWR) + is_rdwr = 0; + if ((flag & 1) && getfl_ret != -1 && + (getfl_ret & mask) == O_RDONLY) + is_rdwr = 2; + if ((flag & 2) && getfl_ret != -1 && + (getfl_ret & mask) == O_WRONLY) + is_rdwr = 3; + } + if (stat_ret != NULL) + *stat_ret = st_ret; + if (stbuf_ret != NULL) + memcpy(stbuf_ret, &stbuf, sizeof(struct stat)); + if (read_size_ret != NULL) + *read_size_ret = read_size; + return is_rdwr; +} + + +/* flag bit0= ( not needed yet: grab even if it is already grabbed ) +*/ +int burn_drive_grab_stdio(struct burn_drive *d, int flag) +{ + int stat_ret = -1, is_rdwr, ret; + struct stat stbuf; + off_t read_size= 0, size= 0; + char fd_name[40], *name_pt = NULL; + + if(d->stdio_fd >= 0) { + sprintf(fd_name, "/dev/fd/%d", d->stdio_fd); + name_pt = fd_name; + } else if (d->devname[0]) { + name_pt = d->devname; + } + if (name_pt != NULL) { + /* re-assess d->media_read_capacity and free space */ + is_rdwr = burn_drive__is_rdwr(name_pt, &stat_ret, &stbuf, + &read_size, 1 | 2); + /* despite its name : last valid address, not size */ + d->media_read_capacity = + read_size / 2048 - !(read_size % 2048); + if ((stat_ret == -1 || is_rdwr) && d->devname[0]) { + ret = burn_os_stdio_capacity(d->devname, 0, &size); + if (ret > 0) + burn_drive_set_media_capacity_remaining(d, + size); + } + } + + d->released = 0; + d->current_profile = 0xffff; + if(d->drive_role == 2 || d->drive_role == 3) { + d->status = BURN_DISC_BLANK; + } else if(d->drive_role == 4) { + if (d->media_read_capacity > 0) + d->status = BURN_DISC_FULL; + else + d->status = BURN_DISC_EMPTY; + } else if(d->drive_role == 5) { + if (stat_ret != -1 && S_ISREG(stbuf.st_mode) && + stbuf.st_size > 0) { + d->status = BURN_DISC_APPENDABLE; + if (stbuf.st_size / (off_t) 2048 + >= 0x7ffffff0) { + d->status = BURN_DISC_FULL; + d->role_5_nwa = 0x7ffffff0; + } else + d->role_5_nwa = stbuf.st_size / 2048 + + !!(stbuf.st_size % 2048); + } else + d->status = BURN_DISC_BLANK; + } else { + d->status = BURN_DISC_EMPTY; + d->current_profile = 0; + } + d->busy = BURN_DRIVE_IDLE; + return 1; +} + + +int burn_drive_grab(struct burn_drive *d, int le) +{ + int errcode; + /* ts A61125 - B20122 */ + int ret, sose, signal_action_mem = -1; + + sose = d->silent_on_scsi_error; + if (!d->released) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020189, LIBDAX_MSGS_SEV_FATAL, + LIBDAX_MSGS_PRIO_LOW, + "Drive is already grabbed by libburn", 0, 0); + return 0; + } + if(d->drive_role != 1) { + ret = burn_drive_grab_stdio(d, 0); + return ret; + } + + d->status = BURN_DISC_UNREADY; + errcode = d->grab(d); + if (errcode == 0) + return 0; + + burn_grab_prepare_sig_action(&signal_action_mem, 0); + d->busy = BURN_DRIVE_GRABBING; + + if (le) + d->load(d); + if (d->cancel || burn_is_aborting(0)) + {ret = 0; goto ex;} + + d->lock(d); + if (d->cancel || burn_is_aborting(0)) + {ret = 0; goto ex;} + + /* ts A61118 */ + d->start_unit(d); + if (d->cancel || burn_is_aborting(0)) + {ret = 0; goto ex;} + + /* ts A61202 : gave bit1 of le a meaning */ + if (!le) + d->silent_on_scsi_error = 1; + /* ts A61125 : outsourced media state inquiry aspects */ + ret = burn_drive_inquire_media(d); + if (d->cancel || burn_is_aborting(0)) + {ret = 0; goto ex;} + + burn_drive_send_default_page_05(d, 0); + if (d->cancel || burn_is_aborting(0)) + {ret = 0; goto ex;} + +ex:; + if (d->cancel || burn_is_aborting(0)) { + d->unlock(d); + d->release(d); + } + d->silent_on_scsi_error = sose; + d->busy = BURN_DRIVE_IDLE; + burn_grab_restore_sig_action(signal_action_mem, 0); + 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; + memset(&(d->thread_tid), 0, sizeof(d->thread_tid)); + d->toc_entries = 0; + d->toc_entry = NULL; + d->disc = NULL; + d->erasable = 0; + d->write_opts = NULL; + +#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 = NULL; + char *msg = NULL; + int ret; + + BURN_ALLOC_MEM(msg, char, BURN_DRIVE_ADR_LEN + 160); + + 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 */ + ret = t->grab(t); + if (ret) { + t->getcaps(t); + t->unlock(t); + t->released = 1; + } else { + /* ts A90602 */ + d->mdata->p2a_valid = -1; + sprintf(msg, "Unable to grab scanned drive %s", d->devname); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002016f, LIBDAX_MSGS_SEV_DEBUG, + LIBDAX_MSGS_PRIO_LOW, msg, 0, 0); + burn_drive_unregister(t); + t = NULL; + } + + /* ts A60821 */ + mmc_function_spy(NULL, "enumerate_common : ----- would release "); + +ex: + BURN_FREE_MEM(msg); + return t; +} + + +/* ts A61125 : model aspects of burn_drive_release */ +/* @param flag bit3= do not close d->stdio_fd +*/ +int burn_drive_mark_unready(struct burn_drive *d, int flag) +{ + /* 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->write_opts != NULL) { + burn_write_opts_free(d->write_opts); + d->write_opts = NULL; + } + if (d->disc != NULL) { + burn_disc_free(d->disc); + d->disc = NULL; + } + if (!(flag & 8)) { + if (d->stdio_fd >= 0) + close (d->stdio_fd); + d->stdio_fd = -1; + } + return 1; +} + + +/* ts A70918 : outsourced from burn_drive_release() and enhanced */ +/** @param flag bit0-2 = mode : 0=unlock , 1=unlock+eject , 2=leave locked + bit3= do not call d->release() +*/ +int burn_drive_release_fl(struct burn_drive *d, int flag) +{ + if (d->released) { + /* ts A61007 */ + 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); + if (!(flag & 8)) { + burn_drive_snooze(d, 0); + 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, flag & 8); + return 1; +} + + +/* API */ +/* ts A90824 + @param flag bit0= wake up (else start snoozing) +*/ +int burn_drive_snooze(struct burn_drive *d, int flag) +{ + if (d->drive_role != 1) + return 0; + if (flag & 1) + d->start_unit(d); + else + d->stop_unit(d); + return 1; +} + + +/* API */ +void burn_drive_release(struct burn_drive *d, int le) +{ + burn_drive_release_fl(d, !!le); +} + + +/* ts B11002 */ +/* API */ +int burn_drive_re_assess(struct burn_drive *d, int flag) +{ + int ret, signal_action_mem; + + 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 burn_drive_re_assess()", + 0, 0); + return 0; + } + burn_drive_release_fl(d, 2 | 8); + + if(d->drive_role != 1) { + ret = burn_drive_grab_stdio(d, 0); + return ret; + } + + burn_grab_prepare_sig_action(&signal_action_mem, 0); + d->busy = BURN_DRIVE_GRABBING; + ret = burn_drive_inquire_media(d); + burn_drive_send_default_page_05(d, 0); + d->busy = BURN_DRIVE_IDLE; + burn_grab_restore_sig_action(signal_action_mem, 0); + d->released = 0; + return ret; +} + + +/* 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) +{ + int ret, was_error = 0; + + if (d->drive_role == 5) { /* Random access write-only drive */ + ret = truncate(d->devname, (off_t) 0); + if (ret == -1) { + libdax_msgs_submit(libdax_messenger, -1, + 0x00020182, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Cannot truncate disk file for pseudo blanking", + 0, 0); + return; + } + d->role_5_nwa = 0; + d->cancel = 0; + d->status = BURN_DISC_BLANK; + d->busy = BURN_DRIVE_IDLE; + d->progress.sector = 0x10000; + return; + } + + d->cancel = 0; + +#ifdef Libburn_reset_progress_asynC + /* <<< This is now done in async.c */ + /* 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; +#endif /* Libburn_reset_progress_asynC */ + + d->erase(d, fast); + d->busy = BURN_DRIVE_ERASING; + +#ifdef Libburn_old_progress_looP + + /* 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); + +#else /* Libburn_old_progress_looP */ + + while (1) { + ret = d->get_erase_progress(d); + if (ret == -2 || ret > 0) + break; + if (ret == -3) + was_error = 1; + sleep(1); + } + while (1) { + ret = d->get_erase_progress(d); + if(ret == -2) + break; + if (ret == -3) + was_error = 1; + if (ret >= 0) + d->progress.sector = ret; + sleep(1); + } + +#endif /* ! Libburn_old_progress_looP */ + + d->progress.sector = 0x10000; + + /* ts A61125 : update media state records */ + burn_drive_mark_unready(d, 0); + if (d->drive_role == 1) + burn_drive_inquire_media(d); + d->busy = BURN_DRIVE_IDLE; + if (was_error) + d->cancel = 1; +} + +/* + @param flag: bit0 = fill formatted size with zeros + bit1, bit2 , bit4, bit5, 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; + int was_error = 0; + off_t num_bufs; + char msg[80]; + struct buffer *buf = NULL, *buf_mem = d->buffer; + + BURN_ALLOC_MEM(buf, struct buffer, 1); + +#ifdef Libburn_reset_progress_asynC + /* <<< This is now done in async.c */ + /* 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; +#endif /* Libburn_reset_progress_asynC */ + + stages = 1 + ((flag & 1) && size > 1024 * 1024); + d->cancel = 0; + d->busy = BURN_DRIVE_FORMATTING; + + ret = d->format_unit(d, size, flag & 0xfff6); /* forward bits */ + if (ret <= 0) + d->cancel = 1; + +#ifdef Libburn_old_progress_looP + + 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); + } + +#else /* Libburn_old_progress_looP */ + + while (1) { + ret = d->get_erase_progress(d); + if (ret == -2 || ret > 0) + break; + if (ret == -3) + was_error = 1; + sleep(1); + } + while (1) { + pseudo_sector = d->get_erase_progress(d); + if(pseudo_sector == -2) + break; + if (pseudo_sector == -3) + was_error = 1; + if (pseudo_sector >= 0) + d->progress.sector = pseudo_sector / stages; + sleep(1); + } + +#endif /* ! Libburn_old_progress_looP */ + + d->sync_cache(d); + + if (size <= 0) + goto ex; + + /* update media state records */ + burn_drive_mark_unready(d, 0); + 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 = buf_mem; + if (was_error) + d->cancel = 1; + BURN_FREE_MEM(buf); +} + + +/* 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) +{ + /* --- Part of asynchronous signal handling --- */ + /* This frequently used call may be used to react on messages from + the libburn built-in signal handler. + */ + + /* ts B00225 : + If aborting with action 2: + catch control thread after it returned from signal handler. + Let it run burn_abort(4440,...) + */ + burn_init_catch_on_abort(0); + + /* ts A70928 : inform control thread of signal in sub-threads */ + if (burn_builtin_triggered_action < 2 && burn_global_abort_level > 0) + burn_global_abort_level++; + if (burn_builtin_triggered_action < 2 && 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; + } + + /* --- End of asynchronous signal handling --- */ + + + if (p != NULL) { + memcpy(p, &(d->progress), sizeof(struct burn_progress)); + /* TODO: add mutex */ + } + return d->busy; +} + +int burn_drive_set_stream_recording(struct burn_drive *d, int recmode, + int start, int flag) +{ + + if (recmode == 1) + d->do_stream_recording = 1; + else if (recmode == -1) + d->do_stream_recording = 0; + d->stream_recording_start = start; + return(1); +} + +void burn_drive_cancel(struct burn_drive *d) +{ +/* ts B00225 : these mutexes are unnecessary because "= 1" is atomar. + pthread_mutex_lock(&d->access_lock); +*/ + if (!d->cancel) { + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + "burn_drive_cancel() was called", 0, 0); + } + d->cancel = 1; +/* + pthread_mutex_unlock(&d->access_lock); +*/ +} + + +static void strip_spaces(char *str, size_t len) +{ + char *tmp, *tmp2; + + /* Remove trailing blanks */ + for (tmp = str + len - 1; tmp >= str && (isspace(*tmp) || !*tmp); tmp--) + *tmp = 0; + /* Condense remaining blank intervals to single blanks */ + for (tmp = str; tmp < str + len - 1 && *tmp; tmp++) { + if (isspace(*tmp) && isspace(*(tmp + 1))) { + for (tmp2 = tmp + 1; tmp2 < str + len && *tmp2; tmp2++) + *(tmp2 - 1) = *tmp2; + *(tmp2 - 1) = '\0'; + tmp--; /* try same first blank again */ + } + } +} + +static int drive_getcaps(struct burn_drive *d, struct burn_drive_info *out) +{ + struct burn_scsi_inquiry_data *id; + int i, profile; + struct burn_feature_descr *feat; + + /* ts A61007 : now prevented in enumerate_common() */ +#if 0 + a ssert(d->idata); + a ssert(d->mdata); +#endif + + if(d->idata->valid <= 0) + return 0; + + id = (struct burn_scsi_inquiry_data *)d->idata; + + memcpy(out->vendor, id->vendor, sizeof(id->vendor)); + strip_spaces(out->vendor, sizeof(id->vendor)); + memcpy(out->product, id->product, sizeof(id->product)); + strip_spaces(out->product, sizeof(id->product)); + memcpy(out->revision, id->revision, sizeof(id->revision)); + strip_spaces(out->revision, sizeof(id->revision)); + strncpy(out->location, d->devname, 16); + out->location[16] = '\0'; + + if (d->mdata->p2a_valid > 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; + } else { + out->buffer_size = out->read_dvdram = out->read_dvdr = 0; + out->read_dvdrom = out->read_cdr = out->read_cdrw = 0; + out->write_dvdram = out->write_dvdr = out->write_cdr = 0; + out->write_cdrw = out->write_simulate = out->c2_errors = 0; + for (i = 0; i < d->num_profiles; i++) { + profile = (d->all_profiles[i * 4] << 8) | + d->all_profiles[i * 4 + 1]; + if (profile == 0x09) + out->write_cdr = out->read_cdr = 1; + else if (profile == 0x0a) + out->write_cdrw = out->read_cdrw = 1; + else if (profile == 0x10) + out->read_dvdrom = 1; + else if (profile == 0x11) + out->write_dvdr = out->read_dvdr = 1; + else if (profile == 0x12) + out->write_dvdram = out->read_dvdram = 1; + } + + /* Test Write bit of CD TAO, CD Mastering, DVD-R/-RW Write */ + for (i = 0x002D; i <= 0x002F; i++) + if (burn_drive_has_feature(d, i, &feat, 0)) + if (feat->data_lenght > 0) + out->write_simulate |= + !!(feat->data[0] & 4); + } + + out->drive = d; + +#ifdef Libburn_dummy_probe_write_modeS + + /* ts A91112 */ + /* Set default block types. The call d->probe_write_modes() is quite + obtrusive. It may be performed explicitely by new API call + burn_drive_probe_cd_write_modes(). + */ + if (out->write_dvdram || out->write_dvdr || + out->write_cdrw || out->write_cdr) { + out->tao_block_types = d->block_types[BURN_WRITE_TAO] = + BURN_BLOCK_MODE1 | BURN_BLOCK_RAW0; + out->sao_block_types = d->block_types[BURN_WRITE_SAO] = + BURN_BLOCK_SAO; + } else { + out->tao_block_types = d->block_types[BURN_WRITE_TAO] = 0; + out->sao_block_types = d->block_types[BURN_WRITE_SAO] = 0; + } + out->raw_block_types = d->block_types[BURN_WRITE_RAW] = 0; + out->packet_block_types = 0; + +#else /* Libburn_dummy_probe_write_modeS */ + + /* 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]; + +#endif /* ! Libburn_dummy_probe_write_modeS */ + + return 1; +} + + + +/* ts A91112 - B00114 API */ +/* Probe available CD write modes and block types. +*/ +int burn_drive_probe_cd_write_modes(struct burn_drive_info *dinfo) +{ + struct burn_drive *d = dinfo->drive; + + if (d == NULL) + return 0; + if (dinfo->write_dvdram || dinfo->write_dvdr || + dinfo->write_cdrw || dinfo->write_cdr) + d->probe_write_modes(d); + dinfo->tao_block_types = d->block_types[BURN_WRITE_TAO]; + dinfo->sao_block_types = d->block_types[BURN_WRITE_SAO]; + dinfo->raw_block_types = d->block_types[BURN_WRITE_RAW]; + dinfo->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, ret; + + /* 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 < (int) 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 <= (int) count; i++) /* invalidate */ + (*drives)[i].drive = NULL; + } else + *drives = NULL; + + for (i = 0; i < (int) 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 */ + + /* ts A90602 : This old loop is not plausible. See A70907. + while (!drive_getcaps(&drive_array[i], + &(*drives)[*n_drives])) { + sleep(1); + } + */ + /* ts A90602 : A single call shall do (rather than a loop) */ + ret = drive_getcaps(&drive_array[i], &(*drives)[*n_drives]); + if (ret > 0) + (*n_drives)++; + scanned[i / 8] |= 1 << (i % 8); + } + if (*drives != NULL && *n_drives == 0) { + free ((char *) *drives); + *drives = NULL; + } + + 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[]) +{ +#ifndef Libburn_free_all_drives_on_infO + int i; +#endif + +/* 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) +{ + return d->mdata->max_read_speed; +} + +int burn_drive_get_write_speed(struct burn_drive *d) +{ + return d->mdata->max_write_speed; +} + +/* ts A61021 : New API function */ +int burn_drive_get_min_write_speed(struct burn_drive *d) +{ + return d->mdata->min_write_speed; +} + + +/* ts A51221 */ +static char *enumeration_whitelist[BURN_DRIVE_WHITELIST_LEN]; +static int enumeration_whitelist_top = -1; + +/** Add a device to the list of permissible drives. As soon as some entry is in + the whitelist all non-listed drives are banned from enumeration. + @return 1 success, <=0 failure +*/ +int burn_drive_add_whitelist(char *device_address) +{ + char *new_item; + if(enumeration_whitelist_top+1 >= BURN_DRIVE_WHITELIST_LEN) + return 0; + enumeration_whitelist_top++; + new_item = calloc(1, 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 A80731 */ +int burn_drive_whitelist_count(void) +{ + return enumeration_whitelist_top + 1; +} + +char *burn_drive_whitelist_item(int idx, int flag) +{ + if (idx < 0 || idx > enumeration_whitelist_top) + return NULL; + return enumeration_whitelist[idx]; +} + + +static int burn_role_by_access(char *fname, int flag) +{ +/* We normally need _LARGEFILE64_SOURCE defined by the build system. + Nevertheless the system might use large address integers by default. +*/ +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + int fd; + + fd = open(fname, O_RDWR | O_LARGEFILE | O_BINARY); + if (fd != -1) { + close(fd); + return 2; + } + fd = open(fname, O_RDONLY | O_LARGEFILE | O_BINARY); + if (fd != -1) { + close(fd); + return 4; + } + fd = open(fname, O_WRONLY | O_LARGEFILE | O_BINARY); + if (fd != -1) { + close(fd); + return 5; + } + if (flag & 1) + return 0; + return 2; +} + + +/* ts A70903 : Implements adquiration of pseudo drives */ +int burn_drive_grab_dummy(struct burn_drive_info *drive_infos[], char *fname) +{ + int ret = -1, role = 0, fd; + int is_rdwr = 0, stat_ret = -1; + /* divided by 512 it needs to fit into a signed long integer */ + off_t size = ((off_t) (512 * 1024 * 1024 - 1) * (off_t) 2048); + off_t read_size = -1; + struct burn_drive *d= NULL, *regd_d; + struct stat stbuf; + + if (fname[0] != 0) { + fd = burn_drive__fd_from_special_adr(fname); + is_rdwr = burn_drive__is_rdwr(fname, &stat_ret, &stbuf, + &read_size, 1 | 2); + if (stat_ret == -1 || is_rdwr) { + ret = burn_os_stdio_capacity(fname, 0, &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) { + if (is_rdwr == 2 && + (burn_drive_role_4_allowed & 1)) + role = 4; + else if (is_rdwr == 3 && + (burn_drive_role_4_allowed & 1)) + role = 5; + else + role = 2; + if (stat_ret != -1 && role == 2 && fd == -1 && + (burn_drive_role_4_allowed & 3) == 3) + role = burn_role_by_access(fname, + !!(burn_drive_role_4_allowed & 4)); + } else + role = 0; + } else { + 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 <= 5) { + if (d->drive_role == 4) { + if (read_size > 0) + d->status = BURN_DISC_FULL; + else + d->status = BURN_DISC_EMPTY; + d->block_types[BURN_WRITE_TAO] = 0; + d->block_types[BURN_WRITE_SAO] = 0; + } else { + if (d->drive_role == 5 && stat_ret != -1 && + S_ISREG(stbuf.st_mode) && stbuf.st_size > 0 && + (burn_drive_role_4_allowed & 8)) { + d->status = BURN_DISC_APPENDABLE; + d->block_types[BURN_WRITE_SAO] = 0; + if (stbuf.st_size / (off_t) 2048 + >= 0x7ffffff0) { + d->status = BURN_DISC_FULL; + d->role_5_nwa = 0x7ffffff0; + } else + d->role_5_nwa = stbuf.st_size / 2048 + + !!(stbuf.st_size % 2048); + } else { + d->status = BURN_DISC_BLANK; + d->block_types[BURN_WRITE_SAO] = + BURN_BLOCK_SAO; + d->role_5_nwa = 0; + } + d->block_types[BURN_WRITE_TAO] = BURN_BLOCK_MODE1; + } + 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; + if (read_size >= 0) + /* despite its name : last valid address, not size */ + d->media_read_capacity = + read_size / 2048 - !(read_size % 2048); + burn_drive_set_media_capacity_remaining(d, size); + } 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 if (d->drive_role == 4) { + strcpy((*drive_infos)[0].vendor,"YOYODYNE"); + strcpy((*drive_infos)[0].product,"WARP DRIVE"); + strcpy((*drive_infos)[0].revision,"FX03"); + } else if (d->drive_role == 5) { + strcpy((*drive_infos)[0].vendor,"YOYODYNE"); + strcpy((*drive_infos)[0].product,"WARP DRIVE"); + strcpy((*drive_infos)[0].revision,"FX04"); + } 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) { + burn_drive_forget(drive_infos[0]->drive, 0); + 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 = NULL, *msgpt; + + BURN_ALLOC_MEM(msg, char, 4096); + 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); +ex:; + BURN_FREE_MEM(msg); + 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 */ +/* @param flag bit0= no debug messages + bit1= resolve only links, + do not rely on drive list for resolving via st_rdev +*/ +int burn_drive_resolve_link(char *path, char adr[], int *recursion_count, + int flag) +{ + int ret, link_target_size = 4096; + char *link_target = NULL, *msg = NULL, *link_adr = NULL, *adrpt; + struct stat stbuf; + + BURN_ALLOC_MEM(link_target, char, link_target_size); + BURN_ALLOC_MEM(msg, char, link_target_size + 100); + BURN_ALLOC_MEM(link_adr, char, link_target_size); + + if (flag & 1) + burn_drive_adr_debug_msg("burn_drive_resolve_link( %s )", + path); + if (*recursion_count >= BURN_DRIVE_MAX_LINK_DEPTH) { + if (flag & 1) + burn_drive_adr_debug_msg( + "burn_drive_resolve_link aborts because link too deep", + NULL); + {ret = 0; goto ex;} + } + (*recursion_count)++; + ret = readlink(path, link_target, link_target_size); + if (ret == -1) { + if (flag & 1) + burn_drive_adr_debug_msg("readlink( %s ) returns -1", + path); + {ret = 0; goto ex;} + } + if (ret >= link_target_size - 1) { + sprintf(msg,"readlink( %s ) returns %d (too much)", path, ret); + if (flag & 1) + burn_drive_adr_debug_msg(msg, NULL); + {ret = -1; goto ex;} + } + 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; + } + if (flag & 2) { + /* Link-only recursion */ + if (lstat(adrpt, &stbuf) == -1) { + ; + } else if((stbuf.st_mode & S_IFMT) == S_IFLNK) { + ret = burn_drive_resolve_link(adrpt, adr, + recursion_count, flag); + } else { + strcpy(adr, adrpt); + } + } else { + /* Link and device number recursion */ + ret = burn_drive_convert_fs_adr_sub(adrpt, adr, + recursion_count); + sprintf(msg,"burn_drive_convert_fs_adr( %s ) returns %d", + link_target, ret); + } + if (flag & 1) + burn_drive_adr_debug_msg(msg, NULL); +ex:; + BURN_FREE_MEM(link_target); + BURN_FREE_MEM(msg); + BURN_FREE_MEM(link_adr); + 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 = NULL, *msg = NULL; + int ret = 0, first = 1, fname_size = 4096; + struct stat stbuf; + burn_drive_enumerator_t enm; + + BURN_ALLOC_MEM(fname, char, fname_size); + BURN_ALLOC_MEM(msg, char, fname_size + 100); + + while (1) { + ret = sg_give_next_adr(&enm, fname, fname_size, 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) + {ret= -1; goto ex;} + + 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, fname_size, -1); + BURN_FREE_MEM(fname); + BURN_FREE_MEM(msg); + 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 = NULL; + + BURN_ALLOC_MEM(adr, char, 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) + {ret = 1; goto ex;} + 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) + {ret = 0; goto ex;} + {ret = 1; goto ex;} + } + } + + ret = sg_obtain_scsi_adr(path, bus_no, host_no, channel_no, + target_no, lun_no); +ex:; + BURN_FREE_MEM(adr); + 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 = NULL, *msg = NULL; + int ret = 0, first = 1, i_bus_no = -1, fname_size = 4096; + int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1; + burn_drive_enumerator_t enm; + + BURN_ALLOC_MEM(fname, char, fname_size); + BURN_ALLOC_MEM(msg, char, fname_size + 100); + + 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, fname_size, 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, fname_size, -1); + BURN_FREE_MEM(fname); + BURN_FREE_MEM(msg); + 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; + if (strncmp(path, "stdio:", 6) != 0) + 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, 0); + 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; +} + +/* API */ +/** 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; +} + + +/* API */ +int burn_lookup_device_link(char *dev_adr, char link_adr[], + char *dir_adr, char **ranks, int rank_count, int flag) +{ + DIR *dirpt= NULL; + struct dirent *entry; + struct stat link_stbuf; + char *adr= NULL, *namept, *sys_adr= NULL; + int ret, name_rank, found_rank= 0x7fffffff, dirlen, i, rec_count = 0; + static char default_ranks_data[][8] = + {"dvdrw", "cdrw", "dvd", "cdrom", "cd"}; + char *default_ranks[5]; + + link_adr[0] = 0; + if (ranks == NULL) { + for (i = 0; i < 5; i++) + default_ranks[i] = default_ranks_data[i]; + ranks = default_ranks; + rank_count= 5; + } + dirlen= strlen(dir_adr) + 1; + if (strlen(dir_adr) + 1 >= BURN_DRIVE_ADR_LEN) { + + /* >>> Issue warning about oversized directory address */; + + {ret = 0; goto ex;} + } + BURN_ALLOC_MEM(adr, char, BURN_DRIVE_ADR_LEN); + BURN_ALLOC_MEM(sys_adr, char, BURN_DRIVE_ADR_LEN); + + dirpt = opendir(dir_adr); + if (dirpt == NULL) + {ret = 0; goto ex;} + strcpy(adr, dir_adr); + strcat(adr, "/"); + namept = adr + strlen(dir_adr) + 1; + while(1) { + entry = readdir(dirpt); + if(entry == NULL) + break; + if (strlen(entry->d_name) + dirlen >= BURN_DRIVE_ADR_LEN) + continue; + strcpy(namept, entry->d_name); + if(lstat(adr, &link_stbuf) == -1) + continue; + if((link_stbuf.st_mode & S_IFMT) != S_IFLNK) + continue; + /* Determine rank and omit uninteresting ones */ + for(name_rank= 0; name_rank < rank_count; name_rank++) + if(strncmp(namept, ranks[name_rank], + strlen(ranks[name_rank])) == 0) + break; + /* we look for lowest rank */ + if(name_rank >= rank_count || + name_rank > found_rank || + (name_rank == found_rank && + strcmp(namept, link_adr + dirlen) >= 0)) + continue; + + /* Does name point to the same device as dev_adr ? */ + ret= burn_drive_resolve_link(adr, sys_adr, &rec_count, 2); + if(ret < 0) + goto ex; + if(ret == 0) + continue; + if(strcmp(dev_adr, sys_adr) == 0) { + strcpy(link_adr, adr); + found_rank= name_rank; + } + } + ret= 2; + if(found_rank < 0x7fffffff) + ret= 1; +ex:; + if(dirpt != NULL) + closedir(dirpt); + BURN_FREE_MEM(adr); + BURN_FREE_MEM(sys_adr); + 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); +} + + +/* ts B00226 : Outsourced backend of burn_abort() + @param flag bit0= do not call burn_finish() +*/ +int burn_abort_5(int patience, + int (*pacifier_func)(void *handle, int patience, int elapsed), + void *handle, int elapsed, int flag) +{ + 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; + +#ifndef NIX + time_t stdio_patience = 3; +#endif + + +/* + fprintf(stderr, + "libburn_EXPERIMENTAL: burn_abort_5(%d,%d)\n", patience, flag); +*/ + + current_time = start_time = pacifier_time = time(0); + start_time -= elapsed; + 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 <= 0 && first_round)) { + still_not_done = 0; + + for(i = 0; i < drivetop + 1; i++) { + occup = burn_drive_is_occupied(&(drive_array[i])); + if(occup == -2) + continue; + + if(drive_array[i].drive_role != 1) { + +#ifdef NIX + + /* ts A90302 + <<< this causes a race condition with drive + usage and drive disposal. + */ + drive_array[i].busy = BURN_DRIVE_IDLE; + burn_drive_forget(&(drive_array[i]), 1); + continue; + +#else /* NIX */ + + /* ts A90318 + >>> but if a pipe breaks then the drive + never gets idle. + So for now with a short patience timespan + and eventually a deliberate memory leak. + */ + if (current_time - start_time > + stdio_patience) { + drive_array[i].global_index = -1; + continue; + } + +#endif /* ! NIX */ + + } + + if(occup < 10) { + if (!drive_array[i].cancel) + burn_drive_cancel(&(drive_array[i])); + if (drive_array[i].drive_role != 1) + /* occup == -1 comes early */ + usleep(1000000); + burn_drive_forget(&(drive_array[i]), 1); + } else if(occup <= 100) { + if (!drive_array[i].cancel) + burn_drive_cancel(&(drive_array[i])); + still_not_done++; + } else if(occup <= 1000) { + still_not_done++; + } + } + first_round = 0; + + if(still_not_done == 0 || patience <= 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; + } + } + if (!(flag & 1)) + burn_finish(); + return(still_not_done == 0); +} + + +/** 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, flg = 0; + + if (patience < 0) { + patience = 0; + flg |= 1; + } + ret = burn_abort_5(patience, pacifier_func, handle, 0, flg); + return ret; +} + + +/* 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 B31110 API function */ +int burn_disc_pretend_full_uncond(struct burn_drive *d) +{ + 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->mdata->p2a_valid > 0 && d->mdata->cdrw_write) || + d->current_profile != 0x08)) { + 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 + (and it seems meaningless for non-burners). + ts A90823: Pseudo-CD U3 memory stick stalls with ATIP. + It is !cdrw_write and profile is 0x08. + */ + 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 == 5 && trackno == 0 && + d->status == BURN_DISC_APPENDABLE) { + *lba = *nwa = d->role_5_nwa; + return 1; + } + if (d->drive_role != 1) + return 0; + if (o != NULL) + d->send_write_parameters(d, NULL, -1, 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, ret; + off_t bytes, start_byte = 0; + + 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 (o != NULL) + start_byte = o->start_byte; + ret = burn_os_stdio_capacity(d->devname, start_byte, &bytes); + if (ret != 1) + bytes = d->media_capacity_remaining; + if (bytes <= 0) + bytes = (off_t) (512 * 1024 * 1024 - 1) * (off_t) 2048; + if (bytes != d->media_capacity_remaining) + burn_drive_set_media_capacity_remaining(d, bytes); + } else { + if (o != NULL) + d->send_write_parameters(d, NULL, -1, 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 A90815 : New API function */ +int burn_drive_get_all_profiles(struct burn_drive *d, int *num_profiles, + int profiles[64], char is_current[64]) +{ + int i; + + *num_profiles = d->num_profiles; + for (i = 0; i < d->num_profiles; i++) { + profiles[i] = (d->all_profiles[i * 4] << 8) | + d->all_profiles[i * 4 + 1]; + is_current[i] = d->all_profiles[i * 4 + 2] & 1; + } + return 1; +} + + +/* ts A90815 : New API function */ +int burn_obtain_profile_name(int profile_number, char name[80]) +{ + strcpy(name, mmc_obtain_profile_name(profile_number)); + return(name[0] != 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 = calloc(1, sizeof(struct burn_speed_descriptor)); + if (o == NULL) + return -1; + o->source = 0; + o->profile_loaded = -2; + o->profile_name[0] = 0; + o->wrc = 0; + o->exact = 0; + o->mrw = 0; + o->end_lba = -1; + o->write_speed = 0; + o->read_speed = 0; + + o->prev = prev; + if (prev != NULL) { + next = prev->next; + prev->next = o; + } + o->next = next; + if (next != NULL) + next->prev = o; + return 1; +} + + +/* ts A61226 */ +/* @param flag bit0= destroy whole next-chain of descriptors */ +int burn_speed_descriptor_destroy(struct burn_speed_descriptor **s, int flag) +{ + struct burn_speed_descriptor *next, *o; + + if ((*s) == NULL) + return 0; + if (flag&1) + for (o = (*s); o->prev != NULL; o = o->prev); + else + o = (*s); + next = o->next; + if (next != NULL) + next->prev = o->prev; + if (o->prev != NULL) + o->prev->next = next; + free((char *) (*s)); + (*s) = NULL; + if (flag&1) + return burn_speed_descriptor_destroy(&next, flag&1); + return 1; +} + + +/* ts A61226 */ +int burn_speed_descriptor_copy(struct burn_speed_descriptor *from, + struct burn_speed_descriptor *to, int flag) +{ + to->source = from->source; + to->profile_loaded = from->profile_loaded; + strcpy(to->profile_name, from->profile_name); + to->wrc = from->wrc; + to->exact = from->exact; + to->mrw = from->mrw; + to->end_lba = from->end_lba; + to->write_speed = from->write_speed; + to->read_speed = from->read_speed; + return 1; +} + + +/* ts A61226 : free dynamically allocated sub data of struct scsi_mode_data */ +int burn_mdata_free_subs(struct scsi_mode_data *m) +{ + burn_speed_descriptor_destroy(&(m->speed_descriptors), 1); + return 1; +} + + +/* ts A61226 : API function */ +int burn_drive_get_speedlist(struct burn_drive *d, + struct burn_speed_descriptor **speed_list) +{ + int ret; + struct burn_speed_descriptor *sd, *csd = NULL; + + (*speed_list) = NULL; + for (sd = d->mdata->speed_descriptors; sd != NULL; sd = sd->next) { + ret = burn_speed_descriptor_new(&csd, NULL, csd, 0); + if (ret <= 0) + return -1; + burn_speed_descriptor_copy(sd, csd, 0); + } + (*speed_list) = csd; + return (csd != NULL); +} + + +/* ts 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; + 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 *) + calloc(1, 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 || d->drive_role == 4) + 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, 0, &size); + burn_drive_set_media_capacity_remaining(d, size); + o->start_range_high = d->media_capacity_remaining; + 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 == 5) { + /* stdio file drive : random access write-only */ + o->start_adr = 1; + size = d->media_capacity_remaining; + burn_os_stdio_capacity(d->devname, 0, &size); + burn_drive_set_media_capacity_remaining(d, size); + + /* >>> start_range_low = file size rounded to 2048 */; + + o->start_range_high = d->media_capacity_remaining; + o->start_alignment = 2048; /* imposting a drive, not a file */ + if (s == BURN_DISC_APPENDABLE) { + if (wt == BURN_WRITE_SAO || wt == BURN_WRITE_RAW) + return 0; + o->might_do_sao = 0; + } else + 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->p2a_valid > 0 && + 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 || + d->current_profile == 0x43 + ) { + /* DVD-RAM, overwriteable DVD-RW, DVD+RW, BD-RE */ + 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; + if (o->start_range_high < 0) + o->start_range_high = 0; + } 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 || + d->current_profile == 0x41) { + /* DVD+R , DVD+R/DL , BD-R SRM */ + 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 & BURN_MODE_BITS) != + (track->mode & BURN_MODE_BITS)) + 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 = NULL, *adr2 = adr2_in; + char *conv_adr1 = NULL, *conv_adr2 = NULL; + char *npt1, *dpt1, *npt2, *dpt2; + int role1, stat_ret1, stat_ret2, conv_ret2, exact_role_matters = 0, fd; + int ret; + + BURN_ALLOC_MEM(adr1, char, BURN_DRIVE_ADR_LEN); + BURN_ALLOC_MEM(conv_adr1, char, BURN_DRIVE_ADR_LEN); + BURN_ALLOC_MEM(conv_adr2, char, BURN_DRIVE_ADR_LEN); + + role1 = burn_drive_get_drive_role(d1); + burn_drive_d_get_adr(d1, adr1); + stat_ret1 = stat(adr1, &stbuf1); + + /* If one of the candidate paths depicts an open file descriptor then + its read-write capability decides about its role and the difference + between roles 2 and 3 does matter. + */ + fd = burn_drive__fd_from_special_adr(d1->devname); + if (fd != -1) + exact_role_matters = 1; + if (strncmp(adr2, "stdio:", 6) == 0) { + adr2+= 6; + if (adr2[0] == 0) { + role2 = 0; + } else { + fd = burn_drive__fd_from_special_adr(adr2); + if (fd != -1) + exact_role_matters = 1; + ret = burn_drive__is_rdwr(adr2, NULL, NULL, NULL, + 1 | 2); + if (ret == 2 && (burn_drive_role_4_allowed & 1)) + role2 = 4; + else if (ret == 3 && (burn_drive_role_4_allowed & 1)) + role2 = 5; + else if (ret > 0) + role2 = 2; + else + role2 = 3; + if (fd == -1 && + role2 == 2 && (burn_drive_role_4_allowed & 3) == 3) + role2 = burn_role_by_access(adr2, + !!(burn_drive_role_4_allowed & 4)); + } + } + + if (strlen(adr2) >= BURN_DRIVE_ADR_LEN) + {ret = -1; goto ex;} + stat_ret2 = stat(adr2, &stbuf2); + conv_ret2 = burn_drive_convert_fs_adr(adr2, conv_adr2); + + if (!exact_role_matters) { + /* roles >= 2 have the same name space and object + interpretation */ + if (role1 >= 2) + role1 = 2; + if (role2 >= 2) + role2 = 2; + } + + if (strcmp(adr1, adr2) == 0 && role1 == role2) + {ret = 1; goto ex;} /* equal role and address */ + if (role1 == 1 && role2 == 1) { + /* MMC drive meets wannabe MMC drive */ + if (conv_ret2 <= 0) + {ret = 0; goto ex;} /* no MMC drive at adr2 */ + if (strcmp(adr1, conv_adr2) == 0) + {ret = 1; goto ex;} /* equal real MMC drives */ + {ret = 0; goto ex;} + + } else if (role1 == 0 || role2 == 0) + {ret = 0; goto ex;} /* one null-drive, one not */ + + else if (role1 != 1 && role2 != 1) { + /* pseudo-drive meets file object */ + + if (role1 != role2) + {ret = 0; goto ex;} + if (stat_ret1 == -1 || stat_ret2 == -1) { + if (stat_ret1 != -1 || stat_ret2 != -1) + {ret = 0; goto ex;} + /* 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)) + {ret = 0; goto ex;} /* basenames differ */ + stat_ret1= stat(adr1, &stbuf1); + stat_ret2= stat(adr2, &stbuf2); + if (stat_ret1 != stat_ret2) + {ret = 0; goto ex;} + /* 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) + {ret = 1; goto ex;} /* same filesystem object */ + + if (S_ISBLK(stbuf1.st_mode) && S_ISBLK(stbuf2.st_mode) && + stbuf1.st_rdev == stbuf2.st_rdev) + {ret = 1; goto ex;}/* same major,minor device number */ + if (S_ISCHR(stbuf1.st_mode) && S_ISCHR(stbuf2.st_mode) && + stbuf1.st_rdev == stbuf2.st_rdev) + {ret = 1; goto ex;}/* same major,minor device number */ + + /* Are both filesystem objects related to the same MMC drive */ + if (conv_ret2 <= 0) + {ret = 0; goto ex;} /* no MMC drive at adr2 */ + if (burn_drive_convert_fs_adr(adr1, conv_adr1) <= 0) + {ret = 0; goto ex;} /* no MMC drive at adr1 */ + if (strcmp(conv_adr1, conv_adr2) == 0) + {ret = 1; goto ex;} /* same MMC drive */ + + {ret = 0; goto ex;} /* all filesystem disguises are checked */ + + } else if (role1 == 1 && role2 != 1) { + /* MMC drive meets file object */ + + if (conv_ret2 <= 0) + {ret = 0; goto ex;} /* no MMC drive at adr2 */ + if (strcmp(adr1, conv_adr2) == 0) + {ret = 1; goto ex;} /* same MMC drive */ + {ret = 0; goto ex;} + + } else if (role1 != 1 && role2 == 1) { + /* stdio-drive meets wannabe MMC drive */ + + if (conv_ret2 <= 0) + {ret = 0; goto ex;} /* no MMC drive at adr2 */ + if (burn_drive_convert_fs_adr(adr1, conv_adr1) <= 0) + {ret = 0; goto ex;} /* no MMC drive at adr1 */ + if (strcmp(conv_adr1, conv_adr2) == 0) + {ret = 1; goto ex;} /* same MMC drive */ + {ret = 0; goto ex;} + + } + ret = 0; +ex:; + BURN_FREE_MEM(adr1); + BURN_FREE_MEM(conv_adr1); + BURN_FREE_MEM(conv_adr2); + return ret; +} + + +int burn_drive_find_by_thread_pid(struct burn_drive **d, pid_t pid, + pthread_t tid) +{ + 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 && + pthread_equal(drive_array[i].thread_tid, tid)) { + *d = &(drive_array[i]); + return 1; + } + } + return 0; +} + + +/* ts A80422 : centralizing this setting for debugging purposes +*/ +int burn_drive_set_media_capacity_remaining(struct burn_drive *d, off_t value) +{ + if (value / (off_t) 2048 > (off_t) 0x7ffffff0) + value = ((off_t) 0x7ffffff0) * (off_t) 2048; + d->media_capacity_remaining = value; + return 1; +} + + +/* ts A81215 : API */ +int burn_get_read_capacity(struct burn_drive *d, int *capacity, int flag) +{ + *capacity = d->media_read_capacity + 1; + return (d->media_read_capacity != 0x7fffffff); +} + + +/* ts A90903 : API */ +int burn_disc_get_media_id(struct burn_drive *d, + char **product_id, char **media_code1, char **media_code2, + char **book_type, int flag) +{ + int ret; + + *product_id = *media_code1 = *media_code2 = *book_type = NULL; + if (burn_drive_get_drive_role(d) != 1) + return 0; + ret = mmc_get_media_product_id(d, + product_id, media_code1, media_code2, book_type, + flag & 1); + return ret; +} + + +/* ts A90909 : API */ +/** + @param valid Replies bits which indicate the validity of other reply + parameters or the state of certain CD info bits: + bit0= disc_type valid + bit1= disc_id valid + bit2= bar_code valid + bit3= disc_app_code valid + bit4= Disc is unrestricted (URU bit) + bit5= Disc is nominally erasable (Erasable bit) + This will be set with overwriteable media which + libburn normally considers to be unerasable blank. +*/ +int burn_disc_get_cd_info(struct burn_drive *d, char disc_type[80], + unsigned int *disc_id, char bar_code[9], int *app_code, + int *valid) +{ + if (d->disc_type == 0x00) { + strcpy(disc_type, "CD-DA or CD-ROM"); + } else if (d->disc_type == 0x10) { + strcpy(disc_type, "CD-I"); + } else if (d->disc_type == 0x20) { + strcpy(disc_type, "CD-ROM XA"); + } else { + strcpy(disc_type, "undefined"); + } + *disc_id = d->disc_id; + memcpy(bar_code, d->disc_bar_code, 8); + bar_code[8]= 0; + *app_code = d->disc_app_code; + *valid = d->disc_info_valid; + return 1; +} + + +/* ts B00924 : API */ +int burn_disc_get_bd_spare_info(struct burn_drive *d, + int *alloc_blocks, int *free_blocks, int flag) +{ + int ret; + + if (burn_drive_get_drive_role(d) != 1) + return 0; + *alloc_blocks = *free_blocks = 0; + ret = mmc_get_bd_spare_info(d, alloc_blocks, free_blocks, 0); + return ret; +} + + +/* ts B10801 : API */ +int burn_disc_get_phys_format_info(struct burn_drive *d, int *disk_category, + char **book_name, int *part_version, int *num_layers, + int *num_blocks, int flag) +{ + int ret; + + if (burn_drive_get_drive_role(d) != 1) + return 0; + *disk_category = *part_version = *num_layers = *num_blocks = 0; + ret = mmc_get_phys_format_info(d, disk_category, book_name, + part_version, num_layers, num_blocks, 0); + return ret; +} + + + +/* ts B10525 : API */ +int burn_disc_next_track_is_damaged(struct burn_drive *d, int flag) +{ + return d->next_track_damaged; +} + + +/* ts B11201 : API */ +/* Read the CD-TEXT data from the Lead-in of an Audio CD +*/ +int burn_disc_get_leadin_text(struct burn_drive *d, + unsigned char **text_packs, int *num_packs, + int flag) +{ + int ret; + + ret = mmc_get_leadin_text(d, text_packs, num_packs, 0); + return ret; +} + + +/* ts B31023 API */ +/* Inquire for DVD-RW failure of TAO +*/ +int burn_drive_was_feat21_failure(struct burn_drive *d) +{ + return !!d->was_feat21h_failure; +} + + +/* ts B40106 */ +int burn_feature_descr_new(struct burn_feature_descr **new, + unsigned char *descr, int descr_len, int flag) +{ + struct burn_feature_descr *o; + + *new = NULL; + if (descr_len < 4) + return 0; + (*new) = o = calloc(1, sizeof(struct burn_speed_descriptor)); + if (o == NULL) + return -1; + o->feature_code = (descr[0] << 8) | descr[1]; + o->flags = descr[2]; + if (descr[3] > descr_len - 4) + o->data_lenght = 0; + else + o->data_lenght = descr[3]; + o->data = NULL; + o->next = NULL; + if (o->data_lenght > 0) { + o->data = calloc(1, o->data_lenght); + if (o->data == NULL) { + burn_feature_descr_free(new, 0); + return -1; + } + memcpy(o->data, descr + 4, o->data_lenght); + } + return 1; +} + + +/* ts B40106 */ +int burn_feature_descr_free(struct burn_feature_descr **descr, int flag) +{ + struct burn_feature_descr *o, *next; + + if (*descr == NULL) + return 0; + for (o = *descr; o != NULL; o = next) { + next = o->next; + if (o->data != NULL) + free(o->data); + free((char *) o); + } + *descr = NULL; + return 1; +} + + +/* ts B40107 */ +int burn_drive_has_feature(struct burn_drive *d, int feature_code, + struct burn_feature_descr **descr, int flag) +{ + struct burn_feature_descr *o; + + for (o = d->features; o != NULL; o = o->next) { + if (o->feature_code == feature_code) { + if (descr != NULL) + *descr = o; + return 1; + } + } + return 0; +} + + diff --git a/trunk/libburn/drive.h b/trunk/libburn/drive.h new file mode 100644 index 0000000..752a847 --- /dev/null +++ b/trunk/libburn/drive.h @@ -0,0 +1,173 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + +#ifndef __DRIVE +#define __DRIVE + +#include "libburn.h" +#include "toc.h" +#include "structure.h" +#include <pthread.h> + +struct burn_drive; +struct command; +struct mempage; +struct scsi_mode_data; +struct burn_speed_descriptor; +struct burn_feature_descr; + +#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, int flag); + + +/* 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 , tid */ +int burn_drive_find_by_thread_pid(struct burn_drive **d, pid_t pid, + pthread_t tid); + + +/* ts A51221 - A80731 : Whitelist inquiry functions */ +int burn_drive_is_banned(char *device_address); +int burn_drive_whitelist_count(void); +char *burn_drive_whitelist_item(int idx, int flag); + + +/* ts A80801 */ +int burn_drive_is_listed(char *path, struct burn_drive **found, int flag); + + +/* ts B00226 : Outsourced backend of burn_abort() + @param elapsed to be subtracted from start time + @param flag bit0= do not shutdown the library +*/ +int burn_abort_5(int patience, + int (*pacifier_func)(void *handle, int patience, int elapsed), + void *handle, int elapsed, int flag); + +/* ts B10730 */ +/* Send a default mode page 05 to CD and DVD-R-oids */ +int burn_drive_send_default_page_05(struct burn_drive *d, int flag); + + +/* ts B40106 */ +int burn_feature_descr_new(struct burn_feature_descr **new, + unsigned char *descr, int descr_len, int flag); + +/* ts B40106 */ +int burn_feature_descr_free(struct burn_feature_descr **new, int flag); + +/* ts B40107 */ +int burn_drive_has_feature(struct burn_drive *d, int feature_code, + struct burn_feature_descr **descr, int flag); + +int burn_drive_grab_stdio(struct burn_drive *d, int flag); + +#endif /* __DRIVE */ diff --git a/trunk/libburn/ecma130ab.c b/trunk/libburn/ecma130ab.c new file mode 100644 index 0000000..7b9c6f6 --- /dev/null +++ b/trunk/libburn/ecma130ab.c @@ -0,0 +1,855 @@ + +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +/* ts A91016 : libburn/ecma130ab.c is the replacement for old libburn/lec.c + + Copyright 2009, Thomas Schmitt <scdbackup@gmx.net>, libburnia-project.org + Provided under GPL version 2 or later. + + This code module implements the production of RSPC parity bytes (P- and Q- + parity) and the scrambling of raw CD-ROM sectors as specified in ECMA-130: + http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-130.pdf + + The following statements about Galois Fields have been learned mostly from + http://www.cs.utk.edu/~plank/plank/papers/CS-96-332.pdf + by James S. Plank after an e-mail exchange with Norbert Preining. + + The output has been compared with the output of the old code of libburn + which was labeled "borrowed HEAVILY from cdrdao" and claimed by Joerg + Schilling to stem from code by Heiko Eissfeldt. + + ------------------------------------------------------------------------- + Note: In this text, "^" denotes exponentiation and not the binary exor + operation. Confusingly in the C code "^" is said exor. + Note: This is not C1, C2 which is rather mentioned in ECMA-130 Annex C and + always performed inside the drive. + ------------------------------------------------------------------------- + + + RSPC resp. P- and Q-Parity + + ECMA-130 Annex A prescribes to compute the parity bytes for P-columns and + Q-diagonals by RSPC based on a Galois Field GF(2^8) with enumerating + polynomials x^8+x^4+x^3+x^2+1 (i.e. 0x11d) and x^1 (i.e. 0x02). + Bytes 12 to 2075 of a audio-sized sector get ordered in two byte words + as 24 rows and 43 columns. Then this matrix is split into a LSB matrix + and a MSB matrix of the same layout. Parity bytes are to be computed + from these 8-bit values. + 2 P-bytes cover each column of 24 bytes. They get appended to the matrix + as rows 24 and 25. + 2 Q-bytes cover each the 26 diagonals of the extended matrix. + + Both parity byte pairs have to be computed so that extended rows or + diagonals match this linear equation: + H x V = (0,0) + H is a 2-row matrix of size n matching the length of the V ectors + [ 1 1 ... 1 1 ] + [ x^(n-1) x^(n-2) x^1 1 ] + Vp represents a P-row. It is a byte vector consisting of row bytes at + position 0 to 23 and the two parity bytes which shall be determined + at position 24 and 25. So Hp has 26 columns. + Vq represents a Q-diagonal. It is a byte vector consisting of diagonal + bytes at position 0 to 42 and the two parity bytes at position 43 and 44. + So Hq has 45 columns. The Q-diagonals cover P-parity bytes. + + By applying some high school algebra one gets the parity bytes b0, b1 of + vector V = (n_payload_bytes, b0 , b1) as + + b0 = ( H[n] * SUM(n_payload_bytes) - H[0..(n-1)] x n_payload_bytes ) + / (H[n+1] - H[n]) + b1 = - SUM(n_payload_bytes) - b0 + + H[i] is the i-the element of the second row of matrix H. E.g. H[0] = x^(n-1) + The result has to be computed by Galois field arithmetics. See below. + + The P-parity bytes of each column get reunited as LSB and MSB of two words. + word1 gets written to positions 1032 to 1074, word0 to 1075 to 1117. + The Q-parity bytes of each diagonal get reunited too. word1 goes to 1118 + to 1143, word0 to 1144 to 1169. + >>> I do not read this swap of word1 and word0 from ECMA-130 Annex A. + >>> But the new output matches the old output only if it is done that way. + >>> See correctness reservation below. + + Algebra on Galois fields is the same as on Rational Numbers. + But arithmetics is defined by operations on polynomials rather than the + usual integer arithmetics on binary numbers. + Addition and subtraction are identical with the binary exor operator. + Multiplication and division would demand polynomial division, e.g. by the + euclidian algorithm. The computing path over logarithms and powers follows + algebra and allows to reduce the arithmetic task to table lookups, additions + modulo 255, and exor operations. Note that the logarithms are natural + numbers, not polynomials. They get added or subtracted by the usual addition + (not by exor) and their polynomial power depends on their value modulo 255. + + Needed are a logarithm table and a power table (or inverse logarithm table) + for Galois Field GF(2^8) which will serve to perform the peculiar + multiplication and division operation of Galois fields. + + The power table is simply an enumeration of x^n accorting to + GF-multiplication. It also serves as second line of matrix H for the parity + equations: + Hp[i] = gfpow[25-i] , i out of {0,..,25} + Hq[i] = gfpow[44-i] , i out of {0,..,44} + + The logarithm table is the inverse permutation of the power table. + + Some simplifications apply to the implementation: + In the world of Galois fields there is no difference between - and +. + The term (H[n+1] - H[n]) is constant: 3. + + ------------------------------------------------------------------------- + + + Scrambling + + ECMA-130 Annex B prescribes to exor the byte stream of an audio-sized sector + with a sequence of pseudo random bytes. It mentions polynomial x^15+x+1 and + a 15-bit register. + It shows a diagram of a Feedback Shift Register with 16 bit boxes, though. + + Comparing this with explanations in + http://www.newwaveinstruments.com/resources/articles/m_sequence_linear_feedback_shift_register_lfsr.htm + one can recognize the diagram as a Fibonacci Implementation. But there seems + really to be one bit box too many. + + The difference of both lengths is expressed in function next_bit() by + the constants 0x3fff,0x4000 for 15 bit versus 0x7fff,0x8000 for 16 bits. + Comparing the output of both alternatives with the old scrambler output + lets 15 bit win for now. + + So the prescription is to start with 15 bit value 1, to use the lowest bit + as output, to shift the bits down by one, to exor the output bit with the + next lowest bit, and to put that exor result into bit 14 of the register. + + ------------------------------------------------------------------------- + + + Correctness Reservation + + In both cases, parity and scrambling, the goal for now is to replicate the + output of the dismissed old lec.c by output which is based on published + specs and own implementation code. Whether they comply to ECMA-130 is a + different question which can only be answered by real test cases for + raw CD recording. + + Of course this implementation will be corrected so that it really complies + to ECMA-130 as soon as evidence emerges that it does not yet. + +*/ + + +/* ------------------------------------------------------------------------- */ + + +/* Power and logarithm tables for GF(2^8), parity matrices for ECMA-130. + Generated by burn_rspc_setup_tables() and burn_rspc_print_tables(). + + The highest possible sum of gflog[] values is is 508. So the table gfpow[] + with period 255 was manually unrolled to 509 elements to avoid one modulo + 255 operation in burn_rspc_mult(). + Proposed by D. Hugh Redelmeier. + +*/ + +static unsigned char gfpow[509] = { + 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, + 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, +}; + +static unsigned char gflog[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 +}; + + +#define Libburn_use_h_matriceS 1 + +#ifdef Libburn_use_h_matriceS + +/* On my AMD 2x64 bit 3000 MHz processor h[i] costs about 7 % more time + than using gfpow[25-i] resp. gfpow[44-1]. I blame this on the more + condensed data representation which slightly increases the rate of cache + hits. + Nevertheless this effect is very likely depending on the exact cache + size and architecture. In general, using h[] saves more than 8000 + subtractions per sector. +*/ + +/* Parity matrices H as prescribed by ECMA-130 Annex A. + Actually just reverted order start pieces of gfpow[]. +*/ +static unsigned char h26[26] = { + 3, 143, 201, 234, 117, 180, 90, 45, 152, 76, + 38, 19, 135, 205, 232, 116, 58, 29, 128, 64, + 32, 16, 8, 4, 2, 1, +}; + +static unsigned char h45[45] = { + 238, 119, 181, 212, 106, 53, 148, 74, 37, 156, + 78, 39, 157, 192, 96, 48, 24, 12, 6, 3, + 143, 201, 234, 117, 180, 90, 45, 152, 76, 38, + 19, 135, 205, 232, 116, 58, 29, 128, 64, 32, + 16, 8, 4, 2, 1, +}; + +#endif /* Libburn_use_h_matriceS */ + + +/* Pseudo-random bytes which of course are exactly the same as with the + previously used code. + Generated by function print_ecma_130_scrambler(). +*/ +static unsigned char ecma_130_annex_b[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 +}; + + +/* ------------------------------------------------------------------------- */ + + +/* This is the new implementation of P- and Q-parity generation. + It needs about the same computing time as the old implementation (both + with gcc -O2 on AMD 64 bit). Measurements indicate that about 280 MIPS + are needed for 48x CD speed (7.1 MB/s). +*/ + +static unsigned char burn_rspc_mult(unsigned char a, unsigned char b) +{ + if (a == 0 || b == 0) + return 0; + /* Optimization of (a == 0 || b == 0) by D. Hugh Redelmeier + if((((int)a - 1) | ((int)b - 1)) < 0) + return 0; + */ + + return gfpow[gflog[a] + gflog[b]]; + /* % 255 not necessary because gfpow is unrolled up to index 510 */ +} + + +/* Divide by polynomial 0x03. Derived from burn_rspc_div() and using the + unrolled size of the gfpow[] array. +*/ +static unsigned char burn_rspc_div_3(unsigned char a) +{ + if (a == 0) + return 0; + return gfpow[230 + gflog[a]]; +} + + +static void burn_rspc_p0p1(unsigned char *sector, int col, + unsigned char *p0_lsb, unsigned char *p0_msb, + unsigned char *p1_lsb, unsigned char *p1_msb) +{ + unsigned char *start, b; + unsigned int i, sum_v_lsb = 0, sum_v_msb = 0; + unsigned int hxv_lsb = 0, hxv_msb = 0; + + start = sector + 12 + 2 * col; + for(i = 0; i < 24; i++) { + b = *start; + sum_v_lsb ^= b; + +#ifdef Libburn_use_h_matriceS + hxv_lsb ^= burn_rspc_mult(b, h26[i]); +#else + hxv_lsb ^= burn_rspc_mult(b, gfpow[25 - i]); +#endif + + b = *(start + 1); + sum_v_msb ^= b; + +#ifdef Libburn_use_h_matriceS + hxv_msb ^= burn_rspc_mult(b, h26[i]); +#else + hxv_msb ^= burn_rspc_mult(b, gfpow[25 - i]); +#endif + + start += 86; + } + + /* 3 = gfpow[1] ^ gfpow[0] , 2 = gfpow[1] */ + *p0_lsb = burn_rspc_div_3(burn_rspc_mult(2, sum_v_lsb) ^ hxv_lsb); + *p0_msb = burn_rspc_div_3(burn_rspc_mult(2, sum_v_msb) ^ hxv_msb); + *p1_lsb = sum_v_lsb ^ *p0_lsb; + *p1_msb = sum_v_msb ^ *p0_msb; +} + + +void burn_rspc_parity_p(unsigned char *sector) +{ + int i; + unsigned char p0_lsb, p0_msb, p1_lsb, p1_msb; + + /* Loop over P columns */ + for(i = 0; i < 43; i++) { + burn_rspc_p0p1(sector, i, &p0_lsb, &p0_msb, &p1_lsb, &p1_msb); + sector[2162 + 2 * i] = p0_lsb; + sector[2162 + 2 * i + 1] = p0_msb; + sector[2076 + 2 * i] = p1_lsb; + sector[2076 + 2 * i + 1] = p1_msb; + +#ifdef Libburn_with_lec_generatoR + if(verbous) { + printf("p %2d : %2.2X %2.2X ", i, + (unsigned int) p0_lsb, (unsigned int) p0_msb); + printf("%2.2X %2.2X ", + (unsigned int) p1_lsb, (unsigned int) p1_msb); + printf("-> %d,%d\n", 2162 + 2 * i, 2076 + 2 * i); + } +#endif /* Libburn_with_lec_generatoR */ + + } +} + + +static void burn_rspc_q0q1(unsigned char *sector, int diag, + unsigned char *q0_lsb, unsigned char *q0_msb, + unsigned char *q1_lsb, unsigned char *q1_msb) +{ + unsigned char *start, b; + unsigned int i, idx, sum_v_lsb = 0, sum_v_msb = 0; + unsigned int hxv_lsb = 0, hxv_msb = 0; + + start = sector + 12; + idx = 2 * 43 * diag; + for(i = 0; i < 43; i++) { + if (idx >= 2236) + idx -= 2236; + b = start[idx]; + sum_v_lsb ^= b; + +#ifdef Libburn_use_h_matriceS + hxv_lsb ^= burn_rspc_mult(b, h45[i]); +#else + hxv_lsb ^= burn_rspc_mult(b, gfpow[44 - i]); +#endif + + b = start[idx + 1]; + sum_v_msb ^= b; + +#ifdef Libburn_use_h_matriceS + hxv_msb ^= burn_rspc_mult(b, h45[i]); +#else + hxv_msb ^= burn_rspc_mult(b, gfpow[44 - i]); +#endif + + idx += 88; + } + /* 3 = gfpow[1] ^ gfpow[0] , 2 = gfpow[1] */ + *q0_lsb = burn_rspc_div_3(burn_rspc_mult(2, sum_v_lsb) ^ hxv_lsb); + *q0_msb = burn_rspc_div_3(burn_rspc_mult(2, sum_v_msb) ^ hxv_msb); + *q1_lsb = sum_v_lsb ^ *q0_lsb; + *q1_msb = sum_v_msb ^ *q0_msb; +} + + +void burn_rspc_parity_q(unsigned char *sector) +{ + int i; + unsigned char q0_lsb, q0_msb, q1_lsb, q1_msb; + + /* Loop over Q diagonals */ + for(i = 0; i < 26; i++) { + burn_rspc_q0q1(sector, i, &q0_lsb, &q0_msb, &q1_lsb, &q1_msb); + sector[2300 + 2 * i] = q0_lsb; + sector[2300 + 2 * i + 1] = q0_msb; + sector[2248 + 2 * i] = q1_lsb; + sector[2248 + 2 * i + 1] = q1_msb; + +#ifdef Libburn_with_lec_generatoR + if(verbous) { + printf("q %2d : %2.2X %2.2X ", i, + (unsigned int) q0_lsb, (unsigned int) q0_msb); + printf("%2.2X %2.2X ", + (unsigned int) q1_lsb, (unsigned int) q1_msb); + printf("-> %d,%d\n", 2300 + 2 * i, 2248 + 2 * i); + } +#endif /* Libburn_with_lec_generatoR */ + + } +} + +/* ------------------------------------------------------------------------- */ + + +/* The new implementation of the ECMA-130 Annex B scrambler. + It is totally unoptimized. One should make use of larger word operations. + Measurements indicate that about 50 MIPS are needed for 48x CD speed. +*/ + +void burn_ecma130_scramble(unsigned char *sector) +{ + int i; + unsigned char *s; + + s = sector + 12; + for (i = 0; i < 2340; i++) + s[i] ^= ecma_130_annex_b[i]; +} + + +/* ------------------------------------------------------------------------- */ + + +/* The following code is not needed for libburn but rather documents the + origin of the tables above. In libburn it will not be compiled. +*/ + + +#ifdef Libburn_with_lec_generatoR + + +/* This function produced the content of gflog[] and gfpow[] +*/ +static int burn_rspc_setup_tables(void) +{ + unsigned int b, l; + + memset(gflog, 0, sizeof(gflog)); + memset(gfpow, 0, sizeof(gfpow)); + b = 1; + for (l = 0; l < 255; l++) { + gfpow[l] = (unsigned char) b; + gflog[b] = (unsigned char) l; + b = b << 1; + if (b & 256) + b = b ^ 0x11d; + } + return 0; +} + + +/* This function printed the content of gflog[] and gfpow[] as C code + and compared the content with the tables of the old implementation. + h26[] and h45[] are reverted order copies of gfpow[] +*/ +static int burn_rspc_print_tables(void) +{ + int i; + + printf("static unsigned char gfpow[255] = {"); + printf("\n\t"); + for(i= 0; i < 255; i++) { + printf("%3u, ", gfpow[i]); + +#ifdef Libburn_with_old_lec_comparisoN + if(gfpow[i] != gf8_ilog[i]) + fprintf(stderr, "*** ILOG %d : %d != %d ***\n", i, gfpow[i], gf8_ilog[i]); +#endif + + if((i % 10) == 9) + printf("\n\t"); + } + printf("\n};\n\n"); + + printf("static unsigned char gflog[256] = {"); + printf("\n\t"); + for(i= 0; i < 256; i++) { + printf(" %3u,", gflog[i]); + +#ifdef Libburn_with_old_lec_comparisoN + if(gflog[i] != gf8_log[i]) + fprintf(stderr, "*** LOG %d : %d != %d ***\n", i, gflog[i], gf8_log[i]); +#endif + + if((i % 10) == 9) + printf("\n\t"); + } + printf("\n};\n\n"); + + printf("static unsigned char h26[26] = {"); + printf("\n\t"); + for(i= 0; i < 26; i++) { + printf(" %3u,", gfpow[25 - i]); + if((i % 10) == 9) + printf("\n\t"); + } + printf("\n};\n\n"); + + printf("static unsigned char h45[45] = {"); + printf("\n\t"); + for(i= 0; i < 45; i++) { + printf(" %3u,",gfpow[44 - i]); + if((i % 10) == 9) + printf("\n\t"); + } + printf("\n};\n\n"); + + return 0; +} + + +/* This code was used to generate the content of array ecma_130_annex_b[]. +*/ +static unsigned short ecma_130_fsr = 1; + +static int next_bit(void) +{ + int ret; + + ret = ecma_130_fsr & 1; + ecma_130_fsr = (ecma_130_fsr >> 1) & 0x3fff; + if (ret ^ (ecma_130_fsr & 1)) + ecma_130_fsr |= 0x4000; + return ret; +} + + +static int print_ecma_130_scrambler(void) +{ + int i, j, b; + + ecma_130_fsr = 1; + printf("static unsigned char ecma_130_annex_b[2340] = {"); + printf("\n\t"); + for (i = 0; i < 2340; i++) { + b = 0; + for (j = 0; j < 8; j++) + b |= next_bit() << j; + + printf("%3u, ", b); + if ((i % 10) == 9) + printf("\n\t"); + } + printf("\n};\n"); + return 1; +} + + +#ifdef Libburn_with_general_rspc_diV + +/* This is a general polynomial division function. + burn_rspc_div_3() has been derived from this by setting b to constant 3. +*/ +static unsigned char burn_rspc_div(unsigned char a, unsigned char b) +{ + int d; + + if (a == 0) + return 0; + if (b == 0) + return -1; + d = gflog[a] - gflog[b]; + if (d < 0) + d += 255; + return gfpow[d]; +} + +#endif /* Libburn_with_general_rspc_diV */ + + +#endif /* Libburn_with_lec_generatoR */ + diff --git a/trunk/libburn/ecma130ab.h b/trunk/libburn/ecma130ab.h new file mode 100644 index 0000000..88294e3 --- /dev/null +++ b/trunk/libburn/ecma130ab.h @@ -0,0 +1,24 @@ + +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* ts A91016 : libburn/ecma130ab.h is the replacement for old libburn/lec.h + + Copyright 2009, Thomas Schmitt <scdbackup@gmx.net>, libburnia-project.org + Provided under GPL version 2 or later. + + This code module implements the computations prescribed in ECMA-130 Annex A + and B. For explanations of the underlying mathematics see ecma130ab.c . + +*/ + +#ifndef Libburn_ecma130ab_includeD +#define Libburn_ecma130ab_includeD 1 + +void burn_rspc_parity_p(unsigned char *sector); + +void burn_rspc_parity_q(unsigned char *sector); + +void burn_ecma130_scramble(unsigned char *sector); + +#endif /* ! Libburn_ecma130ab_includeD */ + diff --git a/trunk/libburn/error.h b/trunk/libburn/error.h new file mode 100644 index 0000000..74d4f68 --- /dev/null +++ b/trunk/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/trunk/libburn/file.c b/trunk/libburn/file.c new file mode 100644 index 0000000..fcc8973 --- /dev/null +++ b/trunk/libburn/file.c @@ -0,0 +1,1083 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + + +#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 <pthread.h> + +/* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#include "source.h" +#include "libburn.h" +#include "file.h" +#include "async.h" +#include "init.h" +#include "util.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". +*/ +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) != 0) + return (off_t) 0; + if ((buf.st_mode & S_IFMT) != S_IFREG) + 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 | O_BINARY); + if (fd1 == -1) + return NULL; + if (subpath != NULL) { + fd2 = open(subpath, O_RDONLY | O_BINARY); + if (fd2 == -1) { + close(fd1); + return NULL; + } + } + fs = calloc(1, 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 = burn_alloc_mem(sizeof(struct burn_source_file), 1, 0); + 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 unsigned long sleeptime = 50000; /* 50 ms */ + + usleep(sleeptime); + return 0; +} + + +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, counted = 0; + + 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; + } + if (!counted) + fs->empty_counter++; + counted = 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); +*/ + + fs->get_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; + + burn_fifo_abort(fs, 0); + if (fs->inp != NULL) + burn_source_free(fs->inp); + + if (fs->buf != NULL) + burn_os_free_buffer(fs->buf, + ((size_t) fs->chunksize) * (size_t) fs->chunks, 0); + 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, fill; + int counted; + char *bufpt; + pthread_t thread_handle_storage; + + fs->thread_handle= &thread_handle_storage; + *((pthread_t *) fs->thread_handle)= pthread_self(); + fs->thread_pid = getpid(); + fs->thread_is_valid = 1; + + bufsize = fs->chunksize * fs->chunks; + while (!fs->end_of_consumption) { + + /* wait for enough buffer space available */ + wpos = fs->buf_writepos; + counted = 0; + 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->inp_read_size) + trans_end = 1; + } + if (free_bytes >= fs->inp_read_size) + break; + if (!counted) + fs->full_counter++; + counted = 1; + fifo_sleep(0); + } + + fill = bufsize - free_bytes - 1; + if (fill < fs->total_min_fill) + fs->total_min_fill = fill; + if (fill < fs->interval_min_fill) + fs->interval_min_fill = fill; + + /* prepare the receiving memory */ + bufpt = fs->buf + wpos; + if (trans_end) { + bufpt = burn_os_alloc_buffer( + (size_t) fs->inp_read_size, 0); + 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->inp_read_size); + else + ret = fs->inp->read_xt( fs->inp, + (unsigned char *) bufpt, fs->inp_read_size); + if (ret == 0) { + + /* >>> ??? ts B00326 */ + /* >>> report EOF of fifo input and fs->in_counter */; + + break; /* EOF */ + } else if (ret < 0) { + 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; + } + fs->in_counter += ret; + fs->put_counter++; + + /* activate read chunk */ + if (ret > fs->inp_read_size) + /* beware of ill custom burn_source */ + ret = fs->inp_read_size; + 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->inp_read_size - (bufsize - wpos)); + burn_os_free_buffer(bufpt, (size_t) fs->inp_read_size, + 0); + 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. + */ + if(fs->buf != NULL) + burn_os_free_buffer(fs->buf, + ((size_t) fs->chunksize) * (size_t) fs->chunks, 0); + fs->buf = NULL; + + fs->thread_handle= NULL; + fs->thread_is_valid = 0; + return (fs->input_error == 0); +} + + +int burn_fifo_cancel(struct burn_source *source) +{ + int ret; + struct burn_source_fifo *fs = source->data; + + ret = burn_source_cancel(fs->inp); + return ret; +} + +/* + @param flag bit0= allow larger read chunks +*/ +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 = burn_alloc_mem(sizeof(struct burn_source_fifo), 1, 0); + if (fs == NULL) + return NULL; + fs->is_started = 0; + fs->thread_handle = NULL; + fs->thread_pid = 0; + fs->thread_is_valid = 0; + fs->inp = NULL; /* set later */ + if (flag & 1) + fs->inp_read_size = 32 * 1024; + else + fs->inp_read_size = chunksize; + 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; + fs->total_min_fill = fs->interval_min_fill = 0; + fs->put_counter = fs->get_counter = 0; + fs->empty_counter = fs->full_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; +} + + +/* ts A91125 : API */ +void burn_fifo_get_statistics(struct burn_source *source, + int *total_min_fill, int *interval_min_fill, + int *put_counter, int *get_counter, + int *empty_counter, int *full_counter) +{ + struct burn_source_fifo *fs = source->data; + + *total_min_fill = fs->total_min_fill; + *interval_min_fill = fs->interval_min_fill; + *put_counter = fs->put_counter; + *get_counter = fs->get_counter; + *empty_counter = fs->empty_counter; + *full_counter = fs->full_counter; +} + + +/* ts A91125 : API */ +void burn_fifo_next_interval(struct burn_source *source, + int *interval_min_fill) +{ + struct burn_source_fifo *fs = source->data; + int size, free_bytes, ret; + char *status_text; + + *interval_min_fill = fs->interval_min_fill; + ret = burn_fifo_inquire_status(source, + &size, &free_bytes, &status_text); + if (ret < 0) + return; + fs->interval_min_fill = size - free_bytes - 1; +} + + +/* @param flag bit0= do not copy to buf but only wait until the fifo has read + bufsize or input ended. + The same happens if buf is NULL. + bit1= fill to max fifo size +*/ +int burn_fifo_fill_data(struct burn_source *source, char *buf, int bufsize, + int flag) +{ + int size, free_bytes, ret, wait_count= 0; + char *status_text; + struct burn_source_fifo *fs = source->data; + + if (buf == NULL) + flag |= 1; + + /* Eventually start fifo thread by reading 0 bytes */ + ret = fifo_read(source, (unsigned char *) NULL, 0); + if (ret<0) + {ret = 0; goto ex;} + + /* wait for at least bufsize bytes being ready */ + while (1) { + ret= burn_fifo_inquire_status(source, + &size, &free_bytes, &status_text); + if (flag & 2) { + bufsize = size - (size % fs->inp_read_size) - + fs->inp_read_size; + if (bufsize <= 0) + {ret = 0; goto ex;} + } + if (size - fs->inp_read_size < bufsize) { + if (flag & 1) { + bufsize = size - (size % fs->inp_read_size) - + fs->inp_read_size; + if (bufsize <= 0) + {ret = 0; goto ex;} + } else { + libdax_msgs_submit(libdax_messenger, -1, + 0x0002015c, LIBDAX_MSGS_SEV_FAILURE, + LIBDAX_MSGS_PRIO_HIGH, + "Fifo size too small for desired peek buffer", + 0, 0); + {ret = -1; goto ex;} + } + } + if (fs->out_counter > 0 || (ret & 4) || fs->buf == NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002015e, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Fifo is already under consumption when peeking is desired", + 0, 0); + {ret = -1; goto ex;} + } + if(size - free_bytes >= bufsize) { + + /* <<< + fprintf(stderr, + "libburn_DEBUG: after waiting cycle %d : fifo %s , %d bytes\n", + wait_count, status_text, size - free_bytes); + */ + if(!(flag & 1)) + memcpy(buf, fs->buf, bufsize); + {ret = 1; goto ex;} + } + + if (ret & 2) { + /* input has ended, not enough data arrived */ + if (flag & 1) + {ret = 0; goto ex;} + libdax_msgs_submit(libdax_messenger, -1, 0x0002015d, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Fifo input ended short of desired peek buffer size", + 0, 0); + {ret = 0; goto ex;} + } + + if (free_bytes < fs->inp_read_size) { + /* Usable fifo size filled, not enough data arrived */ + if (flag & 1) + {ret = 0; goto ex;} + libdax_msgs_submit(libdax_messenger, -1, 0x00020174, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Fifo alignment does not allow desired read size", + 0, 0); + {ret = 0; goto ex;} + } + + usleep(100000); + wait_count++; + + /* <<< + if(wait_count%10==0) + fprintf(stderr, + "libburn_DEBUG: waiting cycle %d : fifo %s , %d bytes\n", + wait_count, status_text, size - free_bytes); + */ + + } + ret = 0; +ex:; + fs->total_min_fill = fs->interval_min_fill = fs->buf_writepos; + return(ret); +} + + +/* ts A80713 : API */ +int burn_fifo_peek_data(struct burn_source *source, char *buf, int bufsize, + int flag) +{ + return burn_fifo_fill_data(source, buf, bufsize, 0); +} + + +/* ts A91125 : API */ +int burn_fifo_fill(struct burn_source *source, int bufsize, int flag) +{ + return burn_fifo_fill_data(source, NULL, bufsize, + 1 | ((flag & 1) << 1)); +} + + +/* ----------------------------- Offset source ----------------------------- */ +/* ts B00922 */ + +static void offst_free(struct burn_source *source); + +/* @param flag bit0 = do not check for burn_source_offst, do not return NULL +*/ +static struct burn_source_offst *offst_auth(struct burn_source *source, + int flag) +{ + if (source->free_data != offst_free && !(flag & 1)) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002017a, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Expected offset source object as parameter", + 0, 0); + return NULL; + } + return (struct burn_source_offst *) source->data; +} + +static off_t offst_get_size(struct burn_source *source) +{ + struct burn_source_offst *fs; + + if ((fs = offst_auth(source, 0)) == NULL) + return (off_t) 0; + return fs->nominal_size; +} + +static int offst_set_size(struct burn_source *source, off_t size) +{ + struct burn_source_offst *fs; + + if ((fs = offst_auth(source, 0)) == NULL) + return 0; + + fs->nominal_size = size; + if (fs->size <= 0 || fs->size_adjustable) + fs->size = size; + return 1; +} + +static void offst_free(struct burn_source *source) +{ + struct burn_source_offst *fs; + + if ((fs = offst_auth(source, 0)) == NULL) + return; + if (fs->prev != NULL) + offst_auth(fs->prev, 1)->next = fs->next; + if (fs->next != NULL) + offst_auth(fs->next, 1)->prev = fs->prev; + if (fs->inp != NULL) + burn_source_free(fs->inp); /* i.e. decrement refcount */ + free(source->data); +} + +static int offst_read(struct burn_source *source, unsigned char *buffer, + int size) +{ + int ret, to_read, todo; + struct burn_source_offst *fs; + + if ((fs = offst_auth(source, 0)) == NULL) + return -1; + + /* Eventually skip bytes up to start position */; + if (!fs->running) { + if (fs->prev != NULL) + fs->pos = offst_auth(fs->prev, 1)->pos; + fs->running= 1; + } + if(fs->pos < fs->start) { + todo = fs->start - fs->pos; + while (todo > 0) { + to_read = todo; + if (to_read > size) + to_read = size; + ret = burn_source_read(fs->inp, buffer, to_read); + if (ret <= 0) + return ret; + todo -= ret; + fs->pos += ret; + } + } + + /* Produce EOF if source size is exhausted. + burn_source delivers no incomplete sector buffers. + */ + if (fs->pos + size > fs->start + fs->size) + return 0; + + /* Read payload */ + ret = burn_source_read(fs->inp, buffer, size); + if (ret > 0) + fs->pos += ret; + return ret; +} + +static int offst_cancel(struct burn_source *source) +{ + int ret; + struct burn_source_offst *fs; + + if ((fs = offst_auth(source, 0)) == NULL) + return -1; + ret = burn_source_cancel(fs->inp); + return ret; +} + +struct burn_source *burn_offst_source_new( + struct burn_source *inp, struct burn_source *prev, + off_t start, off_t size, int flag) +{ + struct burn_source *src; + struct burn_source_offst *fs, *prev_fs = NULL; + + if (prev != NULL) + if ((prev_fs = offst_auth(prev, 0)) == NULL) + return NULL; /* Not type burn_source_offst */ + + fs = calloc(1, sizeof(struct burn_source_offst)); + if (fs == NULL) + return NULL; + src = burn_source_new(); + if (src == NULL) { + free((char *) fs); + return NULL; + } + src->read = NULL; + src->read_sub = NULL; + src->get_size = offst_get_size; + src->set_size = offst_set_size; + src->free_data = offst_free; + src->data = fs; + src->version= 1; + src->read_xt = offst_read; + src->cancel= offst_cancel; + fs->inp = inp; + fs->prev = prev; + fs->next = NULL; + if (prev != NULL) { + if (prev_fs->next != NULL) { + offst_auth(prev_fs->next, 1)->prev = src; + fs->next = prev_fs->next; + } + prev_fs->next = src; + if (prev_fs->start + prev_fs->size > start) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020179, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Offset source start address is before end of previous source", + 0, 0); + return NULL; + } + } + fs->start = start; + fs->size = size; + fs->size_adjustable = !(flag & 1); + fs->nominal_size = size; + fs->running = 0; + fs->pos = 0; + inp->refcount++; /* make sure inp lives longer than src */ + + return src; +} + + +/* -------------------- WAVE file extractor ------------------- */ + + +/* ts B30522 */ +/* API + @param flag Bitfield for control purposes: + bit0= Report about progress by UPDATE message + bit3= Enable DAP : "flaw obscuring mechanisms like + audio data mute and interpolate" + +*/ +int burn_drive_extract_audio(struct burn_drive *drive, + int start_sector, int sector_count, + char *target_path, int flag) +{ + int fd = -1, ret, todo, sector_no, val, min, sec, fr; + int sectors_done= 0; + off_t data_size, data_count = 0; + time_t last_pacified = 0, now; + char *msg = NULL, *buf = NULL; + + BURN_ALLOC_MEM(msg, char, 4096); + BURN_ALLOC_MEM(buf, char, 24 * 2352); + + fd = open(target_path, O_WRONLY | O_CREAT | O_BINARY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (fd == -1) { + sprintf(msg, "Cannot open disk file for writing: %.4000s", + target_path); + libdax_msgs_submit(libdax_messenger, -1, 0x000201a1, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, errno, 0); + ret = 0; goto ex; + } + + /* WAV header */ + strcpy(buf, "RIFF"); + val = 4 + 8 + 16 + 8 + sector_count * 2352; /* ChunkSize */ + burn_int_to_lsb(val, buf + 4); + strcpy(buf + 8, "WAVE"); + strcpy(buf + 12, "fmt "); + burn_int_to_lsb(16, buf + 16); /* Subchunk1Size */ + buf[20] = 1; /* AudioFormat */ + buf[21] = 0; + buf[22] = 2; /* NumChannels */ + buf[23] = 0; + burn_int_to_lsb(44100, buf + 24); /* SampleRate */ + burn_int_to_lsb(176400, buf + 28); /* ByteRate */ + buf[32] = 4; /* BlockAlign */ + buf[33] = 0; + buf[34] = 16; /* BitsPerSample */ + buf[35] = 0; + strcpy(buf + 36, "data"); + burn_int_to_lsb(sector_count * 2352, buf + 40); /* Subchunk2Size */ + + ret = write(fd, buf, 44); + if (ret == -1) + goto write_error; + + /* Audio data */ + todo = sector_count; + sector_no = start_sector; + while (todo > 0) { + if (todo > 24) + data_size = 24 * 2352; + else + data_size = todo * 2352; + ret = burn_read_audio(drive, sector_no, buf, data_size, + &data_count, flag & 8); + if (ret <= 0) { + sprintf(msg, "Failure to read audio sectors"); + libdax_msgs_submit(libdax_messenger, -1, 0x000201a4, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + goto ex; + } + ret = write(fd, buf, data_count); + if (ret == -1) { +write_error:; + sprintf(msg, + "Error while writing to disk file: %.4000s", + target_path); + libdax_msgs_submit(libdax_messenger, -1, 0x000201a2, + LIBDAX_MSGS_SEV_FAILURE, + LIBDAX_MSGS_PRIO_HIGH, + msg, errno, 0); + ret = 0; goto ex; + } + todo -= data_count / 2352; + sectors_done += data_count / 2352; + sector_no += data_count / 2352; + if ((flag & 1) && (now = time(NULL)) - last_pacified >= 1) { + last_pacified = now; + burn_lba_to_msf(sectors_done, &min, &sec, &fr); + sprintf(msg, + "Minutes:seconds of audio data read: %2d:%2.2d (%6.2f MB)", + min, sec, + ((double) sectors_done) * 2352.0 / 1048576.0); + libdax_msgs_submit(libdax_messenger, -1, 0x000201a3, + LIBDAX_MSGS_SEV_UPDATE, + LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 1); + } + } + if ((flag & 1)) { + burn_lba_to_msf(sectors_done, &min, &sec, &fr); + sprintf(msg, + "Minutes:seconds of audio data read: %2d:%2.2d (%6.2f MB)", + min, sec, ((double) sectors_done) * 2352.0 / 1048576.0); + libdax_msgs_submit(libdax_messenger, -1, 0x000201a3, + LIBDAX_MSGS_SEV_UPDATE, + LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + } + ret = 1; +ex:; + BURN_FREE_MEM(buf); + BURN_FREE_MEM(msg); + if (fd != -1) + close(fd); + return ret; +} + + +/* ts B30522 */ +/* API + @param flag Bitfield for control purposes: + bit0= Report about progress by UPDATE message + bit3= Enable DAP : "flaw obscuring mechanisms like + audio data mute and interpolate" +*/ +int burn_drive_extract_audio_track(struct burn_drive *drive, + struct burn_track *track, + char *target_path, int flag) +{ + int ret; + struct burn_toc_entry toc_entry; + + burn_track_get_entry(track, &toc_entry); + if (!(toc_entry.extensions_valid & 1)) { + /* Can only happen if burn_disc_cd_toc_extensions() is skipped + in mmc_read_toc_al(). + */ + libdax_msgs_submit(libdax_messenger, -1, 0x00000004, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Internal libburn error: Outdated burn_toc_entry format encountered", + errno, 0); + return -1; + } + ret = burn_drive_extract_audio(drive, toc_entry.start_lba, + toc_entry.track_blocks, + target_path, flag & (1 | 8)); + return ret; +} + diff --git a/trunk/libburn/file.h b/trunk/libburn/file.h new file mode 100644 index 0000000..5c50e37 --- /dev/null +++ b/trunk/libburn/file.h @@ -0,0 +1,99 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2010 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#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; + + void *thread_handle; /* actually a pointer to a thread_t */ + int thread_pid; + int thread_is_valid; + + /* the burn_source for which this fifo is acting as proxy */ + struct burn_source *inp; + int inp_read_size; + + /* <<< 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; + + int total_min_fill; + int interval_min_fill; + int put_counter; + int get_counter; + int empty_counter; + int full_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); + + +/* ts B00922 */ +struct burn_source_offst { + + /* See burn_offst_source_new() */ + struct burn_source *inp; + struct burn_source *prev; + off_t start; + off_t size; + int size_adjustable; + + /* for set_size/get_size */ + int nominal_size; + + /* To help offst_free() */ + struct burn_source *next; + + /* The current reading position */ + int running; + off_t pos; + +}; + +#endif /* LIBBURN__FILE_H */ diff --git a/trunk/libburn/init.c b/trunk/libburn/init.c new file mode 100644 index 0000000..97d0098 --- /dev/null +++ b/trunk/libburn/init.c @@ -0,0 +1,664 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2013 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + + +#include <unistd.h> + +/* ts A61007 */ +/* #include <a ssert.h> */ + +#include <stdio.h> +#include <signal.h> +#include <string.h> +#include <stdlib.h> +#include <pthread.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" +#include "util.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; + +double lib_start_time; + + +/* ts A60813 : GNU/Linux: whether to use O_EXCL on open() of device files + ts B00212 : FreeBSD: whether to use flock(LOCK_EX) after open() +*/ +int burn_sg_open_o_excl = 1; + +/* ts A70403 : GNU/Linux: wether to use fcntl(,F_SETLK,) + after open() of device files */ +int burn_sg_fcntl_f_setlk = 1; + +/* ts A70314 : GNU/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; + + +/* The message returned from sg_id_string() and/or sg_initialize() +*/ +static char sg_initialize_msg[1024] = {""}; + + +/* ts A61002 */ + +#include "cleanup.h" + +/* Parameters for builtin abort handler */ +static char abort_message_prefix[81] = {"libburn : "}; +static pid_t abort_control_pid= 0; +static pthread_t abort_control_thread; +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; +int burn_builtin_signal_action = 0; /* burn_set_signal_handling() */ +volatile int burn_builtin_triggered_action = 0; /* burn_is_aborting() */ + + +/* ts A70223 : wether implemented untested profiles are supported */ +int burn_support_untested_profiles = 0; + +/* ts A91111 : + whether to log SCSI commands (to be implemented in sg-*.c) + bit0= log in /tmp/libburn_sg_command_log + bit1= log to stderr + bit2= flush every line +*/ +int burn_sg_log_scsi = 0; + + +/* ts B10312 : + Whether to map random-access readonly files to drive role 4. + Else it is role 2 overwriteable drive +*/ +int burn_drive_role_4_allowed = 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; + + lib_start_time = burn_get_time(0); + burn_support_untested_profiles = 0; + ret = burn_msgs_initialize(); + if (ret <= 0) + return 0; + ret = sg_initialize(sg_initialize_msg, 0); + if (ret <= 0) { + libdax_msgs_submit(libdax_messenger, -1, + 0x00020175, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + sg_initialize_msg, 0, 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); + + sg_shutdown(0); + + burn_drive_clear_whitelist(); + + burn_running = 0; +} + + +/* ts A91226 */ +/** API function. See libburn.h */ +char *burn_scsi_transport_id(int flag) +{ + if (!burn_running) + sg_id_string(sg_initialize_msg, 0); + return sg_initialize_msg; +} + + +/* 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_ALL; + 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 = 0x00040008; + } + } + 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); + return ret; +} + + +/* ts A80202 API */ +int burn_sev_to_text(int severity_number, char **severity_name, int flag) +{ + int ret; + + ret = libdax_msgs__sev_to_text(severity_number, severity_name, 0); + return ret; +} + + +/* ts B21214 API */ +char *burn_list_sev_texts(int flag) +{ + char *sev_list; + + libdax_msgs__sev_to_text(0, &sev_list, 1); + return sev_list; +} + + +/* ts B00224 */ +char *burn_util_thread_id(pid_t pid, pthread_t tid, char text[80]) +{ + int i, l; + + sprintf(text, "[%lu,", (unsigned long int) getpid()); + l= strlen(text); + for(i= 0; i < ((int) sizeof(pthread_t)) && 2 * i < 80 - l - 3; i++) + sprintf(text + l + 2 * i, + "%2.2X", ((unsigned char *) &tid)[i]); + + sprintf(text + l + 2 * i, "]"); + return text; +} + + +/* ts B20122 */ +/* @param value 0=return rather than exit(value) +*/ +int burn_abort_exit(int value) +{ + 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); + if (value) + exit(value); + burn_global_abort_level = -2; + return(1); +} + + +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 + char text[80]; + + fprintf(stderr, "libburn_ABORT: in = %s\n", + burn_util_thread_id(getpid(), pthread_self(), text)); + fprintf(stderr, "libburn_ABORT: ctrl = %s\n", + burn_util_thread_id(abort_control_pid, abort_control_thread, + text)); + if (burn_global_signal_handler == burn_builtin_abort_handler) + fprintf(stderr, "libburn_ABORT: signal action = %d\n", + burn_builtin_signal_action); + + /* >>> find writing drives and report their tid + fprintf(stderr, "libburn_ABORT: wrt = %s\n", + burn_util_thread_id(0, burn_write_thread_id, text)); + fprintf(stderr, "libburn_ABORT: sig= %d\n", signum); + */ +#endif + + burn_builtin_triggered_action = burn_builtin_signal_action; + burn_global_abort_level = -1; + + if (burn_builtin_signal_action > 1) { + Cleanup_set_handlers(NULL, NULL, 2); + if (burn_builtin_signal_action == 4) + return -2; + fprintf(stderr,"%sABORT : Trying to shut down busy drives\n", + abort_message_prefix); + fprintf(stderr, + "%sABORT : Wait the normal burning time before any kill -9\n", + abort_message_prefix); + burn_abort_5(0, burn_abort_pacifier, abort_message_prefix, + 0, 1); + libdax_msgs_submit(libdax_messenger, -1, 0x00020177, + LIBDAX_MSGS_SEV_ABORT, LIBDAX_MSGS_PRIO_HIGH, + "Urged drive worker threads to do emergency halt", + 0, 0); + return -2; + } + + + /* ---- old deprecated stuck-in-abort-handler loop ---- */ + + /* 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(), + pthread_self()); + 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_exit(0); + return (1); +} + + +/* ts A61002 : API */ +void burn_set_signal_handling(void *handle, burn_abort_handler_t handler, + int mode) +{ + +/* + fprintf(stderr, "libburn_experimental: burn_set_signal_handling, handler==%lx mode=%d\n", (unsigned long) handler, mode); +*/ + + if(handler == NULL) { + handler = burn_builtin_abort_handler; +/* + if ((mode & ~4) == 0) + fprintf(stderr, "libburn_experimental: activated burn_builtin_abort_handler() with handle '%s'\n",(handle==NULL ? "libburn : " : (char *) handle)); +*/ + + } + strcpy(abort_message_prefix, "libburn : "); + abort_message_prefix[0] = 0; + if(handle != NULL && handler == burn_builtin_abort_handler) + strncpy(abort_message_prefix, (char *) handle, + sizeof(abort_message_prefix)-1); + abort_message_prefix[sizeof(abort_message_prefix)-1] = 0; + abort_control_pid = getpid(); + abort_control_thread = pthread_self(); + burn_builtin_signal_action = (mode >> 4) & 15; + if((mode & 11) != 0) + burn_builtin_signal_action = 0; + if(burn_builtin_signal_action > 1) + burn_builtin_triggered_action = 0; + if(burn_builtin_signal_action == 0) + burn_builtin_signal_action = 1; + Cleanup_set_handlers(handle, (Cleanup_app_handler_T) handler, + (mode & 15) | 4 | (mode & 256)); + burn_global_signal_handle = handle; + burn_global_signal_handler = handler; +} + + +/* ts B00304 : API */ +int burn_is_aborting(int flag) +{ + return burn_builtin_triggered_action; +} + + +/* ts B00225 */ +/* @return 0= no abort action 2 pending , 1= not control thread +*/ +int burn_init_catch_on_abort(int flag) +{ + if (burn_builtin_triggered_action != 2) + return 0; + if (abort_control_pid != getpid() || + abort_control_thread != pthread_self()) + return 1; + 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); + exit(1); +} + + +/* B20122 */ +/* Temporarily disable builtin actions 0,1,2 to avoid that burn_abort() + waits for its own thread to end grabbing. +*/ +int burn_grab_prepare_sig_action(int *signal_action_mem, int flag) +{ + *signal_action_mem = -1; + if (burn_global_signal_handler == burn_builtin_abort_handler && + burn_builtin_signal_action >= 0 && + burn_builtin_signal_action <= 2) { + *signal_action_mem = burn_builtin_signal_action; + burn_builtin_signal_action = 3; + } + return 1; +} + + +/* B20122 */ +/* Re-enable builtin actions 0,1,2 and perform delayed signal reactions +*/ +int burn_grab_restore_sig_action(int signal_action_mem, int flag) +{ + if (signal_action_mem >= 0) + burn_builtin_signal_action = signal_action_mem; + if (burn_is_aborting(0) && signal_action_mem >= 0) { + if (signal_action_mem == 0 || signal_action_mem == 1) { + burn_abort_exit(1); /* Never comes back */ + } else if (signal_action_mem == 2) { + burn_builtin_triggered_action = signal_action_mem; + } + } + return 1; +} + + +/* 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; +} + + +/* ts A91111 API */ +void burn_set_scsi_logging(int flag) +{ + burn_sg_log_scsi = flag & 7; +} + + +/* ts B10312 API */ +void burn_allow_drive_role_4(int allowed) +{ + burn_drive_role_4_allowed = (allowed & 0xf); +} + + +/* ts B10606 */ +void *burn_alloc_mem(size_t size, size_t count, int flag) +{ + void *pt; + + pt = calloc(count, size); + if(pt == NULL) + libdax_msgs_submit(libdax_messenger, -1, 0x00000003, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Out of virtual memory", 0, 0); + return pt; +} + diff --git a/trunk/libburn/init.h b/trunk/libburn/init.h new file mode 100644 index 0000000..b0919a6 --- /dev/null +++ b/trunk/libburn/init.h @@ -0,0 +1,63 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2013 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#ifndef BURN__INIT_H +#define BURN__INIT_H + +extern int burn_running; + +extern double lib_start_time; + +/** 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; + +extern int burn_builtin_signal_action; /* burn_set_signal_handling() */ +extern volatile int burn_builtin_triggered_action; /* burn_is_aborting() */ + + +/* ts B00225 */ +/* @return 0= no abort pending , 1= not control thread , + -1= surprisingly burn_abort returned +*/ +int burn_init_catch_on_abort(int flag); + +/* ts B10606 */ +void *burn_alloc_mem(size_t size, size_t count, int flag); + +#define BURN_ALLOC_MEM(pt, typ, count) { \ + pt= (typ *) burn_alloc_mem(sizeof(typ), (size_t) (count), 0); \ + if(pt == NULL) { \ + ret= -1; goto ex; \ + } } + +#define BURN_ALLOC_MEM_VOID(pt, typ, count) { \ + pt= (typ *) burn_alloc_mem(sizeof(typ), (size_t) (count), 0); \ + if(pt == NULL) { \ + goto ex; \ + } } + +#define BURN_FREE_MEM(pt) { \ + if(pt != NULL) \ + free((char *) pt); \ + } + + +/* B20122 */ +int burn_grab_prepare_sig_action(int *signal_action_mem, int flag); +int burn_grab_restore_sig_action(int signal_action_mem, int flag); + + +#endif /* BURN__INIT_H */ diff --git a/trunk/libburn/libburn.h b/trunk/libburn/libburn.h new file mode 100644 index 0000000..4b9ab5a --- /dev/null +++ b/trunk/libburn/libburn.h @@ -0,0 +1,4275 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. + + This is the official API definition of libburn. + +*/ +/* Important: If you add a public API function then add its name to file + libburn/libburn.ver +*/ + + +#ifndef LIBBURN_H +#define LIBBURN_H + +/* + +Applications must use 64 bit off_t. E.g. by defining + #define _LARGEFILE_SOURCE + #define _FILE_OFFSET_BITS 64 +or take special precautions to interface with the library by 64 bit integers +where this .h files prescribe off_t. + +To prevent 64 bit file i/o in the library would keep the application from +processing tracks of more than 2 GB size. + +*/ +#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) + +/* ts B11230 */ +/** Track mode modifier - Serial Copy Management System, SAO only + If this is set and BURN_COPY is not set, then copying the emerging + track will be forbidden. + @since 1.2.0 +*/ +#define BURN_SCMS (1 << 13) + + +/** 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+R and BD-R: Track of open size + With DVD-RAM, DVD+RW, BD-RE: 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) + With other DVD or BD media: same as BURN_WRITE_TAO but may demand + that track size is known in advance. + */ + 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 + With DVD and BD media: not supported. + + ts A90901: This had been disabled because its implementation + relied on code from cdrdao which is not understood + currently. + A burn run will abort with "FATAL" error message + if this mode is attempted. + @since 0.7.2 + ts A91016: Re-implemented according to ECMA-130 Annex A and B. + Now understood, explained and not stemming from cdrdao. + @since 0.7.4 + */ + 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 will not 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. It is ready for writing from scratch. + Unused multi-session media: + CD-R, CD-RW, DVD-R, DVD-RW, DVD+R, BD-R + Blanked multi-session media (i.e. treated by burn_disc_erase()) + CD-RW, DVD-RW + Overwriteable media with or without valid data + DVD-RAM, DVD+RW, formatted DVD-RW, BD-RE + */ + BURN_DISC_BLANK, + + /** There is no disc at all in the drive */ + BURN_DISC_EMPTY, + + /** There is an incomplete disc in the drive. It is ready for appending + another session. + Written but not yet closed multi-session media + CD-R, CD-RW, DVD-R, DVD-RW, DVD+R, BD-R + */ + BURN_DISC_APPENDABLE, + + /** There is a disc with data on it in the drive. It is usable only for + reading. + Written and closed multi-session media + CD-R, CD-RW, DVD-R, DVD-RW, DVD+R, BD-R + Read-Only media + CD-ROM, DVD-ROM, BD-ROM + Note that many DVD-ROM drives report any written media + as Read-Only media and not by their real media types. + */ + BURN_DISC_FULL, + + /* ts A61007 */ + /* @since 0.2.4 */ + /** The drive was not grabbed when the status was inquired */ + BURN_DISC_UNGRABBED, + + /* ts A61020 */ + /* @since 0.2.6 */ + /** The media seems to be unsuitable for reading and for writing */ + 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 */ + /* @since 0.2.6 */ + /** 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 */ + /* @since 0.3.0 */ + /** The drive is formatting media */ + BURN_DRIVE_FORMATTING, + + /* ts A70822 */ + /* @since 0.4.0 */ + /** 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. + +*/ +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 @since 0.3.2 + @since 0.5.2 : DVD extensions are made valid for CD too + bit1= LRA extension is valid @since 0.7.2 + bit2= Track status bits extension is valid @since 1.2.8 + */ + unsigned char extensions_valid; + + /* ts A70201 : DVD extension. extensions_valid:bit0 + If invalid the members are guaranteed to be 0. */ + /* @since 0.3.2 */ + /* 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; + + /* ts A90909 : LRA extension. extensions_valid:bit1 */ + /* @since 0.7.2 */ + /* MMC-5 6.27.3.18 : The Last Recorded Address is valid for DVD-R, + DVD-R DL when LJRS = 00b, DVD-RW, HD DVD-R, and BD-R. + This would mean profiles: 0x11, 0x15, 0x13, 0x14, 0x51, 0x41, 0x42 + */ + int last_recorded_address; + + /* ts B30112 : Track status bits extension. extensions_valid:bit2 */ + /* @since 1.2.8 */ + /* Names as of READ TRACK INFORMATION, MMC-5 6.27.3 : + bit0 - bit3 = Track Mode + bit4 = Copy + bit5 = Damage + bit6 - bit7 = LJRS + bit8 - bit11 = Data Mode + bit12 = FP + bit13 = Packet/Inc + bit14 = Blank + bit15 = RT + bit16 = NWA_V + bit17 = LRA_V + */ + int track_status_bits; + +}; + + +/** 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. + + If this call is reading from a pipe then it will learn + about the end of data only when that pipe gets closed on the + feeder side. So if the track size is not fixed or if the pipe + delivers less than the predicted amount or if the size is not + block aligned, then burning will halt until the input process + closes the pipe. + + 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. */ + /* @since 0.3.2 */ + /** 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. */ + /* @since 0.4.2 */ + /** 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 drive + device file address. Now always use function burn_drive_d_get_adr() + to inquire a device file address. ^^^^^ ALWAYS ^^^^^^^*/ + char location[17]; + + /* NOTE: The capability to write particular media types is also + announced by their profile number being in the list returned + by burn_drive_get_all_profile(). This is the only way to + inquire types DVD-RW, DVD+R, DVD+R DL, DVD+RW, BD-R, BD-RE. + */ + /** 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; + + /** DEPRECATED: Can the drive report C2 errors */ + unsigned int c2_errors:1; + + /** DEPRECATED: 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 */ + /* @since 0.2.6 */ + /** 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 */ + /* @since 0.2.6 */ + /** 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 */ +/* @since 0.3.0 */ +/** 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 Type 03h + 3 = ACh GET PERFORMANCE Type 00h Data Type 10h (read speed) + */ + 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. Once this was 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 eventually call burn_finish(). + + You MUST shut down the busy drives 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 built-in signal handling: + burn_set_signal_handling("my_app_name : ", NULL, 0); + Else you may eventually call burn_drive_cancel() on the active drives and + wait for them to assume state BURN_DRIVE_IDLE. + @param patience Maximum number of seconds to wait for drives to + finish. + @since 0.7.8 : + If this is -1, then only the cancel operations will + be performed and no burn_finish() will happen. + @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 + @since 0.2.6 +*/ +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 A91111 */ +/** Enable resp. disable logging of SCSI commands. + This call can be made at any time - even before burn_initialize(). + It is in effect for all active drives and currently not very thread + safe for multiple drives. + @param flag Bitfield for control purposes. The default is 0. + bit0= log to file /tmp/libburn_sg_command_log + bit1= log to stderr + bit2= flush output after each line + @since 0.7.4 +*/ +void burn_set_scsi_logging(int flag); + +/* 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 + 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 + on GNU/Linux , flock(LOCK_EX) on FreeBSD.) + 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 on GNU/Linux 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. + @since 0.2.2 +*/ +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 no media types are under test reservation - + 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) + @since 0.3.4 +*/ +void burn_allow_untested_profiles(int yes); + + +/* ts A60823 */ +/** Aquire a drive with known device file 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. Nevertheless each + burn_disc_write() run may only write a single track. + + 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. + This is a result from call burn_drive_scan(). See there. + Use with driveno 0 only. + @param adr The device file 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 + @since 0.2.2 +*/ +int burn_drive_scan_and_grab(struct burn_drive_info *drive_infos[], + char* adr, int load); + + +/* ts A51221 */ +/* @since 0.2.2 */ +/** 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 + @since 0.2.2 +*/ +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, + @since 0.2.2 +*/ +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 */ +/* @since 0.2.2 */ +/** Maximum length+1 to expect with a drive device file address string */ +#define BURN_DRIVE_ADR_LEN 1024 + +/* ts A70906 */ +/** Inquire the device file 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 device file address gets copied to it. + @return >0 success , <=0 error (due to libburn internal problem) + @since 0.4.0 +*/ +int burn_drive_d_get_adr(struct burn_drive *drive, char adr[]); + +/* A60823 */ +/** Inquire the device file 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 device file address gets copied to it. + @return >0 success , <=0 error (due to libburn internal problem) + @since 0.2.6 +*/ +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 drive device file address + which could be listed by a run of burn_drive_scan(). No check is made + whether a device file with this address exists or whether it leads + to a usable MMC drive. + @return 1 means yes, 0 means no + @since 0.2.6 +*/ +int burn_drive_is_enumerable_adr(char *adr); + +/* ts A60922 ticket 33 */ +/** Try to convert a given existing filesystem address into a drive device file + 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 device file address gets copied to it. + @return 1 = success , 0 = failure , -1 = severe error + @since 0.2.6 +*/ +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 drive device file 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 device file address gets copied to it. + @return 1 = success , 0 = failure , -1 = severe error + @since 0.2.6 +*/ +int burn_drive_convert_scsi_adr(int bus_no, int host_no, int channel_no, + int target_no, int lun_no, char adr[]); + +/* ts B10728 */ +/** Try to convert a given drive device file address into the address of a + symbolic link that points to this drive address. + Modern GNU/Linux systems may shuffle drive addresses from boot to boot. + The udev daemon is supposed to create links which always point to the + same drive, regardless of its system address. + This call tries to find such links. + @param dev_adr Should contain a drive address as returned by + burn_drive_scan(). + @param link_adr An application provided array of at least + BURN_DRIVE_ADR_LEN characters size. The found link + address gets copied to it. + @param dir_adr The address of the directory where to look for links. + Normally: "/dev" + @param templ An array of pointers to name templates, which + links have to match. A symbolic link in dir_adr matches + a name template if it begins by that text. E.g. + link address "/dev/dvdrw1" matches template "dvdrw". + If templ is NULL, then the default array gets used: + {"dvdrw", "cdrw", "dvd", "cdrom", "cd"} + If several links would match, then a link will win, + which matches the template with the lowest array index. + Among these candidates, the one with the lowest strcmp() + rank will be chosen as link_adr. + @param num_templ Number of array elements in templ. + @param flag Bitfield for control purposes. Unused yet. Submit 0. + @return <0 severe error, 0 failed to search, 2 nothing found + 1 success, link_adr is valid + @since 1.1.4 +*/ +int burn_lookup_device_link(char *dev_adr, char link_adr[], + char *dir_adr, char **templ, int num_templ, int flag); + +/* 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 drive device file + 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 + @since 0.2.6 +*/ +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); + +/* ts B00114 */ +/* Probe available CD write modes and block types. In earlier versions this + was done unconditionally on drive examination or aquiration. But it is + lengthy and obtrusive, up to spoiling burn runs on the examined drives. + So now this probing is omitted by default. All drives which announce to be + capable of CD or DVD writing, get blindly attributed the capability for + SAO and TAO. Applications which are interested in RAW modes or want to + rely on the traditional write mode information, may use this call. + @param drive_info drive object to be inquired + @return >0 indicates success, <=0 means failure + @since 0.7.6 +*/ +int burn_drive_probe_cd_write_modes(struct burn_drive_info *drive_info); + +/* ts A90824 */ +/** Calm down or alert a drive. Some drives stay alert after reading for + quite some time. This saves time with the startup for the next read + operation but also causes noise and consumes extra energy. It makes + sense to calm down the drive if no read operation is expected for the + next few seconds. The drive will get alert automatically if operations + are required. + @param d The drive to influence. + @param flag Bitfield for control purposes + bit0= become alert (else start snoozing) + This is not mandatory to allow further drive operations + @return 1= success , 0= drive role not suitable for calming + @since 0.7.0 +*/ +int burn_drive_snooze(struct burn_drive *d, int flag); + + +/** Re-assess drive and media status. This should be done after a drive + underwent a status change and shall be further used without intermediate + burn_drive_release(), burn_drive_grab(). E.g. after blanking or burning. + @param d The already grabbed drive to re-assess. + @param flag Unused yet. Submit 0. + @return 1 success , <= 0 could not determine drive and media state + @since 1.1.8 +*/ +int burn_drive_re_assess(struct burn_drive *d, int flag); + + +/** Release a drive. This should not be done until the drive is no longer + busy (see burn_drive_get_status). + @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 d The drive to release and leave locked. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 means success, <=0 means failure + @since 0.4.0 +*/ +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 + @since 0.2.6 +*/ +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. + @since 0.2.6 +*/ +int burn_disc_pretend_full(struct burn_drive *drive); + + +/* ts B31110 */ +/** WARNING: This overrides the safety measures against unsuitable media. + Sets the drive status to BURN_DISC_FULL unconditionally. + @since 1.3.4 +*/ +int burn_disc_pretend_full_uncond(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 + @since 0.2.6 +*/ +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 + @since 0.2.6 +*/ +int burn_drive_get_start_end_lba(struct burn_drive *drive, + int *start_lba, int *end_lba, int flag); + + +/* ts A90902 */ +/** Guess the manufacturer name of CD media from the ATIP addresses of lead-in + and lead-out. (Currently only lead-in is interpreted. Lead-out may in + future be used to identify the media type in more detail.) + The parameters of this call should be obtained by burn_disc_read_atip(d), + burn_drive_get_start_end_lba(d, &start_lba, &end_lba, 0), + burn_lba_to_msf(start_lba, &m_li, &s_li, &f_li) and + burn_lba_to_msf(end_lba, &m_lo, &s_lo, &f_lo). + @param m_li "minute" part of ATIP lead-in resp. start_lba + @param s_li "second" of lead-in resp. start_lba + @param f_li "frame" of lead-in + @param m_lo "minute" part of ATIP lead-out + @param s_lo "second" of lead-out + @param f_lo "frame" of lead-out + @param flag Bitfield for control purposes, + bit0= append a text "(aka ...)" to reply if other brands or + vendor names are known. + @return Printable text or NULL on memory shortage. + Dispose by free() when no longer needed. + @since 0.7.2 +*/ +char *burn_guess_cd_manufacturer(int m_li, int s_li, int f_li, + int m_lo, int s_lo, int f_lo, int flag); + +/* ts A90909 */ +/** Retrieve some media information which is mainly specific to CD. For other + media only the bits in reply parameter valid are supposed to be meaningful. + @param d The drive to query. + @param disc_type A string saying either "CD-DA or CD-ROM", or "CD-I", + or ""CD-ROM XA", or "undefined". + @param disc_id A 32 bit number read from the media. (Meaning unclear yet) + @param bar_code 8 hex digits from a barcode on media read by the drive + (if the drive has a bar code reader built in). + @param app_code The Host Application Code which must be set in the Write + Parameters Page if the media is not unrestricted (URU==0). + @param valid Replies bits which indicate the validity of other reply + parameters or the state of certain CD info bits: + bit0= disc_type is valid + bit1= disc_id is valid + bit2= bar_code is valid + bit3= disc_app_code is valid + bit4= Disc is unrestricted (URU bit, 51h READ DISC INFO) + This seems to be broken with my drives. The bit is + 0 and the validity bit for disc_app_code is 0 too. + bit5= Disc is nominally erasable (Erasable bit) + This will be set with overwriteable media which + libburn normally considers to be unerasable blank. + @return 1 success, <= 0 an error occured + @since 0.7.2 +*/ +int burn_disc_get_cd_info(struct burn_drive *d, char disc_type[80], + unsigned int *disc_id, char bar_code[9], int *app_code, + int *valid); + +/* ts B11201 */ +/** Read the array of CD-TEXT packs from the Lead-in of an audio CD. + Each pack consists of 18 bytes, of which 4 are header. 12 bytes are pieces + of 0-terminated texts or binary data. 2 bytes hold a CRC. + For a description of the format of the array, see file doc/cdtext.txt. + @param d The drive to query. + @param text_packs Will point to an allocated memory buffer with CD-TEXT. + It will only contain text packs, and not be prepended + by the TOC header of four bytes, which gets stored with + file cdtext.dat by cdrecord -vv -toc. (The first two of + these bytes are supposed to hold the number of CD-TEXT + bytes + 2. The other two bytes are supposed to be 0.) + Dispose this buffer by free(), when no longer needed. + @param num_packs Will tell the number of text packs, i.e. the number of + bytes in text_packs divided by 18. + @param flag Bitfield for control purposes, + Unused yet. Submit 0. + @return 1 success, 0= no CD-TEXT found, < 0 an error occured + @since 1.2.0 +*/ +int burn_disc_get_leadin_text(struct burn_drive *d, + unsigned char **text_packs, int *num_packs, + int flag); + +/* ts B00924 */ +/** Read the current usage of the eventual BD Spare Area. This area gets + reserved on BD media during formatting. During writing it is used to + host replacements of blocks which failed the checkread immediately after + writing. + This call applies only to recordable BD media. I.e. profiles 0x41 to 0x43. + @param d The drive to query. + @param alloc_blocks Returns the number of blocks reserved as Spare Area + @param free_blocks Returns the number of yet unused blocks in that area + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 = reply prarameters are valid, + <=0 = reply is invalid (e.g. because no BD profile) + @since 0.8.8 +*/ +int burn_disc_get_bd_spare_info(struct burn_drive *d, + int *alloc_blocks, int *free_blocks, int flag); + +/* ts B10801 */ +/** Retrieve some media information which is mainly specific to media of + the DVD-R family: DVD-R , DVD-RW , DVD-R DL , HD DVD-R + Currently the information cannot be retrieved from other media types. + @param d The drive to query. + @param disk_category returns DVD Book to which the media complies + @param book_name returns a pointer to the book name of disk_category. + This memory is static. Do not alter or free it ! + @param part_version returns the Media Version in the DVD Book + @param num_layers returns the number of media layers + @param num_blocks returns the number of blocks between pysical start + and physical end of the media + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 1 = reply prarameters are valid, + <=0 = reply is invalid (e.g. because no DVD-R) + @since 1.1.4 +*/ +int burn_disc_get_phys_format_info(struct burn_drive *d, int *disk_category, + char **book_name, int *part_version, int *num_layers, + int *num_blocks, 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 + The first existing track on a CD may have a number higher + than 1. Use burn_session_get_start_tno() to inquire this + start number. + @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 + @since 0.2.6 +*/ +int burn_disc_track_lba_nwa(struct burn_drive *d, struct burn_write_opts *o, + int trackno, int *lba, int *nwa); + +/* ts B10525 */ +/** Tells whether a previous attempt to determine the Next Writeable Address + of the upcomming track reveiled that the READ TRACK INFORMATION Damage Bit + is set for this track, resp. that no valid writable address is available. + See MMC-5 6.27.3.7 Damage Bit, 6.27.3.11 NWA_V (NWA valid) + @param d The drive to query. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 0= Looks ok: Damage Bit is not set, NWA_V is set + 1= Damaged and theoretically writable (NWA_V is set) + 2= Not writable: NWA_V is not set + 3= Damaged and not writable (NWA_V is not set), + @since 1.1.0 +*/ +int burn_disc_next_track_is_damaged(struct burn_drive *d, int flag); + +/* ts B10527 */ +/** Try to close the last track and session of media which have bit0 set in + the return value of call burn_disc_next_track_is_damaged(). + Whether it helps depends much on the reason why the media is reported + as damaged by the drive. + This call works only for profiles 0x09 CD-R, 0x0a CD-RW, 0x11 DVD-R, + 0x14 DVD-RW sequential, 0x1b DVD+R, 0x2b DVD+R DL, 0x41 BD-R sequential. + Note: After writing it is advised to give up the drive and to grab it again + in order to learn about its view on the new media state. + @param o Write options created by burn_write_opts_new() and + manipulated by burn_write_opts_set_multi(). + burn_write_opts_set_write_type() should be set to + BURN_WRITE_TAO, burn_write_opts_set_simulate() should be + set to 0. + @param flag Bitfield for control purposes + bit0= force close, even if no damage was seen + @return <=0 media not marked as damaged, or media type not suitable, + or closing attempted but failed + 1= attempt finished without error indication + @since 1.1.0 +*/ +int burn_disc_close_damaged(struct burn_write_opts *o, int flag); + + +/* 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 + @since 0.3.2 +*/ +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 + taken into respect with the capacity estimation. Negative results get + defaulted to 0. + If the drive is actually a file in a large filesystem or a large block + device, then the capacity is curbed to a maximum of 0x7ffffff0 blocks + = 4 TB - 32 KB. + @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 + @since 0.3.4 +*/ +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", + 0x15 "DVD-R/DL sequential recording", + 0x1a "DVD+RW" + 0x1b "DVD+R", + 0x2b "DVD+R/DL", + 0x41 "BD-R sequential recording", + 0x43 "BD-RE", + 0xffff "stdio file" + Note: 0xffff is not a MMC profile but a libburn invention. + Read-only are the profiles + 0x08 "CD-ROM", + 0x10 "DVD-ROM", + 0x40 "BD-ROM", + Read-only for now is this BD-R profile (testers wanted) + 0x42 "BD-R random recording" + Empty drives are supposed to report + 0x00 "" + @param d The drive where the media is inserted. + @param pno Profile Number. See also 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 + @since 0.3.0 +*/ +int burn_disc_get_profile(struct burn_drive *d, int *pno, char name[80]); + + +/* ts A90903 : API */ +/** Obtain product id and standards defined media codes. + The product id is a printable string which is supposed to be the same + for identical media but should vary with non-identical media. Some media + do not allow to obtain such an id at all. + The pair (profile_number, product_id) should be the best id to identify + media with identical product specifications. + The reply parameters media_code1 and media_code2 can be used with + burn_guess_manufacturer() + The reply parameters have to be disposed by free() when no longer needed. + @param d The drive where the media is inserted. + @param product_id Reply: Printable text depicting manufacturer and + eventually media id. + @param media_code1 Reply: The eventual manufacturer identification as read + from DVD/BD media or a text "XXmYYsZZf" from CD media + ATIP lead-in. + @param media_code2 The eventual media id as read from DVD+/BD media or a + text "XXmYYsZZf" from CD ATIP lead-out. + @param book_type Book type text for DVD and BD. + Caution: is NULL with CD, even if return value says ok. + @param flag Bitfield for control purposes + bit0= do not escape " _/" (not suitable for + burn_guess_manufacturer()) + @return 1= ok, product_id and media codes are valid, + 0= no product id_available, reply parameters are NULL + <0= error + @since 0.7.2 +*/ +int burn_disc_get_media_id(struct burn_drive *d, + char **product_id, char **media_code1, char **media_code2, + char **book_type, int flag); + + +/* ts A90904 */ +/** Guess the name of a manufacturer by profile number, manufacturer code + and media code. The profile number can be obtained by + burn_disc_get_profile(), the other two parameters can be obtained as + media_code1 and media_code2 by burn_disc_get_media_id(). + @param profile_no Profile number (submit -1 if not known) + @param manuf_code Manufacturer code from media (e.g. "RICOHJPN") + @param media_code Media ID code from media (e.g. "W11") + @param flag Bitfield for control purposes, submit 0 + @return Printable text or NULL on memory shortage. + If the text begins with "Unknown " then no item of the + manufacturer list matched the codes. + Dispose by free() when no longer needed. + @since 0.7.2 +*/ +char *burn_guess_manufacturer(int profile_no, + char *manuf_code, char *media_code, int flag); + + +/** Tells whether a disc can be erased or not + @param d The drive to inquire. + @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. + The returned object must later 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 + @since 0.4.0 +*/ +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. + Only drive roles 1 (MMC) and 5 (stdio random write-only) + support erasing. + @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 - A70417 */ +/** 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 bit4 of flag. DVD-RAM and BD-RE may get formatted initially + or re-formatted to adjust their Defect Managment. + This function usually returns while the drive is still in the process + of formatting. The formatting is done, when burn_drive_get_status() + returns BURN_DRIVE_IDLE. This may be immediately after return or may + need several thousand seconds to occur. + @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 and on parameter flag. + @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+2: size mode + 0 = use parameter size as far as it makes sense + 1 = insist in size 0 even if there is a better default known + (on DVD-RAM or BD-R identical to size mode 0, + i.e. they never get formatted with payload size 0) + 2 = without bit7: format to maximum available size + with bit7 : take size from indexed format descriptor + 3 = without bit7: format to default size + with bit7 : take size from indexed format descriptor + bit3= -reserved- + bit4= enforce re-format of (partly) formatted media + bit5= try to disable eventual defect management + bit6= try to avoid lengthy media certification + bit7, bit8 to bit15 = + bit7 enables MMC expert application mode (else libburn + tries to choose a suitable format type): + If it is set then 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, 0x01, 0x10, 0x11, 0x13, + 0x15, 0x26, 0x30, 0x31, 0x32. + If bit7 is set, then bit4 is set automatically. + bit16= enable POW on blank BD-R + @since 0.3.0 +*/ +void burn_disc_format(struct burn_drive *drive, off_t size, int flag); + + +/* ts A70112 */ +/* @since 0.3.0 */ +/** Possible formatting status values */ +#define BURN_FORMAT_IS_UNFORMATTED 1 +#define BURN_FORMAT_IS_FORMATTED 2 +#define BURN_FORMAT_IS_UNKNOWN 3 + +/* ts A70112 */ +/** 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 + @since 0.3.0 +*/ +int burn_disc_get_formats(struct burn_drive *drive, int *status, off_t *size, + unsigned *bl_sas, int *num_formats); + +/* ts A70112 */ +/** 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, 0x30=BD-RE with spare areas, + 0x31=BD-RE without spare areas + @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 + @since 0.3.0 +*/ +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 */ +/* @since 0.3.4 */ +/** 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 + @since 0.3.4 +*/ +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(). + Note: After writing it is advised to give up the drive and to grab it again + in order to learn about its view on the new media state. + Note: Before mounting the written media it might be necessary to eject + and reload in order to allow the operating system to notice the new + media state. + @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); + + +/* ts A90227 */ +/** Control stream recording during the write run and eventually set the start + LBA for stream recording. + Stream recording is set from struct burn_write_opts when the write run + gets started. See burn_write_opts_set_stream_recording(). + The call described here can be used later to override this setting and + to program automatic switching at a given LBA. It also affects subsequent + calls to burn_random_access_write(). + @param drive The drive which performs the write operation. + @param recmode -1= disable stream recording + 0= leave setting as is + 1= enable stream recording + @param start The LBA where actual stream recording shall start. + (0 means unconditional stream recording) + @param flag Bitfield for control purposes (unused yet, submit 0). + @return 1=success , <=0 failure + @since 0.6.4 +*/ +int burn_drive_set_stream_recording(struct burn_drive *drive, int recmode, + int start, int flag); + +/** 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 asynchronous media job was successful. + This applies to burn_disc_erase(), burn_disc_format(), burn_disc_write(). + Reasons for non-success may be: rejection of burn parameters, abort due to + fatal errors during write, blank or format, 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 + @since 0.2.6 +*/ +int burn_drive_wrote_well(struct burn_drive *d); + + +/* ts B31023 */ +/** Inquire whether a write error occured which is suspected to have happened + due to a false report about DVD-RW capability to be written in write type + BURN_WRITE_TAO. + @param d The drive to inquire. + @return 1= it seems that BURN_WRITE_TAO on DVD-RW caused error, + 0= it does not seem so + @since 1.3.4 +*/ +int burn_drive_was_feat21_failure(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); + + +/* ts B11219 */ +/** Read a CDRWIN cue sheet file and equip the session object by tracks and + CD-TEXT according to the content of the file. + For a description of CDRWIN file format see + http://digitalx.org/cue-sheet/syntax/ + Fully supported commands are: + CATALOG , CDTEXTFILE , FLAGS , INDEX , ISRC , PERFORMER , + POSTGAP , PREGAP , REM , SONGWRITER , TITLE + Further supported commands introduced by cdrecord (usage like PERFORMER): + ARRANGER , COMPOSER , MESSAGE + Partly supported commands are: + FILE which supports only types BINARY , MOTOROLA , WAVE + TRACK which supports only datatypes AUDIO , MODE1/2048 + Unsupported types of FILE or TRACK lead to failure of the call. + libburn does not yet support mixing of AUDIO and MODE1/2048. So this call + will fail if such a mix is found. + CD-TEXT information is allowed only if all tracks are of datatype AUDIO. + Empty lines and lines which start by '#' are ignored. + @param session Session where to attach tracks. It must not yet have + tracks or else this call will fail. + @param path Filesystem address of the CDRWIN cue sheet file. + Normally with suffix .cue + @param fifo_size Number of bytes in fifo. This will be rounded up by + the block size of the track mode. <= 0 means no fifo. + @param fifo Returns a reference to the burn_source object that + was installed as fifo between FILE and the track + burn sources. One may use this to inquire the fifo + state. Dispose it by burn_source_free() when no longer + needed. It is permissible to pass this parameter to + libburn as NULL, in order to immediately drop ownership + on the fifo. + @param text_packs Returns pre-formatted CD-TEXT packs resulting from + cue sheet command CDTEXTFILE. To be used with call + burn_write_opts_set_leadin_text(). + It is permissible to pass this parameter to libburn + as NULL, in order to disable CDTEXTFILE. + @param num_packs Returns the number of 18 byte records in text_packs. + @param flag Bitfield for control purposes. + bit0= Do not attach CD-TEXT information to session and + tracks. Do not load text_packs. + bit1= Do not use media catalog string of session or ISRC + strings of tracks for writing to Q sub-channel. + @return > 0 indicates success, <= 0 indicates failure + @since 1.2.0 +*/ +int burn_session_by_cue_file(struct burn_session *session, + char *path, int fifo_size, struct burn_source **fifo, + unsigned char **text_packs, int *num_packs, int flag); + + +/** Create a track */ +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); + + +/* ts B20107 */ +/** Set the number which shall be written as CD track number with the first + track of the session. The following tracks will then get written with + consecutive CD track numbers. The resulting number of the last track + must not exceed 99. The lowest possible start number is 1, which is also + the default. This setting applies only to CD SAO writing. + @param session The session to be manipulated + @param tno A number between 1 and 99 + @param flag Bitfield for control purposes. Unused yet. Submit 0. + @return > 0 indicates success, <= 0 indicates failure + @since 1.2.0 +*/ +int burn_session_set_start_tno(struct burn_session *session, int tno, + int flag); + +/* ts B20108 */ +/** Inquire the CD track start number, as set by default or by + burn_session_set_start_tno(). + @param session The session to be inquired + @param flag Bitfield for control purposes. Unused yet. Submit 0. + @return > 0 is the currently set CD track start number + <= 0 indicates failure + @since 1.2.0 +*/ +int burn_session_get_start_tno(struct burn_session *session, int flag); + + + +/* ts B11206 */ +/** Set the Character Codes, the Copyright bytes, and the Language Codes + for CD-TEXT blocks 0 to 7. They will be used in the block summaries + of text packs which get generated from text or binary data submitted + by burn_session_set_cdtext() and burn_track_set_cdtext(). + Character Code value can be + 0x00 = ISO-8859-1 + 0x01 = 7 bit ASCII + 0x80 = MS-JIS (japanesei Kanji, double byte characters) + Copyright byte value can be + 0x00 = not copyrighted + 0x03 = copyrighted + Language Code value will typically be 0x09 = English or 0x69 = Japanese. + See below macros BURN_CDTEXT_LANGUAGES_0X00 and BURN_CDTEXT_LANGUAGES_0X45, + but be aware that many of these codes have never been seen on CD, and that + many of them do not have a character representation among the above + Character Codes. + Default is 0x09 = English for block 0 and 0x00 = Unknown for block 1 to 7. + Copyright and Character Code are 0x00 for all blocks by default. + See also file doc/cdtext.txt, "Format of a CD-TEXT packs array", + "Pack type 0x8f". + + Parameter value -1 leaves the current setting of the session parameter + unchanged. + @param s Session where to change settings + @param char_codes Character Codes for block 0 to 7 + @param copyrights Copyright bytes for block 0 to 7 + @param languages Language Codes for block 0 to 7 + @param flag Bitfiled for control purposes. Unused yet. Submit 0. + @return <=0 failure, > 0 success + @since 1.2.0 +*/ +int burn_session_set_cdtext_par(struct burn_session *s, + int char_codes[8], int copyrights[8], + int languages[8], int flag); + +/** This is the first list of languages sorted by their Language codes, + which start at 0x00. They stem from from EBU Tech 3264, appendix 3. + E.g. language 0x00 is "Unknown", 0x08 is "German", 0x10 is "Frisian", + 0x18 is "Latvian", 0x20 is "Polish", 0x28 is "Swedish", 0x2b is "Wallon". + See also file doc/cdtext.txt. + @since 1.2.0 +*/ +#define BURN_CDTEXT_LANGUAGES_0X00 \ + "Unknown", "Albanian", "Breton", "Catalan", \ + "Croatian", "Welsh", "Czech", "Danish", \ + "German", "English", "Spanish", "Esperanto", \ + "Estonian", "Basque", "Faroese", "French", \ + "Frisian", "Irish", "Gaelic", "Galician", \ + "Icelandic", "Italian", "Lappish", "Latin", \ + "Latvian", "Luxembourgian", "Lithuanian", "Hungarian", \ + "Maltese", "Dutch", "Norwegian", "Occitan", \ + "Polish", "Portuguese", "Romanian", "Romansh", \ + "Serbian", "Slovak", "Slovenian", "Finnish", \ + "Swedish", "Turkish", "Flemish", "Wallon" + +/** This is the second list of languages sorted by their Language codes, + which start at 0x45. They stem from from EBU Tech 3264, appendix 3. + E.g. language 0x45 is "Zulu", 0x50 is "Sranan Tongo", 0x58 is "Pushtu", + 0x60 is "Moldavian", 0x68 is "Kannada", 0x70 is "Greek", 0x78 is "Bengali", + 0x7f is "Amharic". + See also file doc/cdtext.txt. + @since 1.2.0 +*/ +#define BURN_CDTEXT_LANGUAGES_0X45 \ + "Zulu", "Vietnamese", "Uzbek", \ + "Urdu", "Ukrainian", "Thai", "Telugu", \ + "Tatar", "Tamil", "Tadzhik", "Swahili", \ + "Sranan Tongo", "Somali", "Sinhalese", "Shona", \ + "Serbo-croat", "Ruthenian", "Russian", "Quechua", \ + "Pushtu", "Punjabi", "Persian", "Papamiento", \ + "Oriya", "Nepali", "Ndebele", "Marathi", \ + "Moldavian", "Malaysian", "Malagasay", "Macedonian", \ + "Laotian", "Korean", "Khmer", "Kazakh", \ + "Kannada", "Japanese", "Indonesian", "Hindi", \ + "Hebrew", "Hausa", "Gurani", "Gujurati", \ + "Greek", "Georgian", "Fulani", "Dari", \ + "Churash", "Chinese", "Burmese", "Bulgarian", \ + "Bengali", "Bielorussian", "Bambora", "Azerbaijani", \ + "Assamese", "Armenian", "Arabic", "Amharic" + +/* This is the list of empty languages names between 0x30 and 0x44. + Together the three macros fill an array of 128 char pointers. + static char *languages[] = { + BURN_CDTEXT_LANGUAGES_0X00, + BURN_CDTEXT_FILLER, + BURN_CDTEXT_LANGUAGES_0X45 + }; +*/ +#define BURN_CDTEXT_FILLER \ + "", "", "", "", \ + "", "", "", "", \ + "", "", "", "", \ + "", "", "", "", \ + "", "", "", "", \ + "", "", "", "", \ + "" + +/* ts B11206 */ +/** Obtain the current settings as of burn_session_set_cdtext_par() resp. + by default. + @param s Session which to inquire + @param char_codes Will return Character Codes for block 0 to 7 + @param copyrights Will return Copyright bytes for block 0 to 7 + @param block_languages Will return Language Codes for block 0 to 7 + @param flag Bitfiled for control purposes. Unused yet. Submit 0. + @return <=0 failure, reply invalid, > 0 success, reply valid + @since 1.2.0 +*/ +int burn_session_get_cdtext_par(struct burn_session *s, + int char_codes[8], int copyrights[8], + int block_languages[8], int flag); + + +/* ts B11206 */ +/** Attach text or binary data as CD-TEXT attributes to a session. + They can be used to generate CD-TEXT packs by burn_cdtext_from_session() + or to write CD-TEXT packs into the lead-in of a CD SAO session. + The latter happens only if no array of CD-TEXT packs is attached to + the write options by burn_write_opts_set_leadin_text(). + For details of the CD-TEXT format and of the payload content, see file + doc/cdtext.txt . + @param s Session where to attach CD-TEXT attribute + @param block Number of the language block in which the attribute + shall appear. Possible values: 0 to 7. + @param pack_type Pack type number. 0x80 to 0x8e. Used if pack_type_name + is NULL or empty text. Else submit 0 and a name. + Pack type 0x8f is generated automatically and may not + be set by applications. + @param pack_type_name The pack type by name. Defined names are: + 0x80 = "TITLE" 0x81 = "PERFORMER" + 0x82 = "SONGWRITER" 0x83 = "COMPOSER" + 0x84 = "ARRANGER" 0x85 = "MESSAGE" + 0x86 = "DISCID" 0x87 = "GENRE" + 0x88 = "TOC" 0x89 = "TOC2" + 0x8d = "CLOSED" 0x8e = "UPC_ISRC" + Names are recognized uppercase and lowercase. + @param payload Text or binary bytes. The data will be copied to + session-internal memory. + Pack types 0x80 to 0x85 contain 0-terminated cleartext + encoded according to the block's Character Code. + If double byte characters are used, then two 0-bytes + terminate the cleartext. + Pack type 0x86 is 0-terminated ASCII cleartext. + Pack type 0x87 consists of two byte big-endian + Genre code (see below BURN_CDTEXT_GENRE_LIST), and + 0-terminated ASCII cleartext of genre description. + Pack type 0x88 mirrors the session table-of-content. + Pack type 0x89 is not understood yet. + Pack types 0x8a to 0x8c are reserved. + Pack type 0x8d contains ISO-8859-1 cleartext which is + not to be shown by commercial audio CD players. + Pack type 0x8e is ASCII cleartext with UPC/EAN code. + @param length Number of bytes in payload. Including terminating + 0-bytes. + @param flag Bitfield for control purposes. + bit0= payload contains double byte characters + (with character code 0x80 MS-JIS japanese Kanji) + @return > 0 indicates success , <= 0 is failure + @since 1.2.0 +*/ +int burn_session_set_cdtext(struct burn_session *s, int block, + int pack_type, char *pack_type_name, + unsigned char *payload, int length, int flag); + + +/** This is the list of Genres sorted by their Genre codes. + E.g. genre code 0x0000 is "No Used", 0x0008 is "Dance, 0x0010 is "Musical", + 0x0018 is "Rhythm & Blues", 0x001b is "World Music". + See also file doc/cdtext.txt. + @since 1.2.0 +*/ +#define BURN_CDTEXT_GENRE_LIST \ + "Not Used", "Not Defined", "Adult Contemporary", "Alternative Rock", \ + "Childrens Music", "Classical", "Contemporary Christian", "Country", \ + "Dance", "Easy Listening", "Erotic", "Folk", \ + "Gospel", "Hip Hop", "Jazz", "Latin", \ + "Musical", "New Age", "Opera", "Operetta", \ + "Pop Music", "Rap", "Reggae", "Rock Music", \ + "Rhythm & Blues", "Sound Effects", "Spoken Word", "World Music" + +/* The number of genre names in BURN_CDTEXT_GENRE_LIST. +*/ +#define BURN_CDTEXT_NUM_GENRES 28 + + +/* ts B11206 */ +/** Obtain a CD-TEXT attribute that was set by burn_session_set_cdtext() + @param s Session to inquire + @param block Number of the language block to inquire. + @param pack_type Pack type number to inquire. Used if pack_type_name + is NULL or empty text. Else submit 0 and a name. + Pack type 0x8f is generated automatically and may not + be inquire in advance. Use burn_cdtext_from_session() + to generate all packs including type 0x8f packs. + @param pack_type_name The pack type by name. + See above burn_session_set_cdtext(). + @param payload Will return a pointer to text or binary bytes. + Not a copy of data. Do not free() this address. + If no text attribute is attached for pack type and + block, then payload is returned as NULL. The return + value will not indicate error in this case. + @param length Will return the number of bytes pointed to by payload. + Including terminating 0-bytes. + @param flag Bitfield for control purposes. Unused yet. Submit 0. + @return 1 single byte char, 2 double byte char, <=0 error + @since 1.2.0 +*/ +int burn_session_get_cdtext(struct burn_session *s, int block, + int pack_type, char *pack_type_name, + unsigned char **payload, int *length, int flag); + + +/* ts B11215 */ +/** Read a Sony CD-TEXT Input Sheet Version 0.7T file and attach its text + attributes to the given session and its tracks for the given CD-TEXT + block number. This overrides previous settings made by + burn_session_set_cdtext(), burn_track_set_cdtext(), burn_track_set_isrc(), + burn_session_set_start_tno(). It can later be overridden by said function + calls. + The media catalog number from purpose specifier "UPC / EAN" gets into + effect only if burn_write_opts_set_has_mediacatalog() is set to 0. + The format of a v07t sheet file is documented in doc/cdtext.txt. + @param session Session where to attach CD-TEXT attributes + @param path Local filesystem address of the sheet file which + shall be read and interpreted. + @param block Number of the language block in which the attributes + shall appear. Possible values: 0 to 7. + @param flag Bitfield for control purposes. + bit0= Permission to read multiple blocks from the + given sheet file. Each block is supposed to begin + by a line "Input Sheet Version = 0.7T". Therefore + this permission is only valid if the input file + begins by such a line. + @since 1.3.2 + bit1= Do not use media catalog string of session or ISRC + strings of tracks for writing to Q sub-channel. + @since 1.2.0 + @return > 0 indicates success and the number of interpreted + blocks (1 if not flag bit0 is set). + <= 0 indicates failure + @since 1.2.0 +*/ +int burn_session_input_sheet_v07t(struct burn_session *session, + char *path, int block, int flag); + + +/* ts B11210 */ +/** Produce an array of CD-TEXT packs that could be submitted to + burn_write_opts_set_leadin_text(), or stored as *.cdt file, + or submitted to burn_make_input_sheet_v07t(). + For a description of the format of the array, see file doc/cdtext.txt. + The input data stem from burn_session_set_cdtext_par(), + burn_session_set_cdtext(), and burn_track_set_cdtext(). + @param s Session from which to produce CD-TEXT packs. + @param text_packs Will return the buffer with the CD-TEXT packs. + Dispose by free() when no longer needed. + @param num_packs Will return the number of 18 byte text packs. + @param flag Bitfield for control purposes. + bit0= do not return generated CD-TEXT packs, + but check whether production would work and + indicate the number of packs by the call return + value. This happens also if + (text_packs == NULL || num_packs == NULL). + @return Without flag bit0: > 0 is success, <= 0 failure + With flag bit0: > 0 is number of packs, + 0 means no packs will be generated, + < 0 means failure + @since 1.2.0 +*/ +int burn_cdtext_from_session(struct burn_session *s, + unsigned char **text_packs, int *num_packs, + int flag); + + +/* ts B30519 */ +/** Convert an array of CD-TEXT packs into the text format of + Sony CD-TEXT Input Sheet Version 0.7T . + + @param text_packs Array of bytes which form CD-TEXT packs of 18 bytes + each. For a description of the format of the array, + see file doc/cdtext.txt. + No header of 4 bytes must be prepended which would + tell the number of pack bytes + 2. + This parameter may be NULL if the currently attached + array of packs shall be removed. + @param num_packs The number of 18 byte packs in text_packs. + @param start_tno The start number of track counting, if known from + CD table-of-content or other sources. + Submit 0 to enable the attempt to read it and the + track_count from pack type 0x8f. + @param track_count The number of tracks, if known from CD table-of-content + or orther sources. + @param result Will return the buffer with Sheet text. + Dispose by free() when no longer needed. + It will be filled by the text for the v07t sheet file + plus a trailing 0-byte. (Be aware that double-byte + characters might contain 0-bytes, too.) + Each CD-TEXT language block starts by the line + "Input Sheet Version = 0.7T" + and a "Remarks" line that tells the block number. + @param char_code Returns the character code of the pack array: + 0x00 = ISO-8859-1 + 0x01 = 7 bit ASCII + 0x80 = MS-JIS (japanese Kanji, double byte characters) + The presence of a code value that is not in this list + will cause this function to fail. + @param flag Bitfield for control purposes. Unused yet. Submit 0. + @return > 0 tells the number of valid text bytes in result. + This does not include the trailing 0-byte. + <= 0 indicates failure. + @since 1.3.2 +*/ +int burn_make_input_sheet_v07t(unsigned char *text_packs, int num_packs, + int start_tno, int track_count, + char **result, int *char_code, int flag); + + +/* ts B11206 */ +/** Remove all CD-TEXT attributes of the given block from the session. + They were attached by burn_session_set_cdtext(). + @param s Session where to remove the CD-TEXT attribute + @param block Number of the language block in which the attribute + shall appear. Possible values: 0 to 7. + -1 causes text packs of all blocks to be removed. + @return > 0 is success, <= 0 failure + @since 1.2.0 +*/ +int burn_session_dispose_cdtext(struct burn_session *s, int block); + + +/* ts B11221*/ +/** Read an array of CD-TEXT packs from a file. This array should be suitable + for burn_write_opts_set_leadin_text(). + The function tolerates and removes 4-byte headers as produced by + cdrecord -vv -toc, if this header tells the correct number of bytes which + matches the file size. If no 4-byte header is present, then the function + tolerates and removes a trailing 0-byte as of Sony specs. + @param path Filesystem address of the CD-TEXT pack file. + Normally with suffix .cdt or .dat + @param text_packs Will return the buffer with the CD-TEXT packs. + Dispose by free() when no longer needed. + @param num_packs Will return the number of 18 byte text packs. + @param flag Bitfield for control purposes. Unused yet.Submit 0. + @return 0 is success, <= 0 failure + @since 1.2.0 +*/ +int burn_cdtext_from_packfile(char *path, unsigned char **text_packs, + int *num_packs, int flag); + + +/** 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 B11206 */ +/** Attach text or binary data as CD-TEXT attributes to a track. + The payload will be used to generate CD-TEXT packs by + burn_cdtext_from_session() or to write CD-TEXT packs into the lead-in + of a CD SAO session. This happens if the CD-TEXT attribute of the session + gets generated, which has the same block number and pack type. In this + case, each track should have such a CD-TEXT attribute, too. + See burn_session_set_cdtext(). + Be cautious not to exceed the maximum number of 253 payload packs per + language block. Use burn_cdtext_from_session() to learn whether a valid + array of CD-TEXT packs can be generated from your attributes. + @param t Track where to attach CD-TEXT attribute. + @param block Number of the language block in which the attribute + shall appear. Possible values: 0 to 7. + @param pack_type Pack type number. 0x80 to 0x85 or 0x8e. Used if + pack_type_name is NULL or empty text. Else submit 0 + and a name. + @param pack_type_name The pack type by name. Applicable names are: + 0x80 = "TITLE" 0x81 = "PERFORMER" + 0x82 = "SONGWRITER" 0x83 = "COMPOSER" + 0x84 = "ARRANGER" 0x85 = "MESSAGE" + 0x8e = "UPC_ISRC" + @param payload 0-terminated cleartext. If double byte characters + are used, then two 0-bytes terminate the cleartext. + @param length Number of bytes in payload. Including terminating + 0-bytes. + @param flag Bitfield for control purposes. + bit0= payload contains double byte characters + (with character code 0x80 MS-JIS japanese Kanji) + @return > 0 indicates success , <= 0 is failure + @since 1.2.0 +*/ +int burn_track_set_cdtext(struct burn_track *t, int block, + int pack_type, char *pack_type_name, + unsigned char *payload, int length, int flag); + +/* ts B11206 */ +/** Obtain a CD-TEXT attribute that was set by burn_track_set_cdtext(). + @param t Track to inquire + @param block Number of the language block to inquire. + @param pack_type Pack type number to inquire. Used if pack_type_name + is NULL or empty text. Else submit 0 and a name. + @param pack_type_name The pack type by name. + See above burn_track_set_cdtext(). + @param payload Will return a pointer to text bytes. + Not a copy of data. Do not free() this address. + If no text attribute is attached for pack type and + block, then payload is returned as NULL. The return + value will not indicate error in this case. + @param length Will return the number of bytes pointed to by payload. + Including terminating 0-bytes. + @param flag Bitfield for control purposes. Unused yet. Submit 0. + @return 1=single byte char , 2= double byte char , <=0 error + @since 1.2.0 +*/ +int burn_track_get_cdtext(struct burn_track *t, int block, + int pack_type, char *pack_type_name, + unsigned char **payload, int *length, int flag); + +/* ts B11206 */ +/** Remove all CD-TEXT attributes of the given block from the track. + They were attached by burn_track_set_cdtext(). + @param t Track where to remove the CD-TEXT attribute. + @param block Number of the language block in which the attribute + shall appear. Possible values: 0 to 7. + -1 causes text packs of all blocks to be removed. + @return > 0 is success, <= 0 failure + @since 1.2.0 +*/ +int burn_track_dispose_cdtext(struct burn_track *t, int block); + + +/* ts A90910 */ +/** Activates CD XA compatibility modes. + libburn currently writes data only in CD mode 1. Some programs insist in + sending data with additional management bytes. These bytes have to be + stripped in order to make the input suitable for BURN_MODE1. + @param t The track to manipulate + @param value 0= no conversion + 1= strip 8 byte sector headers of CD-ROM XA mode 2 form 1 + see MMC-5 4.2.3.8.5.3 Block Format for Mode 2 form 1 Data + all other values are reserved + @return 1=success , 0=unacceptable value + @since 0.7.2 +*/ +int burn_track_set_cdxa_conv(struct burn_track *t, int value); + + +/** Set the ISRC details for a track. When writing to CD media, ISRC will get + written into the Q sub-channel. + @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); + +/* ts B11226 */ +/** Set the composed ISRC string for a track. This is an alternative to + burn_track_set_isrc(). + @param t The track to be manipulated + @param isrc 12 characters which are composed from ISRC details. + Format is CCOOOYYSSSSS, terminated by a 0-byte: + Country, Owner, Year(decimal digits), Serial(decimal digits). + @param flag Bitfield for control purposes. Unused yet. Submit 0. + @return > 0 indicates success, <= 0 means failure + @since 1.2.0 +*/ +int burn_track_set_isrc_string(struct burn_track *t, char isrc[13], int flag); + +/** Disable ISRC parameters for a track + @param t The track to change +*/ +void burn_track_clear_isrc(struct burn_track *t); + + +/* ts B20103 */ +/** Define an index start address within a track. The index numbers inside a + track have to form sequence starting at 0 or 1 with no gaps up to the + highest number used. They affect only writing of CD SAO sessions. + The first index start address of a track must be 0. + Blocks between index 0 and index 1 are considered to be located before the + track start as of the table-of-content. + @param t The track to be manipulated + @param index_number A number between 0 and 99 + @param relative_lba The start address relative to the start of the + burn_source of the track. It will get mapped to the + appropriate absolute block address. + @param flag Bitfield for control purposes. Unused yet. Submit 0. + @return > 0 indicates success, <= 0 means failure + @since 1.2.0 +*/ +int burn_track_set_index(struct burn_track *t, int index_number, + unsigned int relative_lba, int flag); + +/* ts B20103 */ +/** Remove all index start addresses and reset to the default indexing of + CD SAO sessions. This means index 0 of track 1 reaches from LBA -150 + to LBA -1. Index 1 of track 1 reaches from LBA 0 to track end. Index 1 + of track 2 follows immediately. The same happens for all further tracks + after the end of their predecessor. + @param t The track to be manipulated + @param flag Bitfield for control purposes. Unused yet. Submit 0. + @return > 0 indicates success, <= 0 means failure + @since 1.2.0 +*/ +int burn_track_clear_indice(struct burn_track *t, int flag); + + +/* ts B20110 */ +/** Define whether a pre-gap shall be written before the track and how many + sectors this pre-gap shall have. A pre-gap is written in the range of track + index 0 and contains zeros resp. silence. No bytes from the track source + will be read for writing the pre-gap. + This setting affects only CD SAO write runs. + The first track automatically gets a pre-gap of at least 150 sectors. Its + size may be enlarged by this call. Further pre-gaps are demanded by MMC + for tracks which follow tracks of a different mode. (But Mode mixing in + CD SAO sessions is currently not supported by libburn.) + @param t The track to change + @param size Number of sectors in the pre-gap. + -1 disables pre-gap, except for the first track. + libburn allows 0, but MMC does not propose this. + @param flag Bitfield for control purposes. Unused yet. Submit 0. + @return > 0 indicates success, <= 0 means failure + @since 1.2.0 +*/ +int burn_track_set_pregap_size(struct burn_track *t, int size, int flag); + +/* ts B20111 */ +/** Define whether a post-gap shall be written at the end of the track and + how many sectors this gap shall have. A post-gap occupies the range of + an additional index of the track. It contains zeros. No bytes from the + track source will be read for writing the post-gap. + This setting affects only CD SAO write runs. + MMC prescribes to add a post-gap to a data track which is followed by + a non-data track. (But libburn does not yet support mixed mode CD SAO + sessions.) + @param t The track to change + @param size Number of sectors in the post-gap. + -1 disables post-gap. + libburn allows 0, but MMC does not propose this. + @param flag Bitfield for control purposes. Unused yet. Submit 0. + @return > 0 indicates success, <= 0 means failure + @since 1.2.0 +*/ +int burn_track_set_postgap_size(struct burn_track *t, int size, int flag); + + +/* 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 + @since 0.2.6 +*/ +int burn_track_set_byte_swap(struct burn_track *t, int swap_source_bytes); + + +/** 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 + @since 0.3.4 +*/ +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); + + +/* ts A91122 : An interface to open(O_DIRECT) or similar OS tricks. */ + +/** Opens a file with eventual acceleration preparations which may depend + on the operating system and on compile time options of libburn. + You may use this call instead of open(2) for opening file descriptors + which shall be handed to burn_fd_source_new(). + This should only be done for tracks with BURN_BLOCK_MODE1 (2048 bytes + per block). + + If you use this call then you MUST allocate the buffers which you use + with read(2) by call burn_os_alloc_buffer(). Read sizes MUST be a multiple + of a safe buffer amount. Else you risk that track data get altered during + transmission. + burn_disk_write() will allocate a suitable read/write buffer for its own + operations. A fifo created by burn_fifo_source_new() will allocate + suitable memory for its buffer if called with flag bit0 and a multiple + of a safe buffer amount. + @param path The file address to open + @param open_flags The flags as of man 2 open. Normally just O_RDONLY. + @param flag Bitfield for control purposes (unused yet, submit 0). + @return A file descriptor as of open(2). Finally to be disposed + by close(2). + -1 indicates failure. + @since 0.7.4 +*/ +int burn_os_open_track_src(char *path, int open_flags, int flag); + +/** Allocate a memory area that is suitable for reading with a file descriptor + opened by burn_os_open_track_src(). + @param amount Number of bytes to allocate. This should be a multiple + of the operating system's i/o block size. 32 KB is + guaranteed by libburn to be safe. + @param flag Bitfield for control purposes (unused yet, submit 0). + @return The address of the allocated memory, or NULL on failure. + A non-NULL return value has finally to be disposed via + burn_os_free_buffer(). + @since 0.7.4 +*/ +void *burn_os_alloc_buffer(size_t amount, int flag); + +/** Dispose a memory area which was obtained by burn_os_alloc_buffer(), + @param buffer Memory address to be freed. + @param amount The number of bytes which was allocated at that + address. + @param flag Bitfield for control purposes (unused yet, submit 0). + @return 1 success , <=0 failure + @since 0.7.4 +*/ +int burn_os_free_buffer(void *buffer, size_t amount, int flag); + + +/** 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 B00922 */ +/** Creates an offset source which shall provide a byte interval of a stream + to its consumer. It is supposed to be chain-linked with other offset + sources which serve neighboring consumers. The chronological sequence + of consumers and the sequence of offset sources must match. The intervals + of the sources must not overlap. + + A chain of these burn_source objects may be used to feed multiple tracks + from one single stream of input bytes. + Each of the offset sources will skip the bytes up to its start address and + provide the prescribed number of bytes to the track. Skipping takes into + respect the bytes which have been processed by eventual predecessors in the + chain. + Important: It is not allowed to free an offset source before its successor + has ended its work. Best is to keep them all until all tracks + are done. + + @param inp The burn_source object from which to read stream data. + E.g. created by burn_file_source_new(). + @param prev The eventual offset source object which shall read data from + inp before the new offset source will begin its own work. + This must either be a result of burn_offst_source_new() or + it must be NULL. + @param start The byte address where to start reading bytes for the + consumer. inp bytes may get skipped to reach this address. + @param size The number of bytes to be delivered to the consumer. + If size is <= 0 then it may be set later by a call of method + set_size(). If it is >= 0, then it can only be changed if + flag bit0 was set with burn_offst_source_new(). + @param flag Bitfield for control purposes + bit0 = Prevent set_size() from overriding interval sizes > 0. + If such a size is already set, then the new one will + only affect the reply of get_size(). + See also above struct burn_source. + @since 1.2.0 + @return Pointer to a burn_source object, later to be freed by + burn_source_free(). NULL indicates failure. + @since 0.8.8 +*/ +struct burn_source *burn_offst_source_new( + struct burn_source *inp, struct burn_source *prev, + off_t start, off_t size, int flag); + +/* 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. It may be attached to one track as its only data source + by burn_track_set_source(), or it may be used as input for other burn + sources. + A fifo starts its life in "standby" mode with no buffer space allocated. + As soon as its consumer 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, + 2352 for sources which deliver for BURN_BLOCK_AUDIO, + 2056 for sources which shall get treated by + burn_track_set_cdxa_conv(track, 1). + 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: + bit0= The read method of inp is capable of delivering + arbitrary amounts of data per call. Not only one + sector. + Suitable for inp from burn_file_source_new() + and burn_fd_source_new() if not the fd has + exotic limitations on read size. + You MUST use this on inp which uses an fd opened + with burn_os_open_track_src(). + Better do not use with other inp types. + @since 0.7.4 + @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(). + @since 0.4.0 +*/ +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 + @since 0.4.0 +*/ +int burn_fifo_inquire_status(struct burn_source *fifo, int *size, + int *free_bytes, char **status_text); + +/* ts A91125 */ +/** Inquire various counters which reflect the fifo operation. + @param fifo The fifo object to inquire + @param total_min_fill The minimum number of bytes in the fifo. Beginning + from the moment when fifo consumption is enabled. + @param interval_min_fill The minimum byte number beginning from the moment + when fifo consumption is enabled or from the + most recent moment when burn_fifo_next_interval() + was called. + @param put_counter The number of data transactions into the fifo. + @param get_counter The number of data transactions out of the fifo. + @param empty_counter The number of times the fifo was empty. + @param full_counter The number of times the fifo was full. + @since 0.7.4 +*/ +void burn_fifo_get_statistics(struct burn_source *fifo, + int *total_min_fill, int *interval_min_fill, + int *put_counter, int *get_counter, + int *empty_counter, int *full_counter); + +/* ts A91125 */ +/** Inquire the fifo minimum fill counter for intervals and reset that counter. + @param fifo The fifo object to inquire + @param interval_min_fill The minimum number of bytes in the fifo. Beginning + from the moment when fifo consumption is enabled + or from the most recent moment when + burn_fifo_next_interval() was called. + @since 0.7.4 +*/ +void burn_fifo_next_interval(struct burn_source *fifo, int *interval_min_fill); + +/* ts A80713 */ +/** Obtain a preview of the first input data of a fifo which was created + by burn_fifo_source_new(). The data will later be delivered normally to + the consumer track of the fifo. + bufsize may not be larger than the fifo size (chunk_size * chunks) - 32k. + This call will succeed only if data consumption by the track has not + started yet, i.e. best before the call to burn_disc_write(). + It will start the worker thread of the fifo with the expectable side + effects on the external data source. Then it waits either until enough + data have arrived or until it becomes clear that this will not happen. + The call may be repeated with increased bufsize. It will always yield + the bytes beginning from the first one in the fifo. + @param fifo The fifo object to inquire resp. start + @param buf Pointer to memory of at least bufsize bytes where to + deliver the peeked data. + @param bufsize Number of bytes to peek from the start of the fifo data + @param flag Bitfield for control purposes (unused yet, submit 0). + @return <0 on severe error, 0 if not enough data, 1 if bufsize bytes read + @since 0.5.0 +*/ +int burn_fifo_peek_data(struct burn_source *fifo, char *buf, int bufsize, + int flag); + +/* ts A91125 */ +/** Start the fifo worker thread and wait either until the requested number + of bytes have arrived or until it becomes clear that this will not happen. + Filling will go on asynchronously after burn_fifo_fill() returned. + This call and burn_fifo_peek_data() do not disturb each other. + @param fifo The fifo object to start + @param fill Number of bytes desired. Expect to get return 1 if + at least fifo size - 32k were read. + @param flag Bitfield for control purposes. + bit0= fill fifo to maximum size + @return <0 on severe error, 0 if not enough data, + 1 if desired amount or fifo full + @since 0.7.4 +*/ +int burn_fifo_fill(struct burn_source *fifo, int fill, int flag); + + +/* 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 + @since 0.3.6 +*/ +int burn_track_set_size(struct burn_track *t, off_t size); + + +/** Tells how many sectors a track will have on disc, resp. already has on + disc. This includes offset, payload, tail, and post-gap, but not pre-gap. + The result is 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. + @param t The track to inquire + @param read_bytes Number of bytes read from the track source + @param written_bytes Number of bytes written to track + @since 0.2.6 +*/ +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, 1xBD = 4496 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 + @since 0.3.8 +*/ +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 [Derek Foreman's ?] debugging, they will disappear */ +/* ts B11012 : + Of course, API symbols will not disappear. But these functions are of + few use, as they only print DEBUG messages. +*/ +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. + @since 0.3.2 +*/ +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_disc_write() 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. This is only needed with CD media + and possibly with old DVD-R drives. All other media types are not + vulnerable to burn failure due to buffer underrun. + @param opts The write opts to change + @param underrun_proof if non-zero, buffer underrun protection is enabled + @return Returns 1 if the drive announces to be capable of underrun + prevention, + Returns 0 if not. +*/ +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); + + +/** The Q sub-channel of a CD may contain a Media Catalog Number of 13 decimal + digits. This call sets the string of digits, but does not yet activate it + for writing. + @param opts The write opts to change + @param mediacatalog The 13 decimal digits as ASCII bytes. I.e. '0' = 0x30. +*/ +void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, + unsigned char mediacatalog[13]); + +/** This call activates the Media Catalog Number for writing. The digits of + that number have to be set by call burn_write_opts_set_mediacatalog(). + @param opts The write opts to change + @param has_mediacatalog 1= activate writing of catalog to Q sub-channel + 0= deactivate it +*/ +void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts, + int has_mediacatalog); + + +/* 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. + Note: DVD-R[W] in write mode BURN_WRITE_SAO are not capable of this. + DVD-R DL are not capable of this at all. + libburn will refuse to write if burn_write_opts_set_multi() is + enabled under such conditions. + @param opts The option object to be manipulated + @param multi 1=media will be appendable, 0=media will be closed (default) + @since 0.2.6 +*/ +void burn_write_opts_set_multi(struct burn_write_opts *opts, int multi); + + +/* ts B31024 */ +/** Set the severity to be used with write error messages which are potentially + caused by not using write type BURN_WRITE_SAO on fast blanked DVD-RW. + + Normally the call burn_write_opts_auto_write_type() can prevent such + errors by looking for MMC feature 21h "Incremental Streaming Writable" + which anounnces the capability for BURN_WRITE_TAO and multi session. + Regrettable many drives announce feature 21h even if they only can do + BURN_WRITE_SAO. This mistake becomes obvious by an early write error. + + If you plan to call burn_drive_was_feat21_failure() and to repeat the + burn attempt with BURN_WRITE_SAO, then set the severity of the error + message low enough, so that the application does not see reason to abort. + + @param opts The option object to be manipulated + @param severity Severity as with burn_msgs_set_severities(). + "ALL" or empty text means the default severity that + is attributed to other kinds of write errors. +*/ +void burn_write_opts_set_fail21h_sev(struct burn_write_opts *opts, + char *severity); + +/* ts B11204 */ +/** Submit an array of CD-TEXT packs which shall be written to the Lead-in + of a SAO write run on CD. + @param opts The option object to be manipulated + @param text_packs Array of bytes which form CD-TEXT packs of 18 bytes + each. For a description of the format of the array, + see file doc/cdtext.txt. + No header of 4 bytes must be prepended which would + tell the number of pack bytes + 2. + This parameter may be NULL if the currently attached + array of packs shall be removed. + @param num_packs The number of 18 byte packs in text_packs. + This parameter may be 0 if the currently attached + array of packs shall be removed. + @param flag Bitfield for control purposes. + bit0= do not verify checksums + bit1= repair mismatching checksums + bit2= repair checksums if they are 00 00 with each pack + @return 1 on success, <= 0 on failure + @since 1.2.0 +*/ +int burn_write_opts_set_leadin_text(struct burn_write_opts *opts, + unsigned char *text_packs, + int num_packs, int flag); + + +/* 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) + @since 0.3.0 +*/ +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 + @since 0.3.4 +*/ +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 + @since 0.3.4 +*/ +void burn_write_opts_set_force(struct burn_write_opts *opts, int use_force); + + +/* ts A80412 */ +/** Eventually makes use of the more modern write command AAh WRITE12 and + sets the Streaming bit. With DVD-RAM and BD this can override the + traditional slowdown to half nominal speed. But if it speeds up writing + then it also disables error management and correction. Weigh your + priorities. This affects the write operations of burn_disc_write() + and subsequent calls of burn_random_access_write(). + @param opts The write opts to change + @param value 0=use 2Ah WRITE10, 1=use AAh WRITE12 with Streaming bit + @since 0.6.4: + >=16 use WRITE12 but not before the LBA given by value + @since 0.4.6 +*/ +void burn_write_opts_set_stream_recording(struct burn_write_opts *opts, + int value); + +/* ts A91115 */ +/** Overrides the write chunk size for DVD and BD media which is normally + determined according to media type and setting of stream recording. + A chunk size of 64 KB may improve throughput with bus systems which show + latency problems. + @param opts The write opts to change + @param obs Number of bytes which shall be sent by a single write command. + 0 means automatic size, 32768 and 65336 are the only other + accepted sizes for now. + @since 0.7.4 +*/ +void burn_write_opts_set_dvd_obs(struct burn_write_opts *opts, int obs); + + +/* ts B20406 */ +/** Overrides the automatic decision whether to pad up the last write chunk to + its full size. This applies to DVD, BD and stdio: pseudo-drives. + Note: This override may get enabled fixely already at compile time by + defining macro Libburn_dvd_always_obs_paD . + @param opts The write opts to change + @param pad 1 means to pad up in any case, 0 means automatic decision. + @since 1.2.4 +*/ +void burn_write_opts_set_obs_pad(struct burn_write_opts *opts, int pad); + + +/* ts A91115 */ +/** Sets the rythm by which stdio pseudo drives force their output data to + be consumed by the receiving storage device. This forcing keeps the memory + from being clogged with lots of pending data for slow devices. + @param opts The write opts to change + @param rythm Number of 2KB output blocks after which fsync(2) is + performed. + -1 means no fsync() + 0 means default + 1 means fsync() only at end, @since 1.3.8 (noop before 1.3.8) + elsewise the value must be >= 32. + Default is currently 8192 = 16 MB. + @since 0.7.4 +*/ +void burn_write_opts_set_stdio_fsync(struct burn_write_opts *opts, int rythm); + + +/** 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); + + +/* ts A90815 */ +/** Gets the list of profile codes supported by the drive. + Profiles depict the feature sets which constitute media types. For + known profile codes and names see burn_disc_get_profile(). + @param d is the drive to query + @param num_profiles returns the number of supported profiles + @param profiles returns the profile codes + @param is_current returns the status of the corresponding profile code: + 1= current, i.e. the matching media is loaded + 0= not current, i.e. the matching media is not loaded + @return always 1 for now + @since 0.7.0 +*/ +int burn_drive_get_all_profiles(struct burn_drive *d, int *num_profiles, + int profiles[64], char is_current[64]); + + +/* ts A90815 */ +/** Obtains the profile name associated with a profile code. + @param profile_code the profile code to be translated + @param name returns the profile name (e.g. "DVD+RW") + @return 1= known profile code , 0= unknown profile code + @since 0.7.0 +*/ +int burn_obtain_profile_name(int profile_code, char name[80]); + + +/** 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 + @since 0.2.6 +*/ +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 + @since 0.3.0 +*/ +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 + @since 0.3.8 +*/ +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 + @since 0.3.0 +*/ +int burn_drive_free_speedlist(struct burn_speed_descriptor **speed_list); + + +/* ts A70203 */ +/* @since 0.3.2 */ +/** 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 */ + /* @since 0.3.8 */ + /** 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 + @since 0.3.2 +*/ +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 + @since 0.3.2 +*/ +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 complete sessions for the disc + THIS IS NO LONGER VALID AFTER YOU ADD OR REMOVE A SESSION + The result array contains *num + burn_disc_get_incomplete_sessions() + elements. All above *num are incomplete sessions. + Typically there is at most one incomplete session with one empty track. + DVD+R and BD-R seem to allow more than one track with even readable data. + @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); + +/* ts B30112 */ +/* @since 1.2.8 */ +/** Obtains the number of incomplete sessions which are recorded in the + result array of burn_disc_get_sessions() after the complete sessions. + See above. + @param d Disc object to inquire + @return Number of incomplete sessions +*/ +int burn_disc_get_incomplete_sessions(struct burn_disc *d); + + +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. + This is the runtime counterpart of the three build time macros + burn_header_version_* below. + @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 A80129 */ +/* @since 0.4.4 */ +/** These three release version numbers tell the revision of this header file + and of the API it describes. They are memorized by applications at build + time. + Immediately after burn_initialize() an application should do this check: + burn_version(&major, &minor, µ); + if(major > burn_header_version_major + || (major == burn_header_version_major + && (minor > burn_header_version_minor + || (minor == burn_header_version_minor + && micro >= burn_header_version_micro)))) { + ... Young enough. Go on with program run .... + } else { + ... Too old. Do not use this libburn version ... + } + +*/ +#define burn_header_version_major 1 +#define burn_header_version_minor 3 +#define burn_header_version_micro 9 +/** Note: + Above version numbers are also recorded in configure.ac because libtool + wants them as parameters at build time. + For the library compatibility check, BURN_*_VERSION in configure.ac + are not decisive. Only the three numbers above do matter. +*/ +/** Usage discussion: + +Some developers of the libburnia project have differing +opinions how to ensure the compatibility of libaries +and applications. + +It is about whether to use at compile time and at runtime +the version numbers isoburn_header_version_* provided here. +Thomas Schmitt advises to use them. +Vreixo Formoso advises to use other means. + +At compile time: + +Vreixo Formoso advises to leave proper version matching +to properly programmed checks in the the application's +build system, which will eventually refuse compilation. + +Thomas Schmitt advises to use the macros defined here +for comparison with the application's requirements of +library revisions and to eventually break compilation. + +Both advises are combinable. I.e. be master of your +build system and have #if checks in the source code +of your application, nevertheless. + +At runtime (via *_is_compatible()): + +Vreixo Formoso advises to compare the application's +requirements of library revisions with the runtime +library. This is to allow runtime libraries which are +young enough for the application but too old for +the lib*.h files seen at compile time. + +Thomas Schmitt advises to compare the header +revisions defined here with the runtime library. +This is to enforce a strictly monotonous chain +of revisions from app to header to library, +at the cost of excluding some older libraries. + +These two advises are mutually exclusive. + +*/ + +/* ts A91226 */ +/** Obtain the id string of the SCSI transport interface. + This interface may be a system specific adapter module of libburn or + an adapter to a supporting library like libcdio. + @param flag Bitfield for control puposes, submit 0 for now + @return A pointer to the id string. Do not alter the string content. + @since 0.7.6 +*/ +char *burn_scsi_transport_id(int flag); + +/* 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 + @since 0.2.6 +*/ +int burn_msgs_set_severities(char *queue_severity, + char *print_severity, char *print_id); + +/* ts A60924 : ticket 74 */ +/* @since 0.2.6 */ +#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 listed 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 + @since 0.2.6 +*/ +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 + @since 0.4.0 +*/ +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 + @since 0.4.0 +*/ +int burn_text_to_sev(char *severity_name, int *severity_number, int flag); + + +/* ts A80202 */ +/** Convert a severity number into a severity name + @param severity_number The rank number: the higher, the more severe. + @param severity_name A name as with burn_msgs_submit(), e.g. "SORRY". + @param flag Bitfield for control purposes (unused yet, submit 0) + @return >0 success, <=0 failure + @since 0.4.4 +*/ +int burn_sev_to_text(int severity_number, char **severity_name, int flag); + + +/* ts B21214 */ +/** Return a blank separated list of severity names. Sorted from low + to high severity. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return A constant string with the severity names + @since 1.2.6 +*/ +char *burn_list_sev_texts(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 + @since 0.4.0 +*/ +int burn_set_messenger(void *messenger); + + +/* ts A61002 */ +/* @since 0.2.6 */ +/** The prototype of a handler function suitable for burn_set_signal_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 built-in signal handling. Either by setting an own handler or + by activating the built-in signal handler. + + A function parameter handle of NULL activates the built-in abort handler. + Depending on mode it may cancel all drive operations, wait for all drives + to become idle, exit(1). It may also prepare function + burn_drive_get_status() for waiting and performing exit(1). + Parameter handle may be NULL or a text that shall be used as prefix for + pacifier messages of burn_abort_pacifier(). Other than with an application + provided handler, the prefix char array does not have to be kept existing + until the eventual signal event. + Before version 0.7.8 only action 0 was available. I.e. the built-in handler + waited for the drives to become idle and then performed exit(1) directly. + But during burn_disc_write() onto real CD or DVD, FreeBSD 8.0 pauses the + other threads until the signal handler returns. + The new actions try to avoid this deadlock. It is advised to use action 3 + at least during burn_disc_write(), burn_disc_erase(), burn_disc_format(): + burn_set_signal_handling(text, NULL, 0x30); + and to call burn_is_aborting(0) when the drive is BURN_DRIVE_IDLE. + If burn_is_aborting(0) returns 1, then call burn_abort() and exit(1). + + @param handle Opaque handle eventually pointing to an application + provided memory object + @param handler A function to be called on signals, if the handling bits + in parameter mode are set 0. + It will get parameter handle as argument. flag will be 0. + It should finally call burn_abort(). See there. + If the handler function returns 2 or -2, then the wrapping + signal handler of libburn will return and let the program + continue its operations. Any other return value causes + exit(1). + @param mode : bit0 - bit3: Handling of received signals: + 0 Install libburn wrapping signal handler, which will 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 + bit4 - bit7: With handler == NULL : + Action of built-in handler. "control thread" is the one + which called burn_set_signal_handling(). + All actions activate receive mode 2 to ignore further + signals. + 0 Same as 1 (for pre-0.7.8 backward compatibility) + @since 0.7.8 + 1 Catch the control thread in abort handler, call + burn_abort() with a patience value > 0 and + finally exit(1). Does not always work with FreeBSD. + 2 Call burn_abort() with patience -1 and return from + handler. When the control thread calls + burn_drive_get_status(), then call burn_abort() + with patience 1 instead, and finally exit(1). + Does not always work with FreeBSD. + 3 Call burn_abort() with patience -1, return from handler. + It is duty of the application to detect a pending abort + condition by calling burn_is_aborting() and to wait for + all drives to become idle. E.g. by calling burn_abort() + with patience >0. + 4 Like 3, but without calling burn_abort() with -1. Only + the indicator of burn_is_aborting() gets set. + bit8: @since 1.3.2 + try to ignore SIGPIPE (regardless of bit0 - bit3) + + @since 0.2.6 +*/ +void burn_set_signal_handling(void *handle, burn_abort_handler_t handler, + int mode); + + +/* ts B00304 */ +/* Inquire whether the built-in abort handler was triggered by a signal. + This has to be done to detect pending abort handling if signal handling + was set to the built-in handler and action was set to 2 or 3. + @param flag Bitfield for control purposes (unused yet, submit 0) + @return 0 = no abort was triggered + >0 = action that was triggered (action 0 is reported as 1) + @since 0.7.8 +*/ +int burn_is_aborting(int flag); + + +/* 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 transfered bytes * -1 + @since 0.4.0 +*/ +int burn_random_access_write(struct burn_drive *d, off_t byte_address, + char *data, off_t data_count, int flag); + + +/* ts A81215 */ +/** Inquire the maximum amount of readable data. + It is supposed that all LBAs in the range from 0 to media_read_acpacity-1 + can be read via burn_read_data() although some of them may never have been + recorded. If tracks are recognizable then it is better to only read + LBAs which are part of some track. + If the drive is actually a large file or block device, then the capacity + is curbed to a maximum of 0x7ffffff0 blocks = 4 TB - 32 KB. + @param d The drive from which to read + @param capacity Will return the result if valid + @param flag Bitfield for control purposes: Unused yet, submit 0. + @return 1=sucessful , <=0 an error occured + @since 0.6.0 +*/ +int burn_get_read_capacity(struct burn_drive *d, int *capacity, 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 from which to read + @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) + The counted bytes are supposed to be valid. + @param flag Bitfield for control purposes: + bit0= - reserved - + bit1= do not submit error message if read error + bit2= on error do not try to read a second time + with single block steps. + @since 0.5.2 + bit3= return -2 on permission denied error rather than + issueing a warning message. + @since 1.0.6 + bit4= return -3 on SCSI error + 5 64 00 ILLEGAL MODE FOR THIS TRACK + and prevent this error from being reported as + event message. Do not retry reading in this case. + (Useful to try the last two blocks of a CD + track which might be non-data because of TAO.) + @since 1.2.6 + bit5= issue messages with severity DEBUG if they would + be suppressed by bit1. + @since 1.4.0 + @return 1=sucessful , <=0 an error occured + with bit3: -2= permission denied error + @since 0.4.0 +*/ +int burn_read_data(struct burn_drive *d, off_t byte_address, + char data[], off_t data_size, off_t *data_count, int flag); + + +/* ts B21119 */ +/** Read CD audio sectors in random access mode. + The drive must be grabbed successfully before calling this function. + Only CD audio tracks with 2352 bytes per sector can be read this way. + I.e. not data tracks, not CD-video-stream, ... + + Note that audio data do not have exact block addressing. If you read a + sequence of successive blocks then you will get a seamless stream + of data. But the actual start and end position of this audio stream + will differ by a few dozens of milliseconds, depending on individual + CD and individual drive. + Expect leading and trailing zeros, as well as slight truncation. + + @param d The drive from which to read. + It must be a real MMC drive (i.e. not a stdio file) + and it must have a CD loaded (i.e. not DVD or BD). + @param sector_no The sector number (Logical Block Address) + It may be slightly below 0, depending on drive and + medium. -150 is a lower limit. + @param data A memory buffer capable of taking data_size bytes + @param data_size The amount of data to be read. This must be aligned + to full multiples of 2352. + @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 + bit2= on error do not try to read a second time + with single block steps. + bit3= Enable DAP : "flaw obscuring mechanisms like + audio data mute and interpolate" + bit4= return -3 on SCSI error + 5 64 00 ILLEGAL MODE FOR THIS TRACK + and prevent this error from being reported as + event message. Do not retry reading in this case. + (Useful to try the last two blocks of a CD + track which might be non-audio because of TAO.) + bit5= issue messages with severity DEBUG if they would + be suppressed by bit1. + @since 1.4.0 + @return 1=sucessful , <=0 an error occured + with bit3: -2= permission denied error + @since 1.2.6 +*/ +int burn_read_audio(struct burn_drive *d, int sector_no, + char data[], off_t data_size, off_t *data_count, int flag); + + +/* ts B30522 */ +/** Extract an interval of audio sectors from CD and store it as a WAVE + audio file on hard disk. + + @param drive The drive from which to read. + @param start_sector The logical block address of the first audio sector + which shall be read. + @param sector_count The number of audio sectors to be read. + Each sector consists of 2352 bytes. + @param target_path The address of the file where to store the extracted + audio data. Will be opened O_WRONLY | O_CREAT. + The file name should have suffix ".wav". + @param flag Bitfield for control purposes: + bit0= Report about progress by UPDATE messages + bit3= Enable DAP : "flaw obscuring mechanisms like + audio data mute and interpolate" + @since 1.3.2 +*/ +int burn_drive_extract_audio(struct burn_drive *drive, + int start_sector, int sector_count, + char *target_path, int flag); + + +/* ts B30522 */ +/** Extract all audio sectors of a track from CD and store them as a WAVE + audio file on hard disk. + + @param drive The drive from which to read. + @param track The track which shall be extracted. + @param target_path The address of the file where to store the extracted + audio data. Will be opened O_WRONLY | O_CREAT. + The file name should have suffix ".wav". + @param flag Bitfield for control purposes: + bit0= Report about progress by UPDATE messages + bit3= Enable DAP : "flaw obscuring mechanisms like + audio data mute and interpolate" + @since 1.3.2 +*/ +int burn_drive_extract_audio_track(struct burn_drive *drive, + struct burn_track *track, + char *target_path, int flag); + + +/* ts 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 + 4= stdio-drive, random access, read-only + (only if enabled by burn_allow_drive_role_4()) + 5= stdio-drive, random access, write-only + (only if enabled by burn_allow_drive_role_4()) + @since 0.4.0 +*/ +int burn_drive_get_drive_role(struct burn_drive *d); + + +/* ts B10312 */ +/** Allow drive role 4 "random access read-only" + and drive role 5 "random access write-only". + By default a random access file assumes drive role 2 "read-write" + regardless whether it is actually readable or writeable. + If enabled, random-access file objects which recognizably allow no + writing will be classified as role 4 and those which allow no reading + will get role 5. + Candidates are drive addresses of the form stdio:/dev/fd/# , where # is + the integer number of an open file descriptor. If this descriptor was + opened read-only resp. write-only, then it gets role 4 resp. role 5. + Other paths may get tested by an attempt to open them for read-write + (role 2) resp. read-only (role 4) resp. write-only (role 5). See bit1. + @param allowed Bitfield for control purposes: + bit0= Enable roles 4 and 5 for drives which get + aquired after this call + bit1= with bit0: + Test whether the file can be opened for + read-write resp. read-only resp. write-only. + Classify as roles 2 resp. 4 resp. 5. + bit2= with bit0 and bit1: + Classify files which cannot be opened at all + as role 0 : useless dummy. + Else classify as role 2. + bit3= Classify non-empty role 5 drives as + BURN_DISC_APPENDABLE with Next Writeable Address + after the end of the file. It is nevertheless + possible to change this address by call + burn_write_opts_set_start_byte(). + @since 1.0.6 +*/ +void burn_allow_drive_role_4(int allowed); + + +/* 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 + @since 0.4.0 +*/ +int burn_drive_equals_adr(struct burn_drive *d1, char *adr2, int drive_role2); + + + +/* + Audio track data extraction facility. +*/ + +/* Maximum size for address paths and fmt_info strings */ +#define LIBDAX_AUDIOXTR_STRLEN 4096 + + +/** 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; + + +/** 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 + @since 0.2.4 +*/ +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 = Little Endian + 1= Motorola = Big Endian + @param flag Bitfield for control purposes (unused yet, submit 0) + @return >0 success, <=0 failure + @since 0.2.4 +*/ +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 + @since 0.2.4 +*/ +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 + @since 0.2.4 +*/ +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 WAVE (.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 + @since 0.2.4 +*/ +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 + @since 0.2.4 +*/ +int libdax_audioxtr_destroy(struct libdax_audioxtr **xtr, int flag); + + +#ifndef DOXYGEN + +BURN_END_DECLS + +#endif + + +/* ts A91205 */ +/* The following experiments may be interesting in future: +*/ + +/* Perform OPC explicitely. + # define Libburn_pioneer_dvr_216d_with_opC 1 +*/ + +/* Load mode page 5 and modify it rather than composing from scratch. + # define Libburn_pioneer_dvr_216d_load_mode5 1 +*/ + +/* Inquire drive events and react by reading configuration or starting unit. + # define Libburn_pioneer_dvr_216d_get_evenT 1 +*/ + +/* ts A91112 */ +/* Do not probe CD modes but declare only data and audio modes supported. + For other modes resp. real probing one has to call + burn_drive_probe_cd_write_modes(). + +*/ +#define Libburn_dummy_probe_write_modeS 1 + +/* ts B30112 */ +/* Handle DVD+R with reserved tracks in incomplete first session + by loading info about the incomplete session into struct burn_disc +*/ +#define Libburn_disc_with_incomplete_sessioN 1 + + +/* Early experimental: + Do not define Libburn_develop_quality_scaN unless you want to work + towards a usable implementation. + If it gets enabled, then the call must be published in libburn/libburn.ver +*/ +#ifdef Libburn_develop_quality_scaN + +/* ts B21108 */ +/* Experiments mit quality scan command F3 on Optiarc drive */ +int burn_nec_optiarc_rep_err_rate(struct burn_drive *d, + int start_lba, int rate_period, int flag); + +#endif /* Libburn_develop_quality_scaN */ + + + +#endif /*LIBBURN_H*/ diff --git a/trunk/libburn/libburn.ver b/trunk/libburn/libburn.ver new file mode 100644 index 0000000..3bdcf39 --- /dev/null +++ b/trunk/libburn/libburn.ver @@ -0,0 +1,204 @@ +LIBBURN4 { +global: +burn_abort; +burn_abort_pacifier; +burn_allow_drive_role_4; +burn_allow_untested_profiles; +burn_cdtext_from_session; +burn_cdtext_from_packfile; +burn_disc_add_session; +burn_disc_available_space; +burn_disc_close_damaged; +burn_disc_create; +burn_disc_erasable; +burn_disc_erase; +burn_disc_format; +burn_disc_free; +burn_disc_free_multi_caps; +burn_disc_get_bd_spare_info; +burn_disc_get_cd_info; +burn_disc_get_format_descr; +burn_disc_get_formats; +burn_disc_get_incomplete_sessions; +burn_disc_get_leadin_text; +burn_disc_get_media_id; +burn_disc_get_msc1; +burn_disc_get_multi_caps; +burn_disc_get_phys_format_info; +burn_disc_get_profile; +burn_disc_get_sectors; +burn_disc_get_sessions; +burn_disc_get_status; +burn_disc_next_track_is_damaged; +burn_disc_pretend_blank; +burn_disc_pretend_full; +burn_disc_pretend_full_uncond; +burn_disc_read; +burn_disc_read_atip; +burn_disc_remove_session; +burn_disc_track_lba_nwa; +burn_disc_write; +burn_drive_add_whitelist; +burn_drive_cancel; +burn_drive_clear_whitelist; +burn_drive_convert_fs_adr; +burn_drive_convert_scsi_adr; +burn_drive_d_get_adr; +burn_drive_equals_adr; +burn_drive_extract_audio; +burn_drive_extract_audio_track; +burn_drive_free_speedlist; +burn_drive_get_adr; +burn_drive_get_all_profiles; +burn_drive_get_best_speed; +burn_drive_get_disc; +burn_drive_get_drive_role; +burn_drive_get_min_write_speed; +burn_drive_get_read_speed; +burn_drive_get_speedlist; +burn_drive_get_start_end_lba; +burn_drive_get_status; +burn_drive_get_write_speed; +burn_drive_grab; +burn_drive_info_forget; +burn_drive_info_free; +burn_drive_is_enumerable_adr; +burn_drive_leave_locked; +burn_drive_obtain_scsi_adr; +burn_drive_probe_cd_write_modes; +burn_drive_re_assess; +burn_drive_release; +burn_drive_scan; +burn_drive_scan_and_grab; +burn_drive_set_buffer_waiting; +burn_drive_set_speed; +burn_drive_set_stream_recording; +burn_drive_snooze; +burn_drive_was_feat21_failure; +burn_drive_wrote_well; +burn_fd_source_new; +burn_fifo_fill; +burn_fifo_get_statistics; +burn_fifo_inquire_status; +burn_fifo_next_interval; +burn_fifo_peek_data; +burn_fifo_source_new; +burn_file_source_new; +burn_finish; +burn_get_read_capacity; +burn_guess_cd_manufacturer; +burn_guess_manufacturer; +burn_initialize; +burn_is_aborting; +burn_lba_to_msf; +burn_list_sev_texts; +burn_lookup_device_link; +burn_make_input_sheet_v07t; +burn_msf_to_lba; +burn_msf_to_sectors; +burn_msgs_obtain; +burn_msgs_set_severities; +burn_msgs_submit; +burn_obtain_profile_name; +burn_offst_source_new; +burn_os_alloc_buffer; +burn_os_free_buffer; +burn_os_open_track_src; +burn_precheck_write; +burn_preset_device_open; +burn_random_access_write; +burn_read_audio; +burn_read_data; +burn_read_opts_free; +burn_read_opts_new; +burn_read_opts_read_subcodes_audio; +burn_read_opts_read_subcodes_data; +burn_read_opts_report_recovered_errors; +burn_read_opts_set_c2errors; +burn_read_opts_set_hardware_error_recovery; +burn_read_opts_set_hardware_error_retries; +burn_read_opts_set_raw; +burn_read_opts_transfer_damaged_blocks; +burn_scsi_transport_id; +burn_sectors_to_msf; +burn_session_add_track; +burn_session_by_cue_file; +burn_session_create; +burn_session_dispose_cdtext; +burn_session_free; +burn_session_get_cdtext; +burn_session_get_cdtext_par; +burn_session_get_hidefirst; +burn_session_get_leadout_entry; +burn_session_get_sectors; +burn_session_get_start_tno; +burn_session_get_tracks; +burn_session_hide_first_track; +burn_session_input_sheet_v07t; +burn_session_remove_track; +burn_session_set_cdtext; +burn_session_set_cdtext_par; +burn_session_set_start_tno; +burn_set_messenger; +burn_set_scsi_logging; +burn_set_signal_handling; +burn_set_verbosity; +burn_sev_to_text; +burn_source_free; +burn_structure_print_disc; +burn_structure_print_session; +burn_structure_print_track; +burn_text_to_sev; +burn_track_clear_indice; +burn_track_clear_isrc; +burn_track_create; +burn_track_define_data; +burn_track_dispose_cdtext; +burn_track_free; +burn_track_get_cdtext; +burn_track_get_counters; +burn_track_get_entry; +burn_track_get_mode; +burn_track_get_sectors; +burn_track_set_byte_swap; +burn_track_set_cdxa_conv; +burn_track_set_cdtext; +burn_track_set_default_size; +burn_track_set_index; +burn_track_set_isrc; +burn_track_set_isrc_string; +burn_track_set_postgap_size; +burn_track_set_pregap_size; +burn_track_set_size; +burn_track_set_source; +burn_version; +burn_write_opts_auto_write_type; +burn_write_opts_free; +burn_write_opts_get_drive; +burn_write_opts_new; +burn_write_opts_set_dvd_obs; +burn_write_opts_set_fail21h_sev; +burn_write_opts_set_fillup; +burn_write_opts_set_force; +burn_write_opts_set_format; +burn_write_opts_set_has_mediacatalog; +burn_write_opts_set_leadin_text; +burn_write_opts_set_mediacatalog; +burn_write_opts_set_multi; +burn_write_opts_set_obs_pad; +burn_write_opts_set_perform_opc; +burn_write_opts_set_simulate; +burn_write_opts_set_start_byte; +burn_write_opts_set_stdio_fsync; +burn_write_opts_set_stream_recording; +burn_write_opts_set_toc_entries; +burn_write_opts_set_underrun_proof; +burn_write_opts_set_write_type; +libdax_audioxtr_destroy; +libdax_audioxtr_detach_fd; +libdax_audioxtr_get_id; +libdax_audioxtr_get_size; +libdax_audioxtr_new; +libdax_audioxtr_read; +local: *; +}; diff --git a/trunk/libburn/libdax_audioxtr.c b/trunk/libburn/libdax_audioxtr.c new file mode 100644 index 0000000..ea5f322 --- /dev/null +++ b/trunk/libburn/libdax_audioxtr.c @@ -0,0 +1,335 @@ + +/* libdax_audioxtr + Audio track data extraction facility of libdax and libburn. + Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPLv2+ +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#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> + +/* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#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 or include libburn.h */ +#define LIBDAX_AUDIOXTR_H_PUBLIC 1 +#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 *) calloc(1, 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 | O_BINARY); + 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/trunk/libburn/libdax_audioxtr.h b/trunk/libburn/libdax_audioxtr.h new file mode 100644 index 0000000..9c8765e --- /dev/null +++ b/trunk/libburn/libdax_audioxtr.h @@ -0,0 +1,237 @@ + +/* libdax_audioxtr + Audio track data extraction facility of libdax and libburn. + Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPLv2+ +*/ + +#ifndef LIBDAX_AUDIOXTR_H_INCLUDED +#define LIBDAX_AUDIOXTR_H_INCLUDED 1 + + +/* Normally this public API is defined in <libburn/libburn.h> + Macro LIBDAX_AUDIOXTR_H_PUBLIC enables the definition for programs + which only include this file. +*/ +#ifdef LIBDAX_AUDIOXTR_H_PUBLIC + + /* 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); + +#endif /* LIBDAX_AUDIOXTR_H_PUBLIC */ + + +#ifdef LIBDAX_AUDIOXTR________________ + + +-- place documentation text here --- + + +#endif /* LIBDAX_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/trunk/libburn/libdax_msgs.c b/trunk/libburn/libdax_msgs.c new file mode 100644 index 0000000..5b16d1f --- /dev/null +++ b/trunk/libburn/libdax_msgs.c @@ -0,0 +1,450 @@ + +/* libdax_msgs + Message handling facility of libdax. + Copyright (C) 2006 - 2010 Thomas Schmitt <scdbackup@gmx.net>, + provided under GPL version 2 or later. +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/time.h> +#include <pthread.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 *) calloc(1, 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 *) calloc(1, 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,"MISHAP",6)==0) + *severity= LIBDAX_MSGS_SEV_MISHAP; + 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,"ERRFILE",7)==0) + *severity= LIBDAX_MSGS_SEV_ERRFILE; + else if(strncmp(severity_name,"ALL",3)==0) + *severity= LIBDAX_MSGS_SEV_ALL; + else { + *severity= LIBDAX_MSGS_SEV_ALL; + return(0); + } + return(1); +} + + +int libdax_msgs__sev_to_text(int severity, char **severity_name, + int flag) +{ + if(flag&1) { + *severity_name= "ALL ERRFILE DEBUG UPDATE NOTE HINT WARNING SORRY MISHAP FAILURE FATAL ABORT NEVER"; + 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_MISHAP) + *severity_name= "MISHAP"; + 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_ERRFILE) + *severity_name= "ERRFILE"; + else if(severity>=LIBDAX_MSGS_SEV_ALL) + *severity_name= "ALL"; + else { + *severity_name= ""; + return(0); + } + return(1); +} + + +/* + @param flag Bitfield for control purposes + bit0= If direct output to stderr: + CarriageReturn rather than LineFeed +*/ +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%c", m->print_id, sev_text, textpt, + (flag & 1) ? '\r' : '\n'); + 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= calloc(1, 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/trunk/libburn/libdax_msgs.h b/trunk/libburn/libdax_msgs.h new file mode 100644 index 0000000..394368b --- /dev/null +++ b/trunk/libburn/libdax_msgs.h @@ -0,0 +1,770 @@ + +/* libdax_msgs + Message handling facility of libburn and libisofs. + Copyright (C) 2006-2011 Thomas Schmitt <scdbackup@gmx.net>, + provided under GPL version 2 or later. +*/ + + +/* + *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 + + +/** Messages of this severity shall transport plain disk file paths + whenever an event of severity SORRY or above is related with an + individual disk file. + No message text shall be added to the file path. The ERRFILE message + shall be issued before the human readable message which carries the + true event severity. That message should contain the file path so it + can be found by strstr(message, path)!=NULL. + The error code shall be the same as with the human readable message. +*/ +#define LIBDAX_MSGS_SEV_ERRFILE 0x08000000 + + +/** Debugging messages not to be visible to normal users by default +*/ +#define LIBDAX_MSGS_SEV_DEBUG 0x10000000 + +/** Update of a progress report about long running actions +*/ +#define LIBDAX_MSGS_SEV_UPDATE 0x20000000 + +/** Not so usual events which were gracefully handled +*/ +#define LIBDAX_MSGS_SEV_NOTE 0x30000000 + +/** Possibilities to achieve a better result +*/ +#define LIBDAX_MSGS_SEV_HINT 0x40000000 + +/** Warnings about problems which could not be handled optimally +*/ +#define LIBDAX_MSGS_SEV_WARNING 0x50000000 + + +/** Non-fatal error messages indicating that parts of an action failed but + processing may go on if one accepts deviations from the desired result. + + SORRY may also be the severity for incidents which are severe enough + for FAILURE but happen within already started irrevocable actions, + like ISO image generation. A precondition for such a severity ease is + that the action can be continued after the incident. + See below MISHAP for what xorriso would need instead of this kind of SORRY + and generates for itself in case of libisofs image generation. + + E.g.: A pattern yields no result. + A speed setting cannot be made. + A libisofs input file is inaccessible during image generation. + + 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 + + +/** A FAILURE (see below) which can be tolerated during long lasting + operations just because they cannot simply be stopped or revoked. + + xorriso converts libisofs SORRY messages issued during image generation + into MISHAP messages in order to allow its evaluators to distinguish + image generation problems from minor image composition problems. + E.g.: + A libisofs input file is inaccessible during image generation. + + After a MISHAP a function should behave like after SORRY. +*/ +#define LIBDAX_MSGS_SEV_MISHAP 0x64000000 + + +/** Non-fatal error indicating that an important part of an action failed and + that only a new setup of preconditions will give hope for sufficient + success. + + E.g.: No media is inserted in the output drive. + No write mode can be found for inserted media. + A libisofs input file is inaccessible during grafting. + + After FAILURE a function should end with a return value indicating failure. + It is at the discretion of the function whether it ends immediately in any + case or whether it tries to go on if the eventual threshold allows. +*/ +#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 + bit0= If direct output to stderr: + CarriageReturn rather than LineFeed + @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 blank 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 LIBDAX_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 + 0x00000004 (FATAL,HIGH) = Generic fatal error + + +------------------------------------------------------------------------------ +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 + 0x0002000a (FAILURE,HIGH) = Cannot accept '...' as SG_IO CDROM drive + 0x0002000b (FAILURE,HIGH) = File object '...' not found + 0x0002000c (FAILURE,HIGH) = Cannot start device file enumeration + 0x0002000d (FAILURE,HIGH) = Cannot enumerate next device + 0x0002000e (NOTE,HIGH) = Failed to open device during + + 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 (FAILURE,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) = Asynchronous SCSI error + 0x0002014f (SORRY,HIGH) = Timeout with asynchronous SCSI command + 0x00020150 (DEBUG,LOW) = Reporting asynchronous waiting time + 0x00020151 (FAILURE,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 + 0x0002015a (NOTE,HIGH) = Could not examine busy device + 0x0002015b (HINT,HIGH) = Busy '...' seems to be a hard disk, as '...1' exists + 0x0002015c (FAILURE,HIGH) = Fifo size too small for desired peek buffer + 0x0002015d (FAILURE,HIGH) = Fifo input ended short of desired peek buffer size + 0x0002015e (FATAL,HIGH) = Fifo is already under consumption when peeking + 0x0002015f (MISHAP,HIGH) = Damaged CD table-of-content detected and truncated + 0x00020160 (WARNING,HIGH) = Session without leadout encountered + 0x00020161 (WARNING,HIGH) = Empty session deleted + 0x00020162 (SORRY,HIGH) = BD-R not unformatted blank any more. Cannot format + 0x00020163 (NOTE,HIGH) = Blank BD-R left unformatted for zero spare capacity + 0x00020164 (SORRY,HIGH) = Drive does not format BD-RE without spares + 0x00020165 (WARNING,HIGH) = Drive does not support fast formatting + 0x00020166 (WARNING,HIGH) = Drive does not support full formatting + 0x00020167 (SORRY,HIGH) = Drive does not support non-default formatting + 0x00020168 (FAILURE,HIGH) = Media not properly formatted. Cannot write. + 0x00020169 (WARNING,HIGH) = Last session on media is still open + 0x0002016a (FAILURE,HIGH) = No MMC transport adapter is present + 0x0002016b (WARNING,HIGH) = No MMC transport adapter is present + 0x0002016c (DEBUG,HIGH) = No MMC transport adapter is present + 0x0002016e (DEBUG,HIGH) = MODE SENSE page 2A too short + 0x0002016f (DEBUG,HIGH) = Unable to grab scanned drive + 0x00020170 (NOTE,HIGH) = Closing open session before writing new one + 0x00020171 (NOTE,HIGH) = Closing BD-R with accidently open session + 0x00020172 (SORRY,HIGH) = Read start address larger than number of readable blocks + 0x00020173 (FAILURE,HIGH) = Drive tells NWA smaller than last written address + 0x00020174 (SORRY,HIGH) = Fifo alignment does not allow desired read size + 0x00020175 (FATAL,HIGH) = Supporting library is too old + 0x00020176 (NOTE,HIGH) = Stream recording disabled because of small OS buffer + 0x00020177 (ABORT,HIGH) = Urged drive worker threads to do emergency halt + 0x00020178 (DEBUG,HIGH) = Write thread ended + 0x00020179 (FAILURE,HIGH) = Offset source start address is before end of previous source + 0x0002017a (FAILURE,HIGH) = Expected offset source object as parameter + 0x0002017b (WARNING,HIGH) = Sequential BD-R media likely to soon fail writing + 0x0002017c (FAILURE,HIGH) = No valid write type selected + 0x0002017d (FATAL,HIGH) = Invalid file descriptor with stdio pseudo-drive + 0x0002017e (FAILURE,HIGH) = Failed to close track, session, or disc + 0x0002017f (FAILURE,HIGH) = Failed to synchronize drive cache + 0x00020180 (FAILURE,HIGH) = Premature end of input encountered + 0x00020181 (FAILURE,HIGH) = Pseudo-drive is a read-only file. Cannot write. + 0x00020182 (FAILURE,HIGH) = Cannot truncate disk file for pseudo blanking + 0x00020183 (WARNING,HIGH) = Failed to open device (a pseudo-drive) for reading + 0x00020184 (WARNING,HIGH) = No Next-Writable-Address + 0x00020185 (WARNING,HIGH) = Track damaged, not closed and not writable + 0x00020186 (WARNING,HIGH) = Track damaged and not closed + 0x00020187 (NOTE,HIGH) = Track not marked as damaged. No action taken. + 0x00020188 (FAILURE,HIGH) = Cannot close damaged track on given media type + 0x00020189 (FATAL,HIGH) = Drive is already grabbed by libburn + 0x0002018a (SORRY,HIGH) = Timeout exceeded. Retry cancled. + 0x0002018b (FAILURE,HIGH) = Too many CD-TEXT packs + 0x0002018c (FAILURE,HIGH) = CD-TEXT pack type out of range + 0x0002018d (FAILURE,HIGH) = CD-TEXT block number out of range + 0x0002018e (FAILURE,HIGH) = Too many CD-TEXT packs in block + 0x0002018f (FAILURE,HIGH) = CD-TEXT pack CRC mismatch + 0x00020190 (WARNING,HIGH) = CD-TEXT pack CRC mismatch had to be corrected + 0x00020191 (FAILURE,HIGH) = Unknown parameter in text input file + 0x00020192 (FAILURE,HIGH) = Text input file sequence error + 0x00020193 (FAILURE,HIGH) = Text input file readability problem + 0x00020194 (FAILURE,HIGH) = Text input file syntax error or specs violation + 0x00020195 (WARNING,HIGH) = Text input file warning + 0x00020196 (FAILURE,HIGH) = Session has already defined tracks + 0x00020197 (FAILURE,HIGH) = Unsupported text input file feature + 0x00020198 (FAILURE,HIGH) = CD-TEXT pack file readability problem + 0x00020199 (SORRY,HIGH) = Text input file reading aborted + 0x0002019a (SORRY,HIGH) = Bad track index number + 0x0002019b (SORRY,HIGH) = CD track number exceeds range of 1 to 99 + 0x0002019c (SORRY,HIGH) = Session has no defined tracks + 0x0002019d (SORRY,HIGH) = Audio read size not properly aligned + 0x0002019e (NOTE,HIGH) = Drive does not support media certification + 0x0002019f (FAILURE,HIGH) = CD-TEXT binary pack array faulty + 0x000201a0 (WARNING,HIGH) = Maximum number of CD-TEXT blocks exceeded + 0x000201a1 (FAILURE,HIGH) = Cannot open disk file for writing + 0x000201a2 (FAILURE,HIGH) = Error while writing to disk file + 0x000201a3 (UPDATE,HIGH) = Progress message of burn_drive_extract_audio() + 0x000201a4 (FAILURE,HIGH) = Failure to read audio sectors + 0x000201a5 (FAILURE,HIGH) = Asynchronous SCSI error + 0x000201a6 (FATAL,HIGH) = Lost connection to drive + 0x000201a7 (FAILURE,HIGH) = SCSI command yielded host problem + 0x000201a8 (FAILURE,HIGH) = SCSI command yielded driver problem + 0x000201a9 (FAILURE,HIGH) = Implausible lenght from GET CONFIGURATION + + + 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 + + 0x0003ffff (FAILURE,HIGH) = Operation canceled + 0x0003fffe (FATAL,HIGH) = Unknown or unexpected fatal error + 0x0003fffd (FAILURE,HIGH) = Unknown or unexpected error + 0x0003fffc (FATAL,HIGH) = Internal programming error + 0x0003fffb (FAILURE,HIGH) = NULL pointer where NULL not allowed + 0x0003fffa (FATAL,HIGH) = Memory allocation error + 0x0003fff9 (FATAL,HIGH) = Interrupted by a signal + 0x0003fff8 (FAILURE,HIGH) = Invalid parameter value + 0x0003fff7 (FATAL,HIGH) = Cannot create a needed thread + 0x0003fff6 (FAILURE,HIGH) = Write error + 0x0003fff5 (FAILURE,HIGH) = Buffer read error + 0x0003ffc0 (FAILURE,HIGH) = Trying to add a node already added to another dir + 0x0003ffbf (FAILURE,HIGH) = Node with same name already exist + 0x0003ffbe (FAILURE,HIGH) = Trying to remove a node that was not added to dir + 0x0003ffbd (FAILURE,HIGH) = A requested node does not exist + 0x0003ffbc (FAILURE,HIGH) = Image already bootable + 0x0003ffbb (FAILURE,HIGH) = Trying to use an invalid file as boot image + 0x0003ff80 (FAILURE,HIGH) = Error on file operation + 0x0003ff7f (FAILURE,HIGH) = Trying to open an already openned file + 0x0003ff7e (FAILURE,HIGH) = Access to file is not allowed + 0x0003ff7d (FAILURE,HIGH) = Incorrect path to file + 0x0003ff7c (FAILURE,HIGH) = The file does not exist in the filesystem + 0x0003ff7b (FAILURE,HIGH) = Trying to read or close a file not openned + 0x0003ff7a (FAILURE,HIGH) = Directory used where no dir is expected + 0x0003ff79 (FAILURE,HIGH) = File read error + 0x0003ff78 (FAILURE,HIGH) = Not dir used where a dir is expected + 0x0003ff77 (FAILURE,HIGH) = Not symlink used where a symlink is expected + 0x0003ff76 (FAILURE,HIGH) = Cannot seek to specified location + 0x0003ff75 (HINT,MEDIUM) = File not supported in ECMA-119 tree and ignored + 0x0003ff74 (HINT,MEDIUM) = File bigger than supported by used standard + 0x0003ff73 (MISHAP,HIGH) = File read error during image creation + 0x0003ff72 (HINT,MEDIUM) = Cannot convert filename to requested charset + 0x0003ff71 (SORRY,HIGH) = File cannot be added to the tree + 0x0003ff70 (HINT,MEDIUM) = File path breaks specification constraints + 0x0003ff00 (FAILURE,HIGH) = Charset conversion error + 0x0003feff (FAILURE,HIGH) = Too much files to mangle + 0x0003fec0 (FAILURE,HIGH) = Wrong or damaged Primary Volume Descriptor + 0x0003febf (SORRY,HIGH) = Wrong or damaged RR entry + 0x0003febe (SORRY,HIGH) = Unsupported RR feature + 0x0003febd (FAILURE,HIGH) = Wrong or damaged ECMA-119 + 0x0003febc (FAILURE,HIGH) = Unsupported ECMA-119 feature + 0x0003febb (SORRY,HIGH) = Wrong or damaged El-Torito catalog + 0x0003feba (SORRY,HIGH) = Unsupported El-Torito feature + 0x0003feb9 (SORRY,HIGH) = Cannot patch isolinux boot image + 0x0003feb8 (SORRY,HIGH) = Unsupported SUSP feature + 0x0003feb7 (WARNING,HIGH) = Error on a RR entry that can be ignored + 0x0003feb6 (HINT,MEDIUM) = Error on a RR entry that can be ignored + 0x0003feb5 (WARNING,HIGH) = Multiple ER SUSP entries found + 0x0003feb4 (HINT,MEDIUM) = Unsupported volume descriptor found + 0x0003feb3 (WARNING,HIGH) = El-Torito related warning + 0x0003feb2 (MISHAP,HIGH) = Image write cancelled + 0x0003feb1 (WARNING,HIGH) = El-Torito image is hidden + +Outdated codes which may not be re-used for other purposes than +re-instating them, if ever: + +X 0x00031001 (SORRY,HIGH) = Cannot read file (ignored) +X 0x00031002 (FATAL,HIGH) = Cannot read file (operation canceled) +X 0x00031000 (FATAL,HIGH) = Unsupported ISO-9660 image +X 0x00031001 (HINT,MEDIUM) = Unsupported Vol Desc that will be ignored +X 0x00031002 (FATAL,HIGH) = Damaged ISO-9660 image +X 0x00031003 (SORRY,HIGH) = Cannot read previous image file +X 0x00030101 (HINT,MEDIUM) = Unsupported SUSP entry that will be ignored +X 0x00030102 (SORRY,HIGH) = Wrong/damaged SUSP entry +X 0x00030103 (WARNING,MEDIUM)= Multiple SUSP ER entries where found +X 0x00030111 (SORRY,HIGH) = Unsupported RR feature +X 0x00030112 (SORRY,HIGH) = Error in a Rock Ridge entry +X 0x00030201 (HINT,MEDIUM) = Unsupported Boot Vol Desc that will be ignored +X 0x00030202 (SORRY,HIGH) = Wrong El-Torito catalog +X 0x00030203 (HINT,MEDIUM) = Unsupported El-Torito feature +X 0x00030204 (SORRY,HIGH) = Invalid file to be an El-Torito image +X 0x00030205 (WARNING,MEDIUM)= Cannot properly patch isolinux image +X 0x00030206 (WARNING,MEDIUM)= Copying El-Torito from a previous image without +X enought info about it +X 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 + 0x00040008 (*,HIGH) : Application supplied message + + +------------------------------------------------------------------------------ +Range "libisofs-xorriso" : 0x00050000 to 0x0005ffff + +This is an alternative representation of libisofs.so.6 error codes in xorriso. +If values returned by iso_error_get_code() do not fit into 0x30000 to 0x3ffff +then they get truncated to 16 bit and mapped into this range. +(This should never need to happen, of course.) + +------------------------------------------------------------------------------ +Range "libisoburn" : 0x00060000 to 0x00006ffff + + 0x00060000 (*,*) : Message which shall be attributed to libisoburn + + >>> the messages of libisoburn need to be registered individually + + +------------------------------------------------------------------------------ + +#endif /* LIBDAX_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/trunk/libburn/libdax_msgs_to_xyz_msgs.sh b/trunk/libburn/libdax_msgs_to_xyz_msgs.sh new file mode 100755 index 0000000..46f68b1 --- /dev/null +++ b/trunk/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/trunk/libburn/mmc.c b/trunk/libburn/mmc.c new file mode 100644 index 0000000..ff6aa01 --- /dev/null +++ b/trunk/libburn/mmc.c @@ -0,0 +1,5368 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + + +/* 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 <ctype.h> + +/* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#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" +#include "util.h" +#include "init.h" + + +/* ts A70223 : in init.c */ +extern int burn_support_untested_profiles; + +static int mmc_get_configuration_al(struct burn_drive *d, int *alloc_len); + + +#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 */ +/* ts A80410 : applies to BD-RE too */ +#define Libburn_support_dvd_raM 1 + +/* ts A70129 */ +#define Libburn_support_dvd_r_seQ 1 + +/* ts A70306 */ +#define Libburn_support_dvd_plus_R 1 + +/* ts A70509 : handling 0x41 as read-only type */ +#define Libburn_support_bd_r_readonlY 1 + +/* ts A81208 */ +#define Libburn_support_bd_plus_r_srM 1 + + +/* ts A80410 : <<< Dangerous experiment: Pretend that DVD-RAM is BD-RE + # define Libburn_dvd_ram_as_bd_rE yes +*/ +/* ts A80509 : <<< Experiment: pretend that DVD-ROM and CD-ROM are other media + like BD-ROM (0x40), BD-R seq (0x41), BD-R random (0x42) + # define Libburn_rom_as_profilE 0x40 +*/ + + +/* ts A80425 : Prevents command FORMAT UNIT for DVD-RAM or BD-RE. + Useful only to test the selection of format descriptors without + actually formatting the media. + # define Libburn_do_not_format_dvd_ram_or_bd_rE 1 +*/ + + +/* ts A90603 : Simulate the command restrictions of an old MMC-1 drive + # define Libisofs_simulate_old_mmc1_drivE 1 +*/ + + +/* DVD/BD 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 A80228 : Made DVD+R/DL support official after nightmorph reported success + in http://libburnia-project.org/ticket/13 + ts A80416 : drive->do_stream_recording brings DVD-RAM to full nominal + writing speed at cost of no defect management. + ts A80416 : Giulio Orsero reports success with BD-RE writing. With + drive->do_stream_recording it does full nominal speed. + ts A80506 : Giulio Orsero reports success with BD-RE formatting. + BD-RE is now an officially supported profile. + ts A81209 : The first two sessions have been written to BD-R SRM + (auto formatted without Defect Management). + ts A90107 : BD-R is now supported media type +*/ + +/* 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 + +/* ts B31107 The minimum values to be applied if maximum read speed is + requested. Some drives tell only the currently set speed and + thus cannot be made faster by using the highest told value. + (The fractions get added or subtracted to yield an integer + number on the safe side of the intended limit.) +*/ +#define Libburn_cd_max_read_speeD (52 * 150) +#define Libburn_dvd_max_read_speeD (24 * 1385) +#define Libburn_bd_max_read_speeD (20 * 4495.625 + 0.5) + +/* ts B31114 The maximum values for minimum speed +*/ +#define Libburn_cd_min_read_speeD ( 1 * 150) +#define Libburn_dvd_min_read_speeD ( 1 * 1385) +#define Libburn_bd_min_read_speeD ( 1 * 4495.625 - 0.625) + + +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_LEADTEXT[] = { 0x43, 2, 5, 0, 0, 0, 0, 4, 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, 0x7e, 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}; + +/* ts A81210 : Determine the upper limit of readable data size */ +static unsigned char MMC_READ_CAPACITY[] = + { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +/* ts A90903 : Obtain media type specific information. E.g. manufacturer. +*/ +static unsigned char MMC_READ_DISC_STRUCTURE[] = + { 0xAD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +/* ts B21125 : An alternatvie to BEh READ CD +*/ +static unsigned char MMC_READ_CD_MSF[] = + { 0xB9, 0, 0, 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; +} + + +static int mmc_start_for_bit0 = 0; + +/* @param flag bit0= the calling function should need no START UNIT. + (Handling depends on mmc_start_for_bit0) +*/ +int mmc_start_if_needed(struct burn_drive *d, int flag) +{ + if (!d->is_stopped) + return 2; + if ((flag & 1) && !mmc_start_for_bit0) + return 2; + d->start_unit(d); + d->is_stopped = 0; + return 1; +} + + +int mmc_send_cue_sheet(struct burn_drive *d, struct cue_sheet *s) +{ + struct buffer *buf = NULL; + struct command *c; + + c = &(d->casual_command); + mmc_start_if_needed(d, 0); + if (mmc_function_spy(d, "mmc_send_cue_sheet") <= 0) + return 0; + BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); + scsi_init_command(c, 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); +ex:; + BURN_FREE_MEM(buf); + if (c->error) { + d->cancel = 1; + scsi_notify_error(d, c, c->sense, 18, 2); + } + return !c->error; +} + + +/* 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]; + + c = &(d->casual_command); + mmc_start_if_needed(d, 0); + if (mmc_function_spy(d, "mmc_reserve_track") <= 0) + return 0; + + scsi_init_command(c, MMC_RESERVE_TRACK, sizeof(MMC_RESERVE_TRACK)); + c->retry = 1; + + lba = size / 2048; + if (size % 2048) + lba++; + 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; + c->timeout = Libburn_mmc_reserve_timeouT; + d->issue_command(d, c); + if (c->error) { + d->cancel = 1; + scsi_notify_error(d, c, c->sense, 18, 2); + } + 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; + + c = &(d->casual_command); + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "mmc_read_track_info") <= 0) + return 0; + + scsi_init_command(c, 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 || d->current_profile == 0x42 || + d->current_profile == 0x43) + /* DVD+RW , DVD-RW restricted overwrite , DVD-RAM + BD-R random recording, BD-RE */ + trackno = 1; + else if (d->current_profile == 0x10 || + d->current_profile == 0x11 || + d->current_profile == 0x14 || + d->current_profile == 0x15 || + d->current_profile == 0x40 || + d->current_profile == 0x41) + /* DVD-ROM , DVD-R[W] Sequential , + BD-ROM , BD-R 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 = NULL; + int ret, num, alloc_len = 20, err; + unsigned char *data; + char *msg = NULL; + + if (trackno <= 0) + d->next_track_damaged = 0; + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "mmc_get_nwa") <= 0) + {ret = -1; goto ex;} + + /* ts B00327 : Avoid to inquire unsuitable media states */ + if (d->status != BURN_DISC_BLANK && d->status != BURN_DISC_APPENDABLE) + {ret = 0; goto ex;} + + BURN_ALLOC_MEM(buf, struct buffer, 1); + ret = mmc_read_track_info(d, trackno, buf, alloc_len); + if (ret <= 0) + goto ex; + 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); + + /* Pioneer BD-RW BDR-205 and LITE-ON LTR-48125S return -150 as *nwa + of blank media */ + if (*nwa < *lba && d->status == BURN_DISC_BLANK) + *nwa = *lba; + +#ifdef Libburn_pioneer_dvr_216d_load_mode5 + /* >>> memorize track mode : data[6] & 0xf */; +#endif + +{ static int fake_damage = 0; /* bit0= damage on , bit1= NWA_V off */ + + if (fake_damage & 1) + data[5] |= 32; /* Damage bit */ + if (fake_damage & 2) + data[7] &= ~1; + +} + + BURN_ALLOC_MEM(msg, char, 160); + if (trackno > 0) + sprintf(msg, "Track number %d: ", trackno); + else + sprintf(msg, "Upcomming track: "); + if (d->current_profile == 0x1a || d->current_profile == 0x13 || + d->current_profile == 0x12 || d->current_profile == 0x43) { + /* overwriteable */ + *lba = *nwa = num = 0; + + } else if (data[5] & 32) { /* ts B10534 : MMC-5 6.27.3.7 Damage Bit */ + if (!(data[7] & 1)) { /* NWA_V is set to zero */ + /* "not closed due to an incomplete write" */ + strcat(msg, "Damaged, not closed and not writable"); + err= 0x00020185; + } else { + /* "may be recorded further in an incremental manner"*/ + strcat(msg, "Damaged and not closed"); + err= 0x00020186; + } + libdax_msgs_submit(libdax_messenger, d->global_index, err, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + if (trackno <= 0) + d->next_track_damaged |= ((!(data[7] & 1)) << 1) | 1; + {ret = 0; goto ex;} + + } else if (!(data[7] & 1)) { + /* ts A61106 : MMC-1 Table 142 : NWA_V = NWA Valid Flag */ + strcat(msg, "No Next-Writable-Address"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020184, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + if (trackno <= 0) + d->next_track_damaged |= 2; + {ret = 0; goto ex;} + + } + if (num > 0) { + burn_drive_set_media_capacity_remaining(d, + ((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); +*/ + + ret = 1; +ex: + BURN_FREE_MEM(buf); + BURN_FREE_MEM(msg); + return ret; +} + +/* 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, NULL, 0, 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, NULL, 0, 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; + char msg[256]; + int key, asc, ascq; + + c = &(d->casual_command); + if (mmc_function_spy(d, "mmc_close") <= 0) + return; + + scsi_init_command(c, 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; + c->timeout = Libburn_mmc_close_timeouT; + d->issue_command(d, c); + + /* ts A70918 : Immed : wait for drive to complete command */ + if (c->error) { + sprintf(msg, "Failed to close %s (%d)", + session > 1 ? "disc" : session > 0 ? "session" : "track", + ((session & 3) << 1) | !!track); + sprintf(msg + strlen(msg), ". SCSI error : "); + scsi_error_msg(d, c->sense, 14, msg + strlen(msg), + &key, &asc, &ascq); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002017e, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + 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 = NULL; + struct command *c; + int alloc_len = 8, len, evt_code, loops = 0; + unsigned char *evt; + + c = &(d->casual_command); + BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); + if (mmc_function_spy(d, "mmc_get_event") <= 0) + goto ex; + +again:; + scsi_init_command(c, MMC_GET_EVENT, sizeof(MMC_GET_EVENT)); + c->dxfer_len = 8; + + /* >>> have a burn_drive element for Notification Class */; + c->opcode[4] = 0x7e; + + 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) + goto ex; + + evt = c->page->data; + len = ((evt[0] << 8) | evt[1]) + 2; + if (len < 8) + goto ex; + + /* >>> memorize evt[3] in burn_drive element for Notification Class */; + if (evt[3] == 0) /* No event */ + goto ex; + + evt_code = evt[4] & 0xf; + if (evt_code == 0) /* No change */ + goto ex; + + switch (evt[2] & 7) { + case 0: /* no events supported */ + goto ex; + case 1: /* Operational change */ + if (((evt[6] << 8) | evt[7])) { + alloc_len = 8; + mmc_get_configuration_al(d, &alloc_len); + } + break; + case 2: /* Power Management */ + if (evt[5] >= 2) + d->start_unit(d); + break; + case 3: /* External request */ + + /* >>> report about external request */; + + break; + case 4: /* Media */ + if (evt_code == 2) { + d->start_unit(d); + alloc_len = 8; + mmc_get_configuration_al(d, &alloc_len); + } + break; + case 5: /* Multiple Host Events */ + + /* >>> report about foreign host interference */; + + break; + + case 6: /* Device busy */ + if (evt_code == 1 && evt[5]) { + + /* >>> wait the time announced in evt[6],[7] + as 100ms units */; + } + break; + default: /* reserved */ + break; + } + loops++; + if (loops < 100) + goto again; +ex:; + BURN_FREE_MEM(buf); +} + + +/* 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 < (int) d->wfb_min_usec) + usec = d->wfb_min_usec; + else if (usec > (int) 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; + + c = &(d->casual_command); + mmc_start_if_needed(d, 0); + if (mmc_function_spy(d, "mmc_write_12") <= 0) + return; + + len = buf->sectors; + + scsi_init_command(c, MMC_WRITE_12, 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; + c->timeout = Libburn_scsi_write_timeouT; + + d->issue_command(d, c); + + /* ts A70711 */ + d->pessimistic_buffer_free -= buf->bytes; + d->pbf_altered = 1; +} + + +#ifdef Libburn_write_time_debuG + +static int print_time(int flag) +{ + static struct timeval prev = {0, 0}; + struct timeval now; + struct timezone tz; + int ret, diff; + + ret = gettimeofday(&now, &tz); + if (ret == -1) + return 0; + if (now.tv_sec - prev.tv_sec < Libburn_scsi_write_timeouT) { + diff = (now.tv_sec - prev.tv_sec) * 1000000 + + ((int) (now.tv_usec) - (int) prev.tv_usec); + fprintf(stderr, "\nlibburn_DEBUG: %d.%-6d : %d\n", (int) now.tv_sec, (int) now.tv_usec, diff); + } + memcpy(&prev, &now, sizeof(struct timeval)); + return 1; +} + +#endif /* Libburn_write_time_debuG */ + + +int mmc_write(struct burn_drive *d, int start, struct buffer *buf) +{ + int cancelled; + struct command *c; + int len, key, asc, ascq; + char *msg = NULL; + +#ifdef Libburn_write_time_debuG + extern int burn_sg_log_scsi; +#endif + +/* +fprintf(stderr, "libburn_DEBUG: buffer sectors= %d bytes= %d\n", + buf->sectors, buf->bytes); +*/ + + + c = &(d->casual_command); + +#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 | O_BINARY, + S_IRUSR | S_IWUSR); +#endif /* Libburn_log_in_and_out_streaM */ + + mmc_start_if_needed(d, 0); + 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) { + + msg = calloc(1, 160); + if (msg != NULL) { + 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); + free(msg); + } + 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... */ + + /* ts A70711 */ + if(d->wait_for_buffer_free) + mmc_wait_for_buffer_free(d, buf); + +#ifdef Libburn_write_time_debuG + if (burn_sg_log_scsi & 3) + print_time(0); +#endif + + /* ts A80412 */ + if(d->do_stream_recording > 0 && start >= d->stream_recording_start) { + + /* >>> ??? is WRITE12 available ? */ + /* >>> ??? inquire feature 107h Stream Writing bit ? */ + + scsi_init_command(c, MMC_WRITE_12, sizeof(MMC_WRITE_12)); + mmc_int_to_four_char(c->opcode + 2, start); + mmc_int_to_four_char(c->opcode + 6, len); + c->opcode[10] = 1<<7; /* Streaming bit */ + } else { + scsi_init_command(c, MMC_WRITE_10, sizeof(MMC_WRITE_10)); + 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->retry = 1; + c->page = buf; + c->dir = TO_DRIVE; + c->timeout = Libburn_scsi_write_timeouT; + +#ifdef Libburn_log_in_and_out_streaM + /* <<< ts A61031 */ + if(tee_fd!=-1) { + write(tee_fd, c->page->data, c->page->bytes); + } +#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 */ + spc_decode_sense(c->sense, 0, &key, &asc, &ascq); + if (c->error && key != 0) { + int key, asc, ascq; + int err_sev = LIBDAX_MSGS_SEV_FATAL; + + msg = calloc(1, 256); + if (msg != NULL) { + sprintf(msg, "SCSI error on write(%d,%d): ", + start, len); + scsi_error_msg(d, c->sense, 14, msg + strlen(msg), + &key, &asc, &ascq); + } + + /* ts B31023 */ + /* Memorize if on DVD-RW write mode is TAO/Incremental and + error [5 64 00] occurs within the first drive buffer fill. + */ + if (d->current_profile == 0x14 && d->write_opts != NULL && + (d->progress.buffer_capacity == 0 || + start < (int) d->progress.buffer_capacity / 2048) && + key == 5 && asc == 0x64 && ascq == 0) { + if (d->write_opts->write_type == BURN_WRITE_TAO) { + d->was_feat21h_failure = 1 + (start == 0); + if (d->write_opts->feat21h_fail_sev != 0) + err_sev = + d->write_opts->feat21h_fail_sev; + } + } + + if (msg != NULL) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002011d, + err_sev, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + free(msg); + } + 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, + unsigned char *last_adr_data) +{ + int min, sec, frames, num; + + /* mark DVD extensions and Track Info extension as valid */ + entry->extensions_valid |= (1 | 2); + + /* 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; + entry->last_recorded_address = mmc_four_char_to_int(last_adr_data); + 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 = NULL; + struct command *c = NULL; + int dlen, i, old_alloc_len, session_number, prev_session = -1, ret; + int lba, size; + unsigned char *tdata, size_data[4], start_data[4], end_data[4]; + + if (*alloc_len < 4) + {ret = 0; goto ex;} + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + 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)); + {ret = 0; goto ex;} + } + dlen = c->page->data[0] * 256 + c->page->data[1]; + old_alloc_len = *alloc_len; + *alloc_len = dlen + 2; + if (old_alloc_len < 12) + {ret = 1; goto ex;} + if (dlen + 2 > old_alloc_len) + dlen = old_alloc_len - 2; + d->complete_sessions = 1 + c->page->data[3] - c->page->data[2]; + +#ifdef Libburn_disc_with_incomplete_sessioN + /* ts B30112 : number of open sessions */ + d->incomplete_sessions = 0; +#endif + + 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) + {ret = 0; goto ex;} + d->toc_entry = calloc(d->toc_entries, sizeof(struct burn_toc_entry)); + if(d->toc_entry == NULL) + {ret = 0; goto ex;} + + d->disc = burn_disc_create(); + if (d->disc == NULL) + {ret = 0; goto ex;} + for (i = 0; i < d->complete_sessions; i++) { + session = burn_session_create(); + if (session == NULL) + {ret = 0; goto ex;} + 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_int_to_four_char(end_data, lba - 1); + mmc_fake_toc_entry(entry, prev_session, 0xA2, + size_data, start_data, end_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) + {ret = -1; goto ex;} + 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); + mmc_int_to_four_char(end_data, + mmc_four_char_to_int(size_data) - 1); + 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, end_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_int_to_four_char(end_data, + mmc_four_char_to_int(start_data) - 1); + mmc_fake_toc_entry(entry, prev_session, 0xA2, + size_data, start_data, end_data); + entry->min= entry->sec= entry->frame= 0; + d->disc->session[prev_session - 1]->leadout_entry = entry; + } + ret = 1; +ex:; + BURN_FREE_MEM(buf); + BURN_FREE_MEM(c); + return ret; +} + + +/* 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; + + mmc_start_if_needed(d, 1); + 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 = NULL; + int i, session_number, prev_session = -1, ret, lba, alloc_len = 34; + unsigned char *tdata, size_data[4], start_data[4], end_data[4]; + char *msg = NULL; + + if (mmc_function_spy(d, "mmc_fake_toc") <= 0) + {ret = -1; goto ex;} + BURN_ALLOC_MEM(buf, struct buffer, 1); + +#ifdef Libburn_disc_with_incomplete_sessioN + + if (d->last_track_no <= 0 || + d->complete_sessions + d->incomplete_sessions <= 0 || + d->status == BURN_DISC_BLANK) + {ret = 2; goto ex;} + +#else + + if (d->last_track_no <= 0 || d->complete_sessions <= 0 || + d->status == BURN_DISC_BLANK) + {ret = 2; goto ex;} + +#endif /* ! Libburn_disc_with_incomplete_sessioN */ + + + if (d->last_track_no > BURN_MMC_FAKE_TOC_MAX_SIZE) { + msg = calloc(1, 160); + if (msg != NULL) { + 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); + free(msg); + } + {ret = 0; goto ex;} + } + /* 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); + goto ex; + } + d->disc = burn_disc_create(); + if (d->disc == NULL) + {ret = -1; goto ex;} + d->toc_entries = d->last_track_no + + d->complete_sessions + d->incomplete_sessions; + d->toc_entry = calloc(d->toc_entries, sizeof(struct burn_toc_entry)); + if (d->toc_entry == NULL) + {ret = -1; goto ex;} + memset(d->toc_entry, 0,d->toc_entries * sizeof(struct burn_toc_entry)); + +#ifdef Libburn_disc_with_incomplete_sessioN + + for (i = 0; i < d->complete_sessions + d->incomplete_sessions; i++) { + +#else + + for (i = 0; i < d->complete_sessions; i++) { + +#endif + + session = burn_session_create(); + if (session == NULL) + {ret = -1; goto ex;} + burn_disc_add_session(d->disc, session, BURN_POS_END); + burn_session_free(session); + } + +#ifdef Libburn_disc_with_incomplete_sessioN + d->disc->incomplete_sessions = d->incomplete_sessions; +#endif + + 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) + goto ex; + 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_int_to_four_char(end_data, lba - 1); + mmc_fake_toc_entry(entry, prev_session, 0xA2, + size_data, start_data, end_data); + entry->min= entry->sec= entry->frame= 0; + d->disc->session[prev_session - 1]->leadout_entry = + entry; + } + +#ifdef Libburn_disc_with_incomplete_sessioN + + if (session_number > d->complete_sessions) { + +#else + + if (session_number > d->disc->sessions) { + +#endif + + if (i == d->last_track_no - 1) { + /* ts A70212 : Last track field Free Blocks */ + burn_drive_set_media_capacity_remaining(d, + ((off_t) mmc_four_char_to_int(tdata + 16)) * + ((off_t) 2048)); + d->media_lba_limit = 0; + } + +#ifdef Libburn_disc_with_incomplete_sessioN + + if (session_number > d->disc->sessions ) + continue; + +#else + + continue; + +#endif + + } + + entry = &(d->toc_entry[i + session_number - 1]); + track = burn_track_create(); + if (track == NULL) + {ret = -1; goto ex;} + 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); + memcpy(end_data, tdata + 28, 4); + mmc_fake_toc_entry(entry, session_number, i + 1, + size_data, start_data, end_data); + entry->track_status_bits = tdata[5] | (tdata[6] << 8) | + (tdata[7] << 16); + entry->extensions_valid |= 4; + + 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_int_to_four_char(end_data, lba - 1); + mmc_fake_toc_entry(entry, prev_session, 0xA2, + size_data, start_data, end_data); + entry->min= entry->sec= entry->frame= 0; + d->disc->session[prev_session - 1]->leadout_entry = entry; + } + ret = 1; +ex:; + BURN_FREE_MEM(buf); + return ret; +} + + +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 = NULL; + struct command *c = NULL; + int dlen; + int i, old_alloc_len, t_idx, ret; + unsigned char *tdata; + char *msg = NULL; + + if (*alloc_len < 4) + {ret = 0; goto ex;} + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + BURN_ALLOC_MEM(msg, char, 321); + + 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; + {ret = 1; goto ex;} + } + + /* ts A90823: + SanDisk Cruzer U3 memory stick stalls on format 2. + Format 0 seems to be more conservative with read-only drives. + */ + if (!((d->mdata->p2a_valid > 0 && d->mdata->cdrw_write) || + d->current_profile != 0x08)) { + ret = mmc_read_toc_fmt0(d); + goto ex; + } + + scsi_init_command(c, MMC_GET_TOC, 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)); + {ret = 0; goto ex;} + } + + dlen = c->page->data[0] * 256 + c->page->data[1]; + old_alloc_len = *alloc_len; + *alloc_len = dlen + 2; + if (old_alloc_len < 15) + {ret = 1; goto ex;} + if (dlen + 2 > old_alloc_len) + dlen = old_alloc_len - 2; + d->toc_entries = (dlen - 2) / 11; + if (d->toc_entries < 1) + {ret = 0; goto ex;} +/* + some drives fail this check. + + ts A61007 : if re-enabled then not via Assert. + a ssert(((dlen - 2) % 11) == 0); +*/ + /* ts A81202: plus number of sessions as reserve for leadout default */ + d->toc_entry = calloc(d->toc_entries + (unsigned char) c->page->data[3], + sizeof(struct burn_toc_entry)); + if(d->toc_entry == NULL) /* ts A70825 */ + {ret = 0; goto ex;} + tdata = c->page->data + 4; + + d->disc = burn_disc_create(); + if (d->disc == NULL) /* ts A70825 */ + {ret = 0; goto ex;} + + for (i = 0; i < c->page->data[3]; i++) { + session = burn_session_create(); + if (session == NULL) /* ts A70825 */ + {ret = 0; goto ex;} + burn_disc_add_session(d->disc, session, BURN_POS_END); + burn_session_free(session); + } + + /* ts A61022 */ + + for (i = 0; i < d->toc_entries; i++, tdata += 11) { + +/* + fprintf(stderr, "libburn_experimental: toc entry #%d : %d %d %d\n",i,tdata[8], tdata[9], tdata[10]); +*/ + +#ifdef Libburn_allow_first_hiddeN + /* ts B00430 : this causes problems because the track has + no entry. One would have to coordinate this + with other parts of libburn. + */ + 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); + } + } +#endif /* Libburn_allow_first_hiddeN */ + + 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 A70131 : was (d->status != BURN_DISC_BLANK) */ + if (d->status == BURN_DISC_UNREADY) + d->status = BURN_DISC_FULL; + toc_find_modes(d); + + /* ts A81202 ticket 146 : a drive reported a session with no leadout */ + for (i = 0; i < d->disc->sessions; i++) { + if (d->disc->session[i]->leadout_entry != NULL) + continue; + sprintf(msg, "Session %d of %d encountered without leadout", + i + 1, d->disc->sessions); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020160, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + + /* Produce default leadout entry from last track of session + which will thus get its size set to 0 */; + if (d->disc->session[i]->track != NULL && + d->disc->session[i]->tracks > 0) { + t_idx = d->toc_entries++; + memcpy(d->toc_entry + t_idx, + d->disc->session[i]->track[ + d->disc->session[i]->tracks - 1]->entry, + sizeof(struct burn_toc_entry)); + d->toc_entry[t_idx].point = 0xA2; + d->disc->session[i]->leadout_entry = + d->toc_entry + t_idx; + } else { + burn_disc_remove_session(d->disc, d->disc->session[i]); + sprintf(msg, + "Empty session %d deleted. Now %d sessions.", + i + 1, d->disc->sessions); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020161, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + i--; + } + } + + /* A80808 */ + burn_disc_cd_toc_extensions(d, 0); + + ret = 1; +ex:; + BURN_FREE_MEM(msg); + BURN_FREE_MEM(c); + BURN_FREE_MEM(buf); + return ret; +} + + +void mmc_read_toc(struct burn_drive *d) +{ + int alloc_len = 4, ret; + + mmc_start_if_needed(d, 1); + 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); + if (ret <= 0) + return; +} + + +/* 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 = NULL; + struct command *c = NULL; + unsigned char *tdata; + int num_sessions, session_no, num_tracks, alloc_len = 12, ret; + struct burn_disc *disc; + struct burn_session **sessions; + struct burn_track **tracks; + struct burn_toc_entry toc_entry; + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "mmc_read_multi_session_c1") <= 0) + {ret = 0; goto ex;} + + /* 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) + {ret = 1; goto ex;} + +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)); + 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) + {ret = 0; goto ex;} + + tdata = c->page->data + 4; + *trackno = tdata[2]; + *start = mmc_four_char_to_int(tdata + 4); + ret = 1; +ex:; + BURN_FREE_MEM(buf); + BURN_FREE_MEM(c); + return ret; +} + + +/* ts A61201 */ +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 A90603 : to be used if the drive knows no GET CONFIGURATION +*/ +static int mmc_guess_profile(struct burn_drive *d, int flag) +{ + int cp; + + cp = 0; + if (d->status == BURN_DISC_BLANK || + d->status == BURN_DISC_APPENDABLE) { + cp = 0x09; + } else if (d->status == BURN_DISC_FULL) { + cp = 0x08; + } + if (cp) + if (d->erasable) + cp = 0x0a; + d->current_profile = cp; + if (cp == 0) + return 0; + d->current_is_cd_profile = 1; + d->current_is_supported_profile = 1; + strcpy(d->current_profile_text, mmc_obtain_profile_name(cp)); + return 1; +} + + +static int mmc_read_disc_info_al(struct burn_drive *d, int *alloc_len) +{ + struct buffer *buf = NULL; + unsigned char *data; + struct command *c = NULL; + char *msg = NULL; + /* ts A70131 : had to move mmc_read_toc() to end of function */ + int do_read_toc = 0, disc_status, len, old_alloc_len; + int ret, number_of_sessions = -1; + int key, asc, ascq; + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + + /* ts A61020 */ + d->start_lba = d->end_lba = -2000000000; + d->erasable = 0; + d->last_track_no = 1; + + /* ts B10730 */ + d->sent_default_page_05 = 0; + /* ts A70212 - A70215 */ + d->media_capacity_remaining = 0; + d->media_lba_limit = 0; + + /* ts A81210 */ + d->media_read_capacity = 0x7fffffff; + + /* ts A61202 */ + d->toc_entries = 0; + if (d->status == BURN_DISC_EMPTY) + {ret = 1; goto ex;} + + mmc_get_configuration(d); + + scsi_init_command(c, MMC_GET_DISC_INFO, 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) { + spc_decode_sense(c->sense, 0, &key, &asc, &ascq); + if (key == 5 && asc == 0x20 && ascq == 0) { + /* ts B11031 : qemu -cdrom does not know + 051h READ DISC INFORMATION + */ + ret = mmc_read_toc_fmt0(d); + if (ret > 0) { + + /* >>> ??? anything more to be set ? */; + + mmc_read_capacity(d); + *alloc_len = 0; + goto ex; + } + } + + d->busy = BURN_DRIVE_IDLE; + {ret = 0; goto ex;} + } + + data = c->page->data; + len = (data[0] << 8) | data[1]; + old_alloc_len = *alloc_len; + *alloc_len = len + 2; + if (old_alloc_len < 34) + {ret = 1; goto ex;} + if (*alloc_len < 24) /* data[23] is the last mandatory byte here */ + {ret = 0; goto ex;} + if (len + 2 > old_alloc_len) + len = old_alloc_len - 2; + + d->erasable = !!(data[2] & 16); + + /* ts A90908 */ + d->disc_type = data[8]; + d->disc_info_valid = 1; + d->disc_id = mmc_four_char_to_int(data + 12); + d->disc_info_valid |= (!!(data[7] & 128)) << 1; + if (len + 2 > 31 && (data[7] & 64)) { + memcpy(d->disc_bar_code, data + 24, 8); + d->disc_bar_code[8] = 0; + d->disc_info_valid |= 4; + } + if (len + 2 > 32 && (data[7] & 16)) { + d->disc_app_code = data[32]; + d->disc_info_valid |= 8; + } + if (data[7] & 32) + d->disc_info_valid |= 16; + if (data[2] & 16) + d->disc_info_valid |= 32; + + disc_status = data[2] & 3; + d->state_of_last_session = (data[2] >> 2) & 3; + number_of_sessions = (data[9] << 8) | data[4]; + + if (d->current_profile == 0x10 || d->current_profile == 0x40) { + /* DVD-ROM , BD-ROM */ + disc_status = 2; /* always full and finalized */ + d->erasable = 0; /* never erasable */ + } + +#ifdef Libburn_support_bd_r_readonlY + /* <<< For now: declaring BD-R read-only + */ +#ifndef Libburn_support_bd_plus_r_srM + if (d->current_profile == 0x41) { + /* BD-R seq as readonly dummy */ + disc_status = 2; /* always full and finalized */ + d->erasable = 0; /* never erasable */ + } +#endif + if (d->current_profile == 0x42) { + /* BD-R rnd */ + disc_status = 2; /* always full and finalized */ + d->erasable = 0; /* never erasable */ + } +#endif /* Libburn_support_bd_r_readonlY */ + + /* MMC-5 6.22.3.1.16: + Last Session Lead-in Start Address bytes 16 to 19 + Last Possible Lead-out Start Address bytes 20 to 23 + MSF for CD, LBA else + */ + if(d->current_profile == 0x08 || d->current_profile == 0x09 || + d->current_profile == 0x0a) { + d->last_lead_in = + burn_msf_to_lba(data[17], data[18], data[19]); + d->last_lead_out = + burn_msf_to_lba(data[21], data[22], data[23]); + } else { + d->last_lead_in = mmc_four_char_to_int(data + 16); + d->last_lead_out = mmc_four_char_to_int(data + 20); + } + + switch (disc_status) { + case 0: +regard_as_blank:; + d->toc_entries = 0; + +/* + 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; + d->start_lba = d->last_lead_in; + d->end_lba = d->last_lead_out; + break; + case 1: + d->status = BURN_DISC_APPENDABLE; + + case 2: + if (disc_status == 2) + d->status = BURN_DISC_FULL; + + /* ts A81210 */ + ret = mmc_read_capacity(d); + /* Freshly formatted, unwritten BD-R pretend to be appendable + but in our model they need to be regarded as blank. + Criterion: BD-R seq, read capacity known and 0, + declared appendable, single empty session + */ + if (d->current_profile == 0x41 && + d->status == BURN_DISC_APPENDABLE && + ret > 0 && d->media_read_capacity == 0 && + d->state_of_last_session == 0 && number_of_sessions == 1) + goto regard_as_blank; + + if (d->current_profile == 0x41 && + d->status == BURN_DISC_APPENDABLE && + d->state_of_last_session == 1) { + + /* ??? apply this test to other media types ? */ + + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020169, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + "Last session on media is still open.", 0, 0); + } + + do_read_toc = 1; + break; + case 3: + /* ts A91009 : DVD-RAM has disc status "others" */ + mmc_read_capacity(d); + break; + } + + /* ts A90603 : An MMC-1 drive might not know the media type yet */ + if (d->current_is_guessed_profile && d->current_profile == 0) + mmc_guess_profile(d, 0); + + if ((d->current_profile != 0 || d->status != BURN_DISC_UNREADY) + && ! d->current_is_supported_profile) { + if (!(d->silent_on_scsi_error == 1 || + d->silent_on_scsi_error == 2)) { + msg = calloc(1, 160); + if (msg != NULL) { + 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, + d->silent_on_scsi_error == 3 ? + LIBDAX_MSGS_SEV_DEBUG : + LIBDAX_MSGS_SEV_SORRY, + LIBDAX_MSGS_PRIO_HIGH, msg, 0,0); + free(msg); + } + } + d->status = BURN_DISC_UNSUITABLE; + {ret = 0; goto ex;} + } + + /* ts A61217 : + growisofs performs OPC if (data[0]<<8)|data[1]<=32 + which indicates no OPC entries are attached to the + reply from the drive. + ts A91104 : + Actually growisofs performs OPC only on DVD-R[W]. + */ + d->num_opc_tables = 0; + if(((data[0] << 8) | data[1]) > 32) /* i.e. > 34 bytes are available */ + d->num_opc_tables = data[33]; + + /* 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->current_profile == 0x43) + d->status = BURN_DISC_BLANK; + +#ifdef Libburn_disc_with_incomplete_sessioN + /* ts B30112 : number of open sessions */ + d->incomplete_sessions = 0; +#endif + + if (d->status == BURN_DISC_BLANK) { + d->last_track_no = 1; /* The "incomplete track" */ + d->complete_sessions = 0; + } else { + /* ts A70131 : number of closed sessions */ + d->complete_sessions = number_of_sessions; + /* mmc5r03c.pdf 6.22.3.1.3 State of Last Session: 3=complete */ + if (d->state_of_last_session != 3 && + d->complete_sessions >= 1) { + d->complete_sessions--; + +#ifdef Libburn_disc_with_incomplete_sessioN + d->incomplete_sessions++; +#endif + + } + + /* 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); + ret = 1; +ex: + BURN_FREE_MEM(buf); + BURN_FREE_MEM(c); + return ret; +} + + +void mmc_read_disc_info(struct burn_drive *d) +{ + int alloc_len = 34, ret; + + mmc_start_if_needed(d, 1); + 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); +*/ + if (ret <= 0) + return; + + /* for now there is no need to inquire the variable lenght part */ +} + + +/* @param flag bit= do not allocate text_packs +*/ +static int mmc_get_leadin_text_al(struct burn_drive *d, + unsigned char **text_packs, int *alloc_len, + int flag) +{ + struct buffer *buf = NULL; + struct command *c = NULL; + unsigned char *data; + int ret, data_length; + + *text_packs = NULL; + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + + scsi_init_command(c, MMC_GET_LEADTEXT, sizeof(MMC_GET_LEADTEXT)); + 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) + {ret = 0; goto ex;} + + data = c->page->data; + data_length = (data[0] << 8) + data[1]; + *alloc_len = data_length + 2; + if (*alloc_len >= 22 && !(flag & 1)) { + BURN_ALLOC_MEM(*text_packs, unsigned char, *alloc_len - 4); + memcpy(*text_packs, data + 4, *alloc_len - 4); + } + ret = 1; +ex:; + BURN_FREE_MEM(c); + BURN_FREE_MEM(buf); + return ret; +} + + +/* ts B11201 */ +/* Read the CD-TEXT data from the Lead-in of an Audio CD +*/ +int mmc_get_leadin_text(struct burn_drive *d, + unsigned char **text_packs, int *num_packs, int flag) +{ + int alloc_len = 4, ret; + + *num_packs = 0; + if (mmc_function_spy(d, "mmc_get_leadin_text") <= 0) + return -1; + ret = mmc_get_leadin_text_al(d, text_packs, &alloc_len, 1); + if (ret <= 0 || alloc_len < 22) + return (ret > 0 ? 0 : ret); + ret = mmc_get_leadin_text_al(d, text_packs, &alloc_len, 0); + if (ret <= 0 || alloc_len < 22) { + if (*text_packs != NULL) + free(*text_packs); + *text_packs = NULL; + return (ret > 0 ? 0 : ret); + } + *num_packs = (alloc_len - 4) / 18; + return ret; +} + + +void mmc_read_atip(struct burn_drive *d) +{ + struct buffer *buf = NULL; + struct command *c = NULL; + 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, -, -, -, - */ + + BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); + BURN_ALLOC_MEM_VOID(c, struct command, 1); + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "mmc_read_atip") <= 0) + goto ex; + + scsi_init_command(c, MMC_GET_ATIP, 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); + /* ts B00501 : now caring for error */ + if (c->error) { + d->erasable = 0; + d->start_lba = 0; + d->end_lba = 0; + goto ex; + } + + /* 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]); + + /* ts B21124 : LITE-ON LTR-48125S returns crap on pressed + audio CD and CD-ROM + */ + if (d->start_lba >= d->end_lba) { + d->start_lba = 0; + d->end_lba = 0; + } + + 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 ? + +*/ + +ex:; + BURN_FREE_MEM(buf); + BURN_FREE_MEM(c); +} + + +int mmc_eval_read_error(struct burn_drive *d, struct command *c, char *what, + int start_m, int start_s, int start_f, + int end_m, int end_s, int end_f, int flag) +{ + char *msg = NULL; + int key, asc, ascq, silent; + + if (!c->error) + return 0; + + msg = calloc(1, 256); + if (msg != NULL) { + if (start_s < 0 || start_f < 0 || end_s < 0 || end_f < 0) { + sprintf(msg, + "SCSI error on %s(%d,%d): ", what, start_m, end_m); + } else { + sprintf(msg, "SCSI error on %s(%dm%ds%df,%dm%ds%df): ", + what, + start_m, start_s, start_f, end_m, end_s, end_f); + } + scsi_error_msg(d, c->sense, 14, msg + strlen(msg), + &key, &asc, &ascq); + silent = (d->silent_on_scsi_error == 1); + if (key == 5 && asc == 0x64 && ascq == 0x0) { + d->had_particular_error |= 1; + if (d->silent_on_scsi_error == 2) + silent = 1; + } + if(!silent) + libdax_msgs_submit(libdax_messenger, + d->global_index, + 0x00020144, + d->silent_on_scsi_error == 3 ? + LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, + LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); + free(msg); + } + return BE_CANCELLED; +} + + +/* ts B21119 : Derived from older mmc_read_sectors() + @param flag bit0= set DAP bit (also with o->dap_bit) +*/ +int mmc_read_cd_msf(struct burn_drive *d, + int start_m, int start_s, int start_f, + int end_m, int end_s, int end_f, + int sec_type, int main_ch, + const struct burn_read_opts *o, struct buffer *buf, int flag) +{ + int req, ret, dap_bit; + int subcodes_audio = 0, subcodes_data = 0; + struct command *c; +#ifdef Libburn_mmc_report_recovereD + int report_recovered_errors = 0; +#endif + + c = &(d->casual_command); + mmc_start_if_needed(d, 0); + if (mmc_function_spy(d, "mmc_read_cd_msf") <= 0) + return -1; + + dap_bit = flag & 1; + if (o != NULL) { + subcodes_audio = o->subcodes_audio; + subcodes_data = o->subcodes_data; + dap_bit |= o->dap_bit; + +#ifdef Libburn_mmc_report_recovereD + report_recovered_errors = o->report_recovered_errors; +#endif + } + + scsi_init_command(c, MMC_READ_CD_MSF, sizeof(MMC_READ_CD_MSF)); + c->retry = 1; + c->opcode[1] = ((sec_type & 7) << 2) | ((!!dap_bit) << 1); + c->opcode[3] = start_m; + c->opcode[4] = start_s; + c->opcode[5] = start_f; + c->opcode[6] = end_m; + c->opcode[7] = end_s; + c->opcode[8] = end_f; + + req = main_ch & 0xf8; + +#ifdef Libburn_mmc_report_recovereD + /* ts A61106 : LG GSA-4082B dislikes this. key=5h asc=24h ascq=00h */ + if (d->busy == BURN_DRIVE_GRABBING || report_recovered_errors) + req |= 2; +#endif /* Libburn_mmc_report_recovereD */ + + c->opcode[9] = req; + + c->opcode[10] = 0; +/* always read the subcode, throw it away later, since we don't know + what we're really reading +*/ +/* >>> ts B21125 : This is very obscure: + MMC-3 has sub channel selection 001b as "RAW" + MMC-5 does neither mention 001b nor "RAW". + And why should a non-grabbed drive get here ? +*/ + if (d->busy == BURN_DRIVE_GRABBING || subcodes_audio || subcodes_data) + c->opcode[10] = 1; + +/* <<< ts B21125 : test with sub channel selection 100b + no data, only sub channel + c->opcode[9] = 0; + c->opcode[10] = 4; + Did not help either with reading before LBA -150 +*/ +/* <<< ts B21125 : test with sub channel selection 001b and no user data + c->opcode[9] = 0; + c->opcode[10] = 1; +*/ + + c->page = buf; + c->dir = FROM_DRIVE; + d->issue_command(d, c); + ret = mmc_eval_read_error(d, c, "read_cd_msf", + start_m, start_s, start_f, + end_m, end_s, end_f, 0); + return ret; +} + + +/* ts B21119 : Derived from older mmc_read_sectors() + @param flag bit0= set DAP bit (also with o->dap_bit) +*/ +int mmc_read_cd(struct burn_drive *d, int start, int len, + int sec_type, int main_ch, + const struct burn_read_opts *o, struct buffer *buf, int flag) +{ + int temp, req, ret, dap_bit; + int subcodes_audio = 0, subcodes_data = 0; + struct command *c; + +#ifdef Libburn_mmc_report_recovereD + int report_recovered_errors = 0; +#endif + +/* # define Libburn_read_cd_by_msF 1 */ +#ifdef Libburn_read_cd_by_msF + + int start_m, start_s, start_f, end_m, end_s, end_f; + + burn_lba_to_msf(start, &start_m, &start_s, &start_f); + burn_lba_to_msf(start + len, &end_m, &end_s, &end_f); + ret = mmc_read_cd_msf(d, start_m, start_s, start_f, + end_m, end_s, end_f, + sec_type, main_ch, o, buf, flag); + return ret; + +#endif /* Libburn_read_cd_by_msF */ + + c = &(d->casual_command); + mmc_start_if_needed(d, 0); + if (mmc_function_spy(d, "mmc_read_cd") <= 0) + return -1; + + dap_bit = flag & 1; + if (o != NULL) { + subcodes_audio = o->subcodes_audio; + subcodes_data = o->subcodes_data; + dap_bit |= o->dap_bit; + +#ifdef Libburn_mmc_report_recovereD + report_recovered_errors = o->report_recovered_errors; +#endif + } + + scsi_init_command(c, MMC_READ_CD, sizeof(MMC_READ_CD)); + c->retry = 1; + c->opcode[1] = ((sec_type & 7) << 2) | ((!!dap_bit) << 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 = main_ch & 0xf8; + +#ifdef Libburn_mmc_report_recovereD + /* ts A61106 : LG GSA-4082B dislikes this. key=5h asc=24h ascq=00h */ + if (d->busy == BURN_DRIVE_GRABBING || report_recovered_errors) + req |= 2; +#endif /* Libburn_mmc_report_recovereD */ + + c->opcode[9] = req; + c->opcode[10] = 0; +/* always read the subcode, throw it away later, since we don't know + what we're really reading +*/ +/* >>> ts B21125 : This is very obscure: + MMC-3 has sub channel selection 001b as "RAW" + MMC-5 does neither mention 001b nor "RAW". + And why should a non-grabbed drive get here ? +*/ + if (d->busy == BURN_DRIVE_GRABBING || subcodes_audio || subcodes_data) + c->opcode[10] = 1; + +/* <<< ts B21125 : test with sub channel selection 100b + c->opcode[10] = 4; +*/ +/* <<< ts B21125 : test with sub channel selection 001b and no user data + c->opcode[9] = 0; + c->opcode[10] = 1; +*/ + + c->page = buf; + c->dir = FROM_DRIVE; + d->issue_command(d, c); + ret = mmc_eval_read_error(d, c, "read_cd", start, -1, -1, + len, -1, -1, 0); + return ret; +} + +void mmc_erase(struct burn_drive *d, int fast) +{ + struct command *c; + + c = &(d->casual_command); + mmc_start_if_needed(d, 0); + if (mmc_function_spy(d, "mmc_erase") <= 0) + return; + + scsi_init_command(c, MMC_BLANK, 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; + c->timeout = Libburn_mmc_blank_timeouT; + d->issue_command(d, c); + if (c->error) { + d->cancel = 1; + scsi_notify_error(d, c, c->sense, 14, 2); + } +} + +void mmc_read_lead_in(struct burn_drive *d, struct buffer *buf) +{ + struct command *c; + + c = &(d->casual_command); + mmc_start_if_needed(d, 0); + if (mmc_function_spy(d, "mmc_read_lead_in") <= 0) + return; + + scsi_init_command(c, MMC_READ_CD, 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; + + c = &(d->casual_command); + mmc_start_if_needed(d, 0); + if (mmc_function_spy(d, "mmc_perform_opc") <= 0) + return; + + scsi_init_command(c, MMC_SEND_OPC, sizeof(MMC_SEND_OPC)); + c->retry = 1; + c->opcode[1] = 1; + c->page = NULL; + c->dir = NO_TRANSFER; + c->timeout = Libburn_mmc_opc_timeouT; + 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 = NULL; + struct command *c = NULL; + int b, eff_end_lba, ret; + char *msg = NULL; + unsigned char *pd; + int key, asc, ascq; + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + BURN_ALLOC_MEM(msg, char, 256); + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "mmc_set_streaming") <= 0) + {ret = 0; goto ex;} + + scsi_init_command(c, 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", + eff_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] = (eff_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) { + spc_decode_sense(c->sense, 0, &key, &asc, &ascq); + if (key != 0 && d->silent_on_scsi_error != 1 && + d->silent_on_scsi_error != 2) { + sprintf(msg, + "SCSI error on set_streaming(%d): ", w_speed); + scsi_error_msg(d, c->sense, 14, msg + strlen(msg), + &key, &asc, &ascq); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020124, + d->silent_on_scsi_error == 3 ? + LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, + LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); + } + {ret = 0; goto ex;} + } + ret = 1; +ex:; + BURN_FREE_MEM(msg); + BURN_FREE_MEM(c); + BURN_FREE_MEM(buf); + return ret; +} + + +void mmc_set_speed(struct burn_drive *d, int r, int w) +{ + struct command *c; + int ret, end_lba = 0, get_max, get_min; + struct burn_speed_descriptor *best_sd = NULL; + + c = &(d->casual_command); + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "mmc_set_speed") <= 0) + return; + + if (r <= 0 || w <= 0) { + /* ts A70712 : now searching for best speed descriptor */ + /* ts B31030 : keeping max read speed from sinking too low */ + if (r <= 0) { + get_max = (r == 0); + get_min = (r == -1); + burn_drive_get_best_speed(d, r, &best_sd, 1 | 2); + if (best_sd != NULL) { + r = best_sd->read_speed; + end_lba = best_sd->end_lba; + } + if (get_max) { + if (d->current_is_cd_profile) { + if (r < Libburn_cd_max_read_speeD) + r = Libburn_cd_max_read_speeD; + } else if (d->current_profile >= 0x10 && + d->current_profile <= 0x2f) { + if (r < Libburn_dvd_max_read_speeD) + r = Libburn_dvd_max_read_speeD; + } else if (d->current_profile >= 0x40 && + d->current_profile <= 0x43) { + if (r < Libburn_bd_max_read_speeD) + r = Libburn_bd_max_read_speeD; + } + } else if(get_min) { + if (d->current_is_cd_profile) { + if (r > Libburn_cd_min_read_speeD) + r = Libburn_cd_min_read_speeD; + } else if (d->current_profile >= 0x10 && + d->current_profile <= 0x2f) { + if (r > Libburn_dvd_min_read_speeD) + r = Libburn_dvd_min_read_speeD; + } else if (d->current_profile >= 0x40 && + d->current_profile <= 0x43) { + if (r > Libburn_bd_min_read_speeD) + r = Libburn_bd_min_read_speeD; + } + } + } + if (w <= 0) { + burn_drive_get_best_speed(d, w, &best_sd, 2); + if (best_sd != NULL) { + w = best_sd->write_speed; + d->nominal_write_speed = w; + if (end_lba < best_sd->end_lba) + 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 + || + strstr(d->current_profile_text, "BD") == 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)); + 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 : found in unfunctional state + */ +static int mmc_get_configuration_al(struct burn_drive *d, int *alloc_len) +{ + struct buffer *buf = NULL; + int len, cp, descr_len = 0, feature_code, only_current = 1, i; + int old_alloc_len, only_current_profile = 0, key, asc, ascq, ret; + int feature_is_current; + unsigned char *descr, *prf, *up_to, *prf_end; + struct command *c = NULL; + int phys_if_std = 0; + char *phys_name = ""; + struct burn_feature_descr *recent_feature = NULL, *new_feature; + +/* Enable this to get loud and repeated reports about the feature set : + # define Libburn_print_feature_descriptorS 1 +*/ +#ifdef Libburn_print_feature_descriptorS + int prf_number; + + only_current = 0; +#endif + + if (*alloc_len < 8) + {ret = 0; goto ex;} + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + d->current_profile = 0; + d->current_profile_text[0] = 0; + d->current_is_cd_profile = 0; + d->current_is_supported_profile = 0; + d->current_is_guessed_profile = 0; + d->num_profiles = 0; + if (d->features != NULL) + burn_feature_descr_free(&(d->features), 0); + d->current_has_feat21h = 0; + d->current_feat21h_link_size = -1; + d->current_feat23h_byte4 = 0; + d->current_feat23h_byte8 = 0; + d->current_feat2fh_byte4 = -1; + + scsi_init_command(c, MMC_GET_CONFIGURATION, + 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); + +#ifdef Libisofs_simulate_old_mmc1_drivE + c->error = 1; + c->sense[0] = 0x70; /* Fixed format sense data */ + c->sense[2] = 0x5; + c->sense[12] = 0x20; + c->sense[13] = 0x0; +#endif /* Libisofs_simulate_old_mmc1_drivE */ + + if (c->error) { + /* ts A90603 : MMC-1 drive do not know 46h GET CONFIGURATION */ + spc_decode_sense(c->sense, 0, &key, &asc, &ascq); + if (key == 0x5 && asc == 0x20 && ascq == 0x0) { + d->current_is_guessed_profile = 1; + /* Will yield a non-zero profile only after + mmc_read_disc_info_al() was called */ + mmc_guess_profile(d, 0); + } + {ret = 0; goto ex;} + } + old_alloc_len = *alloc_len; + *alloc_len = len = mmc_four_char_to_int(c->page->data) + 4; + if (len > old_alloc_len) + len = old_alloc_len; + if (len < 8 || len > 4096) + {ret = 0; goto ex;} + cp = (c->page->data[6]<<8) | c->page->data[7]; + +#ifdef Libburn_rom_as_profilE + if (cp == 0x08 || cp == 0x10 || cp==0x40) + cp = Libburn_rom_as_profilE; +#endif /* Libburn_rom_as_profilE */ + + d->current_profile = cp; + strcpy(d->current_profile_text, mmc_obtain_profile_name(cp)); + + /* Read-only supported media */ + + if (cp == 0x08) /* CD-ROM */ + d->current_is_supported_profile = d->current_is_cd_profile = 1; + if (cp == 0x10) /* DVD-ROM */ + d->current_is_supported_profile = 1; + if (cp == 0x40) /* BD-ROM */ + d->current_is_supported_profile = 1; + +#ifdef Libburn_support_bd_r_readonlY +#ifndef Libburn_support_bd_plus_r_srM + if (cp == 0x41) /* BD-R sequential (here as read-only dummy) */ + d->current_is_supported_profile = 1; +#endif + if (cp == 0x42) /* BD-R random recording */ + d->current_is_supported_profile = 1; +#endif + + + /* Write supported media (they get declared suitable in + burn_disc_get_multi_caps) */ + + if (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 || cp == 0x43) { /* DVD-RAM , BD-RE */ + d->current_is_supported_profile = 1; + +#ifdef Libburn_dvd_ram_as_bd_rE + cp = d->current_profile = 0x43; + strcpy(d->current_profile_text, mmc_obtain_profile_name(cp)); +#endif + + } +#endif +#ifdef Libburn_support_dvd_r_seQ + if (cp == 0x11 || cp == 0x14) /* DVD-R, DVD-RW */ + d->current_is_supported_profile = 1; + if (cp == 0x15) /* DVD-R/DL */ + d->current_is_supported_profile = 1; +#endif +#ifdef Libburn_support_dvd_plus_R + if (cp == 0x1b || cp == 0x2b) /* DVD+R , DVD+R/DL */ + d->current_is_supported_profile = 1; +#endif +#ifdef Libburn_support_bd_plus_r_srM + if (cp == 0x41) /* BD-R SRM */ + d->current_is_supported_profile = 1; +#endif + + /* 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, (int) (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]; + feature_is_current = descr[2] & 1; + + ret = burn_feature_descr_new(&new_feature, descr, + up_to - descr, 0); + if (ret > 0) { + if (d->features == NULL) + d->features = new_feature; + else + recent_feature->next = new_feature; + recent_feature = new_feature; + } + + if (only_current && !feature_is_current) + continue; + +#ifdef Libburn_print_feature_descriptorS + fprintf(stderr, + "LIBBURN_EXPERIMENTAL : %s feature %4.4Xh :", + (descr[2] & 1) ? "+" : "-", + feature_code); + if (feature_code != 0x00) + for (i = 2; i < descr_len; i++) + fprintf(stderr, " %2.2X", descr[i]); + fprintf(stderr, "\n"); +#endif /* Libburn_print_feature_descriptorS */ + + if (feature_code == 0x0) { + prf_end = descr + 4 + descr[3]; + d->num_profiles = descr[3] / 4; + if (d->num_profiles > 64) + d->num_profiles = 64; + if (d->num_profiles > 0) + memcpy(d->all_profiles, descr + 4, + d->num_profiles * 4); + for (prf = descr + 4; prf + 2 < prf_end; prf += 4) { + if (only_current_profile && !(prf[2] & 1)) + continue; + +#ifdef Libburn_print_feature_descriptorS + prf_number = (prf[0] << 8) | prf[1]; + 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) { + + d->current_has_feat21h = feature_is_current; + 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 == 0x23) { + if (feature_is_current) { + d->current_feat23h_byte4 = descr[4]; + d->current_feat23h_byte8 = descr[8]; + } +#ifdef Libburn_print_feature_descriptorS + if (cp >= 0x41 && cp <= 0x43) + fprintf(stderr, + "LIBBURN_EXPERIMENTAL : BD formats: %s%s%s%s%s\n", + descr[4] & 1 ? " Cert" : "", + descr[4] & 2 ? " QCert" : "", + descr[4] & 4 ? " Expand" : "", + descr[4] & 8 ? " RENoSA" : "", + descr[8] & 1 ? " RRM" : ""); +#endif /* Libburn_print_feature_descriptorS */ + + } else if (feature_code == 0x2F) { + if (feature_is_current) + 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[7]; + 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 */ + + } + } + ret = 1; +ex: + BURN_FREE_MEM(buf); + BURN_FREE_MEM(c); + return ret; +} + + +void mmc_get_configuration(struct burn_drive *d) +{ + int alloc_len = 8, ret; + char *msg = NULL; + + if (d->current_profile > 0 && d->current_profile < 0xffff) + goto ex; + + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "mmc_get_configuration") <= 0) + goto ex; + + /* 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) { + + if (alloc_len > 4096) { + /* MMC-5 6.6.2.1: The maximum is less than 1 KB */ + BURN_ALLOC_MEM_VOID(msg, char, 256); + sprintf(msg, "Implausible lenght announcement from SCSI command GET CONFIGURATION: %d", alloc_len); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x000201a9, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + goto ex; + } + + /* second execution with announced length */ + mmc_get_configuration_al(d, &alloc_len); + } +ex:; + BURN_FREE_MEM(msg); +} + + +/* 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 = NULL; + int len, type, score, num_descr, max_score = -2000000000, i, sign = 1; + int old_alloc_len, ret; + off_t size, num_blocks; + struct command *c = NULL; + unsigned char *dpt; + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + if (*alloc_len < 4) + {ret = 0; goto ex;} + + 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)); + 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) + {ret = 0; goto ex;} + + len = c->page->data[3]; + old_alloc_len = *alloc_len; + *alloc_len = len + 4; + if (old_alloc_len < 12) + {ret = 1; goto ex;} + if (len + 4 > old_alloc_len) + len = old_alloc_len - 4; + if (len < 8) + {ret = 0; goto ex;} + + 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]; + if (d->format_descr_type == BURN_FORMAT_IS_UNKNOWN) + d->format_curr_max_size = 0; + d->format_curr_blsas = (dpt[5] << 16) + (dpt[6] << 8) + dpt[7]; + d->format_curr_max_size *= (off_t) 2048; + if((d->current_profile == 0x12 || d->current_profile == 0x43) + && d->media_capacity_remaining == 0) { + burn_drive_set_media_capacity_remaining(d, + d->format_curr_max_size); + d->media_lba_limit = d->format_curr_max_size / 2048; + } + + +#ifdef Libburn_dvd_ram_as_bd_rE + /* <<< dummy format descriptor list as obtained from + dvd+rw-mediainfo by Giulio Orsero in April 2008 + */ + d->num_format_descr = 5; + d->format_descriptors[0].type = 0x00; + d->format_descriptors[0].size = (off_t) 11826176 * (off_t) 2048; + d->format_descriptors[0].tdp = 0x3000; + d->format_descriptors[1].type = 0x30; + d->format_descriptors[1].size = (off_t) 11826176 * (off_t) 2048; + d->format_descriptors[1].tdp = 0x3000; + d->format_descriptors[2].type = 0x30; + d->format_descriptors[2].size = (off_t) 11564032 * (off_t) 2048; + d->format_descriptors[2].tdp = 0x5000; + d->format_descriptors[3].type = 0x30; + d->format_descriptors[3].size = (off_t) 12088320 * (off_t) 2048; + d->format_descriptors[3].tdp = 0x1000; + d->format_descriptors[4].type = 0x31; + d->format_descriptors[4].size = (off_t) 12219392 * (off_t) 2048; + d->format_descriptors[4].tdp = 0x800; + d->best_format_type = 0x00; + d->best_format_size = (off_t) 11826176 * (off_t) 2048; + + /* silencing compiler warnings about unused variables */ + num_blocks = size = sign = i = max_score = num_descr = score = type = 0; + + if (d->current_profile == 0x12 || d->current_profile == 0x43) + {ret = 1; goto ex;} + d->num_format_descr = 0; + +#endif /* Libburn_dvd_ram_as_bd_rE */ + + 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; + } + /* Criterion is proximity to quick intermediate state */ + if (type == 0x00) { /* full format (with lead out) */ + score = 1 * sign; + } else if (type == 0x10) { /* DVD-RW full format */ + score = 10 * sign; + } else if(type == 0x13) { /* DVD-RW quick grow last session */ + score = 100 * sign; + } else if(type == 0x15) { /* DVD-RW Quick */ + score = 50 * sign; + if(d->current_profile == 0x13) { + burn_drive_set_media_capacity_remaining(d, + size); + d->media_lba_limit = num_blocks; + } + } else if(type == 0x26) { /* DVD+RW */ + score = 1 * sign; + burn_drive_set_media_capacity_remaining(d, 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; + } + } + ret = 1; +ex: + BURN_FREE_MEM(buf); + BURN_FREE_MEM(c); + return ret; +} + + +int mmc_read_format_capacities(struct burn_drive *d, int top_wanted) +{ + int alloc_len = 4, ret; + + mmc_start_if_needed(d, 1); + 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 = NULL; + char *msg = NULL; + int key, asc, ascq; + + if (mmc_function_spy(d, "mmc_sync_cache") <= 0) + goto ex; + + BURN_ALLOC_MEM_VOID(c, struct command, 1); + BURN_ALLOC_MEM_VOID(msg, char, 256); + + scsi_init_command(c, MMC_SYNC_CACHE, sizeof(MMC_SYNC_CACHE)); + c->retry = 1; + c->opcode[1] |= 2; /* ts A70918 : Immed */ + c->page = NULL; + c->dir = NO_TRANSFER; + c->timeout = Libburn_mmc_sync_timeouT; + + 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) { + 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) { + sprintf(msg, "Failed to synchronize drive cache"); + sprintf(msg + strlen(msg), ". SCSI error : "); + scsi_error_msg(d, c->sense, 14, msg + strlen(msg), + &key, &asc, &ascq); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002017f, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + d->cancel = 1; + goto ex; + } + + if (spc_wait_unit_attention(d, 3600, "SYNCHRONIZE CACHE", 0) <= 0) + d->cancel = 1; + else + d->needs_sync_cache = 0; +ex: + BURN_FREE_MEM(msg); + BURN_FREE_MEM(c); +} + + +/* ts A61023 : http://libburn.pykix.org/ticket/14 + get size and free space of drive buffer +*/ +int mmc_read_buffer_capacity(struct burn_drive *d) +{ + struct buffer *buf = NULL; + struct command *c = NULL; + unsigned char *data; + int alloc_len = 12, ret; + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + if (mmc_function_spy(d, "mmc_read_buffer_capacity") <= 0) + {ret = 0; goto ex;} + + scsi_init_command(c, MMC_READ_BUFFER_CAPACITY, + 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) + {ret = 0; goto ex;} + + data = c->page->data; + + d->progress.buffer_capacity = + (data[4]<<24)|(data[5]<<16)|(data[6]<<8)|data[7]; + d->progress.buffer_available = + (data[8]<<24)|(data[9]<<16)|(data[10]<<8)|data[11]; + if (d->progress.buffer_capacity < d->progress.buffer_available) { + /* Default mad buffer usage to 50 percent */ + d->progress.buffer_available = d->progress.buffer_capacity / 2; + } + 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; + } + ret = 1; +ex:; + BURN_FREE_MEM(c); + BURN_FREE_MEM(buf); + return ret; +} + + +/* 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+2: size mode + 0 = use parameter size as far as it makes sense + 1 = insist in size 0 even if there is a better default known + 2 = without bit7: format to maximum available size + with bit7 : take size from indexed format descriptor + 3 = format to default size + bit3= expand format up to at least size + bit4= enforce re-format of (partly) formatted media + bit5= try to disable eventual defect management + bit6= try to avoid lengthy media certification + bit7= bit8 to bit15 contain the index of the format to use + bit8-bit15 = see bit7 + bit16= enable POW on blank BD-R +*/ +int mmc_format_unit(struct burn_drive *d, off_t size, int flag) +{ + struct buffer *buf = NULL; + struct command *c = NULL; + int ret, tolerate_failure = 0, return_immediately = 0, i, format_type; + int index, format_sub_type = 0, format_00_index, size_mode; + int accept_count = 0; + off_t num_of_blocks = 0, diff, format_size, i_size, format_00_max_size; + off_t min_size = -1, max_size = -1; + char *msg = NULL, descr[80]; + int key, asc, ascq; + int full_format_type = 0x00; /* Full Format (or 0x10 for DVD-RW ?) */ + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + BURN_ALLOC_MEM(msg, char, 256); + mmc_start_if_needed(d, 0); + if (mmc_function_spy(d, "mmc_format_unit") <= 0) + {ret = 0; goto ex;} + size_mode = (flag >> 1) & 3; + + scsi_init_command(c, 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; + c->timeout = Libburn_mmc_blank_timeouT; + 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); + {ret = 0; goto ex;} + } + if (!(d->current_profile == 0x13 || + d->current_profile == 0x14 || + d->current_profile == 0x1a || + d->current_profile == 0x12 || + d->current_profile == 0x41 || + d->current_profile == 0x43)) + goto unsuitable_media; + + format_type = d->format_descriptors[index].type; + if (!(format_type == 0x00 || format_type == 0x01 || + format_type == 0x10 || + format_type == 0x11 || format_type == 0x13 || + format_type == 0x15 || format_type == 0x26 || + format_type == 0x30 || format_type == 0x31 || + format_type == 0x32)) + 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; + if (format_type == 0x30 || format_type == 0x31) { + format_sub_type = 0; + if (flag & 64) { + if (d->current_feat23h_byte4 & 2) + /* Quick certification */ + format_sub_type = 3; + } else { + if (d->current_feat23h_byte4 & 1) + /* Full certification */ + format_sub_type = 2; + } + } else if (format_type == 0x32 || + (format_type == 0x00 && d->current_profile == 0x41)) { + if (flag & (1 << 16)) + format_sub_type = 0; /* SRM + POW */ + else + format_sub_type = 1; /* SRM (- POW) */ + } + if (d->current_profile == 0x12 && format_type !=0x01 && + (flag & 64)) { + /* DCRT and CmpList, see below */ + c->page->data[1] |= 0x20; + c->opcode[1] |= 0x08; + } + c->page->data[1] |= 0x80; /* FOV = this flag vector is valid */ + sprintf(descr, "%s (descr %d)", d->current_profile_text,index); + 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; + + /* >>> ??? is this "| 8" a bug ? */ + + 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); + {ret = 2; goto ex;} + } + 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) + {ret = 1; goto ex;} + } else { + if (d->format_descr_type == 2) /* formatted */ + {ret = 1; goto ex;} + if (d->format_descr_type == 3){/*intermediate*/ + d->needs_close_session = 1; + {ret = 1; goto ex;} + } + /* 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 { +no_suitable_formatting_type:; + 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); + {ret = 0; goto ex;} + } + format_type = d->best_format_type; + sprintf(descr, "DVD-RW %s", + format_type == 0x15 ? "quick" : "full"); + return_immediately = 1; /* caller must do the waiting */ + + } else if (d->current_profile == 0x12) { + /* ts A80417 : DVD-RAM */ + /* 6.5.4.2.1 Format Type = 00h (Full Format) + 6.5.4.2.2 Format Type = 01h (Spare Area Expansion) + */ + index = format_00_index = -1; + format_size = format_00_max_size = -1; + for (i = 0; i < d->num_format_descr; i++) { + format_type = d->format_descriptors[i].type; + i_size = d->format_descriptors[i].size; + if (format_type != 0x00 && format_type != 0x01) + continue; + if (flag & 32) { /* No defect mgt */ + /* Search for largest 0x00 format descriptor */ + if (format_type != 0x00) + continue; + if (i_size < format_size) + continue; + format_size = i_size; + index = i; + continue; + } else if (flag & 4) { /*Max or default size with mgt*/ + /* Search for second largest 0x00 + format descriptor. For max size allow + format type 0x01. + */ + if (format_type == 0x00) { + if (i_size < format_size) + continue; + if (i_size < format_00_max_size) { + format_size = i_size; + index = i; + continue; + } + format_size = format_00_max_size; + index = format_00_index; + format_00_max_size = i_size; + format_00_index = i; + continue; + } + if (size_mode==3) + continue; + if (i_size > format_size) { + format_size = i_size; + index = i; + } + continue; + } + /* Search for smallest 0x0 or 0x01 + descriptor >= size */; + if (d->format_descriptors[i].size >= size && + (format_size < 0 || i_size < format_size) + ) { + format_size = i_size; + index = i; + } + } + if(index < 0 && (flag & 4) && !(flag & 32)) { + format_size = format_00_max_size; + index = format_00_index; + } + if(index < 0) + goto no_suitable_formatting_type; + format_type = d->format_descriptors[index].type; + num_of_blocks = d->format_descriptors[index].size / 2048; + mmc_int_to_four_char(c->page->data + 4, num_of_blocks); + for (i = 0; i < 3; i++) + c->page->data[9 + i] = + ( d->format_descriptors[index].tdp >> + (16 - 8 * i)) & 0xff; + sprintf(descr, "%s", d->current_profile_text); + return_immediately = 1; /* caller must do the waiting */ + c->page->data[1] |= 0x80; /* FOV = this flag vector is valid */ + + if ((flag & 64) && format_type != 0x01) { + /* MMC-5 6.5.3.2 , 6.5.4.2.1.2 + DCRT: Disable Certification and maintain number + of blocks + CmpList: Override maintaining of number of blocks + with DCRT + */ + /* ts A80426 : prevents change of formatted size + with PHILIPS SPD3300L and Verbatim 3x DVD-RAM + and format_type 0x00. Works on TSSTcorp SH-S203B + */ + c->page->data[1] |= 0x20; + c->opcode[1] |= 0x08; + } + + } else if (d->current_profile == 0x41) { + /* BD-R SRM */ + + index = -1; + format_size = -1; + if (d->num_format_descr <= 0) + goto no_suitable_formatting_type; + if (d->format_descriptors[0].type != 0) + goto no_suitable_formatting_type; + for (i = 0; i < d->num_format_descr; i++) { + format_type = d->format_descriptors[i].type; + i_size = d->format_descriptors[i].size; + if (format_type != 0x00 && format_type != 0x32) + continue; + if (flag & 32) { /* No defect mgt */ + /* ts A81211 : MMC-5 6.5.4.2.17.1 + When formatted with Format Type 32h, + the BD-R disc is required to allocate + a non-zero number of spares. + */ + goto no_suitable_formatting_type; + + } else if(size_mode == 2) { /* max payload size */ + /* search largest 0x32 format descriptor */ + if(format_type != 0x32) + continue; + } else if(size_mode == 3) { /* default payload size */ + if (format_type == 0x00) { + index = i; + break; + } + continue; + } else { /* defect managed format with size wish */ + +#ifdef Libburn_bd_r_format_olD + + /* search for smallest 0x32 >= size */ + if(format_type != 0x32) + continue; + if (i_size < size) + continue; + if (format_size >= 0 && i_size >= format_size) + continue; + index = i; + format_size = i_size; + continue; + +#else /* Libburn_bd_r_format_olD */ + + /* search largest and smallest 0x32 */ + if(format_type != 0x32) + continue; + if (i_size < min_size || min_size < 0) + min_size = i_size; + if (i_size > max_size) + max_size = i_size; + +#endif /* ! Libburn_bd_r_format_olD */ + + } + /* common for all cases which search largest + descriptors */ + if (i_size > format_size) { + format_size = i_size; + index = i; + } + } + if (size_mode == 2 && index < 0 && !(flag & 32)) + index = 0; + if (index < 0) + goto no_suitable_formatting_type; + format_type = d->format_descriptors[index].type; + if (flag & (1 << 16)) + format_sub_type = 0; /* SRM + POW */ + else + format_sub_type = 1; /* SRM (- POW) */ + +#ifdef Libburn_bd_r_format_olD + if (0) { +#else + if (size_mode == 0 || size_mode == 1) { +#endif /* ! Libburn_bd_r_format_olD */ + + if (min_size < 0 || max_size < 0) + goto no_suitable_formatting_type; + if (size <= 0) + size = min_size; + if (size % 0x10000) + size += 0x10000 - (size % 0x10000); + if (size < min_size) + goto no_suitable_formatting_type; + else if(size > max_size) + goto no_suitable_formatting_type; + num_of_blocks = size / 2048; + mmc_int_to_four_char(c->page->data + 4, num_of_blocks); + for (i = 0; i < 3; i++) + c->page->data[9 + i] = 0; + } else { + num_of_blocks = + d->format_descriptors[index].size / 2048; + mmc_int_to_four_char(c->page->data + 4, num_of_blocks); + for (i = 0; i < 3; i++) + c->page->data[9 + i] = + ( d->format_descriptors[index].tdp >> + (16 - 8 * i)) & 0xff; + } + sprintf(descr, "%s", d->current_profile_text); + return_immediately = 1; /* caller must do the waiting */ + c->page->data[1] |= 0x80; /* FOV = this flag vector is valid */ + + } else if (d->current_profile == 0x43) { + /* BD-RE */ + index = -1; + format_size = -1; + if (d->num_format_descr <= 0) + goto no_suitable_formatting_type; + if (d->format_descriptors[0].type != 0) + goto no_suitable_formatting_type; + for (i = 0; i < d->num_format_descr; i++) { + format_type = d->format_descriptors[i].type; + i_size = d->format_descriptors[i].size; + if (format_type != 0x00 && format_type != 0x30 && + format_type != 0x31) + continue; + if (flag & 32) { /* No defect mgt */ + /* search largest format 0x31 */ + if(format_type != 0x31) + continue; + } else if(size_mode == 2) { /* max payload size */ + /* search largest 0x30 format descriptor */ + if(format_type != 0x30) + continue; + } else if(size_mode == 3) { /* default payload size */ + if (accept_count < 1) + index = 0; /* this cannot certify */ + + /* ts A81129 + LG GGW-H20L YL03 refuses on 0x30 with + "Quick certification". dvd+rw-format + does 0x00 by default and succeeds quickly. + */ + if ((flag & 64) && format_type == 0x00) { + index = i; + break; + } + + if(format_type != 0x30) + continue; + accept_count++; + if (accept_count == 1) + index = i; + continue; + } else { /* defect managed format with size wish */ + +#ifdef Libburn_bd_re_format_olD + + /* search for smallest 0x30 >= size */ + if(format_type != 0x30) + continue; + if (i_size < size) + continue; + if (format_size >= 0 && i_size >= format_size) + continue; + index = i; + format_size = i_size; + continue; + +#else /* Libburn_bd_re_format_olD */ + + /* search largest and smallest 0x30 */ + if(format_type != 0x30) + continue; + if (i_size < min_size || min_size < 0) + min_size = i_size; + if (i_size > max_size) + max_size = i_size; + +#endif /* ! Libburn_bd_re_format_olD */ + + } + /* common for all cases which search largest + descriptors */ + if (i_size > format_size) { + format_size = i_size; + index = i; + } + } + + if (size_mode == 2 && index < 0 && !(flag & 32)) + index = 0; + if (index < 0) + goto no_suitable_formatting_type; + format_type = d->format_descriptors[index].type; + if (format_type == 0x30 || format_type == 0x31) { + if ((flag & 64) || !(d->current_feat23h_byte4 & 3)) { + format_sub_type = 0; + if (!(flag & 64)) + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x0002019e, + LIBDAX_MSGS_SEV_NOTE, + LIBDAX_MSGS_PRIO_HIGH, + "Drive does not support media certification", + 0, 0); + } else { + /* With Certification */ + if (d->current_feat23h_byte4 & 1) + format_sub_type = 2; /* Full */ + else + format_sub_type = 3; /* Quick */ + } + } + +#ifdef Libburn_bd_re_format_olD + if (0) { +#else + if (size_mode == 0 || size_mode == 1) { +#endif /* ! Libburn_bd_re_format_olD */ + + if (min_size < 0 || max_size < 0) + goto no_suitable_formatting_type; + if (size <= 0) + size = min_size; + if (size % 0x10000) + size += 0x10000 - (size % 0x10000); + if (size < min_size) + goto no_suitable_formatting_type; + else if(size > max_size) + goto no_suitable_formatting_type; + num_of_blocks = size / 2048; + mmc_int_to_four_char(c->page->data + 4, num_of_blocks); + for (i = 0; i < 3; i++) + c->page->data[9 + i] = 0; + } else { + num_of_blocks = + d->format_descriptors[index].size / 2048; + mmc_int_to_four_char(c->page->data + 4, num_of_blocks); + for (i = 0; i < 3; i++) + c->page->data[9 + i] = + ( d->format_descriptors[index].tdp >> + (16 - 8 * i)) & 0xff; + } + sprintf(descr, "%s", d->current_profile_text); + return_immediately = 1; /* caller must do the waiting */ + c->page->data[1] |= 0x80; /* FOV = this flag vector is valid */ + + } 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); + {ret = 0; goto ex;} + } + c->page->data[8] = (format_type << 2) | (format_sub_type & 3); + + /* MMC-5 Table 253 , column Type Dependent Parameter */ + if (format_type == 0x00 || format_type == 0x01 || + format_type == 0x31) { + /* Block Length 0x0800 = 2k */ + c->page->data[ 9] = 0x00; + c->page->data[10] = 0x08; + c->page->data[11] = 0x00; + } else if (format_type >= 0x10 && format_type <= 0x15) { + /* ECC block size = 16 * 2k */ + c->page->data[ 9] = 0; + c->page->data[10] = 0; + c->page->data[11] = 16; + } + + sprintf(msg, "Format type %2.2Xh \"%s\", blocks = %.f", + 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); + sprintf(msg, "CDB: "); + for (i = 0; i < 6; i++) + sprintf(msg + strlen(msg), "%2.2X ", c->opcode[i]); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + sprintf(msg, "Format list: "); + for (i = 0; i < 12; i++) + sprintf(msg + strlen(msg), "%2.2X ", c->page->data[i]); + strcat(msg, "\n"); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + +#ifdef Libburn_do_not_format_dvd_ram_or_bd_rE + if(d->current_profile == 0x43 || d->current_profile == 0x12) { + sprintf(msg, + "Formatting of %s not implemented yet - This is a dummy", + d->current_profile_text); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00000002, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + {ret = 1; goto ex;} + } +#endif /* Libburn_do_not_format_dvd_ram_or_bd_rE */ + +/* <<< +fprintf(stderr, "\nlibburn_DEBUG: FORMAT UNIT temporarily disabled.\n"); +ret = 1; goto ex; + */ + + d->issue_command(d, c); + if (c->error && !tolerate_failure) { + spc_decode_sense(c->sense, 0, &key, &asc, &ascq); + if (key != 0) { + sprintf(msg, "SCSI error on format_unit(%s): ", descr); + scsi_error_msg(d, c->sense, 14, msg + strlen(msg), + &key, &asc, &ascq); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020122, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + + } + {ret = 0; goto ex;} + } else if ((!c->error) && (format_type == 0x13 || format_type == 0x15)) + d->needs_close_session = 1; + if (return_immediately) + {ret = 1; goto ex;} + 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); + ret = 1; +ex:; + BURN_FREE_MEM(msg); + BURN_FREE_MEM(c); + BURN_FREE_MEM(buf); + return ret; +} + + +/* ts B40107 : Outsourced from mmc_get_performance_al() +*/ +static int new_burn_speed_descr(struct burn_drive *d, int sd_source, + struct burn_speed_descriptor **sd, int flag) +{ + int ret; + + ret = burn_speed_descriptor_new(&(d->mdata->speed_descriptors), + NULL, d->mdata->speed_descriptors, 0); + if (ret <= 0) + return ret; + + *sd = d->mdata->speed_descriptors; + (*sd)->source = sd_source; + if (d->current_profile > 0) { + (*sd)->profile_loaded = d->current_profile; + strcpy((*sd)->profile_name, d->current_profile_text); + } + return 1; +} + + +/* ts B40107 : Outsourced from mmc_get_performance_al() + and extended for descr_type 0x00 + @param flag bit0= register speed descriptors +*/ +static int interpret_performance(struct burn_drive *d, struct command *c, + int descr_type, int *alloc_len, int *max_descr, + int *num_descr, int flag) +{ + int len, i, b, ret, old_alloc_len; + int exact_bit, read_speed, write_speed, start_speed; + int min_write_speed = 0x7fffffff, max_write_speed = 0; + int min_read_speed = 0x7fffffff, max_read_speed = 0; + unsigned long end_lba; + unsigned char *pd; + struct burn_speed_descriptor *sd; + + /* ts A61225 : 1 = report about speed descriptors */ + static int speed_debug = 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; + {ret = 1; goto ex;} + } + if (old_alloc_len < 16) + {ret = 1; goto ex;} + if (len < 12) + {ret = 0; goto ex;} + + min_write_speed = d->mdata->min_write_speed; + max_write_speed = d->mdata->max_write_speed; + pd = c->page->data; + if (*num_descr > *max_descr) + *num_descr = *max_descr; + for (i = 0; i < *num_descr && (flag & 1); i++) { + end_lba = read_speed = write_speed = start_speed = 0; + + if (descr_type == 0x03) { + exact_bit = !!(pd[8 + i*16] & 2); + 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); + + ret = new_burn_speed_descr(d, 2, &sd, 0); + if (ret > 0) { + 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; + } + + } else { /* descr_type == 0 */ + for (b = 0; b < 4 ; b++) { + start_speed += pd[8 + i*16 + 4 + b] + << (24 - 8 * b); + end_lba += pd[8 + i*16 + 8 + b] + << (24 - 8 * b); + read_speed += pd[8 + i*16 + 12 + b] + << (24 - 8 * b); + } + + if (speed_debug) + fprintf(stderr, + "LIBBURN_DEBUG: start=%d end=%d lba=%lu\n", + start_speed, read_speed, end_lba); + + if (end_lba > 0x7ffffffe) + end_lba = 0x7ffffffe; + ret = new_burn_speed_descr(d, 3, &sd, 0); + if (ret > 0) { + sd->end_lba = end_lba; + sd->read_speed = start_speed; + } + if (start_speed > 0 && start_speed < min_read_speed) + min_read_speed = start_speed; + if (start_speed > max_read_speed) + max_read_speed = start_speed; + ret = new_burn_speed_descr(d, 3, &sd, 0); + if (ret > 0) { + sd->end_lba = end_lba; + sd->read_speed = read_speed; + } + } + + if ((int) end_lba > d->mdata->max_end_lba) + d->mdata->max_end_lba = end_lba; + if ((int) end_lba < d->mdata->min_end_lba) + d->mdata->min_end_lba = end_lba; + if (write_speed > 0 && write_speed < min_write_speed) + min_write_speed = write_speed; + if (write_speed > max_write_speed) + max_write_speed = write_speed; + if (read_speed > 0 && 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; + + ret = 1; +ex:; + return ret; +} + + +/* ts A61225 */ +/* @param flag bit0= register speed descriptors +*/ +static int mmc_get_performance_al(struct burn_drive *d, int descr_type, + int *alloc_len, int *max_descr, int flag) +{ + int num_descr, ret; + struct buffer *buf = NULL; + struct command *c = NULL; + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + + if (d->current_profile < 0) + mmc_get_configuration(d); + + if (*alloc_len < 8) + {ret = 0; goto ex;} + if (descr_type != 0x00 && descr_type != 0x03) + {ret = 0; goto ex;} + + scsi_init_command(c, MMC_GET_PERFORMANCE, + sizeof(MMC_GET_PERFORMANCE)); + + /* >>> 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; + + if (descr_type == 0x00) + c->opcode[1] = 0x10; /* Data Type: nominal read performance */ + c->opcode[8] = ( *max_descr >> 8 ) & 0xff; + c->opcode[9] = ( *max_descr >> 0 ) & 0xff; + c->opcode[10] = descr_type; + c->retry = 1; + c->page = buf; + c->page->sectors = 0; + c->page->bytes = 0; + c->dir = FROM_DRIVE; + d->issue_command(d, c); + +#ifdef Libisofs_simulate_old_mmc1_drivE + c->error = 1; + c->sense[0] = 0x70; /* Fixed format sense data */ + c->sense[2] = 0x5; + c->sense[12] = 0x20; + c->sense[13] = 0x0; +#endif /* Libisofs_simulate_old_mmc1_drivE */ + + if (c->error) + {ret = 0; goto ex;} + + ret = interpret_performance(d, c, descr_type, alloc_len, max_descr, + &num_descr, flag); + if (ret <= 0) + goto ex; + + ret = num_descr; +ex:; + BURN_FREE_MEM(buf); + BURN_FREE_MEM(c); + return ret; +} + + +int mmc_get_performance(struct burn_drive *d, int descr_type, int flag) +{ + int alloc_len = 8, max_descr = 0, ret; + + mmc_start_if_needed(d, 1); + 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_performance_al(d, descr_type, &alloc_len, &max_descr, 0); + if (max_descr > 0 && ret > 0) { + /* Some drives announce only 1 descriptor if asked for 0. + So ask twice for non-0 descriptors. + */ + ret = mmc_get_performance_al(d, descr_type, + &alloc_len, &max_descr, 0); + } +/* + fprintf(stderr,"LIBBURN_DEBUG: ACh alloc_len = %d , ret = %d\n", + alloc_len, ret); +*/ + if (max_descr > 0 && ret > 0) { + /* final execution with announced length */ + max_descr = (alloc_len - 8) / 16; + ret = mmc_get_performance_al(d, descr_type, + &alloc_len, &max_descr, 1); + } + return ret; +} + + +int mmc_get_write_performance(struct burn_drive *d) +{ + int ret; + + ret = mmc_get_performance(d, 0x03, 0); + 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, struct burn_session *s, + int tnum, const struct burn_write_opts *o, + unsigned char *pd) +{ + unsigned char *catalog = NULL; + char isrc_text[13]; + struct isrc *isrc; + + 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 , Copy = 0, Track Mode = 5 */ + pd[3] = 5; + +#ifdef Libburn_pioneer_dvr_216d_load_mode5 + + /* >>> use track mode from mmc_get_nwa() */ + /* >>> pd[3] = (pd[3] & ~0xf) | (d->track_inf[5] & 0xf); */ + +#endif + + /* 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 || + d->current_profile == 0x41 || d->current_profile == 0x42 || + d->current_profile == 0x43) { + /* not with DVD+R[W][/DL] or DVD-RAM or BD-R[E] */; + 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); + +/* +fprintf(stderr, "libburn_EXPERIMENTAL: block_type = %d, pd[4]= %u\n", + o->block_type, (unsigned int) pd[4]); +*/ + + /* 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 */ + + /* Media Catalog Number at byte 16 to 31, + MMC-5, 7.5, Tables 664, 670 + */ + if (o->has_mediacatalog) + catalog = (unsigned char *) o->mediacatalog; + else if (s != NULL) { + if (s->mediacatalog[0]) + catalog = s->mediacatalog; + } + if (catalog != NULL && d->mdata->write_page_length >= 30) { + pd[16] = 0x80; /* MCVAL */ + memcpy(pd + 17, catalog, 13); + } + + /* ISRC at bytes 32 to 47. Tables 664, 671 */ + /* SCMS at byte 3 bit 4 */ + isrc_text[0] = 0; + if (s != NULL && o->write_type == BURN_WRITE_TAO) { + if (tnum >= 0 && tnum < s->tracks) { + if (s->track[tnum]->isrc.has_isrc) { + isrc = &(s->track[tnum]->isrc); + isrc_text[0] = isrc->country[0]; + isrc_text[1] = isrc->country[1]; + isrc_text[2] = isrc->owner[0]; + isrc_text[3] = isrc->owner[1]; + isrc_text[4] = isrc->owner[2]; + sprintf(isrc_text + 5, "%-2.2u%-5.5u", + (unsigned int) isrc->year, + isrc->serial); + } + if ((s->track[tnum]->mode & BURN_SCMS) && + !(s->track[tnum]->mode & BURN_COPY)) + pd[3] |= 0x10; + } + } + if (isrc_text[0] != 0 && d->mdata->write_page_length >= 46) { + pd[32] = 0x80; /* TCVAL */ + memcpy(pd + 33, isrc_text, 12); + } + } + return 1; +} + + +/* A70812 ts */ +int mmc_read_10(struct burn_drive *d, int start,int amount, struct buffer *buf) +{ + struct command *c; + char *msg = NULL; + int key, asc, ascq, silent; + + c = &(d->casual_command); + mmc_start_if_needed(d, 0); + 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); + + /* <<< replace by mmc_eval_read_error */; + if (c->error) { + msg = calloc(1, 256); + if (msg != NULL) { + sprintf(msg, + "SCSI error on read_10(%d,%d): ", start, amount); + scsi_error_msg(d, c->sense, 14, msg + strlen(msg), + &key, &asc, &ascq); + silent = (d->silent_on_scsi_error == 1); + if (key == 5 && asc == 0x64 && ascq == 0x0) { + d->had_particular_error |= 1; + if (d->silent_on_scsi_error == 2) + silent = 1; + } + if(!silent) + libdax_msgs_submit(libdax_messenger, + d->global_index, + 0x00020144, + (d->silent_on_scsi_error == 3) ? + LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, + LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); + free(msg); + } + return BE_CANCELLED; + } + + buf->sectors = amount; + buf->bytes = amount * 2048; + return 0; +} + + +#ifdef Libburn_develop_quality_scaN + +/* B21108 ts : Vendor specific command REPORT ERROR RATE, see + http://liggydee.cdfreaks.com/ddl/errorcheck.pdf +*/ +int mmc_nec_optiarc_f3(struct burn_drive *d, int sub_op, + int start_lba, int rate_period, + int *ret_lba, int *error_rate1, int *error_rate2) +{ + struct buffer *buf = NULL; + struct command *c; + char *msg = NULL; + int key, asc, ascq, ret; + static unsigned char MMC_NEC_OPTIARC_F3[] = + { 0xF3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + mmc_start_if_needed(d, 0); + if (mmc_function_spy(d, "mmc_nec_optiarc_f3") <= 0) + return -1; + + scsi_init_command(c, MMC_NEC_OPTIARC_F3, sizeof(MMC_NEC_OPTIARC_F3)); + if (sub_op == 3) { + c->dxfer_len = 8; + c->dir = FROM_DRIVE; + } else { + c->dxfer_len = 0; + c->dir = NO_TRANSFER; + } + c->retry = 0; + c->opcode[1] = sub_op; + mmc_int_to_four_char(c->opcode + 2, start_lba); + c->opcode[8] = rate_period; + c->page = buf; + c->page->bytes = 0; + c->page->sectors = 0; + d->issue_command(d, c); + if (c->error) { + msg = calloc(1, 256); + if (msg != NULL) { + sprintf(msg, + "SCSI error on nec_optiarc_f3(%d, %d, %d): ", + sub_op, start_lba, rate_period); + scsi_error_msg(d, c->sense, 14, msg + strlen(msg), + &key, &asc, &ascq); + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020144, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + free(msg); + } + return BE_CANCELLED; + } + + if (sub_op == 3) { + *ret_lba = mmc_four_char_to_int(c->page->data); + *error_rate1 = c->page->data[4] * 256 + c->page->data[5]; + *error_rate2 = c->page->data[6] * 256 + c->page->data[7]; + } + + ret = 1; +ex:; + BURN_FREE_MEM(c); + BURN_FREE_MEM(buf); + return ret; +} + +#endif /* Libburn_develop_quality_scaN */ + + +/* ts A81210 : Determine the upper limit of readable data size */ +int mmc_read_capacity(struct burn_drive *d) +{ + struct buffer *buf = NULL; + struct command *c = NULL; + int alloc_len= 8, ret; + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + d->media_read_capacity = 0x7fffffff; + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "mmc_read_capacity") <= 0) + {ret = 0; goto ex;} + + scsi_init_command(c, MMC_READ_CAPACITY, sizeof(MMC_READ_CAPACITY)); + c->dxfer_len = alloc_len; + c->retry = 1; + c->page = buf; + c->page->bytes = 0; + c->page->sectors = 0; + c->dir = FROM_DRIVE; + d->issue_command(d, c); + d->media_read_capacity = mmc_four_char_to_int(c->page->data); + if (d->media_read_capacity < 0) { + d->media_read_capacity = 0x7fffffff; + {ret = 0; goto ex;} + } + ret = 1; +ex:; + BURN_FREE_MEM(c); + BURN_FREE_MEM(buf); + return ret; +} + + +/* ts A90903 */ +/* mmc5r03c.pdf 6.23 ADh READ DISC STRUCTURE obtains media specific information +*/ +static int mmc_read_disc_structure_al(struct burn_drive *d, int *alloc_len, + int media_type, int layer_number, int format, + int min_len, char **reply, int *reply_len, + int flag) +{ + struct buffer *buf = NULL; + int old_alloc_len, len, ret; + struct command *c = NULL; + unsigned char *dpt; + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + *reply = NULL; + *reply_len = 0; + + if (*alloc_len < 4) + {ret = 0; goto ex;} + + scsi_init_command(c, MMC_READ_DISC_STRUCTURE, + sizeof(MMC_READ_DISC_STRUCTURE)); + c->dxfer_len = *alloc_len; + c->retry = 1; + c->opcode[1]= media_type; + c->opcode[7]= format; + c->opcode[8]= (c->dxfer_len >> 8) & 0xff; + c->opcode[9]= 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) + {ret = 0; goto ex;} + + len = (c->page->data[0] << 8) | (c->page->data[1]); + old_alloc_len = *alloc_len; + *alloc_len = len + 2; + if (old_alloc_len <= 4) + {ret = 1; goto ex;} + if (len + 2 > old_alloc_len) + len = old_alloc_len - 2; + if (len < 4) + {ret = 0; goto ex;} + + dpt = c->page->data + 4; + if (len - 2 < min_len) + {ret = 0; goto ex;} + *reply = calloc(len - 2, 1); + if (*reply == NULL) + {ret = 0; goto ex;} + *reply_len = len - 2; + memcpy(*reply, dpt, len - 2); + ret = 1; +ex:; + BURN_FREE_MEM(c); + BURN_FREE_MEM(buf); + return ret; +} + + +int mmc_read_disc_structure(struct burn_drive *d, + int media_type, int layer_number, int format, int min_len, + char **reply, int *reply_len, int flag) +{ + int alloc_len = 4, ret; + char msg[80]; + + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "mmc_read_disc_structure") <= 0) + return 0; + + ret = mmc_read_disc_structure_al(d, &alloc_len, + media_type, layer_number, format, min_len, + reply, reply_len, 0); +/* + fprintf(stderr,"LIBBURN_DEBUG: ADh alloc_len = %d , ret = %d\n", + alloc_len, ret); +*/ + if (ret <= 0) + return ret; + if (alloc_len < 12) { + sprintf(msg, + "READ DISC STRUCTURE announces only %d bytes of reply\n", + alloc_len); + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + ret = 0; + +/* ts A91205 + LG GH22LS30 revision 1.00 returns for DVD-R format + code 0x0E an allocation length of 4 (= 0 payload). + A MS-Windows tool can inquire media code "RITEKF1", + though. + This macro causes a try to unconditionally read the + desired payload bytes. The drive then returns 35 + bytes as requested and the media id is "RITEKF1". + Nevertheless this is not a generally usable gesture + because older GNU/Linux USB dislikes requests to fetch + more bytes than the drive will deliver. + + # define Libburn_enforce_structure_code_0x0E 1 +*/ + +#ifdef Libburn_enforce_structure_code_0x0E + if (format == 0x0E) { + alloc_len = min_len + 4; + ret = mmc_read_disc_structure_al(d, &alloc_len, + media_type, layer_number, format, min_len, + reply, reply_len, 0); + if (*reply_len < min_len || *reply == NULL) + ret = 0; + sprintf(msg, "READ DISC STRUCTURE returns %d bytes of required %d\n", + *reply_len + 4, min_len + 4); + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + } +#endif + + } else + ret = mmc_read_disc_structure_al(d, &alloc_len, + media_type, layer_number, format, min_len, + reply, reply_len, 0); + return ret; +} + +/* ts A90903 */ +/* + @param flag bit0= set bit1 in flag for burn_util_make_printable_word + and do not append media revision +*/ +static int mmc_set_product_id(char *reply, + int manuf_idx, int type_idx, int rev_idx, + char **product_id, char **media_code1, char **media_code2, int flag) +{ + int ret; + + *product_id = calloc(17, 1); + *media_code1 = calloc(9, 1); + *media_code2 = calloc(8, 1); + if (*product_id == NULL || + *media_code1 == NULL || *media_code2 == NULL) + return -1; + sprintf(*media_code1, "%.8s", reply + manuf_idx); + ret = burn_util_make_printable_word(media_code1, + 1 | ((flag & 1) << 1)); + if (ret <= 0) + return -1; + sprintf(*media_code2, "%.3s%s", reply + type_idx, + (flag & 1) ? "" : "xxxx"); + ret = burn_util_make_printable_word(media_code2, + 1 | ((flag & 1) << 1)); + if (ret <= 0) + return -1; + if (!(flag & 1)) { + sprintf(*media_code2 + strlen(*media_code2) - 4, "/%d", + (int) ((unsigned char *) reply)[rev_idx]); + } + sprintf(*product_id, "%s/%s", *media_code1, *media_code2); + return 1; +} + + +/* ts A90903 */ +/* MMC backend of API call burn_disc_get_media_id() + See also doc/mediainfo.txt + @param flag Bitfield for control purposes + bit0= do not escape " _/" (not suitable for + burn_guess_manufacturer()) + +*/ +int mmc_get_media_product_id(struct burn_drive *d, + char **product_id, char **media_code1, char **media_code2, + char **book_type, int flag) +{ + int prf, ret, reply_len, i, has_11h = -1, bt, start_lba, end_lba; + int min, sec, fr, media_type = 0; + char *reply = NULL, *wpt; + + static char *books[16] = { + "DVD-ROM", "DVD-RAM", "DVD-R", "DVD-RW", + "HD DVD-ROM", "HD DVD-RAM", "HD DVD-R", "unknown", + "unknown", "DVD+RW", "DVD+R", "unknown", + "unknown", "DVD+RW DL" "DVD+R DL", "unknown"}; + + *product_id = *media_code1 = *media_code2 = *book_type = NULL; + prf = d->current_profile; + if (prf == 0x09 || prf == 0x0A) { + + *product_id = calloc(20, 1); + *media_code1 = calloc(10, 1); + *media_code2 = calloc(10, 1); + if (*product_id == NULL || + *media_code1 == NULL || *media_code2 == NULL) { + ret = -1; + goto ex; + } + ret = burn_disc_read_atip(d); + if (ret <= 0) + goto ex; + ret = burn_drive_get_start_end_lba(d, &start_lba, &end_lba, 0); + if (ret <= 0) + goto ex; + burn_lba_to_msf(start_lba, &min, &sec, &fr); + sprintf(*media_code1, "%2.2dm%2.2ds%2.2df", min, sec, fr); + burn_lba_to_msf(end_lba, &min, &sec, &fr); + sprintf(*media_code2, "%2.2dm%2.2ds%2.2df", min, sec, fr); + sprintf(*product_id, "%s/%s", *media_code1, *media_code2); + ret = 1; + goto ex; /* No booktype with CD media */ + + } else if (prf == 0x11 || prf == 0x13 || prf == 0x14 || prf == 0x15) { + /* DVD-R */ + + ret = mmc_read_disc_structure(d, 0, 0, 0x0E, 31, &reply, + &reply_len, 0); + if (ret <= 0) + goto ex; + /* ECMA-279 for DVD-R promises a third sixpack in field 5, + but ECMA-338 for DVD-RW defines a different meaning. + DVD-R and DVD-RW bear unprintable characters in there. + */ + if (reply[16] != 3 || reply[24] != 4) { + ret = 0; + goto ex; + } + *media_code1 = calloc(19, 1); + *media_code2 = strdup(""); + if (*media_code1 == NULL || *media_code2 == NULL) { + ret = -1; + goto ex; + } + memcpy(*media_code1, reply + 17, 6); + memcpy(*media_code1 + 6, reply + 25, 6); + + /* Clean out 0 bytes */ + wpt = *media_code1; + for (i = 0; i < 18; i++) + if ((*media_code1)[i]) + *(wpt++) = (*media_code1)[i]; + *wpt = 0; + ret = burn_util_make_printable_word(media_code1, + 1 | ((flag & 1) << 1)); + if (ret <= 0) + goto ex; + *product_id = strdup(*media_code1); + if (*product_id == NULL) { + ret = -1; + goto ex; + } + + } else if (prf == 0x1a || prf == 0x1b || prf == 0x2b) { /* DVD+R[W] */ + + /* Check whether the drive supports format 11h */ + has_11h = 0; + ret = mmc_read_disc_structure(d, 0, 0, 0xff, 4, &reply, + &reply_len, 0); + if (ret > 0) { + for (i = 0; i < reply_len; i += 4) { + if (reply[i] == 0x11 && (reply[i + 1] & 64)) + has_11h = 1; + } + } + if (reply != NULL) + free(reply); + reply = NULL; + ret = mmc_read_disc_structure(d, 0, 0, 0x11, 29, &reply, + &reply_len, 0); + if (ret <= 0) { + /* Hope for format 00h */ + has_11h = 0; + } else { + /* Dig out manufacturer, media type and revision */ + ret = mmc_set_product_id(reply, 19, 27, 28, + product_id, media_code1, media_code2, + flag & 1); + if (ret <= 0) + goto ex; + } + } else if (prf == 0x41 || prf == 0x43 || prf == 0x40 || prf == 0x42) { + /* BD */ + media_type = 1; + ret = mmc_read_disc_structure(d, 1, 0, 0x00, 112, &reply, + &reply_len, 0); + if (ret <= 0) + goto ex; + if (reply[0] != 'D' || reply[1] != 'I') { + ret = 0; + goto ex; + } + /* Dig out manufacturer, media type and revision */ + ret = mmc_set_product_id(reply, 100, 106, 111, + product_id, media_code1, media_code2, + flag & 1); + if (ret <= 0) + goto ex; + + } else { + + /* Source of DVD-RAM manufacturer and media id not found yet */ + ret = 0; + goto ex; + } + + if (reply != NULL) + free(reply); + reply = NULL; + ret = mmc_read_disc_structure(d, media_type, 0, 0x00, 1, + &reply, &reply_len, 0); + if (ret <= 0) + goto ex; + bt = (reply[0] >> 4) & 0xf; + *book_type = calloc(80 + strlen(books[bt]), 1); + if (*book_type == NULL) { + ret = -1; + goto ex; + } + sprintf(*book_type, "%2.2Xh, %s book [revision %d]", + bt, books[bt], reply[0] & 0xf); + + if (has_11h == 0 && *product_id == NULL && reply_len > 28) { + /* DVD+ with no format 11h */ + /* Get manufacturer and media type from bytes 19 and 27 */ + ret = mmc_set_product_id(reply, 19, 27, 28, product_id, + media_code1, media_code2, + flag & 1); + if (*product_id == NULL) { + ret = 0; + goto ex; + } + } + + ret = 1; +ex:; + if (reply != NULL) + free(reply); + if (ret <= 0) { + if (*product_id != NULL) + free(*product_id); + if (*media_code1 != NULL) + free(*media_code1); + if (*media_code2 != NULL) + free(*media_code2); + if (*book_type != NULL) + free(*book_type); + *product_id = *media_code1 = *media_code2 = *book_type = NULL; + } + return ret; +} + + +/* ts B00924 + MMC-5, 6.23.3.3.4 Format Code 0Ah: Spare Area Information +*/ +int mmc_get_bd_spare_info(struct burn_drive *d, + int *alloc_blocks, int *free_blocks, int flag) +{ + int ret, reply_len, prf; + char *reply = NULL; + + prf = d->current_profile; + if (!(prf == 0x41 || prf == 0x43 || prf == 0x42)) + return 0; /* Not a BD loaded */ + + ret = mmc_read_disc_structure(d, 1, 0, 0x0a, 12, &reply, + &reply_len, 0); + if (ret <= 0) + goto ex; + *alloc_blocks = mmc_four_char_to_int((unsigned char *) reply + 8); + *free_blocks = mmc_four_char_to_int((unsigned char *) reply + 4); + ret = 1; +ex:; + if (reply != NULL) + free(reply); + return ret; +} + + +/* ts B10801 + MMC-5, 6.23.3.2.1 Format Code 00h: Physical Format Information + 6.23.3.2.16 Format Code 10h: Format Information of + Control Data Zone in the Lead-in + disk_category +*/ +int mmc_get_phys_format_info(struct burn_drive *d, int *disk_category, + char **book_name, int *part_version, int *num_layers, + int *num_blocks, int flag) +{ + int ret, reply_len, prf; + char *reply = NULL; + static char book_names[][16] = { + "DVD-ROM", "DVD-RAM", "DVD-R", "DVD-RW", + "HD DVD-ROM", "HD DVD-RAM", "HD DVD-R", "unknown", + "unknown", "DVD+RW", "DVD+R", "unknown", "unknown", + "unknown", "DVD+RW DL", "DVD+R DL", "unknown" + }; + + prf = d->current_profile; + if (!(prf == 0x11 || prf == 0x13 || prf == 0x14 || prf == 0x15 || + prf == 0x51)) + return 0; /* Not a [HD] DVD-R[W] loaded */ + ret = mmc_read_disc_structure(d, 0, 0, 0x10, 12, &reply, + &reply_len, 0); + if (ret <= 0) + goto ex; + if(reply_len < 12) { + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + "READ DISC STRUCTURE format 10h: Less than 12 bytes", + 0, 0); + {ret = 0; goto ex;} + } + *disk_category = (reply[0] >> 4) & 0xf; + *book_name = book_names[*disk_category]; + *part_version = reply[0] & 0xf; + *num_layers = ((reply[2] >> 5) & 0x3) + 1; + *num_blocks = ((reply[9] << 16) | (reply[10] << 8) | reply[11]) - + ((reply[5] << 16) | (reply[6] << 8) | reply[7]) + 1; + ret = 1; +ex:; + if (reply != NULL) + free(reply); + return ret; +} + + + +/* 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_cd = mmc_read_cd; + 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 - A90815*/ + 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_is_guessed_profile = 0; + memset(d->all_profiles, 0, 256); + d->num_profiles = 0; + d->current_has_feat21h = 0; + d->current_feat21h_link_size = -1; + d->current_feat23h_byte4 = 0; + d->current_feat23h_byte8 = 0; + d->current_feat2fh_byte4 = -1; + d->next_track_damaged = 0; + d->needs_close_session = 0; + d->needs_sync_cache = 0; + d->bg_format_status = -1; + d->num_opc_tables = -1; + d->last_lead_in = -2000000000; + d->last_lead_out = -2000000000; + d->disc_type = 0xff; + d->disc_id = 0; + memset(d->disc_bar_code, 0, 9); + d->disc_app_code = 0; + d->disc_info_valid = 0; + d->num_format_descr = 0; + d->complete_sessions = 0; + +#ifdef Libburn_disc_with_incomplete_sessioN + d->incomplete_sessions = 0; +#endif + + d->state_of_last_session = -1; + d->last_track_no = 1; + d->media_capacity_remaining = 0; + d->media_lba_limit = 0; + d->media_read_capacity = 0x7fffffff; + 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; + d->sent_default_page_05 = 0; + + return 1; +} + diff --git a/trunk/libburn/mmc.h b/trunk/libburn/mmc.h new file mode 100644 index 0000000..9bf8c49 --- /dev/null +++ b/trunk/libburn/mmc.h @@ -0,0 +1,141 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + +#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 *); +int mmc_read_cd(struct burn_drive *d, int start, int len, + int sec_type, int main_ch, + const struct burn_read_opts *o, struct buffer *buf, int flag); +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); + +/* ts B11228 : changed from void to int */ +int 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, + struct burn_session *s, int tno, + const struct burn_write_opts *o, + unsigned char *pd); + +/* ts A70201 */ +int mmc_four_char_to_int(unsigned char *data); + +/* 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); + +/* 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); + +/* ts A81210 : Determine the upper limit of readable data size */ +int mmc_read_capacity(struct burn_drive *d); + +/* ts A61201 */ +char *mmc_obtain_profile_name(int profile_number); + + +/* mmc5r03c.pdf 4.3.4.4.1 d) "The maximum number of RZones is 2 302." */ +#define BURN_MMC_FAKE_TOC_MAX_SIZE 2302 + + +/* ts A90903 */ +/* MMC backend of API call burn_get_media_product_id() +*/ +int mmc_get_media_product_id(struct burn_drive *d, + char **product_id, char **media_code1, char **media_code2, + char **book_type, int flag); + + +/* ts A60910 (estimated) */ +int mmc_function_spy(struct burn_drive *d, char * text); + +/* ts A91118 */ +int mmc_start_if_needed(struct burn_drive *d, int flag); + +/* ts B00924 */ +int mmc_get_bd_spare_info(struct burn_drive *d, + int *alloc_blocks, int *free_blocks, int flag); + +/* ts B10801 */ +int mmc_get_phys_format_info(struct burn_drive *d, int *disk_category, + char **book_name, int *part_version, int *num_layers, + int *num_blocks, int flag); + +/* ts B11201 */ +int mmc_get_leadin_text(struct burn_drive *d, + unsigned char **text_packs, int *num_packs, int flag); + +/* ts B40107 */ +int mmc_get_performance(struct burn_drive *d, int descr_type, int flag); + + +#ifdef Libburn_develop_quality_scaN +/* B21108 ts */ +int mmc_nec_optiarc_f3(struct burn_drive *d, int sub_op, + int start_lba, int rate_period, + int *eba, int *error_rate1, int *error_rate2); +#endif + +#endif /*__MMC*/ diff --git a/trunk/libburn/null.c b/trunk/libburn/null.c new file mode 100644 index 0000000..71e102e --- /dev/null +++ b/trunk/libburn/null.c @@ -0,0 +1,40 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Provided under GPL version 2 or later. +*/ + + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#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 = calloc(1, 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/trunk/libburn/null.h b/trunk/libburn/null.h new file mode 100644 index 0000000..1a7aae3 --- /dev/null +++ b/trunk/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/trunk/libburn/options.c b/trunk/libburn/options.c new file mode 100644 index 0000000..ca88ea9 --- /dev/null +++ b/trunk/libburn/options.c @@ -0,0 +1,588 @@ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2012 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#include "libburn.h" +#include "options.h" +#include "drive.h" +#include "transport.h" +#include "init.h" +#include "write.h" + +/* ts A61007 */ +/* #include <a ssert.h> */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.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 = calloc(1, 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->p2a_valid > 0 && + drive->mdata->underrun_proof; + opts->perform_opc = 1; + opts->obs = -1; + +#ifdef Libburn_dvd_always_obs_paD + opts->obs_pad = 1; +#else + opts->obs_pad = 0; +#endif + + opts->start_byte = -1; + opts->fill_up_media = 0; + opts->force_is_set = 0; + opts->do_stream_recording = 0; + opts->dvd_obs_override = 0; + opts->stdio_fsync_size = Libburn_stdio_fsync_limiT; + opts->text_packs = NULL; + opts->num_text_packs = 0; + opts->no_text_pack_crc_check = 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) + return; + if (opts->text_packs != NULL) + free(opts->text_packs); + free(opts); +} + +int burn_write_opts_clone(struct burn_write_opts *from, + struct burn_write_opts **to, int flag) +{ + if (*to != NULL) + burn_write_opts_free(*to); + if (from == NULL) + return 1; + *to = calloc(1, sizeof(struct burn_write_opts)); + if (*to == NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00000003, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Out of virtual memory", 0, 0); + return -1; + } + memcpy(*to, from, sizeof(struct burn_write_opts)); + (*to)->refcount= 1; + return 1; +} + +struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive) +{ + struct burn_read_opts *opts; + + opts = calloc(1, 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; + opts->dap_bit = 0; + + 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 = calloc(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) +{ + opts->simulate = !!sim; + return 1; +} + +int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts, + int underrun_proof) +{ + if (opts->drive->mdata->p2a_valid <= 0 || + 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 B31024 */ +/* API */ +void burn_write_opts_set_fail21h_sev(struct burn_write_opts *opts, + char *severity) +{ + int ret, sevno; + + ret = libdax_msgs__text_to_sev(severity, &sevno, 0); + if (ret <= 0) + opts->feat21h_fail_sev = 0; + else + opts->feat21h_fail_sev = sevno; +} + + +/* ts B11204 */ +/* @param flag bit0=do not verify checksums + bit1= repair mismatching checksums + bit2= repair checksums if they are 00 00 with each pack +*/ +int burn_write_opts_set_leadin_text(struct burn_write_opts *opts, + unsigned char *text_packs, + int num_packs, int flag) +{ + int ret; + unsigned char *pack_buffer = NULL; + + if (num_packs > Libburn_leadin_cdtext_packs_maX ) { + libdax_msgs_submit(libdax_messenger, opts->drive->global_index, + 0x0002018b, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Too many CD-TEXT packs", 0, 0); + ret= 0; goto ex; + } + + if (num_packs > 0) + BURN_ALLOC_MEM(pack_buffer, unsigned char, num_packs * 18); + + if (opts->text_packs != NULL) { + free(opts->text_packs); + opts->text_packs = NULL; + } + + if (flag & 1) { + opts->no_text_pack_crc_check = 1; + } else { + opts->no_text_pack_crc_check = 0; + ret = burn_cdtext_crc_mismatches(text_packs, num_packs, + (flag >> 1) & 3); + if (ret > 0) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002018f, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "CD-TEXT pack CRC mismatch", 0, 0); + ret = 0; goto ex; + } else if (ret < 0) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020190, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + "CD-TEXT pack CRC mismatch had to be corrected", + 0, 0); + } + } + + if (num_packs > 0) { + memcpy(pack_buffer, text_packs, num_packs * 18); + opts->text_packs = pack_buffer; + } + opts->num_text_packs = num_packs; + ret = 1; +ex:; + return ret; +} + + +/* 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 (opts->num_text_packs > 0) { + strcat(reasons, "CD-TEXT: write type SAO required, "); + {wt = BURN_WRITE_NONE; goto ex;} + } + 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 A80412: API */ +void burn_write_opts_set_stream_recording(struct burn_write_opts *opts, + int value) +{ + opts->do_stream_recording = value; +} + + +/* ts A91115: API */ +void burn_write_opts_set_dvd_obs(struct burn_write_opts *opts, int obs) +{ + if (obs != 0 && obs != 32 * 1024 && obs != 64 * 1024) + return; + opts->dvd_obs_override = obs; +} + + +/* ts B20406: API */ +void burn_write_opts_set_obs_pad(struct burn_write_opts *opts, int pad) +{ + opts->obs_pad = 2 * !!pad; +} + + +/* ts A91115: API */ +void burn_write_opts_set_stdio_fsync(struct burn_write_opts *opts, int rythm) +{ + if (rythm == -1) + opts->stdio_fsync_size = -1; /* never */ + else if (rythm == 0) + opts->stdio_fsync_size = Libburn_stdio_fsync_limiT; + else if (rythm == 1) + opts->stdio_fsync_size = 0; /* only at end of writing */ + else if (rythm >= 32) + opts->stdio_fsync_size = rythm; +} + + +/* 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/trunk/libburn/options.h b/trunk/libburn/options.h new file mode 100644 index 0000000..1512079 --- /dev/null +++ b/trunk/libburn/options.h @@ -0,0 +1,157 @@ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2013 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#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; /* >0 pad up last block to obs, 0 do not + 2 indicates burn_write_opts_set_obs_pad(,1) + */ + + /* 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; + + /* ts A80412 : whether to use WRITE12 with Streaming bit set + rather than WRITE10. Speeds up DVD-RAM. Might help with BD-RE. + This gets transferred to burn_drive.do_stream_recording + */ + int do_stream_recording; + + /* ts A91115 : override value for .obs on DVD media. + Only values 0, 32K and 64K are allowed for now. */ + int dvd_obs_override; + + /* ts A91115 : size of the fsync() interval for stdio writing. + Values 0 or >= 32 counted in 2 KB blocks. */ + int stdio_fsync_size; + + /* ts B11203 : CD-TEXT */ + unsigned char *text_packs; + int num_text_packs; + int no_text_pack_crc_check; + + /** 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; + + /* Whether to keep medium appendable */ + unsigned char multi; + + /* ts B31024 */ + /* The severity to be attributed to error messages about failed + write attempt with blank DVD-RW, possibly due to falsely reported + feature 21h Incremental Streaming Writable + */ + int feat21h_fail_sev; +}; + +/* Default value for burn_write_opts.stdio_flush_size +*/ +#define Libburn_stdio_fsync_limiT 8192 + +/* Maximum number of Lead-in text packs. + READ TOC/PMA/ATIP can at most return 3640.7 packs. + The sequence counters of the packs have 8 bits. There are 8 blocks at most. + Thus max 2048 packs. + */ +#define Libburn_leadin_cdtext_packs_maX 2048 + + +/** 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; + + /* ts B21119 */ + /* >>> Needs API access */ + /** Whether to set DAP bit which allows drive to apply + "flaw obscuring mechanisms like audio data mute and interpolate" + */ + unsigned int dap_bit; + +}; + + +int burn_write_opts_clone(struct burn_write_opts *from, + struct burn_write_opts **to, int flag); + + +#endif /* BURN__OPTIONS_H */ diff --git a/trunk/libburn/os-dummy.h b/trunk/libburn/os-dummy.h new file mode 100644 index 0000000..8722fe2 --- /dev/null +++ b/trunk/libburn/os-dummy.h @@ -0,0 +1,88 @@ + +/* os-dummy.h + Operating system specific libburn definitions and declarations. Included + by os.h in case of compilation for + Unknown POSIX like systems + with the dummy MMC transport adapter sg-dummy.c + + Copyright (C) 2009 - 2013 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPLv2+ +*/ + + +/** 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 + +/* 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" + +/* The number of above list items */ +#define BURN_OS_SIGNAL_COUNT 13 + +/** The list of all signals which shall surely not be caught. + It depends on the particular signal whether it can be ignored or whether + it will lead to sudden death of the process. + Some signals are not POSIX, + but nevertheless ought to be ignored if they are defined. +*/ + +#ifdef SIGWINCH +#define BURN_OS_SIG_WINCH ,SIGWINCH +#define BURN_OS_SIG_WINCH_CNT 1 +#else +#define BURN_OS_SIG_WINCH +#define BURN_OS_SIG_WINCH_CNT 0 +#endif + +#ifdef SIGURG +#define BURN_OS_SIG_URG ,SIGURG +#define BURN_OS_SIG_URG_CNT 1 +#else +#define BURN_OS_SIG_URG +#define BURN_OS_SIG_URG_CNT 0 +#endif + +/** The combined list of all signals which shall not be caught. + */ +#define BURN_OS_NON_SIGNAL_MACRO_LIST \ +SIGKILL, SIGCHLD, SIGSTOP, SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU \ +BURN_OS_SIG_WINCH BURN_OS_SIG_URG + +/* The number of above list items */ +#define BURN_OS_NON_SIGNAL_COUNT \ +( 7 + BURN_OS_SIG_WINCH_CNT + BURN_OS_SIG_URG_CNT ) + + +/* The maximum size for a (SCSI) i/o transaction */ +/* Important : MUST be at least 32768 ! */ +#define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 + + +/* To hold the position of the most recently delivered address from + device enumeration. +*/ +struct burn_drive_enumerator_struct { + int pos; + int info_count; + char **info_list; +}; + +#define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \ +typedef struct burn_drive_enumerator_struct burn_drive_enumerator_t; + + +/* The list of operating system dependent elements in struct burn_drive. + Usually they are initialized in sg-*.c:enumerate_common(). +*/ +#define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \ + int just_a_dummy; + diff --git a/trunk/libburn/os-freebsd.h b/trunk/libburn/os-freebsd.h new file mode 100644 index 0000000..a3fa876 --- /dev/null +++ b/trunk/libburn/os-freebsd.h @@ -0,0 +1,65 @@ + +/* 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 - 2013 Thomas Schmitt <scdbackup@gmx.net>, + Provided under GPLv2+ +*/ + +/** 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, 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", "SIGBUS", "SIGPROF", \ + "SIGSYS", "SIGTRAP", "SIGVTALRM", "SIGXCPU", "SIGXFSZ" + +/* The number of above list items */ +#define BURN_OS_SIGNAL_COUNT 20 + +/** To list all signals which shall surely not be caught */ +#define BURN_OS_NON_SIGNAL_MACRO_LIST \ +SIGKILL, SIGCHLD, SIGSTOP, SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU, SIGURG, SIGWINCH + +/* The number of above list items */ +#define BURN_OS_NON_SIGNAL_COUNT 9 + + +/* The maximum size for a (SCSI) i/o transaction */ +/* Important : MUST be at least 32768 ! */ +/* Older BSD info says that 32 kB is maximum. But 64 kB seems to work well + on 8-STABLE. It is by default only used with BD in streaming mode. + So older systems should still be quite safe with this buffer max size. +*/ +#define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 + + +/** 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; \ +int lock_fd; \ +int is_ahci; \ + + diff --git a/trunk/libburn/os-libcdio.h b/trunk/libburn/os-libcdio.h new file mode 100644 index 0000000..c21203c --- /dev/null +++ b/trunk/libburn/os-libcdio.h @@ -0,0 +1,95 @@ + +/* os-libcdio.h + Operating system specific libburn definitions and declarations. Included + by os.h in case of compilation for + Unknown X/Open-like systems + with GNU libcdio MMC transport adapter sg-libcdio.c + + Copyright (C) 2009 - 2013 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPLv2+ +*/ + + +/** 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 + +/* 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" + +/* The number of above list items */ +#define BURN_OS_SIGNAL_COUNT 13 + + +/** The list of all signals which shall surely not be caught. + It depends on the particular signal whether it can be ignored or whether + it will lead to sudden death of the process. + Some signals are not POSIX, + but nevertheless ought to be ignored if they are defined. +*/ + +#ifdef SIGWINCH +#define BURN_OS_SIG_WINCH ,SIGWINCH +#define BURN_OS_SIG_WINCH_CNT 1 +#else +#define BURN_OS_SIG_WINCH +#define BURN_OS_SIG_WINCH_CNT 0 +#endif + +#ifdef SIGURG +#define BURN_OS_SIG_URG ,SIGURG +#define BURN_OS_SIG_URG_CNT 1 +#else +#define BURN_OS_SIG_URG +#define BURN_OS_SIG_URG_CNT 0 +#endif + +/** The combined list of all signals which shall not be caught. + */ +#define BURN_OS_NON_SIGNAL_MACRO_LIST \ +SIGKILL, SIGCHLD, SIGSTOP, SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU \ +BURN_OS_SIG_WINCH BURN_OS_SIG_URG + +/* The number of above list items */ +#define BURN_OS_NON_SIGNAL_COUNT \ +( 7 + BURN_OS_SIG_WINCH_CNT + BURN_OS_SIG_URG_CNT ) + + +/* The maximum size for a (SCSI) i/o transaction */ +/* My Blu-ray burner LG GGW-H20 writes junk if stream recording is combined + with buffer size 32 kB. So stream recording is allowed only with size 64k. + Older BSD info says that 32 kB is maximum. But 64 kB seems to work well + on 8-STABLE. It is by default only used with BD in streaming mode. + So older systems should still be quite safe with this buffer max size. +*/ +/* Important : MUST be at least 32768 ! */ +#define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 + + +/* To hold the position of the most recently delivered address from + device enumeration. +*/ +struct burn_drive_enumerator_struct { + char **ppsz_cd_drives; + char **pos; +}; + +#define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \ +typedef struct burn_drive_enumerator_struct burn_drive_enumerator_t; + + +/* The list of operating system dependent elements in struct burn_drive. + Usually they are initialized in sg-*.c:enumerate_common(). +*/ +#define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \ + void *p_cdio; /* actually a pointer to CdIo_t */ \ + char libcdio_name[4096]; /* The drive path as used by libcdio */ \ + diff --git a/trunk/libburn/os-linux.h b/trunk/libburn/os-linux.h new file mode 100644 index 0000000..b77d1e8 --- /dev/null +++ b/trunk/libburn/os-linux.h @@ -0,0 +1,81 @@ + +/* 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, GNU/Linux SCSI Generic (sg) + + Copyright (C) 2006 - 2013 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + +/** 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, 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", "SIGBUS", "SIGPOLL", \ + "SIGPROF", "SIGSYS", "SIGTRAP", "SIGVTALRM", "SIGXCPU", \ + "SIGXFSZ" + +/* The number of above list items */ +#define BURN_OS_SIGNAL_COUNT 21 + +/** To list all signals which shall surely not be caught */ +#define BURN_OS_NON_SIGNAL_MACRO_LIST \ +SIGKILL, SIGCHLD, SIGSTOP, SIGTSTP, SIGCONT, SIGURG, SIGWINCH, SIGTTIN, SIGTTOU + +/* The number of above list items */ +#define BURN_OS_NON_SIGNAL_COUNT 9 + + +/* 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 32768 +*/ +/* ts A80414 : curbed in write.c CD media to Libburn_cd_obS = 32 kiB + re-enlarged transport to 64 kiB for BD-RE experiments +*/ +#define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 + + +/* To hold the position of the most recently delivered address from + device enumeration. +*/ +struct burn_drive_enumerator_struct { + int pos; + int info_count; + char **info_list; +}; + +#define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \ +typedef struct burn_drive_enumerator_struct 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/trunk/libburn/os-netbsd.h b/trunk/libburn/os-netbsd.h new file mode 100644 index 0000000..fc38fdf --- /dev/null +++ b/trunk/libburn/os-netbsd.h @@ -0,0 +1,71 @@ + +/* os-netbsd.h + Operating system specific libburn definitions and declarations. Included + by os.h in case of compilation for + NetBSD 6 + with MMC transport adapter sg-netbsd.c + >>> for OpenBSD too ? + + Copyright (C) 2010 - 2014 Thomas Schmitt <scdbackup@gmx.net> + provided under GPLv2+ + + Derived 2014 from libburn/os-solaris.c +*/ + + +/** List of all signals which shall be caught by signal handlers and trigger + a graceful abort of libburn. (See man signal.h) +*/ +/* Once as system defined macros */ +#define BURN_OS_SIGNAL_MACRO_LIST \ + SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, \ + SIGABRT, SIGEMT, SIGFPE, SIGBUS, SIGSEGV, \ + SIGSYS, SIGPIPE, SIGALRM, SIGTERM, SIGXCPU, \ + SIGXFSZ, SIGVTALRM, SIGPROF, SIGUSR1, SIGUSR2 + +/* Once as text 1:1 list of strings for messages and interpreters */ +#define BURN_OS_SIGNAL_NAME_LIST \ + "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", \ + "SIGABRT", "SIGEMT", "SIGFPE", "SIGBUS", "SIGSEGV", \ + "SIGSYS", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGXCPU", \ + "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGUSR1", "SIGUSR2" + +/* The number of above list items */ +#define BURN_OS_SIGNAL_COUNT 20 + +/** To list all signals which shall surely not be caught */ +#define BURN_OS_NON_SIGNAL_MACRO_LIST \ + SIGKILL, SIGURG, SIGSTOP, SIGTSTP, SIGCONT, \ + SIGCHLD, SIGTTIN, SIGTTOU, SIGIO, SIGWINCH, \ + SIGINFO, SIGPWR + +/* The number of above list items */ +#define BURN_OS_NON_SIGNAL_COUNT 12 + + +/* The maximum size for a (SCSI) i/o transaction */ +/* Important : MUST be at least 32768 ! */ +/* My Blu-ray burner LG GGW-H20 writes junk if stream recording is combined + with buffer size 32 kB. So stream recording is allowed only with size 64k. +*/ +/* >>> ??? Does it do 64 kB ? */ +#define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 + + +/* To hold the position of the most recently delivered address from + device enumeration. +*/ +struct burn_drive_enumerator_struct { + int cdno; +}; + +#define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \ +typedef struct burn_drive_enumerator_struct burn_drive_enumerator_t; + + +/* The list of operating system dependent elements in struct burn_drive. + Usually they are initialized in sg-*.c:enumerate_common(). +*/ +#define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \ + int fd; + diff --git a/trunk/libburn/os-solaris.h b/trunk/libburn/os-solaris.h new file mode 100644 index 0000000..27330e6 --- /dev/null +++ b/trunk/libburn/os-solaris.h @@ -0,0 +1,65 @@ + +/* os-solaris.h + Operating system specific libburn definitions and declarations. Included + by os.h in case of compilation for + Solaris based systems, e.g. SunOS 5.11 + with Solaris uscsi MMC transport adapter sg-solaris.c + + Copyright (C) 2010 - 2013 Thomas Schmitt <scdbackup@gmx.net> + provided under GPLv2+ +*/ + + +/** List of all signals which shall be caught by signal handlers and trigger + a graceful abort of libburn. (See man signal.h) +*/ +/* Once as system defined macros */ +#define BURN_OS_SIGNAL_MACRO_LIST \ + SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \ + SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \ + SIGUSR1, SIGUSR2, SIGXCPU + +/* 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" + +/* The number of above list items */ +#define BURN_OS_SIGNAL_COUNT 13 + +/** To list all signals which shall surely not be caught */ +#define BURN_OS_NON_SIGNAL_MACRO_LIST \ +SIGKILL, SIGCHLD, SIGSTOP, SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU, SIGURG, SIGWINCH + +/* The number of above list items */ +#define BURN_OS_NON_SIGNAL_COUNT 9 + + +/* The maximum size for a (SCSI) i/o transaction */ +/* Important : MUST be at least 32768 ! */ +/* My Blu-ray burner LG GGW-H20 writes junk if stream recording is combined + with buffer size 32 kB. So stream recording is allowed only with size 64k. +*/ +#define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 + + +/* >>> */ + +/* To hold the position of the most recently delivered address from + device enumeration. +*/ +struct burn_drive_enumerator_struct { + void *dir; +}; + +#define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \ +typedef struct burn_drive_enumerator_struct burn_drive_enumerator_t; + + +/* The list of operating system dependent elements in struct burn_drive. + Usually they are initialized in sg-*.c:enumerate_common(). +*/ +#define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \ + int fd; + diff --git a/trunk/libburn/os.h b/trunk/libburn/os.h new file mode 100644 index 0000000..9ed5e15 --- /dev/null +++ b/trunk/libburn/os.h @@ -0,0 +1,97 @@ + +/* 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) 2009 - 2014 Thomas Schmitt <scdbackup@gmx.net>, + provided under GPLv2+ +*/ + +#ifndef BURN_OS_H_INCLUDED +#define BURN_OS_H_INCLUDED 1 + +/* + Operating system case distinction +*/ + + +/* <<< Until it is known whether this adapter would work on OpenBSD too */ +#ifdef __NetBSD__ +#define Libburn_use_sg_netbsD +#endif + + +#ifdef Libburn_use_sg_dummY + + +/* --------- Any other system. With dummy MMC transport sg-dummy.c --------- */ +#include "os-dummy.h" + + +#else +#ifdef Libburn_use_libcdiO + + +/* -------------------------- X/Open with GNU libcdio ---------------------- */ +#include "os-libcdio.h" + + +#else +#ifdef Libburn_use_sg_netbsD +/* To become: # ifdef __NetBSD__ */ + + +/* -------------------------- NetBSD with SCIOCCOMMAND --------------------- */ +#include "os-netbsd.h" + + +#else +#ifdef __FreeBSD__ + + +/* ----------------------------- FreeBSD with CAM -------------------------- */ +#include "os-freebsd.h" + + +#else +#ifdef __FreeBSD_kernel__ + + +/* ----------------------- FreeBSD with CAM under Debian ------------------- */ +#include "os-freebsd.h" + + +#else +#ifdef __linux + + +/* ------- Linux kernels 2.4 and 2.6 with GNU/Linux SCSI Generic (sg) ------ */ +#include "os-linux.h" + + +#else +#ifdef __sun + + +/* ------- Solaris (e.g. SunOS 5.11) with uscsi ------ */ +#include "os-solaris.h" + + +#else + + +/* --------- Any other system. With dummy MMC transport sg-dummy.c --------- */ +#include "os-dummy.h" + + +#endif /* ! __sun*/ +#endif /* ! __linux */ +#endif /* ! __FreeBSD__kernel__ */ +#endif /* ! __FreeBSD__ */ +#endif /* ! Libburn_use_sg_netbsD */ +#endif /* ! Libburn_use_libcdiO */ +#endif /* ! Libburn_use_sg_dummY */ + + +#endif /* ! BURN_OS_H_INCLUDED */ + diff --git a/trunk/libburn/read.c b/trunk/libburn/read.c new file mode 100644 index 0000000..b3ef220 --- /dev/null +++ b/trunk/libburn/read.c @@ -0,0 +1,778 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#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> + +/* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#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 "toc.h" +#include "util.h" +#include "mmc.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; <- needs to become dynamic memory + 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; + +#ifdef Libburn_no_crc_C + crc = 0; /* dummy */ +#else + crc = crc_ccitt(fakesub + 12, 10); +#endif + + fakesub[22] = crc >> 8; + fakesub[23] = crc & 0xFF; + write(o->subfd, fakesub, 96); + } + while (1) { + seclen = burn_sector_length_read(d, o); + + 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))) { + 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++; + /* session switch to d->currsession */ + /* skipping a lead out */ + drive_lba = CURRENT_SESSION_START(d); +/* XXX more of the same + end = burn_track_end(d, d->currsession, + 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 */ + /* >>> ts B21123 : Would now be d->read_cd() with + with sectype = 0 , mainch = 0xf8 */ + 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]; + int ptr = 2352, i, j, code, fb; + int audio = 1; +#ifndef Libburn_no_crc_C + unsigned short crc; +#endif + + if (o->c2errors) { + fb = bitcount(data + ptr, 294); + if (fb) { + /* bitcount(data + ptr, 294) damaged bits */; + } + 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]++; + } + } + } + +#ifndef Libburn_no_crc_C + 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"); +*/; + } +#endif + + /* 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) +{ + static char sec[2352], initialized = 0; + + if (!initialized) { + memset(sec, 0, 2352); + initialized = 1; + } + 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); +} +*/ + + +/** @param flag bit1= be silent on failure + bit5= report failure with severity DEBUG +*/ +static int burn_stdio_seek(int fd, off_t byte_address, struct burn_drive *d, + int flag) +{ + char msg[80]; + + if (lseek(fd, byte_address, SEEK_SET) != -1) + return 1; + if (!(flag & 2)) { + sprintf(msg, "Cannot address start byte %.f", + (double) byte_address); + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020147, + (flag & 32) ? + LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, + LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); + } + return 0; +} + + +/* ts A70904 */ +/** @param flag bit0= be silent on data shortage + bit5= report data shortage with severity DEBUG +*/ +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, + (flag & 32) ? + LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, + LIBDAX_MSGS_PRIO_HIGH, + "Cannot read desired amount of data", errno, 0); + } + if (count < 0) + return -1; + return (bufsize - todo); +} + + +/* With DVD and BD media, the minimum ECC entity is read instead of single + blocks. + @param flag see burn_read_data() in libburn.h +*/ +static int retry_mmc_read(struct burn_drive *d, int chunksize, int sose_mem, + int start, char **wpt, off_t *data_count, + int flag) +{ + int i, err, todo; + int retry_at, retry_size; + + retry_at = start; + retry_size = chunksize; + todo = chunksize; + retry_size = 16; /* DVD ECC block size */ + if (d->current_is_cd_profile) { + retry_size = 1; /* CD block size */ + } else if (d->current_profile >= 0x40 && d->current_profile <= 0x43) { + retry_size = 32; /* BD cluster size */ + } + for (i = 0; todo > 0; i++) { + if (flag & 2) + d->silent_on_scsi_error = 1; + else if (flag & 32) + d->silent_on_scsi_error = 3; + retry_at = start + i * retry_size; + if (retry_size > todo) + retry_size = todo; + err = d->read_10(d, retry_at, retry_size, d->buffer); + if (flag & (2 | 32)) + d->silent_on_scsi_error = sose_mem; + if (err == BE_CANCELLED) + return 0; + memcpy(*wpt, d->buffer->data, retry_size * 2048); + *wpt += retry_size * 2048; + *data_count += retry_size * 2048; + todo -= retry_size; + } + return 1; +} + + +/* @param flag see burn_read_data() in libburn.h +*/ +static int retry_stdio_read(struct burn_drive *d, int fd, int chunksize, + int start, char **wpt, off_t *data_count, + int flag) +{ + int i, ret, to_read, todo; + + ret = burn_stdio_seek(fd, ((off_t) start) * 2048, d, flag & 2); + if (ret <= 0) + return ret; + todo = chunksize * 2048; + for (i = 0; todo > 0; i += 2048) { + to_read = todo; + if (to_read > 2048) + to_read = 2048; + ret = burn_stdio_read(fd, (char *) d->buffer->data, to_read, + d, 1); + if (ret <= 0) + return 0; + memcpy(*wpt, d->buffer->data, to_read); + *wpt += to_read; + *data_count += to_read; + todo -= to_read; + } + return 1; +} + + +/* 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; + int sose_mem = 0, fd = -1, ret; + char msg[81], *wpt; + struct buffer *buf = NULL, *buffer_mem = d->buffer; + +/* +#define Libburn_read_data_adr_logginG 1 +*/ +#ifdef Libburn_read_data_adr_logginG + static FILE *log_fp= NULL; + + if(log_fp == NULL) + log_fp = fopen("/tmp/burn_read_data_log", "a"); + if(log_fp!=NULL) + fprintf(log_fp, "%d\n", (int) (byte_address / 2048)); +#endif /* Libburn_read_data_logginG */ + + BURN_ALLOC_MEM(buf, struct buffer, 1); + *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); + {ret = 0; goto ex;} + } + 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); + {ret = 0; goto ex;} + } else if (d->drive_role == 3) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020151, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Read attempt on write-only drive", 0, 0); + {ret = 0; goto ex;} + } + 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); + {ret = 0; goto ex;} + } + if (d->media_read_capacity != 0x7fffffff && byte_address >= + ((off_t) d->media_read_capacity + (off_t) 1) * (off_t) 2048) { + if (!(flag & 2)) { + sprintf(msg, + "Read start address %ds larger than number of readable blocks %d", + (int) (byte_address / 2048 + !!(byte_address % 2048)), + d->media_read_capacity); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020172, (flag & 32) ? + LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, + LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); + } + {ret = 0; goto ex;} + } + + 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); + {ret = 0; goto ex;} + } + + if (d->drive_role != 1) { + +/* <<< We need _LARGEFILE64_SOURCE defined by the build system. +*/ +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + + fd = d->stdio_fd; + if (fd < 0) + d->stdio_fd = fd = + open(d->devname, + O_RDONLY | O_LARGEFILE | O_BINARY); + if (fd == -1) { + if (errno == EACCES && (flag & 2)) { + if (!(flag & 8)) + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020183, + LIBDAX_MSGS_SEV_WARNING, + LIBDAX_MSGS_PRIO_HIGH, + "Failed to open device (a pseudo-drive) for reading", + errno, 0); + } else if (errno != ENOENT || !(flag & 2)) + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020005, + (flag & 32) && errno == ENOENT ? + LIBDAX_MSGS_SEV_DEBUG : + LIBDAX_MSGS_SEV_SORRY, + LIBDAX_MSGS_PRIO_HIGH, + "Failed to open device (a pseudo-drive) for reading", + errno, 0); + ret = 0; + if (errno == EACCES && (flag & 8)) + ret= -2; + goto ex; + } + ret = burn_stdio_seek(fd, byte_address, d, flag & (2 | 32)); + if (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 > (BUFFER_SIZE / 2048)) { + chunksize = (BUFFER_SIZE / 2048); + cpy_size = BUFFER_SIZE; + } else + cpy_size = data_size - *data_count; + if (flag & 2) + d->silent_on_scsi_error = 1; + else if (flag & 32) + d->silent_on_scsi_error = 3; + if (flag & 16) { + d->had_particular_error &= ~1; + if (!d->silent_on_scsi_error) + d->silent_on_scsi_error = 2; + } + 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 & 32) | !!(flag & 2)); + err = 0; + if (ret <= 0) + err = BE_CANCELLED; + } + if (flag & (2 | 16 | 32)) + d->silent_on_scsi_error = sose_mem; + if (err == BE_CANCELLED) { + if ((flag & 16) && (d->had_particular_error & 1)) + {ret = -3; goto ex;} + /* Retry: with CD read by single blocks + with other media: retry in full chunks + */ + if(flag & 4) + goto bad_read; + if (d->drive_role == 1) { + ret = retry_mmc_read(d, chunksize, sose_mem, + start, &wpt, data_count, flag); + } else { + ret = retry_stdio_read(d, fd, chunksize, + start, &wpt, data_count, flag); + } + if (ret <= 0) + goto bad_read; + } else { + memcpy(wpt, d->buffer->data, cpy_size); + wpt += cpy_size; + *data_count += cpy_size; + } + } + + ret = 1; +ex:; + BURN_FREE_MEM(buf); + d->buffer = buffer_mem; + d->busy = BURN_DRIVE_IDLE; + return ret; + +bad_read:; + if (!(flag & 2)) + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020000, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + "burn_read_data() returns 0", 0, 0); + ret = 0; goto ex; +} + + +/* ts B21119 : API function*/ +int burn_read_audio(struct burn_drive *d, int sector_no, + char data[], off_t data_size, off_t *data_count, int flag) +{ + int alignment = 2352, start, upto, chunksize = 1, err, cpy_size, i; + int sose_mem = 0, ret; + char msg[81], *wpt; + struct buffer *buf = NULL, *buffer_mem = d->buffer; + + BURN_ALLOC_MEM(buf, struct buffer, 1); + *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); + {ret = 0; goto ex;} + } + if (d->drive_role != 1) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020146, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Drive is a virtual placeholder (stdio-drive or null-drive)", + 0, 0); + {ret = 0; goto ex;} + } + if ((data_size % alignment) != 0) { + sprintf(msg, + "Audio read size not properly aligned (%d bytes)", + alignment); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002019d, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + {ret = 0; goto ex;} + } + 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 audio", 0, 0); + {ret = 0; goto ex;} + } + + d->busy = BURN_DRIVE_READING_SYNC; + d->buffer = buf; + + start = sector_no; + upto = start + data_size / alignment; + wpt = data; + for (; start < upto; start += chunksize) { + chunksize = upto - start; + if (chunksize > (BUFFER_SIZE / alignment)) + chunksize = (BUFFER_SIZE / alignment); + cpy_size = chunksize * alignment; + if (flag & 2) + d->silent_on_scsi_error = 1; + else if (flag & 32) + d->silent_on_scsi_error = 3; + if (flag & 16) { + d->had_particular_error &= ~1; + if (!d->silent_on_scsi_error) + d->silent_on_scsi_error = 2; + } + err = d->read_cd(d, start, chunksize, 1, 0x10, NULL, d->buffer, + (flag & 8) >> 3); + if (flag & (2 | 16 | 32)) + d->silent_on_scsi_error = sose_mem; + if (err == BE_CANCELLED) { + if ((flag & 16) && (d->had_particular_error & 1)) + {ret = -3; goto ex;} + if(!(flag & 4)) + for (i = 0; i < chunksize - 1; i++) { + if (flag & 2) + d->silent_on_scsi_error = 1; + else if (flag & 32) + d->silent_on_scsi_error = 3; + err = d->read_cd(d, start + i, 1, 1, 0x10, + NULL, d->buffer, (flag & 8) >> 3); + if (flag & (2 | 32)) + d->silent_on_scsi_error = sose_mem; + if (err == BE_CANCELLED) + break; + memcpy(wpt, d->buffer->data, alignment); + wpt += alignment; + *data_count += alignment; + } + + ret = 0; goto ex; + } + memcpy(wpt, d->buffer->data, cpy_size); + wpt += cpy_size; + *data_count += cpy_size; + } + + ret = 1; +ex: + BURN_FREE_MEM(buf); + d->buffer = buffer_mem; + d->busy = BURN_DRIVE_IDLE; + return ret; +} + + +#ifdef Libburn_develop_quality_scaN + +/* B21108 ts */ +int burn_nec_optiarc_rep_err_rate(struct burn_drive *d, + int start_lba, int rate_period, int flag) +{ + int ret, lba = 0, error_rate1 = 0, error_rate2 = 0, enabled = 0, dret; + + /* Sub Operation Code 1 : Enable Error Rate reporting function */ + ret = mmc_nec_optiarc_f3(d, 1, start_lba, rate_period, + &lba, &error_rate1, &error_rate2); + if (ret <= 0) + goto ex; + enabled = 1; + + /* >>> Sub Operation Code 2 : Seek to starting address + start_lba , rate_period + */; + + /* >>> Loop with Sub Operation Code 3 : Send Error Rate information + reply: 4-byte LBA , 2-byte C1/PIE , 2-byte C2/PIF + */; + + ret = 1; +ex:; + if (enabled) { + /* Code F : Disable Error Rate reporting function */ + dret = mmc_nec_optiarc_f3(d, 0xf, 0, 0, + &lba, &error_rate1, &error_rate2); + if (dret < ret) + ret = dret; + } + return ret; +} + +#endif /* Libburn_develop_quality_scaN */ + diff --git a/trunk/libburn/read.h b/trunk/libburn/read.h new file mode 100644 index 0000000..fc07958 --- /dev/null +++ b/trunk/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/trunk/libburn/sbc.c b/trunk/libburn/sbc.c new file mode 100644 index 0000000..d29f167 --- /dev/null +++ b/trunk/libburn/sbc.c @@ -0,0 +1,183 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* scsi block commands */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2010 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#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); + + +/* START STOP UNIT as of SBC-1 and SBC-2 + 0: Opcode 0x1B + 1: bit0= Immed + bit1-7= reserved + 2: reserved + 3: reserved + 4: bit0= Start (else Stop unit) + bit1= Load/Eject (according to Start resp. Stop) + bit2-3= reserved + bit4-7= Power Condition + 0= Start Valid: process Start and Load/Eject bits + 1= assume Active state + 2= assume Idle state + 3= assume Stanby state + (5= SBC-1 only: assume Sleep state) + 7= transfer control of power conditions to logical unit + 10= force idle condition timer to 0 + 11= force standby condition timer to 0 + All others are reserved. + 5: Control (set to 0) +*/ +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 }; +static unsigned char SBC_STOP_UNIT[] = { 0x1b, 0, 0, 0, 0, 0 }; + +void sbc_load(struct burn_drive *d) +{ + struct command *c; + + c = &(d->casual_command); + if (mmc_function_spy(d, "load") <= 0) + return; + + scsi_init_command(c, SBC_LOAD, sizeof(SBC_LOAD)); + 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; + c->timeout = Libburn_mmc_load_timeouT; + 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; + + c = &(d->casual_command); + if (mmc_function_spy(d, "eject") <= 0) + return; + + scsi_init_command(c, SBC_UNLOAD, sizeof(SBC_UNLOAD)); + /* c->opcode[1] |= 1; / * ts A70918 : Immed , ts B00109 : revoked */ + c->page = NULL; + c->dir = NO_TRANSFER; + d->issue_command(d, c); + /* ts A70918 : Wait long. A late eject could surprise or hurt user. + ts B00109 : Asynchronous eject revoked, as one cannot reliably + distinguish out from unready. + if (c->error) + return; + spc_wait_unit_attention(d, 1800, "STOP UNIT (+ EJECT)", 0); + */ +} + + +/* ts A91112 : Now with flag */ +/* @param flag bit0= asynchronous waiting +*/ +int sbc_start_unit_flag(struct burn_drive *d, int flag) +{ + struct command *c; + int ret; + + c = &(d->casual_command); + if (mmc_function_spy(d, "start_unit") <= 0) + return 0; + + scsi_init_command(c, SBC_START_UNIT, sizeof(SBC_START_UNIT)); + c->retry = 1; + c->opcode[1] |= (flag & 1); /* ts A70918 : Immed */ + c->dir = NO_TRANSFER; + d->issue_command(d, c); + if (c->error) + return 0; + if (!(flag & 1)) + return 1; + /* ts A70918 : asynchronous */ + ret = spc_wait_unit_attention(d, 1800, "START UNIT", 0); + return ret; +} + + +int sbc_start_unit(struct burn_drive *d) +{ + int ret; + + d->is_stopped = 0; /* no endless starting attempts */ + + /* Asynchronous, not to block controller by waiting */ + ret = sbc_start_unit_flag(d, 1); + if (ret <= 0) + return ret; + /* Synchronous to catch Pioneer DVR-216D which is ready too early. + A pending START UNIT can prevent ejecting of the tray. + */ + ret = sbc_start_unit_flag(d, 0); + return ret; +} + + +/* ts A90824 : Trying to reduce drive noise */ +int sbc_stop_unit(struct burn_drive *d) +{ + struct command *c; + int ret; + + c = &(d->casual_command); + if (mmc_function_spy(d, "stop_unit") <= 0) + return 0; + + scsi_init_command(c, SBC_STOP_UNIT, sizeof(SBC_STOP_UNIT)); + c->retry = 0; + c->opcode[1] |= 1; /* Immed */ + c->dir = NO_TRANSFER; + d->issue_command(d, c); + if (c->error) + return 0; + ret = spc_wait_unit_attention(d, 1800, "STOP UNIT", 0); + d->is_stopped = 1; + return ret; +} + + + +/* 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; + d->stop_unit = sbc_stop_unit; + d->is_stopped = 0; + return 1; +} + diff --git a/trunk/libburn/sbc.h b/trunk/libburn/sbc.h new file mode 100644 index 0000000..9f4b303 --- /dev/null +++ b/trunk/libburn/sbc.h @@ -0,0 +1,24 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2010 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + +#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/trunk/libburn/sector.c b/trunk/libburn/sector.c new file mode 100644 index 0000000..e4d24cd --- /dev/null +++ b/trunk/libburn/sector.c @@ -0,0 +1,953 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#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 "toc.h" +#include "write.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + +#include "ecma130ab.h" + + +#ifdef Libburn_log_in_and_out_streaM +/* ts A61031 */ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#endif /* Libburn_log_in_and_out_streaM */ + +/* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/*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 | O_BINARY, + 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 - B10103 */ + if (shortage >= count) + track->track_data_done = 1; + if (track->end_on_premature_eoi && shortage >= count && + !track->open_ended) { + char msg[80]; + off_t missing, inp_block_size, track_blocks; + + inp_block_size = burn_sector_length(track->mode); + track_blocks = burn_track_get_sectors_2(track, 1); + missing = track_blocks * inp_block_size - track->sourcecount; + sprintf(msg, + "Premature end of input encountered. Missing: %.f bytes", + (double) missing); + libdax_msgs_submit(libdax_messenger, -1, 0x00020180, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + /* Memorize that premature end of input happened */ + track->end_on_premature_eoi = 2; + } + if (track->open_ended || track->end_on_premature_eoi) + 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 B20113 : outsourced from get_sector() */ +int sector_write_buffer(struct burn_drive *d, + struct burn_track *track, int flag) +{ + int err, i; + struct buffer *out; + + out = d->buffer; + if (out->sectors <= 0) + return 2; + err = d->write(d, d->nwa, out); + if (err == BE_CANCELLED) + return 0; + + /* ts A61101 */ + if(track != NULL) { + track->writecount += out->bytes; + track->written_sectors += out->sectors; + + /* Determine current index */ + for (i = d->progress.index; i + 1 < track->indices; i++) { + if (track->index[i + 1] > d->nwa + out->sectors) + break; + d->progress.index = i + 1; + } + } + /* ts A61119 */ + d->progress.buffered_bytes += out->bytes; + + d->nwa += out->sectors; + out->bytes = 0; + out->sectors = 0; + return 1; +} + + +/* 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, seclen, write_ret; + 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 */ + /* (there is enough buffer size reserve for track->cdxa_conversion) */ + if (out->bytes + seclen > BUFFER_SIZE || + (opts->obs > 0 && out->bytes + seclen > opts->obs)) { + write_ret = sector_write_buffer(d, track, 0); + if (write_ret <= 0) + return NULL; + } + 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)) { + /* see MMC-5 4.2.3.8.5.3 Block Format for Mode 2 form 1 Data + Table 24 Mode 2 Formed Sector Sub-header Format */ + if (track != NULL) + if (track->cdxa_conversion == 1) + inlen += 8; + + get_bytes(track, inlen, data); + + if (track != NULL) + if (track->cdxa_conversion == 1) + memmove(data, data + 8, inlen - 8); + 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); + +#ifdef Libburn_no_crc_C + crc = 0; /* dummy */ +#else + crc = crc_ccitt(q, 10); +#endif + + 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); + if (sector_headers(o, data, mode, 1) <= 0) + return 0; + 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); + if (sector_headers(o, data, mode, 0) <= 0) + return 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); + if (sector_headers(o, data, mode, 0) <= 0) + return 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); + +#ifdef Libburn_no_crc_C + crc = 0; /* dummy */ +#else + crc = crc_ccitt(q, 10); +#endif + + 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; + + +#ifdef Libburn_no_crc_C + crc = 0; /* dummy */ +#else + crc = crc_ccitt(q, 10); +#endif + + 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); + if (sector_headers(o, data, mode, 0) <= 0) + return 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 == NULL) + return 0; + /* ts A61010 */ + if (convert_data(o, t, t->mode, data) <= 0) + return 0; + + /* ts A61031 */ + if ((t->open_ended || t->end_on_premature_eoi) && 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); + + if (sector_headers(o, data, t->mode, 0) <= 0) + return 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; +} + +/* ts A90830 : changed return type to int + @return 0= failure + 1= success +*/ +int sector_headers(struct burn_write_opts *o, unsigned char *out, + int mode, int leadin) +{ + +#ifdef Libburn_ecma130ab_includeD + + struct burn_drive *d = o->drive; + unsigned int crc; + int min, sec, frame; + int modebyte = -1; + int ret; + + ret = sector_headers_is_ok(o, mode); + if (ret != 2) + return !!ret; + 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) { + +#ifdef Libburn_no_crc_C + crc = 0; /* dummy */ +#else + crc = crc_32(out, 2064); +#endif + + 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); + burn_rspc_parity_p(out); + burn_rspc_parity_q(out); + } + burn_ecma130_scramble(out); + return 1; + +#else /* Libburn_ecma130ab_includeD */ + + int ret; + + ret = sector_headers_is_ok(o, mode); + if (ret != 2) + return (!! ret); + + /* ts A90830 : lec.c is copied from cdrdao. + I have no idea yet how lec.c implements the Reed-Solomon encoding + which is described in ECMA-130 for CD-ROM. + So this got removed for now. + */ + libdax_msgs_submit(libdax_messenger, o->drive->global_index, + 0x0002010a, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Raw CD write modes are not supported", 0, 0); + return 0; + +#endif /* ! Libburn_ecma130ab_includeD */ + +} + +#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) +{ + +/* + burn_ecma130_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/trunk/libburn/sector.h b/trunk/libburn/sector.h new file mode 100644 index 0000000..422444d --- /dev/null +++ b/trunk/libburn/sector.h @@ -0,0 +1,44 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2010 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#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 B20113 */ +int sector_write_buffer(struct burn_drive *d, + struct burn_track *track, int flag); + +/* ts A61009 */ +int sector_headers_is_ok(struct burn_write_opts *o, int mode); + +int 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/trunk/libburn/sg-dummy.c b/trunk/libburn/sg-dummy.c new file mode 100644 index 0000000..fefcaeb --- /dev/null +++ b/trunk/libburn/sg-dummy.c @@ -0,0 +1,359 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* + Copyright (c) 2009 - 2011 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + + +/* + +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: default dummy which enables libburn only to work + with stdio: pseudo drive addresses. + For real implementations see sg-linux.c, sg-freebsd.c, + sg-libcdio.c +*/ + + +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <string.h> +#include <stdlib.h> + +#ifdef Libburn_os_has_statvfS +#include <sys/statvfs.h> +#endif /* Libburn_os_has_stavtfS */ + +/* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#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 "init.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + + +/** Returns the id string of the SCSI transport adapter and eventually + needed operating system facilities. + This call is usable even if sg_initialize() was not called yet. In that + case a preliminary constant message might be issued if detailed info is + not available yet. + @param msg returns id string + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_id_string(char msg[1024], int flag) +{ + strcpy(msg, "internal X/Open adapter sg-dummy"); + return 1; +} + + +/** Performs global initialization of the SCSI transport adapter and eventually + needed operating system facilities. Checks for compatibility supporting + software components. + @param msg returns ids and/or error messages of eventual helpers + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_initialize(char msg[1024], int flag) +{ + return sg_id_string(msg, 0); +} + + +/** Performs global finalization of the SCSI transport adapter and eventually + needed operating system facilities. Releases globally aquired resources. + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_shutdown(int flag) +{ + return 1; +} + + +/** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of + struct burn_drive which are defined in os-*.h. + This will be called when a burn_drive gets disposed. + @param d the drive to be finalized + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_dispose_drive(struct burn_drive *d, int flag) +{ + return 1; +} + + +/** 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) +{ + return 0; +} + + +/** Brings all available, not-whitelist-banned, and accessible drives into + libburn's list of drives. +*/ +/* ts A61115: replacing call to sg-implementation internals from drive.c */ +int scsi_enumerate_drives(void) +{ + libdax_msgs_submit(libdax_messenger, -1, 0x0002016b, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + "No MMC transport adapter is present. Running on sg-dummy.c.", + 0, 0); + 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) +{ + return 0; +} + + +/** 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) +{ + libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002016a, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "No MMC transport adapter is present. Running on sg-dummy.c.", + 0, 0); + return 0; +} + + +/** 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) +{ + 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) +{ + libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002016a, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "No MMC transport adapter is present. Running on sg-dummy.c.", + 0, 0); + 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) +{ + libdax_msgs_submit(libdax_messenger, -1, 0x0002016c, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + "No MMC transport adapter is present. Running on sg-dummy.c.", + 0, 0); + return 0; +} + + +/** Tells wether a text is a persistent address as listed by the enumeration + functions. +*/ +int sg_is_enumerable_adr(char *adr) +{ + return(0); +} + + +/* Return 1 if the given path leads to a regular file or a device that can be + seeked, read, and possibly written with 2 kB granularity. +*/ +int burn_os_is_2k_seekrw(char *path, int flag) +{ + struct stat stbuf; + + if (stat(path, &stbuf) == -1) + return 0; + if (S_ISREG(stbuf.st_mode)) + return 1; + if (S_ISBLK(stbuf.st_mode)) + return 1; + return 0; +} + + +/** 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 write_start, off_t *bytes) +{ + struct stat stbuf; + +#ifdef Libburn_os_has_statvfS + struct statvfs vfsbuf; +#endif + + char *testpath = NULL, *cpt; + off_t add_size = 0; + int ret; + + BURN_ALLOC_MEM(testpath, char, 4096); + + testpath[0] = 0; + 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) + {ret = -1; goto ex;} + +#ifdef Libburn_if_this_was_linuX + + } else if(S_ISBLK(stbuf.st_mode)) { + long blocks; + + blocks = *bytes / 512; + fd = open(path, open_mode | O_BINARY); + if (fd == -1) + {ret = -2; goto ex;} + ret = ioctl(fd, BLKGETSIZE, &blocks); + close(fd); + if (ret == -1) + {ret = -2; goto ex;} + *bytes = ((off_t) blocks) * (off_t) 512; + +#endif /* Libburn_if_this_was_linuX */ + + } else if(S_ISREG(stbuf.st_mode)) { + add_size = burn_sparse_file_addsize(write_start, &stbuf); + strcpy(testpath, path); + } else + {ret = 0; goto ex;} + + if (testpath[0]) { + +#ifdef Libburn_os_has_statvfS + + if (statvfs(testpath, &vfsbuf) == -1) + {ret = -2; goto ex;} + *bytes = add_size + ((off_t) vfsbuf.f_frsize) * + (off_t) vfsbuf.f_bavail; + +#else /* Libburn_os_has_statvfS */ + + {ret = 0; goto ex;} + +#endif /* ! Libburn_os_has_stavtfS */ + + } + ret = 1; +ex:; + BURN_FREE_MEM(testpath); + return ret; +} + + +/* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ + +#ifdef Libburn_read_o_direcT + + /* No special O_DIRECT-like precautions are implemented here */ + +#endif /* Libburn_read_o_direcT */ + + +int burn_os_open_track_src(char *path, int open_flags, int flag) +{ + int fd; + + fd = open(path, open_flags | O_BINARY); + return fd; +} + + +void *burn_os_alloc_buffer(size_t amount, int flag) +{ + void *buf = NULL; + + buf = calloc(1, amount); + return buf; +} + + +int burn_os_free_buffer(void *buffer, size_t amount, int flag) +{ + if (buffer == NULL) + return 0; + free(buffer); + return 1; +} + diff --git a/trunk/libburn/sg-freebsd-port.c b/trunk/libburn/sg-freebsd-port.c new file mode 100644 index 0000000..db1c687 --- /dev/null +++ b/trunk/libburn/sg-freebsd-port.c @@ -0,0 +1,824 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* + Copyright (c) 2006 - 2011 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + + /* THIS CODE IS NOT FUNCTIONAL YET !!! */ + + +/* + +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_id_string() returns an id string of the SCSI transport adapter. + It may be called before initialization but then may + return only a preliminary id. + +sg_initialize() performs global initialization of the SCSI transport + adapter and eventually needed operating system + facilities. Checks for compatibility of supporting + software components. + +sg_shutdown() performs global finalizations and releases golbally + aquired resources. + +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_dispose_drive() finalizes adapter specifics of struct burn_drive + on destruction. Releases resources which were aquired + underneath scsi_enumerate_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_is_2k_seekrw() tells whether the given path leads to a file object + that can be used in 2 kB granularity by lseek(2), + read(2), and possibly write(2) if not read-only. + E.g. a USB stick or a hard disk. + +burn_os_stdio_capacity() estimates the emulated media space of stdio-drives. + +burn_os_open_track_src() opens a disk file in a way that allows best + throughput with file reading and/or SCSI write command + transmission. + +burn_os_alloc_buffer() allocates a memory area that is suitable for file + descriptors issued by burn_os_open_track_src(). + The buffer size may be rounded up for alignment + reasons. + +burn_os_free_buffer() delete a buffer obtained by burn_os_alloc_buffer(). + + +Porting hints are marked by the text "PORTING:". +Send feedback to libburn-hackers@pykix.org . + +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + + +/** 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 */ +#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; + } + + memset(&(idx->ccb), 0, 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 *) + calloc(1, idx->bufsize); + if (idx->ccb.cdm.matches == NULL) { + warnx("cannot allocate 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 id string of the SCSI transport adapter and eventually + needed operating system facilities. + This call is usable even if sg_initialize() was not called yet. In that + case a preliminary constant message might be issued if detailed info is + not available yet. + @param msg returns id string + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_id_string(char msg[1024], int flag) +{ + strcpy(msg, "internal FreeBSD CAM adapter sg-freebsd-port"); + return 1; +} + + +/** Performs global initialization of the SCSI transport adapter and eventually + needed operating system facilities. Checks for compatibility supporting + software components. + @param msg returns ids and/or error messages of eventual helpers + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_initialize(char msg[1024], int flag) +{ + return sg_id_string(msg, 0); +} + + +/** Performs global finalization of the SCSI transport adapter and eventually + needed operating system facilities. Releases globally aquired resources. + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_shutdown(int flag) +{ + return 1; +} + + +/** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of + struct burn_drive which are defined in os-*.h. + The eventual initialization of those components was made underneath + scsi_enumerate_drives(). + This will be called when a burn_drive gets disposed. + @param d the drive to be finalized + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_dispose_drive(struct burn_drive *d, int flag) +{ + return 1; +} + + +/** 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, ret; + 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); + 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) +{ + 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) +{ + struct cam_device *cam; + + if(d->cam != NULL) { + 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; + } + 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) + 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); + + 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 { + memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); + 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, ret; + 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(path, 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, ret; + 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 B00115 */ +/* Return 1 if the given path leads to a regular file or a device that can be + seeked, written, and read with 2 kB granularity. +*/ +int burn_os_is_2k_seekrw(char *path, int flag) +{ + struct stat stbuf; + char *spt; + int i, e; + + if (stat(path, &stbuf) == -1) + return 0; + if (S_ISREG(stbuf.st_mode)) + return 1; + if (!S_ISCHR(stbuf.st_mode)) + return 0; + spt = strrchr(path, '/'); + if (spt == NULL) + spt = path; + else + spt++; + e = strlen(spt); + for (i = strlen(spt) - 1; i > 0; i--) + if (spt[i] >= '0' && spt[i] <= '9') + e = i; + if (strncmp(spt, "da", e) == 0) /* SCSI disk. E.g. USB stick. */ + return 1; + if (strncmp(spt, "cd", e) == 0) /* SCSI CD drive might be writeable. */ + return 1; + if (strncmp(spt, "ad", e) == 0) /* IDE hard drive */ + return 1; + if (strncmp(spt, "acd", e) == 0) /* IDE CD drive might be writeable */ + return 1; + if (strncmp(spt, "fd", e) == 0) /* Floppy disk */ + return 1; + if (strncmp(spt, "fla", e) == 0) /* Flash drive */ + return 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 write_start, off_t *bytes) +{ + struct stat stbuf; + struct statvfs vfsbuf; + char *testpath = NULL, *cpt; + off_t add_size = 0; + int fd, ret; + + BURN_ALLOC_MEM(testpath, char, 4096); + testpath[0] = 0; + 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) + {ret = -1; goto ex;} + +#ifdef Libburn_if_this_was_linuX + + } else if(S_ISBLK(stbuf.st_mode)) { + int open_mode = O_RDWR, fd, ret; + long blocks; + + blocks = *bytes / 512; + if(burn_sg_open_o_excl) + open_mode |= O_EXCL; + fd = open(path, open_mode); + if (fd == -1) + {ret = -2; goto ex;} + ret = ioctl(fd, BLKGETSIZE, &blocks); + close(fd); + if (ret == -1) + {ret = -2; goto ex;} + *bytes = ((off_t) blocks) * (off_t) 512; + +#endif /* Libburn_if_this_was_linuX */ + + + } else if(S_ISCHR(stbuf.st_mode)) { + fd = open(path, O_RDONLY); + if (fd == -1) + {ret = -2; goto ex;} + ret = ioctl(fd, DIOCGMEDIASIZE, &add_size); + close(fd); + if (ret == -1) + {ret = -2; goto ex;} + *bytes = add_size; + } else if(S_ISREG(stbuf.st_mode)) { + add_size = burn_sparse_file_addsize(write_start, &stbuf); + strcpy(testpath, path); + } else + {ret = 0; goto ex;} + + if (testpath[0]) { + if (statvfs(testpath, &vfsbuf) == -1) + {ret = -2; goto ex;} + *bytes = add_size + ((off_t) vfsbuf.f_frsize) * + (off_t) vfsbuf.f_bavail; + } + ret = 1; +ex: + BURN_FREE_MEM(testpath); + return ret; +} + + +/* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ + +#ifdef Libburn_read_o_direcT + + /* No special O_DIRECT-like precautions are implemented here */ + +#endif /* Libburn_read_o_direcT */ + + +int burn_os_open_track_src(char *path, int open_flags, int flag) +{ + int fd; + + fd = open(path, open_flags); + return fd; +} + + +void *burn_os_alloc_buffer(size_t amount, int flag) +{ + void *buf = NULL; + + buf = calloc(1, amount); + return buf; +} + + +int burn_os_free_buffer(void *buffer, size_t amount, int flag) +{ + if (buffer == NULL) + return 0; + free(buffer); + return 1; +} + diff --git a/trunk/libburn/sg-freebsd.c b/trunk/libburn/sg-freebsd.c new file mode 100644 index 0000000..6ca26f2 --- /dev/null +++ b/trunk/libburn/sg-freebsd.c @@ -0,0 +1,1159 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* + Copyright (c) 2006 - 2013 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later + and under FreeBSD license revised, i.e. without advertising clause. +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#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 <sys/file.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 */ +#include <sys/statvfs.h> + +/* ts B00121 */ +#include <sys/disk.h> /* DIOCGMEDIASIZE */ + + +/* ts B00326 : For use of CAM_PASS_ERR_RECOVER with ahci */ +#define Libburn_for_freebsd_ahcI yes + +/* ts B00327 : for debugging of cam_send_cdb() failures + # define Libburn_ahci_verbouS yes +*/ + +/* ts B00327 : Apply CAM_PASS_ERR_RECOVER to drives even if not ahci + # define libburn_ahci_style_for_alL yes +*/ + + +#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 "init.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 B00113 + Whether to log SCSI commands: + bit0= log in /tmp/libburn_sg_command_log + bit1= log to stderr + bit2= flush every line +*/ +extern int burn_sg_log_scsi; + +/* ts B00114 */ +/* Storage object is in libburn/init.c + whether to strive for exclusive access to the drive +*/ +extern int burn_sg_open_o_excl; + + +/* ts A91227 */ +/** Returns the id string of the SCSI transport adapter and eventually + needed operating system facilities. + This call is usable even if sg_initialize() was not called yet. In that + case a preliminary constant message might be issued if detailed info is + not available yet. + @param msg returns id string + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_id_string(char msg[1024], int flag) +{ + strcpy(msg, "internal FreeBSD CAM adapter sg-freebsd"); + return 1; +} + + +/* ts A91227 */ +/** Performs global initialization of the SCSI transport adapter and eventually + needed operating system facilities. Checks for compatibility supporting + software components. + @param msg returns ids and/or error messages of eventual helpers + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_initialize(char msg[1024], int flag) +{ + return sg_id_string(msg, 0); +} + + +/* ts A91227 */ +/** Performs global finalization of the SCSI transport adapter and eventually + needed operating system facilities. Releases globally aquired resources. + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_shutdown(int flag) +{ + return 1; +} + + +/** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of + struct burn_drive which are defined in os-*.h. + The eventual initialization of those components was made underneath + scsi_enumerate_drives(). + This will be called when a burn_drive gets disposed. + @param d the drive to be finalized + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_dispose_drive(struct burn_drive *d, int flag) +{ + return 1; +} + + +/* 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 = calloc(1, sizeof(*idx)); + if (idx == NULL) { + warnx("cannot allocate memory for enumerator"); + return -1; + } + idx->skip_device = 0; + + if ((idx->fd = open(XPT_DEVICE, O_RDWR)) == -1) { + warn("could not open %s", XPT_DEVICE); + free(idx); + idx = NULL; + return -1; + } + + memset(&(idx->ccb), 0, 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 *) calloc(1, bufsize); + if (idx->ccb.cdm.matches == NULL) { + warnx("cannot allocate 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 object state 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); +/* ts B00112 : we really want only "cd" devices. + + if (idx->skip_device || + strcmp(result->periph_name, "pass") == 0) + break; +*/ + if (idx->skip_device || + strcmp(result->periph_name, "cd") != 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: + /* fprintf(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; + } + if (d->lock_fd > 0) { + close(d->lock_fd); + d->lock_fd = -1; + } + 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 = strdup(fname); + + out.cam = NULL; + out.lock_fd = -1; + out.is_ahci = 0; + + 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_cd = mmc_read_cd; + 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 = calloc(1, sizeof(struct burn_scsi_inquiry_data)); + out.idata->valid = 0; + out.mdata = calloc(1, sizeof(struct scsi_mode_data)); + 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; + } + out.mdata->p2a_valid = 0; + 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)) { + t->getcaps(t); + t->unlock(t); + t->released = 1; + } + +/* 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; + out.lock_fd = -1; + out.is_ahci = 0; + + /* 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 */ + + +/* Lock the inode associated to dev_fd and the inode associated to devname. + Return OS errno, number of pass device of dev_fd, locked fd to devname, + error message. + A return value of > 0 means success, <= 0 means failure. +*/ +static int freebsd_dev_lock(int dev_fd, char *devname, + int *os_errno, int *pass_dev_no, int *lock_fd, char msg[4096], + int flag) +{ + int lock_denied = 0, fd_stbuf_valid, name_stbuf_valid, i, pass_l = 100; + int max_retry = 3, tries = 0; + struct stat fd_stbuf, name_stbuf; + char pass_name[16], *lock_name; + + *os_errno = 0; + *pass_dev_no = -1; + *lock_fd = -1; + msg[0] = 0; + + fd_stbuf_valid = !fstat(dev_fd, &fd_stbuf); + + /* Try to find name of pass device by inode number */ + lock_name = (char *) "effective device"; + if(fd_stbuf_valid) { + for (i = 0; i < pass_l; i++) { + sprintf(pass_name, "/dev/pass%d", i); + if (stat(pass_name, &name_stbuf) != -1) + if(fd_stbuf.st_ino == name_stbuf.st_ino && + fd_stbuf.st_dev == name_stbuf.st_dev) + break; + } + if (i < pass_l) { + lock_name = pass_name; + *pass_dev_no = i; + } + } + + name_stbuf_valid = !stat(devname, &name_stbuf); + for (tries= 0; tries <= max_retry; tries++) { + lock_denied = flock(dev_fd, LOCK_EX | LOCK_NB); + *os_errno = errno; + if (lock_denied) { + if (errno == EAGAIN && tries < max_retry) { + /* <<< debugging + fprintf(stderr, + "\nlibcdio_DEBUG: EAGAIN pass, tries= %d\n", + tries); + */ + usleep(2000000); + continue; + } + sprintf(msg, + "Device busy. flock(LOCK_EX) failed on %s of %s", + strlen(lock_name) > 2000 || *pass_dev_no < 0 ? + "pass device" : lock_name, + strlen(devname) > 2000 ? "drive" : devname); + return 0; + } + break; + } + + /* + fprintf(stderr, "libburn_DEBUG: flock obtained on %s of %s\n", + lock_name, devname); + */ + + /* Eventually lock the official device node too */ + if (fd_stbuf_valid && name_stbuf_valid && + (fd_stbuf.st_ino != name_stbuf.st_ino || + fd_stbuf.st_dev != name_stbuf.st_dev)) { + + *lock_fd = open(devname, O_RDONLY); + if (*lock_fd == 0) { + close(*lock_fd); + *lock_fd = -1; + } if (*lock_fd > 0) { + for (tries = 0; tries <= max_retry; tries++) { + lock_denied = + flock(*lock_fd, LOCK_EX | LOCK_NB); + if (lock_denied) { + if (errno == EAGAIN && + tries < max_retry) { + /* <<< debugging + fprintf(stderr, + "\nlibcdio_DEBUG: EAGAIN dev, tries= %d\n", + tries); + */ + + usleep(2000000); + continue; + } + close(*lock_fd); + *lock_fd = -1; + sprintf(msg, + "Device busy. flock(LOCK_EX) failed on %s", + strlen(devname) > 4000 ? "drive" : devname); + return 0; + } + break; + } + } + +/* + fprintf(stderr, "libburn_DEBUG: flock obtained on %s\n", + devname); +*/ + + } + return 1; +} + + +static int sg_lock(struct burn_drive *d, int flag) +{ + int ret, os_errno, pass_dev_no = -1, flock_fd = -1; + char *msg = NULL; + + BURN_ALLOC_MEM(msg, char, 4096); + ret = freebsd_dev_lock(d->cam->fd, d->devname, + &os_errno, &pass_dev_no, &flock_fd, msg, 0); + if (ret <= 0) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020008, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, os_errno, 0); + sg_close_drive(d); + {ret = 0; goto ex;} + } + if (d->lock_fd > 0) + close(d->lock_fd); + d->lock_fd = flock_fd; + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +int sg_grab(struct burn_drive *d) +{ + struct cam_device *cam; + char path_string[80]; + + 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", errno, 0); + return 0; + } + d->cam = cam; + if (burn_sg_open_o_excl & 63) + if (sg_lock(d, 0) <= 0) + return 0; + fcntl(cam->fd, F_SETOWN, getpid()); + + cam_path_string(d->cam, path_string, sizeof(path_string)); + +#ifdef Libburn_ahci_verbouS + fprintf(stderr, "libburn_EXPERIMENTAL: CAM path = %s\n", path_string); +#endif + + if (strstr(path_string, ":ahcich") != NULL) + d->is_ahci = 1; + else + d->is_ahci = -1; + + d->released = 0; + return 1; +} + + +/* + 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) + 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, err, sense_len = 0, ret, ignore_error, i; + int cam_pass_err_recover = 0, key, asc, ascq, timeout_ms; + union ccb *ccb; + static FILE *fp = NULL; + time_t start_time; + + mmc_function_spy(NULL, "sg_issue_command"); + + c->error = 0; + memset(c->sense, 0, sizeof(c->sense)); + + if (d->cam == NULL) + return 0; + if (burn_sg_log_scsi & 1) { + if (fp == NULL) { + fp= fopen("/tmp/libburn_sg_command_log", "a"); + fprintf(fp, + "\n-----------------------------------------\n"); + } + } + if (burn_sg_log_scsi & 3) + scsi_log_cmd(c,fp,0); + + c->error = 0; + if (c->timeout > 0) + timeout_ms = c->timeout; + else + timeout_ms = 200000; + + 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 */ + timeout_ms); /* 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; + } + +#ifdef Libburn_for_freebsd_ahcI + /* ts B00325 : Advise by Alexander Motin */ + /* Runs well on 8-STABLE (23 Mar 2003) + But on 8-RELEASE cam_send_ccb() returns non-zero with errno 6 + on eject. Long lasting TEST UNIT READY cycles break with + errno 16. + */ +#ifdef Libburn_ahci_style_for_alL + { +#else + if (d->is_ahci > 0) { +#endif + ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; + cam_pass_err_recover = 1; + } +#endif /* Libburn_for_freebsd_ahcI */ + + ccb->csio.cdb_len = c->oplen; + memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen); + + if (c->page) { + ccb->csio.data_ptr = c->page->data; + if (c->dir == FROM_DRIVE) { + + /* ts A90430 : Ticket 148 , by jwehle : + "On ... FreeBSD 6.4 which has a usb memory reader in + addition to a ATAPI DVD burner sg_issue_command + will hang while the SCSI bus is being scanned" + */ + if (c->dxfer_len >= 0) + ccb->csio.dxfer_len = c->dxfer_len; + else + ccb->csio.dxfer_len = BUFFER_SIZE; + +/* touch page so we can use valgrind */ + memset(c->page->data, 0, BUFFER_SIZE); + } else { + ccb->csio.dxfer_len = c->page->bytes; + } + } else { + ccb->csio.data_ptr = NULL; + ccb->csio.dxfer_len = 0; + } + + start_time = time(NULL); + for (i = 0; !done; i++) { + + memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); + memset(c->sense, 0, sizeof(c->sense)); + c->start_time = burn_get_time(0); + + err = cam_send_ccb(d->cam, ccb); + + c->end_time = burn_get_time(0); + ignore_error = sense_len = 0; + /* ts B00325 : CAM_AUTOSNS_VALID advised by Alexander Motin */ + if (ccb->ccb_h.status & CAM_AUTOSNS_VALID) { + /* ts B00110 */ + /* Better curb sense_len */ + sense_len = ccb->csio.sense_len; + if (sense_len > (int) sizeof(c->sense)) + sense_len = sizeof(c->sense); + memcpy(c->sense, &ccb->csio.sense_data, sense_len); + spc_decode_sense(c->sense, sense_len, + &key, &asc, &ascq); + if (sense_len >= 14 && cam_pass_err_recover && key) + ignore_error = 1; + } + + if (err == -1 && cam_pass_err_recover && ! ignore_error) { + +#ifdef Libburn_ahci_verbouS + fprintf(stderr, "libburn_EXPERIMENTAL: errno = %d . cam_errbuf = '%s'\n", errno, cam_errbuf); +#endif + + if (errno == ENXIO && c->opcode[0] != 0) { + /* Operations on empty or ejected tray */ + /* MEDIUM NOT PRESENT */ + +#ifdef Libburn_ahci_verbouS + fprintf(stderr, "libburn_EXPERIMENTAL: Emulating [2,3A,00] MEDIUM NOT PRESENT\n"); +#endif + + c->sense[0] = 0x70; /*Fixed format sense data*/ + c->sense[2] = 0x02; + c->sense[12] = 0x3A; + c->sense[13] = 0x00; + sense_len = 14; + ignore_error = 1; + } else if (c->opcode[0] == 0 && + (errno == EBUSY || errno == ENXIO)) { + /* Timeout of TEST UNIT READY loop */ + /* Inquiries while tray is being loaded */ + /*LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE*/ + +#ifdef Libburn_ahci_verbouS + fprintf(stderr, "libburn_EXPERIMENTAL: Emulating [2,04,00] LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE\n"); +#endif + + c->sense[0] = 0x70; /*Fixed format sense data*/ + c->sense[2] = 0x02; + c->sense[12] = 0x04; + c->sense[13] = 0x00; + sense_len = 14; + ignore_error = 1; + } else if (errno == EINVAL) { + /* Inappropriate MODE SENSE */ + /* INVALID FIELD IN CDB */ + +#ifdef Libburn_ahci_verbouS + fprintf(stderr, "libburn_EXPERIMENTAL: Emulating [5,24,00] INVALID FIELD IN CDB\n"); +#endif + + c->sense[0] = 0x70; /*Fixed format sense data*/ + c->sense[2] = 0x05; + c->sense[12] = 0x24; + c->sense[13] = 0x00; + sense_len = 14; + ignore_error = 1; + } + } + + if (err == -1 && !ignore_error) { + 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; + {ret = -1; goto ex;} + } + /* XXX */ + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + if (sense_len < 14) { + /*LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE*/ + +#ifdef Libburn_ahci_verbouS + fprintf(stderr, "libburn_EXPERIMENTAL: CAM_STATUS= %d .Emulating [2,04,00] LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE\n", (ccb->ccb_h.status & CAM_STATUS_MASK)); +#endif + + c->sense[0] = 0x70; /*Fixed format sense data*/ + c->sense[2] = 0x02; + c->sense[12] = 0x04; + c->sense[13] = 0x00; + done = 1; + } + } + done = scsi_eval_cmd_outcome(d, c, fp, c->sense, + sense_len, start_time, + timeout_ms, i, !!ignore_error); + if (d->cancel) + done = 1; + } while (!done); + ret = 1; +ex:; + cam_freeccb(ccb); + return ret; +} + + +/* ts B00115 */ +/* Return 1 if the given path leads to a regular file or a device that can be + seeked, read and eventually written with 2 kB granularity. +*/ +int burn_os_is_2k_seekrw(char *path, int flag) +{ + struct stat stbuf; +#ifdef Libburn_DIOCGMEDIASIZE_ISBLK + int fd, ret; + off_t add_size; +#else + char *spt; + int i, e; +#endif /* ! Libburn_DIOCGMEDIASIZE_ISBLK */ + + if (stat(path, &stbuf) == -1) + return 0; + if (S_ISREG(stbuf.st_mode)) + return 1; + if (!S_ISCHR(stbuf.st_mode)) + return 0; + +#ifdef Libburn_DIOCGMEDIASIZE_ISBLK + + /* If it throws no error with DIOCGMEDIASIZE then it is a + 'block device' + */ + fd = open(path, O_RDONLY); + if (fd == -1) + return 0; + ret = ioctl(fd, DIOCGMEDIASIZE, &add_size); + close(fd); + + return (ret != -1); + +#else /* Libburn_DIOCGMEDIASIZE_ISBLK */ + + spt = strrchr(path, '/'); + if (spt == NULL) + spt = path; + else + spt++; + e = strlen(spt); + for (i = strlen(spt) - 1; i > 0; i--) + if (spt[i] >= '0' && spt[i] <= '9') + e = i; + if (strncmp(spt, "da", e) == 0) /* SCSI disk. E.g. USB stick. */ + return 1; + if (strncmp(spt, "cd", e) == 0) /* SCSI CD drive might be writeable. */ + return 1; + if (strncmp(spt, "ad", e) == 0) /* IDE hard drive */ + return 1; + if (strncmp(spt, "acd", e) == 0) /* IDE CD drive might be writeable */ + return 1; + if (strncmp(spt, "fd", e) == 0) /* Floppy disk */ + return 1; + if (strncmp(spt, "fla", e) == 0) /* Flash drive */ + return 1; + return 0; + +#endif /* ! Libburn_DIOCGMEDIASIZE_ISBLK */ + +} + + +/* 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 write_start, off_t *bytes) +{ + struct stat stbuf; + struct statvfs vfsbuf; + char *testpath = NULL, *cpt; + off_t add_size = 0; + int fd, ret; + + BURN_ALLOC_MEM(testpath, char, 4096); + testpath[0] = 0; + 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) + {ret = -1; goto ex;} + +#ifdef Libburn_if_this_was_linuX + + } else if(S_ISBLK(stbuf.st_mode)) { + int open_mode = O_RDWR, fd, ret; + long blocks; + + blocks = *bytes / 512; + if(burn_sg_open_o_excl) + open_mode |= O_EXCL; + fd = open(path, open_mode); + if (fd == -1) + {ret = -2; goto ex;} + ret = ioctl(fd, BLKGETSIZE, &blocks); + close(fd); + if (ret == -1) + {ret = -2; goto ex;} + *bytes = ((off_t) blocks) * (off_t) 512; + +#endif /* Libburn_if_this_was_linuX */ + + + } else if(S_ISCHR(stbuf.st_mode)) { + fd = open(path, O_RDONLY); + if (fd == -1) + {ret = -2; goto ex;} + ret = ioctl(fd, DIOCGMEDIASIZE, &add_size); + close(fd); + if (ret == -1) + {ret = -2; goto ex;} + *bytes = add_size; + } else if(S_ISREG(stbuf.st_mode)) { + add_size = burn_sparse_file_addsize(write_start, &stbuf); + strcpy(testpath, path); + } else + {ret = 0; goto ex;} + + if (testpath[0]) { + if (statvfs(testpath, &vfsbuf) == -1) + {ret = -2; goto ex;} + *bytes = add_size + ((off_t) vfsbuf.f_frsize) * + (off_t) vfsbuf.f_bavail; + } + ret = 1; +ex: + BURN_FREE_MEM(testpath); + return ret; +} + + +/* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ + +#ifdef Libburn_read_o_direcT + + /* No special O_DIRECT-like precautions are implemented here */ + +#endif /* Libburn_read_o_direcT */ + + +int burn_os_open_track_src(char *path, int open_flags, int flag) +{ + int fd; + + fd = open(path, open_flags); + return fd; +} + + +void *burn_os_alloc_buffer(size_t amount, int flag) +{ + void *buf = NULL; + + buf = calloc(1, amount); + return buf; +} + + +int burn_os_free_buffer(void *buffer, size_t amount, int flag) +{ + if (buffer == NULL) + return 0; + free(buffer); + return 1; +} + diff --git a/trunk/libburn/sg-libcdio.c b/trunk/libburn/sg-libcdio.c new file mode 100644 index 0000000..eb852f4 --- /dev/null +++ b/trunk/libburn/sg-libcdio.c @@ -0,0 +1,1014 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* + Copyright (c) 2009 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + +/* + +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: GNU libcdio , for X/Open compliant operating systems + + +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_id_string() returns an id string of the SCSI transport adapter. + It may be called before initialization but then may + return only a preliminary id. + +sg_initialize() performs global initialization of the SCSI transport + adapter and eventually needed operating system + facilities. Checks for compatibility of supporting + software components. + +sg_shutdown() performs global finalizations and releases golbally + aquired resources. + +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_dispose_drive() finalizes adapter specifics of struct burn_drive + on destruction. Releases resources which were aquired + underneath scsi_enumerate_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_is_2k_seekrw() tells whether the given path leads to a file object + that can be used in 2 kB granularity by lseek(2), + read(2), and possibly write(2) if not read-only.. + E.g. a USB stick or a hard disk. + +burn_os_stdio_capacity() estimates the emulated media space of stdio-drives. + +burn_os_open_track_src() opens a disk file in a way that allows best + throughput with file reading and/or SCSI write command + transmission. + +burn_os_alloc_buffer() allocates a memory area that is suitable for file + descriptors issued by burn_os_open_track_src(). + The buffer size may be rounded up for alignment + reasons. + +burn_os_free_buffer() delete a buffer obtained by burn_os_alloc_buffer(). + +Porting hints are marked by the text "PORTING:". +Send feedback to libburn-hackers@pykix.org . + +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + + +/** PORTING : ------- OS dependent headers and definitions ------ */ + +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <string.h> +#include <stdlib.h> + +#ifdef Libburn_os_has_statvfS +#include <sys/statvfs.h> +#endif /* Libburn_os_has_stavtfS */ + +#ifdef __linux +/* for ioctl(BLKGETSIZE) */ +#include <sys/ioctl.h> +#include <linux/fs.h> +#endif + +#ifdef __FreeBSD__ +#define Libburn_is_on_freebsD 1 +#endif +#ifdef __FreeBSD_kernel__ +#define Libburn_is_on_freebsD 1 +#endif +#ifdef Libburn_is_on_freebsD +/* To avoid ATAPI devices */ +#define Libburn_guess_freebsd_atapi_devicE 1 +/* To obtain size of disk-like devices */ +#include <sys/disk.h> /* DIOCGMEDIASIZE */ +#endif /* Libburn_is_on_freebsD */ + +#define Libburn_guess_freebsd_atapi_devicE 1 + +#ifdef sun +#define Libburn_is_on_solariS 1 +#endif +#ifdef __sun +#define Libburn_is_on_solariS 1 +#endif + +/* Proposal by Rocky Bernstein to avoid macro clashes with cdio_config.h */ +#define __CDIO_CONFIG_H__ 1 + +#include <cdio/cdio.h> +#include <cdio/logging.h> +#include <cdio/mmc.h> + +/* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + + +/* The waiting time before eventually retrying a failed SCSI command. + Before each retry wait Libburn_sg_linux_retry_incR longer than with + the previous one. +*/ +#define Libburn_sg_libcdio_retry_usleeP 100000 +#define Libburn_sg_libcdio_retry_incR 100000 + + +/** PORTING : ------ libburn portable headers and definitions ----- */ + +#include "transport.h" +#include "drive.h" +#include "sg.h" +#include "spc.h" +/* collides with symbols of <cdio/mmc.h> + #include "mmc.h" +*/ +#include "sbc.h" +#include "debug.h" +#include "toc.h" +#include "util.h" +#include "init.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); +int burn_drive_resolve_link(char *path, char adr[], + int *recursion_count, int flag); /* drive.c */ + +/* Whether to log SCSI commands: + bit0= log in /tmp/libburn_sg_command_log + bit1= log to stderr + bit2= flush every line +*/ +extern int burn_sg_log_scsi; + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Private definitions. Port only if needed by public functions. */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + + +/* Storage object is in libburn/init.c + whether to strive for exclusive access to the drive +*/ +extern int burn_sg_open_o_excl; + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Private functions. Port only if needed by public functions */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + + +static int sg_close_drive(struct burn_drive * d) +{ + CdIo_t *p_cdio; + + if (d->p_cdio != NULL) { + p_cdio = (CdIo_t *) d->p_cdio; + cdio_destroy(p_cdio); + d->p_cdio = NULL; + } + return 0; +} + + +static int sg_give_next_adr_raw(burn_drive_enumerator_t *idx, + char adr[], int adr_size, int initialize) +{ + char **pos; + int count = 0; + + if (initialize == 1) { + idx->pos = idx->ppsz_cd_drives = + cdio_get_devices(DRIVER_DEVICE); + if (idx->ppsz_cd_drives == NULL) + return 0; + + for (pos = idx->ppsz_cd_drives ; pos != NULL; pos++) { + if (*pos == NULL) + break; + count++; + } + + } else if (initialize == -1) { + if (idx->ppsz_cd_drives != NULL) + if (*(idx->ppsz_cd_drives) != NULL) + cdio_free_device_list(idx->ppsz_cd_drives); + idx->ppsz_cd_drives = NULL; + } + +#ifdef Libburn_guess_freebsd_atapi_devicE +try_next:; +#endif + + if (idx->pos == NULL) + return 0; + if (*(idx->pos) == NULL) + return 0; + +#ifdef Libburn_guess_freebsd_atapi_devicE + if (strncmp(*(idx->pos), "/dev/acd", 8) == 0) { + (idx->pos)++; + goto try_next; + } +#endif + + if ((ssize_t) strlen(*(idx->pos)) >= adr_size) + return -1; + strcpy(adr, *(idx->pos)); + (idx->pos)++; + return 1; +} + + +/* ----------------------------------------------------------------------- */ +/* 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, char *cdio_name, + 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 --------------- */ + + /* Transport adapter is libcdio */ + /* Adapter specific handles and data */ + out.p_cdio = NULL; + strcpy(out.libcdio_name, fname); + if (strlen(cdio_name) < sizeof(out.libcdio_name)) + strcpy(out.libcdio_name, cdio_name); + + /* 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); +} + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Public functions. These MUST be ported. */ +/* ------------------------------------------------------------------------ */ + + +/** Returns the id string of the SCSI transport adapter and eventually + needed operating system facilities. + This call is usable even if sg_initialize() was not called yet. In that + case a preliminary constant message might be issued if detailed info is + not available yet. + @param msg returns id string + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_id_string(char msg[1024], int flag) +{ + char *version_text; + + sprintf(msg, "sg-libcdio h%d with libcdio ", LIBCDIO_VERSION_NUM); + + #if LIBCDIO_VERSION_NUM < 83 + +LIBBURN_MISCONFIGURATION = 0; +INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_cdio_version_dot_h_TOO_OLD__NEED_libcdio_VERSION_NUM_83 = 0; +LIBBURN_MISCONFIGURATION_ = 0; + + #endif /* LIBCDIO_VERSION_NUM < 83 */ + + version_text = (char *) cdio_version_string; + strncat(msg, version_text, 800); + return 1; +} + + +/** Performs global initialization of the SCSI transport adapter and eventually + needed operating system facilities. Checks for compatibility of supporting + software components. + @param msg returns ids and/or error messages of eventual helpers + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_initialize(char msg[1024], int flag) +{ + int cdio_ver; + char *msg_pt; + + cdio_loglevel_default = CDIO_LOG_ASSERT; + + msg[0] = 0; + sg_id_string(msg, 0); + cdio_ver = libcdio_version_num; + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg , 0, 0); + if (cdio_ver < LIBCDIO_VERSION_NUM) { + strcat(msg, " ---> "); + msg_pt = msg + strlen(msg); + sprintf(msg_pt, + "libcdio TOO OLD: numeric version %d , need at least %d", + cdio_ver, LIBCDIO_VERSION_NUM); + libdax_msgs_submit(libdax_messenger, -1, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg_pt, 0, 0); + return 0; + } + return 1; +} + + +/** Performs global finalization of the SCSI transport adapter and eventually + needed operating system facilities. Releases globally aquired resources. + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_shutdown(int flag) +{ + return 1; +} + + +/** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of + struct burn_drive which are defined in os-*.h. + The eventual initialization of those components was made underneath + scsi_enumerate_drives(). + This will be called when a burn_drive gets disposed. + @param d the drive to be finalized + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_dispose_drive(struct burn_drive *d, int flag) +{ + return 1; +} + + +/** 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, recursion_count = 0, path_size = 4096; + char *path = NULL; +#ifdef Libburn_is_on_solariS + int l; +#endif + BURN_ALLOC_MEM(path, char, path_size); + + ret = sg_give_next_adr_raw(idx, adr, adr_size, initialize); + if (ret <= 0) + goto ex; + if ((ssize_t) strlen(adr) >= path_size) + goto ex; + +#ifdef Libburn_is_on_solariS + /* >>> provisory : preserve Solaris /dev/rdsk/cXtYdZs2 addresses */ + l = strlen(adr); + if (l >= 18) + if (strncmp(adr, "/dev/rdsk/c", 11) == 0 && adr[11] >= '0' && + adr[11] <= '9' && strcmp(adr + (l - 2), "s2") == 0) + {ret = 1; goto ex;} +#endif /* Libburn_is_on_solariS */ + + ret = burn_drive_resolve_link(adr, path, &recursion_count, 2); + if(ret > 0 && (ssize_t) strlen(path) < adr_size) + strcpy(adr, path); + ret = (ret >= 0); +ex: + BURN_FREE_MEM(path); + return ret; +} + + +/** 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, ret, i_bus_no = -1, recursion_count = 0; + int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1; + int buf_size = 4096; + char *buf = NULL, *target = NULL; +#ifdef Libburn_is_on_solariS + int l; +#endif + + BURN_ALLOC_MEM(buf, char, buf_size); + BURN_ALLOC_MEM(target, char, buf_size); + + while(1) { + ret = sg_give_next_adr_raw(&idx, buf, buf_size, initialize); + initialize = 0; + if (ret <= 0) + break; + ret = 1; + +#ifdef Libburn_is_on_solariS + /* >>> provisory : preserve Solaris /dev/rdsk/cXtYdZs2 */ + l = strlen(buf); + if (l >= 18) + if (strncmp(buf, "/dev/rdsk/c", 11) == 0 && + buf[11] >= '0' && buf[11] <= '9' && + strcmp(buf + (l - 2), "s2") == 0) + ret = 0; +#endif /* Libburn_is_on_solariS */ + + if (ret == 1) { + ret = burn_drive_resolve_link(buf, target, + &recursion_count,2); + } + if (ret <= 0) + strcpy(target, buf); + if (burn_drive_is_banned(target)) + continue; + sg_obtain_scsi_adr(buf, &i_bus_no, &i_host_no, + &i_channel_no, &i_target_no, &i_lun_no); + enumerate_common(target, buf, + i_bus_no, i_host_no, i_channel_no, + i_target_no, i_lun_no); + } + sg_give_next_adr(&idx, buf, buf_size, -1); + ret = 1; +ex:; + BURN_FREE_MEM(buf); + BURN_FREE_MEM(target); + return ret; +} + + +/** Tells whether 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->p_cdio != 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) +{ + CdIo_t *p_cdio; + char *am_eff, *msg = NULL, *am_wanted; + int os_errno, second_try = 0, ret; + + if (d->p_cdio != NULL) { + d->released = 0; + {ret = 1; goto ex;} + } + if (d->libcdio_name[0] == 0) /* just to be sure it is initialized */ + strcpy(d->libcdio_name, d->devname); + am_wanted = (burn_sg_open_o_excl & 63) ? "MMC_RDWR_EXCL" : "MMC_RDWR"; +try_to_open:; + p_cdio = cdio_open_am(d->libcdio_name, DRIVER_DEVICE, am_wanted); + if (p_cdio == NULL) { + BURN_ALLOC_MEM(msg, char, 4096); + os_errno = errno; + sprintf(msg, "Could not grab drive '%s'", d->devname); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020003, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, os_errno, 0); + {ret = 0; goto ex;} + } + am_eff = (char *) cdio_get_arg(p_cdio, "access-mode"); + if (strncmp(am_eff, "MMC_RDWR", 8) != 0) { + cdio_destroy(p_cdio); + if (!second_try) { + am_wanted = (burn_sg_open_o_excl & 63) ? + "MMC_RDWR" : "MMC_RDWR_EXCL"; + second_try = 1; + goto try_to_open; + } + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020003, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "libcdio provides no MMC_RDWR access mode", 0, 0); + {ret = 0; goto ex;} + } + + d->p_cdio = p_cdio; + d->released = 0; + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +/** PORTING: Is mainly about the call to sg_close_drive() and whether 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->p_cdio == NULL) + 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 sense_valid = 0, i, timeout_ms, sense_len; + int key = 0, asc = 0, ascq = 0, done = 0; + time_t start_time; + driver_return_code_t i_status; + unsigned int dxfer_len; + static FILE *fp = NULL; + mmc_cdb_t cdb = {{0, }}; + cdio_mmc_direction_t e_direction; + CdIo_t *p_cdio; + cdio_mmc_request_sense_t *sense_pt = NULL; + + c->error = 0; + memset(c->sense, 0, sizeof(c->sense)); + + if (d->p_cdio == NULL) { + return 0; + } + p_cdio = (CdIo_t *) d->p_cdio; + if (burn_sg_log_scsi & 1) { + if (fp == NULL) { + fp= fopen("/tmp/libburn_sg_command_log", "a"); + fprintf(fp, + "\n-----------------------------------------\n"); + } + } + if (burn_sg_log_scsi & 3) + scsi_log_cmd(c,fp,0); + + memcpy(cdb.field, c->opcode, c->oplen); + if (c->dir == TO_DRIVE) { + dxfer_len = c->page->bytes; + e_direction = SCSI_MMC_DATA_WRITE; + } else if (c->dir == FROM_DRIVE) { + if (c->dxfer_len >= 0) + dxfer_len = c->dxfer_len; + else + dxfer_len = BUFFER_SIZE; + e_direction = SCSI_MMC_DATA_READ; + /* touch page so we can use valgrind */ + memset(c->page->data, 0, BUFFER_SIZE); + } else { + dxfer_len = 0; + e_direction = SCSI_MMC_DATA_NONE; + } + + /* retry-loop */ + start_time = time(NULL); + if (c->timeout > 0) + timeout_ms = c->timeout; + else + timeout_ms = 200000; + for(i = 0; !done; i++) { + + memset(c->sense, 0, sizeof(c->sense)); + c->start_time = burn_get_time(0); + + i_status = mmc_run_cmd(p_cdio, timeout_ms, &cdb, e_direction, + dxfer_len, c->page->data); + + c->end_time = burn_get_time(0); + sense_valid = mmc_last_cmd_sense(p_cdio, &sense_pt); + if (sense_valid >= 18) { + memcpy(c->sense, (unsigned char *) sense_pt, + (size_t) sense_valid >= sizeof(c->sense) ? + sizeof(c->sense) : (size_t) sense_valid ); + spc_decode_sense(c->sense, 0, &key, &asc, &ascq); + } else + key = asc = ascq = 0; + if (sense_pt != NULL) + free(sense_pt); + +/* Regrettably mmc_run_cmd() does not clearly distinguish between transport + failure and SCSI error reply. + This reaction here would be for transport failure: + + if (i_status != 0 && i_status != DRIVER_OP_ERROR) { + 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 ((!sense_valid) || (key == 0 && asc == 0 && ascq == 0)) { + memset(c->sense, 0, sizeof(c->sense)); + if (i_status != 0) { /* set dummy sense */ + /*LOGICAL UNIT NOT READY, + CAUSE NOT REPORTABLE*/ + c->sense[0] = 0x70; /*Fixed format sense data*/ + c->sense[2] = 0x02; + c->sense[12] = 0x04; + done = 1; + } + } + if (key || asc || ascq) + sense_len = 18; + else + sense_len = 0; + done = scsi_eval_cmd_outcome(d, c, fp, c->sense, sense_len, + start_time, timeout_ms, i, 0); + if (d->cancel) + done = 1; + + } /* end of retry-loop */ + + 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) +{ + CdIo_t *p_cdio; + char *tuple; + + *bus_no = *host_no = *channel_no = *target_no = *lun_no = -1; + + p_cdio = cdio_open(path, DRIVER_DEVICE); + if (p_cdio == NULL) + return 0; + + /* Try whether a bus,host,channel,target,lun address tuple is + available */ + tuple = (char *) cdio_get_arg(p_cdio, "scsi-tuple"); + if (tuple != NULL) if (tuple[0]) { + sscanf(tuple, "%d,%d,%d,%d,%d", + bus_no, host_no, channel_no, target_no, lun_no); + } + + cdio_destroy(p_cdio); + return (*bus_no >= 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, ret; + 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); +} + + +#ifdef __FreeBSD__ +#define Libburn_guess_block_devicE 1 +#endif +#ifdef __FreeBSD_kernel__ +#define Libburn_guess_block_devicE 1 +#endif + +#ifdef Libburn_guess_block_devicE + +/* ts B00115 */ +/* The FreeBSD implementation of burn_os_is_2k_seekrw(). + On FreeBSD there are no block devices. +*/ +static int freebsd_is_2k_seekrw(char *path, int flag) +{ + struct stat stbuf; + char *spt; + int i, e; + + if (stat(path, &stbuf) == -1) + return 0; + if (S_ISREG(stbuf.st_mode)) + return 1; + if (!S_ISCHR(stbuf.st_mode)) + return 0; + spt = strrchr(path, '/'); + if (spt == NULL) + spt = path; + else + spt++; + e = strlen(spt); + for (i = strlen(spt) - 1; i > 0; i--) + if (spt[i] >= '0' && spt[i] <= '9') + e = i; + if (strncmp(spt, "da", e) == 0) /* SCSI disk. E.g. USB stick. */ + return 1; + if (strncmp(spt, "cd", e) == 0) /* SCSI CD drive might be writeable. */ + return 1; + if (strncmp(spt, "ad", e) == 0) /* IDE hard drive */ + return 1; + if (strncmp(spt, "acd", e) == 0) /* IDE CD drive might be writeable */ + return 1; + if (strncmp(spt, "fd", e) == 0) /* Floppy disk */ + return 1; + if (strncmp(spt, "fla", e) == 0) /* Flash drive */ + return 1; + return 0; +} + +#endif /* Libburn_guess_block_devicE */ + + +/* Return 1 if the given path leads to a regular file or a device that can be + seeked, read, and possibly written with 2 kB granularity. +*/ +int burn_os_is_2k_seekrw(char *path, int flag) +{ +#ifdef Libburn_guess_block_devicE + return freebsd_is_2k_seekrw(path, flag); +#else + struct stat stbuf; + + if (stat(path, &stbuf) == -1) + return 0; + if (S_ISREG(stbuf.st_mode)) + return 1; + if (S_ISBLK(stbuf.st_mode)) + return 1; + return 0; +#endif /* ! Libburn_guess_block_devicE */ +} + + +/** 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 write_start, off_t *bytes) +{ + struct stat stbuf; + +#ifdef Libburn_os_has_statvfS + struct statvfs vfsbuf; +#endif + + char *testpath = NULL, *cpt; + off_t add_size = 0; + int ret; + + BURN_ALLOC_MEM(testpath, char, 4096); + testpath[0] = 0; + 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) + {ret = -1; goto ex;} + +#ifdef __linux + + /* GNU/Linux specific determination of block device size */ + } else if(S_ISBLK(stbuf.st_mode)) { + int open_mode = O_RDONLY | O_BINARY, fd; + long blocks; + + blocks = *bytes / 512; + fd = open(path, open_mode); + if (fd == -1) + {ret = -2; goto ex;} + ret = ioctl(fd, BLKGETSIZE, &blocks); + close(fd); + if (ret == -1) + {ret = -2; goto ex;} + *bytes = ((off_t) blocks) * (off_t) 512; + +#endif /* __linux */ + +#ifdef Libburn_is_on_freebsD + + } else if(S_ISCHR(stbuf.st_mode)) { + int fd; + + fd = open(path, O_RDONLY | O_BINARY); + if (fd == -1) + {ret = -2; goto ex;} + ret = ioctl(fd, DIOCGMEDIASIZE, &add_size); + close(fd); + if (ret == -1) + {ret = -2; goto ex;} + *bytes = add_size; + +#endif /* Libburn_is_on_freebsD */ + +#ifdef Libburn_is_on_solariS + + } else if(S_ISBLK(stbuf.st_mode)) { + int open_mode = O_RDONLY | O_BINARY, fd; + + fd = open(path, open_mode); + if (fd == -1) + {ret = -2; goto ex;} + *bytes = lseek(fd, 0, SEEK_END); + close(fd); + if (*bytes == -1) { + *bytes = 0; + {ret = 0; goto ex;} + } + +#endif /* Libburn_is_on_solariS */ + + } else if(S_ISREG(stbuf.st_mode)) { + add_size = burn_sparse_file_addsize(write_start, &stbuf); + strcpy(testpath, path); + } else + {ret = 0; goto ex;} + + if (testpath[0]) { + +#ifdef Libburn_os_has_statvfS + + if (statvfs(testpath, &vfsbuf) == -1) + {ret = -2; goto ex;} + *bytes = add_size + ((off_t) vfsbuf.f_frsize) * + (off_t) vfsbuf.f_bavail; + +#else /* Libburn_os_has_statvfS */ + + {ret = 0; goto ex;} + +#endif /* ! Libburn_os_has_stavtfS */ + + } + ret = 1; +ex:; + BURN_FREE_MEM(testpath); + return ret; +} + + +/* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ + +#ifdef Libburn_read_o_direcT + + /* No special O_DIRECT-like precautions are implemented here */ + +#endif /* Libburn_read_o_direcT */ + + +int burn_os_open_track_src(char *path, int open_flags, int flag) +{ + int fd; + + fd = open(path, open_flags | O_BINARY); + return fd; +} + + +void *burn_os_alloc_buffer(size_t amount, int flag) +{ + void *buf = NULL; + + buf = calloc(1, amount); + return buf; +} + + +int burn_os_free_buffer(void *buffer, size_t amount, int flag) +{ + if (buffer == NULL) + return 0; + free(buffer); + return 1; +} + diff --git a/trunk/libburn/sg-linux.c b/trunk/libburn/sg-linux.c new file mode 100644 index 0000000..7b7739c --- /dev/null +++ b/trunk/libburn/sg-linux.c @@ -0,0 +1,2526 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + + +/* <<< ts A91112 : experiments to get better speed with USB +#define Libburn_sgio_as_growisofS 1 +*/ + + +/* + +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: GNU/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_id_string() returns an id string of the SCSI transport adapter. + It may be called before initialization but then may + return only a preliminary id. + +sg_initialize() performs global initialization of the SCSI transport + adapter and eventually needed operating system + facilities. Checks for compatibility of supporting + software components. + +sg_shutdown() performs global finalizations and releases golbally + aquired resources. + +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_dispose_drive() finalizes adapter specifics of struct burn_drive + on destruction. Releases resources which were aquired + underneath scsi_enumerate_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_is_2k_seekrw() tells whether the given path leads to a file object + that can be used in 2 kB granularity by lseek(2) and + read(2), and possibly write(2) if not read-only. + E.g. a USB stick or a hard disk. + +burn_os_stdio_capacity() estimates the emulated media space of stdio-drives. + +burn_os_open_track_src() opens a disk file in a way that allows best + throughput with file reading and/or SCSI write command + transmission. + +burn_os_alloc_buffer() allocates a memory area that is suitable for file + descriptors issued by burn_os_open_track_src(). + The buffer size may be rounded up for alignment + reasons. + +burn_os_free_buffer() delete a buffer obtained by burn_os_alloc_buffer(). + + +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. + +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + + +/** PORTING : ------- OS dependent headers and definitions ------ */ + + +#ifdef Libburn_read_o_direcT +# ifndef _GNU_SOURCE +# define _GNU_SOURCE +# endif +#endif /* Libburn_read_o_direcT */ + +#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> + +/* for mmap() */ +#include <sys/mman.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, + If no such assertion is made, then this adapter performs INQUIRE and + looks for first reply byte 0x05. + + 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; + + +/* The waiting time before eventually retrying a failed SCSI command. + Before each retry wait Libburn_sg_linux_retry_incR longer than with + the previous one. +*/ +#define Libburn_sg_linux_retry_usleeP 100000 +#define Libburn_sg_linux_retry_incR 100000 + + +/** PORTING : ------ libburn portable headers and definitions ----- */ + +#include "libburn.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 "init.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 fd_in, int bus_no, int host_no, + int channel_no, int target_no, int lun_no); + +static int sg_obtain_scsi_adr_fd(char *path, int fd_in, + int *bus_no, int *host_no, int *channel_no, + int *target_no, int *lun_no); + + +/* ts A60813 : storage objects are in libburn/init.c + whether to use O_EXCL with open(2) of devices + whether 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 + whether to use O_NOBLOCK with open(2) on devices + whether 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 A91111 : + whether to log SCSI commands: + bit0= log in /tmp/libburn_sg_command_log + bit1= log to stderr + bit2= flush every line +*/ +extern int burn_sg_log_scsi; + +/* 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; + } +} + + +/* ts A80701 */ +/* This cares for the case that no /dev/srNN but only /dev/scdNN exists. + A theoretical case which has its complement in SuSE 10.2 having + /dev/sr but not /dev/scd. +*/ +static int sg_exchange_scd_for_sr(char *fname, int flag) +{ + struct stat stbuf; + char scd[17], *msg = NULL; + + if (burn_sg_use_family != 0 || strncmp(fname, "/dev/sr", 7)!=0 || + strlen(fname)>9 || strlen(fname)<8) + return 2; + if (fname[7] < '0' || fname[7] > '9') + return 2; + if (fname [8] != 0 && (fname[7] < '0' || fname[7] > '9')) + return 2; + if (stat(fname, &stbuf) != -1) + return 2; + strcpy(scd, "/dev/scd"); + strcpy(scd + 8, fname + 7); + if (stat(scd, &stbuf) == -1) + return 2; + msg = calloc(strlen(scd) + strlen(fname) + 80, 1); + if (msg != NULL) { + sprintf(msg, "%s substitutes for non-existent %s", scd, fname); + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + free(msg); + } + strcpy(fname, scd); + return 1; +} + + +/* ts B11110 */ +/* This is an early stage version of scsi_log_cmd. + >>> It will become obsolete when the /tmp file handler is moved into + >>> scsi_log_command(). +*/ +static int sgio_log_cmd(unsigned char *cmd, int cmd_len, FILE *fp_in, int flag) +{ + FILE *fp = fp_in; + int ret; + + /* >>> ts B11110 : move this into scsi_log_command() */ + if (fp == NULL && (burn_sg_log_scsi & 1)) { + fp= fopen("/tmp/libburn_sg_command_log", "a"); + fprintf(fp, "\n=========================================\n"); + } + + ret = scsi_log_command(cmd, cmd_len, NO_TRANSFER, NULL, 0, fp, flag); + if (fp_in == NULL && fp != NULL) + fclose(fp); + return ret; +} + + +/* ts B11110 */ +static int sgio_log_reply(unsigned char *opcode, int data_dir, + unsigned char *data, int dxfer_len, + void *fp_in, unsigned char sense[18], int sense_len, + double duration, int flag) +{ + int ret; + + ret = scsi_log_reply(opcode, data_dir, data, dxfer_len, fp_in, + sense, sense_len, duration, flag); + return ret; +} + + +static int sgio_test(int fd) +{ + unsigned char test_ops[] = { 0, 0, 0, 0, 0, 0 }; + sg_io_hdr_t s; + int ret; + double c_start_time, c_end_time; + + 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; + + sgio_log_cmd(s.cmdp, s.cmd_len, NULL, 0); + + c_start_time = burn_get_time(0); + ret= ioctl(fd, SG_IO, &s); + c_end_time = burn_get_time(0); + + sgio_log_reply(s.cmdp, NO_TRANSFER, NULL, 0, NULL, + (unsigned char *) (s.sbp), + s.sb_len_wr, c_end_time - c_start_time, 0); + return ret; +} + + +static int sgio_inquiry_cd_drive(int fd, char *fname) +{ + unsigned char test_ops[] = { 0x12, 0, 0, 0, 36, 0 }; + sg_io_hdr_t s; + struct buffer *buf = NULL; + unsigned char *sense = NULL; + char *msg = NULL, *msg_pt; + int ret = 0, i; + double c_start_time, c_end_time; + + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(sense, unsigned char, 128); + BURN_ALLOC_MEM(msg, char, strlen(fname) + 1024); + + memset(&s, 0, sizeof(sg_io_hdr_t)); + s.interface_id = 'S'; + s.dxfer_direction = SG_DXFER_FROM_DEV; + s.cmd_len = 6; + s.cmdp = test_ops; + s.mx_sb_len = 32; + s.sbp = sense; + s.timeout = 30000; + s.dxferp = buf; + s.dxfer_len = 36; + s.usr_ptr = NULL; + + sgio_log_cmd(s.cmdp, s.cmd_len, NULL, 0); + + c_start_time = burn_get_time(0); + ret = ioctl(fd, SG_IO, &s); + c_end_time = burn_get_time(0); + if (ret == -1) { + sprintf(msg, + "INQUIRY on '%s' : ioctl(SG_IO) failed , errno= %d", + fname, errno); + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + goto ex; + } + + sgio_log_reply(s.cmdp, FROM_DRIVE, buf->data, s.dxfer_len, NULL, + (unsigned char *) (s.sbp), + s.sb_len_wr, c_end_time - c_start_time, 0); + + if (s.sb_len_wr > 0 || s.host_status != Libburn_sg_host_oK || + s.driver_status != Libburn_sg_driver_oK) { + sprintf(msg, "INQUIRY failed on '%s' : host_status= %hd , driver_status= %hd", fname, s.host_status, s.driver_status); + if (s.sb_len_wr > 0) { + sprintf(msg + strlen(msg), " , sense data="); + msg_pt = msg + strlen(msg); + for (i = 0 ; i < s.sb_len_wr; i++) + sprintf(msg_pt + i * 3, " %2.2X", + ((unsigned char *) (s.sbp))[i]); + } + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + ret = -1; + goto ex; + } + ret = 0; + if (buf->data[0] == 0x5) { + /* Peripheral qualifier 0, device type 0x5 = CD/DVD device. + SPC-3 tables 82 and 83 */ + ret = 1; + } else { + sprintf(msg, "INQUIRY on '%s' : byte 0 = 0x%2.2X", + fname, buf->data[0]); + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + } + +ex:; + BURN_FREE_MEM(msg); + BURN_FREE_MEM(sense); + BURN_FREE_MEM(buf); + return ret; +} + + +/* ts A60924 */ +static int sg_handle_busy_device(char *fname, int os_errno) +{ + char *msg = NULL; + struct stat stbuf; + int looks_like_hd= 0, fd, ret; + + BURN_ALLOC_MEM(msg, char, 4096); + + /* ts A80713 : + check existence of /dev/hdX1 as hint for hard disk rather than CD + Hint by Giulio Orsero: check /proc/ide/hdX/media for "disk" + */ + if (strncmp(fname, "/dev/hd", 7)==0) { + sprintf(msg, "%s1", fname); + if (stat(msg, &stbuf) != -1) + looks_like_hd= 1; + sprintf(msg, "/proc/ide/hd%c/media", fname[7]); + fd = open(msg, O_RDONLY); + if (fd != -1) { + ret = read(fd, msg, 10); + if (ret < 0) + ret = 0; + msg[ret]= 0; + close(fd); + if (strncmp(msg, "disk\n", 5) == 0 || + strcmp(msg, "disk") == 0) + looks_like_hd= 2; + else if (strncmp(msg, "cdrom\n", 6) == 0 || + strcmp(msg, "cdrom") == 0) + looks_like_hd= 0; + } + } + + /* 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 */ + if (looks_like_hd == 2) { /* is surely hard disk */ + ; + } else if (looks_like_hd) { + sprintf(msg, "Could not examine busy device '%s'", fname); + libdax_msgs_submit(libdax_messenger, -1, 0x0002015a, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_LOW, + msg, os_errno, 0); + sprintf(msg, + "Busy '%s' seems to be a hard disk, as '%s1' exists. But better check.", + fname, fname); + libdax_msgs_submit(libdax_messenger, -1, 0x0002015b, + LIBDAX_MSGS_SEV_HINT, LIBDAX_MSGS_PRIO_LOW, + msg, 0, 0); + + } else { + 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); + } + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +/* 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 = NULL; + + if(*fd < 0) + {ret = 0; goto ex;} + BURN_ALLOC_MEM(msg, char, 4096 + 100); + +#ifdef CDROM_MEDIA_CHANGED_disabled_because_not_helpful +#ifdef CDSL_CURRENT + /* ts A80217 : wondering whether the os knows about our activities */ + ret = ioctl(*fd, CDROM_MEDIA_CHANGED, CDSL_CURRENT); + sprintf(msg, "ioctl(CDROM_MEDIA_CHANGED) == %d", ret); + libdax_msgs_submit(libdax_messenger, driveno, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); + +#ifdef BLKFLSBUF_disabled_because_not_helpful + ret = ioctl(*fd, BLKFLSBUF, 0); + sprintf(msg, "ioctl(BLKFLSBUF) == %d", ret); + os_errno = 0; + if(ret == -1) + os_errno = errno; + libdax_msgs_submit(libdax_messenger, driveno, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno,0); +#endif /* BLKFLSBUF */ + +#endif /* CDSL_CURRENT */ +#endif /* CDROM_MEDIA_CHANGED */ + + ret = close(*fd); + *fd = -1337; + if(ret != -1) { + /* ts A70409 : DDLP-B */ + /* >>> release single lock on fname */ + {ret = 1; goto ex;} + } + 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); + ret = 0; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +/* 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 */ +/* @param scan_mode 0= open for drivce aquiration + 1= open for scanning with guessed names + 2= open for scanning with /proc/sys/dev/cdrom/info names +*/ +static int sg_open_drive_fd(char *fname, int scan_mode) +{ + int open_mode = O_RDWR, fd, tries= 0, is_std_adr, report_as_note = 0; + char msg[81]; + struct stat stbuf; + + /* 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 B10904: O_NONBLOCK is prescribed by <linux/cdrom.h> + 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)); +*/ + +try_open:; + fd = open(fname, open_mode); + if (fd == -1) { +/* <<< debugging + fprintf(stderr, + "\nlibburn: experimental: fname= %s , errno= %d\n", + fname,errno); +*/ + if (errno == EBUSY) { + tries++; + +/* <<< debugging + fprintf(stderr, + "\nlibburn_DEBUG: EBUSY , tries= %d\n", tries); +*/ + + if (tries < 4) { + usleep(2000000); + goto try_open; + } + sg_handle_busy_device(fname, errno); + return -1; + + } + sprintf(msg, "Failed to open device '%s'",fname); + if (scan_mode) { + is_std_adr = (strncmp(fname, "/dev/sr", 7) == 0 || + strncmp(fname, "/dev/scd", 8) == 0); + if(scan_mode == 1 && is_std_adr && + stat(fname, &stbuf) != -1) + report_as_note = 1; + else if(scan_mode == 2 && (!is_std_adr) && + stat(fname, &stbuf) != -1) + report_as_note = 1; + if (report_as_note) + libdax_msgs_submit(libdax_messenger, -1, + 0x0002000e, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, + msg, errno, 0); + } else { + 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 = NULL, fname[40]; + struct stat stbuf; + dev_t last_rdev = 0, path_rdev; + + static char tldev[][20]= {"/dev/sr%d", "/dev/scd%d", "/dev/sg%d", ""}; + /* ts A70609: removed "/dev/st%d" */ + + if (strlen(path) > BURN_MSGS_MESSAGE_LEN - 160) + {ret = 0; goto ex;} + + BURN_ALLOC_MEM(msg, char, BURN_MSGS_MESSAGE_LEN); + + if(stat(path, &stbuf) == -1) + {ret = 0; goto ex;} + path_rdev = stbuf.st_rdev; + + sg_select_device_family(); + if (linux_sg_device_family[0] == 0) + {ret = 1; goto ex;} + + if(host_no < 0 || id_no < 0 || channel_no < 0 || lun_no < 0) + {ret = 2; goto ex;} + 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; + } + } + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +failed:; + sg_release_siblings(sibling_fds, sibling_fnames, sibling_count); + ret = 0; + goto ex; +} + + +/* ts A80731 */ +static int is_ata_drive(char *fname, int fd_in) +{ + int fd; + struct hd_driveid tm; + + if (fd_in >= 0) + fd = fd_in; + else + 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)); + return 0; + } + + memset(&tm, 0, sizeof(tm)); + 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"); + if (fd_in < 0) + sg_close_drive_fd(fname, -1, &fd, 0); + return 0; + } + + /* 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)); + if (fd_in < 0) + sg_close_drive_fd(fname, -1, &fd, 0); + return 0; + } + if (fd_in >= 0) + return 1; + 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)); + return 0; + } + return 1; +} + + +static int is_scsi_drive(char *fname, int fd_in, int *bus_no, int *host_no, + int *channel_no, int *target_no, int *lun_no) +{ + int fd = -1, sid_ret = 0, ret, fail_sev_sorry = 0; + struct sg_scsi_id sid; + int *sibling_fds = NULL, sibling_count= 0; + typedef char burn_sg_sibling_fname[BURN_OS_SG_MAX_NAMELEN]; + burn_sg_sibling_fname *sibling_fnames = NULL; + + BURN_ALLOC_MEM(sibling_fds, int, BURN_OS_SG_MAX_SIBLINGS); + BURN_ALLOC_MEM(sibling_fnames, burn_sg_sibling_fname, + BURN_OS_SG_MAX_SIBLINGS); + + if (fd_in >= 0) + fd = fd_in; + else + 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)); + {ret = 0; goto ex;} + } + 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)); + + {ret = 0; goto ex;} + } + +#ifdef CDROM_DRIVE_STATUS + /* 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 */ + + } + + if (sid_ret == -1) { + /* ts B11109 : Try device type from INQUIRY byte 0 */ + if (sgio_inquiry_cd_drive(fd, fname) == 1) { + sid_ret = 0; + sid.scsi_type = TYPE_ROM; + } + } + + +#ifdef SCSI_IOCTL_GET_BUS_NUMBER + /* Hearsay A61005 */ + if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, bus_no) == -1) + *bus_no = -1; +#endif + + fail_sev_sorry = (sid.scsi_type == TYPE_ROM); + 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); + {ret = 0; goto ex;} + } + + if (sid_ret == -1 || sid.scsi_id < 0) { + /* ts A61211 : employ a more general ioctl */ + /* ts B11001 : re-use fd */ + ret = sg_obtain_scsi_adr_fd(fname, fd, 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_fd() failed\n"); + {ret = 0; goto ex;} + } + } + + /* 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); + {ret = 0; goto ex;} + } + /* 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 + *host_no= sid.host_no; + *channel_no= sid.channel; + *target_no= sid.scsi_id; + *lun_no= sid.lun; + ret = 1; +ex:; + if (fd_in < 0 && fd >= 0) { + if (sg_close_drive_fd(fname, -1, &fd, fail_sev_sorry) <= 0) { + if (linux_sg_enumerate_debug) + fprintf(stderr, + "cannot close properly, errno=%d '%s'\n", + errno, strerror(errno)); + if (ret > 0) + ret = 0; + } + } + BURN_FREE_MEM(sibling_fds); + BURN_FREE_MEM(sibling_fnames); + return ret; +} + + +/* @param flag bit0= do not complain about failure to open /dev/sr /dev/scd */ +static int sg_open_for_enumeration(char *fname, int flag) +{ + int fd; + + fd = sg_open_drive_fd(fname, 1 + (flag & 1)); + if (fd < 0) { + if (linux_sg_enumerate_debug || linux_ata_enumerate_verbous) + fprintf(stderr, "open failed, errno=%d '%s'\n", + errno, strerror(errno)); + return -1; + } + return fd; +} + + +/** Speciality of GNU/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) +{ + int ret, i, fd = -1; + 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_for_enumeration(fname, 0); + if (fd < 0) + continue; + ret = is_ata_drive(fname, fd); + if (ret < 0) + break; + if (ret == 0) + continue; + if (linux_ata_enumerate_verbous) + fprintf(stderr, "accepting as drive without SCSI address\n"); + enumerate_common(fname, fd, -1, -1, -1, -1, -1); + } +} + + +/** Detects (probably emulated) SCSI drives */ +static void sg_enumerate(void) +{ + int i, ret, fd = -1; + int bus_no= -1, host_no= -1, channel_no= -1, target_no= -1, lun_no= -1; + char fname[17]; + + 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); + + /* ts A80702 */ + sg_exchange_scd_for_sr(fname, 0); + + 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; + } + fd = sg_open_for_enumeration(fname, 0); + if (fd < 0) + continue; + + ret = is_scsi_drive(fname, fd, &bus_no, &host_no, &channel_no, + &target_no, &lun_no); + if (ret < 0) + break; + if (ret == 0) + continue; + if (linux_sg_enumerate_debug) + fprintf(stderr, "accepting as SCSI %d,%d,%d,%d bus=%d\n", + host_no, channel_no, target_no, lun_no, bus_no); + enumerate_common(fname, fd, bus_no, host_no, channel_no, + target_no, lun_no); + + } +} + + + +/* ts A80805 : eventually produce the other official name of a device file */ +static int fname_other_name(char *fname, char other_name[80], int flag) +{ + if(strncmp(fname, "/dev/sr", 7) == 0 && + (fname[7] >= '0' && fname[7] <= '9') && + (fname[8] == 0 || + (fname[8] >= '0' && fname[8] <= '9' && fname[9] == 0))) { + sprintf(other_name, "/dev/scd%s", fname + 7); + return 1; + } + if(strncmp(fname, "/dev/scd", 8) == 0 && + (fname[8] >= '0' && fname[8] <= '9') && + (fname[9] == 0 || + (fname[9] >= '0' && fname[9] <= '9' && fname[10] == 0))) { + sprintf(other_name, "/dev/sr%s", fname + 8); + return 1; + } + return 0; +} + + +/* ts A80805 */ +static int fname_drive_is_listed(char *fname, int flag) +{ + char other_fname[80]; + + if (burn_drive_is_listed(fname, NULL, 0)) + return 1; + if (fname_other_name(fname, other_fname, 0) > 0) + if (burn_drive_is_listed(other_fname, NULL, 0)) + return 2; + return 0; +} + + +/* ts A80731 : Directly open the given address. + @param flag bit0= do not complain about missing file + bit1= do not check whether drive is already listed + bit2= do not complain about failure to open /dev/sr /dev/scd +*/ +static int fname_enumerate(char *fname, int flag) +{ + int is_ata= 0, is_scsi= 0, ret, fd = -1; + int bus_no= -1, host_no= -1, channel_no= -1, target_no= -1, lun_no= -1; + char *msg = NULL; + struct stat stbuf; + + BURN_ALLOC_MEM(msg, char, BURN_DRIVE_ADR_LEN + 80); + + if (!(flag & 2)) + if (fname_drive_is_listed(fname, 0)) + {ret = 2; goto ex;} + if (stat(fname, &stbuf) == -1) { + sprintf(msg, "File object '%s' not found", fname); + if (!(flag & 1)) + libdax_msgs_submit(libdax_messenger, -1, 0x0002000b, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + {ret = -1; goto ex;} + } + + fd = sg_open_for_enumeration(fname, !!(flag & 4)); + if (fd < 0) + {ret = 0; goto ex;} + is_ata = is_ata_drive(fname, fd); + if (is_ata < 0) + {ret = -1; goto ex;} + if (!is_ata) + is_scsi = is_scsi_drive(fname, fd, &bus_no, &host_no, + &channel_no, &target_no, &lun_no); + if (is_scsi < 0) + {ret = -1; goto ex;} + if (is_ata == 0 && is_scsi == 0) + {ret = 0; goto ex;} + + if (linux_sg_enumerate_debug) + fprintf(stderr, + "(single) accepting as SCSI %d,%d,%d,%d bus=%d\n", + host_no, channel_no, target_no, lun_no, bus_no); + + enumerate_common(fname, fd, bus_no, host_no, channel_no, + target_no, lun_no); + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +/* ts A80731 : Directly open the given address from a single-item whitlist */ +static int single_enumerate(int flag) +{ + int ret, wl_count; + char *fname, *msg = NULL; + + wl_count= burn_drive_whitelist_count(); + if (wl_count != 1) + {ret = 0; goto ex;} + fname= burn_drive_whitelist_item(0, 0); + if (fname == NULL) + {ret = 0; goto ex;} + ret = fname_enumerate(fname, 2); + if (ret <= 0) { + BURN_ALLOC_MEM(msg, char, BURN_DRIVE_ADR_LEN + 80); + sprintf(msg, "Cannot access '%s' as SG_IO CDROM drive", fname); + libdax_msgs_submit(libdax_messenger, -1, 0x0002000a, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + ret = -1; + } +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +/* ts A80801 : looking up drives listed in /proc/sys/dev/cdrom/info line like: + drive name: sr1 hdc hda sr0 + @parm flag bit0= release list memory and exit +*/ +static int proc_sys_dev_cdrom_info(char ***list, int *count, int flag) +{ + FILE *fp; + char *line = NULL, *fname = NULL, *cpt, *retpt, *list_data; + int maxl= 0, pass, i, line_size = 1024, ret; + + BURN_ALLOC_MEM(line, char, line_size); + BURN_ALLOC_MEM(fname, char, line_size + 5); + + if (*list != NULL) { + if ((*list)[0] != NULL) + free((*list)[0]); + free(*list); + *list = NULL; + *count = 0; + } + if (flag & 1) + {ret = 1; goto ex;} + + *count = 0; + sg_evaluate_kernel(); + if (sg_kernel_age < 2) /* addresses are not suitable for kernel 2.4 */ + {ret = 1; goto ex;} + fp = fopen("/proc/sys/dev/cdrom/info", "r"); + if (fp == NULL) + {ret = 0; goto ex;} + while (1) { + retpt = fgets(line, line_size, fp); + if (retpt == NULL) + break; + if(strncmp(line, "drive name:", 11) == 0) + break; + } + fclose(fp); + if (retpt == NULL) + {ret = 0; goto ex;} + strcpy(fname, "/dev/"); + for(pass = 0; pass < 2; pass++) { + *count = 0; + cpt = line + 11; + while (*cpt != 0) { + for(; *cpt == ' ' || *cpt == '\t'; cpt++); + if (*cpt == 0 || *cpt == '\n') + break; + sscanf(cpt, "%s", fname + 5); + if ((int) strlen(fname) > maxl) + maxl = strlen(fname); + if (pass == 1) + strcpy((*list)[*count], fname); + (*count)++; + for(cpt++; *cpt != ' ' && *cpt != '\t' + && *cpt != 0 && *cpt != '\n'; cpt++); + } + if (pass == 0) { + list_data = calloc(*count + 1, maxl+1); + *list = calloc(*count + 1, sizeof(char *)); + if(list_data == NULL || *list == NULL) { + libdax_msgs_submit(libdax_messenger, -1, + 0x00000003, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Out of virtual memory", 0, 0); + if (list_data != NULL) + free(list_data); + if (*list != NULL) + free((char *) *list); + {ret = -1; goto ex;} + } + for (i = 0; i <= *count; i++) + (*list)[i] = list_data + i * (maxl + 1); + } + } + ret = 1; +ex:; + BURN_FREE_MEM(line); + BURN_FREE_MEM(fname); + return ret; +} + + +static int add_proc_info_drives(int flag) +{ + int ret, list_count, count = 0, i; + char **list= NULL; + + if (burn_sg_use_family != 0) + return(1); /* Looking only for sr resp. scd resp. sg */ + + ret = proc_sys_dev_cdrom_info(&list, &list_count, 0); + if (ret <= 0) + return ret; + for (i = 0; i < list_count; i++) { + if (burn_drive_is_banned(list[i])) + continue; + ret = fname_enumerate(list[i], 1 | 4); + if (ret == 1) + count++; + } + proc_sys_dev_cdrom_info(&list, &list_count, 1); /* free memory */ + return 1 + count; +} + + +/* 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 fd_in, 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 GNU/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; + if (fd_in >= 0) + out.fd = fd_in; + + /* Finally register drive and inquire drive information. + out is an invalid copy afterwards. Do not use it for anything. + */ + burn_drive_finish_enum(&out); +} + + +/* ts A61115 */ +/* ------------------------------------------------------------------------ */ +/* PORTING: Public functions. These MUST be ported. */ +/* ------------------------------------------------------------------------ */ + + +/** Returns the id string of the SCSI transport adapter and eventually + needed operating system facilities. + This call is usable even if sg_initialize() was not called yet. In that + case a preliminary constant message might be issued if detailed info is + not available yet. + @param msg returns id string + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_id_string(char msg[1024], int flag) +{ + strcpy(msg, "internal GNU/Linux SG_IO adapter sg-linux"); + return 1; +} + + +/** Performs global initialization of the SCSI transport adapter and eventually + needed operating system facilities. Checks for compatibility supporting + software components. + @param msg returns ids and/or error messages of eventual helpers + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_initialize(char msg[1024], int flag) +{ + return sg_id_string(msg, 0); +} + + +/** Performs global finalization of the SCSI transport adapter and eventually + needed operating system facilities. Releases globally aquired resources. + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_shutdown(int flag) +{ + return 1; +} + + +/** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of + struct burn_drive which are defined in os-*.h. + The eventual initialization of those components was made underneath + scsi_enumerate_drives(). + This will be called when a burn_drive gets disposed. + @param d the drive to be finalized + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_dispose_drive(struct burn_drive *d, int flag) +{ + return 1; +} + + +/** PORTING: + In this GNU/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, i; + char other_name[80]; + + if (initialize == -1) { + proc_sys_dev_cdrom_info(&(idx->info_list), &(idx->info_count), + 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->pos = -1; + idx->info_count= 0; + idx->info_list= NULL; + proc_sys_dev_cdrom_info(&(idx->info_list), &(idx->info_count), + 0); + } + (idx->pos)++; + if (idx->pos >= sg_limit) + goto next_ata; + if (adr_size < 11) + return -1; + sprintf(adr, linux_sg_device_family, idx->pos); + + sg_exchange_scd_for_sr(adr, 0); + goto return_1_pre_proc; + +next_ata:; + baseno += sg_limit; + if (idx->pos - baseno >= ata_limit) + goto next_proc_info; + if (adr_size < 9) + return -1; + sprintf(adr, linux_ata_device_family, 'a' + (idx->pos - baseno)); + goto return_1_pre_proc; + +next_proc_info:; + baseno += ata_limit; + for (i = 0; i < idx->info_count; i++) { + if ((idx->info_list)[i][0] == 0) + continue; + if (baseno == idx->pos) { + if (adr_size < (int) strlen((idx->info_list)[i]) + 1) + return -1; + strcpy(adr, (idx->info_list)[i]); + return 1; + } + baseno++; + } + return 0; + +return_1_pre_proc:; + for (i = 0; i < idx->info_count; i++) { + if (strcmp((idx->info_list)[i], adr) == 0) + (idx->info_list)[i][0] = 0; + if (fname_other_name(adr, other_name, 0) > 0) + if (strcmp((idx->info_list)[i], other_name) == 0) + (idx->info_list)[i][0] = 0; + } + return 1; +} + + +/** 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 GNU/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) +{ + int ret; + + /* Direct examination of eventually single whitelisted name */ + ret = single_enumerate(0); + if (ret < 0) + return -1; + if (ret > 0) + return 1; + + sg_enumerate(); + ata_enumerate(); + add_proc_info_drives(0); + 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; + int max_tries = 3, tries = 0; + + /* 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)) { + char msg[120]; + +/* >>> SINGLE_OPEN : This case should be impossible now, since enumeration + transfers the fd from scanning to drive. + So if close-wait-open is desired, then it has to + be done unconditionally. +*/ + +#ifndef Libburn_udev_wait_useC +#define Libburn_udev_wait_useC 100000 +#endif + +#ifndef Libburn_udev_extra_open_cyclE + + if (Libburn_udev_wait_useC > 0) { + /* ts B10921 : workaround for udev which might get + a kernel event from open() and might + remove links if it cannot inspect the + drive. This waiting period shall allow udev + to act after it was woken up by the drive scan + activities. + */ + sprintf(msg, + "To avoid collision with udev: Waiting %lu usec before grabbing", + (unsigned long) Libburn_udev_wait_useC); + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + usleep(Libburn_udev_wait_useC); + } + +#endif /* Libburn_udev_extra_open_cyclE */ + + +try_open:; + /* 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; + +#ifdef Libburn_udev_extra_open_cyclE + + /* ts B10920 : workaround for udev which might get + a kernel event from open() and might + remove links if it cannot inspect the + drive. + ts B10921 : this is more obtrusive than above waiting + before open(). The drive scan already has + opened and closed the drive several times. + So it seems to be merely about giving an + opportunity to udev, before long term grabbing + happens. + */ + if (fd >= 0 && Libburn_udev_wait_useC > 0) { + close(fd); + sprintf(msg, + "To avoid collision with udev: Waiting %lu usec before re-opening", + (unsigned long) Libburn_udev_wait_useC); + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + usleep(Libburn_udev_wait_useC); + fd = open(d->devname, open_mode); + os_errno = errno; + } +#endif /* Libburn_udev_extra_open_cyclE */ + + 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; + } else if (errno == EBUSY) + goto 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", os_errno, 0); + return 0; + +drive_is_in_use:; + tries++; + if (tries < max_tries) { + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + "Drive is in use. Waiting 2 seconds before re-try", + 0, 0); + usleep(2000000); + goto try_open; + } + 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) + 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; +} + +/* @return -1= transport failed, give up drive + 0= transport failed, do not retry + 1= transport succeeded + 2- transport failed, please retry +*/ +static int evaluate_transport_success(struct burn_drive *d, struct command *c, + FILE *fp, + unsigned short host_status, + unsigned short driver_status) +{ + int ret, do_retry= 0, give_up_drive= 0, sev; + char *msg = NULL, *host_problem, *driver_problem, *driver_sugg; + + BURN_ALLOC_MEM(msg, char, 161); + + if ((host_status == Libburn_sg_host_oK && + (driver_status & 0xf7) == Libburn_sg_driver_oK) || c->error) + {ret = 1; goto ex;} /* No transport problems */ + + /* See http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/x291.html */ + + switch(host_status) { + case 0x00: + host_problem = + "SG_ERR_DID_OK (No error)"; + break; case 0x01: + host_problem = + "SG_ERR_DID_NO_CONNECT (Could not connect before timeout period)"; + give_up_drive= 1; + break; case 0x02: + host_problem = + "SG_ERR_DID_BUS_BUSY (Bus stayed busy through time out period)"; + break; case 0x03: + host_problem = + "SG_ERR_DID_TIME_OUT (Timed out for miscellaneous reasons)"; + break; case 0x04: + host_problem = + "SG_ERR_DID_BAD_TARGET (Bad target, device not responding ?)"; + give_up_drive= 1; + break; case 0x05: + host_problem = + "SG_ERR_DID_ABORT (Told to abort)"; + break; case 0x06: + host_problem = + "SG_ERR_DID_PARITY (Parity error)"; + break; case 0x07: + host_problem = + "SG_ERR_DID_ERROR (Internal error detected in the host adapter)"; + give_up_drive= 1; + break; case 0x08: + host_problem = + "SG_ERR_DID_RESET (The SCSI bus or the device have been reset)"; + give_up_drive= 1; + break; case 0x09: + host_problem = + "SG_ERR_DID_BAD_INTR (Got an unexpected interrupt)"; + break; case 0x0a: + host_problem = + "SG_ERR_DID_PASSTHROUGH (Force command past mid-layer)"; + break; case 0x0b: + host_problem = + "SG_ERR_DID_SOFT_ERROR (The low level driver wants a retry)"; + do_retry = 1; + break; default: + host_problem = + "? (unknown host_status code)"; + } + if (host_status != Libburn_sg_host_oK) { + sprintf(msg, "SCSI command %2.2Xh yielded host problem: ", + (unsigned int) c->opcode[0]); + sprintf(msg+strlen(msg), "0x%x %s", + (unsigned int) host_status, host_problem); + sev = LIBDAX_MSGS_SEV_FAILURE; + if (do_retry && !give_up_drive) + sev = LIBDAX_MSGS_SEV_DEBUG; + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x000201a7, sev, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); + sprintf(msg, "--- SG_IO: host_status= 0x%x %s", + (unsigned int) host_status, host_problem); + scsi_log_message(d, fp, msg, 0); + } + + switch (driver_status & 0x07) { + case 0: + driver_problem = "SG_ERR_DRIVER_OK"; + break; case 1: + driver_problem = "SG_ERR_DRIVER_BUSY"; + break; case 2: + driver_problem = "SG_ERR_DRIVER_SOFT"; + break; case 3: + driver_problem = "SG_ERR_DRIVER_MEDIA"; + break; case 4: + driver_problem = "SG_ERR_DRIVER_ERROR"; + break; case 5: + driver_problem = "SG_ERR_DRIVER_INVALID"; + break; case 6: + driver_problem = "SG_ERR_DRIVER_TIMEOUT"; + break; case 7: + driver_problem = "SG_ERR_DRIVER_HARD"; + break; default: + driver_problem = "(unknown driver_status code)"; + } + switch (driver_status & 0xf0) { + case 0: + driver_sugg = "(no suggestion)"; + break; case 0x10: + driver_sugg = "SG_ERR_SUGGEST_RETRY"; + do_retry = 1; + break; case 0x20: + driver_sugg = "SG_ERR_SUGGEST_ABORT"; + give_up_drive= 1; + break; case 0x30: + driver_sugg = "SG_ERR_SUGGEST_REMAP"; + give_up_drive= 1; + break; case 0x40: + driver_sugg = "SG_ERR_SUGGEST_DIE"; + give_up_drive= 1; + break; case 0x80: + driver_sugg = "SG_ERR_SUGGEST_SENSE"; + default: + driver_sugg = "(unknown driver_status suggestion)"; + } + if ((driver_status & 0xf7) != Libburn_sg_driver_oK) { + sprintf(msg, "SCSI command %2.2Xh yielded driver problem: ", + (unsigned int) c->opcode[0]); + sprintf(msg+strlen(msg), "driver_status= 0x%x %s / %s", + (unsigned int) driver_status, + driver_problem, driver_sugg); + sev = LIBDAX_MSGS_SEV_FAILURE; + if (do_retry && !give_up_drive) + sev = LIBDAX_MSGS_SEV_DEBUG; + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x000201a8, sev, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); + sprintf(msg, "--- SG_IO: driver_status= 0x%x %s / %s", + (unsigned int) driver_status, + driver_problem, driver_sugg); + scsi_log_message(d, fp, msg, 0); + } + + if (! do_retry) + c->error = 1; + ret = give_up_drive ? -1 : do_retry ? 2 : 0; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + +static void react_on_drive_loss(struct burn_drive *d, struct command *c, + FILE *fp) +{ + sg_close_drive(d); + d->released = 1; + d->busy = BURN_DRIVE_IDLE; + d->cancel = 1; + c->error = 1; + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x000201a6, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Lost connection to drive", 0, 0); + scsi_log_message(d, fp, "--- SG_IO: Gave up connection to drive", 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(). + @return: 1 success , <=0 failure +*/ +int sg_issue_command(struct burn_drive *d, struct command *c) +{ + int done = 0, no_c_page = 0, i, ret; + int err; + time_t start_time; + sg_io_hdr_t s; + /* ts A61030 */ + static FILE *fp= NULL; + char *msg = NULL; + + BURN_ALLOC_MEM(msg, char, 161); + + c->error = 0; + memset(c->sense, 0, sizeof(c->sense)); + + /* <<< ts A60821 + debug: for tracing calls which might use open drive fds */ + sprintf(msg, "sg_issue_command d->fd= %d d->released= %d\n", + d->fd, d->released); + mmc_function_spy(NULL, msg); + + /* >>> ts B11110 : move this into scsi_log_cmd() together with the + static fp */ + /* ts A61030 */ + if (burn_sg_log_scsi & 1) { + if (fp == NULL) { + fp= fopen("/tmp/libburn_sg_command_log", "a"); + fprintf(fp, + "\n-----------------------------------------\n"); + } + } + + /* ts A61010 : with no fd there is no chance to send an ioctl */ + if (d->fd < 0) { + c->error = 1; + {ret = 0; goto ex;} + } + + + c->error = 0; + memset(&s, 0, sizeof(sg_io_hdr_t)); + if (burn_sg_log_scsi & 3) + scsi_log_cmd(c,fp,0); + + s.interface_id = 'S'; + +#ifdef Libburn_sgio_as_growisofS + /* ??? ts A91112 : does this speed up USB ? (from growisofs) + --- did not help + */ + s.flags = SG_FLAG_DIRECT_IO; +#endif /* Libburn_sgio_as_growisofS */ + + 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; + if (c->timeout > 0) + s.timeout = c->timeout; + else + s.timeout = Libburn_scsi_default_timeouT; + if (c->page && !no_c_page) { + s.dxferp = c->page->data; + +/* # def ine Libburn_debug_dxferP 1 */ +#ifdef Libburn_debug_dxferP + + { char text[1024], *content; int i = c->page->bytes; + if (c->dir == FROM_DRIVE) { + for (i = 0; i < c->page->bytes && c->page->data[i] == 0; i++); + content = (i < c->page->bytes) ? + " (some nonzero)" : " (all zero)"; + } else { + i = c->page->bytes; + content = ""; + } + sprintf(text, "dxferp before = %lx%s", + (unsigned long) s.dxferp, content); + scsi_log_text(text, fp, 0); + } + +#endif + + 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; + {ret = 0; goto ex;} + } + + s.dxfer_len = c->page->bytes; + } + } else { + s.dxferp = NULL; + s.dxfer_len = 0; + } + s.usr_ptr = c; + + start_time = time(NULL); + for(i = 0; !done; i++) { + + memset(c->sense, 0, sizeof(c->sense)); + c->start_time = burn_get_time(0); + + err = ioctl(d->fd, SG_IO, &s); + + c->end_time = burn_get_time(0); + + +#ifdef Libburn_debug_dxferP + if (c->page && !no_c_page) { + char text[1024]; + sprintf(text, "dxferp after = %lx", + (unsigned long) s.dxferp); + scsi_log_text(text, fp, 0); + } + +#endif + + /* 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); + sprintf(msg, "--- SG_IO: return= -1 , "); + sprintf(msg + strlen(msg), "errno= %d , ", errno); + sprintf(msg + strlen(msg), + "host_status= 0x%x , driver_status= 0x%x", + (unsigned int) s.host_status, + (unsigned int) s.driver_status); + scsi_log_message(d, fp, msg, 0); + react_on_drive_loss(d, c, fp); + {ret = -1; goto ex;} + } + done = scsi_eval_cmd_outcome(d, c, fp, + (unsigned char *) (s.sbp), + s.sb_len_wr, + start_time, s.timeout, i, 0); + if (d->cancel) + break; + ret = evaluate_transport_success(d, c, fp, + s.host_status, s.driver_status); + if (ret == -1) + react_on_drive_loss(d, c, fp); + if (ret <= 0) + {ret = -1; goto ex;} + if (d->cancel) + break; + /* if ! done : loop for retry */; + } + + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +/* ts B11001 : outsourced from non-static sg_obtain_scsi_adr() */ +/** Tries to obtain SCSI address parameters. + @return 1 is success , 0 is failure +*/ +static int sg_obtain_scsi_adr_fd(char *path, int fd_in, + 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; + + /* valgrind called idlun unitialized because it is blind for ioctl */ + idlun.x = 0; + idlun.host_unique_id = 0; + + 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 */; + } + if (fd_in >= 0) + fd = fd_in; + else + 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); + + if (fd_in < 0) + 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 */ +/** 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) +{ + return sg_obtain_scsi_adr_fd(path, -1, bus_no, host_no, channel_no, + target_no, lun_no); +} + + +/* 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 = NULL; + int ret = 0, first = 1, fname_size = 4096; + burn_drive_enumerator_t idx; + + BURN_ALLOC_MEM(fname, char, fname_size); + while (1) { + ret= sg_give_next_adr(&idx, fname, fname_size, first); + if(ret <= 0) + break; + first = 0; + if (strcmp(adr, fname) == 0) { + sg_give_next_adr(&idx, fname, fname_size, -1); + {ret = 1; goto ex;} + } + } + ret = 0; +ex:; + if (first == 0) + sg_give_next_adr(&idx, fname, fname_size, -1); + BURN_FREE_MEM(fname); + return ret; +} + + +/* ts B00115 */ +/* Return 1 if the given path leads to a regular file or a device that can be + seeked, read, and possibly written with 2 kB granularity. +*/ +int burn_os_is_2k_seekrw(char *path, int flag) +{ + struct stat stbuf; + + if (stat(path, &stbuf) == -1) + return 0; + if (S_ISREG(stbuf.st_mode)) + return 1; + if (S_ISBLK(stbuf.st_mode)) + return 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 write_start, off_t *bytes) +{ + struct stat stbuf; + struct statvfs vfsbuf; + char *testpath = NULL, *cpt; + long blocks; + int open_mode = O_RDONLY, fd, ret; + off_t add_size = 0; + + BURN_ALLOC_MEM(testpath, char, 4096); + 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) + {ret = -1; goto ex;} + } else if(S_ISBLK(stbuf.st_mode)) { + fd = open(path, open_mode); + if (fd == -1) + {ret = -2; goto ex;} + ret = ioctl(fd, BLKGETSIZE, &blocks); + close(fd); + if (ret == -1) + {ret = -2; goto ex;} + *bytes = ((off_t) blocks) * (off_t) 512; + } else if(S_ISREG(stbuf.st_mode)) { + add_size = burn_sparse_file_addsize(write_start, &stbuf); + strcpy(testpath, path); + } else + {ret = 0; goto ex;} + + if (testpath[0]) { + if (statvfs(testpath, &vfsbuf) == -1) + {ret = -2; goto ex;} + *bytes = add_size + ((off_t) vfsbuf.f_frsize) * + (off_t) vfsbuf.f_bavail; + } + +#ifdef NIX +/* <<< */ + fprintf(stderr, "libburn_DEBUG: Faking 4.5 TB of disk space\n"); + *bytes = ((off_t) 2415919104) * (off_t) 2048; + if (*bytes / (off_t) 2048 > (off_t) 0x7ffffff0) { + *bytes = ((off_t) 0x7ffffff0) * (off_t) 2048; + fprintf(stderr, "libburn_DEBUG: Reducing disk space to 4 TB - 2 kB\n"); + } +/* <<< */ +#endif + + + ret = 1; +ex:; + BURN_FREE_MEM(testpath); + return ret; +} + + +/* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ + +#ifdef PROT_READ +#ifdef PROT_WRITE +#ifdef MAP_SHARED +#ifdef MAP_ANONYMOUS +#ifdef MAP_FAILED +#define Libburn_linux_do_mmaP 1 +#endif +#endif +#endif +#endif +#endif + +#ifdef Libburn_read_o_direcT +#ifdef O_DIRECT +#define Libburn_linux_do_o_direcT 1 +#endif +#endif /* Libburn_read_o_direcT */ + + +int burn_os_open_track_src(char *path, int open_flags, int flag) +{ + int fd; + +#ifdef Libburn_linux_do_o_direcT + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + "Opening track source with O_DIRECT" , 0, 0); + fd = open(path, open_flags | O_DIRECT); +#else + fd = open(path, open_flags); +#endif + return fd; +} + + +void *burn_os_alloc_buffer(size_t amount, int flag) +{ + void *buf = NULL; + +#ifdef Libburn_linux_do_mmaP + + /* >>> check whether size is suitable */; + + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + "Allocating buffer via mmap()" , 0, 0); + buf = mmap(NULL, amount, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, (off_t) 0); + if (buf == MAP_FAILED) + buf = NULL; + else + memset(buf, 0, amount); +#else + buf = calloc(1, amount); +#endif /* ! Libburn_linux_do_mmaP */ + + return buf; +} + + +int burn_os_free_buffer(void *buffer, size_t amount, int flag) +{ + int ret = 0; + + if (buffer == NULL) + return 0; +#ifdef Libburn_linux_do_mmaP + ret = munmap(buffer, amount); +#else + free(buffer); +#endif + return (ret == 0); +} + diff --git a/trunk/libburn/sg-netbsd.c b/trunk/libburn/sg-netbsd.c new file mode 100644 index 0000000..917e00f --- /dev/null +++ b/trunk/libburn/sg-netbsd.c @@ -0,0 +1,894 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* + Copyright (c) 2010 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. + + Derived 2014 from libburn/sg-solaris.c with information learned from + dvd+rw-tools, http://fxr.watson.org/fxr/source/sys/scsiio.h?v=NETBSD, + http://netbsd.gw.com/cgi-bin/man-cgi?scsi+4+NetBSD-current, + and experiments made by Freddy Fisker. +*/ + + +/* + +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: NetBSD 6, ioctl SCIOCCOMMAND + >>> ??? for OpenBSD too ? + +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_id_string() returns an id string of the SCSI transport adapter. + It may be called before initialization but then may + return only a preliminary id. + +sg_initialize() performs global initialization of the SCSI transport + adapter and eventually needed operating system + facilities. Checks for compatibility of supporting + software components. + +sg_shutdown() performs global finalizations and releases golbally + aquired resources. + +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_dispose_drive() finalizes adapter specifics of struct burn_drive + on destruction. Releases resources which were aquired + underneath scsi_enumerate_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_is_2k_seekrw() tells whether the given path leads to a file object + that can be used in 2 kB granularity by lseek(2), + read(2), and possibly write(2) if not read-only.. + E.g. a USB stick or a hard disk. + +burn_os_stdio_capacity() estimates the emulated media space of stdio-drives. + +burn_os_open_track_src() opens a disk file in a way that allows best + throughput with file reading and/or SCSI write command + transmission. + +burn_os_alloc_buffer() allocates a memory area that is suitable for file + descriptors issued by burn_os_open_track_src(). + The buffer size may be rounded up for alignment + reasons. + +burn_os_free_buffer() delete a buffer obtained by burn_os_alloc_buffer(). + +Porting hints are marked by the text "PORTING:". +Send feedback to libburn-hackers@pykix.org . + +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + + +/** PORTING : ------- OS dependent headers and definitions ------ */ + +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <string.h> +#include <stdlib.h> + +#ifdef Libburn_os_has_statvfS +#include <sys/statvfs.h> +#endif /* Libburn_os_has_stavtfS */ + +#include <sys/ioctl.h> +#include <sys/scsiio.h> + + +/** PORTING : ------ libburn portable headers and definitions ----- */ + +#include "transport.h" +#include "drive.h" +#include "sg.h" +#include "spc.h" +#include "sbc.h" +#include "debug.h" +#include "toc.h" +#include "util.h" +#include "init.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); +int burn_drive_resolve_link(char *path, char adr[], + int *recursion_count, int flag); /* drive.c */ + +/* Whether to log SCSI commands: + bit0= log in /tmp/libburn_sg_command_log + bit1= log to stderr + bit2= flush every line +*/ +extern int burn_sg_log_scsi; + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Private definitions. Port only if needed by public functions. */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + + +/* Storage object is in libburn/init.c + whether to strive for exclusive access to the drive +*/ +extern int burn_sg_open_o_excl; + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Private functions. Port only if needed by public functions */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + +static int sg_close_drive(struct burn_drive * d) +{ + if (d->fd != -1) { + close(d->fd); + d->fd = -1; + return 1; + } + 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 --------------- */ + + /* Transport adapter is NetBSD SCIOCCOMMAND */ + /* Adapter specific handles and data */ + + out.fd = -1; + + /* 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); +} + + +static int start_enum_rcdNx(burn_drive_enumerator_t *idx, int flag) +{ + idx->cdno = -1; + return 1; +} + + +/* Trying /dev/rcd[0..63][dc] */ +#define Libburn_netbsd_max_cdnuM 63 + +static int next_enum_rcdNx(burn_drive_enumerator_t *idx, + char adr[], int adr_size, int flag) +{ + static char suffix[2] = {'d', 'c'}; + struct stat stbuf; + int i, stat_ret; + char path[16]; + + while (idx->cdno < Libburn_netbsd_max_cdnuM) { + idx->cdno++; + for (i = 0; i < 2; i++) { + sprintf(path, "/dev/rcd%d%c", idx->cdno, suffix[i]); + stat_ret = stat(path, &stbuf); + if (stat_ret == -1) + continue; + if (!S_ISCHR(stbuf.st_mode)) + continue; + if ((int) strlen(path) >= adr_size) + continue; + strcpy(adr, path); + return 1; + } + } + return 0; +} + + +/* Searching the first byte address that cannot be lseeked and read +*/ +static int guess_size_by_seek_set(int fd, off_t *bytes, int flag) +{ + static off_t abs_limit = ((off_t) 1024) * 1024 * 1024 * 1024 * 1024; + off_t i, step = ((off_t) 1024) * 1024 * 1024 * 1024, ret; + char buf[1]; + + *bytes = 0; + for (i = step; i < abs_limit; i += step) { + ret = lseek(fd, i, SEEK_SET); + if (ret == -1) { + i -= step; + step = step >> 1; + if (step > 0) + continue; + return 1; + } + ret = read(fd, buf, 1); + if (ret == -1) { + i -= step; + step = step >> 1; + if (step > 0) + continue; + return 1; + } + *bytes = i + 1; + } + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Public functions. These MUST be ported. */ +/* ------------------------------------------------------------------------ */ + + +/** Returns the id string of the SCSI transport adapter and eventually + needed operating system facilities. + This call is usable even if sg_initialize() was not called yet. In that + case a preliminary constant message might be issued if detailed info is + not available yet. + @param msg returns id string + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_id_string(char msg[1024], int flag) +{ + sprintf(msg, "internal NetBSD SCIOCCOMMAND adapter sg-netbsd"); + return 1; +} + + +/** Performs global initialization of the SCSI transport adapter and eventually + needed operating system facilities. Checks for compatibility of supporting + software components. + @param msg returns ids and/or error messages of eventual helpers + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_initialize(char msg[1024], int flag) +{ + return sg_id_string(msg, 0); +} + + +/** Performs global finalization of the SCSI transport adapter and eventually + needed operating system facilities. Releases globally aquired resources. + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_shutdown(int flag) +{ + return 1; +} + + +/** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of + struct burn_drive which are defined in os-*.h. + The eventual initialization of those components was made underneath + scsi_enumerate_drives(). + This will be called when a burn_drive gets disposed. + @param d the drive to be finalized + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_dispose_drive(struct burn_drive *d, int flag) +{ + return 1; +} + + +/** 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) +{ + int ret; + + if (initialize == 1) { + ret = start_enum_rcdNx(idx, 0); + if (ret <= 0) + return ret; + } else if (initialize == -1) { + return 0; + } + ret = next_enum_rcdNx(idx, adr, adr_size, 0); + return ret; +} + + +/** 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, ret, i_bus_no = -1, buf_size = 4096; + int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1; + char *buf = NULL; + + BURN_ALLOC_MEM(buf, char, buf_size); + + while(1) { + ret = sg_give_next_adr(&idx, buf, buf_size, initialize); + initialize = 0; + if (ret <= 0) + break; + if (burn_drive_is_banned(buf)) + continue; + sg_obtain_scsi_adr(buf, &i_bus_no, &i_host_no, + &i_channel_no, &i_target_no, &i_lun_no); + enumerate_common(buf, + i_bus_no, i_host_no, i_channel_no, + i_target_no, i_lun_no); + } + sg_give_next_adr(&idx, buf, buf_size, -1); + ret = 1; +ex:; + BURN_FREE_MEM(buf); + return ret; +} + + +/** Tells whether 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->fd != -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) +{ + char *msg = NULL; + int os_errno, ret; + + BURN_ALLOC_MEM(msg, char, 4096); + + if (d->fd != -1) { + d->released = 0; + {ret = 1; goto ex;} + } + d->fd = open(d->devname, O_RDWR | O_NDELAY); + if (d->fd == -1) { + os_errno = errno; + sprintf(msg, "Could not grab drive '%s'", d->devname); + /* (errno == ENXIO is a device file with no drive attached) */ + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020003, + errno == ENXIO ? LIBDAX_MSGS_SEV_DEBUG : + LIBDAX_MSGS_SEV_SORRY, + LIBDAX_MSGS_PRIO_HIGH, + msg, os_errno, 0); + {ret = 0; goto ex;} + } + + d->released = 0; + + /* Make sure by INQUIRY that this is really a MMC drive */ + ret = spc_confirm_cd_drive(d, 0); + if (ret <= 0) + goto revoke; + +/* # define Libburn_sg_netbsd_scsi_debuG */ +#ifdef Libburn_sg_netbsd_scsi_debuG + { + static int sc_db = SC_DB_CMDS | SC_DB_FLOW; + + ret = ioctl(d->fd, SCIOCDEBUG, &sc_db); + if (ret == -1) + fprintf(stderr, + "libburn_DEBUG: ioctl(%d, SCIOCDEBUG, &(0x%X)) returns %d, errno = %d\n", + d->fd, (unsigned int) sc_db, ret, errno); + } +#endif + + + {ret = 1; goto ex;} + +revoke:; + sprintf(msg, "Could not grab drive '%s'.", d->devname); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020003, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + if (d->fd >= 0) { + close(d->fd); + d->fd = -1; + d->released = 1; + } + ret = 0; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +/** PORTING: Is mainly about the call to sg_close_drive() and whether 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->fd < 0) + 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 i, timeout_ms, ret, key, asc, ascq, done = 0, sense_len, max_sl; + time_t start_time; + scsireq_t req; + char msg[160]; + static FILE *fp = NULL; + + c->error = 0; + + if (d->fd == -1) + return 0; + + if (burn_sg_log_scsi & 1) { + if (fp == NULL) { + fp= fopen("/tmp/libburn_sg_command_log", "a"); + fprintf(fp, + "\n-----------------------------------------\n"); + } + } + if (burn_sg_log_scsi & 3) + scsi_log_cmd(c,fp,0); + + if (c->timeout > 0) + timeout_ms = c->timeout; + else + timeout_ms = 200000; + + memset (&req, 0, sizeof(req)); + memcpy(req.cmd, c->opcode, c->oplen); + req.cmdlen = c->oplen; + req.databuf = (caddr_t) c->page->data; + req.flags = SCCMD_ESCAPE; /* probably to make req.cmdlen significant */ + req.timeout = timeout_ms; + max_sl = sizeof(c->sense) > SENSEBUFLEN ? + SENSEBUFLEN : sizeof(c->sense); + req.senselen = max_sl; + if (c->dir == TO_DRIVE) { + req.datalen = c->page->bytes; + req.flags |= SCCMD_WRITE; + } else if (c->dir == FROM_DRIVE) { + req.flags |= SCCMD_READ; + if (c->dxfer_len >= 0) + req.datalen = c->dxfer_len; + else + req.datalen = BUFFER_SIZE; + /* touch page so we can use valgrind */ + memset(c->page->data, 0, BUFFER_SIZE); + } else { + req.flags |= SCCMD_READ; + req.datalen = 0; + } + + /* retry-loop */ + start_time = time(NULL); + for(i = 0; !done; i++) { + memset(c->sense, 0, sizeof(c->sense)); + c->start_time = burn_get_time(0); + + ret = ioctl(d->fd, SCIOCCOMMAND, &req); + +/* <<< Fault mock-up +if (c->opcode[0] == 0x28) { + ret = -1; + errno = 9; +} +*/ + + c->end_time = burn_get_time(0); + +/* #define Libburn_debug_sg_netbsD */ +#ifdef Libburn_debug_sg_netbsD + fprintf(stderr, "libburn_DEBUG: ret= %d, retsts = 0x%X, senselen_used = %d, status = 0x%X, error= 0x%X\n", ret, (unsigned int) req.retsts, (int) req.senselen_used, (unsigned int) req.status, req.error); + fprintf(stderr, "libburn_DEBUG: datalen_used = %u\n", + (unsigned int) req.datalen_used); +#endif + + if (ret != 0 || + (req.retsts != SCCMD_SENSE && req.retsts != SCCMD_OK)) { + sprintf(msg, "Failed to transfer command to drive. (ioctl(%d, SCIOCCOMMAND) = %d, scsireq_t.retsts = 0x%X, errno= %d)", + d->fd, ret, (unsigned int) req.retsts, errno); + if (burn_sg_log_scsi & 3) + scsi_log_message(d, fp, msg, 0); + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x0002010c, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, errno, 0); + sg_close_drive(d); + d->released = 1; + d->busy = BURN_DRIVE_IDLE; + c->error = 1; + return -1; + } + + sense_len = 0; + if (req.retsts == SCCMD_SENSE) { + memcpy(c->sense, req.sense, max_sl); + sense_len = req.senselen > max_sl ? + max_sl : req.senselen; + } + spc_decode_sense(c->sense, sense_len, &key, &asc, &ascq); + if (key || asc || ascq) + sense_len = req.senselen; + else + sense_len = 0; + +/* <<< Fault mock-up +if (c->opcode[0] == 0x5a) { + req.datalen_used = 0; + memset(c->page->data, 0, BUFFER_SIZE); +} +*/ + + if (c->dir == FROM_DRIVE && sense_len == 0 && + req.datalen > 0 && req.datalen_used < req.datalen) { + sprintf(msg, "Short reply from SCSI command %2.2X: expected: %d, got: %d, req.retsts: 0x%X", + (unsigned int) c->opcode[0], + (int) req.datalen, (int) req.datalen_used, + (unsigned int) req.retsts); + if (burn_sg_log_scsi & 3) + scsi_log_message(d, fp, msg, 0); + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + if (req.datalen_used == 0) + c->error = 1; + c->dxfer_len = req.datalen_used; + } + + done = scsi_eval_cmd_outcome(d, c, fp, c->sense, sense_len, + start_time, timeout_ms, i, 0); + if (d->cancel) + done = 1; + } /* end of retry-loop */ + + 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) +{ + int ret, fd = -1; + struct scsi_addr addr; + + fd = open(path, O_RDWR | O_NDELAY); + if (fd == -1) + return 0; + *bus_no = *host_no = *channel_no = *target_no = *lun_no = 0; + memset(&addr, 0, sizeof(addr)); + ret = ioctl(fd, SCIOCIDENTIFY, &addr); + if (ret != 0) + {ret = 0; goto ex;} + if (addr.type != TYPE_SCSI) + {ret = 0; goto ex;} + *bus_no = *host_no = addr.addr.scsi.scbus; + *channel_no = 0; + *target_no = addr.addr.scsi.target; + *lun_no = addr.addr.scsi.lun; + ret = 1; +ex:; + if (fd != -1) + close(fd); + 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, ret; + 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); +} + + +/* Return 1 if the given path leads to a regular file or a device that can be + seeked, read, and possibly written with 2 kB granularity. +*/ +int burn_os_is_2k_seekrw(char *path, int flag) +{ + struct stat stbuf; + int l, i, dev, tl; + char try[16]; + + /* >>> ??? Is this a comprehensive list of lseek()-capable devices ? */ + /* http://www.netbsd.org/docs/guide/en/chap-rmmedia.html */ + static char dev_names[][4] = { + "fd", "rfd", "sd" , "cd", "rcd", "wd", ""}; + + if (path[0] == 0) + return 0; + if (stat(path, &stbuf) == -1) + return 0; + if (S_ISREG(stbuf.st_mode)) + return 1; + if (S_ISBLK(stbuf.st_mode)) + return 1; + + /* Look for known device names which promise the desired capabilities */ + if (strncmp(path, "/dev/", 5) != 0) + return 0; + l = strlen(path); + for (dev = 0; dev_names[dev][0] != 0; dev++) { + sprintf(try, "/dev/%s", dev_names[dev]); + tl = strlen(try); + if (strncmp(path, try, tl) != 0) + continue; + l -= tl; + for (i = 0; i < Libburn_netbsd_max_cdnuM; i++) { + sprintf(try + tl, "%d", i); + if (strncmp(path, try, strlen(try)) == 0) + break; + } + if (i >= Libburn_netbsd_max_cdnuM) + continue; + tl += strlen(try + tl); + if (l == tl) + return 1; + if (l > tl + 1) + continue; + if (path[l - 1] >= 'a' && path[l - 1] <= 'z') + return 1; + } + + return 0; +} + + +/** 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 write_start, off_t *bytes) +{ + struct stat stbuf; + int ret; + +#ifdef Libburn_os_has_statvfS + struct statvfs vfsbuf; +#endif + + char *testpath = NULL, *cpt; + off_t add_size = 0; + + BURN_ALLOC_MEM(testpath, char, 4096); + + testpath[0] = 0; + 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) + {ret = -1; goto ex;} + + } else if(S_ISBLK(stbuf.st_mode)) { + int open_mode = O_RDONLY, fd; + + fd = open(path, open_mode); + if (fd == -1) + {ret = -2; goto ex;} + *bytes = lseek(fd, 0, SEEK_END); + if (*bytes <= 0) + guess_size_by_seek_set(fd, bytes, 0); + close(fd); + if (*bytes == -1) { + *bytes = 0; + {ret = 0; goto ex;} + } + + } else if(S_ISREG(stbuf.st_mode)) { + add_size = burn_sparse_file_addsize(write_start, &stbuf); + strcpy(testpath, path); + } else + {ret = 0; goto ex;} + + if (testpath[0]) { + +#ifdef Libburn_os_has_statvfS + + if (statvfs(testpath, &vfsbuf) == -1) + {ret = -2; goto ex;} + *bytes = add_size + ((off_t) vfsbuf.f_frsize) * + (off_t) vfsbuf.f_bavail; + +#else /* Libburn_os_has_statvfS */ + + {ret = 0; goto ex;} + +#endif /* ! Libburn_os_has_stavtfS */ + + } + ret = 1; +ex:; + BURN_FREE_MEM(testpath); + return ret; +} + + +/* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ + +#ifdef Libburn_read_o_direcT + + /* No special O_DIRECT-like precautions are implemented here */ + +#endif /* Libburn_read_o_direcT */ + + +int burn_os_open_track_src(char *path, int open_flags, int flag) +{ + int fd; + + fd = open(path, open_flags); + return fd; +} + + +void *burn_os_alloc_buffer(size_t amount, int flag) +{ + void *buf = NULL; + + buf = calloc(1, amount); + return buf; +} + + +int burn_os_free_buffer(void *buffer, size_t amount, int flag) +{ + if (buffer == NULL) + return 0; + free(buffer); + return 1; +} + diff --git a/trunk/libburn/sg-solaris.c b/trunk/libburn/sg-solaris.c new file mode 100644 index 0000000..b7f9ba2 --- /dev/null +++ b/trunk/libburn/sg-solaris.c @@ -0,0 +1,1013 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* + Copyright (c) 2010 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + +/* + +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: Solaris uscsi, e.g. for SunOS 5.11 + + +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_id_string() returns an id string of the SCSI transport adapter. + It may be called before initialization but then may + return only a preliminary id. + +sg_initialize() performs global initialization of the SCSI transport + adapter and eventually needed operating system + facilities. Checks for compatibility of supporting + software components. + +sg_shutdown() performs global finalizations and releases golbally + aquired resources. + +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_dispose_drive() finalizes adapter specifics of struct burn_drive + on destruction. Releases resources which were aquired + underneath scsi_enumerate_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_is_2k_seekrw() tells whether the given path leads to a file object + that can be used in 2 kB granularity by lseek(2), + read(2), and possibly write(2) if not read-only.. + E.g. a USB stick or a hard disk. + +burn_os_stdio_capacity() estimates the emulated media space of stdio-drives. + +burn_os_open_track_src() opens a disk file in a way that allows best + throughput with file reading and/or SCSI write command + transmission. + +burn_os_alloc_buffer() allocates a memory area that is suitable for file + descriptors issued by burn_os_open_track_src(). + The buffer size may be rounded up for alignment + reasons. + +burn_os_free_buffer() delete a buffer obtained by burn_os_alloc_buffer(). + +Porting hints are marked by the text "PORTING:". +Send feedback to libburn-hackers@pykix.org . + +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + + +/** PORTING : ------- OS dependent headers and definitions ------ */ + +#include <unistd.h> +#include <stropts.h> +#include <stdio.h> +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <string.h> +#include <stdlib.h> +#include <dirent.h> + +#ifdef Libburn_os_has_statvfS +#include <sys/statvfs.h> +#endif /* Libburn_os_has_stavtfS */ + +#include <volmgt.h> +#include <sys/dkio.h> +#include <sys/vtoc.h> + +#include <sys/scsi/impl/uscsi.h> + + +/* The waiting time before eventually retrying a failed SCSI command. + Before each retry wait Libburn_sg_linux_retry_incR longer than with + the previous one. +*/ +#define Libburn_sg_solaris_retry_usleeP 100000 +#define Libburn_sg_solaris_retry_incR 100000 + + +/** PORTING : ------ libburn portable headers and definitions ----- */ + +#include "transport.h" +#include "drive.h" +#include "sg.h" +#include "spc.h" +#include "sbc.h" +#include "debug.h" +#include "toc.h" +#include "util.h" +#include "init.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); +int burn_drive_resolve_link(char *path, char adr[], + int *recursion_count, int flag); /* drive.c */ + +/* Whether to log SCSI commands: + bit0= log in /tmp/libburn_sg_command_log + bit1= log to stderr + bit2= flush every line +*/ +extern int burn_sg_log_scsi; + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Private definitions. Port only if needed by public functions. */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + + +/* Storage object is in libburn/init.c + whether to strive for exclusive access to the drive +*/ +extern int burn_sg_open_o_excl; + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Private functions. Port only if needed by public functions */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + + +static int sg_close_drive(struct burn_drive * d) +{ + if (d->fd != -1) { + close(d->fd); + d->fd = -1; + return 1; + } + return 0; +} + + +static int decode_btl_number(char **cpt, int stopper, int *no) +{ + *no = 0; + for ((*cpt)++; **cpt != stopper; (*cpt)++) { + if (**cpt < '0' || **cpt > '9') + return 0; + *no = *no * 10 + **cpt - '0'; + } + return 1; +} + + +/* Read bus, target, lun from name "cXtYdZs2" or "cXtYdZ/...". + Return 0 if name is not of the desired form. +*/ +static int decode_btl_solaris(char *name, int *busno, int *tgtno, int *lunno, + int flag) +{ + char *cpt, *cpt_mem; + int ret; + + *busno = *tgtno = *lunno = -1; + cpt = name; + if (*cpt != 'c') + return 0; + ret = decode_btl_number(&cpt, 't', busno); + if (ret <= 0) + return ret; + ret = decode_btl_number(&cpt, 'd', tgtno); + if (ret <= 0) + return ret; + cpt_mem = cpt; + ret = decode_btl_number(&cpt, 's', lunno); + if (ret <= 0) { + cpt = cpt_mem; + ret = decode_btl_number(&cpt, '/', lunno); + if (ret <= 0) + return ret; + return(1); + } + cpt++; + if (*cpt != '2' || *(cpt + 1) != 0) + return 0; + return 1; +} + + +static int start_enum_cXtYdZs2(burn_drive_enumerator_t *idx, int flag) +{ + DIR *dir; + + idx->dir = NULL; + dir = opendir("/dev/rdsk"); + if (dir == NULL) { + libdax_msgs_submit(libdax_messenger, -1, + 0x0002000c, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Cannot start device file enumeration. opendir(\"/dev/rdsk\") failed.", + errno, 0); + return 0; + } + idx->dir = dir; + return 1; +} + + +static int sg_solaris_convert_devname(char *path, char **dev_to_open, int flag) +{ + char *sym_name = NULL, *media_name = NULL, *curr_name, *msg = NULL; + int ret; + + BURN_ALLOC_MEM(msg, char, 4096); + + BURN_FREE_MEM(*dev_to_open); + *dev_to_open = NULL; + curr_name = path; + + if (! volmgt_running()) + goto set_name; + sym_name = volmgt_symname(path); + sprintf(msg, "Volume Management symbolic name: '%s' -> %s", + path, sym_name == NULL ? "NULL" : sym_name); + libdax_msgs_submit(libdax_messenger, -1, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + if (sym_name != NULL) + media_name = media_findname(sym_name); + else + media_name = media_findname(path); + if (media_name != NULL) + curr_name = media_name; + sprintf(msg, "Media name: %s -> %s", + sym_name == NULL ? path : sym_name, + media_name == NULL ? "NULL" : media_name); + libdax_msgs_submit(libdax_messenger, -1, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); +set_name: + BURN_ALLOC_MEM(*dev_to_open, char, strlen(curr_name) + 1); + strcpy(*dev_to_open, curr_name); + ret = 1; +ex: + if (media_name != NULL) + free(media_name); + if (sym_name != NULL) + free(sym_name); + BURN_FREE_MEM(msg); + return(ret); +} + + +static int next_enum_cXtYdZs2(burn_drive_enumerator_t *idx, + char adr[], int adr_size, int flag) +{ + int busno, tgtno, lunno, ret, fd = -1, volpath_size = 160, os_errno; + char *volpath = NULL, *msg = NULL, *dev_to_open = NULL; + struct dirent *entry; + struct dk_cinfo cinfo; + DIR *dir; + + BURN_ALLOC_MEM(volpath, char, volpath_size); + BURN_ALLOC_MEM(msg, char, 4096); + + dir = idx->dir; + while (1) { + errno = 0; + entry = readdir(dir); + if (entry == NULL) { + if (errno) { + libdax_msgs_submit(libdax_messenger, + -1, 0x0002000d, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Cannot enumerate next device. readdir() from \"/dev/rdsk\" failed.", + errno, 0); + {ret = -1; goto ex;} + } + break; + } + if (strlen(entry->d_name) > (size_t) (volpath_size - 11)) + continue; + ret = decode_btl_solaris(entry->d_name, + &busno, &tgtno, &lunno, 0); + if (ret <= 0) + continue; /* not cXtYdZs2 */ + + sprintf(volpath, "/dev/rdsk/%s", entry->d_name); + if (burn_drive_is_banned(volpath)) + continue; + ret = sg_solaris_convert_devname(volpath, &dev_to_open, 0); + if (ret <= 0) + continue; + fd = open(dev_to_open, O_RDONLY | O_NDELAY); + if (fd < 0) { + os_errno = errno; + sprintf(msg, "Could not open '%s' , errno = %d", + dev_to_open, os_errno); + libdax_msgs_submit(libdax_messenger, -1, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, os_errno, 0); + continue; + } + /* See man dkio */ + ret = ioctl(fd, DKIOCINFO, &cinfo); + close(fd); + if (ret < 0) { + os_errno = errno; + sprintf(msg, + "ioctl(DKIOCINFO) failed on drive '%s', errno = %d", + volpath, os_errno); + libdax_msgs_submit(libdax_messenger, -1, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, os_errno, 0); + continue; + } + if (cinfo.dki_ctype != DKC_CDROM) { + sprintf(msg, + "ioctl(DKIOCINFO) classifies drive '%s' as dki_ctype %ld, not as DKC_CDROM = %ld", + volpath, (long int) cinfo.dki_ctype, + (long int) DKC_CDROM); + libdax_msgs_submit(libdax_messenger, -1, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + continue; + } + if (adr_size <= (int) strlen(volpath)) { + sprintf(msg, + "Device path '%s' too long. (Max. %d)", + volpath, adr_size - 1); + libdax_msgs_submit(libdax_messenger, -1, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + {ret = -1; goto ex;} + } + strcpy(adr, volpath); + sprintf(msg, "Accepted as valid drive '%s'", volpath); + libdax_msgs_submit(libdax_messenger, -1, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + {ret = 1; goto ex;} + } + ret = 0; +ex:; + BURN_FREE_MEM(dev_to_open); + BURN_FREE_MEM(msg); + BURN_FREE_MEM(volpath); + return ret; +} + + +static int end_enum_cXtYdZs2(burn_drive_enumerator_t *idx, int flag) +{ + DIR *dir; + + dir = idx->dir; + if(dir != NULL) + closedir(dir); + idx->dir = NULL; + return 1; +} + + +/* ----------------------------------------------------------------------- */ +/* 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 --------------- */ + + /* Transport adapter is Solaris uscsi */ + /* Adapter specific handles and data */ + out.fd = -1; + + /* 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); +} + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Public functions. These MUST be ported. */ +/* ------------------------------------------------------------------------ */ + + +/** Returns the id string of the SCSI transport adapter and eventually + needed operating system facilities. + This call is usable even if sg_initialize() was not called yet. In that + case a preliminary constant message might be issued if detailed info is + not available yet. + @param msg returns id string + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_id_string(char msg[1024], int flag) +{ + sprintf(msg, "internal Solaris uscsi adapter sg-solaris"); + return 1; +} + + +/** Performs global initialization of the SCSI transport adapter and eventually + needed operating system facilities. Checks for compatibility of supporting + software components. + @param msg returns ids and/or error messages of eventual helpers + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_initialize(char msg[1024], int flag) +{ + return sg_id_string(msg, 0); +} + + +/** Performs global finalization of the SCSI transport adapter and eventually + needed operating system facilities. Releases globally aquired resources. + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_shutdown(int flag) +{ + return 1; +} + + +/** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of + struct burn_drive which are defined in os-*.h. + The eventual initialization of those components was made underneath + scsi_enumerate_drives(). + This will be called when a burn_drive gets disposed. + @param d the drive to be finalized + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_dispose_drive(struct burn_drive *d, int flag) +{ + return 1; +} + + +/** 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 = start_enum_cXtYdZs2(idx, 0); + if (ret <= 0) + return ret; + } else if (initialize == -1) { + ret = end_enum_cXtYdZs2(idx, 0); + return 0; + } + ret = next_enum_cXtYdZs2(idx, adr, adr_size, 0); + return ret; +} + + +/** 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, ret, i_bus_no = -1, buf_size = 4096; + int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1; + char *buf = NULL; + + BURN_ALLOC_MEM(buf, char, buf_size); + + while(1) { + ret = sg_give_next_adr(&idx, buf, buf_size, initialize); + initialize = 0; + if (ret <= 0) + break; + if (burn_drive_is_banned(buf)) + continue; + sg_obtain_scsi_adr(buf, &i_bus_no, &i_host_no, + &i_channel_no, &i_target_no, &i_lun_no); + enumerate_common(buf, + i_bus_no, i_host_no, i_channel_no, + i_target_no, i_lun_no); + } + sg_give_next_adr(&idx, buf, buf_size, -1); + ret = 1; +ex:; + BURN_FREE_MEM(buf); + return ret; +} + + +/** Tells whether 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->fd != -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) +{ + char *msg = NULL, *dev_to_open = NULL; + int os_errno, ret; + struct dk_cinfo cinfo; + + BURN_ALLOC_MEM(msg, char, 4096); + + if (d->fd != -1) { + d->released = 0; + {ret = 1; goto ex;} + } + ret = sg_solaris_convert_devname(d->devname, &dev_to_open, 0); + if (ret <= 0) + goto ex; + d->fd = open(dev_to_open, O_RDONLY | O_NDELAY); + if (d->fd == -1) { + os_errno = errno; + sprintf(msg, "Could not grab drive '%s'", + d->devname); + if (strcmp(d->devname, dev_to_open)) + sprintf(msg + strlen(msg), " via '%s'", dev_to_open); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020003, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, os_errno, 0); + {ret = 0; goto ex;} + } + ret = ioctl(d->fd, DKIOCINFO, &cinfo); + if (ret < 0) { + os_errno = errno; + sprintf(msg, "ioctl(DKIOCINFO) failed on drive '%s'", + d->devname); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, os_errno, 0); + goto revoke; + } + if (cinfo.dki_ctype != DKC_CDROM) { + sprintf(msg, + "ioctl(DKIOCINFO) classifies drive '%s' as dki_ctype %ld, not as DKC_CDROM = %ld", + d->devname, (long int) cinfo.dki_ctype, + (long int) DKC_CDROM); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + goto revoke; + } + + /* >>> obtain eventual locks */; + + d->released = 0; + {ret = 1; goto ex;} +revoke:; + sprintf(msg, "Could not grab drive '%s'. Not a CDROM device.", + d->devname); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020003, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + ret = 0; +ex:; + BURN_FREE_MEM(dev_to_open); + BURN_FREE_MEM(msg); + return ret; +} + + +/** PORTING: Is mainly about the call to sg_close_drive() and whether 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->fd < 0) + 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 i, timeout_ms, ret, key, asc, ascq, done = 0, sense_len; + time_t start_time; + struct uscsi_cmd cgc; + char msg[80]; + static FILE *fp = NULL; + + c->error = 0; + memset(c->sense, 0, sizeof(c->sense)); + + if (d->fd == -1) + return 0; + + if (burn_sg_log_scsi & 1) { + if (fp == NULL) { + fp= fopen("/tmp/libburn_sg_command_log", "a"); + fprintf(fp, + "\n-----------------------------------------\n"); + } + } + if (burn_sg_log_scsi & 3) + scsi_log_cmd(c,fp,0); + + if (c->timeout > 0) + timeout_ms = c->timeout; + else + timeout_ms = 200000; + memset (&cgc, 0, sizeof (struct uscsi_cmd)); + /* No error messages, no retries, + do not execute with other commands, request sense data + */ + cgc.uscsi_flags = USCSI_SILENT | USCSI_DIAGNOSE | USCSI_ISOLATE + | USCSI_RQENABLE; + cgc.uscsi_timeout = timeout_ms / 1000; + cgc.uscsi_cdb = (caddr_t) c->opcode; + cgc.uscsi_bufaddr = (caddr_t) c->page->data; + if (c->dir == TO_DRIVE) { + cgc.uscsi_flags |= USCSI_WRITE; + cgc.uscsi_buflen = c->page->bytes; + } else if (c->dir == FROM_DRIVE) { + cgc.uscsi_flags |= USCSI_READ; + if (c->dxfer_len >= 0) + cgc.uscsi_buflen = c->dxfer_len; + else + cgc.uscsi_buflen = BUFFER_SIZE; + /* touch page so we can use valgrind */ + memset(c->page->data, 0, BUFFER_SIZE); + } else { + cgc.uscsi_buflen = 0; + } + cgc.uscsi_cdblen = c->oplen; + cgc.uscsi_rqlen = sizeof(c->sense); + cgc.uscsi_rqbuf = (caddr_t) c->sense; + + /* retry-loop */ + start_time = time(NULL); + for(i = 0; !done; i++) { + + memset(c->sense, 0, sizeof(c->sense)); + c->start_time = burn_get_time(0); + + ret = ioctl(d->fd, USCSICMD, &cgc); + + c->end_time = burn_get_time(0); + + /* For cgc.uscsi_status see SAM-3 5.3.1, Table 22 + 0 = GOOD , 2 = CHECK CONDITION : Sense Data are delivered + 8 = BUSY + */ + if (ret != 0 && cgc.uscsi_status != 2) { + sprintf(msg, + "Failed to transfer command to drive. (uscsi_status = 0x%X)", + (unsigned int) cgc.uscsi_status), + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x0002010c, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, errno, 0); + sg_close_drive(d); + d->released = 1; + d->busy = BURN_DRIVE_IDLE; + c->error = 1; + return -1; + } + + + /* >>> Should replace "18" by realistic sense length. + What's about following older remark ? + */ + /* >>> valid sense: cgc.uscsi_rqlen - cgc.uscsi_rqresid */; + + spc_decode_sense(c->sense, 0, &key, &asc, &ascq); + if (key || asc || ascq) + sense_len = 18; + else + sense_len = 0; + done = scsi_eval_cmd_outcome(d, c, fp, c->sense, sense_len, + start_time, timeout_ms, i, 0); + if (d->cancel) + done = 1; + + } /* end of retry-loop */ + + 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) +{ + int ret; + + /* Try to guess from path */ + if (strncmp("/dev/rdsk/", path, 10) == 0) { + ret = decode_btl_solaris(path + 10, + bus_no, target_no, lun_no, 0); + if (ret > 0) { + *host_no = *bus_no; + *channel_no = 0; + return 1; + } + } + *bus_no = *host_no = *channel_no = *target_no = *lun_no = -1; + + /* >>> Could need a ioctl which gives SCSI numbers */; + + return (0); +} + + +/** Tells wether a text is a persistent address as listed by the enumeration + functions. +*/ + +#ifndef NIX + +int sg_is_enumerable_adr(char* path) +{ + int ret; + int bus_no, target_no, lun_no; + struct stat stbuf; + + if (strncmp("/dev/rdsk/", path, 10) != 0) + return 0; + ret = decode_btl_solaris(path + 10, &bus_no, &target_no, &lun_no, 0); + if (ret <= 0) + return 0; + if (stat(path, &stbuf) == -1) + return 0; + return 1; +} + +#else /* ! NIX */ + +int sg_is_enumerable_adr(char* adr) +{ + burn_drive_enumerator_t idx; + int initialize = 1, ret; + 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); +} +#endif /* NIX */ + + + + +/* Return 1 if the given path leads to a regular file or a device that can be + seeked, read, and possibly written with 2 kB granularity. +*/ +int burn_os_is_2k_seekrw(char *path, int flag) +{ + struct stat stbuf; + + if (stat(path, &stbuf) == -1) + return 0; + if (S_ISREG(stbuf.st_mode)) + return 1; + if (S_ISBLK(stbuf.st_mode)) + return 1; + return 0; +} + + +/** 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 write_start, off_t *bytes) +{ + struct stat stbuf; + int ret; + +#ifdef Libburn_os_has_statvfS + struct statvfs vfsbuf; +#endif + + char *testpath = NULL, *cpt; + off_t add_size = 0; + + BURN_ALLOC_MEM(testpath, char, 4096); + + testpath[0] = 0; + 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) + {ret = -1; goto ex;} + + } else if(S_ISBLK(stbuf.st_mode)) { + int open_mode = O_RDONLY, fd; + + fd = open(path, open_mode); + if (fd == -1) + {ret = -2; goto ex;} + *bytes = lseek(fd, 0, SEEK_END); + close(fd); + if (*bytes == -1) { + *bytes = 0; + {ret = 0; goto ex;} + } + + } else if(S_ISREG(stbuf.st_mode)) { + add_size = burn_sparse_file_addsize(write_start, &stbuf); + strcpy(testpath, path); + } else + {ret = 0; goto ex;} + + if (testpath[0]) { + +#ifdef Libburn_os_has_statvfS + + if (statvfs(testpath, &vfsbuf) == -1) + {ret = -2; goto ex;} + *bytes = add_size + ((off_t) vfsbuf.f_frsize) * + (off_t) vfsbuf.f_bavail; + +#else /* Libburn_os_has_statvfS */ + + {ret = 0; goto ex;} + +#endif /* ! Libburn_os_has_stavtfS */ + + } + ret = 1; +ex:; + BURN_FREE_MEM(testpath); + return ret; +} + + +/* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ + +#ifdef Libburn_read_o_direcT + + /* No special O_DIRECT-like precautions are implemented here */ + +#endif /* Libburn_read_o_direcT */ + + +int burn_os_open_track_src(char *path, int open_flags, int flag) +{ + int fd; + + fd = open(path, open_flags); + return fd; +} + + +void *burn_os_alloc_buffer(size_t amount, int flag) +{ + void *buf = NULL; + + buf = calloc(1, amount); + return buf; +} + + +int burn_os_free_buffer(void *buffer, size_t amount, int flag) +{ + if (buffer == NULL) + return 0; + free(buffer); + return 1; +} + diff --git a/trunk/libburn/sg.c b/trunk/libburn/sg.c new file mode 100644 index 0000000..bc07c95 --- /dev/null +++ b/trunk/libburn/sg.c @@ -0,0 +1,94 @@ + +/* sg.c + Switcher for operating system dependent transport level modules of libburn. + Copyright (C) 2009 - 2014 Thomas Schmitt <scdbackup@gmx.net>, + provided under GPLv2+ +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#undef HAVE_CONFIG_H +#endif + + +/* <<< Until it is known whether this adapter would work on OpenBSD too */ +#ifdef __NetBSD__ +#define Libburn_use_sg_netbsD +#endif + + +#ifdef Libburn_use_sg_dummY + +#include "sg-dummy.c" + +#else +#ifdef Libburn_use_libcdiO + +#include "sg-libcdio.c" + +#else +#ifdef Libburn_use_sg_netbsD +/* To become: # ifdef __NetBSD__ */ + +#include "sg-netbsd.c" + +#else +#ifdef __FreeBSD__ + +#ifdef Libburn_use_sg_freebsd_porT +#include "sg-freebsd-port.c" +#else +#include "sg-freebsd.c" +#endif + +#else +#ifdef __FreeBSD_kernel__ + +#ifdef Libburn_use_sg_freebsd_porT +#include "sg-freebsd-port.c" +#else +#include "sg-freebsd.c" +#endif + +#else +#ifdef __linux + +#include "sg-linux.c" + +#else +#ifdef __sun + +#include "sg-solaris.c" + +#else + +/* The dummy adapter formally fulfills the expectations of libburn towards + its SCSI command transport. It will show no drives and perform no SCSI + commands. + libburn will then be restricted to using its stdio pseudo drives. +*/ +static int intentional_compiler_warning(void) +{ + int INTENTIONAL_COMPILER_WARNING_; + int Cannot_recognize_supported_operating_system_; + int Like_GNU_Linux_or_FreeBSD_or_Solaris_or_NetBSD_; + int Have_to_use_dummy_MMC_transport_adapter_; + int This_libburn_will_not_be_able_to_operate_on_real_CD_drives; + int Have_to_use_dummy_MMC_transport_adapter; + int Like_GNU_Linux_or_FreeBSD_or_Solaris_or_NetBSD; + int Cannot_recognize_supported_operating_system; + int INTENTIONAL_COMPILER_WARNING; + + return(0); +} + +#include "sg-dummy.c" + +#endif /* ! __sun */ +#endif /* ! __linux */ +#endif /* ! __FreeBSD_kernel__ */ +#endif /* ! __FreeBSD__ */ +#endif /* ! Libburn_use_sg_netbsD */ +#endif /* ! Libburn_use_libcdiO */ +#endif /* ! Libburn_use_sg_dummY */ + diff --git a/trunk/libburn/sg.h b/trunk/libburn/sg.h new file mode 100644 index 0000000..fc05a84 --- /dev/null +++ b/trunk/libburn/sg.h @@ -0,0 +1,84 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (C) 2009 Thomas Schmitt <scdbackup@gmx.net>, provided under GPLv2+ +*/ + +#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_is_2k_seekrw(char *path, int flag); + +int burn_os_stdio_capacity(char *path, off_t write_start, off_t *bytes); + +/* ts A91227 */ +/** Returns the id string of the SCSI transport adapter and eventually + needed operating system facilities. + This call is usable even if sg_initialize() was not called yet. In that + case a preliminary constant message might be issued if detailed info is + not available yet. + @param msg returns id string + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_id_string(char msg[1024], int flag); + +/* ts A91225 */ +/** Performs global initialization of the SCSI transport adapter and eventually + needed operating system facilities. Checks for compatibility supporting + software components. + @param msg returns ids and/or error messages of eventual helpers + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_initialize(char msg[1024], int flag); + +/* ts A91227 */ +/** Performs global finalization of the SCSI transport adapter and eventually + needed operating system facilities. Releases globally aquired resources. + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_shutdown(int flag); + +/* ts A91227 */ +/** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of + struct burn_drive which are defined in os-*.h. + The eventual initialization of those components was made underneath + scsi_enumerate_drives(). + This will be called when a burn_drive gets disposed. + @param d the drive to be finalized + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_dispose_drive(struct burn_drive *d, int flag); + + +#endif /* __SG */ diff --git a/trunk/libburn/source.c b/trunk/libburn/source.c new file mode 100644 index 0000000..a2d8aae --- /dev/null +++ b/trunk/libburn/source.c @@ -0,0 +1,76 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2010 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + + +#include <stdlib.h> +#include <string.h> +#include "libburn.h" +#include "source.h" +#include "structure.h" +#include "init.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; + + /* ts A70825 , B11219 */ + out = burn_alloc_mem(sizeof(struct burn_source), 1, 0); + if (out == NULL) + return NULL; + + 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; +} + + +/* ts B00922 */ +int burn_source_read(struct burn_source *src, unsigned char *buffer, int size) +{ + int ret; + + if (src->read != NULL) + ret = src->read(src, buffer, size); + else + ret = src->read_xt(src, buffer, size); + return ret; +} diff --git a/trunk/libburn/source.h b/trunk/libburn/source.h new file mode 100644 index 0000000..90a96b5 --- /dev/null +++ b/trunk/libburn/source.h @@ -0,0 +1,12 @@ +/* -*- 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); + +int burn_source_read(struct burn_source *src, unsigned char *buffer, int size); + +#endif /*__SOURCE*/ diff --git a/trunk/libburn/spc.c b/trunk/libburn/spc.c new file mode 100644 index 0000000..fcbbb7d --- /dev/null +++ b/trunk/libburn/spc.c @@ -0,0 +1,1952 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +/* scsi primary commands */ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#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 "init.h" +#include "util.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + +/* ts A91111 : + whether to log SCSI commands: + bit0= log in /tmp/libburn_sg_command_log + bit1= log to stderr + bit2= flush every line +*/ +extern int burn_sg_log_scsi; + + +/* 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 }; +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; + memset(c, 0, sizeof(struct command)); + 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; + c->timeout = Libburn_scsi_default_timeouT; + return 1; +} + + +/* ts B00728 */ +int spc_decode_sense(unsigned char *sense, int senselen, + int *key, int *asc, int *ascq) +{ + *key = *asc = *ascq = 0; + if ((sense[0] & 0x7f) == 0x72 || (sense[0] & 0x7f) == 0x73) { + if (senselen <= 0 || senselen > 1) + *key = sense[1] & 0x0f; + if (senselen <= 0 || senselen > 2) + *asc = sense[2]; + if (senselen <= 0 || senselen > 3) + *ascq = sense[3]; + return 1; + } + if (senselen <= 0 || senselen > 2) + *key = sense[2] & 0x0f; + if (senselen <= 0 || senselen > 12) + *asc = sense[12]; + if (senselen <= 0 || senselen > 13) + *ascq = sense[13]; + return 1; +} + + +int spc_test_unit_ready_r(struct burn_drive *d, int *key, int *asc, int *ascq, + int *progress) +{ + struct command *c; + + c = &(d->casual_command); + 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->retry = 0; + c->dir = NO_TRANSFER; + d->issue_command(d, c); + *key = *asc = *ascq = 0; + *progress = -1; + if (c->error) { + spc_decode_sense(c->sense, 0, key, asc, ascq); + if (c->sense[0] == 0x70 && + ((c->sense[2] & 0x0f) == 0 || (c->sense[2] & 0x0f) == 2) && + (c->sense[15] & 0x80)) + *progress = (c->sense[16] << 8) + c->sense[17]; + return (key == 0); + } + return 1; +} + + +int spc_test_unit_ready(struct burn_drive *d) +{ + int key, asc, ascq, progress; + + return spc_test_unit_ready_r(d, &key, &asc, &ascq, &progress); +} + + +/* ts A70315 */ +/** @param flag bit0=do not wait 0.1 seconds before first test unit ready + bit1=do not issue success message + */ +/** 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, clueless_start = 0; + static double tests_per_second = 2.0; + int sleep_usecs, loop_limit, clueless_timeout, progress; + char *msg = NULL; + unsigned char sense[14]; + + BURN_ALLOC_MEM(msg, char, 320); + clueless_timeout = 5 * tests_per_second + 1; + loop_limit = max_sec * tests_per_second + 1; + sleep_usecs = 1000000 / tests_per_second; + + if (!(flag & 1)) + usleep(sleep_usecs); + + for(i = !(flag & 1); i < loop_limit; i++) { + ret = spc_test_unit_ready_r(d, &key, &asc, &ascq, &progress); + 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; + +handle_error:; + /* ts A90213 */ + sprintf(msg, + "Asynchronous SCSI error on %s: ", cmd_text); + sense[0] = 0x70; /* Fixed format sense data */ + sense[2] = key; + sense[12] = asc; + sense[13] = ascq; + scsi_error_msg(d, sense, 14, msg + strlen(msg), + &key, &asc, &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; + } else if (ascq == 0x00) { /* CAUSE NOT REPORTABLE */ + /* Might be a clueless system adapter */ + if (clueless_start == 0) + clueless_start = i; + if (i - clueless_start > clueless_timeout) { + libdax_msgs_submit(libdax_messenger, + d->global_index, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + "Ended clueless NOT READY cycle", + 0, 0); + ret = 1; /* medium not present = ok */ + break; + } + } else if (ascq == 0x02 || ascq == 0x03) + goto handle_error; + +slumber:; + usleep(sleep_usecs); + } + if (ret <= 0 || !(flag & 2)) { + 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) + {ret = (ret > 0); goto ex;} + + 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); + ret = 0; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +void spc_request_sense(struct burn_drive *d, struct buffer *buf) +{ + struct command *c; + + c = &(d->casual_command); + if (mmc_function_spy(d, "request_sense") <= 0) + return; + + scsi_init_command(c, SPC_REQUEST_SENSE, sizeof(SPC_REQUEST_SENSE)); + c->retry = 0; + 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); +} + +static int spc_report_async_error(struct burn_drive *d, + int key, int asc, int ascq, int flag) +{ + char *msg = NULL; + unsigned char sense[14]; + int ret; + + BURN_ALLOC_MEM(msg, char, BURN_DRIVE_ADR_LEN + 160); + + sprintf(msg, "Asynchronous SCSI error : "); + sense[0] = 0x70; /* Fixed format sense data */ + sense[2] = key; + sense[12] = asc; + sense[13] = ascq; + scsi_error_msg(d, sense, 14, msg + strlen(msg), &key, &asc, &ascq); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x000201a5, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + +/* @return -3 = other error reported + -2 = drive is ready , + -1 = not ready, but no progress reported , + >= 0 progress indication between 0 and 65535 +*/ +int spc_get_erase_progress(struct burn_drive *d) +{ + struct buffer *b = NULL; + int ret, key, asc, ascq, progress; + + if (mmc_function_spy(d, "get_erase_progress") <= 0) + {ret = 0; goto ex;} + + /* ts B20104 : + TEST UNIT READY seems to be more reliable than REQUEST SENSE. + Nevertheless growisofs still uses the latter as fallback. + */ + ret = spc_test_unit_ready_r(d, &key, &asc, &ascq, &progress); + if (ret > 0) + {ret = -2; goto ex;} + + /* Check key, asc, ascq for errors other than "not yet ready" */ + if (key != 0 && + (key != 0x2 || asc != 0x04 || ascq == 0x02 || ascq ==0x03)) { + spc_report_async_error(d, key, asc, ascq, 0); + ret= -3; goto ex; + } + + if (progress >= 0) + {ret = progress; goto ex;} + + /* Fallback to request sense */ + BURN_ALLOC_MEM(b, struct buffer, 1); + spc_request_sense(d, b); + + /* Checking the preconditions as of SPC-3 4.5.2.4.4 and 4.5.3 */ + ret = -1; + if (b->data[0] == 0x70 && + ((b->data[2] & 0x0f) == 0 || (b->data[2] & 0x0f) == 2) && + (b->data[15] & 0x80)) + ret = (b->data[16] << 8) | b->data[17]; +ex:; + BURN_FREE_MEM(b); + return ret; +} + +void spc_inquiry(struct burn_drive *d) +{ + struct buffer *buf = NULL; + struct burn_scsi_inquiry_data *id; + struct command *c = NULL; + + if (mmc_function_spy(d, "inquiry") <= 0) + return; + + BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); + BURN_ALLOC_MEM_VOID(c, struct command, 1); + scsi_init_command(c, SPC_INQUIRY, 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; + id->peripheral = 0x7f; /* SPC-3: incabable undefined peripheral type */ + id->version = 0; /* SPC-3: no claim for conformance */ + memset(id->vendor, 0, 9); + memset(id->product, 0, 17); + memset(id->revision, 0, 5); + if (c->error) { + id->valid = -1; + goto ex; + } + id->peripheral = ((char *) c->page->data)[0]; + id->version = ((char *) c->page->data)[2]; + 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; +ex:; + BURN_FREE_MEM(buf); + BURN_FREE_MEM(c); + return; +} + +void spc_prevent(struct burn_drive *d) +{ + struct command *c; + + c = &(d->casual_command); + if (mmc_function_spy(d, "prevent") <= 0) + return; + + scsi_init_command(c, SPC_PREVENT, sizeof(SPC_PREVENT)); + c->retry = 1; + c->dir = NO_TRANSFER; + d->issue_command(d, c); + +#ifdef Libburn_pioneer_dvr_216d_get_evenT + mmc_get_event(d); +#endif + +} + +void spc_allow(struct burn_drive *d) +{ + struct command *c; + + c = &(d->casual_command); + if (mmc_function_spy(d, "allow") <= 0) + return; + + scsi_init_command(c, SPC_ALLOW, sizeof(SPC_ALLOW)); + c->retry = 1; + c->dir = NO_TRANSFER; + d->issue_command(d, c); +} + + +/* ts B40216 : Outsourced from spc_sense_caps_al(). + To be called by spc_sense_caps() after spc_sense_caps_al() +*/ +static int spc_try_get_performance(struct burn_drive *d, int flag) +{ + int ret; + struct burn_feature_descr *feature_descr; + + /* ts B40107 : Feature 0x107 announces availability of GET PERFORMANCE + Its WSPD bit announces Type 3. + Try this even if the feature is not current. + */ + ret = burn_drive_has_feature(d, 0x107, &feature_descr, 0); + if (ret <= 0) + return ret; + if (feature_descr->data_lenght <= 0) + return 1; + if (feature_descr->data[0] & 2) /* WSPD */ + ret = mmc_get_write_performance(d); + /* Get read performance */ + mmc_get_performance(d, 0x00, 0); + return 1; +} + + +/* +ts A70518 - A90603 : Do not call with *alloc_len < 10 +*/ +/** @param flag bit0= do only inquire alloc_len + @return 1=ok , <=0 error , + 2=Block Descriptor Length > 0, retry with flag bit1 +*/ +static int spc_sense_caps_al(struct burn_drive *d, int *alloc_len, int flag) +{ + struct buffer *buf = NULL; + struct scsi_mode_data *m; + int page_length, num_write_speeds = 0, i, speed, ret; + int old_alloc_len, was_error = 0, block_descr_len; + unsigned char *page; + struct command *c = NULL; + struct burn_speed_descriptor *sd; + char *msg = NULL; + + /* ts A61225 : 1 = report about post-MMC-1 speed descriptors */ + static int speed_debug = 0; + + if (*alloc_len < 10) + {ret = 0; goto ex;} + + BURN_ALLOC_MEM(msg, char, BURN_DRIVE_ADR_LEN + 160); + BURN_ALLOC_MEM(buf, struct buffer, 1); + BURN_ALLOC_MEM(c, struct command, 1); + + /* ts A90602 : Clearing mdata before command execution */ + m = d->mdata; + m->p2a_valid = 0; + burn_mdata_free_subs(m); + + memset(buf, 0, sizeof(struct buffer)); + scsi_init_command(c, SPC_MODE_SENSE, 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(struct buffer)); + m->p2a_valid = -1; + was_error = 1; + } + + /* ts B11103 : qemu SCSI CD-ROM has Block Descriptor Length > 0. + The descriptors come between header and page. + */ + block_descr_len = c->page->data[6] * 256 + c->page->data[7]; + + if (block_descr_len + 8 + 2 > *alloc_len) { + if (block_descr_len + 8 + 2 > BUFFER_SIZE || !(flag & 1)) { + m->p2a_valid = -1; + sprintf(msg, + "MODE SENSE page 2A with oversized Block Descriptors: %s : %d", + d->devname, block_descr_len); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002016e, LIBDAX_MSGS_SEV_DEBUG, + LIBDAX_MSGS_PRIO_LOW, msg, 0, 0); + {ret = 0; goto ex;} + + } + *alloc_len = block_descr_len + 10; + {ret = 2; goto ex;} + } + + /* Skip over Mode Data Header and block descriptors */ + page = c->page->data + 8 + block_descr_len; + + /* 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". + ts B11031 : + qemu emulates an ATAPI DVD-ROM, which delivers only a page length + of 18. This is now tolerated. + */ + /* ts A90603 : + SPC-1 8.3.3 enumerates mode page format bytes from 0 to n and + defines Page Length as (n-1). + */ + page_length = page[1]; + old_alloc_len = *alloc_len; + *alloc_len = page_length + 10 + block_descr_len; + if (flag & 1) + {ret = !was_error; goto ex;} + if (page_length + 10 > old_alloc_len) + page_length = old_alloc_len - 10; + + /* ts A90602 : page_length N asserts page[N+1]. (see SPC-1 8.3.3) */ + /* ts B11031 : qemu drive has a page_length of 18 */ + if (page_length < 18) { + m->p2a_valid = -1; + sprintf(msg, "MODE SENSE page 2A too short: %s : %d", + d->devname, page_length); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002016e, LIBDAX_MSGS_SEV_DEBUG, + LIBDAX_MSGS_PRIO_LOW, msg, 0, 0); + {ret = 0; goto ex;} + } + + 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 = m->cur_write_speed = 0; + if (page_length >= 18) /* note: page length is page size - 2 */ + m->max_write_speed = page[18] * 256 + page[19]; + if (page_length >= 20) + 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; + + if (!was_error) + m->p2a_valid = 1; + + /* ts A61225 : end of MMC-1 , begin of MMC-3 */ + if (page_length < 30) /* no write speed descriptors ? */ + goto no_speed_descriptors; + + 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) { + 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); + {ret = 0; goto ex;} + } + + 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); + +no_speed_descriptors:; + + ret = !was_error; +ex: + BURN_FREE_MEM(msg); + BURN_FREE_MEM(buf); + BURN_FREE_MEM(c); + return ret; +} + + +void spc_sense_caps(struct burn_drive *d) +{ + int alloc_len, start_len = 30, minimum_len = 28, ret; + + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "sense_caps") <= 0) + return; + + mmc_get_configuration(d); + + /* first command execution to learn Allocation Length */ + alloc_len = start_len; + ret = spc_sense_caps_al(d, &alloc_len, 1); + if (ret == 2) { + /* ts B40205: Unexpectedly found Block Descriptors. + Repeat with new alloc_len. + */ + ret = spc_sense_caps_al(d, &alloc_len, 1); + if (ret == 2) + goto try_get_performance; + } + /* ts B11103: + qemu ATAPI DVD-ROM delivers only 28. + SanDisk Cruzer U3 memory stick throws error on alloc_len < 30. + MMC-1 prescribes that 30 are available. qemu tolerates 30. + */ + if (alloc_len >= minimum_len && ret > 0) + /* second execution with announced length */ + spc_sense_caps_al(d, &alloc_len, 0); + +try_get_performance:; + spc_try_get_performance(d, 0); +} + + +void spc_sense_error_params(struct burn_drive *d) +{ + struct buffer *buf = NULL; + struct scsi_mode_data *m; + int alloc_len = 12 ; + unsigned char *page; + struct command *c = NULL; + + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "sense_error_params") <= 0) + goto ex; + + BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); + BURN_ALLOC_MEM_VOID(c, struct command, 1); + + scsi_init_command(c, SPC_MODE_SENSE, 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); + m = d->mdata; + page = c->page->data + 8; + d->params.retries = page[3]; + m->retry_page_length = page[1]; + m->retry_page_valid = 1; +ex:; + BURN_FREE_MEM(buf); + BURN_FREE_MEM(c); +} + +void spc_select_error_params(struct burn_drive *d, + const struct burn_read_opts *o) +{ + struct buffer *buf = NULL; + struct command *c = NULL; + + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "select_error_params") <= 0) + goto ex; + + BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); + BURN_ALLOC_MEM_VOID(c, struct command, 1); + + scsi_init_command(c, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); + c->retry = 1; + if (d->mdata->retry_page_valid <= 0) + d->mdata->retry_page_length = 0; + c->opcode[8] = 8 + 2 + d->mdata->retry_page_length; + c->page = buf; + c->page->bytes = 0; + c->page->sectors = 0; + + 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; + c->page->data[11] = d->params.retries; + c->dir = TO_DRIVE; + d->issue_command(d, c); +ex:; + BURN_FREE_MEM(buf); + BURN_FREE_MEM(c); +} + +void spc_sense_write_params(struct burn_drive *d) +{ + struct buffer *buf = NULL; + struct scsi_mode_data *m; + int dummy1, dummy2, alloc_len = 10; + unsigned char *page; + struct command *c = NULL; + + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "sense_write_params") <= 0) + goto ex; + + BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); + BURN_ALLOC_MEM_VOID(c, struct command, 1); + + /* 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)); + 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) { + page = c->page->data + 8; + m->write_page_length = page[1]; + if (m->write_page_length > 0) + m->write_page_valid = 1; + else + m->write_page_length = 0x32; + } + 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->current_profile == 0x43) + d->read_format_capacities(d, -1); + else if (d->status == BURN_DISC_BLANK || + (d->current_is_cd_profile && d->status == BURN_DISC_APPENDABLE)) { + burn_drive_send_default_page_05(d, 0); + d->get_nwa(d, -1, &dummy1, &dummy2); + } + /* 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); +*/ + +ex:; + BURN_FREE_MEM(buf); + BURN_FREE_MEM(c); +} + + +/* 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, struct burn_session *s, + int tnum, const struct burn_write_opts *o) +{ + struct buffer *buf = NULL; + struct command *c = NULL; + int alloc_len; + + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "select_write_params") <= 0) + goto ex; + + BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); + BURN_ALLOC_MEM_VOID(c, struct command, 1); + + /* 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)); + */ + + alloc_len = 8 + 2 + d->mdata->write_page_length; + memset(&(buf->data), 0, alloc_len); + +#ifdef Libburn_pioneer_dvr_216d_load_mode5 + + scsi_init_command(c, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); + c->dxfer_len = alloc_len; + c->opcode[7] = (alloc_len >> 8) & 0xff; + c->opcode[8] = alloc_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); + + if (c->error) + memset(&(buf->data), 0, + 8 + 2 + d->mdata->write_page_length); + +#endif /* Libburn_pioneer_dvr_216d_load_mode5 */ + + scsi_init_command(c, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); + c->retry = 1; + c->opcode[7] = (alloc_len >> 8) & 0xff; + c->opcode[8] = alloc_len & 0xff; + c->page = buf; + c->page->bytes = 0; + c->page->sectors = 0; + + c->page->bytes = alloc_len; + + /* ts A61229 */ + if (mmc_compose_mode_page_5(d, s, tnum, o, c->page->data + 8) <= 0) + goto ex; + + c->dir = TO_DRIVE; + d->issue_command(d, c); +ex:; + BURN_FREE_MEM(buf); + BURN_FREE_MEM(c); +} + +void spc_getcaps(struct burn_drive *d) +{ + if (mmc_function_spy(d, "getcaps") <= 0) + return; + + burn_speed_descriptor_destroy(&(d->mdata->speed_descriptors), 1); + 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 = NULL; + 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 = NULL; + + mmc_start_if_needed(d, 1); + if (mmc_function_spy(d, "spc_probe_write_modes") <= 0) + goto ex; + + BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); + BURN_ALLOC_MEM_VOID(c, struct command, 1); + + /* ts A70213 : added pseudo try_write_type 4 to set a suitable mode */ + while (try_write_type != 5) { + /* 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)); + 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; + + spc_decode_sense(c->sense, 0, &key, &asc, &ascq); + if (key) + /* try_block_type not supported */; + else { + /* try_write_type, try_block_type is supported mode */ + 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: + goto ex; + } + } +ex:; + BURN_FREE_MEM(buf); + BURN_FREE_MEM(c); +} + +/* ( 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; + + /* ts B21023 */ + d->had_particular_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->p2a_valid = 0; + d->mdata->max_read_speed = 0; + d->mdata->cur_read_speed = 0; + d->mdata->max_write_speed = 0; + d->mdata->cur_write_speed = 0; + d->mdata->speed_descriptors = NULL; + d->mdata->write_page_length = 0x32; + d->mdata->write_page_valid = 0; + 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 - A80829 */ +enum response scsi_error_msg(struct burn_drive *d, unsigned char *sense, + int senselen, char msg_data[161], + int *key, int *asc, int *ascq) +{ + int ret; + char *msg; + static char key_def[16][40] = { + "(no specific error)", + "Recovered error", + "Drive not ready", + "Medium error", + "Drive error", + "Illegal request", + "Drive event", + "Data protected", + "Blank/Nonblank", + "Vendor specific code", + "Copy aborted", + "Command aborted", + "(obsolete error code)", + "Volume overflow", + "Miscompare", + "(reserved error code)", + }; + + msg= msg_data; + *key= *asc= *ascq= -1; + + ret = spc_decode_sense(sense, senselen, key, asc, ascq); + if (ret <= 0) + *key= *asc= *ascq= -1; + + sprintf(msg, "[%X %2.2X %2.2X] ", *key, *asc, *ascq); + msg= msg + strlen(msg); + + if (key_def[*key & 0xf][0] != '(') { + sprintf(msg, "%s. ", key_def[*key & 0xf]); + msg= msg + strlen(msg); + } + + switch (*asc) { + case 0x00: + if (*key > 0 || *ascq > 0) + break; /* Fall through to unknown error */ + sprintf(msg, "(No error reported by SCSI transaction)"); + return GO_ON; + + case 0x02: + sprintf(msg, "Not ready"); + goto return_retry; + case 0x04: + if (*ascq == 1) + sprintf(msg, + "Logical unit is in the process of becoming ready"); + else + sprintf(msg, "Logical unit is not ready"); + goto return_retry; + case 0x06: + if (*ascq == 0) + sprintf(msg, "No reference position found"); + else + break; + goto return_fail; + case 0x08: + if (*ascq == 0) + sprintf(msg, "Logical unit communication failure"); + else if (*ascq == 1) + sprintf(msg, "Logical unit communication timeout"); + else if (*ascq == 2) + sprintf(msg, "Logical unit communication parity error"); + else if (*ascq == 3) + sprintf(msg, "Logical unit communication crc error"); + else + break; + goto return_retry; + case 0x09: + if (*ascq == 0) + sprintf(msg, "Track following error"); + else if (*ascq == 1) + sprintf(msg, "Tracking servo failure"); + else if (*ascq == 2) + sprintf(msg, "Focus servo failure"); + else if (*ascq == 3) + sprintf(msg, "Spindle servo failure"); + else if (*ascq == 4) + sprintf(msg, "Head select fault"); + else + break; + goto return_fail; + case 0x0C: + if (*ascq == 0) + sprintf(msg, "Write error"); + else if (*ascq == 1) + sprintf(msg, + "Write error, recovered with auto-allocation"); + else if (*ascq == 2) + sprintf(msg, "Write error, auto reallocation failed"); + else if (*ascq == 7) + sprintf(msg, "Write error, recovery needed"); + else if (*ascq == 8) + sprintf(msg, "Write error, recovery failed"); + else if (*ascq == 9) + sprintf(msg, "Write error, loss of streaming"); + else if (*ascq == 0x0f) + sprintf(msg, "Defects in error window"); + else + break; + goto return_fail; + case 0x11: + if (*ascq == 0) + sprintf(msg, "Unrecovered read error"); + else if (*ascq == 1) + sprintf(msg, "Read retries exhausted"); + else if (*ascq == 2) + sprintf(msg, "Error too long to correct"); + else if (*ascq == 5) + sprintf(msg, "L-EC uncorrectable error"); + else if (*ascq == 6) + sprintf(msg, "CIRC uncorrectable error"); + else + break; + goto return_fail; + case 0x15: + if (*ascq == 0) + sprintf(msg, "Random positioning error"); + else if (*ascq == 1) + sprintf(msg, "Mechanical positioning error"); + else + break; + goto return_fail; + case 0x1a: + if (*ascq == 0) + sprintf(msg, "Parameter list length error"); + else + break; + goto return_fail; + case 0x1b: + if (*ascq == 0) + sprintf(msg, "Synchronous data transfer error"); + else + break; + goto return_fail; + case 0x20: + if (*ascq == 0) + sprintf(msg, "Invalid command operation code"); + else + break; + goto return_fail; + case 0x21: + if (*ascq == 0) + sprintf(msg, "Lba out of range"); + else if (*ascq == 1) + sprintf(msg, "Invalid element address"); + else if (*ascq == 2) + sprintf(msg, "Invalid address for write"); + else if (*ascq == 3) + sprintf(msg, "Invalid write crossing layer jump"); + else + break; + goto return_fail; + case 0x24: + if (*ascq == 0) + sprintf(msg, "Invalid field in cdb"); + else + break; + goto return_fail; + case 0x26: + if (*ascq == 0) + sprintf(msg, "Invalid field in parameter list"); + else if (*ascq == 1) + sprintf(msg, "Parameter not supported"); + else if (*ascq == 2) + sprintf(msg, "Parameter value invalid"); + else + break; + goto return_fail; + case 0x27: + sprintf(msg, "Write protected"); + goto return_fail; + case 0x28: + if (*ascq == 0) + sprintf(msg, "Medium may have changed"); + else if (*ascq == 2) + sprintf(msg, "Format layer may have changed"); + else + break; + goto return_retry; + case 0x29: + if (*ascq == 0) + sprintf(msg, + "Power on, reset, or bus device reset occured"); + else if (*ascq == 1) + sprintf(msg, "Power on occured"); + else if (*ascq == 2) + sprintf(msg, "Bus reset occured"); + else if (*ascq == 3) + sprintf(msg, "Bus device reset function occured"); + else if (*ascq == 4) + sprintf(msg, "Device internal reset"); + else + break; + goto return_retry; + case 0x2c: + if (*ascq == 0) + sprintf(msg, "Command sequence error"); + else + break; + goto return_fail; + case 0x2e: + if (*ascq == 0) + sprintf(msg, "Insufficient time for operation"); + else + break; + goto return_fail; + case 0x30: + if (*ascq == 0) + sprintf(msg, "Incompatible medium installed"); + else if (*ascq == 1) + sprintf(msg, "Cannot read medium, unknown format"); + else if (*ascq == 2) + sprintf(msg, + "Cannot read medium, incompatible format"); + else if (*ascq == 4) + sprintf(msg, "Cannot write medium, unknown format"); + else if (*ascq == 5) + sprintf(msg, + "Cannot write medium, incompatible format"); + else if (*ascq == 6) + sprintf(msg, + "Cannot format medium, incompatible medium"); + else if (*ascq == 7) + sprintf(msg, "Cleaning failure"); + else + break; + goto return_fail; + case 0x31: + if (*ascq == 0) + sprintf(msg, "Medium unformatted or format corrupted"); + else if (*ascq == 1) + sprintf(msg, "Format command failed"); + else + break; + goto return_fail; + case 0x32: + if (*ascq == 0) + sprintf(msg, "No defect spare location available"); + else + break; + goto return_fail; + case 0x3A: + if (*ascq == 0) + sprintf(msg, "Medium not present"); + else if (*ascq == 1) + sprintf(msg, "Medium not present, tray closed"); + else if (*ascq == 2) + sprintf(msg, "Medium not present, tray open"); + else if (*ascq == 3) + sprintf(msg, "Medium not present, loadable"); + else + break; + d->status = BURN_DISC_EMPTY; + goto return_fail; + case 0x3E: + if (*ascq == 1) + sprintf(msg, "Logical unit failure"); + else if (*ascq == 2) + sprintf(msg, "Timeout on logical unit"); + else + break; + goto return_fail; + case 0x44: + if (*ascq == 0) + sprintf(msg, "Internal target failure"); + else + break; + goto return_fail; + case 0x51: + if (*ascq == 0) + sprintf(msg, "Erase failure"); + else if (*ascq == 1) + sprintf(msg, "Erase failure. Incomplete erase operation"); + else + break; + goto return_fail; + case 0x57: + if (*ascq == 0) + sprintf(msg, "Unable to recover Table-of-Content"); + else + break; + goto return_fail; + case 0x63: + if (*ascq == 0) + sprintf(msg, + "End of user area encountered on this track"); + else if (*ascq == 1) + sprintf(msg, "Packet does not fit in available space"); + else + break; + goto return_fail; + case 0x64: + if (*ascq == 0) + sprintf(msg, "Illegal mode for this track"); + else if (*ascq == 1) + sprintf(msg, "Invalid packet size"); + else + break; + goto return_fail; + case 0x72: + if (*ascq == 0) + sprintf(msg, "Session fixation error"); + else if (*ascq == 1) + sprintf(msg, "Session fixation error writing lead-in"); + else if (*ascq == 2) + sprintf(msg, + "Session fixation error writing lead-out"); + else if (*ascq == 3) + sprintf(msg, + "Session fixation error, incomplete track in session"); + else if (*ascq == 4) + sprintf(msg, + "Empty or partially written reserved track"); + else if (*ascq == 5) + sprintf(msg, "No more track reservations allowed"); + else + break; + goto return_fail; + case 0x73: + if (*ascq == 0) + sprintf(msg, "CD control error"); + else if (*ascq == 1) + sprintf(msg, "Power calibration area almost full"); + else if (*ascq == 2) + sprintf(msg, "Power calibration area is full"); + else if (*ascq == 3) + sprintf(msg, "Power calibration area error"); + else if (*ascq == 4) + sprintf(msg, "Program memory area update failure"); + else if (*ascq == 5) + sprintf(msg, "Program memory area is full"); + else + break; + goto return_fail; + } + sprintf(msg_data, + "See MMC specs: Sense Key %X \"%s\", ASC %2.2X ASCQ %2.2X", + *key & 0xf, key_def[(*key) & 0xf], *asc, *ascq); + goto return_fail; + +return_fail: + strcat(msg, "."); + if (*key == 1) + return GO_ON; + return FAIL; + +return_retry:; + strcat(msg, "."); + if (*key == 1) + return GO_ON; + return RETRY; +} + + +/* 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, ret = 0; + char *msg = NULL; + enum response resp; + + BURN_ALLOC_MEM(msg, char, 160); + resp = scsi_error_msg(d, sense, senselen, msg, &key, &asc, &ascq); +ex:; + if (ret == -1) + resp = FAIL; + BURN_FREE_MEM(msg); + return resp; +} + + +static char *scsi_command_name(unsigned int c, int flag) +{ + switch (c) { + case 0x00: + return "TEST UNIT READY"; + case 0x03: + return "REQUEST SENSE"; + case 0x04: + return "FORMAT UNIT"; + case 0x1b: + return "START/STOP UNIT"; + case 0x12: + return "INQUIRY"; + case 0x1e: + return "PREVENT/ALLOW MEDIA REMOVAL"; + case 0x23: + return "READ FORMAT CAPACITIES"; + case 0x25: + return "READ CAPACITY"; + case 0x28: + return "READ(10)"; + case 0x2a: + return "WRITE(10)"; + case 0x35: + return "SYNCHRONIZE CACHE"; + case 0x43: + return "READ TOC/PMA/ATIP"; + case 0x46: + return "GET CONFIGURATION"; + case 0x4a: + return "GET EVENT STATUS NOTIFICATION"; + case 0x51: + return "READ DISC INFORMATION"; + case 0x52: + return "READ TRACK INFORMATION"; + case 0x53: + return "RESERVE TRACK"; + case 0x54: + return "SEND OPC INFORMATION"; + case 0x55: + return "MODE SELECT"; + case 0x5a: + return "MODE SENSE"; + case 0x5b: + return "CLOSE TRACK/SESSION"; + case 0x5c: + return "READ BUFFER CAPACITY"; + case 0x5d: + return "SEND CUE SHEET"; + case 0xa1: + return "BLANK"; + case 0xaa: + return "WRITE(12)"; + case 0xac: + return "GET PERFORMANCE"; + case 0xad: + return "READ DISC STRUCTURE"; + case 0xb6: + return "SET STREAMING"; + case 0xb9: + return "READ CD MSF"; + case 0xbb: + return "SET CD SPEED"; + case 0xbe: + return "READ CD"; + +#ifdef Libburn_develop_quality_scaN + + case 0xf3: + return "NEC/OPTIARC REPORT ERROR RATE"; + +#endif /* Libburn_develop_quality_scaN */ + + } + return "(NOT IN LIBBURN COMMAND LIST)"; +} + + +/* ts A61030 - A61115 */ +/* @param flag bit0= do report conditions which are considered not an error + bit1= report with severity FAILURE rather than DEBUG + */ +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 = NULL, *scsi_msg = NULL; + + if (d->silent_on_scsi_error == 1 || d->silent_on_scsi_error == 2) + {ret = 1; goto ex;} + + BURN_ALLOC_MEM(msg, char, 320); + BURN_ALLOC_MEM(scsi_msg, char, 160); + scsi_error_msg(d, sense, senselen, scsi_msg, &key, &asc, &ascq); + + if (!(flag & 1)) { + /* SPC : TEST UNIT READY command */ + if (c->opcode[0] == 0) + {ret = 1; goto ex;} + /* MMC : READ DISC INFORMATION command */ + if (c->opcode[0] == 0x51) + if (key == 0x2 && asc == 0x3A && + ascq>=0 && ascq <= 0x02) /* MEDIUM NOT PRESENT */ + {ret = 1; goto ex;} + if (key == 0 && asc == 0 && ascq == 0) + {ret = 1; goto ex;} + } + + sprintf(msg, "SCSI error condition on command %2.2Xh %s: ", + c->opcode[0], + scsi_command_name((unsigned int) c->opcode[0], 0)); + strcat(msg, scsi_msg); + ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f, + (flag & 2) && d->silent_on_scsi_error != 3 ? + LIBDAX_MSGS_SEV_FAILURE : LIBDAX_MSGS_SEV_DEBUG, + LIBDAX_MSGS_PRIO_HIGH, msg,0,0); +ex:; + BURN_FREE_MEM(msg); + BURN_FREE_MEM(scsi_msg); + return ret; +} + + +/* ts B11110 */ +/* @param flag bit0= do not show eventual data payload sent to the drive + (never with WRITE commands) +*/ +int scsi_show_command(unsigned char *opcode, int oplen, int dir, + unsigned char *data, int bytes, + void *fp_in, int flag) +{ + int i; + FILE *fp = fp_in; + + fprintf(fp, "\n%s\n", + scsi_command_name((unsigned int) opcode[0], 0)); + for(i = 0; i < 16 && i < oplen; i++) + fprintf(fp, "%2.2x ", opcode[i]); + if (i > 0) + fprintf(fp, "\n"); + if (flag & 1) + return 1; + if (opcode[0] == 0x2A) { /* WRITE 10 */ + if (flag & 2) + fprintf(fp, "%d -> %d\n", + (opcode[7] << 8) | opcode[8], + mmc_four_char_to_int(opcode + 2)); + } else if (opcode[0] == 0xAA) { /* WRITE 12 */ + if (flag & 2) + fprintf(fp, "%d -> %d\n", + mmc_four_char_to_int(opcode + 6), + mmc_four_char_to_int(opcode + 2)); + } else if (dir == TO_DRIVE && !(flag & 1)) { + fprintf(fp, "To drive: %db\n", bytes); + for (i = 0; i < bytes; i++) + fprintf(fp, "%2.2x%c", data[i], + ((i % 20) == 19 ? '\n' : ' ')); + if (i % 20) + fprintf(fp, "\n"); + } + return 1; +} + + + +/* ts A91106 */ +/* @param flag bit0= do not show eventual data payload sent to the drive + (never with WRITE commands) +*/ +int scsi_show_cmd_text(struct command *c, void *fp_in, int flag) +{ + return scsi_show_command(c->opcode, c->oplen, c->dir, c->page->data, + c->page->bytes, fp_in, flag); +} + + +/* ts A91106 */ /* ts B11110 */ +int scsi_show_command_reply(unsigned char *opcode, int data_dir, + unsigned char *data, int dxfer_len, + void *fp_in, int flag) +{ + int i; + FILE *fp = fp_in; + + if (data_dir != FROM_DRIVE) + return 2; + if (opcode[0] == 0x28 || opcode[0] == 0x3C || + opcode[0] == 0xA8 || opcode[0] == 0xB9 || opcode[0] == 0xBE) { + /* READ commands */ + /* >>> report amount of data */; + + return 2; + } + fprintf(fp, "From drive: %db\n", dxfer_len); + for (i = 0; i < dxfer_len; i++) + fprintf(fp, "%2.2x%c", data[i], + ((i % 20) == 19 ? '\n' : ' ')); + if (i % 20) + fprintf(fp, "\n"); + return 1; +} + + +/* ts B11110 */ +/** Logs command (before execution) */ +int scsi_log_command(unsigned char *opcode, int oplen, int data_dir, + unsigned char *data, int bytes, + void *fp_in, int flag) +{ + FILE *fp = fp_in; + + if (fp != NULL && (fp == stderr || (burn_sg_log_scsi & 1))) { + scsi_show_command(opcode, oplen, data_dir, data, bytes, fp, 0); + if (burn_sg_log_scsi & 4) + fflush(fp); + } + if (fp == stderr || !(burn_sg_log_scsi & 2)) + return 1; + scsi_log_command(opcode, oplen, data_dir, data, bytes, stderr, 0); + return 1; +} + + +/* ts B40731 */ +/* Arbitrary SCSI log message */ +int scsi_log_text(char *text, void *fp_in, int flag) +{ + FILE *fp = fp_in; + + if (fp != NULL && (fp == stderr || (burn_sg_log_scsi & 1))) { + fprintf(fp, "%s\n", text); + if (burn_sg_log_scsi & 4) + fflush(fp); + } + if (fp == stderr || !(burn_sg_log_scsi & 2)) + return 1; + fprintf(stderr, "%s\n", text); + return 1; +} + + +/* ts A91218 (former sg_log_cmd ts A70518) */ +/** Logs command (before execution) */ +int scsi_log_cmd(struct command *c, void *fp_in, int flag) +{ + int ret, bytes = 0; + unsigned char *data = NULL; + + if (c->page != NULL) { + data = c->page->data; + bytes = c->page->bytes; + } + ret = scsi_log_command(c->opcode, c->oplen, c->dir, data, bytes, + fp_in, flag); + return ret; +} + + +/* ts B11110 */ +/** Logs outcome of a sg command. + @param flag bit0 causes an error message + bit1 do not print duration +*/ +int scsi_log_reply(unsigned char *opcode, int data_dir, unsigned char *data, + int dxfer_len, void *fp_in, unsigned char sense[18], + int sense_len, double duration, int flag) +{ + FILE *fp = fp_in; + int key, asc, ascq, i, l; + + if (fp != NULL && (fp == stderr || (burn_sg_log_scsi & 1))) { + if (flag & 1) { + l = 18; + if ((sense[0] & 0x7f) == 0x72 || + (sense[0] & 0x7f) == 0x73) + l = sense[7] + 7 + 1; /* SPC-3 4.5.2. */ + if (l > sense_len) + l = sense_len; + fprintf(fp, "+++ sense data ="); + for (i = 0 ; i < l; i++) + fprintf(fp, " %2.2X", sense[i]); + fprintf(fp, "\n"); + spc_decode_sense(sense, 0, &key, &asc, &ascq); + fprintf(fp, "+++ key=%X asc=%2.2Xh ascq=%2.2Xh\n", + (unsigned int) key, (unsigned int) asc, + (unsigned int) ascq); + } else { + scsi_show_command_reply(opcode, data_dir, data, + dxfer_len, fp, 0); + } + if (!(flag & 2)) + fprintf(fp, " %8.f us [ %.f ]\n", + duration * 1.0e6, + (burn_get_time(0) - lib_start_time) * 1.0e6); + if (burn_sg_log_scsi & 4) + fflush(fp); + } + if (fp == stderr || !(burn_sg_log_scsi & 2)) + return 1; + scsi_log_reply(opcode, data_dir, data, dxfer_len, + stderr, sense, sense_len, duration, flag); + + return 1; +} + + +/* ts A91221 (former sg_log_err ts A91108) */ +/** Legacy frontend to scsi_log_reply(). + @param flag bit0 causes an error message + bit1 do not print duration +*/ +int scsi_log_err(struct burn_drive *d, struct command *c, + void *fp_in, unsigned char sense[18], + int sense_len, int flag) +{ + int ret; + unsigned char *data = NULL; + + if (c->page != NULL) + data = c->page->data; + + ret= scsi_log_reply(c->opcode, c->dir, data, c->dxfer_len , + fp_in, sense, sense_len, + c->end_time - c->start_time, flag); + return ret; +} + +/* ts B31112 */ +int scsi_log_message(struct burn_drive *d, void *fp_in, char * msg, int flag) +{ + int ret; + FILE *fp = fp_in; + + if (fp != NULL && (fp == stderr || (burn_sg_log_scsi & 1))) { + fprintf(fp, "%s\n", msg); + if (burn_sg_log_scsi & 4) + fflush(fp); + } + if (fp == stderr || !(burn_sg_log_scsi & 2)) + return 1; + ret = scsi_log_message(d, stderr, msg, flag); + return ret; +} + +/* ts B00808 */ +/* + @param flag bit0 = do not retry + bit1 = do not print duration + @return 0 = not yet done , 1 = done , -1 = error +*/ +int scsi_eval_cmd_outcome(struct burn_drive *d, struct command *c, void *fp, + unsigned char *sense, int sense_len, + time_t start_time, int timeout_ms, + int loop_count, int flag) +{ + enum response outcome; + int done = -1, usleep_time; + char *msg = NULL; + + if (burn_sg_log_scsi & 3) + scsi_log_err(d, c, fp, sense, sense_len, + (sense_len > 0) | (flag & 2)); + if (sense_len <= 0) + {done = 1; goto ex;} + + outcome = scsi_error(d, sense, sense_len); + if (outcome == RETRY && c->retry && !(flag & 1)) { + /* Calming down retries and breaking up endless cycle + */ + if (c->opcode[0] == 0x2A || c->opcode[0] == 0xAA) { + /* WRITE(10) , WRITE(12) */ + usleep_time = Libburn_scsi_write_retry_usleeP + + loop_count * Libburn_scsi_write_retry_incR; + if (usleep_time > Libburn_scsi_write_retry_umaX) + usleep_time = Libburn_scsi_write_retry_umaX; + } else { + usleep_time = Libburn_scsi_retry_usleeP + + loop_count * Libburn_scsi_retry_incR; + if (usleep_time > Libburn_scsi_retry_umaX) + usleep_time = Libburn_scsi_retry_umaX; + } + if (time(NULL) + usleep_time / 1000000 - start_time > + timeout_ms / 1000 + 1) { + done = -1; /* In case of alloc failure */ + BURN_ALLOC_MEM_VOID(msg, char, 320); + done = 1; + sprintf(msg, + "Timeout exceed (%d ms). Retry canceled.\n", + timeout_ms); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002018a, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + goto err_ex; + } + if (d->cancel) + {done = 1; goto ex;} + if (usleep_time > 0) + usleep(usleep_time); + if (d->cancel) + {done = 1; goto ex;} + if (burn_sg_log_scsi & 3) + scsi_log_cmd(c, fp, 0); + {done = 0; goto ex;} + } else if (outcome == RETRY) { + done = 1; + } else if (outcome == GO_ON) { + {done = 1; goto ex;} + } else if (outcome == FAIL) { + done = 1; + } +err_ex:; + c->error = 1; + scsi_notify_error(d, c, sense, sense_len, 0); +ex:; + BURN_FREE_MEM(msg); + return done; +} + + +int spc_confirm_cd_drive(struct burn_drive *d, int flag) +{ + char *msg = NULL; + int ret; + + BURN_ALLOC_MEM(msg, char, strlen(d->devname) + 1024); + + spc_inquiry(d); + if (d->idata->valid < 0) { + sprintf(msg, "INQUIRY failed with drive '%s'", d->devname); + libdax_msgs_submit(libdax_messenger, -1, 0x0002000a, + LIBDAX_MSGS_SEV_FAILURE, + LIBDAX_MSGS_PRIO_HIGH, msg, 0,0); + ret = 0; goto ex; + } + if (d->idata->peripheral != 0x5) { + sprintf(msg, "Does not identify itself as CD-ROM drive '%s'", + d->devname); + libdax_msgs_submit(libdax_messenger, -1, 0x0002000a, + LIBDAX_MSGS_SEV_FAILURE, + LIBDAX_MSGS_PRIO_HIGH, msg, 0,0); + ret = 0; goto ex; + } + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + diff --git a/trunk/libburn/spc.h b/trunk/libburn/spc.h new file mode 100644 index 0000000..81f635a --- /dev/null +++ b/trunk/libburn/spc.h @@ -0,0 +1,169 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + +#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 *, + struct burn_session *, int, + 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 *progress); + +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, GO_ON }; +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); + +/* ts A91106 */ +int scsi_show_cmd_text(struct command *c, void *fp, int flag); + +/* ts B11110 */ +/** Logs command (before execution). */ +int scsi_log_command(unsigned char *opcode, int oplen, int data_dir, + unsigned char *data, int bytes, + void *fp_in, int flag); + +/* ts B40731 */ +/* Arbitrary SCSI log message */ +int scsi_log_text(char *text, void *fp_in, int flag); + +/* ts A91218 (former sg_log_cmd ts A70518) */ +/** Legacy frontend to scsi_log_command() */ +int scsi_log_cmd(struct command *c, void *fp, int flag); + +/* ts B11110 */ +/** Logs outcome of a sg command. + @param flag bit0 causes an error message + bit1 do not print duration +*/ +int scsi_log_reply(unsigned char *opcode, int data_dir, unsigned char *data, + int dxfer_len, void *fp_in, unsigned char sense[18], + int sense_len, double duration, int flag); + +/* ts A91221 (former sg_log_err ts A91108) */ +/** Legacy frontend to scsi_log_reply(). + @param flag bit0 causes an error message + bit1 do not print duration +*/ +int scsi_log_err(struct burn_drive *d, struct command *c, + void *fp, unsigned char sense[18], + int sense_len, int flag); + +/* ts B31112 */ +int scsi_log_message(struct burn_drive *d, void *fp, char * msg, int flag); + +/* ts B00728 */ +int spc_decode_sense(unsigned char *sense, int senselen, + int *key, int *asc, int *ascq); + +/* ts B00808 */ +/** Evaluates outcome of a single SCSI command, eventually logs sense data, + and issues DEBUG error message in case the command is evaluated as done. + @param flag bit1 = do not print duration + @return 0 = not yet done , 1 = done , -1 = error +*/ +int scsi_eval_cmd_outcome(struct burn_drive *d, struct command *c, void *fp_in, + unsigned char *sense, int sense_len, + time_t start_time, int timeout_ms, + int loop_count, int flag); + +/* ts B40204 */ +/* Verify by INQUIRY that the drive is indeed a MMC device. +*/ +int spc_confirm_cd_drive(struct burn_drive *d, int flag); + + +/* The waiting time before eventually retrying a failed SCSI command. + Before each retry wait Libburn_scsi_retry_incR longer than with + the previous one. At most wait for Libburn_scsi_retry_umaX microseconds. +*/ +#define Libburn_scsi_retry_usleeP 100000 +#define Libburn_scsi_retry_incR 100000 +#define Libburn_scsi_retry_umaX 500000 + +/* The retry waiting time for commands WRITE(10) and WRITE(12). +*/ +#define Libburn_scsi_write_retry_usleeP 0 +#define Libburn_scsi_write_retry_incR 2000 +#define Libburn_scsi_write_retry_umaX 25000 + + +/* ts B11124 */ +/* Millisecond timeout for quickly responding SPC, SBC, and MMC commands */ +#define Libburn_scsi_default_timeouT 30000 + +/* WRITE(10) and WRITE(12) */ +#define Libburn_scsi_write_timeouT 200000 + +/* RESERVE TRACK */ +#define Libburn_mmc_reserve_timeouT 200000 + +/* CLOSE TRACK/SESSION (with Immed bit) */ +#define Libburn_mmc_close_timeouT 200000 + +/* BLANK , FORMAT UNIT (with Immed bit) */ +#define Libburn_mmc_blank_timeouT 200000 + +/* SEND OPC INFORMATION */ +#define Libburn_mmc_opc_timeouT 200000 + +/* MMC_SYNC_CACHE */ +#define Libburn_mmc_sync_timeouT 200000 + +/* START STOP UNIT with Start bit and Load bit set */ +#define Libburn_mmc_load_timeouT 300000 + +#endif /*__SPC*/ diff --git a/trunk/libburn/structure.c b/trunk/libburn/structure.c new file mode 100644 index 0000000..69d5292 --- /dev/null +++ b/trunk/libburn/structure.c @@ -0,0 +1,2160 @@ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +/* ts A61008 */ +/* #include <a ssert.h> */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +/* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#include "libburn.h" +#include "structure.h" +#include "write.h" +#include "debug.h" +#include "init.h" +#include "util.h" +#include "transport.h" +#include "mmc.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 ((int) 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; + +#ifdef Libburn_disc_with_incomplete_sessioN + d->incomplete_sessions= 0; +#endif + + 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; + int i; + + s = calloc(1, sizeof(struct burn_session)); + if (s == NULL) /* ts A70825 */ + return NULL; + s->firsttrack = 1; + s->lasttrack = 0; + s->refcnt = 1; + s->tracks = 0; + s->track = NULL; + s->hidefirst = 0; + for (i = 0; i < 8; i++) { + s->cdtext[i] = NULL; + s->cdtext_language[i] = 0x00; /* Unknown */ + s->cdtext_char_code[i] = 0x00; /* ISO-8859-1 */ + s->cdtext_copyright[i] = 0x00; + } + s->cdtext_language[0] = 0x09; /* Single-block default is English */ + s->mediacatalog[0] = 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) +{ + int i; + + s->refcnt--; + if (s->refcnt == 0) { + /* dec refs on all elements */ + for (i = 0; i < s->tracks; i++) + burn_track_free(s->track[i]); + for (i = 0; i < 8; i++) + burn_cdtext_free(&(s->cdtext[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; +} + + +/* ts A81202: this function was in the API but not implemented. +*/ +int burn_disc_remove_session(struct burn_disc *d, struct burn_session *s) +{ + int i, skip = 0; + + if (d->session == NULL) + return 0; + for (i = 0; i < d->sessions; i++) { + if (s == d->session[i]) { + skip++; + continue; + } + d->session[i - skip] = d->session[i]; + } + if (!skip) + return 0; + burn_session_free(s); + d->sessions--; + return 1; +} + + +struct burn_track *burn_track_create(void) +{ + struct burn_track *t; + int i; + + t = calloc(1, sizeof(struct burn_track)); + if (t == NULL) /* ts A70825 */ + return NULL; + t->refcnt = 1; + t->indices = 0; + for (i = 0; i < 100; i++) + t->index[i] = 0x7fffffff; + 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; + /* ts B10103 */ + t->end_on_premature_eoi = 0; + + t->pregap1 = 0; + t->pregap2 = 0; + t->pregap2_size = 150; + + t->postgap = 0; + t->postgap_size = 150; + + /* ts A61024 */ + t->swap_source_bytes = 0; + + /* ts B11206 */ + for (i = 0; i < 8; i++) + t->cdtext[i] = NULL; + + return t; +} + +void burn_track_free(struct burn_track *t) +{ + int i; + + t->refcnt--; + if (t->refcnt == 0) { + /* dec refs on all elements */ + if (t->source) + burn_source_free(t->source); + for (i = 0; i < 8; i++) + burn_cdtext_free(&(t->cdtext[i])); + 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; + char msg[40]; + + sprintf(msg, "This disc has %d sessions", d->sessions); + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + 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; + char msg[40]; + + sprintf(msg, " Session has %d tracks", s->tracks); + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + for (i = 0; i < s->tracks; i++) { + burn_structure_print_track(s->track[i]); + } +} +void burn_structure_print_track(struct burn_track *t) +{ + char msg[80]; + + sprintf(msg, " track size %d sectors", + burn_track_get_sectors(t)); + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); +} + +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 */ + char msg[80]; + + type_to_form(mode, &ctladr, &form); + if (form == -1 || burn_sector_length(mode) <= 0) { + + sprintf(msg, + "Attempt to set track mode to unusable value 0x%X", + (unsigned int) 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; +} + + +/* ts A90911 : API */ +int burn_track_set_cdxa_conv(struct burn_track *t, int value) +{ + if (value < 0 || value > 1) + return 0; + t->cdxa_conversion = value; + return 1; +} + + +void burn_track_set_isrc(struct burn_track *t, char *country, char *owner, + unsigned char year, unsigned int serial) +{ + int i; + + /* ts B11226 */ + t->isrc.has_isrc = 0; + + 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; +} + +/* ts B11226 API */ +int burn_track_set_isrc_string(struct burn_track *t, char isrc[13], int flag) +{ + unsigned char year; + unsigned int serial = 2000000000; + + if (strlen(isrc) != 12 || + isrc[5] < '0' || isrc[5] > '9' || isrc[6] < '0' || isrc[6] > '9') { + 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 0; + } + year = (isrc[5] - '0') * 10 + (isrc[6] - '0'); + isrc[12] = 0; + sscanf(isrc + 7, "%u", &serial); + burn_track_set_isrc(t, isrc, isrc + 2, year, serial); + return t->isrc.has_isrc; +} + +void burn_track_clear_isrc(struct burn_track *t) +{ + t->isrc.has_isrc = 0; +} + +/* ts B20103 API */ +int burn_track_set_index(struct burn_track *t, int index_number, + unsigned int relative_lba, int flag) +{ + if (index_number < 0 || index_number > 99) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002019a, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Bad track index number", 0, 0); + return 0; + } + + /* >>> if track size known : check index */; + + t->index[index_number] = relative_lba; + if (index_number >= t->indices) + t->indices = index_number + 1; + return 1; +} + +/* ts B20103 API */ +int burn_track_clear_indice(struct burn_track *t, int flag) +{ + int i; + + for (i = 0; i < 100; i++) + t->index[i] = 0x7fffffff; + t->indices = 0; + return 1; +} + +/* ts B20110 API */ +int burn_track_set_pregap_size(struct burn_track *t, int size, int flag) +{ + t->pregap2 = (size >= 0); + t->pregap2_size = size; + return 1; +} + +/* ts B20111 API */ +int burn_track_set_postgap_size(struct burn_track *t, int size, int flag) +{ + t->postgap = (size >= 0); + t->postgap_size = size; + return 1; +} + +/* ts B20119: outsourced from burn_track_get_sectors() + @param flag bit0= do not add post-gap +*/ +int burn_track_get_sectors_2(struct burn_track *t, int flag) +{ + /* ts A70125 : was int */ + off_t size = 0; + int sectors, seclen; + + seclen = burn_sector_length(t->mode); + + if (t->cdxa_conversion == 1) + /* ts A90911 : will read blocks of 2056 bytes and write 2048 */ + seclen += 8; + + if (t->source != NULL) { /* ts A80808 : mending sigsegv */ + size = t->offset + t->source->get_size(t->source) + t->tail; + /* ts B20119 : adding post-gap */ + if (t->postgap && !(flag & 1)) + size += t->postgap_size; + } else if(t->entry != NULL) { + /* ts A80808 : all burn_toc_entry of track starts should now + have (extensions_valid & 1), even those from CD. + */ + if (t->entry->extensions_valid & 1) + size = ((off_t) t->entry->track_blocks) * (off_t) 2048; + } + sectors = size / seclen; + if (size % seclen) + sectors++; + return sectors; +} + + +int burn_track_get_sectors(struct burn_track *t) +{ + return burn_track_get_sectors_2(t, 0); +} + +/* 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[80]; + + 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 & 0x7fffffff, + (int) ((t->source->get_size(t->source) / 2048) + & 0x7fffffff)); + 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) +{ + +#ifdef Libburn_disc_with_incomplete_sessioN + + *num = d->sessions - d->incomplete_sessions; + +#else + + *num = d->sessions; + +#endif + + + return d->session; +} + + +/* ts B30112 : API */ +int burn_disc_get_incomplete_sessions(struct burn_disc *d) +{ +#ifdef Libburn_disc_with_incomplete_sessioN + + return d->incomplete_sessions; + +#else + + return 0; + +#endif +} + + +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; +} + + +/* ts A80808 : Enhance CD toc to DVD toc */ +int burn_disc_cd_toc_extensions(struct burn_drive *drive, int flag) +{ + int sidx= 0, tidx= 0, ret, track_offset, alloc_len = 34; + struct burn_toc_entry *entry, *prev_entry= NULL; + struct burn_disc *d; + /* ts A81126 : ticket 146 : There was a SIGSEGV in here */ + char *msg_data = NULL, *msg; + struct buffer *buf = NULL; + + d = drive->disc; + BURN_ALLOC_MEM(msg_data, char, 321); + BURN_ALLOC_MEM(buf, struct buffer, 1); + strcpy(msg_data, + "Damaged CD table-of-content detected and truncated."); + strcat(msg_data, " In burn_disc_cd_toc_extensions: "); + msg = msg_data + strlen(msg_data); + if (d->session == NULL) { + strcpy(msg, "d->session == NULL"); + goto failure; + } + if (d->sessions <= 0) { + ret = 1; + goto ex; + } + + for (sidx = 0; sidx < d->sessions; sidx++) { + track_offset = burn_session_get_start_tno(d->session[sidx], 0); + if (track_offset <= 0) + track_offset = 1; + if (d->session[sidx] == NULL) { + sprintf(msg, "d->session[%d of %d] == NULL", + sidx, d->sessions); + goto failure; + } + if (d->session[sidx]->track == NULL) { + sprintf(msg, "d->session[%d of %d]->track == NULL", + sidx, d->sessions); + goto failure; + } + if (d->session[sidx]->leadout_entry == NULL) { + sprintf(msg, + " Session %d of %d: Leadout entry missing.", + sidx, d->sessions); + goto failure; + } + for (tidx = 0; tidx < d->session[sidx]->tracks + 1; tidx++) { + if (tidx < d->session[sidx]->tracks) { + if (d->session[sidx]->track[tidx] == NULL) { + sprintf(msg, + "d->session[%d of %d]->track[%d of %d] == NULL", + sidx, d->sessions, tidx, d->session[sidx]->tracks); + goto failure; + } + entry = d->session[sidx]->track[tidx]->entry; + if (entry == NULL) { + sprintf(msg, + "session %d of %d, track %d of %d, entry == NULL", + sidx, d->sessions, tidx, + d->session[sidx]->tracks); + goto failure; + } + } else + entry = d->session[sidx]->leadout_entry; + entry->session_msb = 0; + entry->point_msb = 0; + entry->start_lba = burn_msf_to_lba(entry->pmin, + entry->psec, entry->pframe); + if (tidx > 0) { + prev_entry->track_blocks = + entry->start_lba + - prev_entry->start_lba; + + /* The drive might know size restrictions + like pre-gaps + */ + ret = mmc_read_track_info(drive, + tidx - 1 + track_offset, buf, + alloc_len); + if (ret > 0) { + ret = mmc_four_char_to_int( + buf->data + 24); + if (ret < prev_entry->track_blocks && + ((!drive->current_is_cd_profile) || + ret < prev_entry->track_blocks - 2)) + prev_entry->track_blocks = ret; + } + prev_entry->extensions_valid |= 1; + } + if (tidx == d->session[sidx]->tracks) { + entry->session_msb = 0; + entry->point_msb = 0; + entry->track_blocks = 0; + entry->extensions_valid |= 1; + } + prev_entry = entry; + } + } + {ret = 1; goto ex;} +failure: + libdax_msgs_submit(libdax_messenger, -1, 0x0002015f, + LIBDAX_MSGS_SEV_MISHAP, LIBDAX_MSGS_PRIO_HIGH, msg_data, 0, 0); + d->sessions= sidx; + ret = 0; +ex:; + BURN_FREE_MEM(buf); + BURN_FREE_MEM(msg_data); + return ret; +} + + +/* ts B20107 API */ +int burn_session_set_start_tno(struct burn_session *session, int tno, int flag) +{ + if (tno < 1 || tno > 99) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002019b, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "CD start track number exceeds range of 1 to 99", + 0, 0); + return 0; + } + if (tno + session->tracks - 1 > 99) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002019b, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "CD track number exceeds 99", 0, 0); + return 0; + } + session->firsttrack = tno; + return 1; +} + + +/* ts B20108 API */ +int burn_session_get_start_tno(struct burn_session *session, int flag) +{ + return (int) session->firsttrack; +} + + +struct burn_cdtext *burn_cdtext_create(void) +{ + struct burn_cdtext *t; + int i; + + t = burn_alloc_mem(sizeof(struct burn_cdtext), 1, 0); + if (t == NULL) + return NULL; + for(i = 0; i < Libburn_pack_num_typeS; i ++) { + t->payload[i] = NULL; + t->length[i] = 0; + } + return t; +} + + +void burn_cdtext_free(struct burn_cdtext **cdtext) +{ + struct burn_cdtext *t; + int i; + + t = *cdtext; + if (t == NULL) + return; + for (i = 0; i < Libburn_pack_num_typeS; i++) + if (t->payload[i] != NULL) + free(t->payload[i]); + free(t); +} + + +static int burn_cdtext_name_to_type(char *pack_type_name) +{ + int i, j; + static char *pack_type_names[] = { + Libburn_pack_type_nameS + }; + + for (i = 0; i < Libburn_pack_num_typeS; i++) { + if (pack_type_names[i][0] == 0) + continue; + for (j = 0; pack_type_names[i][j]; j++) + if (pack_type_names[i][j] != pack_type_name[j] && + tolower(pack_type_names[i][j]) != + pack_type_name[j]) + break; + if (pack_type_names[i][j] == 0) + return Libburn_pack_type_basE + i; + } + return -1; +} + + +/* @param flag bit0= double byte characters +*/ +static int burn_cdtext_set(struct burn_cdtext **cdtext, + int pack_type, char *pack_type_name, + unsigned char *payload, int length, int flag) +{ + int i; + struct burn_cdtext *t; + + if (pack_type_name != NULL) + if (pack_type_name[0]) + pack_type = burn_cdtext_name_to_type(pack_type_name); + if (pack_type < Libburn_pack_type_basE || + pack_type >= Libburn_pack_type_basE + Libburn_pack_num_typeS) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002018c, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "CD-TEXT pack type out of range", 0, 0); + return 0; + } + t = *cdtext; + if (t == NULL) { + *cdtext = t = burn_cdtext_create(); + if (t == NULL) + return -1; + } + i = pack_type - Libburn_pack_type_basE; + if (t->payload[i] != NULL) + free(t->payload[i]); + t->payload[i] = burn_alloc_mem((size_t) length, 1, 0); + if (t->payload[i] == NULL) + return -1; + memcpy(t->payload[i], payload, length); + t->length[i] = length; + t->flags = (t->flags & ~(1 << i)) | (flag & (1 << i)); + return 1; +} + + +/* @return 1=single byte char , 2= double byte char , <=0 error */ +static int burn_cdtext_get(struct burn_cdtext *t, int pack_type, + char *pack_type_name, + unsigned char **payload, int *length, int flag) +{ + if (pack_type_name != NULL) + if (pack_type_name[0]) + pack_type = burn_cdtext_name_to_type(pack_type_name); + if (pack_type < Libburn_pack_type_basE || + pack_type >= Libburn_pack_type_basE + Libburn_pack_num_typeS) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002018c, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "CD-TEXT pack type out of range", 0, 0); + return 0; + } + *payload = t->payload[pack_type - Libburn_pack_type_basE]; + *length = t->length[pack_type - Libburn_pack_type_basE]; + return 1 + ((t->flags >> (pack_type - Libburn_pack_type_basE)) & 1); +} + + +static int burn_cdtext_check_blockno(int block) +{ + if (block < 0 || block > 7) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002018d, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "CD-TEXT block number out of range", 0, 0); + return 0; + } + return 1; +} + + +/* ts B11206 API */ +/* @param flag bit0= double byte characters +*/ +int burn_track_set_cdtext(struct burn_track *t, int block, + int pack_type, char *pack_type_name, + unsigned char *payload, int length, int flag) +{ + int ret; + + if (burn_cdtext_check_blockno(block) <= 0) + return 0; + ret = burn_cdtext_set(&(t->cdtext[block]), pack_type, pack_type_name, + payload, length, flag & 1); + return ret; +} + + +/* ts B11206 API */ +/* @return 1=single byte char , 2= double byte char , <=0 error */ +int burn_track_get_cdtext(struct burn_track *t, int block, + int pack_type, char *pack_type_name, + unsigned char **payload, int *length, int flag) +{ + int ret; + + if (burn_cdtext_check_blockno(block) <= 0) + return 0; + if (t->cdtext[block] == NULL) { + *payload = NULL; + *length = 0; + return 1; + } + ret = burn_cdtext_get(t->cdtext[block], pack_type, pack_type_name, + payload, length, 0); + return ret; +} + + +/* ts B11206 API */ +int burn_track_dispose_cdtext(struct burn_track *t, int block) +{ + int i; + + if (block == -1) { + for (i= 0; i < 8; i++) + burn_cdtext_free(&(t->cdtext[i])); + return 1; + } + if (burn_cdtext_check_blockno(block) <= 0) + return 0; + burn_cdtext_free(&(t->cdtext[0])); + return 1; +} + + +/* ts B11206 API */ +/* @param flag bit0= double byte characters +*/ +int burn_session_set_cdtext(struct burn_session *s, int block, + int pack_type, char *pack_type_name, + unsigned char *payload, int length, int flag) +{ + int ret; + + if (burn_cdtext_check_blockno(block) <= 0) + return 0; + ret = burn_cdtext_set(&(s->cdtext[block]), pack_type, pack_type_name, + payload, length, flag & 1); + return ret; +} + + +/* ts B11206 API */ +/* @return 1=single byte char , 2= double byte char , <=0 error */ +int burn_session_get_cdtext(struct burn_session *s, int block, + int pack_type, char *pack_type_name, + unsigned char **payload, int *length, int flag) +{ + int ret; + + if (burn_cdtext_check_blockno(block) <= 0) + return 0; + + if (s->cdtext[block] == NULL) { + *payload = NULL; + *length = 0; + return 1; + } + ret = burn_cdtext_get(s->cdtext[block], pack_type, pack_type_name, + payload, length, 0); + return ret; +} + + +/* ts B11206 API */ +int burn_session_set_cdtext_par(struct burn_session *s, + int char_codes[8], int copyrights[8], + int block_languages[8], int flag) +{ + int i; + + for (i = 0; i < 8; i++) { + if (char_codes[i] >= 0 && char_codes[i] <= 255) + s->cdtext_char_code[i] = char_codes[i]; + if (copyrights[i] >= 0 && copyrights[i] <= 255) + s->cdtext_copyright[i] = copyrights[i]; + if (block_languages[i] >= 0 && block_languages[i] <= 255) + s->cdtext_language[i] = block_languages[i]; + } + return 1; +} + + +/* ts B11206 API */ +int burn_session_get_cdtext_par(struct burn_session *s, + int char_codes[8], int copyrights[8], + int block_languages[8], int flag) +{ + int i; + + for (i = 0; i < 8; i++) { + char_codes[i] = s->cdtext_char_code[i]; + copyrights[i] = s->cdtext_copyright[i]; + block_languages[i]= s->cdtext_language[i]; + } + return 1; +} + + +/* ts B11206 API */ +int burn_session_dispose_cdtext(struct burn_session *s, int block) +{ + int i; + + if (block == -1) { + for (i= 0; i < 8; i++) { + burn_session_dispose_cdtext(s, i); + s->cdtext_char_code[i] = 0x01; /* 7 bit ASCII */ + s->cdtext_copyright[i] = 0; + s->cdtext_language[i] = 0; + } + return 1; + } + if (burn_cdtext_check_blockno(block) <= 0) + return 0; + burn_cdtext_free(&(s->cdtext[block])); + s->cdtext_language[block] = 0x09; /* english */ + return 1; +} + + +/* --------------------- Reading CDRWIN cue sheet files ----------------- */ + + +struct burn_cue_file_cursor { + char *cdtextfile; + char *source_file; + off_t source_size; + struct burn_source *file_source; + int fifo_size; + struct burn_source *fifo; + int swap_audio_bytes; + int no_cdtext; + int no_catalog_isrc; + int start_track_no; + struct burn_source *offst_source; + int current_file_ba; + int current_index_ba; + struct burn_track *prev_track; + int prev_file_ba; + int prev_block_size; + struct burn_track *track; + int track_no; + int track_current_index; + int track_has_source; + int block_size; + int block_size_locked; + int track_mode; + int flags; +}; + + +static int cue_crs_new(struct burn_cue_file_cursor **reply, int flag) +{ + int ret; + struct burn_cue_file_cursor *crs; + + BURN_ALLOC_MEM(crs, struct burn_cue_file_cursor, 1); + crs->cdtextfile = NULL; + crs->source_file = NULL; + crs->source_size = -1; + crs->file_source = NULL; + crs->fifo_size = 0; + crs->fifo = NULL; + crs->swap_audio_bytes = 0; + crs->no_cdtext = 0; + crs->no_catalog_isrc = 0; + crs->start_track_no = 1; + crs->offst_source = NULL; + crs->current_file_ba = -1000000000; + crs->current_index_ba = -1000000000; + crs->prev_track = NULL; + crs->prev_file_ba = -1000000000; + crs->prev_block_size = 0; + crs->track = NULL; + crs->track_no = 0; + crs->track_current_index = -1; + crs->track_has_source = 0; + crs->block_size = 0; + crs->block_size_locked = 0; + crs->track_mode = 0; + crs->flags = 0; + + *reply = crs; + ret = 1; +ex:; + return ret; +} + + +static int cue_crs_destroy(struct burn_cue_file_cursor **victim, int flag) +{ + struct burn_cue_file_cursor *crs; + + if (*victim == NULL) + return 2; + crs = *victim; + if (crs->cdtextfile != NULL) + free(crs->cdtextfile); + if (crs->source_file != NULL) + free(crs->source_file); + if (crs->file_source != NULL) + burn_source_free(crs->file_source); + if (crs->fifo != NULL) + burn_source_free(crs->fifo); + if (crs->offst_source != NULL) + burn_source_free(crs->offst_source); + if (crs->prev_track != NULL) + burn_track_free(crs->prev_track); + if (crs->track != NULL) + burn_track_free(crs->track); + BURN_FREE_MEM(crs); + *victim = NULL; + return 1; +} + + +static char *cue_unquote_text(char *text, int flag) +{ + char *ept, *spt; + + spt = text; + for (ept = text + strlen(text); ept > text; ept--) + if (*(ept - 1) != 32 && *(ept - 1) != 9) + break; + if (text[0] == '"') { + spt = text + 1; + if (ept > spt) + if (*(ept - 1) == '"') + ept--; + } + *ept = 0; + return spt; +} + + +/* @param flag bit0= insist in having a track object + bit1= remove quotation marks if present +*/ +static int cue_set_cdtext(struct burn_session *session, + struct burn_track *track, int pack_type, char *text, + struct burn_cue_file_cursor *crs, int flag) +{ + int ret; + char *payload; + + if (crs->no_cdtext == 1) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020195, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: Being set to ignore all CD-TEXT aspects", + 0, 0); + crs->no_cdtext = 2; + } + if (crs->no_cdtext) + return 2; + if ((flag & 1) && track == NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020192, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Track attribute set before first track in cue sheet file", + 0, 0); + ret = 0; goto ex; + } + if (flag & 2) + payload = cue_unquote_text(text, 0); + else + payload = text; + if (track != NULL) { + ret = burn_track_set_cdtext(track, 0, pack_type, "", + (unsigned char *) payload, + strlen(payload) + 1, 0); + } else { + ret = burn_session_set_cdtext(session, 0, pack_type, "", + (unsigned char *) payload, + strlen(payload) + 1, 0); + } +ex:; + return ret; +} + + +static int cue_attach_track(struct burn_session *session, + struct burn_cue_file_cursor *crs, int flag) +{ + int ret; + + if (crs->track == NULL) + return 2; + + if (!crs->track_has_source) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: TRACK without INDEX 01", 0, 0); + return 0; + } + if (crs->track_current_index < 1) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020192, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "No INDEX 01 defined for last TRACK in cue sheet file", + 0, 0); + return 0; + } + if (session->tracks == 0) { + crs->start_track_no = crs->track_no; + ret = burn_session_set_start_tno(session, crs->track_no, 0); + if (ret <= 0) + return ret; + } + if (session->tracks + crs->start_track_no - 1 > 99) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002019b, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "CD track number exceeds 99", + 0, 0); + return 0; + } + ret = burn_session_add_track(session, crs->track, BURN_POS_END); + if (ret <= 0) + return ret; + if (crs->prev_track != NULL) + burn_track_free(crs->prev_track); /* release reference */ + crs->prev_track = crs->track; + crs->prev_file_ba = crs->current_file_ba; + crs->prev_block_size = crs->block_size; + crs->track = NULL; + crs->track_current_index = -1; + crs->track_has_source = 0; + crs->current_file_ba = -1; + crs->current_index_ba = -1; + if (!crs->block_size_locked) + crs->block_size = 0; + return 1; +} + + +/* @param flag bit0= do not alter the content of *payload + do not change *payload +*/ +static int cue_read_number(char **payload, int *number, int flag) +{ + int ret, at_end = 0; + char *apt, *msg = NULL; + + for(apt = *payload; *apt != 0 && *apt != 32 && *apt != 9; apt++); + if (*apt == 0) + at_end = 1; + else if (!(flag & 1)) + *apt = 0; + ret = sscanf(*payload, "%d", number); + if (ret != 1) { + BURN_ALLOC_MEM(msg, char, 4096); + sprintf(msg, + "Unsuitable number in cue sheet file: '%.4000s'", + *payload); + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + /* Find start of next argument */ + if (!at_end) + for (apt++; *apt == 32 || *apt == 9; apt++); + if (!(flag & 1)) + *payload = apt; + + ret = 1; +ex: + BURN_FREE_MEM(msg); + return ret; +} + + +/* @param flag bit0-7: desired type : 0=any , 1=.wav +*/ +static int cue_open_audioxtr(char *path, struct burn_cue_file_cursor *crs, + int *fd, int flag) +{ + struct libdax_audioxtr *xtr= NULL; + char *fmt, *fmt_info; + int ret, num_channels, sample_rate, bits_per_sample, msb_first; + char *msg = NULL; + + BURN_ALLOC_MEM(msg, char, 4096); + + ret= libdax_audioxtr_new(&xtr, 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 ((flag & 255) == 1) { + if (strcmp(fmt, ".wav") != 0) { + sprintf(msg, + "In cue sheet: Not recognized as WAVE : FILE '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020193, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + } + ret = libdax_audioxtr_get_size(xtr, &(crs->source_size), 0); + if (ret <= 0) { + sprintf(msg, + "In cue sheet: Cannot get payload size of FILE '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020193, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + ret = libdax_audioxtr_detach_fd(xtr, fd, 0); + if (ret <= 0) { + sprintf(msg, + "In cue sheet: Cannot represent payload as plain fd: FILE '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020193, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + crs->swap_audio_bytes = (msb_first == 1); + + ret = 1; +ex: + if (xtr != NULL) + libdax_audioxtr_destroy(&xtr, 0); + BURN_FREE_MEM(msg); + return ret; +} + + +/* @param flag bit0-7: desired type : 0=any , 1=.wav + bit8= open by libdax_audioxtr functions + +*/ +static int cue_create_file_source(char *path, struct burn_cue_file_cursor *crs, + int flag) +{ + int fd, ret; + char *msg = NULL; + + BURN_ALLOC_MEM(msg, char, 4096); + + if (flag & 256) { + ret = cue_open_audioxtr(path, crs, &fd, flag & 255); + if (ret <= 0) + goto ex; + } else { + fd = open(path, O_RDONLY | O_BINARY); + if (fd == -1) { + sprintf(msg, + "In cue sheet: Cannot open FILE '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020193, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), errno, 0); + ret = 0; goto ex; + } + } + crs->file_source = burn_fd_source_new(fd, -1, crs->source_size); + if (crs->file_source == NULL) { + ret = -1; goto ex; + } + + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +static int cue_read_timepoint_lba(char *apt, char *purpose, int *file_ba, + int flag) +{ + int ret, minute, second, frame; + char *msg = NULL, msf[3], *msf_pt; + + BURN_ALLOC_MEM(msg, char, 4096); + if (strlen(apt) < 8) { +no_time_point:; + sprintf(msg, + "Inappropriate cue sheet file %s '%.4000s'", + purpose, apt); + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + if (apt[2] != ':' || apt[5] != ':' || + (apt[8] != 0 && apt[8] != 32 && apt[8] != 9)) + goto no_time_point; + msf[2] = 0; + msf_pt = msf; + strncpy(msf, apt, 2); + ret = cue_read_number(&msf_pt, &minute, 1); + if (ret <= 0) + goto ex; + strncpy(msf, apt + 3, 2); + ret = cue_read_number(&msf_pt, &second, 1); + if (ret <= 0) + goto ex; + strncpy(msf, apt + 6, 2); + ret = cue_read_number(&msf_pt, &frame, 1); + if (ret <= 0) + goto ex; + + *file_ba = ((minute * 60) + second ) * 75 + frame; + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + +static int cue_check_for_track(struct burn_cue_file_cursor *crs, char *cmd, + int flag) +{ + int ret; + char *msg = NULL; + + if (crs->track == NULL) { + BURN_ALLOC_MEM(msg, char, 4096); + sprintf(msg, "In cue sheet file: %s found before TRACK", + cmd); + libdax_msgs_submit(libdax_messenger, -1, 0x00020192, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + ret = 0; goto ex; + } + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +static int cue_interpret_line(struct burn_session *session, char *line, + struct burn_cue_file_cursor *crs, int flag) +{ + int ret, mode, index_no, file_ba, chunks; + int block_size, step, audio_xtr = 0; + off_t size; + char *cmd, *apt, *msg = NULL, *cpt, *filetype; + struct burn_source *src, *inp_src; + enum burn_source_status source_status; + struct stat stbuf; + + BURN_ALLOC_MEM(msg, char, 4096); + + if (line[0] == 0 || line[0] == '#') { + ret = 1; goto ex; + } + + for (cmd = line; *cmd == 32 || *cmd == 9; cmd++); + for(apt = cmd; *apt != 0 && *apt != 32 && *apt != 9; apt++); + if (*apt != 0) { + *apt = 0; + for (apt++; *apt == 32 || *apt == 9; apt++); + } + + if (strcmp(cmd, "ARRANGER") == 0) { + ret = cue_set_cdtext(session, crs->track, 0x84, apt, crs, 2); + if (ret <= 0) + goto ex; + + } else if (strcmp(cmd, "CATALOG") == 0) { + for (cpt = apt; (cpt - apt) < 13 && *cpt == (*cpt & 0x7f); + cpt++); + if ((cpt - apt) < 13) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: Inappropriate content of CATALOG", + 0, 0); + ret = 0; goto ex; + } + ret = cue_set_cdtext(session, NULL, 0x8e, apt, crs, 0); + if (ret <= 0) + goto ex; + if (!crs->no_catalog_isrc) { + memcpy(session->mediacatalog, apt, 13); + session->mediacatalog[13] = 0; + } + + } else if (strcmp(cmd, "CDTEXTFILE") == 0) { + if (crs->no_cdtext) { + ret = 1; goto ex; + } + apt = cue_unquote_text(apt, 0); + if (crs->cdtextfile != NULL) + free(crs->cdtextfile); + crs->cdtextfile = strdup(apt); + if (crs->cdtextfile == NULL) { +out_of_mem:; + libdax_msgs_submit(libdax_messenger, -1, 0x00000003, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Out of virtual memory", 0, 0); + ret = -1; goto ex; + } + + } else if (strcmp(cmd, "COMPOSER") == 0) { + ret = cue_set_cdtext(session, crs->track, 0x83, apt, crs, 2); + if (ret <= 0) + goto ex; + + } else if (strcmp(cmd, "FILE") == 0) { + if (crs->file_source != NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020192, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: Multiple occurences of FILE", + 0, 0); + ret = 0; goto ex; + } + /* Obtain type */ + for (cpt = apt + (strlen(apt) - 1); + cpt > apt && (*cpt == 32 || *cpt == 9); cpt--); + cpt[1] = 0; + for (; cpt > apt && *cpt != 32 && *cpt != 9; cpt--); + if (cpt <= apt) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: FILE without type word", + 0, 0); + ret = 0; goto ex; + } + *cpt = 0; + filetype = cpt + 1; + if (strcmp(filetype, "BINARY") == 0) { + crs->swap_audio_bytes = 0; + } else if (strcmp(filetype, "MOTOROLA") == 0) { + crs->swap_audio_bytes = 1; + } else if (strcmp(filetype, "WAVE") == 0) { + audio_xtr = 0x101; + } else { + sprintf(msg, + "In cue sheet file: Unsupported FILE type '%.4000s'", + filetype); + libdax_msgs_submit(libdax_messenger, -1, 0x00020197, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + + apt = cue_unquote_text(apt, 0); + if (*apt == 0) + ret = -1; + else + ret = stat(apt, &stbuf); + if (ret == -1) { +not_usable_file:; + sprintf(msg, + "In cue sheet file: Unusable FILE '%.4000s'", + apt); + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + if (!S_ISREG(stbuf.st_mode)) + goto not_usable_file; + crs->source_size = stbuf.st_size; + if (crs->source_file != NULL) + free(crs->source_file); + crs->source_file = strdup(apt); + if (crs->source_file == NULL) + goto out_of_mem; + ret = cue_create_file_source(apt, crs, audio_xtr); + if (ret <= 0) + goto ex; + + } else if (strcmp(cmd, "FLAGS") == 0) { + ret = cue_check_for_track(crs, cmd, 0); + if (ret <= 0) + goto ex; + while (*apt) { + if (strncmp(apt, "DCP", 3) == 0) { + crs->track_mode |= BURN_COPY; + step = 3; + } else if (strncmp(apt, "4CH", 3) == 0) { + crs->track_mode |= BURN_4CH; + step = 3; + } else if (strncmp(apt, "PRE", 3) == 0) { + crs->track_mode |= BURN_PREEMPHASIS; + step = 3; + } else if (strncmp(apt, "SCMS", 4) == 0) { + crs->track_mode |= BURN_SCMS; + step = 4; + } else { +bad_flags:; + for (cpt = apt; + *cpt != 32 && *cpt != 9 && *cpt != 0; cpt++); + *cpt = 0; + sprintf(msg, + "In cue sheet file: Unknown FLAGS option '%.4000s'", + apt); + libdax_msgs_submit(libdax_messenger, -1, + 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, + LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + + /* Look for start of next word */ + if (apt[step] != 0 && apt[step] != 32 && + apt[step] != 9) + goto bad_flags; + for (apt += step; *apt == 32 || *apt == 9; apt++); + } + burn_track_define_data(crs->track, 0, 0, 1, crs->track_mode); + + } else if (strcmp(cmd, "INDEX") == 0) { + ret = cue_check_for_track(crs, cmd, 0); + if (ret <= 0) + goto ex; + ret = cue_read_number(&apt, &index_no, 0); + if (ret <= 0) + goto ex; + ret = cue_read_timepoint_lba(apt, "index time point", + &file_ba, 0); + if (ret <= 0) + goto ex; + if (file_ba < crs->prev_file_ba) { +overlapping_ba:; + libdax_msgs_submit(libdax_messenger, -1, 0x00020192, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Backward INDEX address in cue sheet file", + 0, 0); + ret = 0; goto ex; + } + if (file_ba < crs->current_index_ba) + goto overlapping_ba; + if (crs->prev_track != NULL && crs->track_current_index < 0) { + size = (file_ba - crs->prev_file_ba) * + crs->prev_block_size; + if (size <= 0) + goto overlapping_ba; + burn_track_set_size(crs->prev_track, size); + } + if (crs->track_current_index + 1 != index_no && + !(crs->track_current_index < 0 && index_no <= 1)) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020192, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Unacceptable INDEX number in cue sheet file", + 0, 0); + ret = 0; goto ex; + } + crs->track_current_index = index_no; + + if (crs->current_file_ba < 0) + crs->current_file_ba = file_ba; + crs->current_index_ba = file_ba; + + /* Set index address relative to track source start */ + ret = burn_track_set_index(crs->track, index_no, + file_ba - crs->current_file_ba, 0); + if (ret <= 0) + goto ex; + + if (crs->track_has_source) { + ret = 1; goto ex; + } + + if (crs->block_size_locked && crs->fifo == NULL && + crs->fifo_size > 0) { + /* Now that the block size is known from TRACK: + Create fifo and use it for creating the offset + sources. This will fixate the block size to one + common value. + */ + chunks = crs->fifo_size / crs->block_size + + !!(crs->fifo_size % crs->block_size); + if (chunks < 4) + chunks = 4; + crs->fifo = burn_fifo_source_new(crs->file_source, + crs->block_size, chunks, 0); + if (crs->fifo == NULL) { + ret = -1; goto ex; + } + } + if (crs->fifo != NULL) + inp_src = crs->fifo; + else + inp_src = crs->file_source; + src = burn_offst_source_new(inp_src, crs->offst_source, + (off_t) (file_ba * crs->block_size), (off_t) 0, 1); + if (src == NULL) + goto out_of_mem; + + /* >>> Alternative to above fifo creation: + Create a fifo for each track track. + This will be necessary if mixed-mode sessions get supporded. + */; + + source_status = burn_track_set_source(crs->track, src); + if (source_status != BURN_SOURCE_OK) { + ret = -1; goto ex; + } + + /* Switch current source in crs */ + if (crs->offst_source != NULL) + burn_source_free(crs->offst_source); + crs->offst_source = src; + crs->track_has_source = 1; + + } else if (strcmp(cmd, "ISRC") == 0) { + ret = cue_check_for_track(crs, cmd, 0); + if (ret <= 0) + goto ex; + ret = cue_set_cdtext(session, crs->track, 0x8e, apt, crs, + 1 | 2); + if (ret <= 0) + goto ex; + if (!crs->no_catalog_isrc) { + ret = burn_track_set_isrc_string(crs->track, apt, 0); + if (ret <= 0) + goto ex; + } + + } else if (strcmp(cmd, "MESSAGE") == 0) { + ret = cue_set_cdtext(session, crs->track, 0x85, apt, crs, 2); + if (ret <= 0) + goto ex; + + } else if (strcmp(cmd, "PERFORMER") == 0) { + ret = cue_set_cdtext(session, crs->track, 0x81, apt, crs, 2); + if (ret <= 0) + goto ex; + + } else if (strcmp(cmd, "POSTGAP") == 0) { + ret = cue_check_for_track(crs, cmd, 0); + if (ret <= 0) + goto ex; + ret = cue_read_timepoint_lba(apt, "post-gap duration", + &file_ba, 0); + if (ret <= 0) + goto ex; + ret = burn_track_set_postgap_size(crs->track, file_ba, 0); + if (ret <= 0) + goto ex; + + } else if (strcmp(cmd, "PREGAP") == 0) { + ret = cue_check_for_track(crs, cmd, 0); + if (ret <= 0) + goto ex; + ret = cue_read_timepoint_lba(apt, "pre-gap duration", + &file_ba, 0); + if (ret <= 0) + goto ex; + ret = burn_track_set_pregap_size(crs->track, file_ba, 0); + if (ret <= 0) + goto ex; + + } else if (strcmp(cmd, "REM") == 0) { + ; + + } else if (strcmp(cmd, "SONGWRITER") == 0) { + ret = cue_set_cdtext(session, crs->track, 0x82, apt, crs, 2); + if (ret <= 0) + goto ex; + + } else if (strcmp(cmd, "TITLE") == 0) { + ret = cue_set_cdtext(session, crs->track, 0x80, apt, crs, 2); + if (ret <= 0) + goto ex; + + } else if (strcmp(cmd, "TRACK") == 0) { + if (crs->file_source == NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020192, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "No FILE defined before TRACK in cue sheet file", + 0, 0); + ret = 0; goto ex; + } + /* Attach previous track to session */ + ret = cue_attach_track(session, crs, 0); + if (ret <= 0) + goto ex; + /* Create new track */; + ret = cue_read_number(&apt, &(crs->track_no), 0); + if (ret <= 0) + goto ex; + if (crs->track_no < 1 || crs->track_no > 99) { + sprintf(msg, + "Inappropriate cue sheet file track number %d", + crs->track_no); + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + if (strcmp(apt, "AUDIO") == 0) { + mode = BURN_AUDIO; + block_size = 2352; + } else if (strcmp(apt, "MODE1/2048") == 0) { + mode = BURN_MODE1; + block_size = 2048; + } else { + sprintf(msg, + "Unsupported cue sheet file track datatype '%.4000s'", + apt); + libdax_msgs_submit(libdax_messenger, -1, 0x00020197, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + if (block_size != crs->block_size && crs->block_size > 0 && + crs->block_size_locked) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020197, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: Unsupported mix track block sizes", + 0, 0); + ret = 0; goto ex; + } + crs->block_size = block_size; + + crs->track = burn_track_create(); + if (crs->track == NULL) + goto out_of_mem; + crs->track_has_source = 0; + crs->track_mode = mode; + burn_track_define_data(crs->track, 0, 0, 1, mode); + if (mode & BURN_AUDIO) + burn_track_set_byte_swap(crs->track, + !!crs->swap_audio_bytes); + + } else { + sprintf(msg, "Unknown cue sheet file command '%.4000s'", line); + libdax_msgs_submit(libdax_messenger, -1, 0x00020191, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +/* ts B11216 API */ +/* @param flag bit0= do not attach CD-TEXT information to session and tracks + bit1= do not attach CATALOG to session or ISRC to track for + writing to Q sub-channel +*/ +int burn_session_by_cue_file(struct burn_session *session, char *path, + int fifo_size, struct burn_source **fifo, + unsigned char **text_packs, int *num_packs, int flag) +{ + int ret, num_tracks, i, pack_type, length, double_byte = 0; + int line_counter = 0; + struct burn_track **tracks; + char *msg = NULL, *line = NULL; + unsigned char *payload; + struct stat stbuf; + FILE *fp = NULL; + struct burn_cue_file_cursor *crs = NULL; + + static unsigned char dummy_cdtext[2] = {0, 0}; + + if (fifo != NULL) + *fifo = NULL; + if (text_packs != NULL) + *text_packs = NULL; + *num_packs = 0; + + BURN_ALLOC_MEM(msg, char, 4096); + BURN_ALLOC_MEM(line, char, 4096); + ret = cue_crs_new(&crs, 0); + if (ret <= 0) + goto ex; + crs->no_cdtext = (flag & 1); + crs->no_catalog_isrc = !!(flag & 2); + crs->fifo_size = fifo_size; + crs->block_size_locked = 1; /* No mixed sessions for now */ + + tracks = burn_session_get_tracks(session, &num_tracks); + if (num_tracks > 0) { + sprintf(msg, + "Cue sheet file reader called while session has already defined tracks"); + libdax_msgs_submit(libdax_messenger, -1, 0x00020196, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + if (stat(path, &stbuf) == -1) { +cannot_open:; + sprintf(msg, "Cannot open cue sheet file '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020193, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), errno, 0); + ret = 0; goto ex; + } + if (!S_ISREG(stbuf.st_mode)) { + sprintf(msg, + "File is not of usable type: Cue sheet file '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020193, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + + fp = fopen(path, "rb"); + if (fp == NULL) + goto cannot_open; + + while (1) { + if (burn_sfile_fgets(line, 4095, fp) == NULL) { + if (!ferror(fp)) + break; + sprintf(msg, + "Cannot read all bytes from cue sheet file '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020193, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + line_counter++; + ret = cue_interpret_line(session, line, crs, 0); + if (ret <= 0) { + sprintf(msg, + "Cue sheet file '%.4000s': Reading aborted after line %d", + path, line_counter); + libdax_msgs_submit(libdax_messenger, -1, 0x00020199, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + goto ex; + } + } + + /* Attach last track to session */ + if (crs->track != NULL) { + /* Set track size up to end of file */ + if (crs->current_file_ba < 0 || crs->track_current_index < 1) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020192, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "No INDEX 01 defined for last TRACK in cue sheet file", + 0, 0); + ret = 0; goto ex; + } + if (crs->current_file_ba * crs->block_size >= + crs->source_size) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "TRACK start time point exceeds size of FILE from cue sheet file", + 0, 0); + ret = 0; goto ex; + } + ret = burn_track_set_size(crs->track, crs->source_size - + (off_t) (crs->current_file_ba * crs->block_size)); + if (ret <= 0) + goto ex; + + ret = cue_attach_track(session, crs, 0); + if (ret <= 0) + goto ex; + } + if (crs->cdtextfile != NULL) { + if (text_packs == NULL) { + + /* >>> Warn of ignored text packs */; + + } else { + ret = burn_cdtext_from_packfile(crs->cdtextfile, + text_packs, num_packs, 0); + if (ret <= 0) + goto ex; + } + } + + /* Check which tracks have data of pack types where session has not */ + tracks = burn_session_get_tracks(session, &num_tracks); + for (pack_type = 0x80; pack_type < 0x8f; pack_type++) { + if (pack_type > 0x86 && pack_type != 0x8e) + continue; + ret = burn_session_get_cdtext(session, 0, pack_type, "", + &payload, &length, 0); + if (ret <= 0) + goto ex; + if (payload != NULL) + continue; + for (i = 0; i < num_tracks; i++) { + ret = burn_track_get_cdtext(tracks[i], 0, pack_type, + "", &payload, &length, 0); + if (ret <= 0) + goto ex; + double_byte = (ret > 1); + if (payload != NULL) + break; + } + if (i < num_tracks) { + ret = burn_session_set_cdtext(session, 0, pack_type, + "", dummy_cdtext, 1 + double_byte, + double_byte); + if (ret <= 0) + goto ex; + } + } + ret = 1; +ex: + if (ret <= 0) { + tracks = burn_session_get_tracks(session, &num_tracks); + for (i = 0; i < num_tracks; i++) + burn_track_free(tracks[i]); + } else { + if (fifo != NULL) { + *fifo = crs->fifo; + crs->fifo = NULL; + } + } + cue_crs_destroy(&crs, 0); + BURN_FREE_MEM(line); + BURN_FREE_MEM(msg); + return ret; +} + diff --git a/trunk/libburn/structure.h b/trunk/libburn/structure.h new file mode 100644 index 0000000..aaaff63 --- /dev/null +++ b/trunk/libburn/structure.h @@ -0,0 +1,190 @@ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2011 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#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 */ +}; + +/* ts B11206 */ +#define Libburn_pack_type_basE 0x80 +#define Libburn_pack_num_typeS 0x10 +#define Libburn_pack_type_nameS \ + "TITLE", "PERFORMER", "SONGWRITER", "COMPOSER", \ + "ARRANGER", "MESSAGE", "DISCID", "GENRE", \ + "TOC", "TOC2", "", "", \ + "", "CLOSED", "UPC_ISRC", "BLOCKSIZE" + +struct burn_cdtext +{ + unsigned char *(payload[Libburn_pack_num_typeS]); + int length[Libburn_pack_num_typeS]; + int flags; /* bit0 - bit15= double byte characters */ +}; + +struct burn_track +{ + int refcnt; + struct burn_toc_entry *entry; + unsigned char indices; + /* lba address of the index. CD only. 0x7fffffff means undefined index. + To be programmed relative to track source start before burning, + but to hold absolute addresses after burning or reading. + */ + int index[100]; + /** 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; + /* ts B10103 */ + /** End track writing on premature End-of-input if source is of + defined length. + 0= normal operation in case of eoi + 1= be ready to end track writing on eoi + 2= eoi was encountered with previously set value of 1 + */ + int end_on_premature_eoi; + + /** 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; + + /* ts B20110 */ + /** The number of sectors in pre-gap 2, if .pregap2 is set */ + int pregap2_size; + + /** The track contains a postgap */ + int postgap; + + /* ts B20111 */ + /** The number of sectors in post-gap, if .postgap is set */ + int postgap_size; + + struct isrc isrc; + + /* ts A61024 */ + /** Byte swapping on source data stream : 0=none , 1=pairwise */ + int swap_source_bytes; + + /* ts A90910 : conversions from CD XA prepared input */ + int cdxa_conversion; /* 0=none, 1=remove -xa1 headers (first 8 bytes)*/ + + /* ts B11206 */ + struct burn_cdtext *cdtext[8]; + +}; + +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; + + /* ts B11206 */ + struct burn_cdtext *cdtext[8]; + unsigned char cdtext_char_code[8]; + unsigned char cdtext_copyright[8]; + unsigned char cdtext_language[8]; + + /* ts B11226 */ + unsigned char mediacatalog[14]; /* overrideable by burn_write_opts */ +}; + +struct burn_disc +{ + int sessions; + struct burn_session **session; + +#ifdef Libburn_disc_with_incomplete_sessioN + int incomplete_sessions; +#endif + + 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); + + +/* ts A80808 : Enhance CD toc to DVD toc */ +int burn_disc_cd_toc_extensions(struct burn_drive *drive, int flag); + + +/* ts B11206 */ +struct burn_cdtext *burn_cdtext_create(void); +void burn_cdtext_free(struct burn_cdtext **cdtext); + +/* ts B20119 */ +/* @param flag bit0= do not add post-gap +*/ +int burn_track_get_sectors_2(struct burn_track *t, int flag); + + +#endif /* BURN__STRUCTURE_H */ diff --git a/trunk/libburn/toc.c b/trunk/libburn/toc.c new file mode 100644 index 0000000..e52cec1 --- /dev/null +++ b/trunk/libburn/toc.c @@ -0,0 +1,162 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2011 - 2011 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +/* 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" +#include "init.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) +{ + int i, j; + struct buffer *mem = NULL; + struct burn_toc_entry *e; + +/* ts A70519 : the code which needs this does not work with GNU/Linux 2.4 USB + int lba; + struct burn_read_opts o; + + 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; +*/ + + BURN_ALLOC_MEM_VOID(mem, struct buffer, 1); + + mem->bytes = 0; + mem->sectors = 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; +/* 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 GNU/Linux 2.4 USB because one cannot + predict the exact dxfer_size without knowing the sector type. + if (!e) + lba = 0; + else + lba = burn_msf_to_lba(e->pmin, e->psec, + e->pframe); + mem->sectors = 1; + + ts B21119 : Would now be d->read_cd() with + with sectype = 0 , mainch = 0xf8 + d->read_sectors(d, lba, mem.sectors, &o, mem); + + t->mode = sector_identify(mem->data); +*/ + } + } + +ex: + BURN_FREE_MEM(mem); +} diff --git a/trunk/libburn/toc.h b/trunk/libburn/toc.h new file mode 100644 index 0000000..b1e8bd5 --- /dev/null +++ b/trunk/libburn/toc.h @@ -0,0 +1,53 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Provided under GPL version 2 or later. +*/ + + +#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/trunk/libburn/transport.h b/trunk/libburn/transport.h new file mode 100644 index 0000000..ec4579f --- /dev/null +++ b/trunk/libburn/transport.h @@ -0,0 +1,513 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2014 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + +#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). ) + burn_write_opts.cdxa_conversion can imply an offset of 8 bytes. + */ + 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; + int timeout; /* milliseconds */ + + double start_time; + double end_time; + +}; + +struct burn_scsi_inquiry_data +{ + char peripheral; /* bit0-4: device type should be 5 + bit5-7: qualifier must be 0 */ + + char version; /* should be 3 (SPC-1) to 5 (SPC-3) (or higher ?) + but is often 0. */ + + char vendor[9]; + char product[17]; + char revision[5]; + + int valid; +}; + + +struct scsi_mode_data +{ + int p2a_valid; + 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 c2_pointers; + int underrun_proof; + + int max_read_speed; + int cur_read_speed; + int max_write_speed; + int cur_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 retry_page_length; + int retry_page_valid; + int write_page_length; + int write_page_valid; +}; + + +/* 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; +}; + + +/* ts B40106 : represents a Feature Descriptor as of mmc5r03c.pdf 5.2.2 + There can be many of them. Thus a linked list. +*/ +struct burn_feature_descr { + unsigned short feature_code; + + unsigned char flags; /* bit0= current + bit1= persistent + bit2-5= version + */ + + unsigned char data_lenght; + + /* Additional bytes after the first 4 bytes of the descriptor */ + unsigned char *data; + + struct burn_feature_descr *next; +}; + + +/** 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 + 4=stdio random read-only + 5=stdio random 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 A90603 */ + int current_is_guessed_profile; + /* ts A90815 */ + unsigned char all_profiles[256]; + int num_profiles; + + /* ts B40106 : All feature descriptors as read from drive */ + struct burn_feature_descr *features; + + /* 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; + + /* Some drives announce feature 21h on fast-blanked DVD-RW + although they cannot write them in Incremental mode. + 0= does not look like the recent write run failed due to + Incremental on fast blanked DVD-RW + 1= it seems to have happened + 2= it seems to have happened with write address 0 + */ + int was_feat21h_failure; + + /* Link Size item number 0 from feature 0021h descriptor */ + int current_feat21h_link_size; + + /* Flags from feature 0023h for formatting BD mmc5r03c.pdf 5.3.13 + Byte 4 BD-RE: + bit0= Cert format 30h sub-type 10b + bit1= QCert format 30h sub-type 11b + bit2= Expand format 01h + bit3= RENoSA format 31h + Byte 8 BD-R: + bit0= RRM format 32h sub-type 10b + */ + int current_feat23h_byte4; + int current_feat23h_byte8; + + + /* 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 B10524 : whether the damage bit was set for the future track. + bit0= damage bit , bit1= nwa valid bit + */ + int next_track_damaged; + + /* 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 A80412 : whether to use WRITE12 with Streaming bit set + rather than WRITE10. Speeds up DVD-RAM. Might help + with BD-RE */ + int do_stream_recording; + + /* ts A90227 : the LBA where stream recording shall start. + Writing to lower LBA will be done without streaming. + */ + int stream_recording_start; + + /* ts A61218 from 51h READ DISC INFORMATION */ + int last_lead_in; + int last_lead_out; + int num_opc_tables; /* ts A91104: -1= not yet known */ + int bg_format_status; /* 0=needs format start, 1=needs format restart*/ + int disc_type; /* 0="CD-DA or CD-ROM", 0x10="CD-I", 0x20="CD-ROM XA" */ + unsigned int disc_id; /* a "32 bit binary integer" */ + char disc_bar_code[9]; + int disc_app_code; + int disc_info_valid; /* bit0= disc_type , bit1= disc_id , + bit2= disc_bar_code , bit3= disc_app_code + bit4= URU bit is set (= unrestricted use) + bit5= Erasable bit was set in reply + */ + + /* 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 */ + /* 0= report errors + 1= do not report errors + 2= do not report errors which the libburn function indicates in + member .had_particular_error + 3= report errors with severity DEBUG + */ + int silent_on_scsi_error; + + /* ts B21023 */ + /* bit0= 5 64 00 occured with READ10 in mmc_read_10() + */ + int had_particular_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 A90107 */ + int state_of_last_session; + +#ifdef Libburn_disc_with_incomplete_sessioN + /* ts B30112 */ + int incomplete_sessions; +#endif + + + /* ts A70129 : + from 51h READ DISC INFORMATION Last Track Number in Last Session */ + int last_track_no; + + /* ts B10730 : whether a default mode page 05 was already sent. + */ + int sent_default_page_05; + /* 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; + + /* ts A81210 : Upper limit of readable data size, + 0x7fffffff = unknown + 0x7ffffff0 = 32 bit overflow, or unknown stdio size + */ + int media_read_capacity; + + /* ts B10314 : Next Writeable Adress for drive_role == 5 */ + int role_5_nwa; + + int toc_temp; + struct burn_disc *disc; /* disc structure */ + int block_types[4]; + struct buffer *buffer; + struct burn_progress progress; + + /* To be used by mmc.c, sbc.c, spc.c for SCSI commands where the struct + content surely does not have to persist while another command gets + composed and executed. + (Inherently, sending SCSI commands to the same drive cannot be + thread-safe. But there are functions which send SCSI commands + and also call other such functions. These shall use own allocated + command structs and not this struct here.) + */ + struct command casual_command; + + /* 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; + + /* During write runs, this points to a copy of the applied + struct burn_write_opts. Only read this underneath + burn_disc_write_sync() which removes the copy when done. + Especially do not read it from outside the write thread. + */ + struct burn_write_opts *write_opts; + + /* ts A70929 */ + pid_t thread_pid; + int thread_pid_valid; + /* ts B00225 */ + pthread_t thread_tid; + + +/* 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 *); + + /* ts A90824 : Calming down noisy drives */ + int (*stop_unit) (struct burn_drive *); + int is_stopped; + + void (*read_disc_info) (struct burn_drive *); + int (*read_cd) (struct burn_drive *, int start, int len, + int sec_type, int main_ch, + const struct burn_read_opts *, struct buffer *, + int flag); + 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 *, + struct burn_session *, int tno, + const struct burn_write_opts *); + int (*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 */ + +/* ts A80422 : centralizing this setting for debugging purposes +*/ +int burn_drive_set_media_capacity_remaining(struct burn_drive *d, off_t value); + + +#endif /* __TRANSPORT */ diff --git a/trunk/libburn/util.c b/trunk/libburn/util.c new file mode 100644 index 0000000..9dae9b5 --- /dev/null +++ b/trunk/libburn/util.c @@ -0,0 +1,401 @@ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2013 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#include <string.h> + +/* ts A61008 */ +/* #include <a ssert.h> */ + +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + + +/* ts A80914 : This is unneeded. Version info comes from libburn.h. +#include "v ersion.h" +*/ + +#include "util.h" +#include "libburn.h" + + +void burn_version(int *major, int *minor, int *micro) +{ +/* ts A80408 : switched from configure.ac versioning to libburn.h versioning */ + *major = burn_header_version_major; + *minor = burn_header_version_minor; + *micro = burn_header_version_micro; +} + + +struct cd_mid_record { + char *manufacturer; + int m_li; + int s_li; + int f_li; + char *other_brands; +}; +typedef struct cd_mid_record cd_mid_record_t; + + +/* ts A90902 */ +/** API + @param flag Bitfield for control purposes, + bit0= append "(aka %s)",other_brands to reply + */ +char *burn_guess_cd_manufacturer(int m_li, int s_li, int f_li, + int m_lo, int s_lo, int f_lo, int flag) +{ + static cd_mid_record_t mid_list[]= { + {"SKC", 96, 40, 0, ""}, + {"Ritek Corp" , 96, 43, 30, ""}, + {"TDK / Ritek" , 97, 10, 0, "TRAXDATA"}, + {"TDK Corporation" , 97, 15, 0, ""}, + {"Ritek Corp" , 97, 15, 10, "7-plus, Aopen, PONY, Power Source, TDK, TRAXDATA, HiCO, PHILIPS, Primdisc, Victor.JVC, OPTI STORAGE, Samsung"}, + {"Mitsubishi Chemical Corporation" , 97, 15, 20, ""}, + {"Nan-Ya Plastics Corporation" , 97, 15, 30, "Hatron, MMore, Acer, LITEON"}, + {"Delphi" , 97, 15, 50, ""}, + {"Shenzhen SG&SAST" , 97, 16, 20, ""}, + {"Moser Baer India Limited" , 97, 17, 0, "EMTEC, Intenso, YAKUMO, PLATINUM, Silver Circle"}, + {"SKY media Manufacturing SA" , 97, 17, 10, ""}, + {"Wing" , 97, 18, 10, ""}, + {"DDT" , 97, 18, 20, ""}, + {"Daxon Technology Inc. / Acer" , 97, 22, 60, "Maxmax, Diamond Data, BenQ, gold, SONY"}, + {"Taiyo Yuden Company Limited" , 97, 24, 0, "Maxell, FUJIFILM, SONY"}, + {"Sony Corporation" , 97, 24, 10, "LeadData, Imation"}, + {"Computer Support Italcard s.r.l" , 97, 24, 20, ""}, + {"Unitech Japan Inc." , 97, 24, 30, ""}, + {"MPO, France" , 97, 25, 0, "TDK"}, + {"Hitachi Maxell Ltd." , 97, 25, 20, ""}, + {"Infodisc Technology Co,Ltd." , 97, 25, 30, "MEMOREX, SPEEDA, Lead data"}, + {"Xcitec" , 97, 25, 60, ""}, + {"Fornet International Pte Ltd" , 97, 26, 0, "COMPUSA, Cdhouse"}, + {"Postech Corporation" , 97, 26, 10, "Mr.Platinum"}, + {"SKC Co Ltd." , 97, 26, 20, "Infinite"}, + {"Fuji Photo Film Co,Ltd." , 97, 26, 40, ""}, + {"Lead Data Inc." , 97, 26, 50, "SONY, Gigastorage, MIRAGE"}, + {"CMC Magnetics Corporation" , 97, 26, 60, "Daxon, Verbatim, Memorex, Bi-Winner, PLEXTOR, YAMAHA, Melody, Office DEPOT, Philips, eMARK, imation, HyperMedia, Samsung, Shintaro, Techworks"}, + {"Ricoh Company Limited" , 97, 27, 0, "Sony, Digital Storage, Csita"}, + {"Plasmon Data Systems Ltd" , 97, 27, 10, "Ritek, TDK, EMTEC, ALPHAPET, MANIA"}, + {"Princo Corporation" , 97, 27, 20, ""}, + {"Pioneer" , 97, 27, 30, ""}, + {"Eastman Kodak Company" , 97, 27, 40, ""}, + {"Mitsui Chemicals Inc." , 97, 27, 50, "MAM-A, TDK"}, + {"Ricoh Company Limited" , 97, 27, 60, "Ritek"}, + {"Gigastorage Corporation" , 97, 28, 10, "MaxMax, Nan-Ya"}, + {"Multi Media Masters&Machinary SA" , 97, 28, 20, "King, Mmirex"}, + {"Ritek Corp" , 97, 31, 0, "TDK"}, + {"Grand Advance Technology Sdn. Bhd." , 97, 31, 30, ""}, + {"TDK Corporation" , 97, 32, 00, ""}, + {"Prodisc Technology Inc." , 97, 32, 10, "Smartbuy, Mitsubishi, Digmaster, LG, Media Market"}, + {"Mitsubishi Chemical Corporation" , 97, 34, 20, "YAMAHA, Verbatim"}, + {"Mitsui Chemicals Inc." , 97, 48, 50, ""}, + {"TDK Corporation" , 97, 49, 0, ""}, + {"", 0, 0, 0, ""} + }; + + int i, f_li_0; + char buf[1024]; + char *result = NULL; + + if (m_li == 0 && s_li == 2 && f_li == 0) { + result = strdup("(no manufacturer code)"); + return result; + } + f_li_0 = f_li - (f_li % 10); + for (i = 0; mid_list[i].manufacturer[0]; i++) { + if (m_li == mid_list[i].m_li && + s_li == mid_list[i].s_li && + (f_li_0 == mid_list[i].f_li || f_li == mid_list[i].f_li)) + break; + } + if (mid_list[i].manufacturer[0] == 0) { + sprintf(buf, "Unknown CD manufacturer. Please report code '%2.2dm%2.2ds%2.2df/%2.2dm%2.2ds%2.2df', the human readable brand, size, and speed to scdbackup@gmx.net.", m_li, s_li, f_li, m_lo, s_lo, f_lo); + result = strdup(buf); + return result; + } + + /* Compose, allocate and copy result */ + if ((flag & 1) && mid_list[i].other_brands[0]) { + sprintf(buf, "%s (aka %s)", + mid_list[i].manufacturer, mid_list[i].other_brands); + result = strdup(buf); + } else + result = strdup(mid_list[i].manufacturer); + return result; +} + + +/* ts A90904 */ +struct dvd_mid_record { + char *mc1; + int mc1_sig_len; + char *manufacturer; +}; +typedef struct dvd_mid_record dvd_mid_record_t; + +/* ts A90904 */ +char *burn_guess_manufacturer(int prf, + char *media_code1, char *media_code2, int flag) +{ + int i, l = 0, m_li, s_li, f_li, m_lo, s_lo, f_lo; + char buf[1024]; + char *result = NULL, *cpt; + + /* Important Note: media_code1 and media_code2 are supposed to be + encoded by burn_util_make_printable_word(). + Especially: ' ' -> '_' , {"_%/" unprintables -> %XY) + */ + static dvd_mid_record_t mid_list[]= { + {"AML", 8, "UML"}, + {"BeAll", 5, "BeAll Developers, Inc."}, + {"CMC", 3, "CMC Magnetics Corporation"}, + {"DAXON", 5, "Daxon Technology Inc. / Acer"}, + {"Daxon", 5, "Daxon Technology Inc. / Acer"}, + {"FUJI", 4, "Fujifilm Holdings Corporation"}, + {"INFODISC", 8, "New Star Digital Co., Ltd."}, + {"INFOME", 6, "InfoMedia Inc."}, + {"ISMMBD", 6, "Info Source Multi Media Ltd."}, + {"JVC", 3, "JVC Limited"}, + {"KIC01RG", 7, "AMC"}, + {"LD", 8, "Lead Data Inc."}, + {"LGE", 3, "LG Electronics"}, + {"MAM", 8, "Mitsui Advanced Media, Inc. Europe"}, + {"MAXELL", 6, "Hitachi Maxell Ltd."}, + {"MBI", 3, "Moser Baer India Limited"}, + {"MCC", 8, "Mitsubishi Chemical Corporation"}, + {"MCI", 8, "Mitsui Chemicals Inc."}, + {"MEI", 3, "Panasonic Corporation"}, + {"MKM", 3, "Mitsubishi Kagaku Media Co."}, + {"MMC", 8, "Mitsubishi Kagaku Media Co."}, + {"MXL", 8, "Hitachi Maxell Ltd."}, + {"NANYA", 5, "Nan-Ya Plastics Corporation"}, + {"NSD", 8, "NESA International Inc."}, + {"OPTODISC", 8, "Optodisc Technology Corporation"}, + {"OTCBDR", 8, "Optodisc Technology Corporation"}, + {"PHILIP", 8, "Moser Baer India Limited"}, + {"PHILIPS", 8, "Philips"}, + {"PRINCO", 6, "Princo Corporation"}, + {"PRODISC", 7, "Prodisc Technology Inc."}, + {"Prodisc", 7, "Prodisc Technology Inc."}, + {"PVC", 3, "Pioneer"}, + {"RICOHJPN", 8, "Ricoh Company Limited"}, + {"RITEK", 5, "Ritek Corp"}, + {"SONY", 4, "Sony Corporation"}, + {"TDK", 3, "TDK Corporation"}, + {"TT", 8, "TDK Corporation"}, + {"TY", 8, "Taiyo Yuden Company Limited"}, + {"TYG", 3, "Taiyo Yuden Company Limited"}, + {"UME", 3, "UmeDisc Limited"}, + {"UTJR001", 7, "Unifino Inc."}, + {"VERBAT", 5, "Mitsubishi Kagaku Media Co."}, + {"YUDEN", 5, "Taiyo Yuden Company Limited"}, + {"", 0, ""} + }; + + if (media_code2 != NULL && + (prf == -1 || prf == 0x09 || prf == 0x0A)) { + if (strlen(media_code2) == 9 && media_code1[0] == '9' && + media_code1[2] == 'm' && media_code1[5] == 's' && + media_code1[8] == 'f' && + strchr(media_code1, '%') == NULL) { + sscanf(media_code1, "%dm%ds%df", &m_li, &s_li, &f_li); + sscanf(media_code2, "%dm%ds%df", &m_lo, &s_lo, &f_lo); + if (m_li >= 96 && m_li <= 97 && m_lo > 0) { + result = burn_guess_cd_manufacturer( + m_li, s_li, f_li, m_lo, s_lo, f_lo, 0); + return result; + } + } + } + + /* DVD-R do not keep manufacturer id apart from media id. + Some manufacturers use a blank as separator which would now be '_'. + */ + cpt = strchr(media_code1, '_'); + if (cpt != NULL && (prf == -1 || prf == 0x11 || prf == 0x13 || + prf == 0x14 || prf == 0x15)) + l = cpt - media_code1; + + for (i = 0; mid_list[i].mc1[0]; i++) { + if (strncmp(mid_list[i].mc1, media_code1, + mid_list[i].mc1_sig_len) == 0) + break; + if (l > 0) + if (strncmp(mid_list[i].mc1, media_code1, l) == 0) + break; + } + if (mid_list[i].mc1[0] == 0) { + sprintf(buf, "Unknown DVD/BD manufacturer. Please report code '%s/%s', the human readable brand, size, and speed to scdbackup@gmx.net.", + media_code1, media_code2); + result = strdup(buf); + return result; + } + result = strdup(mid_list[i].manufacturer); + return result; +} + + +/* ts A90905 */ +/* Make *text a single printable word */ +/* IMPORTANT: text must be freeable memory ! + @param flag bit0=escape '/' too + bit1=(overrides bit0) do not escape " _/" +*/ +int burn_util_make_printable_word(char **text, int flag) +{ + int i, esc_add = 0, ret; + char *wpt, *rpt, *new_text = NULL; + + if (flag & 2) + flag &= ~1; + + for (i = 0; (*text)[i]; i++) { + rpt = (*text) + i; + if (*rpt < 32 || *rpt > 126 || *rpt == 96 || + ((*rpt == '_' || *rpt == '%') && (!(flag & 2))) || + (*rpt == '/' && (flag & 1))) + esc_add += 2; + } + if (esc_add) { + new_text = calloc(strlen(*text) + esc_add + 1, 1); + if (new_text == NULL) { + ret = -1; + goto ex; + } + wpt = new_text; + for (i = 0; (*text)[i]; i++) { + rpt = (*text) + i; + if (*rpt < 32 || *rpt > 126 || *rpt == 96 || + ((*rpt == '_' || *rpt == '%') && (!(flag & 2))) || + (*rpt == '/' && (flag & 1))) { + sprintf(wpt, "%%%2.2X", + (unsigned int) *((unsigned char *) rpt)); + wpt+= 3; + } else + *(wpt++) = *rpt; + } + *wpt = 0; + free(*text); + *text = new_text; + } + if (!(flag & 2)) + for (i = 0; (*text)[i]; i++) + if ((*text)[i] == ' ') + (*text)[i] = '_'; + ret = 1; +ex: + return ret; +} + + +/* ts B11216 */ +/** Read a line from fp and strip LF or CRLF */ +char *burn_sfile_fgets(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; +} + + +char *burn_printify(char *msg) +{ + char *cpt; + + for (cpt = msg; *cpt != 0; cpt++) + if (*cpt < 32 || *cpt > 126) + *cpt = '#'; + return msg; +} + + +/* ts B30521 */ +void burn_int_to_lsb(int val, char *target) +{ + unsigned char *buf; + + buf = (unsigned char *) target; + buf[0] = val & 0xff; + buf[1] = (val >> 8) & 0xff; + buf[2] = (val >> 16) & 0xff; + buf[3] = (val >> 24) & 0xff; +} + + +/* ts B30609 */ +double burn_get_time(int flag) +{ + int ret; + struct timeval tv; + +#ifdef Libburn_use_clock_gettime_monotoniC +#ifdef _POSIX_TIMERS +#ifdef _POSIX_MONOTONIC_CLOCK + + /* Enable by + export CFLAGS=-DLibburn_use_clock_gettime_monotoniC + export LIBS=-lrt + ./configure ... && make clean && make + */ + + struct timespec tp; + ret = clock_gettime(CLOCK_MONOTONIC, &tp); + if (ret == 0) + return ((double) tp.tv_sec) + ((double) tp.tv_nsec) * 1.0e-9; + +#endif /* _POSIX_MONOTONIC_CLOCK */ +#endif /* _POSIX_TIMERS */ +#endif /* Xorriso_use_clock_gettime_monotoniC */ + + ret = gettimeofday(&tv, NULL); + if (ret == 0) + return ((double) tv.tv_sec) + ((double) tv.tv_usec) * 1.0e-6; + return (double) time(NULL); +} + +/* ts B40609 */ +off_t burn_sparse_file_addsize(off_t write_start, struct stat *stbuf) +{ + off_t add_size; + + add_size = stbuf->st_blocks * (off_t) 512; + if (add_size < stbuf->st_size) { + /* Sparse file */ + if (write_start < stbuf->st_size) { + /* Might write into sparse gaps */ + if (write_start > add_size) + add_size = write_start; + } else { + /* Will not write into sparse area */ + add_size = stbuf->st_size; + } + } + return add_size; +} + diff --git a/trunk/libburn/util.h b/trunk/libburn/util.h new file mode 100644 index 0000000..144f8cc --- /dev/null +++ b/trunk/libburn/util.h @@ -0,0 +1,25 @@ +#ifndef __UTIL +#define __UTIL + +/* for struct stat */ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +/* ts A90905 */ +int burn_util_make_printable_word(char **text, int flag); + +/* ts B11216 */ +char *burn_sfile_fgets(char *line, int maxl, FILE *fp); +char *burn_printify(char *msg); + +/* ts B30521 */ +void burn_int_to_lsb(int val, char *target); + +/* ts B30609 */ +double burn_get_time(int flag); + +/* ts B40609 */ +off_t burn_sparse_file_addsize(off_t write_start, struct stat *stbuf); + +#endif diff --git a/trunk/libburn/write.c b/trunk/libburn/write.c new file mode 100644 index 0000000..282d1fb --- /dev/null +++ b/trunk/libburn/write.c @@ -0,0 +1,3417 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2012 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#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> + +/* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#include "error.h" +#include "sector.h" +#include "libburn.h" +#include "drive.h" +#include "transport.h" +#include "debug.h" +#include "init.h" +#include "toc.h" +#include "util.h" +#include "sg.h" +#include "write.h" +#include "options.h" +#include "structure.h" +#include "source.h" +#include "mmc.h" +#include "spc.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + + +/* ts A91120 : <<< experimental */ +#ifdef Libburn_mmap_write_buffeR +#include <sys/mman.h> +#endif + + +/* The maximum output size to be used with CD media. This is also curbed + by BURN_OS_TRANSPORT_BUFFER_SIZE. The smaller number gets into effect. +*/ +#define Libburn_cd_obS (32 * 1024) + +/* The size to be used with DVD media. +*/ +#define Libburn_dvd_obS (32 * 1024) + +/* The size to be used with BD-RE media in normal, not streamed mode. +*/ +#define Libburn_bd_re_obS (64 * 1024) + +/* The size to be used with BD-R media in normal, not streamed mode. +*/ +#define Libburn_bd_r_obS (64 * 1024) + +/* The size to be used with BD-RE and BD-R media in streamed mode. +*/ +#define Libburn_bd_streamed_obS (64 * 1024) + +/* The number of retries if write(2) returns a short, non-negative write count. +*/ +#define Libburn_stdio_write_retrieS 16 + + +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; + + /* ts A61106 */ +#ifdef Libburn_experimental_no_close_tracK + return 1; +#endif + + d = o->drive; + + 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) +{ + + /* 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, B20101: + This is useful only when changes about CD SAO get tested. + # define Libburn_write_with_function_print_cuE yes +*/ + +#ifdef Libburn_write_with_function_print_cuE + + +static char cue_printify(char c) +{ + if (c >= 32 && c < 127) + return c; + return '#'; +} + + +static void print_cue(struct cue_sheet *sheet) +{ + int i; + unsigned char *unit; + + printf("\n"); + printf("ctladr|trno|indx|form|scms| msf | text\n"); + printf("------+----+----+----+----+----------+--------\n"); + for (i = 0; i < sheet->count; i++) { + unit = sheet->data + 8 * i; + if ((unit[0] & 0xf) == 2) { + printf( + " %1X %1X | | | | | | %c%c%c%c%c%c%c\n", + (unit[0] & 0xf0) >> 4, unit[0] & 0xf, + cue_printify(unit[1]), cue_printify(unit[2]), + cue_printify(unit[3]), cue_printify(unit[4]), + cue_printify(unit[5]), cue_printify(unit[6]), + unit[7] == 0 ? ' ' : cue_printify(unit[7])); + } else if ((unit[0] & 0xf) == 3) { + printf( + " %1X %1X | %2d | | | | | %c%c%c%c%c%c\n", + (unit[0] & 0xf0) >> 4, unit[0] & 0xf, + unit[1], cue_printify(unit[2]), + cue_printify(unit[3]), cue_printify(unit[4]), + cue_printify(unit[5]), cue_printify(unit[6]), + cue_printify(unit[7])); + } else if (unit[1] > 99) { + printf(" %1X %1X |0x%02X| %2d | %02X | %02X |", + (unit[0] & 0xf0) >> 4, unit[0] & 0xf, + unit[1], unit[2], unit[3], unit[4]); + printf(" %02d:%02d:%02d |\n", + unit[5], unit[6], unit[7]); + } else { + printf(" %1X %1X | %2d | %2d | %02X | %02X |", + (unit[0] & 0xf0) >> 4, unit[0] & 0xf, + unit[1], unit[2], unit[3], unit[4]); + printf(" %02d:%02d:%02d |\n", + unit[5], unit[6], unit[7]); + } + } + fflush(stdout); +} + +#endif /* Libburn_write_with_print_cuE */ + + +/* ts B11226 */ +static int new_cue(struct cue_sheet *sheet, int number, int flag) +{ + unsigned char *ptr; + + ptr = realloc(sheet->data, (sheet->count + number) * 8); + 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; + sheet->count += number; + return 1; +} + + +/* ts B11226 : outsourced new_cue() */ +/** @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; + int m, s, f, ret; + + burn_lba_to_msf(lba, &m, &s, &f); + + ret = new_cue(sheet, 1, 0); + if (ret <= 0) + return -1; + 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 B11226 */ +static int add_catalog_cue(struct cue_sheet *sheet, unsigned char catalog[13]) +{ + unsigned char *unit; + int i, ret; + + ret = new_cue(sheet, 2, 0); + if (ret <= 0) + return -1; + unit = sheet->data + (sheet->count - 2) * 8; + unit[0] = unit[8] = 0x02; + for (i = 0; i < 13; i++) + unit[1 + (i >= 7) * 8 + (i % 7)] = catalog[i]; + unit[15] = 0x00; + return 1; +} + + +/* ts B11226 */ +static int add_isrc_cue(struct cue_sheet *sheet, unsigned char ctladr, int tno, + struct isrc *isrc) +{ + unsigned char *unit; + int i, ret; + char text[8]; + + ret = new_cue(sheet, 2, 0); + if (ret <= 0) + return -1; + unit = sheet->data + (sheet->count - 2) * 8; + unit[0] = unit[8] = (ctladr & 0xf0) | 0x03; + unit[1] = unit[9] = tno; + unit[2] = isrc->country[0]; + unit[3] = isrc->country[1]; + unit[4] = isrc->owner[0]; + unit[5] = isrc->owner[1]; + unit[6] = isrc->owner[2]; + sprintf(text, "%-2.2u%-5.5u", (unsigned int) isrc->year, isrc->serial); + unit[7] = text[0]; + for (i = 1; i < 7; i++) + unit[9 + i] = text[i]; + 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, runtime = -150, ret, track_length; + int leadin_form, leadin_start, pregap = 150, postgap; + unsigned char ctladr, scms; + 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; + +#define Libburn_track_multi_indeX yes + +#ifdef Libburn_track_multi_indeX + int j; +#else + int pform; +#endif + + if (ntr < 1) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002019c, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Session has no defined tracks", 0, 0); + return NULL; + } + + d = o->drive; + +#ifdef Libburn_sao_can_appenD + if (d->status == BURN_DISC_APPENDABLE) + runtime = nwa-150; +#endif + + sheet = calloc(1, sizeof(struct cue_sheet)); + + /* ts A61009 : react on failures of calloc(), 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; + } + if (tar[0]->mode & BURN_AUDIO) + leadin_form = 0x01; + else + leadin_form = 0x14; + if (o->num_text_packs > 0) { + leadin_form |= 0x40; + } else { + /* Check for CD-TEXT in session. Not the final creation, + because the cue sheet content might be needed for CD-TEXT + pack type 0x88 "TOC". + */ + if (o->text_packs == NULL) { + ret = burn_cdtext_from_session(session, NULL, NULL, 1); + if (ret < 0) + goto failed; + else if (ret > 0) + leadin_form |= 0x40; + } + } + + if (o->has_mediacatalog) + ret = add_catalog_cue(sheet, o->mediacatalog); + else if (session->mediacatalog[0]) + ret = add_catalog_cue(sheet, session->mediacatalog); + else + ret = 1; + if (ret <= 0) + goto failed; + + /* ts B11225 + MMC-5 6.33.3.15 Data Form of Sub-channel + seems to indicate that for leadin_form 0x41 one should announce + d->start_lba as start of the leadin (e.g. -12490) and that data + block type should 2 or 3 with mode page 05h. But my drives refuse + on that. + It works with LBA -150 and data block type 0. Shrug. + */ + leadin_start = runtime; + ret = add_cue(sheet, (ctladr & 64) | 1, 0, 0, leadin_form, 0, + leadin_start); + if (ret <= 0) + goto failed; + + 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; + */ + } + if (session->firsttrack + ntr - 1 > 99) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002019b, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "CD track number exceeds 99", 0, 0); + goto failed; + } + session->lasttrack = session->firsttrack + ntr - 1; + + 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 = session->firsttrack; + e[0].psec = o->format; + e[0].adr = 1; + e[1].point = 0xA1; + e[1].pmin = session->lasttrack; + e[1].adr = 1; + if (tar[ntr - 1]->mode & BURN_AUDIO) + e[1].control = TOC_CONTROL_AUDIO; + else + e[1].control = TOC_CONTROL_DATA; + e[2].point = 0xA2; + e[2].control = e[1].control; + e[2].adr = 1; + + tar[0]->pregap2 = 1; + if (tar[0]->pregap2_size < 150) + tar[0]->pregap2_size = 150; + +#ifndef Libburn_track_multi_indeX + pform = form; +#endif + + for (i = 0; i < ntr; i++) { + + /* 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_2(tar[i], 1); + 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); + } + + type_to_form(tar[i]->mode, &ctladr, &form); + if (tar[i]->mode & BURN_SCMS) + scms = 0x80; + else + scms = 0; + + if (tar[i]->isrc.has_isrc) { + ret = add_isrc_cue(sheet, ctladr, + i + session->firsttrack, &(tar[i]->isrc)); + if (ret <= 0) + goto failed; + } + pregap = 0; + if (tar[i]->pregap2) + pregap = tar[i]->pregap2_size; + postgap = 0; + if (tar[i]->postgap) { + if (tar[i]->indices >= 99) { + libdax_msgs_submit(libdax_messenger, -1, + 0x0002019a, LIBDAX_MSGS_SEV_SORRY, + LIBDAX_MSGS_PRIO_HIGH, + "Post-gap index number exceeds 99", + 0, 0); + goto failed; + } + if (tar[i]->indices < 2) + tar[i]->indices = 2; + tar[i]->index[tar[i]->indices] = track_length; + postgap = tar[i]->postgap_size; + } + +#ifdef Libburn_track_multi_indeX + + for(j = 0; j < (tar[i]->indices + !!tar[i]->postgap) || j < 2; + j++) { + if(tar[i]->index[j] == 0x7fffffff) { + if (j > 1) + break; + if (j == 0 && pregap <= 0) + continue; + /* force existence of mandatory index */ + tar[i]->index[j] = 0; + } else if (j == 0) { + tar[i]->index[j] = 0; + } else if (j == 1 && tar[i]->index[0] == 0x7fffffff) { + tar[i]->index[j] = 0; + } + + if (j == 1) { + tar[i]->entry = &e[3 + i]; + e[3 + i].point = i + session->firsttrack; + 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); + } + + /* >>> ??? else if j == 0 && mode change to -data : + Extended pregap */; + + /* >>> check index with track size */; + + tar[i]->index[j] += runtime; + ret = add_cue(sheet, ctladr | 1, + i + session->firsttrack, j, form, scms, + tar[i]->index[j]); + if (ret <= 0) + goto failed; + runtime += pregap; + pregap = 0; + } + + runtime += track_length + postgap; + +#else /* Libburn_track_multi_indeX */ + + if (i == 0) { + ret = add_cue(sheet, ctladr | 1, session->firsttrack, + 0, form, 0, runtime); + if (ret <= 0) + goto failed; + runtime += 150; + } else if (pform != form) { + + /* ts A70121 : This seems to be thw wrong test. Correct would + be to compare tar[]->mode or bit2 of ctladr. + */ + + ret = add_cue(sheet, ctladr | 1, + i + session->firsttrack, 0, form, scms, + 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. + ts B20111: Table 1 of MMC-1 shows two post-gaps. The first matches the + precriptions with SEND CUE SHEET. The second one is riddling. + Both are part of a track and occupy the range of the last index + of the track. Length is 2 seconds for each. + + 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 + session->firsttrack; + 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); + + ret = add_cue(sheet, ctladr | 1, i + session->firsttrack, + 1, form, scms, runtime); + if (ret <= 0) + goto failed; + + runtime += track_length; + +#endif /* ! Libburn_track_multi_indeX */ + + +/* 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--; + } + +#ifndef Libburn_track_multi_indeX + pform = form; +#endif + + } + burn_lba_to_msf(runtime, &m, &s, &f); + e[2].pmin = m; + e[2].psec = s; + e[2].pframe = f; + + ret = add_cue(sheet, ctladr | 1, 0xAA, 1, leadin_form & 0x3f, + 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; + + 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; + 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; +} + + +static int burn_create_text_packs(struct burn_write_opts *o, + struct burn_session *s, + int flag) +{ + int ret, num_packs = 0; + unsigned char *text_packs = NULL; + + ret = burn_cdtext_from_session(s, &text_packs, &num_packs, 0); + if (ret > 0) { + if (o->text_packs != NULL) + free(o->text_packs); + o->text_packs = text_packs; + o->num_text_packs = num_packs; + } + return(ret); +} + + +static int burn_write_leadin_cdtext(struct burn_write_opts *o, + struct burn_session *s, int flag) +{ + int ret, i, j, si, lba, sub_cursor = 0, err, write_lba, sectors = 0; + int self_made_text_packs = 0; + unsigned char *subdata = NULL; + struct burn_drive *d = o->drive; + struct buffer *buf = NULL; + enum burn_drive_status was_busy = o->drive->busy; +#ifdef Libburn_debug_cd_texT + unsigned char *packs; +#endif + + if (o->num_text_packs <= 0) { + if (o->text_packs != NULL) + {ret = 1; goto ex;} + /* Try to create CD-TEXT from .cdtext_* of session and track */ + ret = burn_create_text_packs(o, s, 0); + self_made_text_packs = 1; + if (ret <= 0) + goto ex; + if (o->num_text_packs <= 0) + {ret = 1; goto ex;} + } + + if (!o->no_text_pack_crc_check) { + ret = burn_cdtext_crc_mismatches(o->text_packs, + o->num_text_packs, 0); + if (ret != 0) { + libdax_msgs_submit(libdax_messenger, -1, 0x0002018f, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Program error: CD-TEXT pack CRC mismatch", + 0, 0); + { ret = 0; goto ex; } + } + } + + d->busy = BURN_DRIVE_WRITING_LEADIN; + +#ifdef Libburn_debug_cd_texT + packs = o->text_packs; + fprintf(stderr, + "libburn_DEBUG: 8 bit CD-TEXT packs to be transmitted:\n"); + for (i = 0; i < 18 * o->num_text_packs; i += 18) { + fprintf(stderr, "%4d :", i / 18); + for (j = 0; j < 18; j++) { + if (j >= 4 && j <= 15 && packs[i + j] >= 32 && + packs[i + j] <= 126 && packs[i] != 0x88 && + packs[i] != 0x89 && packs[i] != 0x8f) + fprintf(stderr, " %c", packs[i + j]); + else + fprintf(stderr, " %2.2X", packs[i + j]); + } + fprintf(stderr, "\n"); + } +#endif /* Libburn_debug_cd_texT */ + + /* Chop from 8 bit text pack to 6 bit subchannel */ + BURN_ALLOC_MEM(subdata, unsigned char, o->num_text_packs * 24); + for (i = 0; i < 18 * o->num_text_packs; i += 3) { + si = i / 3 * 4; + subdata[si + 0] = (o->text_packs[i + 0] >> 2) & 0x3f; + subdata[si + 1] = (o->text_packs[i + 0] << 4) & 0x30; + subdata[si + 1] |= (o->text_packs[i + 1] >> 4) & 0x0f; + subdata[si + 2] = (o->text_packs[i + 1] << 2) & 0x3c; + subdata[si + 2] |= (o->text_packs[i + 2] >> 6) & 0x03; + subdata[si + 3] = (o->text_packs[i + 2] >> 0) & 0x3f; + } + + /* Start at Lead-in address of ATIP and write blocks up to -150 */ + BURN_ALLOC_MEM(buf, struct buffer, 1); + write_lba = d->start_lba; + for (lba = d->start_lba; lba < -150; lba++) { + /* Collect subdata in buf */ + for (j = 0; j < 4; j++) { + memcpy(buf->data + buf->bytes, + subdata + sub_cursor * 24, 24); + sub_cursor = (sub_cursor + 1) % o->num_text_packs; + buf->bytes += 24; + } + buf->sectors++; + sectors++; + + /* When full or last sector : perform WRITE */ + if (buf->bytes + 96 >= 32768 || lba == -151) { + +#ifdef Libburn_debug_cd_texT + fprintf(stderr, + "libburn_DEBUG: 6 bit data to be transmitted:\n"); + for (i = 0; i < buf->bytes; i += 24) { + fprintf(stderr, "%4d :", i / 24); + for (j = 0; j < 24; j++) + fprintf(stderr, " %2.2X", + buf->data[i + j]); + fprintf(stderr, "\n"); + } +#endif /* Libburn_debug_cd_texT */ + + err = d->write(d, write_lba, buf); + if (err == BE_CANCELLED) + { ret = 0; goto ex; } + write_lba += sectors; + sectors = buf->sectors = buf->bytes = 0; + } + } + ret = 1; +ex:; + if (self_made_text_packs) { + if (o->text_packs != NULL) + free(o->text_packs); + o->text_packs = NULL; + o->num_text_packs = 0; + } + BURN_FREE_MEM(subdata); + BURN_FREE_MEM(buf); + d->busy = was_busy; + return ret; +} + + +int burn_write_session(struct burn_write_opts *o, struct burn_session *s) +{ + struct burn_drive *d = o->drive; + int i, ret; + + if (o->write_type == BURN_WRITE_SAO) { + ret = burn_write_leadin_cdtext(o, s, 0); + if (ret <= 0) + goto ex; + } + d->rlba = 0; + for (i = 0; i < s->tracks; i++) { + 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); + return ret; +} + + +/* ts A61218 : outsourced from burn_write_track() */ +int burn_disc_init_track_status(struct burn_write_opts *o, + struct burn_session *s, struct burn_track *t, + 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 B20113 */ + d->progress.indices = t->indices; + d->progress.index = 0; + if (d->progress.indices > 1) + if (t->index[0] == 0x7fffffff) + d->progress.index = 1; + + /* 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[160]; + + 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 += t->pregap2_size; + + 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 < t->pregap2_size; i++) + if (!sector_pregap(o, t->entry->point, + t->entry->control, t->mode)) + { ret = 0; goto ex; } + + /* ts B20113 : Flush buffer to avoid influence pregap + on track counter */ + ret = sector_write_buffer(d, NULL, 0); + if (ret <= 0) + goto ex; + + } else { + o->control = t->entry->control; + d->send_write_parameters(d, s, tnum, 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_2(t, 1) * 2048.0, + (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 A91003 */ + if (nwa < d->nwa) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020173, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Drive tells NWA smaller than last written address", + 0, 0); + d->sync_cache(d); + return 0; + } + d->nwa = nwa; + + } + +/* user data */ + + sectors = burn_track_get_sectors_2(t, 1); + open_ended = burn_track_is_open_ended(t); + + burn_disc_init_track_status(o, s, t, 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); + + /* 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++; + } + + /* ts B20113 : Flush buffer to get buffered bytes assigned to the + track counter */ + ret = sector_write_buffer(d, t, 0); + if (ret <= 0) + goto ex; + + if (t->postgap && o->write_type != BURN_WRITE_TAO) { + for (i = 0; i < t->postgap_size; i++) + if (!sector_postgap(o, t->entry->point, + t->entry->control, t->mode)) + { ret = 0; goto ex; } + ret = sector_write_buffer(d, NULL, 0); + if (ret <= 0) + goto ex; + } + + /* 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 */ + /* ts A91003 : + At least in simulation mode this causes NWA=0 for the + next track. cdrecord does not use CLOSE TRACK at all but + ends the tracks by SYNCHRONIZE CACHE alone. + */ + /* ts A91202 : + Peng Shao reports that his LG GH22LS30 issues an SCSI error + on CLOSE TRACK even in non-dummy mode. So i better give up + this gesture which seems not be needed by any drive. + if (!o->simulate) + 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) != 1) + 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, ret; + + d->cancel = 0; + +#ifdef Libburn_reset_progress_asynC + /* <<< This is now done in async.c */ + /* 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; +#endif /* Libburn_reset_progress_asynC */ + + /* 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->was_feat21h_failure = 0; + if(d->write_opts != NULL) + burn_write_opts_free(d->write_opts); + ret = burn_write_opts_clone(o, &(d->write_opts), 0); + if (ret <= 0) + return ret; + + d->busy = BURN_DRIVE_WRITING; + + return 1; +} + + +static int precheck_write_is_audio(struct burn_disc *disc, int flag) +{ + struct burn_session **sessions; + int num_sessions, i, j; + + sessions = burn_disc_get_sessions(disc, &num_sessions); + for (i = 0; i < num_sessions; i++) + for (j = 0; j < sessions[i]->tracks; j++) + if (!(sessions[i]->track[j]->mode & BURN_AUDIO)) + return 0; + return 1; +} + + +static int precheck_disc_has_cdtext(struct burn_disc *disc, int flag) +{ + struct burn_session **sessions; + int num_sessions, i, ret; + + sessions = burn_disc_get_sessions(disc, &num_sessions); + for (i = 0; i < num_sessions; i++) { + ret = burn_cdtext_from_session(sessions[i], NULL, NULL, 1); + if (ret > 0) + return 1; + } + return 0; +} + + +/* 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 = NULL, *reason_pt; + int no_media = 0, ret, has_cdtext; + + reason_pt= reasons; + reasons[0] = 0; + + if (d->drive_role == 0 || d->drive_role == 4) { + if (d->drive_role == 0) + sprintf(reasons, + "DRIVE: is a virtual placeholder (null-drive)"); + else + sprintf(reasons, "DRIVE: read-only pseudo 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 (o->num_text_packs > 0) { + has_cdtext = 1; + } else { + has_cdtext = precheck_disc_has_cdtext(disc, 0); + } + if (has_cdtext > 0) { + if (d->current_profile == 0x09 || d->current_profile == 0x0a) { + ret = precheck_write_is_audio(disc, 0); + if (ret <= 0) + strcat(reasons, + "CD-TEXT supported only with pure audio CD media, "); + } else { + strcat(reasons, + "CD-TEXT supported only with CD media, "); + } + } + if (d->drive_role == 2 || d->drive_role == 5 || + d->current_profile == 0x1a || d->current_profile == 0x12 || + d->current_profile == 0x43) { + /* DVD+RW , DVD-RAM , BD-RE, 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, "); + if (o->num_text_packs > 0) { + if (o->write_type != BURN_WRITE_SAO) + strcat(reasons, + "CD-TEXT supported only with write type SAO, "); + if (d->start_lba == -2000000000) + strcat(reasons, + "No Lead-in start address known with CD-TEXT, "); + } + } 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 || + d->current_profile == 0x41) { + /* DVD-R* Sequential , DVD+R[/DL] , BD-R, + sequential stdio "drive" */ + if (o->start_byte >= 0) + strcat(reasons, "write start address not supported, "); + } else { +unsuitable_profile:; + msg = calloc(1, 160); + if (msg != NULL && !silent) { + 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); + } + if (msg != NULL) + free(msg); + 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 = NULL; + int ret, lba, nwa; + off_t size; + + BURN_ALLOC_MEM(msg, char, 160); + d->send_write_parameters(d, NULL, -1, 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, 0x00000002, + 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); + +#ifdef Libburn_pioneer_dvr_216d_with_opC + fprintf(stderr, "libburn_DEBUG: Libburn_pioneer_dvr_216d_with_opC : num_opc_tables = %d\n", d->num_opc_tables); + if (d->num_opc_tables <= 0 && !o->simulate) { + fprintf(stderr, "libburn_DEBUG: Libburn_pioneer_dvr_216d_with_opC : performing OPC\n"); + d->perform_opc(d); + fprintf(stderr, "libburn_DEBUG: Libburn_pioneer_dvr_216d_with_opC : done\n"); + } +#endif + +#ifdef Libburn_pioneer_dvr_216d_get_evenT + mmc_get_event(d); +#endif + + if (o->write_type == BURN_WRITE_SAO) { /* DAO */ + size = ((off_t) burn_track_get_sectors_2(s->track[tnum], 1)) + * (off_t) 2048; + + /* Eventually round track size up to write chunk */ + if (o->obs_pad && (size % o->obs)) + size += (off_t) (o->obs - (size % o->obs)); + + 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); + {ret = 0; goto ex;} + } + } + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +/* 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 = NULL; + int ret, lba, nwa; + off_t size; + + BURN_ALLOC_MEM(msg, char, 160); + 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, 0x00000002, + 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])) { + /* Reserve track */ + size = ((off_t) burn_track_get_sectors_2(s->track[tnum], 1)) + * (off_t) 2048; + if (o->obs_pad) { + /* Round track size up to write chunk size */ + /* o->obs should be 32k or 64k already. But 32k + alignment was once performed in d->reserve_track()*/ + if (o->obs % 32768) + o->obs += 32768 - (o->obs % 32768); + if (size % o->obs) + size += (off_t) (o->obs - (size % o->obs)); + } + + /* <<< Only for now until the first DVD+R succeeded */ + if (!o->obs_pad) { + sprintf(msg, "Program error: encountered DVD+R without chunk padding"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00000004, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + {ret = 0; goto ex;} + } + + 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); + {ret = 0; goto ex;} + } + } + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +/* ts A70129 */ +int burn_disc_close_track_dvd_minus_r(struct burn_write_opts *o, 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; + char msg[80]; + + sprintf(msg, "Finalizing %s ...", + d->current_profile_text); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + + if(d->current_profile == 0x41) { /* BD-R */ + /* CLOSE SESSION, 110b, Finalize Disc */ + d->close_track_session(d, 3, 0); /* (3<<1)|0 = 6 */ + } else { + /* CLOSE SESSION, 101b, Finalize with minimal radius */ + d->close_track_session(d, 2, 1); /* (2<<1)|1 = 5 */ + } + + sprintf(msg, "... finalizing %s done ", + d->current_profile_text); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg, 0, 0); + + return 1; +} + + +/* ts A70226 */ +int burn_disc_close_track_dvd_plus_r(struct burn_write_opts *o, + 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; +} + + +/* <<< +#define Libburn_simplified_dvd_chunk_transactioN 1 +*/ + +#ifdef Libburn_simplified_dvd_chunk_transactioN + +/* ts A91114 : EXPERIMENTAL, NOT COMPLETELY IMPLEMENTED + + Simplified data transmission for DVD. libburn via GNU/Linux USB is 30 % + slower than growisofs or cdrecord when transmitting 32 KB chunks. + With 64 KB chunks it is 20% faster than the competitors. + No heavy CPU load is visible but there might be subtle race conditions in + the USB driver which work better with shorter time gaps between WRITE + commands. + + Insight: It is actually about the interference of track source reading + with SCSI writing via USB. growisofs reads with O_DIRECT into a + mmap()ed buffer. When doing the same, libburn with 32 KB chunks + reaches similar write speed. + On the other hand, 64 KB chunks are 20% faster than that and + are not improved by reading O_DIRECT. + + O_DIRECT is a property of the input fd of struct burn_source. + It can only be done with properly aligned memory and with aligned + read size. Alignment size is file system system specific. + System call + mmap(NULL, (size_t) buffer_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, (off_t) 0); + is supposed to allocate a properly aligned buffer. + 64 KB is supposed to be a safe size. + Actually mmap() seems to be the main cause for a positive effect + of O_DIRECT. + + This simplified transmission function did not bring visible benefit. + So for now it is not worth to teach it all applicable details of old + CD sector oriented transmission. + + @return 1= ok, go on , 2= no input with track->open_ended = nothing written + <= 0 = error +*/ +static int transact_dvd_chunk(struct burn_write_opts *opts, + struct burn_track *track) +{ + int curr = 0, valid, err; + struct burn_drive *d = opts->drive; + struct buffer *out = d->buffer; + unsigned char *data = out->data; + +#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 | O_BINARY, + S_IRUSR | S_IWUSR); +#endif /* Libburn_log_in_and_out_streaM */ + + + /* Read a chunk full of data */ + + /* ??? Do we have offset padding ? >>> First produce offset padding */; + + /* <<<< */ + if (0 && !track->eos) { + for (curr = 0; curr < opts->obs; curr += 2048) { + if (track->source->read != NULL) + valid = track->source->read(track->source, + data + curr, 2048); + else + valid = track->source->read_xt(track->source, + data + curr, 2048); + if (valid <= 0) { + track->eos = 1; + break; + } + track->sourcecount += valid; + +#ifdef Libburn_log_in_and_out_streaM + if(tee_fd!=-1 && valid>0) { + write(tee_fd, data + curr, valid); + } +#endif /* Libburn_log_in_and_out_streaM */ + + } + } else if (!track->eos){ + valid = track->source->read(track->source, data, opts->obs); + if (valid <= 0) { + track->eos = 1; + } else { + track->sourcecount += valid; + curr = valid; + +#ifdef Libburn_log_in_and_out_streaM + if(tee_fd!=-1 && valid>0) { + write(tee_fd, data, valid); + } +#endif /* Libburn_log_in_and_out_streaM */ + } + } + if (curr == 0 && track->open_ended) { + + /* >>> allow tail padding */; + + return 2; + } + if (curr < opts->obs) + memset(data + curr , 0, opts->obs - curr); + + /* Write chunk */ + out->bytes = opts->obs; + out->sectors = out->bytes / 2048; + err = d->write(d, d->nwa, out); + if (err == BE_CANCELLED) + return 0; + track->writecount += out->bytes; + track->written_sectors += out->sectors; + d->progress.buffered_bytes += out->bytes; + d->nwa += out->sectors; + out->bytes = 0; + out->sectors = 0; + + return 1; +} + +#endif /* Libburn_simplified_dvd_chunk_transactioN */ + + +/* ts A61218 - A81208 */ +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, track_open = 0; + int first_buf_cap = 0, further_cap = 0, buf_cap_step = 1024; + + /* 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; + /* Pioneer DVR-216D rev 1.09 hates multiple buffer inquiries + before the drive buffer is full. + */ + first_buf_cap = 0; + further_cap = -1; + } 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; + } else if (d->current_profile == 0x41) { + /* BD-R SRM */ + ret = burn_disc_open_track_dvd_plus_r(o, s, tnum); + if (ret <= 0) + goto ex; + } + track_open = 1; + + sectors = burn_track_get_sectors_2(t, 1); + open_ended = burn_track_is_open_ended(t); + + /* (offset padding is done within sector_data()) */ + + burn_disc_init_track_status(o, s, t, tnum, sectors); + for (i = 0; open_ended || i < sectors; i++) { + + /* From time to time inquire drive buffer */ + /* ts A91110: Eventually avoid to do this more than once + before the drive buffer is full. See above DVD- + */ + if (i == first_buf_cap || + ((i % buf_cap_step) == 0 && + (i >= further_cap || further_cap < 0))) { + d->read_buffer_capacity(d); + if (further_cap < 0) + further_cap = + d->progress.buffer_capacity / 2048 + 128; + } + +#ifdef Libburn_simplified_dvd_chunk_transactioN + + ret = transact_dvd_chunk(o, t); + if (ret <= 0) + {ret = 0; goto ex;} + i += o->obs / 2048 - 1; + d->progress.sector += o->obs / 2048 - 1; +#else + /* transact a (CD sized) sector */ + if (!sector_data(o, t, 0)) + { ret = 0; goto ex; } +#endif + + 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, 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, tnum, + is_last_track); + if (ret <= 0) + goto ex; + } else if (d->current_profile == 0x41) { + /* BD-R SRM */ + ret = burn_disc_close_track_dvd_plus_r(o, tnum, + is_last_track); + if (ret <= 0) + goto ex; + } + ret = 1; +ex:; + if (d->cancel) + burn_source_cancel(t->source); + if (track_open && !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_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, multi_mem; + struct burn_drive *d = o->drive; + + /* ts A90108 */ + if (d->current_profile == 0x41 && d->status == BURN_DISC_APPENDABLE && + d->state_of_last_session == 1) { + /* last session on BD-R is still open */; + + /* BR-R were not closed by libburn-0.6.0.pl00 if o->multi==0. + This leads to an unreadable, but recoverable) media state. + Technically they are appendable although the last session + is not readable. + + By default the open session gets closed here before the new + session is written. E.g. after writing a small dummy seesion + number 2 one can read session 1 and write session 3 which + points to data of session 1. + + For the case that no media with 3 sessions is desired it is + possible to activate the following coarse single-session + closing code: + No new session will be written but calling programs will + report success. Quite misleading. + Activate only if really needed by + # define Libburn_bug_A90108_close_disC yes + */ + + +#ifdef Libburn_bug_A90108_close_disC + + /* Close open session and media. + That was the goal of the failed run which led to the + unreadable (but recoverable) media state. + + It is not easy to implement a general close function for + all media types. Therefore this pseudo write code is under + control of #ifdef. + */ + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020171, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, + "Closing BD-R with accidently open session", + 0, 0); + d->close_track_session(d, 3, 0); /* CLOSE SESSION, 110b */ + d->state_of_last_session = 3; /* mark as complete session */ + d->status = BURN_DISC_FULL; + sleep(3); /* The caller might need time to arrange itself */ + return 1; + +#else /* Libburn_bug_A90108_close_disC */ + + /* This is the default mode. + */ + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020170, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, + "Closing open session before writing new one", + 0, 0); + d->close_track_session(d, 1, 0); /* CLOSE SESSION, 010b */ + d->state_of_last_session = 3; /* mark as complete session */ + +#endif /* ! Libburn_bug_A90108_close_disC */ + + } + + 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 */ + /* If feature 21h failed on write 0: do not close session */ + if (d->was_feat21h_failure != 2) { + multi_mem = o->multi; + if (!is_last_session) + o->multi = 1; + ret = burn_disc_close_session_dvd_minus_r(o); + o->multi = multi_mem; + if (ret <= 0) + return 0; + } + } else if (d->current_profile == 0x12 || d->current_profile == 0x43) { + /* DVD-RAM , BD-RE */ + /* ??? 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 */; + } else if (d->current_profile == 0x41) { + /* BD-R SRM 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; + + 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; + } + + /* >>> 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[60]; + 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, NULL, -1, 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 - A70415 */ +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 = NULL; + + BURN_ALLOC_MEM(msg, char, 160); + d->needs_close_session = 0; + + /* buffer flush trigger for sector.c:get_sector() */ + o->obs = Libburn_dvd_obS; + + if (d->current_profile == 0x1a || d->current_profile == 0x12 || + d->current_profile == 0x43) { + /* DVD+RW , DVD-RAM , BD-RE */ + 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; + } + 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); + } + if (o->obs_pad < 2) + o->obs_pad = 0; /* no filling-up of last 32k buffer */ + if (d->current_profile == 0x43) /* BD-RE */ + o->obs = Libburn_bd_re_obS; + if (d->do_stream_recording) { + if (o->obs_pad < 2) + o->obs_pad = 1; + if (d->current_profile == 0x43) /* BD-RE */ + o->obs = Libburn_bd_streamed_obS; + } + + } 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); + } + /* Whether to fill-up last 32k buffer of track. */ + if (o->obs_pad < 2) + o->obs_pad = (o->write_type != BURN_WRITE_SAO); + 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; + } + + } else if (d->current_profile == 0x1b || d->current_profile == 0x2b || + d->current_profile == 0x41) { + /* DVD+R , DVD+R/DL , BD-R SRM */ + + /* >>> ts A81208 : with BD-R set o->obs to 64 kB ? */ + + 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 %s", + d->current_profile == 0x41 ? "BD-R" : "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 */ + if (o->obs_pad < 2) + o->obs_pad = 1; /* fill-up track's last obs buffer */ + if (d->current_profile == 0x41) /* BD-R */ + o->obs = Libburn_bd_r_obS; + if (d->do_stream_recording) { + if (d->current_profile == 0x41) /* BD-R */ + o->obs = Libburn_bd_streamed_obS; + } + } + +#ifdef Libburn_dvd_obs_default_64K + o->obs = 64 * 1024; +#endif + + /* <<< test only : Does this increase effective speed with USB ? + ts A90801 : 64kB: speed with 16x DVD-R is 12 rather than 8 + 128kB: glibc complains about double free + With BURN_OS_TRANSPORT_BUFFER_SIZE + enlarged to 128 MB, the first WRITE fails + with an i/o error. + o->obs = 64 * 1024; + */ + + if (o->dvd_obs_override >= 32 * 1024) + o->obs = o->dvd_obs_override; + + if (o->obs > BUFFER_SIZE) { + sprintf(msg, "Chosen write chunk size %d exceeds system dependent buffer size", o->obs); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00000002, LIBDAX_MSGS_SEV_DEBUG, + LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); + o->obs = 32 * 1024; /* This size is required to work */ + } + + if (d->do_stream_recording && + (d->current_profile == 0x43 || d->current_profile == 0x41) && + o->obs < Libburn_bd_streamed_obS) { + /* LG GGW-H20 writes junk with stream recording and obs=32k */ + sprintf(msg, + "Stream recording disabled because of small output buffer"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020176, LIBDAX_MSGS_SEV_NOTE, + LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); + d->do_stream_recording = 0; + } + + sprintf(msg, "dvd/bd Profile= %2.2Xh , obs= %d , obs_pad= %d", + d->current_profile, o->obs, o->obs_pad); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); + + for (i = 0; i < disc->sessions; i++) { + /* update progress */ + d->progress.session = i; + d->progress.tracks = disc->session[i]->tracks; + + 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, 0); + burn_drive_inquire_media(d); + + if (d->current_profile == 0x41 && d->complete_sessions >= 300) { + sprintf(msg, "Sequential BD-R media now contains %d sessions. It is likely to soon fail writing.", d->complete_sessions); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002017b, LIBDAX_MSGS_SEV_WARNING, + LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); + } + BURN_FREE_MEM(msg); + return ret; +early_failure:; + BURN_FREE_MEM(msg); + return 0; +} + + +/* ts A70904 */ +int burn_stdio_open_write(struct burn_drive *d, off_t start_byte, + int sector_size, int flag) +{ + +/* We normally need _LARGEFILE64_SOURCE defined by the build system. + Nevertheless the system might use large address integers by default. +*/ +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + + int fd = -1; + int mode = O_RDWR | O_CREAT | O_LARGEFILE; + char msg[60]; + off_t lseek_res; + + if(d->drive_role == 4) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020181, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Pseudo-drive is a read-only file. Cannot write.", + 0, 0); + return 0; + } + if (d->drive_role == 5 || d->drive_role == 3) + mode = O_WRONLY | O_CREAT | O_LARGEFILE; + 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 | O_BINARY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + 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 || d->drive_role == 5) { + lseek_res = lseek(fd, start_byte, SEEK_SET); + if (lseek_res == -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) +{ + int ret = 0; + char *msg = NULL; + int todo, done, retries; + + if (d->cancel || count <= 0) + return 0; + + todo = count; + done = 0; + for (retries = 0; todo > 0 && retries <= Libburn_stdio_write_retrieS; + retries++) { +/* +fprintf(stderr, "libburn_DEBUG: write(%d, %lX, %d)\n", + fd, (unsigned long) buf, count); +*/ + ret = write(fd, buf + done, todo); + if (ret < 0) + break; + done += ret; + todo -= ret; + } + if (done != count) { + BURN_ALLOC_MEM(msg, char, 160); + + sprintf(msg, "Cannot write desired amount of %d bytes.", count); + if (retries > 1) + sprintf(msg + strlen(msg), " Did %d retries. Last", + retries - 1); + sprintf(msg + strlen(msg), " write(2) returned %d.", ret); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020148, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, errno, 0); + d->cancel = 1; + ret = 0; goto ex; + } +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +/* 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) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002017d, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Invalid file descriptor with stdio pseudo-drive", + 0, 0); + 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) + bit1= do fsync(2) unconditionally +*/ +int burn_stdio_sync_cache(int fd, struct burn_drive *d, int flag) +{ + int ret, do_fsync; + char *msg = NULL; + + if (fd < 0) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002017d, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Invalid file descriptor with stdio pseudo-drive", + 0, 0); + d->cancel = 1; + return 0; + } + d->needs_sync_cache = 0; + do_fsync = 0; + if (flag & 2) + do_fsync = 1; + else if (d->write_opts != NULL) + do_fsync = (d->write_opts->stdio_fsync_size >= 0); + if (do_fsync) { + if (!(flag & 1)) + libdax_msgs_submit(libdax_messenger, -1, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + "syncing cache (stdio fsync)", 0, 0); + ret = fsync(fd); + } else { + ret = 0; + } + if (ret != 0 && errno == EIO) { + BURN_ALLOC_MEM(msg, char, 160); + + sprintf(msg, + "Cannot write desired amount of data. fsync(2) returned %d.", + ret); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020148, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, errno, 0); + d->cancel = 1; + return 0; + } + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +/* 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 = 16 * 2048, ret, sectors; + struct burn_track *t = s->track[tnum]; + struct burn_drive *d = o->drive; + char *buf = NULL; + int i, prev_sync_sector = 0; + struct buffer *out = d->buffer; + struct timeval prev_time; + + BURN_ALLOC_MEM(buf, char, bufsize); + + sectors = burn_track_get_sectors_2(t, 1); + burn_disc_init_track_status(o, s, t, tnum, sectors); + open_ended = burn_track_is_open_ended(t); + + t->end_on_premature_eoi = (o->write_type == BURN_WRITE_TAO); + + /* 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 (open_ended || t->end_on_premature_eoi) { + if (burn_track_is_data_done(t)) + break; + } + d->progress.sector++; + /* Flush to disk from time to time */ + if (d->progress.sector - prev_sync_sector >= + o->stdio_fsync_size && o->stdio_fsync_size > 0) { + prev_sync_sector = d->progress.sector; + if (!o->simulate) + burn_stdio_sync_cache(d->stdio_fd, d, 1); + } + if ((d->progress.sector % 512) == 0) + 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); + if (t->end_on_premature_eoi == 2) + d->cancel = 1; + BURN_FREE_MEM(buf); + 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; + if (o->obs_pad < 2) + 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 */ + + /* >>> ??? ts B11004 : Why this eagerness to close and open ? */ + + /* open target file */ + if (d->stdio_fd >= 0) + close(d->stdio_fd); + if (d->drive_role == 5 && d->status == BURN_DISC_APPENDABLE && + o->start_byte < 0) + o->start_byte = d->role_5_nwa * 2048; + 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:; + + /* >>> ??? ts B11004 : Why this eagerness to close ? */ + + if (d->stdio_fd >= 0) + close(d->stdio_fd); + d->stdio_fd = -1; + + /* update pseudo-media state records by re-grabbing */ + burn_drive_mark_unready(d, 8); + burn_drive_grab_stdio(d, 1); + + 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 *buffer_mem = o->drive->buffer; + struct burn_session *s; + struct burn_track *lt, *t; + int first = 1, i, ret, lba, nwa = 0, multi_mem; + 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 */ + + /* ts A80412 , A90227 */ + d->do_stream_recording = !!o->do_stream_recording; + if (o->do_stream_recording >= 16) + d->stream_recording_start = o->do_stream_recording; + else + d->stream_recording_start = 0; + + /* ts A91122 : Get buffer suitable for sources made by + burn_os_open_track_src() */ + d->buffer = burn_os_alloc_buffer(sizeof(struct buffer), 0); + if (d->buffer == NULL) + goto fail_wo_sync; + +/* >>> ts A90321 + + memset(d->buffer, 0, sizeof(struct buffer)); + +fprintf(stderr, "libburn_DEBUG: d->buffer = %lX , size = %d\n", + (unsigned long) d->buffer, (int) sizeof(struct buffer)); + +calloc() seems not to have the desired effect. valgrind warns: +==18251== Syscall param write(buf) points to uninitialised byte(s) +==18251== at 0x5071DEB: (within /lib64/libpthread-2.5.so) +==18251== by 0x4723FA: burn_stdio_write (write.c:1850) +==18251== by 0x4725DC: burn_stdio_mmc_write (write.c:1894) +==18251== by 0x483B7A: get_sector (sector.c:229) +==18251== by 0x484F11: sector_data (sector.c:639) +==18251== by 0x4729FE: burn_stdio_write_track (write.c:2012) +==18251== by 0x472CF4: burn_stdio_write_sync (write.c:2072) +==18251== by 0x472E8D: burn_disc_write_sync (write.c:2125) <<< we are here +==18251== by 0x460254: write_disc_worker_func (async.c:514) +==18251== by 0x506B09D: start_thread (in /lib64/libpthread-2.5.so) +==18251== by 0x55484CC: clone (in /lib64/libc-2.5.so) +*/ + + d->rlba = -150; + d->toc_temp = 9; + + if(d->drive_role == 4) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020181, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Pseudo-drive is a read-only file. Cannot write.", + 0, 0); + goto fail_wo_sync; + } + /* ts A70904 */ + if (d->drive_role != 1) { + ret = burn_stdio_write_sync(o, disc); + if (ret <= 0) + goto fail_wo_sync; + goto ex; + } + /* ts A61218 */ + if (! d->current_is_cd_profile) { + ret = burn_dvd_write_sync(o, disc); + if (ret <= 0) + goto fail_wo_sync; + goto ex; + } + + /* ts A70521 : GNU/Linux 2.4 USB audio fails with 64 kiB */ + /* ts A80414 : might need 64 kiB for BD-RE streaming */ + /* buffer flush trigger for sector.c:get_sector() */ + o->obs = Libburn_cd_obS; + + sprintf(msg, "cd Profile= %2.2Xh , obs= %d , obs_pad= %d", + d->current_profile, o->obs, o->obs_pad); + libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); + + /* 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); + } + } + } + +/* 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 { + if (disc->sessions > 0) + s = disc->session[0]; + else + s = NULL; + d->send_write_parameters(d, s, -1, 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_wo_sync; + +#ifdef Libburn_write_with_function_print_cuE + print_cue(sheet); + /* goto fail_wo_sync; */ +#endif /* Libburn_write_with_function_print_cuE */ + + ret = 1; + if (o->write_type == BURN_WRITE_SAO) + ret = d->send_cue_sheet(d, sheet); + if (sheet->data != NULL) + free(sheet->data); + free(sheet); + if (ret <= 0) + goto fail_wo_sync; + + /* --- From here on, final sync is needed. --- */ + + 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, 0x00000002, + 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; + } + } + multi_mem = o->multi; + if(i < disc->sessions - 1) + o->multi = 1; + ret = burn_write_session(o, disc->session[i]); + o->multi = multi_mem; + if (!ret) + 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, 0); + burn_drive_inquire_media(d); + + /* ts A61012 : This return was traditionally missing. I suspect this + to have caused Cdrskin_eject() failures */ + goto ex; + +fail: + d->sync_cache(d); +fail_wo_sync:; + usleep(500001); /* ts A61222: to avoid a warning from remove_worker()*/ + 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; */ +ex:; + d->do_stream_recording = 0; + if (d->buffer != NULL) + burn_os_free_buffer((char *) d->buffer, + sizeof(struct buffer), 0); + d->buffer = buffer_mem; + if (d->write_opts != NULL) { + burn_write_opts_free(d->write_opts); + d->write_opts = NULL; + } + return; +} + +/* 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; + int do_close = 0, getfl_ret; + char msg[81], *rpt; + struct buffer *buf = NULL, *buffer_mem = d->buffer; + + BURN_ALLOC_MEM(buf, struct buffer, 1); + 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); + {ret = 0; goto ex;} + } + 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); + {ret = 0; goto ex;} + } + if(d->drive_role == 4) { + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020181, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Pseudo-drive is a read-only file. Cannot write.", + 0, 0); + {ret = 0; goto ex;} + } + + if(d->drive_role == 2 || d->drive_role == 5) + 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 (d->current_profile == 0x43) /* BD-RE */ + 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); + {ret = 0; goto ex;} + } + 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); + {ret = 0; goto ex;} + } + 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); + {ret = 0; goto ex;} + } + 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); + {ret = 0; goto ex;} + } + if (d->drive_role != 1) { + if (d->stdio_fd >= 0) { + /* Avoid to have a read-only fd open */ + getfl_ret = fcntl(d->stdio_fd, F_GETFL); + if (((O_RDWR | O_WRONLY | O_RDONLY) & getfl_ret) == + O_RDONLY) { + close(d->stdio_fd); + d->stdio_fd = -1; + } + } + if (d->stdio_fd >= 0) { + /* Avoid to have two fds open */ + fd = d->stdio_fd; + } else { + fd = burn_stdio_open_write(d, byte_address, 2048, 0); + if (fd == -1) + {ret = 0; goto ex;} + do_close = 1; + } + } + d->cancel = 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 && do_close) + close(fd); + {ret = -(start * 2048 - byte_address); goto ex;} + } + } + + 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, 2); + d->needs_sync_cache = 0; + } + + if(fd >= 0 && do_close) + close(fd); + d->buffer = buffer_mem; + d->busy = BURN_DRIVE_IDLE; + ret = 1; +ex: + BURN_FREE_MEM(buf); + return ret; +} + + +/* ts B10527 */ +/* @param bit0= force close, even if no damage was seen +*/ +int burn_disc_close_damaged(struct burn_write_opts *o, int flag) +{ + struct burn_drive *d; + int ret; + enum burn_drive_status busy; + + d = o->drive; + busy = d->busy; + + if (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 damaged session", + 0, 0); + {ret = 0; goto ex;} + } + if (!((d->next_track_damaged & 1) || (flag & 1))) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020187, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, + "Track not marked as damaged. No action taken.", + 0, 0); + {ret = 0; goto ex;} + } + d->busy = BURN_DRIVE_WRITING; + + if (d->current_profile == 0x09 || d->current_profile == 0x0a) { + /* Close CD track and session */ + o->write_type = BURN_WRITE_TAO; /* no action without TAO */ + + /* Send mode page 5 */; + d->send_write_parameters(d, NULL, -1, o); + + ret = burn_write_close_session(o); + if (ret <= 0) + goto ex; + + } else if(d->current_profile == 0x11 || d->current_profile == 0x14) { + /* Close DVD-R[W] track and session */ + o->write_type = BURN_WRITE_TAO; /* no action without TAO */ + + /* Send mode page 5 */; + d->send_write_parameters(d, NULL, -1, o); + + ret = burn_disc_close_track_dvd_minus_r(o, 0); + if (ret <= 0) + goto ex; + ret = burn_disc_close_session_dvd_minus_r(o); + if (ret <= 0) + goto ex; + + } else if(d->current_profile == 0x1b || d->current_profile == 0x2b) { + /* Close DVD+R track and session */ + ret = burn_disc_close_track_dvd_plus_r(o, d->last_track_no, 1); + if (ret <= 0) + goto ex; + + } else if(d->current_profile == 0x41) { + /* Close BD-R track and session */ + ret = burn_disc_close_track_dvd_plus_r(o, d->last_track_no, 1); + if (ret <= 0) + goto ex; + + } else { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x00020188, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Cannot close damaged track on given media type", + 0, 0); + {ret = 0; goto ex;} + + } + ret = 1; +ex:; + d->busy = busy; + /* Record with drive that repair was attempted */ + d->next_track_damaged &= ~1; + return ret; +} + + diff --git a/trunk/libburn/write.h b/trunk/libburn/write.h new file mode 100644 index 0000000..75ffc53 --- /dev/null +++ b/trunk/libburn/write.h @@ -0,0 +1,61 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens + Copyright (c) 2006 - 2010 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL version 2 or later. +*/ + + +#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); + +/* @param flag bit0= repair checksum + bit1= repair checksum if all pack CRCs are 0 + @return 0= no mismatch , >0 number of unrepaired mismatches + <0 number of repaired mismatches +*/ +int burn_cdtext_crc_mismatches(unsigned char *packs, int num_packs, int flag); + + + +/* 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/trunk/libcevap/cgen.c b/trunk/libcevap/cgen.c new file mode 100644 index 0000000..81718f7 --- /dev/null +++ b/trunk/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/trunk/libcevap/cgen.h b/trunk/libcevap/cgen.h new file mode 100644 index 0000000..5b464a0 --- /dev/null +++ b/trunk/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/trunk/libcevap/cgen.txt b/trunk/libcevap/cgen.txt new file mode 100644 index 0000000..d260af9 --- /dev/null +++ b/trunk/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/trunk/libcevap/ctyp.c b/trunk/libcevap/ctyp.c new file mode 100644 index 0000000..2b27575 --- /dev/null +++ b/trunk/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/trunk/libcevap/ctyp.h b/trunk/libcevap/ctyp.h new file mode 100644 index 0000000..13657b0 --- /dev/null +++ b/trunk/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/trunk/libcevap/extract_cgen_input.sh b/trunk/libcevap/extract_cgen_input.sh new file mode 100755 index 0000000..3ca096f --- /dev/null +++ b/trunk/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/trunk/libcevap/libcevap_gen.sh b/trunk/libcevap/libcevap_gen.sh new file mode 100755 index 0000000..48ed786 --- /dev/null +++ b/trunk/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/trunk/libcevap/libdax_equip.gif b/trunk/libcevap/libdax_equip.gif new file mode 100644 index 0000000..3df86ae Binary files /dev/null and b/trunk/libcevap/libdax_equip.gif differ diff --git a/trunk/libcevap/libdax_job.gif b/trunk/libcevap/libdax_job.gif new file mode 100644 index 0000000..8138ad2 Binary files /dev/null and b/trunk/libcevap/libdax_job.gif differ diff --git a/trunk/libcevap/libdax_model.txt b/trunk/libcevap/libdax_model.txt new file mode 100644 index 0000000..5b5c3c7 --- /dev/null +++ b/trunk/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/trunk/libcevap/libdax_overview.gif b/trunk/libcevap/libdax_overview.gif new file mode 100644 index 0000000..f4e5715 Binary files /dev/null and b/trunk/libcevap/libdax_overview.gif differ diff --git a/trunk/libcevap/main.c b/trunk/libcevap/main.c new file mode 100644 index 0000000..b537b5f --- /dev/null +++ b/trunk/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/trunk/libcevap/smem.c b/trunk/libcevap/smem.c new file mode 100644 index 0000000..94aed82 --- /dev/null +++ b/trunk/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/trunk/libcevap/smem.h b/trunk/libcevap/smem.h new file mode 100644 index 0000000..28b5e98 --- /dev/null +++ b/trunk/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/trunk/test/Makefile b/trunk/test/Makefile new file mode 100644 index 0000000..062350d --- /dev/null +++ b/trunk/test/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/trunk/test/dewav.c b/trunk/test/dewav.c new file mode 100644 index 0000000..f84fdf2 --- /dev/null +++ b/trunk/test/dewav.c @@ -0,0 +1,216 @@ + +/* 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; + +/* The API for .wav extraction */ +#define LIBDAX_AUDIOXTR_H_PUBLIC 1 +#include "../libburn/libdax_audioxtr.h" + +#else /* Dewav_without_libburN */ + +/* This build environment uses libdax_msgs and libdax_audioxtr via libburn */ +/* Thus the API header of libburn */ +#include "../libburn/libburn.h" + +#endif /* ! Dewav_without_libburN */ + + + +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/trunk/test/fake_au.c b/trunk/test/fake_au.c new file mode 100644 index 0000000..01d7a41 --- /dev/null +++ b/trunk/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/trunk/test/libburner.c b/trunk/test/libburner.c new file mode 100644 index 0000000..a6db21a --- /dev/null +++ b/trunk/test/libburner.c @@ -0,0 +1,825 @@ + +/* test/libburner.c , API illustration of burning data or audio tracks to CD */ +/* Copyright (C) 2005 - 2011 Thomas Schmitt <scdbackup@gmx.net> */ +/* Provided under GPL, see also "License and copyright aspects" at file end */ + + +/** Overview + + libburner is a minimal demo application for the library libburn as provided + on http://libburnia-project.org . It can list the available devices, can + blank a CD-RW or DVD-RW, can format DVD-RW and BD, can burn to CD-R, + CD-RW, DVD-R, DVD+R, DVD+R/DL, DVD+RW, DVD-RW, DVD-RAM, BD-R, BD-RE. + Not supported yet: DVD-R/DL. + + 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("libburner : ", NULL, 0x0) + 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 twoi + 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 or DVD-RW as shown in + libburner_blank_disc() + or you can format a DVD-RW to profile "Restricted Overwrite" (needed once) + or an unused BD to default size with spare blocks + libburner_format() + With the aquired drive you can burn to CD, DVD, BD. See + libburner_payload() + + These three functions switch temporarily to a non-fatal signal handler + while they are waiting for the drive to become idle again: + burn_set_signal_handling("libburner : ", NULL, 0x30) + After the waiting loop ended, they check for eventual abort events by + burn_is_aborting(0) + The 0x30 handler will eventually execute + burn_abort() + but not wait for the drive to become idle and not call exit(). + This is needed because the worker threads might block as long as the signal + handler has not returned. The 0x0 handler would wait for them to finish. + Take this into respect when implementing own signal handlers. + + When everything is done, main() releases the drive and shuts down libburn: + burn_drive_release(); + burn_finish() + + Applications must use 64 bit off_t. E.g. by defining + #define _LARGEFILE_SOURCE + #define _FILE_OFFSET_BITS 64 + or take special precautions to interface with the library by 64 bit integers + where libburn/libburn.h prescribes off_t. + This program gets fed with appropriate settings externally by libburn's + autotools generated build system. +*/ + + +/** 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 works on Linux systems with kernel 2.4 or 2.6, FreeBSD, Solaris */ +#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 || *driveno <= 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 < (int) 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 ((int) 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 < (int) 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 or unformatted DVD-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; + } + /* Switch to asynchronous signal handling for the time of waiting */ + burn_set_signal_handling("libburner : ", NULL, 0x30); + + 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); + } + if (burn_is_aborting(0) > 0) + return -1; + /* Back to synchronous handling */ + burn_set_signal_handling("libburner : ", NULL, 0x0); + printf("Done\n"); + return 1; +} + + +/** Formats unformatted DVD-RW 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. + + Formats unformatted BD-RE to default size. This will allocate some + reserve space, test for bad blocks and make the media ready for writing. + Expect a very long run time. + + Formats unformatted blank BD-R to hold a default amount of spare blocks + for eventual mishaps during writing. If BD-R get written without being + formatted, then they get no such reserve and will burn at full speed. +*/ +int libburner_format(struct burn_drive *drive) +{ + struct burn_progress p; + double percent = 1.0; + int ret, status, num_formats, format_flag= 0; + off_t size = 0; + unsigned dummy; + enum burn_disc_status disc_state; + + if (current_profile == 0x13) { + fprintf(stderr, "IDLE: DVD-RW media is already formatted\n"); + return 2; + } else if (current_profile == 0x41 || current_profile == 0x43) { + disc_state = burn_disc_get_status(drive); + if (disc_state != BURN_DISC_BLANK && current_profile == 0x41) { + fprintf(stderr, + "FATAL: BD-R is not blank. Cannot format.\n"); + return 0; + } + ret = burn_disc_get_formats(drive, &status, &size, &dummy, + &num_formats); + if (ret > 0 && status != BURN_FORMAT_IS_UNFORMATTED) { + fprintf(stderr, + "IDLE: BD media is already formatted\n"); + return 2; + } + size = 0; /* does not really matter */ + format_flag = 3<<1; /* format to default size, no quick */ + } else if (current_profile == 0x14) { /* sequential DVD-RW */ + size = 128 * 1024 * 1024; + format_flag = 1; /* write initial 128 MiB */ + } else { + fprintf(stderr, "FATAL: Can only format DVD-RW or BD\n"); + return 0; + } + burn_set_signal_handling("libburner : ", NULL, 0x30); + + printf("Beginning to format media.\n"); + burn_disc_format(drive, size, format_flag); + + 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); + } + if (burn_is_aborting(0) > 0) + return -1; + burn_set_signal_handling("libburner : ", NULL, 0x0); + burn_disc_get_profile(drive_list[0].drive, ¤t_profile, + current_profile_name); + if (current_profile == 0x14 || current_profile == 0x13) + printf("Media type now: %4.4xh \"%s\"\n", + current_profile, current_profile_name); + if (current_profile == 0x14) { + 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) { + fprintf(stderr, + "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; + } + burn_set_signal_handling("libburner : ", NULL, 0x30); + + 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 (burn_is_aborting(0) > 0) + return -1; + if (multi && current_profile != 0x1a && current_profile != 0x13 && + current_profile != 0x12 && current_profile != 0x43) + /* not with DVD+RW, formatted DVD-RW, DVD-RAM, BD-RE */ + 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")) || + (!strcmp(argv[i], "--format"))) { + 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] [--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, BD-RE or BD-R:\n"); + printf(" %s --drive /dev/hdc --format\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; + + /* A warning to programmers who start their own projekt from here. */ + if (sizeof(off_t) != 8) { + fprintf(stderr, + "\nFATAL: Compile time misconfiguration. off_t is not 64 bit.\n\n"); + exit(39); + } + + 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 synchronous signal handler which eventually will try to + properly shutdown drive and library on aborting events. */ + burn_set_signal_handling("libburner : ", NULL, 0x0); + + /** 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(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:; + if (burn_is_aborting(0) > 0) { + burn_abort(4400, burn_abort_pacifier, "libburner : "); + fprintf(stderr,"\nlibburner run aborted\n"); + exit(1); + } + /* 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. + +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/trunk/test/offst_source.c b/trunk/test/offst_source.c new file mode 100644 index 0000000..d4011d9 --- /dev/null +++ b/trunk/test/offst_source.c @@ -0,0 +1,134 @@ + +/* + cc -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS -g -o test/offst_source test/offst_source.c -lburn +*/ + +#include "../libburn/libburn.h" + +/* Just everything from test/libburner.c */ +#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> + + +static int create_original(struct burn_source **original, char *path, int flag) +{ + printf("create_original: path='%s'\n", path); + *original = burn_file_source_new(path, NULL); + if (*original == NULL) + return 0; + return 1; +} + + +static int set_up_offst_sources(struct burn_source *original, + struct burn_source *offsetters[], + int count, int flag) +{ + int i; + off_t start = 3, size = 10, gap = 7; + + for (i = 0; i < count; i++) { + offsetters[i] = burn_offst_source_new(original, + i > 0 ? offsetters[i - 1] : NULL, + start, size, 0); + if (offsetters[i] == NULL) + return 0; + printf("set_up_offst_sources: idx=%d, start=%d\n", + i, (int) start); + start += size + gap; + } + return 1; +} + + +static int consume_source(struct burn_source *src, int flag) +{ + int ret, count = 0; + unsigned char buf[1]; + + while (1) { + ret = src->read_xt(src, buf, 1); + if (ret < 0) { + printf("\n"); + fprintf(stderr, "consume_source: count=%d, ret=%d\n", + count, ret); + return 0; + } + if (ret == 0) + break; + printf("%u ", buf[0]); + count++; + } + printf(" count=%d\n", count); + return 1; +} + + +static int consume_all_sources(struct burn_source *offsetters[], + int count, int flag) +{ + int i, ret; + + for (i = 0; i < count; i++) { + printf("consume_source: idx=%d\n", i); + ret = consume_source(offsetters[i], 0); + if (ret <= 0) + return ret; + } + return 1; +} + + +static int free_all_sources(struct burn_source *original, + struct burn_source *offsetters[], + int count, int flag) +{ + int i; + + for (i = 0; i < count; i++) + burn_source_free(offsetters[i]); + burn_source_free(original); + return 1; +} + + +int main(int argc, char **argv) +{ + int ret; + char *path = "./COPYRIGHT"; + struct burn_source *original = NULL, *offsetters[4]; + + if (argc > 1) + path = argv[1]; + + if (burn_initialize() == 0) + exit(1); + + ret = create_original(&original, path, 0); + if (ret <= 0) + exit(2); + + ret = set_up_offst_sources(original, offsetters, 4, 0); + if (ret <= 0) + exit(3); + + ret = consume_all_sources(offsetters, 4, 0); + if (ret <= 0) + exit(4); + + ret = free_all_sources(original, offsetters, 4, 0); + if (ret <= 0) + exit(5); + + burn_finish(); + exit(0); +} + diff --git a/trunk/test/open-cd-excl.c b/trunk/test/open-cd-excl.c new file mode 100644 index 0000000..fffe4d0 --- /dev/null +++ b/trunk/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/trunk/test/poll.c b/trunk/test/poll.c new file mode 100644 index 0000000..4026a84 --- /dev/null +++ b/trunk/test/poll.c @@ -0,0 +1,75 @@ +/* -*- 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) +{ + 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 (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 < (int) n_drives; i++) { + NEXT=0; + poll_drive(i); + } + sigaction(SIGINT, &oldact, NULL); + burn_drive_info_free(drives); + burn_finish(); + return 0; +} diff --git a/trunk/test/structest.c b/trunk/test/structest.c new file mode 100644 index 0000000..6820bf8 --- /dev/null +++ b/trunk/test/structest.c @@ -0,0 +1,51 @@ +#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; + + burn_initialize(); + burn_msgs_set_severities("NEVER", "ALL", "structest: "); + + 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/trunk/test/telltoc.c b/trunk/test/telltoc.c new file mode 100644 index 0000000..d1b23f0 --- /dev/null +++ b/trunk/test/telltoc.c @@ -0,0 +1,969 @@ + +/* test/telltoc.c , API illustration of obtaining media status info */ +/* Copyright (C) 2006 - 2011 Thomas Schmitt <scdbackup@gmx.net> + Provided under GPL */ + +/** 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, + multisession info for mkisofs option -C, and can read audio or data tracks. + + 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 audio or data CD or from DVD or BD + 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); + + +/* Messages from --toc to --read_and_print (CD tracksize is a bit tricky) */ +static int last_track_start = 0, last_track_size = -1; +static int medium_is_cd_profile = 0; /* 0 = undecided , -1 = no , 1 = yes */ +static int cd_is_audio = 0; /* 0 = undecided , -1 = no , 1 = yes */ + + +/* ------------------------------- 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]; + + /* 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. + @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 < (int) 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 ((int) 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 < (int) 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; +} + + +void telltoc_detect_cd(struct burn_drive *drive) +{ + int pno; + char profile_name[80]; + + if (burn_disc_get_profile(drive, &pno, profile_name) > 0) { + if (pno >= 0x08 && pno <= 0x0a) + medium_is_cd_profile = 1; + else + medium_is_cd_profile = -1; + } +} + + +int telltoc_toc(struct burn_drive *drive) +{ + int num_sessions = 0 , num_tracks = 0 , lba = 0, pmin, psec, pframe; + int track_count = 0, track_is_audio; + int session_no, track_no; + struct burn_disc *disc= NULL; + struct burn_session **sessions; + struct burn_track **tracks; + struct burn_toc_entry toc_entry; + + disc = burn_drive_get_disc(drive); + if (disc==NULL) { + fprintf(stderr, "SORRY: Cannot obtain Table Of Content\n"); + return 2; + } + sessions = burn_disc_get_sessions(disc, &num_sessions); + for (session_no = 0; session_no<num_sessions; session_no++) { + tracks = burn_session_get_tracks(sessions[session_no], + &num_tracks); + if (tracks==NULL) + continue; + for(track_no= 0; track_no<num_tracks; track_no++) { + track_count++; + burn_track_get_entry(tracks[track_no], &toc_entry); + 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); + } + + if ((toc_entry.control & 7) < 4) { + if (cd_is_audio == 0) + cd_is_audio = 1; + track_is_audio = 1; + } else { + track_is_audio = 0; + cd_is_audio = -1; + } + + printf("Media content: session %2d ", session_no+1); + printf("track %2d %s lba: %9d %4.2d:%2.2d:%2.2d\n", + track_count, + (track_is_audio ? "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; + telltoc_detect_cd(drive); + } + 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, sector_size, chunk_size, read_audio = 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 (medium_is_cd_profile == 0) + telltoc_detect_cd(drive); + if (start_sector == -1) + start_sector = last_track_start; + if (sector_count == -1) { + sector_count = last_track_start + last_track_size + - start_sector; + if (medium_is_cd_profile > 0) /* In case it is a TAO track */ + final_cd_try = 0; /* allow it (-1 is denial) */ + } + + if (sector_count <= 0) + sector_count = 2147483632; + + 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); + + /* Whether to read audio or data */ + if (cd_is_audio > 0) { + read_audio = 1; + } else if (medium_is_cd_profile > 0 && cd_is_audio == 0) { + /* Try whether the start sector is audio */ + ret = burn_read_audio(drive, start_sector, + buf, (off_t) 2352, &data_count, 2 | 4); + if (ret > 0) + read_audio = 1; + } + if (read_audio) { + sector_size = 2352; + chunk_size = 12; + } else { + sector_size = 2048; + chunk_size = 16; + if (start_sector < 0) + start_sector = 0; + } + + todo = sector_count - 2*(final_cd_try > -1); + for (done = 0; done < todo && final_cd_try != 1; done += request) { + if (todo - done > chunk_size) + request = chunk_size; + else + request = todo - done; + + if (read_audio) { + ret = burn_read_audio(drive, start_sector + done, + buf, (off_t) (request * sector_size), + &data_count, 0); + } else { + ret = burn_read_data(drive, + ((off_t) start_sector + done) * + (off_t) sector_size, + buf, (off_t) (request * sector_size), + &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 / sector_size, + i % sector_size); + 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 * sector_size) { + fprintf(stderr, + "\rReading data : start=%ds , count=%ds , read=%ds ", + start_sector, sector_count, + (int) (total_count / (off_t) sector_size)); + last_reported_count = total_count; + } + if (ret <= 0) { + fprintf(stderr, "SORRY : Reading failed.\n"); + break; + } + } + if (ret > 0 && medium_is_cd_profile > 0 && final_cd_try == 0) { + /* In a SAO track the last 2 frames should be data too */ + final_cd_try = 1; + if (read_audio) { + ret = burn_read_audio(drive, start_sector + todo, + buf, (off_t) (2 * sector_size), + &data_count, 2); + } else { + burn_read_data(drive, + ((off_t) start_sector + todo) * + (off_t) sector_size, + buf, (off_t) (2 * sector_size), + &data_count, 2); + } + if (data_count < 2 * sector_size) + 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) sector_size)); + + 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 audio or data CD or DVD or BD in human readable form\n"); + printf(" %s --drive /dev/sr1 --read_and_print 16 4 0 | less\n", + argv[0]); + printf("Copy last 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 != -2 && (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/trunk/version.h.in b/trunk/version.h.in new file mode 100644 index 0000000..13ada99 --- /dev/null +++ b/trunk/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@