summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/i386/i386/elan-mmcr.c159
1 files changed, 136 insertions, 23 deletions
diff --git a/sys/i386/i386/elan-mmcr.c b/sys/i386/i386/elan-mmcr.c
index cea33e9..3c60bdb 100644
--- a/sys/i386/i386/elan-mmcr.c
+++ b/sys/i386/i386/elan-mmcr.c
@@ -26,6 +26,10 @@
#include <sys/sysctl.h>
#include <sys/timetc.h>
#include <sys/proc.h>
+#include <sys/uio.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/malloc.h>
#include <machine/md_var.h>
@@ -34,6 +38,11 @@
uint16_t *elan_mmcr;
+/* Relating to the /dev/soekris-errled */
+static struct mtx errled_mtx;
+static char *errled;
+static struct callout_handle errled_h = CALLOUT_HANDLE_INITIALIZER(&errled_h);
+static void timeout_errled(void *);
static unsigned
elan_get_timecount(struct timecounter *tc)
@@ -84,17 +93,19 @@ init_AMD_Elan_sc520(void)
* Device driver initialization stuff
*/
-static d_open_t elan_open;
-static d_close_t elan_close;
+static d_write_t elan_write;
static d_ioctl_t elan_ioctl;
static d_mmap_t elan_mmap;
+#define ELAN_MMCR 0
+#define ELAN_ERRLED 1
+
#define CDEV_MAJOR 100 /* Share with xrpu */
static struct cdevsw elan_cdevsw = {
- /* open */ elan_open,
- /* close */ elan_close,
+ /* open */ nullopen,
+ /* close */ nullclose,
/* read */ noread,
- /* write */ nowrite,
+ /* write */ elan_write,
/* ioctl */ elan_ioctl,
/* poll */ nopoll,
/* mmap */ elan_mmap,
@@ -106,21 +117,135 @@ static struct cdevsw elan_cdevsw = {
/* flags */ 0,
};
-static int
-elan_open(dev_t dev, int flag, int mode, struct thread *td)
+static void
+elan_drvinit(void)
{
- return (0);
+
+ if (elan_mmcr == NULL)
+ return;
+ printf("Elan-mmcr driver: MMCR at %p\n", elan_mmcr);
+ make_dev(&elan_cdevsw, ELAN_MMCR,
+ UID_ROOT, GID_WHEEL, 0600, "elan-mmcr");
+ make_dev(&elan_cdevsw, ELAN_ERRLED,
+ UID_ROOT, GID_WHEEL, 0600, "soekris-errled");
+ mtx_init(&errled_mtx, "Elan-errled", MTX_DEF, 0);
+ return;
}
+SYSINIT(elan, SI_SUB_PSEUDO, SI_ORDER_MIDDLE+CDEV_MAJOR,elan_drvinit,NULL);
+
+#define LED_ON() do {elan_mmcr[0xc34 / 2] = 0x200;} while(0)
+#define LED_OFF() do {elan_mmcr[0xc38 / 2] = 0x200;} while(0)
+
+static void
+timeout_errled(void *p)
+{
+ static enum {NOTHING, FLASH, DIGIT} mode;
+ static int count, cnt2, state;
+
+ mtx_lock(&errled_mtx);
+ if (p != NULL) {
+ mode = NOTHING;
+ /* Our instructions changed */
+ if (*errled == '1') { /* Turn LED on */
+ LED_ON();
+ } else if (*errled == '0') { /* Turn LED off */
+ LED_OFF();
+ } else if (*errled == 'f') { /* Flash */
+ mode = FLASH;
+ cnt2 = 10;
+ if (errled[1] >= '1' && errled[1] <= '9')
+ cnt2 = errled[1] - '0';
+ cnt2 = hz / cnt2;
+ LED_ON();
+ errled_h = timeout(timeout_errled, NULL, cnt2);
+ } else if (*errled == 'd') { /* Digit */
+ mode = DIGIT;
+ count = 0;
+ cnt2 = 0;
+ state = 0;
+ LED_OFF();
+ errled_h = timeout(timeout_errled, NULL, hz/10);
+ }
+ } else if (mode == FLASH) {
+ if (count)
+ LED_ON();
+ else
+ LED_OFF();
+ count = !count;
+ errled_h = timeout(timeout_errled, NULL, cnt2);
+ } else if (mode == DIGIT) {
+ if (cnt2 > 0) {
+ if (state) {
+ LED_OFF();
+ state = 0;
+ cnt2--;
+ } else {
+ LED_ON();
+ state = 1;
+ }
+ errled_h = timeout(timeout_errled, NULL, hz/5);
+ } else {
+ do
+ count++;
+ while (errled[count] != '\0' &&
+ (errled[count] < '0' || errled[count] > '9'));
+ if (errled[count] == '\0') {
+ count = 0;
+ errled_h = timeout(timeout_errled, NULL, hz * 2);
+ } else {
+ cnt2 = errled[count] - '0';
+ state = 0;
+ errled_h = timeout(timeout_errled, NULL, hz);
+ }
+ }
+ }
+ mtx_unlock(&errled_mtx);
+ return;
+}
+
+/*
+ * The write function is used for the error-LED.
+ */
+
static int
-elan_close(dev_t dev, int flag, int mode, struct thread *td)
-{
- return (0);
+elan_write(dev_t dev, struct uio *uio, int ioflag)
+{
+ int error;
+ char *s, *q;
+
+ if (minor(dev) != ELAN_ERRLED)
+ return (EOPNOTSUPP);
+
+ if (uio->uio_resid > 512)
+ return (EINVAL);
+ s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK);
+ if (s == NULL)
+ return (ENOMEM);
+ untimeout(timeout_errled, NULL, errled_h);
+ s[uio->uio_resid] = '\0';
+ error = uiomove(s, uio->uio_resid, uio);
+ if (error) {
+ free(s, M_DEVBUF);
+ return (error);
+ }
+ mtx_lock(&errled_mtx);
+ q = errled;
+ errled = s;
+ mtx_unlock(&errled_mtx);
+ if (q != NULL)
+ free(q, M_DEVBUF);
+ timeout_errled(errled);
+
+ return(0);
}
static int
elan_mmap(dev_t dev, vm_offset_t offset, int nprot)
{
+
+ if (minor(dev) != ELAN_MMCR)
+ return (EOPNOTSUPP);
if (offset >= 0x1000)
return (-1);
return (i386_btop(0xfffef000));
@@ -132,15 +257,3 @@ elan_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct thread *tdr)
return(ENOENT);
}
-static void
-elan_drvinit(void)
-{
-
- if (elan_mmcr == NULL)
- return;
- printf("Elan-mmcr driver: MMCR at %p\n", elan_mmcr);
- make_dev(&elan_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "elan-mmcr");
- return;
-}
-
-SYSINIT(elan, SI_SUB_PSEUDO, SI_ORDER_MIDDLE+CDEV_MAJOR,elan_drvinit,NULL);
OpenPOWER on IntegriCloud