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)