widget connection lists are described as a series of nids. the widget gives the number of connection list entries. if the most significant bit of a connection list entry is set, all nids between the last nid in the connection list and the nid in the entry with the most significant bit set are connected.
this means the number of connected nids isn't necessarily the same as the number of connection list entries. azalia currently assumes they are the same, and ends up ignoring some of the connections, which leads to widgets being considered unusable. the following runs through the connection list entries twice. the first is to get the number of connections. then once the number of connections is known, an array to hold the nids of the connected widgets is allocated. then loop over the connection list entries again and insert the nids into the array. this affects almost all Analog Devices (AD198x) and Sigmatel/IDT (STAC*/92HD*) codecs. please test and report to me. please include a dmesg and a diff of 'mixerctl -v' output from before and after applying the diff. thanks. -- jake...@sdf.lonestar.org SDF Public Access UNIX System - http://sdf.lonestar.org Index: azalia.c =================================================================== RCS file: /cvs/src/sys/dev/pci/azalia.c,v retrieving revision 1.188 diff -u -p azalia.c --- azalia.c 12 Sep 2010 03:17:34 -0000 1.188 +++ azalia.c 8 Feb 2011 03:52:17 -0000 @@ -3330,7 +3330,7 @@ azalia_widget_init_connection(widget_t *this, const co 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 co 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)