mirror of
https://github.com/torvalds/linux.git
synced 2024-12-30 14:52:05 +00:00
fbc3edf7d7
The 0day bot reports the below failure which happens occasionally, with their randconfig testing (once every ~100 boots). The Code points at the private pointer ->driver_data being NULL, which hints at a race of sorts where the private driver_data descriptor has disappeared by the time we get to run the workqueue. So let's check that pointer before we continue with issuing the command to the drive. This fix is of the brown paper bag nature but considering that IDE is long deprecated, let's do that so that random testing which happens to enable CONFIG_IDE during randconfig builds, doesn't fail because of this. Besides, failing the TEST_UNIT_READY command because the drive private data is gone is something which we could simply do anyway, to denote that there was a problem communicating with the device. BUG: unable to handle kernel NULL pointer dereference at 000001c0 IP: cdrom_check_status *pde = 00000000 Oops: 0000 [#1] SMP CPU: 1 PID: 155 Comm: kworker/1:2 Not tainted 4.14.0-rc8 #127 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014 Workqueue: events_freezable_power_ disk_events_workfn task: 4fe90980 task.stack: 507ac000 EIP: cdrom_check_status+0x2c/0x90 EFLAGS: 00210246 CPU: 1 EAX: 00000000 EBX: 4fefec00 ECX: 00000000 EDX: 00000000 ESI: 00000003 EDI: ffffffff EBP: 467a9340 ESP: 507aded0 DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 CR0: 80050033 CR2: 000001c0 CR3: 06e0f000 CR4: 00000690 Call Trace: ? ide_cdrom_check_events_real ? cdrom_check_events ? disk_check_events ? process_one_work ? process_one_work ? worker_thread ? kthread ? process_one_work ? __kthread_create_on_node ? ret_from_fork Code: 53 83 ec 14 89 c3 89 d1 be 03 00 00 00 65 a1 14 00 00 00 89 44 24 10 31 c0 8b 43 18 c7 44 24 04 00 00 00 00 c7 04 24 00 00 00 00 <8a> 80 c0 01 00 00 c7 44 24 08 00 00 00 00 83 e0 03 c7 44 24 0c EIP: cdrom_check_status+0x2c/0x90 SS:ESP: 0068:507aded0 CR2: 00000000000001c0 ---[ end trace 2410e586dd8f88b2 ]--- Reported-and-tested-by: Fengguang Wu <fengguang.wu@intel.com> Signed-off-by: Borislav Petkov <bp@suse.de> Cc: "David S. Miller" <davem@davemloft.net> Cc: Jens Axboe <axboe@kernel.dk> Cc: Bart Van Assche <bart.vanassche@sandisk.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1830 lines
46 KiB
C
1830 lines
46 KiB
C
/*
|
|
* ATAPI CD-ROM driver.
|
|
*
|
|
* Copyright (C) 1994-1996 Scott Snyder <snyder@fnald0.fnal.gov>
|
|
* Copyright (C) 1996-1998 Erik Andersen <andersee@debian.org>
|
|
* Copyright (C) 1998-2000 Jens Axboe <axboe@suse.de>
|
|
* Copyright (C) 2005, 2007-2009 Bartlomiej Zolnierkiewicz
|
|
*
|
|
* May be copied or modified under the terms of the GNU General Public
|
|
* License. See linux/COPYING for more information.
|
|
*
|
|
* See Documentation/cdrom/ide-cd for usage information.
|
|
*
|
|
* Suggestions are welcome. Patches that work are more welcome though. ;-)
|
|
*
|
|
* Documentation:
|
|
* Mt. Fuji (SFF8090 version 4) and ATAPI (SFF-8020i rev 2.6) standards.
|
|
*
|
|
* For historical changelog please see:
|
|
* Documentation/ide/ChangeLog.ide-cd.1994-2004
|
|
*/
|
|
|
|
#define DRV_NAME "ide-cd"
|
|
#define PFX DRV_NAME ": "
|
|
|
|
#define IDECD_VERSION "5.00"
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/types.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/sched/task_stack.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/cdrom.h>
|
|
#include <linux/ide.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/bcd.h>
|
|
|
|
/* For SCSI -> ATAPI command conversion */
|
|
#include <scsi/scsi.h>
|
|
|
|
#include <linux/io.h>
|
|
#include <asm/byteorder.h>
|
|
#include <linux/uaccess.h>
|
|
#include <asm/unaligned.h>
|
|
|
|
#include "ide-cd.h"
|
|
|
|
static DEFINE_MUTEX(ide_cd_mutex);
|
|
static DEFINE_MUTEX(idecd_ref_mutex);
|
|
|
|
static void ide_cd_release(struct device *);
|
|
|
|
static struct cdrom_info *ide_cd_get(struct gendisk *disk)
|
|
{
|
|
struct cdrom_info *cd = NULL;
|
|
|
|
mutex_lock(&idecd_ref_mutex);
|
|
cd = ide_drv_g(disk, cdrom_info);
|
|
if (cd) {
|
|
if (ide_device_get(cd->drive))
|
|
cd = NULL;
|
|
else
|
|
get_device(&cd->dev);
|
|
|
|
}
|
|
mutex_unlock(&idecd_ref_mutex);
|
|
return cd;
|
|
}
|
|
|
|
static void ide_cd_put(struct cdrom_info *cd)
|
|
{
|
|
ide_drive_t *drive = cd->drive;
|
|
|
|
mutex_lock(&idecd_ref_mutex);
|
|
put_device(&cd->dev);
|
|
ide_device_put(drive);
|
|
mutex_unlock(&idecd_ref_mutex);
|
|
}
|
|
|
|
/*
|
|
* Generic packet command support and error handling routines.
|
|
*/
|
|
|
|
/* Mark that we've seen a media change and invalidate our internal buffers. */
|
|
static void cdrom_saw_media_change(ide_drive_t *drive)
|
|
{
|
|
drive->dev_flags |= IDE_DFLAG_MEDIA_CHANGED;
|
|
drive->atapi_flags &= ~IDE_AFLAG_TOC_VALID;
|
|
}
|
|
|
|
static int cdrom_log_sense(ide_drive_t *drive, struct request *rq)
|
|
{
|
|
struct request_sense *sense = &drive->sense_data;
|
|
int log = 0;
|
|
|
|
if (!sense || !rq || (rq->rq_flags & RQF_QUIET))
|
|
return 0;
|
|
|
|
ide_debug_log(IDE_DBG_SENSE, "sense_key: 0x%x", sense->sense_key);
|
|
|
|
switch (sense->sense_key) {
|
|
case NO_SENSE:
|
|
case RECOVERED_ERROR:
|
|
break;
|
|
case NOT_READY:
|
|
/*
|
|
* don't care about tray state messages for e.g. capacity
|
|
* commands or in-progress or becoming ready
|
|
*/
|
|
if (sense->asc == 0x3a || sense->asc == 0x04)
|
|
break;
|
|
log = 1;
|
|
break;
|
|
case ILLEGAL_REQUEST:
|
|
/*
|
|
* don't log START_STOP unit with LoEj set, since we cannot
|
|
* reliably check if drive can auto-close
|
|
*/
|
|
if (scsi_req(rq)->cmd[0] == GPCMD_START_STOP_UNIT && sense->asc == 0x24)
|
|
break;
|
|
log = 1;
|
|
break;
|
|
case UNIT_ATTENTION:
|
|
/*
|
|
* Make good and sure we've seen this potential media change.
|
|
* Some drives (i.e. Creative) fail to present the correct sense
|
|
* key in the error register.
|
|
*/
|
|
cdrom_saw_media_change(drive);
|
|
break;
|
|
default:
|
|
log = 1;
|
|
break;
|
|
}
|
|
return log;
|
|
}
|
|
|
|
static void cdrom_analyze_sense_data(ide_drive_t *drive,
|
|
struct request *failed_command)
|
|
{
|
|
struct request_sense *sense = &drive->sense_data;
|
|
struct cdrom_info *info = drive->driver_data;
|
|
unsigned long sector;
|
|
unsigned long bio_sectors;
|
|
|
|
ide_debug_log(IDE_DBG_SENSE, "error_code: 0x%x, sense_key: 0x%x",
|
|
sense->error_code, sense->sense_key);
|
|
|
|
if (failed_command)
|
|
ide_debug_log(IDE_DBG_SENSE, "failed cmd: 0x%x",
|
|
failed_command->cmd[0]);
|
|
|
|
if (!cdrom_log_sense(drive, failed_command))
|
|
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 (which is a
|
|
* confusing error)
|
|
*/
|
|
if (failed_command && scsi_req(failed_command)->cmd[0] == GPCMD_READ_TOC_PMA_ATIP)
|
|
if (sense->sense_key == 0x05 && sense->asc == 0x24)
|
|
return;
|
|
|
|
/* current error */
|
|
if (sense->error_code == 0x70) {
|
|
switch (sense->sense_key) {
|
|
case MEDIUM_ERROR:
|
|
case VOLUME_OVERFLOW:
|
|
case ILLEGAL_REQUEST:
|
|
if (!sense->valid)
|
|
break;
|
|
if (failed_command == NULL ||
|
|
blk_rq_is_passthrough(failed_command))
|
|
break;
|
|
sector = (sense->information[0] << 24) |
|
|
(sense->information[1] << 16) |
|
|
(sense->information[2] << 8) |
|
|
(sense->information[3]);
|
|
|
|
if (queue_logical_block_size(drive->queue) == 2048)
|
|
/* device sector size is 2K */
|
|
sector <<= 2;
|
|
|
|
bio_sectors = max(bio_sectors(failed_command->bio), 4U);
|
|
sector &= ~(bio_sectors - 1);
|
|
|
|
/*
|
|
* The SCSI specification allows for the value
|
|
* returned by READ CAPACITY to be up to 75 2K
|
|
* sectors past the last readable block.
|
|
* Therefore, if we hit a medium error within the
|
|
* last 75 2K sectors, we decrease the saved size
|
|
* value.
|
|
*/
|
|
if (sector < get_capacity(info->disk) &&
|
|
drive->probed_capacity - sector < 4 * 75)
|
|
set_capacity(info->disk, sector);
|
|
}
|
|
}
|
|
|
|
ide_cd_log_error(drive->name, failed_command, sense);
|
|
}
|
|
|
|
static void ide_cd_complete_failed_rq(ide_drive_t *drive, struct request *rq)
|
|
{
|
|
/*
|
|
* For ATA_PRIV_SENSE, "rq->special" points to the original
|
|
* failed request. Also, the sense data should be read
|
|
* directly from rq which might be different from the original
|
|
* sense buffer if it got copied during mapping.
|
|
*/
|
|
struct request *failed = (struct request *)rq->special;
|
|
void *sense = bio_data(rq->bio);
|
|
|
|
if (failed) {
|
|
/*
|
|
* Sense is always read into drive->sense_data, copy back to the
|
|
* original request.
|
|
*/
|
|
memcpy(scsi_req(failed)->sense, sense, 18);
|
|
scsi_req(failed)->sense_len = scsi_req(rq)->sense_len;
|
|
cdrom_analyze_sense_data(drive, failed);
|
|
|
|
if (ide_end_rq(drive, failed, BLK_STS_IOERR, blk_rq_bytes(failed)))
|
|
BUG();
|
|
} else
|
|
cdrom_analyze_sense_data(drive, NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* Allow the drive 5 seconds to recover; some devices will return NOT_READY
|
|
* while flushing data from cache.
|
|
*
|
|
* returns: 0 failed (write timeout expired)
|
|
* 1 success
|
|
*/
|
|
static int ide_cd_breathe(ide_drive_t *drive, struct request *rq)
|
|
{
|
|
|
|
struct cdrom_info *info = drive->driver_data;
|
|
|
|
if (!scsi_req(rq)->result)
|
|
info->write_timeout = jiffies + ATAPI_WAIT_WRITE_BUSY;
|
|
|
|
scsi_req(rq)->result = 1;
|
|
|
|
if (time_after(jiffies, info->write_timeout))
|
|
return 0;
|
|
else {
|
|
/*
|
|
* take a breather
|
|
*/
|
|
blk_delay_queue(drive->queue, 1);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns:
|
|
* 0: if the request should be continued.
|
|
* 1: if the request will be going through error recovery.
|
|
* 2: if the request should be ended.
|
|
*/
|
|
static int cdrom_decode_status(ide_drive_t *drive, u8 stat)
|
|
{
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
struct request *rq = hwif->rq;
|
|
int err, sense_key, do_end_request = 0;
|
|
|
|
/* get the IDE error register */
|
|
err = ide_read_error(drive);
|
|
sense_key = err >> 4;
|
|
|
|
ide_debug_log(IDE_DBG_RQ, "cmd: 0x%x, rq->cmd_type: 0x%x, err: 0x%x, "
|
|
"stat 0x%x",
|
|
rq->cmd[0], rq->cmd_type, err, stat);
|
|
|
|
if (ata_sense_request(rq)) {
|
|
/*
|
|
* We got an error trying to get sense info from the drive
|
|
* (probably while trying to recover from a former error).
|
|
* Just give up.
|
|
*/
|
|
rq->rq_flags |= RQF_FAILED;
|
|
return 2;
|
|
}
|
|
|
|
/* if we have an error, pass CHECK_CONDITION as the SCSI status byte */
|
|
if (blk_rq_is_scsi(rq) && !scsi_req(rq)->result)
|
|
scsi_req(rq)->result = SAM_STAT_CHECK_CONDITION;
|
|
|
|
if (blk_noretry_request(rq))
|
|
do_end_request = 1;
|
|
|
|
switch (sense_key) {
|
|
case NOT_READY:
|
|
if (req_op(rq) == REQ_OP_WRITE) {
|
|
if (ide_cd_breathe(drive, rq))
|
|
return 1;
|
|
} else {
|
|
cdrom_saw_media_change(drive);
|
|
|
|
if (!blk_rq_is_passthrough(rq) &&
|
|
!(rq->rq_flags & RQF_QUIET))
|
|
printk(KERN_ERR PFX "%s: tray open\n",
|
|
drive->name);
|
|
}
|
|
do_end_request = 1;
|
|
break;
|
|
case UNIT_ATTENTION:
|
|
cdrom_saw_media_change(drive);
|
|
|
|
if (blk_rq_is_passthrough(rq))
|
|
return 0;
|
|
|
|
/*
|
|
* Arrange to retry the request but be sure to give up if we've
|
|
* retried too many times.
|
|
*/
|
|
if (++scsi_req(rq)->result > ERROR_MAX)
|
|
do_end_request = 1;
|
|
break;
|
|
case ILLEGAL_REQUEST:
|
|
/*
|
|
* Don't print error message for this condition -- SFF8090i
|
|
* indicates that 5/24/00 is the correct response to a request
|
|
* to close the tray if the drive doesn't have that capability.
|
|
*
|
|
* cdrom_log_sense() knows this!
|
|
*/
|
|
if (scsi_req(rq)->cmd[0] == GPCMD_START_STOP_UNIT)
|
|
break;
|
|
/* fall-through */
|
|
case DATA_PROTECT:
|
|
/*
|
|
* No point in retrying after an illegal request or data
|
|
* protect error.
|
|
*/
|
|
if (!(rq->rq_flags & RQF_QUIET))
|
|
ide_dump_status(drive, "command error", stat);
|
|
do_end_request = 1;
|
|
break;
|
|
case MEDIUM_ERROR:
|
|
/*
|
|
* No point in re-trying a zillion times on a bad sector.
|
|
* If we got here the error is not correctable.
|
|
*/
|
|
if (!(rq->rq_flags & RQF_QUIET))
|
|
ide_dump_status(drive, "media error "
|
|
"(bad sector)", stat);
|
|
do_end_request = 1;
|
|
break;
|
|
case BLANK_CHECK:
|
|
/* disk appears blank? */
|
|
if (!(rq->rq_flags & RQF_QUIET))
|
|
ide_dump_status(drive, "media error (blank)",
|
|
stat);
|
|
do_end_request = 1;
|
|
break;
|
|
default:
|
|
if (blk_rq_is_passthrough(rq))
|
|
break;
|
|
if (err & ~ATA_ABORTED) {
|
|
/* go to the default handler for other errors */
|
|
ide_error(drive, "cdrom_decode_status", stat);
|
|
return 1;
|
|
} else if (++scsi_req(rq)->result > ERROR_MAX)
|
|
/* we've racked up too many retries, abort */
|
|
do_end_request = 1;
|
|
}
|
|
|
|
if (blk_rq_is_passthrough(rq)) {
|
|
rq->rq_flags |= RQF_FAILED;
|
|
do_end_request = 1;
|
|
}
|
|
|
|
/*
|
|
* End a request through request sense analysis when we have sense data.
|
|
* We need this in order to perform end of media processing.
|
|
*/
|
|
if (do_end_request)
|
|
goto end_request;
|
|
|
|
/* if we got a CHECK_CONDITION status, queue a request sense command */
|
|
if (stat & ATA_ERR)
|
|
return ide_queue_sense_rq(drive, NULL) ? 2 : 1;
|
|
return 1;
|
|
|
|
end_request:
|
|
if (stat & ATA_ERR) {
|
|
hwif->rq = NULL;
|
|
return ide_queue_sense_rq(drive, rq) ? 2 : 1;
|
|
} else
|
|
return 2;
|
|
}
|
|
|
|
static void ide_cd_request_sense_fixup(ide_drive_t *drive, struct ide_cmd *cmd)
|
|
{
|
|
struct request *rq = cmd->rq;
|
|
|
|
ide_debug_log(IDE_DBG_FUNC, "rq->cmd[0]: 0x%x", rq->cmd[0]);
|
|
|
|
/*
|
|
* Some of the trailing request sense fields are optional,
|
|
* and some drives don't send them. Sigh.
|
|
*/
|
|
if (scsi_req(rq)->cmd[0] == GPCMD_REQUEST_SENSE &&
|
|
cmd->nleft > 0 && cmd->nleft <= 5)
|
|
cmd->nleft = 0;
|
|
}
|
|
|
|
int ide_cd_queue_pc(ide_drive_t *drive, const unsigned char *cmd,
|
|
int write, void *buffer, unsigned *bufflen,
|
|
struct request_sense *sense, int timeout,
|
|
req_flags_t rq_flags)
|
|
{
|
|
struct cdrom_info *info = drive->driver_data;
|
|
int retries = 10;
|
|
bool failed;
|
|
|
|
ide_debug_log(IDE_DBG_PC, "cmd[0]: 0x%x, write: 0x%x, timeout: %d, "
|
|
"rq_flags: 0x%x",
|
|
cmd[0], write, timeout, rq_flags);
|
|
|
|
/* start of retry loop */
|
|
do {
|
|
struct request *rq;
|
|
int error;
|
|
bool delay = false;
|
|
|
|
rq = blk_get_request(drive->queue,
|
|
write ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, __GFP_RECLAIM);
|
|
memcpy(scsi_req(rq)->cmd, cmd, BLK_MAX_CDB);
|
|
ide_req(rq)->type = ATA_PRIV_PC;
|
|
rq->rq_flags |= rq_flags;
|
|
rq->timeout = timeout;
|
|
if (buffer) {
|
|
error = blk_rq_map_kern(drive->queue, rq, buffer,
|
|
*bufflen, GFP_NOIO);
|
|
if (error) {
|
|
blk_put_request(rq);
|
|
return error;
|
|
}
|
|
}
|
|
|
|
blk_execute_rq(drive->queue, info->disk, rq, 0);
|
|
error = scsi_req(rq)->result ? -EIO : 0;
|
|
|
|
if (buffer)
|
|
*bufflen = scsi_req(rq)->resid_len;
|
|
if (sense)
|
|
memcpy(sense, scsi_req(rq)->sense, sizeof(*sense));
|
|
|
|
/*
|
|
* FIXME: we should probably abort/retry or something in case of
|
|
* failure.
|
|
*/
|
|
failed = (rq->rq_flags & RQF_FAILED) != 0;
|
|
if (failed) {
|
|
/*
|
|
* The request failed. Retry if it was due to a unit
|
|
* attention status (usually means media was changed).
|
|
*/
|
|
struct request_sense *reqbuf = scsi_req(rq)->sense;
|
|
|
|
if (reqbuf->sense_key == UNIT_ATTENTION)
|
|
cdrom_saw_media_change(drive);
|
|
else if (reqbuf->sense_key == NOT_READY &&
|
|
reqbuf->asc == 4 && reqbuf->ascq != 4) {
|
|
/*
|
|
* The drive is in the process of loading
|
|
* a disk. Retry, but wait a little to give
|
|
* the drive time to complete the load.
|
|
*/
|
|
delay = true;
|
|
} else {
|
|
/* otherwise, don't retry */
|
|
retries = 0;
|
|
}
|
|
--retries;
|
|
}
|
|
blk_put_request(rq);
|
|
if (delay)
|
|
ssleep(2);
|
|
} while (failed && retries >= 0);
|
|
|
|
/* return an error if the command failed */
|
|
return failed ? -EIO : 0;
|
|
}
|
|
|
|
/*
|
|
* returns true if rq has been completed
|
|
*/
|
|
static bool ide_cd_error_cmd(ide_drive_t *drive, struct ide_cmd *cmd)
|
|
{
|
|
unsigned int nr_bytes = cmd->nbytes - cmd->nleft;
|
|
|
|
if (cmd->tf_flags & IDE_TFLAG_WRITE)
|
|
nr_bytes -= cmd->last_xfer_len;
|
|
|
|
if (nr_bytes > 0) {
|
|
ide_complete_rq(drive, BLK_STS_OK, nr_bytes);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
|
|
{
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
struct ide_cmd *cmd = &hwif->cmd;
|
|
struct request *rq = hwif->rq;
|
|
ide_expiry_t *expiry = NULL;
|
|
int dma_error = 0, dma, thislen, uptodate = 0;
|
|
int write = (rq_data_dir(rq) == WRITE) ? 1 : 0, rc = 0;
|
|
int sense = ata_sense_request(rq);
|
|
unsigned int timeout;
|
|
u16 len;
|
|
u8 ireason, stat;
|
|
|
|
ide_debug_log(IDE_DBG_PC, "cmd: 0x%x, write: 0x%x", rq->cmd[0], write);
|
|
|
|
/* check for errors */
|
|
dma = drive->dma;
|
|
if (dma) {
|
|
drive->dma = 0;
|
|
drive->waiting_for_dma = 0;
|
|
dma_error = hwif->dma_ops->dma_end(drive);
|
|
ide_dma_unmap_sg(drive, cmd);
|
|
if (dma_error) {
|
|
printk(KERN_ERR PFX "%s: DMA %s error\n", drive->name,
|
|
write ? "write" : "read");
|
|
ide_dma_off(drive);
|
|
}
|
|
}
|
|
|
|
/* check status */
|
|
stat = hwif->tp_ops->read_status(hwif);
|
|
|
|
if (!OK_STAT(stat, 0, BAD_R_STAT)) {
|
|
rc = cdrom_decode_status(drive, stat);
|
|
if (rc) {
|
|
if (rc == 2)
|
|
goto out_end;
|
|
return ide_stopped;
|
|
}
|
|
}
|
|
|
|
/* using dma, transfer is complete now */
|
|
if (dma) {
|
|
if (dma_error)
|
|
return ide_error(drive, "dma error", stat);
|
|
uptodate = 1;
|
|
goto out_end;
|
|
}
|
|
|
|
ide_read_bcount_and_ireason(drive, &len, &ireason);
|
|
|
|
thislen = !blk_rq_is_passthrough(rq) ? len : cmd->nleft;
|
|
if (thislen > len)
|
|
thislen = len;
|
|
|
|
ide_debug_log(IDE_DBG_PC, "DRQ: stat: 0x%x, thislen: %d",
|
|
stat, thislen);
|
|
|
|
/* If DRQ is clear, the command has completed. */
|
|
if ((stat & ATA_DRQ) == 0) {
|
|
switch (req_op(rq)) {
|
|
default:
|
|
/*
|
|
* If we're not done reading/writing, complain.
|
|
* Otherwise, complete the command normally.
|
|
*/
|
|
uptodate = 1;
|
|
if (cmd->nleft > 0) {
|
|
printk(KERN_ERR PFX "%s: %s: data underrun "
|
|
"(%u bytes)\n", drive->name, __func__,
|
|
cmd->nleft);
|
|
if (!write)
|
|
rq->rq_flags |= RQF_FAILED;
|
|
uptodate = 0;
|
|
}
|
|
goto out_end;
|
|
case REQ_OP_DRV_IN:
|
|
case REQ_OP_DRV_OUT:
|
|
ide_cd_request_sense_fixup(drive, cmd);
|
|
|
|
uptodate = cmd->nleft ? 0 : 1;
|
|
|
|
/*
|
|
* suck out the remaining bytes from the drive in an
|
|
* attempt to complete the data xfer. (see BZ#13399)
|
|
*/
|
|
if (!(stat & ATA_ERR) && !uptodate && thislen) {
|
|
ide_pio_bytes(drive, cmd, write, thislen);
|
|
uptodate = cmd->nleft ? 0 : 1;
|
|
}
|
|
|
|
if (!uptodate)
|
|
rq->rq_flags |= RQF_FAILED;
|
|
goto out_end;
|
|
case REQ_OP_SCSI_IN:
|
|
case REQ_OP_SCSI_OUT:
|
|
goto out_end;
|
|
}
|
|
}
|
|
|
|
rc = ide_check_ireason(drive, rq, len, ireason, write);
|
|
if (rc)
|
|
goto out_end;
|
|
|
|
cmd->last_xfer_len = 0;
|
|
|
|
ide_debug_log(IDE_DBG_PC, "data transfer, rq->cmd_type: 0x%x, "
|
|
"ireason: 0x%x",
|
|
rq->cmd_type, ireason);
|
|
|
|
/* transfer data */
|
|
while (thislen > 0) {
|
|
int blen = min_t(int, thislen, cmd->nleft);
|
|
|
|
if (cmd->nleft == 0)
|
|
break;
|
|
|
|
ide_pio_bytes(drive, cmd, write, blen);
|
|
cmd->last_xfer_len += blen;
|
|
|
|
thislen -= blen;
|
|
len -= blen;
|
|
|
|
if (sense && write == 0)
|
|
scsi_req(rq)->sense_len += blen;
|
|
}
|
|
|
|
/* pad, if necessary */
|
|
if (len > 0) {
|
|
if (blk_rq_is_passthrough(rq) || write == 0)
|
|
ide_pad_transfer(drive, write, len);
|
|
else {
|
|
printk(KERN_ERR PFX "%s: confused, missing data\n",
|
|
drive->name);
|
|
blk_dump_rq_flags(rq, "cdrom_newpc_intr");
|
|
}
|
|
}
|
|
|
|
switch (req_op(rq)) {
|
|
case REQ_OP_SCSI_IN:
|
|
case REQ_OP_SCSI_OUT:
|
|
timeout = rq->timeout;
|
|
break;
|
|
case REQ_OP_DRV_IN:
|
|
case REQ_OP_DRV_OUT:
|
|
expiry = ide_cd_expiry;
|
|
/*FALLTHRU*/
|
|
default:
|
|
timeout = ATAPI_WAIT_PC;
|
|
break;
|
|
}
|
|
|
|
hwif->expiry = expiry;
|
|
ide_set_handler(drive, cdrom_newpc_intr, timeout);
|
|
return ide_started;
|
|
|
|
out_end:
|
|
if (blk_rq_is_scsi(rq) && rc == 0) {
|
|
scsi_req(rq)->resid_len = 0;
|
|
blk_end_request_all(rq, BLK_STS_OK);
|
|
hwif->rq = NULL;
|
|
} else {
|
|
if (sense && uptodate)
|
|
ide_cd_complete_failed_rq(drive, rq);
|
|
|
|
if (!blk_rq_is_passthrough(rq)) {
|
|
if (cmd->nleft == 0)
|
|
uptodate = 1;
|
|
} else {
|
|
if (uptodate <= 0 && scsi_req(rq)->result == 0)
|
|
scsi_req(rq)->result = -EIO;
|
|
}
|
|
|
|
if (uptodate == 0 && rq->bio)
|
|
if (ide_cd_error_cmd(drive, cmd))
|
|
return ide_stopped;
|
|
|
|
/* make sure it's fully ended */
|
|
if (blk_rq_is_passthrough(rq)) {
|
|
scsi_req(rq)->resid_len -= cmd->nbytes - cmd->nleft;
|
|
if (uptodate == 0 && (cmd->tf_flags & IDE_TFLAG_WRITE))
|
|
scsi_req(rq)->resid_len += cmd->last_xfer_len;
|
|
}
|
|
|
|
ide_complete_rq(drive, uptodate ? BLK_STS_OK : BLK_STS_IOERR, blk_rq_bytes(rq));
|
|
|
|
if (sense && rc == 2)
|
|
ide_error(drive, "request sense failure", stat);
|
|
}
|
|
return ide_stopped;
|
|
}
|
|
|
|
static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq)
|
|
{
|
|
struct cdrom_info *cd = drive->driver_data;
|
|
struct request_queue *q = drive->queue;
|
|
int write = rq_data_dir(rq) == WRITE;
|
|
unsigned short sectors_per_frame =
|
|
queue_logical_block_size(q) >> SECTOR_BITS;
|
|
|
|
ide_debug_log(IDE_DBG_RQ, "rq->cmd[0]: 0x%x, rq->cmd_flags: 0x%x, "
|
|
"secs_per_frame: %u",
|
|
rq->cmd[0], rq->cmd_flags, sectors_per_frame);
|
|
|
|
if (write) {
|
|
/* disk has become write protected */
|
|
if (get_disk_ro(cd->disk))
|
|
return ide_stopped;
|
|
} else {
|
|
/*
|
|
* We may be retrying this request after an error. Fix up any
|
|
* weirdness which might be present in the request packet.
|
|
*/
|
|
q->prep_rq_fn(q, rq);
|
|
}
|
|
|
|
/* fs requests *must* be hardware frame aligned */
|
|
if ((blk_rq_sectors(rq) & (sectors_per_frame - 1)) ||
|
|
(blk_rq_pos(rq) & (sectors_per_frame - 1)))
|
|
return ide_stopped;
|
|
|
|
/* use DMA, if possible */
|
|
drive->dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA);
|
|
|
|
if (write)
|
|
cd->devinfo.media_written = 1;
|
|
|
|
rq->timeout = ATAPI_WAIT_PC;
|
|
|
|
return ide_started;
|
|
}
|
|
|
|
static void cdrom_do_block_pc(ide_drive_t *drive, struct request *rq)
|
|
{
|
|
|
|
ide_debug_log(IDE_DBG_PC, "rq->cmd[0]: 0x%x, rq->cmd_type: 0x%x",
|
|
rq->cmd[0], rq->cmd_type);
|
|
|
|
if (blk_rq_is_scsi(rq))
|
|
rq->rq_flags |= RQF_QUIET;
|
|
else
|
|
rq->rq_flags &= ~RQF_FAILED;
|
|
|
|
drive->dma = 0;
|
|
|
|
/* sg request */
|
|
if (rq->bio) {
|
|
struct request_queue *q = drive->queue;
|
|
char *buf = bio_data(rq->bio);
|
|
unsigned int alignment;
|
|
|
|
drive->dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA);
|
|
|
|
/*
|
|
* check if dma is safe
|
|
*
|
|
* NOTE! The "len" and "addr" checks should possibly have
|
|
* separate masks.
|
|
*/
|
|
alignment = queue_dma_alignment(q) | q->dma_pad_mask;
|
|
if ((unsigned long)buf & alignment
|
|
|| blk_rq_bytes(rq) & q->dma_pad_mask
|
|
|| object_is_on_stack(buf))
|
|
drive->dma = 0;
|
|
}
|
|
}
|
|
|
|
static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq,
|
|
sector_t block)
|
|
{
|
|
struct ide_cmd cmd;
|
|
int uptodate = 0;
|
|
unsigned int nsectors;
|
|
|
|
ide_debug_log(IDE_DBG_RQ, "cmd: 0x%x, block: %llu",
|
|
rq->cmd[0], (unsigned long long)block);
|
|
|
|
if (drive->debug_mask & IDE_DBG_RQ)
|
|
blk_dump_rq_flags(rq, "ide_cd_do_request");
|
|
|
|
switch (req_op(rq)) {
|
|
default:
|
|
if (cdrom_start_rw(drive, rq) == ide_stopped)
|
|
goto out_end;
|
|
break;
|
|
case REQ_OP_SCSI_IN:
|
|
case REQ_OP_SCSI_OUT:
|
|
handle_pc:
|
|
if (!rq->timeout)
|
|
rq->timeout = ATAPI_WAIT_PC;
|
|
cdrom_do_block_pc(drive, rq);
|
|
break;
|
|
case REQ_OP_DRV_IN:
|
|
case REQ_OP_DRV_OUT:
|
|
switch (ide_req(rq)->type) {
|
|
case ATA_PRIV_MISC:
|
|
/* right now this can only be a reset... */
|
|
uptodate = 1;
|
|
goto out_end;
|
|
case ATA_PRIV_SENSE:
|
|
case ATA_PRIV_PC:
|
|
goto handle_pc;
|
|
default:
|
|
BUG();
|
|
}
|
|
}
|
|
|
|
/* prepare sense request for this command */
|
|
ide_prep_sense(drive, rq);
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
|
|
if (rq_data_dir(rq))
|
|
cmd.tf_flags |= IDE_TFLAG_WRITE;
|
|
|
|
cmd.rq = rq;
|
|
|
|
if (!blk_rq_is_passthrough(rq) || blk_rq_bytes(rq)) {
|
|
ide_init_sg_cmd(&cmd, blk_rq_bytes(rq));
|
|
ide_map_sg(drive, &cmd);
|
|
}
|
|
|
|
return ide_issue_pc(drive, &cmd);
|
|
out_end:
|
|
nsectors = blk_rq_sectors(rq);
|
|
|
|
if (nsectors == 0)
|
|
nsectors = 1;
|
|
|
|
ide_complete_rq(drive, uptodate ? BLK_STS_OK : BLK_STS_IOERR, nsectors << 9);
|
|
|
|
return ide_stopped;
|
|
}
|
|
|
|
/*
|
|
* Ioctl handling.
|
|
*
|
|
* Routines which queue packet commands take as a final argument a pointer to a
|
|
* request_sense struct. If execution of the command results in an error with a
|
|
* CHECK CONDITION status, this structure will be filled with the results of the
|
|
* subsequent request sense command. The pointer can also be NULL, in which case
|
|
* no sense information is returned.
|
|
*/
|
|
static void msf_from_bcd(struct atapi_msf *msf)
|
|
{
|
|
msf->minute = bcd2bin(msf->minute);
|
|
msf->second = bcd2bin(msf->second);
|
|
msf->frame = bcd2bin(msf->frame);
|
|
}
|
|
|
|
int cdrom_check_status(ide_drive_t *drive, struct request_sense *sense)
|
|
{
|
|
struct cdrom_info *info = drive->driver_data;
|
|
struct cdrom_device_info *cdi;
|
|
unsigned char cmd[BLK_MAX_CDB];
|
|
|
|
ide_debug_log(IDE_DBG_FUNC, "enter");
|
|
|
|
if (!info)
|
|
return -EIO;
|
|
|
|
cdi = &info->devinfo;
|
|
|
|
memset(cmd, 0, BLK_MAX_CDB);
|
|
cmd[0] = GPCMD_TEST_UNIT_READY;
|
|
|
|
/*
|
|
* Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to switch CDs
|
|
* instead of supporting the LOAD_UNLOAD opcode.
|
|
*/
|
|
cmd[7] = cdi->sanyo_slot % 3;
|
|
|
|
return ide_cd_queue_pc(drive, cmd, 0, NULL, NULL, sense, 0, RQF_QUIET);
|
|
}
|
|
|
|
static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity,
|
|
unsigned long *sectors_per_frame,
|
|
struct request_sense *sense)
|
|
{
|
|
struct {
|
|
__be32 lba;
|
|
__be32 blocklen;
|
|
} capbuf;
|
|
|
|
int stat;
|
|
unsigned char cmd[BLK_MAX_CDB];
|
|
unsigned len = sizeof(capbuf);
|
|
u32 blocklen;
|
|
|
|
ide_debug_log(IDE_DBG_FUNC, "enter");
|
|
|
|
memset(cmd, 0, BLK_MAX_CDB);
|
|
cmd[0] = GPCMD_READ_CDVD_CAPACITY;
|
|
|
|
stat = ide_cd_queue_pc(drive, cmd, 0, &capbuf, &len, sense, 0,
|
|
RQF_QUIET);
|
|
if (stat)
|
|
return stat;
|
|
|
|
/*
|
|
* Sanity check the given block size, in so far as making
|
|
* sure the sectors_per_frame we give to the caller won't
|
|
* end up being bogus.
|
|
*/
|
|
blocklen = be32_to_cpu(capbuf.blocklen);
|
|
blocklen = (blocklen >> SECTOR_BITS) << SECTOR_BITS;
|
|
switch (blocklen) {
|
|
case 512:
|
|
case 1024:
|
|
case 2048:
|
|
case 4096:
|
|
break;
|
|
default:
|
|
printk_once(KERN_ERR PFX "%s: weird block size %u; "
|
|
"setting default block size to 2048\n",
|
|
drive->name, blocklen);
|
|
blocklen = 2048;
|
|
break;
|
|
}
|
|
|
|
*capacity = 1 + be32_to_cpu(capbuf.lba);
|
|
*sectors_per_frame = blocklen >> SECTOR_BITS;
|
|
|
|
ide_debug_log(IDE_DBG_PROBE, "cap: %lu, sectors_per_frame: %lu",
|
|
*capacity, *sectors_per_frame);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag,
|
|
int format, char *buf, int buflen,
|
|
struct request_sense *sense)
|
|
{
|
|
unsigned char cmd[BLK_MAX_CDB];
|
|
|
|
ide_debug_log(IDE_DBG_FUNC, "enter");
|
|
|
|
memset(cmd, 0, BLK_MAX_CDB);
|
|
|
|
cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
|
|
cmd[6] = trackno;
|
|
cmd[7] = (buflen >> 8);
|
|
cmd[8] = (buflen & 0xff);
|
|
cmd[9] = (format << 6);
|
|
|
|
if (msf_flag)
|
|
cmd[1] = 2;
|
|
|
|
return ide_cd_queue_pc(drive, cmd, 0, buf, &buflen, sense, 0, RQF_QUIET);
|
|
}
|
|
|
|
/* Try to read the entire TOC for the disk into our internal buffer. */
|
|
int ide_cd_read_toc(ide_drive_t *drive, struct request_sense *sense)
|
|
{
|
|
int stat, ntracks, i;
|
|
struct cdrom_info *info = drive->driver_data;
|
|
struct cdrom_device_info *cdi = &info->devinfo;
|
|
struct atapi_toc *toc = info->toc;
|
|
struct {
|
|
struct atapi_toc_header hdr;
|
|
struct atapi_toc_entry ent;
|
|
} ms_tmp;
|
|
long last_written;
|
|
unsigned long sectors_per_frame = SECTORS_PER_FRAME;
|
|
|
|
ide_debug_log(IDE_DBG_FUNC, "enter");
|
|
|
|
if (toc == NULL) {
|
|
/* try to allocate space */
|
|
toc = kmalloc(sizeof(struct atapi_toc), GFP_KERNEL);
|
|
if (toc == NULL) {
|
|
printk(KERN_ERR PFX "%s: No cdrom TOC buffer!\n",
|
|
drive->name);
|
|
return -ENOMEM;
|
|
}
|
|
info->toc = toc;
|
|
}
|
|
|
|
/*
|
|
* Check to see if the existing data is still valid. If it is,
|
|
* just return.
|
|
*/
|
|
(void) cdrom_check_status(drive, sense);
|
|
|
|
if (drive->atapi_flags & IDE_AFLAG_TOC_VALID)
|
|
return 0;
|
|
|
|
/* try to get the total cdrom capacity and sector size */
|
|
stat = cdrom_read_capacity(drive, &toc->capacity, §ors_per_frame,
|
|
sense);
|
|
if (stat)
|
|
toc->capacity = 0x1fffff;
|
|
|
|
set_capacity(info->disk, toc->capacity * sectors_per_frame);
|
|
/* save a private copy of the TOC capacity for error handling */
|
|
drive->probed_capacity = toc->capacity * sectors_per_frame;
|
|
|
|
blk_queue_logical_block_size(drive->queue,
|
|
sectors_per_frame << SECTOR_BITS);
|
|
|
|
/* first read just the header, so we know how long the TOC is */
|
|
stat = cdrom_read_tocentry(drive, 0, 1, 0, (char *) &toc->hdr,
|
|
sizeof(struct atapi_toc_header), sense);
|
|
if (stat)
|
|
return stat;
|
|
|
|
if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) {
|
|
toc->hdr.first_track = bcd2bin(toc->hdr.first_track);
|
|
toc->hdr.last_track = bcd2bin(toc->hdr.last_track);
|
|
}
|
|
|
|
ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
|
|
if (ntracks <= 0)
|
|
return -EIO;
|
|
if (ntracks > MAX_TRACKS)
|
|
ntracks = MAX_TRACKS;
|
|
|
|
/* now read the whole schmeer */
|
|
stat = cdrom_read_tocentry(drive, toc->hdr.first_track, 1, 0,
|
|
(char *)&toc->hdr,
|
|
sizeof(struct atapi_toc_header) +
|
|
(ntracks + 1) *
|
|
sizeof(struct atapi_toc_entry), sense);
|
|
|
|
if (stat && toc->hdr.first_track > 1) {
|
|
/*
|
|
* Cds with CDI tracks only don't have any TOC entries, despite
|
|
* of this the returned values are
|
|
* first_track == last_track = number of CDI tracks + 1,
|
|
* so that this case is indistinguishable from the same layout
|
|
* plus an additional audio track. If we get an error for the
|
|
* regular case, we assume a CDI without additional audio
|
|
* tracks. In this case the readable TOC is empty (CDI tracks
|
|
* are not included) and only holds the Leadout entry.
|
|
*
|
|
* Heiko Eißfeldt.
|
|
*/
|
|
ntracks = 0;
|
|
stat = cdrom_read_tocentry(drive, CDROM_LEADOUT, 1, 0,
|
|
(char *)&toc->hdr,
|
|
sizeof(struct atapi_toc_header) +
|
|
(ntracks + 1) *
|
|
sizeof(struct atapi_toc_entry),
|
|
sense);
|
|
if (stat)
|
|
return stat;
|
|
|
|
if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) {
|
|
toc->hdr.first_track = (u8)bin2bcd(CDROM_LEADOUT);
|
|
toc->hdr.last_track = (u8)bin2bcd(CDROM_LEADOUT);
|
|
} else {
|
|
toc->hdr.first_track = CDROM_LEADOUT;
|
|
toc->hdr.last_track = CDROM_LEADOUT;
|
|
}
|
|
}
|
|
|
|
if (stat)
|
|
return stat;
|
|
|
|
toc->hdr.toc_length = be16_to_cpu(toc->hdr.toc_length);
|
|
|
|
if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) {
|
|
toc->hdr.first_track = bcd2bin(toc->hdr.first_track);
|
|
toc->hdr.last_track = bcd2bin(toc->hdr.last_track);
|
|
}
|
|
|
|
for (i = 0; i <= ntracks; i++) {
|
|
if (drive->atapi_flags & IDE_AFLAG_TOCADDR_AS_BCD) {
|
|
if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD)
|
|
toc->ent[i].track = bcd2bin(toc->ent[i].track);
|
|
msf_from_bcd(&toc->ent[i].addr.msf);
|
|
}
|
|
toc->ent[i].addr.lba = msf_to_lba(toc->ent[i].addr.msf.minute,
|
|
toc->ent[i].addr.msf.second,
|
|
toc->ent[i].addr.msf.frame);
|
|
}
|
|
|
|
if (toc->hdr.first_track != CDROM_LEADOUT) {
|
|
/* read the multisession information */
|
|
stat = cdrom_read_tocentry(drive, 0, 0, 1, (char *)&ms_tmp,
|
|
sizeof(ms_tmp), sense);
|
|
if (stat)
|
|
return stat;
|
|
|
|
toc->last_session_lba = be32_to_cpu(ms_tmp.ent.addr.lba);
|
|
} else {
|
|
ms_tmp.hdr.last_track = CDROM_LEADOUT;
|
|
ms_tmp.hdr.first_track = ms_tmp.hdr.last_track;
|
|
toc->last_session_lba = msf_to_lba(0, 2, 0); /* 0m 2s 0f */
|
|
}
|
|
|
|
if (drive->atapi_flags & IDE_AFLAG_TOCADDR_AS_BCD) {
|
|
/* re-read multisession information using MSF format */
|
|
stat = cdrom_read_tocentry(drive, 0, 1, 1, (char *)&ms_tmp,
|
|
sizeof(ms_tmp), sense);
|
|
if (stat)
|
|
return stat;
|
|
|
|
msf_from_bcd(&ms_tmp.ent.addr.msf);
|
|
toc->last_session_lba = msf_to_lba(ms_tmp.ent.addr.msf.minute,
|
|
ms_tmp.ent.addr.msf.second,
|
|
ms_tmp.ent.addr.msf.frame);
|
|
}
|
|
|
|
toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track);
|
|
|
|
/* now try to get the total cdrom capacity */
|
|
stat = cdrom_get_last_written(cdi, &last_written);
|
|
if (!stat && (last_written > toc->capacity)) {
|
|
toc->capacity = last_written;
|
|
set_capacity(info->disk, toc->capacity * sectors_per_frame);
|
|
drive->probed_capacity = toc->capacity * sectors_per_frame;
|
|
}
|
|
|
|
/* Remember that we've read this stuff. */
|
|
drive->atapi_flags |= IDE_AFLAG_TOC_VALID;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ide_cdrom_get_capabilities(ide_drive_t *drive, u8 *buf)
|
|
{
|
|
struct cdrom_info *info = drive->driver_data;
|
|
struct cdrom_device_info *cdi = &info->devinfo;
|
|
struct packet_command cgc;
|
|
int stat, attempts = 3, size = ATAPI_CAPABILITIES_PAGE_SIZE;
|
|
|
|
ide_debug_log(IDE_DBG_FUNC, "enter");
|
|
|
|
if ((drive->atapi_flags & IDE_AFLAG_FULL_CAPS_PAGE) == 0)
|
|
size -= ATAPI_CAPABILITIES_PAGE_PAD_SIZE;
|
|
|
|
init_cdrom_command(&cgc, buf, size, CGC_DATA_UNKNOWN);
|
|
do {
|
|
/* we seem to get stat=0x01,err=0x00 the first time (??) */
|
|
stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
|
|
if (!stat)
|
|
break;
|
|
} while (--attempts);
|
|
return stat;
|
|
}
|
|
|
|
void ide_cdrom_update_speed(ide_drive_t *drive, u8 *buf)
|
|
{
|
|
struct cdrom_info *cd = drive->driver_data;
|
|
u16 curspeed, maxspeed;
|
|
|
|
ide_debug_log(IDE_DBG_FUNC, "enter");
|
|
|
|
if (drive->atapi_flags & IDE_AFLAG_LE_SPEED_FIELDS) {
|
|
curspeed = le16_to_cpup((__le16 *)&buf[8 + 14]);
|
|
maxspeed = le16_to_cpup((__le16 *)&buf[8 + 8]);
|
|
} else {
|
|
curspeed = be16_to_cpup((__be16 *)&buf[8 + 14]);
|
|
maxspeed = be16_to_cpup((__be16 *)&buf[8 + 8]);
|
|
}
|
|
|
|
ide_debug_log(IDE_DBG_PROBE, "curspeed: %u, maxspeed: %u",
|
|
curspeed, maxspeed);
|
|
|
|
cd->current_speed = DIV_ROUND_CLOSEST(curspeed, 176);
|
|
cd->max_speed = DIV_ROUND_CLOSEST(maxspeed, 176);
|
|
}
|
|
|
|
#define IDE_CD_CAPABILITIES \
|
|
(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_DRIVE_STATUS | CDC_CD_R | \
|
|
CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | CDC_GENERIC_PACKET | \
|
|
CDC_MO_DRIVE | CDC_MRW | CDC_MRW_W | CDC_RAM)
|
|
|
|
static const struct cdrom_device_ops ide_cdrom_dops = {
|
|
.open = ide_cdrom_open_real,
|
|
.release = ide_cdrom_release_real,
|
|
.drive_status = ide_cdrom_drive_status,
|
|
.check_events = ide_cdrom_check_events_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,
|
|
.capability = IDE_CD_CAPABILITIES,
|
|
.generic_packet = ide_cdrom_packet,
|
|
};
|
|
|
|
static int ide_cdrom_register(ide_drive_t *drive, int nslots)
|
|
{
|
|
struct cdrom_info *info = drive->driver_data;
|
|
struct cdrom_device_info *devinfo = &info->devinfo;
|
|
|
|
ide_debug_log(IDE_DBG_PROBE, "nslots: %d", nslots);
|
|
|
|
devinfo->ops = &ide_cdrom_dops;
|
|
devinfo->speed = info->current_speed;
|
|
devinfo->capacity = nslots;
|
|
devinfo->handle = drive;
|
|
strcpy(devinfo->name, drive->name);
|
|
|
|
if (drive->atapi_flags & IDE_AFLAG_NO_SPEED_SELECT)
|
|
devinfo->mask |= CDC_SELECT_SPEED;
|
|
|
|
devinfo->disk = info->disk;
|
|
return register_cdrom(devinfo);
|
|
}
|
|
|
|
static int ide_cdrom_probe_capabilities(ide_drive_t *drive)
|
|
{
|
|
struct cdrom_info *cd = drive->driver_data;
|
|
struct cdrom_device_info *cdi = &cd->devinfo;
|
|
u8 buf[ATAPI_CAPABILITIES_PAGE_SIZE];
|
|
mechtype_t mechtype;
|
|
int nslots = 1;
|
|
|
|
ide_debug_log(IDE_DBG_PROBE, "media: 0x%x, atapi_flags: 0x%lx",
|
|
drive->media, drive->atapi_flags);
|
|
|
|
cdi->mask = (CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R |
|
|
CDC_DVD_RAM | CDC_SELECT_DISC | CDC_PLAY_AUDIO |
|
|
CDC_MO_DRIVE | CDC_RAM);
|
|
|
|
if (drive->media == ide_optical) {
|
|
cdi->mask &= ~(CDC_MO_DRIVE | CDC_RAM);
|
|
printk(KERN_ERR PFX "%s: ATAPI magneto-optical drive\n",
|
|
drive->name);
|
|
return nslots;
|
|
}
|
|
|
|
if (drive->atapi_flags & IDE_AFLAG_PRE_ATAPI12) {
|
|
drive->atapi_flags &= ~IDE_AFLAG_NO_EJECT;
|
|
cdi->mask &= ~CDC_PLAY_AUDIO;
|
|
return nslots;
|
|
}
|
|
|
|
/*
|
|
* We have to cheat a little here. the packet will eventually be queued
|
|
* with ide_cdrom_packet(), which extracts the drive from cdi->handle.
|
|
* Since this device hasn't been registered with the Uniform layer yet,
|
|
* it can't do this. Same goes for cdi->ops.
|
|
*/
|
|
cdi->handle = drive;
|
|
cdi->ops = &ide_cdrom_dops;
|
|
|
|
if (ide_cdrom_get_capabilities(drive, buf))
|
|
return 0;
|
|
|
|
if ((buf[8 + 6] & 0x01) == 0)
|
|
drive->dev_flags &= ~IDE_DFLAG_DOORLOCKING;
|
|
if (buf[8 + 6] & 0x08)
|
|
drive->atapi_flags &= ~IDE_AFLAG_NO_EJECT;
|
|
if (buf[8 + 3] & 0x01)
|
|
cdi->mask &= ~CDC_CD_R;
|
|
if (buf[8 + 3] & 0x02)
|
|
cdi->mask &= ~(CDC_CD_RW | CDC_RAM);
|
|
if (buf[8 + 2] & 0x38)
|
|
cdi->mask &= ~CDC_DVD;
|
|
if (buf[8 + 3] & 0x20)
|
|
cdi->mask &= ~(CDC_DVD_RAM | CDC_RAM);
|
|
if (buf[8 + 3] & 0x10)
|
|
cdi->mask &= ~CDC_DVD_R;
|
|
if ((buf[8 + 4] & 0x01) || (drive->atapi_flags & IDE_AFLAG_PLAY_AUDIO_OK))
|
|
cdi->mask &= ~CDC_PLAY_AUDIO;
|
|
|
|
mechtype = buf[8 + 6] >> 5;
|
|
if (mechtype == mechtype_caddy ||
|
|
mechtype == mechtype_popup ||
|
|
(drive->atapi_flags & IDE_AFLAG_NO_AUTOCLOSE))
|
|
cdi->mask |= CDC_CLOSE_TRAY;
|
|
|
|
if (cdi->sanyo_slot > 0) {
|
|
cdi->mask &= ~CDC_SELECT_DISC;
|
|
nslots = 3;
|
|
} else if (mechtype == mechtype_individual_changer ||
|
|
mechtype == mechtype_cartridge_changer) {
|
|
nslots = cdrom_number_of_slots(cdi);
|
|
if (nslots > 1)
|
|
cdi->mask &= ~CDC_SELECT_DISC;
|
|
}
|
|
|
|
ide_cdrom_update_speed(drive, buf);
|
|
|
|
printk(KERN_INFO PFX "%s: ATAPI", drive->name);
|
|
|
|
/* don't print speed if the drive reported 0 */
|
|
if (cd->max_speed)
|
|
printk(KERN_CONT " %dX", cd->max_speed);
|
|
|
|
printk(KERN_CONT " %s", (cdi->mask & CDC_DVD) ? "CD-ROM" : "DVD-ROM");
|
|
|
|
if ((cdi->mask & CDC_DVD_R) == 0 || (cdi->mask & CDC_DVD_RAM) == 0)
|
|
printk(KERN_CONT " DVD%s%s",
|
|
(cdi->mask & CDC_DVD_R) ? "" : "-R",
|
|
(cdi->mask & CDC_DVD_RAM) ? "" : "/RAM");
|
|
|
|
if ((cdi->mask & CDC_CD_R) == 0 || (cdi->mask & CDC_CD_RW) == 0)
|
|
printk(KERN_CONT " CD%s%s",
|
|
(cdi->mask & CDC_CD_R) ? "" : "-R",
|
|
(cdi->mask & CDC_CD_RW) ? "" : "/RW");
|
|
|
|
if ((cdi->mask & CDC_SELECT_DISC) == 0)
|
|
printk(KERN_CONT " changer w/%d slots", nslots);
|
|
else
|
|
printk(KERN_CONT " drive");
|
|
|
|
printk(KERN_CONT ", %dkB Cache\n",
|
|
be16_to_cpup((__be16 *)&buf[8 + 12]));
|
|
|
|
return nslots;
|
|
}
|
|
|
|
/* standard prep_rq_fn that builds 10 byte cmds */
|
|
static int ide_cdrom_prep_fs(struct request_queue *q, struct request *rq)
|
|
{
|
|
int hard_sect = queue_logical_block_size(q);
|
|
long block = (long)blk_rq_pos(rq) / (hard_sect >> 9);
|
|
unsigned long blocks = blk_rq_sectors(rq) / (hard_sect >> 9);
|
|
struct scsi_request *req = scsi_req(rq);
|
|
|
|
scsi_req_init(req);
|
|
memset(req->cmd, 0, BLK_MAX_CDB);
|
|
|
|
if (rq_data_dir(rq) == READ)
|
|
req->cmd[0] = GPCMD_READ_10;
|
|
else
|
|
req->cmd[0] = GPCMD_WRITE_10;
|
|
|
|
/*
|
|
* fill in lba
|
|
*/
|
|
req->cmd[2] = (block >> 24) & 0xff;
|
|
req->cmd[3] = (block >> 16) & 0xff;
|
|
req->cmd[4] = (block >> 8) & 0xff;
|
|
req->cmd[5] = block & 0xff;
|
|
|
|
/*
|
|
* and transfer length
|
|
*/
|
|
req->cmd[7] = (blocks >> 8) & 0xff;
|
|
req->cmd[8] = blocks & 0xff;
|
|
req->cmd_len = 10;
|
|
return BLKPREP_OK;
|
|
}
|
|
|
|
/*
|
|
* Most of the SCSI commands are supported directly by ATAPI devices.
|
|
* This transform handles the few exceptions.
|
|
*/
|
|
static int ide_cdrom_prep_pc(struct request *rq)
|
|
{
|
|
u8 *c = scsi_req(rq)->cmd;
|
|
|
|
/* transform 6-byte read/write commands to the 10-byte version */
|
|
if (c[0] == READ_6 || c[0] == WRITE_6) {
|
|
c[8] = c[4];
|
|
c[5] = c[3];
|
|
c[4] = c[2];
|
|
c[3] = c[1] & 0x1f;
|
|
c[2] = 0;
|
|
c[1] &= 0xe0;
|
|
c[0] += (READ_10 - READ_6);
|
|
scsi_req(rq)->cmd_len = 10;
|
|
return BLKPREP_OK;
|
|
}
|
|
|
|
/*
|
|
* it's silly to pretend we understand 6-byte sense commands, just
|
|
* reject with ILLEGAL_REQUEST and the caller should take the
|
|
* appropriate action
|
|
*/
|
|
if (c[0] == MODE_SENSE || c[0] == MODE_SELECT) {
|
|
scsi_req(rq)->result = ILLEGAL_REQUEST;
|
|
return BLKPREP_KILL;
|
|
}
|
|
|
|
return BLKPREP_OK;
|
|
}
|
|
|
|
static int ide_cdrom_prep_fn(struct request_queue *q, struct request *rq)
|
|
{
|
|
if (!blk_rq_is_passthrough(rq))
|
|
return ide_cdrom_prep_fs(q, rq);
|
|
else if (blk_rq_is_scsi(rq))
|
|
return ide_cdrom_prep_pc(rq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct cd_list_entry {
|
|
const char *id_model;
|
|
const char *id_firmware;
|
|
unsigned int cd_flags;
|
|
};
|
|
|
|
#ifdef CONFIG_IDE_PROC_FS
|
|
static sector_t ide_cdrom_capacity(ide_drive_t *drive)
|
|
{
|
|
unsigned long capacity, sectors_per_frame;
|
|
|
|
if (cdrom_read_capacity(drive, &capacity, §ors_per_frame, NULL))
|
|
return 0;
|
|
|
|
return capacity * sectors_per_frame;
|
|
}
|
|
|
|
static int idecd_capacity_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
ide_drive_t *drive = m->private;
|
|
|
|
seq_printf(m, "%llu\n", (long long)ide_cdrom_capacity(drive));
|
|
return 0;
|
|
}
|
|
|
|
static int idecd_capacity_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, idecd_capacity_proc_show, PDE_DATA(inode));
|
|
}
|
|
|
|
static const struct file_operations idecd_capacity_proc_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = idecd_capacity_proc_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
static ide_proc_entry_t idecd_proc[] = {
|
|
{ "capacity", S_IFREG|S_IRUGO, &idecd_capacity_proc_fops },
|
|
{}
|
|
};
|
|
|
|
static ide_proc_entry_t *ide_cd_proc_entries(ide_drive_t *drive)
|
|
{
|
|
return idecd_proc;
|
|
}
|
|
|
|
static const struct ide_proc_devset *ide_cd_proc_devsets(ide_drive_t *drive)
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
static const struct cd_list_entry ide_cd_quirks_list[] = {
|
|
/* SCR-3231 doesn't support the SET_CD_SPEED command. */
|
|
{ "SAMSUNG CD-ROM SCR-3231", NULL, IDE_AFLAG_NO_SPEED_SELECT },
|
|
/* Old NEC260 (not R) was released before ATAPI 1.2 spec. */
|
|
{ "NEC CD-ROM DRIVE:260", "1.01", IDE_AFLAG_TOCADDR_AS_BCD |
|
|
IDE_AFLAG_PRE_ATAPI12, },
|
|
/* Vertos 300, some versions of this drive like to talk BCD. */
|
|
{ "V003S0DS", NULL, IDE_AFLAG_VERTOS_300_SSD, },
|
|
/* Vertos 600 ESD. */
|
|
{ "V006E0DS", NULL, IDE_AFLAG_VERTOS_600_ESD, },
|
|
/*
|
|
* Sanyo 3 CD changer uses a non-standard command for CD changing
|
|
* (by default standard ATAPI support for CD changers is used).
|
|
*/
|
|
{ "CD-ROM CDR-C3 G", NULL, IDE_AFLAG_SANYO_3CD },
|
|
{ "CD-ROM CDR-C3G", NULL, IDE_AFLAG_SANYO_3CD },
|
|
{ "CD-ROM CDR_C36", NULL, IDE_AFLAG_SANYO_3CD },
|
|
/* Stingray 8X CD-ROM. */
|
|
{ "STINGRAY 8422 IDE 8X CD-ROM 7-27-95", NULL, IDE_AFLAG_PRE_ATAPI12 },
|
|
/*
|
|
* ACER 50X CD-ROM and WPI 32X CD-ROM require the full spec length
|
|
* mode sense page capabilities size, but older drives break.
|
|
*/
|
|
{ "ATAPI CD ROM DRIVE 50X MAX", NULL, IDE_AFLAG_FULL_CAPS_PAGE },
|
|
{ "WPI CDS-32X", NULL, IDE_AFLAG_FULL_CAPS_PAGE },
|
|
/* ACER/AOpen 24X CD-ROM has the speed fields byte-swapped. */
|
|
{ "", "241N", IDE_AFLAG_LE_SPEED_FIELDS },
|
|
/*
|
|
* Some drives used by Apple don't advertise audio play
|
|
* but they do support reading TOC & audio datas.
|
|
*/
|
|
{ "MATSHITADVD-ROM SR-8187", NULL, IDE_AFLAG_PLAY_AUDIO_OK },
|
|
{ "MATSHITADVD-ROM SR-8186", NULL, IDE_AFLAG_PLAY_AUDIO_OK },
|
|
{ "MATSHITADVD-ROM SR-8176", NULL, IDE_AFLAG_PLAY_AUDIO_OK },
|
|
{ "MATSHITADVD-ROM SR-8174", NULL, IDE_AFLAG_PLAY_AUDIO_OK },
|
|
{ "Optiarc DVD RW AD-5200A", NULL, IDE_AFLAG_PLAY_AUDIO_OK },
|
|
{ "Optiarc DVD RW AD-7200A", NULL, IDE_AFLAG_PLAY_AUDIO_OK },
|
|
{ "Optiarc DVD RW AD-7543A", NULL, IDE_AFLAG_NO_AUTOCLOSE },
|
|
{ "TEAC CD-ROM CD-224E", NULL, IDE_AFLAG_NO_AUTOCLOSE },
|
|
{ NULL, NULL, 0 }
|
|
};
|
|
|
|
static unsigned int ide_cd_flags(u16 *id)
|
|
{
|
|
const struct cd_list_entry *cle = ide_cd_quirks_list;
|
|
|
|
while (cle->id_model) {
|
|
if (strcmp(cle->id_model, (char *)&id[ATA_ID_PROD]) == 0 &&
|
|
(cle->id_firmware == NULL ||
|
|
strstr((char *)&id[ATA_ID_FW_REV], cle->id_firmware)))
|
|
return cle->cd_flags;
|
|
cle++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ide_cdrom_setup(ide_drive_t *drive)
|
|
{
|
|
struct cdrom_info *cd = drive->driver_data;
|
|
struct cdrom_device_info *cdi = &cd->devinfo;
|
|
struct request_queue *q = drive->queue;
|
|
u16 *id = drive->id;
|
|
char *fw_rev = (char *)&id[ATA_ID_FW_REV];
|
|
int nslots;
|
|
|
|
ide_debug_log(IDE_DBG_PROBE, "enter");
|
|
|
|
blk_queue_prep_rq(q, ide_cdrom_prep_fn);
|
|
blk_queue_dma_alignment(q, 31);
|
|
blk_queue_update_dma_pad(q, 15);
|
|
|
|
drive->dev_flags |= IDE_DFLAG_MEDIA_CHANGED;
|
|
drive->atapi_flags = IDE_AFLAG_NO_EJECT | ide_cd_flags(id);
|
|
|
|
if ((drive->atapi_flags & IDE_AFLAG_VERTOS_300_SSD) &&
|
|
fw_rev[4] == '1' && fw_rev[6] <= '2')
|
|
drive->atapi_flags |= (IDE_AFLAG_TOCTRACKS_AS_BCD |
|
|
IDE_AFLAG_TOCADDR_AS_BCD);
|
|
else if ((drive->atapi_flags & IDE_AFLAG_VERTOS_600_ESD) &&
|
|
fw_rev[4] == '1' && fw_rev[6] <= '2')
|
|
drive->atapi_flags |= IDE_AFLAG_TOCTRACKS_AS_BCD;
|
|
else if (drive->atapi_flags & IDE_AFLAG_SANYO_3CD)
|
|
/* 3 => use CD in slot 0 */
|
|
cdi->sanyo_slot = 3;
|
|
|
|
nslots = ide_cdrom_probe_capabilities(drive);
|
|
|
|
blk_queue_logical_block_size(q, CD_FRAMESIZE);
|
|
|
|
if (ide_cdrom_register(drive, nslots)) {
|
|
printk(KERN_ERR PFX "%s: %s failed to register device with the"
|
|
" cdrom driver.\n", drive->name, __func__);
|
|
cd->devinfo.handle = NULL;
|
|
return 1;
|
|
}
|
|
|
|
ide_proc_register_driver(drive, cd->driver);
|
|
return 0;
|
|
}
|
|
|
|
static void ide_cd_remove(ide_drive_t *drive)
|
|
{
|
|
struct cdrom_info *info = drive->driver_data;
|
|
|
|
ide_debug_log(IDE_DBG_FUNC, "enter");
|
|
|
|
ide_proc_unregister_driver(drive, info->driver);
|
|
device_del(&info->dev);
|
|
del_gendisk(info->disk);
|
|
|
|
mutex_lock(&idecd_ref_mutex);
|
|
put_device(&info->dev);
|
|
mutex_unlock(&idecd_ref_mutex);
|
|
}
|
|
|
|
static void ide_cd_release(struct device *dev)
|
|
{
|
|
struct cdrom_info *info = to_ide_drv(dev, cdrom_info);
|
|
struct cdrom_device_info *devinfo = &info->devinfo;
|
|
ide_drive_t *drive = info->drive;
|
|
struct gendisk *g = info->disk;
|
|
|
|
ide_debug_log(IDE_DBG_FUNC, "enter");
|
|
|
|
kfree(info->toc);
|
|
if (devinfo->handle == drive)
|
|
unregister_cdrom(devinfo);
|
|
drive->driver_data = NULL;
|
|
blk_queue_prep_rq(drive->queue, NULL);
|
|
g->private_data = NULL;
|
|
put_disk(g);
|
|
kfree(info);
|
|
}
|
|
|
|
static int ide_cd_probe(ide_drive_t *);
|
|
|
|
static struct ide_driver ide_cdrom_driver = {
|
|
.gen_driver = {
|
|
.owner = THIS_MODULE,
|
|
.name = "ide-cdrom",
|
|
.bus = &ide_bus_type,
|
|
},
|
|
.probe = ide_cd_probe,
|
|
.remove = ide_cd_remove,
|
|
.version = IDECD_VERSION,
|
|
.do_request = ide_cd_do_request,
|
|
#ifdef CONFIG_IDE_PROC_FS
|
|
.proc_entries = ide_cd_proc_entries,
|
|
.proc_devsets = ide_cd_proc_devsets,
|
|
#endif
|
|
};
|
|
|
|
static int idecd_open(struct block_device *bdev, fmode_t mode)
|
|
{
|
|
struct cdrom_info *info;
|
|
int rc = -ENXIO;
|
|
|
|
mutex_lock(&ide_cd_mutex);
|
|
info = ide_cd_get(bdev->bd_disk);
|
|
if (!info)
|
|
goto out;
|
|
|
|
rc = cdrom_open(&info->devinfo, bdev, mode);
|
|
if (rc < 0)
|
|
ide_cd_put(info);
|
|
out:
|
|
mutex_unlock(&ide_cd_mutex);
|
|
return rc;
|
|
}
|
|
|
|
static void idecd_release(struct gendisk *disk, fmode_t mode)
|
|
{
|
|
struct cdrom_info *info = ide_drv_g(disk, cdrom_info);
|
|
|
|
mutex_lock(&ide_cd_mutex);
|
|
cdrom_release(&info->devinfo, mode);
|
|
|
|
ide_cd_put(info);
|
|
mutex_unlock(&ide_cd_mutex);
|
|
}
|
|
|
|
static int idecd_set_spindown(struct cdrom_device_info *cdi, unsigned long arg)
|
|
{
|
|
struct packet_command cgc;
|
|
char buffer[16];
|
|
int stat;
|
|
char spindown;
|
|
|
|
if (copy_from_user(&spindown, (void __user *)arg, sizeof(char)))
|
|
return -EFAULT;
|
|
|
|
init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN);
|
|
|
|
stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0);
|
|
if (stat)
|
|
return stat;
|
|
|
|
buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
|
|
return cdrom_mode_select(cdi, &cgc);
|
|
}
|
|
|
|
static int idecd_get_spindown(struct cdrom_device_info *cdi, unsigned long arg)
|
|
{
|
|
struct packet_command cgc;
|
|
char buffer[16];
|
|
int stat;
|
|
char spindown;
|
|
|
|
init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN);
|
|
|
|
stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0);
|
|
if (stat)
|
|
return stat;
|
|
|
|
spindown = buffer[11] & 0x0f;
|
|
if (copy_to_user((void __user *)arg, &spindown, sizeof(char)))
|
|
return -EFAULT;
|
|
return 0;
|
|
}
|
|
|
|
static int idecd_locked_ioctl(struct block_device *bdev, fmode_t mode,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct cdrom_info *info = ide_drv_g(bdev->bd_disk, cdrom_info);
|
|
int err;
|
|
|
|
switch (cmd) {
|
|
case CDROMSETSPINDOWN:
|
|
return idecd_set_spindown(&info->devinfo, arg);
|
|
case CDROMGETSPINDOWN:
|
|
return idecd_get_spindown(&info->devinfo, arg);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
err = generic_ide_ioctl(info->drive, bdev, cmd, arg);
|
|
if (err == -EINVAL)
|
|
err = cdrom_ioctl(&info->devinfo, bdev, mode, cmd, arg);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int idecd_ioctl(struct block_device *bdev, fmode_t mode,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
int ret;
|
|
|
|
mutex_lock(&ide_cd_mutex);
|
|
ret = idecd_locked_ioctl(bdev, mode, cmd, arg);
|
|
mutex_unlock(&ide_cd_mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static unsigned int idecd_check_events(struct gendisk *disk,
|
|
unsigned int clearing)
|
|
{
|
|
struct cdrom_info *info = ide_drv_g(disk, cdrom_info);
|
|
return cdrom_check_events(&info->devinfo, clearing);
|
|
}
|
|
|
|
static int idecd_revalidate_disk(struct gendisk *disk)
|
|
{
|
|
struct cdrom_info *info = ide_drv_g(disk, cdrom_info);
|
|
struct request_sense sense;
|
|
|
|
ide_cd_read_toc(info->drive, &sense);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct block_device_operations idecd_ops = {
|
|
.owner = THIS_MODULE,
|
|
.open = idecd_open,
|
|
.release = idecd_release,
|
|
.ioctl = idecd_ioctl,
|
|
.check_events = idecd_check_events,
|
|
.revalidate_disk = idecd_revalidate_disk
|
|
};
|
|
|
|
/* module options */
|
|
static unsigned long debug_mask;
|
|
module_param(debug_mask, ulong, 0644);
|
|
|
|
MODULE_DESCRIPTION("ATAPI CD-ROM Driver");
|
|
|
|
static int ide_cd_probe(ide_drive_t *drive)
|
|
{
|
|
struct cdrom_info *info;
|
|
struct gendisk *g;
|
|
struct request_sense sense;
|
|
|
|
ide_debug_log(IDE_DBG_PROBE, "driver_req: %s, media: 0x%x",
|
|
drive->driver_req, drive->media);
|
|
|
|
if (!strstr("ide-cdrom", drive->driver_req))
|
|
goto failed;
|
|
|
|
if (drive->media != ide_cdrom && drive->media != ide_optical)
|
|
goto failed;
|
|
|
|
drive->debug_mask = debug_mask;
|
|
drive->irq_handler = cdrom_newpc_intr;
|
|
|
|
info = kzalloc(sizeof(struct cdrom_info), GFP_KERNEL);
|
|
if (info == NULL) {
|
|
printk(KERN_ERR PFX "%s: Can't allocate a cdrom structure\n",
|
|
drive->name);
|
|
goto failed;
|
|
}
|
|
|
|
g = alloc_disk(1 << PARTN_BITS);
|
|
if (!g)
|
|
goto out_free_cd;
|
|
|
|
ide_init_disk(g, drive);
|
|
|
|
info->dev.parent = &drive->gendev;
|
|
info->dev.release = ide_cd_release;
|
|
dev_set_name(&info->dev, "%s", dev_name(&drive->gendev));
|
|
|
|
if (device_register(&info->dev))
|
|
goto out_free_disk;
|
|
|
|
info->drive = drive;
|
|
info->driver = &ide_cdrom_driver;
|
|
info->disk = g;
|
|
|
|
g->private_data = &info->driver;
|
|
|
|
drive->driver_data = info;
|
|
|
|
g->minors = 1;
|
|
g->flags = GENHD_FL_CD | GENHD_FL_REMOVABLE;
|
|
if (ide_cdrom_setup(drive)) {
|
|
put_device(&info->dev);
|
|
goto failed;
|
|
}
|
|
|
|
ide_cd_read_toc(drive, &sense);
|
|
g->fops = &idecd_ops;
|
|
g->flags |= GENHD_FL_REMOVABLE | GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE;
|
|
device_add_disk(&drive->gendev, g);
|
|
return 0;
|
|
|
|
out_free_disk:
|
|
put_disk(g);
|
|
out_free_cd:
|
|
kfree(info);
|
|
failed:
|
|
return -ENODEV;
|
|
}
|
|
|
|
static void __exit ide_cdrom_exit(void)
|
|
{
|
|
driver_unregister(&ide_cdrom_driver.gen_driver);
|
|
}
|
|
|
|
static int __init ide_cdrom_init(void)
|
|
{
|
|
printk(KERN_INFO DRV_NAME " driver " IDECD_VERSION "\n");
|
|
return driver_register(&ide_cdrom_driver.gen_driver);
|
|
}
|
|
|
|
MODULE_ALIAS("ide:*m-cdrom*");
|
|
MODULE_ALIAS("ide-cd");
|
|
module_init(ide_cdrom_init);
|
|
module_exit(ide_cdrom_exit);
|
|
MODULE_LICENSE("GPL");
|