// ATM_QoS.cpp,v 4.2 1999/08/03 21:57:01 joeh Exp

#define ACE_BUILD_DLL
#include "ace/ATM_QoS.h"

ACE_RCSID(ace, ATM_QoS, "ATM_QoS.cpp,v 4.2 1999/08/03 21:57:01 joeh Exp")

#if defined (ACE_HAS_ATM)

#if !defined (__ACE_INLINE__)
#include "ace/ATM_QoS.i"
#endif /* __ACE_INLINE__ */

#if defined (ACE_HAS_FORE_ATM_XTI) || defined (ACE_HAS_FORE_ATM_WS2)
#define BHLI_MAGIC "FORE_ATM"
// This is line rate in cells/s for an OC-3 MM interface.
const long ACE_ATM_QoS::LINE_RATE = 353207;
const int ACE_ATM_QoS::OPT_FLAGS_CPID = 0x1;
const int ACE_ATM_QoS::OPT_FLAGS_PMP = 0x2;
const int ACE_ATM_QoS::DEFAULT_SELECTOR = 0x99;
#else
const long ACE_ATM_QoS::LINE_RATE = 0L;
const int ACE_ATM_QoS::OPT_FLAGS_CPID = 0;
const int ACE_ATM_QoS::OPT_FLAGS_PMP = 0;
const int ACE_ATM_QoS::DEFAULT_SELECTOR = 0x0;
#endif /* ACE_HAS_FORE_ATM_XTI || ACE_HAS_FORE_ATM_WS2 */

ACE_ALLOC_HOOK_DEFINE(ACE_ATM_QoS)

void
ACE_ATM_QoS::dump (void) const
{
  ACE_TRACE ("ACE_ATM_QoS::dump");
}

void
ACE_ATM_QoS::set_rate (ACE_HANDLE fd,
                       int rate,
                       int flags)
{
  ACE_TRACE ("ACE_ATM_QoS::set_rate");
#if defined (ACE_HAS_FORE_ATM_WS2)
  ACE_UNUSED_ARG (rate);
#elif defined (ACE_HAS_FORE_ATM_XTI)
  long optlen = 0;
  qos_.buf = construct_options(fd,
                               rate,
                               flags,
                               &optlen);
  qos_.len = optlen;
#else
  ACE_UNUSED_ARG (rate);
#endif /* ACE_HAS_FORE_ATM_WS2 */
}

void
ACE_ATM_QoS::set_cbr_rate (int rate)
{
  ACE_TRACE ("ACE_ATM_QoS::set_cbr_rate");
#if defined (ACE_HAS_FORE_ATM_WS2)
  AAL_PARAMETERS_IE ie_aalparams;
  ATM_TRAFFIC_DESCRIPTOR_IE ie_td;
  ATM_BROADBAND_BEARER_CAPBILITY_IE ie_bbc;
  ATM_QOS_CLASS_IE ie_qos;
  Q2931_IE *ie_ptr;
  int size;

  ie_aalparams.AALType = AALTYPE_5;
  ie_aalparams.AALSpecificParameters.AAL5Parameters.ForwardMaxCPCSSDUSize =
    1516; 
  ie_aalparams.AALSpecificParameters.AAL5Parameters.BackwardMaxCPCSSDUSize =
    1516; 
  ie_aalparams.AALSpecificParameters.AAL5Parameters.Mode = AAL5_MODE_MESSAGE;
  ie_aalparams.AALSpecificParameters.AAL5Parameters.SSCSType = AAL5_SSCS_NULL;

  size = sizeof(Q2931_IE_TYPE) + sizeof(ULONG) + sizeof(AAL_PARAMETERS_IE);

  ie_td.Forward.PeakCellRate_CLP0 = SAP_FIELD_ABSENT;
  ie_td.Forward.PeakCellRate_CLP01 = cellRate;
  ie_td.Forward.SustainableCellRate_CLP0 = SAP_FIELD_ABSENT;
  ie_td.Forward.SustainableCellRate_CLP01 = SAP_FIELD_ABSENT;
  ie_td.Forward.MaxBurstSize_CLP0 = SAP_FIELD_ABSENT;
  ie_td.Forward.MaxBurstSize_CLP01 = SAP_FIELD_ABSENT;
  ie_td.Forward.Tagging = SAP_FIELD_ABSENT;

  ie_td.Backward.PeakCellRate_CLP0 = SAP_FIELD_ABSENT;
  ie_td.Backward.PeakCellRate_CLP01 = cellRate;
  ie_td.Backward.SustainableCellRate_CLP0 = SAP_FIELD_ABSENT;
  ie_td.Backward.SustainableCellRate_CLP01 = SAP_FIELD_ABSENT;
  ie_td.Backward.MaxBurstSize_CLP0 = SAP_FIELD_ABSENT;
  ie_td.Backward.MaxBurstSize_CLP01 = SAP_FIELD_ABSENT;
  ie_td.Backward.Tagging = SAP_FIELD_ABSENT;

  ie_td.BestEffort = 0; // Note: this must be set to zero for CBR

  size +=
    sizeof(Q2931_IE_TYPE) + sizeof(ULONG) + sizeof(ATM_TRAFFIC_DESCRIPTOR_IE);

  ie_bbc.BearerClass = BCOB_X;
  ie_bbc.TrafficType = TT_CBR;
  ie_bbc.TimingRequirements = TR_END_TO_END;
  ie_bbc.ClippingSusceptability = CLIP_NOT;
  ie_bbc.UserPlaneConnectionConfig = UP_P2P;

  size += sizeof(Q2931_IE_TYPE) + sizeof(ULONG) 
    + sizeof(ATM_BROADBAND_BEARER_CAPABILITY_IE);

  ie_qos.QOSClassForward = QOS_CLASS1;
  ie_qos.QOSClassBackward = QOS_CLASS1; 

  size += sizeof(Q2931_IE_TYPE) + sizeof(ULONG) + sizeof(ATM_QOS_CLASS_IE);

  qos_.ProviderSpecific.buf = (char *) malloc(size);
  qos_.ProviderSpecific.len = size;
  ACE_OS::memset(qos_.ProviderSpecific.buf,
                 0,
                 size);

  ie_ptr = (Q2931_IE *) qos_.ProviderSpecific.buf;
  ie_ptr->IEType = IE_AALParameters;
  ie_ptr->IELength = sizeof(Q2931_IE_TYPE) + sizeof(ULONG) 
    + sizeof(AAL_PARAMETERS_IE);
  ACE_OS::memcpy(ie_ptr->IE,
                 &ie_aalparams,
                 sizeof(AAL_PARAMETERS_IE));

  ie_ptr = (Q2931_IE *) ((char *)ie_ptr + ie_ptr->IELength);
  ie_ptr->IEType = IE_TrafficDescriptor;
  ie_ptr->IELength =
    sizeof(Q2931_IE_TYPE) + sizeof(ULONG) + sizeof(ATM_TRAFFIC_DESCRIPTOR_IE);
  ACE_OS::memcpy(ie_ptr->IE,
                 &ie_td,
                 sizeof(ATM_TRAFFIC_DESCRIPTOR_IE));

  ie_ptr = (Q2931_IE *) ((char *)ie_ptr + ie_ptr->IELength);
  ie_ptr->IEType = IE_BroadbandBearerCapability;
  ie_ptr->IELength = sizeof(Q2931_IE_TYPE) + sizeof(ULONG) +
    sizeof(ATM_BROADBAND_BEARER_CAPABILITY_IE);
  ACE_OS::memcpy(ie_ptr->IE,
                 &ie_bbc,
                 sizeof(ATM_BROADBAND_BEARER_CAPABILITY_IE));

  ie_ptr = (Q2931_IE *) ((char *)ie_ptr + ie_ptr->IELength);
  ie_ptr->IEType = IE_QOSClass;
  ie_ptr->IELength =
    sizeof(Q2931_IE_TYPE) + sizeof(ULONG) + sizeof(ATM_QOS_CLASS_IE);
  ACE_OS::memcpy(ie_ptr->IE,
                 &ie_qos,
                 sizeof(ATM_QOS_CLASS_IE));

  qos_.SendingFlowspec.TokenRate = -1;
  qos_.SendingFlowspec.TokenBucketSize = -1;
  qos_.SendingFlowspec.PeakBandwidth = -1;
  qos_.SendingFlowspec.Latency = -1;
  qos_.SendingFlowspec.DelayVariation = -1;
  qos_.SendingFlowspec.LevelOfGuarantee = BestEffortService; 
  // will most probably be ignored by the service provider
  qos_.SendingFlowspec.CostOfCall = 0;

  qos_.ReceivingFlowspec.TokenRate = -1;
  qos_.ReceivingFlowspec.TokenBucketSize = -1;
  qos_.ReceivingFlowspec.PeakBandwidth = -1;
  qos_.ReceivingFlowspec.Latency = -1;
  qos_.ReceivingFlowspec.DelayVariation = -1;
  qos_.ReceivingFlowspec.LevelOfGuarantee = BestEffortService; 
  // will most probably be ignored by the service provider
  qos_.ReceivingFlowspec.CostOfCall = 0;
#elif defined (ACE_HAS_FORE_ATM_XTI)
  ACE_UNUSED_ARG (rate);
#else
  ACE_UNUSED_ARG (rate);
#endif /* ACE_HAS_FORE_ATM_WS2 */
}

char*
ACE_ATM_QoS::construct_options (ACE_HANDLE fd,
                                int rate,
                                int flags,
                                long *len)
{
#if defined (ACE_HAS_FORE_ATM_WS2)
  ACE_UNUSED_ARG (fd);
  ACE_UNUSED_ARG (rate);
  ACE_UNUSED_ARG (flag);
  ACE_UNUSED_ARG (len);
  return (0);
#elif defined (ACE_HAS_FORE_ATM_XTI)
  struct t_opthdr *popt;
  char *buf;
  int qos_cells;
  struct t_info info;

  if (ACE_OS::t_getinfo (fd, &info) == -1)
    {
      ACE_OS::t_error ("t_getinfo");
      return 0;
    }
 
  buf = (char *) ACE_OS::malloc (info.options);

  if (buf == 0)
    ACE_ERROR_RETURN ((LM_ERROR,
                       ASYS_TEXT ("Unable to allocate %ld bytes for options\n"),
                       info.options),
                      0);

  popt = (struct t_opthdr *) buf;

  if (flags & OPT_FLAGS_CPID)
    {
      // This constructs the T_ATM_ORIG_ADDR option, which is used to
      // signal the UNI 3.1 Calling Party ID Information Element.
      t_atm_addr *source_addr;

      popt->len	= sizeof (struct t_opthdr) + sizeof (t_atm_addr);
      popt->level = T_ATM_SIGNALING;
      popt->name = T_ATM_ORIG_ADDR;
      popt->status = 0;

      source_addr = 
        (t_atm_addr *)((char *) popt + sizeof (struct t_opthdr));

      source_addr->address_format = T_ATM_ENDSYS_ADDR;
      source_addr->address_length = ATMNSAP_ADDR_LEN;

      ATMSAPAddress local_addr;
      struct t_bind boundaddr;

      boundaddr.addr.maxlen = sizeof(local_addr);
      boundaddr.addr.buf = (char *) &local_addr;

      //if (ACE_OS::t_getprotaddr(fd, &boundaddr, NULL) < 0) {
      if (ACE_OS::t_getname(fd,
                            &boundaddr.addr,
                            LOCALNAME) < 0)
        {
          ACE_OS::t_error("t_getname (local_address)");
          ACE_ERROR ((LM_ERROR,
                      ASYS_TEXT ("Can't get local address!\n")));
          ACE_OS::free (buf);
          return 0;
        }

      ACE_OS::memcpy(source_addr->address,
                     local_addr.sap.t_atm_sap_addr.address,
                     ATMNSAP_ADDR_LEN);

      popt = T_OPT_NEXTHDR (buf, info.options , popt);
    }

  // This constructs all options necessary (bearer cap., QoS, and
  // Traffic Descriptor) to signal for a CBR connection with the
  // specified QoS in kbit/sec., and/or specify a PMP connection.

  // For FORE 200e cards, the adapter shapes traffic to CBR with rate
  // equal to PCR CLP=0+1 (traffic.forward.PCR_all_traffic)

  qos_cells = (rate * 1000) / (48*8);

  if ((qos_cells > 0 && qos_cells < LINE_RATE) 
      || (ACE_BIT_ENABLED (flags, OPT_FLAGS_PMP)))
    {
      struct t_atm_bearer *bearer;
      struct t_atm_traffic *traffic;

      // T_ATM_BEARER_CAP: Broadband bearer capability
      popt->len	= sizeof (struct t_opthdr) + sizeof (struct t_atm_bearer);
      popt->level = T_ATM_SIGNALING;
      popt->name = T_ATM_BEARER_CAP;
      popt->status = 0;

      bearer = (struct t_atm_bearer *)((char *) popt +
                                       sizeof (struct t_opthdr));
      bearer->bearer_class = T_ATM_CLASS_X;

      if (qos_cells)
        {
          bearer->traffic_type = T_ATM_CBR;
          bearer->timing_requirements = T_ATM_END_TO_END;
        }
      else
        {
          bearer->traffic_type	 = 0; // UBR
          bearer->timing_requirements = 0;
        }
      bearer->clipping_susceptibility = T_ATM_NULL;

      if (ACE_BIT_ENABLED (flags, OPT_FLAGS_PMP))
        bearer->connection_configuration = T_ATM_1_TO_MANY;
      else
        bearer->connection_configuration = T_ATM_1_TO_1;

      popt = T_OPT_NEXTHDR (buf, info.options, popt);

      // T_ATM_TRAFFIC: traffic descriptor
      popt->len	= sizeof (struct t_opthdr) + sizeof (struct t_atm_traffic);
      popt->level = T_ATM_SIGNALING;
      popt->name = T_ATM_TRAFFIC;
      popt->status = 0;

      traffic = (struct t_atm_traffic *)((char *) popt +
                                         sizeof (struct t_opthdr));

      traffic->forward.PCR_high_priority = T_ATM_ABSENT;
      traffic->forward.PCR_all_traffic = qos_cells ? qos_cells : LINE_RATE;
      traffic->forward.SCR_high_priority = T_ATM_ABSENT;
      traffic->forward.SCR_all_traffic = T_ATM_ABSENT;
      traffic->forward.MBS_high_priority = T_ATM_ABSENT;
      traffic->forward.MBS_all_traffic = T_ATM_ABSENT;
      traffic->forward.tagging = T_NO;

      traffic->backward.PCR_high_priority = T_ATM_ABSENT;
      traffic->backward.PCR_all_traffic	= 
        (ACE_BIT_ENABLED (flags, OPT_FLAGS_PMP)) 
        ? 0 : qos_cells ? qos_cells : LINE_RATE;
      traffic->backward.SCR_high_priority = T_ATM_ABSENT;
      traffic->backward.SCR_all_traffic	= T_ATM_ABSENT;
      traffic->backward.MBS_high_priority = T_ATM_ABSENT;
      traffic->backward.MBS_all_traffic	= T_ATM_ABSENT;
      traffic->backward.tagging = T_NO;

      traffic->best_effort = qos_cells ? T_NO : T_YES;

      popt = T_OPT_NEXTHDR (buf,
                            info.options,
                            popt);
    }

  if (qos_cells > 0 && qos_cells < LINE_RATE)
    {
      struct t_atm_qos *qos;

      // T_ATM_QOS: Quality of Service
      popt->len	= sizeof (struct t_opthdr) + sizeof (struct t_atm_qos);
      popt->level = T_ATM_SIGNALING;
      popt->name = T_ATM_QOS;
      popt->status = 0;

      qos = (struct t_atm_qos *)((char *) popt + sizeof (struct t_opthdr));
      qos->coding_standard = T_ATM_ITU_CODING;
      qos->forward.qos_class = T_ATM_QOS_CLASS_1;
      qos->backward.qos_class = T_ATM_QOS_CLASS_1;

      popt = T_OPT_NEXTHDR (buf, info.options, popt);
    }

  // return actual size of options and option buffer to user
  *len = (char *) popt - buf;

  return buf;
#else
  ACE_UNUSED_ARG (fd);
  ACE_UNUSED_ARG (rate);
  ACE_UNUSED_ARG (flag);
  ACE_UNUSED_ARG (len);
  return (0);
#endif /* ACE_HAS_FORE_ATM_WS2 */
}

#endif /* ACE_HAS_ATM */
