diff options
Diffstat (limited to 'device')
-rw-r--r-- | device/ds_routines.c | 55 | ||||
-rw-r--r-- | device/ds_routines.h | 3 | ||||
-rw-r--r-- | device/intr.c | 198 |
3 files changed, 256 insertions, 0 deletions
diff --git a/device/ds_routines.c b/device/ds_routines.c index dbff7f8..4f6b345 100644 --- a/device/ds_routines.c +++ b/device/ds_routines.c @@ -318,6 +318,43 @@ ds_device_map (device_t dev, vm_prot_t prot, vm_offset_t offset, offset, size, pager, unmap); } +io_return_t +experimental_device_intr_register (ipc_port_t master_port, int line, + int id, int flags, ipc_port_t receive_port) +{ +#ifdef MACH_XEN + return D_INVALID_OPERATION; +#else /* MACH_XEN */ + extern int install_user_intr_handler (unsigned int line, + unsigned long flags, + ipc_port_t dest); + io_return_t ret; + + /* Open must be called on the master device port. */ + if (master_port != master_device_port) + return D_INVALID_OPERATION; + + /* XXX: move to arch-specific */ + if (line < 0 || line >= 16) + return D_INVALID_OPERATION; + + ret = insert_intr_entry (line, receive_port); + if (ret) + return ret; + // TODO The original port should be replaced + // when the same device driver calls it again, + // in order to handle the case that the device driver crashes and restarts. + ret = install_user_intr_handler (line, flags, receive_port); + + /* If the port is installed successfully, increase its reference by 1. + * Thus, the port won't be destroyed after its task is terminated. */ + if (ret == 0) + ip_reference (receive_port); + + return ret; +#endif /* MACH_XEN */ +} + boolean_t ds_notify (mach_msg_header_t *msg) { @@ -1798,6 +1835,24 @@ device_writev_trap (mach_device_t device, dev_mode_t mode, return (result); } +kern_return_t +experimental_device_intr_enable(ipc_port_t master_port, int line, char status) +{ +#ifdef MACH_XEN + return D_INVALID_OPERATION; +#else /* MACH_XEN */ + if (master_port != master_device_port) + return D_INVALID_OPERATION; + + if (status) + /* TODO: better name for generic-to-arch-specific call */ + enable_irq (line); + else + disable_irq (line); + return 0; +#endif /* MACH_XEN */ +} + struct device_emulation_ops mach_device_emulation_ops = { (void*) mach_device_reference, diff --git a/device/ds_routines.h b/device/ds_routines.h index c0543cb..e9f115f 100644 --- a/device/ds_routines.h +++ b/device/ds_routines.h @@ -83,4 +83,7 @@ io_return_t ds_device_writev_trap( io_buf_vec_t *iovec, vm_size_t count); +/* XXX arch-specific */ +extern ipc_port_t intr_rcv_ports[16]; + #endif /* DS_ROUTINES_H */ diff --git a/device/intr.c b/device/intr.c new file mode 100644 index 0000000..6fca328 --- /dev/null +++ b/device/intr.c @@ -0,0 +1,198 @@ +#include <device/intr.h> +#include <device/ds_routines.h> +#include <kern/queue.h> +#include <kern/printf.h> + +#ifndef MACH_XEN +// TODO this is only for x86 system +#define sti() __asm__ __volatile__ ("sti": : :"memory") +#define cli() __asm__ __volatile__ ("cli": : :"memory") + +static boolean_t deliver_intr (int line, ipc_port_t dest_port); + +struct intr_entry +{ + queue_chain_t chain; + ipc_port_t dest; + int line; + /* The number of interrupts occur since last run of intr_thread. */ + int interrupts; +}; + +static queue_head_t intr_queue; +/* The total number of unprocessed interrupts. */ +static int tot_num_intr; + +static struct intr_entry * +search_intr (int line, ipc_port_t dest) +{ + struct intr_entry *e; + queue_iterate (&intr_queue, e, struct intr_entry *, chain) + { + if (e->dest == dest && e->line == line) + return e; + } + return NULL; +} + +/* This function can only be used in the interrupt handler. */ +void +queue_intr (int line, ipc_port_t dest) +{ + extern void intr_thread (); + struct intr_entry *e; + + cli (); + e = search_intr (line, dest); + assert (e); + e->interrupts++; + tot_num_intr++; + sti (); + + thread_wakeup ((event_t) &intr_thread); +} + +/* insert an interrupt entry in the queue. + * This entry exists in the queue until + * the corresponding interrupt port is removed.*/ +int +insert_intr_entry (int line, ipc_port_t dest) +{ + int err = 0; + struct intr_entry *e, *new; + int free = 0; + + new = (struct intr_entry *) kalloc (sizeof (*new)); + if (new == NULL) + return D_NO_MEMORY; + + /* check whether the intr entry has been in the queue. */ + cli (); + e = search_intr (line, dest); + if (e) + { + printf ("the interrupt entry for line %d and port %p has been inserted\n", + line, dest); + free = 1; + err = D_ALREADY_OPEN; + goto out; + } + new->line = line; + new->dest = dest; + new->interrupts = 0; + queue_enter (&intr_queue, new, struct intr_entry *, chain); +out: + sti (); + if (free) + kfree ((vm_offset_t) new, sizeof (*new)); + return err; +} + +/* this function should be called when line is disabled. */ +void mark_intr_removed (int line, ipc_port_t dest) +{ + struct intr_entry *e; + + e = search_intr (line, dest); + if (e) + e->dest = NULL; +} + +void +intr_thread () +{ + struct intr_entry *e; + int line; + ipc_port_t dest; + queue_init (&intr_queue); + + for (;;) + { + assert_wait ((event_t) &intr_thread, FALSE); + cli (); + while (tot_num_intr) + { + int del = 0; + + queue_iterate (&intr_queue, e, struct intr_entry *, chain) + { + /* if an entry doesn't have dest port, + * we should remove it. */ + if (e->dest == NULL) + { + del = 1; + break; + } + + if (e->interrupts) + { + line = e->line; + dest = e->dest; + e->interrupts--; + tot_num_intr--; + + sti (); + deliver_intr (line, dest); + cli (); + } + } + + /* remove the entry without dest port from the queue and free it. */ + if (del) + { + assert (!queue_empty (&intr_queue)); + queue_remove (&intr_queue, e, struct intr_entry *, chain); + sti (); + kfree ((vm_offset_t) e, sizeof (*e)); + cli (); + } + } + sti (); + thread_block (NULL); + } +} + +static boolean_t +deliver_intr (int line, ipc_port_t dest_port) +{ + ipc_kmsg_t kmsg; + mach_intr_notification_t *n; + mach_port_t dest = (mach_port_t) dest_port; + + if (dest == MACH_PORT_NULL) + return FALSE; + + kmsg = ikm_alloc(sizeof *n); + if (kmsg == IKM_NULL) + return FALSE; + + ikm_init(kmsg, sizeof *n); + n = (mach_intr_notification_t *) &kmsg->ikm_header; + + mach_msg_header_t *m = &n->intr_header; + mach_msg_type_t *t = &n->intr_type; + + m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0); + m->msgh_size = sizeof *n; + m->msgh_seqno = INTR_NOTIFY_MSGH_SEQNO; + m->msgh_local_port = MACH_PORT_NULL; + m->msgh_remote_port = MACH_PORT_NULL; + m->msgh_id = MACH_INTR_NOTIFY; + + t->msgt_name = MACH_MSG_TYPE_INTEGER_32; + t->msgt_size = 32; + t->msgt_number = 1; + t->msgt_inline = TRUE; + t->msgt_longform = FALSE; + t->msgt_deallocate = FALSE; + t->msgt_unused = 0; + + n->intr_header.msgh_remote_port = dest; + n->line = line; + + ipc_port_copy_send (dest_port); + ipc_mqueue_send_always(kmsg); + + return TRUE; +} +#endif /* MACH_XEN */ |