[comp.os.minix] Possible solution to PS/2 problems.

johnc@rtmvax.UUCP (John Connin) (01/26/89)

Regarding, "Request for advice on PS/2 problems", in message <1944@ast.cs.vu.nl>
Andy Tanenbaum writes:

[stuff deleted]
>My question is: does anybody see a better and cleaner way to do this?
>I don't especially want the code around line 1888 having to figure out
>which of the 8259A's to diddle, and which bit, etc.  For the floppy it
>may not be too bad, but for serial I/O, networking, etc, it will make
>things more complicated and error prone in a sensitive area.  On the other
>hand, I don't want a major redesign of things.  I think the original
>design is fairly simple and don't want to muck it up because of the way
>the PS/2 does things.) I'd also like to minimize the amount of code of the sort
>
>if (ps)                         /* Models 25 - 80 */
>if (mca) ...                    /* microchannel architectures only */
>if (ps && mca == FALSE) ...     /* Models 25 and 30 */
>etc.
[stuff deleted]

 I have previously posted the following code to comp.os.minix -- but
perhaps this time things are ripe.  Anyway, I believe the flag
mechanism illustrated below is a "better and cleaner" way to
process / coordinate hardware interrupts.

The following code illustrates replacement code for interrupt() in
kernel/proc.c et al.  Since the code is commented, I will let if speak for
its self other than to note:

   o The flag mechanism works well -- I have been using it successfully
     for approx 9 months.

   o It is general purpose -- I know of no interrupt situation within
     Minix where it cannot be employed.

   o The mechanism is simple:  The driver sets a preasigned flag when
     when waiting for an interrupt which inturn causes a rescheduling
     to occur.  When the interrupt occurs the ISR sets the corresponding
     flag causing the waiting process to be make ready.  If an interrupt
     occurs before a flagwait, the flagcount is incremented but no
     task is make ready.  In this case, when the flagwait does occur,
     control returns immediately since the flagcount is non-zero.
     Gee, I hope thats how it works -:)

   o A few adjustments will be required for official Minix -- in general
     my kernel is highly non-standard.

   o The interrupt service routine has some 'extras' which to do not relate
     to interrupt flags.  Specifically, interrupts are permitted to nest
     but dispatching from within the ISR is not permited if interrupts are
     nested.

   o A disclaimer.  I am not familar with the PS-2 model 80 but
     on the surface I do not see any interference with this (flag)
     mechansism.

        -----------------------------------------------------

#define  MAXFLAG  20

struct flag {
    int           flag_cnt;
    struct proc  *flag_proc;
};

struct flag  flag_tbl[ MAXFLAG];

#define isbadflag(x)   (x < 0 || x >= MAXFLAG)
#define flagaddr(x)     &flag_tbl[ x]


#define CRITICAL  int crit_save
#define C_ENTER   crit_save = lock()
#define C_LEAVE   restore(crit_save)


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

  Function:     sys_waitflag( flagno )

  Description:  System call used by a process to wait for a hardware
                interrupt.

  The calling process relinquishes the CPU until a hardware interrupt
  routine resets the designated flag via the setflag() system call.
  When waitflag returns to the calling process, either a hardware
  interrupt has occured, or an error has occured.  An error occurs
  if another process is already waiting for the flag.

  If the flag was set before the waitflag call, the system call
  returns (successfully) without waiting or relinquishing the CPU.

  If the designated flag is not set, the waitflag action is to
  remove the calling process from the ready queue and place it in
  the flag table.  The next ready process is then selected and
  restarted.

  NB.  The flag resource is reserved exclusively for use by
       hardware interrupt routines to communicated to kernel
       interrupt tasks that a logical interrupt has occuried.
       All other users of this resource will be punished.
-------------------------------------------------------------------*/

sys_waitflag(flagno,procnr)
int  flagno;
int  procnr;
{
    CRITICAL;
    struct flag  *fp;
    struct pcb   *pp;

    if (isbadflag( flagno ))   return( E_BAD_ADDR );

    /*  verify caller is indeed a kernel task  */
    if (procnr >= 0)  return( E_NO_PERM );

    /* we have an error if another process is already camping on this flag */
    if ( (fp = flagaddr(flagno))->flag_procnr)
        return( E_BAD_PROC );

    pp = ProcAddr(procnr);

    C_ENTER;

    if ( fp->flag_cnt ) {
        fp->flag_cnt    = 0;
        fp->flag_procnr = 0;
        C_LEAVE;
        return( OK );
    }

    fp->flag_procnr = procnr;


    /*  remove process from ready list, and pick the next process to run.
        Even though unready() designated a critical section we declare
        the following critical so to prevent intrusion between unready()
        and dispatch() */

    resched();

    C_LEAVE;

    return( OK );
}


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

  Function:     sys_setflag( flagno )

  Description:  System call used by interrupt routines to notify
                the system that a logical interrupt has occured.

  The process waiting for the flag (if any) is placed in the ready
  queue.  If no process is waiting, then the next process wait for
  this flag will return (successfully) without relinquishing the CPU
  (ie. the caller is not wait flaged).

  NB.  The flag resource is reserved exclusively for use by
       hardware interrupt routines to communicated to kernel
       interrupt tasks that a logical interrupt has occuried.
       All other users of this resource will be punished.
-------------------------------------------------------------------*/

sys_setflag( flagno )
int  flagno;
{
    CRITICAL;
    int  procnr;
    struct flag  *fp;

    if (isbadflag( flagno ))  return( E_BAD_ADDR );
 
    C_ENTER;

    if ((fp = flagaddr(flagno))->flag_procnr) {
        procnr = fp->flag_procnr;
        fp->flag_cnt    = 0;
        fp->flag_procnr = 0;

        /* place process on ready list. ready() is designated as a critical
           section. */
        ready(procnr);
        sysdata.disp_flag++;
    }
    else   fp->flag_cnt++;

    C_LEAVE;

    return( OK );
}


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

  Function:     sys_clrflag( flagno )

  Description:  System call to clear (initialize) a specific flag.
                If a process is waiting for the flag, the process
                is make ready.

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

sys_clrflag(flagno)
int  flagno;
{
    CRITICAL;
    struct flag  *fp;

    if (isbadflag( flagno ))   return( E_BAD_ADDR );

    C_ENTER;

    fp = flagaddr(flagno);

    if ( fp->flag_cnt ) {
        fp->flag_cnt = 0;
        if (fp->flag_procnr)
            ready(fp->flag_procnr);
        fp->flag_procnr = 0;
    }

    C_LEAVE;
    return( OK );
}


Typical driver code fragment using flags ( from floppy.c).

/*==========================================================================
                        transfer()

    The drive is now on the proper cylinder.  Read or write 1 block.
===========================================================================*/

PRIVATE int transfer(fp)
register struct floppy *fp;     /* pointer to the drive struct */
{

    int r, s, op;

    ...

    fdc_out(nr_sectors[d]);           /* tell controller how big a track is */
    fdc_out(gap[d]);                  /* tell controller how big sector gap is */
    fdc_out(DTL);                     /* tell controller about data length */
    /*  Block, waiting for disk interrupt. */

    if (need_reset)  return( ERR_TRANSFER ); /* if controller is sick, abort op */

    /*  wait for disk interrupt */
    sys_waitflag(FDC_FLAG,flpy_procnr);

    /*  Received disk interrupt, get controller status and check for errors. */
    r = fdc_results(fp);
    if (r != OK)  return (r);

    ...

}


A typical flag interrupt service routine (ISR).  In my instance the
ISR is a task, but it should be easy to modify to execute in the 
current context.

;===========================================================================
;               flpy_isr             ( task )
;===========================================================================

flpy_isr  proc near                  ; Interrupt routine for terminal input.
                                     ;
        cli                          ;
        les   di, DGROUP: _sysdata   ;
        sub   ax,ax                  ; get and protect the interrupt token
        xchg  ax, es:[di].sy_itoken  ;
        push  ax                     ;

        push  di                     ;
        push  es                     ;

        in    al, PIC_MASK           ; mask OFF the floppy interrupt
        or    al, not DISK_CHANNEL   ;
        out   PIC_MASK,al            ;

        mov   al, 20h                ; reset primary PIC and enable
        out   20h, al                ;  interrupts
        sti                          ;
                                     ;
        push  FDC_FLAG               ; set floppy flag, FDC_FLAG = 2
        call  _sys_setflag           ;
        add   sp, 2                  ;

        cli                          ;
        in   al, PIC_MASK            ; mask ON the floppy interrupt
        and  al, DISK_CHANNEL        ;
        out  PIC_MASK,al             ;

        pop  es                      ;
        pop  di                      ;

        pop  ax                      ; determine if interrupts are nested
        cmp  ax, 0                   ;   token - 0, nested
        xchg ax, es:[di].sy_itoken   ;   token - 1, not nested
        jz   flpy_int_1              ;

        cmp  es:[di].sy_dispflag,0   ; interrupts are not nested, determine
        jnz  flpy_int_2              ;   if we should dispatch a new task

flpy_int_1:                          ;
        iret                         ; return to the interrupted task
        jmp  flpy_intr               ;
                                     ; -----------------------------------
flpy_int_2:                          ; dispatch to next task
        call  _intr_disp             ;
        jmp  flpy_intr               ;
                                     ;
flpy_intr  endp

John Connin (peroa!rtmvax!johnc)