//*************************************************************************** // // Module Name: LOGIDRVR.C // // Driver for the Logitech 3D Mouse. // //*************************************************************************** // // Routines to configure and initiate communication to the 3D Mouse. // // Global Routines defined: logitech_init // logitech_open // logitech_update // logitech_close // cu_request_report // cu_request_operating_config // cu_reset_control_unit // cu_request_diagnostics // cu_demand_reporting // cu_incremental_reporting // cu_stream_reporting // cu_audio_on_mode // cu_audio_off_mode // cu_slave_transmitter_type // cu_filter_point // cu_set_rcvr_dimensions // cu_request_copyrights // cu_set_mplus_format // cu_set_mscomp_format // cu_request_std_config // cu_request_spec_config // Euler_record_2_absolute // update_operating_config_data // rts_reset // // Notes: // This driver assumes the EEPROM is either in default // mode (not programmed) or is programmed to 3D mode. // //*************************************************************************** // System includes #include // for malloc and free #include // for clock #include // for string routines // Other includes #include "logidrvr.h" // this driver's header file #include "asynch_0.h" // C Asynch Manager, level 0 #include "asynch_1.h" // C Asynch Manager, level 1 // Local function prototypes int reset_into_6d (config_data_struct *, unsigned char *); int reset_into_2d (config_data_struct *); void reset_control_unit (int); int get_valid_record (config_data_struct *, char *); int get_record (config_data_struct *, char *); int record_valid (config_data_struct *, char *); void default_operating_config_data (config_data_struct *); // External variables extern unsigned char open_line_state[MAX_COM_PORT]; // Static variables static int continuous_error_count = 0; // number of record read errors /**************************************************************************** Initializes the Configuration Data structure to default values. Parameters: config_data_struct * ptr to config data struct Returns: none ****************************************************************************/ void logitech_init (config_data_struct * config_data_ptr) { config_data_ptr->serial_com.com_port = COM2; config_data_ptr->serial_com.baud_rate = B_19200; config_data_ptr->serial_com.data_bits = EIGHT_DATA_BITS; config_data_ptr->serial_com.parity_bit = NO_PARITY; config_data_ptr->serial_com.stop_bit = ONE_STOP_BIT; config_data_ptr->comm_buffer = NIL; } /**************************************************************************** Initializes the serial port and validates proper 3D Mouse operation for either 3D Mode or 2D Mode. 2D Mode is indicated by Baud Rate being set to 1200 Baud in config_data_struct. Parameters: config_data_struct * ptr to config data struct unsigned char * ptr to hold diagnostic data int target dimension TWO_D_MODE - set control unit to 2d mode THREE_D_MODE - set control unit to 3d mode 0 - just configure com port Returns: int return_status Return Status OPEN_OK - Success OPEN_NO_MEMORY - Failure, could not allocate memory for serial communication OPEN_BAD_COMM_PARAM - Failure, could not program serial port due to bad communication parameter OPEN_NOT_CONNECTED - Failure, port not connected OPEN_BAD_COMPORT - Failure, port not connected OPEN_NO_DIAGNOSTICS_RETURNED - Failure, no diag data OPEN_BAD_DIAGNOSTICS- Failure, bad diagnostics OPEN_OUT_OF_RANGE - Failure, receiver not in range OPEN_BAD_RECORD - Failure, none or bad serial record OPEN_NO_2D_STATUS_RETURNED - Failure, no 2D status msg OPEN_BAD_2D_STATUS - Failure, bad 2D status msg ****************************************************************************/ int logitech_open (config_data_struct * config_data_ptr, unsigned char * diag_ptr, int target_dimension) { char record[MAX_RECORD_SIZE]; // buffer large enough to hold any record int ercode; int pparm; int return_status; int diag_status; int int_level = 0; int port_ads = 0; // reserve space for the i/o buffers if ((config_data_ptr->comm_buffer = malloc(COMM_QUEUE_SIZE)) == NIL) { return (OPEN_NO_MEMORY); } // open the serial port // The RTS line is being left in the default state, therefore the control // unit is not being reset by this open command. open_line_state[config_data_ptr->serial_com.com_port - 1] = NO_ASSERT; // set interrupt level and port address parameters switch (config_data_ptr->serial_com.com_port) { case COM3: int_level = 4; // IRQ 4 port_ads = 0x3E8; // 0000:03E8 break; case COM4: int_level = 3; // IRQ 3 port_ads = 0x2E8; // 0000:02E8 break; default: break; } ercode = open_a1 (config_data_ptr->serial_com.com_port, COMM_INPUT_QUEUE_SIZE, COMM_OUTPUT_QUEUE_SIZE, int_level, port_ads, config_data_ptr->comm_buffer); // force DTR and RTS to NEVER be asserted setop_a1 (config_data_ptr->serial_com.com_port, OPT_SET_LHW_FLOW_CNTRL_MSK, 0); setop_a1 (config_data_ptr->serial_com.com_port, OPT_SET_OPN_RDY_LHW_FLOW_CNTRL, 0); setop_a1 (config_data_ptr->serial_com.com_port, OPT_SET_OPN_UNRDY_LHW_FLOW_CNTL, 0); if (ercode == A_OK) { // verify a serial device is connected to our port // first gather the port data retop_a1 (config_data_ptr->serial_com.com_port, OPT_GET_SIMULATED_MSR, &pparm); // then, verify the CTS line is active if (pparm & CTS) { // some device is connected, excellent! // program the serial port ercode = set_com_port (&config_data_ptr->serial_com); if (ercode == A_OK) { // assume diagnostic will pass diag_status = TRUE; if (target_dimension == THREE_D_MODE) { // 3D Mode // start our communication with the control box return_status = (reset_into_6d (config_data_ptr, diag_ptr)); // Save failed status. This is necessary to update // operating config data even on BAD DIAGNOSTICS. if (return_status == OPEN_BAD_DIAGNOSTICS) { diag_status = FALSE; } if ((return_status == OPEN_OK) || (return_status == OPEN_BAD_DIAGNOSTICS)) { // request current mouse configuration return_status = update_operating_config_data (config_data_ptr); if (return_status == FALSE) { // Failed most likely because developer kit is connected. // Developer Kits with 09-28-91 firmware did not support // operating configuration data. // Fill operating configuration data with default for // developer kit. default_operating_config_data (config_data_ptr); } return_status = OPEN_OK; // request next report cu_request_report (config_data_ptr->serial_com.com_port); // collect first report and check validity if (get_valid_record (config_data_ptr, record) == GET_OK) { // this record is good, so clear the continuous error counter continuous_error_count = 0; switch (config_data_ptr->operating_config_data.report_data_type) { case EULER_REC_TYPE: // receiver in range? if (!(record[0] & logitech_OUTOFRANGEBIT)) { // receiver in range -- excellent! } else { // receiver out of range return_status = OPEN_OUT_OF_RANGE; } // request next report cu_request_report (config_data_ptr->serial_com.com_port); break; default: // request next report cu_request_report (config_data_ptr->serial_com.com_port); break; } } else { // no record or bad record received return_status = OPEN_INVALID_RECORD; } } } else if (target_dimension == TWO_D_MODE) { // 2D Mode return_status = reset_into_2d (config_data_ptr); } else { // Just configure com port return (OPEN_OK); } if (!diag_status) { return (OPEN_BAD_DIAGNOSTICS); } else { // return status of reset or particular failure return (return_status); } } else { // could not program the serial port return (OPEN_BAD_COM_PARAM); } } else { return (OPEN_NOT_CONNECTED); } } else { return (OPEN_BAD_COMPORT); } } /**************************************************************************** Collect a data report from the 3D Mouse. Currently the 3D Mouse must be in Euler record mode. Parameters: config_data_struct * ptr to config data struct raw_data_struct * ptr to store converted Euler data Returns: int return_status Return Status UPDATE_OK - Success UPDATE_INVALID_RECORD - Failure, no record or bad record received from 3D Mouse UPDATE_EXCESSIVE_ERRORS - Failure, too many continuous errors occurred UPDATE_NOT_ENOUGH_CHARACTERS - Record not received yet ****************************************************************************/ int logitech_update (config_data_struct * config_data_ptr, raw_data_struct * raw_data_ptr) { char record[MAX_RECORD_SIZE]; // buffer large enough to hold any record int return_status; int get_valid_status; // collect report and check validity get_valid_status = get_valid_record (config_data_ptr, record); switch (get_valid_status) { case GET_OK: // convert incoming record to absolute form Euler_record_2_absolute (record, raw_data_ptr); // this record is good, so clear the continuous error counter continuous_error_count = 0; // good status return_status = UPDATE_OK; switch (config_data_ptr->operating_config_data.reporting_mode) { case DEMAND_REPORTING: cu_request_report (config_data_ptr->serial_com.com_port); break; default: break; } break; case GET_NO_RECORD: switch (config_data_ptr->operating_config_data.reporting_mode) { case DEMAND_REPORTING: // this record is bad, increment the continuous error counter continuous_error_count++; if (continuous_error_count >= MAX_CONTINUOUS_ERRORS) { return_status = UPDATE_EXCESSIVE_ERRORS; } else { cu_request_report (config_data_ptr->serial_com.com_port); return_status = UPDATE_INVALID_RECORD; } break; case STREAM_REPORTING: // this record is bad, increment the continuous error counter continuous_error_count++; if (continuous_error_count >= MAX_CONTINUOUS_ERRORS) { return_status = UPDATE_EXCESSIVE_ERRORS; } else { return_status = UPDATE_INVALID_RECORD; } break; case INCREMENTAL_REPORTING: // don't consider it an error if record not received yet return_status = UPDATE_NOT_ENOUGH_CHARACTERS; break; default: break; } break; case GET_BAD_RECORD: switch (config_data_ptr->operating_config_data.reporting_mode) { case DEMAND_REPORTING: return_status = UPDATE_EXCESSIVE_ERRORS; break; case INCREMENTAL_REPORTING: case STREAM_REPORTING: // this record is bad, increment the continuous error counter continuous_error_count++; if (continuous_error_count >= MAX_CONTINUOUS_ERRORS) { // too many continuous errors, notify caller return_status = UPDATE_EXCESSIVE_ERRORS; } else { // Try to resync data stream. // place the control unit in demand mode cu_demand_reporting (config_data_ptr->serial_com.com_port); // wait for the command to be processed; 1000 ms delay_a1 ((unsigned long) 1000); // flush any possible inbound characters iflsh_a1 (config_data_ptr->serial_com.com_port); switch (config_data_ptr->operating_config_data.reporting_mode) { case STREAM_REPORTING: return_status = UPDATE_INVALID_RECORD; cu_stream_reporting (config_data_ptr->serial_com.com_port); break; case INCREMENTAL_REPORTING: return_status = UPDATE_INVALID_RECORD; cu_incremental_reporting (config_data_ptr->serial_com.com_port); break; default: return_status = UPDATE_EXCESSIVE_ERRORS; break; } } break; default: return_status = UPDATE_EXCESSIVE_ERRORS; break; } break; default: return_status = UPDATE_EXCESSIVE_ERRORS; break; } // return success status return (return_status); } /**************************************************************************** Returns any allocated memory and closes the serial port. Also shuts down audio output from speakers. Parameters: config_data_struct * ptr to config data struct Returns: TRUE - Success FALSE - Failure ****************************************************************************/ int logitech_close (config_data_struct * config_data_ptr) { int return_status; // reset the control box reset_control_unit (config_data_ptr->serial_com.com_port); // turn off audio output from unit cu_audio_off_mode (config_data_ptr->serial_com.com_port); // wait for the command to be processed; 150 ms delay_a1 ((unsigned long) 150); // close the com port and return success status return_status = close_a1 (config_data_ptr->serial_com.com_port); // free up any space I have taken if (config_data_ptr->comm_buffer != NIL) { free (config_data_ptr->comm_buffer); } // returns success or failure return (return_status == A_OK); } // ******************* local functions follow ******************** /**************************************************************************** Reset the 3D mouse and get diagnostic information. Parameters: config_data_struct * ptr to config data struct unsigned char * diag_ptr ptr to buffer to hold diagnostic data Returns: int return_status Return Status OPEN_OK - Success OPEN_BAD_COMPORT - Failure, port not connected OPEN_NO_DIAGNOSTICS_RETURNED - Failure, no diag data OPEN_BAD_DIAGNOSTICS- Failure, bad diagnostics ****************************************************************************/ int reset_into_6d (config_data_struct * config_data_ptr, unsigned char * diag_ptr) { unsigned char data[10]; int pno_read, piq_size; unsigned int pstatus; int return_status; // reset the control box reset_control_unit (config_data_ptr->serial_com.com_port); // place the control unit in demand mode cu_demand_reporting (config_data_ptr->serial_com.com_port); // wait for the command to be processed; 300 ms delay_a1 ((unsigned long) 300); // request diagnostics cu_request_diagnostics (config_data_ptr->serial_com.com_port); // wait for the incoming diagnostic report return_status = iwait_chars (config_data_ptr->serial_com.com_port, DIAGNOSTIC_SIZE, 1000); if (return_status == A_OK) { // collect diagnostics report return_status = rdst_a1 (config_data_ptr->serial_com.com_port, DIAGNOSTIC_SIZE, (char *) data, &pno_read, &piq_size, &pstatus); if (return_status == A_OK) { if (pno_read == DIAGNOSTIC_SIZE) { diag_ptr[0] = data[0]; // save diagnostics diag_ptr[1] = data[1]; } if ((pno_read == DIAGNOSTIC_SIZE) && (data[0] == 0xbf) && (data[1] == 0x3f)) { // all tests passed // return good status return (OPEN_OK); } else { // return bad status return (OPEN_BAD_DIAGNOSTICS); } } else { // return bad status return (OPEN_BAD_COMPORT); } } else { // return bad status return (OPEN_NO_DIAGNOSTICS_RETURNED); } } /**************************************************************************** Reset the 3D System into 2D mode and validate 2D status message. Parameters: config_data_struct * ptr to config data struct Returns: int return_status Return Status OPEN_OK - Success OPEN_BAD_COMPORT - Failure, port not connected OPEN_NO_2D_STATUS_RETURNED - Failure, no 2D status msg OPEN_BAD_2D_STATUS - Failure, bad 2D status msg ****************************************************************************/ int reset_into_2d (config_data_struct * config_data_ptr) { unsigned char data[10]; int pno_read, piq_size; unsigned int pstatus; int return_status; // Set the control unit to 3D mode. This is to flush out chars // in the serial port in case unit is already in 2D mode or the // unit is in a stream or incremental reporting mode. reset_control_unit (config_data_ptr->serial_com.com_port); // now set the control unit to 2D mode rts_reset (config_data_ptr->serial_com.com_port, TWO_D_MODE); // wait for the incoming 2D status msg return_status = iwait_chars (config_data_ptr->serial_com.com_port, TWO_D_STATUS_MSG_SIZE, 1000); if (return_status == A_OK) { // collect 2D status msg return_status = rdst_a1 (config_data_ptr->serial_com.com_port, TWO_D_STATUS_MSG_SIZE, (char *) data, &pno_read, &piq_size, &pstatus); if (return_status == A_OK) { if (pno_read == TWO_D_STATUS_MSG_SIZE) { if ((data[0] == 'M') && (data[1] == '3') && (data[2] == 0x24)) { // return good status return (OPEN_OK); } else { // return bad 2D status return (OPEN_BAD_2D_STATUS); } } else { // return bad status return (OPEN_BAD_COMPORT); } } } else { // return bad status return (OPEN_NO_2D_STATUS_RETURNED); } } /**************************************************************************** Set the control unit into demand reporting mode, send reset control to control unit, and flush all communication characters in the input queue. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void reset_control_unit (int port_num) { // reset control unit to 3D mode rts_reset (port_num, THREE_D_MODE); // send the reset command to the control unit // This command is necessary for firmware versions before 2.2 which // does not support RTS reset. cu_reset_control_unit (port_num); // wait for a bit; 1 second delay_a1 ((unsigned long) 1000); // flush any possible inbound characters iflsh_a1 (port_num); } /**************************************************************************** Read a single Red Baron record and check for validity Parameters: config_data_struct * ptr to config data struct char * buffer containing record Returns: int return_status Return Status GET_OK - Record received and valid GET_NO_RECORD - Record not received GET_BAD_RECORD - Record not valid ****************************************************************************/ int get_valid_record (config_data_struct * config_data_ptr, char record[MAX_RECORD_SIZE]) { // collect report if (get_record (config_data_ptr, record)) { // check for record validity if (record_valid (config_data_ptr, record)) { return (GET_OK); } else { return (GET_BAD_RECORD); } } else { return (GET_NO_RECORD); } } /**************************************************************************** Read a single Red Baron record from the serial port Parameters: config_data_struct * ptr to config data struct char * buffer containing record Returns: int return_status Return Status TRUE - Record received FALSE - Record not received ****************************************************************************/ int get_record (config_data_struct * config_data_ptr, char record[MAX_RECORD_SIZE]) { int pno_read, piq_size; unsigned int pstatus; int return_status = FALSE; // assume record won't be received int ercode; // wait for report to fully arrive // if report is already waiting, the routine will return immediately ercode = iwait_chars (config_data_ptr->serial_com.com_port, config_data_ptr->operating_config_data.report_size, 1000); if (ercode == A_OK) { // go get the record ercode = rdst_a1 (config_data_ptr->serial_com.com_port, config_data_ptr->operating_config_data.report_size, record, &pno_read, &piq_size, &pstatus); if (ercode == A_OK) { if (pstatus != STATUS_ERR) { // check for correct record size if ((unsigned int) pno_read == config_data_ptr->operating_config_data.report_size) { return_status = TRUE; } } } } // an error occur? if ((return_status == FALSE) && (config_data_ptr->operating_config_data.reporting_mode == DEMAND_REPORTING)) { // flush the input queue iflsh_a1 (config_data_ptr->serial_com.com_port); } // return status return (return_status); } /**************************************************************************** Examine record and determine validity. Parameters: config_data_struct * ptr to config data struct char * buffer containing record Returns: int return_status Return Status TRUE - Record Valid FALSE - Record Invalid ****************************************************************************/ int record_valid (config_data_struct * config_data_ptr, char record[MAX_RECORD_SIZE]) { unsigned int cnt; // loop counter int return_status = TRUE; // assume the record is valid switch (config_data_ptr->operating_config_data.report_data_type) { case EULER_REC_TYPE: // first, check that the flag bit exists if (record[0] & logitech_FLAGBIT) { // then, check that no other flag bit exists for (cnt = 1; cnt < config_data_ptr->operating_config_data.report_size; cnt++) { if (record[cnt] & logitech_FLAGBIT) { // flag bit is inappropriately set return_status = FALSE; break; } } } else { // flag bit is not set return_status = FALSE; } break; default: break; } // return status return (return_status); } /**************************************************************************** Send the request a single report command to the control unit. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void cu_request_report (int port_num) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, 'd'); } /**************************************************************************** Send the request for the operating configuration data struct to the control unit. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void cu_request_operating_config (int port_num) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, 'm'); } /**************************************************************************** Send the software reset command to the control unit. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void cu_reset_control_unit (int port_num) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, 'R'); } /**************************************************************************** Send the demand reporting command to the control unit. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void cu_demand_reporting (int port_num) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, 'D'); } /**************************************************************************** Send the incremental reporting command to the control unit. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void cu_incremental_reporting (int port_num) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, 'I'); } /**************************************************************************** Send the stream reporting command to the control unit. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void cu_stream_reporting (int port_num) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, 'S'); } /**************************************************************************** Send the diagnostics command to the control unit. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void cu_request_diagnostics (int port_num) { // send the request diagnostics command to the control unit wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, (char) 0x05); // ascii enq // allow the diagnostics to run; wait 1 second delay_a1 ((unsigned long) 1000); } /**************************************************************************** Send the speaker audio on command to the control unit. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void cu_audio_on_mode (int port_num) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, 'A'); } /**************************************************************************** Send the speaker audio off command to the control unit. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void cu_audio_off_mode (int port_num) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, 'O'); } /**************************************************************************** Send the set slave transmitter type command to the slave control unit. Parameters: int port_num Communication Port number connected to the control unit unsigned char transmitter_type Transmitter Type connected to the slave Returns: none ****************************************************************************/ void cu_slave_transmitter_type (int port_num, unsigned char transmitter_type) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, '$'); wrtch_a1 (port_num, (char) 0x2); wrtch_a1 (port_num, (char) CMD_TYPE_SLAVE_XMITTER_TYPE); wrtch_a1 (port_num, (char) transmitter_type); } /**************************************************************************** Send the set number of points in filter command to the control unit. Parameters: int port_num Communication Port number connected to the control unit unsigned char filter_point_count filter point count Returns: none ****************************************************************************/ void cu_filter_point (int port_num, unsigned char filter_point_count) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, '$'); wrtch_a1 (port_num, (char) 0x2); wrtch_a1 (port_num, (char) CMD_TYPE_FILTER_POINT); wrtch_a1 (port_num, (char) filter_point_count); } /**************************************************************************** Send the set receiver dimensions command to the control unit. Parameters: int port_num Comm Port connected to the control unit unsigned char rcvr_id ID of custom receiver unsigned int left_right_dist Left to Right Mic Distance (.001 inch units) unsigned int left_top_dist Left to Top Mic Distance (.001 inch units) unsigned char rcvr_buttons 1 = Buttons Disabled, 0 = Buttons Enabled Returns: none NOTE: Only the custom receiver dimensions may be altered. Also the maximum distance between any of the microphones is 10000 or 10 inches and the minimum distance between any of the microphones is 500 or .5 inches. If any of these values are sent the control unit shall default to the head tracker receiver dimensions. ****************************************************************************/ void cu_set_rcvr_dimensions (int port_num, unsigned char rcvr_id, unsigned int left_right_dist, unsigned int left_top_dist, unsigned char rcvr_buttons) { char *ch_ptr; wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, '$'); wrtch_a1 (port_num, (char) 0x7); wrtch_a1 (port_num, (char) CMD_TYPE_RCVR_DIMENSIONS); wrtch_a1 (port_num, (char) rcvr_id); ch_ptr = (char *) &left_right_dist; wrtch_a1 (port_num, (char) *ch_ptr++); wrtch_a1 (port_num, (char) *ch_ptr); ch_ptr = (char *) &left_top_dist; wrtch_a1 (port_num, (char) *ch_ptr++); wrtch_a1 (port_num, (char) *ch_ptr); wrtch_a1 (port_num, (char) rcvr_buttons); } /**************************************************************************** Send the set M+ format command to the control unit. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void cu_set_mplus_format (int port_num) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, 'X'); } /**************************************************************************** Send the set MS-Compatible format command to the control unit. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void cu_set_mscomp_format (int port_num) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, 'V'); } /**************************************************************************** Send the request Copyright and Version Number command to the control unit. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void cu_request_copyrights (int port_num) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, 'c'); } /**************************************************************************** Send the request Standard Configuration command to the control unit. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void cu_request_std_config (int port_num) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, '?'); } /**************************************************************************** Send the request Specific Configuration command to the control unit. Parameters: int port_num Communication Port number connected to the control unit Returns: none ****************************************************************************/ void cu_request_spec_config (int port_num) { wrtch_a1 (port_num, '*'); wrtch_a1 (port_num, '!'); } /**************************************************************************** Converts the Euler data from the control unit to the raw data structure form. Parameters: char * record ptr to Euler data from control unit raw_data_struct * ptr to store converted raw data Returns: none ****************************************************************************/ void Euler_record_2_absolute (char * record, raw_data_struct * raw_data_ptr) { long ax, ay, az; // integer form of absolute translational data short arx, ary, arz; // integer form of absolute rotational data // collect unit's miscellaneous information raw_data_ptr->buttons = (unsigned char) record[0] & (unsigned char) ~logitech_FLAGBIT; // gather the translational information // first sign extend if needed and then grab the rest of the information ax = (record[1] & 0x40) ? 0xFFE00000 : 0; ax |= (long)(record[1] & 0x7f) << 14; ax |= (long)(record[2] & 0x7f) << 7; ax |= (record[3] & 0x7f); ay = (record[4] & 0x40) ? 0xFFE00000 : 0; ay |= (long)(record[4] & 0x7f) << 14; ay |= (long)(record[5] & 0x7f) << 7; ay |= (record[6] & 0x7f); az = (record[7] & 0x40) ? 0xFFE00000 : 0; az |= (long)(record[7] & 0x7f) << 14; az |= (long)(record[8] & 0x7f) << 7; az |= (record[9] & 0x7f); // calculate the positional floating point values raw_data_ptr->position[X] = ((float) ax) / 1000.0; raw_data_ptr->position[Y] = ((float) ay) / 1000.0; raw_data_ptr->position[Z] = ((float) az) / 1000.0; // gather the rotational information arx = (record[10] & 0x7f) << 7; arx += (record[11] & 0x7f); ary = (record[12] & 0x7f) << 7; ary += (record[13] & 0x7f); arz = (record[14] & 0x7f) << 7; arz += (record[15] & 0x7f); // calculate the rotational floating point values raw_data_ptr->orientation[X] = ((float) arx) / 40.0; raw_data_ptr->orientation[Y] = ((float) ary) / 40.0; raw_data_ptr->orientation[Z] = ((float) arz) / 40.0; } /**************************************************************************** Requests operating configuration data from control unit and updates internal operating configuration data structure. Parameters: config_data_struct * ptr to config data struct Returns: int return_status Return Status TRUE - success FALSE - failure NOTE: Control unit must be in demand reporting mode prior to call. ****************************************************************************/ int update_operating_config_data (config_data_struct * config_data_ptr) { int pno_read, piq_size; unsigned int pstatus; int return_status = FALSE; // assume record won't be received int ercode; // request operating configuration data from control unit cu_request_operating_config (config_data_ptr->serial_com.com_port); // wait for report to fully arrive // if report is already waiting, the routine will return immediately ercode = iwait_chars (config_data_ptr->serial_com.com_port, OPERATING_CONFIG_SIZE, 1000); if (ercode == A_OK) { // go get the record ercode = rdst_a1 (config_data_ptr->serial_com.com_port, OPERATING_CONFIG_SIZE, &config_data_ptr->operating_config_data.firmware_version_indx, &pno_read, &piq_size, &pstatus); if (ercode == A_OK) { if (pstatus != STATUS_ERR) { // check for correct record size and msb is set in 1st byte if (((unsigned int) pno_read == OPERATING_CONFIG_SIZE) && (config_data_ptr->operating_config_data.firmware_version_indx & 0x80) != 0) { // mask off msb config_data_ptr->operating_config_data.firmware_version_indx &= 0x7f; return_status = TRUE; } } } } // an error occur? if (return_status == FALSE) { // flush the input queue iflsh_a1 (config_data_ptr->serial_com.com_port); } // return status return (return_status); } /**************************************************************************** Sets operating configuration data structure with default values specifically for Developer Kits. Parameters: config_data_struct * ptr to config data struct Returns: none ****************************************************************************/ void default_operating_config_data (config_data_struct * config_data_ptr) { config_data_ptr->operating_config_data.firmware_version_indx = FRMVERIDX_DEVELOPER_KIT_092891; config_data_ptr->operating_config_data.dim_mode = THREE_D_MODE; config_data_ptr->operating_config_data.reporting_mode = DEMAND_REPORTING; config_data_ptr->operating_config_data.report_data_type = EULER_REC_TYPE; config_data_ptr->operating_config_data.report_size = DK_EULER_REC_SIZE; config_data_ptr->operating_config_data.tracking_mode = MOUSE_MODE; config_data_ptr->operating_config_data.class_level = MASTER_MODE; config_data_ptr->operating_config_data.audio_level = ON_AUDIO; config_data_ptr->operating_config_data.rcvr_type = RCVR_TYPE_MOUSE; config_data_ptr->operating_config_data.transmitter_type = XMIT_TYPE_HEAD_TRACKER; config_data_ptr->operating_config_data.cur_rcvr_data.LeftMic_RightMic_Dist = DK_MOUSE_RCVR_TRI_LENGTH_AB; config_data_ptr->operating_config_data.cur_rcvr_data.LeftMic_TopMic_Dist = DK_MOUSE_RCVR_TRI_LENGTH_AC; config_data_ptr->operating_config_data.cur_rcvr_data.rcvr_supplement = 0; config_data_ptr->operating_config_data.cur_xmtr_data.LeftSpkr_RightSpkr_Dist = DK_HDTRK_XMIT_FRAME_LENGTH_AB; config_data_ptr->operating_config_data.cur_xmtr_data.LeftSpkr_TopSpkr_Dist = DK_HDTRK_XMIT_FRAME_LENGTH_AC; config_data_ptr->operating_config_data.cur_xmtr_data.LeftSpkr_CalibMic_Dist = DK_HDTRK_XMIT_FRAME_LENGTH_AM; return; } /**************************************************************************** Wait for specified number of characters to arrive in specified serial port. Parameters: int port_num Communication Port number connected to the control unit int num_chars Number of characters to wait for int timeout_ms Time (in milliseconds) to wait before timeing out Returns: int return_status Return Status A_OK - Success TIMEOUT - Failure, timed out before receiving specified number of characters INV_PORT - Failure, invalid port specified PORT_NOT_OPEN - Failure, port not open ****************************************************************************/ int iwait_chars (int port_num, int num_chars, int timeout_ms) { int return_status; int piq_size; unsigned pstatus; clock_t start; // start the timeout counting start = clock (); do { // get number of characters in the input queue return_status = qsize_a1 (port_num, &piq_size, &pstatus); if (return_status != A_OK) { return (return_status); } } while ((piq_size < num_chars) && ((clock () - start) <= timeout_ms)); if (piq_size < num_chars) { return (TIMEOUT); } else { return (A_OK); } } /**************************************************************************** Configures Serial Communication Port. Parameters: serial_com_data_struct * ptr to serial communication data struct Returns: int return_status Return Status A_OK - Success INV_PORT - Failure, invalid port number INV_OPT_PARAM - Failure, invalid port parmeter NOTE: This routine is necessary because C Asynch Manager Version 3.0 does not support 38.4K Baud. When a version finally does support 38.4K Baud, user should rewrite routines to utilize C Asynch Manager's version. ****************************************************************************/ int set_com_port (serial_com_data_struct * serial) { unsigned char data; // line control register information void far *address; int ioport, baud_divisor; // set ioport to base address of selected port's UART address = (void far *) &ioport; switch (serial->com_port) { case COM1: movedata (0, 0x400, FP_SEG (address), FP_OFF (address), 2); break; case COM2: movedata (0, 0x402, FP_SEG (address), FP_OFF (address), 2); break; case COM3: movedata (0, 0x404, FP_SEG (address), FP_OFF (address), 2); break; case COM4: movedata (0, 0x406, FP_SEG (address), FP_OFF (address), 2); break; default: return (INV_PORT); } // set baud divisor switch (serial->baud_rate) { case B_110: baud_divisor = 0x417; break; case B_150: baud_divisor = 0x300; break; case B_300: baud_divisor = 0x180; break; case B_600: baud_divisor = 0xC0; break; case B_1200: baud_divisor = 0x60; break; case B_2400: baud_divisor = 0x30; break; case B_4800: baud_divisor = 0x18; break; case B_9600: baud_divisor = 0x0C; break; case B_19200: baud_divisor = 0x6; break; case B_38400: baud_divisor = 0x3; break; default: return (INV_OPT_PARAM); } // save the data data = (unsigned char )inp (ioport + LINE_CTRL_REG); // set data value to configure data, stop, and parity bits data &= DATA_BIT_MASK; switch (serial->data_bits) { case SEVEN_DATA_BITS: data |= SEVEN_DATA_BITS_MASK; break; case EIGHT_DATA_BITS: data |= EIGHT_DATA_BITS_MASK; break; default: return (INV_OPT_PARAM); } data &= STOP_BIT_MASK; switch (serial->stop_bit) { case ONE_STOP_BIT: data |= ONE_STOP_BIT_MASK; break; case TWO_STOP_BITS: data |= TWO_STOP_BITS_MASK; break; default: return (INV_OPT_PARAM); } data &= PARITY_MASK; switch (serial->parity_bit) { case NO_PARITY: data |= NO_PARITY_MASK; break; case ODD_PARITY: data |= ODD_PARITY_MASK; break; case EVEN_PARITY: data |= EVEN_PARITY_MASK; break; default: return (INV_OPT_PARAM); } // set DLAB to permit access to divisor registers outp (ioport + LINE_CTRL_REG, data | 0x80); // set baud rate outp (ioport + BAUD_MSB, baud_divisor >> 8); outp (ioport + BAUD_LSB, baud_divisor & 0x00ff); // set data, stop, and parity bits outp (ioport + LINE_CTRL_REG, data); return (A_OK); } /**************************************************************************** Reset the control unit into 2D or 3D mode via the RTS and DTR lines on the serial interface. Parameters: int port_num Communication Port number connected to the control unit int target_mode TWO_D_MODE - 2D THREE_D_MODE - 3D Returns: int return_status Return Status TRUE - success FALSE - failure ****************************************************************************/ int rts_reset (int port_num, int target_mode) { void far *address; int ioport; // set ioport to base address of selected port's UART address = (void far *) &ioport; switch (port_num) { case COM1: movedata (0, 0x400, FP_SEG (address), FP_OFF (address), 2); break; case COM2: movedata (0, 0x402, FP_SEG (address), FP_OFF (address), 2); break; case COM3: movedata (0, 0x404, FP_SEG (address), FP_OFF (address), 2); break; case COM4: movedata (0, 0x406, FP_SEG (address), FP_OFF (address), 2); break; default: return (FALSE); // Bad COM Port Number break; } if (target_mode == THREE_D_MODE) { // set the DTR line low outp (ioport + MODEM_CTRL_REG, (inp (ioport + MODEM_CTRL_REG)) & 0xfe); } else if (target_mode == TWO_D_MODE) { // set the DTR line high outp (ioport + MODEM_CTRL_REG, (inp (ioport + MODEM_CTRL_REG)) | 0x01); } else { return (FALSE); // Bad target mode } // wait for DTR line to go to desired state delay_a1 (200L); // set the RTS line low outp (ioport + MODEM_CTRL_REG, (inp (ioport + MODEM_CTRL_REG)) & 0xfd); delay_a1 (200L); // set the RTS line high outp (ioport + MODEM_CTRL_REG, (inp (ioport + MODEM_CTRL_REG)) | 0x02); delay_a1 (20L); return (TRUE); }