Example #1
0
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;

}