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); }