mirror of
https://github.com/raphnet/gc_n64_usb-v3
synced 2024-12-21 23:08:53 -05:00
Tool for configuration and special functions
This commit is contained in:
parent
3b935d12fc
commit
df95fd8856
26
tool/Makefile
Normal file
26
tool/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
CC=gcc
|
||||
LD=$(CC)
|
||||
|
||||
CFLAGS=-Wall -g `pkg-config hidapi-hidraw --cflags`
|
||||
LDFLAGS=`pkg-config hidapi-hidraw --libs`
|
||||
|
||||
PREFIX=/usr/local
|
||||
|
||||
PROG=gcn64ctl
|
||||
|
||||
OBJS=main.o gcn64.o mempak.o
|
||||
|
||||
.PHONY : clean install
|
||||
|
||||
all: $(OBJS)
|
||||
$(LD) $(LDFLAGS) $(OBJS) -o $(PROG)
|
||||
|
||||
%.o: %.c %.h
|
||||
$(CC) $(CFLAGS) -c $<
|
||||
|
||||
clean:
|
||||
rm -f *.o $(PROG)
|
||||
|
||||
install:
|
||||
@echo "Install not done yet. Sorry"
|
||||
|
26
tool/Makefile.mingw
Normal file
26
tool/Makefile.mingw
Normal file
@ -0,0 +1,26 @@
|
||||
CC=gcc
|
||||
LD=$(CC)
|
||||
|
||||
CFLAGS=-Wall -g
|
||||
LDFLAGS=-lsetupapi
|
||||
|
||||
PREFIX=/usr/local
|
||||
|
||||
PROG=wusbmote_ctl
|
||||
|
||||
OBJS=main.o wusbmote.o hid.o
|
||||
|
||||
.PHONY : clean install
|
||||
|
||||
all: $(OBJS)
|
||||
$(LD) $(OBJS) $(LDFLAGS) -o $(PROG)
|
||||
|
||||
%.o: %.c %.h
|
||||
$(CC) $(CFLAGS) -c $<
|
||||
|
||||
clean:
|
||||
rm -f *.o $(PROG)
|
||||
|
||||
install:
|
||||
@echo "Install not done yet. Sorry"
|
||||
|
4
tool/README
Normal file
4
tool/README
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
|
||||
libhidapi-dev
|
||||
pkg-config
|
219
tool/gcn64.c
Normal file
219
tool/gcn64.c
Normal file
@ -0,0 +1,219 @@
|
||||
/* wusbmote: Wiimote accessory to USB Adapter
|
||||
* Copyright (C) 2012-2014 Raphaël Assénat
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* The author may be contacted at raph@raphnet.net
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "gcn64.h"
|
||||
#include "gcn64_priv.h"
|
||||
#include "../requests.h"
|
||||
|
||||
#include "hidapi.h"
|
||||
|
||||
static int dusbr_verbose = 0;
|
||||
|
||||
|
||||
#define IS_VERBOSE() (dusbr_verbose)
|
||||
|
||||
int gcn64_init(int verbose)
|
||||
{
|
||||
dusbr_verbose = verbose;
|
||||
hid_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gcn64_shutdown(void)
|
||||
{
|
||||
hid_exit();
|
||||
}
|
||||
|
||||
static char isProductIdHandled(unsigned short pid, int interface_number)
|
||||
{
|
||||
switch (pid)
|
||||
{
|
||||
case 0x0017: // GC/N64 USB v3
|
||||
if (interface_number == 1)
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct gcn64_list_ctx *gcn64_allocListCtx(void)
|
||||
{
|
||||
struct gcn64_list_ctx *ctx;
|
||||
ctx = calloc(1, sizeof(struct gcn64_list_ctx));
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void gcn64_freeListCtx(struct gcn64_list_ctx *ctx)
|
||||
{
|
||||
if (ctx) {
|
||||
if (ctx->devs) {
|
||||
hid_free_enumeration(ctx->devs);
|
||||
}
|
||||
free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief List instances of our rgbleds device on the USB busses.
|
||||
* \param dst Destination buffer for device serial number/id.
|
||||
* \param dstbuf_size Destination buffer size.
|
||||
*/
|
||||
struct gcn64_info *gcn64_listDevices(struct gcn64_info *info, struct gcn64_list_ctx *ctx)
|
||||
{
|
||||
memset(info, 0, sizeof(struct gcn64_info));
|
||||
|
||||
if (!ctx) {
|
||||
fprintf(stderr, "gcn64_listDevices: Passed null context\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ctx->devs)
|
||||
goto jumpin;
|
||||
|
||||
if (IS_VERBOSE()) {
|
||||
printf("Start listing\n");
|
||||
}
|
||||
|
||||
ctx->devs = hid_enumerate(OUR_VENDOR_ID, 0x0000);
|
||||
if (!ctx->devs) {
|
||||
printf("Hid enumerate returned NULL\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (ctx->cur_dev = ctx->devs; ctx->cur_dev; ctx->cur_dev = ctx->cur_dev->next)
|
||||
{
|
||||
if (IS_VERBOSE()) {
|
||||
printf("Considering 0x%04x:0x%04x\n", ctx->cur_dev->vendor_id, ctx->cur_dev->product_id);
|
||||
}
|
||||
if (isProductIdHandled(ctx->cur_dev->product_id, ctx->cur_dev->interface_number))
|
||||
{
|
||||
wcsncpy(info->str_prodname, ctx->cur_dev->product_string, PRODNAME_MAXCHARS-1);
|
||||
wcsncpy(info->str_serial, ctx->cur_dev->serial_number, SERIAL_MAXCHARS-1);
|
||||
strncpy(info->str_path, ctx->cur_dev->path, PATH_MAXCHARS-1);
|
||||
return info;
|
||||
}
|
||||
|
||||
jumpin:
|
||||
// prevent 'error: label at end of compound statement'
|
||||
continue;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gcn64_hdl_t gcn64_openDevice(struct gcn64_info *dev)
|
||||
{
|
||||
hid_device *hdev;
|
||||
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
if (IS_VERBOSE()) {
|
||||
printf("Opening device path: '%s'\n", dev->str_path);
|
||||
}
|
||||
|
||||
hdev = hid_open_path(dev->str_path);
|
||||
if (!hdev)
|
||||
return NULL;
|
||||
|
||||
return hdev;
|
||||
}
|
||||
|
||||
void gcn64_closeDevice(gcn64_hdl_t hdl)
|
||||
{
|
||||
hid_device *hdev = (hid_device*)hdl;
|
||||
if (hdev) {
|
||||
hid_close(hdev);
|
||||
}
|
||||
}
|
||||
|
||||
int gcn64_send_cmd(gcn64_hdl_t hdl, const unsigned char *cmd, int cmdlen)
|
||||
{
|
||||
hid_device *hdev = (hid_device*)hdl;
|
||||
unsigned char buffer[cmdlen + 1];
|
||||
int n;
|
||||
|
||||
buffer[0] = 0x00; // report ID set to 0 (device has only one)
|
||||
memcpy(buffer + 1, cmd, cmdlen);
|
||||
|
||||
n = hid_send_feature_report(hdev, buffer, cmdlen+1);
|
||||
if (n < 0) {
|
||||
fprintf(stderr, "Could not send feature report (%ls)\n", hid_error(hdev));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gcn64_poll_result(gcn64_hdl_t hdl, unsigned char *cmd, int cmd_maxlen)
|
||||
{
|
||||
hid_device *hdev = (hid_device*)hdl;
|
||||
unsigned char buffer[cmd_maxlen + 1];
|
||||
int res_len;
|
||||
int n;
|
||||
|
||||
memset(buffer, 0, cmd_maxlen + 1);
|
||||
buffer[0] = 0x00; // report ID set to 0 (device has only one)
|
||||
|
||||
n = hid_get_feature_report(hdev, buffer, cmd_maxlen+1);
|
||||
if (n < 0) {
|
||||
fprintf(stderr, "Could not send feature report (%ls)\n", hid_error(hdev));
|
||||
return -1;
|
||||
}
|
||||
if (n==0) {
|
||||
return 0;
|
||||
}
|
||||
res_len = n-1;
|
||||
|
||||
if (res_len>0)
|
||||
memcpy(cmd, buffer+1, res_len);
|
||||
|
||||
return res_len;
|
||||
}
|
||||
|
||||
int gcn64_exchange(gcn64_hdl_t hdl, unsigned char *outcmd, int outlen, unsigned char *result, int result_max)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = gcn64_send_cmd(hdl, outcmd, outlen);
|
||||
if (n<0) {
|
||||
fprintf(stderr, "Error sending command\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Answer to the command comes later. For now, this is polled, but in
|
||||
* the future an interrupt-in transfer could be used. */
|
||||
do {
|
||||
n = gcn64_poll_result(hdl, result, result_max);
|
||||
if (n < 0) {
|
||||
fprintf(stderr, "Error\r\n");
|
||||
break;
|
||||
}
|
||||
if (n==0) {
|
||||
// printf("."); fflush(stdout);
|
||||
}
|
||||
|
||||
} while (n==0);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
40
tool/gcn64.h
Normal file
40
tool/gcn64.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef _gcn64_h__
|
||||
#define _gcn64_h__
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
#define OUR_VENDOR_ID 0x289b
|
||||
#define PRODNAME_MAXCHARS 256
|
||||
#define SERIAL_MAXCHARS 256
|
||||
#define PATH_MAXCHARS 256
|
||||
|
||||
struct gcn64_info {
|
||||
wchar_t str_prodname[PRODNAME_MAXCHARS];
|
||||
wchar_t str_serial[SERIAL_MAXCHARS];
|
||||
char str_path[PATH_MAXCHARS];
|
||||
int major, minor;
|
||||
int access; // True unless direct access to read serial/prodname failed due to permissions.
|
||||
};
|
||||
|
||||
struct gcn64_list_ctx;
|
||||
|
||||
typedef void* gcn64_hdl_t; // Cast from hid_device
|
||||
|
||||
int gcn64_init(int verbose);
|
||||
void gcn64_shutdown(void);
|
||||
|
||||
struct gcn64_list_ctx *gcn64_allocListCtx(void);
|
||||
void gcn64_freeListCtx(struct gcn64_list_ctx *ctx);
|
||||
struct gcn64_info *gcn64_listDevices(struct gcn64_info *info, struct gcn64_list_ctx *ctx);
|
||||
|
||||
gcn64_hdl_t gcn64_openDevice(struct gcn64_info *dev);
|
||||
void gcn64_closeDevice(gcn64_hdl_t hdl);
|
||||
|
||||
int gcn64_send_cmd(gcn64_hdl_t hdl, const unsigned char *cmd, int len);
|
||||
int gcn64_poll_result(gcn64_hdl_t hdl, unsigned char *cmd, int cmdlen);
|
||||
|
||||
int gcn64_exchange(gcn64_hdl_t hdl, unsigned char *outcmd, int outlen, unsigned char *result, int result_max);
|
||||
|
||||
|
||||
#endif // _gcn64_h__
|
||||
|
10
tool/gcn64_priv.h
Normal file
10
tool/gcn64_priv.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef _gcn64_priv_h__
|
||||
#define _gcn64_priv_h__
|
||||
|
||||
#include "hidapi.h"
|
||||
|
||||
struct gcn64_list_ctx {
|
||||
struct hid_device_info *devs, *cur_dev;
|
||||
};
|
||||
|
||||
#endif
|
923
tool/hid.c
Normal file
923
tool/hid.c
Normal file
@ -0,0 +1,923 @@
|
||||
/*******************************************************
|
||||
HIDAPI - Multi-Platform library for
|
||||
communication with HID devices.
|
||||
|
||||
Alan Ott
|
||||
Signal 11 Software
|
||||
|
||||
8/22/2009
|
||||
|
||||
Copyright 2009, All Rights Reserved.
|
||||
|
||||
At the discretion of the user of this library,
|
||||
this software may be licensed under the terms of the
|
||||
GNU General Public License v3, a BSD-Style license, or the
|
||||
original HIDAPI license as outlined in the LICENSE.txt,
|
||||
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
||||
files located at the root of the source distribution.
|
||||
These files may also be found in the public source
|
||||
code repository located at:
|
||||
http://github.com/signal11/hidapi .
|
||||
********************************************************/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef _NTDEF_
|
||||
typedef LONG NTSTATUS;
|
||||
#endif
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <ntdef.h>
|
||||
#include <winbase.h>
|
||||
#endif
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#include <ntdef.h>
|
||||
#define _wcsdup wcsdup
|
||||
#endif
|
||||
|
||||
/*#define HIDAPI_USE_DDK*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <setupapi.h>
|
||||
#include <winioctl.h>
|
||||
#ifdef HIDAPI_USE_DDK
|
||||
#include <hidsdi.h>
|
||||
#endif
|
||||
|
||||
/* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */
|
||||
#define HID_OUT_CTL_CODE(id) \
|
||||
CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
|
||||
#define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100)
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#include "hidapi.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* Thanks Microsoft, but I know how to use strncpy(). */
|
||||
#pragma warning(disable:4996)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef HIDAPI_USE_DDK
|
||||
/* Since we're not building with the DDK, and the HID header
|
||||
files aren't part of the SDK, we have to define all this
|
||||
stuff here. In lookup_functions(), the function pointers
|
||||
defined below are set. */
|
||||
typedef struct _HIDD_ATTRIBUTES{
|
||||
ULONG Size;
|
||||
USHORT VendorID;
|
||||
USHORT ProductID;
|
||||
USHORT VersionNumber;
|
||||
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
|
||||
|
||||
typedef USHORT USAGE;
|
||||
typedef struct _HIDP_CAPS {
|
||||
USAGE Usage;
|
||||
USAGE UsagePage;
|
||||
USHORT InputReportByteLength;
|
||||
USHORT OutputReportByteLength;
|
||||
USHORT FeatureReportByteLength;
|
||||
USHORT Reserved[17];
|
||||
USHORT fields_not_used_by_hidapi[10];
|
||||
} HIDP_CAPS, *PHIDP_CAPS;
|
||||
typedef void* PHIDP_PREPARSED_DATA;
|
||||
#define HIDP_STATUS_SUCCESS 0x110000
|
||||
|
||||
typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib);
|
||||
typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len);
|
||||
typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
|
||||
typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
|
||||
typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length);
|
||||
typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length);
|
||||
typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len);
|
||||
typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data);
|
||||
typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data);
|
||||
typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps);
|
||||
|
||||
static HidD_GetAttributes_ HidD_GetAttributes;
|
||||
static HidD_GetSerialNumberString_ HidD_GetSerialNumberString;
|
||||
static HidD_GetManufacturerString_ HidD_GetManufacturerString;
|
||||
static HidD_GetProductString_ HidD_GetProductString;
|
||||
static HidD_SetFeature_ HidD_SetFeature;
|
||||
static HidD_GetFeature_ HidD_GetFeature;
|
||||
static HidD_GetIndexedString_ HidD_GetIndexedString;
|
||||
static HidD_GetPreparsedData_ HidD_GetPreparsedData;
|
||||
static HidD_FreePreparsedData_ HidD_FreePreparsedData;
|
||||
static HidP_GetCaps_ HidP_GetCaps;
|
||||
|
||||
static HMODULE lib_handle = NULL;
|
||||
static BOOLEAN initialized = FALSE;
|
||||
#endif /* HIDAPI_USE_DDK */
|
||||
|
||||
struct hid_device_ {
|
||||
HANDLE device_handle;
|
||||
BOOL blocking;
|
||||
USHORT output_report_length;
|
||||
size_t input_report_length;
|
||||
void *last_error_str;
|
||||
DWORD last_error_num;
|
||||
BOOL read_pending;
|
||||
char *read_buf;
|
||||
OVERLAPPED ol;
|
||||
};
|
||||
|
||||
static hid_device *new_hid_device()
|
||||
{
|
||||
hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
|
||||
dev->device_handle = INVALID_HANDLE_VALUE;
|
||||
dev->blocking = TRUE;
|
||||
dev->output_report_length = 0;
|
||||
dev->input_report_length = 0;
|
||||
dev->last_error_str = NULL;
|
||||
dev->last_error_num = 0;
|
||||
dev->read_pending = FALSE;
|
||||
dev->read_buf = NULL;
|
||||
memset(&dev->ol, 0, sizeof(dev->ol));
|
||||
dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void free_hid_device(hid_device *dev)
|
||||
{
|
||||
CloseHandle(dev->ol.hEvent);
|
||||
CloseHandle(dev->device_handle);
|
||||
LocalFree(dev->last_error_str);
|
||||
free(dev->read_buf);
|
||||
free(dev);
|
||||
}
|
||||
|
||||
static void register_error(hid_device *device, const char *op)
|
||||
{
|
||||
WCHAR *ptr, *msg;
|
||||
|
||||
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPVOID)&msg, 0/*sz*/,
|
||||
NULL);
|
||||
|
||||
/* Get rid of the CR and LF that FormatMessage() sticks at the
|
||||
end of the message. Thanks Microsoft! */
|
||||
ptr = msg;
|
||||
while (*ptr) {
|
||||
if (*ptr == '\r') {
|
||||
*ptr = 0x0000;
|
||||
break;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
|
||||
/* Store the message off in the Device entry so that
|
||||
the hid_error() function can pick it up. */
|
||||
LocalFree(device->last_error_str);
|
||||
device->last_error_str = msg;
|
||||
}
|
||||
|
||||
#ifndef HIDAPI_USE_DDK
|
||||
static int lookup_functions()
|
||||
{
|
||||
lib_handle = LoadLibraryA("hid.dll");
|
||||
if (lib_handle) {
|
||||
#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1;
|
||||
RESOLVE(HidD_GetAttributes);
|
||||
RESOLVE(HidD_GetSerialNumberString);
|
||||
RESOLVE(HidD_GetManufacturerString);
|
||||
RESOLVE(HidD_GetProductString);
|
||||
RESOLVE(HidD_SetFeature);
|
||||
RESOLVE(HidD_GetFeature);
|
||||
RESOLVE(HidD_GetIndexedString);
|
||||
RESOLVE(HidD_GetPreparsedData);
|
||||
RESOLVE(HidD_FreePreparsedData);
|
||||
RESOLVE(HidP_GetCaps);
|
||||
#undef RESOLVE
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static HANDLE open_device(const char *path, BOOL enumerate)
|
||||
{
|
||||
HANDLE handle;
|
||||
DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ);
|
||||
DWORD share_mode = (enumerate)?
|
||||
FILE_SHARE_READ|FILE_SHARE_WRITE:
|
||||
FILE_SHARE_READ;
|
||||
|
||||
handle = CreateFileA(path,
|
||||
desired_access,
|
||||
share_mode,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/
|
||||
0);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_init(void)
|
||||
{
|
||||
#ifndef HIDAPI_USE_DDK
|
||||
if (!initialized) {
|
||||
if (lookup_functions() < 0) {
|
||||
hid_exit();
|
||||
return -1;
|
||||
}
|
||||
initialized = TRUE;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_exit(void)
|
||||
{
|
||||
#ifndef HIDAPI_USE_DDK
|
||||
if (lib_handle)
|
||||
FreeLibrary(lib_handle);
|
||||
lib_handle = NULL;
|
||||
initialized = FALSE;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
|
||||
{
|
||||
BOOL res;
|
||||
struct hid_device_info *root = NULL; /* return object */
|
||||
struct hid_device_info *cur_dev = NULL;
|
||||
|
||||
/* Windows objects for interacting with the driver. */
|
||||
GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} };
|
||||
SP_DEVINFO_DATA devinfo_data;
|
||||
SP_DEVICE_INTERFACE_DATA device_interface_data;
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL;
|
||||
HDEVINFO device_info_set = INVALID_HANDLE_VALUE;
|
||||
int device_index = 0;
|
||||
int i;
|
||||
|
||||
if (hid_init() < 0)
|
||||
return NULL;
|
||||
|
||||
/* Initialize the Windows objects. */
|
||||
memset(&devinfo_data, 0x0, sizeof(devinfo_data));
|
||||
devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
|
||||
device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
/* Get information for all the devices belonging to the HID class. */
|
||||
device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||
|
||||
/* Iterate over each device in the HID class, looking for the right one. */
|
||||
|
||||
for (;;) {
|
||||
HANDLE write_handle = INVALID_HANDLE_VALUE;
|
||||
DWORD required_size = 0;
|
||||
HIDD_ATTRIBUTES attrib;
|
||||
|
||||
res = SetupDiEnumDeviceInterfaces(device_info_set,
|
||||
NULL,
|
||||
&InterfaceClassGuid,
|
||||
device_index,
|
||||
&device_interface_data);
|
||||
|
||||
if (!res) {
|
||||
/* A return of FALSE from this function means that
|
||||
there are no more devices. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Call with 0-sized detail size, and let the function
|
||||
tell us how long the detail struct needs to be. The
|
||||
size is put in &required_size. */
|
||||
res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
|
||||
&device_interface_data,
|
||||
NULL,
|
||||
0,
|
||||
&required_size,
|
||||
NULL);
|
||||
|
||||
/* Allocate a long enough structure for device_interface_detail_data. */
|
||||
device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size);
|
||||
device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
|
||||
|
||||
/* Get the detailed data for this device. The detail data gives us
|
||||
the device path for this device, which is then passed into
|
||||
CreateFile() to get a handle to the device. */
|
||||
res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
|
||||
&device_interface_data,
|
||||
device_interface_detail_data,
|
||||
required_size,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
if (!res) {
|
||||
/* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail");
|
||||
Continue to the next device. */
|
||||
goto cont;
|
||||
}
|
||||
|
||||
/* Make sure this device is of Setup Class "HIDClass" and has a
|
||||
driver bound to it. */
|
||||
for (i = 0; ; i++) {
|
||||
char driver_name[256];
|
||||
|
||||
/* Populate devinfo_data. This function will return failure
|
||||
when there are no more interfaces left. */
|
||||
res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data);
|
||||
if (!res)
|
||||
goto cont;
|
||||
|
||||
res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
|
||||
SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
|
||||
if (!res)
|
||||
goto cont;
|
||||
|
||||
if (strcmp(driver_name, "HIDClass") == 0) {
|
||||
/* See if there's a driver bound. */
|
||||
res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
|
||||
SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
|
||||
if (res)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath);
|
||||
|
||||
/* Open a handle to the device */
|
||||
write_handle = open_device(device_interface_detail_data->DevicePath, TRUE);
|
||||
|
||||
/* Check validity of write_handle. */
|
||||
if (write_handle == INVALID_HANDLE_VALUE) {
|
||||
/* Unable to open the device. */
|
||||
//register_error(dev, "CreateFile");
|
||||
goto cont_close;
|
||||
}
|
||||
|
||||
|
||||
/* Get the Vendor ID and Product ID for this device. */
|
||||
attrib.Size = sizeof(HIDD_ATTRIBUTES);
|
||||
HidD_GetAttributes(write_handle, &attrib);
|
||||
//wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID);
|
||||
|
||||
/* Check the VID/PID to see if we should add this
|
||||
device to the enumeration list. */
|
||||
if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) &&
|
||||
(product_id == 0x0 || attrib.ProductID == product_id)) {
|
||||
|
||||
#define WSTR_LEN 512
|
||||
const char *str;
|
||||
struct hid_device_info *tmp;
|
||||
PHIDP_PREPARSED_DATA pp_data = NULL;
|
||||
HIDP_CAPS caps;
|
||||
BOOLEAN res;
|
||||
NTSTATUS nt_res;
|
||||
wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */
|
||||
size_t len;
|
||||
|
||||
/* VID/PID match. Create the record. */
|
||||
tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
|
||||
if (cur_dev) {
|
||||
cur_dev->next = tmp;
|
||||
}
|
||||
else {
|
||||
root = tmp;
|
||||
}
|
||||
cur_dev = tmp;
|
||||
|
||||
/* Get the Usage Page and Usage for this device. */
|
||||
res = HidD_GetPreparsedData(write_handle, &pp_data);
|
||||
if (res) {
|
||||
nt_res = HidP_GetCaps(pp_data, &caps);
|
||||
if (nt_res == HIDP_STATUS_SUCCESS) {
|
||||
cur_dev->usage_page = caps.UsagePage;
|
||||
cur_dev->usage = caps.Usage;
|
||||
}
|
||||
|
||||
HidD_FreePreparsedData(pp_data);
|
||||
}
|
||||
|
||||
/* Fill out the record */
|
||||
cur_dev->next = NULL;
|
||||
str = device_interface_detail_data->DevicePath;
|
||||
if (str) {
|
||||
len = strlen(str);
|
||||
cur_dev->path = (char*) calloc(len+1, sizeof(char));
|
||||
strncpy(cur_dev->path, str, len+1);
|
||||
cur_dev->path[len] = '\0';
|
||||
}
|
||||
else
|
||||
cur_dev->path = NULL;
|
||||
|
||||
/* Serial Number */
|
||||
res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr));
|
||||
wstr[WSTR_LEN-1] = 0x0000;
|
||||
if (res) {
|
||||
cur_dev->serial_number = _wcsdup(wstr);
|
||||
}
|
||||
|
||||
/* Manufacturer String */
|
||||
res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr));
|
||||
wstr[WSTR_LEN-1] = 0x0000;
|
||||
if (res) {
|
||||
cur_dev->manufacturer_string = _wcsdup(wstr);
|
||||
}
|
||||
|
||||
/* Product String */
|
||||
res = HidD_GetProductString(write_handle, wstr, sizeof(wstr));
|
||||
wstr[WSTR_LEN-1] = 0x0000;
|
||||
if (res) {
|
||||
cur_dev->product_string = _wcsdup(wstr);
|
||||
}
|
||||
|
||||
/* VID/PID */
|
||||
cur_dev->vendor_id = attrib.VendorID;
|
||||
cur_dev->product_id = attrib.ProductID;
|
||||
|
||||
/* Release Number */
|
||||
cur_dev->release_number = attrib.VersionNumber;
|
||||
|
||||
/* Interface Number. It can sometimes be parsed out of the path
|
||||
on Windows if a device has multiple interfaces. See
|
||||
http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or
|
||||
search for "Hardware IDs for HID Devices" at MSDN. If it's not
|
||||
in the path, it's set to -1. */
|
||||
cur_dev->interface_number = -1;
|
||||
if (cur_dev->path) {
|
||||
char *interface_component = strstr(cur_dev->path, "&mi_");
|
||||
if (interface_component) {
|
||||
char *hex_str = interface_component + 4;
|
||||
char *endptr = NULL;
|
||||
cur_dev->interface_number = strtol(hex_str, &endptr, 16);
|
||||
if (endptr == hex_str) {
|
||||
/* The parsing failed. Set interface_number to -1. */
|
||||
cur_dev->interface_number = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cont_close:
|
||||
CloseHandle(write_handle);
|
||||
cont:
|
||||
/* We no longer need the detail data. It can be freed */
|
||||
free(device_interface_detail_data);
|
||||
|
||||
device_index++;
|
||||
|
||||
}
|
||||
|
||||
/* Close the device information handle. */
|
||||
SetupDiDestroyDeviceInfoList(device_info_set);
|
||||
|
||||
return root;
|
||||
|
||||
}
|
||||
|
||||
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
|
||||
{
|
||||
/* TODO: Merge this with the Linux version. This function is platform-independent. */
|
||||
struct hid_device_info *d = devs;
|
||||
while (d) {
|
||||
struct hid_device_info *next = d->next;
|
||||
free(d->path);
|
||||
free(d->serial_number);
|
||||
free(d->manufacturer_string);
|
||||
free(d->product_string);
|
||||
free(d);
|
||||
d = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
|
||||
{
|
||||
/* TODO: Merge this functions with the Linux version. This function should be platform independent. */
|
||||
struct hid_device_info *devs, *cur_dev;
|
||||
const char *path_to_open = NULL;
|
||||
hid_device *handle = NULL;
|
||||
|
||||
devs = hid_enumerate(vendor_id, product_id);
|
||||
cur_dev = devs;
|
||||
while (cur_dev) {
|
||||
if (cur_dev->vendor_id == vendor_id &&
|
||||
cur_dev->product_id == product_id) {
|
||||
if (serial_number) {
|
||||
if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
|
||||
path_to_open = cur_dev->path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
path_to_open = cur_dev->path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cur_dev = cur_dev->next;
|
||||
}
|
||||
|
||||
if (path_to_open) {
|
||||
/* Open the device */
|
||||
handle = hid_open_path(path_to_open);
|
||||
}
|
||||
|
||||
hid_free_enumeration(devs);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
|
||||
{
|
||||
hid_device *dev;
|
||||
HIDP_CAPS caps;
|
||||
PHIDP_PREPARSED_DATA pp_data = NULL;
|
||||
BOOLEAN res;
|
||||
NTSTATUS nt_res;
|
||||
|
||||
if (hid_init() < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev = new_hid_device();
|
||||
|
||||
/* Open a handle to the device */
|
||||
dev->device_handle = open_device(path, FALSE);
|
||||
|
||||
/* Check validity of write_handle. */
|
||||
if (dev->device_handle == INVALID_HANDLE_VALUE) {
|
||||
/* Unable to open the device. */
|
||||
register_error(dev, "CreateFile");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Get the Input Report length for the device. */
|
||||
res = HidD_GetPreparsedData(dev->device_handle, &pp_data);
|
||||
if (!res) {
|
||||
register_error(dev, "HidD_GetPreparsedData");
|
||||
goto err;
|
||||
}
|
||||
nt_res = HidP_GetCaps(pp_data, &caps);
|
||||
if (nt_res != HIDP_STATUS_SUCCESS) {
|
||||
register_error(dev, "HidP_GetCaps");
|
||||
goto err_pp_data;
|
||||
}
|
||||
dev->output_report_length = caps.OutputReportByteLength;
|
||||
dev->input_report_length = caps.InputReportByteLength;
|
||||
HidD_FreePreparsedData(pp_data);
|
||||
|
||||
dev->read_buf = (char*) malloc(dev->input_report_length);
|
||||
|
||||
return dev;
|
||||
|
||||
err_pp_data:
|
||||
HidD_FreePreparsedData(pp_data);
|
||||
err:
|
||||
free_hid_device(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)
|
||||
{
|
||||
DWORD bytes_written;
|
||||
BOOL res;
|
||||
|
||||
OVERLAPPED ol;
|
||||
unsigned char *buf;
|
||||
memset(&ol, 0, sizeof(ol));
|
||||
|
||||
/* Make sure the right number of bytes are passed to WriteFile. Windows
|
||||
expects the number of bytes which are in the _longest_ report (plus
|
||||
one for the report number) bytes even if the data is a report
|
||||
which is shorter than that. Windows gives us this value in
|
||||
caps.OutputReportByteLength. If a user passes in fewer bytes than this,
|
||||
create a temporary buffer which is the proper size. */
|
||||
if (length >= dev->output_report_length) {
|
||||
/* The user passed the right number of bytes. Use the buffer as-is. */
|
||||
buf = (unsigned char *) data;
|
||||
} else {
|
||||
/* Create a temporary buffer and copy the user's data
|
||||
into it, padding the rest with zeros. */
|
||||
buf = (unsigned char *) malloc(dev->output_report_length);
|
||||
memcpy(buf, data, length);
|
||||
memset(buf + length, 0, dev->output_report_length - length);
|
||||
length = dev->output_report_length;
|
||||
}
|
||||
|
||||
res = WriteFile(dev->device_handle, buf, length, NULL, &ol);
|
||||
|
||||
if (!res) {
|
||||
if (GetLastError() != ERROR_IO_PENDING) {
|
||||
/* WriteFile() failed. Return error. */
|
||||
register_error(dev, "WriteFile");
|
||||
bytes_written = -1;
|
||||
goto end_of_function;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait here until the write is done. This makes
|
||||
hid_write() synchronous. */
|
||||
res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/);
|
||||
if (!res) {
|
||||
/* The Write operation failed. */
|
||||
register_error(dev, "WriteFile");
|
||||
bytes_written = -1;
|
||||
goto end_of_function;
|
||||
}
|
||||
|
||||
end_of_function:
|
||||
if (buf != data)
|
||||
free(buf);
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
|
||||
{
|
||||
DWORD bytes_read = 0;
|
||||
BOOL res;
|
||||
|
||||
/* Copy the handle for convenience. */
|
||||
HANDLE ev = dev->ol.hEvent;
|
||||
|
||||
if (!dev->read_pending) {
|
||||
/* Start an Overlapped I/O read. */
|
||||
dev->read_pending = TRUE;
|
||||
memset(dev->read_buf, 0, dev->input_report_length);
|
||||
ResetEvent(ev);
|
||||
res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol);
|
||||
|
||||
if (!res) {
|
||||
if (GetLastError() != ERROR_IO_PENDING) {
|
||||
/* ReadFile() has failed.
|
||||
Clean up and return error. */
|
||||
CancelIo(dev->device_handle);
|
||||
dev->read_pending = FALSE;
|
||||
goto end_of_function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (milliseconds >= 0) {
|
||||
/* See if there is any data yet. */
|
||||
res = WaitForSingleObject(ev, milliseconds);
|
||||
if (res != WAIT_OBJECT_0) {
|
||||
/* There was no data this time. Return zero bytes available,
|
||||
but leave the Overlapped I/O running. */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Either WaitForSingleObject() told us that ReadFile has completed, or
|
||||
we are in non-blocking mode. Get the number of bytes read. The actual
|
||||
data has been copied to the data[] array which was passed to ReadFile(). */
|
||||
res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
|
||||
|
||||
/* Set pending back to false, even if GetOverlappedResult() returned error. */
|
||||
dev->read_pending = FALSE;
|
||||
|
||||
if (res && bytes_read > 0) {
|
||||
if (dev->read_buf[0] == 0x0) {
|
||||
/* If report numbers aren't being used, but Windows sticks a report
|
||||
number (0x0) on the beginning of the report anyway. To make this
|
||||
work like the other platforms, and to make it work more like the
|
||||
HID spec, we'll skip over this byte. */
|
||||
size_t copy_len;
|
||||
bytes_read--;
|
||||
copy_len = length > bytes_read ? bytes_read : length;
|
||||
memcpy(data, dev->read_buf+1, copy_len);
|
||||
}
|
||||
else {
|
||||
/* Copy the whole buffer, report number and all. */
|
||||
size_t copy_len = length > bytes_read ? bytes_read : length;
|
||||
memcpy(data, dev->read_buf, copy_len);
|
||||
}
|
||||
}
|
||||
|
||||
end_of_function:
|
||||
if (!res) {
|
||||
register_error(dev, "GetOverlappedResult");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length)
|
||||
{
|
||||
return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
|
||||
}
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock)
|
||||
{
|
||||
dev->blocking = !nonblock;
|
||||
return 0; /* Success */
|
||||
}
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
|
||||
{
|
||||
BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length);
|
||||
if (!res) {
|
||||
register_error(dev, "HidD_SetFeature");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
|
||||
{
|
||||
BOOL res;
|
||||
#if 0
|
||||
res = HidD_GetFeature(dev->device_handle, data, length);
|
||||
if (!res) {
|
||||
register_error(dev, "HidD_GetFeature");
|
||||
return -1;
|
||||
}
|
||||
return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */
|
||||
#else
|
||||
DWORD bytes_returned;
|
||||
|
||||
OVERLAPPED ol;
|
||||
memset(&ol, 0, sizeof(ol));
|
||||
|
||||
res = DeviceIoControl(dev->device_handle,
|
||||
IOCTL_HID_GET_FEATURE,
|
||||
data, length,
|
||||
data, length,
|
||||
&bytes_returned, &ol);
|
||||
|
||||
if (!res) {
|
||||
if (GetLastError() != ERROR_IO_PENDING) {
|
||||
/* DeviceIoControl() failed. Return error. */
|
||||
register_error(dev, "Send Feature Report DeviceIoControl");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait here until the write is done. This makes
|
||||
hid_get_feature_report() synchronous. */
|
||||
res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/);
|
||||
if (!res) {
|
||||
/* The operation failed. */
|
||||
register_error(dev, "Send Feature Report GetOverLappedResult");
|
||||
return -1;
|
||||
}
|
||||
return bytes_returned;
|
||||
#endif
|
||||
}
|
||||
|
||||
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
return;
|
||||
CancelIo(dev->device_handle);
|
||||
free_hid_device(dev);
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
||||
{
|
||||
BOOL res;
|
||||
|
||||
res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
|
||||
if (!res) {
|
||||
register_error(dev, "HidD_GetManufacturerString");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
||||
{
|
||||
BOOL res;
|
||||
|
||||
res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
|
||||
if (!res) {
|
||||
register_error(dev, "HidD_GetProductString");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
||||
{
|
||||
BOOL res;
|
||||
|
||||
res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
|
||||
if (!res) {
|
||||
register_error(dev, "HidD_GetSerialNumberString");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
|
||||
{
|
||||
BOOL res;
|
||||
|
||||
res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * maxlen);
|
||||
if (!res) {
|
||||
register_error(dev, "HidD_GetIndexedString");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
|
||||
{
|
||||
return (wchar_t*)dev->last_error_str;
|
||||
}
|
||||
|
||||
|
||||
/*#define PICPGM*/
|
||||
/*#define S11*/
|
||||
#define P32
|
||||
#ifdef S11
|
||||
unsigned short VendorID = 0xa0a0;
|
||||
unsigned short ProductID = 0x0001;
|
||||
#endif
|
||||
|
||||
#ifdef P32
|
||||
unsigned short VendorID = 0x04d8;
|
||||
unsigned short ProductID = 0x3f;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef PICPGM
|
||||
unsigned short VendorID = 0x04d8;
|
||||
unsigned short ProductID = 0x0033;
|
||||
#endif
|
||||
|
||||
|
||||
#if 0
|
||||
int __cdecl main(int argc, char* argv[])
|
||||
{
|
||||
int res;
|
||||
unsigned char buf[65];
|
||||
|
||||
UNREFERENCED_PARAMETER(argc);
|
||||
UNREFERENCED_PARAMETER(argv);
|
||||
|
||||
/* Set up the command buffer. */
|
||||
memset(buf,0x00,sizeof(buf));
|
||||
buf[0] = 0;
|
||||
buf[1] = 0x81;
|
||||
|
||||
|
||||
/* Open the device. */
|
||||
int handle = open(VendorID, ProductID, L"12345");
|
||||
if (handle < 0)
|
||||
printf("unable to open device\n");
|
||||
|
||||
|
||||
/* Toggle LED (cmd 0x80) */
|
||||
buf[1] = 0x80;
|
||||
res = write(handle, buf, 65);
|
||||
if (res < 0)
|
||||
printf("Unable to write()\n");
|
||||
|
||||
/* Request state (cmd 0x81) */
|
||||
buf[1] = 0x81;
|
||||
write(handle, buf, 65);
|
||||
if (res < 0)
|
||||
printf("Unable to write() (2)\n");
|
||||
|
||||
/* Read requested state */
|
||||
read(handle, buf, 65);
|
||||
if (res < 0)
|
||||
printf("Unable to read()\n");
|
||||
|
||||
/* Print out the returned buffer. */
|
||||
for (int i = 0; i < 4; i++)
|
||||
printf("buf[%d]: %d\n", i, buf[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
387
tool/hidapi.h
Normal file
387
tool/hidapi.h
Normal file
@ -0,0 +1,387 @@
|
||||
/*******************************************************
|
||||
HIDAPI - Multi-Platform library for
|
||||
communication with HID devices.
|
||||
|
||||
Alan Ott
|
||||
Signal 11 Software
|
||||
|
||||
8/22/2009
|
||||
|
||||
Copyright 2009, All Rights Reserved.
|
||||
|
||||
At the discretion of the user of this library,
|
||||
this software may be licensed under the terms of the
|
||||
GNU General Public License v3, a BSD-Style license, or the
|
||||
original HIDAPI license as outlined in the LICENSE.txt,
|
||||
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
||||
files located at the root of the source distribution.
|
||||
These files may also be found in the public source
|
||||
code repository located at:
|
||||
http://github.com/signal11/hidapi .
|
||||
********************************************************/
|
||||
|
||||
/** @file
|
||||
* @defgroup API hidapi API
|
||||
*/
|
||||
|
||||
#ifndef HIDAPI_H__
|
||||
#define HIDAPI_H__
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define HID_API_EXPORT __declspec(dllexport)
|
||||
#define HID_API_CALL
|
||||
#else
|
||||
#define HID_API_EXPORT /**< API export macro */
|
||||
#define HID_API_CALL /**< API call macro */
|
||||
#endif
|
||||
|
||||
#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
struct hid_device_;
|
||||
typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
|
||||
|
||||
/** hidapi info structure */
|
||||
struct hid_device_info {
|
||||
/** Platform-specific device path */
|
||||
char *path;
|
||||
/** Device Vendor ID */
|
||||
unsigned short vendor_id;
|
||||
/** Device Product ID */
|
||||
unsigned short product_id;
|
||||
/** Serial Number */
|
||||
wchar_t *serial_number;
|
||||
/** Device Release Number in binary-coded decimal,
|
||||
also known as Device Version Number */
|
||||
unsigned short release_number;
|
||||
/** Manufacturer String */
|
||||
wchar_t *manufacturer_string;
|
||||
/** Product string */
|
||||
wchar_t *product_string;
|
||||
/** Usage Page for this Device/Interface
|
||||
(Windows/Mac only). */
|
||||
unsigned short usage_page;
|
||||
/** Usage for this Device/Interface
|
||||
(Windows/Mac only).*/
|
||||
unsigned short usage;
|
||||
/** The USB interface which this logical device
|
||||
represents. Valid on both Linux implementations
|
||||
in all cases, and valid on the Windows implementation
|
||||
only if the device contains more than one interface. */
|
||||
int interface_number;
|
||||
|
||||
/** Pointer to the next device */
|
||||
struct hid_device_info *next;
|
||||
};
|
||||
|
||||
|
||||
/** @brief Initialize the HIDAPI library.
|
||||
|
||||
This function initializes the HIDAPI library. Calling it is not
|
||||
strictly necessary, as it will be called automatically by
|
||||
hid_enumerate() and any of the hid_open_*() functions if it is
|
||||
needed. This function should be called at the beginning of
|
||||
execution however, if there is a chance of HIDAPI handles
|
||||
being opened by different threads simultaneously.
|
||||
|
||||
@ingroup API
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_init(void);
|
||||
|
||||
/** @brief Finalize the HIDAPI library.
|
||||
|
||||
This function frees all of the static data associated with
|
||||
HIDAPI. It should be called at the end of execution to avoid
|
||||
memory leaks.
|
||||
|
||||
@ingroup API
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_exit(void);
|
||||
|
||||
/** @brief Enumerate the HID Devices.
|
||||
|
||||
This function returns a linked list of all the HID devices
|
||||
attached to the system which match vendor_id and product_id.
|
||||
If @p vendor_id is set to 0 then any vendor matches.
|
||||
If @p product_id is set to 0 then any product matches.
|
||||
If @p vendor_id and @p product_id are both set to 0, then
|
||||
all HID devices will be returned.
|
||||
|
||||
@ingroup API
|
||||
@param vendor_id The Vendor ID (VID) of the types of device
|
||||
to open.
|
||||
@param product_id The Product ID (PID) of the types of
|
||||
device to open.
|
||||
|
||||
@returns
|
||||
This function returns a pointer to a linked list of type
|
||||
struct #hid_device, containing information about the HID devices
|
||||
attached to the system, or NULL in the case of failure. Free
|
||||
this linked list by calling hid_free_enumeration().
|
||||
*/
|
||||
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
|
||||
|
||||
/** @brief Free an enumeration Linked List
|
||||
|
||||
This function frees a linked list created by hid_enumerate().
|
||||
|
||||
@ingroup API
|
||||
@param devs Pointer to a list of struct_device returned from
|
||||
hid_enumerate().
|
||||
*/
|
||||
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);
|
||||
|
||||
/** @brief Open a HID device using a Vendor ID (VID), Product ID
|
||||
(PID) and optionally a serial number.
|
||||
|
||||
If @p serial_number is NULL, the first device with the
|
||||
specified VID and PID is opened.
|
||||
|
||||
@ingroup API
|
||||
@param vendor_id The Vendor ID (VID) of the device to open.
|
||||
@param product_id The Product ID (PID) of the device to open.
|
||||
@param serial_number The Serial Number of the device to open
|
||||
(Optionally NULL).
|
||||
|
||||
@returns
|
||||
This function returns a pointer to a #hid_device object on
|
||||
success or NULL on failure.
|
||||
*/
|
||||
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);
|
||||
|
||||
/** @brief Open a HID device by its path name.
|
||||
|
||||
The path name be determined by calling hid_enumerate(), or a
|
||||
platform-specific path name can be used (eg: /dev/hidraw0 on
|
||||
Linux).
|
||||
|
||||
@ingroup API
|
||||
@param path The path name of the device to open
|
||||
|
||||
@returns
|
||||
This function returns a pointer to a #hid_device object on
|
||||
success or NULL on failure.
|
||||
*/
|
||||
HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path);
|
||||
|
||||
/** @brief Write an Output report to a HID device.
|
||||
|
||||
The first byte of @p data[] must contain the Report ID. For
|
||||
devices which only support a single report, this must be set
|
||||
to 0x0. The remaining bytes contain the report data. Since
|
||||
the Report ID is mandatory, calls to hid_write() will always
|
||||
contain one more byte than the report contains. For example,
|
||||
if a hid report is 16 bytes long, 17 bytes must be passed to
|
||||
hid_write(), the Report ID (or 0x0, for devices with a
|
||||
single report), followed by the report data (16 bytes). In
|
||||
this example, the length passed in would be 17.
|
||||
|
||||
hid_write() will send the data on the first OUT endpoint, if
|
||||
one exists. If it does not, it will send the data through
|
||||
the Control Endpoint (Endpoint 0).
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param data The data to send, including the report number as
|
||||
the first byte.
|
||||
@param length The length in bytes of the data to send.
|
||||
|
||||
@returns
|
||||
This function returns the actual number of bytes written and
|
||||
-1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Read an Input report from a HID device with timeout.
|
||||
|
||||
Input reports are returned
|
||||
to the host through the INTERRUPT IN endpoint. The first byte will
|
||||
contain the Report number if the device uses numbered reports.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param data A buffer to put the read data into.
|
||||
@param length The number of bytes to read. For devices with
|
||||
multiple reports, make sure to read an extra byte for
|
||||
the report number.
|
||||
@param milliseconds timeout in milliseconds or -1 for blocking wait.
|
||||
|
||||
@returns
|
||||
This function returns the actual number of bytes read and
|
||||
-1 on error. If no packet was available to be read within
|
||||
the timeout period, this function returns 0.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
|
||||
|
||||
/** @brief Read an Input report from a HID device.
|
||||
|
||||
Input reports are returned
|
||||
to the host through the INTERRUPT IN endpoint. The first byte will
|
||||
contain the Report number if the device uses numbered reports.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param data A buffer to put the read data into.
|
||||
@param length The number of bytes to read. For devices with
|
||||
multiple reports, make sure to read an extra byte for
|
||||
the report number.
|
||||
|
||||
@returns
|
||||
This function returns the actual number of bytes read and
|
||||
-1 on error. If no packet was available to be read and
|
||||
the handle is in non-blocking mode, this function returns 0.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Set the device handle to be non-blocking.
|
||||
|
||||
In non-blocking mode calls to hid_read() will return
|
||||
immediately with a value of 0 if there is no data to be
|
||||
read. In blocking mode, hid_read() will wait (block) until
|
||||
there is data to read before returning.
|
||||
|
||||
Nonblocking can be turned on and off at any time.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param nonblock enable or not the nonblocking reads
|
||||
- 1 to enable nonblocking
|
||||
- 0 to disable nonblocking.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock);
|
||||
|
||||
/** @brief Send a Feature report to the device.
|
||||
|
||||
Feature reports are sent over the Control endpoint as a
|
||||
Set_Report transfer. The first byte of @p data[] must
|
||||
contain the Report ID. For devices which only support a
|
||||
single report, this must be set to 0x0. The remaining bytes
|
||||
contain the report data. Since the Report ID is mandatory,
|
||||
calls to hid_send_feature_report() will always contain one
|
||||
more byte than the report contains. For example, if a hid
|
||||
report is 16 bytes long, 17 bytes must be passed to
|
||||
hid_send_feature_report(): the Report ID (or 0x0, for
|
||||
devices which do not use numbered reports), followed by the
|
||||
report data (16 bytes). In this example, the length passed
|
||||
in would be 17.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param data The data to send, including the report number as
|
||||
the first byte.
|
||||
@param length The length in bytes of the data to send, including
|
||||
the report number.
|
||||
|
||||
@returns
|
||||
This function returns the actual number of bytes written and
|
||||
-1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Get a feature report from a HID device.
|
||||
|
||||
Make sure to set the first byte of @p data[] to the Report
|
||||
ID of the report to be read. Make sure to allow space for
|
||||
this extra byte in @p data[].
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param data A buffer to put the read data into, including
|
||||
the Report ID. Set the first byte of @p data[] to the
|
||||
Report ID of the report to be read.
|
||||
@param length The number of bytes to read, including an
|
||||
extra byte for the report ID. The buffer can be longer
|
||||
than the actual report.
|
||||
|
||||
@returns
|
||||
This function returns the number of bytes read and
|
||||
-1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Close a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
*/
|
||||
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device);
|
||||
|
||||
/** @brief Get The Manufacturer String from a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get The Product String from a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get The Serial Number String from a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get a string from a HID device, based on its string index.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param string_index The index of the string to get.
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get a string describing the last error which occurred.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
|
||||
@returns
|
||||
This function returns a string containing the last error
|
||||
which occurred or NULL if none has occurred.
|
||||
*/
|
||||
HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
298
tool/main.c
Normal file
298
tool/main.c
Normal file
@ -0,0 +1,298 @@
|
||||
/* wusbmote: Wiimote accessory to USB Adapter
|
||||
* Copyright (C) 2012-2014 Raphaël Assénat
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* The author may be contacted at raph@raphnet.net
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "version.h"
|
||||
#include "gcn64.h"
|
||||
#include "../requests.h"
|
||||
#include "../gcn64_protocol.h"
|
||||
|
||||
static void printUsage(void)
|
||||
{
|
||||
printf("./gcn64_ctl [OPTION]... [COMMAND]....\n");
|
||||
printf("Control tool for WUSBmote adapter. Version %s\n", VERSION_STR);
|
||||
printf("\n");
|
||||
printf("Options:\n");
|
||||
printf(" -h, --help Print help\n");
|
||||
printf(" -l, --list List devices\n");
|
||||
printf(" -s serial Operate on specified device (required unless -f is specified)\n");
|
||||
printf(" -f, --force If no serial is specified, use first device detected.\n");
|
||||
printf(" -o, --outfile Output file for read operations (eg: --n64-mempak-dump)\n");
|
||||
printf("\n");
|
||||
printf("Configuration commands:\n");
|
||||
printf(" --set_serial serial Assign a new device serial number\n");
|
||||
printf(" --get_serial Read serial from eeprom\n");
|
||||
printf("\n");
|
||||
printf("Advanced commands:\n");
|
||||
printf(" --bootloader Re-enumerate in bootloader mode\n");
|
||||
printf(" --suspend_polling Stop polling controller\n");
|
||||
printf("\n");
|
||||
printf("Raw controller commands:\n");
|
||||
printf(" --n64_getstatus Read N64 controller status now\n");
|
||||
printf(" --gc_getstatus Read GC controller status now (turns rumble OFF)\n");
|
||||
printf(" --gc_getstatus_rumble Read GC controller status now (turns rumble ON)\n");
|
||||
printf(" --n64_getcaps Get N64 controller capabilities (or status such as pak present)\n");
|
||||
printf(" --n64_mempak_dump Dump (display) N64 mempak contents\n");
|
||||
}
|
||||
|
||||
#define OPT_OUTFILE 'o'
|
||||
#define OPT_SET_SERIAL 257
|
||||
#define OPT_GET_SERIAL 258
|
||||
#define OPT_BOOTLOADER 300
|
||||
#define OPT_N64_GETSTATUS 301
|
||||
#define OPT_GC_GETSTATUS 302
|
||||
#define OPT_GC_GETSTATUS_RUMBLE 303
|
||||
#define OPT_N64_MEMPAK_DUMP 304
|
||||
#define OPT_N64_GETCAPS 305
|
||||
#define OPT_SUSPEND_POLLING 306
|
||||
|
||||
struct option longopts[] = {
|
||||
{ "help", 0, NULL, 'h' },
|
||||
{ "list", 0, NULL, 'l' },
|
||||
{ "force", 0, NULL, 'f' },
|
||||
{ "set_serial", 1, NULL, OPT_SET_SERIAL },
|
||||
{ "get_serial", 0, NULL, OPT_GET_SERIAL },
|
||||
{ "bootloader", 0, NULL, OPT_BOOTLOADER },
|
||||
{ "n64_getstatus", 0, NULL, OPT_N64_GETSTATUS },
|
||||
{ "gc_getstatus", 0, NULL, OPT_GC_GETSTATUS },
|
||||
{ "gc_getstatus_rumble", 0, NULL, OPT_GC_GETSTATUS_RUMBLE },
|
||||
{ "n64_getcaps", 0, NULL, OPT_N64_GETCAPS },
|
||||
{ "n64_mempak_dump", 0, NULL, OPT_N64_MEMPAK_DUMP },
|
||||
{ "suspend_polling", 0, NULL, OPT_SUSPEND_POLLING },
|
||||
{ "outfile", 0, NULL, OPT_OUTFILE },
|
||||
{ },
|
||||
};
|
||||
|
||||
static int listDevices(void)
|
||||
{
|
||||
int n_found = 0;
|
||||
struct gcn64_list_ctx *listctx;
|
||||
struct gcn64_info inf;
|
||||
|
||||
listctx = gcn64_allocListCtx();
|
||||
if (!listctx) {
|
||||
fprintf(stderr, "List context could not be allocated\n");
|
||||
return -1;
|
||||
}
|
||||
while (gcn64_listDevices(&inf, listctx))
|
||||
{
|
||||
n_found++;
|
||||
printf("Found device '%ls', serial '%ls'\n", inf.str_prodname, inf.str_serial);
|
||||
}
|
||||
gcn64_freeListCtx(listctx);
|
||||
printf("%d device(s) found\n", n_found);
|
||||
|
||||
return n_found;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
gcn64_hdl_t hdl;
|
||||
struct gcn64_list_ctx *listctx;
|
||||
int opt, retval = 0;
|
||||
struct gcn64_info inf;
|
||||
struct gcn64_info *selected_device = NULL;
|
||||
int verbose = 0, use_first = 0, serial_specified = 0;
|
||||
int cmd_list = 0;
|
||||
#define TARGET_SERIAL_CHARS 128
|
||||
wchar_t target_serial[TARGET_SERIAL_CHARS];
|
||||
const char *short_optstr = "hls:vfo:";
|
||||
const char *outfile = NULL;
|
||||
|
||||
while((opt = getopt_long(argc, argv, short_optstr, longopts, NULL)) != -1) {
|
||||
switch(opt)
|
||||
{
|
||||
case 's':
|
||||
{
|
||||
mbstate_t ps;
|
||||
memset(&ps, 0, sizeof(ps));
|
||||
if (mbsrtowcs(target_serial, (const char **)&optarg, TARGET_SERIAL_CHARS, &ps) < 1) {
|
||||
fprintf(stderr, "Invalid serial number specified\n");
|
||||
return -1;
|
||||
}
|
||||
serial_specified = 1;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
use_first = 1;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'h':
|
||||
printUsage();
|
||||
return 0;
|
||||
case 'l':
|
||||
cmd_list = 1;
|
||||
break;
|
||||
case 'o':
|
||||
outfile = optarg;
|
||||
printf("Output file: %s\n", outfile);
|
||||
break;
|
||||
case '?':
|
||||
fprintf(stderr, "Unrecognized argument. Try -h\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
gcn64_init(verbose);
|
||||
|
||||
if (cmd_list) {
|
||||
printf("Simply listing the devices...\n");
|
||||
return listDevices();
|
||||
}
|
||||
|
||||
if (!serial_specified && !use_first) {
|
||||
fprintf(stderr, "A serial number or -f must be used. Try -h for more information.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
listctx = gcn64_allocListCtx();
|
||||
while ((selected_device = gcn64_listDevices(&inf, listctx)))
|
||||
{
|
||||
if (serial_specified) {
|
||||
if (0 == wcscmp(inf.str_serial, target_serial)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// use_first == 1
|
||||
printf("Will use device '%ls' serial '%ls'\n", inf.str_prodname, inf.str_serial);
|
||||
break;
|
||||
}
|
||||
}
|
||||
gcn64_freeListCtx(listctx);
|
||||
|
||||
if (!selected_device) {
|
||||
if (serial_specified) {
|
||||
fprintf(stderr, "Device not found\n");
|
||||
} else {
|
||||
fprintf(stderr, "No device found\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
hdl = gcn64_openDevice(selected_device);
|
||||
if (!hdl) {
|
||||
printf("Error opening device. (Do you have permissions?)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Ready.\n");
|
||||
|
||||
optind = 1;
|
||||
while((opt = getopt_long(argc, argv, short_optstr, longopts, NULL)) != -1)
|
||||
{
|
||||
unsigned char cmd[64] = { };
|
||||
int n;
|
||||
int cmdlen = 0;
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
case OPT_SET_SERIAL:
|
||||
printf("Setting serial...");
|
||||
if (strlen(optarg) != 6) {
|
||||
fprintf(stderr, "Serial number must be 6 characters\n");
|
||||
return -1;
|
||||
}
|
||||
cmd[0] = RQ_GCN64_SET_CONFIG_PARAM;
|
||||
cmd[1] = CFG_PARAM_SERIAL;
|
||||
memcpy(cmd + 2, optarg, 6);
|
||||
cmdlen = 8;
|
||||
break;
|
||||
|
||||
case OPT_GET_SERIAL:
|
||||
cmd[0] = RQ_GCN64_GET_CONFIG_PARAM;
|
||||
cmd[1] = CFG_PARAM_SERIAL;
|
||||
cmdlen = 2;
|
||||
break;
|
||||
|
||||
case OPT_BOOTLOADER:
|
||||
printf("Sending 'jump to bootloader' command...");
|
||||
cmd[0] = RQ_GCN64_JUMP_TO_BOOTLOADER;
|
||||
cmdlen = 1;
|
||||
break;
|
||||
|
||||
case OPT_SUSPEND_POLLING:
|
||||
cmd[0] = RQ_GCN64_SUSPEND_POLLING;
|
||||
cmdlen = 1;
|
||||
break;
|
||||
|
||||
case OPT_N64_GETSTATUS:
|
||||
cmd[0] = RQ_GCN64_RAW_SI_COMMAND;
|
||||
cmd[1] = 0x01; // Length of SI command
|
||||
cmd[2] = N64_GET_STATUS; // N64 GET status
|
||||
cmdlen = 3;
|
||||
break;
|
||||
|
||||
case OPT_GC_GETSTATUS:
|
||||
cmd[0] = RQ_GCN64_RAW_SI_COMMAND;
|
||||
cmd[1] = 0x03; // Length of SI command
|
||||
cmd[2] = GC_GETSTATUS1;
|
||||
cmd[3] = GC_GETSTATUS2;
|
||||
cmd[4] = GC_GETSTATUS3(0);
|
||||
cmdlen = 5;
|
||||
break;
|
||||
|
||||
case OPT_GC_GETSTATUS_RUMBLE:
|
||||
cmd[0] = RQ_GCN64_RAW_SI_COMMAND;
|
||||
cmd[1] = 0x03; // Length of SI command
|
||||
cmd[2] = GC_GETSTATUS1;
|
||||
cmd[3] = GC_GETSTATUS2;
|
||||
cmd[4] = GC_GETSTATUS3(0);
|
||||
cmdlen = 5;
|
||||
break;
|
||||
|
||||
case OPT_N64_GETCAPS:
|
||||
cmd[0] = RQ_GCN64_RAW_SI_COMMAND;
|
||||
cmd[1] = 0x01; // Length of SI command
|
||||
cmd[2] = N64_GET_CAPABILITIES; // N64 GET status
|
||||
cmdlen = 3;
|
||||
break;
|
||||
|
||||
case OPT_N64_MEMPAK_DUMP:
|
||||
mempak_dump(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd[0]) {
|
||||
int i;
|
||||
n = gcn64_exchange(hdl, cmd, cmdlen, cmd, sizeof(cmd));
|
||||
if (n<0)
|
||||
break;
|
||||
|
||||
printf("Result: %d bytes: ", n);
|
||||
for (i=0; i<n; i++) {
|
||||
printf("%02x ", cmd[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
gcn64_closeDevice(hdl);
|
||||
gcn64_shutdown();
|
||||
|
||||
return retval;
|
||||
}
|
209
tool/mempak.c
Normal file
209
tool/mempak.c
Normal file
@ -0,0 +1,209 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include "gcn64.h"
|
||||
#include "../gcn64_protocol.h"
|
||||
#include "../requests.h"
|
||||
|
||||
/* __calc_address_crc is from libdragon which is public domain. */
|
||||
|
||||
/**
|
||||
* @brief Calculate the 5 bit CRC on a mempak address
|
||||
*
|
||||
* This function, given an address intended for a mempak read or write, will
|
||||
* calculate the CRC on the address, returning the corrected address | CRC.
|
||||
*
|
||||
* @param[in] address
|
||||
* The mempak address to calculate CRC over
|
||||
*
|
||||
* @return The mempak address | CRC
|
||||
*/
|
||||
static uint16_t __calc_address_crc( uint16_t address )
|
||||
{
|
||||
/* CRC table */
|
||||
uint16_t xor_table[16] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x1F, 0x0B, 0x16, 0x19, 0x07, 0x0E, 0x1C, 0x0D, 0x1A, 0x01 };
|
||||
uint16_t crc = 0;
|
||||
int i;
|
||||
|
||||
/* Make sure we have a valid address */
|
||||
address &= ~0x1F;
|
||||
|
||||
/* Go through each bit in the address, and if set, xor the right value into the output */
|
||||
for( i = 15; i >= 5; i-- )
|
||||
{
|
||||
/* Is this bit set? */
|
||||
if( ((address >> i) & 0x1) )
|
||||
{
|
||||
crc ^= xor_table[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Just in case */
|
||||
crc &= 0x1F;
|
||||
|
||||
/* Create a new address with the CRC appended */
|
||||
return address | crc;
|
||||
}
|
||||
|
||||
/* __calc_data_crc is from libdragon which is public domain. */
|
||||
|
||||
/**
|
||||
* @brief Calculate the 8 bit CRC over a 32-byte block of data
|
||||
*
|
||||
* This function calculates the 8 bit CRC appropriate for checking a 32-byte
|
||||
* block of data intended for or retrieved from a mempak.
|
||||
*
|
||||
* @param[in] data
|
||||
* Pointer to 32 bytes of data to run the CRC over
|
||||
*
|
||||
* @return The calculated 8 bit CRC over the data
|
||||
*/
|
||||
static uint8_t __calc_data_crc( uint8_t *data )
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
int i,j;
|
||||
|
||||
for( i = 0; i <= 32; i++ )
|
||||
{
|
||||
for( j = 7; j >= 0; j-- )
|
||||
{
|
||||
int tmp = 0;
|
||||
|
||||
if( ret & 0x80 )
|
||||
{
|
||||
tmp = 0x85;
|
||||
}
|
||||
|
||||
ret <<= 1;
|
||||
|
||||
if( i < 32 )
|
||||
{
|
||||
if( data[i] & (0x01 << j) )
|
||||
{
|
||||
ret |= 0x1;
|
||||
}
|
||||
}
|
||||
|
||||
ret ^= tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int mempak_writeBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char data[32])
|
||||
{
|
||||
unsigned char cmd[64];
|
||||
int cmdlen;
|
||||
int n;
|
||||
uint16_t addr_crc;
|
||||
|
||||
addr_crc = __calc_address_crc(addr);
|
||||
|
||||
cmd[0] = RQ_GCN64_RAW_SI_COMMAND;
|
||||
cmd[1] = 3;
|
||||
cmd[2] = N64_EXPANSION_READ;
|
||||
cmd[3] = addr_crc>>8; // Address high byte
|
||||
cmd[4] = addr_crc&0xff; // Address low byte
|
||||
memcpy(cmd + 5, data, 0x20);
|
||||
|
||||
cmdlen = 5 + 0x20;
|
||||
|
||||
n = gcn64_exchange(hdl, cmd, cmdlen, cmd, sizeof(cmd));
|
||||
if (n != 35)
|
||||
return -1;
|
||||
|
||||
return 0x20;
|
||||
}
|
||||
|
||||
int mempak_readBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char dst[32])
|
||||
{
|
||||
unsigned char cmd[64];
|
||||
int cmdlen;
|
||||
int n;
|
||||
uint16_t addr_crc;
|
||||
unsigned char crc;
|
||||
|
||||
addr_crc = __calc_address_crc(addr);
|
||||
|
||||
cmd[0] = RQ_GCN64_RAW_SI_COMMAND;
|
||||
cmd[1] = 3;
|
||||
cmd[2] = N64_EXPANSION_READ;
|
||||
cmd[3] = addr_crc>>8; // Address high byte
|
||||
cmd[4] = addr_crc&0xff; // Address low byte
|
||||
cmdlen = 5;
|
||||
|
||||
//printf("Addr 0x%04x with crc -> 0x%04x\n", addr, addr_crc);
|
||||
|
||||
n = gcn64_exchange(hdl, cmd, cmdlen, cmd, sizeof(cmd));
|
||||
if (n != 35)
|
||||
return -1;
|
||||
|
||||
memcpy(dst, cmd + 2, 0x20);
|
||||
|
||||
crc = __calc_data_crc(dst);
|
||||
if (crc != cmd[34]) {
|
||||
fprintf(stderr, "Bad CRC reading address 0x%04x\n", addr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0x20;
|
||||
}
|
||||
|
||||
int mempak_init(gcn64_hdl_t hdl)
|
||||
{
|
||||
unsigned char buf[0x20];
|
||||
|
||||
memset(buf, 0xfe, 32);
|
||||
mempak_writeBlock(hdl, 0x8000, buf);
|
||||
memset(buf, 0x80, 32);
|
||||
mempak_writeBlock(hdl, 0x8000, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DUMP_SIZE 0x8000
|
||||
|
||||
int mempak_readAll(gcn64_hdl_t hdl, unsigned char dstbuf[0x8000])
|
||||
{
|
||||
unsigned short addr;
|
||||
|
||||
for (addr = 0x0000; addr < DUMP_SIZE; addr+= 0x20)
|
||||
{
|
||||
printf("Reading address 0x%04x / 0x8000\r", addr); fflush(stdout);
|
||||
if (mempak_readBlock(hdl, addr, &dstbuf[addr]) != 0x20) {
|
||||
fprintf(stderr, "Error: Short read\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
printf("\nDone!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mempak_dump(gcn64_hdl_t hdl)
|
||||
{
|
||||
unsigned char cardbuf[0x8000];
|
||||
int i,j;
|
||||
|
||||
printf("Init pak\n");
|
||||
mempak_init(hdl);
|
||||
|
||||
printf("Reading card...\n");
|
||||
mempak_readAll(hdl, cardbuf);
|
||||
|
||||
for (i=0; i<DUMP_SIZE; i+=0x20) {
|
||||
printf("%04x: ", i);
|
||||
for (j=0; j<0x20; j++) {
|
||||
printf("%02x ", cardbuf[i+j]);
|
||||
}
|
||||
printf(" ");
|
||||
|
||||
for (j=0; j<0x20; j++) {
|
||||
printf("%c", isprint(cardbuf[i+j]) ? cardbuf[i+j] : '.' );
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
4
tool/mempak.h
Normal file
4
tool/mempak.h
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
void mempak_dump(gcn64_hdl_t hdl);
|
||||
int mempak_readBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char dst[32]);
|
||||
|
6
tool/version.h
Normal file
6
tool/version.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef _version_h__
|
||||
#define _version_h__
|
||||
|
||||
#define VERSION_STR "1.1.1"
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user