FreeBSD kernel usb device Code
ugold.c
Go to the documentation of this file.
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
64enum {
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 */
85static uint8_t cmd_data[8] = {0x01, 0x80, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00};
86static uint8_t cmd_init[8] = {0x01, 0x82, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00};
87
88#if 0
89static uint8_t cmd_type[8] = {0x01, 0x86, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00};
90
91#endif
92
93struct ugold_softc;
96 struct ugold_softc *sc;
97};
98
102
103 struct callout sc_callout;
104 struct mtx sc_mtx;
106
112 uint8_t sc_iface_index[2];
113};
114
115/* prototypes */
116
117static device_probe_t ugold_probe;
118static device_attach_t ugold_attach;
119static device_detach_t ugold_detach;
120
122
124
125static devclass_t ugold_devclass;
126
127static 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
135static driver_t ugold_driver = {
136 .name = "ugold",
137 .methods = ugold_methods,
138 .size = sizeof(struct ugold_softc),
139};
140
142 {USB_VPI(USB_VENDOR_CHICONY2, USB_PRODUCT_CHICONY2_TEMPER, 0)},
143};
144
145DRIVER_MODULE(ugold, uhub, ugold_driver, ugold_devclass, NULL, NULL);
146MODULE_DEPEND(ugold, usb, 1, 1, 1);
147MODULE_DEPEND(ugold, hid, 1, 1, 1);
150
152 [UGOLD_INTR_DT] = {
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
163static void
165{
166 struct ugold_softc *sc = arg;
167
170 &sc->sc_readout_msg[0], &sc->sc_readout_msg[1]);
172
173 callout_reset(&sc->sc_callout, 6 * hz, &ugold_timeout, sc);
174}
175
176static int
177ugold_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
192static int
193ugold_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;
205 sc->sc_readout_msg[0].sc = sc;
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
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
223 }
224
225 /* figure out report ID */
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
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 microCelsius");
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 microCelsius");
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 microCelsius");
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 microCelsius");
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);
282 ugold_timeout(sc);
283 mtx_unlock(&sc->sc_mtx);
284
285 return (0);
286
287detach:
288 DPRINTF("error=%s\n", usbd_errstr(error));
290 return (error);
291}
292
293static int
294ugold_detach(device_t dev)
295{
296 struct ugold_softc *sc = device_get_softc(dev);
297
298 callout_drain(&sc->sc_callout);
299
302 &sc->sc_readout_msg[0], &sc->sc_readout_msg[1]);
304
306
307 mtx_destroy(&sc->sc_mtx);
308
309 return (0);
310}
311
312static int
313ugold_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
321static void
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)) {
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:
371tr_setup:
374 break;
375 default: /* Error */
376 if (error != USB_ERR_CANCELLED) {
377 /* try clear stall first */
379 goto tr_setup;
380 }
381 break;
382 }
383}
384
385static int
386ugold_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,
390}
391
392static void
394{
395 struct ugold_softc *sc = ((struct ugold_readout_msg *)pm)->sc;
396
398
399 mtx_lock(&sc->sc_mtx);
400 if (sc->sc_num_sensors == 0)
402
404 mtx_unlock(&sc->sc_mtx);
405
407}
uint16_t len
Definition: ehci.h:41
struct @109 error
device_t dev
struct ugold_softc * sc
Definition: ugold.c:96
struct usb_proc_msg hdr
Definition: ugold.c:95
struct ugold_readout_msg sc_readout_msg[2]
Definition: ugold.c:105
int sc_valid[UGOLD_MAX_SENSORS]
Definition: ugold.c:110
struct usb_xfer * sc_xfer[UGOLD_N_TRANSFER]
Definition: ugold.c:101
struct mtx sc_mtx
Definition: ugold.c:104
struct usb_device * sc_udev
Definition: ugold.c:100
uint8_t sc_iface_index[2]
Definition: ugold.c:112
struct callout sc_callout
Definition: ugold.c:103
uint8_t sc_report_id
Definition: ugold.c:111
int sc_calib[UGOLD_MAX_SENSORS]
Definition: ugold.c:109
int sc_sensor[UGOLD_MAX_SENSORS]
Definition: ugold.c:108
int sc_num_sensors
Definition: ugold.c:107
enum usb_hc_mode usb_mode
Definition: usbdi.h:432
struct usbd_lookup_info info
Definition: usbdi.h:426
struct usb_device * device
Definition: usbdi.h:430
uint8_t type
Definition: usbdi.h:238
usb_proc_callback_t * pm_callback
Definition: usbdi.h:522
uint8_t bIfaceIndex
Definition: usbdi.h:417
uint8_t bInterfaceClass
Definition: usbdi.h:414
static int ugold_issue_cmd(struct ugold_softc *sc, uint8_t *cmd, int len)
Definition: ugold.c:386
static usb_proc_callback_t ugold_readout_msg
Definition: ugold.c:121
static device_detach_t ugold_detach
Definition: ugold.c:119
static device_attach_t ugold_attach
Definition: ugold.c:118
MODULE_VERSION(ugold, 1)
static driver_t ugold_driver
Definition: ugold.c:135
#define UGOLD_INNER
Definition: ugold.c:57
USB_PNP_HOST_INFO(ugold_devs)
#define UGOLD_CMD_DATA
Definition: ugold.c:61
static uint8_t cmd_data[8]
Definition: ugold.c:85
static const struct usb_config ugold_config[UGOLD_N_TRANSFER]
Definition: ugold.c:151
#define UGOLD_CMD_INIT
Definition: ugold.c:62
__FBSDID("$FreeBSD$")
static usb_callback_t ugold_intr_callback
Definition: ugold.c:123
@ UGOLD_N_TRANSFER
Definition: ugold.c:66
@ UGOLD_INTR_DT
Definition: ugold.c:65
static device_probe_t ugold_probe
Definition: ugold.c:117
static devclass_t ugold_devclass
Definition: ugold.c:125
MODULE_DEPEND(ugold, usb, 1, 1, 1)
static const STRUCT_USB_HOST_ID ugold_devs[]
Definition: ugold.c:141
static void ugold_timeout(void *arg)
Definition: ugold.c:164
static uint8_t cmd_init[8]
Definition: ugold.c:86
#define UGOLD_OUTER
Definition: ugold.c:58
DRIVER_MODULE(ugold, uhub, ugold_driver, ugold_devclass, NULL, NULL)
static int ugold_ds75_temp(uint8_t msb, uint8_t lsb)
Definition: ugold.c:313
static device_method_t ugold_methods[]
Definition: ugold.c:127
#define UGOLD_MAX_SENSORS
Definition: ugold.c:59
#define DPRINTF(...)
Definition: umass.c:179
#define UE_INTERRUPT
Definition: usb.h:544
#define UE_ADDR_ANY
Definition: usb.h:537
#define UE_DIR_IN
Definition: usb.h:531
#define UICLASS_HID
Definition: usb.h:453
@ USB_MODE_HOST
Definition: usb.h:778
void usbd_copy_out(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len)
Definition: usb_busdma.c:283
void * usb_proc_explore_msignal(struct usb_device *udev, void *pm1, void *pm2)
void usb_proc_explore_mwait(struct usb_device *udev, void *pm1, void *pm2)
void usb_proc_explore_unlock(struct usb_device *udev)
void usb_proc_explore_lock(struct usb_device *udev)
void usbd_set_parent_iface(struct usb_device *udev, uint8_t iface_index, uint8_t parent_index)
Definition: usb_device.c:1395
struct usb_interface * usbd_get_iface(struct usb_device *udev, uint8_t iface_index)
Definition: usb_device.c:2370
const char * usbd_errstr(usb_error_t err)
Definition: usb_error.c:93
usb_error_t usbd_req_get_hid_desc(struct usb_device *udev, struct mtx *mtx, void **descp, uint16_t *sizep, struct malloc_type *mem, uint8_t iface_index)
Definition: usb_hid.c:113
INTERFACE usb
Definition: usb_if.m:35
int usbd_lookup_id_by_uaa(const struct usb_device_id *id, usb_size_t sizeof_id, struct usb_attach_arg *uaa)
Definition: usb_lookup.c:143
usb_error_t usbd_req_set_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id)
Definition: usb_request.c:1806
void usbd_transfer_submit(struct usb_xfer *xfer)
void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup)
void usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex, usb_frlength_t len)
struct usb_page_cache * usbd_xfer_get_frame(struct usb_xfer *xfer, usb_frcount_t frindex)
usb_error_t usbd_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **ppxfer, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *xfer_mtx)
Definition: usb_transfer.c:987
void usbd_transfer_start(struct usb_xfer *xfer)
void * usbd_xfer_softc(struct usb_xfer *xfer)
void usbd_xfer_set_stall(struct usb_xfer *xfer)
void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes)
usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer)
void device_set_usb_desc(device_t dev)
Definition: usb_util.c:73
void() usb_proc_callback_t(struct usb_proc_msg *)
Definition: usbdi.h:95
#define USB_ST_SETUP
Definition: usbdi.h:502
usb_error_t
Definition: usbdi.h:45
@ USB_ERR_CANCELLED
Definition: usbdi.h:51
#define USB_ST_TRANSFERRED
Definition: usbdi.h:503
void() usb_callback_t(struct usb_xfer *, usb_error_t)
Definition: usbdi.h:94
#define USB_VPI(vend, prod, info)
Definition: usbdi.h:367
#define STRUCT_USB_HOST_ID
Definition: usbdi.h:258
#define USB_GET_STATE(xfer)
Definition: usbdi.h:515
#define UHID_OUTPUT_REPORT
Definition: usbhid.h:67