#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/1394/t1394.h>
#include <sys/1394/s1394.h>
#include <sys/1394/h1394.h>
static int s1394_fcp_register_common(s1394_target_t *target,
t1394_fcp_evts_t *evts, s1394_fa_type_t type, s1394_fa_descr_t *descr);
static int s1394_fcp_unregister_common(s1394_target_t *target,
s1394_fa_type_t type);
static void s1394_fcp_resp_recv_write_request(cmd1394_cmd_t *req);
static void s1394_fcp_cmd_recv_write_request(cmd1394_cmd_t *req);
static void s1394_fcp_recv_write_request(cmd1394_cmd_t *req,
s1394_fa_type_t type);
static void s1394_fcp_recv_write_unclaimed(s1394_hal_t *hal,
cmd1394_cmd_t *req);
uint_t s1394_fcp_notify_retry_cnt = 3;
s1394_fa_descr_t s1394_fcp_ctl_descr = {
IEC61883_FCP_RESP_ADDR,
IEC61883_FCP_RESP_SIZE,
T1394_ADDR_WRENBL,
{ NULL, s1394_fcp_resp_recv_write_request, NULL },
IEC61883_FCP_CMD_ADDR
};
s1394_fa_descr_t s1394_fcp_tgt_descr = {
IEC61883_FCP_CMD_ADDR,
IEC61883_FCP_CMD_SIZE,
T1394_ADDR_WRENBL,
{ NULL, s1394_fcp_cmd_recv_write_request, NULL },
IEC61883_FCP_RESP_ADDR
};
int
s1394_fcp_hal_init(s1394_hal_t *hal)
{
int ret = DDI_SUCCESS;
if ((ddi_prop_exists(DDI_DEV_T_ANY, hal->halinfo.dip, DDI_PROP_DONTPASS,
"h1394-fcp-claim-on-demand")) == 0) {
ret = s1394_fa_claim_addr(hal, S1394_FA_TYPE_FCP_CTL,
&s1394_fcp_ctl_descr);
if (ret == DDI_SUCCESS) {
ret = s1394_fa_claim_addr(hal, S1394_FA_TYPE_FCP_TGT,
&s1394_fcp_tgt_descr);
if (ret != DDI_SUCCESS) {
s1394_fa_free_addr(hal, S1394_FA_TYPE_FCP_CTL);
}
}
}
return (ret);
}
int
s1394_fcp_register_ctl(s1394_target_t *target, t1394_fcp_evts_t *evts)
{
return (s1394_fcp_register_common(target, evts, S1394_FA_TYPE_FCP_CTL,
&s1394_fcp_ctl_descr));
}
int
s1394_fcp_register_tgt(s1394_target_t *target, t1394_fcp_evts_t *evts)
{
return (s1394_fcp_register_common(target, evts, S1394_FA_TYPE_FCP_TGT,
&s1394_fcp_tgt_descr));
}
int
s1394_fcp_unregister_ctl(s1394_target_t *target)
{
return (s1394_fcp_unregister_common(target, S1394_FA_TYPE_FCP_CTL));
}
int
s1394_fcp_unregister_tgt(s1394_target_t *target)
{
return (s1394_fcp_unregister_common(target, S1394_FA_TYPE_FCP_TGT));
}
static int
s1394_fcp_register_common(s1394_target_t *target, t1394_fcp_evts_t *evts,
s1394_fa_type_t type, s1394_fa_descr_t *descr)
{
s1394_hal_t *hal = target->on_hal;
s1394_fcp_target_t *fcp;
rw_enter(&hal->target_list_rwlock, RW_WRITER);
if (s1394_fa_list_is_empty(hal, type)) {
if (s1394_fa_claim_addr(hal, type, descr) != DDI_SUCCESS) {
rw_exit(&hal->target_list_rwlock);
return (DDI_FAILURE);
}
}
s1394_fa_list_add(hal, target, type);
fcp = &target->target_fa[type].fat_u.fcp;
fcp->fc_evts = *evts;
rw_exit(&hal->target_list_rwlock);
return (DDI_SUCCESS);
}
static int
s1394_fcp_unregister_common(s1394_target_t *target, s1394_fa_type_t type)
{
s1394_hal_t *hal = target->on_hal;
int result;
rw_enter(&hal->target_list_rwlock, RW_WRITER);
result = s1394_fa_list_remove(hal, target, type);
if (result == DDI_SUCCESS) {
if (s1394_fa_list_is_empty(hal, type)) {
s1394_fa_free_addr(hal, type);
}
}
rw_exit(&hal->target_list_rwlock);
return (result);
}
int
s1394_fcp_write_check_cmd(cmd1394_cmd_t *cmd)
{
int len;
if (cmd->cmd_type == CMD1394_ASYNCH_WR_BLOCK) {
len = cmd->cmd_u.b.blk_length;
if (len == 4) {
cmd->cmd_result = CMD1394_ETYPE_ERROR;
return (DDI_FAILURE);
}
} else {
len = 4;
}
if ((cmd->cmd_addr & IEEE1394_ADDR_OFFSET_MASK) + len >
IEC61883_FCP_CMD_SIZE) {
cmd->cmd_result = CMD1394_EADDRESS_ERROR;
return (DDI_FAILURE);
}
if (cmd->cmd_options & CMD1394_OVERRIDE_ADDR) {
cmd->cmd_result = CMD1394_EINVALID_COMMAND;
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
s1394_fcp_resp_recv_write_request(cmd1394_cmd_t *req)
{
s1394_fcp_recv_write_request(req, S1394_FA_TYPE_FCP_CTL);
}
static void
s1394_fcp_cmd_recv_write_request(cmd1394_cmd_t *req)
{
s1394_fcp_recv_write_request(req, S1394_FA_TYPE_FCP_TGT);
}
static void
s1394_fcp_recv_write_request(cmd1394_cmd_t *req, s1394_fa_type_t type)
{
s1394_hal_t *hal = (s1394_hal_t *)req->cmd_callback_arg;
s1394_target_t *target;
s1394_fa_target_t *fat;
uint_t saved_gen;
int num_retries = 0;
int (*cb)(cmd1394_cmd_t *req);
boolean_t restored = B_FALSE;
int ret = T1394_REQ_UNCLAIMED;
rw_enter(&hal->target_list_rwlock, RW_READER);
start:
target = hal->hal_fa[type].fal_head;
if (target) {
s1394_fa_restore_cmd(hal, req);
restored = B_TRUE;
do {
fat = &target->target_fa[type];
cb = fat->fat_u.fcp.fc_evts.fcp_write_request;
if (cb == NULL) {
continue;
}
req->cmd_callback_arg = fat->fat_u.fcp.fc_evts.fcp_arg;
saved_gen = s1394_fa_list_gen(hal, type);
rw_exit(&hal->target_list_rwlock);
ret = cb(req);
rw_enter(&hal->target_list_rwlock, RW_READER);
if (ret == T1394_REQ_CLAIMED) {
break;
}
if (saved_gen != s1394_fa_list_gen(hal, type)) {
num_retries++;
if (num_retries <= s1394_fcp_notify_retry_cnt) {
goto start;
} else {
break;
}
}
target = fat->fat_next;
} while (target != NULL);
}
rw_exit(&hal->target_list_rwlock);
if (ret != T1394_REQ_CLAIMED) {
if (restored) {
s1394_fa_convert_cmd(hal, req);
}
s1394_fcp_recv_write_unclaimed(hal, req);
}
}
static void
s1394_fcp_recv_write_unclaimed(s1394_hal_t *hal, cmd1394_cmd_t *req)
{
req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
(void) s1394_send_response(hal, req);
}