1 /* $NetBSD: fwmem.c,v 1.19 2018/09/03 16:29:31 riastradh Exp $ */ 2 /*- 3 * Copyright (c) 2002-2003 4 * Hidetoshi Shimokawa. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 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 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * 17 * This product includes software developed by Hidetoshi Shimokawa. 18 * 19 * 4. Neither the name of the author nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: fwmem.c,v 1.19 2018/09/03 16:29:31 riastradh Exp $"); 39 40 #include <sys/param.h> 41 #include <sys/device.h> 42 #include <sys/errno.h> 43 #include <sys/buf.h> 44 #include <sys/bus.h> 45 #include <sys/conf.h> 46 #include <sys/fcntl.h> 47 #include <sys/malloc.h> 48 #include <sys/sysctl.h> 49 50 #include <dev/ieee1394/firewire.h> 51 #include <dev/ieee1394/firewirereg.h> 52 #include <dev/ieee1394/fwmem.h> 53 54 #include "ioconf.h" 55 56 static int fwmem_speed=2, fwmem_debug=0; 57 static struct fw_eui64 fwmem_eui64; 58 59 static int sysctl_fwmem_verify(SYSCTLFN_PROTO, int, int); 60 static int sysctl_fwmem_verify_speed(SYSCTLFN_PROTO); 61 62 static struct fw_xfer *fwmem_xfer_req(struct fw_device *, void *, int, int, 63 int, void *); 64 static void fwmem_biodone(struct fw_xfer *); 65 66 /* 67 * Setup sysctl(3) MIB, hw.fwmem.* 68 * 69 * TBD condition CTLFLAG_PERMANENT on being a module or not 70 */ 71 SYSCTL_SETUP(sysctl_fwmem, "sysctl fwmem subtree setup") 72 { 73 int rc, fwmem_node_num; 74 const struct sysctlnode *node; 75 76 if ((rc = sysctl_createv(clog, 0, NULL, &node, 77 CTLFLAG_PERMANENT, CTLTYPE_NODE, "fwmem", 78 SYSCTL_DESCR("IEEE1394 Memory Access"), 79 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) { 80 goto err; 81 } 82 fwmem_node_num = node->sysctl_num; 83 84 /* fwmem target EUI64 high/low */ 85 if ((rc = sysctl_createv(clog, 0, NULL, &node, 86 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, 87 "eui64_hi", SYSCTL_DESCR("Fwmem target EUI64 high"), 88 NULL, 0, &fwmem_eui64.hi, 89 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) { 90 goto err; 91 } 92 if ((rc = sysctl_createv(clog, 0, NULL, &node, 93 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, 94 "eui64_lo", SYSCTL_DESCR("Fwmem target EUI64 low"), 95 NULL, 0, &fwmem_eui64.lo, 96 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) { 97 goto err; 98 } 99 100 /* fwmem link speed */ 101 if ((rc = sysctl_createv(clog, 0, NULL, &node, 102 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, 103 "speed", SYSCTL_DESCR("Fwmem link speed"), 104 sysctl_fwmem_verify_speed, 0, &fwmem_speed, 105 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) { 106 goto err; 107 } 108 109 /* fwmem driver debug flag */ 110 if ((rc = sysctl_createv(clog, 0, NULL, &node, 111 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, 112 "fwmem_debug", SYSCTL_DESCR("Fwmem driver debug flag"), 113 NULL, 0, &fwmem_debug, 114 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) { 115 goto err; 116 } 117 118 return; 119 120 err: 121 printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc); 122 } 123 124 static int 125 sysctl_fwmem_verify(SYSCTLFN_ARGS, int lower, int upper) 126 { 127 int error, t; 128 struct sysctlnode node; 129 130 node = *rnode; 131 t = *(int*)rnode->sysctl_data; 132 node.sysctl_data = &t; 133 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 134 if (error || newp == NULL) 135 return error; 136 137 if (t < lower || t > upper) 138 return EINVAL; 139 140 *(int*)rnode->sysctl_data = t; 141 142 return 0; 143 } 144 145 static int 146 sysctl_fwmem_verify_speed(SYSCTLFN_ARGS) 147 { 148 149 return sysctl_fwmem_verify(SYSCTLFN_CALL(rnode), 0, FWSPD_S400); 150 } 151 152 #define MAXLEN (512 << fwmem_speed) 153 154 struct fwmem_softc { 155 struct fw_eui64 eui; 156 struct firewire_softc *sc; 157 int refcount; 158 STAILQ_HEAD(, fw_xfer) xferlist; 159 }; 160 161 162 int 163 fwmem_open(dev_t dev, int flags, int fmt, struct lwp *td) 164 { 165 struct firewire_softc *sc; 166 struct fwmem_softc *fms; 167 struct fw_xfer *xfer; 168 169 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev)); 170 if (sc == NULL) 171 return ENXIO; 172 173 if (sc->si_drv1 != NULL) { 174 if ((flags & FWRITE) != 0) { 175 return EBUSY; 176 } 177 fms = (struct fwmem_softc *)sc->si_drv1; 178 fms->refcount++; 179 } else { 180 sc->si_drv1 = (void *)-1; 181 sc->si_drv1 = malloc(sizeof(struct fwmem_softc), 182 M_FW, M_WAITOK); 183 if (sc->si_drv1 == NULL) 184 return ENOMEM; 185 fms = (struct fwmem_softc *)sc->si_drv1; 186 memcpy(&fms->eui, &fwmem_eui64, sizeof(struct fw_eui64)); 187 fms->sc = sc; 188 fms->refcount = 1; 189 STAILQ_INIT(&fms->xferlist); 190 xfer = fw_xfer_alloc(M_FW); 191 STAILQ_INSERT_TAIL(&fms->xferlist, xfer, link); 192 } 193 if (fwmem_debug) 194 printf("%s: refcount=%d\n", __func__, fms->refcount); 195 196 return 0; 197 } 198 199 int 200 fwmem_close(dev_t dev, int flags, int fmt, struct lwp *td) 201 { 202 struct firewire_softc *sc; 203 struct fwmem_softc *fms; 204 struct fw_xfer *xfer; 205 206 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev)); 207 if (sc == NULL) 208 return ENXIO; 209 210 fms = (struct fwmem_softc *)sc->si_drv1; 211 212 fms->refcount--; 213 if (fwmem_debug) 214 printf("%s: refcount=%d\n", __func__, fms->refcount); 215 if (fms->refcount < 1) { 216 while ((xfer = STAILQ_FIRST(&fms->xferlist)) != NULL) { 217 STAILQ_REMOVE_HEAD(&fms->xferlist, link); 218 fw_xfer_free(xfer); 219 } 220 free(sc->si_drv1, M_FW); 221 sc->si_drv1 = NULL; 222 } 223 224 return 0; 225 } 226 227 void 228 fwmem_strategy(struct bio *bp) 229 { 230 struct firewire_softc *sc; 231 struct fwmem_softc *fms; 232 struct fw_device *fwdev; 233 struct fw_xfer *xfer; 234 dev_t dev = bp->bio_dev; 235 int iolen, err = 0, s; 236 237 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev)); 238 if (sc == NULL) 239 return; 240 241 /* XXX check request length */ 242 243 s = splvm(); 244 fms = (struct fwmem_softc *)sc->si_drv1; 245 fwdev = fw_noderesolve_eui64(fms->sc->fc, &fms->eui); 246 if (fwdev == NULL) { 247 if (fwmem_debug) 248 printf("fwmem: no such device ID:%08x%08x\n", 249 fms->eui.hi, fms->eui.lo); 250 err = EINVAL; 251 goto error; 252 } 253 254 iolen = MIN(bp->bio_bcount, MAXLEN); 255 if ((bp->bio_cmd & BIO_READ) == BIO_READ) { 256 if (iolen == 4 && (bp->bio_offset & 3) == 0) 257 xfer = fwmem_read_quad(fwdev, (void *) bp, fwmem_speed, 258 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 259 bp->bio_data, fwmem_biodone); 260 else 261 xfer = fwmem_read_block(fwdev, (void *) bp, fwmem_speed, 262 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 263 iolen, bp->bio_data, fwmem_biodone); 264 } else { 265 if (iolen == 4 && (bp->bio_offset & 3) == 0) 266 xfer = fwmem_write_quad(fwdev, (void *)bp, fwmem_speed, 267 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 268 bp->bio_data, fwmem_biodone); 269 else 270 xfer = fwmem_write_block(fwdev, (void *)bp, fwmem_speed, 271 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 272 iolen, bp->bio_data, fwmem_biodone); 273 } 274 if (xfer == NULL) { 275 err = EIO; 276 goto error; 277 } 278 /* XXX */ 279 bp->bio_resid = bp->bio_bcount - iolen; 280 error: 281 splx(s); 282 if (err != 0) { 283 if (fwmem_debug) 284 printf("%s: err=%d\n", __func__, err); 285 bp->bio_error = err; 286 bp->bio_resid = bp->bio_bcount; 287 biodone(bp); 288 } 289 } 290 291 int 292 fwmem_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *td) 293 { 294 struct firewire_softc *sc; 295 struct fwmem_softc *fms; 296 int err = 0; 297 298 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev)); 299 if (sc == NULL) 300 return ENXIO; 301 302 fms = (struct fwmem_softc *)sc->si_drv1; 303 switch (cmd) { 304 case FW_SDEUI64: 305 memcpy(&fms->eui, data, sizeof(struct fw_eui64)); 306 break; 307 308 case FW_GDEUI64: 309 memcpy(data, &fms->eui, sizeof(struct fw_eui64)); 310 break; 311 312 default: 313 err = EINVAL; 314 } 315 return err; 316 } 317 318 319 struct fw_xfer * 320 fwmem_read_quad(struct fw_device *fwdev, void * sc, uint8_t spd, 321 uint16_t dst_hi, uint32_t dst_lo, void *data, 322 void (*hand)(struct fw_xfer *)) 323 { 324 struct fw_xfer *xfer; 325 struct fw_pkt *fp; 326 327 xfer = fwmem_xfer_req(fwdev, (void *)sc, spd, 0, 4, hand); 328 if (xfer == NULL) 329 return NULL; 330 331 fp = &xfer->send.hdr; 332 fp->mode.rreqq.tcode = FWTCODE_RREQQ; 333 fp->mode.rreqq.dest_hi = dst_hi; 334 fp->mode.rreqq.dest_lo = dst_lo; 335 336 xfer->send.payload = NULL; 337 xfer->recv.payload = (uint32_t *)data; 338 339 if (fwmem_debug) 340 aprint_error("fwmem_read_quad: %d %04x:%08x\n", 341 fwdev->dst, dst_hi, dst_lo); 342 343 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 344 return xfer; 345 346 fw_xfer_free(xfer); 347 return NULL; 348 } 349 350 struct fw_xfer * 351 fwmem_write_quad(struct fw_device *fwdev, void *sc, uint8_t spd, 352 uint16_t dst_hi, uint32_t dst_lo, void *data, 353 void (*hand)(struct fw_xfer *)) 354 { 355 struct fw_xfer *xfer; 356 struct fw_pkt *fp; 357 358 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand); 359 if (xfer == NULL) 360 return NULL; 361 362 fp = &xfer->send.hdr; 363 fp->mode.wreqq.tcode = FWTCODE_WREQQ; 364 fp->mode.wreqq.dest_hi = dst_hi; 365 fp->mode.wreqq.dest_lo = dst_lo; 366 fp->mode.wreqq.data = *(uint32_t *)data; 367 368 xfer->send.payload = xfer->recv.payload = NULL; 369 370 if (fwmem_debug) 371 aprint_error("fwmem_write_quad: %d %04x:%08x %08x\n", 372 fwdev->dst, dst_hi, dst_lo, *(uint32_t *)data); 373 374 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 375 return xfer; 376 377 fw_xfer_free(xfer); 378 return NULL; 379 } 380 381 struct fw_xfer * 382 fwmem_read_block(struct fw_device *fwdev, void *sc, uint8_t spd, 383 uint16_t dst_hi, uint32_t dst_lo, int len, void *data, 384 void (*hand)(struct fw_xfer *)) 385 { 386 struct fw_xfer *xfer; 387 struct fw_pkt *fp; 388 389 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand); 390 if (xfer == NULL) 391 return NULL; 392 393 fp = &xfer->send.hdr; 394 fp->mode.rreqb.tcode = FWTCODE_RREQB; 395 fp->mode.rreqb.dest_hi = dst_hi; 396 fp->mode.rreqb.dest_lo = dst_lo; 397 fp->mode.rreqb.len = len; 398 fp->mode.rreqb.extcode = 0; 399 400 xfer->send.payload = NULL; 401 xfer->recv.payload = data; 402 403 if (fwmem_debug) 404 aprint_error("fwmem_read_block: %d %04x:%08x %d\n", 405 fwdev->dst, dst_hi, dst_lo, len); 406 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 407 return xfer; 408 409 fw_xfer_free(xfer); 410 return NULL; 411 } 412 413 struct fw_xfer * 414 fwmem_write_block(struct fw_device *fwdev, void *sc, uint8_t spd, 415 uint16_t dst_hi, uint32_t dst_lo, int len, void *data, 416 void (*hand)(struct fw_xfer *)) 417 { 418 struct fw_xfer *xfer; 419 struct fw_pkt *fp; 420 421 xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand); 422 if (xfer == NULL) 423 return NULL; 424 425 fp = &xfer->send.hdr; 426 fp->mode.wreqb.tcode = FWTCODE_WREQB; 427 fp->mode.wreqb.dest_hi = dst_hi; 428 fp->mode.wreqb.dest_lo = dst_lo; 429 fp->mode.wreqb.len = len; 430 fp->mode.wreqb.extcode = 0; 431 432 xfer->send.payload = data; 433 xfer->recv.payload = NULL; 434 435 if (fwmem_debug) 436 aprint_error("fwmem_write_block: %d %04x:%08x %d\n", 437 fwdev->dst, dst_hi, dst_lo, len); 438 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 439 return xfer; 440 441 fw_xfer_free(xfer); 442 return NULL; 443 } 444 445 446 static struct fw_xfer * 447 fwmem_xfer_req(struct fw_device *fwdev, void *sc, int spd, int slen, int rlen, 448 void *hand) 449 { 450 struct fw_xfer *xfer; 451 452 xfer = fw_xfer_alloc(M_FW); 453 if (xfer == NULL) 454 return NULL; 455 456 xfer->fc = fwdev->fc; 457 xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst; 458 if (spd < 0) 459 xfer->send.spd = fwdev->speed; 460 else 461 xfer->send.spd = uimin(spd, fwdev->speed); 462 xfer->hand = hand; 463 xfer->sc = sc; 464 xfer->send.pay_len = slen; 465 xfer->recv.pay_len = rlen; 466 467 return xfer; 468 } 469 470 static void 471 fwmem_biodone(struct fw_xfer *xfer) 472 { 473 struct bio *bp; 474 475 bp = (struct bio *)xfer->sc; 476 bp->bio_error = xfer->resp; 477 478 if (bp->bio_error != 0) { 479 if (fwmem_debug) 480 printf("%s: err=%d\n", __func__, bp->bio_error); 481 bp->bio_resid = bp->bio_bcount; 482 } 483 484 fw_xfer_free(xfer); 485 biodone(bp); 486 } 487