/* * 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 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 #endif #include "iec61883.h" #include "iec61883-private.h" #include "cooked.h" #include #include #include #include #include #include #include #include #include /* * 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; }