l3gd20 gyroscope sending back repeated bytes on stm32f3discovery - embedded

I am working on the stm32f3discovery and am trying to read data from the l3gd20 gyro on the board. When I try to read 6 bytes continuously from OUT_X_L to OUT_Z_H (which is all the data that is used to read readings from the gyroscope) the first three bytes sent are the same as the last three sent.
And when I try to read each register individually all the bytes are the same, I have checked on an oscilloscope and the registers are changing on the MOSI line but the response is 6 repeated bytes.
Both the WHO_AM_I and the OUT_TEMP registers work perfectly fine so I know that the actual SPI isn't the issue.
Here is any appropriate code, any help or point in the right direction will be much appreciated, thanks.
Main.rs
#![no_std]
#![no_main]
extern crate panic_itm; // logs messages over ITM; requires ITM support
use cortex_m::{asm, iprintln, iprint, Peripherals as core_peripherals};
use cortex_m_rt::entry;
use cortex_m_semihosting::{debug, hprintln};
use stm32f3::stm32f303;
mod my_api;
mod sensor;
#[entry]
fn main() -> ! {
let periph = stm32f303::Peripherals::take().unwrap();
let mut core_p = core_peripherals::take().unwrap();
let itm = &mut core_p.ITM.stim[0];
let gpioa = periph.GPIOA;
let gpioe = periph.GPIOE;
let rcc = periph.RCC
let flash = periph.FLASH;
let spi1 = periph.SPI1;
set_to_72MHz(&rcc, &flash);
let spi = my_api::spi_mod::spi_func::new(&rcc, &gpioa, &gpioe, &spi1);
let mut gyro = sensor::Gyro::gyro::new(&spi, &spi1).init(&spi, &spi1, &gpioe);
loop {
iprintln!(itm, "{:?}", gyro.read_data(&spi, &spi1, &gpioe));
}
}
fn set_to_72MHz(rcc: &stm32f3::stm32f303::RCC, flash: &stm32f3::stm32f303::FLASH){
rcc.cr.modify(|_,w| w.hseon().set_bit()); //turns on the external oscillator
while rcc.cr.read().hserdy().bit_is_clear(){} //wait for it to be ready
flash.acr.modify(|_,w| {
w.prftbe().set_bit(); //turn on prefetch
unsafe{w.latency().bits(0b010)} //set latency for 72MHz
});
rcc.cfgr.modify(|_,w| {
w.pllxtpre().clear_bit();
unsafe{w.pllsrc().bits(0b10)};
w.pllmul().bits(0b0111);
unsafe{w.hpre().bits(0b0000);
w.ppre1().bits(0b100);
w.ppre2().bits(0b000)}
});
rcc.cr.modify(|_,w| w.pllon().set_bit());
while rcc.cr.read().pllrdy().bit_is_clear(){}
rcc.cfgr.modify(|_,w| unsafe{w.sw().bits(0b10)});
while rcc.cfgr.read().sws().bits() != 0b10{}
}
my_api.rs
pub mod spi_mod{
pub struct spi_func{}
impl spi_func{
pub fn new(rcc: &stm32f3::stm32f303::RCC, gpioa: &stm32f3::stm32f303::GPIOA, gpioe: &stm32f3::stm32f303::GPIOE, spi: &stm32f3::stm32f303::SPI1) -> Self {
rcc.apb2enr.modify(|_,w| w.spi1en().set_bit()); //enable spi clock
rcc.ahbenr.modify(|_,w| w.iopaen().set_bit()); // enable gpio clocks
rcc.ahbenr.modify(|_,w| w.iopeen().set_bit());
gpioe.moder.modify(|_,w| w.moder3().bits(0b01)); //CS pin
gpioe.otyper.modify(|_,w| w.ot3().clear_bit());
gpioe.odr.modify(|_,w| w.odr3().set_bit()); //bring pe3 high to disable gyro com
// PA5 -> SCL // PA6 -> MISO // PA7 -> MOSI
gpioa.moder.modify(|_,w| {
w.moder5().bits(0b10); // output type
w.moder6().bits(0b10);
w.moder7().bits(0b10)});
gpioa.otyper.modify(|_,w| {
w.ot5().clear_bit();// push pull type
w.ot6().clear_bit();
w.ot7().clear_bit()});
gpioa.ospeedr.modify(|_,w| {
w.ospeedr5().bits(0b11);// fast mode
w.ospeedr6().bits(0b11);
w.ospeedr7().bits(0b11)});
gpioa.afrl.modify(|_,w| {
w.afrl5().bits(0b0101);//Alternate function 5
w.afrl6().bits(0b0101);
w.afrl7().bits(0b0101)
});
spi.cr1.modify(|_,w| {
w.bidimode().clear_bit(); //bidirectional mode
w.ssm().set_bit(); // hardware slave management disabled
w.ssi().set_bit(); // slave not selected -------------------------------
w.br().bits(0b011); // bring rate from 72MHz to 72/16MHz = 4.5MHz as gyro cant go above 10MHz
w.mstr().set_bit(); // set to master mode
w.cpol().set_bit(); // set to mode 3 to work for the l3gd20
w.cpha().set_bit()
});
spi.cr2.modify(|_,w| {
//unsafe{w.ds().bits(0b0111)}; // data size = 8 bits
w.errie().clear_bit();
w.txeie().set_bit(); //enable interupt flags
w.rxneie().set_bit();
w.ssoe().clear_bit() //set to single master mode
});
spi.cr1.modify(|_,w| w.spe().set_bit()); // enable spi
spi_func{}
}
fn check_faults(&self, spi1: &stm32f3::stm32f303::SPI1) -> bool {
if spi1.sr.read().ovr().bit_is_set(){ //checks through all the flags
false
}else if spi1.sr.read().modf().bit_is_set(){
false
}else if spi1.sr.read().crcerr().bit_is_set(){
false
}else if spi1.sr.read().udr().bit_is_set(){
false
} else {
true
}
}
pub fn write(&self, spi1: &stm32f3::stm32f303::SPI1, data: u16) {
if self.check_faults(&spi1){
spi1.dr.modify(|_,w| w.dr().bits(data)); //write to the dr register
while spi1.sr.read().bsy().bit_is_set(){}
}
}
pub fn read(&self, spi1: &stm32f3::stm32f303::SPI1) -> u8 {
if self.check_faults(&spi1){
while spi1.sr.read().bsy().bit_is_set(){}
return (spi1.dr.read().dr().bits() >> 8) as u8 //read from the dr register
}
0
}
pub fn write_data(&self, spi1: &stm32f3::stm32f303::SPI1, reg_address: u8, data: u8) {
let total_data: u16 = ((data as u16) << 8) + (reg_address as u16);
self.write(&spi1, total_data);
}
pub fn read_register(&self, spi1: &stm32f3::stm32f303::SPI1, reg_address: u8) -> u8{
self.write(&spi1, (reg_address | 0x80) as u16); //set the bit to read mode and send
self.read(&spi1) //read the result
}
pub fn read_multiple_register(&self, spi1: &stm32f3::stm32f303::SPI1, reg_address: u8, rx_data: &mut [u8]){
self.write(&spi1, (reg_address | 0x80 | 0x40) as u16);
for d in rx_data.iter_mut(){
self.write(&spi1, 0x00);
*d = self.read(&spi1);
}
}
}
}
senor.rs
pub mod Gyro{
pub use crate::my_api::spi_mod;
const WHO_AM_I: u8 = 0x0F;
const CTRL_REG1: u8 = 0x20;
const CTRL_REG4: u8 = 0x23;
const OUT_TEMP: u8 = 0x26;
const OUT_X_L: u8 = 0x28;
const OUT_X_H: u8 = 0x29;
const OUT_Y_L: u8 = 0x2A;
const OUT_Y_H: u8 = 0x2B;
const OUT_Z_L: u8 = 0x2C;
const OUT_Z_H: u8 = 0x2D;
pub struct gyro{
pub x: i16,
pub y: i16,
pub z: i16,
offset_x: i32,
offset_y: i32,
offset_z: i32,
pub temp: i8
}
impl gyro{
pub fn new(my_spi_mod: &spi_mod::spi_func, spi1: &stm32f3::stm32f303::SPI1) -> Self {
gyro{
x: 0,
y: 0,
z: 0,
offset_x: 0,
offset_y: 0,
offset_z: 0,
temp: 0,
}
}
pub fn init(self, spi: &spi_mod::spi_func, spi1: &stm32f3::stm32f303::SPI1, gpioe: &stm32f3::stm32f303::GPIOE) -> Self{
self.cs_low(&gpioe);
spi.write_data(&spi1, CTRL_REG1, 0x3F); //power on the gyro
self.cs_high(&gpioe);
self.cs_low(&gpioe);
spi.write_data(&spi1, CTRL_REG4, 0x10); //power on the gyro
self.cs_high(&gpioe);
self
}
pub fn who_am_i(&self, spi: &spi_mod::spi_func, spi1: &stm32f3::stm32f303::SPI1, gpioe: &stm32f3::stm32f303::GPIOE) -> u8 {
self.cs_low(&gpioe);
let data = spi.read_register(&spi1, WHO_AM_I);
self.cs_high(&gpioe);
data
}
pub fn read_temp(&mut self, spi: &spi_mod::spi_func, spi1: &stm32f3::stm32f303::SPI1, gpioe: &stm32f3::stm32f303::GPIOE) {
self.cs_low(&gpioe);
self.temp = spi.read_register(&spi1, OUT_TEMP) as i8;
self.cs_high(&gpioe);
}
pub fn read_data(&mut self, spi: &spi_mod::spi_func, spi1: &stm32f3::stm32f303::SPI1, gpioe: &stm32f3::stm32f303::GPIOE) -> [u8;6]{
let mut data: [u8;6] = [0,0,0,0,0,0];
self.cs_low(&gpioe);
spi.read_multiple_register(&spi1, OUT_X_L, &mut data);
// data[0] = spi.read_register(&spi1, OUT_X_L);
// data[1] = spi.read_register(&spi1, OUT_X_H);
// data[2] = spi.read_register(&spi1, OUT_Y_L);
// data[3] = spi.read_register(&spi1, OUT_Y_H);
// data[4] = spi.read_register(&spi1, OUT_Z_L);
// data[5] = spi.read_register(&spi1, OUT_Z_H);
self.cs_high(&gpioe);
self.x = ((data[0] as u16) + ((data[1] as u16) << 8)) as i16;
self.y = ((data[2] as u16) + ((data[3] as u16) << 8)) as i16;
self.z = ((data[4] as u16) + ((data[5] as u16) << 8)) as i16;
data
}
fn cs_low(&self, gpioe: &stm32f3::stm32f303::GPIOE){
gpioe.odr.modify(|_,w| w.odr3().clear_bit());
}
fn cs_high(&self, gpioe: &stm32f3::stm32f303::GPIOE){
gpioe.odr.modify(|_,w| w.odr3().set_bit());
}
}
}
This is the result when using
sensor.rs -> read_data() -> spi.read_multiple_register(&spi1, OUT_X_L, &mut data);
return data from board with repeated 3 bytes
And then this is what I get when when I use the commented out bit instead
return data when all bytes are the same

Have you tried using using the STATUS_REG at 0x27H? Section 3.2 Page 10

Related

elegant way of capturing a reference to an integer variable?

I have this snippet:
let mut animation_index = 0 as usize;
let mut ptr : *mut usize = &mut animation_index as _;
{
io_context.window().add_key_callback(
Box::new(move |key_states| {
if key_states[KbKey::Space.to_index()] == KeyActionState::Press
{
unsafe {
*ptr += 1;
println!("{}", animation_index);
}
}
})
);
}
Basically it adds a callback such that if and when I press space, the integer variable animation_index goes up by 1. This works, but requires the use of mutable pointers and unsafe, which is very ugly.
I'd like to have the same logic but ideally do it with pure safe rust isntead.
It looks like you are trying to share a mutable value across threads.
Typically, this is done with atomics, Arc<Mutex<T>> or Arc<RwLock<T>>.
use std::synce::{Arc, RwLock};
let mut animation_index = Arc::new(RwLock::new(0usize));
{
// a clone of the counter that can be moved into the callback
let animation_index = animation_index.clone();
io_context.window().add_key_callback(
Box::new(move |key_states| {
if key_states[KbKey::Space.to_index()] == KeyActionState::Press
{
let index = animation_index.write().unwrap();
*index += 1;
println!("{}", index);
}
})
);
}
With atomics it would look something like this:
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
let mut animation_index = Arc::new(AtomicUsize::new(0));
{
// a clone of the counter that can be moved into the callback
let animation_index = animation_index.clone();
io_context.window().add_key_callback(
Box::new(move |key_states| {
if key_states[KbKey::Space.to_index()] == KeyActionState::Press
{
let index = animation_index.fetch_add(1, Ordering::SeqCst);
println!("{}", index);
}
})
);
}

Playing WAV data with AVAudioEngine

Currently, I'm getting an EXC_BAD_ACCESS error on the audio thread, and I'm trying to deduce what is going wrong.
When converting .wav file data from Data to an AVAudioPCMBuffer, do I need to strip the RIFF header first?
import AVFoundation
public class Player : NSObject {
let engine = AVAudioEngine()
public override init() {
super.init()
do {
let _ = engine.mainMixerNode
try engine.start()
} catch {
print("Player error: \(error)")
}
}
#objc public func play(_ data: Data) {
let format = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 48000, channels: 2, interleaved: true)!
let buffer = data.toPCMBuffer(format: format)!
let player = AVAudioPlayerNode()
engine.attach(player)
engine.connect(player, to: engine.mainMixerNode, format: nil)
player.scheduleBuffer(buffer, at: nil, completionCallbackType: .dataPlayedBack) {
callbackType in
// Nothing in here.
}
player.play()
}
}
Here's the toPCMBuffer extension:
// Taken from: https://stackoverflow.com/a/52731480/2228559
extension Data {
func toPCMBuffer(format: AVAudioFormat) -> AVAudioPCMBuffer? {
let streamDesc = format.streamDescription.pointee
let frameCapacity = UInt32(count) / streamDesc.mBytesPerFrame
guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCapacity) else { return nil }
buffer.frameLength = buffer.frameCapacity
let audioBuffer = buffer.audioBufferList.pointee.mBuffers
withUnsafeBytes { addr in
audioBuffer.mData?.copyMemory(from: addr, byteCount: Int(audioBuffer.mDataByteSize))
}
return buffer
}
}
Note: I cannot use AVAudioFile because the .wav file data is loaded over-the-wire.
IDK, but my mac crashes if I play interleaved AVAudioPCMBuffers, and garbled audio if they're not float data, so you could convert to non-interleaved float data:
#objc public func play(_ data: Data) {
let sampleRate: Double = 48000
let interleavedFormat = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: sampleRate, channels: 2, interleaved: true)!
let interleavedBuffer = data.toPCMBuffer(format: interleavedFormat)!
let nonInterleavedFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: sampleRate, channels: 2, interleaved: false)!
let nonInterleavedBuffer = AVAudioPCMBuffer(pcmFormat: nonInterleavedFormat, frameCapacity: interleavedBuffer.frameCapacity)!
nonInterleavedBuffer.frameLength = interleavedBuffer.frameLength
let converter = AVAudioConverter(from: interleavedFormat, to: nonInterleavedFormat)!
try! converter.convert(to: nonInterleavedBuffer, from: interleavedBuffer)
let player = AVAudioPlayerNode()
engine.attach(player)
engine.connect(player, to: engine.mainMixerNode, format: nil)
player.scheduleBuffer(nonInterleavedBuffer, at: nil, completionCallbackType: .dataPlayedBack) {
callbackType in
// Nothing in here.
}
player.play()
}
extension Data {
func toPCMBuffer(format: AVAudioFormat) -> AVAudioPCMBuffer? {
assert(format.isInterleaved)
let streamDesc = format.streamDescription.pointee
let frameCapacity = UInt32(count) / streamDesc.mBytesPerFrame
guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCapacity) else { return nil }
buffer.frameLength = buffer.frameCapacity
let b = UnsafeMutableBufferPointer(start: buffer.int16ChannelData![0], count: buffer.stride * Int(frameCapacity))
let bytesCopied = self.copyBytes(to: b)
assert(bytesCopied == count)
return buffer
}
}

USB device send/receive data

I have implemented function to detect USB device. It works and now i need to send/read data.
I started look over a lot of obj-c sources and found only one good article in apple documentation, that describes how do we can send package to our USB device:
IOReturn WriteToDevice(IOUSBDeviceInterface **dev, UInt16 deviceAddress,
UInt16 length, UInt8 writeBuffer[])
{
IOUSBDevRequest request;
request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor,
kUSBDevice);
request.bRequest = 0xa0;
request.wValue = deviceAddress;
request.wIndex = 0;
request.wLength = length;
request.pData = writeBuffer;
return (*dev)->DeviceRequest(dev, &request);
}
But I didn't find a way how to create and send data with Swift. The struct on Swift looks like:
public struct IOUSBDevRequest {
public var bmRequestType: UInt8
public var bRequest: UInt8
public var wValue: UInt16
public var wIndex: UInt16
public var wLength: UInt16
public var pData: UnsafeMutableRawPointer!
public var wLenDone: UInt32
public init()
public init(bmRequestType: UInt8, bRequest: UInt8, wValue: UInt16, wIndex: UInt16, wLength: UInt16, pData: UnsafeMutableRawPointer!, wLenDone: UInt32)
}
I can't figure out what parameters is pData, zwLenDone.
This is data that i need to send:
{
'direction':'in',
'recipient':'device',
'requestType': 'standard',
'request': 6,
'value': 0x300,
'index': 0,
'length': 255
}
The next question is: How i can receive data. I know the answer is in this article, but i can't convert it to Swift.
Here is what i could converted on Swift 3. My class detects USB device, get his configuration:
class DFUDevice: NSObject {
let vendorId = 0x0483
let productId = 0xdf11
static let sharedInstance = DFUDevice()
var deviceName:String = ""
private func deviceAdded(iterator: io_iterator_t) {
var plugInInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>?>?
var deviceInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOUSBDeviceInterface>?>?
var configPtr:IOUSBConfigurationDescriptorPtr?
var score: Int32 = 0
while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 {
// io_name_t imports to swift as a tuple (Int8, ..., Int8) 128 ints
// although in device_types.h it's defined:
// typedef char io_name_t[128];
var deviceNameCString: [CChar] = [CChar](repeating: 0, count: 128)
let deviceNameResult = IORegistryEntryGetName(usbDevice, &deviceNameCString)
if(deviceNameResult != kIOReturnSuccess) {
print("Error getting device name")
}
self.deviceName = String.init(cString: &deviceNameCString)
print("usb Device Name: \(deviceName)")
// Get plugInInterface for current USB device
let plugInInterfaceResult = IOCreatePlugInInterfaceForService(
usbDevice,
kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID,
&plugInInterfacePtrPtr,
&score)
// dereference pointer for the plug in interface
guard plugInInterfaceResult == kIOReturnSuccess,
let plugInInterface = plugInInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Plug-In Interface")
continue
}
// use plug in interface to get a device interface
let deviceInterfaceResult = withUnsafeMutablePointer(to: &deviceInterfacePtrPtr) {
$0.withMemoryRebound(to: Optional<LPVOID>.self, capacity: 1) {
plugInInterface.QueryInterface(
plugInInterfacePtrPtr,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
$0)
}
}
// dereference pointer for the device interface
guard deviceInterfaceResult == kIOReturnSuccess,
let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Device Interface")
continue
}
var ret = deviceInterface.USBDeviceOpen(deviceInterfacePtrPtr)
if (ret == kIOReturnSuccess)
{
// set first configuration as active
ret = deviceInterface.GetConfigurationDescriptorPtr(deviceInterfacePtrPtr, 0, &configPtr)
if (ret != kIOReturnSuccess)
{
print("Could not set active configuration (error: %x)\n", ret);
continue
}
guard let config = configPtr?.pointee else {
continue
}
if config.bLength > 0 {
//HERE I NEED SEND DATA
} else {
print("ConfigurationDescriptor not valid")
}
print(config.bLength)
}
else if (ret == kIOReturnExclusiveAccess)
{
// this is not a problem as we can still do some things
}
else
{
print("Could not open device (error: %x)\n", ret)
continue
}
IOObjectRelease(usbDevice)
}
}
func initUsb() {
var matchedIterator:io_iterator_t = 0
var removalIterator:io_iterator_t = 0
let notifyPort:IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
IONotificationPortSetDispatchQueue(notifyPort, DispatchQueue(label: "IODetector"))
let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
as NSMutableDictionary
matchingDict[kUSBVendorID] = NSNumber(value: self.vendorId)
matchingDict[kUSBProductID] = NSNumber(value: self.productId)
let matchingCallback:IOServiceMatchingCallback = { (userData, iterator) in
let this = Unmanaged<DFUDevice>
.fromOpaque(userData!).takeUnretainedValue()
this.deviceAdded(iterator: iterator)
this.connected(iterator: iterator)
}
let removalCallback: IOServiceMatchingCallback = {
(userData, iterator) in
let this = Unmanaged<DFUDevice>
.fromOpaque(userData!).takeUnretainedValue()
this.disconnected(iterator: iterator)
}
let selfPtr = Unmanaged.passUnretained(self).toOpaque()
IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, matchingCallback, selfPtr, &matchedIterator)
IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, removalCallback, selfPtr, &removalIterator)
self.deviceAdded(iterator: matchedIterator)
self.deviceAdded(iterator: removalIterator)
RunLoop.current.run()
}
}
I call it like:
let DFUDeviceDaemon = Thread(target: DFUDevice.sharedInstance, selector:#selector(DFUDevice.initUsb), object: nil)
DFUDeviceDaemon.start()
The article you reference has a function called WriteToDevice. One of its parameters is
UInt8 writeBuffer[]
This writeBuffer, the data that you want to send, is a C array of bytes:
uint8_t msgLength = 3;
uint8_t writeBuffer[msgLength];
writeBuffer[0] = 0x41; // ASCII 'A'
writeBuffer[1] = 0x42; // ASCII 'B'
writeBuffer[2] = 0x43; // ASCII 'C'
What bytes do you need to send? That really depends on the device at the other end -- the technical data from the manufacturer should tell you that.
To pass the C-array as NSData, which is probably what pData is, you'd use:
NSData *data = [NSData dataWithBytes:&writeBuffer length:3];
The (z)wLenDone is probably what I called the msgLength, 3. C-array's have no knowledge of their own length, so most functions require the length as a separate parameter.
As for receiving data, I would guess that happens in the matchingCallback: you use the iterator to receive the bytes and then parse them.
ANSWER TO COMMENT:
I'm not familiar with C#, and I'm no expert at this stuff, but maybe this will help:
var package = new UsbSetupPacket(
(byte)(UsbCtrlFlags.Direction_In |
UsbCtrlFlags.Recipient_Device |
UsbCtrlFlags.RequestType_Standard), // Index
6, // length of data, second phase
0x200, // Request
0, // RequestType
(short)length); // Value
A few observations: know nothing of C#, but should not the package be typed to struct? RequestType is 0, so you will receive no response -- is that what you want? Or did you want to send UsbCtrlFlags.RequestType_Standard as the fourth parameter? Why send the length as a value?
Anyway, what you do now is send the package to the USB device and see what happens.
After a lot of questions on stackoverflow and learning sources i figure it out:
First define not implemented functions
import Foundation
import IOKit
import IOKit.usb
import IOKit.usb.IOUSBLib
//from IOUSBLib.h
let kIOUSBDeviceUserClientTypeID = CFUUIDGetConstantUUIDWithBytes(nil,
0x9d, 0xc7, 0xb7, 0x80, 0x9e, 0xc0, 0x11, 0xD4,
0xa5, 0x4f, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)
let kIOUSBDeviceInterfaceID = CFUUIDGetConstantUUIDWithBytes(nil,
0x5c, 0x81, 0x87, 0xd0, 0x9e, 0xf3, 0x11, 0xD4,
0x8b, 0x45, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)
//from IOCFPlugin.h
let kIOCFPlugInInterfaceID = CFUUIDGetConstantUUIDWithBytes(nil,
0xC2, 0x44, 0xE8, 0x58, 0x10, 0x9C, 0x11, 0xD4,
0x91, 0xD4, 0x00, 0x50, 0xE4, 0xC6, 0x42, 0x6F)
/*!
#defined USBmakebmRequestType
#discussion Macro to encode the bRequest field of a Device Request. It is used when constructing an IOUSBDevRequest.
*/
func USBmakebmRequestType(direction:Int, type:Int, recipient:Int) -> UInt8 {
return UInt8((direction & kUSBRqDirnMask) << kUSBRqDirnShift)|UInt8((type & kUSBRqTypeMask) << kUSBRqTypeShift)|UInt8(recipient & kUSBRqRecipientMask)
}
Then create our class:
extension Notification.Name {
static let dfuDeviceConnected = Notification.Name("DFUDeviceConnected")
static let dfuDeviceDisconnected = Notification.Name("DFUDeviceDisconnected")
}
class DFUDevice: NSObject {
let vendorId = 0x0483
let productId = 0xdf11
static let sharedInstance = DFUDevice()
var deviceInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOUSBDeviceInterface>?>?
var plugInInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>?>?
var interfacePtrPtr:UnsafeMutablePointer<UnsafeMutablePointer<IOUSBInterfaceInterface>?>?
private func rawDeviceAdded(iterator: io_iterator_t) {
var score:Int32 = 0
var kr:Int32 = 0
while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 {
// io_name_t imports to swift as a tuple (Int8, ..., Int8) 128 ints
// although in device_types.h it's defined:
// typedef char io_name_t[128];
var deviceNameCString: [CChar] = [CChar](repeating: 0, count: 128)
let deviceNameResult = IORegistryEntryGetName(usbDevice, &deviceNameCString)
if(deviceNameResult != kIOReturnSuccess) {
print("Error getting device name")
}
let deviceName = String.init(cString: &deviceNameCString)
print("usb Device Name: \(deviceName)")
// Get plugInInterface for current USB device
let plugInInterfaceResult = IOCreatePlugInInterfaceForService(
usbDevice,
kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID,
&plugInInterfacePtrPtr,
&score)
// USB device object is no longer needed.
IOObjectRelease(usbDevice)
// Dereference pointer for the plug-in interface
guard plugInInterfaceResult == kIOReturnSuccess,
let plugInInterface = plugInInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Plug-In Interface")
continue
}
// use plug in interface to get a device interface
let deviceInterfaceResult = withUnsafeMutablePointer(to: &deviceInterfacePtrPtr) {
$0.withMemoryRebound(to: Optional<LPVOID>.self, capacity: 1) {
plugInInterface.QueryInterface(
plugInInterfacePtrPtr,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
$0)
}
}
// dereference pointer for the device interface
guard deviceInterfaceResult == kIOReturnSuccess,
let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Device Interface")
continue
}
kr = deviceInterface.USBDeviceOpen(deviceInterfacePtrPtr)
if (kr != kIOReturnSuccess)
{
print("Could not open device (error: \(kr))")
continue
}
else if (kr == kIOReturnExclusiveAccess)
{
// this is not a problem as we can still do some things
continue
}
self.connected()
}
}
private func rawDeviceRemoved(iterator: io_iterator_t) {
var kr:Int32 = 0
while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 {
// USB device object is no longer needed.
kr = IOObjectRelease(usbDevice)
if (kr != kIOReturnSuccess)
{
print("Couldn’t release raw device object (error: \(kr))")
continue
}
self.disconnected()
}
}
func getStatus() throws -> [UInt8] {
guard let deviceInterface = self.deviceInterfacePtrPtr?.pointee?.pointee else {
throw DFUDeviceError.DeviceInterfaceNotFound
}
var kr:Int32 = 0
let length:Int = 6
var requestPtr:[UInt8] = [UInt8](repeating: 0, count: length)
var request = IOUSBDevRequest(bmRequestType: USBmakebmRequestType(direction: kUSBIn, type: kUSBDevice, recipient: kUSBStandard),
bRequest: DFUREQUEST.GETSTATUS.rawValue,
wValue: 0,
wIndex: 0,
wLength: UInt16(length),
pData: &requestPtr,
wLenDone: 255)
kr = deviceInterface.DeviceRequest(self.deviceInterfacePtrPtr, &request)
if (kr != kIOReturnSuccess) {
throw DFUDeviceError.RequestError(desc: "Get device status request error: \(kr)")
}
return requestPtr
}
private func configureDevice() -> Int32 {
var kr:Int32 = 0
guard let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Device Interface")
return -1
}
var numConfig:UInt8 = 0
kr = deviceInterface.GetNumberOfConfigurations(deviceInterfacePtrPtr, &numConfig)
if numConfig == 0 {
print("Device Number Of Configurations: 0")
return -1
}
var configPtr:IOUSBConfigurationDescriptorPtr?
// set first configuration as active
kr = deviceInterface.GetConfigurationDescriptorPtr(deviceInterfacePtrPtr, 0, &configPtr)
if (kr != kIOReturnSuccess)
{
print("Couldn’t get configuration descriptor for index (error: %x)\n", kr);
return -1
}
guard let config = configPtr?.pointee else {
return -1
}
//Set the device’s configuration. The configuration value is found in
//the bConfigurationValue field of the configuration descriptor
kr = deviceInterface.SetConfiguration(deviceInterfacePtrPtr, config.bConfigurationValue)
if (kr != kIOReturnSuccess)
{
print("Couldn’t set configuration to value (error: %x)\n", kr);
return -1
}
return kIOReturnSuccess
}
func connected() {
NotificationCenter.default.post(name: .dfuDeviceConnected, object: nil)
globalLogPost("DFU device has been device connected")
}
func disconnected() {
NotificationCenter.default.post(name: .dfuDeviceDisconnected, object: nil)
globalLogPost("DFU device has been disconnected")
}
func initUsb() {
var matchedIterator:io_iterator_t = 0
var removalIterator:io_iterator_t = 0
let notifyPort:IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
IONotificationPortSetDispatchQueue(notifyPort, DispatchQueue(label: "IODetector"))
let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
as NSMutableDictionary
matchingDict[kUSBVendorID] = NSNumber(value: self.vendorId)
matchingDict[kUSBProductID] = NSNumber(value: self.productId)
let matchingCallback:IOServiceMatchingCallback = { (userData, iterator) in
let this = Unmanaged<DFUDevice>
.fromOpaque(userData!).takeUnretainedValue()
this.rawDeviceAdded(iterator: iterator)
}
let removalCallback: IOServiceMatchingCallback = {
(userData, iterator) in
let this = Unmanaged<DFUDevice>
.fromOpaque(userData!).takeUnretainedValue()
this.rawDeviceRemoved(iterator: iterator)
}
let selfPtr = Unmanaged.passUnretained(self).toOpaque()
IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, matchingCallback, selfPtr, &matchedIterator)
IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, removalCallback, selfPtr, &removalIterator)
self.rawDeviceAdded(iterator: matchedIterator)
self.rawDeviceRemoved(iterator: removalIterator)
RunLoop.current.run()
}
}
You can look on method getStatus where i create a USBRequest and send it to device. Then in requestPtr:[UInt8] i received answer from device. Thank you for helping guys.
We can use ore device pointer anywhere in project, for example:
func upload(value:UInt16, length:UInt16) throws -> [UInt8] {
guard let deviceInterface = DFUDevice.sharedInstance.deviceInterfacePtrPtr?.pointee?.pointee else {
throw DFUDeviceError.DeviceInterfaceNotFound
}
var kr:Int32 = 0
var requestPtr:[UInt8] = [UInt8](repeating: 0, count: Int(length))
var request = IOUSBDevRequest(bmRequestType: 161,
bRequest: DFUREQUEST.UPLOAD.rawValue,
wValue: value,
wIndex: 0,
wLength: length,
pData: &requestPtr,
wLenDone: 255)
kr = deviceInterface.DeviceRequest(DFUDevice.sharedInstance.deviceInterfacePtrPtr, &request)
if (kr != kIOReturnSuccess) {
throw DFUDeviceError.RequestError(desc: "Upload request error: \(kr), request data: \(request)")
}
return requestPtr
}

How to read a C struct from a binary file in Rust? [duplicate]

This question already has answers here:
How to read a struct from a file in Rust?
(3 answers)
Closed 6 years ago.
I have read How to read a struct from file in Rust?, but std::slice::raw::mut_buf_as_slice has been deprecated, hence I want to re-ask this question.
I want to read a struct utmp from /var/run/utmp, and I have tried the following code:
fn read_utmp() -> Utmp {
let mut reader = BufReader::new(File::open("/var/run/utmp").unwrap());
let mut ut = vec![];
let size = mem::size_of::<Utmp>();
reader.take(size as u64).read_to_end(&mut ut);
unsafe {
std::mem::transmute(ut)
}
}
And as expected, the compiler complaint:
error: transmute called with differently sized types: std::vec::Vec (192 bits) to Utmp (3056 bits) [E0512]
How could I do this?
I believe that raw::mut_buf_as_slice was replaced with slice::from_raw_parts_mut.
Note that the following code does not take into account any endianness or padding issues and is intended to be used with POD types. struct utmp should be safe in this case.
Here is a function that can read a struct (of a pod type) from a file:
use std::io::{self, BufReader, Read};
use std::fs::{self, File};
use std::path::Path;
use std::slice;
fn read_struct<T, R: Read>(mut read: R) -> io::Result<T> {
let num_bytes = ::std::mem::size_of::<T>();
unsafe {
let mut s = ::std::mem::uninitialized();
let mut buffer = slice::from_raw_parts_mut(&mut s as *mut T as *mut u8, num_bytes);
match read.read_exact(buffer) {
Ok(()) => Ok(s),
Err(e) => {
::std::mem::forget(s);
Err(e)
}
}
}
}
// use
// read_struct::<Utmp>(reader)
If you want to read all utmp structs from the utmp file, you can execute read_struct multiple times or read all the file at once:
fn read_structs<T, P: AsRef<Path>>(path: P) -> io::Result<Vec<T>> {
let path = path.as_ref();
let struct_size = ::std::mem::size_of::<T>();
let num_bytes = try!(fs::metadata(path)).len() as usize;
let num_structs = num_bytes / struct_size;
let mut reader = BufReader::new(try!(File::open(path)));
let mut r = Vec::<T>::with_capacity(num_structs);
unsafe {
let mut buffer = slice::from_raw_parts_mut(r.as_mut_ptr() as *mut u8, num_bytes);
try!(reader.read_exact(buffer));
r.set_len(num_structs);
}
Ok(r)
}
// use
// read_structs::<Utmp, _>("/var/run/utmp"))

is it possible to filter on a vector in-place?

I'd like to remove some elements from a Vec, but vec.iter().filter().collect() creates a new vector with borrowed items.
I'd like to mutate the original Vec without extra memory allocation (and keep memory of removed elements as an extra capacity of the vector).
If you want to remove elements, you can use retain(), which removes elements from the vector if the closure returns false:
let mut vec = vec![1, 2, 3, 4];
vec.retain(|&x| x % 2 == 0);
assert_eq!(vec, [2, 4]);
If you want to modify the elements in place, you have to do that in a for x in vec.iter_mut().
If you truly want to mutate the vector's elements while filtering it, you can use the nightly-only method Vec::drain_filter, an extremely flexible tool:
#![feature(drain_filter)]
fn main() {
let mut vec = vec![1, 2, 3, 4];
vec.drain_filter(|x| {
if *x % 2 == 0 {
true
} else {
*x += 100;
false
}
});
assert_eq!(vec, [101, 103]);
}
It also allows you to get the removed elements as the return value of the entire method is an iterator!
Till Vec::drain_filter gets stable, we can solve the problem with homebrewed rust:
fn main() {
let mut v = vec![1, 2, 3, 4];
let mut i = 0;
while i < v.len() {
if v[i] % 2 == 0 {
v.remove(i);
} else {
v[i] += 100;
i += 1;
}
}
println!("{:?}", v); // [101, 103]
}
BTW remove() is an O(n) operation but doesn't allocate memory.
Playground
I am providing my take for this problem as I was unaware of the retain method:
impl<T> RemoveFilter<T> for Vec<T> {}
pub trait RemoveFilter<T>: BorrowMut<Vec<T>> {
fn remove_filter<F: for<'b> FnMut(&'b T) -> bool>(&mut self, mut cb: F) {
let vec: &mut Vec<T> = self.borrow_mut();
let mut write_to = 0;
let mut read_from = 0;
while read_from < vec.len() {
let maintain = cb(&mut vec[read_from]);
if maintain {
vec.as_mut_slice().swap(read_from, write_to);
write_to += 1;
}
read_from += 1;
}
vec.resize_with(write_to, || panic!("We are shrinking the vector"));
}
}
It will shift the elements as it iterates and then remove anything that is left behind. I think this is code may easily modified to solve other problems.