1204076Spjd/* $NetBSD: ualea.c,v 1.19 2022/03/20 13:18:30 riastradh Exp $ */ 2204076Spjd 3211877Spjd/*- 4204076Spjd * Copyright (c) 2017 The NetBSD Foundation, Inc. 5204076Spjd * All rights reserved. 6204076Spjd * 7204076Spjd * This code is derived from software contributed to The NetBSD Foundation 8204076Spjd * by Taylor R. Campbell. 9204076Spjd * 10204076Spjd * Redistribution and use in source and binary forms, with or without 11204076Spjd * modification, are permitted provided that the following conditions 12204076Spjd * are met: 13204076Spjd * 1. Redistributions of source code must retain the above copyright 14204076Spjd * notice, this list of conditions and the following disclaimer. 15204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 16204076Spjd * notice, this list of conditions and the following disclaimer in the 17204076Spjd * documentation and/or other materials provided with the distribution. 18204076Spjd * 19204076Spjd * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20204076Spjd * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21204076Spjd * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22204076Spjd * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23204076Spjd * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24204076Spjd * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25204076Spjd * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26204076Spjd * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27204076Spjd * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28204076Spjd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29204076Spjd * POSSIBILITY OF SUCH DAMAGE. 30204076Spjd */ 31204076Spjd 32204076Spjd#include <sys/cdefs.h> 33204076Spjd__KERNEL_RCSID(0, "$NetBSD: ualea.c,v 1.19 2022/03/20 13:18:30 riastradh Exp $"); 34204076Spjd 35204076Spjd#include <sys/types.h> 36204076Spjd#include <sys/atomic.h> 37204076Spjd#include <sys/device_if.h> 38204076Spjd#include <sys/kmem.h> 39204076Spjd#include <sys/module.h> 40204076Spjd#include <sys/rndsource.h> 41204076Spjd 42204076Spjd#include <dev/usb/usb.h> 43204076Spjd#include <dev/usb/usbdevs.h> 44204076Spjd#include <dev/usb/usbdi.h> 45204076Spjd#include <dev/usb/usbdi_util.h> 46204076Spjd 47204076Spjdstruct ualea_softc { 48204076Spjd device_t sc_dev; 49204076Spjd kmutex_t sc_lock; 50204076Spjd krndsource_t sc_rnd; 51204076Spjd uint16_t sc_maxpktsize; 52204076Spjd struct usbd_pipe *sc_pipe; 53204076Spjd /* 54204076Spjd * Lock covers: 55204076Spjd * - sc_needed 56204076Spjd * - sc_inflight 57204076Spjd * - usbd_transfer(sc_xfer) 58204076Spjd */ 59204076Spjd struct usbd_xfer *sc_xfer; 60204076Spjd size_t sc_needed; 61204076Spjd bool sc_attached:1; 62204076Spjd bool sc_inflight:1; 63204076Spjd}; 64204076Spjd 65204076Spjdstatic int ualea_match(device_t, cfdata_t, void *); 66204076Spjdstatic void ualea_attach(device_t, device_t, void *); 67204076Spjdstatic int ualea_detach(device_t, int); 68204076Spjdstatic void ualea_get(size_t, void *); 69204076Spjdstatic void ualea_xfer_done(struct usbd_xfer *, void *, usbd_status); 70204076Spjd 71204076SpjdCFATTACH_DECL_NEW(ualea, sizeof(struct ualea_softc), 72204076Spjd ualea_match, ualea_attach, ualea_detach, NULL); 73204076Spjd 74204076Spjdstatic const struct usb_devno ualea_devs[] = { 75204076Spjd { USB_VENDOR_ARANEUS, USB_PRODUCT_ARANEUS_ALEA }, 76204076Spjd}; 77204076Spjd 78204076Spjdstatic int 79204076Spjdualea_match(device_t parent, cfdata_t match, void *aux) 80204076Spjd{ 81204076Spjd struct usbif_attach_arg *uiaa = aux; 82204076Spjd 83204076Spjd if (usb_lookup(ualea_devs, uiaa->uiaa_vendor, uiaa->uiaa_product)) 84204076Spjd return UMATCH_VENDOR_PRODUCT; 85204076Spjd 86204076Spjd return UMATCH_NONE; 87204076Spjd} 88204076Spjd 89204076Spjdstatic void 90204076Spjdualea_attach(device_t parent, device_t self, void *aux) 91204076Spjd{ 92204076Spjd struct usbif_attach_arg *uiaa = aux; 93204076Spjd struct ualea_softc *sc = device_private(self); 94204076Spjd const usb_endpoint_descriptor_t *ed; 95204076Spjd char *devinfop; 96204076Spjd usbd_status status; 97204076Spjd 98204076Spjd /* Print the device info. */ 99204076Spjd aprint_naive("\n"); 100204076Spjd aprint_normal("\n"); 101204076Spjd devinfop = usbd_devinfo_alloc(uiaa->uiaa_device, 0); 102204076Spjd aprint_normal_dev(self, "%s\n", devinfop); 103204076Spjd usbd_devinfo_free(devinfop); 104204076Spjd 105204076Spjd /* Initialize the softc. */ 106211877Spjd sc->sc_dev = self; 107211877Spjd mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTSERIAL); 108211877Spjd 109211877Spjd /* Get endpoint descriptor 0. Make sure it's bulk-in. */ 110211877Spjd ed = usbd_interface2endpoint_descriptor(uiaa->uiaa_iface, 0); 111211877Spjd if (ed == NULL) { 112211877Spjd aprint_error_dev(sc->sc_dev, "failed to read endpoint 0\n"); 113211877Spjd return; 114211877Spjd } 115211877Spjd if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || 116211877Spjd UE_GET_XFERTYPE(ed->bmAttributes) != UE_BULK) { 117211877Spjd aprint_error_dev(sc->sc_dev, "invalid endpoint\n"); 118211877Spjd return; 119211877Spjd } 120211877Spjd 121211877Spjd /* Remember the maximum packet size. */ 122211877Spjd sc->sc_maxpktsize = UGETW(ed->wMaxPacketSize); 123211877Spjd 124211877Spjd /* Open an exclusive MP-safe pipe for endpoint 0. */ 125211877Spjd status = usbd_open_pipe(uiaa->uiaa_iface, ed->bEndpointAddress, 126204076Spjd USBD_EXCLUSIVE_USE|USBD_MPSAFE, &sc->sc_pipe); 127204076Spjd if (status) { 128204076Spjd aprint_error_dev(sc->sc_dev, "failed to open pipe: %d\n", 129204076Spjd status); 130204076Spjd return; 131204076Spjd } 132204076Spjd 133204076Spjd /* Create an xfer of maximum packet size on the pipe. */ 134204076Spjd status = usbd_create_xfer(sc->sc_pipe, sc->sc_maxpktsize, 135204076Spjd 0, 0, &sc->sc_xfer); 136204076Spjd if (status) { 137204076Spjd aprint_error_dev(sc->sc_dev, "failed to create xfer: %d\n", 138204076Spjd status); 139204076Spjd return; 140204076Spjd } 141204076Spjd 142204076Spjd if (!pmf_device_register(self, NULL, NULL)) 143204076Spjd aprint_error_dev(sc->sc_dev, "failed to register power handler" 144204076Spjd "\n"); 145204076Spjd 146204076Spjd /* Success! We are ready to run. */ 147204076Spjd sc->sc_attached = true; 148204076Spjd rndsource_setcb(&sc->sc_rnd, ualea_get, sc); 149204076Spjd rnd_attach_source(&sc->sc_rnd, device_xname(self), RND_TYPE_RNG, 150204076Spjd RND_FLAG_COLLECT_VALUE|RND_FLAG_HASCB); 151210879Spjd} 152210879Spjd 153210879Spjdstatic int 154204076Spjdualea_detach(device_t self, int flags) 155204076Spjd{ 156204076Spjd struct ualea_softc *sc = device_private(self); 157204076Spjd 158210879Spjd /* Prevent new use of xfer. */ 159210879Spjd if (sc->sc_attached) 160210879Spjd rnd_detach_source(&sc->sc_rnd); 161204076Spjd 162204076Spjd /* Prevent xfer from rescheduling itself, if still pending. */ 163204076Spjd mutex_enter(&sc->sc_lock); 164204076Spjd sc->sc_needed = 0; 165204076Spjd mutex_exit(&sc->sc_lock); 166204076Spjd 167204076Spjd /* Cancel pending xfer. */ 168204076Spjd if (sc->sc_pipe) 169204076Spjd usbd_abort_pipe(sc->sc_pipe); 170204076Spjd KASSERT(!sc->sc_inflight); 171204076Spjd 172204076Spjd /* All users have drained. Tear it all down. */ 173204076Spjd if (sc->sc_xfer) 174204076Spjd usbd_destroy_xfer(sc->sc_xfer); 175204076Spjd if (sc->sc_pipe) 176204076Spjd usbd_close_pipe(sc->sc_pipe); 177204076Spjd mutex_destroy(&sc->sc_lock); 178204076Spjd 179204076Spjd return 0; 180204076Spjd} 181204076Spjd 182204076Spjdstatic void 183204076Spjdualea_xfer(struct ualea_softc *sc) 184204076Spjd{ 185204076Spjd usbd_status status; 186204076Spjd 187204076Spjd KASSERT(mutex_owned(&sc->sc_lock)); 188204076Spjd KASSERT(sc->sc_attached); 189204076Spjd KASSERT(!sc->sc_inflight); 190204076Spjd 191204076Spjd /* Do nothing if we need nothing. */ 192204076Spjd if (sc->sc_needed == 0) 193204076Spjd return; 194204076Spjd 195204076Spjd /* Setup the xfer to call ualea_xfer_done with sc. */ 196204076Spjd usbd_setup_xfer(sc->sc_xfer, sc, usbd_get_buffer(sc->sc_xfer), 197204076Spjd sc->sc_maxpktsize, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, 198204076Spjd ualea_xfer_done); 199204076Spjd 200204076Spjd /* Issue xfer or complain if we can't. */ 201204076Spjd status = usbd_transfer(sc->sc_xfer); 202204076Spjd KASSERT(status != USBD_NORMAL_COMPLETION); /* asynchronous xfer */ 203204076Spjd if (status != USBD_IN_PROGRESS) { 204204076Spjd device_printf(sc->sc_dev, "failed to issue xfer: %s\n", 205204076Spjd usbd_errstr(status)); 206204076Spjd /* We failed -- let someone else have a go. */ 207204076Spjd return; 208204076Spjd } 209204076Spjd 210204076Spjd /* Mark xfer in-flight. */ 211204076Spjd sc->sc_inflight = true; 212204076Spjd} 213204076Spjd 214204076Spjdstatic void 215204076Spjdualea_get(size_t nbytes, void *cookie) 216204076Spjd{ 217204076Spjd struct ualea_softc *sc = cookie; 218204076Spjd 219204076Spjd mutex_enter(&sc->sc_lock); 220204076Spjd sc->sc_needed = MAX(sc->sc_needed, nbytes); 221204076Spjd if (!sc->sc_inflight) 222204076Spjd ualea_xfer(sc); 223204076Spjd mutex_exit(&sc->sc_lock); 224204076Spjd} 225204076Spjd 226204076Spjdstatic void 227204076Spjdualea_xfer_done(struct usbd_xfer *xfer, void *cookie, usbd_status status) 228204076Spjd{ 229204076Spjd struct ualea_softc *sc = cookie; 230204076Spjd void *pkt; 231204076Spjd uint32_t pktsize; 232204076Spjd 233204076Spjd /* 234204076Spjd * If the transfer failed, give up -- forget what we need and 235204076Spjd * don't reschedule ourselves. 236204076Spjd */ 237204076Spjd if (status) { 238204076Spjd device_printf(sc->sc_dev, "xfer failed: %s\n", 239204076Spjd usbd_errstr(status)); 240204076Spjd mutex_enter(&sc->sc_lock); 241204076Spjd sc->sc_needed = 0; 242204076Spjd sc->sc_inflight = false; 243204076Spjd mutex_exit(&sc->sc_lock); 244204076Spjd return; 245204076Spjd } 246204076Spjd 247204076Spjd /* Get the transferred size. */ 248204076Spjd usbd_get_xfer_status(xfer, NULL, &pkt, &pktsize, NULL); 249204076Spjd KASSERTMSG(pktsize <= sc->sc_maxpktsize, 250204076Spjd "pktsize %"PRIu32" > %"PRIu16" (max)", 251204076Spjd pktsize, sc->sc_maxpktsize); 252204076Spjd 253204076Spjd /* 254204076Spjd * Enter the data, debit what we contributed from what we need, 255204076Spjd * mark the xfer as done, and reschedule the xfer if we still 256204076Spjd * need more. 257204076Spjd * 258204076Spjd * Must enter the data under the lock so it happens atomically 259204076Spjd * with updating sc_needed -- otherwise we might hang needing 260204076Spjd * entropy and not scheduling xfer. Must not touch pkt after 261204076Spjd * clearing sc_inflight and possibly rescheduling the xfer. 262204076Spjd */ 263204076Spjd mutex_enter(&sc->sc_lock); 264204076Spjd rnd_add_data(&sc->sc_rnd, pkt, pktsize, NBBY*pktsize); 265204076Spjd sc->sc_needed -= MIN(sc->sc_needed, pktsize); 266204076Spjd sc->sc_inflight = false; 267204076Spjd ualea_xfer(sc); 268204076Spjd mutex_exit(&sc->sc_lock); 269204076Spjd} 270204076Spjd 271204076SpjdMODULE(MODULE_CLASS_DRIVER, ualea, NULL); 272204076Spjd 273204076Spjd#ifdef _MODULE 274204076Spjd#include "ioconf.c" 275204076Spjd#endif 276204076Spjd 277204076Spjdstatic int 278204076Spjdualea_modcmd(modcmd_t cmd, void *aux) 279204076Spjd{ 280204076Spjd int error = 0; 281204076Spjd 282204076Spjd switch (cmd) { 283204076Spjd case MODULE_CMD_INIT: 284204076Spjd#ifdef _MODULE 285204076Spjd error = config_init_component(cfdriver_ioconf_ualea, 286204076Spjd cfattach_ioconf_ualea, cfdata_ioconf_ualea); 287204076Spjd#endif 288204076Spjd return error; 289204076Spjd case MODULE_CMD_FINI: 290204076Spjd#ifdef _MODULE 291204076Spjd error = config_fini_component(cfdriver_ioconf_ualea, 292204076Spjd cfattach_ioconf_ualea, cfdata_ioconf_ualea); 293204076Spjd#endif 294204076Spjd return error; 295204076Spjd default: 296204076Spjd return ENOTTY; 297204076Spjd } 298204076Spjd} 299204076Spjd