1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/drivers/cd_linux.c Fri Nov 16 23:51:23 2007 +0000
1.5 + * $Id: linux.c,v 1.9 2007-11-04 05:07:49 nkeynes Exp $
1.7 + * Linux cd-rom device driver.
1.9 + * Copyright (c) 2005 Nathan Keynes.
1.11 + * This program is free software; you can redistribute it and/or modify
1.12 + * it under the terms of the GNU General Public License as published by
1.13 + * the Free Software Foundation; either version 2 of the License, or
1.14 + * (at your option) any later version.
1.16 + * This program is distributed in the hope that it will be useful,
1.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.19 + * GNU General Public License for more details.
1.21 +#include <unistd.h>
1.22 +#include <stdlib.h>
1.24 +#include <string.h>
1.26 +#include <linux/cdrom.h>
1.27 +#include <sys/stat.h>
1.28 +#include <sys/ioctl.h>
1.32 +#include "gdrom/gdrom.h"
1.33 +#include "gdrom/packet.h"
1.36 +#define MAXTOCENTRIES 600 /* This is a fairly generous overestimate really */
1.37 +#define MAXTOCSIZE 4 + (MAXTOCENTRIES*11)
1.38 +#define MAX_SECTORS_PER_CALL 1
1.40 +#define MSFTOLBA( m,s,f ) (f + (s*CD_FRAMES) + (m*CD_FRAMES*CD_SECS))
1.42 +static uint32_t inline lbatomsf( uint32_t lba ) {
1.43 + union cdrom_addr addr;
1.44 + lba = lba + CD_MSF_OFFSET;
1.45 + addr.msf.frame = lba % CD_FRAMES;
1.46 + int seconds = lba / CD_FRAMES;
1.47 + addr.msf.second = seconds % CD_SECS;
1.48 + addr.msf.minute = seconds / CD_SECS;
1.52 +#define LBATOMSF( lba ) lbatomsf(lba)
1.55 +static gboolean linux_image_is_valid( FILE *f );
1.56 +static gdrom_disc_t linux_open_device( const gchar *filename, FILE *f );
1.57 +static gdrom_error_t linux_read_disc_toc( gdrom_image_t disc );
1.58 +static gdrom_error_t linux_read_sector( gdrom_disc_t disc, uint32_t sector,
1.59 + int mode, unsigned char *buf, uint32_t *length );
1.60 +static gdrom_error_t linux_send_command( int fd, char *cmd, unsigned char *buffer, size_t *buflen,
1.62 +static int linux_drive_status( gdrom_disc_t disc );
1.64 +struct gdrom_image_class cdrom_device_class = { "Linux", NULL,
1.65 + linux_image_is_valid, linux_open_device };
1.66 +GList *gdrom_get_native_devices(void)
1.68 + GList *list = NULL;
1.69 + struct fstab *ent;
1.72 + while( (ent = getfsent()) != NULL ) {
1.73 + if( (stat(ent->fs_spec, &st) != -1) &&
1.74 + S_ISBLK(st.st_mode) ) {
1.75 + /* Got a valid block device - is it a CDROM? */
1.76 + int fd = open(ent->fs_spec, O_RDONLY|O_NONBLOCK);
1.79 + int caps = ioctl(fd, CDROM_GET_CAPABILITY);
1.80 + if( caps != -1 ) {
1.81 + /* Appears to support CDROM functions */
1.82 + list = g_list_append( list, g_strdup(ent->fs_spec) );
1.90 +static gboolean linux_image_is_valid( FILE *f )
1.93 + struct cdrom_tochdr tochdr;
1.95 + if( fstat(fileno(f), &st) == -1 ) {
1.96 + return FALSE; /* can't stat device? */
1.98 + if( !S_ISBLK(st.st_mode) ) {
1.99 + return FALSE; /* Not a block device */
1.102 + if( ioctl(fileno(f), CDROMREADTOCHDR, &tochdr) == -1 ) {
1.103 + /* Quick check that this is really a CD */
1.110 +static gdrom_disc_t linux_open_device( const gchar *filename, FILE *f )
1.112 + gdrom_disc_t disc;
1.114 + disc = gdrom_image_new(filename, f);
1.115 + if( disc == NULL ) {
1.116 + ERROR("Unable to allocate memory!");
1.120 + gdrom_error_t status = linux_read_disc_toc( (gdrom_image_t)disc );
1.121 + if( status != 0 ) {
1.122 + gdrom_image_destroy_no_close(disc);
1.123 + if( status == 0xFFFF ) {
1.124 + ERROR("Unable to load disc table of contents (%s)", strerror(errno));
1.126 + ERROR("Unable to load disc table of contents (sense %d,%d)",
1.127 + status &0xFF, status >> 8 );
1.131 + disc->read_sector = linux_read_sector;
1.132 + disc->drive_status = linux_drive_status;
1.133 + ((gdrom_image_t)disc)->disc_type = IDE_DISC_CDROM;
1.137 +static int linux_drive_status( gdrom_disc_t disc )
1.139 + int fd = fileno(((gdrom_image_t)disc)->file);
1.140 + int status = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
1.141 + if( status == CDS_DISC_OK ) {
1.142 + status = ioctl(fd, CDROM_MEDIA_CHANGED, CDSL_CURRENT);
1.143 + if( status != 0 ) {
1.144 + linux_read_disc_toc( (gdrom_image_t)disc);
1.146 + return ((gdrom_image_t)disc)->disc_type | IDE_DISC_READY;
1.148 + return IDE_DISC_NONE;
1.152 + * Read the full table of contents into the disc from the device.
1.154 +static gdrom_error_t linux_read_disc_toc( gdrom_image_t disc )
1.156 + int fd = fileno(disc->file);
1.157 + unsigned char buf[MAXTOCSIZE];
1.158 + size_t buflen = sizeof(buf);
1.159 + char cmd[12] = { 0x43, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1.161 + cmd[7] = (sizeof(buf))>>8;
1.162 + cmd[8] = (sizeof(buf))&0xFF;
1.163 + memset( buf, 0, sizeof(buf) );
1.164 + gdrom_error_t status = linux_send_command( fd, cmd, buf, &buflen, CGC_DATA_READ );
1.165 + if( status != 0 ) {
1.169 + int max_track = 0;
1.170 + int last_track = -1;
1.171 + int leadout = -1;
1.172 + int len = (buf[0] << 8) | buf[1];
1.173 + int session_type = GDROM_MODE1;
1.175 + for( i = 4; i<len; i+=11 ) {
1.176 + int session = buf[i];
1.177 + int adr = buf[i+1] >> 4;
1.178 + int point = buf[i+3];
1.179 + if( adr == 0x01 && point > 0 && point < 100 ) {
1.181 + int trackno = point-1;
1.182 + if( point > max_track ) {
1.183 + max_track = point;
1.185 + disc->track[trackno].flags = (buf[i+1] & 0x0F) << 4;
1.186 + disc->track[trackno].session = session - 1;
1.187 + disc->track[trackno].lba = MSFTOLBA(buf[i+8],buf[i+9],buf[i+10]);
1.188 + if( disc->track[trackno].flags & TRACK_DATA ) {
1.189 + disc->track[trackno].mode = GDROM_MODE1;
1.191 + disc->track[trackno].mode = GDROM_CDDA;
1.193 + if( last_track != -1 ) {
1.194 + disc->track[last_track].sector_count = disc->track[trackno].lba -
1.195 + disc->track[last_track].lba;
1.197 + last_track = trackno;
1.198 + } else switch( (adr << 8) | point ) {
1.199 + case 0x1A0: /* session info */
1.200 + if( buf[i+9] == 0x20 ) {
1.201 + session_type = GDROM_MODE2;
1.203 + session_type = GDROM_MODE1;
1.205 + case 0x1A2: /* leadout */
1.206 + leadout = MSFTOLBA(buf[i+8], buf[i+9], buf[i+10]);
1.210 + disc->track_count = max_track;
1.212 + if( leadout != -1 && last_track != -1 ) {
1.213 + disc->track[last_track].sector_count = leadout - disc->track[last_track].lba;
1.218 + gdrom_error_t linux_play_audio( gdrom_disc_t disc, uint32_t lba, uint32_t endlba )
1.220 + int fd = fileno( ((gdrom_image_t)disc)->file );
1.221 + uint32_t real_sector = lba - CD_MSF_OFFSET;
1.222 + uint32_t length = endlba - lba;
1.223 + uint32_t buflen = 0;
1.224 + char cmd[12] = { 0xA5, 0,0,0, 0,0,0,0, 0,0,0,0 };
1.225 + cmd[2] = (real_sector >> 24) & 0xFF;
1.226 + cmd[3] = (real_sector >> 16) & 0xFF;
1.227 + cmd[4] = (real_sector >> 8) & 0xFF;
1.228 + cmd[5] = real_sector & 0xFF;
1.229 + cmd[6] = (length >> 24) & 0xFF;
1.230 + cmd[7] = (length >> 16) & 0xFF;
1.231 + cmd[8] = (length >> 8) & 0xFF;
1.232 + cmd[9] = length & 0xFF;
1.234 + return linux_send_command( fd, cmd, NULL, &buflen, CGC_DATA_NONE );
1.237 +gdrom_error_t linux_stop_audio( gdrom_disc_t disc )
1.239 + int fd = fileno( ((gdrom_image_t)disc)->file );
1.240 + uint32_t buflen = 0;
1.241 + char cmd[12] = {0x4E,0,0,0, 0,0,0,0, 0,0,0,0};
1.242 + return linux_send_command( fd, cmd, NULL, &buflen, CGC_DATA_NONE );
1.245 +static gdrom_error_t linux_read_sector( gdrom_disc_t disc, uint32_t sector,
1.246 + int mode, unsigned char *buf, uint32_t *length )
1.248 + gdrom_image_t image = (gdrom_image_t)disc;
1.249 + int fd = fileno(image->file);
1.250 + uint32_t real_sector = sector - CD_MSF_OFFSET;
1.251 + uint32_t sector_size = MAX_SECTOR_SIZE;
1.252 + char cmd[12] = { 0xBE, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1.254 + cmd[1] = (mode & 0x0E) << 1;
1.255 + cmd[2] = (real_sector >> 24) & 0xFF;
1.256 + cmd[3] = (real_sector >> 16) & 0xFF;
1.257 + cmd[4] = (real_sector >> 8) & 0xFF;
1.258 + cmd[5] = real_sector & 0xFF;
1.264 + gdrom_error_t status = linux_send_command( fd, cmd, buf, §or_size, CGC_DATA_READ );
1.265 + if( status != 0 ) {
1.273 + * Send a packet command to the device and wait for a response.
1.274 + * @return 0 on success, -1 on an operating system error, or a sense error
1.275 + * code on a device error.
1.277 +static gdrom_error_t linux_send_command( int fd, char *cmd, unsigned char *buffer, size_t *buflen,
1.280 + struct request_sense sense;
1.281 + struct cdrom_generic_command cgc;
1.283 + memset( &cgc, 0, sizeof(cgc) );
1.284 + memset( &sense, 0, sizeof(sense) );
1.285 + memcpy( cgc.cmd, cmd, 12 );
1.286 + cgc.buffer = buffer;
1.287 + cgc.buflen = *buflen;
1.288 + cgc.sense = &sense;
1.289 + cgc.data_direction = direction;
1.291 + if( ioctl(fd, CDROM_SEND_PACKET, &cgc) == -1 ) {
1.292 + if( sense.sense_key == 0 ) {
1.295 + /* TODO: Map newer codes back to the ones used by the gd-rom. */
1.296 + return sense.sense_key | (sense.asc<<8);
1.299 + *buflen = cgc.buflen;