Skip site navigation (1)Skip section navigation (2)
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>