Jinjinov/Usb.Events

Support for 32 bit Raspberry Pi OS

tcochunk opened this issue · 26 comments

Thank you for the library....i was using it on Windows and it is very nice....easy to use and works as expected. Much appreciated.

I am wanting to use it on a Raspberry Pi, compiling the test project with .Net 6 (I see that the project is compatible with .Net Core 2 and higher) but when i execute the test program, i find no usb devices. I have a mouse, keyboard, and a Raspberry Pico plugged in. Here is my test project.....


using IUsbEventWatcher usbEventWatcher = new UsbEventWatcher(startImmediately: true, addAlreadyPresentDevicesToList: true, usePnPEntity: true, includeTTY: true);

Console.WriteLine("Iterating over existing usb devices: " + usbEventWatcher.UsbDeviceList.Count());

foreach (var device in usbEventWatcher.UsbDeviceList)
{
    Console.WriteLine($"{DateTime.Now} Existing:" + Environment.NewLine + device + Environment.NewLine);
}
Console.WriteLine("Done Iterating over existing usb devices: " + usbEventWatcher.UsbDeviceList.Count());

I cant figure out what i am missing....any ideas?

Thanks

What OS are you using?

Sorry for the delay in response...

I am running Raspbian Linux 11 bullseye the 32bit version\

Because Usb.Events uses native C for Linux and not C# it can behave differently than on Windows.

It is also possible that the native C code doesn't work on Raspbian Linux - I tested it only on Ubuntu.

Can you try testing with this code:

using Usb.Events;

 class Program
 {
     static void Main(string[] _)
     {
         using IUsbEventWatcher usbEventWatcher = new UsbEventWatcher();

         usbEventWatcher.UsbDeviceRemoved += (_, device) => Console.WriteLine("Removed:" + Environment.NewLine + device + Environment.NewLine);

         usbEventWatcher.UsbDeviceAdded += (_, device) => Console.WriteLine("Added:" + Environment.NewLine + device + Environment.NewLine);

         usbEventWatcher.UsbDriveEjected += (_, path) => Console.WriteLine("Ejected:" + Environment.NewLine + path + Environment.NewLine);

         usbEventWatcher.UsbDriveMounted += (_, path) =>
         {
             Console.WriteLine("Mounted:" + Environment.NewLine + path + Environment.NewLine);

             foreach (string entry in Directory.GetFileSystemEntries(path))
                 Console.WriteLine(entry);

             Console.WriteLine();
         };

         Console.ReadLine();
     }
 }

You can also try:

  • on Ubuntu on a PC
  • on Ubuntu on the Raspberry Pi
  • on Windows IoT on Raspberry Pi

Try it with all USB devices you have - all USB flash drives, external hard drives, any keyboard, mouse, webcam, printer, joypad, ...
Try it on all USB ports

So i tried keyboard, mouse, pico, and usb drive.....nothing triggered an event. I believe Raspbian is based on the debian linux os but optimized for raspberry pi.

besides switching operating systems do you have any other ideas?

i know its asking ALOT but any chance you can test it on debian?

A few other ideas - make sure you have gcc sudo apt install build-essential and ludev sudo apt-get install libudev-dev:

  • try running Usb.Events.Test directly from the source code, so there is no NuGet package - this way, the native Linux C code will compile on Raspberry Pi, see here.
  • you can also try running the native C code by itself, without the C#, use this makefile. This way you can put breakpoints directly into the native C code and see exactly what happens.

I will try to test it in Debian or, even better, on Raspbian for PC:
https://www.raspberrypi.com/software/raspberry-pi-desktop

I hope that the reason is not that the native C code works on Intel, but not on ARM.

I tried it with Raspberry Pi OS and I found the reason: Usb.Events works only on x64

I somehow forgot that, because I started the project a long time ago

Can you try with 64 bit Raspberry Pi OS?
https://www.raspberrypi.com/software/operating-systems/#raspberry-pi-os-64-bit

Thank you for the update. I migrated my pi to 64bit and could NOT get it to work. no usb devices are recognized. I went back my 32 bit version of the OS and that also did not work. I was using version 11.0.0.0

I compiled for AnyCPU and .net 7. I published it for linux-arm (and linux-arm64 when i was using the 64 bit OS)

I was running the code you suggested I run.

any more thoughts?

That is my fault - I was focused on making it for 32 bit and I forgot on ARM.

At first I compiled the native C code with:

gcc UsbEventWatcher.Linux.c -o UsbEventWatcher.Linux.so

Now I added:

gcc -m32 UsbEventWatcher.Linux.c -o UsbEventWatcher.Linux.so
gcc -m64 UsbEventWatcher.Linux.c -o UsbEventWatcher.Linux.so

And I thought that solved the problem.

Of course, to solve the problem for ARM, I will have to add:

arm-linux-gnueabi-gcc -march=armv7-a UsbEventWatcher.Linux.c -o UsbEventWatcher.Linux.so
aarch64-linux-gnu-gcc -march=armv8-a UsbEventWatcher.Linux.c -o UsbEventWatcher.Linux.so

I will do this as soon as I find the time to do it, perhaps today...

If you want to try it yourself, just add the gcc commands here: https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/Usb.Events.csproj#L54

you will need:

sudo apt install gcc-arm-linux-gnueabi
sudo apt install gcc-aarch64-linux-gnu

and add the package to the right folder:

runtimes\linux-arm\native
runtimes\linux-arm64\native

here: https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/Usb.Events.csproj#L66

I have somewhat bad news - installing ARM versions of gcc:

sudo apt-get install gcc-arm-linux-gnueabi
sudo apt-get install gcc-aarch64-linux-gnu

clashes with the package that enables to compile for 32 bit Intel on 64 bit Intel

sudo apt-get install gcc-multilib

That means the csproj file can not simultaneously include instructions to compile for ARM and Intel, which would be ideal.
To make it work, there would have to be 2 csproj files and 2 Linux virtual machines.

Even worse news:

sudo apt-get install libudev-dev:armhf libudev-dev:arm64

returns:

Unable to locate package libudev-dev:armhf
Unable to locate package libudev-dev:arm64

It has something to do with the fact that ARM packages are not available on Intel platforms by default.
I couldn't find a solution in the last 6 hours.
I don't know why I can install ARM versions of gcc on an Intel PC, but not the necessary libraries.

Right now I can think of only 3 solutions:

  • I buy a Raspberry Pi
  • You compile the source code on Raspberry Pi
  • We find an expert who knows how to compile for ARM on an Intel PC

ChatGPT hallucinations led me down the wrong path, wasting A LOT of my time.

What I wrote in the previous comment is not all true.

I will try to find a solution for this.

EDIT:

it is unbelievable how confident ChatGPT, Bing AI and Bard AI are when misleading you about compiling for ARM on Intel. All three consistently give wrong information. They all suggest cross-compilation, but that works only if you don't need the libudev for ARM - once you hit that wall, they keep you running in circles.

I finally got 3 options that will probably work:

  • Obtaining libudev with QEMU emulation
  • Obtaining libudev with Docker
  • Obtaining libudev with Virtualization on ARM-based Macs using Hypervisor.framework

I will probably try Docker first, sounds promising.

This was the hardest issue so far, but I learned a lot! :)

I hope it works, I have no way to test it.

https://github.com/Jinjinov/Usb.Events/releases/tag/v11.0.0.1

Sorry for the delayed response....work has been crazy. I missed the ChatGPT comment until today....I chuckled and simultaneously felt bad for you and appreciative of your efforts.

You mentioned that what you said in the previous comment was not correct - i am not sure what part was not correct and I dont understand how using a Container (docker) could resolved the issue.

When i tested 11.0.0.1, i was not able to get it to work, I tried on 32 and 64 bit Pi OS.

Is there something I can do, you had mentioned compiling on the Pi (maybe that was the "wrong" part of your comment??)
Since i have the Pi, maybe i can do something to contribute to your project? I would offer to send you a Pi, but they are so hard to get right now....its crazy.

There was a flood of my comments in this thread, I deleted most of them and truncated others...

It still doesn't work, huh... there is not much more I can do, but perhaps you can help - instructions at the end, you can skip there if details don't interest you.

To explain all the changes since the start of this issue - most of them are in the Usb.Events.csproj

The problem: we need to compile UsbEventWatcher.Linux.c to UsbEventWatcher.Linux.so for platforms other than 64 bit Intel.

  1. I ran sudo apt-get install libudev-dev:i386 and sudo apt-get install gcc-multilib to my Ubuntu so that this line can execute:
    https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/Usb.Events.csproj#L59
    it compiles a 32 bit UsbEventWatcher.Linux.so on a 64 bit Linux - but that only got us UsbEventWatcher.Linux.so for 32 bit Intel, not ARM.

  2. because I couldn't get a gcc cross compiler for ARM to work (it would work if UsbEventWatcher.Linux.c wouldn't depend on libudev but it does, and I couldn't find a way to install an ARM version of libudev-dev on my Intel like I managed with libudev-dev:i386 - but that was only for 32 bit Intel on 64 bit Intel, it works) - so I was left with the option to compile the ARM version in ARM environment - that is where Docker comes into play. Docker uses QEMU to emulate ARM on Intel, meaning you can have an ARM Ubuntu inside a Docker container running on Intel. In that ARM Ubuntu you can install gcc normally and add an ARM version of libudev-dev and use gcc to compile UsbEventWatcher.Linux.c to UsbEventWatcher.Linux.so for ARM. This happens here:
    https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/Usb.Events.csproj#L70
    using these files:
    https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/Dockerfile.arm
    https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/Dockerfile.arm64
    https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/entrypoint.sh

The new NuGet has all these versions of UsbEventWatcher.Linux.so as you can see here if you open the runtimes folder:
https://nuget.info/packages/Usb.Events/11.0.1
you will see:

  • linux-arm
  • linux-arm64
  • linux-x64
  • linux-x86

I tested only linux-x64 and linux-x86 and they both work. But I can't test inserting physical USB devices to Docker - at least I don't know how to.

TL;DR:

What you could do is relatively simple, but it would help a lot:

  1. compile only the C code directly on ARM:
    • run sudo apt-get install build-essential on ARM
    • run sudo apt-get install libudev-dev on ARM
    • open the Linux folder in VS Code on ARM
    • debug main.c in VS Code
      this will tell us if the C code even works on ARM.
  2. if the C code works on ARM, you can test the C# on ARM
    • install C# Dev Kit extension in VS Code on ARM (it includes the C# extension)
    • install .NET 7 SDK on ARM
    • copy the UsbEventWatcher.Linux.so you got in the first step to Usb.Events\Usb.Events\bin\Debug\netstandard2.0 and Usb.Events\Usb.Events.Test\bin\Debug\net7.0 because the copy commands that do that in Usb.Events.csproj on Intel will not work on ARM
    • build Usb.Events
    • debug Usb.Events.Test
  3. repeat everything for Release - VS Code is configured for both Debug and Release:

You could also do a binary compare of your UsbEventWatcher.Linux.so and
https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/arm/Debug/UsbEventWatcher.Linux.so
https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/arm/Release/UsbEventWatcher.Linux.so
https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/arm64/Debug/UsbEventWatcher.Linux.so
https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/arm64/Release/UsbEventWatcher.Linux.so

I got the c library compiled on raspberry pi 64 bit using this link:

https://stackoverflow.com/questions/14884126/build-so-file-from-c-file-using-gcc-command-line

When I ran main.c, it seemed to be working as expected (see screen shot)

CProgramResults

However, when i tried to build the C# code i got errors about gcc and -m32:

2023-07-30-161705_1920x1200_scrot

I am not sure how to resolve.....this seems to be coming from the project file from line 59 with Command="gcc -m32..."

it almost like it is tryingto build for x86 even though i am on an ARM???

any thoughts?

I am glad that you were able to run main.c and that it works! :)

You don't have to build from the terminal, the file that runs the gcc is the Makefile:
https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/Linux/Makefile
and it is already integrated into VS Code with these:
https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/Linux/.vscode/launch.json
https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/Linux/.vscode/tasks.json
so you can just select it from the top menu in VS Code: Terminal / Run build task
If you don't have that option in your VS Code, install the C/C++ extension and the Makefile Tools extension.

The problem with -m32 is my mistake, I added tasks to Usb.Events.csproj that run the gcc to compile the C code before building the C# code. I didn't foresee that anyone would run the Usb.Events.csproj on ARM and I didn't think about it when I wrote you the instructions.

I changed the Usb.Events.csproj to check for architecture before running the gcc, so it should work now.

I tried a couple different things but could not get it to work. This is what I did but i feel like I am screwing something up.

  1. Built USBEventWatcher.Linux.o using make but added the fPIC CFLAG so i could convert it to a .so
  2. Confirmed the c lib recognized the inserting and removing of Pico
  3. Converted obj/USBEventWatcher.Linux.o to obj/USBEventWatcher.Linux.so because it was not created by default when building the c lib.
  4. copied the .so file to Usb.Events/bin/Debug/netstandard2.0 (there was not a net7.0 folder)
  5. built Usb.Events
  6. built Usb.Events.Tests but got an error about undefined symbol udev_new

Like i said, i feel like i am screwing something up but dont know where to go. Much of this work is not what i am used to (Linux, c, VSCode for C#, etc...)

2023-08-05-161720_1920x1200_scrot

Thank you for trying!

I know there is a lot involved, especially for someone who is not used to all these technologies :)

If you carefully follow all these instructions, it should work:

Install these:
https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools
https://marketplace.visualstudio.com/items?itemName=ms-vscode.makefile-tools
https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp
https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit

Don't run gcc from the command line because you probably didn't use the right parameters.
Open .\Usb.Events\Usb.Events\Linux folder in VS Code and use Terminal / Run Build Task... to build:

image

or at least use the Makefile where all the parameters are already defined.
To run gcc with a Makefile, simply type make in the command line in the same folder.

Copy the UsbEventWatcher.Linux.so to:

  • Usb.Events\Usb.Events\bin\Debug\netstandard2.0 and
  • Usb.Events\Usb.Events.Test\bin\Debug\net7.0

Be careful, one is Usb.Events\Usb.Events and the other is Usb.Events\Usb.Events.Test

Then open .\Usb.Events folder in VS Code and use Terminal / Run Build Task... to build.

I confirmed i had the 4 correct vscode tools. I opened the linux folder, selected the makefile, clicked Terminal->Run Build Task.

I was prompted with a tool selection -> Build or Build Debug. I selected Build Debug but only got a UsbEventWatcher.Linux.o file was created in the Usb.Events/Usb.Events/Linux/obj directory.

Is there something i should be doing to get the .so file or looking in a different place?

That is my fault again. I forgot that my script works only on Intel, not on ARM. There are so many different places in the build process where I never considered ARM, and I can't test it all because I don't have an ARM. I'm on vacation this week, I will take a look when I get back home.

Did you use the -ludev flag beside the -fPIC and -shared flags?

If you don't use gcc -shared -ludev -fPIC then you get undefined symbol: udev_new

sorry to keep coming back.....i only get time intermittently to work on this...i found an issue in the csproj for compiling against arm64....around line 121 it says

exec condition isOsPlatform = linux and IsArm = true

but it is missing the longbit check.....because on 64 bit it tries to use -march=armv7-a+fp which i think is incorrect.....maybe i am wrong but when i put hte longbit check in, the buld correctly selected the msbuild cmd on line 129

i guess the commands could also just be reordered but that seems hacky

Could you use the latest version? I think that the line numbers don't match.

This line compiles to ./arm/ folder for ARM 32 on 32 and 64 bit systems
https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/Usb.Events.csproj#L116
so no check is needed because a 64 bit system can compile for 32 bit

This line compiles to ./arm64/ folder for ARM 64 on 64 bit systems
https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/Usb.Events.csproj#L124
so we need the 64 bit check because a 32 bit system can't compile for 64 bit

Did you try what I suggested the last time?

Did you use the -ludev flag beside the -fPIC and -shared flags?

If you don't use gcc -shared -ludev -fPIC then you get undefined symbol: udev_new

I think you were really close to making it work, because I can see undefined symbol: udev_new in your screenshot.

I also wrote more information in the Readme: https://github.com/Jinjinov/Usb.Events#how-to-build

you are correct the line numbers dont match, earlier i had added a couple log statements that messed up the line numbers. however we are talking about the same line numbers and the command on line 118 was giving me the error. when i added the LongBit check the build correctly choose the path of your second reference and successfully built with line 126.

I then built the Test project and everything seems to be working as expected. HOORAY!!!

I will now test it with my code. THANK YOU for your guidance and help!!!!

I am glad that you made it work! :)

Can you please tell me about your exact setup you are using now?
Which OS exactly?
Is your system now 32 or 64 bit?
What is the exact error you got on line 118?

If there are any mistakes in the csproj file, I want to fix them.

When using Intel, gcc can compile for 32 bit on a 64 bit system, that is why there is no bit check this line
https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/Usb.Events.csproj#L98

I was using the same logic for ARM, but I can't test it because I don't have an ARM.
If 64 bit ARM can't compile for 32 bit ARM then I will add the bit check.

Can you try adding -m32 to line 118 without the bit check in line 116?
change
gcc $(Flags) -march=armv7-a+fp
to
gcc $(Flags) -m32 -march=armv7-a+fp

Which OS exactly?
using cat /etc/os-version
OS Debian GNU Linux 11

using uname -a
Linux raspberrypi 6.1.21-v8+ #1642 SMP PREEMPT Mon Apr 3 17:24:16 BST 2023 aarch64 GNU/Linux

Is your system now 32 or 64 bit?
64 bit

What is the exact error you got on line 118?
I had some comments in so i think in the image below it is line 123 that was erroring
ExactErrorMessage

Can you try adding -m32 to line 118 without the bit check in line 116?
I had some comments in so i think in the image below it is line 121 that was erroring with this message
m32ErrorMessage

I made the following changes to the csproj file:
image

I made the following changes to the makefile:
image

In addition, when building the linux library on the PI, the so file was not create by the build process so i had to run a command to convert the .o to .so. This is what i ran in the linux obj directory:
gcc UsbEventWatcher.Linux.o -shared -fPIC -ludev -o UsbEventWatcher.Linux.so

I copied this file to the required locations per your instrunctions and your test project worked as expected. i plugged in my pico and the test project found it, i unplugged the pico and the test project recognized the pico was unplugged. However, when i tried to use that .so file in my test project, i could not get anything to work??? Any ideas? i was wondering if compiling the linux code in debug or release would matter???