Index: uspace/srv/bd/ata_bd/ata_bd.c
===================================================================
--- uspace/srv/bd/ata_bd/ata_bd.c	(revision 47b70062180b321b58ca43746901c748ff7a6b2d)
+++ uspace/srv/bd/ata_bd/ata_bd.c	(revision aa893e0c1c2438f546d8cc1d5ff8f9942998561c)
@@ -51,8 +51,10 @@
 #include <libarch/ddi.h>
 #include <ddi.h>
+#include <ipc/ipc.h>
 #include <ipc/bd.h>
 #include <async.h>
 #include <as.h>
 #include <fibril_synch.h>
+#include <stdint.h>
 #include <str.h>
 #include <devmap.h>
@@ -61,4 +63,5 @@
 #include <errno.h>
 #include <bool.h>
+#include <byteorder.h>
 #include <task.h>
 #include <macros.h>
@@ -73,6 +76,9 @@
 #define LEGACY_CTLS 4
 
-/** Physical block size. Should be always 512. */
-static const size_t block_size = 512;
+/**
+ * Size of data returned from Identify Device or Identify Packet Device
+ * command.
+ */
+static const size_t identify_data_size = 512;
 
 /** Size of the communication area. */
@@ -105,10 +111,16 @@
 static int ata_bd_write_blocks(int disk_id, uint64_t ba, size_t cnt,
     const void *buf);
-static int ata_bd_read_block(int disk_id, uint64_t ba, size_t cnt,
+static int ata_rcmd_read(int disk_id, uint64_t ba, size_t cnt,
     void *buf);
-static int ata_bd_write_block(int disk_id, uint64_t ba, size_t cnt,
+static int ata_rcmd_write(int disk_id, uint64_t ba, size_t cnt,
     const void *buf);
 static int disk_init(disk_t *d, int disk_id);
 static int drive_identify(int drive_id, void *buf);
+static int identify_pkt_dev(int dev_idx, void *buf);
+static int ata_cmd_packet(int dev_idx, const void *cpkt, size_t cpkt_size,
+    void *obuf, size_t obuf_size);
+static int ata_pcmd_inquiry(int dev_idx, void *obuf, size_t obuf_size);
+static int ata_pcmd_read_12(int dev_idx, uint64_t ba, size_t cnt,
+    void *obuf, size_t obuf_size);
 static void disk_print_summary(disk_t *d);
 static int coord_calc(disk_t *d, uint64_t ba, block_coord_t *bc);
@@ -203,15 +215,20 @@
 	printf("%s: ", d->model);
 
-	switch (d->amode) {
-	case am_chs:
-		printf("CHS %u cylinders, %u heads, %u sectors",
-		    disk->geom.cylinders, disk->geom.heads, disk->geom.sectors);
-		break;
-	case am_lba28:
-		printf("LBA-28");
-		break;
-	case am_lba48:
-		printf("LBA-48");
-		break;
+	if (d->dev_type == ata_reg_dev) {
+		switch (d->amode) {
+		case am_chs:
+			printf("CHS %u cylinders, %u heads, %u sectors",
+			    disk->geom.cylinders, disk->geom.heads,
+			    disk->geom.sectors);
+			break;
+		case am_lba28:
+			printf("LBA-28");
+			break;
+		case am_lba48:
+			printf("LBA-48");
+			break;
+		}
+	} else {
+		printf("PACKET");
 	}
 
@@ -265,5 +282,5 @@
 	sysarg_t method;
 	devmap_handle_t dh;
-	unsigned int flags;
+	int flags;
 	int retval;
 	uint64_t ba;
@@ -281,13 +298,13 @@
 
 	if (disk_id < 0 || disk[disk_id].present == false) {
-		async_answer_0(iid, EINVAL);
+		ipc_answer_0(iid, EINVAL);
 		return;
 	}
 
 	/* Answer the IPC_M_CONNECT_ME_TO call. */
-	async_answer_0(iid, EOK);
+	ipc_answer_0(iid, EOK);
 
 	if (!async_share_out_receive(&callid, &comm_size, &flags)) {
-		async_answer_0(callid, EHANGUP);
+		ipc_answer_0(callid, EHANGUP);
 		return;
 	}
@@ -295,5 +312,5 @@
 	fs_va = as_get_mappable_page(comm_size);
 	if (fs_va == NULL) {
-		async_answer_0(callid, EHANGUP);
+		ipc_answer_0(callid, EHANGUP);
 		return;
 	}
@@ -307,5 +324,5 @@
 		case IPC_M_PHONE_HUNGUP:
 			/* The other side has hung up. */
-			async_answer_0(callid, EOK);
+			ipc_answer_0(callid, EOK);
 			return;
 		case BD_READ_BLOCKS:
@@ -313,5 +330,5 @@
 			    IPC_GET_ARG2(call));
 			cnt = IPC_GET_ARG3(call);
-			if (cnt * block_size > comm_size) {
+			if (cnt * disk[disk_id].block_size > comm_size) {
 				retval = ELIMIT;
 				break;
@@ -323,5 +340,5 @@
 			    IPC_GET_ARG2(call));
 			cnt = IPC_GET_ARG3(call);
-			if (cnt * block_size > comm_size) {
+			if (cnt * disk[disk_id].block_size > comm_size) {
 				retval = ELIMIT;
 				break;
@@ -330,8 +347,8 @@
 			break;
 		case BD_GET_BLOCK_SIZE:
-			async_answer_1(callid, EOK, block_size);
+			ipc_answer_1(callid, EOK, disk[disk_id].block_size);
 			continue;
 		case BD_GET_NUM_BLOCKS:
-			async_answer_2(callid, EOK, LOWER32(disk[disk_id].blocks),
+			ipc_answer_2(callid, EOK, LOWER32(disk[disk_id].blocks),
 			    UPPER32(disk[disk_id].blocks));
 			continue;
@@ -340,5 +357,5 @@
 			break;
 		}
-		async_answer_0(callid, retval);
+		ipc_answer_0(callid, retval);
 	}
 }
@@ -353,4 +370,5 @@
 	identify_data_t idata;
 	uint8_t model[40];
+	ata_inquiry_data_t inq_data;
 	uint16_t w;
 	uint8_t c;
@@ -359,11 +377,42 @@
 	unsigned i;
 
+	d->present = false;
+	fibril_mutex_initialize(&d->lock);
+
+	/* Try identify command. */
 	rc = drive_identify(disk_id, &idata);
-	if (rc != EOK) {
-		d->present = false;
-		return rc;
-	}
-
-	if ((idata.caps & cap_lba) == 0) {
+	if (rc == EOK) {
+		/* Success. It's a register (non-packet) device. */
+		printf("ATA register-only device found.\n");
+		d->dev_type = ata_reg_dev;
+	} else if (rc == EIO) {
+		/*
+		 * There is something, but not a register device.
+		 * It could be a packet device.
+		 */
+		rc = identify_pkt_dev(disk_id, &idata);
+		if (rc == EOK) {
+			/* We have a packet device. */
+			d->dev_type = ata_pkt_dev;
+		} else {
+			/* Nope. Something's there, but not recognized. */
+			return EIO;
+		}
+	} else {
+		/* Operation timed out. That means there is no device there. */
+		return EIO;
+	}
+
+	printf("device caps: 0x%04x\n", idata.caps);
+	if (d->dev_type == ata_pkt_dev) {
+		/* Packet device */
+		d->amode = 0;
+
+		d->geom.cylinders = 0;
+		d->geom.heads = 0;
+		d->geom.sectors = 0;
+
+		d->blocks = 0;
+	} else if ((idata.caps & rd_cap_lba) == 0) {
 		/* Device only supports CHS addressing. */
 		d->amode = am_chs;
@@ -422,7 +471,25 @@
 	d->model[pos] = '\0';
 
+	if (d->dev_type == ata_pkt_dev) {
+		/* Send inquiry. */
+		rc = ata_pcmd_inquiry(0, &inq_data, sizeof(inq_data));
+		if (rc != EOK) {
+			printf("Device inquiry failed.\n");
+			d->present = false;
+			return EIO;
+		}
+
+		/* Check device type. */
+		if (INQUIRY_PDEV_TYPE(inq_data.pdev_type) != PDEV_TYPE_CDROM)
+			printf("Warning: Peripheral device type is not CD-ROM.\n");
+
+		/* Assume 2k block size for now. */
+		d->block_size = 2048;
+	} else {
+		/* Assume register Read always uses 512-byte blocks. */
+		d->block_size = 512;
+	}
+
 	d->present = true;
-	fibril_mutex_initialize(&d->lock);
-
 	return EOK;
 }
@@ -435,5 +502,10 @@
 
 	while (cnt > 0) {
-		rc = ata_bd_read_block(disk_id, ba, 1, buf);
+		if (disk[disk_id].dev_type == ata_reg_dev)
+			rc = ata_rcmd_read(disk_id, ba, 1, buf);
+		else
+			rc = ata_pcmd_read_12(disk_id, ba, 1, buf,
+			    disk[disk_id].block_size);
+
 		if (rc != EOK)
 			return rc;
@@ -441,5 +513,5 @@
 		++ba;
 		--cnt;
-		buf += block_size;
+		buf += disk[disk_id].block_size;
 	}
 
@@ -453,6 +525,9 @@
 	int rc;
 
+	if (disk[disk_id].dev_type != ata_reg_dev)
+		return ENOTSUP;
+
 	while (cnt > 0) {
-		rc = ata_bd_write_block(disk_id, ba, 1, buf);
+		rc = ata_rcmd_write(disk_id, ba, 1, buf);
 		if (rc != EOK)
 			return rc;
@@ -460,5 +535,5 @@
 		++ba;
 		--cnt;
-		buf += block_size;
+		buf += disk[disk_id].block_size;
 	}
 
@@ -473,4 +548,7 @@
  * @param disk_id	Device ID, 0 or 1.
  * @param buf		Pointer to a 512-byte buffer.
+ *
+ * @return		ETIMEOUT on timeout (this can mean the device is
+ *			not present). EIO if device responds with error.
  */
 static int drive_identify(int disk_id, void *buf)
@@ -484,5 +562,5 @@
 
 	if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
-		return EIO;
+		return ETIMEOUT;
 
 	pio_write_8(&cmd->drive_head, drv_head);
@@ -493,15 +571,15 @@
 	 */
 	if (wait_status(SR_DRDY, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
-		return EIO;
+		return ETIMEOUT;
 
 	pio_write_8(&cmd->command, CMD_IDENTIFY_DRIVE);
 
 	if (wait_status(0, ~SR_BSY, &status, TIMEOUT_PROBE) != EOK)
-		return EIO;
+		return ETIMEOUT;
 
 	/* Read data from the disk buffer. */
 
 	if ((status & SR_DRQ) != 0) {
-		for (i = 0; i < block_size / 2; i++) {
+		for (i = 0; i < identify_data_size / 2; i++) {
 			data = pio_read_16(&cmd->data_port);
 			((uint16_t *) buf)[i] = data;
@@ -509,6 +587,204 @@
 	}
 
+	if ((status & SR_ERR) != 0) {
+		return EIO;
+	}
+
+	return EOK;
+}
+
+/** Issue Identify Packet Device command.
+ *
+ * Reads @c identify data into the provided buffer. This is used to detect
+ * whether an ATAPI device is present and if so, to determine its parameters.
+ *
+ * @param dev_idx	Device index, 0 or 1.
+ * @param buf		Pointer to a 512-byte buffer.
+ */
+static int identify_pkt_dev(int dev_idx, void *buf)
+{
+	uint16_t data;
+	uint8_t status;
+	uint8_t drv_head;
+	size_t i;
+
+	drv_head = ((dev_idx != 0) ? DHR_DRV : 0);
+
+	if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
+		return EIO;
+
+	pio_write_8(&cmd->drive_head, drv_head);
+
+	/* For ATAPI commands we do not need to wait for DRDY. */
+	if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
+		return EIO;
+
+	pio_write_8(&cmd->command, CMD_IDENTIFY_PKT_DEV);
+
+	if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK)
+		return EIO;
+
+	/* Read data from the device buffer. */
+
+	if ((status & SR_DRQ) != 0) {
+		for (i = 0; i < identify_data_size / 2; i++) {
+			data = pio_read_16(&cmd->data_port);
+			((uint16_t *) buf)[i] = data;
+		}
+	}
+
 	if ((status & SR_ERR) != 0)
 		return EIO;
+
+	return EOK;
+}
+
+/** Issue packet command (i. e. write a command packet to the device).
+ *
+ * Only data-in commands are supported (e.g. inquiry, read).
+ *
+ * @param dev_idx	Device index (0 or 1)
+ * @param obuf		Buffer for storing data read from device
+ * @param obuf_size	Size of obuf in bytes
+ *
+ * @return EOK on success, EIO on error.
+ */
+static int ata_cmd_packet(int dev_idx, const void *cpkt, size_t cpkt_size,
+    void *obuf, size_t obuf_size)
+{
+	size_t i;
+	uint8_t status;
+	uint8_t drv_head;
+	disk_t *d;
+	size_t data_size;
+	uint16_t val;
+
+	d = &disk[dev_idx];
+	fibril_mutex_lock(&d->lock);
+
+	/* New value for Drive/Head register */
+	drv_head =
+	    ((dev_idx != 0) ? DHR_DRV : 0);
+
+	if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK) {
+		fibril_mutex_unlock(&d->lock);
+		return EIO;
+	}
+
+	pio_write_8(&cmd->drive_head, drv_head);
+
+	if (wait_status(0, ~(SR_BSY|SR_DRQ), NULL, TIMEOUT_BSY) != EOK) {
+		fibril_mutex_unlock(&d->lock);
+		return EIO;
+	}
+
+	/* Byte count <- max. number of bytes we can read in one transfer. */
+	pio_write_8(&cmd->cylinder_low, 0xfe);
+	pio_write_8(&cmd->cylinder_high, 0xff);
+
+	pio_write_8(&cmd->command, CMD_PACKET);
+
+	if (wait_status(SR_DRQ, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
+		fibril_mutex_unlock(&d->lock);
+		return EIO;
+	}
+
+	/* Write command packet. */
+	for (i = 0; i < (cpkt_size + 1) / 2; i++)
+		pio_write_16(&cmd->data_port, ((uint16_t *) cpkt)[i]);
+
+	if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
+		fibril_mutex_unlock(&d->lock);
+		return EIO;
+	}
+
+	if ((status & SR_DRQ) == 0) {
+		fibril_mutex_unlock(&d->lock);
+		return EIO;
+	}
+
+	/* Read byte count. */
+	data_size = (uint16_t) pio_read_8(&cmd->cylinder_low) +
+	    ((uint16_t) pio_read_8(&cmd->cylinder_high) << 8);
+
+	/* Check whether data fits into output buffer. */
+	if (data_size > obuf_size) {
+		/* Output buffer is too small to store data. */
+		fibril_mutex_unlock(&d->lock);
+		return EIO;
+	}
+
+	/* Read data from the device buffer. */
+	for (i = 0; i < (data_size + 1) / 2; i++) {
+		val = pio_read_16(&cmd->data_port);
+		((uint16_t *) obuf)[i] = val;
+	}
+
+	if (status & SR_ERR) {
+		fibril_mutex_unlock(&d->lock);
+		return EIO;
+	}
+
+	fibril_mutex_unlock(&d->lock);
+
+	return EOK;
+}
+
+/** Issue ATAPI Inquiry.
+ *
+ * @param dev_idx	Device index (0 or 1)
+ * @param obuf		Buffer for storing inquiry data read from device
+ * @param obuf_size	Size of obuf in bytes
+ *
+ * @return EOK on success, EIO on error.
+ */
+static int ata_pcmd_inquiry(int dev_idx, void *obuf, size_t obuf_size)
+{
+	ata_pcmd_inquiry_t cp;
+	int rc;
+
+	memset(&cp, 0, sizeof(cp));
+
+	cp.opcode = PCMD_INQUIRY;
+	cp.alloc_len = min(obuf_size, 0xff); /* Allocation length */
+
+	rc = ata_cmd_packet(0, &cp, sizeof(cp), obuf, obuf_size);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+/** Issue ATAPI read(12) command.
+ *
+ * Output buffer must be large enough to hold the data, otherwise the
+ * function will fail.
+ *
+ * @param dev_idx	Device index (0 or 1)
+ * @param ba		Starting block address
+ * @param cnt		Number of blocks to read
+ * @param obuf		Buffer for storing inquiry data read from device
+ * @param obuf_size	Size of obuf in bytes
+ *
+ * @return EOK on success, EIO on error.
+ */
+static int ata_pcmd_read_12(int dev_idx, uint64_t ba, size_t cnt,
+    void *obuf, size_t obuf_size)
+{
+	ata_pcmd_read_12_t cp;
+	int rc;
+
+	if (ba > UINT32_MAX)
+		return EINVAL;
+
+	memset(&cp, 0, sizeof(cp));
+
+	cp.opcode = PCMD_READ_12;
+	cp.ba = host2uint32_t_be(ba);
+	cp.nblocks = host2uint32_t_be(cnt);
+
+	rc = ata_cmd_packet(0, &cp, sizeof(cp), obuf, obuf_size);
+	if (rc != EOK)
+		return rc;
 
 	return EOK;
@@ -524,5 +800,5 @@
  * @return EOK on success, EIO on error.
  */
-static int ata_bd_read_block(int disk_id, uint64_t ba, size_t blk_cnt,
+static int ata_rcmd_read(int disk_id, uint64_t ba, size_t blk_cnt,
     void *buf)
 {
@@ -579,5 +855,5 @@
 		/* Read data from the device buffer. */
 
-		for (i = 0; i < block_size / 2; i++) {
+		for (i = 0; i < disk[disk_id].block_size / 2; i++) {
 			data = pio_read_16(&cmd->data_port);
 			((uint16_t *) buf)[i] = data;
@@ -601,5 +877,5 @@
  * @return EOK on success, EIO on error.
  */
-static int ata_bd_write_block(int disk_id, uint64_t ba, size_t cnt,
+static int ata_rcmd_write(int disk_id, uint64_t ba, size_t cnt,
     const void *buf)
 {
@@ -655,5 +931,5 @@
 		/* Write data to the device buffer. */
 
-		for (i = 0; i < block_size / 2; i++) {
+		for (i = 0; i < disk[disk_id].block_size / 2; i++) {
 			pio_write_16(&cmd->data_port, ((uint16_t *) buf)[i]);
 		}
