Index: uspace/drv/block/ata_bd/Makefile
===================================================================
--- uspace/drv/block/ata_bd/Makefile	(revision a8196c90457a5217af091168fccd2409d482c81d)
+++ uspace/drv/block/ata_bd/Makefile	(revision a8196c90457a5217af091168fccd2409d482c81d)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2013 Jiri Svoboda
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../../..
+LIBS = $(LIBDRV_PREFIX)/libdrv.a
+EXTRA_CFLAGS += -I$(LIBDRV_PREFIX)/include
+BINARY = ata_bd
+
+SOURCES = \
+	ata_bd.c \
+	main.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/block/ata_bd/ata_bd.c
===================================================================
--- uspace/drv/block/ata_bd/ata_bd.c	(revision a8196c90457a5217af091168fccd2409d482c81d)
+++ uspace/drv/block/ata_bd/ata_bd.c	(revision a8196c90457a5217af091168fccd2409d482c81d)
@@ -0,0 +1,1150 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup bd
+ * @{
+ */
+
+/**
+ * @file
+ * @brief ATA disk driver
+ *
+ * This driver supports CHS, 28-bit and 48-bit LBA addressing, as well as
+ * PACKET devices. It only uses PIO transfers. There is no support DMA
+ * or any other fancy features such as S.M.A.R.T, removable devices, etc.
+ *
+ * This driver is based on the ATA-1, ATA-2, ATA-3 and ATA/ATAPI-4 through 7
+ * standards, as published by the ANSI, NCITS and INCITS standards bodies,
+ * which are freely available. This driver contains no vendor-specific
+ * code at this moment.
+ *
+ * The driver services a single controller which can have up to two disks
+ * attached.
+ */
+
+#include <stdio.h>
+#include <ddi.h>
+#include <async.h>
+#include <as.h>
+#include <bd_srv.h>
+#include <fibril_synch.h>
+#include <stdint.h>
+#include <str.h>
+#include <loc.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <byteorder.h>
+#include <task.h>
+#include <macros.h>
+
+#include "ata_hw.h"
+#include "ata_bd.h"
+#include "main.h"
+
+#define NAME       "ata_bd"
+#define NAMESPACE  "bd"
+
+/** Number of defined legacy controller base addresses. */
+#define LEGACY_CTLS 4
+
+/**
+ * Size of data returned from Identify Device or Identify Packet Device
+ * command.
+ */
+static const size_t identify_data_size = 512;
+
+/** I/O base addresses for legacy (ISA-compatible) controllers. */
+static ata_base_t legacy_base[LEGACY_CTLS] = {
+	{ 0x1f0, 0x3f0 },
+	{ 0x170, 0x370 },
+	{ 0x1e8, 0x3e8 },
+	{ 0x168, 0x368 }
+};
+
+static int ata_bd_init_io(ata_ctrl_t *ctrl);
+static void ata_bd_fini_io(ata_ctrl_t *ctrl);
+
+static int ata_bd_open(bd_srvs_t *, bd_srv_t *);
+static int ata_bd_close(bd_srv_t *);
+static int ata_bd_read_blocks(bd_srv_t *, uint64_t ba, size_t cnt, void *buf,
+    size_t);
+static int ata_bd_read_toc(bd_srv_t *, uint8_t session, void *buf, size_t);
+static int ata_bd_write_blocks(bd_srv_t *, uint64_t ba, size_t cnt,
+    const void *buf, size_t);
+static int ata_bd_get_block_size(bd_srv_t *, size_t *);
+static int ata_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
+
+static int ata_rcmd_read(disk_t *disk, uint64_t ba, size_t cnt,
+    void *buf);
+static int ata_rcmd_write(disk_t *disk, uint64_t ba, size_t cnt,
+    const void *buf);
+static int disk_init(ata_ctrl_t *ctrl, disk_t *d, int disk_id);
+static int drive_identify(disk_t *disk, void *buf);
+static int identify_pkt_dev(disk_t *disk, void *buf);
+static int ata_cmd_packet(disk_t *disk, const void *cpkt, size_t cpkt_size,
+    void *obuf, size_t obuf_size);
+static int ata_pcmd_inquiry(disk_t *disk, void *obuf, size_t obuf_size);
+static int ata_pcmd_read_12(disk_t *disk, uint64_t ba, size_t cnt,
+    void *obuf, size_t obuf_size);
+static int ata_pcmd_read_toc(disk_t *disk, uint8_t ses,
+    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);
+static void coord_sc_program(ata_ctrl_t *ctrl, const block_coord_t *bc,
+    uint16_t scnt);
+static int wait_status(ata_ctrl_t *ctrl, unsigned set, unsigned n_reset,
+    uint8_t *pstatus, unsigned timeout);
+
+bd_ops_t ata_bd_ops = {
+	.open = ata_bd_open,
+	.close = ata_bd_close,
+	.read_blocks = ata_bd_read_blocks,
+	.read_toc = ata_bd_read_toc,
+	.write_blocks = ata_bd_write_blocks,
+	.get_block_size = ata_bd_get_block_size,
+	.get_num_blocks = ata_bd_get_num_blocks
+};
+
+static disk_t *bd_srv_disk(bd_srv_t *bd)
+{
+	return (disk_t *)bd->srvs->sarg;
+}
+
+static int disk_dev_idx(disk_t *disk)
+{
+	return (disk->disk_id & 1);
+}
+
+/** Initialize ATA controller. */
+int ata_ctrl_init(ata_ctrl_t *ctrl)
+{
+	int i, rc;
+	int n_disks;
+	unsigned ctl_num;
+
+	printf(NAME ": ata_ctrl_init()\n");
+
+	ctl_num = 1;
+
+	fibril_mutex_initialize(&ctrl->lock);
+	ctrl->cmd_physical = legacy_base[ctl_num - 1].cmd;
+	ctrl->ctl_physical = legacy_base[ctl_num - 1].ctl;
+
+	printf("I/O address %p/%p\n", (void *) ctrl->cmd_physical,
+	    (void *) ctrl->ctl_physical);
+
+	rc = ata_bd_init_io(ctrl);
+	if (rc != EOK)
+		return rc;
+
+	for (i = 0; i < MAX_DISKS; i++) {
+		printf("Identify drive %d... ", i);
+		fflush(stdout);
+
+		rc = disk_init(ctrl, &ctrl->disk[i], i);
+
+		if (rc == EOK) {
+			disk_print_summary(&ctrl->disk[i]);
+		} else {
+			printf("Not found.\n");
+		}
+	}
+
+	n_disks = 0;
+
+	for (i = 0; i < MAX_DISKS; i++) {
+		/* Skip unattached drives. */
+		if (ctrl->disk[i].present == false)
+			continue;
+
+		rc = ata_fun_create(&ctrl->disk[i]);
+		if (rc != EOK) {
+			printf(NAME ": Unable to create function for disk %d.\n",
+			    i);
+			goto error;
+		}
+		++n_disks;
+	}
+
+	if (n_disks == 0) {
+		printf("No disks detected.\n");
+		rc = EIO;
+		goto error;
+	}
+
+	return EOK;
+error:
+	for (i = 0; i < MAX_DISKS; i++) {
+		if (ata_fun_remove(&ctrl->disk[i]) != EOK) {
+			printf(NAME ": Unable to clean up function for disk %d.\n",
+			    i);
+		}
+	}
+	ata_bd_fini_io(ctrl);
+	return rc;
+}
+
+/** Remove ATA controller. */
+int ata_ctrl_remove(ata_ctrl_t *ctrl)
+{
+	int i, rc;
+
+	printf(NAME ": ata_ctrl_remove()\n");
+
+	fibril_mutex_lock(&ctrl->lock);
+
+	for (i = 0; i < MAX_DISKS; i++) {
+		rc = ata_fun_remove(&ctrl->disk[i]);
+		if (rc != EOK) {
+			printf(NAME ": Unable to clean up function for disk %d.\n",
+			    i);
+			return rc;
+		}
+	}
+
+	ata_bd_fini_io(ctrl);
+	fibril_mutex_unlock(&ctrl->lock);
+
+	return EOK;
+}
+
+/** Surprise removal of ATA controller. */
+int ata_ctrl_gone(ata_ctrl_t *ctrl)
+{
+	int i, rc;
+
+	printf(NAME ": ata_ctrl_gone()\n");
+
+	fibril_mutex_lock(&ctrl->lock);
+
+	for (i = 0; i < MAX_DISKS; i++) {
+		rc = ata_fun_unbind(&ctrl->disk[i]);
+		if (rc != EOK) {
+			printf(NAME ": Unable to clean up function for disk %d.\n",
+			    i);
+			return rc;
+		}
+	}
+
+	ata_bd_fini_io(ctrl);
+	fibril_mutex_unlock(&ctrl->lock);
+
+	return EOK;
+}
+
+/** Print one-line device summary. */
+static void disk_print_summary(disk_t *d)
+{
+	uint64_t mbytes;
+
+	printf("%s: ", d->model);
+
+	if (d->dev_type == ata_reg_dev) {
+		switch (d->amode) {
+		case am_chs:
+			printf("CHS %u cylinders, %u heads, %u sectors",
+			    d->geom.cylinders, d->geom.heads,
+			    d->geom.sectors);
+			break;
+		case am_lba28:
+			printf("LBA-28");
+			break;
+		case am_lba48:
+			printf("LBA-48");
+			break;
+		}
+	} else {
+		printf("PACKET");
+	}
+
+	printf(" %" PRIu64 " blocks", d->blocks);
+
+	mbytes = d->blocks / (2 * 1024);
+	if (mbytes > 0)
+		printf(" %" PRIu64 " MB.", mbytes);
+
+	printf("\n");
+}
+
+/** Enable device I/O. */
+static int ata_bd_init_io(ata_ctrl_t *ctrl)
+{
+	int rc;
+	void *vaddr;
+
+	rc = pio_enable((void *) ctrl->cmd_physical, sizeof(ata_cmd_t), &vaddr);
+	if (rc != EOK) {
+		printf("%s: Could not initialize device I/O space.\n", NAME);
+		return rc;
+	}
+
+	ctrl->cmd = vaddr;
+
+	rc = pio_enable((void *) ctrl->ctl_physical, sizeof(ata_ctl_t), &vaddr);
+	if (rc != EOK) {
+		printf("%s: Could not initialize device I/O space.\n", NAME);
+		return rc;
+	}
+
+	ctrl->ctl = vaddr;
+
+	return EOK;
+}
+
+/** Clean up device I/O. */
+static void ata_bd_fini_io(ata_ctrl_t *ctrl)
+{
+	(void) ctrl;
+	/* XXX TODO */
+}
+
+/** Initialize a disk.
+ *
+ * Probes for a disk, determines its parameters and initializes
+ * the disk structure.
+ */
+static int disk_init(ata_ctrl_t *ctrl, disk_t *d, int disk_id)
+{
+	identify_data_t idata;
+	uint8_t model[40];
+	ata_inquiry_data_t inq_data;
+	uint16_t w;
+	uint8_t c;
+	uint16_t bc;
+	size_t pos, len;
+	int rc;
+	unsigned i;
+
+	d->ctrl = ctrl;
+	d->disk_id = disk_id;
+	d->present = false;
+	d->afun = NULL;
+
+	/* Try identify command. */
+	rc = drive_identify(d, &idata);
+	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. Check to see
+		 * whether the IDENTIFY command left the packet signature in
+		 * the registers in case this is a packet device.
+		 *
+		 * According to the ATA specification, the LBA low and
+		 * interrupt reason registers should be set to 0x01. However,
+		 * there are many devices that do not follow this and only set
+		 * the byte count registers. So, only check these.
+		 */
+		bc = ((uint16_t)pio_read_8(&ctrl->cmd->cylinder_high) << 8) |
+		    pio_read_8(&ctrl->cmd->cylinder_low);
+
+		if (bc == PDEV_SIGNATURE_BC) {
+			rc = identify_pkt_dev(d, &idata);
+			if (rc == EOK) {
+				/* We have a packet device. */
+				d->dev_type = ata_pkt_dev;
+			} else {
+				return EIO;
+			}
+		} else {
+			/* Nope. Something's there, but not recognized. */
+			return EIO;
+		}
+	} else {
+		/* Operation timed out. That means there is no device there. */
+		return EIO;
+	}
+
+	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;
+
+		d->geom.cylinders = idata.cylinders;
+		d->geom.heads = idata.heads;
+		d->geom.sectors = idata.sectors;
+
+		d->blocks = d->geom.cylinders * d->geom.heads * d->geom.sectors;
+	} else if ((idata.cmd_set1 & cs1_addr48) == 0) {
+		/* Device only supports LBA-28 addressing. */
+		d->amode = am_lba28;
+
+		d->geom.cylinders = 0;
+		d->geom.heads = 0;
+		d->geom.sectors = 0;
+
+		d->blocks =
+		     (uint32_t) idata.total_lba28_0 | 
+		    ((uint32_t) idata.total_lba28_1 << 16);
+	} else {
+		/* Device supports LBA-48 addressing. */
+		d->amode = am_lba48;
+
+		d->geom.cylinders = 0;
+		d->geom.heads = 0;
+		d->geom.sectors = 0;
+
+		d->blocks =
+		     (uint64_t) idata.total_lba48_0 |
+		    ((uint64_t) idata.total_lba48_1 << 16) |
+		    ((uint64_t) idata.total_lba48_2 << 32) | 
+		    ((uint64_t) idata.total_lba48_3 << 48);
+	}
+
+	/*
+	 * Convert model name to string representation.
+	 */
+	for (i = 0; i < 20; i++) {
+		w = idata.model_name[i];
+		model[2 * i] = w >> 8;
+		model[2 * i + 1] = w & 0x00ff;
+	}
+
+	len = 40;
+	while (len > 0 && model[len - 1] == 0x20)
+		--len;
+
+	pos = 0;
+	for (i = 0; i < len; ++i) {
+		c = model[i];
+		if (c >= 0x80) c = '?';
+
+		chr_encode(c, d->model, &pos, 40);
+	}
+	d->model[pos] = '\0';
+
+	if (d->dev_type == ata_pkt_dev) {
+		/* Send inquiry. */
+		rc = ata_pcmd_inquiry(d, &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;
+	return EOK;
+}
+
+static int ata_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
+{
+	return EOK;
+}
+
+static int ata_bd_close(bd_srv_t *bd)
+{
+	return EOK;
+}
+
+/** Read multiple blocks from the device. */
+static int ata_bd_read_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt,
+    void *buf, size_t size)
+{
+	disk_t *disk = bd_srv_disk(bd);
+	int rc;
+
+	if (size < cnt * disk->block_size)
+		return EINVAL;
+
+	while (cnt > 0) {
+		if (disk->dev_type == ata_reg_dev) {
+			rc = ata_rcmd_read(disk, ba, 1, buf);
+		} else {
+			rc = ata_pcmd_read_12(disk, ba, 1, buf,
+			    disk->block_size);
+		}
+
+		if (rc != EOK)
+			return rc;
+
+		++ba;
+		--cnt;
+		buf += disk->block_size;
+	}
+
+	return EOK;
+}
+
+/** Read TOC from device. */
+static int ata_bd_read_toc(bd_srv_t *bd, uint8_t session, void *buf, size_t size)
+{
+	disk_t *disk = bd_srv_disk(bd);
+
+	return ata_pcmd_read_toc(disk, session, buf, size);
+}
+
+/** Write multiple blocks to the device. */
+static int ata_bd_write_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt,
+    const void *buf, size_t size)
+{
+	disk_t *disk = bd_srv_disk(bd);
+	int rc;
+
+	if (disk->dev_type != ata_reg_dev)
+		return ENOTSUP;
+
+	if (size < cnt * disk->block_size)
+		return EINVAL;
+
+	while (cnt > 0) {
+		rc = ata_rcmd_write(disk, ba, 1, buf);
+		if (rc != EOK)
+			return rc;
+
+		++ba;
+		--cnt;
+		buf += disk->block_size;
+	}
+
+	return EOK;
+}
+
+/** Get device block size. */
+static int ata_bd_get_block_size(bd_srv_t *bd, size_t *rbsize)
+{
+	disk_t *disk = bd_srv_disk(bd);
+
+	*rbsize = disk->block_size;
+	return EOK;
+}
+
+/** Get device number of blocks. */
+static int ata_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
+{
+	disk_t *disk = bd_srv_disk(bd);
+
+	*rnb = disk->blocks;
+	return EOK;
+}
+
+/** Issue IDENTIFY command.
+ *
+ * Reads @c identify data into the provided buffer. This is used to detect
+ * whether an ATA device is present and if so, to determine its parameters.
+ *
+ * @param disk		Disk
+ * @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(disk_t *disk, void *buf)
+{
+	ata_ctrl_t *ctrl = disk->ctrl;
+	uint16_t data;
+	uint8_t status;
+	uint8_t drv_head;
+	size_t i;
+
+	drv_head = ((disk_dev_idx(disk) != 0) ? DHR_DRV : 0);
+
+	if (wait_status(ctrl, 0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
+		return ETIMEOUT;
+
+	pio_write_8(&ctrl->cmd->drive_head, drv_head);
+
+	/*
+	 * Do not wait for DRDY to be set in case this is a packet device.
+	 * We determine whether the device is present by waiting for DRQ to be
+	 * set after issuing the command.
+	 */
+	if (wait_status(ctrl, 0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
+		return ETIMEOUT;
+
+	pio_write_8(&ctrl->cmd->command, CMD_IDENTIFY_DRIVE);
+
+	if (wait_status(ctrl, 0, ~SR_BSY, &status, TIMEOUT_PROBE) != EOK)
+		return ETIMEOUT;
+
+	/*
+	 * If ERR is set, this may be a packet device, so return EIO to cause
+	 * the caller to check for one.
+	 */
+	if ((status & SR_ERR) != 0) {
+		return EIO;
+	}
+
+	if (wait_status(ctrl, SR_DRQ, ~SR_BSY, &status, TIMEOUT_PROBE) != EOK)
+		return ETIMEOUT;
+
+	/* Read data from the disk buffer. */
+
+	for (i = 0; i < identify_data_size / 2; i++) {
+		data = pio_read_16(&ctrl->cmd->data_port);
+		((uint16_t *) buf)[i] = data;
+	}
+
+	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 disk		Disk
+ * @param buf		Pointer to a 512-byte buffer.
+ */
+static int identify_pkt_dev(disk_t *disk, void *buf)
+{
+	ata_ctrl_t *ctrl = disk->ctrl;
+	uint16_t data;
+	uint8_t status;
+	uint8_t drv_head;
+	size_t i;
+
+	drv_head = ((disk_dev_idx(disk) != 0) ? DHR_DRV : 0);
+
+	if (wait_status(ctrl, 0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
+		return EIO;
+
+	pio_write_8(&ctrl->cmd->drive_head, drv_head);
+
+	/* For ATAPI commands we do not need to wait for DRDY. */
+	if (wait_status(ctrl, 0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
+		return EIO;
+
+	pio_write_8(&ctrl->cmd->command, CMD_IDENTIFY_PKT_DEV);
+
+	if (wait_status(ctrl, 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(&ctrl->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 disk		Disk
+ * @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(disk_t *disk, const void *cpkt, size_t cpkt_size,
+    void *obuf, size_t obuf_size)
+{
+	ata_ctrl_t *ctrl = disk->ctrl;
+	size_t i;
+	uint8_t status;
+	uint8_t drv_head;
+	size_t data_size;
+	uint16_t val;
+
+	fibril_mutex_lock(&ctrl->lock);
+
+	/* New value for Drive/Head register */
+	drv_head =
+	    ((disk_dev_idx(disk) != 0) ? DHR_DRV : 0);
+
+	if (wait_status(ctrl, 0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK) {
+		fibril_mutex_unlock(&ctrl->lock);
+		return EIO;
+	}
+
+	pio_write_8(&ctrl->cmd->drive_head, drv_head);
+
+	if (wait_status(ctrl, 0, ~(SR_BSY|SR_DRQ), NULL, TIMEOUT_BSY) != EOK) {
+		fibril_mutex_unlock(&ctrl->lock);
+		return EIO;
+	}
+
+	/* Byte count <- max. number of bytes we can read in one transfer. */
+	pio_write_8(&ctrl->cmd->cylinder_low, 0xfe);
+	pio_write_8(&ctrl->cmd->cylinder_high, 0xff);
+
+	pio_write_8(&ctrl->cmd->command, CMD_PACKET);
+
+	if (wait_status(ctrl, SR_DRQ, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
+		fibril_mutex_unlock(&ctrl->lock);
+		return EIO;
+	}
+
+	/* Write command packet. */
+	for (i = 0; i < (cpkt_size + 1) / 2; i++)
+		pio_write_16(&ctrl->cmd->data_port, ((uint16_t *) cpkt)[i]);
+
+	if (wait_status(ctrl, 0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
+		fibril_mutex_unlock(&ctrl->lock);
+		return EIO;
+	}
+
+	if ((status & SR_DRQ) == 0) {
+		fibril_mutex_unlock(&ctrl->lock);
+		return EIO;
+	}
+
+	/* Read byte count. */
+	data_size = (uint16_t) pio_read_8(&ctrl->cmd->cylinder_low) +
+	    ((uint16_t) pio_read_8(&ctrl->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(&ctrl->lock);
+		return EIO;
+	}
+
+	/* Read data from the device buffer. */
+	for (i = 0; i < (data_size + 1) / 2; i++) {
+		val = pio_read_16(&ctrl->cmd->data_port);
+		((uint16_t *) obuf)[i] = val;
+	}
+
+	fibril_mutex_unlock(&ctrl->lock);
+
+	if (status & SR_ERR)
+		return EIO;
+
+	return EOK;
+}
+
+/** Issue ATAPI Inquiry.
+ *
+ * @param disk		Disk
+ * @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(disk_t *disk, 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(disk, &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 disk		Disk
+ * @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(disk_t *disk, 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(disk, &cp, sizeof(cp), obuf, obuf_size);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+/** Issue ATAPI read TOC command.
+ *
+ * Read TOC in 'multi-session' format (first and last session number
+ * with last session LBA).
+ *
+ * http://suif.stanford.edu/~csapuntz/specs/INF-8020.PDF page 171
+ *
+ * Output buffer must be large enough to hold the data, otherwise the
+ * function will fail.
+ *
+ * @param disk		Disk
+ * @param session	Starting session
+ * @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_toc(disk_t *disk, uint8_t session, void *obuf,
+    size_t obuf_size)
+{
+	ata_pcmd_read_toc_t cp;
+	int rc;
+
+	memset(&cp, 0, sizeof(cp));
+
+	cp.opcode = PCMD_READ_TOC;
+	cp.msf = 0;
+	cp.format = 0x01; /* 0x01 = multi-session mode */
+	cp.start = session;
+	cp.size = host2uint16_t_be(obuf_size);
+	cp.oldformat = 0x40; /* 0x01 = multi-session mode (shifted to MSB) */
+	
+	rc = ata_cmd_packet(disk, &cp, sizeof(cp), obuf, obuf_size);
+	if (rc != EOK)
+		return rc;
+	
+	return EOK;
+}
+
+/** Read a physical from the device.
+ *
+ * @param disk		Disk
+ * @param ba		Address the first block.
+ * @param cnt		Number of blocks to transfer.
+ * @param buf		Buffer for holding the data.
+ *
+ * @return EOK on success, EIO on error.
+ */
+static int ata_rcmd_read(disk_t *disk, uint64_t ba, size_t blk_cnt,
+    void *buf)
+{
+	ata_ctrl_t *ctrl = disk->ctrl;
+	size_t i;
+	uint16_t data;
+	uint8_t status;
+	uint8_t drv_head;
+	block_coord_t bc;
+
+	/* Silence warning. */
+	memset(&bc, 0, sizeof(bc));
+
+	/* Compute block coordinates. */
+	if (coord_calc(disk, ba, &bc) != EOK)
+		return EINVAL;
+
+	/* New value for Drive/Head register */
+	drv_head =
+	    ((disk_dev_idx(disk) != 0) ? DHR_DRV : 0) |
+	    ((disk->amode != am_chs) ? DHR_LBA : 0) |
+	    (bc.h & 0x0f);
+
+	fibril_mutex_lock(&ctrl->lock);
+
+	/* Program a Read Sectors operation. */
+
+	if (wait_status(ctrl, 0, ~SR_BSY, NULL, TIMEOUT_BSY) != EOK) {
+		fibril_mutex_unlock(&ctrl->lock);
+		return EIO;
+	}
+
+	pio_write_8(&ctrl->cmd->drive_head, drv_head);
+
+	if (wait_status(ctrl, SR_DRDY, ~SR_BSY, NULL, TIMEOUT_DRDY) != EOK) {
+		fibril_mutex_unlock(&ctrl->lock);
+		return EIO;
+	}
+
+	/* Program block coordinates into the device. */
+	coord_sc_program(ctrl, &bc, 1);
+
+	pio_write_8(&ctrl->cmd->command, disk->amode == am_lba48 ?
+	    CMD_READ_SECTORS_EXT : CMD_READ_SECTORS);
+
+	if (wait_status(ctrl, 0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
+		fibril_mutex_unlock(&ctrl->lock);
+		return EIO;
+	}
+
+	if ((status & SR_DRQ) != 0) {
+		/* Read data from the device buffer. */
+
+		for (i = 0; i < disk->block_size / 2; i++) {
+			data = pio_read_16(&ctrl->cmd->data_port);
+			((uint16_t *) buf)[i] = data;
+		}
+	}
+
+	fibril_mutex_unlock(&ctrl->lock);
+
+	if ((status & SR_ERR) != 0)
+		return EIO;
+
+	return EOK;
+}
+
+/** Write a physical block to the device.
+ *
+ * @param disk		Disk
+ * @param ba		Address of the first block.
+ * @param cnt		Number of blocks to transfer.
+ * @param buf		Buffer holding the data to write.
+ *
+ * @return EOK on success, EIO on error.
+ */
+static int ata_rcmd_write(disk_t *disk, uint64_t ba, size_t cnt,
+    const void *buf)
+{
+	ata_ctrl_t *ctrl = disk->ctrl;
+	size_t i;
+	uint8_t status;
+	uint8_t drv_head;
+	block_coord_t bc;
+
+	/* Silence warning. */
+	memset(&bc, 0, sizeof(bc));
+
+	/* Compute block coordinates. */
+	if (coord_calc(disk, ba, &bc) != EOK)
+		return EINVAL;
+
+	/* New value for Drive/Head register */
+	drv_head =
+	    ((disk_dev_idx(disk) != 0) ? DHR_DRV : 0) |
+	    ((disk->amode != am_chs) ? DHR_LBA : 0) |
+	    (bc.h & 0x0f);
+
+	fibril_mutex_lock(&ctrl->lock);
+
+	/* Program a Write Sectors operation. */
+
+	if (wait_status(ctrl, 0, ~SR_BSY, NULL, TIMEOUT_BSY) != EOK) {
+		fibril_mutex_unlock(&ctrl->lock);
+		return EIO;
+	}
+
+	pio_write_8(&ctrl->cmd->drive_head, drv_head);
+
+	if (wait_status(ctrl, SR_DRDY, ~SR_BSY, NULL, TIMEOUT_DRDY) != EOK) {
+		fibril_mutex_unlock(&ctrl->lock);
+		return EIO;
+	}
+
+	/* Program block coordinates into the device. */
+	coord_sc_program(ctrl, &bc, 1);
+
+	pio_write_8(&ctrl->cmd->command, disk->amode == am_lba48 ?
+	    CMD_WRITE_SECTORS_EXT : CMD_WRITE_SECTORS);
+
+	if (wait_status(ctrl, 0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
+		fibril_mutex_unlock(&ctrl->lock);
+		return EIO;
+	}
+
+	if ((status & SR_DRQ) != 0) {
+		/* Write data to the device buffer. */
+
+		for (i = 0; i < disk->block_size / 2; i++) {
+			pio_write_16(&ctrl->cmd->data_port, ((uint16_t *) buf)[i]);
+		}
+	}
+
+	fibril_mutex_unlock(&ctrl->lock);
+
+	if (status & SR_ERR)
+		return EIO;
+
+	return EOK;
+}
+
+/** Calculate block coordinates.
+ *
+ * Calculates block coordinates in the best coordinate system supported
+ * by the device. These can be later programmed into the device using
+ * @c coord_sc_program().
+ *
+ * @return EOK on success or EINVAL if block index is past end of device.
+ */
+static int coord_calc(disk_t *d, uint64_t ba, block_coord_t *bc)
+{
+	uint64_t c;
+	uint64_t idx;
+
+	/* Check device bounds. */
+	if (ba >= d->blocks)
+		return EINVAL;
+
+	bc->amode = d->amode;
+
+	switch (d->amode) {
+	case am_chs:
+		/* Compute CHS coordinates. */
+		c = ba / (d->geom.heads * d->geom.sectors);
+		idx = ba % (d->geom.heads * d->geom.sectors);
+
+		bc->cyl_lo = c & 0xff;
+		bc->cyl_hi = (c >> 8) & 0xff;
+		bc->h      = (idx / d->geom.sectors) & 0x0f;
+		bc->sector = (1 + (idx % d->geom.sectors)) & 0xff;
+		break;
+
+	case am_lba28:
+		/* Compute LBA-28 coordinates. */
+		bc->c0 = ba & 0xff;		/* bits 0-7 */
+		bc->c1 = (ba >> 8) & 0xff;	/* bits 8-15 */
+		bc->c2 = (ba >> 16) & 0xff;	/* bits 16-23 */
+		bc->h  = (ba >> 24) & 0x0f;	/* bits 24-27 */
+		break;
+
+	case am_lba48:
+		/* Compute LBA-48 coordinates. */
+		bc->c0 = ba & 0xff;		/* bits 0-7 */
+		bc->c1 = (ba >> 8) & 0xff;	/* bits 8-15 */
+		bc->c2 = (ba >> 16) & 0xff;	/* bits 16-23 */
+		bc->c3 = (ba >> 24) & 0xff;	/* bits 24-31 */
+		bc->c4 = (ba >> 32) & 0xff;	/* bits 32-39 */
+		bc->c5 = (ba >> 40) & 0xff;	/* bits 40-47 */
+		bc->h  = 0;
+		break;
+	}
+
+	return EOK;
+}
+
+/** Program block coordinates and sector count into ATA registers.
+ *
+ * Note that bc->h must be programmed separately into the device/head register.
+ *
+ * @param ctrl		Controller
+ * @param bc		Block coordinates
+ * @param scnt		Sector count
+ */
+static void coord_sc_program(ata_ctrl_t *ctrl, const block_coord_t *bc,
+    uint16_t scnt)
+{
+	ata_cmd_t *cmd = ctrl->cmd;
+
+	if (bc->amode == am_lba48) {
+		/* Write high-order bits. */
+		pio_write_8(&cmd->sector_count, scnt >> 8);
+		pio_write_8(&cmd->sector_number, bc->c3);
+		pio_write_8(&cmd->cylinder_low, bc->c4);
+		pio_write_8(&cmd->cylinder_high, bc->c5);
+	}
+
+	/* Write low-order bits. */
+	pio_write_8(&cmd->sector_count, scnt & 0x00ff);
+	pio_write_8(&cmd->sector_number, bc->c0);
+	pio_write_8(&cmd->cylinder_low, bc->c1);
+	pio_write_8(&cmd->cylinder_high, bc->c2);
+}
+
+/** Wait until some status bits are set and some are reset.
+ *
+ * Example: wait_status(ctrl, SR_DRDY, ~SR_BSY, ...) waits for SR_DRDY to become
+ * set and SR_BSY to become reset.
+ *
+ * @param ctrl		Controller
+ * @param set		Combination if bits which must be all set.
+ * @param n_reset	Negated combination of bits which must be all reset.
+ * @param pstatus	Pointer where to store last read status or NULL.
+ * @param timeout	Timeout in 10ms units.
+ *
+ * @return		EOK on success, EIO on timeout.
+ */
+static int wait_status(ata_ctrl_t *ctrl, unsigned set, unsigned n_reset,
+    uint8_t *pstatus, unsigned timeout)
+{
+	uint8_t status;
+	int cnt;
+
+	status = pio_read_8(&ctrl->cmd->status);
+
+	/*
+	 * This is crude, yet simple. First try with 1us delays
+	 * (most likely the device will respond very fast). If not,
+	 * start trying every 10 ms.
+	 */
+
+	cnt = 100;
+	while ((status & ~n_reset) != 0 || (status & set) != set) {
+		async_usleep(1);
+		--cnt;
+		if (cnt <= 0) break;
+
+		status = pio_read_8(&ctrl->cmd->status);
+	}
+
+	cnt = timeout;
+	while ((status & ~n_reset) != 0 || (status & set) != set) {
+		async_usleep(10000);
+		--cnt;
+		if (cnt <= 0) break;
+
+		status = pio_read_8(&ctrl->cmd->status);
+	}
+
+	if (pstatus)
+		*pstatus = status;
+
+	if (cnt == 0)
+		return EIO;
+
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/block/ata_bd/ata_bd.h
===================================================================
--- uspace/drv/block/ata_bd/ata_bd.h	(revision a8196c90457a5217af091168fccd2409d482c81d)
+++ uspace/drv/block/ata_bd/ata_bd.h	(revision a8196c90457a5217af091168fccd2409d482c81d)
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2009 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup bd
+ * @{
+ */
+/** @file ATA driver definitions.
+ */
+
+#ifndef __ATA_BD_H__
+#define __ATA_BD_H__
+
+#include <async.h>
+#include <bd_srv.h>
+#include <ddf/driver.h>
+#include <sys/types.h>
+#include <fibril_synch.h>
+#include <str.h>
+#include "ata_hw.h"
+
+#define NAME "ata_bd"
+
+/** Base addresses for ATA I/O blocks. */
+typedef struct {
+	uintptr_t cmd;	/**< Command block base address. */
+	uintptr_t ctl;	/**< Control block base address. */
+} ata_base_t;
+
+/** Timeout definitions. Unit is 10 ms. */
+enum ata_timeout {
+	TIMEOUT_PROBE	=  100, /*  1 s */
+	TIMEOUT_BSY	=  100, /*  1 s */
+	TIMEOUT_DRDY	= 1000  /* 10 s */
+};
+
+enum ata_dev_type {
+	ata_reg_dev,	/* Register device (no packet feature set support) */
+	ata_pkt_dev	/* Packet device (supports packet feature set). */
+};
+
+/** Register device block addressing mode. */
+enum rd_addr_mode {
+	am_chs,		/**< CHS block addressing */
+	am_lba28,	/**< LBA-28 block addressing */
+	am_lba48	/**< LBA-48 block addressing */
+};
+
+/** Block coordinates */
+typedef struct {
+	enum rd_addr_mode amode;
+
+	union {
+		/** CHS coordinates */
+		struct {
+			uint8_t sector;
+			uint8_t cyl_lo;
+			uint8_t cyl_hi;
+		};
+		/** LBA coordinates */
+		struct {
+			uint8_t c0;
+			uint8_t c1;
+			uint8_t c2;
+			uint8_t c3;
+			uint8_t c4;
+			uint8_t c5;
+		};
+	};
+
+	/** Lower 4 bits for device/head register */
+	uint8_t h;
+} block_coord_t;
+
+/** ATA device state structure. */
+typedef struct {
+	bool present;
+	struct ata_ctrl *ctrl;
+	struct ata_fun *afun;
+
+	/** Device type */
+	enum ata_dev_type dev_type;
+
+	/** Addressing mode to use (if register device) */
+	enum rd_addr_mode amode;
+
+	/*
+	 * Geometry. Only valid if operating in CHS mode.
+	 */
+	struct {
+		unsigned heads;
+		unsigned cylinders;
+		unsigned sectors;
+	} geom;
+
+	uint64_t blocks;
+	size_t block_size;
+
+	char model[STR_BOUNDS(40) + 1];
+
+	int disk_id;
+} disk_t;
+
+/** ATA controller */
+typedef struct ata_ctrl {
+	/** DDF device */
+	ddf_dev_t *dev;
+	/** I/O base address of the command registers */
+	uintptr_t cmd_physical;
+	/** I/O base address of the control registers */
+	uintptr_t ctl_physical;
+
+	/** Command registers */
+	ata_cmd_t *cmd;
+	/** Control registers */
+	ata_ctl_t *ctl;
+
+	/** Per-disk state. */
+	disk_t disk[MAX_DISKS];
+
+	fibril_mutex_t lock;
+} ata_ctrl_t;
+
+typedef struct ata_fun {
+	ddf_fun_t *fun;
+	disk_t *disk;
+	bd_srvs_t bds;
+} ata_fun_t;
+
+extern int ata_ctrl_init(ata_ctrl_t *);
+extern int ata_ctrl_remove(ata_ctrl_t *);
+extern int ata_ctrl_gone(ata_ctrl_t *);
+
+extern bd_ops_t ata_bd_ops;
+
+#endif
+
+/** @}
+ */
Index: uspace/drv/block/ata_bd/ata_bd.ma
===================================================================
--- uspace/drv/block/ata_bd/ata_bd.ma	(revision a8196c90457a5217af091168fccd2409d482c81d)
+++ uspace/drv/block/ata_bd/ata_bd.ma	(revision a8196c90457a5217af091168fccd2409d482c81d)
@@ -0,0 +1,1 @@
+10 isa/ata_bd
Index: uspace/drv/block/ata_bd/ata_hw.h
===================================================================
--- uspace/drv/block/ata_bd/ata_hw.h	(revision a8196c90457a5217af091168fccd2409d482c81d)
+++ uspace/drv/block/ata_bd/ata_hw.h	(revision a8196c90457a5217af091168fccd2409d482c81d)
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2009 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup bd
+ * @{
+ */
+/** @file ATA hardware protocol (registers, data structures).
+ */
+
+#ifndef __ATA_HW_H__
+#define __ATA_HW_H__
+
+#include <sys/types.h>
+
+enum {
+	CTL_READ_START  = 0,
+	CTL_WRITE_START = 1,
+};
+
+enum {
+	STATUS_FAILURE = 0
+};
+
+enum {
+	MAX_DISKS	= 2
+};
+
+/** ATA Command Register Block. */
+typedef union {
+	/* Read/Write */
+	struct {
+		uint16_t data_port;
+		uint8_t sector_count;
+		uint8_t sector_number;
+		uint8_t cylinder_low;
+		uint8_t cylinder_high;
+		uint8_t drive_head;
+		uint8_t pad_rw0;
+	};
+
+	/* Read Only */
+	struct {
+		uint8_t pad_ro0;
+		uint8_t error;
+		uint8_t pad_ro1[5];
+		uint8_t status;
+	};
+
+	/* Write Only */
+	struct {
+		uint8_t pad_wo0;
+		uint8_t features;
+		uint8_t pad_wo1[5];
+		uint8_t command;
+	};
+} ata_cmd_t;
+
+typedef union {
+	/* Read */
+	struct {
+		uint8_t pad0[6];
+		uint8_t alt_status;
+		uint8_t drive_address;
+	};
+
+	/* Write */
+	struct {
+		uint8_t pad1[6];
+		uint8_t device_control;
+		uint8_t pad2;
+	};
+} ata_ctl_t;
+
+enum devctl_bits {
+	DCR_SRST	= 0x04, /**< Software Reset */
+	DCR_nIEN	= 0x02  /**< Interrupt Enable (negated) */
+};
+
+enum status_bits {
+	SR_BSY		= 0x80, /**< Busy */
+	SR_DRDY		= 0x40, /**< Drive Ready */
+	SR_DWF		= 0x20, /**< Drive Write Fault */
+	SR_DSC		= 0x10, /**< Drive Seek Complete */
+	SR_DRQ		= 0x08, /**< Data Request */
+	SR_CORR		= 0x04, /**< Corrected Data */
+	SR_IDX		= 0x02, /**< Index */
+	SR_ERR		= 0x01  /**< Error */
+};
+
+enum drive_head_bits {
+	DHR_LBA		= 0x40,	/**< Use LBA addressing mode */
+	DHR_DRV		= 0x10	/**< Select device 1 */
+};
+
+enum error_bits {
+	ER_BBK		= 0x80, /**< Bad Block Detected */
+	ER_UNC		= 0x40, /**< Uncorrectable Data Error */
+	ER_MC		= 0x20, /**< Media Changed */
+	ER_IDNF		= 0x10, /**< ID Not Found */
+	ER_MCR		= 0x08, /**< Media Change Request */
+	ER_ABRT		= 0x04, /**< Aborted Command */
+	ER_TK0NF	= 0x02, /**< Track 0 Not Found */
+	ER_AMNF		= 0x01  /**< Address Mark Not Found */
+};
+
+enum ata_command {
+	CMD_READ_SECTORS	= 0x20,
+	CMD_READ_SECTORS_EXT	= 0x24,
+	CMD_WRITE_SECTORS	= 0x30,
+	CMD_WRITE_SECTORS_EXT	= 0x34,
+	CMD_PACKET		= 0xA0,
+	CMD_IDENTIFY_PKT_DEV	= 0xA1,
+	CMD_IDENTIFY_DRIVE	= 0xEC
+};
+
+/** Data returned from identify device and identify packet device command. */
+typedef struct {
+	uint16_t gen_conf;
+	uint16_t cylinders;
+	uint16_t _res2;
+	uint16_t heads;
+	uint16_t _vs4;
+	uint16_t _vs5;
+	uint16_t sectors;
+	uint16_t _vs7;
+	uint16_t _vs8;
+	uint16_t _vs9;
+
+	uint16_t serial_number[10];
+	uint16_t _vs20;
+	uint16_t _vs21;
+	uint16_t vs_bytes;
+	uint16_t firmware_rev[4];
+	uint16_t model_name[20];
+
+	uint16_t max_rw_multiple;
+	uint16_t _res48;
+	uint16_t caps;		/* Different meaning for packet device */
+	uint16_t _res50;
+	uint16_t pio_timing;
+	uint16_t dma_timing;
+
+	uint16_t validity;
+	uint16_t cur_cyl;
+	uint16_t cur_heads;
+	uint16_t cur_sectors;
+	uint16_t cur_capacity0;
+	uint16_t cur_capacity1;
+	uint16_t mss;
+	uint16_t total_lba28_0;
+	uint16_t total_lba28_1;
+	uint16_t sw_dma;
+	uint16_t mw_dma;
+	uint16_t pio_modes;
+	uint16_t min_mw_dma_cycle;
+	uint16_t rec_mw_dma_cycle;
+	uint16_t min_raw_pio_cycle;
+	uint16_t min_iordy_pio_cycle;
+
+	uint16_t _res69;
+	uint16_t _res70;
+	uint16_t _res71;
+	uint16_t _res72;
+	uint16_t _res73;
+	uint16_t _res74;
+
+	uint16_t queue_depth;
+	uint16_t _res76[1 + 79 - 76];
+	uint16_t version_maj;
+	uint16_t version_min;
+	uint16_t cmd_set0;
+	uint16_t cmd_set1;
+	uint16_t csf_sup_ext;
+	uint16_t csf_enabled0;
+	uint16_t csf_enabled1;
+	uint16_t csf_default;
+	uint16_t udma;
+
+	uint16_t _res89[1 + 99 - 89];
+
+	/* Total number of blocks in LBA-48 addressing */
+	uint16_t total_lba48_0;
+	uint16_t total_lba48_1;
+	uint16_t total_lba48_2;
+	uint16_t total_lba48_3;
+
+	/* Note: more fields are defined in ATA/ATAPI-7 */
+	uint16_t _res104[1 + 127 - 104];
+	uint16_t _vs128[1 + 159 - 128];
+	uint16_t _res160[1 + 255 - 160];
+} identify_data_t;
+
+/** Capability bits for register device. */
+enum ata_regdev_caps {
+	rd_cap_iordy		= 0x0800,
+	rd_cap_iordy_cbd	= 0x0400,
+	rd_cap_lba		= 0x0200,
+	rd_cap_dma		= 0x0100
+};
+
+/** Capability bits for packet device. */
+enum ata_pktdev_caps {
+	pd_cap_ildma		= 0x8000,
+	pd_cap_cmdqueue		= 0x4000,
+	pd_cap_overlap		= 0x2000,
+	pd_cap_need_softreset	= 0x1000,	/* Obsolete (ATAPI-6) */
+	pd_cap_iordy		= 0x0800,
+	pd_cap_iordy_dis	= 0x0400,
+	pd_cap_lba		= 0x0200,	/* Must be on */
+	pd_cap_dma		= 0x0100
+};
+
+/** Bits of @c identify_data_t.cmd_set1 */
+enum ata_cs1 {
+	cs1_addr48	= 0x0400	/**< 48-bit address feature set */
+};
+
+/** ATA packet command codes. */
+enum ata_pkt_command {
+	PCMD_INQUIRY		= 0x12,
+	PCMD_READ_12		= 0xa8,
+	PCMD_READ_TOC		= 0x43
+};
+
+/** ATAPI Inquiry command */
+typedef struct {
+	uint8_t opcode;		/**< Operation code (PCMD_INQUIRY) */
+	uint8_t _res0;
+	uint8_t _res1;
+	uint8_t _res2;
+	uint8_t alloc_len;	/**< Allocation length */
+	uint8_t _res3;
+	uint8_t _res4;
+	uint8_t _res5;
+	uint32_t _res6;
+} __attribute__ ((packed)) ata_pcmd_inquiry_t;
+
+/** ATAPI Read(12) command */
+typedef struct {
+	uint8_t opcode;		/**< Operation code (PCMD_READ_12) */
+	uint8_t _res0;
+	uint32_t ba;		/**< Starting block address */
+	uint32_t nblocks;	/**< Number of blocks to transfer */
+	uint8_t _res1;
+	uint8_t _res2;
+} __attribute__ ((packed)) ata_pcmd_read_12_t;
+
+/** ATAPI Read TOC command */
+typedef struct {
+	uint8_t opcode;		/**< Operation code (PCMD_READ_TOC) */
+	uint8_t msf;            /**< 0x2 = MSF bit set */
+	uint8_t format;         /**< low 3 bits */
+	uint8_t _res0;
+	uint8_t _res1;
+	uint8_t _res2;
+	uint8_t start;          /**< starting track/session number */
+	uint16_t size;		/**< Allocation length */
+	uint8_t oldformat;         /**< high 2 bits */
+	uint8_t _res3;
+	uint8_t _res4;
+} __attribute__ ((packed)) ata_pcmd_read_toc_t;
+
+/** Data returned from Inquiry command (mandatory part) */
+typedef struct {
+	uint8_t pdev_type;	/** Reserved, Peripheral device type */
+	uint8_t rmb;		/** RMB, Reserved */
+	uint8_t std_version;	/** ISO version, ECMA version, ANSI version */
+	uint8_t atapi_ver_rdf;	/** ATAPI version, Response data format */
+	uint8_t additional_len;	/** Additional length */
+	uint8_t _res0;
+	uint8_t _res1;
+	uint8_t _res2;
+	uint8_t vendor_id[8];	/** Vendor ID */
+	uint8_t product_id[8];	/** Product ID */
+	uint8_t product_rev[4];	/** Product revision level */
+} ata_inquiry_data_t;
+
+/** Extract value of ata_inquiry_data_t.pdev_type */
+#define INQUIRY_PDEV_TYPE(val) ((val) & 0x1f)
+
+/** Values for ata_inquiry_data_t.pdev_type */
+enum ata_pdev_type {
+	PDEV_TYPE_CDROM		= 0x05
+};
+
+enum ata_pdev_signature {
+	/**
+	 * Signature put by a packet device in byte count register
+	 * in response to Identify command.
+	 */
+	PDEV_SIGNATURE_BC	= 0xEB14
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/drv/block/ata_bd/main.c
===================================================================
--- uspace/drv/block/ata_bd/main.c	(revision a8196c90457a5217af091168fccd2409d482c81d)
+++ uspace/drv/block/ata_bd/main.c	(revision a8196c90457a5217af091168fccd2409d482c81d)
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+#include <ddf/driver.h>
+#include <ddf/log.h>
+
+#include "ata_bd.h"
+#include "main.h"
+
+static int ata_dev_add(ddf_dev_t *dev);
+static int ata_dev_remove(ddf_dev_t *dev);
+static int ata_dev_gone(ddf_dev_t *dev);
+static int ata_fun_online(ddf_fun_t *fun);
+static int ata_fun_offline(ddf_fun_t *fun);
+
+static void ata_bd_connection(ipc_callid_t, ipc_call_t *, void *);
+
+static driver_ops_t driver_ops = {
+	.dev_add = &ata_dev_add,
+	.dev_remove = &ata_dev_remove,
+	.dev_gone = &ata_dev_gone,
+	.fun_online = &ata_fun_online,
+	.fun_offline = &ata_fun_offline
+};
+
+static driver_t ata_driver = {
+	.name = NAME,
+	.driver_ops = &driver_ops
+};
+
+
+/** Add new device
+ *
+ * @param  dev New device
+ * @return     EOK on success or negative error code.
+ */
+static int ata_dev_add(ddf_dev_t *dev)
+{
+	ata_ctrl_t *ctrl;
+	int rc;
+
+	ctrl = ddf_dev_data_alloc(dev, sizeof(ata_ctrl_t));
+	if (ctrl == NULL) {
+		ddf_msg(LVL_ERROR, "Failed allocating soft state.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	ctrl->dev = dev;
+
+	rc = ata_ctrl_init(ctrl);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed initializing ATA controller.");
+		rc = EIO;
+		goto error;
+	}
+
+	return EOK;
+error:
+	return rc;
+}
+
+static char *ata_fun_name(disk_t *disk)
+{
+	char *fun_name;
+
+	if (asprintf(&fun_name, "d%u", disk->disk_id) < 0)
+		return NULL;
+
+	return fun_name;
+}
+
+int ata_fun_create(disk_t *disk)
+{
+	ata_ctrl_t *ctrl = disk->ctrl;
+	int rc;
+	char *fun_name = NULL;
+	ddf_fun_t *fun = NULL;
+	ata_fun_t *afun = NULL;
+
+	fun_name = ata_fun_name(disk);
+	if (fun_name == NULL) {
+		ddf_msg(LVL_ERROR, "Out of memory.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	fun = ddf_fun_create(ctrl->dev, fun_exposed, fun_name);
+	if (fun == NULL) {
+		ddf_msg(LVL_ERROR, "Failed creating DDF function.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	/* Allocate soft state */
+	afun = ddf_fun_data_alloc(fun, sizeof(ata_fun_t));
+	if (afun == NULL) {
+		ddf_msg(LVL_ERROR, "Failed allocating softstate.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	afun->fun = fun;
+	afun->disk = disk;
+
+	bd_srvs_init(&afun->bds);
+	afun->bds.ops = &ata_bd_ops;
+	afun->bds.sarg = disk;
+
+	/* Set up a connection handler. */
+	ddf_fun_set_conn_handler(fun, ata_bd_connection);
+
+	rc = ddf_fun_bind(fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed binding DDF function %s: %s",
+		    fun_name, str_error(rc));
+		goto error;
+	}
+
+	free(fun_name);
+	disk->afun = afun;
+	return EOK;
+error:
+	if (fun != NULL)
+		ddf_fun_destroy(fun);
+	if (fun_name != NULL)
+		free(fun_name);
+
+	return rc;
+}
+
+int ata_fun_remove(disk_t *disk)
+{
+	int rc;
+	char *fun_name;
+
+	if (disk->afun == NULL)
+		return EOK;
+
+	fun_name = ata_fun_name(disk);
+	if (fun_name == NULL) {
+		ddf_msg(LVL_ERROR, "Out of memory.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	ddf_msg(LVL_DEBUG, "ata_fun_remove(%p, '%s')", disk, fun_name);
+	rc = ddf_fun_offline(disk->afun->fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Error offlining function '%s'.", fun_name);
+		goto error;
+	}
+
+	rc = ddf_fun_unbind(disk->afun->fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.", fun_name);
+		goto error;
+	}
+
+	ddf_fun_destroy(disk->afun->fun);
+	disk->afun = NULL;
+	free(fun_name);
+	return EOK;
+error:
+	if (fun_name != NULL)
+		free(fun_name);
+	return rc;
+}
+
+int ata_fun_unbind(disk_t *disk)
+{
+	int rc;
+	char *fun_name;
+
+	if (disk->afun == NULL)
+		return EOK;
+
+	fun_name = ata_fun_name(disk);
+	if (fun_name == NULL) {
+		ddf_msg(LVL_ERROR, "Out of memory.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	ddf_msg(LVL_DEBUG, "ata_fun_unbind(%p, '%s')", disk, fun_name);
+	rc = ddf_fun_unbind(disk->afun->fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.", fun_name);
+		goto error;
+	}
+
+	ddf_fun_destroy(disk->afun->fun);
+	disk->afun = NULL;
+	free(fun_name);
+	return EOK;
+error:
+	if (fun_name != NULL)
+		free(fun_name);
+	return rc;
+}
+
+static int ata_dev_remove(ddf_dev_t *dev)
+{
+	ata_ctrl_t *ctrl = (ata_ctrl_t *)ddf_dev_data_get(dev);
+
+	ddf_msg(LVL_DEBUG, "ata_dev_remove(%p)", dev);
+
+	return ata_ctrl_remove(ctrl);
+}
+
+static int ata_dev_gone(ddf_dev_t *dev)
+{
+	ata_ctrl_t *ctrl = (ata_ctrl_t *)ddf_dev_data_get(dev);
+
+	ddf_msg(LVL_DEBUG, "ata_dev_gone(%p)", dev);
+
+	return ata_ctrl_gone(ctrl);
+}
+
+static int ata_fun_online(ddf_fun_t *fun)
+{
+	ddf_msg(LVL_DEBUG, "ata_fun_online()");
+	return ddf_fun_online(fun);
+}
+
+static int ata_fun_offline(ddf_fun_t *fun)
+{
+	ddf_msg(LVL_DEBUG, "ata_fun_offline()");
+	return ddf_fun_offline(fun);
+}
+
+/** Block device connection handler */
+static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	ata_fun_t *afun;
+
+	afun = (ata_fun_t *) ddf_fun_data_get((ddf_fun_t *)arg);
+	bd_conn(iid, icall, &afun->bds);
+}
+
+int main(int argc, char *argv[])
+{
+	printf(NAME ": HelenOS ATA(PI) device driver\n");
+	ddf_log_init(NAME);
+	return ddf_driver_main(&ata_driver);
+}
+
Index: uspace/drv/block/ata_bd/main.h
===================================================================
--- uspace/drv/block/ata_bd/main.h	(revision a8196c90457a5217af091168fccd2409d482c81d)
+++ uspace/drv/block/ata_bd/main.h	(revision a8196c90457a5217af091168fccd2409d482c81d)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup bd
+ * @{
+ */
+/** @file ATA driver main module
+ */
+
+#ifndef __ATA_MAIN_H__
+#define __ATA_MAIN_H__
+
+#include "ata_bd.h"
+
+extern int ata_fun_create(disk_t *);
+extern int ata_fun_remove(disk_t *);
+extern int ata_fun_unbind(disk_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/drv/bus/isa/isa.dev
===================================================================
--- uspace/drv/bus/isa/isa.dev	(revision 89ac55135165454a51d40d8fa82bf99517b61933)
+++ uspace/drv/bus/isa/isa.dev	(revision a8196c90457a5217af091168fccd2409d482c81d)
@@ -31,2 +31,5 @@
 	match 100 isa/cmos-rtc
 	io_range 70 2
+
+ata_bd:
+	match 100 isa/ata_bd
