diff --git a/firmware/common/operacake.c b/firmware/common/operacake.c index 6230cb39..3a19c4cd 100644 --- a/firmware/common/operacake.c +++ b/firmware/common/operacake.c @@ -74,6 +74,9 @@ #define OPERACAKE_MAX_BOARDS 8 +#define INVALID_RANGE 0xFF; +static uint8_t current_range = INVALID_RANGE; + i2c_bus_t* const oc_bus = &i2c0; enum operacake_switching_mode { @@ -151,38 +154,6 @@ void operacake_get_boards(uint8_t *addresses) { } } -void operacake_set_mode(uint8_t address, uint8_t mode) { - if (address >= OPERACAKE_MAX_BOARDS) - return; - - operacake_boards[address].mode = mode; - - if (mode == MODE_TIME) { - // Switch Opera Cake to pin-control mode - uint8_t config_pins = (uint8_t)~(OPERACAKE_PIN_OE(1) | OPERACAKE_PIN_LEDEN(1) | OPERACAKE_PIN_LEDEN2(1)); - operacake_write_reg(oc_bus, address, OPERACAKE_REG_CONFIG, config_pins); - operacake_write_reg(oc_bus, address, OPERACAKE_REG_OUTPUT, OPERACAKE_GPIO_ENABLE | OPERACAKE_EN_LEDS); - } else { - operacake_write_reg(oc_bus, address, OPERACAKE_REG_CONFIG, OPERACAKE_CONFIG_ALL_OUTPUT); - operacake_set_ports(address, operacake_boards[address].PA, operacake_boards[address].PB); - } - - // If any boards are in MODE_TIME, enable the sctimer events. - bool enable_sctimer = false; - for (int i = 0; i < OPERACAKE_MAX_BOARDS; i++) { - if (operacake_boards[i].mode == MODE_TIME) - enable_sctimer = true; - } - operacake_sctimer_enable(enable_sctimer); -} - -uint8_t operacake_get_mode(uint8_t address) { - if (address >= OPERACAKE_MAX_BOARDS) - return 0; - - return operacake_boards[address].mode; -} - uint8_t port_to_pins(uint8_t port) { switch(port) { case OPERACAKE_PA1: @@ -206,46 +177,98 @@ uint8_t port_to_pins(uint8_t port) { return 0xFF; } -uint8_t operacake_set_ports(uint8_t address, uint8_t PA, uint8_t PB) { +/* + * Issue I2C command to activate ports. + */ +uint8_t operacake_activate_ports(uint8_t address, uint8_t PA, uint8_t PB) +{ uint8_t side, pa, pb, reg; - /* Start with some error checking, - * which should have been done either - * on the host or elsewhere in firmware - */ - if((PA > OPERACAKE_PB4) || (PB > OPERACAKE_PB4)) { + /* Ensure PA and PB are within the valid range. */ + if ((PA > OPERACAKE_PB4) || (PB > OPERACAKE_PB4)) { return 1; } - /* Check which side PA and PB are on */ - if(((PA <= OPERACAKE_PA4) && (PB <= OPERACAKE_PA4)) - || ((PA > OPERACAKE_PA4) && (PB > OPERACAKE_PA4))) { + /* Ensure PA and PB are on opposite sides. */ + if (((PA <= OPERACAKE_PA4) && (PB <= OPERACAKE_PA4)) + || ((PA > OPERACAKE_PA4) && (PB > OPERACAKE_PA4))) { return 1; } - if(PA > OPERACAKE_PA4) { + if (PA > OPERACAKE_PA4) { side = OPERACAKE_CROSSOVER; } else { side = OPERACAKE_SAMESIDE; } + pa = port_to_pins(PA); + pb = port_to_pins(PB); + + reg = (OPERACAKE_GPIO_DISABLE | side | pa | pb | OPERACAKE_EN_LEDS); + operacake_write_reg(oc_bus, address, OPERACAKE_REG_OUTPUT, reg); + return 0; +} + +void operacake_set_mode(uint8_t address, uint8_t mode) { + if (address >= OPERACAKE_MAX_BOARDS) + return; + + operacake_boards[address].mode = mode; + current_range = INVALID_RANGE; + + if (mode == MODE_TIME) { + // Switch Opera Cake to pin-control mode + uint8_t config_pins = (uint8_t)~(OPERACAKE_PIN_OE(1) | OPERACAKE_PIN_LEDEN(1) | OPERACAKE_PIN_LEDEN2(1)); + operacake_write_reg(oc_bus, address, OPERACAKE_REG_CONFIG, config_pins); + operacake_write_reg(oc_bus, address, OPERACAKE_REG_OUTPUT, OPERACAKE_GPIO_ENABLE | OPERACAKE_EN_LEDS); + } else { + operacake_write_reg(oc_bus, address, OPERACAKE_REG_CONFIG, OPERACAKE_CONFIG_ALL_OUTPUT); + operacake_activate_ports(address, operacake_boards[address].PA, operacake_boards[address].PB); + } + + // If any boards are in MODE_TIME, enable the sctimer events. + bool enable_sctimer = false; + for (int i = 0; i < OPERACAKE_MAX_BOARDS; i++) { + if (operacake_boards[i].mode == MODE_TIME) + enable_sctimer = true; + } + operacake_sctimer_enable(enable_sctimer); +} + +uint8_t operacake_get_mode(uint8_t address) { + if (address >= OPERACAKE_MAX_BOARDS) + return 0; + + return operacake_boards[address].mode; +} + +/* + * Set manual mode ports. + */ +uint8_t operacake_set_ports(uint8_t address, uint8_t PA, uint8_t PB) +{ + /* Ensure PA and PB are within the valid range. */ + if ((PA > OPERACAKE_PB4) || (PB > OPERACAKE_PB4)) { + return 1; + } + /* Ensure PA and PB are on opposite sides. */ + if (((PA <= OPERACAKE_PA4) && (PB <= OPERACAKE_PA4)) + || ((PA > OPERACAKE_PA4) && (PB > OPERACAKE_PA4))) { + return 1; + } + // Keep track of manual settings for when we switch in/out of time/frequency modes. operacake_boards[address].PA = PA; operacake_boards[address].PB = PB; - // Only apply register settings if the board is in manual mode. - if (operacake_boards[address].mode != MODE_MANUAL) - return 0; + // Immediately apply register settings if the board is in manual mode. + if (operacake_boards[address].mode == MODE_MANUAL) { + return operacake_activate_ports(address, PA, PB); + } - pa = port_to_pins(PA); - pb = port_to_pins(PB); - - reg = (OPERACAKE_GPIO_DISABLE | side - | pa | pb | OPERACAKE_EN_LEDS); - operacake_write_reg(oc_bus, address, OPERACAKE_REG_OUTPUT, reg); return 0; } /* - * Opera Glasses + * frequency mode aka "Opera Glasses" */ typedef struct { uint16_t freq_min; @@ -264,18 +287,19 @@ uint8_t operacake_add_range(uint16_t freq_min, uint16_t freq_max, uint8_t port) ranges[range_idx].freq_min = freq_min; ranges[range_idx].freq_max = freq_max; ranges[range_idx].portA = port; - ranges[range_idx].portB = 7; - if(port <= OPERACAKE_PA4) { - ranges[range_idx].portB = range_idx+4; - } else { - ranges[range_idx].portB = OPERACAKE_PA1; - } + /* Make the B port mirror the A port. */ + ranges[range_idx].portB = (port + 4) % 8; range_idx++; + current_range = INVALID_RANGE; return 0; } +void operacake_clear_ranges(void) +{ + range_idx = 0; +} + #define FREQ_ONE_MHZ (1000000ull) -static uint8_t current_range = 0xFF; uint8_t operacake_set_range(uint32_t freq_mhz) { if(range_idx == 0) { @@ -289,13 +313,17 @@ uint8_t operacake_set_range(uint32_t freq_mhz) { break; } } + /* Use the last range if there was no match. */ + if (range == range_idx) { + range--; + } if(range == current_range) { return 1; } for (int i = 0; i < OPERACAKE_MAX_BOARDS; i++) { if (operacake_is_board_present(i) && operacake_get_mode(i) == MODE_FREQUENCY) { - operacake_set_ports(i, ranges[range].portA, ranges[range].portB); + operacake_activate_ports(i, ranges[range].portA, ranges[range].portB); break; } } diff --git a/firmware/common/operacake.h b/firmware/common/operacake.h index 88bb135c..3b2621bb 100644 --- a/firmware/common/operacake.h +++ b/firmware/common/operacake.h @@ -50,6 +50,7 @@ uint8_t operacake_get_mode(uint8_t address); uint8_t operacake_set_ports(uint8_t address, uint8_t PA, uint8_t PB); uint8_t operacake_add_range(uint16_t freq_min, uint16_t freq_max, uint8_t port); uint8_t operacake_set_range(uint32_t freq_mhz); +void operacake_clear_ranges(void); uint16_t gpio_test(uint8_t address); #ifdef __cplusplus diff --git a/firmware/hackrf_usb/usb_api_operacake.c b/firmware/hackrf_usb/usb_api_operacake.c index 00374522..43f14a52 100644 --- a/firmware/hackrf_usb/usb_api_operacake.c +++ b/firmware/hackrf_usb/usb_api_operacake.c @@ -65,6 +65,7 @@ usb_request_status_t usb_vendor_request_operacake_set_ranges( if((num_ranges == 0) || (num_ranges > MAX_OPERACAKE_RANGES)) { return USB_REQUEST_STATUS_STALL; } + operacake_clear_ranges(); usb_transfer_schedule_block(endpoint->out, &data, endpoint->setup.length, NULL, NULL); } else if (stage == USB_TRANSFER_STAGE_DATA) { diff --git a/host/hackrf-tools/src/hackrf_operacake.c b/host/hackrf-tools/src/hackrf_operacake.c index d2db60bc..763a673a 100644 --- a/host/hackrf-tools/src/hackrf_operacake.c +++ b/host/hackrf-tools/src/hackrf_operacake.c @@ -57,9 +57,9 @@ static void usage() { } static struct option long_options[] = { - { "device", no_argument, 0, 'd' }, - { "address", no_argument, 0, 'o' }, - { "mode", no_argument, 0, 'm' }, + { "device", required_argument, 0, 'd' }, + { "address", required_argument, 0, 'o' }, + { "mode", required_argument, 0, 'm' }, { "list", no_argument, 0, 'l' }, { "gpio_test", no_argument, 0, 'g' }, { "help", no_argument, 0, 'h' }, diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index 5be23d6f..025c2d59 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -2204,7 +2204,7 @@ int ADDCALL hackrf_get_operacake_mode(hackrf_device* device, uint8_t address, en } } -/* Set Operacake ports */ +/* Set Operacake manual mode ports. */ int ADDCALL hackrf_set_operacake_ports(hackrf_device* device, uint8_t address, uint8_t port_a, diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h index 70acec8c..4fb56363 100644 --- a/host/libhackrf/src/hackrf.h +++ b/host/libhackrf/src/hackrf.h @@ -110,7 +110,7 @@ enum operacake_switching_mode { */ OPERACAKE_MODE_MANUAL, /** - * Port connections are switched automatically when the frequency is changed. Frequency ranges can be set using @ref hackrf_set_operacake_ranges. + * Port connections are switched automatically when the frequency is changed. Frequency ranges can be set using @ref hackrf_set_operacake_freq_ranges. */ OPERACAKE_MODE_FREQUENCY, /**