/* * Write to a hardware-level file handle. */ static int emu_write(struct emu_softc *sc, uint32_t handle, uint32_t len, struct uio *uio) { int result; KASSERT(uio->uio_rw == UIO_WRITE); if (uio->uio_offset > (off_t)0xffffffff) { return EFBIG; } lock_acquire(sc->e_lock); emu_wreg(sc, REG_HANDLE, handle); emu_wreg(sc, REG_IOLEN, len); emu_wreg(sc, REG_OFFSET, uio->uio_offset); result = uiomove(sc->e_iobuf, len, uio); membar_store_store(); if (result) { goto out; } emu_wreg(sc, REG_OPER, EMU_OP_WRITE); result = emu_waitdone(sc); out: lock_release(sc->e_lock); return result; }
/* * Common code for read and readdir. */ static int emu_doread(struct emu_softc *sc, uint32_t handle, uint32_t len, uint32_t op, struct uio *uio) { int result; KASSERT(uio->uio_rw == UIO_READ); if (uio->uio_offset > (off_t)0xffffffff) { /* beyond the largest size the file can have; generate EOF */ return 0; } lock_acquire(sc->e_lock); emu_wreg(sc, REG_HANDLE, handle); emu_wreg(sc, REG_IOLEN, len); emu_wreg(sc, REG_OFFSET, uio->uio_offset); emu_wreg(sc, REG_OPER, op); result = emu_waitdone(sc); if (result) { goto out; } membar_load_load(); result = uiomove(sc->e_iobuf, emu_rreg(sc, REG_IOLEN), uio); uio->uio_offset = emu_rreg(sc, REG_OFFSET); out: lock_release(sc->e_lock); return result; }
/* * Routine for closing a file we opened at the hardware level. * This is not necessarily called at VOP_LASTCLOSE time; it's called * at VOP_RECLAIM time. */ static int emu_close(struct emu_softc *sc, uint32_t handle) { int result; bool mine; int retries = 0; mine = lock_do_i_hold(sc->e_lock); if (!mine) { lock_acquire(sc->e_lock); } while (1) { /* Retry operation up to 10 times */ emu_wreg(sc, REG_HANDLE, handle); emu_wreg(sc, REG_OPER, EMU_OP_CLOSE); result = emu_waitdone(sc); if (result==EIO && retries < 10) { kprintf("emu%d: I/O error on close, retrying\n", sc->e_unit); retries++; continue; } break; } if (!mine) { lock_release(sc->e_lock); } return result; }
/* * Common code for read and readdir. */ static int emu_doread(struct emu_softc *sc, uint32_t handle, uint32_t len, uint32_t op, struct uio *uio) { int result; KASSERT(uio->uio_rw == UIO_READ); lock_acquire(sc->e_lock); emu_wreg(sc, REG_HANDLE, handle); emu_wreg(sc, REG_IOLEN, len); emu_wreg(sc, REG_OFFSET, uio->uio_offset); emu_wreg(sc, REG_OPER, op); result = emu_waitdone(sc); if (result) { goto out; } result = uiomove(sc->e_iobuf, emu_rreg(sc, REG_IOLEN), uio); uio->uio_offset = emu_rreg(sc, REG_OFFSET); out: lock_release(sc->e_lock); return result; }
/* * Truncate a hardware-level file handle. */ static int emu_trunc(struct emu_softc *sc, uint32_t handle, off_t len) { int result; lock_acquire(sc->e_lock); emu_wreg(sc, REG_HANDLE, handle); emu_wreg(sc, REG_IOLEN, len); emu_wreg(sc, REG_OPER, EMU_OP_TRUNC); result = emu_waitdone(sc); lock_release(sc->e_lock); return result; }
/* * Get the file size associated with a hardware-level file handle. */ static int emu_getsize(struct emu_softc *sc, uint32_t handle, off_t *retval) { int result; lock_acquire(sc->e_lock); emu_wreg(sc, REG_HANDLE, handle); emu_wreg(sc, REG_OPER, EMU_OP_GETSIZE); result = emu_waitdone(sc); if (result==0) { *retval = emu_rreg(sc, REG_IOLEN); } lock_release(sc->e_lock); return result; }
/* * Common file open routine (for both VOP_LOOKUP and VOP_CREATE). Not * for VOP_EACHOPEN. At the hardware level, we need to "open" files in * order to look at them, so by the time VOP_EACHOPEN is called the * files are already open. */ static int emu_open(struct emu_softc *sc, uint32_t handle, const char *name, bool create, bool excl, mode_t mode, uint32_t *newhandle, int *newisdir) { uint32_t op; int result; if (strlen(name)+1 > EMU_MAXIO) { return ENAMETOOLONG; } if (create && excl) { op = EMU_OP_EXCLCREATE; } else if (create) { op = EMU_OP_CREATE; } else { op = EMU_OP_OPEN; } /* mode isn't supported (yet?) */ (void)mode; lock_acquire(sc->e_lock); strcpy(sc->e_iobuf, name); membar_store_store(); emu_wreg(sc, REG_IOLEN, strlen(name)); emu_wreg(sc, REG_HANDLE, handle); emu_wreg(sc, REG_OPER, op); result = emu_waitdone(sc); if (result==0) { *newhandle = emu_rreg(sc, REG_HANDLE); *newisdir = emu_rreg(sc, REG_IOLEN)>0; } lock_release(sc->e_lock); return result; }