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