summaryrefslogtreecommitdiffhomepage
path: root/src/kernel/HALKit/AMD64/Network/Generic+Basic+RTL8139.cc
blob: f9865139841e45f44b9b044a5cc4502873020be6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/* ========================================

Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license.

======================================== */

#include <DmaKit/DmaPool.h>
#include <HALKit/AMD64/Processor.h>
#include <modules/ACPI/ACPIFactoryInterface.h>

using namespace Kernel;
using namespace Kernel::HAL;

STATIC UInt16 kRTLIOBase = 0xFFFF;

STATIC BOOL kTXRXEnabled = NO;

STATIC UInt32                 kRXOffset     = 0UL;
STATIC constexpr CONST UInt32 kRXBufferSize = 8192 + 16 + 1500;

STATIC UInt8* kRXUpperLayer = nullptr;
STATIC UInt8* kRXBuffer     = nullptr;

/***********************************************************************************/
///@brief RTL8139 Init routine.
/***********************************************************************************/

EXTERN_C BOOL rtl_init_nic_rtl8139(UInt16 io_base) noexcept {
  if (kTXRXEnabled) return NO;

  kRTLIOBase = io_base;

  MUST_PASS(io_base != 0xFFFF);

  kRXBuffer = reinterpret_cast<UInt8*>(rtl_dma_alloc(sizeof(UInt8) * kRXBufferSize, 0));

  MUST_PASS(kRXBuffer);

  /// Reset first.

  rt_out8(io_base + 0x37, 0x10);

  UInt16 timeout = 0U;

  while (rt_in8(io_base + 0x37) & 0x10) {
    ++timeout;
    if (timeout > 0x1000) break;
  }

  if (timeout <= 0x1000) {
    return NO;
  }

  rt_out32(io_base + 0x30, (UInt32) (UIntPtr) kRXBuffer);

  rt_out8(io_base + 0x37, 0x0C);

  rt_out32(io_base + 0x44, 0xF | (1 << 7));

  rt_out16(io_base + 0x3C, 0x0005);

  kTXRXEnabled = YES;

  return YES;
}

/***********************************************************************************/
/// @brief RTL8139 I/O interrupt handler.
/// @param rsp stack pointer.
/// @note This function is called when the device interrupts to retrieve network data.
/***********************************************************************************/

EXTERN_C Void rtl_rtl8139_interrupt_handler(UIntPtr rsp) {
  if (kRTLIOBase == 0xFFFF || kRTLIOBase == 0) return;

  NE_UNUSED(rsp);

  UInt16 status = rt_in16(kRTLIOBase + 0x3E);
  rt_out16(kRTLIOBase + 0x3E, status);

  if (status & 0x01) {
    // While we receive data.
    while ((rt_in8(kRTLIOBase + 0x37) & 0x01) == 0) {
      // We grab an offset from the RX buffer.
      UInt32 offset = kRXOffset % kRXBufferSize;

      // If the offset is too high, we reset it.
      if (offset >= (kRXBufferSize - 16)) {
        kRXOffset = 0UL;
        offset    = 0UL;
      }

      volatile UInt8* packet = kRXBuffer + offset + 4;
      UInt16          len    = *(UInt16*) (kRXBuffer + offset + 2);

      kRXUpperLayer[(offset + 4)] = *packet;
      kRXOffset += (len + 4);

      rt_out16(kRTLIOBase + 0x38, (UInt16) (kRXOffset - 16));
    }
  }

  if (!(status & 0x04)) {
    err_global_get() = kErrorNoNetwork;
  }
}

/***********************************************************************************/
/// @brief RTL8139 get upper layer function
/// @return the upper layer.
/// @retval nullptr if no upper layer is set.
/// @retval pointer to the upper layer if set.
/***********************************************************************************/

EXTERN_C UInt8* rtl_rtl8139_get_upper_layer() {
  return kRXUpperLayer;
}

/***********************************************************************************/
/// @brief RTL8139 set upper layer function
/// @param layer the upper layer.
/***********************************************************************************/

EXTERN_C BOOL rtl_rtl8139_set_upper_layer(UInt8* layer) {
  if (!layer) return NO;
  kRXUpperLayer = layer;

  return YES;
}