1 /*- 2 * SPDX-License-Identifier: BSD-4-Clause 3 * 4 * Copyright (c) 2002-2003 5 * Hidetoshi Shimokawa. 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * 18 * This product includes software developed by Hidetoshi Shimokawa. 19 * 20 * 4. Neither the name of the author nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 */ 37 38 #ifdef __FreeBSD__ 39 #include <sys/cdefs.h> 40 __FBSDID("$FreeBSD$"); 41 #endif 42 43 #include <sys/param.h> 44 #include <sys/systm.h> 45 #include <sys/types.h> 46 47 #include <sys/kernel.h> 48 #include <sys/malloc.h> 49 #include <sys/conf.h> 50 #include <sys/sysctl.h> 51 #include <sys/bio.h> 52 53 #include <sys/bus.h> 54 #include <machine/bus.h> 55 56 #include <sys/signal.h> 57 #include <sys/mman.h> 58 #include <sys/ioccom.h> 59 #include <sys/fcntl.h> 60 61 #include <dev/firewire/firewire.h> 62 #include <dev/firewire/firewirereg.h> 63 #include <dev/firewire/fwmem.h> 64 65 static int fwmem_speed = 2, fwmem_debug = 0; 66 static struct fw_eui64 fwmem_eui64; 67 SYSCTL_DECL(_hw_firewire); 68 static SYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0, 69 "FireWire Memory Access"); 70 SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW, 71 &fwmem_eui64.hi, 0, "Fwmem target EUI64 high"); 72 SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, CTLFLAG_RW, 73 &fwmem_eui64.lo, 0, "Fwmem target EUI64 low"); 74 SYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0, 75 "Fwmem link speed"); 76 SYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0, 77 "Fwmem driver debug flag"); 78 79 static MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/FireWire"); 80 81 #define MAXLEN (512 << fwmem_speed) 82 83 struct fwmem_softc { 84 struct fw_eui64 eui; 85 struct firewire_softc *sc; 86 int refcount; 87 }; 88 89 static struct fw_xfer * 90 fwmem_xfer_req( 91 struct fw_device *fwdev, 92 caddr_t sc, 93 int spd, 94 int slen, 95 int rlen, 96 void *hand) 97 { 98 struct fw_xfer *xfer; 99 100 xfer = fw_xfer_alloc(M_FWMEM); 101 if (xfer == NULL) 102 return NULL; 103 104 xfer->fc = fwdev->fc; 105 xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst; 106 if (spd < 0) 107 xfer->send.spd = fwdev->speed; 108 else 109 xfer->send.spd = min(spd, fwdev->speed); 110 xfer->hand = hand; 111 xfer->sc = sc; 112 xfer->send.pay_len = slen; 113 xfer->recv.pay_len = rlen; 114 115 return xfer; 116 } 117 118 struct fw_xfer * 119 fwmem_read_quad( 120 struct fw_device *fwdev, 121 caddr_t sc, 122 uint8_t spd, 123 uint16_t dst_hi, 124 uint32_t dst_lo, 125 void *data, 126 void (*hand)(struct fw_xfer *)) 127 { 128 struct fw_xfer *xfer; 129 struct fw_pkt *fp; 130 131 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 4, hand); 132 if (xfer == NULL) { 133 return NULL; 134 } 135 136 fp = &xfer->send.hdr; 137 fp->mode.rreqq.tcode = FWTCODE_RREQQ; 138 fp->mode.rreqq.dest_hi = dst_hi; 139 fp->mode.rreqq.dest_lo = dst_lo; 140 141 xfer->send.payload = NULL; 142 xfer->recv.payload = (uint32_t *)data; 143 144 if (fwmem_debug) 145 printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst, 146 dst_hi, dst_lo); 147 148 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 149 return xfer; 150 151 fw_xfer_free(xfer); 152 return NULL; 153 } 154 155 struct fw_xfer * 156 fwmem_write_quad( 157 struct fw_device *fwdev, 158 caddr_t sc, 159 uint8_t spd, 160 uint16_t dst_hi, 161 uint32_t dst_lo, 162 void *data, 163 void (*hand)(struct fw_xfer *)) 164 { 165 struct fw_xfer *xfer; 166 struct fw_pkt *fp; 167 168 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand); 169 if (xfer == NULL) 170 return NULL; 171 172 fp = &xfer->send.hdr; 173 fp->mode.wreqq.tcode = FWTCODE_WREQQ; 174 fp->mode.wreqq.dest_hi = dst_hi; 175 fp->mode.wreqq.dest_lo = dst_lo; 176 fp->mode.wreqq.data = *(uint32_t *)data; 177 178 xfer->send.payload = xfer->recv.payload = NULL; 179 180 if (fwmem_debug) 181 printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst, 182 dst_hi, dst_lo, *(uint32_t *)data); 183 184 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 185 return xfer; 186 187 fw_xfer_free(xfer); 188 return NULL; 189 } 190 191 struct fw_xfer * 192 fwmem_read_block( 193 struct fw_device *fwdev, 194 caddr_t sc, 195 uint8_t spd, 196 uint16_t dst_hi, 197 uint32_t dst_lo, 198 int len, 199 void *data, 200 void (*hand)(struct fw_xfer *)) 201 { 202 struct fw_xfer *xfer; 203 struct fw_pkt *fp; 204 205 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand); 206 if (xfer == NULL) 207 return NULL; 208 209 fp = &xfer->send.hdr; 210 fp->mode.rreqb.tcode = FWTCODE_RREQB; 211 fp->mode.rreqb.dest_hi = dst_hi; 212 fp->mode.rreqb.dest_lo = dst_lo; 213 fp->mode.rreqb.len = len; 214 fp->mode.rreqb.extcode = 0; 215 216 xfer->send.payload = NULL; 217 xfer->recv.payload = data; 218 219 if (fwmem_debug) 220 printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst, 221 dst_hi, dst_lo, len); 222 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 223 return xfer; 224 225 fw_xfer_free(xfer); 226 return NULL; 227 } 228 229 struct fw_xfer * 230 fwmem_write_block( 231 struct fw_device *fwdev, 232 caddr_t sc, 233 uint8_t spd, 234 uint16_t dst_hi, 235 uint32_t dst_lo, 236 int len, 237 void *data, 238 void (*hand)(struct fw_xfer *)) 239 { 240 struct fw_xfer *xfer; 241 struct fw_pkt *fp; 242 243 xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand); 244 if (xfer == NULL) 245 return NULL; 246 247 fp = &xfer->send.hdr; 248 fp->mode.wreqb.tcode = FWTCODE_WREQB; 249 fp->mode.wreqb.dest_hi = dst_hi; 250 fp->mode.wreqb.dest_lo = dst_lo; 251 fp->mode.wreqb.len = len; 252 fp->mode.wreqb.extcode = 0; 253 254 xfer->send.payload = data; 255 xfer->recv.payload = NULL; 256 257 if (fwmem_debug) 258 printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst, 259 dst_hi, dst_lo, len); 260 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 261 return xfer; 262 263 fw_xfer_free(xfer); 264 return NULL; 265 } 266 267 int 268 fwmem_open(struct cdev *dev, int flags, int fmt, fw_proc *td) 269 { 270 struct fwmem_softc *fms; 271 struct firewire_softc *sc; 272 int unit = DEV2UNIT(dev); 273 274 sc = devclass_get_softc(firewire_devclass, unit); 275 if (sc == NULL) 276 return (ENXIO); 277 278 FW_GLOCK(sc->fc); 279 if (dev->si_drv1 != NULL) { 280 if ((flags & FWRITE) != 0) { 281 FW_GUNLOCK(sc->fc); 282 return (EBUSY); 283 } 284 FW_GUNLOCK(sc->fc); 285 fms = dev->si_drv1; 286 fms->refcount++; 287 } else { 288 dev->si_drv1 = (void *)-1; 289 FW_GUNLOCK(sc->fc); 290 dev->si_drv1 = malloc(sizeof(struct fwmem_softc), 291 M_FWMEM, M_WAITOK); 292 dev->si_iosize_max = DFLTPHYS; 293 fms = dev->si_drv1; 294 bcopy(&fwmem_eui64, &fms->eui, sizeof(struct fw_eui64)); 295 fms->sc = sc; 296 fms->refcount = 1; 297 } 298 if (fwmem_debug) 299 printf("%s: refcount=%d\n", __func__, fms->refcount); 300 301 return (0); 302 } 303 304 int 305 fwmem_close (struct cdev *dev, int flags, int fmt, fw_proc *td) 306 { 307 struct fwmem_softc *fms; 308 309 fms = dev->si_drv1; 310 311 FW_GLOCK(fms->sc->fc); 312 fms->refcount--; 313 FW_GUNLOCK(fms->sc->fc); 314 if (fwmem_debug) 315 printf("%s: refcount=%d\n", __func__, fms->refcount); 316 if (fms->refcount < 1) { 317 free(dev->si_drv1, M_FWMEM); 318 dev->si_drv1 = NULL; 319 } 320 321 return (0); 322 } 323 324 325 static void 326 fwmem_biodone(struct fw_xfer *xfer) 327 { 328 struct bio *bp; 329 330 bp = (struct bio *)xfer->sc; 331 bp->bio_error = xfer->resp; 332 333 if (bp->bio_error != 0) { 334 if (fwmem_debug) 335 printf("%s: err=%d\n", __func__, bp->bio_error); 336 bp->bio_flags |= BIO_ERROR; 337 bp->bio_resid = bp->bio_bcount; 338 } 339 340 fw_xfer_free(xfer); 341 biodone(bp); 342 } 343 344 void 345 fwmem_strategy(struct bio *bp) 346 { 347 struct fwmem_softc *fms; 348 struct fw_device *fwdev; 349 struct fw_xfer *xfer; 350 struct cdev *dev; 351 int err = 0, iolen; 352 353 dev = bp->bio_dev; 354 /* XXX check request length */ 355 356 fms = dev->si_drv1; 357 fwdev = fw_noderesolve_eui64(fms->sc->fc, &fms->eui); 358 if (fwdev == NULL) { 359 if (fwmem_debug) 360 printf("fwmem: no such device ID:%08x%08x\n", 361 fms->eui.hi, fms->eui.lo); 362 err = EINVAL; 363 goto error; 364 } 365 366 iolen = MIN(bp->bio_bcount, MAXLEN); 367 if (bp->bio_cmd == BIO_READ) { 368 if (iolen == 4 && (bp->bio_offset & 3) == 0) 369 xfer = fwmem_read_quad(fwdev, 370 (void *)bp, fwmem_speed, 371 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 372 bp->bio_data, fwmem_biodone); 373 else 374 xfer = fwmem_read_block(fwdev, 375 (void *)bp, fwmem_speed, 376 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 377 iolen, bp->bio_data, fwmem_biodone); 378 } else { 379 if (iolen == 4 && (bp->bio_offset & 3) == 0) 380 xfer = fwmem_write_quad(fwdev, 381 (void *)bp, fwmem_speed, 382 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 383 bp->bio_data, fwmem_biodone); 384 else 385 xfer = fwmem_write_block(fwdev, 386 (void *)bp, fwmem_speed, 387 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 388 iolen, bp->bio_data, fwmem_biodone); 389 } 390 if (xfer == NULL) { 391 err = EIO; 392 goto error; 393 } 394 /* XXX */ 395 bp->bio_resid = bp->bio_bcount - iolen; 396 error: 397 if (err != 0) { 398 if (fwmem_debug) 399 printf("%s: err=%d\n", __func__, err); 400 bp->bio_error = err; 401 bp->bio_flags |= BIO_ERROR; 402 bp->bio_resid = bp->bio_bcount; 403 biodone(bp); 404 } 405 } 406 407 int 408 fwmem_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, fw_proc *td) 409 { 410 struct fwmem_softc *fms; 411 int err = 0; 412 413 fms = dev->si_drv1; 414 switch (cmd) { 415 case FW_SDEUI64: 416 bcopy(data, &fms->eui, sizeof(struct fw_eui64)); 417 break; 418 case FW_GDEUI64: 419 bcopy(&fms->eui, data, sizeof(struct fw_eui64)); 420 break; 421 default: 422 err = EINVAL; 423 } 424 return (err); 425 } 426 427 int 428 fwmem_poll(struct cdev *dev, int events, fw_proc *td) 429 { 430 return EINVAL; 431 } 432 433 int 434 fwmem_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, 435 int nproto, vm_memattr_t *memattr) 436 { 437 return EINVAL; 438 } 439