blob: 734e32221b752225b39a2b819379bb413db3bf29 [file] [log] [blame]
/*
* libiec61883 - Linux IEEE 1394 streaming media library.
* Copyright (C) 2004 Kristian Hogsberg, Dan Dennedy, and Dan Maas.
* This file written by Dan Dennedy.
*
* Bits of raw1394 ARM handling borrowed from
* Christian Toegel's <christian.toegel@gmx.at> demos.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "iec61883.h"
#include "iec61883-private.h"
#include "cooked.h"
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <netinet/in.h>
#include <libraw1394/csr.h>
/*
* Plug register access functions
*
* Please see the convenience macros defined in iec61883.h.
*/
int
iec61883_plug_get(raw1394handle_t h, nodeid_t n, nodeaddr_t a, quadlet_t *value)
{
quadlet_t temp; /* register value */
int result;
result = iec61883_cooked_read( h, n, CSR_REGISTER_BASE + a, sizeof(quadlet_t), &temp);
if (result >= 0)
*value = ntohl(temp); /* endian conversion */
return result;
}
int
iec61883_plug_set(raw1394handle_t h, nodeid_t n, nodeaddr_t a, quadlet_t value)
{
quadlet_t compare, swap, new;
int result;
/* get the current register value for comparison */
result = iec61883_plug_get( h, n, a, &compare );
if (result >= 0)
{
/* convert endian */
compare = htonl(compare);
swap = htonl(value);
result = raw1394_lock( h, n, CSR_REGISTER_BASE + a, EXTCODE_COMPARE_SWAP, swap, compare, &new);
if (new != compare)
result = -EAGAIN;
}
return result;
}
/*
* Local host plugs implementation
*
* This requires the address range mapping feature of libraw1394 1.0.
*/
/* ARM context identifiers */
static char g_arm_callback_context_out[] = "libiec61883 output context";
static char g_arm_callback_context_in[] = "libiec61883 input context";
static struct raw1394_arm_reqhandle g_arm_reqhandle_out;
static struct raw1394_arm_reqhandle g_arm_reqhandle_in;
/* register space */
/* Please note that this is host global. Each port (host adapter)
does not get its own register space. Also, this is only intended
for use by a single application. It appears that when multiple processes
host these plugs, it still works; as long as the application uses the
plug access functions above, then it should work. */
static struct output_registers {
struct iec61883_oMPR mpr;
struct iec61883_oPCR pcr[IEC61883_PCR_MAX];
} g_data_out;
static struct input_registers {
struct iec61883_iMPR mpr;
struct iec61883_iPCR pcr[IEC61883_PCR_MAX];
} g_data_in;
/** Send an async packet in response to a register read.
*
* This function handles host to bus endian conversion.
*
* \param handle A raw1394 handle.
* \param arm_req A pointer to an arm_request struct from the ARM callback
* handler.
* \param a The CSR offset address of the register.
* \param data The base address of the register space to read.
* \return 0 for success, -1 on error.
*/
static int
do_arm_read(raw1394handle_t handle, struct raw1394_arm_request *arm_req,
nodeaddr_t a, quadlet_t *data)
{
quadlet_t *response;
int num=4, offset;
/* allocate response packet */
response = malloc(num * sizeof(quadlet_t));
if (!response)
FAIL("unable to allocate response packet");
memset(response, 0x00, num * sizeof(quadlet_t));
/* fill data of response */
response[0] =
((arm_req->source_nodeid & 0xFFFF) << 16) +
((arm_req->tlabel & 0x3F) << 10) +
(6 << 4); /* tcode = 6 */
response[1] = ((arm_req->destination_nodeid & 0xFFFF) << 16);
/* rcode = resp_complete implied */
DEBUG (" destination_offset=%d",
arm_req->destination_offset - CSR_REGISTER_BASE - a);
offset = (arm_req->destination_offset - CSR_REGISTER_BASE - a)/4;
response[3] = htonl(data[offset]);
DEBUG(" response: 0x%8.8X",response[0]);
DEBUG(" 0x%8.8X",response[1]);
DEBUG(" 0x%8.8X",response[2]);
DEBUG(" 0x%8.8X",response[3]);
/* send response */
raw1394_start_async_send(handle, 16 , 16, 0, response, 0);
free (response);
return 0;
}
/** Update a local register value, and send a response packet.
*
* This function performs a compare/swap lock operation only.
* This function handles host to bus endian conversion.
*
* \param handle A raw1394 handle.
* \param arm_req A pointer to an arm_request struct from the ARM callback
* handler.
* \param a The CSR offset address of the register.
* \param data The base address of the register space to update.
* \return 0 for success, -1 on error.
*/
static int
do_arm_lock(raw1394handle_t handle, struct raw1394_arm_request *arm_req,
nodeaddr_t a, quadlet_t *data)
{
quadlet_t *response = NULL;
int num, offset;
int rcode = RCODE_COMPLETE;
int requested_length = 4;
if (arm_req->extended_transaction_code == EXTCODE_COMPARE_SWAP)
{
quadlet_t arg_q, data_q, old_q, new_q;
/* allocate response packet */
num = 4 + requested_length;
response = malloc(num * sizeof(quadlet_t));
if (!response)
FAIL("unable to allocate response packet");
memset(response, 0x00, num * sizeof(quadlet_t));
/* load data */
offset = (arm_req->destination_offset - CSR_REGISTER_BASE - a)/4;
response[4] = htonl(data[offset]);
/* compare */
arg_q = *(quadlet_t *) (&arm_req->buffer[0]);
data_q = *(quadlet_t *) (&arm_req->buffer[4]);
old_q = *(quadlet_t *) (&response[4]);
new_q = (old_q == arg_q) ? data_q : old_q;
/* swap */
data[offset] = ntohl(new_q);
}
else
{
rcode = RCODE_TYPE_ERROR;
requested_length = 0;
}
/* fill data of response */
response[0] =
((arm_req->source_nodeid & 0xFFFF) << 16) +
((arm_req->tlabel & 0x3F) << 10) +
(0xB << 4); /* tcode = B */
response[1] =
((arm_req->destination_nodeid & 0xFFFF) << 16) +
((rcode & 0xF) << 12);
response[3] =
((requested_length & 0xFFFF) << 16) +
(arm_req->extended_transaction_code & 0xFF);
DEBUG(" response: 0x%8.8X",response[0]);
DEBUG(" 0x%8.8X",response[1]);
DEBUG(" 0x%8.8X",response[2]);
DEBUG(" 0x%8.8X",response[3]);
DEBUG(" 0x%8.8X",response[4]);
/* send response */
raw1394_start_async_send(handle, requested_length + 16, 16, 0, response, 0);
free (response);
return 0;
}
/* local plug ARM handler */
static int
iec61883_arm_callback (raw1394handle_t handle,
struct raw1394_arm_request_response *arm_req_resp,
unsigned int requested_length,
void *pcontext, byte_t request_type)
{
struct raw1394_arm_request *arm_req = arm_req_resp->request;
DEBUG( "request type=%d tcode=%d length=%d", request_type, arm_req->tcode, requested_length);
DEBUG( "context = %s", (char *) pcontext);
fflush(stdout);
if (pcontext == g_arm_callback_context_out && requested_length == 4)
{
if (request_type == RAW1394_ARM_READ && arm_req->tcode == 4)
{
do_arm_read( handle, arm_req, CSR_O_MPR, (quadlet_t *) &g_data_out );
}
else if (request_type == RAW1394_ARM_LOCK)
{
do_arm_lock( handle, arm_req, CSR_O_MPR, (quadlet_t *) &g_data_out );
}
else
{
/* error response */
}
}
else if (pcontext == g_arm_callback_context_in && requested_length == 4)
{
if (request_type == RAW1394_ARM_READ)
{
do_arm_read( handle, arm_req, CSR_I_MPR, (quadlet_t *) &g_data_in );
}
else if (request_type == RAW1394_ARM_LOCK)
{
do_arm_lock( handle, arm_req, CSR_I_MPR, (quadlet_t *) &g_data_in );
}
else
{
/* error response */
}
}
else
{
/* error response */
}
fflush(stdout);
return 0;
}
int
iec61883_plug_impr_init (raw1394handle_t h, unsigned int data_rate)
{
/* validate parameters */
if (data_rate >> 2 != 0)
errno = -EINVAL;
/* initialize data */
memset (&g_data_in, 0, sizeof (g_data_in));
g_data_in.mpr.data_rate = data_rate;
/* initialize host environment */
memset (&g_arm_reqhandle_in, 0, sizeof(g_arm_reqhandle_in));
g_arm_reqhandle_in.arm_callback = (arm_req_callback_t) iec61883_arm_callback;
g_arm_reqhandle_in.pcontext = g_arm_callback_context_in;
/* register callback */
return raw1394_arm_register( h, CSR_REGISTER_BASE + CSR_I_MPR, sizeof(g_data_in),
(byte_t *) &g_data_in, (unsigned long) &g_arm_reqhandle_in,
0, 0, ( RAW1394_ARM_READ | RAW1394_ARM_LOCK ) );
}
void
iec61883_plug_impr_clear (raw1394handle_t h)
{
g_data_in.mpr.n_plugs = 0;
}
int
iec61883_plug_impr_close (raw1394handle_t h)
{
g_data_in.mpr.n_plugs = 0;
return raw1394_arm_unregister(h, CSR_REGISTER_BASE + CSR_I_MPR);
}
int
iec61883_plug_ipcr_add (raw1394handle_t h, unsigned int online)
{
int i = g_data_in.mpr.n_plugs;
/* validate parameters */
if (g_arm_reqhandle_in.arm_callback == NULL)
return -EPERM;
if (i + 1 > IEC61883_PCR_MAX)
return -ENOSPC;
if (online >> 1 != 0)
return -EINVAL;
/* update data */
g_data_in.pcr[i].online = online;
g_data_in.mpr.n_plugs++;
/* return which plug is added */
return i;
}
int
iec61883_plug_ompr_init (raw1394handle_t h, unsigned int data_rate,
unsigned int bcast_channel)
{
/* validate parameters */
if (data_rate >> 2 != 0)
errno = -EINVAL;
if (bcast_channel >> 6 != 0)
errno = -EINVAL;
/* initialize data */
memset (&g_data_out, 0, sizeof (g_data_out));
g_data_out.mpr.data_rate = data_rate;
g_data_out.mpr.bcast_channel = bcast_channel;
/* initialize host environment */
memset (&g_arm_reqhandle_out, 0, sizeof(g_arm_reqhandle_out));
g_arm_reqhandle_out.arm_callback = (arm_req_callback_t) iec61883_arm_callback;
g_arm_reqhandle_out.pcontext = g_arm_callback_context_out;
/* register callback */
return raw1394_arm_register( h, CSR_REGISTER_BASE + CSR_O_MPR, sizeof(g_data_out),
(byte_t *) &g_data_out, (unsigned long) &g_arm_reqhandle_out,
0, 0, ( RAW1394_ARM_READ | RAW1394_ARM_LOCK ) );
}
void
iec61883_plug_ompr_clear (raw1394handle_t h)
{
g_data_out.mpr.n_plugs = 0;
}
int
iec61883_plug_ompr_close (raw1394handle_t h)
{
g_data_out.mpr.n_plugs = 0;
return raw1394_arm_unregister(h, CSR_REGISTER_BASE + CSR_O_MPR);
}
int
iec61883_plug_opcr_add (raw1394handle_t h, unsigned int online,
unsigned int overhead_id, unsigned int payload)
{
int i = g_data_out.mpr.n_plugs;
/* validate parameters */
if (g_arm_reqhandle_out.arm_callback == NULL)
return -EPERM;
if (i + 1 > IEC61883_PCR_MAX)
return -ENOSPC;
if (online >> 1 != 0)
return -EINVAL;
if (overhead_id >> 4 != 0)
return -EINVAL;
if (payload >> 10 != 0)
return -EINVAL;
/* update plug */
g_data_out.pcr[i].online = online;
g_data_out.pcr[i].overhead_id = overhead_id;
g_data_out.pcr[i].payload = payload;
/* increment the number of plugs available */
g_data_out.mpr.n_plugs++;
/* return which plug is added */
return i;
}