#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/1394/h1394.h>
#include <sys/1394/adapters/hci1394.h>
uint_t hci1394_iso_ctxt_stop_delay_uS = 1000;
uint_t hci1394_iso_ctxt_stop_intr_timeout_uS = 5 * 1000000;
void
hci1394_isoch_init(hci1394_drvinfo_t *drvinfo, hci1394_ohci_handle_t ohci,
hci1394_isoch_handle_t *isoch_hdl)
{
hci1394_isoch_t *isochp;
int i;
ASSERT(drvinfo != NULL);
ASSERT(isoch_hdl != NULL);
isochp = kmem_alloc(sizeof (hci1394_isoch_t), KM_SLEEP);
for (i = 0; i < HCI1394_MAX_ISOCH_CONTEXTS; i++) {
isochp->ctxt_xmit[i].ctxt_index = i;
isochp->ctxt_xmit[i].ctxt_flags = 0;
mutex_init(&isochp->ctxt_xmit[i].intrprocmutex, NULL,
MUTEX_DRIVER, drvinfo->di_iblock_cookie);
cv_init(&isochp->ctxt_xmit[i].intr_cv, NULL,
CV_DRIVER, NULL);
isochp->ctxt_recv[i].ctxt_index = i;
isochp->ctxt_recv[i].ctxt_flags = HCI1394_ISO_CTXT_RECV;
mutex_init(&isochp->ctxt_recv[i].intrprocmutex, NULL,
MUTEX_DRIVER, drvinfo->di_iblock_cookie);
cv_init(&isochp->ctxt_recv[i].intr_cv, NULL,
CV_DRIVER, NULL);
}
isochp->isoch_dma_alloc_cnt = 0;
isochp->cycle_lost_thresh.last_intr_time = 0;
isochp->cycle_lost_thresh.delta_t_counter = 0;
isochp->cycle_lost_thresh.delta_t_thresh = HCI1394_CYC_LOST_DELTA;
isochp->cycle_lost_thresh.counter_thresh = HCI1394_CYC_LOST_COUNT;
isochp->cycle_incon_thresh.last_intr_time = 0;
isochp->cycle_incon_thresh.delta_t_counter = 0;
isochp->cycle_incon_thresh.delta_t_thresh = HCI1394_CYC_INCON_DELTA;
isochp->cycle_incon_thresh.counter_thresh = HCI1394_CYC_INCON_COUNT;
isochp->ctxt_xmit_count = hci1394_ohci_it_ctxt_count_get(ohci);
isochp->ctxt_recv_count = hci1394_ohci_ir_ctxt_count_get(ohci);
mutex_init(&isochp->ctxt_list_mutex, NULL, MUTEX_DRIVER,
drvinfo->di_iblock_cookie);
*isoch_hdl = isochp;
}
void
hci1394_isoch_fini(hci1394_isoch_handle_t *isoch_hdl)
{
hci1394_isoch_t *isochp;
int i;
ASSERT(isoch_hdl != NULL);
isochp = *isoch_hdl;
for (i = 0; i < HCI1394_MAX_ISOCH_CONTEXTS; i++) {
mutex_destroy(&isochp->ctxt_xmit[i].intrprocmutex);
mutex_destroy(&isochp->ctxt_recv[i].intrprocmutex);
cv_destroy(&isochp->ctxt_xmit[i].intr_cv);
cv_destroy(&isochp->ctxt_recv[i].intr_cv);
}
mutex_destroy(&isochp->ctxt_list_mutex);
kmem_free(isochp, sizeof (hci1394_isoch_t));
*isoch_hdl = NULL;
}
int
hci1394_isoch_resume(hci1394_state_t *soft_state)
{
return (DDI_SUCCESS);
}
int
hci1394_alloc_isoch_dma(void *hal_private, id1394_isoch_dmainfo_t *idi,
void **hal_idma_handlep, int *resultp)
{
int i;
int err;
hci1394_state_t *soft_statep = (hci1394_state_t *)hal_private;
hci1394_isoch_t *isochp;
hci1394_iso_ctxt_t *ctxtp;
ASSERT(soft_statep != NULL);
ASSERT(hal_idma_handlep != NULL);
isochp = soft_statep->isoch;
*hal_idma_handlep = NULL;
mutex_enter(&isochp->ctxt_list_mutex);
if ((idi->idma_options & ID1394_TALK) != 0) {
for (i = 0; i < isochp->ctxt_xmit_count; i++) {
if ((isochp->ctxt_xmit[i].ctxt_flags &
HCI1394_ISO_CTXT_INUSE) == 0) {
break;
}
}
if (i >= isochp->ctxt_xmit_count) {
mutex_exit(&isochp->ctxt_list_mutex);
*resultp = IXL1394_ENO_DMA_RESRCS;
return (DDI_FAILURE);
}
isochp->ctxt_xmit[i].ctxt_flags |= HCI1394_ISO_CTXT_INUSE;
ctxtp = &isochp->ctxt_xmit[i];
isochp->ctxt_xmit[i].ctxt_regsp =
&soft_statep->ohci->ohci_regs->it[i];
} else {
for (i = 0; i < isochp->ctxt_recv_count; i++) {
if ((isochp->ctxt_recv[i].ctxt_flags &
HCI1394_ISO_CTXT_INUSE) == 0) {
break;
}
}
if (i >= isochp->ctxt_recv_count) {
mutex_exit(&isochp->ctxt_list_mutex);
*resultp = IXL1394_ENO_DMA_RESRCS;
return (DDI_FAILURE);
}
if ((idi->idma_options & ID1394_LISTEN_BUF_MODE) != 0) {
isochp->ctxt_recv[i].ctxt_flags |=
HCI1394_ISO_CTXT_BFFILL;
}
if ((idi->idma_options & ID1394_RECV_HEADERS) != 0) {
isochp->ctxt_recv[i].ctxt_flags |=
HCI1394_ISO_CTXT_RHDRS;
}
isochp->ctxt_recv[i].ctxt_flags |= HCI1394_ISO_CTXT_INUSE;
ctxtp = &isochp->ctxt_recv[i];
isochp->ctxt_recv[i].ctxt_regsp = (hci1394_ctxt_regs_t *)
&soft_statep->ohci->ohci_regs->ir[i];
}
mutex_exit(&isochp->ctxt_list_mutex);
ctxtp->isochan = idi->channel_num;
ctxtp->default_tag = idi->default_tag;
ctxtp->default_sync = idi->default_sync;
ctxtp->global_callback_arg = idi->global_callback_arg;
ctxtp->isoch_dma_stopped = idi->isoch_dma_stopped;
ctxtp->idma_evt_arg = idi->idma_evt_arg;
ctxtp->isospd = idi->it_speed;
ctxtp->default_skipmode = idi->it_default_skip;
ctxtp->default_skiplabelp = idi->it_default_skiplabel;
err = hci1394_compile_ixl(soft_statep, ctxtp, idi->ixlp, resultp);
if (err != DDI_SUCCESS) {
mutex_enter(&isochp->ctxt_list_mutex);
if ((ctxtp->ctxt_flags & HCI1394_ISO_CTXT_RECV) != 0) {
isochp->ctxt_recv[i].ctxt_flags &=
~HCI1394_ISO_CTXT_BFFILL;
isochp->ctxt_recv[i].ctxt_flags &=
~HCI1394_ISO_CTXT_RHDRS;
}
ctxtp->ctxt_flags &= ~HCI1394_ISO_CTXT_INUSE;
mutex_exit(&isochp->ctxt_list_mutex);
return (DDI_FAILURE);
}
mutex_enter(&isochp->ctxt_list_mutex);
if (isochp->isoch_dma_alloc_cnt == 0) {
hci1394_ohci_intr_clear(soft_statep->ohci,
OHCI_INTR_CYC_LOST | OHCI_INTR_CYC_INCONSISTENT);
hci1394_ohci_intr_enable(soft_statep->ohci,
OHCI_INTR_CYC_LOST | OHCI_INTR_CYC_INCONSISTENT);
}
isochp->isoch_dma_alloc_cnt++;
mutex_exit(&isochp->ctxt_list_mutex);
ctxtp->intr_flags = 0;
*hal_idma_handlep = ctxtp;
return (DDI_SUCCESS);
}
int
hci1394_start_isoch_dma(void *hal_private, void *hal_isoch_dma_handle,
id1394_isoch_dma_ctrlinfo_t *idma_ctrlinfop, uint_t flags, int *result)
{
hci1394_state_t *soft_statep = (hci1394_state_t *)hal_private;
hci1394_iso_ctxt_t *ctxtp;
int tag0, tag1, tag2, tag3;
ctxtp = (hci1394_iso_ctxt_t *)hal_isoch_dma_handle;
ASSERT(hal_private != NULL);
ASSERT(ctxtp != NULL);
ASSERT(idma_ctrlinfop != NULL);
mutex_enter(&soft_statep->isoch->ctxt_list_mutex);
if ((ctxtp->ctxt_flags & HCI1394_ISO_CTXT_RUNNING) != 0) {
mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
return (DDI_SUCCESS);
}
ctxtp->ctxt_flags |= HCI1394_ISO_CTXT_RUNNING;
mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
ctxtp->intr_flags &= ~HCI1394_ISO_CTXT_STOP;
ctxtp->ixl_execp = ctxtp->ixl_firstp;
ctxtp->ixl_exec_depth = 0;
ctxtp->dma_last_time = 0;
ctxtp->rem_noadv_intrs = ctxtp->max_noadv_intrs;
hci1394_ixl_reset_status(ctxtp);
if (ctxtp->ctxt_flags & HCI1394_ISO_CTXT_RECV) {
hci1394_ohci_ir_cmd_ptr_set(soft_statep->ohci,
ctxtp->ctxt_index, ctxtp->dma_mem_execp);
tag0 = 0;
tag1 = 1;
tag2 = 2;
tag3 = 3;
if (ctxtp->default_tag == 0x0)
tag0 = 1;
else if (ctxtp->default_tag == 0x1)
tag1 = 1;
else if (ctxtp->default_tag == 0x2)
tag2 = 1;
else if (ctxtp->default_tag == 0x3)
tag3 = 1;
HCI1394_IRCTXT_MATCH_WRITE(soft_statep, ctxtp->ctxt_index, tag3,
tag2, tag1, tag0,
idma_ctrlinfop->start_cycle ,
ctxtp->default_sync , 0 ,
ctxtp->isochan );
HCI1394_IRCTXT_CTRL_CLR(soft_statep, ctxtp->ctxt_index,
(uint32_t)1, 1, 1, 1, 1);
HCI1394_IRCTXT_CTRL_SET(soft_statep, ctxtp->ctxt_index,
(ctxtp->ctxt_flags & HCI1394_ISO_CTXT_BFFILL) != 0 ,
(ctxtp->ctxt_flags & HCI1394_ISO_CTXT_RHDRS) != 0 ,
(flags & ID1394_START_ON_CYCLE) != 0 ,
0 , 1 , 0 );
hci1394_ohci_ir_intr_clear(soft_statep->ohci,
(uint32_t)(0x1 << ctxtp->ctxt_index));
hci1394_ohci_ir_intr_enable(soft_statep->ohci,
(uint32_t)(0x1 << ctxtp->ctxt_index));
} else {
hci1394_ohci_it_cmd_ptr_set(soft_statep->ohci,
ctxtp->ctxt_index, ctxtp->dma_mem_execp);
HCI1394_ITCTXT_CTRL_SET(soft_statep, ctxtp->ctxt_index,
((flags & ID1394_START_ON_CYCLE) != 0) ,
idma_ctrlinfop->start_cycle ,
1 , 0 );
hci1394_ohci_it_intr_clear(soft_statep->ohci,
(uint32_t)(0x1 << ctxtp->ctxt_index));
hci1394_ohci_it_intr_enable(soft_statep->ohci,
(uint32_t)(0x1 << ctxtp->ctxt_index));
}
return (DDI_SUCCESS);
}
int
hci1394_update_isoch_dma(void *hal_private, void *hal_isoch_dma_handle,
id1394_isoch_dma_updateinfo_t *idma_updateinfop, uint_t flags, int *resultp)
{
hci1394_state_t *soft_statep = (hci1394_state_t *)hal_private;
hci1394_iso_ctxt_t *ctxtp;
ixl1394_command_t *cur_new_ixlp;
ixl1394_command_t *cur_orig_ixlp;
int ii;
int err = DDI_SUCCESS;
ctxtp = (hci1394_iso_ctxt_t *)hal_isoch_dma_handle;
ASSERT(hal_private != NULL);
ASSERT(ctxtp != NULL);
ASSERT(idma_updateinfop != NULL);
cur_new_ixlp = idma_updateinfop->temp_ixlp;
cur_orig_ixlp = idma_updateinfop->orig_ixlp;
ASSERT(cur_new_ixlp != NULL);
ASSERT(cur_orig_ixlp != NULL);
for (ii = 0; (ii < idma_updateinfop->ixl_count) && (err == DDI_SUCCESS);
ii++) {
if ((cur_new_ixlp == NULL) || (cur_orig_ixlp == NULL)) {
*resultp = IXL1394_ECOUNT_MISMATCH;
err = DDI_FAILURE;
break;
}
err = hci1394_ixl_update(soft_statep, ctxtp, cur_new_ixlp,
cur_orig_ixlp, 0, resultp);
cur_new_ixlp = cur_new_ixlp->next_ixlp;
cur_orig_ixlp = cur_orig_ixlp->next_ixlp;
}
return (err);
}
void
hci1394_stop_isoch_dma(void *hal_private, void *hal_isoch_dma_handle,
int *result)
{
hci1394_state_t *soft_statep = (hci1394_state_t *)hal_private;
hci1394_iso_ctxt_t *ctxtp;
ctxtp = (hci1394_iso_ctxt_t *)hal_isoch_dma_handle;
ASSERT(hal_private != NULL);
ASSERT(ctxtp != NULL);
hci1394_do_stop(soft_statep, ctxtp, B_FALSE, 0);
hci1394_ixl_interrupt(soft_statep, ctxtp, B_TRUE);
}
void
hci1394_do_stop(hci1394_state_t *soft_statep, hci1394_iso_ctxt_t *ctxtp,
boolean_t do_callback, id1394_isoch_dma_stopped_t stop_args)
{
int count;
clock_t upto;
mutex_enter(&soft_statep->isoch->ctxt_list_mutex);
if ((ctxtp->ctxt_flags & HCI1394_ISO_CTXT_RUNNING) == 0) {
mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
return;
}
ctxtp->ctxt_flags &= ~HCI1394_ISO_CTXT_RUNNING;
mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
if (ctxtp->ctxt_flags & HCI1394_ISO_CTXT_RECV) {
hci1394_ohci_ir_intr_disable(soft_statep->ohci,
(uint32_t)(0x1 << ctxtp->ctxt_index));
HCI1394_IRCTXT_CTRL_CLR(soft_statep, ctxtp->ctxt_index,
0 , 0 , 0 ,
0 , 1 );
} else {
hci1394_ohci_it_intr_disable(soft_statep->ohci,
(uint32_t)(0x1 << ctxtp->ctxt_index));
HCI1394_ITCTXT_CTRL_CLR(soft_statep, ctxtp->ctxt_index,
0 , 0 , 1 );
}
mutex_enter(&ctxtp->intrprocmutex);
ctxtp->intr_flags |= HCI1394_ISO_CTXT_STOP;
if (ctxtp->intr_flags & HCI1394_ISO_CTXT_ININTR) {
upto = ddi_get_lbolt() +
drv_usectohz(hci1394_iso_ctxt_stop_intr_timeout_uS);
while (ctxtp->intr_flags & HCI1394_ISO_CTXT_ININTR) {
if (cv_timedwait(&ctxtp->intr_cv, &ctxtp->intrprocmutex,
upto) <= 0) {
break;
}
}
}
mutex_exit(&ctxtp->intrprocmutex);
count = 0;
while (count < hci1394_iso_ctxt_stop_delay_uS) {
if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) == 0)
break;
drv_usecwait(1);
count++;
}
if (count >= hci1394_iso_ctxt_stop_delay_uS) {
h1394_error_detected(soft_statep->drvinfo.di_sl_private,
H1394_SELF_INITIATED_SHUTDOWN, NULL);
cmn_err(CE_WARN, "hci1394(%d): driver shutdown: "
"unable to stop isoch context",
soft_statep->drvinfo.di_instance);
hci1394_shutdown(soft_statep->drvinfo.di_dip);
return;
}
if (do_callback == B_TRUE) {
if (ctxtp->isoch_dma_stopped != NULL) {
ctxtp->isoch_dma_stopped(
(struct isoch_dma_handle *)ctxtp,
ctxtp->idma_evt_arg, stop_args);
}
}
}
void
hci1394_free_isoch_dma(void *hal_private, void *hal_isoch_dma_handle)
{
hci1394_state_t *soft_statep = (hci1394_state_t *)hal_private;
hci1394_iso_ctxt_t *ctxtp;
hci1394_isoch_t *isochp;
ctxtp = (hci1394_iso_ctxt_t *)hal_isoch_dma_handle;
ASSERT(soft_statep);
ASSERT(ctxtp);
isochp = soft_statep->isoch;
mutex_enter(&soft_statep->isoch->ctxt_list_mutex);
hci1394_ixl_cleanup(soft_statep, ctxtp);
ctxtp->ctxt_flags &= ~(HCI1394_ISO_CTXT_INUSE |
HCI1394_ISO_CTXT_BFFILL | HCI1394_ISO_CTXT_RHDRS);
ASSERT(isochp->isoch_dma_alloc_cnt > 0);
isochp->isoch_dma_alloc_cnt--;
if (isochp->isoch_dma_alloc_cnt == 0) {
hci1394_ohci_intr_disable(soft_statep->ohci,
OHCI_INTR_CYC_LOST | OHCI_INTR_CYC_INCONSISTENT);
}
mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
}
int
hci1394_isoch_recv_count_get(hci1394_isoch_handle_t isoch_hdl)
{
ASSERT(isoch_hdl != NULL);
return (isoch_hdl->ctxt_recv_count);
}
hci1394_iso_ctxt_t *
hci1394_isoch_recv_ctxt_get(hci1394_isoch_handle_t isoch_hdl, int num)
{
ASSERT(isoch_hdl != NULL);
return (&isoch_hdl->ctxt_recv[num]);
}
int
hci1394_isoch_xmit_count_get(hci1394_isoch_handle_t isoch_hdl)
{
ASSERT(isoch_hdl != NULL);
return (isoch_hdl->ctxt_xmit_count);
}
hci1394_iso_ctxt_t *
hci1394_isoch_xmit_ctxt_get(hci1394_isoch_handle_t isoch_hdl, int num)
{
ASSERT(isoch_hdl != NULL);
return (&isoch_hdl->ctxt_xmit[num]);
}
void
hci1394_isoch_error_ints_enable(hci1394_state_t *soft_statep)
{
ASSERT(soft_statep);
mutex_enter(&soft_statep->isoch->ctxt_list_mutex);
if (soft_statep->isoch->isoch_dma_alloc_cnt != 0) {
soft_statep->isoch->cycle_lost_thresh.delta_t_counter = 0;
soft_statep->isoch->cycle_incon_thresh.delta_t_counter = 0;
hci1394_ohci_intr_clear(soft_statep->ohci,
OHCI_INTR_CYC_LOST | OHCI_INTR_CYC_INCONSISTENT);
hci1394_ohci_intr_enable(soft_statep->ohci,
OHCI_INTR_CYC_LOST | OHCI_INTR_CYC_INCONSISTENT);
}
mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
}