Rust bindgen clang error, incompatible constant for this __builtin_neon function - objective-c

Trying to generate bindings for an Obj-C++ header which is a part of Superpowered crossplatform audio library with rust bindgen.
I am in Catalina 10.15.6,
clang version 10.0.1
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: /usr/local/opt/llvm/bin
I got stuck with this error. Searched a lot but it seems rare.
I have tried to use the clang provided in the Xcode App bundle to try but couldn't manage to do it.
The library builds fine in Xcode, I didn't get the same error there.
The header :
#import <AVFoundation/AVFoundation.h>
/// #brief Output channel mapping for iOS audio I/O.
/// This structure maps the channels you provide in the audio processing callback to the appropriate output channels.
/// You can have multi-channel (more than a single stereo channel) output if a HDMI or USB audio device is connected. iOS does not provide multi-channel output for other audio devices, such as wireless audio accessories.
/// #em Example:
/// Let's say you have four output channels, and you'd like the first stereo pair on USB 3+4, and the other stereo pair on the iPad's headphone socket.
/// 1. Set deviceChannels[0] to 2, deviceChannels[1] to 3.
/// 2. Set USBChannels[2] to 0, USBChannels[3] to 1.
/// #em Explanation:
/// - Your four output channels are having the identifiers: 0, 1, 2, 3.
/// - Every iOS device has just one stereo built-in output. This is represented by deviceChannels, and (1.) sets your second stereo pair (2, 3) to these.
/// - You other stereo pair (0, 1) is mapped to USBChannels. USBChannels[2] represents the third USB channel.
/// #since Multi-channel output is available in iOS 6.0 and later.
typedef struct multiOutputChannelMap {
int deviceChannels[2]; ///< The iOS device's built-in output channels. Write only.
int HDMIChannels[8]; ///< HDMI output channels. Write only.
int USBChannels[32]; ///< USB output channels. Write only.
int numberOfHDMIChannelsAvailable; ///< Number of available HDMI output channels. Read only.
int numberOfUSBChannelsAvailable; ///< Number of available USB output channels. Read only.
bool headphoneAvailable; ///< Something is plugged into the iOS device's headphone socket or not. Read only.
} multiOutputChannelMap;
/// #brief Input channel mapping for iOS audio I/O.
/// Similar to the output channels, you can map the input channels to channels in the audio processing callback. This feature works with USB only.
/// Let's say you set the channel count to 4, so RemoteIO provides 4 input channel buffers. Using this struct, you can map which USB input channel appears on the specific buffer positions.
/// #since Available in iOS 6.0 and later.
/// #see #c multiOutputChannelMap
typedef struct multiInputChannelMap {
int USBChannels[32]; ///< Example: set USBChannels[0] to 3, to receive the input of the third USB channel on the first buffer. Write only.
int numberOfUSBChannelsAvailable; ///< Number of USB input channels. Read only.
} multiInputChannelMap;
#protocol SuperpoweredIOSAudioIODelegate;
/// #brief The audio processing callback prototype.
/// #return Return false for no audio output (silence).
/// #param clientData A custom pointer your callback receives.
/// #param inputBuffers Input buffers.
/// #param inputChannels The number of input channels.
/// #param outputBuffers Output buffers.
/// #param outputChannels The number of output channels.
/// #param numberOfFrames The number of frames requested.
/// #param samplerate The current sample rate in Hz.
/// #param hostTime A mach timestamp, indicates when this buffer of audio will be passed to the audio output.
typedef bool (*audioProcessingCallback) (void *clientData, float **inputBuffers, unsigned int inputChannels, float **outputBuffers, unsigned int outputChannels, unsigned int numberOfFrames, unsigned int samplerate, unsigned long long hostTime);
/// #brief Handles all audio session, audio lifecycle (interruptions), output, buffer size, samplerate and routing headaches.
/// #warning All methods and setters should be called on the main thread only!
#interface SuperpoweredIOSAudioIO: NSObject {
int preferredBufferSizeMs;
int preferredSamplerate;
bool saveBatteryInBackground;
bool started;
}
#property (nonatomic, assign) int preferredBufferSizeMs; ///< The preferred buffer size in milliseconds. Recommended: 12.
#property (nonatomic, assign) int preferredSamplerate; ///< The preferred sample rate in Hz.
#property (nonatomic, assign) bool saveBatteryInBackground; ///< Save battery if output is silence and the app runs in background mode. True by default.
#property (nonatomic, assign, readonly) bool started; ///< Indicates if the instance has been started.
/// #brief Constructor.
/// #param delegate The object fully implementing the SuperpoweredIOSAudioIODelegate protocol. Not retained.
/// #param preferredBufferSize The initial value for preferredBufferSizeMs. 12 is good for every iOS device (512 frames).
/// #param preferredSamplerate The preferred sample rate. 44100 or 48000 are recommended for good sound quality.
/// #param audioSessionCategory The audio session category. Audio input is enabled for the appropriate categories only!
/// #param channels The number of output channels in the audio processing callback regardless the actual hardware capabilities. The number of input channels in the audio processing callback will reflect the actual hardware configuration.
/// #param callback The audio processing callback.
/// #param clientdata Custom data passed to the audio processing callback.
- (id)initWithDelegate:(id<SuperpoweredIOSAudioIODelegate>)delegate preferredBufferSize:(unsigned int)preferredBufferSize preferredSamplerate:(unsigned int)preferredSamplerate audioSessionCategory:(NSString *)audioSessionCategory channels:(int)channels audioProcessingCallback:(audioProcessingCallback)callback clientdata:(void *)clientdata;
/// #brief Starts audio I/O.
/// #return True if successful, false if failed.
- (bool)start;
/// #brief Stops audio I/O.
- (void)stop;
/// #brief Call this to re-configure the channel mapping.
- (void)mapChannels;
/// #brief Call this to re-configure the audio session category (such as enabling/disabling recording).
- (void)reconfigureWithAudioSessionCategory:(NSString *)audioSessionCategory;
#end
/// #brief You MUST implement this protocol to use SuperpoweredIOSAudioIO.
#protocol SuperpoweredIOSAudioIODelegate
/// #brief The audio session may be interrupted by a phone call, etc. This method is called on the main thread when the interrupt starts.
#optional
- (void)interruptionStarted;
/// #brief The audio session may be interrupted by a phone call, etc. This method is called on the main thread when audio resumes.
#optional
- (void)interruptionEnded;
/// #brief Called if the user did not grant a recording permission for the app.
#optional
- (void)recordPermissionRefused;
/// #brief This method is called on the main thread when a multi-channel audio device is connected or disconnected.
/// #param outputMap Map the output channels here.
/// #param inputMap Map the input channels here.
/// #param externalAudioDeviceName The name of the attached audio device, such as the model of the sound card.
/// #param outputsAndInputs A human readable description about the available outputs and inputs.
#optional
- (void)mapChannels:(multiOutputChannelMap *)outputMap inputMap:(multiInputChannelMap *)inputMap externalAudioDeviceName:(NSString *)externalAudioDeviceName outputsAndInputs:(NSString *)outputsAndInputs;
#end
build.rs :
use std::env;
use std::path::PathBuf;
fn main() {
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let out_dir = env::var("OUT_DIR").unwrap();
println!(
"cargo:rustc-flags=-l static=libSuperpoweredAudioIOS -L native={}/Superpowered",
manifest_dir
);
let bindings = bindgen::Builder::default()
.header("Superpowered/OpenSource/SuperpoweredIOSAudioIO.h")
.clang_arg("-ObjC++")
.clang_arg("-v")
.clang_arg("--target=arm64-apple-ios")
.clang_arg("--sysroot=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.generate()
.expect("Unable to generate bindings");
let out_path = PathBuf::from(out_dir);
bindings
.write_to_file(out_path.join("SuperpoweredIOSAudioIOBindings.rs"))
.expect("Couldn't write bindings!");
}
Clang errors with :
--- stderr
clang version 5.0.2 (tags/RELEASE_502/final)
Target: arm64-apple-ios
Thread model: posix
InstalledDir:
ignoring nonexistent directory "/usr/include"
ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk/usr/include/c++/v1"
ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk/usr/local/include"
ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk/Library/Frameworks"
ignoring duplicate directory "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk/usr/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/local/Cellar/llvm/10.0.1_1/lib/clang/10.0.1/include
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk/usr/include
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk/System/Library/Frameworks
/usr/local/opt/llvm#5/include/c++/v1
/usr/local/opt/llvm#5/lib/clang/5.0.2/include
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk/System/Library/Frameworks (framework directory)
End of search list.
/usr/local/Cellar/llvm/10.0.1_1/lib/clang/10.0.1/include/arm_neon.h:32395:25: error: incompatible constant for this __builtin_neon function
/usr/local/Cellar/llvm/10.0.1_1/lib/clang/10.0.1/include/arm_neon.h:32416:25: error: incompatible constant for this __builtin_neon function
/usr/local/Cellar/llvm/10.0.1_1/lib/clang/10.0.1/include/arm_neon.h:33990:23: error: use of undeclared identifier '__builtin_neon_vrndns_f32'
/usr/local/Cellar/llvm/10.0.1_1/lib/clang/10.0.1/include/arm_neon.h:32395:25: error: incompatible constant for this __builtin_neon function, err: true
/usr/local/Cellar/llvm/10.0.1_1/lib/clang/10.0.1/include/arm_neon.h:32416:25: error: incompatible constant for this __builtin_neon function, err: true
/usr/local/Cellar/llvm/10.0.1_1/lib/clang/10.0.1/include/arm_neon.h:33990:23: error: use of undeclared identifier '__builtin_neon_vrndns_f32', err: true
Got stuck here, any ideas ?
Found this here:
def err_invalid_neon_type_code : Error<
"incompatible constant for this __builtin_neon function">
Latest news :) :
--target=armv7-apple-ios builds fine.
--target=arm64-apple-ios has the error.
I have updated to Xcode 12.0, will try and report.

Try --target=aarch64-apple-ios. At least rustup target list doesn't have arm64-apple-ios, it has aarch64-apple-ios.

Related

(STM32) Wrinting into flash with HAL_FLASH_PROGRAM does not work

I am trying to write on the flash memory of an STM32L476RG with the HAL_FLASH_Program function but it always returns an error.
static FLASH_EraseInitTypeDef EraseInitStruct;
uint32_t PAGEError;
uint32_t Address = 0x080FFF10;
uint64_t data = 5;
/* Unlock the Flash to enable the flash control register access *************/
HAL_FLASH_Unlock();
/* Erase the user Flash area*/
/* Fill EraseInit structure*/
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.Page = Address;
EraseInitStruct.NbPages = 1;
if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)
{
/*Error occurred while page erase.*/
HAL_FLASH_GetError ();
}
/*Write into flash*/
HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, 0x1FFF7000, data);
if (status== HAL_OK)
{
printf("it works\n\r");
}
else
{
/* Error occurred while writing data in Flash memory*/
HAL_FLASH_GetError();
}
HAL_FLASH_Lock();
I tried to find wthe flash error code with the HAL_FLASH_GetError() function.
The error code I get is "168" (0xa8 in Hex) and I have no idea to what it corresponds.
My questions :
What error is the code 168 (0xa8 in Hex)
what do i need to change so that HAL_FLASH_Program works properly
The problem is how the fields in EraseInitStruct are being set. The HAL driver for some STM32 parts expects an address. However, the HAL library for the STM32L476 expects a page number.
typedef struct
{
uint32_t TypeErase; /*!< Mass erase or page erase.
This parameter can be a value of #ref FLASH_Type_Erase */
uint32_t Banks; /*!< Select bank to erase.
This parameter must be a value of #ref FLASH_Banks
(FLASH_BANK_BOTH should be used only for mass erase) */
uint32_t Page; /*!< Initial Flash page to erase when page erase is disabled
This parameter must be a value between 0 and (max number of pages in the bank - 1)
(eg : 255 for 1MB dual bank) */
uint32_t NbPages; /*!< Number of pages to be erased.
This parameter must be a value between 1 and (max number of pages in the bank - value of initial page)*/
} FLASH_EraseInitTypeDef;
So you need to set the page number correctly, and also specify which flash bank you are trying to erase:
EraseInitStruct.Banks = FLASH_BANK_2;
EraseInitStruct.Page = 255u;
It is good practice to check the result of all HAL function calls, and abort the operation if there is an error.
#Lundin brought up a good point about possibly being unable to erase / program the flash bank that you are running code from. This is an issue for some devices, but the reference manual for the STM32L476 (in section 3.3.5) says this is ok:
... during a program/erase operation to the Flash memory, any attempt to read the same Flash memory bank will stall the bus. The read operation will proceed correctly once the program/erase operation has completed.

buffers in CCL code samples along with the oneapi toolkit

I Was going through the CCL code samples along with the oneapi toolkit.
In the below DPC++(SYCL) code initially sendbuf a buffer is created in the cpu side and is not initialised and in the part where offloading to target device takes place the dev_acc_sbuf[id] variable, which is a variable in the kernel scope is modified. This variable(dev_acc_sbuf) is not hence used in the program neither is its value copied back to sendbuf.Then in the next line the sendbuf variable is used for allreduce. I am not able to understand how changing the dev_acc_sbuf makes change in the sendbuf.
cl::sycl::queue q;
cl::sycl::buffer<int, 1> sendbuf(COUNT);
/* open sendbuf and modify it on the target device side */
q.submit([&](cl::sycl::handler& cgh) {
auto dev_acc_sbuf = sendbuf.get_access<mode::write>(cgh);
cgh.parallel_for<class allreduce_test_sbuf_modify>(range<1>{COUNT}, [=](item<1> id) {
dev_acc_sbuf[id] += 1;
});
});
/* invoke ccl_allreduce on the CPU side */
ccl_allreduce(&sendbuf,
&recvbuf,
COUNT,
ccl_dtype_int,
ccl_reduction_sum,
NULL,
NULL,
stream,
&request);
In the line "auto dev_acc_sbuf = sendbuf.get_access<mode::write>(cgh);" the dev_acc_sbuf is a handle that accesses sendbuf and not a seperate buffer. The changes made in the dev_acc_sbuf handle gets reflected to the original buffer ie the sendbuffer . This is an advantage in SYCL as the changes made in the kernel scope is automatically copied back to the original variable
On most systems, the host and the device do not share physical memory, the CPU might use RAM and the GPU might use its own global memory. SYCL needs to know which data it will be sharing between the host and the devices.
For this purpose, SYCL uses its buffers, the buffer class is generic over the element type and the number of dimensions. When passed a raw pointer, the buffer(T* ptr, range size) constructor takes ownership of the memory it has been passed. This means that we absolutely cannot use that memory ourselves while the buffer exists, which is why we begin a C++ scope. At the end of their scope, the buffers will be destroyed and the memory returned to the user. A size argument is a range object, which has to have the same number of dimensions as the buffer and is initialized with the number of elements in each dimension. Here, we have one dimension with one element.
Buffers are not associated with a particular queue or context, so they are capable of handling data transparently between multiple devices.
Accessors are used to access request control over the device memory from the buffer objects. Their modes will take care of data movement between host and device. So we need not have to explicitly copy back the result from device to host.
Below is the example for more clarification:
#include <bits/stdc++.h>
#include <CL/sycl.hpp>
using namespace std;
class vector_addition;
int main(int, char**) {
//creating host memory
int *a=(int *)malloc(10*sizeof(int));
int *b=(int *)malloc(10*sizeof(int));
int *c=(int *)malloc(10*sizeof(int));
for(int i=0;i<10;i++){
a[i]=i;
b[i]=10-i;
}
cl::sycl::default_selector device_selector;
cl::sycl::queue queue(device_selector);
std::cout << "Running on "<< queue.get_device().get_info<cl::sycl::info::device::name>()<< "\n";
{
//creating buffer from pointer of host memory
cl::sycl::buffer<int, 1> a_sycl{a, cl::sycl::range<1>{10} };
cl::sycl::buffer<int, 1> b_sycl{b, cl::sycl::range<1>{10} };
cl::sycl::buffer<int, 1> c_sycl{c, cl::sycl::range<1>{10} };
queue.submit([&] (cl::sycl::handler& cgh) {
//creating accessor of buffer with proper mode
auto a_acc = a_sycl.get_access<cl::sycl::access::mode::read>(cgh);
auto b_acc = b_sycl.get_access<cl::sycl::access::mode::read>(cgh);
auto c_acc = c_sycl.get_access<cl::sycl::access::mode::write>(cgh);//responsible for copying back to host memory
//kernel for execution
cgh.parallel_for<class vector_addition>(cl::sycl::range<1>{ 10 }, [=](cl::sycl::id<1> idx) {
c_acc[idx] = a_acc[idx] + b_acc[idx];
});
});
}
for(int i=0;i<10;i++){
cout<<c[i]<<" ";
}
cout<<"\n";
return 0;
}

Accessing Kinect Accelerometer

I want to access Kinect accelerometer to determine whether the device carrier is moving or not. Is it possible with Kinect accelerometer? If it so, how can i do that? is ofxKinect framework useful for this purpose?
The microsoft SDK contains a method to read the accelerometer: KinectSensor.AccelerometerGetCurrentReading
private void OnAllFramesReady(object sender, AllFramesReadyEventArgs e)
{
if (this.KinectSensor == null)
{
return;
}
Vector4 reading = this.KinectSensor.AccelerometerGetCurrentReading();
}
ofxKinect has a few methods that sound promising:
/// get the XYZ accelerometer values
///
/// ... yes, the kinect has an accelerometer
/// raw axis values
ofPoint getRawAccel();
/// axis-based gravity adjusted accelerometer values
///
/// from libfreeenect:
///
/// as laid out via the accelerometer data sheet, which is available at
///
/// http://www.kionix.com/Product%20Sheets/KXSD9%20Product%20Brief.pdf
///
ofPoint getMksAccel();
/// get the current pitch (x axis) & roll (z axis) of the kinect in degrees
///
/// useful to correct the 3d scene based on the camera inclination
///
float getAccelPitch();
float getAccelRoll();

Reading data from an HID device from userspace in OSX [duplicate]

I am attempting to communicate with a rather specific USB device and developing both Windows and Mac code to do so.
The device is a USB device with a HID interface (class 3) with two endpoints, an interrupt input and an interrupt output. The nature of the device is such that data is sent out from the device on the input endpoint only when data is requested from the host: the host sends it data which the device responds to on its input interrupt endpoint. Getting data to the device (a write) is much more simple...
The code for Windows is rather straight-forward: I get a handle to the device and then call either ReadFile or WriteFile. Apparently much of the underlying asynchronous behavior is abstracted out. It appears to work fine.
On Mac, however, it is a bit stickier. I have tried a number of things, none which have been fully successful, but here are the two things which seemed most promising...
1.) Attempt to get access to the device (as USB) via IOUSBInterfaceInterface, iterate through the endpoints to determine the input and output endpoints, and (hopefully) use ReadPipe and WritePipe to communicate. Unfortunately I am unable to open the interface once I have it, with the return value (kIOReturnExclusiveAccess) noting that something already has the device open exclusively. I have tried using IOUSBinterfaceInterface183, so that I could call USBInterfaceOpenSeize, but that results in the same return error value.
--- update 7/30/2010 ---
Apparently, the Apple IOUSBHIDDriver matches early to the device and this is what likely is preventing opening the IOUSBInterfaceInterface. From some digging about it seems that the common way to prevent the IOUSBHIDDriver from matching is to write a code-less kext (kernel extension) with a higher probe score. This would match early, preventing the IOUSBHIDDriver from opening the device, and should, in theory, permit me to open the interface and to write and read to endpoints directly. This is OK, but I would much prefer not having to install something additional on the user machine. If anyone knows of a solid alternative I would be thankful for the information.
2.) Open the device as an IOHIDDeviceInterface122 (or later). To read, I set up an async port, event source and callback method to be called when data is ready - when data is sent from the device on the input interrupt endpoint. However, to write the data — that the device needs — to initialize a response I can't find a way. I'm stumped. setReport typically writes to the control endpoint, plus I need a write that does not expect any direct response, no blocking.
I have looked around online and have tried many things, but none of them is giving me success. Any advice? I can not use much of the Apple HIDManager code since much of that is 10.5+ and my application must work on 10.4 as well.
I have now a working Mac driver to a USB device that requires communication through interrupt endpoints. Here is how I did it:
Ultimately the method that worked well for me was option 1 (noted above). As noted, I was having issues opening the COM-style IOUSBInterfaceInterface to the device. It became clear over time that this was due to the HIDManager capturing the device. I was unable to wrest control of the device from the HIDManager once it was captured (not even the USBInterfaceOpenSeize call or the USBDeviceOpenSeize calls would work).
To take control of the device I needed to grab it before the HIDManager. The solution to this was to write a codeless kext (kernel extension). A kext is essentially a bundle that sits in System/Library/Extensions that contains (usually) a plist (property list) and (occasionally) a kernel-level driver, among other items. In my case I wanted only the plist, which would give the instructions to the kernel on what devices it matches. If the data gives a higher probe score than the HIDManager then I could essentially capture the device and use a user-space driver to communicate with it.
The kext plist written, with some project-specific details modified, is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>OSBundleLibraries</key>
<dict>
<key>com.apple.iokit.IOUSBFamily</key>
<string>1.8</string>
<key>com.apple.kernel.libkern</key>
<string>6.0</string>
</dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleGetInfoString</key>
<string>Demi USB Device</string>
<key>CFBundleIdentifier</key>
<string>com.demiart.mydevice</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Demi USB Device</string>
<key>CFBundlePackageType</key>
<string>KEXT</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
<key>IOKitPersonalities</key>
<dict>
<key>Device Driver</key>
<dict>
<key>CFBundleIdentifier</key>
<string>com.apple.kernel.iokit</string>
<key>IOClass</key>
<string>IOService</string>
<key>IOProviderClass</key>
<string>IOUSBInterface</string>
<key>idProduct</key>
<integer>12345</integer>
<key>idVendor</key>
<integer>67890</integer>
<key>bConfigurationValue</key>
<integer>1</integer>
<key>bInterfaceNumber</key>
<integer>0</integer>
</dict>
</dict>
<key>OSBundleRequired</key>
<string>Local-Root</string>
</dict>
</plist>
The idVendor and idProduct values give the kext specificity and increase its probe score sufficiently.
In order to use the kext, the following things need to be done (which my installer will do for clients):
Change the owner to root:wheel (sudo chown root:wheel DemiUSBDevice.kext)
Copy the kext to Extensions (sudo cp DemiUSBDevice.kext /System/Library/Extensions)
Call the kextload utility to load the kext for immediate use without restart (sudo kextload -vt /System/Library/Extensions/DemiUSBDevice.kext)
Touch the Extensions folder so that the next restart will force a cache rebuild (sudo touch /System/Library/Extensions)
At this point the system should use the kext to keep the HIDManager from capturing my device. Now, what to do with it? How to write to and read from it?
Following are some simplified snippets of my code, minus any error handling, that illustrate the solution. Before being able to do anything with the device, the application needs to know when the device attaches (and detaches). Note that this is merely for purposes of illustration — some of the variables are class-level, some are global, etc. Here is the initialization code that sets the attach/detach events up:
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>
#include <mach/mach.h>
#define DEMI_VENDOR_ID 12345
#define DEMI_PRODUCT_ID 67890
void DemiUSBDriver::initialize(void)
{
IOReturn result;
Int32 vendor_id = DEMI_VENDOR_ID;
Int32 product_id = DEMI_PRODUCT_ID;
mach_port_t master_port;
CFMutableDictionaryRef matching_dict;
IONotificationPortRef notify_port;
CFRunLoopSourceRef run_loop_source;
//create a master port
result = IOMasterPort(bootstrap_port, &master_port);
//set up a matching dictionary for the device
matching_dict = IOServiceMatching(kIOUSBDeviceClassName);
//add matching parameters
CFDictionarySetValue(matching_dict, CFSTR(kUSBVendorID),
CFNumberCreate(kCFAllocatorDefault, kCFNumberInt32Type, &vendor_id));
CFDictionarySetValue(matching_dict, CFSTR(kUSBProductID),
CFNumberCreate(kCFAllocatorDefault, kCFNumberInt32Type, &product_id));
//create the notification port and event source
notify_port = IONotificationPortCreate(master_port);
run_loop_source = IONotificationPortGetRunLoopSource(notify_port);
CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source,
kCFRunLoopDefaultMode);
//add an additional reference for a secondary event
// - each consumes a reference...
matching_dict = (CFMutableDictionaryRef)CFRetain(matching_dict);
//add a notification callback for detach event
//NOTE: removed_iter is a io_iterator_t, declared elsewhere
result = IOServiceAddMatchingNotification(notify_port,
kIOTerminatedNotification, matching_dict, device_detach_callback,
NULL, &removed_iter);
//call the callback to 'arm' the notification
device_detach_callback(NULL, removed_iter);
//add a notification callback for attach event
//NOTE: added_iter is a io_iterator_t, declared elsewhere
result = IOServiceAddMatchingNotification(notify_port,
kIOFirstMatchNotification, matching_dict, device_attach_callback,
NULL, &g_added_iter);
if (result)
{
throw Exception("Unable to add attach notification callback.");
}
//call the callback to 'arm' the notification
device_attach_callback(NULL, added_iter);
//'pump' the run loop to handle any previously added devices
service();
}
There are two methods that are used as callbacks in this initialization code: device_detach_callback and device_attach_callback (both declared at static methods). device_detach_callback is straightforward:
//implementation
void DemiUSBDevice::device_detach_callback(void* context, io_iterator_t iterator)
{
IOReturn result;
io_service_t obj;
while ((obj = IOIteratorNext(iterator)))
{
//close all open resources associated with this service/device...
//release the service
result = IOObjectRelease(obj);
}
}
device_attach_callback is where most of the magic happens. In my code I have this broken into multiple methods, but here I'll present it as a big monolithic method...:
void DemiUSBDevice::device_attach_callback(void * context,
io_iterator_t iterator)
{
IOReturn result;
io_service_t usb_service;
IOCFPlugInInterface** plugin;
HRESULT hres;
SInt32 score;
UInt16 vendor;
UInt16 product;
IOUSBFindInterfaceRequest request;
io_iterator_t intf_iterator;
io_service_t usb_interface;
UInt8 interface_endpoint_count = 0;
UInt8 pipe_ref = 0xff;
UInt8 direction;
UInt8 number;
UInt8 transfer_type;
UInt16 max_packet_size;
UInt8 interval;
CFRunLoopSourceRef m_event_source;
CFRunLoopSourceRef compl_event_source;
IOUSBDeviceInterface245** dev = NULL;
IOUSBInterfaceInterface245** intf = NULL;
while ((usb_service = IOIteratorNext(iterator)))
{
//create the intermediate plugin
result = IOCreatePlugInInterfaceForService(usb_service,
kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin,
&score);
//get the device interface
hres = (*plugin)->QueryInterface(plugin,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245), (void**)&dev);
//release the plugin - no further need for it
IODestroyPlugInInterface(plugin);
//double check ids for correctness
result = (*dev)->GetDeviceVendor(dev, &vendor);
result = (*dev)->GetDeviceProduct(dev, &product);
if ((vendor != DEMI_VENDOR_ID) || (product != DEMI_PRODUCT_ID))
{
continue;
}
//set up interface find request
request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
result = (*dev)->CreateInterfaceIterator(dev, &request, &intf_iterator);
while ((usb_interface = IOIteratorNext(intf_iterator)))
{
//create intermediate plugin
result = IOCreatePlugInInterfaceForService(usb_interface,
kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin,
&score);
//release the usb interface - not needed
result = IOObjectRelease(usb_interface);
//get the general interface interface
hres = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(
kIOUSBInterfaceInterfaceID245), (void**)&intf);
//release the plugin interface
IODestroyPlugInInterface(plugin);
//attempt to open the interface
result = (*intf)->USBInterfaceOpen(intf);
//check that the interrupt endpoints are available on this interface
//calling 0xff invalid...
m_input_pipe = 0xff; //UInt8, pipe from device to Mac
m_output_pipe = 0xff; //UInt8, pipe from Mac to device
result = (*intf)->GetNumEndpoints(intf, &interface_endpoint_count);
if (!result)
{
//check endpoints for direction, type, etc.
//note that pipe_ref == 0 is the control endpoint (we don't want it)
for (pipe_ref = 1; pipe_ref <= interface_endpoint_count; pipe_ref++)
{
result = (*intf)->GetPipeProperties(intf, pipe_ref, &direction,
&number, &transfer_type, &max_packet_size, &interval);
if (result)
{
break;
}
if (transfer_type == kUSBInterrupt)
{
if (direction == kUSBIn)
{
m_input_pipe = pipe_ref;
}
else if (direction == kUSBOut)
{
m_output_pipe = pipe_ref;
}
}
}
}
//set up async completion notifications
result = (*m_intf)->CreateInterfaceAsyncEventSource(m_intf,
&compl_event_source);
CFRunLoopAddSource(CFRunLoopGetCurrent(), compl_event_source,
kCFRunLoopDefaultMode);
break;
}
break;
}
}
At this point we should have the numbers of the interrupt endpoints and an open IOUSBInterfaceInterface to the device. An asynchronous writing of data can be done by calling something like:
result = (intf)->WritePipeAsync(intf, m_output_pipe,
data, OUTPUT_DATA_BUF_SZ, device_write_completion,
NULL);
where data is a char buffer of data to write, the final parameter is an optional context object to pass into the callback, and device_write_completion is a static method with the following general form:
void DemiUSBDevice::device_write_completion(void* context,
IOReturn result, void* arg0)
{
//...
}
reading from the interrupt endpoint is similar:
result = (intf)->ReadPipeAsync(intf, m_input_pipe,
data, INPUT_DATA_BUF_SZ, device_read_completion,
NULL);
where device_read_completion is of the following form:
void DemiUSBDevice::device_read_completion(void* context,
IOReturn result, void* arg0)
{
//...
}
Note that to receive these callbacks the run loop must be running (see this link for more information about the CFRunLoop). One way to achieve this is to call CFRunLoopRun() after calling the async read or write methods at which point the main thread blocks while the run loop runs. After handling your callback you can call CFRunLoopStop(CFRunLoopGetCurrent()) to stop the run loop and hand execution back to the main thread.
Another alternative (which I do in my code) is to pass a context object (named 'request' in the following code sample) into the WritePipeAsync/ReadPipeAsync methods - this object contains a boolean completion flag (named 'is_done' in this example). After calling the read/write method, instead of calling CFRunLoopRun(), something like the following can be executed:
while (!(request->is_done))
{
//run for 1/10 second to handle events
Boolean returnAfterSourceHandled = false;
CFTimeInterval seconds = 0.1;
CFStringRef mode = kCFRunLoopDefaultMode;
CFRunLoopRunInMode(mode, seconds, returnAfterSourceHandled);
}
This has the benefit that if you have other threads that use the run loop you won't prematurely exit should another thread stop the run loop...
I hope that this is helpful to people. I had to pull from many incomplete sources to solve this problem and this required considerable work to get running well...
After reading this question a few times and thinking about it for a bit, I thought of another solution for emulating blocking read behavior, but using the HID manager instead of replacing it.
A blocking read function can register an input callback for the device, register the device on the current run loop, and then block by calling CFRunLoopRun(). The input callback can then copy the report into a shared buffer and call CFRunLoopStop(), which causes CFRunLoopRun() to return, thereby unblocking read(). Then, read() can return the report to the caller.
The first issue I can think of is the case where the device is already scheduled on a run loop. Scheduling and then unscheduling the device in the read function may have adverse affects. But that would only be a problem if the application is trying to use both synchronous and asynchronous calls on the same device.
The second thing that comes to mind is the case where the calling code already has a run loop running (Cocoa and Qt apps for example). But, the documentation for CFRunLoopStop() seems to indicate that nested calls to CFRunLoopRun() are handled properly. So, it should be ok.
Here's a bit of simplified code to go with that. I just implemented something similar in my HID Library and it seems to work, although I haven't tested it extensively.
/* An IN report callback that stops its run loop when called.
This is purely for emulating blocking behavior in the read() method */
static void input_oneshot(void* context,
IOReturn result,
void* deviceRef,
IOHIDReportType type,
uint32_t reportID,
uint8_t* report,
CFIndex length)
{
buffer_type *const buffer = static_cast<HID::buffer_type*>(context);
/* If the report is valid, copy it into the caller's buffer
The Report ID is prepended to the buffer so the caller can identify
the report */
if( buffer )
{
buffer->clear(); // Return an empty buffer on error
if( !result && report && deviceRef )
{
buffer->reserve(length+1);
buffer->push_back(reportID);
buffer->insert(buffer->end(), report, report+length);
}
}
CFRunLoopStop(CFRunLoopGetCurrent());
}
// Block while waiting for an IN interrupt report
bool read(buffer_type& buffer)
{
uint8_t _bufferInput[_lengthInputBuffer];
// Register a callback
IOHIDDeviceRegisterInputReportCallback(deviceRef, _bufferInput, _lengthInputBuffer, input_oneshot, &buffer);
// Schedule the device on the current run loop
IOHIDDeviceScheduleWithRunLoop(deviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
// Trap in the run loop until a report is received
CFRunLoopRun();
// The run loop has returned, so unschedule the device
IOHIDDeviceUnscheduleFromRunLoop(deviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
if( buffer.size() )
return true;
return false;
}
I ran into this same kIOReturnExclusiveAccess. Instead of fighting it (building a kext, etc). I found the device and used the POSIX api's.
//My funcation was named differently, but I'm using this for continuity..
void DemiUSBDevice::device_attach_callback(void * context,
io_iterator_t iterator)
{
DeviceManager *deviceManager = (__bridge DADeviceManager *)context;
io_registry_entry_t device;
while ((device = IOIteratorNext(iterator))) {
CFTypeRef prop;
prop = IORegistryEntrySearchCFProperty(device,
kIOServicePlane,
CFSTR(kIODialinDeviceKey),
kCFAllocatorDefault,
kIORegistryIterateRecursively);
if(prop){
deviceManager->devPath = (__bridge NSString *)prop;
[deviceManager performSelector:#selector(openDevice)];
}
}
}
once devPath is set you can call open and read/write..
int dfd;
dfd = open([devPath UTF8String], O_RDWR | O_NOCTTY | O_NDELAY);
if (dfd == -1) {
//Could not open the port.
NSLog(#"open_port: Unable to open %#", devPath);
return;
} else {
fcntl(fd, F_SETFL, 0);
}

Problem handling signals in SystemC simulation application

I am simulating a CPU and I'm doing this using high level simulation tools. SystemC is a good resource for these purposes. I'm using two modules:
DataPath
Memory
CPU datapath is modeled as a unique high level entity, however the following code will sure be better than any other explaination:
The following is datapath.hpp
SC_MODULE(DataPath) {
sc_in_clk clk;
sc_in<bool> rst;
///
/// Outgoing data from memory.
///
sc_in<w32> mem_data;
///
/// Memory read enable control signal.
///
sc_out<sc_logic> mem_ctr_memreadenable;
///
/// Memory write enable control signal.
///
sc_out<sc_logic> mem_ctr_memwriteenable;
///
/// Data to be written in memory.
///
sc_out<w32> mem_dataw; //w32 is sc_lv<32>
///
/// Address in mem to read and write.
///
sc_out<memaddr> mem_addr;
///
/// Program counter.
///
sc_signal<w32> pc;
///
/// State signal.
///
sc_signal<int> cu_state;
///
/// Other internal signals mapping registers' value.
/// ...
// Defining process functions
///
/// Clock driven process to change state.
///
void state_process();
///
/// State driven process to apply control signals.
///
void control_process();
// Constructors
SC_CTOR(DataPath) {
// Defining first process
SC_CTHREAD(state_process, clk.neg());
reset_signal_is(this->rst, true);
// Defining second process
SC_METHOD(control_process);
sensitive << (this->cu_state) << (this->rst);
}
// Defining general functions
void reset_signals();
};
The following is datapath.cpp
void DataPath::state_process() {
// Useful variables
w32 ir_value; /* Placing here IR register value */
// Initialization phase
this->cu_state.write(StateFetch); /* StateFetch is a constant */
wait(); /* Wait next clock fall edge */
// Cycling
for (;;) {
// Checking state
switch (this->cu_state.read()) { // Basing on state, let's change the next one
case StateFetch: /* FETCH */
this->cu_state.write(StateDecode); /* Transition to DECODE */
break;
case StateDecode: /* DECODE */
// Doing decode
break;
case StateExecR: /* EXEC R */
// For every state, manage transition to the next state
break;
//...
//...
default: /* Possible not recognized state */
this->cu_state.write(StateFetch); /* Come back to fetch */
} /* switch */
// After doing, wait for the next clock fall edge
wait();
} /* for */
} /* function */
// State driven process for managing signal assignment
// This is a method process
void DataPath::control_process() {
// If reset signal is up then CU must be resetted
if (this->rst.read()) {
// Reset
this->reset_signals(); /* Initializing signals */
} else {
// No Reset
// Switching on state
switch (this->cu_state.read()) {
case StateFetch: /* FETCH */
// Managing memory address and instruction fetch to place in IR
this->mem_ctr_memreadenable.write(logic_sgm_1); /* Enabling memory to be read */
this->mem_ctr_memwriteenable.write(logic_sgm_0); /* Disabling memory from being written */
std::cout << "Entering fetch, memread=" << this->mem_ctr_memreadenable.read() << " memwrite=" << this->mem_ctr_memreadenable.read() << std::endl;
// Here I read from memory and get the instruction with some code that you do not need to worry about because my problem occurs HERE ###
break;
case kCUStateDecode: /* DECODE */
// ...
break;
//...
//...
default: /* Unrecognized */
newpc = "00000000000000000000000000000000";
} /* state switch */
} /* rst if */
} /* function */
// Resetting signals
void DataPath::reset_signals() {
// Out signals
this->mem_ctr_memreadenable.write(logic_sgm_1);
this->mem_ctr_memwriteenable.write(logic_sgm_0);
}
As you can see we have a clock driven process that handles cpu transitions (changing state) and a state driven process that sets signals for cpu.
My problem is that when I arrive in ### I expect the instruction being released by memory (you cannot see the instructions but they are correct, the memory component is connected to datapath using in and out singals you can see in the hpp file).
Memory gets me "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" because mem_ctr_memreadenable and mem_ctr_memwriteenable are both set to '0'.
Memory module is written in order to be an instant component. It is written using a SC_METHOD whose sensitive is defined on input signals (read enable and write enable included). The memory component gets "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" when the mem_ctr_memreadenable signal is '0'.
Why is it '0'? I reset signals and set that signal to '1'. I do not understand why I keep having '0' for the read enable signal.
Can you help me?
Thankyou.
I'm no SystemC guru, but it looks like it might be a similar problem to a common VHDL problem of signals not updating until at least a delta-cycle has passed:
this->mem_ctr_memreadenable.write(logic_sgm_1); /* Enabling memory to be read */
this->mem_ctr_memwriteenable.write(logic_sgm_0); /* Disabling memory from being written */
My guess: No time passes between these two lines and this next line:
std::cout << "Entering fetch, memread=" << this->mem_ctr_memreadenable.read() << " memwrite=" << this->mem_ctr_memreadenable.read() << std::endl;
So the memory hasn't yet seen the read signal change. BTW, should one of the read() calls attached to mem_ctr_memwriteenable - the both seem to be on readenable?
If you:
wait(1, SC_NS);
between those two points, does it improve matters?
To get a zero time synchronization with the memory module you should use
wait(SC_ZERO_TIME); //wait one delta cycle
not to introduce an arbitrary consumtion of time in your timed simulation.
This also impose you upgrade your control_process to an SC_THREAD