xref: /freebsd/sys/dev/hid/u2f.c (revision 4a04e0a6c703db9d2d9e6a0ef2b000644143b705)
1*4a04e0a6SVladimir Kondratyev /*-
2*4a04e0a6SVladimir Kondratyev  * SPDX-License-Identifier: BSD-2-Clause
3*4a04e0a6SVladimir Kondratyev  *
4*4a04e0a6SVladimir Kondratyev  * Copyright (c) 2022-2023 Vladimir Kondratyev <wulf@FreeBSD.org>
5*4a04e0a6SVladimir Kondratyev  *
6*4a04e0a6SVladimir Kondratyev  * Redistribution and use in source and binary forms, with or without
7*4a04e0a6SVladimir Kondratyev  * modification, are permitted provided that the following conditions
8*4a04e0a6SVladimir Kondratyev  * are met:
9*4a04e0a6SVladimir Kondratyev  * 1. Redistributions of source code must retain the above copyright
10*4a04e0a6SVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer.
11*4a04e0a6SVladimir Kondratyev  * 2. Redistributions in binary form must reproduce the above copyright
12*4a04e0a6SVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer in the
13*4a04e0a6SVladimir Kondratyev  *    documentation and/or other materials provided with the distribution.
14*4a04e0a6SVladimir Kondratyev  *
15*4a04e0a6SVladimir Kondratyev  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*4a04e0a6SVladimir Kondratyev  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*4a04e0a6SVladimir Kondratyev  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*4a04e0a6SVladimir Kondratyev  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19*4a04e0a6SVladimir Kondratyev  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*4a04e0a6SVladimir Kondratyev  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*4a04e0a6SVladimir Kondratyev  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*4a04e0a6SVladimir Kondratyev  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*4a04e0a6SVladimir Kondratyev  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*4a04e0a6SVladimir Kondratyev  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*4a04e0a6SVladimir Kondratyev  * SUCH DAMAGE.
26*4a04e0a6SVladimir Kondratyev  */
27*4a04e0a6SVladimir Kondratyev 
28*4a04e0a6SVladimir Kondratyev #include "opt_hid.h"
29*4a04e0a6SVladimir Kondratyev 
30*4a04e0a6SVladimir Kondratyev #include <sys/param.h>
31*4a04e0a6SVladimir Kondratyev #ifdef COMPAT_FREEBSD32
32*4a04e0a6SVladimir Kondratyev #include <sys/abi_compat.h>
33*4a04e0a6SVladimir Kondratyev #endif
34*4a04e0a6SVladimir Kondratyev #include <sys/bus.h>
35*4a04e0a6SVladimir Kondratyev #include <sys/conf.h>
36*4a04e0a6SVladimir Kondratyev #include <sys/fcntl.h>
37*4a04e0a6SVladimir Kondratyev #include <sys/filio.h>
38*4a04e0a6SVladimir Kondratyev #include <sys/ioccom.h>
39*4a04e0a6SVladimir Kondratyev #include <sys/kernel.h>
40*4a04e0a6SVladimir Kondratyev #include <sys/lock.h>
41*4a04e0a6SVladimir Kondratyev #include <sys/malloc.h>
42*4a04e0a6SVladimir Kondratyev #include <sys/module.h>
43*4a04e0a6SVladimir Kondratyev #include <sys/mutex.h>
44*4a04e0a6SVladimir Kondratyev #include <sys/poll.h>
45*4a04e0a6SVladimir Kondratyev #include <sys/priv.h>
46*4a04e0a6SVladimir Kondratyev #include <sys/proc.h>
47*4a04e0a6SVladimir Kondratyev #include <sys/selinfo.h>
48*4a04e0a6SVladimir Kondratyev #include <sys/sysctl.h>
49*4a04e0a6SVladimir Kondratyev #include <sys/systm.h>
50*4a04e0a6SVladimir Kondratyev #include <sys/uio.h>
51*4a04e0a6SVladimir Kondratyev 
52*4a04e0a6SVladimir Kondratyev #include <dev/evdev/input.h>
53*4a04e0a6SVladimir Kondratyev 
54*4a04e0a6SVladimir Kondratyev #define HID_DEBUG_VAR	u2f_debug
55*4a04e0a6SVladimir Kondratyev #include <dev/hid/hid.h>
56*4a04e0a6SVladimir Kondratyev #include <dev/hid/hidbus.h>
57*4a04e0a6SVladimir Kondratyev #include <dev/hid/hidquirk.h>
58*4a04e0a6SVladimir Kondratyev 
59*4a04e0a6SVladimir Kondratyev #include <dev/usb/usb_ioctl.h>
60*4a04e0a6SVladimir Kondratyev 
61*4a04e0a6SVladimir Kondratyev #ifdef HID_DEBUG
62*4a04e0a6SVladimir Kondratyev static int u2f_debug = 0;
63*4a04e0a6SVladimir Kondratyev static SYSCTL_NODE(_hw_hid, OID_AUTO, u2f, CTLFLAG_RW, 0,
64*4a04e0a6SVladimir Kondratyev     "FIDO/U2F authenticator");
65*4a04e0a6SVladimir Kondratyev SYSCTL_INT(_hw_hid_u2f, OID_AUTO, debug, CTLFLAG_RWTUN,
66*4a04e0a6SVladimir Kondratyev     &u2f_debug, 0, "Debug level");
67*4a04e0a6SVladimir Kondratyev #endif
68*4a04e0a6SVladimir Kondratyev 
69*4a04e0a6SVladimir Kondratyev #define	U2F_MAX_REPORT_SIZE	64
70*4a04e0a6SVladimir Kondratyev 
71*4a04e0a6SVladimir Kondratyev /* A match on these entries will load u2f */
72*4a04e0a6SVladimir Kondratyev static const struct hid_device_id u2f_devs[] = {
73*4a04e0a6SVladimir Kondratyev 	{ HID_BUS(BUS_USB), HID_TLC(HUP_FIDO, HUF_U2FHID) },
74*4a04e0a6SVladimir Kondratyev };
75*4a04e0a6SVladimir Kondratyev 
76*4a04e0a6SVladimir Kondratyev struct u2f_softc {
77*4a04e0a6SVladimir Kondratyev 	device_t sc_dev;		/* base device */
78*4a04e0a6SVladimir Kondratyev 	struct cdev *dev;
79*4a04e0a6SVladimir Kondratyev 
80*4a04e0a6SVladimir Kondratyev 	struct mtx sc_mtx;		/* hidbus private mutex */
81*4a04e0a6SVladimir Kondratyev 	void *sc_rdesc;
82*4a04e0a6SVladimir Kondratyev 	hid_size_t sc_rdesc_size;
83*4a04e0a6SVladimir Kondratyev 	hid_size_t sc_isize;
84*4a04e0a6SVladimir Kondratyev 	hid_size_t sc_osize;
85*4a04e0a6SVladimir Kondratyev 	struct selinfo sc_rsel;
86*4a04e0a6SVladimir Kondratyev 	struct {			/* driver state */
87*4a04e0a6SVladimir Kondratyev 		bool	open:1;		/* device is open */
88*4a04e0a6SVladimir Kondratyev 		bool	aslp:1;		/* waiting for device data in read() */
89*4a04e0a6SVladimir Kondratyev 		bool	sel:1;		/* waiting for device data in poll() */
90*4a04e0a6SVladimir Kondratyev 		bool	data:1;		/* input report is stored in sc_buf */
91*4a04e0a6SVladimir Kondratyev 		int	reserved:28;
92*4a04e0a6SVladimir Kondratyev 	} sc_state;
93*4a04e0a6SVladimir Kondratyev 	int sc_fflags;			/* access mode for open lifetime */
94*4a04e0a6SVladimir Kondratyev 
95*4a04e0a6SVladimir Kondratyev 	uint8_t sc_buf[U2F_MAX_REPORT_SIZE];
96*4a04e0a6SVladimir Kondratyev };
97*4a04e0a6SVladimir Kondratyev 
98*4a04e0a6SVladimir Kondratyev static d_open_t		u2f_open;
99*4a04e0a6SVladimir Kondratyev static d_read_t		u2f_read;
100*4a04e0a6SVladimir Kondratyev static d_write_t	u2f_write;
101*4a04e0a6SVladimir Kondratyev static d_ioctl_t	u2f_ioctl;
102*4a04e0a6SVladimir Kondratyev static d_poll_t		u2f_poll;
103*4a04e0a6SVladimir Kondratyev static d_kqfilter_t	u2f_kqfilter;
104*4a04e0a6SVladimir Kondratyev 
105*4a04e0a6SVladimir Kondratyev static d_priv_dtor_t	u2f_dtor;
106*4a04e0a6SVladimir Kondratyev 
107*4a04e0a6SVladimir Kondratyev static struct cdevsw u2f_cdevsw = {
108*4a04e0a6SVladimir Kondratyev 	.d_version =	D_VERSION,
109*4a04e0a6SVladimir Kondratyev 	.d_open =	u2f_open,
110*4a04e0a6SVladimir Kondratyev 	.d_read =	u2f_read,
111*4a04e0a6SVladimir Kondratyev 	.d_write =	u2f_write,
112*4a04e0a6SVladimir Kondratyev 	.d_ioctl =	u2f_ioctl,
113*4a04e0a6SVladimir Kondratyev 	.d_poll =	u2f_poll,
114*4a04e0a6SVladimir Kondratyev 	.d_kqfilter =	u2f_kqfilter,
115*4a04e0a6SVladimir Kondratyev 	.d_name =	"u2f",
116*4a04e0a6SVladimir Kondratyev };
117*4a04e0a6SVladimir Kondratyev 
118*4a04e0a6SVladimir Kondratyev static hid_intr_t	u2f_intr;
119*4a04e0a6SVladimir Kondratyev 
120*4a04e0a6SVladimir Kondratyev static device_probe_t	u2f_probe;
121*4a04e0a6SVladimir Kondratyev static device_attach_t	u2f_attach;
122*4a04e0a6SVladimir Kondratyev static device_detach_t	u2f_detach;
123*4a04e0a6SVladimir Kondratyev 
124*4a04e0a6SVladimir Kondratyev static int		u2f_kqread(struct knote *, long);
125*4a04e0a6SVladimir Kondratyev static void		u2f_kqdetach(struct knote *);
126*4a04e0a6SVladimir Kondratyev static void		u2f_notify(struct u2f_softc *);
127*4a04e0a6SVladimir Kondratyev 
128*4a04e0a6SVladimir Kondratyev static struct filterops u2f_filterops_read = {
129*4a04e0a6SVladimir Kondratyev 	.f_isfd =	1,
130*4a04e0a6SVladimir Kondratyev 	.f_detach =	u2f_kqdetach,
131*4a04e0a6SVladimir Kondratyev 	.f_event =	u2f_kqread,
132*4a04e0a6SVladimir Kondratyev };
133*4a04e0a6SVladimir Kondratyev 
134*4a04e0a6SVladimir Kondratyev static int
u2f_probe(device_t dev)135*4a04e0a6SVladimir Kondratyev u2f_probe(device_t dev)
136*4a04e0a6SVladimir Kondratyev {
137*4a04e0a6SVladimir Kondratyev 	int error;
138*4a04e0a6SVladimir Kondratyev 
139*4a04e0a6SVladimir Kondratyev 	error = HIDBUS_LOOKUP_DRIVER_INFO(dev, u2f_devs);
140*4a04e0a6SVladimir Kondratyev 	if (error != 0)
141*4a04e0a6SVladimir Kondratyev                 return (error);
142*4a04e0a6SVladimir Kondratyev 
143*4a04e0a6SVladimir Kondratyev 	hidbus_set_desc(dev, "Authenticator");
144*4a04e0a6SVladimir Kondratyev 
145*4a04e0a6SVladimir Kondratyev 	return (BUS_PROBE_GENERIC);
146*4a04e0a6SVladimir Kondratyev }
147*4a04e0a6SVladimir Kondratyev 
148*4a04e0a6SVladimir Kondratyev static int
u2f_attach(device_t dev)149*4a04e0a6SVladimir Kondratyev u2f_attach(device_t dev)
150*4a04e0a6SVladimir Kondratyev {
151*4a04e0a6SVladimir Kondratyev 	struct u2f_softc *sc = device_get_softc(dev);
152*4a04e0a6SVladimir Kondratyev 	struct hid_device_info *hw = __DECONST(struct hid_device_info *,
153*4a04e0a6SVladimir Kondratyev 	    hid_get_device_info(dev));
154*4a04e0a6SVladimir Kondratyev 	struct make_dev_args mda;
155*4a04e0a6SVladimir Kondratyev 	int error;
156*4a04e0a6SVladimir Kondratyev 
157*4a04e0a6SVladimir Kondratyev 	sc->sc_dev = dev;
158*4a04e0a6SVladimir Kondratyev 
159*4a04e0a6SVladimir Kondratyev 	error = hid_get_report_descr(dev, &sc->sc_rdesc, &sc->sc_rdesc_size);
160*4a04e0a6SVladimir Kondratyev 	if (error != 0)
161*4a04e0a6SVladimir Kondratyev 		return (ENXIO);
162*4a04e0a6SVladimir Kondratyev 	sc->sc_isize = hid_report_size_max(sc->sc_rdesc, sc->sc_rdesc_size,
163*4a04e0a6SVladimir Kondratyev 	    hid_input, NULL);
164*4a04e0a6SVladimir Kondratyev 	if (sc->sc_isize > U2F_MAX_REPORT_SIZE) {
165*4a04e0a6SVladimir Kondratyev 		device_printf(dev, "Input report size too large. Truncate.\n");
166*4a04e0a6SVladimir Kondratyev 		sc->sc_isize = U2F_MAX_REPORT_SIZE;
167*4a04e0a6SVladimir Kondratyev 	}
168*4a04e0a6SVladimir Kondratyev 	sc->sc_osize = hid_report_size_max(sc->sc_rdesc, sc->sc_rdesc_size,
169*4a04e0a6SVladimir Kondratyev 	    hid_output, NULL);
170*4a04e0a6SVladimir Kondratyev 	if (sc->sc_osize > U2F_MAX_REPORT_SIZE) {
171*4a04e0a6SVladimir Kondratyev 		device_printf(dev, "Output report size too large. Truncate.\n");
172*4a04e0a6SVladimir Kondratyev 		sc->sc_osize = U2F_MAX_REPORT_SIZE;
173*4a04e0a6SVladimir Kondratyev 	}
174*4a04e0a6SVladimir Kondratyev 
175*4a04e0a6SVladimir Kondratyev 	mtx_init(&sc->sc_mtx, "u2f lock", NULL, MTX_DEF);
176*4a04e0a6SVladimir Kondratyev 	knlist_init_mtx(&sc->sc_rsel.si_note, &sc->sc_mtx);
177*4a04e0a6SVladimir Kondratyev 
178*4a04e0a6SVladimir Kondratyev 	make_dev_args_init(&mda);
179*4a04e0a6SVladimir Kondratyev 	mda.mda_flags = MAKEDEV_WAITOK;
180*4a04e0a6SVladimir Kondratyev 	mda.mda_devsw = &u2f_cdevsw;
181*4a04e0a6SVladimir Kondratyev 	mda.mda_uid = UID_ROOT;
182*4a04e0a6SVladimir Kondratyev 	mda.mda_gid = GID_U2F;
183*4a04e0a6SVladimir Kondratyev 	mda.mda_mode = 0660;
184*4a04e0a6SVladimir Kondratyev 	mda.mda_si_drv1 = sc;
185*4a04e0a6SVladimir Kondratyev 
186*4a04e0a6SVladimir Kondratyev 	error = make_dev_s(&mda, &sc->dev, "u2f/%d", device_get_unit(dev));
187*4a04e0a6SVladimir Kondratyev 	if (error) {
188*4a04e0a6SVladimir Kondratyev 		device_printf(dev, "Can not create character device\n");
189*4a04e0a6SVladimir Kondratyev 		u2f_detach(dev);
190*4a04e0a6SVladimir Kondratyev 		return (error);
191*4a04e0a6SVladimir Kondratyev 	}
192*4a04e0a6SVladimir Kondratyev #ifdef U2F_MAKE_UHID_ALIAS
193*4a04e0a6SVladimir Kondratyev 	(void)make_dev_alias(sc->dev, "uhid%d", device_get_unit(dev));
194*4a04e0a6SVladimir Kondratyev #endif
195*4a04e0a6SVladimir Kondratyev 
196*4a04e0a6SVladimir Kondratyev 	hid_add_dynamic_quirk(hw, HQ_NO_READAHEAD);
197*4a04e0a6SVladimir Kondratyev 
198*4a04e0a6SVladimir Kondratyev 	hidbus_set_lock(dev, &sc->sc_mtx);
199*4a04e0a6SVladimir Kondratyev 	hidbus_set_intr(dev, u2f_intr, sc);
200*4a04e0a6SVladimir Kondratyev 
201*4a04e0a6SVladimir Kondratyev 	return (0);
202*4a04e0a6SVladimir Kondratyev }
203*4a04e0a6SVladimir Kondratyev 
204*4a04e0a6SVladimir Kondratyev static int
u2f_detach(device_t dev)205*4a04e0a6SVladimir Kondratyev u2f_detach(device_t dev)
206*4a04e0a6SVladimir Kondratyev {
207*4a04e0a6SVladimir Kondratyev 	struct u2f_softc *sc = device_get_softc(dev);
208*4a04e0a6SVladimir Kondratyev 
209*4a04e0a6SVladimir Kondratyev 	DPRINTF("sc=%p\n", sc);
210*4a04e0a6SVladimir Kondratyev 
211*4a04e0a6SVladimir Kondratyev 	if (sc->dev != NULL) {
212*4a04e0a6SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
213*4a04e0a6SVladimir Kondratyev 		sc->dev->si_drv1 = NULL;
214*4a04e0a6SVladimir Kondratyev 		/* Wake everyone */
215*4a04e0a6SVladimir Kondratyev 		u2f_notify(sc);
216*4a04e0a6SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
217*4a04e0a6SVladimir Kondratyev 		destroy_dev(sc->dev);
218*4a04e0a6SVladimir Kondratyev 	}
219*4a04e0a6SVladimir Kondratyev 
220*4a04e0a6SVladimir Kondratyev 	hid_intr_stop(sc->sc_dev);
221*4a04e0a6SVladimir Kondratyev 
222*4a04e0a6SVladimir Kondratyev 	knlist_clear(&sc->sc_rsel.si_note, 0);
223*4a04e0a6SVladimir Kondratyev 	knlist_destroy(&sc->sc_rsel.si_note);
224*4a04e0a6SVladimir Kondratyev 	seldrain(&sc->sc_rsel);
225*4a04e0a6SVladimir Kondratyev 	mtx_destroy(&sc->sc_mtx);
226*4a04e0a6SVladimir Kondratyev 
227*4a04e0a6SVladimir Kondratyev 	return (0);
228*4a04e0a6SVladimir Kondratyev }
229*4a04e0a6SVladimir Kondratyev 
230*4a04e0a6SVladimir Kondratyev void
u2f_intr(void * context,void * buf,hid_size_t len)231*4a04e0a6SVladimir Kondratyev u2f_intr(void *context, void *buf, hid_size_t len)
232*4a04e0a6SVladimir Kondratyev {
233*4a04e0a6SVladimir Kondratyev 	struct u2f_softc *sc = context;
234*4a04e0a6SVladimir Kondratyev 
235*4a04e0a6SVladimir Kondratyev 	mtx_assert(&sc->sc_mtx, MA_OWNED);
236*4a04e0a6SVladimir Kondratyev 
237*4a04e0a6SVladimir Kondratyev 	DPRINTFN(5, "len=%d\n", len);
238*4a04e0a6SVladimir Kondratyev 	DPRINTFN(5, "data = %*D\n", len, buf, " ");
239*4a04e0a6SVladimir Kondratyev 
240*4a04e0a6SVladimir Kondratyev 	if (sc->sc_state.data)
241*4a04e0a6SVladimir Kondratyev 		return;
242*4a04e0a6SVladimir Kondratyev 
243*4a04e0a6SVladimir Kondratyev 	if (len > sc->sc_isize)
244*4a04e0a6SVladimir Kondratyev 		len = sc->sc_isize;
245*4a04e0a6SVladimir Kondratyev 
246*4a04e0a6SVladimir Kondratyev 	bcopy(buf, sc->sc_buf, len);
247*4a04e0a6SVladimir Kondratyev 
248*4a04e0a6SVladimir Kondratyev 	/* Make sure we don't process old data */
249*4a04e0a6SVladimir Kondratyev 	if (len < sc->sc_isize)
250*4a04e0a6SVladimir Kondratyev 		bzero(sc->sc_buf + len, sc->sc_isize - len);
251*4a04e0a6SVladimir Kondratyev 
252*4a04e0a6SVladimir Kondratyev 	sc->sc_state.data = true;
253*4a04e0a6SVladimir Kondratyev 
254*4a04e0a6SVladimir Kondratyev 	u2f_notify(sc);
255*4a04e0a6SVladimir Kondratyev }
256*4a04e0a6SVladimir Kondratyev 
257*4a04e0a6SVladimir Kondratyev static int
u2f_open(struct cdev * dev,int flag,int mode,struct thread * td)258*4a04e0a6SVladimir Kondratyev u2f_open(struct cdev *dev, int flag, int mode, struct thread *td)
259*4a04e0a6SVladimir Kondratyev {
260*4a04e0a6SVladimir Kondratyev 	struct u2f_softc *sc = dev->si_drv1;
261*4a04e0a6SVladimir Kondratyev 	int error;
262*4a04e0a6SVladimir Kondratyev 
263*4a04e0a6SVladimir Kondratyev 	if (sc == NULL)
264*4a04e0a6SVladimir Kondratyev 		return (ENXIO);
265*4a04e0a6SVladimir Kondratyev 
266*4a04e0a6SVladimir Kondratyev 	DPRINTF("sc=%p\n", sc);
267*4a04e0a6SVladimir Kondratyev 
268*4a04e0a6SVladimir Kondratyev 	mtx_lock(&sc->sc_mtx);
269*4a04e0a6SVladimir Kondratyev 	if (sc->sc_state.open) {
270*4a04e0a6SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
271*4a04e0a6SVladimir Kondratyev 		return (EBUSY);
272*4a04e0a6SVladimir Kondratyev 	}
273*4a04e0a6SVladimir Kondratyev 	sc->sc_state.open = true;
274*4a04e0a6SVladimir Kondratyev 	mtx_unlock(&sc->sc_mtx);
275*4a04e0a6SVladimir Kondratyev 
276*4a04e0a6SVladimir Kondratyev 	error = devfs_set_cdevpriv(sc, u2f_dtor);
277*4a04e0a6SVladimir Kondratyev 	if (error != 0) {
278*4a04e0a6SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
279*4a04e0a6SVladimir Kondratyev 		sc->sc_state.open = false;
280*4a04e0a6SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
281*4a04e0a6SVladimir Kondratyev 		return (error);
282*4a04e0a6SVladimir Kondratyev 	}
283*4a04e0a6SVladimir Kondratyev 
284*4a04e0a6SVladimir Kondratyev 	/* Set up interrupt pipe. */
285*4a04e0a6SVladimir Kondratyev 	sc->sc_state.data = false;
286*4a04e0a6SVladimir Kondratyev 	sc->sc_fflags = flag;
287*4a04e0a6SVladimir Kondratyev 
288*4a04e0a6SVladimir Kondratyev 	return (0);
289*4a04e0a6SVladimir Kondratyev }
290*4a04e0a6SVladimir Kondratyev 
291*4a04e0a6SVladimir Kondratyev 
292*4a04e0a6SVladimir Kondratyev static void
u2f_dtor(void * data)293*4a04e0a6SVladimir Kondratyev u2f_dtor(void *data)
294*4a04e0a6SVladimir Kondratyev {
295*4a04e0a6SVladimir Kondratyev 	struct u2f_softc *sc = data;
296*4a04e0a6SVladimir Kondratyev 
297*4a04e0a6SVladimir Kondratyev #ifdef NOT_YET
298*4a04e0a6SVladimir Kondratyev 	/* Disable interrupts. */
299*4a04e0a6SVladimir Kondratyev 	hid_intr_stop(sc->sc_dev);
300*4a04e0a6SVladimir Kondratyev #endif
301*4a04e0a6SVladimir Kondratyev 
302*4a04e0a6SVladimir Kondratyev 	mtx_lock(&sc->sc_mtx);
303*4a04e0a6SVladimir Kondratyev 	sc->sc_state.open = false;
304*4a04e0a6SVladimir Kondratyev 	mtx_unlock(&sc->sc_mtx);
305*4a04e0a6SVladimir Kondratyev }
306*4a04e0a6SVladimir Kondratyev 
307*4a04e0a6SVladimir Kondratyev static int
u2f_read(struct cdev * dev,struct uio * uio,int flag)308*4a04e0a6SVladimir Kondratyev u2f_read(struct cdev *dev, struct uio *uio, int flag)
309*4a04e0a6SVladimir Kondratyev {
310*4a04e0a6SVladimir Kondratyev 	uint8_t buf[U2F_MAX_REPORT_SIZE];
311*4a04e0a6SVladimir Kondratyev 	struct u2f_softc *sc = dev->si_drv1;
312*4a04e0a6SVladimir Kondratyev 	size_t length = 0;
313*4a04e0a6SVladimir Kondratyev 	int error;
314*4a04e0a6SVladimir Kondratyev 
315*4a04e0a6SVladimir Kondratyev 	DPRINTFN(1, "\n");
316*4a04e0a6SVladimir Kondratyev 
317*4a04e0a6SVladimir Kondratyev 	if (sc == NULL)
318*4a04e0a6SVladimir Kondratyev 		return (EIO);
319*4a04e0a6SVladimir Kondratyev 
320*4a04e0a6SVladimir Kondratyev 	if (!sc->sc_state.data)
321*4a04e0a6SVladimir Kondratyev 		hid_intr_start(sc->sc_dev);
322*4a04e0a6SVladimir Kondratyev 
323*4a04e0a6SVladimir Kondratyev 	mtx_lock(&sc->sc_mtx);
324*4a04e0a6SVladimir Kondratyev 	if (dev->si_drv1 == NULL) {
325*4a04e0a6SVladimir Kondratyev 		error = EIO;
326*4a04e0a6SVladimir Kondratyev 		goto exit;
327*4a04e0a6SVladimir Kondratyev 	}
328*4a04e0a6SVladimir Kondratyev 
329*4a04e0a6SVladimir Kondratyev 	while (!sc->sc_state.data) {
330*4a04e0a6SVladimir Kondratyev 		if (flag & O_NONBLOCK) {
331*4a04e0a6SVladimir Kondratyev 			error = EWOULDBLOCK;
332*4a04e0a6SVladimir Kondratyev 			goto exit;
333*4a04e0a6SVladimir Kondratyev 		}
334*4a04e0a6SVladimir Kondratyev 		sc->sc_state.aslp = true;
335*4a04e0a6SVladimir Kondratyev 		DPRINTFN(5, "sleep on %p\n", &sc->sc_buf);
336*4a04e0a6SVladimir Kondratyev 		error = mtx_sleep(&sc->sc_buf, &sc->sc_mtx, PZERO | PCATCH,
337*4a04e0a6SVladimir Kondratyev 		    "u2frd", 0);
338*4a04e0a6SVladimir Kondratyev 		DPRINTFN(5, "woke, error=%d\n", error);
339*4a04e0a6SVladimir Kondratyev 		if (dev->si_drv1 == NULL)
340*4a04e0a6SVladimir Kondratyev 			error = EIO;
341*4a04e0a6SVladimir Kondratyev 		if (error) {
342*4a04e0a6SVladimir Kondratyev 			sc->sc_state.aslp = false;
343*4a04e0a6SVladimir Kondratyev 			goto exit;
344*4a04e0a6SVladimir Kondratyev 		}
345*4a04e0a6SVladimir Kondratyev 	}
346*4a04e0a6SVladimir Kondratyev 
347*4a04e0a6SVladimir Kondratyev 	if (sc->sc_state.data && uio->uio_resid > 0) {
348*4a04e0a6SVladimir Kondratyev 		length = min(uio->uio_resid, sc->sc_isize);
349*4a04e0a6SVladimir Kondratyev 		memcpy(buf, sc->sc_buf, length);
350*4a04e0a6SVladimir Kondratyev 		sc->sc_state.data = false;
351*4a04e0a6SVladimir Kondratyev 	}
352*4a04e0a6SVladimir Kondratyev exit:
353*4a04e0a6SVladimir Kondratyev 	mtx_unlock(&sc->sc_mtx);
354*4a04e0a6SVladimir Kondratyev 	if (length != 0) {
355*4a04e0a6SVladimir Kondratyev 		/* Copy the data to the user process. */
356*4a04e0a6SVladimir Kondratyev 		DPRINTFN(5, "got %lu chars\n", (u_long)length);
357*4a04e0a6SVladimir Kondratyev 		error = uiomove(buf, length, uio);
358*4a04e0a6SVladimir Kondratyev 	}
359*4a04e0a6SVladimir Kondratyev 
360*4a04e0a6SVladimir Kondratyev 	return (error);
361*4a04e0a6SVladimir Kondratyev }
362*4a04e0a6SVladimir Kondratyev 
363*4a04e0a6SVladimir Kondratyev static int
u2f_write(struct cdev * dev,struct uio * uio,int flag)364*4a04e0a6SVladimir Kondratyev u2f_write(struct cdev *dev, struct uio *uio, int flag)
365*4a04e0a6SVladimir Kondratyev {
366*4a04e0a6SVladimir Kondratyev 	uint8_t buf[U2F_MAX_REPORT_SIZE];
367*4a04e0a6SVladimir Kondratyev 	struct u2f_softc *sc = dev->si_drv1;
368*4a04e0a6SVladimir Kondratyev 	int error;
369*4a04e0a6SVladimir Kondratyev 
370*4a04e0a6SVladimir Kondratyev 	DPRINTFN(1, "\n");
371*4a04e0a6SVladimir Kondratyev 
372*4a04e0a6SVladimir Kondratyev 	if (sc == NULL)
373*4a04e0a6SVladimir Kondratyev 		return (EIO);
374*4a04e0a6SVladimir Kondratyev 
375*4a04e0a6SVladimir Kondratyev 	if (uio->uio_resid != sc->sc_osize)
376*4a04e0a6SVladimir Kondratyev 		return (EINVAL);
377*4a04e0a6SVladimir Kondratyev 	error = uiomove(buf, uio->uio_resid, uio);
378*4a04e0a6SVladimir Kondratyev 	if (error == 0)
379*4a04e0a6SVladimir Kondratyev 		error = hid_write(sc->sc_dev, buf, sc->sc_osize);
380*4a04e0a6SVladimir Kondratyev 
381*4a04e0a6SVladimir Kondratyev 	return (error);
382*4a04e0a6SVladimir Kondratyev }
383*4a04e0a6SVladimir Kondratyev 
384*4a04e0a6SVladimir Kondratyev #ifdef COMPAT_FREEBSD32
385*4a04e0a6SVladimir Kondratyev static void
update_ugd32(const struct usb_gen_descriptor * ugd,struct usb_gen_descriptor32 * ugd32)386*4a04e0a6SVladimir Kondratyev update_ugd32(const struct usb_gen_descriptor *ugd,
387*4a04e0a6SVladimir Kondratyev     struct usb_gen_descriptor32 *ugd32)
388*4a04e0a6SVladimir Kondratyev {
389*4a04e0a6SVladimir Kondratyev 	/* Don't update hgd_data pointer */
390*4a04e0a6SVladimir Kondratyev 	CP(*ugd, *ugd32, ugd_lang_id);
391*4a04e0a6SVladimir Kondratyev 	CP(*ugd, *ugd32, ugd_maxlen);
392*4a04e0a6SVladimir Kondratyev 	CP(*ugd, *ugd32, ugd_actlen);
393*4a04e0a6SVladimir Kondratyev 	CP(*ugd, *ugd32, ugd_offset);
394*4a04e0a6SVladimir Kondratyev 	CP(*ugd, *ugd32, ugd_config_index);
395*4a04e0a6SVladimir Kondratyev 	CP(*ugd, *ugd32, ugd_string_index);
396*4a04e0a6SVladimir Kondratyev 	CP(*ugd, *ugd32, ugd_iface_index);
397*4a04e0a6SVladimir Kondratyev 	CP(*ugd, *ugd32, ugd_altif_index);
398*4a04e0a6SVladimir Kondratyev 	CP(*ugd, *ugd32, ugd_endpt_index);
399*4a04e0a6SVladimir Kondratyev 	CP(*ugd, *ugd32, ugd_report_type);
400*4a04e0a6SVladimir Kondratyev 	/* Don't update reserved */
401*4a04e0a6SVladimir Kondratyev }
402*4a04e0a6SVladimir Kondratyev #endif
403*4a04e0a6SVladimir Kondratyev 
404*4a04e0a6SVladimir Kondratyev static int
u2f_ioctl(struct cdev * dev,u_long cmd,caddr_t addr,int flag,struct thread * td)405*4a04e0a6SVladimir Kondratyev u2f_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
406*4a04e0a6SVladimir Kondratyev     struct thread *td)
407*4a04e0a6SVladimir Kondratyev {
408*4a04e0a6SVladimir Kondratyev #ifdef COMPAT_FREEBSD32
409*4a04e0a6SVladimir Kondratyev 	struct usb_gen_descriptor local_ugd;
410*4a04e0a6SVladimir Kondratyev 	struct usb_gen_descriptor32 *ugd32 = NULL;
411*4a04e0a6SVladimir Kondratyev #endif
412*4a04e0a6SVladimir Kondratyev 	struct u2f_softc *sc = dev->si_drv1;
413*4a04e0a6SVladimir Kondratyev 	struct usb_gen_descriptor *ugd = (struct usb_gen_descriptor *)addr;
414*4a04e0a6SVladimir Kondratyev 	uint32_t size;
415*4a04e0a6SVladimir Kondratyev 
416*4a04e0a6SVladimir Kondratyev 	DPRINTFN(2, "cmd=%lx\n", cmd);
417*4a04e0a6SVladimir Kondratyev 
418*4a04e0a6SVladimir Kondratyev 	if (sc == NULL)
419*4a04e0a6SVladimir Kondratyev 		return (EIO);
420*4a04e0a6SVladimir Kondratyev 
421*4a04e0a6SVladimir Kondratyev #ifdef COMPAT_FREEBSD32
422*4a04e0a6SVladimir Kondratyev 	switch (cmd) {
423*4a04e0a6SVladimir Kondratyev 	case USB_GET_REPORT_DESC32:
424*4a04e0a6SVladimir Kondratyev 		cmd = _IOC_NEWTYPE(cmd, struct usb_gen_descriptor);
425*4a04e0a6SVladimir Kondratyev 		ugd32 = (struct usb_gen_descriptor32 *)addr;
426*4a04e0a6SVladimir Kondratyev 		ugd = &local_ugd;
427*4a04e0a6SVladimir Kondratyev 		PTRIN_CP(*ugd32, *ugd, ugd_data);
428*4a04e0a6SVladimir Kondratyev 		CP(*ugd32, *ugd, ugd_lang_id);
429*4a04e0a6SVladimir Kondratyev 		CP(*ugd32, *ugd, ugd_maxlen);
430*4a04e0a6SVladimir Kondratyev 		CP(*ugd32, *ugd, ugd_actlen);
431*4a04e0a6SVladimir Kondratyev 		CP(*ugd32, *ugd, ugd_offset);
432*4a04e0a6SVladimir Kondratyev 		CP(*ugd32, *ugd, ugd_config_index);
433*4a04e0a6SVladimir Kondratyev 		CP(*ugd32, *ugd, ugd_string_index);
434*4a04e0a6SVladimir Kondratyev 		CP(*ugd32, *ugd, ugd_iface_index);
435*4a04e0a6SVladimir Kondratyev 		CP(*ugd32, *ugd, ugd_altif_index);
436*4a04e0a6SVladimir Kondratyev 		CP(*ugd32, *ugd, ugd_endpt_index);
437*4a04e0a6SVladimir Kondratyev 		CP(*ugd32, *ugd, ugd_report_type);
438*4a04e0a6SVladimir Kondratyev 		/* Don't copy reserved */
439*4a04e0a6SVladimir Kondratyev 		break;
440*4a04e0a6SVladimir Kondratyev 	}
441*4a04e0a6SVladimir Kondratyev #endif
442*4a04e0a6SVladimir Kondratyev 
443*4a04e0a6SVladimir Kondratyev 	/* fixed-length ioctls handling */
444*4a04e0a6SVladimir Kondratyev 	switch (cmd) {
445*4a04e0a6SVladimir Kondratyev 	case FIONBIO:
446*4a04e0a6SVladimir Kondratyev 		/* All handled in the upper FS layer. */
447*4a04e0a6SVladimir Kondratyev 		return (0);
448*4a04e0a6SVladimir Kondratyev 
449*4a04e0a6SVladimir Kondratyev 	case USB_GET_REPORT_DESC:
450*4a04e0a6SVladimir Kondratyev 		size = MIN(sc->sc_rdesc_size, ugd->ugd_maxlen);
451*4a04e0a6SVladimir Kondratyev 		ugd->ugd_actlen = size;
452*4a04e0a6SVladimir Kondratyev #ifdef COMPAT_FREEBSD32
453*4a04e0a6SVladimir Kondratyev 		if (ugd32 != NULL)
454*4a04e0a6SVladimir Kondratyev 			update_ugd32(ugd, ugd32);
455*4a04e0a6SVladimir Kondratyev #endif
456*4a04e0a6SVladimir Kondratyev 		if (ugd->ugd_data == NULL)
457*4a04e0a6SVladimir Kondratyev 			return (0);		/* descriptor length only */
458*4a04e0a6SVladimir Kondratyev 
459*4a04e0a6SVladimir Kondratyev 		return (copyout(sc->sc_rdesc, ugd->ugd_data, size));
460*4a04e0a6SVladimir Kondratyev 
461*4a04e0a6SVladimir Kondratyev 	case USB_GET_DEVICEINFO:
462*4a04e0a6SVladimir Kondratyev 		return(hid_ioctl(
463*4a04e0a6SVladimir Kondratyev 		    sc->sc_dev, USB_GET_DEVICEINFO, (uintptr_t)addr));
464*4a04e0a6SVladimir Kondratyev 	}
465*4a04e0a6SVladimir Kondratyev 
466*4a04e0a6SVladimir Kondratyev 	return (EINVAL);
467*4a04e0a6SVladimir Kondratyev }
468*4a04e0a6SVladimir Kondratyev 
469*4a04e0a6SVladimir Kondratyev static int
u2f_poll(struct cdev * dev,int events,struct thread * td)470*4a04e0a6SVladimir Kondratyev u2f_poll(struct cdev *dev, int events, struct thread *td)
471*4a04e0a6SVladimir Kondratyev {
472*4a04e0a6SVladimir Kondratyev 	struct u2f_softc *sc = dev->si_drv1;
473*4a04e0a6SVladimir Kondratyev 	int revents = 0;
474*4a04e0a6SVladimir Kondratyev 	bool start_intr = false;
475*4a04e0a6SVladimir Kondratyev 
476*4a04e0a6SVladimir Kondratyev 	if (sc == NULL)
477*4a04e0a6SVladimir Kondratyev                 return (POLLHUP);
478*4a04e0a6SVladimir Kondratyev 
479*4a04e0a6SVladimir Kondratyev 	if (events & (POLLOUT | POLLWRNORM) && (sc->sc_fflags & FWRITE))
480*4a04e0a6SVladimir Kondratyev 		revents |= events & (POLLOUT | POLLWRNORM);
481*4a04e0a6SVladimir Kondratyev 	if (events & (POLLIN | POLLRDNORM) && (sc->sc_fflags & FREAD)) {
482*4a04e0a6SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
483*4a04e0a6SVladimir Kondratyev 		if (sc->sc_state.data)
484*4a04e0a6SVladimir Kondratyev 			revents |= events & (POLLIN | POLLRDNORM);
485*4a04e0a6SVladimir Kondratyev 		else {
486*4a04e0a6SVladimir Kondratyev 			sc->sc_state.sel = true;
487*4a04e0a6SVladimir Kondratyev 			start_intr = true;
488*4a04e0a6SVladimir Kondratyev 			selrecord(td, &sc->sc_rsel);
489*4a04e0a6SVladimir Kondratyev 		}
490*4a04e0a6SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
491*4a04e0a6SVladimir Kondratyev 		if (start_intr)
492*4a04e0a6SVladimir Kondratyev 			hid_intr_start(sc->sc_dev);
493*4a04e0a6SVladimir Kondratyev 	}
494*4a04e0a6SVladimir Kondratyev 
495*4a04e0a6SVladimir Kondratyev 	return (revents);
496*4a04e0a6SVladimir Kondratyev }
497*4a04e0a6SVladimir Kondratyev 
498*4a04e0a6SVladimir Kondratyev static int
u2f_kqfilter(struct cdev * dev,struct knote * kn)499*4a04e0a6SVladimir Kondratyev u2f_kqfilter(struct cdev *dev, struct knote *kn)
500*4a04e0a6SVladimir Kondratyev {
501*4a04e0a6SVladimir Kondratyev 	struct u2f_softc *sc = dev->si_drv1;
502*4a04e0a6SVladimir Kondratyev 
503*4a04e0a6SVladimir Kondratyev 	if (sc == NULL)
504*4a04e0a6SVladimir Kondratyev 		return (ENXIO);
505*4a04e0a6SVladimir Kondratyev 
506*4a04e0a6SVladimir Kondratyev 	switch(kn->kn_filter) {
507*4a04e0a6SVladimir Kondratyev 	case EVFILT_READ:
508*4a04e0a6SVladimir Kondratyev 		if (sc->sc_fflags & FREAD) {
509*4a04e0a6SVladimir Kondratyev 			kn->kn_fop = &u2f_filterops_read;
510*4a04e0a6SVladimir Kondratyev 			break;
511*4a04e0a6SVladimir Kondratyev 		}
512*4a04e0a6SVladimir Kondratyev 		/* FALLTHROUGH */
513*4a04e0a6SVladimir Kondratyev 	default:
514*4a04e0a6SVladimir Kondratyev 		return(EINVAL);
515*4a04e0a6SVladimir Kondratyev 	}
516*4a04e0a6SVladimir Kondratyev 	kn->kn_hook = sc;
517*4a04e0a6SVladimir Kondratyev 
518*4a04e0a6SVladimir Kondratyev 	knlist_add(&sc->sc_rsel.si_note, kn, 0);
519*4a04e0a6SVladimir Kondratyev 	return (0);
520*4a04e0a6SVladimir Kondratyev }
521*4a04e0a6SVladimir Kondratyev 
522*4a04e0a6SVladimir Kondratyev static int
u2f_kqread(struct knote * kn,long hint)523*4a04e0a6SVladimir Kondratyev u2f_kqread(struct knote *kn, long hint)
524*4a04e0a6SVladimir Kondratyev {
525*4a04e0a6SVladimir Kondratyev 	struct u2f_softc *sc = kn->kn_hook;
526*4a04e0a6SVladimir Kondratyev 	int ret;
527*4a04e0a6SVladimir Kondratyev 
528*4a04e0a6SVladimir Kondratyev 	mtx_assert(&sc->sc_mtx, MA_OWNED);
529*4a04e0a6SVladimir Kondratyev 
530*4a04e0a6SVladimir Kondratyev 	if (sc->dev->si_drv1 == NULL) {
531*4a04e0a6SVladimir Kondratyev 		kn->kn_flags |= EV_EOF;
532*4a04e0a6SVladimir Kondratyev 		ret = 1;
533*4a04e0a6SVladimir Kondratyev 	} else {
534*4a04e0a6SVladimir Kondratyev 		ret = sc->sc_state.data ? 1 : 0;
535*4a04e0a6SVladimir Kondratyev 		if (!sc->sc_state.data)
536*4a04e0a6SVladimir Kondratyev 			hid_intr_start(sc->sc_dev);
537*4a04e0a6SVladimir Kondratyev 	}
538*4a04e0a6SVladimir Kondratyev 
539*4a04e0a6SVladimir Kondratyev 	return (ret);
540*4a04e0a6SVladimir Kondratyev }
541*4a04e0a6SVladimir Kondratyev 
542*4a04e0a6SVladimir Kondratyev static void
u2f_kqdetach(struct knote * kn)543*4a04e0a6SVladimir Kondratyev u2f_kqdetach(struct knote *kn)
544*4a04e0a6SVladimir Kondratyev {
545*4a04e0a6SVladimir Kondratyev 	struct u2f_softc *sc = kn->kn_hook;
546*4a04e0a6SVladimir Kondratyev 
547*4a04e0a6SVladimir Kondratyev 	knlist_remove(&sc->sc_rsel.si_note, kn, 0);
548*4a04e0a6SVladimir Kondratyev }
549*4a04e0a6SVladimir Kondratyev 
550*4a04e0a6SVladimir Kondratyev static void
u2f_notify(struct u2f_softc * sc)551*4a04e0a6SVladimir Kondratyev u2f_notify(struct u2f_softc *sc)
552*4a04e0a6SVladimir Kondratyev {
553*4a04e0a6SVladimir Kondratyev 	mtx_assert(&sc->sc_mtx, MA_OWNED);
554*4a04e0a6SVladimir Kondratyev 
555*4a04e0a6SVladimir Kondratyev 	if (sc->sc_state.aslp) {
556*4a04e0a6SVladimir Kondratyev 		sc->sc_state.aslp = false;
557*4a04e0a6SVladimir Kondratyev 		DPRINTFN(5, "waking %p\n", &sc->sc_buf);
558*4a04e0a6SVladimir Kondratyev 		wakeup(&sc->sc_buf);
559*4a04e0a6SVladimir Kondratyev 	}
560*4a04e0a6SVladimir Kondratyev 	if (sc->sc_state.sel) {
561*4a04e0a6SVladimir Kondratyev 		sc->sc_state.sel = false;
562*4a04e0a6SVladimir Kondratyev 		selwakeuppri(&sc->sc_rsel, PZERO);
563*4a04e0a6SVladimir Kondratyev 	}
564*4a04e0a6SVladimir Kondratyev 	KNOTE_LOCKED(&sc->sc_rsel.si_note, 0);
565*4a04e0a6SVladimir Kondratyev }
566*4a04e0a6SVladimir Kondratyev 
567*4a04e0a6SVladimir Kondratyev static device_method_t u2f_methods[] = {
568*4a04e0a6SVladimir Kondratyev 	/* Device interface */
569*4a04e0a6SVladimir Kondratyev 	DEVMETHOD(device_probe,		u2f_probe),
570*4a04e0a6SVladimir Kondratyev 	DEVMETHOD(device_attach,	u2f_attach),
571*4a04e0a6SVladimir Kondratyev 	DEVMETHOD(device_detach,	u2f_detach),
572*4a04e0a6SVladimir Kondratyev 
573*4a04e0a6SVladimir Kondratyev 	DEVMETHOD_END
574*4a04e0a6SVladimir Kondratyev };
575*4a04e0a6SVladimir Kondratyev 
576*4a04e0a6SVladimir Kondratyev static driver_t u2f_driver = {
577*4a04e0a6SVladimir Kondratyev #ifdef U2F_MAKE_UHID_ALIAS
578*4a04e0a6SVladimir Kondratyev 	"uhid",
579*4a04e0a6SVladimir Kondratyev #else
580*4a04e0a6SVladimir Kondratyev 	"u2f",
581*4a04e0a6SVladimir Kondratyev #endif
582*4a04e0a6SVladimir Kondratyev 	u2f_methods,
583*4a04e0a6SVladimir Kondratyev 	sizeof(struct u2f_softc)
584*4a04e0a6SVladimir Kondratyev };
585*4a04e0a6SVladimir Kondratyev 
586*4a04e0a6SVladimir Kondratyev DRIVER_MODULE(u2f, hidbus, u2f_driver, NULL, NULL);
587*4a04e0a6SVladimir Kondratyev MODULE_DEPEND(u2f, hidbus, 1, 1, 1);
588*4a04e0a6SVladimir Kondratyev MODULE_DEPEND(u2f, hid, 1, 1, 1);
589*4a04e0a6SVladimir Kondratyev MODULE_VERSION(u2f, 1);
590*4a04e0a6SVladimir Kondratyev HID_PNP_INFO(u2f_devs);
591