/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* hci1394_ixl_misc.c
* Isochronous IXL miscellaneous routines.
* Contains common routines used by the ixl compiler, interrupt handler and
* dynamic update.
*/
#include <sys/kmem.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/tnf_probe.h>
#include <sys/1394/h1394.h>
#include <sys/1394/ixl1394.h>
#include <sys/1394/adapters/hci1394.h>
/* local routines */
static void hci1394_delete_dma_desc_mem(hci1394_state_t *soft_statep,
hci1394_idma_desc_mem_t *);
static void hci1394_delete_xfer_ctl(hci1394_xfer_ctl_t *);
/*
* hci1394_ixl_set_start()
* Set up the context structure with the first ixl command to process
* and the first hci descriptor to execute.
*
* This function assumes the current context is stopped!
*
* If ixlstp IS NOT null AND is not the first compiled ixl command and
* is not an ixl label command, returns an error.
* If ixlstp IS null, uses the first compiled ixl command (ixl_firstp)
* in place of ixlstp.
*
* If no executeable xfer found along exec path from ixlstp, returns error.
*/
int
hci1394_ixl_set_start(hci1394_iso_ctxt_t *ctxtp, ixl1394_command_t *ixlstp)
{
ixl1394_command_t *ixl_exec_startp;
TNF_PROBE_0_DEBUG(hci1394_ixl_set_start_enter,
HCI1394_TNF_HAL_STACK_ISOCH, "");
/* if ixl start command is null, use first compiled ixl command */
if (ixlstp == NULL) {
ixlstp = ctxtp->ixl_firstp;
}
/*
* if ixl start command is not first ixl compiled and is not a label,
* error
*/
if ((ixlstp != ctxtp->ixl_firstp) && (ixlstp->ixl_opcode !=
IXL1394_OP_LABEL)) {
TNF_PROBE_0_DEBUG(hci1394_ixl_set_start_exit,
HCI1394_TNF_HAL_STACK_ISOCH, "");
return (-1);
}
/* follow exec path to find first ixl command that's an xfer command */
(void) hci1394_ixl_find_next_exec_xfer(ixlstp, NULL, &ixl_exec_startp);
/*
* if there was one, then in it's compiler private, its
* hci1394_xfer_ctl structure has the appropriate bound address
*/
if (ixl_exec_startp != NULL) {
/* set up for start of context and return done */
ctxtp->dma_mem_execp = (uint32_t)((hci1394_xfer_ctl_t *)
ixl_exec_startp->compiler_privatep)->dma[0].dma_bound;
ctxtp->dma_last_time = 0;
ctxtp->ixl_exec_depth = 0;
ctxtp->ixl_execp = ixlstp;
ctxtp->rem_noadv_intrs = ctxtp->max_noadv_intrs;
TNF_PROBE_0_DEBUG(hci1394_ixl_set_start_exit,
HCI1394_TNF_HAL_STACK_ISOCH, "");
return (0);
}
/* else no executeable xfer command found, return error */
TNF_PROBE_0_DEBUG(hci1394_ixl_set_start_exit,
HCI1394_TNF_HAL_STACK_ISOCH, "");
return (1);
}
#ifdef _KERNEL
/*
* hci1394_ixl_reset_status()
* Reset all statuses in all hci descriptor blocks associated with the
* current linked list of compiled ixl commands.
*
* This function assumes the current context is stopped!
*/
void
hci1394_ixl_reset_status(hci1394_iso_ctxt_t *ctxtp)
{
ixl1394_command_t *ixlcur;
ixl1394_command_t *ixlnext;
hci1394_xfer_ctl_t *xferctlp;
uint_t ixldepth;
uint16_t timestamp;
TNF_PROBE_0_DEBUG(hci1394_ixl_reset_status_enter,
HCI1394_TNF_HAL_STACK_ISOCH, "");
ixlnext = ctxtp->ixl_firstp;
/*
* Scan for next ixl xfer start command along ixl link path.
* Once xfer command found, clear its hci descriptor block's
* status. If is composite ixl xfer command, clear statuses
* in each of its hci descriptor blocks.
*/
while (ixlnext != NULL) {
/* set current and next ixl command */
ixlcur = ixlnext;
ixlnext = ixlcur->next_ixlp;
/* skip to examine next if this is not xfer start ixl command */
if (((ixlcur->ixl_opcode & IXL1394_OPF_ISXFER) == 0) ||
((ixlcur->ixl_opcode & IXL1394_OPTY_MASK) == 0)) {
continue;
}
/* get control struct for this xfer start ixl command */
xferctlp = (hci1394_xfer_ctl_t *)ixlcur->compiler_privatep;
/* clear status in each hci descriptor block for this ixl cmd */
ixldepth = 0;
while (ixldepth < xferctlp->cnt) {
(void) hci1394_ixl_check_status(
&xferctlp->dma[ixldepth], ixlcur->ixl_opcode,
&timestamp, B_TRUE);
ixldepth++;
}
}
TNF_PROBE_0_DEBUG(hci1394_ixl_reset_status_exit,
HCI1394_TNF_HAL_STACK_ISOCH, "");
}
#endif
/*
* hci1394_ixl_find_next_exec_xfer()
* Follows execution path of ixl linked list until finds next xfer start IXL
* command, including the current IXL command or finds end of IXL linked
* list. Counts callback commands found along the way. (Previously, counted
* store timestamp commands, as well.)
*
* To detect an infinite loop of label<->jump without an intervening xfer,
* a tolerance level of HCI1394_IXL_MAX_SEQ_JUMPS is used. Once this
* number of jumps is traversed, the IXL prog is assumed to have a loop.
*
* Returns DDI_SUCCESS or DDI_FAILURE. DDI_FAILURE, indicates an infinite
* loop of labels & jumps was detected without any intervening xfers.
* DDI_SUCCESS indicates the next_exec_ixlpp contains the next xfer ixlp
* address, or NULL indicating the end of the list was reached. Note that
* DDI_FAILURE can only be returned during the IXL compilation phase, and
* not during ixl_update processing.
*/
int
hci1394_ixl_find_next_exec_xfer(ixl1394_command_t *ixl_start,
uint_t *callback_cnt, ixl1394_command_t **next_exec_ixlpp)
{
uint16_t ixlopcode;
boolean_t xferfound;
ixl1394_command_t *ixlp;
int ii;
TNF_PROBE_0_DEBUG(hci1394_ixl_find_next_exec_xfer_enter,
HCI1394_TNF_HAL_STACK_ISOCH, "");
ixlp = ixl_start;
xferfound = B_FALSE;
ii = HCI1394_IXL_MAX_SEQ_JUMPS;
if (callback_cnt != NULL) {
*callback_cnt = 0;
}
/* continue until xfer start ixl cmd or end of ixl list found */
while ((xferfound == B_FALSE) && (ixlp != NULL) && (ii > 0)) {
/* get current ixl cmd opcode without update flag */
ixlopcode = ixlp->ixl_opcode & ~IXL1394_OPF_UPDATE;
/* if found an xfer start ixl command, are done */
if (((ixlopcode & IXL1394_OPF_ISXFER) != 0) &&
((ixlopcode & IXL1394_OPTY_MASK) != 0)) {
xferfound = B_TRUE;
continue;
}
/* if found jump command, adjust to follow its path */
if (ixlopcode == IXL1394_OP_JUMP) {
ixlp = (ixl1394_command_t *)
((ixl1394_jump_t *)ixlp)->label;
ii--;
/* if exceeded tolerance, give up */
if (ii == 0) {
TNF_PROBE_1(
hci1394_ixl_find_next_exec_xfer_error,
HCI1394_TNF_HAL_ERROR_ISOCH, "", tnf_string,
errmsg, "Infinite loop w/no xfers");
TNF_PROBE_0_DEBUG(
hci1394_ixl_find_next_exec_xfer_exit,
HCI1394_TNF_HAL_STACK_ISOCH, "");
return (DDI_FAILURE);
}
continue;
}
/* if current ixl command is a callback, count it */
if ((ixlopcode == IXL1394_OP_CALLBACK) &&
(callback_cnt != NULL)) {
(*callback_cnt)++;
}
/* advance to next linked ixl command */
ixlp = ixlp->next_ixlp;
}
/* return ixl xfer start command found, if any */
*next_exec_ixlpp = ixlp;
TNF_PROBE_0_DEBUG(hci1394_ixl_find_next_exec_xfer_exit,
HCI1394_TNF_HAL_STACK_ISOCH, "");
return (DDI_SUCCESS);
}
#ifdef _KERNEL
/*
* hci1394_ixl_check_status()
* Read the descriptor status and hdrs, clear as appropriate.
*/
int32_t
hci1394_ixl_check_status(hci1394_xfer_ctl_dma_t *dma, uint16_t ixlopcode,
uint16_t *timestamp, boolean_t do_status_reset)
{
uint16_t bufsiz;
uint16_t hcicnt;
uint16_t hcirecvcnt;
hci1394_desc_t *hcidescp;
off_t hcidesc_off;
ddi_acc_handle_t acc_hdl;
ddi_dma_handle_t dma_hdl;
uint32_t desc_status;
uint32_t desc_hdr;
int err;
TNF_PROBE_0_DEBUG(hci1394_ixl_check_status_enter,
HCI1394_TNF_HAL_STACK_ISOCH, "");
/* last dma descriptor in descriptor block from dma structure */
hcidescp = (hci1394_desc_t *)(dma->dma_descp);
hcidesc_off = (off_t)hcidescp - (off_t)dma->dma_buf->bi_kaddr;
acc_hdl = dma->dma_buf->bi_handle;
dma_hdl = dma->dma_buf->bi_dma_handle;
/* if current ixl command opcode is xmit */
if ((ixlopcode & IXL1394_OPF_ONXMIT) != 0) {
/* Sync the descriptor before we get the status */
err = ddi_dma_sync(dma_hdl, hcidesc_off,
sizeof (hci1394_desc_t), DDI_DMA_SYNC_FORCPU);
if (err != DDI_SUCCESS) {
TNF_PROBE_1(hci1394_ixl_check_status_error,
HCI1394_TNF_HAL_ERROR_ISOCH, "", tnf_string, errmsg,
"dma_sync() failed");
}
desc_status = ddi_get32(acc_hdl, &hcidescp->status);
/* check if status is set in last dma descriptor in block */
if ((desc_status & DESC_XFER_ACTIVE_MASK) != 0) {
/*
* dma descriptor status set - I/O done.
* if not to reset status, just return; else extract
* timestamp, reset desc status and return dma
* descriptor block status set
*/
if (do_status_reset == B_FALSE) {
return (1);
}
*timestamp = (uint16_t)
((desc_status & DESC_ST_TIMESTAMP_MASK) >>
DESC_ST_TIMESTAMP_SHIFT);
ddi_put32(acc_hdl, &hcidescp->status, 0);
/* Sync descriptor for device (status was cleared) */
err = ddi_dma_sync(dma_hdl, hcidesc_off,
sizeof (hci1394_desc_t), DDI_DMA_SYNC_FORDEV);
if (err != DDI_SUCCESS) {
TNF_PROBE_1(hci1394_ixl_check_status_error,
HCI1394_TNF_HAL_ERROR_ISOCH, "", tnf_string,
errmsg, "dma_sync() failed");
}
TNF_PROBE_0_DEBUG(hci1394_ixl_check_status_exit,
HCI1394_TNF_HAL_STACK_ISOCH, "");
return (1);
}
/* else, return dma descriptor block status not set */
TNF_PROBE_0_DEBUG(hci1394_ixl_check_status_exit,
HCI1394_TNF_HAL_STACK_ISOCH, "");
return (0);
}
/* else current ixl opcode is recv */
hcirecvcnt = 0;
/* get count of descriptors in current dma descriptor block */
hcicnt = dma->dma_bound & DESC_Z_MASK;
hcidescp -= (hcicnt - 1);
hcidesc_off = (off_t)hcidescp - (off_t)dma->dma_buf->bi_kaddr;
/* iterate fwd through hci descriptors until end or find status set */
while (hcicnt-- != 0) {
/* Sync the descriptor before we get the status */
err = ddi_dma_sync(dma_hdl, hcidesc_off,
hcicnt * sizeof (hci1394_desc_t), DDI_DMA_SYNC_FORCPU);
if (err != DDI_SUCCESS) {
TNF_PROBE_1(hci1394_ixl_check_status_error,
HCI1394_TNF_HAL_ERROR_ISOCH, "", tnf_string, errmsg,
"dma_sync() failed");
}
desc_hdr = ddi_get32(acc_hdl, &hcidescp->hdr);
/* get cur buffer size & accumulate potential buffr usage */
bufsiz = (desc_hdr & DESC_HDR_REQCOUNT_MASK) >>
DESC_HDR_REQCOUNT_SHIFT;
hcirecvcnt += bufsiz;
desc_status = ddi_get32(acc_hdl, &hcidescp->status);
/* check if status set on this descriptor block descriptor */
if ((desc_status & DESC_XFER_ACTIVE_MASK) != 0) {
/*
* dma descriptor status set - I/O done.
* if not to reset status, just return; else extract
* buffer space used, reset desc status and return dma
* descriptor block status set
*/
if (do_status_reset == B_FALSE) {
TNF_PROBE_0_DEBUG(hci1394_ixl_check_status_exit,
HCI1394_TNF_HAL_STACK_ISOCH, "");
return (1);
}
hcirecvcnt -= (desc_status & DESC_ST_RESCOUNT_MASK) >>
DESC_ST_RESCOUNT_SHIFT;
*timestamp = hcirecvcnt;
desc_status = (bufsiz << DESC_ST_RESCOUNT_SHIFT) &
DESC_ST_RESCOUNT_MASK;
ddi_put32(acc_hdl, &hcidescp->status, desc_status);
/* Sync descriptor for device (status was cleared) */
err = ddi_dma_sync(dma_hdl, hcidesc_off,
sizeof (hci1394_desc_t), DDI_DMA_SYNC_FORDEV);
if (err != DDI_SUCCESS) {
TNF_PROBE_1(hci1394_ixl_check_status_error,
HCI1394_TNF_HAL_ERROR_ISOCH, "", tnf_string,
errmsg, "dma_sync() failed");
}
TNF_PROBE_0_DEBUG(hci1394_ixl_check_status_exit,
HCI1394_TNF_HAL_STACK_ISOCH, "");
return (1);
} else {
/* else, set to evaluate next descriptor. */
hcidescp++;
hcidesc_off = (off_t)hcidescp -
(off_t)dma->dma_buf->bi_kaddr;
}
}
/* return input not complete status */
TNF_PROBE_0_DEBUG(hci1394_ixl_check_status_exit,
HCI1394_TNF_HAL_STACK_ISOCH, "");
return (0);
}
#endif
/*
* hci1394_ixl_cleanup()
* Delete all memory that has earlier been allocated for a context's IXL prog
*/
void
hci1394_ixl_cleanup(hci1394_state_t *soft_statep, hci1394_iso_ctxt_t *ctxtp)
{
TNF_PROBE_0_DEBUG(hci1394_ixl_cleanup_enter,
HCI1394_TNF_HAL_STACK_ISOCH, "");
hci1394_delete_xfer_ctl((hci1394_xfer_ctl_t *)ctxtp->xcs_firstp);
hci1394_delete_dma_desc_mem(soft_statep, ctxtp->dma_firstp);
TNF_PROBE_0_DEBUG(hci1394_ixl_cleanup_exit,
HCI1394_TNF_HAL_STACK_ISOCH, "");
}
/*
* hci1394_delete_dma_desc_mem()
* Iterate through linked list of dma memory descriptors, deleting
* allocated dma memory blocks, then deleting the dma memory
* descriptor after advancing to next one
*/
static void
/* ARGSUSED */
hci1394_delete_dma_desc_mem(hci1394_state_t *soft_statep,
hci1394_idma_desc_mem_t *dma_firstp)
{
hci1394_idma_desc_mem_t *dma_next;
TNF_PROBE_0_DEBUG(hci1394_delete_dma_desc_mem_enter,
HCI1394_TNF_HAL_STACK_ISOCH, "");
while (dma_firstp != NULL) {
dma_next = dma_firstp->dma_nextp;
#ifdef _KERNEL
/*
* if this dma descriptor memory block has the handles, then
* free the memory. (Note that valid handles are kept only with
* the most recently acquired cookie, and that each cookie is in
* it's own idma_desc_mem_t struct.)
*/
if (dma_firstp->mem_handle != NULL) {
hci1394_buf_free(&dma_firstp->mem_handle);
}
/* free current dma memory descriptor */
kmem_free(dma_firstp, sizeof (hci1394_idma_desc_mem_t));
#else
/* user mode free */
/* free dma memory block and current dma mem descriptor */
free(dma_firstp->mem.bi_kaddr);
free(dma_firstp);
#endif
/* advance to next dma memory descriptor */
dma_firstp = dma_next;
}
TNF_PROBE_0_DEBUG(hci1394_delete_dma_desc_mem_exit,
HCI1394_TNF_HAL_STACK_ISOCH, "");
}
/*
* hci1394_delete_xfer_ctl()
* Iterate thru linked list of xfer_ctl structs, deleting allocated memory.
*/
void
hci1394_delete_xfer_ctl(hci1394_xfer_ctl_t *xcsp)
{
hci1394_xfer_ctl_t *delp;
TNF_PROBE_0_DEBUG(hci1394_delete_xfer_ctl_enter,
HCI1394_TNF_HAL_STACK_ISOCH, "");
while ((delp = xcsp) != NULL) {
/* advance ptr to next xfer_ctl struct */
xcsp = xcsp->ctl_nextp;
/*
* delete current xfer_ctl struct and included
* xfer_ctl_dma structs
*/
#ifdef _KERNEL
kmem_free(delp,
sizeof (hci1394_xfer_ctl_t) +
sizeof (hci1394_xfer_ctl_dma_t) * (delp->cnt - 1));
#else
free(delp);
#endif
}
TNF_PROBE_0_DEBUG(hci1394_delete_xfer_ctl_exit,
HCI1394_TNF_HAL_STACK_ISOCH, "");
}