ARPDAS_QNX6 1.0
|
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 }