Cleaned up code:
/ *
* Set SPDIFOUT - program to control the SPDIF-output on a CM6206 chip
*
* Based on: Set LED - program to control a USB LED device
* from user space using libusb (Writing a Real Driver—In User Space | Linux Journal)
*
* Copyright (C) 2004
* Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can
* redistribute it and/or modify it under the terms
* of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the
* License.
*
*/
// https://www.linuxjournal.com/article/7466
#include <stdio.h>
#include <string.h>
#include <linux/types.h>
#include <usb.h>
//Id's for the C-Media Electronics, Inc. CM106 Like Sound Device
// 0d8c:0102
#define LED_VENDOR_ID 0x0d8c
#define LED_PRODUCT_ID 0x0102
#define BIT(nr) (1UL << (nr))
/*
* CM6206 registers from the CM6206 datasheet rev 2.1
*/
#define CM6206_REG0_DMA_MASTER BIT(15)
#define CM6206_REG0_SPDIFO_RATE_48K (2 << 12)
#define CM6206_REG0_SPDIFO_RATE_96K (7 << 12)
/* Bit 4 thru 11 is the S/PDIF category code */
#define CM6206_REG0_SPDIFO_CAT_CODE_GENERAL (0 << 4)
#define CM6206_REG0_SPDIFO_EMPHASIS_CD BIT(3)
#define CM6206_REG0_SPDIFO_COPYRIGHT_NA BIT(2)
#define CM6206_REG0_SPDIFO_NON_AUDIO BIT(1)
#define CM6206_REG0_SPDIFO_PRO_FORMAT BIT(0)
#define CM6206_REG1_TEST_SEL_CLK BIT(14)
#define CM6206_REG1_PLLBIN_EN BIT(13)
#define CM6206_REG1_SOFT_MUTE_EN BIT(12)
#define CM6206_REG1_GPIO4_OUT BIT(11)
#define CM6206_REG1_GPIO4_OE BIT(10)
#define CM6206_REG1_GPIO3_OUT BIT(9)
#define CM6206_REG1_GPIO3_OE BIT(8)
#define CM6206_REG1_GPIO2_OUT BIT(7)
#define CM6206_REG1_GPIO2_OE BIT(6)
#define CM6206_REG1_GPIO1_OUT BIT(5)
#define CM6206_REG1_GPIO1_OE BIT(4)
#define CM6206_REG1_SPDIFO_INVALID BIT(3)
#define CM6206_REG1_SPDIF_LOOP_EN BIT(2)
#define CM6206_REG1_SPDIFO_DIS BIT(1)
#define CM6206_REG1_SPDIFI_MIX BIT(0)
#define CM6206_REG2_DRIVER_ON BIT(15)
#define CM6206_REG2_HEADP_SEL_SIDE_CHANNELS (0 << 13)
#define CM6206_REG2_HEADP_SEL_SURROUND_CHANNELS (1 << 13)
#define CM6206_REG2_HEADP_SEL_CENTER_SUBW (2 << 13)
#define CM6206_REG2_HEADP_SEL_FRONT_CHANNELS (3 << 13)
#define CM6206_REG2_MUTE_HEADPHONE_RIGHT BIT(12)
#define CM6206_REG2_MUTE_HEADPHONE_LEFT BIT(11)
#define CM6206_REG2_MUTE_REAR_SURROUND_RIGHT BIT(10)
#define CM6206_REG2_MUTE_REAR_SURROUND_LEFT BIT(9)
#define CM6206_REG2_MUTE_SIDE_SURROUND_RIGHT BIT(8)
#define CM6206_REG2_MUTE_SIDE_SURROUND_LEFT BIT(7)
#define CM6206_REG2_MUTE_SUBWOOFER BIT(6)
#define CM6206_REG2_MUTE_CENTER BIT(5)
#define CM6206_REG2_MUTE_RIGHT_FRONT BIT(4)
#define CM6206_REG2_MUTE_LEFT_FRONT BIT(3)
#define CM6206_REG2_EN_BTL BIT(2)
#define CM6206_REG2_MCUCLKSEL_1_5_MHZ (0)
#define CM6206_REG2_MCUCLKSEL_3_MHZ (1)
#define CM6206_REG2_MCUCLKSEL_6_MHZ (2)
#define CM6206_REG2_MCUCLKSEL_12_MHZ (3)
/* Bit 11..13 sets the sensitivity to FLY tuner volume control VP/VD signal */
#define CM6206_REG3_FLYSPEED_DEFAULT (2 << 11)
#define CM6206_REG3_VRAP25EN BIT(10)
#define CM6206_REG3_MSEL1 BIT(9)
#define CM6206_REG3_SPDIFI_RATE_44_1K BIT(0 << 7)
#define CM6206_REG3_SPDIFI_RATE_48K BIT(2 << 7)
#define CM6206_REG3_SPDIFI_RATE_32K BIT(3 << 7)
#define CM6206_REG3_PINSEL BIT(6)
#define CM6206_REG3_FOE BIT(5)
#define CM6206_REG3_ROE BIT(4)
#define CM6206_REG3_CBOE BIT(3)
#define CM6206_REG3_LOSE BIT(2)
#define CM6206_REG3_HPOE BIT(1)
#define CM6206_REG3_SPDIFI_CANREC BIT(0)
#define CM6206_REG5_DA_RSTN BIT(13)
#define CM6206_REG5_AD_RSTN BIT(12)
#define CM6206_REG5_SPDIFO_AD2SPDO BIT(12)
#define CM6206_REG5_SPDIFO_SEL_FRONT (0 << 9)
#define CM6206_REG5_SPDIFO_SEL_SIDE_SUR (1 << 9)
#define CM6206_REG5_SPDIFO_SEL_CEN_LFE (2 << 9)
#define CM6206_REG5_SPDIFO_SEL_REAR_SUR (3 << 9)
#define CM6206_REG5_CODECM BIT(8)
#define CM6206_REG5_EN_HPF BIT(7)
#define CM6206_REG5_T_SEL_DSDA4 BIT(6)
#define CM6206_REG5_T_SEL_DSDA3 BIT(5)
#define CM6206_REG5_T_SEL_DSDA2 BIT(4)
#define CM6206_REG5_T_SEL_DSDA1 BIT(3)
#define CM6206_REG5_T_SEL_DSDAD_NORMAL 0
#define CM6206_REG5_T_SEL_DSDAD_FRONT 4
#define CM6206_REG5_T_SEL_DSDAD_S_SURROUND 5
#define CM6206_REG5_T_SEL_DSDAD_CEN_LFE 6
#define CM6206_REG5_T_SEL_DSDAD_R_SURROUND 7
// Sundry USB defines
#define USB_DIR_OUT 0 /* to device */
#define USB_REQ_SET_CONFIGURATION 0x09
#define USB_TYPE_CLASS (0x01 << 5)
#define USB_RECIP_ENDPOINT 0x02
static struct usb_device *device_init(void)
{
struct usb_bus *usb_bus;
struct usb_device *dev;
usb_init();
usb_find_busses();
usb_find_devices();
for (usb_bus = usb_busses;
usb_bus;
usb_bus = usb_bus->next) {
for (dev = usb_bus->devices;
dev;
dev = dev->next) {
if ((dev->descriptor.idVendor
== LED_VENDOR_ID) &&
(dev->descriptor.idProduct
== LED_PRODUCT_ID))
return dev;
}
}
return NULL;
}
/*
* C-Media CM106/CM106+ have four 16-bit internal registers that are nicely
* documented in the device's data sheet.
*/
static int snd_usb_cm106_write_int_reg(usb_dev_handle *handle, int reg, __u16 value)
{
char buf[4];
buf[0] = 0x20;
buf[1] = value & 0xff;
buf[2] = (value >> 8) & 0xff;
buf[3] = reg;
//https://sourceforge.net/p/libusb-win32/wiki/Documentation/
return usb_control_msg(handle, // handle
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, // Request type
USB_REQ_SET_CONFIGURATION, // Request
0, // value
0, // index
buf, // payload
4,
5000); // length
}
static int snd_usb_cm106_read_int_reg(usb_dev_handle *handle, int reg, __u16 value)
{
char buf[4];
buf[0] = 0x30;
buf[1] = value & 0xff;
buf[2] = (value >> 8) & 0xff;
buf[3] = reg;
//https://sourceforge.net/p/libusb-win32/wiki/Documentation/
return usb_control_msg(handle, // handle
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, // Request type
USB_REQ_SET_CONFIGURATION, // Request
0, // value
0, // index
buf, // payload
4,
5000); // length
}
int main(int argc, char **argv)
{
struct usb_device *usb_dev;
struct usb_dev_handle *usb_handle;
int retval = 1;
int i, rc, sel = 0;
int val;
char b[4];
usb_set_debug(4);
if(argc < 2)
{
fprintf(stderr, "Set CM6206 SPDIFOUT. Usage: setspdif n, where n is one of 0 - front, 1 - side surround, 2 - center, 3 - rear surround\n");
return 0;
}
usb_dev = device_init();
if (usb_dev == NULL) {
fprintf(stderr, "CM6206 Device not found\n");
goto exit;
}
usb_handle = usb_open(usb_dev);
if (usb_handle == NULL) {
fprintf(stderr, "Cannot open CM6206\n");
goto exit;
}
usb_handle = usb_open(usb_dev);
if (usb_handle == NULL) {
fprintf(stderr,
"Not able to claim the CM6206 USB device\n");
goto exit;
}
if( argc == 2) sel = atoi(argv[1]);
sel = sel << 9;
fprintf(stderr, "Selector is %X\n", sel);
/* REG5: de-assert AD/DA reset signals */
val = CM6206_REG5_DA_RSTN | CM6206_REG5_AD_RSTN | sel;// | CM6206_REG5_SPDIFO_SEL_SIDE_SUR; // | CM6206_REG5_SPDIFO_SEL_CEN_LFE;
rc = snd_usb_cm106_write_int_reg(usb_handle, 5, val);
fprintf(stderr,
"Wrote %X to register 5, rc = %d\n", val, rc);
retval = 0;
exit:
usb_close(usb_handle);
return retval;
}