// This function should return number of bytes read... not status. static SIZE_OR_ERROR FS_r_given_bus(struct one_wire_query *owq) { // Device locking occurs here struct parsedname *pn = PN(owq); SIZE_OR_ERROR read_or_error = 0; LEVEL_DEBUG("About to read <%s> extension=%d size=%d offset=%d",SAFESTRING(pn->path),(int) pn->extension,(int) OWQ_size(owq),(int) OWQ_offset(owq)); if (KnownBus(pn) && BusIsServer(pn->selected_connection)) { /* The bus is not local... use a network connection instead */ LEVEL_DEBUG("pid=%ld call ServerRead", pthread_self()); // Read afar -- returns already formatted in buffer read_or_error = ServerRead(owq); LEVEL_DEBUG("back from server"); //printf("FS_r_given_bus pid=%ld r=%d\n",pthread_self(), read_or_error); } else { STAT_ADD1(read_calls); /* statistics */ if (DeviceLockGet(pn) == 0) { read_or_error = FS_r_local(owq); // this returns status DeviceLockRelease(pn); LEVEL_DEBUG("return=%d", read_or_error); if (read_or_error >= 0) { // local success -- now format in buffer read_or_error = OWQ_parse_output(owq); // this returns nr. bytes } } else { LEVEL_DEBUG("Cannot lock bus to perform read") ; read_or_error = -EADDRINUSE; } } LEVEL_DEBUG("After read is performed (bytes or error %d)", read_or_error); Debug_OWQ(owq); return read_or_error; }
/* Write now that connection is set */ static ZERO_OR_ERROR FS_w_given_bus(struct one_wire_query *owq) { struct parsedname *pn = PN(owq); if ( BAD(TestConnection(pn)) ) { return -ECONNABORTED; } else if (KnownBus(pn) && BusIsServer(pn->selected_connection)) { return ServerWrite(owq); } else if (OWQ_pn(owq).type == ePN_real) { ZERO_OR_ERROR write_or_error = DeviceLockGet(pn); if (write_or_error == 0) { write_or_error = FS_w_local(owq); DeviceLockRelease(pn); } else { LEVEL_DEBUG("Cannot lock device for writing") ; } return write_or_error ; } else if ( IsInterfaceDir(pn) ) { ZERO_OR_ERROR write_or_error; BUSLOCK(pn); write_or_error = FS_w_local(owq); BUSUNLOCK(pn); return write_or_error ; } else { return FS_w_local(owq); } }
/* Read real device (Non-virtual). Will repeat 3 times if needed */ static SIZE_OR_ERROR FS_read_real(struct one_wire_query *owq) { struct parsedname *pn = PN(owq); SIZE_OR_ERROR read_or_error; /* First try */ /* in and bus_nr already set */ read_or_error = FS_read_distribute(owq); /* Second Try */ /* if not a specified bus, relook for chip location */ if (read_or_error < 0) { //error STAT_ADD1(read_tries[1]); if (SpecifiedBus(pn)) { // this bus or bust! if ( BAD(TestConnection(pn)) ) { read_or_error = -ECONNABORTED; } else { read_or_error = FS_read_distribute(owq); // 2nd try if (read_or_error < 0) { // third try STAT_ADD1(read_tries[2]); read_or_error = FS_read_distribute(owq); } } } else if (BusIsServer(pn->selected_connection)) { int bus_nr = pn->selected_connection->index ; // current selected bus INDEX_OR_ERROR busloc_or_error = ReCheckPresence(pn) ; // special handling or remote // only repeat if the bus number is wrong // because the remote does the rereads if ( bus_nr != busloc_or_error ) { if (busloc_or_error < 0) { read_or_error = -ENOENT; } else { read_or_error = FS_read_distribute(owq); if (read_or_error < 0) { // third try STAT_ADD1(read_tries[2]); read_or_error = FS_read_distribute(owq); } } } } else { INDEX_OR_ERROR busloc_or_error = ReCheckPresence(pn); // search again if (busloc_or_error < 0) { read_or_error = -ENOENT; } else { read_or_error = FS_read_distribute(owq); if (read_or_error < 0) { // third try STAT_ADD1(read_tries[2]); read_or_error = FS_read_distribute(owq); } } } } return read_or_error; }
static struct connection_in * NextServer( struct connection_in * in ) { do { if ( in == NO_CONNECTION ) { return NO_CONNECTION ; } if ( BusIsServer(in) ) { return in ; } in = in->next ; } while (1) ; }
/* Write to settings dir */ static ZERO_OR_ERROR FS_w_settings(struct one_wire_query *owq) { struct parsedname *pn = PN(owq); if ( BAD(TestConnection(pn)) ) { // safe for connection_in == NULL return -ECONNABORTED; } else if (KnownBus(pn) && BusIsServer(pn->selected_connection)) { return ServerWrite(owq); } else if ( pn->selected_connection != NO_CONNECTION ) { LEVEL_DEBUG("Attempt to write to local bus for /settings"); return -ENODEV ; } else { return FS_w_local(owq); } }
// Look on a given connection for the device static INDEX_OR_ERROR CheckThisConnection(int bus_nr, struct parsedname *pn) { struct parsedname s_pn_copy; struct parsedname * pn_copy = &s_pn_copy ; struct connection_in * in = find_connection_in(bus_nr) ; INDEX_OR_ERROR connection_result = INDEX_BAD ; if ( in == NO_CONNECTION ) { return INDEX_BAD ; } memcpy(pn_copy, pn, sizeof(struct parsedname)); // shallow copy pn_copy->selected_connection = in; if ( BAD( TestConnection(pn_copy) ) ) { // Connection currently disconnected return INDEX_BAD; } else if (BusIsServer(in)) { // Server if ( INDEX_VALID( ServerPresence(pn_copy) ) ) { connection_result = in->index; } } else if ( in->iroutines.flags & ADAP_FLAG_sham ) { return INDEX_BAD ; } else if ( in->iroutines.flags & ADAP_FLAG_presence_from_dirblob ) { // local connection with a dirblob (like fake, mock, ...) if ( GOOD( PresenceFromDirblob( pn_copy ) ) ) { connection_result = in->index ; } } else { // local connection but need to ask directly struct transaction_log t[] = { TRXN_NVERIFY, TRXN_END, }; if ( GOOD( BUS_transaction(t, pn_copy) ) ) { connection_result = in->index ; } } if ( connection_result == INDEX_BAD ) { LEVEL_DEBUG("Presence of "SNformat" NOT found on bus %s",SNvar(pn_copy->sn),SAFESTRING(DEVICENAME(in))) ; } else { LEVEL_DEBUG("Presence of "SNformat" FOUND on bus %s",SNvar(pn_copy->sn),SAFESTRING(DEVICENAME(in))) ; Cache_Add_Device(in->index,pn_copy->sn) ; // add or update cache */ } return connection_result ; }
/* We've reached a /bus.n entry */ static enum parse_enum Parse_Bus( INDEX_OR_ERROR bus_number, struct parsedname *pn) { static regex_t rx_p_bus ; struct ow_regmatch orm ; ow_regcomp( &rx_p_bus, "^/bus\\.[[:digit:]]+/?", REG_ICASE ) ; orm.number = 0 ; /* Processing for bus.X directories -- eventually will make this more generic */ if ( INDEX_NOT_VALID(bus_number) ) { return parse_error; } /* Should make a presence check on remote buses here, but * it's not a major problem if people use bad paths since * they will just end up with empty directory listings. */ if (SpecifiedLocalBus(pn)) { /* already specified a "bus." */ /* too many levels of bus for a non-remote adapter */ return parse_error; } else if (SpecifiedRemoteBus(pn)) { /* already specified a "bus." */ /* Let the remote bus do the heavy lifting */ pn->state |= ePS_busveryremote; return parse_first; } /* Since we are going to use a specific in-device now, set * pn->selected_connection to point at that device at once. */ if ( SetKnownBus(bus_number, pn) ) { return parse_error ; // bus doesn't exist } pn->state |= BusIsServer((pn)->selected_connection) ? ePS_busremote : ePS_buslocal ; if (SpecifiedLocalBus(pn)) { /* don't return bus-list for local paths. */ pn->control_flags &= (~SHOULD_RETURN_BUS_LIST); } /* Create the path without the "bus.x" part in pn->path_to_server */ if ( ow_regexec( &rx_p_bus, pn->path, &orm ) == 0 ) { strcpy( pn->path_to_server, orm.pre[0] ) ; strcat( pn->path_to_server, "/" ) ; strcat( pn->path_to_server, orm.post[0] ) ; ow_regexec_free( &orm ) ; } return parse_first; }
/* After parsing, but before sending to various devices. Will repeat 3 times if needed */ SIZE_OR_ERROR FS_read_postparse(struct one_wire_query *owq) { struct parsedname *pn = PN(owq); SIZE_OR_ERROR read_or_error; /* Normal read. Try three times */ LEVEL_DEBUG("%s", pn->path); STATLOCK; AVERAGE_IN(&read_avg); AVERAGE_IN(&all_avg); STATUNLOCK; /* First try */ STAT_ADD1(read_tries[0]); /* Check file type. */ if (pn->selected_device == NO_DEVICE || pn->selected_filetype == NO_FILETYPE) { if (KnownBus(pn) && BusIsServer(pn->selected_connection)) { /* Pass unknown remote filetype to remote owserver. */ read_or_error = FS_r_given_bus(owq); } else { /* Local unknown filetypes are directories. */ return -EISDIR; } } else { /* Local known filetypes are handled here. */ read_or_error = (pn->type == ePN_real) ? FS_read_real(owq) : FS_r_virtual(owq); } STATLOCK; if (read_or_error >= 0) { ++read_success; /* statistics */ read_bytes += read_or_error; /* statistics */ } AVERAGE_OUT(&read_avg); AVERAGE_OUT(&all_avg); STATUNLOCK; LEVEL_DEBUG("%s return %d", pn->path, read_or_error); return read_or_error; }
/* If error, try twice more */ static ZERO_OR_ERROR FS_write_real(int depth, struct one_wire_query *owq) { ZERO_OR_ERROR write_or_error; struct parsedname *pn = PN(owq); struct filetype * ft = pn->selected_filetype ; INDEX_OR_ERROR initial_bus = pn->selected_connection->index ; // current selected bus INDEX_OR_ERROR rechecked_bus ; if ( depth > 1 ) { LEVEL_DEBUG("Too many bus changes for write"); return -ENODEV ; } if ( ft->write == FS_w_alias ) { // Special check for alias // it's ok for fake and tester and mock as well // so do this before the fake test // also no need to three-peat. return FS_write_owq(owq) ; } /* Special case for "fake" adapter */ switch (get_busmode(pn->selected_connection)) { case bus_mock: // Mock -- write even "unwritable" to the cache for testing OWQ_Cache_Add(owq) ; // fall through case bus_fake: case bus_tester: return ( ft->write == NO_WRITE_FUNCTION ) ? -ENOTSUP : 0 ; default: // non-virtual devices get handled below break ; } /* First try */ /* in and bus_nr already set */ STAT_ADD1(write_tries[0]); write_or_error = FS_w_given_bus(owq); if ( write_or_error ==0 ) { return 0 ; } /* Second Try */ STAT_ADD1(write_tries[1]); if (SpecifiedBus(pn)) { // The bus number casn't be changed -- it was specified in the path write_or_error = FS_w_given_bus(owq); if ( write_or_error == 0 ) { return 0 ; } // The bus number casn't be changed -- it was specified in the path STAT_ADD1(write_tries[2]); return FS_w_given_bus(owq); } /* Recheck location */ /* if not a specified bus, relook for chip location */ rechecked_bus = ReCheckPresence(pn) ; if ( rechecked_bus < 0 ) { // can't find the location return -ENOENT ; } if ( initial_bus == rechecked_bus ) { // special handling for remote // only repeat if the bus number is wrong // because the remote does the rewrites if (BusIsServer(pn->selected_connection)) { return write_or_error ; } // try again STAT_ADD1(write_tries[1]); write_or_error = FS_w_given_bus(owq); if ( write_or_error == 0 ) { return 0 ; } // third try STAT_ADD1(write_tries[2]); return FS_w_given_bus(owq); } // Changed location retry everything LEVEL_DEBUG("Bus location changed from %d to %d\n",initial_bus,rechecked_bus); return FS_write_real(depth+1,owq); }