/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* av1394 isochronous receive module
*/
/* configuration routines */
static void av1394_ir_cleanup(av1394_ic_t *, int);
static int av1394_ir_build_ixl(av1394_ic_t *);
static void av1394_ir_ixl_label_init(av1394_ir_ixl_data_t *,
int);
int);
static void av1394_ir_destroy_ixl(av1394_ic_t *);
static int av1394_ir_alloc_isoch_dma(av1394_ic_t *);
static void av1394_ir_free_isoch_dma(av1394_ic_t *);
static void av1394_ir_dma_sync_frames(av1394_ic_t *, int, int);
/* callbacks */
static void av1394_ir_dma_stopped_cb(t1394_isoch_dma_handle_t,
/* data transfer routines */
static int av1394_ir_add_frames(av1394_ic_t *, int, int);
static int av1394_ir_wait_frames(av1394_ic_t *, int *, int *);
static void av1394_ir_zero_pkts(av1394_ic_t *, int, int);
/* value complementary to hi & lo watermarks (modulo number of frames) */
int av1394_ir_dump_ixl = 0;
int
{
int nframes;
if (nframes == 0) {
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
return (0);
}
void
{
}
int
{
int result;
int err;
int ret = 0;
return (0);
}
irp->ir_first_full = 0;
irp->ir_read_cnt = 0;
&idma_ctrlinfo, 0, &result);
if (err == DDI_SUCCESS) {
} else {
}
return (ret);
}
int
{
}
return (0);
}
int
{
int ret = 0;
/* check arguments */
return (EINVAL);
}
if (cnt > 0) {
/* add empty frames to the pool */
return (ret);
}
}
/* wait for new frames to arrive */
return (ret);
}
int
{
int ret = 0;
int empty_cnt;
/* wait for full frames, if necessary */
if (irp->ir_read_cnt == 0) {
irp->ir_read_off = 0;
if (ret != 0) {
return (ret);
}
}
/* copyout the data */
/* return freed frames to the pool */
if (empty_cnt > 0) {
}
}
return (ret);
}
/*
*
* --- configuration routines
*
*/
static void
{
switch (level) {
default:
/* FALLTHRU */
case 3:
/* FALLTHRU */
case 2:
/* FALLTHRU */
case 1:
/* FALLTHRU */
}
}
/*
* av1394_ir_build_ixl()
* Build an IXL chain to receive CIP data. The smallest instance of data
* that can be received is a packet, typically 512 bytes. Frames consist
* of a number of packets, typically 250-300. Packet size, frame size and
* number of frames allocated are set by a user process. The received data
* made available to the user process in full frames, hence there an IXL
* callback at the end of each frame. A sequence of IXL commands that
* receives one frame is further referred to as an IXL data block.
*
* During normal operation, frames are in a circular list and IXL chain
* does not change. When the user process does not keep up with the
* data flow and there are too few empty frames left, the jump following
* last empty frame is dynamically updated to point to NULL -- otherwise
* the first full frame would be overwritten. When IXL execution reaches
* the nulled jump, it just waits until the driver updates it again or
* stops the transfer. Once a user process frees up enough frames, the
* jump is restored and transfer continues. User process will be able to
* detect dropped packets using continuity conters embedded in the data.
*
* Because RECV_BUF buffer size is limited to AV1394_IXL_BUFSZ_MAX, and due
* to isoch pool segmentaion, the number of RECV_BUF commands per IXL data
* block depends on frame size. Also, to simplify calculations, we consider
* a sequence of RECV_BUF commands to consist of two parts: zero or more
* equal-sized RECV_BUF commands followed by one "tail" REC_BUF command,
* whose size may not be equal to others.
*
* Schematically the IXL chain looks like this:
*
* ...
* LABEL N;
* RECV_BUF(buf)
* ...
* RECV_BUF(tail)
* CALLBACK(frame done);
* JUMP_U(LABEL (N+1)%nframes or NULL);
* ...
*/
static int
{
int i; /* segment index */
int j;
/* allocate space for IXL data blocks */
sizeof (av1394_ir_ixl_data_t), KM_SLEEP);
/*
* We have a bunch of segments, and each is divided into cookies. We
* need to cover the segments with RECV_BUFs such that they
* - don't span cookies
* - don't span frames
* - are at most AV1394_IXL_BUFSZ_MAX
*
* The straightforward algorithm is to start from the beginning, find
* the next lowest frame or cookie boundary, and either make a buf for
* it if it is smaller than AV1394_IXL_BUFSZ_MAX, or make multiple
* bufs for it as with av1394_ic_ixl_seg_decomp(). And repeat.
*/
irp->ir_ixl_nbufs = 0;
int ci = 0;
for (;;) {
++irp->ir_ixl_nbufs;
} else {
/* count the tail buffer */
++irp->ir_ixl_nbufs;
}
break;
if (off == cookie_end) {
++ci;
}
}
}
sizeof (ixl1394_xfer_buf_t), KM_SLEEP);
fi = 0;
bi = 0;
int ci = 0;
for (;;) {
nextp = (ixl1394_command_t *)
else
nextp = (ixl1394_command_t *)
++bi;
} else {
for (j = 0; j < nbufs; ++j) {
++bi;
}
++bi;
}
break;
++fi;
}
if (off == cookie_end) {
++ci;
coff = 0;
}
}
++fi;
}
if (av1394_ir_dump_ixl) {
}
return (DDI_SUCCESS);
}
static void
{
}
static void
{
}
/*ARGSUSED*/
static void
{
}
static void
{
int next_idx;
}
static void
{
}
static int
{
int result;
int ret;
}
return (ret);
}
static void
{
}
static void
{
}
/*
*
* --- callbacks
*
*/
/*ARGSUSED*/
static void
{
/*
* signal the overflow condition early, so we get enough
* time to handle it before old data is overwritten
*/
}
}
}
/*
* received data overflow
*/
void
{
int idx;
int err;
int result;
/*
* in the circular IXL chain overflow means overwriting the least
* recent data. to avoid that, we suspend the transfer by NULL'ing
* the last IXL block until the user process frees up some frames.
*/
&update_info, 0, &result);
if (err == DDI_SUCCESS) {
} else {
}
}
/*
* restore from overflow condition
*/
static void
{
int err;
int result;
/*
* restore the jump command we NULL'ed in av1394_ir_overflow()
*/
if (err == DDI_SUCCESS) {
} else {
}
}
/*ARGSUSED*/
static void
{
}
/*
*
* --- data transfer routines
*
* av1394_ir_add_frames()
* Add empty frames to the pool.
*/
static int
{
/* can only add to the tail */
return (EINVAL);
}
/* turn full frames into empty ones */
/* if suspended due to overflow, check if iwe can resume */
}
return (0);
}
static int
{
int ret = 0;
break;
}
}
ret = 0;
}
return (ret);
}
/*
* copyout the data, adjust to data format and remove empty CIPs if possible
*/
static int
{
int len;
int ret = 0;
*empty_cnt = 0;
/* DBS -> block size */
}
/* copyout data blocks, skipping empty CIPs */
/*
* a quadlet following CIP header can't be zero
* unless in an empty packet
*/
if ((pkt_off == 0) &&
frame_resid -= pktsz;
continue;
}
}
if (len > frame_resid) {
len = frame_resid;
}
break;
}
} else {
frame_resid -= len;
}
}
if (frame_resid > 0) {
} else {
irp->ir_read_off = 0;
cnt--;
(*empty_cnt)++;
}
}
return (ret);
}
/*
* zero a quadlet in each packet so we can recognize empty CIPs
*/
static void
{
int i;
for (i = cnt; i > 0; i--) {
do {
}
}