Warning, file /multimedia/libkcompactdisc/src/wmlib/plat_linux.c was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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__ */