aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/staging/Kconfig2
-rw-r--r--drivers/staging/Makefile1
-rw-r--r--drivers/staging/unisys/Kconfig14
-rw-r--r--drivers/staging/unisys/Makefile5
-rw-r--r--drivers/staging/unisys/include/periodic_work.h40
-rw-r--r--drivers/staging/unisys/include/procobjecttree.h48
-rw-r--r--drivers/staging/unisys/include/timskmod.h558
-rw-r--r--drivers/staging/unisys/include/timskmodutils.h194
-rw-r--r--drivers/staging/unisys/include/uniklog.h193
-rw-r--r--drivers/staging/unisys/visorutil/Kconfig10
-rw-r--r--drivers/staging/unisys/visorutil/Makefile11
-rw-r--r--drivers/staging/unisys/visorutil/charqueue.c144
-rw-r--r--drivers/staging/unisys/visorutil/charqueue.h37
-rw-r--r--drivers/staging/unisys/visorutil/easyproc.c365
-rw-r--r--drivers/staging/unisys/visorutil/easyproc.h86
-rw-r--r--drivers/staging/unisys/visorutil/memregion.h43
-rw-r--r--drivers/staging/unisys/visorutil/memregion_direct.c221
-rw-r--r--drivers/staging/unisys/visorutil/periodic_work.c230
-rw-r--r--drivers/staging/unisys/visorutil/procobjecttree.c334
-rw-r--r--drivers/staging/unisys/visorutil/visorkmodutils.c721
20 files changed, 3257 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index d729978cc6e..47cf1754300 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -144,4 +144,6 @@ source "drivers/staging/gs_fpgaboot/Kconfig"
source "drivers/staging/nokia_h4p/Kconfig"
+source "drivers/staging/unisys/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 9eb4e8a7d13..d12f6189db4 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -64,3 +64,4 @@ obj-$(CONFIG_DGAP) += dgap/
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/
obj-$(CONFIG_BT_NOKIA_H4P) += nokia_h4p/
+obj-$(CONFIG_UNISYSSPAR) += unisys/
diff --git a/drivers/staging/unisys/Kconfig b/drivers/staging/unisys/Kconfig
new file mode 100644
index 00000000000..60bbca0bde7
--- /dev/null
+++ b/drivers/staging/unisys/Kconfig
@@ -0,0 +1,14 @@
+#
+# Unisys SPAR driver configuration
+#
+menuconfig UNISYSSPAR
+ bool "Unisys SPAR driver support"
+ depends on X86_64
+ ---help---
+ Support for the Unisys SPAR drivers
+
+if UNISYSSPAR
+
+source "drivers/staging/unisys/visorutil/Kconfig"
+
+endif # UNISYSSPAR
diff --git a/drivers/staging/unisys/Makefile b/drivers/staging/unisys/Makefile
new file mode 100644
index 00000000000..129c28fb50f
--- /dev/null
+++ b/drivers/staging/unisys/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for Unisys SPAR drivers
+#
+obj-$(CONFIG_UNISYS_VISORUTIL) += visorutil/
+
diff --git a/drivers/staging/unisys/include/periodic_work.h b/drivers/staging/unisys/include/periodic_work.h
new file mode 100644
index 00000000000..6e725dfb3ff
--- /dev/null
+++ b/drivers/staging/unisys/include/periodic_work.h
@@ -0,0 +1,40 @@
+/* periodic_work.h
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 of the License, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __PERIODIC_WORK_H__
+#define __PERIODIC_WORK_H__
+
+#include "timskmod.h"
+
+
+
+/* PERIODIC_WORK an opaque structure to users.
+ * Fields are declared only in the implementation .c files.
+ */
+typedef struct PERIODIC_WORK_Tag PERIODIC_WORK;
+
+PERIODIC_WORK *periodic_work_create(ulong jiffy_interval,
+ struct workqueue_struct *workqueue,
+ void (*workfunc)(void *),
+ void *workfuncarg,
+ const char *devnam);
+void periodic_work_destroy(PERIODIC_WORK *periodic_work);
+BOOL periodic_work_nextperiod(PERIODIC_WORK *periodic_work);
+BOOL periodic_work_start(PERIODIC_WORK *periodic_work);
+BOOL periodic_work_stop(PERIODIC_WORK *periodic_work);
+
+#endif
diff --git a/drivers/staging/unisys/include/procobjecttree.h b/drivers/staging/unisys/include/procobjecttree.h
new file mode 100644
index 00000000000..d3b69f47a8e
--- /dev/null
+++ b/drivers/staging/unisys/include/procobjecttree.h
@@ -0,0 +1,48 @@
+/* procobjecttree.h
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 of the License, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/** @file *********************************************************************
+ *
+ * This describes the interfaces necessary for creating a tree of types,
+ * objects, and properties in /proc.
+ *
+ ******************************************************************************
+ */
+
+#ifndef __PROCOBJECTTREE_H__
+#define __PROCOBJECTTREE_H__
+
+#include "uniklog.h"
+#include "timskmod.h"
+
+/* These are opaque structures to users.
+ * Fields are declared only in the implementation .c files.
+ */
+typedef struct MYPROCOBJECT_Tag MYPROCOBJECT;
+typedef struct MYPROCTYPE_Tag MYPROCTYPE;
+
+MYPROCOBJECT *proc_CreateObject(MYPROCTYPE *type, const char *name,
+ void *context);
+void proc_DestroyObject(MYPROCOBJECT *obj);
+MYPROCTYPE *proc_CreateType(struct proc_dir_entry *procRootDir,
+ const char **name,
+ const char **propertyNames,
+ void (*show_property)(struct seq_file *,
+ void *, int));
+void proc_DestroyType(MYPROCTYPE *type);
+
+#endif
diff --git a/drivers/staging/unisys/include/timskmod.h b/drivers/staging/unisys/include/timskmod.h
new file mode 100644
index 00000000000..0d0b29c9c4b
--- /dev/null
+++ b/drivers/staging/unisys/include/timskmod.h
@@ -0,0 +1,558 @@
+/* timskmod.h
+ *
+ * Copyright � 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 of the License, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __TIMSKMOD_H__
+#define __TIMSKMOD_H__
+
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/cdev.h>
+#include <linux/types.h>
+#include <asm/irq.h>
+#include <linux/io.h>
+#include <asm/dma.h>
+#include <linux/uaccess.h>
+#include <linux/list.h>
+#include <linux/poll.h>
+/* #define EXPORT_SYMTAB */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/fcntl.h>
+#include <linux/aio.h>
+#include <linux/workqueue.h>
+#include <linux/kthread.h>
+#include <linux/seq_file.h>
+#include <linux/mm.h>
+
+/* #define DEBUG */
+#ifndef BOOL
+#define BOOL int
+#endif
+#define FALSE 0
+#define TRUE 1
+#if !defined SUCCESS
+#define SUCCESS 0
+#endif
+#define FAILURE (-1)
+#define DRIVERNAMEMAX 50
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define STRUCTSEQUAL(x, y) (memcmp(&x, &y, sizeof(x)) == 0)
+#ifndef HOSTADDRESS
+#define HOSTADDRESS unsigned long long
+#endif
+
+typedef long VMMIO; /**< Virtual MMIO address (returned from ioremap), which
+ * is a virtual address pointer to a memory-mapped region.
+ * These are declared as "long" instead of u32* to force you to
+ * use readb()/writeb()/memcpy_fromio()/etc to access them.
+ * (On x86 we could probably get away with treating them as
+ * pointers.)
+ */
+typedef long VMMIO8; /**< #VMMIO pointing to 8-bit data */
+typedef long VMMIO16;/**< #VMMIO pointing to 16-bit data */
+typedef long VMMIO32;/**< #VMMIO pointing to 32-bit data */
+
+#define LOCKSEM(sem) down_interruptible(sem)
+#define LOCKSEM_UNINTERRUPTIBLE(sem) down(sem)
+#define UNLOCKSEM(sem) up(sem)
+
+/** lock read/write semaphore for reading.
+ Note that all read/write semaphores are of the "uninterruptible" variety.
+ @param sem (rw_semaphore *) points to semaphore to lock
+ */
+#define LOCKREADSEM(sem) down_read(sem)
+
+/** unlock read/write semaphore for reading.
+ Note that all read/write semaphores are of the "uninterruptible" variety.
+ @param sem (rw_semaphore *) points to semaphore to unlock
+ */
+#define UNLOCKREADSEM(sem) up_read(sem)
+
+/** lock read/write semaphore for writing.
+ Note that all read/write semaphores are of the "uninterruptible" variety.
+ @param sem (rw_semaphore *) points to semaphore to lock
+ */
+#define LOCKWRITESEM(sem) down_write(sem)
+
+/** unlock read/write semaphore for writing.
+ Note that all read/write semaphores are of the "uninterruptible" variety.
+ @param sem (rw_semaphore *) points to semaphore to unlock
+ */
+#define UNLOCKWRITESEM(sem) up_write(sem)
+
+#ifdef ENABLE_RETURN_TRACE
+#define RETTRACE(x) \
+ do { \
+ if (1) { \
+ INFODRV("RET 0x%lx in %s", \
+ (ulong)(x), __func__); \
+ } \
+ } while (0)
+#else
+#define RETTRACE(x)
+#endif
+
+/** return from a void function, using a common exit point "Away" */
+#define RETVOID do { RETTRACE(0); goto Away; } while (0)
+/** return from an int function, using a common exit point "Away"
+ * @param x the value to return
+ */
+#define RETINT(x) do { rc = (x); RETTRACE(x); goto Away; } while (0)
+/** return from a void* function, using a common exit point "Away"
+ * @param x the value to return
+ */
+#define RETPTR(x) do { rc = (x); RETTRACE(x); goto Away; } while (0)
+/** return from a BOOL function, using a common exit point "Away"
+ * @param x the value to return
+ */
+#define RETBOOL(x) do { rc = (x); RETTRACE(x); goto Away; } while (0)
+/** Given a typedef/struct/union and a member field name,
+ * return the number of bytes occupied by that field.
+ * @param TYPE the typedef name, or "struct xx" or "union xx"
+ * @param MEMBER the name of the member field whose size is to be determined
+ * @return the size of the field in bytes
+ */
+#define FAIL(msg, status) do { \
+ ERRDRV("'%s'" \
+ ": error (status=%d)\n", \
+ msg, status); \
+ RETINT(status); \
+ } while (0)
+#define FAIL_WPOSTCODE_1(msg, status, EVENT_PC) do { \
+ ERRDRV("'%s'" \
+ ": error (status=%d)\n", \
+ msg, status); \
+ POSTCODE_LINUX_2(EVENT_PC, DIAG_SEVERITY_ERR); \
+ RETINT(status); \
+ } while (0)
+#define FAIL_WPOSTCODE_2(msg, status, EVENT_PC, pcval32bit) do { \
+ ERRDRV("'%s'" \
+ ": error (status=%d)\n", \
+ msg, status); \
+ POSTCODE_LINUX_3(EVENT_PC, pcval32bit, DIAG_SEVERITY_ERR); \
+ RETINT(status); \
+ } while (0)
+#define FAIL_WPOSTCODE_3(msg, status, EVENT_PC, pcval16bit1, pcval16bit2) \
+ do { \
+ ERRDRV("'%s'" \
+ ": error (status=%d)\n", \
+ msg, status); \
+ POSTCODE_LINUX_4(EVENT_PC, pcval16bit1, pcval16bit2, \
+ DIAG_SEVERITY_ERR); \
+ RETINT(status); \
+ } while (0)
+/** Try to evaulate the provided expression, and do a RETINT(x) iff
+ * the expression evaluates to < 0.
+ * @param x the expression to try
+ */
+#define TRY(x) do { int status = (x); \
+ if (status < 0) \
+ FAIL(__stringify(x), status); \
+ } while (0)
+
+#define TRY_WPOSTCODE_1(x, EVENT_PC) do { \
+ int status = (x); \
+ if (status < 0) \
+ FAIL_WPOSTCODE_1(__stringify(x), status, EVENT_PC); \
+ } while (0)
+
+#define TRY_WPOSTCODE_2(x, EVENT_PC, pcval32bit) do { \
+ int status = (x); \
+ if (status < 0) \
+ FAIL_WPOSTCODE_2(__stringify(x), status, EVENT_PC, \
+ pcval32bit); \
+ } while (0)
+
+#define TRY_WPOSTCODE_3(x, EVENT_PC, pcval16bit1, pcval16bit2) do { \
+ int status = (x); \
+ if (status < 0) \
+ FAIL_WPOSTCODE_3(__stringify(x), status, EVENT_PC, \
+ pcval16bit1, pcval16bit2); \
+ } while (0)
+
+#define ASSERT(cond) \
+ do { if (!(cond)) \
+ HUHDRV("ASSERT failed - %s", \
+ __stringify(cond)); \
+ } while (0)
+
+#define sizeofmember(TYPE, MEMBER) (sizeof(((TYPE *)0)->MEMBER))
+/** "Covered quotient" function */
+#define COVQ(v, d) (((v) + (d) - 1) / (d))
+#define SWAPPOINTERS(p1, p2) \
+ do { \
+ void *SWAPPOINTERS_TEMP = (void *)p1; \
+ (void *)(p1) = (void *)(p2); \
+ (void *)(p2) = SWAPPOINTERS_TEMP; \
+ } while (0)
+
+/**
+ * @addtogroup driverlogging
+ * @{
+ */
+
+#define PRINTKDRV(fmt, args...) LOGINF(fmt, ## args)
+#define TBDDRV(fmt, args...) LOGERR(fmt, ## args)
+#define HUHDRV(fmt, args...) LOGERR(fmt, ## args)
+#define ERRDRV(fmt, args...) LOGERR(fmt, ## args)
+#define WARNDRV(fmt, args...) LOGWRN(fmt, ## args)
+#define SECUREDRV(fmt, args...) LOGWRN(fmt, ## args)
+#define INFODRV(fmt, args...) LOGINF(fmt, ## args)
+#define DEBUGDRV(fmt, args...) DBGINF(fmt, ## args)
+
+#define PRINTKDEV(devname, fmt, args...) LOGINFDEV(devname, fmt, ## args)
+#define TBDDEV(devname, fmt, args...) LOGERRDEV(devname, fmt, ## args)
+#define HUHDEV(devname, fmt, args...) LOGERRDEV(devname, fmt, ## args)
+#define ERRDEV(devname, fmt, args...) LOGERRDEV(devname, fmt, ## args)
+#define ERRDEVX(devno, fmt, args...) LOGERRDEVX(devno, fmt, ## args)
+#define WARNDEV(devname, fmt, args...) LOGWRNDEV(devname, fmt, ## args)
+#define SECUREDEV(devname, fmt, args...) LOGWRNDEV(devname, fmt, ## args)
+#define INFODEV(devname, fmt, args...) LOGINFDEV(devname, fmt, ## args)
+#define INFODEVX(devno, fmt, args...) LOGINFDEVX(devno, fmt, ## args)
+#define DEBUGDEV(devname, fmt, args...) DBGINFDEV(devname, fmt, ## args)
+
+
+/* @} */
+
+/** Used to add a single line to the /proc filesystem buffer */
+#define ADDPROCLINE(buf, bufsize, line, linelen, totallen) \
+ { \
+ if ((totallen) + (linelen) >= bufsize) \
+ RETINT(totallen); \
+ if (linelen > 0) { \
+ strcat(buf, line); \
+ totallen += linelen; \
+ } \
+ }
+
+
+
+/** Verifies the consistency of your PRIVATEDEVICEDATA structure using
+ * conventional "signature" fields:
+ * <p>
+ * - sig1 should contain the size of the structure
+ * - sig2 should contain a pointer to the beginning of the structure
+ */
+#define DDLOOKSVALID(dd) \
+ ((dd != NULL) && \
+ ((dd)->sig1 == sizeof(PRIVATEDEVICEDATA)) && \
+ ((dd)->sig2 == dd))
+
+/** Verifies the consistency of your PRIVATEFILEDATA structure using
+ * conventional "signature" fields:
+ * <p>
+ * - sig1 should contain the size of the structure
+ * - sig2 should contain a pointer to the beginning of the structure
+ */
+#define FDLOOKSVALID(fd) \
+ ((fd != NULL) && \
+ ((fd)->sig1 == sizeof(PRIVATEFILEDATA)) && \
+ ((fd)->sig2 == fd))
+
+/** Verifies the consistency of a PRIVATEDEVICEDATA structure and reacts
+ * if necessary
+ */
+#define CHKDDX(dd, x) ( \
+ if (!DDLOOKSVALID((dd))) { \
+ PRINTKDRV("bad device structure"); \
+ RETINT(x); \
+ })
+
+/** Verifies the consistency of a PRIVATEDEVICEDATA structure and reacts
+ * if necessary
+ */
+#define CHKDD(dd) ( \
+ if (!DDLOOKSVALID(dd)) { \
+ PRINTKDRV("bad device structure"); \
+ RETVOID; \
+ })
+
+/** Verifies the consistency of a PRIVATEFILEDATA structure and reacts
+ * if necessary
+ */
+#define CHKFDX(fd, x) ( \
+ if (!FDLOOKSVALID(fd)) { \
+ PRINTKDRV("bad file structure"); \
+ RETINT(x); \
+ })
+
+/** Verifies the consistency of a PRIVATEFILEDATA structure and reacts
+ * if necessary
+ */
+#define CHKFD(fd) ( \
+ if (!FDLOOKSVALID(fd)) { \
+ PRINTKDRV("bad file structure"); \
+ RETVOID; \
+ })
+
+/** Converts a device index #devix into #devData, after checking for validity.
+ * Can only be called from functions returning void.
+ * @param devix your device index within the #DevData array.
+ * @param devData the #PRIVATEDEVICEDATA pointer that will be set on return.
+ * @param where string identifying the calling function, to be printed in
+ * debug message
+ * @param dbg 1 iff debug messages are enabled
+ */
+#define DEVFROMID(devix, devData, where, dbg) \
+ { \
+ if (devix >= MAXDEVICES) { \
+ PRINTKDRV("bad devix passed to %s()", where); \
+ RETVOID; \
+ } \
+ if (dbg) \
+ DEBUGDEV(devix, "%s", where); \
+ if (devix >= MAXDEVICES) { \
+ DEBUGDEV(devix, "%s - bad devix %d", \
+ where, devix); \
+ RETVOID; \
+ } \
+ devData = DevData[devix]; \
+ CHKDD(devData); \
+ }
+
+/** Converts a device index #devix into #devData, after checking for validity.
+ * Can only be called from functions returning int.
+ * @param devix your device index within the #DevData array.
+ * @param devData the #PRIVATEDEVICEDATA pointer that will be set on return.
+ * @param errcode error code that your function will return on error.
+ * @param where string identifying the calling function, to be printed in
+ * debug message
+ * @param dbg 1 iff debug messages are enabled
+ */
+#define DEVFROMIDX(devix, devData, errcode, where, dbg) \
+ { \
+ if (devix >= MAXDEVICES) { \
+ PRINTKDRV("bad devix passed to %s()", where); \
+ RETINT(errcode); \
+ } \
+ if (dbg) \
+ DEBUGDEV(devix, "%s", where); \
+ if (devix >= MAXDEVICES) { \
+ DEBUGDEV(devix, "%s - bad devix %d", \
+ where, devix); \
+ RETINT(-ENODEV); \
+ } \
+ devData = DevData[devix]; \
+ CHKDDX(devData, -EIO); \
+ }
+
+/** Converts an inode pointer #inode into a #devix and #devData, after
+ * checking for validity.
+ * Can only be called from functions returning int.
+ * @param devix your device index within the #DevData array.
+ * @param devData the #PRIVATEDEVICEDATA pointer that will be set on return.
+ * @param inode input inode pointer
+ * @param errcode error code that your function will return on error.
+ * @param where string identifying the calling function, to be printed in
+ * debug message
+ * @param dbg 1 iff debug messages are enabled
+ */
+#define DEVFROMINODE(devix, devData, inode, errcode, where, dbg) \
+ { \
+ if (inode == NULL) { \
+ PRINTKDRV("bad inode passed to %s()", where); \
+ RETINT(errcode); \
+ } \
+ devix = MINOR(inode->i_rdev); \
+ if (dbg) \
+ DEBUGDEV(devix, "%s", where); \
+ if (devix >= MAXDEVICES) { \
+ DEBUGDEV(devix, "%s - bad devix %d", \
+ where, devix); \
+ RETINT(-ENODEV); \
+ } \
+ devData = DevData[devix]; \
+ CHKDDX(devData, -EIO); \
+ }
+
+/** Converts a file pointer #file into a #devix and #devData, after checking
+ * for validity.
+ * Can only be called from functions returning int.
+ * @param devix your device index within the #DevData array.
+ * @param devData the #PRIVATEDEVICEDATA pointer that will be set on return.
+ * @param file input file pointer
+ * @param errcode error code that your function will return on error.
+ * @param where string identifying the calling function, to be printed in
+ * debug message
+ * @param dbg 1 iff debug messages are enabled
+ */
+#define DEVFROMFILE(devix, devData, fileData, file, errcode, where, dbg) \
+ { \
+ if (file == NULL) { \
+ PRINTKDRV("bad file passed to %s()", where); \
+ RETINT(errcode); \
+ } \
+ CHKFDX((PRIVATEFILEDATA *)(file->private_data), -EIO); \
+ fileData = file->private_data; \
+ devix = fileData->devix; \
+ if (dbg) \
+ DEBUGDEV(devix, "%s %p", where, file); \
+ if (devix >= MAXDEVICES) { \
+ DEBUGDEV(devix, "%s - bad devix %d", \
+ where, devix); \
+ RETINT(-ENODEV); \
+ } \
+ devData = DevData[devix]; \
+ CHKDDX(devData, -EIO); \
+ }
+
+/** Locks dd->lockDev if you havn't already locked it */
+#define LOCKDEV(dd) \
+ { \
+ if (!lockedDev) { \
+ spin_lock(&dd->lockDev); \
+ lockedDev = TRUE; \
+ } \
+ }
+
+/** Unlocks dd->lockDev if you previously locked it */
+#define UNLOCKDEV(dd) \
+ { \
+ if (lockedDev) { \
+ spin_unlock(&dd->lockDev); \
+ lockedDev = FALSE; \
+ } \
+ }
+
+/** Locks dd->lockDevISR if you havn't already locked it */
+#define LOCKDEVISR(dd) \
+ { \
+ if (!lockedDevISR) { \
+ spin_lock_irqsave(&dd->lockDevISR, flags); \
+ lockedDevISR = TRUE; \
+ } \
+ }
+
+/** Unlocks dd->lockDevISR if you previously locked it */
+#define UNLOCKDEVISR(dd) \
+ { \
+ if (lockedDevISR) { \
+ spin_unlock_irqrestore(&dd->lockDevISR, flags); \
+ lockedDevISR = FALSE; \
+ } \
+ }
+
+/** Locks LockGlobalISR if you havn't already locked it */
+#define LOCKGLOBALISR \
+ { \
+ if (!lockedGlobalISR) { \
+ spin_lock_irqsave(&LockGlobalISR, flags); \
+ lockedGlobalISR = TRUE; \
+ } \
+ }
+
+/** Unlocks LockGlobalISR if you previously locked it */
+#define UNLOCKGLOBALISR \
+ { \
+ if (lockedGlobalISR) { \
+ spin_unlock_irqrestore(&LockGlobalISR, flags); \
+ lockedGlobalISR = FALSE; \
+ } \
+ }
+
+/** Locks LockGlobal if you havn't already locked it */
+#define LOCKGLOBAL \
+ { \
+ if (!lockedGlobal) { \
+ spin_lock(&LockGlobal); \
+ lockedGlobal = TRUE; \
+ } \
+ }
+
+/** Unlocks LockGlobal if you previously locked it */
+#define UNLOCKGLOBAL \
+ { \
+ if (lockedGlobal) { \
+ spin_unlock(&LockGlobal); \
+ lockedGlobal = FALSE; \
+ } \
+ }
+
+/** Use this at the beginning of functions where you intend to
+ * use #LOCKDEV/#UNLOCKDEV, #LOCKDEVISR/#UNLOCKDEVISR,
+ * #LOCKGLOBAL/#UNLOCKGLOBAL, #LOCKGLOBALISR/#UNLOCKGLOBALISR.
+ *
+ * Note that __attribute__((unused)) is how you tell GNU C to suppress
+ * any warning messages about the variable being unused.
+ */
+#define LOCKPREAMBLE \
+ ulong flags __attribute__((unused)) = 0; \
+ BOOL lockedDev __attribute__((unused)) = FALSE; \
+ BOOL lockedDevISR __attribute__((unused)) = FALSE; \
+ BOOL lockedGlobal __attribute__((unused)) = FALSE; \
+ BOOL lockedGlobalISR __attribute__((unused)) = FALSE
+
+
+
+/** Sleep for an indicated number of seconds (for use in kernel mode).
+ * @param x the number of seconds to sleep.
+ */
+#define SLEEP(x) \
+ do { current->state = TASK_INTERRUPTIBLE; \
+ schedule_timeout((x)*HZ); \
+ } while (0)
+
+/** Sleep for an indicated number of jiffies (for use in kernel mode).
+ * @param x the number of jiffies to sleep.
+ */
+#define SLEEPJIFFIES(x) \
+ do { current->state = TASK_INTERRUPTIBLE; \
+ schedule_timeout(x); \
+ } while (0)
+
+#ifndef max
+#define max(a, b) (((a) > (b)) ? (a):(b))
+#endif
+
+static inline struct cdev *cdev_alloc_init(struct module *owner,
+ const struct file_operations *fops)
+{
+ struct cdev *cdev = NULL;
+ cdev = cdev_alloc();
+ if (!cdev)
+ return NULL;
+ cdev->ops = fops;
+ cdev->owner = owner;
+
+ /* Note that the memory allocated for cdev will be deallocated
+ * when the usage count drops to 0, because it is controlled
+ * by a kobject of type ktype_cdev_dynamic. (This
+ * deallocation could very well happen outside of our kernel
+ * module, like via the cdev_put in __fput() for example.)
+ */
+ return cdev;
+}
+
+#include "timskmodutils.h"
+
+#endif
diff --git a/drivers/staging/unisys/include/timskmodutils.h b/drivers/staging/unisys/include/timskmodutils.h
new file mode 100644
index 00000000000..ad4175e83ab
--- /dev/null
+++ b/drivers/staging/unisys/include/timskmodutils.h
@@ -0,0 +1,194 @@
+/* timskmodutils.h
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 of the License, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __TIMSKMODUTILS_H__
+#define __TIMSKMODUTILS_H__
+
+#include "timskmod.h"
+
+void *kmalloc_kernel(size_t siz);
+void *kmalloc_kernel_dma(size_t siz);
+void kfree_kernel(const void *p, size_t siz);
+void *vmalloc_kernel(size_t siz);
+void vfree_kernel(const void *p, size_t siz);
+void *pgalloc_kernel(size_t siz);
+void pgfree_kernel(const void *p, size_t siz);
+void myprintk(const char *myDrvName, const char *devname,
+ const char *template, ...);
+void myprintkx(const char *myDrvName, int devno, const char *template, ...);
+
+/** Print the hexadecimal contents of a data buffer to a supplied print buffer.
+ * @param dest the print buffer where text characters will be
+ * written
+ * @param destSize the maximum number of bytes that can be written
+ * to #dest
+ * @param src the buffer that contains the data that is to be
+ * hex-dumped
+ * @param srcLen the number of bytes at #src to be hex-dumped
+ * @param bytesToDumpPerLine output will be formatted such that at most this
+ * many of the input data bytes will be represented
+ * on each line of output
+ * @return the number of text characters written to #dest
+ * (not including the trailing '\0' byte)
+ * @ingroup internal
+ */
+int hexDumpToBuffer(char *dest,
+ int destSize,
+ char *prefix,
+ char *src,
+ int srcLen,
+ int bytesToDumpPerLine);
+
+/** Print the hexadecimal contents of a data buffer to a supplied print buffer.
+ * Assume the data buffer contains 32-bit words in little-endian format,
+ * and dump the words with MSB first and LSB last.
+ * @param dest the print buffer where text characters will be
+ * written
+ * @param destSize the maximum number of bytes that can be written
+ * to #dest
+ * @param src the buffer that contains the data that is to be
+ * hex-dumped
+ * @param srcWords the number of 32-bit words at #src to be
+ & hex-dumped
+ * @param wordsToDumpPerLine output will be formatted such that at most this
+ * many of the input data words will be represented
+ * on each line of output
+ * @return the number of text characters written to #dest
+ * (not including the trailing '\0' byte)
+ * @ingroup internal
+ */
+int hexDumpWordsToBuffer(char *dest,
+ int destSize,
+ char *prefix,
+ uint32_t *src,
+ int srcWords,
+ int wordsToDumpPerLine);
+
+
+/** Use printk to print the hexadecimal contents of a data buffer.
+ * See #INFOHEXDRV and #INFOHEXDEV for info.
+ * @ingroup internal
+ */
+int myPrintkHexDump(char *myDrvName,
+ char *devname,
+ char *prefix,
+ char *src,
+ int srcLen,
+ int bytesToDumpPerLine);
+
+/** Given as input a number of seconds in #seconds, creates text describing
+ * the time within #s. Also breaks down the number of seconds into component
+ * days, hours, minutes, and seconds, and stores to *#days, *#hours,
+ * *#minutes, and *#secondsx.
+ * @param seconds input number of seconds
+ * @param days points to a long value where the days component for the
+ * days+hours+minutes+seconds representation of #seconds will
+ * be stored
+ * @param hours points to a long value where the hours component for the
+ * days+hours+minutes+seconds representation of #seconds will
+ * be stored
+ * @param minutes points to a long value where the minutes component for the
+ * days+hours+minutes+seconds representation of #seconds will
+ * be stored
+ * @param secondsx points to a long value where the seconds component for the
+ * days+hours+minutes+seconds representation of #seconds will
+ * be stored
+ * @param s points to a character buffer where a text representation of
+ * the #seconds value will be stored. This buffer MUST be
+ * large enough to hold the resulting string; to be safe it
+ * should be at least 100 bytes long.
+ */
+void expandSeconds(time_t seconds,
+ long *days, long *hours,
+ long *minutes,
+ long *secondsx,
+ char *s);
+
+/*--------------------------------*
+ *--- GENERAL MESSAGEQ STUFF ---*
+ *--------------------------------*/
+
+struct MessageQEntry;
+
+/** the data structure used to hold an arbitrary data item that you want
+ * to place on a #MESSAGEQ. Declare and initialize as follows:
+ * @code
+ * MESSAGEQENTRY myEntry;
+ * initMessageQEntry (&myEntry, pointerToMyDataItem);
+ * @endcode
+ * This structure should be considered opaque; the client using it should
+ * never access the fields directly.
+ * Refer to these functions for more info:
+ * - initMessageQ()
+ * - initMessageQEntry()
+ * - enqueueMessage()
+ * - dequeueMessage()
+ * - dequeueMessageNoBlock()
+ * - getQueueCount()
+ *
+ * @ingroup messageq
+ */
+typedef struct MessageQEntry {
+ void *data;
+ struct MessageQEntry *qNext;
+ struct MessageQEntry *qPrev;
+} MESSAGEQENTRY;
+
+/** the data structure used to hold a FIFO queue of #MESSAGEQENTRY<b></b>s.
+ * Declare and initialize as follows:
+ * @code
+ * MESSAGEQ myQueue;
+ * initMessageQ (&myQueue);
+ * @endcode
+ * This structure should be considered opaque; the client using it should
+ * never access the fields directly.
+ * Refer to these functions for more info:
+ * - initMessageQ()
+ * - initMessageQEntry()
+ * - enqueueMessage()
+ * - dequeueMessage()
+ * - dequeueMessageNoBlock()
+ * - getQueueCount()
+ *
+ * @ingroup messageq
+ */
+typedef struct MessageQ {
+ MESSAGEQENTRY *qHead;
+ MESSAGEQENTRY *qTail;
+ struct semaphore nQEntries;
+ spinlock_t queueLock;
+} MESSAGEQ;
+
+char *cyclesToSeconds(u64 cycles, u64 cyclesPerSecond,
+ char *buf, size_t bufsize);
+char *cyclesToIterationSeconds(u64 cycles, u64 cyclesPerSecond,
+ u64 iterations, char *buf, size_t bufsize);
+char *cyclesToSomethingsPerSecond(u64 cycles, u64 cyclesPerSecond,
+ u64 somethings, char *buf, size_t bufsize);
+void initMessageQ(MESSAGEQ *q);
+void initMessageQEntry(MESSAGEQENTRY *p, void *data);
+MESSAGEQENTRY *dequeueMessage(MESSAGEQ *q);
+MESSAGEQENTRY *dequeueMessageNoBlock(MESSAGEQ *q);
+void enqueueMessage(MESSAGEQ *q, MESSAGEQENTRY *pEntry);
+size_t getQueueCount(MESSAGEQ *q);
+int waitQueueLen(wait_queue_head_t *q);
+void debugWaitQ(wait_queue_head_t *q);
+struct seq_file *seq_file_new_buffer(void *buf, size_t buf_size);
+void seq_file_done_buffer(struct seq_file *m);
+void seq_hexdump(struct seq_file *seq, u8 *pfx, void *buf, ulong nbytes);
+
+#endif
diff --git a/drivers/staging/unisys/include/uniklog.h b/drivers/staging/unisys/include/uniklog.h
new file mode 100644
index 00000000000..4d7b87cefa6
--- /dev/null
+++ b/drivers/staging/unisys/include/uniklog.h
@@ -0,0 +1,193 @@
+/* uniklog.h
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 of the License, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/* This module contains macros to aid developers in logging messages.
+ *
+ * This module is affected by the DEBUG compiletime option.
+ *
+ */
+#ifndef __UNIKLOG_H__
+#define __UNIKLOG_H__
+
+
+#include <linux/printk.h>
+
+/*
+ * # DBGINF
+ *
+ * \brief Log debug informational message - log a LOG_INFO message only
+ * if DEBUG compiletime option enabled
+ *
+ * \param devname the device name of the device reporting this message, or
+ * NULL if this message is NOT device-related.
+ * \param fmt printf()-style format string containing the message to log.
+ * \param args Optional arguments to be formatted and inserted into the
+ * format string.
+ * \return nothing
+ *
+ * Log a message at the LOG_INFO level, but only if DEBUG is enabled. If
+ * DEBUG is disabled, this expands to a no-op.
+ */
+
+/*
+ * # DBGVER
+ *
+ * \brief Log debug verbose message - log a LOG_DEBUG message only if
+ * DEBUG compiletime option enabled
+ *
+ * \param devname the device name of the device reporting this message, or
+ * NULL if this message is NOT device-related.
+ * \param fmt printf()-style format string containing the message to log.
+ * \param args Optional arguments to be formatted and inserted into the
+ * format string.
+ * \return nothing
+ *
+ * Log a message at the LOG_DEBUG level, but only if DEBUG is enabled. If
+ * DEBUG is disabled, this expands to a no-op. Note also that LOG_DEBUG
+ * messages can be enabled/disabled at runtime as well.
+ */
+#define DBGINFDEV(devname, fmt, args...) do { } while (0)
+#define DBGVERDEV(devname, fmt, args...) do { } while (0)
+#define DBGINF(fmt, args...) do { } while (0)
+#define DBGVER(fmt, args...) do { } while (0)
+
+/*
+ * # LOGINF
+ *
+ * \brief Log informational message - logs a message at the LOG_INFO level
+ *
+ * \param devname the device name of the device reporting this message, or
+ * NULL if this message is NOT device-related.
+ * \param fmt printf()-style format string containing the message to log.
+ * \param args Optional arguments to be formatted and inserted into the
+ * format string.
+ * \return nothing
+ *
+ * Logs the specified message at the LOG_INFO level.
+ */
+
+#define LOGINF(fmt, args...) pr_info(fmt, ## args)
+#define LOGINFDEV(devname, fmt, args...) \
+ pr_info("%s " fmt, devname, ## args)
+#define LOGINFDEVX(devno, fmt, args...) \
+ pr_info("dev%d " fmt, devno, ## args)
+#define LOGINFNAME(vnic, fmt, args...) \
+ do { \
+ if (vnic != NULL) { \
+ pr_info("%s " fmt, vnic->name, ## args); \
+ } else { \
+ pr_info(fmt, ## args); \
+ } \
+ } while (0)
+
+/*
+ * # LOGVER
+ *
+ * \brief Log verbose message - logs a message at the LOG_DEBUG level,
+ * which can be disabled at runtime
+ *
+ * \param devname the device name of the device reporting this message, or
+ * NULL if this message is NOT device-related.
+ * \param fmt printf()-style format string containing the message to log.
+ * \param args Optional arguments to be formatted and inserted into the format
+ * \param string.
+ * \return nothing
+ *
+ * Logs the specified message at the LOG_DEBUG level. Note also that
+ * LOG_DEBUG messages can be enabled/disabled at runtime as well.
+ */
+#define LOGVER(fmt, args...) pr_debug(fmt, ## args)
+#define LOGVERDEV(devname, fmt, args...) \
+ pr_debug("%s " fmt, devname, ## args)
+#define LOGVERNAME(vnic, fmt, args...) \
+ do { \
+ if (vnic != NULL) { \
+ pr_debug("%s " fmt, vnic->name, ## args); \
+ } else { \
+ pr_debug(fmt, ## args); \
+ } \
+ } while (0)
+
+
+/*
+ * # LOGERR
+ *
+ * \brief Log error message - logs a message at the LOG_ERR level,
+ * including source line number information
+ *
+ * \param devname the device name of the device reporting this message, or
+ * NULL if this message is NOT device-related.
+ * \param fmt printf()-style format string containing the message to log.
+ * \param args Optional arguments to be formatted and inserted into the format
+ * \param string.
+ * \return nothing
+ *
+ * Logs the specified error message at the LOG_ERR level. It will also
+ * include the file, line number, and function name of where the error
+ * originated in the log message.
+ */
+#define LOGERR(fmt, args...) pr_err(fmt, ## args)
+#define LOGERRDEV(devname, fmt, args...) \
+ pr_err("%s " fmt, devname, ## args)
+#define LOGERRDEVX(devno, fmt, args...) \
+ pr_err("dev%d " fmt, devno, ## args)
+#define LOGERRNAME(vnic, fmt, args...) \
+ do { \
+ if (vnic != NULL) { \
+ pr_err("%s " fmt, vnic->name, ## args); \
+ } else { \
+ pr_err(fmt, ## args); \
+ } \
+ } while (0)
+#define LOGORDUMPERR(seqfile, fmt, args...) do { \
+ if (seqfile) { \
+ seq_printf(seqfile, fmt, ## args); \
+ } else { \
+ LOGERR(fmt, ## args); \
+ } \
+ } while (0)
+
+/*
+ * # LOGWRN
+ *
+ * \brief Log warning message - Logs a message at the LOG_WARNING level,
+ * including source line number information
+ *
+ * \param devname the device name of the device reporting this message, or
+ * NULL if this message is NOT device-related.
+ * \param fmt printf()-style format string containing the message to log.
+ * \param args Optional arguments to be formatted and inserted into the format
+ * \param string.
+ * \return nothing
+ *
+ * Logs the specified error message at the LOG_WARNING level. It will also
+ * include the file, line number, and function name of where the error
+ * originated in the log message.
+ */
+#define LOGWRN(fmt, args...) pr_warn(fmt, ## args)
+#define LOGWRNDEV(devname, fmt, args...) \
+ pr_warn("%s " fmt, devname, ## args)
+#define LOGWRNNAME(vnic, fmt, args...) \
+ do { \
+ if (vnic != NULL) { \
+ pr_warn("%s " fmt, vnic->name, ## args); \
+ } else { \
+ pr_warn(fmt, ## args); \
+ } \
+ } while (0)
+
+#endif /* __UNIKLOG_H__ */
diff --git a/drivers/staging/unisys/visorutil/Kconfig b/drivers/staging/unisys/visorutil/Kconfig
new file mode 100644
index 00000000000..4ff61a7c506
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/Kconfig
@@ -0,0 +1,10 @@
+#
+# Unisys timskmod configuration
+#
+
+config UNISYS_VISORUTIL
+ tristate "Unisys visorutil driver"
+ depends on UNISYSSPAR
+ ---help---
+ If you say Y here, you will enable the Unisys visorutil driver.
+
diff --git a/drivers/staging/unisys/visorutil/Makefile b/drivers/staging/unisys/visorutil/Makefile
new file mode 100644
index 00000000000..3f463888dce
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for Unisys timskmod
+#
+
+obj-$(CONFIG_UNISYS_VISORUTIL) += visorutil.o
+
+visorutil-y := charqueue.o easyproc.o periodic_work.o procobjecttree.o \
+ memregion_direct.o visorkmodutils.o
+
+ccflags-y += -Idrivers/staging/unisys/include
+ccflags-y += -DCONFIG_SPAR_GUEST -DGUESTDRIVERBUILD -DNOAUTOVERSION
diff --git a/drivers/staging/unisys/visorutil/charqueue.c b/drivers/staging/unisys/visorutil/charqueue.c
new file mode 100644
index 00000000000..2bce94e7009
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/charqueue.c
@@ -0,0 +1,144 @@
+/* charqueue.c
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 of the License, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * Simple character queue implementation for Linux kernel mode.
+ */
+
+#include "charqueue.h"
+
+#define MYDRVNAME "charqueue"
+
+#define IS_EMPTY(charqueue) (charqueue->head == charqueue->tail)
+
+
+
+struct CHARQUEUE_Tag {
+ int alloc_size;
+ int nslots;
+ spinlock_t lock;
+ int head, tail;
+ unsigned char buf[0];
+};
+
+
+
+CHARQUEUE *charqueue_create(ulong nslots)
+{
+ int alloc_size = sizeof(CHARQUEUE) + nslots + 1;
+ CHARQUEUE *cq = kmalloc(alloc_size, GFP_KERNEL|__GFP_NORETRY);
+ if (cq == NULL) {
+ ERRDRV("charqueue_create allocation failed (alloc_size=%d)",
+ alloc_size);
+ return NULL;
+ }
+ cq->alloc_size = alloc_size;
+ cq->nslots = nslots;
+ cq->head = cq->tail = 0;
+ spin_lock_init(&cq->lock);
+ return cq;
+}
+EXPORT_SYMBOL_GPL(charqueue_create);
+
+
+
+void charqueue_enqueue(CHARQUEUE *charqueue, unsigned char c)
+{
+ int alloc_slots = charqueue->nslots+1; /* 1 slot is always empty */
+
+ spin_lock(&charqueue->lock);
+ charqueue->head = (charqueue->head+1) % alloc_slots;
+ if (charqueue->head == charqueue->tail)
+ /* overflow; overwrite the oldest entry */
+ charqueue->tail = (charqueue->tail+1) % alloc_slots;
+ charqueue->buf[charqueue->head] = c;
+ spin_unlock(&charqueue->lock);
+}
+EXPORT_SYMBOL_GPL(charqueue_enqueue);
+
+
+
+BOOL charqueue_is_empty(CHARQUEUE *charqueue)
+{
+ BOOL b;
+ spin_lock(&charqueue->lock);
+ b = IS_EMPTY(charqueue);
+ spin_unlock(&charqueue->lock);
+ return b;
+}
+EXPORT_SYMBOL_GPL(charqueue_is_empty);
+
+
+
+static int charqueue_dequeue_1(CHARQUEUE *charqueue)
+{
+ int alloc_slots = charqueue->nslots + 1; /* 1 slot is always empty */
+
+ if (IS_EMPTY(charqueue))
+ return -1;
+ charqueue->tail = (charqueue->tail+1) % alloc_slots;
+ return charqueue->buf[charqueue->tail];
+}
+
+
+
+int charqueue_dequeue(CHARQUEUE *charqueue)
+{
+ int rc = -1;
+
+ spin_lock(&charqueue->lock);
+ RETINT(charqueue_dequeue_1(charqueue));
+Away:
+ spin_unlock(&charqueue->lock);
+ return rc;
+}
+
+
+
+int charqueue_dequeue_n(CHARQUEUE *charqueue, unsigned char *buf, int n)
+{
+ int rc = -1, counter = 0, c;
+
+ spin_lock(&charqueue->lock);
+ for (;;) {
+ if (n <= 0)
+ break; /* no more buffer space */
+ c = charqueue_dequeue_1(charqueue);
+ if (c < 0)
+ break; /* no more input */
+ *buf = (unsigned char)(c);
+ buf++;
+ n--;
+ counter++;
+ }
+ RETINT(counter);
+
+Away:
+ spin_unlock(&charqueue->lock);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(charqueue_dequeue_n);
+
+
+
+void charqueue_destroy(CHARQUEUE *charqueue)
+{
+ if (charqueue == NULL)
+ return;
+ kfree(charqueue);
+}
+EXPORT_SYMBOL_GPL(charqueue_destroy);
diff --git a/drivers/staging/unisys/visorutil/charqueue.h b/drivers/staging/unisys/visorutil/charqueue.h
new file mode 100644
index 00000000000..e9ce0a90d86
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/charqueue.h
@@ -0,0 +1,37 @@
+/* charqueue.h
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 of the License, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __CHARQUEUE_H__
+#define __CHARQUEUE_H__
+
+#include "uniklog.h"
+#include "timskmod.h"
+
+/* CHARQUEUE is an opaque structure to users.
+ * Fields are declared only in the implementation .c files.
+ */
+typedef struct CHARQUEUE_Tag CHARQUEUE;
+
+CHARQUEUE *charqueue_create(ulong nslots);
+void charqueue_enqueue(CHARQUEUE *charqueue, unsigned char c);
+int charqueue_dequeue(CHARQUEUE *charqueue);
+int charqueue_dequeue_n(CHARQUEUE *charqueue, unsigned char *buf, int n);
+BOOL charqueue_is_empty(CHARQUEUE *charqueue);
+void charqueue_destroy(CHARQUEUE *charqueue);
+
+#endif
+
diff --git a/drivers/staging/unisys/visorutil/easyproc.c b/drivers/staging/unisys/visorutil/easyproc.c
new file mode 100644
index 00000000000..2b750eef9f0
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/easyproc.c
@@ -0,0 +1,365 @@
+/* Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 of the License, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/** @file *********************************************************************
+ *
+ * Handle procfs-specific tasks.
+ * Note that this file does not know about any module-specific things, nor
+ * does it know anything about what information to reveal as part of the proc
+ * entries. The 2 functions that take care of displaying device and
+ * driver specific information are passed as parameters to
+ * easyproc_InitDriver().
+ *
+ * void show_device_info(struct seq_file *seq, void *p);
+ * void show_driver_info(struct seq_file *seq);
+ *
+ * The second parameter to show_device_info is actually a pointer to the
+ * device-specific info to show. It is the context that was originally
+ * passed to easyproc_InitDevice().
+ *
+ ******************************************************************************
+ */
+
+#include <linux/proc_fs.h>
+
+#include "uniklog.h"
+#include "timskmod.h"
+#include "easyproc.h"
+
+#define MYDRVNAME "easyproc"
+
+
+
+/*
+ * /proc/<ProcId> ProcDir
+ * /proc/<ProcId>/driver ProcDriverDir
+ * /proc/<ProcId>/driver/diag ProcDriverDiagFile
+ * /proc/<ProcId>/device ProcDeviceDir
+ * /proc/<ProcId>/device/0 procDevicexDir
+ * /proc/<ProcId>/device/0/diag procDevicexDiagFile
+ */
+
+
+static ssize_t proc_write_device(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos);
+static ssize_t proc_write_driver(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos);
+
+static struct proc_dir_entry *
+ createProcDir(char *name, struct proc_dir_entry *parent)
+{
+ struct proc_dir_entry *p = proc_mkdir_mode(name, S_IFDIR, parent);
+ if (p == NULL)
+ ERRDRV("failed to create /proc directory %s", name);
+ return p;
+}
+
+static int seq_show_driver(struct seq_file *seq, void *offset);
+static int proc_open_driver(struct inode *inode, struct file *file)
+{
+ return single_open(file, seq_show_driver, PDE_DATA(inode));
+}
+static const struct file_operations proc_fops_driver = {
+ .open = proc_open_driver,
+ .read = seq_read,
+ .write = proc_write_driver,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int seq_show_device(struct seq_file *seq, void *offset);
+static int seq_show_device_property(struct seq_file *seq, void *offset);
+static int proc_open_device(struct inode *inode, struct file *file)
+{
+ return single_open(file, seq_show_device, PDE_DATA(inode));
+}
+static const struct file_operations proc_fops_device = {
+ .open = proc_open_device,
+ .read = seq_read,
+ .write = proc_write_device,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+static int proc_open_device_property(struct inode *inode, struct file *file)
+{
+ return single_open(file, seq_show_device_property, PDE_DATA(inode));
+}
+static const struct file_operations proc_fops_device_property = {
+ .open = proc_open_device_property,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+
+
+void easyproc_InitDriver(struct easyproc_driver_info *pdriver,
+ char *procId,
+ void (*show_driver_info)(struct seq_file *),
+ void (*show_device_info)(struct seq_file *, void *))
+{
+ memset(pdriver, 0, sizeof(struct easyproc_driver_info));
+ pdriver->ProcId = procId;
+ if (pdriver->ProcId == NULL)
+ ERRDRV("ProcId cannot be NULL (trouble ahead)!");
+ pdriver->Show_driver_info = show_driver_info;
+ pdriver->Show_device_info = show_device_info;
+ if (pdriver->ProcDir == NULL)
+ pdriver->ProcDir = createProcDir(pdriver->ProcId, NULL);
+ if ((pdriver->ProcDir != NULL) && (pdriver->ProcDriverDir == NULL))
+ pdriver->ProcDriverDir = createProcDir("driver",
+ pdriver->ProcDir);
+ if ((pdriver->ProcDir != NULL) && (pdriver->ProcDeviceDir == NULL))
+ pdriver->ProcDeviceDir = createProcDir("device",
+ pdriver->ProcDir);
+ if ((pdriver->ProcDriverDir != NULL) &&
+ (pdriver->ProcDriverDiagFile == NULL)) {
+ pdriver->ProcDriverDiagFile =
+ proc_create_data("diag", 0,
+ pdriver->ProcDriverDir,
+ &proc_fops_driver, pdriver);
+ if (pdriver->ProcDriverDiagFile == NULL)
+ ERRDRV("failed to register /proc/%s/driver/diag entry",
+ pdriver->ProcId);
+ }
+}
+EXPORT_SYMBOL_GPL(easyproc_InitDriver);
+
+
+
+void easyproc_InitDriverEx(struct easyproc_driver_info *pdriver,
+ char *procId,
+ void (*show_driver_info)(struct seq_file *),
+ void (*show_device_info)(struct seq_file *, void *),
+ void (*write_driver_info)(char *buf, size_t count,
+ loff_t *ppos),
+ void (*write_device_info)(char *buf, size_t count,
+ loff_t *ppos, void *p))
+{
+ easyproc_InitDriver(pdriver, procId,
+ show_driver_info, show_device_info);
+ pdriver->Write_driver_info = write_driver_info;
+ pdriver->Write_device_info = write_device_info;
+}
+EXPORT_SYMBOL_GPL(easyproc_InitDriverEx);
+
+
+
+void easyproc_DeInitDriver(struct easyproc_driver_info *pdriver)
+{
+ if (pdriver->ProcDriverDiagFile != NULL) {
+ remove_proc_entry("diag", pdriver->ProcDriverDir);
+ pdriver->ProcDriverDiagFile = NULL;
+ }
+ if (pdriver->ProcDriverDir != NULL) {
+ remove_proc_entry("driver", pdriver->ProcDir);
+ pdriver->ProcDriverDir = NULL;
+ }
+ if (pdriver->ProcDeviceDir != NULL) {
+ remove_proc_entry("device", pdriver->ProcDir);
+ pdriver->ProcDeviceDir = NULL;
+ }
+ if (pdriver->ProcDir != NULL) {
+ remove_proc_entry(pdriver->ProcId, NULL);
+ pdriver->ProcDir = NULL;
+ }
+ pdriver->ProcId = NULL;
+ pdriver->Show_driver_info = NULL;
+ pdriver->Show_device_info = NULL;
+ pdriver->Write_driver_info = NULL;
+ pdriver->Write_device_info = NULL;
+}
+EXPORT_SYMBOL_GPL(easyproc_DeInitDriver);
+
+
+
+void easyproc_InitDevice(struct easyproc_driver_info *pdriver,
+ struct easyproc_device_info *p, int devno,
+ void *devdata)
+{
+ if ((pdriver->ProcDeviceDir != NULL) && (p->procDevicexDir == NULL)) {
+ char s[29];
+ sprintf(s, "%d", devno);
+ p->procDevicexDir = createProcDir(s, pdriver->ProcDeviceDir);
+ p->devno = devno;
+ }
+ p->devdata = devdata;
+ p->pdriver = pdriver;
+ p->devno = devno;
+ if ((p->procDevicexDir != NULL) && (p->procDevicexDiagFile == NULL)) {
+ p->procDevicexDiagFile =
+ proc_create_data("diag", 0, p->procDevicexDir,
+ &proc_fops_device, p);
+ if (p->procDevicexDiagFile == NULL)
+ ERRDEVX(devno, "failed to register /proc/%s/device/%d/diag entry",
+ pdriver->ProcId, devno
+ );
+ }
+ memset(&(p->device_property_info[0]), 0,
+ sizeof(p->device_property_info));
+}
+EXPORT_SYMBOL_GPL(easyproc_InitDevice);
+
+
+
+void easyproc_CreateDeviceProperty(struct easyproc_device_info *p,
+ void (*show_property_info)(struct seq_file *, void *),
+ char *property_name)
+{
+ size_t i;
+ struct easyproc_device_property_info *px = NULL;
+
+ if (p->procDevicexDir == NULL) {
+ ERRDRV("state error");
+ return;
+ }
+ for (i = 0; i < ARRAY_SIZE(p->device_property_info); i++) {
+ if (p->device_property_info[i].procEntry == NULL) {
+ px = &(p->device_property_info[i]);
+ break;
+ }
+ }
+ if (!px) {
+ ERRDEVX(p->devno, "too many device properties");
+ return;
+ }
+ px->devdata = p->devdata;
+ px->pdriver = p->pdriver;
+ px->procEntry = proc_create_data(property_name, 0, p->procDevicexDir,
+ &proc_fops_device_property, px);
+ if (strlen(property_name)+1 > sizeof(px->property_name)) {
+ ERRDEVX(p->devno, "device property name %s too long",
+ property_name);
+ return;
+ }
+ strcpy(px->property_name, property_name);
+ if (px->procEntry == NULL) {
+ ERRDEVX(p->devno, "failed to register /proc/%s/device/%d/%s entry",
+ p->pdriver->ProcId, p->devno, property_name
+ );
+ return;
+ }
+ px->show_device_property_info = show_property_info;
+}
+EXPORT_SYMBOL_GPL(easyproc_CreateDeviceProperty);
+
+
+
+void easyproc_DeInitDevice(struct easyproc_driver_info *pdriver,
+ struct easyproc_device_info *p, int devno)
+{
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(p->device_property_info); i++) {
+ if (p->device_property_info[i].procEntry != NULL) {
+ struct easyproc_device_property_info *px =
+ &(p->device_property_info[i]);
+ remove_proc_entry(px->property_name, p->procDevicexDir);
+ px->procEntry = NULL;
+ }
+ }
+ if (p->procDevicexDiagFile != NULL) {
+ remove_proc_entry("diag", p->procDevicexDir);
+ p->procDevicexDiagFile = NULL;
+ }
+ if (p->procDevicexDir != NULL) {
+ char s[29];
+ sprintf(s, "%d", devno);
+ remove_proc_entry(s, pdriver->ProcDeviceDir);
+ p->procDevicexDir = NULL;
+ }
+ p->devdata = NULL;
+ p->pdriver = NULL;
+}
+EXPORT_SYMBOL_GPL(easyproc_DeInitDevice);
+
+
+
+static int seq_show_driver(struct seq_file *seq, void *offset)
+{
+ struct easyproc_driver_info *p =
+ (struct easyproc_driver_info *)(seq->private);
+ if (!p)
+ return 0;
+ (*(p->Show_driver_info))(seq);
+ return 0;
+}
+
+
+
+static int seq_show_device(struct seq_file *seq, void *offset)
+{
+ struct easyproc_device_info *p =
+ (struct easyproc_device_info *)(seq->private);
+ if ((!p) || (!(p->pdriver)))
+ return 0;
+ (*(p->pdriver->Show_device_info))(seq, p->devdata);
+ return 0;
+}
+
+
+
+static int seq_show_device_property(struct seq_file *seq, void *offset)
+{
+ struct easyproc_device_property_info *p =
+ (struct easyproc_device_property_info *)(seq->private);
+ if ((!p) || (!(p->show_device_property_info)))
+ return 0;
+ (*(p->show_device_property_info))(seq, p->devdata);
+ return 0;
+}
+
+
+
+static ssize_t proc_write_driver(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *seq = (struct seq_file *)file->private_data;
+ struct easyproc_driver_info *p = NULL;
+ char local_buf[256];
+ if (seq == NULL)
+ return 0;
+ p = (struct easyproc_driver_info *)(seq->private);
+ if ((!p) || (!(p->Write_driver_info)))
+ return 0;
+ if (count >= sizeof(local_buf))
+ return -ENOMEM;
+ if (copy_from_user(local_buf, buffer, count))
+ return -EFAULT;
+ local_buf[count] = '\0'; /* be friendly */
+ (*(p->Write_driver_info))(local_buf, count, ppos);
+ return count;
+}
+
+
+
+static ssize_t proc_write_device(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *seq = (struct seq_file *)file->private_data;
+ struct easyproc_device_info *p = NULL;
+ char local_buf[256];
+ if (seq == NULL)
+ return 0;
+ p = (struct easyproc_device_info *)(seq->private);
+ if ((!p) || (!(p->pdriver)) || (!(p->pdriver->Write_device_info)))
+ return 0;
+ if (count >= sizeof(local_buf))
+ return -ENOMEM;
+ if (copy_from_user(local_buf, buffer, count))
+ return -EFAULT;
+ local_buf[count] = '\0'; /* be friendly */
+ (*(p->pdriver->Write_device_info))(local_buf, count, ppos, p->devdata);
+ return count;
+}
diff --git a/drivers/staging/unisys/visorutil/easyproc.h b/drivers/staging/unisys/visorutil/easyproc.h
new file mode 100644
index 00000000000..a1b4df75f3e
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/easyproc.h
@@ -0,0 +1,86 @@
+/* easyproc.h
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 of the License, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/** @file *********************************************************************
+ *
+ * This describes the interfaces necessary for a simple /proc file
+ * implementation for a driver.
+ *
+ ******************************************************************************
+ */
+
+#ifndef __EASYPROC_H__
+#define __EASYPROC_H__
+
+#include "timskmod.h"
+
+
+struct easyproc_driver_info {
+ struct proc_dir_entry *ProcDir;
+ struct proc_dir_entry *ProcDriverDir;
+ struct proc_dir_entry *ProcDriverDiagFile;
+ struct proc_dir_entry *ProcDeviceDir;
+ char *ProcId;
+ void (*Show_device_info)(struct seq_file *seq, void *p);
+ void (*Show_driver_info)(struct seq_file *seq);
+ void (*Write_device_info)(char *buf, size_t count,
+ loff_t *ppos, void *p);
+ void (*Write_driver_info)(char *buf, size_t count, loff_t *ppos);
+};
+
+/* property is a file under /proc/<x>/device/<x>/<property_name> */
+struct easyproc_device_property_info {
+ char property_name[25];
+ struct proc_dir_entry *procEntry;
+ struct easyproc_driver_info *pdriver;
+ void *devdata;
+ void (*show_device_property_info)(struct seq_file *seq, void *p);
+};
+
+struct easyproc_device_info {
+ struct proc_dir_entry *procDevicexDir;
+ struct proc_dir_entry *procDevicexDiagFile;
+ struct easyproc_driver_info *pdriver;
+ void *devdata;
+ int devno;
+ /* allow for a number of custom properties for each device: */
+ struct easyproc_device_property_info device_property_info[10];
+};
+
+void easyproc_InitDevice(struct easyproc_driver_info *pdriver,
+ struct easyproc_device_info *p, int devno,
+ void *devdata);
+void easyproc_DeInitDevice(struct easyproc_driver_info *pdriver,
+ struct easyproc_device_info *p, int devno);
+void easyproc_InitDriver(struct easyproc_driver_info *pdriver,
+ char *procId,
+ void (*show_driver_info)(struct seq_file *),
+ void (*show_device_info)(struct seq_file *, void *));
+void easyproc_InitDriverEx(struct easyproc_driver_info *pdriver,
+ char *procId,
+ void (*show_driver_info)(struct seq_file *),
+ void (*show_device_info)(struct seq_file *, void *),
+ void (*Write_driver_info)(char *buf, size_t count,
+ loff_t *ppos),
+ void (*Write_device_info)(char *buf, size_t count,
+ loff_t *ppos, void *p));
+void easyproc_DeInitDriver(struct easyproc_driver_info *pdriver);
+void easyproc_CreateDeviceProperty(struct easyproc_device_info *p,
+ void (*show_property_info)(struct seq_file *, void *),
+ char *property_name);
+
+#endif
diff --git a/drivers/staging/unisys/visorutil/memregion.h b/drivers/staging/unisys/visorutil/memregion.h
new file mode 100644
index 00000000000..a1fce7bb1e8
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/memregion.h
@@ -0,0 +1,43 @@
+/* memregion.h
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 of the License, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __MEMREGION_H__
+#define __MEMREGION_H__
+
+#include "timskmod.h"
+
+/* MEMREGION is an opaque structure to users.
+ * Fields are declared only in the implementation .c files.
+ */
+typedef struct MEMREGION_Tag MEMREGION;
+
+MEMREGION *memregion_create(HOSTADDRESS physaddr, ulong nbytes);
+MEMREGION *memregion_create_overlapped(MEMREGION *parent,
+ ulong offset, ulong nbytes);
+int memregion_resize(MEMREGION *memregion, ulong newsize);
+int memregion_read(MEMREGION *memregion,
+ ulong offset, void *dest, ulong nbytes);
+int memregion_write(MEMREGION *memregion,
+ ulong offset, void *src, ulong nbytes);
+void memregion_destroy(MEMREGION *memregion);
+HOSTADDRESS memregion_get_physaddr(MEMREGION *memregion);
+ulong memregion_get_nbytes(MEMREGION *memregion);
+void memregion_dump(MEMREGION *memregion, char *s,
+ ulong off, ulong len, struct seq_file *seq);
+void *memregion_get_pointer(MEMREGION *memregion);
+
+#endif
diff --git a/drivers/staging/unisys/visorutil/memregion_direct.c b/drivers/staging/unisys/visorutil/memregion_direct.c
new file mode 100644
index 00000000000..fbb460f69bd
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/memregion_direct.c
@@ -0,0 +1,221 @@
+/* memregion_direct.c
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 of the License, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * This is an implementation of memory regions that can be used to read/write
+ * channel memory (in main memory of the host system) from code running in
+ * a virtual partition.
+ */
+#include "uniklog.h"
+#include "timskmod.h"
+#include "memregion.h"
+
+#define MYDRVNAME "memregion"
+
+struct MEMREGION_Tag {
+ HOSTADDRESS physaddr;
+ ulong nbytes;
+ void *mapped;
+ BOOL requested;
+ BOOL overlapped;
+};
+
+static BOOL mapit(MEMREGION *memregion);
+static void unmapit(MEMREGION *memregion);
+
+MEMREGION *
+memregion_create(HOSTADDRESS physaddr, ulong nbytes)
+{
+ MEMREGION *rc = NULL;
+ MEMREGION *memregion = kmalloc(sizeof(MEMREGION),
+ GFP_KERNEL|__GFP_NORETRY);
+ if (memregion == NULL) {
+ ERRDRV("memregion_create allocation failed");
+ return NULL;
+ }
+ memset(memregion, 0, sizeof(MEMREGION));
+ memregion->physaddr = physaddr;
+ memregion->nbytes = nbytes;
+ memregion->overlapped = FALSE;
+ if (!mapit(memregion))
+ RETPTR(NULL);
+ RETPTR(memregion);
+
+Away:
+ if (rc == NULL) {
+ if (memregion != NULL) {
+ memregion_destroy(memregion);
+ memregion = NULL;
+ }
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(memregion_create);
+
+MEMREGION *
+memregion_create_overlapped(MEMREGION *parent, ulong offset, ulong nbytes)
+{
+ MEMREGION *memregion = NULL;
+
+ if (parent == NULL) {
+ ERRDRV("%s parent is NULL", __func__);
+ return NULL;
+ }
+ if (parent->mapped == NULL) {
+ ERRDRV("%s parent is not mapped!", __func__);
+ return NULL;
+ }
+ if ((offset >= parent->nbytes) ||
+ ((offset + nbytes) >= parent->nbytes)) {
+ ERRDRV("%s range (%lu,%lu) out of parent range",
+ __func__, offset, nbytes);
+ return NULL;
+ }
+ memregion = kmalloc(sizeof(MEMREGION), GFP_KERNEL|__GFP_NORETRY);
+ if (memregion == NULL) {
+ ERRDRV("%s allocation failed", __func__);
+ return NULL;
+ }
+ memset(memregion, 0, sizeof(MEMREGION));
+ memregion->physaddr = parent->physaddr + offset;
+ memregion->nbytes = nbytes;
+ memregion->mapped = ((u8 *) (parent->mapped)) + offset;
+ memregion->requested = FALSE;
+ memregion->overlapped = TRUE;
+ return memregion;
+}
+EXPORT_SYMBOL_GPL(memregion_create_overlapped);
+
+
+static BOOL
+mapit(MEMREGION *memregion)
+{
+ ulong physaddr = (ulong) (memregion->physaddr);
+ ulong nbytes = memregion->nbytes;
+
+ memregion->requested = FALSE;
+ if (!request_mem_region(physaddr, nbytes, MYDRVNAME))
+ ERRDRV("cannot reserve channel memory @0x%lx for 0x%lx-- no big deal", physaddr, nbytes);
+ else
+ memregion->requested = TRUE;
+ memregion->mapped = ioremap_cache(physaddr, nbytes);
+ if (memregion->mapped == NULL) {
+ ERRDRV("cannot ioremap_cache channel memory @0x%lx for 0x%lx",
+ physaddr, nbytes);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+unmapit(MEMREGION *memregion)
+{
+ if (memregion->mapped != NULL) {
+ iounmap(memregion->mapped);
+ memregion->mapped = NULL;
+ }
+ if (memregion->requested) {
+ release_mem_region((ulong) (memregion->physaddr),
+ memregion->nbytes);
+ memregion->requested = FALSE;
+ }
+}
+
+HOSTADDRESS
+memregion_get_physaddr(MEMREGION *memregion)
+{
+ return memregion->physaddr;
+}
+EXPORT_SYMBOL_GPL(memregion_get_physaddr);
+
+ulong
+memregion_get_nbytes(MEMREGION *memregion)
+{
+ return memregion->nbytes;
+}
+EXPORT_SYMBOL_GPL(memregion_get_nbytes);
+
+void *
+memregion_get_pointer(MEMREGION *memregion)
+{
+ return memregion->mapped;
+}
+EXPORT_SYMBOL_GPL(memregion_get_pointer);
+
+int
+memregion_resize(MEMREGION *memregion, ulong newsize)
+{
+ if (newsize == memregion->nbytes)
+ return 0;
+ if (memregion->overlapped)
+ /* no error check here - we no longer know the
+ * parent's range!
+ */
+ memregion->nbytes = newsize;
+ else {
+ unmapit(memregion);
+ memregion->nbytes = newsize;
+ if (!mapit(memregion))
+ return -1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(memregion_resize);
+
+
+static int
+memregion_readwrite(BOOL is_write,
+ MEMREGION *memregion, ulong offset,
+ void *local, ulong nbytes)
+{
+ if (offset + nbytes > memregion->nbytes) {
+ ERRDRV("memregion_readwrite offset out of range!!");
+ return -EFAULT;
+ }
+ if (is_write)
+ memcpy_toio(memregion->mapped + offset, local, nbytes);
+ else
+ memcpy_fromio(local, memregion->mapped + offset, nbytes);
+
+ return 0;
+}
+
+int
+memregion_read(MEMREGION *memregion, ulong offset, void *dest, ulong nbytes)
+{
+ return memregion_readwrite(FALSE, memregion, offset, dest, nbytes);
+}
+EXPORT_SYMBOL_GPL(memregion_read);
+
+int
+memregion_write(MEMREGION *memregion, ulong offset, void *src, ulong nbytes)
+{
+ return memregion_readwrite(TRUE, memregion, offset, src, nbytes);
+}
+EXPORT_SYMBOL_GPL(memregion_write);
+
+void
+memregion_destroy(MEMREGION *memregion)
+{
+ if (memregion == NULL)
+ return;
+ if (!memregion->overlapped)
+ unmapit(memregion);
+ kfree(memregion);
+}
+EXPORT_SYMBOL_GPL(memregion_destroy);
+
diff --git a/drivers/staging/unisys/visorutil/periodic_work.c b/drivers/staging/unisys/visorutil/periodic_work.c
new file mode 100644
index 00000000000..1350b8ef59a
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/periodic_work.c
@@ -0,0 +1,230 @@
+/* periodic_work.c
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 of the License, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * Helper functions to schedule periodic work in Linux kernel mode.
+ */
+
+#include "uniklog.h"
+#include "timskmod.h"
+#include "periodic_work.h"
+
+#define MYDRVNAME "periodic_work"
+
+
+
+struct PERIODIC_WORK_Tag {
+ rwlock_t lock;
+ struct delayed_work work;
+ void (*workfunc)(void *);
+ void *workfuncarg;
+ BOOL is_scheduled;
+ BOOL want_to_stop;
+ ulong jiffy_interval;
+ struct workqueue_struct *workqueue;
+ const char *devnam;
+};
+
+
+
+static void periodic_work_func(struct work_struct *work)
+{
+ PERIODIC_WORK *periodic_work =
+ container_of(work, struct PERIODIC_WORK_Tag, work.work);
+ (*periodic_work->workfunc)(periodic_work->workfuncarg);
+}
+
+
+
+PERIODIC_WORK *periodic_work_create(ulong jiffy_interval,
+ struct workqueue_struct *workqueue,
+ void (*workfunc)(void *),
+ void *workfuncarg,
+ const char *devnam)
+{
+ PERIODIC_WORK *periodic_work = kmalloc(sizeof(PERIODIC_WORK),
+ GFP_KERNEL|__GFP_NORETRY);
+ if (periodic_work == NULL) {
+ ERRDRV("periodic_work allocation failed ");
+ return NULL;
+ }
+ memset(periodic_work, '\0', sizeof(PERIODIC_WORK));
+ rwlock_init(&periodic_work->lock);
+ periodic_work->jiffy_interval = jiffy_interval;
+ periodic_work->workqueue = workqueue;
+ periodic_work->workfunc = workfunc;
+ periodic_work->workfuncarg = workfuncarg;
+ periodic_work->devnam = devnam;
+ return periodic_work;
+}
+EXPORT_SYMBOL_GPL(periodic_work_create);
+
+
+
+void periodic_work_destroy(PERIODIC_WORK *periodic_work)
+{
+ if (periodic_work == NULL)
+ return;
+ kfree(periodic_work);
+}
+EXPORT_SYMBOL_GPL(periodic_work_destroy);
+
+
+
+/** Call this from your periodic work worker function to schedule the next
+ * call.
+ * If this function returns FALSE, there was a failure and the
+ * periodic work is no longer scheduled
+ */
+BOOL periodic_work_nextperiod(PERIODIC_WORK *periodic_work)
+{
+ BOOL rc = FALSE;
+ write_lock(&periodic_work->lock);
+ if (periodic_work->want_to_stop) {
+ periodic_work->is_scheduled = FALSE;
+ periodic_work->want_to_stop = FALSE;
+ RETBOOL(TRUE); /* yes, TRUE; see periodic_work_stop() */
+ } else if (queue_delayed_work(periodic_work->workqueue,
+ &periodic_work->work,
+ periodic_work->jiffy_interval) < 0) {
+ ERRDEV(periodic_work->devnam, "queue_delayed_work failed!");
+ periodic_work->is_scheduled = FALSE;
+ RETBOOL(FALSE);
+ }
+ RETBOOL(TRUE);
+Away:
+ write_unlock(&periodic_work->lock);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(periodic_work_nextperiod);
+
+
+
+/** This function returns TRUE iff new periodic work was actually started.
+ * If this function returns FALSE, then no work was started
+ * (either because it was already started, or because of a failure).
+ */
+BOOL periodic_work_start(PERIODIC_WORK *periodic_work)
+{
+ BOOL rc = FALSE;
+
+ write_lock(&periodic_work->lock);
+ if (periodic_work->is_scheduled)
+ RETBOOL(FALSE);
+ if (periodic_work->want_to_stop) {
+ ERRDEV(periodic_work->devnam,
+ "dev_start_periodic_work failed!");
+ RETBOOL(FALSE);
+ }
+ INIT_DELAYED_WORK(&periodic_work->work, &periodic_work_func);
+ if (queue_delayed_work(periodic_work->workqueue,
+ &periodic_work->work,
+ periodic_work->jiffy_interval) < 0) {
+ ERRDEV(periodic_work->devnam,
+ "%s queue_delayed_work failed!", __func__);
+ RETBOOL(FALSE);
+ }
+ periodic_work->is_scheduled = TRUE;
+ RETBOOL(TRUE);
+Away:
+ write_unlock(&periodic_work->lock);
+ return rc;
+
+}
+EXPORT_SYMBOL_GPL(periodic_work_start);
+
+
+
+
+/** This function returns TRUE iff your call actually stopped the periodic
+ * work.
+ *
+ * -- PAY ATTENTION... this is important --
+ *
+ * NO NO #1
+ *
+ * Do NOT call this function from some function that is running on the
+ * same workqueue as the work you are trying to stop might be running
+ * on! If you violate this rule, periodic_work_stop() MIGHT work, but it
+ * also MIGHT get hung up in an infinite loop saying
+ * "waiting for delayed work...". This will happen if the delayed work
+ * you are trying to cancel has been put in the workqueue list, but can't
+ * run yet because we are running that same workqueue thread right now.
+ *
+ * Bottom line: If you need to call periodic_work_stop() from a workitem,
+ * be sure the workitem is on a DIFFERENT workqueue than the workitem that
+ * you are trying to cancel.
+ *
+ * If I could figure out some way to check for this "no no" condition in
+ * the code, I would. It would have saved me the trouble of writing this
+ * long comment. And also, don't think this is some "theoretical" race
+ * condition. It is REAL, as I have spent the day chasing it.
+ *
+ * NO NO #2
+ *
+ * Take close note of the locks that you own when you call this function.
+ * You must NOT own any locks that are needed by the periodic work
+ * function that is currently installed. If you DO, a deadlock may result,
+ * because stopping the periodic work often involves waiting for the last
+ * iteration of the periodic work function to complete. Again, if you hit
+ * this deadlock, you will get hung up in an infinite loop saying
+ * "waiting for delayed work...".
+ */
+BOOL periodic_work_stop(PERIODIC_WORK *periodic_work)
+{
+ BOOL stopped_something = FALSE;
+
+ write_lock(&periodic_work->lock);
+ stopped_something = periodic_work->is_scheduled &&
+ (!periodic_work->want_to_stop);
+ while (periodic_work->is_scheduled) {
+ periodic_work->want_to_stop = TRUE;
+ if (cancel_delayed_work(&periodic_work->work)) {
+ /* We get here if the delayed work was pending as
+ * delayed work, but was NOT run.
+ */
+ ASSERT(periodic_work->is_scheduled);
+ periodic_work->is_scheduled = FALSE;
+ } else {
+ /* If we get here, either the delayed work:
+ * - was run, OR,
+ * - is running RIGHT NOW on another processor, OR,
+ * - wasn't even scheduled (there is a miniscule
+ * timing window where this could be the case)
+ * flush_workqueue() would make sure it is finished
+ * executing, but that still isn't very useful, which
+ * explains the loop...
+ */
+ }
+ if (periodic_work->is_scheduled) {
+ write_unlock(&periodic_work->lock);
+ WARNDEV(periodic_work->devnam,
+ "waiting for delayed work...");
+ /* We rely on the delayed work function running here,
+ * and eventually calling periodic_work_nextperiod(),
+ * which will see that want_to_stop is set, and
+ * subsequently clear is_scheduled.
+ */
+ SLEEPJIFFIES(10);
+ write_lock(&periodic_work->lock);
+ } else
+ periodic_work->want_to_stop = FALSE;
+ }
+ write_unlock(&periodic_work->lock);
+ return stopped_something;
+}
+EXPORT_SYMBOL_GPL(periodic_work_stop);
diff --git a/drivers/staging/unisys/visorutil/procobjecttree.c b/drivers/staging/unisys/visorutil/procobjecttree.c
new file mode 100644
index 00000000000..f59b37e27d1
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/procobjecttree.c
@@ -0,0 +1,334 @@
+/* procobjecttree.c
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 of the License, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#include "procobjecttree.h"
+
+#define MYDRVNAME "procobjecttree"
+
+
+
+/** This is context info that we stash in each /proc file entry, which we
+ * need in order to call the callback function that supplies the /proc read
+ * info for that file.
+ */
+typedef struct {
+ void (*show_property)(struct seq_file *, void *, int);
+ MYPROCOBJECT *procObject;
+ int propertyIndex;
+
+} PROCDIRENTRYCONTEXT;
+
+/** This describes the attributes of a tree rooted at
+ * <procDirRoot>/<name[0]>/<name[1]>/...
+ * Properties for each object of this type will be located under
+ * <procDirRoot>/<name[0]>/<name[1]>/.../<objectName>/<propertyName>.
+ */
+struct MYPROCTYPE_Tag {
+ const char **name; /**< node names for this type, ending with NULL */
+ int nNames; /**< num of node names in <name> */
+
+ /** root dir for this type tree in /proc */
+ struct proc_dir_entry *procDirRoot;
+
+ struct proc_dir_entry **procDirs; /**< for each node in <name> */
+
+ /** bottom dir where objects will be rooted; i.e., this is
+ * <procDirRoot>/<name[0]>/<name[1]>/.../, which is the same as the
+ * last entry in the <procDirs> array. */
+ struct proc_dir_entry *procDir;
+
+ /** name for each property that objects of this type can have */
+ const char **propertyNames;
+
+ int nProperties; /**< num of names in <propertyNames> */
+
+ /** Call this, passing MYPROCOBJECT.context and the property index
+ * whenever someone reads the proc entry */
+ void (*show_property)(struct seq_file *, void *, int);
+};
+
+
+
+struct MYPROCOBJECT_Tag {
+ MYPROCTYPE *type;
+
+ /** This is the name of the dir node in /proc under which the
+ * properties of this object will appear as files. */
+ char *name;
+
+ int namesize; /**< number of bytes allocated for name */
+ void *context; /**< passed to MYPROCTYPE.show_property */
+
+ /** <type.procDirRoot>/<type.name[0]>/<type.name[1]>/.../<name> */
+ struct proc_dir_entry *procDir;
+
+ /** a proc dir entry for each of the properties of the object;
+ * properties are identified in MYPROCTYPE.propertyNames, so each of
+ * the <procDirProperties> describes a single file like
+ * <type.procDirRoot>/<type.name[0]>/<type.name[1]>/...
+ * /<name>/<propertyName>
+ */
+ struct proc_dir_entry **procDirProperties;
+
+ /** this is a holding area for the context information that is needed
+ * to run the /proc callback function */
+ PROCDIRENTRYCONTEXT *procDirPropertyContexts;
+};
+
+
+
+static struct proc_dir_entry *
+createProcDir(const char *name, struct proc_dir_entry *parent)
+{
+ struct proc_dir_entry *p = proc_mkdir_mode(name, S_IFDIR, parent);
+ if (p == NULL)
+ ERRDRV("failed to create /proc directory %s", name);
+ return p;
+}
+
+static struct proc_dir_entry *
+createProcFile(const char *name, struct proc_dir_entry *parent,
+ const struct file_operations *fops, void *data)
+{
+ struct proc_dir_entry *p = proc_create_data(name, 0, parent,
+ fops, data);
+ if (p == NULL)
+ ERRDRV("failed to create /proc file %s", name);
+ return p;
+}
+
+static int seq_show(struct seq_file *seq, void *offset);
+static int proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, seq_show, PDE_DATA(inode));
+}
+
+static const struct file_operations proc_fops = {
+ .open = proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+
+
+MYPROCTYPE *proc_CreateType(struct proc_dir_entry *procDirRoot,
+ const char **name,
+ const char **propertyNames,
+ void (*show_property)(struct seq_file *,
+ void *, int))
+{
+ int i = 0;
+ MYPROCTYPE *rc = NULL, *type = NULL;
+ struct proc_dir_entry *parent = NULL;
+
+ if (procDirRoot == NULL)
+ FAIL("procDirRoot cannot be NULL!", 0);
+ if (name == NULL || name[0] == NULL)
+ FAIL("name must contain at least 1 node name!", 0);
+ type = kmalloc(sizeof(MYPROCTYPE), GFP_KERNEL|__GFP_NORETRY);
+ if (type == NULL)
+ FAIL("out of memory", 0);
+ memset(type, 0, sizeof(MYPROCTYPE));
+ type->name = name;
+ type->propertyNames = propertyNames;
+ type->nProperties = 0;
+ type->nNames = 0;
+ type->show_property = show_property;
+ type->procDirRoot = procDirRoot;
+ if (type->propertyNames != 0)
+ while (type->propertyNames[type->nProperties] != NULL)
+ type->nProperties++;
+ while (type->name[type->nNames] != NULL)
+ type->nNames++;
+ type->procDirs = kmalloc((type->nNames+1)*
+ sizeof(struct proc_dir_entry *),
+ GFP_KERNEL|__GFP_NORETRY);
+ if (type->procDirs == NULL)
+ FAIL("out of memory", 0);
+ memset(type->procDirs, 0, (type->nNames + 1) *
+ sizeof(struct proc_dir_entry *));
+ parent = procDirRoot;
+ for (i = 0; i < type->nNames; i++) {
+ type->procDirs[i] = createProcDir(type->name[i], parent);
+ if (type->procDirs[i] == NULL)
+ RETPTR(NULL);
+ parent = type->procDirs[i];
+ }
+ type->procDir = type->procDirs[type->nNames-1];
+ RETPTR(type);
+Away:
+ if (rc == NULL) {
+ if (type != NULL) {
+ proc_DestroyType(type);
+ type = NULL;
+ }
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(proc_CreateType);
+
+
+
+void proc_DestroyType(MYPROCTYPE *type)
+{
+ if (type == NULL)
+ return;
+ if (type->procDirs != NULL) {
+ int i = type->nNames-1;
+ while (i >= 0) {
+ if (type->procDirs[i] != NULL) {
+ struct proc_dir_entry *parent = NULL;
+ if (i == 0)
+ parent = type->procDirRoot;
+ else
+ parent = type->procDirs[i-1];
+ remove_proc_entry(type->name[i], parent);
+ }
+ i--;
+ }
+ kfree(type->procDirs);
+ type->procDirs = NULL;
+ }
+ kfree(type);
+}
+EXPORT_SYMBOL_GPL(proc_DestroyType);
+
+
+
+MYPROCOBJECT *proc_CreateObject(MYPROCTYPE *type,
+ const char *name, void *context)
+{
+ MYPROCOBJECT *obj = NULL, *rc = NULL;
+ int i = 0;
+
+ if (type == NULL)
+ FAIL("type cannot be NULL", 0);
+ obj = kmalloc(sizeof(MYPROCOBJECT), GFP_KERNEL | __GFP_NORETRY);
+ if (obj == NULL)
+ FAIL("out of memory", 0);
+ memset(obj, 0, sizeof(MYPROCOBJECT));
+ obj->type = type;
+ obj->context = context;
+ if (name == NULL) {
+ obj->name = NULL;
+ obj->procDir = type->procDir;
+ } else {
+ obj->namesize = strlen(name)+1;
+ obj->name = kmalloc(obj->namesize, GFP_KERNEL | __GFP_NORETRY);
+ if (obj->name == NULL) {
+ obj->namesize = 0;
+ FAIL("out of memory", 0);
+ }
+ strcpy(obj->name, name);
+ obj->procDir = createProcDir(obj->name, type->procDir);
+ if (obj->procDir == NULL)
+ RETPTR(NULL);
+ }
+ obj->procDirPropertyContexts =
+ kmalloc((type->nProperties+1)*sizeof(PROCDIRENTRYCONTEXT),
+ GFP_KERNEL|__GFP_NORETRY);
+ if (obj->procDirPropertyContexts == NULL)
+ FAIL("out of memory", 0);
+ memset(obj->procDirPropertyContexts, 0,
+ (type->nProperties+1)*sizeof(PROCDIRENTRYCONTEXT));
+ obj->procDirProperties =
+ kmalloc((type->nProperties+1) * sizeof(struct proc_dir_entry *),
+ GFP_KERNEL|__GFP_NORETRY);
+ if (obj->procDirProperties == NULL)
+ FAIL("out of memory", 0);
+ memset(obj->procDirProperties, 0,
+ (type->nProperties+1) * sizeof(struct proc_dir_entry *));
+ for (i = 0; i < type->nProperties; i++) {
+ obj->procDirPropertyContexts[i].procObject = obj;
+ obj->procDirPropertyContexts[i].propertyIndex = i;
+ obj->procDirPropertyContexts[i].show_property =
+ type->show_property;
+ if (type->propertyNames[i][0] != '\0') {
+ /* only create properties that have names */
+ obj->procDirProperties[i] =
+ createProcFile(type->propertyNames[i],
+ obj->procDir, &proc_fops,
+ &obj->procDirPropertyContexts[i]);
+ if (obj->procDirProperties[i] == NULL)
+ RETPTR(NULL);
+ }
+ }
+ RETPTR(obj);
+Away:
+ if (rc == NULL) {
+ if (obj != NULL) {
+ proc_DestroyObject(obj);
+ obj = NULL;
+ }
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(proc_CreateObject);
+
+
+
+void proc_DestroyObject(MYPROCOBJECT *obj)
+{
+ MYPROCTYPE *type = NULL;
+ if (obj == NULL)
+ return;
+ type = obj->type;
+ if (type == NULL)
+ return;
+ if (obj->procDirProperties != NULL) {
+ int i = 0;
+ for (i = 0; i < type->nProperties; i++) {
+ if (obj->procDirProperties[i] != NULL) {
+ remove_proc_entry(type->propertyNames[i],
+ obj->procDir);
+ obj->procDirProperties[i] = NULL;
+ }
+ }
+ kfree(obj->procDirProperties);
+ obj->procDirProperties = NULL;
+ }
+ if (obj->procDirPropertyContexts != NULL) {
+ kfree(obj->procDirPropertyContexts);
+ obj->procDirPropertyContexts = NULL;
+ }
+ if (obj->procDir != NULL) {
+ if (obj->name != NULL)
+ remove_proc_entry(obj->name, type->procDir);
+ obj->procDir = NULL;
+ }
+ if (obj->name != NULL) {
+ kfree(obj->name);
+ obj->name = NULL;
+ }
+ kfree(obj);
+}
+EXPORT_SYMBOL_GPL(proc_DestroyObject);
+
+
+
+static int seq_show(struct seq_file *seq, void *offset)
+{
+ PROCDIRENTRYCONTEXT *ctx = (PROCDIRENTRYCONTEXT *)(seq->private);
+ if (ctx == NULL) {
+ ERRDRV("I don't have a freakin' clue...");
+ return 0;
+ }
+ (*ctx->show_property)(seq, ctx->procObject->context,
+ ctx->propertyIndex);
+ return 0;
+}
diff --git a/drivers/staging/unisys/visorutil/visorkmodutils.c b/drivers/staging/unisys/visorutil/visorkmodutils.c
new file mode 100644
index 00000000000..13e73266ea5
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/visorkmodutils.c
@@ -0,0 +1,721 @@
+/* timskmodutils.c
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 of the License, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#include "uniklog.h"
+#include "timskmod.h"
+
+#define MYDRVNAME "timskmodutils"
+
+BOOL Debug_Malloc_Enabled = FALSE;
+
+
+
+void myprintk(const char *myDrvName, const char *devname,
+ const char *template, ...)
+{
+ va_list ap;
+ char temp[999];
+ char *ptemp = temp;
+ char pfx[20];
+ char msg[sizeof(pfx) + strlen(myDrvName) + 50];
+
+ if (myDrvName == NULL)
+ return;
+ temp[sizeof(temp)-1] = '\0';
+ pfx[0] = '\0';
+ msg[0] = '\0';
+ va_start(ap, template);
+ vsprintf(temp, template, ap);
+ va_end(ap);
+ if (temp[0] == '<') {
+ size_t i = 0;
+ for (i = 0; i < sizeof(pfx) - 1; i++) {
+ pfx[i] = temp[i];
+ if (pfx[i] == '>' || pfx[i] == '\0') {
+ if (pfx[i] == '>')
+ ptemp = temp+i+1;
+ i++;
+ break;
+ }
+ }
+ pfx[i] = '\0';
+ }
+ if (devname == NULL)
+ sprintf(msg, "%s%s: ", pfx, myDrvName);
+ else
+ sprintf(msg, "%s%s[%s]: ", pfx, myDrvName, devname);
+ printk(KERN_INFO "%s", msg);
+
+ /* The <prefix> applies up until the \n, so we should not include
+ * it in these printks. That's why we use <ptemp> to point to the
+ * first char after the ">" in the prefix.
+ */
+ printk(KERN_INFO "%s", ptemp);
+ printk("\n");
+
+}
+
+
+
+void myprintkx(const char *myDrvName, int devno, const char *template, ...)
+{
+ va_list ap;
+ char temp[999];
+ char *ptemp = temp;
+ char pfx[20];
+ char msg[sizeof(pfx) + strlen(myDrvName) + 50];
+
+ if (myDrvName == NULL)
+ return;
+ temp[sizeof(temp)-1] = '\0';
+ pfx[0] = '\0';
+ msg[0] = '\0';
+ va_start(ap, template);
+ vsprintf(temp, template, ap);
+ va_end(ap);
+ if (temp[0] == '<') {
+ size_t i = 0;
+ for (i = 0; i < sizeof(pfx) - 1; i++) {
+ pfx[i] = temp[i];
+ if (pfx[i] == '>' || pfx[i] == '\0') {
+ if (pfx[i] == '>')
+ ptemp = temp+i+1;
+ i++;
+ break;
+ }
+ }
+ pfx[i] = '\0';
+ }
+ if (devno < 0)
+ sprintf(msg, "%s%s: ", pfx, myDrvName);
+ else
+ sprintf(msg, "%s%s[%d]: ", pfx, myDrvName, devno);
+ printk(KERN_INFO "%s", msg);
+
+ /* The <prefix> applies up until the \n, so we should not include
+ * it in these printks. That's why we use <ptemp> to point to the
+ * first char after the ">" in the prefix.
+ */
+ printk(KERN_INFO "%s", ptemp);
+ printk("\n");
+}
+
+
+
+int hexDumpWordsToBuffer(char *dest,
+ int destSize,
+ char *prefix,
+ uint32_t *src,
+ int srcWords,
+ int wordsToDumpPerLine)
+{
+ int i = 0;
+ int pos = 0;
+ char hex[(wordsToDumpPerLine * 9) + 1];
+ char *line = NULL;
+ int linesize = 1000;
+ int linelen = 0;
+ int currentlen = 0;
+ char emptystring[] = "";
+ char *pfx = prefix;
+ int baseaddr = 0;
+ int rc = 0;
+ uint8_t b1, b2, b3, b4;
+
+ line = vmalloc(linesize);
+ if (line == NULL)
+ RETINT(currentlen);
+
+ if (pfx == NULL || (strlen(pfx) > 50))
+ pfx = emptystring;
+ memset(hex, ' ', wordsToDumpPerLine * 9);
+ hex[wordsToDumpPerLine * 9] = '\0';
+ if (destSize > 0)
+ dest[0] = '\0';
+
+ for (i = 0; i < srcWords; i++) {
+ pos = i % wordsToDumpPerLine;
+ if ((pos == 0) && (i > 0)) {
+ hex[wordsToDumpPerLine * 9] = '\0';
+ linelen = sprintf(line, "%s%-6.6x %s\n", pfx,
+ baseaddr, hex);
+ if ((currentlen) + (linelen) >= destSize)
+ RETINT(currentlen);
+ strcat(dest, line);
+ currentlen += linelen;
+ memset(hex, ' ', wordsToDumpPerLine * 9);
+ baseaddr = i * 4;
+ }
+ b1 = (uint8_t)((src[i] >> 24) & 0xff);
+ b2 = (uint8_t)((src[i] >> 16) & 0xff);
+ b3 = (uint8_t)((src[i] >> 8) & 0xff);
+ b4 = (uint8_t)((src[i]) & 0xff);
+ sprintf(hex + (pos * 9), "%-2.2x%-2.2x%-2.2x%-2.2x ",
+ b1, b2, b3, b4);
+ *(hex + (pos * 9) + 9) = ' '; /* get rid of null */
+ }
+ pos = i%wordsToDumpPerLine;
+ if (i > 0) {
+ hex[wordsToDumpPerLine * 9] = '\0';
+ linelen = sprintf(line, "%s%-6.6x %s\n", pfx, baseaddr, hex);
+ if ((currentlen) + (linelen) >= destSize)
+ RETINT(currentlen);
+ strcat(dest, line);
+ currentlen += linelen;
+ }
+ RETINT(currentlen);
+
+Away:
+ if (line)
+ vfree(line);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(hexDumpWordsToBuffer);
+
+
+
+int myPrintkHexDump(char *myDrvName,
+ char *devname,
+ char *prefix,
+ char *src,
+ int srcLen,
+ int bytesToDumpPerLine)
+{
+ int i = 0;
+ int pos = 0;
+ char printable[bytesToDumpPerLine + 1];
+ char hex[(bytesToDumpPerLine*3) + 1];
+ char *line = NULL;
+ int linesize = 1000;
+ int linelen = 0;
+ int currentlen = 0;
+ char emptystring[] = "";
+ char *pfx = prefix;
+ int baseaddr = 0;
+ int rc = 0;
+ int linecount = 0;
+
+ line = vmalloc(linesize);
+ if (line == NULL)
+ RETINT(currentlen);
+
+ if (pfx == NULL || (strlen(pfx) > 50))
+ pfx = emptystring;
+ memset(hex, ' ', bytesToDumpPerLine * 3);
+ hex[bytesToDumpPerLine * 3] = '\0';
+ memset(printable, ' ', bytesToDumpPerLine);
+ printable[bytesToDumpPerLine] = '\0';
+
+ for (i = 0; i < srcLen; i++) {
+ pos = i % bytesToDumpPerLine;
+ if ((pos == 0) && (i > 0)) {
+ hex[bytesToDumpPerLine*3] = '\0';
+ linelen = sprintf(line, "%s%-6.6x %s %s",
+ pfx, baseaddr, hex, printable);
+ myprintk(myDrvName, devname, KERN_INFO "%s", line);
+ currentlen += linelen;
+ linecount++;
+ if ((linecount % 50) == 0)
+ SLEEPJIFFIES(10);
+ memset(hex, ' ', bytesToDumpPerLine*3);
+ memset(printable, ' ', bytesToDumpPerLine);
+ baseaddr = i;
+ }
+ sprintf(hex + (pos * 3), "%-2.2x ", (uint8_t)(src[i]));
+ *(hex + (pos * 3) + 3) = ' '; /* get rid of null */
+ if (((uint8_t)(src[i]) >= ' ') && (uint8_t)(src[i]) < 127)
+ printable[pos] = src[i];
+ else
+ printable[pos] = '.';
+ }
+ pos = i%bytesToDumpPerLine;
+ if (i > 0) {
+ hex[bytesToDumpPerLine*3] = '\0';
+ linelen = sprintf(line, "%s%-6.6x %s %s",
+ pfx, baseaddr, hex, printable);
+ myprintk(myDrvName, devname, KERN_INFO "%s", line);
+ currentlen += linelen;
+ }
+ RETINT(currentlen);
+
+Away:
+ if (line)
+ vfree(line);
+ return rc;
+}
+
+
+
+/** Given as input a number of seconds in #seconds, creates text describing
+ the time within #s. Also breaks down the number of seconds into component
+ days, hours, minutes, and seconds, and stores to *#days, *#hours,
+ *#minutes, and *#secondsx.
+ * @param seconds input number of seconds
+ * @param days points to a long value where the days component for the
+ * days+hours+minutes+seconds representation of #seconds will
+ * be stored
+ * @param hours points to a long value where the hours component for the
+ * days+hours+minutes+seconds representation of #seconds will
+ * be stored
+ * @param minutes points to a long value where the minutes component for the
+ * days+hours+minutes+seconds representation of #seconds will
+ * be stored
+ * @param secondsx points to a long value where the seconds component for the
+ * days+hours+minutes+seconds representation of #seconds will
+ * be stored
+ * @param s points to a character buffer where a text representation of
+ * the #seconds value will be stored. This buffer MUST be
+ * large enough to hold the resulting string; to be safe it
+ * should be at least 100 bytes long.
+ */
+void expandSeconds(time_t seconds, long *days, long *hours, long *minutes,
+ long *secondsx, char *s)
+{
+ BOOL started = FALSE;
+ char buf[99];
+
+ *days = seconds / (60*60*24);
+ seconds -= ((*days)*(60*60*24));
+ *hours = seconds / (60*60);
+ seconds -= ((*hours)*(60*60));
+ *minutes = seconds/60;
+ seconds -= ((*minutes)*60);
+ *secondsx = (long)seconds;
+ if (s == NULL)
+ RETVOID;
+ s[0] = '\0';
+ if (*days > 0) {
+ sprintf(buf, "%lu day", *days);
+ strcat(s, buf);
+ if (*days != 1)
+ strcat(s, "s");
+ started = TRUE;
+ }
+ if ((*hours > 0) || started) {
+ if (started)
+ strcat(s, ", ");
+ sprintf(buf, "%lu hour", *hours);
+ strcat(s, buf);
+ if (*hours != 1)
+ strcat(s, "s");
+ started = TRUE;
+ }
+ if ((*minutes > 0) || started) {
+ if (started)
+ strcat(s, ", ");
+ sprintf(buf, "%lu minute", *minutes);
+ strcat(s, buf);
+ if (*minutes != 1)
+ strcat(s, "s");
+ started = TRUE;
+ }
+ if (started)
+ strcat(s, ", ");
+ sprintf(buf, "%lu second", *secondsx);
+ strcat(s, buf);
+ if (*secondsx != 1)
+ strcat(s, "s");
+
+Away:
+ return;
+}
+
+
+
+/** Initialize a #MESSAGEQ for use (initially it will be empty, of course).
+ * @param q the #MESSAGEQ to initialize
+ * @ingroup messageq
+ */
+void initMessageQ(MESSAGEQ *q)
+{
+ q->qHead = NULL;
+ q->qTail = NULL;
+ sema_init(&q->nQEntries, 0); /* will block initially */
+ spin_lock_init(&q->queueLock);
+}
+
+
+
+/** Initialize #p with your data structure in #data,
+ * so you can later place #p onto a #MESSAGEQ.
+ * @param p the queue entry that will house your data structure
+ * @param data a pointer to your data structure that you want
+ * to queue
+ * @ingroup messageq
+ */
+void initMessageQEntry(MESSAGEQENTRY *p, void *data)
+{
+ p->data = data;
+ p->qNext = NULL;
+ p->qPrev = NULL;
+}
+
+
+
+MESSAGEQENTRY *dequeueMessageGuts(MESSAGEQ *q, BOOL canBlock)
+{
+ MESSAGEQENTRY *pEntry = NULL;
+ MESSAGEQENTRY *rc = NULL;
+ BOOL locked = FALSE;
+ ulong flags = 0;
+ int res = 0;
+
+ if (canBlock) {
+ /* wait for non-empty q */
+ res = down_interruptible(&q->nQEntries);
+ if (signal_pending(current)) {
+ DEBUGDRV("got signal in dequeueMessage");
+ RETPTR(NULL);
+ }
+ } else if (down_trylock(&q->nQEntries))
+ RETPTR(NULL);
+ spin_lock_irqsave(&q->queueLock, flags);
+ locked = TRUE;
+#ifdef PARANOID
+ if (q->qHead == NULL) {
+ HUHDRV("unexpected empty queue in getQueue");
+ RETPTR(NULL);
+ }
+#endif
+ pEntry = q->qHead;
+ if (pEntry == q->qTail) {
+ /* only 1 item in the queue */
+ q->qHead = NULL;
+ q->qTail = NULL;
+ } else {
+ q->qHead = pEntry->qNext;
+ q->qHead->qPrev = NULL;
+ }
+ RETPTR(pEntry);
+Away:
+ if (locked) {
+ spin_unlock_irqrestore(&q->queueLock, flags);
+ locked = FALSE;
+ }
+ return rc;
+}
+
+
+
+/** Remove the next message at the head of the FIFO queue, and return it.
+ * Wait for the queue to become non-empty if it is empty when this
+ * function is called.
+ * @param q the queue where the message is to be obtained from
+ * @return the queue entry obtained from the head of the
+ * FIFO queue, or NULL iff a signal was received
+ * while waiting for the queue to become non-empty
+ * @ingroup messageq
+ */
+MESSAGEQENTRY *dequeueMessage(MESSAGEQ *q)
+{
+ return dequeueMessageGuts(q, TRUE);
+}
+
+
+
+/** Remove the next message at the head of the FIFO queue, and return it.
+ * This function will never block (it returns NULL instead).
+ * @param q the queue where the message is to be obtained from
+ * @return the queue entry obtained from the head of the
+ * FIFO queue, or NULL iff the queue is empty.
+ * @ingroup messageq
+ */
+MESSAGEQENTRY *dequeueMessageNoBlock(MESSAGEQ *q)
+{
+ return dequeueMessageGuts(q, FALSE);
+}
+
+
+
+/** Add an entry to a FIFO queue.
+ * @param q the queue where the entry is to be added
+ * @param pEntry the entry you want to add to the queue
+ * @ingroup messageq
+ */
+void enqueueMessage(MESSAGEQ *q, MESSAGEQENTRY *pEntry)
+{
+ BOOL locked = FALSE;
+ ulong flags = 0;
+
+ spin_lock_irqsave(&q->queueLock, flags);
+ locked = TRUE;
+ if (q->qHead == NULL) {
+#ifdef PARANOID
+ if (q->qTail != NULL) {
+ HUHDRV("qHead/qTail not consistent");
+ RETVOID;
+ }
+#endif
+ q->qHead = pEntry;
+ q->qTail = pEntry;
+ pEntry->qNext = NULL;
+ pEntry->qPrev = NULL;
+ } else {
+#ifdef PARANOID
+ if (q->qTail == NULL) {
+ HUHDRV("qTail should not be NULL here");
+ RETVOID;
+ }
+#endif
+ q->qTail->qNext = pEntry;
+ pEntry->qPrev = q->qTail;
+ pEntry->qNext = NULL;
+ q->qTail = pEntry;
+ }
+ spin_unlock_irqrestore(&q->queueLock, flags);
+ locked = FALSE;
+ up(&q->nQEntries);
+ RETVOID;
+Away:
+ if (locked) {
+ spin_unlock_irqrestore(&q->queueLock, flags);
+ locked = FALSE;
+ }
+ return;
+}
+
+
+
+/** Return the number of entries in the queue.
+ * @param q the queue to be examined
+ * @return the number of entries on #q
+ * @ingroup messageq
+ */
+size_t getQueueCount(MESSAGEQ *q)
+{
+ return (size_t)__sync_fetch_and_add(&(q->nQEntries.count), 0);
+}
+
+
+
+/** Return the number of processes waiting in a standard wait queue.
+ * @param q the pointer to the wait queue to be
+ * examined
+ * @return the number of waiters
+ * @ingroup internal
+ */
+int waitQueueLen(wait_queue_head_t *q)
+{
+ struct list_head *x;
+ int count = 0;
+ list_for_each(x, &(q->task_list))
+ count++;
+ return count;
+}
+
+
+
+/** Display information about the processes on a standard wait queue.
+ * @param q the pointer to the wait queue to be
+ * examined
+ * @ingroup internal
+ */
+void debugWaitQ(wait_queue_head_t *q)
+{
+ DEBUGDRV("task_list.next= %-8.8x",
+ ((struct __wait_queue_head *)(q))->task_list.next);
+ DEBUGDRV("task_list.prev= %-8.8x",
+ ((struct __wait_queue_head *)(q))->task_list.prev);
+}
+
+
+
+/** Print the hexadecimal contents of a data buffer to a supplied print buffer.
+ * @param dest the print buffer where text characters will
+ * be written
+ * @param destSize the maximum number of bytes that can be written
+ * to #dest
+ * @param src the buffer that contains the data that is to be
+ * hex-dumped
+ * @param srcLen the number of bytes at #src to be hex-dumped
+ * @param bytesToDumpPerLine output will be formatted such that at most
+ * this many of the input data bytes will be
+ * represented on each line of output
+ * @return the number of text characters written to #dest
+ * (not including the trailing '\0' byte)
+ * @ingroup internal
+ */
+int hexDumpToBuffer(char *dest, int destSize, char *prefix, char *src,
+ int srcLen, int bytesToDumpPerLine)
+{
+ int i = 0;
+ int pos = 0;
+ char printable[bytesToDumpPerLine + 1];
+ char hex[(bytesToDumpPerLine * 3) + 1];
+ char *line = NULL;
+ int linesize = 1000;
+ int linelen = 0;
+ int currentlen = 0;
+ char emptystring[] = "";
+ char *pfx = prefix;
+ int baseaddr = 0;
+ int rc = 0;
+
+ line = vmalloc(linesize);
+ if (line == NULL)
+ RETINT(currentlen);
+
+ if (pfx == NULL || (strlen(pfx) > 50))
+ pfx = emptystring;
+ memset(hex, ' ', bytesToDumpPerLine * 3);
+ hex[bytesToDumpPerLine * 3] = '\0';
+ memset(printable, ' ', bytesToDumpPerLine);
+ printable[bytesToDumpPerLine] = '\0';
+ if (destSize > 0)
+ dest[0] = '\0';
+
+ for (i = 0; i < srcLen; i++) {
+ pos = i % bytesToDumpPerLine;
+ if ((pos == 0) && (i > 0)) {
+ hex[bytesToDumpPerLine*3] = '\0';
+ linelen = sprintf(line, "%s%-6.6x %s %s\n", pfx,
+ baseaddr, hex, printable);
+ if ((currentlen) + (linelen) >= destSize)
+ RETINT(currentlen);
+ strcat(dest, line);
+ currentlen += linelen;
+ memset(hex, ' ', bytesToDumpPerLine * 3);
+ memset(printable, ' ', bytesToDumpPerLine);
+ baseaddr = i;
+ }
+ sprintf(hex + (pos * 3), "%-2.2x ", (uint8_t)(src[i]));
+ *(hex + (pos * 3) + 3) = ' '; /* get rid of null */
+ if (((uint8_t)(src[i]) >= ' ') && (uint8_t)(src[i]) < 127)
+ printable[pos] = src[i];
+ else
+ printable[pos] = '.';
+ }
+ pos = i%bytesToDumpPerLine;
+ if (i > 0) {
+ hex[bytesToDumpPerLine * 3] = '\0';
+ linelen = sprintf(line, "%s%-6.6x %s %s\n",
+ pfx, baseaddr, hex, printable);
+ if ((currentlen) + (linelen) >= destSize)
+ RETINT(currentlen);
+ strcat(dest, line);
+ currentlen += linelen;
+ }
+ RETINT(currentlen);
+
+Away:
+ if (line)
+ vfree(line);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(hexDumpToBuffer);
+
+
+/** Callers to interfaces that set __GFP_NORETRY flag below
+ * must check for a NULL (error) result as we are telling the
+ * kernel interface that it is okay to fail.
+ */
+
+void *kmalloc_kernel(size_t siz)
+{
+ return kmalloc(siz, GFP_KERNEL | __GFP_NORETRY);
+}
+
+void *kmalloc_kernel_dma(size_t siz)
+{
+ return kmalloc(siz, GFP_KERNEL | __GFP_NORETRY|GFP_DMA);
+}
+
+void kfree_kernel(const void *p, size_t siz)
+{
+ kfree(p);
+}
+
+void *vmalloc_kernel(size_t siz)
+{
+ return vmalloc((unsigned long)(siz));
+}
+
+void vfree_kernel(const void *p, size_t siz)
+{
+ vfree((void *)(p));
+}
+
+void *pgalloc_kernel(size_t siz)
+{
+ return (void *)__get_free_pages(GFP_KERNEL|__GFP_NORETRY,
+ get_order(siz));
+}
+
+void pgfree_kernel(const void *p, size_t siz)
+{
+ free_pages((ulong)(p), get_order(siz));
+}
+
+
+
+/* Use these handy-dandy seq_file_xxx functions if you want to call some
+ * functions that write stuff into a seq_file, but you actually just want
+ * to dump that output into a buffer. Use them as follows:
+ * - call seq_file_new_buffer to create the seq_file (you supply the buf)
+ * - call whatever functions you want that take a seq_file as an argument
+ * (the buf you supplied will get the output data)
+ * - call seq_file_done_buffer to dispose of your seq_file
+ */
+struct seq_file *seq_file_new_buffer(void *buf, size_t buf_size)
+{
+ struct seq_file *rc = NULL;
+ struct seq_file *m = kmalloc_kernel(sizeof(struct seq_file));
+
+ if (m == NULL)
+ RETPTR(NULL);
+ memset(m, 0, sizeof(struct seq_file));
+ m->buf = buf;
+ m->size = buf_size;
+ RETPTR(m);
+Away:
+ if (rc == NULL) {
+ seq_file_done_buffer(m);
+ m = NULL;
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(seq_file_new_buffer);
+
+
+
+void seq_file_done_buffer(struct seq_file *m)
+{
+ if (!m)
+ return;
+ kfree(m);
+}
+EXPORT_SYMBOL_GPL(seq_file_done_buffer);
+
+
+
+void seq_hexdump(struct seq_file *seq, u8 *pfx, void *buf, ulong nbytes)
+{
+ int fmtbufsize = 100 * COVQ(nbytes, 16);
+ char *fmtbuf = NULL;
+ int i = 0;
+ if (buf == NULL) {
+ seq_printf(seq, "%s<NULL>\n", pfx);
+ return;
+ }
+ fmtbuf = kmalloc_kernel(fmtbufsize);
+ if (fmtbuf == NULL)
+ return;
+ hexDumpToBuffer(fmtbuf, fmtbufsize, pfx, (char *)(buf), nbytes, 16);
+ for (i = 0; fmtbuf[i] != '\0'; i++)
+ seq_printf(seq, "%c", fmtbuf[i]);
+ kfree(fmtbuf);
+}