mirror of
https://github.com/preble/libpinproc
synced 2026-02-24 18:25:23 +01:00
Merge branch 'master' of git@github.com:preble/P-ROC
This commit is contained in:
@@ -1,18 +1,18 @@
|
|||||||
cmake_minimum_required(VERSION 2.6)
|
cmake_minimum_required(VERSION 2.6)
|
||||||
|
|
||||||
#set( CMAKE_OSX_ARCHITECTURES ppc;i386 ) #Comment out if not universal binary
|
#set( CMAKE_OSX_ARCHITECTURES ppc;i386 ) # Uncomment for universal binary
|
||||||
|
|
||||||
#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ../bin)
|
#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ../bin)
|
||||||
#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ../bin)
|
#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ../bin)
|
||||||
#set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ../bin)
|
#set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ../bin)
|
||||||
|
|
||||||
include_directories(/usr/local/include ./include)
|
include_directories(/usr/local/include $ENV{EXTRA_INC})
|
||||||
|
|
||||||
link_directories(/usr/local/lib)
|
link_directories(/usr/local/lib $ENV{EXTRA_LINK})
|
||||||
|
|
||||||
set(FILES src/pinproc.cpp src/PRDevice.cpp src/PRHardware.cpp)
|
set(FILES src/pinproc.cpp src/PRDevice.cpp src/PRHardware.cpp)
|
||||||
add_library(pinproc ${FILES})
|
add_library(pinproc ${FILES})
|
||||||
|
|
||||||
add_executable(pinproctest examples/pinproctest/pinproctest.cpp)
|
add_executable(pinproctest examples/pinproctest/pinproctest.cpp)
|
||||||
target_link_libraries(pinproctest pinproc usb ftdi)
|
target_link_libraries(pinproctest pinproc usb ftdi yaml-cpp)
|
||||||
|
|
||||||
|
|||||||
23
Makefile
23
Makefile
@@ -1,23 +0,0 @@
|
|||||||
#
|
|
||||||
# File: Makefile (for library)
|
|
||||||
#
|
|
||||||
CC=g++
|
|
||||||
LIB=libpinproc.a
|
|
||||||
LIBDEST=./bin/
|
|
||||||
|
|
||||||
LIBSRC=src/pinproc.cpp src/PRDevice.cpp src/PRHardware.cpp
|
|
||||||
|
|
||||||
LIBOBJ=$(LIBSRC:.cpp=.o)
|
|
||||||
|
|
||||||
CXXFLAGS=
|
|
||||||
|
|
||||||
$(LIB): $(LIBOBJ)
|
|
||||||
@echo lib Makefile - archiving $(LIB)
|
|
||||||
$(AR) r $(LIBDEST)$(LIB) $(LIBOBJ)
|
|
||||||
|
|
||||||
.cpp.o:
|
|
||||||
@echo lib Makefile - compiling $<
|
|
||||||
$(CC) $(CXXFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(LIBOBJ) $(LIBDEST)$(LIB)
|
|
||||||
@@ -11,14 +11,16 @@ libpinproc requires:
|
|||||||
- [libusb-0.1.12](http://libusb.wiki.sourceforge.net/): Install with the default /usr/local prefix.
|
- [libusb-0.1.12](http://libusb.wiki.sourceforge.net/): Install with the default /usr/local prefix.
|
||||||
- [libftdi-0.16](http://www.intra2net.com/en/developer/libftdi/): Install with the default /usr/local prefix.
|
- [libftdi-0.16](http://www.intra2net.com/en/developer/libftdi/): Install with the default /usr/local prefix.
|
||||||
|
|
||||||
Once required but not right now:
|
The pinproctest example requires [yaml-cpp](http://code.google.com/p/yaml-cpp/). Follow the build instructions, creating the build subdirectory. After building, from the build directory, run the following commands to manually install it:
|
||||||
|
|
||||||
- [yaml-cpp](http://code.google.com/p/yaml-cpp/): Should be checked out in the directory two levels below libpinproc in the full source tree (at the same level as ./P-ROC) in a directory named yaml-cpp. Follow the build instructions, creating the build subdirectory. The Makefiles and other project files expect to find libyaml-cpp.a in the yaml-cpp/build/bin directory.
|
sudo cp bin/libyaml-cpp.a /usr/local/lib/
|
||||||
|
sudo mkdir /usr/local/include/yaml-cpp
|
||||||
We are presently experimenting with different build mechanisms. As such there are three ways to build libpinproc at this time: CMake, GNU Make, and the Xcode project (for Mac). As the preferred method is presently CMake, here's how. Before you get started, you will need CMake if you don't already have it.
|
sudo cp ../include/*.h /usr/local/include/yaml-cpp/
|
||||||
|
|
||||||
#### Building with CMake
|
#### Building with CMake
|
||||||
|
|
||||||
|
Download and install [CMake](http://www.cmake.org/cmake/resources/software.html). Then:
|
||||||
|
|
||||||
cd libpinproc
|
cd libpinproc
|
||||||
mkdir build; cd build
|
mkdir build; cd build
|
||||||
cmake ..
|
cmake ..
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
PRGameName: My Great Pin
|
|
||||||
PRDrivers:
|
|
||||||
1: driver one
|
|
||||||
2: driver two
|
|
||||||
PRSwitches:
|
|
||||||
1: switch one
|
|
||||||
2: switch two
|
|
||||||
29
examples/pinproctest/JD.yaml
Normal file
29
examples/pinproctest/JD.yaml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# P-ROC Game Description file for Judge Dredd
|
||||||
|
PRGame:
|
||||||
|
machineType: wpc
|
||||||
|
PRFlippers:
|
||||||
|
- flipperLwR
|
||||||
|
- flipperLwL
|
||||||
|
- flipperUpR
|
||||||
|
- flipperUpL
|
||||||
|
PRBumpers:
|
||||||
|
- slingL
|
||||||
|
- slingR
|
||||||
|
PRSwitches:
|
||||||
|
flipperLwR: 1
|
||||||
|
flipperLwL: 3
|
||||||
|
flipperUpR: 5
|
||||||
|
flipperUpL: 7
|
||||||
|
slingL: 96
|
||||||
|
slingR: 97
|
||||||
|
PRCoils:
|
||||||
|
flipperLwRMain: 32
|
||||||
|
flipperLwRHold: 33
|
||||||
|
flipperLwLMain: 34
|
||||||
|
flipperLwLHold: 35
|
||||||
|
flipperUpRMain: 36
|
||||||
|
flipperUpRHold: 37
|
||||||
|
flipperUpLMain: 38
|
||||||
|
flipperUpLHold: 39
|
||||||
|
slingL: 70
|
||||||
|
slingR: 71
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
#
|
|
||||||
# File: Makefile for application
|
|
||||||
#
|
|
||||||
PINPROC_PATH=../..
|
|
||||||
|
|
||||||
CC=g++
|
|
||||||
LDFLAGS=-L$(PINPROC_PATH)/bin -L/usr/local/lib
|
|
||||||
LIBS=-lpinproc -lusb -lftdi
|
|
||||||
|
|
||||||
SRC=pinproctest.cpp
|
|
||||||
|
|
||||||
CXXFLAGS=-I$(PINPROC_PATH)/include
|
|
||||||
|
|
||||||
OBJS=$(SRC:.cpp=.o)
|
|
||||||
|
|
||||||
EXE=pinproctest
|
|
||||||
|
|
||||||
all: $(EXE)
|
|
||||||
|
|
||||||
# FIXME: This makes the exe require libpinproc but not in a very graceful way.
|
|
||||||
$(EXE): $(OBJS) $(PINPROC_PATH)/bin/libpinproc.a
|
|
||||||
@echo application Makefile - linking $<
|
|
||||||
$(CC) $^ $(LDFLAGS) $(LIBS) -o $@
|
|
||||||
|
|
||||||
.cpp.o:
|
|
||||||
@echo application Makefile - compiling $<
|
|
||||||
$(CC) $(CXXFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(OBJS) $(EXE)
|
|
||||||
@@ -32,29 +32,14 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include "pinproc.h" // Include libpinproc's header.
|
#include "../../include/pinproc.h" // Include libpinproc's header.
|
||||||
|
#include <fstream>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
#define kFlippersSection "PRFlippers"
|
||||||
#define kFlipperLwRightSw (1)
|
#define kBumpersSection "PRBumpers"
|
||||||
#define kFlipperLwLeftSw (3)
|
#define kCoilsSection "PRCoils"
|
||||||
#define kFlipperUpRightSw (5)
|
#define kSwitchesSection "PRSwitches"
|
||||||
#define kFlipperUpLeftSw (7)
|
|
||||||
|
|
||||||
#define kFlipperLwRightMain (32)
|
|
||||||
#define kFlipperLwLeftMain (34)
|
|
||||||
#define kFlipperUpRightMain (36)
|
|
||||||
#define kFlipperUpLeftMain (38)
|
|
||||||
|
|
||||||
#define kFlipperLwRightHold (33)
|
|
||||||
#define kFlipperLwLeftHold (35)
|
|
||||||
#define kFlipperUpRightHold (37)
|
|
||||||
#define kFlipperUpLeftHold (39)
|
|
||||||
|
|
||||||
#define kSlingLeftSw (96)
|
|
||||||
#define kSlingRightSw (97)
|
|
||||||
|
|
||||||
#define kSlingLeftCoil (70)
|
|
||||||
#define kSlingRightCoil (71)
|
|
||||||
|
|
||||||
#define kFlipperPulseTime (34) // 34 ms
|
#define kFlipperPulseTime (34) // 34 ms
|
||||||
#define kBumperPulseTime (25) // 25 ms
|
#define kBumperPulseTime (25) // 25 ms
|
||||||
@@ -69,7 +54,42 @@ void TestLogger(const char *text)
|
|||||||
fprintf(stderr, "TEST: %s", text);
|
fprintf(stderr, "TEST: %s", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureDrivers(PRHandle proc)
|
PRResult LoadConfiguration(YAML::Node& yamlDoc, const char *yamlFilePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::ifstream fin(yamlFilePath);
|
||||||
|
if (fin.is_open() == false)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "YAML file not found: %s\n", yamlFilePath);
|
||||||
|
return kPRFailure;
|
||||||
|
}
|
||||||
|
YAML::Parser parser(fin);
|
||||||
|
|
||||||
|
while(parser)
|
||||||
|
{
|
||||||
|
parser.GetNextDocument(yamlDoc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (YAML::ParserException& ex)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "YAML parse error at line=%d col=%d: %s\n", ex.line, ex.column, ex.msg.c_str());
|
||||||
|
return kPRFailure;
|
||||||
|
}
|
||||||
|
catch (YAML::RepresentationException& ex)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "YAML representation error at line=%d col=%d: %s\n", ex.line, ex.column, ex.msg.c_str());
|
||||||
|
return kPRFailure;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Unexpected exception while parsing YAML config.\n");
|
||||||
|
return kPRFailure;
|
||||||
|
}
|
||||||
|
return kPRSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureDrivers(PRHandle proc, YAML::Node& yamlDoc)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -131,7 +151,7 @@ void ConfigureDrivers(PRHandle proc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureSwitches(PRHandle proc)
|
void ConfigureSwitches(PRHandle proc, YAML::Node& yamlDoc)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -191,17 +211,32 @@ void ConfigureBumperRule (PRHandle proc, int swNum, int coilNum, int pulseTime)
|
|||||||
PRSwitchUpdateRule(proc,swNum, kPREventTypeSwitchClosedNondebounced, &sw, drivers, numDriverRules);
|
PRSwitchUpdateRule(proc,swNum, kPREventTypeSwitchClosedNondebounced, &sw, drivers, numDriverRules);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureSwitchRules(PRHandle proc)
|
void ConfigureSwitchRules(PRHandle proc, YAML::Node& yamlDoc)
|
||||||
{
|
{
|
||||||
// WPC Flippers
|
// WPC Flippers
|
||||||
ConfigureWPCFlipperSwitchRule (proc, kFlipperLwRightSw, kFlipperLwRightMain, kFlipperLwRightHold, kFlipperPulseTime); // Lower Right WPC Flipper
|
const YAML::Node& flippers = yamlDoc[kFlippersSection];
|
||||||
ConfigureWPCFlipperSwitchRule (proc, kFlipperLwLeftSw, kFlipperLwLeftMain, kFlipperLwLeftHold, kFlipperPulseTime); // Lower Left WPC Flipper
|
for (YAML::Iterator flippersIt = flippers.begin(); flippersIt != flippers.end(); ++flippersIt)
|
||||||
ConfigureWPCFlipperSwitchRule (proc, kFlipperUpRightSw, kFlipperUpRightMain, kFlipperUpRightHold, kFlipperPulseTime); // Upper Right WPC Flipper
|
{
|
||||||
ConfigureWPCFlipperSwitchRule (proc, kFlipperUpLeftSw, kFlipperUpLeftMain, kFlipperUpLeftHold, kFlipperPulseTime); // Upper Left WPC Flipper
|
int swNum, coilMain, coilHold;
|
||||||
|
std::string flipperName;
|
||||||
|
*flippersIt >> flipperName;
|
||||||
|
yamlDoc[kSwitchesSection][flipperName] >> swNum;
|
||||||
|
yamlDoc[kCoilsSection][flipperName + "Main"] >> coilMain;
|
||||||
|
yamlDoc[kCoilsSection][flipperName + "Hold"] >> coilHold;
|
||||||
|
ConfigureWPCFlipperSwitchRule (proc, swNum, coilMain, coilHold, kFlipperPulseTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
const YAML::Node& bumpers = yamlDoc[kBumpersSection];
|
||||||
|
for (YAML::Iterator bumpersIt = bumpers.begin(); bumpersIt != bumpers.end(); ++bumpersIt)
|
||||||
|
{
|
||||||
|
int swNum, coilNum;
|
||||||
// WPC Slingshots
|
// WPC Slingshots
|
||||||
ConfigureBumperRule (proc, kSlingRightSw, kSlingRightCoil, kBumperPulseTime); // WPC Right Slingshot
|
std::string bumperName;
|
||||||
ConfigureBumperRule (proc, kSlingLeftSw, kSlingLeftCoil, kBumperPulseTime); // WPC Left Slingshot
|
*bumpersIt >> bumperName;
|
||||||
|
yamlDoc[kSwitchesSection][bumperName] >> swNum;
|
||||||
|
yamlDoc[kCoilsSection][bumperName] >> coilNum;
|
||||||
|
ConfigureBumperRule (proc, swNum, coilNum, kBumperPulseTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureDMD(PRHandle proc)
|
void ConfigureDMD(PRHandle proc)
|
||||||
@@ -325,26 +360,49 @@ void sigint(int)
|
|||||||
printf("Exiting...\n");
|
printf("Exiting...\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(const char **argv, int argc)
|
int main(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
// Set a signal handler so that we can exit gracefully on Ctrl-C:
|
// Set a signal handler so that we can exit gracefully on Ctrl-C:
|
||||||
signal(SIGINT, sigint);
|
signal(SIGINT, sigint);
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s <yaml machine description>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
const char *yamlFilename = argv[1];
|
||||||
|
|
||||||
// Assign a custom logging callback to demonstrate capturing log information from P-ROC:
|
// Assign a custom logging callback to demonstrate capturing log information from P-ROC:
|
||||||
PRLogSetCallback(TestLogger);
|
PRLogSetCallback(TestLogger);
|
||||||
|
|
||||||
|
YAML::Node yamlDoc;
|
||||||
|
LoadConfiguration(yamlDoc, yamlFilename);
|
||||||
|
|
||||||
|
PRMachineType machineType = kPRMachineInvalid;
|
||||||
|
std::string machineTypeString;
|
||||||
|
yamlDoc["PRGame"]["machineType"] >> machineTypeString;
|
||||||
|
if (machineTypeString == "wpc")
|
||||||
|
machineType = kPRMachineWPC;
|
||||||
|
else if(machineTypeString == "stern")
|
||||||
|
machineType = kPRMachineStern;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Unknown machine type: %s\n", machineTypeString.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Finally instantiate the P-ROC device:
|
// Finally instantiate the P-ROC device:
|
||||||
PRHandle proc = PRCreate(kPRMachineWPC);
|
PRHandle proc = PRCreate(machineType);
|
||||||
if (proc == kPRHandleInvalid)
|
if (proc == kPRHandleInvalid)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
ConfigureDMD(proc);
|
ConfigureDMD(proc);
|
||||||
ConfigureSwitches(proc); // Notify host for all debounced switch events.
|
ConfigureSwitches(proc, yamlDoc); // Notify host for all debounced switch events.
|
||||||
ConfigureSwitchRules(proc); // Flippers, slingshots
|
ConfigureSwitchRules(proc, yamlDoc); // Flippers, slingshots
|
||||||
|
|
||||||
// Make Drivers the last thing to configure so watchdog doesn't expire
|
// Make Drivers the last thing to configure so watchdog doesn't expire
|
||||||
// before the RunLoop begins.
|
// before the RunLoop begins.
|
||||||
ConfigureDrivers(proc);
|
ConfigureDrivers(proc, yamlDoc);
|
||||||
|
|
||||||
printf("Running. Hit Ctrl-C to exit.\n");
|
printf("Running. Hit Ctrl-C to exit.\n");
|
||||||
|
|
||||||
|
|||||||
@@ -225,10 +225,7 @@
|
|||||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||||
GCC_MODEL_TUNING = G5;
|
GCC_MODEL_TUNING = G5;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = /usr/local/include;
|
||||||
/usr/local/include,
|
|
||||||
"../../yaml-cpp/include",
|
|
||||||
);
|
|
||||||
INSTALL_PATH = /usr/local/lib;
|
INSTALL_PATH = /usr/local/lib;
|
||||||
PRODUCT_NAME = pinproc;
|
PRODUCT_NAME = pinproc;
|
||||||
};
|
};
|
||||||
@@ -240,10 +237,7 @@
|
|||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
GCC_MODEL_TUNING = G5;
|
GCC_MODEL_TUNING = G5;
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = /usr/local/include;
|
||||||
/usr/local/include,
|
|
||||||
"../../yaml-cpp/include",
|
|
||||||
);
|
|
||||||
INSTALL_PATH = /usr/local/lib;
|
INSTALL_PATH = /usr/local/lib;
|
||||||
PRODUCT_NAME = pinproc;
|
PRODUCT_NAME = pinproc;
|
||||||
};
|
};
|
||||||
@@ -284,8 +278,12 @@
|
|||||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||||
GCC_MODEL_TUNING = G5;
|
GCC_MODEL_TUNING = G5;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
HEADER_SEARCH_PATHS = /usr/local/include;
|
||||||
INSTALL_PATH = /usr/local/bin;
|
INSTALL_PATH = /usr/local/bin;
|
||||||
OTHER_LDFLAGS = "-lftdi";
|
OTHER_LDFLAGS = (
|
||||||
|
"-lyaml-cpp",
|
||||||
|
"-lftdi",
|
||||||
|
);
|
||||||
PREBINDING = NO;
|
PREBINDING = NO;
|
||||||
PRODUCT_NAME = pinproctest;
|
PRODUCT_NAME = pinproctest;
|
||||||
};
|
};
|
||||||
@@ -299,8 +297,12 @@
|
|||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||||
GCC_MODEL_TUNING = G5;
|
GCC_MODEL_TUNING = G5;
|
||||||
|
HEADER_SEARCH_PATHS = /usr/local/include;
|
||||||
INSTALL_PATH = /usr/local/bin;
|
INSTALL_PATH = /usr/local/bin;
|
||||||
OTHER_LDFLAGS = "-lftdi";
|
OTHER_LDFLAGS = (
|
||||||
|
"-lyaml-cpp",
|
||||||
|
"-lftdi",
|
||||||
|
);
|
||||||
PREBINDING = NO;
|
PREBINDING = NO;
|
||||||
PRODUCT_NAME = pinproctest;
|
PRODUCT_NAME = pinproctest;
|
||||||
ZERO_LINK = NO;
|
ZERO_LINK = NO;
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
|
|
||||||
#include "PRHardware.h"
|
#include "PRHardware.h"
|
||||||
#include "PRCommon.h"
|
#include "PRCommon.h"
|
||||||
#include "pinproc.h"
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t CreateRegRequestWord( uint32_t select, uint32_t addr, uint32_t num_words ) {
|
uint32_t CreateRegRequestWord( uint32_t select, uint32_t addr, uint32_t num_words ) {
|
||||||
|
|||||||
Reference in New Issue
Block a user