Skip to contentSkip to conversation

CD-MRW (Mt Rainier) support

PATCH In review Jens Axboe 4 participants 5 messages Mar 21, 2002 Uniform cdrom driver

lore
Share & tools
lore

Jens Axboe

author
#0 lore raw

PATCH 1/1 CD-MRW (Mt Rainier) support

Hi,

I've written the basic kernel and user space code to support the "new" mt rainier cd-rw drives (hence forth known as cd-mrw in this mail).

What are these drives? They are basically CD-RW drives with a twist. The twist, to put it shortly:

- back ground formatting support is a requirement
- read-modify-write cycle support is in firmware, not software
- bad block management is in firmware, not file system

The drives use regular CD-RW media. The media needs to be initialized before use, the supplied mtr tool can help you do that. Basically you just need to start a back ground format:

# ./mtr -d /dev/hdc -f full

This will take ~10 seconds or so, after which the media can be used for writing. No more waiting for format completion. Should you wish to enjoy the silence and watch the media format, there's a -p poll switch for that:

# ./mtr -d /dev/hdc -p

(can of course also be used while starting the format). If you want to remove the media before the format has completed, you need to suspend it.

# ./mtr -d /dev/hdc -s

ide-cd will do this automagically if you remove the driver while a format is in progress, btw (from dmesg).

cdrom: issuing MRW bgformat suspend

The mtr tool can restart a format for you as well:

# ./mtr -d /dev/hdc -f restart

ide-cd will also do this automagically if you mount a media rw which is partially formatted, if you didn't disable the bgformat restart option when loading cdrom.o (new options, mrw_format_restart, defaults to 1):

cdrom: mount opening for WRITE
cdrom open: mrw_status 'bgformat inactive'
cdrom: Restarting format

I dunno which drives have cd-mrw support yet, I've got some Philips samples here. I have heard that there are at least TEAC drives with a firmware upgrade option to support cd-mrw, and I'm sure that pretty soon all new burners are going to have this feature. Running the mtr tool will tell you if your drive supports cd-mrw or not.

Any file system will work, but using UDF is recommended. Make sure to remember to include UDF rw support :-). Incidentally, 2.4.19-pre4 has a bug that prevents UDF from being built with read-write support. Patch is included for that and sent to Marcelo separately. Recommended format options:

# mkudffs --media-type=cdrw -b 2048 /dev/hdc
start=0, blocks=16, type=RESERVED 
start=16, blocks=3, type=VRS 
start=19, blocks=237, type=USPACE 
start=256, blocks=1, type=ANCHOR 
start=257, blocks=31, type=USPACE 
start=288, blocks=32, type=PVDS 
start=320, blocks=32, type=LVID 
start=352, blocks=32, type=STABLE 
start=384, blocks=1024, type=SSPACE 
start=1408, blocks=256608, type=PSPACE 
start=258016, blocks=31, type=USPACE 
start=258047, blocks=1, type=ANCHOR 
start=258048, blocks=160, type=USPACE 
start=258208, blocks=32, type=STABLE 
start=258240, blocks=32, type=RVDS 
start=258272, blocks=31, type=USPACE 
start=258303, blocks=1, type=ANCHOR 

This is the first release. Works for me, YMMV.

Patch your 2.4.19-pre4 kernel and give it a spin. Note that only ide-cd supports Mt Rainier right now. The bulk of the support is in the uniform layer, so adding SCSI CD-ROM support is going to be a breeze. I'm not quite done shoving code around between ide-cd and cdrom, so that's why I didn't bother adding the SCSI bits just yet.

Signature · 1 line
Jens Axboe
drivers/cdrom/cdrom.c renamed from opt/kernel/linux-2.4.19-pre4/drivers/cdrom/cdrom.c +293 -22
diff -ur -X /usr/src/exclude /opt/kernel/linux-2.4.19-pre4/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
@@ -228,10 +228,16 @@
    3.12 Oct 18, 2000 - Jens Axboe <[email protected]>
   -- Use quiet bit on packet commands not known to work
 
+   3.20 Sep 24, 2001 - Jens Axboe <[email protected]>
+  -- Various fixes and lots of cleanups not listed :-)
+  -- Locking fixes
+  -- Mt Rainier support
+  -- DVD-RAM write open fixes
+
 -------------------------------------------------------------------------*/
 
-#define REVISION "Revision: 3.12"
-#define VERSION "Id: cdrom.c 3.12 2000/10/18"
+#define REVISION "Revision: 3.20"
+#define VERSION "Id: cdrom.c 3.20 2001/09/24"
 
 /* I use an error-log mask to give fine grain control over the type of
    messages dumped to the system logs.  The available masks include: */
@@ -266,7 +272,6 @@
 #include <linux/init.h>
 
 #include <asm/fcntl.h>
-#include <asm/segment.h>
 #include <asm/uaccess.h>
 
 /* used to tell the module to turn on full debugging messages */
@@ -279,11 +284,23 @@
 static int lockdoor = 1;
 /* will we ever get to use this... sigh. */
 static int check_media_type;
+/* automatically restart mrw format */
+static int mrw_format_restart = 1;
 MODULE_PARM(debug, "i");
 MODULE_PARM(autoclose, "i");
 MODULE_PARM(autoeject, "i");
 MODULE_PARM(lockdoor, "i");
 MODULE_PARM(check_media_type, "i");
+MODULE_PARM(mrw_format_restart, "i");
+
+static spinlock_t cdrom_lock = SPIN_LOCK_UNLOCKED;
+
+static const char *mrw_format_status[4] = {
+	"not mrw",
+	"bgformat inactive",
+	"bgformat active",
+	"mrw complete",
+};
 
 #if (ERRLOGMASK!=CD_NOTHING)
 #define cdinfo(type, fmt, args...) \
@@ -321,6 +338,8 @@
 int cdrom_get_last_written(kdev_t dev, long *last_written);
 int cdrom_get_next_writable(kdev_t dev, long *next_writable);
 
+static int __cdrom_get_di(struct cdrom_device_info *cdi, disc_information *di);
+
 #ifdef CONFIG_SYSCTL
 static void cdrom_sysctl_register(void);
 #endif /* CONFIG_SYSCTL */ 
@@ -348,13 +367,14 @@
 		return -1;
 	if (cdo->open == NULL || cdo->release == NULL)
 		return -2;
-	if ( !banner_printed ) {
+	if (!banner_printed) {
 		printk(KERN_INFO "Uniform CD-ROM driver " REVISION "\n");
 		banner_printed = 1;
 #ifdef CONFIG_SYSCTL
 		cdrom_sysctl_register();
 #endif /* CONFIG_SYSCTL */ 
 	}
+
 	ENSURE(drive_status, CDC_DRIVE_STATUS );
 	ENSURE(media_changed, CDC_MEDIA_CHANGED);
 	ENSURE(tray_move, CDC_CLOSE_TRAY | CDC_OPEN_TRAY);
@@ -400,8 +420,10 @@
 		}
 	}
 	cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name);
+	spin_lock(&cdrom_lock);
 	cdi->next = topCdromPtr; 	
 	topCdromPtr = cdi;
+	spin_unlock(&cdrom_lock);
 	return 0;
 }
 #undef ENSURE
@@ -417,18 +439,23 @@
 		return -1;
 
 	prev = NULL;
+	spin_lock(&cdrom_lock);
 	cdi = topCdromPtr;
 	while (cdi != NULL && cdi->dev != unreg->dev) {
 		prev = cdi;
 		cdi = cdi->next;
 	}
 
-	if (cdi == NULL)
+	if (cdi == NULL) {
+		spin_unlock(&cdrom_lock);
 		return -2;
+	}
 	if (prev)
 		prev->next = cdi->next;
 	else
 		topCdromPtr = cdi->next;
+	spin_unlock(&cdrom_lock);
+
 	cdi->ops->n_minors--;
 	devfs_unregister (cdi->de);
 	devfs_dealloc_unique_number (&cdrom_numspace, cdi->number);
@@ -440,13 +467,213 @@
 {
 	struct cdrom_device_info *cdi;
 
+	spin_lock(&cdrom_lock);
 	cdi = topCdromPtr;
 	while (cdi != NULL && cdi->dev != dev)
 		cdi = cdi->next;
+	spin_unlock(&cdrom_lock);
 
 	return cdi;
 }
 
+int cdrom_get_media_event(struct cdrom_device_info *cdi,
+			  struct media_event_desc *med)
+{
+	struct cdrom_generic_command cgc;
+	unsigned char buffer[8];
+	struct event_header *eh = (struct event_header *) buffer;
+
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
+	cgc.cmd[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION;
+	cgc.cmd[1] = 1;		/* IMMED */
+	cgc.cmd[4] = 1 << 4;	/* media event */
+	cgc.cmd[8] = sizeof(buffer);
+	cgc.quiet = 1;
+
+	if (!cdi->ops->generic_packet(cdi, &cgc)) {
+		if (be16_to_cpu(eh->data_len) < sizeof(*med))
+			return 1;
+		memcpy(med, &buffer[sizeof(*eh)], sizeof(*med));
+		return 0;
+	}
+
+	return 1;
+}
+
+static int cdrom_mrw_bgformat(struct cdrom_device_info *cdi, int cont)
+{
+	struct cdrom_generic_command cgc;
+	unsigned char buffer[12];
+	int ret;
+
+	printk("cdrom: %sstarting format\n", cont ? "Re" : "");
+
+	/*
+	 * FmtData bit set (bit 4), format type is 1
+	 */
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_WRITE);
+	cgc.cmd[0] = GPCMD_FORMAT_UNIT;
+	cgc.cmd[1] = (1 << 4) | 1;
+
+	cgc.timeout = 5 * 60 * HZ;
+
+	/*
+	 * 4 byte format list header, 8 byte format list descriptor
+	 */
+	buffer[1] = 1 << 1;
+	buffer[3] = 8;
+
+	/*
+	 * nr_blocks field
+	 */
+	buffer[4] = 0xff;
+	buffer[5] = 0xff;
+	buffer[6] = 0xff;
+	buffer[7] = 0xff;
+
+	buffer[8] = 0x24 << 2;
+	buffer[11] = cont;
+
+	ret = cdi->ops->generic_packet(cdi, &cgc);
+	if (ret)
+		printk("cdrom: bgformat failed\n");
+
+	return ret;
+}
+
+static int cdrom_mrw_bgformat_susp(struct cdrom_device_info *cdi, int immed)
+{
+	struct cdrom_generic_command cgc;
+
+	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+	cgc.cmd[0] = GPCMD_CLOSE_TRACK;
+
+	/*
+	 * Session = 1, Track = 0
+	 */
+	cgc.cmd[1] = !!immed;
+	cgc.cmd[2] = 1 << 1;
+
+	cgc.timeout = 120 * HZ;
+
+	return cdi->ops->generic_packet(cdi, &cgc);
+}
+
+static int cdrom_flush_cache(struct cdrom_device_info *cdi)
+{
+	struct cdrom_generic_command cgc;
+
+	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+	cgc.cmd[0] = GPCMD_FLUSH_CACHE;
+
+	cgc.timeout = 5 * 60 * HZ;
+
+	return cdi->ops->generic_packet(cdi, &cgc);
+}
+
+static int cdrom_mrw_exit(struct cdrom_device_info *cdi)
+{
+	disc_information di;
+	int ret = 0;
+
+	if (__cdrom_get_di(cdi, &di))
+		return 1;
+
+	if (di.mrw_status == CDM_MRW_BGFORMAT_ACTIVE) {
+		printk("cdrom: issuing MRW bgformat suspend\n");
+		ret = cdrom_mrw_bgformat_susp(cdi, 0);
+	}
+
+	if (!ret)
+		ret = cdrom_flush_cache(cdi);
+
+	return ret;
+}
+
+/*
+ * called by sub driver before module is removed to take any last
+ * actions
+ */
+int exit_cdrom(struct cdrom_device_info *cdi)
+{
+	if (CDROM_CAN(CDC_MRW_W))
+		return cdrom_mrw_exit(cdi);
+
+	return 0;
+}
+
+static int cdrom_media_erasable(struct cdrom_device_info *cdi)
+{
+	disc_information di;
+
+	if (__cdrom_get_di(cdi, &di))
+		return 0;
+
+	return di.erasable;
+}
+
+/*
+ * FIXME: check RO bit
+ */
+static int cdrom_dvdram_open_write(struct cdrom_device_info *cdi)
+{
+	return !cdrom_media_erasable(cdi);
+}
+
+static int cdrom_mrw_open_write(struct cdrom_device_info *cdi)
+{
+	disc_information di;
+	int ret;
+
+	if (__cdrom_get_di(cdi, &di))
+		return 1;
+
+	if (!di.erasable)
+		return 1;
+
+	/*
+	 * mrw_status
+	 * 0	-	not MRW formatted
+	 * 1	-	MRW bgformat started, but not running or complete
+	 * 2	-	MRW bgformat in progress
+	 * 3	-	MRW formatting complete
+	 */
+	ret = 0;
+	printk("cdrom open: mrw_status '%s'\n", mrw_format_status[di.mrw_status]);
+	if (!di.mrw_status)
+		ret = 1;
+	else if (di.mrw_status == CDM_MRW_BGFORMAT_INACTIVE && mrw_format_restart)
+		ret = cdrom_mrw_bgformat(cdi, 1);
+
+	return ret;
+}
+
+/*
+ * returns 0 for ok to open write, non-0 to disallow
+ */
+static int cdrom_open_write(struct cdrom_device_info *cdi)
+{
+	int ret;
+
+	if (CDROM_CAN(CDC_MRW_W))
+		ret = cdrom_mrw_open_write(cdi);
+	else if (CDROM_CAN(CDC_DVD_RAM))
+		ret = cdrom_dvdram_open_write(cdi);
+	else
+		ret = 1;
+
+	return ret;
+}
+
+static int cdrom_close_write(struct cdrom_device_info *cdi)
+{
+#if 0
+	return cdrom_flush_cache(cdi);
+#else
+	return 0;
+#endif
+}
+
 /* We use the open-option O_NONBLOCK to indicate that the
  * purpose of opening is only for subsequent ioctl() calls; no device
  * integrity checks are performed.
@@ -465,8 +692,15 @@
 	if ((cdi = cdrom_find_device(dev)) == NULL)
 		return -ENODEV;
 
-	if ((fp->f_mode & FMODE_WRITE) && !CDROM_CAN(CDC_DVD_RAM))
-		return -EROFS;
+	cdi->use_count++;
+	ret = -EROFS;
+	if (fp->f_mode & FMODE_WRITE) {
+		printk("cdrom: %s opening for WRITE\n", current->comm);
+		if (!CDROM_CAN(CDC_RAM))
+			goto out;
+		if (cdrom_open_write(cdi))
+			goto out;
+	}
 
 	/* if this was a O_NONBLOCK open and we should honor the flags,
 	 * do a quick open without drive/disc integrity checks. */
@@ -475,12 +709,13 @@
 	else
 		ret = open_for_data(cdi);
 
-	if (!ret) cdi->use_count++;
-
 	cdinfo(CD_OPEN, "Use count for \"/dev/%s\" now %d\n", cdi->name, cdi->use_count);
 	/* Do this on open.  Don't wait for mount, because they might
 	    not be mounting, but opening with O_NONBLOCK */
 	check_disk_change(dev);
+out:
+	if (ret)
+		cdi->use_count--;
 	return ret;
 }
 
@@ -491,6 +726,7 @@
 	struct cdrom_device_ops *cdo = cdi->ops;
 	tracktype tracks;
 	cdinfo(CD_OPEN, "entering open_for_data\n");
+
 	/* Check if the driver can report drive status.  If it can, we
 	   can do clever things.  If it can't, well, we at least tried! */
 	if (cdo->drive_status != NULL) {
@@ -568,7 +804,7 @@
 		cdinfo(CD_OPEN, "open device failed.\n"); 
 		goto clean_up_and_return;
 	}
-	if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) {
+	if (CDROM_CAN(CDC_LOCK) && (cdi->options & CDO_LOCK)) {
 			cdo->lock_door(cdi, 1);
 			cdinfo(CD_OPEN, "door locked.\n");
 	}
@@ -646,7 +882,6 @@
 	return 0;
 }
 
-
 /* Admittedly, the logic below could be performed in a nicer way. */
 int cdrom_release(struct inode *ip, struct file *fp)
 {
@@ -657,17 +892,23 @@
 
 	cdinfo(CD_CLOSE, "entering cdrom_release\n"); 
 
-	if (cdi->use_count > 0)
-		cdi->use_count--;
-	if (cdi->use_count == 0)
+	if (!--cdi->use_count) {
 		cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name);
-	if (cdi->use_count == 0 &&
-	    cdo->capability & CDC_LOCK && !keeplocked) {
-		cdinfo(CD_CLOSE, "Unlocking door!\n");
-		cdo->lock_door(cdi, 0);
+		if ((cdo->capability & CDC_LOCK) && !keeplocked) {
+			cdinfo(CD_CLOSE, "Unlocking door!\n");
+			cdo->lock_door(cdi, 0);
+		}
 	}
+
 	opened_for_data = !(cdi->options & CDO_USE_FFLAGS) ||
 		!(fp && fp->f_flags & O_NONBLOCK);
+
+	/*
+	 * flush cache on last write release
+	 */
+	if (CDROM_CAN(CDC_RAM) && !cdi->use_count && opened_for_data)
+		cdrom_close_write(cdi);
+
 	cdo->release(cdi);
 	if (cdi->use_count == 0) {      /* last process that closes dev*/
 		if (opened_for_data &&
@@ -1902,10 +2143,24 @@
 		}
 	}
 
+	/*
+	 * queue command and wait for it to complete
+	 */
 	ret = cdi->ops->generic_packet(cdi, cgc);
-	__copy_to_user(usense, cgc->sense, sizeof(*usense));
+
+	/*
+	 * always copy back sense, command need not have failed for it to
+	 * contain useful info
+	 */
+	if (usense)
+		__copy_to_user(usense, cgc->sense, sizeof(*usense));
+
+	/*
+	 * this really needs to be modified to copy back good bytes
+	 */
 	if (!ret && cgc->data_direction == CGC_DATA_READ)
 		__copy_to_user(ubuf, cgc->buffer, cgc->buflen);
+
 	kfree(cgc->buffer);
 	return ret;
 }
@@ -2234,10 +2489,8 @@
 	return cdo->generic_packet(cdi, &cgc);
 }
 
-/* requires CD R/RW */
-int cdrom_get_disc_info(kdev_t dev, disc_information *di)
+static int __cdrom_get_di(struct cdrom_device_info *cdi, disc_information *di)
 {
-	struct cdrom_device_info *cdi = cdrom_find_device(dev);
 	struct cdrom_device_ops *cdo = cdi->ops;
 	struct cdrom_generic_command cgc;
 	int ret;
@@ -2264,6 +2517,14 @@
 	return cdo->generic_packet(cdi, &cgc);
 }
 
+/* requires CD R/RW */
+int cdrom_get_disc_info(kdev_t dev, disc_information *di)
+{
+	struct cdrom_device_info *cdi = cdrom_find_device(dev);
+
+	return __cdrom_get_di(cdi, di);
+}
+
 
 /* return the last written block on the CD-R media. this is for the udf
    file system. */
@@ -2379,6 +2640,8 @@
 EXPORT_SYMBOL(cdrom_mode_sense);
 EXPORT_SYMBOL(init_cdrom_command);
 EXPORT_SYMBOL(cdrom_find_device);
+EXPORT_SYMBOL(exit_cdrom);
+EXPORT_SYMBOL(cdrom_get_media_event);
 
 #ifdef CONFIG_SYSCTL
 
@@ -2474,6 +2737,14 @@
 	pos += sprintf(info+pos, "\nCan write DVD-RAM:");
 	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
 	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_DVD_RAM) != 0);
+
+	pos += sprintf(info+pos, "\nCan read MRW:");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(info+pos, "\t\t%d", CDROM_CAN(CDC_MRW) != 0);
+
+	pos += sprintf(info+pos, "\nCan write MRW:");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(info+pos, "\t\t%d", CDROM_CAN(CDC_MRW_W) != 0);
 
 	strcpy(info+pos,"\n\n");
 		
drivers/ide/ide-cd.c renamed from opt/kernel/linux-2.4.19-pre4/drivers/ide/ide-cd.c +150 -69

402 lines not on this page · raw message

drivers/ide/ide-cd.h renamed from opt/kernel/linux-2.4.19-pre4/drivers/ide/ide-cd.h +8 -0
diff -ur -X /usr/src/exclude /opt/kernel/linux-2.4.19-pre4/drivers/ide/ide-cd.h linux/drivers/ide/ide-cd.h
@@ -35,6 +35,11 @@
 #define NO_DOOR_LOCKING 0
 #endif
 
+/*
+ * typical timeout for packet command
+ */
+#define ATAPI_WAIT_PC	(60 * HZ)
+
 /************************************************************************/
 
 #define SECTOR_BITS 		9
@@ -75,6 +80,9 @@
 	__u8 dvd		: 1; /* Drive is a DVD-ROM */
 	__u8 dvd_r		: 1; /* Drive can write DVD-R */
 	__u8 dvd_ram		: 1; /* Drive can write DVD-RAM */
+	__u8 mrw		: 1; /* drive can read mrw */
+	__u8 mrw_w		: 1; /* drive can write mrw */
+	__u8 ram		: 1; /* generic WRITE (dvd-ram/mrw) */
 	__u8 test_write		: 1; /* Drive can fake writes */
 	__u8 supp_disc_present	: 1; /* Changer can report exact contents
 					of slots. */
drivers/ide/ide-dma.c renamed from opt/kernel/linux-2.4.19-pre4/drivers/ide/ide-dma.c +0 -19
diff -ur -X /usr/src/exclude /opt/kernel/linux-2.4.19-pre4/drivers/ide/ide-dma.c linux/drivers/ide/ide-dma.c
@@ -204,25 +204,6 @@
 #endif /* CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
 
 /*
- * Our Physical Region Descriptor (PRD) table should be large enough
- * to handle the biggest I/O request we are likely to see.  Since requests
- * can have no more than 256 sectors, and since the typical blocksize is
- * two or more sectors, we could get by with a limit of 128 entries here for
- * the usual worst case.  Most requests seem to include some contiguous blocks,
- * further reducing the number of table entries required.
- *
- * The driver reverts to PIO mode for individual requests that exceed
- * this limit (possible with 512 byte blocksizes, eg. MSDOS f/s), so handling
- * 100% of all crazy scenarios here is not necessary.
- *
- * As it turns out though, we must allocate a full 4KB page for this,
- * so the two PRD tables (ide0 & ide1) will each get half of that,
- * allowing each to have about 256 entries (8 bytes each) from this.
- */
-#define PRD_BYTES	8
-#define PRD_ENTRIES	(PAGE_SIZE / (2 * PRD_BYTES))
-
-/*
  * dma_intr() is the handler for disk read/write DMA interrupts
  */
 ide_startstop_t ide_dma_intr (ide_drive_t *drive)
include/linux/cdrom.h renamed from opt/kernel/linux-2.4.19-pre4/include/linux/cdrom.h +99 -18

180 lines not on this page · raw message

include/linux/ide.h renamed from opt/kernel/linux-2.4.19-pre4/include/linux/ide.h +19 -0
diff -ur -X /usr/src/exclude /opt/kernel/linux-2.4.19-pre4/include/linux/ide.h linux/include/linux/ide.h
@@ -259,6 +259,25 @@
 #endif
 
 /*
+ * Our Physical Region Descriptor (PRD) table should be large enough
+ * to handle the biggest I/O request we are likely to see.  Since requests
+ * can have no more than 256 sectors, and since the typical blocksize is
+ * two or more sectors, we could get by with a limit of 128 entries here for
+ * the usual worst case.  Most requests seem to include some contiguous blocks,
+ * further reducing the number of table entries required.
+ *
+ * The driver reverts to PIO mode for individual requests that exceed
+ * this limit (possible with 512 byte blocksizes, eg. MSDOS f/s), so handling
+ * 100% of all crazy scenarios here is not necessary.
+ *
+ * As it turns out though, we must allocate a full 4KB page for this,
+ * so the two PRD tables (ide0 & ide1) will each get half of that,
+ * allowing each to have about 256 entries (8 bytes each) from this.
+ */
+#define PRD_BYTES	8
+#define PRD_ENTRIES	(PAGE_SIZE / (2 * PRD_BYTES))
+
+/*
  * hwif_chipset_t is used to keep track of the specific hardware
  * chipset used by each IDE interface, if known.
  */
include/linux/udf_fs.h renamed from opt/kernel/linux-2.4.19-pre4/include/linux/udf_fs.h +1 -0
diff -ur -X /usr/src/exclude /opt/kernel/linux-2.4.19-pre4/include/linux/udf_fs.h linux/include/linux/udf_fs.h
@@ -30,6 +30,7 @@
  * HISTORY
  *
  */
+#include <linux/config.h>
 
 #ifndef _UDF_FS_H
 #define _UDF_FS_H 1

/*
* Copyright (c) 2002 Jens Axboe <[email protected]> *
* mtr -- utility to manage mt rainier cd drives + media *

 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License version 2 as
 *   published by the Free Software Foundation.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include <linux/cdrom.h>

#define INIT_CGC(cgc) memset((cgc), 0, sizeof(struct cdrom_generic_command))

#define FORMAT_TYPE_RESTART 1
#define FORMAT_TYPE_FULL 2

static int format_type, format_force, poll_wait, poll_err, suspend_format;

static char mrw_device[256];

void dump_cgc(struct cdrom_generic_command *cgc)

{
	struct request_sense *sense = cgc->sense;
	int i;

	printf("cdb: ");
	for (i = 0; i < 12; i++)
		printf("%02x ", cgc->cmd[i]);
	printf("\n");

	printf("buffer (%d): ", cgc->buflen);
	for (i = 0; i < cgc->buflen; i++)
		printf("%02x ", cgc->buffer[i]);
	printf("\n");

	if (!sense)
		return;

	printf("sense: %02x.%02x.%02x\n", sense->sense_key, sense->asc, sense->ascq);
}

/*
* issue packet command (blocks until it has completed)
*/
int wait_cmd(int fd, struct cdrom_generic_command *cgc, void *buffer,
int len, int ddir, int timeout)

{
	struct request_sense sense;
	int ret;

	memset(&sense, 0, sizeof(sense));

	cgc->timeout = timeout;
	cgc->buffer = buffer;
	cgc->buflen = len;
	cgc->data_direction = ddir;
	cgc->sense = &sense;
	cgc->quiet = 0;

	ret = ioctl(fd, CDROM_SEND_PACKET, cgc);
	if (ret == -1) {
		perror("ioctl");
		dump_cgc(cgc);
	}

	return ret;
}

int start_bg_format(int fd, int new)

{
	struct cdrom_generic_command cgc;
	unsigned char buffer[12];

	INIT_CGC(&cgc);
	memset(buffer, 0, sizeof(buffer));

	cgc.cmd[0] = GPCMD_FORMAT_UNIT;
	cgc.cmd[1] = (1 << 4) | 1;

	buffer[1] = 1 << 1;
	buffer[3] = 8;

	buffer[4] = 0xff;
	buffer[5] = 0xff;
	buffer[6] = 0xff;
	buffer[7] = 0xff;
	buffer[8] = 0x24 << 2;
	buffer[11] = !new;

	return wait_cmd(fd, &cgc, buffer, sizeof(buffer), CGC_DATA_WRITE, 600);
}

/*
* instantiate a format, if appropriate
*/
int mrw_format(int fd, int media_status)

{
	if (media_status == CDM_MRW_BGFORMAT_ACTIVE) {
		printf("%s: back ground format already active\n", mrw_device);
		return 1;
	} else if (media_status == CDM_MRW_BGFORMAT_COMPLETE && !format_force) {
		printf("%s: disc is already mrw formatted\n", mrw_device);
		return 1;
	}

	if (format_type == FORMAT_TYPE_RESTART && media_status != CDM_MRW_BGFORMAT_INACTIVE) {
		printf("%s: can't restart format, need full\n", mrw_device);
		return 1;
	}

	return start_bg_format(fd, format_type == FORMAT_TYPE_FULL);
}

int mrw_format_suspend(int fd, int media_status)

{
	struct cdrom_generic_command cgc;

	if (media_status != CDM_MRW_BGFORMAT_ACTIVE) {
		printf("%s: can't suspend, format not running\n", mrw_device);
		return 1;
	}

	printf("%s: suspending back ground format: ", mrw_device);

	INIT_CGC(&cgc);

	cgc.cmd[0] = GPCMD_CLOSE_TRACK;
	cgc.cmd[1] = 0; /* IMMED */
	cgc.cmd[2] = 1 << 1;

	if (wait_cmd(fd, &cgc, NULL, 0, CGC_DATA_NONE, 1000)) {
		printf("failed\n");
		return 1;
	}

	printf("done\n");
	return 0;
}

int get_media_event(int fd)

{
	struct cdrom_generic_command cgc;
	unsigned char buffer[8];
	int ret;

	INIT_CGC(&cgc);
	memset(buffer, 0, sizeof(buffer));

	cgc.cmd[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION;
	cgc.cmd[1] = 1;
	cgc.cmd[4] = 16;
	cgc.cmd[8] = sizeof(buffer);

	ret = wait_cmd(fd, &cgc, buffer, sizeof(buffer), CGC_DATA_READ, 600);
	if (ret < 0) {
		perror("GET_EVENT");
		return ret;
	}

	return buffer[4] & 0xf;
}

int get_progress(int fd)

{
	struct cdrom_generic_command cgc;
	struct request_sense sense;
	int progress;

	INIT_CGC(&cgc);
	memset(&sense, 0, sizeof(sense));

	cgc.cmd[0] = GPCMD_TEST_UNIT_READY;
	cgc.sense = &sense;

	(void) wait_cmd(fd, &cgc, NULL, 0, CGC_DATA_NONE, 1000);

	printf("progress: ");
	if (sense.sks[0] & 0x80) {
		progress = (sense.sks[1] << 8) + sense.sks[2];
		fprintf(stderr, "%d%%\r", progress);
	} else
		printf("no progress indicator\n");

	return 0;
}

int get_format_progress(int fd)

{
	struct cdrom_generic_command cgc;
	struct request_sense sense;
#if 0
	if (poll_err)
		return 0;

#endif

	INIT_CGC(&cgc);
	memset(&sense, 0, sizeof(sense));

	cgc.cmd[0] = GPCMD_TEST_UNIT_READY;
	cgc.sense = &sense;

	if (wait_cmd(fd, &cgc, NULL, 0, CGC_DATA_NONE, 1000)) {
		printf("%s: TUR failed\n", mrw_device);
		return 0;
	}

	if (sense.sense_key != 0x02 && sense.asc != 0x04 && sense.ascq != 0x04)
		printf("%02x.%02x.%02x\n", sense.sense_key, sense.asc, sense.ascq);

	/*
	 * all mrw drives should support progress indicator, but you never
	 * know...
	 */
	if (!(sense.sks[0] & 0x80)) {
		printf("drive fails to support progress indicator\n");
		poll_err = 1;
		//return 0;
	}

	return (sense.sks[1] << 8) + sense.sks[2];
}

/*
* return mrw media status bits from disc info or -1 on failure */
int get_mrw_media_status(int fd)

{
	struct cdrom_generic_command cgc;
	disc_information di;

	INIT_CGC(&cgc);

	cgc.cmd[0] = GPCMD_READ_DISC_INFO;
	cgc.cmd[8] = sizeof(di);

	if (wait_cmd(fd, &cgc, &di, sizeof(di), CGC_DATA_READ, 60)) {
		printf("read disc info failed\n");
		return -1;
	}

	return di.mrw_status;
}

int poll_events(int fd)

{
	int event, quit, first, progress, media_status;

	quit = 0;
	first = 1;
	do {
		event = get_media_event(fd);
		if (event < 0)
			break;

		switch (event) {
			case 0:
				if (first)
					printf("no media change\n");
				break;
			case 1:
				printf("eject request\n");
				if ((media_status = get_mrw_media_status(fd)) == -1)
					break;
				if (media_status == CDM_MRW_BGFORMAT_ACTIVE)
					mrw_format_suspend(fd, media_status);
				quit = 1;
				break;
			case 2:
				printf("new media\n");
				break;
			case 3:
				printf("media removal\n");
				quit = 1;
				break;
			case 4:
				printf("media change\n");
				break;
			case 5:
				printf("bgformat complete!\n");
				quit = 1;
				break;
			case 6:
				printf("bgformat automatically restarted\n");
				break;
			default:
				printf("unknown media event (%d)\n", event);
				break;
		}

		if (!quit) {
			first = 0;
			progress = get_progress(fd);
			if (event)
				continue;

			sleep(2);
		}

	} while (!quit);

	return event;
}

/*
* issue GET_CONFIGURATION and check for the mt rainier profile */
int check_for_mrw(int fd)

{
	struct mrw_feature_desc *mfd;
	struct cdrom_generic_command cgc;
	char buffer[16];

	INIT_CGC(&cgc);

	cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
	cgc.cmd[3] = CDF_MRW;
	cgc.cmd[8] = sizeof(buffer);

	if (wait_cmd(fd, &cgc, buffer, sizeof(buffer), CGC_DATA_READ, 60))
		return 1;

	mfd = (struct mrw_feature_desc *)&buffer[sizeof(struct feature_header)];

	return !mfd->write;
}

void print_mrw_status(int media_status)

{
	switch (media_status) {
		case CDM_MRW_NOTMRW:
			printf("not a mrw formatted disc\n");
			break;
		case CDM_MRW_BGFORMAT_INACTIVE:
			printf("mrw format inactive and not complete\n");
			break;
		case CDM_MRW_BGFORMAT_ACTIVE:
			printf("mrw format running\n");
			break;
		case CDM_MRW_BGFORMAT_COMPLETE:
			printf("disc is mrw formatted\n");
			break;
	}
}

void print_options(const char *prg)

{
	printf("%s: options:\n", prg);
	printf("\t-d:\t<device>\n");
	printf("\t-f:\t<{restart, full} format type\n");
	printf("\t-F:\tforce format\n");
	printf("\t-s:\tsuspend format\n");
	printf("\t-p:\tpoll for format completion\n");
}

void get_options(int argc, char *argv[])

{
	char c;

	strcpy(mrw_device, "/dev/cdrom");

	while ((c = getopt(argc, argv, "d:f:Fps")) != EOF) {
		switch (c) {
			case 'd':
				strcpy(mrw_device, optarg);
				break;
			case 'f':
				if (!strcmp(optarg, "full"))
					format_type = FORMAT_TYPE_FULL;
				else if (!strcmp(optarg, "restart"))
					format_type = FORMAT_TYPE_RESTART;
				else
					printf("%s: invalid format type %s\n", argv[0], optarg);
				break;
			case 'F':
				format_force = 1;
				break;
			case 'p':
				poll_wait = 1;
				break;
			case 's':
				suspend_format = 1;
				break;
			default:
				if (optarg)
					printf("%s: unknown option '%s'\n", argv[0], optarg);
				print_options(argv[0]);
				exit(1);
		}
	}
}

int main(int argc, char *argv[])

{
	int fd, media_status, progress;

	if (argc == 1) {
		print_options(argv[0]);
		return 1;
	}

	get_options(argc, argv);

	fd = open(mrw_device, O_RDONLY | O_NONBLOCK);
	if (fd == -1) {
		perror("open");
		return 1;
	}

	if (check_for_mrw(fd)) {
		printf("%s: %s is not a mrw drive or mrw reader\n", argv[0], mrw_device);
		return 1;
	}

	if ((media_status = get_mrw_media_status(fd)) == -1) {
		printf("%s: failed to retrieve media status\n", argv[0]);
		return 1;
	}

	print_mrw_status(media_status);

	if (media_status == CDM_MRW_BGFORMAT_ACTIVE) {
		progress = get_format_progress(fd);
		printf("%s: back ground format %d%% complete\n", mrw_device, progress);
	}

	if (format_type) {
		if (mrw_format(fd, media_status))
			return 1;
	} else if (suspend_format)
		mrw_format_suspend(fd, media_status);

	if (poll_wait)
		poll_events(fd);

	return 0;
}
#1 lore raw

Hello Jens,

drivers/ide/ide-cd.c line 297

Quoted 10 lines

diff -ur -X /usr/src/exclude /opt/kernel/linux-2.4.19-pre4/drivers/ide/ide-cd.c linux/drivers/ide/ide-cd.c

drivers/ide/ide-cd.c renamed from opt/kernel/linux-2.4.19-pre4/drivers/ide/ide-cd.c +3 -0
@@ -292,9 +292,13 @@
  *			  correctly reporting tray status -- from
  *			  Michael D Johnson <[email protected]>
  *
+ * 4.60  Mar 21, 2002	- Add mt rainier support
+ *			- Bump timeout value for packet commands
+ *			- Odd stuff

Do you think you've fixed that oops too? I can reproduce this on 2.4.19-pre2-ac3 kernel by setting 'append = "hdb=noprobe hdb=cdrom"'
in lilo.conf and after booting doing a 'cat /dev/hdb | od' without
having a medium in the DVD drive. BTW 2.5.x crashes horribly when I enable this append line in /etc/lilo.conf. I haven't had time to track this down yet, so you might as well give it a whirl. I've got CONFIG_BLK_DEV_PIIX=y and CONFIG_PIIX_TUNING=y. More of the .config upon request.

Unable to handle kernel NULL pointer dereference at virtual address 00000063
c01a9300
*pde = 00000000
Oops: 0000
CPU:    0
EIP:    0010:[<c01a9300>]    Not tainted

Using defaults from ksymoops -t elf32-i386 -a i386
EFLAGS: 00010246
eax: c02a86ac ebx: 00000000 ecx: 00000006 edx: 00000004
esi: 00000005 edi: c02a86ac ebp: 00000001 esp: df1f5edc
ds: 0018 es: 0018 ss: 0018
Process modprobe (pid: 584, stackpage=df1f5000)

Stack: c02a86ac 00000286 e285a820 c01a9347 c02a86ac c01a5e03 00000004 c02a86ac
       00000007 c02a86ac df975e00 c02a86ac 00000001 e285799d c02a86ac 
e285a820
       00000001 e2854000 00000001 00000001 000068a0 c0115c1d df1f4000 
08053c1b
Call Trace: [<e285a820>] [<c01a9347>] [<c01a5e03>] [<e285799d>] [<e285a820>]
   [<c0115c1d>] [<e2854060>] [<c0106bb3>] [<c020002b>]
Code: f6 43 63 08 75 06 f6 43 6a 02 74 0e be 07 00 00 00 57 e8 15
Quoted 1 line
Quoted 1 line

EIP; c01a9300 <config_drive_xfer_rate+b0/e4> <=====

Trace; e285a820 <[ide-cd]ide_cdrom_driver+0/50>
Trace; c01a9346 <piix_dmaproc+12/2c>
Trace; c01a5e02 <ide_register_subdriver+9e/fc>
Trace; e285799c <[ide-cd]init_module+94/196>
Trace; e285a820 <[ide-cd]ide_cdrom_driver+0/50>
Trace; c0115c1c <sys_init_module+504/5a8>
Trace; e2854060 <[ide-cd]__module_license+0/0>
Trace; c0106bb2 <system_call+32/38>
Trace; c020002a <number+12a/43c>
Code; c01a9300 <config_drive_xfer_rate+b0/e4>
00000000 <_EIP>:
Code; c01a9300 <config_drive_xfer_rate+b0/e4> <=====
0: f6 43 63 08 testb $0x8,0x63(%ebx) <=====
Code; c01a9304 <config_drive_xfer_rate+b4/e4>
4: 75 06 jne c <_EIP+0xc> c01a930c
<config_drive_xfer_rate+bc/e4>
Code; c01a9306 <config_drive_xfer_rate+b6/e4>
6: f6 43 6a 02 testb $0x2,0x6a(%ebx)
Code; c01a930a <config_drive_xfer_rate+ba/e4>
a: 74 0e je 1a <_EIP+0x1a> c01a931a
<config_drive_xfer_rate+ca/e4>
Code; c01a930c <config_drive_xfer_rate+bc/e4>
c: be 07 00 00 00 mov $0x7,%esi
Code; c01a9310 <config_drive_xfer_rate+c0/e4>
11: 57 push %edi
Code; c01a9312 <config_drive_xfer_rate+c2/e4>
12: e8 15 00 00 00 call 2c <_EIP+0x2c> c01a932c
<config_drive_xfer_rate+dc/e4>

<1>Unable to handle kernel NULL pointer dereference at virtual address
0000000ce2857543
*pde = 00000000

Oops: 0000
CPU:    0
EIP:    0010:[<e2857543>]    Not tainted
EFLAGS: 00010206
eax: e2857524   ebx: 00000000   ecx: df2a8000   edx: e285a820
esi: c02a86ac   edi: fffffff4   ebp: 00000340   esp: df2a9ef4
ds: 0018   es: 0018   ss: 0018
Process cat (pid: 582, stackpage=df2a9000)
Stack: c02a86ac df2164c0 dfe512bc c01a3b55 df2164c0 df3adbe0 c02a86ac dfe512a0
       00000340 c0136ea5 df2164c0 df3adbe0 df2164c0 df3adbe0 ffffffe9 
dffe7320
       00000000 c0136ff6 dfe512a0 df2164c0 df3adbe0 df2164c0 df3adbe0 
df2164c0
Call Trace: [<c01a3b55>] [<c0136ea5>] [<c0136ff6>] [<c0130f15>] [<c0130e2a>]
   [<c013115a>] [<c0106bb3>]
Code: 83 7b 0c 00 75 19 68 f0 01 00 00 68 00 00 01 00 e8 38 18 8d
Quoted 1 line
Quoted 1 line

EIP; e2857542 <[ide-cd]ide_cdrom_open+1e/70> <=====

Trace; c01a3b54 <ide_open+cc/fc>
Trace; c0136ea4 <do_open+94/14c>
Trace; c0136ff6 <blkdev_open+22/28>
Trace; c0130f14 <dentry_open+e0/188>
Trace; c0130e2a <filp_open+52/5c>
Trace; c013115a <sys_open+36/98>
Trace; c0106bb2 <system_call+32/38>
Code; e2857542 <[ide-cd]ide_cdrom_open+1e/70>
00000000 <_EIP>:
Code; e2857542 <[ide-cd]ide_cdrom_open+1e/70> <=====
0: 83 7b 0c 00 cmpl $0x0,0xc(%ebx) <=====
Code; e2857546 <[ide-cd]ide_cdrom_open+22/70>
4: 75 19 jne 1f <_EIP+0x1f> e2857560
<[ide-cd]ide_cdrom_open+3c/70>
Code; e2857548 <[ide-cd]ide_cdrom_open+24/70>
6: 68 f0 01 00 00 push $0x1f0
Code; e285754c <[ide-cd]ide_cdrom_open+28/70>
b: 68 00 00 01 00 push $0x10000
Code; e2857552 <[ide-cd]ide_cdrom_open+2e/70>
10: e8 38 18 8d 00 call 8d184d <_EIP+0x8d184d>
e3128d8e <END_OF_CODE+8ce4f0/????>

Cheers,
Roberto Nibali, ratz

#2 lore raw
In reply to Jens Axboe
Quoted 1 line · Jens Axboe
# ./mtr -d /dev/hdc -f full

You may need to rename this biary since I think some other tools use this name also...

#3 lore raw
Quoted 5 lines · Jens Axboe
Quoted 1 line
# ./mtr -d /dev/hdc -f full

You may need to rename this biary since I think some other tools use this name also...

Yes - mtr is a graphical/text mode trace route/ping/etc tool

author
#4 lore raw
In reply to Mike Fedyk
Quoted 7 lines · Alan Cox
Quoted 5 lines · Jens Axboe
Quoted 1 line
# ./mtr -d /dev/hdc -f full

You may need to rename this biary since I think some other tools use this name also...

Yes - mtr is a graphical/text mode trace route/ping/etc tool

Ok for the record, I've gotten 10 of these now. Does anyone have constructive comments besides the name (which I have changed now)? :-)

Signature · 1 line
Jens Axboe