Imported Upstream version 1.1.11
[platform/upstream/cdrkit.git] / wodim / fifo.c
1 /*
2  * This file has been modified for the cdrkit suite.
3  *
4  * The behaviour and appearence of the program code below can differ to a major
5  * extent from the version distributed by the original author(s).
6  *
7  * For details, see Changelog file distributed with the cdrkit package. If you
8  * received this file from another source then ask the distributing person for
9  * a log of modifications.
10  *
11  */
12
13 /* @(#)fifo.c   1.49 06/02/08 Copyright 1989,1997-2006 J. Schilling */
14 /*
15  *      A "fifo" that uses shared memory between two processes
16  *
17  *      The actual code is a mixture of borrowed code from star's fifo.c
18  *      and a proposal from Finn Arne Gangstad <finnag@guardian.no>
19  *      who had the idea to use a ring buffer to handle average size chunks.
20  *
21  *      Copyright (c) 1989,1997-2006 J. Schilling
22  */
23 /*
24  * This program is free software; you can redistribute it and/or modify
25  * it under the terms of the GNU General Public License version 2
26  * as published by the Free Software Foundation.
27  *
28  * This program is distributed in the hope that it will be useful,
29  * but WITHOUT ANY WARRANTY; without even the implied warranty of
30  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
31  * GNU General Public License for more details.
32  *
33  * You should have received a copy of the GNU General Public License along with
34  * this program; see the file COPYING.  If not, write to the Free Software
35  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36  */
37
38 #ifndef DEBUG
39 #define DEBUG
40 #endif
41 /*#define       XDEBUG*/
42 #include <mconfig.h>
43
44   /* We always wish FIFO unless it is disabled below */
45 #ifndef FIFO
46 #define FIFO
47 #endif
48
49 #if     defined(HAVE_OS_H) && \
50         defined(HAVE_CLONE_AREA) && defined(HAVE_CREATE_AREA) && \
51         defined(HAVE_DELETE_AREA)
52 #include <OS.h>
53 #       define  HAVE_BEOS_AREAS /* BeOS/Zeta */
54 #endif
55 #if     !defined(HAVE_SMMAP) && !defined(HAVE_USGSHM) && \
56         !defined(HAVE_DOSALLOCSHAREDMEM) && !defined(HAVE_BEOS_AREAS)
57 #undef  FIFO                    /* We cannot have a FIFO on this platform */
58 #endif
59 #if     !defined(HAVE_FORK)
60 #undef  FIFO                    /* We cannot have a FIFO on this platform */
61 #endif
62 #ifdef  FIFO
63 #if !defined(USE_MMAP) && !defined(USE_USGSHM)
64 #define USE_MMAP
65 #endif
66 #ifndef HAVE_SMMAP
67 #       undef   USE_MMAP
68 #       define  USE_USGSHM      /* now SYSV shared memory is the default*/
69 #endif
70 #ifdef  USE_MMAP                /* Only want to have one implementation */
71 #       undef   USE_USGSHM      /* mmap() is preferred                  */
72 #endif
73
74 #ifdef  HAVE_DOSALLOCSHAREDMEM  /* This is for OS/2 */
75 #       undef   USE_MMAP
76 #       undef   USE_USGSHM
77 #       define  USE_OS2SHM
78 #endif
79
80 #ifdef  HAVE_BEOS_AREAS         /* This is for BeOS/Zeta */
81 #       undef   USE_MMAP
82 #       undef   USE_USGSHM
83 #       undef   USE_OS2SHM
84 #       define  USE_BEOS_AREAS
85 #endif
86
87 #include <stdio.h>
88 #include <stdxlib.h>
89 #include <unixstd.h>    /* includes <sys/types.h> */
90 #include <utypes.h>
91 #include <fctldefs.h>
92 #if defined(HAVE_SMMAP) && defined(USE_MMAP)
93 #include <mmapdefs.h>
94 #endif
95 #include <waitdefs.h>
96 #include <standard.h>
97 #include <errno.h>
98 #include <signal.h>
99 #include <libport.h>
100 #include <schily.h>
101
102 #include "wodim.h"
103 #include "xio.h"
104
105 #ifdef DEBUG
106 #ifdef XDEBUG
107 FILE    *ef;
108 #define USDEBUG1        if (debug) {if (s == owner_reader) fprintf(ef, "r"); else fprintf(ef, "w"); fflush(ef); }
109 #define USDEBUG2        if (debug) {if (s == owner_reader) fprintf(ef, "R"); else fprintf(ef, "W"); fflush(ef); }
110 #else
111 #define USDEBUG1
112 #define USDEBUG2
113 #endif
114 #define EDEBUG(a)   if (debug) schily_error a
115 #else
116 #define EDEBUG(a)
117 #define USDEBUG1
118 #define USDEBUG2
119 #endif
120
121 #define palign(x, a)    (((char *)(x)) + ((a) - 1 - (((UIntptr_t)((x)-1))%(a))))
122
123 typedef enum faio_owner {
124         owner_none,             /* Unused in real life                      */
125         owner_writer,           /* owned by process that writes into FIFO   */
126         owner_faio,             /* Intermediate state when buf still in use */
127         owner_reader            /* owned by process that reads from FIFO    */
128 } fowner_t;
129
130 char    *onames[] = {
131         "none",
132         "writer",
133         "faio",
134         "reader",
135 };
136
137 typedef struct faio {
138         int     len;
139         volatile fowner_t owner;
140         volatile int users;
141         short   fd;
142         short   saved_errno;
143         char    *bufp;
144 } faio_t;
145
146 struct faio_stats {
147         long    puts;
148         long    gets;
149         long    empty;
150         long    full;
151         long    done;
152         long    cont_low;
153         int     users;
154 } *sp;
155
156 #define MIN_BUFFERS     3
157
158 #define MSECS   1000
159 #define SECS    (1000*MSECS)
160
161 /*
162  * Note: WRITER_MAXWAIT & READER_MAXWAIT need to be greater than the SCSI
163  * timeout for commands that write to the media. This is currently 200s
164  * if we are in SAO mode.
165  */
166 /* microsecond delay between each buffer-ready probe by writing process */
167 #define WRITER_DELAY    (20*MSECS)
168 #define WRITER_MAXWAIT  (240*SECS)      /* 240 seconds max wait for data */
169
170 /* microsecond delay between each buffer-ready probe by reading process */
171 #define READER_DELAY    (80*MSECS)
172 #define READER_MAXWAIT  (240*SECS)      /* 240 seconds max wait for reader */
173
174 static  char    *buf;
175 static  char    *bufbase;
176 static  char    *bufend;
177 static  long    buflen;                 /* The size of the FIFO buffer */
178
179 extern  int     debug;
180 extern  int     lverbose;
181
182 void    init_fifo(long);
183 #ifdef  USE_MMAP
184 static  char    *mkshare(int size);
185 #endif
186 #ifdef  USE_USGSHM
187 static  char    *mkshm(int size);
188 #endif
189 #ifdef  USE_OS2SHM
190 static  char    *mkos2shm(int size);
191 #endif
192 #ifdef  USE_BEOS_AREAS
193 static  char    *mkbeosshm(int size);
194 static  void    beosshm_child(void);
195 #endif
196
197 BOOL    init_faio(track_t *trackp, int);
198 BOOL    await_faio(void);
199 void    kill_faio(void);
200 int     wait_faio(void);
201 static  void    faio_reader(track_t *trackp);
202 static  void    faio_read_track(track_t *trackp);
203 static  void    faio_wait_on_buffer(faio_t *f, fowner_t s, unsigned long delay,
204                                                                                           unsigned long max_wait);
205 static  int     faio_read_segment(int fd, faio_t *f, track_t *track, long secno, 
206                                                                                         int len);
207 static  faio_t  *faio_ref(int n);
208 int     faio_read_buf(int f, char *bp, int size);
209 int     faio_get_buf(int f, char **bpp, int size);
210 void    fifo_stats(void);
211 int     fifo_percent(BOOL addone);
212
213
214 void
215 init_fifo(long fs)
216 {
217         int     pagesize;
218
219         if (fs == 0L)
220                 return;
221
222         pagesize = getpagesize();
223         buflen = roundup(fs, pagesize) + pagesize;
224         EDEBUG(("fs: %ld buflen: %ld\n", fs, buflen));
225
226 #if     defined(USE_MMAP)
227         buf = mkshare(buflen);
228 #endif
229 #if     defined(USE_USGSHM)
230         buf = mkshm(buflen);
231 #endif
232 #if     defined(USE_OS2SHM)
233         buf = mkos2shm(buflen);
234 #endif
235 #if     defined(USE_BEOS_AREAS)
236         buf = mkbeosshm(buflen);
237 #endif
238
239         bufbase = buf;
240         bufend = buf + buflen;
241         EDEBUG(("buf: %p bufend: %p, buflen: %ld\n", buf, bufend, buflen));
242         buf = palign(buf, pagesize);
243         buflen -= buf - bufbase;
244         EDEBUG(("buf: %p bufend: %p, buflen: %ld (align %ld)\n", buf, bufend, buflen, (long)(buf - bufbase)));
245
246         /*
247          * Dirty the whole buffer. This can die with various signals if
248          * we're trying to lock too much memory
249          */
250         fillbytes(buf, buflen, '\0');
251
252 #ifdef  XDEBUG
253         if (debug)
254                 ef = fopen("/tmp/ef", "w");
255 #endif
256 }
257
258 #ifdef  USE_MMAP
259 static char *
260 mkshare(int size)
261 {
262         int     f;
263         char    *addr;
264
265 #ifdef  MAP_ANONYMOUS   /* HP/UX */
266         f = -1;
267         addr = mmap(0, mmap_sizeparm(size),
268                         PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, f, 0);
269 #else
270         if ((f = open("/dev/zero", O_RDWR)) < 0)
271                 comerr("Cannot open '/dev/zero'.\n");
272         addr = mmap(0, mmap_sizeparm(size),
273                         PROT_READ|PROT_WRITE, MAP_SHARED, f, 0);
274 #endif
275         if (addr == (char *)-1)
276                 comerr("Cannot get mmap for %d Bytes on /dev/zero.\n", size);
277         if (f >= 0)
278                 close(f);
279
280         if (debug) errmsgno(EX_BAD, "shared memory segment attached at: %p size %d\n",
281                                 (void *)addr, size);
282
283         return (addr);
284 }
285 #endif
286
287 #ifdef  USE_USGSHM
288 #include <sys/ipc.h>
289 #include <sys/shm.h>
290 static char *
291 mkshm(int size)
292 {
293         int     id;
294         char    *addr;
295         /*
296          * Unfortunately, a declaration of shmat() is missing in old
297          * implementations such as AT&T SVr0 and SunOS.
298          * We cannot add this definition here because the return-type
299          * changed on newer systems.
300          *
301          * We will get a warning like this:
302          *
303          * warning: assignment of pointer from integer lacks a cast
304          * or
305          * warning: illegal combination of pointer and integer, op =
306          */
307 /*      extern  char *shmat();*/
308
309         if ((id = shmget(IPC_PRIVATE, size, IPC_CREAT|0600)) == -1)
310                 comerr("shmget failed\n");
311
312         if (debug) errmsgno(EX_BAD, "shared memory segment allocated: %d\n", id);
313
314         if ((addr = shmat(id, (char *)0, 0600)) == (char *)-1)
315                 comerr("shmat failed\n");
316
317         if (debug) errmsgno(EX_BAD, "shared memory segment attached at: %p size %d\n",
318                                 (void *)addr, size);
319
320         if (shmctl(id, IPC_RMID, 0) < 0)
321                 comerr("shmctl failed to detach shared memory segment\n");
322
323 #ifdef  SHM_LOCK
324         /*
325          * Although SHM_LOCK is standard, it seems that all versions of AIX
326          * ommit this definition.
327          */
328         if (shmctl(id, SHM_LOCK, 0) < 0)
329                 comerr("shmctl failed to lock shared memory segment\n");
330 #endif
331
332         return (addr);
333 }
334 #endif
335
336 #ifdef  USE_OS2SHM
337 static char *
338 mkos2shm(int size)
339 {
340         char    *addr;
341
342         /*
343          * The OS/2 implementation of shm (using shm.dll) limits the size of one shared
344          * memory segment to 0x3fa000 (aprox. 4MBytes). Using OS/2 native API we have
345          * no such restriction so I decided to use it allowing fifos of arbitrary size.
346          */
347         if (DosAllocSharedMem(&addr, NULL, size, 0X100L | 0x1L | 0x2L | 0x10L))
348                 comerr("DosAllocSharedMem() failed\n");
349
350         if (debug) errmsgno(EX_BAD, "shared memory allocated attached at: %p size %d\n",
351                                 (void *)addr, size);
352
353         return (addr);
354 }
355 #endif
356
357 #ifdef  USE_BEOS_AREAS
358 static  area_id faio_aid;
359 static  void    *faio_addr;
360 static  char    faio_name[32];
361
362 static char *
363 mkbeosshm(int size)
364 {
365         snprintf(faio_name, sizeof (faio_name), "cdrecord FIFO %lld",
366                 (Llong)getpid());
367
368         faio_aid = create_area(faio_name, &faio_addr,
369                         B_ANY_ADDRESS,
370                         size,
371                         B_NO_LOCK, B_READ_AREA|B_WRITE_AREA);
372         if (faio_addr == NULL) {
373                 comerrno(faio_aid,
374                         "Cannot get create_area for %d Bytes FIFO.\n", size);
375         }
376         if (debug) errmsgno(EX_BAD, "shared memory allocated attached at: %p size %d\n",
377                                 (void *)faio_addr, size);
378         return (faio_addr);
379 }
380
381 static void
382 beosshm_child()
383 {
384         /*
385          * Delete the area created by fork that is copy-on-write.
386          */
387         delete_area(area_for(faio_addr));
388         /*
389          * Clone (share) the original one.
390          */
391         faio_aid = clone_area(faio_name, &faio_addr,
392                         B_ANY_ADDRESS, B_READ_AREA|B_WRITE_AREA,
393                         faio_aid);
394         if (bufbase != faio_addr) {
395                 errmsgno(EX_BAD, "Panic FIFO addr.\n");
396                 return (FALSE);
397         }
398 }
399 #endif
400
401 static  int     faio_buffers;
402 static  int     faio_buf_size;
403 static  int     buf_idx = 0;            /* Initialize to fix an Amiga bug   */
404 static  int     buf_idx_reader = 0;     /* Separate var to allow vfork()    */
405                                         /* buf_idx_reader is for the process */
406                                         /* that fills the FIFO              */
407 static  pid_t   faio_pid = -1;
408 static  BOOL    faio_didwait;
409
410 #ifdef AMIGA
411 /*
412  * On Amiga fork will be replaced by the speciall vfork() like call ix_vfork,
413  * which lets the parent asleep. The child process later wakes up the parent
414  * process by calling ix_fork_resume().
415  */
416 #define fork()           ix_vfork()
417 #define __vfork_resume() ix_vfork_resume()
418
419 #else   /* !AMIGA */
420 #define __vfork_resume()
421 #endif
422
423
424 /*#define       faio_ref(n)     (&((faio_t *)buf)[n])*/
425
426
427 BOOL
428 init_faio(track_t *trackp, int bufsize)
429 {
430         int     n;
431         faio_t  *f;
432         int     pagesize;
433         char    *base;
434
435         if (buflen == 0L)
436                 return (FALSE);
437
438         pagesize = getpagesize();
439         faio_buf_size = bufsize;
440         f = (faio_t *)buf;
441
442         /*
443          * Compute space for buffer headers.
444          * Round bufsize up to pagesize to make each FIFO segment
445          * properly page aligned.
446          */
447         bufsize = roundup(bufsize, pagesize);
448         faio_buffers = (buflen - sizeof (*sp)) / bufsize;
449         EDEBUG(("bufsize: %d buffers: %d hdrsize %ld\n", bufsize, faio_buffers, (long)faio_buffers * sizeof (struct faio)));
450
451         /*
452          * Reduce buffer space by header space.
453          */
454         n = sizeof (*sp) + faio_buffers * sizeof (struct faio);
455         n = roundup(n, pagesize);
456         faio_buffers = (buflen-n) / bufsize;
457         EDEBUG(("bufsize: %d buffers: %d hdrsize %ld\n", bufsize, faio_buffers, (long)faio_buffers * sizeof (struct faio)));
458
459         if (faio_buffers < MIN_BUFFERS) {
460                 errmsgno(EX_BAD,
461                         "write-buffer too small, minimum is %dk. Disabling.\n",
462                                                 MIN_BUFFERS*bufsize/1024);
463                 return (FALSE);
464         }
465
466         if (debug)
467                 printf("Using %d buffers of %d bytes.\n", faio_buffers, faio_buf_size);
468
469         f = (faio_t *)buf;
470         base = buf + roundup(sizeof (*sp) + faio_buffers * sizeof (struct faio),
471                                 pagesize);
472
473         for (n = 0; n < faio_buffers; n++, f++, base += bufsize) {
474                 /* Give all the buffers to the file reader process */
475                 f->owner = owner_writer;
476                 f->users = 0;
477                 f->bufp = base;
478                 f->fd = -1;
479         }
480         sp = (struct faio_stats *)f;    /* point past headers */
481         sp->gets = sp->puts = sp->done = 0L;
482         sp->users = 1;
483
484         faio_pid = fork();
485         if (faio_pid < 0)
486                 comerr("fork(2) failed");
487
488         if (faio_pid == 0) {
489                 /*
490                  * child (background) process that fills the FIFO.
491                  */
492                 raisepri(1);            /* almost max priority */
493
494 #ifdef USE_OS2SHM
495                 DosGetSharedMem(buf, 3); /* PAG_READ|PAG_WRITE */
496 #endif
497 #ifdef  USE_BEOS_AREAS
498                 beosshm_child();
499 #endif
500                 /* Ignoring SIGALRM cures the SCO usleep() bug */
501 /*              signal(SIGALRM, SIG_IGN);*/
502                 __vfork_resume();       /* Needed on some platforms */
503                 faio_reader(trackp);
504                 /* NOTREACHED */
505         } else {
506 #ifdef  __needed__
507                 Uint    t;
508 #endif
509
510                 faio_didwait = FALSE;
511
512                 /*
513                  * XXX We used to close all track files in the foreground
514                  * XXX process. This was not correct before we used "xio"
515                  * XXX and with "xio" it will start to fail because we need
516                  * XXX the fd handles for the faio_get_buf() function.
517                  */
518 #ifdef  __needed__
519                 /* close all file-descriptors that only the child will use */
520                 for (t = 1; t <= trackp->tracks; t++) {
521                         if (trackp[t].xfp != NULL)
522                                 xclose(trackp[t].xfp);
523                 }
524 #endif
525         }
526
527         return (TRUE);
528 }
529
530 BOOL
531 await_faio()
532 {
533         int     n;
534         int     lastfd = -1;
535         faio_t  *f;
536
537         /*
538          * Wait until the reader is active and has filled the buffer.
539          */
540         if (lverbose || debug) {
541                 printf("Waiting for reader process to fill input buffer ... ");
542                 flush();
543         }
544
545         faio_wait_on_buffer(faio_ref(faio_buffers - 1), owner_reader,
546                             500*MSECS, 0);
547
548         if (lverbose || debug)
549                 printf("input buffer ready.\n");
550
551         sp->empty = sp->full = 0L;      /* set correct stat state */
552         sp->cont_low = faio_buffers;    /* set cont to max value  */
553
554         f = faio_ref(0);
555         for (n = 0; n < faio_buffers; n++, f++) {
556                 if (f->fd != lastfd &&
557                         f->fd == STDIN_FILENO && f->len == 0) {
558                         errmsgno(EX_BAD, "Premature EOF on stdin.\n");
559                         kill(faio_pid, SIGKILL);
560                         return (FALSE);
561                 }
562                 lastfd = f->fd;
563         }
564         return (TRUE);
565 }
566
567 void
568 kill_faio()
569 {
570         if (faio_pid > 0)
571                 kill(faio_pid, SIGKILL);
572   faio_pid=-1;
573 }
574
575 int
576 wait_faio()
577 {
578         if (faio_pid > 0 && !faio_didwait)
579                 return (wait(0));
580         faio_didwait = TRUE;
581         return (0);
582 }
583
584 static void
585 faio_reader(track_t *trackp)
586 {
587         /* This function should not return, but _exit. */
588         Uint    trackno;
589
590         if (debug)
591                 printf("\nfaio_reader starting\n");
592
593         for (trackno = 1; trackno <= trackp->tracks; trackno++) {
594                 if (debug)
595                         printf("\nfaio_reader reading track %u\n", trackno);
596                 faio_read_track(&trackp[trackno]);
597         }
598         sp->done++;
599         if (debug)
600                 printf("\nfaio_reader all tracks read, exiting\n");
601
602         /* Prevent hang if buffer is larger than all the tracks combined */
603         if (sp->gets == 0)
604                 faio_ref(faio_buffers - 1)->owner = owner_reader;
605
606 #ifdef  USE_OS2SHM
607         DosFreeMem(buf);
608         sleep(30000);   /* XXX If calling _exit() here the parent process seems to be blocked */
609                         /* XXX This should be fixed soon */
610 #endif
611         if (debug)
612                 fprintf(stderr, "\nfaio_reader _exit(0)\n");
613         _exit(0);
614 }
615
616 #ifndef faio_ref
617 static faio_t *
618 faio_ref(int n)
619 {
620         return (&((faio_t *)buf)[n]);
621 }
622 #endif
623
624
625 static void
626 faio_read_track(track_t *trackp)
627 {
628         int     fd = -1;
629         int     bytespt = trackp->secsize * trackp->secspt;
630         int     secspt = trackp->secspt;
631         int     l;
632         long    secno = trackp->trackstart;
633         tsize_t tracksize = trackp->tracksize;
634         tsize_t bytes_read = (tsize_t)0;
635         long    bytes_to_read;
636
637         if (trackp->xfp != NULL)
638                 fd = xfileno(trackp->xfp);
639
640         if (bytespt > faio_buf_size) {
641                 comerrno(EX_BAD,
642                 "faio_read_track fatal: secsize %d secspt %d, bytespt(%d) > %d !!\n",
643                         trackp->secsize, trackp->secspt, bytespt,
644                         faio_buf_size);
645         }
646
647         do {
648                 bytes_to_read = bytespt;
649                 if (tracksize > 0) {
650                         if ((tracksize - bytes_read) > bytespt) {
651                                 bytes_to_read = bytespt;
652                         } else {
653                                 bytes_to_read = tracksize - bytes_read;
654                         }
655                 }
656                 l = faio_read_segment(fd, faio_ref(buf_idx_reader), trackp, secno, bytes_to_read);
657                 if (++buf_idx_reader >= faio_buffers)
658                         buf_idx_reader = 0;
659                 if (l <= 0)
660                         break;
661                 bytes_read += l;
662                 secno += secspt;
663         } while (tracksize < 0 || bytes_read < tracksize);
664
665         xclose(trackp->xfp);    /* Don't keep files open longer than neccesary */
666 }
667
668 static void
669 #ifdef  PROTOTYPES
670 faio_wait_on_buffer(faio_t *f, fowner_t s,
671                         unsigned long delay,
672                         unsigned long max_wait)
673 #else
674 faio_wait_on_buffer(faio_t *f, fowner_t *s, unsigned long delay, unsigned long max_wait)
675 #endif
676 {
677         unsigned long max_loops;
678
679         if (f->owner == s)
680                 return;         /* return immediately if the buffer is ours */
681
682         if (s == owner_reader)
683                 sp->empty++;
684         else
685                 sp->full++;
686
687         max_loops = max_wait / delay + 1;
688
689         while (max_wait == 0 || max_loops--) {
690                 USDEBUG1;
691                 usleep(delay);
692                 USDEBUG2;
693
694                 if (f->owner == s)
695                         return;
696         }
697         if (debug) {
698                 errmsgno(EX_BAD,
699                 "%lu microseconds passed waiting for %d current: %d idx: %ld\n",
700                 max_wait, s, f->owner, (long)(f - faio_ref(0))/sizeof (*f));
701         }
702         comerrno(EX_BAD, "faio_wait_on_buffer for %s timed out.\n",
703         (s > owner_reader || s < owner_none) ? "bad_owner" : onames[s-owner_none]);
704 }
705
706 static int
707 faio_read_segment(int fd, faio_t *f, track_t *trackp, long secno, int len)
708 {
709         int l;
710
711         faio_wait_on_buffer(f, owner_writer, WRITER_DELAY, WRITER_MAXWAIT);
712
713         f->fd = fd;
714         l = fill_buf(fd, trackp, secno, f->bufp, len);
715         f->len = l;
716         f->saved_errno = geterrno();
717         f->owner = owner_reader;
718         f->users = sp->users;
719
720         sp->puts++;
721
722         return (l);
723 }
724
725 int
726 faio_read_buf(int fd, char *bp, int size)
727 {
728         char *bufp;
729
730         int len = faio_get_buf(fd, &bufp, size);
731         if (len > 0) {
732                 movebytes(bufp, bp, len);
733         }
734         return (len);
735 }
736
737 int
738 faio_get_buf(int fd, char **bpp, int size)
739 {
740         faio_t  *f;
741         int     len;
742
743 again:
744         f = faio_ref(buf_idx);
745         if (f->owner == owner_faio) {
746                 f->owner = owner_writer;
747                 if (++buf_idx >= faio_buffers)
748                         buf_idx = 0;
749                 f = faio_ref(buf_idx);
750         }
751
752         if ((sp->puts - sp->gets) < sp->cont_low && sp->done == 0) {
753                 EDEBUG(("gets: %ld puts: %ld cont: %ld low: %ld\n", sp->gets, sp->puts, sp->puts - sp->gets, sp->cont_low));
754                 sp->cont_low = sp->puts - sp->gets;
755         }
756         faio_wait_on_buffer(f, owner_reader, READER_DELAY, READER_MAXWAIT);
757         len = f->len;
758
759         if (f->fd != fd) {
760                 if (f->len == 0) {
761                         /*
762                          * If the tracksize for this track was known, and
763                          * the tracksize is 0 mod bytespt, this happens.
764                          */
765                         goto again;
766                 }
767                 comerrno(EX_BAD,
768                 "faio_get_buf fatal: fd=%d, f->fd=%d, f->len=%d f->errno=%d\n",
769                 fd, f->fd, f->len, f->saved_errno);
770         }
771         if (size < len) {
772                 comerrno(EX_BAD,
773                 "unexpected short read-attempt in faio_get_buf. size = %d, len = %d\n",
774                 size, len);
775         }
776
777         if (len < 0)
778                 seterrno(f->saved_errno);
779
780         sp->gets++;
781
782         *bpp = f->bufp;
783         if (--f->users <= 0)
784                 f->owner = owner_faio;
785         return (len);
786 }
787
788 void
789 fifo_stats()
790 {
791         if (sp == NULL) /* We might not use a FIFO */
792                 return;
793
794         errmsgno(EX_BAD, "fifo had %ld puts and %ld gets.\n",
795                 sp->puts, sp->gets);
796         errmsgno(EX_BAD, "fifo was %ld times empty and %ld times full, min fill was %ld%%.\n",
797                 sp->empty, sp->full, (100L*sp->cont_low)/faio_buffers);
798 }
799
800 int
801 fifo_percent(BOOL addone)
802 {
803         int     percent;
804
805         if (sp == NULL) /* We might not use a FIFO */
806                 return (-1);
807
808         if (sp->done)
809                 return (100);
810         percent = (100*(sp->puts + 1 - sp->gets)/faio_buffers);
811         if (percent > 100)
812                 return (100);
813         return (percent);
814 }
815 #else   /* FIFO */
816
817 #include <standard.h>
818 #include <utypes.h>     /* includes sys/types.h */
819 #include <schily.h>
820
821 #include "wodim.h"
822
823 void    init_fifo(long);
824 BOOL    init_faio(track_t *track, int);
825 BOOL    await_faio(void);
826 void    kill_faio(void);
827 int     wait_faio(void);
828 int     faio_read_buf(int f, char *bp, int size);
829 int     faio_get_buf(int f, char **bpp, int size);
830 void    fifo_stats(void);
831 int     fifo_percent(BOOL addone);
832
833
834 void init_fifo(long fs)
835 {
836         errmsgno(EX_BAD, "Fifo not supported.\n");
837 }
838
839 BOOL init_faio(track_t *track, 
840                int bufsize /* The size of a single transfer buffer */)
841 {
842         return (FALSE);
843 }
844
845 BOOL await_faio()
846 {
847         return (TRUE);
848 }
849
850 void kill_faio()
851 {
852 }
853
854 int wait_faio()
855 {
856         return (0);
857 }
858
859 int faio_read_buf(int fd, char *bp, int size)
860 {
861         return (0);
862 }
863
864 int faio_get_buf(int fd, char **bpp, int size)
865 {
866         return (0);
867 }
868
869 void fifo_stats()
870 {
871 }
872
873 int fifo_percent(BOOL addone)
874 {
875         return (-1);
876 }
877
878 #endif  /* FIFO */