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.
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.3 2006/02/02 17:00:29 eirikn Exp $
39 #include <sys/errno.h>
40 #include <sys/ioctl.h>
43 #include <sys/cdrio.h>
44 #include <sys/param.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 * 177, 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 if (strcasecmp("max", optarg
) == 0)
114 speed
= CDR_MAX_SPEED
;
116 speed
= atoi(optarg
) * 177;
118 errx(EX_USAGE
, "Invalid speed: %s", optarg
);
139 if ((fd
= open(dev
, O_RDWR
, 0)) < 0)
140 err(EX_NOINPUT
, "open(%s)", dev
);
142 if (ioctl(fd
, CDRIOCGETBLOCKSIZE
, &saved_block_size
) < 0)
143 err(EX_IOERR
, "ioctl(CDRIOCGETBLOCKSIZE)");
145 if (ioctl(fd
, CDRIOCWRITESPEED
, &speed
) < 0)
146 err(EX_IOERR
, "ioctl(CDRIOCWRITESPEED)");
148 err_set_exit(cleanup
);
150 for (arg
= 0; arg
< argc
; arg
++) {
151 if (!strcasecmp(argv
[arg
], "fixate")) {
155 if (!strcasecmp(argv
[arg
], "msinfo")) {
156 struct ioc_read_toc_single_entry entry
;
157 struct ioc_toc_header header
;
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
);
173 if (!strcasecmp(argv
[arg
], "erase") || !strcasecmp(argv
[arg
], "blank")){
174 int error
, blank
, percent
;
176 if (!strcasecmp(argv
[arg
], "erase"))
181 fprintf(stderr
, "%sing CD, please wait..\r",
182 blank
== CDR_B_ALL
? "eras" : "blank");
184 if (ioctl(fd
, CDRIOCBLANK
, &blank
) < 0)
185 err(EX_IOERR
, "ioctl(CDRIOCBLANK)");
188 error
= ioctl(fd
, CDRIOCGETPROGRESS
, &percent
);
189 if (percent
> 0 && !quiet
)
191 "%sing CD - %d %% done \r",
193 "eras" : "blank", percent
);
194 if (error
|| percent
== 100)
201 if (!strcasecmp(argv
[arg
], "audio") || !strcasecmp(argv
[arg
], "raw")) {
202 block_type
= CDR_DB_RAW
;
206 if (!strcasecmp(argv
[arg
], "data") || !strcasecmp(argv
[arg
], "mode1")) {
207 block_type
= CDR_DB_ROM_MODE1
;
211 if (!strcasecmp(argv
[arg
], "mode2")) {
212 block_type
= CDR_DB_ROM_MODE2
;
216 if (!strcasecmp(argv
[arg
], "xamode1")) {
217 block_type
= CDR_DB_XA_MODE1
;
221 if (!strcasecmp(argv
[arg
], "xamode2")) {
222 block_type
= CDR_DB_XA_MODE2_F2
;
226 if (!strcasecmp(argv
[arg
], "vcd")) {
227 block_type
= CDR_DB_XA_MODE2_F2
;
234 errx(EX_NOINPUT
, "no data format selected");
236 char file_buf
[MAXPATHLEN
+ 1], *eol
;
239 if ((fp
= fopen(argv
[arg
], "r")) == NULL
)
240 err(EX_NOINPUT
, "fopen(%s)", argv
[arg
]);
242 while (fgets(file_buf
, sizeof(file_buf
), fp
) != NULL
) {
243 if (*file_buf
== '#' || *file_buf
== '\n')
245 if ((eol
= strchr(file_buf
, '\n')))
247 add_track(file_buf
, block_size
, block_type
, nogap
);
252 err(EX_IOERR
, "fgets(%s)", file_buf
);
255 add_track(argv
[arg
], block_size
, block_type
, nogap
);
258 if (ioctl(fd
, CDIOCSTART
, 0) < 0)
259 err(EX_IOERR
, "ioctl(CDIOCSTART)");
261 if (ioctl(fd
, CDRIOCINITWRITER
, &test_write
) < 0)
262 err(EX_IOERR
, "ioctl(CDRIOCINITWRITER)");
266 do_DAO(test_write
, multi
);
268 do_TAO(test_write
, preemp
);
270 if (fixate
&& !dao
) {
272 fprintf(stderr
, "fixating CD, please wait..\n");
273 if (ioctl(fd
, CDRIOCFIXATE
, &multi
) < 0)
274 err(EX_IOERR
, "ioctl(CDRIOCFIXATE)");
277 if (ioctl(fd
, CDRIOCSETBLOCKSIZE
, &saved_block_size
) < 0) {
279 err(EX_IOERR
, "ioctl(CDRIOCSETBLOCKSIZE)");
283 if (ioctl(fd
, CDIOCEJECT
) < 0)
284 err(EX_IOERR
, "ioctl(CDIOCEJECT)");
290 add_track(char *name
, int block_size
, int block_type
, int nogap
)
294 static int done_stdin
= 0;
296 if (!strcmp(name
, "-")) {
298 warn("skipping multiple usages of stdin");
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;
313 tracks
[notracks
].file_size
= sb
.st_size
;
314 tracks
[notracks
].block_size
= block_size
;
315 tracks
[notracks
].block_type
= block_type
;
317 if (nogap
&& notracks
)
318 tracks
[notracks
].pregap
= 0;
320 if (tracks
[notracks
- (notracks
> 0)].block_type
== block_type
)
321 tracks
[notracks
].pregap
= 150;
323 tracks
[notracks
].pregap
= 255;
329 if (tracks
[notracks
].file_size
/ tracks
[notracks
].block_size
!=
330 roundup_blocks(&tracks
[notracks
]))
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)" : "");
342 do_DAO(int test_write
, int multi
)
344 struct cdr_cuesheet sheet
;
345 struct cdr_cue_entry cue
[100];
346 int format
= CDR_SESS_CDROM
;
349 int bt2ctl
[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
350 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, -1, -1 };
352 int bt2df
[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
353 0x10, 0x30, 0x20, -1, 0x21, -1, -1, -1 };
355 if (ioctl(fd
, CDRIOCNEXTWRITEABLEADDR
, &addr
) < 0)
356 err(EX_IOERR
, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
358 fprintf(stderr
, "next writeable LBA %d\n", addr
);
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
);
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");
369 if (tracks
[i
].block_type
>= CDR_DB_XA_MODE1
)
370 format
= CDR_SESS_CDROM_XA
;
373 addr
+= tracks
[i
].pregap
;
374 tracks
[i
].addr
= addr
;
376 cue_ent(&cue
[j
++], bt2ctl
[tracks
[i
].block_type
],
377 0x01, i
+1, 0x1, bt2df
[tracks
[i
].block_type
],
382 if (tracks
[i
].pregap
) {
383 if (tracks
[i
].block_type
> 0x7) {
384 cue_ent(&cue
[j
++],bt2ctl
[tracks
[i
].block_type
],
386 (bt2df
[tracks
[i
].block_type
] & 0xf0) |
387 (tracks
[i
].block_type
< 8 ? 0x01 :0x04),
391 cue_ent(&cue
[j
++],bt2ctl
[tracks
[i
].block_type
],
393 bt2df
[tracks
[i
].block_type
],
396 tracks
[i
].addr
= tracks
[i
- 1].addr
+
397 roundup_blocks(&tracks
[i
- 1]);
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
);
403 if (tracks
[i
].block_type
> 0x7)
404 addr
+= tracks
[i
].pregap
;
406 addr
+= roundup_blocks(&tracks
[i
]);
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
);
415 sheet
.test_write
= test_write
;
416 sheet
.session_type
= multi
? CDR_SESS_MULTI
: CDR_SESS_NONE
;
417 sheet
.session_format
= format
;
419 u_int8_t
*ptr
= (u_int8_t
*)sheet
.entries
;
421 fprintf(stderr
,"CUE sheet:");
422 for (i
= 0; i
< sheet
.len
; i
++)
424 fprintf(stderr
," %02x", ptr
[i
]);
426 fprintf(stderr
,"\n%02x", ptr
[i
]);
427 fprintf(stderr
,"\n");
430 if (ioctl(fd
, CDRIOCSENDCUE
, &sheet
) < 0)
431 err(EX_IOERR
, "ioctl(CDRIOCSENDCUE)");
433 for (i
= 0; i
< notracks
; i
++) {
434 if (write_file(&tracks
[i
]))
435 err(EX_IOERR
, "write_file");
438 ioctl(fd
, CDRIOCFLUSH
);
442 do_TAO(int test_write
, int preemp
)
444 struct cdr_track track
;
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)");
454 if (ioctl(fd
, CDRIOCNEXTWRITEABLEADDR
, &tracks
[i
].addr
) < 0)
455 err(EX_IOERR
, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
457 fprintf(stderr
, "next writeable LBA %d\n",
459 if (write_file(&tracks
[i
]))
460 err(EX_IOERR
, "write_file");
461 if (ioctl(fd
, CDRIOCFLUSH
) < 0)
462 err(EX_IOERR
, "ioctl(CDRIOCFLUSH)");
467 write_file(struct track_info
*track_info
)
469 int size
, count
, filesize
;
470 char buf
[2352*BLOCKS
];
471 static int tot_size
= 0;
473 filesize
= track_info
->file_size
/ 1024;
475 if (ioctl(fd
, CDRIOCSETBLOCKSIZE
, &track_info
->block_size
) < 0)
476 err(EX_IOERR
, "ioctl(CDRIOCSETBLOCKSIZE)");
478 if (track_info
->addr
>= 0)
479 lseek(fd
, track_info
->addr
* track_info
->block_size
, SEEK_SET
);
482 fprintf(stderr
, "addr = %d size = %d blocks = %d\n",
483 track_info
->addr
, track_info
->file_size
,
484 roundup_blocks(track_info
));
487 if (track_info
->file
== STDIN_FILENO
)
488 fprintf(stderr
, "writing from stdin\n");
491 "writing from file %s size %d KB\n",
492 track_info
->file_name
, filesize
);
496 while ((count
= read(track_info
->file
, buf
,
497 MIN((track_info
->file_size
- size
),
498 track_info
->block_size
* BLOCKS
))) > 0) {
501 if (count
% track_info
->block_size
) {
502 /* pad file to % block_size */
504 (track_info
->block_size
* BLOCKS
) - count
);
505 count
= ((count
/ track_info
->block_size
) + 1) *
506 track_info
->block_size
;
508 if ((res
= write(fd
, buf
, count
)) != count
) {
509 fprintf(stderr
, "\nonly wrote %d of %d bytes err=%d\n",
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
);
523 fprintf(stderr
, " total %d KB\r", tot_size
/1024);
525 if (size
>= track_info
->file_size
)
530 fprintf(stderr
, "\n");
531 close(track_info
->file
);
536 roundup_blocks(struct track_info
*track
)
538 return ((track
->file_size
+ track
->block_size
- 1) / track
->block_size
);
542 cue_ent(struct cdr_cue_entry
*cue
, int ctl
, int adr
, int track
, int idx
,
543 int dataform
, int scms
, int lba
)
549 cue
->dataform
= dataform
;
552 cue
->min
= lba
/ (60*75);
553 cue
->sec
= (lba
% (60*75)) / 75;
554 cue
->frame
= (lba
% (60*75)) % 75;
558 cleanup(int dummy __unused
)
560 if (ioctl(fd
, CDRIOCSETBLOCKSIZE
, &saved_block_size
) < 0)
561 err(EX_IOERR
, "ioctl(CDRIOCSETBLOCKSIZE)");
568 "Usage: %s [-delmnpqtv] [-f device] [-s speed] [command]"
569 " [command file ...]\n", getprogname());