| /* |
| * 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; |
| } |