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