gpu: pvr: get rid of unnecessary hash lookups for the proc object
[sgx.git] / pvr / pvr_pdumpfs.c
1 /*
2  * Copyright (c) 2010-2011 by Luc Verhaegen <libv@codethink.co.uk>
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */
18
19 #include <linux/mutex.h>
20 #include <linux/uaccess.h>
21 #include <linux/debugfs.h>
22
23 #include "img_defs.h"
24 #include "services_headers.h"
25 #include "pvr_pdump.h"
26 #include "pvr_pdumpfs.h"
27
28 static struct mutex pdumpfs_mutex[1];
29
30 enum pdumpfs_mode {
31         PDUMPFS_MODE_DISABLED,
32         PDUMPFS_MODE_STANDARD,
33         PDUMPFS_MODE_FULL,
34 };
35
36 static enum pdumpfs_mode pdumpfs_mode =
37 #if defined(CONFIG_PVR_PDUMP_MODE_STANDARD)
38         PDUMPFS_MODE_STANDARD
39 #elif defined(CONFIG_PVR_PDUMP_MODE_FULL)
40         PDUMPFS_MODE_FULL
41 #else
42         PDUMPFS_MODE_DISABLED
43 #endif
44         ;
45
46 #define FRAME_PAGE_COUNT 2040
47
48 struct pdumpfs_frame {
49         struct pdumpfs_frame *next;
50
51         u32 pid;
52         u32 number;
53
54         size_t offset;
55         int page_count;
56         unsigned long pages[FRAME_PAGE_COUNT];
57 };
58
59 #define MAX_FRAME_COUNT_HARD 1024
60 static u32 frame_count_max = CONFIG_PVR_PDUMP_INITIAL_MAX_FRAME_COUNT;
61 static u32 frame_count;
62
63 static struct pdumpfs_frame *frame_init;
64 static struct pdumpfs_frame *frame_stream;
65 static struct pdumpfs_frame *frame_current;
66
67 static struct pdumpfs_frame *frame_current_debugfs;
68 static int frame_current_open_count;
69 static int frame_stream_open_count;
70
71 static loff_t stream_start;
72 static loff_t stream_f_pos;
73 static loff_t stream_end;
74
75 static struct pdumpfs_frame *
76 frame_create(void)
77 {
78         struct pdumpfs_frame *frame =
79                 kmalloc(sizeof(struct pdumpfs_frame), GFP_KERNEL);
80         if (!frame)
81                 return NULL;
82
83         memset(frame, 0, sizeof(struct pdumpfs_frame));
84
85         return frame;
86 }
87
88 static void
89 frame_destroy(struct pdumpfs_frame *frame)
90 {
91         int i;
92
93         if (!frame)
94                 return;
95
96         for (i = 0; i < frame->page_count; i++)
97                 free_page(frame->pages[i]);
98
99         kfree(frame);
100 }
101
102 static void
103 frame_destroy_all(void)
104 {
105         /* detach from possible clones */
106         if (frame_current_debugfs &&
107             ((frame_current_debugfs == frame_init) ||
108              (frame_current_debugfs == frame_current) ||
109              frame_current_debugfs->next))
110                 frame_current_debugfs = NULL;
111
112         frame_current = NULL;
113
114         frame_destroy(frame_init);
115         frame_init = NULL;
116
117         frame_destroy(frame_current_debugfs);
118         frame_current_debugfs = NULL;
119
120         while (frame_stream) {
121                 struct pdumpfs_frame *frame = frame_stream;
122
123                 frame_stream = frame->next;
124
125                 frame_destroy(frame);
126                 frame_count--;
127         }
128
129         stream_start = 0;
130         stream_f_pos = 0;
131         stream_end = 0;
132 }
133
134 static void
135 frame_cull_first(void)
136 {
137         struct pdumpfs_frame *frame = frame_stream;
138
139         frame_stream = frame->next;
140         frame->next = NULL;
141
142         if (frame_stream_open_count)
143                 stream_start += frame->offset;
144         else
145                 stream_end -= frame->offset;
146
147         /*
148          * we cannot have frames vanish in the middle of reading
149          * them through debugfs
150          */
151         if (frame != frame_current_debugfs)
152                 frame_destroy(frame);
153
154         frame_count--;
155 }
156
157 static void
158 frame_cull(void)
159 {
160
161         if (!frame_stream_open_count) {
162                 while (frame_count > frame_count_max)
163                         frame_cull_first();
164         } else {
165                 while (((stream_start + frame_stream->offset) < stream_f_pos) ||
166                        (frame_count > MAX_FRAME_COUNT_HARD))
167                         frame_cull_first();
168         }
169 }
170
171 static int
172 frame_new(u32 pid, u32 number)
173 {
174         struct pdumpfs_frame *frame = frame_create();
175
176         if (!frame) {
177                 pr_err("%s: Failed to create frame.\n", __func__);
178                 return -ENOMEM;
179         }
180
181         frame->pid = pid;
182         frame->number = number;
183
184         if (!frame_init)
185                 frame_init = frame;
186         else {
187                 if (frame_current != frame_init)
188                         frame_current->next = frame;
189                 else
190                         frame_stream = frame;
191                 frame_count++;
192         }
193
194         frame_current = frame;
195
196         frame_cull();
197
198         return 0;
199 }
200
201 void
202 pdumpfs_frame_set(u32 pid, u32 frame)
203 {
204         mutex_lock(pdumpfs_mutex);
205
206         frame_new(pid, frame);
207
208         mutex_unlock(pdumpfs_mutex);
209 }
210
211 bool
212 pdumpfs_capture_enabled(void)
213 {
214         bool ret;
215
216         mutex_lock(pdumpfs_mutex);
217
218         if ((pdumpfs_mode == PDUMPFS_MODE_FULL) &&
219             (frame_current != frame_init)) /* simulator bails otherwise */
220                 ret = true;
221         else
222                 ret = false;
223
224         mutex_unlock(pdumpfs_mutex);
225
226         return ret;
227 }
228
229 bool
230 pdumpfs_flags_check(u32 flags)
231 {
232         bool ret;
233
234         if (flags & PDUMP_FLAGS_NEVER)
235                 return false;
236
237         mutex_lock(pdumpfs_mutex);
238
239         if (pdumpfs_mode == PDUMPFS_MODE_DISABLED)
240                 ret = false;
241         else if ((pdumpfs_mode == PDUMPFS_MODE_FULL) &&
242                  (frame_current != frame_init)) /* simulator bails otherwise */
243                 ret = true;
244         else if (flags & PDUMP_FLAGS_CONTINUOUS)
245                 ret = true;
246         else
247                 ret = false;
248
249         mutex_unlock(pdumpfs_mutex);
250
251         return ret;
252 }
253
254 static size_t
255 pdumpfs_frame_write_low(void *buffer, size_t size, bool from_user)
256 {
257         struct pdumpfs_frame *frame = frame_current;
258         size_t ret = 0;
259
260         while (size) {
261                 size_t count = size;
262                 size_t offset = frame->offset & ~PAGE_MASK;
263                 unsigned long page;
264
265                 if (!offset) {
266                         if (frame->page_count >= FRAME_PAGE_COUNT) {
267                                 pr_err("%s: Frame size overrun.\n", __func__);
268                                 return -ENOMEM;
269                         }
270
271                         page = __get_free_page(GFP_KERNEL);
272                         if (!page) {
273                                 pr_err("%s: failed to get free page.\n",
274                                        __func__);
275                                 return -ENOMEM;
276                         }
277
278                         frame->pages[frame->page_count] = page;
279                         frame->page_count++;
280                 } else
281                         page =
282                                 frame->pages[frame->page_count - 1];
283
284                 if (count > (PAGE_SIZE - offset))
285                         count = PAGE_SIZE - offset;
286
287                 if (from_user) {
288                         if (copy_from_user(((u8 *) page) + offset,
289                                            (void __user __force *) buffer,
290                                            count))
291                                 return -EINVAL;
292                 } else
293                         memcpy(((u8 *) page) + offset, buffer, count);
294
295                 buffer += count;
296                 size -= count;
297                 ret += count;
298                 frame->offset += count;
299         }
300         return ret;
301 }
302
303 static size_t
304 pdumpfs_frame_write(void *buffer, size_t size, bool from_user)
305 {
306         size_t ret;
307
308         if ((frame_current->offset + size) > (PAGE_SIZE * FRAME_PAGE_COUNT)) {
309                 u32 pid = OSGetCurrentProcessIDKM();
310                 struct task_struct *task;
311
312                 pr_err("Frame overrun!!!\n");
313
314                 ret = frame_new(pid, -1);
315                 if (ret)
316                         return ret;
317
318                 rcu_read_lock();
319                 task = pid_task(find_vpid(pid), PIDTYPE_PID);
320
321 #define FRAME_OVERRUN_MESSAGE "-- Starting forced frame caused by "
322                 pdumpfs_frame_write_low(FRAME_OVERRUN_MESSAGE,
323                                         sizeof(FRAME_OVERRUN_MESSAGE), false);
324                 pdumpfs_frame_write_low(task->comm, strlen(task->comm), false);
325                 pdumpfs_frame_write_low("\r\n", 2, false);
326
327                 pr_err("%s: Frame size overrun, caused by %d (%s)\n",
328                        __func__, pid, task->comm);
329
330                 rcu_read_unlock();
331         }
332
333         return pdumpfs_frame_write_low(buffer, size, from_user);
334 }
335
336 enum PVRSRV_ERROR
337 pdumpfs_write_data(void *buffer, size_t size, bool from_user)
338 {
339         mutex_lock(pdumpfs_mutex);
340
341         size = pdumpfs_frame_write(buffer, size, from_user);
342         if ((size > 0) && (frame_current != frame_init))
343                 stream_end += size;
344
345         mutex_unlock(pdumpfs_mutex);
346
347         if ((size >= 0) || (size == -ENOMEM))
348                 return PVRSRV_OK;
349         else
350                 return PVRSRV_ERROR_GENERIC;
351 }
352
353 void
354 pdumpfs_write_string(char *string)
355 {
356         size_t size;
357
358         mutex_lock(pdumpfs_mutex);
359
360         size = pdumpfs_frame_write(string, strlen(string), false);
361         if ((size > 0) && (frame_current != frame_init))
362                 stream_end += size;
363
364         mutex_unlock(pdumpfs_mutex);
365 }
366
367 /*
368  * DebugFS entries.
369  */
370 static const struct {
371         char *name;
372         enum pdumpfs_mode mode;
373 } pdumpfs_modes[] = {
374         {"disabled",   PDUMPFS_MODE_DISABLED},
375         {"standard",   PDUMPFS_MODE_STANDARD},
376         {"full",       PDUMPFS_MODE_FULL},
377         {NULL, PDUMPFS_MODE_DISABLED}
378 };
379
380 static ssize_t
381 pdumpfs_mode_read(struct file *filp, char __user *buf, size_t size,
382                   loff_t *f_pos)
383 {
384         char tmp[16];
385         int i;
386
387         tmp[0] = 0;
388
389         mutex_lock(pdumpfs_mutex);
390
391         for (i = 0; pdumpfs_modes[i].name; i++)
392                 if (pdumpfs_modes[i].mode == pdumpfs_mode)
393                         snprintf(tmp, sizeof(tmp), "%s\n",
394                                  pdumpfs_modes[i].name);
395
396         mutex_unlock(pdumpfs_mutex);
397
398         if (strlen(tmp) < *f_pos)
399                 return 0;
400
401         if ((strlen(tmp) + 1) < (*f_pos + size))
402                 size = strlen(tmp) + 1 - *f_pos;
403
404         if (copy_to_user(buf, tmp + *f_pos, size))
405                 return -EFAULT;
406
407         *f_pos += size;
408         return size;
409 }
410
411 static ssize_t
412 pdumpfs_mode_write(struct file *filp, const char __user *buf, size_t size,
413                    loff_t *f_pos)
414 {
415         static char tmp[16];
416         int i;
417
418         if (*f_pos > sizeof(tmp))
419                 return -EINVAL;
420
421         if (size > (sizeof(tmp) - *f_pos))
422                 size = sizeof(tmp) - *f_pos;
423
424         if (copy_from_user(tmp + *f_pos, buf, size))
425                 return -EFAULT;
426
427         *f_pos += size;
428
429         mutex_lock(pdumpfs_mutex);
430
431         for (i = 0; pdumpfs_modes[i].name; i++)
432                 if (!strnicmp(tmp, pdumpfs_modes[i].name,
433                               strlen(pdumpfs_modes[i].name))) {
434                         pdumpfs_mode = pdumpfs_modes[i].mode;
435                         mutex_unlock(pdumpfs_mutex);
436                         return size;
437                 }
438
439         mutex_unlock(pdumpfs_mutex);
440         return -EINVAL;
441 }
442
443 static const struct file_operations pdumpfs_mode_fops = {
444         .owner = THIS_MODULE,
445         .llseek = no_llseek,
446         .read = pdumpfs_mode_read,
447         .write = pdumpfs_mode_write,
448 };
449
450 static ssize_t
451 pdumpfs_modes_possible_read(struct file *filp, char __user *buf, size_t size,
452                             loff_t *f_pos)
453 {
454         unsigned int i, skip = *f_pos, pos = 0;
455
456         for (i = 0; pdumpfs_modes[i].name; i++) {
457                 if (i) { /* space */
458                         if (skip)
459                                 skip--;
460                         else if (size > pos) {
461                                 if (copy_to_user(buf + pos, " ", 1))
462                                         return -EFAULT;
463                                 pos++;
464                         }
465                 }
466
467                 if (size) {
468                         int len = strlen(pdumpfs_modes[i].name);
469
470                         if (skip >= len) {
471                                 skip -= len;
472                         } else if (size > pos) {
473                                 len = min(len - skip, size - pos);
474
475                                 if (copy_to_user(buf + pos,
476                                                  pdumpfs_modes[i].name + skip,
477                                                  len))
478                                         return -EFAULT;
479
480                                 skip = 0;
481                                 pos += len;
482                         }
483                 }
484         }
485
486         *f_pos += pos;
487         return pos;
488 }
489
490 static const struct file_operations pdumpfs_modes_possible_fops = {
491         .owner = THIS_MODULE,
492         .llseek = no_llseek,
493         .read = pdumpfs_modes_possible_read,
494 };
495
496 static ssize_t
497 pdumpfs_frame_count_max_read(struct file *filp, char __user *buf, size_t size,
498                              loff_t *f_pos)
499 {
500         char tmp[16];
501
502         tmp[0] = 0;
503
504         mutex_lock(pdumpfs_mutex);
505         snprintf(tmp, sizeof(tmp), "%d", frame_count_max);
506         mutex_unlock(pdumpfs_mutex);
507
508         if (strlen(tmp) < *f_pos)
509                 return 0;
510
511         if ((strlen(tmp) + 1) < (*f_pos + size))
512                 size = strlen(tmp) + 1 - *f_pos;
513
514         if (copy_to_user(buf, tmp + *f_pos, size))
515                 return -EFAULT;
516
517         *f_pos += size;
518         return size;
519 }
520
521 static ssize_t
522 pdumpfs_frame_count_max_write(struct file *filp, const char __user *buf,
523                               size_t size, loff_t *f_pos)
524 {
525         static char tmp[16];
526         unsigned long result = 0;
527
528         if (*f_pos > sizeof(tmp))
529                 return -EINVAL;
530
531         if (size > (sizeof(tmp) - *f_pos))
532                 size = sizeof(tmp) - *f_pos;
533
534         if (copy_from_user(tmp + *f_pos, buf, size))
535                 return -EFAULT;
536
537         tmp[size] = 0;
538
539         mutex_lock(pdumpfs_mutex);
540
541         if (!strict_strtoul(tmp, 0, &result)) {
542                 if (result > 1024)
543                         result = 1024;
544                 if (!result)
545                         result = 1;
546                 frame_count_max = result;
547         }
548
549         mutex_unlock(pdumpfs_mutex);
550
551         *f_pos += size;
552         return size;
553 }
554
555 static const struct file_operations pdumpfs_frame_count_max_fops = {
556         .owner = THIS_MODULE,
557         .llseek = no_llseek,
558         .read = pdumpfs_frame_count_max_read,
559         .write = pdumpfs_frame_count_max_write,
560 };
561
562 static ssize_t
563 pdumpfs_frame_read_single(struct pdumpfs_frame *frame, char __user *buf,
564                           size_t size, loff_t f_pos)
565 {
566         int page;
567         size_t offset;
568
569         if (f_pos >= frame->offset)
570                 return 0;
571
572         if (size > (frame->offset - f_pos))
573                 size = frame->offset - f_pos;
574
575         page = f_pos / PAGE_SIZE;
576         offset = f_pos % PAGE_SIZE;
577
578         if (size > (PAGE_SIZE - offset))
579                 size = PAGE_SIZE - offset;
580
581         if (copy_to_user(buf, ((u8 *) frame->pages[page]) + offset, size))
582                 return -EFAULT;
583
584         return size;
585 }
586
587 static loff_t
588 pdumpfs_llseek_helper(struct file *filp, loff_t offset, int whence, loff_t max)
589 {
590         loff_t f_pos;
591
592         switch (whence) {
593         case SEEK_SET:
594                 if ((offset > max) || (offset < 0))
595                         f_pos = -EINVAL;
596                 else
597                         f_pos = offset;
598                 break;
599         case SEEK_CUR:
600                 if (((filp->f_pos + offset) > max) ||
601                     ((filp->f_pos + offset) < 0))
602                         f_pos = -EINVAL;
603                 else
604                         f_pos = filp->f_pos + offset;
605                 break;
606         case SEEK_END:
607                 if ((offset > 0) ||
608                     (offset < -max))
609                         f_pos = -EINVAL;
610                 else
611                         f_pos = max + offset;
612                 break;
613         default:
614                 f_pos = -EINVAL;
615                 break;
616         }
617
618         if (f_pos >= 0)
619                 filp->f_pos = f_pos;
620
621         return f_pos;
622 }
623
624 static loff_t
625 pdumpfs_init_llseek(struct file *filp, loff_t offset, int whence)
626 {
627         loff_t f_pos;
628
629         mutex_lock(pdumpfs_mutex);
630
631         f_pos = pdumpfs_llseek_helper(filp, offset, whence, frame_init->offset);
632
633         mutex_unlock(pdumpfs_mutex);
634
635         return f_pos;
636 }
637
638 static ssize_t
639 pdumpfs_init_read(struct file *filp, char __user *buf, size_t size,
640                   loff_t *f_pos)
641 {
642         mutex_lock(pdumpfs_mutex);
643
644         size = pdumpfs_frame_read_single(frame_init,
645                                          buf, size, *f_pos);
646
647         mutex_unlock(pdumpfs_mutex);
648
649         if (size > 0)
650                 *f_pos += size;
651         return size;
652 }
653
654 static const struct file_operations pdumpfs_init_fops = {
655         .owner = THIS_MODULE,
656         .llseek = pdumpfs_init_llseek,
657         .read = pdumpfs_init_read,
658 };
659
660 /*
661  * We need to make sure that our frame doesn't vanish while we are still
662  * reading it: so reference this frame again.
663  */
664 static int
665 pdumpfs_current_open(struct inode *inode, struct file *filp)
666 {
667         mutex_lock(pdumpfs_mutex);
668
669         if (!frame_current_open_count)
670                 frame_current_debugfs = frame_current;
671
672         frame_current_open_count++;
673
674         mutex_unlock(pdumpfs_mutex);
675         return 0;
676 }
677
678 static int
679 pdumpfs_current_release(struct inode *inode, struct file *filp)
680 {
681         mutex_lock(pdumpfs_mutex);
682
683         frame_current_open_count--;
684
685         if (!frame_current_open_count) {
686                 if ((frame_current_debugfs != frame_init) &&
687                     (frame_current_debugfs != frame_current) &&
688                     !frame_current_debugfs->next)
689                         frame_destroy(frame_current_debugfs);
690                 frame_current_debugfs = NULL;
691         }
692
693         mutex_unlock(pdumpfs_mutex);
694         return 0;
695 }
696
697 static loff_t
698 pdumpfs_current_llseek(struct file *filp, loff_t offset, int whence)
699 {
700         loff_t f_pos;
701
702         mutex_lock(pdumpfs_mutex);
703
704         f_pos = pdumpfs_llseek_helper(filp, offset, whence,
705                                       frame_current_debugfs->offset);
706
707         mutex_unlock(pdumpfs_mutex);
708
709         return f_pos;
710 }
711
712 static ssize_t
713 pdumpfs_current_read(struct file *filp, char __user *buf, size_t size,
714                      loff_t *f_pos)
715 {
716         mutex_lock(pdumpfs_mutex);
717
718         if (frame_current_debugfs->offset)
719                 size = pdumpfs_frame_read_single(frame_current_debugfs,
720                                                  buf, size, *f_pos);
721         else
722                 size = 0;
723
724         mutex_unlock(pdumpfs_mutex);
725
726         if (size > 0)
727                 *f_pos += size;
728         return size;
729 }
730
731 static const struct file_operations pdumpfs_current_fops = {
732         .owner = THIS_MODULE,
733         .llseek = pdumpfs_current_llseek,
734         .read = pdumpfs_current_read,
735         .open = pdumpfs_current_open,
736         .release = pdumpfs_current_release,
737 };
738
739 /*
740  * So we can track when we can alter stream offsets.
741  */
742 static int
743 pdumpfs_stream_open(struct inode *inode, struct file *filp)
744 {
745         int ret;
746
747         mutex_lock(pdumpfs_mutex);
748
749         if (frame_stream_open_count)
750                 ret = -EUSERS;
751         else {
752                 frame_stream_open_count++;
753                 ret = 0;
754         }
755
756         mutex_unlock(pdumpfs_mutex);
757         return ret;
758 }
759
760 static int
761 pdumpfs_stream_release(struct inode *inode, struct file *filp)
762 {
763         mutex_lock(pdumpfs_mutex);
764
765         frame_stream_open_count--;
766
767         /* fix the damage done while it was open */
768         if (!frame_stream_open_count) {
769                 stream_end -= stream_start;
770                 stream_start = 0;
771                 stream_f_pos = 0;
772         }
773
774         mutex_unlock(pdumpfs_mutex);
775         return 0;
776 }
777
778 static ssize_t
779 pdumpfs_stream_buffer_clear(char __user *buf, size_t size)
780 {
781         char tmp[80];
782
783         memset(tmp, '.', sizeof(tmp) - 1);
784         tmp[sizeof(tmp) - 1] = '\n';
785
786         if (size >= sizeof(tmp)) {
787                 int i;
788
789                 for (i = 0; (i  + sizeof(tmp)) < size; i += sizeof(tmp))
790                         if (copy_to_user(buf + i, tmp, sizeof(tmp)))
791                                 return -EFAULT;
792                 return i;
793         } else {
794                 if (copy_to_user(buf, tmp + sizeof(tmp) - size, size))
795                         return -EFAULT;
796                 return size;
797         }
798 }
799
800 static ssize_t
801 pdumpfs_stream_buffer_fill(struct pdumpfs_frame *frame,
802                            char __user *buf, size_t offset, size_t size)
803 {
804         int page = offset / PAGE_SIZE;
805
806         if (size > (frame->offset - offset))
807                 size = frame->offset - offset;
808
809         offset %= PAGE_SIZE;
810
811         if (size > (PAGE_SIZE - offset))
812                 size = PAGE_SIZE - offset;
813
814         if (copy_to_user(buf, ((u8 *) frame->pages[page]) + offset, size))
815                 return -EFAULT;
816
817         stream_f_pos += size;
818
819         return size;
820 }
821
822 static loff_t
823 pdumpfs_stream_llseek(struct file *filp, loff_t offset, int whence)
824 {
825         loff_t f_pos;
826
827         mutex_lock(pdumpfs_mutex);
828
829         switch (whence) {
830         case SEEK_SET:
831                 if ((offset > stream_end) || (offset < stream_start))
832                         f_pos = -EINVAL;
833                 else
834                         f_pos = offset;
835                 break;
836         case SEEK_CUR:
837                 if (((filp->f_pos + offset) > stream_end) ||
838                     ((filp->f_pos + offset) < stream_start))
839                         f_pos = -EINVAL;
840                 else
841                         f_pos = filp->f_pos + offset;
842                 break;
843         case SEEK_END:
844                 if ((offset > 0) ||
845                     (offset < (stream_start - stream_end)))
846                         f_pos = -EINVAL;
847                 else
848                         f_pos = stream_end + offset;
849                 break;
850         default:
851                 f_pos = -EINVAL;
852                 break;
853         }
854
855         if (f_pos >= 0) {
856                 filp->f_pos = f_pos;
857                 stream_f_pos = f_pos;
858         }
859
860         mutex_unlock(pdumpfs_mutex);
861
862         return f_pos;
863 }
864
865 static ssize_t
866 pdumpfs_stream_read(struct file *filp, char __user *buf, size_t size,
867                     loff_t *f_pos)
868 {
869         size_t ret = 0;
870
871         mutex_lock(pdumpfs_mutex);
872
873         if ((stream_end <= 0) || (*f_pos >= stream_end))
874                 ret = 0;
875         else if (*f_pos < stream_start) {
876                 if (size > (stream_start - *f_pos))
877                         size = stream_start - *f_pos;
878                 ret = pdumpfs_stream_buffer_clear(buf, size);
879         } else {
880                 loff_t start = stream_start;
881                 struct pdumpfs_frame *frame = frame_stream;
882
883                 /* skip frames that are before our offset */
884                 while ((start + frame->offset) <= *f_pos) {
885                         start += frame->offset;
886                         frame = frame->next;
887                 }
888
889                 ret = pdumpfs_stream_buffer_fill(frame, buf, *f_pos - start,
890                                                  size);
891         }
892
893         if (ret > 0)
894                 *f_pos += ret;
895         mutex_unlock(pdumpfs_mutex);
896         return ret;
897 }
898
899 static const struct file_operations pdumpfs_stream_fops = {
900         .owner = THIS_MODULE,
901         .llseek = pdumpfs_stream_llseek,
902         .read = pdumpfs_stream_read,
903         .open = pdumpfs_stream_open,
904         .release = pdumpfs_stream_release,
905 };
906
907 static struct dentry *pdumpfs_dir;
908
909 static void
910 pdumpfs_file_create(const char *name, mode_t mode,
911                     const struct file_operations *fops)
912 {
913         struct dentry *tmp = NULL;
914
915         tmp = debugfs_create_file(name, mode, pdumpfs_dir, NULL, fops);
916         if (!tmp)
917                 pr_err("%s: failed to create pvr/%s file.\n", __func__, name);
918 }
919
920 static int
921 pdumpfs_fs_init(void)
922 {
923         if (!pvr_debugfs_dir) {
924                 pr_err("%s: debugfs pvr/ directory does not exist.\n",
925                        __func__);
926                 return -ENOENT;
927         }
928
929         pdumpfs_dir = debugfs_create_dir("pdump", pvr_debugfs_dir);
930         if (!pdumpfs_dir) {
931                 pr_err("%s: failed to create top level directory.\n",
932                        __func__);
933                 return -ENOENT;
934         }
935
936         pdumpfs_file_create("mode", S_IRUSR | S_IWUSR,
937                             &pdumpfs_mode_fops);
938         pdumpfs_file_create("modes_possible", S_IRUSR,
939                             &pdumpfs_modes_possible_fops);
940
941         pdumpfs_file_create("frame_count_max", S_IRUSR | S_IWUSR,
942                             &pdumpfs_frame_count_max_fops);
943
944         pdumpfs_file_create("init_frame", S_IRUSR,
945                             &pdumpfs_init_fops);
946         pdumpfs_file_create("current_frame", S_IRUSR,
947                             &pdumpfs_current_fops);
948         pdumpfs_file_create("stream_frames", S_IRUSR,
949                             &pdumpfs_stream_fops);
950
951         return 0;
952 }
953
954 static void
955 pdumpfs_fs_destroy(void)
956 {
957         if (pdumpfs_dir)
958                 debugfs_remove_recursive(pdumpfs_dir);
959 }
960
961 int
962 pdumpfs_init(void)
963 {
964         int ret;
965
966         mutex_init(pdumpfs_mutex);
967
968         ret = frame_new(0, 0);
969         if (ret < 0)
970                 return ret;
971
972         pdumpfs_fs_init();
973
974         return 0;
975 }
976
977 void
978 pdumpfs_cleanup(void)
979 {
980         pdumpfs_fs_destroy();
981
982         frame_destroy_all();
983 }