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.
38 #include <sys/errno.h>
39 #include <sys/ioctl.h>
42 #include <sys/cdrio.h>
43 #include <sys/param.h>
44 #include <arpa/inet.h>
57 static struct track_info tracks[100];
58 static int fd, quiet, verbose, saved_block_size, notracks;
60 void add_track(char *, int, int, int);
61 void do_DAO(int, int);
62 void do_TAO(int, int);
63 int write_file(struct track_info *);
64 int roundup_blocks(struct track_info *);
65 void cue_ent(struct cdr_cue_entry *, int, int, int, int, int, int, int);
70 main(int argc, char **argv)
73 int dao = 0, eject = 0, fixate = 0, list = 0, multi = 0, preemp = 0;
74 int nogap = 0, speed = 4, test_write = 0;
75 int block_size = 0, block_type = 0, cdopen = 0;
76 const char *dev = "/dev/acd0c";
78 while ((ch = getopt(argc, argv, "def:lmnpqs:tv")) != -1) {
113 speed = atoi(optarg);
115 errx(EX_USAGE, "Invalid speed: %s", optarg);
136 if ((fd = open(dev, O_RDWR, 0)) < 0)
137 err(EX_NOINPUT, "open(%s)", dev);
139 if (ioctl(fd, CDRIOCGETBLOCKSIZE, &saved_block_size) < 0)
140 err(EX_IOERR, "ioctl(CDRIOCGETBLOCKSIZE)");
142 if (ioctl(fd, CDRIOCWRITESPEED, &speed) < 0)
143 err(EX_IOERR, "ioctl(CDRIOCWRITESPEED)");
145 err_set_exit(cleanup);
147 for (arg = 0; arg < argc; arg++) {
148 if (!strcasecmp(argv[arg], "fixate")) {
152 if (!strcasecmp(argv[arg], "msinfo")) {
153 struct ioc_read_toc_single_entry entry;
154 struct ioc_toc_header header;
156 if (ioctl(fd, CDIOREADTOCHEADER, &header) < 0)
157 err(EX_IOERR, "ioctl(CDIOREADTOCHEADER)");
158 bzero(&entry, sizeof(struct ioc_read_toc_single_entry));
159 entry.address_format = CD_LBA_FORMAT;
160 entry.track = header.ending_track;
161 if (ioctl(fd, CDIOREADTOCENTRY, &entry) < 0)
162 err(EX_IOERR, "ioctl(CDIOREADTOCENTRY)");
163 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
164 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
165 fprintf(stdout, "%d,%d\n",
166 ntohl(entry.entry.addr.lba), addr);
170 if (!strcasecmp(argv[arg], "erase") || !strcasecmp(argv[arg], "blank")){
171 int error, blank, percent;
173 if (!strcasecmp(argv[arg], "erase"))
178 fprintf(stderr, "%sing CD, please wait..\r",
179 blank == CDR_B_ALL ? "eras" : "blank");
181 if (ioctl(fd, CDRIOCBLANK, &blank) < 0)
182 err(EX_IOERR, "ioctl(CDRIOCBLANK)");
185 error = ioctl(fd, CDRIOCGETPROGRESS, &percent);
186 if (percent > 0 && !quiet)
188 "%sing CD - %d %% done \r",
190 "eras" : "blank", percent);
191 if (error || percent == 100)
198 if (!strcasecmp(argv[arg], "audio") || !strcasecmp(argv[arg], "raw")) {
199 block_type = CDR_DB_RAW;
203 if (!strcasecmp(argv[arg], "data") || !strcasecmp(argv[arg], "mode1")) {
204 block_type = CDR_DB_ROM_MODE1;
208 if (!strcasecmp(argv[arg], "mode2")) {
209 block_type = CDR_DB_ROM_MODE2;
213 if (!strcasecmp(argv[arg], "xamode1")) {
214 block_type = CDR_DB_XA_MODE1;
218 if (!strcasecmp(argv[arg], "xamode2")) {
219 block_type = CDR_DB_XA_MODE2_F2;
223 if (!strcasecmp(argv[arg], "vcd")) {
224 block_type = CDR_DB_XA_MODE2_F2;
231 err(EX_NOINPUT, "no data format selected");
233 char file_buf[MAXPATHLEN + 1], *eol;
236 if ((fp = fopen(argv[arg], "r")) == NULL)
237 err(EX_NOINPUT, "fopen(%s)", argv[arg]);
239 while (fgets(file_buf, sizeof(file_buf), fp) != NULL) {
240 if (*file_buf == '#' || *file_buf == '\n')
242 if ((eol = strchr(file_buf, '\n')))
244 add_track(file_buf, block_size, block_type, nogap);
249 err(EX_IOERR, "fgets(%s)", file_buf);
252 add_track(argv[arg], block_size, block_type, nogap);
255 if (ioctl(fd, CDIOCSTART, 0) < 0)
256 err(EX_IOERR, "ioctl(CDIOCSTART)");
258 if (ioctl(fd, CDRIOCINITWRITER, &test_write) < 0)
259 err(EX_IOERR, "ioctl(CDRIOCINITWRITER)");
263 do_DAO(test_write, multi);
265 do_TAO(test_write, preemp);
267 if (fixate && !dao) {
269 fprintf(stderr, "fixating CD, please wait..\n");
270 if (ioctl(fd, CDRIOCFIXATE, &multi) < 0)
271 err(EX_IOERR, "ioctl(CDRIOCFIXATE)");
274 if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) {
276 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
280 if (ioctl(fd, CDIOCEJECT) < 0)
281 err(EX_IOERR, "ioctl(CDIOCEJECT)");
287 add_track(char *name, int block_size, int block_type, int nogap)
291 static int done_stdin = 0;
293 if (!strcmp(name, "-")) {
295 warn("skipping multiple usages of stdin");
301 else if ((file = open(name, O_RDONLY, 0)) < 0)
302 err(EX_NOINPUT, "open(%s)", name);
303 if (fstat(file, &sb) < 0)
304 err(EX_IOERR, "fstat(%s)", name);
305 tracks[notracks].file = file;
306 tracks[notracks].file_name = name;
307 tracks[notracks].file_size = sb.st_size;
308 tracks[notracks].block_size = block_size;
309 tracks[notracks].block_type = block_type;
311 if (nogap && notracks)
312 tracks[notracks].pregap = 0;
314 if (tracks[notracks - (notracks > 0)].block_type == block_type)
315 tracks[notracks].pregap = 150;
317 tracks[notracks].pregap = 255;
323 if (tracks[notracks].file_size / tracks[notracks].block_size !=
324 roundup_blocks(&tracks[notracks]))
327 "adding type 0x%02x file %s size %d KB %d blocks %s\n",
328 tracks[notracks].block_type, name, (int)sb.st_size/1024,
329 roundup_blocks(&tracks[notracks]),
330 pad ? "(0 padded)" : "");
336 do_DAO(int test_write, int multi)
338 struct cdr_cuesheet sheet;
339 struct cdr_cue_entry cue[100];
340 int format = CDR_SESS_CDROM;
343 int bt2ctl[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
344 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, -1, -1 };
346 int bt2df[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
347 0x10, 0x30, 0x20, -1, 0x21, -1, -1, -1 };
349 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
350 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
352 fprintf(stderr, "next writeable LBA %d\n", addr);
354 cue_ent(&cue[j++], bt2ctl[tracks[0].block_type], 0x01, 0x00, 0x0,
355 (bt2df[tracks[0].block_type] & 0xf0) |
356 (tracks[0].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
358 for (i = 0; i < notracks; i++) {
359 if (bt2ctl[tracks[i].block_type] < 0 ||
360 bt2df[tracks[i].block_type] < 0)
361 err(EX_IOERR, "track type not supported in DAO mode");
363 if (tracks[i].block_type >= CDR_DB_XA_MODE1)
364 format = CDR_SESS_CDROM_XA;
367 addr += tracks[i].pregap;
368 tracks[i].addr = addr;
370 cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
371 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
376 if (tracks[i].pregap) {
377 if (tracks[i].block_type > 0x7) {
378 cue_ent(&cue[j++],bt2ctl[tracks[i].block_type],
380 (bt2df[tracks[i].block_type] & 0xf0) |
381 (tracks[i].block_type < 8 ? 0x01 :0x04),
385 cue_ent(&cue[j++],bt2ctl[tracks[i].block_type],
387 bt2df[tracks[i].block_type],
390 tracks[i].addr = tracks[i - 1].addr +
391 roundup_blocks(&tracks[i - 1]);
393 cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
394 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
395 0x00, addr + tracks[i].pregap);
397 if (tracks[i].block_type > 0x7)
398 addr += tracks[i].pregap;
400 addr += roundup_blocks(&tracks[i]);
403 cue_ent(&cue[j++], bt2ctl[tracks[i - 1].block_type], 0x01, 0xaa, 0x01,
404 (bt2df[tracks[i - 1].block_type] & 0xf0) |
405 (tracks[i - 1].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
409 sheet.test_write = test_write;
410 sheet.session_type = multi ? CDR_SESS_MULTI : CDR_SESS_NONE;
411 sheet.session_format = format;
413 u_int8_t *ptr = (u_int8_t *)sheet.entries;
415 fprintf(stderr,"CUE sheet:");
416 for (i = 0; i < sheet.len; i++)
418 fprintf(stderr," %02x", ptr[i]);
420 fprintf(stderr,"\n%02x", ptr[i]);
421 fprintf(stderr,"\n");
424 if (ioctl(fd, CDRIOCSENDCUE, &sheet) < 0)
425 err(EX_IOERR, "ioctl(CDRIOCSENDCUE)");
427 for (i = 0; i < notracks; i++) {
428 if (write_file(&tracks[i]))
429 err(EX_IOERR, "write_file");
432 ioctl(fd, CDRIOCFLUSH);
436 do_TAO(int test_write, int preemp)
438 struct cdr_track track;
441 for (i = 0; i < notracks; i++) {
442 track.test_write = test_write;
443 track.datablock_type = tracks[i].block_type;
444 track.preemp = preemp;
445 if (ioctl(fd, CDRIOCINITTRACK, &track) < 0)
446 err(EX_IOERR, "ioctl(CDRIOCINITTRACK)");
448 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &tracks[i].addr) < 0)
449 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
451 fprintf(stderr, "next writeable LBA %d\n",
453 if (write_file(&tracks[i]))
454 err(EX_IOERR, "write_file");
455 if (ioctl(fd, CDRIOCFLUSH) < 0)
456 err(EX_IOERR, "ioctl(CDRIOCFLUSH)");
461 write_file(struct track_info *track_info)
463 int size, count, filesize;
464 char buf[2352*BLOCKS];
465 static int tot_size = 0;
467 filesize = track_info->file_size / 1024;
469 if (ioctl(fd, CDRIOCSETBLOCKSIZE, &track_info->block_size) < 0)
470 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
472 if (track_info->addr >= 0)
473 lseek(fd, track_info->addr * track_info->block_size, SEEK_SET);
476 fprintf(stderr, "addr = %d size = %d blocks = %d\n",
477 track_info->addr, track_info->file_size,
478 roundup_blocks(track_info));
481 if (track_info->file == STDIN_FILENO)
482 fprintf(stderr, "writing from stdin\n");
485 "writing from file %s size %d KB\n",
486 track_info->file_name, filesize);
490 while ((count = read(track_info->file, buf,
491 MIN((track_info->file_size - size),
492 track_info->block_size * BLOCKS))) > 0) {
495 if (count % track_info->block_size) {
496 /* pad file to % block_size */
498 (track_info->block_size * BLOCKS) - count);
499 count = ((count / track_info->block_size) + 1) *
500 track_info->block_size;
502 if ((res = write(fd, buf, count)) != count) {
503 fprintf(stderr, "\nonly wrote %d of %d bytes err=%d\n",
512 fprintf(stderr, "written this track %d KB", size/1024);
513 if (track_info->file != STDIN_FILENO && filesize) {
514 pct = (size / 1024) * 100 / filesize;
515 fprintf(stderr, " (%d%%)", pct);
517 fprintf(stderr, " total %d KB\r", tot_size/1024);
519 if (size >= track_info->file_size)
524 fprintf(stderr, "\n");
525 close(track_info->file);
530 roundup_blocks(struct track_info *track)
532 return ((track->file_size + track->block_size - 1) / track->block_size);
536 cue_ent(struct cdr_cue_entry *cue, int ctl, int adr, int track, int idx,
537 int dataform, int scms, int lba)
543 cue->dataform = dataform;
546 cue->min = lba / (60*75);
547 cue->sec = (lba % (60*75)) / 75;
548 cue->frame = (lba % (60*75)) % 75;
552 cleanup(int dummy __unused)
554 if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0)
555 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
562 "Usage: %s [-delmnpqtv] [-f device] [-s speed] [command]"
563 " [command file ...]\n", getprogname());