text: /*
   * ipmi_sel.cpp
   *
   * Copyright (c) 2003 by FORCE Computers
   * Copyright (c) 2005 by ESO Technologies.
   *
   * Note that this file is based on parts of OpenIPMI
   * written by Corey Minyard <minyard@mvista.com>
   * of MontaVista Software. Corey's code was helpful
   * and many thanks go to him. He gave the permission
   * to use this code in OpenHPI under BSD license.
   *
   * 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.  This
   * file and program are licensed under a BSD style license.  See
   * the Copying file included with the OpenHPI distribution for
   * full licensing terms.
   *
   * Authors:
   *     Thomas Kanngieser <thomas.kanngieser@fci.com>
   *     Pierre Sangouard  <psangouard@eso-tech.com>
   */
  
  #include <stdlib.h>
  #include <string.h>
  #include <errno.h>
  
  #include "ipmi_cmd.h"
  #include "ipmi_sel.h"
  #include "ipmi_mc.h"
  #include "ipmi_log.h"
  #include "ipmi_utils.h"
  #include "ipmi_event.h"
  #include "ipmi_domain.h"
  
  
  cIpmiSel::cIpmiSel( cIpmiMc *mc, unsigned int lun )
    : m_mc( mc ), m_lun( lun ),
      m_major_version( 0 ), m_minor_version( 0 ),
      m_entries( 0 ),
      m_last_addition_timestamp( 0 ),
      m_last_erase_timestamp( 0 ),
      m_overflow( false ),
      m_supports_delete_sel( false ), m_supports_partial_add_sel( false ),
      m_supports_reserve_sel( false ), m_supports_get_sel_allocation( false ),
      m_fetched( false ), m_reservation( 0 ),
      m_sels_changed( false ),
      m_sel( 0 ), m_sel_num( 0 ),
      m_async_events( 0 ), m_async_events_num( 0 )
  {
  }
  
  
  cIpmiSel::~cIpmiSel()
  {
    m_sel_lock.Lock();
  
    if ( m_sel )
         ClearList( m_sel );
  
    if ( m_async_events )
         ClearList( m_async_events );
  
    m_sel_lock.Unlock();
  }
  
  
  SaErrorT
  cIpmiSel::ClearSel()
  {
    cThreadLockAuto al( m_sel_lock );
  
    SaErrorT rv;
  
    // do a reservation only when needed
    if (    m_supports_reserve_sel 
         && m_reservation == 0 )
       {
         rv = Reserve();
  
         if ( rv != SA_OK )
              return rv;
       }
  
    stdlog << "clear SEL.\n";
  
    cIpmiMsg msg( eIpmiNetfnStorage, eIpmiCmdClearSel );
    msg.m_data_len = 6;
    IpmiSetUint16( msg.m_data, m_reservation );
    msg.m_data[2]  = 'C';
    msg.m_data[3]  = 'L';
    msg.m_data[4]  = 'R';
    msg.m_data[5]  = 0xaa;
  
    cIpmiMsg rsp;
  
    rv = m_mc->SendCommand( msg, rsp, m_lun );
  
    if ( rv != SA_OK )
         return rv;
  
    if ( rsp.m_data[0] == 0 )
       {
         m_sel = ClearList( m_sel );
         m_sel_num = 0;
       }
  
    return SA_OK;
  }
  
  
  SaErrorT
  cIpmiSel::GetInfo()
  {
    cIpmiMsg     msg( eIpmiNetfnStorage, eIpmiCmdGetSelInfo );
    cIpmiMsg     rsp;
    SaErrorT     rv;
    unsigned int add_timestamp;
    unsigned int erase_timestamp;
  
    // Fetch the repository info.
    rv = m_mc->SendCommand( msg, rsp, m_lun );
  
    if ( rv != SA_OK )
       {
         stdlog << "could not send get sel info: " << rv << " !\n";
         return rv;
       }
  
    if ( rsp.m_data[0] != 0 )
       {
         stdlog << "IpmiSelGetInfo: IPMI error from SEL info fetch: "
                << rsp.m_data[0] << " !\n";
  
         return SA_ERR_HPI_INVALID_PARAMS;
       }
  
    if ( rsp.m_data_len < 15 )
       {
         stdlog << "handle_sel_info: SEL info too short !\n";
         return SA_ERR_HPI_INVALID_DATA;
       }
  
    unsigned short num = m_entries;
  
    // Pull pertinant info from the response.
    m_major_version               = rsp.m_data[1] & 0xf;
    m_minor_version               = (rsp.m_data[1] >> 4) & 0xf;
    m_entries                     = IpmiGetUint16( rsp.m_data + 2 );
    m_overflow                    = (rsp.m_data[14] & 0x80) == 0x80;
    m_supports_delete_sel         = (rsp.m_data[14] & 0x08) == 0x08;
    m_supports_partial_add_sel    = (rsp.m_data[14] & 0x04) == 0x04;
    m_supports_reserve_sel        = (rsp.m_data[14] & 0x02) == 0x02;
    m_supports_get_sel_allocation = (rsp.m_data[14] & 0x01) == 0x01;
  
    add_timestamp   = IpmiGetUint32( rsp.m_data + 6 );
    erase_timestamp = IpmiGetUint32( rsp.m_data + 10 );
  
    // If the timestamps still match, no need to re-fetch the repository
    if (    m_fetched
         && m_entries == num
         && (add_timestamp   == m_last_addition_timestamp )
         && (erase_timestamp == m_last_erase_timestamp    ) )
       {
         // no need to read sel
         return -1;
       }
  
    m_last_addition_timestamp = add_timestamp;
    m_last_erase_timestamp    = erase_timestamp;
  
    m_sels_changed = true;
    m_fetched      = true;
  
    return SA_OK;
  }
  
  
  SaErrorT
  cIpmiSel::Reserve()
  {
    cIpmiMsg msg( eIpmiNetfnStorage, eIpmiCmdReserveSel );
    cIpmiMsg rsp;
    SaErrorT rv;
  
    // Get a reservation.
    rv = m_mc->SendCommand( msg, rsp, m_lun );
  
    if ( rv != SA_OK )
       {
         stdlog << "cannot send reserve sel: " << rv << " !\n";
         return rv;
       }
  
    if ( rsp.m_data[0] != 0 )
       {
         stdlog << "sel_handle_reservation: Failed getting reservation !\n";
         return SA_ERR_HPI_INVALID_PARAMS;
       }
  
    if ( rsp.m_data_len < 3 )
       {
         stdlog << "sel_handle_reservation: got invalid reservation length !\n";
         return SA_ERR_HPI_INVALID_DATA;
       }
  
    m_reservation = IpmiGetUint16( rsp.m_data + 1 );
  
    return SA_OK;
  }
  
  
  GList *
  cIpmiSel::ClearList( GList *list )
  {
    while( list )
       {
         cIpmiEvent *e = (cIpmiEvent *)list->data;
         list = g_list_remove( list, e );
         delete e;
       }
  
    return 0;
  }
  
  
  int
  cIpmiSel::ReadSelRecord( cIpmiEvent &event, unsigned int &next_rec_id )
  {
    // read record
    cIpmiMsg msg( eIpmiNetfnStorage, eIpmiCmdGetSelEntry );
    cIpmiMsg rsp;
  
    IpmiSetUint16( msg.m_data, m_reservation );
    IpmiSetUint16( msg.m_data+2, next_rec_id );
    msg.m_data[4] = 0;
    msg.m_data[5] = 0xff;
    msg.m_data_len = 6;
  
    SaErrorT rv = m_mc->SendCommand( msg, rsp, m_lun );
  
    if ( rv != SA_OK )
       {
         stdlog << "Could not send SEL fetch command: " << rv << " !\n";
         return -1;
       }
    
    if ( rsp.m_data[0] == eIpmiCcInvalidReservation )
       {
         stdlog << "SEL reservation lost !\n";
         m_reservation = 0;
  
         return eIpmiCcInvalidReservation;
       }
  
    if ( rsp.m_data[0] != 0 )
       {
         stdlog << "IPMI error from SEL fetch: " 
                << rsp.m_data[0] << " !\n";
  
         return -1;
       }
  
    next_rec_id = IpmiGetUint16( rsp.m_data + 1 );
  
    event.m_mc        = m_mc;
    event.m_record_id = IpmiGetUint16( rsp.m_data + 3 );
    event.m_type      = rsp.m_data[5];
    memcpy( event.m_data, rsp.m_data + 6, 13 );
  
    return 0;
  }
  
  
  GList *
  cIpmiSel::ReadSel( unsigned int &num, bool &uptodate )
  {
    SaErrorT rv = SA_OK;
    GList *new_events = 0;
    num = 0;
    int fetch_retry_count = 0;
    uptodate = false;
  
    while( true )
       {
         if ( fetch_retry_count >= dMaxSelFetchRetries )
            {
              stdlog << "too many lost reservations in SEL fetch !\n";
              return 0;
            }
  
         fetch_retry_count++;
  
         // get reservation
         m_reservation = 0;
  
         rv = GetInfo();
  
         if ( rv == -1 )
            {
              // no new entries
              uptodate = true;
              return 0;
            }
  
         if ( rv != SA_OK || m_entries == 0 )
              return 0;
  
         if ( m_supports_reserve_sel )
            {
              rv = Reserve();
  
              if ( rv )
                   continue;
            }
  
         // read records
         unsigned int next_rec_id = 0;
  
         do
            {
              cIpmiEvent *event = new cIpmiEvent;
  
              rv = ReadSelRecord( *event, next_rec_id );
  
              if ( rv )
                 {
                   delete event;
                   ClearList( new_events );
                   new_events = 0;
                   num = 0;
  
                   if ( rv == eIpmiCcInvalidReservation )
                        break;
  
                   return 0;
                 }
  
              new_events = g_list_append( new_events, event );
              num++;
            }
         while( next_rec_id != 0xffff );
  
         if ( next_rec_id == 0xffff )
              break;
       }
  
    return new_events;
  }
  
  
  cIpmiEvent *
  cIpmiSel::FindEvent( GList *list, unsigned int record_id )
  {
    while( list )
       {
         cIpmiEvent *e = (cIpmiEvent *)list->data;
         
         if ( e ->m_record_id == record_id )
              return e;
         
         list = g_list_next( list );
       }
  
    return 0;
  }
  
  
  bool
  cIpmiSel::CheckEvent( GList *&list, cIpmiEvent *event )
  {
    cIpmiEvent *e = FindEvent( list, event->m_record_id );
  
    if ( !e )
         return false;
  
    // remove old event from list
    list = g_list_remove( list, e );
  
    // return true if event is old event
    bool rv = event->Cmp( *e ) == 0 ? true : false;
  
    delete e;
  
    return rv;
  }
  
  
  GList *
  cIpmiSel::GetEvents()
  {
    cThreadLockAuto al( m_sel_lock );
  
    stdlog << "reading SEL.\n";
  
    // read sel
    bool uptodate = false;
    unsigned int events_num = 0;
    GList *events = ReadSel( events_num, uptodate );
  
    if ( uptodate )
       {
         return 0;
       }
  
    // build a list of new events
    GList *new_events = 0;
  
    for( GList *item = events; item; item = g_list_next( item ) )
       {
         cIpmiEvent *current = (cIpmiEvent *)item->data;
  
         if ( CheckEvent( m_sel, current ) == false )
            {
              m_async_events_lock.Lock();
              bool rv = CheckEvent( m_async_events, current );
              m_async_events_lock.Unlock();
  
              if ( rv == false )
                 {
                   // new event found
                   cIpmiEvent *e = new cIpmiEvent( *current );
                   new_events = g_list_append( new_events, e );
                 }
            }
       }
  
    ClearList( m_sel );
    m_sel     = events;
    m_sel_num = events_num;
  
    return new_events;
  }
  
  
  SaErrorT
  cIpmiSel::GetSelEntry( unsigned short rid, unsigned short &prev, 
                         unsigned short &next, cIpmiEvent &event )
  {
    cThreadLockAuto al( m_sel_lock );
  
    // empty sel
    if ( m_sel == 0 )
       {
         prev = 0;
         next = 0xffff;
  
         return SA_ERR_HPI_NOT_PRESENT;
       }
  
    cIpmiEvent *e = 0;
    GList *item;
    GList *i;
  
    if ( rid == 0 )
       {
         // first entry
         e = (cIpmiEvent *)m_sel->data;
         event = *e;
  
         // prev
         prev = 0;
  
         // next
         item = g_list_next( m_sel );
   
         if ( item )
            {
              e = (cIpmiEvent *)item->data;
              next = e->m_record_id;
            }
         else
              next = 0xffff;
  
         return SA_OK;
       }
  
    if ( rid == 0xffff )
       {
         // last entry
         item = g_list_last( m_sel );
  
         e = (cIpmiEvent *)item->data;
  
         event = *e;
         
         // prev
         item = g_list_previous( item );
         
         if ( item )
            {
              e = (cIpmiEvent *)item->data;
              prev = e->m_record_id;
            }
         else
              prev = 0;
  
         next = 0xffff;
  
         return SA_OK;  
       }
  
    item = 0;
  
    // find rid event
    for( i = m_sel; i; i = g_list_next( i ) )
       {
         e = (cIpmiEvent *)i->data;
  
         if ( e->m_record_id == rid )
            {
              item = i;
              break;
            }
       }
  
    if ( item == 0 )
         return SA_ERR_HPI_NOT_PRESENT;
    
    // event found
    e = (cIpmiEvent *)item->data;
  
    event = *e;
  
    // prev
    i = g_list_previous( item );
  
    if ( i )
       {
         e = (cIpmiEvent *)i->data;
         prev = e->m_record_id;
       }
    else
         prev = 0;
  
    // next
    i = g_list_next( item );
  
    if ( i )
       {
         e = (cIpmiEvent *)i->data;
         next = e->m_record_id;
       }
    else
         next = 0xffff;
  
    return SA_OK;
  }
  
  
  SaErrorT
  cIpmiSel::DeleteSelEntry( SaHpiEventLogEntryIdT sid )
  {
    cThreadLockAuto al( m_sel_lock );
  
    unsigned short rid = (unsigned short)sid;
  
    if ( sid == SAHPI_OLDEST_ENTRY )
         rid = 0;
    else if ( sid == SAHPI_NEWEST_ENTRY )
         rid = 0xffff;
    else
         rid = sid;
  
    for( int i = 0; i < dMaxSelFetchRetries; i++ )
       {
         SaErrorT rv = Reserve();
  
         if ( rv != SA_OK )
              return rv;
  
         cIpmiMsg msg( eIpmiNetfnStorage, eIpmiCmdDeleteSelEntry );
         cIpmiMsg rsp;
  
         IpmiSetUint16( msg.m_data, m_reservation );
         IpmiSetUint16( msg.m_data + 2, rid );
         msg.m_data_len = 4;
  
         rv = m_mc->SendCommand( msg, rsp );
  
         if ( rv != SA_OK )
            {
              stdlog << "Could not send delete SEL entry: " << rv << " !\n";
              return rv;
            }
  
         if ( rsp.m_data[0] != eIpmiCcOk )
            {
              if ( rsp.m_data[0] == eIpmiCcInvalidReservation )
                   // reservation lost
                   continue;
  
              stdlog << "IPMI error from delete SEL entry: "
                     << rsp.m_data[0] << " !\n";
  
              return SA_ERR_HPI_INVALID_CMD;
            }
  
         if ( rsp.m_data_len < 3 )
            {
              stdlog << "IPMI error from delete SEL entry: message to short "
                     << rsp.m_data_len << " !\n";
  
              return SA_ERR_HPI_INVALID_DATA;
            }
  
         // deleted record id
         rid = IpmiGetUint16( rsp.m_data + 1 );
  
         // remove record from m_sel
         cIpmiEvent *e = FindEvent( m_sel, rid );
  
         if ( e )
            {
              m_sel = g_list_remove( m_sel, e );
              m_sel_num--;
            }
  
         // remove record from async event list
         m_async_events_lock.Lock();
  
         e = FindEvent( m_async_events, rid );
  
         if ( e )
            {
              m_async_events = g_list_remove( m_async_events, e );
              m_async_events_num--;
            }
  
         m_async_events_lock.Unlock();
  
         return SA_OK;
       }
  
    // reservation lost too many times
    stdlog << "IPMI error from delete SEL entry: reservation lost too many times !\n";
  
    return SA_ERR_HPI_INVALID_CMD;
  }
  
  
  SaErrorT
  cIpmiSel::GetSelTime( SaHpiTimeT &ht )
  {
    cIpmiMsg msg( eIpmiNetfnStorage, eIpmiCmdGetSelTime );
    cIpmiMsg rsp;
  
    SaErrorT rv = m_mc->SendCommand( msg, rsp );
  
    if ( rv != SA_OK )
       {
         stdlog << "Could not send get SEL time: " << rv << " !\n";
         return rv;
       }
  
    if ( rsp.m_data[0] != eIpmiCcOk )
       {
         stdlog << "IPMI error from get SEL time: " 
                << rsp.m_data[0] << " !\n";
  
         return SA_ERR_HPI_INVALID_CMD;
       }
  
    if ( rsp.m_data_len < 5 )
       {
         stdlog << "IPMI error from get SEL time: message to short "
                << rsp.m_data_len << " !\n";
  
         return SA_ERR_HPI_INVALID_DATA;
       }
  
    ht = IpmiGetUint32( rsp.m_data + 1 );
    ht *= 1000000000;
  
    return SA_OK;
  }
  
  
  static time_t
  CovertToAbsTimeT( SaHpiTimeT ti )
  {
    if ( ti <= SAHPI_TIME_MAX_RELATIVE )
       {
         timeval tv;
         gettimeofday( &tv, 0 );
         
         tv.tv_sec  += ti / 1000000000;
         tv.tv_usec += ti % 1000000000 / 1000;
  
         while( tv.tv_usec > 1000000 )
            {
              tv.tv_sec++;
              tv.tv_usec -= 1000000;
            }
   
         return tv.tv_sec;
       }
  
    return ti / 1000000000;
  }
  
  
  SaErrorT
  cIpmiSel::SetSelTime( SaHpiTimeT ht )
  {
    if ( ht == SAHPI_TIME_UNSPECIFIED )
         return SA_ERR_HPI_ERROR;
  
    // convert HPI time to time_t
    time_t t = CovertToAbsTimeT( ht );
  
    cIpmiMsg msg( eIpmiNetfnStorage, eIpmiCmdSetSelTime );
    cIpmiMsg rsp;
  
    IpmiSetUint32( msg.m_data, t );
    msg.m_data_len = 4;
  
    SaErrorT rv = m_mc->SendCommand( msg, rsp );
  
    if ( rv != SA_OK )
       {
         stdlog << "Could not send set SEL time: " << rv << " !\n";
         return rv;
       }
  
    if ( rsp.m_data[0] != eIpmiCcOk )
       {
         stdlog << "IPMI error from set SEL time: "
                << rsp.m_data[0] << " !\n";
  
         return SA_ERR_HPI_INVALID_CMD;
       }
  
    return SA_OK;
  }
  
  
  int
  cIpmiSel::AddAsyncEvent( cIpmiEvent *new_event )
  {
    cIpmiEvent *e = FindEvent( m_sel, new_event->m_record_id );
  
    // event is already in the sel
    if ( e && new_event->Cmp( *e ) == 0 )
         return 0;
  
    m_async_events_lock.Lock();
  
    e = FindEvent( m_async_events, new_event->m_record_id );
  
    if ( !e )
       {
         // add new event to list
         e = new cIpmiEvent;
         *e = *new_event;
         m_async_events = g_list_append( m_async_events, e );
         m_async_events_num++;
         m_async_events_lock.Unlock();
  
         return 0;
       }
  
    m_async_events_lock.Unlock();
  
    if ( new_event->Cmp( *e ) == 0 )
         // event is already in the list of async events
         return 0;
  
    // overwrite old event
    *e = *new_event;
  
    return 0;
  }
  
  void
  cIpmiSel::Dump( cIpmiLog &dump, const char *name )
  {
    if ( dump.IsRecursive() )
       {
         // dump events
         int i = 0;
  
         for( GList *list = m_sel; list; list = g_list_next( list ) )
  	  {
  	    cIpmiEvent *e = (cIpmiEvent *)list->data;
  
  	    char str[80];
  	    snprintf( str, sizeof(str), "Event%02x_%d", m_mc->GetAddress(), i++ );
  	    e->Dump( dump, str );
  	  }
       }
  
    dump.Begin( "Sel", name );
    dump.Entry( "Version" ) << (int)m_major_version << ", " << (int)m_minor_version << ";\n";
    dump.Entry( "Overflow" ) << m_overflow << ";\n";
    dump.Entry( "SupportsDeleteSel" ) << m_supports_delete_sel << ";\n";
    dump.Entry( "SupportsPartialAddSel" ) << m_supports_partial_add_sel << ";\n";
    dump.Entry( "SupportsReserveSel" ) << m_supports_reserve_sel << ";\n";
    dump.Entry( "SupportsGetSelAllocation" ) << m_supports_get_sel_allocation << ";\n";
  
    // dump events
    if ( dump.IsRecursive() && m_sel )
       {
         int i = 0;
  
         dump.Entry( "Event" );
  
         for( GList *list = m_sel; list; list = g_list_next( list ) )
            {
              if ( i != 0 )
                   dump << ", ";
  
              char str[80];
              snprintf( str, sizeof(str), "Event%02x_%d", m_mc->GetAddress(), i++ );
              dump << str;
            }
  
         dump << ";\n";
       }
  
    dump.End();
  }
  
  SaErrorT
  cIpmiSel::GetSelInfo( SaHpiEventLogInfoT &info )
  {
    cIpmiMc *mc = Mc();
    int lun = Lun();
  
    cIpmiMsg msg( eIpmiNetfnStorage, eIpmiCmdGetSelTime );
    cIpmiMsg rsp;
  
    SaErrorT rv = mc->SendCommand( msg, rsp, lun );
  
    if ( rv != SA_OK || rsp.m_data[0] != eIpmiCcOk )
         return (rv != SA_OK) ? rv : SA_ERR_HPI_INVALID_DATA;
  
    Lock();
  
    info.Entries              = SelNum();
    info.Size                 = 0xffff;
    // We don't support adding entries to SEL yet
    info.UserEventMaxSize     = 0;
  
    if ( AdditionTimestamp() > EraseTimestamp() )
         info.UpdateTimestamp = AdditionTimestamp();
    else
         info.UpdateTimestamp      = EraseTimestamp();
  
    info.UpdateTimestamp     *= 1000000000;
    info.CurrentTime          = IpmiGetUint32( rsp.m_data + 1 );
    info.CurrentTime         *= 1000000000;
    info.Enabled              = SAHPI_TRUE; // ?????
    info.OverflowFlag         = Overflow() ? SAHPI_TRUE : SAHPI_FALSE;
    info.OverflowResetable    = SAHPI_FALSE;
    info.OverflowAction       = SAHPI_EL_OVERFLOW_DROP;
  
    Unlock();
  
    return SA_OK;
  }
  
  
  SaErrorT
  cIpmiSel::AddSelEntry( const SaHpiEventT & /*Event*/ )
  {
    return SA_ERR_HPI_UNSUPPORTED_API;
  }
  
  
  SaErrorT
  cIpmiSel::GetSelEntry( SaHpiEventLogEntryIdT current,
  		       SaHpiEventLogEntryIdT &prev, SaHpiEventLogEntryIdT &next,
  		       SaHpiEventLogEntryT &entry,
                 SaHpiRdrT &rdr,
                 SaHpiRptEntryT &rptentry )
  {
    unsigned short rid = (unsigned short)current;
  
    if ( current == SAHPI_OLDEST_ENTRY )
         rid = 0;
    else if ( current == SAHPI_NEWEST_ENTRY )
         rid = 0xffff;
  
    unsigned short p;
    unsigned short n;
  
    cIpmiEvent e;
  
    SaErrorT rv = GetSelEntry( rid, p, n, e );
  
    if ( rv != SA_OK )
         return rv;
  
    cIpmiMc     *mc = 0;
    cIpmiSensor *sensor = 0;
    cIpmiAddr    addr;
  
    addr.m_type = eIpmiAddrTypeIpmb;
  
    if ( e.m_data[6] == 0x03 )
         addr.m_channel = 0;
    else
         addr.m_channel = e.m_data[5] >> 4;
  
    addr.m_slave_addr = e.m_data[4];
    addr.m_lun = 0;
  
    mc = Mc()->Domain()->FindMcByAddr( addr );
  
    if ( mc )
         sensor = mc->FindSensor((e.m_data[5] & 0x3), e.m_data[8], e.m_data[4]);
  
    prev = p;
    next = n;
  
    if ( prev == 0 )
         prev = SAHPI_NO_MORE_ENTRIES;
  
    if ( next == 0xffff )
         next = SAHPI_NO_MORE_ENTRIES;
  
    entry.EntryId = e.m_record_id;
    entry.Timestamp = IpmiGetUint32( e.m_data );
  
    if ( entry.Timestamp == 0 )
         entry.Timestamp = SAHPI_TIME_UNSPECIFIED;
    else
         entry.Timestamp *= 1000000000;
  
    entry.Event.Timestamp = entry.Timestamp;
  
    if ( &rptentry != NULL )
      rptentry.ResourceCapabilities = 0;
    if ( &rdr != NULL )
      rdr.RdrType = SAHPI_NO_RECORD;
  
    if ( !sensor )
       {
         // this is possible an event of a resource
         // no longer present.
         entry.Event.Source    = 0;
         entry.Event.EventType = SAHPI_ET_OEM;
         entry.Event.Severity  = SAHPI_MAJOR;
  
         return SA_OK;
       }
  
    if ( &rptentry != NULL )
    {
        SaHpiRptEntryT *selrpt = oh_get_resource_by_id( sensor->Resource()->Domain()->GetHandler()->rptcache,
                                                        sensor->Resource()->m_resource_id );
  
        if ( selrpt != NULL )
            rptentry = *selrpt;
    }
  
    if ( &rdr != NULL )
    {
        SaHpiRdrT *selrdr = oh_get_rdr_by_id( sensor->Resource()->Domain()->GetHandler()->rptcache,
                                              sensor->Resource()->m_resource_id, sensor->RecordId() );
  
        if ( selrdr != NULL )
            rdr = *selrdr;
    }
  
    rv = sensor->CreateEvent( &e, entry.Event );
  
    if ( rv == SA_ERR_HPI_DUPLICATE )
        rv = SA_OK;
  
    return rv;
  }
  