Search
lxdream.org :: lxdream/src/gdrom/linux.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/gdrom/linux.c
changeset 259:7c6881790cc2
prev237:6f1a429c9d12
next342:850502f0e8de
author nkeynes
date Fri Dec 29 00:24:43 2006 +0000 (13 years ago)
permissions -rw-r--r--
last change Limit number of sectors read at once (linux driver seems to throw a hissy
fit if you request too many at a time)
view annotate diff log raw
     1 /**
     2  * $Id: linux.c,v 1.2 2006-12-29 00:24:43 nkeynes Exp $
     3  *
     4  * Linux cd-rom device driver
     5  *
     6  * Copyright (c) 2005 Nathan Keynes.
     7  *
     8  * This program is free software; you can redistribute it and/or modify
     9  * it under the terms of the GNU General Public License as published by
    10  * the Free Software Foundation; either version 2 of the License, or
    11  * (at your option) any later version.
    12  *
    13  * This program is distributed in the hope that it will be useful,
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16  * GNU General Public License for more details.
    17  */
    18 #include <stdlib.h>
    19 #include <stdio.h>
    20 #include <string.h>
    21 #include <errno.h>
    22 #include <linux/cdrom.h>
    23 #include <sys/stat.h>
    24 #include <sys/ioctl.h>
    26 #include "gdrom/gdrom.h"
    27 #include "gdrom/packet.h"
    28 #include "dream.h"
    30 #define MAXTOCENTRIES 600  /* This is a fairly generous overestimate really */
    31 #define MAXTOCSIZE 4 + (MAXTOCENTRIES*11)
    32 #define MAX_SECTORS_PER_CALL 32
    34 #define MSFTOLBA( m,s,f ) (f + (s*CD_FRAMES) + (m*CD_FRAMES*CD_SECS))
    36 static uint32_t inline lbatomsf( uint32_t lba ) {
    37     union cdrom_addr addr;
    38     lba = lba + CD_MSF_OFFSET;
    39     addr.msf.frame = lba % CD_FRAMES;
    40     int seconds = lba / CD_FRAMES;
    41     addr.msf.second = seconds % CD_SECS;
    42     addr.msf.minute = seconds / CD_SECS;
    43     return addr.lba;
    44 }
    46 #define LBATOMSF( lba ) lbatomsf(lba)
    49 static gboolean linux_image_is_valid( FILE *f );
    50 static gdrom_disc_t linux_open_device( const gchar *filename, FILE *f );
    51 static gdrom_error_t linux_read_disc_toc( gdrom_disc_t disc );
    52 static gdrom_error_t linux_read_sectors( gdrom_disc_t disc, uint32_t sector,
    53 					 uint32_t sector_count, int mode, char *buf,
    54 					 uint32_t *length );
    55 static gdrom_error_t linux_send_command( int fd, char *cmd, char *buffer, size_t buflen,
    56 					 int direction );
    59 struct gdrom_image_class linux_device_class = { "Linux", NULL,
    60 					     linux_image_is_valid, linux_open_device };
    62 static gboolean linux_image_is_valid( FILE *f )
    63 {
    64     struct stat st;
    65     struct cdrom_tochdr tochdr;
    67     if( fstat(fileno(f), &st) == -1 ) {
    68 	return FALSE; /* can't stat device? */
    69     }
    70     if( !S_ISBLK(st.st_mode) ) {
    71 	return FALSE; /* Not a block device */
    72     }
    74     if( ioctl(fileno(f), CDROMREADTOCHDR, &tochdr) == -1 ) {
    75 	/* Quick check that this is really a CD */
    76 	return FALSE;
    77     }
    79     return TRUE;
    80 }
    82 static gdrom_disc_t linux_open_device( const gchar *filename, FILE *f ) 
    83 {
    84     gdrom_disc_t disc;
    85     int fd = fileno(f);
    87     disc = gdrom_image_new(f);
    88     if( disc == NULL ) {
    89 	ERROR("Unable to allocate memory!");
    90 	return NULL;
    91     }
    93     gdrom_error_t status = linux_read_disc_toc( disc );
    94     if( status != 0 ) {
    95 	free(disc);
    96 	if( status == 0xFFFF ) {
    97 	    ERROR("Unable to load disc table of contents (%s)", strerror(errno));
    98 	} else {
    99 	    ERROR("Unable to load disc table of contents (sense %d,%d)",
   100 		  status &0xFF, status >> 8 );
   101 	}
   102 	return NULL;
   103     }
   104     disc->read_sectors = linux_read_sectors;
   105     disc->disc_type = IDE_DISC_CDROM;
   106     return disc;
   107 }
   109 /**
   110  * Read the full table of contents into the disc from the device.
   111  */
   112 static gdrom_error_t linux_read_disc_toc( gdrom_disc_t disc )
   113 {
   114     int fd = fileno(disc->file);
   115     unsigned char buf[MAXTOCSIZE];
   116     char cmd[12] = { 0x43, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
   118     cmd[7] = (sizeof(buf))>>8;
   119     cmd[8] = (sizeof(buf))&0xFF;
   120     memset( buf, 0, sizeof(buf) );
   121     gdrom_error_t status = linux_send_command( fd, cmd, buf, sizeof(buf), CGC_DATA_READ );
   122     if( status != 0 ) {
   123 	return status;
   124     }
   126     int max_track = 0;
   127     int last_track = -1;
   128     int leadout = -1;
   129     int len = (buf[0] << 8) | buf[1];
   130     int session_type = GDROM_MODE1;
   131     int i;
   132     for( i = 4; i<len; i+=11 ) {
   133 	int session = buf[i];
   134 	int adr = buf[i+1] >> 4;
   135 	int point = buf[i+3];
   136 	if( adr == 0x01 && point > 0 && point < 100 ) {
   137 	    /* Track info */
   138 	    int trackno = point-1;
   139 	    if( point > max_track ) {
   140 		max_track = point;
   141 	    }
   142 	    disc->track[trackno].flags = (buf[i+1] & 0x0F) << 4;
   143 	    disc->track[trackno].session = session - 1;
   144 	    disc->track[trackno].lba = MSFTOLBA(buf[i+8],buf[i+9],buf[i+10]);
   145 	    if( disc->track[trackno].flags & TRACK_DATA ) {
   146 		disc->track[trackno].mode = GDROM_MODE1;
   147 	    } else {
   148 		disc->track[trackno].mode = GDROM_CDDA;
   149 	    }
   150 	    if( last_track != -1 ) {
   151 		disc->track[last_track].sector_count = disc->track[trackno].lba -
   152 		    disc->track[last_track].lba;
   153 	    }
   154 	    last_track = trackno;
   155 	} else switch( (adr << 8) | point ) {
   156 	case 0x1A0: /* session info */
   157 	    if( buf[i+9] == 0x20 ) {
   158 		session_type = GDROM_MODE2;
   159 	    } else {
   160 		session_type = GDROM_MODE1;
   161 	    }
   162 	case 0x1A2: /* leadout */
   163 	    leadout = MSFTOLBA(buf[i+8], buf[i+9], buf[i+10]);
   164 	    break;
   165 	}
   166     }
   167     disc->track_count = max_track;
   169     if( leadout != -1 && last_track != -1 ) {
   170 	disc->track[last_track].sector_count = leadout - disc->track[last_track].lba;
   171     }
   172     return 0;
   173 }
   175 static gdrom_error_t linux_read_sectors_ioctl( gdrom_disc_t disc, uint32_t sector,
   176 					 uint32_t sector_count, int mode, char *buf,
   177 					 uint32_t *length )
   178 {
   179     int fd = fileno(disc->file);
   180     int call;
   181     struct cdrom_read read;
   182     read.cdread_lba = LBATOMSF(sector);
   183     read.cdread_bufaddr = buf;
   184     switch(mode) {
   185     case GDROM_MODE1:
   186 	call = CDROMREADMODE1;
   187 	read.cdread_buflen = sector_count * 2048;
   188 	break;
   189     case GDROM_MODE2:
   190 	call = CDROMREADMODE2;
   191 	read.cdread_buflen = sector_count * 2336;
   192 	break;
   193     default:
   194 	return PKT_ERR_BADREADMODE;
   195     }
   197     if( ioctl( fd, call, &read ) == -1 ) {
   198 	ERROR( "Error reading disc (%s)", strerror(errno) );
   199 	return PKT_ERR_BADREAD;
   200     }
   201     *length = read.cdread_buflen;
   202     return PKT_ERR_OK;
   203 }
   205 static gdrom_error_t linux_read_sectors( gdrom_disc_t disc, uint32_t sector,
   206 					 uint32_t sector_count, int mode, char *buf,
   207 					 uint32_t *length )
   208 {
   209     int fd = fileno(disc->file);
   210     uint32_t real_sector = sector - CD_MSF_OFFSET;
   211     uint32_t sector_size = 2048;
   212     int buflen = sector_count * sector_size;
   213     int i;
   214     char cmd[12] = { 0xBE, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
   216     for( i=0; i<sector_count; i += MAX_SECTORS_PER_CALL ) {
   217 	int count = MIN(MAX_SECTORS_PER_CALL, sector_count);
   218 	cmd[2] = (real_sector >> 24) & 0xFF;
   219 	cmd[3] = (real_sector >> 16) & 0xFF;
   220 	cmd[4] = (real_sector >> 8) & 0xFF;
   221 	cmd[5] = real_sector & 0xFF;
   222 	cmd[6] = (count >> 16) & 0xFF;
   223 	cmd[7] = (count >> 8) & 0xFF;
   224 	cmd[8] = count & 0xFF;
   225 	cmd[9] = 0x10;
   227 	gdrom_error_t status = linux_send_command( fd, cmd, buf, count * sector_size, CGC_DATA_READ );
   228 	if( status != 0 ) {
   229 	    return status;
   230 	}
   231 	real_sector += count;
   232 	buf += count * sector_size;
   233     }
   234     *length = buflen;
   235     return 0;
   236 }
   238 /**
   239  * Send a packet command to the device and wait for a response. 
   240  * @return 0 on success, -1 on an operating system error, or a sense error
   241  * code on a device error.
   242  */
   243 static gdrom_error_t linux_send_command( int fd, char *cmd, char *buffer, size_t buflen,
   244 					 int direction )
   245 {
   246     struct request_sense sense;
   247     struct cdrom_generic_command cgc;
   249     memset( &cgc, 0, sizeof(cgc) );
   250     memset( &sense, 0, sizeof(sense) );
   251     memcpy( cgc.cmd, cmd, 12 );
   252     cgc.buffer = buffer;
   253     cgc.buflen = buflen;
   254     cgc.sense = &sense;
   255     cgc.data_direction = direction;
   257     if( ioctl(fd, CDROM_SEND_PACKET, &cgc) == -1 ) {
   258 	if( sense.sense_key == 0 ) {
   259 	    return -1; 
   260 	} else {
   261 	    return sense.sense_key | (sense.asc<<8);
   262 	}
   263     } else {
   264 	return 0;
   265     }
   266 }
.