static gint xmms_cdda_read (xmms_xform_t *xform, void *buffer, gint len, xmms_error_t *error) { xmms_cdda_data_t *data; gint ret; g_return_val_if_fail (xform, -1); g_return_val_if_fail (buffer, -1); g_return_val_if_fail (error, -1); data = xmms_xform_private_data_get (xform); g_return_val_if_fail (data, -1); if (cdio_get_media_changed (data->cdio)) { xmms_error_set (error, XMMS_ERROR_GENERIC, "CD ejected"); return -1; } if (data->current_lsn >= data->last_lsn) { return 0; } if (data->buf_used == CDIO_CD_FRAMESIZE_RAW) { cdio_cddap_read (data->drive, data->read_buf, data->current_lsn, 1); data->current_lsn++; data->buf_used = 0; } if (len >= CDIO_CD_FRAMESIZE_RAW) { ret = CDIO_CD_FRAMESIZE_RAW - data->buf_used; memcpy (buffer, data->read_buf + data->buf_used, ret); } else { gulong buf_left = CDIO_CD_FRAMESIZE_RAW - data->buf_used; if (buf_left < len) { memcpy (buffer, data->read_buf + data->buf_used, buf_left); ret = buf_left; } else { memcpy (buffer, data->read_buf + data->buf_used, len); ret = len; } } data->buf_used += ret; return ret; }
static void *cdromreader_thread(void *threadarg) { int nreturn=0, nrsectors, nrstartsector, nrsectorsread, nrtotalsectorsread; struct resource_struct *resource, *device_resource; struct local_cdrom_struct *cdrom; struct audio_track_struct *audio_track; struct cached_block_struct *cached_block; struct read_call_struct *read_call; unsigned char tries1, tries2; bool foundincache, cachetested; struct read_command_struct *read_command=NULL; struct read_command_struct *read_command_again=NULL; char *buffread, *buffer; unsigned char cachereadlock; logoutput("CDROMREADER"); pthread_mutex_init(&queue_lockmutex, NULL); pthread_cond_init(&queue_lockcond, NULL); while (1) { // wait for something on the queue and free to lock pthread_mutex_lock(&queue_lockmutex); while ( queue_lock==1 || ! head_queue_read_commands) { pthread_cond_wait(&queue_lockcond, &queue_lockmutex); } // set it to locked queue_lock=1; // queue is not empty: process the read command // first get the read request (to read cached_block) read_command=head_queue_read_commands; // remove from queue head_queue_read_commands=read_command->next; if ( tail_queue_read_commands == read_command ) tail_queue_read_commands=head_queue_read_commands; if ( head_queue_read_commands ) head_queue_read_commands->prev=NULL; queue_lock=0; pthread_cond_broadcast(&(queue_lockcond)); pthread_mutex_unlock(&(queue_lockmutex)); /* release the queue */ // really detach from the queue read_command->next=NULL; read_command->prev=NULL; // process the read request // first find out which file to write to // TODO: no read_call when a read ahead if ( read_command->readaheadpolicy==READAHEAD_POLICY_NONE ) { // not initiated for read ahead purposes // so there must be a read_call read_call=read_command->read_call; if ( ! read_call ) { free(read_command); read_command=NULL; logoutput2("error!! original read call not found...."); continue; } } // // determine the track/resource // resource=read_command->resource; if ( ! resource ) { logoutput2("error!! audio_track not found!! serious io error"); continue; } audio_track=(struct audio_track_struct *) resource->data; foundincache=false; cachetested=false; read_command_again=NULL; cached_block=NULL; // // a read lock on the cache for this file/track // nreturn=try2increaseactions(resource); if ( nreturn<0 ) { logoutput2("error!! not able to get read lock audio_track for track %i!! serious io error", read_call->tracknr); // what to do here??? try again? } else { cachereadlock=(unsigned char) nreturn; } while ( ! cachetested ) { if ( ! cached_block ) { // get the first cached block... cached_block=get_first_cached_block_internal(audio_track, read_command->startsector, read_command->endsector); } else { cached_block=cached_block->next; } if ( ! cached_block ) { // nothing found in cache break; } else if ( cached_block->startsector<=read_command->startsector ) { // a cached block found, with startsector left of the desired sector // check the endsector if ( cached_block->endsector >= read_command->endsector ) { // read_command->(startsector,endsector) is in cache foundincache=true; break; } else { // // check the next interval right of cached_block->endsector // // right of cached_block->endsector is not in cache // correct the read command read_command->startsector=cached_block->endsector+1; continue; } } else { // cached_block->startsector>tmpsector if ( cached_block->startsector>read_command->endsector ) { // the cached block found doesn't offer anything // so the interval has to be read break; } else { // cached_block->startsector<=read_command->endsector if ( cached_block->endsector<read_command->endsector ) { read_command_again=get_read_command(); if ( read_command_again ) { read_command_again->read_call=read_command->read_call; read_command_again->resource=resource; read_command_again->startsector=cached_block->endsector+1; read_command_again->endsector=read_command->endsector; read_command_again->readaheadlevel=read_command->readaheadlevel; read_command_again->readaheadpolicy=read_command->readaheadpolicy; } } read_command->endsector=cached_block->startsector-1; break; } } } decreaseactions(resource, cachereadlock); // readahead // put more read_commands in the queue // if there is read_command_again (created above) use that if ( read_command->readaheadpolicy==READAHEAD_POLICY_PIECE || read_command->readaheadpolicy==READAHEAD_POLICY_WHOLE ) { logoutput2("cdromreader: readahead"); if ( ! read_command_again ) { if ( read_command->endsector<audio_track->lastsector ) { // create only when there is some space left over // to readahead if ( read_command->readaheadpolicy==READAHEAD_POLICY_PIECE ) { if ( read_command->readaheadlevel > 0 ) { // read ahead for a piece // and do this only when level>0 read_command_again=get_read_command(); if ( read_command_again ) { read_command_again->read_call=NULL; read_command_again->resource=resource; read_command_again->startsector=read_command->endsector+1; read_command_again->endsector=read_command_again->startsector+READAHEAD_POLICY_PIECE_SECTORS; read_command_again->readaheadpolicy=READAHEAD_POLICY_PIECE; read_command_again->readaheadlevel=read_command->readaheadlevel-1; } } } else if ( read_command->readaheadpolicy==READAHEAD_POLICY_WHOLE ) { read_command_again=get_read_command(); if ( read_command_again ) { read_command_again->read_call=NULL; read_command_again->resource=resource; read_command_again->startsector=read_command->endsector+1; read_command_again->endsector=read_command_again->startsector+READAHEAD_POLICY_WHOLE_SECTORS; read_command_again->readaheadpolicy=READAHEAD_POLICY_WHOLE; read_command_again->readaheadlevel=read_command->readaheadlevel; } } } } else { // there is already a read_command_again // adjust that // do not adjust the readeahead level if ( read_command_again->readaheadpolicy==READAHEAD_POLICY_PIECE ) { if ( read_command_again->startsector + READAHEAD_POLICY_PIECE_SECTORS > read_command_again->endsector ) { read_command_again->endsector = read_command_again->startsector + READAHEAD_POLICY_PIECE_SECTORS; } } else if ( read_command_again->readaheadpolicy==READAHEAD_POLICY_WHOLE ) { if ( read_command_again->startsector + READAHEAD_POLICY_WHOLE_SECTORS > read_command_again->endsector ) { read_command_again->endsector = read_command_again->startsector + READAHEAD_POLICY_WHOLE_SECTORS; } } } } if ( read_command_again ) { // do not read past last sector for this track if ( read_command_again->endsector > audio_track->lastsector ) { read_command_again->endsector = audio_track->lastsector; } logoutput2("cdrom reader: sending a read command (%i - %i) again", read_command_again->startsector, read_command_again->endsector); add_read_command_to_queue(read_command_again); } if (foundincache) { // // signal and loop // if ( read_command->read_call ) { // notify waiting clients ..... of course only when there is one notify_waiting_clients(read_command->read_call, read_command->startsector, read_command->endsector); } free(read_command); read_command=NULL; continue; } // correction and readahead done // ready to process read_command nrsectors = read_command->endsector - read_command->startsector + 1; if ( nrsectors <= 0 ) { // invalid logoutput2("invalid block,nrsectors %i serious io error", nrsectors); free(read_command); read_command=NULL; continue; } // does this work..??? // mail on list..... device_resource=resource->parent; cdrom=(struct local_cdrom_struct *) device_resource->data; if ( cdio_cddap_speed_set((cdrom_drive_t *) cdrom->media.audio.cdda, -1) ) { logoutput("setting cdrom to full speed failed..."); } else { logoutput("cdrom set to full speed..."); } // the read of the cdrom goes in batches with size probably much smaller than the size requested // (batch size 20110920: 25 sectors) // but because it's not know here how much sectors are read in one batch (and how to change that) // we take the "overall" size of the buffer // and copy the sectors read to another buffer with the right size size_t size=nrsectors * CD_FRAMESIZE_RAW; createbuffer: buffer=malloc(size); if ( ! buffer ) { // maybe retry here in case of errors logoutput("error!! cannot create buffer to read cdrom.....ioerrors will be result..."); continue; } tries1=0; nrstartsector=read_command->startsector; nrsectorsread=0; nrtotalsectorsread=0; logoutput2("cdromreader: about to read %i sectors from sector %i", nrsectors, nrstartsector); readfromcd: // clear the buffer memset(buffer, '\0', size); nrsectorsread=cdio_cddap_read((cdrom_drive_t *) cdrom->media.audio.cdda, buffer, nrstartsector, nrsectors - nrtotalsectorsread); if ( nrsectorsread<0 ) { // handle error here logoutput2("cdromreader: error %i reading cd", nrsectorsread); tries1++; if ( tries1>4 ) { // howto report back to the original caller there is an error // // just for now do nothing to report back // // just leave it for the original caller (a cdfs_read call!) to expire and leave it there // // for an incident this is ok, but for serious error, like the cdrom is not readable // anymore the reading should be stopped in an earlier stage than here // free(read_command); read_command=NULL; logoutput("error!! serious errors (%i) reading the cd", nrsectorsread); free(buffer); continue; } goto readfromcd; } else { // send the bytes to the cache if ( nrtotalsectorsread+nrsectorsread > nrsectors ) nrsectorsread=nrsectors-nrtotalsectorsread; tries2=0; // // create the buffer read, it's best to copy this to another buffer cause the writing to the cache // might take longer than the reading of the next buffer, overwriting existing read result // maybe this is nonsense but to be safe // createbufferread: buffread=malloc(nrsectorsread * CD_FRAMESIZE_RAW); if ( ! buffread ) { tries2++; if ( tries2>4 ) { // same as above // no way yet to report back // // free(read_command); read_command=NULL; logoutput("error!! serious errors creating buffer reading the cd"); free(buffer); continue; } goto createbufferread; } // copy the bytes just read in the cdrom read buffer (size: nrsectors) to the // result buffer (size: nrsectorsread) memcpy(buffread, buffer, nrsectorsread * CD_FRAMESIZE_RAW); nreturn=send_read_result_to_cache(read_command->read_call, resource, nrstartsector, buffread, nrsectorsread); if ( nreturn<0 ) { free(read_command); read_command=NULL; logoutput("error!! serious errors creating buffer reading the cd"); free(buffread); free(buffer); continue; } // update counters nrtotalsectorsread+=nrsectorsread; nrstartsector+=nrsectorsread; logoutput2("cdromreader: %i sectors read, total so far: %i, %i requested)", nrsectorsread, nrtotalsectorsread, nrsectors); if ( nrtotalsectorsread>=nrsectors ) { // ready: continue // note it's not necessary to free the buffread, that's freed when it's written to the cache free(read_command); read_command=NULL; free(buffer); continue; } goto readfromcd; } } pthread_mutex_destroy(&queue_lockmutex); pthread_cond_destroy(&queue_lockcond); return NULL; }