tags:

views:

983

answers:

2

The CM108 from C-Media has 4 GPIO pin that you can access via a hid interface.

Using the generic write function in Windows I was able to write to the gpio pins.

However I'm trying to do the same thing in Linux without success.

The linux kernel detect the device as a hidraw device.

Note: I was able to read from the device, just not write. (I've run the app as root just to make sure it wasn't a permission issue).

A: 

Most hardware in Linux is accessible as a file. If the driver created a hardware node for it on the file-system, you're in luck. You will be able to write to it using regular file routines. Otherwise, you may need to do some assembly magic, which may require you to write a kernel module to do it.

sybreon
Correct, I'm opening the device with the open system call, and then writing to it using the write system call. I'm connecting the the /dev/hidraw device.
SjB
I've been monitoring the usb bus using "Sniffer for USB (on win32)" and the kernel debug in linux. I can see that when from the Packets that I'm not talking to the correct Interface on Linux. Windows send the message to Interface 2 and linux to interface 0. The weird thing is that Linux reports the HID being on Interface 2 (using lsusb -v)
SjB
So, I guess that you've isolated the problem. I'm guessing that the standard `fopen` defaults to opening interface 0. Sometimes, a USB device creates multiple /dev entries. You may want to create a new question to ask about this specifically - accessing other interfaces.
sybreon
+1  A: 

I got this working, here's how.

I needed to create a new linux hid kernel mod. (it wasn't that hard)/*

/*
 * Driver for the C-Media 108 chips
 *
 * Copyright (C) 2009 Steve Beaulac <[email protected]>
 *
 * 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.
 */

/*
 * This driver is based on the cm109.c driver
 */

#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>

#define DRIVER_VERSION "20090526"
#define DRIVER_AUTHOR "Steve Beaulac"
#define DRIVER_DESC "C-Media 108 chip"

#define CM108_VENDOR_ID 0x0d8c
#define CM108_PRODUCT_ID 0x000c

#ifdef CONFIG_USB_DYNAMIC_MINORS
#define CM108_MINOR_BASE 0
#else
#define CM108_MINOR_BASE 96
#endif

/*
 * Linux interface and usb initialisation
 */

static int cm108_hid_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
    int ret;

    ret = hid_parse(hdev);
    if (ret) {
     dev_err(&hdev->dev, "parse failed\n");
     goto error;
    }

    ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
    if (ret) {
     dev_err(&hdev->dev, "hw start failed\n");
     goto error;
    }
    return 0;

error:
    return ret;
}

static struct hid_device_id cm108_device_table[] = {
    { HID_USB_DEVICE (CM108_VENDOR_ID, CM108_PRODUCT_ID) },
    /* you can add more devices here with product ID 0x0008 - 0x000f */
    { }
};
MODULE_DEVICE_TABLE (hid, cm108_device_table);

static struct hid_driver hid_cm108_driver = {
    .name = "cm108",
    .id_table = cm108_device_table, 
    .probe = cm108_hid_probe,
};

static int hid_cm108_init(void)
{
    return hid_register_driver(&hid_cm108_driver);
}

static void hid_cm108_exit(void)
{
    hid_unregister_driver(&hid_cm108_driver); 
}

module_init(hid_cm108_init);
module_exit(hid_cm108_exit);

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

used This makefile

obj-m += cm108.o

and compile the module

make -C /lib/modules/`uname -r`/build/ M=`pwd` EXTRAVERSION="-generic" modules

sudo make -C /lib/modules/`uname -r`/build/ M=`pwd` EXTRAVERSION="-generic" modules_install

depmod -a

I had to modify the modules.order file so that my module would get queried before the generic hid linux module.

This modules make sure that the hidraw uses Interface 2.

Then I can use fopen to read and write to the GPIO pin of the CM108 chip.

BTW: when writing you need to write 5byte the 1st byte is used for the HID_OUTPUT_REPORT

SjB