diff options
Diffstat (limited to 'drivers/ide/ide-cd.c')
-rw-r--r-- | drivers/ide/ide-cd.c | 268 |
1 files changed, 236 insertions, 32 deletions
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 1ec89b3e0..3a6a9e8d9 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -513,9 +513,9 @@ static void cdrom_queue_request_sense(ide_drive_t *drive, struct request_sense *sense, struct packet_command *failed_command) { - struct cdrom_info *info = drive->driver_data; + struct cdrom_info *info = drive->driver_data; + struct packet_command *pc = &info->request_sense_pc; struct request *rq; - struct packet_command *pc = &info->request_sense_pc; if (sense == NULL) sense = &info->sense_data; @@ -541,13 +541,14 @@ static void cdrom_end_request (int uptodate, ide_drive_t *drive) struct request *rq = HWGROUP(drive)->rq; if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate) { - struct packet_command *pc = (struct packet_command *)rq->buffer; + struct packet_command *pc = (struct packet_command *) rq->buffer; cdrom_analyze_sense_data(drive, (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)); } @@ -628,7 +629,7 @@ static int cdrom_decode_status (ide_startstop_t *startstop, ide_drive_t *drive, if ((stat & ERR_STAT) != 0) 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. */ @@ -679,12 +680,22 @@ static int cdrom_timer_expiry(ide_drive_t *drive) 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; } @@ -706,8 +717,15 @@ static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive, 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 (info->cmd == READ) { + info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive); + } else if (info->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); @@ -737,11 +755,20 @@ static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive, by cdrom_start_packet_command. HANDLER is the interrupt handler to call when the command completes or there's data ready. */ +/* + * changed 5 parameters to 3 for dvd-ram + * struct packet_command *pc; now packet_command_t *pc; + */ +#undef CLASSIC_PACKET_STRUCT static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive, - unsigned char *cmd_buf, int cmd_len, - ide_handler_t *handler, - unsigned int timeout) + struct packet_command *pc, + ide_handler_t *handler) { +#ifdef CLASSIC_PACKET_STRUCT + unsigned char *cmd_buf = pc->c; + int cmd_len = sizeof(pc->c); + unsigned int timeout = pc->timeout; +#endif ide_startstop_t startstop; if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { @@ -759,16 +786,25 @@ static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive, } /* Arm the interrupt handler. */ +#ifdef CLASSIC_PACKET_STRUCT + /* Arm the interrupt handler. */ ide_set_handler (drive, handler, timeout, cdrom_timer_expiry); /* Send the command to the device. */ atapi_output_bytes (drive, cmd_buf, cmd_len); +#else /* !CLASSIC_PACKET_STRUCT */ + /* Arm the interrupt handler. */ +// ide_set_handler (drive, handler, (unsigned int) pc->timeout, cdrom_timer_expiry); + ide_set_handler (drive, handler, pc->timeout, cdrom_timer_expiry); + + /* Send the command to the device. */ +// atapi_output_bytes (drive, (void *)pc->c, (unsigned int) sizeof(pc->c)); + atapi_output_bytes (drive, pc->c, sizeof(pc->c)); +#endif /* CLASSIC_PACKET_STRUCT */ return ide_started; } - - /**************************************************************************** * Block read functions. */ @@ -1101,10 +1137,10 @@ static ide_startstop_t cdrom_start_read_continuation (ide_drive_t *drive) pc.c[7] = (nframes >> 8); pc.c[8] = (nframes & 0xff); put_unaligned(cpu_to_be32(frame), (unsigned int *) &pc.c[2]); + pc.timeout = WAIT_CMD; /* Send the command to the drive and return. */ - return cdrom_transfer_packet_command(drive, pc.c, sizeof(pc.c), - &cdrom_read_intr, WAIT_CMD); + return cdrom_transfer_packet_command(drive, &pc, &cdrom_read_intr); } @@ -1153,7 +1189,9 @@ static ide_startstop_t cdrom_start_seek_continuation (ide_drive_t *drive) 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, WAIT_CMD); + + pc.timeout = WAIT_CMD; + return cdrom_transfer_packet_command(drive, &pc, &cdrom_seek_intr); } static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block) @@ -1161,6 +1199,7 @@ static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block) struct cdrom_info *info = drive->driver_data; info->dma = 0; + info->cmd = 0; info->start_seek = jiffies; return cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation); } @@ -1213,6 +1252,7 @@ static ide_startstop_t cdrom_start_read (ide_drive_t *drive, unsigned int block) else info->dma = 0; + info->cmd = READ; /* Start sending the read request to the drive. */ return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation); } @@ -1332,8 +1372,7 @@ static ide_startstop_t cdrom_do_pc_continuation (ide_drive_t *drive) pc->timeout = WAIT_CMD; /* Send the command to the drive and return. */ - return cdrom_transfer_packet_command(drive, pc->c, sizeof(pc->c), - &cdrom_pc_intr, pc->timeout); + return cdrom_transfer_packet_command(drive, pc, &cdrom_pc_intr); } @@ -1345,6 +1384,7 @@ static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive) struct cdrom_info *info = drive->driver_data; info->dma = 0; + info->cmd = 0; pc->stat = 0; len = pc->buflen; @@ -1414,6 +1454,162 @@ int cdrom_queue_packet_command(ide_drive_t *drive, struct packet_command *pc) 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; /* packet_command_t 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 /* the immediate bit */ + 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]); + pc.timeout = 2 * WAIT_CMD; + + return cdrom_transfer_packet_command(drive, &pc, cdrom_write_intr); +} + +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; + info->cmd = WRITE; + + /* Start sending the read request to the drive. */ + return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont); +} + /**************************************************************************** * cdrom driver request routine. */ @@ -1424,6 +1620,7 @@ ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block) 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; @@ -1440,8 +1637,12 @@ ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block) } 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; } @@ -1457,7 +1658,7 @@ ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block) } 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; } @@ -1849,8 +2050,9 @@ static int cdrom_select_speed(ide_drive_t *drive, int speed, 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 */ @@ -1902,10 +2104,6 @@ static int cdrom_get_toc_entry(ide_drive_t *drive, int track, return 0; } - - - - /* the generic packet interface to cdrom.c */ static int ide_cdrom_packet(struct cdrom_device_info *cdi, struct cdrom_generic_command *cgc) @@ -2441,6 +2639,9 @@ int ide_cdrom_setup (ide_drive_t *drive) int minor = drive->select.b.unit << PARTN_BITS; int nslots; + /* + * default to read-only always and fix latter at the bottom + */ set_device_ro(MKDEV(HWIF(drive)->major, minor), 1); set_blocksize(MKDEV(HWIF(drive)->major, minor), CD_FRAMESIZE); @@ -2560,6 +2761,9 @@ int ide_cdrom_setup (ide_drive_t *drive) nslots = ide_cdrom_probe_capabilities (drive); + if (CDROM_CONFIG_FLAGS(drive)->dvd_ram) + set_device_ro(MKDEV(HWIF(drive)->major, minor), 0); + if (ide_cdrom_register (drive, nslots)) { printk ("%s: ide_cdrom_setup failed to register device with the cdrom driver.\n", drive->name); info->devinfo.handle = NULL; |