Commit f644f166 authored by Jens Axboe's avatar Jens Axboe Committed by Linus Torvalds

[PATCH] CDROMREADAUDIO dma support

This small patch builds on top of the blk_rq_map_user() patch just sent,
and enables us to easily support DMA for CDROMREADAUDIO cdda extraction.
It's quite amazing how much cool stuff you can with the new block layer
:-)

Patch has intelligent fall back from multi frame dma to single frame
dma, and further to old-style pio ripping in case of hardware problems.
parent 637b48c8
......@@ -406,6 +406,11 @@ int register_cdrom(struct cdrom_device_info *cdi)
if (CDROM_CAN(CDC_MRW_W))
cdi->exit = cdrom_mrw_exit;
if (cdi->disk)
cdi->cdda_method = CDDA_BPC_FULL;
else
cdi->cdda_method = CDDA_OLD;
cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name);
spin_lock(&cdrom_lock);
cdi->next = topCdromPtr;
......@@ -1788,6 +1793,149 @@ static int cdrom_read_block(struct cdrom_device_info *cdi,
return cdo->generic_packet(cdi, cgc);
}
static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf,
int lba, int nframes)
{
struct cdrom_generic_command cgc;
int nr, ret;
memset(&cgc, 0, sizeof(cgc));
/*
* start with will ra.nframes size, back down if alloc fails
*/
nr = nframes;
do {
cgc.buffer = kmalloc(CD_FRAMESIZE_RAW * nr, GFP_KERNEL);
if (cgc.buffer)
break;
nr >>= 1;
} while (nr);
if (!nr)
return -ENOMEM;
if (!access_ok(VERIFY_WRITE, ubuf, nframes * CD_FRAMESIZE_RAW)) {
kfree(cgc.buffer);
return -EFAULT;
}
cgc.data_direction = CGC_DATA_READ;
while (nframes > 0) {
if (nr > nframes)
nr = nframes;
ret = cdrom_read_block(cdi, &cgc, lba, nr, 1, CD_FRAMESIZE_RAW);
if (ret)
break;
__copy_to_user(ubuf, cgc.buffer, CD_FRAMESIZE_RAW * nr);
ubuf += CD_FRAMESIZE_RAW * nr;
nframes -= nr;
lba += nr;
}
kfree(cgc.buffer);
return 0;
}
static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf,
int lba, int nframes)
{
request_queue_t *q = cdi->disk->queue;
struct request *rq;
unsigned int len;
int nr, ret = 0;
if (!q)
return -ENXIO;
while (nframes) {
nr = nframes;
if (cdi->cdda_method == CDDA_BPC_SINGLE)
nr = 1;
if (nr * CD_FRAMESIZE_RAW > (q->max_sectors << 9))
nr = (q->max_sectors << 9) / CD_FRAMESIZE_RAW;
len = nr * CD_FRAMESIZE_RAW;
rq = blk_rq_map_user(q, READ, ubuf, len);
if (IS_ERR(rq))
return PTR_ERR(rq);
memset(rq->cmd, 0, sizeof(rq->cmd));
rq->cmd[0] = GPCMD_READ_CD;
rq->cmd[1] = 1 << 2;
rq->cmd[2] = (lba >> 24) & 0xff;
rq->cmd[3] = (lba >> 16) & 0xff;
rq->cmd[4] = (lba >> 8) & 0xff;
rq->cmd[5] = lba & 0xff;
rq->cmd[6] = (nr >> 16) & 0xff;
rq->cmd[7] = (nr >> 8) & 0xff;
rq->cmd[8] = nr & 0xff;
rq->cmd[9] = 0xf8;
rq->cmd_len = 12;
rq->flags |= REQ_BLOCK_PC;
rq->timeout = 60 * HZ;
if (blk_execute_rq(q, cdi->disk, rq)) {
struct request_sense *s = rq->sense;
ret = -EIO;
cdi->last_sense = s->sense_key;
}
if (blk_rq_unmap_user(rq, ubuf, len))
ret = -EFAULT;
if (ret)
break;
nframes -= nr;
lba += nr;
}
return ret;
}
static int cdrom_read_cdda(struct cdrom_device_info *cdi, __u8 __user *ubuf,
int lba, int nframes)
{
int ret;
if (cdi->cdda_method == CDDA_OLD)
return cdrom_read_cdda_old(cdi, ubuf, lba, nframes);
retry:
/*
* for anything else than success and io error, we need to retry
*/
ret = cdrom_read_cdda_bpc(cdi, ubuf, lba, nframes);
if (!ret || ret != -EIO)
return ret;
/*
* I've seen drives get sense 4/8/3 udma crc errors on multi
* frame dma, so drop to single frame dma if we need to
*/
if (cdi->cdda_method == CDDA_BPC_FULL && nframes > 1) {
printk("cdrom: dropping to single frame dma\n");
cdi->cdda_method = CDDA_BPC_SINGLE;
goto retry;
}
/*
* so we have an io error of some sort with multi frame dma. if the
* condition wasn't a hardware error
* problems, not for any error
*/
if (cdi->last_sense != 0x04 && cdi->last_sense != 0x0b)
return ret;
printk("cdrom: dropping to old style cdda (sense=%x)\n", cdi->last_sense);
cdi->cdda_method = CDDA_OLD;
return cdrom_read_cdda_old(cdi, ubuf, lba, nframes);
}
/* Just about every imaginable ioctl is supported in the Uniform layer
* these days. ATAPI / SCSI specific code now mainly resides in
* mmc_ioct().
......@@ -2280,7 +2428,7 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
}
case CDROMREADAUDIO: {
struct cdrom_read_audio ra;
int lba, nr;
int lba;
IOCTL_IN(arg, struct cdrom_read_audio, ra);
......@@ -2297,40 +2445,7 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
if (lba < 0 || ra.nframes <= 0 || ra.nframes > CD_FRAMES)
return -EINVAL;
/*
* start with will ra.nframes size, back down if alloc fails
*/
nr = ra.nframes;
do {
cgc.buffer = kmalloc(CD_FRAMESIZE_RAW * nr, GFP_KERNEL);
if (cgc.buffer)
break;
nr >>= 1;
} while (nr);
if (!nr)
return -ENOMEM;
if (!access_ok(VERIFY_WRITE, ra.buf, ra.nframes*CD_FRAMESIZE_RAW)) {
kfree(cgc.buffer);
return -EFAULT;
}
cgc.data_direction = CGC_DATA_READ;
while (ra.nframes > 0) {
if (nr > ra.nframes)
nr = ra.nframes;
ret = cdrom_read_block(cdi, &cgc, lba, nr, 1, CD_FRAMESIZE_RAW);
if (ret)
break;
__copy_to_user(ra.buf, cgc.buffer, CD_FRAMESIZE_RAW*nr);
ra.buf += CD_FRAMESIZE_RAW * nr;
ra.nframes -= nr;
lba += nr;
}
kfree(cgc.buffer);
return ret;
return cdrom_read_cdda(cdi, ra.buf, lba, ra.nframes);
}
case CDROMSUBCHNL: {
struct cdrom_subchnl q;
......
......@@ -2931,6 +2931,7 @@ static int ide_cdrom_register (ide_drive_t *drive, int nslots)
if (!CDROM_CONFIG_FLAGS(drive)->mrw_w)
devinfo->mask |= CDC_MRW_W;
devinfo->disk = drive->disk;
return register_cdrom(devinfo);
}
......
......@@ -566,10 +566,13 @@ static int sr_probe(struct device *dev)
snprintf(disk->devfs_name, sizeof(disk->devfs_name),
"%s/cd", sdev->devfs_name);
disk->driverfs_dev = &sdev->sdev_gendev;
register_cdrom(&cd->cdi);
set_capacity(disk, cd->capacity);
disk->private_data = &cd->driver;
disk->queue = sdev->request_queue;
cd->cdi.disk = disk;
if (register_cdrom(&cd->cdi))
goto fail_put;
dev_set_drvdata(dev, cd);
add_disk(disk);
......
......@@ -877,10 +877,18 @@ struct mode_page_header {
#include <linux/fs.h> /* not really needed, later.. */
#include <linux/device.h>
/*
* _OLD will use PIO transfer on atapi devices, _BPC_* will use DMA
*/
#define CDDA_OLD 0 /* old style */
#define CDDA_BPC_SINGLE 1 /* single frame block pc */
#define CDDA_BPC_FULL 2 /* multi frame block pc */
/* Uniform cdrom data structures for cdrom.c */
struct cdrom_device_info {
struct cdrom_device_ops *ops; /* link to device_ops */
struct cdrom_device_info *next; /* next device_info for this major */
struct gendisk *disk; /* matching block layer disk */
void *handle; /* driver-dependent data */
/* specifications */
int mask; /* mask of capability: disables them */
......@@ -894,6 +902,8 @@ struct cdrom_device_info {
/* per-device flags */
__u8 sanyo_slot : 2; /* Sanyo 3 CD changer support */
__u8 reserved : 6; /* not used yet */
int cdda_method; /* see flags */
__u8 last_sense;
int for_data;
int (*exit)(struct cdrom_device_info *);
int mrw_mode_page;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment