nkeynes@520 | 1 | /**
|
nkeynes@561 | 2 | * $Id$
|
nkeynes@520 | 3 | *
|
nkeynes@1023 | 4 | * Linux cd-rom device driver. Implemented using the SCSI transport.
|
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@806 | 23 | #include <ctype.h>
|
nkeynes@520 | 24 | #include <linux/cdrom.h>
|
nkeynes@520 | 25 | #include <sys/stat.h>
|
nkeynes@520 | 26 | #include <sys/ioctl.h>
|
nkeynes@520 | 27 | #include <fstab.h>
|
nkeynes@520 | 28 | #include <fcntl.h>
|
nkeynes@520 | 29 |
|
nkeynes@1097 | 30 | #include "drivers/cdrom/cdimpl.h"
|
nkeynes@520 | 31 |
|
nkeynes@1023 | 32 | static gboolean linux_is_cdrom_device( FILE *f );
|
nkeynes@1097 | 33 | static gboolean linux_cdrom_disc_init( cdrom_disc_t disc, ERROR *err );
|
nkeynes@1097 | 34 | static cdrom_disc_t linux_cdrom_drive_open( cdrom_drive_t drive, ERROR *err );
|
nkeynes@1097 | 35 | static cdrom_error_t linux_cdrom_do_cmd( int fd, char *cmd,
|
nkeynes@1097 | 36 | unsigned char *buf, unsigned int *buflen, unsigned char direction );
|
nkeynes@1097 | 37 | static cdrom_error_t linux_packet_read( cdrom_disc_t disc, char *cmd,
|
nkeynes@1023 | 38 | unsigned char *buf, uint32_t *buflen );
|
nkeynes@1097 | 39 | static cdrom_error_t linux_packet_cmd( cdrom_disc_t disc, char *cmd );
|
nkeynes@1097 | 40 | static gboolean linux_media_changed( cdrom_disc_t disc );
|
nkeynes@520 | 41 |
|
nkeynes@520 | 42 |
|
nkeynes@1097 | 43 | struct cdrom_disc_factory linux_cdrom_drive_factory = { "Linux", NULL,
|
nkeynes@1097 | 44 | linux_is_cdrom_device, linux_cdrom_disc_init, cdrom_disc_scsi_read_toc };
|
nkeynes@1023 | 45 |
|
nkeynes@1097 | 46 | static struct cdrom_scsi_transport linux_scsi_transport = {
|
nkeynes@1023 | 47 | linux_packet_read, linux_packet_cmd, linux_media_changed };
|
nkeynes@1023 | 48 |
|
nkeynes@1023 | 49 | static gboolean linux_is_cdrom_device( FILE *f )
|
nkeynes@1023 | 50 | {
|
nkeynes@1023 | 51 | int caps = ioctl(fileno(f), CDROM_GET_CAPABILITY);
|
nkeynes@1023 | 52 | if( caps == -1 ) {
|
nkeynes@1023 | 53 | /* Quick check that this is really a CD device */
|
nkeynes@1023 | 54 | return FALSE;
|
nkeynes@1023 | 55 | } else {
|
nkeynes@1023 | 56 | return TRUE;
|
nkeynes@1023 | 57 | }
|
nkeynes@1023 | 58 | }
|
nkeynes@1023 | 59 |
|
nkeynes@1097 | 60 | void cdrom_drive_scan(void)
|
nkeynes@520 | 61 | {
|
nkeynes@1097 | 62 | unsigned char ident[256];
|
nkeynes@1097 | 63 | uint32_t identlen;
|
nkeynes@1097 | 64 | char cmd[12] = {0x12,0,0,0, 0xFF,0,0,0, 0,0,0,0};
|
nkeynes@1097 | 65 |
|
nkeynes@520 | 66 | struct fstab *ent;
|
nkeynes@520 | 67 | struct stat st;
|
nkeynes@520 | 68 | setfsent();
|
nkeynes@520 | 69 | while( (ent = getfsent()) != NULL ) {
|
nkeynes@736 | 70 | if( (stat(ent->fs_spec, &st) != -1) &&
|
nkeynes@736 | 71 | S_ISBLK(st.st_mode) ) {
|
nkeynes@736 | 72 | /* Got a valid block device - is it a CDROM? */
|
nkeynes@736 | 73 | int fd = open(ent->fs_spec, O_RDONLY|O_NONBLOCK);
|
nkeynes@736 | 74 | if( fd == -1 )
|
nkeynes@736 | 75 | continue;
|
nkeynes@736 | 76 | int caps = ioctl(fd, CDROM_GET_CAPABILITY);
|
nkeynes@736 | 77 | if( caps != -1 ) {
|
nkeynes@736 | 78 | /* Appears to support CDROM functions */
|
nkeynes@1097 | 79 | identlen = sizeof(ident);
|
nkeynes@1097 | 80 | if( linux_cdrom_do_cmd( fd, cmd, ident, &identlen, CGC_DATA_READ ) ==
|
nkeynes@1097 | 81 | CDROM_ERROR_OK ) {
|
nkeynes@1097 | 82 | const char *drive_name = mmc_parse_inquiry( ident );
|
nkeynes@1097 | 83 | cdrom_drive_add( ent->fs_spec, drive_name, linux_cdrom_drive_open );
|
nkeynes@1023 | 84 | }
|
nkeynes@736 | 85 | }
|
nkeynes@1097 | 86 | close(fd);
|
nkeynes@736 | 87 | }
|
nkeynes@520 | 88 | }
|
nkeynes@520 | 89 | }
|
nkeynes@520 | 90 |
|
nkeynes@1097 | 91 | gboolean linux_cdrom_disc_init( cdrom_disc_t disc, ERROR *err )
|
nkeynes@709 | 92 | {
|
nkeynes@1097 | 93 | if( linux_is_cdrom_device( cdrom_disc_get_base_file(disc) ) ) {
|
nkeynes@1097 | 94 | cdrom_disc_scsi_init(disc, &linux_scsi_transport);
|
nkeynes@1097 | 95 | return TRUE;
|
nkeynes@1097 | 96 | } else {
|
nkeynes@1097 | 97 | return FALSE;
|
nkeynes@1097 | 98 | }
|
nkeynes@709 | 99 | }
|
nkeynes@709 | 100 |
|
nkeynes@1097 | 101 | cdrom_disc_t linux_cdrom_drive_open( cdrom_drive_t drive, ERROR *err )
|
nkeynes@520 | 102 | {
|
nkeynes@1097 | 103 |
|
nkeynes@1097 | 104 | int fd = open(drive->name, O_RDONLY|O_NONBLOCK);
|
nkeynes@1097 | 105 | if( fd == -1 ) {
|
nkeynes@1109 | 106 | SET_ERROR(err, LX_ERR_FILE_NOOPEN, "Unable to open device '%s': %s", drive->name, strerror(errno) );
|
nkeynes@1097 | 107 | return NULL;
|
nkeynes@1097 | 108 | } else {
|
nkeynes@1097 | 109 | FILE *f = fdopen(fd,"ro");
|
nkeynes@1097 | 110 | if( !linux_is_cdrom_device(f) ) {
|
nkeynes@1109 | 111 | SET_ERROR(err, LX_ERR_FILE_UNKNOWN, "Device '%s' is not a CDROM drive", drive->name );
|
nkeynes@1097 | 112 | return NULL;
|
nkeynes@1097 | 113 | }
|
nkeynes@1097 | 114 | return cdrom_disc_scsi_new_file(f, drive->name, &linux_scsi_transport, err);
|
nkeynes@1097 | 115 | }
|
nkeynes@520 | 116 | }
|
nkeynes@520 | 117 |
|
nkeynes@1097 | 118 | static gboolean linux_media_changed( cdrom_disc_t disc )
|
nkeynes@520 | 119 | {
|
nkeynes@1097 | 120 | int fd = cdrom_disc_get_base_fd(disc);
|
nkeynes@520 | 121 | int status = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
|
nkeynes@520 | 122 | if( status == CDS_DISC_OK ) {
|
nkeynes@736 | 123 | status = ioctl(fd, CDROM_MEDIA_CHANGED, CDSL_CURRENT);
|
nkeynes@1023 | 124 | return status == 0 ? FALSE : TRUE;
|
nkeynes@520 | 125 | } else {
|
nkeynes@1097 | 126 | return disc->disc_type == CDROM_DISC_NONE ? FALSE : TRUE;
|
nkeynes@520 | 127 | }
|
nkeynes@520 | 128 | }
|
nkeynes@520 | 129 |
|
nkeynes@1097 | 130 | static cdrom_error_t linux_cdrom_do_cmd( int fd, char *cmd,
|
nkeynes@1097 | 131 | unsigned char *buffer, unsigned int *buflen,
|
nkeynes@1097 | 132 | unsigned char direction )
|
nkeynes@1097 | 133 | {
|
nkeynes@1097 | 134 | struct request_sense sense;
|
nkeynes@1097 | 135 | struct cdrom_generic_command cgc;
|
nkeynes@1097 | 136 |
|
nkeynes@1097 | 137 | memset( &cgc, 0, sizeof(cgc) );
|
nkeynes@1097 | 138 | memset( &sense, 0, sizeof(sense) );
|
nkeynes@1097 | 139 | memcpy( cgc.cmd, cmd, 12 );
|
nkeynes@1097 | 140 | cgc.buffer = buffer;
|
nkeynes@1097 | 141 | if( buflen == NULL )
|
nkeynes@1097 | 142 | cgc.buflen = 0;
|
nkeynes@1097 | 143 | else
|
nkeynes@1097 | 144 | cgc.buflen = *buflen;
|
nkeynes@1097 | 145 | cgc.sense = &sense;
|
nkeynes@1097 | 146 | cgc.data_direction = direction;
|
nkeynes@1097 | 147 |
|
nkeynes@1097 | 148 | if( ioctl(fd, CDROM_SEND_PACKET, &cgc) < 0 ) {
|
nkeynes@1097 | 149 | if( sense.sense_key == 0 ) {
|
nkeynes@1097 | 150 | return -1;
|
nkeynes@1097 | 151 | } else {
|
nkeynes@1097 | 152 | return sense.sense_key | (sense.asc<<8);
|
nkeynes@1097 | 153 | }
|
nkeynes@1097 | 154 | } else {
|
nkeynes@1097 | 155 | if( buflen != NULL )
|
nkeynes@1097 | 156 | *buflen = cgc.buflen;
|
nkeynes@1097 | 157 | return CDROM_ERROR_OK;
|
nkeynes@1097 | 158 | }
|
nkeynes@1097 | 159 |
|
nkeynes@1097 | 160 | }
|
nkeynes@1097 | 161 |
|
nkeynes@520 | 162 | /**
|
nkeynes@520 | 163 | * Send a packet command to the device and wait for a response.
|
nkeynes@520 | 164 | * @return 0 on success, -1 on an operating system error, or a sense error
|
nkeynes@520 | 165 | * code on a device error.
|
nkeynes@520 | 166 | */
|
nkeynes@1097 | 167 | static cdrom_error_t linux_packet_read( cdrom_disc_t disc, char *cmd,
|
nkeynes@1097 | 168 | unsigned char *buffer, unsigned int *buflen )
|
nkeynes@520 | 169 | {
|
nkeynes@1097 | 170 | int fd = cdrom_disc_get_base_fd(disc);
|
nkeynes@1097 | 171 | return linux_cdrom_do_cmd( fd, cmd, buffer, buflen, CGC_DATA_READ );
|
nkeynes@520 | 172 | }
|
nkeynes@1023 | 173 |
|
nkeynes@1097 | 174 | static cdrom_error_t linux_packet_cmd( cdrom_disc_t disc, char *cmd )
|
nkeynes@1023 | 175 | {
|
nkeynes@1097 | 176 | int fd = cdrom_disc_get_base_fd(disc);
|
nkeynes@1097 | 177 | return linux_cdrom_do_cmd( fd, cmd, NULL, NULL, CGC_DATA_NONE );
|
nkeynes@1097 | 178 | }
|
nkeynes@1023 | 179 |
|