]> git.hungrycats.org Git - linux/commitdiff
[PATCH] DVD+RW support
authorPeter Osterlund <petero2@telia.com>
Tue, 19 Oct 2004 00:57:09 +0000 (17:57 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Tue, 19 Oct 2004 00:57:09 +0000 (17:57 -0700)
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.

  Nov 5 2001, Aug 8 2002. Modified by Andy Polyakov
  <appro@fy.chalmers.se> to support MMC-3 complaint DVD+RW units.

  Modified by Nigel Kukard <nkukard@lbsd.net> - support DVD+RW
  2.4.x patch by Andy Polyakov <appro@fy.chalmers.se>

This patch implements CDRW packet writing as a kernel block device.  Usage
instructions are in the packet-writing.txt file.

A hint: If you don't want to wait for a complete disc format, you can
format just a part of the disc.  For example:

        cdrwtool -d /dev/hdc -m 10240

This will format 10240 blocks, ie 20MB.

Signed-off-by: Peter Osterlund <petero2@telia.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
drivers/cdrom/cdrom.c
drivers/ide/ide-cd.c
drivers/scsi/sr.c
include/linux/cdrom.h

index e57d19031f8ea3d7b98e018b66fde36038d592c9..d815b8a5b1e8f38cab386d513df91f379dc7c8db 100644 (file)
@@ -848,6 +848,41 @@ static int cdrom_ram_open_write(struct cdrom_device_info *cdi)
        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
  */
@@ -889,10 +924,50 @@ static int cdrom_open_write(struct cdrom_device_info *cdi)
                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
@@ -925,6 +1000,7 @@ int cdrom_open(struct cdrom_device_info *cdi, struct inode *ip, struct file *fp)
                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))
@@ -932,6 +1008,7 @@ int cdrom_open(struct cdrom_device_info *cdi, struct inode *ip, struct file *fp)
                        if (!CDROM_CAN(CDC_RAM))
                                goto err;
                        ret = 0;
+                       cdi->media_written = 0;
                }
        }
 
@@ -1123,6 +1200,8 @@ int cdrom_release(struct cdrom_device_info *cdi, struct file *fp)
                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");
@@ -1329,6 +1408,7 @@ int media_changed(struct cdrom_device_info *cdi, int queue)
        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;
index cc6b66e34e37cfac5c6663d1b49070638a42cdfc..468eec62246f97c189d5c72bc8f8a19d3dcddb85 100644 (file)
@@ -1932,6 +1932,8 @@ static ide_startstop_t cdrom_start_write(ide_drive_t *drive, struct request *rq)
        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);
 }
index b6c76b5680a2045b2b5798c80cf2dca61bcad4a9..20a10a3ed5e4d1a3a12e64cdb004f81120996802 100644 (file)
@@ -377,6 +377,7 @@ static int sr_init_command(struct scsi_cmnd * SCpnt)
                        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;
index bcc9410761d90ea871178e000eb1af8b4b4b656b..73a1ade6a8e929d2c24cbf3911402805199aacfe 100644 (file)
@@ -947,6 +947,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;