1 /* $NetBSD: ualea.c,v 1.12 2020/07/13 13:53:04 simonb 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.12 2020/07/13 13:53:04 simonb 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 47 struct 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 65 static int ualea_match(device_t, cfdata_t, void *); 66 static void ualea_attach(device_t, device_t, void *); 67 static int ualea_detach(device_t, int); 68 static void ualea_get(size_t, void *); 69 static void ualea_xfer_done(struct usbd_xfer *, void *, usbd_status); 70 71 CFATTACH_DECL_NEW(ualea, sizeof(struct ualea_softc), 72 ualea_match, ualea_attach, ualea_detach, NULL); 73 74 static const struct usb_devno ualea_devs[] = { 75 { USB_VENDOR_ARANEUS, USB_PRODUCT_ARANEUS_ALEA }, 76 }; 77 78 static int 79 ualea_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 89 static void 90 ualea_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_SOFTUSB); 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 /* Success! We are ready to run. */ 143 sc->sc_attached = true; 144 rndsource_setcb(&sc->sc_rnd, ualea_get, sc); 145 rnd_attach_source(&sc->sc_rnd, device_xname(self), RND_TYPE_RNG, 146 RND_FLAG_COLLECT_VALUE|RND_FLAG_HASCB); 147 } 148 149 static int 150 ualea_detach(device_t self, int flags) 151 { 152 struct ualea_softc *sc = device_private(self); 153 154 /* Prevent new use of xfer. */ 155 if (sc->sc_attached) 156 rnd_detach_source(&sc->sc_rnd); 157 158 /* Cancel pending xfer. */ 159 if (sc->sc_pipe) 160 (void)usbd_abort_pipe(sc->sc_pipe); 161 KASSERT(!sc->sc_inflight); 162 163 /* All users have drained. Tear it all down. */ 164 if (sc->sc_xfer) 165 usbd_destroy_xfer(sc->sc_xfer); 166 if (sc->sc_pipe) 167 (void)usbd_close_pipe(sc->sc_pipe); 168 mutex_destroy(&sc->sc_lock); 169 170 return 0; 171 } 172 173 static void 174 ualea_xfer(struct ualea_softc *sc) 175 { 176 usbd_status status; 177 178 KASSERT(mutex_owned(&sc->sc_lock)); 179 KASSERT(sc->sc_attached); 180 KASSERT(!sc->sc_inflight); 181 182 /* Do nothing if we need nothing. */ 183 if (sc->sc_needed == 0) 184 return; 185 186 /* Setup the xfer to call ualea_xfer_done with sc. */ 187 usbd_setup_xfer(sc->sc_xfer, sc, usbd_get_buffer(sc->sc_xfer), 188 sc->sc_maxpktsize, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, 189 ualea_xfer_done); 190 191 /* Issue xfer or complain if we can't. */ 192 status = usbd_transfer(sc->sc_xfer); 193 KASSERT(status != USBD_NORMAL_COMPLETION); /* asynchronous xfer */ 194 if (status != USBD_IN_PROGRESS) { 195 aprint_error_dev(sc->sc_dev, "failed to issue xfer: %d\n", 196 status); 197 /* We failed -- let someone else have a go. */ 198 return; 199 } 200 201 /* Mark xfer in-flight. */ 202 sc->sc_inflight = true; 203 } 204 205 static void 206 ualea_get(size_t nbytes, void *cookie) 207 { 208 struct ualea_softc *sc = cookie; 209 210 mutex_enter(&sc->sc_lock); 211 sc->sc_needed = MAX(sc->sc_needed, nbytes); 212 if (!sc->sc_inflight) 213 ualea_xfer(sc); 214 mutex_exit(&sc->sc_lock); 215 } 216 217 static void 218 ualea_xfer_done(struct usbd_xfer *xfer, void *cookie, usbd_status status) 219 { 220 struct ualea_softc *sc = cookie; 221 void *pkt; 222 uint32_t pktsize; 223 224 /* Check the transfer status. */ 225 if (status) { 226 aprint_error_dev(sc->sc_dev, "xfer failed: %d\n", status); 227 return; 228 } 229 230 /* Get and sanity-check the transferred size. */ 231 usbd_get_xfer_status(xfer, NULL, &pkt, &pktsize, NULL); 232 if (pktsize > sc->sc_maxpktsize) { 233 aprint_error_dev(sc->sc_dev, 234 "bogus packet size: %"PRIu32" > %"PRIu16" (max), ignoring" 235 "\n", 236 pktsize, sc->sc_maxpktsize); 237 goto out; 238 } 239 240 /* Add the data to the pool. */ 241 rnd_add_data(&sc->sc_rnd, pkt, pktsize, NBBY*pktsize); 242 243 out: 244 mutex_enter(&sc->sc_lock); 245 246 /* Debit what we contributed from what we need. */ 247 sc->sc_needed -= MIN(sc->sc_needed, pktsize); 248 249 /* Mark xfer done. */ 250 sc->sc_inflight = false; 251 252 /* Reissue xfer if we still need more. */ 253 ualea_xfer(sc); 254 255 mutex_exit(&sc->sc_lock); 256 } 257 258 MODULE(MODULE_CLASS_DRIVER, ualea, NULL); 259 260 #ifdef _MODULE 261 #include "ioconf.c" 262 #endif 263 264 static int 265 ualea_modcmd(modcmd_t cmd, void *aux) 266 { 267 int error = 0; 268 269 switch (cmd) { 270 case MODULE_CMD_INIT: 271 #ifdef _MODULE 272 error = config_init_component(cfdriver_ioconf_ualea, 273 cfattach_ioconf_ualea, cfdata_ioconf_ualea); 274 #endif 275 return error; 276 case MODULE_CMD_FINI: 277 #ifdef _MODULE 278 error = config_fini_component(cfdriver_ioconf_ualea, 279 cfattach_ioconf_ualea, cfdata_ioconf_ualea); 280 #endif 281 return error; 282 default: 283 return ENOTTY; 284 } 285 } 286