/******************************************************************************
 *
 * RCS ID
 * $Id: nmbdzm,v 1.4 2002/11/17 16:43:22 david Exp $
 *
 * HISTORY
 * $Log: nmbdzm,v $
 * Revision 1.4  2002/11/17 16:43:22  david
 * Updates to compiler with new 32-bit compiler
 *
 * Revision 1.3  2000/04/22 14:50:57  david
 * Updates for nmbd module debugging.
 * Updates for integrating nmbd with main smbserver application
 *
 * Revision 1.2  2000/04/17 16:46:29  david
 * updates to run in RISCOS MODULE
 *
 * Revision 1.1  2000/04/02 16:57:37  david
 * Updates to use the Internet event, use wimp poll event,
 * use non blocking sockets correctly,
 * compile libs in format suitable for RISCOS module
 * new nmbd module
 *
 *
 *
 *****************************************************************************/

/* Module entry points */

#include <kernel.h>
#include <stdio.h>
#include "swis.h"

#define EventV        16
#define EnableEvent   14
#define DisableEvent  13

#define InternetEvent 19    

#define Socket_Async_Event  1
#define Socket_Urgent_Event 2
#define Socket_Broken_Event 3
#define RarpReply           4

#define  TM                     "NMBD"
#define  UNKNOWN_CMD_SERVICE    4
#define  UNUSED(x)              (x = x)

static _kernel_oserror tm_error = { 0x541c0, "*** " TM " error ***" };

static int unknown_cmd_count;
static int other_service_call_count;
static int xxx;

extern int internet_event_entry(_kernel_swi_regs *r, void *pw);
extern int CallBack_Entry(_kernel_swi_regs *r, void *pw);
extern int CallBack_Entry2(_kernel_swi_regs *r, void *pw);
extern int nmbd_fd_isset(int fd);
extern void print_fds(void);
extern void print_interfaces(FILE *fp);
extern void print_all_namelists(FILE *fp);
extern void print_browse_list(FILE *fp);

extern int nmbd_entry(void);
extern void nmbd_exit(void);
extern int nmbd_main(int);

int CallBack_Handler(_kernel_swi_regs *regs,void *pw);

int eventPollWord=0;
int eventPollValue=0;
int semaphore=0;

int addingCallBack=0;

int callBackPending=0;
int oneSec=0, tenSec=0;

char *date_string = __DATE__;
char *time_string = __TIME__;
char *ver_string  = "0.01";

void AddCallBackAfter(void *pw, int delay)
{
  if (callBackPending==0)
  {
    _kernel_swi_regs ARM;   
    ARM.r[0]=delay;
    ARM.r[1]=(int)CallBack_Entry;
    ARM.r[2]=(int)pw;
    _kernel_swi(XOS_Bit|OS_CallAfter,&ARM,&ARM); 
  }
  callBackPending=1;
}

void AddCallBack(void *pw)
{

    _kernel_swi_regs ARM;  
    ARM.r[0]=(int)CallBack_Entry2;
    ARM.r[1]=(int)pw;
    _kernel_swi(XOS_Bit|OS_AddCallBack,&ARM,&ARM);
  
  addingCallBack++;
}               

void RemoveCallBack(void *pw)
{                     
  if (callBackPending==1)
  {
    _kernel_swi_regs ARM;   
    ARM.r[0]=(int)CallBack_Entry;
    ARM.r[1]=(int)pw;
    _kernel_swi(XOS_Bit|OS_RemoveTickerEvent,&ARM,&ARM);  
  }
  callBackPending=0;
}                                         

_kernel_oserror *nmbd_swi(int swi_no, _kernel_swi_regs *r, void *private_word)
{
    UNUSED(private_word);

    switch (swi_no)
    {
            
      case 0:
        r->r[0] = (int) &eventPollWord;
        r->r[1] = (int) &semaphore;  
        break;

      case 1:    
        _kernel_irqs_off();
        eventPollWord = 0;
        semaphore--;
        _kernel_irqs_on();
        break;

      case 2:
        eventPollValue = r->r[1];
        break;

      case 3:
        break;

      case 4:       
        semaphore=0;
        break;

      default:
        break;  

    }
   
    return 0;
}

_kernel_oserror *nmbd_cmd(char *arg_string, int argc, int cmd_no, void *pw)
{
/*
    char *s = arg_string;
    UNUSED(pw);
    printf("%s: '*' command %d called with %d args - arg string ",
           TM, cmd_no, argc);
    while (*s >= ' ' || *s == '\t') putchar(*s++);
    putchar('\n');                           
*/
    switch (cmd_no)
    {
       case 0:           
         printf("Netbios Name Server: Version %s %s %s\n\n", ver_string, date_string, time_string);
         printf("No of CallAfter Call Backs      %d (%d, %d)\n", eventPollWord, oneSec, tenSec);
         printf("No of Internet Event Call Backs %d\n\n", addingCallBack);  
         print_fds();
         break;

       case 1:
         print_interfaces(stdout);
         break;

       case 2:
         print_all_namelists(stdout);
         break;

       case 3:
         print_browse_list(stdout);
         break;

       default:
         break;
    }

    return 0;
}

void nmbd_service(int service_number, _kernel_swi_regs *r, void *private_word)
{
    UNUSED(r);
    UNUSED(private_word);
/*
 *  In general, it is a Bad Thing to use printf inside an arbitrary
 *  service call handler (e.g. in one that catches errors - call 6 -
 *  as this one does. Therefore we merely count what's happening and
 * print it out when the TestCModule's SWI is called.
 */
    if (service_number == UNKNOWN_CMD_SERVICE)
        ++unknown_cmd_count;
    else
        ++other_service_call_count;
}


                      
static void claim_release(int claim, void *pw)
{
  _kernel_swi_regs r;

  r.r[0] = EventV;
  r.r[1] = (int) internet_event_entry;
  r.r[2] = (int) pw;
  _kernel_swi(claim ? OS_Claim : OS_Release, &r, &r);
}                                                    

static void add_remove(int add)
{
  _kernel_swi_regs r;

  r.r[0] = add ? EnableEvent : DisableEvent;
  r.r[1] = InternetEvent;
  _kernel_swi(OS_Byte, &r, &r);
}                                     

static void claim_free_events(int claim, void *pw)
{
  if (claim)
  {
    claim_release(1, pw);
    add_remove(1);
  }
  else
  {
    add_remove(0);
    claim_release(0, pw);
  }
}                      

extern _kernel_oserror *events_init(char *cmd_tail, 
                                    int  podule_base,
                                    void *pw)
{
  UNUSED(cmd_tail);
  UNUSED(podule_base);
  claim_free_events(1,pw);
  return NULL;
}

extern _kernel_oserror *events_final(int  fatal,
                                     int  podule,
                                     void *pw)
{
  UNUSED(fatal);
  UNUSED(podule);
  claim_free_events(0,pw);
  return NULL;
}


int internet_event_handler(_kernel_swi_regs *r, void *pw)
{
  
  switch (r->r[0])
  {
    case InternetEvent:
      switch (r->r[1])
      {         
        case Socket_Async_Event:
          if (nmbd_fd_isset(r->r[2]))
          {         
            AddCallBack(pw);   
            return 0;
          }
          break;
       
        case Socket_Broken_Event:
          if (nmbd_fd_isset(r->r[2]))
          {  
            AddCallBack(pw);   
            return 0;
          }
          break;                 

        default:
          break;
      }      
      break;

    default:
      break;
  }

  return 1;
}

int nmbd_initialise(char *cmd_tail, int podule_base, void *private_word)
{
    UNUSED(cmd_tail);
    UNUSED(podule_base);
    UNUSED(private_word);
    unknown_cmd_count = other_service_call_count = 0;  

    events_init(cmd_tail, podule_base, private_word);
    
    if (nmbd_entry()==0)
    {
      CallBack_Handler(NULL, private_word);
    }
                                  
    return 0;                                          
}

int nmbd_finish(int fatal, int podule, void *pw)
{ 
              
  RemoveCallBack(pw);

  events_final(fatal, podule, pw);

  nmbd_exit();  

  return 0;
}

int CallBack_Handler(_kernel_swi_regs *regs,void *pw)
{          
    eventPollWord++;    
    callBackPending=0;

    if (nmbd_main(1)==1)
    {
        AddCallBackAfter(pw,10);
        oneSec++;
    }
    else
    {
        AddCallBackAfter(pw,1000);
        tenSec++;
    }

    return 1; /* Don't do anything */
}

int CallBack_Handler2(_kernel_swi_regs *regs,void *pw)
{   


    if (nmbd_main(0)==1)
    { 
      RemoveCallBack(pw);
      AddCallBackAfter(pw,10);
      oneSec++;
    }
 

    return 1; /* Don't do anything */
}
