/* vim: set filetype=c:
 *
 **************************************************************************
 *
 * Boot-ROM-Code to load an operating system across a TCP/IP network.
 *
 * Module:  bootpbuf.c86
 * Purpose: Handle BOOTP/DHCP buffers
 * Entries: new_bootp_buf, set_bootp_size
 *
 **************************************************************************
 *
 * Copyright (C) 1999-2007 Gero Kuhlmann <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  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.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: bootpbuf.c86,v 1.7 2007/01/06 18:30:46 gkminix Exp $
 */


#include <common.h86>
#include <romconfig.h86>
#include <i386/string.h86>
#include "headers/net.h86"
#include "headers/arpa.h86"
#include "bootp.h86"



/*
 **************************************************************************
 * 
 * BOOTP/DHCP buffers. These get allocated dynamically.
 */
#define MAX_BUF_SIZE	(BOOTP_PACKET_NUM * sizeof(struct bootp))

static short last_buf;				/* ID of last alloc'd buffer */
static __u8 main_buf[MAX_BUF_SIZE];		/* main buffer */
static __u8 *main_end;				/* end of main buffer */
struct bootp *bootp_bufs[BOOTP_PACKET_NUM];	/* BOOTP buffer pointers */
size_t bootp_sizes[BOOTP_PACKET_NUM];		/* BOOTP buffer sizes */
short cur_bootp_buf;				/* ID of current buffer */



/*
 **************************************************************************
 * 
 * Initialize BOOTP buffer handling.
 */
void init_bootp_buf(void)
{
  register short i;

  last_buf = -1;
  main_end = main_buf;
  for (i = 0; i < BOOTP_PACKET_NUM; i++) {
	bootp_bufs[i] = NULL;
	bootp_sizes[i] = 0;
  }
}



/*
 **************************************************************************
 * 
 * Allocate a new BOOTP/DHCP buffer.
 *
 */
bool_t new_bootp_buf(short id)
{
  size_t newsize;

  if (bootp_bufs[id] == NULL) {
	newsize = (size_t)(main_buf + MAX_BUF_SIZE - main_end);
	if (newsize > BOOTP_MAX_SIZE)
		newsize = BOOTP_MAX_SIZE;
	if (last_buf != -1 || newsize < sizeof(struct bootp))
		return(FALSE);
	bootp_bufs[id] = (struct bootp *)main_end;
	bootp_sizes[id] = newsize;
	main_end += newsize;
	last_buf = id;
  }
  memset((__u8 *)(bootp_bufs[id]), VEND_RFC_END, bootp_sizes[id]);
  cur_bootp_buf = id;
  return(TRUE);
}



/*
 **************************************************************************
 * 
 * Resize the current BOOTP/DHCP buffer to it's actual size.
 *
 */
size_t set_bootp_size(size_t minsize, bool_t update)
{
  size_t newsize;
  __u8 *cp, *curbuf = (__u8 *)(bootp_bufs[cur_bootp_buf]);

  /*
   * Determine size of buffer. We need to make the buffer at least as large
   * as an ethernet frame (e.g. the maximum size a DHCP packet can have with-
   * out a TFTP-loaded vendor extension file), so that any downloaded client
   * program has enough space to eventually modify the buffer contents.
   */
  newsize = sizeof(struct bootp);
  (void)get_vend(VEND_RFC_NOP);
  if ((cp = get_vend(VEND_RFC_END)) != NULL) {
	newsize = (size_t)(cp - curbuf);
	if (newsize < minsize)
		newsize = minsize;
  }

  /*
   * Eventually update any global variables. If this is the last allocated
   * buffer, we can make space for new buffers.
   */
  if (update) {
	bootp_sizes[cur_bootp_buf] = newsize;
	if (last_buf == cur_bootp_buf) {
		main_end = curbuf + newsize;
		last_buf = -1;
	}
  }
  return(newsize);
}

