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

/******************************************************************************
 *
 * RCS ID
 * $Id: CModule,v 1.6 2002/11/17 16:43:22 david Exp $
 *
 * HISTORY
 * $Log: CModule,v $
 * Revision 1.6  2002/11/17 16:43:22  david
 * Updates to compiler with new 32-bit compiler
 *
 * Revision 1.5  2001/04/20 14:39:00  david
 * Updates for 0.7a
 * Prevent index of fd outside fd array
 *
 * Revision 1.4  2000/04/23 16:49:19  david
 * Changes to attemp fix to copying large files from Windows to
 * RISCOS problem. Fixes failed!
 *
 * Revision 1.3  2000/04/22 14:51:17  david
 * Updates for nmbd module debugging.
 * Updates for integrating nmbd with main smbserver application
 *
 * Revision 1.2  2000/04/17 16:49:19  david
 * Added call back to set the poll word rather than do it in
 * the event routine
 *
 * Revision 1.1  2000/04/02 16:59:41  david
 * New cmodule for internet event and poll word changed event
 *
 *
 *
 *****************************************************************************/


extern void close_sockets(void);
extern int init_server(int portNo);

#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                     "SmbServer"
#define  UNKNOWN_CMD_SERVICE    4
#define  UNUSED(x)              (x = x)

/* TM */
/* unused kernel_oserror removed */

static int unknown_cmd_count;
static int other_service_call_count;

extern int internet_event_entry(_kernel_swi_regs *r, void *pw);
extern void fd_zero(int);
extern int fd_isset(int,int);
extern void event_fd_set(int,int);
extern void event_read_fd_set(int,int);
extern void event_fd_clear(int,int);
extern void event_read_fd_clear(int,int);
extern int smbdSockCallBack_Entry(_kernel_swi_regs *r, void *pw);
extern int nmbdSockCallBack_Entry(_kernel_swi_regs *r, void *pw);
extern int nmbdTimerCallBack_Entry(_kernel_swi_regs *r, void *pw);
extern void event_fd_clr(int whichSet, int fd);
extern void event_read_fd_clr(int whichSet, int fd);
extern void event_select(_kernel_swi_regs *r);
extern void print_fds(int);

void AddCallBackAfter(void *pw, int delay);
void RemoveCallBack(void *pw);

int eventPollWord=0;
int eventPollValue=0;
int eventType=0;
int smbdSemaphore=0;
int nmbdSemaphore=0;
int smbdSockCallBackCount=0;
int nmbdSockCallBackCount=0;
int nmbdShortTimerCallBackCount=0;
int nmbdLongTimerCallBackCount=0;

int nmbdTimerCallBackPending=0;
int lastDelay = 0;

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

    switch (swi_no)
    {

      /* get variables */
      case 0:
        r->r[0] = (int) &eventPollWord;
        r->r[1] = (int) &eventType;
        r->r[2] = (int) &smbdSemaphore;
        r->r[3] = (int) &nmbdSemaphore;
        r->r[4] = (int) &smbdSockCallBackCount;
        smbdSockCallBackCount=0;
        nmbdSockCallBackCount=0;
        nmbdShortTimerCallBackCount=0;
        nmbdLongTimerCallBackCount=0;
        break;

      /* acknowledge poll word non zero event */
      case 1:
        /* _kernel_irqs_off(); */
        eventPollWord = 0;

        if (r->r[0] & SMBD_SOCKET_EVENT) /* event being acked */
        {
          if (smbdSemaphore!=0)
             smbdSemaphore--;
          eventType &= ~SMBD_SOCKET_EVENT;
        }

        if (r->r[0] & NMBD_SOCKET_EVENT)
        {
          if (nmbdSemaphore!=0)
            nmbdSemaphore--;
          eventType &= ~NMBD_SOCKET_EVENT;
        }

        if (r->r[0] & NMBD_TIMEOUT_EVENT)
        {
          eventType &= ~NMBD_TIMEOUT_EVENT;
        }

        /* _kernel_irqs_on(); */
        break;

      /* fdset */
      case 2:
        event_fd_set(r->r[0], r->r[1]);
        eventPollValue = 1;
        break;

      /* fdclr */
      case 3:
        event_fd_clr(r->r[0], r->r[1]);
        break;

      /* fdzero */
      case 4:
        fd_zero(r->r[0]);
        if (r->r[0]==0)
          smbdSemaphore=0;
        else
          nmbdSemaphore=0;
        break;

      /* nmbd timer start */
      case 5:
        AddCallBackAfter(private_word,r->r[0]);
        break;

      /* nmbd timer stop */
      case 6:
        RemoveCallBack(private_word);
        break;

      /* smbserver select */
      case 7:
        event_select(r);
        break;

      /* fdclr */
      case 8:
        event_read_fd_clr(r->r[0], r->r[1]);
        break;

      default:
        break;

    }

    return 0;
}

_kernel_oserror *smb_cmd(char *arg_string, int argc, int cmd_no, void *pw)
{
    char *s = arg_string;
    UNUSED(pw);

    printf("SmbServer helper module. Version 0.09. %s %s\n",__DATE__,__TIME__);
    printf("Version 0.09 onwards are 32 bit compatible\n");
    printf("Address of event poll word 0x%.8x\n", &eventPollWord);
    printf("eventPollWord    0x%x\n", eventPollWord);
    printf("no of smbd socket call backs      %d\n", smbdSockCallBackCount);
    printf("no of nmbd socket call backs      %d\n", nmbdSockCallBackCount);
    printf("no of nmbd long timer call backs  %d\n",nmbdLongTimerCallBackCount);
    printf("no of nmbd short timer call backs %d\n\n",nmbdShortTimerCallBackCount);

    printf("Smbd sockets\n");
    print_fds(SMBD_SOCK_SET);
    printf("Nmbd sockets\n");
    print_fds(NMBD_SOCK_SET);

    return 0;
}

void smb_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 smbdSockCallBack_Handler(_kernel_swi_regs *regs,void *pw)
{

    eventPollWord |= eventPollValue;
    eventType |= SMBD_SOCKET_EVENT;
    smbdSemaphore++;

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

int nmbdSockCallBack_Handler(_kernel_swi_regs *regs,void *pw)
{
    RemoveCallBack(pw);

    eventPollWord |= eventPollValue;
    eventType |= NMBD_SOCKET_EVENT;
    nmbdSemaphore++;

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

int nmbdTimerCallBack_Handler(_kernel_swi_regs *regs,void *pw)
{
    eventPollWord |= eventPollValue;
    eventType |= NMBD_TIMEOUT_EVENT;

    if (lastDelay==10)
    {
      nmbdShortTimerCallBackCount++;
    }
    else
    {
      nmbdLongTimerCallBackCount++;
    }

    return 1;
}

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

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

    lastDelay = delay;

  }
  nmbdTimerCallBackPending=1;
}

void AddCallBack(void *pw, int whichOne)
{

    _kernel_swi_regs r;

    if (whichOne==SMBD_SOCK_SET)
    {
      r.r[0]=(int)smbdSockCallBack_Entry;
      smbdSockCallBackCount++;
    }
    else
    {
      r.r[0]=(int)nmbdSockCallBack_Entry;
      nmbdSockCallBackCount++;
    }

    r.r[1]=(int)pw;
    _kernel_swi(XOS_Bit|OS_AddCallBack,&r,&r);

}

int internet_event_handler(_kernel_swi_regs *r, void *pw)
{
  UNUSED(pw);

  switch (r->r[0])
  {
    case InternetEvent:
      switch (r->r[1])
      {
        case Socket_Async_Event:
          if (fd_isset(SMBD_SOCK_SET,r->r[2]))
          {
            event_read_fd_set(SMBD_SOCK_SET,r->r[2]);
            AddCallBack(pw, SMBD_SOCK_SET);
            return 0;
          }
          if (fd_isset(NMBD_SOCK_SET,r->r[2]))
          {
            event_read_fd_set(NMBD_SOCK_SET,r->r[2]);
            AddCallBack(pw, NMBD_SOCK_SET);
            return 0;
          }
          break;

        case Socket_Broken_Event:
          if (fd_isset(SMBD_SOCK_SET,r->r[2]))
          {
            event_read_fd_set(SMBD_SOCK_SET,r->r[2]);
            AddCallBack(pw, SMBD_SOCK_SET);
            return 0;
          }
          if (fd_isset(NMBD_SOCK_SET,r->r[2]))
          {
            event_read_fd_set(NMBD_SOCK_SET,r->r[2]);
            AddCallBack(pw, NMBD_SOCK_SET);
            return 0;
          }
          break;

        default:
          break;
      }
      break;


    default:
      break;
  }

  return 1;
}

int smb_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;

    fd_zero(SMBD_SOCK_SET);
    fd_zero(NMBD_SOCK_SET);

    events_init(cmd_tail, podule_base, private_word);


    return 0;
}

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

  events_final(fatal, podule, pw);

  return 0;
}
