]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/usb/misc/ugold.c
if_smsc: Add the ability to disable "turbo_mode", also called RX frame batching,
[FreeBSD/FreeBSD.git] / sys / dev / usb / misc / ugold.c
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 devclass_t ugold_devclass;
126
127 static device_method_t ugold_methods[] = {
128         DEVMETHOD(device_probe, ugold_probe),
129         DEVMETHOD(device_attach, ugold_attach),
130         DEVMETHOD(device_detach, ugold_detach),
131
132         DEVMETHOD_END
133 };
134
135 static driver_t ugold_driver = {
136         .name = "ugold",
137         .methods = ugold_methods,
138         .size = sizeof(struct ugold_softc),
139 };
140
141 static const STRUCT_USB_HOST_ID ugold_devs[] = {
142         {USB_VPI(USB_VENDOR_CHICONY2, USB_PRODUCT_CHICONY2_TEMPER, 0)},
143 };
144
145 DRIVER_MODULE(ugold, uhub, ugold_driver, ugold_devclass, NULL, NULL);
146 MODULE_DEPEND(ugold, usb, 1, 1, 1);
147 MODULE_DEPEND(ugold, hid, 1, 1, 1);
148 MODULE_VERSION(ugold, 1);
149 USB_PNP_HOST_INFO(ugold_devs);
150
151 static const struct usb_config ugold_config[UGOLD_N_TRANSFER] = {
152         [UGOLD_INTR_DT] = {
153                 .type = UE_INTERRUPT,
154                 .endpoint = UE_ADDR_ANY,
155                 .direction = UE_DIR_IN,
156                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
157                 .bufsize = 0,           /* use wMaxPacketSize */
158                 .callback = &ugold_intr_callback,
159                 .if_index = 1,
160         },
161 };
162
163 static void
164 ugold_timeout(void *arg)
165 {
166         struct ugold_softc *sc = arg;
167
168         usb_proc_explore_lock(sc->sc_udev);
169         (void)usb_proc_explore_msignal(sc->sc_udev,
170             &sc->sc_readout_msg[0], &sc->sc_readout_msg[1]);
171         usb_proc_explore_unlock(sc->sc_udev);
172
173         callout_reset(&sc->sc_callout, 6 * hz, &ugold_timeout, sc);
174 }
175
176 static int
177 ugold_probe(device_t dev)
178 {
179         struct usb_attach_arg *uaa;
180
181         uaa = device_get_ivars(dev);
182         if (uaa->usb_mode != USB_MODE_HOST)
183                 return (ENXIO);
184         if (uaa->info.bInterfaceClass != UICLASS_HID)
185                 return (ENXIO);
186         if (uaa->info.bIfaceIndex != 0)
187                 return (ENXIO);
188
189         return (usbd_lookup_id_by_uaa(ugold_devs, sizeof(ugold_devs), uaa));
190 }
191
192 static int
193 ugold_attach(device_t dev)
194 {
195         struct ugold_softc *sc = device_get_softc(dev);
196         struct usb_attach_arg *uaa = device_get_ivars(dev);
197         struct sysctl_oid *sensor_tree;
198         uint16_t d_len;
199         void *d_ptr;
200         int error;
201         int i;
202
203         sc->sc_udev = uaa->device;
204         sc->sc_readout_msg[0].hdr.pm_callback = &ugold_readout_msg;
205         sc->sc_readout_msg[0].sc = sc;
206         sc->sc_readout_msg[1].hdr.pm_callback = &ugold_readout_msg;
207         sc->sc_readout_msg[1].sc = sc;
208         sc->sc_iface_index[0] = uaa->info.bIfaceIndex;
209         sc->sc_iface_index[1] = uaa->info.bIfaceIndex + 1;
210
211         device_set_usb_desc(dev);
212         mtx_init(&sc->sc_mtx, "ugold lock", NULL, MTX_DEF | MTX_RECURSE);
213         callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
214
215         /* grab all interfaces from other drivers */
216         for (i = 0;; i++) {
217                 if (i == uaa->info.bIfaceIndex)
218                         continue;
219                 if (usbd_get_iface(uaa->device, i) == NULL)
220                         break;
221
222                 usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
223         }
224
225         /* figure out report ID */
226         error = usbd_req_get_hid_desc(uaa->device, NULL,
227             &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
228
229         if (error)
230                 goto detach;
231
232         (void)hid_report_size_max(d_ptr, d_len, hid_input, &sc->sc_report_id);
233
234         free(d_ptr, M_TEMP);
235
236         error = usbd_transfer_setup(uaa->device,
237             sc->sc_iface_index, sc->sc_xfer, ugold_config,
238             UGOLD_N_TRANSFER, sc, &sc->sc_mtx);
239         if (error)
240                 goto detach;
241
242         sensor_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
243             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensors",
244             CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "");
245
246         if (sensor_tree == NULL) {
247                 error = ENOMEM;
248                 goto detach;
249         }
250         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
251             SYSCTL_CHILDREN(sensor_tree),
252             OID_AUTO, "inner", CTLFLAG_RD, &sc->sc_sensor[UGOLD_INNER], 0,
253             "Inner temperature in microCelcius");
254
255         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
256             SYSCTL_CHILDREN(sensor_tree),
257             OID_AUTO, "inner_valid", CTLFLAG_RD, &sc->sc_valid[UGOLD_INNER], 0,
258             "Inner temperature is valid");
259
260         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
261             SYSCTL_CHILDREN(sensor_tree),
262             OID_AUTO, "inner_calib", CTLFLAG_RWTUN, &sc->sc_calib[UGOLD_INNER], 0,
263             "Inner calibration temperature in microCelcius");
264
265         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
266             SYSCTL_CHILDREN(sensor_tree),
267             OID_AUTO, "outer", CTLFLAG_RD, &sc->sc_sensor[UGOLD_OUTER], 0,
268             "Outer temperature in microCelcius");
269
270         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
271             SYSCTL_CHILDREN(sensor_tree),
272             OID_AUTO, "outer_calib", CTLFLAG_RWTUN, &sc->sc_calib[UGOLD_OUTER], 0,
273             "Outer calibration temperature in microCelcius");
274
275         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
276             SYSCTL_CHILDREN(sensor_tree),
277             OID_AUTO, "outer_valid", CTLFLAG_RD, &sc->sc_valid[UGOLD_OUTER], 0,
278             "Outer temperature is valid");
279
280         mtx_lock(&sc->sc_mtx);
281         usbd_transfer_start(sc->sc_xfer[UGOLD_INTR_DT]);
282         ugold_timeout(sc);
283         mtx_unlock(&sc->sc_mtx);
284
285         return (0);
286
287 detach:
288         DPRINTF("error=%s\n", usbd_errstr(error));
289         ugold_detach(dev);
290         return (error);
291 }
292
293 static int
294 ugold_detach(device_t dev)
295 {
296         struct ugold_softc *sc = device_get_softc(dev);
297
298         callout_drain(&sc->sc_callout);
299
300         usb_proc_explore_lock(sc->sc_udev);
301         usb_proc_explore_mwait(sc->sc_udev,
302             &sc->sc_readout_msg[0], &sc->sc_readout_msg[1]);
303         usb_proc_explore_unlock(sc->sc_udev);
304
305         usbd_transfer_unsetup(sc->sc_xfer, UGOLD_N_TRANSFER);
306
307         mtx_destroy(&sc->sc_mtx);
308
309         return (0);
310 }
311
312 static int
313 ugold_ds75_temp(uint8_t msb, uint8_t lsb)
314 {
315         /* DS75: 12bit precision mode : 0.0625 degrees Celsius ticks */
316         /* NOTE: MSB has a sign bit for negative temperatures */
317         int32_t temp = (msb << 24) | ((lsb & 0xF0) << 16);
318         return (((int64_t)temp * (int64_t)1000000LL) >> 24);
319 }
320
321 static void
322 ugold_intr_callback(struct usb_xfer *xfer, usb_error_t error)
323 {
324         struct ugold_softc *sc = usbd_xfer_softc(xfer);
325         struct usb_page_cache *pc;
326         uint8_t buf[8];
327         int temp;
328         int len;
329
330         usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
331
332         switch (USB_GET_STATE(xfer)) {
333         case USB_ST_TRANSFERRED:
334                 memset(buf, 0, sizeof(buf));
335
336                 pc = usbd_xfer_get_frame(xfer, 0);
337                 usbd_copy_out(pc, 0, buf, MIN(len, sizeof(buf)));
338
339                 switch (buf[0]) {
340                 case UGOLD_CMD_INIT:
341                         if (sc->sc_num_sensors)
342                                 break;
343
344                         sc->sc_num_sensors = MIN(buf[1], UGOLD_MAX_SENSORS) /* XXX */ ;
345
346                         DPRINTF("%d sensor%s type ds75/12bit (temperature)\n",
347                             sc->sc_num_sensors, (sc->sc_num_sensors == 1) ? "" : "s");
348                         break;
349                 case UGOLD_CMD_DATA:
350                         switch (buf[1]) {
351                         case 4:
352                                 temp = ugold_ds75_temp(buf[4], buf[5]);
353                                 sc->sc_sensor[UGOLD_OUTER] = temp + sc->sc_calib[UGOLD_OUTER];
354                                 sc->sc_valid[UGOLD_OUTER] = 1;
355                                 /* FALLTHROUGH */
356                         case 2:
357                                 temp = ugold_ds75_temp(buf[2], buf[3]);
358                                 sc->sc_sensor[UGOLD_INNER] = temp + sc->sc_calib[UGOLD_INNER];
359                                 sc->sc_valid[UGOLD_INNER] = 1;
360                                 break;
361                         default:
362                                 DPRINTF("invalid data length (%d bytes)\n", buf[1]);
363                         }
364                         break;
365                 default:
366                         DPRINTF("unknown command 0x%02x\n", buf[0]);
367                         break;
368                 }
369                 /* FALLTHROUGH */
370         case USB_ST_SETUP:
371 tr_setup:
372                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
373                 usbd_transfer_submit(xfer);
374                 break;
375         default:                        /* Error */
376                 if (error != USB_ERR_CANCELLED) {
377                         /* try clear stall first */
378                         usbd_xfer_set_stall(xfer);
379                         goto tr_setup;
380                 }
381                 break;
382         }
383 }
384
385 static int
386 ugold_issue_cmd(struct ugold_softc *sc, uint8_t *cmd, int len)
387 {
388         return (usbd_req_set_report(sc->sc_udev, &sc->sc_mtx, cmd, len,
389             sc->sc_iface_index[1], UHID_OUTPUT_REPORT, sc->sc_report_id));
390 }
391
392 static void
393 ugold_readout_msg(struct usb_proc_msg *pm)
394 {
395         struct ugold_softc *sc = ((struct ugold_readout_msg *)pm)->sc;
396
397         usb_proc_explore_unlock(sc->sc_udev);
398
399         mtx_lock(&sc->sc_mtx);
400         if (sc->sc_num_sensors == 0)
401                 ugold_issue_cmd(sc, cmd_init, sizeof(cmd_init));
402
403         ugold_issue_cmd(sc, cmd_data, sizeof(cmd_data));
404         mtx_unlock(&sc->sc_mtx);
405
406         usb_proc_explore_lock(sc->sc_udev);
407 }