/*
EIBD eib bus access and management daemon
Copyright (C) 2005-2007 Martin Koegler <mkoegler@auto.tuwien.ac.at>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "groupcache.h"
#include "tpdu.h"
#include "apdu.h"
GroupCache::GroupCache (Layer3 * l3, Trace * t)
{
TRACEPRINTF (t, 4, this, "GroupCacheInit");
this->t = t;
this->layer3 = l3;
this->enable = 0;
pth_mutex_init (&mutex);
pth_cond_init (&cond);
}
GroupCache::~GroupCache ()
{
TRACEPRINTF (t, 4, this, "GroupCacheDestroy");
if (enable)
layer3->deregisterGroupCallBack (this, 0);
Clear ();
}
GroupCacheEntry *
GroupCache::find (eibaddr_t dst)
{
int l = 0, r = cache () - 1;
while (l <= r)
{
int p = (l + r) / 2;
if (cache[p]->dst == dst)
return cache[p];
if (dst > cache[p]->dst)
l = p + 1;
else
r = p - 1;
}
return 0;
}
void
GroupCache::remove (eibaddr_t addr)
{
TRACEPRINTF (t, 4, this, "GroupCacheRemove %d/%d/%d", (addr >> 11) & 0x1f,
(addr >> 8) & 0x07, (addr) & 0xff);
int l = 0, r = cache () - 1;
while (l <= r)
{
int p = (l + r) / 2;
if (cache[p]->dst == addr)
{
delete cache[p];
cache.deletepart (p, 1);
return;
}
if (addr > cache[p]->dst)
l = p + 1;
else
r = p - 1;
}
return;
}
void
GroupCache::add (GroupCacheEntry * entry)
{
unsigned p;
cache.resize (cache () + 1);
p = cache () - 1;
while (p > 0 && cache[p - 1]->dst > entry->dst)
{
cache[p] = cache[p - 1];
p--;
}
cache[p] = entry;
}
void
GroupCache::Get_L_Data (L_Data_PDU * l)
{
GroupCacheEntry *c;
if (enable)
{
TPDU *t = TPDU::fromPacket (l->data);
if (t->getType () == T_DATA_XXX_REQ)
{
T_DATA_XXX_REQ_PDU *t1 = (T_DATA_XXX_REQ_PDU *) t;
if (t1->data () >= 2 && !(t1->data[0] & 0x3) &&
((t1->data[1] & 0xC0) == 0x40 || (t1->data[1] & 0xC0) == 0x80))
{
c = find (l->dest);
if (c)
{
c->data = t1->data;
c->src = l->source;
c->dst = l->dest;
c->recvtime = time (0);
pth_cond_notify (&cond, 1);
}
else
{
c = new GroupCacheEntry;
c->data = t1->data;
c->src = l->source;
c->dst = l->dest;
c->recvtime = time (0);
add (c);
pth_cond_notify (&cond, 1);
}
}
}
delete t;
}
delete l;
}
bool
GroupCache::Start ()
{
TRACEPRINTF (t, 4, this, "GroupCacheEnable");
if (!enable)
if (!layer3->registerGroupCallBack (this, 0))
return false;
enable = 1;
return true;
}
void
GroupCache::Clear ()
{
int i;
TRACEPRINTF (t, 4, this, "GroupCacheClear");
for (i = 0; i < cache (); i++)
delete cache[i];
cache.resize (0);
}
void
GroupCache::Stop ()
{
Clear ();
TRACEPRINTF (t, 4, this, "GroupCacheStop");
if (enable)
layer3->deregisterGroupCallBack (this, 0);
enable = 0;
}
GroupCacheEntry
GroupCache::Read (eibaddr_t addr, unsigned Timeout, uint16_t age)
{
TRACEPRINTF (t, 4, this, "GroupCacheRead %d/%d/%d %d %d",
(addr >> 11) & 0x1f, (addr >> 8) & 0x07, (addr) & 0xff,
Timeout, age);
bool rm = false;
GroupCacheEntry *c;
if (!enable)
{
GroupCacheEntry f;
f.src = 0;
f.dst = 0;
TRACEPRINTF (t, 4, this, "GroupCache not enabled");
return f;
}
c = find (addr);
if (c && age && c->recvtime + age < time (0))
rm = true;
if (c && !rm)
{
TRACEPRINTF (t, 4, this, "GroupCache found: %d.%d.%d",
(c->src >> 12) & 0xf, (c->src >> 8) & 0xf,
(c->src) & 0xff);
return *c;
}
if (!Timeout)
{
GroupCacheEntry f;
f.src = 0;
f.dst = addr;
TRACEPRINTF (t, 4, this, "GroupCache no entry");
return f;
}
A_GroupValue_Read_PDU apdu;
T_DATA_XXX_REQ_PDU tpdu;
L_Data_PDU *l;
pth_event_t timeout = pth_event (PTH_EVENT_TIME, pth_timeout (Timeout, 0));;
tpdu.data = apdu.ToPacket ();
l = new L_Data_PDU;
l->data = tpdu.ToPacket ();
l->source = 0;
l->dest = addr;
l->AddrType = GroupAddress;
layer3->send_L_Data (l);
do
{
c = find (addr);
rm = false;
if (c && age && c->recvtime + age < time (0))
rm = true;
if (c && !rm)
{
TRACEPRINTF (t, 4, this, "GroupCache found: %d.%d.%d",
(c->src >> 12) & 0xf, (c->src >> 8) & 0xf,
(c->src) & 0xff);
pth_event_free (timeout, PTH_FREE_THIS);
return *c;
}
if (pth_event_status (timeout) == PTH_STATUS_OCCURRED && c)
{
GroupCacheEntry gc;
gc.src = 0;
gc.dst = addr;
TRACEPRINTF (t, 4, this, "GroupCache reread timeout");
pth_event_free (timeout, PTH_FREE_THIS);
return gc;
}
if (pth_event_status (timeout) == PTH_STATUS_OCCURRED)
{
c = new GroupCacheEntry;
c->src = 0;
c->dst = addr;
c->recvtime = time (0);
add (c);
TRACEPRINTF (t, 4, this, "GroupCache timeout");
pth_event_free (timeout, PTH_FREE_THIS);
return *c;
}
pth_mutex_acquire (&mutex, 0, 0);
pth_cond_await (&cond, &mutex, timeout);
pth_mutex_release (&mutex);
}
while (1);
}
syntax highlighted by Code2HTML, v. 0.9.1