/*
	Copyright (c) 2004 Fabrice Bellard and QEMU contributors

	This file is part of Flycast.

    Flycast 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.

    Flycast 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 Flycast.  If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "types.h"

#include <climits>
#include <cinttypes>
#include <cstring>

typedef u8 uint8_t;
typedef u16 uint16_t;
typedef u64 uint64_t;

typedef uint64_t dma_addr_t;
typedef uint64_t hwaddr;

#ifdef _MSC_VER
#if defined(_WIN64)
typedef __int64 ssize_t;
#else
typedef long ssize_t;
#endif
#endif

#define ETH_ALEN 6

#define DMA_ADDR_FMT "%" PRIx64

struct MACAddr {
    uint8_t a[6];
};

uint32_t net_crc32(const uint8_t *p, int len);

struct NetClientState {
    int link_down;
};

typedef struct NICConf {
    MACAddr macaddr;
} NICConf;

/** MemoryRegion:
 *
 * A struct representing a memory region.
 */
struct MemoryRegion {
    uint32_t size;
};

#define glue(a, b) _glue(a, b)
#define _glue(a, b) a ## b

#if defined(HOST_WORDS_BIGENDIAN)
#define be_bswap(v, size) (v)
#define le_bswap(v, size) glue(bswap, size)(v)
#define be_bswaps(v, size)
#define le_bswaps(p, size) do { *p = glue(bswap, size)(*p); } while(0)
#else
#define le_bswap(v, size) (v)
#define be_bswap(v, size) glue(bswap, size)(v)
#define le_bswaps(v, size)
#define be_bswaps(p, size) do { *p = glue(bswap, size)(*p); } while(0)
#endif

#define CPU_CONVERT(endian, size, type)\
static inline type endian ## size ## _to_cpu(type v)\
{\
    return glue(endian, _bswap)(v, size);\
}\
\
static inline type cpu_to_ ## endian ## size(type v)\
{\
    return glue(endian, _bswap)(v, size);\
}\
\
static inline void endian ## size ## _to_cpus(type *p)\
{\
    glue(endian, _bswaps)(p, size);\
}\
\
static inline void cpu_to_ ## endian ## size ## s(type *p)\
{\
    glue(endian, _bswaps)(p, size);\
}

CPU_CONVERT(le, 16, uint16_t)

CPU_CONVERT(le, 32, uint32_t)

static inline int lduw_he_p(const void *ptr)
{
    uint16_t r;
    memcpy(&r, ptr, sizeof(r));
    return r;
}
static inline void stl_he_p(void *ptr, uint32_t v)
{
    memcpy(ptr, &v, sizeof(v));
}

static inline int lduw_le_p(const void *ptr)
{
    return (uint16_t)le_bswap(lduw_he_p(ptr), 16);
}
static inline void stl_le_p(void *ptr, uint32_t v)
{
    stl_he_p(ptr, le_bswap(v, 32));
}

/*
 * PCI support
 */
static inline uint16_t pci_get_word(const uint8_t *config)
{
    return lduw_le_p(config);
}
static inline void pci_set_long(uint8_t *config, uint32_t val)
{
    stl_le_p(config, val);
}

typedef uint64_t pcibus_t;

#define PCI_DEVICE(d) ((PCIDevice *)(d))
#define RTL8139(d) ((RTL8139State *)(d));

typedef struct PCIIORegion {
    pcibus_t addr; /* current PCI mapping address. -1 means not mapped */
#define PCI_BAR_UNMAPPED (~(pcibus_t)0)
    pcibus_t size;
    uint8_t type;
} PCIIORegion;

#define PCI_ROM_SLOT 6
#define PCI_NUM_REGIONS 7

struct PCIDevice {
    /* PCI config space */
    uint8_t *config;

    /* Used to enable config checks on load. Note that writable bits are
     * never checked even if set in cmask. */
    uint8_t *cmask;

    /* Used to implement R/W bytes */
    uint8_t *wmask;

    PCIIORegion io_regions[PCI_NUM_REGIONS];
};

void pci_set_irq(PCIDevice *pci_dev, int level);
void pci_register_bar(PCIDevice *pci_dev, int region_num, uint8_t type, MemoryRegion *memory);

void pci_dma_read(PCIDevice *dev, dma_addr_t addr, void *buf, dma_addr_t len);
void pci_dma_write(PCIDevice *dev, dma_addr_t addr, const void *buf, dma_addr_t len);

#define g_malloc malloc
#define g_free free

#define PCI_VENDOR_ID_REALTEK            0x10ec
#define PCI_DEVICE_ID_REALTEK_8139       0x8139

#define FMT_PCIBUS                      PRIx64

#define PCI_HEADER_TYPE		0x0e	/* 8 bits */
#define  PCI_HEADER_TYPE_BRIDGE		1
#define  PCI_HEADER_TYPE_MULTI_FUNCTION 0x80

/*
* Base addresses specify locations in memory or I/O space.
* Decoded size can be determined by writing a value of
* 0xffffffff to the register, and reading it back.  Only
* 1 bits are decoded.
*/
#define PCI_BASE_ADDRESS_0	0x10	/* 32 bits */
#define  PCI_BASE_ADDRESS_SPACE_IO	0x01
#define  PCI_BASE_ADDRESS_SPACE_MEMORY	0x00
#define  PCI_BASE_ADDRESS_MEM_TYPE_64	0x04	/* 64 bit address */
#define PCI_ROM_ADDRESS		0x30	/* Bits 31..11 are address, 10..1 reserved */
#define  PCI_ROM_ADDRESS_ENABLE	0x01

#define PCI_CAPABILITY_LIST	0x34	/* Offset of first capability list entry */
#define PCI_INTERRUPT_PIN	0x3d	/* 8 bits */

#define PCI_ROM_ADDRESS1	0x38	/* Same as PCI_ROM_ADDRESS, but for htype 1 */

struct RTL8139State;

ssize_t qemu_send_packet(RTL8139State *s, const uint8_t *buf, int size);

void pci_rtl8139_realize(PCIDevice *dev);

uint64_t rtl8139_ioport_read(void *opaque, hwaddr addr, unsigned size);
void rtl8139_ioport_write(void *opaque, hwaddr addr, uint64_t val, unsigned size);
void rtl8139_reset(RTL8139State *s);
bool rtl8139_can_receive(RTL8139State *s);
ssize_t rtl8139_receive(RTL8139State *s, const uint8_t *buf, size_t size);

RTL8139State *rtl8139_init(NICConf *conf);
void rtl8139_destroy(RTL8139State *state);
void rtl8139_serialize(RTL8139State *state, Serializer& ser);
bool rtl8139_deserialize(RTL8139State *state, Deserializer& deser);
