USB bulk transfer in C

Hi,

I need to write an application (possibly using C) to communicate with a USB High Speed Device CDC class I am developing, but unfortunately I have no development experience under Mac OS, so I am here to ask for a few help/advice. I hope I am in the right place.

Since I have a working code using libusb on Linux, I have first tried to use such lib on a Mac OS without success. The device is listed correctly using

ioreg -w0 -l -p

but it seems to be always busy:

MYUSBDEVICE@fa410000 <class IOUSBHostDevice, id 0x100001769, registered, matched, active, busy 0 (262 ms), retain 24>

in fact, attempting to use libusb always results in error:

LIBUSB_ERROR_NOT_FOUND
libusb_bulk_transfer: Entity not found

After searching a bit, I have read that it is impossible (?) to use libusb on MacOS.

Then I came across these following pages:

  1. https://developer.apple.com/library/archive/documentation/DeviceDrivers/Conceptual/USBBook/USBDeviceInterfaces/USBDevInterfaces.html
  2. https://developer.apple.com/documentation/usbdriverkit

I would possible avoid to write a CDC kernel driver for my application, so link #2 seems more appropriate to what I need.

But isn't it available any API to develop USB CDC communications C application on MacOS ?

Any suggestion is appreciated. Thanks in advance.

Regards, Simon

I need to write an application (possibly using C) to communicate with a USB High Speed Device CDC class I am developing, but unfortunately I have no development experience under Mac OS, so I am here to ask for a few help/advice. I hope I am in the right place.

The first place I'd start is with the original, core IOKit documentation. While all of these documents are quite old and some details have changed, the overall architecture is unchanged as are most of the APIs.

Moving on to more specific advice:

The device is listed correctly using ioreg -w0 -l -p

I would recommend downloading and experimenting with IORegistryExplorer.app, which you will find inside "Additional Tools for Xcode..." on our "More Downloads" page. Note that while we release new versions of that package for every release, IORegistryExplorer.app only changes VERY rarely.

A few quick comments on it:

  • I recommend this tool over ioreg because I think it does a much better job of showing how driver actually connect to each other and relate. Case in point, the "busy" state you mentioned is much less relevant than the driver stack that's loaded on top of it.

  • The tries to keep it's state up to date, but that can make the interface feel sluggish and will periodically reset the interface. You can save and then open registry snapshots to avoid this.

Moving on to USB:

Since I have a working code using libusb on Linux, I have first tried to use such lib on a Mac OS without success.

But isn't it available any API to develop USB CDC communications C application on MacOS ?

MacOS actually has two separate USB access libraries. The original API is IOUSBLib, which is a C library built on COM. It is both very old (introduced in 10.0) and relies on a compatibility layer built into the kernel. I would strongly recommend you not use it.

It's replacement was the IOUSBHost framework, which is an ObjectiveC framework we created when when we rewrote our USB stack. Note that as you look at our USB APIs, "IOUSB<>" is the legacy API/compatibility layer, while "IOUSBHost<>" is the modern driver stack.

If you're new to our platform you might prefer to use C over ObjectiveC, but my personal recommendation would be to strongly prefer the IOUSBHost framework over IOUSBLib. Frankly, ObjectiveC is nicer to pick up than you might think, a sentence no one on earth has ever said about COM.

in fact, attempting to use libusb always results in error:

I'm not sure what the state of libusb is today, but it was originally written using IOUSBLib. However, I suspect that the issue here is actually that there is another driver "in the way", not that the it doesn't work at all.

Getting to the device itself:

but it seems to be always busy: MYUSBDEVICE@fa410000 <class IOUSBHostDevice, id 0x100001769, registered, matched, active, busy 0 (262 ms), retain 24>

The basic question here is "what's actually going on and what do you want to happen"? The main reason you wouldn't be able to open a given device is that "something else" has claimed it first, but what exactly you do about that depends on exactly what the device is, what's claimed it, and how you want everything to ultimately work.

However, the general approach falls into one of two approaches:

  1. If you only want to temporarily control the device and then return it to the system driver's control, then you need to put the driver stack into the correct state, after which the system will allow you to claim the device yourself. For example, if a block storage device is completely unmounted, then the USB stack will allow that device to be seized. When your app releases the device, the mass storage driver stack will then reload.

  2. You can use a codeless DEXT (see "Support custom hardware without writing driver code") to modify the devices matching state. This will allow you to change which system driver controls the device or have the system treat it as a vendor specific device, allowing your app to control it at any time.

https://developer.apple.com/documentation/usbdriverkit I would possible avoid to write a CDC kernel driver for my application, so link #2 seems more appropriate to what I need.

It's possible you'll need a codeless DEXT to modify what's currently controlling it, but there's no reason you'd need to write a true DEXT/KEXT.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Hello Kevin,

First of all thank You for your extensive reply, I think there is a reasonable amount of information to move on.

MacOS actually has two separate USB access libraries. The original API is IOUSBLib, which is a C library built on COM. It is both very old (introduced in 10.0) and relies on a compatibility layer built into the kernel. I would strongly recommend you not use it.

It's replacement was the IOUSBHost framework, which is an ObjectiveC framework we created when when we rewrote our USB stack. Note that as you look at our USB APIs, "IOUSB<>" is the legacy API/compatibility layer, while "IOUSBHost<>" is the modern driver stack.

If you're new to our platform you might prefer to use C over ObjectiveC, but my personal recommendation would be to strongly prefer the IOUSBHost framework over IOUSBLib. Frankly, ObjectiveC is nicer to pick up than you might think, a sentence no one on earth has ever said about COM.

The main reason to work with C it's because we are upgrading an application for which the control SW was initially developed using C, to run on both Mac/Win/Linux. The idea is to maintain this philosophy as much as possible.

If Objective-C API framework environment is the preferred and most supported by Apple, it is likely we will use it for USB communication.

I actually don't know the required steps to link together Objective-C and C compiled binaries, it might be not so easy to accomplish, but not impossible of course.

The basic question here is "what's actually going on and what do you want to happen"?

Yep. Of course I have no idea why the OS keep the device in busy state. I suspect a driver is actually keeping it busy, how and why is out of my understatement right now. I have to investigate.

Thanks again! Simon

The main reason to work with C it's because we are upgrading an application for which the control SW was initially developed using C, to run on both Mac/Win/Linux. The idea is to maintain this philosophy as much as possible.

If Objective-C API framework environment is the preferred and most supported by Apple, it is likely we will use it for USB communication.

I understand. This is a case of picking the best tradeoff and the IOUSBHost Framework is definitely the better choice.

I actually don't know the required steps to link together Objective-C and C compiled binaries, it might be not so easy to accomplish, but not impossible of course.

I think you'll find it quite straightforward. Note for example, that our website doesn't actually differentiate between "C" and "ObjectiveC" APIs and that's because the transition between them is pretty trivial.

Finally, jumping back to here:

Yep. Of course I have no idea why the OS keep the device in busy state.

Your device isn't actually "busy" or, more specifically, the "busy" here doesn't mean what you think it does:

MYUSBDEVICE@fa410000 <class IOUSBHostDevice, id 0x100001769, registered, matched, active, busy 0 (262 ms), retain 24>

For the curious, "ioreg" is opensource so you can see what it's actually printing. The "busy" value there is actually the value returned by IOServiceGetBusyState()* which means:

"Many activities in IOService are asynchronous. When registration, matching, or termination is in progress on an IOService, its busyState is increased by one. Change in busyState to or from zero also changes the IOService's provider's busyState by one, which means that an IOService is marked busy when any of the above activities is occurring on it or any of its clients."

*Strictly speaking, it's calling the private function "IOServiceGetBusyStateAndTime", but that just returns busy state... and how long it's been since it was busy.

In other words, what "busy 0 (262 ms)" actually means is actually "I'm not busy now and the last time I was busy was 262 ms ago". In any case, the key point here is that busy state is specifically about the over all load/unload process, NOT general activity. Indeed, the API that actually useful here is "IOServiceWaitQuiet", since that's how you wait for the "rest" of the driver stack to finish loading before you try and interact with a given device.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

USB bulk transfer in C
 
 
Q