1 /*- 2 * Copyright (c) 2000,2001,2002 S�ren Schmidt <sos@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 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. 16 * 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. 27 * 28 * $FreeBSD: src/usr.sbin/burncd/burncd.c,v 1.10.2.6 2002/11/20 00:26:18 njl Exp $ 29 * $DragonFly: src/usr.sbin/burncd/burncd.c,v 1.4 2008/06/05 18:06:33 swildner Exp $ 30 */ 31 32 #include <unistd.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <err.h> 37 #include <sysexits.h> 38 #include <fcntl.h> 39 #include <sys/errno.h> 40 #include <sys/ioctl.h> 41 #include <sys/stat.h> 42 #include <sys/cdio.h> 43 #include <sys/cdrio.h> 44 #include <sys/param.h> 45 46 #define BLOCKS 16 47 48 struct track_info { 49 int file; 50 char *file_name; 51 u_int file_size; 52 int block_size; 53 int block_type; 54 int pregap; 55 int addr; 56 }; 57 static struct track_info tracks[100]; 58 static int fd, quiet, verbose, saved_block_size, notracks; 59 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); 66 void cleanup(int); 67 void usage(void); 68 69 int 70 main(int argc, char **argv) 71 { 72 int ch, arg, addr; 73 int dao = 0, eject = 0, fixate = 0, list = 0, multi = 0, preemp = 0; 74 int nogap = 0, speed = 4 * 177, test_write = 0; 75 int block_size = 0, block_type = 0, cdopen = 0; 76 const char *dev = "/dev/acd0c"; 77 78 while ((ch = getopt(argc, argv, "def:lmnpqs:tv")) != -1) { 79 switch (ch) { 80 case 'd': 81 dao = 1; 82 break; 83 84 case 'e': 85 eject = 1; 86 break; 87 88 case 'f': 89 dev = optarg; 90 break; 91 92 case 'l': 93 list = 1; 94 break; 95 96 case 'm': 97 multi = 1; 98 break; 99 100 case 'n': 101 nogap = 1; 102 break; 103 104 case 'p': 105 preemp = 1; 106 break; 107 108 case 'q': 109 quiet = 1; 110 break; 111 112 case 's': 113 if (strcasecmp("max", optarg) == 0) 114 speed = CDR_MAX_SPEED; 115 else 116 speed = atoi(optarg) * 177; 117 if (speed <= 0) 118 errx(EX_USAGE, "Invalid speed: %s", optarg); 119 break; 120 121 case 't': 122 test_write = 1; 123 break; 124 125 case 'v': 126 verbose = 1; 127 break; 128 129 default: 130 usage(); 131 } 132 } 133 argc -= optind; 134 argv += optind; 135 136 if (argc == 0) 137 usage(); 138 139 if ((fd = open(dev, O_RDWR, 0)) < 0) 140 err(EX_NOINPUT, "open(%s)", dev); 141 142 if (ioctl(fd, CDRIOCGETBLOCKSIZE, &saved_block_size) < 0) 143 err(EX_IOERR, "ioctl(CDRIOCGETBLOCKSIZE)"); 144 145 if (ioctl(fd, CDRIOCWRITESPEED, &speed) < 0) 146 err(EX_IOERR, "ioctl(CDRIOCWRITESPEED)"); 147 148 err_set_exit(cleanup); 149 150 for (arg = 0; arg < argc; arg++) { 151 if (!strcasecmp(argv[arg], "fixate")) { 152 fixate = 1; 153 break; 154 } 155 if (!strcasecmp(argv[arg], "msinfo")) { 156 struct ioc_read_toc_single_entry entry; 157 struct ioc_toc_header header; 158 159 if (ioctl(fd, CDIOREADTOCHEADER, &header) < 0) 160 err(EX_IOERR, "ioctl(CDIOREADTOCHEADER)"); 161 bzero(&entry, sizeof(struct ioc_read_toc_single_entry)); 162 entry.address_format = CD_LBA_FORMAT; 163 entry.track = header.ending_track; 164 if (ioctl(fd, CDIOREADTOCENTRY, &entry) < 0) 165 err(EX_IOERR, "ioctl(CDIOREADTOCENTRY)"); 166 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0) 167 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)"); 168 fprintf(stdout, "%d,%d\n", 169 ntohl(entry.entry.addr.lba), addr); 170 171 break; 172 } 173 if (!strcasecmp(argv[arg], "erase") || !strcasecmp(argv[arg], "blank")){ 174 int error, blank, percent; 175 176 if (!strcasecmp(argv[arg], "erase")) 177 blank = CDR_B_ALL; 178 else 179 blank = CDR_B_MIN; 180 if (!quiet) 181 fprintf(stderr, "%sing CD, please wait..\r", 182 blank == CDR_B_ALL ? "eras" : "blank"); 183 184 if (ioctl(fd, CDRIOCBLANK, &blank) < 0) 185 err(EX_IOERR, "ioctl(CDRIOCBLANK)"); 186 while (1) { 187 sleep(1); 188 error = ioctl(fd, CDRIOCGETPROGRESS, &percent); 189 if (percent > 0 && !quiet) 190 fprintf(stderr, 191 "%sing CD - %d %% done \r", 192 blank == CDR_B_ALL ? 193 "eras" : "blank", percent); 194 if (error || percent == 100) 195 break; 196 } 197 if (!quiet) 198 printf("\n"); 199 continue; 200 } 201 if (!strcasecmp(argv[arg], "audio") || !strcasecmp(argv[arg], "raw")) { 202 block_type = CDR_DB_RAW; 203 block_size = 2352; 204 continue; 205 } 206 if (!strcasecmp(argv[arg], "data") || !strcasecmp(argv[arg], "mode1")) { 207 block_type = CDR_DB_ROM_MODE1; 208 block_size = 2048; 209 continue; 210 } 211 if (!strcasecmp(argv[arg], "mode2")) { 212 block_type = CDR_DB_ROM_MODE2; 213 block_size = 2336; 214 continue; 215 } 216 if (!strcasecmp(argv[arg], "xamode1")) { 217 block_type = CDR_DB_XA_MODE1; 218 block_size = 2048; 219 continue; 220 } 221 if (!strcasecmp(argv[arg], "xamode2")) { 222 block_type = CDR_DB_XA_MODE2_F2; 223 block_size = 2324; 224 continue; 225 } 226 if (!strcasecmp(argv[arg], "vcd")) { 227 block_type = CDR_DB_XA_MODE2_F2; 228 block_size = 2352; 229 dao = 1; 230 nogap = 1; 231 continue; 232 } 233 if (!block_size) 234 errx(EX_NOINPUT, "no data format selected"); 235 if (list) { 236 char file_buf[MAXPATHLEN + 1], *eol; 237 FILE *fp; 238 239 if ((fp = fopen(argv[arg], "r")) == NULL) 240 err(EX_NOINPUT, "fopen(%s)", argv[arg]); 241 242 while (fgets(file_buf, sizeof(file_buf), fp) != NULL) { 243 if (*file_buf == '#' || *file_buf == '\n') 244 continue; 245 if ((eol = strchr(file_buf, '\n'))) 246 *eol = 0; 247 add_track(file_buf, block_size, block_type, nogap); 248 } 249 if (feof(fp)) 250 fclose(fp); 251 else 252 err(EX_IOERR, "fgets(%s)", file_buf); 253 } 254 else 255 add_track(argv[arg], block_size, block_type, nogap); 256 } 257 if (notracks) { 258 if (ioctl(fd, CDIOCSTART, 0) < 0) 259 err(EX_IOERR, "ioctl(CDIOCSTART)"); 260 if (!cdopen) { 261 if (ioctl(fd, CDRIOCINITWRITER, &test_write) < 0) 262 err(EX_IOERR, "ioctl(CDRIOCINITWRITER)"); 263 cdopen = 1; 264 } 265 if (dao) 266 do_DAO(test_write, multi); 267 else 268 do_TAO(test_write, preemp); 269 } 270 if (fixate && !dao) { 271 if (!quiet) 272 fprintf(stderr, "fixating CD, please wait..\n"); 273 if (ioctl(fd, CDRIOCFIXATE, &multi) < 0) 274 err(EX_IOERR, "ioctl(CDRIOCFIXATE)"); 275 } 276 277 if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) { 278 err_set_exit(NULL); 279 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)"); 280 } 281 282 if (eject) 283 if (ioctl(fd, CDIOCEJECT) < 0) 284 err(EX_IOERR, "ioctl(CDIOCEJECT)"); 285 close(fd); 286 exit(EX_OK); 287 } 288 289 void 290 add_track(char *name, int block_size, int block_type, int nogap) 291 { 292 struct stat sb; 293 int file; 294 static int done_stdin = 0; 295 296 if (!strcmp(name, "-")) { 297 if (done_stdin) { 298 warn("skipping multiple usages of stdin"); 299 return; 300 } 301 file = STDIN_FILENO; 302 done_stdin = 1; 303 } 304 else if ((file = open(name, O_RDONLY, 0)) < 0) 305 err(EX_NOINPUT, "open(%s)", name); 306 if (fstat(file, &sb) < 0) 307 err(EX_IOERR, "fstat(%s)", name); 308 tracks[notracks].file = file; 309 tracks[notracks].file_name = name; 310 if (file == STDIN_FILENO) 311 tracks[notracks].file_size = -1; 312 else 313 tracks[notracks].file_size = sb.st_size; 314 tracks[notracks].block_size = block_size; 315 tracks[notracks].block_type = block_type; 316 317 if (nogap && notracks) 318 tracks[notracks].pregap = 0; 319 else { 320 if (tracks[notracks - (notracks > 0)].block_type == block_type) 321 tracks[notracks].pregap = 150; 322 else 323 tracks[notracks].pregap = 255; 324 } 325 326 if (verbose) { 327 int pad = 0; 328 329 if (tracks[notracks].file_size / tracks[notracks].block_size != 330 roundup_blocks(&tracks[notracks])) 331 pad = 1; 332 fprintf(stderr, 333 "adding type 0x%02x file %s size %d KB %d blocks %s\n", 334 tracks[notracks].block_type, name, (int)sb.st_size/1024, 335 roundup_blocks(&tracks[notracks]), 336 pad ? "(0 padded)" : ""); 337 } 338 notracks++; 339 } 340 341 void 342 do_DAO(int test_write, int multi) 343 { 344 struct cdr_cuesheet sheet; 345 struct cdr_cue_entry cue[100]; 346 int format = CDR_SESS_CDROM; 347 int addr, i, j = 0; 348 349 int bt2ctl[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1, 350 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, -1, -1 }; 351 352 int bt2df[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1, 353 0x10, 0x30, 0x20, -1, 0x21, -1, -1, -1 }; 354 355 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0) 356 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)"); 357 if (verbose) 358 fprintf(stderr, "next writeable LBA %d\n", addr); 359 360 cue_ent(&cue[j++], bt2ctl[tracks[0].block_type], 0x01, 0x00, 0x0, 361 (bt2df[tracks[0].block_type] & 0xf0) | 362 (tracks[0].block_type < 8 ? 0x01 : 0x04), 0x00, addr); 363 364 for (i = 0; i < notracks; i++) { 365 if (bt2ctl[tracks[i].block_type] < 0 || 366 bt2df[tracks[i].block_type] < 0) 367 err(EX_IOERR, "track type not supported in DAO mode"); 368 369 if (tracks[i].block_type >= CDR_DB_XA_MODE1) 370 format = CDR_SESS_CDROM_XA; 371 372 if (i == 0) { 373 addr += tracks[i].pregap; 374 tracks[i].addr = addr; 375 376 cue_ent(&cue[j++], bt2ctl[tracks[i].block_type], 377 0x01, i+1, 0x1, bt2df[tracks[i].block_type], 378 0x00, addr); 379 380 } 381 else { 382 if (tracks[i].pregap) { 383 if (tracks[i].block_type > 0x7) { 384 cue_ent(&cue[j++],bt2ctl[tracks[i].block_type], 385 0x01, i+1, 0x0, 386 (bt2df[tracks[i].block_type] & 0xf0) | 387 (tracks[i].block_type < 8 ? 0x01 :0x04), 388 0x00, addr); 389 } 390 else 391 cue_ent(&cue[j++],bt2ctl[tracks[i].block_type], 392 0x01, i+1, 0x0, 393 bt2df[tracks[i].block_type], 394 0x00, addr); 395 } 396 tracks[i].addr = tracks[i - 1].addr + 397 roundup_blocks(&tracks[i - 1]); 398 399 cue_ent(&cue[j++], bt2ctl[tracks[i].block_type], 400 0x01, i+1, 0x1, bt2df[tracks[i].block_type], 401 0x00, addr + tracks[i].pregap); 402 403 if (tracks[i].block_type > 0x7) 404 addr += tracks[i].pregap; 405 } 406 addr += roundup_blocks(&tracks[i]); 407 } 408 409 cue_ent(&cue[j++], bt2ctl[tracks[i - 1].block_type], 0x01, 0xaa, 0x01, 410 (bt2df[tracks[i - 1].block_type] & 0xf0) | 411 (tracks[i - 1].block_type < 8 ? 0x01 : 0x04), 0x00, addr); 412 413 sheet.len = j * 8; 414 sheet.entries = cue; 415 sheet.test_write = test_write; 416 sheet.session_type = multi ? CDR_SESS_MULTI : CDR_SESS_NONE; 417 sheet.session_format = format; 418 if (verbose) { 419 u_int8_t *ptr = (u_int8_t *)sheet.entries; 420 421 fprintf(stderr,"CUE sheet:"); 422 for (i = 0; i < sheet.len; i++) 423 if (i % 8) 424 fprintf(stderr," %02x", ptr[i]); 425 else 426 fprintf(stderr,"\n%02x", ptr[i]); 427 fprintf(stderr,"\n"); 428 } 429 430 if (ioctl(fd, CDRIOCSENDCUE, &sheet) < 0) 431 err(EX_IOERR, "ioctl(CDRIOCSENDCUE)"); 432 433 for (i = 0; i < notracks; i++) { 434 if (write_file(&tracks[i])) 435 err(EX_IOERR, "write_file"); 436 } 437 438 ioctl(fd, CDRIOCFLUSH); 439 } 440 441 void 442 do_TAO(int test_write, int preemp) 443 { 444 struct cdr_track track; 445 int i; 446 447 for (i = 0; i < notracks; i++) { 448 track.test_write = test_write; 449 track.datablock_type = tracks[i].block_type; 450 track.preemp = preemp; 451 if (ioctl(fd, CDRIOCINITTRACK, &track) < 0) 452 err(EX_IOERR, "ioctl(CDRIOCINITTRACK)"); 453 454 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &tracks[i].addr) < 0) 455 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)"); 456 if (!quiet) 457 fprintf(stderr, "next writeable LBA %d\n", 458 tracks[i].addr); 459 if (write_file(&tracks[i])) 460 err(EX_IOERR, "write_file"); 461 if (ioctl(fd, CDRIOCFLUSH) < 0) 462 err(EX_IOERR, "ioctl(CDRIOCFLUSH)"); 463 } 464 } 465 466 int 467 write_file(struct track_info *track_info) 468 { 469 int size, count, filesize; 470 char buf[2352*BLOCKS]; 471 static int tot_size = 0; 472 473 filesize = track_info->file_size / 1024; 474 475 if (ioctl(fd, CDRIOCSETBLOCKSIZE, &track_info->block_size) < 0) 476 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)"); 477 478 if (track_info->addr >= 0) 479 lseek(fd, track_info->addr * track_info->block_size, SEEK_SET); 480 481 if (verbose) 482 fprintf(stderr, "addr = %d size = %d blocks = %d\n", 483 track_info->addr, track_info->file_size, 484 roundup_blocks(track_info)); 485 486 if (!quiet) { 487 if (track_info->file == STDIN_FILENO) 488 fprintf(stderr, "writing from stdin\n"); 489 else 490 fprintf(stderr, 491 "writing from file %s size %d KB\n", 492 track_info->file_name, filesize); 493 } 494 size = 0; 495 496 while ((count = read(track_info->file, buf, 497 MIN((track_info->file_size - size), 498 track_info->block_size * BLOCKS))) > 0) { 499 int res; 500 501 if (count % track_info->block_size) { 502 /* pad file to % block_size */ 503 bzero(&buf[count], 504 (track_info->block_size * BLOCKS) - count); 505 count = ((count / track_info->block_size) + 1) * 506 track_info->block_size; 507 } 508 if ((res = write(fd, buf, count)) != count) { 509 fprintf(stderr, "\nonly wrote %d of %d bytes err=%d\n", 510 res, count, errno); 511 break; 512 } 513 size += count; 514 tot_size += count; 515 if (!quiet) { 516 int pct; 517 518 fprintf(stderr, "written this track %d KB", size/1024); 519 if (track_info->file != STDIN_FILENO && filesize) { 520 pct = (size / 1024) * 100 / filesize; 521 fprintf(stderr, " (%d%%)", pct); 522 } 523 fprintf(stderr, " total %d KB\r", tot_size/1024); 524 } 525 if (size >= track_info->file_size) 526 break; 527 } 528 529 if (!quiet) 530 fprintf(stderr, "\n"); 531 close(track_info->file); 532 return 0; 533 } 534 535 int 536 roundup_blocks(struct track_info *track) 537 { 538 return ((track->file_size + track->block_size - 1) / track->block_size); 539 } 540 541 void 542 cue_ent(struct cdr_cue_entry *cue, int ctl, int adr, int track, int idx, 543 int dataform, int scms, int lba) 544 { 545 cue->adr = adr; 546 cue->ctl = ctl; 547 cue->track = track; 548 cue->index = idx; 549 cue->dataform = dataform; 550 cue->scms = scms; 551 lba += 150; 552 cue->min = lba / (60*75); 553 cue->sec = (lba % (60*75)) / 75; 554 cue->frame = (lba % (60*75)) % 75; 555 } 556 557 void 558 cleanup(int dummy __unused) 559 { 560 if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) 561 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)"); 562 } 563 564 void 565 usage(void) 566 { 567 fprintf(stderr, 568 "Usage: %s [-delmnpqtv] [-f device] [-s speed] [command]" 569 " [command file ...]\n", getprogname()); 570 exit(EX_USAGE); 571 } 572