Initial import
This commit is contained in:
4
libburn/Makefile
Normal file
4
libburn/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
all clean:
|
||||
$(MAKE) -C .. -$(MAKEFLAGS) $@
|
||||
|
||||
.PHONY: all clean
|
65
libburn/Makefile.am
Normal file
65
libburn/Makefile.am
Normal file
@ -0,0 +1,65 @@
|
||||
pkgconfigdir=$(libdir)/pkgconfig
|
||||
libincludedir=$(includedir)/libburn/@BURN_MAJOR_VERSION@
|
||||
|
||||
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
|
||||
|
||||
## ========================================================================= ##
|
191
libburn/async.c
Normal file
191
libburn/async.c
Normal file
@ -0,0 +1,191 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include "libburn.h"
|
||||
#include "transport.h"
|
||||
#include "drive.h"
|
||||
#include "write.h"
|
||||
#include "options.h"
|
||||
#include "async.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define SCAN_GOING() (workers && !workers->drive)
|
||||
|
||||
typedef void *(*WorkerFunc) (void *);
|
||||
|
||||
struct scan_opts
|
||||
{
|
||||
struct burn_drive_info **drives;
|
||||
unsigned int *n_drives;
|
||||
|
||||
int done;
|
||||
};
|
||||
|
||||
struct erase_opts
|
||||
{
|
||||
struct burn_drive *drive;
|
||||
int fast;
|
||||
};
|
||||
|
||||
struct write_opts
|
||||
{
|
||||
struct burn_drive *drive;
|
||||
struct burn_write_opts *opts;
|
||||
struct burn_disc *disc;
|
||||
};
|
||||
|
||||
struct w_list
|
||||
{
|
||||
struct burn_drive *drive;
|
||||
pthread_t thread;
|
||||
|
||||
struct w_list *next;
|
||||
|
||||
union w_list_data
|
||||
{
|
||||
struct scan_opts scan;
|
||||
struct erase_opts erase;
|
||||
struct write_opts write;
|
||||
} u;
|
||||
};
|
||||
|
||||
static struct w_list *workers;
|
||||
|
||||
static struct w_list *find_worker(struct burn_drive *d)
|
||||
{
|
||||
struct w_list *a;
|
||||
|
||||
for (a = workers; a; a = a->next)
|
||||
if (a->drive == d)
|
||||
return a;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void add_worker(struct burn_drive *d, WorkerFunc f, void *data)
|
||||
{
|
||||
struct w_list *a;
|
||||
struct w_list *tmp;
|
||||
|
||||
a = malloc(sizeof(struct w_list));
|
||||
a->drive = d;
|
||||
a->u = *(union w_list_data *)data;
|
||||
|
||||
/* insert at front of the list */
|
||||
a->next = workers;
|
||||
tmp = workers;
|
||||
workers = a;
|
||||
|
||||
if (d)
|
||||
d->busy = BURN_DRIVE_SPAWNING;
|
||||
|
||||
if (pthread_create(&a->thread, NULL, f, a)) {
|
||||
free(a);
|
||||
workers = tmp;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_worker(pthread_t th)
|
||||
{
|
||||
struct w_list *a, *l = NULL;
|
||||
|
||||
for (a = workers; a; l = a, a = a->next)
|
||||
if (a->thread == th) {
|
||||
if (l)
|
||||
l->next = a->next;
|
||||
else
|
||||
workers = a->next;
|
||||
free(a);
|
||||
break;
|
||||
}
|
||||
assert(a != NULL); /* wasn't found.. this should not be possible */
|
||||
}
|
||||
|
||||
static void *scan_worker_func(struct w_list *w)
|
||||
{
|
||||
burn_drive_scan_sync(w->u.scan.drives, w->u.scan.n_drives);
|
||||
w->u.scan.done = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int burn_drive_scan(struct burn_drive_info *drives[], unsigned int *n_drives)
|
||||
{
|
||||
struct scan_opts o;
|
||||
int ret = 0;
|
||||
|
||||
/* cant be anything working! */
|
||||
assert(!(workers && workers->drive));
|
||||
|
||||
if (!workers) {
|
||||
/* start it */
|
||||
o.drives = drives;
|
||||
o.n_drives = n_drives;
|
||||
o.done = 0;
|
||||
add_worker(NULL, (WorkerFunc) scan_worker_func, &o);
|
||||
} else if (workers->u.scan.done) {
|
||||
/* its done */
|
||||
ret = workers->u.scan.done;
|
||||
remove_worker(workers->thread);
|
||||
assert(workers == NULL);
|
||||
} else {
|
||||
/* still going */
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *erase_worker_func(struct w_list *w)
|
||||
{
|
||||
burn_disc_erase_sync(w->u.erase.drive, w->u.erase.fast);
|
||||
remove_worker(pthread_self());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void burn_disc_erase(struct burn_drive *drive, int fast)
|
||||
{
|
||||
struct erase_opts o;
|
||||
|
||||
assert(drive);
|
||||
assert(!SCAN_GOING());
|
||||
assert(!find_worker(drive));
|
||||
|
||||
o.drive = drive;
|
||||
o.fast = fast;
|
||||
add_worker(drive, (WorkerFunc) erase_worker_func, &o);
|
||||
}
|
||||
|
||||
static void *write_disc_worker_func(struct w_list *w)
|
||||
{
|
||||
burn_disc_write_sync(w->u.write.opts, w->u.write.disc);
|
||||
|
||||
/* the options are refcounted, free out ref count which we added below
|
||||
*/
|
||||
burn_write_opts_free(w->u.write.opts);
|
||||
|
||||
remove_worker(pthread_self());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void burn_disc_write(struct burn_write_opts *opts, struct burn_disc *disc)
|
||||
{
|
||||
struct write_opts o;
|
||||
|
||||
assert(!SCAN_GOING());
|
||||
assert(!find_worker(opts->drive));
|
||||
o.drive = opts->drive;
|
||||
o.opts = opts;
|
||||
o.disc = disc;
|
||||
|
||||
opts->refcount++;
|
||||
|
||||
add_worker(opts->drive, (WorkerFunc) write_disc_worker_func, &o);
|
||||
}
|
||||
|
||||
void burn_async_join_all(void)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
while (workers)
|
||||
pthread_join(workers->thread, &ret);
|
||||
}
|
8
libburn/async.h
Normal file
8
libburn/async.h
Normal file
@ -0,0 +1,8 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef BURN__ASYNC_H
|
||||
#define BURN__ASYNC_H
|
||||
|
||||
void burn_async_join_all(void);
|
||||
struct burn_write_opts;
|
||||
#endif /* BURN__ASYNC_H */
|
122
libburn/crc.c
Normal file
122
libburn/crc.c
Normal file
@ -0,0 +1,122 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include "crc.h"
|
||||
|
||||
static unsigned short ccitt_table[256] = {
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
|
||||
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
|
||||
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
|
||||
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
|
||||
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
|
||||
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
|
||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
|
||||
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
|
||||
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
|
||||
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
|
||||
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
|
||||
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
|
||||
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
|
||||
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
|
||||
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
|
||||
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
|
||||
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
|
||||
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
|
||||
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
|
||||
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
||||
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
|
||||
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
|
||||
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
|
||||
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
|
||||
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
|
||||
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
|
||||
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
|
||||
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
|
||||
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
|
||||
};
|
||||
|
||||
unsigned long crc32_table[256] = {
|
||||
0x00000000L, 0x90910101L, 0x91210201L, 0x01B00300L,
|
||||
0x92410401L, 0x02D00500L, 0x03600600L, 0x93F10701L,
|
||||
0x94810801L, 0x04100900L, 0x05A00A00L, 0x95310B01L,
|
||||
0x06C00C00L, 0x96510D01L, 0x97E10E01L, 0x07700F00L,
|
||||
0x99011001L, 0x09901100L, 0x08201200L, 0x98B11301L,
|
||||
0x0B401400L, 0x9BD11501L, 0x9A611601L, 0x0AF01700L,
|
||||
0x0D801800L, 0x9D111901L, 0x9CA11A01L, 0x0C301B00L,
|
||||
0x9FC11C01L, 0x0F501D00L, 0x0EE01E00L, 0x9E711F01L,
|
||||
0x82012001L, 0x12902100L, 0x13202200L, 0x83B12301L,
|
||||
0x10402400L, 0x80D12501L, 0x81612601L, 0x11F02700L,
|
||||
0x16802800L, 0x86112901L, 0x87A12A01L, 0x17302B00L,
|
||||
0x84C12C01L, 0x14502D00L, 0x15E02E00L, 0x85712F01L,
|
||||
0x1B003000L, 0x8B913101L, 0x8A213201L, 0x1AB03300L,
|
||||
0x89413401L, 0x19D03500L, 0x18603600L, 0x88F13701L,
|
||||
0x8F813801L, 0x1F103900L, 0x1EA03A00L, 0x8E313B01L,
|
||||
0x1DC03C00L, 0x8D513D01L, 0x8CE13E01L, 0x1C703F00L,
|
||||
0xB4014001L, 0x24904100L, 0x25204200L, 0xB5B14301L,
|
||||
0x26404400L, 0xB6D14501L, 0xB7614601L, 0x27F04700L,
|
||||
0x20804800L, 0xB0114901L, 0xB1A14A01L, 0x21304B00L,
|
||||
0xB2C14C01L, 0x22504D00L, 0x23E04E00L, 0xB3714F01L,
|
||||
0x2D005000L, 0xBD915101L, 0xBC215201L, 0x2CB05300L,
|
||||
0xBF415401L, 0x2FD05500L, 0x2E605600L, 0xBEF15701L,
|
||||
0xB9815801L, 0x29105900L, 0x28A05A00L, 0xB8315B01L,
|
||||
0x2BC05C00L, 0xBB515D01L, 0xBAE15E01L, 0x2A705F00L,
|
||||
0x36006000L, 0xA6916101L, 0xA7216201L, 0x37B06300L,
|
||||
0xA4416401L, 0x34D06500L, 0x35606600L, 0xA5F16701L,
|
||||
0xA2816801L, 0x32106900L, 0x33A06A00L, 0xA3316B01L,
|
||||
0x30C06C00L, 0xA0516D01L, 0xA1E16E01L, 0x31706F00L,
|
||||
0xAF017001L, 0x3F907100L, 0x3E207200L, 0xAEB17301L,
|
||||
0x3D407400L, 0xADD17501L, 0xAC617601L, 0x3CF07700L,
|
||||
0x3B807800L, 0xAB117901L, 0xAAA17A01L, 0x3A307B00L,
|
||||
0xA9C17C01L, 0x39507D00L, 0x38E07E00L, 0xA8717F01L,
|
||||
0xD8018001L, 0x48908100L, 0x49208200L, 0xD9B18301L,
|
||||
0x4A408400L, 0xDAD18501L, 0xDB618601L, 0x4BF08700L,
|
||||
0x4C808800L, 0xDC118901L, 0xDDA18A01L, 0x4D308B00L,
|
||||
0xDEC18C01L, 0x4E508D00L, 0x4FE08E00L, 0xDF718F01L,
|
||||
0x41009000L, 0xD1919101L, 0xD0219201L, 0x40B09300L,
|
||||
0xD3419401L, 0x43D09500L, 0x42609600L, 0xD2F19701L,
|
||||
0xD5819801L, 0x45109900L, 0x44A09A00L, 0xD4319B01L,
|
||||
0x47C09C00L, 0xD7519D01L, 0xD6E19E01L, 0x46709F00L,
|
||||
0x5A00A000L, 0xCA91A101L, 0xCB21A201L, 0x5BB0A300L,
|
||||
0xC841A401L, 0x58D0A500L, 0x5960A600L, 0xC9F1A701L,
|
||||
0xCE81A801L, 0x5E10A900L, 0x5FA0AA00L, 0xCF31AB01L,
|
||||
0x5CC0AC00L, 0xCC51AD01L, 0xCDE1AE01L, 0x5D70AF00L,
|
||||
0xC301B001L, 0x5390B100L, 0x5220B200L, 0xC2B1B301L,
|
||||
0x5140B400L, 0xC1D1B501L, 0xC061B601L, 0x50F0B700L,
|
||||
0x5780B800L, 0xC711B901L, 0xC6A1BA01L, 0x5630BB00L,
|
||||
0xC5C1BC01L, 0x5550BD00L, 0x54E0BE00L, 0xC471BF01L,
|
||||
0x6C00C000L, 0xFC91C101L, 0xFD21C201L, 0x6DB0C300L,
|
||||
0xFE41C401L, 0x6ED0C500L, 0x6F60C600L, 0xFFF1C701L,
|
||||
0xF881C801L, 0x6810C900L, 0x69A0CA00L, 0xF931CB01L,
|
||||
0x6AC0CC00L, 0xFA51CD01L, 0xFBE1CE01L, 0x6B70CF00L,
|
||||
0xF501D001L, 0x6590D100L, 0x6420D200L, 0xF4B1D301L,
|
||||
0x6740D400L, 0xF7D1D501L, 0xF661D601L, 0x66F0D700L,
|
||||
0x6180D800L, 0xF111D901L, 0xF0A1DA01L, 0x6030DB00L,
|
||||
0xF3C1DC01L, 0x6350DD00L, 0x62E0DE00L, 0xF271DF01L,
|
||||
0xEE01E001L, 0x7E90E100L, 0x7F20E200L, 0xEFB1E301L,
|
||||
0x7C40E400L, 0xECD1E501L, 0xED61E601L, 0x7DF0E700L,
|
||||
0x7A80E800L, 0xEA11E901L, 0xEBA1EA01L, 0x7B30EB00L,
|
||||
0xE8C1EC01L, 0x7850ED00L, 0x79E0EE00L, 0xE971EF01L,
|
||||
0x7700F000L, 0xE791F101L, 0xE621F201L, 0x76B0F300L,
|
||||
0xE541F401L, 0x75D0F500L, 0x7460F600L, 0xE4F1F701L,
|
||||
0xE381F801L, 0x7310F900L, 0x72A0FA00L, 0xE231FB01L,
|
||||
0x71C0FC00L, 0xE151FD01L, 0xE0E1FE01L, 0x7070FF00L
|
||||
};
|
||||
|
||||
unsigned short crc_ccitt(unsigned char *q, int len)
|
||||
{
|
||||
unsigned short crc = 0;
|
||||
|
||||
while (len-- > 0)
|
||||
crc = ccitt_table[(crc >> 8 ^ *q++) & 0xff] ^ (crc << 8);
|
||||
return ~crc;
|
||||
}
|
||||
unsigned int crc_32(unsigned char *data, int len)
|
||||
{
|
||||
unsigned int crc = 0;
|
||||
|
||||
while (len-- > 0)
|
||||
crc = crc32_table[(crc ^ *data++) & 0xffL] ^ (crc >> 8);
|
||||
return crc;
|
||||
}
|
9
libburn/crc.h
Normal file
9
libburn/crc.h
Normal file
@ -0,0 +1,9 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef BURN__CRC_H
|
||||
#define BURN__CRC_H
|
||||
|
||||
unsigned short crc_ccitt(unsigned char *, int len);
|
||||
unsigned int crc_32(unsigned char *, int len);
|
||||
|
||||
#endif /* BURN__CRC_H */
|
35
libburn/debug.c
Normal file
35
libburn/debug.c
Normal file
@ -0,0 +1,35 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include "libburn.h"
|
||||
#include "debug.h"
|
||||
|
||||
static int burn_verbosity = 0;
|
||||
|
||||
void burn_set_verbosity(int v)
|
||||
{
|
||||
burn_verbosity = v;
|
||||
}
|
||||
|
||||
void burn_print(int level, const char *a, ...)
|
||||
{
|
||||
#ifdef WIN32
|
||||
char debug_string_data[256];
|
||||
#endif
|
||||
va_list vl;
|
||||
|
||||
if (level <= burn_verbosity) {
|
||||
va_start(vl, a);
|
||||
#ifdef WIN32
|
||||
vsprintf(debug_string_data, a, vl);
|
||||
OutputDebugString(debug_string_data);
|
||||
#else
|
||||
vfprintf(stderr, a, vl);
|
||||
#endif
|
||||
}
|
||||
}
|
8
libburn/debug.h
Normal file
8
libburn/debug.h
Normal file
@ -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 */
|
369
libburn/drive.c
Normal file
369
libburn/drive.c
Normal file
@ -0,0 +1,369 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include <malloc.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <pthread.h>
|
||||
#include "libburn.h"
|
||||
#include "drive.h"
|
||||
#include "transport.h"
|
||||
#include "message.h"
|
||||
#include "debug.h"
|
||||
#include "init.h"
|
||||
#include "toc.h"
|
||||
#include "util.h"
|
||||
#include "sg.h"
|
||||
#include "structure.h"
|
||||
|
||||
static struct burn_drive drive_array[255];
|
||||
static int drivetop = -1;
|
||||
|
||||
void burn_drive_free(void)
|
||||
{
|
||||
int i;
|
||||
struct burn_drive *d;
|
||||
|
||||
for (i = 0; i < drivetop + 1; i++) {
|
||||
d = &drive_array[i];
|
||||
free((void *)d->idata);
|
||||
free((void *)d->mdata);
|
||||
free((void *)d->toc_entry);
|
||||
free(d->devname);
|
||||
}
|
||||
drivetop = -1;
|
||||
memset(drive_array, 0, sizeof(drive_array));
|
||||
}
|
||||
|
||||
/*
|
||||
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;
|
||||
}
|
||||
|
||||
int burn_drive_grab(struct burn_drive *d, int le)
|
||||
{
|
||||
int errcode;
|
||||
|
||||
if (!d->released) {
|
||||
burn_print(1, "can't grab - already grabbed\n");
|
||||
return 0;
|
||||
}
|
||||
errcode = d->grab(d);
|
||||
|
||||
if (errcode == 0) {
|
||||
burn_print(1, "low level drive grab failed\n");
|
||||
return 0;
|
||||
}
|
||||
d->busy = BURN_DRIVE_GRABBING;
|
||||
|
||||
if (le)
|
||||
d->load(d);
|
||||
|
||||
d->lock(d);
|
||||
d->status = BURN_DISC_BLANK;
|
||||
if (d->mdata->cdr_write || d->mdata->cdrw_write ||
|
||||
d->mdata->dvdr_write || d->mdata->dvdram_write) {
|
||||
d->read_disc_info(d);
|
||||
} else
|
||||
d->read_toc(d);
|
||||
d->busy = BURN_DRIVE_IDLE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct burn_drive *burn_drive_register(struct burn_drive *d)
|
||||
{
|
||||
d->block_types[0] = 0;
|
||||
d->block_types[1] = 0;
|
||||
d->block_types[2] = 0;
|
||||
d->block_types[3] = 0;
|
||||
d->toc_temp = 0;
|
||||
d->nwa = 0;
|
||||
d->alba = 0;
|
||||
d->rlba = 0;
|
||||
d->cancel = 0;
|
||||
d->busy = BURN_DRIVE_IDLE;
|
||||
d->toc_entries = 0;
|
||||
d->toc_entry = NULL;
|
||||
d->disc = NULL;
|
||||
d->erasable = 0;
|
||||
memcpy(&drive_array[drivetop + 1], d, sizeof(struct burn_drive));
|
||||
pthread_mutex_init(&drive_array[drivetop + 1].access_lock, NULL);
|
||||
return &drive_array[++drivetop];
|
||||
}
|
||||
|
||||
void burn_drive_release(struct burn_drive *d, int le)
|
||||
{
|
||||
if (d->released)
|
||||
burn_print(1, "second release on drive!\n");
|
||||
assert(!d->busy);
|
||||
if (le)
|
||||
d->eject(d);
|
||||
d->unlock(d);
|
||||
|
||||
d->release(d);
|
||||
|
||||
d->status = BURN_DISC_UNREADY;
|
||||
d->released = 1;
|
||||
if (d->toc_entry)
|
||||
free(d->toc_entry);
|
||||
d->toc_entry = NULL;
|
||||
d->toc_entries = 0;
|
||||
if (d->disc != NULL) {
|
||||
burn_disc_free(d->disc);
|
||||
d->disc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
assert(d->released);
|
||||
}
|
||||
if (!finished)
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
void burn_disc_erase_sync(struct burn_drive *d, int fast)
|
||||
{
|
||||
burn_message_clear_queue();
|
||||
|
||||
burn_print(1, "erasing drive %s %s\n", d->idata->vendor,
|
||||
d->idata->product);
|
||||
|
||||
if (d->status != BURN_DISC_FULL)
|
||||
return;
|
||||
d->cancel = 0;
|
||||
d->busy = BURN_DRIVE_ERASING;
|
||||
d->erase(d, fast);
|
||||
/* reset the progress */
|
||||
d->progress.session = 0;
|
||||
d->progress.sessions = 1;
|
||||
d->progress.track = 0;
|
||||
d->progress.tracks = 1;
|
||||
d->progress.index = 0;
|
||||
d->progress.indices = 1;
|
||||
d->progress.start_sector = 0;
|
||||
d->progress.sectors = 0x10000;
|
||||
d->progress.sector = 0;
|
||||
/* read the initial 0 stage */
|
||||
while (!d->test_unit_ready(d) && d->get_erase_progress(d) == 0)
|
||||
sleep(1);
|
||||
while (!d->test_unit_ready(d) &&
|
||||
(d->progress.sector = d->get_erase_progress(d)) > 0)
|
||||
sleep(1);
|
||||
d->progress.sector = 0x10000;
|
||||
d->busy = BURN_DRIVE_IDLE;
|
||||
}
|
||||
|
||||
enum burn_disc_status burn_disc_get_status(struct burn_drive *d)
|
||||
{
|
||||
assert(!d->released);
|
||||
return d->status;
|
||||
}
|
||||
|
||||
int burn_disc_erasable(struct burn_drive *d)
|
||||
{
|
||||
return d->erasable;
|
||||
}
|
||||
enum burn_drive_status burn_drive_get_status(struct burn_drive *d,
|
||||
struct burn_progress *p)
|
||||
{
|
||||
if (p) {
|
||||
memcpy(p, &(d->progress), sizeof(struct burn_progress));
|
||||
/* TODO: add mutex */
|
||||
}
|
||||
return d->busy;
|
||||
}
|
||||
|
||||
void burn_drive_cancel(struct burn_drive *d)
|
||||
{
|
||||
pthread_mutex_lock(&d->access_lock);
|
||||
d->cancel = 1;
|
||||
pthread_mutex_unlock(&d->access_lock);
|
||||
}
|
||||
|
||||
int burn_drive_get_block_types(struct burn_drive *d,
|
||||
enum burn_write_types write_type)
|
||||
{
|
||||
burn_print(12, "write type: %d\n", write_type);
|
||||
assert( /* (write_type >= BURN_WRITE_PACKET) && */
|
||||
(write_type <= BURN_WRITE_RAW));
|
||||
return d->block_types[write_type];
|
||||
}
|
||||
|
||||
static void strip_spaces(char *str)
|
||||
{
|
||||
char *tmp;
|
||||
|
||||
tmp = str + strlen(str) - 1;
|
||||
while (isspace(*tmp))
|
||||
*(tmp--) = '\0';
|
||||
|
||||
tmp = str;
|
||||
while (*tmp) {
|
||||
if (isspace(*tmp) && isspace(*(tmp + 1))) {
|
||||
char *tmp2;
|
||||
|
||||
for (tmp2 = tmp + 1; *tmp2; ++tmp2)
|
||||
*(tmp2 - 1) = *tmp2;
|
||||
*(tmp2 - 1) = '\0';
|
||||
} else
|
||||
++tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static int drive_getcaps(struct burn_drive *d, struct burn_drive_info *out)
|
||||
{
|
||||
struct scsi_inquiry_data *id;
|
||||
|
||||
assert(d->idata);
|
||||
assert(d->mdata);
|
||||
if (!d->idata->valid || !d->mdata->valid)
|
||||
return 0;
|
||||
|
||||
id = (struct scsi_inquiry_data *)d->idata;
|
||||
|
||||
memcpy(out->vendor, id->vendor, sizeof(id->vendor));
|
||||
strip_spaces(out->vendor);
|
||||
memcpy(out->product, id->product, sizeof(id->product));
|
||||
strip_spaces(out->product);
|
||||
memcpy(out->revision, id->revision, sizeof(id->revision));
|
||||
strip_spaces(out->revision);
|
||||
strncpy(out->location, d->devname, 16);
|
||||
out->location[16] = '\0';
|
||||
out->buffer_size = d->mdata->buffer_size;
|
||||
out->read_dvdram = !!d->mdata->dvdram_read;
|
||||
out->read_dvdr = !!d->mdata->dvdr_read;
|
||||
out->read_dvdrom = !!d->mdata->dvdrom_read;
|
||||
out->read_cdr = !!d->mdata->cdr_read;
|
||||
out->read_cdrw = !!d->mdata->cdrw_read;
|
||||
out->write_dvdram = !!d->mdata->dvdram_write;
|
||||
out->write_dvdr = !!d->mdata->dvdr_write;
|
||||
out->write_cdr = !!d->mdata->cdr_write;
|
||||
out->write_cdrw = !!d->mdata->cdrw_write;
|
||||
out->write_simulate = !!d->mdata->simulate;
|
||||
out->c2_errors = !!d->mdata->c2_pointers;
|
||||
out->drive = d;
|
||||
/* update available block types for burners */
|
||||
if (out->write_dvdram || out->write_dvdr ||
|
||||
out->write_cdrw || out->write_cdr)
|
||||
d->probe_write_modes(d);
|
||||
out->tao_block_types = d->block_types[BURN_WRITE_TAO];
|
||||
out->sao_block_types = d->block_types[BURN_WRITE_SAO];
|
||||
out->raw_block_types = d->block_types[BURN_WRITE_RAW];
|
||||
out->packet_block_types = d->block_types[BURN_WRITE_PACKET];
|
||||
return 1;
|
||||
}
|
||||
|
||||
int burn_drive_scan_sync(struct burn_drive_info *drives[],
|
||||
unsigned int *n_drives)
|
||||
{
|
||||
/* state vars for the scan process */
|
||||
static int scanning = 0, scanned, found;
|
||||
static unsigned num_scanned, count;
|
||||
unsigned int i;
|
||||
struct burn_drive *d;
|
||||
|
||||
assert(burn_running);
|
||||
|
||||
if (!scanning) {
|
||||
scanning = 1;
|
||||
/* make sure the drives aren't in use */
|
||||
burn_wait_all(); /* make sure the queue cleans up
|
||||
before checking for the released
|
||||
state */
|
||||
|
||||
d = drive_array;
|
||||
count = burn_drive_count();
|
||||
for (i = 0; i < count; ++i, ++d)
|
||||
assert(d->released == 1);
|
||||
|
||||
/* refresh the lib's drives */
|
||||
sg_enumerate();
|
||||
ata_enumerate();
|
||||
count = burn_drive_count();
|
||||
if (count)
|
||||
*drives =
|
||||
malloc(sizeof(struct burn_drive_info) * count);
|
||||
else
|
||||
*drives = NULL;
|
||||
*n_drives = scanned = found = num_scanned = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
if (scanned & (1 << i))
|
||||
continue; /* already scanned the device */
|
||||
|
||||
while (!drive_getcaps(&drive_array[i],
|
||||
&(*drives)[num_scanned])) {
|
||||
sleep(1);
|
||||
}
|
||||
scanned |= 1 << i;
|
||||
found |= 1 << i;
|
||||
num_scanned++;
|
||||
(*n_drives)++;
|
||||
}
|
||||
|
||||
if (num_scanned == count) {
|
||||
/* done scanning */
|
||||
scanning = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void burn_drive_info_free(struct burn_drive_info *info)
|
||||
{
|
||||
free(info);
|
||||
burn_drive_free();
|
||||
}
|
||||
|
||||
struct burn_disc *burn_drive_get_disc(struct burn_drive *d)
|
||||
{
|
||||
d->disc->refcnt++;
|
||||
return d->disc;
|
||||
}
|
||||
|
||||
void burn_drive_set_speed(struct burn_drive *d, int r, int w)
|
||||
{
|
||||
d->set_speed(d, r, w);
|
||||
}
|
||||
|
||||
int burn_msf_to_sectors(int m, int s, int f)
|
||||
{
|
||||
return (m * 60 + s) * 75 + f;
|
||||
}
|
||||
|
||||
void burn_sectors_to_msf(int sectors, int *m, int *s, int *f)
|
||||
{
|
||||
*m = sectors / (60 * 75);
|
||||
*s = (sectors - *m * 60 * 75) / 75;
|
||||
*f = sectors - *m * 60 * 75 - *s * 75;
|
||||
}
|
||||
|
||||
int burn_drive_get_read_speed(struct burn_drive *d)
|
||||
{
|
||||
return d->mdata->max_read_speed;
|
||||
}
|
||||
|
||||
int burn_drive_get_write_speed(struct burn_drive *d)
|
||||
{
|
||||
return d->mdata->max_write_speed;
|
||||
}
|
53
libburn/drive.h
Normal file
53
libburn/drive.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef __DRIVE
|
||||
#define __DRIVE
|
||||
|
||||
#include "libburn.h"
|
||||
#include "toc.h"
|
||||
#include "structure.h"
|
||||
|
||||
struct burn_drive;
|
||||
struct command;
|
||||
struct mempage;
|
||||
|
||||
#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 *);
|
||||
|
||||
unsigned int burn_drive_count(void);
|
||||
void burn_wait_all(void);
|
||||
int burn_sector_length_write(struct burn_drive *d);
|
||||
int burn_track_control(struct burn_drive *d, int);
|
||||
void burn_write_empty_sector(int fd);
|
||||
void burn_write_empty_subcode(int fd);
|
||||
void burn_drive_free(void);
|
||||
|
||||
int burn_drive_scan_sync(struct burn_drive_info *drives[],
|
||||
unsigned int *n_drives);
|
||||
void burn_disc_erase_sync(struct burn_drive *d, int fast);
|
||||
int burn_drive_get_block_types(struct burn_drive *d,
|
||||
enum burn_write_types write_type);
|
||||
|
||||
#endif /* __DRIVE */
|
8
libburn/error.h
Normal file
8
libburn/error.h
Normal file
@ -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 */
|
187
libburn/file.c
Normal file
187
libburn/file.c
Normal file
@ -0,0 +1,187 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include "source.h"
|
||||
#include "libburn.h"
|
||||
#include "file.h"
|
||||
|
||||
/* main channel data can be padded on read, but 0 padding the subs will make
|
||||
an unreadable disc */
|
||||
|
||||
|
||||
/* This is a generic OS oriented function wrapper which compensates
|
||||
shortcommings of read() in respect to a guaranteed amount of return data.
|
||||
See man 2 read , paragraph "RETURN VALUE".
|
||||
Possibly libburn/file.c is not the right storage location for this.
|
||||
To make it ready for a move, this function is not declared static.
|
||||
*/
|
||||
static int read_full_buffer(int fd, unsigned char *buffer, int size)
|
||||
{
|
||||
int ret,summed_ret = 0;
|
||||
|
||||
/* make safe against partial buffer returns */
|
||||
while (1) {
|
||||
ret = read(fd, buffer + summed_ret, size - summed_ret);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
summed_ret += ret;
|
||||
if (summed_ret >= size)
|
||||
break;
|
||||
}
|
||||
if (ret < 0) /* error encountered. abort immediately */
|
||||
return ret;
|
||||
return summed_ret;
|
||||
}
|
||||
|
||||
|
||||
static int file_read(struct burn_source *source,
|
||||
unsigned char *buffer,
|
||||
int size)
|
||||
{
|
||||
struct burn_source_fd *fs = source->data;
|
||||
|
||||
return read_full_buffer(fs->datafd, buffer, size);
|
||||
}
|
||||
|
||||
static int file_read_sub(struct burn_source *source,
|
||||
unsigned char *buffer,
|
||||
int size)
|
||||
{
|
||||
struct burn_source_file *fs = source->data;
|
||||
|
||||
return read_full_buffer(fs->subfd, buffer, size);
|
||||
}
|
||||
|
||||
static void file_free(struct burn_source *source)
|
||||
{
|
||||
struct burn_source_file *fs = source->data;
|
||||
|
||||
close(fs->datafd);
|
||||
if (source->read_sub)
|
||||
close(fs->subfd);
|
||||
free(fs);
|
||||
}
|
||||
|
||||
static off_t file_size(struct burn_source *source)
|
||||
{
|
||||
struct stat buf;
|
||||
struct burn_source_file *fs = source->data;
|
||||
|
||||
if (fstat(fs->datafd, &buf) == -1)
|
||||
return (off_t) 0;
|
||||
/* for now we keep it compatible to the old (int) return value */
|
||||
if(buf.st_size >= 1308622848) /* 2 GB - 800 MB to prevent rollover */
|
||||
return (off_t) 1308622848;
|
||||
return (off_t) buf.st_size;
|
||||
}
|
||||
|
||||
struct burn_source *burn_file_source_new(const char *path, const char *subpath)
|
||||
{
|
||||
struct burn_source_file *fs;
|
||||
struct burn_source *src;
|
||||
int fd1, fd2 = 0;
|
||||
|
||||
if (!path)
|
||||
return NULL;
|
||||
fd1 = open(path, O_RDONLY);
|
||||
if (fd1 == -1)
|
||||
return NULL;
|
||||
if (subpath) {
|
||||
fd2 = open(subpath, O_RDONLY);
|
||||
if (fd2 == -1) {
|
||||
close(fd1);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
fs = malloc(sizeof(struct burn_source_file));
|
||||
fs->datafd = fd1;
|
||||
|
||||
if (subpath)
|
||||
fs->subfd = fd2;
|
||||
|
||||
src = burn_source_new();
|
||||
src->read = file_read;
|
||||
if (subpath)
|
||||
src->read_sub = file_read_sub;
|
||||
|
||||
src->get_size = file_size;
|
||||
src->free_data = file_free;
|
||||
src->data = fs;
|
||||
return src;
|
||||
}
|
||||
|
||||
|
||||
/* ------ provisory location for the new source subclass fd --------- */
|
||||
|
||||
static off_t fd_get_size(struct burn_source *source)
|
||||
{
|
||||
struct stat buf;
|
||||
struct burn_source_fd *fs = source->data;
|
||||
|
||||
if (fs->fixed_size > 0)
|
||||
return fs->fixed_size;
|
||||
if (fstat(fs->datafd, &buf) == -1)
|
||||
return (off_t) 0;
|
||||
/* for now we keep it compatible to the old (int) return value */
|
||||
if (buf.st_size >= 1308622848) /* 2 GB - 800 MB to prevent rollover */
|
||||
return (off_t) 1308622848;
|
||||
return buf.st_size;
|
||||
}
|
||||
|
||||
static int fd_read(struct burn_source *source,
|
||||
unsigned char *buffer,
|
||||
int size)
|
||||
{
|
||||
struct burn_source_fd *fs = source->data;
|
||||
|
||||
return read_full_buffer(fs->datafd, buffer, size);
|
||||
}
|
||||
|
||||
|
||||
static int fd_read_sub(struct burn_source *source,
|
||||
unsigned char *buffer,
|
||||
int size)
|
||||
{
|
||||
struct burn_source_fd *fs = source->data;
|
||||
|
||||
return read_full_buffer(fs->subfd, buffer, size);
|
||||
}
|
||||
|
||||
|
||||
static void fd_free_data(struct burn_source *source)
|
||||
{
|
||||
struct burn_source_fd *fs = source->data;
|
||||
|
||||
close(fs->datafd);
|
||||
if (source->read_sub)
|
||||
close(fs->subfd);
|
||||
free(fs);
|
||||
}
|
||||
|
||||
|
||||
struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size)
|
||||
{
|
||||
struct burn_source_fd *fs;
|
||||
struct burn_source *src;
|
||||
|
||||
if (datafd == -1)
|
||||
return NULL;
|
||||
fs = malloc(sizeof(struct burn_source_fd));
|
||||
fs->datafd = datafd;
|
||||
fs->subfd = subfd;
|
||||
fs->fixed_size = size;
|
||||
|
||||
src = burn_source_new();
|
||||
src->read = fd_read;
|
||||
if(subfd != -1)
|
||||
src->read = fd_read_sub;
|
||||
src->get_size = fd_get_size;
|
||||
src->free_data = fd_free_data;
|
||||
src->data = fs;
|
||||
return src;
|
||||
}
|
||||
|
22
libburn/file.h
Normal file
22
libburn/file.h
Normal file
@ -0,0 +1,22 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef BURN__FILE_H
|
||||
#define BURN__FILE_H
|
||||
|
||||
struct burn_source_file
|
||||
{
|
||||
int datafd;
|
||||
int subfd;
|
||||
};
|
||||
|
||||
|
||||
/* ------ provisory location for the new source subclass fd --------- */
|
||||
|
||||
struct burn_source_fd
|
||||
{
|
||||
int datafd;
|
||||
int subfd;
|
||||
off_t fixed_size;
|
||||
};
|
||||
|
||||
#endif /* LIBBURN__FILE_H */
|
33
libburn/init.c
Normal file
33
libburn/init.c
Normal file
@ -0,0 +1,33 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include "init.h"
|
||||
#include "sg.h"
|
||||
#include "error.h"
|
||||
#include "libburn.h"
|
||||
#include "drive.h"
|
||||
|
||||
int burn_running = 0;
|
||||
|
||||
int burn_initialize(void)
|
||||
{
|
||||
if (burn_running)
|
||||
return 1;
|
||||
|
||||
burn_running = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void burn_finish(void)
|
||||
{
|
||||
assert(burn_running);
|
||||
|
||||
burn_wait_all();
|
||||
|
||||
burn_drive_free();
|
||||
|
||||
burn_running = 0;
|
||||
}
|
8
libburn/init.h
Normal file
8
libburn/init.h
Normal file
@ -0,0 +1,8 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef BURN__INIT_H
|
||||
#define BURN__INIT_H
|
||||
|
||||
extern int burn_running;
|
||||
|
||||
#endif /* BURN__INIT_H */
|
451
libburn/lec.c
Normal file
451
libburn/lec.c
Normal file
@ -0,0 +1,451 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
/* borrowed HEAVILY from cdrdao */
|
||||
|
||||
#include <string.h>
|
||||
#include "lec.h"
|
||||
|
||||
#define LEC_HEADER_OFFSET 12
|
||||
#define LEC_MODE1_P_PARITY_OFFSET 2076
|
||||
#define LEC_MODE1_Q_PARITY_OFFSET 2248
|
||||
|
||||
static unsigned char gf8_ilog[255] = {
|
||||
1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76,
|
||||
152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96,
|
||||
192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119,
|
||||
238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186,
|
||||
105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94,
|
||||
188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187,
|
||||
107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217,
|
||||
175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103,
|
||||
206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197,
|
||||
151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79,
|
||||
158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85,
|
||||
170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99,
|
||||
198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227,
|
||||
219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87,
|
||||
174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224,
|
||||
221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195,
|
||||
155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244,
|
||||
245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125,
|
||||
250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142,
|
||||
};
|
||||
static unsigned char gf8_log[256] = {
|
||||
0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100,
|
||||
224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113,
|
||||
5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18,
|
||||
130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9,
|
||||
120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253,
|
||||
226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143,
|
||||
150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182,
|
||||
163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61,
|
||||
202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115,
|
||||
243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222,
|
||||
237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124,
|
||||
17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188,
|
||||
207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211,
|
||||
171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31,
|
||||
45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12,
|
||||
111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134,
|
||||
177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11,
|
||||
245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231,
|
||||
173, 232, 116, 214, 244, 234, 168, 80, 88, 175,
|
||||
};
|
||||
static unsigned char gf8_q_coeffs[2][45] = {
|
||||
{97, 251, 133, 60, 82, 160, 155, 201, 8, 112, 246, 11, 21, 42, 157,
|
||||
169, 80, 174, 232, 230, 172, 211, 241, 18, 68, 216, 44, 121, 9, 200,
|
||||
75, 103, 221, 252, 96, 176, 88, 167, 114, 76, 199, 26, 1, 0, 0},
|
||||
{190, 96, 250, 132, 59, 81, 159, 154, 200, 7, 111, 245, 10, 20, 41,
|
||||
156, 168, 79, 173, 231, 229, 171, 210, 240, 17, 67, 215, 43, 120, 8,
|
||||
199, 74, 102, 220, 251, 95, 175, 87, 166, 113, 75, 198, 25, 0, 0}
|
||||
};
|
||||
static unsigned char gf8_p_coeffs[2][26] = {
|
||||
{230, 172, 211, 241, 18, 68, 216, 44, 121, 9, 200, 75, 103, 221, 252,
|
||||
96, 176, 88, 167, 114, 76, 199, 26, 1, 0, 0},
|
||||
{231, 229, 171, 210, 240, 17, 67, 215, 43, 120, 8, 199, 74, 102, 220,
|
||||
251, 95, 175, 87, 166, 113, 75, 198, 25, 0, 0}
|
||||
};
|
||||
|
||||
static unsigned char yellowbook_scrambler[2340] = {
|
||||
1, 128, 0, 96, 0, 40, 0, 30, 128, 8, 96, 6, 168, 2, 254, 129, 128, 96,
|
||||
96, 40, 40, 30, 158,
|
||||
136, 104, 102, 174, 170, 252, 127, 1, 224, 0, 72, 0, 54, 128, 22, 224,
|
||||
14, 200, 4, 86, 131, 126, 225,
|
||||
224, 72, 72, 54, 182, 150, 246, 238, 198, 204, 82, 213, 253, 159, 1,
|
||||
168, 0, 126, 128, 32, 96, 24, 40,
|
||||
10, 158, 135, 40, 98, 158, 169, 168, 126, 254, 160, 64, 120, 48, 34,
|
||||
148, 25, 175, 74, 252, 55, 1, 214,
|
||||
128, 94, 224, 56, 72, 18, 182, 141, 182, 229, 182, 203, 54, 215, 86,
|
||||
222, 190, 216, 112, 90, 164, 59, 59,
|
||||
83, 83, 125, 253, 225, 129, 136, 96, 102, 168, 42, 254, 159, 0, 104, 0,
|
||||
46, 128, 28, 96, 9, 232, 6,
|
||||
206, 130, 212, 97, 159, 104, 104, 46, 174, 156, 124, 105, 225, 238,
|
||||
200, 76, 86, 181, 254, 247, 0, 70, 128,
|
||||
50, 224, 21, 136, 15, 38, 132, 26, 227, 75, 9, 247, 70, 198, 178, 210,
|
||||
245, 157, 135, 41, 162, 158, 249,
|
||||
168, 66, 254, 177, 128, 116, 96, 39, 104, 26, 174, 139, 60, 103, 81,
|
||||
234, 188, 79, 49, 244, 20, 71, 79,
|
||||
114, 180, 37, 183, 91, 54, 187, 86, 243, 126, 197, 224, 83, 8, 61, 198,
|
||||
145, 146, 236, 109, 141, 237, 165,
|
||||
141, 187, 37, 179, 91, 53, 251, 87, 3, 126, 129, 224, 96, 72, 40, 54,
|
||||
158, 150, 232, 110, 206, 172, 84,
|
||||
125, 255, 97, 128, 40, 96, 30, 168, 8, 126, 134, 160, 98, 248, 41, 130,
|
||||
158, 225, 168, 72, 126, 182, 160,
|
||||
118, 248, 38, 194, 154, 209, 171, 28, 127, 73, 224, 54, 200, 22, 214,
|
||||
142, 222, 228, 88, 75, 122, 183, 99,
|
||||
54, 169, 214, 254, 222, 192, 88, 80, 58, 188, 19, 49, 205, 212, 85,
|
||||
159, 127, 40, 32, 30, 152, 8, 106,
|
||||
134, 175, 34, 252, 25, 129, 202, 224, 87, 8, 62, 134, 144, 98, 236, 41,
|
||||
141, 222, 229, 152, 75, 42, 183,
|
||||
95, 54, 184, 22, 242, 142, 197, 164, 83, 59, 125, 211, 97, 157, 232,
|
||||
105, 142, 174, 228, 124, 75, 97, 247,
|
||||
104, 70, 174, 178, 252, 117, 129, 231, 32, 74, 152, 55, 42, 150, 159,
|
||||
46, 232, 28, 78, 137, 244, 102, 199,
|
||||
106, 210, 175, 29, 188, 9, 177, 198, 244, 82, 199, 125, 146, 161, 173,
|
||||
184, 125, 178, 161, 181, 184, 119, 50,
|
||||
166, 149, 186, 239, 51, 12, 21, 197, 207, 19, 20, 13, 207, 69, 148, 51,
|
||||
47, 85, 220, 63, 25, 208, 10,
|
||||
220, 7, 25, 194, 138, 209, 167, 28, 122, 137, 227, 38, 201, 218, 214,
|
||||
219, 30, 219, 72, 91, 118, 187, 102,
|
||||
243, 106, 197, 239, 19, 12, 13, 197, 197, 147, 19, 45, 205, 221, 149,
|
||||
153, 175, 42, 252, 31, 1, 200, 0,
|
||||
86, 128, 62, 224, 16, 72, 12, 54, 133, 214, 227, 30, 201, 200, 86, 214,
|
||||
190, 222, 240, 88, 68, 58, 179,
|
||||
83, 53, 253, 215, 1, 158, 128, 104, 96, 46, 168, 28, 126, 137, 224,
|
||||
102, 200, 42, 214, 159, 30, 232, 8,
|
||||
78, 134, 180, 98, 247, 105, 134, 174, 226, 252, 73, 129, 246, 224, 70,
|
||||
200, 50, 214, 149, 158, 239, 40, 76,
|
||||
30, 181, 200, 119, 22, 166, 142, 250, 228, 67, 11, 113, 199, 100, 82,
|
||||
171, 125, 191, 97, 176, 40, 116, 30,
|
||||
167, 72, 122, 182, 163, 54, 249, 214, 194, 222, 209, 152, 92, 106, 185,
|
||||
239, 50, 204, 21, 149, 207, 47, 20,
|
||||
28, 15, 73, 196, 54, 211, 86, 221, 254, 217, 128, 90, 224, 59, 8, 19,
|
||||
70, 141, 242, 229, 133, 139, 35,
|
||||
39, 89, 218, 186, 219, 51, 27, 85, 203, 127, 23, 96, 14, 168, 4, 126,
|
||||
131, 96, 97, 232, 40, 78, 158,
|
||||
180, 104, 119, 110, 166, 172, 122, 253, 227, 1, 137, 192, 102, 208, 42,
|
||||
220, 31, 25, 200, 10, 214, 135, 30,
|
||||
226, 136, 73, 166, 182, 250, 246, 195, 6, 209, 194, 220, 81, 153, 252,
|
||||
106, 193, 239, 16, 76, 12, 53, 197,
|
||||
215, 19, 30, 141, 200, 101, 150, 171, 46, 255, 92, 64, 57, 240, 18,
|
||||
196, 13, 147, 69, 173, 243, 61, 133,
|
||||
209, 163, 28, 121, 201, 226, 214, 201, 158, 214, 232, 94, 206, 184, 84,
|
||||
114, 191, 101, 176, 43, 52, 31, 87,
|
||||
72, 62, 182, 144, 118, 236, 38, 205, 218, 213, 155, 31, 43, 72, 31,
|
||||
118, 136, 38, 230, 154, 202, 235, 23,
|
||||
15, 78, 132, 52, 99, 87, 105, 254, 174, 192, 124, 80, 33, 252, 24, 65,
|
||||
202, 176, 87, 52, 62, 151, 80,
|
||||
110, 188, 44, 113, 221, 228, 89, 139, 122, 231, 99, 10, 169, 199, 62,
|
||||
210, 144, 93, 172, 57, 189, 210, 241,
|
||||
157, 132, 105, 163, 110, 249, 236, 66, 205, 241, 149, 132, 111, 35,
|
||||
108, 25, 237, 202, 205, 151, 21, 174, 143,
|
||||
60, 100, 17, 235, 76, 79, 117, 244, 39, 7, 90, 130, 187, 33, 179, 88,
|
||||
117, 250, 167, 3, 58, 129, 211,
|
||||
32, 93, 216, 57, 154, 146, 235, 45, 143, 93, 164, 57, 187, 82, 243,
|
||||
125, 133, 225, 163, 8, 121, 198, 162,
|
||||
210, 249, 157, 130, 233, 161, 142, 248, 100, 66, 171, 113, 191, 100,
|
||||
112, 43, 100, 31, 107, 72, 47, 118, 156,
|
||||
38, 233, 218, 206, 219, 20, 91, 79, 123, 116, 35, 103, 89, 234, 186,
|
||||
207, 51, 20, 21, 207, 79, 20, 52,
|
||||
15, 87, 68, 62, 179, 80, 117, 252, 39, 1, 218, 128, 91, 32, 59, 88, 19,
|
||||
122, 141, 227, 37, 137, 219,
|
||||
38, 219, 90, 219, 123, 27, 99, 75, 105, 247, 110, 198, 172, 82, 253,
|
||||
253, 129, 129, 160, 96, 120, 40, 34,
|
||||
158, 153, 168, 106, 254, 175, 0, 124, 0, 33, 192, 24, 80, 10, 188, 7,
|
||||
49, 194, 148, 81, 175, 124, 124,
|
||||
33, 225, 216, 72, 90, 182, 187, 54, 243, 86, 197, 254, 211, 0, 93, 192,
|
||||
57, 144, 18, 236, 13, 141, 197,
|
||||
165, 147, 59, 45, 211, 93, 157, 249, 169, 130, 254, 225, 128, 72, 96,
|
||||
54, 168, 22, 254, 142, 192, 100, 80,
|
||||
43, 124, 31, 97, 200, 40, 86, 158, 190, 232, 112, 78, 164, 52, 123, 87,
|
||||
99, 126, 169, 224, 126, 200, 32,
|
||||
86, 152, 62, 234, 144, 79, 44, 52, 29, 215, 73, 158, 182, 232, 118,
|
||||
206, 166, 212, 122, 223, 99, 24, 41,
|
||||
202, 158, 215, 40, 94, 158, 184, 104, 114, 174, 165, 188, 123, 49, 227,
|
||||
84, 73, 255, 118, 192, 38, 208, 26,
|
||||
220, 11, 25, 199, 74, 210, 183, 29, 182, 137, 182, 230, 246, 202, 198,
|
||||
215, 18, 222, 141, 152, 101, 170, 171,
|
||||
63, 63, 80, 16, 60, 12, 17, 197, 204, 83, 21, 253, 207, 1, 148, 0, 111,
|
||||
64, 44, 48, 29, 212, 9,
|
||||
159, 70, 232, 50, 206, 149, 148, 111, 47, 108, 28, 45, 201, 221, 150,
|
||||
217, 174, 218, 252, 91, 1, 251, 64,
|
||||
67, 112, 49, 228, 20, 75, 79, 119, 116, 38, 167, 90, 250, 187, 3, 51,
|
||||
65, 213, 240, 95, 4, 56, 3,
|
||||
82, 129, 253, 160, 65, 184, 48, 114, 148, 37, 175, 91, 60, 59, 81, 211,
|
||||
124, 93, 225, 249, 136, 66, 230,
|
||||
177, 138, 244, 103, 7, 106, 130, 175, 33, 188, 24, 113, 202, 164, 87,
|
||||
59, 126, 147, 96, 109, 232, 45, 142,
|
||||
157, 164, 105, 187, 110, 243, 108, 69, 237, 243, 13, 133, 197, 163, 19,
|
||||
57, 205, 210, 213, 157, 159, 41, 168,
|
||||
30, 254, 136, 64, 102, 176, 42, 244, 31, 7, 72, 2, 182, 129, 182, 224,
|
||||
118, 200, 38, 214, 154, 222, 235,
|
||||
24, 79, 74, 180, 55, 55, 86, 150, 190, 238, 240, 76, 68, 53, 243, 87,
|
||||
5, 254, 131, 0, 97, 192, 40,
|
||||
80, 30, 188, 8, 113, 198, 164, 82, 251, 125, 131, 97, 161, 232, 120,
|
||||
78, 162, 180, 121, 183, 98, 246, 169,
|
||||
134, 254, 226, 192, 73, 144, 54, 236, 22, 205, 206, 213, 148, 95, 47,
|
||||
120, 28, 34, 137, 217, 166, 218, 250,
|
||||
219, 3, 27, 65, 203, 112, 87, 100, 62, 171, 80, 127, 124, 32, 33, 216,
|
||||
24, 90, 138, 187, 39, 51, 90,
|
||||
149, 251, 47, 3, 92, 1, 249, 192, 66, 208, 49, 156, 20, 105, 207, 110,
|
||||
212, 44, 95, 93, 248, 57, 130,
|
||||
146, 225, 173, 136, 125, 166, 161, 186, 248, 115, 2, 165, 193, 187, 16,
|
||||
115, 76, 37, 245, 219, 7, 27, 66,
|
||||
139, 113, 167, 100, 122, 171, 99, 63, 105, 208, 46, 220, 28, 89, 201,
|
||||
250, 214, 195, 30, 209, 200, 92, 86,
|
||||
185, 254, 242, 192, 69, 144, 51, 44, 21, 221, 207, 25, 148, 10, 239,
|
||||
71, 12, 50, 133, 213, 163, 31, 57,
|
||||
200, 18, 214, 141, 158, 229, 168, 75, 62, 183, 80, 118, 188, 38, 241,
|
||||
218, 196, 91, 19, 123, 77, 227, 117,
|
||||
137, 231, 38, 202, 154, 215, 43, 30, 159, 72, 104, 54, 174, 150, 252,
|
||||
110, 193, 236, 80, 77, 252, 53, 129,
|
||||
215, 32, 94, 152, 56, 106, 146, 175, 45, 188, 29, 177, 201, 180, 86,
|
||||
247, 126, 198, 160, 82, 248, 61, 130,
|
||||
145, 161, 172, 120, 125, 226, 161, 137, 184, 102, 242, 170, 197, 191,
|
||||
19, 48, 13, 212, 5, 159, 67, 40, 49,
|
||||
222, 148, 88, 111, 122, 172, 35, 61, 217, 209, 154, 220, 107, 25, 239,
|
||||
74, 204, 55, 21, 214, 143, 30, 228,
|
||||
8, 75, 70, 183, 114, 246, 165, 134, 251, 34, 195, 89, 145, 250, 236,
|
||||
67, 13, 241, 197, 132, 83, 35, 125,
|
||||
217, 225, 154, 200, 107, 22, 175, 78, 252, 52, 65, 215, 112, 94, 164,
|
||||
56, 123, 82, 163, 125, 185, 225, 178,
|
||||
200, 117, 150, 167, 46, 250, 156, 67, 41, 241, 222, 196, 88, 83, 122,
|
||||
189, 227, 49, 137, 212, 102, 223, 106,
|
||||
216, 47, 26, 156, 11, 41, 199, 94, 210, 184, 93, 178, 185, 181, 178,
|
||||
247, 53, 134, 151, 34, 238, 153, 140,
|
||||
106, 229, 239, 11, 12, 7, 69, 194, 179, 17, 181, 204, 119, 21, 230,
|
||||
143, 10, 228, 7, 11, 66, 135, 113,
|
||||
162, 164, 121, 187, 98, 243, 105, 133, 238, 227, 12, 73, 197, 246, 211,
|
||||
6, 221, 194, 217, 145, 154, 236, 107,
|
||||
13, 239, 69, 140, 51, 37, 213, 219, 31, 27, 72, 11, 118, 135, 102, 226,
|
||||
170, 201, 191, 22, 240, 14, 196,
|
||||
4, 83, 67, 125, 241, 225, 132, 72, 99, 118, 169, 230, 254, 202, 192,
|
||||
87, 16, 62, 140, 16, 101, 204, 43,
|
||||
21, 223, 79, 24, 52, 10, 151, 71, 46, 178, 156, 117, 169, 231, 62, 202,
|
||||
144, 87, 44, 62, 157, 208, 105,
|
||||
156, 46, 233, 220, 78, 217, 244, 90, 199, 123, 18, 163, 77, 185, 245,
|
||||
178, 199, 53, 146, 151, 45, 174, 157,
|
||||
188, 105, 177, 238, 244, 76, 71, 117, 242, 167, 5, 186, 131, 51, 33,
|
||||
213, 216, 95, 26, 184, 11, 50, 135,
|
||||
85, 162, 191, 57, 176, 18, 244, 13, 135, 69, 162, 179, 57, 181, 210,
|
||||
247, 29, 134, 137, 162, 230, 249, 138,
|
||||
194, 231, 17, 138, 140, 103, 37, 234, 155, 15, 43, 68, 31, 115, 72, 37,
|
||||
246, 155, 6, 235, 66, 207, 113,
|
||||
148, 36, 111, 91, 108, 59, 109, 211, 109, 157, 237, 169, 141, 190, 229,
|
||||
176, 75, 52, 55, 87, 86, 190, 190,
|
||||
240, 112, 68, 36, 51, 91, 85, 251, 127, 3, 96, 1, 232, 0, 78, 128, 52,
|
||||
96, 23, 104, 14, 174, 132,
|
||||
124, 99, 97, 233, 232, 78, 206, 180, 84, 119, 127, 102, 160, 42, 248,
|
||||
31, 2, 136, 1, 166, 128, 122, 224,
|
||||
35, 8, 25, 198, 138, 210, 231, 29, 138, 137, 167, 38, 250, 154, 195,
|
||||
43, 17, 223, 76, 88, 53, 250, 151,
|
||||
3, 46, 129, 220, 96, 89, 232, 58, 206, 147, 20, 109, 207, 109, 148, 45,
|
||||
175, 93, 188, 57, 177, 210, 244,
|
||||
93, 135, 121, 162, 162, 249, 185, 130, 242, 225, 133, 136, 99, 38, 169,
|
||||
218, 254, 219, 0, 91, 64, 59, 112,
|
||||
19, 100, 13, 235, 69, 143, 115, 36, 37, 219, 91, 27, 123, 75, 99, 119,
|
||||
105, 230, 174, 202, 252, 87, 1,
|
||||
254, 128, 64, 96, 48, 40, 20, 30, 143, 72, 100, 54, 171, 86, 255, 126,
|
||||
192, 32, 80, 24, 60, 10, 145,
|
||||
199, 44, 82, 157, 253, 169, 129, 190, 224, 112, 72, 36, 54, 155, 86,
|
||||
235, 126, 207, 96, 84, 40, 63, 94,
|
||||
144, 56, 108, 18, 173, 205, 189, 149, 177, 175, 52, 124, 23, 97, 206,
|
||||
168, 84, 126, 191, 96, 112, 40, 36,
|
||||
30, 155, 72, 107, 118, 175, 102, 252, 42, 193, 223, 16, 88, 12, 58,
|
||||
133, 211, 35, 29, 217, 201, 154, 214,
|
||||
235, 30, 207, 72, 84, 54, 191, 86, 240, 62, 196, 16, 83, 76, 61, 245,
|
||||
209, 135, 28, 98, 137, 233, 166,
|
||||
206, 250, 212, 67, 31, 113, 200, 36, 86, 155, 126, 235, 96, 79, 104,
|
||||
52, 46, 151, 92, 110, 185, 236, 114,
|
||||
205, 229, 149, 139, 47, 39, 92, 26, 185, 203, 50, 215, 85, 158, 191,
|
||||
40, 112, 30, 164, 8, 123, 70, 163,
|
||||
114, 249, 229, 130, 203, 33, 151, 88, 110, 186, 172, 115, 61, 229, 209,
|
||||
139, 28, 103, 73, 234, 182, 207, 54,
|
||||
212, 22, 223, 78, 216, 52, 90, 151, 123, 46, 163, 92, 121, 249, 226,
|
||||
194, 201, 145, 150, 236, 110, 205, 236,
|
||||
85, 141, 255, 37, 128, 27, 32, 11, 88, 7, 122, 130, 163, 33, 185, 216,
|
||||
114, 218, 165, 155, 59, 43, 83,
|
||||
95, 125, 248, 33, 130, 152, 97, 170, 168, 127, 62, 160, 16, 120, 12,
|
||||
34, 133, 217, 163, 26, 249, 203, 2,
|
||||
215, 65, 158, 176, 104, 116, 46, 167, 92, 122, 185, 227, 50, 201, 213,
|
||||
150, 223, 46, 216, 28, 90, 137, 251,
|
||||
38, 195, 90, 209, 251, 28, 67, 73, 241, 246, 196, 70, 211, 114, 221,
|
||||
229, 153,
|
||||
};
|
||||
|
||||
void scramble(unsigned char *inout)
|
||||
{
|
||||
unsigned char *r = inout + 12;
|
||||
unsigned char *s = yellowbook_scrambler;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 2340; i; i--) {
|
||||
*r++ ^= *s++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the P parities for the sector.
|
||||
* The 43 P vectors of length 24 are combined with the GF8_P_COEFFS.
|
||||
*/
|
||||
void parity_p(unsigned char *sector)
|
||||
{
|
||||
int i, j;
|
||||
unsigned char p0_msb, p1_msb;
|
||||
unsigned char p0_lsb, p1_lsb;
|
||||
unsigned char *p_msb_start, *p_lsb_start;
|
||||
unsigned char *p_msb, *p_lsb;
|
||||
unsigned char *coeffs0, *coeffs1;
|
||||
unsigned char *p0, *p1;
|
||||
unsigned char d;
|
||||
unsigned short c;
|
||||
|
||||
p_lsb_start = sector + LEC_HEADER_OFFSET;
|
||||
p_msb_start = sector + LEC_HEADER_OFFSET + 1;
|
||||
|
||||
p1 = sector + LEC_MODE1_P_PARITY_OFFSET;
|
||||
p0 = sector + LEC_MODE1_P_PARITY_OFFSET + 2 * 43;
|
||||
|
||||
for (i = 0; i <= 42; i++) {
|
||||
p_lsb = p_lsb_start;
|
||||
p_msb = p_msb_start;
|
||||
|
||||
coeffs0 = gf8_p_coeffs[0];
|
||||
coeffs1 = gf8_p_coeffs[1];
|
||||
|
||||
p0_lsb = p1_lsb = p0_msb = p1_msb = 0;
|
||||
|
||||
for (j = 0; j <= 23; j++) {
|
||||
d = *p_lsb;
|
||||
|
||||
if (d != 0) {
|
||||
c = gf8_log[d] + *coeffs0;
|
||||
if (c >= 255)
|
||||
c -= 255;
|
||||
p0_lsb ^= gf8_ilog[c];
|
||||
|
||||
c = gf8_log[d] + *coeffs1;
|
||||
if (c >= 255)
|
||||
c -= 255;
|
||||
p1_lsb ^= gf8_ilog[c];
|
||||
}
|
||||
|
||||
d = *p_msb;
|
||||
|
||||
if (d != 0) {
|
||||
c = gf8_log[d] + *coeffs0;
|
||||
if (c >= 255)
|
||||
c -= 255;
|
||||
p0_msb ^= gf8_ilog[c];
|
||||
|
||||
c = gf8_log[d] + *coeffs1;
|
||||
if (c >= 255)
|
||||
c -= 255;
|
||||
p1_msb ^= gf8_ilog[c];
|
||||
}
|
||||
|
||||
coeffs0++;
|
||||
coeffs1++;
|
||||
|
||||
p_lsb += 2 * 43;
|
||||
p_msb += 2 * 43;
|
||||
}
|
||||
|
||||
*p0 = p0_lsb;
|
||||
*(p0 + 1) = p0_msb;
|
||||
|
||||
*p1 = p1_lsb;
|
||||
*(p1 + 1) = p1_msb;
|
||||
|
||||
p0 += 2;
|
||||
p1 += 2;
|
||||
|
||||
p_lsb_start += 2;
|
||||
p_msb_start += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the Q parities for the sector.
|
||||
* The 26 Q vectors of length 43 are combined with the GF8_Q_COEFFS.
|
||||
*/
|
||||
void parity_q(unsigned char *sector)
|
||||
{
|
||||
int i, j;
|
||||
unsigned char q0_msb, q1_msb;
|
||||
unsigned char q0_lsb, q1_lsb;
|
||||
unsigned char *q_msb_start, *q_lsb_start;
|
||||
unsigned char *q_msb, *q_lsb;
|
||||
unsigned char *coeffs0, *coeffs1;
|
||||
unsigned char *q0, *q1, *q_start;
|
||||
unsigned char d;
|
||||
unsigned short c;
|
||||
|
||||
q_lsb_start = sector + LEC_HEADER_OFFSET;
|
||||
q_msb_start = sector + LEC_HEADER_OFFSET + 1;
|
||||
|
||||
q_start = sector + LEC_MODE1_Q_PARITY_OFFSET;
|
||||
q1 = sector + LEC_MODE1_Q_PARITY_OFFSET;
|
||||
q0 = sector + LEC_MODE1_Q_PARITY_OFFSET + 2 * 26;
|
||||
|
||||
for (i = 0; i <= 25; i++) {
|
||||
q_lsb = q_lsb_start;
|
||||
q_msb = q_msb_start;
|
||||
|
||||
coeffs0 = gf8_q_coeffs[0];
|
||||
coeffs1 = gf8_q_coeffs[1];
|
||||
|
||||
q0_lsb = q1_lsb = q0_msb = q1_msb = 0;
|
||||
|
||||
for (j = 0; j <= 42; j++) {
|
||||
d = *q_lsb;
|
||||
|
||||
if (d != 0) {
|
||||
c = gf8_log[d] + *coeffs0;
|
||||
if (c >= 255)
|
||||
c -= 255;
|
||||
q0_lsb ^= gf8_ilog[c];
|
||||
|
||||
c = gf8_log[d] + *coeffs1;
|
||||
if (c >= 255)
|
||||
c -= 255;
|
||||
q1_lsb ^= gf8_ilog[c];
|
||||
}
|
||||
|
||||
d = *q_msb;
|
||||
|
||||
if (d != 0) {
|
||||
c = gf8_log[d] + *coeffs0;
|
||||
if (c >= 255)
|
||||
c -= 255;
|
||||
q0_msb ^= gf8_ilog[c];
|
||||
|
||||
c = gf8_log[d] + *coeffs1;
|
||||
if (c >= 255)
|
||||
c -= 255;
|
||||
q1_msb ^= gf8_ilog[c];
|
||||
}
|
||||
|
||||
coeffs0++;
|
||||
coeffs1++;
|
||||
|
||||
q_lsb += 2 * 44;
|
||||
q_msb += 2 * 44;
|
||||
|
||||
if (q_lsb >= q_start) {
|
||||
q_msb -= 2 * 1118;
|
||||
q_lsb -= 2 * 1118;
|
||||
}
|
||||
}
|
||||
|
||||
*q0 = q0_lsb;
|
||||
*(q0 + 1) = q0_msb;
|
||||
|
||||
*q1 = q1_lsb;
|
||||
*(q1 + 1) = q1_msb;
|
||||
|
||||
q0 += 2;
|
||||
q1 += 2;
|
||||
|
||||
q_lsb_start += 2 * 43;
|
||||
q_msb_start += 2 * 43;
|
||||
}
|
||||
}
|
12
libburn/lec.h
Normal file
12
libburn/lec.h
Normal file
@ -0,0 +1,12 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef __LEC
|
||||
#define __LEC
|
||||
|
||||
#define RS_L12_BITS 8
|
||||
|
||||
void scramble(unsigned char *);
|
||||
void parity_p(unsigned char *in);
|
||||
void parity_q(unsigned char *in);
|
||||
|
||||
#endif /* __LEC */
|
928
libburn/libburn.h
Normal file
928
libburn/libburn.h
Normal file
@ -0,0 +1,928 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef LIBBURN_H
|
||||
#define LIBBURN_H
|
||||
|
||||
/* Needed for off_t which is the (POSIX-ly) appropriate type for
|
||||
expressing a file or stream size.
|
||||
|
||||
XXX we should enforce 64-bitness for off_t
|
||||
*/
|
||||
#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;
|
||||
|
||||
/** 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
|
||||
*/
|
||||
#define BURN_AUDIO (1 << 6)
|
||||
/** Track mode modifier - 4 channel audio. */
|
||||
#define BURN_4CH (1 << 7)
|
||||
/** Track mode modifier - Digital copy permitted, can be set on any track.*/
|
||||
#define BURN_COPY (1 << 8)
|
||||
/** Track mode modifier - 50/15uS pre-emphasis */
|
||||
#define BURN_PREEMPHASIS (1 << 9)
|
||||
/** Input mode modifier - subcodes present packed 16 */
|
||||
#define BURN_SUBCODE_P16 (1 << 10)
|
||||
/** Input mode modifier - subcodes present packed 96 */
|
||||
#define BURN_SUBCODE_P96 (1 << 11)
|
||||
/** Input mode modifier - subcodes present raw 96 */
|
||||
#define BURN_SUBCODE_R96 (1 << 12)
|
||||
|
||||
/** Possible disc writing style/modes */
|
||||
enum burn_write_types
|
||||
{
|
||||
/** Packet writing.
|
||||
currently unsupported
|
||||
*/
|
||||
BURN_WRITE_PACKET,
|
||||
/** Track At Once recording.
|
||||
2s gaps between tracks, no fonky lead-ins
|
||||
*/
|
||||
BURN_WRITE_TAO,
|
||||
/** Session At Once.
|
||||
block type MUST be BURN_BLOCK_SAO
|
||||
*/
|
||||
BURN_WRITE_SAO,
|
||||
/** Raw disc at once recording.
|
||||
all subcodes must be provided by lib or user
|
||||
only raw block types are supported
|
||||
*/
|
||||
BURN_WRITE_RAW
|
||||
};
|
||||
|
||||
/** Data format to send to the drive */
|
||||
enum burn_block_types
|
||||
{
|
||||
/** sync, headers, edc/ecc provided by lib/user */
|
||||
BURN_BLOCK_RAW0 = 1,
|
||||
/** sync, headers, edc/ecc and p/q subs provided by lib/user */
|
||||
BURN_BLOCK_RAW16 = 2,
|
||||
/** sync, headers, edc/ecc and packed p-w subs provided by lib/user */
|
||||
BURN_BLOCK_RAW96P = 4,
|
||||
/** sync, headers, edc/ecc and raw p-w subs provided by lib/user */
|
||||
BURN_BLOCK_RAW96R = 8,
|
||||
/** only 2048 bytes of user data provided by lib/user */
|
||||
BURN_BLOCK_MODE1 = 256,
|
||||
/** 2336 bytes of user data provided by lib/user */
|
||||
BURN_BLOCK_MODE2R = 512,
|
||||
/** 2048 bytes of user data provided by lib/user
|
||||
subheader provided in write parameters
|
||||
are we ever going to support this shit? I vote no.
|
||||
(supposed to be supported on all drives...)
|
||||
*/
|
||||
BURN_BLOCK_MODE2_PATHETIC = 1024,
|
||||
/** 2048 bytes of data + 8 byte subheader provided by lib/user
|
||||
hey, this is also dumb
|
||||
*/
|
||||
BURN_BLOCK_MODE2_LAME = 2048,
|
||||
/** 2324 bytes of data provided by lib/user
|
||||
subheader provided in write parameters
|
||||
no sir, I don't like it.
|
||||
*/
|
||||
BURN_BLOCK_MODE2_OBSCURE = 4096,
|
||||
/** 2332 bytes of data supplied by lib/user
|
||||
8 bytes sub header provided in write parameters
|
||||
this is the second least suck mode2, and is mandatory for
|
||||
all drives to support.
|
||||
*/
|
||||
BURN_BLOCK_MODE2_OK = 8192,
|
||||
/** SAO block sizes are based on cue sheet, so use this. */
|
||||
BURN_BLOCK_SAO = 16384
|
||||
};
|
||||
|
||||
/** Possible status' of the drive in regard to the disc in it. */
|
||||
enum burn_disc_status
|
||||
{
|
||||
/** The current status is not yet known */
|
||||
BURN_DISC_UNREADY,
|
||||
/** The drive holds a blank disc */
|
||||
BURN_DISC_BLANK,
|
||||
/** There is no disc at all in the drive */
|
||||
BURN_DISC_EMPTY,
|
||||
/** There is an incomplete disc in the drive */
|
||||
BURN_DISC_APPENDABLE,
|
||||
/** There is a disc with data on it in the drive */
|
||||
BURN_DISC_FULL
|
||||
};
|
||||
|
||||
/** Possible types of messages form the library. */
|
||||
enum burn_message_type
|
||||
{
|
||||
/** Diagnostic/Process information. For the curious user. */
|
||||
BURN_MESSAGE_INFO,
|
||||
/** A warning regarding a possible problem. The user should probably
|
||||
be notified, but its not fatal. */
|
||||
BURN_MESSAGE_WARNING,
|
||||
/** An error message. This usually means the current process will be
|
||||
aborted, and the user should definately see these. */
|
||||
BURN_MESSAGE_ERROR
|
||||
};
|
||||
|
||||
/** Possible information messages */
|
||||
enum burn_message_info
|
||||
{
|
||||
BURN_INFO_FOO
|
||||
};
|
||||
|
||||
/** Possible warning messages */
|
||||
enum burn_message_warning
|
||||
{
|
||||
BURN_WARNING_FOO
|
||||
};
|
||||
|
||||
/** Possible error messages */
|
||||
enum burn_message_error
|
||||
{
|
||||
BURN_ERROR_CANCELLED
|
||||
};
|
||||
|
||||
/** 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
|
||||
};
|
||||
|
||||
/** Information about a track on a disc - this is from the q sub channel of the
|
||||
lead-in area of a disc. The documentation here is very terse.
|
||||
See a document such as mmc3 for proper information.
|
||||
*/
|
||||
struct burn_toc_entry
|
||||
{
|
||||
/** Session the track is in */
|
||||
unsigned char session;
|
||||
/** Type of data. for this struct to be valid, it must be 1 */
|
||||
unsigned char adr;
|
||||
/** Type of data in the track */
|
||||
unsigned char control;
|
||||
/** Zero. Always. Really. */
|
||||
unsigned char tno;
|
||||
/** Track number or special information */
|
||||
unsigned char point;
|
||||
unsigned char min;
|
||||
unsigned char sec;
|
||||
unsigned char frame;
|
||||
unsigned char zero;
|
||||
/** Track start time minutes for normal tracks */
|
||||
unsigned char pmin;
|
||||
/** Track start time seconds for normal tracks */
|
||||
unsigned char psec;
|
||||
/** Track start time frames for normal tracks */
|
||||
unsigned char pframe;
|
||||
};
|
||||
|
||||
|
||||
/** Data source for tracks */
|
||||
struct burn_source {
|
||||
/** Reference count for the data source. Should be 1 when a new source
|
||||
is created. Increment it to take a reference for yourself. Use
|
||||
burn_source_free to destroy your reference to it. */
|
||||
int refcount;
|
||||
|
||||
/** Read data from the source */
|
||||
int (*read)(struct burn_source *,
|
||||
unsigned char *buffer,
|
||||
int size);
|
||||
|
||||
/** Read subchannel data from the source (NULL if lib generated) */
|
||||
int (*read_sub)(struct burn_source *,
|
||||
unsigned char *buffer,
|
||||
int size);
|
||||
|
||||
/** Get the size of the source's data */
|
||||
off_t (*get_size)(struct burn_source *);
|
||||
|
||||
/** Clean up the source specific data */
|
||||
void (*free_data)(struct burn_source *);
|
||||
|
||||
/** Next source, for when a source runs dry and padding is disabled
|
||||
THIS IS AUTOMATICALLY HANDLED, DO NOT TOUCH
|
||||
*/
|
||||
struct burn_source *next;
|
||||
|
||||
/** Source specific data */
|
||||
void *data;
|
||||
};
|
||||
|
||||
|
||||
/** Information on a drive in the system */
|
||||
struct burn_drive_info
|
||||
{
|
||||
/** Name of the vendor of the drive */
|
||||
char vendor[9];
|
||||
/** Name of the drive */
|
||||
char product[17];
|
||||
/** Revision of the drive */
|
||||
char revision[5];
|
||||
/** Location of the drive in the filesystem. */
|
||||
char location[17];
|
||||
|
||||
/** Can the drive read DVD-RAM discs */
|
||||
unsigned int read_dvdram:1;
|
||||
/** Can the drive read DVD-R discs */
|
||||
unsigned int read_dvdr:1;
|
||||
/** Can the drive read DVD-ROM discs */
|
||||
unsigned int read_dvdrom:1;
|
||||
/** Can the drive read CD-R discs */
|
||||
unsigned int read_cdr:1;
|
||||
/** Can the drive read CD-RW discs */
|
||||
unsigned int read_cdrw:1;
|
||||
|
||||
/** Can the drive write DVD-RAM discs */
|
||||
unsigned int write_dvdram:1;
|
||||
/** Can the drive write DVD-R discs */
|
||||
unsigned int write_dvdr:1;
|
||||
/** Can the drive write CD-R discs */
|
||||
unsigned int write_cdr:1;
|
||||
/** Can the drive write CD-RW discs */
|
||||
unsigned int write_cdrw:1;
|
||||
|
||||
/** Can the drive simulate a write */
|
||||
unsigned int write_simulate:1;
|
||||
|
||||
/** Can the drive report C2 errors */
|
||||
unsigned int c2_errors:1;
|
||||
|
||||
/** The size of the drive's buffer (in kilobytes) */
|
||||
int buffer_size;
|
||||
/**
|
||||
* The supported block types in tao mode.
|
||||
* They should be tested with the desired block type.
|
||||
* See also burn_block_types.
|
||||
*/
|
||||
int tao_block_types;
|
||||
/**
|
||||
* The supported block types in sao mode.
|
||||
* They should be tested with the desired block type.
|
||||
* See also burn_block_types.
|
||||
*/
|
||||
int sao_block_types;
|
||||
/**
|
||||
* The supported block types in raw mode.
|
||||
* They should be tested with the desired block type.
|
||||
* See also burn_block_types.
|
||||
*/
|
||||
int raw_block_types;
|
||||
/**
|
||||
* The supported block types in packet mode.
|
||||
* They should be tested with the desired block type.
|
||||
* See also burn_block_types.
|
||||
*/
|
||||
int packet_block_types;
|
||||
|
||||
/** The value by which this drive can be indexed when using functions
|
||||
in the library. This is the value to pass to all libbburn functions
|
||||
that operate on a drive. */
|
||||
struct burn_drive *drive;
|
||||
};
|
||||
|
||||
/** Messages from the library */
|
||||
struct burn_message
|
||||
{
|
||||
/** The drive associated with the message. NULL if the error is not
|
||||
related to a specific drive. */
|
||||
struct burn_drive *drive;
|
||||
|
||||
/** The type of message this is. See message_type for details. */
|
||||
enum burn_message_type type;
|
||||
|
||||
/** The actual message */
|
||||
union detail {
|
||||
struct {
|
||||
enum burn_message_info message;
|
||||
} info;
|
||||
struct {
|
||||
enum burn_message_warning message;
|
||||
} warning;
|
||||
struct {
|
||||
enum burn_message_error message;
|
||||
} error;
|
||||
} detail;
|
||||
};
|
||||
|
||||
/** 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;
|
||||
/** The number of sector */
|
||||
int sectors;
|
||||
/** The current sector being processed */
|
||||
int sector;
|
||||
};
|
||||
|
||||
/** 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.
|
||||
If is possible to 'restart' the library by shutting it down and
|
||||
re-initializing it, though there is no good reason to do that.
|
||||
@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);
|
||||
|
||||
/** 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);
|
||||
|
||||
/** Returns a newly allocated burn_message structure. This message should be
|
||||
freed with burn_message_free() when you are finished with it.
|
||||
@return A message or NULL when there are no more messages to retrieve.
|
||||
*/
|
||||
struct burn_message* burn_get_message(void);
|
||||
|
||||
/** Frees a burn_message structure */
|
||||
void burn_message_free(struct burn_message *msg);
|
||||
|
||||
/** Scans for drives. This function MUST be called until it returns nonzero.
|
||||
No drives can be in use when this is called or it will assert.
|
||||
All drive pointers are invalidated by using this function. Do NOT store
|
||||
drive pointers across calls to this function or death AND pain will ensue.
|
||||
When the app is done with the burn_drive_info array, it must be freed with
|
||||
burn_drive_info_free()
|
||||
@param drives Returns an array of drives (cdroms/burners). The returned
|
||||
array should be freed when it is no longer needed, and
|
||||
before calling this function again to rescan.
|
||||
@param n_drives Returns the number of hardware drives in @c drives.
|
||||
@return Zero while scanning is not complete; non-zero when it is finished.
|
||||
*/
|
||||
int burn_drive_scan(struct burn_drive_info *drives[],
|
||||
unsigned int *n_drives);
|
||||
/** Frees a burn_drive_info array returned by burn_drive_scan
|
||||
@param info The array to free
|
||||
*/
|
||||
void burn_drive_info_free(struct burn_drive_info *info);
|
||||
|
||||
/** Grab a drive. This must be done before the drive can be used (for reading,
|
||||
writing, etc). It may be neccesary to call this function more than once
|
||||
to grab a drive. See burn_grab for details.
|
||||
@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 the drive has been grabbed, else 0
|
||||
*/
|
||||
int burn_drive_grab(struct burn_drive *drive, int load);
|
||||
|
||||
/** Release a drive. This should not be done until the drive is no longer
|
||||
busy (see burn_drive_get_status).
|
||||
@param drive The drive to release.
|
||||
@param eject Nonzero to make the drive eject the disc in it.
|
||||
*/
|
||||
void burn_drive_release(struct burn_drive *drive, int eject);
|
||||
|
||||
/** Returns what kind of disc a drive is holding. This function may need to be
|
||||
called more than once to get a proper status from it. See burn_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.
|
||||
*/
|
||||
enum burn_disc_status burn_disc_get_status(struct burn_drive *drive);
|
||||
|
||||
/** Tells whether a disc can be erased or not
|
||||
@return Non-zero means erasable
|
||||
*/
|
||||
int burn_disc_erasable(struct burn_drive *d);
|
||||
|
||||
/** Returns the progress and status of a drive.
|
||||
@param drive The drive to query busy state for.
|
||||
@param p Returns the progress of the operation, NULL if you don't care
|
||||
@return the current status of the drive. See also burn_drive_status.
|
||||
*/
|
||||
enum burn_drive_status burn_drive_get_status(struct burn_drive *drive,
|
||||
struct burn_progress *p);
|
||||
|
||||
/** Creates a write_opts struct for burning to the specified drive
|
||||
must be freed with burn_write_opts_free
|
||||
@param drive The drive to write with
|
||||
@return The write_opts
|
||||
*/
|
||||
struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive);
|
||||
|
||||
/** Frees a write_opts struct created with burn_write_opts_new
|
||||
@param opts write_opts to free
|
||||
*/
|
||||
void burn_write_opts_free(struct burn_write_opts *opts);
|
||||
|
||||
/** Creates a write_opts struct for reading from the specified drive
|
||||
must be freed with burn_write_opts_free
|
||||
@param drive The drive to read from
|
||||
@return The read_opts
|
||||
*/
|
||||
struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive);
|
||||
|
||||
/** Frees a read_opts struct created with burn_read_opts_new
|
||||
@param opts write_opts to free
|
||||
*/
|
||||
void burn_read_opts_free(struct burn_read_opts *opts);
|
||||
|
||||
/** Erase a disc in the drive. The drive must be grabbed successfully BEFORE
|
||||
calling this functions. Always ensure that the drive reports a status of
|
||||
BURN_DISC_FULL before calling this function. An erase operation is not
|
||||
cancellable, as control of the operation is passed wholly to the drive and
|
||||
there is no way to interrupt it safely.
|
||||
@param drive The drive with which to erase a disc.
|
||||
@param fast Nonzero to do a fast erase, where only the disc's headers are
|
||||
erased; zero to erase the entire disc.
|
||||
*/
|
||||
void burn_disc_erase(struct burn_drive *drive, int fast);
|
||||
|
||||
/** Read a disc from the drive and write it to an fd pair. The drive must be
|
||||
grabbed successfully BEFORE calling this function. Always ensure that the
|
||||
drive reports a status of BURN_DISC_FULL before calling this function.
|
||||
@param drive The drive from which to read a disc.
|
||||
@param o The options for the read operation.
|
||||
*/
|
||||
void burn_disc_read(struct burn_drive *drive, const struct burn_read_opts *o);
|
||||
|
||||
/** Write a disc in the drive. The drive must be grabbed successfully BEFORE
|
||||
calling this function. Always ensure that the drive reports a status of
|
||||
BURN_DISC_BLANK or BURN_STATUS_FULL (to append a new session to the
|
||||
disc) before calling this function.
|
||||
@param o The options for the writing operation.
|
||||
@param disc The struct burn_disc * that described the disc to be created
|
||||
*/
|
||||
void burn_disc_write(struct burn_write_opts *o, struct burn_disc *disc);
|
||||
|
||||
/** Cancel an operation on a drive.
|
||||
This will only work when the drive's busy state is BURN_DRIVE_READING or
|
||||
BURN_DRIVE_WRITING.
|
||||
@param drive The drive on which to cancel the current operation.
|
||||
*/
|
||||
void burn_drive_cancel(struct burn_drive *drive);
|
||||
|
||||
/** Convert a minute-second-frame (MSF) value to sector count
|
||||
@param m Minute component
|
||||
@param s Second component
|
||||
@param f Frame component
|
||||
@return The sector count
|
||||
*/
|
||||
int burn_msf_to_sectors(int m, int s, int f);
|
||||
|
||||
/** Convert a sector count to minute-second-frame (MSF)
|
||||
@param sectors The sector count
|
||||
@param m Returns the minute component
|
||||
@param s Returns the second component
|
||||
@param f Returns the frame component
|
||||
*/
|
||||
void burn_sectors_to_msf(int sectors, int *m, int *s, int *f);
|
||||
|
||||
/** Convert a minute-second-frame (MSF) value to an lba
|
||||
@param m Minute component
|
||||
@param s Second component
|
||||
@param f Frame component
|
||||
@return The lba
|
||||
*/
|
||||
int burn_msf_to_lba(int m, int s, int f);
|
||||
|
||||
/** Convert an lba to minute-second-frame (MSF)
|
||||
@param lba The lba
|
||||
@param m Returns the minute component
|
||||
@param s Returns the second component
|
||||
@param f Returns the frame component
|
||||
*/
|
||||
void burn_lba_to_msf(int lba, int *m, int *s, int *f);
|
||||
|
||||
/** Create a new disc (for DAO recording)*/
|
||||
struct burn_disc *burn_disc_create(void);
|
||||
|
||||
/** Delete disc and decrease the reference count on all its sessions
|
||||
@param d The disc to be freed
|
||||
*/
|
||||
void burn_disc_free(struct burn_disc *d);
|
||||
|
||||
/** Create a new session (For SAO at once recording, or to be added to a
|
||||
disc for DAO)
|
||||
*/
|
||||
struct burn_session *burn_session_create(void);
|
||||
|
||||
/** Free a session (and decrease reference count on all tracks inside)
|
||||
@param s Session to be freed
|
||||
*/
|
||||
void burn_session_free(struct burn_session *s);
|
||||
|
||||
/** Add a session to a disc at a specific position, increasing the
|
||||
sessions's reference count.
|
||||
@param d Disc to add the session to
|
||||
@param s Session to add to the disc
|
||||
@param pos position to add at (BURN_POS_END is "at the end")
|
||||
@return 0 for failure, 1 for success
|
||||
*/
|
||||
int burn_disc_add_session(struct burn_disc *d, struct burn_session *s,
|
||||
unsigned int pos);
|
||||
|
||||
/** Remove a session from a disc
|
||||
@param d Disc to remove session from
|
||||
@param s Session pointer to find and remove
|
||||
*/
|
||||
int burn_disc_remove_session(struct burn_disc *d, struct burn_session *s);
|
||||
|
||||
|
||||
/** Create a track (for TAO recording, or to put in a session) */
|
||||
struct burn_track *burn_track_create(void);
|
||||
|
||||
/** Free a track
|
||||
@param t Track to free
|
||||
*/
|
||||
void burn_track_free(struct burn_track *t);
|
||||
|
||||
/** Add a track to a session at specified position
|
||||
@param s Session to add to
|
||||
@param t Track to insert in session
|
||||
@param pos position to add at (BURN_POS_END is "at the end")
|
||||
@return 0 for failure, 1 for success
|
||||
*/
|
||||
int burn_session_add_track(struct burn_session *s, struct burn_track *t,
|
||||
unsigned int pos);
|
||||
|
||||
/** Remove a track from a session
|
||||
@param s Session to remove track from
|
||||
@param t Track pointer to find and remove
|
||||
@return 0 for failure, 1 for success
|
||||
*/
|
||||
int burn_session_remove_track(struct burn_session *s, struct burn_track *t);
|
||||
|
||||
|
||||
/** Define the data in a track
|
||||
@param t the track to define
|
||||
@param offset The lib will write this many 0s before start of data
|
||||
@param tail The number of extra 0s to write after data
|
||||
@param pad 1 means the lib should pad the last sector with 0s if the
|
||||
track isn't exactly sector sized. (otherwise the lib will
|
||||
begin reading from the next track)
|
||||
@param mode data format (bitfield)
|
||||
*/
|
||||
void burn_track_define_data(struct burn_track *t, int offset, int tail,
|
||||
int pad, int mode);
|
||||
|
||||
/** Set the ISRC details for a track
|
||||
@param t The track to change
|
||||
@param country the 2 char country code. Each character must be
|
||||
only numbers or letters.
|
||||
@param owner 3 char owner code. Each character must be only numbers
|
||||
or letters.
|
||||
@param year 2 digit year. A number in 0-99 (Yep, not Y2K friendly).
|
||||
@param serial 5 digit serial number. A number in 0-99999.
|
||||
*/
|
||||
void burn_track_set_isrc(struct burn_track *t, char *country, char *owner,
|
||||
unsigned char year, unsigned int serial);
|
||||
|
||||
/** Disable ISRC parameters for a track
|
||||
@param t The track to change
|
||||
*/
|
||||
void burn_track_clear_isrc(struct burn_track *t);
|
||||
|
||||
/** Hide the first track in the "pre gap" of the disc
|
||||
@param s session to change
|
||||
@param onoff 1 to enable hiding, 0 to disable
|
||||
*/
|
||||
void burn_session_hide_first_track(struct burn_session *s, int onoff);
|
||||
|
||||
/** Get the drive's disc struct - free when done
|
||||
@param d drive to query
|
||||
@return the disc struct
|
||||
*/
|
||||
struct burn_disc *burn_drive_get_disc(struct burn_drive *d);
|
||||
|
||||
/** Set the track's data source
|
||||
@param t The track to set the data source for
|
||||
@param s The data source to use for the contents of the track
|
||||
@return An error code stating if the source is ready for use for
|
||||
writing the track, or if an error occured
|
||||
|
||||
*/
|
||||
enum burn_source_status burn_track_set_source(struct burn_track *t,
|
||||
struct burn_source *s);
|
||||
|
||||
/** Free a burn_source (decrease its refcount and maybe free it)
|
||||
@param s Source to free
|
||||
*/
|
||||
void burn_source_free(struct burn_source *s);
|
||||
|
||||
/** Creates a data source for an image file (and maybe subcode file) */
|
||||
struct burn_source *burn_file_source_new(const char *path,
|
||||
const char *subpath);
|
||||
|
||||
/** Creates a data source for an image file (resp. a track) from an open
|
||||
readable filedescriptor, an eventually open readable subcodes file
|
||||
descriptor and eventually a fixed size in bytes.
|
||||
@param datafd The source of data.
|
||||
@param subfd The eventual source for subcodes. Not used if -1.
|
||||
@param size The eventual fixed size of eventually both fds.
|
||||
If this value is 0, the size will be determined from datafd.
|
||||
*/
|
||||
struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size);
|
||||
|
||||
/** Tells how long a track will be on disc */
|
||||
int burn_track_get_sectors(struct burn_track *);
|
||||
|
||||
|
||||
/** Sets drive read and write speed
|
||||
@param d The drive to set speed for
|
||||
@param read Read speed in k/s (0 is max)
|
||||
@param write Write speed in k/s (0 is max)
|
||||
*/
|
||||
void burn_drive_set_speed(struct burn_drive *d, int read, int write);
|
||||
|
||||
/* these are for my debugging, they will disappear */
|
||||
void burn_structure_print_disc(struct burn_disc *d);
|
||||
void burn_structure_print_session(struct burn_session *s);
|
||||
void burn_structure_print_track(struct burn_track *t);
|
||||
|
||||
/** Sets the write type for the write_opts struct
|
||||
@param opts The write opts to change
|
||||
@param write_type The write type to use
|
||||
@param block_type The block type to use
|
||||
@return Returns 1 on success and 0 on failure.
|
||||
*/
|
||||
int burn_write_opts_set_write_type(struct burn_write_opts *opts,
|
||||
enum burn_write_types write_type,
|
||||
int block_type);
|
||||
|
||||
/** Supplies toc entries for writing - not normally required for cd mastering
|
||||
@param opts The write opts to change
|
||||
@param count The number of entries
|
||||
@param toc_entries
|
||||
*/
|
||||
void burn_write_opts_set_toc_entries(struct burn_write_opts *opts,
|
||||
int count,
|
||||
struct burn_toc_entry *toc_entries);
|
||||
|
||||
/** Sets the session format for a disc
|
||||
@param opts The write opts to change
|
||||
@param format The session format to set
|
||||
*/
|
||||
void burn_write_opts_set_format(struct burn_write_opts *opts, int format);
|
||||
|
||||
/** Sets the simulate value for the write_opts struct
|
||||
@param opts The write opts to change
|
||||
@param sim If non-zero, the drive will perform a simulation instead of a burn
|
||||
@return Returns 1 on success and 0 on failure.
|
||||
*/
|
||||
int burn_write_opts_set_simulate(struct burn_write_opts *opts, int sim);
|
||||
|
||||
/** Controls buffer underrun prevention
|
||||
@param opts The write opts to change
|
||||
@param underrun_proof if non-zero, buffer underrun protection is enabled
|
||||
@return Returns 1 on success and 0 on failure.
|
||||
*/
|
||||
int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts,
|
||||
int underrun_proof);
|
||||
|
||||
/** Sets whether to use opc or not with the write_opts struct
|
||||
@param opts The write opts to change
|
||||
@param opc If non-zero, optical power calibration will be performed at
|
||||
start of burn
|
||||
|
||||
*/
|
||||
void burn_write_opts_set_perform_opc(struct burn_write_opts *opts, int opc);
|
||||
|
||||
void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts, int has_mediacatalog);
|
||||
|
||||
void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, unsigned char mediacatalog[13]);
|
||||
|
||||
/** Sets whether to read in raw mode or not
|
||||
@param opts The read opts to change
|
||||
@param raw_mode If non-zero, reading will be done in raw mode, so that everything in the data tracks on the
|
||||
disc is read, including headers.
|
||||
*/
|
||||
void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw_mode);
|
||||
|
||||
/** Sets whether to report c2 errors or not
|
||||
@param opts The read opts to change
|
||||
@param c2errors If non-zero, report c2 errors.
|
||||
*/
|
||||
void burn_read_opts_set_c2errors(struct burn_read_opts *opts, int c2errors);
|
||||
|
||||
/** Sets whether to read subcodes from audio tracks or not
|
||||
@param opts The read opts to change
|
||||
@param subcodes_audio If non-zero, read subcodes from audio tracks on the disc.
|
||||
*/
|
||||
void burn_read_opts_read_subcodes_audio(struct burn_read_opts *opts,
|
||||
int subcodes_audio);
|
||||
|
||||
/** Sets whether to read subcodes from data tracks or not
|
||||
@param opts The read opts to change
|
||||
@param subcodes_data If non-zero, read subcodes from data tracks on the disc.
|
||||
*/
|
||||
void burn_read_opts_read_subcodes_data(struct burn_read_opts *opts,
|
||||
int subcodes_data);
|
||||
|
||||
/** Sets whether to recover errors if possible
|
||||
@param opts The read opts to change
|
||||
@param hardware_error_recovery If non-zero, attempt to recover errors if possible.
|
||||
*/
|
||||
void burn_read_opts_set_hardware_error_recovery(struct burn_read_opts *opts,
|
||||
int hardware_error_recovery);
|
||||
|
||||
/** Sets whether to report recovered errors or not
|
||||
@param opts The read opts to change
|
||||
@param report_recovered_errors If non-zero, recovered errors will be reported.
|
||||
*/
|
||||
void burn_read_opts_report_recovered_errors(struct burn_read_opts *opts,
|
||||
int report_recovered_errors);
|
||||
|
||||
/** Sets whether blocks with unrecoverable errors should be read or not
|
||||
@param opts The read opts to change
|
||||
@param transfer_damaged_blocks If non-zero, blocks with unrecoverable errors will still be read.
|
||||
*/
|
||||
void burn_read_opts_transfer_damaged_blocks(struct burn_read_opts *opts,
|
||||
int transfer_damaged_blocks);
|
||||
|
||||
/** Sets the number of retries to attempt when trying to correct an error
|
||||
@param opts The read opts to change
|
||||
@param hardware_error_retries The number of retries to attempt when correcting an error.
|
||||
*/
|
||||
void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts,
|
||||
unsigned char hardware_error_retries);
|
||||
|
||||
/** Gets the maximum write speed for a drive
|
||||
@param d Drive to query
|
||||
@return Maximum write speed in K/s
|
||||
*/
|
||||
int burn_drive_get_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);
|
||||
|
||||
/** Gets a copy of the toc_entry structure associated with a track
|
||||
@param t Track to get the entry from
|
||||
@param entry Struct for the library to fill out
|
||||
*/
|
||||
void burn_track_get_entry(struct burn_track *t, struct burn_toc_entry *entry);
|
||||
|
||||
/** Gets a copy of the toc_entry structure associated with a session's lead out
|
||||
@param s Session to get the entry from
|
||||
@param entry Struct for the library to fill out
|
||||
*/
|
||||
void burn_session_get_leadout_entry(struct burn_session *s,
|
||||
struct burn_toc_entry *entry);
|
||||
|
||||
/** Gets an array of all the sessions for the disc
|
||||
THIS IS NO LONGER VALID AFTER YOU ADD OR REMOVE A SESSION
|
||||
@param d Disc to get session array for
|
||||
@param num Returns the number of sessions in the array
|
||||
@return array of sessions
|
||||
*/
|
||||
struct burn_session **burn_disc_get_sessions(struct burn_disc *d,
|
||||
int *num);
|
||||
|
||||
int burn_disc_get_sectors(struct burn_disc *d);
|
||||
|
||||
/** Gets an array of all the tracks for a session
|
||||
THIS IS NO LONGER VALID AFTER YOU ADD OR REMOVE A TRACK
|
||||
@param s session to get track array for
|
||||
@param num Returns the number of tracks in the array
|
||||
@return array of tracks
|
||||
*/
|
||||
struct burn_track **burn_session_get_tracks(struct burn_session *s,
|
||||
int *num);
|
||||
|
||||
int burn_session_get_sectors(struct burn_session *s);
|
||||
|
||||
/** Gets the mode of a track
|
||||
@param track the track to query
|
||||
@return the track's mode
|
||||
*/
|
||||
int burn_track_get_mode(struct burn_track *track);
|
||||
|
||||
/** Returns whether the first track of a session is hidden in the pregap
|
||||
@param session the session to query
|
||||
@return non-zero means the first track is hidden
|
||||
*/
|
||||
int burn_session_get_hidefirst(struct burn_session *session);
|
||||
|
||||
/** Returns the library's version in its parts
|
||||
@param major The major version number
|
||||
@param minor The minor version number
|
||||
@param micro The micro version number
|
||||
*/
|
||||
void burn_version(int *major, int *minor, int *micro);
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
BURN_END_DECLS
|
||||
|
||||
#endif
|
||||
|
||||
#endif /*LIBBURN_H*/
|
108
libburn/message.c
Normal file
108
libburn/message.c
Normal file
@ -0,0 +1,108 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include "message.h"
|
||||
#include "libburn.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct message_list
|
||||
{
|
||||
struct message_list *next;
|
||||
|
||||
struct burn_message *msg;
|
||||
};
|
||||
|
||||
static struct message_list *queue;
|
||||
|
||||
void burn_message_free(struct burn_message *msg)
|
||||
{
|
||||
free(msg);
|
||||
}
|
||||
|
||||
void burn_message_clear_queue(void)
|
||||
{
|
||||
struct burn_message *msg;
|
||||
|
||||
if ((msg = burn_get_message())) {
|
||||
burn_print(0,
|
||||
"YOU HAVE MESSAGES QUEUED FROM THE LAST OPERATION. "
|
||||
"YOU SHOULD BE GRABBING THEM ALL!\n");
|
||||
do {
|
||||
burn_message_free(msg);
|
||||
} while ((msg = burn_get_message()));
|
||||
}
|
||||
}
|
||||
|
||||
struct burn_message *burn_get_message()
|
||||
{
|
||||
struct burn_message *msg = NULL;
|
||||
|
||||
if (queue) {
|
||||
struct message_list *next;
|
||||
|
||||
next = queue->next;
|
||||
msg = queue->msg;
|
||||
free(queue);
|
||||
queue = next;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static void queue_push_tail(struct burn_message *msg)
|
||||
{
|
||||
struct message_list *node;
|
||||
|
||||
node = malloc(sizeof(struct message_list));
|
||||
node->next = NULL;
|
||||
node->msg = msg;
|
||||
|
||||
if (!queue)
|
||||
queue = node;
|
||||
else {
|
||||
struct message_list *it;
|
||||
|
||||
for (it = queue; it->next; it = it->next) ;
|
||||
it->next = node;
|
||||
}
|
||||
}
|
||||
|
||||
void burn_message_info_new(struct burn_drive *drive,
|
||||
enum burn_message_info message)
|
||||
{
|
||||
struct burn_message *msg;
|
||||
|
||||
msg = malloc(sizeof(struct burn_message));
|
||||
msg->drive = drive;
|
||||
msg->type = BURN_MESSAGE_INFO;
|
||||
msg->detail.info.message = message;
|
||||
|
||||
queue_push_tail(msg);
|
||||
}
|
||||
|
||||
void burn_message_warning_new(struct burn_drive *drive,
|
||||
enum burn_message_info message)
|
||||
{
|
||||
struct burn_message *msg;
|
||||
|
||||
msg = malloc(sizeof(struct burn_message));
|
||||
msg->drive = drive;
|
||||
msg->type = BURN_MESSAGE_WARNING;
|
||||
msg->detail.warning.message = message;
|
||||
|
||||
queue_push_tail(msg);
|
||||
}
|
||||
|
||||
void burn_message_error_new(struct burn_drive *drive,
|
||||
enum burn_message_info message)
|
||||
{
|
||||
struct burn_message *msg;
|
||||
|
||||
msg = malloc(sizeof(struct burn_message));
|
||||
msg->drive = drive;
|
||||
msg->type = BURN_MESSAGE_ERROR;
|
||||
msg->detail.error.message = message;
|
||||
|
||||
queue_push_tail(msg);
|
||||
}
|
19
libburn/message.h
Normal file
19
libburn/message.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef __MESSAGE
|
||||
#define __MESSAGE
|
||||
|
||||
#include "libburn.h"
|
||||
|
||||
void burn_message_clear_queue(void);
|
||||
|
||||
void burn_message_info_new(struct burn_drive *drive,
|
||||
enum burn_message_info message);
|
||||
|
||||
void burn_message_warning_new(struct burn_drive *drive,
|
||||
enum burn_message_info message);
|
||||
|
||||
void burn_message_error_new(struct burn_drive *drive,
|
||||
enum burn_message_info message);
|
||||
|
||||
#endif
|
506
libburn/mmc.c
Normal file
506
libburn/mmc.c
Normal file
@ -0,0 +1,506 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include "error.h"
|
||||
#include "sector.h"
|
||||
#include "libburn.h"
|
||||
#include "transport.h"
|
||||
#include "mmc.h"
|
||||
#include "spc.h"
|
||||
#include "drive.h"
|
||||
#include "debug.h"
|
||||
#include "toc.h"
|
||||
#include "structure.h"
|
||||
#include "options.h"
|
||||
|
||||
static unsigned char MMC_GET_TOC[] = { 0x43, 2, 2, 0, 0, 0, 0, 16, 0, 0 };
|
||||
static unsigned char MMC_GET_ATIP[] = { 0x43, 2, 4, 0, 0, 0, 0, 16, 0, 0 };
|
||||
static unsigned char MMC_GET_DISC_INFO[] =
|
||||
{ 0x51, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
|
||||
static unsigned char MMC_READ_CD[] = { 0xBE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
static unsigned char MMC_ERASE[] = { 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 };
|
||||
static unsigned char MMC_GET_CONFIGURATION[] =
|
||||
{ 0x46, 0, 0, 0, 0, 0, 16, 0, 0 };
|
||||
static unsigned char MMC_SYNC_CACHE[] = { 0x35, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
static unsigned char MMC_GET_EVENT[] = { 0x4A, 1, 0, 0, 16, 0, 0, 0, 8, 0 };
|
||||
static unsigned char MMC_CLOSE[] = { 0x5B, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
static unsigned char MMC_TRACK_INFO[] = { 0x52, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
|
||||
static unsigned char MMC_SEND_CUE_SHEET[] =
|
||||
{ 0x5D, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
void mmc_send_cue_sheet(struct burn_drive *d, struct cue_sheet *s)
|
||||
{
|
||||
struct buffer buf;
|
||||
struct command c;
|
||||
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_SEND_CUE_SHEET);
|
||||
memcpy(c.opcode, MMC_SEND_CUE_SHEET, sizeof(MMC_SEND_CUE_SHEET));
|
||||
c.page = &buf;
|
||||
c.page->bytes = s->count * 8;
|
||||
c.page->sectors = 0;
|
||||
c.opcode[6] = (c.page->bytes >> 16) & 0xFF;
|
||||
c.opcode[7] = (c.page->bytes >> 8) & 0xFF;
|
||||
c.opcode[8] = c.page->bytes & 0xFF;
|
||||
c.dir = TO_DRIVE;
|
||||
memcpy(c.page->data, s->data, c.page->bytes);
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
int mmc_get_nwa(struct burn_drive *d)
|
||||
{
|
||||
struct buffer buf;
|
||||
struct command c;
|
||||
unsigned char *data;
|
||||
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_TRACK_INFO);
|
||||
memcpy(c.opcode, MMC_TRACK_INFO, sizeof(MMC_TRACK_INFO));
|
||||
c.opcode[1] = 1;
|
||||
c.opcode[5] = 0xFF;
|
||||
c.page = &buf;
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
data = c.page->data;
|
||||
return (data[12] << 24) + (data[13] << 16)
|
||||
+ (data[14] << 8) + data[15];
|
||||
}
|
||||
|
||||
void mmc_close_disc(struct burn_drive *d, struct burn_write_opts *o)
|
||||
{
|
||||
assert(o->drive == d);
|
||||
o->multi = 0;
|
||||
spc_select_write_params(d, o);
|
||||
mmc_close(d, 1, 0);
|
||||
}
|
||||
|
||||
void mmc_close_session(struct burn_drive *d, struct burn_write_opts *o)
|
||||
{
|
||||
assert(o->drive == d);
|
||||
o->multi = 3;
|
||||
spc_select_write_params(d, o);
|
||||
mmc_close(d, 1, 0);
|
||||
}
|
||||
|
||||
void mmc_close(struct burn_drive *d, int session, int track)
|
||||
{
|
||||
struct command c;
|
||||
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_CLOSE);
|
||||
memcpy(c.opcode, MMC_CLOSE, sizeof(MMC_CLOSE));
|
||||
c.opcode[2] = session | !!track;
|
||||
c.opcode[4] = track >> 8;
|
||||
c.opcode[5] = track & 0xFF;
|
||||
c.page = NULL;
|
||||
c.dir = NO_TRANSFER;
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
void mmc_get_event(struct burn_drive *d)
|
||||
{
|
||||
struct buffer buf;
|
||||
struct command c;
|
||||
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_GET_EVENT);
|
||||
memcpy(c.opcode, MMC_GET_EVENT, sizeof(MMC_GET_EVENT));
|
||||
c.page = &buf;
|
||||
c.page->bytes = 0;
|
||||
c.page->sectors = 0;
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
burn_print(12, "0x%x:0x%x:0x%x:0x%x\n",
|
||||
c.page->data[0], c.page->data[1], c.page->data[2],
|
||||
c.page->data[3]);
|
||||
burn_print(12, "event: %d:%d:%d:%d\n", c.page->data[4],
|
||||
c.page->data[5], c.page->data[6], c.page->data[7]);
|
||||
}
|
||||
|
||||
void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf)
|
||||
{
|
||||
struct command c;
|
||||
int len;
|
||||
|
||||
len = buf->sectors;
|
||||
assert(buf->bytes >= buf->sectors); /* can be == at 0... */
|
||||
burn_print(100, "trying to write %d at %d\n", len, start);
|
||||
memcpy(c.opcode, MMC_WRITE_12, sizeof(MMC_WRITE_12));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_WRITE_12);
|
||||
c.opcode[2] = start >> 24;
|
||||
c.opcode[3] = (start >> 16) & 0xFF;
|
||||
c.opcode[4] = (start >> 8) & 0xFF;
|
||||
c.opcode[5] = start & 0xFF;
|
||||
c.opcode[6] = len >> 24;
|
||||
c.opcode[7] = (len >> 16) & 0xFF;
|
||||
c.opcode[8] = (len >> 8) & 0xFF;
|
||||
c.opcode[9] = len & 0xFF;
|
||||
c.page = buf;
|
||||
c.dir = TO_DRIVE;
|
||||
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
int mmc_write(struct burn_drive *d, int start, struct buffer *buf)
|
||||
{
|
||||
int cancelled;
|
||||
struct command c;
|
||||
int len;
|
||||
|
||||
pthread_mutex_lock(&d->access_lock);
|
||||
cancelled = d->cancel;
|
||||
pthread_mutex_unlock(&d->access_lock);
|
||||
|
||||
if (cancelled)
|
||||
return BE_CANCELLED;
|
||||
|
||||
len = buf->sectors;
|
||||
assert(buf->bytes >= buf->sectors); /* can be == at 0... */
|
||||
burn_print(100, "trying to write %d at %d\n", len, start);
|
||||
memcpy(c.opcode, MMC_WRITE_10, sizeof(MMC_WRITE_10));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_WRITE_10);
|
||||
c.opcode[2] = start >> 24;
|
||||
c.opcode[3] = (start >> 16) & 0xFF;
|
||||
c.opcode[4] = (start >> 8) & 0xFF;
|
||||
c.opcode[5] = start & 0xFF;
|
||||
c.opcode[6] = 0;
|
||||
c.opcode[7] = (len >> 8) & 0xFF;
|
||||
c.opcode[8] = len & 0xFF;
|
||||
c.page = buf;
|
||||
c.dir = TO_DRIVE;
|
||||
/*
|
||||
burn_print(12, "%d, %d, %d, %d - ", c->opcode[2], c->opcode[3], c->opcode[4], c->opcode[5]);
|
||||
burn_print(12, "%d, %d, %d, %d\n", c->opcode[6], c->opcode[7], c->opcode[8], c->opcode[9]);
|
||||
*/
|
||||
|
||||
/* write(fileno(stderr), c.page->data, c.page->bytes);*/
|
||||
|
||||
d->issue_command(d, &c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mmc_read_toc(struct burn_drive *d)
|
||||
{
|
||||
/* read full toc, all sessions, in m/s/f form, 4k buffer */
|
||||
struct burn_track *track;
|
||||
struct burn_session *session;
|
||||
struct buffer buf;
|
||||
struct command c;
|
||||
int dlen;
|
||||
int i;
|
||||
unsigned char *tdata;
|
||||
|
||||
memcpy(c.opcode, MMC_GET_TOC, sizeof(MMC_GET_TOC));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_GET_TOC);
|
||||
c.page = &buf;
|
||||
c.page->bytes = 0;
|
||||
c.page->sectors = 0;
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
|
||||
if (c.error) {
|
||||
d->busy = BURN_DRIVE_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
dlen = c.page->data[0] * 256 + c.page->data[1];
|
||||
d->toc_entries = (dlen - 2) / 11;
|
||||
/*
|
||||
some drives fail this check.
|
||||
|
||||
assert(((dlen - 2) % 11) == 0);
|
||||
*/
|
||||
d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry));
|
||||
tdata = c.page->data + 4;
|
||||
|
||||
burn_print(12, "TOC:\n");
|
||||
|
||||
d->disc = burn_disc_create();
|
||||
|
||||
for (i = 0; i < c.page->data[3]; i++) {
|
||||
session = burn_session_create();
|
||||
burn_disc_add_session(d->disc, session, BURN_POS_END);
|
||||
burn_session_free(session);
|
||||
}
|
||||
for (i = 0; i < d->toc_entries; i++, tdata += 11) {
|
||||
burn_print(12, "S %d, PT %d, TNO %d : ", tdata[0], tdata[3],
|
||||
tdata[2]);
|
||||
burn_print(12, "(%d:%d:%d)", tdata[8], tdata[9], tdata[10]);
|
||||
burn_print(12, "A(%d:%d:%d)", tdata[4], tdata[5], tdata[6]);
|
||||
burn_print(12, " - control %d, adr %d\n", tdata[1] & 0xF,
|
||||
tdata[1] >> 4);
|
||||
|
||||
if (tdata[3] == 1) {
|
||||
if (burn_msf_to_lba(tdata[8], tdata[9], tdata[10])) {
|
||||
d->disc->session[0]->hidefirst = 1;
|
||||
track = burn_track_create();
|
||||
burn_session_add_track(d->disc->
|
||||
session[tdata[0] - 1],
|
||||
track, BURN_POS_END);
|
||||
burn_track_free(track);
|
||||
|
||||
}
|
||||
}
|
||||
if (tdata[3] < 100) {
|
||||
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];
|
||||
}
|
||||
if (d->status != BURN_DISC_APPENDABLE)
|
||||
d->status = BURN_DISC_FULL;
|
||||
toc_find_modes(d);
|
||||
}
|
||||
|
||||
void mmc_read_disc_info(struct burn_drive *d)
|
||||
{
|
||||
struct buffer buf;
|
||||
unsigned char *data;
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, MMC_GET_DISC_INFO, sizeof(MMC_GET_DISC_INFO));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_GET_DISC_INFO);
|
||||
c.page = &buf;
|
||||
c.page->sectors = 0;
|
||||
c.page->bytes = 0;
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
|
||||
if (c.error) {
|
||||
d->busy = BURN_DRIVE_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
data = c.page->data;
|
||||
d->erasable = !!(data[2] & 16);
|
||||
switch (data[2] & 3) {
|
||||
case 0:
|
||||
d->toc_entries = 0;
|
||||
d->start_lba = burn_msf_to_lba(data[17], data[18], data[19]);
|
||||
d->end_lba = burn_msf_to_lba(data[21], data[22], data[23]);
|
||||
d->status = BURN_DISC_BLANK;
|
||||
break;
|
||||
case 1:
|
||||
d->status = BURN_DISC_APPENDABLE;
|
||||
case 2:
|
||||
mmc_read_toc(d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void mmc_read_atip(struct burn_drive *d)
|
||||
{
|
||||
struct buffer buf;
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, MMC_GET_ATIP, sizeof(MMC_GET_ATIP));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_GET_ATIP);
|
||||
c.page = &buf;
|
||||
c.page->bytes = 0;
|
||||
c.page->sectors = 0;
|
||||
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
burn_print(1, "atip shit for you\n");
|
||||
}
|
||||
|
||||
void mmc_read_sectors(struct burn_drive *d,
|
||||
int start,
|
||||
int len,
|
||||
const struct burn_read_opts *o, struct buffer *buf)
|
||||
{
|
||||
int temp;
|
||||
int errorblock, req;
|
||||
struct command c;
|
||||
|
||||
assert(len >= 0);
|
||||
/* if the drive isn't busy, why the hell are we here? */
|
||||
assert(d->busy);
|
||||
burn_print(12, "reading %d from %d\n", len, start);
|
||||
memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_READ_CD);
|
||||
temp = start;
|
||||
c.opcode[5] = temp & 0xFF;
|
||||
temp >>= 8;
|
||||
c.opcode[4] = temp & 0xFF;
|
||||
temp >>= 8;
|
||||
c.opcode[3] = temp & 0xFF;
|
||||
temp >>= 8;
|
||||
c.opcode[2] = temp & 0xFF;
|
||||
c.opcode[8] = len & 0xFF;
|
||||
len >>= 8;
|
||||
c.opcode[7] = len & 0xFF;
|
||||
len >>= 8;
|
||||
c.opcode[6] = len & 0xFF;
|
||||
req = 0xF8;
|
||||
if (d->busy == BURN_DRIVE_GRABBING || o->report_recovered_errors)
|
||||
req |= 2;
|
||||
|
||||
c.opcode[10] = 0;
|
||||
/* always read the subcode, throw it away later, since we don't know
|
||||
what we're really reading
|
||||
*/
|
||||
if (d->busy == BURN_DRIVE_GRABBING || (o->subcodes_audio)
|
||||
|| (o->subcodes_data))
|
||||
c.opcode[10] = 1;
|
||||
|
||||
c.opcode[9] = req;
|
||||
c.page = buf;
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
|
||||
if (c.error) {
|
||||
burn_print(12, "got an error over here\n");
|
||||
burn_print(12, "%d, %d, %d, %d\n", c.sense[3], c.sense[4],
|
||||
c.sense[5], c.sense[6]);
|
||||
errorblock =
|
||||
(c.sense[3] << 24) + (c.sense[4] << 16) +
|
||||
(c.sense[5] << 8) + c.sense[6];
|
||||
c.page->sectors = errorblock - start + 1;
|
||||
burn_print(1, "error on block %d\n", errorblock);
|
||||
burn_print(12, "error on block %d\n", errorblock);
|
||||
burn_print(12, "returning %d sectors\n", c.page->sectors);
|
||||
}
|
||||
}
|
||||
|
||||
void mmc_erase(struct burn_drive *d, int fast)
|
||||
{
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, MMC_ERASE, sizeof(MMC_ERASE));
|
||||
c.opcode[1] = 16; /* IMMED set to 1 */
|
||||
c.opcode[1] |= !!fast;
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_ERASE);
|
||||
c.page = NULL;
|
||||
c.dir = NO_TRANSFER;
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
void mmc_read_lead_in(struct burn_drive *d, struct buffer *buf)
|
||||
{
|
||||
int len;
|
||||
struct command c;
|
||||
|
||||
len = buf->sectors;
|
||||
memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_READ_CD);
|
||||
c.opcode[5] = 0;
|
||||
c.opcode[4] = 0;
|
||||
c.opcode[3] = 0;
|
||||
c.opcode[2] = 0xF0;
|
||||
c.opcode[8] = 1;
|
||||
c.opcode[7] = 0;
|
||||
c.opcode[6] = 0;
|
||||
c.opcode[9] = 0;
|
||||
c.opcode[10] = 2;
|
||||
c.page = buf;
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
void mmc_perform_opc(struct burn_drive *d)
|
||||
{
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, MMC_SEND_OPC, sizeof(MMC_SEND_OPC));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_SEND_OPC);
|
||||
c.opcode[1] = 1;
|
||||
c.page = NULL;
|
||||
c.dir = NO_TRANSFER;
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
void mmc_set_speed(struct burn_drive *d, int r, int w)
|
||||
{
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, MMC_SET_SPEED, sizeof(MMC_SET_SPEED));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_SET_SPEED);
|
||||
c.opcode[2] = r >> 8;
|
||||
c.opcode[3] = r & 0xFF;
|
||||
c.opcode[4] = w >> 8;
|
||||
c.opcode[5] = w & 0xFF;
|
||||
c.page = NULL;
|
||||
c.dir = NO_TRANSFER;
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
void mmc_get_configuration(struct burn_drive *d)
|
||||
{
|
||||
struct buffer buf;
|
||||
int len;
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, MMC_GET_CONFIGURATION, sizeof(MMC_GET_CONFIGURATION));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_GET_CONFIGURATION);
|
||||
c.page = &buf;
|
||||
c.page->sectors = 0;
|
||||
c.page->bytes = 0;
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
|
||||
burn_print(1, "got it back\n");
|
||||
len = (c.page->data[0] << 24)
|
||||
+ (c.page->data[1] << 16)
|
||||
+ (c.page->data[2] << 8)
|
||||
+ c.page->data[3];
|
||||
burn_print(1, "all %d bytes of it\n", len);
|
||||
burn_print(1, "%d, %d, %d, %d\n",
|
||||
c.page->data[0],
|
||||
c.page->data[1], c.page->data[2], c.page->data[3]);
|
||||
}
|
||||
|
||||
void mmc_sync_cache(struct burn_drive *d)
|
||||
{
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, MMC_SYNC_CACHE, sizeof(MMC_SYNC_CACHE));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_SYNC_CACHE);
|
||||
c.page = NULL;
|
||||
c.dir = NO_TRANSFER;
|
||||
d->issue_command(d, &c);
|
||||
}
|
37
libburn/mmc.h
Normal file
37
libburn/mmc.h
Normal file
@ -0,0 +1,37 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef __MMC
|
||||
#define __MMC
|
||||
|
||||
struct burn_drive;
|
||||
struct burn_write_opts;
|
||||
struct command;
|
||||
struct buffer;
|
||||
struct cue_sheet;
|
||||
|
||||
/* MMC commands */
|
||||
|
||||
void mmc_read(struct burn_drive *);
|
||||
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(struct burn_drive *, int session, int track);
|
||||
void mmc_get_event(struct burn_drive *);
|
||||
int mmc_write(struct burn_drive *, int start, struct buffer *buf);
|
||||
void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf);
|
||||
void mmc_sync_cache(struct burn_drive *);
|
||||
void mmc_load(struct burn_drive *);
|
||||
void mmc_eject(struct burn_drive *);
|
||||
void mmc_erase(struct burn_drive *, int);
|
||||
void mmc_read_toc(struct burn_drive *);
|
||||
void mmc_read_disc_info(struct burn_drive *);
|
||||
void mmc_read_atip(struct burn_drive *);
|
||||
void mmc_read_sectors(struct burn_drive *,
|
||||
int,
|
||||
int, const struct burn_read_opts *, struct buffer *);
|
||||
void mmc_set_speed(struct burn_drive *, int, int);
|
||||
void mmc_read_lead_in(struct burn_drive *, struct buffer *);
|
||||
void mmc_perform_opc(struct burn_drive *);
|
||||
void mmc_get_configuration(struct burn_drive *);
|
||||
int mmc_get_nwa(struct burn_drive *);
|
||||
void mmc_send_cue_sheet(struct burn_drive *, struct cue_sheet *);
|
||||
#endif /*__MMC*/
|
27
libburn/null.c
Normal file
27
libburn/null.c
Normal file
@ -0,0 +1,27 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include "null.h"
|
||||
#include "libburn.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string.h>
|
||||
int null_read(struct burn_source *source, unsigned char *buffer, int size)
|
||||
{
|
||||
memset(buffer, 0, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
struct burn_source *burn_null_source_new(void)
|
||||
{
|
||||
struct burn_source *src;
|
||||
|
||||
src = malloc(sizeof(struct burn_source));
|
||||
src->refcount = 1;
|
||||
src->read = null_read;
|
||||
src->read_sub = NULL;
|
||||
|
||||
src->get_size = 0;
|
||||
src->free_data = NULL;
|
||||
src->data = NULL;
|
||||
return src;
|
||||
}
|
10
libburn/null.h
Normal file
10
libburn/null.h
Normal file
@ -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 */
|
169
libburn/options.c
Normal file
169
libburn/options.c
Normal file
@ -0,0 +1,169 @@
|
||||
#include "libburn.h"
|
||||
#include "options.h"
|
||||
#include "transport.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive)
|
||||
{
|
||||
struct burn_write_opts *opts;
|
||||
|
||||
opts = malloc(sizeof(struct burn_write_opts));
|
||||
opts->drive = drive;
|
||||
opts->refcount = 1;
|
||||
opts->write_type = BURN_WRITE_TAO;
|
||||
opts->block_type = BURN_BLOCK_MODE1;
|
||||
opts->toc_entry = NULL;
|
||||
opts->toc_entries = 0;
|
||||
opts->simulate = 0;
|
||||
opts->underrun_proof = drive->mdata->underrun_proof;
|
||||
opts->perform_opc = 1;
|
||||
opts->has_mediacatalog = 0;
|
||||
opts->format = BURN_CDROM;
|
||||
opts->multi = 0;
|
||||
opts->control = 0;
|
||||
return opts;
|
||||
}
|
||||
|
||||
void burn_write_opts_free(struct burn_write_opts *opts)
|
||||
{
|
||||
if (--opts->refcount <= 0)
|
||||
free(opts);
|
||||
}
|
||||
|
||||
struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive)
|
||||
{
|
||||
struct burn_read_opts *opts;
|
||||
|
||||
opts = malloc(sizeof(struct burn_read_opts));
|
||||
opts->drive = drive;
|
||||
opts->refcount = 1;
|
||||
opts->raw = 0;
|
||||
opts->c2errors = 0;
|
||||
opts->subcodes_audio = 0;
|
||||
opts->subcodes_data = 0;
|
||||
opts->hardware_error_recovery = 0;
|
||||
opts->report_recovered_errors = 0;
|
||||
opts->transfer_damaged_blocks = 0;
|
||||
opts->hardware_error_retries = 3;
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
void burn_read_opts_free(struct burn_read_opts *opts)
|
||||
{
|
||||
if (--opts->refcount <= 0)
|
||||
free(opts);
|
||||
}
|
||||
|
||||
int burn_write_opts_set_write_type(struct burn_write_opts *opts,
|
||||
enum burn_write_types write_type,
|
||||
int block_type)
|
||||
{
|
||||
if ((write_type == BURN_WRITE_SAO && block_type == BURN_BLOCK_SAO) ||
|
||||
(opts->drive->block_types[write_type] & block_type)) {
|
||||
opts->write_type = write_type;
|
||||
opts->block_type = block_type;
|
||||
return 1;
|
||||
}
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void burn_write_opts_set_toc_entries(struct burn_write_opts *opts, int count,
|
||||
struct burn_toc_entry *toc_entries)
|
||||
{
|
||||
opts->toc_entries = count;
|
||||
opts->toc_entry = malloc(count * sizeof(struct burn_toc_entry));
|
||||
memcpy(opts->toc_entry, &toc_entries,
|
||||
sizeof(struct burn_toc_entry) * count);
|
||||
}
|
||||
|
||||
void burn_write_opts_set_format(struct burn_write_opts *opts, int format)
|
||||
{
|
||||
opts->format = format;
|
||||
}
|
||||
|
||||
int burn_write_opts_set_simulate(struct burn_write_opts *opts, int sim)
|
||||
{
|
||||
if (opts->drive->mdata->simulate) {
|
||||
opts->simulate = sim;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts,
|
||||
int underrun_proof)
|
||||
{
|
||||
if (opts->drive->mdata->underrun_proof) {
|
||||
opts->underrun_proof = underrun_proof;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void burn_write_opts_set_perform_opc(struct burn_write_opts *opts, int opc)
|
||||
{
|
||||
opts->perform_opc = opc;
|
||||
}
|
||||
|
||||
void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts,
|
||||
int has_mediacatalog)
|
||||
{
|
||||
opts->has_mediacatalog = has_mediacatalog;
|
||||
}
|
||||
|
||||
void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts,
|
||||
unsigned char mediacatalog[13])
|
||||
{
|
||||
memcpy(opts->mediacatalog, &mediacatalog, 13);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
78
libburn/options.h
Normal file
78
libburn/options.h
Normal file
@ -0,0 +1,78 @@
|
||||
#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;
|
||||
/** A disc can have a media catalog number */
|
||||
int has_mediacatalog;
|
||||
unsigned char mediacatalog[13];
|
||||
/** Session format */
|
||||
int format;
|
||||
/* internal use only */
|
||||
unsigned char control;
|
||||
unsigned char multi;
|
||||
};
|
||||
|
||||
/** Options for disc reading operations. This should be created with
|
||||
burn_read_opts_new() and freed with burn_read_opts_free(). */
|
||||
struct burn_read_opts
|
||||
{
|
||||
/** Drive the read opts are good for */
|
||||
struct burn_drive *drive;
|
||||
|
||||
/** For internal use. */
|
||||
int refcount;
|
||||
|
||||
/** Read in raw mode, so that everything in the data tracks on the
|
||||
disc is read, including headers. Not needed if just reading a
|
||||
filesystem off a disc, but it should usually be used when making a
|
||||
disc image or copying a disc. */
|
||||
unsigned int raw:1;
|
||||
/** Report c2 errors. Useful for statistics reporting */
|
||||
unsigned int c2errors:1;
|
||||
/** Read subcodes from audio tracks on the disc */
|
||||
unsigned int subcodes_audio:1;
|
||||
/** Read subcodes from data tracks on the disc */
|
||||
unsigned int subcodes_data:1;
|
||||
/** Have the drive recover errors if possible */
|
||||
unsigned int hardware_error_recovery:1;
|
||||
/** Report errors even when they were recovered from */
|
||||
unsigned int report_recovered_errors:1;
|
||||
/** Read blocks even when there are unrecoverable errors in them */
|
||||
unsigned int transfer_damaged_blocks:1;
|
||||
|
||||
/** The number of retries the hardware should make to correct
|
||||
errors. */
|
||||
unsigned char hardware_error_retries;
|
||||
};
|
||||
|
||||
#endif /* BURN__OPTIONS_H */
|
264
libburn/read.c
Normal file
264
libburn/read.c
Normal file
@ -0,0 +1,264 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include <malloc.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "sector.h"
|
||||
#include "libburn.h"
|
||||
#include "drive.h"
|
||||
#include "transport.h"
|
||||
#include "message.h"
|
||||
#include "crc.h"
|
||||
#include "debug.h"
|
||||
#include "init.h"
|
||||
#include "lec.h"
|
||||
#include "toc.h"
|
||||
#include "util.h"
|
||||
#include "sg.h"
|
||||
#include "read.h"
|
||||
#include "options.h"
|
||||
|
||||
void burn_disc_read(struct burn_drive *d, const struct burn_read_opts *o)
|
||||
{
|
||||
#if 0
|
||||
int i, end, maxsects, finish;
|
||||
int seclen;
|
||||
int drive_lba;
|
||||
unsigned short crc;
|
||||
unsigned char fakesub[96];
|
||||
struct buffer page;
|
||||
int speed;
|
||||
|
||||
assert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000));
|
||||
|
||||
assert(!d->busy);
|
||||
assert(d->toc->valid);
|
||||
assert(o->datafd != -1);
|
||||
/* XXX not sure this is a good idea. copy it? */
|
||||
/* XXX also, we have duplicated data now, do we remove the fds from struct
|
||||
drive, or only store a subset of the _opts structs in drives */
|
||||
|
||||
/* set the speed on the drive */
|
||||
speed = o->speed > 0 ? o->speed : d->mdata->max_read_speed;
|
||||
d->set_speed(d, speed, 0);
|
||||
|
||||
d->params.retries = o->hardware_error_retries;
|
||||
d->send_parameters(d, o);
|
||||
|
||||
d->cancel = 0;
|
||||
d->busy = BURN_DRIVE_READING;
|
||||
d->currsession = 0;
|
||||
/* drive_lba = 232000;
|
||||
d->currtrack = 18;
|
||||
*/
|
||||
d->currtrack = 0;
|
||||
drive_lba = 0;
|
||||
/* XXX removal of this line obviously breaks *
|
||||
d->track_end = burn_track_end(d, d->currsession, d->currtrack);*/
|
||||
printf("track ends at %d\n", d->track_end);
|
||||
page.sectors = 0;
|
||||
page.bytes = 0;
|
||||
|
||||
if (o->subfd != -1) {
|
||||
memset(fakesub, 0xFF, 12);
|
||||
memset(fakesub + 12, 0, 84);
|
||||
fakesub[13] = 1;
|
||||
fakesub[14] = 1;
|
||||
fakesub[20] = 2;
|
||||
fakesub[12] = (d->toc->toc_entry[0].control << 4) +
|
||||
d->toc->toc_entry[0].adr;
|
||||
crc = crc_ccitt(fakesub + 12, 10);
|
||||
fakesub[22] = crc >> 8;
|
||||
fakesub[23] = crc & 0xFF;
|
||||
write(o->subfd, fakesub, 96);
|
||||
}
|
||||
while (1) {
|
||||
seclen = burn_sector_length_read(d, o);
|
||||
|
||||
burn_print(12, "received %d blocks\n", page.sectors);
|
||||
for (i = 0; i < page.sectors; i++) {
|
||||
burn_packet_process(d, page.data + seclen * i, o);
|
||||
d->track_end--;
|
||||
drive_lba++;
|
||||
}
|
||||
|
||||
if ((d->cancel) || (drive_lba == LAST_SESSION_END(d))) {
|
||||
burn_print(1, "finished or cancelled\n");
|
||||
d->busy = BURN_DRIVE_IDLE;
|
||||
if (!d->cancel)
|
||||
d->toc->complete = 1;
|
||||
return;
|
||||
}
|
||||
/* XXX: removal of this line obviously breaks *
|
||||
end = burn_track_end(d, d->currsession, d->currtrack); */
|
||||
|
||||
if (drive_lba == end) {
|
||||
d->currtrack++;
|
||||
if (d->currtrack >
|
||||
d->toc->session[d->currsession].lasttrack) {
|
||||
d->currsession++;
|
||||
burn_print(12, "session switch to %d\n",
|
||||
d->currsession);
|
||||
burn_print(12, "skipping a lead out\n");
|
||||
drive_lba = CURRENT_SESSION_START(d);
|
||||
burn_print(12, "new lba %d\n", drive_lba);
|
||||
/* XXX more of the same
|
||||
end = burn_track_end(d, d->currsession,
|
||||
d->currtrack);
|
||||
*/ }
|
||||
burn_print(12, "track switch to %d\n", d->currtrack);
|
||||
}
|
||||
|
||||
page.sectors = 0;
|
||||
page.bytes = 0;
|
||||
|
||||
maxsects = BUFFER_SIZE / seclen;
|
||||
finish = end - drive_lba;
|
||||
|
||||
d->track_end = finish;
|
||||
|
||||
page.sectors = (finish < maxsects) ? finish : maxsects;
|
||||
printf("reading %d sectors from %d\n", page.sectors,
|
||||
drive_lba);
|
||||
d->read_sectors(d, drive_lba, page.sectors, o, &page);
|
||||
printf("Read %d\n", page.sectors);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
int burn_sector_length_read(struct burn_drive *d,
|
||||
const struct burn_read_opts *o)
|
||||
{
|
||||
int dlen = 2352;
|
||||
int data;
|
||||
|
||||
/*XXX how do we handle this crap now?*/
|
||||
/* data = d->toc->track[d->currtrack].toc_entry->control & 4;*/
|
||||
data = 1;
|
||||
if (o->report_recovered_errors)
|
||||
dlen += 294;
|
||||
if ((o->subcodes_data) && data)
|
||||
dlen += 96;
|
||||
if ((o->subcodes_audio) && !data)
|
||||
dlen += 96;
|
||||
return dlen;
|
||||
}
|
||||
|
||||
static int bitcount(unsigned char *data, int n)
|
||||
{
|
||||
int i, j, count = 0;
|
||||
unsigned char tem;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
tem = data[i];
|
||||
for (j = 0; j < 8; j++) {
|
||||
count += tem & 1;
|
||||
tem >>= 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void burn_packet_process(struct burn_drive *d, unsigned char *data,
|
||||
const struct burn_read_opts *o)
|
||||
{
|
||||
unsigned char sub[96];
|
||||
unsigned short crc;
|
||||
int ptr = 2352, i, j, code, fb;
|
||||
int audio = 1;
|
||||
|
||||
if (o->c2errors) {
|
||||
fb = bitcount(data + ptr, 294);
|
||||
if (fb) {
|
||||
burn_print(1, "%d damaged bits\n",
|
||||
bitcount(data + ptr, 294));
|
||||
burn_print(1, "sending error on %s %s\n",
|
||||
d->idata->vendor, d->idata->product);
|
||||
/* XXX send a burn_message! burn_message_error(d,
|
||||
something); */
|
||||
}
|
||||
ptr += 294;
|
||||
}
|
||||
/*
|
||||
if (d->toc->track[d->currtrack].mode == BURN_MODE_UNINITIALIZED) {
|
||||
if ((d->toc->track[d->currtrack].toc_entry->control & 4) == 0)
|
||||
d->toc->track[d->currtrack].mode = BURN_MODE_AUDIO;
|
||||
else
|
||||
switch (data[15]) {
|
||||
case 0:
|
||||
d->toc->track[d->currtrack].mode = BURN_MODE0;
|
||||
break;
|
||||
case 1:
|
||||
d->toc->track[d->currtrack].mode = BURN_MODE1;
|
||||
break;
|
||||
case 2:
|
||||
d->toc->track[d->currtrack].mode =
|
||||
BURN_MODE2_FORMLESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
if ((audio && o->subcodes_audio)
|
||||
|| (!audio && o->subcodes_data)) {
|
||||
memset(sub, 0, sizeof(sub));
|
||||
for (i = 0; i < 12; i++) {
|
||||
for (j = 0; j < 8; j++) {
|
||||
for (code = 0; code < 8; code++) {
|
||||
sub[code * 12 + i] <<= 1;
|
||||
if (data[ptr + j + i * 8] &
|
||||
(1 << (7 - code)))
|
||||
sub[code * 12 + i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
crc = (*(sub + 22) << 8) + *(sub + 23);
|
||||
if (crc != crc_ccitt(sub + 12, 10)) {
|
||||
burn_print(1, "sending error on %s %s\n",
|
||||
d->idata->vendor, d->idata->product);
|
||||
/* e = burn_error();
|
||||
e->drive = d;
|
||||
*/
|
||||
burn_print(1, "crc mismatch in Q\n");
|
||||
}
|
||||
/* else process_q(d, sub + 12); */
|
||||
/*
|
||||
if (o->subfd != -1) write(o->subfd, sub, 96); */
|
||||
}
|
||||
/*
|
||||
if ((d->track_end <= 150)
|
||||
&& (drive_lba + 150 < CURRENT_SESSION_END(d))
|
||||
&& (TOC_ENTRY(d->toc, d->currtrack).control == 4)
|
||||
&& (TOC_ENTRY(d->toc, d->currtrack + 1).control == 0)) {
|
||||
burn_print(12, "pregap : %d\n", d->track_end);
|
||||
write(o->binfd, zeros, 2352);
|
||||
|
||||
#warning XXX WHERE ARE MY SUBCODES
|
||||
} else
|
||||
*//* write(o->datafd, data, 2352); */
|
||||
}
|
||||
|
||||
/* so yeah, when you uncomment these, make them write zeros insted of crap
|
||||
static void write_empty_sector(int fd)
|
||||
{
|
||||
char sec[2352];
|
||||
|
||||
burn_print(1, "writing an 'empty' sector\n");
|
||||
write(fd, sec, 2352);
|
||||
}
|
||||
|
||||
static void write_empty_subcode(int fd)
|
||||
{
|
||||
char sub[96];
|
||||
|
||||
write(fd, sub, 96);
|
||||
}
|
||||
|
||||
static void flipq(unsigned char *sub)
|
||||
{
|
||||
*(sub + 12 + 10) = ~*(sub + 12 + 10);
|
||||
*(sub + 12 + 11) = ~*(sub + 12 + 11);
|
||||
}
|
||||
*/
|
14
libburn/read.h
Normal file
14
libburn/read.h
Normal file
@ -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 */
|
40
libburn/sbc.c
Normal file
40
libburn/sbc.c
Normal file
@ -0,0 +1,40 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
/* scsi block commands */
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <string.h>
|
||||
#include <scsi/sg.h>
|
||||
|
||||
#include "transport.h"
|
||||
#include "sbc.h"
|
||||
#include "options.h"
|
||||
|
||||
/* spc command set */
|
||||
static char SBC_LOAD[] = { 0x1b, 0, 0, 0, 3, 0 };
|
||||
static char SBC_UNLOAD[] = { 0x1b, 0, 0, 0, 2, 0 };
|
||||
|
||||
void sbc_load(struct burn_drive *d)
|
||||
{
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, SBC_LOAD, sizeof(SBC_LOAD));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(SBC_LOAD);
|
||||
c.dir = NO_TRANSFER;
|
||||
c.page = NULL;
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
void sbc_eject(struct burn_drive *d)
|
||||
{
|
||||
struct command c;
|
||||
|
||||
c.page = NULL;
|
||||
memcpy(c.opcode, SBC_UNLOAD, sizeof(SBC_UNLOAD));
|
||||
c.oplen = 1;
|
||||
c.oplen = sizeof(SBC_UNLOAD);
|
||||
c.page = NULL;
|
||||
c.dir = NO_TRANSFER;
|
||||
d->issue_command(d, &c);
|
||||
}
|
11
libburn/sbc.h
Normal file
11
libburn/sbc.h
Normal file
@ -0,0 +1,11 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef __SBC
|
||||
#define __SBC
|
||||
|
||||
struct burn_drive;
|
||||
|
||||
void sbc_load(struct burn_drive *);
|
||||
void sbc_eject(struct burn_drive *);
|
||||
|
||||
#endif /* __SBC */
|
649
libburn/sector.c
Normal file
649
libburn/sector.c
Normal file
@ -0,0 +1,649 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include "error.h"
|
||||
#include "options.h"
|
||||
#include "transport.h"
|
||||
#include "libburn.h"
|
||||
#include "drive.h"
|
||||
#include "sector.h"
|
||||
#include "crc.h"
|
||||
#include "debug.h"
|
||||
#include "lec.h"
|
||||
#include "toc.h"
|
||||
#include "write.h"
|
||||
|
||||
/*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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 0 means "same as inmode" */
|
||||
static int get_outmode(struct burn_write_opts *o)
|
||||
{
|
||||
if (o->write_type == BURN_WRITE_SAO)
|
||||
return 0;
|
||||
else
|
||||
switch (o->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;
|
||||
}
|
||||
assert(0); /* return BURN_MODE_UNIMPLEMENTED :) */
|
||||
}
|
||||
|
||||
static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
||||
{
|
||||
int valid, shortage, curr;
|
||||
|
||||
/* 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)
|
||||
return;
|
||||
|
||||
/* Next we use source data */
|
||||
curr = valid;
|
||||
if (!track->eos) {
|
||||
valid = track->source->read(track->source, data + curr, count - curr);
|
||||
} else valid = 0;
|
||||
|
||||
if (valid == -1) {
|
||||
track->eos = 1;
|
||||
valid = 0;
|
||||
}
|
||||
|
||||
curr += valid;
|
||||
shortage = count - curr;
|
||||
|
||||
if (!shortage)
|
||||
return;
|
||||
|
||||
/* 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)
|
||||
return;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
if (!shortage)
|
||||
return;
|
||||
memset(data + curr, 0, shortage);
|
||||
}
|
||||
static unsigned char *get_sector(struct burn_write_opts *opts, int inmode)
|
||||
{
|
||||
struct burn_drive *d = opts->drive;
|
||||
struct buffer *out = d->buffer;
|
||||
int outmode;
|
||||
int seclen;
|
||||
unsigned char *ret;
|
||||
|
||||
outmode = get_outmode(opts);
|
||||
if (outmode == 0)
|
||||
outmode = inmode;
|
||||
|
||||
seclen = burn_sector_length(outmode) + burn_subcode_length(outmode);
|
||||
|
||||
if (out->bytes + (seclen) >= BUFFER_SIZE) {
|
||||
int err;
|
||||
err = d->write(d, d->nwa, out);
|
||||
if (err == BE_CANCELLED)
|
||||
return NULL;
|
||||
d->nwa += out->sectors;
|
||||
out->bytes = 0;
|
||||
out->sectors = 0;
|
||||
}
|
||||
|
||||
ret = out->data + out->bytes;
|
||||
out->bytes += seclen;
|
||||
out->sectors++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* either inmode == outmode, or outmode == raw. anything else is bad news */
|
||||
static void 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);
|
||||
assert(outlen >= inlen);
|
||||
|
||||
if ((outmode & BURN_MODE_BITS) == (inmode & BURN_MODE_BITS)) {
|
||||
get_bytes(track, inlen, data);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(outmode & BURN_MODE_RAW);
|
||||
|
||||
if (inmode & BURN_MODE1)
|
||||
offset = 16;
|
||||
if (inmode & BURN_MODE_RAW)
|
||||
offset = 0;
|
||||
if (inmode & BURN_AUDIO)
|
||||
offset = 0;
|
||||
assert(offset != -1);
|
||||
get_bytes(track, inlen, data + offset);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void subcode_toc(struct burn_drive *d, int mode, unsigned char *data)
|
||||
{
|
||||
unsigned char *q;
|
||||
int track;
|
||||
int crc;
|
||||
int min, sec, frame;
|
||||
|
||||
track = d->toc_temp / 3;
|
||||
memset(data, 0, 96);
|
||||
q = data + 12;
|
||||
|
||||
burn_lba_to_msf(d->rlba, &min, &sec, &frame);
|
||||
/*XXX track numbers are BCD
|
||||
a0 - 1st track ctrl
|
||||
a1 - last track ctrl
|
||||
a2 - lout ctrl
|
||||
*/
|
||||
q[0] = (d->toc_entry[track].control << 4) + 1;
|
||||
q[1] = 0;
|
||||
if (d->toc_entry[track].point < 100)
|
||||
q[2] = dec_to_bcd(d->toc_entry[track].point);
|
||||
else
|
||||
q[2] = d->toc_entry[track].point;
|
||||
q[3] = dec_to_bcd(min);
|
||||
q[4] = dec_to_bcd(sec);
|
||||
q[5] = dec_to_bcd(frame);
|
||||
q[6] = 0;
|
||||
q[7] = dec_to_bcd(d->toc_entry[track].pmin);
|
||||
q[8] = dec_to_bcd(d->toc_entry[track].psec);
|
||||
q[9] = dec_to_bcd(d->toc_entry[track].pframe);
|
||||
crc = crc_ccitt(q, 10);
|
||||
q[10] = crc >> 8;
|
||||
q[11] = crc & 0xFF;
|
||||
d->toc_temp++;
|
||||
d->toc_temp %= (d->toc_entries * 3);
|
||||
}
|
||||
|
||||
int sector_toc(struct burn_write_opts *o, int mode)
|
||||
{
|
||||
struct burn_drive *d = o->drive;
|
||||
unsigned char *data;
|
||||
unsigned char subs[96];
|
||||
|
||||
data = get_sector(o, mode);
|
||||
if (!data)
|
||||
return 0;
|
||||
convert_data(o, NULL, mode, data);
|
||||
subcode_toc(d, mode, subs);
|
||||
convert_subs(o, mode, subs, data);
|
||||
sector_headers(o, data, mode, 1);
|
||||
sector_common(++)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sector_pregap(struct burn_write_opts *o,
|
||||
unsigned char tno, unsigned char control, int mode)
|
||||
{
|
||||
struct burn_drive *d = o->drive;
|
||||
unsigned char *data;
|
||||
unsigned char subs[96];
|
||||
|
||||
data = get_sector(o, mode);
|
||||
if (!data)
|
||||
return 0;
|
||||
convert_data(o, NULL, mode, data);
|
||||
subcode_user(o, subs, tno, control, 0, NULL, 1);
|
||||
convert_subs(o, mode, subs, data);
|
||||
sector_headers(o, data, mode, 0);
|
||||
sector_common(--)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sector_postgap(struct burn_write_opts *o,
|
||||
unsigned char tno, unsigned char control, int mode)
|
||||
{
|
||||
struct burn_drive *d = o->drive;
|
||||
unsigned char subs[96];
|
||||
unsigned char *data;
|
||||
|
||||
data = get_sector(o, mode);
|
||||
if (!data)
|
||||
return 0;
|
||||
convert_data(o, NULL, mode, data);
|
||||
/* use last index in track */
|
||||
subcode_user(o, subs, tno, control, 1, NULL, 1);
|
||||
convert_subs(o, mode, subs, data);
|
||||
sector_headers(o, data, mode, 0);
|
||||
sector_common(++)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void subcode_lout(struct burn_write_opts *o, unsigned char control,
|
||||
unsigned char *data)
|
||||
{
|
||||
struct burn_drive *d = o->drive;
|
||||
unsigned char *q;
|
||||
int crc;
|
||||
int rmin, min, rsec, sec, rframe, frame;
|
||||
|
||||
memset(data, 0, 96);
|
||||
q = data + 12;
|
||||
|
||||
burn_lba_to_msf(d->alba, &min, &sec, &frame);
|
||||
burn_lba_to_msf(d->rlba, &rmin, &rsec, &rframe);
|
||||
|
||||
if (((rmin == 0) && (rsec == 0) && (rframe == 0)) ||
|
||||
((rsec >= 2) && !((rframe / 19) % 2)))
|
||||
memset(data, 0xFF, 12);
|
||||
q[0] = (control << 4) + 1;
|
||||
q[1] = 0xAA;
|
||||
q[2] = 0x01;
|
||||
q[3] = dec_to_bcd(rmin);
|
||||
q[4] = dec_to_bcd(rsec);
|
||||
q[5] = dec_to_bcd(rframe);
|
||||
q[6] = 0;
|
||||
q[7] = dec_to_bcd(min);
|
||||
q[8] = dec_to_bcd(sec);
|
||||
q[9] = dec_to_bcd(frame);
|
||||
crc = crc_ccitt(q, 10);
|
||||
q[10] = crc >> 8;
|
||||
q[11] = crc & 0xFF;
|
||||
}
|
||||
|
||||
static char char_to_isrc(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return 0x11 + (c - 'A');
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return 0x11 + (c - 'a');
|
||||
assert(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;
|
||||
}
|
||||
}
|
||||
|
||||
assert(qmode == 1 || qmode == 2 || qmode == 3);
|
||||
|
||||
switch (qmode) {
|
||||
case 1:
|
||||
q[1] = dec_to_bcd(tno); /* track number */
|
||||
q[2] = dec_to_bcd(indx); /* index XXX read this shit
|
||||
from the track array */
|
||||
burn_lba_to_msf(d->rlba, &m, &s, &f);
|
||||
q[3] = dec_to_bcd(m); /* rel min */
|
||||
q[4] = dec_to_bcd(s); /* rel sec */
|
||||
q[5] = dec_to_bcd(f); /* rel frame */
|
||||
q[6] = 0; /* zero */
|
||||
burn_lba_to_msf(d->alba, &m, &s, &f);
|
||||
q[7] = dec_to_bcd(m); /* abs min */
|
||||
q[8] = dec_to_bcd(s); /* abs sec */
|
||||
q[9] = dec_to_bcd(f); /* abs frame */
|
||||
break;
|
||||
case 2:
|
||||
/* media catalog number */
|
||||
q[1] = (o->mediacatalog[0] << 4) + o->mediacatalog[1];
|
||||
q[2] = (o->mediacatalog[2] << 4) + o->mediacatalog[3];
|
||||
q[3] = (o->mediacatalog[4] << 4) + o->mediacatalog[5];
|
||||
q[4] = (o->mediacatalog[6] << 4) + o->mediacatalog[7];
|
||||
q[5] = (o->mediacatalog[8] << 4) + o->mediacatalog[9];
|
||||
q[6] = (o->mediacatalog[10] << 4) + o->mediacatalog[11];
|
||||
q[7] = o->mediacatalog[12] << 4;
|
||||
|
||||
q[8] = 0;
|
||||
burn_lba_to_msf(d->alba, &m, &s, &f);
|
||||
q[9] = dec_to_bcd(f); /* abs frame */
|
||||
break;
|
||||
case 3:
|
||||
c = char_to_isrc(isrc->country[0]);
|
||||
/* top 6 bits of [1] is the first country code */
|
||||
q[1] = c << 2;
|
||||
c = char_to_isrc(isrc->country[1]);
|
||||
/* bottom 2 bits of [1] is part of the second country code */
|
||||
q[1] += (c >> 4);
|
||||
/* top 4 bits if [2] is the rest of the second country code */
|
||||
q[2] = c << 4;
|
||||
|
||||
c = char_to_isrc(isrc->owner[0]);
|
||||
/* bottom 4 bits of [2] is part of the first owner code */
|
||||
q[2] += (c >> 2);
|
||||
/* top 2 bits of [3] is the rest of the first owner code */
|
||||
q[3] = c << 6;
|
||||
c = char_to_isrc(isrc->owner[1]);
|
||||
/* bottom 6 bits of [3] is the entire second owner code */
|
||||
q[3] += c;
|
||||
c = char_to_isrc(isrc->owner[2]);
|
||||
/* top 6 bits of [4] are the third owner code */
|
||||
q[4] = c << 2;
|
||||
|
||||
/* [5] is the year in 2 BCD numbers */
|
||||
q[5] = dec_to_bcd(isrc->year % 100);
|
||||
/* [6] is the first 2 digits in the serial */
|
||||
q[6] = dec_to_bcd(isrc->serial % 100);
|
||||
/* [7] is the next 2 digits in the serial */
|
||||
q[7] = dec_to_bcd((isrc->serial / 100) % 100);
|
||||
/* the top 4 bits of [8] is the last serial digit, the rest is
|
||||
zeros */
|
||||
q[8] = dec_to_bcd((isrc->serial / 10000) % 10) << 4;
|
||||
burn_lba_to_msf(d->alba, &m, &s, &f);
|
||||
q[9] = dec_to_bcd(f); /* abs frame */
|
||||
break;
|
||||
}
|
||||
q[0] = (control << 4) + qmode;
|
||||
|
||||
crc = crc_ccitt(q, 10);
|
||||
q[10] = crc >> 8;
|
||||
q[11] = crc & 0xff;
|
||||
}
|
||||
|
||||
int sector_lout(struct burn_write_opts *o, unsigned char control, int mode)
|
||||
{
|
||||
struct burn_drive *d = o->drive;
|
||||
unsigned char subs[96];
|
||||
unsigned char *data;
|
||||
|
||||
data = get_sector(o, mode);
|
||||
if (!data)
|
||||
return 0;
|
||||
convert_data(o, NULL, mode, data);
|
||||
subcode_lout(o, control, subs);
|
||||
convert_subs(o, mode, subs, data);
|
||||
sector_headers(o, data, mode, 0);
|
||||
sector_common(++)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sector_data(struct burn_write_opts *o, struct burn_track *t, int psub)
|
||||
{
|
||||
struct burn_drive *d = o->drive;
|
||||
unsigned char subs[96];
|
||||
unsigned char *data;
|
||||
|
||||
data = get_sector(o, t->mode);
|
||||
if (!data)
|
||||
return 0;
|
||||
convert_data(o, t, t->mode, data);
|
||||
|
||||
if (!t->source->read_sub)
|
||||
subcode_user(o, subs, t->entry->point,
|
||||
t->entry->control, 1, &t->isrc, psub);
|
||||
else if (!t->source->read_sub(t->source, subs, 96))
|
||||
subcode_user(o, subs, t->entry->point,
|
||||
t->entry->control, 1, &t->isrc, psub);
|
||||
convert_subs(o, t->mode, subs, data);
|
||||
|
||||
sector_headers(o, data, t->mode, 0);
|
||||
sector_common(++)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int burn_msf_to_lba(int m, int s, int f)
|
||||
{
|
||||
if (m < 90)
|
||||
return (m * 60 + s) * 75 + f - 150;
|
||||
else
|
||||
return (m * 60 + s) * 75 + f - 450150;
|
||||
}
|
||||
|
||||
void burn_lba_to_msf(int lba, int *m, int *s, int *f)
|
||||
{
|
||||
if (lba >= -150) {
|
||||
*m = (lba + 150) / (60 * 75);
|
||||
*s = (lba + 150 - *m * 60 * 75) / 75;
|
||||
*f = lba + 150 - *m * 60 * 75 - *s * 75;
|
||||
} else {
|
||||
*m = (lba + 450150) / (60 * 75);
|
||||
*s = (lba + 450150 - *m * 60 * 75) / 75;
|
||||
*f = lba + 450150 - *m * 60 * 75 - *s * 75;
|
||||
}
|
||||
}
|
||||
|
||||
int dec_to_bcd(int d)
|
||||
{
|
||||
int top, bottom;
|
||||
|
||||
top = d / 10;
|
||||
bottom = d - (top * 10);
|
||||
return (top << 4) + bottom;
|
||||
}
|
||||
|
||||
void sector_headers(struct burn_write_opts *o, unsigned char *out,
|
||||
int mode, int leadin)
|
||||
{
|
||||
struct burn_drive *d = o->drive;
|
||||
unsigned int crc;
|
||||
int min, sec, frame;
|
||||
int modebyte = -1;
|
||||
|
||||
if (mode & BURN_AUDIO) /* no headers for "audio" */
|
||||
return;
|
||||
if (o->write_type == BURN_WRITE_SAO)
|
||||
return;
|
||||
if (mode & BURN_MODE1)
|
||||
modebyte = 1;
|
||||
|
||||
assert(modebyte == 1);
|
||||
|
||||
out[0] = 0;
|
||||
memset(out + 1, 0xFF, 10); /* sync */
|
||||
out[11] = 0;
|
||||
|
||||
if (leadin) {
|
||||
burn_lba_to_msf(d->rlba, &min, &sec, &frame);
|
||||
out[12] = dec_to_bcd(min) + 0xA0;
|
||||
out[13] = dec_to_bcd(sec);
|
||||
out[14] = dec_to_bcd(frame);
|
||||
out[15] = modebyte;
|
||||
} else {
|
||||
burn_lba_to_msf(d->alba, &min, &sec, &frame);
|
||||
out[12] = dec_to_bcd(min);
|
||||
out[13] = dec_to_bcd(sec);
|
||||
out[14] = dec_to_bcd(frame);
|
||||
out[15] = modebyte;
|
||||
}
|
||||
if (mode & BURN_MODE1) {
|
||||
crc = crc_32(out, 2064);
|
||||
out[2064] = crc & 0xFF;
|
||||
crc >>= 8;
|
||||
out[2065] = crc & 0xFF;
|
||||
crc >>= 8;
|
||||
out[2066] = crc & 0xFF;
|
||||
crc >>= 8;
|
||||
out[2067] = crc & 0xFF;
|
||||
}
|
||||
if (mode & BURN_MODE1) {
|
||||
memset(out + 2068, 0, 8);
|
||||
parity_p(out);
|
||||
parity_q(out);
|
||||
}
|
||||
scramble(out);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void process_q(struct burn_drive *d, unsigned char *q)
|
||||
{
|
||||
unsigned char i[5];
|
||||
int mode;
|
||||
|
||||
mode = q[0] & 0xF;
|
||||
/* burn_print(12, "mode: %d : ", mode);*/
|
||||
switch (mode) {
|
||||
case 1:
|
||||
/* burn_print(12, "tno = %d : ", q[1]);
|
||||
burn_print(12, "index = %d\n", q[2]);
|
||||
*/
|
||||
/* q[1] is the track number (starting at 1) q[2] is the index
|
||||
number (starting at 0) */
|
||||
#warning this is totally bogus
|
||||
if (q[1] - 1 > 99)
|
||||
break;
|
||||
if (q[2] > d->toc->track[q[1] - 1].indices) {
|
||||
burn_print(12, "new index at %d\n", d->alba);
|
||||
d->toc->track[q[1] - 1].index[q[2]] = d->alba;
|
||||
d->toc->track[q[1] - 1].indices++;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* XXX dont ignore these */
|
||||
break;
|
||||
case 3:
|
||||
/* burn_print(12, "ISRC data in mode 3 q\n");*/
|
||||
i[0] = isrc[(q[1] << 2) >> 2];
|
||||
/* burn_print(12, "0x%x 0x%x 0x%x 0x%x 0x%x\n", q[1], q[2], q[3], q[4], q[5]);
|
||||
burn_print(12, "ISRC - %c%c%c%c%c\n", i[0], i[1], i[2], i[3], i[4]);
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* this needs more info. subs in the data? control/adr? */
|
||||
#warning sector_identify needs to be written
|
||||
int sector_identify(unsigned char *data)
|
||||
{
|
||||
scramble(data);
|
||||
/*
|
||||
check mode byte for 1 or 2
|
||||
test parity to see if it's a valid sector
|
||||
if invalid, return BURN_MODE_AUDIO;
|
||||
else return mode byte (what about mode 2 formless? heh)
|
||||
*/
|
||||
return BURN_MODE1;
|
||||
}
|
31
libburn/sector.h
Normal file
31
libburn/sector.h
Normal file
@ -0,0 +1,31 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef __SECTOR
|
||||
#define __SECTOR
|
||||
|
||||
#include "libburn.h"
|
||||
#include "transport.h"
|
||||
|
||||
struct burn_drive;
|
||||
struct isrc;
|
||||
|
||||
int dec_to_bcd(int);
|
||||
|
||||
int sector_toc(struct burn_write_opts *, int mode);
|
||||
int sector_pregap(struct burn_write_opts *, unsigned char tno,
|
||||
unsigned char control, int mode);
|
||||
int sector_postgap(struct burn_write_opts *, unsigned char tno,
|
||||
unsigned char control, int mode);
|
||||
int sector_lout(struct burn_write_opts *, unsigned char control, int mode);
|
||||
int sector_data(struct burn_write_opts *, struct burn_track *t, int psub);
|
||||
void sector_headers(struct burn_write_opts *, unsigned char *,
|
||||
int mode, int leadin);
|
||||
void subcode_user(struct burn_write_opts *, unsigned char *s,
|
||||
unsigned char tno, unsigned char control,
|
||||
unsigned char index, struct isrc *isrc, int psub);
|
||||
|
||||
int sector_identify(unsigned char *);
|
||||
|
||||
void process_q(struct burn_drive *d, unsigned char *q);
|
||||
|
||||
#endif /* __SECTOR */
|
334
libburn/sg.c
Normal file
334
libburn/sg.c
Normal file
@ -0,0 +1,334 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <sys/poll.h>
|
||||
#include <linux/hdreg.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"
|
||||
|
||||
static void enumerate_common(char *fname);
|
||||
|
||||
static int sgio_test(int fd)
|
||||
{
|
||||
unsigned char test_ops[] = { 0, 0, 0, 0, 0, 0 };
|
||||
sg_io_hdr_t s;
|
||||
|
||||
memset(&s, 0, sizeof(sg_io_hdr_t));
|
||||
s.interface_id = 'S';
|
||||
s.dxfer_direction = SG_DXFER_NONE;
|
||||
s.cmd_len = 6;
|
||||
s.cmdp = test_ops;
|
||||
s.timeout = 12345;
|
||||
return ioctl(fd, SG_IO, &s);
|
||||
}
|
||||
|
||||
void ata_enumerate(void)
|
||||
{
|
||||
struct hd_driveid tm;
|
||||
int i, fd;
|
||||
char fname[10];
|
||||
|
||||
for (i = 0; i < 26; i++) {
|
||||
sprintf(fname, "/dev/hd%c", 'a' + i);
|
||||
/* open O_RDWR so we don't think read only drives are
|
||||
in some way useful
|
||||
*/
|
||||
fd = open(fname, O_RDWR | O_NONBLOCK);
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
/* found a drive */
|
||||
ioctl(fd, HDIO_GET_IDENTITY, &tm);
|
||||
|
||||
/* not atapi */
|
||||
if (!(tm.config & 0x8000) || (tm.config & 0x4000)) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if SG_IO fails on an atapi device, we should stop trying to
|
||||
use hd* devices */
|
||||
if (sgio_test(fd) == -1) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
close(fd);
|
||||
enumerate_common(fname);
|
||||
}
|
||||
}
|
||||
|
||||
void sg_enumerate(void)
|
||||
{
|
||||
struct sg_scsi_id sid;
|
||||
int i, fd;
|
||||
char fname[10];
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
sprintf(fname, "/dev/sg%d", i);
|
||||
/* open RDWR so we don't accidentally think read only drives
|
||||
are in some way useful
|
||||
*/
|
||||
fd = open(fname, O_RDWR);
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
/* found a drive */
|
||||
ioctl(fd, SG_GET_SCSI_ID, &sid);
|
||||
close(fd);
|
||||
if (sid.scsi_type != TYPE_ROM)
|
||||
continue;
|
||||
|
||||
enumerate_common(fname);
|
||||
}
|
||||
}
|
||||
|
||||
static void enumerate_common(char *fname)
|
||||
{
|
||||
struct burn_drive *t;
|
||||
struct burn_drive out;
|
||||
|
||||
out.devname = burn_strdup(fname);
|
||||
out.fd = -1337;
|
||||
|
||||
out.grab = sg_grab;
|
||||
out.release = sg_release;
|
||||
out.issue_command = sg_issue_command;
|
||||
out.getcaps = spc_getcaps;
|
||||
out.released = 1;
|
||||
out.status = BURN_DISC_UNREADY;
|
||||
|
||||
out.eject = sbc_eject;
|
||||
out.load = sbc_load;
|
||||
out.lock = spc_prevent;
|
||||
out.unlock = spc_allow;
|
||||
out.read_disc_info = spc_sense_write_params;
|
||||
out.get_erase_progress = spc_get_erase_progress;
|
||||
out.test_unit_ready = spc_test_unit_ready;
|
||||
out.probe_write_modes = spc_probe_write_modes;
|
||||
out.read_toc = mmc_read_toc;
|
||||
out.write = mmc_write;
|
||||
out.erase = mmc_erase;
|
||||
out.read_sectors = mmc_read_sectors;
|
||||
out.perform_opc = mmc_perform_opc;
|
||||
out.set_speed = mmc_set_speed;
|
||||
out.send_parameters = spc_select_error_params;
|
||||
out.send_write_parameters = spc_select_write_params;
|
||||
out.send_cue_sheet = mmc_send_cue_sheet;
|
||||
out.sync_cache = mmc_sync_cache;
|
||||
out.get_nwa = mmc_get_nwa;
|
||||
out.close_disc = mmc_close_disc;
|
||||
out.close_session = mmc_close_session;
|
||||
out.idata = malloc(sizeof(struct scsi_inquiry_data));
|
||||
out.idata->valid = 0;
|
||||
out.mdata = malloc(sizeof(struct scsi_mode_data));
|
||||
out.mdata->valid = 0;
|
||||
memset(&out.params, 0, sizeof(struct params));
|
||||
t = burn_drive_register(&out);
|
||||
|
||||
/* try to get the drive info */
|
||||
if (sg_grab(t)) {
|
||||
burn_print(2, "getting drive info\n");
|
||||
t->getcaps(t);
|
||||
t->unlock(t);
|
||||
t->released = 1;
|
||||
} else {
|
||||
burn_print(2, "unable to grab new located drive\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
we use the sg reference count to decide whether we can use the
|
||||
drive or not.
|
||||
if refcount is not one, drive is open somewhere else.
|
||||
*/
|
||||
int sg_grab(struct burn_drive *d)
|
||||
{
|
||||
int fd, count;
|
||||
|
||||
fd = open(d->devname, O_RDWR | O_NONBLOCK);
|
||||
assert(fd != -1337);
|
||||
if (-1 != fd) {
|
||||
/* er = ioctl(fd, SG_GET_ACCESS_COUNT, &count);*/
|
||||
count = 1;
|
||||
if (1 == count) {
|
||||
d->fd = fd;
|
||||
fcntl(fd, F_SETOWN, getpid());
|
||||
d->released = 0;
|
||||
return 1;
|
||||
}
|
||||
burn_print(1, "could not acquire drive - already open\n");
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
burn_print(1, "could not acquire drive\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
non zero return means you still have the drive and it's not
|
||||
in a state to be released? (is that even possible?)
|
||||
*/
|
||||
|
||||
int sg_release(struct burn_drive *d)
|
||||
{
|
||||
if (d->fd < 1) {
|
||||
burn_print(1, "release an ungrabbed drive. die\n");
|
||||
return 0;
|
||||
}
|
||||
close(d->fd);
|
||||
d->fd = -1337;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sg_issue_command(struct burn_drive *d, struct command *c)
|
||||
{
|
||||
int done = 0;
|
||||
int err;
|
||||
sg_io_hdr_t s;
|
||||
|
||||
c->error = 0;
|
||||
/*
|
||||
this is valid during the mode probe in scan
|
||||
if (d->fd < 1 || d->released) {
|
||||
burn_print(1,
|
||||
"command issued on ungrabbed drive, chaos.\n");
|
||||
burn_print(1, "fd = %d, released = %d\n", d->fd,
|
||||
d->released);
|
||||
}
|
||||
*/
|
||||
memset(&s, 0, sizeof(sg_io_hdr_t));
|
||||
|
||||
s.interface_id = 'S';
|
||||
|
||||
if (c->dir == TO_DRIVE)
|
||||
s.dxfer_direction = SG_DXFER_TO_DEV;
|
||||
else if (c->dir == FROM_DRIVE)
|
||||
s.dxfer_direction = SG_DXFER_FROM_DEV;
|
||||
else if (c->dir == NO_TRANSFER) {
|
||||
s.dxfer_direction = SG_DXFER_NONE;
|
||||
assert(!c->page);
|
||||
}
|
||||
s.cmd_len = c->oplen;
|
||||
s.cmdp = c->opcode;
|
||||
s.mx_sb_len = 32;
|
||||
s.sbp = c->sense;
|
||||
memset(c->sense, 0, sizeof(c->sense));
|
||||
s.timeout = 200000;
|
||||
if (c->page) {
|
||||
s.dxferp = c->page->data;
|
||||
if (c->dir == FROM_DRIVE) {
|
||||
s.dxfer_len = BUFFER_SIZE;
|
||||
/* touch page so we can use valgrind */
|
||||
memset(c->page->data, 0, BUFFER_SIZE);
|
||||
} else {
|
||||
assert(c->page->bytes > 0);
|
||||
s.dxfer_len = c->page->bytes;
|
||||
}
|
||||
} else {
|
||||
s.dxferp = NULL;
|
||||
s.dxfer_len = 0;
|
||||
}
|
||||
s.usr_ptr = c;
|
||||
|
||||
do {
|
||||
err = ioctl(d->fd, SG_IO, &s);
|
||||
assert(err != -1);
|
||||
if (s.sb_len_wr) {
|
||||
if (!c->retry) {
|
||||
c->error = 1;
|
||||
return 1;
|
||||
}
|
||||
switch (scsi_error(d, s.sbp, s.sb_len_wr)) {
|
||||
case RETRY:
|
||||
done = 0;
|
||||
break;
|
||||
case FAIL:
|
||||
done = 1;
|
||||
c->error = 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
done = 1;
|
||||
}
|
||||
} while (!done);
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum response scsi_error(struct burn_drive *d, unsigned char *sense,
|
||||
int senselen)
|
||||
{
|
||||
int key, asc, ascq;
|
||||
|
||||
senselen = senselen;
|
||||
key = sense[2];
|
||||
asc = sense[12];
|
||||
ascq = sense[13];
|
||||
|
||||
burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n",
|
||||
key, asc, ascq, d->idata->vendor, d->idata->product);
|
||||
|
||||
switch (asc) {
|
||||
case 0:
|
||||
burn_print(12, "NO ERROR!\n");
|
||||
return RETRY;
|
||||
|
||||
case 2:
|
||||
burn_print(1, "not ready\n");
|
||||
return RETRY;
|
||||
case 4:
|
||||
burn_print(1,
|
||||
"logical unit is in the process of becoming ready\n");
|
||||
return RETRY;
|
||||
case 0x20:
|
||||
if (key == 5)
|
||||
burn_print(1, "bad opcode\n");
|
||||
return FAIL;
|
||||
case 0x21:
|
||||
burn_print(1, "invalid address or something\n");
|
||||
return FAIL;
|
||||
case 0x24:
|
||||
if (key == 5)
|
||||
burn_print(1, "invalid field in cdb\n");
|
||||
else
|
||||
break;
|
||||
return FAIL;
|
||||
case 0x26:
|
||||
if ( key == 5 )
|
||||
burn_print( 1, "invalid field in parameter list\n" );
|
||||
return FAIL;
|
||||
case 0x28:
|
||||
if (key == 6)
|
||||
burn_print(1,
|
||||
"Not ready to ready change, medium may have changed\n");
|
||||
else
|
||||
break;
|
||||
return RETRY;
|
||||
case 0x3A:
|
||||
burn_print(12, "Medium not present in %s %s\n",
|
||||
d->idata->vendor, d->idata->product);
|
||||
|
||||
d->status = BURN_DISC_EMPTY;
|
||||
return FAIL;
|
||||
}
|
||||
burn_print(1, "unknown failure\n");
|
||||
burn_print(1, "key:0x%x, asc:0x%x, ascq:0x%x\n", key, asc, ascq);
|
||||
return FAIL;
|
||||
}
|
19
libburn/sg.h
Normal file
19
libburn/sg.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef __SG
|
||||
#define __SG
|
||||
|
||||
struct burn_drive;
|
||||
struct command;
|
||||
|
||||
enum response
|
||||
{ RETRY, FAIL };
|
||||
|
||||
void sg_enumerate(void);
|
||||
void ata_enumerate(void);
|
||||
int sg_grab(struct burn_drive *);
|
||||
int sg_release(struct burn_drive *);
|
||||
int sg_issue_command(struct burn_drive *, struct command *);
|
||||
enum response scsi_error(struct burn_drive *, unsigned char *, int);
|
||||
|
||||
#endif /* __SG */
|
36
libburn/source.c
Normal file
36
libburn/source.c
Normal file
@ -0,0 +1,36 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "libburn.h"
|
||||
#include "source.h"
|
||||
#include "structure.h"
|
||||
|
||||
void burn_source_free(struct burn_source *src)
|
||||
{
|
||||
if (--src->refcount < 1) {
|
||||
if (src->free_data)
|
||||
src->free_data(src);
|
||||
free(src);
|
||||
}
|
||||
}
|
||||
|
||||
enum burn_source_status burn_track_set_source(struct burn_track *t,
|
||||
struct burn_source *s)
|
||||
{
|
||||
if (!s->read)
|
||||
return BURN_SOURCE_FAILED;
|
||||
s->refcount++;
|
||||
t->source = s;
|
||||
return BURN_SOURCE_OK;
|
||||
}
|
||||
|
||||
struct burn_source *burn_source_new(void)
|
||||
{
|
||||
struct burn_source *out;
|
||||
|
||||
out = malloc(sizeof(struct burn_source));
|
||||
memset(out, 0, sizeof(struct burn_source));
|
||||
out->refcount = 1;
|
||||
return out;
|
||||
}
|
8
libburn/source.h
Normal file
8
libburn/source.h
Normal file
@ -0,0 +1,8 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef __SOURCE
|
||||
#define __SOURCE
|
||||
|
||||
struct burn_source *burn_source_new(void);
|
||||
|
||||
#endif /*__SOURCE*/
|
405
libburn/spc.c
Normal file
405
libburn/spc.c
Normal file
@ -0,0 +1,405 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
/* scsi primary commands */
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "transport.h"
|
||||
#include "spc.h"
|
||||
#include "mmc.h"
|
||||
#include "sbc.h"
|
||||
#include "drive.h"
|
||||
#include "debug.h"
|
||||
#include "options.h"
|
||||
|
||||
/* spc command set */
|
||||
static unsigned char SPC_INQUIRY[] = { 0x12, 0, 0, 0, 255, 0 };
|
||||
|
||||
/*static char SPC_TEST[]={0,0,0,0,0,0};*/
|
||||
static unsigned char SPC_PREVENT[] = { 0x1e, 0, 0, 0, 1, 0 };
|
||||
static unsigned char SPC_ALLOW[] = { 0x1e, 0, 0, 0, 0, 0 };
|
||||
static unsigned char SPC_MODE_SENSE[] = { 0x5a, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
|
||||
static unsigned char SPC_MODE_SELECT[] =
|
||||
{ 0x55, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
static unsigned char SPC_REQUEST_SENSE[] = { 0x03, 0, 0, 0, 18, 0 };
|
||||
static unsigned char SPC_TEST_UNIT_READY[] = { 0x00, 0, 0, 0, 0, 0 };
|
||||
|
||||
int spc_test_unit_ready(struct burn_drive *d)
|
||||
{
|
||||
struct command c;
|
||||
|
||||
c.retry = 0;
|
||||
c.oplen = sizeof(SPC_TEST_UNIT_READY);
|
||||
memcpy(c.opcode, SPC_TEST_UNIT_READY, sizeof(SPC_TEST_UNIT_READY));
|
||||
c.page = NULL;
|
||||
c.dir = NO_TRANSFER;
|
||||
d->issue_command(d, &c);
|
||||
if (c.error)
|
||||
return (c.sense[2] & 0xF) == 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void spc_request_sense(struct burn_drive *d, struct buffer *buf)
|
||||
{
|
||||
struct command c;
|
||||
|
||||
c.retry = 0;
|
||||
c.oplen = sizeof(SPC_REQUEST_SENSE);
|
||||
memcpy(c.opcode, SPC_REQUEST_SENSE, sizeof(SPC_REQUEST_SENSE));
|
||||
c.page = buf;
|
||||
c.page->sectors = 0;
|
||||
c.page->bytes = 0;
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
int spc_get_erase_progress(struct burn_drive *d)
|
||||
{
|
||||
struct buffer b;
|
||||
|
||||
spc_request_sense(d, &b);
|
||||
return (b.data[16] << 8) | b.data[17];
|
||||
}
|
||||
|
||||
void spc_inquiry(struct burn_drive *d)
|
||||
{
|
||||
struct buffer buf;
|
||||
struct scsi_inquiry_data *id;
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, SPC_INQUIRY, sizeof(SPC_INQUIRY));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(SPC_INQUIRY);
|
||||
c.page = &buf;
|
||||
c.page->bytes = 0;
|
||||
c.page->sectors = 0;
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
|
||||
id = (struct scsi_inquiry_data *)d->idata;
|
||||
id->vendor[8] = 0;
|
||||
id->product[16] = 0;
|
||||
id->revision[4] = 0;
|
||||
|
||||
memcpy(id->vendor, c.page->data + 8, 8);
|
||||
memcpy(id->product, c.page->data + 16, 16);
|
||||
memcpy(id->revision, c.page->data + 32, 4);
|
||||
|
||||
id->valid = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
void spc_prevent(struct burn_drive *d)
|
||||
{
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, SPC_PREVENT, sizeof(SPC_PREVENT));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(SPC_PREVENT);
|
||||
c.page = NULL;
|
||||
c.dir = NO_TRANSFER;
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
void spc_allow(struct burn_drive *d)
|
||||
{
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, SPC_ALLOW, sizeof(SPC_ALLOW));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(SPC_ALLOW);
|
||||
c.page = NULL;
|
||||
c.dir = NO_TRANSFER;
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
void spc_sense_caps(struct burn_drive *d)
|
||||
{
|
||||
struct buffer buf;
|
||||
struct scsi_mode_data *m;
|
||||
int size;
|
||||
unsigned char *page;
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(SPC_MODE_SENSE);
|
||||
c.opcode[2] = 0x2A;
|
||||
c.page = &buf;
|
||||
c.page->bytes = 0;
|
||||
c.page->sectors = 0;
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
|
||||
size = c.page->data[0] * 256 + c.page->data[1];
|
||||
m = d->mdata;
|
||||
page = c.page->data + 8;
|
||||
|
||||
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->max_read_speed = page[8] * 256 + page[9];
|
||||
m->cur_read_speed = page[14] * 256 + page[15];
|
||||
m->max_write_speed = page[18] * 256 + page[19];
|
||||
m->cur_write_speed = page[20] * 256 + page[21];
|
||||
m->c2_pointers = page[5] & 16;
|
||||
m->valid = 1;
|
||||
m->underrun_proof = page[4] & 128;
|
||||
}
|
||||
|
||||
void spc_sense_error_params(struct burn_drive *d)
|
||||
{
|
||||
struct buffer buf;
|
||||
struct scsi_mode_data *m;
|
||||
int size;
|
||||
unsigned char *page;
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(SPC_MODE_SENSE);
|
||||
c.opcode[2] = 0x01;
|
||||
c.page = &buf;
|
||||
c.page->bytes = 0;
|
||||
c.page->sectors = 0;
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
|
||||
size = c.page->data[0] * 256 + c.page->data[1];
|
||||
m = d->mdata;
|
||||
page = c.page->data + 8;
|
||||
d->params.retries = page[3];
|
||||
m->retry_page_length = page[1];
|
||||
m->retry_page_valid = 1;
|
||||
}
|
||||
|
||||
void spc_select_error_params(struct burn_drive *d,
|
||||
const struct burn_read_opts *o)
|
||||
{
|
||||
struct buffer buf;
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(SPC_MODE_SELECT);
|
||||
c.opcode[8] = 8 + 2 + d->mdata->retry_page_length;
|
||||
c.page = &buf;
|
||||
c.page->bytes = 0;
|
||||
c.page->sectors = 0;
|
||||
assert(d->mdata->valid);
|
||||
memset(c.page->data, 0, 8 + 2 + d->mdata->retry_page_length);
|
||||
c.page->bytes = 8 + 2 + d->mdata->retry_page_length;
|
||||
c.page->data[8] = 1;
|
||||
c.page->data[9] = d->mdata->retry_page_length;
|
||||
if (o->transfer_damaged_blocks)
|
||||
c.page->data[10] |= 32;
|
||||
if (o->report_recovered_errors)
|
||||
c.page->data[10] |= 4;
|
||||
if (!o->hardware_error_recovery)
|
||||
c.page->data[10] |= 1;
|
||||
/*burn_print(1, "error parameter 0x%x\n", c->page->data[10]);*/
|
||||
c.page->data[11] = d->params.retries;
|
||||
c.dir = TO_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
void spc_sense_write_params(struct burn_drive *d)
|
||||
{
|
||||
struct buffer buf;
|
||||
struct scsi_mode_data *m;
|
||||
int size;
|
||||
unsigned char *page;
|
||||
struct command c;
|
||||
|
||||
assert(d->mdata->cdr_write || d->mdata->cdrw_write ||
|
||||
d->mdata->dvdr_write || d->mdata->dvdram_write);
|
||||
|
||||
memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(SPC_MODE_SENSE);
|
||||
c.opcode[2] = 0x05;
|
||||
c.page = &buf;
|
||||
c.page->bytes = 0;
|
||||
c.page->sectors = 0;
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
|
||||
size = c.page->data[0] * 256 + c.page->data[1];
|
||||
m = d->mdata;
|
||||
page = c.page->data + 8;
|
||||
burn_print(1, "write page length 0x%x\n", page[1]);
|
||||
m->write_page_length = page[1];
|
||||
m->write_page_valid = 1;
|
||||
mmc_read_disc_info(d);
|
||||
}
|
||||
|
||||
void spc_select_write_params(struct burn_drive *d,
|
||||
const struct burn_write_opts *o)
|
||||
{
|
||||
struct buffer buf;
|
||||
struct command c;
|
||||
int bufe, sim;
|
||||
|
||||
assert(o->drive == d);
|
||||
memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(SPC_MODE_SELECT);
|
||||
c.opcode[8] = 8 + 2 + d->mdata->write_page_length;
|
||||
c.page = &buf;
|
||||
c.page->bytes = 0;
|
||||
c.page->sectors = 0;
|
||||
assert(d->mdata->valid);
|
||||
memset(c.page->data, 0, 8 + 2 + d->mdata->write_page_length);
|
||||
c.page->bytes = 8 + 2 + d->mdata->write_page_length;
|
||||
c.page->data[8] = 5;
|
||||
c.page->data[9] = d->mdata->write_page_length;
|
||||
|
||||
burn_print(12, "using write page length %d (valid %d)\n",
|
||||
d->mdata->write_page_length, d->mdata->write_page_valid);
|
||||
bufe = o->underrun_proof;
|
||||
sim = o->simulate;
|
||||
c.page->data[10] = (bufe << 6)
|
||||
+ (sim << 4)
|
||||
+ o->write_type;
|
||||
c.page->data[11] = (o->multi << 6) | o->control;
|
||||
c.page->data[12] = spc_block_type(o->block_type);
|
||||
c.page->data[22] = 0;
|
||||
c.page->data[23] = 150; /* audio pause length */
|
||||
/*XXX need session format! */
|
||||
c.dir = TO_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
void spc_getcaps(struct burn_drive *d)
|
||||
{
|
||||
spc_inquiry(d);
|
||||
spc_sense_caps(d);
|
||||
spc_sense_error_params(d);
|
||||
}
|
||||
|
||||
/*
|
||||
only called when a blank is present, so we set type to blank
|
||||
(on the last pass)
|
||||
|
||||
don't check totally stupid modes (raw/raw0)
|
||||
some drives say they're ok, and they're not.
|
||||
*/
|
||||
|
||||
void spc_probe_write_modes(struct burn_drive *d)
|
||||
{
|
||||
struct buffer buf;
|
||||
int try_write_type = 1;
|
||||
int try_block_type = 0;
|
||||
int key, asc, ascq;
|
||||
struct command c;
|
||||
|
||||
while (try_write_type != 4) {
|
||||
burn_print(9, "trying %d, %d\n", try_write_type,
|
||||
try_block_type);
|
||||
memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(SPC_MODE_SELECT);
|
||||
c.opcode[8] = 8 + 2 + 0x32;
|
||||
c.page = &buf;
|
||||
|
||||
memset(c.page->data, 0, 8 + 2 + 0x32);
|
||||
c.page->bytes = 8 + 2 + 0x32;
|
||||
|
||||
c.page->data[8] = 5;
|
||||
c.page->data[9] = 0x32;
|
||||
c.page->data[10] = try_write_type;
|
||||
if (try_block_type > 4)
|
||||
c.page->data[11] = 4;
|
||||
else
|
||||
c.page->data[11] = 0;
|
||||
c.page->data[12] = try_block_type;
|
||||
c.page->data[23] = 150;
|
||||
c.dir = TO_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
|
||||
key = c.sense[2];
|
||||
asc = c.sense[12];
|
||||
ascq = c.sense[13];
|
||||
|
||||
if (key)
|
||||
burn_print(7, "%d not supported\n", try_block_type);
|
||||
else {
|
||||
burn_print(7, "%d:%d SUPPORTED MODE!\n",
|
||||
try_write_type, try_block_type);
|
||||
if (try_write_type == 2) /* sao */
|
||||
d->block_types[try_write_type] =
|
||||
BURN_BLOCK_SAO;
|
||||
else
|
||||
d->block_types[try_write_type] |=
|
||||
1 << try_block_type;
|
||||
}
|
||||
switch (try_block_type) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
try_block_type++;
|
||||
break;
|
||||
case 3:
|
||||
try_block_type = 8;
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
try_block_type++;
|
||||
break;
|
||||
case 13:
|
||||
try_block_type = 0;
|
||||
try_write_type++;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
assert(0);
|
||||
}
|
25
libburn/spc.h
Normal file
25
libburn/spc.h
Normal file
@ -0,0 +1,25 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef __SPC
|
||||
#define __SPC
|
||||
|
||||
#include "libburn.h"
|
||||
|
||||
void spc_inquiry(struct burn_drive *);
|
||||
void spc_prevent(struct burn_drive *);
|
||||
void spc_allow(struct burn_drive *);
|
||||
void spc_sense_caps(struct burn_drive *);
|
||||
void spc_sense_error_params(struct burn_drive *);
|
||||
void spc_select_error_params(struct burn_drive *,
|
||||
const struct burn_read_opts *);
|
||||
void spc_getcaps(struct burn_drive *d);
|
||||
void spc_sense_write_params(struct burn_drive *);
|
||||
void spc_select_write_params(struct burn_drive *,
|
||||
const struct burn_write_opts *);
|
||||
void spc_probe_write_modes(struct burn_drive *);
|
||||
void spc_request_sense(struct burn_drive *d, struct buffer *buf);
|
||||
int spc_block_type(enum burn_block_types b);
|
||||
int spc_get_erase_progress(struct burn_drive *d);
|
||||
int spc_test_unit_ready(struct burn_drive *d);
|
||||
|
||||
#endif /*__SPC*/
|
305
libburn/structure.c
Normal file
305
libburn/structure.c
Normal file
@ -0,0 +1,305 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "libburn.h"
|
||||
#include "structure.h"
|
||||
#include "write.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define RESIZE(TO, NEW, pos) {\
|
||||
void *tmp;\
|
||||
\
|
||||
assert(!(pos > BURN_POS_END));\
|
||||
if (pos == BURN_POS_END)\
|
||||
pos = TO->NEW##s;\
|
||||
if (pos > TO->NEW##s)\
|
||||
return 0;\
|
||||
\
|
||||
tmp = realloc(TO->NEW, sizeof(struct NEW *) * (TO->NEW##s + 1));\
|
||||
if (!tmp)\
|
||||
return 0;\
|
||||
TO->NEW = tmp;\
|
||||
memmove(TO->NEW + pos + 1, TO->NEW + pos,\
|
||||
sizeof(struct NEW *) * (TO->NEW##s - pos));\
|
||||
TO->NEW##s++;\
|
||||
}
|
||||
|
||||
struct burn_disc *burn_disc_create(void)
|
||||
{
|
||||
struct burn_disc *d;
|
||||
d = malloc(sizeof(struct burn_disc));
|
||||
memset(d, 0, sizeof(struct burn_disc));
|
||||
d->refcnt = 1;
|
||||
d->sessions = 0;
|
||||
d->session = NULL;
|
||||
return d;
|
||||
}
|
||||
|
||||
void burn_disc_free(struct burn_disc *d)
|
||||
{
|
||||
d->refcnt--;
|
||||
if (d->refcnt == 0) {
|
||||
/* dec refs on all elements */
|
||||
int i;
|
||||
|
||||
for (i = 0; i < d->sessions; i++)
|
||||
burn_session_free(d->session[i]);
|
||||
free(d->session);
|
||||
free(d);
|
||||
}
|
||||
}
|
||||
|
||||
struct burn_session *burn_session_create(void)
|
||||
{
|
||||
struct burn_session *s;
|
||||
s = malloc(sizeof(struct burn_session));
|
||||
memset(s, 0, sizeof(struct burn_session));
|
||||
s->refcnt = 1;
|
||||
s->tracks = 0;
|
||||
s->track = NULL;
|
||||
s->hidefirst = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
void burn_session_hide_first_track(struct burn_session *s, int onoff)
|
||||
{
|
||||
s->hidefirst = onoff;
|
||||
}
|
||||
|
||||
void burn_session_free(struct burn_session *s)
|
||||
{
|
||||
s->refcnt--;
|
||||
if (s->refcnt == 0) {
|
||||
/* dec refs on all elements */
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->tracks; i++)
|
||||
burn_track_free(s->track[i]);
|
||||
free(s->track);
|
||||
free(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int burn_disc_add_session(struct burn_disc *d, struct burn_session *s,
|
||||
unsigned int pos)
|
||||
{
|
||||
RESIZE(d, session, pos);
|
||||
d->session[pos] = s;
|
||||
s->refcnt++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct burn_track *burn_track_create(void)
|
||||
{
|
||||
struct burn_track *t;
|
||||
t = malloc(sizeof(struct burn_track));
|
||||
memset(t, 0, sizeof(struct burn_track));
|
||||
t->refcnt = 1;
|
||||
t->indices = 0;
|
||||
t->offset = 0;
|
||||
t->offsetcount = 0;
|
||||
t->tail = 0;
|
||||
t->tailcount = 0;
|
||||
t->mode = BURN_MODE1;
|
||||
t->isrc.has_isrc = 0;
|
||||
t->pad = 1;
|
||||
t->entry = NULL;
|
||||
t->source = NULL;
|
||||
t->postgap = 0;
|
||||
t->pregap1 = 0;
|
||||
t->pregap2 = 0;
|
||||
return t;
|
||||
}
|
||||
|
||||
void burn_track_free(struct burn_track *t)
|
||||
{
|
||||
t->refcnt--;
|
||||
if (t->refcnt == 0) {
|
||||
/* dec refs on all elements */
|
||||
if (t->source)
|
||||
burn_source_free(t->source);
|
||||
free(t);
|
||||
}
|
||||
}
|
||||
|
||||
int burn_session_add_track(struct burn_session *s, struct burn_track *t,
|
||||
unsigned int pos)
|
||||
{
|
||||
RESIZE(s, track, pos);
|
||||
s->track[pos] = t;
|
||||
t->refcnt++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int burn_session_remove_track(struct burn_session *s, struct burn_track *t)
|
||||
{
|
||||
struct burn_track **tmp;
|
||||
int i, pos = -1;
|
||||
|
||||
assert(s->track != NULL);
|
||||
|
||||
burn_track_free(t);
|
||||
|
||||
/* Find the position */
|
||||
for (i = 0; i < s->tracks; i++) {
|
||||
if (t == s->track[i])
|
||||
pos = i;
|
||||
}
|
||||
|
||||
if (pos == -1)
|
||||
return 0;
|
||||
|
||||
/* Is it the last track? */
|
||||
if (pos != s->tracks) {
|
||||
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)
|
||||
return 0;
|
||||
s->track = tmp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void burn_structure_print_disc(struct burn_disc *d)
|
||||
{
|
||||
int i;
|
||||
|
||||
burn_print(12, "This disc has %d sessions\n", d->sessions);
|
||||
for (i = 0; i < d->sessions; i++) {
|
||||
burn_structure_print_session(d->session[i]);
|
||||
}
|
||||
}
|
||||
void burn_structure_print_session(struct burn_session *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
burn_print(12, " Session has %d tracks\n", s->tracks);
|
||||
for (i = 0; i < s->tracks; i++) {
|
||||
burn_structure_print_track(s->track[i]);
|
||||
}
|
||||
}
|
||||
void burn_structure_print_track(struct burn_track *t)
|
||||
{
|
||||
burn_print(12, "(%p) track size %d sectors\n", t,
|
||||
burn_track_get_sectors(t));
|
||||
}
|
||||
|
||||
void burn_track_define_data(struct burn_track *t, int offset, int tail,
|
||||
int pad, int mode)
|
||||
{
|
||||
t->offset = offset;
|
||||
t->pad = pad;
|
||||
t->mode = mode;
|
||||
t->tail = tail;
|
||||
}
|
||||
|
||||
void burn_track_set_isrc(struct burn_track *t, char *country, char *owner,
|
||||
unsigned char year, unsigned int serial)
|
||||
{
|
||||
int i;
|
||||
|
||||
t->isrc.has_isrc = 1;
|
||||
for (i = 0; i < 2; ++i) {
|
||||
assert((country[i] >= '0' || country[i] < '9') &&
|
||||
(country[i] >= 'a' || country[i] < 'z') &&
|
||||
(country[i] >= 'A' || country[i] < 'Z'));
|
||||
t->isrc.country[i] = country[i];
|
||||
}
|
||||
for (i = 0; i < 3; ++i) {
|
||||
assert((owner[i] >= '0' || owner[i] < '9') &&
|
||||
(owner[i] >= 'a' || owner[i] < 'z') &&
|
||||
(owner[i] >= 'A' || owner[i] < 'Z'));
|
||||
t->isrc.owner[i] = owner[i];
|
||||
}
|
||||
assert(year <= 99);
|
||||
t->isrc.year = year;
|
||||
assert(serial <= 99999);
|
||||
t->isrc.serial = serial;
|
||||
}
|
||||
|
||||
void burn_track_clear_isrc(struct burn_track *t)
|
||||
{
|
||||
t->isrc.has_isrc = 0;
|
||||
}
|
||||
|
||||
int burn_track_get_sectors(struct burn_track *t)
|
||||
{
|
||||
int size;
|
||||
int sectors, seclen;
|
||||
|
||||
seclen = burn_sector_length(t->mode);
|
||||
size = t->offset + t->source->get_size(t->source) + t->tail;
|
||||
sectors = size / seclen;
|
||||
if (size % seclen)
|
||||
sectors++;
|
||||
burn_print(1, "%d sectors of %d length\n", sectors, seclen);
|
||||
return sectors;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
memcpy(entry, t->entry, sizeof(struct burn_toc_entry));
|
||||
}
|
||||
|
||||
void burn_session_get_leadout_entry(struct burn_session *s,
|
||||
struct burn_toc_entry *entry)
|
||||
{
|
||||
memcpy(entry, s->leadout_entry, sizeof(struct burn_toc_entry));
|
||||
}
|
||||
|
||||
struct burn_session **burn_disc_get_sessions(struct burn_disc *d, int *num)
|
||||
{
|
||||
*num = d->sessions;
|
||||
return d->session;
|
||||
}
|
||||
|
||||
struct burn_track **burn_session_get_tracks(struct burn_session *s, int *num)
|
||||
{
|
||||
*num = s->tracks;
|
||||
return s->track;
|
||||
}
|
||||
|
||||
int burn_track_get_mode(struct burn_track *track)
|
||||
{
|
||||
return track->mode;
|
||||
}
|
||||
|
||||
int burn_session_get_hidefirst(struct burn_session *session)
|
||||
{
|
||||
return session->hidefirst;
|
||||
}
|
70
libburn/structure.h
Normal file
70
libburn/structure.h
Normal file
@ -0,0 +1,70 @@
|
||||
#ifndef BURN__STRUCTURE_H
|
||||
#define BURN__STRUCTURE_H
|
||||
|
||||
struct isrc
|
||||
{
|
||||
int has_isrc;
|
||||
char country[2]; /* each must be 0-9, A-Z */
|
||||
char owner[3]; /* each must be 0-9, A-Z */
|
||||
unsigned char year; /* must be 0-99 */
|
||||
unsigned int serial; /* must be 0-99999 */
|
||||
};
|
||||
|
||||
struct burn_track
|
||||
{
|
||||
int refcnt;
|
||||
struct burn_toc_entry *entry;
|
||||
unsigned char indices;
|
||||
/* lba address of the index */
|
||||
unsigned int index[99];
|
||||
/** number of 0 bytes to write before data */
|
||||
int offset;
|
||||
/** how much offset has been used */
|
||||
int offsetcount;
|
||||
/** Number of zeros to write after data */
|
||||
int tail;
|
||||
/** how much tail has been used */
|
||||
int tailcount;
|
||||
/** 1 means Pad with zeros, 0 means start reading the next track */
|
||||
int pad;
|
||||
/** Data source */
|
||||
struct burn_source *source;
|
||||
/** End of Source flag */
|
||||
int eos;
|
||||
/** The audio/data mode for the entry. Derived from control and
|
||||
possibly from reading the track's first sector. */
|
||||
int mode;
|
||||
/** The track contains interval one of a pregap */
|
||||
int pregap1;
|
||||
/** The track contains interval two of a pregap */
|
||||
int pregap2;
|
||||
/** The track contains a postgap */
|
||||
int postgap;
|
||||
struct isrc isrc;
|
||||
};
|
||||
|
||||
struct burn_session
|
||||
{
|
||||
unsigned char firsttrack;
|
||||
unsigned char lasttrack;
|
||||
int hidefirst;
|
||||
unsigned char start_m;
|
||||
unsigned char start_s;
|
||||
unsigned char start_f;
|
||||
struct burn_toc_entry *leadout_entry;
|
||||
|
||||
int tracks;
|
||||
struct burn_track **track;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
struct burn_disc
|
||||
{
|
||||
int sessions;
|
||||
struct burn_session **session;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
int burn_track_get_shortage(struct burn_track *t);
|
||||
|
||||
#endif /* BURN__STRUCTURE_H */
|
130
libburn/toc.c
Normal file
130
libburn/toc.c
Normal file
@ -0,0 +1,130 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "toc.h"
|
||||
#include "transport.h"
|
||||
#include "libburn.h"
|
||||
#include "sector.h"
|
||||
#include "options.h"
|
||||
|
||||
#if 0
|
||||
static void write_clonecd2(volatile struct toc *toc, int f);
|
||||
|
||||
static void write_clonecd2(volatile struct toc *toc, int f)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* header */
|
||||
dprintf(f, "[CloneCD]\r\n");
|
||||
dprintf(f, "Version=2\r\n");
|
||||
dprintf(f, "\r\n");
|
||||
|
||||
/* disc data */
|
||||
dprintf(f, "[Disc]\r\n");
|
||||
|
||||
dprintf(f, "TocEntries=%d\r\n", toc->toc_entries);
|
||||
dprintf(f, "Sessions=%d\r\n", toc->sessions);
|
||||
dprintf(f, "DataTracksScrambled=%d\r\n", toc->datatracksscrambled);
|
||||
dprintf(f, "CDTextLength=%d\r\n", toc->cdtextlength);
|
||||
dprintf(f, "\r\n");
|
||||
|
||||
/* session data */
|
||||
for (i = 0; i < toc->sessions; ++i) {
|
||||
dprintf(f, "[Session %d]\r\n", i + 1);
|
||||
|
||||
{
|
||||
int m;
|
||||
|
||||
switch (toc->session[i].track[0]->mode) {
|
||||
case BURN_MODE_RAW_DATA:
|
||||
case BURN_MODE_AUDIO:
|
||||
m = 0;
|
||||
break;
|
||||
case BURN_MODE0:
|
||||
m = 1;
|
||||
break;
|
||||
case BURN_MODE1:
|
||||
case BURN_MODE2_FORMLESS:
|
||||
case BURN_MODE2_FORM1:
|
||||
case BURN_MODE2_FORM2:
|
||||
case BURN_MODE_UNINITIALIZED:
|
||||
assert(0); /* unhandled! find out ccd's
|
||||
value for these modes! */
|
||||
}
|
||||
dprintf(f, "PreGapMode=%d\r\n", m);
|
||||
}
|
||||
dprintf(f, "\r\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < toc->toc_entries; ++i) {
|
||||
dprintf(f, "[Entry %d]\r\n", i);
|
||||
|
||||
dprintf(f, "Session=%d\r\n", toc->toc_entry[i].session);
|
||||
dprintf(f, "Point=0x%02x\r\n", toc->toc_entry[i].point);
|
||||
dprintf(f, "ADR=0x%02x\r\n", toc->toc_entry[i].adr);
|
||||
dprintf(f, "Control=0x%02x\r\n", toc->toc_entry[i].control);
|
||||
dprintf(f, "TrackNo=%d\r\n", toc->toc_entry[i].tno);
|
||||
dprintf(f, "AMin=%d\r\n", toc->toc_entry[i].min);
|
||||
dprintf(f, "ASec=%d\r\n", toc->toc_entry[i].sec);
|
||||
dprintf(f, "AFrame=%d\r\n", toc->toc_entry[i].frame);
|
||||
dprintf(f, "ALBA=%d\r\n",
|
||||
burn_msf_to_lba(toc->toc_entry[i].min,
|
||||
toc->toc_entry[i].sec,
|
||||
toc->toc_entry[i].frame));
|
||||
dprintf(f, "Zero=%d\r\n", toc->toc_entry[i].zero);
|
||||
dprintf(f, "PMin=%d\r\n", toc->toc_entry[i].pmin);
|
||||
dprintf(f, "PSec=%d\r\n", toc->toc_entry[i].psec);
|
||||
dprintf(f, "PFrame=%d\r\n", toc->toc_entry[i].pframe);
|
||||
dprintf(f, "PLBA=%d\r\n",
|
||||
burn_msf_to_lba(toc->toc_entry[i].pmin,
|
||||
toc->toc_entry[i].psec,
|
||||
toc->toc_entry[i].pframe));
|
||||
dprintf(f, "\r\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void toc_find_modes(struct burn_drive *d)
|
||||
{
|
||||
struct burn_read_opts o;
|
||||
int lba;
|
||||
int i, j;
|
||||
struct buffer mem;
|
||||
struct burn_toc_entry *e;
|
||||
|
||||
assert(d->busy);
|
||||
|
||||
mem.bytes = 0;
|
||||
mem.sectors = 1;
|
||||
o.raw = 1;
|
||||
o.c2errors = 0;
|
||||
o.subcodes_audio = 1;
|
||||
o.subcodes_data = 1;
|
||||
o.hardware_error_recovery = 1;
|
||||
o.report_recovered_errors = 0;
|
||||
o.transfer_damaged_blocks = 1;
|
||||
o.hardware_error_retries = 1;
|
||||
|
||||
for (i = 0; i < d->disc->sessions; i++)
|
||||
for (j = 0; j < d->disc->session[i]->tracks; j++) {
|
||||
struct burn_track *t = d->disc->session[i]->track[j];
|
||||
|
||||
e = t->entry;
|
||||
if (!e)
|
||||
lba = 0;
|
||||
else
|
||||
lba = burn_msf_to_lba(e->pmin, e->psec,
|
||||
e->pframe);
|
||||
/* XXX | in the subcodes if appropriate! */
|
||||
if (e && !(e->control & 4)) {
|
||||
t->mode = BURN_AUDIO;
|
||||
} else {
|
||||
mem.sectors = 1;
|
||||
d->read_sectors(d, lba, mem.sectors, &o, &mem);
|
||||
t->mode = sector_identify(mem.data);
|
||||
}
|
||||
}
|
||||
}
|
48
libburn/toc.h
Normal file
48
libburn/toc.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef __TOC_H
|
||||
#define __TOC_H
|
||||
|
||||
struct command;
|
||||
|
||||
#include "libburn.h"
|
||||
#include "structure.h"
|
||||
|
||||
/* return if a given entry refers to a track position */
|
||||
#define TOC_ENTRY_IS_TRACK(drive, entrynum) \
|
||||
((drive)->toc_entry[entrynum].point < 100)
|
||||
|
||||
/* return if a given entry is in audio or data format */
|
||||
#define TOC_ENTRY_IS_AUDIO(drive, entrynum) \
|
||||
(~(drive)->toc_entry[entrynum].control & 4)
|
||||
|
||||
/* return the point value for a given entry number */
|
||||
#define TOC_POINT(drive, entrynum) ((drive)->toc_entry[entrynum].point)
|
||||
|
||||
/* return the track struct for a given entry number */
|
||||
#define TOC_TRACK(drive, entrynum) \
|
||||
((drive)->track[TOC_POINT(drive, entrynum) - 1])
|
||||
|
||||
/* return the lba of a toc entry */
|
||||
#define TOC_ENTRY_PLBA(drive, entrynum) \
|
||||
burn_msf_to_lba((drive)->toc_entry[(entrynum)].pmin, \
|
||||
(drive)->toc_entry[(entrynum)].psec, \
|
||||
(drive)->toc_entry[(entrynum)].pframe)
|
||||
|
||||
/* flags for the q subchannel control field */
|
||||
#define TOC_CONTROL_AUDIO (0)
|
||||
#define TOC_CONTROL_DATA (1 << 2)
|
||||
#define TOC_CONTROL_AUDIO_TWO_CHANNELS (0)
|
||||
#define TOC_CONTROL_AUDIO_FOUR_CHANNELS (1 << 3)
|
||||
#define TOC_CONTROL_AUDIO_PRE_EMPHASIS (1 << 0)
|
||||
#define TOC_CONTROL_DATA_RECORDED_UNINTERRUPTED (0)
|
||||
#define TOC_CONTROL_DATA_RECORDED_INCREMENT (1 << 0)
|
||||
#define TOC_CONTROL_COPY_PROHIBITED (0)
|
||||
#define TOC_CONTROL_COPY_PERMITTED (1 << 1)
|
||||
|
||||
/** read a sector from each track on disc to determine modes
|
||||
@param d The drive.
|
||||
*/
|
||||
void toc_find_modes(struct burn_drive *d);
|
||||
|
||||
#endif /*__TOC_H*/
|
162
libburn/transport.h
Normal file
162
libburn/transport.h
Normal file
@ -0,0 +1,162 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef __TRANSPORT
|
||||
#define __TRANSPORT
|
||||
|
||||
#include "libburn.h"
|
||||
|
||||
#include <pthread.h>
|
||||
/* sg data structures */
|
||||
#include <sys/types.h>
|
||||
#include <scsi/sg.h>
|
||||
#include <scsi/scsi.h>
|
||||
|
||||
/* kludge! glibc headers don't define all the SCSI shit that we use! */
|
||||
#ifndef SG_GET_ACCESS_COUNT
|
||||
# define SG_GET_ACCESS_COUNT 0x2289
|
||||
#endif
|
||||
|
||||
#define BUFFER_SIZE 65536
|
||||
|
||||
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
|
||||
{
|
||||
unsigned char data[BUFFER_SIZE];
|
||||
int sectors;
|
||||
int bytes;
|
||||
};
|
||||
|
||||
struct command
|
||||
{
|
||||
unsigned char opcode[16];
|
||||
int oplen;
|
||||
int dir;
|
||||
unsigned char sense[128];
|
||||
int error;
|
||||
int retry;
|
||||
struct buffer *page;
|
||||
};
|
||||
|
||||
struct scsi_inquiry_data
|
||||
{
|
||||
char vendor[9];
|
||||
char product[17];
|
||||
char revision[5];
|
||||
int valid;
|
||||
};
|
||||
|
||||
struct scsi_mode_data
|
||||
{
|
||||
int buffer_size;
|
||||
int dvdram_read;
|
||||
int dvdram_write;
|
||||
int dvdr_read;
|
||||
int dvdr_write;
|
||||
int dvdrom_read;
|
||||
int cdrw_read;
|
||||
int cdrw_write;
|
||||
int cdr_read;
|
||||
int cdr_write;
|
||||
int simulate;
|
||||
int max_read_speed;
|
||||
int max_write_speed;
|
||||
int cur_read_speed;
|
||||
int cur_write_speed;
|
||||
int retry_page_length;
|
||||
int retry_page_valid;
|
||||
int write_page_length;
|
||||
int write_page_valid;
|
||||
int c2_pointers;
|
||||
int valid;
|
||||
int underrun_proof;
|
||||
};
|
||||
|
||||
struct burn_drive
|
||||
{
|
||||
int host;
|
||||
int id;
|
||||
int channel;
|
||||
int lun;
|
||||
char *devname;
|
||||
int fd;
|
||||
|
||||
pthread_mutex_t access_lock;
|
||||
|
||||
enum burn_disc_status status;
|
||||
int erasable;
|
||||
volatile int released;
|
||||
int nwa; /* next writeable address */
|
||||
int alba; /* absolute lba */
|
||||
int rlba; /* relative lba in section */
|
||||
int start_lba;
|
||||
int end_lba;
|
||||
int toc_temp;
|
||||
struct burn_disc *disc; /* disc structure */
|
||||
int block_types[4];
|
||||
struct buffer *buffer;
|
||||
struct burn_progress progress;
|
||||
|
||||
volatile int cancel;
|
||||
volatile enum burn_drive_status busy;
|
||||
/* transport functions */
|
||||
int (*grab) (struct burn_drive *);
|
||||
int (*release) (struct burn_drive *);
|
||||
int (*issue_command) (struct burn_drive *, struct command *);
|
||||
|
||||
/* lower level functions */
|
||||
void (*erase) (struct burn_drive *, int);
|
||||
void (*getcaps) (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 *);
|
||||
void (*read_disc_info) (struct burn_drive *);
|
||||
void (*read_sectors) (struct burn_drive *,
|
||||
int start,
|
||||
int len,
|
||||
const struct burn_read_opts *, struct buffer *);
|
||||
void (*perform_opc) (struct burn_drive *);
|
||||
void (*set_speed) (struct burn_drive *, int, int);
|
||||
void (*send_parameters) (struct burn_drive *,
|
||||
const struct burn_read_opts *);
|
||||
void (*send_write_parameters) (struct burn_drive *,
|
||||
const struct burn_write_opts *);
|
||||
void (*send_cue_sheet) (struct burn_drive *, struct cue_sheet *);
|
||||
void (*sync_cache) (struct burn_drive *);
|
||||
int (*get_erase_progress) (struct burn_drive *);
|
||||
int (*get_nwa) (struct burn_drive *);
|
||||
void (*close_disc) (struct burn_drive * d, struct burn_write_opts * o);
|
||||
void (*close_session) (struct burn_drive * d,
|
||||
struct burn_write_opts * o);
|
||||
int (*test_unit_ready) (struct burn_drive * d);
|
||||
void (*probe_write_modes) (struct burn_drive * d);
|
||||
struct params params;
|
||||
struct scsi_inquiry_data *idata;
|
||||
struct scsi_mode_data *mdata;
|
||||
int toc_entries;
|
||||
struct burn_toc_entry *toc_entry;
|
||||
};
|
||||
|
||||
/* end of generic 'drive' data structures */
|
||||
|
||||
#endif /* __TRANSPORT */
|
44
libburn/util.c
Normal file
44
libburn/util.c
Normal file
@ -0,0 +1,44 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "../version.h"
|
||||
#include "util.h"
|
||||
#include "libburn.h"
|
||||
|
||||
char *burn_strdup(char *s)
|
||||
{
|
||||
char *ret;
|
||||
int l;
|
||||
|
||||
assert(s);
|
||||
|
||||
l = strlen(s) + 1;
|
||||
ret = malloc(l);
|
||||
memcpy(ret, s, l);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *burn_strndup(char *s, int n)
|
||||
{
|
||||
char *ret;
|
||||
int l;
|
||||
|
||||
assert(s);
|
||||
assert(n > 0);
|
||||
|
||||
l = strlen(s);
|
||||
ret = malloc(l < n ? l : n);
|
||||
|
||||
memcpy(ret, s, l < n - 1 ? l : n - 1);
|
||||
ret[n - 1] = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void burn_version(int *major, int *minor, int *micro)
|
||||
{
|
||||
*major = BURN_MAJOR_VERSION;
|
||||
*minor = BURN_MINOR_VERSION;
|
||||
*micro = BURN_MICRO_VERSION;
|
||||
}
|
8
libburn/util.h
Normal file
8
libburn/util.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef __UTIL
|
||||
#define __UTIL
|
||||
|
||||
char *burn_strdup(char *s);
|
||||
|
||||
char *burn_strndup(char *s, int n);
|
||||
|
||||
#endif
|
529
libburn/write.c
Normal file
529
libburn/write.c
Normal file
@ -0,0 +1,529 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include "error.h"
|
||||
#include "sector.h"
|
||||
#include "libburn.h"
|
||||
#include "drive.h"
|
||||
#include "transport.h"
|
||||
#include "message.h"
|
||||
#include "crc.h"
|
||||
#include "debug.h"
|
||||
#include "init.h"
|
||||
#include "lec.h"
|
||||
#include "toc.h"
|
||||
#include "util.h"
|
||||
#include "sg.h"
|
||||
#include "write.h"
|
||||
#include "options.h"
|
||||
|
||||
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
|
||||
assert(0);
|
||||
|
||||
if (mode & BURN_COPY)
|
||||
ctrl |= 2;
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
/* only the ctrl nibble is set here (not adr) */
|
||||
static void type_to_form(int mode, unsigned char *ctladr, int *form)
|
||||
{
|
||||
*ctladr = type_to_ctrl(mode) << 4;
|
||||
|
||||
if (mode & BURN_AUDIO)
|
||||
*form = 0;
|
||||
if (mode & BURN_MODE0)
|
||||
assert(0);
|
||||
if (mode & BURN_MODE1)
|
||||
*form = 0x10;
|
||||
if (mode & BURN_MODE2)
|
||||
assert(0); /* XXX someone's gonna want this sometime */
|
||||
if (mode & BURN_MODE_RAW)
|
||||
*form = 0;
|
||||
if (mode & BURN_SUBCODE_P16) /* must be expanded to R96 */
|
||||
*form |= 0x40;
|
||||
if (mode & BURN_SUBCODE_P96)
|
||||
*form |= 0xC0;
|
||||
if (mode & BURN_SUBCODE_R96)
|
||||
*form |= 0x40;
|
||||
}
|
||||
|
||||
int burn_write_flush(struct burn_write_opts *o)
|
||||
{
|
||||
struct burn_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;
|
||||
d->nwa += d->buffer->sectors;
|
||||
}
|
||||
d->sync_cache(d);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void print_cue(struct cue_sheet *sheet)
|
||||
{
|
||||
int i;
|
||||
unsigned char *unit;
|
||||
|
||||
printf("\n");
|
||||
printf("ctladr|trno|indx|form|scms| msf\n");
|
||||
printf("------+----+----+----+----+--------\n");
|
||||
for (i = 0; i < sheet->count; i++) {
|
||||
unit = sheet->data + 8 * i;
|
||||
printf(" %1X %1X | %02X | %02X | %02X | %02X |",
|
||||
(unit[0] & 0xf0) >> 4, unit[0] & 0xf, unit[1], unit[2],
|
||||
unit[3], unit[4]);
|
||||
printf("%02X:%02X:%02X\n", unit[5], unit[6], unit[7]);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_cue(struct cue_sheet *sheet, unsigned char ctladr,
|
||||
unsigned char tno, unsigned char indx,
|
||||
unsigned char form, unsigned char scms, int lba)
|
||||
{
|
||||
unsigned char *unit;
|
||||
unsigned char *ptr;
|
||||
int m, s, f;
|
||||
|
||||
burn_lba_to_msf(lba, &m, &s, &f);
|
||||
|
||||
sheet->count++;
|
||||
ptr = realloc(sheet->data, sheet->count * 8);
|
||||
assert(ptr);
|
||||
sheet->data = ptr;
|
||||
unit = sheet->data + (sheet->count - 1) * 8;
|
||||
unit[0] = ctladr;
|
||||
unit[1] = tno;
|
||||
unit[2] = indx;
|
||||
unit[3] = form;
|
||||
unit[4] = scms;
|
||||
unit[5] = m;
|
||||
unit[6] = s;
|
||||
unit[7] = f;
|
||||
}
|
||||
|
||||
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
||||
struct burn_session *session)
|
||||
{
|
||||
int i, m, s, f, form, pform, runtime = -150;
|
||||
unsigned char ctladr;
|
||||
struct burn_drive *d;
|
||||
struct burn_toc_entry *e;
|
||||
struct cue_sheet *sheet;
|
||||
struct burn_track **tar = session->track;
|
||||
int ntr = session->tracks;
|
||||
int rem = 0;
|
||||
|
||||
d = o->drive;
|
||||
|
||||
sheet = malloc(sizeof(struct cue_sheet));
|
||||
sheet->data = NULL;
|
||||
sheet->count = 0;
|
||||
|
||||
type_to_form(tar[0]->mode, &ctladr, &form);
|
||||
add_cue(sheet, ctladr | 1, 0, 0, 1, 0, runtime);
|
||||
add_cue(sheet, ctladr | 1, 1, 0, form, 0, runtime);
|
||||
runtime += 150;
|
||||
|
||||
burn_print(1, "toc for %d tracks:\n", ntr);
|
||||
d->toc_entries = ntr + 3;
|
||||
assert(d->toc_entry == NULL);
|
||||
d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry));
|
||||
e = d->toc_entry;
|
||||
memset((void *)e, 0, d->toc_entries * sizeof(struct burn_toc_entry));
|
||||
e[0].point = 0xA0;
|
||||
if (tar[0]->mode & BURN_AUDIO)
|
||||
e[0].control = TOC_CONTROL_AUDIO;
|
||||
else
|
||||
e[0].control = TOC_CONTROL_DATA;
|
||||
e[0].pmin = 1;
|
||||
e[0].psec = o->format;
|
||||
e[0].adr = 1;
|
||||
e[1].point = 0xA1;
|
||||
e[1].pmin = ntr;
|
||||
e[1].adr = 1;
|
||||
if (tar[ntr - 1]->mode & BURN_AUDIO)
|
||||
e[1].control = TOC_CONTROL_AUDIO;
|
||||
else
|
||||
e[1].control = TOC_CONTROL_DATA;
|
||||
e[2].point = 0xA2;
|
||||
e[2].control = e[1].control;
|
||||
e[2].adr = 1;
|
||||
|
||||
tar[0]->pregap2 = 1;
|
||||
pform = form;
|
||||
for (i = 0; i < ntr; i++) {
|
||||
type_to_form(tar[i]->mode, &ctladr, &form);
|
||||
if (pform != form) {
|
||||
add_cue(sheet, ctladr | 1, i + 1, 0, form, 0, runtime);
|
||||
runtime += 150;
|
||||
/* XXX fix pregap interval 1 for data tracks */
|
||||
// if (!(form & BURN_AUDIO))
|
||||
// tar[i]->pregap1 = 1;
|
||||
tar[i]->pregap2 = 1;
|
||||
}
|
||||
/* XXX HERE IS WHERE WE DO INDICES IN THE CUE SHEET */
|
||||
/* XXX and we should make sure the gaps conform to ecma-130... */
|
||||
tar[i]->entry = &e[3 + i];
|
||||
e[3 + i].point = i + 1;
|
||||
burn_lba_to_msf(runtime, &m, &s, &f);
|
||||
e[3 + i].pmin = m;
|
||||
e[3 + i].psec = s;
|
||||
e[3 + i].pframe = f;
|
||||
e[3 + i].adr = 1;
|
||||
e[3 + i].control = type_to_ctrl(tar[i]->mode);
|
||||
burn_print(1, "track %d control %d\n", tar[i]->mode,
|
||||
e[3 + i].control);
|
||||
add_cue(sheet, ctladr | 1, i + 1, 1, form, 0, runtime);
|
||||
runtime += burn_track_get_sectors(tar[i]);
|
||||
/* if we're padding, we'll clear any current shortage.
|
||||
if we're not, we'll slip toc entries by a sector every time our
|
||||
shortage is more than a sector
|
||||
XXX this is untested :)
|
||||
*/
|
||||
if (!tar[i]->pad) {
|
||||
rem += burn_track_get_shortage(tar[i]);
|
||||
if (i +1 != ntr)
|
||||
tar[i]->source->next = tar[i+1]->source;
|
||||
} else if (rem) {
|
||||
rem = 0;
|
||||
runtime++;
|
||||
}
|
||||
if (rem > burn_sector_length(tar[i]->mode)) {
|
||||
rem -= burn_sector_length(tar[i]->mode);
|
||||
runtime--;
|
||||
}
|
||||
pform = form;
|
||||
}
|
||||
burn_lba_to_msf(runtime, &m, &s, &f);
|
||||
e[2].pmin = m;
|
||||
e[2].psec = s;
|
||||
e[2].pframe = f;
|
||||
burn_print(1, "run time is %d (%d:%d:%d)\n", runtime, m, s, f);
|
||||
for (i = 0; i < d->toc_entries; i++)
|
||||
burn_print(1, "point %d (%02d:%02d:%02d)\n",
|
||||
d->toc_entry[i].point, d->toc_entry[i].pmin,
|
||||
d->toc_entry[i].psec, d->toc_entry[i].pframe);
|
||||
add_cue(sheet, ctladr | 1, 0xAA, 1, 1, 0, runtime);
|
||||
return sheet;
|
||||
}
|
||||
|
||||
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;
|
||||
assert(0);
|
||||
return 12345;
|
||||
}
|
||||
|
||||
int burn_subcode_length(int tracktype)
|
||||
{
|
||||
if (tracktype & BURN_SUBCODE_P16)
|
||||
return 16;
|
||||
if ((tracktype & BURN_SUBCODE_P96) || (tracktype & BURN_SUBCODE_R96))
|
||||
return 96;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int burn_write_leadin(struct burn_write_opts *o,
|
||||
struct burn_session *s, int first)
|
||||
{
|
||||
struct burn_drive *d = o->drive;
|
||||
int count;
|
||||
|
||||
d->busy = BURN_DRIVE_WRITING_LEADIN;
|
||||
|
||||
burn_print(5, first ? " first leadin\n" : " leadin\n");
|
||||
|
||||
if (first)
|
||||
count = 0 - d->alba - 150;
|
||||
else
|
||||
count = 4500;
|
||||
|
||||
d->progress.start_sector = d->alba;
|
||||
d->progress.sectors = count;
|
||||
d->progress.sector = 0;
|
||||
|
||||
while (count != 0) {
|
||||
if (!sector_toc(o, s->track[0]->mode))
|
||||
return 0;
|
||||
count--;
|
||||
d->progress.sector++;
|
||||
}
|
||||
d->busy = BURN_DRIVE_WRITING;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int burn_write_leadout(struct burn_write_opts *o,
|
||||
int first, unsigned char control, int mode)
|
||||
{
|
||||
struct burn_drive *d = o->drive;
|
||||
int count;
|
||||
|
||||
d->busy = BURN_DRIVE_WRITING_LEADOUT;
|
||||
|
||||
d->rlba = -150;
|
||||
burn_print(5, first ? " first leadout\n" : " leadout\n");
|
||||
if (first)
|
||||
count = 6750;
|
||||
else
|
||||
count = 2250;
|
||||
d->progress.start_sector = d->alba;
|
||||
d->progress.sectors = count;
|
||||
d->progress.sector = 0;
|
||||
|
||||
while (count != 0) {
|
||||
if (!sector_lout(o, control, mode))
|
||||
return 0;
|
||||
count--;
|
||||
d->progress.sector++;
|
||||
}
|
||||
d->busy = BURN_DRIVE_WRITING;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int burn_write_session(struct burn_write_opts *o, struct burn_session *s)
|
||||
{
|
||||
struct burn_drive *d = o->drive;
|
||||
struct burn_track *prev = NULL, *next = NULL;
|
||||
int i;
|
||||
|
||||
d->rlba = 0;
|
||||
burn_print(1, " writing a session\n");
|
||||
for (i = 0; i < s->tracks; i++) {
|
||||
if (i > 0)
|
||||
prev = s->track[i - 1];
|
||||
if (i + 1 < s->tracks)
|
||||
next = s->track[i + 1];
|
||||
else
|
||||
next = NULL;
|
||||
|
||||
if (!burn_write_track(o, s, i))
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
int sectors;
|
||||
|
||||
d->rlba = -150;
|
||||
|
||||
/* XXX for tao, we don't want the pregaps but still want post? */
|
||||
if (o->write_type != BURN_WRITE_TAO) {
|
||||
if (t->pregap1)
|
||||
d->rlba += 75;
|
||||
if (t->pregap2)
|
||||
d->rlba += 150;
|
||||
|
||||
if (t->pregap1) {
|
||||
struct burn_track *pt = s->track[tnum - 1];
|
||||
|
||||
if (tnum == 0) {
|
||||
printf("first track should not have a pregap1\n");
|
||||
pt = t;
|
||||
}
|
||||
for (i = 0; i < 75; i++)
|
||||
if (!sector_pregap(o, t->entry->point,
|
||||
pt->entry->control, pt->mode))
|
||||
return 0;
|
||||
}
|
||||
if (t->pregap2)
|
||||
for (i = 0; i < 150; i++)
|
||||
if (!sector_pregap(o, t->entry->point,
|
||||
t->entry->control, t->mode))
|
||||
return 0;
|
||||
} else {
|
||||
o->control = t->entry->control;
|
||||
d->send_write_parameters(d, o);
|
||||
}
|
||||
|
||||
/* user data */
|
||||
sectors = burn_track_get_sectors(t);
|
||||
|
||||
/* Update progress */
|
||||
d->progress.start_sector = d->nwa;
|
||||
d->progress.sectors = sectors;
|
||||
d->progress.sector = 0;
|
||||
|
||||
burn_print(12, "track is %d sectors long\n", sectors);
|
||||
|
||||
if (tnum == s->tracks)
|
||||
tmp = sectors > 150 ? 150 : sectors;
|
||||
|
||||
for (i = 0; i < sectors - tmp; i++) {
|
||||
if (!sector_data(o, t, 0))
|
||||
return 0;
|
||||
|
||||
/* update current progress */
|
||||
d->progress.sector++;
|
||||
}
|
||||
for (; i < sectors; i++) {
|
||||
burn_print(1, "last track, leadout prep\n");
|
||||
if (!sector_data(o, t, 1))
|
||||
return 0;
|
||||
|
||||
/* update progress */
|
||||
d->progress.sector++;
|
||||
}
|
||||
|
||||
if (t->postgap)
|
||||
for (i = 0; i < 150; i++)
|
||||
if (!sector_postgap(o, t->entry->point, t->entry->control,
|
||||
t->mode))
|
||||
return 0;
|
||||
i = t->offset;
|
||||
if (o->write_type == BURN_WRITE_SAO) {
|
||||
if (d->buffer->bytes) {
|
||||
int err;
|
||||
err = d->write(d, d->nwa, d->buffer);
|
||||
if (err == BE_CANCELLED)
|
||||
return 0;
|
||||
d->nwa += d->buffer->sectors;
|
||||
d->buffer->bytes = 0;
|
||||
d->buffer->sectors = 0;
|
||||
}
|
||||
}
|
||||
if (o->write_type == BURN_WRITE_TAO)
|
||||
if (!burn_write_flush(o))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc)
|
||||
{
|
||||
struct cue_sheet *sheet;
|
||||
struct burn_drive *d = o->drive;
|
||||
struct buffer buf;
|
||||
struct burn_track *lt;
|
||||
int first = 1, i;
|
||||
int res;
|
||||
|
||||
burn_message_clear_queue();
|
||||
|
||||
burn_print(1, "sync write of %d sessions\n", disc->sessions);
|
||||
d->buffer = &buf;
|
||||
memset(d->buffer, 0, sizeof(struct buffer));
|
||||
|
||||
d->rlba = -150;
|
||||
|
||||
d->toc_temp = 9;
|
||||
|
||||
/* Apparently some drives require this command to be sent, and a few drives
|
||||
return crap. so we send the command, then ignore the result.
|
||||
*/
|
||||
res = d->get_nwa(d);
|
||||
/* printf("ignored nwa: %d\n", res);*/
|
||||
|
||||
d->alba = d->start_lba;
|
||||
d->nwa = d->alba;
|
||||
|
||||
if (o->write_type != BURN_WRITE_TAO)
|
||||
d->send_write_parameters(d, o);
|
||||
|
||||
/* 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->busy = BURN_DRIVE_WRITING;
|
||||
|
||||
for (i = 0; i < disc->sessions; i++) {
|
||||
/* update progress */
|
||||
d->progress.session = i;
|
||||
d->progress.tracks = disc->session[i]->tracks;
|
||||
|
||||
sheet = burn_create_toc_entries(o, disc->session[i]);
|
||||
/* print_cue(sheet);*/
|
||||
if (o->write_type == BURN_WRITE_SAO)
|
||||
d->send_cue_sheet(d, sheet);
|
||||
free(sheet);
|
||||
|
||||
if (o->write_type == BURN_WRITE_RAW) {
|
||||
if (!burn_write_leadin(o, disc->session[i], first))
|
||||
goto fail;
|
||||
} else {
|
||||
if (first) {
|
||||
d->nwa = -150;
|
||||
d->alba = -150;
|
||||
} else {
|
||||
d->nwa += 4500;
|
||||
d->alba += 4500;
|
||||
}
|
||||
}
|
||||
if (!burn_write_session(o, disc->session[i]))
|
||||
goto fail;
|
||||
|
||||
lt = disc->session[i]->track[disc->session[i]->tracks - 1];
|
||||
if (o->write_type == BURN_WRITE_RAW) {
|
||||
if (!burn_write_leadout(o, first, lt->entry->control,
|
||||
lt->mode))
|
||||
goto fail;
|
||||
} else {
|
||||
if (!burn_write_flush(o))
|
||||
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;
|
||||
}
|
||||
if (o->write_type != BURN_WRITE_SAO)
|
||||
if (!burn_write_flush(o))
|
||||
goto fail;
|
||||
sleep(1);
|
||||
|
||||
burn_print(1, "done\n");
|
||||
d->busy = BURN_DRIVE_IDLE;
|
||||
|
||||
fail:
|
||||
d->sync_cache(d);
|
||||
burn_print(1, "done - failed\n");
|
||||
d->busy = BURN_DRIVE_IDLE;
|
||||
}
|
25
libburn/write.h
Normal file
25
libburn/write.h
Normal file
@ -0,0 +1,25 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef BURN__WRITE_H
|
||||
#define BURN__WRITE_H
|
||||
|
||||
struct cue_sheet;
|
||||
struct burn_session;
|
||||
struct burn_write_opts;
|
||||
struct burn_disc;
|
||||
|
||||
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
||||
struct burn_session *session);
|
||||
int burn_sector_length(int trackmode);
|
||||
int burn_subcode_length(int trackmode);
|
||||
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);
|
||||
|
||||
#endif /* BURN__WRITE_H */
|
Reference in New Issue
Block a user