1
0
mirror of https://github.com/raphnet/4nes4snes synced 2024-12-22 06:58:51 -05:00
This commit is contained in:
Raphaël Assénat 2007-04-18 23:58:01 +00:00
parent b772e29b7a
commit ec9d9997ab
6 changed files with 240 additions and 187 deletions

View File

@ -1,2 +1,9 @@
--- v1.1 (18 Apr 2007)
- Changed report descriptor. There are now four separate report IDs, one
per controller. This was necessary because even though all axis and buttons
were perfectly functional in 'control panel -> game controllers', it was not
the case in many games, including zsnes and snes9x!
- Changed device id to 0x0A9D
--- v1.0 (24 Mar 2007)
Initial release.

View File

@ -57,9 +57,9 @@ Table of contents:
http://www.mecanique.co.uk/products/usb/pid.html
6) Where do I get more information and updates?
6) Where can I get more information and updates?
--------------------------------------------
Visit 4nes4snes page:
Visit the 4nes4snes webpage:
http://www.raphnet.net/electronique/4nes4snes/index_en.php
you may also contact me by email:
Raphael Assenat <raph@raphnet.net>

View File

@ -3,7 +3,7 @@
typedef struct {
// size of reports built by buildReport
int report_size;
char num_reports;
int reportDescriptorSize;
void *reportDescriptor; // must be in flash
@ -13,8 +13,13 @@ typedef struct {
void (*init)(void);
void (*update)(void);
char (*changed)(void);
void (*buildReport)(unsigned char *buf);
char (*changed)(char id);
/**
* \param id Controller id (starting at 1 to match report IDs)
* \return The number of bytes written to buf.
* */
char (*buildReport)(unsigned char *buf, char id);
} Gamepad;
#endif // _gamepad_h__

164
main.c
View File

@ -25,6 +25,9 @@
#include "leds.h"
#include "devdesc.h"
/* The maximum number of independent reports that are supported. */
#define MAX_REPORTS 8
int usbCfgSerialNumberStringDescriptor[] PROGMEM = {
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LENGTH),
'1', '0', '0', '0'
@ -78,75 +81,60 @@ static uchar reportBuffer[12]; /* buffer for HID reports */
/* ----------------------------- USB interface ----------------------------- */
/* ------------------------------------------------------------------------- */
static uchar idleRate; /* in 4 ms units */
static uchar idleRates[MAX_REPORTS]; /* in 4 ms units */
static uchar reportPos=0;
uchar usbFunctionSetup(uchar data[8])
{
usbRequest_t *rq = (void *)data;
int i;
usbMsgPtr = reportBuffer;
if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */
if(rq->bRequest == USBRQ_HID_GET_REPORT){ /* wValue: ReportType (highbyte), ReportID (lowbyte) */
/* we only have one report type, so don't look at wValue */
/* class request type */
if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){
switch (rq->bRequest)
{
case USBRQ_HID_GET_REPORT:
/* wValue: ReportType (highbyte), ReportID (lowbyte) */
reportPos=0;
//curGamepad->buildReport(reportBuffer);
//return curGamepad->report_size;
return 0xff;
}else if(rq->bRequest == USBRQ_HID_GET_IDLE){
usbMsgPtr = &idleRate;
return curGamepad->buildReport(reportBuffer, rq->wValue.bytes[0]);
case USBRQ_HID_GET_IDLE:
if (rq->wValue.bytes[0] > 0 && rq->wValue.bytes[0] <= MAX_REPORTS) {
usbMsgPtr = idleRates + (rq->wValue.bytes[0] - 1);
return 1;
}else if(rq->bRequest == USBRQ_HID_SET_IDLE){
idleRate = rq->wValue.bytes[1];
}
}else{
break;
case USBRQ_HID_SET_IDLE:
if (rq->wValue.bytes[0]==0) {
for (i=0; i<MAX_REPORTS; i++)
idleRates[i] = rq->wValue.bytes[1];
}
else {
if (rq->wValue.bytes[0] > 0 && rq->wValue.bytes[0] <= MAX_REPORTS) {
idleRates[rq->wValue.bytes[0]-1] = rq->wValue.bytes[1];
}
}
break;
}
} else {
/* no vendor specific requests implemented */
}
return 0;
}
uchar usbFunctionRead(uchar *data, uchar len)
{
char i,c;
for (c=0; reportPos < sizeof(reportBuffer) && c<len; c++, reportPos++)
{
*data = reportBuffer[reportPos];
i++;
}
return c;
}
/* ------------------------------------------------------------------------- */
int main(void)
{
char must_report = 0, first_run = 1;
uchar idleCounter = 0;
// int run_mode;
char must_report = 0; /* bitfield */
uchar idleCounters[MAX_REPORTS];
int i;
// led pin as output
// DDRD |= 0x20;
#if 0
/* Dip switch common: DB0, outputs: DB1 and DB2 */
DDRB |= 0x01;
DDRB &= ~0x06;
PORTB |= 0x06; /* enable pull up on DB1 and DB2 */
PORTB &= ~0x01; /* Set DB0 to low */
_delay_ms(10); /* let pins settle */
run_mode = (PINB & 0x06)>>1;
switch(run_mode)
{
default:
case 3:
curGamepad = snesGetGamepad();
break;
}
#endif
memset(idleCounters, 0, MAX_REPORTS);
curGamepad = snesGetGamepad();
@ -173,6 +161,9 @@ int main(void)
curGamepad->init();
odDebugInit();
usbInit();
curGamepad->update();
sei();
DBG1(0x00, 0, 0);
@ -183,73 +174,68 @@ int main(void)
// this must be called at each 50 ms or less
usbPoll();
if (first_run) {
curGamepad->update();
first_run = 0;
}
if(TIFR & (1<<TOV0)){ /* 22 ms timer */
TIFR = 1<<TOV0;
if(idleRate != 0){
if(idleCounter > 4){
idleCounter -= 5; /* 22 ms in units of 4 ms */
}else{
idleCounter = idleRate;
must_report = 1;
}
}
}
/* Read the controllers at 60hz */
if (TIFR & (1<<OCF2))
{
TIFR = 1<<OCF2;
if (!must_report)
{
curGamepad->update();
if (curGamepad->changed()) {
must_report = 1;
/* Check what will have to be reported */
for (i=0; i<curGamepad->num_reports; i++) {
if (curGamepad->changed(i+1)) {
must_report |= (1<<i);
}
}
}
/* Try to report at the granularity requested by
* the host. */
if (TIFR & (1<<TOV0)) { /* 22 ms timer */
TIFR = 1<<TOV0;
for (i=0; i<curGamepad->num_reports; i++)
{
// 0 means
if(idleRates[i] != 0){
if (idleCounters[i] > 4) {
idleCounters[i] -= 5; /* 22 ms in units of 4 ms */
} else {
// reset the counter and schedule a report for this
idleCounters[i] = idleRates[i];
must_report |= (1<<i);
}
}
}
}
if(must_report)
{
for (i = 0; i < curGamepad->num_reports; i++)
{
if ((must_report & (1<<i)) == 0)
continue;
if (usbInterruptIsReady())
{
int reported=0;
unsigned char empty=0;
char len;
curGamepad->buildReport(reportBuffer);
reportPos = 0;
while (reported < curGamepad->report_size)
{
int cur_report_siz;
if (curGamepad->report_size - reported >= 8)
cur_report_siz = 8;
else
cur_report_siz = curGamepad->report_size - reported;
usbSetInterrupt(&reportBuffer[reported], cur_report_siz);
len = curGamepad->buildReport(reportBuffer, i+1);
usbSetInterrupt(reportBuffer, len);
while (!usbInterruptIsReady())
{
usbPoll();
wdt_reset();
}
reported += cur_report_siz;
}
}
usbSetInterrupt(&empty, 0);
must_report = 0;
}
}
}
return 0;
}

175
snes.c
View File

@ -16,7 +16,6 @@
#include "leds.h"
#include "snes.h"
#define REPORT_SIZE 12
#define GAMEPAD_BYTES 8 /* 2 byte per snes controller * 4 controllers */
/******** IO port definitions **************/
@ -50,8 +49,8 @@
/*********** prototypes *************/
static void snesInit(void);
static void snesUpdate(void);
static char snesChanged(void);
static void snesBuildReport(unsigned char *reportBuffer);
static char snesChanged(char report_id);
static char snesBuildReport(unsigned char *reportBuffer, char report_id);
// the most recent bytes we fetched from the controller
@ -205,13 +204,13 @@ static void snesUpdate(void)
}
static char snesChanged(void)
static char snesChanged(char report_id)
{
static int first = 1;
if (first) { first = 0; return 1; }
report_id--; // first report is 1
return memcmp(last_read_controller_bytes,
last_reported_controller_bytes, GAMEPAD_BYTES);
return memcmp( &last_read_controller_bytes[report_id<<1],
&last_reported_controller_bytes[report_id<<1],
2);
}
static char getX(unsigned char nesByte1)
@ -245,8 +244,13 @@ static unsigned char snesReorderButtons(unsigned char bytes[2])
return v;
}
static void snesBuildReport(unsigned char *reportBuffer)
static char snesBuildReport(unsigned char *reportBuffer, char id)
{
char idx;
if (id < 0 || id > 4)
return 0;
/* last_read_controller_bytes[] structure:
*
* [0] : controller 1, 8 first bits (dpad + start + sel + y|a + b)
@ -262,86 +266,139 @@ static void snesBuildReport(unsigned char *reportBuffer)
* [7] : controller 4, 4 extra snes buttons
*/
idx = id - 1;
if (reportBuffer != NULL)
{
reportBuffer[0]=getX(last_read_controller_bytes[0]);
reportBuffer[1]=getY(last_read_controller_bytes[0]);
reportBuffer[2]=getX(last_read_controller_bytes[2]);
reportBuffer[3]=getY(last_read_controller_bytes[2]);
reportBuffer[4]=getX(last_read_controller_bytes[4]);
reportBuffer[5]=getY(last_read_controller_bytes[4]);
reportBuffer[6]=getX(last_read_controller_bytes[6]);
reportBuffer[7]=getY(last_read_controller_bytes[6]);
reportBuffer[0]=id;
reportBuffer[1]=getX(last_read_controller_bytes[idx*2]);
reportBuffer[2]=getY(last_read_controller_bytes[idx*2]);
if (nesMode & 0x01)
reportBuffer[8] = last_read_controller_bytes[0] & 0xf0;
if (nesMode & (0x01<<idx))
reportBuffer[3] = last_read_controller_bytes[idx*2] & 0xf0;
else
reportBuffer[8] = snesReorderButtons(&last_read_controller_bytes[0]);
if (nesMode & 0x02)
reportBuffer[9] = last_read_controller_bytes[2] & 0xf0;
else
reportBuffer[9] = snesReorderButtons(&last_read_controller_bytes[2]);
if (nesMode & 0x04)
reportBuffer[10] = last_read_controller_bytes[4] & 0xf0;
else
reportBuffer[10] = snesReorderButtons(&last_read_controller_bytes[4]);
if (nesMode & 0x08)
reportBuffer[11] = last_read_controller_bytes[6] & 0xf0;
else
reportBuffer[11] = snesReorderButtons(&last_read_controller_bytes[6]);
reportBuffer[3] = snesReorderButtons(&last_read_controller_bytes[idx*2]);
}
memcpy(last_reported_controller_bytes,
last_read_controller_bytes,
GAMEPAD_BYTES);
memcpy(&last_reported_controller_bytes[idx*2],
&last_read_controller_bytes[idx*2],
2);
return 4;
}
const char snes_usbHidReportDescriptor[] PROGMEM = {
/* Controller and report_id 1 */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x04, // USAGE (Joystick)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x85, 0x01, // REPORT_ID (1)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x32, // USAGE (Z)
0x09, 0x33, // USAGE (Rx)
0x09, 0x34, // USAGE (Ry)
0x09, 0x35, // USAGE (Rz)
0x09, 0x36, // USAGE (Slider)
0x09, 0x37, // USAGE (Dial)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x08, // REPORT_COUNT (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 1, // USAGE_MINIMUM (Button 1)
0x29, 32, // USAGE_MAXIMUM (Button 32)
0x29, 8, // USAGE_MAXIMUM (Button 8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 1, // REPORT_SIZE (1)
0x95, 32, // REPORT_COUNT (32)
0x95, 8, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
/* Controller and report_id 2 */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x04, // USAGE (Joystick)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x85, 0x02, // REPORT_ID (2)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 1, // USAGE_MINIMUM (Button 1)
0x29, 8, // USAGE_MAXIMUM (Button 8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 1, // REPORT_SIZE (1)
0x95, 8, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
/* Controller and report_id 3 */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x04, // USAGE (Joystick)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x85, 0x03, // REPORT_ID (3)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 1, // USAGE_MINIMUM (Button 1)
0x29, 8, // USAGE_MAXIMUM (Button 8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 1, // REPORT_SIZE (1)
0x95, 8, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
/* Controller and report_id 4 */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x04, // USAGE (Joystick)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x85, 0x04, // REPORT_ID (4)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 1, // USAGE_MINIMUM (Button 1)
0x29, 8, // USAGE_MAXIMUM (Button 8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 1, // REPORT_SIZE (1)
0x95, 8, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
Gamepad SnesGamepad = {
report_size: REPORT_SIZE,
reportDescriptorSize: sizeof(snes_usbHidReportDescriptor),
init: snesInit,
update: snesUpdate,
changed: snesChanged,
buildReport: snesBuildReport
.num_reports = 4,
.reportDescriptorSize = sizeof(snes_usbHidReportDescriptor),
.init = snesInit,
.update = snesUpdate,
.changed = snesChanged,
.buildReport = snesBuildReport
};
Gamepad *snesGetGamepad(void)

View File

@ -5,7 +5,7 @@
* Tabsize: 4
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
* License: Proprietary, free under certain conditions. See Documentation.
* This Revision: $Id: usbconfig.h,v 1.4 2007-03-28 02:29:23 raph Exp $
* This Revision: $Id: usbconfig.h,v 1.5 2007-04-18 23:58:01 raph Exp $
*/
#ifndef __usbconfig_h_included__
@ -55,7 +55,7 @@ must be adapted to your hardware.
/* Define this to 1 if the device has its own power supply. Set it to 0 if the
* device is powered from the USB bus.
*/
#define USB_CFG_MAX_BUS_POWER 100
#define USB_CFG_MAX_BUS_POWER 120
/* Set this variable to the maximum USB bus power consumption of your device.
* The value is in milliamperes. [It will be divided by two since USB
* communicates power requirements in units of 2 mA.]
@ -74,7 +74,7 @@ must be adapted to your hardware.
* transfers. Set it to 0 if you don't need it and want to save a couple of
* bytes.
*/
#define USB_CFG_IMPLEMENT_FN_READ 1
#define USB_CFG_IMPLEMENT_FN_READ 0
/* Set this to 1 if you need to send control replies which are generated
* "on the fly" when usbFunctionRead() is called. If you only want to send
* data from a static buffer, set it to 0 and return the data from
@ -95,14 +95,14 @@ must be adapted to your hardware.
* own Vendor ID, define it here. Otherwise you use obdev's free shared
* VID/PID pair. Be sure to read USBID-License.txt for rules!
*/
#define USB_CFG_DEVICE_ID 0x9c, 0x0a
#define USB_CFG_DEVICE_ID 0x9d, 0x0a
/* This is the ID of the product, low byte first. It is interpreted in the
* scope of the vendor ID. If you have registered your own VID with usb.org
* or if you have licensed a PID from somebody else, define it here. Otherwise
* you use obdev's free shared VID/PID pair. Be sure to read the rules in
* USBID-License.txt!
*/
#define USB_CFG_DEVICE_VERSION 0x00, 0x01
#define USB_CFG_DEVICE_VERSION 0x01, 0x01
/* Version number of the device: Minor number first, then major number.
*/
#define USB_CFG_VENDOR_NAME 'r', 'a', 'p', 'h', 'n', 'e', 't', '.', 'n', 'e', 't'
@ -115,10 +115,8 @@ must be adapted to your hardware.
* obdev's free shared VID/PID pair. See the file USBID-License.txt for
* details.
*/
#define USB_CFG_DEVICE_NAME 'R','a','p','n','e','t','.','n','e','t', \
'\'', 's', ' ', '8',' ','A','x','e','s', \
',',' ', '3','2',' ','B','u','t','t','o','n','s'
#define USB_CFG_DEVICE_NAME_LEN 31
#define USB_CFG_DEVICE_NAME '4','n','e','s','4','s','n','e','s'
#define USB_CFG_DEVICE_NAME_LEN 9
/* Same as above for the device name. If you don't want a device name, undefine
* the macros. See the file USBID-License.txt before you assign a name.
*/