// SPDX-License-Identifier: MPL-2.0
#ifndef SH_LIB_IOAPIC_H
#define SH_LIB_IOAPIC_H
#include "std/stdlib.h"
#include "memory/page.h"
#define SH_IOAPIC_OFFSET_IOREGSEL 0x00
#define SH_IOAPIC_OFFSET_IOWIN 0x10
#define SH_IOAPIC_REGISTER_IOAPICID 0x00
#define SH_IOAPIC_REGISTER_IOAPICVER 0x01
#define SH_IOAPIC_REGISTER_IOAPICARB 0x02
#define SH_IOAPIC_REGISTER_IOREDTBL_BASE 0x10
// IOAPIC device structure
typedef struct {
  volatile sh_uint32 *base;
  sh_uint8 ioapic_id;
  sh_uint32 gsi_base;
  sh_uint32 gsi_count; // must be completed by reading the IOAPICVER register in the sh_ioapic_init call
  sh_SPIN_LOCK lock;
} sh_ioapic_DEVICE;
// Initialize an IOAPIC device structure, intented to initialize all IOAPIC devices structures during MADT parsing
SH_STATUS sh_ioapic_init(sh_uint32 base,sh_uint8 ioapic_id,sh_uint32 gsi_base,sh_ioapic_DEVICE *ioapic);
// Initialize DevS IOAPIC backend (max_ioapic_id is the max IOAPIC id, not the number of IOAPIC counted)
SH_STATUS sh_ioapic_init_devs(sh_uint8 max_ioapic_id);
// Bind a LAPIC device to DevS LAPIC backend
SH_STATUS sh_ioapic_bind(sh_ioapic_DEVICE *lapic_dev);
// Return a pointer to an IOAPIC device based on the provided IOAPIC id
sh_ioapic_DEVICE *sh_ioapic_get_dev_ioapic_id(sh_uint64 ioapic_id);
#define SH_IOAPIC_IOREDTBL_DELIVERY_FIXED 0b000
#define SH_IOAPIC_IOREDTBL_DELIVERY_LOWEST_PRIORITY 0b001
#define SH_IOAPIC_IOREDTBL_DELIVERY_NMI 0b100
#define SH_IOAPIC_IOREDTBL_DELIVERY_INIT 0b101
#define SH_IOAPIC_IOREDTBL_DELIVERY_EXTINT 0b111
#define SH_IOAPIC_IOREDTBL_TRIGGER_EDGE 0b0
#define SH_IOAPIC_IOREDTBL_TRIGGER_LEVEL 0b1
#define SH_IOAPIC_IOREDTBL_POLARITY_HIGH 0b0
#define SH_IOAPIC_IOREDTBL_POLARITY_LOW 0b1
#define SH_IOAPIC_IOREDTBL_DEST_MODE_PHYSICAL 0b0
#define SH_IOAPIC_IOREDTBL_DEST_MODE_LOGICAL 0b1
// IOREDTBL entry structure
typedef struct {
  sh_uint32 low;
  sh_uint32 high;
} sh_ioapic_IOREDTBL_ENTRY;
// Create an IOREDTBL entry
inline sh_ioapic_IOREDTBL_ENTRY sh_ioapic_make_ioredtbl_entry(sh_uint8 vector,sh_uint8 delivery_mode,sh_uint8 destination_mode,sh_uint8 polarity,sh_uint8 trigger_mode,sh_bool mask,sh_uint8 apic_id) {
  sh_ioapic_IOREDTBL_ENTRY e={0};
  e.low=(e.low& ~0xFFU)|vector;
  e.low=(e.low& ~(0x7U<<8))|((delivery_mode&0x7U)<<8);
  e.low=(e.low& ~(1U<<11))|((destination_mode&0x1U)<<11);
  e.low=(e.low& ~(1U<<13))|((polarity&0x1U)<<13);
  e.low=(e.low& ~(1U<<15))|((trigger_mode&0x1U)<<15);
  e.low=(e.low& ~(1U<<16))|((mask&0x1U)<<16);
  e.high=(e.high& ~(0xFFU<<24))|((sh_uint32)apic_id<<24);
  return e;
}
// Mask an IOREDTBL entry for the provided GSI
SH_STATUS sh_ioapic_mask_gsi(sh_ioapic_DEVICE *dev,sh_uint32 gsi);
// Unmask an IOREDTBL entry for the provided GSI
SH_STATUS sh_ioapic_unmask_gsi(sh_ioapic_DEVICE *dev,sh_uint32 gsi);
// Read an IOREDTBL entry for the provided GSI
SH_STATUS sh_ioapic_read_ioredtbl_entry(sh_ioapic_DEVICE *ioapic_dev,sh_uint32 gsi,sh_ioapic_IOREDTBL_ENTRY *entry);
// Write an IOREDTBL entry for the provided GSI
SH_STATUS sh_ioapic_write_ioredtbl_entry(sh_ioapic_DEVICE *ioapic_dev,sh_uint32 gsi,sh_ioapic_IOREDTBL_ENTRY *entry);
// Mask all IOREDTBL entries for all IOAPIC
SH_STATUS sh_ioapic_mask_all();
// Return the IOAPIC device corresponding to the provided GSI
sh_ioapic_DEVICE *sh_ioapic_get_dev_by_gsi(sh_uint32 gsi);
// Parse an IOAPIC DevS query
SH_STATUS sh_ioapic_devs_query(char *sub_path,sh_devs_RESULT *result);
#endif
