mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
gpio i2c?
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
<h2>
|
||||
|
||||
[Join THE Flipper Uncensored Discord](https://discord.gg/gF2bBUzAFe) & [support us if you like what you see](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/SUPPORT.md)! 😄🚀💸</h2>
|
||||
<table><tr><td>
|
||||
|
||||
#### Thank you to all the supporters; this firmware is a fork of [Unleashed/xMasterX](https://github.com/Eng1n33r/flipperzero-firmware) & the main Flipper Devices FW! I will try to keep active development and updates from both in this build along with any other projects that can be found to be useful to the community. I try to keep this FW build the most cutting edge with updates from both and updates from active community projects. All features and projects pulled are listed in expandable sections below. Please do [support us](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/SUPPORT.md) and/or [xMasterX](https://github.com/Eng1n33r/flipperzero-firmware)! Everyone gives much of their uncompensated free time to ensure the success of the Flipper Zero!
|
||||
|
||||
<center><img src="https://user-images.githubusercontent.com/10697207/186202043-26947e28-b1cc-459a-8f20-ffcc7fc0c71c.png" align="center" alt="fzCUSTOM" border="0" width="50%" height="50%"></center>
|
||||
</td><td><center><img src="https://user-images.githubusercontent.com/10697207/186202043-26947e28-b1cc-459a-8f20-ffcc7fc0c71c.png" align="center" alt="fzCUSTOM" border="0" width="30%" height="30%"></center></td></tr></table>
|
||||
|
||||
<b>Latest Updates:</b>
|
||||
|
||||
@@ -14,8 +16,6 @@
|
||||
<summary><B>TO DO</b></summary><br/>
|
||||
|
||||
- GPIO: Feature to read EEPROM of SFP Modules using I2C [(By marcusju)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/198)
|
||||
- Settings: Favorite Game by holding UP on Desktop [Thanks to gotnull](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/57)
|
||||
- Settings: Hold Down for Games Menu [(Thanks to ESurge)](https://github.com/ESurge/flipperzero-firmware-wPlugins)
|
||||
- Settings: Rename from SD `dolphin/name.txt` [(Thanks to E_Surge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/259)
|
||||
- Settings: Scan names will have timestamp instead of random name assigned for [NFC](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/lib/toolbox/random_name.c) and [SubGHz](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/applications/subghz/scenes/subghz_scene_read_raw.c) (By RogueMaster)
|
||||
- SubGHz: [Add settings to subghz read functionality to allow setting RSSI threshold (raw only) (By PolymerPrints)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/184)
|
||||
@@ -26,6 +26,8 @@
|
||||
- [Keynote BT plugin: long press on OK to switch between Space and Retur… #1729 (By coded-with-claws)](https://github.com/flipperdevices/flipperzero-firmware/pull/1729)
|
||||
- [SubGhz: fix display information in the file if the frequenc… #1724 (By Skorpionm)](https://github.com/flipperdevices/flipperzero-firmware/pull/1724)
|
||||
- [Show error popup when NFC chip is not init/disconnected #1722 (By Astrrra)](https://github.com/flipperdevices/flipperzero-firmware/pull/1722)
|
||||
- Lost To Faps: Settings: Favorite Game by holding UP on Desktop [Thanks to gotnull](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/57)
|
||||
- Lost To Faps: Settings: Hold Down for Games Menu [(Thanks to ESurge)](https://github.com/ESurge/flipperzero-firmware-wPlugins)
|
||||
|
||||
</details>
|
||||
|
||||
@@ -132,7 +134,6 @@ $ ./fbt plugin_dist FIRMWARE_APP_SET=ext_apps
|
||||
- Settings: Desktop => [Games Only Mode (By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/GAMES_ONLY.md) [(Thanks to Astrrra for Inverted Mode to Mimic)](https://github.com/wetox-team/flipperzero-firmware/commit/ce91582b7417c5d7a9d8416c17a102d3a5868238)
|
||||
- - [UP UP DOWN DOWN LEFT RIGHT LEFT RIGHT FROM CLOCK](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/GAMES_ONLY.md)<== FULL LIST OF GAMES ONLY CONTROLS
|
||||
- Settings: "DUMB Mode" is now "Lock W PIN + Off" [(By RogueMaster)]
|
||||
- Settings: Favorite Game by holding UP on Desktop [Thanks to gotnull](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/57)
|
||||
- Settings: Hold Down for Games Menu [(Thanks to ESurge)](https://github.com/ESurge/flipperzero-firmware-wPlugins)
|
||||
- Settings: LCD Timeout Options Added: 10s+90s+2min+5min+10min [(By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/applications/notification/notification_settings_app.c)
|
||||
- Settings: Rename from SD `dolphin/name.txt` [(Thanks to E_Surge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/259)
|
||||
|
||||
@@ -51,6 +51,16 @@ GpioApp* gpio_app_alloc() {
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test));
|
||||
|
||||
app->gpio_i2c_scanner = gpio_i2c_scanner_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
GpioAppViewI2CScanner,
|
||||
gpio_i2c_scanner_get_view(app->gpio_i2c_scanner));
|
||||
|
||||
app->gpio_i2c_sfp = gpio_i2c_sfp_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, GpioAppViewI2CSfp, gpio_i2c_sfp_get_view(app->gpio_i2c_sfp));
|
||||
|
||||
app->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, GpioAppViewUsbUartCloseRpc, widget_get_view(app->widget));
|
||||
@@ -75,6 +85,8 @@ void gpio_app_free(GpioApp* app) {
|
||||
// Views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewVarItemList);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioTest);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewI2CScanner);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewI2CSfp);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCfg);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc);
|
||||
@@ -82,6 +94,8 @@ void gpio_app_free(GpioApp* app) {
|
||||
widget_free(app->widget);
|
||||
gpio_test_free(app->gpio_test);
|
||||
gpio_usb_uart_free(app->gpio_usb_uart);
|
||||
gpio_i2c_scanner_free(app->gpio_i2c_scanner);
|
||||
gpio_i2c_sfp_free(app->gpio_i2c_sfp);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <gui/modules/widget.h>
|
||||
#include "views/gpio_test.h"
|
||||
#include "views/gpio_usb_uart.h"
|
||||
#include "views/gpio_i2c_scanner.h"
|
||||
#include "views/gpio_i2c_sfp.h"
|
||||
|
||||
struct GpioApp {
|
||||
Gui* gui;
|
||||
@@ -27,6 +29,8 @@ struct GpioApp {
|
||||
GpioTest* gpio_test;
|
||||
GpioUsbUart* gpio_usb_uart;
|
||||
UsbUartBridge* usb_uart_bridge;
|
||||
GpioI2CScanner* gpio_i2c_scanner;
|
||||
GpioI2CSfp* gpio_i2c_sfp;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
@@ -35,4 +39,6 @@ typedef enum {
|
||||
GpioAppViewUsbUart,
|
||||
GpioAppViewUsbUartCfg,
|
||||
GpioAppViewUsbUartCloseRpc,
|
||||
GpioAppViewI2CScanner,
|
||||
GpioAppViewI2CSfp
|
||||
} GpioAppView;
|
||||
|
||||
@@ -5,6 +5,8 @@ typedef enum {
|
||||
GpioStartEventOtgOn,
|
||||
GpioStartEventManualControl,
|
||||
GpioStartEventUsbUart,
|
||||
GpioStartEventI2CScanner,
|
||||
GpioStartEventI2CSfp,
|
||||
|
||||
GpioCustomEventErrorBack,
|
||||
|
||||
|
||||
23
applications/main/gpio/gpio_i2c_scanner_control.c
Normal file
23
applications/main/gpio/gpio_i2c_scanner_control.c
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "gpio_i2c_scanner_control.h"
|
||||
#include <furi.h>
|
||||
|
||||
void gpio_i2c_scanner_run_once(I2CScannerState* i2c_scanner_state) {
|
||||
//Reset the number of items for rewriting the array
|
||||
i2c_scanner_state->items = 0;
|
||||
furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
|
||||
|
||||
uint32_t response_timeout_ticks = furi_ms_to_ticks(5.f);
|
||||
|
||||
//Addresses 0 to 7 are reserved and won't be scanned
|
||||
for(int i = FIRST_NON_RESERVED_I2C_ADDRESS; i <= HIGHEST_I2C_ADDRESS; i++) {
|
||||
if(furi_hal_i2c_is_device_ready(
|
||||
&furi_hal_i2c_handle_external,
|
||||
i << 1,
|
||||
response_timeout_ticks)) { //Bitshift of 1 bit to convert 7-Bit Address into 8-Bit Address
|
||||
i2c_scanner_state->responding_address[i2c_scanner_state->items] = i;
|
||||
i2c_scanner_state->items++;
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_i2c_release(&furi_hal_i2c_handle_external);
|
||||
}
|
||||
21
applications/main/gpio/gpio_i2c_scanner_control.h
Normal file
21
applications/main/gpio/gpio_i2c_scanner_control.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <furi_hal_i2c.h>
|
||||
|
||||
#define FIRST_NON_RESERVED_I2C_ADDRESS 8
|
||||
#define HIGHEST_I2C_ADDRESS 127
|
||||
#define AVAILABLE_NONRESVERED_I2C_ADDRESSES 120
|
||||
|
||||
typedef struct {
|
||||
uint8_t items;
|
||||
uint8_t responding_address[AVAILABLE_NONRESVERED_I2C_ADDRESSES];
|
||||
} I2CScannerState;
|
||||
|
||||
/** Scans the I2C-Bus (SDA: Pin 15, SCL: Pin 16) for available 7-Bit slave addresses. Saves the number of detected slaves and their addresses.
|
||||
*
|
||||
* @param i2c_scanner_state State including the detected addresses and the number of addresses saved.
|
||||
*/
|
||||
void gpio_i2c_scanner_run_once(I2CScannerState* st);
|
||||
334
applications/main/gpio/gpio_i2c_sfp_control.c
Normal file
334
applications/main/gpio/gpio_i2c_sfp_control.c
Normal file
@@ -0,0 +1,334 @@
|
||||
#include "gpio_i2c_sfp_control.h"
|
||||
#include <furi_hal.h>
|
||||
#include <string.h>
|
||||
|
||||
// This is map mapping the connector type to the appropriate name. (see SFF-8024 Rev. 4.9, Table 4-3)
|
||||
const char* sfp_connector_map[256] = {
|
||||
"Unknown or unspecified",
|
||||
"SC",
|
||||
"Fibre Channel Style 1",
|
||||
"Fibre Channel Style 2",
|
||||
"BNC/TNC",
|
||||
"Fibre Channel Coax",
|
||||
"Fiber Jack",
|
||||
"LC",
|
||||
"MT-RJ",
|
||||
"MU",
|
||||
"SG",
|
||||
"Optical Pigtail",
|
||||
"MP0 1x12",
|
||||
"MP 2x16",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"HSSDC II",
|
||||
"Copper pigtail",
|
||||
"RJ45",
|
||||
"No seperable connector",
|
||||
"MXC 2x16",
|
||||
"CS optical connector",
|
||||
"SN (prev. Mini CS)",
|
||||
"MPO 2x12",
|
||||
"MPO 1x16",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved"};
|
||||
|
||||
void str_part(uint8_t* data, char* buffer, int start, int len) {
|
||||
int i;
|
||||
for(i = start; i < start + len; i++) {
|
||||
buffer[i - start] = data[i];
|
||||
}
|
||||
buffer[i - start] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
void print_part(uint8_t *data, char *ptype, char *prefix, int start, int len){
|
||||
printf("%s: ", prefix);
|
||||
for (int i=start; i<start+len; i++){
|
||||
printf(ptype, data[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
*/
|
||||
|
||||
void gpio_i2c_sfp_run_once(I2CSfpState* i2c_sfp_state) {
|
||||
furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
|
||||
// Allocate buffer for SFP Data
|
||||
uint8_t sfp_data[255];
|
||||
uint32_t response_timeout_ticks = furi_ms_to_ticks(5.f);
|
||||
|
||||
// Check for SFP Module on I2C Bus
|
||||
if(furi_hal_i2c_is_device_ready(
|
||||
&furi_hal_i2c_handle_external, SFP_I2C_ADDRESS << 1, response_timeout_ticks)) {
|
||||
for(uint8_t i = 0; i <= 254; i++) {
|
||||
uint8_t data = 0;
|
||||
// Read data from register
|
||||
furi_hal_i2c_read_reg_8(
|
||||
&furi_hal_i2c_handle_external,
|
||||
SFP_I2C_ADDRESS << 1,
|
||||
i,
|
||||
&data,
|
||||
response_timeout_ticks);
|
||||
// Save data
|
||||
sfp_data[i] = data;
|
||||
}
|
||||
|
||||
/*
|
||||
print_part(sfp_data, "%c", "Vendor", 20, 16);
|
||||
print_part(sfp_data, "%02X", "OUI", 37, 3);
|
||||
print_part(sfp_data, "%c", "Rev", 56, 4);
|
||||
print_part(sfp_data, "%c", "PN", 40, 16);
|
||||
print_part(sfp_data, "%c", "SN", 68, 16);
|
||||
print_part(sfp_data, "%c", "DC", 84, 6);
|
||||
|
||||
printf("Typ: 0x%02X\r\n", sfp_data[0]);
|
||||
printf("Connector: 0x%02X\r\n", sfp_data[2]);
|
||||
printf("Bitrate: %u MBd\r\n", sfp_data[12]*100);
|
||||
printf("Wavelength: %u nm\r\n", sfp_data[60] * 256 + sfp_data[61]);
|
||||
printf(" %-6s %-6s %-6s %-6s %-6s\r\n", "SM", "OM1", "OM2", "OM3", "OM4");
|
||||
printf("Max length: %3u km %4u m %4u m %4u m %4u m\r\n", sfp_data[14], sfp_data[17]*10, sfp_data[16]*10, sfp_data[19]*10, sfp_data[18]*10);
|
||||
*/
|
||||
|
||||
str_part(sfp_data, i2c_sfp_state->vendor, 20, 16);
|
||||
str_part(sfp_data, i2c_sfp_state->rev, 56, 4);
|
||||
str_part(sfp_data, i2c_sfp_state->pn, 40, 16);
|
||||
str_part(sfp_data, i2c_sfp_state->sn, 68, 16);
|
||||
str_part(sfp_data, i2c_sfp_state->dc, 84, 6);
|
||||
|
||||
//Look up connector in table and copy to struct.
|
||||
strcpy(i2c_sfp_state->connector, sfp_connector_map[sfp_data[2]]);
|
||||
i2c_sfp_state->bitrate = sfp_data[12] * 100;
|
||||
i2c_sfp_state->wavelength = sfp_data[60] * 256 + sfp_data[61];
|
||||
i2c_sfp_state->sm_reach = sfp_data[14];
|
||||
i2c_sfp_state->mm_reach_om3 = sfp_data[19] * 10;
|
||||
}
|
||||
furi_hal_i2c_release(&furi_hal_i2c_handle_external);
|
||||
}
|
||||
32
applications/main/gpio/gpio_i2c_sfp_control.h
Normal file
32
applications/main/gpio/gpio_i2c_sfp_control.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <furi_hal_i2c.h>
|
||||
|
||||
#define FIRST_NON_RESERVED_I2C_ADDRESS 8
|
||||
#define HIGHEST_I2C_ADDRESS 127
|
||||
#define AVAILABLE_NONRESVERED_I2C_ADDRESSES 120
|
||||
#define SFP_I2C_ADDRESS 0x50
|
||||
|
||||
typedef struct {
|
||||
char vendor[32];
|
||||
char oui[32];
|
||||
char rev[32];
|
||||
char pn[32];
|
||||
char sn[32];
|
||||
char dc[32];
|
||||
uint8_t type;
|
||||
char connector[32];
|
||||
int wavelength;
|
||||
int sm_reach;
|
||||
int mm_reach_om3;
|
||||
int bitrate;
|
||||
} I2CSfpState;
|
||||
|
||||
/** Reads data from a connected SFP on I2C-Bus (SDA: Pin 15, SCL: Pin 16). Saves data from SFP.
|
||||
*
|
||||
* @param i2c_sfp_state Data collected from SFP.
|
||||
*/
|
||||
void gpio_i2c_sfp_run_once(I2CSfpState* st);
|
||||
@@ -3,3 +3,5 @@ ADD_SCENE(gpio, test, Test)
|
||||
ADD_SCENE(gpio, usb_uart, UsbUart)
|
||||
ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg)
|
||||
ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc)
|
||||
ADD_SCENE(gpio, i2c_scanner, I2CScanner)
|
||||
ADD_SCENE(gpio, i2c_sfp, I2CSfp)
|
||||
|
||||
36
applications/main/gpio/scenes/gpio_scene_i2c_scanner.c
Normal file
36
applications/main/gpio/scenes/gpio_scene_i2c_scanner.c
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "../gpio_app_i.h"
|
||||
#include <furi_hal_gpio.h>
|
||||
|
||||
static I2CScannerState* i2c_scanner_state;
|
||||
|
||||
void gpio_scene_i2c_scanner_ok_callback(InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
GpioApp* app = context;
|
||||
|
||||
if(type == InputTypeRelease) {
|
||||
notification_message(app->notifications, &sequence_set_green_255);
|
||||
gpio_i2c_scanner_run_once(i2c_scanner_state);
|
||||
notification_message(app->notifications, &sequence_reset_green);
|
||||
gpio_i2c_scanner_update_state(app->gpio_i2c_scanner, i2c_scanner_state);
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_scene_i2c_scanner_on_enter(void* context) {
|
||||
GpioApp* app = context;
|
||||
i2c_scanner_state = malloc(sizeof(I2CScannerState));
|
||||
|
||||
gpio_i2c_scanner_set_ok_callback(
|
||||
app->gpio_i2c_scanner, gpio_scene_i2c_scanner_ok_callback, app);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewI2CScanner);
|
||||
}
|
||||
|
||||
bool gpio_scene_i2c_scanner_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
void gpio_scene_i2c_scanner_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
free(i2c_scanner_state);
|
||||
}
|
||||
35
applications/main/gpio/scenes/gpio_scene_i2c_sfp.c
Normal file
35
applications/main/gpio/scenes/gpio_scene_i2c_sfp.c
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "../gpio_app_i.h"
|
||||
#include <furi_hal_gpio.h>
|
||||
|
||||
static I2CSfpState* i2c_sfp_state;
|
||||
|
||||
void gpio_scene_i2c_sfp_ok_callback(InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
GpioApp* app = context;
|
||||
|
||||
if(type == InputTypeRelease) {
|
||||
notification_message(app->notifications, &sequence_set_green_255);
|
||||
gpio_i2c_sfp_run_once(i2c_sfp_state);
|
||||
notification_message(app->notifications, &sequence_reset_green);
|
||||
gpio_i2c_sfp_update_state(app->gpio_i2c_sfp, i2c_sfp_state);
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_scene_i2c_sfp_on_enter(void* context) {
|
||||
GpioApp* app = context;
|
||||
i2c_sfp_state = malloc(sizeof(I2CSfpState));
|
||||
|
||||
gpio_i2c_sfp_set_ok_callback(app->gpio_i2c_sfp, gpio_scene_i2c_sfp_ok_callback, app);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewI2CSfp);
|
||||
}
|
||||
|
||||
bool gpio_scene_i2c_sfp_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
void gpio_scene_i2c_sfp_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
free(i2c_sfp_state);
|
||||
}
|
||||
@@ -6,6 +6,8 @@ enum GpioItem {
|
||||
GpioItemUsbUart,
|
||||
GpioItemTest,
|
||||
GpioItemOtg,
|
||||
GpioItemI2CScanner,
|
||||
GpioItemI2CSfp,
|
||||
};
|
||||
|
||||
enum GpioOtg {
|
||||
@@ -26,6 +28,10 @@ static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t ind
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventManualControl);
|
||||
} else if(index == GpioItemUsbUart) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventUsbUart);
|
||||
} else if(index == GpioItemI2CScanner) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventI2CScanner);
|
||||
} else if(index == GpioItemI2CSfp) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventI2CSfp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +73,9 @@ void gpio_scene_start_on_enter(void* context) {
|
||||
variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOff]);
|
||||
}
|
||||
|
||||
variable_item_list_add(var_item_list, "I2C-Scanner", 0, NULL, NULL);
|
||||
variable_item_list_add(var_item_list, "I2C-SFP", 0, NULL, NULL);
|
||||
|
||||
variable_item_list_set_selected_item(
|
||||
var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioSceneStart));
|
||||
|
||||
@@ -85,6 +94,12 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
} else if(event.event == GpioStartEventManualControl) {
|
||||
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemTest);
|
||||
scene_manager_next_scene(app->scene_manager, GpioSceneTest);
|
||||
} else if(event.event == GpioStartEventI2CScanner) {
|
||||
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemI2CScanner);
|
||||
scene_manager_next_scene(app->scene_manager, GpioSceneI2CScanner);
|
||||
} else if(event.event == GpioStartEventI2CSfp) {
|
||||
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemI2CSfp);
|
||||
scene_manager_next_scene(app->scene_manager, GpioSceneI2CSfp);
|
||||
} else if(event.event == GpioStartEventUsbUart) {
|
||||
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart);
|
||||
if(!furi_hal_usb_is_locked()) {
|
||||
|
||||
136
applications/main/gpio/views/gpio_i2c_scanner.c
Normal file
136
applications/main/gpio/views/gpio_i2c_scanner.c
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <gui/elements.h>
|
||||
#include "gpio_i2c_scanner.h"
|
||||
#include "../gpio_item.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
struct GpioI2CScanner {
|
||||
View* view;
|
||||
GpioI2CScannerOkCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t items;
|
||||
uint8_t responding_address[AVAILABLE_NONRESVERED_I2C_ADDRESSES];
|
||||
} GpioI2CScannerModel;
|
||||
|
||||
static bool gpio_i2c_scanner_process_ok(GpioI2CScanner* gpio_i2c_scanner, InputEvent* event);
|
||||
|
||||
static void gpio_i2c_scanner_draw_callback(Canvas* canvas, void* _model) {
|
||||
GpioI2CScannerModel* model = _model;
|
||||
|
||||
char temp_str[25];
|
||||
elements_button_center(canvas, "Start scan");
|
||||
canvas_draw_line(canvas, 2, 10, 125, 10);
|
||||
canvas_draw_line(canvas, 2, 52, 125, 52);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2, 9, "I2C-Scanner");
|
||||
canvas_draw_str(canvas, 3, 25, "SDA:");
|
||||
canvas_draw_str(canvas, 3, 42, "SCL:");
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
snprintf(temp_str, 25, "Slaves: %u", model->items);
|
||||
canvas_draw_str_aligned(canvas, 126, 8, AlignRight, AlignBottom, temp_str);
|
||||
|
||||
canvas_draw_str(canvas, 29, 25, "Pin 15");
|
||||
canvas_draw_str(canvas, 29, 42, "Pin 16");
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
char temp_str2[6];
|
||||
if(model->items > 0) {
|
||||
snprintf(temp_str, 25, "Addr: ");
|
||||
for(int i = 0; i < model->items; i++) {
|
||||
snprintf(temp_str2, 6, "0x%x ", model->responding_address[i]);
|
||||
strcat(temp_str, temp_str2);
|
||||
|
||||
if(i == 1 || model->items == 1) { //Draw a maximum of two addresses in the first line
|
||||
canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, temp_str);
|
||||
temp_str[0] = '\0';
|
||||
} else if(
|
||||
i == 4 || (model->items - 1 == i &&
|
||||
i < 6)) { //Draw a maximum of three addresses in the second line
|
||||
canvas_draw_str_aligned(canvas, 127, 36, AlignRight, AlignBottom, temp_str);
|
||||
temp_str[0] = '\0';
|
||||
} else if(i == 7 || model->items - 1 == i) { //Draw a maximum of three addresses in the third line
|
||||
canvas_draw_str_aligned(canvas, 127, 48, AlignRight, AlignBottom, temp_str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool gpio_i2c_scanner_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
GpioI2CScanner* gpio_i2c_scanner = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event->key == InputKeyOk) {
|
||||
consumed = gpio_i2c_scanner_process_ok(gpio_i2c_scanner, event);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static bool gpio_i2c_scanner_process_ok(GpioI2CScanner* gpio_i2c_scanner, InputEvent* event) {
|
||||
bool consumed = false;
|
||||
gpio_i2c_scanner->callback(event->type, gpio_i2c_scanner->context);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
GpioI2CScanner* gpio_i2c_scanner_alloc() {
|
||||
GpioI2CScanner* gpio_i2c_scanner = malloc(sizeof(GpioI2CScanner));
|
||||
|
||||
gpio_i2c_scanner->view = view_alloc();
|
||||
view_allocate_model(gpio_i2c_scanner->view, ViewModelTypeLocking, sizeof(GpioI2CScannerModel));
|
||||
view_set_context(gpio_i2c_scanner->view, gpio_i2c_scanner);
|
||||
view_set_draw_callback(gpio_i2c_scanner->view, gpio_i2c_scanner_draw_callback);
|
||||
view_set_input_callback(gpio_i2c_scanner->view, gpio_i2c_scanner_input_callback);
|
||||
|
||||
return gpio_i2c_scanner;
|
||||
}
|
||||
|
||||
void gpio_i2c_scanner_free(GpioI2CScanner* gpio_i2c_scanner) {
|
||||
furi_assert(gpio_i2c_scanner);
|
||||
view_free(gpio_i2c_scanner->view);
|
||||
free(gpio_i2c_scanner);
|
||||
}
|
||||
|
||||
View* gpio_i2c_scanner_get_view(GpioI2CScanner* gpio_i2c_scanner) {
|
||||
furi_assert(gpio_i2c_scanner);
|
||||
return gpio_i2c_scanner->view;
|
||||
}
|
||||
|
||||
void gpio_i2c_scanner_set_ok_callback(
|
||||
GpioI2CScanner* gpio_i2c_scanner,
|
||||
GpioI2CScannerOkCallback callback,
|
||||
void* context) {
|
||||
furi_assert(gpio_i2c_scanner);
|
||||
furi_assert(callback);
|
||||
with_view_model(
|
||||
gpio_i2c_scanner->view, (GpioI2CScannerModel * model) {
|
||||
UNUSED(model);
|
||||
gpio_i2c_scanner->callback = callback;
|
||||
gpio_i2c_scanner->context = context;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void gpio_i2c_scanner_update_state(GpioI2CScanner* instance, I2CScannerState* st) {
|
||||
furi_assert(instance);
|
||||
furi_assert(st);
|
||||
|
||||
with_view_model(
|
||||
instance->view, (GpioI2CScannerModel * model) {
|
||||
model->items = st->items;
|
||||
|
||||
for(int i = 0; i < model->items; i++) {
|
||||
model->responding_address[i] = st->responding_address[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
20
applications/main/gpio/views/gpio_i2c_scanner.h
Normal file
20
applications/main/gpio/views/gpio_i2c_scanner.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "../gpio_i2c_scanner_control.h"
|
||||
|
||||
typedef struct GpioI2CScanner GpioI2CScanner;
|
||||
typedef void (*GpioI2CScannerOkCallback)(InputType type, void* context);
|
||||
|
||||
GpioI2CScanner* gpio_i2c_scanner_alloc();
|
||||
|
||||
void gpio_i2c_scanner_free(GpioI2CScanner* gpio_i2c_scanner);
|
||||
|
||||
View* gpio_i2c_scanner_get_view(GpioI2CScanner* gpio_i2c_scanner);
|
||||
|
||||
void gpio_i2c_scanner_set_ok_callback(
|
||||
GpioI2CScanner* gpio_i2c_scanner,
|
||||
GpioI2CScannerOkCallback callback,
|
||||
void* context);
|
||||
|
||||
void gpio_i2c_scanner_update_state(GpioI2CScanner* instance, I2CScannerState* st);
|
||||
145
applications/main/gpio/views/gpio_i2c_sfp.c
Normal file
145
applications/main/gpio/views/gpio_i2c_sfp.c
Normal file
@@ -0,0 +1,145 @@
|
||||
#include <gui/elements.h>
|
||||
#include "gpio_i2c_sfp.h"
|
||||
#include "../gpio_item.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
struct GpioI2CSfp {
|
||||
View* view;
|
||||
GpioI2CSfpOkCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char vendor[32];
|
||||
char oui[32];
|
||||
char rev[32];
|
||||
char pn[32];
|
||||
char sn[32];
|
||||
char dc[32];
|
||||
uint8_t type;
|
||||
char connector[32];
|
||||
int wavelength;
|
||||
int sm_reach;
|
||||
int mm_reach_om3;
|
||||
int bitrate;
|
||||
} GpioI2CSfpModel;
|
||||
|
||||
static bool gpio_i2c_sfp_process_ok(GpioI2CSfp* gpio_i2c_sfp, InputEvent* event);
|
||||
|
||||
static void gpio_i2c_sfp_draw_callback(Canvas* canvas, void* _model) {
|
||||
GpioI2CSfpModel* model = _model;
|
||||
|
||||
// Temp String for formatting output
|
||||
char temp_str[280];
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_button_center(canvas, "Read");
|
||||
canvas_draw_str(canvas, 2, 63, "P15 SCL");
|
||||
canvas_draw_str(canvas, 92, 63, "P16 SDA");
|
||||
|
||||
snprintf(temp_str, 280, "Vendor: %s", model->vendor);
|
||||
canvas_draw_str(canvas, 2, 9, temp_str);
|
||||
|
||||
snprintf(temp_str, 280, "PN: %s", model->pn);
|
||||
canvas_draw_str(canvas, 2, 19, temp_str);
|
||||
|
||||
snprintf(temp_str, 280, "SN: %s", model->sn);
|
||||
canvas_draw_str(canvas, 2, 29, temp_str);
|
||||
|
||||
snprintf(temp_str, 280, "REV: %s", model->rev);
|
||||
canvas_draw_str(canvas, 2, 39, temp_str);
|
||||
|
||||
snprintf(temp_str, 280, "CON: %s", model->connector);
|
||||
canvas_draw_str(canvas, 50, 39, temp_str);
|
||||
|
||||
//Print Wavelength of Module
|
||||
snprintf(temp_str, 280, "%u nm", model->wavelength);
|
||||
canvas_draw_str(canvas, 2, 49, temp_str);
|
||||
|
||||
// These values will be zero if not applicable..
|
||||
if(model->sm_reach != 0) {
|
||||
snprintf(temp_str, 280, "%u km (SM)", model->sm_reach);
|
||||
canvas_draw_str(canvas, 50, 49, temp_str);
|
||||
}
|
||||
if(model->mm_reach_om3 != 0) {
|
||||
snprintf(temp_str, 280, "%u m (MM OM3)", model->mm_reach_om3);
|
||||
canvas_draw_str(canvas, 50, 49, temp_str);
|
||||
}
|
||||
}
|
||||
|
||||
static bool gpio_i2c_sfp_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
GpioI2CSfp* gpio_i2c_sfp = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event->key == InputKeyOk) {
|
||||
consumed = gpio_i2c_sfp_process_ok(gpio_i2c_sfp, event);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static bool gpio_i2c_sfp_process_ok(GpioI2CSfp* gpio_i2c_sfp, InputEvent* event) {
|
||||
bool consumed = false;
|
||||
gpio_i2c_sfp->callback(event->type, gpio_i2c_sfp->context);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
GpioI2CSfp* gpio_i2c_sfp_alloc() {
|
||||
GpioI2CSfp* gpio_i2c_sfp = malloc(sizeof(GpioI2CSfp));
|
||||
|
||||
gpio_i2c_sfp->view = view_alloc();
|
||||
view_allocate_model(gpio_i2c_sfp->view, ViewModelTypeLocking, sizeof(GpioI2CSfpModel));
|
||||
view_set_context(gpio_i2c_sfp->view, gpio_i2c_sfp);
|
||||
view_set_draw_callback(gpio_i2c_sfp->view, gpio_i2c_sfp_draw_callback);
|
||||
view_set_input_callback(gpio_i2c_sfp->view, gpio_i2c_sfp_input_callback);
|
||||
|
||||
return gpio_i2c_sfp;
|
||||
}
|
||||
|
||||
void gpio_i2c_sfp_free(GpioI2CSfp* gpio_i2c_sfp) {
|
||||
furi_assert(gpio_i2c_sfp);
|
||||
view_free(gpio_i2c_sfp->view);
|
||||
free(gpio_i2c_sfp);
|
||||
}
|
||||
|
||||
View* gpio_i2c_sfp_get_view(GpioI2CSfp* gpio_i2c_sfp) {
|
||||
furi_assert(gpio_i2c_sfp);
|
||||
return gpio_i2c_sfp->view;
|
||||
}
|
||||
|
||||
void gpio_i2c_sfp_set_ok_callback(
|
||||
GpioI2CSfp* gpio_i2c_sfp,
|
||||
GpioI2CSfpOkCallback callback,
|
||||
void* context) {
|
||||
furi_assert(gpio_i2c_sfp);
|
||||
furi_assert(callback);
|
||||
with_view_model(
|
||||
gpio_i2c_sfp->view, (GpioI2CSfpModel * model) {
|
||||
UNUSED(model);
|
||||
gpio_i2c_sfp->callback = callback;
|
||||
gpio_i2c_sfp->context = context;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void gpio_i2c_sfp_update_state(GpioI2CSfp* instance, I2CSfpState* st) {
|
||||
furi_assert(instance);
|
||||
furi_assert(st);
|
||||
|
||||
with_view_model(
|
||||
instance->view, (GpioI2CSfpModel * model) {
|
||||
// Insert values into model...
|
||||
strcpy(model->vendor, st->vendor);
|
||||
strcpy(model->pn, st->pn);
|
||||
strcpy(model->sn, st->sn);
|
||||
strcpy(model->rev, st->rev);
|
||||
strcpy(model->connector, st->connector);
|
||||
model->wavelength = st->wavelength;
|
||||
model->sm_reach = st->sm_reach;
|
||||
model->mm_reach_om3 = st->mm_reach_om3;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
20
applications/main/gpio/views/gpio_i2c_sfp.h
Normal file
20
applications/main/gpio/views/gpio_i2c_sfp.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "../gpio_i2c_sfp_control.h"
|
||||
|
||||
typedef struct GpioI2CSfp GpioI2CSfp;
|
||||
typedef void (*GpioI2CSfpOkCallback)(InputType type, void* context);
|
||||
|
||||
GpioI2CSfp* gpio_i2c_sfp_alloc();
|
||||
|
||||
void gpio_i2c_sfp_free(GpioI2CSfp* gpio_i2c_sfp);
|
||||
|
||||
View* gpio_i2c_sfp_get_view(GpioI2CSfp* gpio_i2c_sfp);
|
||||
|
||||
void gpio_i2c_sfp_set_ok_callback(
|
||||
GpioI2CSfp* gpio_i2c_sfp,
|
||||
GpioI2CSfpOkCallback callback,
|
||||
void* context);
|
||||
|
||||
void gpio_i2c_sfp_update_state(GpioI2CSfp* instance, I2CSfpState* st);
|
||||
Reference in New Issue
Block a user