Close

Digging deep inside Linux drivers

A project log for Using old CD-drive as a music player

Trying to build a simple CD-player from an old CD-drive. I want it to read audio CDs, have button controls and an audio output.

jurc192jurc192 09/10/2017 at 21:460 Comments

My theory is: if my computer is communicating to the CD drive, so can I. And thanks to the free & open source community, I can see how "he" does it.

I guess reading about some kind of Linux CD drivers would be a good start. I refreshed my C programming language knowledge, by writing some simple programs and started reading about Linux kernel and drivers. It was a huge and interesting "detour", but had nothing to do with this project. Crap!

One of the simplest task I could think of was to eject the cd tray. Thanks to this awesome Introduction to the Linux CDROM Interface it was done in no time.

/* eject.c
** Copyright Paul Dwerryhouse, 1997-2004
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/cdrom.h>
#define CDDEVICE "/dev/cdrom"           /* CDROM device */
int main(int argc,char **argv)
{
   int cdrom;                           /* CDROM device file descriptor */
   /* Open the CDROM device. The linux/cdrom.h header file specifies that
   ** it must be given the O_NONBLOCK flag when opening. My tests showed
   ** that if this isn't done, this program will not work. 
   */
   if ((cdrom = open(CDDEVICE,O_RDONLY | O_NONBLOCK)) < 0) {
        perror("open");
        exit(1);
   }
   /* Use ioctl to send the CDROMEJECT command to the device
   */
   if (ioctl(cdrom,CDROMEJECT,0)<0) {
        perror("ioctl");
        exit(1);
   }
   close(cdrom);
}

The main things here are ioctl and the cdrom header file.

IOCTL (Input Output ConTroL) is "a kind of device-specific system call". Every device has it's own ioctl commands. Longer explanation is in "The Linux Kernel Module Programming Guide".

And the cdrom header file can be found in /usr/include/linux/cdrom.h . It contains commands and definitions for accessing CD-drive. I got mine from Linus Torvald's github - linux kernel source tree. Actually there are two cdrom.h files in the kernel source. The one I found (more) useful is in /include/uapi/linux/cdrom.h and the other one is in /include/linux/cdrom.h . Confusing at first, but here's the answer why.

My friend Otto joined me, and we tried sending various ioctl commands. Sending CDROMSTART and CDROMSTOP commands was succesfull, but we couldn't tell what they are doing. Commands CDROM-PAUSE or -PLAYMSF for example didn't work on our device -> ioctl: operation not supported.

Command CDROMSUBCHNL (read sub-channel data) was interesting, since it outputs "current" track, minute, second and frame (1sec = 75 frames).
After playing a song from cd and stopping (and exiting the audio program) the same command gave different results. Exactly where we stopped the playback.

Next challenge was to read audio from the CD. We're guessing that -PLAYTRKIND (Play track / index addressing) and -PLAYMSF (Play track / minute-second-frame addresing) refer to playing audio from those analog audio outputs on the CD-drives themselves. Because those operations were not supported on our modern CD-drive without analog audio output.

Anyway, we got to read the audio with -READAUDIO command!
We put  the returned output to a file (.wav) and played that with aplay cmd audio player. Btw the flag -f cd gives you the correct output, but playnig around with different bitrate and channel number might produce funny results.

After playing around we tried to go one level deeper into the kernel. I really wanted to get to the switch statement that would recieve those CDROM-xy commands we send via ioctl function. Well, it wasn't easy to find it but it is in [kernel source]/drivers/cdrom/cdrom.c at line 3331. We followed function calls a bit more, but soon we got lost in all the files and stopped at this point.

switch (cmd) {
    case CDROMMULTISESSION:
        return cdrom_ioctl_multisession(cdi, argp);
    case CDROMEJECT:
        return cdrom_ioctl_eject(cdi);
    case CDROMCLOSETRAY:
        return cdrom_ioctl_closetray(cdi);
    case CDROMEJECT_SW:
        return cdrom_ioctl_eject_sw(cdi, arg);
// And so on...

I guess finding information about the cd-drive controls from linux drivers was not a very productive idea. But it was fun and we learned a lot, studying and reading the linux kernel.

Discussions