diff options
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); +} |