diff -c --new-file --recursive linux-2.6.4_vanilla/drivers/cdrom/cdrom.c linux-2.6.4_dvd+rw/drivers/cdrom/cdrom.c *** linux-2.6.4_vanilla/drivers/cdrom/cdrom.c Thu Mar 11 04:55:22 2004 --- linux-2.6.4_dvd+rw/drivers/cdrom/cdrom.c Mon Mar 22 14:12:28 2004 *************** *** 766,771 **** --- 766,774 ---- /* if this was a O_NONBLOCK open and we should honor the flags, * do a quick open without drive/disc integrity checks. */ + /* But do it earlier so that media_change can (re)sense MMC3 + profile and tune mask accordingly. */ + check_disk_change(ip->i_bdev); cdi->use_count++; if ((fp->f_flags & O_NONBLOCK) && (cdi->options & CDO_USE_FFLAGS)) { ret = cdi->ops->open(cdi, 1); *************** *** 787,793 **** cdi->name, cdi->use_count); /* Do this on open. Don't wait for mount, because they might not be mounting, but opening with O_NONBLOCK */ - check_disk_change(ip->i_bdev); return 0; err: cdi->use_count--; --- 790,795 ---- diff -c --new-file --recursive linux-2.6.4_vanilla/drivers/scsi/sr.c linux-2.6.4_dvd+rw/drivers/scsi/sr.c *** linux-2.6.4_vanilla/drivers/scsi/sr.c Thu Mar 11 04:55:24 2004 --- linux-2.6.4_dvd+rw/drivers/scsi/sr.c Mon Mar 22 16:13:19 2004 *************** *** 30,35 **** --- 30,39 ---- * * Modified by Arnaldo Carvalho de Melo * check resource allocation in sr_init and some cleanups + * + * Modified by Nigel Kukard - support DVD+RW + * 2.4.x patch by Andy Polyakov + * */ #include *************** *** 149,157 **** /* If the disk changed, the capacity will now be different, * so we force a re-read of this information */ if (retval) { - /* check multisession offset etc */ - sr_cd_check(cdi); - /* * If the disk changed, the capacity will now be different, * so we force a re-read of this information --- 153,158 ---- *************** *** 161,170 **** --- 162,191 ---- */ cd->needs_sector_size = 1; cd->device->sector_size = 2048; + /* DVD+RW bookkeeping, mark clean */ + cd->media_written = 0; + + /* check multisession offset etc */ + sr_cd_check(cdi); } return retval; } + static void flush_intr(struct scsi_cmnd *SCpnt) + { + /* this is a temporary hack which suspends i/o if unexpected happens */ + if (driver_byte(SCpnt->result) != 0 && SCpnt->sense_buffer[0] & 0xF0) { + printk("sr%d: unable to \"SYNCHRONIZE CACHE\" [%02x/%02x]? " + "Suspending I/O...\n", + SCpnt->sense_buffer[2], + SCpnt->sense_buffer[12], + SCpnt->sense_buffer[13]); + SCpnt->device->changed = 1; + } + + scsi_release_request(SCpnt->sc_request); + } + static inline struct scsi_cd *scsi_cd(struct gendisk *disk) { return container_of(disk->private_data, struct scsi_cd, driver); *************** *** 251,256 **** --- 272,357 ---- } } + if (cd->mmc3_profile == 0x1A && + driver_byte(result) != 0 && + SCpnt->sense_buffer[0] & 0xF0) /* permit for vendor codes */ { + if (SCpnt->sense_buffer[2] == MEDIUM_ERROR && + SCpnt->sense_buffer[12] == 0x15 && + rq_data_dir(SCpnt->request) == WRITE) { + printk("%s: This one beats me! Suspending I/O...\n", + cd->cdi.name); + SCpnt->device->changed = 1; + } + else if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST && + SCpnt->sense_buffer[12] == 0x2c) do { + Scsi_Request *SRpnt; + + printk("%s: injecting \"SYNCHRONIZE CACHE\"\n", + cd->cdi.name); + + SRpnt = scsi_allocate_request(SCpnt->device,GFP_KERNEL); + if (SRpnt == NULL) { + printk ("%s: out of kernel memory :-(\n", + cd->cdi.name); + SCpnt->device->changed = 1; + break; /* mind do{}while(0) around */ + } + + SRpnt->sr_cmnd[0] = 0x35; + SRpnt->sr_cmd_len = 10; + SRpnt->sr_data_direction = SCSI_DATA_NONE; + SRpnt->sr_timeout_per_command = 30*HZ; + SRpnt->sr_done = flush_intr; + + #if 0 /* scsi_insert_special_req isn't exported :-( */ + scsi_insert_special_req(SRpnt,1); + #elseif 0 + /* + * As scsi_insert_special_req is not exported, I + * just copy corresponding code from scsi_lib.c. + * This is a TEMPORARY solution and the ONLY + * reason for doing this is that I don't want to + * patch scsi_syms.c just yet! Below is inlined + * call to __scsi_insert_special(q,rq,SRpnt,1) + * from 2.4.18. + */ + { + request_queue_t *q = &SRpnt->sr_device->request_queue; + struct request *rq = &SRpnt->sr_request; + unsigned long flags; + + rq->cmd = SPECIAL; + rq->special = SRpnt; + rq->q = NULL; + rq->nr_segments = 0; + rq->elevator_sequence = 0; + + spin_lock_irqsave(&io_request_lock, flags); + + list_add(&rq->queue, &q->queue_head); + + q->request_fn(q); + + spin_unlock_irqrestore(&io_request_lock, flags); + } + #else + SRpnt->sr_request->flags &= ~REQ_DONTPREP; + blk_insert_request(SRpnt->sr_device->request_queue, SRpnt->sr_request, + 1, SRpnt, 0); + #endif + /* + * fake "LOGICAL UNIT IS IN PROCESS OF BECOMING READY" + * for current command to fool scsi_io_completion(). + */ + SCpnt->sense_buffer[0] = 0xF0; + SCpnt->sense_buffer[2] &= 0xF0; + SCpnt->sense_buffer[2] |= NOT_READY; + SCpnt->sense_buffer[12] = 4; + SCpnt->sense_buffer[13] = 1; + good_bytes = 0; + } while (0); + } + /* * This calls the generic completion function, now that we know * how many actual sectors finished, and how many sectors we need *************** *** 295,301 **** --- 396,405 ---- if (!rq->data_len) SCpnt->sc_data_direction = SCSI_DATA_NONE; else if (rq_data_dir(rq) == WRITE) + { SCpnt->sc_data_direction = SCSI_DATA_WRITE; + cd->media_written = 1; + } else SCpnt->sc_data_direction = SCSI_DATA_READ; *************** *** 550,555 **** --- 654,661 ---- cd->use = 1; cd->readcd_known = 0; cd->readcd_cdda = 0; + cd->media_written = 0; + cd->close_track_sniffed = 0; cd->cdi.ops = &sr_dops; cd->cdi.handle = cd; diff -c --new-file --recursive linux-2.6.4_vanilla/drivers/scsi/sr.h linux-2.6.4_dvd+rw/drivers/scsi/sr.h *** linux-2.6.4_vanilla/drivers/scsi/sr.h Thu Mar 11 04:55:25 2004 --- linux-2.6.4_dvd+rw/drivers/scsi/sr.h Mon Mar 22 14:12:28 2004 *************** *** 12,17 **** --- 12,21 ---- * Modified by Eric Youngdale eric@andante.org to * add scatter-gather, multiple outstanding request, and other * enhancements. + * + * Modified by Nigel Kukard - support DVD+RW + * 2.4.x patch by Andy Polyakov + * */ #ifndef _SR_H *************** *** 24,29 **** --- 28,37 ---- /* In fact, it is very slow if it has to spin up first */ #define IOCTL_TIMEOUT 30*HZ + /* primitive to determine whether we need to have GFP_DMA set based on + * the status of the unchecked_isa_dma flag in the host structure */ + #define SR_GFP_DMA(cd) (((cd)->device->host->unchecked_isa_dma) ? GFP_DMA : 0) + typedef struct scsi_cd { struct scsi_driver *driver; unsigned capacity; /* size in blocks */ *************** *** 35,40 **** --- 43,51 ---- unsigned xa_flag:1; /* CD has XA sectors ? */ unsigned readcd_known:1; /* drive supports READ_CD (0xbe) */ unsigned readcd_cdda:1; /* reading audio data using READ_CD */ + unsigned media_written:1; /* dirty flag, DVD+RW bookkeeping */ + unsigned close_track_sniffed:1; /* dirty flag, DVD+RW bookkeeping */ + unsigned short mmc3_profile; /* current MMC3 profile, see sr_vendor.c */ struct cdrom_device_info cdi; struct gendisk *disk; } Scsi_CD; *************** *** 58,62 **** --- 69,75 ---- void sr_vendor_init(Scsi_CD *); int sr_cd_check(struct cdrom_device_info *); int sr_set_blocklength(Scsi_CD *, int blocklength); + /* anchor for DVD+RW "finalization" at unlock */ + void sr_vendor_lock_door (Scsi_CD *,int lock); #endif diff -c --new-file --recursive linux-2.6.4_vanilla/drivers/scsi/sr_ioctl.c linux-2.6.4_dvd+rw/drivers/scsi/sr_ioctl.c *** linux-2.6.4_vanilla/drivers/scsi/sr_ioctl.c Thu Mar 11 04:55:35 2004 --- linux-2.6.4_dvd+rw/drivers/scsi/sr_ioctl.c Mon Mar 22 14:12:28 2004 *************** *** 160,165 **** --- 160,172 ---- err = -EIO; } } + else if (cgc->cmd[0] == 0x5B) { + /* Even though this flag is commented as DVD+RW specific + * in sr.h, no check if currently mounted media is DVD+RW + * is performed. That's because the flag does good in + * in either case. */ + cd->close_track_sniffed = 1; + } if (cgc->sense) memcpy(cgc->sense, SRpnt->sr_sense_buffer, sizeof(*cgc->sense)); *************** *** 205,210 **** --- 212,218 ---- { Scsi_CD *cd = cdi->handle; + sr_vendor_lock_door(cd,lock); return scsi_set_medium_removal(cd->device, lock ? SCSI_REMOVAL_PREVENT : SCSI_REMOVAL_ALLOW); } *************** *** 262,271 **** return 0; } - /* primitive to determine whether we need to have GFP_DMA set based on - * the status of the unchecked_isa_dma flag in the host structure */ - #define SR_GFP_DMA(cd) (((cd)->device->host->unchecked_isa_dma) ? GFP_DMA : 0) - int sr_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn) { Scsi_CD *cd = cdi->handle; --- 270,275 ---- diff -c --new-file --recursive linux-2.6.4_vanilla/drivers/scsi/sr_vendor.c linux-2.6.4_dvd+rw/drivers/scsi/sr_vendor.c *** linux-2.6.4_vanilla/drivers/scsi/sr_vendor.c Thu Mar 11 04:55:27 2004 --- linux-2.6.4_dvd+rw/drivers/scsi/sr_vendor.c Mon Mar 22 19:04:12 2004 *************** *** 32,37 **** --- 32,41 ---- * - HP: Much like SONY, but a little different... (Thomas) * HP-Writers only ??? Maybe other CD-Writers work with this too ? * HP 6020 writers now supported. + * -------------------------------------------------------------------------- + * + * Modified by Nigel Kukard - support DVD+RW + * 2.4.x patch by Andy Polyakov */ #include *************** *** 61,79 **** #define VENDOR_TIMEOUT 30*HZ void sr_vendor_init(Scsi_CD *cd) { #ifndef CONFIG_BLK_DEV_SR_VENDOR cd->vendor = VENDOR_SCSI3; #else char *vendor = cd->device->vendor; char *model = cd->device->model; /* default */ cd->vendor = VENDOR_SCSI3; ! if (cd->readcd_known) /* this is true for scsi3/mmc drives - no more checks */ return; if (cd->device->type == TYPE_WORM) { cd->vendor = VENDOR_WRITER; --- 65,203 ---- #define VENDOR_TIMEOUT 30*HZ + static unsigned short sr_mmc3_profile(Scsi_CD *cd) + { + char *buffer = kmalloc(32, GFP_KERNEL | SR_GFP_DMA(cd)); + int rc, mmc3_profile; + struct cdrom_generic_command cgc; + + /* Read MMC-3 profile */ + memset(&cgc, 0, sizeof(struct cdrom_generic_command)); + cgc.cmd[0] = GPCMD_GET_CONFIGURATION; + cgc.cmd[1] = (cd->device->scsi_level <= SCSI_2) ? + (cd->device->lun << 5) : 0; + cgc.cmd[2] = cgc.cmd[3] = 0; /* Starting Feature Number */ + cgc.cmd[4] = cgc.cmd[5] = cgc.cmd [6] = 0; /* Reserved */ + cgc.cmd[7] = 0; cgc.cmd [8] = 8; /* Allocation Length */ + cgc.cmd[9] = 0; /* Control */ + cgc.buffer = buffer; + cgc.buflen = 8; + cgc.timeout = IOCTL_TIMEOUT; + cgc.data_direction = SCSI_DATA_READ; + + rc = sr_do_ioctl(cd, &cgc); + if (rc) { /* doesn't seem to support MMC3 profiles!!! */ + mmc3_profile = 0xFFFF; + } else { + mmc3_profile = (buffer[6] << 8) | buffer[7]; + switch (mmc3_profile) { + case 0x0A: /* CD-RW */ + cd->cdi.mask &= ~CDC_CD_RW; + cd->device->writeable = 1; + break; + case 0x12: /* DVD-RAM */ + case 0x1A: /* DVD+RW */ + cd->cdi.mask &= ~CDC_DVD_RAM; + cd->device->writeable = 1; + break; + case 0x1B: /* DVD+R */ + /* Needed for accessing of opened media */ + cgc.cmd[0] = GPCMD_READ_FORMAT_CAPACITIES; + cgc.cmd[8] = 12; + cgc.timeout = VENDOR_TIMEOUT; + + rc = sr_do_ioctl(cd, &cgc); + if (!rc) { + cd->capacity = ((buffer[4] << 24) | + (buffer[5] << 16) | + (buffer[6] << 8) | + buffer[7]) * 4; + cd->device->sector_size = 2048; + cd->disk->capacity = cd->disk->capacity >> (BLOCK_SIZE_BITS - 9); + cd->needs_sector_size = 0; + } + /* no break; */ + default: + cd->cdi.mask |= CDC_DVD_RAM; + cd->device->writeable = 0; + break; + } + } + cd->mmc3_profile = mmc3_profile; + + kfree(buffer); + return mmc3_profile; + } + + void sr_vendor_lock_door(Scsi_CD *cd, int lock) + { + struct cdrom_generic_command cgc; + + if (lock != 0) return; + + if ( (cd->media_written) && + (cd->mmc3_profile == 0x1A) ) { + printk ("%s: dirty DVD+RW media, \"finalizing\"\n", + cd->cdi.name); + + memset(&cgc, 0, sizeof(struct cdrom_generic_command)); + cgc.cmd[0] = GPCMD_FLUSH_CACHE; + cgc.timeout = IOCTL_TIMEOUT; + cgc.data_direction = SCSI_DATA_NONE; + sr_do_ioctl(cd, &cgc); + + memset(&cgc, 0, sizeof(struct cdrom_generic_command)); + cgc.cmd[0] = GPCMD_CLOSE_TRACK; + cgc.timeout = 3000*HZ; + cgc.data_direction = SCSI_DATA_NONE; + sr_do_ioctl(cd, &cgc); + + memset(&cgc, 0, sizeof(struct cdrom_generic_command)); + cgc.cmd[0] = GPCMD_CLOSE_TRACK; + cgc.cmd[2] = 2; /* Close session */ + cgc.timeout = 3000*HZ; + cgc.data_direction = SCSI_DATA_NONE; + sr_do_ioctl(cd, &cgc); + + /* bookkeeping, mark clean and changed */ + cd->media_written = 0; + cd->close_track_sniffed = 0; + cd->device->changed = 1; + } + else if (cd->close_track_sniffed) { + cd->close_track_sniffed = 0; + cd->device->changed = 1; + } + } + void sr_vendor_init(Scsi_CD *cd) { + int rc; #ifndef CONFIG_BLK_DEV_SR_VENDOR cd->vendor = VENDOR_SCSI3; + + /* check for MMC-3 Profile capability */ + rc = sr_mmc3_profile(cd); + if (rc != 0xFFFF) + printk ("%s: mmc-3 profile capable," + " current profile: %Xh\n", cd->cdi.name, rc); + return; #else char *vendor = cd->device->vendor; char *model = cd->device->model; /* default */ cd->vendor = VENDOR_SCSI3; ! if (cd->readcd_known) { ! /* this is true for scsi3/mmc drives */ ! /* check for MMC-3 Profile capability */ ! rc = sr_mmc3_profile(cd); ! if (rc != 0xFFFF) ! printk ("%s: mmc-3 profile capable," ! " current profile: %Xh\n", cd->cdi.name, rc); /* this is true for scsi3/mmc drives - no more checks */ return; + } if (cd->device->type == TYPE_WORM) { cd->vendor = VENDOR_WRITER; *************** *** 160,165 **** --- 284,300 ---- struct cdrom_generic_command cgc; int rc, no_multi; + + if ((cd->vendor == VENDOR_SCSI3) && + (cd->mmc3_profile != 0xFFFF)) { + rc = sr_mmc3_profile(cd); + if (rc != 0xFFFF) + printk ("%s: mmc-3 profile: %Xh\n", cd->cdi.name, rc); + else + printk ("%s: Failed to sense mmc-3 profile\n", + cd->cdi.name); + } + if (cd->cdi.mask & CDC_MULTI_SESSION) return 0; *************** *** 177,184 **** case VENDOR_SCSI3: cgc.cmd[0] = READ_TOC; cgc.cmd[8] = 12; - cgc.cmd[9] = 0x40; cgc.buffer = buffer; cgc.buflen = 12; cgc.quiet = 1; --- 312,319 ---- case VENDOR_SCSI3: cgc.cmd[0] = READ_TOC; + cgc.cmd[2] = 1; cgc.cmd[8] = 12; cgc.buffer = buffer; cgc.buflen = 12; cgc.quiet = 1; *************** *** 187,193 **** rc = sr_do_ioctl(cd, &cgc); if (rc != 0) break; ! if ((buffer[0] << 8) + buffer[1] < 0x0a) { printk(KERN_INFO "%s: Hmm, seems the drive " "doesn't support multisession CD's\n", cd->cdi.name); no_multi = 1; --- 322,328 ---- rc = sr_do_ioctl(cd, &cgc); if (rc != 0) break; ! if ((buffer[0] << 8) + buffer[1] < 8) { printk(KERN_INFO "%s: Hmm, seems the drive " "doesn't support multisession CD's\n", cd->cdi.name); no_multi = 1;