#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sbp.c,v 1.43 2025/07/11 22:19:53 andvar Exp $");
#include <sys/param.h>
#include <sys/device.h>
#include <sys/errno.h>
#include <sys/buf.h>
#include <sys/callout.h>
#include <sys/condvar.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/bus.h>
#include <dev/scsipi/scsi_spc.h>
#include <dev/scsipi/scsi_all.h>
#include <dev/scsipi/scsipi_all.h>
#include <dev/scsipi/scsiconf.h>
#include <dev/scsipi/scsipiconf.h>
#include <dev/ieee1394/firewire.h>
#include <dev/ieee1394/firewirereg.h>
#include <dev/ieee1394/fwdma.h>
#include <dev/ieee1394/iec13213.h>
#include <dev/ieee1394/sbp.h>
#include "locators.h"
#define SBP_FWDEV_ALIVE(fwdev) (((fwdev)->status == FWDEVATTACHED) \
&& crom_has_specver((fwdev)->csrrom, CSRVAL_ANSIT10, CSRVAL_T10SBP2))
#define SBP_NUM_TARGETS 8
#define SBP_NUM_LUNS 64
#define SBP_MAXPHYS MIN(MAXPHYS, (512*1024) )
#define SBP_DMA_SIZE PAGE_SIZE
#define SBP_LOGIN_SIZE sizeof(struct sbp_login_res)
#define SBP_QUEUE_LEN ((SBP_DMA_SIZE - SBP_LOGIN_SIZE) / sizeof(struct sbp_ocb))
#define SBP_NUM_OCB (SBP_QUEUE_LEN * SBP_NUM_TARGETS)
#define SBP_BIND_HI 0x1
#define SBP_DEV2ADDR(u, l) \
(((uint64_t)SBP_BIND_HI << 32) |\
(((u) & 0x3fff) << 10) |\
(((l) & 0xff) << 2))
#define SBP_ADDR2UNIT(a) (((a) >> 10) & 0x3fff)
#define SBP_ADDR2LUN(a) (((a) >> 2) & 0xff)
#define SBP_INITIATOR 7
static const char *orb_fun_name[] = {
ORB_FUN_NAMES
};
static int debug = 0;
static int auto_login = 1;
static int max_speed = -1;
static int sbp_cold = 1;
static int ex_login = 1;
static int login_delay = 1000;
static int scan_delay = 500;
static int use_doorbell = 0;
static int sbp_tags = 0;
static int sysctl_sbp_verify(SYSCTLFN_PROTO, int lower, int upper);
static int sysctl_sbp_verify_max_speed(SYSCTLFN_PROTO);
static int sysctl_sbp_verify_tags(SYSCTLFN_PROTO);
SYSCTL_SETUP(sysctl_sbp, "sysctl sbp(4) subtree setup")
{
int rc, sbp_node_num;
const struct sysctlnode *node;
if ((rc = sysctl_createv(clog, 0, NULL, &node,
CTLFLAG_PERMANENT, CTLTYPE_NODE, "sbp",
SYSCTL_DESCR("sbp controls"), NULL, 0, NULL,
0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0)
goto err;
sbp_node_num = node->sysctl_num;
if ((rc = sysctl_createv(clog, 0, NULL, &node,
CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
"auto_login", SYSCTL_DESCR("SBP perform login automatically"),
NULL, 0, &auto_login,
0, CTL_HW, sbp_node_num, CTL_CREATE, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(clog, 0, NULL, &node,
CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
"max_speed", SYSCTL_DESCR("SBP transfer max speed"),
sysctl_sbp_verify_max_speed, 0, &max_speed,
0, CTL_HW, sbp_node_num, CTL_CREATE, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(clog, 0, NULL, &node,
CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
"exclusive_login", SYSCTL_DESCR("SBP enable exclusive login"),
NULL, 0, &ex_login,
0, CTL_HW, sbp_node_num, CTL_CREATE, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(clog, 0, NULL, &node,
CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
"login_delay", SYSCTL_DESCR("SBP login delay in msec"),
NULL, 0, &login_delay,
0, CTL_HW, sbp_node_num, CTL_CREATE, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(clog, 0, NULL, &node,
CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
"scan_delay", SYSCTL_DESCR("SBP scan delay in msec"),
NULL, 0, &scan_delay,
0, CTL_HW, sbp_node_num, CTL_CREATE, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(clog, 0, NULL, &node,
CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
"use_doorbell", SYSCTL_DESCR("SBP use doorbell request"),
NULL, 0, &use_doorbell,
0, CTL_HW, sbp_node_num, CTL_CREATE, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(clog, 0, NULL, &node,
CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
"tags", SYSCTL_DESCR("SBP tagged queuing support"),
sysctl_sbp_verify_tags, 0, &sbp_tags,
0, CTL_HW, sbp_node_num, CTL_CREATE, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(clog, 0, NULL, &node,
CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
"sbp_debug", SYSCTL_DESCR("SBP debug flag"),
NULL, 0, &debug,
0, CTL_HW, sbp_node_num, CTL_CREATE, CTL_EOL)) != 0)
goto err;
return;
err:
aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
}
static int
sysctl_sbp_verify(SYSCTLFN_ARGS, int lower, int upper)
{
int error, t;
struct sysctlnode node;
node = *rnode;
t = *(int*)rnode->sysctl_data;
node.sysctl_data = &t;
error = sysctl_lookup(SYSCTLFN_CALL(&node));
if (error || newp == NULL)
return error;
if (t < lower || t > upper)
return EINVAL;
*(int*)rnode->sysctl_data = t;
return 0;
}
static int
sysctl_sbp_verify_max_speed(SYSCTLFN_ARGS)
{
return sysctl_sbp_verify(SYSCTLFN_CALL(rnode), 0, FWSPD_S400);
}
static int
sysctl_sbp_verify_tags(SYSCTLFN_ARGS)
{
return sysctl_sbp_verify(SYSCTLFN_CALL(rnode), -1, 1);
}
#define NEED_RESPONSE 0
#define SBP_SEG_MAX rounddown(0xffff, PAGE_SIZE)
#ifdef __sparc64__
#define SBP_IND_MAX howmany(SBP_MAXPHYS, SBP_SEG_MAX)
#else
#define SBP_IND_MAX howmany(SBP_MAXPHYS, PAGE_SIZE)
#endif
struct sbp_ocb {
uint32_t orb[8];
#define IND_PTR_OFFSET (sizeof(uint32_t) * 8)
struct ind_ptr ind_ptr[SBP_IND_MAX];
struct scsipi_xfer *xs;
struct sbp_dev *sdev;
uint16_t index;
uint16_t flags;
bus_dmamap_t dmamap;
bus_addr_t bus_addr;
STAILQ_ENTRY(sbp_ocb) ocb;
};
#define SBP_ORB_DMA_SYNC(dma, i, op) \
bus_dmamap_sync((dma).dma_tag, (dma).dma_map, \
sizeof(struct sbp_ocb) * (i), \
sizeof(ocb->orb) + sizeof(ocb->ind_ptr), (op));
#define OCB_ACT_MGM 0
#define OCB_ACT_CMD 1
#define OCB_MATCH(o,s) ((o)->bus_addr == ntohl((s)->orb_lo))
struct sbp_dev{
#define SBP_DEV_RESET 0
#define SBP_DEV_LOGIN 1
#if 0
#define SBP_DEV_RECONN 2
#endif
#define SBP_DEV_TOATTACH 3
#define SBP_DEV_PROBE 4
#define SBP_DEV_ATTACHED 5
#define SBP_DEV_DEAD 6
#define SBP_DEV_RETRY 7
uint8_t status:4,
timeout:4;
uint8_t type;
uint16_t lun_id;
uint16_t freeze;
#define ORB_LINK_DEAD (1 << 0)
#define VALID_LUN (1 << 1)
#define ORB_POINTER_ACTIVE (1 << 2)
#define ORB_POINTER_NEED (1 << 3)
#define ORB_DOORBELL_ACTIVE (1 << 4)
#define ORB_DOORBELL_NEED (1 << 5)
#define ORB_SHORTAGE (1 << 6)
uint16_t flags;
struct scsipi_periph *periph;
struct sbp_target *target;
struct fwdma_alloc dma;
struct sbp_login_res *login;
struct callout login_callout;
struct sbp_ocb *ocb;
STAILQ_HEAD(, sbp_ocb) ocbs;
STAILQ_HEAD(, sbp_ocb) free_ocbs;
struct sbp_ocb *last_ocb;
char vendor[32];
char product[32];
char revision[10];
char bustgtlun[32];
};
struct sbp_target {
int target_id;
int num_lun;
struct sbp_dev **luns;
struct sbp_softc *sbp;
struct fw_device *fwdev;
uint32_t mgm_hi, mgm_lo;
struct sbp_ocb *mgm_ocb_cur;
STAILQ_HEAD(, sbp_ocb) mgm_ocb_queue;
struct callout mgm_ocb_timeout;
STAILQ_HEAD(, fw_xfer) xferlist;
int n_xfer;
};
struct sbp_softc {
struct firewire_dev_comm sc_fd;
struct scsipi_adapter sc_adapter;
struct scsipi_channel sc_channel;
device_t sc_bus;
struct lwp *sc_lwp;
struct sbp_target sc_target;
struct fw_bind sc_fwb;
bus_dma_tag_t sc_dmat;
struct timeval sc_last_busreset;
int sc_flags;
kmutex_t sc_mtx;
kcondvar_t sc_cv;
};
MALLOC_DEFINE(M_SBP, "sbp", "SBP-II/IEEE1394");
MALLOC_DECLARE(M_SBP);
static int sbpmatch(device_t, cfdata_t, void *);
static void sbpattach(device_t, device_t, void *);
static int sbpdetach(device_t, int);
static void sbp_scsipi_request(struct scsipi_channel *, scsipi_adapter_req_t,
void *);
static void sbp_minphys(struct buf *);
static void sbp_show_sdev_info(struct sbp_dev *);
static void sbp_alloc_lun(struct sbp_target *);
static struct sbp_target *sbp_alloc_target(struct sbp_softc *,
struct fw_device *);
static void sbp_probe_lun(struct sbp_dev *);
static void sbp_login_callout(void *);
static void sbp_login(struct sbp_dev *);
static void sbp_probe_target(void *);
static void sbp_post_busreset(void *);
static void sbp_post_explore(void *);
#if NEED_RESPONSE
static void sbp_loginres_callback(struct fw_xfer *);
#endif
static inline void sbp_xfer_free(struct fw_xfer *);
static void sbp_reset_start_callback(struct fw_xfer *);
static void sbp_reset_start(struct sbp_dev *);
static void sbp_mgm_callback(struct fw_xfer *);
static void sbp_scsipi_scan_target(void *);
static inline void sbp_scan_dev(struct sbp_dev *);
static void sbp_do_attach(struct fw_xfer *);
static void sbp_agent_reset_callback(struct fw_xfer *);
static void sbp_agent_reset(struct sbp_dev *);
static void sbp_busy_timeout_callback(struct fw_xfer *);
static void sbp_busy_timeout(struct sbp_dev *);
static void sbp_orb_pointer_callback(struct fw_xfer *);
static void sbp_orb_pointer(struct sbp_dev *, struct sbp_ocb *);
static void sbp_doorbell_callback(struct fw_xfer *);
static void sbp_doorbell(struct sbp_dev *);
static struct fw_xfer *sbp_write_cmd(struct sbp_dev *, int, int);
static void sbp_mgm_orb(struct sbp_dev *, int, struct sbp_ocb *);
static void sbp_print_scsi_cmd(struct sbp_ocb *);
static void sbp_scsi_status(struct sbp_status *, struct sbp_ocb *);
static void sbp_fix_inq_data(struct sbp_ocb *);
static void sbp_recv(struct fw_xfer *);
static int sbp_logout_all(struct sbp_softc *);
static void sbp_free_sdev(struct sbp_dev *);
static void sbp_free_target(struct sbp_target *);
static void sbp_scsipi_detach_sdev(struct sbp_dev *);
static void sbp_scsipi_detach_target(struct sbp_target *);
static void sbp_target_reset(struct sbp_dev *, int);
static void sbp_mgm_timeout(void *);
static void sbp_timeout(void *);
static void sbp_action1(struct sbp_softc *, struct scsipi_xfer *);
static void sbp_execute_ocb(struct sbp_ocb *, bus_dma_segment_t *, int);
static struct sbp_ocb *sbp_dequeue_ocb(struct sbp_dev *, struct sbp_status *);
static struct sbp_ocb *sbp_enqueue_ocb(struct sbp_dev *, struct sbp_ocb *);
static struct sbp_ocb *sbp_get_ocb(struct sbp_dev *);
static void sbp_free_ocb(struct sbp_dev *, struct sbp_ocb *);
static void sbp_abort_ocb(struct sbp_ocb *, int);
static void sbp_abort_all_ocbs(struct sbp_dev *, int);
static const char *orb_status0[] = {
"No additional information to report",
"Request type not supported",
"Speed not supported",
"Page size not supported",
"Access denied",
"Logical unit not supported",
"Maximum payload too small",
"Reserved for future standardization",
"Resources unavailable",
"Function rejected",
"Login ID not recognized",
"Dummy ORB completed",
"Request aborted",
"Unspecified error"
#define MAX_ORB_STATUS0 0xd
};
static const char *orb_status1_object[] = {
"Operation request block (ORB)",
"Data buffer",
"Page table",
"Unable to specify"
};
static const char *orb_status1_serial_bus_error[] = {
"Missing acknowledge",
"Reserved; not to be used",
"Time-out error",
"Reserved; not to be used",
"Busy retry limit exceeded(X)",
"Busy retry limit exceeded(A)",
"Busy retry limit exceeded(B)",
"Reserved for future standardization",
"Reserved for future standardization",
"Reserved for future standardization",
"Reserved for future standardization",
"Tardy retry limit exceeded",
"Conflict error",
"Data error",
"Type error",
"Address error"
};
CFATTACH_DECL_NEW(sbp, sizeof(struct sbp_softc),
sbpmatch, sbpattach, sbpdetach, NULL);
int
sbpmatch(device_t parent, cfdata_t cf, void *aux)
{
struct fw_attach_args *fwa = aux;
if (strcmp(fwa->name, "sbp") == 0)
return 1;
return 0;
}
static void
sbpattach(device_t parent, device_t self, void *aux)
{
struct sbp_softc *sc = device_private(self);
struct fw_attach_args *fwa = (struct fw_attach_args *)aux;
struct firewire_comm *fc;
struct scsipi_adapter *sc_adapter = &sc->sc_adapter;
struct scsipi_channel *sc_channel = &sc->sc_channel;
struct sbp_target *target = &sc->sc_target;
int dv_unit;
aprint_naive("\n");
aprint_normal(": SBP-2/SCSI over IEEE1394\n");
sc->sc_fd.dev = self;
if (cold)
sbp_cold++;
sc->sc_fd.fc = fc = fwa->fc;
mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_VM);
cv_init(&sc->sc_cv, "sbp");
if (max_speed < 0)
max_speed = fc->speed;
sc->sc_dmat = fc->dmat;
sc->sc_target.fwdev = NULL;
sc->sc_target.luns = NULL;
mutex_init(&sc->sc_fwb.fwb_mtx, MUTEX_DEFAULT, IPL_VM);
STAILQ_INIT(&sc->sc_fwb.xferlist);
if (sbp_alloc_target(sc, fwa->fwdev) == NULL)
return;
sc_adapter->adapt_dev = sc->sc_fd.dev;
sc_adapter->adapt_nchannels = 1;
sc_adapter->adapt_max_periph = 1;
sc_adapter->adapt_request = sbp_scsipi_request;
sc_adapter->adapt_minphys = sbp_minphys;
sc_adapter->adapt_openings = 8;
sc_channel->chan_adapter = sc_adapter;
sc_channel->chan_bustype = &scsi_bustype;
sc_channel->chan_defquirks = PQUIRK_ONLYBIG;
sc_channel->chan_channel = 0;
sc_channel->chan_flags = SCSIPI_CHAN_CANGROW | SCSIPI_CHAN_NOSETTLE;
sc_channel->chan_ntargets = 1;
sc_channel->chan_nluns = target->num_lun;
sc_channel->chan_id = 1;
sc->sc_bus = config_found(sc->sc_fd.dev, sc_channel, scsiprint,
CFARGS_NONE);
if (sc->sc_bus == NULL) {
aprint_error_dev(self, "attach failed\n");
return;
}
dv_unit = device_unit(sc->sc_fd.dev);
sc->sc_fwb.start = SBP_DEV2ADDR(dv_unit, 0);
sc->sc_fwb.end = SBP_DEV2ADDR(dv_unit, -1);
fw_xferlist_add(&sc->sc_fwb.xferlist, M_SBP,
0, SBP_RECV_LEN, SBP_NUM_OCB / 2,
fc, (void *)sc, sbp_recv);
fw_bindadd(fc, &sc->sc_fwb);
sc->sc_fd.post_busreset = sbp_post_busreset;
sc->sc_fd.post_explore = sbp_post_explore;
if (fc->status != FWBUSNOTREADY) {
sbp_post_busreset((void *)sc);
sbp_post_explore((void *)sc);
}
}
static int
sbpdetach(device_t self, int flags)
{
struct sbp_softc *sc = device_private(self);
struct firewire_comm *fc = sc->sc_fd.fc;
sbp_scsipi_detach_target(&sc->sc_target);
if (sc->sc_target.fwdev && SBP_FWDEV_ALIVE(sc->sc_target.fwdev)) {
sbp_logout_all(sc);
mutex_enter(&sc->sc_mtx);
cv_timedwait_sig(&sc->sc_cv, &sc->sc_mtx, hz/2);
mutex_exit(&sc->sc_mtx);
}
sbp_free_target(&sc->sc_target);
fw_bindremove(fc, &sc->sc_fwb);
fw_xferlist_remove(&sc->sc_fwb.xferlist);
mutex_destroy(&sc->sc_fwb.fwb_mtx);
mutex_destroy(&sc->sc_mtx);
cv_destroy(&sc->sc_cv);
return 0;
}
static void
sbp_scsipi_request(struct scsipi_channel *channel, scsipi_adapter_req_t req,
void *arg)
{
struct sbp_softc *sc = device_private(channel->chan_adapter->adapt_dev);
struct scsipi_xfer *xs = arg;
int i;
SBP_DEBUG(1)
printf("Called sbp_scsipi_request\n");
END_DEBUG
switch (req) {
case ADAPTER_REQ_RUN_XFER:
SBP_DEBUG(1)
printf("Got req_run_xfer\n");
printf("xs control: 0x%08x, timeout: %d\n",
xs->xs_control, xs->timeout);
printf("opcode: 0x%02x\n", (int)xs->cmd->opcode);
for (i = 0; i < 15; i++)
printf("0x%02x ",(int)xs->cmd->bytes[i]);
printf("\n");
END_DEBUG
if (xs->xs_control & XS_CTL_RESET) {
SBP_DEBUG(1)
printf("XS_CTL_RESET not support\n");
END_DEBUG
break;
}
#define SBPSCSI_SBP2_MAX_CDB 12
if (xs->cmdlen > SBPSCSI_SBP2_MAX_CDB) {
SBP_DEBUG(0)
printf(
"sbp doesn't support cdb's larger than %d bytes\n",
SBPSCSI_SBP2_MAX_CDB);
END_DEBUG
xs->error = XS_DRIVER_STUFFUP;
scsipi_done(xs);
return;
}
sbp_action1(sc, xs);
break;
case ADAPTER_REQ_GROW_RESOURCES:
SBP_DEBUG(1)
printf("Got req_grow_resources\n");
END_DEBUG
break;
case ADAPTER_REQ_SET_XFER_MODE:
SBP_DEBUG(1)
printf("Got set xfer mode\n");
END_DEBUG
break;
default:
panic("Unknown request: %d\n", (int)req);
}
}
static void
sbp_minphys(struct buf *bp)
{
minphys(bp);
}
static void
sbp_show_sdev_info(struct sbp_dev *sdev)
{
struct fw_device *fwdev = sdev->target->fwdev;
struct sbp_softc *sc = sdev->target->sbp;
aprint_normal_dev(sc->sc_fd.dev,
"ordered:%d type:%d EUI:%08x%08x node:%d speed:%d maxrec:%d\n",
(sdev->type & 0x40) >> 6,
(sdev->type & 0x1f),
fwdev->eui.hi,
fwdev->eui.lo,
fwdev->dst,
fwdev->speed,
fwdev->maxrec);
aprint_normal_dev(sc->sc_fd.dev, "%s '%s' '%s' '%s'\n",
sdev->bustgtlun, sdev->vendor, sdev->product, sdev->revision);
}
static void
sbp_alloc_lun(struct sbp_target *target)
{
struct crom_context cc;
struct csrreg *reg;
struct sbp_dev *sdev, **newluns;
struct sbp_softc *sc;
int maxlun, lun, i;
sc = target->sbp;
crom_init_context(&cc, target->fwdev->csrrom);
maxlun = -1;
while (cc.depth >= 0) {
reg = crom_search_key(&cc, CROM_LUN);
if (reg == NULL)
break;
lun = reg->val & 0xffff;
SBP_DEBUG(0)
printf("target %d lun %d found\n", target->target_id, lun);
END_DEBUG
if (maxlun < lun)
maxlun = lun;
crom_next(&cc);
}
if (maxlun < 0)
aprint_normal_dev(sc->sc_fd.dev, "%d: no LUN found\n",
target->target_id);
maxlun++;
if (maxlun >= SBP_NUM_LUNS)
maxlun = SBP_NUM_LUNS;
for (lun = 0; lun < target->num_lun; lun++) {
sdev = target->luns[lun];
if (sdev == NULL)
continue;
sdev->flags &= ~VALID_LUN;
if (lun >= maxlun) {
sbp_scsipi_detach_sdev(sdev);
sbp_free_sdev(sdev);
target->luns[lun] = NULL;
}
}
if (maxlun != target->num_lun) {
newluns = (struct sbp_dev **) realloc(target->luns,
sizeof(struct sbp_dev *) * maxlun,
M_SBP, M_WAITOK | M_ZERO);
if (maxlun > target->num_lun) {
const int sbp_dev_p_sz = sizeof(struct sbp_dev *);
memset(&newluns[target->num_lun], 0,
sbp_dev_p_sz * (maxlun - target->num_lun));
}
target->luns = newluns;
target->num_lun = maxlun;
}
crom_init_context(&cc, target->fwdev->csrrom);
while (cc.depth >= 0) {
int new = 0;
reg = crom_search_key(&cc, CROM_LUN);
if (reg == NULL)
break;
lun = reg->val & 0xffff;
if (lun >= SBP_NUM_LUNS) {
aprint_error_dev(sc->sc_fd.dev, "too large lun %d\n",
lun);
goto next;
}
sdev = target->luns[lun];
if (sdev == NULL) {
sdev = malloc(sizeof(struct sbp_dev),
M_SBP, M_WAITOK | M_ZERO);
target->luns[lun] = sdev;
sdev->lun_id = lun;
sdev->target = target;
STAILQ_INIT(&sdev->ocbs);
callout_init(&sdev->login_callout, CALLOUT_MPSAFE);
callout_setfunc(&sdev->login_callout,
sbp_login_callout, sdev);
sdev->status = SBP_DEV_RESET;
new = 1;
snprintf(sdev->bustgtlun, 32, "%s:%d:%d",
device_xname(sc->sc_fd.dev),
sdev->target->target_id,
sdev->lun_id);
if (!sc->sc_lwp)
if (kthread_create(
PRI_NONE, KTHREAD_MPSAFE, NULL,
sbp_scsipi_scan_target, &sc->sc_target,
&sc->sc_lwp,
"sbp%d_attach", device_unit(sc->sc_fd.dev)))
aprint_error_dev(sc->sc_fd.dev,
"unable to create thread");
}
sdev->flags |= VALID_LUN;
sdev->type = (reg->val & 0xff0000) >> 16;
if (new == 0)
goto next;
fwdma_alloc_setup(sc->sc_fd.dev, sc->sc_dmat, SBP_DMA_SIZE,
&sdev->dma, sizeof(uint32_t), BUS_DMA_NOWAIT);
if (sdev->dma.v_addr == NULL) {
free(sdev, M_SBP);
target->luns[lun] = NULL;
goto next;
}
sdev->ocb = (struct sbp_ocb *)sdev->dma.v_addr;
sdev->login = (struct sbp_login_res *)&sdev->ocb[SBP_QUEUE_LEN];
memset((char *)sdev->ocb, 0,
sizeof(struct sbp_ocb) * SBP_QUEUE_LEN);
STAILQ_INIT(&sdev->free_ocbs);
for (i = 0; i < SBP_QUEUE_LEN; i++) {
struct sbp_ocb *ocb = &sdev->ocb[i];
ocb->index = i;
ocb->bus_addr =
sdev->dma.bus_addr + sizeof(struct sbp_ocb) * i;
if (bus_dmamap_create(sc->sc_dmat, 0x100000,
SBP_IND_MAX, SBP_SEG_MAX, 0, 0, &ocb->dmamap)) {
aprint_error_dev(sc->sc_fd.dev,
"cannot create dmamap %d\n", i);
goto next;
}
sbp_free_ocb(sdev, ocb);
}
next:
crom_next(&cc);
}
for (lun = 0; lun < target->num_lun; lun++) {
sdev = target->luns[lun];
if (sdev != NULL && (sdev->flags & VALID_LUN) == 0) {
sbp_scsipi_detach_sdev(sdev);
sbp_free_sdev(sdev);
target->luns[lun] = NULL;
}
}
}
static struct sbp_target *
sbp_alloc_target(struct sbp_softc *sc, struct fw_device *fwdev)
{
struct sbp_target *target;
struct crom_context cc;
struct csrreg *reg;
SBP_DEBUG(1)
printf("sbp_alloc_target\n");
END_DEBUG
target = &sc->sc_target;
target->sbp = sc;
target->fwdev = fwdev;
target->target_id = 0;
target->mgm_ocb_cur = NULL;
SBP_DEBUG(1)
printf("target: mgm_port: %x\n", target->mgm_lo);
END_DEBUG
STAILQ_INIT(&target->xferlist);
target->n_xfer = 0;
STAILQ_INIT(&target->mgm_ocb_queue);
callout_init(&target->mgm_ocb_timeout, CALLOUT_MPSAFE);
target->luns = NULL;
target->num_lun = 0;
crom_init_context(&cc, target->fwdev->csrrom);
reg = crom_search_key(&cc, CROM_MGM);
if (reg == NULL || reg->val == 0) {
aprint_error_dev(sc->sc_fd.dev, "NULL management address\n");
target->fwdev = NULL;
return NULL;
}
target->mgm_hi = 0xffff;
target->mgm_lo = 0xf0000000 | (reg->val << 2);
return target;
}
static void
sbp_probe_lun(struct sbp_dev *sdev)
{
struct fw_device *fwdev;
struct crom_context c, *cc = &c;
struct csrreg *reg;
memset(sdev->vendor, 0, sizeof(sdev->vendor));
memset(sdev->product, 0, sizeof(sdev->product));
fwdev = sdev->target->fwdev;
crom_init_context(cc, fwdev->csrrom);
crom_search_key(cc, CSRKEY_VENDOR);
crom_next(cc);
crom_parse_text(cc, sdev->vendor, sizeof(sdev->vendor));
while ((reg = crom_search_key(cc, CSRKEY_VER)) != NULL) {
if (reg->val == CSRVAL_T10SBP2)
break;
crom_next(cc);
}
reg = crom_search_key(cc, CSRKEY_FIRM_VER);
if (reg != NULL)
snprintf(sdev->revision, sizeof(sdev->revision), "%06x",
reg->val);
crom_search_key(cc, CSRKEY_MODEL);
crom_next(cc);
crom_parse_text(cc, sdev->product, sizeof(sdev->product));
}
static void
sbp_login_callout(void *arg)
{
struct sbp_dev *sdev = (struct sbp_dev *)arg;
sbp_mgm_orb(sdev, ORB_FUN_LGI, NULL);
}
static void
sbp_login(struct sbp_dev *sdev)
{
struct sbp_softc *sc = sdev->target->sbp;
struct timeval delta;
struct timeval t;
int ticks = 0;
microtime(&delta);
timersub(&delta, &sc->sc_last_busreset, &delta);
t.tv_sec = login_delay / 1000;
t.tv_usec = (login_delay % 1000) * 1000;
timersub(&t, &delta, &t);
if (t.tv_sec >= 0 && t.tv_usec > 0)
ticks = (t.tv_sec * 1000 + t.tv_usec / 1000) * hz / 1000;
SBP_DEBUG(0)
printf("%s: sec = %lld usec = %ld ticks = %d\n", __func__,
(long long)t.tv_sec, (long)t.tv_usec, ticks);
END_DEBUG
callout_schedule(&sdev->login_callout, ticks);
}
static void
sbp_probe_target(void *arg)
{
struct sbp_target *target = (struct sbp_target *)arg;
struct sbp_dev *sdev;
int i;
SBP_DEBUG(1)
printf("%s %d\n", __func__, target->target_id);
END_DEBUG
sbp_alloc_lun(target);
for (i = 0; i < target->num_lun; i++) {
sdev = target->luns[i];
if (sdev == NULL || sdev->status == SBP_DEV_DEAD)
continue;
if (sdev->periph != NULL) {
scsipi_periph_freeze(sdev->periph, 1);
sdev->freeze++;
}
sbp_probe_lun(sdev);
sbp_show_sdev_info(sdev);
sbp_abort_all_ocbs(sdev, XS_RESET);
switch (sdev->status) {
case SBP_DEV_RESET:
if (auto_login)
sbp_login(sdev);
break;
case SBP_DEV_TOATTACH:
case SBP_DEV_PROBE:
case SBP_DEV_ATTACHED:
case SBP_DEV_RETRY:
default:
sbp_mgm_orb(sdev, ORB_FUN_RCN, NULL);
break;
}
}
}
static void
sbp_post_busreset(void *arg)
{
struct sbp_softc *sc = (struct sbp_softc *)arg;
struct sbp_target *target = &sc->sc_target;
struct fw_device *fwdev = target->fwdev;
int alive;
alive = SBP_FWDEV_ALIVE(fwdev);
SBP_DEBUG(0)
printf("sbp_post_busreset\n");
if (!alive)
printf("not alive\n");
END_DEBUG
microtime(&sc->sc_last_busreset);
if (!alive)
return;
scsipi_channel_freeze(&sc->sc_channel, 1);
}
static void
sbp_post_explore(void *arg)
{
struct sbp_softc *sc = (struct sbp_softc *)arg;
struct sbp_target *target = &sc->sc_target;
struct fw_device *fwdev = target->fwdev;
int alive;
alive = SBP_FWDEV_ALIVE(fwdev);
SBP_DEBUG(0)
printf("sbp_post_explore (sbp_cold=%d)\n", sbp_cold);
if (!alive)
printf("not alive\n");
END_DEBUG
if (!alive)
return;
if (!firewire_phydma_enable)
return;
if (sbp_cold > 0)
sbp_cold--;
SBP_DEBUG(0)
printf("sbp_post_explore: EUI:%08x%08x ", fwdev->eui.hi, fwdev->eui.lo);
END_DEBUG
sbp_probe_target((void *)target);
if (target->num_lun == 0)
sbp_free_target(target);
scsipi_channel_thaw(&sc->sc_channel, 1);
}
#if NEED_RESPONSE
static void
sbp_loginres_callback(struct fw_xfer *xfer)
{
struct sbp_dev *sdev = (struct sbp_dev *)xfer->sc;
struct sbp_softc *sc = sdev->target->sbp;
SBP_DEBUG(1)
printf("sbp_loginres_callback\n");
END_DEBUG
mutex_enter(&sc->sc_fwb.fwb_mtx);
STAILQ_INSERT_TAIL(&sc->sc_fwb.xferlist, xfer, link);
mutex_exit(&sc->sc_fwb.fwb_mtx);
return;
}
#endif
static inline void
sbp_xfer_free(struct fw_xfer *xfer)
{
struct sbp_dev *sdev = (struct sbp_dev *)xfer->sc;
struct sbp_softc *sc = sdev->target->sbp;
fw_xfer_unload(xfer);
mutex_enter(&sc->sc_mtx);
STAILQ_INSERT_TAIL(&sdev->target->xferlist, xfer, link);
mutex_exit(&sc->sc_mtx);
}
static void
sbp_reset_start_callback(struct fw_xfer *xfer)
{
struct sbp_dev *tsdev, *sdev = (struct sbp_dev *)xfer->sc;
struct sbp_target *target = sdev->target;
int i;
if (xfer->resp != 0)
aprint_error("%s: sbp_reset_start failed: resp=%d\n",
sdev->bustgtlun, xfer->resp);
for (i = 0; i < target->num_lun; i++) {
tsdev = target->luns[i];
if (tsdev != NULL && tsdev->status == SBP_DEV_LOGIN)
sbp_login(tsdev);
}
}
static void
sbp_reset_start(struct sbp_dev *sdev)
{
struct fw_xfer *xfer;
struct fw_pkt *fp;
SBP_DEBUG(0)
printf("%s: sbp_reset_start: %s\n",
device_xname(sdev->target->sbp->sc_fd.dev), sdev->bustgtlun);
END_DEBUG
xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0);
if (xfer == NULL)
return;
xfer->hand = sbp_reset_start_callback;
fp = &xfer->send.hdr;
fp->mode.wreqq.dest_hi = 0xffff;
fp->mode.wreqq.dest_lo = 0xf0000000 | RESET_START;
fp->mode.wreqq.data = htonl(0xf);
if (fw_asyreq(xfer->fc, -1, xfer) != 0)
sbp_xfer_free(xfer);
}
static void
sbp_mgm_callback(struct fw_xfer *xfer)
{
struct sbp_dev *sdev;
sdev = (struct sbp_dev *)xfer->sc;
SBP_DEBUG(1)
printf("%s: sbp_mgm_callback: %s\n",
device_xname(sdev->target->sbp->sc_fd.dev), sdev->bustgtlun);
END_DEBUG
sbp_xfer_free(xfer);
return;
}
static void
sbp_scsipi_scan_target(void *arg)
{
struct sbp_target *target = (struct sbp_target *)arg;
struct sbp_softc *sc = target->sbp;
struct sbp_dev *sdev;
struct scsipi_channel *chan = &sc->sc_channel;
struct scsibus_softc *sc_bus = device_private(sc->sc_bus);
int lun, yet;
do {
mutex_enter(&sc->sc_mtx);
cv_wait_sig(&sc->sc_cv, &sc->sc_mtx);
mutex_exit(&sc->sc_mtx);
yet = 0;
for (lun = 0; lun < target->num_lun; lun++) {
sdev = target->luns[lun];
if (sdev == NULL)
continue;
if (sdev->status != SBP_DEV_PROBE) {
yet++;
continue;
}
if (sdev->periph == NULL) {
if (chan->chan_nluns < target->num_lun)
chan->chan_nluns = target->num_lun;
scsi_probe_bus(sc_bus, target->target_id,
sdev->lun_id);
sdev->periph = scsipi_lookup_periph(chan,
target->target_id, lun);
}
sdev->status = SBP_DEV_ATTACHED;
}
} while (yet > 0);
sc->sc_lwp = NULL;
kthread_exit(0);
}
static inline void
sbp_scan_dev(struct sbp_dev *sdev)
{
struct sbp_softc *sc = sdev->target->sbp;
sdev->status = SBP_DEV_PROBE;
mutex_enter(&sc->sc_mtx);
cv_signal(&sdev->target->sbp->sc_cv);
mutex_exit(&sc->sc_mtx);
}
static void
sbp_do_attach(struct fw_xfer *xfer)
{
struct sbp_dev *sdev;
struct sbp_target *target;
struct sbp_softc *sc;
sdev = (struct sbp_dev *)xfer->sc;
target = sdev->target;
sc = target->sbp;
SBP_DEBUG(0)
printf("%s:%s:%s\n", device_xname(sc->sc_fd.dev), __func__,
sdev->bustgtlun);
END_DEBUG
sbp_xfer_free(xfer);
sbp_scan_dev(sdev);
return;
}
static void
sbp_agent_reset_callback(struct fw_xfer *xfer)
{
struct sbp_dev *sdev = (struct sbp_dev *)xfer->sc;
struct sbp_softc *sc = sdev->target->sbp;
SBP_DEBUG(1)
printf("%s:%s:%s\n", device_xname(sc->sc_fd.dev), __func__,
sdev->bustgtlun);
END_DEBUG
if (xfer->resp != 0)
aprint_error_dev(sc->sc_fd.dev, "%s:%s: resp=%d\n", __func__,
sdev->bustgtlun, xfer->resp);
sbp_xfer_free(xfer);
if (sdev->periph != NULL) {
scsipi_periph_thaw(sdev->periph, sdev->freeze);
scsipi_channel_thaw(&sc->sc_channel, 0);
sdev->freeze = 0;
}
}
static void
sbp_agent_reset(struct sbp_dev *sdev)
{
struct fw_xfer *xfer;
struct fw_pkt *fp;
SBP_DEBUG(0)
printf("%s:%s:%s\n", device_xname(sdev->target->sbp->sc_fd.dev),
__func__, sdev->bustgtlun);
END_DEBUG
xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0x04);
if (xfer == NULL)
return;
if (sdev->status == SBP_DEV_ATTACHED || sdev->status == SBP_DEV_PROBE)
xfer->hand = sbp_agent_reset_callback;
else
xfer->hand = sbp_do_attach;
fp = &xfer->send.hdr;
fp->mode.wreqq.data = htonl(0xf);
if (fw_asyreq(xfer->fc, -1, xfer) != 0)
sbp_xfer_free(xfer);
sbp_abort_all_ocbs(sdev, XS_RESET);
}
static void
sbp_busy_timeout_callback(struct fw_xfer *xfer)
{
struct sbp_dev *sdev = (struct sbp_dev *)xfer->sc;
SBP_DEBUG(1)
printf("%s:%s:%s\n", device_xname(sdev->target->sbp->sc_fd.dev),
__func__, sdev->bustgtlun);
END_DEBUG
sbp_xfer_free(xfer);
sbp_agent_reset(sdev);
}
static void
sbp_busy_timeout(struct sbp_dev *sdev)
{
struct fw_pkt *fp;
struct fw_xfer *xfer;
SBP_DEBUG(0)
printf("%s:%s:%s\n", device_xname(sdev->target->sbp->sc_fd.dev),
__func__, sdev->bustgtlun);
END_DEBUG
xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0);
if (xfer == NULL)
return;
xfer->hand = sbp_busy_timeout_callback;
fp = &xfer->send.hdr;
fp->mode.wreqq.dest_hi = 0xffff;
fp->mode.wreqq.dest_lo = 0xf0000000 | BUSY_TIMEOUT;
fp->mode.wreqq.data = htonl((1 << (13+12)) | 0xf);
if (fw_asyreq(xfer->fc, -1, xfer) != 0)
sbp_xfer_free(xfer);
}
static void
sbp_orb_pointer_callback(struct fw_xfer *xfer)
{
struct sbp_dev *sdev = (struct sbp_dev *)xfer->sc;
struct sbp_softc *sc = sdev->target->sbp;
SBP_DEBUG(1)
printf("%s:%s:%s\n", device_xname(sc->sc_fd.dev), __func__,
sdev->bustgtlun);
END_DEBUG
if (xfer->resp != 0)
aprint_error_dev(sc->sc_fd.dev, "%s:%s: xfer->resp = %d\n",
__func__, sdev->bustgtlun, xfer->resp);
sbp_xfer_free(xfer);
sdev->flags &= ~ORB_POINTER_ACTIVE;
if ((sdev->flags & ORB_POINTER_NEED) != 0) {
struct sbp_ocb *ocb;
sdev->flags &= ~ORB_POINTER_NEED;
ocb = STAILQ_FIRST(&sdev->ocbs);
if (ocb != NULL)
sbp_orb_pointer(sdev, ocb);
}
return;
}
static void
sbp_orb_pointer(struct sbp_dev *sdev, struct sbp_ocb *ocb)
{
struct sbp_softc *sc = sdev->target->sbp;
struct fw_xfer *xfer;
struct fw_pkt *fp;
SBP_DEBUG(1)
printf("%s:%s:%s: 0x%08x\n", device_xname(sc->sc_fd.dev), __func__,
sdev->bustgtlun, (uint32_t)ocb->bus_addr);
END_DEBUG
if ((sdev->flags & ORB_POINTER_ACTIVE) != 0) {
SBP_DEBUG(0)
printf("%s: orb pointer active\n", __func__);
END_DEBUG
sdev->flags |= ORB_POINTER_NEED;
return;
}
sdev->flags |= ORB_POINTER_ACTIVE;
xfer = sbp_write_cmd(sdev, FWTCODE_WREQB, 0x08);
if (xfer == NULL)
return;
xfer->hand = sbp_orb_pointer_callback;
fp = &xfer->send.hdr;
fp->mode.wreqb.len = 8;
fp->mode.wreqb.extcode = 0;
xfer->send.payload[0] =
htonl(((sc->sc_fd.fc->nodeid | FWLOCALBUS) << 16));
xfer->send.payload[1] = htonl((uint32_t)ocb->bus_addr);
if (fw_asyreq(xfer->fc, -1, xfer) != 0) {
sbp_xfer_free(xfer);
ocb->xs->error = XS_DRIVER_STUFFUP;
scsipi_done(ocb->xs);
}
}
static void
sbp_doorbell_callback(struct fw_xfer *xfer)
{
struct sbp_dev *sdev = (struct sbp_dev *)xfer->sc;
struct sbp_softc *sc = sdev->target->sbp;
SBP_DEBUG(1)
printf("%s:%s:%s\n", device_xname(sc->sc_fd.dev), __func__,
sdev->bustgtlun);
END_DEBUG
if (xfer->resp != 0) {
aprint_error_dev(sc->sc_fd.dev, "%s: xfer->resp = %d\n",
__func__, xfer->resp);
}
sbp_xfer_free(xfer);
sdev->flags &= ~ORB_DOORBELL_ACTIVE;
if ((sdev->flags & ORB_DOORBELL_NEED) != 0) {
sdev->flags &= ~ORB_DOORBELL_NEED;
sbp_doorbell(sdev);
}
return;
}
static void
sbp_doorbell(struct sbp_dev *sdev)
{
struct fw_xfer *xfer;
struct fw_pkt *fp;
SBP_DEBUG(1)
printf("%s:%s:%s\n", device_xname(sdev->target->sbp->sc_fd.dev),
__func__, sdev->bustgtlun);
END_DEBUG
if ((sdev->flags & ORB_DOORBELL_ACTIVE) != 0) {
sdev->flags |= ORB_DOORBELL_NEED;
return;
}
sdev->flags |= ORB_DOORBELL_ACTIVE;
xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0x10);
if (xfer == NULL)
return;
xfer->hand = sbp_doorbell_callback;
fp = &xfer->send.hdr;
fp->mode.wreqq.data = htonl(0xf);
if (fw_asyreq(xfer->fc, -1, xfer) != 0)
sbp_xfer_free(xfer);
}
static struct fw_xfer *
sbp_write_cmd(struct sbp_dev *sdev, int tcode, int offset)
{
struct sbp_softc *sc;
struct fw_xfer *xfer;
struct fw_pkt *fp;
struct sbp_target *target;
int new = 0;
target = sdev->target;
sc = target->sbp;
mutex_enter(&sc->sc_mtx);
xfer = STAILQ_FIRST(&target->xferlist);
if (xfer == NULL) {
if (target->n_xfer > 5 ) {
aprint_error_dev(sc->sc_fd.dev,
"no more xfer for this target\n");
mutex_exit(&sc->sc_mtx);
return NULL;
}
xfer = fw_xfer_alloc_buf(M_SBP, 8, 0);
if (xfer == NULL) {
aprint_error_dev(sc->sc_fd.dev,
"fw_xfer_alloc_buf failed\n");
mutex_exit(&sc->sc_mtx);
return NULL;
}
target->n_xfer++;
SBP_DEBUG(0)
printf("sbp: alloc %d xfer\n", target->n_xfer);
END_DEBUG
new = 1;
} else
STAILQ_REMOVE_HEAD(&target->xferlist, link);
mutex_exit(&sc->sc_mtx);
microtime(&xfer->tv);
if (new) {
xfer->recv.pay_len = 0;
xfer->send.spd = uimin(target->fwdev->speed, max_speed);
xfer->fc = target->sbp->sc_fd.fc;
}
if (tcode == FWTCODE_WREQB)
xfer->send.pay_len = 8;
else
xfer->send.pay_len = 0;
xfer->sc = (void *)sdev;
fp = &xfer->send.hdr;
fp->mode.wreqq.dest_hi = sdev->login->cmd_hi;
fp->mode.wreqq.dest_lo = sdev->login->cmd_lo + offset;
fp->mode.wreqq.tlrt = 0;
fp->mode.wreqq.tcode = tcode;
fp->mode.wreqq.pri = 0;
fp->mode.wreqq.dst = FWLOCALBUS | target->fwdev->dst;
return xfer;
}
static void
sbp_mgm_orb(struct sbp_dev *sdev, int func, struct sbp_ocb *aocb)
{
struct fw_xfer *xfer;
struct fw_pkt *fp;
struct sbp_ocb *ocb;
struct sbp_target *target;
int nid, dv_unit;
target = sdev->target;
nid = target->sbp->sc_fd.fc->nodeid | FWLOCALBUS;
dv_unit = device_unit(target->sbp->sc_fd.dev);
mutex_enter(&target->sbp->sc_mtx);
if (func == ORB_FUN_RUNQUEUE) {
ocb = STAILQ_FIRST(&target->mgm_ocb_queue);
if (target->mgm_ocb_cur != NULL || ocb == NULL) {
mutex_exit(&target->sbp->sc_mtx);
return;
}
STAILQ_REMOVE_HEAD(&target->mgm_ocb_queue, ocb);
mutex_exit(&target->sbp->sc_mtx);
goto start;
}
if ((ocb = sbp_get_ocb(sdev)) == NULL) {
mutex_exit(&target->sbp->sc_mtx);
return;
}
mutex_exit(&target->sbp->sc_mtx);
ocb->flags = OCB_ACT_MGM;
ocb->sdev = sdev;
memset(ocb->orb, 0, sizeof(ocb->orb));
ocb->orb[6] = htonl((nid << 16) | SBP_BIND_HI);
ocb->orb[7] = htonl(SBP_DEV2ADDR(dv_unit, sdev->lun_id));
SBP_DEBUG(0)
printf("%s:%s:%s: %s\n", device_xname(sdev->target->sbp->sc_fd.dev),
__func__, sdev->bustgtlun, orb_fun_name[(func>>16)&0xf]);
END_DEBUG
switch (func) {
case ORB_FUN_LGI:
{
const off_t sbp_login_off =
sizeof(struct sbp_ocb) * SBP_QUEUE_LEN;
ocb->orb[0] = ocb->orb[1] = 0;
ocb->orb[2] = htonl(nid << 16);
ocb->orb[3] = htonl(sdev->dma.bus_addr + sbp_login_off);
ocb->orb[4] = htonl(ORB_NOTIFY | sdev->lun_id);
if (ex_login)
ocb->orb[4] |= htonl(ORB_EXV);
ocb->orb[5] = htonl(SBP_LOGIN_SIZE);
bus_dmamap_sync(sdev->dma.dma_tag, sdev->dma.dma_map,
sbp_login_off, SBP_LOGIN_SIZE, BUS_DMASYNC_PREREAD);
break;
}
case ORB_FUN_ATA:
ocb->orb[0] = htonl((0 << 16) | 0);
ocb->orb[1] = htonl(aocb->bus_addr & 0xffffffff);
case ORB_FUN_RCN:
case ORB_FUN_LGO:
case ORB_FUN_LUR:
case ORB_FUN_RST:
case ORB_FUN_ATS:
ocb->orb[4] = htonl(ORB_NOTIFY | func | sdev->login->id);
break;
}
if (target->mgm_ocb_cur != NULL) {
mutex_enter(&target->sbp->sc_mtx);
STAILQ_INSERT_TAIL(&sdev->target->mgm_ocb_queue, ocb, ocb);
mutex_exit(&target->sbp->sc_mtx);
return;
}
start:
target->mgm_ocb_cur = ocb;
callout_reset(&target->mgm_ocb_timeout, 5 * hz, sbp_mgm_timeout, ocb);
xfer = sbp_write_cmd(sdev, FWTCODE_WREQB, 0);
if (xfer == NULL)
return;
xfer->hand = sbp_mgm_callback;
fp = &xfer->send.hdr;
fp->mode.wreqb.dest_hi = sdev->target->mgm_hi;
fp->mode.wreqb.dest_lo = sdev->target->mgm_lo;
fp->mode.wreqb.len = 8;
fp->mode.wreqb.extcode = 0;
xfer->send.payload[0] = htonl(nid << 16);
xfer->send.payload[1] = htonl(ocb->bus_addr & 0xffffffff);
SBP_ORB_DMA_SYNC(sdev->dma, ocb->index, BUS_DMASYNC_PREWRITE);
if (fw_asyreq(xfer->fc, -1, xfer) != 0)
sbp_xfer_free(xfer);
}
static void
sbp_print_scsi_cmd(struct sbp_ocb *ocb)
{
struct scsipi_xfer *xs = ocb->xs;
printf("%s:%d:%d:"
" cmd: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x,"
" flags: 0x%02x, %db cmd/%db data\n",
device_xname(ocb->sdev->target->sbp->sc_fd.dev),
xs->xs_periph->periph_target,
xs->xs_periph->periph_lun,
xs->cmd->opcode,
xs->cmd->bytes[0], xs->cmd->bytes[1],
xs->cmd->bytes[2], xs->cmd->bytes[3],
xs->cmd->bytes[4], xs->cmd->bytes[5],
xs->cmd->bytes[6], xs->cmd->bytes[7],
xs->cmd->bytes[8],
xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT),
xs->cmdlen, xs->datalen);
}
static void
sbp_scsi_status(struct sbp_status *sbp_status, struct sbp_ocb *ocb)
{
struct sbp_cmd_status *sbp_cmd_status;
struct scsi_sense_data *sense = &ocb->xs->sense.scsi_sense;
sbp_cmd_status = (struct sbp_cmd_status *)sbp_status->data;
SBP_DEBUG(0)
sbp_print_scsi_cmd(ocb);
printf("%s:"
" SCSI status %x sfmt %x valid %x key %x code %x qlfr %x len %d\n",
ocb->sdev->bustgtlun,
sbp_cmd_status->status,
sbp_cmd_status->sfmt,
sbp_cmd_status->valid,
sbp_cmd_status->s_key,
sbp_cmd_status->s_code,
sbp_cmd_status->s_qlfr,
sbp_status->len);
END_DEBUG
switch (sbp_cmd_status->status) {
case SCSI_CHECK:
case SCSI_BUSY:
case SCSI_TERMINATED:
if (sbp_cmd_status->sfmt == SBP_SFMT_CURR)
sense->response_code = SSD_RCODE_CURRENT;
else
sense->response_code = SSD_RCODE_DEFERRED;
if (sbp_cmd_status->valid)
sense->response_code |= SSD_RCODE_VALID;
sense->flags = sbp_cmd_status->s_key;
if (sbp_cmd_status->mark)
sense->flags |= SSD_FILEMARK;
if (sbp_cmd_status->eom)
sense->flags |= SSD_EOM;
if (sbp_cmd_status->ill_len)
sense->flags |= SSD_ILI;
memcpy(sense->info, &sbp_cmd_status->info, 4);
if (sbp_status->len <= 1)
sense->extra_len = 0;
else if (sbp_status->len <= 4)
sense->extra_len = 6;
else
sense->extra_len = 10;
memcpy(sense->csi, &sbp_cmd_status->cdb, 4);
sense->asc = sbp_cmd_status->s_code;
sense->ascq = sbp_cmd_status->s_qlfr;
sense->fru = sbp_cmd_status->fru;
memcpy(sense->sks.sks_bytes, sbp_cmd_status->s_keydep, 3);
ocb->xs->error = XS_SENSE;
ocb->xs->xs_status = sbp_cmd_status->status;
break;
default:
aprint_error_dev(ocb->sdev->target->sbp->sc_fd.dev,
"%s:%s: unknown scsi status 0x%x\n",
__func__, ocb->sdev->bustgtlun, sbp_cmd_status->status);
}
}
static void
sbp_fix_inq_data(struct sbp_ocb *ocb)
{
struct scsipi_xfer *xs = ocb->xs;
struct sbp_dev *sdev;
struct scsipi_inquiry_data *inq =
(struct scsipi_inquiry_data *)xs->data;
sdev = ocb->sdev;
#if 0
#define SI_EVPD 0x01
if (xs->cmd->bytes[0] & SI_EVPD)
return;
#endif
SBP_DEBUG(1)
printf("%s:%s:%s\n", device_xname(sdev->target->sbp->sc_fd.dev),
__func__, sdev->bustgtlun);
END_DEBUG
switch (inq->device & SID_TYPE) {
case T_DIRECT:
#if 0
if ((inq->device & SID_TYPE) == T_DIRECT)
inq->device |= T_SIMPLE_DIRECT;
#endif
case T_SIMPLE_DIRECT:
#if 1
memcpy(inq->vendor, sdev->vendor, sizeof(inq->vendor));
memcpy(inq->product, sdev->product, sizeof(inq->product));
memcpy(inq->revision + 2, sdev->revision,
sizeof(inq->revision));
#endif
break;
}
if (sbp_tags > 0)
inq->flags3 |= SID_CmdQue;
else if (sbp_tags < 0)
inq->flags3 &= ~SID_CmdQue;
}
static void
sbp_recv(struct fw_xfer *xfer)
{
struct fw_pkt *rfp;
#if NEED_RESPONSE
struct fw_pkt *sfp;
#endif
struct sbp_softc *sc;
struct sbp_dev *sdev;
struct sbp_ocb *ocb;
struct sbp_login_res *login_res = NULL;
struct sbp_status *sbp_status;
struct sbp_target *target;
int orb_fun, status_valid0, status_valid, l, reset_agent = 0;
uint32_t addr;
sc = (struct sbp_softc *)xfer->sc;
if (xfer->resp != 0) {
aprint_error_dev(sc->sc_fd.dev,
"sbp_recv: xfer->resp = %d\n", xfer->resp);
goto done0;
}
if (xfer->recv.payload == NULL) {
aprint_error_dev(sc->sc_fd.dev,
"sbp_recv: xfer->recv.payload == NULL\n");
goto done0;
}
rfp = &xfer->recv.hdr;
if (rfp->mode.wreqb.tcode != FWTCODE_WREQB) {
aprint_error_dev(sc->sc_fd.dev,
"sbp_recv: tcode = %d\n", rfp->mode.wreqb.tcode);
goto done0;
}
sbp_status = (struct sbp_status *)xfer->recv.payload;
addr = rfp->mode.wreqb.dest_lo;
SBP_DEBUG(2)
printf("received address 0x%x\n", addr);
END_DEBUG
target = &sc->sc_target;
l = SBP_ADDR2LUN(addr);
if (l >= target->num_lun || target->luns[l] == NULL) {
aprint_error_dev(sc->sc_fd.dev,
"sbp_recv1: invalid lun %d (target=%d)\n",
l, target->target_id);
goto done0;
}
sdev = target->luns[l];
ocb = NULL;
switch (sbp_status->src) {
case SRC_NEXT_EXISTS:
case SRC_NO_NEXT:
ocb = target->mgm_ocb_cur;
if (ocb != NULL)
if (OCB_MATCH(ocb, sbp_status)) {
callout_stop(&target->mgm_ocb_timeout);
target->mgm_ocb_cur = NULL;
break;
}
ocb = sbp_dequeue_ocb(sdev, sbp_status);
if (ocb == NULL)
aprint_error_dev(sc->sc_fd.dev,
"%s:%s: No ocb(%x) on the queue\n", __func__,
sdev->bustgtlun, ntohl(sbp_status->orb_lo));
break;
case SRC_UNSOL:
aprint_error_dev(sc->sc_fd.dev,
"%s:%s: unsolicit status received\n",
__func__, sdev->bustgtlun);
break;
default:
aprint_error_dev(sc->sc_fd.dev,
"%s:%s: unknown sbp_status->src\n",
__func__, sdev->bustgtlun);
}
status_valid0 = (sbp_status->src < 2
&& sbp_status->resp == SBP_REQ_CMP
&& sbp_status->dead == 0);
status_valid = (status_valid0 && sbp_status->status == 0);
if (!status_valid0 || debug > 2) {
int status;
SBP_DEBUG(0)
printf("%s:%s:%s: ORB status src:%x resp:%x dead:%x"
" len:%x stat:%x orb:%x%08x\n",
device_xname(sc->sc_fd.dev), __func__, sdev->bustgtlun,
sbp_status->src, sbp_status->resp, sbp_status->dead,
sbp_status->len, sbp_status->status,
ntohs(sbp_status->orb_hi), ntohl(sbp_status->orb_lo));
END_DEBUG
printf("%s:%s\n", device_xname(sc->sc_fd.dev), sdev->bustgtlun);
status = sbp_status->status;
switch (sbp_status->resp) {
case SBP_REQ_CMP:
if (status > MAX_ORB_STATUS0)
printf("%s\n", orb_status0[MAX_ORB_STATUS0]);
else
printf("%s\n", orb_status0[status]);
break;
case SBP_TRANS_FAIL:
printf("Obj: %s, Error: %s\n",
orb_status1_object[(status>>6) & 3],
orb_status1_serial_bus_error[status & 0xf]);
break;
case SBP_ILLE_REQ:
printf("Illegal request\n");
break;
case SBP_VEND_DEP:
printf("Vendor dependent\n");
break;
default:
printf("unknown response code %d\n", sbp_status->resp);
}
}
if (sbp_status->dead) {
if (sdev->periph != NULL) {
scsipi_periph_freeze(sdev->periph, 1);
sdev->freeze++;
}
reset_agent = 1;
}
if (ocb == NULL)
goto done;
switch (ntohl(ocb->orb[4]) & ORB_FMT_MSK) {
case ORB_FMT_NOP:
break;
case ORB_FMT_VED:
break;
case ORB_FMT_STD:
switch (ocb->flags) {
case OCB_ACT_MGM:
orb_fun = ntohl(ocb->orb[4]) & ORB_FUN_MSK;
reset_agent = 0;
switch (orb_fun) {
case ORB_FUN_LGI:
{
const struct fwdma_alloc *dma = &sdev->dma;
const off_t sbp_login_off =
sizeof(struct sbp_ocb) * SBP_QUEUE_LEN;
bus_dmamap_sync(dma->dma_tag, dma->dma_map,
sbp_login_off, SBP_LOGIN_SIZE,
BUS_DMASYNC_POSTREAD);
login_res = sdev->login;
login_res->len = ntohs(login_res->len);
login_res->id = ntohs(login_res->id);
login_res->cmd_hi = ntohs(login_res->cmd_hi);
login_res->cmd_lo = ntohl(login_res->cmd_lo);
if (status_valid) {
SBP_DEBUG(0)
printf("%s:%s:%s: login:"
" len %d, ID %d, cmd %08x%08x,"
" recon_hold %d\n",
device_xname(sc->sc_fd.dev),
__func__, sdev->bustgtlun,
login_res->len, login_res->id,
login_res->cmd_hi,
login_res->cmd_lo,
ntohs(login_res->recon_hold));
END_DEBUG
sbp_busy_timeout(sdev);
} else {
aprint_error_dev(sc->sc_fd.dev,
"%s:%s: login failed\n",
__func__, sdev->bustgtlun);
sdev->status = SBP_DEV_RESET;
}
break;
}
case ORB_FUN_RCN:
login_res = sdev->login;
if (status_valid) {
SBP_DEBUG(0)
printf("%s:%s:%s: reconnect:"
" len %d, ID %d, cmd %08x%08x\n",
device_xname(sc->sc_fd.dev),
__func__, sdev->bustgtlun,
login_res->len, login_res->id,
login_res->cmd_hi,
login_res->cmd_lo);
END_DEBUG
sbp_agent_reset(sdev);
} else {
SBP_DEBUG(0)
aprint_error_dev(sc->sc_fd.dev,
"%s:%s: reconnect failed\n",
__func__, sdev->bustgtlun);
END_DEBUG
sbp_login(sdev);
}
break;
case ORB_FUN_LGO:
sdev->status = SBP_DEV_RESET;
break;
case ORB_FUN_RST:
sbp_busy_timeout(sdev);
break;
case ORB_FUN_LUR:
case ORB_FUN_ATA:
case ORB_FUN_ATS:
sbp_agent_reset(sdev);
break;
default:
aprint_error_dev(sc->sc_fd.dev,
"%s:%s: unknown function %d\n",
__func__, sdev->bustgtlun, orb_fun);
break;
}
sbp_mgm_orb(sdev, ORB_FUN_RUNQUEUE, NULL);
break;
case OCB_ACT_CMD:
sdev->timeout = 0;
if (ocb->xs != NULL) {
struct scsipi_xfer *xs = ocb->xs;
if (sbp_status->len > 1)
sbp_scsi_status(sbp_status, ocb);
else
if (sbp_status->resp != SBP_REQ_CMP)
xs->error = XS_DRIVER_STUFFUP;
else {
xs->error = XS_NOERROR;
xs->resid = 0;
}
if (xs->cmd->opcode == INQUIRY)
sbp_fix_inq_data(ocb);
scsipi_done(xs);
}
break;
default:
break;
}
}
if (!use_doorbell)
sbp_free_ocb(sdev, ocb);
done:
if (reset_agent)
sbp_agent_reset(sdev);
done0:
xfer->recv.pay_len = SBP_RECV_LEN;
#if NEED_RESPONSE
xfer->send.off = 0;
sfp = (struct fw_pkt *)xfer->send.buf;
sfp->mode.wres.dst = rfp->mode.wreqb.src;
xfer->dst = sfp->mode.wres.dst;
xfer->spd = uimin(sdev->target->fwdev->speed, max_speed);
xfer->hand = sbp_loginres_callback;
sfp->mode.wres.tlrt = rfp->mode.wreqb.tlrt;
sfp->mode.wres.tcode = FWTCODE_WRES;
sfp->mode.wres.rtcode = 0;
sfp->mode.wres.pri = 0;
if (fw_asyreq(xfer->fc, -1, xfer) != 0) {
aprint_error_dev(sc->sc_fd.dev, "mgm_orb failed\n");
mutex_enter(&sc->sc_fwb.fwb_mtx);
STAILQ_INSERT_TAIL(&sc->sc_fwb.xferlist, xfer, link);
mutex_exit(&sc->sc_fwb.fwb_mtx);
}
#else
mutex_enter(&sc->sc_fwb.fwb_mtx);
STAILQ_INSERT_TAIL(&sc->sc_fwb.xferlist, xfer, link);
mutex_exit(&sc->sc_fwb.fwb_mtx);
#endif
return;
}
static int
sbp_logout_all(struct sbp_softc *sbp)
{
struct sbp_target *target;
struct sbp_dev *sdev;
int i;
SBP_DEBUG(0)
printf("sbp_logout_all\n");
END_DEBUG
target = &sbp->sc_target;
if (target->luns != NULL) {
for (i = 0; i < target->num_lun; i++) {
sdev = target->luns[i];
if (sdev == NULL)
continue;
callout_stop(&sdev->login_callout);
if (sdev->status >= SBP_DEV_TOATTACH &&
sdev->status <= SBP_DEV_ATTACHED)
sbp_mgm_orb(sdev, ORB_FUN_LGO, NULL);
}
}
return 0;
}
static void
sbp_free_sdev(struct sbp_dev *sdev)
{
struct sbp_softc *sc = sdev->target->sbp;
int i;
if (sdev == NULL)
return;
for (i = 0; i < SBP_QUEUE_LEN; i++)
bus_dmamap_destroy(sc->sc_dmat, sdev->ocb[i].dmamap);
fwdma_free(sdev->dma.dma_tag, sdev->dma.dma_map, sdev->dma.v_addr);
free(sdev, M_SBP);
}
static void
sbp_free_target(struct sbp_target *target)
{
struct fw_xfer *xfer, *next;
int i;
if (target->luns == NULL)
return;
callout_stop(&target->mgm_ocb_timeout);
for (i = 0; i < target->num_lun; i++)
sbp_free_sdev(target->luns[i]);
for (xfer = STAILQ_FIRST(&target->xferlist);
xfer != NULL; xfer = next) {
next = STAILQ_NEXT(xfer, link);
fw_xfer_free_buf(xfer);
}
STAILQ_INIT(&target->xferlist);
free(target->luns, M_SBP);
target->num_lun = 0;
target->luns = NULL;
target->fwdev = NULL;
}
static void
sbp_scsipi_detach_sdev(struct sbp_dev *sdev)
{
struct sbp_target *target;
struct sbp_softc *sbp;
if (sdev == NULL)
return;
target = sdev->target;
if (target == NULL)
return;
sbp = target->sbp;
if (sdev->status == SBP_DEV_DEAD)
return;
if (sdev->status == SBP_DEV_RESET)
return;
if (sdev->periph != NULL) {
scsipi_periph_thaw(sdev->periph, sdev->freeze);
scsipi_channel_thaw(&sbp->sc_channel, 0);
sdev->freeze = 0;
if (scsipi_target_detach(&sbp->sc_channel,
target->target_id, sdev->lun_id, DETACH_FORCE) != 0) {
aprint_error_dev(sbp->sc_fd.dev, "detach failed\n");
}
sdev->periph = NULL;
}
sbp_abort_all_ocbs(sdev, XS_DRIVER_STUFFUP);
}
static void
sbp_scsipi_detach_target(struct sbp_target *target)
{
struct sbp_softc *sbp = target->sbp;
int i;
if (target->luns != NULL) {
SBP_DEBUG(0)
printf("sbp_detach_target %d\n", target->target_id);
END_DEBUG
for (i = 0; i < target->num_lun; i++)
sbp_scsipi_detach_sdev(target->luns[i]);
if (config_detach(sbp->sc_bus, DETACH_FORCE) != 0)
aprint_error_dev(sbp->sc_fd.dev, "%d detach failed\n",
target->target_id);
sbp->sc_bus = NULL;
}
}
static void
sbp_target_reset(struct sbp_dev *sdev, int method)
{
struct sbp_target *target = sdev->target;
struct sbp_dev *tsdev;
int i;
for (i = 0; i < target->num_lun; i++) {
tsdev = target->luns[i];
if (tsdev == NULL)
continue;
if (tsdev->status == SBP_DEV_DEAD)
continue;
if (tsdev->status == SBP_DEV_RESET)
continue;
if (sdev->periph != NULL) {
scsipi_periph_freeze(tsdev->periph, 1);
tsdev->freeze++;
}
sbp_abort_all_ocbs(tsdev, XS_TIMEOUT);
if (method == 2)
tsdev->status = SBP_DEV_LOGIN;
}
switch (method) {
case 1:
aprint_error("target reset\n");
sbp_mgm_orb(sdev, ORB_FUN_RST, NULL);
break;
case 2:
aprint_error("reset start\n");
sbp_reset_start(sdev);
break;
}
}
static void
sbp_mgm_timeout(void *arg)
{
struct sbp_ocb *ocb = (struct sbp_ocb *)arg;
struct sbp_dev *sdev = ocb->sdev;
struct sbp_target *target = sdev->target;
aprint_error_dev(sdev->target->sbp->sc_fd.dev,
"%s:%s: request timeout(mgm orb:0x%08x) ... ",
__func__, sdev->bustgtlun, (uint32_t)ocb->bus_addr);
target->mgm_ocb_cur = NULL;
sbp_free_ocb(sdev, ocb);
#if 0
aprint_error("run next request\n");
sbp_mgm_orb(sdev, ORB_FUN_RUNQUEUE, NULL);
#endif
aprint_error_dev(sdev->target->sbp->sc_fd.dev,
"%s:%s: reset start\n", __func__, sdev->bustgtlun);
sbp_reset_start(sdev);
}
static void
sbp_timeout(void *arg)
{
struct sbp_ocb *ocb = (struct sbp_ocb *)arg;
struct sbp_dev *sdev = ocb->sdev;
aprint_error_dev(sdev->target->sbp->sc_fd.dev,
"%s:%s: request timeout(cmd orb:0x%08x) ... ",
__func__, sdev->bustgtlun, (uint32_t)ocb->bus_addr);
sdev->timeout++;
switch (sdev->timeout) {
case 1:
aprint_error("agent reset\n");
if (sdev->periph != NULL) {
scsipi_periph_freeze(sdev->periph, 1);
sdev->freeze++;
}
sbp_abort_all_ocbs(sdev, XS_TIMEOUT);
sbp_agent_reset(sdev);
break;
case 2:
case 3:
sbp_target_reset(sdev, sdev->timeout - 1);
break;
default:
aprint_error("\n");
#if 0
sbp_scsipi_detach_target(target);
if (target->luns != NULL)
free(target->luns, M_SBP);
target->num_lun = 0;
target->luns = NULL;
target->fwdev = NULL;
#endif
}
}
static void
sbp_action1(struct sbp_softc *sc, struct scsipi_xfer *xs)
{
struct sbp_target *target = &sc->sc_target;
struct sbp_dev *sdev = NULL;
struct sbp_ocb *ocb;
int speed, flag, error;
void *cdb;
if (target->fwdev != NULL &&
xs->xs_periph->periph_lun < target->num_lun) {
sdev = target->luns[xs->xs_periph->periph_lun];
if (sdev != NULL && sdev->status != SBP_DEV_ATTACHED &&
sdev->status != SBP_DEV_PROBE)
sdev = NULL;
}
if (sdev == NULL) {
SBP_DEBUG(1)
printf("%s:%d:%d: Invalid target (target needed)\n",
sc ? device_xname(sc->sc_fd.dev) : "???",
xs->xs_periph->periph_target,
xs->xs_periph->periph_lun);
END_DEBUG
xs->error = XS_DRIVER_STUFFUP;
scsipi_done(xs);
return;
}
SBP_DEBUG(2)
printf("%s:%d:%d:"
" cmd: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x,"
" flags: 0x%02x, %db cmd/%db data\n",
device_xname(sc->sc_fd.dev),
xs->xs_periph->periph_target,
xs->xs_periph->periph_lun,
xs->cmd->opcode,
xs->cmd->bytes[0], xs->cmd->bytes[1],
xs->cmd->bytes[2], xs->cmd->bytes[3],
xs->cmd->bytes[4], xs->cmd->bytes[5],
xs->cmd->bytes[6], xs->cmd->bytes[7],
xs->cmd->bytes[8],
xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT),
xs->cmdlen, xs->datalen);
END_DEBUG
mutex_enter(&sc->sc_mtx);
ocb = sbp_get_ocb(sdev);
mutex_exit(&sc->sc_mtx);
if (ocb == NULL) {
xs->error = XS_REQUEUE;
if (sdev->freeze == 0) {
scsipi_periph_freeze(sdev->periph, 1);
sdev->freeze++;
}
scsipi_done(xs);
return;
}
ocb->flags = OCB_ACT_CMD;
ocb->sdev = sdev;
ocb->xs = xs;
ocb->orb[0] = htonl(1 << 31);
ocb->orb[1] = 0;
ocb->orb[2] = htonl(((sc->sc_fd.fc->nodeid | FWLOCALBUS) << 16));
ocb->orb[3] = htonl(ocb->bus_addr + IND_PTR_OFFSET);
speed = uimin(target->fwdev->speed, max_speed);
ocb->orb[4] =
htonl(ORB_NOTIFY | ORB_CMD_SPD(speed) | ORB_CMD_MAXP(speed + 7));
if ((xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) ==
XS_CTL_DATA_IN) {
ocb->orb[4] |= htonl(ORB_CMD_IN);
flag = BUS_DMA_READ;
} else
flag = BUS_DMA_WRITE;
cdb = xs->cmd;
memcpy((void *)&ocb->orb[5], cdb, xs->cmdlen);
if (xs->datalen > 0) {
error = bus_dmamap_load(sc->sc_dmat, ocb->dmamap,
xs->data, xs->datalen, NULL, BUS_DMA_NOWAIT | flag);
if (error) {
aprint_error_dev(sc->sc_fd.dev,
"DMA map load error %d\n", error);
xs->error = XS_DRIVER_STUFFUP;
scsipi_done(xs);
} else
sbp_execute_ocb(ocb, ocb->dmamap->dm_segs,
ocb->dmamap->dm_nsegs);
} else
sbp_execute_ocb(ocb, NULL, 0);
return;
}
static void
sbp_execute_ocb(struct sbp_ocb *ocb, bus_dma_segment_t *segments, int seg)
{
struct sbp_ocb *prev;
bus_dma_segment_t *s;
int i;
SBP_DEBUG(2)
printf("sbp_execute_ocb: seg %d", seg);
for (i = 0; i < seg; i++)
printf(", %jx:%jd", (uintmax_t)segments[i].ds_addr,
(uintmax_t)segments[i].ds_len);
printf("\n");
END_DEBUG
if (seg == 1) {
s = segments;
if (s->ds_len > SBP_SEG_MAX)
panic("ds_len > SBP_SEG_MAX, fix busdma code");
ocb->orb[3] = htonl(s->ds_addr);
ocb->orb[4] |= htonl(s->ds_len);
} else if (seg > 1) {
for (i = 0; i < seg; i++) {
s = &segments[i];
SBP_DEBUG(0)
if (s->ds_len < 16)
printf("sbp_execute_ocb: warning, "
"segment length(%jd) is less than 16."
"(seg=%d/%d)\n",
(uintmax_t)s->ds_len, i + 1, seg);
END_DEBUG
if (s->ds_len > SBP_SEG_MAX)
panic("ds_len > SBP_SEG_MAX, fix busdma code");
ocb->ind_ptr[i].hi = htonl(s->ds_len << 16);
ocb->ind_ptr[i].lo = htonl(s->ds_addr);
}
ocb->orb[4] |= htonl(ORB_CMD_PTBL | seg);
}
if (seg > 0) {
struct sbp_softc *sc = ocb->sdev->target->sbp;
const int flag = (ntohl(ocb->orb[4]) & ORB_CMD_IN) ?
BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE;
bus_dmamap_sync(sc->sc_dmat, ocb->dmamap,
0, ocb->dmamap->dm_mapsize, flag);
}
prev = sbp_enqueue_ocb(ocb->sdev, ocb);
SBP_ORB_DMA_SYNC(ocb->sdev->dma, ocb->index, BUS_DMASYNC_PREWRITE);
if (use_doorbell) {
if (prev == NULL) {
if (ocb->sdev->last_ocb != NULL)
sbp_doorbell(ocb->sdev);
else
sbp_orb_pointer(ocb->sdev, ocb);
}
} else
if (prev == NULL || (ocb->sdev->flags & ORB_LINK_DEAD) != 0) {
ocb->sdev->flags &= ~ORB_LINK_DEAD;
sbp_orb_pointer(ocb->sdev, ocb);
}
}
static struct sbp_ocb *
sbp_dequeue_ocb(struct sbp_dev *sdev, struct sbp_status *sbp_status)
{
struct sbp_softc *sc = sdev->target->sbp;
struct sbp_ocb *ocb;
struct sbp_ocb *next;
int order = 0;
SBP_DEBUG(1)
printf("%s:%s:%s: 0x%08x src %d\n", device_xname(sc->sc_fd.dev),
__func__, sdev->bustgtlun, ntohl(sbp_status->orb_lo),
sbp_status->src);
END_DEBUG
mutex_enter(&sc->sc_mtx);
for (ocb = STAILQ_FIRST(&sdev->ocbs); ocb != NULL; ocb = next) {
next = STAILQ_NEXT(ocb, ocb);
if (OCB_MATCH(ocb, sbp_status)) {
SBP_ORB_DMA_SYNC(sdev->dma, ocb->index,
BUS_DMASYNC_POSTWRITE);
STAILQ_REMOVE(&sdev->ocbs, ocb, sbp_ocb, ocb);
if (ocb->xs != NULL)
callout_stop(&ocb->xs->xs_callout);
if (ntohl(ocb->orb[4]) & 0xffff) {
const int flag =
(ntohl(ocb->orb[4]) & ORB_CMD_IN) ?
BUS_DMASYNC_POSTREAD :
BUS_DMASYNC_POSTWRITE;
bus_dmamap_sync(sc->sc_dmat, ocb->dmamap,
0, ocb->dmamap->dm_mapsize, flag);
bus_dmamap_unload(sc->sc_dmat, ocb->dmamap);
}
if (!use_doorbell) {
if (sbp_status->src == SRC_NO_NEXT) {
if (next != NULL)
sbp_orb_pointer(sdev, next);
else if (order > 0)
sdev->flags |= ORB_LINK_DEAD;
}
}
break;
} else
order++;
}
mutex_exit(&sc->sc_mtx);
if (ocb && use_doorbell) {
if (sdev->last_ocb != NULL)
sbp_free_ocb(sdev, sdev->last_ocb);
sdev->last_ocb = ocb;
if (next != NULL &&
sbp_status->src == SRC_NO_NEXT)
sbp_doorbell(sdev);
}
SBP_DEBUG(0)
if (ocb && order > 0)
printf("%s:%s:%s: unordered execution order:%d\n",
device_xname(sc->sc_fd.dev), __func__, sdev->bustgtlun,
order);
END_DEBUG
return ocb;
}
static struct sbp_ocb *
sbp_enqueue_ocb(struct sbp_dev *sdev, struct sbp_ocb *ocb)
{
struct sbp_softc *sc = sdev->target->sbp;
struct sbp_ocb *tocb, *prev, *prev2;
SBP_DEBUG(1)
printf("%s:%s:%s: 0x%08jx\n", device_xname(sc->sc_fd.dev),
__func__, sdev->bustgtlun, (uintmax_t)ocb->bus_addr);
END_DEBUG
mutex_enter(&sc->sc_mtx);
prev = NULL;
STAILQ_FOREACH(tocb, &sdev->ocbs, ocb)
prev = tocb;
prev2 = prev;
STAILQ_INSERT_TAIL(&sdev->ocbs, ocb, ocb);
mutex_exit(&sc->sc_mtx);
callout_reset(&ocb->xs->xs_callout, mstohz(ocb->xs->timeout),
sbp_timeout, ocb);
if (use_doorbell && prev == NULL)
prev2 = sdev->last_ocb;
if (prev2 != NULL) {
SBP_DEBUG(2)
printf("linking chain 0x%jx -> 0x%jx\n",
(uintmax_t)prev2->bus_addr, (uintmax_t)ocb->bus_addr);
END_DEBUG
*(volatile uint32_t *)&prev2->orb[1] = htonl(ocb->bus_addr);
*(volatile uint32_t *)&prev2->orb[0] = 0;
}
return prev;
}
static struct sbp_ocb *
sbp_get_ocb(struct sbp_dev *sdev)
{
struct sbp_softc *sc = sdev->target->sbp;
struct sbp_ocb *ocb;
KASSERT(mutex_owned(&sc->sc_mtx));
ocb = STAILQ_FIRST(&sdev->free_ocbs);
if (ocb == NULL) {
sdev->flags |= ORB_SHORTAGE;
aprint_error_dev(sc->sc_fd.dev,
"ocb shortage!!!\n");
return NULL;
}
STAILQ_REMOVE_HEAD(&sdev->free_ocbs, ocb);
ocb->xs = NULL;
return ocb;
}
static void
sbp_free_ocb(struct sbp_dev *sdev, struct sbp_ocb *ocb)
{
struct sbp_softc *sc = sdev->target->sbp;
int count;
ocb->flags = 0;
ocb->xs = NULL;
mutex_enter(&sc->sc_mtx);
STAILQ_INSERT_TAIL(&sdev->free_ocbs, ocb, ocb);
mutex_exit(&sc->sc_mtx);
if (sdev->flags & ORB_SHORTAGE) {
sdev->flags &= ~ORB_SHORTAGE;
count = sdev->freeze;
sdev->freeze = 0;
if (sdev->periph)
scsipi_periph_thaw(sdev->periph, count);
scsipi_channel_thaw(&sc->sc_channel, 0);
}
}
static void
sbp_abort_ocb(struct sbp_ocb *ocb, int status)
{
struct sbp_softc *sc;
struct sbp_dev *sdev;
sdev = ocb->sdev;
sc = sdev->target->sbp;
SBP_DEBUG(0)
printf("%s:%s:%s: sbp_abort_ocb 0x%jx\n", device_xname(sc->sc_fd.dev),
__func__, sdev->bustgtlun, (uintmax_t)ocb->bus_addr);
END_DEBUG
SBP_DEBUG(1)
if (ocb->xs != NULL)
sbp_print_scsi_cmd(ocb);
END_DEBUG
if (ntohl(ocb->orb[4]) & 0xffff) {
const int flag = (ntohl(ocb->orb[4]) & ORB_CMD_IN) ?
BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE;
bus_dmamap_sync(sc->sc_dmat, ocb->dmamap,
0, ocb->dmamap->dm_mapsize, flag);
bus_dmamap_unload(sc->sc_dmat, ocb->dmamap);
}
if (ocb->xs != NULL) {
callout_stop(&ocb->xs->xs_callout);
ocb->xs->error = status;
scsipi_done(ocb->xs);
}
sbp_free_ocb(sdev, ocb);
}
static void
sbp_abort_all_ocbs(struct sbp_dev *sdev, int status)
{
struct sbp_softc *sc = sdev->target->sbp;
struct sbp_ocb *ocb, *next;
STAILQ_HEAD(, sbp_ocb) temp;
mutex_enter(&sc->sc_mtx);
STAILQ_INIT(&temp);
STAILQ_CONCAT(&temp, &sdev->ocbs);
STAILQ_INIT(&sdev->ocbs);
mutex_exit(&sc->sc_mtx);
for (ocb = STAILQ_FIRST(&temp); ocb != NULL; ocb = next) {
next = STAILQ_NEXT(ocb, ocb);
sbp_abort_ocb(ocb, status);
}
if (sdev->last_ocb != NULL) {
sbp_free_ocb(sdev, sdev->last_ocb);
sdev->last_ocb = NULL;
}
}