Hello All.
currently we are trying to use an HD usb device. The problem is the following:
- The first time the (attached) slightly changed version of
test_libhid.c is run, there is no problem, the device is connecting
and the communication is possible. When the test programm is run for
the second time most of the time we are unable to connect to the
device. The only solution is unplugging / plugging the device.
Resetting the USB device using libusb also does not solve the problem.
Debug output of libhid and libusb does not give me any clue what is wrong.
When pressing control-c during the execution of the program we are
able to restart the programm without any problem multiple times.
Does anyone have a clue why the program is behaving this way ?
Currently I am clueless.
Kind regards,
Johan Uiterwijk Winkel.
#include <hid.h>
#include <stdio.h>
#include <string.h>
#define DEBUG
#ifdef DEBUG
#define TRACE printf
#else
#define TRACE(...)
#endif
#define PATHLEN 3
#define HID_EP 0x81
#define PRODUCT_ID 0x1101
#define VENDOR_ID 0x112A
int arrint_aHid_device_path[] = {0xffa00001, 0xffa00002, 0xffa10005}; // The Hid device_path
uint uint_pathlen = 3; ///< the number of fields of the file path to the HID
uint uint_hid_ep = 0x81; ///< the HID endpoint
int HID_INPUT_MAX_PACKET_SIZE = 19;
HIDInterface* hid;
hid_return ret;
bool match_serial_number(struct usb_dev_handle* usbdev, void* custom, unsigned int len)
{
bool ret;
char* buffer = (char*)malloc(len);
usb_get_string_simple(usbdev, usb_device(usbdev)->descriptor.iSerialNumber,
buffer, len);
ret = strncmp(buffer, (char*)custom, len) == 0;
free(buffer);
return ret;
}
int usbhidcontrol_read(char *ptr_char_data, int int_len, uint timeout)
{
hid_return ret; // libhid return variable used by libhid functions
int i;
TRACE("> %s --- %s \n",__FILE__,__FUNCTION__);
TRACE("-- %s --- %s int_len: %i \n",__FILE__,__FUNCTION__,int_len);
if (ptr_char_data == NULL)
{
TRACE("-- %s --- %s ptr_char_data == NULL, FATAL ERROR \n",__FILE__,__FUNCTION__);
return -1;
}
if (int_len <= 0)
{
TRACE("-- %s --- %s int_len <= 0, FATAL ERROR \n",__FILE__,__FUNCTION__);
return -1;
}
ret = hid_interrupt_read(hid, uint_hid_ep, ptr_char_data, HID_INPUT_MAX_PACKET_SIZE, timeout);
if (ret != 0)
{
TRACE("-- %s --- %s Error during hid_interrupt_read : %i \n",__FILE__,__FUNCTION__,ret);
return ret;
}
#ifdef DEBUG
TRACE("-- %s --- %s output :",__FILE__,__FUNCTION__);
for (i=0;i<int_len;i++)
{
TRACE("%02X ",(unsigned char)ptr_char_data[i]);
}
TRACE(" \n");
#endif
TRACE("-- %s --- %s ret: %i \n",__FILE__,__FUNCTION__,ret);
TRACE("< %s --- %s \n",__FILE__,__FUNCTION__);
return ret;
}
int usbhidcontrol_write(char *ptr_char_data, int int_len)
{
hid_return ret; // libhid return variable used by libhid functions
int i;
TRACE("> %s --- %s \n",__FILE__,__FUNCTION__);
if (ptr_char_data == NULL)
{
TRACE("-- %s --- %s ptr_char_data == NULL, FATAL ERROR \n",__FILE__,__FUNCTION__);
return -1;
}
if (int_len <= 0)
{
TRACE("-- %s --- %s int_len <= 0, FATAL ERROR \n",__FILE__,__FUNCTION__);
return -1;
}
#ifdef DEBUG
TRACE("-- %s --- %s input :",__FILE__,__FUNCTION__);
for (i=0;i<int_len;i++)
{
TRACE("%02X ",(unsigned char)ptr_char_data[i]);
}
TRACE(" \n");
#endif
ret = hid_set_output_report(hid, arrint_aHid_device_path, uint_pathlen, ptr_char_data, int_len);
if (ret != 0)
{
TRACE("-- %s --- %s Error during hid_set_output_report : %i \n",__FILE__,__FUNCTION__,ret);
return ret;
}
TRACE("-- %s --- %s ret: %i \n",__FILE__,__FUNCTION__,ret);
TRACE("< %s --- %s \n",__FILE__,__FUNCTION__);
return 0;
}
int main(void)
{
/* How to use a custom matcher function:
*
* The third member of the HIDInterfaceMatcher is a function pointer, and
* the forth member will be passed to it on invocation, together with the
* USB device handle. The fifth member holds the length of the buffer
* passed. See above. This can be used to do custom selection e.g. if you
* have multiple identical devices which differ in the serial number.
*
* char const* const serial = "01518";
* HIDInterfaceMatcher matcher = {
* 0x06c2, // vendor ID
* 0x0038, // product ID
* match_serial_number, // custom matcher function pointer
* (void*)serial, // custom matching data
* strlen(serial)+1 // length of custom data
* };
*
* If you do not want to use this, set the third member to NULL.
* Then the match will only be on vendor and product ID.
*/
int result;
int i;
char test_send_off [] = {0x06, 0xg1, 0xd2, 0x0b, 0x04, 0x34, 0x00, 0x00, 0x00, 0x00};
char test_send [400] = {0x00,};
HIDInterfaceMatcher matcher = {VENDOR_ID, PRODUCT_ID, NULL, NULL, 0};
/* see include/debug.h for possible values */
hid_set_debug(HID_DEBUG_ALL);
hid_set_debug_stream(stderr);
/* passed directly to libusb */
hid_set_usb_debug(0);
ret = hid_init();
if (ret != HID_RET_SUCCESS) {
fprintf(stderr, "hid_init failed with return code %d\n", ret);
return 1;
}
hid = hid_new_HIDInterface();
if (hid == 0) {
fprintf(stderr, "hid_new_HIDInterface() failed, out of memory?\n");
return 1;
}
/* How to detach a device from the kernel HID driver:
*
* The hid.o or usbhid.ko kernel modules claim a HID device on insertion,
* usually. To be able to use it with libhid, you need to blacklist the
* device (which requires a kernel recompilation), or simply tell libhid to
* detach it for you. hid_open just opens the device, hid_force_open will
* try n times to detach the device before failing.
* In the following, n == 3.
*
* To open the HID, you need permission to the file in the /proc usbfs
* (which must be mounted -- most distros do that by default):
* mount -t usbfs none /proc/bus/usb
* You can use hotplug to automatically give permissions to the device on
* connection. Please see
* http://cvs.ailab.ch/cgi-bin/viewcvs.cgi/external/libphidgets/hotplug/
* for an example. Try NOT to work as root!
*/
ret = hid_force_open(hid, 0, &matcher, 3);
if (ret != HID_RET_SUCCESS) {
fprintf(stderr, "hid_force_open failed with return code %d\n", ret);
return 1;
}
ret = hid_write_identification(stdout, hid);
if (ret != HID_RET_SUCCESS) {
fprintf(stderr, "hid_write_identification failed with return code %d\n", ret);
return 1;
}
ret = hid_dump_tree(stdout, hid);
if (ret != HID_RET_SUCCESS) {
fprintf(stderr, "hid_dump_tree failed with return code %d\n", ret);
return 1;
}
/* How to write to and read from a device:
*
* Writing to a device requires the HID usage path, a buffer, and the length
* of the latter. You must also know the exact length of a packet expected
* by the device, and the protocol to speak over HID.
*
* libhid uses the MGE hidparser, which parses the HID usage tree and places
* the available usages at its leafs (leaves?). The path information can be
* read from the `lsusb -vvv` output, or by inspecting the output of
* hid_dump_tree. In the output, 0x80 denotes an input endoint (sent by the
* device), and 0x90 an output endpoint (sent to the device). These are then
* used to communicate with the device.
*
* In the example of the Phidgets QuadServoController (www.phidgets.com, and
* libphidgets.alioth.debian.org), the following two paths identify the
* input and output descriptors respectively.
*
* unsigned char const PATHLEN = 3;
* int const PATH_IN[PATHLEN] = { 0xffa00001, 0xffa00002, 0xffa10003 };
* int const PATH_OUT[PATHLEN] = { 0xffa00001, 0xffa00002, 0xffa10004 };
*
* This is derived from the output of `lsusb -d 0x06c2:0x0038 -vvv` as
* follows. You need to run `libhid_detach_device 06c2:0038` before lsusb
* will output this info:
*
* Bus 001 Device 028: ID 06c2:0038 GLAB Chester 4-Motor PhidgetServo v3.0
* Device Descriptor:
* [...]
* Configuration Descriptor:
* [...]
* Interface Descriptor:
* [...]
* iInterface
* HID Device Descriptor:
* [...]
* Report Descriptor:
* [...]
* Item(Global): Usage Page, data= [ 0xa0 0xff ] 65440
* [...]
* Item(Local ): Usage, data= [ 0x01 ] 1
* [...]
*
* Item(Local ): Usage, data= [ 0x02 ] 2
* [...]
*
* Item(Global): Usage Page, data= [ 0xa1 0xff ] 65441
* [...]
*
* Item(Local ): Usage, data= [ 0x03 ] 3
* [...]
* Item(Main ): Input, data= [ 0x02 ] 2
* [...]
*
* Item(Local ): Usage, data= [ 0x04 ] 4
* [...]
* Item(Main ): Output, data= [ 0x02 ] 2
* [...]
*
* So working backwards,
* "Item(Main) Output" is usage 4 of usage page 65441,
* which is rooted at "Item(Local) ... 2" of usage page 65440,
* which is rooted at "Item(Local) ... 1" of usage page 65440
*
* A path component is 32 bits, the high 16 bits identify the usage page,
* and the low 16 bits the item number. Thus (now working forwards):
*
* 65440 << 16 + 1 -> 0xffa00001
* 65440 << 16 + 2 -> 0xffa00002
* 65441 << 16 + 4 -> 0xffa10004
*
* which gives the path the the output usage of the HID. The input usage may
* be found analogously.
*
* Now, to send 6 bytes:
*
* unsigned char const SEND_PACKET_LEN = 6;
* // fill an example packet:
* char const PACKET[SEND_PACKET_LEN] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5 };
*
* ret = hid_set_output_report(hid, PATH_IN, PATHLEN, PACKET, SEND_PACKET_LEN);
* if (ret != HID_RET_SUCCESS) {
* fprintf(stderr, "hid_set_output_report failed with return code %d\n", ret);
* }
*
* And reading works similarly:
* char packet[RECV_PACKET_LEN];
* ret = hid_get_input_report(hid, PATH_OUT, PATHLEN, packet, RECV_PACKET_LEN);
* if (ret != HID_RET_SUCCESS) {
* fprintf(stderr, "hid_get_input_report failed with return code %d\n", ret);
* }
* // now use the RECV_PACKET_LEN bytes starting at *packet.
*/
result = usbhidcontrol_write(test_send_off,6);
result = usbhidcontrol_read(test_send,19,4000);
result = usbhidcontrol_read(test_send,19,4000);
for (i=0;i<10;i++)
{
result = usbhidcontrol_read(test_send,19,4000);
}
ret = hid_close(hid);
if (ret != HID_RET_SUCCESS) {
fprintf(stderr, "hid_close failed with return code %d\n", ret);
return 1;
}
hid_delete_HIDInterface(&hid);
ret = hid_cleanup();
if (ret != HID_RET_SUCCESS) {
fprintf(stderr, "hid_cleanup failed with return code %d\n", ret);
return 1;
}
return 0;
}
/* COPYRIGHT --
*
* This file is part of libhid, a user-space HID access library.
* libhid is (c) 2003-2005
* Martin F. Krafft <[email protected]>
* Charles Lepple <[email protected]>
* Arnaud Quette <[email protected]> && <[email protected]>
* and distributed under the terms of the GNU General Public License.
* See the file ./COPYING in the source distribution for more information.
*
* THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES
* OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
_______________________________________________
libhid-discuss mailing list
[email protected]
http://lists.alioth.debian.org/mailman/listinfo/libhid-discuss
http://libhid.alioth.debian.org/