Index: linux-2.6.25-source/drivers/scsi/coscsi.c
===================================================================
--- /dev/null
+++ linux-2.6.25-source/drivers/scsi/coscsi.c
@@ -0,0 +1,1175 @@
+/*
+ *  Copyright (C) 2008 Steve Shoecraft
+ *
+ *  Cooperative Linux SCSI Driver implementation
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/dma-mapping.h>
+#include <asm/string.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include <linux/cooperative.h>
+#include <linux/cooperative_internal.h>
+#include <linux/cooperative_pci.h>
+
+#include <linux/cdrom.h>
+#include <scsi/coscsi.h>
+
+/* Special pass through type */
+#define TYPE_PASS 0x1f
+
+#define COSCSI_VERSION "1.01"
+
+MODULE_AUTHOR("Steve Shoecraft <sshoecraft@earthlink.net>");
+MODULE_DESCRIPTION("Cooperative Linux SCSI Driver " COSCSI_VERSION);
+MODULE_LICENSE("GPL");
+
+#define COSCSI_DUMP_STATS 0
+#define COSCSI_DUMP_CONFIG 0
+#define COSCSI_DUMP_PARAMS 0
+
+#ifdef min
+#undef min
+#endif
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+/* Keep sg size to <= 1 page */
+#define COSCSI_SGSIZE ( 4096 / sizeof(struct scatterlist) )
+
+#define COSCSI_DEBUG 0
+#define COSCSI_DEBUG_PCI 0
+#define COSCSI_DEBUG_ISR 0
+#define COSCSI_DEBUG_HOST 0
+#define COSCSI_DEBUG_XFER 0
+#define COSCSI_DEBUG_COMM 0
+#define COSCSI_DEBUG_INQ 0
+#define COSCSI_DEBUG_SENSE 0
+#define COSCSI_DEBUG_PASS 0
+
+#if COSCSI_DEBUG_XFER || COSCSI_DEBUG_COMM || COSCSI_DEBUG_SENSE
+#define DUMP_DATA 1
+#else
+#define DUMP_DATA 0
+#endif
+
+/* OPs not found in scsi.h, use from cdrom.h */
+#define GET_CONFIGURATION	GPCMD_GET_CONFIGURATION
+#define GET_EVENT_STATUS	GPCMD_GET_EVENT_STATUS_NOTIFICATION
+#define READ_DISC_INFO		GPCMD_READ_DISC_INFO
+
+/* Sense codes */
+#define LOGICAL_UNIT_NOT_READY 0x4
+#define INVALID_FIELD_IN_CDB 0x24
+#define MEDIUM_NOT_PRESENT 0x3a
+
+#include "coscsi_rom.h"
+
+struct coscsi_device {
+	int unit;
+	int type;
+	coscsi_rom_t *rom;
+	unsigned long flags;
+	unsigned long long max_lba;
+	unsigned long long size;
+	void *os_handle;
+	int prevent;
+	int key;
+	int asc;
+	int asq;
+	int debug;
+	char msg[192];
+};
+typedef struct coscsi_device coscsi_device_t;
+
+/* Device flags */
+enum DEVICE_FLAG {
+	DFLAG_NONE,
+	DFLAG_DEBUG,			/* Allow debugging output for this dev */
+	DFLAG_OPEN,			/* Prevent medium removal */
+	DFLAG_CHECK,			/* Check condition */
+	DFLAG_PREVENT,			/* Prevent medium removal */
+	DFLAG_EVENT,			/* Outstanding Event */
+};
+
+#define DFLAG(d,f) ((d->flags & f) != 0)
+#define dprintk(m) if (DFLAG(dp, DFLAG_DEBUG)) printk(KERN_INFO "scsi%d: ", dp->unit), printk(KERN_INFO m)
+
+struct coscsi_worker {
+	coscsi_device_t *dp;
+	struct scsi_cmnd *scp;
+};
+typedef struct coscsi_worker coscsi_worker_t;
+
+/* Private info */
+char scsi_rev[5];
+static coscsi_device_t devices[CO_MODULE_MAX_COSCSI];
+
+#if DUMP_DATA
+static void _dump_data(int unit, char *str, void *data, int data_len) {
+	unsigned char *p;
+	int x,y,len;
+
+	printk(KERN_INFO "scsi%d: %s(%d bytes):\n",unit,str,data_len);
+	len = data_len;
+	p = data;
+	for(x=y=0; x < len; x++) {
+		printk(KERN_INFO " %02x", p[x]);
+		y++;
+		if (y > 15) {
+			printk(KERN_INFO "\n");
+			y = 0;
+		}
+	}
+	if (y) printk(KERN_INFO "\n");
+}
+#define dump_data(u, s,a,b) _dump_data(u,s,a,b)
+#else
+#define dump_data(u, s,a,b) /* noop */
+#endif
+
+static spinlock_t coscsi_isr_lock;
+
+static irqreturn_t coscsi_isr(int irq, void *dev_id)
+{
+	co_message_node_t *node_message;
+	co_linux_message_t *message;
+	co_scsi_intr_t *info;
+	struct scsi_cmnd *scp;
+
+	spin_lock(&coscsi_isr_lock);
+#if COSCSI_DEBUG_ISR
+	printk(KERN_INFO "coscsi_isr: getting messages!\n");
+#endif
+	while (co_get_message(&node_message, CO_DEVICE_SCSI)) {
+
+		message = (co_linux_message_t *)&node_message->msg.data;
+
+		info = (co_scsi_intr_t *) &message->data;
+		scp = info->ctx;
+		scp->result = info->result;
+		scsi_set_resid(scp, info->delta);
+#if COSCSI_DEBUG_ISR
+		printk(KERN_INFO "coscsi_isr: scp: %p result: %d, delta: %d\n", scp, info->result, info->delta);
+#endif
+		scp->scsi_done(scp);
+		co_free_message(node_message);
+	}
+	spin_unlock(&coscsi_isr_lock);
+
+	return IRQ_HANDLED;
+}
+
+/****************************************************************************************************
+ *
+ *
+ * HOST functions
+ *
+ *
+ ****************************************************************************************************/
+
+/*
+ * Open handle
+*/
+static int host_open(coscsi_device_t *dp) {
+	unsigned long flags;
+	int rc = 0;
+
+#if COSCSI_DEBUG_HOST
+	if (dp->debug) printk(KERN_INFO "host_open: handle: %p\n", dp->os_handle);
+#endif
+	if (!dp->os_handle) {
+		co_passage_page_assert_valid();
+		co_passage_page_acquire(&flags);
+		co_passage_page->operation = CO_OPERATION_DEVICE;
+		co_passage_page->params[0] = CO_DEVICE_SCSI;
+		co_passage_page->params[1] = CO_SCSI_OPEN;
+		co_passage_page->params[2] = dp->unit;
+
+		co_switch_wrapper();
+
+		rc = co_passage_page->params[0];
+		if (!rc) dp->os_handle = (void *) co_passage_page->params[1];
+		co_passage_page_release(flags);
+	}
+
+#if COSCSI_DEBUG_HOST
+	if (dp->debug) printk(KERN_INFO "host_open: rc: %d, handle: %p\n", rc, dp->os_handle);
+#endif
+	if (rc) printk(KERN_ERR "coscsi%d: unable to open device! rc: %x\n", dp->unit, rc);
+	return rc;
+}
+
+/*
+ * Close handle
+*/
+static int host_close(coscsi_device_t *dp) {
+	unsigned long flags;
+	int rc;
+
+#if COSCSI_DEBUG_HOST
+	if (dp->debug) printk(KERN_INFO "host_close: handle: %p\n", dp->os_handle);
+#endif
+	if (!dp->os_handle) return 0;
+	co_passage_page_assert_valid();
+	co_passage_page_acquire(&flags);
+	co_passage_page->operation = CO_OPERATION_DEVICE;
+	co_passage_page->params[0] = CO_DEVICE_SCSI;
+	co_passage_page->params[1] = CO_SCSI_CLOSE;
+	co_passage_page->params[2] = dp->unit;
+
+	co_switch_wrapper();
+
+	rc = co_passage_page->params[0];
+	co_passage_page_release(flags);
+
+	dp->os_handle = 0;
+#if COSCSI_DEBUG_HOST
+	if (dp->debug) printk(KERN_INFO "host_close: rc: %d\n", rc);
+#endif
+	return rc;
+}
+
+#if COSCSI_DUMP_STATS
+static unsigned int max_segs = 1;
+static unsigned int max_xfer = 4096;
+#endif
+
+/*
+ * Read/Write block(s)
+*/
+
+static int host_rw(coscsi_worker_t *wp, unsigned long long lba, unsigned long num, int write)
+{
+	struct scatterlist *sg = scsi_sglist(wp->scp);
+	struct scsi_cmnd *scp = wp->scp;
+	unsigned long flags;
+	co_scsi_io_t *iop;
+	int count,rc,total;
+
+#if COSCSI_DEBUG_HOST
+	if (wp->dp->debug) printk(KERN_INFO "host_rw: lba: %lld, sector_size: %d, num: %ld, write: %d\n",
+		lba, scp->device->sector_size, num, write);
+#endif
+
+	if (!wp->dp->os_handle) {
+		if (host_open(wp->dp))
+			return 1;
+	}
+
+	/* XXX needed when clustering is enabled */
+	count = dma_map_sg(&scp->device->host->shost_gendev, sg, scsi_sg_count(scp), scp->sc_data_direction);
+
+	/* Get passage page */
+	co_passage_page_assert_valid();
+	co_passage_page_acquire(&flags);
+	co_passage_page->operation = CO_OPERATION_DEVICE;
+	co_passage_page->params[0] = CO_DEVICE_SCSI;
+	co_passage_page->params[1] = CO_SCSI_IO;
+	co_passage_page->params[2] = wp->dp->unit;
+
+	/* Setup iop */
+	iop = (co_scsi_io_t *) &co_passage_page->params[3];
+	iop->scp = scp;
+	iop->offset = lba * scp->device->sector_size;
+	iop->count = count;
+	iop->write = write;
+	iop->sg = scsi_sglist(scp);
+	iop->reqlen = total = num * scp->device->sector_size;
+
+	/* Do it */
+	co_switch_wrapper();
+
+	rc = co_passage_page->params[0];
+	co_passage_page_release(flags);
+
+	dma_unmap_sg(&scp->device->host->shost_gendev, sg, scsi_sg_count(scp), scp->sc_data_direction);
+
+#if COSCSI_DUMP_STATS
+	if (rc == GOOD) {
+		if (count > max_segs) {
+			max_segs = count;
+			printk(KERN_WARN "COSCSI: max_segs: %d\n", max_segs);
+		}
+
+		if (total > max_xfer) {
+			max_xfer = total;
+			printk(KERN_WARN "COSCSI: max_xfer: %dKB\n", max_xfer >> 10);
+		}
+	}
+#endif
+
+#if COSCSI_DEBUG_HOST
+	if (wp->dp->debug) printk(KERN_INFO "host_rw: rc: %d\n", rc);
+#endif
+	return rc;
+}
+
+static int get_bs_bits(coscsi_device_t *dp, int sector_size) {
+	unsigned long mask = 0x80000000;
+	int bs_bits;
+
+	bs_bits = 31;
+	while(mask) {
+		if (sector_size & mask)
+			break;
+		mask >>= 1;
+		bs_bits--;
+	}
+
+	return bs_bits;
+}
+
+/*
+ * File/Device size
+*/
+static int host_size(coscsi_device_t *dp, struct scsi_cmnd *scp) {
+	unsigned long long s;
+	unsigned long flags;
+	int rc, bits;
+
+#if COSCSI_DEBUG_HOST
+	if (dp->debug) printk(KERN_INFO "host_size: getting size...\n");
+#endif
+	if (!dp->os_handle) {
+		if (host_open(dp))
+			return 1;
+	}
+
+	co_passage_page_assert_valid();
+	co_passage_page_acquire(&flags);
+	co_passage_page->operation = CO_OPERATION_DEVICE;
+	co_passage_page->params[0] = CO_DEVICE_SCSI;
+	co_passage_page->params[1] = CO_SCSI_SIZE;
+	co_passage_page->params[2] = dp->unit;
+
+	co_switch_wrapper();
+
+	rc = co_passage_page->params[0];
+	dp->size = *((unsigned long long *)&co_passage_page->params[1]);
+	co_passage_page_release(flags);
+
+	bits = get_bs_bits(dp, scp->device->sector_size);
+
+	s = dp->size >> bits;
+	s *= scp->device->sector_size;
+	if (s < dp->size) s += scp->device->sector_size;
+
+	dp->max_lba = (s >> bits) - 1;
+
+#if COSCSI_DEBUG_HOST
+	if (dp->debug) printk(KERN_INFO "host_size: rc: %d, size: %lld, max_lba: %lld\n", rc, dp->size, dp->max_lba);
+#endif
+
+	return rc;
+}
+
+/*
+ * Pass-through
+*/
+static int host_pass(coscsi_device_t *dp, struct scsi_cmnd *scp) {
+	unsigned long flags;
+	void *buffer;
+	unsigned long buflen;
+	co_scsi_pass_t *pass;
+	int rc;
+
+	if (!dp->os_handle) {
+		if (host_open(dp))
+			return 1;
+	}
+
+	/* Scatter/Gather */
+	if (scsi_sg_count(scp)) {
+		struct scatterlist *sg;
+
+		/* Should never be more than 1 for non r/w transfers */
+		if (scsi_sg_count(scp) > 1) panic("COSCSI: host_pass: use_sg (%d) > 1!\n", scsi_sg_count(scp));
+
+		sg = scsi_sglist(scp);
+#if COSCSI_DEBUG_HOST
+		if (dp->debug) printk(KERN_INFO "host_pass: sg: page: %p, offset: %d, length: %d\n",
+			sg_page(sg), sg->offset, sg->length);
+#endif
+		buffer = sg_virt(sg);
+		buflen = sg->length;
+	/* Direct */
+	} else {
+		buffer = scsi_sglist(scp);
+		buflen = scsi_bufflen(scp);
+	}
+
+	co_passage_page_assert_valid();
+	co_passage_page_acquire(&flags);
+	co_passage_page->operation = CO_OPERATION_DEVICE;
+	co_passage_page->params[0] = CO_DEVICE_SCSI;
+	co_passage_page->params[1] = CO_SCSI_PASS;
+	co_passage_page->params[2] = dp->unit;
+
+	pass = (co_scsi_pass_t *) &co_passage_page->params[3];
+	memcpy(&pass->cdb, &scp->cmnd, 16);
+	pass->cdb_len = scp->cmd_len;
+	pass->write = (scp->sc_data_direction == DMA_TO_DEVICE);
+	pass->buffer = buffer;
+	pass->buflen = buflen;
+
+	co_switch_wrapper();
+
+	rc = co_passage_page->params[0];
+	co_passage_page_release(flags);
+
+#if COSCSI_DEBUG_PASS
+	if (rc == GOOD && dp->debug) dump_data(dp->unit, "host_pass", buffer, buflen);
+#endif
+
+#if COSCSI_DEBUG_HOST
+	if (dp->debug) printk(KERN_INFO "host_pass: rc: %d\n", rc);
+#endif
+
+	return rc;
+}
+
+/****************************************************************************************************
+ *
+ *
+ * SCSI functions
+ *
+ *
+ ****************************************************************************************************/
+
+static int check_condition(struct coscsi_device *dp, int key, int asc, int asq) {
+	dp->key = key;
+	dp->asc = asc;
+	dp->asq = asq;
+	return CHECK_CONDITION;
+}
+
+static int response(coscsi_worker_t *wp, void *data, int len) {
+	struct scsi_cmnd *scp = wp->scp;
+	void *buffer;
+	unsigned long buflen;
+	int act_len;
+
+	/* Scatter/Gather */
+	if (scsi_sg_count(scp)) {
+		struct scatterlist *sg;
+		int i;
+
+		/* scatter-gather list too long? */
+		BUG_ON(scsi_sg_count(scp) > COSCSI_SGSIZE);
+
+		scsi_for_each_sg(scp, sg, scsi_sg_count(scp), i) {
+#if COSCSI_DEBUG
+			if (wp->dp->debug) printk(KERN_INFO "response: sg: page: %p, offset: %d, length: %d\n",
+				sg_page(sg), sg->offset, sg->length);
+#endif
+			buffer = sg_virt(sg);
+			buflen = sg->length;
+			act_len = min(buflen, len);
+#if COSCSI_DEBUG_COMM
+			if (wp->dp->debug) dump_data(wp->dp->unit, "response", data, act_len);
+#endif
+			memcpy(buffer, data, act_len);
+			data += act_len;
+			len -= act_len;
+		}
+	/* Direct */
+	} else {
+		buffer = scsi_sglist(scp);
+		buflen = scsi_bufflen(scp);
+		if (!buflen) return GOOD;
+		act_len = min(buflen, len);
+#if COSCSI_DEBUG_COMM
+		if (wp->dp->debug) dump_data(wp->dp->unit, "response", data, act_len);
+#endif
+		memcpy(buffer, data, act_len);
+	}
+
+	return GOOD;
+}
+
+static int unit_ready(coscsi_worker_t *wp) {
+	int error, rc;
+
+	rc = GOOD;
+	error = (wp->dp->os_handle == 0 ? host_open(wp->dp) : GOOD);
+	if (error) {
+		switch(wp->dp->type) {
+		case TYPE_ROM:
+		case TYPE_TAPE:
+			rc = check_condition(wp->dp, NOT_READY, MEDIUM_NOT_PRESENT, 0x2);
+			break;
+		default:
+			rc = check_condition(wp->dp, NOT_READY, LOGICAL_UNIT_NOT_READY, 0x2);
+			break;
+		}
+	}
+
+	return rc;
+}
+
+static int inquiry(coscsi_worker_t *wp) {
+	int x, alloc_len;
+	struct scsi_cmnd *scp = wp->scp;
+
+	alloc_len = (scp->cmnd[3] << 8) + scp->cmnd[4];
+#if COSCSI_DEBUG_INQ
+	if (wp->dp->debug) printk(KERN_INFO "scsi_inq: alloc_len: %d, buflen: %d\n", alloc_len, scsi_bufflen(scp));
+#endif
+
+	/* EVPD? */
+	if (scp->cmnd[1] & 1) {
+		coscsi_page_t *vpd = wp->dp->rom->vpd;
+		int page = scp->cmnd[2];
+
+#if COSCSI_DEBUG_INQ
+		if (wp->dp->debug) printk(KERN_INFO "scsi_inq: sending VPD page %d\n", page);
+#endif
+		/* For page 00, generate dynamically */
+		if (page == 0) {
+			unsigned char data[32];
+			int i;
+
+			memset(data, 0, sizeof(data));
+			data[0] = wp->dp->rom->std.page[0];
+			i = 4;
+			for(x=0; vpd[x].page; x++) data[i++] = vpd[x].num;
+			data[3] = i - 3;
+
+			return response(wp, data, min(alloc_len, sizeof(data)));
+		} else {
+			for(x=0; vpd[x].page; x++) {
+				if (vpd[x].num == page)
+					return response(wp, vpd[x].page, min(alloc_len, vpd[x].size));
+			}
+			return check_condition(wp->dp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+		}
+
+	/* Standard page */
+	} else {
+		unsigned char *std = wp->dp->rom->std.page;
+
+#if COSCSI_DEBUG_INQ
+		printk(KERN_INFO "scsi_inq: sending STD page\n");
+#endif
+		strcpy((char *)&std[8], "coLinux");
+		std[1] = ((wp->dp->type == TYPE_ROM || wp->dp->type == TYPE_TAPE) ? 0x80 : 0);
+		memcpy(&std[16], wp->dp->rom->name, strlen(wp->dp->rom->name)+1);
+		memcpy(&std[32], scsi_rev, min(4, strlen(scsi_rev)+1));
+		return response(wp, std, min(alloc_len, wp->dp->rom->std.size));
+	}
+}
+
+static int read_capacity(coscsi_worker_t *wp) {
+	coscsi_device_t *dp = wp->dp;
+	struct scsi_cmnd *scp = wp->scp;
+
+	/* Get the size */
+	if (host_size(dp, scp)) return check_condition(dp, HARDWARE_ERROR, 0x3e, 1);
+
+	/* Convert to read_capacity format */
+	if (dp->max_lba > 0xfffffffe || scp->cmnd[8] & 1) {
+		dp->msg[0] = 0xff;
+		dp->msg[1] = 0xff;
+		dp->msg[2] = 0xff;
+		dp->msg[3] = 0xff;
+	} else {
+		dp->msg[0] = (wp->dp->max_lba >> 24);
+		dp->msg[1] = (wp->dp->max_lba >> 16) & 0xff;
+		dp->msg[2] = (wp->dp->max_lba >> 8) & 0xff;
+		dp->msg[3] = wp->dp->max_lba & 0xff;
+	}
+	dp->msg[4] = (scp->device->sector_size >> 24);
+	dp->msg[5] = (scp->device->sector_size >> 16) & 0xff;
+	dp->msg[6] = (scp->device->sector_size >> 8) & 0xff;
+	dp->msg[7] = scp->device->sector_size & 0xff;
+
+	return response(wp, &dp->msg, 8);
+}
+
+static int mode_sense(coscsi_worker_t *wp) {
+	unsigned char data[256],*ap;
+	int offset, bd_len, page;
+	coscsi_page_t *pages = wp->dp->rom->mode;
+	coscsi_device_t *dp = wp->dp;
+	struct scsi_cmnd *scp = wp->scp;
+	register int x;
+
+	memset(data, 0, sizeof(data));
+	offset = 4;
+	bd_len = 8;
+
+	data[2] = 0x10; /* DPOFUA */
+	data[3] = bd_len;
+
+	ap = data + offset;
+	if (dp->max_lba > 0xfffffffe) {
+		ap[0] = 0xff;
+		ap[1] = 0xff;
+		ap[2] = 0xff;
+		ap[3] = 0xff;
+	} else {
+		ap[0] = (dp->max_lba >> 24) & 0xff;
+		ap[1] = (dp->max_lba >> 16) & 0xff;
+		ap[2] = (dp->max_lba >> 8) & 0xff;
+		ap[3] = dp->max_lba & 0xff;
+	}
+	ap[5] = (scp->device->sector_size >> 16) & 0xff;
+	ap[6] = (scp->device->sector_size >> 8) & 0xff;
+	ap[7] = scp->device->sector_size & 0xff;
+
+	offset += bd_len;
+	ap = data + offset;
+#if COSCSI_DEBUG_SENSE
+	if (dp->debug) printk(KERN_INFO "mode_sense: ap: %p, offset: %d\n", ap, offset);
+#endif
+	page = scp->cmnd[2] & 0x3f;
+	if (page == 0x3f) {
+		/* All pages */
+		if (scp->cmnd[3] == 0 || scp->cmnd[3] == 0xFF) {
+			for(x=0; pages[x].page; x++) {
+#if COSCSI_DEBUG_SENSE
+				if (dp->debug) dump_data(dp->unit, "page", pages[x].page, pages[x].size);
+#endif
+				memcpy(ap, pages[x].page, pages[x].size);
+				ap += pages[x].size;
+				offset += pages[x].size;
+			}
+		} else {
+			return check_condition(wp->dp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+		}
+	} else {
+		/* Specific page */
+		int found = 0;
+		for(x=0; pages[x].page; x++) {
+#if COSCSI_DEBUG_SENSE
+			if (dp->debug) printk(KERN_INFO "mode_sense: pages[%d].num: %d, page: %d\n", x, pages[x].num, page);
+#endif
+			if (pages[x].num == page) {
+#if COSCSI_DEBUG_SENSE
+				if (dp->debug) dump_data(dp->unit, "page", pages[x].page, pages[x].size);
+#endif
+				memcpy(ap, pages[x].page, pages[x].size);
+				offset += pages[x].size;
+				found = 1;
+				break;
+			}
+		}
+		if (!found) return check_condition(wp->dp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+	}
+#if COSCSI_DEBUG_SENSE
+	if (dp->debug) printk(KERN_INFO "scsi_mode_sense: offset: %d\n", offset);
+#endif
+	data[0] = offset - 1;
+	return response(wp, data, min(scp->cmnd[4], offset));
+}
+
+/*
+The Logical Block Address field contains the LBA of the first block from which data shall be returned. If the
+Logical Block Address is beyond the range of recorded data, the Drive shall terminate the command with
+CHECK CONDITION status and SK/ASC/ASCQ values shall be set to ILLEGAL REQUEST/LOGICAL BLOCK
+ADDRESS OUT OF RANGE.
+*/
+
+static int read_write(coscsi_worker_t *wp) {
+	unsigned long long lba;
+	unsigned long num;
+	register unsigned char *p = wp->scp->cmnd;
+
+	lba = num = 0;
+	switch(*p) {
+	case READ_16:
+	case WRITE_16:
+		{
+		register int x;
+
+		for (x = 0; x < 8; x++) {
+			if (x) lba <<= 8;
+			lba |= *(p+2+x);
+		}
+		num = *(p+10) << 24 | *(p+11) << 16 | *(p+12) << 8 | *(p+13);
+		}
+		break;
+	case READ_12:
+	case WRITE_12:
+		lba = *(p+2) << 24 | *(p+3) << 16 | *(p+4) << 8 | *(p+5);
+		num = *(p+6) << 24 | *(p+7) << 16 | *(p+8) << 8 | *(p+9);
+		break;
+	case READ_10:
+	case WRITE_10:
+		lba = *(p+2) << 24 | *(p+3) << 16 | *(p+4) << 8 | *(p+5);
+		num = *(p+7) << 8 | *(p+8);
+		break;
+	case READ_6:
+	case WRITE_6:
+		lba = (*(p+1) & 0x1f) << 16 | *(p+2) << 8 | *(p+3);
+		num = *(p+4) ? *(p+4) : 0xff;
+		break;
+	default:
+		printk(KERN_ERR "scsi%d: read_write: unknown opcode: %x\n", wp->dp->unit, *p);
+		return check_condition(wp->dp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+	}
+
+#if COSCSI_DEBUG_XFER
+	if (wp->dp->debug) printk(KERN_INFO "read_write: lba: %lld, num: %ld\n", lba, num);
+#endif
+
+	if (host_rw(wp, lba, num, (wp->scp->cmnd[0] & 2) >> 1))
+		return check_condition(wp->dp, HARDWARE_ERROR, 0x3e, 1);
+	else
+		return GOOD;
+}
+
+static int request_sense(coscsi_worker_t *wp) {
+	coscsi_device_t *dp = wp->dp;
+
+	if (wp->scp->cmnd[1] & 1) {
+		dp->msg[0] = 0x72;
+		dp->msg[1] = dp->key;
+		dp->msg[2] = dp->asc;
+		dp->msg[3] = dp->asq;
+	} else {
+		dp->msg[0] = 0x70;
+		dp->msg[2] = dp->key;
+		dp->msg[7] = 0xa;
+		dp->msg[12] = dp->asc;
+		dp->msg[13] = dp->asq;
+	}
+	return response(wp, &dp->msg, min(wp->scp->cmnd[4], 18));
+}
+
+static int prevent_allow(coscsi_worker_t *wp) {
+	wp->dp->prevent = wp->scp->cmnd[4] & 1;
+	return GOOD;
+}
+
+static int get_config(coscsi_worker_t *wp) {
+	char buf[] = {
+		0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x10,
+		0x00,0x00,0x03,0x08,0x00,0x10,0x01,0x00,
+		0x00,0x08,0x00,0x00,0x00,0x01,0x03,0x04,
+		0x00,0x00,0x00,0x01,0x00,0x02,0x03,0x04
+	};
+
+	return response(wp, buf, sizeof(buf));
+}
+
+static int read_toc(coscsi_worker_t *wp) {
+	int msf = ((wp->scp->cmnd[1] >> 1) & 1);
+	int len = wp->scp->cmnd[7] << 8 | wp->scp->cmnd[8];
+	int start;
+	unsigned char data[12];
+
+	/* We only support format 0 when MSF is set */
+	if (msf && wp->scp->cmnd[2] & 0x0f) return check_condition(wp->dp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+
+	start = (msf ? 32 : 0);
+
+	/* TOC header */
+	data[0] = 0;		/* Len MSB */
+	data[1] = sizeof(data);	/* Len LSB */
+	data[2] = 1;		/* 1st track */
+	data[3] = 1;		/* Last track */
+
+	/* Track 1 descriptor */
+	data[4] = 0;		/* Reserved */
+	data[5] = 0x14;		/* ADR & CONTROL */
+	data[6] = 1;		/* Track # */
+	data[7] = 0;		/* Reserved */
+	data[8] = (start >> 24) & 0xff;	/* Start */
+	data[9] = (start >> 16) & 0xff;
+	data[10] = (start >> 8) & 0xff;
+	data[11] = start & 0xff;
+
+	return response(wp, data, min(len, sizeof(data)));
+}
+
+/*
+The Polled bit is used to select operational mode. When Polled is set to zero, the Host is requesting
+asynchronous operation. If the Drive does not support asynchronous operation, the command shall be
+terminated with CHECK CONDITION status and the values for SK/ASC/ASCQ shall be set to ILLEGAL
+REQUEST/INVALID FIELD IN CDB.
+Note 12. If Polled is zero while a Group 2 timeout command is executing, the GET EVENT STATUS
+NOTIFICATION command may be queued, but it never terminates.
+When Polled is set to one, the Host is requesting polled operation. The Drive shall return event information for
+the highest priority requested event. If no event has occurred, the Drive shall report the .No Change. event for
+the highest priority requested event class.
+*/
+
+static int event_status(coscsi_worker_t *wp) {
+	return check_condition(wp->dp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+}
+
+static int coscsi_queue(struct scsi_cmnd *scp, void (*done)(struct scsi_cmnd *)) {
+	coscsi_device_t *dp;
+	coscsi_worker_t worker;
+	int rc;
+
+#if COSCSI_DEBUG
+	printk(KERN_INFO "coscsi_queue: id: %d, lun: %d, cdb[0]: 0x%02x\n",
+		scp->device->id, scp->device->lun, scp->cmnd[0]);
+#endif
+
+	/* Get device pointer */
+	dp = &devices[scp->device->id];
+
+#if COSCSI_DEBUG_COMM
+	if (dp->debug) dump_data(dp->unit, "request", &scp->cmnd, sizeof(scp->cmnd));
+#endif
+
+	/* Setup worker */
+	worker.dp = dp;
+	worker.scp = scp;
+
+	/* Do we have the requested device? */
+	if ((scp->device->id >= CO_MODULE_MAX_COSCSI) || (dp->rom == 0)) {
+		if (scp->cmnd[0] == INQUIRY) {
+			char temp[96];
+			memset(temp,0,sizeof(temp));
+			temp[0] = 0x7f;
+			temp[3] = 2;
+			temp[4] = 92;
+			scp->result = response(&worker, temp, min(scp->cmnd[4],96));
+		} else
+			scp->result = check_condition(dp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+		goto req_done;
+	}
+
+	/* Set done for async funcs */
+	scp->scsi_done = done;
+
+	/* Pass-through? */
+	if (dp->type == SCSI_PTYPE_PASS) {
+		switch(scp->cmnd[0]) {
+		case READ_6:
+		case READ_10:
+		case READ_16:
+		case WRITE_6:
+		case WRITE_10:
+		case WRITE_16:
+			/* r/w may be async */
+			rc = read_write(&worker);
+#if COSCSI_ASYNC
+			if (rc == GOOD) goto req_out;
+#endif
+			break;
+		default:
+			rc = host_pass(dp, scp);
+			break;
+		}
+		scp->result = rc;
+		goto req_done;
+	}
+
+	/* Process command */
+	switch(scp->cmnd[0]) {
+	case INQUIRY:
+		scp->result = inquiry(&worker);
+		break;
+	case TEST_UNIT_READY:
+		scp->result = unit_ready(&worker);
+		break;
+	case REQUEST_SENSE:
+		scp->result = request_sense(&worker);
+		break;
+	case READ_CAPACITY:
+		scp->result = read_capacity(&worker);
+		break;
+	case REPORT_LUNS:
+		/* We only support 1 lun right now */
+		memset(dp->msg, 0, 16);
+		dp->msg[3] = 1;
+		scp->result = response(&worker, &dp->msg, 16);
+		break;
+	case MODE_SENSE:
+		scp->result = mode_sense(&worker);
+		break;
+	case ALLOW_MEDIUM_REMOVAL:
+		scp->result = prevent_allow(&worker);
+		break;
+	case READ_TOC:
+		scp->result = read_toc(&worker);
+		break;
+	case GET_CONFIGURATION:
+		scp->result = get_config(&worker);
+		break;
+	case GET_EVENT_STATUS:
+		scp->result = event_status(&worker);
+		break;
+	case READ_6:
+	case READ_10:
+	case READ_16:
+	case WRITE_6:
+	case WRITE_10:
+	case WRITE_16:
+		/* r/w may be async */
+		rc = read_write(&worker);
+#if COSCSI_ASYNC
+		if (rc == GOOD) goto req_out;
+#endif
+		scp->result = rc;
+		break;
+	case SYNCHRONIZE_CACHE:
+		scp->result = GOOD;
+		break;
+	case READ_DISC_INFO:
+		{
+			disc_information di = { 0, };
+
+			di.disc_information_length = cpu_to_be16(1);
+			/* di.erasable = 0; */
+			scp->result = response(&worker, &di, sizeof(di.disc_information_length) + 1);
+		}
+		break;
+	default:
+		printk(KERN_NOTICE "scsi%d: unhandled opcode: %x\n", dp->unit, scp->cmnd[0]);
+		scp->result = check_condition(dp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+	}
+
+req_done:
+	done(scp);
+#if COSCSI_ASYNC
+req_out:
+#endif
+#if COSCSI_DEBUG_COMM
+	if (dp->debug) printk(KERN_INFO "coscsi_queue: scp->result: %02x (code: %x)\n", scp->result, scp->result & 0xffff);
+	if (dp->debug) printk(KERN_INFO "------------------------------------------------------------------------\n");
+#endif
+	return 0;
+}
+
+static int coscsi_config(struct scsi_device *sdev) {
+	switch(sdev->type) {
+	case TYPE_ROM:
+	case TYPE_WORM:
+		/* XXX required to get rid of "unaligned transfer" errors */
+		blk_queue_hardsect_size(sdev->request_queue, 2048);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+struct scsi_host_template coscsi_template = {
+	.module			= THIS_MODULE,
+	.name			= "Cooperative Linux SCSI Adapter",
+	.proc_name		= "coscsi",
+	.queuecommand		= coscsi_queue,
+	.slave_configure	= coscsi_config,
+	.this_id		= -1,
+	.sg_tablesize		= COSCSI_SGSIZE,
+	.max_sectors		= 0xFFFF,
+	.can_queue		= 65535,
+	.cmd_per_lun		= 2048,
+	.use_clustering		= ENABLE_CLUSTERING,
+	.skip_settle_delay	= 1,
+	.max_host_blocked	= 1,
+};
+
+/****************************************************************************************************
+ *
+ *
+ * PCI functions
+ *
+ *
+ ****************************************************************************************************/
+
+/*
+ * PCI Probe - probe for a single device
+*/
+static int __devinit coscsi_pci_probe( struct pci_dev *pdev, const struct pci_device_id *ent )
+{
+	struct Scsi_Host *shost;
+	unsigned long flags;
+	coscsi_device_t *dp;
+	register int x;
+	int rc;
+
+#if COSCSI_DEBUG
+	printk(KERN_INFO "coscsi_pci_probe: adding host...\n");
+#endif
+
+	/* Get our config from the host */
+	memset(&devices, 0, sizeof(devices));
+	co_passage_page_acquire(&flags);
+	co_passage_page->operation = CO_OPERATION_DEVICE;
+	co_passage_page->params[0] = CO_DEVICE_SCSI;
+	co_passage_page->params[1] = CO_SCSI_GET_CONFIG;
+	co_passage_page->params[2] = 0;
+	co_switch_wrapper();
+
+	/* Get the result */
+	if (!co_passage_page->params[0]) {
+		for(x=0; x < CO_MODULE_MAX_COSCSI; x++) {
+			if ((co_passage_page->params[x+1] & COSCSI_DEVICE_ENABLED) == 0)
+				continue;
+			dp = &devices[x];
+			dp->unit = x;
+			dp->type = co_passage_page->params[x+1] & 0x1f;
+			dp->debug = 1;
+			switch(dp->type) {
+			case TYPE_DISK:
+				dp->rom = &disk_rom;
+				break;
+			case TYPE_ROM:
+			case TYPE_WORM:
+				dp->rom = &cd_rom;
+				break;
+			case TYPE_PASS:
+				dp->rom = (void *) ~0L;
+				break;
+			case TYPE_MEDIUM_CHANGER:
+//				dp->rom = &changer_rom;
+				break;
+			case TYPE_TAPE:
+//				dp->rom = &tape_rom;
+				break;
+			default:
+				dp->unit = -1;
+				break;
+			}
+		}
+	}
+
+	/* Release the page */
+	co_passage_page_release(flags);
+
+#if COSCSI_DUMP_CONFIG
+	printk(KERN_INFO "SCSI: device configuration:\n");
+	for(x=0; x < CO_MODULE_MAX_COSCSI; x++) {
+		dp = &devices[x];
+		printk(KERN_INFO "scsi%02d: type: %02d, rom: %p\n", dp->unit, dp->type, dp->rom);
+	}
+#endif
+
+	/* Get shost */
+	shost = scsi_host_alloc(&coscsi_template, sizeof(void *));
+	if (!shost) {
+		printk(KERN_ERR "coscsi_pci_probe: scsi_host_alloc failed");
+		return -ENOMEM;
+	}
+
+	/* Set params */
+	shost->irq = SCSI_IRQ;
+	shost->max_id = CO_MODULE_MAX_COSCSI;
+	shost->max_lun = 1;
+	shost->max_channel = 0;
+
+#if COSCSI_DUMP_PARAMS
+#define SDUMP(s,f) printk(KERN_INFO "  %16s: %d\n", #f, (s)->f)
+	printk(KERN_INFO "COSCSI: host parameters:\n");
+	SDUMP(shost,max_id);
+	SDUMP(shost,max_lun);
+	SDUMP(shost,max_channel);
+	SDUMP(shost,unique_id);
+	SDUMP(&coscsi_template,can_queue);
+	SDUMP(&coscsi_template,cmd_per_lun);
+	SDUMP(&coscsi_template,sg_tablesize);
+	SDUMP(&coscsi_template,max_sectors);
+	SDUMP(&coscsi_template,use_clustering);
+	SDUMP(shost,use_blk_tcq);
+	SDUMP(shost,reverse_ordering);
+	SDUMP(&coscsi_template,ordered_tag);
+	SDUMP(&coscsi_template,max_host_blocked);
+#undef SDUMP
+#endif
+
+	/* Add host */
+	rc = scsi_add_host(shost, &pdev->dev);
+	if (rc) {
+		printk(KERN_ERR "coscsi_pci_probe: scsi_add_host failed");
+		goto err_put;
+	}
+	pci_set_drvdata(pdev, shost);
+
+	/* Scan devs */
+	scsi_scan_host(shost);
+
+	return 0;
+
+err_put:
+	scsi_host_put(shost);
+	return rc;
+}
+
+/*
+ * PCI Remove - hotplug removal
+*/
+static void __devexit coscsi_pci_remove(struct pci_dev *pdev)
+{
+	pci_set_drvdata(pdev, NULL);
+}
+
+/* We only support the COSCSI adapter :) */
+static struct pci_device_id coscsi_pci_ids[] __devinitdata = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_CO, PCI_DEVICE_ID_COSCSI) },
+	{ 0 }
+};
+
+static struct pci_driver coscsi_pci_driver = {
+	.name		= "coscsi",
+	.id_table	= coscsi_pci_ids,
+	.probe		= coscsi_pci_probe,			/* FIXME: Use bus_type methods */
+	.remove		= __devexit_p(coscsi_pci_remove),	/* FIXME: Use bus_type methods */
+};
+
+extern int coio_test(void);
+
+/*
+ * PCI Init - module load
+*/
+static int __init coscsi_pci_init(void) {
+	int rc;
+
+	/* XXX COSCSI_VERSION better be <= 4 bytes */
+	strncpy(scsi_rev, COSCSI_VERSION, 4);
+
+	memset(&devices, 0, sizeof(devices));
+
+	rc = request_irq(SCSI_IRQ, &coscsi_isr, IRQF_SAMPLE_RANDOM, "coscsi", NULL);
+	if (rc) {
+		printk(KERN_ERR "coscsi_pci_init: unable to get irq %d", SCSI_IRQ);
+		return rc;
+	}
+	spin_lock_init(&coscsi_isr_lock);
+
+#if COSCSI_DEBUG_PCI
+	printk(KERN_INFO "coscsi_pci_init: registering...\n");
+#endif
+	return pci_register_driver(&coscsi_pci_driver);
+}
+
+/*
+ * PCI Exit - module unload
+*/
+static void __exit coscsi_pci_exit(void) {
+	register int x;
+
+#if COSCSI_DEBUG_PCI
+	printk(KERN_INFO "coscsi_pci_exit: closing handles\n");
+#endif
+
+	/* Close the handles */
+	for(x=0; x < CO_MODULE_MAX_COSCSI; x++) host_close(&devices[x]);
+
+	/* Unmap the page */
+
+#if COSCSI_DEBUG_PCI
+	printk(KERN_INFO "coscsi_pci_exit: exiting\n");
+#endif
+	pci_unregister_driver(&coscsi_pci_driver);
+}
+
+module_init(coscsi_pci_init);
+module_exit(coscsi_pci_exit);
Index: linux-2.6.25-source/drivers/scsi/coscsi_rom.h
===================================================================
--- /dev/null
+++ linux-2.6.25-source/drivers/scsi/coscsi_rom.h
@@ -0,0 +1,142 @@
+
+#ifndef __COSCSI_ROM_H
+#define __COSCSI_ROM_H
+
+/* Mode/Inq page data */
+struct coscsi_page {
+	int num;
+	unsigned char *page;
+	int size;
+};
+typedef struct coscsi_page coscsi_page_t;
+
+#define COSCSI_ROM_PAGE(n,p) { n, p, sizeof(p) }
+
+struct coscsi_rom {
+        char *name;
+        coscsi_page_t std;
+        coscsi_page_t *vpd;
+	coscsi_page_t *mode;
+};
+typedef struct coscsi_rom coscsi_rom_t;
+
+/*
+ * Disk pages
+*/
+
+/* Standard Inquiry page */
+static unsigned char disk_std_page[] = {
+	0x00,0x00,0x05,0x02,0x5c,0x00,0x01,0x20,	/* 00 - 07 */
+	0x63,0x6f,0x4c,0x69,0x6e,0x75,0x78,0x00,	/* 08 - 15 */
+	0x43,0x4f,0x44,0x49,0x53,0x4b,0x00,0x00,	/* 16 - 23 */
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 24 - 31 */
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 32 - 39 */
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 40 - 47 */
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 48 - 55 */
+	0x00,0x00,0x00,0x77,0x00,0x14,0x03,0x3d,	/* 56 - 63 */
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 64 - 71 */
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 72 - 79 */
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 80 - 87 */
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 88 - 95 */
+};
+
+#if 0
+/* Supported VPD Pages */
+static unsigned char disk_vpd_00[] = {
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 00 - 07 */
+};
+#endif
+
+#if 0
+/* Unit Serial Number VPD page */
+static unsigned char disk_vpd_80[] = {
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 00 - 07 */
+};
+#endif
+
+/* Device Identification VPD page */
+static unsigned char disk_vpd_83[] = {
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 00 - 07 */
+};
+
+/* Block Limits VPD page */
+static unsigned char disk_vpd_b0[] = {
+	0x00,0xB0,0x00,0x10,0x00,0x00,0x00,0x00,	/* 00 - 07 */
+};
+
+static coscsi_page_t disk_vpd_pages[] = {
+#if 0
+	COSCSI_ROM_PAGE(0x80, disk_vpd_80),		/* Unit Serial Number */
+#endif
+	COSCSI_ROM_PAGE(0x83, disk_vpd_83),		/* Device Identification */
+	COSCSI_ROM_PAGE(0xb0, disk_vpd_b0),		/* Block limits (SBC) */
+	{ 0, 0, 0 }
+};
+
+static unsigned char disk_mode_08[] = {
+	0x08, 0x12, 0x14, 0x00, 0xff, 0xff, 0x00, 0x00,
+	0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00
+};
+
+static coscsi_page_t disk_mode_pages[] = {
+	COSCSI_ROM_PAGE(0x08, disk_mode_08),
+	{ 0, 0, 0 }
+};
+
+static coscsi_rom_t disk_rom = {
+	.name =		"CODISK",
+	.std =		COSCSI_ROM_PAGE(0, disk_std_page),
+	.vpd =		disk_vpd_pages,
+	.mode =		disk_mode_pages,
+};
+
+/*
+ * CD pages
+*/
+
+static unsigned char cd_std_page[] = {
+	0x05,0x80,0x02,0x02,0x1f,0x00,0x00,0x10,	/* 00 - 07 */
+	0x4f,0x50,0x30,0x34,0x32,0x5a,0x20,0x49,	/* 08 - 15 */
+	0x52,0x53,0x30,0x36,0x50,0x20,0x20,0x20,	/* 16 - 23 */
+	0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,	/* 24 - 31 */
+	0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,	/* 24 - 31 */
+	0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,	/* 32 - 39 */
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 40 - 47 */
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 48 - 55 */
+	0x00,0x00,0x00,0x77,0x00,0x14,0x03,0x3d,	/* 56 - 63 */
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 64 - 71 */
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 72 - 79 */
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 80 - 87 */
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 88 - 95 */
+};
+
+/* Device Identification VPD page */
+static unsigned char cd_vpd_83[] = {
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 00 - 07 */
+};
+
+static coscsi_page_t cd_vpd_pages[] = {
+	COSCSI_ROM_PAGE(0x83, cd_vpd_83),		/* Device Identification */
+	{ 0, 0, 0 }
+};
+
+unsigned char cd_mode_2a[] = {
+	0x2a,0x18,0x3f,0x00,0x75,0x7f,0x29,0x00, 	/* 00 - 07 */
+	0x16,0x00,0x01,0x00,0x02,0x00,0x16,0x00,	/* 08 - 15 */
+	0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x01,	/* 16 - 23 */
+};
+
+static coscsi_page_t cd_mode_pages[] = {
+	COSCSI_ROM_PAGE(0x2a, cd_mode_2a),
+	{ 0, 0, 0 }
+};
+
+static coscsi_rom_t cd_rom = {
+	.name =		"COCD",
+	.std =		COSCSI_ROM_PAGE(0, cd_std_page),
+	.vpd = 		cd_vpd_pages,
+	.mode =		cd_mode_pages,
+};
+
+#endif
Index: linux-2.6.25-source/include/scsi/coscsi.h
===================================================================
--- /dev/null
+++ linux-2.6.25-source/include/scsi/coscsi.h
@@ -0,0 +1,66 @@
+
+#ifndef __SCSI_COSCSI_H
+#define __SCSI_COSCSI_H
+
+#if defined(CO_KERNEL) || defined(CO_HOST_KERNEL)
+
+/* Set this to 1 to enable background I/O in the host */
+#define COSCSI_ASYNC 		1
+
+typedef enum {
+	CO_SCSI_GET_CONFIG,
+	CO_SCSI_OPEN,
+	CO_SCSI_CLOSE,
+	CO_SCSI_SIZE,
+	CO_SCSI_IO,
+	CO_SCSI_PASS,
+	CO_SCSI_IOS,
+} CO_SCSI_OPS;
+
+#define COSCSI_DEVICE_ENABLED 0x80
+
+typedef struct {
+	void *scp;
+	unsigned long long offset;
+	vm_ptr_t sg;
+	int count;
+	int reqlen;
+	int write;
+} __attribute__((packed)) co_scsi_io_t;
+
+typedef struct {
+	void *ctx;
+	int result;
+	int delta;
+} __attribute__((packed)) co_scsi_intr_t;
+
+typedef struct {
+	unsigned char cdb[16];
+	unsigned cdb_len: 7;
+	unsigned write: 1;
+	vm_ptr_t buffer;
+	unsigned long buflen;
+} __attribute__((packed)) co_scsi_pass_t;
+
+#endif /* CO_KERNEL || CO_HOST_KERNEL */
+
+/* Device types */
+#define SCSI_PTYPE_DISK 		0x00
+#define SCSI_PTYPE_TAPE			0x01
+#define SCSI_PTYPE_PRINTER		0x02
+#define SCSI_PTYPE_PROC			0x03
+#define SCSI_PTYPE_WORM			0x04
+#define SCSI_PTYPE_CDDVD		0x05
+#define SCSI_PTYPE_SCANNER		0x06
+#define SCSI_PTYPE_OPTICAL		0x07
+#define SCSI_PTYPE_CHANGER		0x08
+#define SCSI_PTYPE_COMM			0x09
+#define SCSI_PTYPE_RAID			0x0C
+#define SCSI_PTYPE_ENC			0x0D
+#define SCSI_PTYPE_SDISK		0x0E
+#define SCSI_PTYPE_CARD			0x0F
+#define SCSI_PTYPE_BRIDGE		0x10
+#define SCSI_PTYPE_OSD			0x11
+#define SCSI_PTYPE_PASS			0x1F		/* Special */
+
+#endif
