Search
lxdream.org :: lxdream/src/drivers/cd_linux.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/drivers/cd_linux.c
changeset 678:35eb00945316
prev644:ccae4bfa5f82
next709:18c39a8e504c
author nkeynes
date Sat Jun 14 11:54:15 2008 +0000 (15 years ago)
permissions -rw-r--r--
last change Change colour params to float
Convert background processing over to scene structure (fixes some depth issues as well)
Add color unclamp when supported
file annotate diff log raw
nkeynes@520
     1
/**
nkeynes@561
     2
 * $Id$
nkeynes@520
     3
 *
nkeynes@520
     4
 * Linux cd-rom device driver. 
nkeynes@520
     5
 *
nkeynes@520
     6
 * Copyright (c) 2005 Nathan Keynes.
nkeynes@520
     7
 *
nkeynes@520
     8
 * This program is free software; you can redistribute it and/or modify
nkeynes@520
     9
 * it under the terms of the GNU General Public License as published by
nkeynes@520
    10
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@520
    11
 * (at your option) any later version.
nkeynes@520
    12
 *
nkeynes@520
    13
 * This program is distributed in the hope that it will be useful,
nkeynes@520
    14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@520
    15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@520
    16
 * GNU General Public License for more details.
nkeynes@520
    17
 */
nkeynes@520
    18
#include <unistd.h>
nkeynes@520
    19
#include <stdlib.h>
nkeynes@520
    20
#include <stdio.h>
nkeynes@520
    21
#include <string.h>
nkeynes@520
    22
#include <errno.h>
nkeynes@520
    23
#include <linux/cdrom.h>
nkeynes@520
    24
#include <sys/stat.h>
nkeynes@520
    25
#include <sys/ioctl.h>
nkeynes@520
    26
#include <fstab.h>
nkeynes@520
    27
#include <fcntl.h>
nkeynes@520
    28
nkeynes@678
    29
#include "gdrom/gddriver.h"
nkeynes@520
    30
#include "gdrom/packet.h"
nkeynes@520
    31
#include "dream.h"
nkeynes@520
    32
nkeynes@520
    33
#define MAXTOCENTRIES 600  /* This is a fairly generous overestimate really */
nkeynes@520
    34
#define MAXTOCSIZE 4 + (MAXTOCENTRIES*11)
nkeynes@520
    35
#define MAX_SECTORS_PER_CALL 1
nkeynes@520
    36
nkeynes@520
    37
#define MSFTOLBA( m,s,f ) (f + (s*CD_FRAMES) + (m*CD_FRAMES*CD_SECS))
nkeynes@520
    38
nkeynes@520
    39
static uint32_t inline lbatomsf( uint32_t lba ) {
nkeynes@520
    40
    union cdrom_addr addr;
nkeynes@520
    41
    lba = lba + CD_MSF_OFFSET;
nkeynes@520
    42
    addr.msf.frame = lba % CD_FRAMES;
nkeynes@520
    43
    int seconds = lba / CD_FRAMES;
nkeynes@520
    44
    addr.msf.second = seconds % CD_SECS;
nkeynes@520
    45
    addr.msf.minute = seconds / CD_SECS;
nkeynes@520
    46
    return addr.lba;
nkeynes@520
    47
}
nkeynes@520
    48
nkeynes@520
    49
#define LBATOMSF( lba ) lbatomsf(lba)
nkeynes@520
    50
nkeynes@520
    51
nkeynes@520
    52
static gboolean linux_image_is_valid( FILE *f );
nkeynes@520
    53
static gdrom_disc_t linux_open_device( const gchar *filename, FILE *f );
nkeynes@520
    54
static gdrom_error_t linux_read_disc_toc( gdrom_image_t disc );
nkeynes@520
    55
static gdrom_error_t linux_read_sector( gdrom_disc_t disc, uint32_t sector,
nkeynes@520
    56
					int mode, unsigned char *buf, uint32_t *length );
nkeynes@520
    57
static gdrom_error_t linux_send_command( int fd, char *cmd, unsigned char *buffer, size_t *buflen,
nkeynes@520
    58
					 int direction );
nkeynes@520
    59
static int linux_drive_status( gdrom_disc_t disc );
nkeynes@520
    60
nkeynes@520
    61
struct gdrom_image_class cdrom_device_class = { "Linux", NULL,
nkeynes@520
    62
						linux_image_is_valid, linux_open_device };
nkeynes@520
    63
GList *gdrom_get_native_devices(void)
nkeynes@520
    64
{
nkeynes@520
    65
    GList *list = NULL;
nkeynes@520
    66
    struct fstab *ent;
nkeynes@520
    67
    struct stat st;
nkeynes@520
    68
    setfsent();
nkeynes@520
    69
    while( (ent = getfsent()) != NULL ) {
nkeynes@520
    70
	if( (stat(ent->fs_spec, &st) != -1) && 
nkeynes@520
    71
	    S_ISBLK(st.st_mode) ) {
nkeynes@520
    72
	    /* Got a valid block device - is it a CDROM? */
nkeynes@520
    73
	    int fd = open(ent->fs_spec, O_RDONLY|O_NONBLOCK);
nkeynes@520
    74
	    if( fd == -1 )
nkeynes@520
    75
		continue;
nkeynes@520
    76
	    int caps = ioctl(fd, CDROM_GET_CAPABILITY);
nkeynes@520
    77
	    if( caps != -1 ) {
nkeynes@520
    78
		/* Appears to support CDROM functions */
nkeynes@520
    79
		list = g_list_append( list, g_strdup(ent->fs_spec) );
nkeynes@520
    80
	    }
nkeynes@520
    81
	    close(fd);
nkeynes@520
    82
	}
nkeynes@520
    83
    }
nkeynes@520
    84
    return list;
nkeynes@520
    85
}
nkeynes@520
    86
nkeynes@520
    87
static gboolean linux_image_is_valid( FILE *f )
nkeynes@520
    88
{
nkeynes@520
    89
    struct stat st;
nkeynes@520
    90
    struct cdrom_tochdr tochdr;
nkeynes@520
    91
nkeynes@520
    92
    if( fstat(fileno(f), &st) == -1 ) {
nkeynes@520
    93
	return FALSE; /* can't stat device? */
nkeynes@520
    94
    }
nkeynes@520
    95
    if( !S_ISBLK(st.st_mode) ) {
nkeynes@520
    96
	return FALSE; /* Not a block device */
nkeynes@520
    97
    }
nkeynes@520
    98
nkeynes@520
    99
    if( ioctl(fileno(f), CDROMREADTOCHDR, &tochdr) == -1 ) {
nkeynes@520
   100
	/* Quick check that this is really a CD */
nkeynes@520
   101
	return FALSE;
nkeynes@520
   102
    }
nkeynes@520
   103
nkeynes@520
   104
    return TRUE;
nkeynes@520
   105
}
nkeynes@520
   106
nkeynes@520
   107
static gdrom_disc_t linux_open_device( const gchar *filename, FILE *f ) 
nkeynes@520
   108
{
nkeynes@520
   109
    gdrom_disc_t disc;
nkeynes@520
   110
nkeynes@520
   111
    disc = gdrom_image_new(filename, f);
nkeynes@520
   112
    if( disc == NULL ) {
nkeynes@520
   113
	ERROR("Unable to allocate memory!");
nkeynes@520
   114
	return NULL;
nkeynes@520
   115
    }
nkeynes@520
   116
nkeynes@520
   117
    gdrom_error_t status = linux_read_disc_toc( (gdrom_image_t)disc );
nkeynes@520
   118
    if( status != 0 ) {
nkeynes@520
   119
	gdrom_image_destroy_no_close(disc);
nkeynes@520
   120
	if( status == 0xFFFF ) {
nkeynes@520
   121
	    ERROR("Unable to load disc table of contents (%s)", strerror(errno));
nkeynes@520
   122
	} else {
nkeynes@520
   123
	    ERROR("Unable to load disc table of contents (sense %d,%d)",
nkeynes@520
   124
		  status &0xFF, status >> 8 );
nkeynes@520
   125
	}
nkeynes@520
   126
	return NULL;
nkeynes@520
   127
    }
nkeynes@520
   128
    disc->read_sector = linux_read_sector;
nkeynes@520
   129
    disc->drive_status = linux_drive_status;
nkeynes@520
   130
    return disc;
nkeynes@520
   131
}
nkeynes@520
   132
nkeynes@520
   133
static int linux_drive_status( gdrom_disc_t disc )
nkeynes@520
   134
{
nkeynes@520
   135
    int fd = fileno(((gdrom_image_t)disc)->file);
nkeynes@520
   136
    int status = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
nkeynes@520
   137
    if( status == CDS_DISC_OK ) {
nkeynes@520
   138
	status = ioctl(fd, CDROM_MEDIA_CHANGED, CDSL_CURRENT);
nkeynes@520
   139
	if( status != 0 ) {
nkeynes@520
   140
	    linux_read_disc_toc( (gdrom_image_t)disc);
nkeynes@520
   141
	}
nkeynes@520
   142
	return ((gdrom_image_t)disc)->disc_type | IDE_DISC_READY;
nkeynes@520
   143
    } else {
nkeynes@520
   144
	return IDE_DISC_NONE;
nkeynes@520
   145
    }
nkeynes@520
   146
}
nkeynes@520
   147
/**
nkeynes@520
   148
 * Read the full table of contents into the disc from the device.
nkeynes@520
   149
 */
nkeynes@520
   150
static gdrom_error_t linux_read_disc_toc( gdrom_image_t disc )
nkeynes@520
   151
{
nkeynes@520
   152
    int fd = fileno(disc->file);
nkeynes@520
   153
    unsigned char buf[MAXTOCSIZE];
nkeynes@520
   154
    size_t buflen = sizeof(buf);
nkeynes@520
   155
    char cmd[12] = { 0x43, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
nkeynes@520
   156
    
nkeynes@520
   157
    cmd[7] = (sizeof(buf))>>8;
nkeynes@520
   158
    cmd[8] = (sizeof(buf))&0xFF;
nkeynes@520
   159
    memset( buf, 0, sizeof(buf) );
nkeynes@520
   160
    gdrom_error_t status = linux_send_command( fd, cmd, buf, &buflen, CGC_DATA_READ );
nkeynes@520
   161
    if( status != 0 ) {
nkeynes@520
   162
	return status;
nkeynes@520
   163
    }
nkeynes@520
   164
nkeynes@520
   165
    int max_track = 0;
nkeynes@520
   166
    int last_track = -1;
nkeynes@520
   167
    int leadout = -1;
nkeynes@520
   168
    int len = (buf[0] << 8) | buf[1];
nkeynes@644
   169
    int session_type = -1;
nkeynes@520
   170
    int i;
nkeynes@520
   171
    for( i = 4; i<len; i+=11 ) {
nkeynes@520
   172
	int session = buf[i];
nkeynes@520
   173
	int adr = buf[i+1] >> 4;
nkeynes@520
   174
	int point = buf[i+3];
nkeynes@520
   175
	if( adr == 0x01 && point > 0 && point < 100 ) {
nkeynes@520
   176
	    /* Track info */
nkeynes@520
   177
	    int trackno = point-1;
nkeynes@520
   178
	    if( point > max_track ) {
nkeynes@520
   179
		max_track = point;
nkeynes@520
   180
	    }
nkeynes@520
   181
	    disc->track[trackno].flags = (buf[i+1] & 0x0F) << 4;
nkeynes@520
   182
	    disc->track[trackno].session = session - 1;
nkeynes@520
   183
	    disc->track[trackno].lba = MSFTOLBA(buf[i+8],buf[i+9],buf[i+10]);
nkeynes@520
   184
	    if( disc->track[trackno].flags & TRACK_DATA ) {
nkeynes@520
   185
		disc->track[trackno].mode = GDROM_MODE1;
nkeynes@520
   186
	    } else {
nkeynes@520
   187
		disc->track[trackno].mode = GDROM_CDDA;
nkeynes@520
   188
	    }
nkeynes@520
   189
	    if( last_track != -1 ) {
nkeynes@520
   190
		disc->track[last_track].sector_count = disc->track[trackno].lba -
nkeynes@520
   191
		    disc->track[last_track].lba;
nkeynes@520
   192
	    }
nkeynes@520
   193
	    last_track = trackno;
nkeynes@520
   194
	} else switch( (adr << 8) | point ) {
nkeynes@520
   195
	case 0x1A0: /* session info */
nkeynes@520
   196
	    if( buf[i+9] == 0x20 ) {
nkeynes@644
   197
		session_type = IDE_DISC_CDROMXA;
nkeynes@520
   198
	    } else {
nkeynes@644
   199
		session_type = IDE_DISC_CDROM;
nkeynes@520
   200
	    }
nkeynes@644
   201
	    disc->disc_type = session_type;
nkeynes@644
   202
	    break;
nkeynes@520
   203
	case 0x1A2: /* leadout */
nkeynes@520
   204
	    leadout = MSFTOLBA(buf[i+8], buf[i+9], buf[i+10]);
nkeynes@520
   205
	    break;
nkeynes@520
   206
	}
nkeynes@520
   207
    }
nkeynes@520
   208
    disc->track_count = max_track;
nkeynes@520
   209
nkeynes@520
   210
    if( leadout != -1 && last_track != -1 ) {
nkeynes@520
   211
	disc->track[last_track].sector_count = leadout - disc->track[last_track].lba;
nkeynes@520
   212
    }
nkeynes@520
   213
    return 0;
nkeynes@520
   214
}
nkeynes@520
   215
nkeynes@520
   216
 gdrom_error_t linux_play_audio( gdrom_disc_t disc, uint32_t lba, uint32_t endlba )
nkeynes@520
   217
{
nkeynes@520
   218
    int fd = fileno( ((gdrom_image_t)disc)->file );
nkeynes@520
   219
    uint32_t real_sector = lba - CD_MSF_OFFSET;
nkeynes@520
   220
    uint32_t length = endlba - lba;
nkeynes@520
   221
    uint32_t buflen = 0;
nkeynes@520
   222
    char cmd[12] = { 0xA5, 0,0,0, 0,0,0,0, 0,0,0,0 };
nkeynes@520
   223
    cmd[2] = (real_sector >> 24) & 0xFF;
nkeynes@520
   224
    cmd[3] = (real_sector >> 16) & 0xFF;
nkeynes@520
   225
    cmd[4] = (real_sector >> 8) & 0xFF;
nkeynes@520
   226
    cmd[5] = real_sector & 0xFF;
nkeynes@520
   227
    cmd[6] = (length >> 24) & 0xFF;
nkeynes@520
   228
    cmd[7] = (length >> 16) & 0xFF;
nkeynes@520
   229
    cmd[8] = (length >> 8) & 0xFF;
nkeynes@520
   230
    cmd[9] = length & 0xFF;
nkeynes@520
   231
    
nkeynes@520
   232
    return linux_send_command( fd, cmd, NULL, &buflen, CGC_DATA_NONE );
nkeynes@520
   233
}
nkeynes@520
   234
nkeynes@520
   235
gdrom_error_t linux_stop_audio( gdrom_disc_t disc )
nkeynes@520
   236
{
nkeynes@520
   237
    int fd = fileno( ((gdrom_image_t)disc)->file );
nkeynes@520
   238
    uint32_t buflen = 0;
nkeynes@520
   239
    char cmd[12] = {0x4E,0,0,0, 0,0,0,0, 0,0,0,0};
nkeynes@520
   240
    return linux_send_command( fd, cmd, NULL, &buflen, CGC_DATA_NONE );
nkeynes@520
   241
}
nkeynes@520
   242
nkeynes@520
   243
static gdrom_error_t linux_read_sector( gdrom_disc_t disc, uint32_t sector,
nkeynes@520
   244
					int mode, unsigned char *buf, uint32_t *length )
nkeynes@520
   245
{
nkeynes@520
   246
    gdrom_image_t image = (gdrom_image_t)disc;
nkeynes@520
   247
    int fd = fileno(image->file);
nkeynes@520
   248
    uint32_t real_sector = sector - CD_MSF_OFFSET;
nkeynes@520
   249
    uint32_t sector_size = MAX_SECTOR_SIZE;
nkeynes@520
   250
    char cmd[12] = { 0xBE, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
nkeynes@520
   251
nkeynes@520
   252
    cmd[1] = (mode & 0x0E) << 1;
nkeynes@520
   253
    cmd[2] = (real_sector >> 24) & 0xFF;
nkeynes@520
   254
    cmd[3] = (real_sector >> 16) & 0xFF;
nkeynes@520
   255
    cmd[4] = (real_sector >> 8) & 0xFF;
nkeynes@520
   256
    cmd[5] = real_sector & 0xFF;
nkeynes@520
   257
    cmd[6] = 0;
nkeynes@520
   258
    cmd[7] = 0;
nkeynes@520
   259
    cmd[8] = 1;
nkeynes@644
   260
nkeynes@644
   261
    if( READ_CD_RAW(mode) ) {
nkeynes@644
   262
	cmd[9] = 0xF0;
nkeynes@644
   263
    } else {
nkeynes@644
   264
	if( READ_CD_HEADER(mode) ) {
nkeynes@644
   265
	    cmd[9] = 0xA0;
nkeynes@644
   266
	}
nkeynes@644
   267
	if( READ_CD_SUBHEAD(mode) ) {
nkeynes@644
   268
	    cmd[9] |= 0x40;
nkeynes@644
   269
	}
nkeynes@644
   270
	if( READ_CD_DATA(mode) ) {
nkeynes@644
   271
	    cmd[9] |= 0x10;
nkeynes@644
   272
	}
nkeynes@644
   273
    }
nkeynes@520
   274
    
nkeynes@520
   275
    gdrom_error_t status = linux_send_command( fd, cmd, buf, &sector_size, CGC_DATA_READ );
nkeynes@520
   276
    if( status != 0 ) {
nkeynes@520
   277
	return status;
nkeynes@520
   278
    }
nkeynes@520
   279
    *length = 2048;
nkeynes@520
   280
    return 0;
nkeynes@520
   281
}
nkeynes@520
   282
nkeynes@520
   283
/**
nkeynes@520
   284
 * Send a packet command to the device and wait for a response. 
nkeynes@520
   285
 * @return 0 on success, -1 on an operating system error, or a sense error
nkeynes@520
   286
 * code on a device error.
nkeynes@520
   287
 */
nkeynes@520
   288
static gdrom_error_t linux_send_command( int fd, char *cmd, unsigned char *buffer, size_t *buflen,
nkeynes@520
   289
					 int direction )
nkeynes@520
   290
{
nkeynes@520
   291
    struct request_sense sense;
nkeynes@520
   292
    struct cdrom_generic_command cgc;
nkeynes@520
   293
nkeynes@520
   294
    memset( &cgc, 0, sizeof(cgc) );
nkeynes@520
   295
    memset( &sense, 0, sizeof(sense) );
nkeynes@520
   296
    memcpy( cgc.cmd, cmd, 12 );
nkeynes@520
   297
    cgc.buffer = buffer;
nkeynes@520
   298
    cgc.buflen = *buflen;
nkeynes@520
   299
    cgc.sense = &sense;
nkeynes@520
   300
    cgc.data_direction = direction;
nkeynes@520
   301
    
nkeynes@644
   302
    if( ioctl(fd, CDROM_SEND_PACKET, &cgc) < 0 ) {
nkeynes@520
   303
	if( sense.sense_key == 0 ) {
nkeynes@520
   304
	    return -1; 
nkeynes@520
   305
	} else {
nkeynes@520
   306
	    /* TODO: Map newer codes back to the ones used by the gd-rom. */
nkeynes@520
   307
	    return sense.sense_key | (sense.asc<<8);
nkeynes@520
   308
	}
nkeynes@520
   309
    } else {
nkeynes@520
   310
	*buflen = cgc.buflen;
nkeynes@520
   311
	return 0;
nkeynes@520
   312
    }
nkeynes@520
   313
}
.