gpu: pvr: pdumpfs: add Kconfig and debugfs pdump mode handling
[sgx.git] / pvr / proc.c
1 /**********************************************************************
2  *
3  * Copyright(c) 2008 Imagination Technologies Ltd. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU General Public License,
7  * version 2, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful but, except
10  * as otherwise stated in writing, without any warranty; without even the
11  * implied warranty of merchantability or fitness for a particular purpose.
12  * See the GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * The full GNU General Public License is included in this distribution in
19  * the file called "COPYING".
20  *
21  * Contact Information:
22  * Imagination Technologies Ltd. <gpl-support@imgtec.com>
23  * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK
24  *
25  ******************************************************************************/
26
27 #include <linux/init.h>
28 #include <linux/module.h>
29 #include <linux/version.h>
30 #include <linux/fs.h>
31 #include <linux/proc_fs.h>
32
33 #include "services_headers.h"
34
35 #include "queue.h"
36 #include "resman.h"
37 #include "pvrmmap.h"
38 #include "pvr_debug.h"
39 #include "pvrversion.h"
40 #include "proc.h"
41 #include "perproc.h"
42 #include "env_perproc.h"
43
44 /* The proc entry for our /proc/pvr directory */
45
46 static struct proc_dir_entry *dir;
47
48 static off_t procDumpSysNodes(char *buf, size_t size, off_t off);
49 static off_t procDumpVersion(char *buf, size_t size, off_t off);
50
51 static const char PVRProcDirRoot[] = "pvr";
52
53 off_t printAppend(char *buffer, size_t size, off_t off, const char *format, ...)
54 {
55         int n;
56         int space = size - off;
57         va_list ap;
58
59         PVR_ASSERT(space >= 0);
60
61         va_start(ap, format);
62         n = vsnprintf(buffer + off, space, format, ap);
63         va_end(ap);
64
65         if (n >= space || n < 0) {
66
67                 buffer[size - 1] = 0;
68                 return size - 1;
69         } else {
70                 return off + n;
71 }
72 }
73
74 static int pvr_read_proc(char *page, char **start, off_t off,
75                          int count, int *eof, void *data)
76 {
77         off_t (*pprn)(char *, size_t, off_t) = data;
78
79         off_t len = pprn(page, count, off);
80
81         if (len == END_OF_FILE) {
82                 len = 0;
83                 *eof = 1;
84         } else if (!len) {
85                 *start = (char *)0;
86         } else {
87                 *start = (char *)1;
88         }
89
90         return len;
91 }
92
93 static int CreateProcEntryInDir(struct proc_dir_entry *pdir, const char *name,
94                                 read_proc_t rhandler, write_proc_t whandler,
95                                 void *data)
96 {
97         struct proc_dir_entry *file;
98         mode_t mode;
99
100         if (!pdir) {
101                 PVR_DPF(PVR_DBG_ERROR,
102                         "CreateProcEntryInDir: parent directory doesn't exist");
103
104                 return -ENOMEM;
105         }
106
107         mode = S_IFREG;
108
109         if (rhandler)
110                 mode |= S_IRUGO;
111
112         if (whandler)
113                 mode |= S_IWUSR;
114
115         file = create_proc_entry(name, mode, pdir);
116
117         if (file) {
118                 file->read_proc = rhandler;
119                 file->write_proc = whandler;
120                 file->data = data;
121
122                 PVR_DPF(PVR_DBG_MESSAGE, "Created proc entry %s in %s", name,
123                          pdir->name);
124
125                 return 0;
126         }
127
128         PVR_DPF(PVR_DBG_ERROR,
129                  "CreateProcEntry: cannot create proc entry %s in %s", name,
130                  pdir->name);
131
132         return -ENOMEM;
133 }
134
135 int CreateProcEntry(const char *name, read_proc_t rhandler,
136                     write_proc_t whandler, void *data)
137 {
138         return CreateProcEntryInDir(dir, name, rhandler, whandler, data);
139 }
140
141 static struct proc_dir_entry *
142 ProcessProcDirCreate(u32 pid)
143 {
144         struct PVRSRV_ENV_PER_PROCESS_DATA *psPerProc;
145         char dirname[16];
146         int ret;
147
148         psPerProc = PVRSRVPerProcessPrivateData(pid);
149         if (!psPerProc) {
150                 pr_err("%s: no per process data for %d\n", __func__, pid);
151                 return NULL;
152         }
153
154         if (psPerProc->psProcDir)
155                 return psPerProc->psProcDir;
156
157         ret = snprintf(dirname, sizeof(dirname), "%u", pid);
158         if (ret <= 0 || ret >= sizeof(dirname)) {
159                 pr_err("%s: couldn't generate per process proc dir for %d\n",
160                        __func__, pid);
161                 return NULL;
162         }
163
164         psPerProc->psProcDir = proc_mkdir(dirname, dir);
165         if (!psPerProc->psProcDir)
166                 pr_err("%s: couldn't create /proc/%s/%u\n",
167                        __func__, PVRProcDirRoot, pid);
168
169         return psPerProc->psProcDir;
170 }
171
172 static struct proc_dir_entry *
173 ProcessProcDirGet(u32 pid)
174 {
175         struct PVRSRV_ENV_PER_PROCESS_DATA *psPerProc;
176
177         psPerProc = PVRSRVPerProcessPrivateData(pid);
178         if (!psPerProc) {
179                 pr_err("%s: no per process data for %d\n", __func__, pid);
180                 return NULL;
181         }
182
183         if (!psPerProc->psProcDir) {
184                 pr_err("%s: couldn't retrieve /proc/%s/%u\n", __func__,
185                        PVRProcDirRoot, pid);
186                 return NULL;
187         }
188
189         return psPerProc->psProcDir;
190 }
191
192 int CreatePerProcessProcEntry(u32 pid, const char *name, read_proc_t rhandler,
193                               void *data)
194 {
195         if (!dir) {
196                 PVR_DPF(PVR_DBG_ERROR,
197                          "CreatePerProcessProcEntries: /proc/%s doesn't exist",
198                          PVRProcDirRoot);
199
200                 return -ENOMEM;
201         }
202
203         if (pid) {
204                 struct proc_dir_entry *pid_dir = ProcessProcDirCreate(pid);
205
206                 if (!pid_dir)
207                         return -ENOMEM;
208
209                 return CreateProcEntryInDir(pid_dir, name, rhandler, NULL,
210                                             data);
211         } else
212                 return CreateProcEntryInDir(dir, name, rhandler, NULL, data);
213 }
214
215 int CreateProcReadEntry(const char *name,
216                         off_t (handler)(char *, size_t, off_t))
217 {
218         struct proc_dir_entry *file;
219
220         if (!dir) {
221                 PVR_DPF(PVR_DBG_ERROR, "CreateProcReadEntry: "
222                                 "cannot make proc entry /proc/%s/%s: no parent",
223                          PVRProcDirRoot, name);
224
225                 return -ENOMEM;
226         }
227
228         file =
229             create_proc_read_entry(name, S_IFREG | S_IRUGO, dir, pvr_read_proc,
230                                    (void *)handler);
231
232         if (file)
233                 return 0;
234
235         PVR_DPF(PVR_DBG_ERROR, "CreateProcReadEntry: "
236                                 "cannot make proc entry /proc/%s/%s: no memory",
237                  PVRProcDirRoot, name);
238
239         return -ENOMEM;
240 }
241
242 int CreateProcEntries(void)
243 {
244         dir = proc_mkdir(PVRProcDirRoot, NULL);
245
246         if (!dir) {
247                 PVR_DPF(PVR_DBG_ERROR,
248                          "CreateProcEntries: cannot make /proc/%s directory",
249                          PVRProcDirRoot);
250
251                 return -ENOMEM;
252         }
253
254         if (CreateProcReadEntry("queue", QueuePrintQueues) ||
255             CreateProcReadEntry("version", procDumpVersion) ||
256             CreateProcReadEntry("nodes", procDumpSysNodes)) {
257                 PVR_DPF(PVR_DBG_ERROR,
258                          "CreateProcEntries: couldn't make /proc/%s files",
259                          PVRProcDirRoot);
260
261                 return -ENOMEM;
262         }
263 #ifdef CONFIG_PVR_DEBUG_EXTRA
264         if (CreateProcEntry
265             ("debug_level", PVRDebugProcGetLevel, PVRDebugProcSetLevel, NULL)) {
266                 PVR_DPF(PVR_DBG_ERROR,
267                         "CreateProcEntries: couldn't make /proc/%s/debug_level",
268                          PVRProcDirRoot);
269
270                 return -ENOMEM;
271         }
272 #endif
273
274         return 0;
275 }
276
277 void RemoveProcEntry(const char *name)
278 {
279         if (dir) {
280                 remove_proc_entry(name, dir);
281                 PVR_DPF(PVR_DBG_MESSAGE, "Removing /proc/%s/%s",
282                          PVRProcDirRoot, name);
283         }
284 }
285
286 void RemovePerProcessProcEntry(u32 pid, const char *name)
287 {
288         if (pid) {
289                 struct proc_dir_entry *pid_dir = ProcessProcDirGet(pid);
290
291                 if (!pid_dir)
292                         return;
293
294                 remove_proc_entry(name, pid_dir);
295
296                 PVR_DPF(PVR_DBG_MESSAGE, "Removing proc entry %s from %s",
297                          name, pid_dir->name);
298         } else
299                 RemoveProcEntry(name);
300 }
301
302 void RemovePerProcessProcDir(struct PVRSRV_ENV_PER_PROCESS_DATA *psPerProc)
303 {
304         if (psPerProc->psProcDir) {
305                 while (psPerProc->psProcDir->subdir) {
306                         PVR_DPF(PVR_DBG_WARNING,
307                                  "Belatedly removing /proc/%s/%s/%s",
308                                  PVRProcDirRoot, psPerProc->psProcDir->name,
309                                  psPerProc->psProcDir->subdir->name);
310
311                         RemoveProcEntry(psPerProc->psProcDir->subdir->name);
312                 }
313                 RemoveProcEntry(psPerProc->psProcDir->name);
314         }
315 }
316
317 void RemoveProcEntries(void)
318 {
319 #ifdef CONFIG_PVR_DEBUG_EXTRA
320         RemoveProcEntry("debug_level");
321 #endif
322         RemoveProcEntry("queue");
323         RemoveProcEntry("nodes");
324         RemoveProcEntry("version");
325
326         while (dir->subdir) {
327                 PVR_DPF(PVR_DBG_WARNING, "Belatedly removing /proc/%s/%s",
328                          PVRProcDirRoot, dir->subdir->name);
329
330                 RemoveProcEntry(dir->subdir->name);
331         }
332
333         remove_proc_entry(PVRProcDirRoot, NULL);
334 }
335
336 static off_t procDumpVersion(char *buf, size_t size, off_t off)
337 {
338         struct SYS_DATA *psSysData;
339
340         if (off == 0)
341                 return printAppend(buf, size, 0, "Version %s (%s) %s\n",
342                                    PVRVERSION_STRING, PVR_BUILD_TYPE,
343                                    PVR_BUILD_DIR);
344
345         if (SysAcquireData(&psSysData) != PVRSRV_OK)
346                 return PVRSRV_ERROR_GENERIC;
347
348         if (off == 1) {
349                 char *pszSystemVersionString = "None";
350
351                 if (psSysData->pszVersionString)
352                         pszSystemVersionString = psSysData->pszVersionString;
353
354                 if (strlen(pszSystemVersionString) +
355                     strlen("System Version String: \n") + 1 > size)
356                         return 0;
357                 return printAppend(buf, size, 0, "System Version String: %s\n",
358                                    pszSystemVersionString);
359         }
360
361         return END_OF_FILE;
362 }
363
364 static const char *deviceTypeToString(enum PVRSRV_DEVICE_TYPE deviceType)
365 {
366         switch (deviceType) {
367         default:
368                 {
369                         static char text[10];
370                         sprintf(text, "?%x", deviceType);
371                         return text;
372                 }
373         }
374 }
375
376 static const char *deviceClassToString(enum PVRSRV_DEVICE_CLASS deviceClass)
377 {
378         switch (deviceClass) {
379         case PVRSRV_DEVICE_CLASS_3D:
380                 {
381                         return "3D";
382                 }
383         case PVRSRV_DEVICE_CLASS_DISPLAY:
384                 {
385                         return "display";
386                 }
387         case PVRSRV_DEVICE_CLASS_BUFFER:
388                 {
389                         return "buffer";
390                 }
391         default:
392                 {
393                         static char text[10];
394
395                         sprintf(text, "?%x", deviceClass);
396                         return text;
397                 }
398         }
399 }
400
401 static off_t procDumpSysNodes(char *buf, size_t size, off_t off)
402 {
403         struct SYS_DATA *psSysData;
404         struct PVRSRV_DEVICE_NODE *psDevNode;
405         off_t len;
406
407         if (size < 80)
408                 return 0;
409
410         if (off == 0)
411                 return printAppend(buf, size, 0,
412                                    "Registered nodes\n"
413                 "Addr     Type     Class    Index Ref pvDev     Size Res\n");
414
415         if (SysAcquireData(&psSysData) != PVRSRV_OK)
416                 return PVRSRV_ERROR_GENERIC;
417
418         for (psDevNode = psSysData->psDeviceNodeList;
419              --off && psDevNode; psDevNode = psDevNode->psNext)
420                 ;
421
422         if (!psDevNode)
423                 return END_OF_FILE;
424
425         len = printAppend(buf, size, 0,
426                           "%p %-8s %-8s %4d  %2u  %p  %3u  %p\n",
427                           psDevNode,
428                           deviceTypeToString(psDevNode->sDevId.eDeviceType),
429                           deviceClassToString(psDevNode->sDevId.eDeviceClass),
430                           psDevNode->sDevId.eDeviceClass,
431                           psDevNode->ui32RefCount,
432                           psDevNode->pvDevice,
433                           psDevNode->ui32pvDeviceSize,
434                           psDevNode->hResManContext);
435         return len;
436 }