staging: visorutil driver to provide common functionality to other s-Par drivers

The visorutil module is a support library required by all other s-Par
driver modules. Among its features it abstracts reading, writing, and
manipulating a block of memory.

Signed-off-by: Ken Cox <jkc@redhat.com>
Cc: Ben Romer <sparmaintainer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Ken Cox 2014-03-04 07:58:05 -06:00 committed by Greg Kroah-Hartman
parent 6b029336d9
commit 9d9baadd40
20 changed files with 3257 additions and 0 deletions

View file

@ -144,4 +144,6 @@ source "drivers/staging/gs_fpgaboot/Kconfig"
source "drivers/staging/nokia_h4p/Kconfig"
source "drivers/staging/unisys/Kconfig"
endif # STAGING

View file

@ -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/

View file

@ -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

View file

@ -0,0 +1,5 @@
#
# Makefile for Unisys SPAR drivers
#
obj-$(CONFIG_UNISYS_VISORUTIL) += visorutil/

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,558 @@
/* timskmod.h
*
* Copyright <EFBFBD> 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

View file

@ -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

View file

@ -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__ */

View file

@ -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.

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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);
}