/* Copyright 1998 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
*
*  LanMan.C -- Lan Manager network client main module
*
*  Versions
*
*  28-02-94 INH Derived from FSinC (added c.Statics)
*
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include "kernel.h"
#include "swis.h"

#include "stdtypes.h"
#include "lanman.h"
#include "lmvars.h"
#include "Params.h"
#include "corefn.h"

#include "buflib.h"
#include "netbios.h"
#include "SMB.h"
#include "xlate.h"
#include "version.h"
#include "omni.h"
#include "printers.h"
#include "RPC.h"
#include "stats.h"
#include "logon.h"
#include "DCI4.h"

#include "sys/types.h"
#include "netinet/in.h"
#include "arpa/inet.h"


/* ---------------------------------- */

/* Added SNB 980224 */
extern char *LM_Status(void);

#define DEFAULT_ETHER_TYPE 	"eh0"

typedef void (*pfnShutdown)(void);

enum {
        LMInitState_Uninitialised,
        LMInitState_PreInit,
        LMInitState_FullyInited,
        LMInitState_Boot
};

static _kernel_oserror *LM_init_phase_2(void);

/* ---------------------------------- */

int  LM_pw;          /* Our module's private word */
struct NETBIOS_TRANSPORT *NB_ActiveTransport; /* Selected net transport */
static bool LM_Declared;  /* True if we're registered as a filing system */

/* ------------------------------- */

#define CMOS_FSNUMBER   5
/* Contains number of FS to be booted */
#define CMOS_FSFLAGS    16
/* Contains flags, bit 4 is set if booting is enabled */
#define FSF_BOOT        0x10

#define CMOS_FSSTAT     1 /* NAS 11/Feb/97 */
#define CMOS_FSERVER1   2 /* NAS 11/Feb/97 */
#define CMOS_FSERVER    158 /* NAS 11/Feb/97 */
#define FSERVER_LEN     (172-159+1)
/* Locations in CMOS containing file server name.
   It's assumed this is less than NAME_LIMIT. If it's not, put FSERVER_LEN
   at NAME_LIMIT-1, and it'll have to be truncated. */

#define CMOS_NB_TYPE    0x6F
#define	NBTYPE_IP_BIT	(1<<0)

/* Can select IP or NetBEUI transport with this bit in CMOS. It's 0 if
   we should use NetBEUI, 1 if IP */


/* ------------------ */

int RdCMOS ( int addr )
{
  return (_kernel_osbyte ( 161, addr, 0 ) & 0xFF00 ) >> 8;
}

/* ------------------ */

static void WrCMOS ( int addr, int data )
{
  _kernel_osbyte ( 162, addr, data );
}

/* --------------------- */

static void SetFSName(char *buf_in)
{
  int i, t;

  WrCMOS(CMOS_FSSTAT, 0);
  WrCMOS(CMOS_FSERVER1, buf_in[0]);
  if (buf_in[0] == 0) return;

  for (i=1; i < FSERVER_LEN; i++)
  {
    /* NAS 11/Feb/97 */
    t = buf_in[i];
    if (t <= 0x20 || t > 0x7F) t = 0;
    WrCMOS ( CMOS_FSERVER+i-1, t);
    if (t == 0) break;
  }
}

/* --------------------- */

static void GetFSName ( char *buf_out )
{
  int i;

  if ( RdCMOS ( CMOS_FSSTAT ) == 0 )
  {
    /* NAS 11/Feb/97 */
    *buf_out++ = RdCMOS ( CMOS_FSERVER1 );
    for ( i=1; i < FSERVER_LEN; i++ )
      *buf_out++ = RdCMOS ( CMOS_FSERVER+i-1 );
  }

  *buf_out = 0;
}

/* Finalisation code ------------------------------------ */

static void LM_Undeclare ( void )
{
  _kernel_swi_regs r;

  if ( LM_Declared )
  {
    r.r[0] = FSControl_RemoveFilingSystem;
    r.r[1] = (int)FilingSystemName;

    _kernel_swi( XOS_Bit | OS_FSControl, &r, &r );
    LM_Declared = false;
  }
}

/* ------------------ */

_kernel_oserror * LM_Finalise (int fatal, int podule, void *pw)
{
  debug0("Finalise\n");
  (void) fatal;
  (void) podule;
  (void) pw;
  if (LM_Vars.initialised == LMInitState_FullyInited) {
    Omni_Shutdown();
    OmniS_ResourceShutdown();
    Prn_Shutdown();
    SMB_Shutdown();
    NB_Shutdown();
    Buf_Shutdown();
    LM_Vars.initialised = LMInitState_PreInit;
  }

  LM_Undeclare();
  return NULL;
}


/* LM_declare ------------------------------------------ */

static _kernel_oserror *LM_Declare ( void )
{
  _kernel_swi_regs r;
  _kernel_oserror *err;
  int InfoBlk [ Information_Block_Size ];

  LM_Declared = false;

  InfoBlk[0] = (int)FilingSystemName             - (int)Image_RO_Base;
  InfoBlk[1] = -1; /*(int)"Lan Man network connection" - (int)Image_RO_Base;*/
  InfoBlk[2] = (int) veneer_fsentry_open         - (int)Image_RO_Base;
  InfoBlk[3] = (int) veneer_fsentry_getbytes     - (int)Image_RO_Base;
  InfoBlk[4] = (int) veneer_fsentry_putbytes     - (int)Image_RO_Base;
  InfoBlk[5] = (int) veneer_fsentry_args         - (int)Image_RO_Base;
  InfoBlk[6] = (int) veneer_fsentry_close        - (int)Image_RO_Base;
  InfoBlk[7] = (int) veneer_fsentry_file         - (int)Image_RO_Base;
  InfoBlk[8] = Information_Word;
  InfoBlk[9] = (int) veneer_fsentry_func         - (int)Image_RO_Base;
  InfoBlk[10] = (int)veneer_fsentry_gbpb         - (int)Image_RO_Base;

  r.r[0] = FSControl_AddFilingSystem;
  r.r[1] = (int)Image_RO_Base;
  r.r[2] = (int)InfoBlk - (int)Image_RO_Base;
  r.r[3] = (int)LM_pw;


  err = _kernel_swi( XOS_Bit | OS_FSControl, &r, &r );
  if ( err == NULL )
    LM_Declared=true;

  return err;
}


/* *Command processor ------------------------------------ */

#define MAX_ARGS    20
#define MAX_CMDLEN  256

static char Cmdbuf[ MAX_CMDLEN ];

/* --------------------- */

static int GetArgs ( char *args, char *argv_out[], int maxargs )
{
  /* Splits "args" into a number of separate arguments. Each one is
     copied into Cmdbuf so it is held statically. Pointers to the
     start of each one are put in argv_out. Any unused spaces in
     argv_out are set to NULL for convenience. Returns the number
     of arguments actually found.  */

  int i, argc;

  for (i=0; i < maxargs; i++ ) argv_out[i] = NULL;

  i=0; /* In this loop, i counts characters in Cmdbuf */
  argc=0;

  do
  {
    while ( *args == ' ' ) /* Skip spaces */
      args++;

    if ( *args < ' ' )  /* The end */
      break;

    /* Got one */

    if ( argc >= maxargs )  /* Too many! */
      break;

    argv_out[argc++] = Cmdbuf+i;

    while ( i < MAX_CMDLEN-1 && *args > ' ' ) /* Copy it */
      Cmdbuf[i++] = *args++;

    Cmdbuf[i++] = 0;     /* Terminate it */
  }
    while ( i < MAX_CMDLEN-1 );

  return argc;
}

/* --------------------- */

static _kernel_oserror *Cmd_LANMAN ( char *args )
{
  /* No args! */
  _kernel_swi_regs r;
  (void) args;

  r.r[0] = FSControl_SelectFilingSystem;
  r.r[1] = (int)FilingSystemName;

  return _kernel_swi( XOS_Bit | OS_FSControl, &r, &r );
}

/* --------------------- */

static _kernel_oserror *Cmd_CONNECT ( char *args )
{
  char *argv[5];
  err_t res;
  int   tmp;

  if ( GetArgs ( args, argv, 5 ) < 3 )
    return Xlt_Error(EBADPARAM);

  /* Convert '-' user name to NULL */

  if ( argv[3] != NULL    &&
       argv[3][0] == '-'  &&
       argv[3][1] == 0 )
    argv[3] = NULL;

  res =  Omni_MountServer ( argv[1],  /* Server */
          argv[3], /* User name or NULL */
          argv[4], /* password or NULL */
          argv[0], /* Mountname */
          argv[2], /* Drive name */
          &tmp ); /* MountID_out */

  Omni_RecheckInfo ( RI_MOUNTS );
  return Xlt_Error(res);
}

/* --------------------- */

static _kernel_oserror *Cmd_DISCONNECT ( char *args )
{
  char *argv[1];
  int mountID;
  err_t res;

  if ( GetArgs( args, argv, 1 ) < 1 )
    return Xlt_Error(EBADPARAM);

  mountID = Omni_GetMountID(argv[0]);
  if ( mountID == 0 ) return Xlt_Error ( EBADDRV );

  res = Omni_DismountServer(mountID);
  Omni_RecheckInfo ( RI_MOUNTS );
  return Xlt_Error (res);
}

/* --------------------- */

struct LMvars LM_Vars;

/* --------------------- */

static void SetDefaultVars ( void )
{
  char *name;
  char fs_name [ FSERVER_LEN+1 ];

#ifdef DEBUG
    LM_Vars.verbose = true; /* DEBUG NAS */
#else
    LM_Vars.verbose = false;
#endif

  LM_Vars.initialised = LMInitState_Uninitialised;
  name = getenv("Inet$EtherType");
  strcpyn ( LM_Vars.drivername,
                (name==NULL) ? DEFAULT_ETHER_TYPE : name, NAME_LIMIT );

  name = getenv("Inet$HostName");
  /* Trap added to catch 'ARM_NoName', as set by STB OS */
  if ((name == NULL) || (stricmp(name,"ARM_NoName")==0))
    strcpy ( LM_Vars.machinename,   "" );
  else
  {
    strcpyn_upper ( LM_Vars.machinename, name, NAME_LIMIT );
    /* Truncate at first dot - it might be a full domain name */
    name = strchr ( LM_Vars.machinename, '.' );
    if ( name != NULL ) *name = 0;
  }

  /* Following 'get from CMOS' bits added 980127:RCE                *
   * to address user complain that it has to be typed in every time *
   */

  Lgn_Init();
  GetFSName(fs_name);
  if (fs_name[0] != '\0')
  {
    strcpy(LM_Vars.workgroup,fs_name);
  }

  LM_Vars.namemode  = NM_FIRSTCAPS;

  /* Set transport type from CMOS, unless overridden on command line */

  if ( RdCMOS ( CMOS_NB_TYPE ) & NBTYPE_IP_BIT )
    NB_NBIP_Setup();
  else
    NB_NetBEUI_Setup();

}

/* --------------------- */

static _kernel_oserror *Cmd_LOGOFF( char *args )
{
  (void) args;
  Lgn_Logoff();
  return NULL;
}

/* --------------------- */

static _kernel_oserror *Cmd_LOGON( char *args )
{
  char *argv[3];
  if ( GetArgs(args, argv, 3) < 2 )
    return Xlt_Error(EBADPARAM);

  /* argv[2] may be NULL for blank password */

  return Xlt_Error (Lgn_Logon ( argv[0], argv[1], argv[2] ) );
}


/* --------------------- */

static _kernel_oserror *Cmd_LMINFO ( char *args )
{
  (void) args;
  Omni_Debug();
  if (LM_Vars.verbose)
          printf("Status: %s\n", LM_Status());
  return NULL;
}

/* --------------------- */

static _kernel_oserror *Cmd_LMNAMEMODE ( char *args )
{
  char *argv[1];
  int tmp;

  if ( GetArgs( args, argv, 1 ) < 1 ||
       sscanf( argv[0], "%d", &tmp) != 1 ||
       tmp < 0 ||
       tmp > 2
     )
    return Xlt_Error (EBADPARAM);

  LM_Vars.namemode = tmp;
  return NULL;
}

/* --------------------- */

static _kernel_oserror *Cmd_LMSERVER ( char *args )
{
  char *argv[MAX_ARGS];
  int i;

  if ( GetArgs( args, argv, MAX_ARGS ) < 1 )
    return Xlt_Error ( EBADPARAM );

  if ( argv[1] == NULL ) /* Just name of server */
  {
    Omni_AddInfo ( OAI_SERVER, argv[0], NULL );
  }
  else
  {
    for ( i=1; i < MAX_ARGS && argv[i] != NULL; i++ )
      Omni_AddInfo ( OAI_DISK, argv[0], argv[i] );
  }

  Omni_RecheckInfo (RI_SERVERS);
  return NULL;
}

/* --------------------- */

static _kernel_oserror *Cmd_LMPRINTERS ( char *args )
{
  char *argv[MAX_ARGS];
  int i;

  if ( GetArgs( args, argv, MAX_ARGS ) < 1 )
    return Xlt_Error ( EBADPARAM );

  if ( argv[1] == NULL ) /* Just name of server */
  {
    Omni_AddInfo ( OAI_SERVER, argv[0], NULL );
  }
  else
  {
    for ( i=1; i < MAX_ARGS && argv[i] != NULL; i++ )
      Omni_AddInfo ( OAI_PRINTER, argv[0], argv[i] );
  }

  Omni_RecheckInfo (RI_SERVERS);
  Omni_RecheckInfo (RI_PRINTERS);
  return NULL;
}

/* --------------------- */

static _kernel_oserror *Cmd_LMSTATS ( char *args )
{
  (void) args;
  Stat_Show();
  return NULL;
}

/* --------------------- */

static _kernel_oserror *Cmd_FS ( char *args )
{
  /* This is a *Configure/Status command handler - it's therefore unusual */
  char *argv[1];

  if ( (int) args == 0 ) /* Show 'Configure' options */
  {                      /* Syntax corrected 980127 RCE */
    printf("FS           <domain-name> | <file-server>\n");
    return NULL;
  }
  else if ( (int) args == 1 ) /* Show FS status */
  {
    char buf[NAME_LIMIT];
    GetFSName(buf);
    printf("FS           %s\n", buf);
    return NULL;
  }

  if ( GetArgs( args, argv, 1 ) < 1 )
    return Xlt_Error ( EBADPARAM );

  SetFSName(argv[0]);
  return NULL;
}

/* --------------------- */

static _kernel_oserror *Cmd_LMTRANSPORT ( char *args )
{
  /* This is a *Configure/Status command handler */
  char *argv[1];
  char *tp0 = "NetBEUI";
  char *tp1 = "IP";

  if ( (int) args == 0 ) /* Show 'Configure' options */
  {
    printf("LMTransport  [%s | %s]\n", tp0, tp1);
    return NULL;
  }
  else if ( (int) args == 1 ) /* Show FS status */
  {
    printf("LMTransport  %s\n",
      (RdCMOS(CMOS_NB_TYPE) & NBTYPE_IP_BIT) ? tp1:tp0);
    return NULL;
  }

  if ( GetArgs( args, argv, 1 ) < 1 )
    return Xlt_Error ( EBADPARAM );

  if ( stricmp ( argv[0], tp0 ) == 0 )
    WrCMOS(CMOS_NB_TYPE, RdCMOS(CMOS_NB_TYPE) & ~NBTYPE_IP_BIT );
  else if ( stricmp ( argv[0], tp1 ) == 0 )
    WrCMOS(CMOS_NB_TYPE, RdCMOS(CMOS_NB_TYPE) | NBTYPE_IP_BIT );
  else
    printf("'%s' isn't a supported option\n", argv[0] );

  return NULL;
}

/* --------------------- */
static _kernel_oserror *Cmd_NBNSIP(char *args)
{
  /* This is a *Configure/Status command handler - it's therefore unusual */
  char *argv[1];
  struct in_addr address;
  int result;

  if ( (int)args == 0 ) /* Show 'Configure' options */
  {
    printf("LMNameServer xx.xx.xx.xx\n");
    return NULL;
  }
  else if ( (int) args == 1 ) /* Show FS status */
  {
    int b0, b1, b2, b3;

    b0 = RdCMOS(NBNSIPCMOS0);
    b1 = RdCMOS(NBNSIPCMOS1);
    b2 = RdCMOS(NBNSIPCMOS2);
    b3 = RdCMOS(NBNSIPCMOS3);

    if ( ((b0 != 0) && (b0 != 127) && (b0 <= 223))  &&  ((b3 != 0) && (b3 != 255)) )
    {
      printf("LMNameServer %d.%d.%d.%d\n", b0,b1,b2,b3);
    }
    else
    {
      printf("LMNameServer <unset>\n");
    }
    return NULL;
  }
  else if (GetArgs(args,argv,1) == 1)
  {
    result = inet_aton(argv[0],&address);
    if (result)
    {
      int b0, b1, b2, b3;

      b0 = (address.s_addr & 0x000000ff);
      b1 = (address.s_addr & 0x0000ff00) >>  8;
      b2 = (address.s_addr & 0x00ff0000) >> 16;
      b3 = (address.s_addr & 0xff000000) >> 24;

      if ( ((b0 != 127) && (b0 <= 223))  &&  ((b3 != 255)) )
      {
        WrCMOS(NBNSIPCMOS0,b0);
        WrCMOS(NBNSIPCMOS1,b1);
        WrCMOS(NBNSIPCMOS2,b2);
        WrCMOS(NBNSIPCMOS3,b3);
        return NULL;
      }
    }
  }
  return Xlt_Error(EBADPARAM);
}

/* --------------------- */

typedef _kernel_oserror * (*CommandFnPtr) ( char *args );

#define MAX_CMDS 13
static CommandFnPtr Cmd_Dispatch[MAX_CMDS] =
{
  Cmd_LANMAN,
  Cmd_CONNECT,
  Cmd_DISCONNECT,
  Cmd_LOGON,
  Cmd_LMINFO,
  Cmd_LMNAMEMODE,
  Cmd_LOGOFF,
  Cmd_LMSERVER,
  Cmd_LMPRINTERS,
  Cmd_LMSTATS,
  Cmd_FS,
  Cmd_LMTRANSPORT,
  Cmd_NBNSIP
};

/* --------------------- */

_kernel_oserror *LM_Command ( char *args, int argc, int cmd_no, void *pw )
{
  (void) pw;
  (void) argc;

  if ( cmd_no < 0 || cmd_no >= MAX_CMDS )
    return Xlt_Error(ENOTPRESENT);

  return Cmd_Dispatch[cmd_no](args);
}

/* Service call handler --------------------------------- */
static void LM_check_driver_status(_kernel_swi_regs *r);

void LM_Service ( int service_number, _kernel_swi_regs *r, void *pw )
{
  (void) pw;
  /* Any changes to list of service calls?  Must mirror in cmhg inputfile too! */

  switch(service_number)
  {
    case Service_FSRedeclare:
      LM_Declare();
      break;

    case Service_OmniAction:
      Omni_ServiceCall (r);
      break;

    case Service_ResourceFSStarting:
      OmniS_ResFSStarting(r->r[2], r->r[3]);
      break;

    case Service_DCIDriverStatus:
      LM_check_driver_status(r);
      break;

    default:
      break;
  }
}

/* SWI handler ------------------------------------------ */

_kernel_oserror *LM_Swi( int swi_ofs, _kernel_swi_regs *r, void *pw )
{
  (void) pw;  /* Not used */
  switch ( LanMan_SWI_base + swi_ofs )
  {
    case SWI_LanMan_OmniOp:
      return OmniOp_SWI(r);

    case SWI_LanMan_LogonOp:
      return Lgn_LogonOp_SWI(r);

    case SWI_LanMan_FreeOp:
      return Omni_FreeOp_SWI(r);

    case SWI_LanMan_NameOp:
      return Xlt_Error(RPC_NameOp(r->r[0], /* reason code */
                                  (char *)(r->r[1]), /* name in */
                                  (char *)(r->r[2])  /* buffer out */
                                   ));
    case SWI_LanMan_Transact:
      return Xlt_Error(RPC_Transact((char *)(r->r[0]), /* server */
                                    (char *)(r->r[1]), /* share name */
                                    (void *)(r->r[2]) /* params */
                                     ));

    default:
      return Xlt_Error(ENOTPRESENT);
  }

  return NULL;
}

/* Initialisation code ---------------------------------- */

static char *copy_space ( char *dst, char *src, int maxlen )
{
  while ( *src > ' ' )
  {
    if ( --maxlen > 0 )
      *dst++ = *src;
    src++;
  }

  *dst = 0;
  return src;
}

/* ------------------------------- */

static err_t ProcessCmdLine ( char *line )
{
  char cli[256];
  char *end = line;

  /* This code relies on NB_xxx_Setup() only setting variables, not
     allocating anything. Multiple NB_xxx_Setup()'s may be called if
     the user is being perverse */

  /* NAS 20/3/97 - RO doesn't pass a 0-terminated string, but a ctrl-terminated one */
  strncpy(cli, line, 255);
  line = end = cli;
  while (*end >= ' ') end++;
  *end = '\0';

  while ((line = strchr(line, '-')) != NULL)
  {
    line++;
    switch ( toupper(*line) )
    {
      case 'D':    /* Specify driver name ************ */
         line = copy_space ( LM_Vars.drivername, line+1, NAME_LIMIT );
         break;

      case 'H':
      case '?':
         printf( "Lan Man client for RISCOS, version " VERSION_STRING
                 "\nWritten by Ian Harvey 1994-1996\nUse:\n"
                 "\t-dea0\tto use 'ea0' for network driver (etc)\n"
                 "\t-n\tto disable network browsing\n"
                 "\t-mMYNAME\tto set machine name to MYNAME\n"
                 "\t-i\tto use NetBIOS-over-IP transport\n"
                 "\t-t\tto use NetBEUI transport\n"
                 "\t-v\tto show progress information on startup\n"
                 "\t-?\tto display this help\n" );
         return ENOTINSTALLED;

      case 'V':
         LM_Vars.verbose = true;
         break;

      case 'N':
         Lgn_Logoff();
         break;

      case 'M':
         line = copy_space( LM_Vars.machinename, line+1, NAME_LIMIT );
         strcpyn_upper( LM_Vars.machinename,LM_Vars.machinename,NAME_LIMIT );
         break;

      case 'I':
         NB_NBIP_Setup();
         break;

      case 'T':
         NB_NetBEUI_Setup();
         break;

      default:
         printf("\nCommand line switch '-%c' not recognised\n", line[0] );
         printf("Use '-?' to show available options\n");
         return ECMDLINE;

    }

  }

  return OK;
}


/* ------------------------------- */

#if 0
static void LM_StartupBoot ( void )
{

  /* Checks to see if we need to boot (by reading the CMOS RAM).
     If so, it

     i)   Gets the workgroup/domain name from CMOS RAM
     ii)  Tries to find a DC for the given name. If so, it uses this
           as the server name.
     iii) Attempts to mount "ARMBOOT", with user ARMBOOT and no password.

  */

  if ( RdCMOS(CMOS_FSNUMBER) != Our_FS_Number )
    return;

  if ( (RdCMOS(CMOS_FSFLAGS) & FSF_BOOT) == 0 )
    return;

  #ifdef AUTOBOOT_ON_INIT
  LM_Boot();
  #endif
}
#endif

void LM_Boot(void)
{
  int tmp;
  err_t res;
  char *serv_name;
  char fs_name [ FSERVER_LEN+1 ];

  if (LM_Vars.initialised < LMInitState_FullyInited) {
  if (LM_Vars.verbose) {
          printf("LanManFS: LM_Boot called, but cannot comply: %s\n", LM_Status());
  }
    LM_Vars.initialised = LMInitState_Boot;
    return;
  }

  GetFSName(fs_name);

  if ( strlen(fs_name) == 0 )
  {
    printf("Auto-boot: server name has not been set...\n");
    return;
  }

  /* Ready to go... */

  serv_name = RPC_GetDomainController(fs_name);
  if ( serv_name == NULL )
    serv_name = fs_name;

  if ( LM_Vars.verbose )
    printf("  Server name '%s'\n", serv_name );

  /* Wash'n'go... */

  res =  Omni_MountServer ( serv_name,  /* Server */
          "ARMBOOT", "", /* User name & password */
          "BOOT",        /* Mountname */
          "ARMBOOT",     /* Drive name */
          &tmp );          /* MountID_out */

  if ( res != OK )
  {
    printf("  Error during boot: %s\n", Xlt_Error(res)->errmess);
  }
}

/* ------------------------------- */


/* SNB 980224.
 *
 *
 *  This function has been split into two as it is inappropriate for some
 *  parts of this code to be executed during a service call.  The initialised
 *  member of LM_Vars has been promoted to 'int' type so that it can hold any
 *  of the values in the anonymous enum at the top of this file.  This is used
 *  to control the execution of the LM_init_phase_2 function.
 *
 *
 */

_kernel_oserror *LM_Initialise(char *cmd_tail, int pod_base, void *pw)
{
  _kernel_oserror *err;
  _kernel_swi_regs R;

  LM_Vars.initialised = LMInitState_Uninitialised;

  (void)pod_base;
  if (pw != NULL)
  {
    LM_pw = (int)pw;
  }

  SetDefaultVars();

  R.r[0] = 129;
  R.r[1] = 0;
  R.r[2] = 255;
  _kernel_swi(OS_Byte, &R, &R);

  if (R.r[1] < 0xA2)
    return Xlt_Error(ERISCOSVER);

  if (cmd_tail != NULL)
  {
    err = Xlt_Error(ProcessCmdLine(cmd_tail));
    if (err != NULL)
      return err;
  }

  err = LM_Declare();
  if (err != NULL)
      return err;

  LM_Vars.initialised = LMInitState_PreInit;
  (void) LM_init_phase_2();
  return NULL;
}

static _kernel_oserror *LM_init_phase_2(void)
{
  /* Initialise various modules */

  enum { MAXEXITS = 4 };
  pfnShutdown shutdowns[MAXEXITS];
  int ctr = 0;
  int want_boot;
  int oldstate = LM_Vars.initialised;
  _kernel_oserror *err;


  want_boot = (LM_Vars.initialised == LMInitState_Boot);

  if (LM_Vars.verbose)
    printf("Initialising...\n");

  if (!FS_Init() || !SMB_Init()) goto initfailed;
  shutdowns[ctr++] = (pfnShutdown) SMB_Shutdown;
  if (!RPC_Init() || !Prn_Init()) goto initfailed;
  shutdowns[ctr++] = (pfnShutdown) Prn_Shutdown;
  if (!Stat_Init()) goto initfailed;

  err = Xlt_Error(Buf_Init());
  if (err != NULL) goto abort;

  shutdowns[ctr++] = (pfnShutdown) Buf_Shutdown;

  /* Try to start various modules */

  if (LM_Vars.verbose)
    printf("Starting network transport...\n");

  err = Xlt_Error( NB_Startup() );
  if (err != NULL)
  {
    debug2(    "Error - NB_Startup() returned %d (%s)\n",err->errnum,err->errmess);
    goto abort;
  }

#if 0
  if (LM_Vars.verbose)
    printf( "Starting filing system...\n");

  err = LM_Declare();
  if (err != NULL) {
          if (LM_Vars.verbose) printf("LanManFS: %s\n", err->errmess);
          NB_Shutdown();
          goto abort;
  }
#endif

  /*atexit(LM_Finalise); Somebody appears to have a lack of confidence in CMHG..? */

  /* We have now managed to fully initialise everything that we need - whee! */
  LM_Vars.initialised = LMInitState_FullyInited;
  ctr=0; /* Forget destructors */

  Omni_StartUp();  /* Try to contact OmniFiler */

  OmniS_ResourceInit();

  _kernel_oscli("IconSprites Resources:$.ThirdParty.OmniClient.LanMan.Sprites");

  /*LM_StartupBoot();*/
  if (want_boot) {
          if (LM_Vars.verbose) printf("Booting ...\n");
          LM_Boot();
  }

  if ( LM_Vars.verbose )
    printf("All done...\n");

  return NULL;

initfailed:
  err = Xlt_Error(EINITFAILED);
#ifdef DEBUG
  if (LM_Vars.verbose) {
          printf("LanManFS: %s (%s)\n", err->errmess, LM_Status());
  }
#endif
  while (--ctr >= 0) (shutdowns[ctr])();
  return err;

abort:
  LM_Vars.initialised = oldstate;
  while (--ctr >= 0) (shutdowns[ctr])();
#ifdef DEBUG
  if (LM_Vars.verbose) {
          printf("LanManFS: %s\n", LM_Status());
  }
#endif
  return NULL;

/* abort used to use this, but that assumes deterministic module startup,   *
 * which can't be assumed under DCI4, so now it just sits around waiting    *
 * for an appropriate set of service calls to wake it up.  RCE 980212       *

abort:
  LM_Finalise();
  return err;
 */
}

/* SNB 980224 added this function to verify the actual contents of the
 * Service_DCIDriverStatus service call
 */
static void LM_check_driver_status(_kernel_swi_regs *r)
{
        char if_name[16];
        dci4_dib *dib = (dci4_dib *) (r->r[0]);

        sprintf(if_name, "%-.8s%d", dib->dib_name, dib->dib_unit);

	/*
        printf("Service_DCIDriverStatus called (interface %s%d is %sing)\n"
        	"  Driver supports DCI version %d.%02d\n",
        	dib->dib_name, dib->dib_unit, r->r[2] ? "dy" : "start",
		r->r[3] / 100, r->r[3] % 100
                );

        printf("We are looking for driver `%s' - got `%s'\n", LM_Vars.drivername,
        	if_name);
        */

        if (stricmp(LM_Vars.drivername, if_name) != 0) {
                /* printf("Not interested\n"); */
                return;
        }
        else {
                /* printf("Ding!  This was our interface\n"); */
		if (r->r[2] == 0) {
		        if (LM_Vars.initialised ==  LMInitState_FullyInited) {
		                LM_Finalise(0,0,0);
		        }
	                LM_init_phase_2();
		}
		else if (r->r[2] == 1) {
	                LM_Finalise(0,0,0);
		}
        }
}


char *LM_Status(void)
{
        switch (LM_Vars.initialised) {
        case LMInitState_Uninitialised: return "Dormant";
        case LMInitState_PreInit: return "Waiting for driver";
        case LMInitState_FullyInited: return "Active";
        case LMInitState_Boot: return "Waiting to boot";
        default: return "Dead";
        }
}