/* text2qos.c - Converts textual representation of QOS parameters to binary
		encoding */

/* Written 1996-2000 by Werner Almesberger, EPFL-LRC/ICA */

#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>

#include "atm.h"
#include "../../../../kernel/drivers/adsl/mgmt/atmgdef.h"

#define FAILURE -1
#define SUCCESS 0
#define fetch __atmlib_fetch


#define RATE_ERROR -2


int __t2q_get_rate(const char **text,int up)
{
    const char mult[] = "kKmMgGg";
    const char *multiplier;
    char *end;
    unsigned int rate,fract;
    int power;

    if (!strncmp(*text,"max",3)) {
	*text += 3;
	return ATM_MAX_PCR;
    }
    rate = strtoul(*text,&end,10);
    power = fract = 0;
    if (*end == '.')
	for (end++; *end && isdigit(*end); end++) {
	    fract = fract*10+*end-48;
	    if (--power == -9) break;
	}
    multiplier = NULL;
    if (*end && (multiplier = strchr(mult,*end))) {
	while (multiplier >= mult) {
	    if (rate > UINT_MAX/1000) return RATE_ERROR;
	    rate *= 1000;
	    power += 3;
	    multiplier -= 2;
	}
	end++;
    }
    while (power && fract)
	if (power < 0) {
	    fract /= 10;
	    power++;
	}
	else {
	    fract *= 10;
	    power--;
	}
    rate += fract;
    if (strlen(end) < 3) {
	if (multiplier) return RATE_ERROR;
    }
    else if (!strncmp(end,"cps",3)) end += 3;
	else if (!strncmp(end,"bps",3)) {
		rate = (rate+(up ? 8*ATM_CELL_PAYLOAD-1 : 0))/8/
		  ATM_CELL_PAYLOAD;
		end += 3;
	    }
	    else if (multiplier) return RATE_ERROR;
    if (rate > INT_MAX) return RATE_ERROR;
    *text = end;
    return rate;
}


static int params(const char **text,struct atm_trafprm *a,
  struct atm_trafprm *b)
{
    int value;
    char *end;

    if (*(*text)++ != ':') return -1;
    while (1) {
	if (!**text) return -1;
	switch (fetch(text,"max_pcr=","pcr=","min_pcr=","max_sdu=","sdu=",
	"cdvt=","burst=",NULL)) {
	    case 0:
		if ((value = __t2q_get_rate(text,0)) == RATE_ERROR) return -1;
		if (a) a->max_pcr = value;
		if (b) b->max_pcr = value;
		break;
	    case 1:
		if ((value = __t2q_get_rate(text,0)) == RATE_ERROR) return -1;
		if (a) a->pcr = value;
		if (b) b->pcr = value;
		break;
	    case 2:
		if ((value = __t2q_get_rate(text,1)) == RATE_ERROR) return -1;
		if (value == ATM_MAX_PCR) return -1;
		if (a) a->min_pcr = value;
		if (b) b->min_pcr = value;
		break;
	    case 3:
	    case 4:
		value = strtol(*text,&end,10);
		if (value < 0) return -1;
		*text = end;
		if (a) a->max_sdu = value;
		if (b) b->max_sdu = value;
		break;
	    case 5:
		value = strtol(*text,&end,10);
		if (value < 0) return -1;
		*text = end;
		if (a) a->max_cdv = value;
		if (b) b->max_cdv = value;
		break;
	    case 6:
		value = strtol(*text,&end,10);
		if (value < 0) return -1;
		*text = end;
		if (a) a->mbs = value;
		if (b) b->mbs = value;
		break;
	    default:
		return 0;
	}
	if (!**text) break;
	if (*(*text)++ != ',') return -1;
    }
    return 0;
}


int text2qos(const char *text,struct atm_qos *qos,int flags)
{
    int traffic_class,aal;

    traffic_class = ATM_NONE;
    aal = ATM_NO_AAL;
    do {
	static const unsigned char aal_number[] = { ATM_AAL0, ATM_AAL5 };
	int item;

	item = fetch(&text,"!none","ubr","cbr","rtvbr","abr","aal0","aal5","nrtvbr",NULL);
	switch (item) {
	    case 1:
	    case 2:
	    case 3:
		/* we don't support VBR yet */
	    case 4:
	    case 7:
		traffic_class = item;
		break;
	    case 5:
	    case 6:
		aal = aal_number[item-5];
		break;
	    default:
		return -1;
	}
    }
    while (*text == ',' ? text++ : 0);
    if (!traffic_class) return -1;
    if (qos && !(flags & T2Q_DEFAULTS)) memset(qos,0,sizeof(*qos));
    if (qos) qos->txtp.traffic_class = qos->rxtp.traffic_class = traffic_class;
    if (qos && aal) qos->aal = aal;
    if (!*text) return 0;
    if (params(&text,qos ? &qos->txtp : NULL,qos ? &qos->rxtp : NULL))
	return -1;
    if(atmQosValidation(qos) == -1)
    {
        printf("QOS parameter Invalid\r\n");
        return -1;
    }
    if (!*text) return 0;
    switch (fetch(&text,"tx","rx",NULL)) {
	case 0:
	    if (!fetch(&text,":none",NULL)) {
		if (qos) qos->txtp.traffic_class = ATM_NONE;
		if (*text == ',') text++;
		break;
	    }
	    if (params(&text,qos ? &qos->txtp : NULL,NULL)) return -1;
	    break;
	case 1:
	    text -= 2;
	    break;
	default:
	    return -1;
    }
    if (!*text) return 0;
    if (fetch(&text,"rx",NULL)) return -1;
    if (!fetch(&text,":none",NULL) && qos)
          qos->rxtp.traffic_class = ATM_NONE;
    else if (params(&text,qos ? &qos->rxtp : NULL,NULL))
        return -1;
    
    return *text ? -1 : 0;
}

int atmQosValidation(struct atm_qos *ptr2qos)
{
     // Validate traffic parameters
     if ( ptr2qos->txtp.max_pcr > UI_ATM_MAX_PCR)
     {
       printf("Error: Maximun Peak cell rate exceeds the maximum of %d\r\n" , UI_ATM_MAX_PCR);
       return FAILURE;
     }

     if ( ptr2qos->txtp.mbs > UI_ATM_MAX_MBS)
     {
       printf("Error: Maximum burst size exceeds the maximum of %d\r\n", UI_ATM_MAX_MBS);
       return FAILURE;
     }

     if ( ptr2qos->txtp.max_cdv > UI_ATM_MAX_CDVT)
     {
       printf("Error: CDVT exceeds the maximum of %d\r\n", UI_ATM_MAX_CDVT);
       return FAILURE;
     }

     // Validate parameters compliance
     if( ptr2qos->txtp.traffic_class == ATM_CBR
#if UBR_NO_SCR_MBS
     ||  ptr2qos->txtp.traffic_class == ATM_UBR
#endif
       )
     {
       if( ( ptr2qos->txtp.pcr != 0) || ( ptr2qos->txtp.mbs != 0) )
       {
         printf("Peak cell rate or maximum burst rate is not used for CBR traffic\r\n");
         return FAILURE;
       }
     }

     if( ptr2qos->txtp.traffic_class == ATM_VBR ||
         ptr2qos->txtp.traffic_class == ATM_NRTVBR ||
         ptr2qos->txtp.traffic_class == ATM_CBR )
     {
       if( ptr2qos->txtp.max_pcr == 0)
       {
         printf( "Missing peak value\r\n");
         return FAILURE;
       }

       /* Assigned default values if not specified by the user */
       if( ptr2qos->txtp.max_cdv == 0 )
       {
         ptr2qos->txtp.max_cdv = UI_ATM_DEFAULT_CDVT;
       }
     }

     /* Ensure that PCR is smaller or equal to the MAX_PCR */
     if( ptr2qos->txtp.pcr > ptr2qos->txtp.max_pcr )
     {
         printf("Peak cell rate cannot be higher than Maximum peak cell rate\r\n");
         return FAILURE;
     }

     if( ptr2qos->txtp.traffic_class == ATM_VBR ||
         ptr2qos->txtp.traffic_class == ATM_NRTVBR )
     {
       if( ptr2qos->txtp.pcr <= 0 )
       {
         ptr2qos->txtp.pcr = ptr2qos->txtp.max_pcr;
       }

       /* Assigned default values if not specified by the user */
       if( ptr2qos->txtp.mbs == 0 )
       {
         ptr2qos->txtp.mbs = UI_ATM_DEFAULT_MBS;
       }
     }

     if( ptr2qos->txtp.traffic_class == ATM_UBR )
     {
       /* Assigned default values if not specified by the user */
       if( ptr2qos->txtp.max_pcr == 0 )
       {
         ptr2qos->txtp.max_pcr = UI_ATM_DEFAULT_PCR;
       }

       /* Assigned default values if not specified by the user */
       if( ptr2qos->txtp.pcr == 0 )
       {
         ptr2qos->txtp.pcr = ptr2qos->txtp.max_pcr;
       }

       /* Assigned default values if not specified by the user */
       if( ptr2qos->txtp.mbs == 0 )
       {
         ptr2qos->txtp.mbs = UI_ATM_DEFAULT_MBS;
       }

       /* Assigned default values if not specified by the user */
       if( ptr2qos->txtp.max_cdv == 0 )
       {
         ptr2qos->txtp.max_cdv = UI_ATM_DEFAULT_CDVT;
       }
     }
     return SUCCESS;
}
