On 30/04/13(Tue) 17:39, Stuart Henderson wrote:
> On 2013/04/30 15:06, Stuart Henderson wrote:
> > On 2013/03/31 17:56, SASANO Takayoshi wrote:
> > > Hello,
> > >
> > > I rewrote patched uthum(4) to new ugold(4) driver.
> > > Thanks for advice by yuo@ and deraadt@.
Some comments inline.
> Index: dev/usb/ugold.c
> ===================================================================
> [...]
> +/* Driver for Microdia's HID base TEMPer Temperature sensor */
> +
> +#include <sys/param.h>
> +#include <sys/proc.h>
> +#include <sys/systm.h>
> +#include <sys/kernel.h>
> +#include <sys/malloc.h>
> +#include <sys/device.h>
> +#include <sys/conf.h>
> +#include <sys/sensors.h>
> +
> +#include <dev/usb/usb.h>
> +#include <dev/usb/usbhid.h>
> +#include <dev/usb/usbdi.h>
> +#include <dev/usb/usbdi_util.h>
> +#include <dev/usb/usbdevs.h>
> +#include <dev/usb/uhidev.h>
> +#include <dev/usb/hid.h>
Please make sure you include only the required headers, at least proc.h is
not needed.
> +
> +#ifdef USB_DEBUG
> +#define UGOLD_DEBUG
> +#endif
> +
> +#ifdef UGOLD_DEBUG
> +int ugolddebug = 0;
> +#define DPRINTFN(n, x) do { if (ugolddebug > (n)) printf x; } while (0)
> +#else
> +#define DPRINTFN(n, x)
> +#endif
> +
> +#define DPRINTF(x) DPRINTFN(0, x)
If you don't use the *N() variant, just keep it simple a only define the
DPRINTF() macro, this will also allow you to remove the ugolddebug
variable.
> +
> +/* Device types */
> +#define UGOLD_TYPE_TEMPER 0
> +#define UGOLD_TYPE_UNKNOWN 0xffff
> +
> +/* sensors */
> +#define UGOLD_TEMPER_INNER 0
> +#define UGOLD_TEMPER_OUTER 1
> +#define UGOLD_MAX_SENSORS 2
> +
> +enum ugold_sensor_type {
> + UGOLD_SENSOR_UNKNOWN,
> + UGOLD_SENSOR_DS75,
> + UGOLD_SENSOR_MAXTYPES,
> +};
> +
> +static const char * const ugold_sensor_type_s[UGOLD_SENSOR_MAXTYPES] = {
> + "unknown",
> + "ds75/12bit",
> +};
> +
> +static uint8_t cmd_led_off[2] =
> + { 0x01, 0x00 };
> +static uint8_t cmd_get_offset[8] =
> + { 0x01, 0x82, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00 };
> +static uint8_t cmd_init[8] =
> + { 0x01, 0x86, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00 };
> +static uint8_t cmd_get_data[8] =
> + { 0x01, 0x80, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00 };
> +
> +struct ugold_sensor {
> + struct ksensor sensor;
> + int cal_offset; /* 10mC or m%RH */
> + int attached;
> + enum ugold_sensor_type dev_type;
> +};
> +
> +struct ugold_softc {
> + struct uhidev sc_hdev;
> + usbd_device_handle sc_udev;
> + u_char sc_dying;
You don't use sc_dying so you can probably remove it and the *activate()
function also.
> + uint16_t sc_flag;
This field is also unused.
> + int sc_device_type;
Because you're attaching only one kind of device there's no need for
this field. I suggest you to remove it and all the unneeded logic that
comes with it.
> + int sc_initialized;
> + int sc_num_sensors;
> +
> + /* uhidev parameters */
> + size_t sc_flen; /* feature report length */
> + size_t sc_ilen; /* input report length */
> + size_t sc_olen; /* output report length */
> +
> + uint8_t *sc_ibuf;
> +
> + /* sensor framework */
> + struct ugold_sensor sc_sensor[UGOLD_MAX_SENSORS];
> + struct ksensordev sc_sensordev;
> + struct sensor_task *sc_sensortask;
> +};
> +
> +const struct usb_devno ugold_devs[] = {
> + { USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_TEMPER },
> +};
> +#define ugold_lookup(v, p) usb_lookup(ugold_devs, v, p)
> +
> +int ugold_match(struct device *, void *, void *);
> +void ugold_attach(struct device *, struct device *, void *);
> +int ugold_detach(struct device *, int);
> +int ugold_activate(struct device *, int);
> +
> +int ugold_issue_cmd(struct ugold_softc *, uint8_t *, int, int);
> +int ugold_read_data(struct ugold_softc *);
> +int ugold_init_device(struct ugold_softc *);
> +void ugold_setup_sensors(struct ugold_softc *);
> +int ugold_setup_device(struct ugold_softc *);
> +
> +void ugold_intr(struct uhidev *, void *, u_int);
> +void ugold_refresh(void *);
> +void ugold_refresh_temper(struct ugold_softc *);
> +
> +int ugold_ds75_temp(uint8_t, uint8_t);
> +void ugold_print_sensorinfo(struct ugold_softc *, int);
> +
> +struct cfdriver ugold_cd = {
> + NULL, "ugold", DV_DULL
> +};
> +
> +const struct cfattach ugold_ca = {
> + sizeof(struct ugold_softc),
> + ugold_match,
> + ugold_attach,
> + ugold_detach,
> + ugold_activate,
> +};
> +
> +int
> +ugold_match(struct device *parent, void *match, void *aux)
> +{
> + struct usb_attach_arg *uaa = aux;
> + struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
> +
> + if (ugold_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
> + return UMATCH_NONE;
> +
> + return (UMATCH_VENDOR_PRODUCT);
> +}
> +
> +void
> +ugold_attach(struct device *parent, struct device *self, void *aux)
> +{
> + struct ugold_softc *sc = (struct ugold_softc *)self;
> + struct usb_attach_arg *uaa = aux;
> + struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
> + usbd_device_handle dev = uha->parent->sc_udev;
> + int i, size, repid, err;
> + void *desc;
> +
> + sc->sc_udev = dev;
> + sc->sc_hdev.sc_intr = ugold_intr;
> + sc->sc_hdev.sc_parent = uha->parent;
> + sc->sc_hdev.sc_report_id = uha->reportid;
> + sc->sc_initialized = 0;
> + sc->sc_num_sensors = 0;
> +
> + uhidev_get_report_desc(uha->parent, &desc, &size);
> + repid = uha->reportid;
> + sc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
> + sc->sc_olen = hid_report_size(desc, size, hid_output, repid);
> + sc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
> +
> + printf("\n");
> +
> + /* check device type */
> + if (uha->uaa->vendor == USB_VENDOR_MICRODIA &&
> + uha->uaa->product == USB_PRODUCT_MICRODIA_TEMPER) {
> + sc->sc_device_type = UGOLD_TYPE_TEMPER;
> + } else {
> + sc->sc_device_type = UGOLD_TYPE_UNKNOWN;
> + }
> +
> + if (sc->sc_flen < 8) {
> + /* not sensor interface, just attach */
> + return;
> + }
> +
> + /* sensor data comes from interrupt pipe */
> + err = uhidev_open(&sc->sc_hdev);
> + if (err) {
> + printf("ugold: uhidev_open %d\n", err);
> + return;
> + }
> + sc->sc_ibuf = malloc(sc->sc_ilen, M_USBDEV, M_WAITOK);
I'm not a big fan of using the same name "sc_ibuf" for your buffer as
the one given to the USB framework, I find it really misleading... But
it looks like it's done in a lot of drivers :/
But anyway, I think that in your case you don't need it. Why don't you
update directly your sensor values when you receive the data in
ugold_intr() instead of copying to a intermediate buffer?
> + /*
> + * interrupt pipe is opened but data comes after ugold_attach()
> + * is finished. simply attach sensors here and the device will be
> + * initialized at ugold_refresh().
> + *
> + * at this point, the number of sensors is unknown. setup maximum
> + * sensors here and detach unused sensor later.
> + */
Why don't you initialize the device before opening the interrupt pipe?
> + /* setup sensor */
> + strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
> + sizeof(sc->sc_sensordev.xname));
> + ugold_setup_sensors(sc);
> +
> + /* attach sensors */
> + for (i = 0; i < UGOLD_MAX_SENSORS; i++) {
> + sc->sc_sensor[i].sensor.flags |= SENSOR_FINVALID;
> + sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i].sensor);
> + sc->sc_sensor[i].attached = 1;
> + sc->sc_num_sensors++;
> + }
> +
> + /* 0.1Hz */
> + sc->sc_sensortask = sensor_task_register(sc, ugold_refresh, 6);
> + if (sc->sc_sensortask == NULL) {
> + printf(", unable to register update task\n");
> + return;
> + }
> + sensordev_install(&sc->sc_sensordev);
> +
> + DPRINTF(("ugold_attach: complete\n"));
> +}
> +
> +int
> +ugold_detach(struct device *self, int flags)
> +{
> + struct ugold_softc *sc = (struct ugold_softc *)self;
> + int i, rv = 0;
> +
> + if (sc->sc_num_sensors > 0) {
> + wakeup(&sc->sc_sensortask);
> + sensordev_deinstall(&sc->sc_sensordev);
> + for (i = 0; i < UGOLD_MAX_SENSORS; i++) {
> + if (sc->sc_sensor[i].attached)
> + sensor_detach(&sc->sc_sensordev,
> + &sc->sc_sensor[i].sensor);
> + }
> + if (sc->sc_sensortask != NULL)
> + sensor_task_unregister(sc->sc_sensortask);
> + }
> +
> + if (sc->sc_ibuf != NULL) {
> + free(sc->sc_ibuf, M_USBDEV);
> + sc->sc_ibuf = NULL;
> + }
> +
> + return (rv);
> +}
> +void
> +ugold_intr(struct uhidev *addr, void *ibuf, u_int len)
> +{
> + struct ugold_softc *sc = (struct ugold_softc *)addr;
> +
> + if (sc->sc_ibuf == NULL)
> + return;
> +
> + /* receive sensor data */
> + memcpy(sc->sc_ibuf, ibuf, len);
> +
> + return;
> +}
> +
> +int
> +ugold_issue_cmd(struct ugold_softc *sc, uint8_t *cmd, int len, int delay)
> +{
> + usb_device_request_t req;
> +
> + bzero(sc->sc_ibuf, sc->sc_ilen);
> +
> + req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
> + req.bRequest = UR_SET_REPORT;
> + USETW(req.wValue, 0x0200);
> + USETW(req.wIndex, 0x0001);
> + USETW(req.wLength, len);
> + if (usbd_do_request(sc->sc_udev, &req, cmd))
> + return EIO;
I would suggest you to have a look at uhidev_set_report{,_async}() instead of
writing your own version here.
> + /* wait if required */
> + if (delay > 0 &&
> + !tsleep(&sc->sc_sensortask, 0, "ugold", (delay*hz+999)/1000 + 1))
> + return EIO;
> +
> + return 0;
> +}
> +
> +int
> +ugold_init_device(struct ugold_softc *sc)
> +{
> + usb_device_request_t req;
> + usbd_status error;
> +
> + /* send SetReport request to another (Keyboard) interface */
> + req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
> + req.bRequest = UR_SET_REPORT;
> + USETW(req.wValue, 0x0201);
> + USETW(req.wIndex, 0x0000);
> + USETW(req.wLength, sizeof(cmd_led_off));
> + error = usbd_do_request(sc->sc_udev, &req, cmd_led_off);
> + if (error)
> + return EIO;
Same here, this is likely to be another uhidev_set_report() with a
different interface, no?
> + /* init process */
> + if (ugold_issue_cmd(sc, cmd_get_offset, sizeof(cmd_get_offset), 200))
> + return EIO;
> +
> + /* received one interrupt message, it contains offset parameter */
> + sc->sc_num_sensors = sc->sc_ibuf[1];
How can you be sure sc_ibuf contains the data you asked for?
> + if (sc->sc_num_sensors > UGOLD_MAX_SENSORS)
> + sc->sc_num_sensors = UGOLD_MAX_SENSORS;
> + sc->sc_sensor[UGOLD_TEMPER_INNER].cal_offset =
> + ((int8_t)sc->sc_ibuf[2] * 25) / 4;
> + sc->sc_sensor[UGOLD_TEMPER_OUTER].cal_offset =
> + ((int8_t)sc->sc_ibuf[3] * 25) / 4;
> +
> + if (ugold_issue_cmd(sc, cmd_init, sizeof(cmd_init), 200))
> + return EIO;
> +
> + /* received two interrupt messages, discard them */
> +
> + return 0;
> +}
> +
> +void
> +ugold_setup_sensors(struct ugold_softc *sc)
> +{
> + int i;
> +
> + for (i = 0; i < UGOLD_MAX_SENSORS; i++)
> + sc->sc_sensor[i].dev_type = UGOLD_SENSOR_UNKNOWN;
> +
> + switch (sc->sc_device_type) {
> + case UGOLD_TYPE_TEMPER:
> + sc->sc_sensor[UGOLD_TEMPER_OUTER].dev_type =
> + UGOLD_SENSOR_DS75;
> + sc->sc_sensor[UGOLD_TEMPER_OUTER].sensor.type =
> + SENSOR_TEMP;
> + strlcpy(sc->sc_sensor[UGOLD_TEMPER_OUTER].sensor.desc,
> + "outer",
> + sizeof(sc->sc_sensor[UGOLD_TEMPER_OUTER].sensor.desc));
> + sc->sc_sensor[UGOLD_TEMPER_INNER].dev_type =
> + UGOLD_SENSOR_DS75;
> + sc->sc_sensor[UGOLD_TEMPER_INNER].sensor.type =
> + SENSOR_TEMP;
> + strlcpy(sc->sc_sensor[UGOLD_TEMPER_INNER].sensor.desc,
> + "inner",
> + sizeof(sc->sc_sensor[UGOLD_TEMPER_INNER].sensor.desc));
> + break;
> + default:
> + /* do nothing */
> + break;
> + }
> +}
> +
> +int
> +ugold_setup_device(struct ugold_softc *sc)
> +{
> + int i;
> +
> + if (ugold_init_device(sc)) {
> + DPRINTF(("ugold: device initialize failed\n"));
> + return EIO;
> + }
> +
> + /* detach unused sensors */
> + for (i = 0; i < UGOLD_MAX_SENSORS; i++) {
> + if (sc->sc_sensor[i].attached &&
> + i >= sc->sc_num_sensors) {
> + sensor_detach(&sc->sc_sensordev,
> + &sc->sc_sensor[i].sensor);
> + sc->sc_sensor[i].attached = 0;
> + }
> + }
> +
> + for (i = 0; i < sc->sc_num_sensors; i++) {
> + if (sc->sc_sensor[i].attached)
> + ugold_print_sensorinfo(sc, i);
> + }
> +
> + return 0;
> +}
> +
> +void
> +ugold_refresh(void *arg)
> +{
> + struct ugold_softc *sc = arg;
> +
> + /* if device is not initialized, initialize first */
> + if (!sc->sc_initialized) {
> + if (ugold_setup_device(sc))
> + return;
> + sc->sc_initialized = 1;
> + }
> +
> + switch (sc->sc_device_type) {
> + case UGOLD_TYPE_TEMPER:
> + ugold_refresh_temper(sc);
> + break;
> + default:
> + break;
> + /* NOTREACHED */
> + }
> +}
> +
> +void
> +ugold_refresh_temper(struct ugold_softc *sc)
> +{
> + int temp;
> +
> + /* get sensor data */
> + if (ugold_read_data(sc)) {
> + DPRINTF(("ugold: data read failed\n"));
> + sc->sc_sensor[UGOLD_TEMPER_OUTER].sensor.flags
> + |= SENSOR_FINVALID;
> + sc->sc_sensor[UGOLD_TEMPER_INNER].sensor.flags
> + |= SENSOR_FINVALID;
> + return;
> + }
How can you be sure sc_ibuf contains the data you want to read?
> + switch (sc->sc_num_sensors) {
> + case 2:
> + temp = ugold_ds75_temp(sc->sc_ibuf[4], sc->sc_ibuf[5]);
> + temp += sc->sc_sensor[UGOLD_TEMPER_OUTER].cal_offset;
> + sc->sc_sensor[UGOLD_TEMPER_OUTER].sensor.value =
> + (temp * 10000) + 273150000;
> + sc->sc_sensor[UGOLD_TEMPER_OUTER].sensor.flags &=
> + ~SENSOR_FINVALID;
> + /* FALLTHROUGH */
> + case 1:
> + temp = ugold_ds75_temp(sc->sc_ibuf[2], sc->sc_ibuf[3]);
> + temp += sc->sc_sensor[UGOLD_TEMPER_INNER].cal_offset;
> + sc->sc_sensor[UGOLD_TEMPER_INNER].sensor.value =
> + (temp * 10000) + 273150000;
> + sc->sc_sensor[UGOLD_TEMPER_INNER].sensor.flags &=
> + ~SENSOR_FINVALID;
> + break;
> + }
> +}
> +
> +/* return C-degree * 100 value */
> +int
> +ugold_ds75_temp(uint8_t msb, uint8_t lsb)
> +{
> + /* DS75: 12bit precision mode : 0.0625 degrees Celsius ticks */
> + return (msb * 100) + ((lsb >> 4) * 25 / 4);
> +}
> +
> +void
> +ugold_print_sensorinfo(struct ugold_softc *sc, int num)
> +{
> + struct ugold_sensor *s;
> + s = &sc->sc_sensor[num];
> +
> + printf("%s: ", sc->sc_hdev.sc_dev.dv_xname);
> + switch (s->sensor.type) {
> + case SENSOR_TEMP:
> + printf("type %s (temperature)",
> + ugold_sensor_type_s[s->dev_type]);
> + if (s->cal_offset)
> + printf(", calibration offset %d.%d degC",
> + s->cal_offset / 100, abs(s->cal_offset % 100));
> + break;
> + case SENSOR_HUMIDITY:
> + printf("type %s (humidity)",
> + ugold_sensor_type_s[s->dev_type]);
> + if (s->cal_offset)
> + printf("calibration offset %d.%d %%RH",
> + s->cal_offset / 100, abs(s->cal_offset % 100));
> + break;
> + default:
> + printf("unknown");
> + }
> + printf("\n");
> +}