;**************************************************************************** ; ; file: wusb-ls-main.asm ; Description: This code acts as a bridge for a WirelessUSB mouse and keyboard ; Target: Cypress CY7C63743 ; Version: 1.3.0000 ; ; This code is based on the logo.asm fw ; ; Overview: ; This firmware executes A single simple program loop. ; On reset, the firmware initializes the radio and then ; waits until enumerated by the USB host. ; It then continuously polls the radio "data ready" ; input, and when active it reads and processes ; radio received data. If valid, the received data ; is loaded into the EP1 FIFO, and EP1 is armed ; for transmission. ; ; USB: ; At bus reset the USB interface is re-initialized, ; AND the firmware soft reboots. We are then able to ; handle the standard chapter nine requests on ; endpoint zero (the control endpoint). After this ; device enumerates as A HID mouse on the USB, the ; requests come to endpoint one (the data endpoint). ; Endpoint one is used to send mouse displacement AND ; button status information. ; ; Pin Connections: ; ; ------------------- ; | P0[0] P0[4] | ; RX Data Rdy | P0[1] P0[5] | ; | P0[2] P0[6] | ; | P0[3] P0[7] | ; | P1[0] P1[1] | ; | P1[2] P1[3] | ; | P1[4] P1[5] | ; | P1[6] P1[7] | ; GND | VSS D+/SCLK | USB D+ / PS2 SCLK ; GND | VPP D-/SDATA| USB D- / PS2 SDATA ; PULLUP | VREG VCC | +5V ; | XTALIN XTALOUT | ; ------------------- ; ; Revisions: ; ;**************************************************************************** ; Copyright (2003), Cypress Semiconductor Corporation ; This software is owned by Cypress Semiconductor Corporation (Cypress) and is ; protected by United States copyright laws and international treaty provisions. ; Cypress hereby grants to Licensee a personal, non-exclusive, non-transferable ; license to copy, use, modify, create derivative works of, and compile the ; Cypress Source Code and derivative works for the sole purpose of creating ; custom software in support of Licensee product ("Licensee Product") to be used ; only in conjunction with a Cypress integrated circuit. Any reproduction, ; modification, translation, compilation, or representation of this software ; except as specified above is prohibited without the express written permission ; of Cypress. ; Disclaimer: Cypress makes no warranty of any kind, express or implied, with ; regard to this material, including, but not limited to, the implied warranties ; of merchantability and fitness for a particular purpose. Cypress reserves the ; right to make changes without further notice to the materials described herein. ; Cypress does not assume any liability arising out of the application or use of ; any product or circuit described herein. Cypress’ products described herein are ; not authorized for use as components in life-support devices. ; This software is protected by and subject to worldwide patent coverage, ; including U.S. and foreign patents. Use may be limited by and subject to the ; Cypress Software License Agreement. ; ;**************************************************************************** ;---------------- assembler directives -------------------- INCLUDE "config.inc" ; includes protocol configuration IFDEF PDC_9075 CPU 63743 ENDIF IFDEF PDC_9168 CPU 63723 ENDIF XPAGEON INCLUDE "637xx.inc" INCLUDE "wusb-ls-headers.inc" ;--------------- interrupt vector table ------------------- ORG 00h JMP reset ; reset vector JMP bus_reset ; bus reset interrupt JMP error ; 128us interrupt JMP ms_timer ; 1.024ms interrupt JMP endpoint0_stub ; endpoint 0 interrupt JMP error ; endpoint 1 interrupt JMP error ; endpoint 2 interrupt JMP error ; reserved JMP error ; Capture timer A interrupt Vector JMP error ; Capture timer B interrupt Vector JMP error ; GPIO interrupt JMP wakeup_isr ; Wake-up interrupt vector ;-------------- program listing --------------------------- ORG 1Ah error: JMP reset ;------------------------------------------------------- ; MACROS ;------------------------------------------------------- MACRO set_port_0 mask MOV A, mask OR [port0Shadow], A MOV A, [port0Shadow] IOWR port0 ENDM MACRO clr_port_0 mask MOV A, ~mask AND [port0Shadow], A MOV A, [port0Shadow] IOWR port0 ENDM MACRO set_port_1 mask MOV A, mask OR [port1Shadow], A MOV A, [port1Shadow] IOWR port1 ENDM MACRO clr_port_1 mask MOV A, ~mask AND [port1Shadow], A MOV A, [port1Shadow] IOWR port1 ENDM endpoint0_stub: ; cannot CALL directly from the interrupt table CALL endpoint0 ; into the upper 4K block RETI ;---------------------------------------------------------- ; ; Interrupt handler: wakeup_isr ; ;---------------------------------------------------------- wakeup_isr: RETI ;---------------------------------------------------------- ; ; Interrupt handler: bus_reset ; Purpose: The program jumps to this routine when ; the microcontroller has A bus reset. ; ;---------------------------------------------------------- bus_reset: MOV A, STALL_IN_OUT ; set to STALL INs&OUTs IOWR ep0_mode MOV A, ADDRESS_ENABLE ; enable USB address 0 IOWR usb_address MOV A, DISABLE ; disable endpoint1 IOWR ep1_mode IOWR ep2_mode MOV A, 00h ; reset program stack pointer MOV psp,A JMP reset ;---------------------------------------------------------- ; ; Interrupt: ms_timer ; Purpose: Every 1ms this interrupt handler clears ; the watchdog timer. ; ;---------------------------------------------------------- ms_timer: PUSH A PUSH X IOWR watchdog INC [app_timer] INC [timer] ; INC 1ms counter IFDEF MFG_TEST MOV A, [in_mfg_test] CMP A, 00h JNZ ms_timer_done ENDIF INC [led_timer] JNC ms_suspend_timer MOV A, f0hF0h ; prevent led_timer from overflowing MOV [led_timer], A ms_suspend_timer: IORD usb_status ; read bus activity bit AND A, BUS_ACTIVITY ; mask off activity bit JNZ bus_activity INC [suspend_count] ; increment suspend counter MOV A, [suspend_count] CMP A, 04h ; no bus activity for 4ms, suspend JNC prepare_suspend JMP ms_timer_done prepare_suspend: ; We get here if we need to go into suspend mode CALL radio_suspend CALL encore_suspend ; Put the enCoRe into suspend usb_suspend: ; Put the enCoRe into suspend MOV A, [remote_wakeup] ; if remote_wakeup enabled CMP A, 01h JNZ no_wake_up_int MOV A, (USB_RESET_INT | WAKEUP_INT) ; wake on USB_RESET_INT JMP global_i no_wake_up_int: MOV A, (USB_RESET_INT) ; wake on USB_RESET_INT global_i: IOWR global_int IORD control OR A, (SUSPEND|RUN) ; set suspend bit AND A, 11001111b ; clear the RESET bits EI IOWR control ;;; IN SUSPEND STATE HERE ;;; NOP ; This is first instruction executed after woken up ; Stay here if waking up while RESET IORD control AND A, 00110000b ; check for reset and brownout reset (just in case) JZ check_bus_activity MOV A, 00h MOV [remote_wakeup], A ; disable remote wakeup wait_reset: IOWR watchdog JMP wait_reset ;************************************************************** ; This section gets called during suspend due to the wake up ; timer. The bridge initiates a resume under the following ; conditions: ; 1. resume from system (bus activity) ; 2. resume from the bind button (force resume functionality) ; 3. resume from wireless device ;************************************************************** check_bus_activity: ; if bus activity, exit suspend IORD usb_status AND A, BUS_ACTIVITY JNZ prepare_resume while_host_is_suspended: ;DEFINE RESUME_FROM_BIND_BUTTON IFDEF RESUME_FROM_BIND_BUTTON CALL check_for_bind AND A, ffh JNZ prepare_resume ENDIF CALL rwu_poll_radio CMP A, 01h JNZ do_rwu_resume JMP usb_suspend prepare_resume: CALL encore_resume CALL radio_resume JMP do_rwu_resume ; saw wake up event do_rwu_resume: MOV A, [remote_wakeup] ; if remote_wakeup enabled CMP A, 01h JNZ back_in_business CALL signal_usb_resume MOV A,NAK_IN IOWR ep1_mode back_in_business: MOV A, (1MS_INT|USB_RESET_INT) IOWR global_int bus_activity: MOV A, 00h ; reset suspend counter MOV [suspend_count], A IORD usb_status AND A, ~BUS_ACTIVITY ; clear bus activity bit IOWR usb_status ms_timer_done: POP X POP A RETI ;---------------------------------------------------------- ; ; Interrupt handler: reset ; Purpose: The program jumps to this routine when ; the microcontroller has A power on reset. ; ;---------------------------------------------------------- reset: MOV A, (LVR_ENABLE | INTERNAL_CLK) ; set for use with internal oscillator IOWR clock_config MOV A, DSP_TOP ; set data stack pointer swap A, dsp MOV A, PSP_BASE ; set PC stack pointer MOV psp,A IOWR watchdog CALL radio_interface_init ; configure the GPIOs ; clear variables MOV A, 00h MOV [ep0_in_machine], A MOV [configuration], A MOV [ep1_stall], A MOV [idle], A MOV [suspend_count], A MOV [ep1_dmabuff0], A MOV [ep1_dmabuff1], A MOV [ep1_dmabuff2], A MOV [int_temp], A MOV [idle_timer], A MOV [idle_prescaler], A MOV [event_machine], A MOV [ep0_transtype], A MOV [pc_has_desc], A MOV [remote_wakeup], A ; remote wake up is disabled by default IFDEF MFG_TEST MOV [in_mfg_test], A ENDIF ; MFG_TEST MOV [c0h], A MOV [c1h], A MOV [c2h], A MOV [c3h], A MOV [c4h], A MOV [c5h], A MOV [c6h], A MOV [c7h], A MOV [c8h], A MOV [c9h], A MOV [cah], A MOV [cbh], A MOV [cch], A MOV [cdh], A MOV [ceh], A MOV [cfh], A MOV [button_pushed], A MOV [rssi_a], A MOV [rssi_b], A IFDEF AGC_SUPPORT MOV [corrupt_rssi], A MOV [agc_control], A ENDIF ; AGC_SUPPORT ;initialize application variables MOV [rx_counter], A MOV [packet_ready], A MOV [sequence_id_a], A MOV [app_timer], A IFDEF USE_DEVICE_ID_BIT MOV [sequence_id_b], A ENDIF ; USE_DEVICE_ID_BIT MOV [timer], A MOV [checksum_a], A MOV [checksum_b], A IFDEF BIND_AUTO MOV [corrupt_counter], A MOV [tx_ack_pending], A ENDIF IFDEF TWO_WAY_DATA MOV [tx_app_counter], A ENDIF IFDEF ENCRYPT_DATA_A MOV [encrypt_signature], A ENDIF MOV [bad_packets_a_low], A MOV [bad_packets_a_high], A MOV [good_packets_a_low], A MOV [good_packets_a_high], A MOV [bad_packets_b_low], A MOV [bad_packets_b_high], A MOV [good_packets_b_low], A MOV [good_packets_b_high], A MOV [led_state], A MOV [ep1_dmabuff + 0], A MOV [ep1_dmabuff + 1], A MOV [ep1_dmabuff + 2], A MOV [ep1_dmabuff + 3], A MOV [ep1_dmabuff + 4], A MOV [ep1_dmabuff + 5], A MOV [ep1_dmabuff + 6], A MOV [ep1_dmabuff + 7], A MOV [ep2_dmabuff + 0], A MOV [ep2_dmabuff + 1], A MOV [ep2_dmabuff + 2], A MOV [ep2_dmabuff + 3], A MOV [ep2_dmabuff + 4], A MOV [ep2_dmabuff + 5], A MOV [ep2_dmabuff + 6], A MOV [ep2_dmabuff + 7], A IFDEF DYNAMIC_PA_SUPPORT MOV A, PA_BIAS MOV [pa_bias_a], A MOV [pa_bias_b], A ENDIF ; DYNAMIC_PA_SUPPORT MOV A, 01h MOV [protocol], A MOV A, WAKEUP_ADJUST2|WAKEUP_ADJUST1|WAKEUP_ADJUST0|PRECISION_CLK_ENABLE|INTERNAL_CLK ;128*tWAKE IOWR clock_config ; set wakeup timer interval AND disable XTALOUT ; enable global interrupts MOV A, (1MS_INT | USB_RESET_INT) IOWR global_int ; enable endpoint 0 interrupt MOV A, EP0_INT IOWR endpoint_int ; enable USB address for endpoint 0 MOV A, ADDRESS_ENABLE IOWR usb_address MOV A,ffh MOV [led_timer], A IFDEF TWO_WAY_DATA MOV A, 50h ; THIS IS DEBUG CODE FOR TWO_WAY_DATA MOV [tx_app_buffer], A ENDIF ; TWO_WAY_DATA EI ; enable all interrupts MOV A, VREG_ENABLE ; enable USB pullup resistor IOWR usb_status ; to signal connection to host IFDEF MFG_TEST IORD port2 AND A, 30h CMP A, 30h JNZ not_se1_state CALL delay200us CALL delay200us IORD port2 AND A, 30h CMP A, 30h JNZ not_se1_state MOV A, FFh MOV [in_mfg_test], A JMP mfg_test_mode ; SE1 state signals mfg test mode not_se1_state: ENDIF ; MFG_TEST enum_wait: IOWR watchdog MOV A, [configuration] ; wait until configured CMP A, 01h JNZ enum_wait IFDEF ENDPOINT_1 ;send A null USB report on EP1 MOV A, NUM_EP1_BYTES ; set endpoint 1 to send defined number of bytes OR A, [ep1_data_toggle] IOWR ep1_count MOV A, ACK_IN ; set to ack on endpoint 1 IOWR ep1_mode ; AND flag that data is waiting IFDEF STARTUP_WAIT_ON_EP .desc_wait1: IOWR watchdog ; wait until PC takes that first null report IORD ep1_mode ; which indicates that PCHasDesc AND A, 0Fh CMP A, ACK_IN JZ .desc_wait1 CMP A, NAK_IN JZ .desc_wait_end1 MOV A, ACK_IN IOWR ep1_mode JMP .desc_wait1 .desc_wait_end1: MOV A, 01h MOV [pc_has_desc], A ENDIF ; STARTUP_WAIT_ON_EP ENDIF ; ENDPOINT_1 CALL app_init_a CALL app_init_b IFDEF BIND_AUTO JMP power_on_mode ENDIF ; BIND_AUTO ;********************************************************** ; ; connected_mode: ; ;********************************************************** connected_mode_init: IFNDEF BIND_AUTO CALL radio_init ; set up the radio CALL bind_init ; set up the PN_CODE AND channel CALL setup_rx ; set the radio into RX mode... ENDIF ; BIND_AUTO connected_mode: IFDEF BIND_AUTO CALL switch_rx ENDIF ; BIND_AUTO MOV A, 00h MOV [packet_type], A MOV [rx_counter], A IFDEF BIND_AUTO MOV [corrupt_counter], A MOV [rssi_counter], A ENDIF connected_loop: IOWR watchdog MOV A, 0 MOV [packet_ready], A IORD port0 AND A, IRQ JZ connected_bind_check CALL process_rx_int check_packet: MOV A, [rx_counter] ; sometimes process_rx_int triggers null length packets AND A, ffh ; at 32 AND 64 kbps JZ connected_loop MOV A, [packet_ready] AND A, bFLAG_EOF JZ connected_loop CALL verify_packet ; process packet on channel A MOV A, [packet_ready] ; check if the packet is valid AND A, bFLAG_VALID JZ process_corrupt_packet CALL switch_tx IFDEF BIND_AUTO CALL dec_corrupt_counter ; decrement the corrupt counter for each valid packet received ENDIF ; BIND_AUTO MOV A, [packet_type] ; Check for DATA packet CMP A, PT_DATA JZ process_data IFDEF BIND_AUTO MOV A, [packet_type] CMP A, PT_CONN_REQ JNZ conn_check_ping CALL process_conn_req CMP A, ffh ; A == ffh if A good conn req JNZ process_packet_complete MOV A, bWAIT_FOR_ACK_A MOV [tx_ack_pending], A ; wait for an ACK for this conn req MOV A, 0 MOV [timer], A ; reset the timer so that the ACK doesn't time out JMP process_packet_complete conn_check_ping: ; Check for PING packet MOV A, [packet_type] CMP A, PT_PING JNZ conn_check_null CALL send_ping_rsp JMP process_packet_complete ENDIF ; BIND_AUTO conn_check_null: MOV A, [packet_type] CMP A, PT_NULL JNZ conn_check_ack MOV A, [rx_counter] CMP A, LEN_NULL ; either 1 or 2 bytes long JNZ process_packet_complete IFDEF USE_DEVICE_ID_BIT MOV A, [rx_buffer] AND A, bDEVICE_ID_NULL JNZ conn_null_b ENDIF ; USE_DEVICE_ID_BIT conn_null_a: IFDEF USE_NULL_PKTS MOV A, [rx_buffer] ; store the received sequence_id MOV [sequence_id_a], A CALL send_ack_a MOV A, [rx_buffer] ASR ASR AND A, 03h ; A = two data bits from NULL header MOV X, 0 CALL app_data_received_a ENDIF ; USE_NULL_PKTS JMP process_packet_complete IFDEF USE_DEVICE_ID_BIT conn_null_b: IFDEF USE_NULL_PKTS MOV A, [rx_buffer] ; store the received sequence_id MOV [sequence_id_b], A CALL send_ack_b MOV A, [rx_buffer] ASR ASR AND A, 03h ; A = two data bits from NULL header MOV X, 0 CALL app_data_received_b ENDIF ; USE_NULL_PKTS JMP process_packet_complete ENDIF ; USE_DEVICE_ID_BIT conn_check_ack: MOV A, [packet_type] CMP A, PT_ACK JZ conn_process_ack IFDEF ENCRYPT_DATA_A MOV A, [packet_type] CMP A, PT_KEY_REQUEST JNZ process_packet_complete MOV A, [rx_counter] CMP A, 1 ; KEY_REQUEST packets must be 1 byte long JNZ process_packet_complete CALL process_key_req ENDIF process_packet_complete: ; process_data_a jumps to here when finished CALL switch_rx MOV A, 00h MOV [packet_ready], A MOV [packet_type], A MOV [rx_counter], A JMP connected_loop_complete connected_bind_check: MOV A, [timer] SUB A, BIND_POLL_TIMEOUT JC connected_loop MOV A, [rx_counter] CMP A, 0 JNZ connected_loop ; do not check RSSI or bind if we are in the middle of receiving a packet IFDEF BIND_BASIC CALL bind_process ; this should CALL bind_process every 48ms except when there is data... ENDIF IFDEF BIND_SEMI CALL check_for_bind ; if bind button is pushed, check_for_bind returns A non-zero A AND A, ffh JZ connected_check_rssi ; clear any pressed buttons CALL app_disconnect_a JMP bind_mode ENDIF ; BIND_SEMI IFDEF BIND_AUTO CALL check_for_bind ; if bind button is pushed, check_for_bind returns A non-zero A AND A, ffh JZ connected_check_rssi ; clear any pressed buttons CALL app_disconnect_a JMP bind_mode ENDIF ; BIND_AUTO connected_check_rssi: IFDEF USE_RSSI MOV A, [rssi_counter] ; multiply rssi_counter by 3 ASL A ADD A, [rssi_counter] ; ;7.14 TRY MOV [rssi_counter], AA, CONNECTED_MODE_RSSI CALL check_rssi ADD A, [rssi_counter] ; add current rssi reading to rssi_counter ASR A ASR A AND A, 03Fh ; ASR may not shift a 0 into top bit MOV [rssi_counter], A CMP A, [rssi_adjust]NUM_HIGH_RSSI_READINGS JC connected_rssi_reset CALL app_disconnect_a MOV A, PING_REASON_RSSI ; jumping to ping due to RSSI JMP ping_mode ; Change channels if this channel is too noisy connected_rssi_reset: CALL setup_rx ENDIF ; USE_RSSI IFDEF BIND_AUTO MOV A, 00h MOV [tx_ack_pending], A ENDIF ; BIND_AUTO connected_loop_complete: MOV A, 00h MOV [timer], A CALL clear_rx_packet CALL connected_leds MOV A, [app_timer] CMP A, APP_IDLE_TIMEOUT JC connected_loop CALL app_idle_a MOV A, 0 MOV [app_timer], A JMP connected_loop ;------------------------------------------------------- ; process_corrupt_packet_a ;------------------------------------------------------- process_corrupt_packet: IFDEF AGC_SUPPORT MOV A, [corrupt_rssi] ; multiply corrupt_rssi by 3 ASL A ADD A, [corrupt_rssi] MOV [corrupt_rssi], A CALL get_rssi ADD A, [corrupt_rssi] ; add current rssi reading to corrupt_rssi ASR A ASR A AND A, 03Fh ; ASR may not shift a 0 into top bit MOV [corrupt_rssi], A ; the corrupt packets could be caused by a device in close proximity CMP A, CORRUPT_RSSI_MAX_THRESHOLD JC process_corrupt_check_threshold MOV A, [corrupt_counter] CMP A, AGC_CORRUPT_THRESHOLD JC process_corrupt_check_threshold CALL radio_agc_on JMP process_packet_complete ENDIF ; AGC_SUPPORT process_corrupt_check_threshold: IFDEF BIND_AUTO MOV A, [corrupt_counter] CMP A, CORRUPT_THRESHOLD JC process_packet_complete process_corrupt_disconnect: CALL app_disconnect_a MOV A, PING_REASON_CORRUPT JMP ping_mode ENDIF ; BIND_AUTO process_corrupt_okay: INC [bad_packets_a_low] JNC corrupt_link_qual_b INC [bad_packets_b_high] corrupt_link_qual_b: INC [bad_packets_a_low] JNC process_packet_complete INC [bad_packets_b_high] JMP process_packet_complete ;------------------------------------------------------- ; process_data ; ; Checks for retransmitted packet AND unchanged data ; ; NOTE: process_data falls through to send_ack ;------------------------------------------------------- process_data: MOV A, [rx_counter] CMP A, 2 JC process_packet_complete IFDEF USE_DEVICE_ID_BIT MOV A, [rx_buffer] AND A, bDEVICE_ID_DATA JNZ process_data_b ENDIF ; USE_DEVICE_ID_BIT process_data_a: MOV A, CONNECTED_MODE_RSSI CALL getcheck_rssi ADD A, [rssi_a] ASR A AND A, 7Fh MOV [rssi_a], A IFDEF AGC_SUPPORT CMP A, RSSI_MIN_AGC_THRESHOLD JNC check_seq_a ; Is the low signal RSSI caused by AGC being on? CALL radio_agc_off ENDIF check_seq_a: MOV A, [sequence_id_a] AND A, FFh JZ store_seq_a CMP A, [rx_buffer] ; compare last received sequence_id with sequence_id byte JNZ store_seq_a MOV X, [rx_counter] MOV A, [X+rx_buffer-1] CMP A, [checksum_a] JNZ store_seq_a CALL send_ack_a INC [bad_packets_a_low] JNC check_seq_a_link_qual_b INC [bad_packets_a_high] check_seq_a_link_qual_b: INC [bad_packets_b_low] JNC process_packet_complete INC [bad_packets_b_high] JMP process_packet_complete store_seq_a: MOV X, [rx_counter] DEC X ; substract header and checksum DEC X MOV A, 00h ; to be sent. CALL app_verify_packet_a ; IF A == 0 then packet is bad, else packet is good AND A, ffh JZ process_packet_complete MOV A, [rx_buffer] ; store the received sequence_id MOV [sequence_id_a], A MOV X, [rx_counter] MOV A, [X+rx_buffer-1] MOV [checksum_a], A ; store the checksum CALL send_ack_a MOV X, [rx_counter] DEC X ; substract header and checksum DEC X MOV A, 00h IFDEF ENCRYPT_DATA_A MOV [encrypt_signature], A ENDIF CALL app_data_received_a ; app-specific routine to format the data and hand it off to app good_link_qual_a: INC [good_packets_a_low] JNC good_link_qual_a_b INC [good_packets_a_high] JNC good_link_qual_a_b ; reset corrupt packet count if total packet count rolled over MOV A, 0 MOV [bad_packets_a_low], A MOV [bad_packets_a_high], A good_link_qual_a_b: INC [good_packets_b_low] JNC process_packet_complete INC [good_packets_b_high] JNC process_packet_complete ; reset corrupt packet count if total packet count rolled over MOV A, 0 MOV [bad_packets_b_low], A MOV [bad_packets_b_high], A JMP process_packet_complete IFDEF USE_DEVICE_ID_BIT process_data_b: MOV A, CONNECTED_MODE_RSSI ; check the RSSI of the channel CALL getcheck_rssi ADD A, [rssi_b] ASR A AND A, 7Fh MOV [rssi_b], A IFDEF AGC_SUPPORT CMP A, RSSI_MIN_AGC_THRESHOLD JNC check_seq_b ; Is the low signal RSSI caused by AGC being on? CALL radio_agc_off ENDIF ; AGC_SUPPORT check_seq_b: MOV A, [sequence_id_b] AND A, FFh JZ store_seq_b MOV A, [sequence_id_b] ; Check for retransmitted packet CMP A, [rx_buffer] ; compare last received sequence_id with sequence_id byte JNZ store_seq_b MOV X, [rx_counter] MOV A, [X+rx_buffer-1] CMP A, [checksum_b] JNZ store_seq_b CALL send_ack_b ; IF same THEN discard packet, but send an ACK INC [bad_packets_a_low] JNC check_seq_b_link_qual_b INC [bad_packets_a_high] check_seq_b_link_qual_b: INC [bad_packets_b_low] JNC process_packet_complete INC [bad_packets_b_high] JMP process_packet_complete store_seq_b: MOV X, [rx_counter] DEC X ; substract header AND checksum DEC X MOV A, 0 CALL app_verify_packet_b ; IF A == 0 then packet is bad, else packet is good AND A, ffh JZ process_packet_complete MOV A, [rx_buffer] ; store the received sequence_id MOV [sequence_id_b], A MOV X, [rx_counter] MOV A, [X+rx_buffer-1] MOV [checksum_b], A ; store the checksum CALL send_ack_b MOV X, [rx_counter] DEC X ; substract header AND checksum DEC X MOV A, 0 CALL app_data_received_b ; app-specific routine to format the data AND hand it off to USB good_link_qual_b: INC [good_packets_b_low] JNC good_link_qual_b_a INC [good_packets_b_high] JNC good_link_qual_b_a ; reset corrupt packet count if total packet count rolled over MOV A, 0 MOV [bad_packets_b_low], A MOV [bad_packets_b_high], A good_link_qual_b_a: INC [good_packets_a_low] JNC process_packet_complete INC [good_packets_a_high] JNC process_packet_complete ; reset corrupt packet count if total packet count rolled over MOV A, 0 MOV [bad_packets_a_low], A MOV [bad_packets_a_high], A JMP process_packet_complete JMP process_packet_complete ENDIF ; USE_DEVICE_ID_BIT ;------------------------------------------------------- ; get_rssi ;------------------------------------------------------- get_rssi: MOV A, REG_RSSI MOV [reg_addr], A CALL SPI_read CALL SPI_read ; RWW we may not need this SPI_read MOV A, [reg_data] AND A, mRSSI ; mask-off high-order 3 bits RET ;------------------------------------------------------- ; send_ack ;------------------------------------------------------- send_ack_a: IFDEF DYNAMIC_PA_SUPPORT CALL set_pa_a ENDIF ; DYNAMIC_PA_SUPPORT MOV A, [batt_report_req_a] ; batt_report_req_a can be 0 or 1. MOV [tx_app_counter], A ; When tx_app_counter = 1 we send ACK_DATA instead of ACK ; Put the KBD signature in the data byte of ACK_DATA ; If batt_report_req_a == 0, signature will be overwritten by ACK SWAP A,X MOV A, 74h MOV [X + tx_app_header], A MOV A, 0h ; Reset batt_report_req_a to 0 MOV [batt_report_req_a], A MOV A, [sequence_id_a] ASR AND A, 02h ; clear everything but the data toggle bit IFDEF USE_DEVICE_ID_BIT JMP send_ack send_ack_b: IFDEF DYNAMIC_PA_SUPPORT CALL set_pa_b ENDIF ; DYNAMIC_PA_SUPPORT MOV A, [batt_report_req_b] ; batt_report_req_b can be 0 or 1. MOV [tx_app_counter], A ; When tx_app_counter = 1 we send ACK_DATA instead of ACK ; Put the Mouse signature in the data byte of ACK_DATA ; If batt_report_req_b == 0, signature will be overwritten by ACK SWAP A,X MOV A, 1Eh MOV [X + tx_app_header], A MOV A, 0h ; Reset batt_report_req_b to 0 MOV [batt_report_req_b], A MOV A, [sequence_id_b] ASR AND A, 02h ; clear everything but the data toggle bit OR A, bDEVICE_ID_ACK ENDIF ; USE_DEVICE_ID_BIT send_ack: IFDEF TWO_WAY_DATA MOV [ls_temp], A MOV A, [tx_app_counter] ; if tx_app_counter > 0, send_ack_data_a AND A, ffh ; MOV doesn't set zero flag JNZ send_ack_data MOV A, [ls_temp] ENDIF ; TWO_WAY_DATA OR A, PT_ACK ; set header nibble as ACK OR A, bHEADER_FLAG MOV [tx_sys_buffer], A CALL set_sys_parity ; set the parity bit MOV A, LEN_ACK ; ACK Packet length MOV [tx_sys_counter], A IFDEF CHECKSUM_ALL_PACKETS MOV A, [checksum_seed] ; A = checksum seed CALL set_sys_checksum ; calculate the checksum ENDIF ; CHECKSUM_ALL_PACKETS CALL delay50us ; the HID isn't turning around as fast as the bridge... CALL delay50us CALL delay50us CALL delay50us CALL delay50us CALL delay50us CALL transmit_sys IFDEF TWO_WAY_DATA MOV A, 00h MOV [tx_ack_pending], A ; clear tx_ack_pending flag ENDIF ; TWO_WAY_DATA RET IFDEF TWO_WAY_DATA ;------------------------------------------------------- ; send_ack_data ;------------------------------------------------------- send_ack_data: MOV A, [rx_buffer] ; extract the data toggle bit from the recvd packet ASR A AND A, 02h ; OR A, PT_ACKDATA ; set packet type OR A, bHEADER_FLAG ; set flag for positive ACK MOV [tx_app_header], A MOV A, [tx_ack_pending] ; IF the ACK/Data was previously sent, but AND A, bWAIT_FOR_ACK_A ; an ACK was not received, do not toggle the JNZ send_ack_data_parity ; the data toggle MOV A, bWAIT_FOR_ACK_A MOV [tx_ack_pending], A ; set tx_ack_pending to a non-zero value MOV A, [tx_data_toggle] AND A, ffh JNZ send_ack_data_toggle_off MOV A, [tx_app_header] ; set the TX data toggle bit OR A, bTX_DATA_TOGGLE MOV [tx_app_header], A MOV [tx_data_toggle], A ; set the tx_data_toggle JMP send_ack_data_parity send_ack_data_toggle_off: MOV A, 0 MOV [tx_data_toggle], A ; clear the tx_data_toggle send_ack_data_parity: CALL set_app_parity INC [tx_app_counter] ; increment the counter to include the data byte INC [tx_app_counter] ; increment the counter to include the checksum CALL set_app_checksum CALL transmit_app MOV A, 00h MOV [tx_app_counter], A ; clear so that ACK/DATA won't be sent again RET ;------------------------------------------------------- ; transmit_app_packet ; ;------------------------------------------------------- transmit_app_packet: MOV A, PT_DATA ; set packet type OR A, bHEADER_FLAG ; set flag for positive ACK MOV [tx_app_header], A MOV A, [tx_data_toggle] AND A, ffh JNZ transmit_app_toggle_off MOV A, [tx_app_header] ; set the TX data toggle bit OR A, bTX_DATA_TOGGLE MOV [tx_app_header], A MOV [tx_data_toggle], A ; set the tx_data_toggle JMP transmit_app_parity transmit_app_toggle_off: MOV A, 0 MOV [tx_data_toggle], A ; clear the tx_data_toggle transmit_app_parity: CALL set_app_parity INC [tx_app_counter] ; increment the counter to include the header byte INC [tx_app_counter] ; increment the counter to include the checksum CALL set_app_checksum CALL transmit_app RET ENDIF ; TWO_WAY_DATA ;------------------------------------------------------- ; conn_process_ack ;------------------------------------------------------- conn_process_ack: IFNDEF TWO_WAY_DATA IFDEF BIND_AUTO MOV A, [rx_counter] CMP A, LEN_ACK JNZ process_packet_complete MOV A, [tx_ack_pending] AND A, bWAIT_FOR_ACK_A JNZ wait_for_ack_received INC [corrupt_counter] ; if we receive an unexpected valid ACK someone else is on our channel/PN code MOV A, [corrupt_counter] CMP A, CORRUPT_THRESHOLD JC wait_for_ack_received ; check the corrupt threshold CALL app_disconnect_a MOV A, PING_REASON_CORRUPT JMP ping_mode wait_for_ack_received: MOV A, 0 MOV [tx_ack_pending], A JMP process_packet_complete ENDIF ; BIND_AUTO ELSE ; TWO_WAY_DATA MOV A, [tx_ack_pending] ; are we waiting for an ACK? AND A, bWAIT_FOR_ACK_A JNZ conn_process_ack_expected ; We're not waiting for an ACK, it's probably unsolicited IFDEF BIND_AUTO INC [corrupt_counter] MOV A, [corrupt_counter] CMP A, CORRUPT_THRESHOLD JC conn_process_ack_complete CALL app_disconnect_a MOV A, PING_REASON_CORRUPT JMP ping_mode ELSE ; BIND_AUTO JMP conn_process_ack_complete ENDIF ; BIND_AUTO conn_process_ack_expected: MOV A, [rx_buffer] AND A, bHEADER_FLAG JZ conn_process_ack_complete ; Bad ACK... do not clear flags MOV A, 00h ; good ACK, clear tx_app_counter MOV [tx_app_counter], A MOV [tx_ack_pending], A JMP conn_process_ack_complete ENDIF ; TWO_WAY_DATA conn_process_ack_complete: JMP process_packet_complete ;**************************************************************************** ; encore_suspend ; ; Processing: ; The radio is put into Suspend state ; ;**************************************************************************** encore_suspend : ; Set all pins to resistive mode if possible to prevent current flow during suspend. ; Set Port 1.0 to Hiz, others to resistive (data=1!) for low power MOV A, P1_MODE_1_SUSPEND IOWR port1_mode1 MOV A, P1_MODE_0_SUSPEND IOWR port1_mode0 MOV A, P1_DATA_SUSPEND IOWR port1 ; Set Port 0 to resistive mode MOV A, P0_MODE_1_SUSPEND IOWR port0_mode1 MOV A, P0_MODE_0_SUSPEND IOWR port0_mode0 MOV A, P0_DATA_SUSPEND IOWR port0 RET ;**************************************************************************** ; encore_resume ; ; Processing: ; The radio is put into Suspend state ; ;**************************************************************************** encore_resume : ;Put the GPIOs back to their normal state MOV A, [port0Shadow] IOWR port0 MOV A, P0_MODE_0_DEFAULT IOWR port0_mode0 MOV A, P0_MODE_1_DEFAULT IOWR port0_mode1 MOV A, [port1Shadow] IOWR port1 MOV A, P1_MODE_0_DEFAULT IOWR port1_mode0 MOV A, P1_MODE_1_DEFAULT IOWR port1_mode1 RET ;**************************************************************************** ; signal_usb_resume ; ; Processing: ; The USB bus is forced into resume state ; ;**************************************************************************** signal_usb_resume: MOV A, VREG_ENABLE | CONTROL1 ; force J (D+ Low, D- High) IOWR usb_status MOV A, VREG_ENABLE | CONTROL0 ; force K (D+ High, D- Low) IOWR usb_status MOV A, 10h ; wait (USB Spec is 1ms - 15ms) MOV X, A usb_res_timer_1: IOWR watchdog MOV A, FFh usb_res_timer_0: DEC A JNZ usb_res_timer_0 DEC X JNZ usb_res_timer_1 MOV A, VREG_ENABLE | CONTROL1 ; force J - to prevent SE1 on the bus IOWR usb_status MOV A, VREG_ENABLE ; disable forcing IOWR usb_status MOV A, 0h ; Flag that we have signalled resume on USB MOV [remote_wakeup], A ; Keeps us from re-signalling resume (for ill-behaved USB boxes) MOV [sequence_id_a], A ; Ensures that the wakeup packet actually wakes-up the host MOV [sequence_id_b], A RET ;**************************************************************************** ; rwu_poll_radio ; ; Processing: ; The radio is polled during suspend to allow remote wakeup from HID device ; ; Output: ; A == 1 ==> radio was resuspended (no remote wakeup) ; A == 0 ==> radio was not resuspended (remote wakeup activated) ; ;**************************************************************************** rwu_timer_0: equ C8h rwu_timer_1: equ C9h rwu_resuspend: equ CAh rwu_poll_radio: CALL encore_resume CALL radio_resume MOV A,00h MOV [packet_ready],A ; packet_ready = 0; MOV [rx_counter],A ; rx_counter = 0; MOV A,01h MOV [rwu_resuspend],A ; rwu_resuspend = 1; MOV A,06h ; ~ ms ON time of radio MOV [rwu_timer_1],A ; Period is ~ 270 ms ; 0A = 10 ms ON, 260 ms OFF .rwu_timer_1_loop: IOWR watchdog ; WATCHDOG = AC; MOV A,ffh MOV [rwu_timer_0],A ; rwu_timer_0 = 0xFF; ; do .rwu_timer_0_loop: IORD port0 ; AC = PORT0; AND A,IRQ ; if(!(AC & IRQ)) JZ .dec_rwu_timer_0 .rwu_poll_radio: CALL process_rx_int MOV A,[rx_counter] ; if(rx_counter) CMP A,00h JZ .dec_rwu_timer_0 MOV A,00h MOV [rwu_resuspend],A ; rwu_resuspend = 0; .dec_rwu_timer_0: DEC [rwu_timer_0] ; rwu_timer_0--; MOV A,[rwu_timer_0] ; while(rwu_timer_0 && rwu_resuspend); CMP A,00h JZ .dec_rwu_timer_1 MOV A,[rwu_resuspend] CMP A,00h JNZ .rwu_timer_0_loop .dec_rwu_timer_1: DEC [rwu_timer_1] ; rwu_timer_1--; MOV A,[rwu_timer_1] ; while(rwu_timer_1 && rwu_resuspend); CMP A,00h JZ .if_rwe_resuspend MOV A,[rwu_resuspend] CMP A,00h JNZ .rwu_timer_1_loop .if_rwe_resuspend: MOV A,[rwu_resuspend] ; if(rwu_resuspend) CMP A,00h JZ .radio_poll_return_val .radio_poll_resuspend: CALL radio_suspend CALL encore_suspend .radio_poll_return_val: MOV A,[rwu_resuspend] ; AC = rwu_resuspend; RET ;-------------- end program listing ----------------------------------------- INCLUDE "utilities.asm" INCLUDE "radio.asm" INCLUDE "bind-auto.asm" IFDEF MFG_TEST INCLUDE "mfgtest.asm" ENDIF ; MFG_TEST IFDEF ENCRYPT_DATA_A INCLUDE "encrypt_support.asm" ENDIF ; Place USB code in upper 4K block ORG 1000h INCLUDE "usbcode.asm" ORG 1800h IFDEF ENCRYPT_DATA_A INCLUDE "encrypt.asm" ENDIF INCLUDE "dvk_hardware.asm" INCLUDE "rdk_keyboard.asm" INCLUDE "rdk_mouse.asm" IFNDEF PROTOCOL_1_1 INCLUDE "E2.asm" ELSE ; PROTOCOL_1_1 IFDEF ENCRYPT_DATA_A INCLUDE "E2.asm" ENDIF ; ENCRYPT_DATA_A ENDIF ; PROTOCOL_1_1