Skip to content
4 changes: 2 additions & 2 deletions lib/hal/video.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ static DWORD dwEncoderSettings = 0;
static VIDEO_MODE vmCurrent = { 0, 0, 0, 0 };
static int flickerLevel = 5;
static BOOL flickerSet = FALSE;
static BOOL softenFilter = TRUE;
static BOOL softenFilter = FALSE;
static BOOL softenSet = FALSE;
static GAMMA_RAMP_ENTRY gammaRampEntries[256];

Expand Down Expand Up @@ -381,7 +381,7 @@ void XVideoInit(DWORD dwMode, int width, int height, int bpp)
_fb = framebufferMemory;

XVideoSetFlickerFilter(5);
XVideoSetSoftenFilter(TRUE);
XVideoSetSoftenFilter(FALSE);

for(i = 0; i < 256; i++) {
defaultGammaRampEntries[i].red = i;
Expand Down
12 changes: 12 additions & 0 deletions lib/net/nvnetdrv/nvnetdrv.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ static void nvnetdrv_handle_mii_irq (uint32_t miiStatus, bool init)

if (miiStatus & NVREG_MIISTAT_LINKCHANGE) {
nvnetdrv_start_txrx();
nvnetdrv_link_state_change_callback(linkState & XNET_ETHERNET_LINK_ACTIVE);
}

INC_STAT(phy_interrupts, 1);
Expand Down Expand Up @@ -654,3 +655,14 @@ void nvnetdrv_rx_release (void *buffer_virt)
g_rxRing[index].flags = NV_RX_AVAIL;
reg32(NvRegTxRxControl) = NVREG_TXRXCTL_GET;
}

bool nvnetdrv_is_link_up (void)
{
uint32_t linkState = PhyGetLinkState(false);
return (linkState & XNET_ETHERNET_LINK_ACTIVE) != 0;
}

__attribute__((weak)) void nvnetdrv_link_state_change_callback (bool link_active)
{
(void)link_active;
}
12 changes: 12 additions & 0 deletions lib/net/nvnetdrv/nvnetdrv.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <xboxkrnl/xboxkrnl.h>

// Must be greater than max ethernet frame size. A multiple of page size prevents page boundary crossing
Expand Down Expand Up @@ -87,4 +88,15 @@ void nvnetdrv_submit_tx_descriptors (nvnetdrv_descriptor_t *buffers, size_t coun
*/
void nvnetdrv_rx_release (void *buffer_virt);

/**
* @brief Check if the link is up.
* @return true if link connected, false if link is down.
*/
bool nvnetdrv_is_link_up (void);

/**
* Called by the ISR when the link state changes.
* @param link_up True if the link is up, false if it is down.
*/
void nvnetdrv_link_state_change_callback (bool link_up);
#endif
27 changes: 25 additions & 2 deletions lib/net/nvnetdrv/nvnetdrv_lwip.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "lwip/snmp.h"
#include "lwip/stats.h"
#include "lwip/sys.h"
#include "lwip/tcpip.h"
#include "netif/etharp.h"
#include "netif/ppp/pppoe.h"
#include "nvnetdrv.h"
Expand Down Expand Up @@ -81,6 +82,17 @@ void rx_callback (void *buffer, uint16_t length)
}
}


static void change_link_state(void *ctx)
{
bool link_active = (bool)ctx;
if (link_active) {
netif_set_link_up(g_pnetif);
} else {
netif_set_link_down(g_pnetif);
}
}

/**
* In this function, the hardware should be initialized.
* Called from nforceif_init().
Expand All @@ -97,7 +109,7 @@ static err_t low_level_init (struct netif *netif)

/* device capabilities */
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;

#if LWIP_IPV6 && LWIP_IPV6_MLD
/*
Expand All @@ -123,6 +135,10 @@ static err_t low_level_init (struct netif *netif)
/* set MAC hardware address */
memcpy(netif->hwaddr, nvnetdrv_get_ethernet_addr(), 6);

// Let LWIP know if a ethernet cable is currently connected
bool link_up = nvnetdrv_is_link_up();
change_link_state((void *)link_up);

return ERR_OK;
}

Expand Down Expand Up @@ -277,4 +293,11 @@ err_t nvnetif_init (struct netif *netif)

/* initialize the hardware */
return low_level_init(netif);
}
}

void nvnetdrv_link_state_change_callback (bool link_active)
{
// ISR safe mbox to tcpip thread. This is very unlikely to fail as the mbox is large and the messages
// are allocated from the kernel's pool allocator.
tcpip_try_callback(change_link_state, (void *)link_active);
}
2 changes: 1 addition & 1 deletion lib/pdclib
15 changes: 15 additions & 0 deletions lib/usb/libusbohci_xbox/xid_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,21 @@ int32_t usbh_xid_rumble(xid_dev_t *xid_dev, uint16_t l_value, uint16_t h_value){
return usbh_xid_write(xid_dev, 0, (uint8_t *)&command, sizeof(xid_gamepad_out), NULL);
}

int32_t usbh_xid_sbc_lights(xid_dev_t *xid_dev, uint8_t lights[STEELBATTALION_LIGHT_BYTES]) {
if (xid_dev->xid_desc.bType != XID_TYPE_STEELBATTALION)
{
return USBH_ERR_NOT_SUPPORTED;
}

xid_steelbattalion_out command =
{
.startByte = 0,
.bLength = sizeof(xid_steelbattalion_out),
};
memcpy(command.lights, lights, STEELBATTALION_LIGHT_BYTES);
return usbh_xid_write(xid_dev, 0, (uint8_t *)&command, sizeof(xid_gamepad_out), NULL);
}

/**
* @brief Returns the xid_type.
*
Expand Down
52 changes: 48 additions & 4 deletions lib/usb/libusbohci_xbox/xid_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ extern "C"
struct xid_dev;
typedef void(XID_CONN_FUNC)(struct xid_dev *hdev, int param);

typedef struct __attribute__((packed))
typedef struct __attribute__((packed))
{
uint8_t bLength;
uint8_t bDescriptorType;
Expand All @@ -36,7 +36,24 @@ typedef struct __attribute__((packed))
uint16_t wAlternateProductIds[4];
} xid_descriptor;

typedef struct __attribute__((packed))
//XINPUT defines and struct format from
//https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_gamepad

@JayFoxRox JayFoxRox Jul 23, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These docs are probably intended for Xbox 360 and up (when MS publicly documented XInput and provided Windows drivers).
Original Xbox doesn't even have "LEFT_SHOULDER" and "RIGHT_SHOULDER" - it uses black and white face-buttons. A,B,X and Y are also analog buttons on original Xbox.

#define XINPUT_GAMEPAD_DPAD_UP 0x0001
#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002
#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004
#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008
#define XINPUT_GAMEPAD_START 0x0010
#define XINPUT_GAMEPAD_BACK 0x0020
#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040
#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080
#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100
#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200
#define XINPUT_GAMEPAD_A 0x1000
#define XINPUT_GAMEPAD_B 0x2000
#define XINPUT_GAMEPAD_X 0x4000
#define XINPUT_GAMEPAD_Y 0x8000

typedef struct __attribute__((packed))
{
uint8_t startByte;
uint8_t bLength;
Expand All @@ -47,8 +64,8 @@ typedef struct __attribute__((packed))
uint8_t y;
uint8_t black;
uint8_t white;
uint8_t l;
uint8_t r;
uint8_t leftTrigger;
uint8_t rightTrigger;
int16_t leftStickX;
int16_t leftStickY;
int16_t rightStickX;
Expand All @@ -63,6 +80,32 @@ typedef struct __attribute__((packed))
uint16_t hValue;
} xid_gamepad_out;

typedef struct __attribute__((packed))
{
uint8_t startByte;
uint8_t bLength;
uint16_t buttons[3];
uint16_t aimingLeverX; // 0 = Left, 0xFFFF = Right
uint16_t aimingLeverY; // 0 = Top, 0xFFFF = Bottom
int16_t turningLever;
int16_t sightChangeX;
int16_t sightChangeY;
uint16_t slidePedal;
uint16_t brakePedal;
uint16_t accelPedal;
uint8_t tuner; // 0-15 is from 9oclock, around clockwise
int8_t shifter; // -2 = R, -1 = N, 0 = Error, 1 = 1st, 2 = 2nd, 3 = 3rnd, 4 = 4th, 5 = 5th
} xid_steelbattalion_in;

#define STEELBATTALION_LIGHT_COUNT 40
#define STEELBATTALION_LIGHT_BYTES (STEELBATTALION_LIGHT_COUNT / 2) // Each nibble is 1 light
typedef struct __attribute__((packed))
{
uint8_t startByte;
uint8_t bLength;
uint8_t lights[STEELBATTALION_LIGHT_BYTES];
} xid_steelbattalion_out;

//Ref https://xboxdevwiki.net/Xbox_DVD_Movie_Playback_Kit
typedef struct __attribute__((packed))
{
Expand Down Expand Up @@ -106,6 +149,7 @@ int32_t usbh_xid_read(xid_dev_t *xid_dev, uint8_t ep_addr, void *rx_complete_cal
int32_t usbh_xid_write(xid_dev_t *xid_dev, uint8_t ep_addr, uint8_t *txbuff, uint32_t len, void *tx_complete_callback);
xid_type usbh_xid_get_type(xid_dev_t *xid_dev);
int32_t usbh_xid_rumble(xid_dev_t *xid_dev, uint16_t l_value, uint16_t h_value);
int32_t usbh_xid_sbc_lights(xid_dev_t *xid_dev, uint8_t lights[STEELBATTALION_LIGHT_BYTES]);

#ifdef __cplusplus
}
Expand Down
1 change: 1 addition & 0 deletions lib/winapi/fileapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ DWORD GetFileAttributesA (LPCSTR lpFileName);
BOOL GetFileAttributesExA (LPCSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, LPVOID lpFileInformation);
BOOL SetFileAttributesA (LPCSTR lpFileName, DWORD dwFileAttributes);

LONG CompareFileTime (const FILETIME *lpFileTime1, const FILETIME *lpFileTime2);
BOOL GetFileTime (HANDLE hFile, LPFILETIME lpCreationTime, LPFILETIME lpLastAccessTime, LPFILETIME lpLastWriteTime);
BOOL SetFileTime (HANDLE hFile, const FILETIME *lpCreationTime, const FILETIME *lpLastAccessTime, const FILETIME *lpLastWriteTime);

Expand Down
22 changes: 22 additions & 0 deletions lib/winapi/filemanip.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,28 @@ BOOL SetFileAttributesA (LPCSTR lpFileName, DWORD dwFileAttributes)
return TRUE;
}

LONG CompareFileTime (const FILETIME *lpFileTime1, const FILETIME *lpFileTime2)
{
ULARGE_INTEGER filetime1;
ULARGE_INTEGER filetime2;

assert(lpFileTime1 != NULL);
assert(lpFileTime2 != NULL);

filetime1.LowPart = lpFileTime1->dwLowDateTime;
filetime1.HighPart = lpFileTime1->dwHighDateTime;
filetime2.LowPart = lpFileTime2->dwLowDateTime;
filetime2.HighPart = lpFileTime2->dwHighDateTime;

if (filetime1.QuadPart < filetime2.QuadPart) {
return -1;
}
if (filetime1.QuadPart > filetime2.QuadPart) {
return 1;
}
return 0;
}

BOOL GetFileTime (HANDLE hFile, LPFILETIME lpCreationTime, LPFILETIME lpLastAccessTime, LPFILETIME lpLastWriteTime)
{
NTSTATUS status;
Expand Down
4 changes: 2 additions & 2 deletions lib/winapi/sysinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ void GetSystemTime (LPSYSTEMTIME lpSystemTime)
lpSystemTime->wHour = timeFields.Hour;
lpSystemTime->wMinute = timeFields.Minute;
lpSystemTime->wSecond = timeFields.Second;
lpSystemTime->wMilliseconds = timeFields.Millisecond;
lpSystemTime->wMilliseconds = timeFields.Milliseconds;
lpSystemTime->wDayOfWeek = timeFields.Weekday;
}

Expand Down Expand Up @@ -92,6 +92,6 @@ void GetLocalTime (LPSYSTEMTIME lpSystemTime)
lpSystemTime->wHour = timeFields.Hour;
lpSystemTime->wMinute = timeFields.Minute;
lpSystemTime->wSecond = timeFields.Second;
lpSystemTime->wMilliseconds = timeFields.Millisecond;
lpSystemTime->wMilliseconds = timeFields.Milliseconds;
lpSystemTime->wDayOfWeek = timeFields.Weekday;
}
59 changes: 59 additions & 0 deletions lib/winapi/timezoneapi.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// SPDX-License-Identifier: MIT

// SPDX-FileCopyrightText: 2023 Ryan Wendland
// SPDX-FileCopyrightText: 2025 Stefan Schmidt

#include <assert.h>
#include <string.h>
#include <timezoneapi.h>
#include <winerror.h>
#include <winbase.h>
#include <xboxkrnl/xboxkrnl.h>

Expand Down Expand Up @@ -210,3 +212,60 @@ DWORD GetTimeZoneInformation (LPTIME_ZONE_INFORMATION lpTimeZoneInformation)
return TIME_ZONE_ID_STANDARD;
}
}

BOOL FileTimeToSystemTime (const FILETIME *lpFileTime, LPSYSTEMTIME lpSystemTime)
{
if (!lpFileTime || !lpSystemTime) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

LARGE_INTEGER filetime;
filetime.LowPart = lpFileTime->dwLowDateTime;
filetime.HighPart = lpFileTime->dwHighDateTime;
if (filetime.QuadPart < 0) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

TIME_FIELDS timefields;
RtlTimeToTimeFields(&filetime, &timefields);

lpSystemTime->wYear = timefields.Year;
lpSystemTime->wMonth = timefields.Month;
lpSystemTime->wDay = timefields.Day;
lpSystemTime->wDayOfWeek = timefields.Weekday;
lpSystemTime->wHour = timefields.Hour;
lpSystemTime->wMinute = timefields.Minute;
lpSystemTime->wSecond = timefields.Second;
lpSystemTime->wMilliseconds = timefields.Milliseconds;

return TRUE;
}

BOOL SystemTimeToFileTime (const SYSTEMTIME *lpSystemTime, LPFILETIME lpFileTime)
{
if (!lpSystemTime || !lpFileTime) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

TIME_FIELDS timefields;
timefields.Year = lpSystemTime->wYear;
timefields.Month = lpSystemTime->wMonth;
timefields.Day = lpSystemTime->wDay;
timefields.Hour = lpSystemTime->wHour;
timefields.Minute = lpSystemTime->wMinute;
timefields.Second = lpSystemTime->wSecond;
timefields.Milliseconds = lpSystemTime->wMilliseconds;

LARGE_INTEGER filetime;
if (!RtlTimeFieldsToTime(&timefields, &filetime)) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

lpFileTime->dwLowDateTime = filetime.LowPart;
lpFileTime->dwHighDateTime = filetime.HighPart;
return TRUE;
}
4 changes: 4 additions & 0 deletions lib/winapi/timezoneapi.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT

// SPDX-FileCopyrightText: 2023 Ryan Wendland
// SPDX-FileCopyrightText: 2025 Stefan Schmidt

#ifndef __TIMEZONEAPI_H__
#define __TIMEZONEAPI_H__
Expand Down Expand Up @@ -29,6 +30,9 @@ typedef struct _TIME_ZONE_INFORMATION

DWORD GetTimeZoneInformation (LPTIME_ZONE_INFORMATION lpTimeZoneInformation);

BOOL FileTimeToSystemTime (const FILETIME *lpFileTime, LPSYSTEMTIME lpSystemTime);
BOOL SystemTimeToFileTime (const SYSTEMTIME *lpSystemTime, LPFILETIME lpFileTime);

#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 1 addition & 1 deletion lib/winapi/winbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ extern "C" {
#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000

#define THREAD_PRIORITY_ABOVE_NORMAL 1
#define THREAD_PRIOIRTY_BELOW_NORMAL (-1)
#define THREAD_PRIORITY_BELOW_NORMAL (-1)
#define THREAD_PRIORITY_HIGHEST 2
#define THREAD_PRIORITY_IDLE (-15)
#define THREAD_PRIORITY_LOWEST (-2)
Expand Down
Loading