Blame src/plug.c

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