fwmem.c revision 113584
1103285Sikob/* 2113584Ssimokawa * Copyright (c) 2002-2003 3103285Sikob * Hidetoshi Shimokawa. All rights reserved. 4103285Sikob * 5103285Sikob * Redistribution and use in source and binary forms, with or without 6103285Sikob * modification, are permitted provided that the following conditions 7103285Sikob * are met: 8103285Sikob * 1. Redistributions of source code must retain the above copyright 9103285Sikob * notice, this list of conditions and the following disclaimer. 10103285Sikob * 2. Redistributions in binary form must reproduce the above copyright 11103285Sikob * notice, this list of conditions and the following disclaimer in the 12103285Sikob * documentation and/or other materials provided with the distribution. 13103285Sikob * 3. All advertising materials mentioning features or use of this software 14103285Sikob * must display the following acknowledgement: 15103285Sikob * 16103285Sikob * This product includes software developed by Hidetoshi Shimokawa. 17103285Sikob * 18103285Sikob * 4. Neither the name of the author nor the names of its contributors 19103285Sikob * may be used to endorse or promote products derived from this software 20103285Sikob * without specific prior written permission. 21103285Sikob * 22103285Sikob * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23103285Sikob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24103285Sikob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25103285Sikob * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26103285Sikob * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27103285Sikob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28103285Sikob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29103285Sikob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30103285Sikob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31103285Sikob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32103285Sikob * SUCH DAMAGE. 33103285Sikob * 34103285Sikob * $FreeBSD: head/sys/dev/firewire/fwmem.c 113584 2003-04-17 03:38:03Z simokawa $ 35103285Sikob */ 36103285Sikob 37103285Sikob#include <sys/param.h> 38103285Sikob#include <sys/systm.h> 39103285Sikob#include <sys/types.h> 40103285Sikob 41103285Sikob#include <sys/kernel.h> 42103285Sikob#include <sys/malloc.h> 43103285Sikob#include <sys/conf.h> 44103285Sikob#include <sys/sysctl.h> 45103285Sikob 46103285Sikob#include <sys/bus.h> 47113584Ssimokawa#include <machine/bus.h> 48103285Sikob 49103285Sikob#include <sys/signal.h> 50103285Sikob#include <sys/mman.h> 51103285Sikob#include <sys/ioccom.h> 52103285Sikob 53103285Sikob#include <dev/firewire/firewire.h> 54103285Sikob#include <dev/firewire/firewirereg.h> 55103285Sikob#include <dev/firewire/fwmem.h> 56103285Sikob 57106810Ssimokawastatic int fwmem_speed=2, fwmem_debug=0; 58106810Ssimokawastatic struct fw_eui64 fwmem_eui64; 59103285SikobSYSCTL_DECL(_hw_firewire); 60103285SikobSYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0, 61108281Ssimokawa "FireWire Memory Access"); 62106810SsimokawaSYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW, 63106810Ssimokawa &fwmem_eui64.hi, 0, "Fwmem target EUI64 high"); 64110582SsimokawaSYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, CTLFLAG_RW, 65106810Ssimokawa &fwmem_eui64.lo, 0, "Fwmem target EUI64 low"); 66103285SikobSYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0, 67103285Sikob "Fwmem link speed"); 68103285SikobSYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0, 69103285Sikob "Fwmem driver debug flag"); 70103285Sikob 71106816Ssimokawastatic struct fw_xfer * 72106816Ssimokawafwmem_xfer_req( 73106810Ssimokawa struct fw_device *fwdev, 74106816Ssimokawa caddr_t sc, 75106816Ssimokawa int spd, 76113584Ssimokawa int slen, 77113584Ssimokawa int rlen, 78106816Ssimokawa void *hand) 79103285Sikob{ 80103285Sikob struct fw_xfer *xfer; 81103285Sikob 82113584Ssimokawa xfer = fw_xfer_alloc_buf(M_FWXFER, slen, rlen); 83106804Ssimokawa if (xfer == NULL) 84103285Sikob return NULL; 85106804Ssimokawa 86106810Ssimokawa xfer->fc = fwdev->fc; 87106810Ssimokawa xfer->dst = FWLOCALBUS | fwdev->dst; 88106816Ssimokawa if (spd < 0) 89106816Ssimokawa xfer->spd = fwdev->speed; 90106816Ssimokawa else 91106816Ssimokawa xfer->spd = min(spd, fwdev->speed); 92106804Ssimokawa xfer->act.hand = hand; 93103285Sikob xfer->retry_req = fw_asybusy; 94106816Ssimokawa xfer->sc = sc; 95103285Sikob 96106816Ssimokawa return xfer; 97106816Ssimokawa} 98106816Ssimokawa 99106816Ssimokawastruct fw_xfer * 100106816Ssimokawafwmem_read_quad( 101106816Ssimokawa struct fw_device *fwdev, 102106816Ssimokawa caddr_t sc, 103106816Ssimokawa u_int8_t spd, 104106816Ssimokawa u_int16_t dst_hi, 105106816Ssimokawa u_int32_t dst_lo, 106106816Ssimokawa void (*hand)(struct fw_xfer *)) 107106816Ssimokawa{ 108106816Ssimokawa struct fw_xfer *xfer; 109106816Ssimokawa struct fw_pkt *fp; 110106816Ssimokawa 111113584Ssimokawa xfer = fwmem_xfer_req(fwdev, sc, spd, 12, 16, hand); 112106816Ssimokawa if (xfer == NULL) 113106816Ssimokawa return NULL; 114106816Ssimokawa 115103285Sikob fp = (struct fw_pkt *)xfer->send.buf; 116103285Sikob fp->mode.rreqq.tcode = FWTCODE_RREQQ; 117113584Ssimokawa fp->mode.rreqq.dst = xfer->dst; 118113584Ssimokawa fp->mode.rreqq.dest_hi = dst_hi; 119113584Ssimokawa fp->mode.rreqq.dest_lo = dst_lo; 120103285Sikob 121103285Sikob if (fwmem_debug) 122106816Ssimokawa printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst, 123106816Ssimokawa dst_hi, dst_lo); 124106804Ssimokawa 125106810Ssimokawa if (fw_asyreq(xfer->fc, -1, xfer) == 0) 126103285Sikob return xfer; 127106804Ssimokawa 128103285Sikob fw_xfer_free(xfer); 129103285Sikob return NULL; 130103285Sikob} 131103285Sikob 132103285Sikobstruct fw_xfer * 133106810Ssimokawafwmem_write_quad( 134106810Ssimokawa struct fw_device *fwdev, 135106810Ssimokawa caddr_t sc, 136106810Ssimokawa u_int8_t spd, 137106810Ssimokawa u_int16_t dst_hi, 138106810Ssimokawa u_int32_t dst_lo, 139106810Ssimokawa u_int32_t data, 140106810Ssimokawa void (*hand)(struct fw_xfer *)) 141106810Ssimokawa{ 142106810Ssimokawa struct fw_xfer *xfer; 143106810Ssimokawa struct fw_pkt *fp; 144106810Ssimokawa 145113584Ssimokawa xfer = fwmem_xfer_req(fwdev, sc, spd, 16, 12, hand); 146106810Ssimokawa if (xfer == NULL) 147106810Ssimokawa return NULL; 148106810Ssimokawa 149106810Ssimokawa fp = (struct fw_pkt *)xfer->send.buf; 150110072Ssimokawa fp->mode.wreqq.tcode = FWTCODE_WREQQ; 151113584Ssimokawa fp->mode.wreqq.dst = xfer->dst; 152113584Ssimokawa fp->mode.wreqq.dest_hi = dst_hi; 153113584Ssimokawa fp->mode.wreqq.dest_lo = dst_lo; 154106810Ssimokawa 155112523Ssimokawa fp->mode.wreqq.data = data; 156106810Ssimokawa 157106810Ssimokawa if (fwmem_debug) 158106816Ssimokawa printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst, 159106816Ssimokawa dst_hi, dst_lo, data); 160106810Ssimokawa 161106810Ssimokawa if (fw_asyreq(xfer->fc, -1, xfer) == 0) 162106810Ssimokawa return xfer; 163106810Ssimokawa 164106810Ssimokawa fw_xfer_free(xfer); 165106810Ssimokawa return NULL; 166106810Ssimokawa} 167106810Ssimokawa 168106810Ssimokawastruct fw_xfer * 169103285Sikobfwmem_read_block( 170106810Ssimokawa struct fw_device *fwdev, 171106810Ssimokawa caddr_t sc, 172106804Ssimokawa u_int8_t spd, 173103285Sikob u_int16_t dst_hi, 174103285Sikob u_int32_t dst_lo, 175106804Ssimokawa int len, 176106804Ssimokawa void (*hand)(struct fw_xfer *)) 177103285Sikob{ 178103285Sikob struct fw_xfer *xfer; 179103285Sikob struct fw_pkt *fp; 180103285Sikob 181113584Ssimokawa xfer = fwmem_xfer_req(fwdev, sc, spd, 16, roundup2(16+len,4), hand); 182106804Ssimokawa if (xfer == NULL) 183103285Sikob return NULL; 184106804Ssimokawa 185103285Sikob fp = (struct fw_pkt *)xfer->send.buf; 186103285Sikob fp->mode.rreqb.tcode = FWTCODE_RREQB; 187113584Ssimokawa fp->mode.rreqb.dst = xfer->dst; 188113584Ssimokawa fp->mode.rreqb.dest_hi = dst_hi; 189113584Ssimokawa fp->mode.rreqb.dest_lo = dst_lo; 190113584Ssimokawa fp->mode.rreqb.len = len; 191103285Sikob 192103285Sikob if (fwmem_debug) 193106816Ssimokawa printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst, 194106810Ssimokawa dst_hi, dst_lo, len); 195106810Ssimokawa if (fw_asyreq(xfer->fc, -1, xfer) == 0) 196103285Sikob return xfer; 197106804Ssimokawa 198103285Sikob fw_xfer_free(xfer); 199103285Sikob return NULL; 200103285Sikob} 201103285Sikob 202110337Ssimokawastruct fw_xfer * 203110337Ssimokawafwmem_write_block( 204110337Ssimokawa struct fw_device *fwdev, 205110337Ssimokawa caddr_t sc, 206110337Ssimokawa u_int8_t spd, 207110337Ssimokawa u_int16_t dst_hi, 208110337Ssimokawa u_int32_t dst_lo, 209110337Ssimokawa int len, 210110337Ssimokawa char *data, 211110337Ssimokawa void (*hand)(struct fw_xfer *)) 212110337Ssimokawa{ 213110337Ssimokawa struct fw_xfer *xfer; 214110337Ssimokawa struct fw_pkt *fp; 215110337Ssimokawa 216113584Ssimokawa xfer = fwmem_xfer_req(fwdev, sc, spd, roundup(16+len, 4), 12, hand); 217110337Ssimokawa if (xfer == NULL) 218110337Ssimokawa return NULL; 219110337Ssimokawa 220110337Ssimokawa fp = (struct fw_pkt *)xfer->send.buf; 221110406Ssimokawa fp->mode.wreqb.tcode = FWTCODE_WREQB; 222113584Ssimokawa fp->mode.wreqb.dst = xfer->dst; 223113584Ssimokawa fp->mode.wreqb.dest_hi = dst_hi; 224113584Ssimokawa fp->mode.wreqb.dest_lo = dst_lo; 225113584Ssimokawa fp->mode.wreqb.len = len; 226110337Ssimokawa bcopy(data, &fp->mode.wreqb.payload[0], len); 227110337Ssimokawa 228110337Ssimokawa if (fwmem_debug) 229110337Ssimokawa printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst, 230110337Ssimokawa dst_hi, dst_lo, len); 231110337Ssimokawa if (fw_asyreq(xfer->fc, -1, xfer) == 0) 232110337Ssimokawa return xfer; 233110337Ssimokawa 234110337Ssimokawa fw_xfer_free(xfer); 235110337Ssimokawa return NULL; 236110337Ssimokawa} 237110337Ssimokawa 238110337Ssimokawa 239103285Sikobint 240103285Sikobfwmem_open (dev_t dev, int flags, int fmt, fw_proc *td) 241103285Sikob{ 242110582Ssimokawa struct fw_eui64 *eui; 243110582Ssimokawa 244113584Ssimokawa eui = (struct fw_eui64 *)malloc(sizeof(struct fw_eui64), 245113584Ssimokawa M_FW, M_WAITOK); 246110582Ssimokawa if (eui == NULL) 247110582Ssimokawa return ENOMEM; 248110582Ssimokawa bcopy(&fwmem_eui64, eui, sizeof(struct fw_eui64)); 249110582Ssimokawa dev->si_drv1 = (void *)eui; 250110582Ssimokawa 251110582Ssimokawa return (0); 252103285Sikob} 253103285Sikob 254103285Sikobint 255103285Sikobfwmem_close (dev_t dev, int flags, int fmt, fw_proc *td) 256103285Sikob{ 257110582Ssimokawa free(dev->si_drv1, M_FW); 258110582Ssimokawa return (0); 259103285Sikob} 260103285Sikob 261103285Sikob#define MAXLEN 2048 262103285Sikob#define USE_QUAD 0 263103285Sikobint 264103285Sikobfwmem_read (dev_t dev, struct uio *uio, int ioflag) 265103285Sikob{ 266103285Sikob struct firewire_softc *sc; 267106810Ssimokawa struct fw_device *fwdev; 268103285Sikob struct fw_xfer *xfer; 269110337Ssimokawa int err = 0; 270103285Sikob int unit = DEV2UNIT(dev); 271103285Sikob u_int16_t dst_hi; 272103285Sikob u_int32_t dst_lo; 273103285Sikob off_t offset; 274103285Sikob int len; 275103285Sikob 276103285Sikob sc = devclass_get_softc(firewire_devclass, unit); 277110582Ssimokawa fwdev = fw_noderesolve_eui64(sc->fc, (struct fw_eui64 *)dev->si_drv1); 278106810Ssimokawa if (fwdev == NULL) { 279110577Ssimokawa if (fwmem_debug) 280110577Ssimokawa printf("fwmem: no such device ID:%08x%08x\n", 281110577Ssimokawa fwmem_eui64.hi, fwmem_eui64.lo); 282106810Ssimokawa return EINVAL; 283106810Ssimokawa } 284103285Sikob 285110465Ssimokawa while(uio->uio_resid > 0 && !err) { 286103285Sikob offset = uio->uio_offset; 287103285Sikob dst_hi = (offset >> 32) & 0xffff; 288103285Sikob dst_lo = offset & 0xffffffff; 289110337Ssimokawa len = uio->uio_resid; 290110337Ssimokawa if (len == 4 && (dst_lo & 3) == 0) { 291110337Ssimokawa xfer = fwmem_read_quad(fwdev, NULL, fwmem_speed, 292106804Ssimokawa dst_hi, dst_lo, fw_asy_callback); 293110465Ssimokawa if (xfer == NULL) { 294110465Ssimokawa err = EINVAL; 295110465Ssimokawa break; 296110465Ssimokawa } 297111956Ssimokawa err = tsleep((caddr_t)xfer, FWPRI, "fwmrq", 0); 298111956Ssimokawa if (xfer->recv.buf == NULL) 299110406Ssimokawa err = EIO; 300111956Ssimokawa else if (xfer->resp != 0) 301111956Ssimokawa err = xfer->resp; 302111956Ssimokawa else if (err == 0) 303113584Ssimokawa err = uiomove(xfer->recv.buf + 4*3, 4, uio); 304110337Ssimokawa } else { 305110337Ssimokawa if (len > MAXLEN) 306110337Ssimokawa len = MAXLEN; 307110337Ssimokawa xfer = fwmem_read_block(fwdev, NULL, fwmem_speed, 308106804Ssimokawa dst_hi, dst_lo, len, fw_asy_callback); 309110465Ssimokawa if (xfer == NULL) { 310110465Ssimokawa err = EINVAL; 311110465Ssimokawa break; 312110465Ssimokawa } 313111956Ssimokawa err = tsleep((caddr_t)xfer, FWPRI, "fwmrb", 0); 314111956Ssimokawa if (xfer->recv.buf == NULL) 315110406Ssimokawa err = EIO; 316111956Ssimokawa else if (xfer->resp != 0) 317111956Ssimokawa err = xfer->resp; 318111956Ssimokawa else if (err == 0) 319113584Ssimokawa err = uiomove(xfer->recv.buf + 4*4, len, uio); 320110337Ssimokawa } 321110337Ssimokawa fw_xfer_free(xfer); 322103285Sikob } 323103285Sikob return err; 324103285Sikob} 325103285Sikobint 326103285Sikobfwmem_write (dev_t dev, struct uio *uio, int ioflag) 327103285Sikob{ 328110337Ssimokawa struct firewire_softc *sc; 329110337Ssimokawa struct fw_device *fwdev; 330110337Ssimokawa struct fw_xfer *xfer; 331110337Ssimokawa int err = 0; 332110337Ssimokawa int unit = DEV2UNIT(dev); 333110337Ssimokawa u_int16_t dst_hi; 334110337Ssimokawa u_int32_t dst_lo, quad; 335110337Ssimokawa char *data; 336110337Ssimokawa off_t offset; 337110337Ssimokawa int len; 338110337Ssimokawa 339110337Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 340110582Ssimokawa fwdev = fw_noderesolve_eui64(sc->fc, (struct fw_eui64 *)dev->si_drv1); 341110337Ssimokawa if (fwdev == NULL) { 342110577Ssimokawa if (fwmem_debug) 343110577Ssimokawa printf("fwmem: no such device ID:%08x%08x\n", 344110577Ssimokawa fwmem_eui64.hi, fwmem_eui64.lo); 345110337Ssimokawa return EINVAL; 346110337Ssimokawa } 347110337Ssimokawa 348113584Ssimokawa data = malloc(MAXLEN, M_FW, M_WAITOK); 349110337Ssimokawa if (data == NULL) 350110337Ssimokawa return ENOMEM; 351110337Ssimokawa 352110465Ssimokawa while(uio->uio_resid > 0 && !err) { 353110337Ssimokawa offset = uio->uio_offset; 354110337Ssimokawa dst_hi = (offset >> 32) & 0xffff; 355110337Ssimokawa dst_lo = offset & 0xffffffff; 356110337Ssimokawa len = uio->uio_resid; 357110337Ssimokawa if (len == 4 && (dst_lo & 3) == 0) { 358110337Ssimokawa err = uiomove((char *)&quad, sizeof(quad), uio); 359110337Ssimokawa xfer = fwmem_write_quad(fwdev, NULL, fwmem_speed, 360110337Ssimokawa dst_hi, dst_lo, quad, fw_asy_callback); 361110465Ssimokawa if (xfer == NULL) { 362110465Ssimokawa err = EINVAL; 363110465Ssimokawa break; 364110465Ssimokawa } 365111956Ssimokawa err = tsleep((caddr_t)xfer, FWPRI, "fwmwq", 0); 366111956Ssimokawa if (xfer->resp != 0) 367111956Ssimokawa err = xfer->resp; 368110337Ssimokawa } else { 369110337Ssimokawa if (len > MAXLEN) 370110337Ssimokawa len = MAXLEN; 371110337Ssimokawa err = uiomove(data, len, uio); 372110337Ssimokawa if (err) 373110465Ssimokawa break; 374110337Ssimokawa xfer = fwmem_write_block(fwdev, NULL, fwmem_speed, 375110337Ssimokawa dst_hi, dst_lo, len, data, fw_asy_callback); 376110465Ssimokawa if (xfer == NULL) { 377110465Ssimokawa err = EINVAL; 378110465Ssimokawa break; 379110465Ssimokawa } 380111956Ssimokawa err = tsleep((caddr_t)xfer, FWPRI, "fwmwb", 0); 381111956Ssimokawa if (xfer->resp != 0) 382111956Ssimokawa err = xfer->resp; 383110337Ssimokawa } 384110406Ssimokawa fw_xfer_free(xfer); 385110337Ssimokawa } 386110465Ssimokawa free(data, M_FW); 387110337Ssimokawa return err; 388103285Sikob} 389110337Ssimokawa 390103285Sikobint 391103285Sikobfwmem_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td) 392103285Sikob{ 393110582Ssimokawa int err = 0; 394110582Ssimokawa switch (cmd) { 395110582Ssimokawa case FW_SDEUI64: 396110582Ssimokawa bcopy(data, dev->si_drv1, sizeof(struct fw_eui64)); 397110582Ssimokawa break; 398110582Ssimokawa case FW_GDEUI64: 399110582Ssimokawa bcopy(dev->si_drv1, data, sizeof(struct fw_eui64)); 400110582Ssimokawa break; 401110582Ssimokawa default: 402110582Ssimokawa err = EINVAL; 403110582Ssimokawa } 404110582Ssimokawa return(err); 405103285Sikob} 406103285Sikobint 407103285Sikobfwmem_poll (dev_t dev, int events, fw_proc *td) 408103285Sikob{ 409103285Sikob return EINVAL; 410103285Sikob} 411103285Sikobint 412113584Ssimokawa#if __FreeBSD_version < 500102 413111615Ssimokawafwmem_mmap (dev_t dev, vm_offset_t offset, int nproto) 414111615Ssimokawa#else 415113584Ssimokawafwmem_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto) 416111615Ssimokawa#endif 417103285Sikob{ 418103285Sikob return EINVAL; 419103285Sikob} 420