1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 Nicolas Pennequin
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
25 struct plugin_api
* rb
;
27 #define GUARD_SIZE (32*1024)
29 /* amount of data to read in one read() call */
30 #define AUDIO_DEFAULT_FILECHUNK (1024*32)
32 /* Ring buffer helper macros */
33 /* Buffer pointer (p) plus value (v), wrapped if necessary */
34 #define RINGBUF_ADD(p,v) ((p+v)<buffer_len ? p+v : p+v-buffer_len)
35 /* Buffer pointer (p) minus value (v), wrapped if necessary */
36 #define RINGBUF_SUB(p,v) ((p>=v) ? p-v : p+buffer_len-v)
37 /* How far value (v) plus buffer pointer (p1) will cross buffer pointer (p2) */
38 #define RINGBUF_ADD_CROSS(p1,v,p2) \
39 ((p1<p2) ? (int)(p1+v)-(int)p2 : (int)(p1+v-p2)-(int)buffer_len)
40 /* Bytes available in the buffer */
41 #define BUF_USED RINGBUF_SUB(buf_widx, buf_ridx)
43 #ifdef ROCKBOX_HAS_LOGF
44 #define DEBUGF rb->logf
47 struct memory_handle
{
48 int id
; /* A unique ID for the handle */
49 //enum data_type type;
50 //enum data_state state;
53 size_t data
; /* Start index of the handle's data buffer */
54 size_t data_len
; /* Length of the data buffer for the handle */
55 size_t ridx
; /* Current read pointer, relative to the main buffer */
56 size_t widx
; /* Current write pointer */
57 size_t filesize
; /* File total length */
58 size_t filerem
; /* Remaining bytes of file NOT in buffer */
59 size_t available
; /* Available bytes to read from buffer */
60 size_t offset
; /* Offset at which we started reading the file */
61 struct memory_handle
*next
;
83 struct bufadvance_data
{
94 struct bufgetdata_data
{
102 static char *guard_buffer
;
104 static size_t buffer_len
;
105 static size_t buf_widx
;
106 static size_t buf_ridx
;
108 static size_t conf_filechunk
;
110 /* current memory handle in the linked list. NULL when the list is empty. */
111 static struct memory_handle
*cur_handle
;
112 /* first memory handle in the linked list. NULL when the list is empty. */
113 static struct memory_handle
*first_handle
;
114 static int num_handles
;
116 static void graph_view(int width
);
118 /* exported API functions (will be present in the header file) */
119 int bufopen(char *file
, size_t offset
);
120 void bufclose(int handle_id
);
121 void bufseek(int handle_id
, size_t offset
);
122 void bufadvance(int handle_id
, ssize_t offset
);
123 int bufread(int handle_id
, size_t size
, char *dest
);
124 int bufgetdata(int handle_id
, size_t size
, unsigned char **data
);
127 /* add a new handle to the linked list and return it. It will have become the
128 new current handle. The handle will reserve "data_size" bytes or if that's
129 not possible, decrease "data_size" to allow adding the handle. */
130 static struct memory_handle
*add_handle(int *data_size
)
132 /* this will give each handle a unique id */
133 static int cur_handle_id
= 0;
135 int len
= (data_size
? *data_size
: 0) + sizeof(struct memory_handle
);
137 /* check that we actually can add the handle and its data */
138 int overlap
= RINGBUF_ADD_CROSS(buf_widx
, len
+ 3, buf_ridx
);
140 *data_size
-= overlap
;
143 if (len
< sizeof(struct memory_handle
)) {
147 /* make sure buf_widx is 32-bit aligned so that the handle struct is. */
148 buf_widx
= (RINGBUF_ADD(buf_widx
, 3)) & ~3;
150 struct memory_handle
*new_handle
= (struct memory_handle
*)(buffer
+ buf_widx
);
152 /* only advance the buffer write index of the size of the struct */
153 buf_widx
= RINGBUF_ADD(buf_widx
, sizeof(struct memory_handle
));
156 /* the new handle is the first one */
157 first_handle
= new_handle
;
161 cur_handle
->next
= new_handle
;
164 cur_handle
= new_handle
;
165 cur_handle
->id
= cur_handle_id
++;
166 cur_handle
->next
= NULL
;
171 /* delete a given memory handle from the linked list
172 and return true for success. Nothing is actually erased from memory. */
173 static bool rm_handle(struct memory_handle
*h
)
175 if (h
== first_handle
) {
176 first_handle
= h
->next
;
177 if (h
== cur_handle
) {
178 DEBUGF("removing the first and last handle\n");
179 /* h was the first and last handle */
183 buf_ridx
= (void *)first_handle
- (void *)buffer
;
186 struct memory_handle
*m
= first_handle
;
187 while (m
&& m
->next
!= h
) {
190 if (h
&& m
&& m
->next
== h
) {
192 if (h
== cur_handle
) {
204 /* these are unfortunalty needed to be global
205 so move_handle can invalidate them */
206 static int cached_handle_id
= -1;
207 static struct memory_handle
*cached_handle
= NULL
;
209 /* Return a pointer to the memory handle of given ID.
210 NULL if the handle wasn't found */
211 static struct memory_handle
*find_handle(int handle_id
)
213 /* simple caching because most of the time the requested handle
214 will either be the same as the last, or the one after the last */
217 if (cached_handle_id
== handle_id
&& cached_handle_id
== cached_handle
->id
)
218 return cached_handle
;
219 else if (cached_handle
->next
&& (cached_handle
->next
->id
== handle_id
))
221 /* JD's quick testing showd this block was only entered
222 2/1971 calls to find_handle.
223 8/1971 calls to find_handle resulted in a cache miss */
224 cached_handle
= cached_handle
->next
;
225 cached_handle_id
= handle_id
;
226 return cached_handle
;
230 struct memory_handle
*m
= first_handle
;
231 while (m
&& m
->id
!= handle_id
) {
234 cached_handle_id
= handle_id
;
236 return (m
&& m
->id
== handle_id
) ? m
: NULL
;
239 /* Move a memory handle to newpos.
240 Return a pointer to the new location of the handle */
241 static struct memory_handle
*move_handle(size_t newpos
, struct memory_handle
*h
)
243 /* make sure newpos is 32-bit aligned so that the handle struct is. */
244 newpos
= (RINGBUF_ADD(newpos
, 3)) & ~3;
246 DEBUGF("move_handle\n");
247 struct memory_handle
*dest
= (struct memory_handle
*)(buffer
+ newpos
);
249 /* Invalidate the cache to prevent it from keeping the old location of h */
250 if (h
== cached_handle
)
251 cached_handle
= NULL
;
253 /* the cur_handle pointer might need updating */
254 if (h
== cur_handle
) {
258 if (h
== first_handle
) {
262 struct memory_handle
*m
= first_handle
;
263 while (m
&& m
->next
!= h
) {
266 if (h
&& m
&& m
->next
== h
) {
273 DEBUGF("h: %lx", (long)h);
274 DEBUGF("h->id: %d", h->id);
275 DEBUGF("dest: %lx", (long)dest);
276 DEBUGF("dest->id: %d", dest->id);
279 rb
->memmove(dest
, h
, sizeof(struct memory_handle
));
281 DEBUGF("h: %lx", (long)h);
282 DEBUGF("dest: %lx", (long)dest);
283 DEBUGF("dest->id: %d", dest->id);
287 /* Buffer data for the given handle. Return the amount of data buffered
288 or -1 if the handle wasn't found */
289 static int buffer_handle(int handle_id
)
291 DEBUGF("buffer_handle(%d)", handle_id
);
292 struct memory_handle
*h
= find_handle(handle_id
);
296 if (h
->filerem
== 0) {
297 /* nothing left to buffer */
301 if (h
->fd
< 0) /* file closed, reopen */
304 h
->fd
= rb
->open(h
->path
, O_RDONLY
);
312 rb
->lseek(h
->fd
, h
->offset
, SEEK_SET
);
316 while (h
->filerem
> 0)
318 //DEBUGF("h: %d\n", (void *)h - (void *)buffer);
319 //DEBUGF("buf_widx: %ld\n", (long)buf_widx);
320 /* max amount to copy */
321 size_t copy_n
= MIN(conf_filechunk
, buffer_len
- buf_widx
);
323 /* stop copying if it would overwrite the reading position */
324 if (RINGBUF_ADD_CROSS(buf_widx
, copy_n
, buf_ridx
) >= 0)
327 /* rc is the actual amount read */
328 int rc
= rb
->read(h
->fd
, &buffer
[buf_widx
], copy_n
);
332 //DEBUGF("failed on fd %d at buf_widx = %ld for handle %d\n", h->fd, (long)buf_widx, h->id);
333 DEBUGF("File ended %ld bytes early\n", (long)h
->filerem
);
334 h
->filesize
-= h
->filerem
;
340 h
->widx
= RINGBUF_ADD(h
->widx
, rc
);
348 if (h
->filerem
== 0) {
349 /* finished buffering the file */
353 DEBUGF("buffered %d bytes (%d of %d available, remaining: %d)\n",
354 ret
, h
->available
, h
->filesize
, h
->filerem
);
358 /* Free buffer space by moving the first handle struct right before the useful
359 part of its data buffer */
360 static void free_buffer(int handle_id
)
362 DEBUGF("free_buffer(%d)\n", handle_id
);
363 struct memory_handle
*h
= find_handle(handle_id
);
367 size_t delta
= RINGBUF_SUB(h
->ridx
, h
->data
);
368 size_t dest
= RINGBUF_ADD((void *)h
- (void *)buffer
, delta
);
369 //DEBUGF("buf_ridx: %ld, buf_widx: %ld\n", (long)buf_ridx, (long)buf_widx);
370 //DEBUGF("dest: %ld, delta: %ld\n", (long)dest, (long)delta);
371 h
= move_handle(dest
, h
);
372 h
->data
= RINGBUF_ADD(h
->data
, delta
);
374 h
->available
-= delta
;
375 #ifndef ROCKBOX_HAS_LOGF
380 /* Request a file be buffered
381 filename: name of the file t open
382 offset: starting offset to buffer from the file
383 RETURNS: <0 if the file cannot be opened, or one file already
384 queued to be opened, otherwise the handle for the file in the buffer
386 static int buffering_bufopen(char *file
, size_t offset
)
388 DEBUGF("bufopen: %s (offset: %d)\n", file
, offset
);
390 int fd
= rb
->open(file
, O_RDONLY
);
395 rb
->lseek(fd
, offset
, SEEK_SET
);
397 int size
= rb
->filesize(fd
) - offset
;
398 struct memory_handle
*h
= add_handle(&size
);
401 DEBUGF("failed to add handle\n");
405 rb
->strncpy(h
->path
, file
, MAX_PATH
);
407 h
->filesize
= rb
->filesize(fd
);
408 h
->filerem
= h
->filesize
- offset
;
416 DEBUGF("added handle : %d\n", h
->id
);
420 /* Close the handle. Return 0 for success and < 0 for failure */
421 static int buffering_bufclose(int handle_id
)
423 DEBUGF("bufclose(%d)\n", handle_id
);
424 struct memory_handle
*h
= find_handle(handle_id
);
432 /* Set the reading index in a handle (relatively to the start of the handle data).
433 Return 0 for success and < 0 for failure */
434 static int buffering_bufseek(int handle_id
, size_t offset
)
436 struct memory_handle
*h
= find_handle(handle_id
);
440 if (offset
> h
->available
)
443 h
->ridx
= RINGBUF_ADD(h
->data
, offset
);
447 /* Advance the reading index in a handle (relatively to its current position).
448 Return 0 for success and < 0 for failure */
449 static int buffering_bufadvance(int handle_id
, ssize_t offset
)
451 struct memory_handle
*h
= find_handle(handle_id
);
457 if (offset
> h
->available
- RINGBUF_SUB(h
->ridx
, h
->data
))
460 h
->ridx
= RINGBUF_ADD(h
->ridx
, offset
);
464 if (-offset
> RINGBUF_SUB(h
->ridx
, h
->data
))
467 h
->ridx
= RINGBUF_SUB(h
->ridx
, -offset
);
473 /* Copy data from the given handle to the dest buffer.
474 Return the number of bytes copied or -1 for failure. */
475 static int buffering_bufread(int handle_id
, size_t size
, char *dest
)
477 struct memory_handle
*h
= find_handle(handle_id
);
478 size_t buffered_data
;
481 if (h
->available
== 0)
483 buffered_data
= MIN(size
, h
->available
);
485 if (h
->ridx
+ buffered_data
> buffer_len
)
487 size_t read
= buffer_len
- h
->ridx
;
488 rb
->memcpy(dest
, &buffer
[h
->ridx
], read
);
489 rb
->memcpy(dest
+read
, buffer
, buffered_data
- read
);
491 else rb
->memcpy(dest
, &buffer
[h
->ridx
], buffered_data
);
493 h
->ridx
= RINGBUF_ADD(h
->ridx
, buffered_data
);
494 h
->available
-= buffered_data
;
495 return buffered_data
;
498 /* Update the "data" pointer to make the handle's data available to the caller.
499 Return the length of the available linear data or -1 for failure. */
500 static long buffering_bufgetdata(int handle_id
, size_t size
, unsigned char **data
)
502 struct memory_handle
*h
= find_handle(handle_id
);
508 if (h
->ridx
+ size
> buffer_len
&&
509 h
->available
- RINGBUF_SUB(h
->ridx
, h
->data
) >= size
)
511 /* use the guard buffer to provide what was requested. */
512 int copy_n
= h
->ridx
+ size
- buffer_len
;
513 rb
->memcpy(guard_buffer
, (unsigned char *)buffer
, copy_n
);
518 ret
= MIN(h
->available
- RINGBUF_SUB(h
->ridx
, h
->data
),
519 buffer_len
- h
->ridx
);
522 *data
= (unsigned char *)(buffer
+ h
->ridx
);
524 //DEBUGF("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id, (long)h->ridx, ret);
530 struct memory_handle
*m1
, *m2
, *m3
, *m4
;
532 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
535 m1
= add_handle(NULL
);
537 if (cur_handle
!= m1
|| first_handle
!= m1
|| m1
->next
!= NULL
)
540 m2
= add_handle(NULL
);
542 if (cur_handle
!= m2
|| first_handle
!= m1
|| m1
->next
!= m2
|| m2
->next
!= NULL
)
545 m3
= add_handle(NULL
);
547 if (cur_handle
!= m3
|| first_handle
!= m1
|| m2
->next
!= m3
|| m3
->next
!= NULL
)
552 if (cur_handle
!= m3
|| first_handle
!= m1
|| m1
->next
!= m3
)
557 if (cur_handle
!= m1
|| first_handle
!= m1
|| m1
->next
!= NULL
)
560 m4
= add_handle(NULL
);
562 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m4
|| m4
->next
!= NULL
)
567 if (cur_handle
!= m4
|| first_handle
!= m4
)
572 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
575 m1
= add_handle(NULL
);
576 m2
= add_handle(NULL
);
577 m3
= add_handle(NULL
);
578 m4
= add_handle(NULL
);
580 if (cur_handle
!= m4
|| first_handle
!= m1
)
583 m2
= move_handle(RINGBUF_ADD(m2
->data
, 1024*100), m2
);
585 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m2
|| m2
->next
!= m3
)
588 m1
= move_handle(RINGBUF_ADD(m1
->data
, 1024*100*3), m1
);
590 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m2
)
598 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
605 static void list_handles(void)
607 struct memory_handle
*m
= first_handle
;
609 DEBUGF("%02d - %d\n", m
->id
, (void *)m
-(void *)buffer
);
615 #ifndef ROCKBOX_HAS_LOGF
616 /* display a nice graphical view of the ringbuffer. */
617 static void graph_view(int width
)
620 r_pos
= buf_ridx
* width
/ buffer_len
;
621 w_pos
= buf_widx
* width
/ buffer_len
;
624 for (i
=0; i
<= width
; i
++)
626 if (i
!= r_pos
&& i
!= w_pos
)
628 if (buf_ridx
<= buf_widx
)
630 if (i
> r_pos
&& i
< w_pos
) {
638 if (i
> r_pos
|| i
< w_pos
) {
647 if (i
== r_pos
&& i
== w_pos
)
649 if (buf_ridx
<= buf_widx
) {
654 } else if (i
== r_pos
) {
656 } else if (i
== w_pos
) {
666 bool buffer_init(void)
668 buffer
= rb
->plugin_get_audio_buffer(&buffer_len
);
671 DEBUGF("couldn't allocate buffer\n");
674 buffer_len
-= GUARD_SIZE
;
675 guard_buffer
= buffer
+ buffer_len
;
683 conf_filechunk
= AUDIO_DEFAULT_FILECHUNK
;
688 /* returns true if the file still has some on disk unread */
689 bool handle_has_data(int handle
)
691 struct memory_handle
*m
= find_handle(handle
);
694 return m
->filerem
!= 0;
699 bool need_rebuffer(void)
702 wasted
= first_handle
? RINGBUF_SUB(first_handle
->ridx
, first_handle
->data
) : 0;
703 while (wasted
> buffer_len
/ 2)
705 free_buffer(first_handle
->id
);
706 wasted
= first_handle
? RINGBUF_SUB(first_handle
->ridx
, first_handle
->data
) : 0;
708 free
= buffer_len
- BUF_USED
;
709 return ((free
>= buffer_len
/4));
712 bool disk_is_spinning(void)
719 static long playback_stack
[DEFAULT_STACK_SIZE
/sizeof(long)];
720 static struct thread_entry
* playbackthread_id
;
723 static struct event_queue buffering_queue
;
724 static struct queue_sender_list buffering_queue_sender_list
;
726 bool done_buffering
= false;
727 bool done_playing
= false;
730 #define MAX_HANDLES 64
731 static int handle_order
[MAX_HANDLES
];
732 static int last_handle
= -1;
734 void playback_thread(void)
736 int reading_handle
= 0;
737 int fd
= -1; /* used to write the files out as they are read */
740 /* "Playback thread" section */
743 DEBUGF("reading handle: %d\n", handle_order
[reading_handle
]);
747 if (reading_handle
>= last_handle
748 && !handle_has_data(handle_order
[reading_handle
]))
753 rb
->snprintf(file
, MAX_PATH
, "/file%d.mp3", reading_handle
);
754 fd
= rb
->open(file
, O_CREAT
|O_TRUNC
|O_WRONLY
);
757 DEBUGF("ERROROROROR\n");
758 rb
->splash(HZ
, "couldn't create file");
763 //read = bufread(handle_order[reading_handle], GUARD_SIZE,read_buffer);
764 //write(fd, read_buffer, read);
765 read
= bufgetdata(handle_order
[reading_handle
], GUARD_SIZE
, &data
);
766 read
= MIN(read
, GUARD_SIZE
);
767 rb
->write(fd
, data
, read
);
769 bufadvance(handle_order
[reading_handle
], read
);
774 DEBUGF("read %ld bytes from handle %d\n", total
, handle_order
[reading_handle
]);
776 /* close the fd/handle if there is no more data or an error */
777 if (read
< 0 || handle_has_data(handle_order
[reading_handle
]) == false)
779 DEBUGF("finished reading %d\n",handle_order
[reading_handle
]);
780 bufclose(handle_order
[reading_handle
]);
787 DEBUGF("there is data left to buffer for %d\n", handle_order
[reading_handle
]);
793 /* this is the plugin entry point */
794 enum plugin_status
plugin_start(struct plugin_api
* api
, void* parameter
)
800 char *argv
[] = { NULL
,
801 "/070623 - David Vendetta.mp3",
802 "/africanism all stars feat the hard boys - hard (bob sinclair remix).mp3",
803 "/alan_braxe__kris_menace_-_lumberjack.mp3",
804 "/Ame - Rej (A Hundred Birds Remix).mp3",
805 "/Antena - Camino del Sol (Joakim Remix).mp3" };
808 int current_handle
= -1;
809 struct memory_handle
*m
= NULL
;
817 DEBUGF("linked list test failed\n");
818 rb
->splash(HZ
, "linked list test failed");
822 /* Msg queue init - no need for queue_remove since it's not a registered
824 rb
->queue_init( &buffering_queue
, false );
826 playbackthread_id
= rb
->create_thread(playback_thread
,
828 sizeof(playback_stack
),
830 IF_PRIO(,PRIORITY_PLAYBACK
)
831 IF_COP(, CPU
, false));
833 if (!playbackthread_id
)
835 rb
->splash(HZ
, "failed to create playback thread");
843 while (!done_buffering
|| !done_playing
)
846 DEBUGF("buffer usage: %d handles_used: %d\n", BUF_USED, num_handles);
847 #ifndef ROCKBOX_HAS_LOGF
851 rb
->queue_wait_w_tmo(&buffering_queue
, &ev
, HZ
/2);
858 struct bufopen_data
*data
;
859 data
= (struct bufopen
*)ev
.data
;
860 buffering_bufopen(data
->file
, data
->offset
);
866 buffering_bufclose((int)ev
.data
);
872 struct bufseek_data
*data
;
873 data
= (struct bufseek_data
*)ev
.data
;
874 buffering_bufseek(data
->handle_id
, data
->offset
);
880 struct bufadvance_data
*data
;
881 data
= (struct bufadvance_data
*)ev
.data
;
882 buffering_bufadvance(data
->handle_id
, data
->offset
);
888 struct bufread_data
*data
;
889 data
= (struct bufread_data
*)ev
.data
;
890 buffering_bufread(data
->handle_id
, data
->size
, data
->dest
);
896 struct bufgetdata_data
*data
;
897 data
= (struct bufgetdata_data
*)ev
.data
;
898 buffering_bufgetdata(data
->handle_id
, data
->size
, data
->data
);
904 rb
->queue_reply(&buffering_queue
, result
);
906 /* "Buffering thread" section */
907 if (!done_buffering
&& need_rebuffer() && disk_is_spinning())
909 m
= find_handle(current_handle
);
910 if ( !m
|| ((m
->filerem
== 0) && (m
->next
== NULL
)))
912 int h
= bufopen(argv
[next_file
], 0);
917 //DEBUGF("new handle %d\n",h);
919 handle_order
[last_handle
] = h
;
920 buffer_handle(m
->id
);
933 current_handle
= m
?m
->id
:-1;
937 DEBUGF("buffering handle %d\n",m
->id
);
938 buffer_handle(m
->id
);
942 if (next_file
== argc
&& m
->filerem
== 0)
943 done_buffering
= true;
951 DEBUGF("buffer usage: %d handles_used: %d\n", BUF_USED,num_handles);
952 #ifndef ROCKBOX_HAS_LOGF
961 int bufopen(char *file
, size_t offset
)
963 static struct bufopen_data data
;
965 data
.offset
= offset
;
967 return rb
->queue_send(&buffering_queue
, Q_BUFOPEN
, &data
);
970 void bufclose(int handle_id
)
972 rb
->queue_post(&buffering_queue
, Q_BUFCLOSE
, handle_id
);
975 void bufseek(int handle_id
, size_t offset
)
977 static struct bufseek_data data
;
978 data
.handle_id
= handle_id
;
979 data
.offset
= offset
;
981 rb
->queue_post(&buffering_queue
, Q_BUFSEEK
, &data
);
984 void bufadvance(int handle_id
, ssize_t offset
)
986 static struct bufadvance_data data
;
987 data
.handle_id
= handle_id
;
988 data
.offset
= offset
;
990 rb
->queue_post(&buffering_queue
, Q_BUFADVANCE
, &data
);
993 int bufread(int handle_id
, size_t size
, char *dest
)
995 static struct bufread_data data
;
996 data
.handle_id
= handle_id
;
1000 return rb
->queue_send(&buffering_queue
, Q_BUFREAD
, &data
);
1003 int bufgetdata(int handle_id
, size_t size
, unsigned char **data
)
1005 static struct bufgetdata_data argdata
;
1006 argdata
.handle_id
= handle_id
;
1007 argdata
.size
= size
;
1008 argdata
.data
= data
;
1010 return rb
->queue_send(&buffering_queue
, Q_BUFGETDATA
, &argdata
);