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