#include "aic7xxx_osm.h"
#include "aic7xxx_inline.h"
#include "aicasm/aicasm_insformat.h"
static const char *const ahc_chip_names[] = …;
struct ahc_hard_error_entry { … };
static const struct ahc_hard_error_entry ahc_hard_errors[] = …;
static const u_int num_errors = …;
static const struct ahc_phase_table_entry ahc_phase_table[] = …;
static const u_int num_phases = …;
static const struct ahc_syncrate ahc_syncrates[] = …;
#include "aic7xxx_seq.h"
static void ahc_force_renegotiation(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo);
static struct ahc_tmode_tstate*
ahc_alloc_tstate(struct ahc_softc *ahc,
u_int scsi_id, char channel);
#ifdef AHC_TARGET_MODE
static void ahc_free_tstate(struct ahc_softc *ahc,
u_int scsi_id, char channel, int force);
#endif
static const struct ahc_syncrate*
ahc_devlimited_syncrate(struct ahc_softc *ahc,
struct ahc_initiator_tinfo *,
u_int *period,
u_int *ppr_options,
role_t role);
static void ahc_update_pending_scbs(struct ahc_softc *ahc);
static void ahc_fetch_devinfo(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo);
static void ahc_scb_devinfo(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo,
struct scb *scb);
static void ahc_assert_atn(struct ahc_softc *ahc);
static void ahc_setup_initiator_msgout(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo,
struct scb *scb);
static void ahc_build_transfer_msg(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo);
static void ahc_construct_sdtr(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo,
u_int period, u_int offset);
static void ahc_construct_wdtr(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo,
u_int bus_width);
static void ahc_construct_ppr(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo,
u_int period, u_int offset,
u_int bus_width, u_int ppr_options);
static void ahc_clear_msg_state(struct ahc_softc *ahc);
static void ahc_handle_proto_violation(struct ahc_softc *ahc);
static void ahc_handle_message_phase(struct ahc_softc *ahc);
ahc_msgtype;
static int ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type,
u_int msgval, int full);
static int ahc_parse_msg(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo);
static int ahc_handle_msg_reject(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo);
static void ahc_handle_ign_wide_residue(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo);
static void ahc_reinitialize_dataptrs(struct ahc_softc *ahc);
static void ahc_handle_devreset(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo,
cam_status status, char *message,
int verbose_level);
#ifdef AHC_TARGET_MODE
static void ahc_setup_target_msgin(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo,
struct scb *scb);
#endif
static bus_dmamap_callback_t ahc_dmamap_cb;
static void ahc_build_free_scb_list(struct ahc_softc *ahc);
static int ahc_init_scbdata(struct ahc_softc *ahc);
static void ahc_fini_scbdata(struct ahc_softc *ahc);
static void ahc_qinfifo_requeue(struct ahc_softc *ahc,
struct scb *prev_scb,
struct scb *scb);
static int ahc_qinfifo_count(struct ahc_softc *ahc);
static u_int ahc_rem_scb_from_disc_list(struct ahc_softc *ahc,
u_int prev, u_int scbptr);
static void ahc_add_curscb_to_free_list(struct ahc_softc *ahc);
static u_int ahc_rem_wscb(struct ahc_softc *ahc,
u_int scbpos, u_int prev);
static void ahc_reset_current_bus(struct ahc_softc *ahc);
#ifdef AHC_DUMP_SEQ
static void ahc_dumpseq(struct ahc_softc *ahc);
#endif
static int ahc_loadseq(struct ahc_softc *ahc);
static int ahc_check_patch(struct ahc_softc *ahc,
const struct patch **start_patch,
u_int start_instr, u_int *skip_addr);
static void ahc_download_instr(struct ahc_softc *ahc,
u_int instrptr, uint8_t *dconsts);
#ifdef AHC_TARGET_MODE
static void ahc_queue_lstate_event(struct ahc_softc *ahc,
struct ahc_tmode_lstate *lstate,
u_int initiator_id,
u_int event_type,
u_int event_arg);
static void ahc_update_scsiid(struct ahc_softc *ahc,
u_int targid_mask);
static int ahc_handle_target_cmd(struct ahc_softc *ahc,
struct target_cmd *cmd);
#endif
static u_int ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl);
static void ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl);
static void ahc_busy_tcl(struct ahc_softc *ahc,
u_int tcl, u_int busyid);
static void ahc_run_untagged_queues(struct ahc_softc *ahc);
static void ahc_run_untagged_queue(struct ahc_softc *ahc,
struct scb_tailq *queue);
static void ahc_alloc_scbs(struct ahc_softc *ahc);
static void ahc_shutdown(void *arg);
static void ahc_clear_intstat(struct ahc_softc *ahc);
static void ahc_run_qoutfifo(struct ahc_softc *ahc);
#ifdef AHC_TARGET_MODE
static void ahc_run_tqinfifo(struct ahc_softc *ahc, int paused);
#endif
static void ahc_handle_brkadrint(struct ahc_softc *ahc);
static void ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat);
static void ahc_handle_scsiint(struct ahc_softc *ahc,
u_int intstat);
static void ahc_clear_critical_section(struct ahc_softc *ahc);
static void ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb);
static int ahc_abort_scbs(struct ahc_softc *ahc, int target,
char channel, int lun, u_int tag,
role_t role, uint32_t status);
static void ahc_calc_residual(struct ahc_softc *ahc,
struct scb *scb);
static inline void ahc_freeze_untagged_queues(struct ahc_softc *ahc);
static inline void ahc_release_untagged_queues(struct ahc_softc *ahc);
static inline void
ahc_freeze_untagged_queues(struct ahc_softc *ahc)
{ … }
static inline void
ahc_release_untagged_queues(struct ahc_softc *ahc)
{ … }
static void
ahc_pause_bug_fix(struct ahc_softc *ahc)
{ … }
int
ahc_is_paused(struct ahc_softc *ahc)
{ … }
void
ahc_pause(struct ahc_softc *ahc)
{ … }
void
ahc_unpause(struct ahc_softc *ahc)
{ … }
static struct ahc_dma_seg *
ahc_sg_bus_to_virt(struct scb *scb, uint32_t sg_busaddr)
{ … }
static uint32_t
ahc_sg_virt_to_bus(struct scb *scb, struct ahc_dma_seg *sg)
{ … }
static uint32_t
ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index)
{ … }
static void
ahc_sync_scb(struct ahc_softc *ahc, struct scb *scb, int op)
{ … }
void
ahc_sync_sglist(struct ahc_softc *ahc, struct scb *scb, int op)
{ … }
#ifdef AHC_TARGET_MODE
static uint32_t
ahc_targetcmd_offset(struct ahc_softc *ahc, u_int index)
{
return (((uint8_t *)&ahc->targetcmds[index]) - ahc->qoutfifo);
}
#endif
static void
ahc_update_residual(struct ahc_softc *ahc, struct scb *scb)
{ … }
struct ahc_initiator_tinfo *
ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id,
u_int remote_id, struct ahc_tmode_tstate **tstate)
{ … }
uint16_t
ahc_inw(struct ahc_softc *ahc, u_int port)
{ … }
void
ahc_outw(struct ahc_softc *ahc, u_int port, u_int value)
{ … }
uint32_t
ahc_inl(struct ahc_softc *ahc, u_int port)
{ … }
void
ahc_outl(struct ahc_softc *ahc, u_int port, uint32_t value)
{ … }
uint64_t
ahc_inq(struct ahc_softc *ahc, u_int port)
{ … }
void
ahc_outq(struct ahc_softc *ahc, u_int port, uint64_t value)
{ … }
struct scb *
ahc_get_scb(struct ahc_softc *ahc)
{ … }
void
ahc_free_scb(struct ahc_softc *ahc, struct scb *scb)
{ … }
struct scb *
ahc_lookup_scb(struct ahc_softc *ahc, u_int tag)
{ … }
static void
ahc_swap_with_next_hscb(struct ahc_softc *ahc, struct scb *scb)
{ … }
void
ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb)
{ … }
struct scsi_sense_data *
ahc_get_sense_buf(struct ahc_softc *ahc, struct scb *scb)
{ … }
static uint32_t
ahc_get_sense_bufaddr(struct ahc_softc *ahc, struct scb *scb)
{ … }
static void
ahc_sync_qoutfifo(struct ahc_softc *ahc, int op)
{ … }
static void
ahc_sync_tqinfifo(struct ahc_softc *ahc, int op)
{ … }
#define AHC_RUN_QOUTFIFO …
#define AHC_RUN_TQINFIFO …
static u_int
ahc_check_cmdcmpltqueues(struct ahc_softc *ahc)
{ … }
int
ahc_intr(struct ahc_softc *ahc)
{ … }
static void
ahc_restart(struct ahc_softc *ahc)
{ … }
static void
ahc_run_qoutfifo(struct ahc_softc *ahc)
{ … }
static void
ahc_run_untagged_queues(struct ahc_softc *ahc)
{ … }
static void
ahc_run_untagged_queue(struct ahc_softc *ahc, struct scb_tailq *queue)
{ … }
static void
ahc_handle_brkadrint(struct ahc_softc *ahc)
{ … }
static void
ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
{ … }
static void
ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
{ … }
static void
ahc_force_renegotiation(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
{ … }
#define AHC_MAX_STEPS …
static void
ahc_clear_critical_section(struct ahc_softc *ahc)
{ … }
static void
ahc_clear_intstat(struct ahc_softc *ahc)
{ … }
#ifdef AHC_DEBUG
uint32_t ahc_debug = …;
#endif
#if 0
static void
ahc_print_scb(struct scb *scb)
{
int i;
struct hardware_scb *hscb = scb->hscb;
printk("scb:%p control:0x%x scsiid:0x%x lun:%d cdb_len:%d\n",
(void *)scb,
hscb->control,
hscb->scsiid,
hscb->lun,
hscb->cdb_len);
printk("Shared Data: ");
for (i = 0; i < sizeof(hscb->shared_data.cdb); i++)
printk("%#02x", hscb->shared_data.cdb[i]);
printk(" dataptr:%#x datacnt:%#x sgptr:%#x tag:%#x\n",
ahc_le32toh(hscb->dataptr),
ahc_le32toh(hscb->datacnt),
ahc_le32toh(hscb->sgptr),
hscb->tag);
if (scb->sg_count > 0) {
for (i = 0; i < scb->sg_count; i++) {
printk("sg[%d] - Addr 0x%x%x : Length %d\n",
i,
(ahc_le32toh(scb->sg_list[i].len) >> 24
& SG_HIGH_ADDR_BITS),
ahc_le32toh(scb->sg_list[i].addr),
ahc_le32toh(scb->sg_list[i].len));
}
}
}
#endif
static struct ahc_tmode_tstate *
ahc_alloc_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel)
{ … }
#ifdef AHC_TARGET_MODE
static void
ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
{
struct ahc_tmode_tstate *tstate;
if (((channel == 'B' && scsi_id == ahc->our_id_b)
|| (channel == 'A' && scsi_id == ahc->our_id))
&& force == FALSE)
return;
if (channel == 'B')
scsi_id += 8;
tstate = ahc->enabled_targets[scsi_id];
kfree(tstate);
ahc->enabled_targets[scsi_id] = NULL;
}
#endif
static const struct ahc_syncrate *
ahc_devlimited_syncrate(struct ahc_softc *ahc,
struct ahc_initiator_tinfo *tinfo,
u_int *period, u_int *ppr_options, role_t role)
{ … }
const struct ahc_syncrate *
ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
u_int *ppr_options, u_int maxsync)
{ … }
u_int
ahc_find_period(struct ahc_softc *ahc, u_int scsirate, u_int maxsync)
{ … }
static void
ahc_validate_offset(struct ahc_softc *ahc,
struct ahc_initiator_tinfo *tinfo,
const struct ahc_syncrate *syncrate,
u_int *offset, int wide, role_t role)
{ … }
static void
ahc_validate_width(struct ahc_softc *ahc, struct ahc_initiator_tinfo *tinfo,
u_int *bus_width, role_t role)
{ … }
int
ahc_update_neg_request(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
struct ahc_tmode_tstate *tstate,
struct ahc_initiator_tinfo *tinfo, ahc_neg_type neg_type)
{ … }
void
ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
const struct ahc_syncrate *syncrate, u_int period,
u_int offset, u_int ppr_options, u_int type, int paused)
{ … }
void
ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
u_int width, u_int type, int paused)
{ … }
static void
ahc_set_tags(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
struct ahc_devinfo *devinfo, ahc_queue_alg alg)
{ … }
static void
ahc_update_pending_scbs(struct ahc_softc *ahc)
{ … }
static void
ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
{ … }
static const struct ahc_phase_table_entry*
ahc_lookup_phase_entry(int phase)
{ … }
void
ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int our_id, u_int target,
u_int lun, char channel, role_t role)
{ … }
void
ahc_print_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
{ … }
static void
ahc_scb_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
struct scb *scb)
{ … }
static void
ahc_assert_atn(struct ahc_softc *ahc)
{ … }
static void
ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
struct scb *scb)
{ … }
static void
ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
{ … }
static void
ahc_construct_sdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
u_int period, u_int offset)
{ … }
static void
ahc_construct_wdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
u_int bus_width)
{ … }
static void
ahc_construct_ppr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
u_int period, u_int offset, u_int bus_width,
u_int ppr_options)
{ … }
static void
ahc_clear_msg_state(struct ahc_softc *ahc)
{ … }
static void
ahc_handle_proto_violation(struct ahc_softc *ahc)
{ … }
static void
ahc_handle_message_phase(struct ahc_softc *ahc)
{ … }
static int
ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type, u_int msgval, int full)
{ … }
static int
ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
{ … }
static int
ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
{ … }
static void
ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
{ … }
static void
ahc_reinitialize_dataptrs(struct ahc_softc *ahc)
{ … }
static void
ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
cam_status status, char *message, int verbose_level)
{ … }
#ifdef AHC_TARGET_MODE
static void
ahc_setup_target_msgin(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
struct scb *scb)
{
ahc->msgout_index = 0;
ahc->msgout_len = 0;
if (scb != NULL && (scb->flags & SCB_AUTO_NEGOTIATE) != 0)
ahc_build_transfer_msg(ahc, devinfo);
else
panic("ahc_intr: AWAITING target message with no message");
ahc->msgout_index = 0;
ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
}
#endif
struct ahc_softc *
ahc_alloc(void *platform_arg, char *name)
{ … }
int
ahc_softc_init(struct ahc_softc *ahc)
{ … }
void
ahc_set_unit(struct ahc_softc *ahc, int unit)
{ … }
void
ahc_set_name(struct ahc_softc *ahc, char *name)
{ … }
void
ahc_free(struct ahc_softc *ahc)
{ … }
static void
ahc_shutdown(void *arg)
{ … }
int
ahc_reset(struct ahc_softc *ahc, int reinit)
{ … }
int
ahc_probe_scbs(struct ahc_softc *ahc) { … }
static void
ahc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{ … }
static void
ahc_build_free_scb_list(struct ahc_softc *ahc)
{ … }
static int
ahc_init_scbdata(struct ahc_softc *ahc)
{ … }
static void
ahc_fini_scbdata(struct ahc_softc *ahc)
{ … }
static void
ahc_alloc_scbs(struct ahc_softc *ahc)
{ … }
void
ahc_controller_info(struct ahc_softc *ahc, char *buf)
{ … }
int
ahc_chip_init(struct ahc_softc *ahc)
{ … }
int
ahc_init(struct ahc_softc *ahc)
{ … }
void
ahc_intr_enable(struct ahc_softc *ahc, int enable)
{ … }
void
ahc_pause_and_flushwork(struct ahc_softc *ahc)
{ … }
int __maybe_unused
ahc_suspend(struct ahc_softc *ahc)
{ … }
int __maybe_unused
ahc_resume(struct ahc_softc *ahc)
{ … }
static u_int
ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl)
{ … }
static void
ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl)
{ … }
static void
ahc_busy_tcl(struct ahc_softc *ahc, u_int tcl, u_int scbid)
{ … }
int
ahc_match_scb(struct ahc_softc *ahc, struct scb *scb, int target,
char channel, int lun, u_int tag, role_t role)
{ … }
static void
ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
{ … }
void
ahc_qinfifo_requeue_tail(struct ahc_softc *ahc, struct scb *scb)
{ … }
static void
ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb,
struct scb *scb)
{ … }
static int
ahc_qinfifo_count(struct ahc_softc *ahc)
{ … }
int
ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
int lun, u_int tag, role_t role, uint32_t status,
ahc_search_action action)
{ … }
int
ahc_search_untagged_queues(struct ahc_softc *ahc, ahc_io_ctx_t ctx,
int target, char channel, int lun, uint32_t status,
ahc_search_action action)
{ … }
int
ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel,
int lun, u_int tag, int stop_on_first, int remove,
int save_state)
{ … }
static u_int
ahc_rem_scb_from_disc_list(struct ahc_softc *ahc, u_int prev, u_int scbptr)
{ … }
static void
ahc_add_curscb_to_free_list(struct ahc_softc *ahc)
{ … }
static u_int
ahc_rem_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
{ … }
static int
ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel,
int lun, u_int tag, role_t role, uint32_t status)
{ … }
static void
ahc_reset_current_bus(struct ahc_softc *ahc)
{ … }
int
ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
{ … }
static void
ahc_calc_residual(struct ahc_softc *ahc, struct scb *scb)
{ … }
#ifdef AHC_TARGET_MODE
static void
ahc_queue_lstate_event(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate,
u_int initiator_id, u_int event_type, u_int event_arg)
{
struct ahc_tmode_event *event;
int pending;
xpt_freeze_devq(lstate->path, 1);
if (lstate->event_w_idx >= lstate->event_r_idx)
pending = lstate->event_w_idx - lstate->event_r_idx;
else
pending = AHC_TMODE_EVENT_BUFFER_SIZE + 1
- (lstate->event_r_idx - lstate->event_w_idx);
if (event_type == EVENT_TYPE_BUS_RESET
|| event_type == TARGET_RESET) {
lstate->event_r_idx = 0;
lstate->event_w_idx = 0;
xpt_release_devq(lstate->path, pending, FALSE);
}
if (pending == AHC_TMODE_EVENT_BUFFER_SIZE) {
xpt_print_path(lstate->path);
printk("immediate event %x:%x lost\n",
lstate->event_buffer[lstate->event_r_idx].event_type,
lstate->event_buffer[lstate->event_r_idx].event_arg);
lstate->event_r_idx++;
if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
lstate->event_r_idx = 0;
xpt_release_devq(lstate->path, 1, FALSE);
}
event = &lstate->event_buffer[lstate->event_w_idx];
event->initiator_id = initiator_id;
event->event_type = event_type;
event->event_arg = event_arg;
lstate->event_w_idx++;
if (lstate->event_w_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
lstate->event_w_idx = 0;
}
void
ahc_send_lstate_events(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate)
{
struct ccb_hdr *ccbh;
struct ccb_immed_notify *inot;
while (lstate->event_r_idx != lstate->event_w_idx
&& (ccbh = SLIST_FIRST(&lstate->immed_notifies)) != NULL) {
struct ahc_tmode_event *event;
event = &lstate->event_buffer[lstate->event_r_idx];
SLIST_REMOVE_HEAD(&lstate->immed_notifies, sim_links.sle);
inot = (struct ccb_immed_notify *)ccbh;
switch (event->event_type) {
case EVENT_TYPE_BUS_RESET:
ccbh->status = CAM_SCSI_BUS_RESET|CAM_DEV_QFRZN;
break;
default:
ccbh->status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN;
inot->message_args[0] = event->event_type;
inot->message_args[1] = event->event_arg;
break;
}
inot->initiator_id = event->initiator_id;
inot->sense_len = 0;
xpt_done((union ccb *)inot);
lstate->event_r_idx++;
if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
lstate->event_r_idx = 0;
}
}
#endif
#ifdef AHC_DUMP_SEQ
void
ahc_dumpseq(struct ahc_softc* ahc)
{
int i;
ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
ahc_outb(ahc, SEQADDR0, 0);
ahc_outb(ahc, SEQADDR1, 0);
for (i = 0; i < ahc->instruction_ram_size; i++) {
uint8_t ins_bytes[4];
ahc_insb(ahc, SEQRAM, ins_bytes, 4);
printk("0x%08x\n", ins_bytes[0] << 24
| ins_bytes[1] << 16
| ins_bytes[2] << 8
| ins_bytes[3]);
}
}
#endif
static int
ahc_loadseq(struct ahc_softc *ahc)
{ … }
static int
ahc_check_patch(struct ahc_softc *ahc, const struct patch **start_patch,
u_int start_instr, u_int *skip_addr)
{ … }
static void
ahc_download_instr(struct ahc_softc *ahc, u_int instrptr, uint8_t *dconsts)
{ … }
int
ahc_print_register(const ahc_reg_parse_entry_t *table, u_int num_entries,
const char *name, u_int address, u_int value,
u_int *cur_column, u_int wrap_point)
{ … }
void
ahc_dump_card_state(struct ahc_softc *ahc)
{ … }
#ifdef AHC_TARGET_MODE
cam_status
ahc_find_tmode_devs(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb,
struct ahc_tmode_tstate **tstate,
struct ahc_tmode_lstate **lstate,
int notfound_failure)
{
if ((ahc->features & AHC_TARGETMODE) == 0)
return (CAM_REQ_INVALID);
if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD
&& ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
*tstate = NULL;
*lstate = ahc->black_hole;
} else {
u_int max_id;
max_id = (ahc->features & AHC_WIDE) ? 16 : 8;
if (ccb->ccb_h.target_id >= max_id)
return (CAM_TID_INVALID);
if (ccb->ccb_h.target_lun >= AHC_NUM_LUNS)
return (CAM_LUN_INVALID);
*tstate = ahc->enabled_targets[ccb->ccb_h.target_id];
*lstate = NULL;
if (*tstate != NULL)
*lstate =
(*tstate)->enabled_luns[ccb->ccb_h.target_lun];
}
if (notfound_failure != 0 && *lstate == NULL)
return (CAM_PATH_INVALID);
return (CAM_REQ_CMP);
}
void
ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
{
struct ahc_tmode_tstate *tstate;
struct ahc_tmode_lstate *lstate;
struct ccb_en_lun *cel;
cam_status status;
u_long s;
u_int target;
u_int lun;
u_int target_mask;
u_int our_id;
int error;
char channel;
status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, &lstate,
FALSE);
if (status != CAM_REQ_CMP) {
ccb->ccb_h.status = status;
return;
}
if (cam_sim_bus(sim) == 0)
our_id = ahc->our_id;
else
our_id = ahc->our_id_b;
if (ccb->ccb_h.target_id != our_id) {
if ((ahc->features & AHC_MULTIROLE) != 0) {
if ((ahc->features & AHC_MULTI_TID) != 0
&& (ahc->flags & AHC_INITIATORROLE) != 0) {
status = CAM_TID_INVALID;
} else if ((ahc->flags & AHC_INITIATORROLE) != 0
|| ahc->enabled_luns > 0) {
status = CAM_TID_INVALID;
}
} else if ((ahc->features & AHC_MULTI_TID) == 0
&& ahc->enabled_luns > 0) {
status = CAM_TID_INVALID;
}
}
if (status != CAM_REQ_CMP) {
ccb->ccb_h.status = status;
return;
}
if ((ahc->flags & AHC_TARGETROLE) == 0
&& ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
u_long s;
ahc_flag saved_flags;
printk("Configuring Target Mode\n");
ahc_lock(ahc, &s);
if (LIST_FIRST(&ahc->pending_scbs) != NULL) {
ccb->ccb_h.status = CAM_BUSY;
ahc_unlock(ahc, &s);
return;
}
saved_flags = ahc->flags;
ahc->flags |= AHC_TARGETROLE;
if ((ahc->features & AHC_MULTIROLE) == 0)
ahc->flags &= ~AHC_INITIATORROLE;
ahc_pause(ahc);
error = ahc_loadseq(ahc);
if (error != 0) {
ahc->flags = saved_flags;
(void)ahc_loadseq(ahc);
ahc_restart(ahc);
ahc_unlock(ahc, &s);
ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
return;
}
ahc_restart(ahc);
ahc_unlock(ahc, &s);
}
cel = &ccb->cel;
target = ccb->ccb_h.target_id;
lun = ccb->ccb_h.target_lun;
channel = SIM_CHANNEL(ahc, sim);
target_mask = 0x01 << target;
if (channel == 'B')
target_mask <<= 8;
if (cel->enable != 0) {
u_int scsiseq;
if (lstate != NULL) {
xpt_print_path(ccb->ccb_h.path);
printk("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;
printk("Non-zero Group Codes\n");
return;
}
if (target != CAM_TARGET_WILDCARD && tstate == NULL) {
tstate = ahc_alloc_tstate(ahc, target, channel);
if (tstate == NULL) {
xpt_print_path(ccb->ccb_h.path);
printk("Couldn't allocate tstate\n");
ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
return;
}
}
lstate = kzalloc(sizeof(*lstate), GFP_ATOMIC);
if (lstate == NULL) {
xpt_print_path(ccb->ccb_h.path);
printk("Couldn't allocate lstate\n");
ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
return;
}
status = xpt_create_path(&lstate->path, 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) {
kfree(lstate);
xpt_print_path(ccb->ccb_h.path);
printk("Couldn't allocate path\n");
ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
return;
}
SLIST_INIT(&lstate->accept_tios);
SLIST_INIT(&lstate->immed_notifies);
ahc_lock(ahc, &s);
ahc_pause(ahc);
if (target != CAM_TARGET_WILDCARD) {
tstate->enabled_luns[lun] = lstate;
ahc->enabled_luns++;
if ((ahc->features & AHC_MULTI_TID) != 0) {
u_int targid_mask;
targid_mask = ahc_inb(ahc, TARGID)
| (ahc_inb(ahc, TARGID + 1) << 8);
targid_mask |= target_mask;
ahc_outb(ahc, TARGID, targid_mask);
ahc_outb(ahc, TARGID+1, (targid_mask >> 8));
ahc_update_scsiid(ahc, targid_mask);
} else {
u_int our_id;
char channel;
channel = SIM_CHANNEL(ahc, sim);
our_id = SIM_SCSI_ID(ahc, sim);
if (target != our_id) {
u_int sblkctl;
char cur_channel;
int swap;
sblkctl = ahc_inb(ahc, SBLKCTL);
cur_channel = (sblkctl & SELBUSB)
? 'B' : 'A';
if ((ahc->features & AHC_TWIN) == 0)
cur_channel = 'A';
swap = cur_channel != channel;
if (channel == 'A')
ahc->our_id = target;
else
ahc->our_id_b = target;
if (swap)
ahc_outb(ahc, SBLKCTL,
sblkctl ^ SELBUSB);
ahc_outb(ahc, SCSIID, target);
if (swap)
ahc_outb(ahc, SBLKCTL, sblkctl);
}
}
} else
ahc->black_hole = lstate;
if (ahc->black_hole != NULL && ahc->enabled_luns > 0) {
scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
scsiseq |= ENSELI;
ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
scsiseq = ahc_inb(ahc, SCSISEQ);
scsiseq |= ENSELI;
ahc_outb(ahc, SCSISEQ, scsiseq);
}
ahc_unpause(ahc);
ahc_unlock(ahc, &s);
ccb->ccb_h.status = CAM_REQ_CMP;
xpt_print_path(ccb->ccb_h.path);
printk("Lun now enabled for target mode\n");
} else {
struct scb *scb;
int i, empty;
if (lstate == NULL) {
ccb->ccb_h.status = CAM_LUN_INVALID;
return;
}
ahc_lock(ahc, &s);
ccb->ccb_h.status = CAM_REQ_CMP;
LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
struct ccb_hdr *ccbh;
ccbh = &scb->io_ctx->ccb_h;
if (ccbh->func_code == XPT_CONT_TARGET_IO
&& !xpt_path_comp(ccbh->path, ccb->ccb_h.path)){
printk("CTIO pending\n");
ccb->ccb_h.status = CAM_REQ_INVALID;
ahc_unlock(ahc, &s);
return;
}
}
if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
printk("ATIOs pending\n");
ccb->ccb_h.status = CAM_REQ_INVALID;
}
if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
printk("INOTs pending\n");
ccb->ccb_h.status = CAM_REQ_INVALID;
}
if (ccb->ccb_h.status != CAM_REQ_CMP) {
ahc_unlock(ahc, &s);
return;
}
xpt_print_path(ccb->ccb_h.path);
printk("Target mode disabled\n");
xpt_free_path(lstate->path);
kfree(lstate);
ahc_pause(ahc);
if (target != CAM_TARGET_WILDCARD) {
tstate->enabled_luns[lun] = NULL;
ahc->enabled_luns--;
for (empty = 1, i = 0; i < 8; i++)
if (tstate->enabled_luns[i] != NULL) {
empty = 0;
break;
}
if (empty) {
ahc_free_tstate(ahc, target, channel,
FALSE);
if (ahc->features & AHC_MULTI_TID) {
u_int targid_mask;
targid_mask = ahc_inb(ahc, TARGID)
| (ahc_inb(ahc, TARGID + 1)
<< 8);
targid_mask &= ~target_mask;
ahc_outb(ahc, TARGID, targid_mask);
ahc_outb(ahc, TARGID+1,
(targid_mask >> 8));
ahc_update_scsiid(ahc, targid_mask);
}
}
} else {
ahc->black_hole = NULL;
empty = TRUE;
}
if (ahc->enabled_luns == 0) {
u_int scsiseq;
scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
scsiseq &= ~ENSELI;
ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
scsiseq = ahc_inb(ahc, SCSISEQ);
scsiseq &= ~ENSELI;
ahc_outb(ahc, SCSISEQ, scsiseq);
if ((ahc->features & AHC_MULTIROLE) == 0) {
printk("Configuring Initiator Mode\n");
ahc->flags &= ~AHC_TARGETROLE;
ahc->flags |= AHC_INITIATORROLE;
(void)ahc_loadseq(ahc);
ahc_restart(ahc);
}
}
ahc_unpause(ahc);
ahc_unlock(ahc, &s);
}
}
static void
ahc_update_scsiid(struct ahc_softc *ahc, u_int targid_mask)
{
u_int scsiid_mask;
u_int scsiid;
if ((ahc->features & AHC_MULTI_TID) == 0)
panic("ahc_update_scsiid called on non-multitid unit\n");
if ((ahc->features & AHC_ULTRA2) != 0)
scsiid = ahc_inb(ahc, SCSIID_ULTRA2);
else
scsiid = ahc_inb(ahc, SCSIID);
scsiid_mask = 0x1 << (scsiid & OID);
if ((targid_mask & scsiid_mask) == 0) {
u_int our_id;
our_id = ffs(targid_mask);
if (our_id == 0)
our_id = ahc->our_id;
else
our_id--;
scsiid &= TID;
scsiid |= our_id;
}
if ((ahc->features & AHC_ULTRA2) != 0)
ahc_outb(ahc, SCSIID_ULTRA2, scsiid);
else
ahc_outb(ahc, SCSIID, scsiid);
}
static void
ahc_run_tqinfifo(struct ahc_softc *ahc, int paused)
{
struct target_cmd *cmd;
if ((ahc->features & AHC_AUTOPAUSE) != 0)
paused = TRUE;
ahc_sync_tqinfifo(ahc, BUS_DMASYNC_POSTREAD);
while ((cmd = &ahc->targetcmds[ahc->tqinfifonext])->cmd_valid != 0) {
if (ahc_handle_target_cmd(ahc, cmd) != 0)
break;
cmd->cmd_valid = 0;
ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
ahc->shared_data_dmamap,
ahc_targetcmd_offset(ahc, ahc->tqinfifonext),
sizeof(struct target_cmd),
BUS_DMASYNC_PREREAD);
ahc->tqinfifonext++;
if ((ahc->tqinfifonext & (HOST_TQINPOS - 1)) == 1) {
if ((ahc->features & AHC_HS_MAILBOX) != 0) {
u_int hs_mailbox;
hs_mailbox = ahc_inb(ahc, HS_MAILBOX);
hs_mailbox &= ~HOST_TQINPOS;
hs_mailbox |= ahc->tqinfifonext & HOST_TQINPOS;
ahc_outb(ahc, HS_MAILBOX, hs_mailbox);
} else {
if (!paused)
ahc_pause(ahc);
ahc_outb(ahc, KERNEL_TQINPOS,
ahc->tqinfifonext & HOST_TQINPOS);
if (!paused)
ahc_unpause(ahc);
}
}
}
}
static int
ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd)
{
struct ahc_tmode_tstate *tstate;
struct ahc_tmode_lstate *lstate;
struct ccb_accept_tio *atio;
uint8_t *byte;
int initiator;
int target;
int lun;
initiator = SCSIID_TARGET(ahc, cmd->scsiid);
target = SCSIID_OUR_ID(cmd->scsiid);
lun = (cmd->identify & MSG_IDENTIFY_LUNMASK);
byte = cmd->bytes;
tstate = ahc->enabled_targets[target];
lstate = NULL;
if (tstate != NULL)
lstate = tstate->enabled_luns[lun];
if (lstate == NULL)
lstate = ahc->black_hole;
atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
if (atio == NULL) {
ahc->flags |= AHC_TQINFIFO_BLOCKED;
if (bootverbose)
printk("%s: ATIOs exhausted\n", ahc_name(ahc));
return (1);
} else
ahc->flags &= ~AHC_TQINFIFO_BLOCKED;
#if 0
printk("Incoming command from %d for %d:%d%s\n",
initiator, target, lun,
lstate == ahc->black_hole ? "(Black Holed)" : "");
#endif
SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
if (lstate == ahc->black_hole) {
atio->ccb_h.target_id = target;
atio->ccb_h.target_lun = lun;
}
atio->sense_len = 0;
atio->init_id = initiator;
if (byte[0] != 0xFF) {
atio->tag_action = *byte++;
atio->tag_id = *byte++;
atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
} else {
atio->ccb_h.flags = 0;
}
byte++;
switch (*byte >> CMD_GROUP_CODE_SHIFT) {
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:
atio->cdb_len = 1;
printk("Reserved or VU command code type encountered\n");
break;
}
memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
atio->ccb_h.status |= CAM_CDB_RECVD;
if ((cmd->identify & MSG_IDENTIFY_DISCFLAG) == 0) {
#if 0
printk("Received Immediate Command %d:%d:%d - %p\n",
initiator, target, lun, ahc->pending_device);
#endif
ahc->pending_device = lstate;
ahc_freeze_ccb((union ccb *)atio);
atio->ccb_h.flags |= CAM_DIS_DISCONNECT;
}
xpt_done((union ccb*)atio);
return (0);
}
#endif