// This module is for reading deltas (MFM bit transition delta times) from the // PRU and optionally writing them to a file. The deltas are also available // through the pointer returned by deltas setup. The deltas are 16 bit unsigned // values with each word the time difference between the current rising edge and // the previous rising edge of the MFM data signal in 200 MHz clock // counts. // // The PRU puts the deltas in the shared DDR memory. These // routines copy them to normal DDR memory. The shared DDR memory is uncached // so slow to access. Copying is a little faster. // A thread is used to read the data from the PRU and update the available delta // count // // Call deltas_setup once to setup the process. // Call deltas_start_thread to start the reader thread and optionally start // writing delta data to filedeltas_get_count // Call deltas_start_read after each read command is sent to the PRU. // Call deltas_get_count to get the number of deltas available // Call deltas_wait_read_finished to wait until all deltas are received // Call deltas_stop_thread when done with the delta thread // // 06/27/2015 DJG Made CMD_STATUS_READ_OVERRUN a warning instead of fatal error // 05/16/2015 DJG Changes for deltas_read_file.c // 01/04/2015 DJG Changes for start_time_ns // 11/09/2014 DJG Changes for new note option // // Copyright 2014 David Gesswein. // This file is part of MFM disk utilities. // // MFM disk utilities is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // MFM disk utilities is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with MFM disk utilities. If not, see . #include #include #include #include #include #include #include #include #include #include #include "msg.h" #include "crc_ecc.h" #include "emu_tran_file.h" #include "mfm_decoder.h" #include "cmd.h" #include "pru_setup.h" #include "deltas_read.h" #include "drive.h" static void *delta_proc(void *arg); // Semaphore to control when the delta reader starts looking for more deltas static sem_t deltas_sem; // Pointer to delta memory static uint16_t *deltas; // The current cylinder and head processing. Used when writing transitions to file static int deltas_cyl, deltas_head; // The thread to process deltas pthread_t delta_thread; // State of thread static volatile enum { THREAD_NOT_CREATED, THREAD_SHUTDOWN, THREAD_RUNNING } thread_state = THREAD_NOT_CREATED; // Number of deltas in buffer static int num_deltas; // Deltas are being received from read thread static int streaming; // Allocate the memory to hold the deltas and initialize the semaphore. // Call once before calling other routines. // // ddr_mem_size: Size of ddr shared memory in bytes void *deltas_setup(int ddr_mem_size) { // Create storage for delta data read from PRU deltas = msg_malloc(ddr_mem_size, "deltas_setup deltas"); if (sem_init(&deltas_sem, 0, 0) == -1) { msg(MSG_FATAL, "Sem creation failed\n"); exit(1); } return deltas; } // Start the delta thread. // // drive_params: NULL if no transition data should be written void deltas_start_thread(DRIVE_PARAMS *drive_params) { if (pthread_create(&delta_thread, NULL, &delta_proc, drive_params) == 0) { thread_state = THREAD_RUNNING; } else { msg(MSG_FATAL, "Unable to create delta thread\n"); exit(1); } } // Routine to stop the delta thread void deltas_stop_thread() { if (thread_state == THREAD_RUNNING) { thread_state = THREAD_SHUTDOWN; // Let it run to see the shutdown sem_post(&deltas_sem); // And wait for it to exit pthread_join(delta_thread, NULL); thread_state = THREAD_NOT_CREATED; } } // Call to start reading the deltas after starting the PRU CMD_READ_TRACK. // It may also be called again if it is desired to reprocess the same deltas. // // cyl, head: Track being read void deltas_start_read(int cyl, int head) { deltas_cyl = cyl; deltas_head = head; // Init state before releasing thread deltas_update_count(0, 1); sem_post(&deltas_sem); } // Write deltas to file. Don't call if transitions_file is -1. // // deltas: delta data to write // num_deltas: number of deltas to write in words static void write_deltas(int fd, uint16_t deltas[], int num_deltas) { tran_file_write_track_deltas(fd, deltas, num_deltas, deltas_cyl, deltas_head); } // This is the thread for processing deltas. // We read a tracks worth of delta from PRU into global deltas then // wait for semaphore to repeat. If global thread_state is // THREAD_SHUTDOWN then we exit. // // arg: pointer to drive_params if output files should be created or NULL // to not create a file static void *delta_proc(void *arg) { // Total number of deltas we have gotten on this track int track_deltas = 0; // Bytes of deltas to read int num_bytes; // set to 1 when PRU has indicated read is done int pru_finished_read = 0; // When 1 wait for read to be started int wait_read = 1; DRIVE_PARAMS *drive_params = (DRIVE_PARAMS *) arg; // Open file if requested if (drive_params != NULL && drive_params->transitions_filename != NULL) { drive_params->tran_fd = tran_file_write_header( drive_params->transitions_filename, drive_params->num_cyl, drive_params->num_head, drive_params->cmdline, drive_params->note, drive_params->start_time_ns); } // And loop reading delta transitions while (1) { // Wait till read started if (wait_read) { track_deltas = 0; sem_wait(&deltas_sem); wait_read = 0; if (thread_state == THREAD_SHUTDOWN) break; } num_bytes = pru_read_word(MEM_PRU0_DATA, PRU0_WRITE_PTR) - track_deltas * sizeof(deltas[0]); pru_read_mem(MEM_DDR, &deltas[track_deltas], num_bytes, track_deltas * sizeof(deltas[0])); track_deltas += num_bytes / sizeof(deltas[0]); deltas_update_count(track_deltas, 1); // If we didn't get very many deltas sleep to reduce overhead if (num_bytes < 300) { // CMD_STATUS_READ_STARTED indicates read is in progress, // CMD_STATUS_OK indicates PRU has finished read if (pru_get_cmd_status() != CMD_STATUS_READ_STARTED) { if (pru_get_cmd_status() != CMD_STATUS_OK) { if (pru_get_cmd_status() == CMD_STATUS_DELTA_OVERFLOW) { msg(MSG_ERR_SERIOUS, "Delta transition time overflow, raw transitions will not accuractly represent cyl %d head %d\n", deltas_cyl, deltas_head); } else if (pru_get_cmd_status() == CMD_STATUS_READ_OVERRUN) { msg(MSG_ERR_SERIOUS, "Delta transitions lost, raw transitions will not accuractly represent cyl %d head %d\n", deltas_cyl, deltas_head); } else { msg(MSG_FATAL, "Fault reading deltas cmd %x status %x cyl %d head %d\n", pru_get_cmd_status(), drive_get_drive_status(), deltas_cyl, deltas_head); drive_print_drive_status(MSG_FATAL, drive_get_drive_status()); msg(MSG_FATAL, "CMD_DATA %x delta count %x\n", pru_get_cmd_data(), pru_read_word(MEM_PRU0_DATA, PRU0_WRITE_PTR)); exit(1); } } // PRU says its done. Use pru_finished_read variable to loop one more time // to ensure we have read all the deltas if (pru_finished_read) { // This must be before update_mfm_deltas to prevent // next read starting while we are still writing deltas if (drive_params != NULL && drive_params->transitions_filename != NULL) { write_deltas(drive_params->tran_fd, deltas, track_deltas); } // All are transferred, tell MFM decoder all deltas available deltas_update_count(track_deltas, 0); pru_finished_read = 0; // Indicate we should wait for next read wait_read = 1; } else { // check one more time to avoid race conditions pru_finished_read = 1; } } usleep(500); } } if (drive_params != NULL && drive_params->transitions_filename != NULL) { tran_file_close(drive_params->tran_fd, 1); } return NULL; } // Update our count of deltas. Streaming indicates we are reading data from // PRU as it comes in. Streaming is set to zero after all data is read from // the PRU. // // num_deltas_in: Total number of deltas read so far // streaming_in: 1 if data is being read from PRU. 0 when all data read. void deltas_update_count(int num_deltas_in, int streaming_in) { num_deltas = num_deltas_in; streaming = streaming_in; } // Get the delta count. While streaming we return the number of deltas. When // we are done streaming and cur_deltas is >= num_deltas we return -1 to // indicate to caller it has processed all of the deltas. // // cur_delta: Number of deltas processed by caller // return: Number of deltas read or -1 if no more deltas int deltas_get_count(int deltas_processed) { if (streaming) { return num_deltas; } else { if (deltas_processed >= num_deltas) { return -1; } else { return num_deltas; } } } // Wait until all deltas received. This is used when writing the deltas to a // file but not decoding. The wait ensures the deltas are written before // staring the next read. // // return: number of deltas read int deltas_wait_read_finished() { int num_deltas, ret_deltas = 0; num_deltas = deltas_get_count(0); while ((num_deltas = deltas_get_count(num_deltas)) >= 0) { ret_deltas = num_deltas; usleep(500); } return ret_deltas; }