1 /* $NetBSD: fwmem.c,v 1.2 2005/12/11 12:22:02 christos 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 #ifdef __FBSDID 39 __FBSDID("$FreeBSD: /repoman/r/ncvs/src/sys/dev/firewire/fwmem.c,v 1.31 2005/01/06 01:42:41 imp Exp $"); 40 #endif 41 42 #if defined(__FreeBSD__) 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 #if defined(__DragonFly__) || __FreeBSD_version < 500000 52 #include <sys/buf.h> 53 #else 54 #include <sys/bio.h> 55 #endif 56 57 #include <sys/bus.h> 58 #include <machine/bus.h> 59 60 #include <sys/signal.h> 61 #include <sys/mman.h> 62 #include <sys/ioccom.h> 63 #include <sys/fcntl.h> 64 #include <sys/ktr.h> 65 66 #ifdef __DragonFly__ 67 #include "fw_port.h" 68 #include "firewire.h" 69 #include "firewirereg.h" 70 #include "fwmem.h" 71 #else 72 #include <dev/firewire/fw_port.h> 73 #include <dev/firewire/firewire.h> 74 #include <dev/firewire/firewirereg.h> 75 #include <dev/firewire/fwmem.h> 76 #endif 77 #elif defined(__NetBSD__) 78 #include <sys/param.h> 79 #include <sys/device.h> 80 #include <sys/errno.h> 81 #include <sys/buf.h> 82 #include <sys/conf.h> 83 #include <sys/fcntl.h> 84 #include <sys/malloc.h> 85 #include <sys/sysctl.h> 86 87 #include <machine/bus.h> 88 89 #include <dev/ieee1394/fw_port.h> 90 #include <dev/ieee1394/firewire.h> 91 #include <dev/ieee1394/firewirereg.h> 92 #include <dev/ieee1394/fwmem.h> 93 #endif 94 95 static int fwmem_speed=2, fwmem_debug=0; 96 static struct fw_eui64 fwmem_eui64; 97 #if defined(__FreeBSD__) 98 SYSCTL_DECL(_hw_firewire); 99 SYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0, 100 "FireWire Memory Access"); 101 SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW, 102 &fwmem_eui64.hi, 0, "Fwmem target EUI64 high"); 103 SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, CTLFLAG_RW, 104 &fwmem_eui64.lo, 0, "Fwmem target EUI64 low"); 105 SYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0, 106 "Fwmem link speed"); 107 SYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0, 108 "Fwmem driver debug flag"); 109 MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/FireWire"); 110 #elif defined(__NetBSD__) 111 static int sysctl_fwmem_verify(SYSCTLFN_PROTO, int, int); 112 static int sysctl_fwmem_verify_speed(SYSCTLFN_PROTO); 113 114 /* 115 * Setup sysctl(3) MIB, hw.fwmem.* 116 * 117 * TBD condition CTLFLAG_PERMANENT on being an LKM or not 118 */ 119 SYSCTL_SETUP(sysctl_fwmem, "sysctl fwmem subtree setup") 120 { 121 int rc, fwmem_node_num; 122 const struct sysctlnode *node; 123 124 if ((rc = sysctl_createv(clog, 0, NULL, NULL, 125 CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL, 126 NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0) { 127 goto err; 128 } 129 130 if ((rc = sysctl_createv(clog, 0, NULL, &node, 131 CTLFLAG_PERMANENT, CTLTYPE_NODE, "fwmem", 132 SYSCTL_DESCR("IEEE1394 Memory Access"), 133 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) { 134 goto err; 135 } 136 fwmem_node_num = node->sysctl_num; 137 138 /* fwmem target EUI64 high/low */ 139 if ((rc = sysctl_createv(clog, 0, NULL, &node, 140 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, 141 "eui64_hi", SYSCTL_DESCR("Fwmem target EUI64 high"), 142 NULL, 0, &fwmem_eui64.hi, 143 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) { 144 goto err; 145 } 146 if ((rc = sysctl_createv(clog, 0, NULL, &node, 147 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, 148 "eui64_lo", SYSCTL_DESCR("Fwmem target EUI64 low"), 149 NULL, 0, &fwmem_eui64.lo, 150 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) { 151 goto err; 152 } 153 154 /* fwmem link speed */ 155 if ((rc = sysctl_createv(clog, 0, NULL, &node, 156 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, 157 "speed", SYSCTL_DESCR("Fwmem link speed"), 158 sysctl_fwmem_verify_speed, 0, &fwmem_speed, 159 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) { 160 goto err; 161 } 162 163 /* fwmem driver debug flag */ 164 if ((rc = sysctl_createv(clog, 0, NULL, &node, 165 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, 166 "fwmem_debug", SYSCTL_DESCR("Fwmem driver debug flag"), 167 NULL, 0, &fwmem_debug, 168 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) { 169 goto err; 170 } 171 172 return; 173 174 err: 175 printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc); 176 } 177 178 static int 179 sysctl_fwmem_verify(SYSCTLFN_ARGS, int lower, int upper) 180 { 181 int error, t; 182 struct sysctlnode node; 183 184 node = *rnode; 185 t = *(int*)rnode->sysctl_data; 186 node.sysctl_data = &t; 187 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 188 if (error || newp == NULL) 189 return (error); 190 191 if (t < lower || t > upper) 192 return (EINVAL); 193 194 *(int*)rnode->sysctl_data = t; 195 196 return (0); 197 } 198 199 static int 200 sysctl_fwmem_verify_speed(SYSCTLFN_ARGS) 201 { 202 return (sysctl_fwmem_verify(SYSCTLFN_CALL(rnode), 0, FWSPD_S400)); 203 } 204 205 MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/IEEE1394"); 206 #endif 207 208 #define MAXLEN (512 << fwmem_speed) 209 210 struct fwmem_softc { 211 struct fw_eui64 eui; 212 int refcount; 213 STAILQ_HEAD(, fw_xfer) xferlist; 214 }; 215 216 static struct fw_xfer * 217 fwmem_xfer_req( 218 struct fw_device *fwdev, 219 caddr_t sc, 220 int spd, 221 int slen, 222 int rlen, 223 void *hand) 224 { 225 struct fw_xfer *xfer; 226 227 xfer = fw_xfer_alloc(M_FWMEM); 228 if (xfer == NULL) 229 return NULL; 230 231 xfer->fc = fwdev->fc; 232 xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst; 233 if (spd < 0) 234 xfer->send.spd = fwdev->speed; 235 else 236 xfer->send.spd = min(spd, fwdev->speed); 237 xfer->hand = hand; 238 xfer->sc = sc; 239 xfer->send.pay_len = slen; 240 xfer->recv.pay_len = rlen; 241 242 return xfer; 243 } 244 245 struct fw_xfer * 246 fwmem_read_quad( 247 struct fw_device *fwdev, 248 caddr_t sc, 249 uint8_t spd, 250 uint16_t dst_hi, 251 uint32_t dst_lo, 252 void *data, 253 void (*hand)(struct fw_xfer *)) 254 { 255 struct fw_xfer *xfer; 256 struct fw_pkt *fp; 257 258 xfer = fwmem_xfer_req(fwdev, (void *)sc, spd, 0, 4, hand); 259 if (xfer == NULL) { 260 return NULL; 261 } 262 263 fp = &xfer->send.hdr; 264 fp->mode.rreqq.tcode = FWTCODE_RREQQ; 265 fp->mode.rreqq.dest_hi = dst_hi; 266 fp->mode.rreqq.dest_lo = dst_lo; 267 268 xfer->send.payload = NULL; 269 xfer->recv.payload = (uint32_t *)data; 270 271 if (fwmem_debug) 272 printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst, 273 dst_hi, dst_lo); 274 275 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 276 return xfer; 277 278 fw_xfer_free(xfer); 279 return NULL; 280 } 281 282 struct fw_xfer * 283 fwmem_write_quad( 284 struct fw_device *fwdev, 285 caddr_t sc, 286 uint8_t spd, 287 uint16_t dst_hi, 288 uint32_t dst_lo, 289 void *data, 290 void (*hand)(struct fw_xfer *)) 291 { 292 struct fw_xfer *xfer; 293 struct fw_pkt *fp; 294 295 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand); 296 if (xfer == NULL) 297 return NULL; 298 299 fp = &xfer->send.hdr; 300 fp->mode.wreqq.tcode = FWTCODE_WREQQ; 301 fp->mode.wreqq.dest_hi = dst_hi; 302 fp->mode.wreqq.dest_lo = dst_lo; 303 fp->mode.wreqq.data = *(uint32_t *)data; 304 305 xfer->send.payload = xfer->recv.payload = NULL; 306 307 if (fwmem_debug) 308 printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst, 309 dst_hi, dst_lo, *(uint32_t *)data); 310 311 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 312 return xfer; 313 314 fw_xfer_free(xfer); 315 return NULL; 316 } 317 318 struct fw_xfer * 319 fwmem_read_block( 320 struct fw_device *fwdev, 321 caddr_t sc, 322 uint8_t spd, 323 uint16_t dst_hi, 324 uint32_t dst_lo, 325 int len, 326 void *data, 327 void (*hand)(struct fw_xfer *)) 328 { 329 struct fw_xfer *xfer; 330 struct fw_pkt *fp; 331 332 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand); 333 if (xfer == NULL) 334 return NULL; 335 336 fp = &xfer->send.hdr; 337 fp->mode.rreqb.tcode = FWTCODE_RREQB; 338 fp->mode.rreqb.dest_hi = dst_hi; 339 fp->mode.rreqb.dest_lo = dst_lo; 340 fp->mode.rreqb.len = len; 341 fp->mode.rreqb.extcode = 0; 342 343 xfer->send.payload = NULL; 344 xfer->recv.payload = data; 345 346 if (fwmem_debug) 347 printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst, 348 dst_hi, dst_lo, len); 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_block( 358 struct fw_device *fwdev, 359 caddr_t sc, 360 uint8_t spd, 361 uint16_t dst_hi, 362 uint32_t dst_lo, 363 int len, 364 void *data, 365 void (*hand)(struct fw_xfer *)) 366 { 367 struct fw_xfer *xfer; 368 struct fw_pkt *fp; 369 370 xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand); 371 if (xfer == NULL) 372 return NULL; 373 374 fp = &xfer->send.hdr; 375 fp->mode.wreqb.tcode = FWTCODE_WREQB; 376 fp->mode.wreqb.dest_hi = dst_hi; 377 fp->mode.wreqb.dest_lo = dst_lo; 378 fp->mode.wreqb.len = len; 379 fp->mode.wreqb.extcode = 0; 380 381 xfer->send.payload = data; 382 xfer->recv.payload = NULL; 383 384 if (fwmem_debug) 385 printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst, 386 dst_hi, dst_lo, len); 387 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 388 return xfer; 389 390 fw_xfer_free(xfer); 391 return NULL; 392 } 393 394 395 FW_OPEN(fwmem) 396 { 397 struct fwmem_softc *fms; 398 struct fw_xfer *xfer; 399 FW_OPEN_START; 400 401 if (dev->si_drv1 != NULL) { 402 if ((flags & FWRITE) != 0) 403 return (EBUSY); 404 fms = (struct fwmem_softc *)dev->si_drv1; 405 fms->refcount ++; 406 } else { 407 fms = (struct fwmem_softc *)malloc(sizeof(struct fwmem_softc), 408 M_FWMEM, M_WAITOK); 409 if (fms == NULL) 410 return ENOMEM; 411 bcopy(&fwmem_eui64, &fms->eui, sizeof(struct fw_eui64)); 412 dev->si_drv1 = (void *)fms; 413 dev->si_iosize_max = DFLTPHYS; 414 fms->refcount = 1; 415 STAILQ_INIT(&fms->xferlist); 416 xfer = fw_xfer_alloc(M_FWMEM); 417 STAILQ_INSERT_TAIL(&fms->xferlist, xfer, link); 418 } 419 if (fwmem_debug) 420 printf("%s: refcount=%d\n", __func__, fms->refcount); 421 422 return (0); 423 } 424 425 FW_CLOSE(fwmem) 426 { 427 struct fwmem_softc *fms; 428 struct fw_xfer *xfer; 429 FW_CLOSE_START; 430 431 fms = (struct fwmem_softc *)dev->si_drv1; 432 fms->refcount --; 433 if (fwmem_debug) 434 printf("%s: refcount=%d\n", __func__, fms->refcount); 435 if (fms->refcount < 1) { 436 while ((xfer = STAILQ_FIRST(&fms->xferlist)) != NULL) { 437 STAILQ_REMOVE_HEAD(&fms->xferlist, link); 438 fw_xfer_free(xfer); 439 } 440 free(dev->si_drv1, M_FW); 441 dev->si_drv1 = NULL; 442 } 443 444 return (0); 445 } 446 447 448 static void 449 fwmem_biodone(struct fw_xfer *xfer) 450 { 451 struct bio *bp; 452 453 bp = (struct bio *)xfer->sc; 454 bp->bio_error = xfer->resp; 455 456 if (bp->bio_error != 0) { 457 if (fwmem_debug) 458 printf("%s: err=%d\n", __func__, bp->bio_error); 459 bp->bio_flags |= BIO_ERROR; 460 bp->bio_resid = bp->bio_bcount; 461 } 462 463 CTR0(KTR_DEV, "biodone0"); 464 fw_xfer_free(xfer); 465 CTR0(KTR_DEV, "biodone1"); 466 biodone(bp); 467 CTR0(KTR_DEV, "biodone2"); 468 } 469 470 void 471 fwmem_strategy(struct bio *bp) 472 { 473 FW_STRATEGY_START; 474 struct fwmem_softc *fms; 475 struct fw_device *fwdev; 476 struct fw_xfer *xfer; 477 int err=0, s, iolen; 478 479 CTR0(KTR_DEV, "strategy"); 480 481 /* XXX check request length */ 482 483 s = splfw(); 484 fms = (struct fwmem_softc *)dev->si_drv1; 485 fwdev = fw_noderesolve_eui64(sc->fc, &fms->eui); 486 if (fwdev == NULL) { 487 if (fwmem_debug) 488 printf("fwmem: no such device ID:%08x%08x\n", 489 fms->eui.hi, fms->eui.lo); 490 err = EINVAL; 491 goto error; 492 } 493 494 iolen = MIN(bp->bio_bcount, MAXLEN); 495 if ((bp->bio_cmd & BIO_READ) == BIO_READ) { 496 if (iolen == 4 && (bp->bio_offset & 3) == 0) 497 xfer = fwmem_read_quad(fwdev, 498 (void *) bp, fwmem_speed, 499 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 500 bp->bio_data, fwmem_biodone); 501 else 502 xfer = fwmem_read_block(fwdev, 503 (void *) bp, fwmem_speed, 504 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 505 iolen, bp->bio_data, fwmem_biodone); 506 } else { 507 if (iolen == 4 && (bp->bio_offset & 3) == 0) 508 xfer = fwmem_write_quad(fwdev, 509 (void *)bp, fwmem_speed, 510 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 511 bp->bio_data, fwmem_biodone); 512 else 513 xfer = fwmem_write_block(fwdev, 514 (void *)bp, fwmem_speed, 515 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 516 iolen, bp->bio_data, fwmem_biodone); 517 } 518 if (xfer == NULL) { 519 err = EIO; 520 goto error; 521 } 522 /* XXX */ 523 bp->bio_resid = bp->bio_bcount - iolen; 524 error: 525 splx(s); 526 if (err != 0) { 527 if (fwmem_debug) 528 printf("%s: err=%d\n", __func__, err); 529 bp->bio_error = err; 530 bp->bio_flags |= BIO_ERROR; 531 bp->bio_resid = bp->bio_bcount; 532 biodone(bp); 533 } 534 } 535 536 FW_IOCTL(fwmem) 537 { 538 FW_IOCTL_START; 539 struct fwmem_softc *fms; 540 int err = 0; 541 542 fms = (struct fwmem_softc *)dev->si_drv1; 543 switch (cmd) { 544 case FW_SDEUI64: 545 bcopy(data, &fms->eui, sizeof(struct fw_eui64)); 546 break; 547 case FW_GDEUI64: 548 bcopy(&fms->eui, data, sizeof(struct fw_eui64)); 549 break; 550 default: 551 err = EINVAL; 552 } 553 return(err); 554 } 555 556 FW_POLL(fwmem) 557 { 558 return EINVAL; 559 } 560 561 FW_MMAP(fwmem) 562 { 563 return EINVAL; 564 } 565