summaryrefslogtreecommitdiffstats
path: root/kscd/libwm
diff options
context:
space:
mode:
Diffstat (limited to 'kscd/libwm')
-rw-r--r--kscd/libwm/Makefile.am23
-rw-r--r--kscd/libwm/PLAT_IMPL_STATUS48
-rw-r--r--kscd/libwm/README20
-rw-r--r--kscd/libwm/audio/Makefile.am5
-rw-r--r--kscd/libwm/audio/audio.c29
-rw-r--r--kscd/libwm/audio/audio.h21
-rw-r--r--kscd/libwm/audio/audio_alsa.c334
-rw-r--r--kscd/libwm/audio/audio_arts.c141
-rw-r--r--kscd/libwm/audio/audio_sun.c525
-rw-r--r--kscd/libwm/buildindex.c221
-rw-r--r--kscd/libwm/cdda.c458
-rw-r--r--kscd/libwm/cddaslave.c572
-rw-r--r--kscd/libwm/cddb.c588
-rw-r--r--kscd/libwm/cdinfo.c890
-rw-r--r--kscd/libwm/cdrom.c899
-rw-r--r--kscd/libwm/cdtext.c428
-rw-r--r--kscd/libwm/configure.in.in72
-rw-r--r--kscd/libwm/database.c1543
-rw-r--r--kscd/libwm/drv_sony.c166
-rw-r--r--kscd/libwm/drv_toshiba.c149
-rw-r--r--kscd/libwm/include/wm_cdda.h203
-rw-r--r--kscd/libwm/include/wm_cddb.h36
-rw-r--r--kscd/libwm/include/wm_cdinfo.h76
-rw-r--r--kscd/libwm/include/wm_cdrom.h114
-rw-r--r--kscd/libwm/include/wm_cdtext.h105
-rw-r--r--kscd/libwm/include/wm_config.h377
-rw-r--r--kscd/libwm/include/wm_database.h42
-rw-r--r--kscd/libwm/include/wm_helpers.h109
-rw-r--r--kscd/libwm/include/wm_index.h36
-rw-r--r--kscd/libwm/include/wm_platform.h51
-rw-r--r--kscd/libwm/include/wm_scsi.h50
-rw-r--r--kscd/libwm/include/wm_struct.h198
-rw-r--r--kscd/libwm/include/wm_version.h35
-rw-r--r--kscd/libwm/include/workman.h52
-rw-r--r--kscd/libwm/include/workman_defs.h30
-rw-r--r--kscd/libwm/index.c383
-rw-r--r--kscd/libwm/plat_aix.c489
-rw-r--r--kscd/libwm/plat_bsd386.c510
-rw-r--r--kscd/libwm/plat_freebsd.c560
-rw-r--r--kscd/libwm/plat_hpux.c358
-rw-r--r--kscd/libwm/plat_irix.c474
-rw-r--r--kscd/libwm/plat_linux.c803
-rw-r--r--kscd/libwm/plat_linux_audio.c489
-rw-r--r--kscd/libwm/plat_linux_cdda.c253
-rw-r--r--kscd/libwm/plat_news.c442
-rw-r--r--kscd/libwm/plat_openbsd.c545
-rw-r--r--kscd/libwm/plat_osf1.c674
-rw-r--r--kscd/libwm/plat_scor5.c426
-rw-r--r--kscd/libwm/plat_sun.c972
-rw-r--r--kscd/libwm/plat_sun_audio.c493
-rw-r--r--kscd/libwm/plat_sun_cdda.c380
-rw-r--r--kscd/libwm/plat_svr4.c482
-rw-r--r--kscd/libwm/plat_template.c295
-rw-r--r--kscd/libwm/plat_ultrix.c663
-rw-r--r--kscd/libwm/scsi.c667
-rw-r--r--kscd/libwm/wm_helpers.c238
56 files changed, 19242 insertions, 0 deletions
diff --git a/kscd/libwm/Makefile.am b/kscd/libwm/Makefile.am
new file mode 100644
index 00000000..ae2a9144
--- /dev/null
+++ b/kscd/libwm/Makefile.am
@@ -0,0 +1,23 @@
+#
+# Makefile.am for libworkman. Based on the example Makefile.am for a
+# shared library.
+#
+
+subdirs = include
+SUBDIRS = audio
+INCLUDES = $(all_includes)
+
+noinst_LTLIBRARIES = libworkman.la
+
+libworkman_la_LDFLAGS = $(ARTSC_LIBS) $(all_libraries)
+libworkman_la_LIBADD = audio/libworkmanaudio.la $(ARTS_LIBASOUND)
+
+libworkman_la_SOURCES = cddb.c cdinfo.c cdrom.c wm_helpers.c cdtext.c\
+database.c index.c scsi.c cdda.c plat_linux_cdda.c plat_sun_cdda.c\
+plat_aix.c plat_bsd386.c plat_freebsd.c plat_hpux.c plat_irix.c \
+plat_linux.c plat_svr4.c plat_ultrix.c plat_news.c plat_openbsd.c \
+plat_osf1.c plat_sun.c plat_scor5.c \
+drv_sony.c drv_toshiba.c
+
+#libworkmanincludedir = $(includedir)/libwm
+#libworkmaninclude_HEADERS = include/wm_cdrom.h include/wm_cdtext.h
diff --git a/kscd/libwm/PLAT_IMPL_STATUS b/kscd/libwm/PLAT_IMPL_STATUS
new file mode 100644
index 00000000..d1919728
--- /dev/null
+++ b/kscd/libwm/PLAT_IMPL_STATUS
@@ -0,0 +1,48 @@
+LIBRARY IMPLEMENTATION STATUS
+-----------------------------
+
+This table should show up where the platform codes differ.
+
+- function not available
+X function available
+\ function dummy, null implementation or commented out
+(X) function available in special configurations
+
+function |aix |bsd386|freebsd|hpux |irix |linux|news |openbsd|osf1 |scor5|sun |svr4 |ultrix|
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+find_cdrom() | - | - | - | - | - | - | - | - | X | - | X | X | X |
+gen_init() | \ | \ | \ | \ | (X) | \ | \ | \ | \ | \ | X | \ | \ |
+wmcd_open() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+wmcd_close() | - | - | - | - | - | - | X | - | - | - | - | - | - |
+wmcd_reopen() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+wm_scsi() | \ | \ | \ | X | \ | X | \ | \ | \ | X | X | X | \ |
+keep_cd_open() | - | - | - | - | - | \ | - | - | - | \ | - | - | - |
+gen_get_drive_status() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+gen_get_trackcount() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_get_trackinfo() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_get_cdlen() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+gen_play() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_pause() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_resume() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_stop() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_eject() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_closetray() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+scale_volume() | X | X | X | - | - | X | - | X | X | - | - | - | X |
+unscale_volume() | X | X | X | - | - | - | - | X | X | - | - | - | X |
+gen_set_volume() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_get_volume() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+cdda_init() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+get_ack() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+cdda_kill() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+gen_set_direction() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+gen_set_speed() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+gen_set_loudness() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+gen_save() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+
+
+
diff --git a/kscd/libwm/README b/kscd/libwm/README
new file mode 100644
index 00000000..69b4ae65
--- /dev/null
+++ b/kscd/libwm/README
@@ -0,0 +1,20 @@
+$Id$
+
+This directory contains the WorkMan library.
+
+libworkman is a multi-plaform CD-Player library for creating various
+CD-Player-UIs.
+
+06.03.2003 Alex Kern <alex.kern@gmx.de>
+ I have reworked most part of libworkman, get rid of most of externals
+ add cdda support for linux and, I hope not breaked it for SUN ;-)
+
+ Interface was cleaned, that means for a potentialy application developers
+ new rules:
+ 1. please include only wm_cdrom.h it's all defines and functions, what
+ you need. You have any exceptions.
+ 2. for cdtext, include wm_cdtext.h
+ 3. for cddb, include wm_cddb.h (think, it's not so powerfull and can be replaced with
+ any new external library.
+ 4. wm_cdinfo declares any externals now, I will change it in the future
+ \ No newline at end of file
diff --git a/kscd/libwm/audio/Makefile.am b/kscd/libwm/audio/Makefile.am
new file mode 100644
index 00000000..318cb0a3
--- /dev/null
+++ b/kscd/libwm/audio/Makefile.am
@@ -0,0 +1,5 @@
+INCLUDES = $(all_includes) $(ARTSC_INCLUDE)
+
+noinst_LTLIBRARIES = libworkmanaudio.la
+
+libworkmanaudio_la_SOURCES = audio.c audio_alsa.c audio_arts.c audio_sun.c
diff --git a/kscd/libwm/audio/audio.c b/kscd/libwm/audio/audio.c
new file mode 100644
index 00000000..39c8330d
--- /dev/null
+++ b/kscd/libwm/audio/audio.c
@@ -0,0 +1,29 @@
+#include "audio.h"
+
+#include <string.h>
+
+struct audio_oops* setup_arts(const char *dev, const char *ctl);
+struct audio_oops* setup_alsa(const char *dev, const char *ctl);
+
+struct audio_oops* setup_soundsystem(const char* ss, const char* dev, const char* ctl)
+{
+ if(!ss) {
+ ERRORLOG("audio: Internal error, trying to setup a NULL soundsystem.\n");
+ return NULL;
+ }
+
+#ifdef USE_ARTS
+ if(!strcmp(ss, "arts"))
+ return setup_arts(dev, ctl);
+#endif
+#if defined(HAVE_ARTS_LIBASOUND2)
+ if(!strcmp(ss, "alsa"))
+ return setup_alsa(dev, ctl);
+#endif
+#ifdef USE_SUN_AUDIO
+ if(!strcmp(ss, "sun"))
+ return setup_sun_audio(dev, ctl);
+#endif
+ ERRORLOG("audio: unknown soundsystem '%s'\n", ss);
+ return NULL;
+}
diff --git a/kscd/libwm/audio/audio.h b/kscd/libwm/audio/audio.h
new file mode 100644
index 00000000..089ea116
--- /dev/null
+++ b/kscd/libwm/audio/audio.h
@@ -0,0 +1,21 @@
+/*
+ * Audio 'LIB' defines
+ */
+#include "../include/wm_cdda.h"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+struct audio_oops {
+ int (*wmaudio_open)(void);
+ int (*wmaudio_close)(void);
+ int (*wmaudio_play)(struct cdda_block*);
+ int (*wmaudio_stop)(void);
+ int (*wmaudio_state)(struct cdda_block*);
+ int (*wmaudio_balance)(int);
+ int (*wmaudio_volume)(int);
+};
+
+extern struct audio_oops* setup_soundsystem(const char*, const char*, const char*);
+
diff --git a/kscd/libwm/audio/audio_alsa.c b/kscd/libwm/audio/audio_alsa.c
new file mode 100644
index 00000000..b1d4e938
--- /dev/null
+++ b/kscd/libwm/audio/audio_alsa.c
@@ -0,0 +1,334 @@
+/*
+ * Driver for Advanced Linux Sound Architecture, http://alsa.jcu.cz
+ *
+ * mpg123 comments:
+ * Code by Anders Semb Hermansen <ahermans@vf.telia.no>
+ * Cleanups by Jaroslav Kysela <perex@jcu.cz>
+ * Ville Syrjala <syrjala@sci.fi>
+ *
+ * adopted for libworkman cdda audio backend from Alexander Kern alex.kern@gmx.de
+ *
+ * Adapted to support both ALSA V0.x and V1.x APIs for PCM calls
+ * (Philip Nelson <teamdba@scotdb.com> 2004-03-15)
+ *
+ * This file comes under GPL license.
+ */
+
+
+
+#include <config.h>
+
+#if defined(HAVE_ARTS_LIBASOUND2)
+
+#include <alsa/asoundlib.h>
+#include "audio.h"
+
+char* device = NULL;
+snd_pcm_t *handle;
+
+snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */
+int rate = 44100; /* stream rate */
+int channels = 2; /* count of channels */
+int buffer_time = 2000000; /* ring buffer length in us */
+int period_time = 100000; /* period time in us */
+
+snd_pcm_sframes_t buffer_size;
+snd_pcm_sframes_t period_size;
+
+int alsa_open(void);
+int alsa_close(void);
+int alsa_stop(void);
+int alsa_play(struct cdda_block *blk);
+int alsa_state(struct cdda_block *blk);
+struct audio_oops* setup_alsa(const char *dev, const char *ctl);
+
+static int set_hwparams(snd_pcm_hw_params_t *params,
+ snd_pcm_access_t accesspar)
+{
+ int err, dir, new_rate;
+
+ /* choose all parameters */
+ err = snd_pcm_hw_params_any(handle, params);
+ if (err < 0) {
+ ERRORLOG("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* set the interleaved read/write format */
+ err = snd_pcm_hw_params_set_access(handle, params, accesspar);
+ if (err < 0) {
+ ERRORLOG("Access type not available for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* set the sample format */
+ err = snd_pcm_hw_params_set_format(handle, params, format);
+ if (err < 0) {
+ ERRORLOG("Sample format not available for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* set the count of channels */
+ err = snd_pcm_hw_params_set_channels(handle, params, channels);
+ if (err < 0) {
+ ERRORLOG("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
+ return err;
+ }
+ /* set the stream rate */
+#if (SND_LIB_MAJOR < 1)
+ err = new_rate = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0);
+#else
+ new_rate = rate;
+ err = snd_pcm_hw_params_set_rate_near(handle, params, &new_rate, 0);
+#endif
+ if (err < 0) {
+ ERRORLOG("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
+ return err;
+ }
+ if (new_rate != rate) {
+ ERRORLOG("Rate doesn't match (requested %iHz, get %iHz)\n", rate, new_rate);
+ return -EINVAL;
+ }
+ /* set the buffer time */
+#if (SND_LIB_MAJOR < 1)
+ err = snd_pcm_hw_params_set_buffer_time_near(handle, params, buffer_time, &dir);
+#else
+ err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
+#endif
+ if (err < 0) {
+ ERRORLOG("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
+ return err;
+ }
+#if (SND_LIB_MAJOR < 1)
+ buffer_size = snd_pcm_hw_params_get_buffer_size(params);
+#else
+ err = snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
+ if (err < 0) {
+ ERRORLOG("Unable to get buffer size : %s\n", snd_strerror(err));
+ return err;
+ }
+#endif
+ DEBUGLOG("buffersize %i\n", buffer_size);
+
+ /* set the period time */
+#if (SND_LIB_MAJOR < 1)
+ err = snd_pcm_hw_params_set_period_time_near(handle, params, period_time, &dir);
+#else
+ err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
+#endif
+ if (err < 0) {
+ ERRORLOG("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
+ return err;
+ }
+
+#if (SND_LIB_MAJOR < 1)
+ period_size = snd_pcm_hw_params_get_period_size(params, &dir);
+#else
+ err = snd_pcm_hw_params_get_period_size(params, &period_size, &dir);
+ if (err < 0) {
+ ERRORLOG("Unable to get hw period size: %s\n", snd_strerror(err));
+ }
+#endif
+
+ DEBUGLOG("period_size %i\n", period_size);
+
+ /* write the parameters to device */
+ err = snd_pcm_hw_params(handle, params);
+ if (err < 0) {
+ ERRORLOG("Unable to set hw params for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+static int set_swparams(snd_pcm_sw_params_t *swparams)
+{
+ int err;
+
+ /* get the current swparams */
+ err = snd_pcm_sw_params_current(handle, swparams);
+ if (err < 0) {
+ ERRORLOG("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* start the transfer when the buffer is full */
+ err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size);
+ if (err < 0) {
+ ERRORLOG("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* allow the transfer when at least period_size samples can be processed */
+ err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size);
+ if (err < 0) {
+ ERRORLOG("Unable to set avail min for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* align all transfers to 1 sample */
+ err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1);
+ if (err < 0) {
+ ERRORLOG("Unable to set transfer align for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* write the parameters to the playback device */
+ err = snd_pcm_sw_params(handle, swparams);
+ if (err < 0) {
+ ERRORLOG("Unable to set sw params for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+int alsa_open( void )
+{
+ int err;
+
+ snd_pcm_hw_params_t *hwparams;
+ snd_pcm_sw_params_t *swparams;
+
+ DEBUGLOG("alsa_open\n");
+
+ snd_pcm_hw_params_alloca(&hwparams);
+ snd_pcm_sw_params_alloca(&swparams);
+
+ if((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0/*SND_PCM_NONBLOCK*/)) < 0 ) {
+ ERRORLOG("open failed: %s\n", snd_strerror(err));
+ return -1;
+ }
+
+ if((err = set_hwparams(hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
+ ERRORLOG("Setting of hwparams failed: %s\n", snd_strerror(err));
+ return -1;
+ }
+ if((err = set_swparams(swparams)) < 0) {
+ ERRORLOG("Setting of swparams failed: %s\n", snd_strerror(err));
+ return -1;
+ }
+
+ return 0;
+}
+
+int alsa_close( void )
+{
+ int err;
+
+ DEBUGLOG("alsa_close\n");
+
+ err = alsa_stop();
+
+#if (SND_LIB_MAJOR < 1)
+ err = snd_pcm_close(handle);
+#else
+ err = snd_pcm_close(handle);
+#endif
+
+ free(device);
+
+ return err;
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+alsa_play(struct cdda_block *blk)
+{
+ signed short *ptr;
+ int err = 0, frames;
+
+ ptr = (signed short *)blk->buf;
+ frames = blk->buflen / (channels * 2);
+ DEBUGLOG("play %i frames, %i bytes\n", frames, blk->buflen);
+ while (frames > 0) {
+ err = snd_pcm_writei(handle, ptr, frames);
+
+ if (err == -EAGAIN)
+ continue;
+ if(err == -EPIPE) {
+ err = snd_pcm_prepare(handle);
+ continue;
+ } else if (err < 0)
+ break;
+
+ ptr += err * channels;
+ frames -= err;
+ DEBUGLOG("played %i, rest %i\n", err / channels, frames);
+ }
+
+ if (err < 0) {
+ ERRORLOG("alsa_write failed: %s\n", snd_strerror(err));
+ err = snd_pcm_prepare(handle);
+
+ if (err < 0) {
+ ERRORLOG("Unable to snd_pcm_prepare pcm stream: %s\n", snd_strerror(err));
+ }
+ blk->status = WM_CDM_CDDAERROR;
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+int
+alsa_stop( void )
+{
+ int err;
+
+ DEBUGLOG("alsa_stop\n");
+
+ err = snd_pcm_drop(handle);
+ if (err < 0) {
+ ERRORLOG("Unable to drop pcm stream: %s\n", snd_strerror(err));
+ }
+
+ err = snd_pcm_prepare(handle);
+ if (err < 0) {
+ ERRORLOG("Unable to snd_pcm_prepare pcm stream: %s\n", snd_strerror(err));
+ }
+
+ return err;
+}
+
+/*
+ * Get the current audio state.
+ */
+int
+alsa_state(struct cdda_block *blk)
+{
+ return -1; /* not implemented yet for ALSA */
+}
+
+static struct audio_oops alsa_oops = {
+ .wmaudio_open = alsa_open,
+ .wmaudio_close = alsa_close,
+ .wmaudio_play = alsa_play,
+ .wmaudio_stop = alsa_stop,
+ .wmaudio_state = alsa_state,
+ .wmaudio_balance = NULL,
+ .wmaudio_volume = NULL
+};
+
+struct audio_oops*
+setup_alsa(const char *dev, const char *ctl)
+{
+ static int init_complete = 0;
+
+ if(dev && strlen(dev) > 0) {
+ device = strdup(dev);
+ } else {
+ device = strdup("plughw:0,0"); /* playback device */
+ }
+
+ if(init_complete) {
+ ERRORLOG("already initialized\n");
+ return NULL;
+ }
+ if(!alsa_open())
+ init_complete = 1;
+ else
+ return NULL;
+
+ return &alsa_oops;
+}
+
+#endif /* ALSA */
diff --git a/kscd/libwm/audio/audio_arts.c b/kscd/libwm/audio/audio_arts.c
new file mode 100644
index 00000000..a7b033c9
--- /dev/null
+++ b/kscd/libwm/audio/audio_arts.c
@@ -0,0 +1,141 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Linux digital audio functions.
+ *
+ *
+ * Forget /dev/audio
+ * most modern soundcards accept 16LE with 44.1kHz
+ * Alexander Kern alex.kern@gmx.de
+ */
+
+#include <config.h>
+
+#ifdef USE_ARTS
+
+#include <artsc.h>
+
+#include "audio.h"
+
+arts_stream_t arts_stream = NULL;
+
+int arts_open(void);
+int arts_close(void);
+int arts_stop(void);
+int arts_play(struct cdda_block *blk);
+int arts_state(struct cdda_block *blk);
+struct audio_oops* setup_arts(const char *dev, const char *ctl);
+
+/*
+ * Initialize the audio device.
+ */
+int
+arts_open(void)
+{
+ int err;
+
+ DEBUGLOG("arts_open\n");
+
+ if(!(arts_stream = arts_play_stream(44100, 16, 2, "cddaslave"))) {
+ ERRORLOG("cannot open ARTS stream for playback\n");
+ return -1;
+ }
+ /* 1000 ms because we read 75 frames = 1 sec */
+ if((err = arts_stream_set(arts_stream, ARTS_P_BUFFER_TIME, 1000)) < 0) {
+ ERRORLOG("arts_stream_set failed (%s)\n", arts_error_text(err));
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Close the audio device.
+ */
+int
+arts_close(void)
+{
+ arts_stop();
+
+ DEBUGLOG("arts_close\n");
+ arts_close_stream(arts_stream);
+
+ arts_free();
+
+ return 0;
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+arts_play(struct cdda_block *blk)
+{
+ int err;
+
+ if((err = arts_write(arts_stream, blk->buf, blk->buflen)) < 0) {
+ ERRORLOG("arts_write failed (%s)\n", arts_error_text(err));
+ blk->status = WM_CDM_CDDAERROR;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+int
+arts_stop(void)
+{
+ DEBUGLOG("arts_stop\n");
+ return 0;
+}
+
+/*
+ * Get the current audio state.
+ */
+int
+arts_state(struct cdda_block *blk)
+{
+ return -1; /* not implemented yet for ARTS */
+}
+
+static struct audio_oops arts_oops = {
+ .wmaudio_open = arts_open,
+ .wmaudio_close = arts_close,
+ .wmaudio_play = arts_play,
+ .wmaudio_stop = arts_stop,
+ .wmaudio_state = arts_state,
+ .wmaudio_balance = NULL,
+ .wmaudio_volume = NULL
+};
+
+struct audio_oops*
+setup_arts(const char *dev, const char *ctl)
+{
+ int err;
+
+ if((err = arts_init())) {
+ ERRORLOG("cannot initialize ARTS audio subsystem (%s)\n", arts_error_text(err));
+ return NULL;
+ }
+
+ arts_open();
+
+ return &arts_oops;
+}
+#endif
diff --git a/kscd/libwm/audio/audio_sun.c b/kscd/libwm/audio/audio_sun.c
new file mode 100644
index 00000000..78b1ab21
--- /dev/null
+++ b/kscd/libwm/audio/audio_sun.c
@@ -0,0 +1,525 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sun (really Solaris) digital audio functions.
+ */
+
+#include <config.h>
+
+#ifdef USE_SUN_AUDIO
+
+#include <stdio.h>
+#include <malloc.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#include <sys/stropts.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "audio.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * Since there's a lag time between writing audio to the audio device and
+ * hearing it, we need to make sure the status indicators correlate to what's
+ * playing out the speaker. Luckily, Solaris gives us some audio
+ * synchronization facilities that make this pretty easy.
+ *
+ * We maintain a circular queue of status information. When we write some
+ * sound to the audio device, we put its status info into the queue. We write
+ * a marker into the audio stream; when the audio device driver encounters the
+ * marker, it increments a field in a status structure. When we see that
+ * field go up, we grab the next status structure from the queue and send it
+ * to the parent process.
+ *
+ * The minimum size of the queue depends on the latency of the audio stream.
+ */
+#define QSIZE 500
+
+struct cdda_block queue[QSIZE];
+int qtail;
+int qstart;
+
+/*
+ * We only send WM_CDM_PLAYING status messages upstream when the CD is supposed
+ * to be playing; this is used to keep track.
+ */
+extern int playing;
+
+static int aufd, aucfd;
+static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */
+
+/*
+ * For fast linear-to-ulaw mapping, we use a lookup table that's generated
+ * at startup.
+ */
+unsigned char *ulawmap, linear_to_ulaw();
+
+char *getenv();
+
+/*
+ * Dummy signal handler so writes to /dev/audio will interrupt.
+ */
+static void
+dummy( void )
+{
+ signal(SIGALRM, dummy);
+}
+
+/*
+ * Initialize the audio device.
+ */
+void
+sun_audio_init( void )
+{
+ audio_info_t info;
+ char *audiodev, *acdev;
+ int linval;
+
+ audiodev = getenv("AUDIODEV");
+ if (audiodev == NULL ||
+ strncmp("/dev/", audiodev, 5) ||
+ strstr(audiodev, "/../") )
+ audiodev = "/dev/audio";
+
+ acdev = malloc(strlen(audiodev) + 4);
+ if (acdev == NULL)
+ {
+ perror("Can't allocate audio control filename");
+ exit(1);
+ }
+ strcpy(acdev, audiodev);
+ strcat(acdev, "ctl");
+
+ aucfd = open(acdev, O_WRONLY, 0);
+ if (aucfd < 0)
+ {
+ perror(acdev);
+ exit(1);
+ }
+ free(acdev);
+
+ aufd = open(audiodev, O_WRONLY, 0);
+ if (aufd < 0)
+ {
+ perror(audiodev);
+ exit(1);
+ }
+
+ signal(SIGALRM, dummy);
+
+ /*
+ * Try to set the device to CD-style audio; we can process it
+ * with the least CPU overhead.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 44100;
+ info.play.channels = 2;
+ info.play.precision = 16;
+ info.play.encoding = AUDIO_ENCODING_LINEAR;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ if (errno == EINVAL)
+ {
+ /*
+ * Oh well, so much for that idea.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 8000;
+ info.play.channels = 1;
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_ULAW;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ {
+ perror("Can't set up audio device");
+ exit(1);
+ }
+
+ /*
+ * Initialize the linear-to-ulaw mapping table.
+ */
+ if (ulawmap == NULL)
+ ulawmap = malloc(65536);
+ if (ulawmap == NULL)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ for (linval = 0; linval < 65536; linval++)
+ ulawmap[linval] = linear_to_ulaw(linval-32768);
+ ulawmap += 32768;
+ raw_audio = 0;
+ }
+ else
+ {
+ perror(audiodev);
+ exit(1);
+ }
+}
+
+/*
+ * Get ready to play some sound.
+ */
+void
+sun_audio_ready( void )
+{
+ audio_info_t info;
+
+ /*
+ * Start at the correct queue position.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ qtail = info.play.eof % QSIZE;
+ qstart = qtail;
+
+ queue[qtail].status = WM_CDM_PLAYING;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+int
+sun_audio_stop( void )
+{
+ if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
+ perror("flush");
+ return 0;
+}
+
+/*
+ * Close the audio device.
+ */
+int
+sun_audio_close( void )
+{
+ wmaudio_stop();
+ close(aufd);
+ close(aucfd);
+ return 0;
+}
+
+/*
+ * Set the volume level.
+ */
+int
+sun_audio_volume(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ info.play.gain = level;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) {
+ perror("AUDIO_SETINFO");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Set the balance level.
+ */
+int
+sun_audio_balance(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ level *= AUDIO_RIGHT_BALANCE;
+ info.play.balance = level / 255;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) {
+ perror("AUDIO_SETINFO");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Mark the most recent audio block on the queue as the last one.
+ */
+void
+sun_audio_mark_last( void )
+{
+ queue[qtail].status = WM_CDM_TRACK_DONE;
+}
+
+/*
+ * Figure out the most recent status information and send it upstream.
+ */
+int
+sun_audio_send_status( void )
+{
+ audio_info_t info;
+ int qhead;
+
+ /*
+ * Now send the most current status information to our parent.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ qhead = info.play.eof % QSIZE;
+
+ if (qhead != qstart && playing)
+ {
+ int balance;
+
+ if (queue[qhead].status != WM_CDM_TRACK_DONE)
+ queue[qhead].status = WM_CDM_PLAYING;
+ queue[qhead].volume = info.play.gain;
+ queue[qhead].balance = (info.play.balance * 255) /
+ AUDIO_RIGHT_BALANCE;
+
+ send_status(queue + qhead);
+ qstart = -1;
+ }
+
+ return (queue[qhead].status == WM_CDM_TRACK_DONE);
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+sun_audio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ int i;
+ short *buf16;
+ int alarmcount = 0;
+ struct itimerval it;
+ long playablelen;
+
+ alarm(1);
+ playablelen = dev_audio_convert(rawbuf, buflen, blk);
+ while (write(aufd, rawbuf, playablelen) <= 0)
+ if (errno == EINTR)
+ {
+ if (! raw_audio && alarmcount++ < 5)
+ {
+ /*
+ * 8KHz /dev/audio blocks for several seconds
+ * waiting for its queue to drop below a low
+ * water mark.
+ */
+ wmaudio_send_status();
+ timerclear(&it.it_interval);
+ timerclear(&it.it_value);
+ it.it_value.tv_usec = 500000;
+ setitimer(ITIMER_REAL, &it, NULL);
+ continue;
+ }
+
+/* close(aufd);
+ close(aucfd);
+ wmaudio_init();
+*/ sun_audio_stop();
+ alarm(2);
+ continue;
+ }
+ else
+ {
+ blk->status = WM_CDM_CDDAERROR;
+ return (-1);
+ }
+ alarm(0);
+
+ /*
+ * Mark this spot in the audio stream.
+ *
+ * Marks don't always succeed (if the audio buffer is empty
+ * this call will block forever) so do it asynchronously.
+ */
+ fcntl(aufd, F_SETFL, O_NONBLOCK);
+ if (write(aufd, rawbuf, 0) < 0)
+ {
+ if (errno != EAGAIN)
+ perror("audio mark");
+ }
+ else
+ qtail = (qtail + 1) % QSIZE;
+
+ fcntl(aufd, F_SETFL, 0);
+
+ queue[qtail] = *blk;
+
+ if (wmaudio_send_status() < 0)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Get the current audio state.
+ */
+int
+sun_audio_state(struct cdda_block *blk)
+{
+ audio_info_t info;
+ int balance;
+
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ blk->volume = info.play.gain;
+ blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
+ return 0;
+}
+
+/*
+** This routine converts from linear to ulaw.
+**
+** Craig Reese: IDA/Supercomputing Research Center
+** Joe Campbell: Department of Defense
+** 29 September 1989
+**
+** References:
+** 1) CCITT Recommendation G.711 (very difficult to follow)
+** 2) "A New Digital Technique for Implementation of Any
+** Continuous PCM Companding Law," Villeret, Michel,
+** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
+** 1973, pg. 11.12-11.17
+** 3) MIL-STD-188-113,"Interoperability and Performance Standards
+** for Analog-to_Digital Conversion Techniques,"
+** 17 February 1987
+**
+** Input: Signed 16 bit linear sample
+** Output: 8 bit ulaw sample
+*/
+#define ZEROTRAP /* turn on the trap as per the MIL-STD */
+#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+unsigned char
+linear_to_ulaw( sample )
+int sample;
+{
+ static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if ( sign != 0 ) sample = -sample; /* get magnitude */
+ if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[( sample >> 7 ) & 0xFF];
+ mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
+ ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
+#ifdef ZEROTRAP
+ if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+
+ return ulawbyte;
+}
+
+/*
+ * Downsample a block of CDDA data, if necessary, for playing out an old-style
+ * audio device.
+ */
+long
+dev_audio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ short *buf16 = (short *)rawbuf;
+ int i, j, samples;
+ int mono_value;
+ unsigned char *rbend = rawbuf + buflen;
+
+ /* Don't do anything if the audio device can take the raw values. */
+ if (raw_audio)
+ return (buflen);
+
+ for (i = 0; buf16 < (short *)(rbend); i++)
+ {
+ /* Downsampling to 8KHz is a little irregular. */
+ samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
+
+ /* And unfortunately, we don't always end on a nice boundary. */
+ if (buf16 + samples > (short *)(rbend))
+ samples = ((short *)rbend) - buf16;
+
+ /*
+ * No need to average all the values; taking the first one
+ * is sufficient and less CPU-intensive. But we do need to
+ * do both channels.
+ */
+ mono_value = (buf16[0] + buf16[1]) / 2;
+ buf16 += samples;
+ rawbuf[i] = ulawmap[mono_value];
+ }
+
+ return (i);
+}
+
+static struct audio_oops sun_audio_oops = {
+ .wmaudio_open = sun_audio_open,
+ .wmaudio_close = sun_audio_close,
+ .wmaudio_play = sun_audio_play,
+ .wmaudio_stop = sun_audio_stop,
+ .wmaudio_state = sun_audio_state,
+ .wmaudio_balance = sun_audio_balance,
+ .wmaudio_volume = sun_audio_volume
+};
+
+struct audio_oops*
+setup_sun_audio(const char *dev, const char *ctl)
+{
+ int err;
+
+ if((err = sun_audio_init())) {
+ ERRORLOG("cannot initialize SUN /dev/audio subsystem \n");
+ return NULL;
+ }
+
+ sun_audio_open();
+
+ return &sun_audio_oops;
+}
+
+#endif /* USE_SUN_AUDIO */
diff --git a/kscd/libwm/buildindex.c b/kscd/libwm/buildindex.c
new file mode 100644
index 00000000..8edc3684
--- /dev/null
+++ b/kscd/libwm/buildindex.c
@@ -0,0 +1,221 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Build a WorkMan database index file from a flat text file. Requires
+ * 4.4BSD libdb library.
+ */
+
+static char buildindex_id[] = "$Id$";
+
+#include <stdio.h>
+#include <db.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netinet/in.h> /* for htonl() */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+char *strrchr();
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ DB *db;
+ DBT key, data;
+ FILE *fp;
+ int lock = 1, i = 0, locked, frame;
+ char buf[1000], indname[MAXPATHLEN + 100], framebuf[8], *c;
+ unsigned long pos;
+ BTREEINFO bt;
+ struct stat st;
+
+ if (argc > 2 && !strcmp(argv[1], "-n"))
+ {
+ lock = 0;
+ i++;
+ }
+
+ if (argc < i + 2)
+ {
+ fprintf(stderr, "Usage: %s [-n] dbfile [dbfile ...]\n",
+ argv[0]);
+ exit(1);
+ }
+
+ data.data = &pos;
+ data.size = sizeof(pos);
+ key.data = framebuf;
+ key.size = 7; /* %07d */
+
+ while (++i < argc)
+ {
+ fprintf(stderr, "Building index for %s\n", argv[i]);
+
+ if ((fp = fopen(argv[i], "r")) == NULL)
+ {
+ perror(argv[i]);
+ continue;
+ }
+
+ /*
+ * Figure out the file's mode, uid, gid, so we can set the
+ * permissions on the index file to the same thing.
+ */
+ if (fstat(fileno(fp), &st))
+ {
+ sprintf(indname, "%s: fstat", argv[i]);
+ perror(indname);
+ fclose(fp);
+ continue;
+ }
+
+ if (lock && lockit(fileno(fp), F_WRLCK))
+ {
+ sprintf(indname, "%s: Warning: Couldn't lock", argv[i]);
+ perror(indname);
+ locked = 0;
+ }
+ else
+ locked = 1;
+
+ /*
+ * Create a database file.
+ */
+ bt.flags = R_DUP; /* allow duplicate keys */
+ bt.cachesize = 0;
+ bt.psize = 0;
+ bt.lorder = 4321;
+ bt.minkeypage = 0;
+ bt.compare = NULL; /* use lexical comparison */
+ bt.prefix = NULL; /* no prefix comparisons */
+
+ /* Index files have ".ind" extensions */
+ sprintf(indname, "%s.ind", argv[i]);
+ if ((db = dbopen(indname, O_CREAT | O_RDWR | O_TRUNC,
+ st.st_mode, DB_BTREE, &bt)) == NULL)
+ {
+ perror(indname);
+ if (locked)
+ lockit(fileno(fp), F_UNLCK);
+ fclose(fp);
+ continue;
+ }
+
+ /*
+ * Now loop through the text file, inserting a record into
+ * the index file for each "tracks" line.
+ */
+ while (! feof(fp))
+ {
+ pos = ftell(fp);
+ buf[0] = '\0';
+ if (fgets(buf, sizeof(buf), fp) == NULL || ! buf[0])
+ {
+ /* End of file? */
+ if (feof(fp))
+ break;
+
+ /* Nope. A read error. Unlink the database. */
+ perror(argv[i]);
+ (void) unlink(indname);
+ break;
+ }
+
+ if (strncmp(buf, "tracks ", 7))
+ continue;
+
+ /*
+ * Found the start of a record. Figure out the start
+ * time of the last track and put an entry in the
+ * index file with that as the key.
+ */
+ c = strrchr(buf, ' '); /* this will always succeed */
+ *c = '\0';
+ c = strrchr(buf, ' '); /* this should too, but... */
+ if (c == NULL)
+ {
+ fprintf(stderr,
+ "%s: Malformed tracks line at %lu\n",
+ argv[i], pos);
+ continue;
+ }
+ sscanf(c+1, "%d", &frame);
+ sprintf(framebuf, "%07d", frame);
+ pos = htonl(pos);
+
+ if ((db->put)(db, &key, &data, 0))
+ {
+ perror(indname);
+ unlink(indname);
+ break;
+ }
+ }
+
+ /*
+ * Clean up.
+ */
+ (void) (db->close)(db);
+ if (locked)
+ lockit(fileno(fp), F_UNLCK);
+ }
+}
+
+/*
+ * Lock a file. Time out after a little while if we can't get a lock;
+ * this usually means the locking system is broken.
+ *
+ * Unfortunately, if there are lots of people contending for a lock,
+ * this can result in the file not getting locked when it probably should.
+ */
+int
+lockit(fd, type)
+ int fd;
+ int type;
+{
+ struct flock fl;
+ int result, timer = 0;
+
+ fl.l_type = type;
+ fl.l_whence = 0;
+ fl.l_start = 0;
+ fl.l_len = 0;
+
+ while ((result = fcntl(fd, F_SETLK, &fl)) < 0)
+ {
+ if (errno != EACCES || errno != EAGAIN)
+ break;
+ if (timer++ == 30)
+ {
+ errno = ETIMEDOUT;
+ break;
+ }
+
+ sleep(1);
+ }
+
+ return (result);
+}
diff --git a/kscd/libwm/cdda.c b/kscd/libwm/cdda.c
new file mode 100644
index 00000000..ca8d76ba
--- /dev/null
+++ b/kscd/libwm/cdda.c
@@ -0,0 +1,458 @@
+/***************************************************************************
+ cdda.c - description
+ -------------------
+ begin : Mon Jan 27 2003
+ copyright : (C) 2003 by Alex Kern
+ email : alex.kern@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+/***************************************************************************
+ * *
+ * This is a common cddamaster piece of code *
+ * *
+ ***************************************************************************/
+
+#include <string.h>
+#include <sys/poll.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdda.h"
+#include "include/wm_cdrom.h"
+#include "audio/audio.h"
+#include <pthread.h>
+
+#if defined(BUILD_CDDA)
+
+static pthread_t thread_read;
+static pthread_t thread_play;
+
+int get_next_block(int x);
+void *cdda_fct_read(void* arg);
+void *cdda_fct_play(void* arg);
+
+#define NUMBLOCKS 2
+#define NUMFRAMES 10
+
+static struct cdda_block blks[NUMBLOCKS];
+static pthread_mutex_t blks_mutex[NUMBLOCKS];
+
+static struct cdda_device dev;
+static pthread_cond_t wakeup_audio;
+
+/*
+ * Loudness setting, plus the floating volume multiplier and decaying-average
+ * volume level.
+ */
+static unsigned int loudness = 0, volume = 32768, level;
+
+/*
+ * This is non-null if we're saving audio to a file.
+ */
+static FILE *output = NULL;
+
+/*
+ * These are driverdependent oops
+ *
+ */
+static struct audio_oops *oops = NULL;
+
+/*
+ * Audio file header format.
+ */
+typedef unsigned long u_32;
+struct auheader {
+ u_32 magic;
+ u_32 hdr_size;
+ u_32 data_size;
+ u_32 encoding;
+ u_32 sample_rate;
+ u_32 channels;
+};
+
+/* had to change #ifdef to #if -> see wm_cdda.h */
+#ifdef __FreeBSD__
+/* Phungus not with htonl on FreeBSD */
+#include <sys/param.h>
+#else
+#if WM_BIG_ENDIAN
+# ifndef htonl
+# define htonl(x) (x)
+# endif
+#else
+extern unsigned long htonl(unsigned long);
+#endif
+#endif
+
+/*
+ * Try to initialize the CDDA slave. Returns 0 on success.
+ */
+int
+gen_cdda_init( struct wm_drive *d )
+{
+ int ret = 0;
+
+ if (d->cdda_slave > -1)
+ return 0;
+
+ memset(&blks, 0, sizeof(blks));
+
+ dev.fd = -1;
+ dev.frames_at_once = NUMFRAMES;
+ dev.blocks = blks;
+ dev.numblocks = NUMBLOCKS;
+ dev.status = WM_CDM_UNKNOWN;
+ dev.devname = d->cd_device;
+
+ if ((ret = wmcdda_init(&dev)))
+ return ret;
+
+ oops = setup_soundsystem(d->soundsystem, d->sounddevice, d->ctldevice);
+ if (!oops) {
+ ERRORLOG("cdda: setup_soundsystem failed\n");
+ wmcdda_close(&dev);
+ return -1;
+ }
+
+ if(pthread_create(&thread_read, NULL, cdda_fct_read, &dev)) {
+ ERRORLOG("error by create pthread");
+ oops->wmaudio_close();
+ wmcdda_close(&dev);
+ return -1;
+ }
+
+ if(pthread_create(&thread_play, NULL, cdda_fct_play, &dev)) {
+ ERRORLOG("error by create pthread");
+ oops->wmaudio_close();
+ wmcdda_close(&dev);
+ return -1;
+ }
+ d->cdda_slave = 0;
+ return 0;
+}
+
+int
+cdda_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *frame, int *track, int *ind )
+{
+ if (d->cdda_slave > -1) {
+ if(dev.status)
+ *mode = dev.status;
+ else
+ *mode = oldmode;
+
+ if (*mode == WM_CDM_PLAYING) {
+ *track = dev.track;
+ *ind = dev.index;
+ *frame = dev.frame;
+ } else if (*mode == WM_CDM_CDDAERROR) {
+ /*
+ * An error near the end of the CD probably
+ * just means we hit the end.
+ */
+ *mode = WM_CDM_TRACK_DONE;
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_play( struct wm_drive *d, int start, int end, int realstart )
+{
+ if (d->cdda_slave > -1) {
+ dev.command = WM_CDM_STOPPED;
+
+ wmcdda_setup(start, end, realstart);
+
+ level = 2500;
+ volume = 1 << 15;
+
+ dev.track = -1;
+ dev.index = 0;
+ dev.frame = start;
+ dev.command = WM_CDM_PLAYING;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_pause( struct wm_drive *d )
+{
+ if (d->cdda_slave > -1) {
+ if(WM_CDM_PLAYING == dev.command) {
+ dev.command = WM_CDM_PAUSED;
+ } else {
+ dev.command = WM_CDM_PLAYING;
+ }
+
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_stop( struct wm_drive *d )
+{
+ if (d->cdda_slave > -1) {
+ dev.command = WM_CDM_STOPPED;
+ oops->wmaudio_stop();
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_eject( struct wm_drive *d )
+{
+ if (d->cdda_slave > -1) {
+ dev.command = WM_CDM_EJECTED;
+ oops->wmaudio_stop();
+ /*wmcdda_close(&dev);*/
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_set_volume( struct wm_drive *d, int left, int right )
+{
+ if (d->cdda_slave > -1) {
+ int bal, vol;
+
+ bal = (right - left) + 100;
+ bal *= 255;
+ bal /= 200;
+ if (right > left)
+ vol = right;
+ else
+ vol = left;
+ vol *= 255;
+ vol /= 100;
+
+ if(oops->wmaudio_balance)
+ oops->wmaudio_balance(bal);
+ if(oops->wmaudio_volume)
+ oops->wmaudio_volume(vol);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+cdda_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ if (d->cdda_slave > -1) {
+ if(!oops->wmaudio_state) {
+ dev.volume = -1;
+ dev.balance = 128;
+ }
+
+ *left = *right = (dev.volume * 100 + 254) / 255;
+
+ if (dev.balance < 110)
+ *right = (((dev.volume * dev.balance + 127) / 128) * 100 + 254) / 255;
+ else if (dev.balance > 146)
+ *left = (((dev.volume * (255 - dev.balance) + 127) / 128) * 100 + 254) / 255;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Turn off the CDDA slave.
+ */
+void
+cdda_kill( struct wm_drive *d )
+{
+ if (d->cdda_slave > -1) {
+ dev.command = WM_CDM_STOPPED;
+ oops->wmaudio_stop();
+ sleep(1);
+ wmcdda_close(&dev);
+ oops->wmaudio_close();
+
+ dev.blocks = NULL;
+ wait(NULL);
+ d->cdda_slave = -1;
+ }
+}
+
+/*
+ * Tell the CDDA slave to set the loudness level.
+ */
+void
+cdda_set_loudness( struct wm_drive *d, int loud )
+{
+ if (d->cdda_slave > -1) {
+ loudness = loud;
+ }
+}
+
+/*
+ * Tell the CDDA slave to start (or stop) saving to a file.
+ */
+void
+cdda_save( struct wm_drive *d, char *filename )
+{
+ #if 0
+ int len;
+
+ if (filename == NULL || filename[0] == '\0')
+ len = 0;
+ else
+ len = strlen(filename);
+ write(d->cdda_slave, "F", 1);
+ write(d->cdda_slave, &len, sizeof(len));
+ if (len)
+ write(d->cdda_slave, filename, len);
+
+
+ read(0, &namelen, sizeof(namelen));
+ if (output != NULL) {
+ fclose(output);
+ output = NULL;
+ }
+ if (namelen) {
+ filename = malloc(namelen + 1);
+ if (filename == NULL) {
+ perror("cddas");
+ wmcdda_close(dev);
+ oops->wmaudio_close();
+ exit(1);
+ }
+
+ read(0, filename, namelen);
+ filename[namelen] = '\0';
+ output = fopen(filename, "w");
+ if (output == NULL) {
+ perror(filename);
+ } else {
+ /* Write an .au file header. */
+ hdr.magic = htonl(0x2e736e64);
+ hdr.hdr_size = htonl(sizeof(hdr) + 28);
+ hdr.data_size = htonl(~0);
+ hdr.encoding = htonl(3); /* linear-16 */
+ hdr.sample_rate = htonl(44100);
+ hdr.channels = htonl(2);
+
+ fwrite(&hdr, sizeof(hdr), 1, output);
+ fwrite("Recorded from CD by WorkMan", 28, 1, output);
+ }
+ free(filename);
+
+#endif
+}
+
+int get_next_block(int x)
+{
+ int y = ++x;
+ return (y < NUMBLOCKS)?y:0;
+}
+
+void *cdda_fct_read(void* arg)
+{
+ struct cdda_device *cddadev = (struct cdda_device*)arg;
+ int i, j, wakeup;
+ long result;
+
+ while (cddadev->blocks) {
+ while(cddadev->command != WM_CDM_PLAYING) {
+ cddadev->status = cddadev->command;
+ sleep(1);
+ }
+
+ i = 0;
+ pthread_mutex_lock(&blks_mutex[i]);
+ wakeup = 1;
+
+ while(cddadev->command == WM_CDM_PLAYING) {
+
+ result = wmcdda_read(cddadev, &blks[i]);
+
+ if (result <= 0 && blks[i].status != WM_CDM_TRACK_DONE) {
+ ERRORLOG("cdda: wmcdda_read failed, stop playing\n");
+ cddadev->command = WM_CDM_STOPPED;
+ break;
+ } else {
+ if (output)
+ fwrite(blks[i].buf, blks[i].buflen, 1, output);
+ }
+
+ j = get_next_block(i);
+ pthread_mutex_lock(&blks_mutex[j]);
+
+ if(wakeup) {
+ wakeup = 0;
+ pthread_cond_signal(&wakeup_audio);
+ }
+
+ pthread_mutex_unlock(&blks_mutex[i]);
+ /* audio can start here */
+
+ i = j;
+ }
+
+ pthread_mutex_unlock(&blks_mutex[i]);
+ }
+
+ return 0;
+}
+
+void *cdda_fct_play(void* arg)
+{
+ struct cdda_device *cddadev = (struct cdda_device*)arg;
+ int i = 0;
+
+ while (cddadev->blocks) {
+ if(cddadev->command != WM_CDM_PLAYING) {
+ i = 0;
+ pthread_mutex_lock(&blks_mutex[i]);
+ pthread_cond_wait(&wakeup_audio, &blks_mutex[i]);
+ } else {
+ i = get_next_block(i);
+ pthread_mutex_lock(&blks_mutex[i]);
+ }
+
+ if (oops->wmaudio_play(&blks[i])) {
+ oops->wmaudio_stop();
+ ERRORLOG("cdda: wmaudio_play failed\n");
+ cddadev->command = WM_CDM_STOPPED;
+ }
+ cddadev->frame = blks[i].frame;
+ cddadev->track = blks[i].track;
+ cddadev->index = blks[i].index;
+ cddadev->status = blks[i].status;
+
+ pthread_mutex_unlock(&blks_mutex[i]);
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/kscd/libwm/cddaslave.c b/kscd/libwm/cddaslave.c
new file mode 100644
index 00000000..9a008b84
--- /dev/null
+++ b/kscd/libwm/cddaslave.c
@@ -0,0 +1,572 @@
+/*
+ * $id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ ******************************************************************
+ *
+ * Digital audio manipulator for WorkMan.
+ *
+ * The CDDA architecture looks like this:
+ *
+ * WorkMan (or another UI!)
+ * ^^^
+ * ||| (separate processes connected by pipe)
+ * vvv
+ * +------------- cddaslave -------------+
+ * | | |
+ * command module CDDA reader audio output
+ * (portable) (per platform) (per platform)
+ *
+ * This source file has the command module and some of the scaffolding
+ * to hold cddaslave together, plus some non-system-dependent audio
+ * processing code. Look in plat_*_cdda.c for system-specific stuff.
+ *
+ */
+
+#include "include/wm_cdda.h"
+
+#ifdef BUILD_CDDA
+
+static char cddaslave_id[] = "$Id$";
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "include/wm_struct.h"
+#include "include/wm_cdda.h"
+#include "include/wm_platform.h"
+#include "audio/audio.h"
+
+void send_status(struct cdda_block *);
+
+#define SEND_ACK(b); { (b)->status |= WMCDDA_ACK; send_status(b); }
+#define SEND_STATUS(b); { (b)->status &= ~WMCDDA_ACK; send_status(b); }
+#define SEND_STATUS_ACK(b, s); { (b)->status = ((s) | WMCDDA_ACK); send_status(b); }
+
+int receive_command(struct cdda_device *, struct cdda_block *);
+
+int playing = 0; /* Should the CD be playing now? */
+int pausing = 0;
+
+/*
+ * Loudness setting, plus the floating volume multiplier and decaying-average
+ * volume level.
+ */
+int loudness = 0;
+unsigned int volume = 32768;
+unsigned int level;
+
+/*
+ * Playback speed (0 = slow)
+ */
+int speed = 128;
+
+/*
+ * This is non-null if we're saving audio to a file.
+ */
+FILE *output = NULL;
+
+/*
+ * These are driverdependent oops
+ *
+ */
+struct audio_oops *oops = NULL;
+
+/*
+ * Audio file header format.
+ */
+typedef unsigned long u_32;
+struct auheader {
+ u_32 magic;
+ u_32 hdr_size;
+ u_32 data_size;
+ u_32 encoding;
+ u_32 sample_rate;
+ u_32 channels;
+};
+
+/* had to change #ifdef to #if -> see wm_cdda.h */
+#ifdef __FreeBSD__
+/* Phungus not with htonl on FreeBSD */
+#include <sys/param.h>
+#else
+#if WM_BIG_ENDIAN
+# ifndef htonl
+# define htonl(x) (x)
+# endif
+#else
+extern unsigned long htonl(x);
+#endif
+#endif
+
+void *malloc();
+long cdda_transform();
+
+/*
+ * Send status information upstream.
+ */
+void
+send_status(struct cdda_block *blk)
+{
+ DEBUGLOG("send_status, send %i(%s | %s)\n", blk->status,
+ gen_status(blk->status & WMCDDA_ACK), gen_status(blk->status & ~WMCDDA_ACK));
+ write(1, blk, sizeof(*blk));
+}
+
+/*
+ * Accept a command from our master.
+ *
+ * The protocol is byte-oriented:
+ * PmsfMSFxyz Play from msf to MSF (MSF can be 0,0,0 to play to end)
+ * xyz is the msf of the start of this chunk, i.e., the
+ * ending boundary for reverse play.
+ * S Stop.
+ * E Eject. This means we just close the CD device and
+ * open it again later.
+ * Q Quit.
+ * Vn Set volume level (0-255).
+ * Bn Set balance level (0-255).
+ * EnL Set an equalizer level (n = 0 for bass, 255 for treble)
+ * G Get current status.
+ * sn Set speed multiplier to n.
+ * dn Set direction to forward (n = 0) or reverse.
+ * Fllllx... Start saving to a file (length = l, followed by name)
+ * F0000 Stop saving.
+ * Ln Set loudness level (0-255).
+ * A Pause/Resume
+ * I Get status, current frame
+ */
+int
+receive_command(struct cdda_device *dev, struct cdda_block* blk)
+{
+ unsigned char inbuf[10];
+ char *filename;
+ int namelen;
+ struct auheader hdr;
+
+ if (read(0, inbuf, 1) <= 0) {
+ wmcdda_close(dev);
+ oops->wmaudio_close();
+/* ERRORLOG("cddaslave: parent died, exit\n");*/
+ exit(0);
+ }
+
+ DEBUGLOG("cddaslave: CMD %c\n", inbuf[0]);
+
+ switch (inbuf[0]) {
+ case 'I':
+ if(dev->fd < 0) wmcdda_init(dev, blk);
+ SEND_ACK(blk);
+ break;
+ case 'A': /* pause/resume */
+ if(WMCDDA_PLAYING & blk->status) {
+ oops->wmaudio_stop();
+ SEND_STATUS_ACK(blk, WMCDDA_PAUSED);
+ } else if (WMCDDA_PAUSED & blk->status) {
+ SEND_STATUS_ACK(blk, WMCDDA_PLAYING);
+ } else {
+ SEND_ACK(blk);
+ }
+ break;
+ case 'E':
+ oops->wmaudio_stop();
+ wmcdda_close(dev);
+ SEND_ACK(blk);
+ break;
+ case 'P':
+ read(0, inbuf, 9);
+
+ wmcdda_setup(inbuf[0] * 60 * 75 + inbuf[1] * 75 + inbuf[2],
+ inbuf[3] * 60 * 75 + inbuf[4] * 75 + inbuf[5],
+ inbuf[6] * 60 * 75 + inbuf[7] * 75 + inbuf[8]);
+
+ level = 2500;
+ volume = 1 << 15;
+
+ blk->track = -1;
+ blk->index = 0;
+ blk->minute = inbuf[6];
+ blk->second = inbuf[7];
+ blk->frame = inbuf[8];
+ SEND_STATUS_ACK(blk, WMCDDA_PLAYING);
+ break;
+ case 'S':
+ oops->wmaudio_stop();
+ SEND_STATUS_ACK(blk, WMCDDA_STOPPED);
+ break;
+ case 'B':
+ read(0, inbuf, 1);
+ if(oops->wmaudio_balance)
+ oops->wmaudio_balance(inbuf[0]);
+ break;
+ case 'V':
+ read(0, inbuf, 1);
+ if(oops->wmaudio_balance)
+ oops->wmaudio_volume(inbuf[0]);
+ break;
+ case 'G':
+ SEND_ACK(blk);
+ if(!oops->wmaudio_state || oops->wmaudio_state(blk) == -1) {
+ blk->volume = -1;
+ blk->balance = 128;
+ }
+ send_status(blk);
+ break;
+ case 'Q':
+ SEND_ACK(blk);
+ wmcdda_close(dev);
+ oops->wmaudio_close();
+ exit(0);
+/*
+ case 's':
+ read(0, inbuf, 1);
+ speed = inbuf[0];
+ wmcdda_speed(speed);
+ SEND_STATUS(blk, WMCDDA_ACK);
+ break;
+
+ case 'd':
+ read(0, inbuf, 1);
+ wmcdda_direction(inbuf[0]);
+ SEND_STATUS(blk, WMCDDA_ACK);
+ break;
+*/
+ case 'L':
+ read(0, inbuf, 1);
+ loudness = inbuf[0];
+ SEND_ACK(blk);
+ break;
+ case 'F':
+ read(0, &namelen, sizeof(namelen));
+ if (output != NULL) {
+ fclose(output);
+ output = NULL;
+ }
+ if (namelen) {
+ filename = malloc(namelen + 1);
+ if (filename == NULL) {
+ perror("cddaslave");
+ wmcdda_close(dev);
+ oops->wmaudio_close();
+ exit(1);
+ }
+
+ read(0, filename, namelen);
+ filename[namelen] = '\0';
+ output = fopen(filename, "w");
+ if (output == NULL)
+ perror(filename);
+ else {
+ /* Write an .au file header. */
+ hdr.magic = htonl(0x2e736e64);
+ hdr.hdr_size = htonl(sizeof(hdr) + 28);
+ hdr.data_size = htonl(~0);
+ hdr.encoding = htonl(3); /* linear-16 */
+ hdr.sample_rate = htonl(44100);
+ hdr.channels = htonl(2);
+
+ fwrite(&hdr, sizeof(hdr), 1, output);
+ fwrite("Recorded from CD by WorkMan", 28, 1, output);
+ }
+ free(filename);
+ }
+ SEND_ACK(blk);
+ }
+
+ return(dev->fd);
+}
+
+
+/*
+ * Transform some CDDA data.
+ */
+long
+wmcdda_transform(unsigned char *rawbuf, long buflen, struct cdda_block *block)
+{
+ long i;
+ long *buf32 = (long *)rawbuf;
+ register short *buf16 = (short *)rawbuf;
+ register int aval;
+
+ /*
+ * Loudness transformation. Basically this is a self-adjusting
+ * volume control; our goal is to keep the average output level
+ * around a certain value (2500 seems to be pleasing.) We do this
+ * by maintaining a decaying average of the recent output levels
+ * (where "recent" is some fraction of a second.) All output levels
+ * are multiplied by the inverse of the decaying average; this has
+ * the volume-leveling effect we desire, and isn't too CPU-intensive.
+ *
+ * This is done by modifying the digital data, rather than adjusting
+ * the system volume control, because (at least on some systems)
+ * tweaking the system volume can generate little pops and clicks.
+ *
+ * There's probably a more elegant way to achieve this effect, but
+ * what the heck, I never took a DSP class and am making this up as
+ * I go along, with a little help from some friends.
+ *
+ * This is all done with fixed-point math, oriented around powers
+ * of two, which with luck will keep the CPU usage to a minimum.
+ * More could probably be done, for example using lookup tables to
+ * replace multiplies and divides; whether the memory hit (128K
+ * for each table) is worthwhile is unclear.
+ */
+ if (loudness)
+ {
+ /* We aren't really going backwards, but i > 0 is fast */
+ for (i = buflen / 2; i > 0; i--)
+ {
+ /*
+ * Adjust this sample to the current level.
+ */
+ aval = (*buf16 = (((long)*buf16) * volume) >> 15);
+ buf16++;
+
+ /*
+ * Don't adjust the decaying average for each sample;
+ * that just spends CPU time for very little benefit.
+ */
+ if (i & 127)
+ continue;
+
+ /*
+ * We want to use absolute values to compute the
+ * decaying average; otherwise it'd sit around 0.
+ */
+ if (aval < 0)
+ aval = -aval;
+
+ /*
+ * Adjust more quickly when we start hitting peaks,
+ * or we'll get clipping when there's a sudden loud
+ * section after lots of quiet.
+ */
+ if (aval & ~8191)
+ aval <<= 3;
+
+ /*
+ * Adjust the decaying average.
+ */
+ level = ((level << 11) - level + aval) >> 11;
+
+ /*
+ * Let *really* quiet sounds play softly, or we'll
+ * amplify background hiss to full volume and blast
+ * the user's speakers when real sound starts up.
+ */
+ if (! (level & ~511))
+ level = 512;
+
+ /*
+ * And adjust the volume setting using the inverse
+ * of the decaying average.
+ */
+ volume = (2500 << 15) / level;
+ }
+ }
+
+ if (speed == 128)
+ return (buflen);
+
+ /*
+ * Half-speed play. Stretch things out.
+ */
+ if (speed == 0)
+ {
+ buflen /= 2; /* Since we're moving 32 bits at a time */
+
+ for (i = buflen - 1; i > 0; i--)
+ {
+ buf32[i] = buf32[i / 2];
+ }
+
+ buflen *= 4; /* 2 for doubling the buffer, 2 from above */
+ }
+
+ /*
+ * Slow play; can't optimize it as well as half-speed.
+ */
+ if (speed && speed < 128)
+ {
+ int multiplier = ((speed + 128) * 128) / 256;
+ int newlen;
+ int tally = 0, pos;
+
+ buflen /= 4; /* Get the number of 32-bit values */
+
+ /*
+ * Buffer length doubles when speed is 0, stays the same
+ * when speed is 128.
+ */
+ newlen = (buflen * 128) / multiplier;
+
+ pos = buflen - 1;
+ for (i = newlen - 1; i > 0; i--)
+ {
+ buf32[i] = buf32[pos];
+ tally += multiplier;
+ if (tally & 128)
+ {
+ pos--;
+ tally ^= 128;
+ }
+ }
+
+ buflen = newlen * 4;
+ }
+
+ return (buflen);
+}
+
+
+int main(int argc, char **argv)
+{
+ fd_set readfd, dummyfd;
+ struct timeval timeout;
+ struct cdda_block blockinfo;
+ long result;
+ int nfds;
+ struct cdda_device dev;
+ const char *sondsystem;
+ const char *sounddevice;
+ const char *sounddevicectl;
+
+ memset(&blockinfo, 0, sizeof(struct cdda_block));
+
+ /*
+ * Device name should be the command-line argument.
+ */
+ if (argc < 2)
+ dev.devname = NULL;
+ else
+ dev.devname = argv[1];
+
+ if (argc < 3)
+ sondsystem = "arts";
+ else
+ sondsystem = argv[2];
+
+ if (argc < 4)
+ sounddevice = NULL;
+ else
+ sounddevice = argv[3];
+
+ if (argc < 5)
+ sounddevicectl = NULL;
+ else
+ sounddevicectl = argv[3];
+
+ DEBUGLOG("cddaslave: called with %s %s %s %s\n",
+ dev.devname?dev.devname:"",
+ sondsystem?sondsystem:"",
+ sounddevice?sounddevice:"",
+ sounddevicectl?sounddevicectl:"");
+
+ /*
+ * If we're running setuid root, bump up our priority then lose
+ * superuser access.
+ */
+ nice(-14);
+ setgid(getgid());
+ setuid(getuid());
+ if (geteuid() != getuid())
+ return 255;
+
+ FD_ZERO(&dummyfd);
+ FD_ZERO(&readfd);
+
+ timerclear(&timeout);
+
+ dev.fd = -1;
+ wmcdda_init(&dev, &blockinfo);
+
+ oops = setup_soundsystem(sondsystem, sounddevice, sounddevicectl);
+ if (!oops) {
+ ERRORLOG("cddaslave: setup_soundsystem failed\n");
+ exit(1);
+ }
+
+ DEBUGLOG("cddaslave: sent first ACK\n");
+ SEND_ACK(&blockinfo);
+
+ /*
+ * Accept commands as they come in, and play some sound if we're
+ * supposed to be doing that.
+ */
+ while (1) {
+ FD_SET(0, &readfd);
+
+ /*
+ * If we're playing, we don't want select to block. Otherwise,
+ * wait a little while for the next command.
+ */
+ if (playing)
+ timeout.tv_usec = 0;
+ else
+ timeout.tv_usec = 500000;
+
+ nfds = select(1, &readfd, &dummyfd, &dummyfd, &timeout);
+
+ if (nfds < 0) { /* Broken pipe; our GUI exited. */
+ wmcdda_close(&dev);
+ oops->wmaudio_close();
+ exit(0);
+ }
+
+ if (FD_ISSET(0, &readfd)) {
+ receive_command(&dev, &blockinfo);
+ /*
+ * Process all commands in rapid succession, rather
+ * than possibly waiting for a CDDA read.
+ */
+ continue;
+ }
+
+ if ((blockinfo.status & ~WMCDDA_ACK) == WMCDDA_PLAYING) {
+ result = wmcdda_read(&dev, &blockinfo);
+ if (result <= 0 && blockinfo.status != WMCDDA_DONE) {
+ ERRORLOG("cddaslave: wmcdda_read failed\n");
+ blockinfo.status = WMCDDA_STOPPED;
+ send_status(&blockinfo);
+ } else {
+ result = wmcdda_normalize(&dev, &blockinfo);
+ if (output)
+ fwrite(dev.buf, result, 1, output);
+
+ if (oops->wmaudio_play(dev.buf, dev.buflen, &blockinfo)) {
+ oops->wmaudio_stop();
+ ERRORLOG("cddaslave: wmaudio_play failed\n");
+ blockinfo.status = WMCDDA_STOPPED;
+ send_status(&blockinfo);
+ }
+ }
+ } else
+ send_status(&blockinfo);
+ }
+
+ return 0;
+}
+
+#endif /* BUILD_CDDA */
diff --git a/kscd/libwm/cddb.c b/kscd/libwm/cddb.c
new file mode 100644
index 00000000..657ab6a4
--- /dev/null
+++ b/kscd/libwm/cddb.c
@@ -0,0 +1,588 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * establish connection to cddb server and get data
+ * socket stuff gotten from gnu port of the finger command
+ *
+ * provided by Sven Oliver Moll
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cddb.h"
+
+struct wm_cddb cddb;
+
+extern struct wm_cdinfo thiscd;
+static int cur_cddb_protocol;
+static char *cur_cddb_server;
+static char *cur_cddb_mail_adress;
+static char *cur_cddb_path_to_cgi;
+static char *cur_cddb_proxy_server;
+
+/* local prototypes */
+int cddb_sum(int);
+char *string_split(char *line, char delim);
+void string_makehello(char *line, char delim);
+int connect_open(void);
+void connect_close(void);
+void connect_getline(char *line);
+void connect_read_entry(void);
+void cddbp_send(const char *line);
+void cddbp_read(char *category, unsigned int id);
+void http_send(char* line);
+void http_read(char *category, unsigned int id);
+void cddb_request(void);
+/* local prototypes END */
+
+static int Socket;
+static FILE *Connection;
+
+/*
+ *
+ */
+void
+cddb_cur2struct(void)
+{
+ cddb.protocol = cur_cddb_protocol;
+ strcpy(cddb.cddb_server, cur_cddb_server);
+ strcpy(cddb.mail_adress, cur_cddb_mail_adress);
+ strcpy(cddb.path_to_cgi, cur_cddb_path_to_cgi);
+ strcpy(cddb.proxy_server, cur_cddb_proxy_server);
+} /* cddb_cur2struct() */
+
+/*
+ *
+ */
+void
+cddb_struct2cur(void)
+{
+ cur_cddb_protocol = cddb.protocol;
+ cur_cddb_server = (cddb.cddb_server);
+ cur_cddb_mail_adress = (cddb.mail_adress);
+ cur_cddb_path_to_cgi = (cddb.path_to_cgi);
+ cur_cddb_proxy_server = (cddb.proxy_server);
+} /* cddb_struct2cur() */
+
+
+/*
+ * Subroutine from cddb_discid
+ */
+int
+cddb_sum(int n)
+{
+ char buf[12],
+ *p;
+ int ret = 0;
+
+ /* For backward compatibility this algorithm must not change */
+ sprintf(buf, "%lu", (unsigned long)n);
+ for (p = buf; *p != '\0'; p++)
+ ret += (*p - '0');
+
+ return (ret);
+} /* cddb_sum() */
+
+
+/*
+ * Calculate the discid of a CD according to cddb
+ */
+unsigned long
+cddb_discid(void)
+{
+ int i,
+ t,
+ n = 0;
+
+ /* For backward compatibility this algorithm must not change */
+ for (i = 0; i < thiscd.ntracks; i++) {
+
+ n += cddb_sum(thiscd.trk[i].start / 75);
+ /*
+ * Just for demonstration (See below)
+ *
+ * t += (thiscd.trk[i+1].start / 75) -
+ * (thiscd.trk[i ].start / 75);
+ */
+ }
+
+ /*
+ * Mathematics can be fun. Example: How to reduce a full loop to
+ * a simple statement. The discid algorhythm is so half-hearted
+ * developed that it doesn't even use the full 32bit range.
+ * But it seems to be always this way: The bad standards will be
+ * accepted, the good ones turned down.
+ * Boy, you pulled out the /75. This is not correct here, because
+ * this calculation needs the integer division for both .start
+ * fields.
+ */
+
+ t = (thiscd.trk[thiscd.ntracks].start / 75)
+ - (thiscd.trk[0].start / 75);
+ return ((n % 0xff) << 24 | t << 8 | thiscd.ntracks);
+} /* cddb_discid() */
+
+/*
+ * Split a string into two components according to the first occurrence of
+ * the delimiter.
+ */
+char *
+string_split(char *line, char delim)
+{
+ char *p1;
+
+ for (p1=line;*p1;p1++)
+ {
+ if(*p1 == delim)
+ {
+ *p1 = 0;
+ return ++p1;
+ }
+ }
+ return (NULL);
+} /* string_split() */
+
+/*
+ * Generate the hello string according to the cddb protocol
+ * delimiter is either ' ' (cddbp) or '+' (http)
+ */
+void
+string_makehello(char *line,char delim)
+{
+ char mail[84],*host;
+
+ strcpy(mail,cddb.mail_adress);
+ host=string_split(mail,'@');
+
+ sprintf(line,"%shello%c%s%c%s%c%s%c%s",
+ delim == ' ' ? "cddb " : "&",
+ delim == ' ' ? ' ' : '=',
+ mail,delim,
+ host,delim,
+ WORKMAN_NAME,delim,
+ WORKMAN_VERSION);
+} /* string_makehello() */
+
+/*
+ * Open the TCP connection to the cddb/proxy server
+ */
+int
+connect_open(void)
+{
+ char *host;
+ struct hostent *hp;
+ struct sockaddr_in soc_in;
+ int port;
+
+ if(cddb.protocol == 3) /* http proxy */
+ host = wm_strdup(cddb.proxy_server);
+ else
+ host = wm_strdup(cddb.cddb_server);
+ /*
+ * t=string_split(host,':');
+ */
+ port=atoi(string_split(host,':'));
+ if(!port)
+ port=8880;
+
+#ifndef NDEBUG
+ printf("%s:%d\n",host,port);
+#endif
+ hp =gethostbyname(host);
+
+ if (hp == NULL)
+ {
+ static struct hostent def;
+ static struct in_addr defaddr;
+ static char *alist[1];
+ static char namebuf[128];
+ int inet_addr();
+
+ defaddr.s_addr = inet_addr(host);
+ if ((int) defaddr.s_addr == -1)
+ {
+#ifndef NDEBUG
+ printf("unknown host: %s\n", host);
+#endif
+ return (-1);
+ }
+ strcpy(namebuf, host);
+ def.h_name = namebuf;
+ def.h_addr_list = alist, def.h_addr = (char *)&defaddr;
+ def.h_length = sizeof (struct in_addr);
+ def.h_addrtype = AF_INET;
+ def.h_aliases = 0;
+ hp = &def;
+ }
+ soc_in.sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, (char *)&soc_in.sin_addr, hp->h_length);
+ soc_in.sin_port = htons(port);
+ Socket = socket(hp->h_addrtype, SOCK_STREAM, 0);
+ if (Socket < 0)
+ {
+ perror("socket");
+ return (-1);
+ }
+ fflush(stdout);
+ if (connect(Socket, (struct sockaddr *)&soc_in, sizeof (soc_in)) < 0)
+ {
+ perror("connect");
+ close(Socket);
+ return (-1);
+ }
+
+ Connection = fdopen(Socket, "r");
+ return (0);
+} /* connect_open() */
+
+
+/*
+ * Close the connection
+ */
+void
+connect_close(void)
+{
+ (void)fclose(Connection);
+ close(Socket);
+} /* connect_close() */
+
+/*
+ * Get a line from the connection with CR and LF stripped
+ */
+void
+connect_getline(char *line)
+{
+ char c;
+
+ while ((c = getc(Connection)) != '\n')
+ {
+ *line = c;
+ if ((c != '\r') && (c != (char)0xff))
+ line++;
+ }
+ *line=0;
+} /* connect_getline() */
+
+/*
+ * Read the CD data from the server and place them into the cd struct
+ */
+void
+connect_read_entry(void)
+{
+ char type;
+ int trknr;
+
+ char *t,*t2,tempbuf[2000];
+
+ while(strcmp(tempbuf,"."))
+ {
+ connect_getline(tempbuf);
+
+ t=string_split(tempbuf,'=');
+ if(t != NULL)
+ {
+ type=tempbuf[0];
+
+ if(strncmp("TITLE",tempbuf+1,5))
+ continue;
+
+ if('D' == type)
+ {
+ /*
+ * Annahme: "Interpret / Titel" ist falsch.
+ * Daher: NULL-String erwarten.
+ */
+ t2=string_split(t,'/');
+ if(t2 == NULL)
+ t2 = t;
+ if(*t2 == ' ')
+ t2++;
+ strncpy(cd->cdname,t2,sizeof(cd->cdname)-1);
+ cd->cdname[sizeof(cd->cdname)-1]='\0';
+
+ for(t2=t;*t2;t2++)
+ {
+ if((*t2 == ' ') && (*(t2+1) == 0))
+ *t2=0;
+ }
+ strncpy(cd->artist,t,sizeof(cd->artist)-1);
+ cd->artist[sizeof(cd->artist)-1]='\0';
+ }
+
+ if('T' == type)
+ {
+ trknr=atoi(tempbuf+6);
+ /*
+ * printf("Track %d:%s\n",trknr,t);
+ */
+ wm_strmcpy(&cd->trk[trknr].songname,t);
+ }
+ /*
+ * fprintf(stderr, "%s %s\n",tempbuf,t);
+ */
+ }
+ }
+} /* connect_read_entry() */
+
+/*
+ * Send a command to the server using cddbp
+ */
+void
+cddbp_send(const char *line)
+{
+ write(Socket, line, strlen(line));
+ write(Socket, "\n", 1);
+} /* cddbp_send() */
+
+/*
+ * Send the "read from cddb" command to the server using cddbp
+ */
+void
+cddbp_read(char *category, unsigned int id)
+{
+ char tempbuf[84];
+ sprintf(tempbuf, "cddb read %s %08x", category, id);
+ cddbp_send(tempbuf);
+} /* cddbp_read() */
+
+/*
+ * Send a command to the server using http
+ */
+void
+http_send(char* line)
+{
+ char tempbuf[2000];
+
+ write(Socket, "GET ", 4);
+#ifndef NDEBUG
+ printf("GET ");
+#endif
+ if(cddb.protocol == 3)
+ {
+ write(Socket, "http://", 7);
+ write(Socket, cddb.cddb_server, strlen(cddb.cddb_server));
+#ifndef NDEBUG
+ printf("http://%s",cddb.cddb_server);
+#endif
+ }
+ write(Socket, cddb.path_to_cgi, strlen(cddb.path_to_cgi));
+ write(Socket, "?cmd=" ,5);
+ write(Socket, line, strlen(line));
+#ifndef NDEBUG
+ printf("%s?cmd=%s",cddb.path_to_cgi,line);
+#endif
+ string_makehello(tempbuf,'+');
+ write(Socket, tempbuf, strlen(tempbuf));
+#ifndef NDEBUG
+ printf("%s",tempbuf);
+#endif
+ write(Socket, "&proto=1 HTTP/1.0\n\n", 19);
+#ifndef NDEBUG
+ printf("&proto=1 HTTP/1.0\n");
+#endif
+ do
+ connect_getline(tempbuf);
+ while(strcmp(tempbuf,""));
+} /* http_send() */
+
+/*
+ * Send the "read from cddb" command to the server using http
+ */
+void
+http_read(char *category, unsigned int id)
+{
+ char tempbuf[84];
+ sprintf(tempbuf, "cddb+read+%s+%08x", category, id);
+ http_send(tempbuf);
+} /* http_read() */
+
+/*
+ * The main routine called from the ui
+ */
+void
+cddb_request(void)
+{
+ int i;
+ char tempbuf[2000];
+ extern int cur_ntracks;
+
+ int status;
+ char category[21];
+ unsigned int id;
+
+ strcpy(cddb.cddb_server,"localhost:888");
+ strcpy(cddb.mail_adress,"svolli@bigfoot.com");
+ /*
+ * cddb.protocol = 1;
+ */
+ wipe_cdinfo();
+
+ switch(cddb.protocol)
+ {
+ case 1: /* cddbp */
+#ifndef NDEBUG
+ printf("USING CDDBP\n");
+ printf("open\n");
+#endif
+ connect_open();
+ connect_getline(tempbuf);
+#ifndef NDEBUG
+ printf("[%s]\n",tempbuf);
+#endif
+ /*
+ * if(atoi(tempbuf) == 201) return;
+ */
+
+ /*
+ * strcpy(tempbuf,"cddb hello svolli bigfoot.com Eierkratzer eins");
+ */
+ string_makehello(tempbuf,' ');
+#ifndef NDEBUG
+ fprintf(stderr, "%s\n", tempbuf);
+#endif
+ cddbp_send(tempbuf);
+ connect_getline(tempbuf);
+#ifndef NDEBUG
+ printf("[%s]\n",tempbuf);
+ printf("query\n");
+#endif
+ sprintf(tempbuf, "cddb query %08x %d",thiscd.cddbid,thiscd.ntracks);
+ for (i = 0; i < cur_ntracks; i++)
+ if (thiscd.trk[i].section < 2)
+ sprintf(tempbuf + strlen(tempbuf), " %d",
+ thiscd.trk[i].start);
+ sprintf(tempbuf + strlen(tempbuf), " %d\n", thiscd.length);
+#ifndef NDEBUG
+ printf(">%s<\n",tempbuf);
+#endif
+ cddbp_send(tempbuf);
+ connect_getline(tempbuf);
+#ifndef NDEBUG
+ printf("[%s]\n",tempbuf);
+#endif
+ status=atoi(tempbuf);
+ /*
+ * fprintf(stderr, "status:%d\n",status);
+ * fprintf(stderr,"category:%s\n",category);
+ * fprintf(stderr,"id:%s\n",id);
+ */
+ if(status == 200) /* Exact match */
+ {
+ sscanf(tempbuf,"%d %20s %08x",&status,category,&id);
+ cddbp_read(category,id);
+ connect_read_entry();
+ }
+
+ if(status == 211) /* Unexact match, multiple possible
+ * Hack: always use first. */
+ {
+ connect_getline(tempbuf);
+ sscanf(tempbuf,"%20s %08x",category,&id);
+ while(strcmp(tempbuf,"."))
+ connect_getline(tempbuf);
+ cddbp_read(category,id);
+ connect_read_entry();
+ }
+
+ cddbp_send("quit");
+ connect_close();
+#ifndef NDEBUG
+ printf("close\n");
+#endif
+ break;
+ case 2: /* http */
+ case 3: /* http proxy */
+#ifndef NDEBUG
+ printf("USING HTTP%s\n",
+ (cddb.protocol == 3) ? " WITH PROXY" : "");
+ printf("query\n");
+#endif
+ sprintf(tempbuf, "cddb+query+%08x+%d",thiscd.cddbid,thiscd.ntracks);
+ for (i = 0; i < cur_ntracks; i++)
+ if (thiscd.trk[i].section < 2)
+ sprintf(tempbuf + strlen(tempbuf), "+%d",
+ thiscd.trk[i].start);
+ sprintf(tempbuf + strlen(tempbuf), "+%d", thiscd.length);
+#ifndef NDEBUG
+ printf(">%s<\n",tempbuf);
+#endif
+ connect_open();
+ http_send(tempbuf);
+ connect_getline(tempbuf);
+#ifndef NDEBUG
+ printf("[%s]\n",tempbuf);
+#endif
+ status=atoi(tempbuf);
+ /*
+ * fprintf(stderr, "status:%d\n",status);
+ * fprintf(stderr, "category:%s\n",category);
+ * fprintf(stderr, "id:%s\n",id);
+ */
+
+ if(status == 200) /* Exact match */
+ {
+ connect_close();
+ connect_open();
+ sscanf(tempbuf,"%d %20s %08x",&status,category,&id);
+ http_read(category,id);
+ connect_read_entry();
+ }
+
+ if(status == 211) /* Unexact match, multiple possible
+ * Hack: always use first. */
+ {
+ connect_getline(tempbuf);
+ sscanf(tempbuf,"%20s %08x",category,&id);
+ while(strcmp(tempbuf,"."))
+ connect_getline(tempbuf);
+ connect_close();
+ connect_open();
+ http_read(category,id);
+ connect_read_entry();
+ }
+ /* moved close above break */
+ connect_close();
+ break;
+ default: /* off */
+ break;
+ }
+} /* cddb_request() */
+
diff --git a/kscd/libwm/cdinfo.c b/kscd/libwm/cdinfo.c
new file mode 100644
index 00000000..b04dec69
--- /dev/null
+++ b/kscd/libwm/cdinfo.c
@@ -0,0 +1,890 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Get information about a CD.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "include/wm_config.h"
+
+#include "include/wm_struct.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_database.h"
+#include "include/wm_helpers.h"
+
+struct wm_play *playlist = NULL;
+struct wm_cdinfo thiscd, *cd = &thiscd;
+
+int cur_track = -1; /* Current track number, starting at 1 */
+int cur_index = 0; /* Current index mark */
+int cur_lasttrack = 999; /* Last track to play in current chunk */
+int cur_firsttrack = 0; /* First track of current chunk */
+int cur_pos_abs; /* Current absolute position in seconds */
+int cur_frame; /* Current frame number */
+int cur_pos_rel; /* Current track-relative position in seconds */
+int cur_tracklen; /* Length in seconds of current track */
+int cur_cdlen; /* Length in seconds of entire CD */
+int cur_ntracks; /* Number of tracks on CD (= tracks + sections) */
+int cur_nsections; /* Number of sections currently defined */
+
+int cur_listno; /* Current index into the play list, if playing */
+char * cur_artist; /* Name of current CD's artist */
+char * cur_cdname; /* Album name */
+char * cur_trackname; /* Take a guess */
+char cur_contd; /* Continued flag */
+char cur_avoid; /* Avoid flag */
+
+int exit_on_eject = 0;
+
+int cur_stopmode = -1;
+int info_modified;
+
+/*
+ * insert_trackinfo()
+ *
+ * Add a new track to the CD info structure. Pass the position of the new
+ * entry in the track list -- 0 will make this the first track, 1 the second,
+ * etc. The new entry will be zeroed out.
+ */
+static void
+insert_trackinfo(int num)
+{
+ struct wm_trackinfo *newtrk;
+
+ /* Easy case: the list is empty */
+ if (cd->trk == NULL) {
+ if ((cd->trk = (struct wm_trackinfo *) calloc(1,
+ sizeof(*newtrk))) == NULL)
+ {
+ perror("insert_trackinfo");
+ exit(1);
+ } else {
+ return;
+ } /* if() else */
+ } /* if() */
+ /* Stick the new entry in cd->trk[]. */
+ if ((newtrk = (struct wm_trackinfo *) malloc(sizeof(*newtrk) *
+ (cur_ntracks + 1))) == NULL)
+ {
+ perror("insert_trackinfo");
+ exit(1);
+ }
+
+ if (num)
+ memcpy(newtrk, cd->trk, sizeof(*newtrk) * num);
+ memset(&newtrk[num], 0, sizeof(*newtrk));
+ if (num < cur_ntracks)
+ memcpy(&newtrk[num + 1], &cd->trk[num], sizeof(*newtrk) *
+ (cur_ntracks - num));
+
+ free(cd->trk);
+ cd->trk = newtrk;
+}
+
+/*
+ * split_trackinfo()
+ *
+ * Split a track in two at a particular position (absolute, in frames). All
+ * internal data structures and variables will be adjusted to the new
+ * numbering scheme. Pass in the track number (>=1) to split, which is also
+ * the index into cd->trk[] of the new entry.
+ *
+ * If pos is within 1 second of the start of another track, the split fails.
+ *
+ * Returns 1 on success, 0 if the track couldn't be inserted.
+ *
+ * Note: updating user interface elements is up to the caller.
+ */
+int
+split_trackinfo( int pos )
+{
+ int i, l, num;
+
+ if (pos < cd->trk[0].start)
+ return (0);
+
+ /* First find the appropriate track. */
+ for (num = 0; num < cur_ntracks; num++)
+ if (cd->trk[num].start - 75 < pos &&
+ cd->trk[num].start + 75 > pos)
+ return (0);
+ else if (cd->trk[num].start > pos)
+ break;
+ if (num == 0)
+ return (0);
+
+ /* Insert the new entry into the track array. */
+ insert_trackinfo(num);
+
+ /* Update the easy variables. */
+ if (cur_track > num)
+ cur_track++;
+ if (cur_firsttrack > num)
+ cur_firsttrack++;
+ if (cur_lasttrack > num)
+ cur_lasttrack++;
+
+ /* Update the user-defined playlists. */
+ if (cd->lists != NULL)
+ for (l = 0; cd->lists[l].name != NULL; l++)
+ if (cd->lists[l].list != NULL)
+ for (i = 0; cd->lists[l].list[i]; i++)
+ if (cd->lists[l].list[i] > num)
+ cd->lists[l].list[i]++;
+
+ /* Update the internal playlist. */
+ if (playlist != NULL)
+ for (i = 0; playlist[i].start; i++)
+ {
+ if (playlist[i].start > num)
+ playlist[i].start++;
+ if (playlist[i].end > num)
+ playlist[i].end++;
+ }
+
+ /* Now adjust the information in cd->trk[]. */
+ cd->trk[num].start = pos;
+ if (num == cur_ntracks)
+ cd->trk[num].length = cur_cdlen - pos / 75;
+ else
+ cd->trk[num].length = (cd->trk[num + 1].start - pos) / 75;
+ cd->trk[num - 1].length -= cd->trk[num].length;
+ if (cur_track == num)
+ cur_tracklen -= cd->trk[num].length;
+ cd->trk[num].track = cd->trk[num - 1].track;
+ cd->trk[num].data = cd->trk[num - 1].data;
+ cd->trk[num].contd = 1;
+ cd->trk[num].volume = cd->trk[num - 1].volume;
+
+ if (cd->trk[num - 1].section == 0)
+ cd->trk[num - 1].section = 1;
+ cd->trk[num].section = cd->trk[num - 1].section + 1;
+
+ cur_ntracks++;
+ cur_nsections++;
+
+ for (i = num + 1; i < cur_ntracks; i++)
+ if (cd->trk[i].track == cd->trk[num].track)
+ cd->trk[i].section++;
+
+ return (1);
+}
+
+/*
+ * remove_trackinfo()
+ *
+ * Remove a track's internal data. This is similar to split_trackinfo()
+ * above, but simpler. A track's initial section can't be removed. Track
+ * numbers start at 0.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+int
+remove_trackinfo( int num )
+{
+ int i, l;
+
+ if (num < 1 || num >= cur_ntracks || cd->trk[num].section < 2)
+ return (0);
+
+ cd->trk[num - 1].length += cd->trk[num].length;
+
+ for (i = num; i < cur_ntracks - 1; i++)
+ memcpy(&cd->trk[i], &cd->trk[i + 1], sizeof(cd->trk[0]));
+
+ if (cur_track > num)
+ cur_track--;
+ if (cur_firsttrack > num)
+ cur_firsttrack--;
+ if (cur_lasttrack > num)
+ cur_lasttrack--;
+
+ /* Update the user-defined playlists. */
+ if (cd->lists != NULL)
+ for (l = 0; cd->lists[l].name != NULL; l++)
+ if (cd->lists[l].list != NULL)
+ for (i = 0; cd->lists[l].list[i]; i++)
+ if (cd->lists[l].list[i] > num)
+ cd->lists[l].list[i]--;
+
+ /* Update the internal playlist. */
+ if (playlist != NULL)
+ for (i = 0; playlist[i].start; i++)
+ {
+ if (playlist[i].start > num)
+ playlist[i].start--;
+ if (playlist[i].end > num)
+ playlist[i].end--;
+ }
+
+ cur_ntracks--;
+ cur_nsections--;
+
+ /*
+ * Update the section numbers for this track. If this is the only
+ * user-created section in a track, get rid of the section number
+ * in the track's entry.
+ */
+ if (num == cur_ntracks || cd->trk[num - 1].track != cd->trk[num].track)
+ {
+ if (cd->trk[num - 1].section == 1)
+ cd->trk[num - 1].section = 0;
+ }
+ else
+ for (i = num; i < cur_ntracks; i++)
+ if (cd->trk[i].track == cd->trk[num - 1].track)
+ cd->trk[i].section--;
+
+ return (1);
+}
+
+/*
+ * listentry()
+ *
+ * Return a scrolling list entry.
+ */
+char *
+listentry( int num )
+{
+ static char buf[600];
+ const char *name;
+ char tracknum[20];
+ int digits;
+ int sdigits;
+
+ if (num >= 0 && num < cur_ntracks)
+ {
+
+/*
+ if (big_spaces)
+ {
+ digits = 2;
+ sdigits = cur_nsections < 9 ? -1 : -2;
+ }
+ else
+ {
+ digits = cd->trk[num].track < 10 ? 3 : 2;
+ sdigits = cur_nsections < 9 ? -1 : -3;
+ }
+*/
+
+ digits = 2;
+ sdigits = cur_nsections < 9 ? -1 : -2;
+
+ name = cd->trk[num].songname ? cd->trk[num].songname : "";
+
+ if (cur_nsections)
+ {
+ if (cd->trk[num].section > 9)
+ {
+ sprintf(tracknum, "%*d.%d", digits,
+ cd->trk[num].track,
+ cd->trk[num].section);
+ } else {
+ if (cd->trk[num].section)
+ {
+ sprintf(tracknum, "%*d.%*d", digits,
+ cd->trk[num].track, sdigits,
+ cd->trk[num].section);
+ } else {
+ sprintf(tracknum, "%*d%*s", digits,
+ cd->trk[num].track,
+ 2 - sdigits, " ");
+/* 2 - sdigits - big_spaces, " ");*/
+ }
+ }
+ } else {
+ sprintf(tracknum, "%*d", digits, cd->trk[num].track);
+ }
+
+ if (cd->trk[num].data)
+ {
+ sprintf(buf, "%s) %3dMB %s", tracknum,
+ cd->trk[num].length / 1024, name);
+ } else {
+ sprintf(buf, "%s) %02d:%02d %s", tracknum,
+ cd->trk[num].length / 60,
+ cd->trk[num].length % 60, name);
+ }
+
+ return (buf);
+ } else {
+ return (NULL);
+ }
+} /* listentry() */
+
+/*
+ * trackname()
+ *
+ * Return a track's name.
+ */
+const char *
+trackname( int num )
+{
+ if (num >= 0 && num < cur_ntracks)
+ {
+ if (cd->trk[num].songname)
+ {
+ return (cd->trk[num].songname);
+ } else {
+ return ("");
+ }
+ } else {
+ return (NULL);
+ }
+} /* trackname() */
+
+/*
+ * tracklen()
+ *
+ * Return a track's length in seconds.
+ */
+int
+tracklen( int num )
+{
+ if (cd != NULL && num >= 0 && num < cur_ntracks)
+ return (cd->trk[num].length);
+ else
+ return (0);
+}
+
+/*
+ * get_default_volume()
+ *
+ * Return the default volume (0-32, 0=none) for the CD or a track.
+ */
+int
+get_default_volume( int track )
+{
+ if (! track)
+ return (cd->volume);
+ else if (track <= cur_ntracks)
+ return (cd->trk[track - 1].volume);
+ else
+ return (0);
+}
+
+/*
+ * get_contd()
+ *
+ * Return the contd value for a track.
+ */
+int
+get_contd( int num )
+{
+ if (num >= 0 && num < cur_ntracks)
+ return (cd->trk[num].contd);
+ else
+ return (0);
+}
+
+/*
+ * get_avoid()
+ *
+ * Return the avoid value for a track.
+ */
+int
+get_avoid( int num )
+{
+ if (num >= 0 && num < cur_ntracks)
+ return (cd->trk[num].avoid);
+ else
+ return (0);
+}
+
+/*
+ * get_autoplay()
+ *
+ * Is autoplay set on this disc?
+ */
+int
+get_autoplay( void )
+{
+ return ( cd->autoplay );
+}
+
+/*
+ * get_playmode()
+ *
+ * Return the default playmode for the CD.
+ */
+int
+get_playmode( void )
+{
+ return ( cd->playmode );
+}
+
+/*
+ * get_runtime()
+ *
+ * Return the total running time for the current playlist in seconds.
+ */
+int
+get_runtime( void )
+{
+ int i;
+
+ if (playlist == NULL || playlist[0].start == 0 || cur_firsttrack == -1)
+ return (cd == NULL ? 0 : cd->length);
+
+ for (i = 0; playlist[i].start; i++)
+ ;
+
+ return (playlist[i].starttime);
+}
+
+/*
+ * default_volume()
+ *
+ * Set the default volume for the CD or a track.
+ */
+void
+default_volume( int track, int vol )
+{
+ if (track == 0)
+ cd->volume = vol;
+ else if (track <= cur_ntracks)
+ cd->trk[track - 1].volume = vol;
+}
+
+/*
+ * Play the next thing on the playlist, if any.
+ */
+void
+play_next_entry( int forward )
+{
+ if (cd == NULL)
+ return;
+ if (playlist != NULL && playlist[cur_listno].start)
+ {
+ wm_cd_play(playlist[cur_listno].start, 0,
+ playlist[cur_listno].end);
+ cur_listno++;
+ }
+ else
+ wm_cd_stop();
+}
+
+/*
+ * Play the next track, following playlists as necessary.
+ */
+void
+play_next_track( int forward )
+{
+ if (cd == NULL)
+ return;
+
+ /* Is the current playlist entry done? Move on, if so. */
+ if (playlist == NULL || cur_track + 1 == playlist[cur_listno - 1].end)
+ play_next_entry( forward );
+ else
+ wm_cd_play(cur_track + 1, 0, playlist[cur_listno - 1].end);
+}
+
+/*
+ * Play the previous track, hopping around the playlist as necessary.
+ */
+void
+play_prev_track( int forward )
+{
+ if (cd == NULL)
+ return;
+
+ if (playlist == NULL)
+ return;
+
+ /* If we're in the middle of this playlist entry, go back one track */
+ if (cur_track > playlist[cur_listno - 1].start)
+ wm_cd_play(cur_track - 1, 0, playlist[cur_listno - 1].end);
+ else
+ if (cur_listno > 1)
+ {
+ cur_listno--;
+ wm_cd_play(playlist[cur_listno - 1].end - 1, 0,
+ playlist[cur_listno - 1].end);
+ }
+ else
+ wm_cd_play(playlist[0].start, 0, playlist[0].end);
+}
+
+/*
+ * stash_cdinfo(artist, cdname)
+ */
+void
+stash_cdinfo(char *artist, char *cdname, int autoplay, int playmode )
+{
+ if (cd != NULL)
+ {
+ if (strcmp(cd->artist, artist))
+ info_modified = 1;
+ strncpy(cd->artist, artist,sizeof(cd->artist)-1);
+ cd->artist[sizeof(cd->artist)-1]='\0';
+
+ if (strcmp(cd->cdname, cdname))
+ info_modified = 1;
+ strncpy(cd->cdname, cdname,sizeof(cd->cdname)-1);
+ cd->cdname[sizeof(cd->cdname)-1]='\0';
+
+ if (!!cd->autoplay != !!autoplay)
+ info_modified = 1;
+ cd->autoplay = autoplay;
+
+ if (!!cd->playmode != !!playmode)
+ info_modified = 1;
+ cd->playmode = playmode;
+ }
+} /* stash_cdinfo() */
+
+/*
+ * wipe_cdinfo()
+ *
+ * Clear out all a CD's soft information (presumably in preparation for
+ * reloading from the database.)
+ */
+void
+wipe_cdinfo( void )
+{
+ struct wm_playlist *l;
+ int i;
+
+ if (cd != NULL)
+ {
+ cd->artist[0] = cd->cdname[0] = '\0';
+ cd->autoplay = cd->playmode = cd->volume = 0;
+ cd->whichdb = NULL;
+ freeup(&cd->otherrc);
+ freeup(&cd->otherdb);
+
+ if (thiscd.lists != NULL)
+ {
+ for (l = thiscd.lists; l->name != NULL; l++)
+ {
+ free(l->name);
+ free(l->list);
+ }
+ free(thiscd.lists);
+ thiscd.lists = NULL;
+ }
+
+ for (i = 0; i < cur_ntracks; i++)
+ {
+ freeup(&cd->trk[i].songname);
+ freeup(&cd->trk[i].otherrc);
+ freeup(&cd->trk[i].otherdb);
+ cd->trk[i].avoid = cd->trk[i].contd = 0;
+ cd->trk[i].volume = 0;
+ if (cd->trk[i].section > 1)
+ remove_trackinfo(i--);
+ }
+ }
+}
+
+/*
+ * stash_trkinfo(track, songname, contd, avoid)
+ *
+ * Update information about a track on the current CD.
+ */
+void
+stash_trkinfo( int track, char *songname, int contd, int avoid )
+{
+ if (cd != NULL)
+ {
+ track--;
+ if (!!cd->trk[track].contd != !!contd)
+ info_modified = 1;
+ cd->trk[track].contd = track ? contd : 0;
+
+ if (!!cd->trk[track].avoid != !!avoid)
+ info_modified = 1;
+ cd->trk[track].avoid = avoid;
+
+ if ((cd->trk[track].songname == NULL && songname[0]) ||
+ (cd->trk[track].songname != NULL &&
+ strcmp(cd->trk[track].songname, songname)))
+ {
+ info_modified = 1;
+ wm_strmcpy(&cd->trk[track].songname, songname);
+ }
+ }
+}
+
+/*
+ * new_playlist()
+ *
+ * Add a playlist to a CD.
+ */
+struct wm_playlist *
+new_playlist(struct wm_cdinfo* cdinfo, char* listname)
+{
+ int nlists = 0;
+ struct wm_playlist *l;
+
+ if (cdinfo->lists != NULL)
+ {
+ for (nlists = 0; cdinfo->lists[nlists].name != NULL; nlists++)
+ ;
+ l = (struct wm_playlist *)realloc(cdinfo->lists, (nlists + 2) *
+ sizeof (struct wm_playlist));
+ }
+ else
+ l = (struct wm_playlist *)malloc(2 * sizeof (struct wm_playlist));
+
+ if (l == NULL)
+ return (NULL);
+
+ l[nlists + 1].name = NULL;
+ l[nlists].name = NULL; /* so wm_strmcpy doesn't free() it */
+ wm_strmcpy(&l[nlists].name, listname);
+ l[nlists].list = NULL;
+ cdinfo->lists = l;
+
+ return (&l[nlists]);
+}
+
+/*
+ * make_playlist()
+ *
+ * Construct a playlist for the current CD. If we're in shuffle mode, play
+ * each non-avoided track once, keeping continued tracks in the right order.
+ *
+ * If playmode is 2, use playlist number (playmode-2). XXX should do
+ * bounds checking on this, probably.
+ *
+ * If consecutive tracks are being played, only make one playlist entry for
+ * them, so the CD player won't pause between tracks while we wake up.
+ */
+void
+make_playlist( int playmode, int starttrack )
+{
+ int i, avoiding = 1, entry = 0, count, track,
+ *thislist;
+
+ cur_listno = 0;
+ if (playlist != NULL)
+ free(playlist);
+ playlist = malloc(sizeof (*playlist) * (cur_ntracks + 1));
+ if (playlist == NULL)
+ {
+ perror("playlist");
+ exit(1);
+ }
+
+ /* If this is a data-only CD, we can't play it. */
+ if ((starttrack && cd->trk[starttrack - 1].data) ||
+ (cur_ntracks == 1 && cd->trk[0].data))
+ {
+ playlist[0].start = 0;
+ playlist[0].end = 0;
+ playlist[1].start = 0;
+ return;
+ }
+
+ if (playmode == 1)
+ {
+ char *done = malloc(cur_ntracks);
+
+ if (done == NULL)
+ {
+ perror("randomizer");
+ exit(1);
+ }
+
+ count = cur_ntracks;
+ if (starttrack && cd->trk[starttrack - 1].avoid)
+ count++;
+ for (i = 0; i < cur_ntracks; i++)
+ if (cd->trk[i].contd || cd->trk[i].avoid ||
+ cd->trk[i].data)
+ {
+ done[i] = 1;
+ count--;
+ }
+ else
+ done[i] = 0;
+
+ for (i = 0; i < count; i++)
+ {
+ int end; /* for readability */
+ if (starttrack)
+ {
+ track = starttrack - 1;
+ starttrack = 0;
+ }
+ else
+ while (done[track = rand() % cur_ntracks])
+ ;
+
+ playlist[i].start = track + 1;
+
+ /* play all subsequent continuation tracks too */
+ for (end = track + 1; end < cur_ntracks + 1; end++)
+ if (! cd->trk[end].contd ||
+ cd->trk[end].avoid ||
+ cd->trk[end].data)
+ break;
+ playlist[i].end = end + 1;
+
+ done[track]++;
+ }
+ playlist[i].start = 0;
+
+ free(done);
+ }
+ else if (playmode >= 2 && cd->lists && cd->lists[playmode - 2].name)
+ {
+ count = 2; /* one terminating entry, and one for start */
+ thislist = cd->lists[playmode - 2].list;
+
+ for (i = 0; thislist[i]; i++)
+ if (thislist[i + 1] != thislist[i] + 1)
+ count++;
+
+ if (playlist != NULL)
+ free(playlist);
+ playlist = malloc(sizeof (*playlist) * count);
+ if (playlist == NULL)
+ {
+ perror("playlist");
+ exit(1);
+ }
+
+ count = 0;
+ if (starttrack)
+ {
+ playlist[0].start = starttrack;
+ for (track = 0; thislist[track]; track++)
+ if (starttrack == thislist[track])
+ break;
+ if (! thislist[track])
+ {
+ playlist[0].end = starttrack + 1;
+ playlist[1].start = thislist[0];
+ count = 1;
+ track = 0;
+ }
+ }
+ else
+ {
+ playlist[0].start = thislist[0];
+ track = 0;
+ }
+
+ for (i = track; thislist[i]; i++)
+ if (thislist[i + 1] != thislist[i] + 1)
+ {
+ playlist[count].end = thislist[i] + 1;
+ count++;
+ playlist[count].start = thislist[i + 1];
+ }
+ }
+ else
+ {
+ for (i = starttrack ? starttrack - 1 : 0; i < cur_ntracks; i++)
+ if (avoiding && ! (cd->trk[i].avoid || cd->trk[i].data))
+ {
+ playlist[entry].start = i + 1;
+ avoiding = 0;
+ }
+ else if (! avoiding && (cd->trk[i].avoid ||
+ cd->trk[i].data))
+ {
+ playlist[entry].end = i + 1;
+ avoiding = 1;
+ entry++;
+ }
+ if (! avoiding)
+ playlist[entry].end = i + 1;
+ playlist[entry + !avoiding].start = 0;
+ }
+
+ /*
+ * Now go through the list, whatever its source, and figure out
+ * cumulative starting times for each entry.
+ */
+ entry = count = 0;
+ do {
+ playlist[entry].starttime = count;
+
+ if (playlist[entry].start)
+ for (i = playlist[entry].start; i <
+ playlist[entry].end; i++)
+ count += cd->trk[i - 1].length;
+ } while (playlist[entry++].start);
+}
+
+/*
+ * Find a particular track's location in the current playlist. Sets the
+ * appropriate variables (cur_listno, cur_firsttrack, cur_lasttrack).
+ */
+void
+pl_find_track( int track )
+{
+ int i;
+
+ if (playlist == NULL)
+ {
+#ifndef NDEBUG
+ fprintf(stderr, "Null playlist! Huh?\n");
+#endif
+ return;
+ }
+
+ for (i = 0; playlist[i].start; i++)
+ if (track >= playlist[i].start && track < playlist[i].end)
+ {
+ cur_listno = i + 1;
+ cur_firsttrack = playlist[i].start;
+ cur_lasttrack = playlist[i].end - 1;
+ return;
+ }
+
+ /*
+ * Couldn't find the track in question. Make a special entry with
+ * just that track.
+ */
+ if (! playlist[i].start)
+ {
+ playlist = realloc(playlist, (i + 2) * sizeof(*playlist));
+ if (playlist == NULL)
+ {
+ perror("playlist realloc");
+ exit(1);
+ }
+
+ playlist[i + 1].start = playlist[i + 1].end = 0;
+ playlist[i + 1].starttime = playlist[i].starttime +
+ cd->trk[track - 1].length;
+ playlist[i].start = track;
+ playlist[i].end = track + 1;
+ cur_listno = i + 1;
+ cur_firsttrack = track;
+ cur_lasttrack = track;
+ }
+}
diff --git a/kscd/libwm/cdrom.c b/kscd/libwm/cdrom.c
new file mode 100644
index 00000000..980ce3ce
--- /dev/null
+++ b/kscd/libwm/cdrom.c
@@ -0,0 +1,899 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Interface between most of WorkMan and the low-level CD-ROM library
+ * routines defined in plat_*.c and drv_*.c. The goal is to have no
+ * platform- or drive-dependent code here.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+/* #include <sys/time.h> */
+
+#include "config.h"
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cddb.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_database.h"
+#include "include/wm_platform.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_cdtext.h"
+
+#ifdef CAN_CLOSE
+#include <fcntl.h>
+#endif
+
+/* local prototypes */
+int read_toc(void);
+
+#define WM_MSG_CLASS WM_MSG_CLASS_CDROM
+
+/* extern struct wm_drive generic_proto, toshiba_proto, sony_proto; */
+/* toshiba33_proto; <=== Somehow, this got lost */
+
+/*
+ * The supported drive types are listed here. NULL means match anything.
+ * The first match in the list is used, and substring matches are done (so
+ * put long names before their shorter prefixes.)
+ */
+struct drivelist {
+ const char *ven;
+ const char *mod;
+ const char *rev;
+ struct wm_drive_proto *proto;
+} drives[] = {
+{ "TOSHIBA", "XM-3501", NULL, &toshiba_proto },
+{ "TOSHIBA", "XM-3401", NULL, &toshiba_proto },
+{ "TOSHIBA", "XM-3301", NULL, &toshiba_proto },
+{ "SONY", "CDU-8012", NULL, &sony_proto },
+{ "SONY", "CDU 561", NULL, &sony_proto },
+{ "SONY", "CDU-76S", NULL, &sony_proto },
+{ WM_STR_GENVENDOR, WM_STR_GENMODEL, WM_STR_GENREV, &generic_proto },
+{ NULL, NULL, NULL, &generic_proto }
+};
+
+/*
+ * Solaris 2.2 will remove the device out from under us. Getting an ENOENT
+ * is therefore sometimes not a problem.
+ */
+int intermittent_dev = 0;
+
+static int wm_cd_cur_balance = 10;
+static int wm_cur_cdmode = WM_CDM_UNKNOWN;
+static char *wm_cd_device = NULL; /* do not use this extern */
+
+static struct wm_drive drive = {
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ -1,
+ -1,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ NULL
+};
+
+extern struct wm_cdinfo thiscd;
+
+/*
+ * Macro magic
+ *
+ */
+#define FREE(x); if(x) free(x); x = NULL;
+
+#define STRDUP(old, neu); \
+ if(old) free(old); old = NULL;\
+ if(neu) old = strdup(neu);
+
+#define CARRAY(id) ((id)-1)
+
+#define DATATRACK 1
+/*
+ * init the workmanlib
+ */
+int wm_cd_init( int cdin, const char *cd_device, const char *soundsystem,
+ const char *sounddevice, const char *ctldevice )
+{
+ drive.cdda = (WM_CDDA == cdin);
+#if !defined(BUILD_CDDA)
+ if(drive.cdda) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "Libworkman library was compiled without cdda support\n");
+ return -1;
+ }
+#endif
+ wm_cd_destroy();
+
+ STRDUP(wm_cd_device, cd_device);
+ drive.cd_device = wm_cd_device;
+ STRDUP(drive.soundsystem, soundsystem);
+ STRDUP(drive.sounddevice, sounddevice);
+ STRDUP(drive.ctldevice, ctldevice);
+
+ return wm_cd_status();
+}
+
+int wm_cd_destroy( void )
+{
+ free_cdtext();
+
+ if(drive.fd != -1) {
+ /* first free one old */
+ if(drive.proto && drive.proto->gen_close)
+ drive.proto->gen_close(&drive);
+ else
+ close(drive.fd);
+ }
+ drive.fd = -1;
+ FREE(wm_cd_device);
+ FREE(drive.soundsystem);
+ FREE(drive.sounddevice);
+ FREE(drive.ctldevice);
+ FREE(drive.vendor);
+ FREE(drive.model);
+ FREE(drive.revision);
+ drive.proto = NULL;
+
+ return 0;
+}
+/*
+ * Give information about the drive we found during wmcd_open()
+ */
+const char *wm_drive_vendor( void )
+{
+ return drive.vendor?drive.vendor:"";
+}
+
+const char *wm_drive_model( void )
+{
+ return drive.model?drive.model:"";
+}
+
+const char *wm_drive_revision( void )
+{
+ return drive.revision?drive.revision:"";
+}
+
+const char *wm_drive_device( void )
+{
+ return drive.cd_device ? drive.cd_device : "";
+}
+
+/*
+ * Figure out which prototype drive structure we should be using based
+ * on the vendor, model, and revision of the current drive.
+ */
+int
+find_drive_struct(const char *vendor, const char *model, const char *rev)
+{
+ struct drivelist *d;
+
+ for (d = drives; d; d++) {
+ if(((d->ven != NULL) && strncmp(d->ven, vendor, strlen(d->ven))) ||
+ ((d->mod != NULL) && strncmp(d->mod, model, strlen(d->mod))) ||
+ ((d->rev != NULL) && strncmp(d->rev, rev, strlen(d->rev))))
+ continue;
+
+ if(!(d->proto))
+ goto fail;
+
+ STRDUP(drive.vendor, vendor);
+ STRDUP(drive.model, model);
+ STRDUP(drive.revision, rev);
+
+ drive.proto = d->proto;
+ return 0;
+ }
+
+fail:
+ return -1;
+} /* find_drive_struct() */
+
+/*
+ * read_toc()
+ *
+ * Read the table of contents from the CD. Return a pointer to a wm_cdinfo
+ * struct containing the relevant information (minus artist/cdname/etc.)
+ * This is a static struct. Returns NULL if there was an error.
+ *
+ * XXX allocates one trackinfo too many.
+ */
+int
+read_toc( void )
+{
+ struct wm_playlist *l;
+ int i;
+ int pos;
+
+ if(!drive.proto)
+ return -1;
+
+ if(drive.proto && drive.proto->gen_get_trackcount &&
+ (drive.proto->gen_get_trackcount)(&drive, &thiscd.ntracks) < 0) {
+ return -1 ;
+ }
+
+ thiscd.artist[0] = thiscd.cdname[0] = '\0';
+ thiscd.whichdb = thiscd.otherrc = thiscd.otherdb = thiscd.user = NULL;
+ thiscd.length = 0;
+ thiscd.autoplay = thiscd.playmode = thiscd.volume = 0;
+
+ /* Free up any left-over playlists. */
+ if (thiscd.lists != NULL) {
+ for (l = thiscd.lists; l->name != NULL; l++) {
+ free(l->name);
+ free(l->list);
+ }
+ free(thiscd.lists);
+ thiscd.lists = NULL;
+ }
+
+ if (thiscd.trk != NULL)
+ free(thiscd.trk);
+
+ thiscd.trk = malloc((thiscd.ntracks + 1) * sizeof(struct wm_trackinfo));
+ if (thiscd.trk == NULL) {
+ perror("malloc");
+ return -1;
+ }
+
+ for (i = 0; i < thiscd.ntracks; i++) {
+ if(drive.proto && drive.proto->gen_get_trackinfo &&
+ (drive.proto->gen_get_trackinfo)(&drive, i + 1, &thiscd.trk[i].data,
+ &thiscd.trk[i].start) < 0) {
+ return -1;
+ }
+
+ thiscd.trk[i].avoid = thiscd.trk[i].data;
+ thiscd.trk[i].length = thiscd.trk[i].start / 75;
+
+ thiscd.trk[i].songname = thiscd.trk[i].otherrc =
+ thiscd.trk[i].otherdb = NULL;
+ thiscd.trk[i].contd = 0;
+ thiscd.trk[i].volume = 0;
+ thiscd.trk[i].track = i + 1;
+ thiscd.trk[i].section = 0;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "track %i, start frame %i\n",
+ thiscd.trk[i].track, thiscd.trk[i].start);
+ }
+
+ if(drive.proto && drive.proto->gen_get_cdlen &&
+ (drive.proto->gen_get_cdlen)(&drive, &thiscd.trk[i].start) < 0) {
+ return -1;
+ }
+ thiscd.trk[i].length = thiscd.trk[i].start / 75;
+
+/* Now compute actual track lengths. */
+ pos = thiscd.trk[0].length;
+ for (i = 0; i < thiscd.ntracks; i++) {
+ thiscd.trk[i].length = thiscd.trk[i+1].length - pos;
+ pos = thiscd.trk[i+1].length;
+ if (thiscd.trk[i].data)
+ thiscd.trk[i].length = (thiscd.trk[i + 1].start - thiscd.trk[i].start) * 2;
+ if (thiscd.trk[i].avoid)
+ wm_strmcpy(&thiscd.trk[i].songname, "DATA TRACK");
+ }
+
+ thiscd.length = thiscd.trk[thiscd.ntracks].length;
+ thiscd.cddbid = cddb_discid();
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "read_toc() successful\n");
+ return 0;
+} /* read_toc() */
+
+/*
+ * wm_cd_status()
+ *
+ * Return values:
+ * see wm_cdrom.h
+ *
+ * Updates variables.
+ */
+int
+wm_cd_status( void )
+{
+ static int oldmode = WM_CDM_UNKNOWN;
+ int mode, err, tmp;
+
+ if(!drive.proto) {
+ oldmode = WM_CDM_UNKNOWN;
+ err = wmcd_open( &drive );
+ if (err < 0) {
+ wm_cur_cdmode = WM_CDM_UNKNOWN;
+ return err;
+ }
+ }
+
+ if(drive.proto && drive.proto->gen_get_drive_status &&
+ (drive.proto->gen_get_drive_status)(&drive, oldmode, &mode, &cur_frame,
+ &(thiscd.curtrack), &cur_index) < 0) {
+ perror("WM gen_get_drive_status");
+ return -1;
+ } else {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS,
+ "gen_get_drive_status returns status %s, track %i, frame %i\n",
+ gen_status(mode), thiscd.curtrack, cur_frame);
+ }
+
+ if(WM_CDS_NO_DISC(oldmode) && WM_CDS_DISC_READY(mode)) {
+ /* device changed */
+ thiscd.ntracks = 0;
+ if(read_toc() || 0 == thiscd.ntracks)
+ {
+ close(drive.fd);
+ drive.fd = -1;
+ mode = WM_CDM_NO_DISC;
+ }
+ else /* refresh cdtext info */
+ get_glob_cdtext(&drive, 1);
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "device status changed() from %s to %s\n",
+ gen_status(oldmode), gen_status(mode));
+ }
+ oldmode = mode;
+
+ /*
+ * it seems all driver have'nt state for stop
+ */
+ if(WM_CDM_PAUSED == mode && 0 == cur_frame) {
+ mode = WM_CDM_STOPPED;
+ thiscd.curtrack = 0;
+ }
+
+ switch (mode) {
+ case WM_CDM_PLAYING:
+ case WM_CDM_PAUSED:
+ cur_pos_abs = cur_frame / 75;
+ /* search for right track */
+ for(tmp = thiscd.ntracks;
+ tmp > 1 && cur_frame < thiscd.trk[CARRAY(tmp)].start;
+ tmp--)
+ ;
+ thiscd.curtrack = tmp;
+ /* Fall through */
+
+
+ case WM_CDM_UNKNOWN:
+ if (mode == WM_CDM_UNKNOWN)
+ {
+ mode = WM_CDM_NO_DISC;
+ cur_lasttrack = cur_firsttrack = -1;
+ }
+ /* Fall through */
+
+ case WM_CDM_STOPPED:
+ if(thiscd.curtrack >= 1 && thiscd.curtrack <= thiscd.ntracks && thiscd.trk != NULL) {
+ cur_trackname = thiscd.trk[CARRAY(thiscd.curtrack)].songname;
+ cur_avoid = thiscd.trk[CARRAY(thiscd.curtrack)].avoid;
+ cur_contd = thiscd.trk[CARRAY(thiscd.curtrack)].contd;
+ cur_pos_rel = (cur_frame - thiscd.trk[CARRAY(thiscd.curtrack)].start) / 75;
+ if (cur_pos_rel < 0)
+ cur_pos_rel = -cur_pos_rel;
+ }
+ if((playlist != NULL) && playlist[0].start & (cur_listno > 0)) {
+ cur_pos_abs -= thiscd.trk[playlist[CARRAY(cur_listno)].start - 1].start / 75;
+ cur_pos_abs += playlist[CARRAY(cur_listno)].starttime;
+ }
+ if (cur_pos_abs < 0)
+ cur_pos_abs = cur_frame = 0;
+
+ if (thiscd.curtrack < 1)
+ thiscd.curtracklen = thiscd.length;
+ else
+ thiscd.curtracklen = thiscd.trk[CARRAY(thiscd.curtrack)].length;
+ /* Fall through */
+
+ case WM_CDM_TRACK_DONE:
+ wm_cur_cdmode = mode;
+ break;
+ case WM_CDM_FORWARD:
+ case WM_CDM_EJECTED:
+ wm_cur_cdmode = mode;
+ break;
+ }
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS,
+ "wm_cd_status returns %s\n", gen_status(wm_cur_cdmode));
+ return wm_cur_cdmode;
+}
+
+int
+wm_cd_getcurtrack( void )
+{
+ if(WM_CDS_NO_DISC(wm_cur_cdmode))
+ return 0;
+ return thiscd.curtrack;
+}
+
+int
+wm_cd_getcurtracklen( void )
+{
+ if(WM_CDS_NO_DISC(wm_cur_cdmode))
+ return 0;
+
+ return thiscd.curtracklen;
+}
+
+int
+wm_cd_getcountoftracks( void )
+{
+ if(WM_CDS_NO_DISC(wm_cur_cdmode))
+ return 0;
+
+ return thiscd.ntracks;
+}
+
+int wm_cd_gettracklen( int track )
+{
+ if (track < 1 ||
+ track > thiscd.ntracks ||
+ thiscd.trk == NULL)
+ return 0;
+
+ return thiscd.trk[CARRAY(track)].length;
+}
+
+/*
+ * wm_cd_play(starttrack, pos, endtrack)
+ *
+ * Start playing the CD or jump to a new position. "pos" is in seconds,
+ * relative to start of track.
+ */
+int
+wm_cd_play( int start, int pos, int end )
+{
+ int real_start, real_end, status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status) || thiscd.ntracks < 1)
+ return -1;
+
+ /*
+ * check ranges
+ */
+ for(real_end = thiscd.ntracks; (thiscd.trk[CARRAY(real_end)].data == DATATRACK); real_end--)
+ ;
+ for(real_start = 1; (thiscd.trk[CARRAY(real_start)].data == DATATRACK); real_start++)
+ ;
+
+ if(end == WM_ENDTRACK) end = real_end;
+ if(end > real_end) end = real_end;
+
+ /*
+ * handle as overrun
+ */
+ if(start < real_start) start = real_start;
+ if(start > real_end) start = real_end;
+
+ /*
+ * Try to avoid mixed mode and CD-EXTRA data tracks
+ */
+ if(start > end || thiscd.trk[CARRAY(start)].data == DATATRACK) {
+ wm_cd_stop();
+ return -1;
+ }
+
+ cur_firsttrack = start;
+ cur_lasttrack = end;
+
+ wm_cd_play_chunk(thiscd.trk[CARRAY(start)].start + pos * 75, end == thiscd.ntracks ?
+ thiscd.length * 75 : thiscd.trk[CARRAY(end)].start - 1, thiscd.trk[CARRAY(start)].start);
+ /* So we don't update the display with the old frame number */
+ wm_cd_status();
+
+ return thiscd.curtrack;
+}
+
+/*
+ * wm_cd_play_chunk(start, end)
+ *
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+wm_cd_play_chunk( int start, int end, int realstart )
+{
+ int status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return -1;
+
+ end--;
+ if (start >= end)
+ start = end-1;
+
+ if(!(drive.proto) || !(drive.proto->gen_play)) {
+ perror("WM gen_play: function pointer NULL");
+ return -1;
+ }
+
+ return (drive.proto->gen_play)(&drive, start, end, realstart);
+}
+
+/*
+ * Set the offset into the current track and play. -1 means end of track
+ * (i.e., go to next track.)
+ */
+int
+wm_cd_play_from_pos( int pos )
+{
+ int status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return -1;
+
+ if (pos == -1)
+ pos = thiscd.trk[thiscd.curtrack - 1].length - 1;
+
+ if (wm_cur_cdmode == WM_CDM_PLAYING)
+ return wm_cd_play(thiscd.curtrack, pos, playlist[cur_listno-1].end);
+ else
+ return -1;
+} /* wm_cd_play_from_pos() */
+
+/*
+ * wm_cd_pause()
+ *
+ * Pause the CD, if it's in play mode. If it's already paused, go back to
+ * play mode.
+ */
+int
+wm_cd_pause( void )
+{
+ static int paused_pos;
+ int status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return -1;
+
+ if(WM_CDM_PLAYING == wm_cur_cdmode) {
+ if(drive.proto && drive.proto->gen_pause)
+ (drive.proto->gen_pause)(&drive);
+
+ paused_pos = cur_pos_rel;
+ } else if(WM_CDM_PAUSED == status) {
+ if(!(drive.proto->gen_resume) || (drive.proto->gen_resume(&drive) > 0)) {
+ wm_cd_play(thiscd.curtrack, paused_pos, playlist[cur_listno-1].end);
+ }
+ } else {
+ return -1;
+ }
+ wm_cd_status();
+ return 0;
+} /* wm_cd_pause() */
+
+/*
+ * wm_cd_stop()
+ *
+ * Stop the CD if it's not already stopped.
+ */
+int
+wm_cd_stop( void )
+{
+ int status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return -1;
+
+ if (status != WM_CDM_STOPPED) {
+
+ if(drive.proto && drive.proto->gen_stop)
+ (drive.proto->gen_stop)(&drive);
+
+ status = wm_cd_status();
+ }
+
+ return (status != WM_CDM_STOPPED);
+} /* wm_cd_stop() */
+
+/*
+ * Eject the current CD, if there is one, and set the mode to 5.
+ *
+ * Returns 0 on success, 1 if the CD couldn't be ejected, or 2 if the
+ * CD contains a mounted filesystem.
+ */
+int
+wm_cd_eject( void )
+{
+ int err = -1;
+
+ wm_cd_stop();
+
+ if(drive.proto && drive.proto->gen_eject)
+ err = (drive.proto->gen_eject)(&drive);
+
+ if (err < 0) {
+ if (err == -3) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+
+ wm_cd_status();
+
+ return 0;
+}
+
+int wm_cd_closetray(void)
+{
+ int status, err = -1;
+
+ status = wm_cd_status();
+ if (status == WM_CDM_UNKNOWN || status == WM_CDM_NO_DISC)
+ return -1;
+
+ if(drive.proto->gen_closetray)
+ err = (drive.proto->gen_closetray)(&drive);
+
+ return(err ? 0 : wm_cd_status()==2 ? 1 : 0);
+} /* wm_cd_closetray() */
+
+struct cdtext_info*
+wm_cd_get_cdtext( void )
+{
+ int status;
+
+ status = wm_cd_status();
+
+ if(WM_CDS_NO_DISC(status))
+ return NULL;
+
+ return get_glob_cdtext(&drive, 0);
+}
+
+/*
+ * find_trkind(track, index, start)
+ *
+ * Start playing at a particular track and index, optionally using a particular
+ * frame as a starting position. Returns a frame number near the start of the
+ * index mark if successful, 0 if the track/index didn't exist.
+ *
+ * This is made significantly more tedious (though probably easier to port)
+ * by the fact that CDROMPLAYTRKIND doesn't work as advertised. The routine
+ * does a binary search of the track, terminating when the interval gets to
+ * around 10 frames or when the next track is encountered, at which point
+ * it's a fair bet the index in question doesn't exist.
+ */
+int
+wm_find_trkind( int track, int ind, int start )
+{
+ int top = 0, bottom, current, interval, ret = 0, i, status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return 0;
+
+ for (i = 0; i < thiscd.ntracks; i++) {
+ if (thiscd.trk[i].track == track)
+ break;
+ }
+
+ bottom = thiscd.trk[i].start;
+
+ for (; i < thiscd.ntracks; i++) {
+ if (thiscd.trk[i].track > track)
+ break;
+ }
+
+ top = i == thiscd.ntracks ? (thiscd.length - 1) * 75 : thiscd.trk[i].start;
+ if (start > bottom && start < top)
+ bottom = start;
+
+ current = (top + bottom) / 2;
+ interval = (top - bottom) / 4;
+
+ do {
+ wm_cd_play_chunk(current, current + 75, current);
+
+ if (wm_cd_status() != 1)
+ return 0;
+ while (cur_frame < current) {
+ if(wm_cd_status() != 1 || wm_cur_cdmode != WM_CDM_PLAYING)
+ return 0;
+ else
+ wm_susleep(1);
+ }
+
+ if (thiscd.trk[thiscd.curtrack - 1].track > track)
+ break;
+
+ if(cur_index >= ind) {
+ ret = current;
+ current -= interval;
+ } else {
+ current += interval;
+ }
+
+ interval /= 2;
+
+ } while (interval > 2);
+
+ return ret;
+} /* find_trkind() */
+
+int
+wm_cd_set_verbosity( int level )
+{
+ wm_lib_set_verbosity(level);
+ return wm_lib_get_verbosity();
+}
+
+/*
+ * volume is valid WM_VOLUME_MUTE <= vol <= WM_VOLUME_MAXIMAL,
+ * balance is valid WM_BALANCE_ALL_LEFTS <= balance <= WM_BALANCE_ALL_RIGHTS
+ */
+
+int
+wm_cd_volume( int vol, int bal )
+{
+ int left, right;
+ const int bal1 = (vol - WM_VOLUME_MUTE)/(WM_BALANCE_ALL_RIGHTS - WM_BALANCE_SYMMETRED);
+
+/*
+ * Set "left" and "right" to volume-slider values accounting for the
+ * balance setting.
+ *
+ */
+ if(vol < WM_VOLUME_MUTE) vol = WM_VOLUME_MUTE;
+ if(vol > WM_VOLUME_MAXIMAL) vol = WM_VOLUME_MAXIMAL;
+ if(bal < WM_BALANCE_ALL_LEFTS) bal = WM_BALANCE_ALL_LEFTS;
+ if(bal > WM_BALANCE_ALL_RIGHTS) bal = WM_BALANCE_ALL_RIGHTS;
+
+ left = vol - (bal * bal1);
+ right = vol + (bal * bal1);
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calculate volume left %i, right %i\n", left, right);
+
+ if (left > WM_VOLUME_MAXIMAL)
+ left = WM_VOLUME_MAXIMAL;
+ if (right > WM_VOLUME_MAXIMAL)
+ right = WM_VOLUME_MAXIMAL;
+
+ if(!(drive.proto) || !(drive.proto->gen_set_volume))
+ return -1;
+ else
+ return (drive.proto->gen_set_volume)(&drive, left, right);
+} /* cd_volume() */
+
+int
+wm_cd_getvolume( void )
+{
+ int left, right;
+
+ if(!(drive.proto) || !(drive.proto->gen_get_volume) ||
+ (drive.proto->gen_get_volume)(&drive, &left, &right) < 0 || left == -1)
+ return -1;
+
+ if (left < right) {
+ wm_cd_cur_balance = (right - left) / 2;
+ if (wm_cd_cur_balance > WM_BALANCE_ALL_RIGHTS)
+ wm_cd_cur_balance = WM_BALANCE_ALL_RIGHTS;
+ return right;
+ } else if (left == right) {
+ wm_cd_cur_balance = WM_BALANCE_SYMMETRED;
+ return left;
+ } else {
+ wm_cd_cur_balance = (right - left) / 2;
+ if (wm_cd_cur_balance < WM_BALANCE_ALL_LEFTS)
+ wm_cd_cur_balance = WM_BALANCE_ALL_LEFTS;
+ return left;
+ }
+}
+
+int
+wm_cd_getbalance( void )
+{
+ int left, right;
+
+ if(!(drive.proto) || !(drive.proto->gen_get_volume) ||
+ (drive.proto->gen_get_volume)(&drive, &left, &right) < 0 || left == -1)
+ return WM_BALANCE_SYMMETRED;
+
+ if (left < right) {
+ wm_cd_cur_balance = (right - left) / 2;
+ if (wm_cd_cur_balance > WM_BALANCE_ALL_RIGHTS)
+ wm_cd_cur_balance = WM_BALANCE_ALL_RIGHTS;
+ } else if (left == right) {
+ wm_cd_cur_balance = WM_BALANCE_SYMMETRED;
+ } else {
+ wm_cd_cur_balance = (right - left) / 2;
+ if (wm_cd_cur_balance < WM_BALANCE_ALL_LEFTS)
+ wm_cd_cur_balance = WM_BALANCE_ALL_LEFTS;
+ }
+ return wm_cd_cur_balance;
+}
+
+/*
+ * Prototype wm_drive structure, with generic functions. The generic functions
+ * will be replaced with drive-specific functions as appropriate once the drive
+ * type has been sensed.
+ */
+struct wm_drive_proto generic_proto = {
+ gen_init, /* functions... */
+ gen_close,
+ gen_get_trackcount,
+ gen_get_cdlen,
+ gen_get_trackinfo,
+ gen_get_drive_status,
+ gen_get_volume,
+ gen_set_volume,
+ gen_pause,
+ gen_resume,
+ gen_stop,
+ gen_play,
+ gen_eject,
+ gen_closetray,
+ gen_get_cdtext
+};
+
+const char*
+gen_status(int status)
+{
+ static char tmp[250];
+ switch(status) {
+ case WM_CDM_TRACK_DONE:
+ return "WM_CDM_TRACK_DONE";
+ case WM_CDM_PLAYING:
+ return "WM_CDM_PLAYING";
+ case WM_CDM_FORWARD:
+ return "WM_CDM_FORWARD";
+ case WM_CDM_PAUSED:
+ return "WM_CDM_PAUSED";
+ case WM_CDM_STOPPED:
+ return "WM_CDM_STOPPED";
+ case WM_CDM_EJECTED:
+ return "WM_CDM_EJECTED";
+ case WM_CDM_DEVICECHANGED:
+ return "WM_CDM_DEVICECHANGED";
+ case WM_CDM_NO_DISC:
+ return "WM_CDM_NO_DISC";
+ case WM_CDM_UNKNOWN:
+ return "WM_CDM_UNKNOWN";
+ case WM_CDM_CDDAERROR:
+ return "WM_CDM_CDDAERROR";
+ case WM_CDM_CDDAACK:
+ return "WM_CDM_CDDAACK";
+ default:
+ {
+ sprintf(tmp, "unexpected status %i", status);
+ return tmp;
+ }
+ }
+}
diff --git a/kscd/libwm/cdtext.c b/kscd/libwm/cdtext.c
new file mode 100644
index 00000000..bb4e19b8
--- /dev/null
+++ b/kscd/libwm/cdtext.c
@@ -0,0 +1,428 @@
+/***************************************************************************
+ cdtext.c - description
+ -------------------
+ begin : Mon Feb 12 2001
+ copyright : (C) 2001,2003 by Alex Kern
+ email : alex.kern@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#ifdef libunicode
+ #include <unicode.h>
+#endif
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_database.h"
+#include "include/wm_platform.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_MISC
+
+/* local prototypes */
+int free_cdtext_info_block(struct cdtext_info_block* cdtextinfoblock);
+int free_cdtext_info(struct cdtext_info* cdtextinfo);
+struct cdtext_info_block* malloc_cdtext_info_block(int count_of_tracks);
+void get_data_from_cdtext_pack(
+ const struct cdtext_pack_data_header *pack,
+ const struct cdtext_pack_data_header *pack_previous,
+ cdtext_string *p_componente);
+
+struct cdtext_info wm_cdtext_info = { 0, 0, 0, 0 };
+
+int free_cdtext_info_block(struct cdtext_info_block* cdtextinfoblock)
+{
+ if(cdtextinfoblock)
+ {
+ if(cdtextinfoblock->name)
+ free(cdtextinfoblock->name);
+ if(cdtextinfoblock->performer)
+ free(cdtextinfoblock->performer);
+ if(cdtextinfoblock->songwriter)
+ free(cdtextinfoblock->songwriter);
+ if(cdtextinfoblock->composer)
+ free(cdtextinfoblock->composer);
+ if(cdtextinfoblock->arranger)
+ free(cdtextinfoblock->arranger);
+ if(cdtextinfoblock->message)
+ free(cdtextinfoblock->message);
+ if(cdtextinfoblock->UPC_EAN_ISRC_code)
+ free(cdtextinfoblock->UPC_EAN_ISRC_code);
+
+ if(cdtextinfoblock->block_encoding_text)
+ free(cdtextinfoblock->block_encoding_text);
+ }
+
+ return 0;
+}
+
+int free_cdtext_info(struct cdtext_info* cdtextinfo)
+{
+ int i;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: free_cdtext_info() called\n");
+
+ if(cdtextinfo)
+ {
+ for(i = 0; i < 8; i++)
+ {
+ if(cdtextinfo->blocks[i])
+ {
+ free_cdtext_info_block(cdtextinfo->blocks[i]);
+ }
+ }
+ memset(cdtextinfo, 0, sizeof(struct cdtext_info));
+ }
+
+ return 0;
+}
+
+struct cdtext_info_block* malloc_cdtext_info_block(int count_of_tracks)
+{
+ int memamount;
+ struct cdtext_info_block* lp_block;
+
+ lp_block = malloc(sizeof(struct cdtext_info_block));
+ if(!lp_block)
+ {
+ return (struct cdtext_info_block*)0;
+ }
+ memset(lp_block, 0, sizeof(struct cdtext_info_block));
+
+ memamount = count_of_tracks * sizeof(cdtext_string);
+
+ lp_block->name = malloc(memamount);
+ if(!lp_block->name)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->name, 0, memamount);
+
+ lp_block->performer = malloc(memamount);
+ if(!lp_block->performer)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->performer, 0, memamount);
+
+ lp_block->songwriter = malloc(memamount);
+ if(!lp_block->songwriter)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->songwriter, 0, memamount);
+
+ lp_block->composer = malloc(memamount);
+ if(!lp_block->composer)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->composer, 0, memamount);
+
+ lp_block->arranger = malloc(memamount);
+ if(!lp_block->arranger)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->arranger, 0, memamount);
+
+ lp_block->message = malloc(memamount);
+ if(!lp_block->message)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->message, 0, memamount);
+
+ lp_block->UPC_EAN_ISRC_code = malloc(memamount);
+ if(!lp_block->UPC_EAN_ISRC_code)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->UPC_EAN_ISRC_code, 0, memamount);
+
+ return lp_block;
+}
+
+void get_data_from_cdtext_pack(
+ const struct cdtext_pack_data_header *pack,
+ const struct cdtext_pack_data_header *pack_previous,
+ cdtext_string *p_componente)
+{
+
+ int arr = pack->header_field_id2_tracknumber;
+ int i;
+ int language_block;
+ int unicode;
+
+ language_block = (pack->header_field_id4_block_no >> 4) & 0x07;
+ unicode = pack->header_field_id4_block_no & 0x80;
+
+ if(!unicode)
+ {
+ for(i = 0; i < DATAFIELD_LENGHT_IN_PACK; i++)
+ {
+ if(pack->text_data_field[i] == 0x00) /* end marker */
+ {
+ arr++;
+ }
+ else if(pack->text_data_field[i] == 0x09) /* repeat last marker */
+ {
+ /* ASSERT(arr > 0) */
+ strcat((char*)(p_componente[arr]), (char*)(p_componente[arr-1]));
+ arr++;
+ }
+ else
+ {
+ strncat((char*)(p_componente[arr]), (char*)(&(pack->text_data_field[i])), 1);
+ }
+ }
+ }
+#ifdef libunicode
+ else /* doublebytes ;-) */
+ {
+ for(i = 0; i < DATAFIELD_LENGHT_IN_PACK; i += 2)
+ {
+ if((Uchar)(pack->text_data_field[i]) == 0x0000) /* end marker */
+ {
+ arr++;
+ }
+ else if((Uchar)(pack->text_data_field[i]) == 0x0909) /* repeat last marker */
+ {
+ /* ASSERT(arr > 0) */
+ strcat((char*)(p_componente[arr]), (char*)(p_componente[arr-1]));
+ arr++;
+ }
+ else
+ {
+ strncat((char*)(p_componente[arr]), u_uc_to_utf8((Uchar*)(&(pack->text_data_field[i]))), 1);
+ }
+ }
+ }
+#else
+ else {
+ wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS, "can't handle unicode");
+ }
+#endif
+}
+
+struct cdtext_info*
+get_glob_cdtext(struct wm_drive *d, int redo)
+{
+ /* alloc cdtext_info */
+
+ unsigned char *buffer;
+ int buffer_length;
+ int ret;
+ int i;
+ struct cdtext_pack_data_header *pack, *pack_previous;
+ cdtext_string *p_componente;
+ struct cdtext_info_block *lp_block;
+ if(!d->proto || d->proto->gen_get_cdtext == NULL || d->proto->gen_get_trackcount == NULL) {
+ return NULL;
+ }
+
+ if(!redo && wm_cdtext_info.valid) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS, "CDTEXT DEBUG: recycle cdtext\n");
+ return &wm_cdtext_info;
+ } else
+ free_cdtext_info(&wm_cdtext_info);
+
+ lp_block = 0;
+ p_componente = 0;
+ buffer = 0;
+ buffer_length = 0;
+
+ ret = (d->proto->gen_get_cdtext)(d, &buffer, &buffer_length);
+ if(!ret)
+ {
+ (d->proto->gen_get_trackcount)(d, &(wm_cdtext_info.count_of_entries));
+ if( wm_cdtext_info.count_of_entries < 0 )
+ wm_cdtext_info.count_of_entries = 1;
+ else
+ wm_cdtext_info.count_of_entries++;
+
+ i = 0;
+
+ pack = 0; /* NULL pointer*/
+ while(i < buffer_length)
+ {
+ pack_previous = pack;
+ pack = (struct cdtext_pack_data_header*)(buffer+i);
+ /* to implement: check_crc(pack); */
+
+ /* only for valid packs */
+ if(pack->header_field_id1_typ_of_pack >= 0x80 && pack->header_field_id1_typ_of_pack < 0x90)
+ {
+ int code, j;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT DEBUG: valid packet at 0x%08X: 0x %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ i,
+ pack->header_field_id1_typ_of_pack,
+ pack->header_field_id2_tracknumber,
+ pack->header_field_id3_sequence,
+ pack->header_field_id4_block_no,
+ pack->text_data_field[0],
+ pack->text_data_field[1],
+ pack->text_data_field[2],
+ pack->text_data_field[3],
+ pack->text_data_field[4],
+ pack->text_data_field[5],
+ pack->text_data_field[6],
+ pack->text_data_field[7],
+ pack->text_data_field[8],
+ pack->text_data_field[9],
+ pack->text_data_field[10],
+ pack->text_data_field[11],
+ pack->crc_byte1,
+ pack->crc_byte2);
+ wm_cdtext_info.count_of_valid_packs++;
+
+ code = (pack->header_field_id4_block_no >> 4) & 0x07;
+ if(0 == lp_block || lp_block->block_code != code) /* find or create one new block */
+ {
+ lp_block = 0;
+ for(j = 0; j < MAX_LANGUAGE_BLOCKS && wm_cdtext_info.blocks[j] != 0 && 0 == lp_block; j++)
+ {
+ if(wm_cdtext_info.blocks[j]->block_code == code)
+ {
+ lp_block = wm_cdtext_info.blocks[j];
+ }
+ }
+
+ if(MAX_LANGUAGE_BLOCKS <= j)
+ {
+ free_cdtext_info(&wm_cdtext_info);
+ wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS,
+ "CDTEXT ERROR: more as 8 languageblocks defined\n");
+ return NULL;
+ }
+
+ if(0 == lp_block)
+ {
+ /* make next new block */
+ lp_block = malloc_cdtext_info_block(wm_cdtext_info.count_of_entries);
+ if(0 == lp_block)
+ {
+ wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS,
+ "CDTEXT ERROR: out of memory, can't create a new language block\n");
+ free_cdtext_info(&wm_cdtext_info);
+ return NULL /*ENOMEM*/;
+ }
+ else
+ {
+ wm_cdtext_info.blocks[j] = lp_block;
+ wm_cdtext_info.blocks[j]->block_code = code;
+ wm_cdtext_info.blocks[j]->block_unicode = pack->header_field_id4_block_no & 0x80;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: created a new language block; code %i, %s characters\n", code, lp_block->block_unicode?"doublebyte":"singlebyte");
+/*
+ unsigned char block_encoding; not jet!
+ cdtext_string* block_encoding_text;
+*/
+ }
+ }
+ }
+ }
+
+ switch(pack->header_field_id1_typ_of_pack)
+ {
+ case 0x80:
+ p_componente = (lp_block->name);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x81:
+ p_componente = (lp_block->performer);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x82:
+ p_componente = (lp_block->songwriter);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x83:
+ p_componente = (lp_block->composer);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x84:
+ p_componente = (lp_block->arranger);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x85:
+ p_componente = (lp_block->message);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x86:
+ memcpy((char*)(lp_block->binary_disc_identification_info),
+ (char*)(pack->text_data_field), DATAFIELD_LENGHT_IN_PACK);
+ break;
+ case 0x87:
+ memcpy((char*)(lp_block->binary_genreidentification_info),
+ (char*)(pack->text_data_field), DATAFIELD_LENGHT_IN_PACK);
+ break;
+ case 0x88:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: PACK with code 0x88 (TOC)\n");
+ break;
+ case 0x89:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: PACK with code 0x89 (second TOC)\n");
+ break;
+ case 0x8A:
+ case 0x8B:
+ case 0x8C:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: PACK with code 0x%02X (reserved)\n", pack->header_field_id1_typ_of_pack);
+ break;
+ case 0x8D:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: PACK with code 0x8D (for content provider only)\n");
+ break;
+ case 0x8E:
+ p_componente = (lp_block->UPC_EAN_ISRC_code);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x8F:
+ memcpy((char*)(lp_block->binary_size_information),
+ (char*)(pack->text_data_field), DATAFIELD_LENGHT_IN_PACK);
+ break;
+ default:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT ERROR: invalid packet at 0x%08X: 0x %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ i,
+ pack->header_field_id1_typ_of_pack,
+ pack->header_field_id2_tracknumber,
+ pack->header_field_id3_sequence,
+ pack->header_field_id4_block_no,
+ pack->text_data_field[0],
+ pack->text_data_field[1],
+ pack->text_data_field[2],
+ pack->text_data_field[3],
+ pack->text_data_field[4],
+ pack->text_data_field[5],
+ pack->text_data_field[6],
+ pack->text_data_field[7],
+ pack->text_data_field[8],
+ pack->text_data_field[9],
+ pack->text_data_field[10],
+ pack->text_data_field[11],
+ pack->crc_byte1,
+ pack->crc_byte2);
+ wm_cdtext_info.count_of_invalid_packs++;
+ }
+ i += sizeof(struct cdtext_pack_data_header);
+ } /* while */
+ }
+
+ if(0 == ret && wm_cdtext_info.count_of_valid_packs > 0)
+ wm_cdtext_info.valid = 1;
+
+ return &wm_cdtext_info;
+}
+
+void free_cdtext(void)
+{
+ if (wm_cdtext_info.valid)
+ free_cdtext_info(&wm_cdtext_info);
+}
diff --git a/kscd/libwm/configure.in.in b/kscd/libwm/configure.in.in
new file mode 100644
index 00000000..a0ce3ab7
--- /dev/null
+++ b/kscd/libwm/configure.in.in
@@ -0,0 +1,72 @@
+dnl +-------------------------------+
+dnl | Checks for LIBWORKMAN |
+dnl +-------------------------------+
+AC_MSG_CHECKING(for CDDA)
+
+AC_ARG_WITH(kscd-cdda, [ --with-kscd-cdda build CDDA support in kscd [default=yes]],
+[
+ if test $withval = yes; then
+ libwm_with_cdda=yes
+ else
+ libwm_with_cdda=no
+ fi
+],libwm_with_cdda=yes)
+
+if test "$libwm_with_cdda" = "yes"; then
+case $host in
+ *-*-linux*)
+ AC_CHECK_HEADERS(pthread.h)
+ AC_TRY_COMPILE(
+ [
+#ifndef __GNUC__
+#define __GNUC__ 1
+#endif
+/* needed for vanilla kernel headers, which do provide __u64 only
+ for ansi */
+#undef __STRICT_ANSI__
+/* needed for non-ansi kernel headers */
+#define asm __asm__
+#define inline __inline__
+#include <linux/types.h>
+#include <linux/cdrom.h>
+#undef asm
+#undef inline
+ ],[
+#if defined(__linux__)
+ioctl(1, CDROMREADAUDIO, 0);
+#else
+ #error platform?
+#endif
+ ],, libwm_with_cdda=no)
+ ;;
+ *-*-sunos*)
+ AC_CHECK_HEADERS(pthread.h)
+ AC_TRY_COMPILE(
+ [
+#include <sys/types.h>
+#include <sys/cdio.h>
+ ],[
+#if defined(__sun) || defined(sun)
+ioctl(1, CDROMCDDA, 0);
+#else
+ #error platform?
+#endif
+ ],, libwm_with_cdda=no)
+ ;;
+ *)
+ libwm_with_cdda=no
+ ;;
+esac
+fi
+
+if test "$libwm_with_cdda" = "yes"; then
+ AC_DEFINE(BUILD_CDDA, 1, [Define if you will CDDA support in kscd])
+fi
+AM_CONDITIONAL(libwm_with_cdda, test "$libwm_with_cdda" = "yes")
+
+AC_MSG_RESULT($libwm_with_cdda)
+
+
+dnl +-------------------------------+
+dnl | End LIBWORKMAN checks |
+dnl +-------------------------------+
diff --git a/kscd/libwm/database.c b/kscd/libwm/database.c
new file mode 100644
index 00000000..f7f4e7cc
--- /dev/null
+++ b/kscd/libwm/database.c
@@ -0,0 +1,1543 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Manage the CD database. All these routines assume that the "cd" global
+ * structure contains current information (as far as the outside world knows;
+ * obviously it won't contain track titles just after a CD is inserted.)
+ */
+
+#define RCFILE "/.workmanrc"
+#define DBFILE "/.workmandb"
+#define FUZZFRAMES 75
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <time.h>
+#include "include/wm_config.h"
+#include "include/wm_helpers.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_cddb.h"
+#include "include/wm_index.h"
+#include "include/wm_database.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_DB
+
+#define SWALLOW_LINE(fp) { int _c; while ((_c = getc(fp)) != '\n' && _c != EOF); }
+
+/* local prototypes */
+char *print_cdinfo(struct wm_cdinfo *pcd, int prefs);
+FILE *open_rcfile(const char *name, const char *mode);
+int *reset_tracks(void);
+int search_db( FILE *fp, int prefs, int scan, int holesize_wanted );
+void spinwheels(int secs);
+int lockit(int fd, int type);
+void save_globals(FILE *fp);
+int save_entry(char *filename, int pref);
+/* local prototypes END */
+
+static int suppress_locking = 0; /* Turn off locking of datafile (dangerous) */
+
+static char *rcfile = NULL; /* Personal rcfile */
+static char *dbfiles = NULL; /* Colon-separated list of databases */
+static char **databases = NULL; /* NULL-terminated list of databases */
+
+static char *otherrc = NULL; /* Unrecognized cruft from start of rcfile */
+
+static long rcpos, rclen; /* XXX */
+
+static int found_in_db, found_in_rc;
+static long holepos, firstpos;
+
+static int fuzz_frames = FUZZFRAMES;
+
+static int wm_db_save_disabled = FALSE;
+
+static int cur_playnew = -1;
+
+extern int cur_ntracks, cur_nsections;
+
+int mark_a = 0;
+int mark_b = 0;
+
+
+/*
+ *
+ */
+int wm_db_get_playnew( void )
+{
+ return 0;
+}
+
+/*
+ * split_workmandb()
+ *
+ * Split the WORKMANDB environment variable, if any, into a list of database
+ * files in the global "databases". If WORKMANDB is not available, make
+ * a single entry with $HOME/DBFILE.
+ *
+ * Also, fill the "rcfile" global with the personal preferences filename.
+ *
+ * The environment variables should have already been read and placed in the
+ * "rcfile" and "dbfiles" globals, respectively.
+ */
+void
+split_workmandb( void )
+{
+ int ndbs, i;
+ char *home, *wmdb;
+ int no_rc = 0, no_db = 0;
+
+ if (rcfile == NULL)
+ {
+ if ((home = getenv("HOME")) != NULL)
+ {
+ rcfile = malloc(strlen(home) + sizeof(RCFILE));
+ if (rcfile == NULL)
+ {
+
+nomem:
+ perror("split_workmandb()");
+ exit(1);
+ }
+
+ strcpy(rcfile, home);
+ strcat(rcfile, RCFILE);
+ }
+ else
+ no_rc = 1;
+
+ }
+
+ if ((wmdb = dbfiles) == NULL)
+ {
+ if ((home = getenv("HOME")) != NULL)
+ {
+ wmdb = malloc(strlen(home) + sizeof(DBFILE));
+ if (wmdb == NULL)
+ goto nomem;
+
+ databases = malloc(2 * sizeof (databases[0]));
+ if (databases == NULL)
+ goto nomem;
+
+ strcpy(wmdb, home);
+ strcat(wmdb, DBFILE);
+ databases[0] = wmdb;
+ databases[1] = NULL;
+ }
+ else
+ {
+ static char *emptydb = NULL;
+
+ databases = &emptydb;
+ no_db = 1;
+ }
+ }
+ else
+ {
+ ndbs = 1;
+ for (home = wmdb; *home; home++)
+ if (*home == ':')
+ {
+ *home = '\0';
+ ndbs++;
+ }
+
+ databases = malloc((ndbs + 1) * sizeof(databases[0]));
+ if (databases == NULL)
+ goto nomem;
+
+ for (i = 0; i < ndbs; i++)
+ {
+ databases[i] = wmdb;
+ wmdb += strlen(wmdb) + 1;
+ }
+
+ databases[i] = NULL;
+ }
+
+ if (no_db || no_rc)
+ {
+#ifndef NDEBUG
+ fprintf(stderr,
+"WorkMan was run without a home directory, probably by a system daemon.\n");
+ fprintf(stderr, "It doesn't know where to find ");
+ if (no_rc)
+ {
+ fprintf(stderr, "your personal preferences file ");
+ if (no_db)
+ fprintf(stderr, "or the\ndatabase of CD descriptions");
+ }
+ else
+ fprintf(stderr, "the database of CD descriptions");
+
+ fprintf(stderr,
+".\nYou can use the X resources \"workman.db.shared\" and \"workman.db.personal\"\nto tell WorkMan where to look.\n");
+#endif
+ wm_db_save_disabled = TRUE;
+ }
+}
+
+/*
+ * print_cdinfo(cd, prefs)
+ *
+ * cd A pointer to a cdinfo struct.
+ * prefs Flag: write personal preferences?
+ *
+ * Print a CD's information (in more or less readable form) to a buffer.
+ * Returns a pointer to the buffer.
+ *
+ * XXX - could be more efficient about calling wm_strmcat() and strlen().
+ */
+char *
+print_cdinfo(struct wm_cdinfo *cd, int prefs)
+{
+ int i;
+ char tempbuf[2000]; /* XXX - is this always big enough? */
+ static char *cdibuf = NULL;
+ struct wm_playlist *l;
+
+ sprintf(tempbuf, "\ntracks %d", cd->ntracks);
+ for (i = 0; i < cur_ntracks; i++)
+ if (cd->trk[i].section < 2)
+ sprintf(tempbuf + strlen(tempbuf), " %d",
+ cd->trk[i].start);
+ sprintf(tempbuf + strlen(tempbuf), " %d\n", cd->length);
+
+ wm_strmcpy(&cdibuf, tempbuf);
+
+ if (cur_nsections)
+ {
+ sprintf(tempbuf, "sections %d", cur_nsections);
+ /* fixed a bug here */
+ for (i = 0; i < cur_ntracks; i++)
+ if (cd->trk[i].section > 1)
+ sprintf(tempbuf + strlen(tempbuf), " %d",
+ cd->trk[i].start);
+ sprintf(tempbuf + strlen(tempbuf), "\n");
+
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+
+ if (prefs)
+ {
+ if (cd->autoplay)
+ wm_strmcat(&cdibuf, "autoplay\n");
+ for (l = cd->lists; l != NULL && l->name != NULL; l++)
+ {
+ wm_strmcat(&cdibuf, "playlist ");
+
+ i = strlen(cdibuf) - 1;
+ wm_strmcat(&cdibuf, l->name);
+ while (cdibuf[++i])
+ if (cdibuf[i] == ' ' || cdibuf[i] == '\t')
+ cdibuf[i] = '_';
+
+ if (l->list != NULL)
+ {
+ for (i = 0; l->list[i]; i++)
+ ;
+ sprintf(tempbuf, " %d", i);
+ wm_strmcat(&cdibuf, tempbuf);
+ for (i = 0; l->list[i]; i++)
+ {
+ sprintf(tempbuf, " %d", l->list[i]);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+ wm_strmcat(&cdibuf, "\n");
+ }
+ else
+ wm_strmcat(&cdibuf, " 0\n");
+ }
+
+ if (cd->volume)
+ {
+ /*
+ * Have to maintain compatibility with old versions,
+ * where volume was 0-32.
+ */
+ sprintf(tempbuf, "cdvolume %d\n", (cd->volume * 32) / 100);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+
+ if (cd->playmode)
+ {
+ sprintf(tempbuf, "playmode %d\n", cd->playmode);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+
+ if (mark_a)
+ {
+ sprintf(tempbuf, "mark %d START\n", mark_a);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+ if (mark_b)
+ {
+ sprintf(tempbuf, "mark %d END\n", mark_b);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+
+ if (cd->otherrc)
+ wm_strmcat(&cdibuf, cd->otherrc);
+
+ for (i = 0; i < cur_ntracks; i++)
+ {
+ if (cd->trk[i].avoid)
+ {
+ sprintf(tempbuf, "dontplay %d\n", i + 1);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+ if (cd->trk[i].volume)
+ {
+ sprintf(tempbuf, "volume %d %d\n", i + 1,
+ (cd->trk[i].volume * 32) / 100);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+ if (cd->trk[i].otherrc)
+ wm_strmcat(&cdibuf, cd->trk[i].otherrc);
+ }
+ }
+ else
+ {
+ if (cd->cdname[0])
+ {
+ wm_strmcat(&cdibuf, "cdname ");
+ wm_strmcat(&cdibuf, cd->cdname);
+ wm_strmcat(&cdibuf, "\n");
+ }
+
+ if (cd->artist[0])
+ {
+ wm_strmcat(&cdibuf, "artist ");
+ wm_strmcat(&cdibuf, cd->artist);
+ wm_strmcat(&cdibuf, "\n");
+ }
+
+ if (cd->otherdb)
+ wm_strmcat(&cdibuf, cd->otherdb);
+
+ for (i = 0; i < cur_ntracks; i++)
+ {
+ if (cd->trk[i].section > 1)
+ wm_strmcat(&cdibuf, "s-");
+ wm_strmcat(&cdibuf, "track ");
+ if (cd->trk[i].songname != NULL)
+ wm_strmcat(&cdibuf, cd->trk[i].songname);
+ wm_strmcat(&cdibuf, "\n");
+ if (cd->trk[i].contd)
+ {
+ if (cd->trk[i].section > 1)
+ wm_strmcat(&cdibuf, "s-");
+ wm_strmcat(&cdibuf, "continue\n");
+ }
+ if (cd->trk[i].otherdb)
+ wm_strmcat(&cdibuf, cd->trk[i].otherdb);
+ }
+ }
+
+ return (cdibuf);
+} /* print_cdinfo() */
+
+/*
+ * Open the rcfile for reading or writing.
+ *
+ * name Filename
+ * mode "r" or "w"
+ */
+FILE *
+open_rcfile(const char *name, const char *mode)
+{
+ FILE *fp;
+ struct stat st;
+
+ fp = fopen(name, mode);
+ if (fp == NULL)
+ {
+ if (errno != ENOENT || mode[0] == 'w')
+ perror(name);
+ }
+ else
+ {
+ /* Don't let people open directories or devices */
+ if (fstat(fileno(fp), &st) < 0)
+ {
+ perror(name);
+ fclose(fp);
+ return (NULL);
+ }
+
+#ifdef S_ISREG
+ if (! S_ISREG(st.st_mode))
+#else
+ if ((st.st_mode & S_IFMT) != S_IFREG)
+#endif
+ {
+ errno = EISDIR;
+ perror(name);
+ fclose(fp);
+ return (NULL);
+ }
+
+ if (mode[0] == 'w') /* create -- put data in so locks work */
+ {
+ fputs("# WorkMan database file\n", fp);
+ fclose(fp);
+ fp = fopen(name, "r+");
+ if (fp == NULL)
+ if (errno != ENOENT)
+ perror(name);
+ }
+ }
+
+ return (fp);
+}
+
+/*
+ *
+ * allocate and clear "trackmap".
+ *
+ */
+int *reset_tracks(void)
+{
+ int i, j;
+ int *trackmap;
+ /*
+ * Since we access track numbers indirectly (to handle sections
+ * with at least a little elegance), the track mapping needs to be
+ * set up before we read anything. Initially it must assume that
+ * no sections will appear in this datafile.
+ */
+ trackmap = malloc(sizeof(int) * cur_ntracks);
+ if (trackmap == NULL)
+ {
+ perror("trackmap");
+ exit(1);
+ }
+ j = 0;
+ for (i = 0; i < cd->ntracks; i++)
+ {
+ trackmap[i] = j;
+ while (cd->trk[++j].section > 1)
+ ;
+ }
+ return trackmap;
+} /* reset_tracks() */
+
+/*
+ * Load a new-format database file, searching for a match with the currently
+ * inserted CD. Modify the in-core copy of the CD info based on what's found
+ * in the database.
+ *
+ * Returns 1 if there was a match or 0 if not.
+ *
+ * fp FILE* of database or rcfile.
+ * prefs 1 if we're searching .workmanrc, 0 for .workmandb,
+ * 2 if just reading settings
+ * scan Scan for "tracks" location and entry size only
+ * holesize_wanted How big a hole we're looking for, if any.
+ *
+ * If a hole was found along the way, update the global "holepos" with its
+ * starting offset in the file. A hole is defined as a bunch of blank lines
+ * preceding a "tracks" line. Holepos will contain the best match.
+ *
+ * In addition, "firstpos" will be filled with the position of the first
+ * "tracks" keyword, so we know how much room is available for global
+ * settings at the rcfile's start.
+ */
+int
+search_db( FILE *fp, int prefs, int scan, int holesize_wanted )
+
+{
+ char keyword[64], listname[64], *c;
+ int b, i, j, track = 0, listsize, ntracks, scratch, searching = 1;
+ int *trackmap = 0, gotsections = 0;
+ int fudge, maxfudge, sizediff, bestfudge = 0;
+ long pos = 0, thisholepos = -1, holesize = 99991239;
+ struct wm_playlist *l;
+
+ rclen = 0;
+
+ wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG , "db: Reached search_db()\n" );
+
+ /* We may not find any holes at all! */
+ if (holesize_wanted)
+ holepos = -1;
+
+ if( prefs != 2 )
+ trackmap = reset_tracks();
+
+ if (prefs)
+ freeup(&otherrc);
+ firstpos = -1;
+ while (! feof(fp))
+ {
+ pos = ftell(fp);
+ keyword[0] = '\0';
+ do
+ b = getc(fp);
+ while (b != EOF && b != '\n' && isspace(b));
+
+ if (b == EOF || feof(fp))
+ break;
+
+ if (b != '\n')
+ {
+ keyword[0] = b;
+ fscanf(fp, "%62s", &keyword[1]);
+ }
+ if (keyword[0] == '\0') /* Blank line. */
+ {
+ if (thisholepos < 0)
+ thisholepos = pos;
+ continue;
+ }
+
+ /* Strip off "s-" if we've seen a "sections" keyword */
+ if (gotsections && keyword[0] == 's' && keyword[1] == '-')
+ {
+ for (c = &keyword[2]; (c[-2] = *c) != '\0'; c++)
+ ;
+ wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG , "db: stripped off the 's-'. Result is %s\n", keyword);
+ }
+
+ /* If this is the start of a CD entry, see if it matches. */
+ if (! strcmp(keyword, "tracks"))
+ {
+ if (prefs == 2)
+ break;
+
+ /* Is this the end of a hole? */
+ if (holesize_wanted && (thisholepos >= 0))
+ {
+ /* Yep. Is it better than the last one? */
+ if (pos - thisholepos < holesize && pos -
+ thisholepos >= holesize_wanted)
+ {
+ holepos = thisholepos;
+ holesize = pos - thisholepos;
+ }
+ thisholepos = -1;
+ }
+
+ /* Is it the start of the CD entries? */
+ if (firstpos == -1)
+ firstpos = pos;
+
+ /* Is this the end of the entry we really wanted? */
+ if (! searching)
+ {
+ rclen = pos - rcpos;
+ break;
+ }
+
+ /* If we have a near match, indicate that we
+ should stop reading tracks, etc now */
+ if (searching == 2)
+ {
+ searching = 3;
+ continue;
+ }
+
+ fscanf(fp, "%d", &ntracks);
+
+ if (ntracks != cd->ntracks)
+ {
+chomp:
+ SWALLOW_LINE(fp);
+ continue;
+ }
+
+ fudge = 0;
+ maxfudge = (ntracks * fuzz_frames) >> 1;
+ track = 0;
+ for (i = 0; i < ntracks; i++)
+ {
+ fscanf(fp, "%d", &scratch);
+ if (scratch != cd->trk[track].start)
+ {
+ sizediff = abs(scratch - cd->trk[track].start);
+ if (sizediff > fuzz_frames ||
+ (sizediff && scan))
+ break;
+ fudge += sizediff;
+ }
+ while (cd->trk[++track].section > 1)
+ ;
+ }
+ if (i != ntracks)
+ goto chomp;
+
+ if (fudge > 0) /* best near match? */
+ {
+ if (fudge > maxfudge)
+ goto chomp;
+ if (bestfudge == 0 || fudge < bestfudge)
+ bestfudge = fudge;
+ else
+ goto chomp;
+ rcpos = pos;
+ track = 0;
+ searching = 2;
+ }
+ else /* probably exact match */
+ {
+ fscanf(fp, "%d", &scratch);
+
+ if (scratch != -1 && scratch != cd->length)
+ goto chomp;
+
+ /* Found it! */
+ rcpos = pos;
+ track = 0;
+ searching = 0;
+ }
+
+ SWALLOW_LINE(fp); /* Get rid of newline */
+ }
+
+ /* Global mode stuff goes here */
+ else if (! strcmp(keyword, "cddbprotocol"))
+ {
+ getc(fp);
+ i = getc(fp); /* only first letter is used */
+ cddb.protocol = i == 'c' ? 1 :
+ i == 'h' ? 2 : 3 ;
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ }
+
+ else if (! strcmp(keyword, "cddbserver"))
+ {
+ getc(fp); /* lose the space */
+ if (cddb.cddb_server[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cddb.cddb_server,
+ sizeof(cddb.cddb_server), fp);
+ if ((i = strlen(cddb.cddb_server)))
+ cddb.cddb_server[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "cddbmailadress"))
+ {
+ getc(fp); /* lose the space */
+ if (cddb.mail_adress[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cddb.mail_adress,
+ sizeof(cddb.mail_adress), fp);
+ if ((i = strlen(cddb.mail_adress)))
+ cddb.mail_adress[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "cddbpathtocgi"))
+ {
+ getc(fp); /* lose the space */
+ if (cddb.path_to_cgi[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cddb.path_to_cgi,
+ sizeof(cddb.path_to_cgi), fp);
+ if ((i = strlen(cddb.path_to_cgi)))
+ cddb.path_to_cgi[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "cddbproxy"))
+ {
+ getc(fp); /* lose the space */
+ if (cddb.proxy_server[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cddb.proxy_server,
+ sizeof(cddb.proxy_server), fp);
+ if ((i = strlen(cddb.proxy_server)))
+ cddb.proxy_server[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "whendone"))
+ {
+ getc(fp);
+ i = getc(fp); /* only first letter is used */
+ if (cur_stopmode == -1) /* user's setting preferred */
+ cur_stopmode = i == 's' ? 0 : i == 'r' ? 1 : 2;
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ }
+
+ else if (! strcmp(keyword, "playnew"))
+ {
+ if (cur_playnew == -1)
+ cur_playnew = 1;
+ SWALLOW_LINE(fp);
+ }
+
+ /* If we're searching, skip to the next "tracks" line. */
+ else if (((searching & 1)|| scan)
+ && !(prefs && firstpos == -1))
+ SWALLOW_LINE(fp)
+
+ else if (! strcmp(keyword, "sections"))
+ {
+ gotsections = 1;
+ fscanf(fp, "%d", &ntracks);
+
+ free(trackmap);
+ trackmap = (int *) malloc(sizeof(int) *
+ (cur_ntracks + ntracks));
+ if (trackmap == NULL)
+ {
+ perror("section mapping");
+ exit(1);
+ }
+
+ /*
+ * If sections are already defined, use these as a
+ * reference, mapping this CD entry's section numbers
+ * to the ones in core.
+ *
+ * Otherwise, split the CD up according to the sections
+ * listed here.
+ */
+ if (cur_nsections)
+ {
+ track = 0;
+ i = 0;
+ while (ntracks)
+ {
+ ntracks--;
+ fscanf(fp, "%d", &scratch);
+ while (scratch > cd->trk[track].start)
+ {
+ if (cd->trk[track].section < 2)
+ trackmap[i++] = track;
+ ++track;
+
+ if (track == cur_ntracks)
+ break;
+ }
+
+ /* rc has later sections than db... */
+ if (track == cur_ntracks)
+ break;
+
+ /* Matches can be approximate */
+ if (scratch+75 > cd->trk[track].start &&
+ scratch-75 < cd->trk[track].start)
+ trackmap[i++] = track++;
+ else
+ trackmap[i++] = -1;
+
+ if (track == cur_ntracks)
+ break;
+ }
+
+ /* This only happens if track == cur_ntracks */
+ while (ntracks--)
+ trackmap[i++] = -1;
+
+ while (track < cur_ntracks)
+ {
+ if (cd->trk[track].section < 2)
+ trackmap[i++] = track;
+ track++;
+ }
+
+ track = 0;
+ SWALLOW_LINE(fp);
+ }
+ else
+ {
+ while (ntracks--)
+ {
+ fscanf(fp, "%d", &scratch);
+ split_trackinfo(scratch);
+ }
+
+ for (i = 0; i < cur_ntracks; i++)
+ {
+ trackmap[i] = i;
+ /* split_trackinfo() sets this */
+ cd->trk[i].contd = 0;
+ }
+
+ SWALLOW_LINE(fp);
+ }
+ }
+
+ else if (! strcmp(keyword, "track"))
+ {
+ char buf[502];
+
+ getc(fp); /* lose the space */
+
+ /* don't overwrite existing track names. */
+ /* However, overwrite them if there was a "bad" fuzzy match before */
+ if ((trackmap[track] == -1 || track > (cd->ntracks + cur_nsections)) && (searching == 2))
+ SWALLOW_LINE(fp)
+ else if (cd->trk[trackmap[track]].songname &&
+ cd->trk[trackmap[track]].songname[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(buf, sizeof(buf), fp);
+ wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG, "found track %s\n", buf);
+ if( (i = strlen(buf)) )
+ buf[i - 1] = '\0';
+ wm_strmcpy(&cd->trk[trackmap[track]].songname, buf);
+ }
+ track++;
+ }
+
+ else if (! strcmp(keyword, "playmode"))
+ fscanf(fp, "%d", &cd->playmode);
+
+ else if (! strcmp(keyword, "autoplay"))
+ cd->autoplay = 1;
+
+ else if (! strcmp(keyword, "cdname"))
+ {
+ /* because of fuzzy matching that may change
+ the disk contents, we reset everything when
+ we find the name, in hopes that we will recover
+ most, if not all, of the information from the
+ file. */
+/*
+ * nasty bug was here. Was it? BUGBUGBUG
+ *
+ * wipe_cdinfo();
+ * trackmap = reset_tracks();
+ */
+
+ getc(fp); /* lose the space */
+ /* don't overwrite existing cd name. */
+ if (cd->cdname[0] && (searching == 2))
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ if (searching > 1)
+ {
+ strcpy(cd->cdname, "Probably://");
+ fgets(cd->cdname + strlen(cd->cdname), sizeof(cd->cdname), fp);
+ }
+ else
+ {
+ fgets(cd->cdname, sizeof(cd->cdname), fp);
+ }
+ if ( (i = strlen(cd->cdname)) )
+ cd->cdname[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "artist"))
+ {
+ getc(fp); /* lose the space */
+ /* don't overwrite existing artist names. */
+ if (cd->artist[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cd->artist, sizeof(cd->artist), fp);
+ if( (i = strlen(cd->artist)) )
+ cd->artist[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "cdvolume"))
+ {
+ fscanf(fp, "%d", &cd->volume);
+ cd->volume = (cd->volume * 100) / 32;
+ }
+
+ else if (! strcmp(keyword, "dontplay"))
+ {
+ fscanf(fp, "%d", &i);
+ if (trackmap[i - 1] != -1)
+ cd->trk[trackmap[i - 1]].avoid = 1;
+ }
+
+ else if (! strcmp(keyword, "continue"))
+ {
+ if (trackmap[track - 1] != -1)
+ cd->trk[trackmap[track - 1]].contd = 1;
+ }
+
+ else if (! strcmp(keyword, "volume"))
+ {
+ fscanf(fp, "%d", &i);
+ if (trackmap[i - 1] == -1)
+ SWALLOW_LINE(fp)
+ else
+ {
+ i = trackmap[i - 1];
+ fscanf(fp, "%d", &cd->trk[i].volume);
+ cd->trk[i].volume = (cd->trk[i].volume*100)/32;
+ if (cd->trk[i].volume > 32)
+ cd->trk[i].volume = 0;
+ }
+ }
+
+ else if (! strcmp(keyword, "playlist"))
+ {
+ getc(fp);
+ fscanf(fp, "%63s", listname);
+
+/* XXX take this out at some point */
+ if (! strcmp(listname, "Default"))
+ strcpy(listname, "List A");
+
+ for (i = 0; listname[i]; i++)
+ if (listname[i] == '_')
+ listname[i] = ' ';
+
+ l = new_playlist(cd, listname);
+ if (l == NULL)
+ {
+plnomem:
+ perror("playlist read");
+ exit(1);
+ }
+
+ fscanf(fp, "%d", &listsize);
+
+ l->list = malloc(sizeof(int) * (listsize + 1));
+ if (l->list == NULL)
+ goto plnomem;
+
+ /* Leave out tracks that weren't in .workmandb. */
+ j = 0;
+ for (i = 0; i < listsize; i++)
+ {
+ fscanf(fp, "%d", &scratch);
+ scratch = trackmap[scratch - 1];
+ if (scratch != -1)
+ l->list[j++] = scratch + 1;
+ }
+
+ l->list[j] = 0;
+ }
+
+ else if (! strcmp(keyword, "mark"))
+ {
+ int mark_val = -1, mark_namelen;
+ char mark_name[32];
+
+ fscanf(fp, "%d", &mark_val);
+ if (mark_val == -1)
+ goto chomp;
+
+ if (getc(fp) != ' ')
+ continue;
+
+ fgets(mark_name, sizeof(mark_name), fp);
+ if( ( mark_namelen = strlen(mark_name)) )
+ mark_name[mark_namelen - 1] = '\0';
+
+/*
+ if (! strcmp(mark_name, "START"))
+ set_abtimer(0, mark_val);
+ else if (! strcmp(mark_name, "END"))
+ set_abtimer(1, mark_val);
+
+*/
+ }
+
+ /* Unrecognized keyword. Put it in the right place. */
+ else
+ {
+ char **buf, input[BUFSIZ];
+
+ if (track && trackmap[track - 1] == -1)
+ {
+ SWALLOW_LINE(fp);
+ continue;
+ }
+
+ i = track ? trackmap[track - 1] : 0;
+ buf = prefs ? i ? &cd->trk[i].otherrc : &cd->otherrc :
+ i ? &cd->trk[i].otherdb : &cd->otherdb;
+ if (firstpos == -1) {
+ if (prefs) {
+ buf = &otherrc;
+ } else {
+ goto chomp;
+ } /* if() else */
+ } /* if() */
+ wm_strmcat(buf, keyword);
+ do {
+ input[sizeof(input) - 1] = 'x';
+ fgets(input, sizeof(input), fp);
+ wm_strmcat(buf, input);
+ } while (input[sizeof(input) - 1] != 'x');
+ }
+ }
+
+ if (rclen == 0 && !searching)
+ rclen = pos - rcpos;
+
+ if (searching > 1) /* A near match has been found. Good enough. */
+ searching = 0;
+
+ cddb_struct2cur();
+ return (! searching);
+
+} /* search_db() */
+
+/*
+ * Delay some amount of time without using interval timers.
+ */
+void
+spinwheels(int secs) {
+ struct timeval tv;
+
+ tv.tv_usec = 0;
+ tv.tv_sec = secs;
+ select(0, NULL, NULL, NULL, &tv);
+} /* spinwheels() */
+
+/*
+ * lockit(fd, type)
+ *
+ * fd file descriptor
+ * type lock type
+ *
+ * Lock a file. Time out after a little while if we can't get a lock;
+ * this usually means the locking system is broken.
+ *
+ * Unfortunately, if there are lots of people contending for a lock,
+ * this can result in the file not getting locked when it probably should.
+ */
+int
+lockit(int fd, int type)
+{
+ struct flock fl;
+ int result, timer = 0;
+
+ if (suppress_locking)
+ return (0);
+
+ fl.l_type = type;
+ fl.l_whence = 0;
+ fl.l_start = 0;
+ fl.l_len = 0;
+
+ while ((result = fcntl(fd, F_SETLK, &fl)) < 0)
+ {
+ if (errno != EACCES || errno != EAGAIN)
+ break;
+ if (timer++ == 30)
+ {
+ errno = ETIMEDOUT;
+ break;
+ }
+
+ spinwheels(1);
+ }
+
+ return (result);
+} /* lockit() */
+
+/*
+ * Search all the database files and our personal preference file for
+ * more information about the current CD.
+ */
+void
+load( void )
+{
+ FILE *fp;
+ char **dbfile;
+ int locked = 0;
+ int dbfound = 0, *trklist, i;
+ unsigned long dbpos;
+
+/* This is some kind of profiling code. I don't change it
+ to wm_lib_message() for now... */
+#ifndef NDEBUG
+ long t1, t2;
+ if( getenv( "WORKMAN_DEBUG" ) != NULL )
+ {
+ time(&t1);
+ printf("%s (%d): search start = %ld\n", __FILE__, __LINE__, t1);
+ fflush(stdout);
+ }
+#endif
+
+ dbfile = databases;
+
+ found_in_db = 0;
+
+ /* Turn the cd->trk array into a simple array of ints. */
+ trklist = (int *)malloc(sizeof(int) * cd->ntracks);
+ for (i = 0; i < cd->ntracks; i++)
+ trklist[i] = cd->trk[i].start;
+
+ do {
+ if (*dbfile && idx_find_entry(*dbfile, cd->ntracks, trklist,
+ cd->length * 75, 0, &dbpos) == 0)
+ dbfound = 1;
+
+ fp = *dbfile ? open_rcfile(*dbfile, "r") : NULL;
+ if (fp != NULL)
+ {
+ if (lockit(fileno(fp), F_RDLCK))
+ perror("Couldn't get read (db) lock");
+ else
+ locked = 1;
+
+ if (dbfound)
+ fseek(fp, dbpos, 0);
+
+ if (search_db(fp, 0, 0, 0))
+ {
+ found_in_db = 1;
+ cd->whichdb = *dbfile;
+ }
+
+ if (locked && lockit(fileno(fp), F_UNLCK))
+ perror("Couldn't relinquish (db) lock");
+
+ fclose(fp);
+ }
+ } while (*++dbfile != NULL && cd->whichdb == NULL);
+
+#ifndef NDEBUG
+ if( getenv( "WORKMAN_DEBUG" ) != NULL )
+ {
+ time(&t2);
+ printf("%s (%d): db search end = %ld, elapsed = %ld\n", __FILE__, __LINE__, t2, t2 - t1);
+ fflush(stdout);
+ }
+#endif
+
+ fp = rcfile ? open_rcfile(rcfile, "r") : NULL;
+ if (fp != NULL)
+ {
+ locked = 0;
+ if (lockit(fileno(fp), F_RDLCK))
+ perror("Couldn't get read (rc) lock");
+ else
+ locked = 1;
+
+ rcpos = 0;
+ found_in_rc = search_db(fp, 1, 0, 0);
+ if (! found_in_rc)
+ cd->autoplay = wm_db_get_playnew();
+
+ if (locked && lockit(fileno(fp), F_UNLCK))
+ perror("Couldn't relinquish (rc) lock");
+
+ fclose(fp);
+ }
+
+ free(trklist);
+
+ if (cur_playnew == -1)
+ cur_playnew = 0;
+
+#ifndef NDEBUG
+ if( getenv( "WORKMAN_DEBUG" ) != NULL )
+ {
+ time(&t2);
+ printf("%s (%d): search end = %ld, elapsed = %ld\n", __FILE__, __LINE__, t2, t2 - t1);
+ fflush(stdout);
+ }
+#endif
+} /* load() */
+
+/*
+ * Load program settings from the rcfile.
+ */
+void
+load_settings( void )
+{
+ FILE *fp;
+ int locked;
+
+ fp = rcfile ? open_rcfile(rcfile, "r") : NULL;
+ if (fp != NULL)
+ {
+ locked = 0;
+ if (lockit(fileno(fp), F_RDLCK))
+ perror("Couldn't get read (rc) lock");
+ else
+ locked = 1;
+
+ rcpos = 0;
+ found_in_rc = search_db(fp, 2, 0, 0);
+ if (! found_in_rc)
+ cd->autoplay = wm_db_get_playnew();
+
+ if (locked && lockit(fileno(fp), F_UNLCK))
+ perror("Couldn't relinquish (rc) lock");
+
+ fclose(fp);
+ }
+} /* load_settings() */
+
+/*
+ * save_globals()
+ *
+ * Save the global preferences, scooting CD entries to the end if needed.
+ * The assumption here is that the rcfile is locked, and that firstpos has
+ * been set by a previous scan.
+ */
+void
+save_globals(FILE *fp)
+{
+ char *globes = NULL, *cdentry = NULL, temp[100];
+ long curpos;
+ int globesize, hit_cdent = 0, c = 0;
+
+ if (otherrc)
+ wm_strmcpy(&globes, otherrc);
+
+ if (cddb.protocol)
+ {
+ sprintf(temp, "cddbprotocol ");
+ switch(cddb.protocol)
+ {
+ case 1: /* cddbp */
+ sprintf(temp + strlen(temp), "cddbp\n");
+ break;
+ case 2: /* http */
+ sprintf(temp + strlen(temp), "http\n");
+ break;
+ case 3: /* proxy */
+ sprintf(temp + strlen(temp), "proxy\n");
+ break;
+ default:
+ break;
+ }
+ wm_strmcat(&globes, temp);
+
+ if(cddb.mail_adress[0])
+ {
+ sprintf(temp,"cddbmailadress %s\n",
+ cddb.mail_adress);
+ wm_strmcat(&globes, temp);
+ }
+
+ if(cddb.cddb_server[0])
+ {
+ sprintf(temp,"cddbserver %s\n",
+ cddb.cddb_server);
+ wm_strmcat(&globes, temp);
+ }
+
+ if(cddb.path_to_cgi[0])
+ {
+ sprintf(temp,"cddbpathtocgi %s\n",
+ cddb.mail_adress);
+ wm_strmcat(&globes, temp);
+ }
+
+ if(cddb.proxy_server[0])
+ {
+ sprintf(temp,"cddbproxy %s\n",
+ cddb.mail_adress);
+ wm_strmcat(&globes, temp);
+ }
+ }
+
+ if (cur_stopmode == 1 || cur_stopmode == 2)
+ {
+ sprintf(temp, "whendone %s\n", cur_stopmode == 1 ? "repeat" :
+ "eject");
+ wm_strmcat(&globes, temp);
+ }
+
+ if (cur_playnew == 1)
+ wm_strmcat(&globes, "playnew\n");
+
+ curpos = firstpos;
+ if (curpos < 0)
+ curpos = 0;
+
+ fseek(fp, curpos, SEEK_SET);
+
+ if (firstpos < (globesize = globes != NULL ? strlen(globes) : 0))
+ {
+ while (1)
+ {
+ temp[sizeof(temp)-1] = 'x';
+
+ if (fgets(temp, sizeof(temp), fp) == NULL)
+ {
+ fseek(fp, 0, SEEK_SET);
+ if (globes != NULL)
+ {
+ fwrite(globes, globesize, 1, fp);
+ free(globes);
+ }
+ if (cdentry != NULL)
+ {
+ fwrite(cdentry, strlen(cdentry), 1, fp);
+ free(cdentry);
+ }
+ return;
+ }
+
+ if (! strncmp(temp, "tracks ", 7))
+ {
+ hit_cdent = 1;
+ if (curpos >= globesize)
+ break;
+ }
+
+ if (! hit_cdent)
+ {
+ curpos += strlen(temp);
+ if (temp[sizeof(temp)-1] == '\0')
+ while ((c = getc(fp)) != '\n' &&
+ c != EOF)
+ curpos++;
+ if (c == '\n')
+ curpos++;
+
+ continue;
+ }
+
+ wm_strmcat(&cdentry, temp);
+ curpos += strlen(temp);
+ while (temp[sizeof(temp)-1] == '\0')
+ {
+ temp[sizeof(temp)-1] = 'x';
+ if (fgets(temp, sizeof(temp), fp) == NULL)
+ break;
+ wm_strmcat(&cdentry, temp);
+ curpos += strlen(temp);
+ }
+ }
+
+ if (cdentry != NULL)
+ {
+ fseek(fp, 0, SEEK_END);
+ fwrite(cdentry, strlen(cdentry), 1, fp);
+ free(cdentry);
+ }
+ }
+
+ if (globes != NULL)
+ {
+ fseek(fp, 0, SEEK_SET);
+ fwrite(globes, globesize, 1, fp);
+ free(globes);
+ }
+
+ while (globesize++ < curpos)
+ putc('\n', fp);
+} /* save_globals() */
+
+/*
+ * save_entry()
+ *
+ * Save the CD information to one database.
+ *
+ * filename Database to save to.
+ * pref 0 for hard data, 1 for preferences.
+ *
+ * If an entry for this CD exists already, overwrite it with the new entry
+ * if the new entry is the same size or smaller, or with newlines if the new
+ * entry is larger (in which case the new entry is appended to the file.)
+ *
+ * Also, if the preference information is being updated, save it to the
+ * file while we've got it locked. Scoot stuff from the beginning of
+ * the file to the end as needed to facilitate this.
+ *
+ * XXX Preference-saving should probably be done elsewhere, like in an
+ * Apply button on the Goodies popup, and in any case needs to go to a
+ * different file (.Xdefaults?)
+ *
+ * Returns 0 on success.
+ */
+int
+save_entry(char *filename, int pref)
+{
+ FILE *fp;
+ char *buf;
+ int len, i, locked = 0;
+
+
+ if( filename == NULL )
+ return (-1);
+
+ fp = open_rcfile(filename, "r+");
+ if (fp == NULL)
+ {
+ if (errno == ENOENT) /* doesn't exist already */
+ fp = open_rcfile(filename, "w");
+ if (fp == NULL)
+ return (-1);
+ }
+
+ if (lockit(fileno(fp), F_WRLCK))
+ perror("Warning: Couldn't get write lock");
+ else
+ locked = 1;
+
+ buf = print_cdinfo(cd, pref);
+ len = strlen(buf); /* doesn't return if there's an error */
+
+ rcpos = -1;
+ search_db(fp, pref, 1, len);
+ if (rcpos != -1) /* XXX */
+ {
+ /*
+ * Jump to the entry's position in the database file, if
+ * it was found.
+ */
+ fseek(fp, rcpos, SEEK_SET);
+
+ if (rclen >= len && holepos == -1)
+ {
+ /*
+ * If the new entry will fit in the space occupied by
+ * the old one, overwrite the old one and make a hole
+ * of the appropriate size at its end.
+ *
+ * No need to update the index file in this case, as
+ * the entry's position hasn't changed.
+ */
+ fputs(buf, fp);
+ for (i = len; i < rclen; i++)
+ fputc('\n', fp);
+ }
+ else
+ {
+ /*
+ * Overwrite the old entry with a hole and delete
+ * its pointer in the index file.
+ */
+ for (i = 0; i < rclen; i++)
+ fputc('\n', fp);
+ idx_delete_entry(filename, cd->trk[cd->ntracks-1].start,
+ 0, rcpos);
+
+ rcpos = -1;
+ }
+ }
+
+ /*
+ * Old entry wasn't found, or its new version wouldn't fit where
+ * the old one was.
+ */
+ if (rcpos == -1)
+ {
+ /*
+ * Write the new entry in a hole, if there is one,
+ * or at the end of the file.
+ */
+ if (holepos >= 0)
+ {
+ fseek(fp, holepos, SEEK_SET);
+ if (holepos < firstpos)
+ firstpos = holepos;
+ }
+ else
+ {
+ fseek(fp, 0, SEEK_END);
+ holepos = ftell(fp);
+ }
+ fputs(buf, fp);
+
+ /*
+ * Write a new index entry for this CD.
+ */
+ idx_write_entry(filename, cd->trk[cd->ntracks - 1].start,
+ holepos);
+ }
+
+ if (pref)
+ save_globals(fp);
+
+ fflush(fp);
+
+ if (locked && lockit(fileno(fp), F_UNLCK))
+ perror("Warning: Couldn't relinquish write lock");
+
+ fclose(fp);
+
+ return (0);
+} /* save_entry() */
+
+/*
+ * save()
+ *
+ * Save CD information to the appropriate datafile (the first file in the
+ * list, unless the entry came from another database file) and to the
+ * personal prefs file.
+ */
+int
+save( void )
+{
+
+ if( wm_db_save_disabled == FALSE )
+ {
+ if (save_entry(rcfile, 1))
+ return (0);
+
+ if (cd->whichdb == NULL || access(cd->whichdb, W_OK))
+ cd->whichdb = databases[0];
+
+ if (save_entry(cd->whichdb, 0))
+ return (0);
+
+ return( WM_DB_SAVE_ERROR );
+ } else {
+ return( WM_DB_SAVE_DISABLED );
+ }
+} /* save() */
diff --git a/kscd/libwm/drv_sony.c b/kscd/libwm/drv_sony.c
new file mode 100644
index 00000000..a9db5d73
--- /dev/null
+++ b/kscd/libwm/drv_sony.c
@@ -0,0 +1,166 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Vendor-specific drive control routines for Sony CDU-8012 series.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+
+#define PAGE_AUDIO 0x0e
+
+/* local prototypes */
+static int sony_init( struct wm_drive *d );
+static int sony_set_volume(struct wm_drive *d, int left, int right );
+static int sony_get_volume( struct wm_drive *d, int *left, int *right );
+
+
+extern int min_volume, max_volume;
+
+struct wm_drive_proto sony_proto = {
+ sony_init, /* functions... */
+ gen_close,
+ gen_get_trackcount,
+ gen_get_cdlen,
+ gen_get_trackinfo,
+ gen_get_drive_status,
+ sony_get_volume,
+ sony_set_volume,
+ gen_pause,
+ gen_resume,
+ gen_stop,
+ gen_play,
+ gen_eject,
+ gen_closetray,
+ NULL /* gen_get_cdtext */
+};
+
+/*
+ * Initialize the driver.
+ */
+static int sony_init( struct wm_drive *d ) {
+
+/* Sun use Sony drives as well */
+#if defined(SYSV) && defined(CODEC)
+ codec_init();
+#endif
+ min_volume = 128;
+ max_volume = 255;
+ return( 0 );
+} /* sony_init() */
+
+/*
+ * On the Sony CDU-8012 drive, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. Additionally, only the top half
+ * of the volume scale is valid; the bottom half is all silent. The actual
+ * formula looks like
+ *
+ * max^2 - (max - vol)^2 max
+ * v = --------------------- + ---
+ * max * 2 2
+ *
+ * Where "max" is the maximum value of the volume scale, usually 100.
+ */
+static int
+scale_volume(vol, max)
+ int vol, max;
+{
+ vol = (max*max - (max - vol) * (max - vol)) / max;
+ return ((vol + max) / 2);
+}
+
+/*
+ * Given a value between min_volume and max_volume, return the standard-scale
+ * volume value needed to achieve that hardware value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume(cd_vol, max)
+ int cd_vol, max;
+{
+ int vol = 0, top = max, bot = 0, scaled = 0;
+
+ cd_vol = (cd_vol * 100 + (max_volume - 1)) / max_volume;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol <= scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ /* Might have looked down too far for repeated scaled values */
+ if (cd_vol < scaled)
+ vol++;
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+}
+
+/*
+ * Get the volume. Sun's CD-ROM driver doesn't support this operation, even
+ * though their drive does. Dumb.
+ */
+static int
+sony_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ unsigned char mode[16];
+
+ /* Get the current audio parameters first. */
+ if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
+ return (-1);
+
+ *left = unscale_volume(mode[9], 100);
+ *right = unscale_volume(mode[11], 100);
+
+ return (0);
+}
+
+/*
+ * Set the volume using the wacky scale outlined above. The Sony drive
+ * responds to the standard set-volume command.
+ */
+static int
+sony_set_volume(struct wm_drive *d, int left, int right )
+{
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+ return (gen_set_volume(d, left, right));
+}
diff --git a/kscd/libwm/drv_toshiba.c b/kscd/libwm/drv_toshiba.c
new file mode 100644
index 00000000..ef009867
--- /dev/null
+++ b/kscd/libwm/drv_toshiba.c
@@ -0,0 +1,149 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Vendor-specific drive control routines for Toshiba XM-3401 series.
+ */
+
+static char drv_toshiba_id[] = "$Id$";
+
+#include <stdio.h>
+#include <errno.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+
+#define SCMD_TOSH_EJECT 0xc4
+
+/* local prototypes */
+static int tosh_init( struct wm_drive *d );
+static int tosh_eject( struct wm_drive *d );
+static int tosh_set_volume( struct wm_drive *d, int left, int right );
+static int tosh_get_volume( struct wm_drive *d, int *left, int *right );
+
+struct wm_drive_proto toshiba_proto = {
+ tosh_init, /* functions... */
+ gen_close,
+ gen_get_trackcount,
+ gen_get_cdlen,
+ gen_get_trackinfo,
+ gen_get_drive_status,
+ tosh_get_volume,
+ tosh_set_volume,
+ gen_pause,
+ gen_resume,
+ gen_stop,
+ gen_play,
+ tosh_eject,
+ gen_closetray,
+ NULL /* gen_get_cdtext */
+};
+
+/*
+ * Initialize the driver.
+ */
+static int
+tosh_init( struct wm_drive *d )
+{
+ extern int min_volume;
+
+/* Sun use Toshiba drives as well */
+#if defined(SYSV) && defined(CODEC)
+ codec_init();
+#endif
+ min_volume = 0;
+ return ( 0 );
+}
+
+/*
+ * Send the Toshiba code to eject the CD.
+ */
+static int
+tosh_eject( struct wm_drive *d )
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_TOSH_EJECT, 1, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Set the volume. The low end of the scale is more sensitive than the high
+ * end, so make up for that by transforming the volume parameters to a square
+ * curve.
+ */
+static int
+tosh_set_volume( struct wm_drive *d, int left, int right )
+{
+ left = (left * left * left) / 10000;
+ right = (right * right * right) / 10000;
+ return (gen_set_volume(d, left, right));
+}
+
+/*
+ * Undo the transformation above using a binary search (so no floating-point
+ * math is required.)
+ */
+static int
+unscale_volume(cd_vol, max)
+ int cd_vol, max;
+{
+ int vol = 0, top = max, bot = 0, scaled = 0;
+
+ /*cd_vol = (cd_vol * 100 + (max_volume - 1)) / max_volume;*/
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = (vol * vol) / max;
+ if (cd_vol <= scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ /* Might have looked down too far for repeated scaled values */
+ if (cd_vol < scaled)
+ vol++;
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+}
+
+/*
+ * Get the volume.
+ */
+static int
+tosh_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ int status;
+
+ status = gen_get_volume(d, left, right);
+ if (status < 0)
+ return (status);
+ *left = unscale_volume(*left, 100);
+ *right = unscale_volume(*right, 100);
+
+ return (0);
+}
diff --git a/kscd/libwm/include/wm_cdda.h b/kscd/libwm/include/wm_cdda.h
new file mode 100644
index 00000000..11c8eb3e
--- /dev/null
+++ b/kscd/libwm/include/wm_cdda.h
@@ -0,0 +1,203 @@
+#ifndef WM_CDDA_H
+#define WM_CDDA_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * Information about a particular block of CDDA data.
+ */
+struct cdda_block {
+ unsigned char status;
+ unsigned char track;
+ unsigned char index;
+ unsigned char reserved;
+
+ int frame;
+ char *buf;
+ long buflen;
+};
+
+struct cdda_device {
+ int fd;
+ const char *devname;
+
+ unsigned char status;
+ unsigned char track;
+ unsigned char index;
+ unsigned char command;
+
+ int frame;
+ int frames_at_once;
+
+ /* Average volume levels, for level meters */
+ unsigned char lev_chan0;
+ unsigned char lev_chan1;
+
+ /* Current volume setting (0-255) */
+ unsigned char volume;
+
+ /* Current balance setting (0-255, 128 = balanced) */
+ unsigned char balance;
+
+ struct cdda_block *blocks;
+ int numblocks;
+};
+
+#include "wm_cdrom.h"
+#include "wm_config.h"
+#include "wm_struct.h"
+/*
+ * cdda_block status codes.
+ */
+
+/*
+ * Enable or disable CDDA building depending on platform capabilities, and
+ * determine endianness based on architecture. (Gross!)
+ *
+ * For header-comfort, the macros LITTLE_ENDIAN and BIG_ENDIAN had to be
+ * renamed. At least Linux does have bytesex.h and endian.h for easy
+ * byte-order examination.
+ */
+
+#ifdef HAVE_MACHINE_ENDIAN_H
+ #include <machine/endian.h>
+ #if BYTE_ORDER == LITTLE_ENDIAN
+ #define WM_LITTLE_ENDIAN 1
+ #define WM_BIG_ENDIAN 0
+ #else
+ #define WM_LITTLE_ENDIAN 0
+ #define WM_BIG_ENDIAN 1
+ #endif
+#elif defined(__sun) || defined(sun)
+# ifdef SYSV
+# include <sys/types.h>
+# include <sys/cdio.h>
+# ifndef CDROMCDDA
+# undef BUILD_CDDA
+# endif
+# ifdef i386
+# define WM_LITTLE_ENDIAN 1
+# define WM_BIG_ENDIAN 0
+# else
+# define WM_BIG_ENDIAN 1
+# define WM_LITTLE_ENDIAN 0
+# endif
+# else
+# undef BUILD_CDDA
+# endif
+
+/* Linux only allows definition of endianness, because there's no
+ * standard interface for CDROM CDDA functions that aren't available
+ * if there is no support.
+ */
+#elif defined(__linux__)
+/*# include <bytesex.h>*/
+# include <endian.h>
+/*
+ * XXX could this be a problem? The results are only 0 and 1 because
+ * of the ! operator. How about other linux compilers than gcc ?
+ */
+# define WM_LITTLE_ENDIAN !(__BYTE_ORDER - __LITTLE_ENDIAN)
+# define WM_BIG_ENDIAN !(__BYTE_ORDER - __BIG_ENDIAN)
+#elif defined WORDS_BIGENDIAN
+ #define WM_LITTLE_ENDIAN 0
+ #define WM_BIG_ENDIAN 1
+#else
+ #define WM_LITTLE_ENDIAN 1
+ #define WM_BIG_ENDIAN 0
+#endif
+
+/*
+ * The following code shouldn't take effect now.
+ * In 1998, the WorkMan platforms don't support __PDP_ENDIAN
+ * architectures.
+ *
+ */
+
+#if !defined(WM_LITTLE_ENDIAN)
+# if !defined(WM_BIG_ENDIAN)
+# error yet unsupported architecture
+ foo bar this is to stop the compiler.
+# endif
+#endif
+
+#if defined(BUILD_CDDA)
+/*
+ * The following code support us by optimize cdda operations
+ */
+#define CDDARETURN(x) if(x && x->cdda == 1) return
+#define IFCDDA(x) if(x && x->cdda == 1)
+int cdda_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind);
+int cdda_play(struct wm_drive *d, int start, int end, int realstart);
+int cdda_pause(struct wm_drive *d);
+int cdda_stop(struct wm_drive *d);
+int cdda_eject(struct wm_drive *d);
+int cdda_set_volume(struct wm_drive *d, int left, int right);
+int cdda_get_volume(struct wm_drive *d, int *left, int *right);
+void cdda_kill(struct wm_drive *d);
+void cdda_save(struct wm_drive *d, char *filename);
+int cdda_get_ack(int);
+int gen_cdda_init(struct wm_drive *d );
+
+void cdda_set_direction(struct wm_drive *d, int newdir);
+void cdda_set_speed(struct wm_drive *d, int speed);
+void cdda_set_loudness(struct wm_drive *d, int loud);
+
+
+int wmcdda_init(struct cdda_device*);
+int wmcdda_open(const char*);
+int wmcdda_close(struct cdda_device*);
+int wmcdda_setup(int start, int end, int realstart);
+long wmcdda_read(struct cdda_device*, struct cdda_block *block);
+void wmcdda_speed(int speed);
+void wmcdda_direction(int newdir);
+
+#else
+ #define CDDARETURN(x)
+ #define IFCDDA(x)
+ #define cdda_get_drive_status
+ #define cdda_play
+ #define cdda_pause
+ #define cdda_resume
+ #define cdda_stop
+ #define cdda_eject
+ #define cdda_set_volume
+ #define cdda_get_volume
+ #define cdda_kill
+ #define cdda_save
+ #define cdda_get_ack
+#endif /* defined(BUILD_CDDA) */
+
+#include <stdio.h>
+
+#ifdef DEBUG
+ #define DEBUGLOG(fmt, args...) fprintf(stderr, fmt, ##args)
+#else
+ #define DEBUGLOG(fmt, args...)
+#endif
+#define ERRORLOG(fmt, args...) fprintf(stderr, fmt, ##args)
+
+#endif /* WM_CDDA_H */
diff --git a/kscd/libwm/include/wm_cddb.h b/kscd/libwm/include/wm_cddb.h
new file mode 100644
index 00000000..26a0c2d9
--- /dev/null
+++ b/kscd/libwm/include/wm_cddb.h
@@ -0,0 +1,36 @@
+#ifndef WM_CDDB_H
+#define WM_CDDB_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+unsigned long cddb_discid(void);
+void cddb_struct2cur(void);
+void cddb_cur2struct(void);
+void cddb_select(void);
+void connect_cddb(void);
+void update_cddbserver(void);
+void cddb_request(void);
+
+#endif /* WM_CDDB_H */
diff --git a/kscd/libwm/include/wm_cdinfo.h b/kscd/libwm/include/wm_cdinfo.h
new file mode 100644
index 00000000..dbb8e949
--- /dev/null
+++ b/kscd/libwm/include/wm_cdinfo.h
@@ -0,0 +1,76 @@
+#ifndef WM_CDINFO_H
+#define WM_CDINFO_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Prototypes from cdinfo.c
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#include "wm_struct.h"
+extern char *cur_trackname; /* Take a guess */
+extern int cur_index; /* Current index mark */
+extern int cur_frame; /* Current frame number */
+extern struct wm_play *playlist; /* = NULL */
+
+/*extern int cur_track;*/ /* Current track number, starting at 1 */
+extern char *cur_artist; /* Name of current CD's artist */
+extern char cur_avoid; /* Avoid flag */
+extern char cur_contd; /* Continued flag */
+extern char *cur_cdname; /* Album name */
+extern int cur_nsections; /* Number of sections currently defined */
+extern int exit_on_eject;
+extern int cur_pos_abs;
+extern int cur_pos_rel;
+extern int cur_cdlen;
+
+extern int cur_ntracks;
+extern int cur_lasttrack;
+extern int cur_firsttrack;
+extern int cur_listno;
+extern int cur_stopmode;
+
+void wipe_cdinfo( void );
+void play_next_entry( int forward );
+void make_playlist( int playmode, int starttrack );
+int get_autoplay( void );
+int get_playmode( void );
+void pl_find_track( int track );
+void play_prev_track( int forward );
+void play_next_track( int forward );
+int tracklen( int num );
+int get_default_volume( int track );
+int split_trackinfo( int pos );
+int remove_trackinfo( int num );
+void freeup( char **x );
+int get_runtime( void );
+const char *trackname( int num );
+void stash_cdinfo( char *artist, char *cdname, int autoplay, int playmode );
+void stash_trkinfo( int track, char *songname, int contd, int avoid );
+int get_avoid( int num );
+int get_contd( int num );
+void default_volume( int track, int vol );
+char *listentry( int num );
+
+#endif /* WM_CDINFO_H */
diff --git a/kscd/libwm/include/wm_cdrom.h b/kscd/libwm/include/wm_cdrom.h
new file mode 100644
index 00000000..d38f8b68
--- /dev/null
+++ b/kscd/libwm/include/wm_cdrom.h
@@ -0,0 +1,114 @@
+#ifndef WM_CDROM_H
+#define WM_CDROM_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Prototypes from cdrom.c
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+#define WM_CDS_NO_DISC(status) (status == WM_CDM_UNKNOWN ||\
+ status == WM_CDM_EJECTED ||\
+ status == WM_CDM_NO_DISC)
+
+#define WM_CDS_DISC_READY(status)(status == WM_CDM_TRACK_DONE ||\
+ status == WM_CDM_PLAYING ||\
+ status == WM_CDM_FORWARD ||\
+ status == WM_CDM_PAUSED ||\
+ status == WM_CDM_STOPPED)
+
+#define WM_CDS_DISC_PLAYING(status)(status == WM_CDM_TRACK_DONE ||\
+ status == WM_CDM_PLAYING ||\
+ status == WM_CDM_FORWARD ||\
+ status == WM_CDM_PAUSED)
+#define WM_CDM_BACK 1
+#define WM_CDM_TRACK_DONE 1
+#define WM_CDM_PLAYING 2
+#define WM_CDM_FORWARD 3
+#define WM_CDM_PAUSED 4
+#define WM_CDM_STOPPED 5
+#define WM_CDM_EJECTED 6
+#define WM_CDM_DEVICECHANGED 9 /* deprecated */
+#define WM_CDM_NO_DISC 10
+#define WM_CDM_UNKNOWN 11
+#define WM_CDM_CDDAERROR 12
+#define WM_CDM_CDDAACK 0xF0
+
+#define WM_CDIN 0
+#define WM_CDDA 1
+
+#define WM_ENDTRACK 0
+
+#define WM_BALANCE_SYMMETRED 0
+#define WM_BALANCE_ALL_LEFTS -10
+#define WM_BALANCE_ALL_RIGHTS 10
+
+#define WM_VOLUME_MUTE 0
+#define WM_VOLUME_MAXIMAL 100
+
+int wm_cd_init( int cdin, const char *cd_device, const char *soundsystem,
+ const char *sounddevice, const char *ctldevice );
+int wm_cd_destroy( void );
+
+int wm_cd_status( void );
+
+int wm_cd_getcurtrack( void );
+int wm_cd_getcurtracklen( void );
+int wm_cd_getcountoftracks( void );
+
+int wm_cd_gettracklen( int track );
+
+int wm_cd_play( int start, int pos, int end );
+int wm_cd_play_chunk( int start, int end, int realstart );
+int wm_cd_play_from_pos( int pos );
+int wm_cd_pause( void );
+int wm_cd_stop( void );
+int wm_cd_eject( void );
+int wm_cd_closetray( void );
+
+int wm_find_trkind( int, int, int );
+
+/*
+ * for vaild values see wm_helpers.h
+ */
+int wm_cd_set_verbosity( int );
+
+const char * wm_drive_vendor( void );
+const char * wm_drive_model( void );
+const char * wm_drive_revision( void );
+const char * wm_drive_device( void );
+
+/*
+ * volume is valid WM_VOLUME_MUTE <= vol <= WM_VOLUME_MAXIMAL,
+ * balance is valid WM_BALANCE_ALL_LEFTS <= balance <= WM_BALANCE_ALL_RIGHTS
+ */
+int wm_cd_volume( int volume, int balance);
+
+/*
+ * please notice, that more OSs don't allow to read balance and volume
+ * in this case you get -1 for volume and WM_BALANCE_SYMMETRED for balance
+ */
+int wm_cd_getvolume( void );
+int wm_cd_getbalance( void );
+
+#endif /* WM_CDROM_H */
diff --git a/kscd/libwm/include/wm_cdtext.h b/kscd/libwm/include/wm_cdtext.h
new file mode 100644
index 00000000..e843ee95
--- /dev/null
+++ b/kscd/libwm/include/wm_cdtext.h
@@ -0,0 +1,105 @@
+#ifndef WM_CDTEXT_H
+#define WM_CDTEXT_H
+
+/***************************************************************************
+ wm_cdtext.h - description
+ -------------------
+ begin : Mon Feb 12 2001
+ copyright : (C) 2001 by Alex Kern
+ email : alex.kern@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+/*
+ * cdtext base structure and defines
+ */
+
+#define MAX_LENGHT_OF_CDTEXT_STRING 162 /* max 160 bytes + 2 * 0x00 by UNICODES */
+#define DATAFIELD_LENGHT_IN_PACK 12
+#define MAX_LANGUAGE_BLOCKS 8
+
+struct cdtext_pack_data_header {
+ unsigned char header_field_id1_typ_of_pack;
+ unsigned char header_field_id2_tracknumber;
+ unsigned char header_field_id3_sequence;
+ unsigned char header_field_id4_block_no;
+ unsigned char text_data_field[DATAFIELD_LENGHT_IN_PACK];
+ unsigned char crc_byte1;
+ unsigned char crc_byte2;
+};
+
+typedef unsigned char cdtext_string[MAX_LENGHT_OF_CDTEXT_STRING];
+
+/* meke it more generic
+ it can be up to 8 blocks with different encoding */
+
+struct cdtext_info_block {
+ /* management */
+ unsigned char block_code;
+ unsigned char block_unicode; /* 0 - single chars, 1 - doublebytes */
+ unsigned char block_encoding; /* orange book -? */
+ cdtext_string* block_encoding_text;
+
+ /* variable part of cdtext */
+ cdtext_string* name;
+ cdtext_string* performer;
+ cdtext_string* songwriter;
+ cdtext_string* composer;
+ cdtext_string* arranger;
+ cdtext_string* message;
+ cdtext_string* UPC_EAN_ISRC_code;
+
+ /* fix part of cdtext */
+ unsigned char binary_disc_identification_info[DATAFIELD_LENGHT_IN_PACK];
+ unsigned char binary_genreidentification_info[DATAFIELD_LENGHT_IN_PACK];
+ unsigned char binary_size_information[DATAFIELD_LENGHT_IN_PACK];
+};
+
+struct cdtext_info {
+ /* somethimes i get hunderts of bytes, without anyone valid pack
+ my CDU-561 for example */
+ int count_of_entries; /* one more because album need one too */
+ int count_of_valid_packs;
+ int count_of_invalid_packs;
+ int valid;
+
+ struct cdtext_info_block *blocks[MAX_LANGUAGE_BLOCKS];
+};
+
+#ifndef IGNORE_FEATURE_LIST
+
+struct feature_list_header {
+ unsigned char lenght_msb;
+ unsigned char lenght_1sb;
+ unsigned char lenght_2sb;
+ unsigned char lenght_lsb;
+ unsigned char reserved1;
+ unsigned char reserved2;
+ unsigned char profile_msb;
+ unsigned char profile_lsb;
+};
+
+struct feature_descriptor_cdread {
+ unsigned char feature_code_msb;
+ unsigned char feature_code_lsb;
+ unsigned char settings;
+ unsigned char add_lenght;
+ unsigned char add_settings;
+ unsigned char reserved1;
+ unsigned char reserved2;
+ unsigned char reserved3;
+};
+
+#endif /* IGNORE_FEATURE_LIST */
+
+struct cdtext_info* wm_cd_get_cdtext( void );
+
+#endif /* WM_CDTEXT_H */
diff --git a/kscd/libwm/include/wm_config.h b/kscd/libwm/include/wm_config.h
new file mode 100644
index 00000000..b04027f4
--- /dev/null
+++ b/kscd/libwm/include/wm_config.h
@@ -0,0 +1,377 @@
+#ifndef WM_CONFIG_H
+#define WM_CONFIG_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ **********************************************************************
+ *
+ * This file consists of several parts. First, there's a generic,
+ * platform independent part. Set needed options there.
+ * The following parts are platform dependent. You may search for the
+ * names listed below and then set your OS specific options there.
+ * Don't be surprised, if there are no options for your OS. They aren't
+ * needed in any case.
+ *
+ * The default values should produce a functional WorkMan on every
+ * platform.
+ *
+ *********************
+ * Current platforms:
+ *********************
+ * BSD386
+ * FreeBSD
+ * HP-UX
+ * Irix (SGI)
+ * Linux
+ * News (Sony NewsOS)
+ * OpenBSD
+ * OSF1
+ * Sun (SunOS/Solaris, Sparc or x86)
+ * SVR4
+ * Ultrix
+ * AIX
+ *
+ * The above order corresponds with the order of the platform specific
+ * options below.
+ */
+
+#include <config.h>
+/******************************************************************
+ * generic options
+ ******************************************************************
+ ** ** *** **** ** **** ** * ** ** ** * **
+ * * * *** **** * * *** * ** ** *** * * * * * * **
+ * * *** **** ** *** ** ** ** * * ** * * **
+ * * * *** **** **** *** * ** ** *** * * * * *** **
+ * * * * ** **** * * ** ** **** ** * * *** **
+ ******************************************************************/
+
+/*
+ * This option is obvious. But please do not forget the original
+ * WorkMan version string if you need support.
+ */
+
+#define WORKMAN_NAME "LibWorkMan"
+#define WORKMAN_VERSION "1.4.0"
+
+/*
+ * If your CD-ROM drive closes its tray if the device is opened, then
+ * the next define can make WorkMans "Eject" button an "open/close"
+ * button. If it disturbs you, just comment it out.
+ *
+ * ### this is preliminary. It may have no effect for you ###
+ */
+#define CAN_CLOSE 1
+
+/*
+ * Define the following if you want the balance slider to
+ * decrease one channel's volume while increasing the other's
+ */
+/* #define SYMETRIC_BALANCE 1 */
+
+
+/*
+ * Define this if you want CDDA support. Supported systems are:
+ *
+ * - Solaris (2.4 or newer)
+ * --> Linux is on the way. Don't define it now. It would lead to errors only.
+ */
+/*#define BUILD_CDDA 1*/
+
+
+
+/******************************************************************
+ * BSD386
+ ******************************************************************
+ *** **** *** ******* ** **** **** ************
+ *** ** ** ****** * ***** ****** ** ** ** ***************
+ *** ****** **** ** *** ***** ***** *** ************
+ *** ** ****** ** * *** ******** ** ** ** ** ***********
+ *** **** *** *** ****** **** **** ************
+ ******************************************************************/
+#if defined(__bsdi__) || defined(__bsdi)
+
+/*
+ * This lets you use the SoundBlaster Mixer on BSD/386
+ */
+#define SOUNDBLASTER 1
+
+#define DEFAULT_CD_DEVICE "/dev/rsr2c"
+
+#endif /* __bsdi__ (BSD/386) */
+
+/******************************************************************
+ * FreeBSD
+ ******************************************************************
+ *** ** *** ** ** **** *** ***********
+ *** ****** ** ** ****** ****** ** ** ****** * **********
+ *** **** *** **** **** ****** **** ** *********
+ *** ****** ** ** ****** ****** ** ****** ** * **********
+ *** ****** ** ** ** ** **** *** ***********
+ ******************************************************************/
+#if defined(__FreeBSD__) || defined(__FreeBSD)
+
+#if __FreeBSD_version >= 500100
+#define DEFAULT_CD_DEVICE "/dev/acd0"
+#else
+#define DEFAULT_CD_DEVICE "/dev/acd0c"
+#endif
+
+#endif /* freebsd */
+
+/* DragonFly */
+#if defined(__DragonFly__)
+#define DEFAULT_CD_DEVICE "/dev/acd0c"
+#endif
+
+/******************************************************************
+ * NetBSD
+ ******************************************************************
+ *** *** ** ** ** **** *** *****************
+ *** ** ** ******** **** ** ** ****** * ****************
+ *** * * ** ****** **** ****** **** ** ***************
+ *** ** ** ******** **** ** ****** ** * ****************
+ *** *** ** **** **** **** *** *****************
+ ******************************************************************/
+#if defined(__NetBSD__) || defined(__NetBSD)
+
+#if defined(__i386__)
+ #define DEFAULT_CD_DEVICE "/dev/rcd0d"
+#else
+ #define DEFAULT_CD_DEVICE "/dev/rcd0c"
+#endif
+
+#endif /* netbsd */
+
+/******************************************************************
+ * HP-UX
+ ******************************************************************
+ *** ** ** ********* ** ** ** ***************************
+ *** ** ** ** ******** ** *** ****************************
+ *** ** *** ** ** **** *****************************
+ *** ** ** ************ ** *** ****************************
+ *** ** ** ************* *** ** ***************************
+ ******************************************************************/
+#if defined(hpux) || defined (__hpux)
+
+#define DEFAULT_CD_DEVICE "/dev/rscsi"
+
+#endif /* hpux */
+
+/******************************************************************
+ * Irix
+ ******************************************************************
+ *** ** *** ** ** *********************************
+ ***** **** ** **** ***** **********************************
+ ***** **** ***** ****** ***********************************
+ ***** **** ** **** ***** **********************************
+ *** ** ** ** ** ** *********************************
+ ******************************************************************/
+#if defined(sgi) || defined(__sgi)
+
+#define DEFAULT_CD_DEVICE "/dev/scsi/sc0d6l0"
+
+#endif /* sgi IRIX */
+
+/******************************************************************
+ * Linux
+ ******************************************************************
+ *** ****** ** *** ** ** ** ** ***********************
+ *** ******** **** ** ** ** *** ************************
+ *** ******** **** * * ** ** **** *************************
+ *** ******** **** ** ** ** *** ************************
+ *** ** ** *** *** *** ** ***********************
+ ******************************************************************/
+#if defined(__linux__)
+
+/*
+ * Uncomment the following line to have WorkMan send SCSI commands
+ * directly to the CD-ROM drive. If you have a SCSI drive you
+ * probably want this, but it will cause WorkMan to not work on IDE
+ * drives.
+ */
+/*#define LINUX_SCSI_PASSTHROUGH 1*/
+
+/*
+ * Which device should be opened by WorkMan at default?
+ */
+#define DEFAULT_CD_DEVICE "/dev/cdrom"
+
+/*
+ * Uncomment the following if you use the sbpcd or mcdx device driver.
+ * It shouldn't hurt if you use it on other devices. It'll be nice to
+ * hear from non-sbpcd (or mcdx) users if this is right.
+ */
+/*#define SBPCD_HACK 1*/
+
+/*
+ * Linux Soundcard support
+ * Disabled by default, because some people rely on it
+ */
+/* #define OSS_SUPPORT 1 */
+
+/*
+ * This has nothing to do with the above.
+ */
+
+/* #define CURVED_VOLUME */
+
+/*
+ * Uncomment the following if you want to try out a better responding
+ * WorkMan, especially with IDE drives. This may work with non-IDE
+ * drives as well. But it may be possible, that it doesn't work at all.
+ * If your drive/driver combination cannot handle the faster access,
+ * the driver will usually hang and you have to reboot your machine.
+ */
+/* #define FAST_IDE 1 */
+
+/*
+ * There are two alternative ways of checking a device containing a
+ * mounted filesystem. Define BSD_MOUNTTEST for the test using
+ * getmntent(). Undefine it for using the SVR4 ustat().
+ * I built in the choice, because it's not clear which method should
+ * be used in Linux. The ustat manpage tells us since 1995, that
+ * fstat() should be used, but I'm too dumb to do so.
+ */
+
+#define BSD_MOUNTTEST
+
+#endif /* __linux */
+
+/******************************************************************
+ * Sony NewsOS
+ ******************************************************************
+ *** *** ** ** ***** *** *****************************
+ *** ** ** ****** ***** ** ********************************
+ *** * * ** **** ** ** **** ******************************
+ *** ** ** ****** * * ****** ****************************
+ *** *** ** ** * *** *****************************
+ ******************************************************************/
+#if defined(__sony_news) || defined(sony_news)
+
+#define DEFAULT_CD_DEVICE "/dev/rsd/b0i6u0p2\0"
+
+#endif
+
+/******************************************************************
+ * OSF1
+ ******************************************************************
+ **** **** *** *** *** *******************************
+ *** ** ** ****** ****** ** *******************************
+ *** ** **** **** *** ***** *******************************
+ *** ** ****** ** **** ****** *******************************
+ **** **** *** *** ***** *****************************
+ ******************************************************************/
+#if defined(__osf__) || defined(__osf)
+
+/* not needed here, it's figured out by plat_osf1.c */
+/* #define DEFAULT_CD_DEVICE "/dev/rcdrom/cd0" */
+
+#endif
+
+/******************************************************************
+ * SunOS/Solaris
+ ******************************************************************
+ **** *** ** ** *** ***************************************
+ *** ****** ** ** ** ***************************************
+ ***** **** ** ** * * ***************************************
+ ******* ** ** ** ** ***************************************
+ **** **** *** *** ***************************************
+ ******************************************************************/
+#if defined(sun) || defined(__sun)
+
+/*
+ * Define the following for Solaris 2.x
+ * If you don't want WorkMan to try to activate the SPARCstation 5
+ * internal audio input so you get sound from the workstation, comment
+ * out the CODEC define.
+ */
+
+#define SYSV 1
+#define CODEC 1
+#define DEFAULT_CD_DEVICE "/vol/dev/aliases/cdrom0"
+
+/*
+ * set the following to "SUNW,CS4231" for Sun and to "SUNW,sb16"
+ * for PC (with SoundBlaster 16) running Solaris x86
+ * (only important if you define CODEC above)
+ */
+#define SUN_AUD_DEV "SUNW,CS4231"
+/*#define SUN_AUD_DEV "SUNW,sbpro"*/
+
+
+#endif
+
+/******************************************************************
+ * SVR4
+ ******************************************************************
+ **** *** **** ** *** * ********************************
+ *** ****** **** ** ** ** * ********************************
+ ***** ***** ** *** *** *******************************
+ ******* *** ** *** ** ***** ********************************
+ **** ***** **** ** ***** ********************************
+ ******************************************************************/
+#if (defined(SVR4) || defined(__SVR4)) && !defined(sun) && !defined(__sun) && !defined(sony_news) && !defined(__sony_news)
+
+#define DEFAULT_CD_DEVICE "/dev/rcdrom/cd0"
+
+#endif
+
+/******************************************************************
+ * Ultrix
+ ******************************************************************
+ *** ** ** ***** ** *** ** ** ******************
+ *** ** ** ******* **** ** **** ***** *******************
+ *** ** ** ******* **** ***** ****** ********************
+ *** ** ** ******* **** ** **** ***** *******************
+ **** *** *** **** ** ** ** ** ******************
+ ******************************************************************/
+#if defined(ultrix) || defined(__ultrix)
+
+#endif
+
+/******************************************************************
+ * IBM AIX
+ ******************************************************************
+ **** *** ** ** *****************************************
+ *** ** **** ***** ******************************************
+ *** **** ****** *******************************************
+ *** ** **** ***** ******************************************
+ *** ** ** ** ** *****************************************
+ ******************************************************************/
+#if defined(AIXV3) || defined(__AIXV3)
+
+#define DEFAULT_CD_DEVICE "/dev/cd0"
+
+#endif /* IBM AIX */
+
+/******************************************************************/
+#endif /* WM_CONFIG_H */
+
+
+
+
+
+
+
+
diff --git a/kscd/libwm/include/wm_database.h b/kscd/libwm/include/wm_database.h
new file mode 100644
index 00000000..401f12bc
--- /dev/null
+++ b/kscd/libwm/include/wm_database.h
@@ -0,0 +1,42 @@
+#ifndef WM_DATABASE_H
+#define WM_DATABASE_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Prototypes for WorkMan database
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+
+#define WM_DB_SAVE_ERROR 1
+#define WM_DB_SAVE_DISABLED 2
+
+int wm_db_get_playnew( void );
+void split_workmandb( void );
+int save( void );
+void load( void );
+void load_settings( void );
+
+
+#endif /* WM_DATABASE_H */
diff --git a/kscd/libwm/include/wm_helpers.h b/kscd/libwm/include/wm_helpers.h
new file mode 100644
index 00000000..ad98a365
--- /dev/null
+++ b/kscd/libwm/include/wm_helpers.h
@@ -0,0 +1,109 @@
+#ifndef WM_HELPERS_H
+#define WM_HELPERS_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Here to be found: Prototypes. Including variable names to be easier
+ * to read.
+ * This is just one more step to a more modular and understandable code.
+ *
+ */
+
+/*
+ * LibWorkMan message levels. I'm not sure how to call them all and which
+ * use they should fulfill. This is not very urgent now, because there
+ * aren't many messages in LibWorkMan now.
+ */
+#define WM_MSG_LEVEL_NONE 0 /**/
+#define WM_MSG_LEVEL_ERROR 1 /**/
+#define WM_MSG_LEVEL_TWO 2
+#define WM_MSG_LEVEL_THREE 3
+#define WM_MSG_LEVEL_FOUR 4
+#define WM_MSG_LEVEL_INFO 5 /**/
+#define WM_MSG_LEVEL_SIX 6
+#define WM_MSG_LEVEL_VERB 7 /**/
+#define WM_MSG_LEVEL_EIGHT 8
+#define WM_MSG_LEVEL_DEBUG 9 /**/
+
+/*
+ * Message classes. This is somehow a definition of
+ * the message's source.
+ */
+
+#define WM_MSG_CLASS_PLATFORM 0x010
+#define WM_MSG_CLASS_SCSI 0x020
+#define WM_MSG_CLASS_CDROM 0x040
+#define WM_MSG_CLASS_DB 0x080
+#define WM_MSG_CLASS_MISC 0x100
+
+#define WM_MSG_CLASS_ALL 0xff0
+
+extern int wm_lib_verbosity;
+
+/*
+ * I did not know any better place...
+ */
+#ifdef DEBUG
+#define CHECKPOINT(t) fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, t );
+#else
+#define CHECKPOINT(t)
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifdef __linux__
+#include <signal.h>
+/* Linux doesn't have a SIGEMT */
+#if !defined( SIGEMT )
+# define SIGEMT SIGUNUSED
+#endif
+#endif /* linux */
+
+void freeup( char **x );
+void wm_strmcat( char **t, const char *s);
+void wm_strmcpy( char **t, const char *s );
+char * wm_strdup( char *s );
+/* Somebody's version query unsatisfied? */
+int wm_libver_major( void ); /* return major internal version number */
+int wm_libver_minor( void ); /* return minor internal version number */
+int wm_libver_pl( void ); /* return internal patchlevel number */
+char * wm_libver_name( void ); /* return internal name (LibWorkMan) */
+char * wm_libver_number( void ); /* returns string: "<major>.<minor>.<pl>" */
+char * wm_libver_string( void ); /* returns string: "<name> <number>" */
+char * wm_libver_date( void ); /* returns string: date of compilation */
+void wm_lib_set_verbosity( int level ); /* set verbosity level */
+int wm_lib_get_verbosity( void ); /* get verbosity level */
+void wm_lib_message( unsigned int level, const char *format, ... )
+#ifdef __GNUC__
+ __attribute__ ((format(printf,2,3)))
+#endif
+ ; /* put out a message on stderr */
+int wm_susleep( int usec );
+
+#endif /* WM_HELPERS_H */
diff --git a/kscd/libwm/include/wm_index.h b/kscd/libwm/include/wm_index.h
new file mode 100644
index 00000000..7c348b75
--- /dev/null
+++ b/kscd/libwm/include/wm_index.h
@@ -0,0 +1,36 @@
+#ifndef WM_INDEX_H
+#define WM_INDEX_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Prototypes for wm_index.c
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+int idx_find_entry( char *file, int ntracks, int *tracks,
+ int len, int fuzz, unsigned long *pos );
+int idx_delete_entry(char *file, int track, int fuzz, unsigned long pos );
+int idx_write_entry( char *file, int track, unsigned long pos );
+
+#endif /* WM_INDEX_H */
diff --git a/kscd/libwm/include/wm_platform.h b/kscd/libwm/include/wm_platform.h
new file mode 100644
index 00000000..edce8119
--- /dev/null
+++ b/kscd/libwm/include/wm_platform.h
@@ -0,0 +1,51 @@
+#ifndef WM_PLATFORM_H
+#define WM_PLATFORM_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * The platform interface
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#include "wm_struct.h"
+
+int wmcd_open( struct wm_drive *d );
+int wmcd_reopen( struct wm_drive *d );
+
+/*
+ * void keep_cd_open( void );
+ */
+int wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply );
+
+/****************************************
+ *
+ * The drive prototypes.
+ *
+ */
+extern struct wm_drive_proto generic_proto;
+extern struct wm_drive_proto sony_proto;
+extern struct wm_drive_proto toshiba_proto;
+
+#endif /* WM_PLATFORM_H */
diff --git a/kscd/libwm/include/wm_scsi.h b/kscd/libwm/include/wm_scsi.h
new file mode 100644
index 00000000..0acc62ef
--- /dev/null
+++ b/kscd/libwm/include/wm_scsi.h
@@ -0,0 +1,50 @@
+#ifndef WM_SCSI_H
+#define WM_SCSI_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * SCSI prototypes (scsi.c)
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#include "wm_struct.h"
+
+#define WM_ERR_SCSI_INQUIRY_FAILED -1
+
+int wm_scsi_mode_sense( struct wm_drive *d, unsigned char page,
+ unsigned char *buf );
+int sendscsi( struct wm_drive *d, void *buf,
+ unsigned int len, int dir,
+ unsigned char a0, unsigned char a1,
+ unsigned char a2, unsigned char a3,
+ unsigned char a4, unsigned char a5,
+ unsigned char a6, unsigned char a7,
+ unsigned char a8, unsigned char a9,
+ unsigned char a10, unsigned char a11 );
+ int wm_scsi_get_drive_type( struct wm_drive *d, char *vendor,
+ char *model, char *rev );
+int wm_scsi_get_cdtext( struct wm_drive *d,
+ unsigned char **pp_buffer, int *p_buffer_length );
+
+#endif /* WM_SCSI_H */
diff --git a/kscd/libwm/include/wm_struct.h b/kscd/libwm/include/wm_struct.h
new file mode 100644
index 00000000..790de3b0
--- /dev/null
+++ b/kscd/libwm/include/wm_struct.h
@@ -0,0 +1,198 @@
+#ifndef WM_STRUCT_H
+#define WM_STRUCT_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+
+/*
+ * Structure for a single track. This is pretty much self-explanatory --
+ * one of these exists for each track on the current CD.
+ */
+struct wm_trackinfo
+{
+ char *songname; /* Name of song, dynamically allocated */
+ char *otherdb; /* Unrecognized info for this track */
+ char *otherrc;
+ int length; /* Length of track in seconds or Kbytes */
+ int start; /* Starting position (f+s*75+m*60*75) */
+ int volume; /* Per-track volume (1-32, 0 to disable) */
+ int track; /* Physical track number */
+ int section; /* Section number (0 if track not split) */
+ int contd; /* Flag: continuation of previous track */
+ int avoid; /* Flag: don't play this track. */
+ int data; /* Flag: data track */
+};
+
+/*
+ * Structure for internal playlist management. The internal playlist is
+ * simply the list of track ranges that are being played currently. This
+ * is built whenever the CD starts playing; it's used in normal and shuffle
+ * modes as well as playlist mode.
+ *
+ * The "starttime" element represents how much time has elapsed by the time
+ * we get to this entry. For instance, if the list begins with a 5-minute
+ * track and a 3-minute track, the third entry would have a starttime of 8
+ * minutes. This is used so that the elapsed play time can be displayed
+ * even in shuffle or playlist modes.
+ *
+ * The last member of the list has a start track of 0, and its starttime is
+ * the total playing time of the playlist (which will usually be overestimated,
+ * since we don't play leadouts in some cases.)
+ */
+struct wm_play
+{
+ int start; /* Start track, or 0 if end of list */
+ int end; /* last track plus 1 */
+ int starttime; /* Number of seconds elapsed previously */
+};
+
+/*
+ * Structure for playlists (as seen by the user.) This is simply a name
+ * followed by a zero-terminated list of track numbers to play. The list
+ * is terminated by a NULL name.
+ */
+struct wm_playlist
+{
+ char *name; /* Name of this playlist */
+ int *list; /* List of tracks */
+};
+
+struct wm_cdinfo
+{
+ char artist[84]; /* Artist's name */
+ char cdname[84]; /* Disc's name */
+ int ntracks; /* Number of tracks on the disc */
+ int curtrack;
+ int curtracklen;
+ int length; /* Total running time in seconds */
+ int autoplay; /* Start playing CD immediately */
+ int playmode; /* How to play the CD */
+ int volume; /* Default volume (1-32, 0 for none) */
+ struct wm_trackinfo *trk; /* struct wm_trackinfo[ntracks] */
+ struct wm_playlist *lists; /* User-specified playlists */
+ char *whichdb; /* Which database is this entry from? */
+ char *otherdb; /* Unrecognized lines from this entry */
+ char *otherrc;
+ char *user; /* Name of originating user */
+ unsigned int cddbid; /* CDDB-ID of the current disc */
+ struct cdinfo *next; /* For browsers, etc. */
+};
+
+/* The global variable "cd" points to the struct for the CD that's playing. */
+extern struct wm_cdinfo *cd;
+
+extern struct wm_playlist *new_playlist();
+
+#define WM_STR_GENVENDOR "Generic"
+#define WM_STR_GENMODEL "drive"
+#define WM_STR_GENREV "type"
+
+
+/*
+ * Drive descriptor structure. Used for access to low-level routines.
+ */
+struct wm_drive_proto
+{
+ int (*gen_init)();
+ int (*gen_close)();
+ int (*gen_get_trackcount)();
+ int (*gen_get_cdlen)();
+ int (*gen_get_trackinfo)();
+ int (*gen_get_drive_status)();
+ int (*gen_get_volume)();
+ int (*gen_set_volume)();
+ int (*gen_pause)();
+ int (*gen_resume)();
+ int (*gen_stop)();
+ int (*gen_play)();
+ int (*gen_eject)();
+ int (*gen_closetray)();
+ int (*gen_get_cdtext)();
+};
+
+struct wm_drive
+{
+ int cdda; /* cdda 1, cdin 0 */
+ const char *cd_device;
+ char *soundsystem;
+ char *sounddevice;
+ char *ctldevice;
+ int fd; /* File descriptor, if used by platform */
+ int cdda_slave; /* File descriptor for CDDA */
+
+ char *vendor; /* Vendor name */
+ char *model; /* Drive model */
+ char *revision; /* Revision of the drive */
+ void *aux; /* Pointer to optional platform-specific info */
+ void *daux; /* Pointer to optional drive-specific info */
+
+ struct wm_drive_proto *proto;
+};
+
+/*
+ * Structure for information of the usage of cddb.
+ */
+struct wm_cddb {
+ int protocol; /* 0-off, 1-cddbp, 2-http, 3-htproxy */
+ char cddb_server[84]; /* host.domain.name:port */
+ char mail_adress[84]; /* user@domain.name */
+ char path_to_cgi[84]; /* (/)path/to/cddb.cgi */
+ char proxy_server[84]; /* host.domain.name:port */
+};
+extern struct wm_cddb cddb;
+
+
+/*
+ * Each platform has to define generic functions, so may as well declare
+ * them all here to save space.
+ * These functions should never be seen outside libworkman. So I don't care
+ * about the wm_ naming convention here.
+ */
+int gen_init( struct wm_drive *d );
+int gen_close( struct wm_drive *d );
+int gen_get_trackcount(struct wm_drive *d, int *tracks);
+int gen_get_cdlen(struct wm_drive *d, int *frames);
+int gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe);
+int gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind );
+int gen_set_volume( struct wm_drive *d, int left, int right );
+int gen_get_volume( struct wm_drive *d, int *left, int *right );
+int gen_pause(struct wm_drive *d);
+int gen_resume(struct wm_drive *d);
+int gen_stop(struct wm_drive *d);
+int gen_play(struct wm_drive *d, int start, int end, int realstart);
+int gen_eject(struct wm_drive *d);
+int gen_closetray(struct wm_drive *d);
+int gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght);
+
+int find_drive_struct(const char *vendor, const char *model, const char *rev);
+
+
+struct cdtext_info* get_glob_cdtext(struct wm_drive*, int);
+void free_cdtext(void);
+const char* gen_status(int);
+
+#endif /* WM_STRUCT_H */
diff --git a/kscd/libwm/include/wm_version.h b/kscd/libwm/include/wm_version.h
new file mode 100644
index 00000000..82e500c5
--- /dev/null
+++ b/kscd/libwm/include/wm_version.h
@@ -0,0 +1,35 @@
+#ifndef WM_VERSION_H
+#define WM_VERSION_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Version information
+ *
+ */
+
+#define WM_LIBVER_MAJOR 1
+#define WM_LIBVER_MINOR 4
+#define WM_LIBVER_PL 3
+#define WM_LIBVER_NAME "LibWorkMan"
+
+#endif /* WM_VERSION_H */
diff --git a/kscd/libwm/include/workman.h b/kscd/libwm/include/workman.h
new file mode 100644
index 00000000..e7c3807d
--- /dev/null
+++ b/kscd/libwm/include/workman.h
@@ -0,0 +1,52 @@
+#ifndef WORKMAN_H
+#define WORKMAN_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * all-in-one libworkman include file.
+ *
+ */
+
+
+/*
+ * wm_config should always be included first
+ */
+#include "wm_config.h"
+
+#include "workman_defs.h"
+#ifdef BUILD_CDDA
+#include "wm_cdda.h"
+#endif
+#include "wm_cddb.h"
+#include "wm_cdinfo.h"
+#include "wm_cdrom.h"
+#include "wm_database.h"
+#include "wm_helpers.h"
+#include "wm_index.h"
+#include "wm_platform.h"
+#include "wm_scsi.h"
+#include "wm_struct.h"
+#include "wm_cdtext.h"
+
+#endif /* WORKMAN_H */
+
diff --git a/kscd/libwm/include/workman_defs.h b/kscd/libwm/include/workman_defs.h
new file mode 100644
index 00000000..9da47f8f
--- /dev/null
+++ b/kscd/libwm/include/workman_defs.h
@@ -0,0 +1,30 @@
+#ifndef WORKMAN_DEFS_H
+#define WORKMAN_DEFS_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player program
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * #defined CONSTANTS
+ *
+ * Too bad this file seems to be so empty...
+ *
+ */
+
+#include "wm_version.h"
+
+#endif /* WORKMAN_DEFS_H */
diff --git a/kscd/libwm/index.c b/kscd/libwm/index.c
new file mode 100644
index 00000000..e15312b7
--- /dev/null
+++ b/kscd/libwm/index.c
@@ -0,0 +1,383 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Maintain an external index file for a WorkMan CD database.
+ * Uses the Berkeley libdb library, available from ftp.cs.berkeley.edu.
+ */
+
+#ifdef LIBDB
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <db.h>
+#include <fcntl.h>
+#include <string.h>
+#include <netinet/in.h> /* for htonl() */
+#include "include/wm_config.h"
+#include "include/wm_index.h"
+
+extern int suppress_locking;
+
+/*
+ * idx_find_entry()
+ *
+ * Find an entry in the index file.
+ *
+ * Input:
+ * file Name of database file (text version).
+ * ntracks Number of tracks on the CD we're looking for.
+ * tracks Array of track start times.
+ * len CD length in frames.
+ * fuzz Fuzz factor (tolerance value).
+ * pos Pointer to return value.
+ *
+ * Output:
+ * 1 No matching record found.
+ * 0 Record found; *pos contains offset into text file.
+ * -1 Index file out of date or inaccessible, or another error.
+ */
+int
+idx_find_entry( char *file, int ntracks, int *tracks,
+ int len, int fuzz, unsigned long *pos )
+{
+ unsigned long dbpos;
+ char *indexname = NULL, keyval[8];
+ int c;
+ FILE *text;
+ DB *index;
+ DBT key, data;
+ BTREEINFO bti;
+
+ /*
+ * First, see if the text file is accessible. Lock it if so.
+ */
+ text = fopen(file, "r");
+ if (text == NULL)
+ return (-1);
+ if ((c = getc(text)) == EOF)
+ {
+ fclose(text);
+ return (-1);
+ }
+ if (! suppress_locking)
+ if (lockit(fileno(text), F_RDLCK))
+ {
+ fclose(text);
+ return (-1);
+ }
+
+ /*
+ * Open the index file.
+ */
+ indexname = malloc(strlen(file) + sizeof(".ind"));
+ if (indexname == NULL)
+ {
+ fclose(text);
+ return (-1);
+ }
+ strcpy(indexname, file);
+ strcat(indexname, ".ind");
+ bti.flags = 0;
+ bti.cachesize = 0;
+ bti.minkeypage = bti.maxkeypage = 0;
+ bti.psize = bti.lorder = 0;
+ bti.compare = NULL;
+ bti.prefix = NULL;
+ index = dbopen(indexname, O_RDONLY, 0666, DB_BTREE, &bti);
+ free(indexname);
+ if (index == NULL)
+ {
+ fclose(text);
+ return (-1);
+ }
+
+ /*
+ * Search for the first matching entry.
+ */
+ sprintf(keyval, "%07d", tracks[ntracks - 1] - fuzz);
+ key.data = keyval;
+ key.size = 7;
+ if (c = (index->seq)(index, &key, &data, R_CURSOR))
+ {
+ (index->close)(index);
+ fclose(text);
+ return (c);
+ }
+
+ /*
+ * Now loop through all the possible matches, collecting them into
+ * memory.
+ */
+ do {
+ char tracksline[750], *s;
+ int i, val;
+
+ /* Hit the end of the valid entries? */
+ sscanf(key.data, "%d", &val);
+ if (val > tracks[ntracks - 1] + fuzz)
+ break;
+
+ dbpos = ntohl(*((unsigned long *) data.data));
+ if (fseek(text, dbpos, 0))
+ break;
+
+ fgets(tracksline, sizeof(tracksline), text);
+ if (strncmp(tracksline, "tracks ", 7))
+ break;
+ (void) strtok(tracksline, " \t");
+
+ /* Got a valid tracks line. See if it matches the CD. */
+ s = strtok(NULL, " \t");
+ if (s == NULL)
+ break;
+ if (atoi(s) != ntracks)
+ continue;
+
+ for (i = 0; i < ntracks; i++)
+ {
+ s = strtok(NULL, " \t");
+ if (s == NULL)
+ break;
+ val = atoi(s);
+ if (val + fuzz < tracks[i] || val - fuzz > tracks[i])
+ break;
+ }
+ if (i != ntracks)
+ continue;
+
+ s = strtok(NULL, " \t");
+ if (s == NULL)
+ continue;
+ val = atoi(s);
+ if (val + fuzz / 75 < len / 75 || val + fuzz / 75 > len / 75)
+ continue;
+
+ /* XXX - add to sorted list! */
+ *pos = dbpos;
+ (index->close)(index);
+ fclose(text);
+ return (0);
+ } while ((c = (index->seq)(index, &key, &data, R_NEXT)) == 0);
+
+ if (c == 0)
+ {
+ /* An error. */
+ (index->close)(index);
+ fclose(text);
+ return (-1);
+ }
+
+ (index->close)(index);
+ fclose(text);
+ return (1);
+}
+
+/*
+ * idx_delete_entry()
+ *
+ * Delete an entry from the index file.
+ *
+ * Input:
+ * file Name of database file (text version).
+ * track Last track's start time (database key).
+ * fuzz Fuzz factor (tolerance value).
+ * pos Position of CD in database file.
+ *
+ * Output:
+ * 1 No matching record found.
+ * 0 Record deleted.
+ * -1 Index file out of date or inaccessible, or another error.
+ *
+ * Note: it is the caller's responsibility to do locking, as it's assumed
+ * that this operation will accompany a modification of the main database
+ * file and will need to be atomic with that modification.
+ */
+int
+idx_delete_entry(char *file, int track, int fuzz, unsigned long pos )
+{
+ unsigned long dbpos;
+ char *indexname = NULL, keyval[8];
+ int c, status;
+ DB *index;
+ DBT key, data;
+ BTREEINFO bti;
+
+ /*
+ * Open the index file.
+ */
+ indexname = malloc(strlen(file) + sizeof(".ind"));
+ if (indexname == NULL)
+ return (-1);
+
+ strcpy(indexname, file);
+ strcat(indexname, ".ind");
+
+ bti.flags = 0;
+ bti.cachesize = 0;
+ bti.minkeypage = bti.maxkeypage = 0;
+ bti.psize = bti.lorder = 0;
+ bti.compare = NULL;
+ bti.prefix = NULL;
+ index = dbopen(indexname, O_RDWR, 0666, DB_BTREE, &bti);
+ free(indexname);
+ if (index == NULL)
+ return (-1);
+
+ /*
+ * Search for the first matching entry.
+ */
+ sprintf(keyval, "%07d", track - fuzz);
+ key.data = keyval;
+ key.size = 7;
+ if (c = (index->seq)(index, &key, &data, R_CURSOR))
+ {
+ /*
+ * Nothing matched!
+ */
+ (index->close)(index);
+ return (c);
+ }
+
+ /*
+ * Look for the entry the user wants to delete.
+ */
+ do {
+ int val;
+
+ /* Hit the end of the valid entries? */
+ sscanf(key.data, "%d", &val);
+ if (val > track + fuzz)
+ break;
+
+ /* Is this the entry we want? */
+ if (pos == ntohl(*((unsigned long *) data.data)))
+ {
+ /*
+ * Yep! Delete it.
+ */
+ status = (index->del)(index, &key, R_CURSOR);
+ (index->close)(index);
+ return (status);
+ }
+ } while ((c = (index->seq)(index, &key, &data, R_NEXT)) == 0);
+
+ if (c == 0)
+ {
+ /* An error. */
+ (index->close)(index);
+ return (-1);
+ }
+
+ (index->close)(index);
+ return (1);
+}
+
+/*
+ * idx_write_entry()
+ *
+ * Write out an index file entry.
+ *
+ * Input:
+ * file Name of database file (text version).
+ * track Start time of last track (database key).
+ * pos Position of entry in text file.
+ *
+ * Output:
+ * 0 Record written.
+ * -1 Index file inaccessible, or another error.
+ *
+ * Note: it is the caller's responsibility to do locking, as it's assumed
+ * that this operation will accompany a modification of the main database
+ * file and will need to be atomic with that modification.
+ */
+int
+idx_write_entry( char *file, int track, unsigned long pos )
+{
+ char *indexname, keyval[8];
+ int status;
+ DB *index;
+ DBT key, data;
+ BTREEINFO bti;
+
+ /*
+ * Open the index file.
+ */
+ indexname = malloc(strlen(file) + sizeof(".ind"));
+ if (indexname == NULL)
+ return (-1);
+
+ strcpy(indexname, file);
+ strcat(indexname, ".ind");
+
+ bti.flags = R_DUP;
+ bti.cachesize = 0;
+ bti.minkeypage = 0;
+ bti.maxkeypage = 0;
+ bti.psize = 0;
+ bti.lorder = 4321; /* network byte order */
+ bti.compare = NULL;
+ bti.prefix = NULL;
+ index = dbopen(indexname, O_RDWR, 0666, DB_BTREE, &bti);
+ free(indexname);
+ if (index == NULL)
+ return (-1);
+
+ /*
+ * Create a new key and value.
+ */
+ pos = htonl(pos);
+ data.data = &pos;
+ data.size = sizeof(pos);
+ key.data = keyval;
+ key.size = 7;
+
+ sprintf(keyval, "%07d", track);
+
+ status = (index->put)(index, &key, &data, 0);
+
+ (index->close)(index);
+ return (status);
+}
+
+#else /* LIBDB */
+
+int
+idx_find_entry()
+{
+ return (1); /* no record found; text file will be searched. */
+}
+
+int
+idx_delete_entry()
+{
+ return (0);
+}
+
+int
+idx_write_entry()
+{
+ return (0);
+}
+
+#endif /* LIBDB */
diff --git a/kscd/libwm/plat_aix.c b/kscd/libwm/plat_aix.c
new file mode 100644
index 00000000..93b596a1
--- /dev/null
+++ b/kscd/libwm/plat_aix.c
@@ -0,0 +1,489 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * plat_aix - AIX 4.x IDE and SCSI support 16 Dec 1998
+ *
+ * AIX 4.x Port: Erik O'Shaughnessy
+ * Original AIX IDE Code: Cloyce Spradling (xmcd libdi_d/aixioc.c )
+ *
+ * Taken from the ascd distribution.
+ *
+ */
+
+
+#if defined(AIXV3) || defined(__AIXV3)
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/cdrom.h>
+#include <sys/devinfo.h>
+#include <sys/scsi.h>
+#include <sys/scdisk.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define LEADOUT 0xaa
+
+int min_volume = 128;
+int max_volume = 255;
+
+
+
+/* NAME: gen_init
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_init(struct wm_drive *d)
+{
+ return 0;
+} /* gen_init() */
+
+
+/* NAME: wmcd_open
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+wmcd_open(struct wm_drive *d)
+{
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ int fd;
+
+ if( ! d )
+ {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if(d->fd > -1) /* device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return 0;
+ }
+
+ if( d->cd_device == (char *)NULL )
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ if( (fd = openx(d->cd_device,O_RDONLY,NULL,SC_SINGLE)) < 0 )
+ {
+ perror("openx");
+ return -6;
+ /* return 1 */
+ }
+
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+ d->init(d);
+ return 0;
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/* NAME: wm_scsi
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+wm_scsi(struct wm_drive *d,
+ uchar_t *cdb, int cdblen,
+ void *retbuf, int retbuflen,
+ int getreply)
+{
+ return 0;
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/* NAME: gen_get_drive_status
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_drive_status(struct wm_drive *d,
+ int oldmode,
+ int *mode,
+ int *pos,
+ int *track,
+ int *index)
+{
+ struct cd_audio_cmd cmd;
+
+ *mode = WM_CDM_EJECTED;
+
+ if(d->fd < 0)
+ switch( wmcd_open(d) )
+ {
+ case -1:
+ return -1;
+ case 1:
+ return 0;
+ }
+
+ cmd.audio_cmds = CD_INFO_AUDIO;
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ return -1;
+
+ switch(cmd.status)
+ {
+ case CD_PLAY_AUDIO:
+ *mode = WM_CDM_PLAYING;
+ *track = cmd.indexing.info_audio.current_track;
+ *index = cmd.indexing.info_audio.current_index;
+ *pos = cmd.indexing.info_audio.current_mins * 60 * 75 +
+ cmd.indexing.info_audio.current_secs * 75 +
+ cmd.indexing.info_audio.current_frames;
+ break;
+
+ case CD_PAUSE_AUDIO:
+ *mode = WM_CDM_PAUSED;
+ *track = cmd.indexing.info_audio.current_track;
+ *index = cmd.indexing.info_audio.current_index;
+ *pos = cmd.indexing.info_audio.current_mins * 60 * 75 +
+ cmd.indexing.info_audio.current_secs * 75 +
+ cmd.indexing.info_audio.current_frames;
+
+ break;
+ case CD_NO_AUDIO: /* no play audio in progress */
+ case CD_COMPLETED: /* play operation completed successfully */
+ case CD_STATUS_ERROR: /* invalid status or play stopped due to err */
+ case CD_NOT_VALID: /* audio status is invalid or not supported */
+ *mode = WM_CDM_STOPPED;
+ break;
+ default:
+ *mode = WM_CDM_UNKNOWN;
+ break;
+ }
+
+ return 0;
+} /* gen_get_drive_status() */
+
+
+
+/* NAME: gen_get_trackcount
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_trackcount(struct wm_drive *d,int *tracks)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_TRK_INFO_AUDIO;
+ cmd.msf_flag = 0;
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ {
+ perror("DKAUDIO");
+ return -1;
+ }
+
+ *tracks = cmd.indexing.track_index.last_track;
+
+ return 0;
+} /* gen_get_trackcount() */
+
+/* NAME: gen_get_trackinfo
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_trackinfo(struct wm_drive *d,int track,int *data,int *startframe)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_GET_TRK_MSF;
+ cmd.msf_flag = 1;
+
+ cmd.indexing.track_msf.track = track;
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ return -1;
+
+ *startframe = cmd.indexing.track_msf.mins * 60 * 75 +
+ cmd.indexing.track_msf.secs * 75 +
+ cmd.indexing.track_msf.frames;
+
+ *data = 0;
+
+ return 0;
+} /* gen_get_trackinfo() */
+
+/* NAME: gen_get_cdlen
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_cdlen(struct wm_drive *d,int *frames)
+{
+ int tmp;
+
+ return gen_get_trackinfo(d,LEADOUT,&tmp,frames);
+} /* gen_get_cdlen() */
+
+
+/* NAME: gen_play
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_play(struct wm_drive *d,int start,int end)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_PLAY_AUDIO;
+ cmd.msf_flag = 1;
+
+ cmd.indexing.msf.first_mins = start / (60*75);
+ cmd.indexing.msf.first_secs = (start % (60*75)) / 75;
+ cmd.indexing.msf.first_frames = start % 75;
+
+ cmd.indexing.msf.last_mins = end / (60*75);
+ cmd.indexing.msf.last_secs = (end % (60*75)) / 75;
+ cmd.indexing.msf.last_frames = end % 75;
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ {
+ perror("DKAUDIO:CD_PLAY_AUDIO");
+ return -1;
+ }
+ return 0;
+} /* gen_play() */
+
+/* NAME: gen_pause
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_pause(struct wm_drive *d)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_PAUSE_AUDIO;
+
+ return ioctl(d->fd,DKAUDIO,&cmd);
+} /* gen_pause() */
+
+
+/* NAME: gen_resume
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_resume(struct wm_drive *d)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_RESUME_AUDIO;
+ return ioctl(d->fd,DKAUDIO,&cmd);
+} /* gen_resume() */
+
+/* NAME: gen_stop
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_STOP_AUDIO;
+ return ioctl(d->fd,DKAUDIO,&cmd);
+} /* gen_stop() */
+
+/* NAME: gen_eject
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ return ioctl(d->fd,DKEJECT,NULL);
+}
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+int
+scale_volume(int vol,int max)
+{
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+}
+
+int
+unscale_volume(int vol,int max)
+{
+ int n;
+ n = ( vol - min_volume ) * max_volume / (max - min_volume);
+ return (n <0)?0:n;
+}
+
+/* NAME: gen_set_volume
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_set_volume(struct wm_drive *d,int left,int right)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_SET_VOLUME;
+ cmd.volume_type = CD_VOLUME_CHNLS;
+
+ cmd.out_port_0_vol = scale_volume(left,100);
+ cmd.out_port_1_vol = scale_volume(right,100);
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ {
+ perror("CD_SET_VOLUME");
+ return -1;
+ }
+
+ return 0;
+} /* gen_set_volume() */
+
+/* NAME: gen_get_volume
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_volume(struct wm_drive *d,int *left,int *right)
+{
+ struct cd_audio_cmd cmd;
+ int l,r;
+
+ fprintf(stderr,"gen_get_volume\n");
+
+ cmd.audio_cmds = CD_INFO_AUDIO;
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ return -1;
+
+ *left = unscale_volume(cmd.out_port_0_vol,100);
+ *right = unscale_volume(cmd.out_port_1_vol,100);
+
+ return 0;
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+
+#endif /* _AIX */
+
+
+
+
+
diff --git a/kscd/libwm/plat_bsd386.c b/kscd/libwm/plat_bsd386.c
new file mode 100644
index 00000000..492d394c
--- /dev/null
+++ b/kscd/libwm/plat_bsd386.c
@@ -0,0 +1,510 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * BSD/386-specific drive control routines.
+ */
+
+static char plat_bsd386_id[] = "$Id$";
+
+#if defined(__bsdi__) || defined(__bsdi)
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+
+/*
+ * The following is included from the Linux module. However, I didn't
+ * see a check here if the CD to be ejected is mounted...
+ */
+#if defined(BSD_MOUNTTEST)
+ #include <mntent.h>
+#endif
+
+
+#include <sys/time.h>
+#include <string.h>
+#include <cdrom.h>
+#ifdef SOUNDBLASTER
+# include <sys/soundcard.h>
+#endif
+
+#include "include/wm_struct.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+
+/*
+ * Since we can't sense the drive type with libcdrom anyway, and since the
+ * library doesn't provide "pause" or "resume" functions, use the daux field
+ * to point to the frame number at which we paused.
+ */
+struct pause_info
+{
+ int frame;
+ int endframe;
+};
+
+#define PAUSE_FRAME (((struct pause_info *) d->daux)->frame)
+#define END_FRAME (((struct pause_info *) d->daux)->endframe)
+#define CUR_CD ((struct cdinfo *) d->aux)
+
+void *malloc();
+
+#ifdef SOUNDBLASTER
+ int min_volume = 0;
+ int max_volume = 100;
+ int min_volume_drive = 10; /* Toshiba drive does low values. */
+ int max_volume_drive = 255;
+#else
+ int min_volume = 10;
+ int max_volume = 255;
+#endif
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+/*-----------------------------------------------------------------------*
+ * Open the CD device. We can't determine the drive type under BSD/386.
+ *-----------------------------------------------------------------------*/
+int
+wmcd_open(struct wm_drvie *d)
+{
+ void *aux = NULL, *daux = NULL;
+ int fd = -1;
+
+ if (d->aux) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (aux=%d)]\n", d->aux);
+ return (0);
+ }
+
+ if ((aux = cdopen(d->cd_device)) == NULL)
+ {
+ fprintf(stderr, "No cdrom found by libcdrom\n");
+ return (-6);
+ }
+
+ if ((daux = malloc(sizeof(struct pause_info))) == NULL)
+ return (-1);
+
+#ifdef SOUNDBLASTER
+ fd = open("/dev/mixer", O_RDWR, 0);
+#endif
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ find_drive_struct("", "", "");
+ d->aux = aux;
+ d->daux = daux;
+ d->fd = fd;
+ PAUSE_FRAME = 0;
+ END_FRAME = 0;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+ int tries = 0;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ tries++;
+ } while ( (status != 0) && (tries < 10) );
+ return status;
+} /* wmcd_reopen() */
+
+
+/*---------------------------------------------*
+ * Send an arbitrary SCSI command to a device.
+ *---------------------------------------------*/
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply)
+{
+ /* Don't know how to do SCSI passthrough... */
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd); /* close mixer if open */
+ d->fd = -1;
+ cdclose( d->aux ); /* close the cdrom drive! */
+ d->aux = NULL;
+ free(d->daux);
+ d->daux = NULL;
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+#define DOPOS \
+ *pos = status.abs_frame; \
+*track = status.track_num; \
+*index = status.index_num
+
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct cdstatus status;
+ extern enum wm_cd_modes cur_cdmode;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ /* Is the device open? */
+ if (d->aux == NULL)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (cdstatus (CUR_CD, &status) < 0)
+ {
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ return (0);
+ }
+
+ switch (status.state)
+ {
+ case cdstate_playing:
+ *mode = WM_CDM_PLAYING;
+ DOPOS;
+ break;
+
+ case cdstate_stopped:
+ /* the MITSUMI drive doesn't have a "paused" state,
+ so it always comes here and not to the paused section.
+ The PAUSE_FRAME stuff below (in gen_pause())
+ fakes out the paused state. */
+ if (oldmode == WM_CDM_PLAYING)
+ {
+ *mode = WM_CDM_TRACK_DONE;
+ break;
+ } else if (cur_cdmode != WM_CDM_PAUSED) {
+ *mode = WM_CDM_STOPPED;
+ DOPOS;
+ break;
+ }
+ /* fall through if paused */
+
+ case cdstate_paused:
+ /* the SCSI2 code in the cdrom library only pauses with
+ cdstop(); it never truly stops a disc (until an in-progress
+ play reaches the end). So it always comes here. */
+ if (cur_cdmode == WM_CDM_STOPPED)
+ {
+ *mode = WM_CDM_STOPPED;
+ DOPOS;
+ break;
+ }
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ DOPOS;
+ } else {
+ *mode = WM_CDM_STOPPED;
+ DOPOS;
+ }
+ break;
+
+ default:
+ *mode = WM_CDM_STOPPED;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ *tracks = CUR_CD->ntracks;
+
+ return (0);
+} /* gen_get_trackcount() */
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ *data = (CUR_CD->tracks[track - 1].control & 4) ? 1 : 0;
+ *startframe = CUR_CD->tracks[track - 1].start_frame;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ *frames = CUR_CD->total_frames;
+
+ return (0);
+} /* gen_get_cdlen() */
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+ END_FRAME = end;
+ if (cdplay(d->aux, start, end) < 0)
+ return (-1);
+ else
+ return (0);
+} /* gen_play() */
+
+/*--------------------------------------------------------------------*
+ * Pause the CD. This is a bit of a trick since there's no cdpause()
+ * function in the library. We fake it by saving the frame number
+ * and stopping.
+ *--------------------------------------------------------------------*/
+int
+gen_pause(struct wm_drive *d)
+{
+ struct cdstatus status;
+
+ if (cdstatus(d->aux, &status) < 0)
+ return (-1);
+ if (status.state != cdstate_playing)
+ PAUSE_FRAME = CUR_CD->tracks[0].start_frame;
+ else
+ PAUSE_FRAME = status.abs_frame;
+ if (cdstop(d->aux) < 0)
+ return (-1);
+
+ return (0);
+} /* gen_pause() */
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume(struct wm_drive *d)
+{
+ int status;
+
+ status = (d->play)(d, PAUSE_FRAME, END_FRAME);
+ PAUSE_FRAME = 0;
+ return (status);
+} /* gen_resume() */
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop(struct wm_drive *d)
+{
+ return cdstop(d->aux);
+} /* gen_stop() */
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject(struct wm_drive *d)
+{
+ cdeject(d->aux);
+ cdclose(d->aux);
+ d->aux = NULL;
+ free(d->daux);
+ d->daux = NULL;
+
+ if (d->fd >= 0)
+ close(d->fd); /* close mixer */
+ d->fd = -1;
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if (!cdload(d->aux))
+ return(0);
+ return(-1);
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*------------------------------------------------------------------------*
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *------------------------------------------------------------------------*/
+static int
+scale_volume(int vol, int max)
+{
+ /* on Toshiba XM-3401B drive, and on soundblaster, this works fine. */
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+} /* scale_volume() */
+
+/*---------------------------------------------------------------------------*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ *--------------------------------------------------------------------------*/
+static int
+unscale_volume(int cd_vol, int max)
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ int level;
+
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+ level = right << 8 | left;
+
+ /* Send a Mixer IOCTL */
+ if (d->fd >= 0)
+ (void) ioctl(d->fd, MIXER_WRITE(SOUND_MIXER_VOLUME), &level);
+#ifdef notnow
+ /* NOTE: the cdvolume2() call is an addition to the cdrom library.
+ Pick it up from the archives on bsdi.com */
+ cdvolume2 (CUR_CD, left < 0 ? 0 : left > 255 ? 255 : left,
+ right < 0 ? 0 : right > 255 ? 255 : right);
+
+#endif
+ return (0);
+}
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ int level;
+
+ /* Most systems can't seem to do this... */
+ *left = *right = -1;
+
+ /* Send a Mixer IOCTL */
+ if (d->fd >= 0) {
+ if (ioctl(d->fd, MIXER_READ(SOUND_MIXER_VOLUME), &level) == 0) {
+ *left = unscale_volume((level & 0xff) & 0xff, 100);
+ *right = unscale_volume((level >> 8) & 0xff, 100);
+ }
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif /* __bsdi__ */
diff --git a/kscd/libwm/plat_freebsd.c b/kscd/libwm/plat_freebsd.c
new file mode 100644
index 00000000..b1028a75
--- /dev/null
+++ b/kscd/libwm/plat_freebsd.c
@@ -0,0 +1,560 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * plat_freebsd.c
+ *
+ * FreeBSD-specific drive control routines.
+ *
+ * Todd Pfaff, 3/20/94
+ *
+ */
+
+#if defined(__FreeBSD__) || defined(__FreeBSD) || defined(__NetBSD__) || defined (__NetBSD) || defined(__DragonFly__)
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/cdio.h>
+
+#if defined(__OpenBSD__)
+# define MSF_MINUTES 1
+# define MSF_SECONDS 2
+# define MSF_FRAMES 3
+# include <sys/scsiio.h>
+# include "/sys/scsi/scsi_all.h"
+# include "/sys/scsi/scsi_cd.h"
+#elif defined(__NetBSD__)
+#include <sys/scsiio.h>
+#include <dev/scsipi/scsipi_cd.h>
+#else
+# define LEFT_PORT 0
+# define RIGHT_PORT 1
+# if defined(__FreeBSD_version) && __FreeBSD_version < 300000
+# include <scsi.h>
+# endif
+#endif
+
+#include "include/wm_struct.h"
+#include "include/wm_platform.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_scsi.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+
+int min_volume = 10;
+int max_volume = 255;
+
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+
+/*-------------------------------------------------------------------*
+ * Open the CD device and figure out what kind of drive is attached.
+ *-------------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ d->fd = open(d->cd_device, 0);
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ return -EACCES;
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+
+ find_drive_struct(vendor, model, rev);
+
+ /*(d->init)(d); */
+
+ d->fd = fd;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ if(status == -EACCES || status == 1)
+ return status;
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*---------------------------------------------*
+ * Send an arbitrary SCSI command to a device.
+ *
+ *---------------------------------------------*/
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply)
+{
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct ioc_read_subchannel sc;
+ struct cd_sub_channel_info scd;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ sc.address_format = CD_MSF_FORMAT;
+ sc.data_format = CD_CURRENT_POSITION;
+ sc.track = 0;
+ sc.data_len = sizeof(scd);
+ sc.data = (struct cd_sub_channel_info *)&scd;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (ioctl(d->fd, CDIOCREADSUBCHANNEL, &sc))
+ {
+ /*
+ * #ifdef __NetBSD__
+ *
+ * Denis Bourez <denis@rsn.fdn.fr> told me, that closing the
+ * device is mandatory for FreeBSD, too.
+ */
+ /* we need to release the device so the kernel will notice
+ reloaded media */
+ (void) close(d->fd);
+ d->fd = -1;
+ /*
+ * #endif
+ */
+ return (0); /* ejected */
+ }
+
+ switch (scd.header.audio_status)
+ {
+ case CD_AS_PLAY_IN_PROGRESS:
+ *mode = WM_CDM_PLAYING;
+ dopos:
+ *pos = scd.what.position.absaddr.msf.minute * 60 * 75 +
+ scd.what.position.absaddr.msf.second * 75 +
+ scd.what.position.absaddr.msf.frame;
+ *track = scd.what.position.track_number;
+ *index = scd.what.position.index_number;
+ break;
+
+ case CD_AS_PLAY_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ goto dopos;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CD_AS_PLAY_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case CD_AS_NO_STATUS:
+ case 0:
+ *mode = WM_CDM_STOPPED;
+ break;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct ioc_toc_header hdr;
+
+ if (ioctl(d->fd, CDIOREADTOCHEADER, &hdr) == -1)
+ return (-1);
+
+ *tracks = hdr.ending_track - hdr.starting_track + 1;
+
+ return (0);
+} /* gen_get_trackcount() */
+
+/*-----------------------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ *-----------------------------------------------------------------------*/
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct ioc_read_toc_entry toc;
+ struct cd_toc_entry toc_buffer;
+
+ bzero((char *)&toc_buffer, sizeof(toc_buffer));
+ toc.address_format = CD_MSF_FORMAT;
+ toc.starting_track = track;
+ toc.data_len = sizeof(toc_buffer);
+ toc.data = &toc_buffer;
+
+ if (ioctl(d->fd, CDIOREADTOCENTRYS, &toc))
+ return (-1);
+
+ *data = ((toc_buffer.control & 0x4) != 0);
+
+ *startframe = toc_buffer.addr.msf.minute*60*75 +
+ toc_buffer.addr.msf.second * 75 +
+ toc_buffer.addr.msf.frame;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+ struct ioc_toc_header hdr;
+ int status;
+
+#define LEADOUT 0xaa /* see scsi.c. what a hack! */
+ return gen_get_trackinfo(d, LEADOUT, &tmp, frames);
+} /* gen_get_cdlen() */
+
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play(struct wm_drive *d, int start, int end, int realstart)
+{
+ struct ioc_play_msf msf;
+
+ msf.start_m = start / (60*75);
+ msf.start_s = (start % (60*75)) / 75;
+ msf.start_f = start % 75;
+ msf.end_m = end / (60*75);
+ msf.end_s = (end % (60*75)) / 75;
+ msf.end_f = end % 75;
+
+ if (ioctl(d->fd, CDIOCSTART))
+ return (-1);
+
+ if (ioctl(d->fd, CDIOCPLAYMSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDIOCPAUSE));
+} /* gen_pause() */
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDIOCRESUME));
+} /* gen_resume() */
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop( struct wm_drive *d)
+{
+ return (ioctl(d->fd, CDIOCSTOP));
+} /* gen_stop() */
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject( struct wm_drive *d )
+{
+ /* On some systems, we can check to see if the CD is mounted. */
+ struct stat stbuf;
+ struct statfs buf;
+ int rval;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (fstatfs(stbuf.st_rdev, &buf) == 0)
+ return (-3);
+
+ rval = ioctl(d->fd, CDIOCALLOW);
+
+ if (rval == 0)
+ rval = ioctl(d->fd, CDIOCEJECT);
+
+ if (rval == 0)
+ rval = ioctl(d->fd, CDIOCPREVENT);
+
+ (void) close(d->fd);
+
+ return rval;
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*---------------------------------------------------------------------------*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. The actual formula looks like
+ *
+ * (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ * max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ * return ((vol * (max_volume - min_volume)) / max + min_volume);
+ *---------------------------------------------------------------------------*/
+static int
+scale_volume(int vol, int max)
+{
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+} /* scale_volume() */
+
+/*---------------------------------------------------------------------------*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ *--------------------------------------------------------------------------*/
+static int
+unscale_volume( int cd_vol, int max )
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ struct ioc_vol vol;
+
+ if (left < 0) /* don't laugh, I saw this happen once! */
+ left = 0;
+ if (right < 0)
+ right = 0;
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ bzero((char *)&vol, sizeof(vol));
+
+ vol.vol[LEFT_PORT] = left;
+ vol.vol[RIGHT_PORT] = right;
+
+ if (ioctl(d->fd, CDIOCSETVOL, &vol))
+ return (-1);
+
+ return (0);
+} /* gen_set_volume() */
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ struct ioc_vol vol;
+
+ if (d->fd >= 0)
+ {
+ bzero((char *)&vol, sizeof(vol));
+
+ if (ioctl(d->fd, CDIOCGETVOL, &vol))
+ *left = *right = -1;
+ else
+ {
+ *left = unscale_volume(vol.vol[LEFT_PORT], 100);
+ *right = unscale_volume(vol.vol[RIGHT_PORT], 100);
+ }
+ } else {
+ *left = *right = -1;
+ }
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif
diff --git a/kscd/libwm/plat_hpux.c b/kscd/libwm/plat_hpux.c
new file mode 100644
index 00000000..53aaee40
--- /dev/null
+++ b/kscd/libwm/plat_hpux.c
@@ -0,0 +1,358 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * HP/UX-specific drive control routines.
+ */
+
+#if defined(hpux) || defined(__hpux)
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ustat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+
+/*
+ * this is for glibc 2.x which the ust structure in
+ * ustat.h not stat.h
+ */
+#ifdef __GLIBC__
+#include <sys/ustat.h>
+#endif
+
+#include <sys/time.h>
+#include <sys/scsi.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+char *strchr();
+
+int min_volume = 0;
+int max_volume = 255;
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+
+/*-------------------------------------------------------------*
+ * Open the CD and figure out which kind of drive is attached.
+ *-------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd, flag = 1;
+ static int warned = 0;
+ /* unsigned ? */
+ char vendor[32], model[32], rev[32];
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ d->fd = open(d->cd_device, O_RDWR);
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ return -EACCES;
+ }
+ else if (errno != EINTR)
+ {
+ return (-6);
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Prepare the device to receive raw SCSI commands. */
+ if (ioctl(d->fd, SIOC_CMD_MODE, &flag) < 0)
+ {
+ fprintf(stderr, "%s: SIOC_CMD_MODE: true: %s\n",
+ d->cd_device, strerror(errno));
+ /*exit(1);*/
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+
+ /* Default drive is the HP one, which might not respond to INQUIRY */
+ strcpy(&vendor, "TOSHIBA");
+ strcpy(&model, "XM-3301");
+ rev[0] = '\0';
+ wm_scsi_get_drive_type(d, vendor, model, rev);
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*----------------------------------*
+ * Send a SCSI command out the bus.
+ *----------------------------------*/
+int
+wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply )
+{
+#ifdef SIOC_IO
+ struct sctl_io cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cdb_length = cdblen;
+ cmd.data = retbuf;
+ cmd.data_length = retbuflen;
+ cmd.max_msecs = 1000;
+ cmd.flags = getreply ? SCTL_READ : 0;
+ memcpy(cmd.cdb, cdb, cdblen);
+
+ return (ioctl(d->fd, SIOC_IO, &cmd));
+#else
+ /* this code, for pre-9.0, is BROKEN! ugh. */
+ char reply_buf[12];
+ struct scsi_cmd_parms cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.clock_ticks = 500;
+ cmd.cmd_mode = 1;
+ cmd.cmd_type = cdblen;
+ memcpy(cmd.command, cdb, cdblen);
+ if (ioctl(d->fd, SIOC_SET_CMD, &cmd) < 0)
+ return (-1);
+
+ if (! retbuf || ! retbuflen)
+ (void) read(d->fd, reply_buf, sizeof(reply_buf));
+ else if (getreply)
+ {
+ if (read(d->fd, retbuf, retbuflen) < 0)
+ return (-1);
+ }
+ else
+ if (write(d->fd, retbuf, retbuflen) < 0)
+ return (-1);
+
+ return (0);
+#endif
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode, int *mode,
+ int *pos, int *track, int *index )
+{
+ return (wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index));
+} /* gen_get_drive_status() */
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks )
+{
+ return (wm_scsi2_get_trackcount(d, tracks));
+} /* gen_get_trackcount() */
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo( struct wm_drive *d, int *track, int *data, int *startframe)
+{
+ return (wm_scsi2_get_trackinfo(d, track, data, startframe));
+} /* gen_get_trackinfo() */
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+
+ return (wm_scsi2_get_cdlen(d, frames));
+} /* gen_get_cdlen() */
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+ return (wm_scsi2_play(d, start, end));
+} /* gen_play() */
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause( struct wm_drive *d )
+{
+ return (wm_scsi2_pause(d));
+} /* gen_pause() */
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume( struct wm_drive *d )
+{
+ return (wm_scsi2_resume(d));
+} /* gen_resume() */
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop( struct wm_drive *d )
+{
+ return (wm_scsi2_stop(d));
+} /* gen_stop() */
+
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject( struct wm_drive *d )
+{
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ return (wm_scsi2_eject(d));
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ return (wm_scsi2_closetray(d));
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ return (wm_scsi2_set_volume(d, left, right));
+} /* gen_set_volume() */
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ return (wm_scsi2_get_volume(d, left, right));
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ /* This needs to be tested ! */
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+
+#endif
+
+
diff --git a/kscd/libwm/plat_irix.c b/kscd/libwm/plat_irix.c
new file mode 100644
index 00000000..7f1e3fc7
--- /dev/null
+++ b/kscd/libwm/plat_irix.c
@@ -0,0 +1,474 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * IRIX specific.
+ *
+ * Taken from the kscd distribution
+ *
+ * Paul Kendall
+ * paul@orion.co.nz, or
+ * paul@kcbbs.gen.nz
+ */
+
+#if defined(sgi) || defined(__sgi)
+
+#include "include/wm_config.h"
+
+/*
+ * Yes, it was designed for WorkMan 1.4b3
+ * Because I did start over from 1.3a, I disable it here.
+ * There is no guarantee of getting working code by defining
+ * CDDA yourself.
+ *
+ */
+#undef CDDA
+/*#define CDDA*/
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sigfpe.h>
+#include <dmedia/cdaudio.h>
+#include <dmedia/audio.h>
+#include <errno.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+char *strchr();
+
+int min_volume = 0;
+int max_volume = 255;
+
+#ifdef CDDA
+static int playing = STOPPED;
+static CDPLAYER *icd;
+static CDPARSER *icdp;
+static CDFRAME cdbuf[12];
+static ALport audioport;
+static ALconfig aconfig;
+static struct itimerval audiotimer = { {0,0}, {0,25000} };
+static int cdtrack=0;
+static int cdframe=0;
+static int cdstopframe=0;
+
+/*
+ * Platform specific internal functions for CDDA
+ */
+void
+cbprognum(void *arg, CDDATATYPES type, CDPROGNUM* prognum)
+{
+ cdtrack = prognum->value;
+} /* cbprognum() */
+
+void
+cbabstime(void *arg, CDDATATYPES type, struct cdtimecode* atime)
+{
+ cdframe = CDtctoframe(atime);
+ if( cdframe == cdstopframe )
+ playing = STOPPED;
+} /* cbabstime() */
+
+void
+cbplayaudio(void *arg, CDDATATYPES type, short* audio)
+{
+ if(playing != PLAYING) return;
+ ALwritesamps(audioport, audio, CDDA_NUMSAMPLES);
+} /* cbplayaudio() */
+
+static void
+alarmsignal()
+{
+ int n, i;
+ if(playing != PLAYING) return;
+ if( ALgetfilled(audioport) < CDDA_NUMSAMPLES*8 )
+ {
+ /* Only get more samples and play them if we're getting low
+ * this ensures that the CD stays close to the sound
+ */
+ n = CDreadda(icd, cdbuf, 12);
+ if( n == 0 ) return;
+ for( i=0 ; i<12 ; i++ )
+ CDparseframe(icdp, &cdbuf[i]);
+ }
+ signal(SIGALRM, alarmsignal);
+ setitimer(ITIMER_REAL, &audiotimer, NULL);
+} /* alarmsignal() */
+#endif
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init( struct wm_drive *d )
+{
+#ifdef CDDA
+ long Param[4];
+ /* Set the audio rate to 44100Hz 16bit 2s-comp stereo */
+ aconfig = ALnewconfig();
+ ALsetwidth(aconfig, AL_SAMPLE_16);
+ ALsetsampfmt(aconfig, AL_SAMPFMT_TWOSCOMP);
+ ALsetchannels(aconfig, 2);
+ Param[0] = AL_OUTPUT_RATE; Param[1] = AL_RATE_44100;
+ Param[2] = AL_CHANNEL_MODE; Param[3] = AL_STEREO;
+ ALsetparams(AL_DEFAULT_DEVICE, Param, 4);
+ audioport = ALopenport("KDE KSCD Audio", "w", aconfig);
+
+ /* setup cdparser */
+ icdp = CDcreateparser();
+ CDaddcallback(icdp, cd_audio, (CDCALLBACKFUNC)cbplayaudio, 0);
+ CDaddcallback(icdp, cd_pnum, (CDCALLBACKFUNC)cbprognum, 0);
+ CDaddcallback(icdp, cd_atime, (CDCALLBACKFUNC)cbabstime, 0);
+
+ /* Lets handle those floating point exceptions expeditiously. */
+ sigfpe_[_UNDERFL].repls = _ZERO;
+ handle_sigfpes(_ON, _EN_UNDERFL, NULL, _ABORT_ON_ERROR, NULL);
+#endif
+ return 0;
+} /* gen_init() */
+
+/*-------------------------------------------------------------*
+ * Open the CD and figure out which kind of drive is attached.
+ *-------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ CDSTATUS s;
+
+ if (d->fd < 0) /* Device already open? */
+ {
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ d->fd = 1;
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+ find_drive_struct("", "", "");
+ d->fd = fd;
+ (d->init)(d);
+
+ d->daux = CDopen(d->cd_device,"r");
+ if (d->daux == 0)
+ {
+ return (-6);
+ }
+#ifdef CDDA
+ icd = d->daux;
+#endif
+ } else {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ }
+
+ CDgetstatus(d->daux, &s);
+ if( s.state==CD_NODISC || s.state==CD_ERROR )
+ return 1;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+
+/*----------------------------------*
+ * Send a SCSI command out the bus.
+ *----------------------------------*/
+int
+wm_scsi( struct wm_drive *d, unsigned char *xcdb, int cdblen,
+ char *retbuf, int retbuflen, int getreply)
+{
+ return -1;
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track,
+ int *index )
+{
+#ifdef CDDA
+ *mode = playing;
+ *track = cdtrack;
+ *pos = cdframe;
+ *index = 0;
+#else
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ *pos = CDmsftoframe(s.min,s.sec,s.frame);
+ *track = s.track;
+ *index = 0;
+ switch( s.state )
+ {
+ case CD_READY: *mode = WM_CDM_STOPPED;
+ break;
+ case CD_STILL:
+ case CD_PAUSED: *mode = WM_CDM_PAUSED;
+ break;
+ case CD_PLAYING: *mode = WM_CDM_PLAYING;
+ break;
+ default: *mode = WM_CDM_UNKNOWN;
+ }
+#endif
+ return 0;
+} /* gen_get_drive_status() */
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount( struct wm_drive *d, int *tracks )
+{
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ *tracks = s.last;
+ return 0;
+} /* gen_get_trackcount() */
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe)
+{
+ CDTRACKINFO i;
+ int ret = CDgettrackinfo(d->daux, track, &i);
+ if( ret == 0 )
+ return -1;
+ *data = 0;
+ *startframe = CDmsftoframe(i.start_min,i.start_sec,i.start_frame);
+ return 0;
+} /* gen_get_trackinfo() */
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen( struct wm_drive *d, int *frames )
+{
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ *frames = CDmsftoframe(s.total_min,s.total_sec,s.total_frame);
+ return 0;
+} /* gen_get_cdlen() */
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+#ifdef CDDA
+ int m, s, f;
+ CDframetomsf(start, &m, &s, &f);
+ CDseek(icd, m, s, f);
+ cdstopframe = end;
+ playing = PLAYING;
+ signal(SIGALRM, alarmsignal);
+ setitimer(ITIMER_REAL, &audiotimer, NULL);
+#else
+ int m, s, f;
+ CDframetomsf(start, &m, &s, &f);
+ CDplayabs(d->daux, m, s, f, 1);
+#endif
+ return 0;
+} /* gen_play() */
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause( struct wm_drive *d )
+{
+#ifdef CDDA
+ playing = WM_CDM_PAUSED;
+#else
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ if(s.state == CD_PLAYING)
+ CDtogglepause(d->daux);
+#endif
+ return 0;
+} /* gen_pause() */
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume( struct wm_drive *d )
+{
+#ifdef CDDA
+ playing = WM_CDM_PLAYING;
+ signal(SIGALRM, alarmsignal);
+ setitimer(ITIMER_REAL, &audiotimer, NULL);
+#else
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ if(s.state == CD_PAUSED)
+ CDtogglepause(d->daux);
+#endif
+ return 0;
+} /* gen_resume() */
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop( struct wm_drive *d )
+{
+#ifdef CDDA
+ playing = WM_CDM_STOPPED;
+#else
+ CDstop(d->daux);
+#endif
+ return 0;
+} /* gen_stop() */
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject( struct wm_drive *d )
+{
+#ifdef CDDA
+ playing = WM_CDM_STOPPED;
+#endif
+ CDeject(d->daux);
+ return 0;
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@Deathsdoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ long Param[4];
+ Param[0] = AL_LEFT_SPEAKER_GAIN; Param[1] = left*255/100;
+ Param[2] = AL_RIGHT_SPEAKER_GAIN; Param[3] = right*255/100;
+ ALsetparams(AL_DEFAULT_DEVICE, Param, 4);
+ return 0;
+} /* gen_set_volume() */
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ long Param[4];
+ Param[0] = AL_LEFT_SPEAKER_GAIN; Param[1] = 0;
+ Param[2] = AL_RIGHT_SPEAKER_GAIN; Param[3] = 0;
+ ALgetparams(AL_DEFAULT_DEVICE, Param, 4);
+ *left = Param[1] * 100 / 255;
+ *right = Param[3] * 100 / 255;
+ return 0;
+} /* gen_get_volume() */
+
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif
+
diff --git a/kscd/libwm/plat_linux.c b/kscd/libwm/plat_linux.c
new file mode 100644
index 00000000..58768a40
--- /dev/null
+++ b/kscd/libwm/plat_linux.c
@@ -0,0 +1,803 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Linux-specific drive control routines. Very similar to the Sun module.
+ */
+
+#if defined(__linux__)
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+/* Try to get around bug #29274 */
+#include <linux/version.h>
+#if 0
+/* this breaks the build on ia64 and s390 for example.
+ sys/types.h is already included and should provide __u64.
+ please tell where we really need this and let's try to find
+ a working #if case for everyone ... adrian@suse.de */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,50)) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,21) && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+#undef __GNUC__
+typedef unsigned long long __u64;
+#endif
+#endif
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#if defined(BSD_MOUNTTEST)
+ #include <mntent.h>
+#else
+ /*
+ * this is for glibc 2.x which defines ust structure in
+ * ustat.h not stat.h
+ */
+ #ifdef __GLIBC__
+ #include <sys/ustat.h>
+ #endif
+#endif
+
+
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#ifndef __GNUC__
+#define __GNUC__ 1
+#endif
+/* needed for vanilla kernel headers, which do provide __u64 only
+ for ansi */
+#undef __STRICT_ANSI__
+/* needed for non-ansi kernel headers */
+#define asm __asm__
+#define inline __inline__
+#include <asm/types.h>
+#include <linux/cdrom.h>
+#undef asm
+#undef inline
+
+#include "include/wm_cdda.h"
+#include "include/wm_struct.h"
+#include "include/wm_platform.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_scsi.h"
+#include "include/wm_helpers.h"
+
+#ifdef OSS_SUPPORT
+#include <linux/soundcard.h>
+#define CD_CHANNEL SOUND_MIXER_CD
+#endif
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+
+#ifdef LINUX_SCSI_PASSTHROUGH
+/* this is from <scsi/scsi_ioctl.h> */
+# define SCSI_IOCTL_SEND_COMMAND 1
+#endif
+
+#ifdef BUILD_CDDA
+int gen_cdda_init( struct wm_drive *d );
+#endif
+
+int min_volume = 0;
+int max_volume = 255;
+
+#ifdef OSS_SUPPORT
+int mixer;
+char mixer_dev_name[20] = "/dev/mixer";
+#endif
+
+/*-------------------------------------------------------*
+ *
+ *
+ * CD-ROM drive functions.
+ *
+ *
+ *-------------------------------------------------------*/
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+/*---------------------------------------------------------------------------*
+ * Open the CD device and figure out what kind of drive is attached.
+ *---------------------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ char vendor[32], model[32], rev[32];
+
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+
+ if (d->fd >= 0) { /* Device already open? */
+/* wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);*/
+ return (0);
+ }
+
+ fd = open(d->cd_device, O_RDONLY | O_NONBLOCK);
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): device=%s fd=%d\n", d->cd_device, fd);
+
+ if (fd < 0)
+ return -errno;
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ d->fd = fd;
+
+ /*
+ * See if we can do digital audio.
+ */
+#if defined(BUILD_CDDA)
+ if(d->cdda && gen_cdda_init(d)) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): failed in gen_cdda_init\n");
+ gen_close(d);
+ return -1;
+ }
+#endif
+
+ /* Can we figure out the drive type? */
+ if (wm_scsi_get_drive_type(d, vendor, model, rev)) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): inquiry failed\n");
+ strcpy(vendor, "Generic");
+ strcpy(model, "drive type");
+ strcpy(rev, "");
+ }
+
+ if(find_drive_struct(vendor, model, rev) < 0) {
+ gen_close(d);
+ return -1;
+ }
+
+ if(d->proto->gen_init)
+ return (d->proto->gen_init)(d);
+
+ return 0;
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+ int tries = 0;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ gen_close(d);
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ tries++;
+ } while ( (status != 0) && (tries < 10) );
+ return status;
+} /* wmcd_reopen() */
+
+/*---------------------------------------------*
+ * Send an arbitrary SCSI command to a device.
+ *---------------------------------------------*/
+int
+wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply )
+{
+#ifdef LINUX_SCSI_PASSTHROUGH
+
+ char *cmd;
+ int cmdsize;
+
+ cmdsize = 2 * sizeof(int);
+ if (retbuf)
+ {
+ if (getreply) cmdsize += max(cdblen, retbuflen);
+ else cmdsize += (cdblen + retbuflen);
+ }
+ else cmdsize += cdblen;
+
+ cmd = malloc(cmdsize);
+ if (cmd == NULL)
+ return (-1);
+
+ ((int*)cmd)[0] = cdblen + ((retbuf && !getreply) ? retbuflen : 0);
+ ((int*)cmd)[1] = ((retbuf && getreply) ? retbuflen : 0);
+
+ memcpy(cmd + 2*sizeof(int), cdb, cdblen);
+ if (retbuf && !getreply)
+ memcpy(cmd + 2*sizeof(int) + cdblen, retbuf, retbuflen);
+
+ if (ioctl(d->fd, SCSI_IOCTL_SEND_COMMAND, cmd))
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "%s: ioctl() failure\n", __FILE__);
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "command buffer is:\n");
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "%02x %02x %02x %02x %02x %02x\n",
+ cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);
+ free(cmd);
+ return (-1);
+ }
+
+ if (retbuf && getreply)
+ memcpy(retbuf, cmd + 2*sizeof(int), retbuflen);
+
+ free(cmd);
+ return 0;
+
+#else /* Linux SCSI passthrough*/
+/*----------------------------------------*
+ * send packet over cdrom interface
+ * kernel >= 2.2.16
+ *----------------------------------------*/
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,2,15))
+
+ struct cdrom_generic_command cdc;
+ struct request_sense sense;
+ int capability;
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wm_scsi over CDROM_SEND_PACKET entered\n");
+
+ capability = ioctl(d->fd, CDROM_GET_CAPABILITY);
+
+ if(!(capability & CDC_GENERIC_PACKET))
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "your CDROM or/and kernel don't support CDC_GENERIC_PACKET ...\n");
+ return -1;
+ }
+
+ memset(&cdc, 0, sizeof(struct cdrom_generic_command));
+ memset(&sense, 0, sizeof(struct request_sense));
+
+ memcpy(cdc.cmd, cdb, cdblen);
+
+ cdc.buffer = retbuf;
+ cdc.buflen = retbuflen;
+ cdc.stat = 0;
+ cdc.sense = &sense;
+ cdc.data_direction = getreply?CGC_DATA_READ:CGC_DATA_WRITE;
+
+ /* sendpacket_over_cdrom_interface() */
+ return ioctl(d->fd, CDROM_SEND_PACKET, &cdc);
+#endif /* CDROM_SEND_PACKET */
+ printf("ERROR: this binary was compiled without CDROM GENERIC PACKET SUPPORT. kernel version < 2.2.16?\n");
+ printf("ERROR: if you have a SCSI CDROM, rebuild it with a #define LINUX_SCSI_PASSTHROUGH\n");
+ return (-1);
+#endif
+} /* wm_scsi */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*--------------------------------*
+ * Keep the CD open all the time.
+ * disabled, analogous to 1.4b3
+ *--------------------------------*
+void
+keep_cd_open( void )
+{
+ int fd;
+ struct flock fl;
+ extern end;
+
+
+ for (fd = 0; fd < 256; fd++)
+ close(fd);
+
+ if (fork())
+ exit(0);
+
+#if defined(O_NOFOLLOW)
+ if ((fd = open("/tmp/cd.lock", O_RDWR | O_CREAT | O_NOFOLLOW, 0666)) < 0)
+#else
+ if ((fd = open("/tmp/cd.lock", O_RDWR | O_CREAT, 0666)) < 0)
+#endif
+ exit(0);
+ fl.l_type = F_WRLCK;
+ fl.l_whence = 0;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ exit(0);
+
+ if (open(cd_device, 0) >= 0)
+ {
+ brk(&end);
+ pause();
+ }
+
+ exit(0);
+}
+*/
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind )
+{
+ struct cdrom_subchnl sc;
+ int ret;
+
+#ifdef SBPCD_HACK
+ static int prevpos = 0;
+#endif
+
+
+ /* Is the device open? */
+ if (d->fd < 0) {
+ ret = wmcd_open(d);
+ if(ret < 0) /* error */
+ return ret;
+
+ if(ret == 1) {
+ /* retry */
+ *mode = WM_CDM_UNKNOWN;
+ return 0;
+ }
+ }
+
+ /* Try to get rid of the door locking */
+ /* Don't care about return value. If it */
+ /* works - fine. If not - ... */
+ ioctl(d->fd, CDROM_LOCKDOOR, 0);
+
+ *mode = WM_CDM_UNKNOWN;
+
+ sc.cdsc_format = CDROM_MSF;
+
+#if defined(BUILD_CDDA)
+ IFCDDA(d) {
+ if(!cdda_get_drive_status(d, oldmode, mode, pos, track, ind)) {
+ if(*mode == WM_CDM_STOPPED)
+ *mode = WM_CDM_UNKNOWN; /* dont believe */
+ }
+ } else
+#endif
+ if(!ioctl(d->fd, CDROMSUBCHNL, &sc)) {
+ switch (sc.cdsc_audiostatus) {
+ case CDROM_AUDIO_PLAY:
+ *mode = WM_CDM_PLAYING;
+ *track = sc.cdsc_trk;
+ *ind = sc.cdsc_ind;
+ *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+ sc.cdsc_absaddr.msf.second * 75 +
+ sc.cdsc_absaddr.msf.frame;
+#ifdef SBPCD_HACK
+ if( *pos < prevpos ) {
+ if( (prevpos - *pos) < 75 ) {
+ *mode = WM_CDM_TRACK_DONE;
+ }
+ }
+
+ prevpos = *pos;
+#endif
+ break;
+
+ case CDROM_AUDIO_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) {
+ *mode = WM_CDM_PAUSED;
+ *track = sc.cdsc_trk;
+ *ind = sc.cdsc_ind;
+ *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+ sc.cdsc_absaddr.msf.second * 75 +
+ sc.cdsc_absaddr.msf.frame;
+ } else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CDROM_AUDIO_NO_STATUS:
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CDROM_AUDIO_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case CDROM_AUDIO_INVALID: /**/
+ default:
+ *mode = WM_CDM_UNKNOWN;
+ break;
+ }
+ }
+
+ if(WM_CDS_NO_DISC(*mode)) {
+ /* verify status of drive */
+ ret = ioctl(d->fd, CDROM_DRIVE_STATUS, 0/* slot */);
+ if(ret == CDS_DISC_OK)
+ ret = ioctl(d->fd, CDROM_DISC_STATUS, 0);
+ switch(ret) {
+ case CDS_NO_DISC:
+ *mode = WM_CDM_NO_DISC;
+ break;
+ case CDS_TRAY_OPEN:
+ *mode = WM_CDM_EJECTED;
+ break;
+ case CDS_AUDIO:
+ case CDS_MIXED:
+ *mode = WM_CDM_STOPPED;
+ break;
+ case CDS_DRIVE_NOT_READY:
+ case CDS_NO_INFO:
+ case CDS_DATA_1:
+ case CDS_DATA_2:
+ case CDS_XA_2_1:
+ case CDS_XA_2_2:
+ default:
+ *mode = WM_CDM_UNKNOWN;
+ }
+ }
+
+ return (0);
+} /* gen_get_drive_status */
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct cdrom_tochdr hdr;
+
+ if (ioctl(d->fd, CDROMREADTOCHDR, &hdr))
+ return (-1);
+
+ *tracks = hdr.cdth_trk1;
+ return (0);
+} /* gen_get_trackcount() */
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct cdrom_tocentry entry;
+
+ entry.cdte_track = track;
+ entry.cdte_format = CDROM_MSF;
+
+ if (ioctl(d->fd, CDROMREADTOCENTRY, &entry))
+ return (-1);
+
+ *startframe = entry.cdte_addr.msf.minute * 60 * 75 +
+ entry.cdte_addr.msf.second * 75 +
+ entry.cdte_addr.msf.frame;
+ *data = entry.cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0;
+
+ return (0);
+}
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+
+ return gen_get_trackinfo( d, CDROM_LEADOUT, &tmp, frames);
+} /* gen_get_cdlen() */
+
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play(struct wm_drive *d, int start, int end, int realstart)
+{
+ struct cdrom_msf msf;
+
+ CDDARETURN(d) cdda_play(d, start, end, realstart);
+
+ msf.cdmsf_min0 = start / (60*75);
+ msf.cdmsf_sec0 = (start % (60*75)) / 75;
+ msf.cdmsf_frame0 = start % 75;
+ msf.cdmsf_min1 = end / (60*75);
+ msf.cdmsf_sec1 = (end % (60*75)) / 75;
+ msf.cdmsf_frame1 = end % 75;
+
+ if (ioctl(d->fd, CDROMPLAYMSF, &msf)) {
+ if (ioctl(d->fd, CDROMSTART))
+ return (-1);
+ if (ioctl(d->fd, CDROMPLAYMSF, &msf))
+ return (-2);
+ }
+
+ /*
+ * I hope no drive gets really confused after CDROMSTART
+ * If so, I need to make this run-time configurable.
+ *
+#ifndef FAST_IDE
+ if (ioctl( d->fd, CDROMSTART))
+ return (-1);
+#endif
+ if (ioctl( d->fd, CDROMPLAYMSF, &msf ))
+ return (-2);
+ */
+
+ return (0);
+} /* gen_play() */
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause(struct wm_drive *d)
+{
+ CDDARETURN(d) cdda_pause(d);
+ return (ioctl(d->fd, CDROMPAUSE));
+}
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume(struct wm_drive *d)
+{
+ CDDARETURN(d) cdda_pause(d);
+ return (ioctl(d->fd, CDROMRESUME));
+}
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop(struct wm_drive *d)
+{
+ CDDARETURN(d) cdda_stop(d);
+ return (ioctl(d->fd, CDROMSTOP));
+}
+
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject(struct wm_drive *d)
+{
+ struct stat stbuf;
+#if !defined(BSD_MOUNTTEST)
+ struct ustat ust;
+#else
+ struct mntent *mnt;
+ FILE *fp;
+#endif
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "ejecting?\n");
+
+ if (fstat(d->fd, &stbuf) != 0) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "that weird fstat() thingy\n");
+ return (-2);
+ }
+
+ /* Is this a mounted filesystem? */
+#if !defined(BSD_MOUNTTEST)
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+#else
+ /*
+ * This is the same test as in the WorkBone interface.
+ * I should eliminate it there, because there is no need
+ * for it in the UI
+ */
+ /* check if drive is mounted (from Mark Buckaway's cdplayer code) */
+ /* Changed it again (look at XPLAYCD from ???? */
+ /* It's better to check the device name rather than one device is */
+ /* mounted as iso9660. That prevents "no playing" if you have more*/
+ /* than one CD-ROM, and one of them is mounted, but it's not the */
+ /* audio CD -dirk */
+ if ((fp = setmntent (MOUNTED, "r")) == NULL)
+ {
+ wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS, "Could not open %s: %s\n", MOUNTED, strerror (errno));
+ return(-3);
+ }
+ while ((mnt = getmntent (fp)) != NULL)
+ {
+ if (strcmp (mnt->mnt_fsname, d->cd_device) == 0)
+ {
+ wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS, "CDROM already mounted (according to mtab). Operation aborted.\n");
+ endmntent (fp);
+ return(-3);
+ }
+ }
+ endmntent (fp);
+#endif /* BSD_MOUNTTEST */
+
+ IFCDDA(d) {
+ cdda_eject(d);
+ }
+
+ ioctl( d->fd, CDROM_LOCKDOOR, 0 );
+
+ if (ioctl(d->fd, CDROMEJECT))
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "eject failed (%s).\n", strerror(errno));
+ return (-1);
+ }
+
+ /*------------------
+ * Things in "foobar_one" are left over from 1.4b3
+ * I put them here for further observation. In 1.4b3, however,
+ * that workaround didn't help at least for /dev/sbpcd
+ * (The tray closed just after ejecting because re-opening the
+ * device causes the tray to close)
+ *------------------*/
+#ifdef foobar_one
+ extern int intermittent_dev
+ /*
+ * Some drives (drivers?) won't recognize a new CD if we leave the
+ * device open.
+ */
+ if (intermittent_dev)
+ gen_close(d);
+#endif
+
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+#ifdef CDROMCLOSETRAY
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "CDROMCLOSETRAY closing tray...\n");
+ if (ioctl(d->fd, CDROMCLOSETRAY))
+ return (-1);
+#else
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen() closing tray...\n");
+ if(!gen_close(d))
+ {
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#endif /* CDROMCLOSETRAY */
+#endif /* CAN_CLOSE */
+ /* Always succeed if the drive can't close. */
+ return(0);
+} /* gen_closetray() */
+
+
+/*------------------------------------------------------------------------*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ * This is not used if sound card support is enabled.
+ *
+ *------------------------------------------------------------------------*/
+static int
+scale_volume( int vol, int max )
+{
+#ifdef CURVED_VOLUME
+ return ((max * max - (max - vol) * (max - vol)) *
+ (max_volume - min_volume) / (max * max) + min_volume);
+#else
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+#endif
+} /* scale_volume() */
+
+static int
+unscale_volume( int vol, int max )
+{
+#ifdef CURVED_VOLUME
+ /* FIXME do it simpler */
+ int tmp = (((max_volume - min_volume - vol) * max * max) - (vol + min_volume));
+ return max - sqrt((tmp/(max_volume - min_volume)));
+#else
+ return (((vol - min_volume) * max) / (max_volume - min_volume));
+#endif
+} /* unscale_volume() */
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ struct cdrom_volctrl v;
+
+ CDDARETURN(d) cdda_set_volume(d, left, right);
+
+ /* Adjust the volume to make up for the CD-ROM drive's weirdness. */
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ v.channel0 = v.channel2 = left < 0 ? 0 : left > 255 ? 255 : left;
+ v.channel1 = v.channel3 = right < 0 ? 0 : right > 255 ? 255 : right;
+
+ return (ioctl(d->fd, CDROMVOLCTRL, &v));
+} /* gen_set_volume() */
+
+/*---------------------------------------------------------------------*
+ * Read the volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ struct cdrom_volctrl v;
+
+ CDDARETURN(d) cdda_get_volume(d, left, right);
+
+#if defined(CDROMVOLREAD)
+ if(!ioctl(d->fd, CDROMVOLREAD, &v)) {
+ *left = unscale_volume((v.channel0 + v.channel2)/2, 100);
+ *right = unscale_volume((v.channel1 + v.channel3)/2, 100);
+ } else
+#endif
+ /* Suns, HPs, Linux, NEWS can't read the volume; oh well */
+ *left = *right = -1;
+
+ return 0;
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *
+ * Return a buffer with cdtext-stream. buffer will be allocated and filled
+ *
+ * needs send packet interface -> for IDE, linux at 2.2.16
+ * depends on scsi.c which depends on wm_scsi defined in here
+ * (which also takes care of IDE drives)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+#endif /* __linux__ */
diff --git a/kscd/libwm/plat_linux_audio.c b/kscd/libwm/plat_linux_audio.c
new file mode 100644
index 00000000..7316a691
--- /dev/null
+++ b/kscd/libwm/plat_linux_audio.c
@@ -0,0 +1,489 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Linux digital audio functions.
+ */
+
+#include "include/wm_config.h"
+
+static char plat_linux_audio_id[] = "$Id$";
+
+#if defined(__linux__) && defined(BUILD_CDDA) /* { */
+
+#include "include/wm_cdda.h"
+
+/* types.h included by wm_cdda.h */
+
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#include <sys/stropts.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * Since there's a lag time between writing audio to the audio device and
+ * hearing it, we need to make sure the status indicators correlate to what's
+ * playing out the speaker. Luckily, Solaris gives us some audio
+ * synchronization facilities that make this pretty easy.
+ *
+ * We maintain a circular queue of status information. When we write some
+ * sound to the audio device, we put its status info into the queue. We write
+ * a marker into the audio stream; when the audio device driver encounters the
+ * marker, it increments a field in a status structure. When we see that
+ * field go up, we grab the next status structure from the queue and send it
+ * to the parent process.
+ *
+ * The minimum size of the queue depends on the latency of the audio stream.
+ */
+#define QSIZE 500
+
+struct cdda_block queue[QSIZE];
+int qtail;
+int qstart;
+
+/*
+ * We only send WMCDDA_PLAYED status messages upstream when the CD is supposed
+ * to be playing; this is used to keep track.
+ */
+extern int playing;
+
+static int aufd, aucfd;
+static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */
+
+/*
+ * For fast linear-to-ulaw mapping, we use a lookup table that's generated
+ * at startup.
+ */
+unsigned char *ulawmap, linear_to_ulaw();
+
+char *getenv();
+
+/*
+ * Dummy signal handler so writes to /dev/audio will interrupt.
+ */
+static void
+dummy( void )
+{
+ signal(SIGALRM, dummy);
+}
+
+/*
+ * Initialize the audio device.
+ */
+void
+wmaudio_init( void )
+{
+ audio_info_t info;
+ char *audiodev, *acdev;
+ int linval;
+
+ audiodev = getenv("AUDIODEV");
+ if (audiodev == NULL)
+ audiodev = "/dev/audio";
+
+ acdev = malloc(strlen(audiodev) + 4);
+ if (acdev == NULL)
+ {
+ perror("Can't allocate audio control filename");
+ exit(1);
+ }
+ strcpy(acdev, audiodev);
+ strcat(acdev, "ctl");
+
+ aucfd = open(acdev, O_WRONLY, 0);
+ if (aucfd < 0)
+ {
+ perror(acdev);
+ exit(1);
+ }
+ free(acdev);
+
+ aufd = open(audiodev, O_WRONLY, 0);
+ if (aufd < 0)
+ {
+ perror(audiodev);
+ exit(1);
+ }
+
+ signal(SIGALRM, dummy);
+
+ /*
+ * Try to set the device to CD-style audio; we can process it
+ * with the least CPU overhead.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 44100;
+ info.play.channels = 2;
+ info.play.precision = 16;
+ info.play.encoding = AUDIO_ENCODING_LINEAR;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ if (errno == EINVAL)
+ {
+ /*
+ * Oh well, so much for that idea.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 8000;
+ info.play.channels = 1;
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_ULAW;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ {
+ perror("Can't set up audio device");
+ exit(1);
+ }
+
+ /*
+ * Initialize the linear-to-ulaw mapping table.
+ */
+ if (ulawmap == NULL)
+ ulawmap = malloc(65536);
+ if (ulawmap == NULL)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ for (linval = 0; linval < 65536; linval++)
+ ulawmap[linval] = linear_to_ulaw(linval-32768);
+ ulawmap += 32768;
+ raw_audio = 0;
+ }
+ else
+ {
+ perror(audiodev);
+ exit(1);
+ }
+}
+
+/*
+ * Get ready to play some sound.
+ */
+void
+wmaudio_ready( void )
+{
+ audio_info_t info;
+
+ /*
+ * Start at the correct queue position.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ qtail = info.play.eof % QSIZE;
+ qstart = qtail;
+
+ queue[qtail].status = WMCDDA_OK;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+void
+wmaudio_stop( void )
+{
+ if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
+ perror("flush");
+}
+
+/*
+ * Close the audio device.
+ */
+void
+wmaudio_close( void )
+{
+ wmaudio_stop();
+ close(aufd);
+ close(aucfd);
+}
+
+/*
+ * Set the volume level.
+ */
+void
+wmaudio_volume(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ info.play.gain = level;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Set the balance level.
+ */
+void
+wmaudio_balance(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ level *= AUDIO_RIGHT_BALANCE;
+ info.play.balance = level / 255;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Mark the most recent audio block on the queue as the last one.
+ */
+void
+wmaudio_mark_last( void )
+{
+ queue[qtail].status = WMCDDA_DONE;
+}
+
+/*
+ * Figure out the most recent status information and send it upstream.
+ */
+int
+wmaudio_send_status( void )
+{
+ audio_info_t info;
+ int qhead;
+
+ /*
+ * Now send the most current status information to our parent.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ qhead = info.play.eof % QSIZE;
+
+ if (qhead != qstart && playing)
+ {
+ int balance;
+
+ if (queue[qhead].status != WMCDDA_DONE)
+ queue[qhead].status = WMCDDA_PLAYED;
+ queue[qhead].volume = info.play.gain;
+ queue[qhead].balance = (info.play.balance * 255) /
+ AUDIO_RIGHT_BALANCE;
+
+ send_status(queue + qhead);
+ qstart = -1;
+ }
+
+ return (queue[qhead].status == WMCDDA_DONE);
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+wmaudio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ int i;
+ short *buf16;
+ int alarmcount = 0;
+ struct itimerval it;
+
+ alarm(1);
+
+ while (write(aufd, rawbuf, buflen) <= 0)
+ if (errno == EINTR)
+ {
+ if (! raw_audio && alarmcount++ < 5)
+ {
+ /*
+ * 8KHz /dev/audio blocks for several seconds
+ * waiting for its queue to drop below a low
+ * water mark.
+ */
+ wmaudio_send_status();
+ timerclear(&it.it_interval);
+ timerclear(&it.it_value);
+ it.it_value.tv_usec = 500000;
+ setitimer(ITIMER_REAL, &it, NULL);
+ continue;
+ }
+
+/* close(aufd);
+ close(aucfd);
+ wmaudio_init();
+*/ wmaudio_stop( void );
+ alarm(2);
+ continue;
+ }
+ else
+ {
+ blk->status = WMCDDA_ERROR;
+ return (-1);
+ }
+ alarm(0);
+
+ /*
+ * Mark this spot in the audio stream.
+ *
+ * Marks don't always succeed (if the audio buffer is empty
+ * this call will block forever) so do it asynchronously.
+ */
+ fcntl(aufd, F_SETFL, O_NONBLOCK);
+ if (write(aufd, rawbuf, 0) < 0)
+ {
+ if (errno != EAGAIN)
+ perror("audio mark");
+ }
+ else
+ qtail = (qtail + 1) % QSIZE;
+
+ fcntl(aufd, F_SETFL, 0);
+
+ queue[qtail] = *blk;
+
+ if (wmaudio_send_status() < 0)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Get the current audio state.
+ */
+void
+wmaudio_state(struct cdda_block *blk)
+{
+ audio_info_t info;
+ int balance;
+
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ blk->volume = info.play.gain;
+ blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
+}
+
+/*
+** This routine converts from linear to ulaw.
+**
+** Craig Reese: IDA/Supercomputing Research Center
+** Joe Campbell: Department of Defense
+** 29 September 1989
+**
+** References:
+** 1) CCITT Recommendation G.711 (very difficult to follow)
+** 2) "A New Digital Technique for Implementation of Any
+** Continuous PCM Companding Law," Villeret, Michel,
+** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
+** 1973, pg. 11.12-11.17
+** 3) MIL-STD-188-113,"Interoperability and Performance Standards
+** for Analog-to_Digital Conversion Techniques,"
+** 17 February 1987
+**
+** Input: Signed 16 bit linear sample
+** Output: 8 bit ulaw sample
+*/
+#define ZEROTRAP /* turn on the trap as per the MIL-STD */
+#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+unsigned char
+linear_to_ulaw( sample )
+int sample;
+{
+ static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if ( sign != 0 ) sample = -sample; /* get magnitude */
+ if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[( sample >> 7 ) & 0xFF];
+ mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
+ ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
+#ifdef ZEROTRAP
+ if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+
+ return ulawbyte;
+}
+
+/*
+ * Downsample a block of CDDA data, if necessary, for playing out an old-style
+ * audio device.
+ */
+long
+wmaudio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ short *buf16 = (short *)rawbuf;
+ int i, j, samples;
+ int mono_value;
+ unsigned char *rbend = rawbuf + buflen;
+
+ /* Don't do anything if the audio device can take the raw values. */
+ if (raw_audio)
+ return (buflen);
+
+ for (i = 0; buf16 < (short *)(rbend); i++)
+ {
+ /* Downsampling to 8KHz is a little irregular. */
+ samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
+
+ /* And unfortunately, we don't always end on a nice boundary. */
+ if (buf16 + samples > (short *)(rbend))
+ samples = ((short *)rbend) - buf16;
+
+ /*
+ * No need to average all the values; taking the first one
+ * is sufficient and less CPU-intensive. But we do need to
+ * do both channels.
+ */
+ mono_value = (buf16[0] + buf16[1]) / 2;
+ buf16 += samples;
+ rawbuf[i] = ulawmap[mono_value];
+ }
+
+ return (i);
+}
+
+#endif /* ... && BUILD_CDDA */
diff --git a/kscd/libwm/plat_linux_cdda.c b/kscd/libwm/plat_linux_cdda.c
new file mode 100644
index 00000000..6456e10a
--- /dev/null
+++ b/kscd/libwm/plat_linux_cdda.c
@@ -0,0 +1,253 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk F�sterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Linux CDDA functions. Derived from the SUN module.
+ */
+
+#include "include/wm_cdda.h"
+
+#if defined(__linux__) && defined(BUILD_CDDA)
+
+#ifndef __GNUC__
+#define __GNUC__ 1
+#endif
+/* needed for vanilla kernel headers, which do provide __u64 only
+ for ansi */
+#undef __STRICT_ANSI__
+/* needed for non-ansi kernel headers */
+#define asm __asm__
+#define inline __inline__
+#include <asm/types.h>
+#include <linux/cdrom.h>
+#undef asm
+#undef inline
+
+#include <stdio.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_cdda.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define CDDABLKSIZE 2352
+
+/* Address of next block to read. */
+static int current_position;
+
+/* Address of last block to read. */
+static int ending_position;
+
+static struct cdrom_read_audio cdda;
+static long wmcdda_normalize(struct cdda_block *block);
+
+
+/*
+ * Initialize the CDDA data buffer and open the appropriate device.
+ *
+ */
+int
+wmcdda_init(struct cdda_device* pdev)
+{
+ int i;
+
+ if (pdev->fd > -1)
+ return -1;
+
+ if(!pdev->devname)
+ return -1;
+
+ for (i = 0; i < pdev->numblocks; i++) {
+ /* in Linux const */
+ pdev->blocks[i].buflen = pdev->frames_at_once * CDDABLKSIZE;
+ pdev->blocks[i].buf = malloc(pdev->blocks[i].buflen);
+ if (!pdev->blocks[i].buf) {
+ ERRORLOG("wmcdda_init ENOMEM\n");
+ return -ENOMEM;
+ }
+ }
+
+ pdev->fd = open(pdev->devname, O_RDONLY | O_NONBLOCK);
+
+ if (pdev->fd > -1) {
+ cdda.addr_format = CDROM_LBA;
+ cdda.addr.lba = 200;
+ cdda.nframes = 1;
+ cdda.buf = (unsigned char*)pdev->blocks[0].buf;
+
+ pdev->status = WM_CDM_STOPPED;
+ if((ioctl(pdev->fd, CDROMREADAUDIO, &cdda) < 0)) {
+ if (errno == ENXIO) {
+ /* CD ejected! */
+ pdev->status = WM_CDM_EJECTED;
+ return 0;
+ } else {
+ /* Sometimes it fails once, dunno why */
+ pdev->status = WM_CDM_CDDAERROR;
+ return 0;
+ }
+ }
+ } else {
+ ERRORLOG("canot open device, errno %i\n", errno);
+ pdev->status = WM_CDM_UNKNOWN;
+ return -1;
+ }
+
+ pdev->status = WM_CDM_UNKNOWN;
+ return 0;
+}
+
+/*
+ * Close the CD-ROM device in preparation for exiting.
+ */
+int
+wmcdda_close(struct cdda_device* pdev)
+{
+ int i;
+
+ if(-1 == pdev->fd)
+ return -1;
+
+ close(pdev->fd);
+ pdev->fd = -1;
+
+ for (i = 0; i < pdev->numblocks; i++) {
+ free(pdev->blocks[i].buf);
+ pdev->blocks[i].buf = 0;
+ pdev->blocks[i].buflen = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Set up for playing the CD. Actually this doesn't play a thing, just sets a
+ * couple variables so we'll know what to do when we're called.
+ */
+int
+wmcdda_setup(int start, int end, int realstart)
+{
+ current_position = start;
+ ending_position = end;
+
+ return 0;
+}
+
+/*
+ * Read some blocks from the CD. Stop if we hit the end of the current region.
+ *
+ * Returns number of bytes read, -1 on error, 0 if stopped for a benign reason.
+ */
+long
+wmcdda_read(struct cdda_device* pdev, struct cdda_block *block)
+{
+ if (pdev->fd < 0 && wmcdda_init(pdev)) {
+ return -1;
+ }
+
+ /* Hit the end of the CD, probably. */
+ if (current_position >= ending_position) {
+ block->status = WM_CDM_TRACK_DONE;
+ return 0;
+ }
+
+ cdda.addr_format = CDROM_LBA;
+ cdda.addr.lba = current_position - CD_MSF_OFFSET;
+ if (ending_position && current_position + pdev->frames_at_once > ending_position)
+ cdda.nframes = ending_position - current_position;
+ else
+ cdda.nframes = pdev->frames_at_once;
+
+ cdda.buf = (unsigned char*)block->buf;
+
+ if (ioctl(pdev->fd, CDROMREADAUDIO, &cdda) < 0) {
+ if (errno == ENXIO) {
+ /* CD ejected! */
+ block->status = WM_CDM_EJECTED;
+ return 0;
+ } else {
+ /* Sometimes it fails once, dunno why */
+ block->status = WM_CDM_CDDAERROR;
+ return 0;
+ }
+ }
+
+ block->track = -1;
+ block->index = 0;
+ block->frame = current_position;
+ block->status = WM_CDM_PLAYING;
+ block->buflen = cdda.nframes * CDDABLKSIZE;
+
+ current_position = current_position + cdda.nframes;
+
+ return wmcdda_normalize(block);
+}
+
+/*
+ * Normalize a bunch of CDDA data. Basically this means doing byte-swapping, since the CD audio is in
+ * littleendian format.
+ */
+long
+wmcdda_normalize(struct cdda_block *block)
+{
+#if WM_BIG_ENDIAN
+ int i;
+ int blocks = block->buflen / CDDABLKSIZE;
+ char *rawbuf = block->buf;
+ char *dest = block->buf;
+
+ while (blocks--) {
+ for (i = 0; i < CDDABLKSIZE / 2; i++) {
+ *dest++ = rawbuf[1];
+ *dest++ = rawbuf[0];
+ rawbuf += 2;
+ }
+ }
+#endif
+ return block->buflen;
+}
+
+/*
+ * Set the playback direction.
+ */
+void
+wmcdda_direction(int newdir)
+{
+}
+
+/*
+ * Do system-specific stuff to get ready to play at a particular speed.
+ */
+void
+wmcdda_speed(int speed)
+{
+}
+
+#endif
diff --git a/kscd/libwm/plat_news.c b/kscd/libwm/plat_news.c
new file mode 100644
index 00000000..0e95b01d
--- /dev/null
+++ b/kscd/libwm/plat_news.c
@@ -0,0 +1,442 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sony NEWS-specific drive control routines.
+ */
+
+static char plat_news_id[] = "$Id$";
+
+#if defined( __sony_news) || defined(sony_news)
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ustat.h>
+#include <CD.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+char *strchr();
+
+extern int intermittent_dev;
+
+int min_volume = 128;
+int max_volume = 255;
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ intermittent_dev = 1;
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ if ((d->fd = CD_Open(d->cd_device, 0)) < 0)
+ {
+ /* Solaris 2.2 volume manager moves links around */
+ if (errno == ENOENT && intermittent_dev)
+ return (0);
+
+ if (errno == EACCES)
+ {
+ if (!warned)
+ {
+ /*
+ char realname[MAXPATHLEN];
+
+ if (realpath(cd_device, realname) == NULL)
+ {
+ perror("realpath");
+ return 1;
+ }
+ */
+ return -EACCES;
+ }
+ }
+ else if (errno != EIO) /* defined at top */
+ {
+ return (-6);
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+
+ /* Figure out the drive type, if possible */
+ wm_scsi_get_drive_type(d, vendor, model, rev);
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Pass SCSI commands to the device.
+ */
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
+ unsigned char *buf, int buflen, int getreply)
+{
+ /* NEWS can't do SCSI passthrough... or can it? */
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ int ret = 0;
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device.\n");
+ ret = CD_Close(d->fd);
+ d->fd = -1;
+ wm_susleep(3000000);
+ }
+ return (ret);
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct CD_Status sc;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d)) {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ /* Disc is ejected. Close the device. */
+ if (CD_GetStatus(d->fd, &sc))
+ {
+ gen_close(d);
+ return (0);
+ }
+
+ switch (sc.status) {
+ case CDSTAT_PLAY:
+ *mode = PLAYING;
+ *track = sc.tno;
+ *index = sc.index;
+ *pos = sc.baddr;
+ break;
+
+ case CDSTAT_PAUSE:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ *track = sc.tno;
+ *index = sc.index;
+ *pos = sc.baddr;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CDSTAT_STOP:
+ if (oldmode == WM_CDM_PLAYING)
+ {
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+ }
+ /* fall through */
+
+ default:
+ *mode = WM_CDM_STOPPED;
+ break;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct CD_Capacity cc;
+
+ if (CD_GetCapacity(d->fd, &cc))
+ return (-1);
+
+ *tracks = cc.etrack - 1;
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe )
+{
+ struct CD_TOCinfo hdr;
+ struct CD_TOCdata ent;
+
+ hdr.strack = track;
+ hdr.ntrack = 1;
+ hdr.data = &ent;
+ if (CD_ReadTOC(d->fd, &hdr))
+ return (-1);
+
+ *data = (ent.control & 4) ? 1 : 0;
+ *startframe = ent.baddr;
+
+ return (0);
+} /* gen_get_trackinfo */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen( struct wm_drive *d, int *frames )
+{
+ int tmp;
+
+ if ((d->get_trackcount)(d, &tmp))
+ return (-1);
+
+ return (gen_get_trackinfo(d, tmp + 1, &tmp, frames));
+} /* gen_get_cdlen() */
+
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+ struct CD_PlayAddr msf;
+
+ msf.addrmode = CD_MSF;
+ msf.addr.msf.startmsf.min = start / (60*75);
+ msf.addr.msf.startmsf.sec = (start % (60*75)) / 75;
+ msf.addr.msf.startmsf.frame = start % 75;
+ msf.addr.msf.endmsf.min = end / (60*75);
+ msf.addr.msf.endmsf.sec = (end % (60*75)) / 75;
+ msf.addr.msf.endmsf.frame = end % 75;
+
+ if (CD_Play(d->fd, &msf))
+ {
+ printf("wm_cd_play_chunk(%d,%d)\n",start,end);
+ printf("msf = %d:%d:%d %d:%d:%d\n",
+ msf.addr.msf.startmsf.min,
+ msf.addr.msf.startmsf.sec,
+ msf.addr.msf.startmsf.frame,
+ msf.addr.msf.endmsf.min,
+ msf.addr.msf.endmsf.sec,
+ msf.addr.msf.endmsf.frame);
+ perror("CD_Play");
+ return (-1);
+ }
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ CD_Pause(d->fd);
+ return (0);
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ CD_Restart(d->fd);
+ return (0);
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop( struct wm_drive *d )
+{
+ CD_Stop(d->fd);
+ return (0);
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject( struct wm_drive *d )
+{
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ if (CD_AutoEject(d->fd))
+ return (-1);
+
+ /* Close the device if it needs to vanish. */
+ if (intermittent_dev)
+ {
+ gen_close(d);
+ }
+
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!gen_close(d))
+ {
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume( struct wm_drive *d, int left, int right)
+{
+ /* NEWS can't adjust volume! */
+ return (0);
+}
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume( struct wm_drive *d, omt *left, int *right)
+{
+ /* Suns, HPs, Linux, NEWS can't read the volume; oh well */
+ *left = *right = -1;
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+#endif
diff --git a/kscd/libwm/plat_openbsd.c b/kscd/libwm/plat_openbsd.c
new file mode 100644
index 00000000..a3b0fd47
--- /dev/null
+++ b/kscd/libwm/plat_openbsd.c
@@ -0,0 +1,545 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * OpenBSD-specific drive control routines. (Based on plat_freebsd.c)
+ *
+ * Michael Shalayeff, 7/24/96
+ * Todd Pfaff, 3/20/94
+ *
+ */
+
+#if defined(__OpenBSD__) || defined(__OpenBSD)
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_helpers.h"
+
+/* this is for glibc 2.x which defines the ust structure in ustat.h not stat.h */
+#ifdef __GLIBC__
+#include <sys/ustat.h>
+#endif
+
+#include <sys/time.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/cdio.h>
+#include <sys/scsiio.h>
+#include <scsi/scsi_all.h>
+#include <scsi/cd.h>
+#include <scsi/scsi_cd.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+
+int min_volume = 10;
+int max_volume = 255;
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+
+char *cds[] = {"/dev/rcd0c", "/dev/rcd1c", "/dev/acd0c", NULL};
+
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open(struct wm_drive *d)
+{
+ int fd;
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+ int i;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ {
+ for (i = 0; cds[i] != NULL; i++)
+ {
+ d->cd_device = cds[i];
+ d->fd = open(d->cd_device, O_RDONLY);
+ if (d->fd >= 0)
+ break;
+ }
+ }
+ else
+ d->fd = open(d->cd_device, O_RDONLY);
+ if (d->fd < 0)
+ {
+ if (errno == EIO)
+ /* No CD in drive. */
+ return 1;
+ else
+ return -errno;
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+
+ find_drive_struct(vendor, model, rev);
+
+ (d->init)(d);
+
+ d->fd = fd;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Send an arbitrary SCSI command to a device.
+ *
+ */
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb,
+ int cdblen, void *retbuf, int retbuflen, int getreply)
+{
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ }
+ d->fd = -1;
+ return 0;
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct ioc_read_subchannel sc;
+ struct cd_sub_channel_info scd;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ sc.address_format = CD_MSF_FORMAT;
+ sc.data_format = CD_CURRENT_POSITION;
+ sc.track = 0;
+ sc.data_len = sizeof(scd);
+ sc.data = (struct cd_sub_channel_info *)&scd;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d)) {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (ioctl(d->fd, CDIOCREADSUBCHANNEL, &sc))
+ {
+ /* we need to release the device so the kernel will notice
+ reloaded media */
+ (void) close(d->fd);
+ d->fd = -1;
+ return (0); /* ejected */
+ }
+
+ switch (scd.header.audio_status)
+ {
+ case CD_AS_PLAY_IN_PROGRESS:
+ *mode = WM_CDM_PLAYING;
+ dopos:
+ *pos = scd.what.position.absaddr.msf.minute * 60 * 75 +
+ scd.what.position.absaddr.msf.second * 75 +
+ scd.what.position.absaddr.msf.frame;
+ *track = scd.what.position.track_number;
+ *index = scd.what.position.index_number;
+ break;
+
+ case CD_AS_PLAY_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ goto dopos;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CD_AS_PLAY_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case CD_AS_NO_STATUS:
+ case 0:
+ *mode = WM_CDM_STOPPED;
+ break;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct ioc_toc_header hdr;
+
+ if (ioctl(d->fd, CDIOREADTOCHEADER, &hdr) == -1)
+ return (-1);
+
+ *tracks = hdr.ending_track - hdr.starting_track + 1;
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct ioc_read_toc_entry toc;
+ struct cd_toc_entry toc_buffer;
+
+ bzero((char *)&toc_buffer, sizeof(toc_buffer));
+ toc.address_format = CD_MSF_FORMAT;
+ toc.starting_track = track;
+ toc.data_len = sizeof(toc_buffer);
+ toc.data = &toc_buffer;
+
+ if (ioctl(d->fd, CDIOREADTOCENTRYS, &toc))
+ return (-1);
+
+ *data = ((toc_buffer.control & 0x4) != 0);
+
+ *startframe = toc_buffer.addr.msf.minute*60*75 +
+ toc_buffer.addr.msf.second * 75 +
+ toc_buffer.addr.msf.frame;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+ struct ioc_toc_header hdr;
+ int status;
+
+#define LEADOUT 0xaa /* see scsi.c. what a hack! */
+ return gen_get_trackinfo(d, LEADOUT, &tmp, frames);
+} /* gen_get_cdlen() */
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+ struct ioc_play_msf msf;
+
+ msf.start_m = start / (60*75);
+ msf.start_s = (start % (60*75)) / 75;
+ msf.start_f = start % 75;
+ msf.end_m = end / (60*75);
+ msf.end_s = (end % (60*75)) / 75;
+ msf.end_f = end % 75;
+
+ if (ioctl(d->fd, CDIOCSTART))
+ return (-1);
+
+ if (ioctl(d->fd, CDIOCPLAYMSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause(struct wm_drive *d)
+{
+ return (ioctl(d->fd, CDIOCPAUSE));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume(struct wm_drive *d)
+{
+ return (ioctl(d->fd, CDIOCRESUME));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ return (ioctl(d->fd, CDIOCSTOP));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ /* On some systems, we can check to see if the CD is mounted. */
+ struct stat stbuf;
+ struct statfs buf;
+ int rval;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (fstatfs(stbuf.st_rdev, &buf) == 0)
+ return (-3);
+
+ rval = ioctl(d->fd, CDIOCALLOW);
+ if (rval == 0)
+ rval = ioctl(d->fd, CDIOCEJECT);
+ if (rval == 0)
+ rval = ioctl(d->fd, CDIOCPREVENT);
+ if (rval == 0)
+ rval = close(d->fd);
+ if (rval == 0)
+ d->fd = -1;
+ return rval;
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. The actual formula looks like
+ *
+ * (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ * max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ * return ((vol * (max_volume - min_volume)) / max + min_volume);
+ */
+static int
+scale_volume(int vol, int max)
+{
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+} /* scale_volume() */
+
+/*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume(int cd_vol, int max)
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ struct ioc_vol vol;
+
+ if (left < 0) /* don't laugh, I saw this happen once! */
+ left = 0;
+ if (right < 0)
+ right = 0;
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ bzero((char *)&vol, sizeof(vol));
+
+ vol.vol[LEFT_PORT] = left;
+ vol.vol[RIGHT_PORT] = right;
+
+ if (ioctl(d->fd, CDIOCSETVOL, &vol))
+ return (-1);
+
+ return (0);
+} /* gen_set_volume() */
+
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ struct ioc_vol vol;
+
+ if (d->fd >= 0)
+ {
+ bzero((char *)&vol, sizeof(vol));
+
+ if (ioctl(d->fd, CDIOCGETVOL, &vol))
+ *left = *right = -1;
+ else
+ {
+ *left = unscale_volume(vol.vol[LEFT_PORT], 100);
+ *right = unscale_volume(vol.vol[RIGHT_PORT], 100);
+ }
+ }
+ else
+ *left = *right = -1;
+
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+#endif
diff --git a/kscd/libwm/plat_osf1.c b/kscd/libwm/plat_osf1.c
new file mode 100644
index 00000000..a2cc0afb
--- /dev/null
+++ b/kscd/libwm/plat_osf1.c
@@ -0,0 +1,674 @@
+/*
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * OSF drive control routines.
+ */
+
+
+#if defined(__osf__) || defined(__osf)
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ustat.h>
+#include <string.h>
+/* #include <sys/rzdisk.h>
+#include <sys/cdrom.h> */
+#include <io/cam/rzdisk.h>
+#include <io/cam/cdrom.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdtext.h"
+
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * This structure will be filled with the TOC header and all entries.
+ * Ultrix doesn't seem to allow getting single TOC entries.
+ * - Chris Ross (cross@eng.umd.edu)
+ */
+struct cd_toc_header_and_entries
+{
+ struct cd_toc_header cdth;
+ struct cd_toc_entry cdte[CDROM_MAX_TRACK+1];
+};
+
+void *malloc();
+char *strchr();
+
+int min_volume = 128;
+int max_volume = 255;
+static char* osf_cd_device = NULL;
+
+/*
+ * fgetline()
+ *
+ * Simulate fgets, but joining continued lines in the output of uerf.
+ *
+ * platform specific internal function
+ *
+ */
+
+#define BUF_SIZE 85 /* Max length of a (real) line */
+
+char *
+fgetline( FILE *fp )
+{
+ static char *retval = NULL;
+ static char holdbuf[BUF_SIZE + 1];
+ char tmp[BUF_SIZE + 1];
+ char *stmp;
+
+ if (!retval)
+ {
+ retval = malloc(BUF_SIZE * 3); /* 3 lines can be joined */
+ if (!retval)
+ return(NULL);
+ else
+ *retval = '\0';
+ }
+
+ if (*holdbuf)
+ {
+ strcpy(retval, holdbuf);
+ retval[strlen(retval)-1] = '\0';
+ memset(holdbuf, 0, BUF_SIZE+1);
+ }
+
+ while (fgets(tmp, BUF_SIZE, fp))
+ {
+ stmp = tmp + strspn(tmp, " \t");
+ if (*stmp == '_') { /* Continuation line */
+ retval[strlen(retval)-1] = '\0'; /* Trim off C/R */
+ strcat(retval, stmp+1);
+ } else {
+ if (*retval)
+ {
+ strcpy(holdbuf, tmp);
+ holdbuf[strlen(holdbuf)-1] = -1;
+ return retval;
+ } else { /* First line read, keep reading */
+ strcat(retval, stmp);
+ retval[strlen(retval)-1] = '\0';
+ }
+ }
+ }
+ return NULL;
+} /* fgetline() */
+
+
+/*
+ * find_cdrom
+ *
+ * Determine the name of the CD-ROM device.
+ *
+ * Read through the boot records (via a call to uerf) and find the SCSI
+ * address of the CD-ROM.
+ */
+const char*
+find_cdrom()
+{
+ char *data;
+ FILE *uerf;
+ int fds[2];
+ int pid;
+ extern char *getenv();
+ const char *device = NULL;
+
+ pipe(fds);
+
+ device = getenv("CDROM");
+ /*
+ ** the path of the device has to start w/ /dev
+ ** otherwise we are vulnerable to race conditions
+ ** Thomas Biege <thomas@suse.de>
+ */
+ if ( device == NULL ||
+ strncmp("/dev/", device, 5) ||
+ strstr(device, "/../")
+ )
+ return NULL;
+
+ if ((pid = fork()) == 0)
+ {
+ close(fds[0]);
+ dup2(fds[1], 1);
+ execl("/etc/uerf", "uerf", "-R", "-r", "300", (void *)0);
+ execl("/usr/sbin/uerf", "uerf", "-R", "-r", "300", (void *)0);
+ return NULL; /* _exit(1); */
+ } else if (pid < 0) {
+ perror("fork");
+ return NULL;
+ }
+
+ close(fds[1]);
+ uerf = fdopen(fds[0], "r");
+
+ while (data = fgetline(uerf))
+ if (strstr(data, "RRD42"))
+ {
+ char *device_p;
+
+ osf_cd_device = (char *)malloc(sizeof("/dev/rrz##c"));
+ strcpy(osf_cd_device, "/dev/r");
+ device_p = strstr(data, "rz");
+ device_p[(int)(strchr(device_p, ' ') - device_p)] = '\0';
+ strcat(osf_cd_device, device_p);
+ strcat(osf_cd_device, "c");
+ device = osf_cd_device;
+ break;
+ }
+
+ fclose(uerf);
+
+ if (device == NULL)
+ {
+ fprintf(stderr,
+ "No cdrom (RRD42) is installed on this system\n");
+ return NULL;
+ }
+
+ kill(pid, 15);
+ (void)wait((int *)NULL);
+ return device;
+} /* find_cdrom() */
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ static int warned = 0;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = find_cdrom();
+
+ d->fd = open(d->cd_device, O_RDWR);
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ return -EACCES;
+ }
+ else if (errno != EINTR)
+ {
+ return (-6);
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ find_drive_struct("", "", "");
+ d->fd = fd;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Send an arbitrary SCSI command to a device.
+ */
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply)
+{
+ /* OSF1 doesn't have a SCSI passthrough interface, does it? */
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct cd_sub_channel sc;
+ struct cd_subc_channel_data scd;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ sc.sch_address_format = CDROM_MSF_FORMAT;
+ sc.sch_data_format = CDROM_CURRENT_POSITION;
+ sc.sch_track_number = 0;
+ sc.sch_alloc_length = sizeof(scd);
+ sc.sch_buffer = (caddr_t)&scd;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (ioctl(d->fd, CDROM_READ_SUBCHANNEL, &sc))
+ return (0); /* ejected */
+
+ switch (scd.scd_header.sh_audio_status)
+ {
+ case AS_PLAY_IN_PROGRESS:
+ *mode = WM_CDM_PLAYING;
+ dopos:
+ *pos = scd.scd_position_data.scp_absaddr.msf.m_units * 60 * 75 +
+ scd.scd_position_data.scp_absaddr.msf.s_units * 75 +
+ scd.scd_position_data.scp_absaddr.msf.f_units;
+ *track = scd.scd_position_data.scp_track_number;
+ *index = scd.scd_position_data.scp_index_number;
+ break;
+
+ case AS_PLAY_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ goto dopos;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case AS_PLAY_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case AS_NO_STATUS:
+ *mode = WM_CDM_STOPPED;
+ break;
+ default:
+ abort();
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+ struct cd_play_audio_msf msf;
+
+ msf.msf_starting_M_unit = start / (60*75);
+ msf.msf_starting_S_unit = (start % (60*75)) / 75;
+ msf.msf_starting_F_unit = start % 75;
+ msf.msf_ending_M_unit = end / (60*75);
+ msf.msf_ending_S_unit = (end % (60*75)) / 75;
+ msf.msf_ending_F_unit = end % 75;
+
+ if (ioctl(d->fd, SCSI_START_UNIT))
+ return (-1);
+ if (ioctl(d->fd, CDROM_PLAY_AUDIO_MSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDROM_PAUSE_PLAY, 0));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDROM_RESUME_PLAY, 0));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop( struct wm_drive *d )
+{
+ return (ioctl(d->fd, SCSI_STOP_UNIT, 0));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ /* On some systems, we can check to see if the CD is mounted. */
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ return (ioctl(d->fd, CDROM_EJECT_CADDY, 0));
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct cd_toc_header hdr;
+
+ if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+ return (-1);
+
+ *tracks = hdr.th_ending_track;
+
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct cd_toc toc;
+ struct cd_toc_header hdr;
+ struct cd_toc_header_and_entries toc_buffer;
+
+ if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+ return (-1);
+
+ bzero((char *)&toc_buffer, sizeof(toc_buffer));
+ toc.toc_address_format = CDROM_MSF_FORMAT;
+ toc.toc_starting_track = 0;
+ toc.toc_alloc_length = (u_short)(((hdr.th_data_len1 << 8) +
+ hdr.th_data_len0) & 0xfff) + 2;
+ toc.toc_buffer = (caddr_t)&toc_buffer;
+
+ if (ioctl(d->fd, CDROM_TOC_ENTRYS, &toc))
+ return (-1);
+
+ if (track == 0)
+ track = hdr.th_ending_track + 1;
+
+ *data = (toc_buffer.cdte[track-1].te_control & CDROM_DATA_TRACK) ? 1:0;
+ *startframe = toc_buffer.cdte[track - 1].te_absaddr.msf.m_units*60*75 +
+ toc_buffer.cdte[track - 1].te_absaddr.msf.s_units * 75 +
+ toc_buffer.cdte[track - 1].te_absaddr.msf.f_units;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen( struct wm_drive *d, int *frames )
+{
+ int tmp;
+
+ return (gen_get_trackinfo(d, 0, &tmp, frames));
+} /* gen_get_cdlen() */
+
+/*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. The actual formula looks like
+ *
+ * (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ * max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ * return ((vol * (max_volume - min_volume)) / max + min_volume);
+ */
+scale_volume(int vol, int max)
+{
+ return ((max * max - (max - vol) * (max - vol)) *
+ (max_volume - min_volume) / (max * max) + min_volume);
+} /* scale_volume() */
+
+/*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume( int cd_vol, int max )
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ struct cd_playback pb;
+ struct cd_playback_status ps;
+ struct cd_playback_control pc;
+
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ bzero((char *)&pb, sizeof(pb));
+ bzero((char *)&ps, sizeof(ps));
+ bzero((char *)&pc, sizeof(pc));
+
+ pb.pb_alloc_length = sizeof(ps);
+ pb.pb_buffer = (caddr_t)&ps;
+
+ if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+ return (-1);
+
+ pc.pc_chan0_select = ps.ps_chan0_select;
+ pc.pc_chan0_volume = (left < CDROM_MIN_VOLUME) ?
+ CDROM_MIN_VOLUME : (left > CDROM_MAX_VOLUME) ?
+ CDROM_MAX_VOLUME : left;
+ pc.pc_chan1_select = ps.ps_chan1_select;
+ pc.pc_chan1_volume = (right < CDROM_MIN_VOLUME) ?
+ CDROM_MIN_VOLUME : (right > CDROM_MAX_VOLUME) ?
+ CDROM_MAX_VOLUME : right;
+
+ pb.pb_alloc_length = sizeof(pc);
+ pb.pb_buffer = (caddr_t)&pc;
+
+ if (ioctl(d->fd, CDROM_PLAYBACK_CONTROL, &pb))
+ return (-1);
+
+ return (0);
+} /* gen_set_volume() */
+
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ struct cd_playback pb;
+ struct cd_playback_status ps;
+
+ bzero((char *)&pb, sizeof(pb));
+ bzero((char *)&ps, sizeof(ps));
+
+ pb.pb_alloc_length = sizeof(ps);
+ pb.pb_buffer = (caddr_t)&ps;
+
+ if (d->fd >= 0)
+ {
+ if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+ *left = *right = -1;
+ else
+ {
+ *left = unscale_volume(ps.ps_chan0_volume, 100);
+ *right = unscale_volume(ps.ps_chan1_volume, 100);
+ }
+ }
+ else
+ *left = *right = -1;
+
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif
diff --git a/kscd/libwm/plat_scor5.c b/kscd/libwm/plat_scor5.c
new file mode 100644
index 00000000..1e5be6e2
--- /dev/null
+++ b/kscd/libwm/plat_scor5.c
@@ -0,0 +1,426 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * SCO Openserver R5 specific. Derived from the KSCD plat_scor5.c
+ *
+ */
+
+
+static char plat_linux_id[] = "$Id$";
+
+#if defined(M_UNIX) || defined(__M_UNIX)
+
+
+#include "include/wm_config.h"
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/scsi.h>
+#include <sys/scsicmd.h>
+#include <errno.h>
+#include <macros.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define SENSE_SZ EXTD_SENSE_LEN
+
+void *malloc();
+char *strchr();
+
+int min_volume = 0;
+int max_volume = 255;
+
+/*
+ * platformspecific internal function
+ */
+static int
+create_cdrom_node(char *dev_name)
+{
+ char pass_through[100];
+ int file_des;
+ struct stat sbuf;
+ int err;
+ int ccode;
+
+
+ strncpy(pass_through, dev_name, sizeof(pass_through)-2);
+ strcat(pass_through, "p" );
+
+ if (setreuid(-1,0) < 0)
+ {
+ perror("setregid/setreuid/access");
+ return -1;
+ }
+
+ ccode = access(pass_through, F_OK);
+
+ if (ccode < 0)
+ {
+
+ if (stat(dev_name, &sbuf) < 0)
+ {
+ perror("Call to get pass-through device number failed");
+ return -1;
+ }
+
+ if (mknod(pass_through, (S_IFCHR | S_IREAD | S_IWRITE),
+ sbuf.st_rdev) < 0)
+ {
+ perror("Unable to make pass-through node");
+ return -1;
+ }
+
+ if (chown(pass_through, 0 , 0) < 0)
+ {
+ perror("chown");
+ return -1;
+ }
+
+ if (chmod(pass_through, 0660 ) < 0)
+ {
+ perror("chmod");
+ return -1;
+ }
+ }
+
+ file_des = open( pass_through, O_RDONLY);
+ err = errno;
+
+ /*
+ if ( (setreuid(-1,getuid()) < 0) || (setregid(-1,getgid()) < 0) )
+ {
+ perror("setreuid/setregid");
+ exit(1);
+ }
+
+ */
+
+ errno = err;
+ return file_des;
+} /* create_cdrom_node() */
+
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * Open the CD and figure out which kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d)
+{
+ int fd;
+ static int warned = 0;
+ char vendor[9], model[17], rev[5];
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ {
+ fprintf(stderr,"cd_device string empty\n");
+ return (-1);
+ }
+
+
+ d->fd = create_cdrom_node(d->cd_device); /* this will do open */
+
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ if (! warned)
+ {
+ fprintf(stderr,"Cannot access %s\n",d->cd_device);
+ warned++;
+ }
+ }
+ else if (errno != EINTR)
+ {
+ perror(d->cd_device);
+ return( -6 );
+ }
+
+ /* can not acces CDROM device */
+ return (-1);
+ }
+
+ if (warned)
+ {
+ warned = 0;
+ fprintf(stderr, "Thank you.\n");
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+
+ fd = d->fd;
+
+ if (wm_scsi_get_drive_type(d, vendor, model, rev) < 0)
+ {
+ perror("Cannot inquiry drive for it's type");
+ return (-1);
+ }
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+
+ return (0);
+} /* wmcd_open */
+
+/*
+ * Re-Open the device
+ */
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+ int tries = 0;
+ do {
+ gen_close(d);
+ susleep( 1000 );
+ status = wmcd_open( d );
+ susleep( 1000 );
+ tries++;
+ } while ( (status != 0) && (tries < 10) );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Send a SCSI command out the bus.
+ */
+int
+wm_scsi(struct wm_drive *d, unsigned char *xcdb, int cdblen,
+ char *retbuf, int retbuflen, int getreply)
+{
+ int ccode;
+ int file_des = d->fd;
+ unsigned char sense_buffer[ SENSE_SZ ];
+
+ /* getreply == 1 is read, == 0 is write */
+
+ struct scsicmd2 sb; /* Use command with automatic sense */
+
+ if (cdblen > SCSICMDLEN)
+ {
+ fprintf(stderr,"Cannot handle longer commands than %d bytes.\n", SCSICMDLEN);
+ exit(-1);
+ }
+
+ /* Get the command */
+ memcpy(sb.cmd.cdb, xcdb, cdblen);
+ sb.cmd.cdb_len = cdblen;
+
+ /* Point to data buffer */
+ sb.cmd.data_ptr = retbuf;
+ sb.cmd.data_len = retbuflen;
+
+ /* Is this write or read ? */
+ sb.cmd.is_write = (getreply==1) ? 0 : 1;
+
+ /* Zero out return status fields */
+ sb.cmd.host_sts = 0;
+ sb.cmd.target_sts = 0;
+
+ /* Set up for possible sense info */
+
+ sb.sense_ptr = sense_buffer;
+ sb.sense_len = sizeof(sense_buffer);
+
+ ccode = ioctl(file_des, SCSIUSERCMD2, &sb);
+
+ if ( sb.cmd.target_sts != 02 )
+ return ccode;
+
+ return 0;
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+void
+keep_cd_open() { }
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(wm_drive *d, int oldmode, int *mode,
+ int *pos, int *track, int *index)
+{
+ return (wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index));
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ return (wm_scsi2_get_trackcount(d, tracks));
+} /* gen_get_trackcount() */
+
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ return (wm_scsi2_get_trackinfo(d, track, data, startframe));
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ return (wm_scsi2_get_cdlen(d, frames));
+} /* gen_get_cdlen() */
+
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+ return (wm_scsi2_play(d, start, end));
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ return (wm_scsi2_pause(d));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ return (wm_scsi2_resume(d));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ return (wm_scsi2_stop(d));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ int stat;
+
+ stat = wm_scsi2_eject(d);
+ sleep(2);
+ return (stat);
+} /* gen_eject() */
+
+
+int
+gen_closetray(struct wm_drive *d)
+{
+ return(wm_scsi2_closetray(d));
+} /* gen_closetray() */
+
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ return (wm_scsi2_set_volume(d, left, right));
+} /* gen_set_volume() */
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ return (wm_scsi2_get_volume(d, left, right));
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ /* This needs to be tested! */
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+
+
+#endif /* M_UNIX */
+
+
+
+
diff --git a/kscd/libwm/plat_sun.c b/kscd/libwm/plat_sun.c
new file mode 100644
index 00000000..f83a6cd8
--- /dev/null
+++ b/kscd/libwm/plat_sun.c
@@ -0,0 +1,972 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sun-specific drive control routines.
+ */
+
+static char plat_sun_id[] = "$Id$";
+
+#if defined(sun) || defined(__sun)
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include "include/wm_config.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_cdtext.h"
+
+#include <ustat.h>
+#include <unistd.h>
+#include <signal.h>
+#ifdef solbourne
+# include <mfg/dklabel.h>
+# include <mfg/dkio.h>
+# include <sys/unistd.h>
+# include <dev/srvar.h>
+#else /* A real Sun */
+# ifdef SYSV
+# include <poll.h>
+# include <stdlib.h>
+# include <sys/cdio.h>
+# include <sys/socket.h>
+# include <sys/scsi/impl/uscsi.h>
+# include "include/wm_cdda.h"
+# else
+# include <sys/buf.h>
+# include <sun/dkio.h>
+# include <scsi/targets/srdef.h>
+# include <scsi/impl/uscsi.h>
+# include <scsi/generic/commands.h>
+# endif
+#endif
+
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+int min_volume = 0;
+int max_volume = 255;
+
+static const char *sun_cd_device = NULL;
+extern int intermittent_dev;
+
+int current_end;
+
+#if defined(SYSV) && defined(SIGTHAW)
+#ifdef __GNUC__
+void sigthawinit(void) __attribute__ ((constructor));
+#else
+#pragma init(sigthawinit)
+#endif /* GNUC */
+
+static int last_left, last_right;
+static struct wm_drive *thecd = NULL;
+
+/*
+ * Handling for Sun's Suspend functionality
+ */
+static void
+thawme(int sig)
+{
+// Just leave this line in as a reminder for a missing
+// functionality in the GUI.
+// change_mode(NULL, WM_CDM_STOPPED, NULL);
+ codec_init();
+ if( thecd )
+ gen_set_volume(thecd, last_left, last_right);
+} /* thawme() */
+
+void
+sigthawinit( void )
+{
+ struct sigaction sa;
+
+ sa.sa_handler = thawme;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ sigaction(SIGTHAW, &sa, NULL);
+} /* sigthawinit() */
+
+#endif /* SYSV && SIGTHAW */
+
+/*
+ * find_cdrom
+ *
+ * Determine the name of the CD-ROM device.
+ *
+ * Use the first of /vol/dev/aliases/cdrom0, /dev/rdsk/c0t6d0s2, and /dev/rsr0
+ * that exists. (Check for /vol/dev/aliases, not cdrom0, since it won't be
+ * there if there's no CD in the drive.) This is done so a single SunOS 4.x
+ * binary can be used on any 4.x or higher Sun system.
+ */
+const char*
+find_cdrom( void )
+{
+ if (access("/vol/dev/aliases", X_OK) == 0)
+ {
+ /* Volume manager. Device might not be there. */
+ intermittent_dev = 1;
+
+ /* If vold is running us, it'll tell us the device name. */
+ sun_cd_device = getenv("VOLUME_DEVICE");
+ /*
+ ** the path of the device has to include /dev
+ ** otherwise we are vulnerable to race conditions
+ ** Thomas Biege <thomas@suse.de>
+ */
+ if (sun_cd_device == NULL ||
+ strncmp("/vol/dev/", sun_cd_device, 9) ||
+ strstr(sun_cd_device, "/../") )
+ return "/vol/dev/aliases/cdrom0";
+ else
+ return sun_cd_device;
+ }
+ else if (access("/dev/rdsk/c0t6d0s2", F_OK) == 0)
+ {
+ /* Solaris 2.x w/o volume manager. */
+ return "/dev/rdsk/c0t6d0s2";
+ }
+ else if (access("/dev/rcd0", F_OK) == 0)
+ {
+ return "/dev/rcd0";
+ }
+ else if (access("/dev/rsr0", F_OK) == 0)
+ return "/dev/rsr0";
+ else
+ {
+ fprintf(stderr, "Couldn't find a CD device!\n");
+ return NULL;
+ }
+} /* find_cdrom() */
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ codec_init();
+ return (0);
+} /* gen_init() */
+
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d )
+{
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = find_cdrom();
+
+ d->fd = open(d->cd_device, 0);
+ if (d->fd < 0)
+ {
+ /* Solaris 2.2 volume manager moves links around */
+ if (errno == ENOENT && intermittent_dev)
+ return (1);
+
+ if (errno == EACCES)
+ {
+ if (!warned)
+ {
+ /*
+ char realname[MAXPATHLEN];
+
+ if (realpath(cd_device, realname) == NULL)
+ {
+ perror("realpath");
+ return (1);
+ }
+ */
+ return -EACCES;
+ }
+ }
+ else if (errno != ENXIO)
+ {
+ return( -6 );
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /*
+ * See if we can do digital audio.
+ */
+#if defined(BUILD_CDDA)
+ if(d->cdda) {
+ if (!gen_cdda_init(d))
+ /* WARNING: Old GUI call. How could this survive? */
+ enable_cdda_controls(1);
+ else {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): failed in gen_cdda_init\n");
+ gen_close(d);
+ return -1;
+ }
+#endif
+
+ /* Can we figure out the drive type? */
+ if (wm_scsi_get_drive_type(d, vendor, model, rev))
+ {
+ if (errno == EPERM)
+ {
+ /*
+ * Solaris 2.4 seems to refuse to do USCSICMD ioctls
+ * when not running as root. SunOS 4.x allows it
+ * as an unprivileged user, though.
+ */
+ fprintf(stderr, "Warning: WorkMan can't adapt itself to your drive unless it runs as root.\n");
+ } else {
+ fprintf(stderr, "Warning: WorkMan couldn't determine drive type\n");
+ }
+ strcpy(vendor, "Generic");
+ strcpy(model, "drive type");
+ strcpy(rev, "");
+ }
+
+ find_drive_struct(vendor, model, rev);
+
+ (d->proto->gen_init)(d);
+ thecd = d;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ gen_close(d);
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+
+#ifndef solbourne
+/*
+ * Send an arbitrary SCSI command out the bus and optionally wait for
+ * a reply if "retbuf" isn't NULL.
+ */
+int
+wm_scsi( struct wm_drive *d,
+ unsigned char *cdb,
+ int cdblen, void *retbuf,
+ int retbuflen, int getreply )
+{
+ char x;
+ struct uscsi_cmd cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.uscsi_cdb = (void *) cdb;
+ cmd.uscsi_cdblen = cdblen;
+ cmd.uscsi_bufaddr = retbuf ? retbuf : (void *)&x;
+ cmd.uscsi_buflen = retbuf ? retbuflen : 0;
+ cmd.uscsi_flags = USCSI_ISOLATE | USCSI_SILENT;
+ if (getreply)
+ cmd.uscsi_flags |= USCSI_READ;
+
+ if (ioctl(d->fd, USCSICMD, &cmd))
+ return (-1);
+
+ if (cmd.uscsi_status)
+ return (-1);
+
+ return (0);
+}
+#else
+int wm_scsi() { return (-1); }
+#endif
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/* Alarm signal handler. */
+static void do_nothing( int x ) { x++; }
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status( struct wm_drive *d,
+ int oldmode,
+ int *mode,
+ int *pos, int *track, int *index )
+{
+ struct cdrom_subchnl sc;
+ struct itimerval old_timer, new_timer;
+ struct sigaction old_sig, new_sig;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+#if defined(BUILD_CDDA)
+ if (oldmode == WM_CDM_PAUSED || oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_STOPPED) {
+ CDDARETURN(d) cdda_get_drive_status(d, oldmode, mode, pos, track, index);
+ }
+#endif
+
+ /*
+ * Solaris 2.2 hangs on this ioctl if someone else ejects the CD.
+ * So we schedule a signal to break out of the hang if the call
+ * takes an unreasonable amount of time. The signal handler and
+ * timer are restored immediately to avoid interfering with XView.
+ */
+ if (intermittent_dev)
+ {
+ /*
+ * First clear out the timer so XView's signal doesn't happen
+ * while we're diddling with the signal handler.
+ */
+ timerclear(&new_timer.it_interval);
+ timerclear(&new_timer.it_value);
+ setitimer(ITIMER_REAL, &new_timer, &old_timer);
+
+ /*
+ * Now install the no-op signal handler.
+ */
+ new_sig.sa_handler = do_nothing;
+ memset(&new_sig.sa_mask, 0, sizeof(new_sig.sa_mask));
+ new_sig.sa_flags = 0;
+ if (sigaction(SIGALRM, &new_sig, &old_sig))
+ perror("sigaction");
+
+ /*
+ * And finally, set the timer.
+ */
+ new_timer.it_value.tv_sec = 2;
+ setitimer(ITIMER_REAL, &new_timer, NULL);
+ }
+
+ sc.cdsc_format = CDROM_MSF;
+
+ if (ioctl(d->fd, CDROMSUBCHNL, &sc))
+ {
+ if (intermittent_dev)
+ {
+ sigaction(SIGALRM, &old_sig, NULL);
+ setitimer(ITIMER_REAL, &old_timer, NULL);
+
+ /* If the device can disappear, let it do so. */
+ close(d->fd);
+ d->fd = -1;
+ }
+
+ return (0);
+ }
+
+ if (intermittent_dev)
+ {
+ sigaction(SIGALRM, &old_sig, NULL);
+ setitimer(ITIMER_REAL, &old_timer, NULL);
+ }
+
+ switch (sc.cdsc_audiostatus) {
+ case CDROM_AUDIO_PLAY:
+ *mode = WM_CDM_PLAYING;
+ *track = sc.cdsc_trk;
+ *index = sc.cdsc_ind;
+ *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+ sc.cdsc_absaddr.msf.second * 75 +
+ sc.cdsc_absaddr.msf.frame;
+ break;
+
+ case CDROM_AUDIO_PAUSED:
+ case CDROM_AUDIO_INVALID:
+ case CDROM_AUDIO_NO_STATUS:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ *track = sc.cdsc_trk;
+ *index = sc.cdsc_ind;
+ *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+ sc.cdsc_absaddr.msf.second * 75 +
+ sc.cdsc_absaddr.msf.frame;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ /* CD ejected manually during play. */
+ case CDROM_AUDIO_ERROR:
+ break;
+
+ case CDROM_AUDIO_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ default:
+ *mode = WM_CDM_UNKNOWN;
+ break;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount( struct wm_drive *d, int *tracks )
+{
+ struct cdrom_tochdr hdr;
+
+ if (ioctl(d->fd, CDROMREADTOCHDR, &hdr))
+ return (-1);
+
+ *tracks = hdr.cdth_trk1;
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct cdrom_tocentry entry;
+
+ entry.cdte_track = track;
+ entry.cdte_format = CDROM_MSF;
+
+ if (ioctl(d->fd, CDROMREADTOCENTRY, &entry))
+ return (-1);
+
+ *startframe = entry.cdte_addr.msf.minute * 60 * 75 +
+ entry.cdte_addr.msf.second * 75 +
+ entry.cdte_addr.msf.frame;
+ *data = entry.cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames )
+{
+ int tmp;
+
+ return (gen_get_trackinfo(d, CDROM_LEADOUT, &tmp, frames));
+} /* gen_get_cdlen() */
+
+/*
+ * Play the CD from one position to another.
+ *
+ * d Drive structure.
+ * start Frame to start playing at.
+ * end End of this chunk.
+ * realstart Beginning of this chunk (<= start)
+ */
+int
+gen_play( struct wm_drive *d, int start, int end, int realstart)
+{
+ struct cdrom_msf msf;
+ unsigned char cmdbuf[10];
+
+ current_end = end;
+
+ CDDARETURN(d) cdda_play(d, start, end, realstart);
+
+ msf.cdmsf_min0 = start / (60*75);
+ msf.cdmsf_sec0 = (start % (60*75)) / 75;
+ msf.cdmsf_frame0 = start % 75;
+ msf.cdmsf_min1 = end / (60*75);
+ msf.cdmsf_sec1 = (end % (60*75)) / 75;
+ msf.cdmsf_frame1 = end % 75;
+
+ codec_start();
+ if (ioctl(d->fd, CDROMSTART))
+ return (-1);
+ if (ioctl(d->fd, CDROMPLAYMSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ CDDARETURN(d) cdda_pause(d);
+
+ codec_stop();
+ return (ioctl(d->fd, CDROMPAUSE));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ CDDARETURN(d) cdda_pause(d);
+
+ codec_start();
+ return (ioctl(d->fd, CDROMRESUME));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop( struct wm_drive *d )
+{
+ CDDARETURN(d) cdda_stop(d);
+
+ codec_stop();
+ return (ioctl(d->fd, CDROMSTOP));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject( struct wm_drive *d )
+{
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ IFCDDA(d) {
+ cdda_eject(d);
+ }
+
+ if (ioctl(d->fd, CDROMEJECT))
+ return (-1);
+
+ /* Close the device if it needs to vanish. */
+ if (intermittent_dev)
+ {
+ close(d->fd);
+ d->fd = -1;
+ /* Also remember to tell the cddaslave since volume
+ manager switches links around on us */
+ if (d->cdda_slave > -1)
+ {
+ write(d->cdda_slave, "E", 1);
+ cdda_get_ack(d->cdda_slave);
+ }
+ }
+
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ struct cdrom_volctrl v;
+
+#if defined(SIGTHAW) && defined(SYSV)
+ last_left = left;
+ last_right = right;
+ thecd = d;
+#endif
+
+ CDDARETURN(d) cdda_set_volume(d, left, right);
+
+ left = (left * (max_volume - min_volume)) / 100 + min_volume;
+ right = (right * (max_volume - min_volume)) / 100 + min_volume;
+
+ v.channel0 = left < 0 ? 0 : left > 255 ? 255 : left;
+ v.channel1 = right < 0 ? 0 : right > 255 ? 255 : right;
+
+ return (ioctl(d->fd, CDROMVOLCTRL, &v));
+} /* gen_set_volume() */
+
+/*
+ * Read the volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ CDDARETURN(d) cdda_get_volume(d, left, right);
+
+ *left = *right = -1;
+
+ return (wm_scsi2_get_volume(d, left, right));
+} /* gen_get_volume() */
+
+#ifdef BUILD_CDDA
+
+/*
+ * Try to initialize the CDDA slave. Returns 0 on success.
+ */
+int
+gen_cdda_init( struct wm_drive *d )
+{
+ int slavefds[2];
+
+ if (d->cdda_slave > -1)
+ return (0);
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, slavefds))
+ {
+ perror("socketpair");
+ return (-1);
+ }
+
+ switch (fork())
+ {
+ case 0:
+ close(slavefds[0]);
+ dup2(slavefds[1], 1);
+ dup2(slavefds[1], 0);
+ close(slavefds[1]);
+ close(d->fd);
+ /* Try the default path first. */
+ execl(cddaslave_path, cddaslave_path, d->cd_device, (void *)0);
+ /* Search $PATH if that didn't work. */
+ execlp("cddaslave", "cddaslave", d->cd_device, (void *)0);
+ perror(cddaslave_path);
+ exit(1);
+
+ case -1:
+ close(slavefds[0]);
+ close(slavefds[1]);
+ perror("fork");
+ return (-2);
+ }
+
+ close(slavefds[1]);
+ d->cdda_slave = slavefds[0];
+
+ if (!cdda_get_ack(d->cdda_slave))
+ {
+ d->cdda_slave = -1;
+ codec_start();
+ return (-3);
+ }
+
+ return (0);
+}
+
+#endif /* BUILD_CDDA */
+
+/*
+ * The following code activates the internal CD audio passthrough on
+ * SPARCstation 5 systems (and possibly others.)
+ *
+ * Thanks to <stevep@ctc.ih.att.com>, Roger Oscarsson <roger@cs.umu.se>
+ * and Steve McKinty <>
+ *
+ * Most CD drives have a headphone socket on the front, but it
+ * is often more convenient to route the audio though the
+ * built-in audio device. That way the user can leave their
+ * headphones plugged-in to the base system, for use with
+ * other audio stuff like ShowMeTV
+ */
+
+#ifdef CODEC /* { */
+#ifdef SYSV /* { */
+
+# include <sys/ioctl.h>
+# include <sys/audioio.h>
+# include <stdlib.h>
+
+#else /* } { */
+
+# include <sun/audioio.h>
+# define AUDIO_DEV_SS5STYLE 5
+typedef int audio_device_t;
+
+#endif /* } */
+#endif /* } */
+
+/*
+ * Don't do anything with /dev/audio if we can't set it to high quality.
+ * Also, don't do anything real if it's not Solaris.
+ */
+#if !defined(AUDIO_ENCODING_LINEAR) || !defined(CODEC) || !defined(SYSV) /* { */
+codec_init() { return 0; }
+codec_start() { return 0; }
+codec_stop() { return 0; }
+#else
+
+#ifndef AUDIO_INTERNAL_CD_IN
+#define AUDIO_INTERNAL_CD_IN 0x4
+#endif
+
+static char* devname = 0;
+static char* ctlname = 0;
+static int ctl_fd = -1;
+static int port = AUDIO_LINE_IN;
+int internal_audio = 1;
+
+codec_init( void )
+{
+ register int i;
+ char* ctlname;
+ audio_info_t foo;
+ audio_device_t aud_dev;
+
+ if (internal_audio == 0)
+ {
+ ctl_fd = -1;
+ return(0);
+ }
+
+ if (!(devname = getenv("AUDIODEV"))) devname = "/dev/audio";
+ ctlname = strcat(strcpy(malloc(strlen(devname) + 4), devname), "ctl");
+ if ((ctl_fd = open(ctlname, O_WRONLY, 0)) < 0)
+ {
+ perror(ctlname);
+ return -1;
+ }
+ if (ioctl(ctl_fd, AUDIO_GETDEV, &aud_dev) < 0)
+ {
+ close(ctl_fd);
+ ctl_fd = -1;
+ return -1;
+ }
+ /*
+ * Instead of filtering the "OLD_SUN_AUDIO", try to find the new ones.
+ * Not sure if this is all correct.
+ */
+#ifdef SYSV
+ if (strcmp(aud_dev.name, "SUNW,CS4231") &&
+ strcmp(aud_dev.name, "SUNW,sb16") &&
+ strcmp(aud_dev.name, "SUNW,sbpro"))
+#else
+ if (aud_dev != AUDIO_DEV_SS5STYLE)
+#endif
+ {
+ close(ctl_fd);
+ ctl_fd = -1;
+ return 0; /* but it's okay */
+ }
+
+ /*
+ * Does the chosen device have an internal CD port?
+ * If so, use it. If not then try and use the
+ * Line In port.
+ */
+ if (ioctl(ctl_fd, AUDIO_GETINFO, &foo) < 0)
+ {
+ perror("AUDIO_GETINFO");
+ close(ctl_fd);
+ ctl_fd = -1;
+ return(-1);
+ }
+ if (foo.record.avail_ports & AUDIO_INTERNAL_CD_IN)
+ port = AUDIO_INTERNAL_CD_IN;
+ else
+ port = AUDIO_LINE_IN;
+
+ /*
+ * now set it up to use it. See audio(7I)
+ */
+
+ AUDIO_INITINFO(&foo);
+ foo.record.port = port;
+ foo.record.balance = foo.play.balance = AUDIO_MID_BALANCE;
+#ifdef BUILD_CDDA
+ if (d->cdda_slave > -1)
+ foo.monitor_gain = 0;
+ else
+#endif
+ foo.monitor_gain = AUDIO_MAX_GAIN;
+ /*
+ * These next ones are tricky. The voulme will depend on the CD drive
+ * volume (set by the knob on the drive and/or by workman's volume
+ * control), the audio device record gain and the audio device
+ * play gain. For simplicity we set the latter two to something
+ * reasonable, but we don't force them to be reset if the user
+ * wants to change them.
+ */
+ foo.record.gain = (AUDIO_MAX_GAIN * 80) / 100;
+ foo.play.gain = (AUDIO_MAX_GAIN * 40) / 100;
+
+ ioctl(ctl_fd, AUDIO_SETINFO, &foo);
+ return 0;
+}
+
+static int
+kick_codec( void )
+{
+ audio_info_t foo;
+ int dev_fd;
+ int retval = 0;
+
+ /*
+ * Open the audio device, not the control device. This
+ * will fail if someone else has taken it.
+ */
+
+ if ((dev_fd = open(devname, O_WRONLY|O_NDELAY, 0)) < 0)
+ {
+ perror(devname);
+ return -1;
+ }
+
+ AUDIO_INITINFO(&foo);
+ foo.record.port = port;
+ foo.monitor_gain = AUDIO_MAX_GAIN;
+
+ /* These can only be set on the real device */
+ foo.play.sample_rate = 44100;
+ foo.play.channels = 2;
+ foo.play.precision = 16;
+ foo.play.encoding = AUDIO_ENCODING_LINEAR;
+
+ if ((retval = ioctl(dev_fd, AUDIO_SETINFO, &foo)) < 0)
+ perror(devname);
+
+ close(dev_fd);
+ return retval;
+} /* kick_codec() */
+
+codec_start( void )
+{
+ audio_info_t foo;
+
+ if (ctl_fd < 0)
+ return 0;
+
+ if (ioctl(ctl_fd, AUDIO_GETINFO, &foo) < 0)
+ return -1;
+
+ if (foo.play.channels != 2) return kick_codec();
+ if (foo.play.encoding != AUDIO_ENCODING_LINEAR) return kick_codec();
+ if (foo.play.precision != 16) return kick_codec();
+ if (foo.play.sample_rate != 44100) return kick_codec();
+
+ if (foo.record.channels != 2) return kick_codec();
+ if (foo.record.encoding != AUDIO_ENCODING_LINEAR) return kick_codec();
+ if (foo.record.precision != 16) return kick_codec();
+ if (foo.record.sample_rate != 44100) return kick_codec();
+
+ if (foo.monitor_gain != AUDIO_MAX_GAIN) return kick_codec();
+ if (foo.record.port != port) return kick_codec();
+
+ return 0;
+} /* codec_start() */
+
+codec_stop( void ) { return 0; }
+
+#endif /* CODEC } */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ /* This needs to be tested */
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+#endif /* sun */
diff --git a/kscd/libwm/plat_sun_audio.c b/kscd/libwm/plat_sun_audio.c
new file mode 100644
index 00000000..da9fe12f
--- /dev/null
+++ b/kscd/libwm/plat_sun_audio.c
@@ -0,0 +1,493 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sun (really Solaris) digital audio functions.
+ */
+
+#include "include/wm_config.h"
+
+static char plat_sun_audio_id[] = "$Id$";
+
+#if defined(sun) || defined(__sun) && defined(SYSV) && defined(BUILD_CDDA) && defined(WMCDDA_DONE)
+
+
+#include "include/wm_cdda.h"
+
+/* types.h included by wmcdda.h */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#include <sys/stropts.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * Since there's a lag time between writing audio to the audio device and
+ * hearing it, we need to make sure the status indicators correlate to what's
+ * playing out the speaker. Luckily, Solaris gives us some audio
+ * synchronization facilities that make this pretty easy.
+ *
+ * We maintain a circular queue of status information. When we write some
+ * sound to the audio device, we put its status info into the queue. We write
+ * a marker into the audio stream; when the audio device driver encounters the
+ * marker, it increments a field in a status structure. When we see that
+ * field go up, we grab the next status structure from the queue and send it
+ * to the parent process.
+ *
+ * The minimum size of the queue depends on the latency of the audio stream.
+ */
+#define QSIZE 500
+
+struct cdda_block queue[QSIZE];
+int qtail;
+int qstart;
+
+/*
+ * We only send WMCDDA_PLAYED status messages upstream when the CD is supposed
+ * to be playing; this is used to keep track.
+ */
+extern int playing;
+
+static int aufd, aucfd;
+static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */
+
+/*
+ * For fast linear-to-ulaw mapping, we use a lookup table that's generated
+ * at startup.
+ */
+unsigned char *ulawmap, linear_to_ulaw();
+
+char *getenv();
+
+/*
+ * Dummy signal handler so writes to /dev/audio will interrupt.
+ */
+static void
+dummy( void )
+{
+ signal(SIGALRM, dummy);
+}
+
+/*
+ * Initialize the audio device.
+ */
+void
+wmaudio_init( void )
+{
+ audio_info_t info;
+ char *audiodev, *acdev;
+ int linval;
+
+ audiodev = getenv("AUDIODEV");
+ if (audiodev == NULL ||
+ strncmp("/dev/", audiodev, 5) ||
+ strstr(audiodev, "/../") )
+ audiodev = "/dev/audio";
+
+ acdev = malloc(strlen(audiodev) + 4);
+ if (acdev == NULL)
+ {
+ perror("Can't allocate audio control filename");
+ exit(1);
+ }
+ strcpy(acdev, audiodev);
+ strcat(acdev, "ctl");
+
+ aucfd = open(acdev, O_WRONLY, 0);
+ if (aucfd < 0)
+ {
+ perror(acdev);
+ exit(1);
+ }
+ free(acdev);
+
+ aufd = open(audiodev, O_WRONLY, 0);
+ if (aufd < 0)
+ {
+ perror(audiodev);
+ exit(1);
+ }
+
+ signal(SIGALRM, dummy);
+
+ /*
+ * Try to set the device to CD-style audio; we can process it
+ * with the least CPU overhead.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 44100;
+ info.play.channels = 2;
+ info.play.precision = 16;
+ info.play.encoding = AUDIO_ENCODING_LINEAR;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ if (errno == EINVAL)
+ {
+ /*
+ * Oh well, so much for that idea.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 8000;
+ info.play.channels = 1;
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_ULAW;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ {
+ perror("Can't set up audio device");
+ exit(1);
+ }
+
+ /*
+ * Initialize the linear-to-ulaw mapping table.
+ */
+ if (ulawmap == NULL)
+ ulawmap = malloc(65536);
+ if (ulawmap == NULL)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ for (linval = 0; linval < 65536; linval++)
+ ulawmap[linval] = linear_to_ulaw(linval-32768);
+ ulawmap += 32768;
+ raw_audio = 0;
+ }
+ else
+ {
+ perror(audiodev);
+ exit(1);
+ }
+}
+
+/*
+ * Get ready to play some sound.
+ */
+void
+wmaudio_ready( void )
+{
+ audio_info_t info;
+
+ /*
+ * Start at the correct queue position.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ qtail = info.play.eof % QSIZE;
+ qstart = qtail;
+
+ queue[qtail].status = WMCDDA_OK;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+void
+wmaudio_stop( void )
+{
+ if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
+ perror("flush");
+}
+
+/*
+ * Close the audio device.
+ */
+void
+wmaudio_close( void )
+{
+ wmaudio_stop();
+ close(aufd);
+ close(aucfd);
+}
+
+/*
+ * Set the volume level.
+ */
+void
+wmaudio_volume(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ info.play.gain = level;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Set the balance level.
+ */
+void
+wmaudio_balance(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ level *= AUDIO_RIGHT_BALANCE;
+ info.play.balance = level / 255;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Mark the most recent audio block on the queue as the last one.
+ */
+void
+wmaudio_mark_last( void )
+{
+ queue[qtail].status = WMCDDA_DONE;
+}
+
+/*
+ * Figure out the most recent status information and send it upstream.
+ */
+int
+wmaudio_send_status( void )
+{
+ audio_info_t info;
+ int qhead;
+
+ /*
+ * Now send the most current status information to our parent.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ qhead = info.play.eof % QSIZE;
+
+ if (qhead != qstart && playing)
+ {
+ int balance;
+
+ if (queue[qhead].status != WMCDDA_DONE)
+ queue[qhead].status = WMCDDA_PLAYED;
+ queue[qhead].volume = info.play.gain;
+ queue[qhead].balance = (info.play.balance * 255) /
+ AUDIO_RIGHT_BALANCE;
+
+ send_status(queue + qhead);
+ qstart = -1;
+ }
+
+ return (queue[qhead].status == WMCDDA_DONE);
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+wmaudio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ int i;
+ short *buf16;
+ int alarmcount = 0;
+ struct itimerval it;
+
+ alarm(1);
+
+ while (write(aufd, rawbuf, buflen) <= 0)
+ if (errno == EINTR)
+ {
+ if (! raw_audio && alarmcount++ < 5)
+ {
+ /*
+ * 8KHz /dev/audio blocks for several seconds
+ * waiting for its queue to drop below a low
+ * water mark.
+ */
+ wmaudio_send_status();
+ timerclear(&it.it_interval);
+ timerclear(&it.it_value);
+ it.it_value.tv_usec = 500000;
+ setitimer(ITIMER_REAL, &it, NULL);
+ continue;
+ }
+
+/* close(aufd);
+ close(aucfd);
+ wmaudio_init();
+*/ wmaudio_stop();
+ alarm(2);
+ continue;
+ }
+ else
+ {
+ blk->status = WMCDDA_ERROR;
+ return (-1);
+ }
+ alarm(0);
+
+ /*
+ * Mark this spot in the audio stream.
+ *
+ * Marks don't always succeed (if the audio buffer is empty
+ * this call will block forever) so do it asynchronously.
+ */
+ fcntl(aufd, F_SETFL, O_NONBLOCK);
+ if (write(aufd, rawbuf, 0) < 0)
+ {
+ if (errno != EAGAIN)
+ perror("audio mark");
+ }
+ else
+ qtail = (qtail + 1) % QSIZE;
+
+ fcntl(aufd, F_SETFL, 0);
+
+ queue[qtail] = *blk;
+
+ if (wmaudio_send_status() < 0)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Get the current audio state.
+ */
+void
+wmaudio_state(struct cdda_block *blk)
+{
+ audio_info_t info;
+ int balance;
+
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ blk->volume = info.play.gain;
+ blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
+}
+
+/*
+** This routine converts from linear to ulaw.
+**
+** Craig Reese: IDA/Supercomputing Research Center
+** Joe Campbell: Department of Defense
+** 29 September 1989
+**
+** References:
+** 1) CCITT Recommendation G.711 (very difficult to follow)
+** 2) "A New Digital Technique for Implementation of Any
+** Continuous PCM Companding Law," Villeret, Michel,
+** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
+** 1973, pg. 11.12-11.17
+** 3) MIL-STD-188-113,"Interoperability and Performance Standards
+** for Analog-to_Digital Conversion Techniques,"
+** 17 February 1987
+**
+** Input: Signed 16 bit linear sample
+** Output: 8 bit ulaw sample
+*/
+#define ZEROTRAP /* turn on the trap as per the MIL-STD */
+#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+unsigned char
+linear_to_ulaw( sample )
+int sample;
+{
+ static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if ( sign != 0 ) sample = -sample; /* get magnitude */
+ if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[( sample >> 7 ) & 0xFF];
+ mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
+ ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
+#ifdef ZEROTRAP
+ if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+
+ return ulawbyte;
+}
+
+/*
+ * Downsample a block of CDDA data, if necessary, for playing out an old-style
+ * audio device.
+ */
+long
+wmaudio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ short *buf16 = (short *)rawbuf;
+ int i, j, samples;
+ int mono_value;
+ unsigned char *rbend = rawbuf + buflen;
+
+ /* Don't do anything if the audio device can take the raw values. */
+ if (raw_audio)
+ return (buflen);
+
+ for (i = 0; buf16 < (short *)(rbend); i++)
+ {
+ /* Downsampling to 8KHz is a little irregular. */
+ samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
+
+ /* And unfortunately, we don't always end on a nice boundary. */
+ if (buf16 + samples > (short *)(rbend))
+ samples = ((short *)rbend) - buf16;
+
+ /*
+ * No need to average all the values; taking the first one
+ * is sufficient and less CPU-intensive. But we do need to
+ * do both channels.
+ */
+ mono_value = (buf16[0] + buf16[1]) / 2;
+ buf16 += samples;
+ rawbuf[i] = ulawmap[mono_value];
+ }
+
+ return (i);
+}
+
+#endif /* ... && BUILD_CDDA */
diff --git a/kscd/libwm/plat_sun_cdda.c b/kscd/libwm/plat_sun_cdda.c
new file mode 100644
index 00000000..3f669a8b
--- /dev/null
+++ b/kscd/libwm/plat_sun_cdda.c
@@ -0,0 +1,380 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sun (really Solaris) CDDA functions.
+ */
+
+#include "include/wm_cdda.h"
+
+#if defined(sun) || defined(__sun__) && defined(SYSV) && defined(BUILD_CDDA)
+
+
+#include "include/wm_struct.h"
+#include "include/wm_cdda.h"
+/* types.h and cdio.h are included by wm_cdda.h */
+
+#include <stdio.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <malloc.h>
+#include <errno.h>
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define CDDABLKSIZE 2368
+#define SAMPLES_PER_BLK 588
+
+/* Address of next block to read. */
+int current_position = 0;
+
+/* Address of first and last blocks to read. */
+int starting_position = 0;
+int ending_position = 0;
+
+/* Playback direction. */
+int direction = 1;
+
+/* Number of blocks to read at once; initialize to the maximum. */
+/* (was 30. Set to 15 for INTeL. Maybe config option? */
+int numblocks = 15;
+
+/*
+ * This is the fastest way to convert from BCD to 8-bit.
+ */
+unsigned char unbcd[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,0,0,0,0,0,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0,0,0,0,0,0,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0,0,0,0,0,0,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 0,0,0,0,0,0,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0,0,0,0,0,0,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 0,0,0,0,0,0,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0,0,0,0,0,0,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 0,0,0,0,0,0,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 0,0,0,0,0,0,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+
+static long wmcdda_normalize(struct cdda_block *block);
+
+/*
+ * Initialize the CDDA data buffer and open the appropriate device.
+ *
+ * NOTE: We allocate twice as much space as we need to actually read a block;
+ * this lets us do audio manipulations without bothering to malloc a second
+ * buffer.
+ *
+ * Also, test to see if we can actually *do* CDDA on this drive; if not, we
+ * need to exit right away so the UI doesn't show the user any CDDA controls.
+ */
+int
+wmcdda_init(struct cdda_device* pdev, struct cdda_block *block)
+{
+ struct cdrom_cdda cdda;
+ int i;
+
+ if (pdev->fd > -1)
+ return -1;
+
+ for (i = 0; i < pdev->numblocks; i++) {
+ /* in Linux const */
+ pdev->blocks[i].buflen = pdev->frames_at_once * CDDABLKSIZE;
+ pdev->blocks[i].buf = malloc(pdev->blocks[i].buflen);
+ if (!pdev->blocks[i].buf)
+ return -ENOMEM;
+ }
+
+ pdev->fd = open(pdev->devname, 0);
+ if (pdev->fd == -1)
+ pdev->fd = open("/dev/rdsk/c0t6d0s2", 0);
+
+ if (pdev->fd > -1)
+ {
+ cdda.cdda_addr = 200;
+ cdda.cdda_length = 1;
+ cdda.cdda_data = pdev->blocks[0].buf;
+ cdda.cdda_subcode = CDROM_DA_SUBQ;
+
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ block->status = WM_CDM_STOPPED;
+ return -1;
+ } else {
+ block->status = WM_CDM_STOPPED;
+ return 0;
+ }
+ } else {
+ block->status = WM_CDM_EJECTED;
+ return -1;
+ }
+}
+
+/*
+ * Close the CD-ROM device in preparation for exiting.
+ */
+int
+wmcdda_close(struct cdda_device* pdev)
+{
+ int i;
+
+ if(-1 == pdev->fd)
+ return -1;
+
+ close(pdev->fd);
+ pdev->fd = -1;
+
+ for (i = 0; i < pdev->numblocks; i++) {
+ free(pdev->blocks[i].buf);
+ pdev->blocks[i].buf = 0;
+ pdev->blocks[i].buflen = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Set up for playing the CD. Actually this doesn't play a thing, just sets a
+ * couple variables so we'll know what to do when we're called.
+ */
+int
+wmcdda_setup(int start, int end, int realstart)
+{
+ current_position = start - 150;
+ ending_position = end - 150;
+ starting_position = realstart - 150;
+
+ /*
+ * Special case: don't start at the "end" of a track if we're
+ * playing backwards!
+ */
+ if (direction == -1 && start == realstart)
+ current_position = ending_position - numblocks;
+ return 0;
+}
+
+/*
+ * Read some blocks from the CD. Stop if we hit the end of the current region.
+ *
+ * Returns number of bytes read, -1 on error, 0 if stopped for a benign reason.
+ */
+long
+wmcdda_read(struct cdda_device* pdev, struct cdda_block *block)
+{
+ struct cdrom_cdda cdda;
+ int blk;
+ unsigned char *q;
+ extern int speed;
+ unsigned char* rawbuf = block->buf;
+
+ if(pdev->fd < 0 && (wmcdda_init(pdev, block) < 0)) {
+ return -1;
+ }
+
+ /*
+ * Hit the end of the CD, probably.
+ */
+ if ((direction > 0 && current_position >= ending_position) ||
+ (direction < 0 && current_position < starting_position))
+ {
+ block->status = WM_CDM_TRACK_DONE;
+ return (0);
+ }
+
+ cdda.cdda_addr = current_position;
+ if (ending_position && current_position + pdev->frames_at_once > ending_position)
+ cdda.cdda_length = ending_position - current_position;
+ else
+ cdda.cdda_length = pdev->frames_at_once;
+ cdda.cdda_data = (unsigned char*)block->buf;
+ cdda.cdda_subcode = CDROM_DA_SUBQ;
+
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ if (errno == ENXIO) /* CD ejected! */
+ {
+ block->status = WM_CDM_EJECTED;
+ return (-1);
+ }
+
+ /* Sometimes it fails once, dunno why */
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ perror("CDROMCDDA");
+ block->status = WM_CDM_CDDAERROR;
+ return (-1);
+ }
+ }
+ }
+ }
+
+ if (speed > 148)
+ {
+ /*
+ * We want speed=148 to advance by cdda_length, but
+ * speed=256 to advance cdda_length * 4.
+ */
+ current_position = current_position +
+ (cdda.cdda_length * direction * (speed - 112)) / 36;
+ }
+ else
+ current_position = current_position + cdda.cdda_length * direction;
+
+ for (blk = 0; blk < numblocks; blk++)
+ {
+ /*
+ * New valid Q-subchannel information? Update the block
+ * status.
+ */
+ q = &rawbuf[blk * CDDABLKSIZE + SAMPLES_PER_BLK * 4];
+ if (*q == 1)
+ {
+ block->track = unbcd[q[1]];
+ block->index = unbcd[q[2]];
+ /*block->minute = unbcd[q[7]];
+ block->second = unbcd[q[8]];*/
+ block->frame = unbcd[q[9]];
+ block->status = WM_CDM_PLAYING;
+ block->buflen = cdda.cdda_length;
+ }
+ }
+
+ return wmcdda_normalize(block);
+}
+
+/*
+ * Normalize a bunch of CDDA data. Basically this means ripping out the
+ * Q subchannel data and doing byte-swapping, since the CD audio is in
+ * littleendian format.
+ *
+ * Scanning is handled here too.
+ *
+ * XXX - do byte swapping on Intel boxes?
+ */
+long
+wmcdda_normalize(struct cdda_block *block)
+{
+ int i, nextq;
+ long buflen = block->buflen;
+ int blocks = buflen / CDDABLKSIZE;
+ unsigned char *rawbuf = block->buf;
+ unsigned char *dest = rawbuf;
+ unsigned char tmp;
+ long *buf32 = (long *)rawbuf, tmp32;
+
+/*
+ * this was #ifndef LITTLEENDIAN
+ * in wmcdda it was called LITTLE_ENDIAN. Was this a flaw?
+ */
+#if WM_BIG_ENDIAN
+ if (blocks--)
+ for (i = 0; i < SAMPLES_PER_BLK * 2; i++)
+ {
+ /* Only need to use temp buffer on first block. */
+ tmp = *rawbuf++;
+ *dest++ = *rawbuf++;
+ *dest++ = tmp;
+ }
+#endif
+
+ while (blocks--)
+ {
+ /* Skip over Q data. */
+ rawbuf += 16;
+
+ for (i = 0; i < SAMPLES_PER_BLK * 2; i++)
+ {
+#if WM_LITTLE_ENDIAN
+ *dest++ = *rawbuf++;
+ *dest++ = *rawbuf++;
+#else
+ *dest++ = rawbuf[1];
+ *dest++ = rawbuf[0];
+ rawbuf += 2;
+#endif
+ }
+ }
+
+ buflen -= ((buflen / CDDABLKSIZE) * 16);
+
+ /*
+ * Reverse the data here if we're playing backwards.
+ * XXX - ideally this should be done above.
+ */
+ if (direction < 0)
+ {
+ buflen /= 4; /* we can move 32 bits at a time. */
+
+ for (i = 0; i < buflen / 2; i++)
+ {
+ tmp32 = buf32[i];
+ buf32[i] = buf32[buflen - i - 1];
+ buf32[buflen - i - 1] = tmp32;
+ }
+
+ buflen *= 4;
+ }
+
+ return (buflen);
+}
+
+/*
+ * Set the playback direction.
+ */
+void
+wmcdda_direction(int newdir)
+{
+ if (newdir == 0)
+ {
+ numblocks = 20;
+ direction = 1;
+ }
+ else
+ {
+ numblocks = 30;
+ direction = -1;
+ }
+}
+
+/*
+ * Do system-specific stuff to get ready to play at a particular speed.
+ */
+void
+wmcdda_speed(int speed)
+{
+ if (speed > 128)
+ numblocks = 12;
+ else
+ numblocks = direction > 0 ? 20 : 30;
+}
+
+#endif /* } */
diff --git a/kscd/libwm/plat_svr4.c b/kscd/libwm/plat_svr4.c
new file mode 100644
index 00000000..ad6e488c
--- /dev/null
+++ b/kscd/libwm/plat_svr4.c
@@ -0,0 +1,482 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * SVR4 specific. Much of this is similar to plat_hpux.c.
+ */
+
+static char plat_svr4_id[] = "$Id$";
+
+#if (defined(SVR4) || defined(__SVR4)) && !defined(sun) && !defined(__sun) && !defined(sony_news) && !defined(__sony_news)
+
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/mkdev.h>
+#include <sys/stat.h>
+#include <sys/sdi.h>
+#include <sys/sdi_edt.h>
+#include <sys/scsi.h>
+#include <errno.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+
+void *malloc();
+char *strchr();
+
+int min_volume = 0;
+int max_volume = 255;
+
+static int
+create_cdrom_node(char *dev_name)
+{
+ char pass_through[100];
+ int file_des;
+ dev_t pass_thru_device;
+ int err;
+ int ccode;
+
+
+ strncpy(pass_through, dev_name, sizeof(pass_through) - 2);
+ strcat(pass_through, "p" );
+
+ if (setreuid(-1,0) < 0)
+ {
+ perror("setregid/setreuid/access");
+ return -1;
+ }
+
+ ccode = access(pass_through, F_OK);
+
+ if (ccode < 0)
+ {
+ if ((file_des = open(dev_name, O_RDONLY)) < 0)
+ {
+ perror("open cdrom devices failed");
+ return -1;
+ }
+
+ if (ioctl(file_des, B_GETDEV, &pass_thru_device) < 0)
+ {
+ perror("Call to get pass-through device number failed");
+ return -1;
+ }
+
+ (void)close(file_des);
+
+ if (mknod(pass_through, (S_IFCHR | S_IREAD | S_IWRITE),
+ pass_thru_device) < 0)
+ {
+ perror("Unable to make pass-through node");
+ return -1;
+ }
+
+ if (chown(pass_through, 0 , 0) < 0)
+ {
+ perror("chown");
+ return -1;
+ }
+
+ if (chmod(pass_through, 0660 ) < 0)
+ {
+ perror("chmod");
+ return -1;
+ }
+ }
+
+ file_des = open( pass_through, O_RDWR);
+ err = errno;
+
+ if ( (setreuid(-1,getuid()) < 0) || (setregid(-1,getgid()) < 0) )
+ {
+ perror("setreuid/setregid");
+ return -1;
+ }
+ errno = err;
+ return file_des;
+} /* create_cdrom_node() */
+
+const char*
+find_cdrom()
+{
+ /*
+ ** the path of the device has to start w/ /dev
+ ** otherwise we are vulnerable to race conditions
+ ** Thomas Biege <thomas@suse.de>
+ */
+ const char* device = NULL;
+
+ device = getenv("CDROM");
+ if ( (device != NULL) &&
+ !(strncmp("/dev/", device, 5) ||
+ strstr(_device, "/../") ))
+ return device;
+
+ if (access("/dev/cdrom/cdrom1", F_OK) == 0)
+ {
+ return "/dev/cdrom/cdrom1";
+ }
+ else if (access("/dev/cdrom/cdrom2", F_OK) == 0)
+ {
+ return "/dev/cdrom/cdrom2";
+ }
+ else
+ {
+ fprintf(stderr, "Couldn't find a CD device!\n");
+ return NULL;
+ }
+} /* find_cdrom() */
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * Open the CD and figure out which kind of drive is attached.
+ */
+int
+wmcd_open(struct wm_drive *d)
+{
+ int fd, flag = 1;
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ d->fd = create_cdrom_node(d->cd_device); /* this will do open */
+
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ if (! warned)
+ {
+ fprintf(stderr,"Cannot access %s\n",d->cd_device);
+ warned++;
+ }
+ }
+ else if (errno != EINTR)
+ {
+ return ( -6 );
+ }
+
+ /* No CD in drive. (Is this true also for svr4 ? XXX ) */
+ return (1);
+ }
+
+ if (warned)
+ {
+ warned = 0;
+ fprintf(stderr, "Thank you.\n");
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+
+ fd = d->fd;
+
+ if (wm_scsi_get_drive_type(d, vendor, model, rev) < 0)
+ {
+ perror("Cannot inquiry drive for it's type");
+ exit(1);
+ }
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+
+/*
+ * Send a SCSI command out the bus.
+ */
+int
+wm_scsi( struct wm_drive *d, unsigned char *xcdb, int cdblen,
+ char *retbuf, int retbuflen, int getreply)
+{
+ int ccode;
+ int file_des = d->fd;
+ int i,j;
+ unsigned char sense_buffer[ SENSE_SZ ];
+ int errno_save;
+
+ /* getreply == 1 is read, == 0 is write */
+
+ struct sb sb;
+ struct scs scs;
+
+ sb.sb_type = ISCB_TYPE;
+
+ sb.SCB.sc_comp_code = SDI_PROGRES;
+ sb.SCB.sc_int = NULL;
+ sb.SCB.sc_wd = 0;
+ sb.SCB.sc_dev.sa_major = 0;
+ sb.SCB.sc_dev.sa_minor = 0;
+ sb.SCB.sc_dev.sa_lun = 0;
+ sb.SCB.sc_dev.sa_exlun = 0;
+ sb.SCB.sc_status = 0;
+ sb.SCB.sc_link = (struct sb *) NULL;
+ sb.SCB.sc_resid = 0;
+
+ sb.SCB.sc_cmdpt = (void *)xcdb;
+ sb.SCB.sc_cmdsz = cdblen;
+
+ sb.SCB.sc_datapt = retbuf ;
+ sb.SCB.sc_datasz = retbuflen ;
+
+ if (getreply == 1)
+ sb.SCB.sc_mode = SCB_READ;
+ else
+ sb.SCB.sc_mode = SCB_WRITE;
+
+ sb.SCB.sc_time = 500;
+
+ ccode = ioctl(file_des, SDI_SEND, &sb);
+
+ if ( (sb.SCB.sc_comp_code != 0xd000000e ) ||
+ ( sb.SCB.sc_status != 02) )
+ return ccode;
+
+ errno_save = errno;
+
+ sb.SCB.sc_comp_code = SDI_PROGRES;
+ sb.SCB.sc_int = NULL;
+ sb.SCB.sc_wd = 0;
+ sb.SCB.sc_dev.sa_major = 0;
+ sb.SCB.sc_dev.sa_minor = 0;
+ sb.SCB.sc_dev.sa_lun = 0;
+ sb.SCB.sc_dev.sa_exlun = 0;
+ sb.SCB.sc_status = 0;
+ sb.SCB.sc_link = (struct sb *) NULL;
+ sb.SCB.sc_resid = 0;
+
+ scs.ss_op = SS_REQSEN;
+ scs.ss_lun = 0;
+ scs.ss_addr1 = 0;
+ scs.ss_addr = 0;
+ scs.ss_len = SENSE_SZ;
+ scs.ss_cont = 0;
+
+ sb.SCB.sc_cmdpt = SCS_AD(&scs);
+ sb.SCB.sc_cmdsz = SCS_SZ;
+ sb.SCB.sc_datapt = sense_buffer;
+ sb.SCB.sc_datasz = 18;
+ sb.SCB.sc_mode = SCB_READ;
+ sb.SCB.sc_time = 5000;
+
+ if (ioctl(file_des, SDI_SEND, &sb) < 0)
+ {
+ fprintf(stderr,"Cannot read sense.\n");
+ exit(-1);
+ }
+
+ errno=errno_save;
+ return -1;
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ return (wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index));
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ return (wm_scsi2_get_trackcount(d, tracks));
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ return (wm_scsi2_get_trackinfo(d, track, data, startframe));
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+
+ return (wm_scsi2_get_cdlen(d, frames));
+} /* gen_get_cdlen() */
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+ return (wm_scsi2_play(d, start, end));
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause(struct wm_drive *d)
+{
+ return (wm_scsi2_pause(d));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume(struct wm_drive *d)
+{
+ return (wm_scsi2_resume(d));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ return (wm_scsi2_stop(d));
+} /* gen_stop() */
+
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ return (wm_scsi2_eject(d));
+} /* gen_eject() */
+
+/*
+ * Close the tray.
+ * please review scsi.c / wm_scsi2_closetray()
+ * and send changes to milliByte@DeathsDoor.com
+ */
+int
+gen_closetray( struct wm_drive *d )
+{
+ return(wm_scsi2_closetray(d));
+} /* gen_closetray() */
+
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ return (wm_scsi2_set_volume(d, left, right));
+} /* gen_set_volume() */
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ return (wm_scsi2_get_volume(d, left, right));
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ /* This needs to be tested */
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+#endif
diff --git a/kscd/libwm/plat_template.c b/kscd/libwm/plat_template.c
new file mode 100644
index 00000000..bd47c20b
--- /dev/null
+++ b/kscd/libwm/plat_template.c
@@ -0,0 +1,295 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * This file surely contains nonsense. It's the porter's part to fill
+ * the gaps and assure that the resulting code makes sense.
+ *
+ */
+
+
+static char plat_template_id[] = "$Id$"
+
+#if [TEMPLATESYSTEM]
+
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * gen_init();
+ *
+ */
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * wmcd_open()
+ *
+ */
+int
+wmcd_open(struct wm_drive *d)
+{
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if( ! d )
+ {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if(d->fd > -1) /* device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return 0;
+ }
+
+ if( d->cd_device == (char *)NULL )
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ /* open() goes here */
+
+ if(find_drive_struct(vendor, model, rev)) {
+ gen_close(d);
+ return -1;
+ }
+
+ d->init(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * wm_scsi()
+ *
+ */
+int
+wm_scsi(struct wm_drive *d,
+ uchar_t *cdb, int cdblen,void *retbuf,int retbuflen,int getreply)
+{
+ return (0);
+} /* wm_scsi() */
+
+/*
+ * close the CD device
+ */
+
+int
+gen_close(struct wm_drive *d)
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+} /* gen_close() */
+
+/*
+ * gen_get_drive_status()
+ *
+ */
+int
+gen_get_drive_status(struct wm_drive *d,
+ int oldmode,
+ int *mode,
+ int *pos,
+ int *track,
+ int *index)
+{
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * gen_get_trackcount()
+ *
+ */
+int
+gen_get_trackcount(struct wm_drive *d,int *tracks)
+{
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * gen_get_trackinfo()
+ *
+ */
+int
+gen_get_trackinfo(struct wm_drive *d,int track,int *data,int *startframe)
+{
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * gen_get_cdlen()
+ *
+ */
+int
+gen_get_cdlen(struct wm_drive *d,int *frames)
+{
+ return (0);
+} /* gen_get_cdlen() */
+
+/*
+ * gen_play()
+ *
+ */
+int
+gen_play(struct wm_drive *d,int start,int end)
+{
+ return (0);
+} /* gen_play() */
+
+/*
+ * gen_pause()
+ *
+ */
+int
+gen_pause(struct wm_drive *d)
+{
+ return ioctl( 0 );
+} /* gen_pause() */
+
+/*
+ * gen_resume
+ *
+ */
+int
+gen_resume(struct wm_drive *d)
+{
+ return (0);
+} /* gen_resume() */
+
+/*
+ * gen_stop()
+ *
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ return (0);
+} /* gen_stop() */
+
+/*
+ * gen_eject()
+ *
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!wmcd_close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+int
+scale_volume(int vol,int max)
+{
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+} /* scale_volume() */
+
+int
+unscale_volume(int vol,int max)
+{
+ int n;
+ n = ( vol - min_volume ) * max_volume / (max - min_volume);
+ return (n <0)?0:n;
+} /* unscale_volume() */
+
+/*
+ * gen_set_volume()
+ *
+ */
+int
+gen_set_volume(struct wm_drive *d,int left,int right)
+{
+ return (0);
+} /* gen_set_volume() */
+
+/*
+ * gen_get_volume()
+ *
+ */
+int
+gen_get_volume(struct wm_drive *d,int *left,int *right)
+{
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *
+ * For systems without working wm_scsi(), this should return -1
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+#endif /* TEMPLATESYSTEM */
diff --git a/kscd/libwm/plat_ultrix.c b/kscd/libwm/plat_ultrix.c
new file mode 100644
index 00000000..2685b0f5
--- /dev/null
+++ b/kscd/libwm/plat_ultrix.c
@@ -0,0 +1,663 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * ULTRIX 4.2 drive control routines.
+ */
+
+static char plat_ultrix_id[] = "$Id$";
+
+#if defined(ultrix) || defined(__ultrix)
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ustat.h>
+#include <string.h>
+#include <sys/rzdisk.h>
+#include <sys/cdrom.h>
+
+#include "include/wm_config.h"
+#include "include/wm_cdtext.h"
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * This structure will be filled with the TOC header and all entries.
+ * Ultrix doesn't seem to allow getting single TOC entries.
+ * - Chris Ross (cross@eng.umd.edu)
+ */
+struct cd_toc_header_and_entries
+{
+ struct cd_toc_header cdth;
+ struct cd_toc_entry cdte[CDROM_MAX_TRACK+1];
+};
+
+void *malloc();
+char *strchr();
+
+int min_volume = 128;
+int max_volume = 255;
+
+char * ultrix_cd_device = NULL;
+/*
+ * fgetline()
+ *
+ * Simulate fgets, but joining continued lines in the output of uerf.
+ */
+
+#define BUF_SIZE 85 /* Max length of a (real) line */
+
+char *
+fgetline( FILE *fp )
+{
+ static char *retval = NULL;
+ static char holdbuf[BUF_SIZE + 1];
+ char tmp[BUF_SIZE + 1];
+ char *stmp;
+
+ if (!retval)
+ {
+ retval = malloc(BUF_SIZE * 3); /* 3 lines can be joined */
+ if (!retval)
+ return(NULL);
+ else
+ *retval = '\0';
+ }
+
+ if (*holdbuf)
+ {
+ strcpy(retval, holdbuf);
+ retval[strlen(retval)-1] = '\0';
+ memset(holdbuf, 0, BUF_SIZE+1);
+ }
+
+ while (fgets(tmp, BUF_SIZE, fp))
+ {
+ stmp = tmp + strspn(tmp, " \t");
+ if (*stmp == '_')
+ { /* Continuation line */
+ retval[strlen(retval)-1] = '\0'; /* Trim off C/R */
+ strcat(retval, stmp+1);
+ } else {
+ if (*retval)
+ {
+ strcpy(holdbuf, tmp);
+ holdbuf[strlen(holdbuf)-1] = -1;
+ return retval;
+ } else { /* First line read, keep reading */
+ strcat(retval, stmp);
+ retval[strlen(retval)-1] = '\0';
+ }
+ }
+ }
+ return NULL;
+} /* fgetline() */
+
+/*
+ * find_cdrom
+ *
+ * Determine the name of the CD-ROM device.
+ *
+ * Read through the boot records (via a call to uerf) and find the SCSI
+ * address of the CD-ROM. If the "CDROM" environment variable is set,
+ * use that instead.
+ */
+const char*
+find_cdrom()
+{
+ char *data;
+ FILE *uerf;
+ int fds[2];
+ int pid;
+ const char* device = NULL;
+
+ device = getenv("CDROM");
+
+ if (device != NULL)
+ {
+ if(strncmp("/dev/", device, 5) || strstr(device, "/../"))
+ return NULL;
+ }
+
+ pipe(fds);
+
+ if ((pid = fork()) == 0)
+ {
+ close(fds[0]);
+ dup2(fds[1], 1);
+ execl("/etc/uerf", "uerf", "-R", "-r", "300", (void *)0);
+ execl("/usr/sbin/uerf", "uerf", "-R", "-r", "300", (void *)0);
+ return NULL; /* _exit(1); */
+ } else if (pid < 0) {
+ perror("fork");
+ return NULL; /* exit(1); */
+ }
+
+ close(fds[1]);
+ uerf = fdopen(fds[0], "r");
+
+ while (data = fgetline(uerf))
+ if (strstr(data, "RRD42"))
+ {
+ char *device_p;
+
+ ultrix_cd_device = (char *)malloc(sizeof("/dev/rrz##c"));
+ strcpy(ultrix_cd_device, "/dev/r");
+ device_p = strstr(data, "rz");
+ device_p[(int)(strchr(device_p, ' ') - device_p)] = '\0';
+ strcat(ultrix_cd_device, device_p);
+ strcat(ultrix_cd_device, "c");
+ device = ultrix_cd_device;
+ break;
+ }
+
+ fclose(uerf);
+
+ if (device == NULL)
+ {
+ fprintf(stderr, "No cdrom (RRD42) is installed on this system\n");
+ return NULL; /* exit(1); */
+ }
+
+ kill(pid, 15);
+ (void)wait((int *)NULL);
+ return device;
+} /* find_cdrom() */
+
+/*
+ * initialize the drive. a no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ static int warned = 0;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = find_cdrom();
+
+ d->fd = open(d->cd_device, 0);
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ return -EACCES;
+ }
+ else if (errno != EINTR)
+ {
+ return( -6 );
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ find_drive_struct("", "", "");
+ d->fd = fd;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Send an arbitrary SCSI command to a device.
+ */
+int
+wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply )
+{
+ /* ULTRIX doesn't have a SCSI passthrough interface, does it? */
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct cd_sub_channel sc;
+ struct cd_subc_channel_data scd;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ sc.sch_address_format = CDROM_MSF_FORMAT;
+ sc.sch_data_format = CDROM_CURRENT_POSITION;
+ sc.sch_track_number = 0;
+ sc.sch_alloc_length = sizeof(scd);
+ sc.sch_buffer = (caddr_t)&scd;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (ioctl(d->fd, CDROM_READ_SUBCHANNEL, &sc))
+ return (0); /* ejected */
+
+ switch (scd.scd_header.sh_audio_status)
+ {
+ case AS_PLAY_IN_PROGRESS:
+ *mode = WM_CDM_PLAYING;
+dopos:
+ *pos = scd.scd_position_data.scp_absaddr.msf.m_units * 60 * 75 +
+ scd.scd_position_data.scp_absaddr.msf.s_units * 75 +
+ scd.scd_position_data.scp_absaddr.msf.f_units;
+ *track = scd.scd_position_data.scp_track_number;
+ *index = scd.scd_position_data.scp_index_number;
+ break;
+
+ case AS_PLAY_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ goto dopos;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case AS_PLAY_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case AS_NO_STATUS:
+ *mode = WM_CDM_STOPPED;
+ break;
+ }
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount( struct wm_drive *d, int *tracks )
+{
+ struct cd_toc_header hdr;
+
+ if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+ return (-1);
+
+ *tracks = hdr.th_ending_track;
+
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct cd_toc toc;
+ struct cd_toc_header hdr;
+ struct cd_toc_header_and_entries toc_buffer;
+
+ if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+ return (-1);
+
+ bzero((char *)&toc_buffer, sizeof(toc_buffer));
+ toc.toc_address_format = CDROM_MSF_FORMAT;
+ toc.toc_starting_track = 0;
+ toc.toc_alloc_length = (u_short)(((hdr.th_data_len1 << 8) +
+ hdr.th_data_len0) & 0xfff) + 2;
+ toc.toc_buffer = (caddr_t)&toc_buffer;
+
+ if (ioctl(d->fd, CDROM_TOC_ENTRYS, &toc))
+ return (-1);
+
+ if (track == 0)
+ track = hdr.th_ending_track + 1;
+
+ *data = (toc_buffer.cdte[track-1].te_control & CDROM_DATA_TRACK) ? 1:0;
+ *startframe = toc_buffer.cdte[track - 1].te_absaddr.msf.m_units*60*75 +
+ toc_buffer.cdte[track - 1].te_absaddr.msf.s_units * 75 +
+ toc_buffer.cdte[track - 1].te_absaddr.msf.f_units;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+ return (gen_get_trackinfo(d, 0, &tmp, frames));
+} /* gen_get_cdlen() */
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+ struct cd_play_audio_msf msf;
+
+ msf.msf_starting_M_unit = start / (60*75);
+ msf.msf_starting_S_unit = (start % (60*75)) / 75;
+ msf.msf_starting_F_unit = start % 75;
+ msf.msf_ending_M_unit = end / (60*75);
+ msf.msf_ending_S_unit = (end % (60*75)) / 75;
+ msf.msf_ending_F_unit = end % 75;
+
+ if (ioctl(d->fd, SCSI_START_UNIT))
+ return (-1);
+ if (ioctl(d->fd, CDROM_PLAY_MSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDROM_PAUSE_PLAY));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDROM_RESUME_PLAY));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop( struct wm_drive *d )
+{
+ return (ioctl(d->fd, SCSI_STOP_UNIT));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ /* On some systems, we can check to see if the CD is mounted. */
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ return (ioctl(d->fd, CDROM_EJECT_CADDY));
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. The actual formula looks like
+ *
+ * (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ * max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ * return ((vol * (max_volume - min_volume)) / max + min_volume);
+ */
+scale_volume( int vol, int max )
+{
+ return ((max * max - (max - vol) * (max - vol)) *
+ (max_volume - min_volume) / (max * max) + min_volume);
+} /* scale_volume() */
+
+
+/*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume( int cd_vol, int max )
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ struct cd_playback pb;
+ struct cd_playback_status ps;
+ struct cd_playback_control pc;
+
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ bzero((char *)&pb, sizeof(pb));
+ bzero((char *)&ps, sizeof(ps));
+ bzero((char *)&pc, sizeof(pc));
+
+ pb.pb_alloc_length = sizeof(ps);
+ pb.pb_buffer = (caddr_t)&ps;
+
+ if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+ return (-1);
+
+ pc.pc_chan0_select = ps.ps_chan0_select;
+ pc.pc_chan0_volume = (left < CDROM_MIN_VOLUME) ?
+ CDROM_MIN_VOLUME : (left > CDROM_MAX_VOLUME) ?
+ CDROM_MAX_VOLUME : left;
+ pc.pc_chan1_select = ps.ps_chan1_select;
+ pc.pc_chan1_volume = (right < CDROM_MIN_VOLUME) ?
+ CDROM_MIN_VOLUME : (right > CDROM_MAX_VOLUME) ?
+ CDROM_MAX_VOLUME : right;
+
+ pb.pb_alloc_length = sizeof(pc);
+ pb.pb_buffer = (caddr_t)&pc;
+
+ if (ioctl(d->fd, CDROM_PLAYBACK_CONTROL, &pb))
+ return (-1);
+
+ return (0);
+} /* gen_set_volume() */
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ struct cd_playback pb;
+ struct cd_playback_status ps;
+
+ bzero((char *)&pb, sizeof(pb));
+ bzero((char *)&ps, sizeof(ps));
+
+ pb.pb_alloc_length = sizeof(ps);
+ pb.pb_buffer = (caddr_t)&ps;
+
+ if (d->fd >= 0)
+ {
+ if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+ *left = *right = -1;
+ else
+ {
+ *left = unscale_volume(ps.ps_chan0_volume, 100);
+ *right = unscale_volume(ps.ps_chan1_volume, 100);
+ }
+ }
+ else
+ *left = *right = -1;
+
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* No SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif
diff --git a/kscd/libwm/scsi.c b/kscd/libwm/scsi.c
new file mode 100644
index 00000000..4e0dd759
--- /dev/null
+++ b/kscd/libwm/scsi.c
@@ -0,0 +1,667 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Frontend functions for sending raw SCSI commands to the CD-ROM drive.
+ * These depend on wm_scsi(), which should be defined in each platform
+ * module.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+#include "include/wm_platform.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_cdtext.h"
+
+#define SCMD_INQUIRY 0x12
+#define SCMD_MODE_SELECT 0x15
+#define SCMD_MODE_SENSE 0x1a
+#define SCMD_START_STOP 0x1b
+#define SCMD_PREVENT 0x1e
+#define SCMD_READ_SUBCHANNEL 0x42
+#define SCMD_READ_TOC 0x43
+#define SCMD_PLAY_AUDIO_MSF 0x47
+#define SCMD_PAUSE_RESUME 0x4b
+
+#define SUBQ_STATUS_INVALID 0x00
+#define SUBQ_STATUS_PLAY 0x11
+#define SUBQ_STATUS_PAUSE 0x12
+#define SUBQ_STATUS_DONE 0x13
+#define SUBQ_STATUS_ERROR 0x14
+#define SUBQ_STATUS_NONE 0x15
+#define SUBQ_STATUS_NO_DISC 0x17 /* Illegal, but Toshiba returns it. */
+#define SUBQ_ILLEGAL 0xff
+
+#define PAGE_AUDIO 0x0e
+#define LEADOUT 0xaa
+
+#define WM_MSG_CLASS WM_MSG_CLASS_SCSI
+
+/* local prototypes */
+int wm_scsi_mode_select( struct wm_drive *d, unsigned char *buf, unsigned char len );
+int wm_scsi2_pause_resume(struct wm_drive *d, int resume);
+int wm_scsi2_prevent(struct wm_drive *d, int prevent);
+int wm_scsi2_play(struct wm_drive *d, int sframe, int eframe);
+int wm_scsi2_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe);
+int wm_scsi2_get_cdlen(struct wm_drive *d, int frames);
+int wm_scsi2_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind);
+int wm_scsi2_get_trackcount(struct wm_drive *d, int *tracks);
+int wm_scsi2_pause(struct wm_drive *d);
+int wm_scsi2_resume(struct wm_drive *d);
+int wm_scsi2_stop(struct wm_drive *d);
+int wm_scsi2_eject(struct wm_drive *d);
+int wm_scsi2_closetray(struct wm_drive *d);
+int wm_scsi2_get_volume(struct wm_drive *d, int *left, int *right);
+int wm_scsi2_set_volume(struct wm_drive *d, int left, int right);
+/* local prototypes END */
+
+/*
+ * Send a SCSI command over the bus, with all the CDB bytes specified
+ * as unsigned char parameters. This doesn't use varargs because some
+ * systems have stdargs instead and the number of bytes in a CDB is
+ * limited to 12 anyway.
+ *
+ * d Drive structure
+ * buf Buffer for data, both sending and receiving
+ * len Size of buffer
+ * dir TRUE if the command expects data to be returned in the buffer.
+ * a0- CDB bytes. Either 6, 10, or 12 of them, depending on the command.
+ */
+/*VARARGS4*/
+int
+sendscsi( struct wm_drive *d, void *buf,
+ unsigned int len, int dir,
+ unsigned char a0, unsigned char a1,
+ unsigned char a2, unsigned char a3,
+ unsigned char a4, unsigned char a5,
+ unsigned char a6, unsigned char a7,
+ unsigned char a8, unsigned char a9,
+ unsigned char a10, unsigned char a11 )
+
+{
+ int cdblen = 0;
+ unsigned char cdb[12];
+
+ cdb[0] = a0;
+ cdb[1] = a1;
+ cdb[2] = a2;
+ cdb[3] = a3;
+ cdb[4] = a4;
+ cdb[5] = a5;
+
+ switch ((a0 >> 5) & 7) {
+ case 0:
+ cdblen = 6;
+ break;
+
+ case 5:
+ cdb[10] = a10;
+ cdb[11] = a11;
+ cdblen = 12;
+
+ case 1:
+ case 2:
+ case 6: /* assume 10-byte vendor-specific codes for now */
+ cdb[6] = a6;
+ cdb[7] = a7;
+ cdb[8] = a8;
+ cdb[9] = a9;
+ if (! cdblen)
+ cdblen = 10;
+ break;
+ }
+
+ return (wm_scsi(d, cdb, cdblen, buf, len, dir));
+}
+
+/*
+ * Send a MODE SENSE command and return the results (minus header cruft)
+ * in a user buffer.
+ *
+ * d Drive structure
+ * page Number of page to query (plus page control bits, if any)
+ * buf Result buffer
+ */
+int
+wm_scsi_mode_sense( struct wm_drive *d, unsigned char page, unsigned char *buf )
+{
+ unsigned char pagebuf[255];
+ int status, i, len, offset;
+
+ status = sendscsi(d, pagebuf, sizeof(pagebuf), 1, SCMD_MODE_SENSE, 0,
+ page, 0, sizeof(pagebuf), 0,0,0,0,0,0,0);
+ if (status < 0)
+ return (status);
+
+ /*
+ * The first byte of the returned data is the transfer length. Then
+ * two more bytes and the length of whatever header blocks are in
+ * front of the page we want.
+ */
+ len = pagebuf[0] - pagebuf[3] - 3;
+ offset = pagebuf[3] + 4;
+ for (i = 0; i < len; i++)
+ buf[i] = pagebuf[offset + i];
+
+ return (0);
+}
+
+/*
+ * Send a MODE SELECT command.
+ *
+ * d Drive structure
+ * buf Page buffer (no need to put on block descriptors)
+ * len Size of page
+ */
+int
+wm_scsi_mode_select( struct wm_drive *d, unsigned char *buf, unsigned char len )
+{
+ unsigned char pagebuf[255];
+ int i;
+
+ pagebuf[0] = pagebuf[1] = pagebuf[2] = pagebuf[3] = 0;
+ for (i = 0; i < (int) len; i++)
+ pagebuf[i + 4] = buf[i];
+
+ return (sendscsi(d, pagebuf, len + 4, 0, SCMD_MODE_SELECT, 0x10, 0,
+ 0, len + 4, 0,0,0,0,0,0,0));
+}
+
+/*
+ * Send an INQUIRY command to get the drive type.
+ *
+ * d Drive structure
+ * vendor Buffer for vendor name (8 bytes + null)
+ * model Buffer for model name (16 bytes + null)
+ * rev Buffer for revision level (4 bytes + null)
+ *
+ * The above string lengths apply to the SCSI INQUIRY command. The
+ * actual WorkMan drive structure reserves 31 bytes + NULL per entry.
+ *
+ * If the model name begins with "CD-ROM" and zero or more spaces, that will
+ * all be stripped off since it's just extra junk to WorkMan.
+ */
+int
+wm_scsi_get_drive_type( struct wm_drive *d, char *vendor,
+ char *model, char *rev )
+{
+/* removed unsigned*/
+ char *s, *t, buf[36];
+ memset(buf, 0, 36);
+
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_INFO, "Sending SCSI inquiry command...\n");
+ if (sendscsi(d, buf, 36, 1, SCMD_INQUIRY, 0, 0, 0, 36, 0,0,0,0,0,0,0))
+ {
+ sprintf( vendor, WM_STR_GENVENDOR);
+ sprintf( model, WM_STR_GENMODEL);
+ sprintf( rev, WM_STR_GENREV);
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_ERROR, "SCSI Inquiry command not supported in this context\n");
+ return -1;
+ }
+
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_DEBUG, "sent.\n");
+
+ memcpy(vendor, buf + 8, 8);
+ vendor[8] = '\0';
+ memcpy(model, buf + 16, 16);
+ model[16] = '\0';
+ memcpy(rev, buf + 32, 4);
+ rev[4] = '\0';
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "SCSI Inquiry result: [%s|%s|%s]\n", vendor, model, rev);
+
+
+ /* Remove "CD-ROM " from the model. */
+ if (! strncmp(model, "CD-ROM", 6))
+ {
+ s = model + 6;
+ t = model;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ while( (*t++ = *s++) )
+ ;
+ }
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_INFO, "scsi: Cooked data: %s %s rev. %s\n", vendor, model,rev);
+ return (0);
+} /* wm_scsi_get_drive_type() */
+
+/*
+ * Send a SCSI-2 PAUSE/RESUME command. "resume" is 1 to resume, 0 to pause.
+ */
+int
+wm_scsi2_pause_resume(struct wm_drive *d, int resume)
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_PAUSE_RESUME, 0, 0, 0, 0, 0, 0,
+ 0, resume ? 1 : 0, 0,0,0));
+}
+
+/*
+ * Send a SCSI-2 "prevent media removal" command. "prevent" is 1 to lock
+ * caddy in.
+ */
+int
+wm_scsi2_prevent(struct wm_drive *d, int prevent)
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_PREVENT, 0, 0, 0, 0, 0, 0,
+ 0, prevent ? 1 : 0, 0,0,0));
+}
+
+/*
+ * Send a SCSI-2 PLAY AUDIO MSF command. Pass the starting and ending
+ * frame numbers.
+ */
+int
+wm_scsi2_play(struct wm_drive *d, int sframe, int eframe)
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_PLAY_AUDIO_MSF, 0, 0,
+ sframe / (60 * 75), (sframe / 75) % 60, sframe % 75,
+ eframe / (60 * 75), (eframe / 75) % 60, eframe % 75,
+ 0,0,0));
+}
+
+/*
+ * Send a SCSI-2 READ TOC command to get the data for a particular track.
+ * Fill in track information from the returned data.
+ */
+int
+wm_scsi2_get_trackinfo(struct wm_drive *d, int track,
+ int *data, int *startframe)
+{
+ unsigned char buf[12]; /* one track's worth of info */
+
+ if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 2,
+ 0, 0, 0, 0, track, sizeof(buf) / 256,
+ sizeof(buf) % 256, 0,0,0))
+ return (-1);
+
+ *data = buf[5] & 4 ? 1 : 0;
+ *startframe = buf[9] * 60 * 75 + buf[10] * 75 + buf[11];
+
+ return (0);
+}
+
+/*
+ * Get the starting frame for the leadout area (which should be the same as
+ * the length of the disc as far as WorkMan is concerned).
+ */
+int
+wm_scsi2_get_cdlen(struct wm_drive *d, int frames)
+{
+ int tmp;
+ return (wm_scsi2_get_trackinfo(d, LEADOUT, &tmp, &frames));
+}
+
+/*
+ * Get the current status of the drive by sending the appropriate SCSI-2
+ * READ SUB-CHANNEL command.
+ */
+int
+wm_scsi2_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind)
+{
+ unsigned char buf[48];
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+/*
+ * stupid somehow, but necessary this time
+ */
+ switch( wmcd_open( d ) ) {
+
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ /* If we can't read status, the CD has been ejected. */
+ buf[1] = SUBQ_ILLEGAL;
+ if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_SUBCHANNEL, 2, 64, 1,
+ 0, 0, 0, sizeof(buf) / 256, sizeof(buf) % 256, 0,0,0))
+ return (0);
+
+ switch (buf[1]) {
+ case SUBQ_STATUS_PLAY:
+ *mode = WM_CDM_PLAYING;
+ *track = buf[6];
+ *ind = buf[7];
+ *pos = buf[9] * 60 * 75 + buf[10] * 75 + buf[11];
+ break;
+
+ case SUBQ_STATUS_PAUSE:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ *track = buf[6];
+ *ind = buf[7];
+ *pos = buf[9] * 60 * 75 +
+ buf[10] * 75 +
+ buf[11];
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ /*
+ * SUBQ_STATUS_DONE is sometimes returned when the CD is idle,
+ * even though the spec says it should only be returned when an
+ * audio play operation finishes.
+ */
+ case SUBQ_STATUS_DONE:
+ case SUBQ_STATUS_NONE:
+ case SUBQ_STATUS_INVALID:
+ if (oldmode == WM_CDM_PLAYING)
+ *mode = WM_CDM_TRACK_DONE;
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ /*
+ * This usually means there's no disc in the drive.
+ */
+ case SUBQ_STATUS_NO_DISC:
+ break;
+
+ /*
+ * This usually means the user ejected the CD manually.
+ */
+ case SUBQ_STATUS_ERROR:
+ break;
+
+ case SUBQ_ILLEGAL: /* call didn't really succeed */
+ break;
+
+ default:
+ *mode = WM_CDM_UNKNOWN;
+#ifndef NDEBUG
+ if( getenv( "WORKMAN_DEBUG" ) != NULL )
+ printf("wm_scsi2_get_drive_status: status is 0x%x\n",
+ buf[1]);
+#endif
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Get the number of tracks on the CD using the SCSI-2 READ TOC command.
+ */
+int
+wm_scsi2_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ unsigned char buf[4];
+
+ if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 0,
+ 0, 0, 0, 0, 0, sizeof(buf) / 256,
+ sizeof(buf) % 256, 0,0,0))
+ return (-1);
+
+ *tracks = buf[3] - buf[2] + 1;
+ return (0);
+}
+
+/*
+ * Pause the CD.
+ */
+int
+wm_scsi2_pause(struct wm_drive *d)
+{
+ return (wm_scsi2_pause_resume(d, 0));
+}
+
+/*
+ * Resume playing after a pause.
+ */
+int
+wm_scsi2_resume(struct wm_drive *d)
+{
+ return (wm_scsi2_pause_resume(d, 1));
+}
+
+/*
+ * Stop playing the CD by sending a START STOP UNIT command.
+ */
+int
+wm_scsi2_stop(struct wm_drive *d)
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 0, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Eject the CD by sending a START STOP UNIT command.
+ */
+int
+wm_scsi2_eject(struct wm_drive *d)
+{
+ /* Unlock the disc (possibly unnecessary). */
+ if (wm_scsi2_prevent(d, 0))
+ return (-1);
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "Issuing START_STOP for ejecting...\n");
+ return (sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Something like a dummy. The SCSI-2 specs are too hard for me to
+ * understand here...
+ *
+ * If you have the correct command handy, please send the code to
+ * milliByte@DeathsDoor.com
+ */
+int
+wm_scsi2_closetray(struct wm_drive *d)
+{
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "Issuing START_STOP for closing...\n");
+ return (sendscsi(d, NULL, 0,0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Get the volume by doing a MODE SENSE command.
+ */
+int
+wm_scsi2_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ unsigned char mode[16];
+
+ *left = *right = -1;
+
+ /* Get the current audio parameters first. */
+ if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
+ return (-1);
+
+ *left = ((int) mode[9] * 100) / 255;
+ *right = ((int) mode[11] * 100) / 255;
+
+ return (0);
+}
+
+/*
+ * Set the volume by doing a MODE SELECT command.
+ */
+int
+wm_scsi2_set_volume(struct wm_drive *d, int left, int right)
+{
+ unsigned char mode[16];
+
+ /* Get the current audio parameters first. */
+ if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
+ return (-1);
+
+ /* Tweak the volume part of the parameters. */
+ mode[9] = (left * 255) / 100;
+ mode[11] = (right * 255) / 100;
+
+ /* And send them back to the drive. */
+ return (wm_scsi_mode_select(d, mode, sizeof(mode)));
+}
+
+/*------------------------------------------------------------------------*
+ * wm_scsi_get_cdtext(drive, buffer, lenght)
+ *
+ * Return a buffer with cdtext-stream. buffer mus be allocated and filled
+ *
+ *
+ *------------------------------------------------------------------------*/
+
+int
+wm_scsi_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_length)
+{
+ int ret;
+ unsigned char temp[8];
+ unsigned char *dynamic_temp;
+ int cdtext_possible;
+ unsigned short cdtext_data_length;
+ unsigned long feature_list_length;
+#define IGNORE_FEATURE_LIST
+#ifndef IGNORE_FEATURE_LIST
+ struct feature_list_header *pHeader;
+ struct feature_descriptor_cdread *pDescriptor;
+#endif /* IGNORE_FEATURE_LIST */
+
+ dynamic_temp = NULL;
+ cdtext_possible = 0;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wm_scsi_get_cdtext entered\n");
+
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: use GET_FEATURY_LIST(0x46)...\n");
+ ret = sendscsi(d, temp, 8, 1,
+ 0x46, 0x02, 0x00, 0x1E, 0,
+ 0, 0, 0, 8, 0, 0, 0);
+
+ if(ret)
+ {
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT ERROR: GET_FEATURY_LIST(0x46) not implemented or broken. ret = %i!\n", ret);
+#ifndef IGNORE_FEATURE_LIST
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT ERROR: Try #define IGNORE_FEATURE_LIST in libwm/scsi.c\n");
+#else
+ cdtext_possible = 1;
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: GET_FEATURY_LIST(0x46) ignored. It's OK, because many CDROMS don't implement this feature\n");
+#endif /* IGNORE_FEATURE_LIST */
+ }
+ else
+ {
+ feature_list_length = temp[0]*0xFFFFFF + temp[1]*0xFFFF + temp[2]*0xFF + temp[3] + 4;
+
+ dynamic_temp = malloc(feature_list_length);
+
+ if(!dynamic_temp)
+ return -1;
+
+ memset(dynamic_temp, 0, feature_list_length);
+ ret = sendscsi(d, dynamic_temp, feature_list_length, 1,
+ 0x46, 0x02, 0x00, 0x1E, 0, 0,
+ 0, (feature_list_length>>8) & 0xFF, feature_list_length & 0xFF, 0, 0, 0);
+
+
+#ifndef IGNORE_FEATURE_LIST
+ if(!ret)
+ {
+ pHeader = (struct feature_list_header*)dynamic_temp;
+/* printf("length = %i, profile = 0x%02X%02X\n", pHeader->lenght_lsb, pHeader->profile_msb, pHeader->profile_lsb);*/
+ pDescriptor = (struct feature_descriptor_cdread*)(dynamic_temp + sizeof(struct feature_list_header));
+/* printf("code = 0x%02X%02X, settings = 0x%02X, add_length = %i, add_settings = 0x%02X \n",
+ pDescriptor->feature_code_msb, pDescriptor->feature_code_lsb, pDescriptor->settings,
+ pDescriptor->add_lenght, pDescriptor->add_settings);*/
+
+ cdtext_possible = pDescriptor->add_settings & 0x01;
+ }
+ else
+ {
+ cdtext_possible = 0;
+ }
+
+#else
+ cdtext_possible = 1;
+#endif /* IGNORE_FEATURE_LIST */
+
+ free (dynamic_temp);
+ dynamic_temp = 0;
+ }
+
+ if(!cdtext_possible)
+ {
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: GET_FEATURY_LIST(0x46) says, CDTEXT is not present!\n");
+ return EXIT_SUCCESS;
+ }
+
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: try to read, how long CDTEXT is?\n");
+ ret = sendscsi(d, temp, 4, 1,
+ SCMD_READ_TOC, 0x00, 0x05, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0);
+
+ if(ret)
+ {
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
+ "CDTEXT ERROR: READ_TOC(0x43) with format code 0x05 not implemented or broken. ret = %i!\n", ret);
+ }
+ else
+ {
+ cdtext_data_length = temp[0]*0xFF + temp[1] + 4 + 1; /* divide by 18 + 4 ? */
+ /* cdtext_data_length%18 == 0;? */
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: CDTEXT is %i byte(s) long\n", cdtext_data_length);
+ /* cdc_buffer[2]; cdc_buffer[3]; reserwed */
+ dynamic_temp = malloc(cdtext_data_length);
+ if(!dynamic_temp)
+ return -1;
+
+ memset(dynamic_temp, 0, cdtext_data_length);
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: try to read CDTEXT\n");
+ ret = sendscsi(d, dynamic_temp, cdtext_data_length, 1,
+ SCMD_READ_TOC, 0x00, 0x05, 0, 0, 0,
+ 0, (cdtext_data_length>>8) & 0xFF, cdtext_data_length & 0xFF, 0, 0, 0);
+
+ if(ret)
+ {
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
+ "CDTEXT ERROR: READ_TOC(0x43) with format code 0x05 not implemented or broken. ret = %i!\n", ret);
+ }
+ else
+ {
+ cdtext_data_length = temp[0]*0xFF + temp[1] + 4 + 1; /* divide by 18 + 4 ? */
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: read %i byte(s) of CDTEXT\n", cdtext_data_length);
+
+ /* send cdtext only 18 bytes packs * ? */
+ *(p_buffer_length) = cdtext_data_length - 4;
+ *pp_buffer = malloc(*p_buffer_length);
+ if(!(*pp_buffer))
+ {
+ return -1;
+ }
+ memcpy(*pp_buffer, dynamic_temp + 4, *p_buffer_length);
+ }
+ free(dynamic_temp);
+ dynamic_temp = 0;
+ }
+
+ return ret;
+} /* wm_scsi_get_cdtext() */
diff --git a/kscd/libwm/wm_helpers.c b/kscd/libwm/wm_helpers.c
new file mode 100644
index 00000000..109efe1b
--- /dev/null
+++ b/kscd/libwm/wm_helpers.c
@@ -0,0 +1,238 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Some helpful functions...
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/time.h>
+#include "include/workman_defs.h"
+#include "include/wm_config.h"
+#include "include/wm_helpers.h"
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_MISC
+
+int wm_lib_verbosity = WM_MSG_LEVEL_NONE;
+
+/*
+ * Some seleced functions of version reporting follow...
+ */
+
+int wm_libver_major( void ){return WM_LIBVER_MAJOR;}
+int wm_libver_minor( void ){return WM_LIBVER_MINOR;}
+int wm_libver_pl( void ){return WM_LIBVER_PL;}
+
+char *wm_libver_name( void )
+{
+ char *s = NULL;
+
+ wm_strmcat(&s, WM_LIBVER_NAME);
+ return s;
+} /* wm_libver_name() */
+
+char *wm_libver_number( void )
+{
+ char *s = NULL;
+
+ s = malloc(10);
+ /* this is not used very often, so don't care about speed...*/
+ sprintf(s, "%d.%d.%d", wm_libver_major(), wm_libver_minor(), wm_libver_pl());
+ return s;
+} /* wm_libver_number() */
+
+char *wm_libver_date( void )
+{
+ char *s = NULL;
+ wm_strmcat(&s, __DATE__);
+ return s;
+} /* wm_libver_date() */
+
+char *wm_libver_string( void )
+{
+ char *s = NULL;
+
+ wm_strmcat( &s, wm_libver_name() );
+ wm_strmcat( &s, " " );
+ wm_strmcat( &s, wm_libver_number() );
+ return s;
+} /* wm_libver_string() */
+
+
+/*
+ *
+ * Now for some memory management...
+ *
+ */
+
+/* Free some memory and set a pointer to null. */
+void freeup( char **x )
+{
+ if (*x != NULL)
+ {
+ free(*x);
+ *x = NULL;
+ }
+} /* freeup() */
+
+/* Copy into a malloced string. */
+void
+wm_strmcpy( char **t, const char *s )
+{
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy(%s, '%s')\n", *t, s);
+ if (*t != NULL)
+ {
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy freeing pointer %p\n", *t);
+ free(*t);
+ }
+
+ *t = malloc(strlen(s) + 1);
+ if (*t == NULL)
+ {
+ perror("wm_strmcpy");
+ exit(1);
+ }
+
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy finally copying (%p, '%s')\n", *t, s);
+ strncpy(*t, s, strlen(s));
+} /* wm_strmcpy() */
+
+/* Add to a malloced string. */
+void
+wm_strmcat( char **t, const char *s)
+{
+ int len = strlen(s) + 1;
+
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcat(%s, %s)\n", *t, s);
+
+ if (*s == '\0')
+ return;
+
+ if (*t != NULL)
+ {
+ len += strlen(*t);
+ *t = realloc(*t, len);
+ if (*t == NULL)
+ {
+ perror("wm_strmcat");
+ exit(1);
+ }
+ strcat(*t, s);
+ }
+ else
+ wm_strmcpy(t, s);
+} /* wm_strmcat() */
+
+/* Duplicate a string. Some systems have this in libc, but not all. */
+char *
+wm_strdup( char *s )
+{
+ char *new;
+
+ new = malloc(strlen(s) + 1);
+ if (new)
+ strcpy(new, s);
+ return (new);
+} /* wm_strdup() */
+
+
+/*
+ * set and get verbosity level.
+ */
+void wm_lib_set_verbosity( int level )
+{
+ if( WM_MSG_LEVEL_NONE <= level && level <= WM_MSG_LEVEL_DEBUG )
+ {
+ wm_lib_verbosity = level;
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "Verbosity set to %d|%d\n", WM_MSG_LEVEL_DEBUG, level & WM_MSG_CLASS_ALL);
+ }
+} /* wm_lib_set_verbosity */
+
+int wm_lib_get_verbosity( void )
+{
+ return wm_lib_verbosity;
+}
+
+/*
+ * wm_lib_message().
+ *
+ * any message that falls into allowed classes and has at least
+ * verbosity level wm_lib_verbosity & 0xf will be printed.
+ *
+ * Usage:
+ *
+ * wm_lib_message( WM_MSG_LEVEL | WM_MSG_CLASS, "format", contents);
+ *
+ * To simplify the usage, you may simply use WM_MSG_CLASS. It should be
+ * defined in each module to reflect the correct message class.
+ *
+ */
+void wm_lib_message( unsigned int level, const char *fmt, ... )
+{
+ va_list ap;
+ /* verbosity level */
+ unsigned int vlevel = wm_lib_verbosity & 0xf;
+ /* allowed classes */
+ unsigned int vclass = (level & WM_MSG_CLASS_ALL) & (wm_lib_verbosity & WM_MSG_CLASS_ALL);
+
+ /*
+ * just give me the level
+ */
+ level &= 0xf;
+ if(level <= WM_MSG_LEVEL_NONE)
+ {
+ fprintf(stderr, "LibWorkMan warning: A LibWorkMan programmer specified an invalid message level.\n");
+ }
+ /*
+ * print it only if level and class are allowed.
+ */
+ if( (level <= vlevel) && (vclass != 0) )
+ {
+ fprintf(stderr, "libWorkMan: ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+} /* wm_lib_message() */
+
+/*
+ * Simulate usleep() using select().
+ */
+int
+wm_susleep( int usec )
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = usec / 1000000;
+ tv.tv_usec = usec % 1000000;
+ return (select(0, NULL, NULL, NULL, &tv));
+} /* wm_susleep() */
+
+