1 /*- 2 * Copyright (c) 2014 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 56 CTASSERT(SYS_RES_IRQ != PROTO_RES_UNUSED && 57 SYS_RES_MEMORY != PROTO_RES_UNUSED && 58 SYS_RES_IOPORT != PROTO_RES_UNUSED); 59 CTASSERT(SYS_RES_IRQ != PROTO_RES_PCICFG && 60 SYS_RES_MEMORY != PROTO_RES_PCICFG && 61 SYS_RES_IOPORT != PROTO_RES_PCICFG); 62 CTASSERT(SYS_RES_IRQ != PROTO_RES_BUSDMA && 63 SYS_RES_MEMORY != PROTO_RES_BUSDMA && 64 SYS_RES_IOPORT != PROTO_RES_BUSDMA); 65 66 devclass_t proto_devclass; 67 char proto_driver_name[] = "proto"; 68 69 static d_open_t proto_open; 70 static d_close_t proto_close; 71 static d_read_t proto_read; 72 static d_write_t proto_write; 73 static d_ioctl_t proto_ioctl; 74 static d_mmap_t proto_mmap; 75 76 struct cdevsw proto_devsw = { 77 .d_version = D_VERSION, 78 .d_flags = 0, 79 .d_name = proto_driver_name, 80 .d_open = proto_open, 81 .d_close = proto_close, 82 .d_read = proto_read, 83 .d_write = proto_write, 84 .d_ioctl = proto_ioctl, 85 .d_mmap = proto_mmap, 86 }; 87 88 static MALLOC_DEFINE(M_PROTO, "PROTO", "PROTO driver"); 89 90 int 91 proto_add_resource(struct proto_softc *sc, int type, int rid, 92 struct resource *res) 93 { 94 struct proto_res *r; 95 96 if (type == PROTO_RES_UNUSED) 97 return (EINVAL); 98 if (sc->sc_rescnt == PROTO_RES_MAX) 99 return (ENOSPC); 100 101 r = sc->sc_res + sc->sc_rescnt++; 102 r->r_type = type; 103 r->r_rid = rid; 104 r->r_d.res = res; 105 return (0); 106 } 107 108 #ifdef notyet 109 static int 110 proto_intr(void *arg) 111 { 112 struct proto_softc *sc = arg; 113 114 /* XXX TODO */ 115 return (FILTER_HANDLED); 116 } 117 #endif 118 119 int 120 proto_attach(device_t dev) 121 { 122 struct proto_softc *sc; 123 struct proto_res *r; 124 u_int res; 125 126 sc = device_get_softc(dev); 127 sc->sc_dev = dev; 128 129 for (res = 0; res < sc->sc_rescnt; res++) { 130 r = sc->sc_res + res; 131 switch (r->r_type) { 132 case SYS_RES_IRQ: 133 /* XXX TODO */ 134 break; 135 case SYS_RES_MEMORY: 136 case SYS_RES_IOPORT: 137 r->r_size = rman_get_size(r->r_d.res); 138 r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666, 139 "proto/%s/%02x.%s", device_get_desc(dev), r->r_rid, 140 (r->r_type == SYS_RES_IOPORT) ? "io" : "mem"); 141 r->r_u.cdev->si_drv1 = sc; 142 r->r_u.cdev->si_drv2 = r; 143 break; 144 case PROTO_RES_PCICFG: 145 r->r_size = 4096; 146 r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666, 147 "proto/%s/pcicfg", device_get_desc(dev)); 148 r->r_u.cdev->si_drv1 = sc; 149 r->r_u.cdev->si_drv2 = r; 150 break; 151 case PROTO_RES_BUSDMA: 152 r->r_d.busdma = proto_busdma_attach(sc); 153 r->r_size = 0; /* no read(2) nor write(2) */ 154 r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666, 155 "proto/%s/busdma", device_get_desc(dev)); 156 r->r_u.cdev->si_drv1 = sc; 157 r->r_u.cdev->si_drv2 = r; 158 break; 159 } 160 } 161 return (0); 162 } 163 164 int 165 proto_detach(device_t dev) 166 { 167 struct proto_softc *sc; 168 struct proto_res *r; 169 u_int res; 170 171 sc = device_get_softc(dev); 172 173 /* Don't detach if we have open device files. */ 174 for (res = 0; res < sc->sc_rescnt; res++) { 175 r = sc->sc_res + res; 176 if (r->r_opened) 177 return (EBUSY); 178 } 179 180 for (res = 0; res < sc->sc_rescnt; res++) { 181 r = sc->sc_res + res; 182 switch (r->r_type) { 183 case SYS_RES_IRQ: 184 /* XXX TODO */ 185 bus_release_resource(dev, r->r_type, r->r_rid, 186 r->r_d.res); 187 break; 188 case SYS_RES_MEMORY: 189 case SYS_RES_IOPORT: 190 bus_release_resource(dev, r->r_type, r->r_rid, 191 r->r_d.res); 192 destroy_dev(r->r_u.cdev); 193 break; 194 case PROTO_RES_PCICFG: 195 destroy_dev(r->r_u.cdev); 196 break; 197 case PROTO_RES_BUSDMA: 198 proto_busdma_detach(sc, r->r_d.busdma); 199 destroy_dev(r->r_u.cdev); 200 break; 201 } 202 r->r_type = PROTO_RES_UNUSED; 203 } 204 sc->sc_rescnt = 0; 205 return (0); 206 } 207 208 /* 209 * Device functions 210 */ 211 212 static int 213 proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td) 214 { 215 struct proto_res *r; 216 217 r = cdev->si_drv2; 218 if (!atomic_cmpset_acq_ptr(&r->r_opened, 0UL, (uintptr_t)td->td_proc)) 219 return (EBUSY); 220 return (0); 221 } 222 223 static int 224 proto_close(struct cdev *cdev, int fflag, int devtype, struct thread *td) 225 { 226 struct proto_res *r; 227 struct proto_softc *sc; 228 229 sc = cdev->si_drv1; 230 r = cdev->si_drv2; 231 if (!atomic_cmpset_acq_ptr(&r->r_opened, (uintptr_t)td->td_proc, 0UL)) 232 return (ENXIO); 233 if (r->r_type == PROTO_RES_BUSDMA) 234 proto_busdma_cleanup(sc, r->r_d.busdma); 235 return (0); 236 } 237 238 static int 239 proto_read(struct cdev *cdev, struct uio *uio, int ioflag) 240 { 241 union { 242 uint8_t x1[8]; 243 uint16_t x2[4]; 244 uint32_t x4[2]; 245 uint64_t x8[1]; 246 } buf; 247 struct proto_softc *sc; 248 struct proto_res *r; 249 device_t dev; 250 off_t ofs; 251 u_long width; 252 int error; 253 254 sc = cdev->si_drv1; 255 dev = sc->sc_dev; 256 r = cdev->si_drv2; 257 258 width = uio->uio_resid; 259 if (width < 1 || width > 8 || bitcount16(width) > 1) 260 return (EIO); 261 ofs = uio->uio_offset; 262 if (ofs + width > r->r_size) 263 return (EIO); 264 265 switch (width) { 266 case 1: 267 buf.x1[0] = (r->r_type == PROTO_RES_PCICFG) ? 268 pci_read_config(dev, ofs, 1) : bus_read_1(r->r_d.res, ofs); 269 break; 270 case 2: 271 buf.x2[0] = (r->r_type == PROTO_RES_PCICFG) ? 272 pci_read_config(dev, ofs, 2) : bus_read_2(r->r_d.res, ofs); 273 break; 274 case 4: 275 buf.x4[0] = (r->r_type == PROTO_RES_PCICFG) ? 276 pci_read_config(dev, ofs, 4) : bus_read_4(r->r_d.res, ofs); 277 break; 278 #ifndef __i386__ 279 case 8: 280 if (r->r_type == PROTO_RES_PCICFG) 281 return (EINVAL); 282 buf.x8[0] = bus_read_8(r->r_d.res, ofs); 283 break; 284 #endif 285 default: 286 return (EIO); 287 } 288 289 error = uiomove(&buf, width, uio); 290 return (error); 291 } 292 293 static int 294 proto_write(struct cdev *cdev, struct uio *uio, int ioflag) 295 { 296 union { 297 uint8_t x1[8]; 298 uint16_t x2[4]; 299 uint32_t x4[2]; 300 uint64_t x8[1]; 301 } buf; 302 struct proto_softc *sc; 303 struct proto_res *r; 304 device_t dev; 305 off_t ofs; 306 u_long width; 307 int error; 308 309 sc = cdev->si_drv1; 310 dev = sc->sc_dev; 311 r = cdev->si_drv2; 312 313 width = uio->uio_resid; 314 if (width < 1 || width > 8 || bitcount16(width) > 1) 315 return (EIO); 316 ofs = uio->uio_offset; 317 if (ofs + width > r->r_size) 318 return (EIO); 319 320 error = uiomove(&buf, width, uio); 321 if (error) 322 return (error); 323 324 switch (width) { 325 case 1: 326 if (r->r_type == PROTO_RES_PCICFG) 327 pci_write_config(dev, ofs, buf.x1[0], 1); 328 else 329 bus_write_1(r->r_d.res, ofs, buf.x1[0]); 330 break; 331 case 2: 332 if (r->r_type == PROTO_RES_PCICFG) 333 pci_write_config(dev, ofs, buf.x2[0], 2); 334 else 335 bus_write_2(r->r_d.res, ofs, buf.x2[0]); 336 break; 337 case 4: 338 if (r->r_type == PROTO_RES_PCICFG) 339 pci_write_config(dev, ofs, buf.x4[0], 4); 340 else 341 bus_write_4(r->r_d.res, ofs, buf.x4[0]); 342 break; 343 #ifndef __i386__ 344 case 8: 345 if (r->r_type == PROTO_RES_PCICFG) 346 return (EINVAL); 347 bus_write_8(r->r_d.res, ofs, buf.x8[0]); 348 break; 349 #endif 350 default: 351 return (EIO); 352 } 353 354 return (0); 355 } 356 357 static int 358 proto_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, 359 struct thread *td) 360 { 361 struct proto_ioc_region *region; 362 struct proto_ioc_busdma *busdma; 363 struct proto_res *r; 364 struct proto_softc *sc; 365 int error; 366 367 sc = cdev->si_drv1; 368 r = cdev->si_drv2; 369 370 error = 0; 371 switch (cmd) { 372 case PROTO_IOC_REGION: 373 if (r->r_type == PROTO_RES_BUSDMA) { 374 error = EINVAL; 375 break; 376 } 377 region = (struct proto_ioc_region *)data; 378 region->size = r->r_size; 379 if (r->r_type == PROTO_RES_PCICFG) 380 region->address = 0; 381 else 382 region->address = rman_get_start(r->r_d.res); 383 break; 384 case PROTO_IOC_BUSDMA: 385 if (r->r_type != PROTO_RES_BUSDMA) { 386 error = EINVAL; 387 break; 388 } 389 busdma = (struct proto_ioc_busdma *)data; 390 error = proto_busdma_ioctl(sc, r->r_d.busdma, busdma); 391 break; 392 default: 393 error = ENOIOCTL; 394 break; 395 } 396 return (error); 397 } 398 399 static int 400 proto_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr, 401 int prot, vm_memattr_t *memattr) 402 { 403 struct proto_res *r; 404 405 if (offset & PAGE_MASK) 406 return (EINVAL); 407 if (prot & PROT_EXEC) 408 return (EACCES); 409 410 r = cdev->si_drv2; 411 412 switch (r->r_type) { 413 case SYS_RES_MEMORY: 414 if (offset >= r->r_size) 415 return (EINVAL); 416 *paddr = rman_get_start(r->r_d.res) + offset; 417 #ifndef __sparc64__ 418 *memattr = VM_MEMATTR_UNCACHEABLE; 419 #endif 420 break; 421 case PROTO_RES_BUSDMA: 422 if (!proto_busdma_mmap_allowed(r->r_d.busdma, offset)) 423 return (EINVAL); 424 *paddr = offset; 425 break; 426 default: 427 return (ENXIO); 428 } 429 return (0); 430 } 431