1 /* $NetBSD: fwmem.c,v 1.17 2012/04/29 18:31:40 dsl 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.17 2012/04/29 18:31:40 dsl 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, NULL, 77 CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL, 78 NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0) { 79 goto err; 80 } 81 82 if ((rc = sysctl_createv(clog, 0, NULL, &node, 83 CTLFLAG_PERMANENT, CTLTYPE_NODE, "fwmem", 84 SYSCTL_DESCR("IEEE1394 Memory Access"), 85 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) { 86 goto err; 87 } 88 fwmem_node_num = node->sysctl_num; 89 90 /* fwmem target EUI64 high/low */ 91 if ((rc = sysctl_createv(clog, 0, NULL, &node, 92 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, 93 "eui64_hi", SYSCTL_DESCR("Fwmem target EUI64 high"), 94 NULL, 0, &fwmem_eui64.hi, 95 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) { 96 goto err; 97 } 98 if ((rc = sysctl_createv(clog, 0, NULL, &node, 99 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, 100 "eui64_lo", SYSCTL_DESCR("Fwmem target EUI64 low"), 101 NULL, 0, &fwmem_eui64.lo, 102 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) { 103 goto err; 104 } 105 106 /* fwmem link speed */ 107 if ((rc = sysctl_createv(clog, 0, NULL, &node, 108 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, 109 "speed", SYSCTL_DESCR("Fwmem link speed"), 110 sysctl_fwmem_verify_speed, 0, &fwmem_speed, 111 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) { 112 goto err; 113 } 114 115 /* fwmem driver debug flag */ 116 if ((rc = sysctl_createv(clog, 0, NULL, &node, 117 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, 118 "fwmem_debug", SYSCTL_DESCR("Fwmem driver debug flag"), 119 NULL, 0, &fwmem_debug, 120 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) { 121 goto err; 122 } 123 124 return; 125 126 err: 127 printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc); 128 } 129 130 static int 131 sysctl_fwmem_verify(SYSCTLFN_ARGS, int lower, int upper) 132 { 133 int error, t; 134 struct sysctlnode node; 135 136 node = *rnode; 137 t = *(int*)rnode->sysctl_data; 138 node.sysctl_data = &t; 139 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 140 if (error || newp == NULL) 141 return error; 142 143 if (t < lower || t > upper) 144 return EINVAL; 145 146 *(int*)rnode->sysctl_data = t; 147 148 return 0; 149 } 150 151 static int 152 sysctl_fwmem_verify_speed(SYSCTLFN_ARGS) 153 { 154 155 return sysctl_fwmem_verify(SYSCTLFN_CALL(rnode), 0, FWSPD_S400); 156 } 157 158 #define MAXLEN (512 << fwmem_speed) 159 160 struct fwmem_softc { 161 struct fw_eui64 eui; 162 struct firewire_softc *sc; 163 int refcount; 164 STAILQ_HEAD(, fw_xfer) xferlist; 165 }; 166 167 168 int 169 fwmem_open(dev_t dev, int flags, int fmt, struct lwp *td) 170 { 171 struct firewire_softc *sc; 172 struct fwmem_softc *fms; 173 struct fw_xfer *xfer; 174 175 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev)); 176 if (sc == NULL) 177 return ENXIO; 178 179 if (sc->si_drv1 != NULL) { 180 if ((flags & FWRITE) != 0) { 181 return EBUSY; 182 } 183 fms = (struct fwmem_softc *)sc->si_drv1; 184 fms->refcount++; 185 } else { 186 sc->si_drv1 = (void *)-1; 187 sc->si_drv1 = malloc(sizeof(struct fwmem_softc), 188 M_FW, M_WAITOK); 189 if (sc->si_drv1 == NULL) 190 return ENOMEM; 191 fms = (struct fwmem_softc *)sc->si_drv1; 192 memcpy(&fms->eui, &fwmem_eui64, sizeof(struct fw_eui64)); 193 fms->sc = sc; 194 fms->refcount = 1; 195 STAILQ_INIT(&fms->xferlist); 196 xfer = fw_xfer_alloc(M_FW); 197 STAILQ_INSERT_TAIL(&fms->xferlist, xfer, link); 198 } 199 if (fwmem_debug) 200 printf("%s: refcount=%d\n", __func__, fms->refcount); 201 202 return 0; 203 } 204 205 int 206 fwmem_close(dev_t dev, int flags, int fmt, struct lwp *td) 207 { 208 struct firewire_softc *sc; 209 struct fwmem_softc *fms; 210 struct fw_xfer *xfer; 211 212 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev)); 213 if (sc == NULL) 214 return ENXIO; 215 216 fms = (struct fwmem_softc *)sc->si_drv1; 217 218 fms->refcount--; 219 if (fwmem_debug) 220 printf("%s: refcount=%d\n", __func__, fms->refcount); 221 if (fms->refcount < 1) { 222 while ((xfer = STAILQ_FIRST(&fms->xferlist)) != NULL) { 223 STAILQ_REMOVE_HEAD(&fms->xferlist, link); 224 fw_xfer_free(xfer); 225 } 226 free(sc->si_drv1, M_FW); 227 sc->si_drv1 = NULL; 228 } 229 230 return 0; 231 } 232 233 void 234 fwmem_strategy(struct bio *bp) 235 { 236 struct firewire_softc *sc; 237 struct fwmem_softc *fms; 238 struct fw_device *fwdev; 239 struct fw_xfer *xfer; 240 dev_t dev = bp->bio_dev; 241 int iolen, err = 0, s; 242 243 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev)); 244 if (sc == NULL) 245 return; 246 247 /* XXX check request length */ 248 249 s = splvm(); 250 fms = (struct fwmem_softc *)sc->si_drv1; 251 fwdev = fw_noderesolve_eui64(fms->sc->fc, &fms->eui); 252 if (fwdev == NULL) { 253 if (fwmem_debug) 254 printf("fwmem: no such device ID:%08x%08x\n", 255 fms->eui.hi, fms->eui.lo); 256 err = EINVAL; 257 goto error; 258 } 259 260 iolen = MIN(bp->bio_bcount, MAXLEN); 261 if ((bp->bio_cmd & BIO_READ) == BIO_READ) { 262 if (iolen == 4 && (bp->bio_offset & 3) == 0) 263 xfer = fwmem_read_quad(fwdev, (void *) bp, fwmem_speed, 264 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 265 bp->bio_data, fwmem_biodone); 266 else 267 xfer = fwmem_read_block(fwdev, (void *) bp, fwmem_speed, 268 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 269 iolen, bp->bio_data, fwmem_biodone); 270 } else { 271 if (iolen == 4 && (bp->bio_offset & 3) == 0) 272 xfer = fwmem_write_quad(fwdev, (void *)bp, fwmem_speed, 273 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 274 bp->bio_data, fwmem_biodone); 275 else 276 xfer = fwmem_write_block(fwdev, (void *)bp, fwmem_speed, 277 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 278 iolen, bp->bio_data, fwmem_biodone); 279 } 280 if (xfer == NULL) { 281 err = EIO; 282 goto error; 283 } 284 /* XXX */ 285 bp->bio_resid = bp->bio_bcount - iolen; 286 error: 287 splx(s); 288 if (err != 0) { 289 if (fwmem_debug) 290 printf("%s: err=%d\n", __func__, err); 291 bp->bio_error = err; 292 bp->bio_resid = bp->bio_bcount; 293 biodone(bp); 294 } 295 } 296 297 int 298 fwmem_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *td) 299 { 300 struct firewire_softc *sc; 301 struct fwmem_softc *fms; 302 int err = 0; 303 304 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev)); 305 if (sc == NULL) 306 return ENXIO; 307 308 fms = (struct fwmem_softc *)sc->si_drv1; 309 switch (cmd) { 310 case FW_SDEUI64: 311 memcpy(&fms->eui, data, sizeof(struct fw_eui64)); 312 break; 313 314 case FW_GDEUI64: 315 memcpy(data, &fms->eui, sizeof(struct fw_eui64)); 316 break; 317 318 default: 319 err = EINVAL; 320 } 321 return err; 322 } 323 324 325 struct fw_xfer * 326 fwmem_read_quad(struct fw_device *fwdev, void * sc, uint8_t spd, 327 uint16_t dst_hi, uint32_t dst_lo, void *data, 328 void (*hand)(struct fw_xfer *)) 329 { 330 struct fw_xfer *xfer; 331 struct fw_pkt *fp; 332 333 xfer = fwmem_xfer_req(fwdev, (void *)sc, spd, 0, 4, hand); 334 if (xfer == NULL) 335 return NULL; 336 337 fp = &xfer->send.hdr; 338 fp->mode.rreqq.tcode = FWTCODE_RREQQ; 339 fp->mode.rreqq.dest_hi = dst_hi; 340 fp->mode.rreqq.dest_lo = dst_lo; 341 342 xfer->send.payload = NULL; 343 xfer->recv.payload = (uint32_t *)data; 344 345 if (fwmem_debug) 346 aprint_error("fwmem_read_quad: %d %04x:%08x\n", 347 fwdev->dst, dst_hi, dst_lo); 348 349 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 350 return xfer; 351 352 fw_xfer_free(xfer); 353 return NULL; 354 } 355 356 struct fw_xfer * 357 fwmem_write_quad(struct fw_device *fwdev, void *sc, uint8_t spd, 358 uint16_t dst_hi, uint32_t dst_lo, void *data, 359 void (*hand)(struct fw_xfer *)) 360 { 361 struct fw_xfer *xfer; 362 struct fw_pkt *fp; 363 364 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand); 365 if (xfer == NULL) 366 return NULL; 367 368 fp = &xfer->send.hdr; 369 fp->mode.wreqq.tcode = FWTCODE_WREQQ; 370 fp->mode.wreqq.dest_hi = dst_hi; 371 fp->mode.wreqq.dest_lo = dst_lo; 372 fp->mode.wreqq.data = *(uint32_t *)data; 373 374 xfer->send.payload = xfer->recv.payload = NULL; 375 376 if (fwmem_debug) 377 aprint_error("fwmem_write_quad: %d %04x:%08x %08x\n", 378 fwdev->dst, dst_hi, dst_lo, *(uint32_t *)data); 379 380 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 381 return xfer; 382 383 fw_xfer_free(xfer); 384 return NULL; 385 } 386 387 struct fw_xfer * 388 fwmem_read_block(struct fw_device *fwdev, void *sc, uint8_t spd, 389 uint16_t dst_hi, uint32_t dst_lo, int len, void *data, 390 void (*hand)(struct fw_xfer *)) 391 { 392 struct fw_xfer *xfer; 393 struct fw_pkt *fp; 394 395 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand); 396 if (xfer == NULL) 397 return NULL; 398 399 fp = &xfer->send.hdr; 400 fp->mode.rreqb.tcode = FWTCODE_RREQB; 401 fp->mode.rreqb.dest_hi = dst_hi; 402 fp->mode.rreqb.dest_lo = dst_lo; 403 fp->mode.rreqb.len = len; 404 fp->mode.rreqb.extcode = 0; 405 406 xfer->send.payload = NULL; 407 xfer->recv.payload = data; 408 409 if (fwmem_debug) 410 aprint_error("fwmem_read_block: %d %04x:%08x %d\n", 411 fwdev->dst, dst_hi, dst_lo, len); 412 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 413 return xfer; 414 415 fw_xfer_free(xfer); 416 return NULL; 417 } 418 419 struct fw_xfer * 420 fwmem_write_block(struct fw_device *fwdev, void *sc, uint8_t spd, 421 uint16_t dst_hi, uint32_t dst_lo, int len, void *data, 422 void (*hand)(struct fw_xfer *)) 423 { 424 struct fw_xfer *xfer; 425 struct fw_pkt *fp; 426 427 xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand); 428 if (xfer == NULL) 429 return NULL; 430 431 fp = &xfer->send.hdr; 432 fp->mode.wreqb.tcode = FWTCODE_WREQB; 433 fp->mode.wreqb.dest_hi = dst_hi; 434 fp->mode.wreqb.dest_lo = dst_lo; 435 fp->mode.wreqb.len = len; 436 fp->mode.wreqb.extcode = 0; 437 438 xfer->send.payload = data; 439 xfer->recv.payload = NULL; 440 441 if (fwmem_debug) 442 aprint_error("fwmem_write_block: %d %04x:%08x %d\n", 443 fwdev->dst, dst_hi, dst_lo, len); 444 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 445 return xfer; 446 447 fw_xfer_free(xfer); 448 return NULL; 449 } 450 451 452 static struct fw_xfer * 453 fwmem_xfer_req(struct fw_device *fwdev, void *sc, int spd, int slen, int rlen, 454 void *hand) 455 { 456 struct fw_xfer *xfer; 457 458 xfer = fw_xfer_alloc(M_FW); 459 if (xfer == NULL) 460 return NULL; 461 462 xfer->fc = fwdev->fc; 463 xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst; 464 if (spd < 0) 465 xfer->send.spd = fwdev->speed; 466 else 467 xfer->send.spd = min(spd, fwdev->speed); 468 xfer->hand = hand; 469 xfer->sc = sc; 470 xfer->send.pay_len = slen; 471 xfer->recv.pay_len = rlen; 472 473 return xfer; 474 } 475 476 static void 477 fwmem_biodone(struct fw_xfer *xfer) 478 { 479 struct bio *bp; 480 481 bp = (struct bio *)xfer->sc; 482 bp->bio_error = xfer->resp; 483 484 if (bp->bio_error != 0) { 485 if (fwmem_debug) 486 printf("%s: err=%d\n", __func__, bp->bio_error); 487 bp->bio_resid = bp->bio_bcount; 488 } 489 490 fw_xfer_free(xfer); 491 biodone(bp); 492 } 493