diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | e2de64d6f1beb9e492daf5b886e19933c1fa41dd (patch) | |
tree | 9047cf9e6b5c43878d5bf82660adae77ceee097a /kscd/libwm | |
download | tdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.tar.gz tdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdemultimedia@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kscd/libwm')
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() */ + + |