106 lines
4.1 KiB
C
106 lines
4.1 KiB
C
/* The system call implemented in this file:
|
|
* m_type: SYS_IRQCTL
|
|
*
|
|
* The parameters for this system call are:
|
|
* m5_c1: IRQ_REQUEST (control operation to perform)
|
|
* m5_c2: IRQ_VECTOR (irq line that must be controlled)
|
|
* m5_i1: IRQ_POLICY (flags to control the IRQCTL request)
|
|
* m5_i2: IRQ_PROC_NR (process number to notify)
|
|
* m5_l1: IRQ_PORT (port to write to / read from)
|
|
* m5_l2: IRQ_VIR_ADDR (virtual address at caller)
|
|
* m5_l3: IRQ_MASK_VAL (value to be written or strobe mask)
|
|
*
|
|
* Author:
|
|
* Jorrit N. Herder <jnherder@cs.vu.nl>
|
|
*/
|
|
|
|
#include "../kernel.h"
|
|
#include "../system.h"
|
|
|
|
|
|
/*===========================================================================*
|
|
* do_irqctl *
|
|
*===========================================================================*/
|
|
PUBLIC int do_irqctl(m_ptr)
|
|
register message *m_ptr; /* pointer to request message */
|
|
{
|
|
/* Dismember the request message. */
|
|
int irq = m_ptr->IRQ_VECTOR; /* which IRQ vector */
|
|
int policy = m_ptr->IRQ_POLICY; /* policy field with flags */
|
|
long port = m_ptr->IRQ_PORT; /* port to read or write */
|
|
vir_bytes vir_addr = m_ptr->IRQ_VIR_ADDR; /* address at caller */
|
|
phys_bytes phys_addr = 0; /* calculate physical address */
|
|
long mask_val = m_ptr->IRQ_MASK_VAL; /* mask or value to be written */
|
|
int proc_nr = m_ptr->IRQ_PROC_NR; /* process number to forward to */
|
|
|
|
/* Check if IRQ line is acceptable. */
|
|
if ((unsigned) irq >= NR_IRQ_VECTORS) {
|
|
kprintf("ST: irq line %d is not acceptable!\n", irq);
|
|
return(EINVAL);
|
|
}
|
|
|
|
/* See what is requested and take needed actions. */
|
|
switch(m_ptr->IRQ_REQUEST) {
|
|
|
|
/* Enable or disable IRQs. This is straightforward. */
|
|
case IRQ_ENABLE: {
|
|
enable_irq(&irqtab[irq].hook);
|
|
break;
|
|
}
|
|
case IRQ_DISABLE: {
|
|
disable_irq(&irqtab[irq].hook);
|
|
break;
|
|
}
|
|
|
|
/* Control IRQ policies. Set a policy and needed details in the IRQ table.
|
|
* This policy is used by a generic function to handle hardware interrupts.
|
|
* The generic_handler() is contained in system.c.
|
|
*/
|
|
case IRQ_SETPOLICY: {
|
|
|
|
if (proc_nr == NONE) { /* remove irqtab entry */
|
|
if (irqtab[irq].proc_nr != m_ptr->m_source) {
|
|
return(EPERM); /* only owner may do so */
|
|
}
|
|
kprintf("ST: notify: cannot remove entry for IRQ %d\n",irq);
|
|
return(ENOSYS); /* not yet supported */
|
|
}
|
|
else { /* install generic handler */
|
|
if (irqtab[irq].proc_nr != NONE) { /* IRQ entry already taken */
|
|
kprintf("ST: notify: slot for IRQ %d already taken\n", irq);
|
|
return(EBUSY); /* cannot overwrite entry */
|
|
}
|
|
if (proc_nr == SELF) /* check for magic proc nr */
|
|
proc_nr = m_ptr->m_source; /* set caller's proc nr */
|
|
if (! isokprocn(proc_nr)) { /* check if proc nr is ok */
|
|
kprintf("ST: notify: invalid proc_nr: %d\n", proc_nr);
|
|
return(EINVAL);
|
|
}
|
|
if (policy & IRQ_READ_PORT) { /* get phys_addr at caller */
|
|
switch(policy & (IRQ_BYTE|IRQ_WORD|IRQ_LONG)) {
|
|
case IRQ_BYTE: phys_addr=numap_local(proc_nr,vir_addr,sizeof( u8_t));
|
|
break;
|
|
case IRQ_WORD: phys_addr=numap_local(proc_nr,vir_addr,sizeof(u16_t));
|
|
break;
|
|
case IRQ_LONG: phys_addr=numap_local(proc_nr,vir_addr,sizeof(u32_t));
|
|
break;
|
|
default: return(EINVAL); /* wrong type flags */
|
|
}
|
|
if (phys_addr==0) return(EFAULT); /* invalid address */
|
|
}
|
|
/* Arguments seem to be OK, register them in the IRQ table. */
|
|
irqtab[irq].policy = policy; /* policy for interrupts */
|
|
irqtab[irq].proc_nr = proc_nr; /* process number to notify */
|
|
irqtab[irq].port = port; /* port to read or write */
|
|
irqtab[irq].addr = phys_addr; /* address to store status */
|
|
irqtab[irq].mask_val = mask_val; /* strobe mask or value */
|
|
put_irq_handler(&irqtab[irq].hook, irq, generic_handler);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
return(EINVAL); /* invalid IRQ_REQUEST */
|
|
}
|
|
return(OK);
|
|
}
|
|
|