diff options
Diffstat (limited to 'pvr-source/services4/srvkm/env/linux/proc.c')
-rw-r--r-- | pvr-source/services4/srvkm/env/linux/proc.c | 1414 |
1 files changed, 1414 insertions, 0 deletions
diff --git a/pvr-source/services4/srvkm/env/linux/proc.c b/pvr-source/services4/srvkm/env/linux/proc.c new file mode 100644 index 0000000..7307257 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/proc.c @@ -0,0 +1,1414 @@ +/*************************************************************************/ /*! +@Title Proc files implementation. +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Functions for creating and reading proc filesystem entries. + Proc filesystem support must be built into the kernel for + these functions to be any use. +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/sched.h> + +#include "services_headers.h" + +#include "queue.h" +#include "resman.h" +#include "pvrmmap.h" +#include "pvr_debug.h" +#include "pvrversion.h" +#include "proc.h" +#include "perproc.h" +#include "env_perproc.h" +#include "linkage.h" + +#include "lists.h" + +// The proc entry for our /proc/pvr directory +static struct proc_dir_entry * dir; + +static const IMG_CHAR PVRProcDirRoot[] = "pvr"; + +static IMG_INT pvr_proc_open(struct inode *inode,struct file *file); +static void *pvr_proc_seq_start (struct seq_file *m, loff_t *pos); +static void pvr_proc_seq_stop (struct seq_file *m, void *v); +static void *pvr_proc_seq_next (struct seq_file *m, void *v, loff_t *pos); +static int pvr_proc_seq_show (struct seq_file *m, void *v); +static ssize_t pvr_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos); + +static struct file_operations pvr_proc_operations = +{ + .open = pvr_proc_open, + .read = seq_read, + .write = pvr_proc_write, + .llseek = seq_lseek, + .release = seq_release, +}; + +static struct seq_operations pvr_proc_seq_operations = +{ + .start = pvr_proc_seq_start, + .next = pvr_proc_seq_next, + .stop = pvr_proc_seq_stop, + .show = pvr_proc_seq_show, +}; + +static struct proc_dir_entry* g_pProcQueue; +static struct proc_dir_entry* g_pProcVersion; +static struct proc_dir_entry* g_pProcSysNodes; + +#ifdef DEBUG +static struct proc_dir_entry* g_pProcDebugLevel; +#endif + +#ifdef PVR_MANUAL_POWER_CONTROL +static struct proc_dir_entry* g_pProcPowerLevel; +#endif + + +static void ProcSeqShowVersion(struct seq_file *sfile,void* el); + +static void ProcSeqShowSysNodes(struct seq_file *sfile,void* el); +static void* ProcSeqOff2ElementSysNodes(struct seq_file * sfile, loff_t off); + +/*! +****************************************************************************** + + @Function : printAppend + + @Description + + Print into the supplied buffer at the specified offset remaining within + the specified total buffer size. + + @Input size : the total size of the buffer + + @Input off : the offset into the buffer to start printing + + @Input format : the printf format string + + @Input ... : format args + + @Return : The number of chars now in the buffer (original value of 'off' + plus number of chars added); 'size' if full. + +*****************************************************************************/ +off_t printAppend(IMG_CHAR * buffer, size_t size, off_t off, const IMG_CHAR * format, ...) +{ + IMG_INT n; + size_t space = size - (size_t)off; + va_list ap; + + va_start (ap, format); + + n = vsnprintf (buffer+off, space, format, ap); + + va_end (ap); + /* According to POSIX, n is greater than or equal to the size available if + * the print would have overflowed the buffer. Other platforms may + * return -1 if printing was truncated. + */ + if (n >= (IMG_INT)space || n < 0) + { + /* Ensure final string is terminated */ + buffer[size - 1] = 0; + return (off_t)(size - 1); + } + else + { + return (off + (off_t)n); + } +} + + +/*! +****************************************************************************** + + @Function : ProcSeq1ElementOff2Element + + @Description + + Heleper Offset -> Element function for /proc files with only one entry + without header. + + @Input sfile : seq_file object related to /proc/ file + + @Input off : the offset into the buffer (id of object) + + @Return : Pointer to element to be shown. + +*****************************************************************************/ +void* ProcSeq1ElementOff2Element(struct seq_file *sfile, loff_t off) +{ + PVR_UNREFERENCED_PARAMETER(sfile); + // Return anything that is not PVR_RPOC_SEQ_START_TOKEN and NULL + if(!off) + return (void*)2; + return NULL; +} + + +/*! +****************************************************************************** + + @Function : ProcSeq1ElementHeaderOff2Element + + @Description + + Heleper Offset -> Element function for /proc files with only one entry + with header. + + @Input sfile : seq_file object related to /proc/ file + + @Input off : the offset into the buffer (id of object) + + @Return : Pointer to element to be shown. + +*****************************************************************************/ +void* ProcSeq1ElementHeaderOff2Element(struct seq_file *sfile, loff_t off) +{ + PVR_UNREFERENCED_PARAMETER(sfile); + + if(!off) + { + return PVR_PROC_SEQ_START_TOKEN; + } + + // Return anything that is not PVR_RPOC_SEQ_START_TOKEN and NULL + if(off == 1) + return (void*)2; + + return NULL; +} + + +/*! +****************************************************************************** + + @Function : pvr_proc_open + + @Description + File opening function passed to proc_dir_entry->proc_fops for /proc entries + created by CreateProcReadEntrySeq. + + @Input inode : inode entry of opened /proc file + + @Input file : file entry of opened /proc file + + @Return : 0 if no errors + +*****************************************************************************/ +static IMG_INT pvr_proc_open(struct inode *inode,struct file *file) +{ + IMG_INT ret = seq_open(file, &pvr_proc_seq_operations); + + struct seq_file *seq = (struct seq_file*)file->private_data; + struct proc_dir_entry* pvr_proc_entry = PDE(inode); + + /* Add pointer to handlers to seq_file structure */ + seq->private = pvr_proc_entry->data; + return ret; +} + +/*! +****************************************************************************** + + @Function : pvr_proc_write + + @Description + File writing function passed to proc_dir_entry->proc_fops for /proc files. + It's exacly the same function that is used as default one (->fs/proc/generic.c), + it calls proc_dir_entry->write_proc for writing procedure. + +*****************************************************************************/ +static ssize_t pvr_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct proc_dir_entry * dp; + + PVR_UNREFERENCED_PARAMETER(ppos); + dp = PDE(inode); + + if (!dp->write_proc) + return -EIO; + + return dp->write_proc(file, buffer, count, dp->data); +} + + +/*! +****************************************************************************** + + @Function : pvr_proc_seq_start + + @Description + Seq_file start function. Detailed description of seq_file workflow can + be found here: http://tldp.org/LDP/lkmpg/2.6/html/x861.html. + This function ises off2element handler. + + @Input proc_seq_file : sequence file entry + + @Input pos : offset within file (id of entry) + + @Return : Pointer to element from we start enumeration (0 ends it) + +*****************************************************************************/ +static void *pvr_proc_seq_start (struct seq_file *proc_seq_file, loff_t *pos) +{ + PVR_PROC_SEQ_HANDLERS *handlers = (PVR_PROC_SEQ_HANDLERS*)proc_seq_file->private; + if(handlers->startstop != NULL) + handlers->startstop(proc_seq_file, IMG_TRUE); + return handlers->off2element(proc_seq_file, *pos); +} + +/*! +****************************************************************************** + + @Function : pvr_proc_seq_stop + + @Description + Seq_file stop function. Detailed description of seq_file workflow can + be found here: http://tldp.org/LDP/lkmpg/2.6/html/x861.html. + + @Input proc_seq_file : sequence file entry + + @Input v : current element pointer + +*****************************************************************************/ +static void pvr_proc_seq_stop (struct seq_file *proc_seq_file, void *v) +{ + PVR_PROC_SEQ_HANDLERS *handlers = (PVR_PROC_SEQ_HANDLERS*)proc_seq_file->private; + PVR_UNREFERENCED_PARAMETER(v); + + if(handlers->startstop != NULL) + handlers->startstop(proc_seq_file, IMG_FALSE); +} + +/*! +****************************************************************************** + + @Function : pvr_proc_seq_next + + @Description + Seq_file next element function. Detailed description of seq_file workflow can + be found here: http://tldp.org/LDP/lkmpg/2.6/html/x861.html. + It uses supplied 'next' handler for fetching next element (or 0 if there is no one) + + @Input proc_seq_file : sequence file entry + + @Input pos : offset within file (id of entry) + + @Input v : current element pointer + + @Return : next element pointer (or 0 if end) + +*****************************************************************************/ +static void *pvr_proc_seq_next (struct seq_file *proc_seq_file, void *v, loff_t *pos) +{ + PVR_PROC_SEQ_HANDLERS *handlers = (PVR_PROC_SEQ_HANDLERS*)proc_seq_file->private; + (*pos)++; + if( handlers->next != NULL) + return handlers->next( proc_seq_file, v, *pos ); + return handlers->off2element(proc_seq_file, *pos); +} + +/*! +****************************************************************************** + + @Function : pvr_proc_seq_show + + @Description + Seq_file show element function. Detailed description of seq_file workflow can + be found here: http://tldp.org/LDP/lkmpg/2.6/html/x861.html. + It call proper 'show' handler to show (dump) current element using seq_* functions + + @Input proc_seq_file : sequence file entry + + @Input v : current element pointer + + @Return : 0 if everything is OK + +*****************************************************************************/ +static int pvr_proc_seq_show (struct seq_file *proc_seq_file, void *v) +{ + PVR_PROC_SEQ_HANDLERS *handlers = (PVR_PROC_SEQ_HANDLERS*)proc_seq_file->private; + handlers->show( proc_seq_file,v ); + return 0; +} + + + +/*! +****************************************************************************** + + @Function : CreateProcEntryInDirSeq + + @Description + + Create a file under the given directory. These dynamic files can be used at + runtime to get or set information about the device. Whis version uses seq_file + interface + + @Input pdir : parent directory + + @Input name : the name of the file to create + + @Input data : aditional data that will be passed to handlers + + @Input next_handler : the function to call to provide the next element. OPTIONAL, if not + supplied, then off2element function is used instead + + @Input show_handler : the function to call to show element + + @Input off2element_handler : the function to call when it is needed to translate offest to element + + @Input startstop_handler : the function to call when output memory page starts or stops. OPTIONAL. + + @Input whandler : the function to interpret writes from the user + + @Return Ptr to proc entry , 0 for failure + + +*****************************************************************************/ +static struct proc_dir_entry* CreateProcEntryInDirSeq( + struct proc_dir_entry *pdir, + const IMG_CHAR * name, + IMG_VOID* data, + pvr_next_proc_seq_t next_handler, + pvr_show_proc_seq_t show_handler, + pvr_off2element_proc_seq_t off2element_handler, + pvr_startstop_proc_seq_t startstop_handler, + write_proc_t whandler + ) +{ + + struct proc_dir_entry * file; + mode_t mode; + + if (!dir) + { + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntryInDirSeq: cannot make proc entry /proc/%s/%s: no parent", PVRProcDirRoot, name)); + return NULL; + } + + mode = S_IFREG; + + if (show_handler) + { + mode |= S_IRUGO; + } + + if (whandler) + { + mode |= S_IWUSR; + } + + file=create_proc_entry(name, mode, pdir); + + if (file) + { + PVR_PROC_SEQ_HANDLERS *seq_handlers; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) + file->owner = THIS_MODULE; +#endif + + file->proc_fops = &pvr_proc_operations; + file->write_proc = whandler; + + /* Pass the handlers */ + file->data = kmalloc(sizeof(PVR_PROC_SEQ_HANDLERS), GFP_KERNEL); + if(file->data) + { + seq_handlers = (PVR_PROC_SEQ_HANDLERS*)file->data; + seq_handlers->next = next_handler; + seq_handlers->show = show_handler; + seq_handlers->off2element = off2element_handler; + seq_handlers->startstop = startstop_handler; + seq_handlers->data = data; + + return file; + } + } + + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntryInDirSeq: cannot make proc entry /proc/%s/%s: no memory", PVRProcDirRoot, name)); + return NULL; +} + + +/*! +****************************************************************************** + + @Function : CreateProcReadEntrySeq + + @Description + + Create a file under /proc/pvr. These dynamic files can be used at runtime + to get information about the device. Creation WILL fail if proc support is + not compiled into the kernel. That said, the Linux kernel is not even happy + to build without /proc support these days. This version uses seq_file structure + for handling content generation. + + @Input name : the name of the file to create + + @Input data : aditional data that will be passed to handlers + + @Input next_handler : the function to call to provide the next element. OPTIONAL, if not + supplied, then off2element function is used instead + + @Input show_handler : the function to call to show element + + @Input off2element_handler : the function to call when it is needed to translate offest to element + + @Input startstop_handler : the function to call when output memory page starts or stops. OPTIONAL. + + @Return Ptr to proc entry , 0 for failure + +*****************************************************************************/ +struct proc_dir_entry* CreateProcReadEntrySeq ( + const IMG_CHAR * name, + IMG_VOID* data, + pvr_next_proc_seq_t next_handler, + pvr_show_proc_seq_t show_handler, + pvr_off2element_proc_seq_t off2element_handler, + pvr_startstop_proc_seq_t startstop_handler + ) +{ + return CreateProcEntrySeq(name, + data, + next_handler, + show_handler, + off2element_handler, + startstop_handler, + NULL); +} + +/*! +****************************************************************************** + + @Function : CreateProcEntrySeq + + @Description + + @Description + + Create a file under /proc/pvr. These dynamic files can be used at runtime + to get information about the device. Creation WILL fail if proc support is + not compiled into the kernel. That said, the Linux kernel is not even happy + to build without /proc support these days. This version uses seq_file structure + for handling content generation and is fuller than CreateProcReadEntrySeq (it + supports write access); + + @Input name : the name of the file to create + + @Input data : aditional data that will be passed to handlers + + @Input next_handler : the function to call to provide the next element. OPTIONAL, if not + supplied, then off2element function is used instead + + @Input show_handler : the function to call to show element + + @Input off2element_handler : the function to call when it is needed to translate offest to element + + @Input startstop_handler : the function to call when output memory page starts or stops. OPTIONAL. + + @Input whandler : the function to interpret writes from the user + + @Return Ptr to proc entry , 0 for failure + +*****************************************************************************/ +struct proc_dir_entry* CreateProcEntrySeq ( + const IMG_CHAR * name, + IMG_VOID* data, + pvr_next_proc_seq_t next_handler, + pvr_show_proc_seq_t show_handler, + pvr_off2element_proc_seq_t off2element_handler, + pvr_startstop_proc_seq_t startstop_handler, + write_proc_t whandler + ) +{ + return CreateProcEntryInDirSeq( + dir, + name, + data, + next_handler, + show_handler, + off2element_handler, + startstop_handler, + whandler + ); +} + + + +/*! +****************************************************************************** + + @Function : CreatePerProcessProcEntrySeq + + @Description + + Create a file under /proc/pvr/<current process ID>. Apart from the + directory where the file is created, this works the same way as + CreateProcEntry. It's seq_file version. + + + + @Input name : the name of the file to create + + @Input data : aditional data that will be passed to handlers + + @Input next_handler : the function to call to provide the next element. OPTIONAL, if not + supplied, then off2element function is used instead + + @Input show_handler : the function to call to show element + + @Input off2element_handler : the function to call when it is needed to translate offest to element + + @Input startstop_handler : the function to call when output memory page starts or stops. OPTIONAL. + + @Input whandler : the function to interpret writes from the user + + @Return Ptr to proc entry , 0 for failure + +*****************************************************************************/ +struct proc_dir_entry* CreatePerProcessProcEntrySeq ( + const IMG_CHAR * name, + IMG_VOID* data, + pvr_next_proc_seq_t next_handler, + pvr_show_proc_seq_t show_handler, + pvr_off2element_proc_seq_t off2element_handler, + pvr_startstop_proc_seq_t startstop_handler, + write_proc_t whandler + ) +{ + PVRSRV_ENV_PER_PROCESS_DATA *psPerProc; + IMG_UINT32 ui32PID; + + if (!dir) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntrySeq: /proc/%s doesn't exist", PVRProcDirRoot)); + return NULL; + } + + ui32PID = OSGetCurrentProcessIDKM(); + + psPerProc = PVRSRVPerProcessPrivateData(ui32PID); + if (!psPerProc) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntrySeq: no per process data")); + + return NULL; + } + + if (!psPerProc->psProcDir) + { + IMG_CHAR dirname_buffer[256]; + IMG_CHAR dirname[256]; + IMG_INT ret; + const IMG_CHAR *proc_basename = dirname_buffer; + dirname_buffer[255] = dirname[255] = '\0'; + + OSGetProcCmdline(ui32PID, dirname_buffer, sizeof(dirname_buffer)); + PVR_DPF((PVR_DBG_MESSAGE, "Command Line of the process with ID %u is %s", ui32PID, dirname_buffer)); + + proc_basename = OSGetPathBaseName(dirname_buffer, sizeof(dirname_buffer)); + PVR_DPF((PVR_DBG_MESSAGE, "Base Name of the process with ID %u is %s\n", ui32PID, proc_basename)); + + ret = snprintf(dirname, sizeof(dirname), "%u-%s", ui32PID, proc_basename); + PVR_DPF((PVR_DBG_MESSAGE, "Creating a new process entry for %s with ID %u\n", proc_basename, ui32PID)); + + if (ret <=0 || ret >= (IMG_INT)sizeof(dirname)) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: couldn't generate per process proc directory name \"%u\"", ui32PID)); + return NULL; + } + else + { + psPerProc->psProcDir = proc_mkdir(dirname, dir); + if (!psPerProc->psProcDir) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: couldn't create per process proc directory /proc/%s/%u", + PVRProcDirRoot, ui32PID)); + return NULL; + } + } + } + + return CreateProcEntryInDirSeq(psPerProc->psProcDir, name, data, next_handler, + show_handler,off2element_handler,startstop_handler,whandler); +} + + +/*! +****************************************************************************** + + @Function : RemoveProcEntrySeq + + @Description + + Remove a single node (created using *Seq function) under /proc/pvr. + + @Input proc_entry : structure returned by Create function. + + @Return nothing + +*****************************************************************************/ +IMG_VOID RemoveProcEntrySeq( struct proc_dir_entry* proc_entry ) +{ + if (dir) + { + void* data = proc_entry->data ; + PVR_DPF((PVR_DBG_MESSAGE, "Removing /proc/%s/%s", PVRProcDirRoot, proc_entry->name)); + + remove_proc_entry(proc_entry->name, dir); + if( data) + kfree( data ); + + } +} + +/*! +****************************************************************************** + + @Function : RemovePerProcessProcEntry Seq + + @Description + + Remove a single node under the per process proc directory (created by *Seq function). + + Remove a single node (created using *Seq function) under /proc/pvr. + + @Input proc_entry : structure returned by Create function. + + @Return nothing + +*****************************************************************************/ +IMG_VOID RemovePerProcessProcEntrySeq(struct proc_dir_entry* proc_entry) +{ + PVRSRV_ENV_PER_PROCESS_DATA *psPerProc; + + psPerProc = LinuxTerminatingProcessPrivateData(); + if (!psPerProc) + { + psPerProc = PVRSRVFindPerProcessPrivateData(); + if (!psPerProc) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: can't " + "remove %s, no per process data", proc_entry->name)); + return; + } + } + + if (psPerProc->psProcDir) + { + void* data = proc_entry->data ; + PVR_DPF((PVR_DBG_MESSAGE, "Removing proc entry %s from %s", proc_entry->name, psPerProc->psProcDir->name)); + + remove_proc_entry(proc_entry->name, psPerProc->psProcDir); + if(data) + kfree( data ); + } +} + +/*! +****************************************************************************** + + @Function : pvr_read_proc_vm + + @Description + + When the user accesses the proc filesystem entry for the device, we are + called here to create the content for the 'file'. We can print anything we + want here. If the info we want to return is too big for one page ('count' + chars), we return successive chunks on each call. For a number of ways of + achieving this, refer to proc_file_read() in linux/fs/proc/generic.c. + + Here, as we are accessing lists of information, we output '1' in '*start' to + instruct proc to advance 'off' by 1 on each call. The number of chars placed + in the buffer is returned. Multiple calls are made here by the proc + filesystem until we set *eof. We can return zero without setting eof to + instruct proc to flush 'page' (causing it to be printed) if there is not + enough space left (eg for a complete line). + + @Input page : where to write the output + + @Input start : memory location into which should be written next offset + to read from. + + @Input off : the offset into the /proc file being read + + @Input count : the size of the buffer 'page' + + @Input eof : memory location into which 1 should be written when at EOF + + @Input data : data specific to this /proc file entry + + @Return : length of string written to page + +*****************************************************************************/ +static IMG_INT pvr_read_proc(IMG_CHAR *page, IMG_CHAR **start, off_t off, + IMG_INT count, IMG_INT *eof, IMG_VOID *data) +{ + /* PRQA S 0307 1 */ /* ignore warning about casting to different pointer type */ + pvr_read_proc_t *pprn = (pvr_read_proc_t *)data; + + off_t len = pprn (page, (size_t)count, off); + + if (len == END_OF_FILE) + { + len = 0; + *eof = 1; + } + else if (!len) /* not enough space in the buffer */ + { + *start = (IMG_CHAR *) 0; /* don't advance the offset */ + } + else + { + *start = (IMG_CHAR *) 1; + } + + return len; +} + + +/*! +****************************************************************************** + + @Function : CreateProcEntryInDir + + @Description + + Create a file under the given directory. These dynamic files can be used at + runtime to get or set information about the device. + + @Input pdir : parent directory + + @Input name : the name of the file to create + + @Input rhandler : the function to supply the content + + @Input whandler : the function to interpret writes from the user + + @Return success code : 0 or -errno. + +*****************************************************************************/ +static IMG_INT CreateProcEntryInDir(struct proc_dir_entry *pdir, const IMG_CHAR * name, read_proc_t rhandler, write_proc_t whandler, IMG_VOID *data) +{ + struct proc_dir_entry * file; + mode_t mode; + + if (!pdir) + { + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntryInDir: parent directory doesn't exist")); + + return -ENOMEM; + } + + mode = S_IFREG; + + if (rhandler) + { + mode |= S_IRUGO; + } + + if (whandler) + { + mode |= S_IWUSR; + } + + file = create_proc_entry(name, mode, pdir); + + if (file) + { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) + file->owner = THIS_MODULE; +#endif + file->read_proc = rhandler; + file->write_proc = whandler; + file->data = data; + + PVR_DPF((PVR_DBG_MESSAGE, "Created proc entry %s in %s", name, pdir->name)); + + return 0; + } + + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntry: cannot create proc entry %s in %s", name, pdir->name)); + + return -ENOMEM; +} + + +/*! +****************************************************************************** + + @Function : CreateProcEntry + + @Description + + Create a file under /proc/pvr. These dynamic files can be used at runtime + to get or set information about the device. + + This interface is fuller than CreateProcReadEntry, and supports write access; + it is really just a wrapper for the native linux functions. + + @Input name : the name of the file to create under /proc/pvr + + @Input rhandler : the function to supply the content + + @Input whandler : the function to interpret writes from the user + + @Return success code : 0 or -errno. + +*****************************************************************************/ +IMG_INT CreateProcEntry(const IMG_CHAR * name, read_proc_t rhandler, write_proc_t whandler, IMG_VOID *data) +{ + return CreateProcEntryInDir(dir, name, rhandler, whandler, data); +} + + +/*! +****************************************************************************** + + @Function : CreatePerProcessProcEntry + + @Description + + Create a file under /proc/pvr/<current process ID>. Apart from the + directory where the file is created, this works the same way as + CreateProcEntry. + + @Input name : the name of the file to create under the per process /proc directory + + @Input rhandler : the function to supply the content + + @Input whandler : the function to interpret writes from the user + + @Return success code : 0 or -errno. + +*****************************************************************************/ +IMG_INT CreatePerProcessProcEntry(const IMG_CHAR * name, read_proc_t rhandler, write_proc_t whandler, IMG_VOID *data) +{ + PVRSRV_ENV_PER_PROCESS_DATA *psPerProc; + IMG_UINT32 ui32PID; + + if (!dir) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: /proc/%s doesn't exist", PVRProcDirRoot)); + + return -ENOMEM; + } + + ui32PID = OSGetCurrentProcessIDKM(); + + psPerProc = PVRSRVPerProcessPrivateData(ui32PID); + if (!psPerProc) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: no per process data")); + + return -ENOMEM; + } + + if (!psPerProc->psProcDir) + { + IMG_CHAR dirname[16]; + IMG_INT ret; + + ret = snprintf(dirname, sizeof(dirname), "%u", ui32PID); + + if (ret <=0 || ret >= (IMG_INT)sizeof(dirname)) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: couldn't generate per process proc directory name \"%u\"", ui32PID)); + + return -ENOMEM; + } + else + { + psPerProc->psProcDir = proc_mkdir(dirname, dir); + if (!psPerProc->psProcDir) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: couldn't create per process proc directory /proc/%s/%u", PVRProcDirRoot, ui32PID)); + + return -ENOMEM; + } + } + } + + return CreateProcEntryInDir(psPerProc->psProcDir, name, rhandler, whandler, data); +} + + +/*! +****************************************************************************** + + @Function : CreateProcReadEntry + + @Description + + Create a file under /proc/pvr. These dynamic files can be used at runtime + to get information about the device. Creation WILL fail if proc support is + not compiled into the kernel. That said, the Linux kernel is not even happy + to build without /proc support these days. + + @Input name : the name of the file to create + + @Input handler : the function to call to provide the content + + @Return 0 for success, -errno for failure + +*****************************************************************************/ +IMG_INT CreateProcReadEntry(const IMG_CHAR * name, pvr_read_proc_t handler) +{ + struct proc_dir_entry * file; + + if (!dir) + { + PVR_DPF((PVR_DBG_ERROR, "CreateProcReadEntry: cannot make proc entry /proc/%s/%s: no parent", PVRProcDirRoot, name)); + + return -ENOMEM; + } + + /* PRQA S 0307 1 */ /* ignore warning about casting to different pointer type */ + file = create_proc_read_entry (name, S_IFREG | S_IRUGO, dir, pvr_read_proc, (IMG_VOID *)handler); + + if (file) + { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) + file->owner = THIS_MODULE; +#endif + return 0; + } + + PVR_DPF((PVR_DBG_ERROR, "CreateProcReadEntry: cannot make proc entry /proc/%s/%s: no memory", PVRProcDirRoot, name)); + + return -ENOMEM; +} + + +/*! +****************************************************************************** + + @Function : CreateProcEntries + + @Description + + Create a directory /proc/pvr and the necessary entries within it. These + dynamic files can be used at runtime to get information about the device. + Creation might fail if proc support is not compiled into the kernel or if + there is no memory + + @Input none + + @Return nothing + +*****************************************************************************/ +IMG_INT CreateProcEntries(IMG_VOID) +{ + dir = proc_mkdir (PVRProcDirRoot, NULL); + + if (!dir) + { + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntries: cannot make /proc/%s directory", PVRProcDirRoot)); + + return -ENOMEM; + } + + g_pProcQueue = CreateProcReadEntrySeq("queue", NULL, NULL, ProcSeqShowQueue, ProcSeqOff2ElementQueue, NULL); + g_pProcVersion = CreateProcReadEntrySeq("version", NULL, NULL, ProcSeqShowVersion, ProcSeq1ElementHeaderOff2Element, NULL); + g_pProcSysNodes = CreateProcReadEntrySeq("nodes", NULL, NULL, ProcSeqShowSysNodes, ProcSeqOff2ElementSysNodes, NULL); + + if(!g_pProcQueue || !g_pProcVersion || !g_pProcSysNodes) + { + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntries: couldn't make /proc/%s files", PVRProcDirRoot)); + + return -ENOMEM; + } + + +#ifdef DEBUG + + g_pProcDebugLevel = CreateProcEntrySeq("debug_level", NULL, NULL, + ProcSeqShowDebugLevel, + ProcSeq1ElementOff2Element, NULL, + (IMG_VOID*)PVRDebugProcSetLevel); + if(!g_pProcDebugLevel) + { + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntries: couldn't make /proc/%s/debug_level", PVRProcDirRoot)); + + return -ENOMEM; + } + +#ifdef PVR_MANUAL_POWER_CONTROL + g_pProcPowerLevel = CreateProcEntrySeq("power_control", NULL, NULL, + ProcSeqShowPowerLevel, + ProcSeq1ElementOff2Element, NULL, + PVRProcSetPowerLevel); + if(!g_pProcPowerLevel) + { + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntries: couldn't make /proc/%s/power_control", PVRProcDirRoot)); + + return -ENOMEM; + } +#endif +#endif + + return 0; +} + + +/*! +****************************************************************************** + + @Function : RemoveProcEntry + + @Description + + Remove a single node under /proc/pvr. + + @Input name : the name of the node to remove + + @Return nothing + +*****************************************************************************/ +IMG_VOID RemoveProcEntry(const IMG_CHAR * name) +{ + if (dir) + { + remove_proc_entry(name, dir); + PVR_DPF((PVR_DBG_MESSAGE, "Removing /proc/%s/%s", PVRProcDirRoot, name)); + } +} + + +/*! +****************************************************************************** + + @Function : RemovePerProcessProcEntry + + @Description + + Remove a single node under the per process proc directory. + + @Input name : the name of the node to remove + + @Return nothing + +*****************************************************************************/ +IMG_VOID RemovePerProcessProcEntry(const IMG_CHAR *name) +{ + PVRSRV_ENV_PER_PROCESS_DATA *psPerProc; + + psPerProc = LinuxTerminatingProcessPrivateData(); + if (!psPerProc) + { + psPerProc = PVRSRVFindPerProcessPrivateData(); + if (!psPerProc) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: can't " + "remove %s, no per process data", name)); + return; + } + } + + if (psPerProc->psProcDir) + { + remove_proc_entry(name, psPerProc->psProcDir); + + PVR_DPF((PVR_DBG_MESSAGE, "Removing proc entry %s from %s", name, psPerProc->psProcDir->name)); + } +} + + +/*! +****************************************************************************** + + @Function : RemovePerProcessProcDir + + @Description + + Remove the per process directorty under /proc/pvr. + + @Input psPerProc : environment specific per process data + + @Return nothing + +*****************************************************************************/ +IMG_VOID RemovePerProcessProcDir(PVRSRV_ENV_PER_PROCESS_DATA *psPerProc) +{ + if (psPerProc->psProcDir) + { + while (psPerProc->psProcDir->subdir) + { + PVR_DPF((PVR_DBG_WARNING, "Belatedly removing /proc/%s/%s/%s", PVRProcDirRoot, psPerProc->psProcDir->name, psPerProc->psProcDir->subdir->name)); + + RemoveProcEntry(psPerProc->psProcDir->subdir->name); + } + RemoveProcEntry(psPerProc->psProcDir->name); + } +} + +/*! +****************************************************************************** + + @Function : RemoveProcEntries + + Description + + Proc filesystem entry deletion - Remove all proc filesystem entries for + the driver. + + @Input none + + @Return nothing + +*****************************************************************************/ +IMG_VOID RemoveProcEntries(IMG_VOID) +{ +#ifdef DEBUG + RemoveProcEntrySeq( g_pProcDebugLevel ); +#ifdef PVR_MANUAL_POWER_CONTROL + RemoveProcEntrySeq( g_pProcPowerLevel ); +#endif /* PVR_MANUAL_POWER_CONTROL */ +#endif + + RemoveProcEntrySeq(g_pProcQueue); + RemoveProcEntrySeq(g_pProcVersion); + RemoveProcEntrySeq(g_pProcSysNodes); + + while (dir->subdir) + { + PVR_DPF((PVR_DBG_WARNING, "Belatedly removing /proc/%s/%s", PVRProcDirRoot, dir->subdir->name)); + + RemoveProcEntry(dir->subdir->name); + } + + remove_proc_entry(PVRProcDirRoot, NULL); +} + +/***************************************************************************** + FUNCTION : ProcSeqShowVersion + + PURPOSE : Print the content of version to /proc file + + PARAMETERS : sfile - /proc seq_file + el - Element to print +*****************************************************************************/ +static void ProcSeqShowVersion(struct seq_file *sfile,void* el) +{ + SYS_DATA *psSysData; + IMG_CHAR *pszSystemVersionString = "None"; + + if(el == PVR_PROC_SEQ_START_TOKEN) + { + seq_printf(sfile, + "Version %s (%s) %s\n", + PVRVERSION_STRING, + PVR_BUILD_TYPE, PVR_BUILD_DIR); + return; + } + + psSysData = SysAcquireDataNoCheck(); + if(psSysData != IMG_NULL && psSysData->pszVersionString != IMG_NULL) + { + pszSystemVersionString = psSysData->pszVersionString; + } + + seq_printf( sfile, "System Version String: %s\n", pszSystemVersionString); +} + +/*! +****************************************************************************** + + @Function procDumpSysNodes (plus deviceTypeToString and deviceClassToString) + + @Description + + Format the contents of /proc/pvr/nodes + + @Input buf : where to place format contents data. + + @Input size : the size of the buffer into which to place data + + @Input off : how far into the file we are. + + @Return amount of data placed in buffer, 0, or END_OF_FILE : + +******************************************************************************/ +static const IMG_CHAR *deviceTypeToString(PVRSRV_DEVICE_TYPE deviceType) +{ + switch (deviceType) + { + default: + { + static IMG_CHAR text[10]; + + sprintf(text, "?%x", (IMG_UINT)deviceType); + + return text; + } + } +} + + +static const IMG_CHAR *deviceClassToString(PVRSRV_DEVICE_CLASS deviceClass) +{ + switch (deviceClass) + { + case PVRSRV_DEVICE_CLASS_3D: + { + return "3D"; + } + case PVRSRV_DEVICE_CLASS_DISPLAY: + { + return "display"; + } + case PVRSRV_DEVICE_CLASS_BUFFER: + { + return "buffer"; + } + default: + { + static IMG_CHAR text[10]; + + sprintf(text, "?%x", (IMG_UINT)deviceClass); + return text; + } + } +} + +static IMG_VOID* DecOffPsDev_AnyVaCb(PVRSRV_DEVICE_NODE *psNode, va_list va) +{ + off_t *pOff = va_arg(va, off_t*); + if (--(*pOff)) + { + return IMG_NULL; + } + else + { + return psNode; + } +} + +/***************************************************************************** + FUNCTION : ProcSeqShowSysNodes + + PURPOSE : Print the content of version to /proc file + + PARAMETERS : sfile - /proc seq_file + el - Element to print +*****************************************************************************/ +static void ProcSeqShowSysNodes(struct seq_file *sfile,void* el) +{ + PVRSRV_DEVICE_NODE *psDevNode; + + if(el == PVR_PROC_SEQ_START_TOKEN) + { + seq_printf( sfile, + "Registered nodes\n" + "Addr Type Class Index Ref pvDev Size Res\n"); + return; + } + + psDevNode = (PVRSRV_DEVICE_NODE*)el; + + seq_printf( sfile, + "%p %-8s %-8s %4d %2u %p %3u %p\n", + psDevNode, + deviceTypeToString(psDevNode->sDevId.eDeviceType), + deviceClassToString(psDevNode->sDevId.eDeviceClass), + psDevNode->sDevId.eDeviceClass, + psDevNode->ui32RefCount, + psDevNode->pvDevice, + psDevNode->ui32pvDeviceSize, + psDevNode->hResManContext); +} + +/***************************************************************************** + FUNCTION : ProcSeqOff2ElementSysNodes + + PURPOSE : Transale offset to element (/proc stuff) + + PARAMETERS : sfile - /proc seq_file + off - the offset into the buffer + + RETURNS : element to print +*****************************************************************************/ +static void* ProcSeqOff2ElementSysNodes(struct seq_file * sfile, loff_t off) +{ + SYS_DATA *psSysData; + PVRSRV_DEVICE_NODE*psDevNode = IMG_NULL; + + PVR_UNREFERENCED_PARAMETER(sfile); + + if(!off) + { + return PVR_PROC_SEQ_START_TOKEN; + } + + psSysData = SysAcquireDataNoCheck(); + if (psSysData != IMG_NULL) + { + /* Find Dev Node */ + psDevNode = (PVRSRV_DEVICE_NODE*) + List_PVRSRV_DEVICE_NODE_Any_va(psSysData->psDeviceNodeList, + DecOffPsDev_AnyVaCb, + &off); + } + + /* Return anything that is not PVR_RPOC_SEQ_START_TOKEN and NULL */ + return (void*)psDevNode; +} + +/***************************************************************************** + End of file (proc.c) +*****************************************************************************/ |