wmmoonclock: Bump to version 1.29.
[dockapps.git] / ascd / libworkman / plat_linux.c
blobe61e898dbe5de83dbb258212585c86d31dd2176c
1 /*
2 * $Id: plat_linux.c,v 1.8 1999/06/17 06:48:03 dirk Exp $
4 * This file is part of WorkMan, the civilized CD player library
5 * (c) 1991-1997 by Steven Grimm (original author)
6 * (c) by Dirk Försterling (current 'author' = maintainer)
7 * The maintainer can be contacted by his e-mail address:
8 * milliByte@DeathsDoor.com
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the Free
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * Linux-specific drive control routines. Very similar to the Sun module.
28 #ifdef linux
29 /* Id for ident command */
30 static char plat_linux_id[] = "$Id: plat_linux.c,v 1.8 1999/06/17 06:48:03 dirk Exp $";
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <malloc.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/socket.h>
42 #include <sys/stat.h>
43 #include <sys/wait.h>
45 #include "include/wm_config.h"
47 #if defined(BSD_MOUNTTEST)
48 #include <mntent.h>
49 #else
51 * this is for glibc 2.x which defines ust structure in
52 * ustat.h not stat.h
54 #ifdef __GLIBC__
55 #include <sys/ustat.h>
56 #endif
57 #endif
59 #include <sys/time.h>
60 #include <sys/ioctl.h>
61 #include <linux/cdrom.h>
63 #include "include/wm_cdda.h"
64 #include "include/wm_struct.h"
65 #include "include/wm_platform.h"
66 #include "include/wm_cdrom.h"
67 #include "include/wm_scsi.h"
68 #include "include/wm_helpers.h"
70 #ifdef OSS_SUPPORT
71 #include <linux/soundcard.h>
72 #define CD_CHANNEL SOUND_MIXER_CD
73 #endif
75 #define max(a,b) ((a) > (b) ? (a) : (b))
77 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
79 #ifdef LINUX_SCSI_PASSTHROUGH
80 /* this is from <scsi/scsi_ioctl.h> */
81 # define SCSI_IOCTL_SEND_COMMAND 1
82 #endif
84 int min_volume = 0;
85 int max_volume = 255;
87 #ifdef OSS_SUPPORT
88 int mixer;
89 char mixer_dev_name[20] = "/dev/mixer";
90 #endif
91 extern char *cd_device, *cddaslave_path;
93 int cdda_slave = -1;
96 * Wait for an acknowledgement from the CDDA slave.
98 static int
99 get_ack(int fd)
101 #if defined(BUILD_CDDA) && defined(WMCDDA_DONE) /* { */
102 struct cdda_block blk;
105 if (read(fd, &blk, sizeof(blk)) <= 0)
106 return (0);
107 while (blk.status != WMCDDA_ACK);
108 #endif /* } */
110 return (1);
111 } /* get_ack() */
113 /*--------------------------------------------------------*
114 * Initialize the drive. A no-op for the generic driver.
115 *--------------------------------------------------------*/
117 gen_init( struct wm_drive *d )
119 #ifdef OSS_SUPPORT
121 * Open and use the mixer device to set volume
123 if( ( mixer = open( mixer_dev_name, O_RDWR, 0 ) ) == -1 )
125 perror( mixer_dev_name );
126 exit( -1 );
128 #endif
129 return (0);
130 } /* gen_init() */
133 * Try to initialize the CDDA slave. Returns 0 on error.
136 cdda_init( struct wm_drive *d )
138 #if defined(BUILD_CDDA) && defined(WMCDDA_DONE) /* { */
139 int slavefds[2];
141 if (cdda_slave > -1)
142 return (1);
144 fprintf( stderr, "slave okay\n" );
146 if (socketpair(AF_UNIX, SOCK_STREAM, 0, slavefds))
148 perror("socketpair");
149 return (0);
152 fprintf( stderr, "going to fork\n" );
153 switch (fork()) {
154 case 0:
155 close(slavefds[0]);
156 dup2(slavefds[1], 1);
157 dup2(slavefds[1], 0);
158 close(slavefds[1]);
159 close(d->fd);
160 /* Try the default path first. */
161 execl(cddaslave_path, cddaslave_path, cd_device, NULL);
162 /* Search $PATH if that didn't work. */
163 execlp("cddaslave", "cddaslave", cd_device, NULL);
164 perror(cddaslave_path);
165 exit(1);
167 case -1:
168 close(slavefds[0]);
169 close(slavefds[1]);
170 perror("fork");
171 return (0);
174 close(slavefds[1]);
175 cdda_slave = slavefds[0];
177 if (!get_ack(cdda_slave))
179 fprintf( stderr, "get_ack failed\n" );
180 cdda_slave = -1;
181 /* codec_start(); */
182 return (0);
185 return (1);
187 #else /* BUILD_CDDA } { */
189 * If we're not building CDDA support, don't even bother trying.
191 return (0);
192 #endif
193 } /* cdda_init() */
196 * Turn off the CDDA slave.
198 void
199 cdda_kill( struct wm_drive *d )
201 if (cdda_slave > -1)
203 write(cdda_slave, "Q", 1);
204 get_ack(cdda_slave);
205 wait(NULL);
206 cdda_slave = -1;
207 /* codec_start(); */
209 } /* cdda_kill() */
211 /*-------------------------------------*
212 * Get the number of tracks on the CD.
213 *-------------------------------------*/
215 gen_get_trackcount(struct wm_drive *d, int *tracks)
217 struct cdrom_tochdr hdr;
219 if (ioctl(d->fd, CDROMREADTOCHDR, &hdr))
220 return (-1);
222 *tracks = hdr.cdth_trk1;
223 return (0);
226 /*---------------------------------------------------------*
227 * Get the start time and mode (data or audio) of a track.
228 *---------------------------------------------------------*/
230 gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
232 struct cdrom_tocentry entry;
234 entry.cdte_track = track;
235 entry.cdte_format = CDROM_MSF;
237 if (ioctl(d->fd, CDROMREADTOCENTRY, &entry))
238 return (-1);
240 *startframe = entry.cdte_addr.msf.minute * 60 * 75 +
241 entry.cdte_addr.msf.second * 75 +
242 entry.cdte_addr.msf.frame;
243 *data = entry.cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0;
245 return (0);
248 /*-------------------------------------*
249 * Get the number of frames on the CD.
250 *-------------------------------------*/
252 gen_get_cdlen(struct wm_drive *d, int *frames)
254 int tmp;
256 return (gen_get_trackinfo(d, CDROM_LEADOUT, &tmp, frames));
260 /* Alarm signal handler.
261 static void do_nothing(int x) { x++; }
264 /*--------------------------------------------------------------------------*
265 * Get the current status of the drive: the current play mode, the absolute
266 * position from start of disc (in frames), and the current track and index
267 * numbers if the CD is playing or paused.
268 *--------------------------------------------------------------------------*/
270 gen_get_drive_status( struct wm_drive *d, enum wm_cd_modes oldmode,
271 enum wm_cd_modes *mode, int *pos, int *track, int *index )
273 struct cdrom_subchnl sc;
275 #ifdef SBPCD_HACK
276 static int prevpos = 0;
277 #endif
279 /* If we can't get status, the CD is ejected, so default to that. */
280 *mode = WM_CDM_EJECTED;
282 /* Is the device open? */
283 if (d->fd < 0)
285 switch (wmcd_open(d))
288 case -1: /* error */
289 return (-1);
291 case 1: /* retry */
292 return (0);
296 sc.cdsc_format = CDROM_MSF;
298 if (ioctl(d->fd, CDROMSUBCHNL, &sc))
299 return (0);
301 switch (sc.cdsc_audiostatus) {
302 case CDROM_AUDIO_PLAY:
303 *mode = WM_CDM_PLAYING;
304 *track = sc.cdsc_trk;
305 *index = sc.cdsc_ind;
306 *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
307 sc.cdsc_absaddr.msf.second * 75 +
308 sc.cdsc_absaddr.msf.frame;
309 #ifdef SBPCD_HACK
310 if( *pos < prevpos )
312 if( (prevpos - *pos) < 75 )
314 *mode = WM_CDM_TRACK_DONE;
318 prevpos = *pos;
319 #endif
320 break;
322 case CDROM_AUDIO_PAUSED:
323 case CDROM_AUDIO_NO_STATUS:
324 case CDROM_AUDIO_INVALID: /**/
325 if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
327 *mode = WM_CDM_PAUSED;
328 *track = sc.cdsc_trk;
329 *index = sc.cdsc_ind;
330 *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
331 sc.cdsc_absaddr.msf.second * 75 +
332 sc.cdsc_absaddr.msf.frame;
334 else
335 *mode = WM_CDM_STOPPED;
336 break;
338 case CDROM_AUDIO_COMPLETED:
339 *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
340 break;
342 default:
343 *mode = WM_CDM_UNKNOWN;
344 break;
347 return (0);
350 /*------------------------------------------------------------------------*
351 * scale_volume(vol, max)
353 * Return a volume value suitable for passing to the CD-ROM drive. "vol"
354 * is a volume slider setting; "max" is the slider's maximum value.
355 * This is not used if sound card support is enabled.
357 *------------------------------------------------------------------------*/
358 #ifndef OSS_SUPPORT
359 static int
360 scale_volume( int vol, int max )
362 #ifdef CURVED_VOLUME
363 return ((max * max - (max - vol) * (max - vol)) *
364 (max_volume - min_volume) / (max * max) + min_volume);
365 #else
366 return ((vol * (max_volume - min_volume)) / max + min_volume);
367 #endif
368 } /* scale_volume() */
369 #endif
371 /*---------------------------------------------------------------------*
372 * Set the volume level for the left and right channels. Their values
373 * range from 0 to 100.
374 *---------------------------------------------------------------------*/
376 gen_set_volume( struct wm_drive *d, int left, int right )
378 #ifndef OSS_SUPPORT
379 struct cdrom_volctrl v;
380 #endif
381 int vol;
383 #ifdef OSS_SUPPORT
385 left = left < 0 ? 0 : left > 100 ? 100 : left;
386 right = right < 0 ? : right > 100 ? 100 : right;
388 vol = ( 0x007f & left ) | ( 0x7f00 & ( right << 8 ) );
389 if( ioctl( mixer, MIXER_WRITE( CD_CHANNEL ), &vol ) == -1 )
391 perror( "MIXER_WRITE" );
392 return ( -1 );
394 return ( 0 );
395 #else
397 /* Adjust the volume to make up for the CD-ROM drive's weirdness. */
398 left = scale_volume(left, 100);
399 right = scale_volume(right, 100);
401 v.channel0 = v.channel2 = left < 0 ? 0 : left > 255 ? 255 : left;
402 v.channel1 = v.channel3 = right < 0 ? 0 : right > 255 ? 255 : right;
404 return (ioctl(d->fd, CDROMVOLCTRL, &v));
405 # endif
408 /*---------------*
409 * Pause the CD.
410 *---------------*/
412 gen_pause(d)
413 struct wm_drive *d;
415 return (ioctl(d->fd, CDROMPAUSE));
418 /*-------------------------------------------------*
419 * Resume playing the CD (assuming it was paused.)
420 *-------------------------------------------------*/
422 gen_resume(struct wm_drive *d)
424 return (ioctl(d->fd, CDROMRESUME));
427 /*--------------*
428 * Stop the CD.
429 *--------------*/
431 gen_stop(struct wm_drive *d)
433 return (ioctl(d->fd, CDROMSTOP));
436 /*------------------------------------------------------------*
437 * Play the CD from one position to another (both in frames.)
438 *------------------------------------------------------------*/
440 gen_play(struct wm_drive *d, int start, int end)
442 struct cdrom_msf msf;
444 msf.cdmsf_min0 = start / (60*75);
445 msf.cdmsf_sec0 = (start % (60*75)) / 75;
446 msf.cdmsf_frame0 = start % 75;
447 msf.cdmsf_min1 = end / (60*75);
448 msf.cdmsf_sec1 = (end % (60*75)) / 75;
449 msf.cdmsf_frame1 = end % 75;
451 #ifndef FAST_IDE
452 if (ioctl(d->fd, CDROMSTART))
453 return (-1);
454 #endif
455 if (ioctl(d->fd, CDROMPLAYMSF, &msf))
456 return (-2);
458 return (0);
461 /*----------------------------------------*
462 * Eject the current CD, if there is one.
463 *----------------------------------------*/
465 gen_eject(struct wm_drive *d)
467 struct stat stbuf;
468 #if !defined(BSD_MOUNTTEST)
469 struct ustat ust;
470 #else
471 struct mntent *mnt;
472 FILE *fp;
473 #endif
475 if (fstat(d->fd, &stbuf) != 0)
476 return (-2);
478 /* Is this a mounted filesystem? */
479 #if !defined(BSD_MOUNTTEST)
480 if (ustat(stbuf.st_rdev, &ust) == 0)
481 return (-3);
482 #else
484 * This is the same test as in the WorkBone interface.
485 * I should eliminate it there, because there is no need
486 * for it in the UI
488 /* check if drive is mounted (from Mark Buckaway's cdplayer code) */
489 /* Changed it again (look at XPLAYCD from ???? */
490 /* It's better to check the device name rather than one device is */
491 /* mounted as iso9660. That prevents "no playing" if you have more*/
492 /* than one CD-ROM, and one of them is mounted, but it's not the */
493 /* audio CD -dirk */
494 if ((fp = setmntent (MOUNTED, "r")) == NULL)
496 fprintf (stderr, "Could not open %s: %s\n", MOUNTED, strerror (errno));
497 return(-3);
499 while ((mnt = getmntent (fp)) != NULL)
501 if (strcmp (mnt->mnt_fsname, cd_device) == 0)
503 fputs ("CDROM already mounted (according to mtab). Operation aborted.\n", stderr);
504 endmntent (fp);
505 return(-3);
508 endmntent (fp);
509 #endif /* BSD_MOUNTTEST */
511 if (ioctl(d->fd, CDROMEJECT))
512 return (-1);
513 /*------------------
514 * Things in "foobar_one" are left over from 1.4b3
515 * I put them here for further observation. In 1.4b3, however,
516 * that workaround didn't help at least for /dev/sbpcd
517 * (The tray closed just after ejecting because re-opening the
518 * device causes the tray to close)
519 *------------------*/
520 #ifdef foobar_one
521 extern int intermittent_dev
523 * Some drives (drivers?) won't recognize a new CD if we leave the
524 * device open.
526 if (intermittent_dev)
528 close(d->fd);
529 d->fd = -1;
531 #endif
533 return (0);
534 } /* gen_eject() */
536 /*----------------------------------------*
537 * Close the CD tray
538 *----------------------------------------*/
540 int gen_closetray(struct wm_drive *d)
542 #ifdef CAN_CLOSE
543 #ifdef CDROMCLOSETRAY
544 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "CDROMCLOSETRAY closing tray...\n");
545 if (ioctl(d->fd, CDROMCLOSETRAY))
546 return (-1);
547 #else
548 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen() closing tray...\n");
549 if(!close(d->fd))
551 d->fd=-1;
552 return(wmcd_reopen(d));
553 } else {
554 return(-1);
556 #endif /* CDROMCLOSETRAY */
557 #else /* CAN_CLOSE */
558 /* Always succeed if the drive can't close. */
559 return(0);
560 #endif /* CAN_CLOSE */
561 } /* gen_closetray() */
563 /*--------------------------------*
564 * Keep the CD open all the time.
565 * disabled, analogous to 1.4b3
566 *--------------------------------*
567 void
568 keep_cd_open( void )
570 int fd;
571 struct flock fl;
572 extern end;
575 for (fd = 0; fd < 256; fd++)
576 close(fd);
578 if (fork())
579 exit(0);
581 if ((fd = open("/tmp/cd.lock", O_RDWR | O_CREAT, 0666)) < 0)
582 exit(0);
583 fl.l_type = F_WRLCK;
584 fl.l_whence = 0;
585 fl.l_start = 0;
586 fl.l_len = 0;
587 if (fcntl(fd, F_SETLK, &fl) < 0)
588 exit(0);
590 if (open(cd_device, 0) >= 0)
592 brk(&end);
593 pause();
596 exit(0);
600 /*---------------------------------------------------------------------*
601 * Read the initial volume from the drive, if available. Each channel
602 * ranges from 0 to 100, with -1 indicating data not available.
603 *---------------------------------------------------------------------*/
605 gen_get_volume( struct wm_drive *d, int *left, int *right )
607 #if defined(BUILD_CDDA) && defined(WMCDDA_DONE) /* { */
608 struct cdda_block blk;
610 if (cdda_slave > -1)
612 write(cdda_slave, "G", 1);
613 get_ack(cdda_slave);
614 read(cdda_slave, &blk, sizeof(blk));
616 *left = *right = (blk.volume * 100 + 254) / 255;
618 if (blk.balance < 110)
619 *right = (((blk.volume * blk.balance + 127) / 128) *
620 100 + 254) / 255;
621 else if (blk.balance > 146)
622 *left = (((blk.volume * (255 - blk.balance) +
623 127) / 128) * 100 + 254) / 255;
625 return (0);
627 #else /* } */
628 #ifdef OSS_SUPPORT
629 int vol;
631 if( ioctl( mixer, MIXER_READ( CD_CHANNEL ), &vol ) == -1 )
633 perror( "MIXER_READ" );
634 *left = *right = -1;
636 *right = 0x007f & ( vol >> 8 );
637 *left = 0x007f & vol;
639 #else
640 /* Suns, HPs, Linux, NEWS can't read the volume; oh well */
641 *left = *right = -1;
643 #endif
644 #endif
645 return (0);
648 #ifdef BUILD_CDDA /* { */
651 * Tell the CDDA slave to set the play direction.
653 void
654 gen_set_direction( int newdir )
656 unsigned char buf[2];
658 if (cdda_slave > -1)
660 buf[0] = 'd';
661 buf[1] = newdir;
662 write(cdda_slave, buf, 2);
663 get_ack(cdda_slave);
668 * Tell the CDDA slave to set the play speed.
670 void
671 gen_set_speed( int speed )
673 unsigned char buf[2];
675 if (cdda_slave > -1)
677 buf[0] = 's';
678 buf[1] = speed;
679 write(cdda_slave, buf, 2);
680 get_ack(cdda_slave);
685 * Tell the CDDA slave to set the loudness level.
687 void
688 gen_set_loudness( int loud )
690 unsigned char buf[2];
692 if (cdda_slave > -1)
694 buf[0] = 'L';
695 buf[1] = loud;
696 write(cdda_slave, buf, 2);
697 get_ack(cdda_slave);
702 * Tell the CDDA slave to start (or stop) saving to a file.
704 void
705 gen_save( char *filename )
707 int len;
709 if (filename == NULL || filename[0] == '\0')
710 len = 0;
711 else
712 len = strlen(filename);
713 write(cdda_slave, "F", 1);
714 write(cdda_slave, &len, sizeof(len));
715 if (len)
716 write(cdda_slave, filename, len);
717 get_ack(cdda_slave);
720 #endif /* BUILD_CDDA } */
722 /*---------------------------------------------*
723 * Send an arbitrary SCSI command to a device.
724 *---------------------------------------------*/
726 wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
727 void *retbuf, int retbuflen, int getreply )
729 #ifdef LINUX_SCSI_PASSTHROUGH
731 char *cmd;
732 int cmdsize;
734 cmdsize = 2 * sizeof(int);
735 if (retbuf)
737 if (getreply) cmdsize += max(cdblen, retbuflen);
738 else cmdsize += (cdblen + retbuflen);
740 else cmdsize += cdblen;
742 cmd = malloc(cmdsize);
743 if (cmd == NULL)
744 return (-1);
746 ((int*)cmd)[0] = cdblen + ((retbuf && !getreply) ? retbuflen : 0);
747 ((int*)cmd)[1] = ((retbuf && getreply) ? retbuflen : 0);
749 memcpy(cmd + 2*sizeof(int), cdb, cdblen);
750 if (retbuf && !getreply)
751 memcpy(cmd + 2*sizeof(int) + cdblen, retbuf, retbuflen);
753 if (ioctl(d->fd, SCSI_IOCTL_SEND_COMMAND, cmd))
755 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "%s: ioctl() failure\n", __FILE__);
756 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "command buffer is:\n");
757 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "%02x %02x %02x %02x %02x %02x\n",
758 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);
759 free(cmd);
760 return (-1);
763 if (retbuf && getreply)
764 memcpy(retbuf, cmd + 2*sizeof(int), retbuflen);
766 free(cmd);
767 return 0;
769 #else /* Linux SCSI passthrough*/
770 return (-1);
771 #endif
774 /*---------------------------------------------------------------------------*
775 * Open the CD device and figure out what kind of drive is attached.
776 *---------------------------------------------------------------------------*/
778 wmcd_open( struct wm_drive *d )
780 int fd;
781 static int warned = 0;
782 int retval = 0;
783 char vendor[32], model[32], rev[32];
785 if (cd_device == NULL)
786 cd_device = DEFAULT_CD_DEVICE;
788 if (d->fd >= 0) /* Device already open? */
790 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
791 return (0);
794 d->fd = open(cd_device, O_RDONLY | O_NONBLOCK);
795 if (d->fd < 0)
797 if (errno == EACCES)
799 if (!warned)
801 fprintf(stderr,
802 "As root, please run\n\nchmod 666 %s\n\n%s\n", cd_device,
803 "to give yourself permission to access the CD-ROM device.");
804 warned++;
807 /* Hack proposed by Carey Evans, introduced by Debian maintainer :
808 * treat EIO like ENXIO since some Linux drives do never return ENXIO
810 else if ((errno != ENXIO) && (errno != EIO) && (errno != ENOMEDIUM))
812 perror(cd_device);
813 exit(1);
816 /* No CD in drive. */
817 retval = 1;
820 if (warned)
822 warned = 0;
823 fprintf(stderr, "Thank you.\n");
826 /* Now fill in the relevant parts of the wm_drive structure. */
827 fd = d->fd;
829 #ifdef LINUX_SCSI_PASSTHROUGH
830 /* Can we figure out the drive type? */
831 wm_scsi_get_drive_type(d, vendor, model, rev);
832 #endif
833 *d = *(find_drive_struct(vendor, model, rev));
834 wm_drive_settype(vendor, model, rev);
836 d->fd = fd;
837 (d->init)(d);
838 return retval;
839 } /* wmcd_open() */
842 * Re-Open the device if it is open.
845 wmcd_reopen( struct wm_drive *d )
847 int status;
849 do {
850 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen ");
851 if (d->fd >= 0) /* Device really open? */
853 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closes the device and ");
854 status = close( d->fd ); /* close it! */
855 /* we know, that the file is closed, do we? */
856 d->fd = -1;
858 wm_susleep( 1000 );
859 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
860 status = wmcd_open( d ); /* open it as usual */
861 wm_susleep( 1000 );
862 } while ( status != 0 );
863 return status;
864 } /* wmcd_reopen() */
866 #endif /* linux */