#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/modctl.h>
#include <sys/stat.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/kmem.h>
#include <sys/note.h>
#include <sys/1394/adapters/hci1394.h>
static int hci1394_q_reserve(hci1394_q_buf_t *qbuf, uint_t size,
uint32_t *io_addr);
static void hci1394_q_unreserve(hci1394_q_buf_t *qbuf);
static void hci1394_q_buf_setup(hci1394_q_buf_t *qbuf);
static void hci1394_q_reset(hci1394_q_handle_t q_handle);
static void hci1394_q_next_buf(hci1394_q_buf_t *qbuf);
static void hci1394_q_at_write_OLI(hci1394_q_handle_t q_handle,
hci1394_q_buf_t *qbuf, hci1394_q_cmd_t *cmd, hci1394_basic_pkt_t *hdr,
uint_t hdrsize);
static void hci1394_q_at_write_OMI(hci1394_q_handle_t q_handle,
hci1394_q_buf_t *qbuf, hci1394_q_cmd_t *cmd, hci1394_basic_pkt_t *hdr,
uint_t hdrsize);
static void hci1394_q_at_write_OL(hci1394_q_handle_t q_handle,
hci1394_q_buf_t *qbuf, hci1394_q_cmd_t *cmd, uint32_t io_addr,
uint_t datasize);
static void hci1394_q_at_rep_put8(hci1394_q_buf_t *qbuf, hci1394_q_cmd_t *cmd,
uint8_t *data, uint_t datasize);
static void hci1394_q_at_copy_from_mblk(hci1394_q_buf_t *qbuf,
hci1394_q_cmd_t *cmd, h1394_mblk_t *mblk);
static void hci1394_q_ar_write_IM(hci1394_q_handle_t q_handle,
hci1394_q_buf_t *qbuf, uint32_t io_addr, uint_t datasize);
_NOTE(SCHEME_PROTECTS_DATA("unique", msgb))
int
hci1394_q_init(hci1394_drvinfo_t *drvinfo,
hci1394_ohci_handle_t ohci_handle, hci1394_q_info_t *qinfo,
hci1394_q_handle_t *q_handle)
{
hci1394_q_buf_t *desc;
hci1394_q_buf_t *data;
hci1394_buf_parms_t parms;
hci1394_q_t *q;
int status;
int index;
ASSERT(drvinfo != NULL);
ASSERT(qinfo != NULL);
ASSERT(q_handle != NULL);
q = kmem_alloc(sizeof (hci1394_q_t), KM_SLEEP);
q->q_drvinfo = drvinfo;
q->q_info = *qinfo;
q->q_ohci = ohci_handle;
mutex_init(&q->q_mutex, NULL, MUTEX_DRIVER, drvinfo->di_iblock_cookie);
desc = &q->q_desc;
data = &q->q_data;
parms.bp_length = qinfo->qi_desc_size;
parms.bp_max_cookies = 1;
parms.bp_alignment = 16;
status = hci1394_buf_alloc(drvinfo, &parms, &desc->qb_buf,
&desc->qb_buf_handle);
if (status != DDI_SUCCESS) {
mutex_destroy(&q->q_mutex);
kmem_free(q, sizeof (hci1394_q_t));
*q_handle = NULL;
return (DDI_FAILURE);
}
desc->qb_cookie[0] = desc->qb_buf.bi_cookie;
for (index = 1; index < desc->qb_buf.bi_cookie_count; index++) {
ddi_dma_nextcookie(desc->qb_buf.bi_dma_handle,
&desc->qb_buf.bi_cookie);
desc->qb_cookie[index] = desc->qb_buf.bi_cookie;
}
parms.bp_length = qinfo->qi_data_size;
parms.bp_max_cookies = 1;
parms.bp_alignment = 16;
status = hci1394_buf_alloc(drvinfo, &parms, &data->qb_buf,
&data->qb_buf_handle);
if (status != DDI_SUCCESS) {
hci1394_buf_free(&desc->qb_buf_handle);
mutex_destroy(&q->q_mutex);
kmem_free(q, sizeof (hci1394_q_t));
*q_handle = NULL;
return (DDI_FAILURE);
}
if ((qinfo->qi_mode == HCI1394_ARQ) &&
(data->qb_buf.bi_cookie_count == 1)) {
data->qb_buf.bi_cookie_count = 2;
data->qb_cookie[0] = data->qb_buf.bi_cookie;
data->qb_cookie[0].dmac_size /= 2;
data->qb_cookie[1] = data->qb_cookie[0];
data->qb_cookie[1].dmac_laddress =
data->qb_cookie[0].dmac_laddress +
data->qb_cookie[0].dmac_size;
data->qb_cookie[1].dmac_address =
data->qb_cookie[0].dmac_address +
data->qb_cookie[0].dmac_size;
} else {
data->qb_cookie[0] = data->qb_buf.bi_cookie;
for (index = 1; index < data->qb_buf.bi_cookie_count; index++) {
ddi_dma_nextcookie(data->qb_buf.bi_dma_handle,
&data->qb_buf.bi_cookie);
data->qb_cookie[index] = data->qb_buf.bi_cookie;
}
}
desc->qb_ptrs.qp_top = desc->qb_buf.bi_kaddr;
desc->qb_ptrs.qp_bottom = desc->qb_buf.bi_kaddr +
desc->qb_buf.bi_real_length - 1;
data->qb_ptrs.qp_top = data->qb_buf.bi_kaddr;
data->qb_ptrs.qp_bottom = data->qb_buf.bi_kaddr +
data->qb_buf.bi_real_length - 1;
hci1394_q_reset(q);
if (qinfo->qi_mode == HCI1394_ATQ) {
hci1394_tlist_init(drvinfo, NULL, &q->q_queued_list);
}
*q_handle = q;
return (DDI_SUCCESS);
}
void
hci1394_q_fini(hci1394_q_handle_t *q_handle)
{
hci1394_q_t *q;
ASSERT(q_handle != NULL);
q = *q_handle;
if (q->q_info.qi_mode == HCI1394_ATQ) {
hci1394_tlist_fini(&q->q_queued_list);
}
mutex_destroy(&q->q_mutex);
hci1394_buf_free(&q->q_desc.qb_buf_handle);
hci1394_buf_free(&q->q_data.qb_buf_handle);
kmem_free(q, sizeof (hci1394_q_t));
*q_handle = NULL;
}
static void
hci1394_q_buf_setup(hci1394_q_buf_t *qbuf)
{
ASSERT(qbuf != NULL);
qbuf->qb_ptrs.qp_current_buf = 0;
qbuf->qb_ptrs.qp_begin = qbuf->qb_ptrs.qp_top;
qbuf->qb_ptrs.qp_end = qbuf->qb_ptrs.qp_begin +
qbuf->qb_cookie[qbuf->qb_ptrs.qp_current_buf].dmac_size - 1;
qbuf->qb_ptrs.qp_current = qbuf->qb_ptrs.qp_begin;
qbuf->qb_ptrs.qp_offset = 0;
qbuf->qb_ptrs.qp_free_buf = qbuf->qb_buf.bi_cookie_count - 1;
qbuf->qb_ptrs.qp_free = qbuf->qb_ptrs.qp_bottom;
qbuf->qb_ptrs.qp_resv_size = 0;
}
static void
hci1394_q_reset(hci1394_q_handle_t q_handle)
{
hci1394_q_buf_t *desc;
hci1394_q_buf_t *data;
int index;
ASSERT(q_handle != NULL);
mutex_enter(&q_handle->q_mutex);
desc = &q_handle->q_desc;
data = &q_handle->q_data;
hci1394_q_buf_setup(desc);
hci1394_q_buf_setup(data);
q_handle->q_dma_running = B_FALSE;
q_handle->q_block_cnt = 0;
q_handle->q_previous = NULL;
if (q_handle->q_info.qi_mode == HCI1394_ARQ) {
q_handle->q_head = desc->qb_ptrs.qp_top;
for (index = 0; index < data->qb_buf.bi_cookie_count; index++) {
hci1394_q_ar_write_IM(q_handle, desc,
data->qb_cookie[index].dmac_address,
data->qb_cookie[index].dmac_size);
}
q_handle->q_space_left = data->qb_cookie[0].dmac_size;
}
mutex_exit(&q_handle->q_mutex);
}
void
hci1394_q_resume(hci1394_q_handle_t q_handle)
{
ASSERT(q_handle != NULL);
hci1394_q_reset(q_handle);
}
void
hci1394_q_stop(hci1394_q_handle_t q_handle)
{
ASSERT(q_handle != NULL);
mutex_enter(&q_handle->q_mutex);
q_handle->q_dma_running = B_FALSE;
mutex_exit(&q_handle->q_mutex);
}
static int
hci1394_q_reserve(hci1394_q_buf_t *qbuf, uint_t size, uint32_t *io_addr)
{
uint_t aligned_size;
ASSERT(qbuf != NULL);
qbuf->qb_backup_ptrs = qbuf->qb_ptrs;
aligned_size = HCI1394_ALIGN_QUAD(size);
if ((qbuf->qb_ptrs.qp_current_buf == qbuf->qb_ptrs.qp_free_buf) &&
(qbuf->qb_ptrs.qp_free >= qbuf->qb_ptrs.qp_current)) {
if ((qbuf->qb_ptrs.qp_current + aligned_size) <=
qbuf->qb_ptrs.qp_free) {
qbuf->qb_ptrs.qp_resv_size = aligned_size;
*io_addr = (uint32_t)(qbuf->qb_cookie[
qbuf->qb_ptrs.qp_current_buf].dmac_address +
qbuf->qb_ptrs.qp_offset);
} else {
qbuf->qb_ptrs.qp_resv_size = 0;
return (DDI_FAILURE);
}
} else if ((qbuf->qb_ptrs.qp_current + aligned_size) >
qbuf->qb_ptrs.qp_end) {
hci1394_q_next_buf(qbuf);
if (qbuf->qb_ptrs.qp_current_buf == qbuf->qb_ptrs.qp_free_buf) {
if ((qbuf->qb_ptrs.qp_current + aligned_size) >
qbuf->qb_ptrs.qp_free) {
qbuf->qb_ptrs.qp_resv_size = 0;
return (DDI_FAILURE);
} else {
qbuf->qb_ptrs.qp_resv_size = aligned_size;
*io_addr = (uint32_t)(qbuf->qb_cookie[
qbuf->qb_ptrs.qp_current_buf].dmac_address +
qbuf->qb_ptrs.qp_offset);
}
} else {
qbuf->qb_ptrs.qp_resv_size = aligned_size;
*io_addr = (uint32_t)(qbuf->qb_cookie[
qbuf->qb_ptrs.qp_current_buf].dmac_address +
qbuf->qb_ptrs.qp_offset);
}
} else {
qbuf->qb_ptrs.qp_resv_size = aligned_size;
*io_addr = (uint32_t)(qbuf->qb_cookie[
qbuf->qb_ptrs.qp_current_buf].dmac_address +
qbuf->qb_ptrs.qp_offset);
}
return (DDI_SUCCESS);
}
static void
hci1394_q_unreserve(hci1394_q_buf_t *qbuf)
{
ASSERT(qbuf != NULL);
qbuf->qb_ptrs = qbuf->qb_backup_ptrs;
}
void
hci1394_q_next_buf(hci1394_q_buf_t *qbuf)
{
ASSERT(qbuf != NULL);
qbuf->qb_ptrs.qp_current_buf++;
if (qbuf->qb_ptrs.qp_current_buf >= qbuf->qb_buf.bi_cookie_count) {
qbuf->qb_ptrs.qp_current_buf = 0;
}
qbuf->qb_ptrs.qp_begin = qbuf->qb_ptrs.qp_end + 1;
if (qbuf->qb_ptrs.qp_begin > qbuf->qb_ptrs.qp_bottom) {
qbuf->qb_ptrs.qp_begin = qbuf->qb_ptrs.qp_top;
}
qbuf->qb_ptrs.qp_end = qbuf->qb_ptrs.qp_begin +
qbuf->qb_cookie[qbuf->qb_ptrs.qp_current_buf].dmac_size - 1;
qbuf->qb_ptrs.qp_current = qbuf->qb_ptrs.qp_begin;
qbuf->qb_ptrs.qp_offset = 0;
}
int
hci1394_q_at(hci1394_q_handle_t q_handle, hci1394_q_cmd_t *cmd,
hci1394_basic_pkt_t *hdr, uint_t hdrsize, int *result)
{
int status;
uint32_t ioaddr;
ASSERT(q_handle != NULL);
ASSERT(cmd != NULL);
ASSERT(hdr != NULL);
mutex_enter(&q_handle->q_mutex);
if ((hci1394_state(q_handle->q_drvinfo) != HCI1394_NORMAL) ||
(hci1394_ohci_current_busgen(q_handle->q_ohci) !=
cmd->qc_generation)) {
*result = H1394_STATUS_INVALID_BUSGEN;
mutex_exit(&q_handle->q_mutex);
return (DDI_FAILURE);
}
cmd->qc_node.tln_addr = cmd;
q_handle->q_block_cnt = 0;
status = hci1394_q_reserve(&q_handle->q_desc,
sizeof (hci1394_desc_imm_t), &ioaddr);
if (status != DDI_SUCCESS) {
*result = H1394_STATUS_NOMORE_SPACE;
mutex_exit(&q_handle->q_mutex);
return (DDI_FAILURE);
}
hci1394_q_at_write_OLI(q_handle, &q_handle->q_desc, cmd, hdr, hdrsize);
hci1394_tlist_add(q_handle->q_queued_list, &cmd->qc_node);
mutex_exit(&q_handle->q_mutex);
return (DDI_SUCCESS);
}
int
hci1394_q_at_with_data(hci1394_q_handle_t q_handle, hci1394_q_cmd_t *cmd,
hci1394_basic_pkt_t *hdr, uint_t hdrsize, uint8_t *data, uint_t datasize,
int *result)
{
uint32_t desc_ioaddr;
uint32_t data_ioaddr;
int status;
ASSERT(q_handle != NULL);
ASSERT(cmd != NULL);
ASSERT(hdr != NULL);
ASSERT(data != NULL);
mutex_enter(&q_handle->q_mutex);
if ((hci1394_state(q_handle->q_drvinfo) != HCI1394_NORMAL) ||
(hci1394_ohci_current_busgen(q_handle->q_ohci) !=
cmd->qc_generation)) {
*result = H1394_STATUS_INVALID_BUSGEN;
mutex_exit(&q_handle->q_mutex);
return (DDI_FAILURE);
}
cmd->qc_node.tln_addr = cmd;
q_handle->q_block_cnt = 0;
status = hci1394_q_reserve(&q_handle->q_desc,
(sizeof (hci1394_desc_imm_t) + sizeof (hci1394_desc_t)),
&desc_ioaddr);
if (status != DDI_SUCCESS) {
*result = H1394_STATUS_NOMORE_SPACE;
mutex_exit(&q_handle->q_mutex);
return (DDI_FAILURE);
}
status = hci1394_q_reserve(&q_handle->q_data, datasize, &data_ioaddr);
if (status != DDI_SUCCESS) {
*result = H1394_STATUS_NOMORE_SPACE;
hci1394_q_unreserve(&q_handle->q_desc);
mutex_exit(&q_handle->q_mutex);
return (DDI_FAILURE);
}
hci1394_q_at_rep_put8(&q_handle->q_data, cmd, data, datasize);
hci1394_q_at_write_OMI(q_handle, &q_handle->q_desc, cmd, hdr, hdrsize);
hci1394_q_at_write_OL(q_handle, &q_handle->q_desc, cmd, data_ioaddr,
datasize);
hci1394_tlist_add(q_handle->q_queued_list, &cmd->qc_node);
mutex_exit(&q_handle->q_mutex);
return (DDI_SUCCESS);
}
int
hci1394_q_at_with_mblk(hci1394_q_handle_t q_handle, hci1394_q_cmd_t *cmd,
hci1394_basic_pkt_t *hdr, uint_t hdrsize, h1394_mblk_t *mblk, int *result)
{
uint32_t desc_ioaddr;
uint32_t data_ioaddr;
int status;
ASSERT(q_handle != NULL);
ASSERT(cmd != NULL);
ASSERT(hdr != NULL);
ASSERT(mblk != NULL);
mutex_enter(&q_handle->q_mutex);
if ((hci1394_state(q_handle->q_drvinfo) != HCI1394_NORMAL) ||
(hci1394_ohci_current_busgen(q_handle->q_ohci) !=
cmd->qc_generation)) {
*result = H1394_STATUS_INVALID_BUSGEN;
mutex_exit(&q_handle->q_mutex);
return (DDI_FAILURE);
}
cmd->qc_node.tln_addr = cmd;
q_handle->q_block_cnt = 0;
status = hci1394_q_reserve(&q_handle->q_desc,
(sizeof (hci1394_desc_imm_t) + sizeof (hci1394_desc_t)),
&desc_ioaddr);
if (status != DDI_SUCCESS) {
*result = H1394_STATUS_NOMORE_SPACE;
mutex_exit(&q_handle->q_mutex);
return (DDI_FAILURE);
}
status = hci1394_q_reserve(&q_handle->q_data, mblk->length,
&data_ioaddr);
if (status != DDI_SUCCESS) {
*result = H1394_STATUS_NOMORE_SPACE;
hci1394_q_unreserve(&q_handle->q_desc);
mutex_exit(&q_handle->q_mutex);
return (DDI_FAILURE);
}
hci1394_q_at_copy_from_mblk(&q_handle->q_data, cmd, mblk);
hci1394_q_at_write_OMI(q_handle, &q_handle->q_desc, cmd, hdr, hdrsize);
hci1394_q_at_write_OL(q_handle, &q_handle->q_desc, cmd, data_ioaddr,
mblk->length);
hci1394_tlist_add(q_handle->q_queued_list, &cmd->qc_node);
mutex_exit(&q_handle->q_mutex);
return (DDI_SUCCESS);
}
void
hci1394_q_at_next(hci1394_q_handle_t q_handle, boolean_t flush_q,
hci1394_q_cmd_t **cmd)
{
hci1394_q_buf_t *desc;
hci1394_q_buf_t *data;
hci1394_tlist_node_t *node;
uint32_t cmd_status;
ASSERT(q_handle != NULL);
ASSERT(cmd != NULL);
mutex_enter(&q_handle->q_mutex);
desc = &q_handle->q_desc;
data = &q_handle->q_data;
(void) ddi_dma_sync(desc->qb_buf.bi_dma_handle, 0,
desc->qb_buf.bi_length, DDI_DMA_SYNC_FORKERNEL);
hci1394_tlist_peek(q_handle->q_queued_list, &node);
if (node == NULL) {
*cmd = NULL;
mutex_exit(&q_handle->q_mutex);
return;
}
*cmd = (hci1394_q_cmd_t *)node->tln_addr;
cmd_status = ddi_get32(desc->qb_buf.bi_handle, (*cmd)->qc_status_addr);
(*cmd)->qc_timestamp = cmd_status & DESC_ST_TIMESTAMP_MASK;
cmd_status = HCI1394_DESC_EVT_GET(cmd_status);
if (flush_q == B_FALSE) {
if (cmd_status == 0) {
*cmd = NULL;
mutex_exit(&q_handle->q_mutex);
return;
}
}
(void) hci1394_tlist_delete(q_handle->q_queued_list, node);
desc->qb_ptrs.qp_free_buf = (*cmd)->qc_descriptor_buf;
desc->qb_ptrs.qp_free = (*cmd)->qc_descriptor_end;
if ((*cmd)->qc_data_used == B_TRUE) {
data->qb_ptrs.qp_free_buf = (*cmd)->qc_data_buf;
data->qb_ptrs.qp_free = (*cmd)->qc_data_end;
}
(*cmd)->qc_status = cmd_status;
mutex_exit(&q_handle->q_mutex);
}
void
hci1394_q_at_write_OMI(hci1394_q_handle_t q_handle, hci1394_q_buf_t *qbuf,
hci1394_q_cmd_t *cmd, hci1394_basic_pkt_t *hdr, uint_t hdrsize)
{
hci1394_desc_imm_t *desc;
uint32_t data;
ASSERT(qbuf != NULL);
ASSERT(cmd != NULL);
ASSERT(hdr != NULL);
ASSERT(MUTEX_HELD(&q_handle->q_mutex));
ASSERT((hdrsize == 8) || (hdrsize == 16));
ASSERT(qbuf->qb_ptrs.qp_resv_size >= sizeof (hci1394_desc_imm_t));
qbuf->qb_ptrs.qp_offset = (uint32_t)(qbuf->qb_ptrs.qp_current -
qbuf->qb_ptrs.qp_begin);
desc = (hci1394_desc_imm_t *)qbuf->qb_ptrs.qp_current;
data = DESC_AT_OMI | (hdrsize & DESC_HDR_REQCOUNT_MASK);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->hdr, data);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->data_addr, 0);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->branch, 0);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->status, cmd->qc_timestamp);
ddi_rep_put32(qbuf->qb_buf.bi_handle, &hdr->q1, &desc->q1,
hdrsize >> 2, DDI_DEV_AUTOINCR);
q_handle->q_block_cnt += 2;
qbuf->qb_ptrs.qp_resv_size -= sizeof (hci1394_desc_imm_t);
qbuf->qb_ptrs.qp_current += sizeof (hci1394_desc_imm_t);
}
void
hci1394_q_at_write_OLI(hci1394_q_handle_t q_handle, hci1394_q_buf_t *qbuf,
hci1394_q_cmd_t *cmd, hci1394_basic_pkt_t *hdr, uint_t hdrsize)
{
hci1394_desc_imm_t *desc;
uint32_t data;
uint32_t command_ptr;
uint32_t tcode;
ASSERT(qbuf != NULL);
ASSERT(cmd != NULL);
ASSERT(hdr != NULL);
ASSERT(MUTEX_HELD(&q_handle->q_mutex));
ASSERT((hdrsize == 8) || (hdrsize == 12) || (hdrsize == 16));
ASSERT(qbuf->qb_ptrs.qp_resv_size >= sizeof (hci1394_desc_imm_t));
qbuf->qb_ptrs.qp_offset = (uint32_t)(qbuf->qb_ptrs.qp_current -
qbuf->qb_ptrs.qp_begin);
desc = (hci1394_desc_imm_t *)qbuf->qb_ptrs.qp_current;
data = DESC_AT_OLI | (hdrsize & DESC_HDR_REQCOUNT_MASK);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->hdr, data);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->data_addr, 0);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->branch, 0);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->status, cmd->qc_timestamp);
tcode = (hdr->q1 & DESC_PKT_TCODE_MASK) >> DESC_PKT_TCODE_SHIFT;
if ((tcode == IEEE1394_TCODE_WRITE_QUADLET) ||
(tcode == IEEE1394_TCODE_READ_QUADLET_RESP)) {
ddi_rep_put32(qbuf->qb_buf.bi_handle, &hdr->q1, &desc->q1, 3,
DDI_DEV_AUTOINCR);
ddi_rep_put8(qbuf->qb_buf.bi_handle, (uint8_t *)&hdr->q4,
(uint8_t *)&desc->q4, 4, DDI_DEV_AUTOINCR);
} else {
ddi_rep_put32(qbuf->qb_buf.bi_handle, &hdr->q1, &desc->q1,
hdrsize >> 2, DDI_DEV_AUTOINCR);
}
q_handle->q_block_cnt += 2;
(void) ddi_dma_sync(qbuf->qb_buf.bi_dma_handle, 0,
qbuf->qb_buf.bi_length, DDI_DMA_SYNC_FORDEV);
cmd->qc_status_addr = &desc->status;
command_ptr = (uint32_t)((qbuf->qb_cookie[qbuf->qb_ptrs.qp_current_buf
].dmac_address + qbuf->qb_ptrs.qp_offset) | (q_handle->q_block_cnt &
DESC_Z_MASK));
if (q_handle->q_previous != NULL) {
ddi_put32(qbuf->qb_buf.bi_handle, &q_handle->q_previous->branch,
command_ptr);
(void) ddi_dma_sync(qbuf->qb_buf.bi_dma_handle, 0,
qbuf->qb_buf.bi_length, DDI_DMA_SYNC_FORDEV);
}
q_handle->q_previous = (hci1394_desc_t *)desc;
qbuf->qb_ptrs.qp_current += sizeof (hci1394_desc_imm_t);
q_handle->q_block_cnt = 0;
qbuf->qb_ptrs.qp_resv_size = 0;
cmd->qc_descriptor_buf = qbuf->qb_ptrs.qp_current_buf;
cmd->qc_descriptor_end = qbuf->qb_ptrs.qp_current - 1;
if (q_handle->q_dma_running == B_FALSE) {
q_handle->q_info.qi_start(q_handle->q_info.qi_callback_arg,
command_ptr);
q_handle->q_dma_running = B_TRUE;
} else {
q_handle->q_info.qi_wake(q_handle->q_info.qi_callback_arg);
}
}
void
hci1394_q_at_write_OL(hci1394_q_handle_t q_handle, hci1394_q_buf_t *qbuf,
hci1394_q_cmd_t *cmd, uint32_t io_addr, uint_t size)
{
hci1394_desc_t *desc;
uint32_t data;
uint32_t command_ptr;
ASSERT(q_handle != NULL);
ASSERT(qbuf != NULL);
ASSERT(cmd != NULL);
ASSERT(MUTEX_HELD(&q_handle->q_mutex));
ASSERT(qbuf->qb_ptrs.qp_resv_size >= sizeof (hci1394_desc_t));
desc = (hci1394_desc_t *)qbuf->qb_ptrs.qp_current;
data = DESC_AT_OL | (size & DESC_HDR_REQCOUNT_MASK);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->hdr, data);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->data_addr, io_addr);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->branch, 0);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->status, 0);
q_handle->q_block_cnt++;
(void) ddi_dma_sync(qbuf->qb_buf.bi_dma_handle, 0,
qbuf->qb_buf.bi_length, DDI_DMA_SYNC_FORDEV);
cmd->qc_status_addr = &desc->status;
command_ptr = (uint32_t)((qbuf->qb_cookie[qbuf->qb_ptrs.qp_current_buf
].dmac_address + qbuf->qb_ptrs.qp_offset) | (q_handle->q_block_cnt &
DESC_Z_MASK));
if (q_handle->q_previous != NULL) {
ddi_put32(qbuf->qb_buf.bi_handle, &q_handle->q_previous->branch,
command_ptr);
(void) ddi_dma_sync(qbuf->qb_buf.bi_dma_handle, 0,
qbuf->qb_buf.bi_length, DDI_DMA_SYNC_FORDEV);
}
q_handle->q_previous = desc;
qbuf->qb_ptrs.qp_current += sizeof (hci1394_desc_t);
q_handle->q_block_cnt = 0;
qbuf->qb_ptrs.qp_resv_size = 0;
cmd->qc_descriptor_buf = qbuf->qb_ptrs.qp_current_buf;
cmd->qc_descriptor_end = qbuf->qb_ptrs.qp_current - 1;
if (q_handle->q_dma_running == B_FALSE) {
q_handle->q_info.qi_start(q_handle->q_info.qi_callback_arg,
command_ptr);
q_handle->q_dma_running = B_TRUE;
} else {
q_handle->q_info.qi_wake(q_handle->q_info.qi_callback_arg);
}
}
void
hci1394_q_at_rep_put8(hci1394_q_buf_t *qbuf, hci1394_q_cmd_t *cmd,
uint8_t *data, uint_t datasize)
{
ASSERT(qbuf != NULL);
ASSERT(cmd != NULL);
ASSERT(data != NULL);
ASSERT(qbuf->qb_ptrs.qp_resv_size >= datasize);
ddi_rep_put8(qbuf->qb_buf.bi_handle, data,
(uint8_t *)qbuf->qb_ptrs.qp_current, datasize, DDI_DEV_AUTOINCR);
qbuf->qb_ptrs.qp_current += datasize;
qbuf->qb_ptrs.qp_offset = (uint32_t)(qbuf->qb_ptrs.qp_current -
qbuf->qb_ptrs.qp_begin);
qbuf->qb_ptrs.qp_resv_size -= datasize;
cmd->qc_data_used = B_TRUE;
cmd->qc_data_buf = qbuf->qb_ptrs.qp_current_buf;
cmd->qc_data_end = qbuf->qb_ptrs.qp_current - 1;
(void) ddi_dma_sync(qbuf->qb_buf.bi_dma_handle, 0,
qbuf->qb_buf.bi_length, DDI_DMA_SYNC_FORDEV);
}
static void
hci1394_q_at_copy_from_mblk(hci1394_q_buf_t *qbuf, hci1394_q_cmd_t *cmd,
h1394_mblk_t *mblk)
{
uint_t bytes_left;
uint_t length;
ASSERT(qbuf != NULL);
ASSERT(cmd != NULL);
ASSERT(mblk != NULL);
mblk->next_offset = mblk->curr_offset;
mblk->next_mblk = mblk->curr_mblk;
bytes_left = mblk->length;
do {
if ((mblk->next_offset + bytes_left) <=
(mblk->next_mblk->b_wptr)) {
hci1394_q_at_rep_put8(qbuf, cmd,
(uint8_t *)mblk->next_offset, bytes_left);
mblk->next_offset += bytes_left;
bytes_left = 0;
if (mblk->next_offset >= mblk->next_mblk->b_wptr) {
mblk->next_mblk = mblk->next_mblk->b_cont;
if (mblk->next_mblk != NULL) {
mblk->next_offset =
mblk->next_mblk->b_rptr;
}
}
} else {
length = mblk->next_mblk->b_wptr - mblk->next_offset;
hci1394_q_at_rep_put8(qbuf, cmd,
(uint8_t *)mblk->next_offset, length);
bytes_left = bytes_left - length;
mblk->next_mblk = mblk->next_mblk->b_cont;
ASSERT(mblk->next_mblk != NULL);
mblk->next_offset = mblk->next_mblk->b_rptr;
}
} while (bytes_left > 0);
}
void
hci1394_q_ar_next(hci1394_q_handle_t q_handle, uint32_t **q_addr)
{
hci1394_desc_t *desc;
hci1394_q_buf_t *descb;
hci1394_q_buf_t *datab;
uint32_t residual_count;
ASSERT(q_handle != NULL);
ASSERT(q_addr != NULL);
descb = &q_handle->q_desc;
datab = &q_handle->q_data;
(void) ddi_dma_sync(descb->qb_buf.bi_dma_handle, 0,
descb->qb_buf.bi_length, DDI_DMA_SYNC_FORKERNEL);
desc = (hci1394_desc_t *)q_handle->q_head;
residual_count = ddi_get32(descb->qb_buf.bi_handle, &desc->status);
residual_count &= DESC_ST_RESCOUNT_MASK;
if (residual_count >= q_handle->q_space_left) {
*q_addr = NULL;
return;
}
(void) ddi_dma_sync(datab->qb_buf.bi_dma_handle, 0,
datab->qb_buf.bi_length, DDI_DMA_SYNC_FORKERNEL);
*q_addr = (uint32_t *)datab->qb_ptrs.qp_current;
}
void
hci1394_q_ar_free(hci1394_q_handle_t q_handle, uint_t size)
{
hci1394_q_buf_t *descb;
hci1394_q_buf_t *datab;
ASSERT(q_handle != NULL);
descb = &q_handle->q_desc;
datab = &q_handle->q_data;
if ((datab->qb_ptrs.qp_current + size) > datab->qb_ptrs.qp_end) {
hci1394_q_ar_write_IM(q_handle, descb,
datab->qb_cookie[datab->qb_ptrs.qp_current_buf
].dmac_address,
datab->qb_cookie[datab->qb_ptrs.qp_current_buf].dmac_size);
hci1394_q_next_buf(datab);
size -= q_handle->q_space_left;
datab->qb_ptrs.qp_current += size;
q_handle->q_space_left =
datab->qb_cookie[datab->qb_ptrs.qp_current_buf].dmac_size -
size;
q_handle->q_head += sizeof (hci1394_desc_t);
if ((q_handle->q_head + sizeof (hci1394_desc_t)) >
(descb->qb_ptrs.qp_bottom + 1)) {
q_handle->q_head = descb->qb_ptrs.qp_top;
}
} else {
q_handle->q_space_left -= size;
datab->qb_ptrs.qp_current += size;
}
}
uint32_t
hci1394_q_ar_get32(hci1394_q_handle_t q_handle, uint32_t *addr)
{
hci1394_q_buf_t *data;
uintptr_t new_addr;
uint32_t data32;
ASSERT(q_handle != NULL);
ASSERT(addr != NULL);
data = &q_handle->q_data;
if ((uintptr_t)addr > (uintptr_t)data->qb_ptrs.qp_bottom) {
new_addr = (uintptr_t)data->qb_ptrs.qp_top + ((uintptr_t)addr -
((uintptr_t)data->qb_ptrs.qp_bottom + (uintptr_t)1));
data32 = ddi_get32(data->qb_buf.bi_handle,
(uint32_t *)new_addr);
} else {
data32 = ddi_get32(data->qb_buf.bi_handle, addr);
}
return (data32);
}
void
hci1394_q_ar_rep_get8(hci1394_q_handle_t q_handle, uint8_t *dest,
uint8_t *q_addr, uint_t size)
{
hci1394_q_buf_t *data;
uintptr_t new_addr;
uint_t new_size;
uintptr_t new_dest;
ASSERT(q_handle != NULL);
ASSERT(dest != NULL);
ASSERT(q_addr != NULL);
data = &q_handle->q_data;
if ((uintptr_t)q_addr > (uintptr_t)data->qb_ptrs.qp_bottom) {
new_addr = (uintptr_t)data->qb_ptrs.qp_top +
((uintptr_t)q_addr - ((uintptr_t)data->qb_ptrs.qp_bottom +
(uintptr_t)1));
ddi_rep_get8(data->qb_buf.bi_handle, dest, (uint8_t *)new_addr,
size, DDI_DEV_AUTOINCR);
} else if (((uintptr_t)q_addr + (uintptr_t)size) >
((uintptr_t)data->qb_ptrs.qp_bottom + (uintptr_t)1)) {
new_size = (uint_t)(((uintptr_t)data->qb_ptrs.qp_bottom +
(uintptr_t)1) - (uintptr_t)q_addr);
ddi_rep_get8(data->qb_buf.bi_handle, dest, q_addr, new_size,
DDI_DEV_AUTOINCR);
new_dest = (uintptr_t)dest + (uintptr_t)new_size;
new_size = size - new_size;
new_addr = (uintptr_t)data->qb_ptrs.qp_top;
ddi_rep_get8(data->qb_buf.bi_handle, (uint8_t *)new_dest,
(uint8_t *)new_addr, new_size, DDI_DEV_AUTOINCR);
} else {
ddi_rep_get8(data->qb_buf.bi_handle, dest, q_addr, size,
DDI_DEV_AUTOINCR);
}
}
void
hci1394_q_ar_copy_to_mblk(hci1394_q_handle_t q_handle, uint8_t *addr,
h1394_mblk_t *mblk)
{
uint8_t *new_addr;
uint_t bytes_left;
uint_t length;
ASSERT(q_handle != NULL);
ASSERT(addr != NULL);
ASSERT(mblk != NULL);
mblk->next_offset = mblk->curr_offset;
mblk->next_mblk = mblk->curr_mblk;
bytes_left = mblk->length;
new_addr = addr;
do {
if ((mblk->next_offset + bytes_left) <=
(mblk->next_mblk->b_datap->db_lim)) {
hci1394_q_ar_rep_get8(q_handle,
(uint8_t *)mblk->next_offset, new_addr, bytes_left);
mblk->next_offset += bytes_left;
mblk->next_mblk->b_wptr = mblk->next_offset;
bytes_left = 0;
if (mblk->next_offset >=
mblk->next_mblk->b_datap->db_lim) {
mblk->next_mblk = mblk->next_mblk->b_cont;
if (mblk->next_mblk != NULL) {
mblk->next_offset =
mblk->next_mblk->b_wptr;
}
}
} else {
length = mblk->next_mblk->b_datap->db_lim -
mblk->next_offset;
hci1394_q_ar_rep_get8(q_handle,
(uint8_t *)mblk->next_offset, new_addr, length);
mblk->next_mblk->b_wptr =
mblk->next_mblk->b_datap->db_lim;
bytes_left = bytes_left - length;
new_addr = (uint8_t *)((uintptr_t)new_addr +
(uintptr_t)length);
mblk->next_mblk = mblk->next_mblk->b_cont;
ASSERT(mblk->next_mblk != NULL);
mblk->next_offset = mblk->next_mblk->b_wptr;
}
} while (bytes_left > 0);
}
void
hci1394_q_ar_write_IM(hci1394_q_handle_t q_handle, hci1394_q_buf_t *qbuf,
uint32_t io_addr, uint_t datasize)
{
hci1394_desc_t *desc;
uint32_t data;
uint32_t command_ptr;
ASSERT(q_handle != NULL);
ASSERT(qbuf != NULL);
if ((qbuf->qb_ptrs.qp_current + sizeof (hci1394_desc_t)) >
(qbuf->qb_ptrs.qp_bottom + 1)) {
hci1394_q_next_buf(qbuf);
} else {
qbuf->qb_ptrs.qp_offset = (uint32_t)(qbuf->qb_ptrs.qp_current -
qbuf->qb_ptrs.qp_begin);
}
desc = (hci1394_desc_t *)qbuf->qb_ptrs.qp_current;
data = DESC_AR_IM | (datasize & DESC_HDR_REQCOUNT_MASK);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->hdr, data);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->data_addr, io_addr);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->branch, 0);
ddi_put32(qbuf->qb_buf.bi_handle, &desc->status, datasize &
DESC_ST_RESCOUNT_MASK);
(void) ddi_dma_sync(qbuf->qb_buf.bi_dma_handle, 0,
qbuf->qb_buf.bi_length, DDI_DMA_SYNC_FORDEV);
command_ptr = (uint32_t)((qbuf->qb_cookie[qbuf->qb_ptrs.qp_current_buf
].dmac_address + qbuf->qb_ptrs.qp_offset) | 1);
if (q_handle->q_previous != NULL) {
ddi_put32(qbuf->qb_buf.bi_handle,
&q_handle->q_previous->branch, command_ptr);
(void) ddi_dma_sync(qbuf->qb_buf.bi_dma_handle, 0,
qbuf->qb_buf.bi_length, DDI_DMA_SYNC_FORDEV);
}
q_handle->q_previous = desc;
qbuf->qb_ptrs.qp_current += sizeof (hci1394_desc_t);
if (q_handle->q_dma_running == B_FALSE) {
q_handle->q_info.qi_start(q_handle->q_info.qi_callback_arg,
command_ptr);
q_handle->q_dma_running = B_TRUE;
} else {
q_handle->q_info.qi_wake(q_handle->q_info.qi_callback_arg);
}
}