在Debian上访问音频CD

這是一個為了進行調查所創建的示範程式。由於在Debian上很少有能夠訪問音頻光碟的示範程式,所以我將其放在這裡。我已經在Debian 9.5上進行了編譯和操作確認。

#include <stdio.h>              // printf, perror
#include <fcntl.h>              // open
#include <unistd.h>             // close, write
#include <sys/ioctl.h>          // ioctl
#include <string.h>             // memset, memcpy
#include <stdlib.h>             // malloc, free
#include <linux/cdrom.h>        //
// ----------------------------------------------------------------------------
#define LBA2MIN(l)      (__u8)((l / CD_FRAMES) / CD_SECS)
#define LBA2SEC(l)      (__u8)((l / CD_FRAMES) % CD_SECS)
#define LBA2FRAME(l)    (__u8)(l % CD_FRAMES)
#define MSF2LBA(m,s,f)  (int)((m * CD_SECS + s) * CD_FRAMES + f - CD_MSF_OFFSET)
// ----------------------------------------------------------------------------
int cdrom_open(const char *pathname, int flags, mode_t mode)
{
    int fd;

    if ((fd = open(pathname, flags, mode)) < 0)
        perror("cdrom_open: open");

    return fd;
}

// ----------------------------------------------------------------------------
int cdrom_close(int *fd)
{
    int ret;

    if ((ret = close(*fd)) < 0)
        perror("cdrom_close: close");

    return ret;
}

// ----------------------------------------------------------------------------
off_t cdrom_lseek(int *fd, off_t offset, int whence)
{
    off_t ret;

    if ((ret = lseek(*fd, offset, whence)) < 0)
        perror("cdrom_lseek: lseek");

    return ret;
}

// ----------------------------------------------------------------------------
ssize_t cdrom_read(int *fd, void *buf, size_t count)
{
    ssize_t ret;

    if ((ret = read(*fd, buf, count)) < 0)
        perror("cdrom_read: read");

    return ret;
}

// ----------------------------------------------------------------------------
ssize_t cdrom_write(int *fd, const void *buf, size_t count)
{
    ssize_t ret;

    if ((ret = write(*fd, buf, count)) < 0)
        perror("cdrom_write: write");

    return ret;
}

// ----------------------------------------------------------------------------
int cdrom_ioctl(int *fd, unsigned long request, void *argp)
{
    int ret;

    if ((ret = ioctl(*fd, request, argp)) < 0)
        perror("cdrom_ioctl: ioctl");

    return ret;
}

// ----------------------------------------------------------------------------
int cdrom_read_tochdr(int *fd, struct cdrom_tochdr *header)
{
    int ret;

    memset(header, 0, sizeof(struct cdrom_tochdr));
    if ((ret = ioctl(*fd, CDROMREADTOCHDR, header)) < 0)
        perror("cdrom_read_tochdr: ioctl");

    return ret;
}

// ----------------------------------------------------------------------------
int cdrom_read_tocentry(int *fd, struct cdrom_tocentry *entry)
{
    int ret;
    struct cdrom_tocentry p;

    memset(&p, 0, sizeof(struct cdrom_tocentry));
    p.cdte_format = entry->cdte_format;
    p.cdte_track = entry->cdte_track;
    if ((ret = ioctl(*fd, CDROMREADTOCENTRY, &p)) < 0)
        perror("cdrom_read_tocentry: ioctl");
    memcpy(entry, &p, sizeof(struct cdrom_tocentry));

    return ret;
}

// ----------------------------------------------------------------------------
int cdrom_reset(int *fd)
{
    int ret;

    if ((ret = ioctl(*fd, CDROMRESET, 0)) < 0)
        perror("cdrom_reset: ioctl");

    return ret;
}

// ----------------------------------------------------------------------------
int cdrom_select_speed(int *fd, int speed)
{
    int ret;

    if (!speed)
        speed = 0xffff;         // set to max
    else
        speed *= 177;           // Nx to kbytes/s

    if ((ret = ioctl(*fd, CDROM_SELECT_SPEED, speed)) < 0)
        perror("cdrom_select_speed: ioctl");

    return ret;
}

// ----------------------------------------------------------------------------
int cdrom_get_mcn(int *fd, struct cdrom_mcn *mcn)
{
    int ret = 0;

    memset(mcn, 0, sizeof(struct cdrom_mcn));
    if ((ret = ioctl(*fd, CDROM_GET_MCN, mcn)) < 0)
        perror("cdrom_get_mcn: ioctl");

    return ret;
}

// ----------------------------------------------------------------------------
int cdrom_read_audio(int *fd, struct cdrom_read_audio *ra)
{
    int ret = 0;

    memset(ra->buf, 0, (ra->nframes * CD_FRAMESIZE_RAW));
    if ((ret = ioctl(*fd, CDROMREADAUDIO, ra)) < 0)
        perror("cdrom_read_audio: ioctl");

    return ret;
}

// ----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
    int ret = 0;
    int ifd, ofd;
    char *ipathname = "/dev/cdrom";
    char *opathname = "./dump.bin";
    struct cdrom_tochdr header;
    struct cdrom_tocentry entry[99];
    struct cdrom_mcn mcn;
    struct cdrom_read_audio ra;
    int i, trk, lba0, lba1;
// --- cdrom_open -------------------------------------------------------------
    if ((ifd = cdrom_open(ipathname, O_RDONLY | O_NONBLOCK, 0)) < 0)
        return -1;
// --- cdrom_reset ------------------------------------------------------------
/*  if (cdrom_reset(&ifd) < 0) {
        cdrom_close(&ifd);
        return -1;
    }*/
// --- cdrom_select_speed -----------------------------------------------------
    if (cdrom_select_speed(&ifd, 0) < 0) {
        cdrom_close(&ifd);
        return -1;
    }
// --- cdrom_read_tochdr ------------------------------------------------------
    if (cdrom_read_tochdr(&ifd, &header) < 0) {
        cdrom_close(&ifd);
        return -1;
    }
// --- cdrom_get_mcn ----------------------------------------------------------
    if (cdrom_get_mcn(&ifd, &mcn) < 0) {
        cdrom_close(&ifd);
        return -1;
    }
// --- cdrom_read_tocentry ----------------------------------------------------
    for (i = 0, trk = header.cdth_trk0; trk <= header.cdth_trk1 + 1; i++, trk++) {
        if (trk >= header.cdth_trk0 && trk <= header.cdth_trk1)
            entry[i].cdte_track = trk;
        else if (trk > header.cdth_trk1)
            entry[i].cdte_track = CDROM_LEADOUT;
        entry[i].cdte_format = CDROM_LBA;
        if (cdrom_read_tocentry(&ifd, &entry[i]) < 0) {
            cdrom_close(&ifd);
            return -1;
        }
    }
// --- cdrom_read_audio -------------------------------------------------------
#if 0
    if ((ofd = cdrom_open(opathname, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR)) < 0) {
        ret = -1;
    } else {
        ra.addr_format = CDROM_LBA; // CDROM_LBA or CDROM_MSF
        ra.nframes = 50;        // number of 2352-byte-frames to read at once
        // frame buffer (size: nframes*2352 bytes)
        if ((ra.buf = malloc(ra.nframes * CD_FRAMESIZE_RAW)) == NULL) {
            perror("malloc:ra.buf");
            ret = -1;
            goto extit_loop2;
        }
        // frame address
        for (i = 0, trk = header.cdth_trk0; trk <= header.cdth_trk1; i++, trk++);
        if (entry[0].cdte_format == CDROM_LBA) {
            lba0 = entry[0].cdte_addr.lba;
            lba1 = entry[i].cdte_addr.lba;
        } else {
            lba0 = MSF2LBA(entry[0].cdte_addr.msf.minute, entry[0].cdte_addr.msf.second, entry[0].cdte_addr.msf.frame);
            lba1 = MSF2LBA(entry[i].cdte_addr.msf.minute, entry[i].cdte_addr.msf.second, entry[i].cdte_addr.msf.frame);
        }
        for (ra.addr.lba = lba0; (ra.addr.lba + ra.nframes) < lba1; ra.addr.lba += ra.nframes) {
            printf("lba = %6d\r", ra.addr.lba);
            if (cdrom_read_audio(&ifd, &ra) < 0) {
                ret = -1;
                goto extit_loop1;
            }
            if (cdrom_write(&ofd, ra.buf, ra.nframes * CD_FRAMESIZE_RAW) < 0) {
                ret = -1;
                goto extit_loop1;
            }
        }
        ra.nframes = 1;
        for (; ra.addr.lba < lba1; ra.addr.lba += ra.nframes) {
            printf("lba = %6d\r", ra.addr.lba);
            if (cdrom_read_audio(&ifd, &ra) < 0) {
                ret = -1;
                goto extit_loop1;
            }
            if (cdrom_write(&ofd, ra.buf, ra.nframes * CD_FRAMESIZE_RAW) < 0) {
                ret = -1;
                goto extit_loop1;
            }
        }
      extit_loop1:
        printf("lba = %6d\n", ra.addr.lba);
        free(ra.buf);
      extit_loop2:
        if ((cdrom_close(&ofd)) < 0)
            ret = -1;
    }
#endif
// --- cdrom_close ------------------------------------------------------------
    if (cdrom_close(&ifd) < 0)
        ret = -1;
// --- debug print ------------------------------------------------------------
    printf("start track=%02u\n", header.cdth_trk0); // start track
    printf("end track  =%02u\n", header.cdth_trk1); // end track
    printf("\n");
    for (i = 0; i < sizeof(mcn.medium_catalog_number); i++)
        printf("mcn[%02d]    =%02x\n", i, mcn.medium_catalog_number[i]);
    printf("\n");
    for (i = 0, trk = header.cdth_trk0; trk <= header.cdth_trk1 + 1; i++, trk++) {
        printf("track      =%02u\n", entry[i].cdte_track);
        printf("adr        =%02x\n", entry[i].cdte_adr);
        printf("ctrl       =%02x\n", entry[i].cdte_ctrl);
        printf("format     =%02x\n", entry[i].cdte_format);
        if (entry[i].cdte_format == CDROM_LBA) {    // CDROM_LBA
            printf("addr lba   =%d\n", entry[i].cdte_addr.lba);
            printf("addr m     =%02u\n", LBA2MIN(entry[i].cdte_addr.lba));
            printf("addr s     =%02u\n", LBA2SEC(entry[i].cdte_addr.lba));
            printf("addr f     =%02u\n", LBA2FRAME(entry[i].cdte_addr.lba));
        } else {                // CDROM_MSF
            printf("addr lba   =%d\n", MSF2LBA(entry[i].cdte_addr.msf.minute, entry[i].cdte_addr.msf.second, entry[i].cdte_addr.msf.frame));
            printf("addr m     =%02u\n", entry[i].cdte_addr.msf.minute);
            printf("addr s     =%02u\n", entry[i].cdte_addr.msf.second);
            printf("addr f     =%02u\n", entry[i].cdte_addr.msf.frame);
        }
        printf("datamode   =%02x\n", entry[i].cdte_datamode);
        printf("\n");
    }
//  printf("%s: %d\n", __FUNCTION__, __LINE__);
// ----------------------------------------------------------------------------
    return ret;
}
PROGRAM     =   cdrom
OBJS        =   cdrom.o
CFLAGS      =   -Os -Wall

all         :   $(PROGRAM)

clean       :;  rm -f *.o *~ $(PROGRAM)

$(PROGRAM)  :   $(OBJS)
                $(CC) $(OBJS) $(LDFLAGS) $(LIBS) -o $(PROGRAM)