Skip to content
Snippets Groups Projects
Commit 0c3cad37 authored by phil's avatar phil
Browse files

initial commit

parents
No related branches found
No related tags found
No related merge requests found
.idea
cmake-build-debug
[submodule "deps/pd-lib-builder"]
path = deps/pd-lib-builder
url = https://github.com/pure-data/pd-lib-builder/
[submodule "deps/libusb"]
path = deps/libusb
url = https://github.com/libusb/libusb
cmake_minimum_required(VERSION 3.17)
project(hid_pd_external C)
set(CMAKE_C_STANDARD 99)
add_custom_target(hid
COMMAND make
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
add_custom_target(install-externals
DEPENDS hid
COMMAND make install
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
add_custom_target(clean-externals
COMMAND make clean
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
\ No newline at end of file
Makefile 0 → 100644
LIBUSB_DIR=${CURDIR}/deps/libusb
PDLIBBUILDER_DIR=${CURDIR}/deps/pd-lib-builder
cflags += -I${LIBUSB_DIR}
ldflags += -L${LIBUSB_DIR}/libusb -lusb
export PDDIR
export PDLIBBUILDER_DIR
export LIBUSB_DIR
export cflags
export ldflags
all: libusb
$(MAKE) -C src/hid
install:
$(MAKE) -C src/hid install
clean:
$(MAKE) -C src/hid clean
$(MAKE) -C ${LIBUSB_DIR} cleanpd
libusb: ${LIBUSB_DIR}/libusb/.libs/libusb-1.0.a
${LIBUSB_DIR}/libusb/libusb-1.0.la: ${LIBUSB_DIR}/Makefile
cd ${LIBUSB_DIR}; make
${LIBUSB_DIR}/Makefile: ${LIBUSB_DIR}/configure
cd ${LIBUSB_DIR}; ./configure
${LIBUSB_DIR}/configure:
cd ${LIBUSB_DIR}; ./autogen.sh
hidapi @ 0ea5e950
Subproject commit 0ea5e9502f31a1e4dbc71567c0f9a781930f1d38
Subproject commit bda2344f5e7d460decd3968492faa7a5fb2797cd
pd-lib-builder @ e6cff665
Subproject commit e6cff665a3a30a967c72c382c6fe92bcdedd7b44
# Makefile to build class 'counter' for Pure Data.
# Needs Makefile.pdlibbuilder as helper makefile for platform-dependent build
# settings and rules.
# library name
lib.name = hid
# input source file (class name == source file basename)
class.sources = hid.c
# all extra files to be included in binary distribution of the library
datafiles =
# include Makefile.pdlibbuilder from submodule directory 'pd-lib-builder'
include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
#include "m_pd.h"
#include "libusb/libusb.h"
#include <stdlib.h>
#include <string.h>
#define UNUSED(x) (void)(x)
static t_class *hid_class;
typedef struct {
t_object x_obj;
libusb_context * context;
// t_inlet *in;
t_outlet *out;
// t_outlet *dbg_out;
// bool runningStatusEnabled;
// uint8_t runningStatusState;
} hid_t;
typedef struct {
libusb_device * dev;
struct libusb_device_descriptor desc;
// struct libusb_config_descriptor *config;
uint8_t interface_num;
uint8_t report_desc[];
#define hid_usage_page report_desc[1]
#define hid_usage report_desc[3]
} hid_device_t;
void hid_setup(void);
static void * hid_new();
static void hid_free(hid_t *hid);
static void hid_anything(hid_t *hid, t_symbol *s, int argc, t_atom *argv);
//void midimessage_gen_anything(t_midimessage_gen *self, t_symbol *s, int argc, t_atom *argv);
//void midimessage_gen_generatorError(int code, uint8_t argc, uint8_t ** argv);
static void hid_cmd_list(hid_t *hid, t_symbol *s, int argc, t_atom *argv);
/**
* define the function-space of the class
*/
void hid_setup(void) {
hid_class = class_new(gensym("hid"),
(t_newmethod)hid_new,
(t_method)hid_free,
sizeof(hid_t),
CLASS_DEFAULT,
0);
/* call a function when object gets banged */
// class_addbang(midimessage_gen_class, midimessage_gen_bang);
// class_addmethod(hid_class,
// (t_method)hid_list
// , gensym("list"), 0);
// class_addmethod(midi)
class_addlist(hid_class, hid_anything);
class_addanything(hid_class, hid_anything);
}
static void *hid_new()
{
hid_t *hid = (hid_t *)pd_new(hid_class);
int r = libusb_init(&hid->context);
if (r < 0){
error("failed to init libusb: %d", r);
return NULL;
}
// generic outlet
hid->out = outlet_new(&hid->x_obj, 0);
// self->in = inlet_new(&self->x_obj, &self->x_obj.ob_pd,
// gensym(""), gensym("list"));
/* create a new outlet for floating-point values */
// self->byte_out = outlet_new(&self->x_obj, &s_float);
// self->dbg_out = outlet_new(&self->x_obj, &s_symbol);
// self->runningStatusEnabled = false;
// self->runningStatusState = MidiMessage_RunningStatusNotSet;
return hid;
}
static void hid_free(hid_t *hid)
{
libusb_exit(hid->context);
}
static void hid_anything(hid_t *hid, t_symbol *s, int argc, t_atom *argv)
{
if (s == gensym("list")){
hid_cmd_list(hid, s, argc, argv);
} else {
error("not a recognized command");
}
}
static int hid_filter_device_list(libusb_device **devs, ssize_t count, hid_device_t ***hiddevs, uint16_t vendor, uint16_t product, char * serial, uint8_t usage_page, uint8_t usage)
{
libusb_device *dev;
int i = 0, o = 0;
// let's assume that there will be fewer HID devices (interfaces) than original usb devices...
*hiddevs = calloc(1+count,sizeof(hid_device_t));
if (!*hiddevs){
error("failed to allocate memory for HID devices list");
return -1;
}
while ((dev = devs[i++]) != NULL) {
struct libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(dev, &desc);
if (r < 0) {
error("failed to get device descriptor");
continue;
}
// actually this only works for simple devices...
if (desc.bDeviceClass != 0 || desc.bDeviceSubClass != 0){
continue;
}
// if filter bby vendor/product id
if ((vendor != 0 && vendor != desc.idVendor) ||
(product != 0 && product != desc.idProduct)){
continue;
}
struct libusb_config_descriptor *config;
r = libusb_get_active_config_descriptor(dev, &config);
if (r < 0){
libusb_get_config_descriptor(dev, 0, &config);
}
if (r < 0){
error("Failed to get config descriptor: %d", r);
continue;
}
for(int k = 0; k < config->bNumInterfaces; k++) {
// printf("\t if %d / num_altsetting = %d\n", k, config->interface[k].num_altsetting);
for (int l = 0; l < config->interface[k].num_altsetting; l++) {
if (config->interface[k].altsetting[l].bInterfaceClass != LIBUSB_CLASS_HID){
continue;
}
libusb_device_handle *handle;
r = libusb_open(dev, &handle);
if (r < 0){
error("failed to open dev: %d", r);
continue;
}
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])) {
// 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 {
hid_device_t * hidDevice = malloc(sizeof(hid_device_t) + r);
hidDevice->dev = dev;
libusb_ref_device(dev); // increase reference count
memcpy(&hidDevice->desc, &desc, sizeof(struct libusb_device_descriptor));
// dev->config = NULL;
hidDevice->interface_num = interface_num;
memcpy(hidDevice->report_desc, report_desc, r);
(*hiddevs)[o++] = hidDevice;
post("usage (page) = %d (%d)", report_desc[3], report_desc[1]);
}
libusb_close(handle);
}
}
libusb_free_config_descriptor(config);
}
// if not found any devices free list;
if (o == 0){
free(*hiddevs);
*hiddevs = NULL;
}
return o;
}
static void hid_free_device_list(hid_device_t ** hiddevs)
{
if (!hiddevs){
return;
}
hid_device_t *dev;
int i = 0;
while( (dev = hiddevs[i++]) != NULL){
libusb_unref_device(dev->dev);
// if (dev->config != NULL) {
// libusb_free_config_descriptor(dev->config);
// }
free(dev);
}
}
static void hid_cmd_list(hid_t *hid, t_symbol *s, int argc, t_atom *argv)
{
UNUSED(hid);
UNUSED(s);
UNUSED(argc);
UNUSED(argv);
post("list");
uint16_t vendor = 0;
uint16_t product = 0;
char * serial = NULL;
uint8_t usage_page = 0;
uint8_t usage = 0;
libusb_device **devs;
ssize_t cnt;
cnt = libusb_get_device_list(NULL, &devs);
if (cnt < 0){
error("Failed to get device list: %zd", cnt);
return;
}
hid_device_t ** hiddevs;
cnt = hid_filter_device_list(devs, cnt, &hiddevs, vendor, product, serial, usage_page, usage);
libusb_free_device_list(devs, 1);
if (cnt < 0){
error("Error during filter operation: %zd", cnt);
return;
}
post("list all devices now: %d", cnt);
for(int i = 0; i < cnt; i++){
int orgc = 4;
t_atom orgv[5];
SETFLOAT(orgv, hiddevs[i]->desc.idVendor);
SETFLOAT(orgv+1, hiddevs[i]->desc.idProduct);
SETFLOAT(orgv+2, hiddevs[i]->hid_usage_page);
SETFLOAT(orgv+4, hiddevs[i]->hid_usage);
outlet_anything(hid->out, gensym("dev"), orgc, orgv);
}
hid_free_device_list(hiddevs);
}
\ No newline at end of file
File added
File added
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment