Date: Wed, 10 Jul 2002 13:48:54 -0700 From: Peter Haight <peterh@sapros.com> To: FreeBSD-gnats-submit@FreeBSD.org Subject: misc/40430: Writing to DVD+RW using burncd does not work. Message-ID: <200207102048.g6AKms7W000327@talri.sapros.com>
next in thread | raw e-mail | index | archive | help
>Number: 40430
>Category: misc
>Synopsis: Writing to DVD+RW using burncd does not work.
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: change-request
>Submitter-Id: current-users
>Arrival-Date: Wed Jul 10 13:50:03 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator: Peter Haight
>Release: FreeBSD 4.6-STABLE i386
>Organization:
>Environment:
System: FreeBSD talri.sapros.com 4.6-STABLE FreeBSD 4.6-STABLE #15: Sun Jun 30 18:34:25 PDT 2002 peterh@talri.sapros.com:/usr/src/sys/compile/TALRI i386
Sony DVD+RW DRU-120A IDE
>Description:
When you try to use burncd to write to a DVD+RW, you get an Input/Output
error. I've come up with a patch that lets you write to a DVD+RW using
burncd.
>How-To-Repeat:
burncd -d /dev/acd0c data foo.iso
>Fix:
I've provided a patch to the kernel and burncd that lets you write to DVD+RW
drives.
There are two reasons why it wasn't working before this patch. The first is
that DVD+RW media needs to be formatted before it is used. You can
accomplish this using the ATAPI_FORMAT_UNIT command. I added an ioctl
command that will let you read the format capacities (DVDREADFORMATCAPS) and
then use one of those format capacity descriptiors to format the DVD+RW
(DVDFORMATUNIT). I added code to burncd to execute these commands. (You can
use 'burncd format' to make it do this).
The other problem was that burncd expects tracks like on a CD-R and DVD+RW
doesn't really have tracks. I added a new write command to burncd to take
this lack of tracks into account. You can use 'burncd dvdwrite <file_name>'
to use this method of writing.
Here's the patch. It is against 4.6-STABLE that I cvsupped from 6/30/02.
--- sys/dev/ata/atapi-all.h.orig Sun Jun 30 20:07:17 2002
+++ sys/dev/ata/atapi-all.h Sat Jun 29 15:37:22 2002
@@ -66,6 +66,7 @@
#define ATAPI_TEST_UNIT_READY 0x00 /* check if device is ready */
#define ATAPI_REZERO 0x01 /* rewind */
#define ATAPI_REQUEST_SENSE 0x03 /* get sense data */
+#define ATAPI_FORMAT_UNIT 0x04 /* format unit */
#define ATAPI_READ 0x08 /* read data */
#define ATAPI_WRITE 0x0a /* write data */
#define ATAPI_WEOF 0x10 /* write filemark */
@@ -81,6 +82,7 @@
#define SS_RETENSION 0x02
#define SS_EJECT 0x04
#define ATAPI_PREVENT_ALLOW 0x1e /* media removal */
+#define ATAPI_READ_FORMAT_CAPACITIES 0x23 /* get format capacities */
#define ATAPI_READ_CAPACITY 0x25 /* get volume capacity */
#define ATAPI_READ_BIG 0x28 /* read data */
#define ATAPI_WRITE_BIG 0x2a /* write data */
--- sys/dev/ata/atapi-cd.c.orig Sat Jun 29 13:11:37 2002
+++ sys/dev/ata/atapi-cd.c Sun Jun 30 18:34:06 2002
@@ -100,6 +100,10 @@
static int acd_mode_select(struct acd_softc *, caddr_t, int);
static int acd_set_speed(struct acd_softc *, int, int);
static void acd_get_cap(struct acd_softc *);
+static int acd_read_format_caps(struct acd_softc *, struct dvd_format_cap_trans *);
+static int acd_format_unit(struct acd_softc *, struct dvd_format_params *);
+static int acd_get_format_progress(struct acd_softc *, int *);
+
/* internal vars */
static u_int32_t acd_lun_map = 0;
@@ -1046,6 +1050,18 @@
((struct partinfo *)addr)->part = &cdp->disklabel.d_partitions[0];
break;
+ case DVDIOREADFORMATCAPS:
+ error = acd_read_format_caps(cdp, (struct dvd_format_cap_trans *)addr);
+ break;
+
+ case DVDIOFORMATUNIT:
+ error = acd_format_unit(cdp, (struct dvd_format_params *)addr);
+ break;
+
+ case DVDIOFORMATPROGRESS:
+ error = acd_get_format_progress(cdp, (int *) addr);
+ break;
+
default:
error = ENOTTY;
}
@@ -1991,4 +2007,69 @@
cdp->cap.cur_write_speed = max(ntohs(cdp->cap.cur_write_speed), 177);
cdp->cap.max_vol_levels = ntohs(cdp->cap.max_vol_levels);
cdp->cap.buf_size = ntohs(cdp->cap.buf_size);
+}
+
+static int
+acd_read_format_caps(struct acd_softc *cdp, struct dvd_format_cap_trans* trans)
+{
+ int error;
+ int8_t *kernel_buf;
+ int8_t ccb[16] = { ATAPI_READ_FORMAT_CAPACITIES, 0, 0, 0, 0, 0, 0,
+ (trans->buf_len >> 8) & 0xff, trans->buf_len & 0xff,
+ 0, 0, 0, 0, 0, 0, 0 };
+
+ kernel_buf = malloc(trans->buf_len, M_ACD, M_NOWAIT);
+ if (kernel_buf == NULL) {
+ return ENOMEM;
+ }
+
+#define DVD_READ_FORMAT_CAPS_TIMEOUT 30
+ error = atapi_queue_cmd(cdp->device, ccb, kernel_buf, trans->buf_len,
+ ATPR_F_READ, DVD_READ_FORMAT_CAPS_TIMEOUT,
+ NULL, NULL);
+ if (!error) {
+ error = copyout(kernel_buf, trans->buf, trans->buf_len);
+ }
+
+ free(kernel_buf, M_ACD);
+
+ return error;
+}
+
+static int
+acd_format_unit(struct acd_softc *cdp, struct dvd_format_params* params)
+{
+ int error;
+ int8_t ccb[16] = { ATAPI_FORMAT_UNIT, 0x11, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0 };
+
+#define DVD_FORMAT_UNIT_TIMEOUT 30
+ error = atapi_queue_cmd(cdp->device, ccb, (u_int8_t *)params,
+ sizeof(struct dvd_format_params), 0,
+ DVD_FORMAT_UNIT_TIMEOUT, NULL, NULL);
+
+ return error;
+}
+
+static int
+acd_get_format_progress(struct acd_softc *cdp, int *finished)
+{
+ int error;
+ int8_t ccb[16] = { ATAPI_REQUEST_SENSE, 0, 0, 0,
+ sizeof(struct atapi_reqsense), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ struct atapi_reqsense response;
+
+ error = atapi_queue_cmd(cdp->device, ccb, (u_int8_t *) &response,
+ sizeof(struct atapi_reqsense), ATPR_F_READ,
+ 30, NULL, NULL);
+ if (error)
+ {
+ return error;
+ }
+ if (response.sksv)
+ *finished = ((response.sk_specific2 | (response.sk_specific1 << 8)) * 100) / 65535;
+ else
+ *finished = 0;
+ return 0;
}
--- sys/sys/dvdio.h.orig Sun Jun 30 20:10:24 2002
+++ sys/sys/dvdio.h Sun Jun 30 18:24:03 2002
@@ -107,4 +107,46 @@
#define DVDIOCSENDKEY _IOWR('c', 201, struct dvd_authinfo)
#define DVDIOCREADSTRUCTURE _IOWR('c', 202, struct dvd_struct)
+struct dvd_format_cap {
+ u_int32_t num_blocks;
+ u_int8_t reserved:2;
+ u_int8_t format_type:6;
+ u_int8_t type_param[3];
+};
+
+struct dvd_format_cap_list_header {
+ u_int8_t reserved[3];
+ u_int8_t cap_list_len;
+};
+
+struct dvd_cur_cap_desc {
+ u_int32_t num_blocks;
+ u_int8_t descriptor_type:2;
+ u_int8_t reserved:6;
+ u_int8_t block_len[3];
+};
+
+struct dvd_format_cap_trans {
+ u_int16_t buf_len;
+ u_int8_t *buf;
+};
+
+struct dvd_format_params {
+ u_int8_t reserved;
+ u_int8_t vs:1;
+ u_int8_t immed:1;
+ u_int8_t try_out:1;
+ u_int8_t ip:1;
+ u_int8_t stpf:1;
+ u_int8_t dcrt:1;
+ u_int8_t dpry:1;
+ u_int8_t fov:1;
+ u_int16_t desc_len;
+ struct dvd_format_cap desc;
+};
+
+#define DVDIOREADFORMATCAPS _IOWR('c', 203, struct dvd_format_cap_trans)
+#define DVDIOFORMATUNIT _IOW('c', 204, struct dvd_format_params)
+#define DVDIOFORMATPROGRESS _IOR('c', 205, struct dvd_format_params)
+
#endif _SYS_DVDIO_H_
--- usr.sbin/burncd/burncd.c.orig Sat Jun 29 16:09:23 2002
+++ usr.sbin/burncd/burncd.c Sun Jun 30 19:39:48 2002
@@ -40,6 +40,7 @@
#include <sys/stat.h>
#include <sys/cdio.h>
#include <sys/cdrio.h>
+#include <sys/dvdio.h>
#include <sys/param.h>
#define BLOCKS 16
@@ -58,7 +59,8 @@
void add_track(char *, int, int, int);
void do_DAO(int, int);
-void do_TAO(int, int);
+void do_TAO(int, int, int);
+void do_format(int, int);
int write_file(struct track_info *);
int roundup_blocks(struct track_info *);
void cue_ent(struct cdr_cue_entry *, int, int, int, int, int, int, int);
@@ -70,11 +72,11 @@
{
int ch, arg, addr;
int dao = 0, eject = 0, fixate = 0, list = 0, multi = 0, preemp = 0;
- int nogap = 0, speed = 4, test_write = 0;
- int block_size = 0, block_type = 0, cdopen = 0;
+ int nogap = 0, speed = 4, test_write = 0, format = 0, force_format = 0;
+ int block_size = 0, block_type = 0, cdopen = 0, dvdprw=0;
const char *dev = "/dev/acd0c";
- while ((ch = getopt(argc, argv, "def:lmnpqs:tv")) != -1) {
+ while ((ch = getopt(argc, argv, "def:Flmnpqs:tv")) != -1) {
switch (ch) {
case 'd':
dao = 1;
@@ -87,6 +89,10 @@
case 'f':
dev = optarg;
break;
+
+ case 'F':
+ force_format = 1;
+ break;
case 'l':
list = 1;
@@ -148,6 +154,10 @@
fixate = 1;
break;
}
+ if (!strcasecmp(argv[arg], "format")) {
+ format = 1;
+ break;
+ }
if (!strcasecmp(argv[arg], "msinfo")) {
struct ioc_read_toc_single_entry entry;
struct ioc_toc_header header;
@@ -226,6 +236,13 @@
nogap = 1;
continue;
}
+ if (!strcasecmp(argv[arg], "dvdprw")) {
+ block_type = CDR_DB_ROM_MODE1;
+ block_size = 2048;
+ dvdprw = 1;
+ continue;
+ }
+
if (!block_size)
err(EX_NOINPUT, "no data format selected");
if (list) {
@@ -251,6 +268,8 @@
add_track(argv[arg], block_size, block_type, nogap);
}
if (notracks) {
+ if (dvdprw && notracks > 1)
+ err(EX_USAGE, "DVD+RW drives can only write one track");
if (ioctl(fd, CDIOCSTART, 0) < 0)
err(EX_IOERR, "ioctl(CDIOCSTART)");
if (!cdopen) {
@@ -261,7 +280,7 @@
if (dao)
do_DAO(test_write, multi);
else
- do_TAO(test_write, preemp);
+ do_TAO(test_write, preemp, dvdprw);
}
if (fixate && !dao) {
if (!quiet)
@@ -270,6 +289,10 @@
err(EX_IOERR, "ioctl(CDRIOCFIXATE)");
}
+ if (format) {
+ do_format(fd, force_format);
+ }
+
if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) {
err_set_exit(NULL);
err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
@@ -435,7 +458,7 @@
}
void
-do_TAO(int test_write, int preemp)
+do_TAO(int test_write, int preemp, int dvdprw)
{
struct cdr_track track;
int i;
@@ -447,8 +470,13 @@
if (ioctl(fd, CDRIOCINITTRACK, &track) < 0)
err(EX_IOERR, "ioctl(CDRIOCINITTRACK)");
- if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &tracks[i].addr) < 0)
- err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
+ if (dvdprw)
+ tracks[i].addr = 0;
+ else
+ if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR,
+ &tracks[i].addr) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
+
if (!quiet)
fprintf(stderr, "next writeable LBA %d\n",
tracks[i].addr);
@@ -457,6 +485,86 @@
if (ioctl(fd, CDRIOCFLUSH) < 0)
err(EX_IOERR, "ioctl(CDRIOCFLUSH)");
}
+}
+
+#define BURNCD_MAX_FORMAT_CAP_LEN 1024
+void
+do_format(int fd, int force_format)
+{
+ int result;
+ int i;
+ int percent;
+ int started;
+ int num_caps;
+ int cap_to_use = -1;
+ u_int8_t *buf_pos;
+ struct dvd_format_cap_list_header *cap_list_header;
+ struct dvd_cur_cap_desc *cap_desc;
+ struct dvd_format_cap *format_cap;
+ struct dvd_format_cap_trans trans;
+ struct dvd_format_params format_params;
+ u_int8_t fmt_cap_buf[BURNCD_MAX_FORMAT_CAP_LEN];
+
+ trans.buf_len = BURNCD_MAX_FORMAT_CAP_LEN;
+ trans.buf = fmt_cap_buf;
+ if (ioctl(fd, DVDIOREADFORMATCAPS, &trans) == -1)
+ err(EX_IOERR, "ioctl(DVDIOREADFORMATCAPS)");
+
+ buf_pos = trans.buf;
+ cap_list_header = (struct dvd_format_cap_list_header *) buf_pos;
+ if (cap_list_header->cap_list_len +
+ sizeof(struct dvd_format_cap_list_header) +
+ sizeof(struct dvd_cur_cap_desc) > BURNCD_MAX_FORMAT_CAP_LEN)
+ err(EX_SOFTWARE, "did not allocate enough memory for cap list");
+
+ buf_pos += sizeof(struct dvd_format_cap_list_header);
+ cap_desc = (struct dvd_cur_cap_desc *) buf_pos;
+ if (verbose) {
+ fprintf(stderr, "format_cap_header: cap_list_len=%d\n",
+ cap_list_header->cap_list_len);
+ fprintf(stderr, "cur_cap_desc: num_blocks=%u, descriptor_type=0x%x, block_len=%u\n", ntohl(cap_desc->num_blocks), cap_desc->descriptor_type, cap_desc->block_len[0] << 16 | cap_desc->block_len[1] << 8 | cap_desc->block_len[2]);
+ }
+ buf_pos += sizeof(struct dvd_cur_cap_desc);
+
+ num_caps = cap_list_header->cap_list_len/sizeof(struct dvd_format_cap);
+ for (i = 0; i < num_caps; ++i) {
+ format_cap = (struct dvd_format_cap *) buf_pos;
+ if (verbose)
+ fprintf(stderr, "format_cap: num_blocks=%u, format_type=0x%x, type_param=%u\n", ntohl(format_cap->num_blocks), format_cap->format_type, format_cap->type_param[0] << 16 | format_cap->type_param[1] << 8 | format_cap->type_param[2]);
+ if (format_cap->format_type == 0x26)
+ cap_to_use = i;
+ buf_pos += sizeof(struct dvd_format_cap);
+ }
+ if (cap_to_use == -1)
+ err(EX_IOERR, "could not finde a format capacity with format type = 0x26.");
+
+ if (!force_format && cap_desc->descriptor_type == 2)
+ err(EX_IOERR, "the media in the drive is already formatted (use -F to override)");
+
+ memset(&format_params, 0, sizeof(struct dvd_format_params));
+ format_params.immed = 1;
+ format_params.desc_len = ntohs(sizeof(struct dvd_format_cap));
+ buf_pos = trans.buf + sizeof(struct dvd_format_cap_list_header) +
+ sizeof(struct dvd_cur_cap_desc) +
+ (cap_to_use * sizeof(struct dvd_format_cap));
+ memcpy(&format_params.desc, buf_pos, sizeof(struct dvd_format_cap));
+ if(ioctl(fd, DVDIOFORMATUNIT, &format_params) == -1)
+ err(EX_IOERR, "ioctl(DVDIOFORMATUNIT)");
+
+ started = 0;
+ while (1) {
+ sleep(1);
+ if (ioctl(fd, DVDIOFORMATPROGRESS, &percent) == -1)
+ err(EX_IOERR, "ioctl(DVDIOFORMATPROGRESS)");
+ if (percent > 0)
+ started = 1;
+ if (percent > 0 && !quiet)
+ fprintf(stderr, "formating DVD - %d %% done \r",
+ percent);
+ if (percent == 100 || (started && percent == 0))
+ break;
+ }
+
}
int
>Release-Note:
>Audit-Trail:
>Unformatted:
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200207102048.g6AKms7W000327>
