1 /* $NetBSD: fwmem.c,v 1.14 2010/05/23 18:56:58 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 __KERNEL_RCSID(0, "$NetBSD: fwmem.c,v 1.14 2010/05/23 18:56:58 christos 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 MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/IEEE1394"); 159 160 #define MAXLEN (512 << fwmem_speed) 161 162 struct fwmem_softc { 163 struct fw_eui64 eui; 164 struct firewire_softc *sc; 165 int refcount; 166 STAILQ_HEAD(, fw_xfer) xferlist; 167 }; 168 169 170 int 171 fwmem_open(dev_t dev, int flags, int fmt, struct lwp *td) 172 { 173 struct firewire_softc *sc; 174 struct fwmem_softc *fms; 175 struct fw_xfer *xfer; 176 177 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev)); 178 if (sc == NULL) 179 return ENXIO; 180 181 if (sc->si_drv1 != NULL) { 182 if ((flags & FWRITE) != 0) { 183 return EBUSY; 184 } 185 fms = (struct fwmem_softc *)sc->si_drv1; 186 fms->refcount++; 187 } else { 188 sc->si_drv1 = (void *)-1; 189 sc->si_drv1 = malloc(sizeof(struct fwmem_softc), 190 M_FWMEM, M_WAITOK); 191 if (sc->si_drv1 == NULL) 192 return ENOMEM; 193 fms = (struct fwmem_softc *)sc->si_drv1; 194 memcpy(&fms->eui, &fwmem_eui64, sizeof(struct fw_eui64)); 195 fms->sc = sc; 196 fms->refcount = 1; 197 STAILQ_INIT(&fms->xferlist); 198 xfer = fw_xfer_alloc(M_FWMEM); 199 STAILQ_INSERT_TAIL(&fms->xferlist, xfer, link); 200 } 201 if (fwmem_debug) 202 printf("%s: refcount=%d\n", __func__, fms->refcount); 203 204 return 0; 205 } 206 207 int 208 fwmem_close(dev_t dev, int flags, int fmt, struct lwp *td) 209 { 210 struct firewire_softc *sc; 211 struct fwmem_softc *fms; 212 struct fw_xfer *xfer; 213 214 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev)); 215 if (sc == NULL) 216 return ENXIO; 217 218 fms = (struct fwmem_softc *)sc->si_drv1; 219 220 fms->refcount--; 221 if (fwmem_debug) 222 printf("%s: refcount=%d\n", __func__, fms->refcount); 223 if (fms->refcount < 1) { 224 while ((xfer = STAILQ_FIRST(&fms->xferlist)) != NULL) { 225 STAILQ_REMOVE_HEAD(&fms->xferlist, link); 226 fw_xfer_free(xfer); 227 } 228 free(sc->si_drv1, M_FW); 229 sc->si_drv1 = NULL; 230 } 231 232 return 0; 233 } 234 235 void 236 fwmem_strategy(struct bio *bp) 237 { 238 struct firewire_softc *sc; 239 struct fwmem_softc *fms; 240 struct fw_device *fwdev; 241 struct fw_xfer *xfer; 242 dev_t dev = bp->bio_dev; 243 int iolen, err = 0, s; 244 245 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev)); 246 if (sc == NULL) 247 return; 248 249 /* XXX check request length */ 250 251 s = splvm(); 252 fms = (struct fwmem_softc *)sc->si_drv1; 253 fwdev = fw_noderesolve_eui64(fms->sc->fc, &fms->eui); 254 if (fwdev == NULL) { 255 if (fwmem_debug) 256 printf("fwmem: no such device ID:%08x%08x\n", 257 fms->eui.hi, fms->eui.lo); 258 err = EINVAL; 259 goto error; 260 } 261 262 iolen = MIN(bp->bio_bcount, MAXLEN); 263 if ((bp->bio_cmd & BIO_READ) == BIO_READ) { 264 if (iolen == 4 && (bp->bio_offset & 3) == 0) 265 xfer = fwmem_read_quad(fwdev, (void *) bp, fwmem_speed, 266 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 267 bp->bio_data, fwmem_biodone); 268 else 269 xfer = fwmem_read_block(fwdev, (void *) bp, fwmem_speed, 270 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 271 iolen, bp->bio_data, fwmem_biodone); 272 } else { 273 if (iolen == 4 && (bp->bio_offset & 3) == 0) 274 xfer = fwmem_write_quad(fwdev, (void *)bp, fwmem_speed, 275 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 276 bp->bio_data, fwmem_biodone); 277 else 278 xfer = fwmem_write_block(fwdev, (void *)bp, fwmem_speed, 279 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, 280 iolen, bp->bio_data, fwmem_biodone); 281 } 282 if (xfer == NULL) { 283 err = EIO; 284 goto error; 285 } 286 /* XXX */ 287 bp->bio_resid = bp->bio_bcount - iolen; 288 error: 289 splx(s); 290 if (err != 0) { 291 if (fwmem_debug) 292 printf("%s: err=%d\n", __func__, err); 293 bp->bio_error = err; 294 bp->bio_resid = bp->bio_bcount; 295 biodone(bp); 296 } 297 } 298 299 int 300 fwmem_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *td) 301 { 302 struct firewire_softc *sc; 303 struct fwmem_softc *fms; 304 int err = 0; 305 306 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev)); 307 if (sc == NULL) 308 return ENXIO; 309 310 fms = (struct fwmem_softc *)sc->si_drv1; 311 switch (cmd) { 312 case FW_SDEUI64: 313 memcpy(&fms->eui, data, sizeof(struct fw_eui64)); 314 break; 315 316 case FW_GDEUI64: 317 memcpy(data, &fms->eui, sizeof(struct fw_eui64)); 318 break; 319 320 default: 321 err = EINVAL; 322 } 323 return err; 324 } 325 326 327 struct fw_xfer * 328 fwmem_read_quad(struct fw_device *fwdev, void * sc, uint8_t spd, 329 uint16_t dst_hi, uint32_t dst_lo, void *data, 330 void (*hand)(struct fw_xfer *)) 331 { 332 struct fw_xfer *xfer; 333 struct fw_pkt *fp; 334 335 xfer = fwmem_xfer_req(fwdev, (void *)sc, spd, 0, 4, hand); 336 if (xfer == NULL) 337 return NULL; 338 339 fp = &xfer->send.hdr; 340 fp->mode.rreqq.tcode = FWTCODE_RREQQ; 341 fp->mode.rreqq.dest_hi = dst_hi; 342 fp->mode.rreqq.dest_lo = dst_lo; 343 344 xfer->send.payload = NULL; 345 xfer->recv.payload = (uint32_t *)data; 346 347 if (fwmem_debug) 348 aprint_error("fwmem_read_quad: %d %04x:%08x\n", 349 fwdev->dst, dst_hi, dst_lo); 350 351 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 352 return xfer; 353 354 fw_xfer_free(xfer); 355 return NULL; 356 } 357 358 struct fw_xfer * 359 fwmem_write_quad(struct fw_device *fwdev, void *sc, uint8_t spd, 360 uint16_t dst_hi, uint32_t dst_lo, void *data, 361 void (*hand)(struct fw_xfer *)) 362 { 363 struct fw_xfer *xfer; 364 struct fw_pkt *fp; 365 366 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand); 367 if (xfer == NULL) 368 return NULL; 369 370 fp = &xfer->send.hdr; 371 fp->mode.wreqq.tcode = FWTCODE_WREQQ; 372 fp->mode.wreqq.dest_hi = dst_hi; 373 fp->mode.wreqq.dest_lo = dst_lo; 374 fp->mode.wreqq.data = *(uint32_t *)data; 375 376 xfer->send.payload = xfer->recv.payload = NULL; 377 378 if (fwmem_debug) 379 aprint_error("fwmem_write_quad: %d %04x:%08x %08x\n", 380 fwdev->dst, dst_hi, dst_lo, *(uint32_t *)data); 381 382 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 383 return xfer; 384 385 fw_xfer_free(xfer); 386 return NULL; 387 } 388 389 struct fw_xfer * 390 fwmem_read_block(struct fw_device *fwdev, void *sc, uint8_t spd, 391 uint16_t dst_hi, uint32_t dst_lo, int len, void *data, 392 void (*hand)(struct fw_xfer *)) 393 { 394 struct fw_xfer *xfer; 395 struct fw_pkt *fp; 396 397 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand); 398 if (xfer == NULL) 399 return NULL; 400 401 fp = &xfer->send.hdr; 402 fp->mode.rreqb.tcode = FWTCODE_RREQB; 403 fp->mode.rreqb.dest_hi = dst_hi; 404 fp->mode.rreqb.dest_lo = dst_lo; 405 fp->mode.rreqb.len = len; 406 fp->mode.rreqb.extcode = 0; 407 408 xfer->send.payload = NULL; 409 xfer->recv.payload = data; 410 411 if (fwmem_debug) 412 aprint_error("fwmem_read_block: %d %04x:%08x %d\n", 413 fwdev->dst, dst_hi, dst_lo, len); 414 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 415 return xfer; 416 417 fw_xfer_free(xfer); 418 return NULL; 419 } 420 421 struct fw_xfer * 422 fwmem_write_block(struct fw_device *fwdev, void *sc, uint8_t spd, 423 uint16_t dst_hi, uint32_t dst_lo, int len, void *data, 424 void (*hand)(struct fw_xfer *)) 425 { 426 struct fw_xfer *xfer; 427 struct fw_pkt *fp; 428 429 xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand); 430 if (xfer == NULL) 431 return NULL; 432 433 fp = &xfer->send.hdr; 434 fp->mode.wreqb.tcode = FWTCODE_WREQB; 435 fp->mode.wreqb.dest_hi = dst_hi; 436 fp->mode.wreqb.dest_lo = dst_lo; 437 fp->mode.wreqb.len = len; 438 fp->mode.wreqb.extcode = 0; 439 440 xfer->send.payload = data; 441 xfer->recv.payload = NULL; 442 443 if (fwmem_debug) 444 aprint_error("fwmem_write_block: %d %04x:%08x %d\n", 445 fwdev->dst, dst_hi, dst_lo, len); 446 if (fw_asyreq(xfer->fc, -1, xfer) == 0) 447 return xfer; 448 449 fw_xfer_free(xfer); 450 return NULL; 451 } 452 453 454 static struct fw_xfer * 455 fwmem_xfer_req(struct fw_device *fwdev, void *sc, int spd, int slen, int rlen, 456 void *hand) 457 { 458 struct fw_xfer *xfer; 459 460 xfer = fw_xfer_alloc(M_FWMEM); 461 if (xfer == NULL) 462 return NULL; 463 464 xfer->fc = fwdev->fc; 465 xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst; 466 if (spd < 0) 467 xfer->send.spd = fwdev->speed; 468 else 469 xfer->send.spd = min(spd, fwdev->speed); 470 xfer->hand = hand; 471 xfer->sc = sc; 472 xfer->send.pay_len = slen; 473 xfer->recv.pay_len = rlen; 474 475 return xfer; 476 } 477 478 static void 479 fwmem_biodone(struct fw_xfer *xfer) 480 { 481 struct bio *bp; 482 483 bp = (struct bio *)xfer->sc; 484 bp->bio_error = xfer->resp; 485 486 if (bp->bio_error != 0) { 487 if (fwmem_debug) 488 printf("%s: err=%d\n", __func__, bp->bio_error); 489 bp->bio_resid = bp->bio_bcount; 490 } 491 492 fw_xfer_free(xfer); 493 biodone(bp); 494 } 495