text: /*
   *   Creation Date: <2003/12/03 22:10:45 samuel>
   *   Time-stamp: <2004/01/07 19:17:45 samuel>
   *
   *	<disk-label.c>
   *
   *	Partition support
   *
   *   Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
   *
   *   This program is free software; you can redistribute it and/or
   *   modify it under the terms of the GNU General Public License
   *   version 2
   *
   */
  
  #include "config.h"
  #include "libopenbios/bindings.h"
  #include "libopenbios/load.h"
  #include "libc/diskio.h"
  #include "libc/vsprintf.h"
  #include "packages.h"
  
  //#define DEBUG_DISK_LABEL
  
  #ifdef DEBUG_DISK_LABEL
  #define DPRINTF(fmt, args...) \
  do { printk("DISK-LABEL - %s: " fmt, __func__ , ##args); } while (0)
  #else
  #define DPRINTF(fmt, args...) do { } while (0)
  #endif
  
  typedef struct {
  	xt_t 		parent_seek_xt;
  	xt_t 		parent_tell_xt;
  	xt_t		parent_read_xt;
  
          ucell	        offs_hi, offs_lo;
          ucell	        size_hi, size_lo;
  	int		block_size;
  	int		type;		/* partition type or -1 */
  
  	ihandle_t	part_ih;
  	phandle_t	filesystem_ph;
  } dlabel_info_t;
  
  DECLARE_NODE( dlabel, 0, sizeof(dlabel_info_t), "/packages/disk-label" );
  
  
  /* ( -- ) */
  static void
  dlabel_close( __attribute__((unused))dlabel_info_t *di )
  {
  }
  
  /* ( -- success? ) */
  static void
  dlabel_open( dlabel_info_t *di )
  {
  	char *path;
  	char block0[512];
  	phandle_t ph;
  	int success=0;
  	cell status;
  
  	path = my_args_copy();
  
  	DPRINTF("dlabel-open '%s'\n", path );
  
  	di->part_ih = 0;
  
  	/* Find parent methods */
  	di->filesystem_ph = 0;
  	di->parent_seek_xt = find_parent_method("seek");
  	di->parent_tell_xt = find_parent_method("tell");
          di->parent_read_xt = find_parent_method("read");	
  
  	/* Read first block from parent device */
  	DPUSH(0);
  	call_package(di->parent_seek_xt, my_parent());
  	POP();
  
  	PUSH(pointer2cell(block0));
  	PUSH(sizeof(block0));
  	call_package(di->parent_read_xt, my_parent());
  	status = POP();
  	if (status != sizeof(block0))
  		goto out;
  
  	/* Find partition handler */
  	PUSH( pointer2cell(block0) );
  	selfword("find-part-handler");
  	ph = POP_ph();
  	if( ph ) {
  		/* We found a suitable partition handler, so interpose it */
  		DPRINTF("Partition found on disk - scheduling interpose with ph " FMT_ucellx "\n", ph);
  
  		push_str(path);
  		PUSH_ph(ph);
  		fword("interpose");	
  
  		success = 1;
  	} else {
  		/* unknown (or missing) partition map,
  		 * try the whole disk
  		 */
  
  		DPRINTF("Unknown or missing partition map; trying whole disk\n");
  
  		/* Probe for filesystem from start of device */
  		DPUSH ( 0 );	
  		PUSH_ih( my_self() );
  		selfword("find-filesystem");
  		ph = POP_ph();
  		if( ph ) {
  			/* If we have been asked to open a particular file, interpose the filesystem package with the passed filename as an argument */
  			di->filesystem_ph = ph;
  
  			DPRINTF("Located filesystem with ph " FMT_ucellx "\n", ph);
  			DPRINTF("path: %s length: %d\n", path, strlen(path));
  
                          if (path && strlen(path)) {
  				DPRINTF("INTERPOSE!\n");
  
  				push_str( path );
  				PUSH_ph( ph );
  				fword("interpose");
  			}
                  } else if (path && strcmp(path, "%BOOT") != 0) {
  			goto out;
  		}
  
  		success = 1;
  	}
  
  out:
  	if( path )
  		free( path );
  	if( !success ) {
  		dlabel_close( di );
  		RET(0);
  	}
  	PUSH(-1);
  }
  
  /* ( addr len -- actual ) */
  static void
  dlabel_read( dlabel_info_t *di )
  {
  	/* Call back up to parent */
  	call_package(di->parent_read_xt, my_parent());
  }
  
  /* ( pos.d -- status ) */
  static void
  dlabel_seek( dlabel_info_t *di )
  {
  	/* Call back up to parent */
  	call_package(di->parent_seek_xt, my_parent());
  }
  
  /* ( -- filepos.d ) */
  static void
  dlabel_tell( dlabel_info_t *di )
  {
  	/* Call back up to parent */
  	call_package(di->parent_tell_xt, my_parent());
  }
  
  /* ( addr len -- actual ) */
  static void
  dlabel_write( __attribute__((unused)) dlabel_info_t *di )
  {
  	DDROP();
  	PUSH( -1 );
  }
  
  /* ( addr -- size ) */
  static void
  dlabel_load( __attribute__((unused)) dlabel_info_t *di )
  {
  	/* Try the load method of the part package */
  #ifdef DEBUG_DISK_LABEL
  	char *buf;
  #endif
  	xt_t xt;
  
  #ifdef DEBUG_DISK_LABEL
  	buf = (char *)POP();
  #else
          POP();
  #endif
  
  	DPRINTF("load invoked with address %p\n", buf);
  
  	/* If we have a partition handle, invoke the load word on it */
  	if (di->part_ih) {
  		xt = find_ih_method("load", di->part_ih);
  		if (!xt) {
  			forth_printf("load currently not implemented for ihandle " FMT_ucellx "\n", di->part_ih);
  			PUSH(0);
  			return;
  		}
  	
  		DPRINTF("calling load on ihandle " FMT_ucellx "\n", di->part_ih);
  
  		call_package(xt, di->part_ih);
  	} else {
  		/* Otherwise attempt load directly on the raw disk */
  		DPRINTF("calling load on raw disk ihandle " FMT_ucellx "\n", my_self());
  
  		load(my_self());
  	}
  }
  
  /* ( pathstr len -- ) */
  static void
  dlabel_dir( dlabel_info_t *di )
  {
  	if ( di->filesystem_ph ) {
  		PUSH( my_self() );
  		push_str("dir");
  		PUSH( di->filesystem_ph );
  		fword("find-method");
  		POP();
  		fword("execute");
   	} else {
  		forth_printf("disk-label: Unable to determine filesystem\n");
  		POP();
  		POP();
  	}
  }
  
  NODE_METHODS( dlabel ) = {
  	{ "open",	dlabel_open 	},
  	{ "close",	dlabel_close 	},
  	{ "load",	dlabel_load 	},
  	{ "read",	dlabel_read 	},
  	{ "write",	dlabel_write 	},
  	{ "seek",	dlabel_seek 	},
  	{ "tell",	dlabel_tell 	},
  	{ "dir",	dlabel_dir 	},
  };
  
  void
  disklabel_init( void )
  {
  	REGISTER_NODE( dlabel );
  }
  