Index: uspace/drv/audio/hdaudio/Makefile
===================================================================
--- uspace/drv/audio/hdaudio/Makefile	(revision 8d0707105ba3fdef10f2df188d7e4cf931cb0f78)
+++ uspace/drv/audio/hdaudio/Makefile	(revision d2d5329e4b1afac22d5b4e82e280e6acb29db280)
@@ -33,4 +33,5 @@
 
 SOURCES = \
+	codec.c \
 	regif.c \
 	hdactl.c \
Index: uspace/drv/audio/hdaudio/codec.c
===================================================================
--- uspace/drv/audio/hdaudio/codec.c	(revision d2d5329e4b1afac22d5b4e82e280e6acb29db280)
+++ uspace/drv/audio/hdaudio/codec.c	(revision d2d5329e4b1afac22d5b4e82e280e6acb29db280)
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2014 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 hdaudio
+ * @{
+ */
+/** @file High Definition Audio codec
+ */
+
+#include <async.h>
+#include <bitops.h>
+#include <ddf/log.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "codec.h"
+#include "hdactl.h"
+#include "spec/codec.h"
+
+static int hda_get_parameter(hda_codec_t *codec, int node, hda_param_id_t param,
+    uint32_t *resp)
+{
+	uint32_t verb;
+
+	verb = (codec->address << 28) | (node << 20) | ((hda_get_param) << 8) | param;
+	return hda_cmd(codec->hda, verb, resp);
+}
+
+static int hda_get_subnc(hda_codec_t *codec, int node, int *startnode,
+    int *nodecount)
+{
+	int rc;
+	uint32_t resp;
+
+	rc = hda_get_parameter(codec, node, hda_sub_nc, &resp);
+	if (rc != EOK)
+		return rc;
+
+	*startnode = BIT_RANGE_EXTRACT(uint32_t, subnc_startnode_h,
+	    subnc_startnode_l, resp);
+	*nodecount = BIT_RANGE_EXTRACT(uint32_t, subnc_nodecount_h,
+	    subnc_nodecount_l, resp);
+
+	return EOK;
+}
+
+/** Get Function Group Type */
+static int hda_get_fgrp_type(hda_codec_t *codec, int node, bool *unsol,
+    hda_fgrp_type_t *type)
+{
+	int rc;
+	uint32_t resp;
+
+	rc = hda_get_parameter(codec, node, hda_fgrp_type, &resp);
+	if (rc != EOK)
+		return rc;
+
+	*unsol = (resp & BIT_V(uint32_t, fgrpt_unsol)) != 0;
+	*type = BIT_RANGE_EXTRACT(uint32_t, fgrpt_type_h, fgrpt_type_l, resp);
+
+	return EOK;
+}
+
+/** Get Audio Widget Capabilities */
+static int hda_get_aw_caps(hda_codec_t *codec, int node,
+    hda_awidget_type_t *type, uint32_t *caps)
+{
+	int rc;
+	uint32_t resp;
+
+	rc = hda_get_parameter(codec, node, hda_aw_caps, &resp);
+	if (rc != EOK)
+		return rc;
+
+	*type = BIT_RANGE_EXTRACT(uint32_t, awc_type_h, awc_type_l, resp);
+	*caps = resp;
+
+	return EOK;
+}
+
+hda_codec_t *hda_codec_init(hda_t *hda, uint8_t address)
+{
+	hda_codec_t *codec;
+	int rc;
+	int sfg, nfg;
+	int saw, naw;
+	int fg, aw;
+	bool unsol;
+	hda_fgrp_type_t grptype;
+	hda_awidget_type_t awtype;
+	uint32_t awcaps;
+
+	codec = calloc(1, sizeof(hda_codec_t));
+	if (codec == NULL)
+		return NULL;
+
+	codec->hda = hda;
+	codec->address = address;
+
+	rc = hda_get_subnc(codec, 0, &sfg, &nfg);
+	if (rc != EOK)
+		goto error;
+
+	ddf_msg(LVL_NOTE, "hda_get_subnc -> %d", rc);
+	ddf_msg(LVL_NOTE, "sfg=%d nfg=%d", sfg, nfg);
+
+	for (fg = sfg; fg < sfg + nfg; fg++) {
+		ddf_msg(LVL_NOTE, "Enumerate FG %d", fg);
+
+		rc = hda_get_fgrp_type(codec, fg, &unsol, &grptype);
+		if (rc != EOK)
+			goto error;
+
+		ddf_msg(LVL_NOTE, "hda_get_fgrp_type -> %d", rc);
+		ddf_msg(LVL_NOTE, "unsol: %d, grptype: %d", unsol, grptype);
+
+		rc = hda_get_subnc(codec, fg, &saw, &naw);
+		if (rc != EOK)
+			goto error;
+
+		ddf_msg(LVL_NOTE, "hda_get_subnc -> %d", rc);
+		ddf_msg(LVL_NOTE, "saw=%d baw=%d", saw, naw);
+
+		for (aw = saw; aw < saw + naw; aw++) {
+			rc = hda_get_aw_caps(codec, aw, &awtype, &awcaps);
+			if (rc != EOK)
+				goto error;
+			ddf_msg(LVL_NOTE, "aw %d: type=0x%x caps=0x%x",
+			    aw, awtype, awcaps);
+		}
+	}
+
+	return codec;
+error:
+	free(codec);
+	return NULL;
+}
+
+void hda_codec_fini(hda_codec_t *codec)
+{
+	ddf_msg(LVL_NOTE, "hda_codec_fini()");
+	free(codec);
+}
+
+/** @}
+ */
Index: uspace/drv/audio/hdaudio/codec.h
===================================================================
--- uspace/drv/audio/hdaudio/codec.h	(revision d2d5329e4b1afac22d5b4e82e280e6acb29db280)
+++ uspace/drv/audio/hdaudio/codec.h	(revision d2d5329e4b1afac22d5b4e82e280e6acb29db280)
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014 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 hdaudio
+ * @{
+ */
+/** @file High Definition Audio controller
+ */
+
+#ifndef CODEC_H
+#define CODEC_H
+
+#include "hdaudio.h"
+
+typedef struct hda_codec {
+	hda_t *hda;
+	uint8_t address;
+} hda_codec_t;
+
+extern hda_codec_t *hda_codec_init(hda_t *, uint8_t);
+extern void hda_codec_fini(hda_codec_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/drv/audio/hdaudio/hdactl.c
===================================================================
--- uspace/drv/audio/hdaudio/hdactl.c	(revision 8d0707105ba3fdef10f2df188d7e4cf931cb0f78)
+++ uspace/drv/audio/hdaudio/hdactl.c	(revision d2d5329e4b1afac22d5b4e82e280e6acb29db280)
@@ -42,4 +42,5 @@
 #include <stdint.h>
 
+#include "codec.h"
 #include "hdactl.h"
 #include "regif.h"
@@ -49,5 +50,6 @@
 	ctrl_init_wait_max = 10,
 	codec_enum_wait_us = 512,
-	corb_wait_max = 10
+	corb_wait_max = 10,
+	rirb_wait_max = 100
 };
 
@@ -231,5 +233,5 @@
 
 	/* Set RINTCNT - Qemu won't read from CORB if this is zero */
-	hda_reg16_write(&hda->regs->rintcnt, 2);
+	hda_reg16_write(&hda->regs->rintcnt, 128);
 
 	hda->ctl->rirb_rp = 0;
@@ -336,24 +338,44 @@
 }
 
-static void hda_rirb_read(hda_t *hda)
+static int hda_rirb_read(hda_t *hda, hda_rirb_entry_t *data, size_t count)
 {
 	size_t wp;
 	hda_rirb_entry_t resp;
 	hda_rirb_entry_t *rirb;
+	int wcnt;
 
 	rirb = (hda_rirb_entry_t *)hda->ctl->rirb_virt;
 
-	wp = hda_get_rirbwp(hda);
-	ddf_msg(LVL_NOTE, "hda_rirb_read: wp=%d", wp);
-	while (hda->ctl->rirb_rp != wp) {
-		++hda->ctl->rirb_rp;
-		resp = rirb[hda->ctl->rirb_rp];
-
-		ddf_msg(LVL_NOTE, "RESPONSE resp=0x%x respex=0x%x",
-		    resp.resp, resp.respex);
-	}
-}
-
-#include "spec/codec.h"
+	while (count > 0) {
+		wp = hda_get_rirbwp(hda);
+		ddf_msg(LVL_NOTE, "hda_rirb_read: wp=%d", wp);
+		while (count > 0 && hda->ctl->rirb_rp != wp) {
+			++hda->ctl->rirb_rp;
+			resp = rirb[hda->ctl->rirb_rp];
+
+			ddf_msg(LVL_NOTE, "RESPONSE resp=0x%x respex=0x%x",
+			    resp.resp, resp.respex);
+			if ((resp.respex & BIT_V(uint32_t, respex_unsol)) == 0) {
+				/* Solicited response */
+				*data++ = resp;
+				--count;
+			}
+		}
+
+		if (count > 0) {
+			wcnt = rirb_wait_max;
+			while (wcnt > 0 && hda_get_rirbwp(hda) == hda->ctl->rirb_rp) {
+				async_usleep(100);
+				--wcnt;
+			}
+
+			if (hda_get_rirbwp(hda) == hda->ctl->rirb_rp)
+				return ETIMEOUT;
+		}
+	}
+
+	return EOK;
+}
+
 hda_ctl_t *hda_ctl_init(hda_t *hda)
 {
@@ -426,17 +448,7 @@
 		goto error;
 
-	uint32_t verb;
-	verb = (0 << 28) | (0 << 20) | ((hda_get_param) << 8) | (hda_sub_nc);
-	rc = hda_corb_write(hda, &verb, 1);
-	ddf_msg(LVL_NOTE, "hda_corb_write -> %d", rc);
-	rc = hda_corb_write(hda, &verb, 1);
-	ddf_msg(LVL_NOTE, "hda_corb_write -> %d", rc);
-	rc = hda_corb_write(hda, &verb, 1);
-	ddf_msg(LVL_NOTE, "hda_corb_write -> %d", rc);
-	rc = hda_corb_write(hda, &verb, 1);
-	ddf_msg(LVL_NOTE, "hda_corb_write -> %d", rc);
-
-	async_usleep(100*1000);
-	hda_rirb_read(hda);
+	hda->ctl->codec = hda_codec_init(hda, 0);
+	if (hda->ctl->codec == NULL)
+		goto error;
 
 	return ctl;
@@ -447,4 +459,25 @@
 }
 
+int hda_cmd(hda_t *hda, uint32_t verb, uint32_t *resp)
+{
+	int rc;
+	hda_rirb_entry_t rentry;
+
+	rc = hda_corb_write(hda, &verb, 1);
+	if (rc != EOK)
+		return rc;
+
+	if (resp != NULL) {
+		rc = hda_rirb_read(hda, &rentry, 1);
+		if (rc != EOK)
+			return rc;
+
+		/* XXX Verify that response came from the correct codec */
+		*resp = rentry.resp;
+	}
+
+	return EOK;
+}
+
 void hda_ctl_fini(hda_ctl_t *ctl)
 {
Index: uspace/drv/audio/hdaudio/hdactl.h
===================================================================
--- uspace/drv/audio/hdaudio/hdactl.h	(revision 8d0707105ba3fdef10f2df188d7e4cf931cb0f78)
+++ uspace/drv/audio/hdaudio/hdactl.h	(revision d2d5329e4b1afac22d5b4e82e280e6acb29db280)
@@ -47,8 +47,11 @@
 	size_t rirb_entries;
 	size_t rirb_rp;
+
+	struct hda_codec *codec;
 } hda_ctl_t;
 
 extern hda_ctl_t *hda_ctl_init(hda_t *);
 extern void hda_ctl_fini(hda_ctl_t *);
+extern int hda_cmd(hda_t *, uint32_t, uint32_t *);
 
 #endif
Index: uspace/drv/audio/hdaudio/spec/codec.h
===================================================================
--- uspace/drv/audio/hdaudio/spec/codec.h	(revision 8d0707105ba3fdef10f2df188d7e4cf931cb0f78)
+++ uspace/drv/audio/hdaudio/spec/codec.h	(revision d2d5329e4b1afac22d5b4e82e280e6acb29db280)
@@ -105,5 +105,5 @@
 	hda_sub_nc = 0x04,
 	/** Function Group Type */
-	hba_fgrp_type = 0x05,
+	hda_fgrp_type = 0x05,
 	/** Audio Function Group Capabilities */
 	hda_afg_caps = 0x08,
@@ -132,4 +132,94 @@
 } hda_param_id_t;
 
+/** Subordinate Node Count Response bits */
+typedef enum {
+	/** Starting Node Number (H) */
+	subnc_startnode_h = 23,
+	/** Starting Node Number (L) */
+	subnc_startnode_l = 16,
+	/** Total Node Count (H) */
+	subnc_nodecount_h = 7,
+	/** Total Node Count (L) */
+	subnc_nodecount_l = 0
+} hda_sub_nc_bits_t;
+
+/** Function Group Type Response bits */
+typedef enum {
+	/** UnSol Capable */
+	fgrpt_unsol = 8,
+	/** Group Type (H) */
+	fgrpt_type_h = 7,
+	/** Group Type (L) */
+	fgrpt_type_l = 0
+} hda_fgrp_type_bits_t;
+
+/** Function Group Type */
+typedef enum {
+	/** Audio Function Group */
+	fgrp_afg = 0x01,
+	/** Vendor Defined Modem Function Group */
+	fgrp_vdmfg = 0x02
+} hda_fgrp_type_t;
+
+/** Audio Widget Capabilities Bits */
+typedef enum {
+	/** Type (H) */
+	awc_type_h = 23,
+	/** Type (L) */
+	awc_type_l = 20,
+	/** Chan Count Ext (H) */
+	awc_chan_count_ext_h = 15,
+	/** Chan Count Ext (L) */
+	awc_chan_count_ext_l = 13,
+	/** CP Caps */
+	awc_cp_caps = 12,
+	/** L-R Swap */
+	awc_lr_swap = 11,
+	/** Power Control */
+	awc_power_cntrl = 10,
+	/** Digital */
+	awc_digital = 9,
+	/** Conn List */
+	awc_conn_list = 8,
+	/** Unsol Capable */
+	awc_unsol_capable = 7,
+	/** Proc Widget */
+	awc_proc_widget = 6,
+	/** Stripe */
+	awc_stripe = 5,
+	/** Format Override */
+	awc_fmt_override = 4,
+	/** Amp Param Override */
+	awc_amp_param_override = 3,
+	/** Out Amp Present */
+	awc_out_amp_present = 2,
+	/** In Amp Present */
+	awc_in_amp_present = 1,
+	/** Chan Count LSB (Stereo) */
+	awc_chan_count_lsb = 0
+} hda_awidget_caps_bits_t;
+
+/** Audio Widget Type */
+typedef enum {
+	/** Audio Output */
+	awt_audio_output = 0x0,
+	/** Audio Input */
+	awt_audio_input = 0x1,
+	/** Audio Mixer */
+	awt_audio_mixer = 0x2,
+	/** Audio Selector */
+	awt_audio_selector = 0x3,
+	/** Pin Complex */
+	awt_pin_complex = 0x4,
+	/** Power Widget */
+	awt_power_widget = 0x5,
+	/** Volume Knob Widget */
+	awt_volume_knob = 0x6,
+	/** Beep Generator Widget */
+	awt_beep_generator = 0x7,
+	/** Vendor-defined audio widget */
+	awt_vendor_defined = 0xf
+} hda_awidget_type_t;
+
 #endif
 
