/* * libiec61883 - Linux IEEE 1394 streaming media library. * Copyright (C) 2004 Kristian Hogsberg, Dan Dennedy, and Dan Maas. * This file written by Dan Dennedy. * * 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 int iec61883_cmp_calc_bandwidth (raw1394handle_t handle, nodeid_t from, int plug, int speed) { struct iec61883_oMPR ompr; struct iec61883_oPCR opcr; int bwu = -1; // failure if (iec61883_get_oMPR (handle, from, &ompr) < 0) { WARN ("%s: Failed to get the oMPR plug for node %d.", __FUNCTION__, (int) from & 0x3f); } else if (ompr.n_plugs == 0) { WARN ("%s: The transmitting device (%d) does not have any output plugs.", __FUNCTION__, (int) from & 0x3f); } else if (plug < ompr.n_plugs && plug < IEC61883_PCR_MAX) { if (iec61883_get_oPCRX (handle, from, &opcr, plug) < 0) { WARN ("%s: Failed to get the oPCR[%d] plug for node %d.", __FUNCTION__, plug, (int) from & 0x3f); } else { if (speed < 0 || speed > 2) speed = opcr.data_rate; if (opcr.overhead_id > 0) bwu = (opcr.overhead_id * 32) + (opcr.payload + 3) * (1 << (2 - speed)) * 4; else bwu = 512 + (opcr.payload + 3) * (1 << (2 - speed)) * 4; } } return bwu; } int iec61883_cmp_create_p2p (raw1394handle_t handle, nodeid_t output_node, int output_plug, nodeid_t input_node, int input_plug, unsigned int channel, unsigned int speed) { struct iec61883_oPCR opcr, save_opcr; struct iec61883_iPCR ipcr; DEBUG ("%s", __FUNCTION__); if (iec61883_get_oPCRX (handle, output_node, &opcr, output_plug) < 0) { WARN ("%s: Failed to get the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); return -1; } if (iec61883_get_iPCRX (handle, input_node, &ipcr, input_plug) < 0) { WARN ("%s: Failed to get the iPCR[%d] plug for node %d.", __FUNCTION__, input_plug, (int) input_node & 0x3f); return -1; } save_opcr = opcr; opcr.channel = ipcr.channel = channel; opcr.data_rate = speed; if (opcr.n_p2p_connections < 63) opcr.n_p2p_connections++; if (ipcr.n_p2p_connections < 63) ipcr.n_p2p_connections++; if (iec61883_set_oPCRX (handle, output_node, opcr, output_plug) < 0) { WARN ("%s: Failed to set the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); return -1; } if (iec61883_set_iPCRX (handle, input_node, ipcr, input_plug) < 0) { WARN ("%s: Failed to set the iPCR[%d] plug for node %d.", __FUNCTION__, input_plug, (int) input_node & 0x3f); // Undo changes on the oPCR if (iec61883_set_oPCRX (handle, output_node, save_opcr, output_plug) < 0) { WARN ("%s: Failed to undo changes on the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); } return -1; } return 0; } int iec61883_cmp_create_p2p_output (raw1394handle_t handle, nodeid_t output_node, int output_plug, unsigned int channel, unsigned int speed) { struct iec61883_oPCR opcr; DEBUG ("%s", __FUNCTION__); if (iec61883_get_oPCRX (handle, output_node, &opcr, output_plug) < 0) { WARN ("%s: Failed to get the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); return -1; } opcr.channel = channel; opcr.data_rate = speed; if (opcr.n_p2p_connections < 63) opcr.n_p2p_connections++; if (iec61883_set_oPCRX (handle, output_node, opcr, output_plug) < 0) { WARN ("%s: Failed to set the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); return -1; } return 0; } int iec61883_cmp_create_p2p_input (raw1394handle_t handle, nodeid_t input_node, int input_plug, unsigned int channel) { struct iec61883_iPCR ipcr; DEBUG ("%s", __FUNCTION__); if (iec61883_get_iPCRX (handle, input_node, &ipcr, input_plug) < 0) { WARN ("%s: Failed to get the iPCR[%d] plug for node %d.", __FUNCTION__, input_plug, (int) input_node & 0x3f); return -1; } ipcr.channel = channel; if (ipcr.n_p2p_connections < 63) ipcr.n_p2p_connections++; if (iec61883_set_iPCRX (handle, input_node, ipcr, input_plug) < 0) { WARN ("%s: Failed to set the iPCR[%d] plug for node %d.", __FUNCTION__, input_plug, (int) input_node & 0x3f); return -1; } return 0; } int iec61883_cmp_create_bcast (raw1394handle_t handle, nodeid_t output_node, int output_plug, nodeid_t input_node, int input_plug, unsigned int channel, unsigned int speed) { struct iec61883_oPCR opcr, save_opcr; struct iec61883_iPCR ipcr; DEBUG ("%s", __FUNCTION__); if (iec61883_get_oPCRX (handle, output_node, &opcr, output_plug) < 0) { WARN ("%s: Failed to get the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); return -1; } if (iec61883_get_iPCRX (handle, input_node, &ipcr, input_plug) < 0) { WARN ("%s: Failed to get the iPCR[%d] plug for node %d.", __FUNCTION__, input_plug, (int) input_node & 0x3f); return -1; } save_opcr = opcr; opcr.channel = ipcr.channel = channel; opcr.data_rate = speed; opcr.bcast_connection = 1; ipcr.bcast_connection = 1; if (iec61883_set_oPCRX (handle, output_node, opcr, output_plug) < 0) { WARN ("%s: Failed to set the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); return -1; } if (iec61883_set_iPCRX (handle, input_node, ipcr, input_plug) < 0) { WARN ("%s: Failed to set the iPCR[%d] plug for node %d.", __FUNCTION__, input_plug, (int) input_node & 0x3f); // Undo changes on the oPCR if (iec61883_set_oPCRX (handle, output_node, save_opcr, output_plug) < 0) { WARN ("%s: Failed to undo changes on the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); } return -1; } return 0; } int iec61883_cmp_create_bcast_output (raw1394handle_t handle, nodeid_t output_node, int output_plug, unsigned int channel, unsigned int speed) { struct iec61883_oPCR opcr; DEBUG ("%s", __FUNCTION__); if (iec61883_get_oPCRX (handle, output_node, &opcr, output_plug) < 0) { WARN ("%s: Failed to get the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); return -1; } opcr.channel = channel; opcr.data_rate = speed; opcr.bcast_connection = 1; if (iec61883_set_oPCRX (handle, output_node, opcr, output_plug) < 0) { WARN ("%s: Failed to set the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); return -1; } return 0; } int iec61883_cmp_create_bcast_input (raw1394handle_t handle, nodeid_t input_node, int input_plug, unsigned int channel) { struct iec61883_iPCR ipcr; DEBUG ("%s", __FUNCTION__); if (iec61883_get_iPCRX (handle, input_node, &ipcr, input_plug) < 0) { WARN ("%s: Failed to get the iPCR[%d] plug for node %d.", __FUNCTION__, input_plug, (int) input_node & 0x3f); return -1; } ipcr.channel = channel; ipcr.bcast_connection = 1; if (iec61883_set_iPCRX (handle, input_node, ipcr, input_plug) < 0) { WARN ("%s: Failed to set the iPCR[%d] plug for node %d.", __FUNCTION__, input_plug, (int) input_node & 0x3f); return -1; } return 0; } int iec61883_cmp_overlay_p2p (raw1394handle_t handle, nodeid_t output_node, int output_plug, nodeid_t input_node, int input_plug) { struct iec61883_oPCR opcr, save_opcr; struct iec61883_iPCR ipcr; DEBUG ("%s", __FUNCTION__); if (iec61883_get_oPCRX (handle, output_node, &opcr, output_plug) < 0) { WARN ("%s: Failed to get the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); return -1; } if (iec61883_get_iPCRX (handle, input_node, &ipcr, input_plug) < 0) { WARN ("%s: Failed to get the iPCR[%d] plug for node %d.", __FUNCTION__, input_plug, (int) input_node & 0x3f); return -1; } if (opcr.bcast_connection == 0) { save_opcr = opcr; if (opcr.n_p2p_connections < 63) opcr.n_p2p_connections++; } if (ipcr.bcast_connection == 0) if (ipcr.n_p2p_connections < 63) ipcr.n_p2p_connections++; if (iec61883_set_oPCRX (handle, output_node, opcr, output_plug) < 0) { WARN ("%s: Failed to set the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); return -1; } if (iec61883_set_iPCRX (handle, input_node, ipcr, input_plug) < 0) { WARN ("%s: Failed to set the iPCR[%d] plug for node %d.", __FUNCTION__, input_plug, (int) input_node & 0x3f); // Undo changes on the oPCR if (iec61883_set_oPCRX (handle, output_node, save_opcr, output_plug) < 0) { WARN ("%s: Failed to undo changes on the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); } return -1; } return 0; } int iec61883_cmp_overlay_p2p_output (raw1394handle_t handle, nodeid_t output_node, int output_plug) { struct iec61883_oPCR opcr; DEBUG ("%s", __FUNCTION__); if (iec61883_get_oPCRX (handle, output_node, &opcr, output_plug) < 0) { WARN ("%s: Failed to get the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); return -1; } if (opcr.bcast_connection == 0) { if (opcr.n_p2p_connections < 63) opcr.n_p2p_connections++; if (iec61883_set_oPCRX (handle, output_node, opcr, output_plug) < 0) { WARN ("%s: Failed to set the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); return -1; } } return 0; } int iec61883_cmp_overlay_p2p_input (raw1394handle_t handle, nodeid_t input_node, int input_plug) { struct iec61883_iPCR ipcr; DEBUG ("%s", __FUNCTION__); if (iec61883_get_iPCRX (handle, input_node, &ipcr, input_plug) < 0) { WARN ("%s: Failed to get the iPCR[%d] plug for node %d.", __FUNCTION__, input_plug, (int) input_node & 0x3f); return -1; } if (ipcr.bcast_connection == 0) { if (ipcr.n_p2p_connections < 63) ipcr.n_p2p_connections++; if (iec61883_set_iPCRX (handle, input_node, ipcr, input_plug) < 0) { WARN ("%s: Failed to set the iPCR[%d] plug for node %d.", __FUNCTION__, input_plug, (int) input_node & 0x3f); return -1; } } return 0; } int iec61883_cmp_overlay_bcast (raw1394handle_t handle, nodeid_t output_node, int output_plug, nodeid_t input_node, int input_plug) { struct iec61883_oPCR opcr, save_opcr; struct iec61883_iPCR ipcr; DEBUG ("%s", __FUNCTION__); if (iec61883_get_oPCRX (handle, output_node, &opcr, output_plug) < 0) { WARN ("%s: Failed to get the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); return -1; } if (iec61883_get_iPCRX (handle, input_node, &ipcr, input_plug) < 0) { WARN ("%s: Failed to get the iPCR[%d] plug for node %d.", __FUNCTION__, input_plug, (int) input_node & 0x3f); return -1; } save_opcr = opcr; // We still need this function because only one of the plugs might have the // bcast_connection set. opcr.bcast_connection = 1; ipcr.bcast_connection = 1; if (iec61883_set_oPCRX (handle, output_node, opcr, output_plug) < 0) { WARN ("%s: Failed to set the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); return -1; } if (iec61883_set_iPCRX (handle, input_node, ipcr, input_plug) < 0) { WARN ("%s: Failed to set the iPCR[%d] plug for node %d.", __FUNCTION__, input_plug, (int) input_node & 0x3f); // Undo changes on the oPCR if (iec61883_set_oPCRX (handle, output_node, save_opcr, output_plug) < 0) { WARN ("%s: Failed to undo changes on the oPCR[%d] plug for node %d.", __FUNCTION__, output_plug, (int) output_node & 0x3f); } return -1; } return 0; } static int allocate_channel (raw1394handle_t handle) { int c = -1; for (c = 0; c < 63; c++) if (raw1394_channel_modify (handle, c, RAW1394_MODIFY_ALLOC) == 0) break; DEBUG ("%s: %d", __FUNCTION__, c); return c; } /** * cmp_connect - establish, re-establish or overlay connection automatically * @handle: a libraw1394 handle * @output: node id of the transmitter * @oplug: the output plug to use. If -1, find the first online plug, and * upon return, contains the plug number used. * @input: node id of the receiver * @iplug: the input plug to use. If -1, find the first online plug, and * upon return, contains the plug number used. * @bandwidth: an input/output parameter. As an input this is a boolean that * indicates whether to perform bandwidth allocation. Supply 0 to skip bandwidth * allocation; any other value permits it. Upon return, actual bandwidth allocation * units allocated are returned, which you supply to the disconnect routine. * @channel: if channel is < 0, the function will establish a new connection. * oherwise, it will try to re-establish an existing connection. * * This is a high level function that attempts to be as smart as possible, but * it gives point-to-point connections higher priority over broadcast connections. * It can automatically handle situations where either @input and/or @output does * not implement plug control registers. However, if one node implements plug * registers it assumes the other node has some sort of manual channel selection * (i.e., through software or a control panel). * * Returns: * It returns the isochronous channel number selected or -1 if the function has * failed for any reason. **/ static int cmp_connect (raw1394handle_t handle, nodeid_t output, int *oplug, nodeid_t input, int *iplug, int *bandwidth, int channel) { struct iec61883_oMPR ompr; struct iec61883_iMPR impr; struct iec61883_oPCR opcr; struct iec61883_iPCR ipcr; int oplug_online = -1, iplug_online = -1; int skip_bandwidth = (*bandwidth == 0); int failure = 0; int new_connection = (channel < 0); DEBUG ("%s", __FUNCTION__); *bandwidth = 0; // Check for plugs on output if (iec61883_get_oMPR (handle, output, &ompr) < 0) ompr.n_plugs = 0; // Check for plugs on input if (iec61883_get_iMPR (handle, input, &impr) < 0) impr.n_plugs = 0; DEBUG ("output node %d #plugs=%d, input node %d #plugs=%d", output & 0x3f, ompr.n_plugs, input & 0x3f, impr.n_plugs); if (ompr.n_plugs > 0 && impr.n_plugs > 0) { // establish or overlay point-to-point // speed to use is lesser of output and input unsigned int speed = impr.data_rate < ompr.data_rate ? impr.data_rate : ompr.data_rate; // determine if output has plug available if (*oplug < 0) { for (*oplug = 0; *oplug < ompr.n_plugs; (*oplug)++) { if (iec61883_get_oPCRX (handle, output, &opcr, *oplug) == 0) { // get first online plug if (oplug_online == -1 && opcr.online) oplug_online = *oplug; if (opcr.online && opcr.n_p2p_connections == 0) break; } } } else if (iec61883_get_oPCRX (handle, output, &opcr, *oplug) < 0) FAIL ("Failed to get plug %d for output node", *oplug); // determine if input has plug available if (*iplug < 0) { for (*iplug = 0; *iplug < impr.n_plugs; (*iplug)++) { if (iec61883_get_iPCRX (handle, input, &ipcr, *iplug) == 0) { // get first online plug if (iplug_online == -1 && ipcr.online) iplug_online = *iplug; if (ipcr.online && ipcr.n_p2p_connections == 0) break; } } } else if (iec61883_get_iPCRX (handle, input, &ipcr, *iplug) < 0) FAIL ("Failed to get plug %d for input node", *iplug); if (*oplug < ompr.n_plugs && *iplug < impr.n_plugs) { if (opcr.bcast_connection == 1) { channel = opcr.channel; iec61883_cmp_overlay_bcast (handle, output, *oplug, input, *iplug); } else { // allocate bandwidth if (!skip_bandwidth) { *bandwidth = iec61883_cmp_calc_bandwidth (handle, output, *oplug, speed); if (*bandwidth < 1) { WARN ("Failed to calculate bandwidth."); failure = 1; } else if (raw1394_bandwidth_modify (handle, *bandwidth, RAW1394_MODIFY_ALLOC) < 0) { WARN ("Failed to allocate bandwidth."); failure = 1; } } if (!failure) { if (new_connection) { channel = allocate_channel (handle); } else { raw1394_channel_modify (handle, channel, RAW1394_MODIFY_ALLOC); } if (iec61883_cmp_create_p2p (handle, output, *oplug, input, *iplug, channel, speed) < 0) { // release channel and bandwidth failure = raw1394_channel_modify (handle, channel, RAW1394_MODIFY_FREE); if (!failure) raw1394_bandwidth_modify (handle, *bandwidth, RAW1394_MODIFY_FREE); channel = -1; } } } } else if (*iplug < impr.n_plugs && oplug_online > -1 ) { // get the channel from output - can not start another transmission // on an existing channel, but can receive from multiple nodes/plugs *oplug = oplug_online; if (iec61883_get_oPCRX (handle, output, &opcr, oplug_online) == 0) { channel = opcr.channel; if (opcr.bcast_connection == 1) { iec61883_cmp_overlay_bcast (handle, output, oplug_online, input, *iplug); } else { if (iec61883_cmp_create_p2p_input (handle, input, *iplug, channel) < 0) channel = -1; else if (iec61883_cmp_overlay_p2p_output (handle, output, oplug_online) < 0) channel = -1; } } } else if (oplug_online != -1 && iplug_online > -1) { // get channel from output *oplug = oplug_online; if (iec61883_get_oPCRX (handle, output, &opcr, oplug_online) == 0) { channel = opcr.channel; if (iec61883_cmp_overlay_p2p (handle, output, oplug_online, input, iplug_online) < 0) channel = -1; } } else { WARN ("All the plugs on both nodes are offline!"); *oplug = *iplug = -1; } } else if (ompr.n_plugs > 0) { // establish or overlay half point-to-point on output *iplug = -1; // determine if output has plug available if (*oplug < 0) { for (*oplug = 0; *oplug < ompr.n_plugs; (*oplug)++) { if (iec61883_get_oPCRX (handle, output, &opcr, *oplug) == 0) { // get first online plug if (oplug_online == -1 && opcr.online) oplug_online = *oplug; if (opcr.online && opcr.n_p2p_connections == 0) break; } } } else if (iec61883_get_oPCRX (handle, output, &opcr, *oplug) < 0) FAIL ("Failed to get plug %d for output node", *oplug); if (*oplug < ompr.n_plugs) { if (opcr.bcast_connection == 1) { channel = opcr.channel; } else { // establish // XXX: the input must provide manual channel selection or we should // do a broadcast. Example use case: DV device is output and local // node is input, but software allows channel select. Failure use // case: local node is output but input device has no channel selection! // Both use cases are actually quite common. Should we provide a // parameter to offer a hint in case operator knows something more // about the device than firewire interfaces on it suggest? // allocate bandwidth if (!skip_bandwidth) { *bandwidth = iec61883_cmp_calc_bandwidth (handle, output, *oplug, ompr.data_rate); if (*bandwidth < 1) { WARN ("Failed to calculate bandwidth."); failure = 1; } else if (raw1394_bandwidth_modify (handle, *bandwidth, RAW1394_MODIFY_ALLOC) < 0) { WARN ("Failed to allocate bandwidth."); failure = 1; } } if (!failure) { if( new_connection ) { channel = allocate_channel (handle); } else { raw1394_channel_modify (handle, channel, RAW1394_MODIFY_ALLOC); } if (iec61883_cmp_create_p2p_output (handle, output, *oplug, channel, ompr.data_rate) == 0) { DEBUG ("Established connection on channel %d.\n" "You may need to manually set the channel on the receiving node.", channel); } else { // release channel and bandwidth failure = raw1394_channel_modify (handle, channel, RAW1394_MODIFY_FREE); if (!failure) raw1394_bandwidth_modify (handle, *bandwidth, RAW1394_MODIFY_FREE); channel = -1; } } } } else if (oplug_online > -1) { // overlay // get channel from output *oplug = oplug_online; if (iec61883_get_oPCRX (handle, output, &opcr, oplug_online) == 0) { channel = opcr.channel; if (opcr.bcast_connection != 1) if (iec61883_cmp_overlay_p2p_output (handle, output, oplug_online) < 0) channel = -1; } DEBUG ("Overlayed connection on channel %d.\n" "You may need to manually set the channel on the receiving node.", channel); } else { WARN ("Transmission node has no plugs online!"); *oplug = -1; // failover to broadcast // allocate bandwidth based upon first out plug if (!skip_bandwidth) { *bandwidth = iec61883_cmp_calc_bandwidth (handle, output, 0, ompr.data_rate); if (*bandwidth < 1) { WARN ("Failed to calculate bandwidth."); failure = 1; } else if (raw1394_bandwidth_modify (handle, *bandwidth, RAW1394_MODIFY_ALLOC) < 0) { WARN ("Failed to allocate bandwidth."); failure = 1; } } if (!failure) { if (raw1394_channel_modify (handle, ompr.bcast_channel, RAW1394_MODIFY_ALLOC) == 0) channel = ompr.bcast_channel; } } } else if (impr.n_plugs > 0) { // establish or overlay half point-to-point on input *oplug = -1; // determine if input has plug available if (*iplug < 0) { for (*iplug = 0; *iplug < impr.n_plugs; (*iplug)++) { if (iec61883_get_iPCRX (handle, input, &ipcr, *iplug) == 0) { // get first online plug if (iplug_online == -1 && ipcr.online) iplug_online = *iplug; if (ipcr.online && ipcr.n_p2p_connections == 0) break; } } } else if (iec61883_get_iPCRX (handle, input, &ipcr, *iplug) < 0) FAIL ("Failed to get plug %d for input node", *iplug); if (*iplug < impr.n_plugs) { if (ipcr.bcast_connection == 1) { channel = ipcr.channel; } else { // establish // allocate bandwidth // cannot accurately allocate bandwidth with no output plug // use an output plug on the input device as a best guess if (!skip_bandwidth) { *bandwidth = iec61883_cmp_calc_bandwidth (handle, input, *iplug, -1); if (*bandwidth < 1) { WARN ("Failed to calculate bandwidth."); failure = 1; } else if (raw1394_bandwidth_modify (handle, *bandwidth, RAW1394_MODIFY_ALLOC) < 0) { WARN ("Failed to allocate bandwidth."); failure = 1; } } if (!failure) { if( new_connection ) { channel = allocate_channel (handle); } else { raw1394_channel_modify (handle, channel, RAW1394_MODIFY_ALLOC); } if (iec61883_cmp_create_p2p_input (handle, input, *iplug, channel) == 0) { DEBUG ("Established connection on channel %d.\n" "You may need to manually set the channel on the transmitting node.", channel); } else { // release channel and bandwidth failure = raw1394_channel_modify (handle, channel, RAW1394_MODIFY_FREE); if (!failure) raw1394_bandwidth_modify (handle, *bandwidth, RAW1394_MODIFY_FREE); channel = -1; } } } } else if (iplug_online > -1) { // overlay // get channel from input *iplug = iplug_online; if (iec61883_get_iPCRX (handle, input, &ipcr, iplug_online) == 0) { channel = ipcr.channel; if (ipcr.bcast_connection != 1) if (iec61883_cmp_overlay_p2p_input (handle, input, iplug_online) < 0) channel = -1; } DEBUG ("Overlayed connection on channel %d.\n" "You may need to manually set the channel on the transmitting node.", channel); } else { WARN ("Receiving node has no plugs online!"); // failover to broadcast // allocate bandwidth based upon first input plug *iplug = -1; if (!skip_bandwidth) { *bandwidth = iec61883_cmp_calc_bandwidth (handle, input, 0, impr.data_rate); if (*bandwidth < 1) { WARN ("Failed to calculate bandwidth."); failure = 1; } else if (raw1394_bandwidth_modify (handle, *bandwidth, RAW1394_MODIFY_ALLOC) < 0) { WARN ("Failed to allocate bandwidth."); failure = 1; } } if (!failure) { if (raw1394_channel_modify (handle, 63, RAW1394_MODIFY_ALLOC) == 0) channel = 63; } } } else { // no input or output plugs - failover broadcast on channel 63 // not enough information to calculate bandwidth *oplug = *iplug = -1; if (raw1394_channel_modify (handle, 63, RAW1394_MODIFY_ALLOC) == 0) channel = 63; if (channel == 63) WARN ("No plugs exist on either node; using default broadcast channel 63."); } return channel; } int iec61883_cmp_reconnect (raw1394handle_t handle, nodeid_t output, int *oplug, nodeid_t input, int *iplug, int *bandwidth, int channel) { /* Passing an existing channel means it is a reconnection. */ return cmp_connect (handle, output, oplug, input, iplug, bandwidth, channel); } int iec61883_cmp_connect (raw1394handle_t handle, nodeid_t output, int *oplug, nodeid_t input, int *iplug, int *bandwidth) { /* Passing "-1" as the channel means it is a new connection. */ return cmp_connect (handle, output, oplug, input, iplug, bandwidth, -1); } int iec61883_cmp_disconnect (raw1394handle_t handle, nodeid_t output, int oplug, nodeid_t input, int iplug, unsigned int channel, unsigned int bandwidth) { struct iec61883_oMPR ompr; struct iec61883_iMPR impr; struct iec61883_oPCR opcr; struct iec61883_iPCR ipcr; int result = 0; DEBUG ("%s: oplug %d iplug %d channel %u bw %u", __FUNCTION__, oplug, iplug, channel, bandwidth); // Check for plugs on output if (iec61883_get_oMPR (handle, output, &ompr) < 0) ompr.n_plugs = 0; // Check for plugs on input if (iec61883_get_iMPR (handle, input, &impr) < 0) impr.n_plugs = 0; if (ompr.n_plugs > 0 && impr.n_plugs > 0) { // establish or overlay point-to-point // determine if output has plug available if (oplug < 0) { for (oplug = 0; oplug < ompr.n_plugs; oplug++) { if (iec61883_get_oPCRX (handle, output, &opcr, oplug) == 0) { if (opcr.online && opcr.channel == channel) break; } } } else if (iec61883_get_oPCRX (handle, output, &opcr, oplug) < 0) FAIL ("Failed to get plug %d for output node", oplug); // determine if input has plug available if (iplug < 0) { for (iplug = 0; iplug < impr.n_plugs; iplug++) { if (iec61883_get_iPCRX (handle, input, &ipcr, iplug) == 0) { if (ipcr.online && ipcr.channel == channel) break; } } } else if (iec61883_get_iPCRX (handle, input, &ipcr, iplug) < 0) FAIL ("Failed to get plug %d for input node", iplug); if (oplug != ompr.n_plugs) { if (opcr.n_p2p_connections > 0) { opcr.n_p2p_connections--; if (opcr.n_p2p_connections == 0) opcr.channel = ompr.bcast_channel; result = iec61883_set_oPCRX (handle, output, opcr, oplug); if (result == 0) { if (opcr.n_p2p_connections == 0) { // release channel and bandwidth result = raw1394_channel_modify (handle, channel, RAW1394_MODIFY_FREE); if (result == 0) result = raw1394_bandwidth_modify (handle, bandwidth, RAW1394_MODIFY_FREE); } } } } if (iplug != impr.n_plugs) { if (ipcr.n_p2p_connections > 0) { ipcr.n_p2p_connections--; // receiver connection count does not affect iso resource management result = iec61883_set_iPCRX (handle, input, ipcr, iplug); } else if (ipcr.bcast_connection == 1) { ipcr.bcast_connection = 0; ipcr.channel = ompr.bcast_channel; result = iec61883_set_iPCRX (handle, input, ipcr, iplug); } } if (oplug == ompr.n_plugs && iplug == impr.n_plugs) result = -1; } else if (ompr.n_plugs > 0) { // establish or overlay half point-to-point on output // determine if output has plug available if (oplug < 0) { for (oplug = 0; oplug < ompr.n_plugs; oplug++) { if (iec61883_get_oPCRX (handle, output, &opcr, oplug) == 0) { if (opcr.online && opcr.channel == channel) break; } } } else if (iec61883_get_oPCRX (handle, output, &opcr, oplug) < 0) FAIL ("Failed to get plug %d for output node", oplug); if (oplug != ompr.n_plugs) { if (opcr.n_p2p_connections > 0) { opcr.n_p2p_connections--; if (opcr.n_p2p_connections == 0) opcr.channel = ompr.bcast_channel; result = iec61883_set_oPCRX (handle, output, opcr, oplug); if (result == 0 && opcr.n_p2p_connections == 0) { // release channel and bandwidth result = raw1394_channel_modify (handle, channel, RAW1394_MODIFY_FREE); if (result == 0) result = raw1394_bandwidth_modify (handle, bandwidth, RAW1394_MODIFY_FREE); } } } else { // release channel and bandwidth result = raw1394_channel_modify (handle, channel, RAW1394_MODIFY_FREE); if (result == 0) result = raw1394_bandwidth_modify (handle, bandwidth, RAW1394_MODIFY_FREE); } } else if (impr.n_plugs > 0) { // establish or overlay half point-to-point on input // determine if input has plug available if (iplug < 0) { for (iplug = 0; iplug < impr.n_plugs; iplug++) { if (iec61883_get_iPCRX (handle, input, &ipcr, iplug) == 0) { if (ipcr.online && ipcr.channel == channel) break; } } } else if (iec61883_get_iPCRX (handle, input, &ipcr, iplug) < 0) FAIL ("Failed to get plug %d for input node", iplug); if (iplug != impr.n_plugs) { if (ipcr.n_p2p_connections > 0) { ipcr.n_p2p_connections--; if (ipcr.n_p2p_connections == 0) ipcr.channel = 63; // Normally, changes on receiver connection count does not // affect iso resource management. However, in this special // half-way management mode, we need to in order to allow // multiple capture sessions. result = iec61883_set_iPCRX (handle, input, ipcr, iplug); if (result == 0 && ipcr.n_p2p_connections == 0) { // release channel and bandwidth result = raw1394_channel_modify (handle, channel, RAW1394_MODIFY_FREE); if (result == 0) result = raw1394_bandwidth_modify (handle, bandwidth, RAW1394_MODIFY_FREE); } } } else { // release channel and bandwidth result = raw1394_channel_modify (handle, channel, RAW1394_MODIFY_FREE); if (result == 0) result = raw1394_bandwidth_modify (handle, bandwidth, RAW1394_MODIFY_FREE); } } else { // no input or output plugs - failover broadcast on channel 63 // just release channel result = raw1394_channel_modify (handle, 63, RAW1394_MODIFY_FREE); } return result; } int iec61883_cmp_normalize_output (raw1394handle_t handle, nodeid_t node) { struct iec61883_oMPR ompr; struct iec61883_oPCR opcr; int oplug; int result = 0; DEBUG ("iec61883_cmp_normalize_output: node %d\n", (int) node & 0x3f); // Check for plugs on output result = iec61883_get_oMPR (handle, node, &ompr); if (result < 0) return result; // locate an ouput plug that has a connection for (oplug = 0; oplug < ompr.n_plugs; oplug++) { if (iec61883_get_oPCRX (handle, node, &opcr, oplug) == 0) { if (opcr.online && (opcr.n_p2p_connections > 0 || opcr.bcast_connection == 1)) { // Make sure the plug's channel is allocated with IRM quadlet_t buffer; nodeaddr_t addr = CSR_REGISTER_BASE; unsigned int c = opcr.channel; quadlet_t compare, swap = 0; quadlet_t new; if (c > 31 && c < 64) { addr += CSR_CHANNELS_AVAILABLE_LO; c -= 32; } else if (c < 64) addr += CSR_CHANNELS_AVAILABLE_HI; else FAIL ("Invalid channel"); c = 31 - c; result = iec61883_cooked_read (handle, raw1394_get_irm_id (handle), addr, sizeof (quadlet_t), &buffer); if (result < 0) FAIL ("Failed to get channels available."); buffer = ntohl (buffer); DEBUG ("channels available before: 0x%08x", buffer); if ((buffer & (1 << c)) != 0) { swap = htonl (buffer & ~(1 << c)); compare = htonl (buffer); result = raw1394_lock (handle, raw1394_get_irm_id (handle), addr, EXTCODE_COMPARE_SWAP, swap, compare, &new); if ( (result < 0) || (new != compare) ) { FAIL ("Failed to modify channel %d", opcr.channel); } DEBUG ("channels available after: 0x%08x", ntohl (swap)); } } } } return result; }