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.
40 #include <sys/errno.h>
41 #include <sys/ioctl.h>
44 #include <sys/cdrio.h>
45 #include <sys/dvdio.h>
46 #include <sys/param.h>
47 #include <arpa/inet.h>
53 char file_name[MAXPATHLEN + 1];
60 static struct track_info tracks[100];
61 static int quiet, verbose, saved_block_size, notracks;
62 static volatile sig_atomic_t global_fd_for_cleanup;
64 void add_track(char *, int, int, int);
65 void do_DAO(int fd, int, int);
66 void do_TAO(int fd, int, int, int);
67 void do_format(int, int, char *);
68 int write_file(int fd, struct track_info *);
69 int roundup_blocks(struct track_info *);
70 void cue_ent(struct cdr_cue_entry *, int, int, int, int, int, int, int);
72 void cleanup_flush(void);
73 void cleanup_signal(int);
77 main(int argc, char **argv)
79 int arg, addr, ch, fd;
80 int dao = 0, eject = 0, fixate = 0, list = 0, multi = 0, preemp = 0;
81 int nogap = 0, speed = 4 * 177, test_write = 0, force = 0;
82 int block_size = 0, block_type = 0, cdopen = 0, dvdrw = 0;
83 const char *dev, *env_speed;
85 if (feature_present("ata_cam")) {
86 errx(1, "\nATA_CAM option is enabled in kernel.\n"
87 "Install the sysutils/cdrtools port and use cdrecord instead.\n\n"
89 "http://www.freebsd.org/doc/handbook/creating-cds.html#CDRECORD");
92 if ((dev = getenv("CDROM")) == NULL)
95 env_speed = getenv("BURNCD_SPEED");
97 while ((ch = getopt(argc, argv, "def:Flmnpqs:tv")) != -1) {
154 if (env_speed == NULL)
156 else if (strcasecmp("max", env_speed) == 0)
157 speed = CDR_MAX_SPEED;
159 speed = atoi(env_speed) * 177;
161 errx(EX_USAGE, "Invalid speed: %s", env_speed);
166 if ((fd = open(dev, O_RDWR, 0)) < 0)
167 err(EX_NOINPUT, "open(%s)", dev);
169 if (ioctl(fd, CDRIOCGETBLOCKSIZE, &saved_block_size) < 0)
170 err(EX_IOERR, "ioctl(CDRIOCGETBLOCKSIZE)");
172 if (ioctl(fd, CDRIOCWRITESPEED, &speed) < 0)
173 err(EX_IOERR, "ioctl(CDRIOCWRITESPEED)");
175 global_fd_for_cleanup = fd;
176 err_set_exit(cleanup);
177 signal(SIGHUP, cleanup_signal);
178 signal(SIGINT, cleanup_signal);
179 signal(SIGTERM, cleanup_signal);
181 for (arg = 0; arg < argc; arg++) {
182 if (!strcasecmp(argv[arg], "fixate")) {
186 if (!strcasecmp(argv[arg], "eject")) {
190 if (!strcasecmp(argv[arg], "msinfo")) {
191 struct ioc_read_toc_single_entry entry;
192 struct ioc_toc_header header;
194 if (ioctl(fd, CDIOREADTOCHEADER, &header) < 0)
195 err(EX_IOERR, "ioctl(CDIOREADTOCHEADER)");
196 bzero(&entry, sizeof(struct ioc_read_toc_single_entry));
197 entry.address_format = CD_LBA_FORMAT;
198 entry.track = header.ending_track;
199 if (ioctl(fd, CDIOREADTOCENTRY, &entry) < 0)
200 err(EX_IOERR, "ioctl(CDIOREADTOCENTRY)");
201 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
202 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
203 fprintf(stdout, "%d,%d\n",
204 ntohl(entry.entry.addr.lba), addr);
208 if ((!strcasecmp(argv[arg], "erase") ||
209 !strcasecmp(argv[arg], "blank")) && !test_write) {
210 int blank, pct, last = 0;
212 if (!strcasecmp(argv[arg], "erase"))
217 fprintf(stderr, "%sing CD, please wait..\r",
218 blank == CDR_B_ALL ? "eras" : "blank");
220 if (ioctl(fd, CDRIOCBLANK, &blank) < 0)
221 err(EX_IOERR, "ioctl(CDRIOCBLANK)");
224 if (ioctl(fd, CDRIOCGETPROGRESS, &pct) == -1)
225 err(EX_IOERR,"ioctl(CDRIOGETPROGRESS)");
226 if (pct > 0 && !quiet)
228 "%sing CD - %d %% done \r",
230 "eras" : "blank", pct);
231 if (pct == 100 || (pct == 0 && last > 90))
239 if (!strcasecmp(argv[arg], "format") && !test_write) {
240 if (arg + 1 < argc &&
241 (!strcasecmp(argv[arg + 1], "dvd+rw") ||
242 !strcasecmp(argv[arg + 1], "dvd-rw")))
243 do_format(fd, force, argv[arg + 1]);
245 errx(EX_NOINPUT, "format media type invalid");
249 if (!strcasecmp(argv[arg], "audio") || !strcasecmp(argv[arg], "raw")) {
250 block_type = CDR_DB_RAW;
254 if (!strcasecmp(argv[arg], "data") || !strcasecmp(argv[arg], "mode1")) {
255 block_type = CDR_DB_ROM_MODE1;
259 if (!strcasecmp(argv[arg], "mode2")) {
260 block_type = CDR_DB_ROM_MODE2;
264 if (!strcasecmp(argv[arg], "xamode1")) {
265 block_type = CDR_DB_XA_MODE1;
269 if (!strcasecmp(argv[arg], "xamode2")) {
270 block_type = CDR_DB_XA_MODE2_F2;
274 if (!strcasecmp(argv[arg], "vcd")) {
275 block_type = CDR_DB_XA_MODE2_F2;
281 if (!strcasecmp(argv[arg], "dvdrw")) {
282 block_type = CDR_DB_ROM_MODE1;
289 errx(EX_NOINPUT, "no data format selected");
291 char file_buf[MAXPATHLEN + 1], *eol;
294 if ((fp = fopen(argv[arg], "r")) == NULL)
295 err(EX_NOINPUT, "fopen(%s)", argv[arg]);
297 while (fgets(file_buf, sizeof(file_buf), fp) != NULL) {
298 if (*file_buf == '#' || *file_buf == '\n')
300 if ((eol = strchr(file_buf, '\n')))
302 add_track(file_buf, block_size, block_type, nogap);
307 err(EX_IOERR, "fgets(%s)", file_buf);
310 add_track(argv[arg], block_size, block_type, nogap);
313 if (dvdrw && notracks > 1)
314 errx(EX_USAGE, "DVD's only have 1 track");
315 if (ioctl(fd, CDIOCSTART, 0) < 0)
316 err(EX_IOERR, "ioctl(CDIOCSTART)");
318 if (ioctl(fd, CDRIOCINITWRITER, &test_write) < 0)
319 err(EX_IOERR, "ioctl(CDRIOCINITWRITER)");
323 do_DAO(fd, test_write, multi);
325 do_TAO(fd, test_write, preemp, dvdrw);
327 if (!test_write && fixate && !dao && !dvdrw) {
329 fprintf(stderr, "fixating CD, please wait..\n");
330 if (ioctl(fd, CDRIOCFIXATE, &multi) < 0)
331 err(EX_IOERR, "ioctl(CDRIOCFIXATE)");
334 if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) {
336 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
340 if (ioctl(fd, CDIOCEJECT) < 0)
341 err(EX_IOERR, "ioctl(CDIOCEJECT)");
343 signal(SIGHUP, SIG_DFL);
344 signal(SIGINT, SIG_DFL);
345 signal(SIGTERM, SIG_DFL);
351 add_track(char *name, int block_size, int block_type, int nogap)
355 static int done_stdin = 0;
357 if (!strcmp(name, "-")) {
359 warn("skipping multiple usages of stdin");
365 else if ((file = open(name, O_RDONLY, 0)) < 0)
366 err(EX_NOINPUT, "open(%s)", name);
367 if (fstat(file, &sb) < 0)
368 err(EX_IOERR, "fstat(%s)", name);
369 tracks[notracks].file = file;
370 strncpy(tracks[notracks].file_name, name, MAXPATHLEN);
371 if (file == STDIN_FILENO)
372 tracks[notracks].file_size = -1;
374 tracks[notracks].file_size = sb.st_size;
375 tracks[notracks].block_size = block_size;
376 tracks[notracks].block_type = block_type;
378 if (nogap && notracks)
379 tracks[notracks].pregap = 0;
381 if (tracks[notracks - (notracks > 0)].block_type == block_type)
382 tracks[notracks].pregap = 150;
384 tracks[notracks].pregap = 255;
390 if (tracks[notracks].file_size / tracks[notracks].block_size !=
391 roundup_blocks(&tracks[notracks]))
394 "adding type 0x%02x file %s size %jd KB %d blocks %s\n",
395 tracks[notracks].block_type, name,
396 (intmax_t)sb.st_size/1024,
397 roundup_blocks(&tracks[notracks]),
398 pad ? "(0 padded)" : "");
404 do_DAO(int fd, int test_write, int multi)
406 struct cdr_cuesheet sheet;
407 struct cdr_cue_entry cue[100];
408 int format = CDR_SESS_CDROM;
411 int bt2ctl[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
412 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, -1, -1 };
414 int bt2df[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
415 0x10, 0x30, 0x20, -1, 0x21, -1, -1, -1 };
417 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
418 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
420 fprintf(stderr, "next writeable LBA %d\n", addr);
422 cue_ent(&cue[j++], bt2ctl[tracks[0].block_type], 0x01, 0x00, 0x0,
423 (bt2df[tracks[0].block_type] & 0xf0) |
424 (tracks[0].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
426 for (i = 0; i < notracks; i++) {
427 if (bt2ctl[tracks[i].block_type] < 0 ||
428 bt2df[tracks[i].block_type] < 0)
429 errx(EX_IOERR, "track type not supported in DAO mode");
431 if (tracks[i].block_type >= CDR_DB_XA_MODE1)
432 format = CDR_SESS_CDROM_XA;
435 addr += tracks[i].pregap;
436 tracks[i].addr = addr;
438 cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
439 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
444 if (tracks[i].pregap) {
445 if (tracks[i].block_type > 0x7) {
446 cue_ent(&cue[j++],bt2ctl[tracks[i].block_type],
448 (bt2df[tracks[i].block_type] & 0xf0) |
449 (tracks[i].block_type < 8 ? 0x01 :0x04),
453 cue_ent(&cue[j++],bt2ctl[tracks[i].block_type],
455 bt2df[tracks[i].block_type],
458 tracks[i].addr = tracks[i - 1].addr +
459 roundup_blocks(&tracks[i - 1]);
461 cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
462 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
463 0x00, addr + tracks[i].pregap);
465 if (tracks[i].block_type > 0x7)
466 addr += tracks[i].pregap;
468 addr += roundup_blocks(&tracks[i]);
471 cue_ent(&cue[j++], bt2ctl[tracks[i - 1].block_type], 0x01, 0xaa, 0x01,
472 (bt2df[tracks[i - 1].block_type] & 0xf0) |
473 (tracks[i - 1].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
477 sheet.test_write = test_write;
478 sheet.session_type = multi ? CDR_SESS_MULTI : CDR_SESS_NONE;
479 sheet.session_format = format;
481 u_int8_t *ptr = (u_int8_t *)sheet.entries;
483 fprintf(stderr,"CUE sheet:");
484 for (i = 0; i < sheet.len; i++)
486 fprintf(stderr," %02x", ptr[i]);
488 fprintf(stderr,"\n%02x", ptr[i]);
489 fprintf(stderr,"\n");
492 if (ioctl(fd, CDRIOCSENDCUE, &sheet) < 0)
493 err(EX_IOERR, "ioctl(CDRIOCSENDCUE)");
495 for (i = 0; i < notracks; i++) {
496 if (write_file(fd, &tracks[i])) {
498 err(EX_IOERR, "write_file");
502 ioctl(fd, CDRIOCFLUSH);
506 do_TAO(int fd, int test_write, int preemp, int dvdrw)
508 struct cdr_track track;
511 for (i = 0; i < notracks; i++) {
512 track.test_write = test_write;
513 track.datablock_type = tracks[i].block_type;
514 track.preemp = preemp;
515 if (ioctl(fd, CDRIOCINITTRACK, &track) < 0)
516 err(EX_IOERR, "ioctl(CDRIOCINITTRACK)");
521 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR,
522 &tracks[i].addr) < 0)
523 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
526 fprintf(stderr, "next writeable LBA %d\n",
528 if (write_file(fd, &tracks[i])) {
530 err(EX_IOERR, "write_file");
532 if (ioctl(fd, CDRIOCFLUSH) < 0)
533 err(EX_IOERR, "ioctl(CDRIOCFLUSH)");
537 #define NTOH3B(x) ((x&0x0000ff)<<16) | (x&0x00ff00) | ((x&0xff0000)>>16)
540 do_format(int the_fd, int force, char *type)
542 struct cdr_format_capacities capacities;
543 struct cdr_format_params format_params;
544 int count, i, pct, last = 0;
546 if (ioctl(the_fd, CDRIOCREADFORMATCAPS, &capacities) == -1)
547 err(EX_IOERR, "ioctl(CDRIOCREADFORMATCAPS)");
550 fprintf(stderr, "format list entries=%zd\n",
551 capacities.length / sizeof(struct cdr_format_capacity));
552 fprintf(stderr, "current format: blocks=%u type=0x%x block_size=%u\n",
553 ntohl(capacities.blocks), capacities.type,
554 NTOH3B(capacities.block_size));
557 count = capacities.length / sizeof(struct cdr_format_capacity);
559 for (i = 0; i < count; ++i)
561 "format %d: blocks=%u type=0x%x param=%u\n",
562 i, ntohl(capacities.format[i].blocks),
563 capacities.format[i].type,
564 NTOH3B(capacities.format[i].param));
567 for (i = 0; i < count; ++i) {
568 if (!strcasecmp(type, "dvd+rw")) {
569 if (capacities.format[i].type == 0x26) {
573 if (!strcasecmp(type, "dvd-rw")) {
574 if (capacities.format[i].type == 0x0) {
580 errx(EX_IOERR, "could not find a valid format capacity");
583 fprintf(stderr,"formatting with blocks=%u type=0x%x param=%u\n",
584 ntohl(capacities.format[i].blocks),
585 capacities.format[i].type,
586 NTOH3B(capacities.format[i].param));
588 if (!force && capacities.type == 2)
589 errx(EX_IOERR, "media already formatted (use -F to override)");
591 memset(&format_params, 0, sizeof(struct cdr_format_params));
592 format_params.fov = 1;
593 format_params.immed = 1;
594 format_params.length = ntohs(sizeof(struct cdr_format_capacity));
595 memcpy(&format_params.format, &capacities.format[i],
596 sizeof(struct cdr_format_capacity));
598 if(ioctl(the_fd, CDRIOCFORMAT, &format_params) == -1)
599 err(EX_IOERR, "ioctl(CDRIOCFORMAT)");
603 if (ioctl(the_fd, CDRIOCGETPROGRESS, &pct) == -1)
604 err(EX_IOERR, "ioctl(CDRIOGETPROGRESS)");
605 if (pct > 0 && !quiet)
606 fprintf(stderr, "formatting DVD - %d %% done \r",
608 if (pct == 100 || (pct == 0 && last > 90))
613 fprintf(stderr, "\n");
617 write_file(int fd, struct track_info *track_info)
619 off_t size, count, filesize;
620 char buf[2352*BLOCKS];
621 static off_t tot_size = 0;
623 filesize = track_info->file_size / 1024;
625 if (ioctl(fd, CDRIOCSETBLOCKSIZE, &track_info->block_size) < 0)
626 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
628 if (track_info->addr >= 0)
629 lseek(fd, track_info->addr * track_info->block_size, SEEK_SET);
632 fprintf(stderr, "addr = %d size = %jd blocks = %d\n",
633 track_info->addr, (intmax_t)track_info->file_size,
634 roundup_blocks(track_info));
637 if (track_info->file == STDIN_FILENO)
638 fprintf(stderr, "writing from stdin\n");
641 "writing from file %s size %jd KB\n",
642 track_info->file_name, (intmax_t)filesize);
646 while ((count = read(track_info->file, buf,
647 track_info->file_size == -1
648 ? track_info->block_size * BLOCKS
649 : MIN((track_info->file_size - size),
650 track_info->block_size * BLOCKS))) > 0) {
653 if (count % track_info->block_size) {
654 /* pad file to % block_size */
656 (track_info->block_size * BLOCKS) - count);
657 count = ((count / track_info->block_size) + 1) *
658 track_info->block_size;
660 if ((res = write(fd, buf, count)) != count) {
662 fprintf(stderr, "\n");
663 close(track_info->file);
666 fprintf(stderr, "\nonly wrote %d of %jd"
667 " bytes\n", res, (intmax_t)count);
675 fprintf(stderr, "written this track %jd KB",
676 (intmax_t)size/1024);
677 if (track_info->file != STDIN_FILENO && filesize) {
678 pct = (size / 1024) * 100 / filesize;
679 fprintf(stderr, " (%d%%)", pct);
681 fprintf(stderr, " total %jd KB\r",
682 (intmax_t)tot_size / 1024);
684 if (track_info->file_size != -1
685 && size >= track_info->file_size)
690 fprintf(stderr, "\n");
691 close(track_info->file);
696 roundup_blocks(struct track_info *track)
698 return ((track->file_size + track->block_size - 1) / track->block_size);
702 cue_ent(struct cdr_cue_entry *cue, int ctl, int adr, int track, int idx,
703 int dataform, int scms, int lba)
709 cue->dataform = dataform;
712 cue->min = lba / (60*75);
713 cue->sec = (lba % (60*75)) / 75;
714 cue->frame = (lba % (60*75)) % 75;
718 cleanup(int dummy __unused)
720 if (ioctl(global_fd_for_cleanup, CDRIOCSETBLOCKSIZE,
721 &saved_block_size) < 0)
722 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
728 if (ioctl(global_fd_for_cleanup, CDRIOCFLUSH) < 0)
729 err(EX_IOERR, "ioctl(CDRIOCFLUSH)");
733 cleanup_signal(int sig)
735 signal(sig, SIG_IGN);
736 ioctl(global_fd_for_cleanup, CDRIOCFLUSH);
737 write(STDERR_FILENO, "\nAborted\n", 10);
745 "usage: %s [-deFlmnpqtv] [-f device] [-s speed] [command]"
746 " [command file ...]\n", getprogname());