The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/usb/misc/ugold.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $OpenBSD: ugold.c,v 1.7 2014/12/11 18:39:27 mpi Exp $   */
    2 
    3 /*
    4  * Copyright (c) 2013 Takayoshi SASANO <sasano@openbsd.org>
    5  * Copyright (c) 2013 Martin Pieuchot <mpi@openbsd.org>
    6  *
    7  * Permission to use, copy, modify, and distribute this software for any
    8  * purpose with or without fee is hereby granted, provided that the above
    9  * copyright notice and this permission notice appear in all copies.
   10  *
   11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES
   12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   18  */
   19 
   20 /* Driver for Microdia's HID based TEMPer Temperature sensor */
   21 
   22 #include <sys/cdefs.h>
   23 __FBSDID("$FreeBSD$");
   24 
   25 #include <sys/stdint.h>
   26 #include <sys/stddef.h>
   27 #include <sys/param.h>
   28 #include <sys/queue.h>
   29 #include <sys/types.h>
   30 #include <sys/systm.h>
   31 #include <sys/kernel.h>
   32 #include <sys/bus.h>
   33 #include <sys/module.h>
   34 #include <sys/lock.h>
   35 #include <sys/mutex.h>
   36 #include <sys/condvar.h>
   37 #include <sys/sysctl.h>
   38 #include <sys/sx.h>
   39 #include <sys/unistd.h>
   40 #include <sys/callout.h>
   41 #include <sys/malloc.h>
   42 #include <sys/priv.h>
   43 #include <sys/conf.h>
   44 
   45 #include <dev/hid/hid.h>
   46 
   47 #include <dev/usb/usb.h>
   48 #include <dev/usb/usbdi.h>
   49 #include <dev/usb/usbhid.h>
   50 #include <dev/usb/usb_process.h>
   51 #include <dev/usb/usbdi_util.h>
   52 #include "usbdevs.h"
   53 
   54 #define USB_DEBUG_VAR usb_debug
   55 #include <dev/usb/usb_debug.h>
   56 
   57 #define UGOLD_INNER             0
   58 #define UGOLD_OUTER             1
   59 #define UGOLD_MAX_SENSORS       2
   60 
   61 #define UGOLD_CMD_DATA          0x80
   62 #define UGOLD_CMD_INIT          0x82
   63 
   64 enum {
   65         UGOLD_INTR_DT,
   66         UGOLD_N_TRANSFER,
   67 };
   68 
   69 /*
   70  * This driver only uses two of the three known commands for the
   71  * TEMPerV1.2 device.
   72  *
   73  * The first byte of the answer corresponds to the command and the
   74  * second one seems to be the size (in bytes) of the answer.
   75  *
   76  * The device always sends 8 bytes and if the length of the answer
   77  * is less than that, it just leaves the last bytes untouched.  That
   78  * is why most of the time the last n bytes of the answers are the
   79  * same.
   80  *
   81  * The third command below seems to generate two answers with a
   82  * string corresponding to the device, for example:
   83  *      'TEMPer1F' and '1.1Per1F' (here Per1F is repeated).
   84  */
   85 static uint8_t cmd_data[8] = {0x01, 0x80, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00};
   86 static uint8_t cmd_init[8] = {0x01, 0x82, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00};
   87 
   88 #if 0
   89 static uint8_t cmd_type[8] = {0x01, 0x86, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00};
   90 
   91 #endif
   92 
   93 struct ugold_softc;
   94 struct ugold_readout_msg {
   95         struct usb_proc_msg hdr;
   96         struct ugold_softc *sc;
   97 };
   98 
   99 struct ugold_softc {
  100         struct usb_device *sc_udev;
  101         struct usb_xfer *sc_xfer[UGOLD_N_TRANSFER];
  102 
  103         struct callout sc_callout;
  104         struct mtx sc_mtx;
  105         struct ugold_readout_msg sc_readout_msg[2];
  106 
  107         int     sc_num_sensors;
  108         int     sc_sensor[UGOLD_MAX_SENSORS];
  109         int     sc_calib[UGOLD_MAX_SENSORS];
  110         int     sc_valid[UGOLD_MAX_SENSORS];
  111         uint8_t sc_report_id;
  112         uint8_t sc_iface_index[2];
  113 };
  114 
  115 /* prototypes */
  116 
  117 static device_probe_t ugold_probe;
  118 static device_attach_t ugold_attach;
  119 static device_detach_t ugold_detach;
  120 
  121 static usb_proc_callback_t ugold_readout_msg;
  122 
  123 static usb_callback_t ugold_intr_callback;
  124 
  125 static device_method_t ugold_methods[] = {
  126         DEVMETHOD(device_probe, ugold_probe),
  127         DEVMETHOD(device_attach, ugold_attach),
  128         DEVMETHOD(device_detach, ugold_detach),
  129 
  130         DEVMETHOD_END
  131 };
  132 
  133 static driver_t ugold_driver = {
  134         .name = "ugold",
  135         .methods = ugold_methods,
  136         .size = sizeof(struct ugold_softc),
  137 };
  138 
  139 static const STRUCT_USB_HOST_ID ugold_devs[] = {
  140         {USB_VPI(USB_VENDOR_CHICONY2, USB_PRODUCT_CHICONY2_TEMPER, 0)},
  141 };
  142 
  143 DRIVER_MODULE(ugold, uhub, ugold_driver, NULL, NULL);
  144 MODULE_DEPEND(ugold, usb, 1, 1, 1);
  145 MODULE_DEPEND(ugold, hid, 1, 1, 1);
  146 MODULE_VERSION(ugold, 1);
  147 USB_PNP_HOST_INFO(ugold_devs);
  148 
  149 static const struct usb_config ugold_config[UGOLD_N_TRANSFER] = {
  150         [UGOLD_INTR_DT] = {
  151                 .type = UE_INTERRUPT,
  152                 .endpoint = UE_ADDR_ANY,
  153                 .direction = UE_DIR_IN,
  154                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
  155                 .bufsize = 0,           /* use wMaxPacketSize */
  156                 .callback = &ugold_intr_callback,
  157                 .if_index = 1,
  158         },
  159 };
  160 
  161 static void
  162 ugold_timeout(void *arg)
  163 {
  164         struct ugold_softc *sc = arg;
  165 
  166         usb_proc_explore_lock(sc->sc_udev);
  167         (void)usb_proc_explore_msignal(sc->sc_udev,
  168             &sc->sc_readout_msg[0], &sc->sc_readout_msg[1]);
  169         usb_proc_explore_unlock(sc->sc_udev);
  170 
  171         callout_reset(&sc->sc_callout, 6 * hz, &ugold_timeout, sc);
  172 }
  173 
  174 static int
  175 ugold_probe(device_t dev)
  176 {
  177         struct usb_attach_arg *uaa;
  178 
  179         uaa = device_get_ivars(dev);
  180         if (uaa->usb_mode != USB_MODE_HOST)
  181                 return (ENXIO);
  182         if (uaa->info.bInterfaceClass != UICLASS_HID)
  183                 return (ENXIO);
  184         if (uaa->info.bIfaceIndex != 0)
  185                 return (ENXIO);
  186 
  187         return (usbd_lookup_id_by_uaa(ugold_devs, sizeof(ugold_devs), uaa));
  188 }
  189 
  190 static int
  191 ugold_attach(device_t dev)
  192 {
  193         struct ugold_softc *sc = device_get_softc(dev);
  194         struct usb_attach_arg *uaa = device_get_ivars(dev);
  195         struct sysctl_oid *sensor_tree;
  196         uint16_t d_len;
  197         void *d_ptr;
  198         int error;
  199         int i;
  200 
  201         sc->sc_udev = uaa->device;
  202         sc->sc_readout_msg[0].hdr.pm_callback = &ugold_readout_msg;
  203         sc->sc_readout_msg[0].sc = sc;
  204         sc->sc_readout_msg[1].hdr.pm_callback = &ugold_readout_msg;
  205         sc->sc_readout_msg[1].sc = sc;
  206         sc->sc_iface_index[0] = uaa->info.bIfaceIndex;
  207         sc->sc_iface_index[1] = uaa->info.bIfaceIndex + 1;
  208 
  209         device_set_usb_desc(dev);
  210         mtx_init(&sc->sc_mtx, "ugold lock", NULL, MTX_DEF | MTX_RECURSE);
  211         callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
  212 
  213         /* grab all interfaces from other drivers */
  214         for (i = 0;; i++) {
  215                 if (i == uaa->info.bIfaceIndex)
  216                         continue;
  217                 if (usbd_get_iface(uaa->device, i) == NULL)
  218                         break;
  219 
  220                 usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
  221         }
  222 
  223         /* figure out report ID */
  224         error = usbd_req_get_hid_desc(uaa->device, NULL,
  225             &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
  226 
  227         if (error)
  228                 goto detach;
  229 
  230         (void)hid_report_size_max(d_ptr, d_len, hid_input, &sc->sc_report_id);
  231 
  232         free(d_ptr, M_TEMP);
  233 
  234         error = usbd_transfer_setup(uaa->device,
  235             sc->sc_iface_index, sc->sc_xfer, ugold_config,
  236             UGOLD_N_TRANSFER, sc, &sc->sc_mtx);
  237         if (error)
  238                 goto detach;
  239 
  240         sensor_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
  241             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensors",
  242             CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "");
  243 
  244         if (sensor_tree == NULL) {
  245                 error = ENOMEM;
  246                 goto detach;
  247         }
  248         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
  249             SYSCTL_CHILDREN(sensor_tree),
  250             OID_AUTO, "inner", CTLFLAG_RD, &sc->sc_sensor[UGOLD_INNER], 0,
  251             "Inner temperature in microCelsius");
  252 
  253         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
  254             SYSCTL_CHILDREN(sensor_tree),
  255             OID_AUTO, "inner_valid", CTLFLAG_RD, &sc->sc_valid[UGOLD_INNER], 0,
  256             "Inner temperature is valid");
  257 
  258         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
  259             SYSCTL_CHILDREN(sensor_tree),
  260             OID_AUTO, "inner_calib", CTLFLAG_RWTUN, &sc->sc_calib[UGOLD_INNER], 0,
  261             "Inner calibration temperature in microCelsius");
  262 
  263         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
  264             SYSCTL_CHILDREN(sensor_tree),
  265             OID_AUTO, "outer", CTLFLAG_RD, &sc->sc_sensor[UGOLD_OUTER], 0,
  266             "Outer temperature in microCelsius");
  267 
  268         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
  269             SYSCTL_CHILDREN(sensor_tree),
  270             OID_AUTO, "outer_calib", CTLFLAG_RWTUN, &sc->sc_calib[UGOLD_OUTER], 0,
  271             "Outer calibration temperature in microCelsius");
  272 
  273         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
  274             SYSCTL_CHILDREN(sensor_tree),
  275             OID_AUTO, "outer_valid", CTLFLAG_RD, &sc->sc_valid[UGOLD_OUTER], 0,
  276             "Outer temperature is valid");
  277 
  278         mtx_lock(&sc->sc_mtx);
  279         usbd_transfer_start(sc->sc_xfer[UGOLD_INTR_DT]);
  280         ugold_timeout(sc);
  281         mtx_unlock(&sc->sc_mtx);
  282 
  283         return (0);
  284 
  285 detach:
  286         DPRINTF("error=%s\n", usbd_errstr(error));
  287         ugold_detach(dev);
  288         return (error);
  289 }
  290 
  291 static int
  292 ugold_detach(device_t dev)
  293 {
  294         struct ugold_softc *sc = device_get_softc(dev);
  295 
  296         callout_drain(&sc->sc_callout);
  297 
  298         usb_proc_explore_lock(sc->sc_udev);
  299         usb_proc_explore_mwait(sc->sc_udev,
  300             &sc->sc_readout_msg[0], &sc->sc_readout_msg[1]);
  301         usb_proc_explore_unlock(sc->sc_udev);
  302 
  303         usbd_transfer_unsetup(sc->sc_xfer, UGOLD_N_TRANSFER);
  304 
  305         mtx_destroy(&sc->sc_mtx);
  306 
  307         return (0);
  308 }
  309 
  310 static int
  311 ugold_ds75_temp(uint8_t msb, uint8_t lsb)
  312 {
  313         /* DS75: 12bit precision mode : 0.0625 degrees Celsius ticks */
  314         /* NOTE: MSB has a sign bit for negative temperatures */
  315         int32_t temp = (msb << 24) | ((lsb & 0xF0) << 16);
  316         return (((int64_t)temp * (int64_t)1000000LL) >> 24);
  317 }
  318 
  319 static void
  320 ugold_intr_callback(struct usb_xfer *xfer, usb_error_t error)
  321 {
  322         struct ugold_softc *sc = usbd_xfer_softc(xfer);
  323         struct usb_page_cache *pc;
  324         uint8_t buf[8];
  325         int temp;
  326         int len;
  327 
  328         usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
  329 
  330         switch (USB_GET_STATE(xfer)) {
  331         case USB_ST_TRANSFERRED:
  332                 memset(buf, 0, sizeof(buf));
  333 
  334                 pc = usbd_xfer_get_frame(xfer, 0);
  335                 usbd_copy_out(pc, 0, buf, MIN(len, sizeof(buf)));
  336 
  337                 switch (buf[0]) {
  338                 case UGOLD_CMD_INIT:
  339                         if (sc->sc_num_sensors)
  340                                 break;
  341 
  342                         sc->sc_num_sensors = MIN(buf[1], UGOLD_MAX_SENSORS) /* XXX */ ;
  343 
  344                         DPRINTF("%d sensor%s type ds75/12bit (temperature)\n",
  345                             sc->sc_num_sensors, (sc->sc_num_sensors == 1) ? "" : "s");
  346                         break;
  347                 case UGOLD_CMD_DATA:
  348                         switch (buf[1]) {
  349                         case 4:
  350                                 temp = ugold_ds75_temp(buf[4], buf[5]);
  351                                 sc->sc_sensor[UGOLD_OUTER] = temp + sc->sc_calib[UGOLD_OUTER];
  352                                 sc->sc_valid[UGOLD_OUTER] = 1;
  353                                 /* FALLTHROUGH */
  354                         case 2:
  355                                 temp = ugold_ds75_temp(buf[2], buf[3]);
  356                                 sc->sc_sensor[UGOLD_INNER] = temp + sc->sc_calib[UGOLD_INNER];
  357                                 sc->sc_valid[UGOLD_INNER] = 1;
  358                                 break;
  359                         default:
  360                                 DPRINTF("invalid data length (%d bytes)\n", buf[1]);
  361                         }
  362                         break;
  363                 default:
  364                         DPRINTF("unknown command 0x%02x\n", buf[0]);
  365                         break;
  366                 }
  367                 /* FALLTHROUGH */
  368         case USB_ST_SETUP:
  369 tr_setup:
  370                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
  371                 usbd_transfer_submit(xfer);
  372                 break;
  373         default:                        /* Error */
  374                 if (error != USB_ERR_CANCELLED) {
  375                         /* try clear stall first */
  376                         usbd_xfer_set_stall(xfer);
  377                         goto tr_setup;
  378                 }
  379                 break;
  380         }
  381 }
  382 
  383 static int
  384 ugold_issue_cmd(struct ugold_softc *sc, uint8_t *cmd, int len)
  385 {
  386         return (usbd_req_set_report(sc->sc_udev, &sc->sc_mtx, cmd, len,
  387             sc->sc_iface_index[1], UHID_OUTPUT_REPORT, sc->sc_report_id));
  388 }
  389 
  390 static void
  391 ugold_readout_msg(struct usb_proc_msg *pm)
  392 {
  393         struct ugold_softc *sc = ((struct ugold_readout_msg *)pm)->sc;
  394 
  395         usb_proc_explore_unlock(sc->sc_udev);
  396 
  397         mtx_lock(&sc->sc_mtx);
  398         if (sc->sc_num_sensors == 0)
  399                 ugold_issue_cmd(sc, cmd_init, sizeof(cmd_init));
  400 
  401         ugold_issue_cmd(sc, cmd_data, sizeof(cmd_data));
  402         mtx_unlock(&sc->sc_mtx);
  403 
  404         usb_proc_explore_lock(sc->sc_udev);
  405 }

Cache object: 17774700fd3f6039ec9d78cbb5dd6788


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.