1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2003-2004 Poul-Henning Kamp 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The names of the authors may not be used to endorse or promote 16 * products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * This is a device driver or the Adlink 9812 and 9810 ADC cards, mainly 32 * intended to support Software Defined Radio reception of timesignals 33 * in the VLF band. See http://phk.freebsd.dk/loran-c 34 * 35 * The driver is configured with ioctls which define a ringbuffer with 36 * a given number of chunks in it. The a control structure and the 37 * ringbuffer can then be mmap(2)'ed into userland and the application 38 * can operate on the data directly. 39 * 40 * Tested with 10MHz external clock, divisor of 2 (ie: 5MHz sampling), 41 * One channel active (ie: 2 bytes per sample = 10MB/sec) on a 660MHz 42 * Celeron PC. 43 * 44 */ 45 46 #ifdef _KERNEL 47 #include <sys/cdefs.h> 48 __FBSDID("$FreeBSD$"); 49 50 #include <sys/param.h> 51 #include <sys/systm.h> 52 #include <sys/malloc.h> 53 #include <sys/kernel.h> 54 #include <sys/module.h> 55 #include <sys/kthread.h> 56 #include <sys/conf.h> 57 #include <sys/bus.h> 58 #include <machine/bus.h> 59 #include <machine/resource.h> 60 #include <sys/rman.h> 61 #include <dev/pci/pcireg.h> 62 #include <dev/pci/pcivar.h> 63 #include <pci_if.h> 64 #include <vm/vm.h> 65 #include <vm/pmap.h> 66 67 #endif /* _KERNEL */ 68 69 #include <sys/ioccom.h> 70 71 #define ADLINK_SETDIVISOR _IOWR('A', 255, u_int) /* divisor */ 72 #define ADLINK_SETCHUNKSIZE _IOWR('A', 254, u_int) /* bytes */ 73 #define ADLINK_SETRINGSIZE _IOWR('A', 253, u_int) /* bytes */ 74 #define ADLINK_START _IOWR('A', 252, u_int) /* dummy */ 75 #define ADLINK_STOP _IOWR('A', 251, u_int) /* dummy */ 76 #define ADLINK_RESET _IOWR('A', 250, u_int) /* dummy */ 77 78 struct page0 { 79 u_int version; 80 int state; 81 # define STATE_RESET -1 82 # define STATE_RUN 0 83 u_int divisor; /* int */ 84 u_int chunksize; /* bytes */ 85 u_int ringsize; /* chunks */ 86 u_int o_sample; /* 87 * offset of ring generation 88 * array 89 */ 90 u_int o_ring; /* offset of ring */ 91 }; 92 93 #define PAGE0VERSION 20050219 94 95 #ifdef _KERNEL 96 97 struct pgstat { 98 uint64_t *sample; 99 vm_paddr_t phys; 100 void *virt; 101 struct pgstat *next; 102 }; 103 104 struct softc { 105 device_t device; 106 void *intrhand; 107 struct resource *res[3]; 108 struct cdev *dev; 109 off_t mapvir; 110 int error; 111 struct page0 *p0; 112 u_int nchunks; 113 struct pgstat *chunks; 114 struct pgstat *next; 115 uint64_t sample; 116 }; 117 118 static d_ioctl_t adlink_ioctl; 119 static d_mmap_t adlink_mmap; 120 static int adlink_intr(void *arg); 121 122 static struct cdevsw adlink_cdevsw = { 123 .d_version = D_VERSION, 124 .d_flags = D_NEEDGIANT, 125 .d_ioctl = adlink_ioctl, 126 .d_mmap = adlink_mmap, 127 .d_name = "adlink", 128 }; 129 130 static int 131 adlink_intr(void *arg) 132 { 133 struct softc *sc; 134 struct pgstat *pg; 135 uint32_t u; 136 137 sc = arg; 138 u = bus_read_4(sc->res[0], 0x38); 139 if (!(u & 0x00800000)) 140 return (FILTER_STRAY); 141 bus_write_4(sc->res[0], 0x38, u | 0x003f4000); 142 143 sc->sample += sc->p0->chunksize / 2; 144 pg = sc->next; 145 *(pg->sample) = sc->sample; 146 147 u = bus_read_4(sc->res[1], 0x18); 148 if (u & 1) 149 sc->p0->state = EIO; 150 151 if (sc->p0->state != STATE_RUN) { 152 printf("adlink: stopping %d\n", sc->p0->state); 153 return (FILTER_STRAY); 154 } 155 156 pg = pg->next; 157 sc->next = pg; 158 *(pg->sample) = 0; 159 bus_write_4(sc->res[0], 0x24, pg->phys); 160 bus_write_4(sc->res[0], 0x28, sc->p0->chunksize); 161 wakeup(sc); 162 return (FILTER_HANDLED); 163 } 164 165 static int 166 adlink_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, 167 int nprot, vm_memattr_t *memattr) 168 { 169 struct softc *sc; 170 vm_offset_t o; 171 int i; 172 struct pgstat *pg; 173 174 sc = dev->si_drv1; 175 if (nprot != VM_PROT_READ) 176 return (-1); 177 if (offset == 0) { 178 *paddr = vtophys(sc->p0); 179 return (0); 180 } 181 o = PAGE_SIZE; 182 pg = sc->chunks; 183 for (i = 0; i < sc->nchunks; i++, pg++) { 184 if (offset - o >= sc->p0->chunksize) { 185 o += sc->p0->chunksize; 186 continue; 187 } 188 *paddr = pg->phys + (offset - o); 189 return (0); 190 } 191 return (-1); 192 } 193 194 static int 195 adlink_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 196 { 197 struct softc *sc; 198 int i, error; 199 u_int u; 200 struct pgstat *pg; 201 uint64_t *sample; 202 203 sc = dev->si_drv1; 204 u = *(u_int*)data; 205 error = 0; 206 switch (cmd) { 207 case ADLINK_SETDIVISOR: 208 if (sc->p0->state == STATE_RUN) 209 return (EBUSY); 210 if (u & 1) 211 return (EINVAL); 212 sc->p0->divisor = u; 213 break; 214 case ADLINK_SETCHUNKSIZE: 215 if (sc->p0->state != STATE_RESET) 216 return (EBUSY); 217 if (u % PAGE_SIZE) 218 return (EINVAL); 219 if (sc->p0->ringsize != 0 && sc->p0->ringsize % u) 220 return (EINVAL); 221 sc->p0->chunksize = u; 222 break; 223 case ADLINK_SETRINGSIZE: 224 if (sc->p0->state != STATE_RESET) 225 return (EBUSY); 226 if (u % PAGE_SIZE) 227 return (EINVAL); 228 if (sc->p0->chunksize != 0 && u % sc->p0->chunksize) 229 return (EINVAL); 230 sc->p0->ringsize = u; 231 break; 232 case ADLINK_START: 233 if (sc->p0->state == STATE_RUN) 234 return (EBUSY); 235 if (sc->p0->state == STATE_RESET) { 236 237 if (sc->p0->chunksize == 0) 238 sc->p0->chunksize = 4 * PAGE_SIZE; 239 if (sc->p0->ringsize == 0) 240 sc->p0->ringsize = 16 * sc->p0->chunksize; 241 if (sc->p0->divisor == 0) 242 sc->p0->divisor = 4; 243 244 sc->nchunks = sc->p0->ringsize / sc->p0->chunksize; 245 if (sc->nchunks * sizeof (*pg->sample) + 246 sizeof *sc->p0 > PAGE_SIZE) 247 return (EINVAL); 248 sc->p0->o_ring = PAGE_SIZE; 249 sample = (uint64_t *)(sc->p0 + 1); 250 sc->p0->o_sample = 251 (uintptr_t)sample - (uintptr_t)(sc->p0); 252 pg = malloc(sizeof *pg * sc->nchunks, 253 M_DEVBUF, M_WAITOK | M_ZERO); 254 sc->chunks = pg; 255 for (i = 0; i < sc->nchunks; i++) { 256 pg->sample = sample; 257 *pg->sample = 0; 258 sample++; 259 pg->virt = contigmalloc(sc->p0->chunksize, 260 M_DEVBUF, M_WAITOK, 261 0ul, 0xfffffffful, 262 PAGE_SIZE, 0); 263 pg->phys = vtophys(pg->virt); 264 if (i == sc->nchunks - 1) 265 pg->next = sc->chunks; 266 else 267 pg->next = pg + 1; 268 pg++; 269 } 270 sc->next = sc->chunks; 271 } 272 273 /* Reset generation numbers */ 274 pg = sc->chunks; 275 for (i = 0; i < sc->nchunks; i++) { 276 *pg->sample = 0; 277 pg++; 278 } 279 280 /* Enable interrupts on write complete */ 281 bus_write_4(sc->res[0], 0x38, 0x00004000); 282 283 /* Sample CH0 only */ 284 bus_write_4(sc->res[1], 0x00, 1); 285 286 /* Divide clock by four */ 287 bus_write_4(sc->res[1], 0x04, sc->p0->divisor); 288 289 /* Software trigger mode: software */ 290 bus_write_4(sc->res[1], 0x08, 0); 291 292 /* Trigger level zero */ 293 bus_write_4(sc->res[1], 0x0c, 0); 294 295 /* Trigger source CH0 (not used) */ 296 bus_write_4(sc->res[1], 0x10, 0); 297 298 /* Fifo control/status: flush */ 299 bus_write_4(sc->res[1], 0x18, 3); 300 301 /* Clock source: external sine */ 302 bus_write_4(sc->res[1], 0x20, 2); 303 304 /* Chipmunks are go! */ 305 sc->p0->state = STATE_RUN; 306 307 /* Set up Write DMA */ 308 pg = sc->next = sc->chunks; 309 *(pg->sample) = 0; 310 bus_write_4(sc->res[0], 0x24, pg->phys); 311 bus_write_4(sc->res[0], 0x28, sc->p0->chunksize); 312 u = bus_read_4(sc->res[0], 0x3c); 313 bus_write_4(sc->res[0], 0x3c, u | 0x00000600); 314 315 /* Acquisition Enable Register: go! */ 316 bus_write_4(sc->res[1], 0x1c, 1); 317 318 break; 319 case ADLINK_STOP: 320 if (sc->p0->state == STATE_RESET) 321 break; 322 sc->p0->state = EINTR; 323 while (*(sc->next->sample) == 0) 324 tsleep(sc, PUSER | PCATCH, "adstop", 1); 325 break; 326 #ifdef notyet 327 /* 328 * I'm not sure we can actually do this. How do we revoke 329 * the mmap'ed pages from any process having them mmapped ? 330 */ 331 case ADLINK_RESET: 332 if (sc->p0->state == STATE_RESET) 333 break; 334 sc->p0->state = EINTR; 335 while (*(sc->next->samp) == 0) 336 tsleep(sc, PUSER | PCATCH, "adreset", 1); 337 /* deallocate ring buffer */ 338 break; 339 #endif 340 default: 341 error = ENOIOCTL; 342 break; 343 } 344 return (error); 345 } 346 347 struct pci_id 348 { 349 uint16_t vendor; 350 uint16_t device; 351 const char *desc; 352 } adlink_id[] = { 353 { .vendor = 0x10e8, .device = 0x80da, 354 .desc ="Adlink PCI-9812 4 ch 12 bit 20 msps" } 355 }; 356 357 static int 358 adlink_probe(device_t self) 359 { 360 int i; 361 uint16_t vendor, device; 362 363 vendor = pci_get_vendor(self); 364 device = pci_get_device(self); 365 for (i = 0; i < nitems(adlink_id); i++) { 366 if (adlink_id[i].vendor == vendor && 367 adlink_id[i].device == device) { 368 device_set_desc(self, adlink_id[i].desc); 369 return (BUS_PROBE_DEFAULT); 370 } 371 } 372 return (ENXIO); 373 } 374 375 static struct resource_spec adlink_res_spec[] = { 376 { SYS_RES_IOPORT, PCIR_BAR(0), RF_ACTIVE}, 377 { SYS_RES_IOPORT, PCIR_BAR(1), RF_ACTIVE}, 378 { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE}, 379 { -1, 0, 0 } 380 }; 381 382 static int 383 adlink_attach(device_t self) 384 { 385 struct softc *sc; 386 int i, error; 387 388 sc = device_get_softc(self); 389 bzero(sc, sizeof *sc); 390 sc->device = self; 391 392 error = bus_alloc_resources(self, adlink_res_spec, sc->res); 393 if (error) 394 return (error); 395 396 i = bus_setup_intr(self, sc->res[2], INTR_TYPE_MISC, 397 adlink_intr, NULL, sc, &sc->intrhand); 398 if (i) { 399 printf("adlink: Couldn't get FAST intr\n"); 400 i = bus_setup_intr(self, sc->res[2], 401 INTR_MPSAFE | INTR_TYPE_MISC, 402 NULL, (driver_intr_t *)adlink_intr, sc, &sc->intrhand); 403 } 404 405 if (i) { 406 bus_release_resources(self, adlink_res_spec, sc->res); 407 return (ENODEV); 408 } 409 410 sc->p0 = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); 411 sc->p0->version = PAGE0VERSION; 412 sc->p0->state = STATE_RESET; 413 414 sc->dev = make_dev(&adlink_cdevsw, device_get_unit(self), 415 UID_ROOT, GID_WHEEL, 0444, "adlink%d", device_get_unit(self)); 416 sc->dev->si_drv1 = sc; 417 418 return (0); 419 } 420 421 static device_method_t adlink_methods[] = { 422 /* Device interface */ 423 DEVMETHOD(device_probe, adlink_probe), 424 DEVMETHOD(device_attach, adlink_attach), 425 DEVMETHOD(device_suspend, bus_generic_suspend), 426 DEVMETHOD(device_resume, bus_generic_resume), 427 DEVMETHOD(device_shutdown, bus_generic_shutdown), 428 429 DEVMETHOD_END 430 }; 431 432 static driver_t adlink_driver = { 433 "adlink", 434 adlink_methods, 435 sizeof(struct softc) 436 }; 437 438 DRIVER_MODULE(adlink, pci, adlink_driver, 0, 0); 439 MODULE_PNP_INFO("U16:vendor;U16:device;D:#", pci, adlink, adlink_id, 440 nitems(adlink_id)); 441 #endif /* _KERNEL */ 442