diff -ur --exclude-from /home/axboe/cdrom/exclude /opt/kernel/linux-2.4.0-test3-pre2/drivers/ide/ide-cd.c linux/drivers/ide/ide-cd.c --- /opt/kernel/linux-2.4.0-test3-pre2/drivers/ide/ide-cd.c Thu Jun 22 07:31:01 2000 +++ linux/drivers/ide/ide-cd.c Wed Jul 5 21:53:47 2000 @@ -285,9 +285,11 @@ * 4.58 May 1, 2000 - Clean up ACER50 stuff. * - Fix small problem with ide_cdrom_capacity * + * 4.59 Jul 5, 2000 - Added write support for DVD-RAM drives + * *************************************************************************/ -#define IDECD_VERSION "4.58" +#define IDECD_VERSION "4.59" #include #include @@ -360,6 +362,15 @@ return; } + /* + * If a read toc is executed for a CD-R or CD-RW medium where + * the first toc has not been recorded yet, it will fail with + * 05/24/00 + */ + if (failed_command && failed_command->c[0] == GPCMD_READ_TOC_PMA_ATIP) + if (sense->sense_key == 0x05 && sense->asc == 0x24) + return; + #if VERBOSE_IDE_CD_ERRORS { int i; @@ -524,8 +535,10 @@ (struct packet_command *) pc->sense, (struct request_sense *) (pc->buffer - pc->c[4])); } - if (rq->cmd == READ && !rq->current_nr_sectors) - uptodate = 1; + + if (rq->cmd == READ || rq->cmd == WRITE) + if (!rq->current_nr_sectors) + uptodate = 1; ide_end_request (uptodate, HWGROUP(drive)); } @@ -609,7 +622,7 @@ cdrom_queue_request_sense(drive, sem, pc->sense, pc); } else { - /* Handle errors from READ requests. */ + /* Handle errors from READ and WRITE requests. */ if (sense_key == NOT_READY) { /* Tray open. */ @@ -661,11 +674,22 @@ struct packet_command *pc = (struct packet_command *) rq->buffer; unsigned long wait = 0; - /* blank and format can take an extremly long time to - * complete, if the IMMED bit was not set. + /* + * Some commands are *slow* and normally take a long time to + * complete. Usually we can use the ATAPI "disconnect" to bypass + * this, but not all commands/drives support that. Let + * ide_timer_expiry keep polling us for these. */ - if (pc->c[0] == GPCMD_BLANK || pc->c[0] == GPCMD_FORMAT_UNIT) - wait = 60*60*HZ; + switch (pc->c[0]) { + case GPCMD_BLANK: + case GPCMD_FORMAT_UNIT: + case GPCMD_RESERVE_RZONE_TRACK: + wait = WAIT_CMD; + break; + default: + wait = 0; + break; + } return wait; } @@ -677,8 +701,10 @@ called when the interrupt from the drive arrives. Otherwise, HANDLER will be called immediately after the drive is prepared for the transfer. */ -static ide_startstop_t cdrom_start_packet_command (ide_drive_t *drive, int xferlen, - ide_handler_t *handler) +static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive, + int xferlen, + ide_handler_t *handler, + int cmd) { ide_startstop_t startstop; struct cdrom_info *info = drive->driver_data; @@ -687,8 +713,15 @@ if (ide_wait_stat(&startstop, drive, 0, BUSY_STAT, WAIT_READY)) return startstop; - if (info->dma) - info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive); + if (info->dma) { + if (cmd == READ) { + info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive); + } else if (cmd == WRITE) { + info->dma = !HWIF(drive)->dmaproc(ide_dma_write, drive); + } else { + printk("ide-cd: DMA set, but not allowed\n"); + } + } /* Set up the controller registers. */ OUT_BYTE (info->dma, IDE_FEATURE_REG); @@ -718,9 +751,10 @@ by cdrom_start_packet_command. HANDLER is the interrupt handler to call when the command completes or there's data ready. */ -static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive, +static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive, unsigned char *cmd_buf, int cmd_len, - ide_handler_t *handler) + ide_handler_t *handler, + unsigned timeout) { if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { /* Here we should have been called after receiving an interrupt @@ -739,10 +773,10 @@ } /* Arm the interrupt handler. */ - ide_set_handler (drive, handler, WAIT_CMD, cdrom_timer_expiry); + ide_set_handler(drive, handler, timeout, cdrom_timer_expiry); /* Send the command to the device. */ - atapi_output_bytes (drive, cmd_buf, cmd_len); + atapi_output_bytes(drive, cmd_buf, cmd_len); return ide_started; } @@ -834,8 +868,8 @@ drive->name, ireason); } - cdrom_end_request (0, drive); - return -1; + cdrom_end_request(0, drive); + return 1; } /* @@ -1076,7 +1110,7 @@ (65534 / CD_FRAMESIZE) : 65535); /* Set up the command */ - memset (&pc.c, 0, sizeof (pc.c)); + memset(&pc.c, 0, sizeof (pc.c)); pc.c[0] = GPCMD_READ_10; pc.c[7] = (nframes >> 8); pc.c[8] = (nframes & 0xff); @@ -1084,7 +1118,7 @@ /* Send the command to the drive and return. */ return cdrom_transfer_packet_command(drive, pc.c, sizeof(pc.c), - &cdrom_read_intr); + &cdrom_read_intr, WAIT_CMD); } @@ -1127,7 +1161,8 @@ memset (&pc.c, 0, sizeof (pc.c)); pc.c[0] = GPCMD_SEEK; put_unaligned(cpu_to_be32(frame), (unsigned int *) &pc.c[2]); - return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr); + return cdrom_transfer_packet_command(drive, pc.c, sizeof(pc.c), + &cdrom_seek_intr, WAIT_CMD); } static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block) @@ -1136,7 +1171,7 @@ info->dma = 0; info->start_seek = jiffies; - return cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation); + return cdrom_start_packet_command(drive, 0, cdrom_start_seek_continuation, 0); } /* Fix up a possibly partially-processed request so that we can @@ -1188,7 +1223,7 @@ info->dma = 0; /* Start sending the read request to the drive. */ - return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation); + return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation, READ); } /**************************************************************************** @@ -1303,8 +1338,8 @@ struct packet_command *pc = (struct packet_command *)rq->buffer; /* Send the command to the drive and return. */ - return cdrom_transfer_packet_command (drive, pc->c, - sizeof (pc->c), &cdrom_pc_intr); + return cdrom_transfer_packet_command(drive, pc->c, sizeof(pc->c), + &cdrom_pc_intr, WAIT_CMD); } @@ -1320,7 +1355,7 @@ len = pc->buflen; /* Start sending the command to the drive. */ - return cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation); + return cdrom_start_packet_command(drive, len, cdrom_do_pc_continuation, 0); } @@ -1381,16 +1416,173 @@ return pc->stat ? -EIO : 0; } +/* + * Write handling + */ +static inline int cdrom_write_check_ireason(ide_drive_t *drive, int len, + int ireason) +{ + /* Two notes about IDE interrupt reason here - 0 means that + * the drive wants to receive data from us, 2 means that + * the drive is expecting data from us. + */ + ireason &= 3; + + if (ireason == 2) { + /* Whoops... The drive wants to send data. */ + printk("%s: cdrom_write_intr: wrong transfer direction!\n", + drive->name); + + /* Throw some data at the drive so it doesn't hang + and quit this request. */ + while (len > 0) { + int dum = 0; + atapi_output_bytes(drive, &dum, sizeof(dum)); + len -= sizeof(dum); + } + } else { + /* Drive wants a command packet, or invalid ireason... */ + printk("%s: cdrom_write_intr: bad interrupt reason %d\n", + drive->name, ireason); + } + + cdrom_end_request(0, drive); + return 1; +} + +static ide_startstop_t cdrom_write_intr(ide_drive_t *drive) +{ + int stat, ireason, len, sectors_to_transfer; + struct cdrom_info *info = drive->driver_data; + int i, dma_error = 0, dma = info->dma; + ide_startstop_t startstop; + + struct request *rq = HWGROUP(drive)->rq; + + /* Check for errors. */ + if (dma) { + info->dma = 0; + if ((dma_error = HWIF(drive)->dmaproc(ide_dma_end, drive))) { + printk("ide-cd: write dma error\n"); + HWIF(drive)->dmaproc(ide_dma_off, drive); + } + } + + if (cdrom_decode_status(&startstop, drive, 0, &stat)) { + printk("ide-cd: write_intr decode_status bad\n"); + return startstop; + } + + if (dma) { + if (dma_error) + return ide_error(drive, "dma error", stat); + + rq = HWGROUP(drive)->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, HWGROUP(drive)); + } + return ide_stopped; + } + + /* Read the interrupt reason and the transfer length. */ + ireason = IN_BYTE(IDE_NSECTOR_REG); + len = IN_BYTE(IDE_LCYL_REG) + 256 * IN_BYTE(IDE_HCYL_REG); + + /* If DRQ is clear, the command has completed. */ + if ((stat & DRQ_STAT) == 0) { + /* If we're not done writing, complain. + * Otherwise, complete the command normally. + */ + if (rq->current_nr_sectors > 0) { + printk("%s: write_intr: data underrun (%ld blocks)\n", + drive->name, rq->current_nr_sectors); + cdrom_end_request(0, drive); + } else + cdrom_end_request(1, drive); + return ide_stopped; + } + + /* Check that the drive is expecting to do the same thing we are. */ + if (ireason & 3) + if (cdrom_write_check_ireason(drive, len, ireason)) + return ide_stopped; + + /* The number of sectors we need to read from the drive. */ + sectors_to_transfer = len / SECTOR_SIZE; + + /* Now loop while we still have data to read from the drive. DMA + * transfers will already have been complete + */ + while (sectors_to_transfer > 0) { + /* If we've filled the present buffer but there's another + chained buffer after it, move on. */ + if (rq->current_nr_sectors == 0 && rq->nr_sectors > 0) + cdrom_end_request(1, drive); + + atapi_output_bytes(drive, rq->buffer, rq->current_nr_sectors); + rq->nr_sectors -= rq->current_nr_sectors; + rq->current_nr_sectors = 0; + rq->sector += rq->current_nr_sectors; + sectors_to_transfer -= rq->current_nr_sectors; + } + + /* arm handler */ + ide_set_handler(drive, &cdrom_write_intr, 5 * WAIT_CMD, NULL); + return ide_started; +} + +static ide_startstop_t cdrom_start_write_cont(ide_drive_t *drive) +{ + struct packet_command pc; + struct request *rq = HWGROUP(drive)->rq; + unsigned nframes, frame; + + nframes = rq->nr_sectors >> 2; + frame = rq->sector >> 2; + + memset(&pc.c, 0, sizeof(pc.c)); + /* + * we might as well use WRITE_12, but none of the device I have + * support the streaming feature anyway, so who cares. + */ + pc.c[0] = GPCMD_WRITE_10; +#if 0 + pc.c[1] = 1 << 3; +#endif + pc.c[7] = (nframes >> 8) & 0xff; + pc.c[8] = nframes & 0xff; + put_unaligned(cpu_to_be32(frame), (unsigned int *)&pc.c[2]); + + return cdrom_transfer_packet_command(drive, pc.c, sizeof(pc.c), + cdrom_write_intr, 2 * WAIT_CMD); +} + +static ide_startstop_t cdrom_start_write(ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + + info->nsectors_buffered = 0; + + /* use dma, if possible. we don't need to check more, since we + * know that the transfer is always (at least!) 2KB aligned */ + info->dma = drive->using_dma ? 1 : 0; + + /* Start sending the read request to the drive. */ + return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont, WRITE); +} + /**************************************************************************** * cdrom driver request routine. */ static ide_startstop_t -ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block) +ide_do_rw_cdrom(ide_drive_t *drive, struct request *rq, unsigned long block) { ide_startstop_t action; struct cdrom_info *info = drive->driver_data; switch (rq->cmd) { + case WRITE: case READ: { if (CDROM_CONFIG_FLAGS(drive)->seeking) { unsigned long elpased = jiffies - info->start_seek; @@ -1407,8 +1599,12 @@ } if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) action = cdrom_start_seek (drive, block); - else - action = cdrom_start_read (drive, block); + else { + if (rq->cmd == READ) + action = cdrom_start_read(drive, block); + else + action = cdrom_start_write(drive); + } info->last_block = block; return action; } @@ -1422,9 +1618,8 @@ cdrom_end_request(1, drive); return ide_do_reset(drive); } - default: { - printk("ide-cd: bad cmd %d\n", rq -> cmd); + printk("ide-cd: bad cmd %d\n", rq->cmd); cdrom_end_request(0, drive); return ide_stopped; } @@ -1768,6 +1963,9 @@ if (stat) toc->capacity = 0x1fffff; + HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS] = (toc->capacity * SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9); + drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME; + /* Remember that we've read this stuff. */ CDROM_STATE_FLAGS (drive)->toc_valid = 1; @@ -1813,8 +2011,9 @@ pc.c[2] = (speed >> 8) & 0xff; /* Read Drive speed in kbytes/second LSB */ pc.c[3] = speed & 0xff; - if ( CDROM_CONFIG_FLAGS(drive)->cd_r || - CDROM_CONFIG_FLAGS(drive)->cd_rw ) { + if (CDROM_CONFIG_FLAGS(drive)->cd_r || + CDROM_CONFIG_FLAGS(drive)->cd_rw || + CDROM_CONFIG_FLAGS(drive)->dvd_r) { /* Write Drive speed in kbytes/second MSB */ pc.c[4] = (speed >> 8) & 0xff; /* Write Drive speed in kbytes/second LSB */ @@ -1846,10 +2045,6 @@ return 0; } - - - - /* the generic packet interface to cdrom.c */ static int ide_cdrom_packet(struct cdrom_device_info *cdi, struct cdrom_generic_command *cgc) @@ -2066,11 +2261,11 @@ struct request_sense sense; int ret; - toc = info->toc; - if (!CDROM_STATE_FLAGS(drive)->toc_valid || toc == NULL) + if (!CDROM_STATE_FLAGS(drive)->toc_valid || info->toc == NULL) if ((ret = cdrom_read_toc(drive, &sense))) return ret; + toc = info->toc; ms_info->addr.lba = toc->last_session_lba; ms_info->xa_flag = toc->xa_flag; @@ -2120,51 +2315,44 @@ } } - -static -int ide_cdrom_open_real (struct cdrom_device_info *cdi, int purpose) +static int ide_cdrom_open_real(struct cdrom_device_info *cdi, int purpose) { return 0; } - /* * Close down the device. Invalidate all cached blocks. */ - -static -void ide_cdrom_release_real (struct cdrom_device_info *cdi) +static void ide_cdrom_release_real(struct cdrom_device_info *cdi) { } - - /**************************************************************************** * Device initialization. */ -static -struct cdrom_device_ops ide_cdrom_dops = { - ide_cdrom_open_real, /* open */ - ide_cdrom_release_real, /* release */ - ide_cdrom_drive_status, /* drive_status */ - ide_cdrom_check_media_change_real, /* media_changed */ - ide_cdrom_tray_move, /* tray_move */ - ide_cdrom_lock_door, /* lock_door */ - ide_cdrom_select_speed, /* select_speed */ - NULL, /* select_disc */ - ide_cdrom_get_last_session, /* get_last_session */ - ide_cdrom_get_mcn, /* get_mcn */ - ide_cdrom_reset, /* reset */ - ide_cdrom_audio_ioctl, /* audio_ioctl */ - ide_cdrom_dev_ioctl, /* dev_ioctl */ - CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED - | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN - | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_IOCTLS - | CDC_DRIVE_STATUS | CDC_CD_R | CDC_CD_RW | CDC_DVD - | CDC_DVD_R| CDC_DVD_RAM | CDC_GENERIC_PACKET, /* capability */ - 0, /* n_minors */ - ide_cdrom_packet +static struct cdrom_device_ops ide_cdrom_dops = { + open: ide_cdrom_open_real, + release: ide_cdrom_release_real, + drive_status: ide_cdrom_drive_status, + media_changed: ide_cdrom_check_media_change_real, + tray_move: ide_cdrom_tray_move, + lock_door: ide_cdrom_lock_door, + select_speed: ide_cdrom_select_speed, + get_last_session: ide_cdrom_get_last_session, + get_mcn: ide_cdrom_get_mcn, + reset: ide_cdrom_reset, + audio_ioctl: ide_cdrom_audio_ioctl, + dev_ioctl: ide_cdrom_dev_ioctl, + capability: CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | + CDC_SELECT_SPEED | CDC_SELECT_DISC | + CDC_MULTI_SESSION | CDC_MCN | + CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | + CDC_IOCTLS | CDC_DRIVE_STATUS | CDC_CD_R | + CDC_CD_RW | CDC_DVD | CDC_DVD_R| CDC_DVD_RAM | + CDC_GENERIC_PACKET, + n_minors: 0, + generic_packet: ide_cdrom_packet }; static int ide_cdrom_register (ide_drive_t *drive, int nslots) @@ -2272,9 +2460,9 @@ if (cap.dvd_ram_read || cap.dvd_r_read || cap.dvd_rom) CDROM_CONFIG_FLAGS (drive)->dvd = 1; if (cap.dvd_ram_write) - CDROM_CONFIG_FLAGS (drive)->dvd_r = 1; - if (cap.dvd_r_write) CDROM_CONFIG_FLAGS (drive)->dvd_ram = 1; + if (cap.dvd_r_write) + CDROM_CONFIG_FLAGS (drive)->dvd_r = 1; if (cap.audio_play) CDROM_CONFIG_FLAGS (drive)->audio_play = 1; if (cap.mechtype == 0) @@ -2316,15 +2504,11 @@ printk(" %dX", CDROM_CONFIG_FLAGS(drive)->max_speed); printk(" %s", CDROM_CONFIG_FLAGS(drive)->dvd ? "DVD-ROM" : "CD-ROM"); - if (CDROM_CONFIG_FLAGS (drive)->dvd_r|CDROM_CONFIG_FLAGS (drive)->dvd_ram) - printk (" DVD%s%s", - (CDROM_CONFIG_FLAGS (drive)->dvd_r)? "-R" : "", - (CDROM_CONFIG_FLAGS (drive)->dvd_ram)? "AM" : ""); - - if (CDROM_CONFIG_FLAGS (drive)->cd_r|CDROM_CONFIG_FLAGS (drive)->cd_rw) - printk (" CD%s%s", - (CDROM_CONFIG_FLAGS (drive)->cd_r)? "-R" : "", - (CDROM_CONFIG_FLAGS (drive)->cd_rw)? "/RW" : ""); + if (CDROM_CONFIG_FLAGS(drive)->dvd_r || CDROM_CONFIG_FLAGS(drive)->dvd_ram) + printk (" DVD-R%s", (CDROM_CONFIG_FLAGS (drive)->dvd_ram)? "AM" : ""); + + if (CDROM_CONFIG_FLAGS(drive)->cd_r ||CDROM_CONFIG_FLAGS(drive)->cd_rw) + printk (" CD-R%s", (CDROM_CONFIG_FLAGS (drive)->cd_rw)? "/RW" : ""); if (CDROM_CONFIG_FLAGS (drive)->is_changer) printk (" changer w/%d slots", nslots); @@ -2361,7 +2545,11 @@ int minor = drive->select.b.unit << PARTN_BITS; int nslots; - set_device_ro(MKDEV(HWIF(drive)->major, minor), 1); + if (CDROM_CONFIG_FLAGS(drive)->dvd_ram) + set_device_ro(MKDEV(HWIF(drive)->major, minor), 0); + else + set_device_ro(MKDEV(HWIF(drive)->major, minor), 1); + set_blocksize(MKDEV(HWIF(drive)->major, minor), CD_FRAMESIZE); drive->special.all = 0; @@ -2507,12 +2695,11 @@ MOD_INC_USE_COUNT; if (info->buffer == NULL) info->buffer = (char *) kmalloc(SECTOR_BUFFER_SIZE, GFP_KERNEL); - rc = cdrom_fops.open (ip, fp); - if (rc) { + if ((rc = cdrom_fops.open(ip, fp))) { drive->usage--; MOD_DEC_USE_COUNT; } - return rc; + return 0; } static @@ -2567,7 +2754,7 @@ struct cdrom_info *info = drive->driver_data; struct cdrom_device_info *devinfo = &info->devinfo; - if (ide_unregister_subdriver (drive)) + if (ide_unregister_subdriver(drive)) return 1; if (info->buffer != NULL) kfree(info->buffer); @@ -2575,7 +2762,7 @@ kfree(info->toc); if (info->changer_info != NULL) kfree(info->changer_info); - if (devinfo->handle == drive && unregister_cdrom (devinfo)) + if (devinfo->handle == drive && unregister_cdrom(devinfo)) printk ("%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name); kfree(info); drive->driver_data = NULL; @@ -2618,7 +2805,7 @@ MODULE_PARM(ignore, "s"); MODULE_DESCRIPTION("ATAPI CD-ROM Driver"); -void __exit ide_cdrom_exit(void) +static void __exit ide_cdrom_exit(void) { ide_drive_t *drive; int failed = 0; @@ -2661,7 +2848,7 @@ kfree (info); continue; } - memset (info, 0, sizeof (struct cdrom_info)); + memset(info, 0, sizeof (struct cdrom_info)); drive->driver_data = info; DRIVER(drive)->busy++; if (ide_cdrom_setup (drive)) {