cd_control.c (11629B)
1 /* ----------------------------------------------------------------------- 2 cd_control process 3 4 part of XfreeCD 5 6 Copyright (C) 1998 Brian C. Lane 7 nexus@tatoosh.com 8 http://www.tatoosh.com/nexus 9 10 This program is free software; you can redistribute it and/or 11 modify it under the terms of the GNU General Public License 12 as published by the Free Software Foundation; either version 2 13 of the License, or (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 23 24 ==========================[ HISTORY ]================================== 25 06/22/98 Bug in track length calculation. It was waiting until track 26 #2 before calculating the lengths (holdover from the change 27 to 0 offset from 1 offset). FIXED. 28 29 05/08/98 Added the control and init code from old version of xfreecd 30 Added software control of the EJECT on CLOSE state - using 31 the CD_SET_EJECT command and an argument of 0 to disable 32 eject on exit and 1 to enable eject on exit. 33 34 05/03/98 Wrote the synchronization code and basic switch statment. 35 Testing different shutdown situations. No hungup child 36 processes yet. 37 38 Things this process needs to do: 39 x 1. Get the data on the current CD and send it to the 40 parent, in response to a request by the parent. 41 x 2. Play a CD 42 x 3. Pause a CD 43 x 4. Resume a CD 44 x 5. Go to next track 45 x 6. Go to Previous track 46 x 8. Eject a CD 47 x 9. Close the CD tray 48 10. Switch CDs for a CD changer 49 11. Seek forward (may need a kernel patch?) 50 12. Seek backwards (may need a kernel patch?) 51 13. Return error/status codes (like if a play is tried and 52 there is no CD in the player). 53 14. Shuffle play (on one Cd or among multiple CDs) 54 15. Repeat Song 55 16. Repeat whole CD, repeat group of CDs 56 57 58 ----------------------------------------------------------------------- */ 59 #include <stdio.h> 60 #include <fcntl.h> 61 #include <sys/ioctl.h> 62 #include <sys/types.h> 63 #include <sys/socket.h> 64 #include <unistd.h> 65 #include <gtk/gtk.h> 66 #include "child_sync.h" 67 #include "cd_control.h" 68 #include "cddb.h" 69 70 int cd_control( int ); 71 int readline( register int, register char *, register int ); 72 73 #undef DEBUG1 74 #undef DEBUG3 75 76 /* 77 Fire up the cd_control process. 78 Create a socket pair. 79 Fork a child process 80 Return the parent's file descriptor for the socket or -1 + errno 81 fill in childpid with the child's PID 82 Child calls cd_control 83 */ 84 int start_cd_control( int *childpid ) 85 { 86 int fd[2]; 87 88 if( socketpair( AF_UNIX, SOCK_STREAM, 0, fd ) < 0 ) 89 return(-1); 90 91 if( ( *childpid = fork() ) < 0 ) 92 { 93 perror("start_cd_control, cannot fork"); 94 return(-1); 95 } else if( *childpid == 0 ) { 96 close( fd[0] ); 97 98 /* Synchronize with the parent, exit if it fails */ 99 if( parent_sync( fd[1] ) == 0 ) 100 cd_control( fd[1] ); 101 102 close( fd[1] ); 103 exit(0); 104 } 105 close( fd[1] ); 106 107 if( child_sync( fd[0] ) == 0 ) 108 return(fd[0]); 109 110 return(-1); 111 } 112 113 114 /* 115 Load a new CD, read all of its info into the cdinfo array 116 */ 117 int cdrom_init( struct CDINFO *cdinfo, struct cdrom_volctrl *volctrl, char *cd_device ) 118 { 119 int fd; 120 int x; 121 122 /* Try and open the /dev/cdrom device */ 123 if( ( fd = open( cd_device, O_RDONLY ) ) < 0 ) 124 { 125 #ifdef DEBUG3 126 g_print( "device = %s\n", cd_device); 127 #endif 128 129 perror("CDROM device" ); 130 return -1; 131 } 132 133 /* Read the cdrom's header information */ 134 if( ioctl( fd, CDROMREADTOCHDR, &cdinfo->tochdr ) != 0 ) 135 { 136 perror("cdrom_init(CDROMREADTOCHDR-2)" ); 137 return -1; 138 } 139 140 /* Get the time on the end of the last track */ 141 cdinfo->leadout.cdte_track = CDROM_LEADOUT; 142 cdinfo->leadout.cdte_format = CDROM_MSF; 143 144 if( ioctl( fd, CDROMREADTOCENTRY, &cdinfo->leadout ) != 0 ) 145 { 146 perror( "cdrom_init(CDROMREADTOCENTRY-2)" ); 147 return -1; 148 } 149 150 /* Calculate the total length of the CD */ 151 cdinfo->cd_length = cdinfo->leadout.cdte_addr.msf.second \ 152 + (cdinfo->leadout.cdte_addr.msf.minute * 60); 153 154 /* Read the current CD volume level - different from soundcard volume */ 155 if( ioctl( fd, CDROMVOLREAD, volctrl ) == 0 ) 156 { 157 cdinfo->volume = volctrl->channel0; 158 } else { 159 perror( "cdrom_init(CDROMVOLREAD-2)" ); 160 return -1; 161 } 162 163 /* Fill in the tracks[] array with info on all the tracks */ 164 for( x = 0; x < cdinfo->tochdr.cdth_trk1; x++ ) 165 { 166 /* Read the info for the current track */ 167 cdinfo->track[x].te.cdte_track = x+1; 168 cdinfo->track[x].te.cdte_format = CDROM_MSF; 169 if( ioctl( fd, CDROMREADTOCENTRY, &cdinfo->track[x].te ) != 0 ) 170 { 171 perror( "set_track_length(CDROMREADTOCENTRY)" ); 172 return -1; 173 } 174 175 cdinfo->track[x].frame_offset = cdinfo->track[x].te.cdte_addr.msf.minute * 60 + cdinfo->track[x].te.cdte_addr.msf.second; 176 177 /* Convert seconds to frames */ 178 cdinfo->track[x].frame_offset *= 75; 179 cdinfo->track[x].frame_offset += cdinfo->track[x].te.cdte_addr.msf.frame; 180 181 /* 182 * Set the length of the previous track 183 * Accomplished by taking the start of the current track - start 184 * of the previous track. 185 */ 186 if( x > 0 ) 187 { 188 /* Set length to the start of the current one */ 189 cdinfo->track[x-1].length = (cdinfo->track[x].te.cdte_addr.msf.minute * 60) + cdinfo->track[x].te.cdte_addr.msf.second; 190 191 /* Subtract the start of the previous track */ 192 cdinfo->track[x-1].length = cdinfo->track[x-1].length - ((cdinfo->track[x-1].te.cdte_addr.msf.minute * 60) + cdinfo->track[x-1].te.cdte_addr.msf.second); 193 194 #ifdef DEBUG1 195 /* Show some debug data on the tracks we find */ 196 g_print("track %d - length = %d seconds\n", x-1, cdinfo->track[x-1].length ); 197 #endif 198 } 199 } 200 201 /* 202 * Set the length of the last track 203 * This is accomplished by subtracting the last track start from the 204 * length of the CD. 205 */ 206 x--; 207 cdinfo->track[x].length = cdinfo->cd_length - ((cdinfo->track[x].te.cdte_addr.msf.minute * 60) + cdinfo->track[x].te.cdte_addr.msf.second ); 208 209 #ifdef DEBUG1 210 /* Show debug data on the last track */ 211 g_print("track %d - length = %d seconds\n", x, cdinfo->track[x].length ); 212 213 g_print("New CD loaded: "); 214 g_print("%d tracks, cd_length = %d, ", cdinfo->tochdr.cdth_trk1, cdinfo->cd_length ); 215 g_print("volume = %d\n", cdinfo->volume ); 216 #endif 217 218 /* Calculate the discid for this CD */ 219 cdinfo->discid = cddb_discid( cdinfo ); 220 221 cdinfo->revision = -1; 222 cdinfo->extd = NULL; 223 224 225 /* All is well */ 226 return fd; 227 } 228 229 230 /* ---------------------------------------------------------------------- 231 Control the low-level audio CD player 232 233 Wait for commands from the parent, execute the command and send back 234 a response if one is needed 235 236 A command is 2 bytes of data - a command and an optional argument. 237 The Commands are defined in cd_control.h as CD_* 238 239 To init the cd, if status is called the first time it will try to init 240 the CD. If this fails it sets cd_fd to -1, so status won't repeatedly 241 try the CD. After the initial failure only a play will try. 242 243 ----------------------------------------------------------------------- */ 244 int cd_control( int control_fd ) 245 { 246 char cmnd[2], 247 cd_device[80]; /* Device to talk to */ 248 struct cdrom_volctrl volctrl; /* Volume control */ 249 struct CDINFO cdinfo; /* All the info on the CD */ 250 int fd_cd=0; 251 252 /* Default cdrom device */ 253 strcpy( cd_device, "/dev/cdrom" ); 254 255 for(;;) 256 { 257 /* Get the command from the parent process */ 258 if( read( control_fd, &cmnd, 2 ) != 2 ) 259 return(-1); 260 261 switch( cmnd[0] ) 262 { 263 case CD_DIAG : 264 puts("cd_control diagnostic\n"); 265 break; 266 267 case CD_STATUS : 268 if( fd_cd > 0 ) 269 { 270 cdinfo.sc.cdsc_format = CDROM_MSF; /* Info in MM:SS:FF format*/ 271 if( ioctl( fd_cd, CDROMSUBCHNL, &cdinfo.sc ) != 0 ) 272 { 273 /* perror( "cd_control(CDROMSUBCHNL-2)" ); */ 274 cdinfo.sc.cdsc_audiostatus = CDROM_AUDIO_NO_STATUS; 275 } 276 } else { 277 cdinfo.sc.cdsc_audiostatus = CDROM_AUDIO_NO_STATUS; 278 } 279 280 /* Send the new data back to the main process */ 281 write( control_fd, &cdinfo, sizeof( struct CDINFO ) ); 282 break; 283 284 285 case CD_PLAY : 286 /* If we are not open yet, try to open it first */ 287 if( fd_cd < 1 ) 288 { 289 fd_cd = cdrom_init( &cdinfo, &volctrl, cd_device ); 290 291 /* Start playing at the beginning*/ 292 cmnd[1] = cdinfo.tochdr.cdth_trk0; 293 } 294 295 if( fd_cd > 0 ) 296 { 297 /* Setup the CD to play the indicated track */ 298 cdinfo.ti.cdti_trk0 = cmnd[1]; 299 cdinfo.ti.cdti_trk1 = cdinfo.tochdr.cdth_trk1; 300 cdinfo.ti.cdti_ind0 = cdinfo.ti.cdti_ind1 = 0; 301 302 /* Stop playing previous track and start playing the next */ 303 /* Try this without the stop and see what happens... */ 304 if( ioctl( fd_cd, CDROMSTOP ) != 0 ) 305 { 306 perror( "cd_control(CDROMSTOP-1)" ); 307 } else if( ioctl( fd_cd, CDROMPLAYTRKIND, &cdinfo.ti ) != 0 ) { 308 perror( "cd_control(CDROMPLAYTRKIND-1)" ); 309 } 310 } 311 break; 312 313 case CD_PAUSE : 314 if( fd_cd > 0 ) 315 { 316 if( ioctl( fd_cd, CDROMPAUSE ) != 0 ) 317 perror( "cd_control(CDROMPAUSE)" ); 318 } 319 break; 320 321 case CD_RESUME : 322 if( fd_cd > 0 ) 323 { 324 if( ioctl( fd_cd, CDROMRESUME ) != 0 ) 325 perror( "cd_control(CDROMRESUME)" ); 326 } 327 break; 328 329 case CD_STOP : 330 if( fd_cd > 0 ) 331 { 332 if( ioctl( fd_cd, CDROMSTOP ) != 0 ) 333 perror( "cd_control(CDROMSTOP-2)" ); 334 335 close( fd_cd ); 336 fd_cd = 0; 337 } 338 break; 339 340 case CD_VOLUME : 341 if( fd_cd > 0 ) 342 { 343 cdinfo.volume = (unsigned char) cmnd[1]; 344 345 volctrl.channel0 = cdinfo.volume; 346 volctrl.channel1 = cdinfo.volume; 347 if( ioctl( fd_cd, CDROMVOLCTRL, &volctrl ) != 0 ) 348 perror( "cd_control(CDROMVOLCTRL-3)" ); 349 } 350 break; 351 352 case CD_EJECT : 353 if( (fd_cd > 0) ) 354 { 355 /* Stop playing, then eject the CD */ 356 if( ioctl( fd_cd, CDROMSTOP ) != 0 ) 357 { 358 perror( "cd_control(CDROMSTOP-3)" ); 359 } else if( ioctl( fd_cd, CDROMEJECT ) != 0 ) { 360 perror( "cd_control(CDROMEJECT)" ); 361 } 362 close( fd_cd ); 363 fd_cd = 0; 364 } 365 break; 366 367 case CD_SET_EJECT : 368 if( fd_cd > 0 ) 369 { 370 /* Set the software configurable eject on close state */ 371 if( ioctl( fd_cd, CDROMEJECT_SW, cmnd[1] ) != 0 ) 372 perror("cd_control(CDROMEJECT_SW)"); 373 } 374 break; 375 376 /* Set the default device, close the current device and open the 377 new one */ 378 case CD_SET_DEVICE : 379 380 #ifdef DEBUG3 381 printf("got CD_SET_DEVICE\n"); 382 #endif 383 384 /* 385 This routine is different from the others, it reads a string from 386 the sending process after receiving the 2 byte command 387 */ 388 if( readline( control_fd, cd_device, 80 ) < 0 ) 389 { 390 perror("readline - 2"); 391 } 392 393 /* Strip trailing CR */ 394 if( cd_device[strlen(cd_device)-1] == '\n' ) 395 cd_device[strlen(cd_device)-1] = 0; 396 397 #ifdef DEBUG3 398 printf("Setting cd_control cd_device = %s\n", cd_device ); 399 #endif 400 /* If a device isn't open, then open one */ 401 if( fd_cd < 1 ) 402 fd_cd = cdrom_init( &cdinfo, &volctrl, cd_device ); 403 404 break; 405 406 407 case CD_QUIT : 408 if( fd_cd ) 409 close( fd_cd ); 410 return(0); 411 break; 412 } 413 } 414 return(0); 415 }