1/*- 2 * Copyright (c) 2014, 2015, 2019 Marcel Moolenaar 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 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD$"); 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/bus.h> 33#include <sys/conf.h> 34#include <sys/cons.h> 35#include <sys/fcntl.h> 36#include <sys/interrupt.h> 37#include <sys/kdb.h> 38#include <sys/kernel.h> 39#include <sys/malloc.h> 40#include <sys/mman.h> 41#include <sys/proc.h> 42#include <sys/queue.h> 43#include <sys/reboot.h> 44#include <machine/bus.h> 45#include <sys/rman.h> 46#include <sys/uio.h> 47#include <machine/resource.h> 48#include <machine/stdarg.h> 49 50#include <dev/pci/pcivar.h> 51 52#include <dev/proto/proto.h> 53#include <dev/proto/proto_dev.h> 54#include <dev/proto/proto_busdma.h> 55 56CTASSERT(SYS_RES_IRQ != PROTO_RES_UNUSED && 57 SYS_RES_DRQ != PROTO_RES_UNUSED && 58 SYS_RES_MEMORY != PROTO_RES_UNUSED && 59 SYS_RES_IOPORT != PROTO_RES_UNUSED); 60CTASSERT(SYS_RES_IRQ != PROTO_RES_PCICFG && 61 SYS_RES_DRQ != PROTO_RES_PCICFG && 62 SYS_RES_MEMORY != PROTO_RES_PCICFG && 63 SYS_RES_IOPORT != PROTO_RES_PCICFG); 64CTASSERT(SYS_RES_IRQ != PROTO_RES_BUSDMA && 65 SYS_RES_DRQ != PROTO_RES_BUSDMA && 66 SYS_RES_MEMORY != PROTO_RES_BUSDMA && 67 SYS_RES_IOPORT != PROTO_RES_BUSDMA); 68 69devclass_t proto_devclass; 70char proto_driver_name[] = "proto"; 71 72static d_open_t proto_open; 73static d_close_t proto_close; 74static d_read_t proto_read; 75static d_write_t proto_write; 76static d_ioctl_t proto_ioctl; 77static d_mmap_t proto_mmap; 78 79struct cdevsw proto_devsw = { 80 .d_version = D_VERSION, 81 .d_flags = 0, 82 .d_name = proto_driver_name, 83 .d_open = proto_open, 84 .d_close = proto_close, 85 .d_read = proto_read, 86 .d_write = proto_write, 87 .d_ioctl = proto_ioctl, 88 .d_mmap = proto_mmap, 89}; 90 91static MALLOC_DEFINE(M_PROTO, "PROTO", "PROTO driver"); 92 93int 94proto_add_resource(struct proto_softc *sc, int type, int rid, 95 struct resource *res) 96{ 97 struct proto_res *r; 98 99 if (type == PROTO_RES_UNUSED) 100 return (EINVAL); 101 if (sc->sc_rescnt == PROTO_RES_MAX) 102 return (ENOSPC); 103 104 r = sc->sc_res + sc->sc_rescnt++; 105 r->r_type = type; 106 r->r_rid = rid; 107 r->r_d.res = res; 108 return (0); 109} 110 111#ifdef notyet 112static int 113proto_intr(void *arg) 114{ 115 struct proto_softc *sc = arg; 116 117 /* XXX TODO */ 118 return (FILTER_HANDLED); 119} 120#endif 121 122int 123proto_probe(device_t dev, const char *prefix, char ***devnamesp) 124{ 125 char **devnames = *devnamesp; 126 const char *dn, *ep, *ev; 127 size_t pfxlen; 128 int idx, names; 129 130 if (devnames == NULL) { 131 pfxlen = strlen(prefix); 132 names = 1; /* NULL pointer */ 133 ev = kern_getenv("hw.proto.attach"); 134 if (ev != NULL) { 135 dn = ev; 136 while (*dn != '\0') { 137 ep = dn; 138 while (*ep != ',' && *ep != '\0') 139 ep++; 140 if ((ep - dn) > pfxlen && 141 strncmp(dn, prefix, pfxlen) == 0) 142 names++; 143 dn = (*ep == ',') ? ep + 1 : ep; 144 } 145 } 146 devnames = malloc(names * sizeof(caddr_t), M_DEVBUF, 147 M_WAITOK | M_ZERO); 148 *devnamesp = devnames; 149 if (ev != NULL) { 150 dn = ev; 151 idx = 0; 152 while (*dn != '\0') { 153 ep = dn; 154 while (*ep != ',' && *ep != '\0') 155 ep++; 156 if ((ep - dn) > pfxlen && 157 strncmp(dn, prefix, pfxlen) == 0) { 158 devnames[idx] = malloc(ep - dn + 1, 159 M_DEVBUF, M_WAITOK | M_ZERO); 160 memcpy(devnames[idx], dn, ep - dn); 161 idx++; 162 } 163 dn = (*ep == ',') ? ep + 1 : ep; 164 } 165 freeenv(__DECONST(char *, ev)); 166 } 167 } 168 169 dn = device_get_desc(dev); 170 while (*devnames != NULL) { 171 if (strcmp(dn, *devnames) == 0) 172 return (BUS_PROBE_SPECIFIC); 173 devnames++; 174 } 175 return (BUS_PROBE_HOOVER); 176} 177 178int 179proto_attach(device_t dev) 180{ 181 struct proto_softc *sc; 182 struct proto_res *r; 183 u_int res; 184 185 sc = device_get_softc(dev); 186 sc->sc_dev = dev; 187 mtx_init(&sc->sc_mtx, "proto-softc", NULL, MTX_DEF); 188 189 for (res = 0; res < sc->sc_rescnt; res++) { 190 r = sc->sc_res + res; 191 switch (r->r_type) { 192 case SYS_RES_IRQ: 193 /* XXX TODO */ 194 break; 195 case SYS_RES_DRQ: 196 break; 197 case SYS_RES_MEMORY: 198 case SYS_RES_IOPORT: 199 r->r_size = rman_get_size(r->r_d.res); 200 r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0600, 201 "proto/%s/%02x.%s", device_get_desc(dev), r->r_rid, 202 (r->r_type == SYS_RES_IOPORT) ? "io" : "mem"); 203 r->r_u.cdev->si_drv1 = sc; 204 r->r_u.cdev->si_drv2 = r; 205 break; 206 case PROTO_RES_PCICFG: 207 r->r_size = 4096; 208 r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0600, 209 "proto/%s/pcicfg", device_get_desc(dev)); 210 r->r_u.cdev->si_drv1 = sc; 211 r->r_u.cdev->si_drv2 = r; 212 break; 213 case PROTO_RES_BUSDMA: 214 r->r_d.busdma = proto_busdma_attach(sc); 215 r->r_size = 0; /* no read(2) nor write(2) */ 216 r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0600, 217 "proto/%s/busdma", device_get_desc(dev)); 218 r->r_u.cdev->si_drv1 = sc; 219 r->r_u.cdev->si_drv2 = r; 220 break; 221 } 222 } 223 return (0); 224} 225 226int 227proto_detach(device_t dev) 228{ 229 struct proto_softc *sc; 230 struct proto_res *r; 231 u_int res; 232 233 sc = device_get_softc(dev); 234 235 mtx_lock(&sc->sc_mtx); 236 if (sc->sc_opencnt == 0) 237 sc->sc_opencnt = -1; 238 mtx_unlock(&sc->sc_mtx); 239 if (sc->sc_opencnt > 0) 240 return (EBUSY); 241 242 for (res = 0; res < sc->sc_rescnt; res++) { 243 r = sc->sc_res + res; 244 245 switch (r->r_type) { 246 case SYS_RES_IRQ: 247 /* XXX TODO */ 248 bus_release_resource(dev, r->r_type, r->r_rid, 249 r->r_d.res); 250 break; 251 case SYS_RES_DRQ: 252 bus_release_resource(dev, r->r_type, r->r_rid, 253 r->r_d.res); 254 break; 255 case SYS_RES_MEMORY: 256 case SYS_RES_IOPORT: 257 destroy_dev(r->r_u.cdev); 258 bus_release_resource(dev, r->r_type, r->r_rid, 259 r->r_d.res); 260 break; 261 case PROTO_RES_PCICFG: 262 destroy_dev(r->r_u.cdev); 263 break; 264 case PROTO_RES_BUSDMA: 265 destroy_dev(r->r_u.cdev); 266 proto_busdma_detach(sc, r->r_d.busdma); 267 break; 268 } 269 r->r_type = PROTO_RES_UNUSED; 270 } 271 mtx_lock(&sc->sc_mtx); 272 sc->sc_rescnt = 0; 273 sc->sc_opencnt = 0; 274 mtx_unlock(&sc->sc_mtx); 275 mtx_destroy(&sc->sc_mtx); 276 return (0); 277} 278 279/* 280 * Device functions 281 */ 282 283static int 284proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td) 285{ 286 struct proto_res *r; 287 struct proto_softc *sc; 288 int error; 289 290 sc = cdev->si_drv1; 291 mtx_lock(&sc->sc_mtx); 292 if (sc->sc_opencnt >= 0) { 293 r = cdev->si_drv2; 294 if (!r->r_opened) { 295 r->r_opened = 1; 296 sc->sc_opencnt++; 297 error = 0; 298 } else 299 error = EBUSY; 300 } else 301 error = ENXIO; 302 mtx_unlock(&sc->sc_mtx); 303 return (error); 304} 305 306static int 307proto_close(struct cdev *cdev, int fflag, int devtype, struct thread *td) 308{ 309 struct proto_res *r; 310 struct proto_softc *sc; 311 int error; 312 313 sc = cdev->si_drv1; 314 mtx_lock(&sc->sc_mtx); 315 if (sc->sc_opencnt > 0) { 316 r = cdev->si_drv2; 317 if (r->r_opened) { 318 if (r->r_type == PROTO_RES_BUSDMA) 319 proto_busdma_cleanup(sc, r->r_d.busdma); 320 r->r_opened = 0; 321 sc->sc_opencnt--; 322 error = 0; 323 } else 324 error = ENXIO; 325 } else 326 error = ENXIO; 327 mtx_unlock(&sc->sc_mtx); 328 return (error); 329} 330 331static int 332proto_read(struct cdev *cdev, struct uio *uio, int ioflag) 333{ 334 union { 335 uint8_t x1[8]; 336 uint16_t x2[4]; 337 uint32_t x4[2]; 338 uint64_t x8[1]; 339 } buf; 340 struct proto_softc *sc; 341 struct proto_res *r; 342 device_t dev; 343 off_t ofs; 344 u_long width; 345 int error; 346 347 sc = cdev->si_drv1; 348 dev = sc->sc_dev; 349 r = cdev->si_drv2; 350 351 width = uio->uio_resid; 352 if (width < 1 || width > 8 || bitcount16(width) > 1) 353 return (EIO); 354 ofs = uio->uio_offset; 355 if (ofs + width > r->r_size) 356 return (EIO); 357 358 switch (width) { 359 case 1: 360 buf.x1[0] = (r->r_type == PROTO_RES_PCICFG) ? 361 pci_read_config(dev, ofs, 1) : bus_read_1(r->r_d.res, ofs); 362 break; 363 case 2: 364 buf.x2[0] = (r->r_type == PROTO_RES_PCICFG) ? 365 pci_read_config(dev, ofs, 2) : bus_read_2(r->r_d.res, ofs); 366 break; 367 case 4: 368 buf.x4[0] = (r->r_type == PROTO_RES_PCICFG) ? 369 pci_read_config(dev, ofs, 4) : bus_read_4(r->r_d.res, ofs); 370 break; 371#ifndef __i386__ 372 case 8: 373 if (r->r_type == PROTO_RES_PCICFG) 374 return (EINVAL); 375 buf.x8[0] = bus_read_8(r->r_d.res, ofs); 376 break; 377#endif 378 default: 379 return (EIO); 380 } 381 382 error = uiomove(&buf, width, uio); 383 return (error); 384} 385 386static int 387proto_write(struct cdev *cdev, struct uio *uio, int ioflag) 388{ 389 union { 390 uint8_t x1[8]; 391 uint16_t x2[4]; 392 uint32_t x4[2]; 393 uint64_t x8[1]; 394 } buf; 395 struct proto_softc *sc; 396 struct proto_res *r; 397 device_t dev; 398 off_t ofs; 399 u_long width; 400 int error; 401 402 sc = cdev->si_drv1; 403 dev = sc->sc_dev; 404 r = cdev->si_drv2; 405 406 width = uio->uio_resid; 407 if (width < 1 || width > 8 || bitcount16(width) > 1) 408 return (EIO); 409 ofs = uio->uio_offset; 410 if (ofs + width > r->r_size) 411 return (EIO); 412 413 error = uiomove(&buf, width, uio); 414 if (error) 415 return (error); 416 417 switch (width) { 418 case 1: 419 if (r->r_type == PROTO_RES_PCICFG) 420 pci_write_config(dev, ofs, buf.x1[0], 1); 421 else 422 bus_write_1(r->r_d.res, ofs, buf.x1[0]); 423 break; 424 case 2: 425 if (r->r_type == PROTO_RES_PCICFG) 426 pci_write_config(dev, ofs, buf.x2[0], 2); 427 else 428 bus_write_2(r->r_d.res, ofs, buf.x2[0]); 429 break; 430 case 4: 431 if (r->r_type == PROTO_RES_PCICFG) 432 pci_write_config(dev, ofs, buf.x4[0], 4); 433 else 434 bus_write_4(r->r_d.res, ofs, buf.x4[0]); 435 break; 436#ifndef __i386__ 437 case 8: 438 if (r->r_type == PROTO_RES_PCICFG) 439 return (EINVAL); 440 bus_write_8(r->r_d.res, ofs, buf.x8[0]); 441 break; 442#endif 443 default: 444 return (EIO); 445 } 446 447 return (0); 448} 449 450static int 451proto_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, 452 struct thread *td) 453{ 454 struct proto_ioc_region *region; 455 struct proto_ioc_busdma *busdma; 456 struct proto_res *r; 457 struct proto_softc *sc; 458 int error; 459 460 sc = cdev->si_drv1; 461 r = cdev->si_drv2; 462 463 error = 0; 464 switch (cmd) { 465 case PROTO_IOC_REGION: 466 if (r->r_type == PROTO_RES_BUSDMA) { 467 error = EINVAL; 468 break; 469 } 470 region = (struct proto_ioc_region *)data; 471 region->size = r->r_size; 472 if (r->r_type == PROTO_RES_PCICFG) 473 region->address = 0; 474 else 475 region->address = rman_get_start(r->r_d.res); 476 break; 477 case PROTO_IOC_BUSDMA: 478 if (r->r_type != PROTO_RES_BUSDMA) { 479 error = EINVAL; 480 break; 481 } 482 busdma = (struct proto_ioc_busdma *)data; 483 error = proto_busdma_ioctl(sc, r->r_d.busdma, busdma, td); 484 break; 485 default: 486 error = ENOIOCTL; 487 break; 488 } 489 return (error); 490} 491 492static int 493proto_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr, 494 int prot, vm_memattr_t *memattr) 495{ 496 struct proto_res *r; 497 498 if (offset & PAGE_MASK) 499 return (EINVAL); 500 if (prot & PROT_EXEC) 501 return (EACCES); 502 503 r = cdev->si_drv2; 504 505 switch (r->r_type) { 506 case SYS_RES_MEMORY: 507 if (offset >= r->r_size) 508 return (EINVAL); 509 *paddr = rman_get_start(r->r_d.res) + offset; 510 *memattr = VM_MEMATTR_UNCACHEABLE; 511 break; 512 case PROTO_RES_BUSDMA: 513 if (!proto_busdma_mmap_allowed(r->r_d.busdma, offset)) 514 return (EINVAL); 515 *paddr = offset; 516 break; 517 default: 518 return (ENXIO); 519 } 520 return (0); 521} 522