Got a USB device that you'd like to use on your Macintosh? Vendor won't provide a driver for it? That's a shame but there is an alternative. If you have a basic understanding of the C language, it's possible to write an OS X driver or application to access it.
| Document Status | Last Update |
| Work in Progress | 9/02/06 |
To write my OS X based USB application, I had to dig thru Apple's esoteric documentation, and scour the web and newsgroups for hours to get enough knowledge to make the project successful. I thought it would be good to write some notes.
This document is written for developers and tinkerers that have some knowledge of the C programming language but do not necessarily have any USB knowledge or any Macintosh API knowledge. I will attempt to cover all aspects of developing a USB based solution from start to finish. The following will be covered:
The control or default pipe is used to talk to the device and pass control and status info between the Macintosh and the device. When the device powers up, the control pipe is selected per the USB specification. The default address is x0000.
An element, or endpoint, is the source of data on a device that is read/written to. Each element has an address and stores a piece of data. Every USB device has a different number of elements so you'll need to figure out how many yours has what type of data they accept. More on that later.
The interface pipe is used to access the data elements on the device. Summing it up:


This pic is hard to see so I dumped the output to a text file. Let's look over it.
Composite device: "PICkit 2 Microcontroller Programmer"
Device Descriptor
Descriptor Version Number: 0x0200
Device VendorID/ProductID: 0x04D8/0x0033 (Microchip Technology Inc.)
Manufacturer String: 1 "Microchip Technology Inc."
Product String: 2 "PICkit 2 Microcontroller Programmer"
In this part you can see some important info. For one thing, we found our device - a PicKit 2 in this example. Secondly, we can identify the description, vendorID, productID, and product string. You will use these in your program to find the device.
"PICkit 2 Microcontroller Programmer"
Total Length of Descriptor: 41
Number of Interfaces: 1
Configuration Value: 1
Attributes: 0x80 (bus-powered)
MaxPower: 100 ma
Interface #0 - HID
Alternate Setting 0
Number of Endpoints 2
Endpoint 0x81 - Interrupt Input
Attributes: 0x81 (IN)
Attributes: 0x03 (Interrupt)
Max Packet Size: 64
Polling Interval: 1 ms
Endpoint 0x01 - Interrupt Output
Attributes: 0x01 (OUT)
Attributes: 0x03 (Interrupt)
Max Packet Size: 64
Polling Interval: 1 ms
Continuing we discover that the two endpoints have a 64 byte packet size. That means the USB controller in the host will split the transaction into 64-byte chunks on the bus. There is one input endpoint and one output endpoint. That means you can read from and write to the device. The device can be polled every 1 millisecond.
Here are the methods that will be used:
| Task | Method(s) |
| Find the device | HIDBuildDeviceList HIDCountDevices HIDGetFirstDevice HIDGetNextDevice |
| Create asynchronous interface | createAsyncPort |
| Setup asynchronous callback | CFRunLoopSourceCreate createAsyncEventSource CFRunLoopAddSource setInterruptReportHandlerCallback |
| Setup timer | CFRunLoopGetCurrent CFRunLoopTimerCreate CFRunLoopAddTimer |
| Start timer | CFRunLoopRun |
| Write to device | HIDSetReport |
| Read device | TimerCallback* callback_rpt* |
| Stop timer | CFRunLoopStop |
| Release device | HIDCloseReleaseInterface |
1. You may be tempted to just use the getReport function of the API. Sadly, this does not work as you might expect. The result will be you wasting a lot of time.
A getReport() will be sent as a request on the default (control) pipe. If your reports are coming through the interrupt IN pipe you will need to use the HID Manager to get the desired elements. There is currently no way to get the raw report.
See the link below for more details.
http://lists.apple.com/archives/usb/2003/Feb/msg00114.html
2. Once a device has been swiped by the HID manager, it cannot be released thru a release request even if the force option is specified.