bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v3 1/2] libirqhelp: user interrupt handler helper library


From: Damien Zammit
Subject: [PATCH v3 1/2] libirqhelp: user interrupt handler helper library
Date: Sun, 25 Sep 2022 04:12:27 +0000

This provides a new helper lib for registering
interrupt handlers in userspace, libirqhelp.

libirqhelp does not depend on libpciaccess.

( -1, bus, dev, fun, ...): will look up gsi from ACPI
(gsi,  -1,  -1,  -1, ...): will use given gsi

---
 Makefile             |   1 +
 libirqhelp/Makefile  |  28 ++++
 libirqhelp/irqhelp.c | 350 +++++++++++++++++++++++++++++++++++++++++++
 libirqhelp/irqhelp.h |  39 +++++
 4 files changed, 418 insertions(+)
 create mode 100644 libirqhelp/Makefile
 create mode 100644 libirqhelp/irqhelp.c
 create mode 100644 libirqhelp/irqhelp.h

diff --git a/Makefile b/Makefile
index 874349c0..cc044712 100644
--- a/Makefile
+++ b/Makefile
@@ -66,6 +66,7 @@ endif

 ifeq ($(HAVE_LIBACPICA),yes)
 prog-subdirs += acpi
+lib-subdirs += libirqhelp
 endif

 # Other directories
diff --git a/libirqhelp/Makefile b/libirqhelp/Makefile
new file mode 100644
index 00000000..c32632ab
--- /dev/null
+++ b/libirqhelp/Makefile
@@ -0,0 +1,28 @@
+# Copyright (C) 2022 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libirqhelp
+makemode := library
+
+SRCS = irqhelp.c acpiUser.c
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS =
+LDLIBS += -lpthread
+libname = libirqhelp
+installhdrs = irqhelp.h
+
+include ../Makeconf
diff --git a/libirqhelp/irqhelp.c b/libirqhelp/irqhelp.c
new file mode 100644
index 00000000..eba64cf7
--- /dev/null
+++ b/libirqhelp/irqhelp.c
@@ -0,0 +1,350 @@
+/* Library providing helper functions for userspace irq handling.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "irqhelp.h"
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <hurd.h>
+#include <hurd/paths.h>
+#include <device/notify.h>
+#include <device/device.h>
+#include "acpi_U.h"
+#include <mach.h>
+#include <stdbool.h>
+
+#define IRQ_THREAD_PRIORITY    2
+
+struct irq {
+  /* public */
+  void (*init_hook)(void *);
+  void (*pre_hook)(void *);
+  void (*post_hook)(void *);
+
+  /* private */
+  void (*handler)(void *);
+  void *context;
+  int gsi;
+  int cookie;
+  mach_port_t port;
+  bool enabled;
+  pthread_mutex_t irqlock;
+  pthread_cond_t irqcond;
+
+  LIST_ENTRY(irq) entries;
+};
+
+static LIST_HEAD(, irq) irqs = LIST_HEAD_INITIALIZER(&irqs);
+
+static mach_port_t master_host;
+static mach_port_t irqdev;
+static mach_port_t acpidev;
+static int refcnt;
+
+static inline int
+atomic_increment(int *global)
+{
+  return __atomic_add_fetch(global, 1, __ATOMIC_SEQ_CST);
+}
+
+static error_t
+get_acpi(void)
+{
+  error_t err = 0;
+  mach_port_t tryacpi, device_master;
+
+  acpidev = MACH_PORT_NULL;
+  err = get_privileged_ports (0, &device_master);
+  if (!err)
+    {
+      err = device_open (device_master, D_READ, "acpi", &tryacpi);
+      if (!err)
+        {
+         mach_port_deallocate (mach_task_self (), device_master);
+         acpidev = tryacpi;
+          return 0;
+        }
+
+      mach_port_deallocate (mach_task_self (), device_master);
+    }
+
+  tryacpi = file_name_lookup (_SERVERS_ACPI, O_RDONLY, 0);
+  if (tryacpi == MACH_PORT_NULL)
+    return ENODEV;
+
+  acpidev = tryacpi;
+  return 0;
+}
+
+static error_t
+get_irq(void)
+{
+  error_t err = 0;
+  mach_port_t tryirq, device_master;
+
+  irqdev = MACH_PORT_NULL;
+
+  err = get_privileged_ports (0, &device_master);
+  if (err)
+    return err;
+
+  err = device_open (device_master, D_READ|D_WRITE, "irq", &tryirq);
+  if (err)
+    {
+      mach_port_deallocate (mach_task_self (), device_master);
+      return err;
+    }
+
+  mach_port_deallocate (mach_task_self (), device_master);
+
+  irqdev = tryirq;
+  return err;
+}
+
+static struct irq *
+lookup_irq_structure(int gsi)
+{
+  struct irq *i;
+
+  LIST_FOREACH(i, &irqs, entries)
+    {
+      if (i->gsi == gsi)
+        return i;
+    }
+  return NULL;
+}
+
+static void
+toggle_irq(struct irq *irq, bool on)
+{
+  pthread_mutex_lock (&irq->irqlock);
+  irq->enabled = on;
+  pthread_cond_signal (&irq->irqcond);
+  pthread_mutex_unlock (&irq->irqlock);
+}
+
+void
+irqhelp_disable_irq(int gsi)
+{
+  struct irq *irq = lookup_irq_structure(gsi);
+  if (!irq)
+    return;
+
+  toggle_irq(irq, false);
+}
+
+void
+irqhelp_enable_irq(int gsi)
+{
+  struct irq *irq = lookup_irq_structure(gsi);
+  if (!irq)
+    return;
+
+  toggle_irq(irq, true);
+}
+
+void *
+irqhelp_server_loop(void *arg)
+{
+  struct irq *irq = (struct irq *)arg;
+
+  if (!irq)
+    return NULL;
+
+  /* init hook */
+  if (irq->init_hook)
+    irq->init_hook(irq->context);
+
+  int interrupt_demuxer (mach_msg_header_t *inp,
+                        mach_msg_header_t *outp)
+  {
+    device_intr_notification_t *n = (device_intr_notification_t *) inp;
+
+    ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+    if (n->intr_header.msgh_id != DEVICE_INTR_NOTIFY)
+      return 0;  /* not an interrupt */
+
+    /* FIXME: id <-> gsi now has an indirection, assuming 1:1 */
+    if (n->id != irq->gsi)
+      return 0;  /* interrupt not for us */
+
+    /* wait if irq disabled */
+    pthread_mutex_lock (&irq->irqlock);
+    while (!irq->enabled)
+      pthread_cond_wait (&irq->irqcond, &irq->irqlock);
+    pthread_mutex_unlock (&irq->irqlock);
+
+    /* pre-handler */
+    if (irq->pre_hook)
+      irq->pre_hook(irq->context);
+
+    /* call handler */
+    irq->handler(irq->context);
+
+    /* post-handler */
+    if (irq->post_hook)
+      irq->post_hook(irq->context);
+
+    /* ACK interrupt */
+    device_intr_ack (irqdev, irq->port, MACH_MSG_TYPE_MAKE_SEND);
+
+    return 1;
+  }
+
+  /* Server loop */
+  mach_msg_server (interrupt_demuxer, 0, irq->port);
+
+  return NULL;
+}
+
+static struct irq *
+interrupt_register(int gsi,
+                  int bus,
+                  int dev,
+                  int fun,
+                  void (*handler)(void *),
+                  void *context,
+                  int *cookie)
+{
+  mach_port_t delivery_port;
+  mach_port_t pset, psetcntl;
+  error_t err;
+  struct irq *irq = NULL;
+
+  irq = malloc(sizeof(struct irq));
+  if (!irq)
+    return NULL;
+
+  LIST_INSERT_HEAD(&irqs, irq, entries);
+
+  irq->handler = handler;
+  irq->context = context;
+  irq->gsi = gsi;
+  irq->init_hook = NULL;       /* can be overriden later by caller */
+  irq->pre_hook = NULL;                /* can be overriden later by caller */
+  irq->post_hook = NULL;       /* can be overriden later by caller */
+  irq->enabled = true;         /* don't require initial explicit enable */
+  pthread_mutex_init (&irq->irqlock, NULL);
+  pthread_cond_init (&irq->irqcond, NULL);
+
+  err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+                           &delivery_port);
+  if (err)
+    goto fail;
+
+  irq->port = delivery_port;
+
+  err = thread_get_assignment (mach_thread_self (), &pset);
+  if (err)
+    goto fail;
+
+  err = host_processor_set_priv (master_host, pset, &psetcntl);
+  if (err)
+    goto fail;
+
+  thread_max_priority (mach_thread_self (), psetcntl, 0);
+  err = thread_priority (mach_thread_self (), IRQ_THREAD_PRIORITY, 0);
+  if (err)
+    goto fail;
+
+  err = device_intr_register(irqdev, irq->gsi,
+                             0, irq->port,
+                             MACH_MSG_TYPE_MAKE_SEND);
+  if (err)
+    goto fail;
+
+  *cookie = irq->cookie = atomic_increment (&refcnt);
+  return irq;
+
+fail:
+  pthread_cond_destroy(&irq->irqcond);
+  pthread_mutex_destroy(&irq->irqlock);
+  free(irq);
+  return NULL;
+}
+
+
+/* Accepts gsi or bus/dev/fun or both, but cant be all -1.
+   If gsi is -1, will lookup the gsi via ACPI.
+   If bus/dev/fun are -1, must pass in gsi.
+   Accepts a pointer to a cookie to be used to
+   deregister the handler later.  */
+struct irqhelp *
+irqhelp_install_interrupt_handler(int gsi,
+                                 int bus,
+                                 int dev,
+                                 int fun,
+                                 void (*handler)(void *),
+                                 void *context,
+                                 int *cookie)
+{
+  struct irq *irq;
+  error_t err;
+
+  if (!handler)
+    return NULL;
+
+  if (!cookie)
+    return NULL;
+
+  err = get_irq();
+  if (err)
+    return NULL;
+
+  if (gsi < 0)
+    {
+      if ((bus < 0) || (dev < 0) || (fun < 0))
+        return NULL;
+
+      err = get_acpi();
+      if (err)
+        return NULL;
+
+      /* We need to call acpi translator to get gsi */
+      err = acpi_get_pci_irq (acpidev, bus, dev, fun, &gsi);
+      if (err)
+        return NULL;
+    }
+
+  err = get_privileged_ports (&master_host, 0);
+  if (err)
+    return NULL;
+
+  irq = interrupt_register(gsi, bus, dev, fun, handler, context, cookie);
+
+  mach_port_deallocate (mach_task_self (), master_host);
+  return (struct irqhelp *)irq;
+}
+
+error_t
+irqhelp_remove_interrupt_handler(int gsi,
+                                int bus,
+                                int dev,
+                                int fun,
+                                int cookie)
+{
+  /* Not implemented yet, but don't fail */
+  return 0;
+}
diff --git a/libirqhelp/irqhelp.h b/libirqhelp/irqhelp.h
new file mode 100644
index 00000000..292d912d
--- /dev/null
+++ b/libirqhelp/irqhelp.h
@@ -0,0 +1,39 @@
+/* Library providing helper functions for userspace irq handling.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _HURD_IRQHELP_
+#define _HURD_IRQHELP_
+
+#include <mach.h>
+#include <hurd/hurd_types.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+struct irqhelp {
+  void (*init_hook)(void *);
+  void (*pre_hook)(void *);
+  void (*post_hook)(void *);
+};
+
+struct irqhelp * irqhelp_install_interrupt_handler(int gsi, int bus, int dev, 
int fun,
+                                                  void (*handler)(void *), 
void *context, int *cookie);
+error_t irqhelp_remove_interrupt_handler(int gsi, int bus, int dev, int fun, 
int cookie);
+void * irqhelp_server_loop(void *arg);
+void irqhelp_enable_irq(int gsi);
+void irqhelp_disable_irq(int gsi);
+
+#endif
--
2.34.1





reply via email to

[Prev in Thread] Current Thread [Next in Thread]