xref: /dflybsd-src/sys/dev/misc/cmx/cmx.c (revision fcf6efefc03a35111797b109fa4994034ebe39ba)
1c4bf625eSHasso Tepper /*-
2c4bf625eSHasso Tepper  * Copyright (c) 2006-2007 Daniel Roethlisberger <daniel@roe.ch>
3c4bf625eSHasso Tepper  * Copyright (c) 2000-2004 OMNIKEY GmbH (www.omnikey.com)
4c4bf625eSHasso Tepper  * All rights reserved.
5c4bf625eSHasso Tepper  *
6c4bf625eSHasso Tepper  * Redistribution and use in source and binary forms, with or without
7c4bf625eSHasso Tepper  * modification, are permitted provided that the following conditions
8c4bf625eSHasso Tepper  * are met:
9c4bf625eSHasso Tepper  * 1. Redistributions of source code must retain the above copyright
10c4bf625eSHasso Tepper  *    notice unmodified, this list of conditions, and the following
11c4bf625eSHasso Tepper  *    disclaimer.
12c4bf625eSHasso Tepper  * 2. Redistributions in binary form must reproduce the above copyright
13c4bf625eSHasso Tepper  *    notice, this list of conditions and the following disclaimer in the
14c4bf625eSHasso Tepper  *    documentation and/or other materials provided with the distribution.
15c4bf625eSHasso Tepper  *
16c4bf625eSHasso Tepper  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17c4bf625eSHasso Tepper  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18c4bf625eSHasso Tepper  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19c4bf625eSHasso Tepper  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20c4bf625eSHasso Tepper  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21c4bf625eSHasso Tepper  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22c4bf625eSHasso Tepper  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23c4bf625eSHasso Tepper  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24c4bf625eSHasso Tepper  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25c4bf625eSHasso Tepper  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26c4bf625eSHasso Tepper  * SUCH DAMAGE.
27c4bf625eSHasso Tepper  *
28c4bf625eSHasso Tepper  * $FreeBSD: src/sys/dev/cmx/cmx.c,v 1.1 2008/03/06 08:09:45 rink Exp $
29c4bf625eSHasso Tepper  */
30c4bf625eSHasso Tepper 
31c4bf625eSHasso Tepper /*
32c4bf625eSHasso Tepper  * OMNIKEY CardMan 4040 a.k.a. CardMan eXtended (cmx) driver.
33c4bf625eSHasso Tepper  * This is a PCMCIA based smartcard reader which seems to work
34c4bf625eSHasso Tepper  * like an I/O port mapped USB CCID smartcard device.
35c4bf625eSHasso Tepper  *
36c4bf625eSHasso Tepper  * I/O originally based on Linux driver version 1.1.0 by OMNIKEY.
37c4bf625eSHasso Tepper  * Dual GPL/BSD.  Almost all of the code has been rewritten.
38c4bf625eSHasso Tepper  * $Omnikey: cm4040_cs.c,v 1.7 2004/10/04 09:08:50 jp Exp $
39c4bf625eSHasso Tepper  */
40c4bf625eSHasso Tepper 
41c4bf625eSHasso Tepper #include <sys/param.h>
42c4bf625eSHasso Tepper #include <sys/systm.h>
43c4bf625eSHasso Tepper #include <sys/kernel.h>
44c4bf625eSHasso Tepper #include <sys/sockio.h>
45c4bf625eSHasso Tepper #include <sys/mbuf.h>
463546e044SSamuel J. Greear #include <sys/event.h>
47c4bf625eSHasso Tepper #include <sys/conf.h>
48c4bf625eSHasso Tepper #include <sys/fcntl.h>
49c4bf625eSHasso Tepper #include <sys/uio.h>
50c4bf625eSHasso Tepper #include <sys/types.h>
51c4bf625eSHasso Tepper #include <sys/lock.h>
52c4bf625eSHasso Tepper #include <sys/device.h>
53c4bf625eSHasso Tepper 
54c4bf625eSHasso Tepper #include <sys/module.h>
55c4bf625eSHasso Tepper #include <sys/bus.h>
56c4bf625eSHasso Tepper #include <sys/resource.h>
57c4bf625eSHasso Tepper #include <sys/rman.h>
58c4bf625eSHasso Tepper 
59c4bf625eSHasso Tepper #include "cmxvar.h"
60c4bf625eSHasso Tepper #include "cmxreg.h"
61c4bf625eSHasso Tepper 
62c4bf625eSHasso Tepper #ifdef CMX_DEBUG
63c4bf625eSHasso Tepper #define	DEBUG_printf(dev, fmt, args...) \
647fd4e1a1SSascha Wildner 	device_printf(dev, "%s: " fmt, __func__, ##args)
65c4bf625eSHasso Tepper #else
66c4bf625eSHasso Tepper #define	DEBUG_printf(dev, fmt, args...)
67c4bf625eSHasso Tepper #endif
68c4bf625eSHasso Tepper 
69c4bf625eSHasso Tepper #define	SPIN_COUNT				1000
70c4bf625eSHasso Tepper #define	WAIT_TICKS				(hz/100)
71c4bf625eSHasso Tepper #define	POLL_TICKS				(hz/10)
72c4bf625eSHasso Tepper 
73c4bf625eSHasso Tepper /* possibly bogus */
74c4bf625eSHasso Tepper #define	CCID_DRIVER_BULK_DEFAULT_TIMEOUT	(150*hz)
75c4bf625eSHasso Tepper #define	CCID_DRIVER_ASYNC_POWERUP_TIMEOUT	(35*hz)
76c4bf625eSHasso Tepper #define	CCID_DRIVER_MINIMUM_TIMEOUT		(3*hz)
77c4bf625eSHasso Tepper 
78c4bf625eSHasso Tepper #ifdef CMX_DEBUG
79c4bf625eSHasso Tepper static char	BSRBITS[] = "\020"
80c4bf625eSHasso Tepper 	"\01BULK_OUT_FULL"		/* 0x01 */
81c4bf625eSHasso Tepper 	"\02BULK_IN_FULL"		/* 0x02 */
82c4bf625eSHasso Tepper 	"\03(0x04)";			/* 0x04 */
83c4bf625eSHasso Tepper #ifdef CMX_INTR
84c4bf625eSHasso Tepper static char	SCRBITS[] = "\020"
85c4bf625eSHasso Tepper 	"\01POWER_DOWN"			/* 0x01 */
86c4bf625eSHasso Tepper 	"\02PULSE_INTERRUPT"		/* 0x02 */
87c4bf625eSHasso Tepper 	"\03HOST_TO_READER_DONE"	/* 0x04 */
88c4bf625eSHasso Tepper 	"\04READER_TO_HOST_DONE"	/* 0x08 */
89c4bf625eSHasso Tepper 	"\05ACK_NOTIFY"			/* 0x10 */
90c4bf625eSHasso Tepper 	"\06EN_NOTIFY"			/* 0x20 */
91c4bf625eSHasso Tepper 	"\07ABORT"			/* 0x40 */
92c4bf625eSHasso Tepper 	"\10HOST_TO_READER_START";	/* 0x80 */
93c4bf625eSHasso Tepper #endif /* CMX_INTR */
94c4bf625eSHasso Tepper static char	POLLBITS[] = "\020"
95c4bf625eSHasso Tepper 	"\01POLLIN"			/* 0x0001 */
96c4bf625eSHasso Tepper 	"\02POLLPRI"			/* 0x0002 */
97c4bf625eSHasso Tepper 	"\03POLLOUT"			/* 0x0004 */
98c4bf625eSHasso Tepper 	"\04POLLERR"			/* 0x0008 */
99c4bf625eSHasso Tepper 	"\05POLLHUP"			/* 0x0010 */
100c4bf625eSHasso Tepper 	"\06POLLINVAL"			/* 0x0020 */
101c4bf625eSHasso Tepper 	"\07POLLRDNORM"			/* 0x0040 */
102c4bf625eSHasso Tepper 	"\10POLLRDBAND"			/* 0x0080 */
103c4bf625eSHasso Tepper 	"\11POLLWRBAND";		/* 0x0100 */
104c4bf625eSHasso Tepper static char	MODEBITS[] = "\020"
105c4bf625eSHasso Tepper 	"\01READ"			/* 0x0001 */
106c4bf625eSHasso Tepper 	"\02WRITE"			/* 0x0002 */
107c4bf625eSHasso Tepper 	"\03NONBLOCK"			/* 0x0004 */
108c4bf625eSHasso Tepper 	"\04APPEND"			/* 0x0008 */
109c4bf625eSHasso Tepper 	"\05SHLOCK"			/* 0x0010 */
110c4bf625eSHasso Tepper 	"\06EXLOCK"			/* 0x0020 */
111c4bf625eSHasso Tepper 	"\07ASYNC"			/* 0x0040 */
112c4bf625eSHasso Tepper 	"\10FSYNC"			/* 0x0080 */
113c4bf625eSHasso Tepper 	"\11NOFOLLOW"			/* 0x0100 */
114c4bf625eSHasso Tepper 	"\12CREAT"			/* 0x0200 */
115c4bf625eSHasso Tepper 	"\13TRUNK"			/* 0x0400 */
116c4bf625eSHasso Tepper 	"\14EXCL"			/* 0x0800 */
117c4bf625eSHasso Tepper 	"\15(0x1000)"			/* 0x1000 */
118c4bf625eSHasso Tepper 	"\16(0x2000)"			/* 0x2000 */
119c4bf625eSHasso Tepper 	"\17HASLOCK"			/* 0x4000 */
120c4bf625eSHasso Tepper 	"\20NOCTTY"			/* 0x8000 */
121c4bf625eSHasso Tepper 	"\21DIRECT";			/* 0x00010000 */
122c4bf625eSHasso Tepper #endif /* CMX_DEBUG */
123c4bf625eSHasso Tepper 
124c4bf625eSHasso Tepper devclass_t cmx_devclass;
125c4bf625eSHasso Tepper 
126c4bf625eSHasso Tepper static d_open_t		cmx_open;
127c4bf625eSHasso Tepper static d_close_t	cmx_close;
128c4bf625eSHasso Tepper static d_read_t		cmx_read;
129c4bf625eSHasso Tepper static d_write_t	cmx_write;
1303546e044SSamuel J. Greear static d_kqfilter_t	cmx_kqfilter;
131c4bf625eSHasso Tepper #ifdef CMX_INTR
132c4bf625eSHasso Tepper static void		cmx_intr(void *arg);
133c4bf625eSHasso Tepper #endif
134c4bf625eSHasso Tepper 
1353546e044SSamuel J. Greear static void cmx_filter_detach(struct knote *);
1363546e044SSamuel J. Greear static int cmx_filter_read(struct knote *, long);
1373546e044SSamuel J. Greear static int cmx_filter_write(struct knote *, long);
1383546e044SSamuel J. Greear 
139c4bf625eSHasso Tepper static struct dev_ops cmx_ops = {
14088abd8b5SSascha Wildner 	{ "cmx", 0, 0 },
141c4bf625eSHasso Tepper 	.d_open =	cmx_open,
142c4bf625eSHasso Tepper 	.d_close =	cmx_close,
143c4bf625eSHasso Tepper 	.d_read =	cmx_read,
144c4bf625eSHasso Tepper 	.d_write =	cmx_write,
1453546e044SSamuel J. Greear 	.d_kqfilter =	cmx_kqfilter
146c4bf625eSHasso Tepper };
147c4bf625eSHasso Tepper 
148c4bf625eSHasso Tepper /*
149c4bf625eSHasso Tepper  * Initialize the softc structure.  Must be called from
150c4bf625eSHasso Tepper  * the bus specific device allocation routine.
151c4bf625eSHasso Tepper  */
152c4bf625eSHasso Tepper void
cmx_init_softc(device_t dev)153c4bf625eSHasso Tepper cmx_init_softc(device_t dev)
154c4bf625eSHasso Tepper {
155c4bf625eSHasso Tepper 	struct cmx_softc *sc = device_get_softc(dev);
156c4bf625eSHasso Tepper 	sc->dev = dev;
157c4bf625eSHasso Tepper 	sc->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
158c4bf625eSHasso Tepper }
159c4bf625eSHasso Tepper 
160c4bf625eSHasso Tepper /*
161c4bf625eSHasso Tepper  * Allocate driver resources.  Must be called from the
162c4bf625eSHasso Tepper  * bus specific device allocation routine.  Caller must
163c4bf625eSHasso Tepper  * ensure to call cmx_release_resources to free the
164c4bf625eSHasso Tepper  * resources when detaching.
165c4bf625eSHasso Tepper  * Return zero if successful, and ENOMEM if the resources
166c4bf625eSHasso Tepper  * could not be allocated.
167c4bf625eSHasso Tepper  */
168c4bf625eSHasso Tepper int
cmx_alloc_resources(device_t dev)169c4bf625eSHasso Tepper cmx_alloc_resources(device_t dev)
170c4bf625eSHasso Tepper {
171c4bf625eSHasso Tepper 	struct cmx_softc *sc = device_get_softc(dev);
172c4bf625eSHasso Tepper #ifdef CMX_INTR
173c4bf625eSHasso Tepper 	int rv;
174c4bf625eSHasso Tepper #endif
175c4bf625eSHasso Tepper 
176c4bf625eSHasso Tepper 	sc->ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
177c4bf625eSHasso Tepper 			&sc->ioport_rid, RF_ACTIVE);
178c4bf625eSHasso Tepper 	if (!sc->ioport) {
179c4bf625eSHasso Tepper 		device_printf(dev, "failed to allocate io port\n");
180c4bf625eSHasso Tepper 		return ENOMEM;
181c4bf625eSHasso Tepper 	}
182c4bf625eSHasso Tepper 	sc->bst = rman_get_bustag(sc->ioport);
183c4bf625eSHasso Tepper 	sc->bsh = rman_get_bushandle(sc->ioport);
184c4bf625eSHasso Tepper 
185c4bf625eSHasso Tepper #ifdef CMX_INTR
186c4bf625eSHasso Tepper 	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
187c4bf625eSHasso Tepper 			&sc->irq_rid, RF_ACTIVE);
188c4bf625eSHasso Tepper 	if (!sc->irq) {
189c4bf625eSHasso Tepper 		device_printf(dev, "failed to allocate irq\n");
190c4bf625eSHasso Tepper 		return ENOMEM;
191c4bf625eSHasso Tepper 	}
192c4bf625eSHasso Tepper 	if ((rv = bus_setup_intr(dev, sc->irq, 0, cmx_intr, sc,
193c4bf625eSHasso Tepper 			&sc->ih, NULL)) != 0) {
194c4bf625eSHasso Tepper 		device_printf(dev, "failed to set up irq\n");
195c4bf625eSHasso Tepper 		return ENOMEM;
196c4bf625eSHasso Tepper 	}
197c4bf625eSHasso Tepper #endif
198c4bf625eSHasso Tepper 
199c4bf625eSHasso Tepper 	lockinit(&sc->mtx, "cmx softc lock", 0, LK_CANRECURSE);
200c4bf625eSHasso Tepper 	callout_init(&sc->ch);
201c4bf625eSHasso Tepper 
202c4bf625eSHasso Tepper 	return 0;
203c4bf625eSHasso Tepper }
204c4bf625eSHasso Tepper 
205c4bf625eSHasso Tepper /*
206c4bf625eSHasso Tepper  * Release the resources allocated by cmx_allocate_resources.
207c4bf625eSHasso Tepper  */
208c4bf625eSHasso Tepper void
cmx_release_resources(device_t dev)209c4bf625eSHasso Tepper cmx_release_resources(device_t dev)
210c4bf625eSHasso Tepper {
211c4bf625eSHasso Tepper 	struct cmx_softc *sc = device_get_softc(dev);
212c4bf625eSHasso Tepper 
213c4bf625eSHasso Tepper 	lockuninit(&sc->mtx);
214c4bf625eSHasso Tepper 
215c4bf625eSHasso Tepper #ifdef CMX_INTR
216c4bf625eSHasso Tepper 	if (sc->ih) {
217c4bf625eSHasso Tepper 		bus_teardown_intr(dev, sc->irq, sc->ih);
218c4bf625eSHasso Tepper 		sc->ih = NULL;
219c4bf625eSHasso Tepper 	}
220c4bf625eSHasso Tepper 	if (sc->irq) {
221c4bf625eSHasso Tepper 		bus_release_resource(dev, SYS_RES_IRQ,
222c4bf625eSHasso Tepper 				sc->irq_rid, sc->irq);
223c4bf625eSHasso Tepper 		sc->irq = NULL;
224c4bf625eSHasso Tepper 	}
225c4bf625eSHasso Tepper #endif
226c4bf625eSHasso Tepper 
227c4bf625eSHasso Tepper 	if (sc->ioport) {
228c4bf625eSHasso Tepper 		bus_deactivate_resource(dev, SYS_RES_IOPORT,
229c4bf625eSHasso Tepper 				sc->ioport_rid, sc->ioport);
230c4bf625eSHasso Tepper 		bus_release_resource(dev, SYS_RES_IOPORT,
231c4bf625eSHasso Tepper 				sc->ioport_rid, sc->ioport);
232c4bf625eSHasso Tepper 		sc->ioport = NULL;
233c4bf625eSHasso Tepper 	}
234c4bf625eSHasso Tepper 	return;
235c4bf625eSHasso Tepper }
236c4bf625eSHasso Tepper 
237c4bf625eSHasso Tepper /*
238c4bf625eSHasso Tepper  * Bus independant device attachment routine.  Creates the
239c4bf625eSHasso Tepper  * character device node.
240c4bf625eSHasso Tepper  */
241c4bf625eSHasso Tepper int
cmx_attach(device_t dev)242c4bf625eSHasso Tepper cmx_attach(device_t dev)
243c4bf625eSHasso Tepper {
244c4bf625eSHasso Tepper 	struct cmx_softc *sc = device_get_softc(dev);
245c4bf625eSHasso Tepper 
246c4bf625eSHasso Tepper 	if (!sc || sc->dying)
247c4bf625eSHasso Tepper 		return ENXIO;
248c4bf625eSHasso Tepper 
249c4bf625eSHasso Tepper 	sc->cdev = make_dev(&cmx_ops, 0, UID_ROOT, GID_WHEEL, 0600,
250c4bf625eSHasso Tepper 	                    "cmx%d", device_get_unit(dev));
251c4bf625eSHasso Tepper 	if (!sc->cdev) {
252c4bf625eSHasso Tepper 		device_printf(dev, "failed to create character device\n");
253c4bf625eSHasso Tepper 		return ENOMEM;
254c4bf625eSHasso Tepper 	}
255c4bf625eSHasso Tepper 	sc->cdev->si_drv1 = sc;
256c4bf625eSHasso Tepper 
257c4bf625eSHasso Tepper 	return 0;
258c4bf625eSHasso Tepper }
259c4bf625eSHasso Tepper 
260c4bf625eSHasso Tepper /*
261c4bf625eSHasso Tepper  * Bus independant device detachment routine.  Makes sure all
262c4bf625eSHasso Tepper  * allocated resources are freed, callouts disabled and waiting
263c4bf625eSHasso Tepper  * processes unblocked.
264c4bf625eSHasso Tepper  */
265c4bf625eSHasso Tepper int
cmx_detach(device_t dev)266c4bf625eSHasso Tepper cmx_detach(device_t dev)
267c4bf625eSHasso Tepper {
268c4bf625eSHasso Tepper 	struct cmx_softc *sc = device_get_softc(dev);
269c4bf625eSHasso Tepper 
270c4bf625eSHasso Tepper 	DEBUG_printf(dev, "called\n");
271c4bf625eSHasso Tepper 
272c4bf625eSHasso Tepper 	sc->dying = 1;
273c4bf625eSHasso Tepper 
274c4bf625eSHasso Tepper 	CMX_LOCK(sc);
275c4bf625eSHasso Tepper 	if (sc->polling) {
276c4bf625eSHasso Tepper 		DEBUG_printf(sc->dev, "disabling polling\n");
277c4bf625eSHasso Tepper 		callout_stop(&sc->ch);
278c4bf625eSHasso Tepper 		sc->polling = 0;
279c4bf625eSHasso Tepper 		CMX_UNLOCK(sc);
2805b22f1a7SSamuel J. Greear 		KNOTE(&sc->kq.ki_note, 0);
281c4bf625eSHasso Tepper 	} else {
282c4bf625eSHasso Tepper 		CMX_UNLOCK(sc);
283c4bf625eSHasso Tepper 	}
284c4bf625eSHasso Tepper 
285c4bf625eSHasso Tepper 	wakeup(sc);
286c4bf625eSHasso Tepper 	DEBUG_printf(dev, "releasing resources\n");
287c4bf625eSHasso Tepper 	cmx_release_resources(dev);
288cd29885aSMatthew Dillon 	dev_ops_remove_minor(&cmx_ops, device_get_unit(dev));
289d54592eeSHasso Tepper 
290c4bf625eSHasso Tepper 	return 0;
291c4bf625eSHasso Tepper }
292c4bf625eSHasso Tepper 
293c4bf625eSHasso Tepper /*
294c4bf625eSHasso Tepper  * Wait for buffer status register events.  If test is non-zero,
295c4bf625eSHasso Tepper  * wait until flags are set, otherwise wait until flags are unset.
296c4bf625eSHasso Tepper  * Will spin SPIN_COUNT times, then sleep until timeout is reached.
297c4bf625eSHasso Tepper  * Returns zero if event happened, EIO if the timeout was reached,
298c4bf625eSHasso Tepper  * and ENXIO if the device was detached in the meantime.  When that
299c4bf625eSHasso Tepper  * happens, the caller must quit immediately, since a detach is
300c4bf625eSHasso Tepper  * in progress.
301c4bf625eSHasso Tepper  */
302c4bf625eSHasso Tepper static inline int
cmx_wait_BSR(struct cmx_softc * sc,uint8_t flags,int test)303c4bf625eSHasso Tepper cmx_wait_BSR(struct cmx_softc *sc, uint8_t flags, int test)
304c4bf625eSHasso Tepper {
305c4bf625eSHasso Tepper 	int rv;
306c4bf625eSHasso Tepper 
307c4bf625eSHasso Tepper 	for (int i = 0; i < SPIN_COUNT; i++) {
308c4bf625eSHasso Tepper 		if (cmx_test_BSR(sc, flags, test))
309c4bf625eSHasso Tepper 			return 0;
310c4bf625eSHasso Tepper 	}
311c4bf625eSHasso Tepper 
312c4bf625eSHasso Tepper 	for (int i = 0; i * WAIT_TICKS < sc->timeout; i++) {
313c4bf625eSHasso Tepper 		if (cmx_test_BSR(sc, flags, test))
314c4bf625eSHasso Tepper 			return 0;
315c4bf625eSHasso Tepper 		rv = tsleep(sc, PCATCH, "cmx", WAIT_TICKS);
316c4bf625eSHasso Tepper 		/*
317c4bf625eSHasso Tepper 		 * Currently, the only reason for waking up with
318c4bf625eSHasso Tepper 		 * rv == 0 is when we are detaching, in which
319c4bf625eSHasso Tepper 		 * case sc->dying is always 1.
320c4bf625eSHasso Tepper 		 */
321c4bf625eSHasso Tepper 		if (sc->dying)
322c4bf625eSHasso Tepper 			return ENXIO;
323c4bf625eSHasso Tepper 		if (rv != EAGAIN)
324c4bf625eSHasso Tepper 			return rv;
325c4bf625eSHasso Tepper 	}
326c4bf625eSHasso Tepper 
327c4bf625eSHasso Tepper 	/* timeout */
328c4bf625eSHasso Tepper 	return EIO;
329c4bf625eSHasso Tepper }
330c4bf625eSHasso Tepper 
331c4bf625eSHasso Tepper /*
332c4bf625eSHasso Tepper  * Set the sync control register to val.  Before and after writing
333c4bf625eSHasso Tepper  * to the SCR, we wait for the BSR to not signal BULK_OUT_FULL.
334c4bf625eSHasso Tepper  * Returns zero if successful, or whatever errors cmx_wait_BSR can
335c4bf625eSHasso Tepper  * return.  ENXIO signals that the device has been detached in the
336c4bf625eSHasso Tepper  * meantime, and that we should leave the kernel immediately.
337c4bf625eSHasso Tepper  */
338c4bf625eSHasso Tepper static inline int
cmx_sync_write_SCR(struct cmx_softc * sc,uint8_t val)339c4bf625eSHasso Tepper cmx_sync_write_SCR(struct cmx_softc *sc, uint8_t val)
340c4bf625eSHasso Tepper {
341c4bf625eSHasso Tepper 	int rv = 0;
342c4bf625eSHasso Tepper 
343c4bf625eSHasso Tepper 	if ((rv = cmx_wait_BSR(sc, BSR_BULK_OUT_FULL, 0)) != 0) {
344c4bf625eSHasso Tepper 		return rv;
345c4bf625eSHasso Tepper 	}
346c4bf625eSHasso Tepper 
347c4bf625eSHasso Tepper 	cmx_write_SCR(sc, val);
348c4bf625eSHasso Tepper 
349c4bf625eSHasso Tepper 	if ((rv = cmx_wait_BSR(sc, BSR_BULK_OUT_FULL, 0)) != 0) {
350c4bf625eSHasso Tepper 		return rv;
351c4bf625eSHasso Tepper 	}
352c4bf625eSHasso Tepper 
353c4bf625eSHasso Tepper 	return 0;
354c4bf625eSHasso Tepper }
355c4bf625eSHasso Tepper 
356c4bf625eSHasso Tepper /*
357c4bf625eSHasso Tepper  * Returns a suitable timeout value based on the given command byte.
358c4bf625eSHasso Tepper  * Some commands appear to need longer timeout values than others.
359c4bf625eSHasso Tepper  */
360c4bf625eSHasso Tepper static inline unsigned long
cmx_timeout_by_cmd(uint8_t cmd)361c4bf625eSHasso Tepper cmx_timeout_by_cmd(uint8_t cmd)
362c4bf625eSHasso Tepper {
363c4bf625eSHasso Tepper 	switch (cmd) {
364c4bf625eSHasso Tepper 	case CMD_PC_TO_RDR_XFRBLOCK:
365c4bf625eSHasso Tepper 	case CMD_PC_TO_RDR_SECURE:
366c4bf625eSHasso Tepper 	case CMD_PC_TO_RDR_TEST_SECURE:
367c4bf625eSHasso Tepper 	case CMD_PC_TO_RDR_OK_SECURE:
368c4bf625eSHasso Tepper 		return CCID_DRIVER_BULK_DEFAULT_TIMEOUT;
369c4bf625eSHasso Tepper 
370c4bf625eSHasso Tepper 	case CMD_PC_TO_RDR_ICCPOWERON:
371c4bf625eSHasso Tepper 		return CCID_DRIVER_ASYNC_POWERUP_TIMEOUT;
372c4bf625eSHasso Tepper 
373c4bf625eSHasso Tepper 	case CMD_PC_TO_RDR_GETSLOTSTATUS:
374c4bf625eSHasso Tepper 	case CMD_PC_TO_RDR_ICCPOWEROFF:
375c4bf625eSHasso Tepper 	case CMD_PC_TO_RDR_GETPARAMETERS:
376c4bf625eSHasso Tepper 	case CMD_PC_TO_RDR_RESETPARAMETERS:
377c4bf625eSHasso Tepper 	case CMD_PC_TO_RDR_SETPARAMETERS:
378c4bf625eSHasso Tepper 	case CMD_PC_TO_RDR_ESCAPE:
379c4bf625eSHasso Tepper 	case CMD_PC_TO_RDR_ICCCLOCK:
380c4bf625eSHasso Tepper 	default:
381c4bf625eSHasso Tepper 		return CCID_DRIVER_MINIMUM_TIMEOUT;
382c4bf625eSHasso Tepper 	}
383c4bf625eSHasso Tepper }
384c4bf625eSHasso Tepper 
385c4bf625eSHasso Tepper /*
386c4bf625eSHasso Tepper  * Periodical callout routine, polling the reader for data
387c4bf625eSHasso Tepper  * availability.  If the reader signals data ready for reading,
3885b22f1a7SSamuel J. Greear  * wakes up the processes which are waiting in select()/poll()/kevent().
389c4bf625eSHasso Tepper  * Otherwise, reschedules itself with a delay of POLL_TICKS.
390c4bf625eSHasso Tepper  */
391c4bf625eSHasso Tepper static void
cmx_tick(void * xsc)392c4bf625eSHasso Tepper cmx_tick(void *xsc)
393c4bf625eSHasso Tepper {
394c4bf625eSHasso Tepper 	struct cmx_softc *sc = xsc;
395c4bf625eSHasso Tepper 	uint8_t bsr;
396c4bf625eSHasso Tepper 
397c4bf625eSHasso Tepper 	CMX_LOCK(sc);
398c4bf625eSHasso Tepper 	if (sc->polling && !sc->dying) {
399c4bf625eSHasso Tepper 		bsr = cmx_read_BSR(sc);
400*2f8af9dbSzrj 		DEBUG_printf(sc->dev, "BSR=%pb%i\n", BSRBITS, bsr);
401c4bf625eSHasso Tepper 		if (cmx_test(bsr, BSR_BULK_IN_FULL, 1)) {
402c4bf625eSHasso Tepper 			sc->polling = 0;
4035b22f1a7SSamuel J. Greear 			KNOTE(&sc->kq.ki_note, 0);
404c4bf625eSHasso Tepper 		} else {
405c4bf625eSHasso Tepper 			callout_reset(&sc->ch, POLL_TICKS, cmx_tick, sc);
406c4bf625eSHasso Tepper 		}
407c4bf625eSHasso Tepper 	}
408c4bf625eSHasso Tepper 	CMX_UNLOCK(sc);
409c4bf625eSHasso Tepper }
410c4bf625eSHasso Tepper 
411c4bf625eSHasso Tepper /*
412c4bf625eSHasso Tepper  * Open the character device.  Only a single process may open the
413c4bf625eSHasso Tepper  * device at a time.
414c4bf625eSHasso Tepper  */
415c4bf625eSHasso Tepper static int
cmx_open(struct dev_open_args * ap)416c4bf625eSHasso Tepper cmx_open(struct dev_open_args *ap)
417c4bf625eSHasso Tepper {
418c4bf625eSHasso Tepper 	cdev_t dev = ap->a_head.a_dev;
419c4bf625eSHasso Tepper 	struct cmx_softc *sc;
420c4bf625eSHasso Tepper 
421c4bf625eSHasso Tepper 	sc = devclass_get_softc(cmx_devclass, minor(dev));
422c4bf625eSHasso Tepper 	if (sc == NULL || sc->dying)
423c4bf625eSHasso Tepper 		return ENXIO;
424c4bf625eSHasso Tepper 
425c4bf625eSHasso Tepper 	CMX_LOCK(sc);
426c4bf625eSHasso Tepper 	if (sc->open) {
427c4bf625eSHasso Tepper 		CMX_UNLOCK(sc);
428c4bf625eSHasso Tepper 		return EBUSY;
429c4bf625eSHasso Tepper 	}
430c4bf625eSHasso Tepper 	sc->open = 1;
431c4bf625eSHasso Tepper 	CMX_UNLOCK(sc);
432c4bf625eSHasso Tepper 
433*2f8af9dbSzrj 	DEBUG_printf(sc->dev, "open (flags=%pb%i thread=%p)\n",
434*2f8af9dbSzrj 			MODEBITS, ap->a_oflags, curthread);
435c4bf625eSHasso Tepper 	return 0;
436c4bf625eSHasso Tepper }
437c4bf625eSHasso Tepper 
438c4bf625eSHasso Tepper /*
439c4bf625eSHasso Tepper  * Close the character device.
440c4bf625eSHasso Tepper  */
441c4bf625eSHasso Tepper static int
cmx_close(struct dev_close_args * ap)442c4bf625eSHasso Tepper cmx_close(struct dev_close_args *ap)
443c4bf625eSHasso Tepper {
444c4bf625eSHasso Tepper 	cdev_t dev = ap->a_head.a_dev;
445c4bf625eSHasso Tepper 	struct cmx_softc *sc;
446c4bf625eSHasso Tepper 
447c4bf625eSHasso Tepper 	sc = devclass_get_softc(cmx_devclass, minor(dev));
448c4bf625eSHasso Tepper 	if (sc == NULL || sc->dying)
449c4bf625eSHasso Tepper 		return ENXIO;
450c4bf625eSHasso Tepper 
451c4bf625eSHasso Tepper 	CMX_LOCK(sc);
452c4bf625eSHasso Tepper 	if (!sc->open) {
453c4bf625eSHasso Tepper 		CMX_UNLOCK(sc);
454c4bf625eSHasso Tepper 		return EINVAL;
455c4bf625eSHasso Tepper 	}
456c4bf625eSHasso Tepper 	if (sc->polling) {
457c4bf625eSHasso Tepper 		DEBUG_printf(sc->dev, "disabling polling\n");
458c4bf625eSHasso Tepper 		callout_stop(&sc->ch);
459c4bf625eSHasso Tepper 		sc->polling = 0;
460c4bf625eSHasso Tepper 		CMX_UNLOCK(sc);
4615b22f1a7SSamuel J. Greear 		KNOTE(&sc->kq.ki_note, 0);
462c4bf625eSHasso Tepper 		CMX_LOCK(sc);
463c4bf625eSHasso Tepper 	}
464c4bf625eSHasso Tepper 	sc->open = 0;
465c4bf625eSHasso Tepper 	CMX_UNLOCK(sc);
466c4bf625eSHasso Tepper 
467*2f8af9dbSzrj 	DEBUG_printf(sc->dev, "close (flags=%pb%i thread=%p)\n",
468*2f8af9dbSzrj 			MODEBITS, ap->a_fflag, curthread);
469c4bf625eSHasso Tepper 	return 0;
470c4bf625eSHasso Tepper }
471c4bf625eSHasso Tepper 
472c4bf625eSHasso Tepper /*
473c4bf625eSHasso Tepper  * Read from the character device.
474c4bf625eSHasso Tepper  * Returns zero if successful, ENXIO if dying, EINVAL if an attempt
475c4bf625eSHasso Tepper  * was made to read less than CMX_MIN_RDLEN bytes or less than the
476c4bf625eSHasso Tepper  * device has available, or any of the errors that cmx_sync_write_SCR
477c4bf625eSHasso Tepper  * can return.  Partial reads are not supported.
478c4bf625eSHasso Tepper  */
479c4bf625eSHasso Tepper static int
cmx_read(struct dev_read_args * ap)480c4bf625eSHasso Tepper cmx_read(struct dev_read_args *ap)
481c4bf625eSHasso Tepper {
482c4bf625eSHasso Tepper 	cdev_t dev = ap->a_head.a_dev;
483c4bf625eSHasso Tepper 	struct cmx_softc *sc;
484c4bf625eSHasso Tepper 	struct uio *uio = ap->a_uio;
485c4bf625eSHasso Tepper 	unsigned long bytes_left;
486c4bf625eSHasso Tepper 	uint8_t uc;
487c4bf625eSHasso Tepper 	int rv, amnt, offset;
488c4bf625eSHasso Tepper 
489c4bf625eSHasso Tepper 	sc = devclass_get_softc(cmx_devclass, minor(dev));
490c4bf625eSHasso Tepper 	if (sc == NULL || sc->dying)
491c4bf625eSHasso Tepper 		return ENXIO;
492c4bf625eSHasso Tepper 
493*2f8af9dbSzrj 	DEBUG_printf(sc->dev, "called (len=%d flag=%pb%i)\n",
494*2f8af9dbSzrj 		uio->uio_resid, MODEBITS, ap->a_ioflag);
495c4bf625eSHasso Tepper 
496c4bf625eSHasso Tepper 	CMX_LOCK(sc);
497c4bf625eSHasso Tepper 	if (sc->polling) {
498c4bf625eSHasso Tepper 		DEBUG_printf(sc->dev, "disabling polling\n");
499c4bf625eSHasso Tepper 		callout_stop(&sc->ch);
500c4bf625eSHasso Tepper 		sc->polling = 0;
501c4bf625eSHasso Tepper 		CMX_UNLOCK(sc);
5025b22f1a7SSamuel J. Greear 		KNOTE(&sc->kq.ki_note, 0);
503c4bf625eSHasso Tepper 	} else {
504c4bf625eSHasso Tepper 		CMX_UNLOCK(sc);
505c4bf625eSHasso Tepper 	}
506c4bf625eSHasso Tepper 
507c4bf625eSHasso Tepper 	if (uio->uio_resid == 0) {
508c4bf625eSHasso Tepper 		return 0;
509c4bf625eSHasso Tepper 	}
510c4bf625eSHasso Tepper 
511c4bf625eSHasso Tepper 	if (uio->uio_resid < CMX_MIN_RDLEN) {
512c4bf625eSHasso Tepper 		return EINVAL;
513c4bf625eSHasso Tepper 	}
514c4bf625eSHasso Tepper 
515c4bf625eSHasso Tepper 	if (ap->a_ioflag & O_NONBLOCK) {
516c4bf625eSHasso Tepper 		if (cmx_test_BSR(sc, BSR_BULK_IN_FULL, 0)) {
517c4bf625eSHasso Tepper 			return EAGAIN;
518c4bf625eSHasso Tepper 		}
519c4bf625eSHasso Tepper 	}
520c4bf625eSHasso Tepper 
521c4bf625eSHasso Tepper 	for (int i = 0; i < 5; i++) {
522c4bf625eSHasso Tepper 		if ((rv = cmx_wait_BSR(sc, BSR_BULK_IN_FULL, 1)) != 0) {
523c4bf625eSHasso Tepper 			return rv;
524c4bf625eSHasso Tepper 		}
525c4bf625eSHasso Tepper 		sc->buf[i] = cmx_read_DTR(sc);
526c4bf625eSHasso Tepper 		DEBUG_printf(sc->dev, "buf[%02x]=%02x\n", i, sc->buf[i]);
527c4bf625eSHasso Tepper 	}
528c4bf625eSHasso Tepper 
529c4bf625eSHasso Tepper 	bytes_left = CMX_MIN_RDLEN +
530c4bf625eSHasso Tepper 	                (0x000000FF&((char)sc->buf[1])) +
531c4bf625eSHasso Tepper 	                (0x0000FF00&((char)sc->buf[2] << 8)) +
532c4bf625eSHasso Tepper 	                (0x00FF0000&((char)sc->buf[3] << 16)) +
533c4bf625eSHasso Tepper 	                (0xFF000000&((char)sc->buf[4] << 24));
534c4bf625eSHasso Tepper 	DEBUG_printf(sc->dev, "msgsz=%lu\n", bytes_left);
535c4bf625eSHasso Tepper 
536c4bf625eSHasso Tepper 	if (uio->uio_resid < bytes_left) {
537c4bf625eSHasso Tepper 		return EINVAL;
538c4bf625eSHasso Tepper 	}
539c4bf625eSHasso Tepper 
540c4bf625eSHasso Tepper 	offset = 5; /* prefetched header */
541c4bf625eSHasso Tepper 	while (bytes_left > 0) {
542c4bf625eSHasso Tepper 		amnt = MIN(bytes_left, sizeof(sc->buf));
543c4bf625eSHasso Tepper 
544c4bf625eSHasso Tepper 		for (int i = offset; i < amnt; i++) {
545c4bf625eSHasso Tepper 			if ((rv = cmx_wait_BSR(sc, BSR_BULK_IN_FULL, 1))!=0) {
546c4bf625eSHasso Tepper 				return rv;
547c4bf625eSHasso Tepper 			}
548c4bf625eSHasso Tepper 			sc->buf[i] = cmx_read_DTR(sc);
549c4bf625eSHasso Tepper 			DEBUG_printf(sc->dev, "buf[%02x]=%02x\n",
550c4bf625eSHasso Tepper 					i, sc->buf[i]);
551c4bf625eSHasso Tepper 		}
552c4bf625eSHasso Tepper 
553c4bf625eSHasso Tepper 		if ((rv = uiomove(sc->buf, amnt, uio)) != 0) {
554c4bf625eSHasso Tepper 			DEBUG_printf(sc->dev, "uiomove failed (%d)\n", rv);
555c4bf625eSHasso Tepper 			return rv;
556c4bf625eSHasso Tepper 		}
557c4bf625eSHasso Tepper 
558c4bf625eSHasso Tepper 		if (offset)
559c4bf625eSHasso Tepper 			offset = 0;
560c4bf625eSHasso Tepper 		bytes_left -= amnt;
561c4bf625eSHasso Tepper 	}
562c4bf625eSHasso Tepper 
563c4bf625eSHasso Tepper 	if ((rv = cmx_wait_BSR(sc, BSR_BULK_IN_FULL, 1)) != 0) {
564c4bf625eSHasso Tepper 		return rv;
565c4bf625eSHasso Tepper 	}
566c4bf625eSHasso Tepper 
567c4bf625eSHasso Tepper 	if ((rv = cmx_sync_write_SCR(sc, SCR_READER_TO_HOST_DONE)) != 0) {
568c4bf625eSHasso Tepper 		return rv;
569c4bf625eSHasso Tepper 	}
570c4bf625eSHasso Tepper 
571c4bf625eSHasso Tepper 	uc = cmx_read_DTR(sc);
572c4bf625eSHasso Tepper 	DEBUG_printf(sc->dev, "success (DTR=%02x)\n", uc);
573c4bf625eSHasso Tepper 	return 0;
574c4bf625eSHasso Tepper }
575c4bf625eSHasso Tepper 
576c4bf625eSHasso Tepper /*
577c4bf625eSHasso Tepper  * Write to the character device.
578c4bf625eSHasso Tepper  * Returns zero if successful, NXIO if dying, EINVAL if less data
579c4bf625eSHasso Tepper  * written than CMX_MIN_WRLEN, or any of the errors that cmx_sync_SCR
580c4bf625eSHasso Tepper  * can return.
581c4bf625eSHasso Tepper  */
582c4bf625eSHasso Tepper static int
cmx_write(struct dev_write_args * ap)583c4bf625eSHasso Tepper cmx_write(struct dev_write_args *ap)
584c4bf625eSHasso Tepper {
585c4bf625eSHasso Tepper 	cdev_t dev = ap->a_head.a_dev;
586c4bf625eSHasso Tepper 	struct cmx_softc *sc;
587c4bf625eSHasso Tepper 	struct uio *uio = ap->a_uio;
588c4bf625eSHasso Tepper 	int rv, amnt;
589c4bf625eSHasso Tepper 
590c4bf625eSHasso Tepper 	sc = devclass_get_softc(cmx_devclass, minor(dev));
591c4bf625eSHasso Tepper 	if (sc == NULL || sc->dying)
592c4bf625eSHasso Tepper 		return ENXIO;
593c4bf625eSHasso Tepper 
594*2f8af9dbSzrj 	DEBUG_printf(sc->dev, "called (len=%d flag=%pb%i)\n",
595*2f8af9dbSzrj 			uio->uio_resid, MODEBITS, ap->a_ioflag);
596c4bf625eSHasso Tepper 
597c4bf625eSHasso Tepper 	if (uio->uio_resid == 0) {
598c4bf625eSHasso Tepper 		return 0;
599c4bf625eSHasso Tepper 	}
600c4bf625eSHasso Tepper 
601c4bf625eSHasso Tepper 	if (uio->uio_resid < CMX_MIN_WRLEN) {
602c4bf625eSHasso Tepper 		return EINVAL;
603c4bf625eSHasso Tepper 	}
604c4bf625eSHasso Tepper 
605c4bf625eSHasso Tepper 	if ((rv = cmx_sync_write_SCR(sc, SCR_HOST_TO_READER_START)) != 0) {
606c4bf625eSHasso Tepper 		return rv;
607c4bf625eSHasso Tepper 	}
608c4bf625eSHasso Tepper 
609c4bf625eSHasso Tepper 	sc->timeout = 0;
610c4bf625eSHasso Tepper 	while (uio->uio_resid > 0) {
611c4bf625eSHasso Tepper 		amnt = MIN(uio->uio_resid, sizeof(sc->buf));
612c4bf625eSHasso Tepper 
613c4bf625eSHasso Tepper 		if ((rv = uiomove(sc->buf, amnt, uio)) != 0) {
614c4bf625eSHasso Tepper 			DEBUG_printf(sc->dev, "uiomove failed (%d)\n", rv);
615c4bf625eSHasso Tepper 			/* wildly guessed attempt to notify device */
616c4bf625eSHasso Tepper 			sc->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
617c4bf625eSHasso Tepper 			cmx_sync_write_SCR(sc, SCR_HOST_TO_READER_DONE);
618c4bf625eSHasso Tepper 			return rv;
619c4bf625eSHasso Tepper 		}
620c4bf625eSHasso Tepper 
621c4bf625eSHasso Tepper 		if (sc->timeout == 0) {
622c4bf625eSHasso Tepper 			sc->timeout = cmx_timeout_by_cmd(sc->buf[0]);
623c4bf625eSHasso Tepper 			DEBUG_printf(sc->dev, "cmd=%02x timeout=%lu\n",
624c4bf625eSHasso Tepper 					sc->buf[0], sc->timeout);
625c4bf625eSHasso Tepper 		}
626c4bf625eSHasso Tepper 
627c4bf625eSHasso Tepper 		for (int i = 0; i < amnt; i++) {
628c4bf625eSHasso Tepper 			if ((rv = cmx_wait_BSR(sc, BSR_BULK_OUT_FULL, 0))!=0) {
629c4bf625eSHasso Tepper 				return rv;
630c4bf625eSHasso Tepper 			}
631c4bf625eSHasso Tepper 			cmx_write_DTR(sc, sc->buf[i]);
632c4bf625eSHasso Tepper 			DEBUG_printf(sc->dev, "buf[%02x]=%02x\n",
633c4bf625eSHasso Tepper 					i, sc->buf[i]);
634c4bf625eSHasso Tepper 		}
635c4bf625eSHasso Tepper 	}
636c4bf625eSHasso Tepper 
637c4bf625eSHasso Tepper 	if ((rv = cmx_sync_write_SCR(sc, SCR_HOST_TO_READER_DONE)) != 0) {
638c4bf625eSHasso Tepper 		return rv;
639c4bf625eSHasso Tepper 	}
640c4bf625eSHasso Tepper 
641c4bf625eSHasso Tepper 	DEBUG_printf(sc->dev, "success\n");
642c4bf625eSHasso Tepper 	return 0;
643c4bf625eSHasso Tepper }
644c4bf625eSHasso Tepper 
6453546e044SSamuel J. Greear static struct filterops cmx_read_filterops =
6464c91dbc9SSamuel J. Greear 	{ FILTEROP_ISFD, NULL, cmx_filter_detach, cmx_filter_read };
6473546e044SSamuel J. Greear static struct filterops cmx_write_filterops =
6484c91dbc9SSamuel J. Greear 	{ FILTEROP_ISFD, NULL, cmx_filter_detach, cmx_filter_write };
6493546e044SSamuel J. Greear 
6503546e044SSamuel J. Greear /*
6513546e044SSamuel J. Greear  * Kevent handler.  Writing is always possible, reading is only possible
6523546e044SSamuel J. Greear  * if BSR_BULK_IN_FULL is set.  Will start the cmx_tick callout and
6533546e044SSamuel J. Greear  * set sc->polling.
6543546e044SSamuel J. Greear  */
6553546e044SSamuel J. Greear static int
cmx_kqfilter(struct dev_kqfilter_args * ap)6563546e044SSamuel J. Greear cmx_kqfilter(struct dev_kqfilter_args *ap)
6573546e044SSamuel J. Greear {
6583546e044SSamuel J. Greear 	cdev_t dev = ap->a_head.a_dev;
6593546e044SSamuel J. Greear 	struct knote *kn = ap->a_kn;
6603546e044SSamuel J. Greear 	struct cmx_softc *sc;
6613546e044SSamuel J. Greear 	struct klist *klist;
6623546e044SSamuel J. Greear 
6633546e044SSamuel J. Greear 	ap->a_result = 0;
6643546e044SSamuel J. Greear 
6653546e044SSamuel J. Greear 	sc = devclass_get_softc(cmx_devclass, minor(dev));
6663546e044SSamuel J. Greear 
6673546e044SSamuel J. Greear 	switch (kn->kn_filter) {
6683546e044SSamuel J. Greear 	case EVFILT_READ:
6693546e044SSamuel J. Greear 		kn->kn_fop = &cmx_read_filterops;
6703546e044SSamuel J. Greear 		kn->kn_hook = (caddr_t)sc;
6713546e044SSamuel J. Greear 		break;
6723546e044SSamuel J. Greear 	case EVFILT_WRITE:
6733546e044SSamuel J. Greear 		kn->kn_fop = &cmx_write_filterops;
6743546e044SSamuel J. Greear 		kn->kn_hook = (caddr_t)sc;
6753546e044SSamuel J. Greear 		break;
6763546e044SSamuel J. Greear 	default:
677b287d649SMatthew Dillon 		ap->a_result = EOPNOTSUPP;
6783546e044SSamuel J. Greear 		return (0);
6793546e044SSamuel J. Greear 	}
6803546e044SSamuel J. Greear 
6815b22f1a7SSamuel J. Greear 	klist = &sc->kq.ki_note;
6825b22f1a7SSamuel J. Greear 	knote_insert(klist, kn);
6833546e044SSamuel J. Greear 
6843546e044SSamuel J. Greear 	return (0);
6853546e044SSamuel J. Greear }
6863546e044SSamuel J. Greear 
6873546e044SSamuel J. Greear static void
cmx_filter_detach(struct knote * kn)6883546e044SSamuel J. Greear cmx_filter_detach(struct knote *kn)
6893546e044SSamuel J. Greear {
6903546e044SSamuel J. Greear 	struct cmx_softc *sc = (struct cmx_softc *)kn->kn_hook;
6915b22f1a7SSamuel J. Greear 	struct klist *klist = &sc->kq.ki_note;
6923546e044SSamuel J. Greear 
6935b22f1a7SSamuel J. Greear 	knote_remove(klist, kn);
6943546e044SSamuel J. Greear }
6953546e044SSamuel J. Greear 
6963546e044SSamuel J. Greear static int
cmx_filter_read(struct knote * kn,long hint)6973546e044SSamuel J. Greear cmx_filter_read(struct knote *kn, long hint)
6983546e044SSamuel J. Greear {
6993546e044SSamuel J. Greear 	struct cmx_softc *sc = (struct cmx_softc *)kn->kn_hook;
7003546e044SSamuel J. Greear 	int ready = 0;
7013546e044SSamuel J. Greear         uint8_t bsr = 0;
7023546e044SSamuel J. Greear 
7033546e044SSamuel J. Greear         if (sc == NULL || sc->dying) {
7043bcb6e5eSSepherosa Ziehau 		kn->kn_flags |= (EV_EOF | EV_NODATA);
7053546e044SSamuel J. Greear                 return (1);
7063546e044SSamuel J. Greear 	}
7073546e044SSamuel J. Greear 
7083546e044SSamuel J. Greear         bsr = cmx_read_BSR(sc);
7093546e044SSamuel J. Greear 	if (cmx_test(bsr, BSR_BULK_IN_FULL, 1)) {
7103546e044SSamuel J. Greear 		ready = 1;
7113546e044SSamuel J. Greear 	} else {
7123546e044SSamuel J. Greear 		CMX_LOCK(sc);
7133546e044SSamuel J. Greear 		if (!sc->polling) {
7143546e044SSamuel J. Greear 			sc->polling = 1;
7153546e044SSamuel J. Greear 			callout_reset(&sc->ch, POLL_TICKS,
7163546e044SSamuel J. Greear 				      cmx_tick, sc);
7173546e044SSamuel J. Greear 		}
7183546e044SSamuel J. Greear 		CMX_UNLOCK(sc);
7193546e044SSamuel J. Greear 	}
7203546e044SSamuel J. Greear 
7213546e044SSamuel J. Greear 	return (ready);
7223546e044SSamuel J. Greear }
7233546e044SSamuel J. Greear 
7243546e044SSamuel J. Greear static int
cmx_filter_write(struct knote * kn,long hint)7253546e044SSamuel J. Greear cmx_filter_write(struct knote *kn, long hint)
7263546e044SSamuel J. Greear {
7273546e044SSamuel J. Greear 	return (1);
7283546e044SSamuel J. Greear }
7293546e044SSamuel J. Greear 
730c4bf625eSHasso Tepper #ifdef CMX_INTR
731c4bf625eSHasso Tepper /*
732c4bf625eSHasso Tepper  * Interrupt handler.  Currently has no function except to
733c4bf625eSHasso Tepper  * print register status (if debugging is also enabled).
734c4bf625eSHasso Tepper  */
735c4bf625eSHasso Tepper static void
cmx_intr(void * arg)736c4bf625eSHasso Tepper cmx_intr(void *arg)
737c4bf625eSHasso Tepper {
738c4bf625eSHasso Tepper 	struct cmx_softc *sc = (struct cmx_softc *)arg;
739c4bf625eSHasso Tepper 
740c4bf625eSHasso Tepper 	if (sc == NULL || sc->dying)
741c4bf625eSHasso Tepper 		return;
742c4bf625eSHasso Tepper 
743*2f8af9dbSzrj 	DEBUG_printf(sc->dev, "received interrupt (SCR=%pb%i BSR=%pb%i)\n",
744*2f8af9dbSzrj 			SCRBITS, cmx_read_SCR(sc),
745*2f8af9dbSzrj 			BSRBITS, cmx_read_BSR(sc));
746c4bf625eSHasso Tepper 
747c4bf625eSHasso Tepper 	return;
748c4bf625eSHasso Tepper }
749c4bf625eSHasso Tepper #endif
750c4bf625eSHasso Tepper 
751