Last active
November 21, 2024 01:10
-
-
Save gretel/965f183a40c2115bf998b1db4dc2e4d9 to your computer and use it in GitHub Desktop.
control 'cm108' based radio interfaces on openbsd (inspired by https://github.com/twilly/cm108)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* CM108/CM119 GPIO control for OpenBSD | |
* For PTT control of USB audio devices via UHID driver | |
*/ | |
#include <sys/types.h> | |
#include <sys/ioctl.h> | |
#include <sys/stat.h> | |
#include <dev/usb/usb.h> | |
#include <dev/usb/usbhid.h> | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <err.h> | |
/* CM108 Constants */ | |
#define CMEDIA_VID 0x0d8c | |
#define CM108_PID1_MIN 0x0008 | |
#define CM108_PID1_MAX 0x000f | |
/* Report size for GPIO control */ | |
#define REPORT_SIZE 4 | |
#define DEBUG 1 | |
static int | |
cm108_write(const char *device, int gpio_num, int state) | |
{ | |
int fd; | |
if (DEBUG) | |
fprintf(stderr, "Opening %s...\n", device); | |
/* Open device */ | |
fd = open(device, O_RDWR | O_NONBLOCK); | |
if (fd < 0) { | |
fprintf(stderr, "Cannot open %s: %s\n", device, strerror(errno)); | |
return -1; | |
} | |
/* Get device info */ | |
struct usb_device_info di; | |
if (ioctl(fd, USB_GET_DEVICEINFO, &di) != -1 && DEBUG) { | |
fprintf(stderr, "Device info:\n"); | |
fprintf(stderr, " Vendor: 0x%04x\n", di.udi_vendorNo); | |
fprintf(stderr, " Product: 0x%04x\n", di.udi_productNo); | |
fprintf(stderr, " Release: 0x%04x\n", di.udi_releaseNo); | |
fprintf(stderr, " Class: 0x%02x\n", di.udi_class); | |
fprintf(stderr, " Product: %s\n", di.udi_product); | |
fprintf(stderr, " Vendor: %s\n", di.udi_vendor); | |
} | |
/* Get report size info */ | |
struct usb_ctl_report_desc desc_info; | |
memset(&desc_info, 0, sizeof(desc_info)); | |
if (ioctl(fd, USB_GET_REPORT_DESC, &desc_info) != -1 && DEBUG) { | |
fprintf(stderr, "Report descriptor size: %d\n", desc_info.ucrd_size); | |
} | |
/* Get report ID */ | |
int report_id = 0; | |
if (ioctl(fd, USB_GET_REPORT_ID, &report_id) != -1 && DEBUG) { | |
fprintf(stderr, "Report ID: %d\n", report_id); | |
} | |
/* Prepare GPIO data */ | |
unsigned char data[REPORT_SIZE] = {0}; | |
data[0] = 0; /* Pad */ | |
data[1] = 1 << (gpio_num - 1); /* GPIO direction (output) */ | |
data[2] = state << (gpio_num - 1); /* GPIO value */ | |
data[3] = 0; /* Pad */ | |
if (DEBUG) { | |
fprintf(stderr, "Sending report data: "); | |
for(int i = 0; i < REPORT_SIZE; i++) | |
fprintf(stderr, "%02x ", data[i]); | |
fprintf(stderr, "\n"); | |
} | |
/* Try direct write first */ | |
ssize_t res = write(fd, data, REPORT_SIZE); | |
if (res < 0) { | |
fprintf(stderr, "Write failed: %s\n", strerror(errno)); | |
/* Fall back to SET_REPORT */ | |
struct usb_ctl_report ucr; | |
memset(&ucr, 0, sizeof(ucr)); | |
ucr.ucr_report = UHID_OUTPUT_REPORT; | |
memcpy(&ucr.ucr_data, data, sizeof(data)); | |
if (ioctl(fd, USB_SET_REPORT, &ucr) < 0) { | |
/* Try HID Get/Set Feature as last resort */ | |
struct usb_ctl_report feature; | |
memset(&feature, 0, sizeof(feature)); | |
feature.ucr_report = UHID_FEATURE_REPORT; | |
/* Try to get current state first */ | |
if (ioctl(fd, USB_GET_REPORT, &feature) < 0) { | |
fprintf(stderr, "USB_GET_REPORT failed: %s\n", strerror(errno)); | |
} else { | |
/* Update only our bits and write back */ | |
feature.ucr_data[1] = data[1]; /* direction */ | |
feature.ucr_data[2] = data[2]; /* value */ | |
if (ioctl(fd, USB_SET_REPORT, &feature) < 0) { | |
fprintf(stderr, "USB_SET_REPORT failed: %s (errno=%d)\n", | |
strerror(errno), errno); | |
close(fd); | |
return -1; | |
} | |
} | |
} | |
} | |
if (DEBUG) | |
fprintf(stderr, "Report sent successfully\n"); | |
close(fd); | |
return 0; | |
} | |
static void | |
list_devices(void) | |
{ | |
char path[32]; | |
struct stat st; | |
int fd; | |
printf("Scanning for USB devices:\n"); | |
/* Check uhid devices */ | |
for (int i = 0; i < 16; i++) { | |
snprintf(path, sizeof(path), "/dev/uhid%d", i); | |
if (stat(path, &st) == 0) { | |
printf(" %s exists (mode=%03o)\n", path, st.st_mode & 0777); | |
fd = open(path, O_RDWR | O_NONBLOCK); | |
if (fd != -1) { | |
struct usb_device_info di; | |
if (ioctl(fd, USB_GET_DEVICEINFO, &di) != -1) { | |
printf(" vid=0x%04x pid=0x%04x %s %s%s\n", | |
di.udi_vendorNo, di.udi_productNo, | |
di.udi_vendor, di.udi_product, | |
(di.udi_vendorNo == CMEDIA_VID && | |
di.udi_productNo >= CM108_PID1_MIN && | |
di.udi_productNo <= CM108_PID1_MAX) ? " [CM108]" : ""); | |
} | |
close(fd); | |
} else { | |
printf(" cannot open: %s\n", strerror(errno)); | |
} | |
} | |
} | |
} | |
void | |
usage(void) | |
{ | |
fprintf(stderr, "usage: cm108 [-l] [-d device -p pin -s state]\n"); | |
fprintf(stderr, " -l: list compatible devices\n"); | |
fprintf(stderr, " -d device: device path (default: /dev/uhid0)\n"); | |
fprintf(stderr, " -p pin: GPIO pin number (1-8)\n"); | |
fprintf(stderr, " -s state: GPIO state (0 or 1)\n"); | |
exit(1); | |
} | |
int | |
main(int argc, char *argv[]) | |
{ | |
char *device = "/dev/uhid0"; /* default to first device */ | |
int ch, pin = 0, state = -1; | |
int list = 0; | |
/* Check if root */ | |
if (geteuid() != 0) | |
errx(1, "must be run as superuser"); | |
while ((ch = getopt(argc, argv, "ld:p:s:h")) != -1) { | |
switch (ch) { | |
case 'l': | |
list = 1; | |
break; | |
case 'd': | |
device = optarg; | |
break; | |
case 'p': | |
pin = atoi(optarg); | |
break; | |
case 's': | |
state = atoi(optarg); | |
break; | |
case 'h': | |
default: | |
usage(); | |
} | |
} | |
if (list) { | |
list_devices(); | |
return 0; | |
} | |
if (pin < 1 || pin > 8 || state < 0 || state > 1) { | |
usage(); | |
} | |
return cm108_write(device, pin, state); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# CM108 GPIO control utility for OpenBSD | |
PROG= cm108 | |
SRCS= cm108.c | |
MAN= # empty, no manpage yet | |
BINDIR= /usr/local/bin | |
BINOWN= root | |
BINGRP= bin | |
BINMODE= 555 | |
CFLAGS+= -Wall -Wextra | |
LDADD= | |
DPADD= | |
.include <bsd.prog.mk> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment