On Wed, Sep 30, 2015 at 06:07:54PM +0200, Joerg Jung wrote:
> On Wed, Sep 30, 2015 at 05:13:31PM +0200, Martijn van Duren wrote:
> > On 09/30/15 14:15, Joerg Jung wrote:
> 
> > Thanks! Although it does add about 5-10 seconds to my boot-time, waiting
> > primarily for the temperature sensors.
> 
> Yes, they are probed during startup. That probing is slow, as the SMC is
> slow.  This needs some rethinking/polishing, moving the probing to a
> later point.

tl;dr: please find below a refactoring diff that fixes this.


The -current asmc(4) code suffers from two problems:

I.  The initial sensors read may take several seconds.
II. The update of the sensor values tsleep()s excessively within the
    sensor task.

The following suggestions and hints to fix these timing issues were
provided to me:

1. mpi@ and Theo recommended not to tsleep() within the sensor task,
   mpi@ says: "Sleeping in the sensor thread might add latency to others
              sensors discovery."

   -> This is totally right, and matches II. problem statement above.

2. Theo suggested an async model using the sensor task (similar to
   ugold(4)), something like: sensor task comes, some command is issued,
   task returns, comes back later, reads result, next sensor, ...

   -> This sounds promising in theory, but I played a bit with this
      approach and it seems to be impossible to implement with asmc(4).
   -> The problem is that SMC requires several read() and write()
      operations for one command/sensor read, all of them acknowledged
      with waiting for the correct status, the protocol looks like this:
         1. write command, wait for status "accepted",
         2. write key, 4 single byte writes, each wait for status "accepted"
         3. write data length expected to read, wait for status "accepted"
         4. finally read data values, single byte reads each waiting for
            status "ready for read"
      Step 1-3 might take in the seldom worst case several seconds 
      (retries + timeout).  I can not see an easy way to do this async
      and step-by-step as waiting too long will result in
      "comm collision" errors or might take ages, as it reads values
      from up to 100 sensors using these steps.

So, I came to the conclusion that solving I.+II. really requires an
additional thread.

3. uebayasi@ suggested to look into sys/dev/ipmi.c, which uses a kthread
   to initially read all sensor values from a slow serial bus.

   -> This was a good hint, related ipmi code is simple and it can be
      easily done in the same way in asmc(4), but according to a quick
      grep it is the ONLY sensor driver which does it in this way, and
      moreover:

4. tedu@ says: "oh, don't make your own thread unless you really need
   the loop. you can create your own taskq as well"

   -> Makes sense, a quick grep revealed, that (again) ONLY one single
      driver does it like this: viomb(4), again using an easy pattern to
      copy into asmc(4).

So, finally the refactoring diff below follows tedu's suggestion and
uses an own taskq to solve I.+II.

Tests, comments and OKs are welcome.

Thanks,
Regards,
Joerg



Index: asmc.c
===================================================================
RCS file: /cvs/src/sys/dev/isa/asmc.c,v
retrieving revision 1.11
diff -u -p -r1.11 asmc.c
--- asmc.c      15 Oct 2015 01:14:33 -0000      1.11
+++ asmc.c      23 Oct 2015 13:24:34 -0000
@@ -23,6 +23,7 @@
 #include <sys/systm.h>
 #include <sys/device.h>
 #include <sys/kernel.h>
+#include <sys/task.h>
 #include <sys/sensors.h>
 
 #include <machine/bus.h>
@@ -62,7 +63,13 @@ struct asmc_softc {
        bus_space_handle_t       sc_ioh;
 
        struct asmc_prod        *sc_prod;
-       uint8_t                  sc_lightlen;   /* light data len */
+       uint8_t                  sc_init;       /* initialization done? */
+       uint8_t                  sc_nfans;      /* number of fans */
+       uint8_t                  sc_lightlen;   /* light data len */
+
+       struct taskq            *sc_taskq;
+       struct task              sc_task_init;
+       struct task              sc_task_refresh;
 
        struct ksensor           sc_sensor_temp[ASMC_MAXTEMP];
        struct ksensor           sc_sensor_fan[ASMC_MAXFAN];
@@ -72,7 +79,12 @@ struct asmc_softc {
        struct sensor_task      *sc_sensor_task;
 };
 
-int    asmc_init(struct asmc_softc *);
+uint8_t        asmc_status(struct asmc_softc *);
+int    asmc_try(struct asmc_softc *, int, const char *, uint8_t *, uint8_t);
+void   asmc_kbdled(struct asmc_softc *, uint8_t);
+
+void   asmc_init(void *);
+void   asmc_refresh(void *);
 void   asmc_update(void *);
 
 int    asmc_match(struct device *, void *, void *);
@@ -243,6 +255,8 @@ asmc_attach(struct device *parent, struc
 {
        struct asmc_softc *sc = (struct asmc_softc *)self;
        struct isa_attach_args *ia = aux;
+       uint8_t buf[6];
+       int i;
 
        if (bus_space_map(ia->ia_iot, ia->ia_iobase, ia->ia_iosize, 0,
            &sc->sc_ioh)) {
@@ -251,23 +265,64 @@ asmc_attach(struct device *parent, struc
        }
        sc->sc_iot = ia->ia_iot;
 
-       printf("\n");
+       if (asmc_try(sc, ASMC_READ, "REV ", buf, 6)) {
+               printf(": revision failed (0x%x)\n", asmc_status(sc));
+               bus_space_unmap(ia->ia_iot, ia->ia_iobase, ASMC_IOSIZE);
+               return;
+       }
+       printf(": rev %x.%x%x%x", buf[0], buf[1], buf[2],
+           ntohs(*(uint16_t *)buf + 4));
 
-       strlcpy(sc->sc_sensor_dev.xname, sc->sc_dev.dv_xname,
-           sizeof(sc->sc_sensor_dev.xname));
+       if (asmc_try(sc, ASMC_READ, "#KEY", buf, 4)) {
+               printf(", no of keys failed (0x%x)\n", asmc_status(sc));
+               bus_space_unmap(ia->ia_iot, ia->ia_iobase, ASMC_IOSIZE);
+               return;
+       }
+       printf(", %u key%s", ntohl(*(uint32_t *)buf),
+           (ntohl(*(uint32_t *)buf) == 1) ? "" : "s");
 
-       if (asmc_init(sc)) {
-               printf("%s: unable to initialize\n", sc->sc_dev.dv_xname);
+       if (!(sc->sc_taskq = taskq_create("asmc", 1, IPL_NONE, 0))) {
+               printf(", can't create task queue\n");
                bus_space_unmap(ia->ia_iot, ia->ia_iobase, ASMC_IOSIZE);
                return;
        }
+       task_set(&sc->sc_task_init, asmc_init, sc);
+       task_set(&sc->sc_task_refresh, asmc_refresh, sc);
 
-       if (!(sc->sc_sensor_task = sensor_task_register(sc, asmc_update, 8))) {
-               printf("%s: unable to register task\n", sc->sc_dev.dv_xname);
+       strlcpy(sc->sc_sensor_dev.xname, sc->sc_dev.dv_xname,
+           sizeof(sc->sc_sensor_dev.xname));
+       for (i = 0; i < ASMC_MAXTEMP; i++) {
+               sc->sc_sensor_temp[i].type = SENSOR_TEMP;
+               sc->sc_sensor_temp[i].flags |= SENSOR_FINVALID;
+               sc->sc_sensor_temp[i].flags |= SENSOR_FUNKNOWN;
+       }
+       for (i = 0; i < ASMC_MAXFAN; i++) {
+               sc->sc_sensor_fan[i].type = SENSOR_FANRPM;
+               sc->sc_sensor_fan[i].flags |= SENSOR_FINVALID;
+               sc->sc_sensor_fan[i].flags |= SENSOR_FUNKNOWN;
+       }
+       for (i = 0; i < ASMC_MAXLIGHT; i++) {
+               sc->sc_sensor_light[i].type = SENSOR_LUX;
+               sc->sc_sensor_light[i].flags |= SENSOR_FINVALID;
+               sc->sc_sensor_light[i].flags |= SENSOR_FUNKNOWN;
+       }
+       for (i = 0; i < ASMC_MAXMOTION; i++) {
+               sc->sc_sensor_motion[i].type = SENSOR_ACCEL;
+               sc->sc_sensor_motion[i].flags |= SENSOR_FINVALID;
+               sc->sc_sensor_motion[i].flags |= SENSOR_FUNKNOWN;
+       }
+       if (!(sc->sc_sensor_task = sensor_task_register(sc, asmc_update, 5))) {
+               printf(", unable to register sensor task\n");
+               taskq_destroy(sc->sc_taskq);
                bus_space_unmap(ia->ia_iot, ia->ia_iobase, ASMC_IOSIZE);
                return;
        }
+       printf("\n");
+
+       asmc_kbdled(sc, 127);
+
        sensordev_install(&sc->sc_sensor_dev);
+       task_add(sc->sc_taskq, &sc->sc_task_init);
 }
 
 int
@@ -276,27 +331,27 @@ asmc_detach(struct device *self, int fla
        struct asmc_softc *sc = (struct asmc_softc *)self;
        int i;
 
-       wakeup(&sc->sc_sensor_task);
-       sensordev_deinstall(&sc->sc_sensor_dev);
        if (sc->sc_sensor_task)
                sensor_task_unregister(sc->sc_sensor_task);
-
+       sensordev_deinstall(&sc->sc_sensor_dev);
        for (i = 0; i < ASMC_MAXMOTION; i++)
                sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_motion[i]);
-
        for (i = 0; i < ASMC_MAXLIGHT; i++)
                sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_light[i]);
-
        for (i = 0; i < ASMC_MAXFAN; i++)
                sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_fan[i]);
-
        for (i = 0; i < ASMC_MAXTEMP; i++)
                sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_temp[i]);
 
+       if (sc->sc_taskq) {
+               task_del(sc->sc_taskq, &sc->sc_task_refresh);
+               task_del(sc->sc_taskq, &sc->sc_task_init);
+               taskq_destroy(sc->sc_taskq);
+       }
        return 0;
 }
 
-static uint8_t
+uint8_t
 asmc_status(struct asmc_softc *sc)
 {
        return bus_space_read_1(sc->sc_iot, sc->sc_ioh, ASMC_STATUS);
@@ -309,7 +364,7 @@ asmc_wait(struct asmc_softc *sc, uint8_t
 
        for (us = (2 << 4); us < (2 << 16); us *= 2) { /* wait up to 128 ms */
                (!sc->sc_sensor_task) ? delay(us) :
-                   tsleep(&sc->sc_sensor_task, 0, "asmc",
+                   tsleep(&sc->sc_taskq, 0, "asmc",
                        (us * hz + 999999) / 1000000 + 1);
                if (bus_space_read_1(sc->sc_iot, sc->sc_ioh, ASMC_COMMAND) & m)
                        return 1;
@@ -320,13 +375,9 @@ asmc_wait(struct asmc_softc *sc, uint8_t
 static int
 asmc_write(struct asmc_softc *sc, uint8_t off, uint8_t val)
 {
-       int i;
-
-       for (i = 0; i < ASMC_RETRY; i++) {
-               bus_space_write_1(sc->sc_iot, sc->sc_ioh, off, val);
-               if (asmc_wait(sc, 0x04)) /* write accepted? */
-                       return 0;
-       }
+       bus_space_write_1(sc->sc_iot, sc->sc_ioh, off, val);
+       if (asmc_wait(sc, 0x04)) /* write accepted? */
+               return 0;
        return 1;
 }
 
@@ -372,7 +423,7 @@ asmc_command(struct asmc_softc *sc, int 
        return 0;
 }
 
-static int
+int
 asmc_try(struct asmc_softc *sc, int cmd, const char *key, uint8_t *buf,
     uint8_t len)
 {
@@ -384,279 +435,244 @@ asmc_try(struct asmc_softc *sc, int cmd,
        return 1;
 }
 
-static int
-asmc_temps(struct asmc_softc *sc, uint8_t *n)
+static uint32_t
+asmc_uk(uint8_t *buf)
 {
-       uint8_t buf[2], s;
-       int i, j;
+       /* spe78: floating point, signed, 7 bits exponent, 8 bits fraction */
+       return (((int16_t)ntohs(*(uint16_t *)buf)) >> 8) * 1000000 + 273150000;
+}
 
-       for (i = 0, *n = 0; i < ASMC_MAXTEMP; i++) {
-               sc->sc_sensor_temp[i].type = SENSOR_TEMP;
-               sc->sc_sensor_temp[i].flags |= SENSOR_FINVALID;
-               if (!sc->sc_prod->pr_temp[i])
-                       continue;
+static uint16_t
+asmc_rpm(uint8_t *buf)
+{
+       /* fpe2: floating point, unsigned, 14 bits exponent, 2 bits fraction */
+       return ntohs(*(uint16_t *)buf) >> 2;
+}
 
-               s = 0;
-               if (asmc_try(sc, ASMC_READ, sc->sc_prod->pr_temp[i], buf, 2) &&
-                   (s = asmc_status(sc)) != ASMC_NOTFOUND) {
-                       printf(", read %s failed (0x%x)",
-                           sc->sc_prod->pr_temp[i], s);
-                       continue; /*return 1;*/
-               }
-               if (s == ASMC_NOTFOUND)
-                       continue;
-
-               (*n)++;
-               strlcpy(sc->sc_sensor_temp[i].desc, sc->sc_prod->pr_temp[i],
-                   sizeof(sc->sc_sensor_temp[i].desc));
-               for (j = 0; asmc_temp_desc[j][0]; j++)
-                       if (!strcmp(asmc_temp_desc[j][0],
-                           sc->sc_prod->pr_temp[i]))
-                               break;
-               if (asmc_temp_desc[j][0]) {
-                       strlcat(sc->sc_sensor_temp[i].desc, " ",
-                           sizeof(sc->sc_sensor_temp[i].desc));
-                       strlcat(sc->sc_sensor_temp[i].desc,
-                           asmc_temp_desc[j][1],
-                           sizeof(sc->sc_sensor_temp[i].desc));
-               }
-               sc->sc_sensor_temp[i].flags &= ~SENSOR_FINVALID;
-               sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_temp[i]);
-       }
-       return 0;
+static uint32_t
+asmc_lux(uint8_t *buf, uint8_t lightlen)
+{
+       /* newer macbooks report a 10 bit big endian value */
+       return (lightlen == 10) ?
+           /* fp18.14: floating point, 18 bits exponent, 14 bits fraction */
+           (ntohl(*(uint32_t *)(buf + 6)) >> 14) * 1000000 :
+           /*
+            * todo: calculate lux from ADC raw data
+            * buf[1] true/false for high/low gain chan reads
+            * chan 0: ntohs(*(uint16_t *)(buf + 2));
+            * chan 1: ntohs(*(uint16_t *)(buf + 4));
+            */
+           ntohs(*(uint16_t *)(buf + 2)) * 1000000;
 }
 
 static int
-asmc_fans(struct asmc_softc *sc, uint8_t *n)
+asmc_temp(struct asmc_softc *sc, uint8_t idx)
 {
-       char key[5];
-       uint8_t buf[16], *end;
+       uint8_t buf[2];
        int i;
 
-       *n = 0;
-       if (asmc_try(sc, ASMC_READ, "FNum", buf, 1)) {
-               printf(", read FNum failed (0x%x)", asmc_status(sc));
+       if (asmc_try(sc, ASMC_READ, sc->sc_prod->pr_temp[idx], buf, 2))
                return 1;
-       }
-       *n = buf[0];
+       sc->sc_sensor_temp[idx].value = asmc_uk(buf);
+       sc->sc_sensor_temp[idx].flags &= ~SENSOR_FUNKNOWN;
 
-       for (i = 0; i < ASMC_MAXFAN; i++) {
-               sc->sc_sensor_fan[i].type = SENSOR_FANRPM;
-               sc->sc_sensor_fan[i].flags |= SENSOR_FINVALID;
-               if (i >= *n)
-                       continue;
+       if (sc->sc_init)
+               return 0;
 
-               snprintf(key, sizeof(key), "F%dID", i);
-               if (asmc_try(sc, ASMC_READ, key, buf, 16)) {
-                       printf(", read %s failed (0x%x)", key,
-                           asmc_status(sc));
-                       return 1;
-               }
-               end = buf + 4 + strlen((char *)buf + 4) - 1;
-               while (buf + 4 < end && *end == ' ') /* trim trailing spaces */
-                       *end-- = '\0';
-               strlcpy(sc->sc_sensor_fan[i].desc, buf + 4,
-                   sizeof(sc->sc_sensor_fan[i].desc));
-               if (buf[2] < nitems(asmc_fan_loc)) {
-                       strlcat(sc->sc_sensor_fan[i].desc, " ",
-                           sizeof(sc->sc_sensor_fan[i].desc));
-                       strlcat(sc->sc_sensor_fan[i].desc,
-                           asmc_fan_loc[buf[2]],
-                           sizeof(sc->sc_sensor_fan[i].desc));
-               }
-               sc->sc_sensor_fan[i].flags &= ~SENSOR_FINVALID;
-               sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_fan[i]);
+       strlcpy(sc->sc_sensor_temp[idx].desc, sc->sc_prod->pr_temp[idx],
+           sizeof(sc->sc_sensor_temp[idx].desc));
+       for (i = 0; asmc_temp_desc[i][0]; i++)
+               if (!strcmp(asmc_temp_desc[i][0], sc->sc_prod->pr_temp[idx]))
+                       break;
+       if (asmc_temp_desc[i][0]) {
+               strlcat(sc->sc_sensor_temp[idx].desc, " ",
+                   sizeof(sc->sc_sensor_temp[idx].desc));
+               strlcat(sc->sc_sensor_temp[idx].desc, asmc_temp_desc[i][1],
+                   sizeof(sc->sc_sensor_temp[idx].desc));
        }
+       sc->sc_sensor_temp[idx].flags &= ~SENSOR_FINVALID;
+       sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_temp[idx]);
        return 0;
 }
 
 static int
-asmc_lights(struct asmc_softc *sc, uint8_t *n)
+asmc_fan(struct asmc_softc *sc, uint8_t idx)
 {
        char key[5];
-       uint8_t buf[6], s;
-       int i;
+       uint8_t buf[16], *end;
 
-       for (i = 0, *n = 0; i < ASMC_MAXLIGHT; i++) {
-               sc->sc_sensor_light[i].type = SENSOR_LUX;
-               sc->sc_sensor_light[i].flags |= SENSOR_FINVALID;
-               s = 0;
-               snprintf(key, sizeof(key), "ALV%d", i);
-               if (asmc_try(sc, ASMC_READ, key, buf, 6) &&
-                   (s = asmc_status(sc)) != ASMC_NOTFOUND) {
-                       printf(", read %s failed (0x%x)", key, s);
-                       return 1;
-               }
-               if (s == ASMC_NOTFOUND || !buf[0]) /* valid data? */
-                       continue;
-
-               (*n)++;
-               if (!sc->sc_lightlen) {
-                       if (asmc_try(sc, ASMC_INFO, key, buf, 6)) {
-                               printf(", info %s failed (0x%x)", key,
-                                   asmc_status(sc));
-                               return 1;
-                       }
-                       sc->sc_lightlen = buf[0];
-               }
-               strlcpy(sc->sc_sensor_light[i].desc, asmc_light_desc[i],
-                   sizeof(sc->sc_sensor_light[i].desc));
-               sc->sc_sensor_light[i].flags &= ~SENSOR_FINVALID;
-               sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_light[i]);
+       snprintf(key, sizeof(key), "F%dAc", idx);
+       if (asmc_try(sc, ASMC_READ, key, buf, 2))
+               return 1;
+       sc->sc_sensor_fan[idx].value = asmc_rpm(buf);
+       sc->sc_sensor_fan[idx].flags &= ~SENSOR_FUNKNOWN;
+
+       if (sc->sc_init)
+               return 0;
+
+       snprintf(key, sizeof(key), "F%dID", idx);
+       if (asmc_try(sc, ASMC_READ, key, buf, 16))
+               return 1;
+       end = buf + 4 + strlen((char *)buf + 4) - 1;
+       while (buf + 4 < end && *end == ' ') /* trim trailing spaces */
+               *end-- = '\0';
+       strlcpy(sc->sc_sensor_fan[idx].desc, buf + 4,
+           sizeof(sc->sc_sensor_fan[idx].desc));
+       if (buf[2] < nitems(asmc_fan_loc)) {
+               strlcat(sc->sc_sensor_fan[idx].desc, " ",
+                   sizeof(sc->sc_sensor_fan[idx].desc));
+               strlcat(sc->sc_sensor_fan[idx].desc, asmc_fan_loc[buf[2]],
+                   sizeof(sc->sc_sensor_fan[idx].desc));
        }
+       sc->sc_sensor_fan[idx].flags &= ~SENSOR_FINVALID;
+       sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_fan[idx]);
        return 0;
 }
 
 static int
-asmc_motions(struct asmc_softc *sc, uint8_t *n)
+asmc_light(struct asmc_softc *sc, uint8_t idx)
 {
-       uint8_t buf[2], s;
-       int i;
+       char key[5];
+       uint8_t buf[10];
 
-       *n = 0;
-       s = 0;
-       if (asmc_try(sc, ASMC_READ, "MOCN", buf, 2) &&
-           (s = asmc_status(sc)) != ASMC_NOTFOUND) {
-               printf(", read MOCN failed (0x%x)", s);
-               return 1;
+       snprintf(key, sizeof(key), "ALV%d", idx);
+       if (!sc->sc_lightlen) {
+               if (asmc_try(sc, ASMC_INFO, key, buf, 6))
+                       return 1;
+               if ((sc->sc_lightlen = buf[0]) > 10)
+                       return 1;
        }
-       if (s == ASMC_NOTFOUND)
+       if (asmc_try(sc, ASMC_READ, key, buf, sc->sc_lightlen))
+               return 1;
+       if (!buf[0]) /* valid data? */
                return 0;
+       sc->sc_sensor_light[idx].value = asmc_lux(buf, sc->sc_lightlen);
+       sc->sc_sensor_light[idx].flags &= ~SENSOR_FUNKNOWN;
 
-       *n = 1;
-#if 0 /* todo: initialize sudden motion sensors and setup interrupt handling */
-       buf[0] = 0xe0, buf[1] = 0xf8;
-       if (asmc_try(sc, ASMC_WRITE, "MOCN", buf, 2)) {
-               printf(", write MOCN failed (0x%x)", asmc_status(sc));
+       if (sc->sc_init)
                return 0;
-       }
-#endif
-       for (i = 0; i < ASMC_MAXMOTION; i++) {
-               sc->sc_sensor_motion[i].type = SENSOR_ACCEL;
-               sc->sc_sensor_motion[i].flags |= SENSOR_FINVALID;
 
-#if 0 /* todo: setup and attach sensors and description */
-               strlcpy(sc->sc_sensor_motion[i].desc, 120 + i, /* x, y, z */
-                   sizeof(sc->sc_sensor_motion[i].desc));
-               strlcat(sc->sc_sensor_motion[i].desc, "-axis",
-                   sizeof(sc->sc_sensor_motion[i].desc));
-               sc->sc_sensor_motion[i].flags &= ~SENSOR_FINVALID;
-               sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_motion[i]);
-#endif
-       }
+       strlcpy(sc->sc_sensor_light[idx].desc, asmc_light_desc[idx],
+           sizeof(sc->sc_sensor_light[idx].desc));
+       sc->sc_sensor_light[idx].flags &= ~SENSOR_FINVALID;
+       sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_light[idx]);
        return 0;
 }
 
-int
-asmc_init(struct asmc_softc *sc)
+#if 0 /* todo: implement motion sensors update and initialization */
+static int
+asmc_motion(struct asmc_softc *sc, uint8_t idx)
 {
-       uint8_t buf[6], n, s;
-
-       printf("%s:", sc->sc_dev.dv_xname);
+       char key[5];
+       uint8_t buf[2];
 
-       if (asmc_try(sc, ASMC_READ, "REV ", buf, 6)) {
-               printf(" revision failed (0x%x)\n", asmc_status(sc));
+       snprintf(key, sizeof(key), "MO_%c", 88 + idx); /* X, Y, Z */
+       if (asmc_try(sc, ASMC_READ, key, buf, 2))
                return 1;
-       }
-       printf(" rev %x.%x%x%x", buf[0], buf[1], buf[2],
-           ntohs(*(uint16_t *)buf + 4));
-
-       if (asmc_try(sc, ASMC_READ, "#KEY", buf, 4)) {
-               printf(", no of keys failed (0x%x)\n", asmc_status(sc));
-               return 1;
-       }
-       printf(", %u key%s", ntohl(*(uint32_t *)buf),
-           (ntohl(*(uint32_t *)buf) == 1) ? "" : "s");
+       sc->sc_sensor_motion[idx].value = 0;
+       sc->sc_sensor_motion[idx].flags &= ~SENSOR_FUNKNOWN;
 
-       if (asmc_temps(sc, &n)) { /* temperature sensors depend on product */
-               printf(", temperature sensors failed\n");
-               return 1;
-       }
-       printf(", %u temperature%s", n, (n == 1) ? "" : "s");
+       if (sc->sc_init)
+               return 0;
 
-       if (asmc_fans(sc, &n)) { /* fan sensors depend on product */
-               printf(", fan sensors failed\n");
-               return 1;
-       }
-       printf(", %u fan%s", n, (n == 1) ? "" : "s");
+       /* todo: setup and attach sensors and description */
+       strlcpy(sc->sc_sensor_motion[idx].desc, 120 + idx, /* x, y, z */
+           sizeof(sc->sc_sensor_motion[idx].desc));
+       strlcat(sc->sc_sensor_motion[idx].desc, "-axis",
+           sizeof(sc->sc_sensor_motion[idx].desc));
+       sc->sc_sensor_motion[idx].flags &= ~SENSOR_FINVALID;
+       sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_motion[idx]);
+       return 0;
+}
+#endif
 
-       if (asmc_lights(sc, &n)) { /* l/r light sensors are optional */
-               printf(", light sensors failed\n");
-               return 1;
-       }
-       printf("%s", n ? ", lights" : "");
+void
+asmc_kbdled(struct asmc_softc *sc, uint8_t b)
+{
+       uint8_t buf[2] = { b, 0 }, s;
 
-       if (asmc_motions(sc, &n)) { /* motion sensors are optional */
-               printf(", sudden motion sensors failed\n");
-               return 1;
+       /* keyboard backlight led is optional */
+       if (asmc_try(sc, ASMC_WRITE, "LKSB", buf, 2)) {
+               if ((s = asmc_status(sc)) != ASMC_NOTFOUND)
+                       printf("%s: keyboard backlight failed (0x%x)\n",
+                           sc->sc_dev.dv_xname, s);
        }
-       printf("%s", n ? ", motions" : "");
+}
 
-       buf[0] = 127, buf[1] = 0; /* keyboard backlight led is optional */
-       if (asmc_try(sc, ASMC_WRITE, "LKSB", buf, 2) &&
-           (s = asmc_status(sc)) != ASMC_NOTFOUND) {
-               printf(", keyboard backlight failed (0x%x)\n", s);
-               return 1;
-       }
-       printf("\n");
+void
+asmc_init(void *arg)
+{
+       struct asmc_softc *sc = arg;
+       uint8_t buf[2], s;
+       int i;
 
-       return 0;
+       /* number of temperature sensors depends on product */
+       for (i = 0; sc->sc_prod->pr_temp[i] && i < ASMC_MAXTEMP; i++)
+               if (asmc_temp(sc, i) && (s = asmc_status(sc)) != ASMC_NOTFOUND)
+                       printf("%s: read temp %d failed (0x%x)\n",
+                           sc->sc_dev.dv_xname, i, s);
+       /* number of fan sensors depends on product */
+       if (asmc_try(sc, ASMC_READ, "FNum", buf, 1))
+               printf("%s: read FNum failed (0x%x)\n", sc->sc_dev.dv_xname,
+                   asmc_status(sc));
+       else
+               sc->sc_nfans = buf[0];
+       for (i = 0; i < sc->sc_nfans && i < ASMC_MAXFAN; i++)
+               if (asmc_fan(sc, i) && (s = asmc_status(sc)) != ASMC_NOTFOUND)
+                       printf("%s: read fan %d failed (0x%x)\n",
+                           sc->sc_dev.dv_xname, i, s);
+       /* left and right light sensors are optional */
+       for (i = 0; i < ASMC_MAXLIGHT; i++)
+               if (asmc_light(sc, i) &&
+                   (s = asmc_status(sc)) != ASMC_NOTFOUND)
+                       printf("%s: read light %d failed (0x%x)\n",
+                           sc->sc_dev.dv_xname, i, s);
+       /* motion sensors are optional */
+       if (asmc_try(sc, ASMC_READ, "MOCN", buf, 2) &&
+           (s = asmc_status(sc)) != ASMC_NOTFOUND)
+               printf("%s: read MOCN failed (0x%x)\n",
+                   sc->sc_dev.dv_xname, s);
+#if 0 /* todo: initialize sudden motion sensors and setup interrupt handling */
+       buf[0] = 0xe0, buf[1] = 0xf8;
+       if (asmc_try(sc, ASMC_WRITE, "MOCN", buf, 2))
+               printf("%s write MOCN failed (0x%x)\n", sc->sc_dev.dv_xname,
+                   asmc_status(sc));
+       for (i = 0; i < ASMC_MAXMOTION; i++)
+               if (asmc_motion(sc, i) &&
+                   (s = asmc_status(sc)) != ASMC_NOTFOUND)
+                       printf("%s: read motion %d failed (0x%x)\n",
+                           sc->sc_dev.dv_xname, i, s);
+#endif
+       sc->sc_init = 1;
 }
 
 void
-asmc_update(void *arg)
+asmc_refresh(void *arg)
 {
        struct asmc_softc *sc = arg;
-       char key[5];
-       uint8_t buf[10];
        int i;
 
-       /* spe78: floating point, signed, 7 bits exponent, 8 bits fraction */
-       for (i = 0; i < ASMC_MAXTEMP; i++) {
-               if (!(sc->sc_sensor_temp[i].flags & SENSOR_FINVALID) &&
-                   !asmc_try(sc, ASMC_READ, sc->sc_prod->pr_temp[i], buf, 2))
-                       sc->sc_sensor_temp[i].value =
-                           (((int16_t)ntohs(*(uint16_t *)buf)) >> 8) *
-                           1000000 + 273150000;
-       }
-
-       /* fpe2: floating point, unsigned, 14 bits exponent, 2 bits fraction */
-       for (i = 0; i < ASMC_MAXFAN; i++) {
-               snprintf(key, sizeof(key), "F%dAc", i);
-               if (!(sc->sc_sensor_fan[i].flags & SENSOR_FINVALID) &&
-                   !asmc_try(sc, ASMC_READ, key, buf, 2))
-                       sc->sc_sensor_fan[i].value =
-                           ntohs(*(uint16_t *)buf) >> 2;
-       }
+       for (i = 0; sc->sc_prod->pr_temp[i] && i < ASMC_MAXTEMP; i++)
+               if (!(sc->sc_sensor_temp[i].flags & SENSOR_FINVALID))
+                       asmc_temp(sc, i);
+       for (i = 0; i < sc->sc_nfans && i < ASMC_MAXFAN; i++)
+               if (!(sc->sc_sensor_fan[i].flags & SENSOR_FINVALID))
+                       asmc_fan(sc, i);
+       for (i = 0; i < ASMC_MAXLIGHT; i++)
+               if (!(sc->sc_sensor_light[i].flags & SENSOR_FINVALID))
+                       asmc_light(sc, i);
+#if 0
+       for (i = 0; i < ASMC_MAXMOTION; i++)
+               if (!(sc->sc_sensor_motion[i].flags & SENSOR_FINVALID))
+                       asmc_motion(sc, i);
+#endif
+}
 
-       for (i = 0; i < ASMC_MAXLIGHT; i++) {
-               snprintf(key, sizeof(key), "ALV%d", i);
-               if (!(sc->sc_sensor_light[i].flags & SENSOR_FINVALID) &&
-                   !asmc_try(sc, ASMC_READ, key, buf, sc->sc_lightlen))
-                       /* newer macbooks report an 10 bit big endian value */
-                       sc->sc_sensor_light[i].value =
-                           (sc->sc_lightlen == 10) ?
-                           /*
-                            * fp18.14: floating point,
-                            * 18 bits exponent, 14 bits fraction
-                            */
-                           (ntohl(*(uint32_t *)(buf + 6)) >> 14) * 1000000 :
-                           /*
-                            * todo: calculate lux from ADC raw data:
-                            * buf[1] true/false for high/low gain chan reads
-                            * chan 0: ntohs(*(uint16_t *)(buf + 2));
-                            * chan 1: ntohs(*(uint16_t *)(buf + 4));
-                            */
-                           ((sc->sc_sensor_light[i].flags |=
-                               SENSOR_FUNKNOWN), 0);
-       }
+void
+asmc_update(void *arg)
+{
+       struct asmc_softc *sc = arg;
 
-#if 0 /* todo: implement motion sensors update */
-       for (i = 0; i < ASMC_MAXMOTION; i++) {
-               snprintf(key, sizeof(key), "MO_%c", 88 + i); /* X, Y, Z */
-               if (!(sc->sc_sensor_motion[i].flags & SENSOR_FINVALID) &&
-                   !asmc_try(sc, ASMC_READ, key, buf, 2))
-                       sc->sc_sensor_motion[i].value = 0;
-       }
-#endif
+       if (sc->sc_init)
+               task_add(sc->sc_taskq, &sc->sc_task_refresh);
 }

Reply via email to