summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/conf/files1
-rw-r--r--sys/dev/firewire/sbp_targ.c1550
-rw-r--r--sys/modules/firewire/Makefile1
-rw-r--r--sys/modules/firewire/sbp_targ/Makefile16
4 files changed, 1568 insertions, 0 deletions
diff --git a/sys/conf/files b/sys/conf/files
index 49d2f3a..83571e9 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -416,6 +416,7 @@ dev/firewire/fwohci.c optional firewire
dev/firewire/fwohci_pci.c optional firewire pci
dev/firewire/if_fwe.c optional fwe
dev/firewire/sbp.c optional sbp
+dev/firewire/sbp_targ.c optional sbp_targ
dev/fxp/if_fxp.c optional fxp
dev/gem/if_gem.c optional gem
dev/gem/if_gem_pci.c optional gem pci
diff --git a/sys/dev/firewire/sbp_targ.c b/sys/dev/firewire/sbp_targ.c
new file mode 100644
index 0000000..6b3465d
--- /dev/null
+++ b/sys/dev/firewire/sbp_targ.c
@@ -0,0 +1,1550 @@
+/*
+ * Copyright (C) 2003
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/malloc.h>
+#if __FreeBSD_version < 500000
+#include <sys/devicestat.h>
+#endif
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/firewirereg.h>
+#include <dev/firewire/iec13213.h>
+#include <dev/firewire/sbp.h>
+#include <dev/firewire/fwmem.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_periph.h>
+#include <cam/scsi/scsi_all.h>
+
+#define SBP_TARG_RECV_LEN (8)
+#define MAX_LUN 63
+/*
+ * management/command block agent registers
+ *
+ * BASE 0xffff f001 0000 management port
+ * BASE 0xffff f001 0020 command port for lun0
+ * BASE 0xffff f001 0040 command port for lun1
+ *
+ */
+#define SBP_TARG_MGM 0x10000 /* offset from 0xffff f000 000 */
+#define SBP_TARG_BIND_HI 0xffff
+#define SBP_TARG_BIND_LO(l) (0xf0000000 + SBP_TARG_MGM + 0x20 * ((l) + 1))
+#define SBP_TARG_BIND_START (((u_int64_t)SBP_TARG_BIND_HI << 32) | \
+ SBP_TARG_BIND_LO(-1))
+#define SBP_TARG_BIND_END (((u_int64_t)SBP_TARG_BIND_HI << 32) | \
+ SBP_TARG_BIND_LO(MAX_LUN))
+#define SBP_TARG_LUN(lo) (((lo) - SBP_TARG_BIND_LO(0))/0x20)
+
+#define FETCH_MGM 0
+#define FETCH_CMD 1
+#define FETCH_POINTER 2
+
+MALLOC_DEFINE(M_SBP_TARG, "sbp_targ", "SBP-II/FireWire target mode");
+
+static int debug = 0;
+
+SYSCTL_INT(_debug, OID_AUTO, sbp_targ_debug, CTLFLAG_RW, &debug, 0,
+ "SBP target mode debug flag");
+
+struct sbp_targ_softc {
+ struct firewire_dev_comm fd;
+ struct cam_sim *sim;
+ struct cam_path *path;
+ struct fw_bind fwb;
+ int ndevs;
+ struct crom_chunk unit;
+ struct sbp_targ_lstate *lstate[MAX_LUN];
+ struct sbp_targ_lstate *black_hole;
+};
+
+struct sbp_targ_lstate {
+ struct sbp_targ_softc *sc;
+ struct cam_path *path;
+ struct ccb_hdr_slist accept_tios;
+ struct ccb_hdr_slist immed_notifies;
+ struct crom_chunk model;
+ /* XXX per initiater data */
+ struct fw_device *fwdev;
+ struct sbp_login_res loginres;
+ u_int32_t flags;
+#define LINK_ACTIVE 1
+#define ATIO_STARVED 2
+ u_int16_t fifo_hi;
+ u_int16_t last_hi;
+ u_int32_t fifo_lo;
+ u_int32_t last_lo;
+ STAILQ_HEAD(, orb_info) orbs;
+ u_int16_t login_id;
+ u_int16_t lun;
+};
+
+struct corb4 {
+#if BYTE_ORDER == BIG_ENDIAN
+ u_int32_t n:1,
+ rq_fmt:2,
+ :1,
+ dir:1,
+ spd:3,
+ max_payload:4,
+ page_table_present:1,
+ page_size:3,
+ data_size:16;
+#else
+ u_int32_t data_size:16,
+ page_size:3,
+ page_table_present:1,
+ max_payload:4,
+ spd:3,
+ dir:1,
+ :1,
+ rq_fmt:2,
+ n:1;
+#endif
+};
+
+struct morb4 {
+#if BYTE_ORDER == BIG_ENDIAN
+ u_int32_t n:1,
+ rq_fmt:2,
+ :9,
+ fun:4,
+ id:16;
+#else
+ u_int32_t id:16,
+ fun:4,
+ :9,
+ rq_fmt:2,
+ n:1;
+#endif
+};
+
+struct orb_info {
+ struct sbp_targ_softc *sc;
+ struct fw_device *fwdev;
+ struct sbp_targ_lstate *lstate;
+ union ccb *ccb;
+ struct ccb_accept_tio *atio;
+ u_int8_t state;
+#define ORBI_STATUS_NONE 0
+#define ORBI_STATUS_FETCH 1
+#define ORBI_STATUS_ATIO 2
+#define ORBI_STATUS_CTIO 3
+#define ORBI_STATUS_STATUS 4
+#define ORBI_STATUS_POINTER 5
+#define ORBI_STATUS_ABORTED 7
+ u_int8_t refcount;
+ u_int16_t orb_hi;
+ u_int32_t orb_lo;
+ u_int32_t data_hi;
+ u_int32_t data_lo;
+ struct corb4 orb4;
+ STAILQ_ENTRY(orb_info) link;
+ u_int32_t orb[8];
+ u_int32_t *page_table;
+ struct sbp_status status;
+};
+
+static char *orb_fun_name[] = {
+ ORB_FUN_NAMES
+};
+
+static void sbp_targ_recv(struct fw_xfer *);
+static void sbp_targ_fetch_orb(struct sbp_targ_softc *, struct fw_device *,
+ u_int16_t, u_int32_t, struct sbp_targ_lstate *, int);
+
+static void
+sbp_targ_identify(driver_t *driver, device_t parent)
+{
+ BUS_ADD_CHILD(parent, 0, "sbp_targ", device_get_unit(parent));
+}
+
+static int
+sbp_targ_probe(device_t dev)
+{
+ device_t pa;
+
+ pa = device_get_parent(dev);
+ if(device_get_unit(dev) != device_get_unit(pa)){
+ return(ENXIO);
+ }
+
+ device_set_desc(dev, "SBP-2/SCSI over FireWire target mode");
+ return (0);
+}
+
+static void
+sbp_targ_post_busreset(void *arg)
+{
+ struct sbp_targ_softc *sc;
+ struct crom_src *src;
+ struct crom_chunk *root;
+ struct crom_chunk *unit;
+ struct sbp_targ_lstate *lstate;
+ int i;
+
+ sc = (struct sbp_targ_softc *) arg;
+ src = sc->fd.fc->crom_src;
+ root = sc->fd.fc->crom_root;
+
+ unit = &sc->unit;
+
+ bzero(unit, sizeof(struct crom_chunk));
+
+ crom_add_chunk(src, root, unit, CROM_UDIR);
+ crom_add_entry(unit, CSRKEY_SPEC, CSRVAL_ANSIT10);
+ crom_add_entry(unit, CSRKEY_VER, CSRVAL_T10SBP2);
+ crom_add_entry(unit, CSRKEY_COM_SPEC, CSRVAL_ANSIT10);
+ crom_add_entry(unit, CSRKEY_COM_SET, CSRVAL_SCSI);
+
+ crom_add_entry(unit, CROM_MGM, SBP_TARG_MGM >> 2);
+ crom_add_entry(unit, CSRKEY_UNIT_CH, (10<<8) | 8);
+
+ for (i = 0; i < MAX_LUN; i ++) {
+ lstate = sc->lstate[i];
+ if (lstate == NULL)
+ continue;
+ crom_add_entry(unit, CSRKEY_FIRM_VER, 1);
+ crom_add_entry(unit, CROM_LUN, i);
+ crom_add_entry(unit, CSRKEY_MODEL, 1);
+ crom_add_simple_text(src, unit, &lstate->model, "TargetMode");
+ }
+}
+
+static cam_status
+sbp_targ_find_devs(struct sbp_targ_softc *sc, union ccb *ccb,
+ struct sbp_targ_lstate **lstate, int notfound_failure)
+{
+ u_int lun;
+
+ /* XXX 0 is the only vaild target_id */
+ if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD &&
+ ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
+ *lstate = sc->black_hole;
+ return (CAM_REQ_CMP);
+ }
+
+ if (ccb->ccb_h.target_id != 0)
+ return (CAM_TID_INVALID);
+
+ lun = ccb->ccb_h.target_lun;
+ if (lun >= MAX_LUN)
+ return (CAM_LUN_INVALID);
+
+ *lstate = sc->lstate[lun];
+
+ if (notfound_failure != 0 && *lstate == NULL)
+ return (CAM_PATH_INVALID);
+
+ return (CAM_REQ_CMP);
+}
+
+static void
+sbp_targ_en_lun(struct sbp_targ_softc *sc, union ccb *ccb)
+{
+ struct ccb_en_lun *cel = &ccb->cel;
+ struct sbp_targ_lstate *lstate;
+ struct orb_info *orbi, *next;
+ cam_status status;
+
+ status = sbp_targ_find_devs(sc, ccb, &lstate, 0);
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ return;
+ }
+
+ if (cel->enable != 0) {
+ if (lstate != NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Lun already enabled\n");
+ ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+ return;
+ }
+ if (cel->grp6_len != 0 || cel->grp7_len != 0) {
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ printf("Non-zero Group Codes\n");
+ return;
+ }
+ lstate = (struct sbp_targ_lstate *)
+ malloc(sizeof(*lstate), M_SBP_TARG, M_NOWAIT | M_ZERO);
+ if (lstate == NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate lstate\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD)
+ sc->black_hole = lstate;
+ else
+ sc->lstate[ccb->ccb_h.target_lun] = lstate;
+ memset(lstate, 0, sizeof(*lstate));
+ lstate->sc = sc;
+ status = xpt_create_path(&lstate->path, /*periph*/NULL,
+ xpt_path_path_id(ccb->ccb_h.path),
+ xpt_path_target_id(ccb->ccb_h.path),
+ xpt_path_lun_id(ccb->ccb_h.path));
+ if (status != CAM_REQ_CMP) {
+ free(lstate, M_SBP_TARG);
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate path\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ SLIST_INIT(&lstate->accept_tios);
+ SLIST_INIT(&lstate->immed_notifies);
+ STAILQ_INIT(&lstate->orbs);
+ lstate->last_hi = 0xffff;
+ lstate->last_lo = 0xffffffff;
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Lun now enabled for target mode\n");
+ /* bus reset */
+ sc->fd.fc->ibr(sc->fd.fc);
+ } else {
+ if (lstate == NULL) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return;
+ }
+ ccb->ccb_h.status = CAM_REQ_CMP;
+
+ if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
+ printf("ATIOs pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ }
+
+ if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
+ printf("INOTs pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ }
+
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ return;
+ }
+
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Target mode disabled\n");
+ xpt_free_path(lstate->path);
+
+ for (orbi = STAILQ_FIRST(&lstate->orbs); orbi != NULL;
+ orbi = next) {
+ next = STAILQ_NEXT(orbi, link);
+ free(orbi, M_SBP_TARG);
+ }
+
+ if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD)
+ sc->black_hole = NULL;
+ else
+ sc->lstate[ccb->ccb_h.target_lun] = NULL;
+ free(lstate, M_SBP_TARG);
+
+ /* bus reset */
+ sc->fd.fc->ibr(sc->fd.fc);
+ }
+}
+
+static void
+sbp_targ_send_lstate_events(struct sbp_targ_softc *sc,
+ struct sbp_targ_lstate *lstate)
+{
+#if 0
+ struct ccb_hdr *ccbh;
+ struct ccb_immed_notify *inot;
+
+ printf("%s: not implemented yet\n", __FUNCTION__);
+#endif
+}
+
+static __inline void
+sbp_targ_remove_orb_info(struct sbp_targ_lstate *lstate, struct orb_info *orbi)
+{
+ STAILQ_REMOVE(&lstate->orbs, orbi, orb_info, link);
+}
+
+/*
+ * tag_id/init_id encoding
+ *
+ * tag_id and init_id has only 32bit for each.
+ * scsi_target can handle very limited number(up to 15) of init_id.
+ * we have to encode 48bit orb and 64bit EUI64 into these
+ * variables.
+ *
+ * tag_id represents lower 32bit of ORB address.
+ * init_id represents node_id now.
+ *
+ */
+
+static struct orb_info *
+sbp_targ_get_orb_info(struct sbp_targ_lstate *lstate,
+ u_int tag_id, u_int init_id)
+{
+ struct orb_info *orbi;
+
+ STAILQ_FOREACH(orbi, &lstate->orbs, link)
+ if (orbi->orb_lo == tag_id &&
+#if 0
+ orbi->orb_hi == (init_id & 0xffff) &&
+ orbi->fwdev->dst == (init_id >> 16))
+#else
+ orbi->fwdev->dst == init_id)
+#endif
+ goto found;
+ printf("%s: orb not found\n", __FUNCTION__);
+ return (NULL);
+found:
+ return (orbi);
+}
+
+static void
+sbp_targ_abort(struct orb_info *orbi)
+{
+ struct orb_info *norbi;
+
+ for (; orbi != NULL; orbi = norbi) {
+ printf("%s: status=%d\n", __FUNCTION__, orbi->state);
+ norbi = STAILQ_NEXT(orbi, link);
+ if (orbi->state != ORBI_STATUS_ABORTED) {
+ if (orbi->ccb != NULL) {
+ orbi->ccb->ccb_h.status = CAM_REQ_ABORTED;
+ xpt_done(orbi->ccb);
+ orbi->ccb = NULL;
+ }
+ if (orbi->state <= ORBI_STATUS_ATIO) {
+ sbp_targ_remove_orb_info(orbi->lstate, orbi);
+ free(orbi, M_SBP_TARG);
+ } else
+ orbi->state = ORBI_STATUS_ABORTED;
+ }
+ }
+}
+
+static void
+sbp_targ_free_orbi(struct fw_xfer *xfer)
+{
+ struct orb_info *orbi;
+
+ orbi = (struct orb_info *)xfer->sc;
+ if (xfer->resp != 0) {
+ /* XXX */
+ printf("%s: xfer->resp != 0\n", __FUNCTION__);
+ }
+ free(orbi, M_SBP_TARG);
+ fw_xfer_free(xfer);
+}
+
+static void
+sbp_targ_status_FIFO(struct orb_info *orbi,
+ u_int32_t fifo_hi, u_int32_t fifo_lo, int dequeue)
+{
+ struct fw_xfer *xfer;
+
+ if (dequeue)
+ sbp_targ_remove_orb_info(orbi->lstate, orbi);
+
+ xfer = fwmem_write_block(orbi->fwdev, (void *)orbi,
+ /*spd*/2, fifo_hi, fifo_lo,
+ sizeof(u_int32_t) * (orbi->status.len + 1), (char *)&orbi->status,
+ sbp_targ_free_orbi);
+
+ if (xfer == NULL) {
+ /* XXX */
+ printf("%s: xfer == NULL\n", __FUNCTION__);
+ }
+}
+
+static void
+sbp_targ_send_status(struct orb_info *orbi, union ccb *ccb)
+{
+ struct sbp_status *sbp_status;
+
+ sbp_status = &orbi->status;
+
+ orbi->state = ORBI_STATUS_STATUS;
+
+ sbp_status->resp = 0; /* XXX */
+ sbp_status->status = 0; /* XXX */
+ sbp_status->dead = 0; /* XXX */
+
+ switch (ccb->csio.scsi_status) {
+ case SCSI_STATUS_OK:
+ if (debug)
+ printf("%s: STATUS_OK\n", __FUNCTION__);
+ sbp_status->len = 1;
+ break;
+ case SCSI_STATUS_CHECK_COND:
+ case SCSI_STATUS_BUSY:
+ case SCSI_STATUS_CMD_TERMINATED:
+ {
+ struct sbp_cmd_status *sbp_cmd_status;
+ struct scsi_sense_data *sense;
+
+ if (debug)
+ printf("%s: STATUS %d\n", __FUNCTION__,
+ ccb->csio.scsi_status);
+ sbp_cmd_status = (struct sbp_cmd_status *)&sbp_status->data[0];
+ sbp_cmd_status->status = ccb->csio.scsi_status;
+ sense = &ccb->csio.sense_data;
+
+ sbp_targ_abort(STAILQ_NEXT(orbi, link));
+
+ if ((sense->error_code & SSD_ERRCODE) == SSD_CURRENT_ERROR)
+ sbp_cmd_status->sfmt = SBP_SFMT_CURR;
+ else
+ sbp_cmd_status->sfmt = SBP_SFMT_DEFER;
+
+ sbp_cmd_status->valid = (sense->error_code & SSD_ERRCODE_VALID)
+ ? 1 : 0;
+ sbp_cmd_status->s_key = sense->flags & SSD_KEY;
+ sbp_cmd_status->mark = (sense->flags & SSD_FILEMARK)? 1 : 0;
+ sbp_cmd_status->eom = (sense->flags & SSD_EOM) ? 1 : 0;
+ sbp_cmd_status->ill_len = (sense->flags & SSD_ILI) ? 1 : 0;
+
+ bcopy(&sense->info[0], &sbp_cmd_status->info, 4);
+
+ if (sense->extra_len <= 6)
+ /* add_sense_code(_qual), info, cmd_spec_info */
+ sbp_status->len = 4;
+ else
+ /* fru, sense_key_spec */
+ sbp_status->len = 5;
+
+ bcopy(&sense->cmd_spec_info[0], &sbp_cmd_status->cdb, 4);
+
+ sbp_cmd_status->s_code = sense->add_sense_code;
+ sbp_cmd_status->s_qlfr = sense->add_sense_code_qual;
+ sbp_cmd_status->fru = sense->fru;
+
+ bcopy(&sense->sense_key_spec[0],
+ &sbp_cmd_status->s_keydep[0], 3);
+
+ break;
+ }
+ default:
+ printf("%s: unknown scsi status 0x%x\n", __FUNCTION__,
+ sbp_status->status);
+ }
+
+ sbp_targ_status_FIFO(orbi,
+ orbi->lstate->fifo_hi, orbi->lstate->fifo_lo, /*dequeue*/1);
+
+ if (orbi->page_table != NULL)
+ free(orbi->page_table, M_SBP_TARG);
+}
+
+static void
+sbp_targ_cam_done(struct fw_xfer *xfer)
+{
+ struct orb_info *orbi;
+ union ccb *ccb;
+
+ orbi = (struct orb_info *)xfer->sc;
+
+ if (debug)
+ printf("%s: resp=%d refcount=%d\n", __FUNCTION__,
+ xfer->resp, orbi->refcount);
+
+ if (xfer->resp != 0) {
+ printf("%s: xfer->resp != 0\n", __FUNCTION__);
+ orbi->status.resp = SBP_TRANS_FAIL;
+ orbi->status.status = htonl(OBJ_DATA | SBE_TIMEOUT /*XXX*/);
+ orbi->status.dead = 1;
+ sbp_targ_abort(STAILQ_NEXT(orbi, link));
+ }
+
+ orbi->refcount --;
+
+ ccb = orbi->ccb;
+ if (orbi->refcount == 0) {
+ if (orbi->state == ORBI_STATUS_ABORTED) {
+ if (debug)
+ printf("%s: orbi aborted\n", __FUNCTION__);
+ sbp_targ_remove_orb_info(orbi->lstate, orbi);
+ if (orbi->page_table != NULL)
+ free(orbi->page_table, M_SBP_TARG);
+ free(orbi, M_SBP_TARG);
+ } else if (orbi->status.resp == 0) {
+ if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0)
+ sbp_targ_send_status(orbi, ccb);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ } else {
+ orbi->status.len = 1;
+ sbp_targ_status_FIFO(orbi,
+ orbi->lstate->fifo_hi, orbi->lstate->fifo_lo,
+ /*dequeue*/1);
+ ccb->ccb_h.status = CAM_REQ_ABORTED;
+ xpt_done(ccb);
+ }
+ }
+
+ fw_xfer_free(xfer);
+}
+
+static cam_status
+sbp_targ_abort_ccb(struct sbp_targ_softc *sc, union ccb *ccb)
+{
+ union ccb *accb;
+ struct sbp_targ_lstate *lstate;
+ struct ccb_hdr_slist *list;
+ struct ccb_hdr *curelm;
+ int found;
+ cam_status status;
+
+ status = sbp_targ_find_devs(sc, ccb, &lstate, 0);
+ if (status != CAM_REQ_CMP)
+ return (status);
+
+ accb = ccb->cab.abort_ccb;
+
+ if (accb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO)
+ list = &lstate->accept_tios;
+ else if (accb->ccb_h.func_code == XPT_IMMED_NOTIFY)
+ list = &lstate->immed_notifies;
+ else
+ return (CAM_UA_ABORT);
+
+ curelm = SLIST_FIRST(list);
+ found = 0;
+ if (curelm == &accb->ccb_h) {
+ found = 1;
+ SLIST_REMOVE_HEAD(list, sim_links.sle);
+ } else {
+ while(curelm != NULL) {
+ struct ccb_hdr *nextelm;
+
+ nextelm = SLIST_NEXT(curelm, sim_links.sle);
+ if (nextelm == &accb->ccb_h) {
+ found = 1;
+ SLIST_NEXT(curelm, sim_links.sle) =
+ SLIST_NEXT(nextelm, sim_links.sle);
+ break;
+ }
+ curelm = nextelm;
+ }
+ }
+ if (found) {
+ accb->ccb_h.status = CAM_REQ_ABORTED;
+ xpt_done(accb);
+ return (CAM_REQ_CMP);
+ }
+ printf("%s: not found\n", __FUNCTION__);
+ return (CAM_PATH_INVALID);
+}
+
+static void
+sbp_targ_xfer_buf(struct orb_info *orbi, u_int offset,
+ u_int16_t dst_hi, u_int32_t dst_lo, u_int size,
+ void (*hand)(struct fw_xfer *))
+{
+ struct fw_xfer *xfer;
+ u_int len, ccb_dir, off = 0;
+ char *ptr;
+
+ if (debug)
+ printf("%s: offset=%d size=%d\n", __FUNCTION__, offset, size);
+ ccb_dir = orbi->ccb->ccb_h.flags & CAM_DIR_MASK;
+ ptr = (char *)orbi->ccb->csio.data_ptr + offset;
+
+ while (size > 0) {
+ /* XXX assume dst_lo + off doesn't overflow */
+ len = MIN(size, 2048 /* XXX */);
+ size -= len;
+ orbi->refcount ++;
+ if (ccb_dir == CAM_DIR_OUT)
+ xfer = fwmem_read_block(orbi->fwdev,
+ (void *)orbi, /*spd*/2,
+ dst_hi, dst_lo + off, len,
+ ptr + off, hand);
+ else
+ xfer = fwmem_write_block(orbi->fwdev,
+ (void *)orbi, /*spd*/2,
+ dst_hi, dst_lo + off, len,
+ ptr + off, hand);
+ if (xfer == NULL) {
+ printf("%s: xfer == NULL", __FUNCTION__);
+ /* XXX what should we do?? */
+ orbi->refcount --;
+ }
+ off += len;
+ }
+}
+
+static void
+sbp_targ_pt_done(struct fw_xfer *xfer)
+{
+ struct orb_info *orbi;
+ union ccb *ccb;
+ u_int i, offset, res, len;
+ u_int32_t t1, t2, *p;
+
+ orbi = (struct orb_info *)xfer->sc;
+ ccb = orbi->ccb;
+ if (orbi->state == ORBI_STATUS_ABORTED) {
+ if (debug)
+ printf("%s: orbi aborted\n", __FUNCTION__);
+ sbp_targ_remove_orb_info(orbi->lstate, orbi);
+ free(orbi->page_table, M_SBP_TARG);
+ free(orbi, M_SBP_TARG);
+ fw_xfer_free(xfer);
+ return;
+ }
+ if (xfer->resp != 0) {
+ printf("%s: xfer->resp != 0\n", __FUNCTION__);
+ orbi->status.resp = SBP_TRANS_FAIL;
+ orbi->status.status = htonl(OBJ_PT | SBE_TIMEOUT /*XXX*/);
+ orbi->status.dead = 1;
+ orbi->status.len = 1;
+ sbp_targ_abort(STAILQ_NEXT(orbi, link));
+
+ sbp_targ_status_FIFO(orbi,
+ orbi->lstate->fifo_hi, orbi->lstate->fifo_lo, /*dequeue*/1);
+ free(orbi->page_table, M_SBP_TARG);
+ fw_xfer_free(xfer);
+ return;
+ }
+ res = ccb->csio.dxfer_len;
+ offset = 0;
+ if (debug)
+ printf("%s: dxfer_len=%d\n", __FUNCTION__, res);
+ orbi->refcount ++;
+ for (p = orbi->page_table, i = orbi->orb4.data_size; i > 0; i --) {
+ t1 = ntohl(*p++);
+ t2 = ntohl(*p++);
+ if (debug)
+ printf("page_table: %04x:%08x %d\n",
+ t1 & 0xffff, t2, t1>>16);
+ len = MIN(t1 >> 16, res);
+ res -= len;
+ sbp_targ_xfer_buf(orbi, offset, t1 & 0xffff, t2, len,
+ sbp_targ_cam_done);
+ offset += len;
+ if (res == 0)
+ break;
+ }
+ orbi->refcount --;
+ if (orbi->refcount == 0)
+ printf("%s: refcount == 0\n", __FUNCTION__);
+ if (res !=0)
+ /* XXX handle res != 0 case */
+ printf("%s: page table is too small(%d)\n", __FUNCTION__, res);
+
+ fw_xfer_free(xfer);
+ return;
+}
+
+static void
+sbp_targ_fetch_pt(struct orb_info *orbi)
+{
+ struct fw_xfer *xfer;
+
+ if (debug)
+ printf("%s: page_table_size=%d\n",
+ __FUNCTION__, orbi->orb4.data_size);
+ orbi->page_table = malloc(orbi->orb4.data_size*8, M_SBP_TARG, M_NOWAIT);
+ if (orbi->page_table == NULL)
+ goto error;
+ xfer = fwmem_read_block(orbi->fwdev, (void *)orbi, /*spd*/2,
+ orbi->data_hi, orbi->data_lo, orbi->orb4.data_size*8,
+ (void *)orbi->page_table, sbp_targ_pt_done);
+ if (xfer != NULL)
+ return;
+error:
+ orbi->ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ xpt_done(orbi->ccb);
+ return;
+}
+
+static void
+sbp_targ_action1(struct cam_sim *sim, union ccb *ccb)
+{
+ struct sbp_targ_softc *sc;
+ struct sbp_targ_lstate *lstate;
+ cam_status status;
+ u_int ccb_dir;
+
+ sc = (struct sbp_targ_softc *)cam_sim_softc(sim);
+
+ status = sbp_targ_find_devs(sc, ccb, &lstate, TRUE);
+
+ switch (ccb->ccb_h.func_code) {
+ case XPT_CONT_TARGET_IO:
+ {
+ struct orb_info *orbi;
+
+ if (debug)
+ printf("%s: XPT_CONT_TARGET_IO\n", __FUNCTION__);
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ xpt_done(ccb);
+ break;
+ }
+ /* XXX transfer from/to initiator */
+ orbi = sbp_targ_get_orb_info(lstate,
+ ccb->csio.tag_id, ccb->csio.init_id);
+ if (orbi == NULL) {
+ printf("%s: no such ORB found, aborted?\n",
+ __FUNCTION__);
+ ccb->ccb_h.status = CAM_REQ_ABORTED; /* XXX */
+ xpt_done(ccb);
+ break;
+ }
+ if (orbi->state == ORBI_STATUS_ABORTED) {
+ if (debug)
+ printf("%s: ctio aborted\n", __FUNCTION__);
+ sbp_targ_remove_orb_info(orbi->lstate, orbi);
+ free(orbi, M_SBP_TARG);
+ break;
+ }
+ orbi->state = ORBI_STATUS_CTIO;
+
+ orbi->ccb = ccb;
+ ccb_dir = ccb->ccb_h.flags & CAM_DIR_MASK;
+
+ /* XXX */
+ if (ccb->csio.dxfer_len == 0)
+ ccb_dir = CAM_DIR_NONE;
+
+ /* Sanity check */
+ if (ccb_dir == CAM_DIR_IN && orbi->orb4.dir == 0)
+ printf("%s: direction mismatch\n", __FUNCTION__);
+
+ /* check page table */
+ if (ccb_dir != CAM_DIR_NONE && orbi->orb4.page_table_present) {
+ if (debug)
+ printf("%s: page_table_present\n",
+ __FUNCTION__);
+ if (orbi->orb4.page_size != 0) {
+ printf("%s: unsupported pagesize %d != 0\n",
+ __FUNCTION__, orbi->orb4.page_size);
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+ sbp_targ_fetch_pt(orbi);
+ break;
+ }
+
+ /* Sanity check */
+ if (ccb_dir != CAM_DIR_NONE &&
+ orbi->orb4.data_size != ccb->csio.dxfer_len)
+ printf("%s: data_size(%d) != dxfer_len(%d)\n",
+ __FUNCTION__, orbi->orb4.data_size,
+ ccb->csio.dxfer_len);
+
+ if (ccb_dir != CAM_DIR_NONE)
+ sbp_targ_xfer_buf(orbi, 0, orbi->data_hi,
+ orbi->data_lo,
+ MIN(orbi->orb4.data_size, ccb->csio.dxfer_len),
+ sbp_targ_cam_done);
+
+ if (ccb_dir == CAM_DIR_NONE) {
+ if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0)
+ sbp_targ_send_status(orbi, ccb);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ }
+ break;
+ }
+ case XPT_ACCEPT_TARGET_IO: /* Add Accept Target IO Resource */
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ xpt_done(ccb);
+ break;
+ }
+ SLIST_INSERT_HEAD(&lstate->accept_tios, &ccb->ccb_h,
+ sim_links.sle);
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+ if ((lstate->flags & ATIO_STARVED) != 0) {
+ if (debug)
+ printf("%s: new atio arrived\n", __FUNCTION__);
+ lstate->flags &= ~ATIO_STARVED;
+ sbp_targ_fetch_orb(lstate->sc, lstate->fwdev,
+ lstate->last_hi, lstate->last_lo,
+ lstate, FETCH_CMD);
+ }
+ break;
+ case XPT_NOTIFY_ACK: /* recycle notify ack */
+ case XPT_IMMED_NOTIFY: /* Add Immediate Notify Resource */
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ xpt_done(ccb);
+ break;
+ }
+ SLIST_INSERT_HEAD(&lstate->immed_notifies, &ccb->ccb_h,
+ sim_links.sle);
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+ sbp_targ_send_lstate_events(sc, lstate);
+ break;
+ case XPT_EN_LUN:
+ sbp_targ_en_lun(sc, ccb);
+ xpt_done(ccb);
+ break;
+ case XPT_PATH_INQ:
+ {
+ struct ccb_pathinq *cpi = &ccb->cpi;
+
+ cpi->version_num = 1; /* XXX??? */
+ cpi->hba_inquiry = PI_TAG_ABLE;
+ cpi->target_sprt = PIT_PROCESSOR
+ | PIT_DISCONNECT
+ | PIT_TERM_IO;
+ cpi->hba_misc = PIM_NOBUSRESET | PIM_NO_6_BYTE;
+ cpi->hba_eng_cnt = 0;
+ cpi->max_target = 7; /* XXX */
+ cpi->max_lun = MAX_LUN - 1;
+ cpi->initiator_id = 7; /* XXX */
+ cpi->bus_id = sim->bus_id;
+ cpi->base_transfer_speed = 400 * 1000 / 8;
+ strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+ strncpy(cpi->hba_vid, "SBP_TARG", HBA_IDLEN);
+ strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN);
+ cpi->unit_number = sim->unit_number;
+
+ cpi->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_ABORT:
+ {
+ union ccb *accb = ccb->cab.abort_ccb;
+
+ switch (accb->ccb_h.func_code) {
+ case XPT_ACCEPT_TARGET_IO:
+ case XPT_IMMED_NOTIFY:
+ ccb->ccb_h.status = sbp_targ_abort_ccb(sc, ccb);
+ break;
+ case XPT_CONT_TARGET_IO:
+ /* XXX */
+ ccb->ccb_h.status = CAM_UA_ABORT;
+ break;
+ default:
+ printf("%s: aborting unknown function %d\n",
+ __FUNCTION__, accb->ccb_h.func_code);
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ break;
+ }
+ xpt_done(ccb);
+ break;
+ }
+ default:
+ printf("%s: unknown function %d\n",
+ __FUNCTION__, ccb->ccb_h.func_code);
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+ return;
+}
+
+static void
+sbp_targ_action(struct cam_sim *sim, union ccb *ccb)
+{
+ int s;
+
+ s = splfw();
+ sbp_targ_action1(sim, ccb);
+ splx(s);
+}
+
+static void
+sbp_targ_poll(struct cam_sim *sim)
+{
+ /* XXX */
+ return;
+}
+
+static void
+sbp_targ_cmd_handler(struct fw_xfer *xfer)
+{
+ struct fw_pkt *fp;
+ u_int32_t *orb;
+ struct corb4 *orb4;
+ struct orb_info *orbi;
+ struct ccb_accept_tio *atio;
+ struct sbp_targ_lstate *lstate;
+ u_char *bytes;
+ int i;
+
+ orbi = (struct orb_info *)xfer->sc;
+ if (xfer->resp != 0) {
+ printf("%s: xfer->resp != 0\n", __FUNCTION__);
+ orbi->status.resp = SBP_TRANS_FAIL;
+ orbi->status.status = htonl(OBJ_ORB | SBE_TIMEOUT /*XXX*/);
+ orbi->status.dead = 1;
+ orbi->status.len = 1;
+ sbp_targ_abort(STAILQ_NEXT(orbi, link));
+
+ sbp_targ_status_FIFO(orbi,
+ orbi->lstate->fifo_hi, orbi->lstate->fifo_lo, /*dequeue*/1);
+ fw_xfer_free(xfer);
+ return;
+ }
+ fp = &xfer->recv.hdr;
+
+ if (orbi->state == ORBI_STATUS_ABORTED) {
+ printf("%s: aborted\n", __FUNCTION__);
+ sbp_targ_remove_orb_info(orbi->lstate, orbi);
+ free(orbi, M_SBP_TARG);
+ goto done0;
+ }
+ orbi->state = ORBI_STATUS_ATIO;
+
+ lstate = orbi->lstate;
+
+ orb = orbi->orb;
+ /* swap payload except SCSI command */
+ for (i = 0; i < 5; i ++)
+ orb[i] = ntohl(orb[i]);
+
+ orb4 = (struct corb4 *)&orb[4];
+ if (orb4->rq_fmt != 0) {
+ /* XXX */
+ printf("%s: rq_fmt(%d) != 0\n", __FUNCTION__, orb4->rq_fmt);
+ }
+
+ atio = orbi->atio;
+ atio->ccb_h.target_id = 0; /* XXX */
+ atio->ccb_h.target_lun = lstate->lun;
+ atio->sense_len = 0;
+ atio->tag_action = 1; /* XXX */
+ atio->tag_id = orbi->orb_lo;
+#if 0
+ atio->init_id = (orbi->fwdev->dst << 16) | (orbi->orb_hi & 0xffff);
+#else
+ atio->init_id = orbi->fwdev->dst;
+#endif
+ atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
+ bytes = (char *)&orb[5];
+ if (debug)
+ printf("%s: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ __FUNCTION__,
+ bytes[0], bytes[1], bytes[2], bytes[3], bytes[4],
+ bytes[5], bytes[6], bytes[7], bytes[8], bytes[9]);
+ switch (bytes[0] >> 5) {
+ case 0:
+ atio->cdb_len = 6;
+ break;
+ case 1:
+ case 2:
+ atio->cdb_len = 10;
+ break;
+ case 4:
+ atio->cdb_len = 16;
+ break;
+ case 5:
+ atio->cdb_len = 12;
+ break;
+ case 3:
+ default:
+ /* Only copy the opcode. */
+ atio->cdb_len = 1;
+ printf("Reserved or VU command code type encountered\n");
+ break;
+ }
+
+ memcpy(atio->cdb_io.cdb_bytes, bytes, atio->cdb_len);
+
+ atio->ccb_h.status |= CAM_CDB_RECVD;
+
+ /* next ORB */
+ if ((orb[0] & (1<<31)) == 0) {
+ if (debug)
+ printf("%s: fetch next orb\n", __FUNCTION__);
+ orbi->status.src = SRC_NEXT_EXISTS;
+ sbp_targ_fetch_orb(orbi->sc, orbi->fwdev,
+ orb[0], orb[1], orbi->lstate, FETCH_CMD);
+ } else {
+ orbi->status.src = SRC_NO_NEXT;
+ orbi->lstate->flags &= ~LINK_ACTIVE;
+ }
+
+ orbi->data_hi = orb[2];
+ orbi->data_lo = orb[3];
+ orbi->orb4 = *orb4;
+
+ xpt_done((union ccb*)atio);
+done0:
+ fw_xfer_free(xfer);
+ return;
+}
+
+static void
+sbp_targ_mgm_handler(struct fw_xfer *xfer)
+{
+ struct sbp_targ_lstate *lstate;
+ struct fw_pkt *fp;
+ u_int32_t *orb;
+ struct morb4 *orb4;
+ struct orb_info *orbi;
+ int i;
+
+ orbi = (struct orb_info *)xfer->sc;
+ if (xfer->resp != 0) {
+ printf("%s: xfer->resp != 0\n", __FUNCTION__);
+ orbi->status.resp = SBP_TRANS_FAIL;
+ orbi->status.status = htonl(OBJ_ORB | SBE_TIMEOUT /*XXX*/);
+ orbi->status.dead = 1;
+ orbi->status.len = 1;
+ sbp_targ_abort(STAILQ_NEXT(orbi, link));
+
+ sbp_targ_status_FIFO(orbi,
+ orbi->lstate->fifo_hi, orbi->lstate->fifo_lo, /*dequeue*/0);
+ fw_xfer_free(xfer);
+ return;
+ }
+ fp = &xfer->recv.hdr;
+
+ orb = orbi->orb;
+ /* swap payload */
+ for (i = 0; i < 8; i ++) {
+ orb[i] = ntohl(orb[i]);
+ }
+ orb4 = (struct morb4 *)&orb[4];
+ if (debug)
+ printf("%s: %s\n", __FUNCTION__, orb_fun_name[orb4->fun]);
+
+ orbi->status.src = SRC_NO_NEXT;
+
+ switch (orb4->fun << 16) {
+ case ORB_FUN_LGI:
+ {
+
+ if (orb4->id >= MAX_LUN || orbi->sc->lstate[orb4->id] == NULL) {
+ /* error */
+ orbi->status.dead = 1;
+ orbi->status.status = STATUS_ACCESS_DENY;
+ orbi->status.len = 1;
+ sbp_targ_status_FIFO(orbi, orb[6], orb[7],
+ /*dequeue*/0);
+ break;
+ }
+ /* XXX check exclusive login */
+ lstate = orbi->sc->lstate[orb4->id];
+ lstate->fifo_hi = orb[6];
+ lstate->fifo_lo = orb[7];
+ lstate->login_id = 0; /* XXX random number? */
+ lstate->lun = orb4->id;
+ lstate->loginres.len = htons(sizeof(u_int32_t) * 4);
+ lstate->loginres.id = htons(lstate->login_id);
+ lstate->loginres.cmd_hi = htons(SBP_TARG_BIND_HI);
+ lstate->loginres.cmd_lo = htonl(SBP_TARG_BIND_LO(orb4->id));
+ lstate->loginres.recon_hold = htons(0); /* XXX */
+ fwmem_write_block(orbi->fwdev, NULL, /*spd*/2, orb[2], orb[3],
+ sizeof(struct sbp_login_res), (void *)&lstate->loginres,
+ fw_asy_callback_free);
+ break;
+ }
+ case ORB_FUN_RCN:
+ orbi->status.dead = 1;
+ orbi->status.status = STATUS_ACCESS_DENY;
+ break;
+ default:
+ printf("%s: %s not implemented yet\n",
+ __FUNCTION__, orb_fun_name[orb4->fun]);
+ break;
+ }
+ orbi->status.len = 1;
+ sbp_targ_status_FIFO(orbi, orb[6], orb[7], /*dequeue*/0);
+ fw_xfer_free(xfer);
+ return;
+}
+
+static void
+sbp_targ_pointer_handler(struct fw_xfer *xfer)
+{
+ struct orb_info *orbi;
+ u_int32_t orb0, orb1;
+
+ orbi = (struct orb_info *)xfer->sc;
+ if (xfer->resp != 0) {
+ printf("%s: xfer->resp != 0\n", __FUNCTION__);
+ goto done;
+ }
+
+ orb0 = ntohl(orbi->orb[0]);
+ orb1 = ntohl(orbi->orb[1]);
+ if ((orb0 & (1 << 31)) != 0) {
+ printf("%s: invalid pointer\n", __FUNCTION__);
+ goto done;
+ }
+ sbp_targ_fetch_orb(orbi->lstate->sc, orbi->fwdev,
+ (u_int16_t)orb0, orb1, orbi->lstate, FETCH_CMD);
+done:
+ free(orbi, M_SBP_TARG);
+ fw_xfer_free(xfer);
+ return;
+}
+
+static void
+sbp_targ_fetch_orb(struct sbp_targ_softc *sc, struct fw_device *fwdev,
+ u_int16_t orb_hi, u_int32_t orb_lo, struct sbp_targ_lstate *lstate,
+ int mode)
+{
+ struct orb_info *orbi;
+
+ if (debug)
+ printf("%s: fetch orb %04x:%08x\n", __FUNCTION__, orb_hi, orb_lo);
+ orbi = malloc(sizeof(struct orb_info), M_SBP_TARG, M_NOWAIT | M_ZERO);
+ if (orbi == NULL) {
+ printf("%s: malloc failed\n", __FUNCTION__);
+ return;
+ }
+ orbi->sc = sc;
+ orbi->fwdev = fwdev;
+ orbi->lstate = lstate;
+ orbi->orb_hi = orb_hi;
+ orbi->orb_lo = orb_lo;
+ orbi->status.orb_hi = htons(orb_hi);
+ orbi->status.orb_lo = htonl(orb_lo);
+
+ switch (mode) {
+ case FETCH_MGM:
+ fwmem_read_block(fwdev, (void *)orbi, /*spd*/2, orb_hi, orb_lo,
+ sizeof(u_int32_t) * 8, &orbi->orb[0],
+ sbp_targ_mgm_handler);
+ break;
+ case FETCH_CMD:
+ orbi->state = ORBI_STATUS_FETCH;
+ lstate->last_hi = orb_hi;
+ lstate->last_lo = orb_lo;
+ lstate->flags |= LINK_ACTIVE;
+ /* dequeue */
+ orbi->atio = (struct ccb_accept_tio *)
+ SLIST_FIRST(&lstate->accept_tios);
+ if (orbi->atio == NULL) {
+ printf("%s: no free atio\n", __FUNCTION__);
+ lstate->flags |= ATIO_STARVED;
+ lstate->fwdev = fwdev;
+ break;
+ }
+ SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
+ fwmem_read_block(fwdev, (void *)orbi, /*spd*/2, orb_hi, orb_lo,
+ sizeof(u_int32_t) * 8, &orbi->orb[0],
+ sbp_targ_cmd_handler);
+ STAILQ_INSERT_TAIL(&lstate->orbs, orbi, link);
+ break;
+ case FETCH_POINTER:
+ orbi->state = ORBI_STATUS_POINTER;
+ fwmem_read_block(fwdev, (void *)orbi, /*spd*/2, orb_hi, orb_lo,
+ sizeof(u_int32_t) * 2, &orbi->orb[0],
+ sbp_targ_pointer_handler);
+ break;
+ default:
+ printf("%s: invalid mode %d\n", __FUNCTION__, mode);
+ }
+}
+
+static void
+sbp_targ_resp_callback(struct fw_xfer *xfer)
+{
+ struct sbp_targ_softc *sc;
+ int s;
+
+ if (debug)
+ printf("%s: xfer=%p\n", __FUNCTION__, xfer);
+ sc = (struct sbp_targ_softc *)xfer->sc;
+ fw_xfer_unload(xfer);
+ xfer->recv.pay_len = SBP_TARG_RECV_LEN;
+ xfer->act.hand = sbp_targ_recv;
+ s = splfw();
+ STAILQ_INSERT_TAIL(&sc->fwb.xferlist, xfer, link);
+ splx(s);
+}
+
+static int
+sbp_targ_cmd(struct fw_xfer *xfer, struct fw_device *fwdev, int lun, int reg)
+{
+ struct sbp_targ_lstate *lstate;
+ struct sbp_targ_softc *sc;
+ int rtcode = 0;
+
+ if (lun < 0 || lun >= MAX_LUN)
+ return(RESP_ADDRESS_ERROR);
+
+ sc = (struct sbp_targ_softc *)xfer->sc;
+ lstate = sc->lstate[lun];
+ if (lstate == NULL)
+ return(RESP_ADDRESS_ERROR);
+
+ /* XXX check logined? */
+ switch (reg) {
+ case 0x08: /* ORB_POINTER */
+ if (debug)
+ printf("%s: ORB_POINTER\n", __FUNCTION__);
+ sbp_targ_fetch_orb(lstate->sc, fwdev,
+ ntohl(xfer->recv.payload[0]),
+ ntohl(xfer->recv.payload[1]),
+ lstate, FETCH_CMD);
+ break;
+ case 0x04: /* AGENT_RESET */
+ if (debug)
+ printf("%s: AGENT RESET\n", __FUNCTION__);
+ lstate->last_hi = 0xffff;
+ lstate->last_lo = 0xffffffff;
+ sbp_targ_abort(STAILQ_FIRST(&lstate->orbs));
+ break;
+ case 0x10: /* DOORBELL */
+ if (debug)
+ printf("%s: DOORBELL\n", __FUNCTION__);
+ if (lstate->last_hi == 0xffff &&
+ lstate->last_lo == 0xffffffff) {
+ printf("%s: no previous pointer(DOORBELL)\n",
+ __FUNCTION__);
+ break;
+ }
+ if ((lstate->flags & LINK_ACTIVE) != 0) {
+ if (debug)
+ printf("link active (DOORBELL)\n");
+ break;
+ }
+ lstate->flags |= LINK_ACTIVE;
+ sbp_targ_fetch_orb(lstate->sc, fwdev,
+ lstate->last_hi, lstate->last_lo,
+ lstate, FETCH_POINTER);
+ break;
+ case 0x00: /* AGENT_STATE */
+ printf("%s: AGENT_STATE (ignore)\n", __FUNCTION__);
+ break;
+ case 0x14: /* UNSOLICITED_STATE_ENABLE */
+ printf("%s: UNSOLICITED_STATE_ENABLE (ignore)\n", __FUNCTION__);
+ break;
+ default:
+ printf("%s: invalid register %d\n", __FUNCTION__, reg);
+ rtcode = RESP_ADDRESS_ERROR;
+ }
+
+ return (rtcode);
+}
+
+static int
+sbp_targ_mgm(struct fw_xfer *xfer, struct fw_device *fwdev)
+{
+ struct sbp_targ_softc *sc;
+ struct fw_pkt *fp;
+
+ sc = (struct sbp_targ_softc *)xfer->sc;
+
+ fp = &xfer->recv.hdr;
+ if (fp->mode.wreqb.tcode != FWTCODE_WREQB){
+ printf("%s: tcode = %d\n", __FUNCTION__, fp->mode.wreqb.tcode);
+ return(RESP_TYPE_ERROR);
+ }
+
+ sbp_targ_fetch_orb(sc, fwdev,
+ ntohl(xfer->recv.payload[0]),
+ ntohl(xfer->recv.payload[1]),
+ NULL, FETCH_MGM);
+
+ return(0);
+}
+
+
+static void
+sbp_targ_recv(struct fw_xfer *xfer)
+{
+ struct fw_pkt *fp, *sfp;
+ struct fw_device *fwdev;
+ u_int32_t lo;
+ int s, rtcode;
+ struct sbp_targ_softc *sc;
+
+ s = splfw();
+ sc = (struct sbp_targ_softc *)xfer->sc;
+ fp = &xfer->recv.hdr;
+ fwdev = fw_noderesolve_nodeid(sc->fd.fc, fp->mode.wreqb.src & 0x3f);
+ if (fwdev == NULL) {
+ printf("%s: cannot resolve nodeid=%d\n",
+ __FUNCTION__, fp->mode.wreqb.src & 0x3f);
+ rtcode = RESP_TYPE_ERROR; /* XXX */
+ goto done;
+ }
+ lo = fp->mode.wreqb.dest_lo;
+ if (lo == SBP_TARG_BIND_LO(-1))
+ rtcode = sbp_targ_mgm(xfer, fwdev);
+ else if (lo >= SBP_TARG_BIND_LO(0))
+ rtcode = sbp_targ_cmd(xfer, fwdev, SBP_TARG_LUN(lo), lo % 0x20);
+ else
+ rtcode = RESP_ADDRESS_ERROR;
+
+done:
+ if (rtcode != 0)
+ printf("%s: rtcode = %d\n", __FUNCTION__, rtcode);
+ sfp = &xfer->send.hdr;
+ xfer->send.spd = 2; /* XXX */
+ xfer->act.hand = sbp_targ_resp_callback;
+ xfer->retry_req = fw_asybusy;
+ sfp->mode.wres.dst = fp->mode.wreqb.src;
+ sfp->mode.wres.tlrt = fp->mode.wreqb.tlrt;
+ sfp->mode.wres.tcode = FWTCODE_WRES;
+ sfp->mode.wres.rtcode = rtcode;
+ sfp->mode.wres.pri = 0;
+
+ fw_asyreq(xfer->fc, -1, xfer);
+ splx(s);
+}
+
+static int
+sbp_targ_attach(device_t dev)
+{
+ struct sbp_targ_softc *sc;
+ struct cam_devq *devq;
+ struct fw_xfer *xfer;
+ int i;
+
+ sc = (struct sbp_targ_softc *) device_get_softc(dev);
+ bzero((void *)sc, sizeof(struct sbp_targ_softc));
+
+ sc->fd.fc = device_get_ivars(dev);
+ sc->fd.dev = dev;
+ sc->fd.post_explore = NULL;
+ sc->fd.post_busreset = (void *) sbp_targ_post_busreset;
+
+ devq = cam_simq_alloc(/*maxopenings*/1);
+ if (devq == NULL)
+ return (ENXIO);
+
+ sc->sim = cam_sim_alloc(sbp_targ_action, sbp_targ_poll,
+ "sbp_targ", sc, device_get_unit(dev),
+ /*untagged*/ 1, /*tagged*/ 1, devq);
+ if (sc->sim == NULL) {
+ cam_simq_free(devq);
+ return (ENXIO);
+ }
+
+ if (xpt_bus_register(sc->sim, /*bus*/0) != CAM_SUCCESS)
+ goto fail;
+
+ if (xpt_create_path(&sc->path, /*periph*/ NULL, cam_sim_path(sc->sim),
+ CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ xpt_bus_deregister(cam_sim_path(sc->sim));
+ goto fail;
+ }
+
+ sc->fwb.start = SBP_TARG_BIND_START;
+ sc->fwb.end = SBP_TARG_BIND_END;
+ sc->fwb.act_type = FWACT_XFER;
+
+ /* pre-allocate xfer */
+ STAILQ_INIT(&sc->fwb.xferlist);
+ for (i = 0; i < MAX_LUN /* XXX */; i ++) {
+ xfer = fw_xfer_alloc_buf(M_SBP_TARG,
+ /* send */ 0,
+ /* recv */ SBP_TARG_RECV_LEN);
+ xfer->act.hand = sbp_targ_recv;
+ xfer->fc = sc->fd.fc;
+ xfer->sc = (caddr_t)sc;
+ STAILQ_INSERT_TAIL(&sc->fwb.xferlist, xfer, link);
+ }
+ fw_bindadd(sc->fd.fc, &sc->fwb);
+ return 0;
+
+fail:
+ cam_sim_free(sc->sim, /*free_devq*/TRUE);
+ return (ENXIO);
+}
+
+static int
+sbp_targ_detach(device_t dev)
+{
+ struct sbp_targ_softc *sc;
+ struct sbp_targ_lstate *lstate;
+ struct fw_xfer *xfer, *next;
+ int i;
+
+ sc = (struct sbp_targ_softc *)device_get_softc(dev);
+ sc->fd.post_busreset = NULL;
+
+ xpt_free_path(sc->path);
+ xpt_bus_deregister(cam_sim_path(sc->sim));
+ cam_sim_free(sc->sim, /*free_devq*/TRUE);
+
+ for (i = 0; i < MAX_LUN; i ++) {
+ lstate = sc->lstate[i];
+ if (lstate != NULL) {
+ xpt_free_path(lstate->path);
+ free(lstate, M_SBP_TARG);
+ }
+ }
+ if (sc->black_hole != NULL) {
+ xpt_free_path(sc->black_hole->path);
+ free(sc->black_hole, M_SBP_TARG);
+ }
+
+ for (xfer = STAILQ_FIRST(&sc->fwb.xferlist);
+ xfer != NULL; xfer = next) {
+ next = STAILQ_NEXT(xfer, link);
+ fw_xfer_free_buf(xfer);
+ }
+ STAILQ_INIT(&sc->fwb.xferlist);
+ fw_bindremove(sc->fd.fc, &sc->fwb);
+
+ return 0;
+}
+
+static devclass_t sbp_targ_devclass;
+
+static device_method_t sbp_targ_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_identify, sbp_targ_identify),
+ DEVMETHOD(device_probe, sbp_targ_probe),
+ DEVMETHOD(device_attach, sbp_targ_attach),
+ DEVMETHOD(device_detach, sbp_targ_detach),
+ { 0, 0 }
+};
+
+static driver_t sbp_targ_driver = {
+ "sbp_targ",
+ sbp_targ_methods,
+ sizeof(struct sbp_targ_softc),
+};
+
+DRIVER_MODULE(sbp_targ, firewire, sbp_targ_driver, sbp_targ_devclass, 0, 0);
+MODULE_VERSION(sbp_targ, 1);
+MODULE_DEPEND(sbp_targ, firewire, 1, 1, 1);
+MODULE_DEPEND(sbp_targ, cam, 1, 1, 1);
diff --git a/sys/modules/firewire/Makefile b/sys/modules/firewire/Makefile
index 4bce1a6..a8ea55c 100644
--- a/sys/modules/firewire/Makefile
+++ b/sys/modules/firewire/Makefile
@@ -3,6 +3,7 @@
SUBDIR =
SUBDIR += firewire
SUBDIR += sbp
+SUBDIR += sbp_targ
SUBDIR += fwe
.include <bsd.subdir.mk>
diff --git a/sys/modules/firewire/sbp_targ/Makefile b/sys/modules/firewire/sbp_targ/Makefile
new file mode 100644
index 0000000..7a105b4
--- /dev/null
+++ b/sys/modules/firewire/sbp_targ/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+# Makefile for the SBP-II Target mode
+
+.PATH: ${.CURDIR}/../../../dev/firewire
+
+KMOD = sbp_targ
+SRCS = bus_if.h device_if.h \
+ opt_cam.h opt_scsi.h \
+ sbp_targ.c sbp.h \
+ firewire.h firewirereg.h \
+ iec13213.h
+
+#CFLAGS += -g
+.include <bsd.kmod.mk>
+
OpenPOWER on IntegriCloud