From: Peter Osterlund <petero2@telia.com>

This patch adds support for using DVD+RW drives as writable block devices.

The patch is based on work from:

        Andy Polyakov <appro@fy.chalmers.se> - Wrote the 2.4 patch
        Nigel Kukard <nkukard@lbsd.net> - Initial porting to 2.6.x

It works for me using an Iomega Super DVD 8x USB drive.

Signed-off-by: Peter Osterlund <petero2@telia.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/cdrom/cdrom.c |   80 ++++++++++++++++++++++++++++++++++++++++++
 25-akpm/drivers/ide/ide-cd.c  |    2 +
 25-akpm/drivers/scsi/sr.c     |    1 
 25-akpm/include/linux/cdrom.h |    2 +
 4 files changed, 85 insertions(+)

diff -puN drivers/cdrom/cdrom.c~dvdrw-support-for-267-bk13 drivers/cdrom/cdrom.c
--- 25/drivers/cdrom/cdrom.c~dvdrw-support-for-267-bk13	2004-08-15 22:47:32.736869024 -0700
+++ 25-akpm/drivers/cdrom/cdrom.c	2004-08-15 22:47:32.753866440 -0700
@@ -821,6 +821,41 @@ static int cdrom_ram_open_write(struct c
 	return ret;
 }
 
+static void cdrom_mmc3_profile(struct cdrom_device_info *cdi)
+{
+	struct packet_command cgc;
+	char buffer[32];
+	int ret, mmc3_profile;
+
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
+
+	cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
+	cgc.cmd[1] = 0;
+	cgc.cmd[2] = cgc.cmd[3] = 0;		/* Starting Feature Number */
+	cgc.cmd[7] = 0; cgc.cmd [8] = 8;	/* Allocation Length */
+	cgc.quiet = 1;
+
+	if ((ret = cdi->ops->generic_packet(cdi, &cgc))) {
+		mmc3_profile = 0xffff;
+	} else {
+		mmc3_profile = (buffer[6] << 8) | buffer[7];
+		printk(KERN_INFO "cdrom: %s: mmc-3 profile capable, current profile: %Xh\n",
+		       cdi->name, mmc3_profile);
+	}
+	cdi->mmc3_profile = mmc3_profile;
+}
+
+static int cdrom_is_dvd_rw(struct cdrom_device_info *cdi)
+{
+	switch (cdi->mmc3_profile) {
+	case 0x12:	/* DVD-RAM	*/
+	case 0x1A:	/* DVD+RW	*/
+		return 0;
+	default:
+		return 1;
+	}
+}
+
 /*
  * returns 0 for ok to open write, non-0 to disallow
  */
@@ -862,10 +897,50 @@ static int cdrom_open_write(struct cdrom
  		ret = cdrom_ram_open_write(cdi);
 	else if (CDROM_CAN(CDC_MO_DRIVE))
 		ret = mo_open_write(cdi);
+	else if (!cdrom_is_dvd_rw(cdi))
+		ret = 0;
 
 	return ret;
 }
 
+static void cdrom_dvd_rw_close_write(struct cdrom_device_info *cdi)
+{
+	struct packet_command cgc;
+
+	if (cdi->mmc3_profile != 0x1a) {
+		cdinfo(CD_CLOSE, "%s: No DVD+RW\n", cdi->name);
+		return;
+	}
+
+	if (!cdi->media_written) {
+		cdinfo(CD_CLOSE, "%s: DVD+RW media clean\n", cdi->name);
+		return;
+	}
+
+	printk(KERN_INFO "cdrom: %s: dirty DVD+RW media, \"finalizing\"\n",
+	       cdi->name);
+
+	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+	cgc.cmd[0] = GPCMD_FLUSH_CACHE;
+	cgc.timeout = 30*HZ;
+	cdi->ops->generic_packet(cdi, &cgc);
+
+	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+	cgc.cmd[0] = GPCMD_CLOSE_TRACK;
+	cgc.timeout = 3000*HZ;
+	cgc.quiet = 1;
+	cdi->ops->generic_packet(cdi, &cgc);
+
+	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+	cgc.cmd[0] = GPCMD_CLOSE_TRACK;
+	cgc.cmd[2] = 2;	 /* Close session */
+	cgc.quiet = 1;
+	cgc.timeout = 3000*HZ;
+	cdi->ops->generic_packet(cdi, &cgc);
+
+	cdi->media_written = 0;
+}
+
 static int cdrom_close_write(struct cdrom_device_info *cdi)
 {
 #if 0
@@ -898,6 +973,7 @@ int cdrom_open(struct cdrom_device_info 
 		ret = open_for_data(cdi);
 		if (ret)
 			goto err;
+		cdrom_mmc3_profile(cdi);
 		if (fp->f_mode & FMODE_WRITE) {
 			ret = -EROFS;
 			if (cdrom_open_write(cdi))
@@ -905,6 +981,7 @@ int cdrom_open(struct cdrom_device_info 
 			if (!CDROM_CAN(CDC_RAM))
 				goto err;
 			ret = 0;
+			cdi->media_written = 0;
 		}
 	}
 
@@ -1096,6 +1173,8 @@ int cdrom_release(struct cdrom_device_in
 		cdi->use_count--;
 	if (cdi->use_count == 0)
 		cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name);
+	if (cdi->use_count == 0)
+		cdrom_dvd_rw_close_write(cdi);
 	if (cdi->use_count == 0 &&
 	    (cdo->capability & CDC_LOCK) && !keeplocked) {
 		cdinfo(CD_CLOSE, "Unlocking door!\n");
@@ -1302,6 +1381,7 @@ int media_changed(struct cdrom_device_in
 	if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) {
 		cdi->mc_flags = 0x3;    /* set bit on both queues */
 		ret |= 1;
+		cdi->media_written = 0;
 	}
 	cdi->mc_flags &= ~mask;         /* clear bit */
 	return ret;
diff -puN drivers/ide/ide-cd.c~dvdrw-support-for-267-bk13 drivers/ide/ide-cd.c
--- 25/drivers/ide/ide-cd.c~dvdrw-support-for-267-bk13	2004-08-15 22:47:32.738868720 -0700
+++ 25-akpm/drivers/ide/ide-cd.c	2004-08-15 22:47:32.761865224 -0700
@@ -1932,6 +1932,8 @@ static ide_startstop_t cdrom_start_write
 	info->dma = drive->using_dma ? 1 : 0;
 	info->cmd = WRITE;
 
+	info->devinfo.media_written = 1;
+
 	/* Start sending the write request to the drive. */
 	return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont);
 }
diff -puN drivers/scsi/sr.c~dvdrw-support-for-267-bk13 drivers/scsi/sr.c
--- 25/drivers/scsi/sr.c~dvdrw-support-for-267-bk13	2004-08-15 22:47:32.740868416 -0700
+++ 25-akpm/drivers/scsi/sr.c	2004-08-15 22:47:32.762865072 -0700
@@ -379,6 +379,7 @@ static int sr_init_command(struct scsi_c
 			return 0;
 		SCpnt->cmnd[0] = WRITE_10;
 		SCpnt->sc_data_direction = DMA_TO_DEVICE;
+ 	 	cd->cdi.media_written = 1;
 	} else if (rq_data_dir(SCpnt->request) == READ) {
 		SCpnt->cmnd[0] = READ_10;
 		SCpnt->sc_data_direction = DMA_FROM_DEVICE;
diff -puN include/linux/cdrom.h~dvdrw-support-for-267-bk13 include/linux/cdrom.h
--- 25/include/linux/cdrom.h~dvdrw-support-for-267-bk13	2004-08-15 22:47:32.741868264 -0700
+++ 25-akpm/include/linux/cdrom.h	2004-08-15 22:47:32.763864920 -0700
@@ -946,6 +946,8 @@ struct cdrom_device_info {
         __u8 reserved		: 6;	/* not used yet */
 	int cdda_method;		/* see flags */
 	__u8 last_sense;
+	__u8 media_written;		/* dirty flag, DVD+RW bookkeeping */
+	unsigned short mmc3_profile;	/* current MMC3 profile */
 	int for_data;
 	int (*exit)(struct cdrom_device_info *);
 	int mrw_mode_page;
_