fwmem.c revision 110465
1103285Sikob/* 2103285Sikob * Copyright (C) 2002 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 110465 2003-02-06 17:23:01Z 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/uio.h> 45103285Sikob#include <sys/sysctl.h> 46103285Sikob 47103285Sikob#include <sys/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"); 64106810SsimokawaSYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_low, 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 *fwmem_xfer_req(struct fw_device *, caddr_t, 72106816Ssimokawa int, int, void *); 73106816Ssimokawa 74106816Ssimokawastatic struct fw_xfer * 75106816Ssimokawafwmem_xfer_req( 76106810Ssimokawa struct fw_device *fwdev, 77106816Ssimokawa caddr_t sc, 78106816Ssimokawa int spd, 79106816Ssimokawa int len, 80106816Ssimokawa void *hand) 81103285Sikob{ 82103285Sikob struct fw_xfer *xfer; 83103285Sikob 84110269Ssimokawa xfer = fw_xfer_alloc(M_FWXFER); 85106804Ssimokawa if (xfer == NULL) 86103285Sikob return NULL; 87106804Ssimokawa 88106810Ssimokawa xfer->fc = fwdev->fc; 89106810Ssimokawa xfer->dst = FWLOCALBUS | fwdev->dst; 90106816Ssimokawa if (spd < 0) 91106816Ssimokawa xfer->spd = fwdev->speed; 92106816Ssimokawa else 93106816Ssimokawa xfer->spd = min(spd, fwdev->speed); 94106816Ssimokawa xfer->send.len = len; 95110195Ssimokawa xfer->send.buf = malloc(len, M_FW, M_NOWAIT | M_ZERO); 96106804Ssimokawa 97106816Ssimokawa if (xfer->send.buf == NULL) { 98106816Ssimokawa fw_xfer_free(xfer); 99106816Ssimokawa return NULL; 100106816Ssimokawa } 101106804Ssimokawa 102103285Sikob xfer->send.off = 0; 103106804Ssimokawa xfer->act.hand = hand; 104103285Sikob xfer->retry_req = fw_asybusy; 105106816Ssimokawa xfer->sc = sc; 106103285Sikob 107106816Ssimokawa return xfer; 108106816Ssimokawa} 109106816Ssimokawa 110106816Ssimokawastruct fw_xfer * 111106816Ssimokawafwmem_read_quad( 112106816Ssimokawa struct fw_device *fwdev, 113106816Ssimokawa caddr_t sc, 114106816Ssimokawa u_int8_t spd, 115106816Ssimokawa u_int16_t dst_hi, 116106816Ssimokawa u_int32_t dst_lo, 117106816Ssimokawa void (*hand)(struct fw_xfer *)) 118106816Ssimokawa{ 119106816Ssimokawa struct fw_xfer *xfer; 120106816Ssimokawa struct fw_pkt *fp; 121106816Ssimokawa 122106816Ssimokawa xfer = fwmem_xfer_req(fwdev, sc, spd, 12, hand); 123106816Ssimokawa if (xfer == NULL) 124106816Ssimokawa return NULL; 125106816Ssimokawa 126103285Sikob fp = (struct fw_pkt *)xfer->send.buf; 127103285Sikob fp->mode.rreqq.tcode = FWTCODE_RREQQ; 128103285Sikob fp->mode.rreqq.dst = htons(xfer->dst); 129103285Sikob fp->mode.rreqq.dest_hi = htons(dst_hi); 130103285Sikob fp->mode.rreqq.dest_lo = htonl(dst_lo); 131103285Sikob 132103285Sikob if (fwmem_debug) 133106816Ssimokawa printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst, 134106816Ssimokawa dst_hi, dst_lo); 135106804Ssimokawa 136106810Ssimokawa if (fw_asyreq(xfer->fc, -1, xfer) == 0) 137103285Sikob return xfer; 138106804Ssimokawa 139103285Sikob fw_xfer_free(xfer); 140103285Sikob return NULL; 141103285Sikob} 142103285Sikob 143103285Sikobstruct fw_xfer * 144106810Ssimokawafwmem_write_quad( 145106810Ssimokawa struct fw_device *fwdev, 146106810Ssimokawa caddr_t sc, 147106810Ssimokawa u_int8_t spd, 148106810Ssimokawa u_int16_t dst_hi, 149106810Ssimokawa u_int32_t dst_lo, 150106810Ssimokawa u_int32_t data, 151106810Ssimokawa void (*hand)(struct fw_xfer *)) 152106810Ssimokawa{ 153106810Ssimokawa struct fw_xfer *xfer; 154106810Ssimokawa struct fw_pkt *fp; 155106810Ssimokawa 156106816Ssimokawa xfer = fwmem_xfer_req(fwdev, sc, spd, 16, hand); 157106810Ssimokawa if (xfer == NULL) 158106810Ssimokawa return NULL; 159106810Ssimokawa 160106810Ssimokawa fp = (struct fw_pkt *)xfer->send.buf; 161110072Ssimokawa fp->mode.wreqq.tcode = FWTCODE_WREQQ; 162106810Ssimokawa fp->mode.wreqq.dst = htons(xfer->dst); 163106810Ssimokawa fp->mode.wreqq.dest_hi = htons(dst_hi); 164106810Ssimokawa fp->mode.wreqq.dest_lo = htonl(dst_lo); 165106810Ssimokawa 166106810Ssimokawa fp->mode.wreqq.data = htonl(data); 167106810Ssimokawa 168106810Ssimokawa if (fwmem_debug) 169106816Ssimokawa printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst, 170106816Ssimokawa dst_hi, dst_lo, data); 171106810Ssimokawa 172106810Ssimokawa if (fw_asyreq(xfer->fc, -1, xfer) == 0) 173106810Ssimokawa return xfer; 174106810Ssimokawa 175106810Ssimokawa fw_xfer_free(xfer); 176106810Ssimokawa return NULL; 177106810Ssimokawa} 178106810Ssimokawa 179106810Ssimokawastruct fw_xfer * 180103285Sikobfwmem_read_block( 181106810Ssimokawa struct fw_device *fwdev, 182106810Ssimokawa caddr_t sc, 183106804Ssimokawa u_int8_t spd, 184103285Sikob u_int16_t dst_hi, 185103285Sikob u_int32_t dst_lo, 186106804Ssimokawa int len, 187106804Ssimokawa void (*hand)(struct fw_xfer *)) 188103285Sikob{ 189103285Sikob struct fw_xfer *xfer; 190103285Sikob struct fw_pkt *fp; 191103285Sikob 192106816Ssimokawa xfer = fwmem_xfer_req(fwdev, sc, spd, 16, hand); 193106804Ssimokawa if (xfer == NULL) 194103285Sikob return NULL; 195106804Ssimokawa 196103285Sikob fp = (struct fw_pkt *)xfer->send.buf; 197103285Sikob fp->mode.rreqb.tcode = FWTCODE_RREQB; 198103285Sikob fp->mode.rreqb.dst = htons(xfer->dst); 199103285Sikob fp->mode.rreqb.dest_hi = htons(dst_hi); 200103285Sikob fp->mode.rreqb.dest_lo = htonl(dst_lo); 201103285Sikob fp->mode.rreqb.len = htons(len); 202103285Sikob 203103285Sikob if (fwmem_debug) 204106816Ssimokawa printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst, 205106810Ssimokawa dst_hi, dst_lo, len); 206106810Ssimokawa if (fw_asyreq(xfer->fc, -1, xfer) == 0) 207103285Sikob return xfer; 208106804Ssimokawa 209103285Sikob fw_xfer_free(xfer); 210103285Sikob return NULL; 211103285Sikob} 212103285Sikob 213110337Ssimokawastruct fw_xfer * 214110337Ssimokawafwmem_write_block( 215110337Ssimokawa struct fw_device *fwdev, 216110337Ssimokawa caddr_t sc, 217110337Ssimokawa u_int8_t spd, 218110337Ssimokawa u_int16_t dst_hi, 219110337Ssimokawa u_int32_t dst_lo, 220110337Ssimokawa int len, 221110337Ssimokawa char *data, 222110337Ssimokawa void (*hand)(struct fw_xfer *)) 223110337Ssimokawa{ 224110337Ssimokawa struct fw_xfer *xfer; 225110337Ssimokawa struct fw_pkt *fp; 226110337Ssimokawa 227110337Ssimokawa xfer = fwmem_xfer_req(fwdev, sc, spd, 16 + roundup(len, 4), hand); 228110337Ssimokawa if (xfer == NULL) 229110337Ssimokawa return NULL; 230110337Ssimokawa 231110337Ssimokawa fp = (struct fw_pkt *)xfer->send.buf; 232110406Ssimokawa fp->mode.wreqb.tcode = FWTCODE_WREQB; 233110337Ssimokawa fp->mode.wreqb.dst = htons(xfer->dst); 234110337Ssimokawa fp->mode.wreqb.dest_hi = htons(dst_hi); 235110337Ssimokawa fp->mode.wreqb.dest_lo = htonl(dst_lo); 236110337Ssimokawa fp->mode.wreqb.len = htons(len); 237110337Ssimokawa bcopy(data, &fp->mode.wreqb.payload[0], len); 238110337Ssimokawa 239110337Ssimokawa if (fwmem_debug) 240110337Ssimokawa printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst, 241110337Ssimokawa dst_hi, dst_lo, len); 242110337Ssimokawa if (fw_asyreq(xfer->fc, -1, xfer) == 0) 243110337Ssimokawa return xfer; 244110337Ssimokawa 245110337Ssimokawa fw_xfer_free(xfer); 246110337Ssimokawa return NULL; 247110337Ssimokawa} 248110337Ssimokawa 249110337Ssimokawa 250103285Sikobint 251103285Sikobfwmem_open (dev_t dev, int flags, int fmt, fw_proc *td) 252103285Sikob{ 253103285Sikob int err = 0; 254103285Sikob return err; 255103285Sikob} 256103285Sikob 257103285Sikobint 258103285Sikobfwmem_close (dev_t dev, int flags, int fmt, fw_proc *td) 259103285Sikob{ 260103285Sikob int err = 0; 261103285Sikob return err; 262103285Sikob} 263103285Sikob 264103285Sikob#define MAXLEN 2048 265103285Sikob#define USE_QUAD 0 266103285Sikobint 267103285Sikobfwmem_read (dev_t dev, struct uio *uio, int ioflag) 268103285Sikob{ 269103285Sikob struct firewire_softc *sc; 270106810Ssimokawa struct fw_device *fwdev; 271103285Sikob struct fw_xfer *xfer; 272110337Ssimokawa int err = 0; 273103285Sikob int unit = DEV2UNIT(dev); 274103285Sikob u_int16_t dst_hi; 275103285Sikob u_int32_t dst_lo; 276103285Sikob off_t offset; 277103285Sikob int len; 278103285Sikob 279103285Sikob sc = devclass_get_softc(firewire_devclass, unit); 280110072Ssimokawa fwdev = fw_noderesolve_eui64(sc->fc, fwmem_eui64); 281106810Ssimokawa if (fwdev == NULL) { 282106810Ssimokawa printf("fwmem: no such device ID:%08x%08x\n", 283106810Ssimokawa fwmem_eui64.hi, fwmem_eui64.lo); 284106810Ssimokawa return EINVAL; 285106810Ssimokawa } 286103285Sikob 287110465Ssimokawa while(uio->uio_resid > 0 && !err) { 288103285Sikob offset = uio->uio_offset; 289103285Sikob dst_hi = (offset >> 32) & 0xffff; 290103285Sikob dst_lo = offset & 0xffffffff; 291110337Ssimokawa len = uio->uio_resid; 292110337Ssimokawa if (len == 4 && (dst_lo & 3) == 0) { 293110337Ssimokawa xfer = fwmem_read_quad(fwdev, NULL, fwmem_speed, 294106804Ssimokawa dst_hi, dst_lo, fw_asy_callback); 295110465Ssimokawa if (xfer == NULL) { 296110465Ssimokawa err = EINVAL; 297110465Ssimokawa break; 298110465Ssimokawa } 299110337Ssimokawa err = tsleep((caddr_t)xfer, FWPRI, "fwmrq", hz); 300110337Ssimokawa if (err !=0 || xfer->resp != 0 301110337Ssimokawa || xfer->recv.buf == NULL) 302110406Ssimokawa err = EIO; 303110406Ssimokawa else 304110406Ssimokawa err = uiomove(xfer->recv.buf 305110406Ssimokawa + xfer->recv.off + 4*3, 4, uio); 306110337Ssimokawa } else { 307110337Ssimokawa if (len > MAXLEN) 308110337Ssimokawa len = MAXLEN; 309110337Ssimokawa xfer = fwmem_read_block(fwdev, NULL, fwmem_speed, 310106804Ssimokawa dst_hi, dst_lo, len, fw_asy_callback); 311110465Ssimokawa if (xfer == NULL) { 312110465Ssimokawa err = EINVAL; 313110465Ssimokawa break; 314110465Ssimokawa } 315110337Ssimokawa err = tsleep((caddr_t)xfer, FWPRI, "fwmrb", hz); 316110337Ssimokawa if (err != 0 || xfer->resp != 0 317110337Ssimokawa || xfer->recv.buf == NULL) 318110406Ssimokawa err = EIO; 319110406Ssimokawa else 320110406Ssimokawa err = uiomove(xfer->recv.buf 321110406Ssimokawa + xfer->recv.off + 4*4, len, uio); 322110337Ssimokawa } 323110337Ssimokawa fw_xfer_free(xfer); 324103285Sikob } 325103285Sikob return err; 326103285Sikob} 327103285Sikobint 328103285Sikobfwmem_write (dev_t dev, struct uio *uio, int ioflag) 329103285Sikob{ 330110337Ssimokawa struct firewire_softc *sc; 331110337Ssimokawa struct fw_device *fwdev; 332110337Ssimokawa struct fw_xfer *xfer; 333110337Ssimokawa int err = 0; 334110337Ssimokawa int unit = DEV2UNIT(dev); 335110337Ssimokawa u_int16_t dst_hi; 336110337Ssimokawa u_int32_t dst_lo, quad; 337110337Ssimokawa char *data; 338110337Ssimokawa off_t offset; 339110337Ssimokawa int len; 340110337Ssimokawa 341110337Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 342110337Ssimokawa fwdev = fw_noderesolve_eui64(sc->fc, fwmem_eui64); 343110337Ssimokawa if (fwdev == NULL) { 344110337Ssimokawa printf("fwmem: no such device ID:%08x%08x\n", 345110337Ssimokawa fwmem_eui64.hi, fwmem_eui64.lo); 346110337Ssimokawa return EINVAL; 347110337Ssimokawa } 348110337Ssimokawa 349110337Ssimokawa data = malloc(MAXLEN, M_FW, 0); 350110337Ssimokawa if (data == NULL) 351110337Ssimokawa return ENOMEM; 352110337Ssimokawa 353110465Ssimokawa while(uio->uio_resid > 0 && !err) { 354110337Ssimokawa offset = uio->uio_offset; 355110337Ssimokawa dst_hi = (offset >> 32) & 0xffff; 356110337Ssimokawa dst_lo = offset & 0xffffffff; 357110337Ssimokawa len = uio->uio_resid; 358110337Ssimokawa if (len == 4 && (dst_lo & 3) == 0) { 359110337Ssimokawa err = uiomove((char *)&quad, sizeof(quad), uio); 360110337Ssimokawa xfer = fwmem_write_quad(fwdev, NULL, fwmem_speed, 361110337Ssimokawa dst_hi, dst_lo, quad, fw_asy_callback); 362110465Ssimokawa if (xfer == NULL) { 363110465Ssimokawa err = EINVAL; 364110465Ssimokawa break; 365110465Ssimokawa } 366110337Ssimokawa err = tsleep((caddr_t)xfer, FWPRI, "fwmwq", hz); 367110406Ssimokawa if (err !=0 || xfer->resp != 0) 368110406Ssimokawa err = EIO; 369110337Ssimokawa } else { 370110337Ssimokawa if (len > MAXLEN) 371110337Ssimokawa len = MAXLEN; 372110337Ssimokawa err = uiomove(data, len, uio); 373110337Ssimokawa if (err) 374110465Ssimokawa break; 375110337Ssimokawa xfer = fwmem_write_block(fwdev, NULL, fwmem_speed, 376110337Ssimokawa dst_hi, dst_lo, len, data, fw_asy_callback); 377110465Ssimokawa if (xfer == NULL) { 378110465Ssimokawa err = EINVAL; 379110465Ssimokawa break; 380110465Ssimokawa } 381110337Ssimokawa err = tsleep((caddr_t)xfer, FWPRI, "fwmwb", hz); 382110406Ssimokawa if (err != 0 || xfer->resp != 0) 383110406Ssimokawa err = EIO; 384110337Ssimokawa } 385110406Ssimokawa fw_xfer_free(xfer); 386110337Ssimokawa } 387110465Ssimokawa free(data, M_FW); 388110337Ssimokawa return err; 389103285Sikob} 390110337Ssimokawa 391103285Sikobint 392103285Sikobfwmem_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td) 393103285Sikob{ 394103285Sikob return EINVAL; 395103285Sikob} 396103285Sikobint 397103285Sikobfwmem_poll (dev_t dev, int events, fw_proc *td) 398103285Sikob{ 399103285Sikob return EINVAL; 400103285Sikob} 401103285Sikobint 402103285Sikobfwmem_mmap (dev_t dev, vm_offset_t offset, int nproto) 403103285Sikob{ 404103285Sikob return EINVAL; 405103285Sikob} 406