/* 
 * Real-Time and Multimedia Systems Laboratory
 * Copyright (c) 2000 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Real-Time and Multimedia Systems Laboratory
 *  Attn: Prof. Raj Rajkumar
 *  Electrical and Computer Engineering, and Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 *  or via email to raj@ece.cmu.edu
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */
/*
 * Portable QoS manager, implementing the comcepts described in
 * ``Adaptive Bandwidth Reservation for Multimedia Computing''
 * by L. Abeni and G. Buttazzo
 * IEEE Real Time Computing Systems and Applications 1999
 *
 *			program developed by Luca Abeni
 *					luca@sssup.it
 *					http://hartik.sssup.it/~luca
 */

/************************************************************************/
/*	This file contains the basic proportional feedback function	*/
/************************************************************************/
#include <stdlib.h>
#include <arch/basetype.h>
#include <arch/funcs.h>

#include "req.h"
#include "adj.h"
#include "stats.h"

#define MAXADJ 64
//#define __ADJ_DEBUG__

#define min(a,b) (a < b) ? a : b

struct reqactions adjactions = {
    adj_init,
    adj_create,
    adj_destroy,
    adj_setb,
    adj_feedb
};

/*
 * Inits the feeedback function environment...
 */
void adj_init(void)
{
/* This is only usefull in a multithreaded environment... */
}

/*
 * Creates and initializes a feedback function structure
 */
void *adj_create(DWORD q, DWORD desired, float maxfeed, DWORD period)
{
    struct adjparm *parms;
    static int i = 0;

    parms = malloc(sizeof(struct adjparm));
    parms->b = USEC2BAND(q, period);
#ifdef __ADJ_DEBUG__
    cprintf("Created with b = %f\n", parms->b);
#endif
    parms->oldb = parms->b;
    parms->incr = 20 * parms->b;
    parms->desired = desired;
    parms->max = maxfeed;
    parms->period = period;
    parms->acc = 0;
    parms->iden = i++;
    parms->progr = 0;

    return parms;
}

/*
 * Releases a feedback function structure
 */
void adj_destroy(void *p)
{
    struct adjparm *parms = (struct adjparm *) p;

    free(parms);
}

/*
 * ... And this is the real feedback function!!!
 */
float adj_feedb(void *p, int l)
{
    struct adjparm *parms = (struct adjparm *) p;
    float newb;

#ifdef __ADJ_DEBUG__
    cprintf("Old BW: %f --- err: %d --- ", parms->oldb,
	    l - parms->desired);
#endif
    newb = parms->oldb + ((l - parms->desired) * parms->b) / parms->period;

#ifdef __ADJ_DEBUG__
    cprintf("Proportional BW: %f\n", newb);
#endif

    if (l == parms->desired) {
	if (parms->acc >= parms->incr / 1) {
	    parms->acc -= parms->incr / 1;
	} else {
	    parms->acc = 0;
	}
    } else {
	parms->acc += parms->incr;
    }

#ifdef __ADJ_DEBUG__
    cprintf("Accumulated value: %f\n", parms->acc);
#endif

    if (newb > parms->max) {
	newb = parms->max;
    }
#ifdef __ADJ_STATS__
    adjstats[parms->progr][parms->iden].time = qos_time();
    adjstats[parms->progr][parms->iden].l = l;
    adjstats[parms->progr][parms->iden].band = newb;
    adjstats[parms->progr][parms->iden].p1 = parms->oldb;
    adjstats[parms->progr][parms->iden].p2 =
	(parms->b * (l - parms->desired)) / parms->period;
    adjstats[parms->progr][parms->iden].p3 = parms->acc;
    adjstats[parms->progr++][parms->iden].q =
	BAND2CAPACITY(parms->period, newb);
#endif

    parms->oldb = newb;

    return newb;
}

/*
 * Sets the reserved bandwidth after an eventual compression...
 */
void adj_setb(void *p, float band)
{
    struct adjparm *parms = (struct adjparm *) p;

    parms->oldb = band;
}
