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.
39 #include <sys/errno.h>
40 #include <sys/ioctl.h>
43 #include <sys/cdrio.h>
44 #include <sys/dvdio.h>
45 #include <sys/param.h>
46 #include <arpa/inet.h>
52 char file_name
[MAXPATHLEN
+ 1];
59 static struct track_info tracks
[100];
60 static int global_fd_for_cleanup
, quiet
, verbose
, saved_block_size
, notracks
;
62 void add_track(char *, int, int, int);
63 void do_DAO(int fd
, int, int);
64 void do_TAO(int fd
, int, int, int);
65 void do_format(int, int, char *);
66 int write_file(int fd
, struct track_info
*);
67 int roundup_blocks(struct track_info
*);
68 void cue_ent(struct cdr_cue_entry
*, int, int, int, int, int, int, int);
73 main(int argc
, char **argv
)
75 int arg
, addr
, ch
, fd
;
76 int dao
= 0, eject
= 0, fixate
= 0, list
= 0, multi
= 0, preemp
= 0;
77 int nogap
= 0, speed
= 4 * 177, test_write
= 0, force
= 0;
78 int block_size
= 0, block_type
= 0, cdopen
= 0, dvdrw
= 0;
81 if ((dev
= getenv("CDROM")) == NULL
)
84 while ((ch
= getopt(argc
, argv
, "def:Flmnpqs:tv")) != -1) {
123 if (strcasecmp("max", optarg
) == 0)
124 speed
= CDR_MAX_SPEED
;
126 speed
= atoi(optarg
) * 177;
128 errx(EX_USAGE
, "Invalid speed: %s", optarg
);
149 if ((fd
= open(dev
, O_RDWR
, 0)) < 0)
150 err(EX_NOINPUT
, "open(%s)", dev
);
152 if (ioctl(fd
, CDRIOCGETBLOCKSIZE
, &saved_block_size
) < 0)
153 err(EX_IOERR
, "ioctl(CDRIOCGETBLOCKSIZE)");
155 if (ioctl(fd
, CDRIOCWRITESPEED
, &speed
) < 0)
156 err(EX_IOERR
, "ioctl(CDRIOCWRITESPEED)");
158 global_fd_for_cleanup
= fd
;
159 err_set_exit(cleanup
);
161 for (arg
= 0; arg
< argc
; arg
++) {
162 if (!strcasecmp(argv
[arg
], "fixate")) {
166 if (!strcasecmp(argv
[arg
], "eject")) {
170 if (!strcasecmp(argv
[arg
], "msinfo")) {
171 struct ioc_read_toc_single_entry entry
;
172 struct ioc_toc_header header
;
174 if (ioctl(fd
, CDIOREADTOCHEADER
, &header
) < 0)
175 err(EX_IOERR
, "ioctl(CDIOREADTOCHEADER)");
176 bzero(&entry
, sizeof(struct ioc_read_toc_single_entry
));
177 entry
.address_format
= CD_LBA_FORMAT
;
178 entry
.track
= header
.ending_track
;
179 if (ioctl(fd
, CDIOREADTOCENTRY
, &entry
) < 0)
180 err(EX_IOERR
, "ioctl(CDIOREADTOCENTRY)");
181 if (ioctl(fd
, CDRIOCNEXTWRITEABLEADDR
, &addr
) < 0)
182 err(EX_IOERR
, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
183 fprintf(stdout
, "%d,%d\n",
184 ntohl(entry
.entry
.addr
.lba
), addr
);
188 if ((!strcasecmp(argv
[arg
], "erase") ||
189 !strcasecmp(argv
[arg
], "blank")) && !test_write
) {
190 int blank
, pct
, last
= 0;
192 if (!strcasecmp(argv
[arg
], "erase"))
197 fprintf(stderr
, "%sing CD, please wait..\r",
198 blank
== CDR_B_ALL
? "eras" : "blank");
200 if (ioctl(fd
, CDRIOCBLANK
, &blank
) < 0)
201 err(EX_IOERR
, "ioctl(CDRIOCBLANK)");
204 if (ioctl(fd
, CDRIOCGETPROGRESS
, &pct
) == -1)
205 err(EX_IOERR
,"ioctl(CDRIOGETPROGRESS)");
206 if (pct
> 0 && !quiet
)
208 "%sing CD - %d %% done \r",
210 "eras" : "blank", pct
);
211 if (pct
== 100 || (pct
== 0 && last
> 90))
219 if (!strcasecmp(argv
[arg
], "format") && !test_write
) {
220 if (arg
+ 1 < argc
&&
221 (!strcasecmp(argv
[arg
+ 1], "dvd+rw") ||
222 !strcasecmp(argv
[arg
+ 1], "dvd-rw")))
223 do_format(fd
, force
, argv
[arg
+ 1]);
225 errx(EX_NOINPUT
, "format media type invalid");
229 if (!strcasecmp(argv
[arg
], "audio") || !strcasecmp(argv
[arg
], "raw")) {
230 block_type
= CDR_DB_RAW
;
234 if (!strcasecmp(argv
[arg
], "data") || !strcasecmp(argv
[arg
], "mode1")) {
235 block_type
= CDR_DB_ROM_MODE1
;
239 if (!strcasecmp(argv
[arg
], "mode2")) {
240 block_type
= CDR_DB_ROM_MODE2
;
244 if (!strcasecmp(argv
[arg
], "xamode1")) {
245 block_type
= CDR_DB_XA_MODE1
;
249 if (!strcasecmp(argv
[arg
], "xamode2")) {
250 block_type
= CDR_DB_XA_MODE2_F2
;
254 if (!strcasecmp(argv
[arg
], "vcd")) {
255 block_type
= CDR_DB_XA_MODE2_F2
;
261 if (!strcasecmp(argv
[arg
], "dvdrw")) {
262 block_type
= CDR_DB_ROM_MODE1
;
269 errx(EX_NOINPUT
, "no data format selected");
271 char file_buf
[MAXPATHLEN
+ 1], *eol
;
274 if ((fp
= fopen(argv
[arg
], "r")) == NULL
)
275 err(EX_NOINPUT
, "fopen(%s)", argv
[arg
]);
277 while (fgets(file_buf
, sizeof(file_buf
), fp
) != NULL
) {
278 if (*file_buf
== '#' || *file_buf
== '\n')
280 if ((eol
= strchr(file_buf
, '\n')))
282 add_track(file_buf
, block_size
, block_type
, nogap
);
287 err(EX_IOERR
, "fgets(%s)", file_buf
);
290 add_track(argv
[arg
], block_size
, block_type
, nogap
);
293 if (dvdrw
&& notracks
> 1)
294 errx(EX_USAGE
, "DVD's only have 1 track");
295 if (ioctl(fd
, CDIOCSTART
, 0) < 0)
296 err(EX_IOERR
, "ioctl(CDIOCSTART)");
298 if (ioctl(fd
, CDRIOCINITWRITER
, &test_write
) < 0)
299 err(EX_IOERR
, "ioctl(CDRIOCINITWRITER)");
303 do_DAO(fd
, test_write
, multi
);
305 do_TAO(fd
, test_write
, preemp
, dvdrw
);
307 if (!test_write
&& fixate
&& !dao
&& !dvdrw
) {
309 fprintf(stderr
, "fixating CD, please wait..\n");
310 if (ioctl(fd
, CDRIOCFIXATE
, &multi
) < 0)
311 err(EX_IOERR
, "ioctl(CDRIOCFIXATE)");
314 if (ioctl(fd
, CDRIOCSETBLOCKSIZE
, &saved_block_size
) < 0) {
316 err(EX_IOERR
, "ioctl(CDRIOCSETBLOCKSIZE)");
320 if (ioctl(fd
, CDIOCEJECT
) < 0)
321 err(EX_IOERR
, "ioctl(CDIOCEJECT)");
327 add_track(char *name
, int block_size
, int block_type
, int nogap
)
331 static int done_stdin
= 0;
333 if (!strcmp(name
, "-")) {
335 warn("skipping multiple usages of stdin");
341 else if ((file
= open(name
, O_RDONLY
, 0)) < 0)
342 err(EX_NOINPUT
, "open(%s)", name
);
343 if (fstat(file
, &sb
) < 0)
344 err(EX_IOERR
, "fstat(%s)", name
);
345 tracks
[notracks
].file
= file
;
346 strncpy(tracks
[notracks
].file_name
, name
, MAXPATHLEN
);
347 if (file
== STDIN_FILENO
)
348 tracks
[notracks
].file_size
= -1;
350 tracks
[notracks
].file_size
= sb
.st_size
;
351 tracks
[notracks
].block_size
= block_size
;
352 tracks
[notracks
].block_type
= block_type
;
354 if (nogap
&& notracks
)
355 tracks
[notracks
].pregap
= 0;
357 if (tracks
[notracks
- (notracks
> 0)].block_type
== block_type
)
358 tracks
[notracks
].pregap
= 150;
360 tracks
[notracks
].pregap
= 255;
366 if (tracks
[notracks
].file_size
/ tracks
[notracks
].block_size
!=
367 roundup_blocks(&tracks
[notracks
]))
370 "adding type 0x%02x file %s size %jd KB %d blocks %s\n",
371 tracks
[notracks
].block_type
, name
,
372 (intmax_t)sb
.st_size
/1024,
373 roundup_blocks(&tracks
[notracks
]),
374 pad
? "(0 padded)" : "");
380 do_DAO(int fd
, int test_write
, int multi
)
382 struct cdr_cuesheet sheet
;
383 struct cdr_cue_entry cue
[100];
384 int format
= CDR_SESS_CDROM
;
387 int bt2ctl
[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
388 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, -1, -1 };
390 int bt2df
[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
391 0x10, 0x30, 0x20, -1, 0x21, -1, -1, -1 };
393 if (ioctl(fd
, CDRIOCNEXTWRITEABLEADDR
, &addr
) < 0)
394 err(EX_IOERR
, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
396 fprintf(stderr
, "next writeable LBA %d\n", addr
);
398 cue_ent(&cue
[j
++], bt2ctl
[tracks
[0].block_type
], 0x01, 0x00, 0x0,
399 (bt2df
[tracks
[0].block_type
] & 0xf0) |
400 (tracks
[0].block_type
< 8 ? 0x01 : 0x04), 0x00, addr
);
402 for (i
= 0; i
< notracks
; i
++) {
403 if (bt2ctl
[tracks
[i
].block_type
] < 0 ||
404 bt2df
[tracks
[i
].block_type
] < 0)
405 errx(EX_IOERR
, "track type not supported in DAO mode");
407 if (tracks
[i
].block_type
>= CDR_DB_XA_MODE1
)
408 format
= CDR_SESS_CDROM_XA
;
411 addr
+= tracks
[i
].pregap
;
412 tracks
[i
].addr
= addr
;
414 cue_ent(&cue
[j
++], bt2ctl
[tracks
[i
].block_type
],
415 0x01, i
+1, 0x1, bt2df
[tracks
[i
].block_type
],
420 if (tracks
[i
].pregap
) {
421 if (tracks
[i
].block_type
> 0x7) {
422 cue_ent(&cue
[j
++],bt2ctl
[tracks
[i
].block_type
],
424 (bt2df
[tracks
[i
].block_type
] & 0xf0) |
425 (tracks
[i
].block_type
< 8 ? 0x01 :0x04),
429 cue_ent(&cue
[j
++],bt2ctl
[tracks
[i
].block_type
],
431 bt2df
[tracks
[i
].block_type
],
434 tracks
[i
].addr
= tracks
[i
- 1].addr
+
435 roundup_blocks(&tracks
[i
- 1]);
437 cue_ent(&cue
[j
++], bt2ctl
[tracks
[i
].block_type
],
438 0x01, i
+1, 0x1, bt2df
[tracks
[i
].block_type
],
439 0x00, addr
+ tracks
[i
].pregap
);
441 if (tracks
[i
].block_type
> 0x7)
442 addr
+= tracks
[i
].pregap
;
444 addr
+= roundup_blocks(&tracks
[i
]);
447 cue_ent(&cue
[j
++], bt2ctl
[tracks
[i
- 1].block_type
], 0x01, 0xaa, 0x01,
448 (bt2df
[tracks
[i
- 1].block_type
] & 0xf0) |
449 (tracks
[i
- 1].block_type
< 8 ? 0x01 : 0x04), 0x00, addr
);
453 sheet
.test_write
= test_write
;
454 sheet
.session_type
= multi
? CDR_SESS_MULTI
: CDR_SESS_NONE
;
455 sheet
.session_format
= format
;
457 u_int8_t
*ptr
= (u_int8_t
*)sheet
.entries
;
459 fprintf(stderr
,"CUE sheet:");
460 for (i
= 0; i
< sheet
.len
; i
++)
462 fprintf(stderr
," %02x", ptr
[i
]);
464 fprintf(stderr
,"\n%02x", ptr
[i
]);
465 fprintf(stderr
,"\n");
468 if (ioctl(fd
, CDRIOCSENDCUE
, &sheet
) < 0)
469 err(EX_IOERR
, "ioctl(CDRIOCSENDCUE)");
471 for (i
= 0; i
< notracks
; i
++) {
472 if (write_file(fd
, &tracks
[i
]))
473 err(EX_IOERR
, "write_file");
476 ioctl(fd
, CDRIOCFLUSH
);
480 do_TAO(int fd
, int test_write
, int preemp
, int dvdrw
)
482 struct cdr_track track
;
485 for (i
= 0; i
< notracks
; i
++) {
486 track
.test_write
= test_write
;
487 track
.datablock_type
= tracks
[i
].block_type
;
488 track
.preemp
= preemp
;
489 if (ioctl(fd
, CDRIOCINITTRACK
, &track
) < 0)
490 err(EX_IOERR
, "ioctl(CDRIOCINITTRACK)");
495 if (ioctl(fd
, CDRIOCNEXTWRITEABLEADDR
,
496 &tracks
[i
].addr
) < 0)
497 err(EX_IOERR
, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
500 fprintf(stderr
, "next writeable LBA %d\n",
502 if (write_file(fd
, &tracks
[i
]))
503 err(EX_IOERR
, "write_file");
504 if (ioctl(fd
, CDRIOCFLUSH
) < 0)
505 err(EX_IOERR
, "ioctl(CDRIOCFLUSH)");
509 #define NTOH3B(x) ((x&0x0000ff)<<16) | (x&0x00ff00) | ((x&0xff0000)>>16)
512 do_format(int the_fd
, int force
, char *type
)
514 struct cdr_format_capacities capacities
;
515 struct cdr_format_params format_params
;
516 int count
, i
, pct
, last
= 0;
518 if (ioctl(the_fd
, CDRIOCREADFORMATCAPS
, &capacities
) == -1)
519 err(EX_IOERR
, "ioctl(CDRIOCREADFORMATCAPS)");
522 fprintf(stderr
, "format list entries=%zd\n",
523 capacities
.length
/ sizeof(struct cdr_format_capacity
));
524 fprintf(stderr
, "current format: blocks=%u type=0x%x block_size=%u\n",
525 ntohl(capacities
.blocks
), capacities
.type
,
526 NTOH3B(capacities
.block_size
));
529 count
= capacities
.length
/ sizeof(struct cdr_format_capacity
);
531 for (i
= 0; i
< count
; ++i
)
533 "format %d: blocks=%u type=0x%x param=%u\n",
534 i
, ntohl(capacities
.format
[i
].blocks
),
535 capacities
.format
[i
].type
,
536 NTOH3B(capacities
.format
[i
].param
));
539 for (i
= 0; i
< count
; ++i
) {
540 if (!strcasecmp(type
, "dvd+rw")) {
541 if (capacities
.format
[i
].type
== 0x26) {
545 if (!strcasecmp(type
, "dvd-rw")) {
546 if (capacities
.format
[i
].type
== 0x0) {
552 errx(EX_IOERR
, "could not find a valid format capacity");
555 fprintf(stderr
,"formatting with blocks=%u type=0x%x param=%u\n",
556 ntohl(capacities
.format
[i
].blocks
),
557 capacities
.format
[i
].type
,
558 NTOH3B(capacities
.format
[i
].param
));
560 if (!force
&& capacities
.type
== 2)
561 errx(EX_IOERR
, "media already formatted (use -F to override)");
563 memset(&format_params
, 0, sizeof(struct cdr_format_params
));
564 format_params
.fov
= 1;
565 format_params
.immed
= 1;
566 format_params
.length
= ntohs(sizeof(struct cdr_format_capacity
));
567 memcpy(&format_params
.format
, &capacities
.format
[i
],
568 sizeof(struct cdr_format_capacity
));
570 if(ioctl(the_fd
, CDRIOCFORMAT
, &format_params
) == -1)
571 err(EX_IOERR
, "ioctl(CDRIOCFORMAT)");
575 if (ioctl(the_fd
, CDRIOCGETPROGRESS
, &pct
) == -1)
576 err(EX_IOERR
, "ioctl(CDRIOGETPROGRESS)");
577 if (pct
> 0 && !quiet
)
578 fprintf(stderr
, "formatting DVD - %d %% done \r",
580 if (pct
== 100 || (pct
== 0 && last
> 90))
585 fprintf(stderr
, "\n");
589 write_file(int fd
, struct track_info
*track_info
)
591 off_t size
, count
, filesize
;
592 char buf
[2352*BLOCKS
];
593 static off_t tot_size
= 0;
595 filesize
= track_info
->file_size
/ 1024;
597 if (ioctl(fd
, CDRIOCSETBLOCKSIZE
, &track_info
->block_size
) < 0)
598 err(EX_IOERR
, "ioctl(CDRIOCSETBLOCKSIZE)");
600 if (track_info
->addr
>= 0)
601 lseek(fd
, track_info
->addr
* track_info
->block_size
, SEEK_SET
);
604 fprintf(stderr
, "addr = %d size = %jd blocks = %d\n",
605 track_info
->addr
, (intmax_t)track_info
->file_size
,
606 roundup_blocks(track_info
));
609 if (track_info
->file
== STDIN_FILENO
)
610 fprintf(stderr
, "writing from stdin\n");
613 "writing from file %s size %jd KB\n",
614 track_info
->file_name
, (intmax_t)filesize
);
618 while ((count
= read(track_info
->file
, buf
,
619 track_info
->file_size
== -1
620 ? track_info
->block_size
* BLOCKS
621 : MIN((track_info
->file_size
- size
),
622 track_info
->block_size
* BLOCKS
))) > 0) {
625 if (count
% track_info
->block_size
) {
626 /* pad file to % block_size */
628 (track_info
->block_size
* BLOCKS
) - count
);
629 count
= ((count
/ track_info
->block_size
) + 1) *
630 track_info
->block_size
;
632 if ((res
= write(fd
, buf
, count
)) != count
) {
634 fprintf(stderr
, "\n%s\n", strerror(errno
));
636 fprintf(stderr
, "\nonly wrote %d of %jd"
637 " bytes\n", res
, (intmax_t)count
);
645 fprintf(stderr
, "written this track %jd KB",
646 (intmax_t)size
/1024);
647 if (track_info
->file
!= STDIN_FILENO
&& filesize
) {
648 pct
= (size
/ 1024) * 100 / filesize
;
649 fprintf(stderr
, " (%d%%)", pct
);
651 fprintf(stderr
, " total %jd KB\r",
652 (intmax_t)tot_size
/ 1024);
654 if (track_info
->file_size
!= -1
655 && size
>= track_info
->file_size
)
660 fprintf(stderr
, "\n");
661 close(track_info
->file
);
666 roundup_blocks(struct track_info
*track
)
668 return ((track
->file_size
+ track
->block_size
- 1) / track
->block_size
);
672 cue_ent(struct cdr_cue_entry
*cue
, int ctl
, int adr
, int track
, int idx
,
673 int dataform
, int scms
, int lba
)
679 cue
->dataform
= dataform
;
682 cue
->min
= lba
/ (60*75);
683 cue
->sec
= (lba
% (60*75)) / 75;
684 cue
->frame
= (lba
% (60*75)) % 75;
688 cleanup(int dummy __unused
)
690 if (ioctl(global_fd_for_cleanup
, CDRIOCSETBLOCKSIZE
,
691 &saved_block_size
) < 0)
692 err(EX_IOERR
, "ioctl(CDRIOCSETBLOCKSIZE)");
699 "usage: %s [-deFlmnpqtv] [-f device] [-s speed] [command]"
700 " [command file ...]\n", getprogname());