On Tue, Feb 08, 2011 at 04:08:53AM +0000, Jacob Meuser wrote:
> 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.

two positive reports for IDT codecs.  but I have not yet heard from
anyone with an analog devices codec.  

-- 
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    9 Feb 2011 05:09:50 -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)

Reply via email to