#include <sys/sysmacros.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/disp.h>
#include <sys/1394/t1394.h>
#include <sys/1394/s1394.h>
#include <sys/1394/h1394.h>
#include <sys/1394/ieee1394.h>
static int s1394_allow_detach = 0;
int
t1394_attach(dev_info_t *dip, int version, uint_t flags,
t1394_attachinfo_t *attachinfo, t1394_handle_t *t1394_hdl)
{
s1394_hal_t *hal;
s1394_target_t *target;
uint_t dev;
uint_t curr;
uint_t unit_dir;
int hp_node = 0;
ASSERT(t1394_hdl != NULL);
ASSERT(attachinfo != NULL);
*t1394_hdl = NULL;
if (version != T1394_VERSION_V1) {
return (DDI_FAILURE);
}
hal = s1394_dip_to_hal(ddi_get_parent(dip));
if (hal == NULL) {
return (DDI_FAILURE);
}
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
hp_node = ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"hp-node");
target = kmem_zalloc(sizeof (s1394_target_t), KM_SLEEP);
mutex_enter(&hal->topology_tree_mutex);
target->target_version = version;
target->target_dip = dip;
target->on_hal = hal;
target->on_node = NULL;
rw_enter(&target->on_hal->target_list_rwlock, RW_WRITER);
if (hp_node != 0) {
s1394_add_target_to_node(target);
if (target->on_node == NULL) {
s1394_remove_target_from_node(target);
rw_exit(&target->on_hal->target_list_rwlock);
mutex_exit(&hal->topology_tree_mutex);
kmem_free(target, sizeof (s1394_target_t));
return (DDI_FAILURE);
}
target->target_state = S1394_TARG_HP_NODE;
if (S1394_NODE_BUS_PWR_CONSUMER(target->on_node) == B_TRUE)
target->target_state |= S1394_TARG_BUS_PWR_CONSUMER;
}
attachinfo->localinfo.bus_generation = target->on_hal->generation_count;
attachinfo->localinfo.local_nodeID = target->on_hal->node_id;
attachinfo->iblock_cookie = target->on_hal->halinfo.hw_interrupt;
attachinfo->acc_attr = target->on_hal->halinfo.acc_attr;
attachinfo->dma_attr = target->on_hal->halinfo.dma_attr;
unit_dir = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "unit-dir-offset", 0);
target->unit_dir = unit_dir;
target->physical_arreq_enabled = 0;
s1394_get_maxpayload(target, &dev, &curr);
target->dev_max_payload = dev;
target->current_max_payload = curr;
if ((target->on_hal->target_head == NULL) &&
(target->on_hal->target_tail == NULL)) {
target->on_hal->target_head = target;
target->on_hal->target_tail = target;
} else {
target->on_hal->target_tail->target_next = target;
target->target_prev = target->on_hal->target_tail;
target->on_hal->target_tail = target;
}
rw_exit(&target->on_hal->target_list_rwlock);
*t1394_hdl = (t1394_handle_t)target;
mutex_exit(&hal->topology_tree_mutex);
return (DDI_SUCCESS);
}
int
t1394_detach(t1394_handle_t *t1394_hdl, uint_t flags)
{
s1394_target_t *target;
uint_t num_cmds;
ASSERT(t1394_hdl != NULL);
target = (s1394_target_t *)(*t1394_hdl);
ASSERT(target->on_hal);
mutex_enter(&target->on_hal->topology_tree_mutex);
rw_enter(&target->on_hal->target_list_rwlock, RW_WRITER);
num_cmds = target->target_num_cmds;
if (num_cmds != 0) {
rw_exit(&target->on_hal->target_list_rwlock);
mutex_exit(&target->on_hal->topology_tree_mutex);
return (DDI_FAILURE);
}
if ((target->on_hal->target_head == target) &&
(target->on_hal->target_tail == target)) {
target->on_hal->target_head = NULL;
target->on_hal->target_tail = NULL;
} else {
if (target->target_prev)
target->target_prev->target_next = target->target_next;
if (target->target_next)
target->target_next->target_prev = target->target_prev;
if (target->on_hal->target_head == target)
target->on_hal->target_head = target->target_next;
if (target->on_hal->target_tail == target)
target->on_hal->target_tail = target->target_prev;
}
s1394_remove_target_from_node(target);
rw_exit(&target->on_hal->target_list_rwlock);
mutex_exit(&target->on_hal->topology_tree_mutex);
kmem_free(target, sizeof (s1394_target_t));
*t1394_hdl = NULL;
return (DDI_SUCCESS);
}
int
t1394_alloc_cmd(t1394_handle_t t1394_hdl, uint_t flags, cmd1394_cmd_t **cmdp)
{
s1394_hal_t *hal;
s1394_target_t *target;
s1394_cmd_priv_t *s_priv;
uint_t num_cmds;
ASSERT(t1394_hdl != NULL);
target = (s1394_target_t *)t1394_hdl;
hal = target->on_hal;
rw_enter(&hal->target_list_rwlock, RW_WRITER);
num_cmds = target->target_num_cmds;
if (num_cmds >= MAX_NUMBER_ALLOC_CMDS) {
rw_exit(&hal->target_list_rwlock);
hal->hal_kstats->cmd_alloc_fail++;
return (DDI_FAILURE);
}
target->target_num_cmds = num_cmds + 1;
if (s1394_alloc_cmd(hal, flags, cmdp) != DDI_SUCCESS) {
target->target_num_cmds = num_cmds;
rw_exit(&hal->target_list_rwlock);
hal->hal_kstats->cmd_alloc_fail++;
return (DDI_FAILURE);
}
rw_exit(&hal->target_list_rwlock);
s_priv = S1394_GET_CMD_PRIV(*cmdp);
mutex_init(&s_priv->blocking_mutex, NULL, MUTEX_DRIVER,
hal->halinfo.hw_interrupt);
cv_init(&s_priv->blocking_cv, NULL, CV_DRIVER, NULL);
return (DDI_SUCCESS);
}
int
t1394_free_cmd(t1394_handle_t t1394_hdl, uint_t flags, cmd1394_cmd_t **cmdp)
{
s1394_hal_t *hal;
s1394_target_t *target;
s1394_cmd_priv_t *s_priv;
uint_t num_cmds;
ASSERT(t1394_hdl != NULL);
target = (s1394_target_t *)t1394_hdl;
hal = target->on_hal;
rw_enter(&hal->target_list_rwlock, RW_WRITER);
num_cmds = target->target_num_cmds;
if (num_cmds == 0) {
rw_exit(&hal->target_list_rwlock);
ASSERT(num_cmds != 0);
return (DDI_FAILURE);
}
s_priv = S1394_GET_CMD_PRIV(*cmdp);
if (s_priv->cmd_in_use == B_TRUE) {
rw_exit(&hal->target_list_rwlock);
ASSERT(s_priv->cmd_in_use == B_FALSE);
return (DDI_FAILURE);
}
target->target_num_cmds--;
rw_exit(&hal->target_list_rwlock);
cv_destroy(&s_priv->blocking_cv);
mutex_destroy(&s_priv->blocking_mutex);
kmem_cache_free(hal->hal_kmem_cachep, *cmdp);
*cmdp = NULL;
hal->hal_kstats->cmd_free++;
return (DDI_SUCCESS);
}
int
t1394_read(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
{
s1394_hal_t *to_hal;
s1394_target_t *target;
s1394_cmd_priv_t *s_priv;
s1394_hal_state_t state;
int ret;
int err;
ASSERT(t1394_hdl != NULL);
ASSERT(cmd != NULL);
s_priv = S1394_GET_CMD_PRIV(cmd);
if (s_priv->cmd_in_use == B_TRUE) {
ASSERT(s_priv->cmd_in_use == B_FALSE);
return (DDI_FAILURE);
}
target = (s1394_target_t *)t1394_hdl;
to_hal = target->on_hal;
cmd->cmd_result = CMD1394_NOSTATUS;
if ((cmd->cmd_type != CMD1394_ASYNCH_RD_QUAD) &&
(cmd->cmd_type != CMD1394_ASYNCH_RD_BLOCK)) {
cmd->cmd_result = CMD1394_EINVALID_COMMAND;
return (DDI_FAILURE);
}
if ((cmd->cmd_options & CMD1394_BLOCKING) &&
(servicing_interrupt())) {
cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
return (DDI_FAILURE);
}
mutex_enter(&to_hal->topology_tree_mutex);
state = to_hal->hal_state;
if (state != S1394_HAL_NORMAL) {
ret = s1394_HAL_asynch_error(to_hal, cmd, state);
if (ret != CMD1394_CMDSUCCESS) {
cmd->cmd_result = ret;
mutex_exit(&to_hal->topology_tree_mutex);
return (DDI_FAILURE);
}
}
ret = s1394_setup_asynch_command(to_hal, target, cmd,
S1394_CMD_READ, &err);
if (ret != DDI_SUCCESS) {
cmd->cmd_result = err;
mutex_exit(&to_hal->topology_tree_mutex);
return (DDI_FAILURE);
}
if (state == S1394_HAL_RESET) {
s1394_remove_q_asynch_cmd(to_hal, cmd);
if (s1394_on_br_thread(to_hal) == B_TRUE) {
if (cmd->cmd_options & CMD1394_BLOCKING) {
mutex_exit(&to_hal->topology_tree_mutex);
s_priv->cmd_in_use = B_FALSE;
cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
return (DDI_FAILURE);
}
}
s1394_pending_q_insert(to_hal, cmd, S1394_PENDING_Q_FRONT);
mutex_exit(&to_hal->topology_tree_mutex);
goto block_on_asynch_cmd;
}
mutex_exit(&to_hal->topology_tree_mutex);
ret = s1394_xfer_asynch_command(to_hal, cmd, &err);
if (ret != DDI_SUCCESS) {
if (err == CMD1394_ESTALE_GENERATION) {
s1394_remove_q_asynch_cmd(to_hal, cmd);
s1394_pending_q_insert(to_hal, cmd,
S1394_PENDING_Q_FRONT);
goto block_on_asynch_cmd;
} else {
s1394_remove_q_asynch_cmd(to_hal, cmd);
s_priv->cmd_in_use = B_FALSE;
cmd->cmd_result = err;
return (DDI_FAILURE);
}
} else {
goto block_on_asynch_cmd;
}
block_on_asynch_cmd:
s1394_block_on_asynch_cmd(cmd);
return (DDI_SUCCESS);
}
int
t1394_write(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
{
s1394_hal_t *to_hal;
s1394_target_t *target;
s1394_cmd_priv_t *s_priv;
s1394_hal_state_t state;
int ret;
int err;
ASSERT(t1394_hdl != NULL);
ASSERT(cmd != NULL);
s_priv = S1394_GET_CMD_PRIV(cmd);
if (s_priv->cmd_in_use == B_TRUE) {
ASSERT(s_priv->cmd_in_use == B_FALSE);
return (DDI_FAILURE);
}
target = (s1394_target_t *)t1394_hdl;
to_hal = target->on_hal;
if (s_priv->cmd_ext_type == S1394_CMD_EXT_FA) {
if (S1394_IS_CMD_FCP(s_priv) &&
(s1394_fcp_write_check_cmd(cmd) != DDI_SUCCESS)) {
return (DDI_FAILURE);
}
s1394_fa_convert_cmd(to_hal, cmd);
}
cmd->cmd_result = CMD1394_NOSTATUS;
if ((cmd->cmd_type != CMD1394_ASYNCH_WR_QUAD) &&
(cmd->cmd_type != CMD1394_ASYNCH_WR_BLOCK)) {
cmd->cmd_result = CMD1394_EINVALID_COMMAND;
s1394_fa_check_restore_cmd(to_hal, cmd);
return (DDI_FAILURE);
}
if ((cmd->cmd_options & CMD1394_BLOCKING) &&
(servicing_interrupt())) {
cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
s1394_fa_check_restore_cmd(to_hal, cmd);
return (DDI_FAILURE);
}
mutex_enter(&to_hal->topology_tree_mutex);
state = to_hal->hal_state;
if (state != S1394_HAL_NORMAL) {
ret = s1394_HAL_asynch_error(to_hal, cmd, state);
if (ret != CMD1394_CMDSUCCESS) {
cmd->cmd_result = ret;
mutex_exit(&to_hal->topology_tree_mutex);
s1394_fa_check_restore_cmd(to_hal, cmd);
return (DDI_FAILURE);
}
}
ret = s1394_setup_asynch_command(to_hal, target, cmd,
S1394_CMD_WRITE, &err);
if (ret != DDI_SUCCESS) {
cmd->cmd_result = err;
mutex_exit(&to_hal->topology_tree_mutex);
s1394_fa_check_restore_cmd(to_hal, cmd);
return (DDI_FAILURE);
}
if (state == S1394_HAL_RESET) {
s1394_remove_q_asynch_cmd(to_hal, cmd);
if (s1394_on_br_thread(to_hal) == B_TRUE) {
if (cmd->cmd_options & CMD1394_BLOCKING) {
mutex_exit(&to_hal->topology_tree_mutex);
s_priv->cmd_in_use = B_FALSE;
cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
s1394_fa_check_restore_cmd(to_hal, cmd);
return (DDI_FAILURE);
}
}
s1394_pending_q_insert(to_hal, cmd, S1394_PENDING_Q_FRONT);
mutex_exit(&to_hal->topology_tree_mutex);
s1394_block_on_asynch_cmd(cmd);
return (DDI_SUCCESS);
}
mutex_exit(&to_hal->topology_tree_mutex);
ret = s1394_xfer_asynch_command(to_hal, cmd, &err);
if (ret != DDI_SUCCESS) {
if (err == CMD1394_ESTALE_GENERATION) {
s1394_remove_q_asynch_cmd(to_hal, cmd);
s1394_pending_q_insert(to_hal, cmd,
S1394_PENDING_Q_FRONT);
s1394_block_on_asynch_cmd(cmd);
return (DDI_SUCCESS);
} else {
s1394_remove_q_asynch_cmd(to_hal, cmd);
s_priv->cmd_in_use = B_FALSE;
cmd->cmd_result = err;
s1394_fa_check_restore_cmd(to_hal, cmd);
return (DDI_FAILURE);
}
} else {
s1394_block_on_asynch_cmd(cmd);
return (DDI_SUCCESS);
}
}
int
t1394_lock(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
{
s1394_hal_t *to_hal;
s1394_target_t *target;
s1394_cmd_priv_t *s_priv;
s1394_hal_state_t state;
cmd1394_lock_type_t lock_type;
uint_t num_retries;
int ret;
ASSERT(t1394_hdl != NULL);
ASSERT(cmd != NULL);
s_priv = S1394_GET_CMD_PRIV(cmd);
if (s_priv->cmd_in_use == B_TRUE) {
ASSERT(s_priv->cmd_in_use == B_FALSE);
return (DDI_FAILURE);
}
target = (s1394_target_t *)t1394_hdl;
to_hal = target->on_hal;
mutex_enter(&to_hal->topology_tree_mutex);
state = to_hal->hal_state;
if (state != S1394_HAL_NORMAL) {
ret = s1394_HAL_asynch_error(to_hal, cmd, state);
if (ret != CMD1394_CMDSUCCESS) {
cmd->cmd_result = ret;
mutex_exit(&to_hal->topology_tree_mutex);
return (DDI_FAILURE);
}
}
mutex_exit(&to_hal->topology_tree_mutex);
if ((cmd->cmd_type != CMD1394_ASYNCH_LOCK_32) &&
(cmd->cmd_type != CMD1394_ASYNCH_LOCK_64)) {
cmd->cmd_result = CMD1394_EINVALID_COMMAND;
return (DDI_FAILURE);
}
cmd->cmd_result = CMD1394_NOSTATUS;
if ((cmd->cmd_options & CMD1394_BLOCKING) &&
(servicing_interrupt())) {
cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
return (DDI_FAILURE);
}
if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_32) {
lock_type = cmd->cmd_u.l32.lock_type;
num_retries = cmd->cmd_u.l32.num_retries;
} else {
lock_type = cmd->cmd_u.l64.lock_type;
num_retries = cmd->cmd_u.l64.num_retries;
}
ASSERT(num_retries <= MAX_NUMBER_OF_LOCK_RETRIES);
switch (lock_type) {
case CMD1394_LOCK_MASK_SWAP:
case CMD1394_LOCK_FETCH_ADD:
case CMD1394_LOCK_LITTLE_ADD:
case CMD1394_LOCK_BOUNDED_ADD:
case CMD1394_LOCK_WRAP_ADD:
case CMD1394_LOCK_COMPARE_SWAP:
ret = s1394_compare_swap(to_hal, target, cmd);
break;
case CMD1394_LOCK_BIT_AND:
case CMD1394_LOCK_BIT_OR:
case CMD1394_LOCK_BIT_XOR:
case CMD1394_LOCK_INCREMENT:
case CMD1394_LOCK_DECREMENT:
case CMD1394_LOCK_ADD:
case CMD1394_LOCK_SUBTRACT:
case CMD1394_LOCK_THRESH_ADD:
case CMD1394_LOCK_THRESH_SUBTRACT:
case CMD1394_LOCK_CLIP_ADD:
case CMD1394_LOCK_CLIP_SUBTRACT:
ret = s1394_split_lock_req(to_hal, target, cmd);
break;
default:
cmd->cmd_result = CMD1394_EINVALID_COMMAND;
ret = DDI_FAILURE;
break;
}
return (ret);
}
int
t1394_alloc_addr(t1394_handle_t t1394_hdl, t1394_alloc_addr_t *addr_allocp,
uint_t flags, int *result)
{
s1394_hal_t *hal;
s1394_target_t *target;
uint64_t addr_lo;
uint64_t addr_hi;
int err;
ASSERT(t1394_hdl != NULL);
ASSERT(addr_allocp != NULL);
target = (s1394_target_t *)t1394_hdl;
hal = target->on_hal;
addr_lo = addr_allocp->aa_address;
addr_hi = addr_lo + addr_allocp->aa_length;
if ((addr_allocp->aa_enable & T1394_ADDR_RDENBL) &&
(addr_allocp->aa_evts.recv_read_request == NULL) &&
(addr_allocp->aa_kmem_bufp == NULL)) {
if ((addr_allocp->aa_type != T1394_ADDR_FIXED) ||
(addr_lo < hal->physical_addr_lo) ||
(addr_hi > hal->physical_addr_hi)) {
*result = T1394_EINVALID_PARAM;
hal->hal_kstats->addr_alloc_fail++;
return (DDI_FAILURE);
} else {
addr_allocp->aa_enable &= ~T1394_ADDR_RDENBL;
}
}
if ((addr_allocp->aa_enable & T1394_ADDR_WRENBL) &&
(addr_allocp->aa_evts.recv_write_request == NULL) &&
(addr_allocp->aa_kmem_bufp == NULL)) {
if ((addr_allocp->aa_type != T1394_ADDR_FIXED) ||
(addr_lo < hal->physical_addr_lo) ||
(addr_hi > hal->physical_addr_hi)) {
*result = T1394_EINVALID_PARAM;
hal->hal_kstats->addr_alloc_fail++;
return (DDI_FAILURE);
} else {
addr_allocp->aa_enable &= ~T1394_ADDR_WRENBL;
}
}
if ((addr_allocp->aa_enable & T1394_ADDR_LKENBL) &&
(addr_allocp->aa_evts.recv_lock_request == NULL) &&
(addr_allocp->aa_kmem_bufp == NULL)) {
if ((addr_allocp->aa_type != T1394_ADDR_FIXED) ||
(addr_lo < hal->physical_addr_lo) ||
(addr_hi > hal->physical_addr_hi)) {
*result = T1394_EINVALID_PARAM;
hal->hal_kstats->addr_alloc_fail++;
return (DDI_FAILURE);
} else {
addr_allocp->aa_enable &= ~T1394_ADDR_LKENBL;
}
}
if (addr_allocp->aa_type != T1394_ADDR_FIXED) {
err = s1394_request_addr_blk((s1394_hal_t *)target->on_hal,
addr_allocp);
if (err != DDI_SUCCESS) {
*result = T1394_EALLOC_ADDR;
hal->hal_kstats->addr_alloc_fail++;
} else {
*result = T1394_NOERROR;
}
return (err);
} else {
err = s1394_claim_addr_blk((s1394_hal_t *)target->on_hal,
addr_allocp);
if (err != DDI_SUCCESS) {
*result = T1394_EALLOC_ADDR;
hal->hal_kstats->addr_alloc_fail++;
} else {
*result = T1394_NOERROR;
if ((addr_lo >= hal->physical_addr_lo) &&
(addr_hi <= hal->physical_addr_hi)) {
rw_enter(&hal->target_list_rwlock, RW_WRITER);
target->physical_arreq_enabled++;
rw_exit(&hal->target_list_rwlock);
s1394_physical_arreq_set_one(target);
}
}
return (err);
}
}
int
t1394_free_addr(t1394_handle_t t1394_hdl, t1394_addr_handle_t *addr_hdl,
uint_t flags)
{
s1394_addr_space_blk_t *curr_blk;
s1394_hal_t *hal;
s1394_target_t *target;
ASSERT(t1394_hdl != NULL);
ASSERT(addr_hdl != NULL);
target = (s1394_target_t *)t1394_hdl;
hal = target->on_hal;
curr_blk = (s1394_addr_space_blk_t *)(*addr_hdl);
if (s1394_free_addr_blk(hal, curr_blk) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (curr_blk->addr_type == T1394_ADDR_FIXED) {
target->physical_arreq_enabled--;
s1394_physical_arreq_clear_one(target);
}
*addr_hdl = NULL;
hal->hal_kstats->addr_space_free++;
return (DDI_SUCCESS);
}
int
t1394_recv_request_done(t1394_handle_t t1394_hdl, cmd1394_cmd_t *resp,
uint_t flags)
{
s1394_hal_t *hal;
s1394_cmd_priv_t *s_priv;
h1394_cmd_priv_t *h_priv;
mblk_t *curr_blk;
size_t msgb_len;
size_t size;
int ret;
boolean_t response = B_TRUE;
boolean_t posted_write = B_FALSE;
boolean_t write_cmd = B_FALSE;
boolean_t mblk_too_small;
ASSERT(t1394_hdl != NULL);
ASSERT(resp != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
s_priv = S1394_GET_CMD_PRIV(resp);
h_priv = (h1394_cmd_priv_t *)&s_priv->hal_cmd_private;
if (s_priv->cmd_ext_type == S1394_CMD_EXT_FA) {
s1394_fa_convert_cmd(hal, resp);
}
if ((resp->cmd_type == CMD1394_ASYNCH_WR_QUAD) ||
(resp->cmd_type == CMD1394_ASYNCH_WR_BLOCK)) {
write_cmd = B_TRUE;
posted_write = s_priv->posted_write;
}
if ((resp->broadcast == 1) ||
((write_cmd == B_TRUE) && (posted_write == B_TRUE)))
response = B_FALSE;
if (response == B_FALSE) {
if ((write_cmd == B_TRUE) && (posted_write == B_TRUE)) {
hal->hal_kstats->arreq_posted_write_error++;
}
HAL_CALL(hal).response_complete(hal->halinfo.hal_private, resp,
h_priv);
return (DDI_SUCCESS);
}
ASSERT(response == B_TRUE);
switch (resp->cmd_result) {
case IEEE1394_RESP_COMPLETE:
if (resp->cmd_type == CMD1394_ASYNCH_RD_BLOCK) {
curr_blk = resp->cmd_u.b.data_block;
size = resp->cmd_u.b.blk_length;
msgb_len = 0;
mblk_too_small = B_TRUE;
if (curr_blk == NULL) {
HAL_CALL(hal).response_complete(
hal->halinfo.hal_private, resp, h_priv);
ASSERT(curr_blk != NULL);
return (DDI_FAILURE);
}
while (curr_blk != NULL) {
msgb_len +=
(curr_blk->b_wptr - curr_blk->b_rptr);
if (msgb_len >= size) {
mblk_too_small = B_FALSE;
break;
}
curr_blk = curr_blk->b_cont;
}
if (mblk_too_small == B_TRUE) {
HAL_CALL(hal).response_complete(
hal->halinfo.hal_private, resp, h_priv);
ASSERT(mblk_too_small != B_TRUE);
return (DDI_FAILURE);
}
}
case IEEE1394_RESP_CONFLICT_ERROR:
case IEEE1394_RESP_DATA_ERROR:
case IEEE1394_RESP_TYPE_ERROR:
case IEEE1394_RESP_ADDRESS_ERROR:
ret = s1394_send_response(hal, resp);
return (ret);
default:
return (DDI_FAILURE);
}
}
int
t1394_fcp_register_controller(t1394_handle_t t1394_hdl, t1394_fcp_evts_t *evts,
uint_t flags)
{
int result;
ASSERT(t1394_hdl != NULL);
result = s1394_fcp_register_ctl((s1394_target_t *)t1394_hdl, evts);
return (result);
}
int
t1394_fcp_unregister_controller(t1394_handle_t t1394_hdl)
{
int result;
ASSERT(t1394_hdl != NULL);
result = s1394_fcp_unregister_ctl((s1394_target_t *)t1394_hdl);
return (result);
}
int
t1394_fcp_register_target(t1394_handle_t t1394_hdl, t1394_fcp_evts_t *evts,
uint_t flags)
{
int result;
ASSERT(t1394_hdl != NULL);
result = s1394_fcp_register_tgt((s1394_target_t *)t1394_hdl, evts);
return (result);
}
int
t1394_fcp_unregister_target(t1394_handle_t t1394_hdl)
{
int result;
ASSERT(t1394_hdl != NULL);
result = s1394_fcp_unregister_tgt((s1394_target_t *)t1394_hdl);
return (result);
}
int
t1394_cmp_register(t1394_handle_t t1394_hdl, t1394_cmp_evts_t *evts,
uint_t flags)
{
int result;
ASSERT(t1394_hdl != NULL);
result = s1394_cmp_register((s1394_target_t *)t1394_hdl, evts);
return (result);
}
int
t1394_cmp_unregister(t1394_handle_t t1394_hdl)
{
int result;
ASSERT(t1394_hdl != NULL);
result = s1394_cmp_unregister((s1394_target_t *)t1394_hdl);
return (result);
}
int
t1394_cmp_read(t1394_handle_t t1394_hdl, t1394_cmp_reg_t reg, uint32_t *valp)
{
int result;
ASSERT(t1394_hdl != NULL);
result = s1394_cmp_read((s1394_target_t *)t1394_hdl, reg, valp);
return (result);
}
int
t1394_cmp_cas(t1394_handle_t t1394_hdl, t1394_cmp_reg_t reg, uint32_t arg_val,
uint32_t new_val, uint32_t *old_valp)
{
int result;
ASSERT(t1394_hdl != NULL);
result = s1394_cmp_cas((s1394_target_t *)t1394_hdl, reg, arg_val,
new_val, old_valp);
return (result);
}
int
t1394_alloc_isoch_single(t1394_handle_t t1394_hdl,
t1394_isoch_singleinfo_t *sii, uint_t flags,
t1394_isoch_single_out_t *output_args,
t1394_isoch_single_handle_t *t1394_single_hdl, int *result)
{
s1394_hal_t *hal;
s1394_isoch_cec_t *cec_new;
t1394_join_isochinfo_t jii;
int ret;
int err;
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_single_hdl != NULL);
ASSERT(sii != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
if (sii->si_channel_mask == 0) {
return (DDI_FAILURE);
}
if ((sii->si_bandwidth <= IEEE1394_BANDWIDTH_MIN) ||
(sii->si_bandwidth > IEEE1394_BANDWIDTH_MAX)) {
return (DDI_FAILURE);
}
if (sii->rsrc_fail_target == NULL) {
return (DDI_FAILURE);
}
cec_new = kmem_zalloc(sizeof (s1394_isoch_cec_t), KM_SLEEP);
cec_new->cec_type = S1394_SINGLE;
mutex_init(&cec_new->isoch_cec_mutex, NULL, MUTEX_DRIVER,
hal->halinfo.hw_interrupt);
cv_init(&cec_new->in_callbacks_cv, NULL, CV_DRIVER,
hal->halinfo.hw_interrupt);
cec_new->cec_member_list_head = NULL;
cec_new->cec_member_list_tail = NULL;
cec_new->filter_min_speed = sii->si_speed;
cec_new->filter_max_speed = sii->si_speed;
cec_new->filter_current_speed = cec_new->filter_max_speed;
cec_new->filter_channel_mask = sii->si_channel_mask;
cec_new->bandwidth = sii->si_bandwidth;
cec_new->state_transitions = ISOCH_CEC_FREE | ISOCH_CEC_JOIN |
ISOCH_CEC_SETUP;
mutex_enter(&hal->isoch_cec_list_mutex);
s1394_isoch_cec_list_insert(hal, cec_new);
mutex_exit(&hal->isoch_cec_list_mutex);
jii.req_channel_mask = sii->si_channel_mask;
jii.req_max_speed = sii->si_speed;
jii.jii_options = T1394_TALKER;
jii.isoch_cec_evts_arg = sii->single_evt_arg;
jii.isoch_cec_evts.setup_target = NULL;
jii.isoch_cec_evts.start_target = NULL;
jii.isoch_cec_evts.stop_target = NULL;
jii.isoch_cec_evts.stop_target = NULL;
jii.isoch_cec_evts.teardown_target = NULL;
jii.isoch_cec_evts.rsrc_fail_target = sii->rsrc_fail_target;
ret = t1394_join_isoch_cec(t1394_hdl,
(t1394_isoch_cec_handle_t)cec_new, 0, &jii);
if (ret != DDI_SUCCESS) {
ret = t1394_free_isoch_cec(t1394_hdl, flags,
(t1394_isoch_cec_handle_t *)&cec_new);
if (ret != DDI_SUCCESS) {
ASSERT(0);
}
*t1394_single_hdl = NULL;
return (DDI_FAILURE);
}
ret = t1394_setup_isoch_cec(t1394_hdl,
(t1394_isoch_cec_handle_t)cec_new, 0, &err);
if (ret != DDI_SUCCESS) {
*result = err;
ret = t1394_leave_isoch_cec(t1394_hdl,
(t1394_isoch_cec_handle_t)cec_new, 0);
if (ret != DDI_SUCCESS) {
ASSERT(0);
}
ret = t1394_free_isoch_cec(t1394_hdl, flags,
(t1394_isoch_cec_handle_t *)&cec_new);
if (ret != DDI_SUCCESS) {
ASSERT(0);
}
*t1394_single_hdl = NULL;
return (DDI_FAILURE);
}
mutex_enter(&cec_new->isoch_cec_mutex);
output_args->channel_num = cec_new->realloc_chnl_num;
mutex_exit(&cec_new->isoch_cec_mutex);
*t1394_single_hdl = (t1394_isoch_single_handle_t)cec_new;
return (DDI_SUCCESS);
}
void
t1394_free_isoch_single(t1394_handle_t t1394_hdl,
t1394_isoch_single_handle_t *t1394_single_hdl, uint_t flags)
{
s1394_isoch_cec_t *cec_curr;
int ret;
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_single_hdl != NULL);
cec_curr = (s1394_isoch_cec_t *)(*t1394_single_hdl);
ret = t1394_teardown_isoch_cec(t1394_hdl,
(t1394_isoch_cec_handle_t)cec_curr, 0);
if (ret != DDI_SUCCESS) {
ASSERT(0);
}
ret = t1394_leave_isoch_cec(t1394_hdl,
(t1394_isoch_cec_handle_t)cec_curr, 0);
if (ret != DDI_SUCCESS) {
ASSERT(0);
}
ret = t1394_free_isoch_cec(t1394_hdl, flags,
(t1394_isoch_cec_handle_t *)&cec_curr);
if (ret != DDI_SUCCESS) {
ASSERT(0);
}
*t1394_single_hdl = NULL;
}
int
t1394_alloc_isoch_cec(t1394_handle_t t1394_hdl, t1394_isoch_cec_props_t *props,
uint_t flags, t1394_isoch_cec_handle_t *t1394_isoch_cec_hdl)
{
s1394_hal_t *hal;
s1394_isoch_cec_t *cec_new;
uint64_t temp;
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
ASSERT(props != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
if (props->cec_channel_mask == 0) {
return (DDI_FAILURE);
}
temp = props->cec_channel_mask;
if (props->cec_options & T1394_NO_IRM_ALLOC) {
if (!ISP2(temp)) {
return (DDI_FAILURE);
}
if (props->cec_min_speed != props->cec_max_speed) {
return (DDI_FAILURE);
}
}
if ((props->cec_bandwidth <= IEEE1394_BANDWIDTH_MIN) ||
(props->cec_bandwidth > IEEE1394_BANDWIDTH_MAX)) {
return (DDI_FAILURE);
}
cec_new = kmem_zalloc(sizeof (s1394_isoch_cec_t), KM_SLEEP);
cec_new->cec_type = S1394_PEER_TO_PEER;
mutex_init(&cec_new->isoch_cec_mutex, NULL, MUTEX_DRIVER,
hal->halinfo.hw_interrupt);
cv_init(&cec_new->in_callbacks_cv, NULL, CV_DRIVER,
hal->halinfo.hw_interrupt);
cec_new->cec_member_list_head = NULL;
cec_new->cec_member_list_tail = NULL;
cec_new->filter_min_speed = props->cec_min_speed;
cec_new->filter_max_speed = props->cec_max_speed;
cec_new->filter_current_speed = cec_new->filter_max_speed;
cec_new->filter_channel_mask = props->cec_channel_mask;
cec_new->bandwidth = props->cec_bandwidth;
cec_new->cec_options = props->cec_options;
cec_new->state_transitions = ISOCH_CEC_FREE | ISOCH_CEC_JOIN |
ISOCH_CEC_SETUP;
mutex_enter(&hal->isoch_cec_list_mutex);
s1394_isoch_cec_list_insert(hal, cec_new);
mutex_exit(&hal->isoch_cec_list_mutex);
*t1394_isoch_cec_hdl = (t1394_isoch_cec_handle_t)cec_new;
return (DDI_SUCCESS);
}
int
t1394_free_isoch_cec(t1394_handle_t t1394_hdl, uint_t flags,
t1394_isoch_cec_handle_t *t1394_isoch_cec_hdl)
{
s1394_hal_t *hal;
s1394_isoch_cec_t *cec_curr;
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
cec_curr = (s1394_isoch_cec_t *)(*t1394_isoch_cec_hdl);
mutex_enter(&cec_curr->isoch_cec_mutex);
if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_FREE) == 0) {
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
mutex_exit(&cec_curr->isoch_cec_mutex);
mutex_enter(&hal->isoch_cec_list_mutex);
s1394_isoch_cec_list_remove(hal, cec_curr);
mutex_exit(&hal->isoch_cec_list_mutex);
cv_destroy(&cec_curr->in_callbacks_cv);
mutex_destroy(&cec_curr->isoch_cec_mutex);
kmem_free(cec_curr, sizeof (s1394_isoch_cec_t));
*t1394_isoch_cec_hdl = NULL;
return (DDI_SUCCESS);
}
int
t1394_join_isoch_cec(t1394_handle_t t1394_hdl,
t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags,
t1394_join_isochinfo_t *join_isoch_info)
{
s1394_hal_t *hal;
s1394_isoch_cec_t *cec_curr;
s1394_isoch_cec_member_t *member_new;
uint64_t check_mask;
uint_t curr_max_speed;
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
member_new = kmem_zalloc(sizeof (s1394_isoch_cec_member_t), KM_SLEEP);
mutex_enter(&cec_curr->isoch_cec_mutex);
while (CEC_IN_ANY_CALLBACKS(cec_curr)) {
cec_curr->cec_want_wakeup = B_TRUE;
cv_wait(&cec_curr->in_callbacks_cv,
&cec_curr->isoch_cec_mutex);
}
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_JOIN) == 0) {
kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
check_mask = join_isoch_info->req_channel_mask &
cec_curr->filter_channel_mask;
if (check_mask == 0) {
kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
if (join_isoch_info->req_max_speed < cec_curr->filter_min_speed) {
kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
} else if (join_isoch_info->req_max_speed <
cec_curr->filter_current_speed) {
curr_max_speed = join_isoch_info->req_max_speed;
} else {
curr_max_speed = cec_curr->filter_current_speed;
}
if ((join_isoch_info->jii_options & T1394_TALKER) &&
(cec_curr->cec_member_talker != NULL)) {
kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
if ((cec_curr->cec_type == S1394_PEER_TO_PEER) &&
((join_isoch_info->isoch_cec_evts.setup_target == NULL) ||
(join_isoch_info->isoch_cec_evts.start_target == NULL) ||
(join_isoch_info->isoch_cec_evts.stop_target == NULL) ||
(join_isoch_info->isoch_cec_evts.rsrc_fail_target == NULL) ||
(join_isoch_info->isoch_cec_evts.teardown_target == NULL))) {
kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
member_new->isoch_cec_evts = join_isoch_info->isoch_cec_evts;
member_new->isoch_cec_evts_arg = join_isoch_info->isoch_cec_evts_arg;
member_new->cec_mem_options = join_isoch_info->jii_options;
member_new->cec_mem_target = (s1394_target_t *)t1394_hdl;
s1394_isoch_cec_member_list_insert(hal, cec_curr, member_new);
cec_curr->filter_channel_mask = check_mask;
cec_curr->filter_current_speed = curr_max_speed;
if (join_isoch_info->jii_options & T1394_TALKER)
cec_curr->cec_member_talker = cec_curr->cec_member_list_head;
CEC_SET_LEGAL(cec_curr, ISOCH_CEC_LEAVE);
CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_FREE);
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_SUCCESS);
}
int
t1394_leave_isoch_cec(t1394_handle_t t1394_hdl,
t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
{
s1394_hal_t *hal;
s1394_isoch_cec_t *cec_curr;
s1394_isoch_cec_member_t *member_curr;
s1394_isoch_cec_member_t *member_temp;
boolean_t found;
uint64_t temp_channel_mask;
uint_t temp_max_speed;
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
mutex_enter(&cec_curr->isoch_cec_mutex);
while (CEC_IN_ANY_CALLBACKS(cec_curr)) {
cec_curr->cec_want_wakeup = B_TRUE;
cv_wait(&cec_curr->in_callbacks_cv,
&cec_curr->isoch_cec_mutex);
}
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_LEAVE) == 0) {
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
found = B_FALSE;
temp_channel_mask = cec_curr->cec_alloc_props.cec_channel_mask;
temp_max_speed = cec_curr->cec_alloc_props.cec_max_speed;
member_curr = cec_curr->cec_member_list_head;
while (member_curr != NULL) {
if (member_curr->cec_mem_target ==
(s1394_target_t *)t1394_hdl) {
member_temp = member_curr;
found = B_TRUE;
} else {
temp_channel_mask &= member_curr->req_channel_mask;
if (member_curr->req_max_speed < temp_max_speed)
temp_max_speed = member_curr->req_max_speed;
}
member_curr = member_curr->cec_mem_next;
}
if (found == B_FALSE) {
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
} else {
cec_curr->filter_current_speed = temp_max_speed;
cec_curr->filter_channel_mask = temp_channel_mask;
}
s1394_isoch_cec_member_list_remove(hal, cec_curr, member_temp);
if (cec_curr->cec_member_talker == member_temp)
cec_curr->cec_member_talker = NULL;
if ((cec_curr->cec_member_list_head == NULL) &&
(cec_curr->cec_member_list_tail == NULL)) {
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_JOIN) != 0)
CEC_SET_LEGAL(cec_curr, ISOCH_CEC_FREE);
CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_LEAVE);
}
mutex_exit(&cec_curr->isoch_cec_mutex);
kmem_free(member_temp, sizeof (s1394_isoch_cec_member_t));
return (DDI_SUCCESS);
}
int
t1394_setup_isoch_cec(t1394_handle_t t1394_hdl,
t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags, int *result)
{
s1394_hal_t *hal;
s1394_isoch_cec_t *cec_curr;
s1394_isoch_cec_member_t *member_curr;
t1394_setup_target_args_t target_args;
uint64_t temp_chnl_mask;
uint32_t old_chnl;
uint32_t try_chnl;
uint_t bw_alloc_units;
uint_t generation;
int chnl_num;
int err;
int ret;
int j;
int (*setup_callback)(t1394_isoch_cec_handle_t, opaque_t,
t1394_setup_target_args_t *);
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
mutex_enter(&cec_curr->isoch_cec_mutex);
if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_SETUP) == 0) {
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
if (cec_curr->cec_options & T1394_NO_IRM_ALLOC) {
goto setup_do_callbacks;
}
for (j = 0; j < S1394_ISOCH_ALLOC_RETRIES; j++) {
generation = hal->generation_count;
bw_alloc_units = s1394_compute_bw_alloc_units(hal,
cec_curr->bandwidth, cec_curr->filter_current_speed);
if (generation != hal->generation_count)
continue;
mutex_exit(&cec_curr->isoch_cec_mutex);
ret = s1394_bandwidth_alloc(hal, bw_alloc_units, generation,
&err);
mutex_enter(&cec_curr->isoch_cec_mutex);
if (ret == DDI_FAILURE) {
if (err == CMD1394_EBUSRESET) {
continue;
} else {
*result = T1394_ENO_BANDWIDTH;
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
}
if (generation != hal->generation_count)
continue;
temp_chnl_mask = cec_curr->filter_channel_mask;
for (chnl_num = 63; chnl_num >= 0; chnl_num--) {
if ((temp_chnl_mask & 1) == 1) {
try_chnl = (1 << ((63 - chnl_num) % 32));
mutex_exit(&cec_curr->isoch_cec_mutex);
if (chnl_num < 32) {
ret = s1394_channel_alloc(hal,
try_chnl, generation,
S1394_CHANNEL_ALLOC_HI, &old_chnl,
&err);
} else {
ret = s1394_channel_alloc(hal,
try_chnl, generation,
S1394_CHANNEL_ALLOC_LO, &old_chnl,
&err);
}
mutex_enter(&cec_curr->isoch_cec_mutex);
if ((ret == DDI_SUCCESS) ||
(err == CMD1394_EBUSRESET))
break;
}
temp_chnl_mask = temp_chnl_mask >> 1;
}
if (chnl_num == 0) {
*result = T1394_ENO_CHANNEL;
if (generation != hal->generation_count)
continue;
mutex_exit(&cec_curr->isoch_cec_mutex);
ret = s1394_bandwidth_free(hal, bw_alloc_units,
generation, &err);
mutex_enter(&cec_curr->isoch_cec_mutex);
if (ret == DDI_FAILURE) {
if (err == CMD1394_EBUSRESET) {
continue;
}
}
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
if (ret == DDI_SUCCESS)
break;
else if (err == CMD1394_EBUSRESET)
continue;
}
if (j == S1394_ISOCH_ALLOC_RETRIES) {
*result = T1394_ENO_BANDWIDTH;
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
cec_curr->realloc_valid = B_TRUE;
cec_curr->realloc_chnl_num = chnl_num;
cec_curr->realloc_bandwidth = cec_curr->bandwidth;
cec_curr->realloc_speed = cec_curr->filter_current_speed;
setup_do_callbacks:
target_args.channel_num = chnl_num;
target_args.channel_speed = cec_curr->filter_current_speed;
cec_curr->in_callbacks = B_TRUE;
mutex_exit(&cec_curr->isoch_cec_mutex);
member_curr = cec_curr->cec_member_list_head;
*result = 0;
while (member_curr != NULL) {
if (member_curr->isoch_cec_evts.setup_target != NULL) {
setup_callback =
member_curr->isoch_cec_evts.setup_target;
ret = setup_callback(t1394_isoch_cec_hdl,
member_curr->isoch_cec_evts_arg, &target_args);
if (ret != DDI_SUCCESS)
*result = T1394_ETARGET;
}
member_curr = member_curr->cec_mem_next;
}
mutex_enter(&cec_curr->isoch_cec_mutex);
cec_curr->in_callbacks = B_FALSE;
if (cec_curr->cec_want_wakeup == B_TRUE) {
cec_curr->cec_want_wakeup = B_FALSE;
cv_broadcast(&cec_curr->in_callbacks_cv);
}
CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_JOIN | ISOCH_CEC_FREE |
ISOCH_CEC_SETUP));
mutex_exit(&cec_curr->isoch_cec_mutex);
if (*result != 0) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
int
t1394_start_isoch_cec(t1394_handle_t t1394_hdl,
t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
{
s1394_isoch_cec_t *cec_curr;
s1394_isoch_cec_member_t *member_curr;
int ret;
boolean_t err;
int (*start_callback)(t1394_isoch_cec_handle_t, opaque_t);
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
mutex_enter(&cec_curr->isoch_cec_mutex);
if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_START) == 0) {
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
cec_curr->in_callbacks = B_TRUE;
mutex_exit(&cec_curr->isoch_cec_mutex);
member_curr = cec_curr->cec_member_list_tail;
err = B_FALSE;
while (member_curr != NULL) {
if (member_curr->isoch_cec_evts.start_target != NULL) {
start_callback =
member_curr->isoch_cec_evts.start_target;
ret = start_callback(t1394_isoch_cec_hdl,
member_curr->isoch_cec_evts_arg);
if (ret != DDI_SUCCESS)
err = B_TRUE;
}
member_curr = member_curr->cec_mem_prev;
}
mutex_enter(&cec_curr->isoch_cec_mutex);
cec_curr->in_callbacks = B_FALSE;
if (cec_curr->cec_want_wakeup == B_TRUE) {
cec_curr->cec_want_wakeup = B_FALSE;
cv_broadcast(&cec_curr->in_callbacks_cv);
}
CEC_SET_LEGAL(cec_curr, ISOCH_CEC_STOP);
CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
mutex_exit(&cec_curr->isoch_cec_mutex);
if (err == B_TRUE) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
int
t1394_stop_isoch_cec(t1394_handle_t t1394_hdl,
t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
{
s1394_isoch_cec_t *cec_curr;
s1394_isoch_cec_member_t *member_curr;
void (*stop_callback)(t1394_isoch_cec_handle_t, opaque_t);
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
mutex_enter(&cec_curr->isoch_cec_mutex);
if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_STOP) == 0) {
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
cec_curr->in_callbacks = B_TRUE;
mutex_exit(&cec_curr->isoch_cec_mutex);
member_curr = cec_curr->cec_member_list_head;
while (member_curr != NULL) {
if (member_curr->isoch_cec_evts.stop_target != NULL) {
stop_callback =
member_curr->isoch_cec_evts.stop_target;
stop_callback(t1394_isoch_cec_hdl,
member_curr->isoch_cec_evts_arg);
}
member_curr = member_curr->cec_mem_next;
}
mutex_enter(&cec_curr->isoch_cec_mutex);
cec_curr->in_callbacks = B_FALSE;
if (cec_curr->cec_want_wakeup == B_TRUE) {
cec_curr->cec_want_wakeup = B_FALSE;
cv_broadcast(&cec_curr->in_callbacks_cv);
}
CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_STOP);
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_SUCCESS);
}
int
t1394_teardown_isoch_cec(t1394_handle_t t1394_hdl,
t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
{
s1394_hal_t *hal;
s1394_isoch_cec_t *cec_curr;
s1394_isoch_cec_member_t *member_curr;
uint32_t chnl_mask;
uint32_t old_chnl_mask;
uint_t bw_alloc_units;
uint_t generation;
int ret;
int err;
void (*teardown_callback)(t1394_isoch_cec_handle_t, opaque_t);
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
mutex_enter(&cec_curr->isoch_cec_mutex);
if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_TEARDOWN) == 0) {
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_FAILURE);
}
if (cec_curr->cec_options & T1394_NO_IRM_ALLOC) {
goto teardown_do_callbacks;
}
if ((cec_curr->realloc_valid == B_FALSE) ||
(cec_curr->realloc_failed == B_TRUE)) {
goto teardown_do_callbacks;
}
generation = hal->generation_count;
bw_alloc_units = s1394_compute_bw_alloc_units(hal,
cec_curr->bandwidth, cec_curr->realloc_speed);
if (generation != hal->generation_count)
goto teardown_do_callbacks;
mutex_exit(&cec_curr->isoch_cec_mutex);
ret = s1394_bandwidth_free(hal, bw_alloc_units, generation, &err);
mutex_enter(&cec_curr->isoch_cec_mutex);
if (ret == DDI_FAILURE) {
if (err == CMD1394_EBUSRESET) {
goto teardown_do_callbacks;
}
}
chnl_mask = (1 << ((63 - cec_curr->realloc_chnl_num) % 32));
mutex_exit(&cec_curr->isoch_cec_mutex);
if (cec_curr->realloc_chnl_num < 32) {
ret = s1394_channel_free(hal, chnl_mask, generation,
S1394_CHANNEL_ALLOC_HI, &old_chnl_mask, &err);
} else {
ret = s1394_channel_free(hal, chnl_mask, generation,
S1394_CHANNEL_ALLOC_LO, &old_chnl_mask, &err);
}
mutex_enter(&cec_curr->isoch_cec_mutex);
teardown_do_callbacks:
cec_curr->realloc_valid = B_FALSE;
cec_curr->realloc_chnl_num = 0;
cec_curr->realloc_bandwidth = 0;
cec_curr->in_callbacks = B_TRUE;
mutex_exit(&cec_curr->isoch_cec_mutex);
member_curr = cec_curr->cec_member_list_head;
while (member_curr != NULL) {
if (member_curr->isoch_cec_evts.teardown_target != NULL) {
teardown_callback =
member_curr->isoch_cec_evts.teardown_target;
teardown_callback(t1394_isoch_cec_hdl,
member_curr->isoch_cec_evts_arg);
}
member_curr = member_curr->cec_mem_next;
}
mutex_enter(&cec_curr->isoch_cec_mutex);
cec_curr->in_callbacks = B_FALSE;
if (cec_curr->cec_want_wakeup == B_TRUE) {
cec_curr->cec_want_wakeup = B_FALSE;
cv_broadcast(&cec_curr->in_callbacks_cv);
}
CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_JOIN | ISOCH_CEC_SETUP));
CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
if ((cec_curr->cec_member_list_head == NULL) &&
(cec_curr->cec_member_list_tail == NULL)) {
CEC_SET_LEGAL(cec_curr, ISOCH_CEC_FREE);
}
mutex_exit(&cec_curr->isoch_cec_mutex);
return (DDI_SUCCESS);
}
int
t1394_alloc_isoch_dma(t1394_handle_t t1394_hdl,
id1394_isoch_dmainfo_t *idi, uint_t flags,
t1394_isoch_dma_handle_t *t1394_idma_hdl, int *result)
{
s1394_hal_t *hal;
int ret;
ASSERT(t1394_hdl != NULL);
ASSERT(idi != NULL);
ASSERT(t1394_idma_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
if ((idi->idma_options & ID1394_TALK) &&
(idi->idma_options != ID1394_TALK)) {
*result = T1394_EIDMA_CONFLICT;
return (DDI_FAILURE);
}
if ((idi->idma_options & ID1394_LISTEN_PKT_MODE) &&
(idi->idma_options & ID1394_LISTEN_BUF_MODE)) {
*result = T1394_EIDMA_CONFLICT;
return (DDI_FAILURE);
}
ret = HAL_CALL(hal).alloc_isoch_dma(hal->halinfo.hal_private, idi,
(void **)t1394_idma_hdl, result);
if (ret != DDI_SUCCESS) {
if (*result == IXL1394_ENO_DMA_RESRCS) {
*result = T1394_EIDMA_NO_RESRCS;
}
}
return (ret);
}
void
t1394_free_isoch_dma(t1394_handle_t t1394_hdl, uint_t flags,
t1394_isoch_dma_handle_t *t1394_idma_hdl)
{
s1394_hal_t *hal;
ASSERT(t1394_hdl != NULL);
ASSERT(*t1394_idma_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
HAL_CALL(hal).free_isoch_dma(hal->halinfo.hal_private, *t1394_idma_hdl);
*t1394_idma_hdl = NULL;
}
int
t1394_start_isoch_dma(t1394_handle_t t1394_hdl,
t1394_isoch_dma_handle_t t1394_idma_hdl,
id1394_isoch_dma_ctrlinfo_t *idma_ctrlinfo, uint_t flags,
int *result)
{
s1394_hal_t *hal;
int ret;
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_idma_hdl != NULL);
ASSERT(idma_ctrlinfo != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
ret = HAL_CALL(hal).start_isoch_dma(hal->halinfo.hal_private,
(void *)t1394_idma_hdl, idma_ctrlinfo, flags, result);
return (ret);
}
void
t1394_stop_isoch_dma(t1394_handle_t t1394_hdl,
t1394_isoch_dma_handle_t t1394_idma_hdl, uint_t flags)
{
s1394_hal_t *hal;
int result;
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_idma_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
HAL_CALL(hal).stop_isoch_dma(hal->halinfo.hal_private,
(void *)t1394_idma_hdl, &result);
}
int
t1394_update_isoch_dma(t1394_handle_t t1394_hdl,
t1394_isoch_dma_handle_t t1394_idma_hdl,
id1394_isoch_dma_updateinfo_t *idma_updateinfo, uint_t flags,
int *result)
{
s1394_hal_t *hal;
int ret;
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_idma_hdl != NULL);
ASSERT(idma_updateinfo != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
ret = HAL_CALL(hal).update_isoch_dma(hal->halinfo.hal_private,
(void *)t1394_idma_hdl, idma_updateinfo, flags, result);
return (ret);
}
void
t1394_initiate_bus_reset(t1394_handle_t t1394_hdl, uint_t flags)
{
s1394_hal_t *hal;
ASSERT(t1394_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
if (hal->halinfo.phy == H1394_PHY_1394A) {
(void) HAL_CALL(hal).short_bus_reset(hal->halinfo.hal_private);
} else {
(void) HAL_CALL(hal).bus_reset(hal->halinfo.hal_private);
}
}
int
t1394_get_topology_map(t1394_handle_t t1394_hdl, uint_t bus_generation,
size_t tm_length, uint_t flags, uint32_t *tm_buffer)
{
s1394_hal_t *hal;
uint32_t *tm_ptr;
uint_t length;
ASSERT(t1394_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
mutex_enter(&hal->topology_tree_mutex);
if (bus_generation != hal->generation_count) {
mutex_exit(&hal->topology_tree_mutex);
return (DDI_FAILURE);
}
tm_ptr = (uint32_t *)hal->CSR_topology_map;
length = tm_ptr[0] >> 16;
length = length * 4;
length = length + 4;
if (length > (uint_t)tm_length) {
mutex_exit(&hal->topology_tree_mutex);
return (DDI_FAILURE);
}
bcopy(tm_ptr, tm_buffer, length);
mutex_exit(&hal->topology_tree_mutex);
return (DDI_SUCCESS);
}
uint_t
t1394_CRC16(uint32_t *d, size_t crc_length, uint_t flags)
{
uint_t ret;
ret = s1394_CRC16((uint_t *)d, (uint_t)crc_length);
return (ret);
}
int
t1394_add_cfgrom_entry(t1394_handle_t t1394_hdl,
t1394_cfgrom_entryinfo_t *cfgrom_entryinfo, uint_t flags,
t1394_cfgrom_handle_t *t1394_cfgrom_hdl, int *result)
{
s1394_hal_t *hal;
s1394_target_t *target;
int ret;
uint_t key;
uint_t size;
uint32_t *buffer;
ASSERT(t1394_hdl != NULL);
target = (s1394_target_t *)t1394_hdl;
key = cfgrom_entryinfo->ce_key;
buffer = cfgrom_entryinfo->ce_buffer;
size = (uint_t)cfgrom_entryinfo->ce_size;
if (size == 0) {
*result = T1394_EINVALID_PARAM;
return (DDI_FAILURE);
}
if (((key << IEEE1212_KEY_VALUE_SHIFT) & IEEE1212_KEY_TYPE_MASK) == 0) {
*result = T1394_EINVALID_PARAM;
return (DDI_FAILURE);
}
hal = target->on_hal;
if (servicing_interrupt()) {
*result = T1394_EINVALID_CONTEXT;
return (DDI_FAILURE);
}
mutex_enter(&hal->local_config_rom_mutex);
ret = s1394_add_config_rom_entry(hal, key, buffer, size,
(void **)t1394_cfgrom_hdl, result);
if (ret != DDI_SUCCESS) {
if (*result == CMD1394_ERSRC_CONFLICT)
*result = T1394_ECFGROM_FULL;
mutex_exit(&hal->local_config_rom_mutex);
return (ret);
}
if (hal->config_rom_timer_set == B_FALSE) {
hal->config_rom_timer_set = B_TRUE;
mutex_exit(&hal->local_config_rom_mutex);
hal->config_rom_timer =
timeout(s1394_update_config_rom_callback, hal,
drv_usectohz(CONFIG_ROM_UPDATE_DELAY * 1000));
} else {
mutex_exit(&hal->local_config_rom_mutex);
}
return (ret);
}
int
t1394_rem_cfgrom_entry(t1394_handle_t t1394_hdl, uint_t flags,
t1394_cfgrom_handle_t *t1394_cfgrom_hdl, int *result)
{
s1394_hal_t *hal;
s1394_target_t *target;
int ret;
ASSERT(t1394_hdl != NULL);
target = (s1394_target_t *)t1394_hdl;
hal = target->on_hal;
if (servicing_interrupt()) {
*result = T1394_EINVALID_CONTEXT;
return (DDI_FAILURE);
}
mutex_enter(&hal->local_config_rom_mutex);
ret = s1394_remove_config_rom_entry(hal, (void **)t1394_cfgrom_hdl,
result);
if (ret != DDI_SUCCESS) {
mutex_exit(&hal->local_config_rom_mutex);
return (ret);
}
if (hal->config_rom_timer_set == B_FALSE) {
hal->config_rom_timer_set = B_TRUE;
mutex_exit(&hal->local_config_rom_mutex);
hal->config_rom_timer =
timeout(s1394_update_config_rom_callback, hal,
drv_usectohz(CONFIG_ROM_UPDATE_DELAY * 1000));
} else {
mutex_exit(&hal->local_config_rom_mutex);
}
return (ret);
}
int
t1394_get_targetinfo(t1394_handle_t t1394_hdl, uint_t bus_generation,
uint_t flags, t1394_targetinfo_t *targetinfo)
{
s1394_hal_t *hal;
s1394_target_t *target;
uint_t dev;
uint_t curr;
uint_t from_node;
uint_t to_node;
ASSERT(t1394_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
target = (s1394_target_t *)t1394_hdl;
mutex_enter(&hal->topology_tree_mutex);
if (bus_generation != hal->generation_count) {
mutex_exit(&hal->topology_tree_mutex);
return (DDI_FAILURE);
}
rw_enter(&hal->target_list_rwlock, RW_READER);
if (((target->target_state & S1394_TARG_GONE) != 0) ||
(target->on_node == NULL)) {
targetinfo->target_nodeID = T1394_INVALID_NODEID;
} else {
targetinfo->target_nodeID =
(target->on_hal->node_id & IEEE1394_BUS_NUM_MASK) |
target->on_node->node_num;
from_node = (target->on_hal->node_id) & IEEE1394_NODE_NUM_MASK;
to_node = target->on_node->node_num;
targetinfo->current_max_speed = (uint_t)s1394_speed_map_get(
hal, from_node, to_node);
s1394_get_maxpayload(target, &dev, &curr);
targetinfo->current_max_payload = curr;
}
rw_exit(&hal->target_list_rwlock);
mutex_exit(&hal->topology_tree_mutex);
return (DDI_SUCCESS);
}