summaryrefslogtreecommitdiff
path: root/sys/dev/pci
diff options
context:
space:
mode:
authorJacob Meuser <jakemsr@cvs.openbsd.org>2011-02-17 16:02:23 +0000
committerJacob Meuser <jakemsr@cvs.openbsd.org>2011-02-17 16:02:23 +0000
commitf7c6876666c56d2043b658a209cd0404b4d69cd1 (patch)
treeb6d3af4f57e0e76c3d45a2901a2f259b0f1c70d0 /sys/dev/pci
parent584e0e0ea2a6dd0ac0ea9b67c5c5f5f90e0c8d4d (diff)
the number of widget connections isn't necessarily the same as the
number of widget connection list entries. if the high bit is set in a connection list entry, all widgets between the previous and current entry are connected. go through the connection list entry once to figure out how many connections there are, then go through it again to build the connection list. ok miod@
Diffstat (limited to 'sys/dev/pci')
-rw-r--r--sys/dev/pci/azalia.c45
1 files changed, 36 insertions, 9 deletions
diff --git a/sys/dev/pci/azalia.c b/sys/dev/pci/azalia.c
index b8558328bdb..b47c7be1863 100644
--- a/sys/dev/pci/azalia.c
+++ b/sys/dev/pci/azalia.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: azalia.c,v 1.188 2010/09/12 03:17:34 jakemsr Exp $ */
+/* $OpenBSD: azalia.c,v 1.189 2011/02/17 16:02:22 jakemsr Exp $ */
/* $NetBSD: azalia.c,v 1.20 2006/05/07 08:31:44 kent Exp $ */
/*-
@@ -3330,7 +3330,7 @@ azalia_widget_init_connection(widget_t *this, const codec_t *codec)
uint32_t result;
int err;
int i, j, k;
- int length, bits, conn, last;
+ int length, nconn, bits, conn, last;
this->selected = -1;
if ((this->widgetcap & COP_AWCAP_CONNLIST) == 0)
@@ -3349,32 +3349,59 @@ azalia_widget_init_connection(widget_t *this, const codec_t *codec)
if (length == 0)
return 0;
- this->nconnections = length;
- this->connections = malloc(sizeof(nid_t) * length, M_DEVBUF, M_NOWAIT);
+ /*
+ * 'length' is the number of entries, not the number of
+ * connections. Find the number of connections, 'nconn', so
+ * enough space can be allocated for the list of connected
+ * nids.
+ */
+ nconn = last = 0;
+ for (i = 0; i < length;) {
+ err = azalia_comresp(codec, this->nid,
+ CORB_GET_CONNECTION_LIST_ENTRY, i, &result);
+ if (err)
+ return err;
+ for (k = 0; i < length && (k < 32 / bits); k++) {
+ conn = (result >> (k * bits)) & ((1 << bits) - 1);
+ /* If high bit is set, this is the end of a continuous
+ * list that started with the last connection.
+ */
+ if ((nconn > 0) && (conn & (1 << (bits - 1))))
+ nconn += (conn & ~(1 << (bits - 1))) - last;
+ else
+ nconn++;
+ last = conn;
+ i++;
+ }
+ }
+
+ this->connections = malloc(sizeof(nid_t) * nconn, M_DEVBUF, M_NOWAIT);
if (this->connections == NULL) {
printf("%s: out of memory\n", XNAME(codec->az));
return ENOMEM;
}
- for (i = 0; i < length;) {
+ for (i = 0; i < nconn;) {
err = azalia_comresp(codec, this->nid,
CORB_GET_CONNECTION_LIST_ENTRY, i, &result);
if (err)
return err;
- for (k = 0; i < length && (k < 32 / bits); k++) {
+ for (k = 0; i < nconn && (k < 32 / bits); k++) {
conn = (result >> (k * bits)) & ((1 << bits) - 1);
/* If high bit is set, this is the end of a continuous
* list that started with the last connection.
*/
if ((i > 0) && (conn & (1 << (bits - 1)))) {
- last = this->connections[i - 1];
- for (j = 1; i < length && j <= conn - last; j++)
+ for (j = 1; i < nconn && j <= conn - last; j++)
this->connections[i++] = last + j;
} else {
this->connections[i++] = conn;
}
+ last = conn;
}
}
- if (length > 0) {
+ this->nconnections = nconn;
+
+ if (nconn > 0) {
err = azalia_comresp(codec, this->nid,
CORB_GET_CONNECTION_SELECT_CONTROL, 0, &result);
if (err)