/* 
 * 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
 */

/************************************************************************/
/*	Low Level parameter changing... This function is OS dependent,	*/
/*	and it is called by the QoS Manager task to adjust (Q,T) to	*/
/*	the new values.							*/
/************************************************************************/
#include <stdio.h>

#include <arch/local.h>

#include "qos.h"
#include "ll.h"			/* 4 prop_getband... */

#define max(a,b)  (((a) > (b)) ? (a) : (b))

/*
#define QOSDBG
*/

int ldbg;

/************************************************************************/
/*	Some parameter has been changed... 				*/
/*	We have to adjust it in the kernel				*/
/************************************************************************/
void lowlev_change(struct qtask_global *head)
{
    DWORD drel;
    float drel1;
    rk_reserve_t rsv;
    cpu_reserve_attr_data_t param;
    int res;
    struct qtask_global *p;

/* Let's use a 2-phase protocol: 
 *	1) decrease util
 *	2) increase it
 * We must do this in order to guarantee the new values...
 */
    p = head;
    while (p != NULL) {
	/* assume the RS is in ``set'' */
	rsv = p->id;
//      rsv = rk_resource_set_get_cpu_rsv(rs);
	if (rsv == NULL) {
	    cprintf("Error!!! NULL reserve!!!\n");
	    return;
	}
	rk_cpu_reserve_get_attr(rsv, &param);

	if ((p->valid == PERIOD_CHANGED) && (p->oldp < p->per)) {
#ifdef QOSDBG
	    cprintf
		("<enlarging period> Task (rsv 0x%lx) changed from %d to %d\n",
		 rsv, p->oldp, p->per);
#endif
	    param.period.tv_sec = p->per / 1000000;
	    param.period.tv_nsec = (p->per % 1000000) * 1000;
	    drel1 =
		param.deadline.tv_sec * 1000000 +
		param.deadline.tv_nsec / 1000;
	    drel1 = drel1 * p->per / p->oldp;
	    drel = (DWORD) drel1;
	    param.deadline.tv_sec = drel / 1000000;
	    param.deadline.tv_nsec = (drel % 1000000) * 1000;

	    res = rk_cpu_reserve_ctl(rsv, &param);
	    printf("res: %d\n", res);
	    p->valid = VALID;
	}
	if ((p->valid == Q_CHANGED) && (p->oldq > p->q)) {
	    if (p->q == 0) {
		ldbg = p->per;
		qos_abort(3750);
	    }
#ifdef QOSDBG
	    cprintf
		("Task (rsv 0x%lx) changed capacity from %d (%d) to %d\n",
		 rsv, p->oldq, (param.compute_time.tv_nsec / 1000), p->q);
#endif
	    /* The entry is valid and recently changed... */
	    param.compute_time.tv_sec = p->q / 1000000;
	    param.compute_time.tv_nsec = (p->q % 1000000) * 1000;

	    res = rk_cpu_reserve_ctl(rsv, &param);
	    if (res != 0) {
		cprintf("Failed!!!\n");
		cprintf
		    ("Task (rsv 0x%lx) changin' capacity from %d (%d) to %d\n",
		     rsv, p->oldq, (param.compute_time.tv_nsec / 1000),
		     p->q);
	    }

	    p->valid = VALID;
	}
	p = p->next;
    }

    /* Second phase... */
    p = head;
    while (p != NULL) {
	/* assume the RS is in ``set'' */
	rsv = p->id;
	rk_cpu_reserve_get_attr(rsv, &param);

	if ((p->valid == PERIOD_CHANGED) && (p->oldp > p->per)) {
#ifdef QOSDBG
	    cprintf("Task (rsv 0x%lx) changed from %d to %d\n",
		    rsv, p->oldp, p->per);
#endif
	    param.period.tv_sec = p->per / 1000000;
	    param.period.tv_nsec = (p->per % 1000000) * 1000;
	    drel1 =
		param.deadline.tv_sec * 1000000 +
		param.deadline.tv_nsec / 1000;
	    drel1 = drel1 * p->per / p->oldp;
	    drel = (DWORD) drel1;
	    param.deadline.tv_sec = drel / 1000000;
	    param.deadline.tv_nsec = (drel % 1000000) * 1000;

	    res = rk_cpu_reserve_ctl(rsv, &param);
	    if (res != 0) {
		cprintf("Res: %d\n", res);
		cprintf
		    ("Task (rsv 0x%lx) failed to change from %d to %d (newdl %lu)\n",
		     rsv, p->oldp, p->per, drel);
	    }
	    p->valid = VALID;
	}
	if (p->valid == Q_CHANGED) {
	    if (p->q == 0) {
		ldbg = p->per;
		qos_abort(3750);
	    }
#ifdef QOSDBG
	    cprintf
		("Task (rsv 0x%lx) changed capacity from %d (%d) to %d\n",
		 rsv, p->oldq, (param.compute_time.tv_nsec / 1000), p->q);
#endif
	    /* The entry is valid and recently changed... */
	    param.compute_time.tv_sec = p->q / 1000000;
	    param.compute_time.tv_nsec = (p->q % 1000000) * 1000;

	    res = rk_cpu_reserve_ctl(rsv, &param);
	    if (res != 0) {
		cprintf("Failed!!!\n");
		cprintf
		    ("Task (rsv 0x%lx) changin' capacity from %d (%d) to %d\n",
		     rsv, p->oldq, (param.compute_time.tv_nsec / 1000),
		     p->q);
	    }

	    p->valid = VALID;
	}
	p = p->next;
    }
}
