/*
 * usbshare - public interface
 *
 * Shared between the kernel driver and the user mode client. Keep this file
 * in sync with internal/usb/driver_windows.go: both sides marshal the same
 * structures, and a mismatch corrupts memory rather than failing cleanly.
 */

#pragma once

#include <initguid.h>

/*
 * Device interface GUID. User mode enumerates this to find devices that have
 * the filter attached.
 *
 * Generate a fresh GUID if you fork this driver: two drivers exposing the
 * same interface would be indistinguishable to clients.
 */
// {8F3D2A14-6C7B-4E59-9A1D-3F5B7C8E2D40}
DEFINE_GUID(GUID_DEVINTERFACE_USBSHARE,
    0x8f3d2a14, 0x6c7b, 0x4e59, 0x9a, 0x1d, 0x3f, 0x5b, 0x7c, 0x8e, 0x2d, 0x40);

#define USBSHARE_DEVICE_TYPE 0x8000

#define USBSHARE_IOCTL(index) \
    CTL_CODE(USBSHARE_DEVICE_TYPE, 0x800 + (index), METHOD_BUFFERED, FILE_ANY_ACCESS)

/*
 * Take exclusive control of the device.
 *
 * While claimed the filter stops passing the class driver's requests down, so
 * the device stops responding to the local system and answers only to URBs
 * submitted here. The claim is bound to the file handle: closing it — or the
 * process dying — releases the device, which is what stops a crashed client
 * from leaving hardware permanently stuck.
 *
 * Input:  none
 * Output: USBSHARE_DEVICE_INFO
 */
#define IOCTL_USBSHARE_CLAIM USBSHARE_IOCTL(0)

/* Release the device back to its class driver. Input/output: none. */
#define IOCTL_USBSHARE_RELEASE USBSHARE_IOCTL(1)

/*
 * Read the raw descriptor blob: device descriptor followed by every
 * configuration descriptor, the same layout a Linux usbdevfs read returns.
 *
 * Input:  none
 * Output: raw bytes; STATUS_BUFFER_TOO_SMALL reports the needed size
 */
#define IOCTL_USBSHARE_GET_DESCRIPTORS USBSHARE_IOCTL(2)

/*
 * Submit a transfer. Completion is asynchronous: the request stays pending
 * until the device answers.
 *
 * Input:  USBSHARE_TRANSFER followed by the payload for OUT transfers
 * Output: USBSHARE_TRANSFER_RESULT followed by the payload for IN transfers
 */
#define IOCTL_USBSHARE_SUBMIT USBSHARE_IOCTL(3)

/*
 * Cancel a previously submitted transfer.
 *
 * Input:  USBSHARE_CANCEL
 * Output: none
 */
#define IOCTL_USBSHARE_CANCEL USBSHARE_IOCTL(4)

/* Select an alternate setting. Input: USBSHARE_SET_INTERFACE. */
#define IOCTL_USBSHARE_SET_INTERFACE USBSHARE_IOCTL(5)

/* Clear a stall on an endpoint. Input: USBSHARE_CLEAR_HALT. */
#define IOCTL_USBSHARE_CLEAR_HALT USBSHARE_IOCTL(6)

/* Reset the port. Input/output: none. */
#define IOCTL_USBSHARE_RESET USBSHARE_IOCTL(7)

#pragma pack(push, 1)

/* Transfer types, matching the USB endpoint attribute values. */
#define USBSHARE_TRANSFER_CONTROL     0
#define USBSHARE_TRANSFER_ISOCHRONOUS 1
#define USBSHARE_TRANSFER_BULK        2
#define USBSHARE_TRANSFER_INTERRUPT   3

/* Direction, taken from the endpoint address bit 7. */
#define USBSHARE_DIR_OUT 0
#define USBSHARE_DIR_IN  1

typedef struct _USBSHARE_DEVICE_INFO {
    USHORT VendorId;
    USHORT ProductId;
    USHORT BcdDevice;
    UCHAR  DeviceClass;
    UCHAR  DeviceSubClass;
    UCHAR  DeviceProtocol;
    UCHAR  ConfigurationValue;
    UCHAR  NumConfigurations;
    /* USB_DEVICE_SPEED_* from usbdi.h, translated by the client. */
    ULONG  Speed;
    /* Hub port number, used to build a stable bus ID. */
    ULONG  PortNumber;
} USBSHARE_DEVICE_INFO, *PUSBSHARE_DEVICE_INFO;

typedef struct _USBSHARE_TRANSFER {
    /* Caller-assigned, unique among outstanding transfers. Used to cancel. */
    ULONG64 Id;

    /* Full bEndpointAddress including the direction bit. */
    UCHAR EndpointAddress;

    /* USBSHARE_TRANSFER_* */
    UCHAR Type;

    /* USBSHARE_DIR_*, redundant with the address bit but explicit. */
    UCHAR Direction;

    UCHAR Reserved;

    /* Bytes of payload following this header (OUT), or expected (IN). */
    ULONG BufferLength;

    /* Milliseconds; 0 means no timeout. */
    ULONG Timeout;

    /*
     * Setup packet for control transfers, in USB wire order (little endian).
     * Ignored for other types.
     */
    UCHAR Setup[8];
} USBSHARE_TRANSFER, *PUSBSHARE_TRANSFER;

typedef struct _USBSHARE_TRANSFER_RESULT {
    ULONG64 Id;

    /* NTSTATUS from the USB stack; 0 means success. */
    LONG Status;

    /* USBD_STATUS, kept separate because it distinguishes stall from timeout. */
    ULONG UsbdStatus;

    /* Bytes actually transferred. Meaningful for OUT transfers too. */
    ULONG ActualLength;
} USBSHARE_TRANSFER_RESULT, *PUSBSHARE_TRANSFER_RESULT;

typedef struct _USBSHARE_CANCEL {
    ULONG64 Id;
} USBSHARE_CANCEL, *PUSBSHARE_CANCEL;

typedef struct _USBSHARE_SET_INTERFACE {
    UCHAR InterfaceNumber;
    UCHAR AlternateSetting;
} USBSHARE_SET_INTERFACE, *PUSBSHARE_SET_INTERFACE;

typedef struct _USBSHARE_CLEAR_HALT {
    UCHAR EndpointAddress;
} USBSHARE_CLEAR_HALT, *PUSBSHARE_CLEAR_HALT;

#pragma pack(pop)
