Index: meson/arch/amd64/meson.build
===================================================================
--- meson/arch/amd64/meson.build	(revision 2791fbb7c1f204fd66ed105c6cc1ddf9e8f5bedf)
+++ meson/arch/amd64/meson.build	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
@@ -62,5 +62,5 @@
 	'drv/intctl/i8259',
 	'drv/platform/pc',
-	'drv/block/ata_bd',
+	'drv/block/isa-ide',
 	'drv/bus/pci/pciintel',
 	'drv/bus/isa',
@@ -76,5 +76,5 @@
 	'drv/audio/sb16',
 	'drv/block/ahci',
-	'drv/block/ata_bd',
+	'drv/block/isa-ide',
 	'drv/block/usbmast',
 	'drv/block/virtio-blk',
Index: meson/arch/ia32/meson.build
===================================================================
--- meson/arch/ia32/meson.build	(revision 2791fbb7c1f204fd66ed105c6cc1ddf9e8f5bedf)
+++ meson/arch/ia32/meson.build	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
@@ -68,5 +68,5 @@
 	'drv/intctl/i8259',
 	'drv/platform/pc',
-	'drv/block/ata_bd',
+	'drv/block/isa-ide',
 	'drv/bus/pci/pciintel',
 	'drv/bus/isa',
@@ -82,5 +82,5 @@
 	'drv/audio/sb16',
 	'drv/block/ahci',
-	'drv/block/ata_bd',
+	'drv/block/isa-ide',
 	'drv/block/usbmast',
 	'drv/block/virtio-blk',
Index: meson/arch/mips32/meson.build
===================================================================
--- meson/arch/mips32/meson.build	(revision 2791fbb7c1f204fd66ed105c6cc1ddf9e8f5bedf)
+++ meson/arch/mips32/meson.build	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
@@ -67,5 +67,5 @@
 		'drv/platform/malta',
 		'drv/intctl/i8259',
-		'drv/block/ata_bd',
+		'drv/block/isa-ide',
 		'drv/bus/pci/pciintel',
 		'drv/bus/isa',
Index: pace/drv/block/ata_bd/ata_bd.c
===================================================================
--- uspace/drv/block/ata_bd/ata_bd.c	(revision 2791fbb7c1f204fd66ed105c6cc1ddf9e8f5bedf)
+++ 	(revision )
@@ -1,527 +1,0 @@
-/*
- * Copyright (c) 2024 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 ata_bd
- * @{
- */
-
-/**
- * @file
- * @brief ISA ATA driver
- *
- * The driver services a single IDE channel.
- */
-
-#include <ddi.h>
-#include <ddf/interrupt.h>
-#include <ddf/log.h>
-#include <async.h>
-#include <as.h>
-#include <fibril_synch.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <str.h>
-#include <inttypes.h>
-#include <errno.h>
-
-#include "ata_bd.h"
-#include "main.h"
-
-#define NAME       "ata_bd"
-
-static errno_t ata_bd_init_io(ata_ctrl_t *ctrl);
-static void ata_bd_fini_io(ata_ctrl_t *ctrl);
-static errno_t ata_bd_init_irq(ata_ctrl_t *ctrl);
-static void ata_bd_fini_irq(ata_ctrl_t *ctrl);
-static void ata_irq_handler(ipc_call_t *call, ddf_dev_t *dev);
-
-static void ata_write_data_16(void *, uint16_t *, size_t);
-static void ata_read_data_16(void *, uint16_t *, size_t);
-static void ata_write_cmd_8(void *, uint16_t, uint8_t);
-static uint8_t ata_read_cmd_8(void *, uint16_t);
-static void ata_write_ctl_8(void *, uint16_t, uint8_t);
-static uint8_t ata_read_ctl_8(void *, uint16_t);
-static errno_t ata_irq_enable(void *);
-static errno_t ata_irq_disable(void *);
-static errno_t ata_add_device(void *, unsigned, void *);
-static errno_t ata_remove_device(void *, unsigned);
-static void ata_msg_debug(void *, char *);
-static void ata_msg_note(void *, char *);
-static void ata_msg_warn(void *, char *);
-static void ata_msg_error(void *, char *);
-
-static const irq_pio_range_t ata_irq_ranges[] = {
-	{
-		.base = 0,
-		.size = sizeof(ata_cmd_t)
-	}
-};
-
-/** ATA interrupt pseudo code. */
-static const irq_cmd_t ata_irq_cmds[] = {
-	{
-		.cmd = CMD_PIO_READ_8,
-		.addr = NULL,  /* will be patched in run-time */
-		.dstarg = 1
-	},
-	{
-		.cmd = CMD_ACCEPT
-	}
-};
-
-/** Initialize ATA controller. */
-errno_t ata_ctrl_init(ata_ctrl_t *ctrl, ata_hwres_t *res)
-{
-	errno_t rc;
-	bool irq_inited = false;
-	ata_params_t params;
-
-	ddf_msg(LVL_DEBUG, "ata_ctrl_init()");
-
-	fibril_mutex_initialize(&ctrl->lock);
-	ctrl->cmd_physical = res->cmd;
-	ctrl->ctl_physical = res->ctl;
-	ctrl->irq = res->irq;
-
-	ddf_msg(LVL_NOTE, "I/O address %p/%p", (void *) ctrl->cmd_physical,
-	    (void *) ctrl->ctl_physical);
-
-	ddf_msg(LVL_DEBUG, "Init I/O");
-	rc = ata_bd_init_io(ctrl);
-	if (rc != EOK)
-		return rc;
-
-	ddf_msg(LVL_DEBUG, "Init IRQ");
-	rc = ata_bd_init_irq(ctrl);
-	if (rc != EOK) {
-		ddf_msg(LVL_NOTE, "init IRQ failed");
-		return rc;
-	}
-
-	irq_inited = true;
-
-	ddf_msg(LVL_DEBUG, "ata_ctrl_init(): Initialize ATA channel");
-
-	params.arg = (void *)ctrl;
-	params.have_irq = (ctrl->irq >= 0) ? true : false;
-	params.write_data_16 = ata_write_data_16;
-	params.read_data_16 = ata_read_data_16;
-	params.write_cmd_8 = ata_write_cmd_8;
-	params.read_cmd_8 = ata_read_cmd_8;
-	params.write_ctl_8 = ata_write_ctl_8;
-	params.read_ctl_8 = ata_read_ctl_8;
-	params.irq_enable = ata_irq_enable;
-	params.irq_disable = ata_irq_disable;
-	params.add_device = ata_add_device;
-	params.remove_device = ata_remove_device;
-	params.msg_debug = ata_msg_debug;
-	params.msg_note = ata_msg_note;
-	params.msg_warn = ata_msg_warn;
-	params.msg_error = ata_msg_error;
-
-	rc = ata_channel_create(&params, &ctrl->channel);
-	if (rc != EOK)
-		goto error;
-
-	rc = ata_channel_initialize(ctrl->channel);
-	if (rc != EOK)
-		goto error;
-
-	ddf_msg(LVL_DEBUG, "ata_ctrl_init: DONE");
-	return EOK;
-error:
-	if (irq_inited)
-		ata_bd_fini_irq(ctrl);
-	ata_bd_fini_io(ctrl);
-	return rc;
-}
-
-/** Remove ATA controller. */
-errno_t ata_ctrl_remove(ata_ctrl_t *ctrl)
-{
-	errno_t rc;
-
-	ddf_msg(LVL_DEBUG, ": ata_ctrl_remove()");
-
-	fibril_mutex_lock(&ctrl->lock);
-
-	rc = ata_channel_destroy(ctrl->channel);
-	if (rc != EOK) {
-		fibril_mutex_unlock(&ctrl->lock);
-		return rc;
-	}
-
-	ata_bd_fini_irq(ctrl);
-	ata_bd_fini_io(ctrl);
-	fibril_mutex_unlock(&ctrl->lock);
-
-	return EOK;
-}
-
-/** Surprise removal of ATA controller. */
-errno_t ata_ctrl_gone(ata_ctrl_t *ctrl)
-{
-	errno_t rc;
-
-	ddf_msg(LVL_DEBUG, "ata_ctrl_gone()");
-
-	fibril_mutex_lock(&ctrl->lock);
-
-	rc = ata_channel_destroy(ctrl->channel);
-	if (rc != EOK) {
-		fibril_mutex_unlock(&ctrl->lock);
-		return rc;
-	}
-
-	ata_bd_fini_io(ctrl);
-	fibril_mutex_unlock(&ctrl->lock);
-
-	return EOK;
-}
-
-/** Enable device I/O. */
-static errno_t ata_bd_init_io(ata_ctrl_t *ctrl)
-{
-	errno_t rc;
-	void *vaddr;
-
-	rc = pio_enable((void *) ctrl->cmd_physical, sizeof(ata_cmd_t), &vaddr);
-	if (rc != EOK) {
-		ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
-		return rc;
-	}
-
-	ctrl->cmd = vaddr;
-
-	rc = pio_enable((void *) ctrl->ctl_physical, sizeof(ata_ctl_t), &vaddr);
-	if (rc != EOK) {
-		ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
-		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 IRQ. */
-static errno_t ata_bd_init_irq(ata_ctrl_t *ctrl)
-{
-	irq_code_t irq_code;
-	irq_pio_range_t *ranges;
-	irq_cmd_t *cmds;
-	errno_t rc;
-
-	if (ctrl->irq < 0)
-		return EOK;
-
-	ranges = malloc(sizeof(ata_irq_ranges));
-	if (ranges == NULL)
-		return ENOMEM;
-
-	cmds = malloc(sizeof(ata_irq_cmds));
-	if (cmds == NULL) {
-		free(cmds);
-		return ENOMEM;
-	}
-
-	memcpy(ranges, &ata_irq_ranges, sizeof(ata_irq_ranges));
-	ranges[0].base = ctrl->cmd_physical;
-	memcpy(cmds, &ata_irq_cmds, sizeof(ata_irq_cmds));
-	cmds[0].addr = &ctrl->cmd->status;
-
-	irq_code.rangecount = sizeof(ata_irq_ranges) / sizeof(irq_pio_range_t);
-	irq_code.ranges = ranges;
-	irq_code.cmdcount = sizeof(ata_irq_cmds) / sizeof(irq_cmd_t);
-	irq_code.cmds = cmds;
-
-	ddf_msg(LVL_NOTE, "IRQ %d", ctrl->irq);
-	rc = register_interrupt_handler(ctrl->dev, ctrl->irq, ata_irq_handler,
-	    &irq_code, &ctrl->ihandle);
-	if (rc != EOK) {
-		ddf_msg(LVL_ERROR, "Error registering IRQ.");
-		goto error;
-	}
-
-	ddf_msg(LVL_DEBUG, "Interrupt handler registered");
-	free(ranges);
-	free(cmds);
-	return EOK;
-error:
-	free(ranges);
-	free(cmds);
-	return rc;
-}
-
-/** Clean up IRQ. */
-static void ata_bd_fini_irq(ata_ctrl_t *ctrl)
-{
-	errno_t rc;
-	async_sess_t *parent_sess;
-
-	parent_sess = ddf_dev_parent_sess_get(ctrl->dev);
-
-	rc = hw_res_disable_interrupt(parent_sess, ctrl->irq);
-	if (rc != EOK)
-		ddf_msg(LVL_ERROR, "Error disabling IRQ.");
-
-	(void) unregister_interrupt_handler(ctrl->dev, ctrl->ihandle);
-}
-
-/** Interrupt handler.
- *
- * @param call Call data
- * @param dev Device that caused the interrupt
- */
-static void ata_irq_handler(ipc_call_t *call, ddf_dev_t *dev)
-{
-	ata_ctrl_t *ctrl = (ata_ctrl_t *)ddf_dev_data_get(dev);
-	uint8_t status;
-	async_sess_t *parent_sess;
-
-	status = ipc_get_arg1(call);
-	ata_channel_irq(ctrl->channel, status);
-
-	parent_sess = ddf_dev_parent_sess_get(dev);
-	hw_res_clear_interrupt(parent_sess, ctrl->irq);
-}
-
-/** Write the data register callback handler.
- *
- * @param arg Argument (ata_ctrl_t *)
- * @param data Data
- * @param nwords Number of words to write
- */
-static void ata_write_data_16(void *arg, uint16_t *data, size_t nwords)
-{
-	ata_ctrl_t *ctrl = (ata_ctrl_t *)arg;
-	size_t i;
-
-	for (i = 0; i < nwords; i++)
-		pio_write_16(&ctrl->cmd->data_port, data[i]);
-}
-
-/** Read the data register callback handler.
- *
- * @param arg Argument (ata_ctrl_t *)
- * @param buf Destination buffer
- * @param nwords Number of words to read
- */
-static void ata_read_data_16(void *arg, uint16_t *buf, size_t nwords)
-{
-	ata_ctrl_t *ctrl = (ata_ctrl_t *)arg;
-	size_t i;
-
-	for (i = 0; i < nwords; i++)
-		buf[i] = pio_read_16(&ctrl->cmd->data_port);
-}
-
-/** Write command register callback handler.
- *
- * @param arg Argument (ata_ctrl_t *)
- * @param off Register offset
- * @param value Value to write to command register
- */
-static void ata_write_cmd_8(void *arg, uint16_t off, uint8_t value)
-{
-	ata_ctrl_t *ctrl = (ata_ctrl_t *)arg;
-
-	pio_write_8(((ioport8_t *)ctrl->cmd) + off, value);
-}
-
-/** Read command register callback handler.
- *
- * @param arg Argument (ata_ctrl_t *)
- * @param off Register offset
- * @return value Value read from command register
- */
-static uint8_t ata_read_cmd_8(void *arg, uint16_t off)
-{
-	ata_ctrl_t *ctrl = (ata_ctrl_t *)arg;
-
-	return pio_read_8(((ioport8_t *)ctrl->cmd) + off);
-}
-
-/** Write control register callback handler.
- *
- * @param arg Argument (ata_ctrl_t *)
- * @param off Register offset
- * @param value Value to write to control register
- */
-static void ata_write_ctl_8(void *arg, uint16_t off, uint8_t value)
-{
-	ata_ctrl_t *ctrl = (ata_ctrl_t *)arg;
-
-	pio_write_8(((ioport8_t *)ctrl->ctl) + off, value);
-}
-
-/** Read control register callback handler.
- *
- * @param arg Argument (ata_ctrl_t *)
- * @param off Register offset
- * @return value Value read from control register
- */
-static uint8_t ata_read_ctl_8(void *arg, uint16_t off)
-{
-	ata_ctrl_t *ctrl = (ata_ctrl_t *)arg;
-
-	return pio_read_8(((ioport8_t *)ctrl->ctl) + off);
-}
-
-/** Enable IRQ callback handler
- *
- * @param arg Argument (ata_ctrl_t *)
- * @return EOK on success or an error code
- */
-static errno_t ata_irq_enable(void *arg)
-{
-	ata_ctrl_t *ctrl = (ata_ctrl_t *)arg;
-	async_sess_t *parent_sess;
-	errno_t rc;
-
-	ddf_msg(LVL_DEBUG, "Enable IRQ");
-
-	parent_sess = ddf_dev_parent_sess_get(ctrl->dev);
-
-	rc = hw_res_enable_interrupt(parent_sess, ctrl->irq);
-	if (rc != EOK) {
-		ddf_msg(LVL_ERROR, "Error enabling IRQ.");
-		(void) unregister_interrupt_handler(ctrl->dev,
-		    ctrl->ihandle);
-		return rc;
-	}
-
-	return EOK;
-}
-
-/** Disable IRQ callback handler
- *
- * @param arg Argument (ata_ctrl_t *)
- * @return EOK on success or an error code
- */
-static errno_t ata_irq_disable(void *arg)
-{
-	ata_ctrl_t *ctrl = (ata_ctrl_t *)arg;
-	async_sess_t *parent_sess;
-	errno_t rc;
-
-	ddf_msg(LVL_DEBUG, "Disable IRQ");
-
-	parent_sess = ddf_dev_parent_sess_get(ctrl->dev);
-
-	rc = hw_res_disable_interrupt(parent_sess, ctrl->irq);
-	if (rc != EOK) {
-		ddf_msg(LVL_ERROR, "Error enabling IRQ.");
-		(void) unregister_interrupt_handler(ctrl->dev,
-		    ctrl->ihandle);
-		return rc;
-	}
-
-	return EOK;
-}
-
-/** Add ATA device callback handler.
- *
- * @param arg Argument (ata_ctrl_t *)
- * @param idx Device index
- * $param charg Connection handler argument
- * @return EOK on success or an error code
- */
-static errno_t ata_add_device(void *arg, unsigned idx, void *charg)
-{
-	ata_ctrl_t *ctrl = (ata_ctrl_t *)arg;
-	return ata_fun_create(ctrl, idx, charg);
-}
-
-/** Remove ATA device callback handler.
- *
- * @param arg Argument (ata_ctrl_t *)
- * @param idx Device index
- * @return EOK on success or an error code
- */
-static errno_t ata_remove_device(void *arg, unsigned idx)
-{
-	ata_ctrl_t *ctrl = (ata_ctrl_t *)arg;
-	return ata_fun_remove(ctrl, idx);
-}
-
-/** Debug message callback handler.
- *
- * @param arg Argument (ata_ctrl_t *)
- * @param msg Message
- */
-static void ata_msg_debug(void *arg, char *msg)
-{
-	(void)arg;
-	ddf_msg(LVL_DEBUG, "%s", msg);
-}
-
-/** Notice message callback handler.
- *
- * @param arg Argument (ata_ctrl_t *)
- * @param msg Message
- */
-static void ata_msg_note(void *arg, char *msg)
-{
-	(void)arg;
-	ddf_msg(LVL_NOTE, "%s", msg);
-}
-
-/** Warning message callback handler.
- *
- * @param arg Argument (ata_ctrl_t *)
- * @param msg Message
- */
-static void ata_msg_warn(void *arg, char *msg)
-{
-	(void)arg;
-	ddf_msg(LVL_WARN, "%s", msg);
-}
-
-/** Error message callback handler.
- *
- * @param arg Argument (ata_ctrl_t *)
- * @param msg Message
- */
-static void ata_msg_error(void *arg, char *msg)
-{
-	(void)arg;
-	ddf_msg(LVL_ERROR, "%s", msg);
-}
-
-/**
- * @}
- */
Index: pace/drv/block/ata_bd/ata_bd.h
===================================================================
--- uspace/drv/block/ata_bd/ata_bd.h	(revision 2791fbb7c1f204fd66ed105c6cc1ddf9e8f5bedf)
+++ 	(revision )
@@ -1,94 +1,0 @@
-/*
- * Copyright (c) 2024 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 ata_bd
- * @{
- */
-/** @file ATA driver definitions.
- */
-
-#ifndef __ATA_BD_H__
-#define __ATA_BD_H__
-
-#include <ata/ata.h>
-#include <ata/ata_hw.h>
-#include <ddf/driver.h>
-#include <fibril_synch.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-#define NAME "ata_bd"
-
-/** ATA hardware resources */
-typedef struct {
-	uintptr_t cmd;	/**< Command block base address. */
-	uintptr_t ctl;	/**< Control block base address. */
-	int irq;	/**< IRQ */
-} ata_hwres_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;
-	/** IRQ (-1 if not used) */
-	int irq;
-	/** IRQ handle */
-	cap_irq_handle_t ihandle;
-
-	/** Synchronize controller access */
-	fibril_mutex_t lock;
-	/** Value of status register read by interrupt handler */
-	uint8_t irq_status;
-
-	/** Libata ATA channel */
-	ata_channel_t *channel;
-	struct ata_fun *fun[2];
-} ata_ctrl_t;
-
-typedef struct ata_fun {
-	ddf_fun_t *fun;
-	void *charg;
-} ata_fun_t;
-
-extern errno_t ata_ctrl_init(ata_ctrl_t *, ata_hwres_t *);
-extern errno_t ata_ctrl_remove(ata_ctrl_t *);
-extern errno_t ata_ctrl_gone(ata_ctrl_t *);
-
-#endif
-
-/** @}
- */
Index: pace/drv/block/ata_bd/ata_bd.ma
===================================================================
--- uspace/drv/block/ata_bd/ata_bd.ma	(revision 2791fbb7c1f204fd66ed105c6cc1ddf9e8f5bedf)
+++ 	(revision )
@@ -1,1 +1,0 @@
-10 isa/ata_bd
Index: pace/drv/block/ata_bd/doc/doxygroups.h
===================================================================
--- uspace/drv/block/ata_bd/doc/doxygroups.h	(revision 2791fbb7c1f204fd66ed105c6cc1ddf9e8f5bedf)
+++ 	(revision )
@@ -1,4 +1,0 @@
-/** @addtogroup ata_bd ata_bd
- * @brief ATA disk driver
- * @ingroup drvs
- */
Index: pace/drv/block/ata_bd/main.c
===================================================================
--- uspace/drv/block/ata_bd/main.c	(revision 2791fbb7c1f204fd66ed105c6cc1ddf9e8f5bedf)
+++ 	(revision )
@@ -1,345 +1,0 @@
-/*
- * Copyright (c) 2024 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 ata_bd
- * @{
- */
-
-/** @file
- */
-
-#include <assert.h>
-#include <stdio.h>
-#include <errno.h>
-#include <str_error.h>
-#include <ddf/driver.h>
-#include <ddf/log.h>
-#include <device/hw_res_parsed.h>
-
-#include "ata_bd.h"
-#include "main.h"
-
-static errno_t ata_dev_add(ddf_dev_t *dev);
-static errno_t ata_dev_remove(ddf_dev_t *dev);
-static errno_t ata_dev_gone(ddf_dev_t *dev);
-static errno_t ata_fun_online(ddf_fun_t *fun);
-static errno_t ata_fun_offline(ddf_fun_t *fun);
-
-static void ata_bd_connection(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
-};
-
-static errno_t ata_get_res(ddf_dev_t *dev, ata_hwres_t *ata_res)
-{
-	async_sess_t *parent_sess;
-	hw_res_list_parsed_t hw_res;
-	errno_t rc;
-
-	parent_sess = ddf_dev_parent_sess_get(dev);
-	if (parent_sess == NULL)
-		return ENOMEM;
-
-	hw_res_list_parsed_init(&hw_res);
-	rc = hw_res_get_list_parsed(parent_sess, &hw_res, 0);
-	if (rc != EOK)
-		return rc;
-
-	if (hw_res.io_ranges.count != 2) {
-		rc = EINVAL;
-		goto error;
-	}
-
-	/* I/O ranges */
-
-	addr_range_t *cmd_rng = &hw_res.io_ranges.ranges[0];
-	addr_range_t *ctl_rng = &hw_res.io_ranges.ranges[1];
-	ata_res->cmd = RNGABS(*cmd_rng);
-	ata_res->ctl = RNGABS(*ctl_rng);
-
-	if (RNGSZ(*ctl_rng) < sizeof(ata_ctl_t)) {
-		rc = EINVAL;
-		goto error;
-	}
-
-	if (RNGSZ(*cmd_rng) < sizeof(ata_cmd_t)) {
-		rc = EINVAL;
-		goto error;
-	}
-
-	/* IRQ */
-	if (hw_res.irqs.count > 0) {
-		ata_res->irq = hw_res.irqs.irqs[0];
-	} else {
-		ata_res->irq = -1;
-	}
-
-	return EOK;
-error:
-	hw_res_list_parsed_clean(&hw_res);
-	return rc;
-}
-
-/** Add new device
- *
- * @param  dev New device
- * @return     EOK on success or an error code.
- */
-static errno_t ata_dev_add(ddf_dev_t *dev)
-{
-	ata_ctrl_t *ctrl;
-	ata_hwres_t res;
-	errno_t rc;
-
-	rc = ata_get_res(dev, &res);
-	if (rc != EOK) {
-		ddf_msg(LVL_ERROR, "Invalid HW resource configuration.");
-		return EINVAL;
-	}
-
-	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, &res);
-	if (rc == ENOENT)
-		goto error;
-
-	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(unsigned idx)
-{
-	char *fun_name;
-
-	if (asprintf(&fun_name, "d%u", idx) < 0)
-		return NULL;
-
-	return fun_name;
-}
-
-errno_t ata_fun_create(ata_ctrl_t *ctrl, unsigned idx, void *charg)
-{
-	errno_t rc;
-	char *fun_name = NULL;
-	ddf_fun_t *fun = NULL;
-	ata_fun_t *afun = NULL;
-	bool bound = false;
-
-	fun_name = ata_fun_name(idx);
-	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->charg = charg;
-
-	/* 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;
-	}
-
-	bound = true;
-
-	rc = ddf_fun_add_to_category(fun, "disk");
-	if (rc != EOK) {
-		ddf_msg(LVL_ERROR, "Failed adding function %s to "
-		    "category 'disk': %s", fun_name, str_error(rc));
-		goto error;
-	}
-
-	free(fun_name);
-	return EOK;
-error:
-	if (bound)
-		ddf_fun_unbind(fun);
-	if (fun != NULL)
-		ddf_fun_destroy(fun);
-	if (fun_name != NULL)
-		free(fun_name);
-
-	return rc;
-}
-
-errno_t ata_fun_remove(ata_ctrl_t *ctrl, unsigned idx)
-{
-	errno_t rc;
-	char *fun_name;
-	ata_fun_t *afun = ctrl->fun[idx];
-
-	fun_name = ata_fun_name(idx);
-	if (fun_name == NULL) {
-		ddf_msg(LVL_ERROR, "Out of memory.");
-		rc = ENOMEM;
-		goto error;
-	}
-
-	ddf_msg(LVL_DEBUG, "ata_fun_remove(%p, '%s')", afun, fun_name);
-	rc = ddf_fun_offline(afun->fun);
-	if (rc != EOK) {
-		ddf_msg(LVL_ERROR, "Error offlining function '%s'.", fun_name);
-		goto error;
-	}
-
-	rc = ddf_fun_unbind(afun->fun);
-	if (rc != EOK) {
-		ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.", fun_name);
-		goto error;
-	}
-
-	ddf_fun_destroy(afun->fun);
-	free(fun_name);
-	return EOK;
-error:
-	if (fun_name != NULL)
-		free(fun_name);
-	return rc;
-}
-
-errno_t ata_fun_unbind(ata_ctrl_t *ctrl, unsigned idx)
-{
-	errno_t rc;
-	char *fun_name;
-	ata_fun_t *afun = ctrl->fun[idx];
-
-	fun_name = ata_fun_name(idx);
-	if (fun_name == NULL) {
-		ddf_msg(LVL_ERROR, "Out of memory.");
-		rc = ENOMEM;
-		goto error;
-	}
-
-	ddf_msg(LVL_DEBUG, "ata_fun_unbind(%p, '%s')", afun, fun_name);
-	rc = ddf_fun_unbind(afun->fun);
-	if (rc != EOK) {
-		ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.", fun_name);
-		goto error;
-	}
-
-	ddf_fun_destroy(afun->fun);
-	free(fun_name);
-	return EOK;
-error:
-	if (fun_name != NULL)
-		free(fun_name);
-	return rc;
-}
-
-static errno_t 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 errno_t 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 errno_t ata_fun_online(ddf_fun_t *fun)
-{
-	ddf_msg(LVL_DEBUG, "ata_fun_online()");
-	return ddf_fun_online(fun);
-}
-
-static errno_t ata_fun_offline(ddf_fun_t *fun)
-{
-	ddf_msg(LVL_DEBUG, "ata_fun_offline()");
-	return ddf_fun_offline(fun);
-}
-
-static void ata_bd_connection(ipc_call_t *icall, void *arg)
-{
-	ata_fun_t *afun;
-
-	afun = (ata_fun_t *) ddf_fun_data_get((ddf_fun_t *)arg);
-	ata_connection(icall, afun->charg);
-}
-
-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: pace/drv/block/ata_bd/main.h
===================================================================
--- uspace/drv/block/ata_bd/main.h	(revision 2791fbb7c1f204fd66ed105c6cc1ddf9e8f5bedf)
+++ 	(revision )
@@ -1,47 +1,0 @@
-/*
- * Copyright (c) 2024 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 ata_bd
- * @{
- */
-/** @file ATA driver main module
- */
-
-#ifndef __ATA_MAIN_H__
-#define __ATA_MAIN_H__
-
-#include "ata_bd.h"
-
-extern errno_t ata_fun_create(ata_ctrl_t *, unsigned, void *);
-extern errno_t ata_fun_remove(ata_ctrl_t *, unsigned);
-extern errno_t ata_fun_unbind(ata_ctrl_t *, unsigned);
-
-#endif
-
-/** @}
- */
Index: pace/drv/block/ata_bd/meson.build
===================================================================
--- uspace/drv/block/ata_bd/meson.build	(revision 2791fbb7c1f204fd66ed105c6cc1ddf9e8f5bedf)
+++ 	(revision )
@@ -1,30 +1,0 @@
-#
-# 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.
-#
-
-deps = [ 'ata', 'scsi' ]
-src = files('ata_bd.c', 'main.c')
Index: uspace/drv/block/isa-ide/doc/doxygroups.h
===================================================================
--- uspace/drv/block/isa-ide/doc/doxygroups.h	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
+++ uspace/drv/block/isa-ide/doc/doxygroups.h	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
@@ -0,0 +1,4 @@
+/** @addtogroup isa-ide isa-ide
+ * @brief ISA IDE driver
+ * @ingroup drvs
+ */
Index: uspace/drv/block/isa-ide/isa-ide.c
===================================================================
--- uspace/drv/block/isa-ide/isa-ide.c	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
+++ uspace/drv/block/isa-ide/isa-ide.c	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
@@ -0,0 +1,525 @@
+/*
+ * Copyright (c) 2024 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 isa-ide
+ * @{
+ */
+
+/**
+ * @file
+ * @brief ISA IDE driver
+ *
+ * The driver services a single IDE channel.
+ */
+
+#include <ddi.h>
+#include <ddf/interrupt.h>
+#include <ddf/log.h>
+#include <async.h>
+#include <as.h>
+#include <fibril_synch.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <str.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include "isa-ide.h"
+#include "main.h"
+
+static errno_t isa_ide_init_io(isa_ide_ctrl_t *ctrl);
+static void isa_ide_fini_io(isa_ide_ctrl_t *ctrl);
+static errno_t isa_ide_init_irq(isa_ide_ctrl_t *ctrl);
+static void isa_ide_fini_irq(isa_ide_ctrl_t *ctrl);
+static void isa_ide_irq_handler(ipc_call_t *call, ddf_dev_t *dev);
+
+static void isa_ide_write_data_16(void *, uint16_t *, size_t);
+static void isa_ide_read_data_16(void *, uint16_t *, size_t);
+static void isa_ide_write_cmd_8(void *, uint16_t, uint8_t);
+static uint8_t isa_ide_read_cmd_8(void *, uint16_t);
+static void isa_ide_write_ctl_8(void *, uint16_t, uint8_t);
+static uint8_t isa_ide_read_ctl_8(void *, uint16_t);
+static errno_t isa_ide_irq_enable(void *);
+static errno_t isa_ide_irq_disable(void *);
+static errno_t isa_ide_add_device(void *, unsigned, void *);
+static errno_t isa_ide_remove_device(void *, unsigned);
+static void isa_ide_msg_debug(void *, char *);
+static void isa_ide_msg_note(void *, char *);
+static void isa_ide_msg_warn(void *, char *);
+static void isa_ide_msg_error(void *, char *);
+
+static const irq_pio_range_t isa_ide_irq_ranges[] = {
+	{
+		.base = 0,
+		.size = sizeof(ata_cmd_t)
+	}
+};
+
+/** IDE interrupt pseudo code. */
+static const irq_cmd_t isa_ide_irq_cmds[] = {
+	{
+		.cmd = CMD_PIO_READ_8,
+		.addr = NULL,  /* will be patched in run-time */
+		.dstarg = 1
+	},
+	{
+		.cmd = CMD_ACCEPT
+	}
+};
+
+/** Initialize ISA IDE controller. */
+errno_t isa_ide_ctrl_init(isa_ide_ctrl_t *ctrl, isa_ide_hwres_t *res)
+{
+	errno_t rc;
+	bool irq_inited = false;
+	ata_params_t params;
+
+	ddf_msg(LVL_DEBUG, "isa_ide_ctrl_init()");
+
+	fibril_mutex_initialize(&ctrl->lock);
+	ctrl->cmd_physical = res->cmd;
+	ctrl->ctl_physical = res->ctl;
+	ctrl->irq = res->irq;
+
+	ddf_msg(LVL_NOTE, "I/O address %p/%p", (void *) ctrl->cmd_physical,
+	    (void *) ctrl->ctl_physical);
+
+	ddf_msg(LVL_DEBUG, "Init I/O");
+	rc = isa_ide_init_io(ctrl);
+	if (rc != EOK)
+		return rc;
+
+	ddf_msg(LVL_DEBUG, "Init IRQ");
+	rc = isa_ide_init_irq(ctrl);
+	if (rc != EOK) {
+		ddf_msg(LVL_NOTE, "init IRQ failed");
+		return rc;
+	}
+
+	irq_inited = true;
+
+	ddf_msg(LVL_DEBUG, "isa_ide_ctrl_init(): Initialize IDE channel");
+
+	params.arg = (void *)ctrl;
+	params.have_irq = (ctrl->irq >= 0) ? true : false;
+	params.write_data_16 = isa_ide_write_data_16;
+	params.read_data_16 = isa_ide_read_data_16;
+	params.write_cmd_8 = isa_ide_write_cmd_8;
+	params.read_cmd_8 = isa_ide_read_cmd_8;
+	params.write_ctl_8 = isa_ide_write_ctl_8;
+	params.read_ctl_8 = isa_ide_read_ctl_8;
+	params.irq_enable = isa_ide_irq_enable;
+	params.irq_disable = isa_ide_irq_disable;
+	params.add_device = isa_ide_add_device;
+	params.remove_device = isa_ide_remove_device;
+	params.msg_debug = isa_ide_msg_debug;
+	params.msg_note = isa_ide_msg_note;
+	params.msg_warn = isa_ide_msg_warn;
+	params.msg_error = isa_ide_msg_error;
+
+	rc = ata_channel_create(&params, &ctrl->channel);
+	if (rc != EOK)
+		goto error;
+
+	rc = ata_channel_initialize(ctrl->channel);
+	if (rc != EOK)
+		goto error;
+
+	ddf_msg(LVL_DEBUG, "isa_ide_ctrl_init: DONE");
+	return EOK;
+error:
+	if (irq_inited)
+		isa_ide_fini_irq(ctrl);
+	isa_ide_fini_io(ctrl);
+	return rc;
+}
+
+/** Remove ISA IDE controller. */
+errno_t isa_ide_ctrl_remove(isa_ide_ctrl_t *ctrl)
+{
+	errno_t rc;
+
+	ddf_msg(LVL_DEBUG, ": isa_ide_ctrl_remove()");
+
+	fibril_mutex_lock(&ctrl->lock);
+
+	rc = ata_channel_destroy(ctrl->channel);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&ctrl->lock);
+		return rc;
+	}
+
+	isa_ide_fini_irq(ctrl);
+	isa_ide_fini_io(ctrl);
+	fibril_mutex_unlock(&ctrl->lock);
+
+	return EOK;
+}
+
+/** Surprise removal of ISA IDE controller. */
+errno_t isa_ide_ctrl_gone(isa_ide_ctrl_t *ctrl)
+{
+	errno_t rc;
+
+	ddf_msg(LVL_DEBUG, "isa_ide_ctrl_gone()");
+
+	fibril_mutex_lock(&ctrl->lock);
+
+	rc = ata_channel_destroy(ctrl->channel);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&ctrl->lock);
+		return rc;
+	}
+
+	isa_ide_fini_io(ctrl);
+	fibril_mutex_unlock(&ctrl->lock);
+
+	return EOK;
+}
+
+/** Enable device I/O. */
+static errno_t isa_ide_init_io(isa_ide_ctrl_t *ctrl)
+{
+	errno_t rc;
+	void *vaddr;
+
+	rc = pio_enable((void *) ctrl->cmd_physical, sizeof(ata_cmd_t), &vaddr);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
+		return rc;
+	}
+
+	ctrl->cmd = vaddr;
+
+	rc = pio_enable((void *) ctrl->ctl_physical, sizeof(ata_ctl_t), &vaddr);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
+		return rc;
+	}
+
+	ctrl->ctl = vaddr;
+	return EOK;
+}
+
+/** Clean up device I/O. */
+static void isa_ide_fini_io(isa_ide_ctrl_t *ctrl)
+{
+	(void) ctrl;
+	/* XXX TODO */
+}
+
+/** Initialize IRQ. */
+static errno_t isa_ide_init_irq(isa_ide_ctrl_t *ctrl)
+{
+	irq_code_t irq_code;
+	irq_pio_range_t *ranges;
+	irq_cmd_t *cmds;
+	errno_t rc;
+
+	if (ctrl->irq < 0)
+		return EOK;
+
+	ranges = malloc(sizeof(isa_ide_irq_ranges));
+	if (ranges == NULL)
+		return ENOMEM;
+
+	cmds = malloc(sizeof(isa_ide_irq_cmds));
+	if (cmds == NULL) {
+		free(cmds);
+		return ENOMEM;
+	}
+
+	memcpy(ranges, &isa_ide_irq_ranges, sizeof(isa_ide_irq_ranges));
+	ranges[0].base = ctrl->cmd_physical;
+	memcpy(cmds, &isa_ide_irq_cmds, sizeof(isa_ide_irq_cmds));
+	cmds[0].addr = &ctrl->cmd->status;
+
+	irq_code.rangecount = sizeof(isa_ide_irq_ranges) / sizeof(irq_pio_range_t);
+	irq_code.ranges = ranges;
+	irq_code.cmdcount = sizeof(isa_ide_irq_cmds) / sizeof(irq_cmd_t);
+	irq_code.cmds = cmds;
+
+	ddf_msg(LVL_NOTE, "IRQ %d", ctrl->irq);
+	rc = register_interrupt_handler(ctrl->dev, ctrl->irq, isa_ide_irq_handler,
+	    &irq_code, &ctrl->ihandle);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Error registering IRQ.");
+		goto error;
+	}
+
+	ddf_msg(LVL_DEBUG, "Interrupt handler registered");
+	free(ranges);
+	free(cmds);
+	return EOK;
+error:
+	free(ranges);
+	free(cmds);
+	return rc;
+}
+
+/** Clean up IRQ. */
+static void isa_ide_fini_irq(isa_ide_ctrl_t *ctrl)
+{
+	errno_t rc;
+	async_sess_t *parent_sess;
+
+	parent_sess = ddf_dev_parent_sess_get(ctrl->dev);
+
+	rc = hw_res_disable_interrupt(parent_sess, ctrl->irq);
+	if (rc != EOK)
+		ddf_msg(LVL_ERROR, "Error disabling IRQ.");
+
+	(void) unregister_interrupt_handler(ctrl->dev, ctrl->ihandle);
+}
+
+/** Interrupt handler.
+ *
+ * @param call Call data
+ * @param dev Device that caused the interrupt
+ */
+static void isa_ide_irq_handler(ipc_call_t *call, ddf_dev_t *dev)
+{
+	isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)ddf_dev_data_get(dev);
+	uint8_t status;
+	async_sess_t *parent_sess;
+
+	status = ipc_get_arg1(call);
+	ata_channel_irq(ctrl->channel, status);
+
+	parent_sess = ddf_dev_parent_sess_get(dev);
+	hw_res_clear_interrupt(parent_sess, ctrl->irq);
+}
+
+/** Write the data register callback handler.
+ *
+ * @param arg Argument (isa_ide_ctrl_t *)
+ * @param data Data
+ * @param nwords Number of words to write
+ */
+static void isa_ide_write_data_16(void *arg, uint16_t *data, size_t nwords)
+{
+	isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)arg;
+	size_t i;
+
+	for (i = 0; i < nwords; i++)
+		pio_write_16(&ctrl->cmd->data_port, data[i]);
+}
+
+/** Read the data register callback handler.
+ *
+ * @param arg Argument (isa_ide_ctrl_t *)
+ * @param buf Destination buffer
+ * @param nwords Number of words to read
+ */
+static void isa_ide_read_data_16(void *arg, uint16_t *buf, size_t nwords)
+{
+	isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)arg;
+	size_t i;
+
+	for (i = 0; i < nwords; i++)
+		buf[i] = pio_read_16(&ctrl->cmd->data_port);
+}
+
+/** Write command register callback handler.
+ *
+ * @param arg Argument (isa_ide_ctrl_t *)
+ * @param off Register offset
+ * @param value Value to write to command register
+ */
+static void isa_ide_write_cmd_8(void *arg, uint16_t off, uint8_t value)
+{
+	isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)arg;
+
+	pio_write_8(((ioport8_t *)ctrl->cmd) + off, value);
+}
+
+/** Read command register callback handler.
+ *
+ * @param arg Argument (isa_ide_ctrl_t *)
+ * @param off Register offset
+ * @return value Value read from command register
+ */
+static uint8_t isa_ide_read_cmd_8(void *arg, uint16_t off)
+{
+	isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)arg;
+
+	return pio_read_8(((ioport8_t *)ctrl->cmd) + off);
+}
+
+/** Write control register callback handler.
+ *
+ * @param arg Argument (isa_ide_ctrl_t *)
+ * @param off Register offset
+ * @param value Value to write to control register
+ */
+static void isa_ide_write_ctl_8(void *arg, uint16_t off, uint8_t value)
+{
+	isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)arg;
+
+	pio_write_8(((ioport8_t *)ctrl->ctl) + off, value);
+}
+
+/** Read control register callback handler.
+ *
+ * @param arg Argument (isa_ide_ctrl_t *)
+ * @param off Register offset
+ * @return value Value read from control register
+ */
+static uint8_t isa_ide_read_ctl_8(void *arg, uint16_t off)
+{
+	isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)arg;
+
+	return pio_read_8(((ioport8_t *)ctrl->ctl) + off);
+}
+
+/** Enable IRQ callback handler
+ *
+ * @param arg Argument (isa_ide_ctrl_t *)
+ * @return EOK on success or an error code
+ */
+static errno_t isa_ide_irq_enable(void *arg)
+{
+	isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)arg;
+	async_sess_t *parent_sess;
+	errno_t rc;
+
+	ddf_msg(LVL_DEBUG, "Enable IRQ");
+
+	parent_sess = ddf_dev_parent_sess_get(ctrl->dev);
+
+	rc = hw_res_enable_interrupt(parent_sess, ctrl->irq);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Error enabling IRQ.");
+		(void) unregister_interrupt_handler(ctrl->dev,
+		    ctrl->ihandle);
+		return rc;
+	}
+
+	return EOK;
+}
+
+/** Disable IRQ callback handler
+ *
+ * @param arg Argument (isa_ide_ctrl_t *)
+ * @return EOK on success or an error code
+ */
+static errno_t isa_ide_irq_disable(void *arg)
+{
+	isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)arg;
+	async_sess_t *parent_sess;
+	errno_t rc;
+
+	ddf_msg(LVL_DEBUG, "Disable IRQ");
+
+	parent_sess = ddf_dev_parent_sess_get(ctrl->dev);
+
+	rc = hw_res_disable_interrupt(parent_sess, ctrl->irq);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Error enabling IRQ.");
+		(void) unregister_interrupt_handler(ctrl->dev,
+		    ctrl->ihandle);
+		return rc;
+	}
+
+	return EOK;
+}
+
+/** Add ATA device callback handler.
+ *
+ * @param arg Argument (isa_ide_ctrl_t *)
+ * @param idx Device index
+ * $param charg Connection handler argument
+ * @return EOK on success or an error code
+ */
+static errno_t isa_ide_add_device(void *arg, unsigned idx, void *charg)
+{
+	isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)arg;
+	return isa_ide_fun_create(ctrl, idx, charg);
+}
+
+/** Remove ATA device callback handler.
+ *
+ * @param arg Argument (isa_ide_ctrl_t *)
+ * @param idx Device index
+ * @return EOK on success or an error code
+ */
+static errno_t isa_ide_remove_device(void *arg, unsigned idx)
+{
+	isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)arg;
+	return isa_ide_fun_remove(ctrl, idx);
+}
+
+/** Debug message callback handler.
+ *
+ * @param arg Argument (isa_ide_ctrl_t *)
+ * @param msg Message
+ */
+static void isa_ide_msg_debug(void *arg, char *msg)
+{
+	(void)arg;
+	ddf_msg(LVL_DEBUG, "%s", msg);
+}
+
+/** Notice message callback handler.
+ *
+ * @param arg Argument (isa_ide_ctrl_t *)
+ * @param msg Message
+ */
+static void isa_ide_msg_note(void *arg, char *msg)
+{
+	(void)arg;
+	ddf_msg(LVL_NOTE, "%s", msg);
+}
+
+/** Warning message callback handler.
+ *
+ * @param arg Argument (isa_ide_ctrl_t *)
+ * @param msg Message
+ */
+static void isa_ide_msg_warn(void *arg, char *msg)
+{
+	(void)arg;
+	ddf_msg(LVL_WARN, "%s", msg);
+}
+
+/** Error message callback handler.
+ *
+ * @param arg Argument (isa_ide_ctrl_t *)
+ * @param msg Message
+ */
+static void isa_ide_msg_error(void *arg, char *msg)
+{
+	(void)arg;
+	ddf_msg(LVL_ERROR, "%s", msg);
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/block/isa-ide/isa-ide.h
===================================================================
--- uspace/drv/block/isa-ide/isa-ide.h	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
+++ uspace/drv/block/isa-ide/isa-ide.h	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2024 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 isa-ide
+ * @{
+ */
+/** @file ISA IDE driver definitions.
+ */
+
+#ifndef ISA_IDE_H
+#define ISA_IDE_H
+
+#include <ata/ata.h>
+#include <ata/ata_hw.h>
+#include <ddf/driver.h>
+#include <fibril_synch.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#define NAME "isa-ide"
+
+/** ISA IDE hardware resources */
+typedef struct {
+	uintptr_t cmd;	/**< Command block base address. */
+	uintptr_t ctl;	/**< Control block base address. */
+	int irq;	/**< IRQ */
+} isa_ide_hwres_t;
+
+/** ISA IDE controller */
+typedef struct isa_ide_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;
+	/** IRQ (-1 if not used) */
+	int irq;
+	/** IRQ handle */
+	cap_irq_handle_t ihandle;
+
+	/** Synchronize controller access */
+	fibril_mutex_t lock;
+	/** Value of status register read by interrupt handler */
+	uint8_t irq_status;
+
+	/** Libata ATA channel */
+	ata_channel_t *channel;
+	struct isa_ide_fun *fun[2];
+} isa_ide_ctrl_t;
+
+typedef struct isa_ide_fun {
+	ddf_fun_t *fun;
+	void *charg;
+} isa_ide_fun_t;
+
+extern errno_t isa_ide_ctrl_init(isa_ide_ctrl_t *, isa_ide_hwres_t *);
+extern errno_t isa_ide_ctrl_remove(isa_ide_ctrl_t *);
+extern errno_t isa_ide_ctrl_gone(isa_ide_ctrl_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/drv/block/isa-ide/isa-ide.ma
===================================================================
--- uspace/drv/block/isa-ide/isa-ide.ma	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
+++ uspace/drv/block/isa-ide/isa-ide.ma	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
@@ -0,0 +1,1 @@
+10 isa/ide
Index: uspace/drv/block/isa-ide/main.c
===================================================================
--- uspace/drv/block/isa-ide/main.c	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
+++ uspace/drv/block/isa-ide/main.c	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2024 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 isa-ide
+ * @{
+ */
+
+/** @file
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+#include <ddf/driver.h>
+#include <ddf/log.h>
+#include <device/hw_res_parsed.h>
+
+#include "isa-ide.h"
+#include "main.h"
+
+static errno_t isa_ide_dev_add(ddf_dev_t *dev);
+static errno_t isa_ide_dev_remove(ddf_dev_t *dev);
+static errno_t isa_ide_dev_gone(ddf_dev_t *dev);
+static errno_t isa_ide_fun_online(ddf_fun_t *fun);
+static errno_t isa_ide_fun_offline(ddf_fun_t *fun);
+
+static void isa_ide_connection(ipc_call_t *, void *);
+
+static driver_ops_t driver_ops = {
+	.dev_add = &isa_ide_dev_add,
+	.dev_remove = &isa_ide_dev_remove,
+	.dev_gone = &isa_ide_dev_gone,
+	.fun_online = &isa_ide_fun_online,
+	.fun_offline = &isa_ide_fun_offline
+};
+
+static driver_t isa_ide_driver = {
+	.name = NAME,
+	.driver_ops = &driver_ops
+};
+
+static errno_t ata_get_res(ddf_dev_t *dev, isa_ide_hwres_t *ata_res)
+{
+	async_sess_t *parent_sess;
+	hw_res_list_parsed_t hw_res;
+	errno_t rc;
+
+	parent_sess = ddf_dev_parent_sess_get(dev);
+	if (parent_sess == NULL)
+		return ENOMEM;
+
+	hw_res_list_parsed_init(&hw_res);
+	rc = hw_res_get_list_parsed(parent_sess, &hw_res, 0);
+	if (rc != EOK)
+		return rc;
+
+	if (hw_res.io_ranges.count != 2) {
+		rc = EINVAL;
+		goto error;
+	}
+
+	/* I/O ranges */
+
+	addr_range_t *cmd_rng = &hw_res.io_ranges.ranges[0];
+	addr_range_t *ctl_rng = &hw_res.io_ranges.ranges[1];
+	ata_res->cmd = RNGABS(*cmd_rng);
+	ata_res->ctl = RNGABS(*ctl_rng);
+
+	if (RNGSZ(*ctl_rng) < sizeof(ata_ctl_t)) {
+		rc = EINVAL;
+		goto error;
+	}
+
+	if (RNGSZ(*cmd_rng) < sizeof(ata_cmd_t)) {
+		rc = EINVAL;
+		goto error;
+	}
+
+	/* IRQ */
+	if (hw_res.irqs.count > 0) {
+		ata_res->irq = hw_res.irqs.irqs[0];
+	} else {
+		ata_res->irq = -1;
+	}
+
+	return EOK;
+error:
+	hw_res_list_parsed_clean(&hw_res);
+	return rc;
+}
+
+/** Add new device
+ *
+ * @param  dev New device
+ * @return     EOK on success or an error code.
+ */
+static errno_t isa_ide_dev_add(ddf_dev_t *dev)
+{
+	isa_ide_ctrl_t *ctrl;
+	isa_ide_hwres_t res;
+	errno_t rc;
+
+	rc = ata_get_res(dev, &res);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Invalid HW resource configuration.");
+		return EINVAL;
+	}
+
+	ctrl = ddf_dev_data_alloc(dev, sizeof(isa_ide_ctrl_t));
+	if (ctrl == NULL) {
+		ddf_msg(LVL_ERROR, "Failed allocating soft state.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	ctrl->dev = dev;
+
+	rc = isa_ide_ctrl_init(ctrl, &res);
+	if (rc == ENOENT)
+		goto error;
+
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed initializing ATA controller.");
+		rc = EIO;
+		goto error;
+	}
+
+	return EOK;
+error:
+	return rc;
+}
+
+static char *isa_ide_fun_name(unsigned idx)
+{
+	char *fun_name;
+
+	if (asprintf(&fun_name, "d%u", idx) < 0)
+		return NULL;
+
+	return fun_name;
+}
+
+errno_t isa_ide_fun_create(isa_ide_ctrl_t *ctrl, unsigned idx, void *charg)
+{
+	errno_t rc;
+	char *fun_name = NULL;
+	ddf_fun_t *fun = NULL;
+	isa_ide_fun_t *ifun = NULL;
+	bool bound = false;
+
+	fun_name = isa_ide_fun_name(idx);
+	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 */
+	ifun = ddf_fun_data_alloc(fun, sizeof(isa_ide_fun_t));
+	if (ifun == NULL) {
+		ddf_msg(LVL_ERROR, "Failed allocating softstate.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	ifun->fun = fun;
+	ifun->charg = charg;
+
+	/* Set up a connection handler. */
+	ddf_fun_set_conn_handler(fun, isa_ide_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;
+	}
+
+	bound = true;
+
+	rc = ddf_fun_add_to_category(fun, "disk");
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed adding function %s to "
+		    "category 'disk': %s", fun_name, str_error(rc));
+		goto error;
+	}
+
+	free(fun_name);
+	return EOK;
+error:
+	if (bound)
+		ddf_fun_unbind(fun);
+	if (fun != NULL)
+		ddf_fun_destroy(fun);
+	if (fun_name != NULL)
+		free(fun_name);
+
+	return rc;
+}
+
+errno_t isa_ide_fun_remove(isa_ide_ctrl_t *ctrl, unsigned idx)
+{
+	errno_t rc;
+	char *fun_name;
+	isa_ide_fun_t *ifun = ctrl->fun[idx];
+
+	fun_name = isa_ide_fun_name(idx);
+	if (fun_name == NULL) {
+		ddf_msg(LVL_ERROR, "Out of memory.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	ddf_msg(LVL_DEBUG, "isa_ide_fun_remove(%p, '%s')", ifun, fun_name);
+	rc = ddf_fun_offline(ifun->fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Error offlining function '%s'.", fun_name);
+		goto error;
+	}
+
+	rc = ddf_fun_unbind(ifun->fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.", fun_name);
+		goto error;
+	}
+
+	ddf_fun_destroy(ifun->fun);
+	free(fun_name);
+	return EOK;
+error:
+	if (fun_name != NULL)
+		free(fun_name);
+	return rc;
+}
+
+errno_t isa_ide_fun_unbind(isa_ide_ctrl_t *ctrl, unsigned idx)
+{
+	errno_t rc;
+	char *fun_name;
+	isa_ide_fun_t *ifun = ctrl->fun[idx];
+
+	fun_name = isa_ide_fun_name(idx);
+	if (fun_name == NULL) {
+		ddf_msg(LVL_ERROR, "Out of memory.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	ddf_msg(LVL_DEBUG, "isa_ide_fun_unbind(%p, '%s')", ifun, fun_name);
+	rc = ddf_fun_unbind(ifun->fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.", fun_name);
+		goto error;
+	}
+
+	ddf_fun_destroy(ifun->fun);
+	free(fun_name);
+	return EOK;
+error:
+	if (fun_name != NULL)
+		free(fun_name);
+	return rc;
+}
+
+static errno_t isa_ide_dev_remove(ddf_dev_t *dev)
+{
+	isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)ddf_dev_data_get(dev);
+
+	ddf_msg(LVL_DEBUG, "isa_ide_dev_remove(%p)", dev);
+
+	return isa_ide_ctrl_remove(ctrl);
+}
+
+static errno_t isa_ide_dev_gone(ddf_dev_t *dev)
+{
+	isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)ddf_dev_data_get(dev);
+
+	ddf_msg(LVL_DEBUG, "isa_ide_dev_gone(%p)", dev);
+
+	return isa_ide_ctrl_gone(ctrl);
+}
+
+static errno_t isa_ide_fun_online(ddf_fun_t *fun)
+{
+	ddf_msg(LVL_DEBUG, "isa_ide_fun_online()");
+	return ddf_fun_online(fun);
+}
+
+static errno_t isa_ide_fun_offline(ddf_fun_t *fun)
+{
+	ddf_msg(LVL_DEBUG, "isa_ide_fun_offline()");
+	return ddf_fun_offline(fun);
+}
+
+static void isa_ide_connection(ipc_call_t *icall, void *arg)
+{
+	isa_ide_fun_t *ifun;
+
+	ifun = (isa_ide_fun_t *) ddf_fun_data_get((ddf_fun_t *)arg);
+	ata_connection(icall, ifun->charg);
+}
+
+int main(int argc, char *argv[])
+{
+	printf(NAME ": HelenOS ISA IDE device driver\n");
+	ddf_log_init(NAME);
+	return ddf_driver_main(&isa_ide_driver);
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/block/isa-ide/main.h
===================================================================
--- uspace/drv/block/isa-ide/main.h	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
+++ uspace/drv/block/isa-ide/main.h	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2024 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 isa-ide
+ * @{
+ */
+/** @file ISA IDE driver main module
+ */
+
+#ifndef MAIN_H
+#define MAIN_H
+
+#include "isa-ide.h"
+
+extern errno_t isa_ide_fun_create(isa_ide_ctrl_t *, unsigned, void *);
+extern errno_t isa_ide_fun_remove(isa_ide_ctrl_t *, unsigned);
+extern errno_t isa_ide_fun_unbind(isa_ide_ctrl_t *, unsigned);
+
+#endif
+
+/** @}
+ */
Index: uspace/drv/block/isa-ide/meson.build
===================================================================
--- uspace/drv/block/isa-ide/meson.build	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
+++ uspace/drv/block/isa-ide/meson.build	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
@@ -0,0 +1,30 @@
+#
+# Copyright (c) 2024 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.
+#
+
+deps = [ 'ata', 'scsi' ]
+src = files('isa-ide.c', 'main.c')
Index: uspace/drv/bus/isa/isa.dev
===================================================================
--- uspace/drv/bus/isa/isa.dev	(revision 2791fbb7c1f204fd66ed105c6cc1ddf9e8f5bedf)
+++ uspace/drv/bus/isa/isa.dev	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
@@ -52,5 +52,5 @@
 
 ata-c1:
-	match 100 isa/ata_bd
+	match 100 isa/ide
 	io_range 0x1f0 8
 	io_range 0x3f0 8
@@ -58,5 +58,5 @@
 
 ata-c2:
-	match 100 isa/ata_bd
+	match 100 isa/ide
 	io_range 0x170 8
 	io_range 0x370 8
Index: uspace/drv/meson.build
===================================================================
--- uspace/drv/meson.build	(revision 2791fbb7c1f204fd66ed105c6cc1ddf9e8f5bedf)
+++ uspace/drv/meson.build	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
@@ -33,6 +33,6 @@
 	'root/virt',
 	'block/ahci',
-	'block/ata_bd',
 	'block/ddisk',
+	'block/isa-ide',
 	'block/usbmast',
 	'block/virtio-blk',
Index: uspace/lib/ata/include/ata/ata.h
===================================================================
--- uspace/lib/ata/include/ata/ata.h	(revision 2791fbb7c1f204fd66ed105c6cc1ddf9e8f5bedf)
+++ uspace/lib/ata/include/ata/ata.h	(revision 59c0f478c5ca79154f904083a83642e63b93af6e)
@@ -44,6 +44,4 @@
 #include <stddef.h>
 #include "ata_hw.h"
-
-#define NAME "ata_bd"
 
 struct ata_device;
