File indexing completed on 2024-04-21 04:54:23

0001 /*
0002  * This file is part of WorkMan, the civilized CD player library
0003  * Copyright (C) 1991-1997 by Steven Grimm <koreth@midwinter.com>
0004  * Copyright (C) by Dirk Försterling <milliByte@DeathsDoor.com>
0005  *
0006  * This library is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU Library General Public
0008  * License as published by the Free Software Foundation; either
0009  * version 2 of the License, or (at your option) any later version.
0010  *
0011  * This library is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014  * Library General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU Library General Public
0017  * License along with this library; if not, write to the Free
0018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0019  *
0020  *
0021  * Linux-specific drive control routines.  Very similar to the Sun module.
0022  */
0023 
0024 #if defined(__linux__)
0025 
0026 #include "include/wm_config.h"
0027 #include "include/wm_struct.h"
0028 #include "include/wm_cdtext.h"
0029 #include "include/wm_cdda.h"
0030 #include "include/wm_platform.h"
0031 #include "include/wm_cdrom.h"
0032 #include "include/wm_scsi.h"
0033 #include "include/wm_helpers.h"
0034 
0035 #include <errno.h>
0036 #include <stdio.h>
0037 #include <stdlib.h>
0038 #include <fcntl.h>
0039 #include <unistd.h>
0040 #include <string.h>
0041 #include <sys/types.h>
0042 #include <sys/param.h>
0043 #include <sys/socket.h>
0044 #include <sys/stat.h>
0045 #include <sys/wait.h>
0046 /* Try to get around bug #29274 */
0047 #include <linux/version.h>
0048 #if 0
0049 /* this breaks the build on ia64 and s390 for example.
0050    sys/types.h is already included and should provide __u64.
0051    please tell where we really need this and let's try to find
0052    a working #if case for everyone ... adrian@suse.de */
0053 #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))
0054 #undef __GNUC__
0055 typedef unsigned long long __u64;
0056 #endif
0057 #endif
0058 
0059 #if defined(BSD_MOUNTTEST)
0060   #include <mntent.h>
0061 #else
0062   /*
0063    * this is for glibc 2.x which defines ust structure in
0064    * ustat.h not stat.h
0065    */
0066   #ifdef __GLIBC__
0067     #include <sys/ustat.h>
0068   #endif
0069 #endif
0070 
0071 
0072 #include <sys/time.h>
0073 #include <sys/ioctl.h>
0074 
0075 #ifndef __GNUC__
0076 #define __GNUC__ 1
0077 #endif
0078 /* needed for vanilla kernel headers, which do provide __u64 only
0079    for ansi */
0080 #undef __STRICT_ANSI__
0081 /* needed for non-ansi kernel headers */
0082 #define asm __asm__
0083 #define inline __inline__
0084 #include <asm/types.h>
0085 #include <linux/cdrom.h>
0086 #undef asm
0087 #undef inline
0088 
0089 #ifdef OSS_SUPPORT
0090 #include <linux/soundcard.h>
0091 #define CD_CHANNEL SOUND_MIXER_CD
0092 #endif
0093 
0094 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
0095 
0096 #ifdef LINUX_SCSI_PASSTHROUGH
0097 /* this is from <scsi/scsi_ioctl.h> */
0098 # define SCSI_IOCTL_SEND_COMMAND 1
0099 #endif
0100 
0101 /*-------------------------------------------------------*
0102  *
0103  *
0104  *                   CD-ROM drive functions.
0105  *
0106  *
0107  *-------------------------------------------------------*/
0108 int gen_init(struct wm_drive *d)
0109 {
0110     return 0;
0111 }
0112 
0113 int gen_open(struct wm_drive *d)
0114 {
0115     if(d->fd > -1) {
0116         wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "plat_open(): [device is open (fd=%d)]\n",
0117             d->fd);
0118         return 0;
0119     }
0120 
0121     d->fd = open(d->cd_device, O_RDONLY | O_NONBLOCK);
0122     wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "plat_open(): device=%s fd=%d\n",
0123         d->cd_device, d->fd);
0124 
0125     if(d->fd < 0)
0126         return -errno;
0127 
0128     return 0;
0129 }
0130 
0131 int gen_close(struct wm_drive *d)
0132 {
0133     wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "linux_close(): closing the device\n");
0134 
0135     close(d->fd);
0136     d->fd = -1;
0137 
0138     return 0;
0139 }
0140 
0141 /*--------------------------------------------------------------------------*
0142  * Get the current status of the drive: the current play mode, the absolute
0143  * position from start of disc (in frames), and the current track and index
0144  * numbers if the CD is playing or paused.
0145  *--------------------------------------------------------------------------*/
0146 int gen_get_drive_status(struct wm_drive *d, int oldmode, int *mode, int *pos, int *track, int *ind)
0147 {
0148     struct cdrom_subchnl sc;
0149     int ret;
0150 
0151 #ifdef SBPCD_HACK
0152     static int prevpos = 0;
0153 #endif
0154 
0155     /* Is the device open? */
0156     if (d->fd > -1) {
0157         ret = d->proto.open(d);
0158         if(ret < 0) /* error */
0159             return ret;
0160 
0161         if(ret == 1) {
0162             /* retry */
0163             *mode = WM_CDM_UNKNOWN;
0164             return 0;
0165         }
0166     }
0167 
0168     /* Try to get rid of the door locking    */
0169     /* Don't care about return value. If it  */
0170     /* works - fine. If not - ...            */
0171     ioctl(d->fd, CDROM_LOCKDOOR, 0);
0172 
0173     *mode = WM_CDM_UNKNOWN;
0174 
0175     sc.cdsc_format = CDROM_MSF;
0176 
0177     if(!ioctl(d->fd, CDROMSUBCHNL, &sc)) {
0178         switch (sc.cdsc_audiostatus) {
0179         case CDROM_AUDIO_PLAY:
0180             *mode = WM_CDM_PLAYING;
0181             *track = sc.cdsc_trk;
0182             *ind = sc.cdsc_ind;
0183             *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
0184             sc.cdsc_absaddr.msf.second * 75 +
0185             sc.cdsc_absaddr.msf.frame;
0186 #ifdef SBPCD_HACK
0187             if( *pos < prevpos ) {
0188                 if( (prevpos - *pos) < 75 ) {
0189                     *mode = WM_CDM_TRACK_DONE;
0190                 }
0191             }
0192 
0193             prevpos = *pos;
0194 #endif
0195             break;
0196 
0197         case CDROM_AUDIO_PAUSED:
0198             if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) {
0199                 *mode = WM_CDM_PAUSED;
0200                 *track = sc.cdsc_trk;
0201                 *ind = sc.cdsc_ind;
0202                 *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
0203                 sc.cdsc_absaddr.msf.second * 75 +
0204                 sc.cdsc_absaddr.msf.frame;
0205             } else
0206                 *mode = WM_CDM_STOPPED;
0207             break;
0208 
0209         case CDROM_AUDIO_NO_STATUS:
0210             *mode = WM_CDM_STOPPED;
0211             break;
0212 
0213         case CDROM_AUDIO_COMPLETED:
0214             *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
0215             break;
0216 
0217         case CDROM_AUDIO_INVALID: /**/
0218         default:
0219             *mode = WM_CDM_UNKNOWN;
0220             break;
0221         }
0222     }
0223 
0224     if(WM_CDS_NO_DISC(*mode)) {
0225         /* verify status of drive */
0226         ret = ioctl(d->fd, CDROM_DRIVE_STATUS, 0/* slot */);
0227         if(ret == CDS_DISC_OK)
0228             ret = ioctl(d->fd, CDROM_DISC_STATUS, 0);
0229 
0230         switch(ret) {
0231         case CDS_NO_DISC:
0232             *mode = WM_CDM_NO_DISC;
0233             break;
0234         case CDS_TRAY_OPEN:
0235             *mode = WM_CDM_EJECTED;
0236             break;
0237         case CDS_AUDIO:
0238         case CDS_MIXED:
0239             *mode = WM_CDM_STOPPED;
0240             break;
0241         case CDS_DRIVE_NOT_READY:
0242         case CDS_NO_INFO:
0243         case CDS_DATA_1:
0244         case CDS_DATA_2:
0245         case CDS_XA_2_1:
0246         case CDS_XA_2_2:
0247         default:
0248             *mode = WM_CDM_UNKNOWN;
0249         }
0250     }
0251 
0252     return 0;
0253 }
0254 
0255 /*-------------------------------------*
0256  * Get the number of tracks on the CD.
0257  *-------------------------------------*/
0258 int gen_get_trackcount(struct wm_drive *d, int *tracks)
0259 {
0260     struct cdrom_tochdr hdr;
0261 
0262     if(ioctl(d->fd, CDROMREADTOCHDR, &hdr))
0263         return -1;
0264 
0265     *tracks = hdr.cdth_trk1;
0266     return 0;
0267 }
0268 
0269 /*---------------------------------------------------------*
0270  * Get the start time and mode (data or audio) of a track.
0271  *---------------------------------------------------------*/
0272 int gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
0273 {
0274     struct cdrom_tocentry entry;
0275 
0276     entry.cdte_track = track;
0277     entry.cdte_format = CDROM_MSF;
0278 
0279     if(ioctl(d->fd, CDROMREADTOCENTRY, &entry))
0280         return -1;
0281 
0282     *startframe = entry.cdte_addr.msf.minute * 60 * 75 +
0283         entry.cdte_addr.msf.second * 75 +
0284         entry.cdte_addr.msf.frame;
0285     *data = entry.cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0;
0286 
0287     return 0;
0288 }
0289 
0290 /*-------------------------------------*
0291  * Get the number of frames on the CD.
0292  *-------------------------------------*/
0293 int gen_get_cdlen(struct wm_drive *d, int *frames)
0294 {
0295     int tmp;
0296 
0297     return d->proto.get_trackinfo(d, CDROM_LEADOUT, &tmp, frames);
0298 }
0299 
0300 /*------------------------------------------------------------*
0301  * Play the CD from one position to another (both in frames.)
0302  *------------------------------------------------------------*/
0303 int gen_play(struct wm_drive *d, int start, int end)
0304 {
0305     struct cdrom_msf msf;
0306 
0307     msf.cdmsf_min0 = start / (60*75);
0308     msf.cdmsf_sec0 = (start % (60*75)) / 75;
0309     msf.cdmsf_frame0 = start % 75;
0310     msf.cdmsf_min1 = end / (60*75);
0311     msf.cdmsf_sec1 = (end % (60*75)) / 75;
0312     msf.cdmsf_frame1 = end % 75;
0313 
0314     if(ioctl(d->fd, CDROMPLAYMSF, &msf)) {
0315         if(ioctl(d->fd, CDROMSTART))
0316             return -1;
0317         if(ioctl(d->fd, CDROMPLAYMSF, &msf))
0318             return -2;
0319     }
0320 
0321     return 0;
0322 }
0323 
0324 /*---------------*
0325  * Pause the CD.
0326  *---------------*/
0327 int gen_pause(struct wm_drive *d)
0328 {
0329     return ioctl(d->fd, CDROMPAUSE);
0330 }
0331 
0332 /*-------------------------------------------------*
0333  * Resume playing the CD (assuming it was paused.)
0334  *-------------------------------------------------*/
0335 int gen_resume(struct wm_drive *d)
0336 {
0337     return ioctl(d->fd, CDROMRESUME);
0338 }
0339 
0340 /*--------------*
0341  * Stop the CD.
0342  *--------------*/
0343 int gen_stop(struct wm_drive *d)
0344 {
0345     return ioctl(d->fd, CDROMSTOP);
0346 }
0347 
0348 /*----------------------------------------*
0349  * Eject the current CD, if there is one.
0350  *----------------------------------------*/
0351 int gen_eject(struct wm_drive *d)
0352 {
0353     struct stat stbuf;
0354 #if !defined(BSD_MOUNTTEST)
0355     struct ustat ust;
0356 #else
0357     struct mntent *mnt;
0358     FILE *fp;
0359 #endif
0360 
0361     wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "ejecting?\n");
0362 
0363     if(fstat(d->fd, &stbuf) != 0) {
0364         wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS, "that weird fstat() thingy\n");
0365         return -2;
0366     }
0367 
0368   /* Is this a mounted filesystem? */
0369 #if !defined(BSD_MOUNTTEST)
0370     if (ustat(stbuf.st_rdev, &ust) == 0)
0371         return -3;
0372 #else
0373     /*
0374     * This is the same test as in the WorkBone interface.
0375     * I should eliminate it there, because there is no need
0376     * for it in the UI
0377     */
0378     /* check if drive is mounted (from Mark Buckaway's cdplayer code) */
0379     /* Changed it again (look at XPLAYCD from ????                    */
0380     /* It's better to check the device name rather than one device is */
0381     /* mounted as iso9660. That prevents "no playing" if you have more*/
0382     /* than one CD-ROM, and one of them is mounted, but it's not the  */
0383     /* audio CD                                              -dirk    */
0384     if((fp = setmntent (MOUNTED, "r")) == NULL) {
0385         wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS, "Could not open %s: %s\n",
0386             MOUNTED, strerror (errno));
0387         return -3;
0388     }
0389 
0390     while((mnt = getmntent (fp)) != NULL) {
0391         if(strcmp (mnt->mnt_fsname, d->cd_device) == 0) {
0392             wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS,
0393                 "CDROM already mounted (according to mtab). Operation aborted.\n");
0394             endmntent (fp);
0395             return -3;
0396         }
0397     }
0398     endmntent (fp);
0399 #endif /* BSD_MOUNTTEST */
0400 
0401     ioctl(d->fd, CDROM_LOCKDOOR, 0);
0402 
0403     if(ioctl(d->fd, CDROMEJECT)) {
0404         wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS, "eject failed (%s).\n", strerror(errno));
0405         return -1;
0406     }
0407 
0408     /*------------------
0409     * Things in "foobar_one" are left over from 1.4b3
0410     * I put them here for further observation. In 1.4b3, however,
0411     * that workaround didn't help at least for /dev/sbpcd
0412     * (The tray closed just after ejecting because re-opening the
0413     * device causes the tray to close)
0414     *------------------*/
0415 #ifdef foobar_one
0416     extern int intermittent_dev
0417     /*
0418     * Some drives (drivers?) won't recognize a new CD if we leave the
0419     * device open.
0420     */
0421     if(intermittent_dev)
0422         d->proto.close(d);
0423 #endif
0424 
0425     return 0;
0426 }
0427 
0428 int gen_closetray(struct wm_drive *d)
0429 {
0430 #ifdef CDROMCLOSETRAY
0431     wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS, "CDROMCLOSETRAY closing tray...\n");
0432     return ioctl(d->fd, CDROMCLOSETRAY);
0433 #else
0434     return -1;
0435 #endif /* CDROMCLOSETRAY */
0436 }
0437 
0438 static int min_volume = 0, max_volume = 255;
0439 /*------------------------------------------------------------------------*
0440  * scale_volume(vol, max)
0441  *
0442  * Return a volume value suitable for passing to the CD-ROM drive.  "vol"
0443  * is a volume slider setting; "max" is the slider's maximum value.
0444  * This is not used if sound card support is enabled.
0445  *
0446  *------------------------------------------------------------------------*/
0447 static int scale_volume(int vol, int max)
0448 {
0449 #ifdef CURVED_VOLUME
0450     return ((max * max - (max - vol) * (max - vol)) *
0451     track_info_status(max_volume - min_volume) / (max * max) + min_volume);
0452 #else
0453     return ((vol * (max_volume - min_volume)) / max + min_volume);
0454 #endif
0455 }
0456 
0457 static int unscale_volume(int vol, int max)
0458 {
0459 #ifdef CURVED_VOLUME
0460     /* FIXME do it simpler */
0461     int tmp = (((max_volume - min_volume - vol) * max * max) - (vol + min_volume));
0462     return max - sqrt((tmp/(max_volume - min_volume)));
0463 #else
0464     return (((vol - min_volume) * max) / (max_volume - min_volume));
0465 #endif
0466 }
0467 
0468 /*---------------------------------------------------------------------*
0469  * Set the volume level for the left and right channels.  Their values
0470  * range from 0 to 100.
0471  *---------------------------------------------------------------------*/
0472 int gen_set_volume(struct wm_drive *d, int left, int right)
0473 {
0474     struct cdrom_volctrl v;
0475 
0476     v.channel0 = v.channel2 = left < 0 ? 0 : left > 255 ? 255 : left;
0477     v.channel1 = v.channel3 = right < 0 ? 0 : right > 255 ? 255 : right;
0478 
0479     return (ioctl(d->fd, CDROMVOLCTRL, &v));
0480 }
0481 
0482 /*---------------------------------------------------------------------*
0483  * Read the volume from the drive, if available.  Each channel
0484  * ranges from 0 to 100, with -1 indicating data not available.
0485  *---------------------------------------------------------------------*/
0486 int gen_get_volume(struct wm_drive *d, int *left, int *right)
0487 {
0488     struct cdrom_volctrl v;
0489 
0490 #if defined(CDROMVOLREAD)
0491     if(!ioctl(d->fd, CDROMVOLREAD, &v)) {
0492         *left = (v.channel0 + v.channel2)/2;
0493         *right = (v.channel1 + v.channel3)/2;
0494     } else
0495 #endif
0496         /* Suns, HPs, Linux, NEWS can't read the volume; oh well */
0497         *left = *right = -1;
0498 
0499     return 0;
0500 }
0501 
0502 int gen_scale_volume(int *left, int *right)
0503 {
0504     /* Adjust the volume to make up for the CD-ROM drive's weirdness. */
0505     *left = scale_volume(*left, 100);
0506     *right = scale_volume(*right, 100);
0507 
0508     return 0;
0509 }
0510 
0511 int gen_unscale_volume(int *left, int *right)
0512 {
0513     *left = unscale_volume(*left, 100);
0514     *right = unscale_volume(*right, 100);
0515 
0516     return 0;
0517 }
0518 
0519 /*---------------------------------------------*
0520  * Send an arbitrary SCSI command to a device.
0521  *---------------------------------------------*/
0522 int gen_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
0523     void *retbuf, int retbuflen, int getreply)
0524 {
0525     int ret;
0526 #ifdef LINUX_SCSI_PASSTHROUGH
0527 
0528     char *cmd;
0529     int cmdsize;
0530 
0531     cmdsize = 2 * sizeof(int);
0532     if(retbuf) {
0533         if (getreply)
0534             cmdsize += max(cdblen, retbuflen);
0535         else
0536             cmdsize += (cdblen + retbuflen);
0537     } else {
0538         cmdsize += cdblen;
0539     }
0540 
0541     cmd = malloc(cmdsize);
0542     if(cmd == NULL) {
0543         return -ENOMEM;
0544     }
0545     ((int*)cmd)[0] = cdblen + ((retbuf && !getreply) ? retbuflen : 0);
0546     ((int*)cmd)[1] = ((retbuf && getreply) ? retbuflen : 0);
0547 
0548     memcpy(cmd + 2*sizeof(int), cdb, cdblen);
0549     if(retbuf && !getreply)
0550         memcpy(cmd + 2*sizeof(int) + cdblen, retbuf, retbuflen);
0551 
0552     if(ioctl(d->fd, SCSI_IOCTL_SEND_COMMAND, cmd)) {
0553         wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "%s: ioctl(SCSI_IOCTL_SEND_COMMAND) failure\n", __FILE__);
0554         wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "command buffer is:\n");
0555         wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "%02x %02x %02x %02x %02x %02x\n",
0556         cmd[0],  cmd[1],  cmd[2],  cmd[3],  cmd[4],  cmd[5]);
0557         free(cmd);
0558         return -1;
0559     }
0560 
0561     if(retbuf && getreply)
0562         memcpy(retbuf, cmd + 2*sizeof(int), retbuflen);
0563 
0564     free(cmd);
0565     return 0;
0566 
0567 #else /* Linux SCSI passthrough*/
0568 /*----------------------------------------*
0569  * send packet over cdrom interface
0570  * kernel >= 2.2.16
0571  *----------------------------------------*/
0572 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,2,15))
0573 
0574     struct cdrom_generic_command cdc;
0575     struct request_sense sense;
0576     int capability;
0577 
0578     wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wm_scsi over CDROM_SEND_PACKET entered\n");
0579 
0580     capability = ioctl(d->fd, CDROM_GET_CAPABILITY);
0581 
0582     if(!(capability & CDC_GENERIC_PACKET)) {
0583         wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS,
0584             "your CDROM or/and kernel does not support CDC_GENERIC_PACKET ...\n");
0585         return -1;
0586     }
0587 
0588     memset(&cdc, 0, sizeof(struct cdrom_generic_command));
0589     memset(&sense, 0, sizeof(struct request_sense));
0590 
0591     memcpy(cdc.cmd, cdb, cdblen);
0592 
0593     cdc.buffer = retbuf;
0594     cdc.buflen = retbuflen;
0595     cdc.stat = 0;
0596     cdc.sense = &sense;
0597     cdc.data_direction = getreply?CGC_DATA_READ:CGC_DATA_WRITE;
0598 
0599     /* sendpacket_over_cdrom_interface() */
0600     if((ret = ioctl(d->fd, CDROM_SEND_PACKET, &cdc)))
0601         wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS,
0602             "ERROR: CDROM_SEND_PACKET %s\n", strerror(errno));
0603     return ret;
0604 #endif /* CDROM_SEND_PACKET */
0605     wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS,
0606         "ERROR: this binary was compiled without CDROM GENERIC PACKET SUPPORT. kernel version < 2.2.16?\n");
0607     wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS,
0608         "ERROR: if you have a SCSI CDROM, rebuild it with a #define LINUX_SCSI_PASSTHROUGH\n");
0609     return -1;
0610 #endif
0611 }
0612 
0613 #if 0
0614 #define CDROM_LBA         0x01   /* "logical block": first frame is #0 */
0615 #define CDROMREADAUDIO    0x530e /* (struct cdrom_read_audio) */
0616 #define CD_MSF_OFFSET     150    /* MSF numbering offset of first frame */
0617 
0618 /* This struct is used by the CDROMREADAUDIO ioctl */
0619 struct cdrom_read_audio
0620 {
0621     union {
0622         struct {
0623             unsigned char minute;
0624             unsigned char second;
0625             unsigned char frame;
0626         } msf;
0627         signed int          lba;
0628     } addr; /* frame address */
0629     unsigned char addr_format;      /* CDROM_LBA or CDROM_MSF */
0630     signed int nframes;           /* number of 2352-byte-frames to read at once */
0631     unsigned char *buf;      /* frame buffer (size: nframes*2352 bytes) */
0632 };
0633 
0634 #define CDDABLKSIZE 2352
0635 
0636 #endif
0637 
0638 int gen_cdda_init(struct wm_drive *d)
0639 {
0640     return 0;
0641 }
0642 
0643 /*
0644  * Initialize the CDDA data buffer and open the appropriate device.
0645  *
0646  */
0647 int gen_cdda_open(struct wm_drive *d)
0648 {
0649     int i;
0650     struct cdrom_read_audio cdda;
0651 
0652     if (d->fd > -1)
0653         return -1;
0654 
0655     for (i = 0; i < d->numblocks; i++) {
0656         d->blocks[i].buflen = d->frames_at_once * CD_FRAMESIZE_RAW;
0657         d->blocks[i].buf = malloc(d->blocks[i].buflen);
0658         if (!d->blocks[i].buf) {
0659             ERRORLOG("plat_cdda_open: ENOMEM\n");
0660             return -ENOMEM;
0661         }
0662     }
0663 
0664     cdda.addr_format = CDROM_LBA;
0665     cdda.addr.lba = 200;
0666     cdda.nframes = 1;
0667     cdda.buf = (unsigned char *)d->blocks[0].buf;
0668 
0669     d->status = WM_CDM_STOPPED;
0670     if((ioctl(d->fd, CDROMREADAUDIO, &cdda) < 0)) {
0671         if (errno == ENXIO) {
0672             /* CD ejected! */
0673             d->status = WM_CDM_EJECTED;
0674         } else {
0675             /* Sometimes it fails once, dunno why */
0676             d->status = WM_CDM_CDDAERROR;
0677         }
0678     } else {
0679         d->status = WM_CDM_UNKNOWN;
0680     }
0681 
0682 
0683     return 0;
0684 }
0685 
0686 /*
0687  * Read some blocks from the CD.  Stop if we hit the end of the current region.
0688  *
0689  * Returns number of bytes read, -1 on error, 0 if stopped for a benign reason.
0690  */
0691 int gen_cdda_read(struct wm_drive *d, struct wm_cdda_block *block)
0692 {
0693     struct cdrom_read_audio cdda;
0694 
0695     if (d->fd < 0)
0696         return -1;
0697 
0698     /* Hit the end of the CD, probably. */
0699     if (d->current_position >= d->ending_position) {
0700         block->status = WM_CDM_TRACK_DONE;
0701         return 0;
0702     }
0703 
0704     cdda.addr_format = CDROM_LBA;
0705     cdda.addr.lba = d->current_position - CD_MSF_OFFSET;
0706     if (d->ending_position && d->current_position + d->frames_at_once > d->ending_position)
0707         cdda.nframes = d->ending_position - d->current_position;
0708     else
0709         cdda.nframes = d->frames_at_once;
0710 
0711     cdda.buf = (unsigned char*)block->buf;
0712 
0713     if (ioctl(d->fd, CDROMREADAUDIO, &cdda) < 0) {
0714         if (errno == ENXIO) {
0715             /* CD ejected! */
0716             block->status = WM_CDM_EJECTED;
0717             return 0;
0718         } else {
0719             /* Sometimes it fails once, dunno why */
0720             block->status = WM_CDM_CDDAERROR;
0721             return 0;
0722         }
0723     }
0724 
0725     block->track =  -1;
0726     block->index =  0;
0727     block->frame  = d->current_position;
0728     block->status = WM_CDM_PLAYING;
0729     block->buflen = cdda.nframes * CD_FRAMESIZE_RAW;
0730 
0731     d->current_position = d->current_position + cdda.nframes;
0732 
0733     return block->buflen;
0734 }
0735 
0736 /*
0737  * Close the CD-ROM device in preparation for exiting.
0738  */
0739 int gen_cdda_close(struct wm_drive *d)
0740 {
0741     int i;
0742 
0743     if (d->fd < 0)
0744         return -1;
0745 
0746     for (i = 0; i < d->numblocks; i++) {
0747         free(d->blocks[i].buf);
0748         d->blocks[i].buf = 0;
0749         d->blocks[i].buflen = 0;
0750     }
0751 
0752     return 0;
0753 }
0754 
0755 #endif /* __linux__ */