Exemple #1
0
static void dump_result(nameinfo_t *result) {
	printf("CMD=%s\n", command_to_name(result->cmd));
	printf("DRIVE=%c\n", result->drive == NAMEINFO_UNUSED_DRIVE ? '-' :
				(result->drive == NAMEINFO_UNDEF_DRIVE ? '*' :
				(result->drive == NAMEINFO_LAST_DRIVE ? 'L' :
				result->drive + 0x30)));
	printf("DRIVENAME='%s'\n", result->drivename ? (char*) result->drivename : nullstring);
	printf("NAME='%s' (%d)\n", result->name ? (char*)result->name : nullstring, result->namelen);
	printf("ACCESS=%c\n", result->access ? result->access : '-');
	printf("TYPE=%c", result->type ? result->type : '-'); debug_putcrlf();
	printf("DRIVE2=%c\n", result->file[0].drive == NAMEINFO_UNUSED_DRIVE ? '-' :
				(result->file[0].drive == NAMEINFO_UNDEF_DRIVE ? '*' :
				(result->file[0].drive == NAMEINFO_LAST_DRIVE ? 'L' :
				result->file[0].drive + 0x30)));
	printf("DRIVENAME2='%s'\n", result->file[0].drivename ? (char*) result->file[0].drivename : nullstring);
	printf("NAME2='%s' (%d)\n", result->file[0].name ? (char*)result->file[0].name : nullstring, result->file[0].namelen);
	printf("RECLEN=%d\n", result->recordlen);
	debug_flush();
}
Exemple #2
0
// opens the file, registers an error code in command->error if necessary
// If the open was successful, setup a channel for the given channel number
// (note: explicitely not secondary device number, as IEEE and IEC in parallel
// use overlapping numbers, so they may be shifted or similar to avoid clashes)
//
// The command buffer is used as transmit buffer, so it must not be overwritten
// until the open has been sent.
//
// note that if it returns a value <0 on error, it has to have the error message
// set appropriately.
//
int8_t file_open(uint8_t channel_no, bus_t *bus, errormsg_t *errormsg,
                 void (*callback)(int8_t errnum, uint8_t *rxdata), uint8_t openflag) {

    assert_not_null(bus, "file_open: bus is null");

    cmd_t *command = &(bus->command);
    rtconfig_t *rtconf = &(bus->rtconf);

#ifdef DEBUG_FILE
    debug_printf("OPEN FILE: FOR CHAN: %d WITH NAME: '%s', OPENFLAG=%d\n",
                 channel_no, (char*)&(command->command_buffer), openflag);
#endif

    // note: in a preemtive env, the following would have to be protected
    // to be atomic as we modify static variables

    parse_filename(command, &nameinfo, (openflag & OPENFLAG_LOAD) ? PARSEHINT_LOAD : 0);

#ifdef DEBUG_FILE
    debug_printf("  PARSE -> ACCESS=%c, TYPE=%c\n", nameinfo.access, nameinfo.type);
#endif

    // drive handling needed for error message drive
    if (nameinfo.drive == NAMEINFO_LAST_DRIVE) {
        nameinfo.drive = rtconf->last_used_drive;
    }
    else if (nameinfo.drive == NAMEINFO_UNUSED_DRIVE) {
        // TODO: match CBM behavior
        nameinfo.drive = rtconf->last_used_drive;
    }
    int8_t errdrive = rtconf->errmsg_with_drive ? nameinfo.drive : -1;

    // post-parse

    if (nameinfo.cmd != CMD_NONE && nameinfo.cmd != CMD_DIR && nameinfo.cmd != CMD_OVERWRITE) {
        // command name during open
        // this is in fact ignored by CBM DOS as checked with VICE's true drive emulation
        debug_printf("NO CORRECT CMD: %s\n", command_to_name(nameinfo.cmd));
        nameinfo.cmd = 0;
    }
    if (nameinfo.type != 0 && nameinfo.type != 'S' && nameinfo.type != 'P'
            && nameinfo.type != 'U' && nameinfo.type != 'L') {
        // not set, or set as not sequential and not program
        debug_puts("UNKOWN FILE TYPE: ");
        debug_putc(nameinfo.type);
        debug_putcrlf();
        set_error_tsd(errormsg, CBM_ERROR_FILE_TYPE_MISMATCH, 0, 0, errdrive);
        return -1;
    }
    if (nameinfo.access != 0 && nameinfo.access != 'W' && nameinfo.access != 'R'
            && nameinfo.access != 'A' && nameinfo.access != 'X') {
        debug_puts("UNKNOWN FILE ACCESS TYPE ");
        debug_putc(nameinfo.access);
        debug_putcrlf();
        // not set, or set as not read, write, or append, or r/w ('X')
        set_error_tsd(errormsg, CBM_ERROR_SYNTAX_UNKNOWN, 0, 0, errdrive);
        return -1;
    }
    if (nameinfo.cmd == CMD_DIR && (nameinfo.access != 0 && nameinfo.access != 'R')) {
        // trying to write to a directory
        debug_puts("WRITE TO DIRECTORY!");
        debug_putcrlf();
        set_error_tsd(errormsg, CBM_ERROR_FILE_EXISTS, 0, 0, errdrive);
        return -1;
    }

    uint8_t type = FS_OPEN_RD;

    // file access default
    if (nameinfo.access == 0) {
        if (openflag == OPENFLAG_LOAD) {
            nameinfo.access = 'R';
        } else if (openflag == OPENFLAG_SAVE) {
            nameinfo.access = 'W';
        }
    }

    // file type defaults
    if (nameinfo.type == 0) {
        // do we create a file (access=='W')?
        if (nameinfo.access == 'W') {
            // write (like save)
            if (openflag == OPENFLAG_SAVE) {
                nameinfo.type = 'P';
            } else {
                nameinfo.type = 'S';
            }
        }
        if (nameinfo.access == 'R') {
            if (openflag == OPENFLAG_LOAD) {
                // on load, 'P' is the default
                nameinfo.type = 'P';
            }
        }
    }

    if (nameinfo.access == 'X') {
        // trying to open up a R/W channel
        debug_puts("OPENING UP A R/W CHANNEL!");
        debug_putcrlf();
        type = FS_OPEN_RW;
    }

    if (nameinfo.name[0] == '#') {
        // trying to open up a direct channel
        // Note: needs to be supported for D64 support with U1/U2/...
        // Note: '#' is still blocking on read!
        debug_puts("OPENING UP A DIRECT CHANNEL!");
        debug_putcrlf();

        type = FS_OPEN_DIRECT;
    }

    // if ",W" or secondary address is one, i.e. save
    if (nameinfo.access == 'W') {
        type = FS_OPEN_WR;
    }
    if (nameinfo.access == 'A') {
        type = FS_OPEN_AP;
    }
    if (nameinfo.cmd == CMD_DIR) {
        if (openflag & OPENFLAG_LOAD) {
            type = FS_OPEN_DR;
        } else {
            type = FS_OPEN_RD;
        }
    } else if (nameinfo.cmd == CMD_OVERWRITE) {
        type = FS_OPEN_OW;
    }

#ifdef DEBUG_FILE
    debug_printf("NAME='%s' (%d)\n", nameinfo.name, nameinfo.namelen);
    debug_printf("ACCESS=%c\n", nameinfo.access);
    debug_printf("CMD=%d\n", nameinfo.cmd);
    debug_flush();
#endif

    return file_submit_call(channel_no, type, command->command_buffer, errormsg, rtconf, callback, 0);

}
Exemple #3
0
uint8_t file_submit_call(uint8_t channel_no, uint8_t type, uint8_t *cmd_buffer,
                         errormsg_t *errormsg, rtconfig_t *rtconf,
                         void (*callback)(int8_t errnum, uint8_t *rxdata), uint8_t iscmd) {

    assert_not_null(errormsg, "file_submit_call: errormsg is null");
    assert_not_null(rtconf, "file_submit_call: rtconf is null");

    // check for default drive (here is the place to set the last used one)
    if (nameinfo.drive == NAMEINFO_LAST_DRIVE) {
        nameinfo.drive = rtconf->last_used_drive;
    }
    else if (nameinfo.drive == NAMEINFO_UNUSED_DRIVE) {
        // TODO: match CBM behavior
        nameinfo.drive = rtconf->last_used_drive;
    }
    else if (nameinfo.drive < MAX_DRIVES) {
        // only save real drive numbers as last used default
        rtconf->last_used_drive = nameinfo.drive;
    }

    // if second name does not have a drive, use drive from first,
    // but only if it is defined
    if (nameinfo.file[0].drive == NAMEINFO_UNUSED_DRIVE && nameinfo.drive != NAMEINFO_UNDEF_DRIVE) {
        nameinfo.file[0].drive = nameinfo.drive;
    }

    // here is the place to plug in other file system providers,
    // like SD-Card, or even an outgoing IEC or IEEE, to convert between
    // the two bus systems. This is done depending on the drive number
    // and managed with the ASSIGN call.
    //provider_t *provider = &serial_provider;
    endpoint_t *endpoint = NULL;
    if (type == FS_OPEN_DIRECT) {
        debug_printf("Getting direct endpoint provider for channel %d\n", channel_no);
        endpoint = direct_provider();
    } else {
        endpoint = provider_lookup(nameinfo.drive, (char*) nameinfo.drivename);
    }

    // convert from bus' PETSCII to provider
    // currently only up to the first zero byte is converted, options like file type
    // are still ASCII only
    // in the future the bus may have an own conversion option...
    cconv_converter(CHARSET_PETSCII, endpoint->provider->charset(endpoint->provdata))
    ((char*)nameinfo.name, strlen((char*)nameinfo.name),
     (char*)nameinfo.name, strlen((char*)nameinfo.name));
    for (uint8_t i=0 ; i < nameinfo.num_files ; ++i) {
        if (nameinfo.file[i].name != NULL) {
            cconv_converter(CHARSET_PETSCII, endpoint->provider->charset(endpoint->provdata))
            ((char*)nameinfo.file[i].name, strlen((char*)nameinfo.file[i].name),
             (char*)nameinfo.file[i].name, strlen((char*)nameinfo.file[i].name));
        }
    }

    if (type == FS_MOVE
            && nameinfo.file[0].drive != NAMEINFO_UNUSED_DRIVE 	// then use ep from first drive anyway
            && nameinfo.file[0].drive != nameinfo.drive) {		// no need to check if the same

        // two-name command(s) with possibly different drive numbers
        endpoint_t *endpoint2 = provider_lookup(nameinfo.file[0].drive, (char*) nameinfo.file[0].name);

        if (endpoint2 != endpoint) {
            debug_printf("ILLEGAL DRIVE COMBINATION: %d vs. %d\n", nameinfo.drive+0x30, nameinfo.file[0].drive+0x30);
            set_error_tsd(errormsg, CBM_ERROR_DRIVE_NOT_READY, 0, 0, nameinfo.drive);
            return -1;
        }
    }

    // check the validity of the drive (note that in general provider_lookup
    // returns a default provider - serial-over-USB to the PC, which then
    // may do further checks
    if (endpoint == NULL) {
        debug_puts("ILLEGAL DRIVE: ");
        debug_putc(0x30+nameinfo.drive);
        debug_putcrlf();
        set_error_tsd(errormsg, CBM_ERROR_DRIVE_NOT_READY, 0, 0, nameinfo.drive);
        return -1;
    }
    provider_t *provider = endpoint->provider;

    // find open slot
    //int8_t slot = -1;
    open_t *activeslot = NULL;
    for (uint8_t i = 0; i < MAX_ACTIVE_OPEN; i++) {
        if (active[i].channel_no < 0) {
            //slot = i;
            activeslot = (open_t*) &active[i];
            break;
        }
    }
    //if (slot < 0) {
    if (activeslot == NULL) {
        debug_puts("NO OPEN SLOT FOR OPEN!");
        debug_putcrlf();
        set_error_tsd(errormsg, CBM_ERROR_NO_CHANNEL, 0, 0, nameinfo.drive);
        return -1;
    }

    activeslot->endpoint = endpoint;

    uint8_t len = assemble_filename_packet(cmd_buffer, &nameinfo);
#ifdef DEBUG_FILE
    debug_printf("LEN AFTER ASSEMBLE=%d\n", len);
#endif
    packet_init(&activeslot->txbuf, len, cmd_buffer);

    // store pointer to runtime config in packet
    // used by providers running on the device
    activeslot->txbuf.rtc = rtconf;

    packet_set_filled(&activeslot->txbuf, channel_no, type, len);

    if (!iscmd) {
        // only for file opens
        // note: we need the provider for the dir converter,
        // so we can only do it in here.

        // open channel
        uint8_t writetype = WTYPE_READONLY;
        if (type == FS_OPEN_WR || type == FS_OPEN_AP || type == FS_OPEN_OW) {
            writetype = WTYPE_WRITEONLY;
        } else if (type == FS_OPEN_RW) {
            writetype = WTYPE_READWRITE;
        }
        if (nameinfo.options & NAMEOPT_NONBLOCKING) {
            writetype |= WTYPE_NONBLOCKING;
        }

        int8_t (*converter)(void *, packet_t*, uint8_t) =
            (type == FS_OPEN_DR) ? (provider->directory_converter) : NULL;


        // TODO: if provider->channel_* are not NULL, we should probably not allocate a channel
        // but that would break the FILE OPEN detection here.
        channel_t *channel = channel_find(channel_no);
        if (channel != NULL) {
            // clean up
            channel_close(channel_no);
            // Note: it seems possible to open the same channel multiple times
            // on a direct file
            if (type != FS_OPEN_DIRECT) {
                debug_puts("FILE OPEN ERROR");
                debug_putcrlf();
                set_error_tsd(errormsg, CBM_ERROR_NO_CHANNEL, 0, 0, nameinfo.drive);
                return -1;
            }
        }
        int8_t e = channel_open(channel_no, writetype, endpoint, converter, nameinfo.drive);
        if (e < 0) {
            debug_puts("E=");
            debug_puthex(e);
            debug_putcrlf();
            set_error_tsd(errormsg, CBM_ERROR_NO_CHANNEL, 0, 0, nameinfo.drive);
            return -1;
        }
    }

    activeslot->callback = callback;

    // no more error here, just the submit.
    // so callers can be sure if this function returns <0, they do not need
    // to close the channel, as it has not been opened
    // If this function returns 0, a callback must be received and handled,
    // and the channel is already opened.

    activeslot->channel_no = channel_no;

    // prepare response buffer
    packet_init(&activeslot->rxbuf, OPEN_RX_DATA_LEN, activeslot->rxdata);

    provider->submit_call(endpoint->provdata, channel_no, &activeslot->txbuf,
                          &activeslot->rxbuf, _file_open_callback);

    return 0;
}