Search
lxdream.org :: lxdream/src/gdrom/linux.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/gdrom/linux.c
changeset 237:6f1a429c9d12
next259:7c6881790cc2
author nkeynes
date Fri Dec 15 10:18:39 2006 +0000 (17 years ago)
permissions -rw-r--r--
last change Initial implementation of the NOP (00h) command
file annotate diff log raw
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.3 @@ -0,0 +1,256 @@
1.4 +/**
1.5 + * $Id: linux.c,v 1.1 2006-12-14 12:31:38 nkeynes Exp $
1.6 + *
1.7 + * Linux cd-rom device driver
1.8 + *
1.9 + * Copyright (c) 2005 Nathan Keynes.
1.10 + *
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.15 + *
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.20 + */
1.21 +#include <stdlib.h>
1.22 +#include <stdio.h>
1.23 +#include <string.h>
1.24 +#include <errno.h>
1.25 +#include <linux/cdrom.h>
1.26 +#include <sys/stat.h>
1.27 +#include <sys/ioctl.h>
1.28 +
1.29 +#include "gdrom/gdrom.h"
1.30 +#include "gdrom/packet.h"
1.31 +#include "dream.h"
1.32 +
1.33 +#define MAXTOCENTRIES 600 /* This is a fairly generous overestimate really */
1.34 +#define MAXTOCSIZE 4 + (MAXTOCENTRIES*11)
1.35 +
1.36 +#define MSFTOLBA( m,s,f ) (f + (s*CD_FRAMES) + (m*CD_FRAMES*CD_SECS))
1.37 +
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.45 + return addr.lba;
1.46 +}
1.47 +
1.48 +#define LBATOMSF( lba ) lbatomsf(lba)
1.49 +
1.50 +
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.58 + int direction );
1.59 +
1.60 +
1.61 +struct gdrom_image_class linux_device_class = { "Linux", NULL,
1.62 + linux_image_is_valid, linux_open_device };
1.63 +
1.64 +static gboolean linux_image_is_valid( FILE *f )
1.65 +{
1.66 + struct stat st;
1.67 + struct cdrom_tochdr tochdr;
1.68 +
1.69 + if( fstat(fileno(f), &st) == -1 ) {
1.70 + return FALSE; /* can't stat device? */
1.71 + }
1.72 + if( !S_ISBLK(st.st_mode) ) {
1.73 + return FALSE; /* Not a block device */
1.74 + }
1.75 +
1.76 + if( ioctl(fileno(f), CDROMREADTOCHDR, &tochdr) == -1 ) {
1.77 + /* Quick check that this is really a CD */
1.78 + return FALSE;
1.79 + }
1.80 +
1.81 + return TRUE;
1.82 +}
1.83 +
1.84 +static gdrom_disc_t linux_open_device( const gchar *filename, FILE *f )
1.85 +{
1.86 + gdrom_disc_t disc;
1.87 + int fd = fileno(f);
1.88 +
1.89 + disc = gdrom_image_new(f);
1.90 + if( disc == NULL ) {
1.91 + ERROR("Unable to allocate memory!");
1.92 + return NULL;
1.93 + }
1.94 +
1.95 + gdrom_error_t status = linux_read_disc_toc( disc );
1.96 + if( status != 0 ) {
1.97 + free(disc);
1.98 + if( status == 0xFFFF ) {
1.99 + ERROR("Unable to load disc table of contents (%s)", strerror(errno));
1.100 + } else {
1.101 + ERROR("Unable to load disc table of contents (sense %d,%d)",
1.102 + status &0xFF, status >> 8 );
1.103 + }
1.104 + return NULL;
1.105 + }
1.106 + disc->read_sectors = linux_read_sectors;
1.107 + disc->disc_type = IDE_DISC_CDROM;
1.108 + return disc;
1.109 +}
1.110 +
1.111 +/**
1.112 + * Read the full table of contents into the disc from the device.
1.113 + */
1.114 +static gdrom_error_t linux_read_disc_toc( gdrom_disc_t disc )
1.115 +{
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.119 +
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.125 + return status;
1.126 + }
1.127 +
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.133 + int i;
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.139 + /* Track info */
1.140 + int trackno = point-1;
1.141 + if( point > max_track ) {
1.142 + max_track = point;
1.143 + }
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.149 + } else {
1.150 + disc->track[trackno].mode = GDROM_CDDA;
1.151 + }
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.155 + }
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.161 + } else {
1.162 + session_type = GDROM_MODE1;
1.163 + }
1.164 + case 0x1A2: /* leadout */
1.165 + leadout = MSFTOLBA(buf[i+8], buf[i+9], buf[i+10]);
1.166 + break;
1.167 + }
1.168 + }
1.169 + disc->track_count = max_track;
1.170 +
1.171 + if( leadout != -1 && last_track != -1 ) {
1.172 + disc->track[last_track].sector_count = leadout - disc->track[last_track].lba;
1.173 + }
1.174 + return 0;
1.175 +}
1.176 +
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.180 +{
1.181 + int fd = fileno(disc->file);
1.182 + int call;
1.183 + struct cdrom_read read;
1.184 + read.cdread_lba = LBATOMSF(sector);
1.185 + read.cdread_bufaddr = buf;
1.186 + switch(mode) {
1.187 + case GDROM_MODE1:
1.188 + call = CDROMREADMODE1;
1.189 + read.cdread_buflen = sector_count * 2048;
1.190 + break;
1.191 + case GDROM_MODE2:
1.192 + call = CDROMREADMODE2;
1.193 + read.cdread_buflen = sector_count * 2336;
1.194 + break;
1.195 + default:
1.196 + return PKT_ERR_BADREADMODE;
1.197 + }
1.198 +
1.199 + if( ioctl( fd, call, &read ) == -1 ) {
1.200 + ERROR( "Error reading disc (%s)", strerror(errno) );
1.201 + return PKT_ERR_BADREAD;
1.202 + }
1.203 + *length = read.cdread_buflen;
1.204 + return PKT_ERR_OK;
1.205 +}
1.206 +
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.210 +{
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.222 + cmd[9] = 0x10;
1.223 +
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.227 + }
1.228 + return status;
1.229 +}
1.230 +
1.231 +/**
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.235 + */
1.236 +static gdrom_error_t linux_send_command( int fd, char *cmd, char *buffer, size_t buflen,
1.237 + int direction )
1.238 +{
1.239 + struct request_sense sense;
1.240 + struct cdrom_generic_command cgc;
1.241 +
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.249 +
1.250 + if( ioctl(fd, CDROM_SEND_PACKET, &cgc) == -1 ) {
1.251 + if( sense.sense_key == 0 ) {
1.252 + return -1;
1.253 + } else {
1.254 + return sense.sense_key | (sense.asc<<8);
1.255 + }
1.256 + } else {
1.257 + return 0;
1.258 + }
1.259 +}
.