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:
parent
6b029336d9
commit
9d9baadd40
|
@ -144,4 +144,6 @@ source "drivers/staging/gs_fpgaboot/Kconfig"
|
|||
|
||||
source "drivers/staging/nokia_h4p/Kconfig"
|
||||
|
||||
source "drivers/staging/unisys/Kconfig"
|
||||
|
||||
endif # STAGING
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# Makefile for Unisys SPAR drivers
|
||||
#
|
||||
obj-$(CONFIG_UNISYS_VISORUTIL) += visorutil/
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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__ */
|
|
@ -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.
|
||||
|
|
@ -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
|
|
@ -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);
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue