Commit 9d9baadd authored by Ken Cox's avatar Ken Cox Committed by Greg Kroah-Hartman

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: default avatarKen Cox <jkc@redhat.com>
Cc: Ben Romer <sparmaintainer@unisys.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6b029336
......@@ -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/
#
# 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
#
# Makefile for Unisys SPAR drivers
#
obj-$(CONFIG_UNISYS_VISORUTIL) += visorutil/
/* 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
/* 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
This diff is collapsed.
/* 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
/* 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__ */
#
# 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.
#
# 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
/* 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);
/* 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
This diff is collapsed.
/* 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
/* 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
/* 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);
/* 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);
/* 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;
}
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment