Commit 165df950 authored by phil's avatar phil
Browse files

docs, adaptions for linux

parent e1797cd2
......@@ -3,17 +3,24 @@ project(hid_pd_external C)
set(CMAKE_C_STANDARD 99)
if (APPLE)
set(MAKEFILE "Makefile.macos")
elseif(UNIX)
set(MAKEFILE "Makefile.unix")
else()
message(ERROR system not supported)
endif()
add_custom_target(hid
COMMAND make
COMMAND make -f ${MAKEFILE}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
add_custom_target(install-externals
DEPENDS hid
COMMAND make install
COMMAND make -f ${MAKEFILE} install
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
add_custom_target(clean-externals
COMMAND make clean
COMMAND make -f ${MAKEFILE} clean
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
\ No newline at end of file
PDLIBBUILDER_DIR=${CURDIR}/deps/pd-lib-builder
LIBUSB_DIR=/usr/include/libusb-1.0
HIDAPI_DIR=/usr/include/hidapi
HIDMAP_DIR=${CURDIR}/deps/USB-HID-Report-Parser
# using invasive detaching.... ahem.
cflags += -I${LIBUSB_DIR} -I${HIDAPI_DIR} -I${HIDMAP_DIR} -DDETACH_KERNEL_DRIVER
ldflags += -lusb-1.0 -lhidapi-libusb -L${HIDMAP_DIR} -lusbhid_map
export PDDIR
export PDLIBBUILDER_DIR
export LIBUSB_DIR
export HIDAPI_DIR
export HIDMAP_DIR
export cflags
export ldflags
all:
$(MAKE) -C src/hid
install:
$(MAKE) -C src/hid install
clean:
$(MAKE) -C src/hid clean
......@@ -5,7 +5,7 @@ HIDAPI_DIR=${CURDIR}/deps/hidapi
HIDMAP_DIR=${CURDIR}/deps/USB-HID-Report-Parser
cflags += -I${LIBUSB_DIR}/libusb -I${HIDAPI_DIR}/hidapi -I${HIDMAP_DIR}
ldflags += -L${LIBUSB_DIR}/libusb -lusb -L${HIDAPI_DIR}/local-install/lib -lhidapi -L${HIDMAP_DIR} -lusbhid_map
ldflags += -L${LIBUSB_DIR}/libusb -lusb -L${HIDAPI_DIR}/local-install/lib #-lhidapi -L${HIDMAP_DIR} #-lusbhid_map
export PDDIR
export PDLIBBUILDER_DIR
......@@ -16,7 +16,7 @@ export cflags
export ldflags
all: libusb hidapi usbhid_map
all: libusb hidapi #usbhid_map
$(MAKE) -C src/hid
install:
......@@ -26,7 +26,6 @@ clean:
$(MAKE) -C src/hid clean
$(MAKE) -C ${LIBUSB_DIR} clean
$(MAKE) -C ${HIDAPI_DIR} clean
$(MAKE) -C ${HIDMAP_DIR} clean
### libusbb
......@@ -55,16 +54,3 @@ ${HIDAPI_DIR}/Makefile: ${HIDAPI_DIR}/configure
${HIDAPI_DIR}/configure:
cd ${HIDAPI_DIR}; ./bootstrap
### usbhid_map
# usbhid_map: ${HIDMAP_DIR}/libusbhid_map.a
# $(MAKE) -C ${HIDMAP_DIR}
# ${HIDMAP_DIR}/libusbhid_map.a: ${HIDMAP_DIR}/Makefile
usbhid_map: ${HIDMAP_DIR}/Makefile
$(MAKE) -C ${HIDMAP_DIR}
${HIDMAP_DIR}/Makefile:
cd ${HIDMAP_DIR}; cmake .
# hid-pd-external
An USB HID external for PureData (PD)
Built and tested on macos 10.15.7.
Tested RPi 4 (5.10.17-v7l+), in principle works but is suboptimal.
## Todos
- [ ] Add output report support
- [ ] Feature reports?
- [ ] Use hidraw instead of libusb (see note under linux)
## Building
### macos
*Should* work as is..
```bash
git clone --recursive https://github.com/tschiemer/hid-pd-external
cd hid-pd-external/
make -f Makefile.macos && make -f Makefile.macos install
```
### linux (Raspberry Pi)
A bit more troublesome.. these instructions might not get you all the way, but should be a start.
*NOTE* as it is, using libusb (in this external) on linux is somewhat invasive on USB devices at it might kernel
drivers might be detached for this to work. Using hidraw (as offered by hidapi) this seems to work without
further problem, but this is something that remains to be done.
Make sure to install required libraries:
```bash
sudo apt install libusb-1.0-0-dev libhidapi-dev
```
```bash
git clone --recursive https://github.com/tschiemer/hid-pd-external
cd hid-pd-external/
make -f Makefile.linux
sudo make -f Makefile.linux install
```
*NOTE* Your user will have to get direct access to HID devices, if you know which device it is, you can do something like
```bash
sudo chmod 777 /dev/bus/usb/001/003
```
In principle you could set up [udev to do this for you](https://askubuntu.com/a/15643) (but didn't work for me).
## `hid` object interface
- `list [vendorid <vendor-id>] [productid <product-id>] [serial <serial-str>] [usage_page <usage-page>] [usage <usage>]`
Output any HID devices matching the given criteria in the form of:
`device <usage-page> <usage> <vendor-id> <product-id> <serial-str> <manufacturer-str> <product-str>`
If `serial-str`, `manufacturer-str` or `product-str` is not defined by the device will return a dash `-`.
- `open [vendorid <vendor-id>] [productid <product-id>] [serial <serial-str>] [usage_page <usage-page>] [usage <usage>]`
Attempts to open first device matching given criteria for interaction.
Outputs `opened` message on success.
- `close`
Closes previously opened device.
- *bang* / `poll <msec>`
Output/process incoming reports and output changed values in the form of
`value <usage-page> <usage> <value>`
If polling (`<msec>` > 0) is enabled *banging* is not possible, to disable polling set `<msec>` equal zero.
## License
Copyright 2021 Philip Tschiemer, ICST Institute for Computer Music and Sound
Technology, Zurich University of the Arts
All rights reserved.
GNU General Public License v3
Relies on (also see [deps](deps)):
- [libusb](https://github.com/libusb/libusb)
- [hidapi](https://github.com/libusb/hidapi)
- [USB-HID-Report-Parser](https://github.com/tschiemer/USB-HID-Report-Parser/tree/usbhid_map)
- [pd-lib-builder](https://github.com/pure-data/pd-lib-builder)
\ No newline at end of file
......@@ -9,6 +9,8 @@ lib.name = hid
# input source file (class name == source file basename)
class.sources = hid.c
common.sources = $(HIDMAP_DIR)/usbhid_map.c
# all extra files to be included in binary distribution of the library
datafiles =
......
#include "m_pd.h"
#include "libusb.h"
#include "hidapi.h"
#include "usbhid_map.h"
#include <libusb.h>
#include <hidapi.h>
//#include "report_item.h"
//#include "report_usage.h"
......@@ -378,100 +379,128 @@ static int hid_filter_device_list(libusb_device **devs, ssize_t count, hid_devic
uint8_t interface_num = config->interface[k].altsetting[l].bInterfaceNumber;
uint8_t report_desc[256];
r = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_INTERFACE,
LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8) | interface_num, 0,
report_desc, sizeof(report_desc), 5000);
if (r < 4) {
error("control_transfer() fail for report descriptor (%04x:%04x): %d", desc.idVendor, desc.idProduct, r);
} else if (report_desc[0] != 0x05 || report_desc[2] != 0x09) {
error("Notice: report descriptor not starting with usage page and usage bytes: %02x %02x %02x %02x",
report_desc[0], report_desc[1], report_desc[2], report_desc[3]);
} else if ((usage_page != 0 && usage_page != report_desc[1]) ||
(usage != 0 && usage != report_desc[3]) ||
(serial != NULL && desc.iSerialNumber == 0)) {
// filter out unwanted usages
// ie, do nothing
#ifdef DETACH_KERNEL_DRIVER
int detached = 0;
r = libusb_kernel_driver_active(handle, interface_num);
if (r == 1) {
r = libusb_detach_kernel_driver(handle, interface_num);
if (r < 0)
error("Couldn't detach kernel driver, even though a kernel driver was attached");
else
detached = 1;
}
#endif
r = libusb_claim_interface(handle, interface_num);
if (r < 0) {
error("claim_interface(): %d", r);
} else {
uint8_t report_desc[256];
r = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_INTERFACE,
LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8) | interface_num, 0,
report_desc, sizeof(report_desc), 5000);
if (r < 4) {
error("control_transfer() fail for report descriptor (%04x:%04x): %d", desc.idVendor, desc.idProduct, r);
} else if (report_desc[0] != 0x05 || report_desc[2] != 0x09) {
error("Notice: report descriptor not starting with usage page and usage bytes: %02x %02x %02x %02x",
report_desc[0], report_desc[1], report_desc[2], report_desc[3]);
} else if ((usage_page != 0 && usage_page != report_desc[1]) ||
(usage != 0 && usage != report_desc[3]) ||
(serial != NULL && desc.iSerialNumber == 0)) {
// filter out unwanted usages
// ie, do nothing
// } else if (o >= count) {
// error("Too many HID interfaces, skipping device %04x:%04x usage %d %d", desc.idVendor,
// desc.idProduct, report_desc[1], report_desc[3]);
} else {
} else {
wchar_t * serial_wstring = NULL;
char serial_cstring[256];
wchar_t * serial_wstring = NULL;
char serial_cstring[256];
if (desc.iSerialNumber > 0 && (serial_wstring = get_usb_wstring(handle, desc.iSerialNumber)) == NULL){
error("failed to load (required) serial for device %04x:%04x", desc.idVendor, desc.idProduct);
} else if (serial != NULL && wchar2char(serial_wstring, serial_cstring, sizeof(serial_cstring)) && strcmp(serial, serial_cstring) != 0){
free(serial_wstring);
} else {
if (desc.iSerialNumber > 0 && (serial_wstring = get_usb_wstring(handle, desc.iSerialNumber)) == NULL){
error("failed to load (required) serial for device %04x:%04x", desc.idVendor, desc.idProduct);
} else if (serial != NULL && wchar2char(serial_wstring, serial_cstring, sizeof(serial_cstring)) && strcmp(serial, serial_cstring) != 0){
free(serial_wstring);
} else {
hid_device_t * hiddev = calloc(1, sizeof(hid_device_t) + r);
hid_device_t * hiddev = calloc(1, sizeof(hid_device_t) + r);
hiddev->dev = dev;
libusb_ref_device(dev); // increase reference count
hiddev->dev = dev;
libusb_ref_device(dev); // increase reference count
memcpy(&hiddev->desc, &desc, sizeof(struct libusb_device_descriptor));
memcpy(&hiddev->desc, &desc, sizeof(struct libusb_device_descriptor));
// on macos causes a fault...
// on macos causes a fault...
// hiddev->config = config;
// config = NULL;
hiddev->interface_num = interface_num;
hiddev->report_desc_len = r;
memcpy(hiddev->report_desc, report_desc, r);
/* Serial Number */
hiddev->serial_string = serial_wstring;
/* Manufacturer and Product strings */
if (desc.iManufacturer > 0)
hiddev->manufacturer_string =
get_usb_wstring(handle, desc.iManufacturer);
if (desc.iProduct > 0)
hiddev->product_string =
get_usb_wstring(handle, desc.iProduct);
/* Find the INPUT and OUTPUT endpoints. An
OUTPUT endpoint is not required. */
for (int e = 0; e < config->interface[k].altsetting[l].bNumEndpoints; e++) {
const struct libusb_endpoint_descriptor *ep = &config->interface[k].altsetting[l].endpoint[i];
/* Determine the type and direction of this
endpoint. */
int is_interrupt =
(ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK)
== LIBUSB_TRANSFER_TYPE_INTERRUPT;
int is_output =
(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
== LIBUSB_ENDPOINT_OUT;
int is_input =
(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
== LIBUSB_ENDPOINT_IN;
/* Decide whether to use it for input or output. */
if (hiddev->input_endpoint == 0 &&
is_interrupt && is_input) {
/* Use this endpoint for INPUT */
hiddev->input_endpoint = ep->bEndpointAddress;
hiddev->input_ep_max_packet_size = ep->wMaxPacketSize;
}
if (hiddev->output_endpoint == 0 &&
is_interrupt && is_output) {
/* Use this endpoint for OUTPUT */
hiddev->output_endpoint = ep->bEndpointAddress;
hiddev->interface_num = interface_num;
hiddev->report_desc_len = r;
memcpy(hiddev->report_desc, report_desc, r);
/* Serial Number */
hiddev->serial_string = serial_wstring;
/* Manufacturer and Product strings */
if (desc.iManufacturer > 0)
hiddev->manufacturer_string =
get_usb_wstring(handle, desc.iManufacturer);
if (desc.iProduct > 0)
hiddev->product_string =
get_usb_wstring(handle, desc.iProduct);
/* Find the INPUT and OUTPUT endpoints. An
OUTPUT endpoint is not required. */
for (int e = 0; e < config->interface[k].altsetting[l].bNumEndpoints; e++) {
const struct libusb_endpoint_descriptor *ep = &config->interface[k].altsetting[l].endpoint[i];
/* Determine the type and direction of this
endpoint. */
int is_interrupt =
(ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK)
== LIBUSB_TRANSFER_TYPE_INTERRUPT;
int is_output =
(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
== LIBUSB_ENDPOINT_OUT;
int is_input =
(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
== LIBUSB_ENDPOINT_IN;
/* Decide whether to use it for input or output. */
if (hiddev->input_endpoint == 0 &&
is_interrupt && is_input) {
/* Use this endpoint for INPUT */
hiddev->input_endpoint = ep->bEndpointAddress;
hiddev->input_ep_max_packet_size = ep->wMaxPacketSize;
}
if (hiddev->output_endpoint == 0 &&
is_interrupt && is_output) {
/* Use this endpoint for OUTPUT */
hiddev->output_endpoint = ep->bEndpointAddress;
}
}
}
(*hiddevs)[o++] = hiddev;
(*hiddevs)[o++] = hiddev;
// post("usage (page) = %d (%d)", report_desc[3], report_desc[1]);
}
}
} // claimed
#ifdef DETACH_KERNEL_DRIVER
/* Re-attach kernel driver if necessary. */
if (detached) {
r = libusb_attach_kernel_driver(handle, interface_num);
if (r < 0)
error("Couldn't re-attach kernel driver.\n");
}
#endif
libusb_close(handle);
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment