1/* $NetBSD: ualea.c,v 1.19 2022/03/20 13:18:30 riastradh Exp $ */ 2 3/*- 4 * Copyright (c) 2017 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Taylor R. Campbell. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33__KERNEL_RCSID(0, "$NetBSD: ualea.c,v 1.19 2022/03/20 13:18:30 riastradh Exp $"); 34 35#include <sys/types.h> 36#include <sys/atomic.h> 37#include <sys/device_if.h> 38#include <sys/kmem.h> 39#include <sys/module.h> 40#include <sys/rndsource.h> 41 42#include <dev/usb/usb.h> 43#include <dev/usb/usbdevs.h> 44#include <dev/usb/usbdi.h> 45#include <dev/usb/usbdi_util.h> 46 47struct ualea_softc { 48 device_t sc_dev; 49 kmutex_t sc_lock; 50 krndsource_t sc_rnd; 51 uint16_t sc_maxpktsize; 52 struct usbd_pipe *sc_pipe; 53 /* 54 * Lock covers: 55 * - sc_needed 56 * - sc_inflight 57 * - usbd_transfer(sc_xfer) 58 */ 59 struct usbd_xfer *sc_xfer; 60 size_t sc_needed; 61 bool sc_attached:1; 62 bool sc_inflight:1; 63}; 64 65static int ualea_match(device_t, cfdata_t, void *); 66static void ualea_attach(device_t, device_t, void *); 67static int ualea_detach(device_t, int); 68static void ualea_get(size_t, void *); 69static void ualea_xfer_done(struct usbd_xfer *, void *, usbd_status); 70 71CFATTACH_DECL_NEW(ualea, sizeof(struct ualea_softc), 72 ualea_match, ualea_attach, ualea_detach, NULL); 73 74static const struct usb_devno ualea_devs[] = { 75 { USB_VENDOR_ARANEUS, USB_PRODUCT_ARANEUS_ALEA }, 76}; 77 78static int 79ualea_match(device_t parent, cfdata_t match, void *aux) 80{ 81 struct usbif_attach_arg *uiaa = aux; 82 83 if (usb_lookup(ualea_devs, uiaa->uiaa_vendor, uiaa->uiaa_product)) 84 return UMATCH_VENDOR_PRODUCT; 85 86 return UMATCH_NONE; 87} 88 89static void 90ualea_attach(device_t parent, device_t self, void *aux) 91{ 92 struct usbif_attach_arg *uiaa = aux; 93 struct ualea_softc *sc = device_private(self); 94 const usb_endpoint_descriptor_t *ed; 95 char *devinfop; 96 usbd_status status; 97 98 /* Print the device info. */ 99 aprint_naive("\n"); 100 aprint_normal("\n"); 101 devinfop = usbd_devinfo_alloc(uiaa->uiaa_device, 0); 102 aprint_normal_dev(self, "%s\n", devinfop); 103 usbd_devinfo_free(devinfop); 104 105 /* Initialize the softc. */ 106 sc->sc_dev = self; 107 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTSERIAL); 108 109 /* Get endpoint descriptor 0. Make sure it's bulk-in. */ 110 ed = usbd_interface2endpoint_descriptor(uiaa->uiaa_iface, 0); 111 if (ed == NULL) { 112 aprint_error_dev(sc->sc_dev, "failed to read endpoint 0\n"); 113 return; 114 } 115 if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || 116 UE_GET_XFERTYPE(ed->bmAttributes) != UE_BULK) { 117 aprint_error_dev(sc->sc_dev, "invalid endpoint\n"); 118 return; 119 } 120 121 /* Remember the maximum packet size. */ 122 sc->sc_maxpktsize = UGETW(ed->wMaxPacketSize); 123 124 /* Open an exclusive MP-safe pipe for endpoint 0. */ 125 status = usbd_open_pipe(uiaa->uiaa_iface, ed->bEndpointAddress, 126 USBD_EXCLUSIVE_USE|USBD_MPSAFE, &sc->sc_pipe); 127 if (status) { 128 aprint_error_dev(sc->sc_dev, "failed to open pipe: %d\n", 129 status); 130 return; 131 } 132 133 /* Create an xfer of maximum packet size on the pipe. */ 134 status = usbd_create_xfer(sc->sc_pipe, sc->sc_maxpktsize, 135 0, 0, &sc->sc_xfer); 136 if (status) { 137 aprint_error_dev(sc->sc_dev, "failed to create xfer: %d\n", 138 status); 139 return; 140 } 141 142 if (!pmf_device_register(self, NULL, NULL)) 143 aprint_error_dev(sc->sc_dev, "failed to register power handler" 144 "\n"); 145 146 /* Success! We are ready to run. */ 147 sc->sc_attached = true; 148 rndsource_setcb(&sc->sc_rnd, ualea_get, sc); 149 rnd_attach_source(&sc->sc_rnd, device_xname(self), RND_TYPE_RNG, 150 RND_FLAG_COLLECT_VALUE|RND_FLAG_HASCB); 151} 152 153static int 154ualea_detach(device_t self, int flags) 155{ 156 struct ualea_softc *sc = device_private(self); 157 158 /* Prevent new use of xfer. */ 159 if (sc->sc_attached) 160 rnd_detach_source(&sc->sc_rnd); 161 162 /* Prevent xfer from rescheduling itself, if still pending. */ 163 mutex_enter(&sc->sc_lock); 164 sc->sc_needed = 0; 165 mutex_exit(&sc->sc_lock); 166 167 /* Cancel pending xfer. */ 168 if (sc->sc_pipe) 169 usbd_abort_pipe(sc->sc_pipe); 170 KASSERT(!sc->sc_inflight); 171 172 /* All users have drained. Tear it all down. */ 173 if (sc->sc_xfer) 174 usbd_destroy_xfer(sc->sc_xfer); 175 if (sc->sc_pipe) 176 usbd_close_pipe(sc->sc_pipe); 177 mutex_destroy(&sc->sc_lock); 178 179 return 0; 180} 181 182static void 183ualea_xfer(struct ualea_softc *sc) 184{ 185 usbd_status status; 186 187 KASSERT(mutex_owned(&sc->sc_lock)); 188 KASSERT(sc->sc_attached); 189 KASSERT(!sc->sc_inflight); 190 191 /* Do nothing if we need nothing. */ 192 if (sc->sc_needed == 0) 193 return; 194 195 /* Setup the xfer to call ualea_xfer_done with sc. */ 196 usbd_setup_xfer(sc->sc_xfer, sc, usbd_get_buffer(sc->sc_xfer), 197 sc->sc_maxpktsize, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, 198 ualea_xfer_done); 199 200 /* Issue xfer or complain if we can't. */ 201 status = usbd_transfer(sc->sc_xfer); 202 KASSERT(status != USBD_NORMAL_COMPLETION); /* asynchronous xfer */ 203 if (status != USBD_IN_PROGRESS) { 204 device_printf(sc->sc_dev, "failed to issue xfer: %s\n", 205 usbd_errstr(status)); 206 /* We failed -- let someone else have a go. */ 207 return; 208 } 209 210 /* Mark xfer in-flight. */ 211 sc->sc_inflight = true; 212} 213 214static void 215ualea_get(size_t nbytes, void *cookie) 216{ 217 struct ualea_softc *sc = cookie; 218 219 mutex_enter(&sc->sc_lock); 220 sc->sc_needed = MAX(sc->sc_needed, nbytes); 221 if (!sc->sc_inflight) 222 ualea_xfer(sc); 223 mutex_exit(&sc->sc_lock); 224} 225 226static void 227ualea_xfer_done(struct usbd_xfer *xfer, void *cookie, usbd_status status) 228{ 229 struct ualea_softc *sc = cookie; 230 void *pkt; 231 uint32_t pktsize; 232 233 /* 234 * If the transfer failed, give up -- forget what we need and 235 * don't reschedule ourselves. 236 */ 237 if (status) { 238 device_printf(sc->sc_dev, "xfer failed: %s\n", 239 usbd_errstr(status)); 240 mutex_enter(&sc->sc_lock); 241 sc->sc_needed = 0; 242 sc->sc_inflight = false; 243 mutex_exit(&sc->sc_lock); 244 return; 245 } 246 247 /* Get the transferred size. */ 248 usbd_get_xfer_status(xfer, NULL, &pkt, &pktsize, NULL); 249 KASSERTMSG(pktsize <= sc->sc_maxpktsize, 250 "pktsize %"PRIu32" > %"PRIu16" (max)", 251 pktsize, sc->sc_maxpktsize); 252 253 /* 254 * Enter the data, debit what we contributed from what we need, 255 * mark the xfer as done, and reschedule the xfer if we still 256 * need more. 257 * 258 * Must enter the data under the lock so it happens atomically 259 * with updating sc_needed -- otherwise we might hang needing 260 * entropy and not scheduling xfer. Must not touch pkt after 261 * clearing sc_inflight and possibly rescheduling the xfer. 262 */ 263 mutex_enter(&sc->sc_lock); 264 rnd_add_data(&sc->sc_rnd, pkt, pktsize, NBBY*pktsize); 265 sc->sc_needed -= MIN(sc->sc_needed, pktsize); 266 sc->sc_inflight = false; 267 ualea_xfer(sc); 268 mutex_exit(&sc->sc_lock); 269} 270 271MODULE(MODULE_CLASS_DRIVER, ualea, NULL); 272 273#ifdef _MODULE 274#include "ioconf.c" 275#endif 276 277static int 278ualea_modcmd(modcmd_t cmd, void *aux) 279{ 280 int error = 0; 281 282 switch (cmd) { 283 case MODULE_CMD_INIT: 284#ifdef _MODULE 285 error = config_init_component(cfdriver_ioconf_ualea, 286 cfattach_ioconf_ualea, cfdata_ioconf_ualea); 287#endif 288 return error; 289 case MODULE_CMD_FINI: 290#ifdef _MODULE 291 error = config_fini_component(cfdriver_ioconf_ualea, 292 cfattach_ioconf_ualea, cfdata_ioconf_ualea); 293#endif 294 return error; 295 default: 296 return ENOTTY; 297 } 298} 299