2 * Copyright (c) 2000,2001,2002 Søren Schmidt <sos@freebsd.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification, immediately at the beginning of the file.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 * $FreeBSD: src/usr.sbin/burncd/burncd.c,v 1.10.2.6 2002/11/20 00:26:18 njl Exp $
38 #include <sys/errno.h>
39 #include <sys/ioctl.h>
42 #include <sys/cdrio.h>
43 #include <sys/param.h>
56 static struct track_info tracks
[100];
57 static int fd
, quiet
, verbose
, saved_block_size
, notracks
;
59 static void add_track(char *, int, int, int);
60 static void do_DAO(int, int);
61 static void do_TAO(int, int);
62 static int write_file(struct track_info
*);
63 static int roundup_blocks(struct track_info
*);
64 static void cue_ent(struct cdr_cue_entry
*, int, int, int, int, int, int, int);
65 static void cleanup(int);
66 static void usage(void);
69 main(int argc
, char **argv
)
72 int dao
= 0, eject
= 0, fixate
= 0, list
= 0, multi
= 0, preemp
= 0;
73 int nogap
= 0, speed
= 4 * 177, test_write
= 0;
74 int block_size
= 0, block_type
= 0, cdopen
= 0;
75 const char *dev
= "/dev/acd0c";
77 while ((ch
= getopt(argc
, argv
, "def:lmnpqs:tv")) != -1) {
112 if (strcasecmp("max", optarg
) == 0)
113 speed
= CDR_MAX_SPEED
;
115 speed
= atoi(optarg
) * 177;
117 errx(EX_USAGE
, "Invalid speed: %s", optarg
);
138 if ((fd
= open(dev
, O_RDWR
, 0)) < 0)
139 err(EX_NOINPUT
, "open(%s)", dev
);
141 if (ioctl(fd
, CDRIOCGETBLOCKSIZE
, &saved_block_size
) < 0)
142 err(EX_IOERR
, "ioctl(CDRIOCGETBLOCKSIZE)");
144 if (ioctl(fd
, CDRIOCWRITESPEED
, &speed
) < 0)
145 err(EX_IOERR
, "ioctl(CDRIOCWRITESPEED)");
147 err_set_exit(cleanup
);
149 for (arg
= 0; arg
< argc
; arg
++) {
150 if (!strcasecmp(argv
[arg
], "fixate")) {
154 if (!strcasecmp(argv
[arg
], "msinfo")) {
155 struct ioc_read_toc_single_entry entry
;
156 struct ioc_toc_header header
;
158 if (ioctl(fd
, CDIOREADTOCHEADER
, &header
) < 0)
159 err(EX_IOERR
, "ioctl(CDIOREADTOCHEADER)");
160 bzero(&entry
, sizeof(struct ioc_read_toc_single_entry
));
161 entry
.address_format
= CD_LBA_FORMAT
;
162 entry
.track
= header
.ending_track
;
163 if (ioctl(fd
, CDIOREADTOCENTRY
, &entry
) < 0)
164 err(EX_IOERR
, "ioctl(CDIOREADTOCENTRY)");
165 if (ioctl(fd
, CDRIOCNEXTWRITEABLEADDR
, &addr
) < 0)
166 err(EX_IOERR
, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
167 fprintf(stdout
, "%d,%d\n",
168 ntohl(entry
.entry
.addr
.lba
), addr
);
172 if (!strcasecmp(argv
[arg
], "erase") || !strcasecmp(argv
[arg
], "blank")){
173 int error
, blank
, percent
;
175 if (!strcasecmp(argv
[arg
], "erase"))
180 fprintf(stderr
, "%sing CD, please wait..\r",
181 blank
== CDR_B_ALL
? "eras" : "blank");
183 if (ioctl(fd
, CDRIOCBLANK
, &blank
) < 0)
184 err(EX_IOERR
, "ioctl(CDRIOCBLANK)");
187 error
= ioctl(fd
, CDRIOCGETPROGRESS
, &percent
);
188 if (percent
> 0 && !quiet
)
190 "%sing CD - %d %% done \r",
192 "eras" : "blank", percent
);
193 if (error
|| percent
== 100)
200 if (!strcasecmp(argv
[arg
], "audio") || !strcasecmp(argv
[arg
], "raw")) {
201 block_type
= CDR_DB_RAW
;
205 if (!strcasecmp(argv
[arg
], "data") || !strcasecmp(argv
[arg
], "mode1")) {
206 block_type
= CDR_DB_ROM_MODE1
;
210 if (!strcasecmp(argv
[arg
], "mode2")) {
211 block_type
= CDR_DB_ROM_MODE2
;
215 if (!strcasecmp(argv
[arg
], "xamode1")) {
216 block_type
= CDR_DB_XA_MODE1
;
220 if (!strcasecmp(argv
[arg
], "xamode2")) {
221 block_type
= CDR_DB_XA_MODE2_F2
;
225 if (!strcasecmp(argv
[arg
], "vcd")) {
226 block_type
= CDR_DB_XA_MODE2_F2
;
233 errx(EX_NOINPUT
, "no data format selected");
235 char file_buf
[MAXPATHLEN
+ 1], *eol
;
238 if ((fp
= fopen(argv
[arg
], "r")) == NULL
)
239 err(EX_NOINPUT
, "fopen(%s)", argv
[arg
]);
241 while (fgets(file_buf
, sizeof(file_buf
), fp
) != NULL
) {
242 if (*file_buf
== '#' || *file_buf
== '\n')
244 if ((eol
= strchr(file_buf
, '\n')))
246 add_track(file_buf
, block_size
, block_type
, nogap
);
251 err(EX_IOERR
, "fgets(%s)", file_buf
);
254 add_track(argv
[arg
], block_size
, block_type
, nogap
);
257 if (ioctl(fd
, CDIOCSTART
, 0) < 0)
258 err(EX_IOERR
, "ioctl(CDIOCSTART)");
260 if (ioctl(fd
, CDRIOCINITWRITER
, &test_write
) < 0)
261 err(EX_IOERR
, "ioctl(CDRIOCINITWRITER)");
265 do_DAO(test_write
, multi
);
267 do_TAO(test_write
, preemp
);
269 if (fixate
&& !dao
) {
271 fprintf(stderr
, "fixating CD, please wait..\n");
272 if (ioctl(fd
, CDRIOCFIXATE
, &multi
) < 0)
273 err(EX_IOERR
, "ioctl(CDRIOCFIXATE)");
276 if (ioctl(fd
, CDRIOCSETBLOCKSIZE
, &saved_block_size
) < 0) {
278 err(EX_IOERR
, "ioctl(CDRIOCSETBLOCKSIZE)");
282 if (ioctl(fd
, CDIOCEJECT
) < 0)
283 err(EX_IOERR
, "ioctl(CDIOCEJECT)");
289 add_track(char *name
, int block_size
, int block_type
, int nogap
)
293 static int done_stdin
= 0;
295 if (!strcmp(name
, "-")) {
297 warn("skipping multiple usages of stdin");
303 else if ((file
= open(name
, O_RDONLY
, 0)) < 0)
304 err(EX_NOINPUT
, "open(%s)", name
);
305 if (fstat(file
, &sb
) < 0)
306 err(EX_IOERR
, "fstat(%s)", name
);
307 tracks
[notracks
].file
= file
;
308 tracks
[notracks
].file_name
= name
;
309 if (file
== STDIN_FILENO
)
310 tracks
[notracks
].file_size
= -1;
312 tracks
[notracks
].file_size
= sb
.st_size
;
313 tracks
[notracks
].block_size
= block_size
;
314 tracks
[notracks
].block_type
= block_type
;
316 if (nogap
&& notracks
)
317 tracks
[notracks
].pregap
= 0;
319 if (tracks
[notracks
- (notracks
> 0)].block_type
== block_type
)
320 tracks
[notracks
].pregap
= 150;
322 tracks
[notracks
].pregap
= 255;
328 if (tracks
[notracks
].file_size
/ tracks
[notracks
].block_size
!=
329 roundup_blocks(&tracks
[notracks
]))
332 "adding type 0x%02x file %s size %d KB %d blocks %s\n",
333 tracks
[notracks
].block_type
, name
, (int)sb
.st_size
/1024,
334 roundup_blocks(&tracks
[notracks
]),
335 pad
? "(0 padded)" : "");
341 do_DAO(int test_write
, int multi
)
343 struct cdr_cuesheet sheet
;
344 struct cdr_cue_entry cue
[100];
345 int format
= CDR_SESS_CDROM
;
348 int bt2ctl
[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
349 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, -1, -1 };
351 int bt2df
[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
352 0x10, 0x30, 0x20, -1, 0x21, -1, -1, -1 };
354 if (ioctl(fd
, CDRIOCNEXTWRITEABLEADDR
, &addr
) < 0)
355 err(EX_IOERR
, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
357 fprintf(stderr
, "next writeable LBA %d\n", addr
);
359 cue_ent(&cue
[j
++], bt2ctl
[tracks
[0].block_type
], 0x01, 0x00, 0x0,
360 (bt2df
[tracks
[0].block_type
] & 0xf0) |
361 (tracks
[0].block_type
< 8 ? 0x01 : 0x04), 0x00, addr
);
363 for (i
= 0; i
< notracks
; i
++) {
364 if (bt2ctl
[tracks
[i
].block_type
] < 0 ||
365 bt2df
[tracks
[i
].block_type
] < 0)
366 err(EX_IOERR
, "track type not supported in DAO mode");
368 if (tracks
[i
].block_type
>= CDR_DB_XA_MODE1
)
369 format
= CDR_SESS_CDROM_XA
;
372 addr
+= tracks
[i
].pregap
;
373 tracks
[i
].addr
= addr
;
375 cue_ent(&cue
[j
++], bt2ctl
[tracks
[i
].block_type
],
376 0x01, i
+1, 0x1, bt2df
[tracks
[i
].block_type
],
381 if (tracks
[i
].pregap
) {
382 if (tracks
[i
].block_type
> 0x7) {
383 cue_ent(&cue
[j
++],bt2ctl
[tracks
[i
].block_type
],
385 (bt2df
[tracks
[i
].block_type
] & 0xf0) |
386 (tracks
[i
].block_type
< 8 ? 0x01 :0x04),
390 cue_ent(&cue
[j
++],bt2ctl
[tracks
[i
].block_type
],
392 bt2df
[tracks
[i
].block_type
],
395 tracks
[i
].addr
= tracks
[i
- 1].addr
+
396 roundup_blocks(&tracks
[i
- 1]);
398 cue_ent(&cue
[j
++], bt2ctl
[tracks
[i
].block_type
],
399 0x01, i
+1, 0x1, bt2df
[tracks
[i
].block_type
],
400 0x00, addr
+ tracks
[i
].pregap
);
402 if (tracks
[i
].block_type
> 0x7)
403 addr
+= tracks
[i
].pregap
;
405 addr
+= roundup_blocks(&tracks
[i
]);
408 cue_ent(&cue
[j
++], bt2ctl
[tracks
[i
- 1].block_type
], 0x01, 0xaa, 0x01,
409 (bt2df
[tracks
[i
- 1].block_type
] & 0xf0) |
410 (tracks
[i
- 1].block_type
< 8 ? 0x01 : 0x04), 0x00, addr
);
414 sheet
.test_write
= test_write
;
415 sheet
.session_type
= multi
? CDR_SESS_MULTI
: CDR_SESS_NONE
;
416 sheet
.session_format
= format
;
418 u_int8_t
*ptr
= (u_int8_t
*)sheet
.entries
;
420 fprintf(stderr
,"CUE sheet:");
421 for (i
= 0; i
< sheet
.len
; i
++)
423 fprintf(stderr
," %02x", ptr
[i
]);
425 fprintf(stderr
,"\n%02x", ptr
[i
]);
426 fprintf(stderr
,"\n");
429 if (ioctl(fd
, CDRIOCSENDCUE
, &sheet
) < 0)
430 err(EX_IOERR
, "ioctl(CDRIOCSENDCUE)");
432 for (i
= 0; i
< notracks
; i
++) {
433 if (write_file(&tracks
[i
]))
434 err(EX_IOERR
, "write_file");
437 ioctl(fd
, CDRIOCFLUSH
);
441 do_TAO(int test_write
, int preemp
)
443 struct cdr_track track
;
446 for (i
= 0; i
< notracks
; i
++) {
447 track
.test_write
= test_write
;
448 track
.datablock_type
= tracks
[i
].block_type
;
449 track
.preemp
= preemp
;
450 if (ioctl(fd
, CDRIOCINITTRACK
, &track
) < 0)
451 err(EX_IOERR
, "ioctl(CDRIOCINITTRACK)");
453 if (ioctl(fd
, CDRIOCNEXTWRITEABLEADDR
, &tracks
[i
].addr
) < 0)
454 err(EX_IOERR
, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
456 fprintf(stderr
, "next writeable LBA %d\n",
458 if (write_file(&tracks
[i
]))
459 err(EX_IOERR
, "write_file");
460 if (ioctl(fd
, CDRIOCFLUSH
) < 0)
461 err(EX_IOERR
, "ioctl(CDRIOCFLUSH)");
466 write_file(struct track_info
*track_info
)
468 int size
, count
, filesize
;
469 char buf
[2352*BLOCKS
];
470 static int tot_size
= 0;
472 filesize
= track_info
->file_size
/ 1024;
474 if (ioctl(fd
, CDRIOCSETBLOCKSIZE
, &track_info
->block_size
) < 0)
475 err(EX_IOERR
, "ioctl(CDRIOCSETBLOCKSIZE)");
477 if (track_info
->addr
>= 0)
478 lseek(fd
, track_info
->addr
* track_info
->block_size
, SEEK_SET
);
481 fprintf(stderr
, "addr = %d size = %d blocks = %d\n",
482 track_info
->addr
, track_info
->file_size
,
483 roundup_blocks(track_info
));
486 if (track_info
->file
== STDIN_FILENO
)
487 fprintf(stderr
, "writing from stdin\n");
490 "writing from file %s size %d KB\n",
491 track_info
->file_name
, filesize
);
495 while ((count
= read(track_info
->file
, buf
,
496 MIN((track_info
->file_size
- size
),
497 track_info
->block_size
* BLOCKS
))) > 0) {
500 if (count
% track_info
->block_size
) {
501 /* pad file to % block_size */
503 (track_info
->block_size
* BLOCKS
) - count
);
504 count
= ((count
/ track_info
->block_size
) + 1) *
505 track_info
->block_size
;
507 if ((res
= write(fd
, buf
, count
)) != count
) {
508 fprintf(stderr
, "\nonly wrote %d of %d bytes err=%d\n",
517 fprintf(stderr
, "written this track %d KB", size
/1024);
518 if (track_info
->file
!= STDIN_FILENO
&& filesize
) {
519 pct
= (size
/ 1024) * 100 / filesize
;
520 fprintf(stderr
, " (%d%%)", pct
);
522 fprintf(stderr
, " total %d KB\r", tot_size
/1024);
524 if (size
>= track_info
->file_size
)
529 fprintf(stderr
, "\n");
530 close(track_info
->file
);
535 roundup_blocks(struct track_info
*track
)
537 return ((track
->file_size
+ track
->block_size
- 1) / track
->block_size
);
541 cue_ent(struct cdr_cue_entry
*cue
, int ctl
, int adr
, int track
, int idx
,
542 int dataform
, int scms
, int lba
)
548 cue
->dataform
= dataform
;
551 cue
->min
= lba
/ (60*75);
552 cue
->sec
= (lba
% (60*75)) / 75;
553 cue
->frame
= (lba
% (60*75)) % 75;
557 cleanup(int dummy __unused
)
559 if (ioctl(fd
, CDRIOCSETBLOCKSIZE
, &saved_block_size
) < 0)
560 err(EX_IOERR
, "ioctl(CDRIOCSETBLOCKSIZE)");
567 "Usage: %s [-delmnpqtv] [-f device] [-s speed] [command]"
568 " [command file ...]\n", getprogname());