gamepad force feedback (vibration) on windows using raw input - c

I'm currently writing a cross platform library in C which includes gamepad support. Gamepad communication on windows is handled by both raw input and xinput, depending on the specific gamepad.
While xinput facilitates force feedback on xbox360 controllers, I have not found a way to do this using raw input. I have some gamepads that can give force feedback, but I cannot find a way to do this through raw input. Is there a way to do this?
I prefer not to use the directinput api, since it's deprecated and discouraged by Microsoft.
Edit:
Since I've implemented gamepads for a large part, maybe I can narrow down the question a bit. I suspect the amount of rumble motors in a gamepad can be found by reading the NumberOutputValueCaps of a HIDP_CAPS structure. This gives the correct result for all my test gamepads.
I'm using the funtcion HidP_GetUsageValue to read axis values, which works fine. Now I suspect calling HidP_SetUsageValue should allow me to change this output value, turning on the rumble motor. Calling this function fails, however. Should this function be able to access rumble motors?

HidP_SetUsageValue() only modifies a report buffer -- you need to first prepare an appropriately-sized buffer (which may be why the function was failing; input reports and output reports won't necessarily be the same size) then send it to the device before it will have any effect. MSDN suggests you can use HidD_SetOutputReport() for that purpose, but I had better luck with WriteFile(), following the sample code at: https://code.msdn.microsoft.com/windowshardware/HClient-HID-Sample-4ec99697/sourcecode?fileId=51262&pathId=340791466
This snippet (based on the Linux driver) lets me control the motors and LED on a DualShock 4:
const char *path = /* from GetRawInputDeviceInfo(RIDI_DEVICENAME) */;
HANDLE hid_device = CreateFile(path, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, 0, NULL);
assert(hid_device != INVALID_HANDLE_VALUE);
uint8_t buf[32];
memset(buf, 0, sizeof(buf));
buf[0] = 0x05;
buf[1] = 0xFF;
buf[4] = right_motor_strength; // 0-255
buf[5] = left_motor_strength; // 0-255
buf[6] = led_red_level; // 0-255
buf[7] = led_green_level; // 0-255
buf[8] = led_blue_level; // 0-255
DWORD bytes_written;
assert(WriteFile(hid_device, buf, sizeof(buf), &bytes_written, NULL));
assert(bytes_written == 32);
(EDIT: fixed buffer offsets)

Related

OpenCL: clBuildProgram Fails without log

What I am trying to accomplish:
I am trying to render some stuff in OpenCL and write it to the OpenGL Framebuffer (As it is the only Framebuffer I can get via Renderbuffers etc., but I will gladly accept any others I could use - You will not help me tho by telling to use glsl shaders)
The Problem:
As the Title says, the OpenCL function clBuildProgram Fails with error -11 (CL_​BUILD_​PROGRAM_​FAILURE). This wouldn't be an issue, but the Log from the CL compiler is empty. I doublechecked my log code, but it should be fine. I posted it down below none the less, just so you can see yourself.
What I tried to fix:
Googling, of course
Reading the docs from the Khronos Groups
Checking, if my device supports the "cl_khr_gl_sharing" extension, which it does (it is contained in the string returned from clGetDeviceInfo(device_id, CL_DEVICE_EXTENSIONS, retSize, extensions, &retSize);)
Modifying the shader/kernel:
Made some intentional errors, to see if the logging code actually works (which it does)
And minifying the shader/kernel, to see if some things in the shader do not work as they should (as I've read, that some missing things can make the compiler for cl crash)
What I found out:
From the last point of what I tried, I noticed, that the write_imageui and read_imageui opencl functions make the compiler fail to compile my code (this is why I checked for the "cl_khr_gl_sharing" extension)
Furthermore:
My operating System is Windows 10, the C Compiler I am using is GCC (I do not know, how that could help, since the host program compiles fine, but here it is none the less)
Some Code:
The Shader/Kernel (as minified as possible to reproduce the problem, I hope also for you; the last two lines of calls are what I think causes the opencl compiler to not work; the other stuff is in there, to make it a shader, that can actually process something, once it is working):
#define ScreenWidth 1000
#define ScreenHight 1000
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_NONE | CLK_FILTER_NEAREST;
__kernel void rainbow(__read_write image2d_t asd) {
int i = get_global_id(0);
unsigned int x = i%ScreenWidth;
unsigned int y = i/ScreenHight;
uint4 pixel;
pixel = read_imageui(asd, sampler, (int2)(x, y));
write_imageui(asd, (int2)(x, y), pixel);
}
Minified Call Code (C) which does all the initialization stuff necessary (note: the log buffer is dynamically changeable):
cl_program program = clCreateProgramWithSource(contextZ, 1, (const char **)&source_str, (const size_t *)&source_size, &ret);
size_t retSize = 0;
clGetDeviceInfo(device_id, CL_DEVICE_EXTENSIONS, 0, NULL, &retSize);
char extensions[retSize];
clGetDeviceInfo(device_id, CL_DEVICE_EXTENSIONS, retSize, extensions, &retSize);
printf("%s\n", extensions);
// Build the program
ret = clBuildProgram(program, 1, &device_id, NULL, NULL, NULL);
if (ret == CL_BUILD_PROGRAM_FAILURE) {
l_logError("Could not build Kernel!");
// Determine the size of the log
size_t log_size;
printf(" reta: %i\n", clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size));
// Allocate memory for the log
char *log = (char *) malloc(log_size);
// Get the log
printf(" retb: %i\n", clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, log_size, log, NULL));
// Print the log
printf(" ret-val: %i\n", ret);
printf("%s\n", log);
}
You might be interested in the output (Last 2 lines are caused by the Kernel not beeing built. The program could be made from the source though - look at the code):
E: Could not build Kernel!
reta: 0
retb: 0
ret-val: -11
E: Could not create Kernel!
kernel error: -45
Did anybody else have a similar problem? Any Ideas, what I should do about it? Might there be a Header for the cl Kernel/Shader, which I need to include in it? It could be possible, that my clBuildProgram call is incorrect? (I read somebody did not pass a device, so maybe something else could be missing in my code)
Make sure to tell me, if you need further details, so I can provide them (I cannot think of any other you might need right now)
Thanks in Advance for your time!
EDIT:
According to the specification, a device needs to support the CL_​DEVICE_​IMAGE_​SUPPORT extension, which it does
I checked it using this:
cl_bool image_support = CL_FALSE;
clGetDeviceInfo(device_id, CL_DEVICE_IMAGE_SUPPORT, sizeof(cl_bool), &image_support, NULL);
printf("image_support: %i\n", image_support);
Which outputs:
image_support: 1
aka. CL_TRUE
Edit 2:
It turns out, OpenCL extensions need to be enabled in the kernel: https://www.khronos.org/registry/OpenCL/sdk/2.2/docs/man/html/EXTENSION.html
Adding #pragma OPENCL EXTENSION all : enable in the first line of the kernel/shader does results in the same issue tho
EDIT 3:
Removing the __read_write flag from the kernel image paramters or replace it with something else (like __read_only) causes the OpenCL compiler to crash or loop infinetly, as clBuildProgram never returns (or will return in like a very long time)
What I found out in the last few days may have been slightly incorrect.
My Edit 3 (from the original post/question) states, that replacing the __read_write with an __read_only causes the compiler to completly crash. This is incorrect, as I can now confirm, I just didn't add any more debug-output code after the compilation call. Adding some more debug lines after the clBuildProgram call shows, that it actually works.
I do not know, why this causes the OpenCL compiler to output literally nothing as an error, and the vendors of the driver should definetly fix this/output something (Device info in comment), to make development somewhat easier. (Even just a warning will be helpful)
I found this stackoverflow post, discussing a similar problem: OpenCL - Pass image2d_t twice to get both read and write from kernel?. This is how I even figured out, that this can cause devastating problems.
To be fair, the official docs state this, but I understood it more like they can be combined to __read_write (read_only | write_only == __read_write):
aQual in the following table refers to one of the access qualifiers. For write functions this may be write_only or read_write.
I replaced my kernel with only a write_image call (write_imageui) and set the image2d_t to be __write_only, to get minimal debug possibilities. This caused the shader/kernel to compile succesfully, but the screen is still empty. But latter is a matter for another question.

Keil uVision - Atmel SAM3U Read Unique Identifier

I've been trying to read the Unique Identifier (UID) from a Atmel SAM3U MCU, but it's proven more difficult than it needs to be to make it happen. Does anyone have any examples or can suggest how to read it properly? Whenever I do, I wait in a do while loop (like the documentation states) for the EEFC (Flash ROM) status register to change states, but it never does so the MCU is then stuck in a loop.
Here is the code I'm using
// must run this from SRAM
__attribute__((section(".ARM.__at_0x20080000"))) void Get_Unique_ID(unsigned int *pdwUniqueID)
{
Efc *p_efc;
unsigned int status;
// clear the array
pdwUniqueID[0] = 0;
pdwUniqueID[1] = 0;
pdwUniqueID[2] = 0;
pdwUniqueID[3] = 0;
// send the Start Read Unique Identifier command (STUI) by writing the Flash Command Register with the STUI command
p_efc->EEFC_FCR = EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FCMD_STUI;
// wait for the Flash Programming Status Register (EEFC_FSR) to fall
do { status = p_efc->EEFC_FSR; }
while ((status & EEFC_FSR_FRDY) == EEFC_FSR_FRDY);
// the Unique Identifier is located in the first 128 bits of the Flash memory mapping
pdwUniqueID[0] = *(unsigned int *)IFLASH0_ADDR;
pdwUniqueID[1] = *(unsigned int *)(IFLASH0_ADDR + 4);
pdwUniqueID[2] = *(unsigned int *)(IFLASH0_ADDR + 8);
pdwUniqueID[3] = *(unsigned int *)(IFLASH0_ADDR + 12);
// to stop the Unique Identifier mode, the user needs to send the Stop Read unique Identifier
// command (SPUI) by writing the Flash Command Register with the SPUI command
p_efc->EEFC_FCR = EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FCMD_SPUI;
// when the Stop Read Unique Unique Identifier command (SPUI) has been performed
// the FRDY bit in the Flash Programming Status Register (EEFC_FSR) rises
do { status = p_efc->EEFC_FSR; }
while ((status & EEFC_FSR_FRDY) != EEFC_FSR_FRDY);
}
Note that __attribute__((section(".ARM.__at_0x20080000"))) isn't the best method to dynamically assign this function to SRAM via the linker and any suggestions on how to make it more dynamic would be appreciated.
SOLVED The problem was the chips I had were fake so SAM-BA was returning whatever was at the SRAM buffer address it specified. It's a bug in SAM-BA since if it received 0x00000000, it should give an error or warning message and then stop reading. Do not buy fake chips from China!
Thanks.
I don't believe p_efc is correctly initialized.
You create a pointer to a Efc datastructure which thus points to something.
You then write something to somewhere and are expect it to work.
Efc *p_efc;
p_efc->EEFC_FCR = EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FCMD_STUI;
My guess would be that you need to intialize it to the correct EEFC base address. The datasheet has the following to say:
The SAM3U4 (256 Kbytes internal Flash
version) embeds two EEFC (EEFC0 for Flash0 and EEFC1 for Flash1)
whereas the SAM3U2/1 embeds one EEFC.
So depending on your MCU version you need to address EEFC0 or EEFC1. I'm assuming that you use libopencm3 but this will work for any other library. Look for the EEFC location define. Following the defines/files/links we get to this page, it tells us to point our Efc pointer to EEFC0_BASE or EEFC1_BASE. I would advise you to use the EEFC0 and EEFC1 defines though as it makes your code portabler.
So your code should work if your Efc is located in EEFC0 if you do:
Efc *p_efc = EEFC0;

Running a block of code from RAM instead of flash

In the following program, what is the meaning of the line of code
fnRAM_code((volatile unsigned char *)FLASH_STATUS_REGISTER); // execute the command from SRAM
in the below section of code. I have some idea about what is happening here,In order to overcome read while write violation, copying the code from flash to RAM using the above lines of code. But what is exact meaning of these lines.
static int fnProgram(unsigned long *ptrWord, unsigned long *ptr_ulWord)
{
while ((FTFL_FSTAT & FTFL_STAT_CCIF) == 0) {} // wait for previous commands to complete
if ((FTFL_FSTAT & (FTFL_STAT_ACCERR | FTFL_STAT_FPVIOL | FTFL_STAT_RDCOLERR)) != 0) { // check for errors in previous command
FTFL_FSTAT = (FTFL_STAT_ACCERR | FTFL_STAT_FPVIOL | FTFL_STAT_RDCOLERR); // clear old errors
}
FTFL_FCCOB0 = FCMD_PROGRAM; // enter the command sequence
FTFL_FCCOB1 = (unsigned char)(((CAST_POINTER_ARITHMETIC)ptrWord) >> 16); // set address in flash
FTFL_FCCOB2 = (unsigned char)(((CAST_POINTER_ARITHMETIC)ptrWord) >> 8);
FTFL_FCCOB3 = (unsigned char)((CAST_POINTER_ARITHMETIC)ptrWord);
FTFL_FCCOB7_4 = *ptr_ulWord++; // enter the long word to be programmed
FTFL_FCCOBB_8 = *ptr_ulWord; // enter the second long word to be programmed
uDisable_Interrupt(); // protect this region from interrupts
fnRAM_code((volatile unsigned char *)FLASH_STATUS_REGISTER); // execute the command from SRAM
uEnable_Interrupt(); // safe to accept interrupts again
return (FTFL_FSTAT & (FTFL_STAT_ACCERR | FTFL_STAT_FPVIOL | FTFL_STAT_MGSTAT0)); // if there was an error this will be non-zero
}
The only code that needs to be in RAM is this:
static void fnFlashRoutineInRam(volatile unsigned char *ptrFTFL_BLOCK)
{
*ptrFTFL_BLOCK = FTFL_STAT_CCIF; // launch the command - this clears the FTFL_STAT_CCIF flag (register is FTFL_FSTAT)
while ((*ptrFTFL_BLOCK & FTFL_STAT_CCIF) == 0) {} // wait for the command to terminate
}
This looks like older NXP (former Freescale/Motorola) HCS08, HCS12 or Coldfire. On those devices, you have different cases when writing a flash driver: either you can execute it from flash or you cannot. This entirely depends on which "bank" the program flash belongs to: generally you cannot execute code on a MCU from the very same flash bank it is currently programming.
So ideally you put the flash programming code in another bank, but some devices only have one single flash bank. Then they provide a work-around by executing the code from RAM, which is kind of a quick & dirty fix.
Commonly they solve this by providing an array of raw data op codes. This array of op codes is copied to RAM and then they set a function pointer to point at the RAM address. I suspect fnRAM_code is such a function pointer. The (volatile unsigned char *)FLASH_STATUS_REGISTER part is simply passing on the address of the flash status register. Likely, FLASH_STATUS_REGISTER is synonymous with FSTAT.
The uDisable_Interrupt(); and uEnable_Interrupt(); should correspond to asm SEI and asm CLI respectively, blocking all maskable interrupts from triggering during the flash write, which would potentially cause the write to fail or the program to hang up.
There should be app notes available describing all of this in detail.
Please note that this code is very close to the hardware and relies on tons of poorly-defined behavior. I wouldn't count on it compiling as expected on anything but the Codewarrior compiler. gcc would for example spew out numerous strict aliasing bugs.

DirectShow data copy is TOO slow

Have USB 3.0 HDMI Capture device. It uses YUY2 format (2 bytes per pixel) and 1920x1080 resolution.
Video capture Output Pin connects directly to Video Render input Pin.
And all works good. It shows me 1920x1080 without any freezes.
But I need to make screenshot every second. So this is what I do:
void CaptureInterface::ScreenShoot() {
IMemInputPin* p_MemoryInputPin = nullptr;
hr = p_RenderInputPin->QueryInterface(IID_IMemInputPin, (void**)&p_MemoryInputPin);
IMemAllocator* p_MemoryAllocator = nullptr;
hr = p_MemoryInputPin->GetAllocator(&p_MemoryAllocator);
IMediaSample* p_MediaSample = nullptr;
hr = p_MemoryAllocator->GetBuffer(&p_MediaSample, 0, 0, 0);
long buff_size = p_MediaSample->GetSize(); //buff_size = 4147200 Bytes
BYTE* buff = nullptr;
hr = p_MediaSample->GetPointer(&buff);
//BYTE CaptureInterface::ScreenBuff[1920*1080*2]; defined in header
//--------- TOO SLOW (1.5 seconds for 4 MBytes) ----------
std::memcpy(ScreenBuff, buff, buff_size);
//--------------------------------------------
p_MediaSample->Release();
p_MemoryAllocator->Release();
p_MemoryInputPin->Release();
return;
}
Any other operations with this buffer is very slow too.
But If I use memcpy on other data (2 arrays in my class for example same size 4MB) It is very fast. <0.01sec
Video memory is (might be) slow to read back by its nature (e.g. VMR9 IBasicVideo->GetCurrentImage very slow and you can find other references). You normally want to grab the data before it actually reaches video memory.
Additionally, the way you read data is not quite reliable. You don't know what frame you are actually copying and it might so happen that you even read blackness or garbage, or vice versa your acquiring access to buffer freezes the main video streaming. This is because you are grabbing an unused buffer from pool of available buffers rather than a buffer that corresponds to specific video frame. Your getting an image from such buffer happen in a fragile assumption that unused data from previously streamed frame was initialized and is not yet overwritten by anything else.

invalid argument error when setting yres_virtual in fb_var_screeninfo

I'm trying to make an application for linux that writes directly to the framebuffer /dev/fb0. In order to make it double buffered I try to make the virtual screen be double the size of the screen. This is the program I wrote:
struct fb_var_screeninfo screeninfo_var;
struct fb_fix_screeninfo screeninfo_fixed;
unsigned int* screenbuffer;
void gfx_init()
{
fb0 = open("/dev/fb0", O_RDWR);
if(fb0 == 0)
error("Could not open framebuffer located in /dev/fb0!");
if (ioctl(fb0, FBIOGET_FSCREENINFO, &screeninfo_fixed) == -1)
error("Could not retrive fixed screen info!");
if (ioctl(fb0, FBIOGET_VSCREENINFO, &screeninfo_var) == -1)
error("Could not retrive variable screen info!");
screeninfo_var.xres_virtual = screeninfo_var.xres;
screeninfo_var.yres_virtual = screeninfo_var.yres * 2;
screeninfo_var.width = screeninfo_var.xres;
screeninfo_var.height = screeninfo_var.yres;
screeninfo_var.xoffset = 0;
screeninfo_var.yoffset = 0;
if (ioctl(fb0, FBIOPUT_VSCREENINFO, &screeninfo_var) == -1)
error("Could not set variable screen info!");
info("Detected monitor of %ix%i pixels using %i bit colors.",screeninfo_var.xres, screeninfo_var.yres, screeninfo_var.bits_per_pixel);
screenbuffersize = screeninfo_var.xres_virtual * screeninfo_var.yres_virtual * screeninfo_var.bits_per_pixel/8;
screenbuffer = (unsigned int *)mmap(0, screenbuffersize, PROT_READ | PROT_WRITE, MAP_SHARED, fb0, 0);
if( (long)screenbuffer == 0 || (long)screenbuffer == -1 )
error("Failed to map framebuffer to device memory!");
}
The program failes on ioctl(fb0, FBIOPUT_VSCREENINFO, &screeninfo_var) reporting the error invalid argument. When removing the line screeninfo_var.yres_virtual = screeninfo_var.yres * 2; it runs fine (but without double buffering).
Does someone see what I do wrong here?
To save anyone a headache in the future, there is a way to properly double buffer using low level graphics on linux (such as /dev/fb0). However per this thread: https://forum.odroid.com/viewtopic.php?f=55&t=8741 it is not possible to truly double buffer the framebuffer by creating a virtual framebuffer of double the size of the original one (I've read that the raspberry PI might be an exception to this rule because it is backed by a different driver).
The right way to double buffer using low level graphics on linux is to go through libdrm (or /dev/dri/card0). There is a really nice example that I followed myself here: https://github.com/dvdhrm/docs/blob/master/drm-howto/modeset-vsync.c. It wasn't very hard to convert my /dev/fb0 code to /dev/dri/card0 code.
Anyways I hope I've helped someone out here who might come across this in the future.
This is a common question and it is caused by the limitation of the video driver. E.g., Intel's 810 chipset only allows 640x480 resolution and the Broadcom's limit the width to no more than 1200 (http://www.raspberrypi.org/phpBB3/viewtopic.php?f=66&t=29968).
Not much you can do here, unless you change the driver or the videocard itself (if possible).
EDIT: if you are on a PC, try using the vesafb, not the intel's driver.
There's a hint here: http://www.mail-archive.com/debian-russian#lists.debian.org/msg27725.html

Resources