diff -ru linux-2.4.20-pre4-ac1.orig/Documentation/Configure.help linux-2.4.20-pre4-ac1/Documentation/Configure.help --- linux-2.4.20-pre4-ac1.orig/Documentation/Configure.help Sat Aug 24 11:01:29 2002 +++ linux-2.4.20-pre4-ac1/Documentation/Configure.help Sun Aug 25 21:18:35 2002 @@ -731,6 +731,17 @@ say M here and read . The module will be called ide-cd.o. +Use DMA for IDE/ATAPI CD-ROM audio +CONFIG_DMA_IDECD + Reading audio from IDE CDROMs always uses PIO. This option + teaches the kernel to use DMA for the CDROMREADAUDIO ioctl. + + Total time to read a CD using cdparanoia improves by up to 20%. + But sometimes there is no change. + + On old systems you may want to say N. However if the drive doesn't + support DMA the kernel falls back to PIO safely. + Include IDE/ATAPI TAPE support CONFIG_BLK_DEV_IDETAPE If you have an IDE tape drive using the ATAPI protocol, say Y. diff -ru linux-2.4.20-pre4-ac1.orig/drivers/ide/ide-cd.c linux-2.4.20-pre4-ac1/drivers/ide/ide-cd.c --- linux-2.4.20-pre4-ac1.orig/drivers/ide/ide-cd.c Sat Aug 24 10:55:26 2002 +++ linux-2.4.20-pre4-ac1/drivers/ide/ide-cd.c Mon Aug 26 14:15:09 2002 @@ -1447,10 +1447,28 @@ int ireason, len, stat, thislen; struct request *rq = HWGROUP(drive)->rq; struct packet_command *pc = (struct packet_command *)rq->buffer; + struct cdrom_info *info = drive->driver_data; + int dma = info->dma; + int dma_error; ide_startstop_t startstop; u8 lowcyl = 0, highcyl = 0; /* Check for errors. */ + if (dma) { + info->dma = 0; + dma_error = HWIF(drive)->ide_dma_end(drive); + if (dma_error) { + /* + * We don't disable drive DMA for packet DMA errors. + * It's handled in cdda_read_audio() + */ + /* HWIF(drive)->ide_dma_off(drive); */ + pc->stat = 2; /* 2 -> DMA error */ + printk(KERN_ERR "CDROM packet DMA error\n"); + } + } + + /* Check for errors. */ if (cdrom_decode_status(&startstop, drive, 0, &stat)) return startstop; @@ -1461,6 +1479,14 @@ len = lowcyl + (256 * highcyl); + if (dma) { + /* + * If DMA succeeded, we have all the data + */ + pc->buffer += pc->buflen; + pc->buflen = 0; + } + /* If DRQ is clear, the command has completed. Complain if we still have data left to transfer. */ if ((stat & DRQ_STAT) == 0) { @@ -1566,7 +1592,11 @@ struct packet_command *pc = (struct packet_command *)rq->buffer; struct cdrom_info *info = drive->driver_data; - info->dma = 0; + if (rq->bh) { + info->dma = 1; + } else { + info->dma = 0; + } info->cmd = 0; pc->stat = 0; len = pc->buflen; @@ -1588,6 +1618,13 @@ sleep = schedule_timeout(sleep); } while (sleep); } + +/* + * end_buffer_io_sync() is not exported + */ +static void cdrom_end_buffer_io_sync(struct buffer_head *bh, int uptodate) +{ +} static int cdrom_queue_packet_command(ide_drive_t *drive, struct packet_command *pc) @@ -1601,7 +1638,25 @@ /* Start of retry loop. */ do { + struct buffer_head bh; + ide_init_drive_cmd (&req); + + if (pc->do_dma) { + /* Hack up a buffer_head for IDE DMA's use */ + memset(&bh, 0, sizeof(bh)); + bh.b_size = pc->buflen; + bh.b_data = pc->buffer; + bh.b_state = (1 << BH_Lock) | (1 << BH_Mapped) | + (1 << BH_Req); + bh.b_end_io = cdrom_end_buffer_io_sync; +#if 0 /* Needed by end_buffer_io_sync, but not cdrom_end_buffer_io_sync */ + atomic_set(&bh.b_count, 1); + init_waitqueue_head(&bh.b_wait); +#endif + req.bh = &bh; + } + req.cmd = PACKET_COMMAND; req.buffer = (char *)pc; ide_do_drive_cmd(drive, &req, ide_wait); @@ -1681,7 +1736,8 @@ /* Check for errors. */ if (dma) { info->dma = 0; - if ((dma_error = HWIF(drive)->ide_dma_end(drive))) { + dma_error = HWIF(drive)->ide_dma_end(drive); + if (dma_error) { printk("ide-cd: write dma error\n"); HWIF(drive)->ide_dma_off(drive); } @@ -2348,7 +2404,12 @@ pc.quiet = cgc->quiet; pc.timeout = cgc->timeout; pc.sense = cgc->sense; - return cgc->stat = cdrom_queue_packet_command(drive, &pc); + if (cgc->do_dma && drive->using_dma) + pc.do_dma = 1; + cgc->stat = cdrom_queue_packet_command(drive, &pc); + if (pc.stat == 2) /* DMA error: fall back to lower mode */ + cgc->dma_error = 1; + return cgc->stat; } static diff -ru linux-2.4.20-pre4-ac1.orig/drivers/ide/ide-cd.h linux-2.4.20-pre4-ac1/drivers/ide/ide-cd.h --- linux-2.4.20-pre4-ac1.orig/drivers/ide/ide-cd.h Sat Aug 24 11:09:06 2002 +++ linux-2.4.20-pre4-ac1/drivers/ide/ide-cd.h Sun Aug 25 21:24:16 2002 @@ -111,6 +111,7 @@ int quiet; int timeout; struct request_sense *sense; + int do_dma; unsigned char c[12]; }; diff -ru linux-2.4.20-pre4-ac1.orig/drivers/cdrom/cdrom.c linux-2.4.20-pre4-ac1/drivers/cdrom/cdrom.c --- linux-2.4.20-pre4-ac1.orig/drivers/cdrom/cdrom.c Sat Aug 24 10:58:29 2002 +++ linux-2.4.20-pre4-ac1/drivers/cdrom/cdrom.c Sun Aug 25 21:04:57 2002 @@ -2067,6 +2067,109 @@ return ret; } +/* + * CDROM audio read, with DMA support. Added in 2.4.18-pre4, akpm. + * + * Initially, we try to perform multiframe bus-mastering. If the IDE + * layer experiences a DMA error, we fall back to single-frame DMA. + * If the IDE layer again detects a DMA error, we fall back to multiframe + * PIO. + * + * We do not want to disable drive-level DMA at any stage, because + * some devices can perform non-packet DMA quite happily, but appear + * to not be able to perform packet DMA correctly. + * + * If the drive is not using_dma, we never attempt packet DMA. + */ +static int cdda_read_audio(int cmd, + struct cdrom_device_info *cdi, + struct cdrom_generic_command *cgc, + struct cdrom_read_audio *ra) +{ + int lba; + unsigned frames_todo; + int ret; + void *xferbuf = 0; + unsigned nr_local_frames; + char *useraddr; + + ret = -EINVAL; + if (ra->addr_format == CDROM_MSF) { + lba = msf_to_lba(ra->addr.msf.minute, + ra->addr.msf.second, + ra->addr.msf.frame); + } else if (ra->addr_format == CDROM_LBA) { + lba = ra->addr.lba; + } else { + goto out; + } + + if (lba < 0 || ra->nframes <= 0) + goto out; + + /* + * We can't sensibly support more that 64k because we later + * use a buffer_head to map the temp buffer. And b_count is + * unisgned short. + */ + nr_local_frames = ra->nframes; + if (nr_local_frames * CD_FRAMESIZE_RAW > 32768) + nr_local_frames = 32768 / CD_FRAMESIZE_RAW; + + if (cdi->dma_mode == CDROM_DMA_SINGLE) + nr_local_frames = 1; + + do { + xferbuf = kmalloc(CD_FRAMESIZE_RAW * nr_local_frames, GFP_KERNEL); + } while (!xferbuf && nr_local_frames--); + ret = -ENOMEM; + if (!xferbuf) + goto out; + + cgc->buffer = xferbuf; + cgc->data_direction = CGC_DATA_READ; +#ifdef CONFIG_DMA_IDECD + if (cdi->dma_mode != CDROM_DMA_NONE) + cgc->do_dma = 1; +#endif + frames_todo = ra->nframes; + useraddr = ra->buf; +retry: + while (frames_todo) { + unsigned frames_now = min(frames_todo, nr_local_frames); + + cgc->dma_error = 0; + ret = cdrom_read_block(cdi, cgc, lba, frames_now, 1, CD_FRAMESIZE_RAW); + if (ret) { + /* + * Here we implement DMA size fallback + */ + if (cgc->dma_error && cdi->dma_mode == CDROM_DMA_MULTI) { + printk(KERN_WARNING "CDROM: falling back to " + "single frame DMA\n"); + cdi->dma_mode = CDROM_DMA_SINGLE; + nr_local_frames = 1; + goto retry; + } else if (cgc->dma_error && cdi->dma_mode == CDROM_DMA_SINGLE) { + printk(KERN_WARNING "CDROM: disabled DMA\n"); + cdi->dma_mode = CDROM_DMA_NONE; + goto retry; + } + goto out; + } + ret = -EFAULT; + if (copy_to_user(useraddr, cgc->buffer, CD_FRAMESIZE_RAW * frames_now)) + goto out; + useraddr += CD_FRAMESIZE_RAW * frames_now; + frames_todo -= frames_now; + lba += frames_now; + } + ret = 0; +out: + kfree(xferbuf); + return ret; +} + static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, unsigned long arg) { @@ -2133,57 +2236,10 @@ } case CDROMREADAUDIO: { struct cdrom_read_audio ra; - int lba, nr; IOCTL_IN(arg, struct cdrom_read_audio, ra); - if (ra.addr_format == CDROM_MSF) - lba = msf_to_lba(ra.addr.msf.minute, - ra.addr.msf.second, - ra.addr.msf.frame); - else if (ra.addr_format == CDROM_LBA) - lba = ra.addr.lba; - else - return -EINVAL; - - /* FIXME: we need upper bound checking, too!! */ - if (lba < 0 || ra.nframes <= 0) - 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 cdda_read_audio(cmd, cdi, &cgc, &ra); } case CDROMSUBCHNL: { struct cdrom_subchnl q; diff -ru linux-2.4.20-pre4-ac1.orig/drivers/ide/Config.in linux-2.4.20-pre4-ac1/drivers/ide/Config.in --- linux-2.4.20-pre4-ac1.orig/drivers/ide/Config.in Sat Aug 24 10:55:26 2002 +++ linux-2.4.20-pre4-ac1/drivers/ide/Config.in Sun Aug 25 21:20:26 2002 @@ -29,6 +29,9 @@ dep_tristate ' PCMCIA IDE support' CONFIG_BLK_DEV_IDECS $CONFIG_BLK_DEV_IDE $CONFIG_PCMCIA dep_tristate ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE + if [ "$CONFIG_BLK_DEV_IDECD" != "n" ]; then + dep_mbool ' Use DMA for IDE/ATAPI CD-ROM audio' CONFIG_DMA_IDECD $CONFIG_BLK_DEV_IDECD + fi dep_mbool ' Reduce media failure retries support' CONFIG_BLK_DEV_IDECD_BAILOUT $CONFIG_BLK_DEV_IDECD dep_tristate ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE dep_tristate ' Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE diff -rauN linux-2.4.19-rc1/include/linux/cdrom.h linux-2.4.19-rc1-idecd/include/linux/cdrom.h --- linux-2.4.19-rc1/include/linux/cdrom.h Thu Nov 22 20:47:04 2001 +++ linux-2.4.19-rc1-idecd/include/linux/cdrom.h Sat Jul 13 12:09:08 2002 @@ -287,6 +287,10 @@ unsigned char data_direction; int quiet; int timeout; +#ifdef CONFIG_DMA_IDECD + int do_dma; /* Try to use DMA */ + int dma_error; /* A DMA_specific error occurred */ +#endif void *reserved[1]; }; @@ -743,10 +747,21 @@ char name[20]; /* name of the device type */ /* per-device flags */ __u8 sanyo_slot : 2; /* Sanyo 3 CD changer support */ - __u8 reserved : 6; /* not used yet */ +#ifdef CONFIG_DMA_IDECD + __u8 dma_mode : 2; /* See below */ + __u8 reserved : 4; /* not used yet */ +#else + __u8 reserved : 6; /* not used yet */ +#endif struct cdrom_write_settings write; }; +#ifdef CONFIG_DMA_IDECD +#define CDROM_DMA_MULTI 0 /* Multiframe DMA (default) */ +#define CDROM_DMA_SINGLE 1 /* Single frame DMA */ +#define CDROM_DMA_NONE 2 /* Multiframe PIO */ +#endif + struct cdrom_device_ops { /* routines */ int (*open) (struct cdrom_device_info *, int);