[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH-2] A new irq device interface
From: |
Junling Ma |
Subject: |
[PATCH-2] A new irq device interface |
Date: |
Sat, 1 Aug 2020 19:01:56 -0700 |
Hi all,
In this patch, the new interface of irq device is implemented. Please see the
first patch for a description.
Junling Ma
---
device/ds_routines.c | 2 +-
device/intr.c | 184 +++++++++++++++++++++++++++++++++----------
device/intr.h | 11 ++-
i386/i386at/conf.c | 4 +-
4 files changed, 154 insertions(+), 47 deletions(-)
diff --git a/device/ds_routines.c b/device/ds_routines.c
index 91787950..333b66c9 100644
--- a/device/ds_routines.c
+++ b/device/ds_routines.c
@@ -345,7 +345,7 @@ ds_device_intr_register (device_t dev, int id,
// TODO detect when the port get destroyed because the driver crashes and
// restart, to replace it when the same device driver calls it again.
- err = install_user_intr_handler (id, receive_port);
+ err = install_user_intr_handler (id, receive_port, mdev, FALSE);
if (err == D_SUCCESS)
{
/* If the port is installed successfully, increase its reference by 1.
diff --git a/device/intr.c b/device/intr.c
index 682b12d7..e78f2193 100644
--- a/device/intr.c
+++ b/device/intr.c
@@ -22,6 +22,7 @@
#include <ipc/ipc_space.h>
#include "io_req.h"
#include "ds_routines.h"
+#include "device.server.h"
#ifndef MACH_XEN
@@ -70,16 +71,20 @@ kern_return_t
irq_acknowledge (ipc_port_t receive_port)
{
user_intr_t *e;
+ user_intr_notification_t n = NULL;
unsigned id;
for (id = 0; id < NINTR; ++id)
{
e = irqtab.handler[id];
- if (e && search_notification (e, receive_port))
+ if (e && (n = search_notification (e, receive_port)) !=
NULL)
break;
}
- if (id == NINTR)
+ if (id == NINTR || n->device_port)
return D_INVALID_OPERATION;
-
+ /* cannot ack twice */
+ if (n->acked)
+ return D_INVALID_OPERATION;
+ n->acked = TRUE;
kern_return_t ret = D_SUCCESS;
PROTECT(e->lock,
{
@@ -105,16 +110,7 @@ deliver_user_intr (int id, void *dev_id, struct pt_regs
*regs)
__disable_irq(id);
}
});
- if (!e) return;
- if (queue_empty(&e->notification_queue))
- {
- irqtab.handler[id] = NULL;
- if (e->n_unacked)
- __enable_irq (id);
- free_irq(id, &irqtab);
- e = NULL;
- }
- else if (e->interrupts)
+ if (e && !queue_empty(&e->notification_queue) && e->interrupts)
thread_wakeup ((event_t) &intr_thread);
}
@@ -137,43 +133,65 @@ intr_thread (void)
user_intr_t *e =
irqtab.handler[id];
if (!e) continue;
clear_wait
(current_thread (), 0, 0);
-
user_intr_notification_t next;
-
user_intr_notification_t n;
- /* go through each
notification to fire or remove dead ones */
+
user_intr_notification_t next, n;
+ /* go through each
notification to fire interrupts or remove dead ones */
PROTECT(e->lock,
{
+
boolean_t delivered = FALSE;
queue_iterate (&e->notification_queue, n, user_intr_notification_t, chain)
{
-
/* is the notification port dead? */
-
if (n->dst_port->ip_references == 1)
+
/* check for dead port */
+
while (n && n->dst_port->ip_references == 1)
{
/* dead, move it to the dead queue */
+
printf("dead port: %p\n", n->dst_port);
next = (user_intr_notification_t)queue_next(&n->chain);
queue_remove(&e->notification_queue, n,
user_intr_notification_t, chain);
-
ipc_port_release (n->dst_port);
-
kfree((vm_offset_t)n, sizeof(*n));
-
--e->n_unacked;
-
if (e->n_unacked == 0)
-
__enable_irq(id);
-
printf("dead notification port %p released \n",
n->dst_port);
-
if (queue_empty(&e->notification_queue))
+
//ipc_port_release (n->dst_port);
+
if (n->request)
+
io_req_free(n->request);
+
/* if waiting for acking, ack it */
+
if (!n->acked && e->n_unacked)
{
-
printf("irq %d has no registered
notifications. remove\n", id);
-
kfree((vm_offset_t)e, sizeof(*e));
-
irqtab.handler[id] = NULL;
-
break;
+
--e->n_unacked;
+
if (e->n_unacked == 0)
+
__enable_irq(id);
}
+
kfree((vm_offset_t)n, sizeof(*n));
+
ds_device_close(&n->device->dev);
n = next;
}
-
else if (e->interrupts)
+
if (!n) break;
+
/* if there is a DEVICE, the notification uses the new interface */
+
/* the notification uses the old interface. is the notification port
dead? */
+
if (!e->interrupts)
+
continue;
+
if (!n->device_port && !deliver_intr(id, n->dst_port))
+
continue; /* failed to delivery using the notification port */
+
if (n->device_port)
{
-
SPLHIGH(e->interrupts--);
-
total_interrupts += e->interrupts;
-
if (deliver_intr(id, n->dst_port))
-
/* n_unacked is increased when firing. Without
firing, there is no ack */
-
e->n_unacked++;
+
if (!n->request)
+
continue;
+
ds_read_done(n->request);
+
io_req_free(n->request);
}
+
n->request = NULL;
+
e->n_unacked++;
+
n->acked = FALSE;
+
delivered = TRUE;
} /* end of queue_iterate */
+ if
(delivered) /* if successfully delivered */
+
{
+
SPLHIGH(e->interrupts--);
+
total_interrupts += e->interrupts;
+
}
+ if
(queue_empty(&e->notification_queue))
+
{
+
printf("there is no registered handler for irq %d. close the device\n",
id);
+
free_irq(id, &irqtab);
+
SPLHIGH(irqtab.handler[id] = NULL);
+
kfree((vm_offset_t)e, sizeof(*e));
+
}
}); /* end of
PROTECT */
}
}
@@ -226,9 +244,9 @@ deliver_intr (int id, ipc_port_t dst_port)
}
int
-install_user_intr_handler (int id, ipc_port_t dst_port)
+install_user_intr_handler (int id, ipc_port_t dst_port, mach_device_t device,
boolean_t device_port)
{
- if (id > NINTR || dst_port == NULL)
+ if (id > NINTR || device == NULL)
return D_INVALID_OPERATION;
user_intr_t *e = irqtab.handler[id];
if (e == NULL) {
@@ -243,7 +261,7 @@ install_user_intr_handler (int id, ipc_port_t dst_port)
/* install the new handler */
kern_return_t r = request_irq (id, deliver_user_intr, SA_SHIRQ,
NULL, &irqtab);
if (r) {
- printf("could not register irq handler: %d(%08x)\n", r,
r);
+ printf("could not register irq handler for irq %d:
%d(%08x)\n", id, r, r);
irqtab.handler[id] = NULL;
kfree((vm_size_t)e, sizeof(*e));
return r;
@@ -253,18 +271,20 @@ install_user_intr_handler (int id, ipc_port_t dst_port)
/* check whether the intr entry has been in the queue. */
user_intr_notification_t n = search_notification (e, dst_port);
if (n)
- {
- printf ("the interrupt entry for irq %d and port %p has already been
inserted\n", id, dst_port);
- return D_ALREADY_OPEN;
- }
+ return D_ALREADY_OPEN;
n = (user_intr_notification_t) kalloc (sizeof (*n));
if (n == NULL)
return D_NO_MEMORY;
n->dst_port = dst_port;
+ ipc_port_reference(dst_port);
+ n->device = device;
+ n->device_port = device_port;
+ n->request = NULL;
+ n->acked = TRUE;
PROTECT(e->lock, queue_enter (&e->notification_queue, n,
user_intr_notification_t, chain));
- printf("irq handler [%d]: new delivery port %p\n", id, dst_port);
+ printf("notification port %p register for irq %d\n", dst_port, id);
return D_SUCCESS;
}
@@ -275,4 +295,82 @@ void irq_init()
irqtab.handler[i] = NULL;
}
+extern int irqopen(dev_t dev, int flag, io_req_t ior)
+{
+ int id = minor(dev);
+ /* for now, we need to handle opening "irq", which is the same as irq0
+ this is for backward compatibility. */
+ if (id == 0)
+ return D_SUCCESS;
+ if (id > NINTR)
+ return D_NO_SUCH_DEVICE;
+ user_intr_t *e = irqtab.handler[id];
+ if (e)
+ return D_SUCCESS;
+ ior->io_error = D_SUCCESS;
+ return install_user_intr_handler (id, ior->io_reply_port,
ior->io_device, TRUE);
+}
+
+void irqclose(dev_t dev, int flags)
+{
+ /* device closes are handled by port death detection */
+}
+
+extern int irqread(dev_t dev, io_req_t ior)
+{
+ irq_t id = minor(dev);
+ if (id > NINTR)
+ return D_NO_SUCH_DEVICE;
+ user_intr_t *e = irqtab.handler[id];
+ user_intr_notification_t n = NULL;
+ if (e)
+ n = search_notification(e, ior->io_reply_port);
+ if (!n)
+ {
+ int err = install_user_intr_handler(id, ior->io_reply_port,
ior->io_device, TRUE);
+ if (err)
+ return err;
+ e = irqtab.handler[id];
+ n = search_notification(e, ior->io_reply_port);
+ }
+ /* already requested */
+ if (n->request || !n->acked)
+ return D_INVALID_OPERATION;
+ n->request = ior;
+ ior->io_error = D_SUCCESS;
+ ior->io_residual = ior->io_count;
+ return D_IO_QUEUED;
+}
+
+extern int irqwrite(dev_t dev, io_req_t ior)
+{
+ irq_t id = minor(dev);
+ if (id > NINTR)
+ return D_NO_SUCH_DEVICE;
+ user_intr_t *e = irqtab.handler[id];
+ if (e == NULL)
+ return D_DEVICE_DOWN;
+ if (e->n_unacked == 0)
+ return D_INVALID_OPERATION;
+ user_intr_notification_t n = search_notification(e, ior->io_reply_port);
+ if (!n || n->request || n->acked)
+ return D_INVALID_OPERATION;
+ n->acked = TRUE;
+ PROTECT(e->lock,
+ {
+ e->n_unacked--;
+ if (e->n_unacked == 0)
+ __enable_irq(id);
+ });
+ boolean_t wait = FALSE;
+ int rc = device_write_get(ior, &wait);
+ if (rc != KERN_SUCCESS)
+ return rc;
+ if (wait)
+ return D_INVALID_OPERATION;
+ ior->io_residual = ior->io_count;
+ ior->io_error = D_SUCCESS;
+ return D_SUCCESS;
+}
+
#endif /* MACH_XEN */
diff --git a/device/intr.h b/device/intr.h
index 90bc6f4c..88b40516 100644
--- a/device/intr.h
+++ b/device/intr.h
@@ -33,7 +33,11 @@ struct irqdev;
/* a struct to hold notifications */
struct user_intr_notification {
queue_chain_t chain;
+ io_req_t request;
+ boolean_t acked;
+ mach_device_t device;
ipc_port_t dst_port; /* Notification port */
+ boolean_t device_port; /* whether dst_port is a device port, i.e, using the
new interface */
};
typedef struct user_intr_notification * user_intr_notification_t;
@@ -49,12 +53,17 @@ struct irqdev {
user_intr_t *handler[NINTR]; /* irq handlers for device_open */
};
-extern int install_user_intr_handler (int id, ipc_port_t dst_port);
+extern int install_user_intr_handler (int id, ipc_port_t dst_port,
mach_device_t device, boolean_t device_port);
extern void irq_init();
void intr_thread (void);
kern_return_t irq_acknowledge (ipc_port_t receive_port);
+extern int irqopen(dev_t dev, int flag, io_req_t ior);
+extern void irqclose(dev_t dev, int flags);
+extern int irqread(dev_t dev, io_req_t ior);
+extern int irqwrite(dev_t dev, io_req_t ior);
+
#endif /* MACH_XEN */
#endif
diff --git a/i386/i386at/conf.c b/i386/i386at/conf.c
index ca5d0dfb..69f12ae2 100644
--- a/i386/i386at/conf.c
+++ b/i386/i386at/conf.c
@@ -152,8 +152,8 @@ struct dev_ops dev_name_list[] =
nodev },
#endif /* MACH_HYP */
- { irqname, nulldev_open, nulldev_close, nulldev_read,
- nulldev_write,nulldev_getstat,nulldev_setstat, nomap,
+ { irqname, irqopen, irqclose, irqread,
+ irqwrite,nulldev_getstat,nulldev_setstat, nomap,
nodev, nulldev, nulldev_portdeath,0,
nodev },
--
2.28.0.rc1
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [PATCH-2] A new irq device interface,
Junling Ma <=