ARPDAS_QNX6 1.0
subbus.c
Go to the documentation of this file.
00001 /* subbus-usb/src/subbus.c 
00002  */
00003 #include <sys/neutrino.h>
00004 #include <unistd.h>
00005 #include <fcntl.h>
00006 #include <string.h>
00007 #include <errno.h>
00008 #include <stdarg.h>
00009 #include "subbus.h"
00010 #include "nortlib.h"
00011 #include "nl_assert.h"
00012 #include "subbusd.h"
00013 
00014 #define SUBBUS_VERSION 0x501 /* subbus version 5.01 QNX6 */
00015 
00016 static int sb_fd = -1;
00017 static iov_t sb_iov[3];
00018 static subbusd_req_hdr_t sb_req_hdr;
00019 static subbusd_rep_t sb_reply;
00020 static char local_subbus_name[SUBBUS_NAME_MAX];
00021 
00022 unsigned short subbus_version = SUBBUS_VERSION;
00023 unsigned short subbus_subfunction; // undefined until initialization
00024 unsigned short subbus_features; // ditto
00025 
00026 /**
00027  @return Status reply from subbusd. Terminates if
00028  communication with subbusd fails.
00029  */
00030 static int send_to_subbusd( unsigned short command, void *data,
00031                 int data_size, unsigned short exp_type ) {
00032   int rv;
00033   if ( sb_fd == -1 )
00034     nl_error( 4, "Attempt to access subbusd before initialization" );
00035   int n_iov = 1;
00036   sb_req_hdr.command = command;
00037   if ( data_size > 0 ) {
00038     SETIOV( &sb_iov[1], data, data_size );
00039     ++n_iov;
00040   }
00041   rv = MsgSendv( sb_fd, sb_iov, n_iov, &sb_iov[2], 1 );
00042   if ( rv == -1 )
00043     nl_error( 3, "Error sending to subbusd: %s",
00044       strerror(errno) );
00045   nl_assert( rv >= sizeof(subbusd_rep_hdr_t) );
00046   if ( sb_reply.hdr.status < 0 ) 
00047     exp_type = SBRT_NONE;
00048   if ( sb_reply.hdr.ret_type != exp_type ) {
00049     nl_error( 4, "Return type for command %u should be %d, is %d",
00050       command, exp_type, sb_reply.hdr.ret_type );
00051   }
00052   switch ( sb_reply.hdr.ret_type ) {
00053     case SBRT_NONE:
00054       nl_assert( rv == sizeof(subbusd_rep_hdr_t));
00055       break;
00056     case SBRT_US:
00057       nl_assert( rv == sizeof(subbusd_rep_hdr_t) + sizeof(unsigned short));
00058       break;
00059     case SBRT_CAP:
00060       nl_assert( rv == sizeof(subbusd_rep_hdr_t) + sizeof(subbusd_cap_t));
00061       break;
00062     case SBRT_MREAD:
00063       break;
00064     default:
00065       nl_error( 4, "Unknown return type: %d", sb_reply.hdr.ret_type );
00066   }
00067   return sb_reply.hdr.status;
00068 }
00069 
00070 
00071 /** Initializes communications with subbusd driver.
00072     Returns library subfunction on success,
00073     zero on failure.
00074  */
00075 int load_subbus(void) {
00076   int rv;
00077   if ( sb_fd != -1 ) {
00078     nl_error( -2, "Attempt to reload subbus" );
00079     return subbus_subfunction;
00080   }
00081   sb_fd = open("/dev/huarp/subbus", O_RDWR );
00082   if ( sb_fd == -1 ) {
00083     nl_error( -2, "Error opening subbusd: %s", strerror(errno));
00084     return 0;
00085   }
00086   SETIOV( &sb_iov[0], &sb_req_hdr, sizeof(sb_req_hdr) );
00087   sb_req_hdr.iohdr.type = _IO_MSG;
00088   sb_req_hdr.iohdr.combine_len = 0;
00089   sb_req_hdr.iohdr.mgrid = SUBBUSD_MGRID;
00090   sb_req_hdr.iohdr.subtype = 0;
00091   sb_req_hdr.sb_kw = SB_KW;
00092   SETIOV( &sb_iov[2], &sb_reply, sizeof(sb_reply) );
00093   rv = send_to_subbusd( SBC_GETCAPS, NULL, 0, SBRT_CAP );
00094   if ( rv != SBS_OK )
00095     nl_error( 4, "Expected SBS_OK while getting capabilities" );
00096   subbus_subfunction = sb_reply.data.capabilities.subfunc;
00097   subbus_features = sb_reply.data.capabilities.features;
00098   strncpy(local_subbus_name, sb_reply.data.capabilities.name, SUBBUS_NAME_MAX);
00099   local_subbus_name[SUBBUS_NAME_MAX-1] = '\0'; // guarantee nul-term.
00100   return subbus_subfunction;
00101 }
00102 
00103 /**
00104  * Returns the hardware name string as originally retrieved from
00105  * subbusd during load_subbus().
00106  */
00107 char *get_subbus_name(void) {
00108   if ( sb_fd == -1 )
00109     nl_error( 4, "Attempt to read subbus_name before initialization" );
00110   return( local_subbus_name );
00111 }
00112 
00113 /**
00114  @return non-zero if hardware read acknowledge was observed.
00115  */
00116 int read_ack( unsigned short addr, unsigned short *data ) {
00117   int rv, rc;
00118   subbusd_req_data1 rdata;
00119 
00120   rdata.data = addr;
00121   rv = send_to_subbusd( SBC_READACK, &rdata, sizeof(rdata), SBRT_US );
00122   *data = sb_reply.data.value;
00123   switch ( rv ) {
00124     case SBS_ACK: rc = 1; break;
00125     case -ETIMEDOUT:
00126     case SBS_NOACK: rc = 0; break;
00127     default:
00128       nl_error( 4, "Invalid status response to read_ack(): %d",
00129         rv );
00130   }
00131   return rc;
00132 }
00133 
00134 /**
00135  @return Cached read value or zero if address is invalid.
00136  */
00137 unsigned short cache_read( unsigned short addr ) {
00138   int rv;
00139   subbusd_req_data1 rdata;
00140   unsigned short data;
00141 
00142   rdata.data = addr;
00143   rv = send_to_subbusd( SBC_READCACHE, &rdata, sizeof(rdata), SBRT_US );
00144   data = sb_reply.data.value;
00145   switch ( rv ) {
00146     case SBS_ACK: break;
00147     case -ETIMEDOUT:
00148     case SBS_NOACK: data = 0; break;
00149     default:
00150       nl_error( 4, "Invalid status response to cache_read(): %d",
00151         rv );
00152   }
00153   return data;
00154 }
00155 
00156 unsigned short read_subbus(unsigned short addr) {
00157   unsigned short data;
00158   read_ack(addr, &data);
00159   return data;
00160 }
00161 
00162 unsigned short sbrb(unsigned short addr) {
00163   unsigned int word;
00164   
00165   word = read_subbus(addr);
00166   if (addr & 1) word >>= 8;
00167   return(word & 0xFF);
00168 }
00169 
00170 /* returns zero if no acknowledge */
00171 unsigned short sbrba(unsigned short addr) {
00172   unsigned short word;
00173   
00174   if ( read_ack( addr, &word ) ) {
00175     if (addr & 1) word >>= 8;
00176     return( word & 0xFF );
00177   } else return 0;
00178 }
00179 
00180 /* returns zero if no acknowledge */
00181 unsigned int sbrwa(unsigned short addr) {
00182   unsigned short word;
00183   
00184   if ( read_ack( addr, &word ) )
00185     return word;
00186   else return 0;
00187 }
00188 
00189 /**
00190  @return non-zero value if the hardware acknowledge is
00191  observed. Historically, the value recorded the number
00192  of iterations in the software loop waiting for
00193  the microsecond timeout.
00194  */
00195 int write_ack(unsigned short addr, unsigned short data) {
00196   int rv, rc;
00197   subbusd_req_data0 wdata;
00198 
00199   wdata.address = addr;
00200   wdata.data = data;
00201   rv = send_to_subbusd( SBC_WRITEACK, &wdata, sizeof(wdata), SBRT_NONE );
00202   switch (rv ) {
00203     case SBS_ACK: rc = 1; break;
00204     case -ETIMEDOUT:
00205     case SBS_NOACK: rc = 0; break;
00206     default:
00207       nl_error( 4, "Invalid status response to write_ack(): %d",
00208         rv );
00209   }
00210   return rc;
00211 }
00212 
00213 /**
00214  @return non-zero value if the hardware acknowledge is
00215  observed. Historically, the value recorded the number
00216  of iterations in the software loop waiting for
00217  the microsecond timeout.
00218  */
00219 int cache_write(unsigned short addr, unsigned short data) {
00220   int rv, rc;
00221   subbusd_req_data0 wdata;
00222 
00223   wdata.address = addr;
00224   wdata.data = data;
00225   rv = send_to_subbusd( SBC_WRITECACHE, &wdata, sizeof(wdata), SBRT_NONE );
00226   switch (rv ) {
00227     case SBS_ACK: rc = 1; break;
00228     case -ETIMEDOUT:
00229     case SBS_NOACK: rc = 0; break;
00230     default:
00231       nl_error( 4, "Invalid status response to cache_write(): %d",
00232         rv );
00233   }
00234   return rc;
00235 }
00236 
00237 /** This is an internal function for sending messages
00238  * with a single unsigned short argument and a simple
00239  * status return.
00240  @return non-zero on success. Zero if unsupported.
00241  */
00242 static int send_CSF( unsigned short command, unsigned short val ) {
00243   int rv;
00244   subbusd_req_data1 csf_data;
00245 
00246   switch ( command ) {
00247     case SBC_SETCMDENBL:
00248     case SBC_SETCMDSTRB:
00249     case SBC_SETFAIL:
00250       break;
00251     default:
00252       nl_error( 4, "Invalid command in set_CSF: %d", command );
00253   }
00254   csf_data.data = val;
00255   rv = send_to_subbusd( command, &csf_data, sizeof(csf_data), SBRT_NONE );
00256   return( (rv == SBS_OK) ? 1 : 0 );
00257 }
00258 
00259 
00260 /** Set cmdenbl value.
00261  @return non-zero on success. Zero if not supported.
00262  */
00263 int set_cmdenbl(int val) {
00264   return send_CSF( SBC_SETCMDENBL, val ? 1 : 0);
00265 }
00266 
00267 /**
00268   Function did not exist at all before version 3.10, so
00269   programs intending to use this function should verify that
00270   the resident library version is at least 3.10. The feature
00271   word can also be checked for support, and that is consistent
00272   back to previous versions.
00273  @param value 1 turns on cmdstrobe, 0 turns off cmdstrobe
00274  @return non-zero on success, zero if operation isn't supported.
00275  */
00276 int set_cmdstrobe(int val) {
00277   return send_CSF(SBC_SETCMDSTRB, val ? 1 : 0);
00278 }
00279 
00280 /**
00281  Sets the value of a dedicated set of indicator lights, usually
00282  located on a control panel on the instrument and/or in the
00283  cockpit of the aircraft. For each bit of the input argument,
00284  a non-zero value indicates the associated light should be on.
00285  
00286  By convention, the least significant bit is associated with the
00287  main "fail light" located in the cockpit on aircraft instruments,
00288  indicating that the instrument is not acquiring data..
00289  This light (and the associated bit value on readback) will also
00290  be set by the system controller's two minute timeout circuit.
00291  
00292  @see read_failure()
00293  @param value Binary-encoded light settings.
00294  */
00295 int set_failure(unsigned short value) {
00296   return send_CSF( SBC_SETFAIL, value );
00297 }
00298 
00299 /** Internal function to handle read_switches() and read_failure(),
00300   which take no arguments, return unsigned short or zero if
00301   the function is not supported.
00302   */
00303 static unsigned short read_special( unsigned short command ) {
00304   int rv;
00305   rv = send_to_subbusd( command, NULL, 0, SBRT_US );
00306   return (rv == SBS_OK) ? sb_reply.data.value : 0;
00307 }
00308 
00309 /**
00310  Reads the positions of a dedicated set of system mode switches,
00311  usually located on a control panel on the instrument.
00312  @return The binary-encoded switch positions, or zero on error.
00313  */
00314 unsigned short read_switches(void) {
00315   return read_special( SBC_READSW );
00316 }
00317 
00318 /**
00319  The value reported represents the current state of the indicator
00320  lights. As noted in read_switches(), the least significant bit
00321  is associated the the main "fail light" located in the cockpit.
00322  This light can be lit via set_failure() or the system controller's
00323  two minute timeout circuit. In either case, read_failure() will
00324  report the actual state of the light.
00325  @return The binary-encoded value of the indicator light settings.
00326  */
00327 unsigned short read_failure(void) {
00328   return read_special( SBC_READFAIL );
00329 }
00330 
00331 /**
00332  Historically, tick_sic() has been associated with two timers.
00333  The first is a 2-second timeout that can reboot the system.
00334  The second is a 2-minute timeout that lights the main fail
00335  light indicating that the instrument is not acquiring data.
00336 
00337  It is unclear whether the new syscon_usb will support the
00338  reboot timer or rely on a motherboard-specific watchdog
00339  timer.
00340  */
00341 int tick_sic( void ) {
00342   return send_to_subbusd( SBC_TICK, NULL, 0, SBRT_NONE );
00343 }
00344 
00345 /**
00346  If system controller is associated with a watchdog timer
00347  that can reboot the system, this command disables that
00348  timer.
00349  */
00350 int disarm_sic(void) {
00351   return send_to_subbusd( SBC_DISARM, NULL, 0, SBRT_NONE );
00352 }
00353 
00354 int subbus_int_attach( char *cardID, unsigned short address,
00355       unsigned short region, struct sigevent *event ) {
00356   subbusd_req_data2 idata;
00357   nl_assert(cardID != NULL);
00358   strncpy( idata.cardID, cardID, 8);//possibly not nul-terminated
00359   idata.address = address;
00360   idata.region = region;
00361   idata.event = *event;
00362   return send_to_subbusd( SBC_INTATT, &idata, sizeof(idata), SBRT_US );
00363 }
00364 
00365 int subbus_int_detach( char *cardID ) {
00366   subbusd_req_data3 idata;
00367   nl_assert(cardID != NULL);
00368   strncpy( idata.cardID, cardID, 8);//possibly not non-terminated
00369   return send_to_subbusd( SBC_INTDET, &idata, sizeof(idata), SBRT_US );
00370 }
00371 
00372 /**
00373  * Requests subbusd to terminate. subbusd will wait until
00374  * all connections are closed.
00375  * @return SBS_OK on success.
00376  */
00377 int subbus_quit(void) {
00378   if ( sb_fd == -1 ) return 0;
00379   return send_to_subbusd( SBC_QUIT, NULL, 0, SBRT_NONE );
00380 }
00381 
00382 /**
00383  * Passes the raw command directly to the subbus driver and parses
00384  * the return string for a multi-read. Up to n_read values will be
00385  * written into the array pointed to by the data argument.
00386  * @return Zero on success. If return value is negative, it is the
00387  * error code returned by the subbusd driver and no values are reported.
00388  * If it is positive (SBS_NOACK), it indicates that although the
00389  * requested number of values are reported, at least one of the
00390  * values did not have an acknowledge, and a zero value was reported.
00391  */
00392 int mread_subbus( subbus_mread_req *req, unsigned short *data) {
00393   // rv should be the number of bytes retuned from subbusd into sb_reply.
00394   int rv;
00395   if ( req == NULL ) return 200;
00396   rv = send_to_subbusd( SBC_MREAD, req, req->req_len, SBRT_MREAD );
00397   if ( rv >= 0 ) {
00398     int i;
00399     nl_assert( req->n_reads == sb_reply.data.mread.n_reads );
00400     for ( i = 0; i < sb_reply.data.mread.n_reads; ++i ) {
00401       data[i] = sb_reply.data.mread.rvals[i];
00402     }
00403   }
00404   return rv;
00405 }
00406 
00407 /**
00408  Packages a request string into a newly allocated structure that
00409  can be passed to mread_subbus(). Called by pack_mread_request()
00410  and pack_mread_requests(). The req_str syntax is:
00411 
00412     <req>
00413       : M <count> '#' <addr_list> '\n'
00414     <addr_list>
00415       : <addr_list_elt>
00416       : <addr_list> ',' <addr_list_elt>
00417     <addr_list_elt>
00418       : <addr>
00419       : <addr> ':' <incr> ':' <addr>
00420       : <count> '@' <addr>
00421 
00422     <count>, <addr>, <incr> are all 1-4 hex digits
00423 
00424  @return the newly allocated request structure.
00425  */
00426 static subbus_mread_req *pack_mread( int req_len, int n_reads, const char *req_str ) {
00427   int req_size = 2*sizeof(unsigned short) + req_len + 1;
00428   subbus_mread_req *req = (subbus_mread_req *)new_memory(req_size);
00429   req->req_len = req_size;
00430   req->n_reads = n_reads;
00431   strcpy( req->multread_cmd, req_str );
00432   return req;
00433 }
00434 
00435 /**
00436  * Takes a zero-terminated list of addresses, generates the appropriate
00437  * text request string and invokes pack_mread().
00438  * @return the newly allocated request structure.
00439  */
00440 subbus_mread_req *pack_mread_requests( unsigned int addr, ... ) {
00441   unsigned short addrs[50];
00442   int n_reads = 0;
00443   
00444   { unsigned int val = addr;
00445     va_list va;
00446     if ( addr == 0 ) return NULL;
00447     va_start( va, addr );
00448     while ( val != 0 && n_reads < 50 ) {
00449       addrs[n_reads++] = (unsigned short) val;
00450       val =  va_arg(va, unsigned int);
00451     }
00452     va_end(va);
00453   }
00454   { char buf[256];
00455     int nc = 0;
00456     int space = 256;
00457     int i = 0;
00458     int nb;
00459 
00460     nb = snprintf( buf, space, "M%X#", n_reads );
00461     nl_assert(nb < space);
00462     nc += nb;
00463     space -= nb;
00464     while ( i < n_reads ) {
00465       nb = 0;
00466       if (i+2 < n_reads &&
00467           addrs[i] <= addrs[i+1] &&
00468           addrs[i+1] <= addrs[i+2] ) {
00469         unsigned d1 = addrs[i+1]-addrs[i];
00470         unsigned d2 = addrs[i+2]-addrs[i+1];
00471         if ( d1 == d2 ) {
00472           // We'll use either the s:i:e syntax or the n@a syntax.
00473           int j;
00474           for ( j = 2; i+j+1 < n_reads; ++j) {
00475             if ( addrs[i+j] + d1 != addrs[i+j+1] )
00476               break;
00477           }
00478           // Now we'll handle samples from i to i+j
00479           if ( d1 == 0 ) {
00480             nb = snprintf( buf+nc, space, "%X@%X,", j-i+1, addrs[i] );
00481           } else {
00482             nb = snprintf( buf+nc, space, "%X:%X:%X,",
00483               addrs[i], d1, addrs[i+j] );
00484           }
00485           i += j+1;
00486         }
00487       }
00488       if (nb == 0) {
00489         // We did not use an optimization, so just output an address
00490         nb = snprintf( buf+nc, space, "%X,", addrs[i++] );
00491       }
00492       if ( nb >= space ) {
00493         nl_error( 2, "Buffer overflow in pack_mread_requests()" );
00494         return NULL;
00495       }
00496       nc += nb;
00497       space -= nb;
00498     }
00499     // replace the trailing comma with a newline:
00500     buf[nc-1] = '\n';
00501     return pack_mread( nc, n_reads, buf );
00502   }
00503 }
00504 
00505 /**
00506  * Takes a multi-read <addr-list> string and invokes pack_mread().
00507  * @return the newly allocated request structure.
00508  */
00509 subbus_mread_req *pack_mread_request( int n_reads, const char *req ) {
00510   char buf[256];
00511   int space = 256;
00512   int nb;
00513 
00514   nb = snprintf( buf, space, "M%X#%s\n", n_reads, req );
00515   if ( nb >= space ) {
00516     nl_error( 2, "Buffer overflow in pack_mread_request()" );
00517     return NULL;
00518   }
00519   return pack_mread( nb, n_reads, buf );
00520 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Defines