1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * mpegplayer buffering routines
12 * Copyright (c) 2007 Michael Sevakis
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
24 #include "mpegplayer.h"
27 static struct mutex disk_buf_mtx SHAREDBSS_ATTR
;
28 static struct event_queue disk_buf_queue SHAREDBSS_ATTR
;
29 static struct queue_sender_list disk_buf_queue_send SHAREDBSS_ATTR
;
30 static uint32_t disk_buf_stack
[DEFAULT_STACK_SIZE
*2/sizeof(uint32_t)];
32 struct disk_buf disk_buf SHAREDBSS_ATTR
;
33 static void *nf_list
[MPEGPLAYER_MAX_STREAMS
+1];
35 static inline void disk_buf_lock(void)
37 rb
->mutex_lock(&disk_buf_mtx
);
40 static inline void disk_buf_unlock(void)
42 rb
->mutex_unlock(&disk_buf_mtx
);
45 static inline void disk_buf_on_clear_data_notify(struct stream_hdr
*sh
)
47 DEBUGF("DISK_BUF_CLEAR_DATA_NOTIFY: 0x%02X (cleared)\n",
48 STR_FROM_HDR(sh
)->id
);
49 list_remove_item(nf_list
, sh
);
52 inline bool disk_buf_is_data_ready(struct stream_hdr
*sh
,
55 /* Data window available? */
56 off_t right
= sh
->win_right
;
58 /* Margins past end-of-file can still return true */
59 if (right
> disk_buf
.filesize
- margin
)
60 right
= disk_buf
.filesize
- margin
;
62 return sh
->win_left
>= disk_buf
.win_left
&&
63 right
+ margin
<= disk_buf
.win_right
;
66 void dbuf_l2_init(struct dbuf_l2_cache
*l2_p
)
68 l2_p
->addr
= OFF_T_MAX
; /* Mark as invalid */
71 static int disk_buf_on_data_notify(struct stream_hdr
*sh
)
73 DEBUGF("DISK_BUF_DATA_NOTIFY: 0x%02X ", STR_FROM_HDR(sh
)->id
);
75 if (sh
->win_left
<= sh
->win_right
)
77 /* Check if the data is already ready already */
78 if (disk_buf_is_data_ready(sh
, 0))
80 /* It was - don't register */
81 DEBUGF("(was ready)\n"
84 sh
->win_left
, sh
->win_right
,
85 disk_buf
.win_left
, disk_buf
.win_right
);
86 /* Be sure it's not listed though if multiple requests were made */
87 list_remove_item(nf_list
, sh
);
88 return DISK_BUF_NOTIFY_OK
;
91 switch (disk_buf
.state
)
94 case TSTATE_BUFFERING
:
96 disk_buf
.state
= TSTATE_BUFFERING
;
97 list_add_item(nf_list
, sh
);
98 DEBUGF("(registered)\n"
100 " dwl:%lu dwr:%lu\n",
101 sh
->win_left
, sh
->win_right
,
102 disk_buf
.win_left
, disk_buf
.win_right
);
103 return DISK_BUF_NOTIFY_REGISTERED
;
108 return DISK_BUF_NOTIFY_ERROR
;
111 static bool check_data_notifies_callback(struct stream_hdr
*sh
, intptr_t data
)
113 if (disk_buf_is_data_ready(sh
, 0))
115 /* Remove from list then post notification - post because send
116 * could result in a wait for each thread to finish resulting
118 list_remove_item(nf_list
, sh
);
119 str_post_msg(STR_FROM_HDR(sh
), DISK_BUF_DATA_NOTIFY
, 0);
120 DEBUGF("DISK_BUF_DATA_NOTIFY: 0x%02X (notified)\n",
121 STR_FROM_HDR(sh
)->id
);
128 /* Check registered streams and notify them if their data is available */
129 static inline void check_data_notifies(void)
131 list_enum_items(nf_list
,
132 (list_enum_callback_t
)check_data_notifies_callback
,
136 /* Clear all registered notifications - do not post them */
137 static inline void clear_data_notifies(void)
139 list_clear_all(nf_list
);
142 /* Background buffering when streaming */
143 static inline void disk_buf_buffer(void)
145 struct stream_window sw
;
147 switch (disk_buf
.state
)
154 /* Get remaining minimum data based upon the stream closest to the
155 * right edge of the window */
156 if (!stream_get_window(&sw
))
159 time
= stream_get_ticks(NULL
);
160 wm
= muldiv_uint32(5*CLOCK_RATE
, sw
.right
- disk_buf
.pos_last
,
161 time
- disk_buf
.time_last
);
162 wm
= MIN(wm
, (size_t)disk_buf
.size
);
163 wm
= MAX(wm
, DISK_BUF_LOW_WATERMARK
);
165 disk_buf
.time_last
= time
;
166 disk_buf
.pos_last
= sw
.right
;
168 /* Fast attack, slow decay */
169 disk_buf
.low_wm
= (wm
> (size_t)disk_buf
.low_wm
) ?
170 wm
: AVERAGE(disk_buf
.low_wm
, wm
, 16);
173 rb
->splashf(0, "*%10ld %10ld", disk_buf
.low_wm
,
174 disk_buf
.win_right
- sw
.right
);
177 if (disk_buf
.win_right
- sw
.right
> disk_buf
.low_wm
)
180 disk_buf
.state
= TSTATE_BUFFERING
;
184 case TSTATE_BUFFERING
:
187 uint32_t tag
, *tag_p
;
189 /* Limit buffering up to the stream with the least progress */
190 if (!stream_get_window(&sw
))
192 disk_buf
.state
= TSTATE_DATA
;
198 if (disk_buf
.tail
>= disk_buf
.end
)
199 disk_buf
.tail
= disk_buf
.start
;
201 len
= disk_buf
.size
- disk_buf
.win_right
+ sw
.left
;
203 if (len
< DISK_BUF_PAGE_SIZE
)
205 /* Free space is less than one page */
206 disk_buf
.state
= TSTATE_DATA
;
207 disk_buf
.low_wm
= DISK_BUF_LOW_WATERMARK
;
212 len
= disk_buf
.tail
- disk_buf
.start
;
213 tag
= MAP_OFFSET_TO_TAG(disk_buf
.win_right
);
214 tag_p
= &disk_buf
.cache
[len
>> DISK_BUF_PAGE_SHIFT
];
218 if (disk_buf
.need_seek
)
220 rb
->lseek(disk_buf
.in_file
, disk_buf
.win_right
, SEEK_SET
);
221 disk_buf
.need_seek
= false;
224 n
= rb
->read(disk_buf
.in_file
, disk_buf
.tail
, DISK_BUF_PAGE_SIZE
);
228 /* Error or end of stream */
229 disk_buf
.state
= TSTATE_EOS
;
234 if (len
< DISK_GUARDBUF_SIZE
)
236 /* Autoguard guard-o-rama - maintain guardbuffer coherency */
237 rb
->memcpy(disk_buf
.end
+ len
, disk_buf
.tail
,
238 MIN(DISK_GUARDBUF_SIZE
- len
, n
));
241 /* Update the cache entry for this page */
246 /* Skipping a read */
247 n
= MIN(DISK_BUF_PAGE_SIZE
,
248 disk_buf
.filesize
- disk_buf
.win_right
);
249 disk_buf
.need_seek
= true;
252 disk_buf
.tail
+= DISK_BUF_PAGE_SIZE
;
254 /* Keep left edge moving forward */
256 /* Advance right edge in temp variable first, then move
257 * left edge if overflow would occur. This avoids a stream
258 * thinking its data might be available when it actually
259 * may not end up that way after a slide of the window. */
260 len
= disk_buf
.win_right
+ n
;
262 if (len
- disk_buf
.win_left
> disk_buf
.size
)
263 disk_buf
.win_left
+= n
;
265 disk_buf
.win_right
= len
;
267 /* Continue buffering until filled or file end */
269 } /* TSTATE_BUFFERING: */
276 static void disk_buf_on_reset(ssize_t pos
)
282 disk_buf
.state
= TSTATE_INIT
;
283 disk_buf
.status
= STREAM_STOPPED
;
284 clear_data_notifies();
286 if (pos
>= disk_buf
.filesize
)
288 /* Anchor on page immediately following the one containing final
290 anchor
= disk_buf
.file_pages
* DISK_BUF_PAGE_SIZE
;
291 disk_buf
.win_left
= disk_buf
.filesize
;
295 anchor
= pos
& ~DISK_BUF_PAGE_MASK
;
296 disk_buf
.win_left
= anchor
;
299 /* Collect all valid data already buffered that is contiguous with the
300 * current position - probe to left, then to right */
303 page
= MAP_OFFSET_TO_PAGE(anchor
);
304 tag
= MAP_OFFSET_TO_TAG(anchor
);
308 if (--tag
, --page
< 0)
309 page
= disk_buf
.pgcount
- 1;
311 if (disk_buf
.cache
[page
] != tag
)
314 disk_buf
.win_left
= tag
<< DISK_BUF_PAGE_SHIFT
;
319 if (anchor
< disk_buf
.filesize
)
321 page
= MAP_OFFSET_TO_PAGE(anchor
);
322 tag
= MAP_OFFSET_TO_TAG(anchor
);
326 if (disk_buf
.cache
[page
] != tag
)
329 if (++tag
, ++page
>= disk_buf
.pgcount
)
332 anchor
+= DISK_BUF_PAGE_SIZE
;
334 while (anchor
< disk_buf
.filesize
);
337 if (anchor
>= disk_buf
.filesize
)
339 disk_buf
.win_right
= disk_buf
.filesize
;
340 disk_buf
.state
= TSTATE_EOS
;
344 disk_buf
.win_right
= anchor
;
347 disk_buf
.tail
= disk_buf
.start
+ MAP_OFFSET_TO_BUFFER(anchor
);
349 DEBUGF("disk buf reset\n"
350 " dwl:%ld dwr:%ld\n",
351 disk_buf
.win_left
, disk_buf
.win_right
);
353 /* Next read position is at right edge */
354 rb
->lseek(disk_buf
.in_file
, disk_buf
.win_right
, SEEK_SET
);
355 disk_buf
.need_seek
= false;
357 disk_buf_reply_msg(disk_buf
.win_right
- disk_buf
.win_left
);
360 static void disk_buf_on_stop(void)
362 bool was_buffering
= disk_buf
.state
== TSTATE_BUFFERING
;
364 disk_buf
.state
= TSTATE_EOS
;
365 disk_buf
.status
= STREAM_STOPPED
;
366 clear_data_notifies();
368 disk_buf_reply_msg(was_buffering
);
371 static void disk_buf_on_play_pause(bool play
, bool forcefill
)
373 struct stream_window sw
;
375 if (disk_buf
.state
!= TSTATE_EOS
)
379 /* Force buffer filling to top */
380 disk_buf
.state
= TSTATE_BUFFERING
;
382 else if (disk_buf
.state
!= TSTATE_BUFFERING
)
384 /* If not filling already, simply monitor */
385 disk_buf
.state
= TSTATE_DATA
;
388 /* else end of stream - no buffering to do */
390 disk_buf
.pos_last
= stream_get_window(&sw
) ? sw
.right
: 0;
391 disk_buf
.time_last
= stream_get_ticks(NULL
);
393 disk_buf
.status
= play
? STREAM_PLAYING
: STREAM_PAUSED
;
396 static int disk_buf_on_load_range(struct dbuf_range
*rng
)
398 uint32_t tag
= rng
->tag_start
;
399 uint32_t tag_end
= rng
->tag_end
;
400 int page
= rng
->pg_start
;
402 /* Check if a seek is required */
403 bool need_seek
= rb
->lseek(disk_buf
.in_file
, 0, SEEK_CUR
)
404 != (off_t
)(tag
<< DISK_BUF_PAGE_SHIFT
);
408 uint32_t *tag_p
= &disk_buf
.cache
[page
];
412 /* Page not cached - load it */
417 rb
->lseek(disk_buf
.in_file
, tag
<< DISK_BUF_PAGE_SHIFT
,
422 o
= page
<< DISK_BUF_PAGE_SHIFT
;
423 n
= rb
->read(disk_buf
.in_file
, disk_buf
.start
+ o
,
429 return DISK_BUF_NOTIFY_ERROR
;
438 if (o
< DISK_GUARDBUF_SIZE
)
440 /* Autoguard guard-o-rama - maintain guardbuffer coherency */
441 rb
->memcpy(disk_buf
.end
+ o
, disk_buf
.start
+ o
,
442 MIN(DISK_GUARDBUF_SIZE
- o
, n
));
445 /* Update the cache entry */
450 /* Skipping a disk read - must seek on next one */
454 if (++page
>= disk_buf
.pgcount
)
457 while (++tag
<= tag_end
);
459 return DISK_BUF_NOTIFY_OK
;
462 static void disk_buf_thread(void)
464 struct queue_event ev
;
466 disk_buf
.state
= TSTATE_EOS
;
467 disk_buf
.status
= STREAM_STOPPED
;
471 if (disk_buf
.state
!= TSTATE_EOS
)
473 /* Poll buffer status and messages */
474 rb
->queue_wait_w_tmo(disk_buf
.q
, &ev
,
475 disk_buf
.state
== TSTATE_BUFFERING
?
480 /* Sit idle and wait for commands */
481 rb
->queue_wait(disk_buf
.q
, &ev
);
487 if (disk_buf
.state
== TSTATE_EOS
)
492 /* Check for any due notifications if any are pending */
493 if (*nf_list
!= NULL
)
494 check_data_notifies();
496 /* Still more data left? */
497 if (disk_buf
.state
!= TSTATE_EOS
)
500 /* Nope - end of stream */
503 case DISK_BUF_CACHE_RANGE
:
504 disk_buf_reply_msg(disk_buf_on_load_range(
505 (struct dbuf_range
*)ev
.data
));
509 disk_buf_on_reset(ev
.data
);
518 disk_buf_on_play_pause(ev
.id
== STREAM_PLAY
, ev
.data
!= 0);
519 disk_buf_reply_msg(1);
523 disk_buf
.state
= TSTATE_EOS
;
526 case DISK_BUF_DATA_NOTIFY
:
527 disk_buf_reply_msg(disk_buf_on_data_notify(
528 (struct stream_hdr
*)ev
.data
));
531 case DISK_BUF_CLEAR_DATA_NOTIFY
:
532 disk_buf_on_clear_data_notify((struct stream_hdr
*)ev
.data
);
533 disk_buf_reply_msg(1);
539 /* Caches some data from the current file */
540 static ssize_t
disk_buf_probe(off_t start
, size_t length
, void **p
)
543 uint32_t tag
, tag_end
;
546 /* Can't read past end of file */
547 if (length
> (size_t)(disk_buf
.filesize
- start
))
549 length
= disk_buf
.filesize
- start
;
552 /* Can't cache more than the whole buffer size */
553 if (length
> (size_t)disk_buf
.size
)
555 length
= disk_buf
.size
;
557 /* Zero-length probes permitted */
559 end
= start
+ length
;
561 /* Prepare the range probe */
562 tag
= MAP_OFFSET_TO_TAG(start
);
563 tag_end
= MAP_OFFSET_TO_TAG(end
);
564 page
= MAP_OFFSET_TO_PAGE(start
);
566 /* If the end is on a page boundary, check one less or an extra
567 * one will be probed */
568 if (tag_end
> tag
&& (end
& DISK_BUF_PAGE_MASK
) == 0)
575 *p
= disk_buf
.start
+ (page
<< DISK_BUF_PAGE_SHIFT
)
576 + (start
& DISK_BUF_PAGE_MASK
);
579 /* Obtain initial load point. If all data was cached, no message is sent
580 * otherwise begin on the first page that is not cached. Since we have to
581 * send the message anyway, the buffering thread will determine what else
582 * requires loading on its end in order to cache the specified range. */
585 if (disk_buf
.cache
[page
] != tag
)
587 static struct dbuf_range rng IBSS_ATTR
;
590 DEBUGF("disk_buf: cache miss\n");
592 rng
.tag_end
= tag_end
;
595 result
= rb
->queue_send(disk_buf
.q
, DISK_BUF_CACHE_RANGE
,
598 return result
== DISK_BUF_NOTIFY_OK
? (ssize_t
)length
: -1;
601 if (++page
>= disk_buf
.pgcount
)
604 while (++tag
<= tag_end
);
609 /* Attempt to get a pointer to size bytes on the buffer. Returns real amount of
610 * data available as well as the size of non-wrapped data after *p. */
611 ssize_t
_disk_buf_getbuffer(size_t size
, void **pp
, void **pwrap
,
616 size
= disk_buf_probe(disk_buf
.offset
, size
, pp
);
618 if (size
!= (size_t)-1 && pwrap
&& sizewrap
)
620 uint8_t *p
= (uint8_t *)*pp
;
622 if (p
+ size
> disk_buf
.end
+ DISK_GUARDBUF_SIZE
)
624 /* Return pointer to wraparound and the size of same */
625 size_t nowrap
= (disk_buf
.end
+ DISK_GUARDBUF_SIZE
) - p
;
626 *pwrap
= disk_buf
.start
+ DISK_GUARDBUF_SIZE
;
627 *sizewrap
= size
- nowrap
;
641 ssize_t
_disk_buf_getbuffer_l2(struct dbuf_l2_cache
*l2
,
642 size_t size
, void **pp
)
651 /* Shouldn't have to check this normally */
652 DEBUGF("disk_buf_getbuffer_l2: l2 = NULL!\n");
655 if (size
> DISK_BUF_L2_CACHE_SIZE
)
657 /* Asking for too much; just go through L1 */
658 return disk_buf_getbuffer(size
, pp
, NULL
, NULL
);
661 offs
= disk_buf
.offset
; /* Other calls keep this within bounds */
664 if (offs
>= l2_addr
&& offs
< l2_addr
+ DISK_BUF_L2_CACHE_SIZE
)
666 /* Data is in the local buffer */
667 offs
&= DISK_BUF_L2_CACHE_MASK
;
669 *pp
= l2
->data
+ offs
;
670 if (offs
+ size
> l2
->size
)
671 size
= l2
->size
- offs
; /* Keep size within file limits */
676 /* Have to probe main buffer */
677 l2_addr
= offs
& ~DISK_BUF_L2_CACHE_MASK
;
678 l2_size
= DISK_BUF_L2_CACHE_SIZE
*2; /* 2nd half is a guard buffer */
682 l2_size
= disk_buf_probe(l2_addr
, l2_size
, &l2_p
);
684 if (l2_size
!= (size_t)-1)
686 rb
->memcpy(l2
->data
, l2_p
, l2_size
);
692 *pp
= l2
->data
+ offs
;
693 if (offs
+ size
> l2
->size
)
694 size
= l2
->size
- offs
; /* Keep size within file limits */
707 /* Read size bytes of data into a buffer - advances the buffer pointer
708 * and returns the real size read. */
709 ssize_t
disk_buf_read(void *buffer
, size_t size
)
715 size
= disk_buf_probe(disk_buf
.offset
, size
, PUN_PTR(void **, &p
));
717 if (size
!= (size_t)-1)
719 if (p
+ size
> disk_buf
.end
+ DISK_GUARDBUF_SIZE
)
722 size_t nowrap
= (disk_buf
.end
+ DISK_GUARDBUF_SIZE
) - p
;
723 rb
->memcpy(buffer
, p
, nowrap
);
724 rb
->memcpy(buffer
+ nowrap
, disk_buf
.start
+ DISK_GUARDBUF_SIZE
,
729 /* Read wasn't wrapped or guardbuffer holds it */
730 rb
->memcpy(buffer
, p
, size
);
733 disk_buf
.offset
+= size
;
741 ssize_t
disk_buf_lseek(off_t offset
, int whence
)
745 /* The offset returned is the result of the current thread's action and
746 * may be invalidated so a local result is returned and not the value
747 * of disk_buf.offset directly */
751 /* offset is just the offset */
754 offset
+= disk_buf
.offset
;
757 offset
= disk_buf
.filesize
+ offset
;
761 return -2; /* Invalid request */
764 if (offset
< 0 || offset
> disk_buf
.filesize
)
770 disk_buf
.offset
= offset
;
778 /* Prepare the buffer to enter the streaming state. Evaluates the available
779 * streaming window. */
780 ssize_t
disk_buf_prepare_streaming(off_t pos
, size_t len
)
786 else if (pos
> disk_buf
.filesize
)
787 pos
= disk_buf
.filesize
;
789 DEBUGF("prepare streaming:\n pos:%ld len:%lu\n", pos
, (unsigned long)len
);
791 pos
= disk_buf_lseek(pos
, SEEK_SET
);
792 len
= disk_buf_probe(pos
, len
, NULL
);
794 DEBUGF(" probe done: pos:%ld len:%lu\n", pos
, (unsigned long)len
);
796 len
= disk_buf_send_msg(STREAM_RESET
, pos
);
803 /* Set the streaming window to an arbitrary position within the file. Makes no
804 * probes to validate data. Use after calling another function to cause data
805 * to be cached and correct values are known. */
806 ssize_t
disk_buf_set_streaming_window(off_t left
, off_t right
)
814 else if (left
> disk_buf
.filesize
)
815 left
= disk_buf
.filesize
;
820 if (right
> disk_buf
.filesize
)
821 right
= disk_buf
.filesize
;
823 disk_buf
.win_left
= left
;
824 disk_buf
.win_right
= right
;
825 disk_buf
.tail
= disk_buf
.start
+ ((right
+ DISK_BUF_PAGE_SIZE
-1) &
826 ~DISK_BUF_PAGE_MASK
) % disk_buf
.size
;
828 len
= disk_buf
.win_right
- disk_buf
.win_left
;
835 void * disk_buf_offset2ptr(off_t offset
)
839 else if (offset
> disk_buf
.filesize
)
840 offset
= disk_buf
.filesize
;
842 return disk_buf
.start
+ (offset
% disk_buf
.size
);
845 void disk_buf_close(void)
849 if (disk_buf
.in_file
>= 0)
851 rb
->close(disk_buf
.in_file
);
852 disk_buf
.in_file
= -1;
854 /* Invalidate entire cache */
855 rb
->memset(disk_buf
.cache
, 0xff,
856 disk_buf
.pgcount
*sizeof (*disk_buf
.cache
));
857 disk_buf
.file_pages
= 0;
858 disk_buf
.filesize
= 0;
865 int disk_buf_open(const char *filename
)
873 fd
= rb
->open(filename
, O_RDONLY
);
877 ssize_t filesize
= rb
->filesize(fd
);
881 rb
->close(disk_buf
.in_file
);
885 disk_buf
.filesize
= filesize
;
886 /* Number of file pages rounded up toward +inf */
887 disk_buf
.file_pages
= ((size_t)filesize
+ DISK_BUF_PAGE_SIZE
-1)
888 / DISK_BUF_PAGE_SIZE
;
889 disk_buf
.in_file
= fd
;
898 intptr_t disk_buf_send_msg(long id
, intptr_t data
)
900 return rb
->queue_send(disk_buf
.q
, id
, data
);
903 void disk_buf_post_msg(long id
, intptr_t data
)
905 rb
->queue_post(disk_buf
.q
, id
, data
);
908 void disk_buf_reply_msg(intptr_t retval
)
910 rb
->queue_reply(disk_buf
.q
, retval
);
913 bool disk_buf_init(void)
917 rb
->mutex_init(&disk_buf_mtx
);
919 disk_buf
.q
= &disk_buf_queue
;
920 rb
->queue_init(disk_buf
.q
, false);
922 disk_buf
.state
= TSTATE_EOS
;
923 disk_buf
.status
= STREAM_STOPPED
;
925 disk_buf
.in_file
= -1;
926 disk_buf
.filesize
= 0;
927 disk_buf
.win_left
= 0;
928 disk_buf
.win_right
= 0;
929 disk_buf
.time_last
= 0;
930 disk_buf
.pos_last
= 0;
931 disk_buf
.low_wm
= DISK_BUF_LOW_WATERMARK
;
933 disk_buf
.start
= mpeg_malloc_all((size_t*)&disk_buf
.size
, MPEG_ALLOC_DISKBUF
);
934 if (disk_buf
.start
== NULL
)
938 CACHEALIGN_BUFFER(disk_buf
.start
, disk_buf
.size
);
939 disk_buf
.start
= UNCACHED_ADDR(disk_buf
.start
);
941 disk_buf
.size
-= DISK_GUARDBUF_SIZE
;
942 disk_buf
.pgcount
= disk_buf
.size
/ DISK_BUF_PAGE_SIZE
;
944 /* Fit it as tightly as possible */
945 while (disk_buf
.pgcount
*(sizeof (*disk_buf
.cache
) + DISK_BUF_PAGE_SIZE
)
946 > (size_t)disk_buf
.size
)
951 disk_buf
.cache
= (typeof (disk_buf
.cache
))disk_buf
.start
;
952 disk_buf
.start
+= sizeof (*disk_buf
.cache
)*disk_buf
.pgcount
;
953 disk_buf
.size
= disk_buf
.pgcount
*DISK_BUF_PAGE_SIZE
;
954 disk_buf
.end
= disk_buf
.start
+ disk_buf
.size
;
955 disk_buf
.tail
= disk_buf
.start
;
957 DEBUGF("disk_buf info:\n"
960 disk_buf
.pgcount
, (long)disk_buf
.size
);
962 rb
->memset(disk_buf
.cache
, 0xff,
963 disk_buf
.pgcount
*sizeof (*disk_buf
.cache
));
965 disk_buf
.thread
= rb
->create_thread(
966 disk_buf_thread
, disk_buf_stack
, sizeof(disk_buf_stack
), 0,
967 "mpgbuffer" IF_PRIO(, PRIORITY_BUFFERING
) IF_COP(, CPU
));
969 rb
->queue_enable_queue_send(disk_buf
.q
, &disk_buf_queue_send
,
972 if (disk_buf
.thread
== 0)
975 /* Wait for thread to initialize */
976 disk_buf_send_msg(STREAM_NULL
, 0);
981 void disk_buf_exit(void)
983 if (disk_buf
.thread
!= 0)
985 rb
->queue_post(disk_buf
.q
, STREAM_QUIT
, 0);
986 rb
->thread_wait(disk_buf
.thread
);