1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/gdrom/linux.c Fri Dec 15 10:18:39 2006 +0000
1.5 + * $Id: linux.c,v 1.1 2006-12-14 12:31:38 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 <stdlib.h>
1.23 +#include <string.h>
1.25 +#include <linux/cdrom.h>
1.26 +#include <sys/stat.h>
1.27 +#include <sys/ioctl.h>
1.29 +#include "gdrom/gdrom.h"
1.30 +#include "gdrom/packet.h"
1.33 +#define MAXTOCENTRIES 600 /* This is a fairly generous overestimate really */
1.34 +#define MAXTOCSIZE 4 + (MAXTOCENTRIES*11)
1.36 +#define MSFTOLBA( m,s,f ) (f + (s*CD_FRAMES) + (m*CD_FRAMES*CD_SECS))
1.38 +static uint32_t inline lbatomsf( uint32_t lba ) {
1.39 + union cdrom_addr addr;
1.40 + lba = lba + CD_MSF_OFFSET;
1.41 + addr.msf.frame = lba % CD_FRAMES;
1.42 + int seconds = lba / CD_FRAMES;
1.43 + addr.msf.second = seconds % CD_SECS;
1.44 + addr.msf.minute = seconds / CD_SECS;
1.48 +#define LBATOMSF( lba ) lbatomsf(lba)
1.51 +static gboolean linux_image_is_valid( FILE *f );
1.52 +static gdrom_disc_t linux_open_device( const gchar *filename, FILE *f );
1.53 +static gdrom_error_t linux_read_disc_toc( gdrom_disc_t disc );
1.54 +static gdrom_error_t linux_read_sectors( gdrom_disc_t disc, uint32_t sector,
1.55 + uint32_t sector_count, int mode, char *buf,
1.56 + uint32_t *length );
1.57 +static gdrom_error_t linux_send_command( int fd, char *cmd, char *buffer, size_t buflen,
1.61 +struct gdrom_image_class linux_device_class = { "Linux", NULL,
1.62 + linux_image_is_valid, linux_open_device };
1.64 +static gboolean linux_image_is_valid( FILE *f )
1.67 + struct cdrom_tochdr tochdr;
1.69 + if( fstat(fileno(f), &st) == -1 ) {
1.70 + return FALSE; /* can't stat device? */
1.72 + if( !S_ISBLK(st.st_mode) ) {
1.73 + return FALSE; /* Not a block device */
1.76 + if( ioctl(fileno(f), CDROMREADTOCHDR, &tochdr) == -1 ) {
1.77 + /* Quick check that this is really a CD */
1.84 +static gdrom_disc_t linux_open_device( const gchar *filename, FILE *f )
1.86 + gdrom_disc_t disc;
1.87 + int fd = fileno(f);
1.89 + disc = gdrom_image_new(f);
1.90 + if( disc == NULL ) {
1.91 + ERROR("Unable to allocate memory!");
1.95 + gdrom_error_t status = linux_read_disc_toc( disc );
1.96 + if( status != 0 ) {
1.98 + if( status == 0xFFFF ) {
1.99 + ERROR("Unable to load disc table of contents (%s)", strerror(errno));
1.101 + ERROR("Unable to load disc table of contents (sense %d,%d)",
1.102 + status &0xFF, status >> 8 );
1.106 + disc->read_sectors = linux_read_sectors;
1.107 + disc->disc_type = IDE_DISC_CDROM;
1.112 + * Read the full table of contents into the disc from the device.
1.114 +static gdrom_error_t linux_read_disc_toc( gdrom_disc_t disc )
1.116 + int fd = fileno(disc->file);
1.117 + unsigned char buf[MAXTOCSIZE];
1.118 + char cmd[12] = { 0x43, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1.120 + cmd[7] = (sizeof(buf))>>8;
1.121 + cmd[8] = (sizeof(buf))&0xFF;
1.122 + memset( buf, 0, sizeof(buf) );
1.123 + gdrom_error_t status = linux_send_command( fd, cmd, buf, sizeof(buf), CGC_DATA_READ );
1.124 + if( status != 0 ) {
1.128 + int max_track = 0;
1.129 + int last_track = -1;
1.130 + int leadout = -1;
1.131 + int len = (buf[0] << 8) | buf[1];
1.132 + int session_type = GDROM_MODE1;
1.134 + for( i = 4; i<len; i+=11 ) {
1.135 + int session = buf[i];
1.136 + int adr = buf[i+1] >> 4;
1.137 + int point = buf[i+3];
1.138 + if( adr == 0x01 && point > 0 && point < 100 ) {
1.140 + int trackno = point-1;
1.141 + if( point > max_track ) {
1.142 + max_track = point;
1.144 + disc->track[trackno].flags = (buf[i+1] & 0x0F) << 4;
1.145 + disc->track[trackno].session = session - 1;
1.146 + disc->track[trackno].lba = MSFTOLBA(buf[i+8],buf[i+9],buf[i+10]);
1.147 + if( disc->track[trackno].flags & TRACK_DATA ) {
1.148 + disc->track[trackno].mode = GDROM_MODE1;
1.150 + disc->track[trackno].mode = GDROM_CDDA;
1.152 + if( last_track != -1 ) {
1.153 + disc->track[last_track].sector_count = disc->track[trackno].lba -
1.154 + disc->track[last_track].lba;
1.156 + last_track = trackno;
1.157 + } else switch( (adr << 8) | point ) {
1.158 + case 0x1A0: /* session info */
1.159 + if( buf[i+9] == 0x20 ) {
1.160 + session_type = GDROM_MODE2;
1.162 + session_type = GDROM_MODE1;
1.164 + case 0x1A2: /* leadout */
1.165 + leadout = MSFTOLBA(buf[i+8], buf[i+9], buf[i+10]);
1.169 + disc->track_count = max_track;
1.171 + if( leadout != -1 && last_track != -1 ) {
1.172 + disc->track[last_track].sector_count = leadout - disc->track[last_track].lba;
1.177 +static gdrom_error_t linux_read_sectors_ioctl( gdrom_disc_t disc, uint32_t sector,
1.178 + uint32_t sector_count, int mode, char *buf,
1.179 + uint32_t *length )
1.181 + int fd = fileno(disc->file);
1.183 + struct cdrom_read read;
1.184 + read.cdread_lba = LBATOMSF(sector);
1.185 + read.cdread_bufaddr = buf;
1.187 + case GDROM_MODE1:
1.188 + call = CDROMREADMODE1;
1.189 + read.cdread_buflen = sector_count * 2048;
1.191 + case GDROM_MODE2:
1.192 + call = CDROMREADMODE2;
1.193 + read.cdread_buflen = sector_count * 2336;
1.196 + return PKT_ERR_BADREADMODE;
1.199 + if( ioctl( fd, call, &read ) == -1 ) {
1.200 + ERROR( "Error reading disc (%s)", strerror(errno) );
1.201 + return PKT_ERR_BADREAD;
1.203 + *length = read.cdread_buflen;
1.204 + return PKT_ERR_OK;
1.207 +static gdrom_error_t linux_read_sectors( gdrom_disc_t disc, uint32_t sector,
1.208 + uint32_t sector_count, int mode, char *buf,
1.209 + uint32_t *length )
1.211 + int fd = fileno(disc->file);
1.212 + uint32_t real_sector = sector - CD_MSF_OFFSET;
1.213 + int buflen = sector_count * 2048;
1.214 + char cmd[12] = { 0xBE, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1.215 + cmd[2] = (real_sector >> 24) & 0xFF;
1.216 + cmd[3] = (real_sector >> 16) & 0xFF;
1.217 + cmd[4] = (real_sector >> 8) & 0xFF;
1.218 + cmd[5] = real_sector & 0xFF;
1.219 + cmd[6] = (sector_count >> 16) & 0xFF;
1.220 + cmd[7] = (sector_count >> 8) & 0xFF;
1.221 + cmd[8] = sector_count & 0xFF;
1.224 + gdrom_error_t status = linux_send_command( fd, cmd, buf, buflen, CGC_DATA_READ );
1.225 + if( status == 0 ) {
1.226 + *length = buflen;
1.232 + * Send a packet command to the device and wait for a response.
1.233 + * @return 0 on success, -1 on an operating system error, or a sense error
1.234 + * code on a device error.
1.236 +static gdrom_error_t linux_send_command( int fd, char *cmd, char *buffer, size_t buflen,
1.239 + struct request_sense sense;
1.240 + struct cdrom_generic_command cgc;
1.242 + memset( &cgc, 0, sizeof(cgc) );
1.243 + memset( &sense, 0, sizeof(sense) );
1.244 + memcpy( cgc.cmd, cmd, 12 );
1.245 + cgc.buffer = buffer;
1.246 + cgc.buflen = buflen;
1.247 + cgc.sense = &sense;
1.248 + cgc.data_direction = direction;
1.250 + if( ioctl(fd, CDROM_SEND_PACKET, &cgc) == -1 ) {
1.251 + if( sense.sense_key == 0 ) {
1.254 + return sense.sense_key | (sense.asc<<8);