limine: Drop terminal request support
This commit is contained in:
parent
2ac43a320f
commit
1ad6d6013e
|
@ -282,28 +282,6 @@ static void *_get_request(uint64_t id[4]) {
|
||||||
#define FEAT_START do {
|
#define FEAT_START do {
|
||||||
#define FEAT_END } while (0);
|
#define FEAT_END } while (0);
|
||||||
|
|
||||||
#if defined (__i386__)
|
|
||||||
extern symbol limine_term_write_entry;
|
|
||||||
void *limine_rt_stack = NULL;
|
|
||||||
uint64_t limine_term_callback_ptr = 0;
|
|
||||||
uint64_t limine_term_write_ptr = 0;
|
|
||||||
void limine_term_callback(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static uint64_t term_arg;
|
|
||||||
static void (*actual_callback)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
|
|
||||||
|
|
||||||
static void callback_shim(struct flanterm_context *ctx, uint64_t a, uint64_t b, uint64_t c, uint64_t d) {
|
|
||||||
(void)ctx;
|
|
||||||
actual_callback(term_arg, a, b, c, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO pair with specific terminal
|
|
||||||
static void term_write_shim(uint64_t context, uint64_t buf, uint64_t count) {
|
|
||||||
(void)context;
|
|
||||||
_term_write(terms[0], buf, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
noreturn void limine_load(char *config, char *cmdline) {
|
noreturn void limine_load(char *config, char *cmdline) {
|
||||||
#if defined (__x86_64__) || defined (__i386__)
|
#if defined (__x86_64__) || defined (__i386__)
|
||||||
uint32_t eax, ebx, ecx, edx;
|
uint32_t eax, ebx, ecx, edx;
|
||||||
|
@ -821,92 +799,9 @@ FEAT_END
|
||||||
parse_resolution(&req_width, &req_height, &req_bpp, resolution);
|
parse_resolution(&req_width, &req_height, &req_bpp, resolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t *term_fb_ptr = NULL;
|
|
||||||
uint64_t term_fb_addr;
|
|
||||||
|
|
||||||
struct fb_info *fbs;
|
struct fb_info *fbs;
|
||||||
size_t fbs_count;
|
size_t fbs_count;
|
||||||
|
|
||||||
// Terminal feature
|
|
||||||
FEAT_START
|
|
||||||
struct limine_terminal_request *terminal_request = get_request(LIMINE_TERMINAL_REQUEST);
|
|
||||||
if (terminal_request == NULL) {
|
|
||||||
break; // next feature
|
|
||||||
}
|
|
||||||
|
|
||||||
struct limine_terminal_response *terminal_response =
|
|
||||||
ext_mem_alloc(sizeof(struct limine_terminal_response));
|
|
||||||
|
|
||||||
terminal_response->revision = 1;
|
|
||||||
|
|
||||||
struct limine_terminal *terminal = ext_mem_alloc(sizeof(struct limine_terminal));
|
|
||||||
|
|
||||||
quiet = false;
|
|
||||||
serial = false;
|
|
||||||
|
|
||||||
char *term_conf_override_s = config_get_value(config, 0, "TERM_CONFIG_OVERRIDE");
|
|
||||||
if (term_conf_override_s != NULL && strcmp(term_conf_override_s, "yes") == 0) {
|
|
||||||
if (!gterm_init(&fbs, &fbs_count, config, req_width, req_height)) {
|
|
||||||
goto term_fail;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!gterm_init(&fbs, &fbs_count, NULL, req_width, req_height)) {
|
|
||||||
goto term_fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0) {
|
|
||||||
term_fail:
|
|
||||||
pmm_free(terminal, sizeof(struct limine_terminal));
|
|
||||||
pmm_free(terminal_response, sizeof(struct limine_terminal_response));
|
|
||||||
break; // next feature
|
|
||||||
}
|
|
||||||
|
|
||||||
if (terminal_request->callback != 0) {
|
|
||||||
terms[0]->callback = callback_shim;
|
|
||||||
|
|
||||||
#if defined (__i386__)
|
|
||||||
actual_callback = (void *)limine_term_callback;
|
|
||||||
limine_term_callback_ptr = terminal_request->callback;
|
|
||||||
#elif defined (__x86_64__) || defined (__aarch64__) || defined (__riscv64)
|
|
||||||
actual_callback = (void *)terminal_request->callback;
|
|
||||||
#else
|
|
||||||
#error Unknown architecture
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
term_arg = reported_addr(terminal);
|
|
||||||
|
|
||||||
#if defined (__i386__)
|
|
||||||
if (limine_rt_stack == NULL) {
|
|
||||||
limine_rt_stack = ext_mem_alloc(16384) + 16384;
|
|
||||||
}
|
|
||||||
|
|
||||||
limine_term_write_ptr = (uintptr_t)term_write_shim;
|
|
||||||
terminal_response->write = (uintptr_t)(void *)limine_term_write_entry;
|
|
||||||
#elif defined (__x86_64__) || defined (__aarch64__) || defined (__riscv64)
|
|
||||||
terminal_response->write = (uintptr_t)term_write_shim;
|
|
||||||
#else
|
|
||||||
#error Unknown architecture
|
|
||||||
#endif
|
|
||||||
|
|
||||||
term_fb_ptr = &terminal->framebuffer;
|
|
||||||
term_fb_addr = reported_addr((void *)(((struct flanterm_fb_context *)terms[0])->framebuffer));
|
|
||||||
|
|
||||||
terminal->columns = terms[0]->cols;
|
|
||||||
terminal->rows = terms[0]->rows;
|
|
||||||
|
|
||||||
uint64_t *term_list = ext_mem_alloc(1 * sizeof(uint64_t));
|
|
||||||
term_list[0] = reported_addr(terminal);
|
|
||||||
|
|
||||||
terminal_response->terminal_count = 1;
|
|
||||||
terminal_response->terminals = reported_addr(term_list);
|
|
||||||
|
|
||||||
terminal_request->response = reported_addr(terminal_response);
|
|
||||||
|
|
||||||
goto skip_fb_init;
|
|
||||||
FEAT_END
|
|
||||||
|
|
||||||
term_notready();
|
term_notready();
|
||||||
|
|
||||||
fb_init(&fbs, &fbs_count, req_width, req_height, req_bpp);
|
fb_init(&fbs, &fbs_count, req_width, req_height, req_bpp);
|
||||||
|
@ -914,7 +809,6 @@ FEAT_END
|
||||||
goto no_fb;
|
goto no_fb;
|
||||||
}
|
}
|
||||||
|
|
||||||
skip_fb_init:
|
|
||||||
for (size_t i = 0; i < fbs_count; i++) {
|
for (size_t i = 0; i < fbs_count; i++) {
|
||||||
memmap_alloc_range(fbs[i].framebuffer_addr,
|
memmap_alloc_range(fbs[i].framebuffer_addr,
|
||||||
(uint64_t)fbs[i].framebuffer_pitch * fbs[i].framebuffer_height,
|
(uint64_t)fbs[i].framebuffer_pitch * fbs[i].framebuffer_height,
|
||||||
|
@ -924,7 +818,7 @@ skip_fb_init:
|
||||||
// Framebuffer feature
|
// Framebuffer feature
|
||||||
FEAT_START
|
FEAT_START
|
||||||
struct limine_framebuffer_request *framebuffer_request = get_request(LIMINE_FRAMEBUFFER_REQUEST);
|
struct limine_framebuffer_request *framebuffer_request = get_request(LIMINE_FRAMEBUFFER_REQUEST);
|
||||||
if (framebuffer_request == NULL && term_fb_ptr == NULL) {
|
if (framebuffer_request == NULL) {
|
||||||
break; // next feature
|
break; // next feature
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -973,14 +867,6 @@ FEAT_START
|
||||||
if (framebuffer_request != NULL) {
|
if (framebuffer_request != NULL) {
|
||||||
framebuffer_request->response = reported_addr(framebuffer_response);
|
framebuffer_request->response = reported_addr(framebuffer_response);
|
||||||
}
|
}
|
||||||
if (term_fb_ptr != NULL) {
|
|
||||||
for (size_t i = 0; i < fbs_count; i++) {
|
|
||||||
if (fbp[i].address == term_fb_addr) {
|
|
||||||
*term_fb_ptr = reported_addr(&fbp[i]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FEAT_END
|
FEAT_END
|
||||||
|
|
||||||
no_fb:
|
no_fb:
|
||||||
|
@ -1228,9 +1114,6 @@ FEAT_START
|
||||||
memmap_request->response = reported_addr(memmap_response);
|
memmap_request->response = reported_addr(memmap_response);
|
||||||
FEAT_END
|
FEAT_END
|
||||||
|
|
||||||
// Clear terminal for kernels that will use the Limine terminal
|
|
||||||
FOR_TERM(flanterm_write(TERM, "\e[2J\e[H", 7));
|
|
||||||
|
|
||||||
#if defined (__x86_64__) || defined (__i386__)
|
#if defined (__x86_64__) || defined (__i386__)
|
||||||
#if defined (BIOS)
|
#if defined (BIOS)
|
||||||
// If we're going 64, we might as well call this BIOS interrupt
|
// If we're going 64, we might as well call this BIOS interrupt
|
||||||
|
|
|
@ -1,120 +0,0 @@
|
||||||
section .bss
|
|
||||||
|
|
||||||
user_stack:
|
|
||||||
resq 1
|
|
||||||
|
|
||||||
user_cs: resq 1
|
|
||||||
user_ds: resq 1
|
|
||||||
user_es: resq 1
|
|
||||||
user_ss: resq 1
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
extern term_write
|
|
||||||
extern limine_rt_stack
|
|
||||||
extern limine_term_callback_ptr
|
|
||||||
extern limine_term_write_ptr
|
|
||||||
|
|
||||||
global limine_term_callback
|
|
||||||
limine_term_callback:
|
|
||||||
bits 32
|
|
||||||
push ebp
|
|
||||||
mov ebp, esp
|
|
||||||
|
|
||||||
push ebx
|
|
||||||
push esi
|
|
||||||
push edi
|
|
||||||
|
|
||||||
; Go 64
|
|
||||||
push 0x28
|
|
||||||
push .mode64
|
|
||||||
retfd
|
|
||||||
bits 64
|
|
||||||
.mode64:
|
|
||||||
mov eax, 0x30
|
|
||||||
mov ds, ax
|
|
||||||
mov es, ax
|
|
||||||
mov ss, ax
|
|
||||||
|
|
||||||
mov rdi, [rbp + 8]
|
|
||||||
mov rsi, [rbp + 16]
|
|
||||||
mov rdx, [rbp + 24]
|
|
||||||
mov rcx, [rbp + 32]
|
|
||||||
mov r8, [rbp + 40]
|
|
||||||
|
|
||||||
mov rbx, rsp
|
|
||||||
mov rsp, [user_stack]
|
|
||||||
call [limine_term_callback_ptr]
|
|
||||||
mov rsp, rbx
|
|
||||||
|
|
||||||
; Go 32
|
|
||||||
push 0x18
|
|
||||||
push .mode32
|
|
||||||
retfq
|
|
||||||
bits 32
|
|
||||||
.mode32:
|
|
||||||
mov eax, 0x20
|
|
||||||
mov ds, ax
|
|
||||||
mov es, ax
|
|
||||||
mov ss, ax
|
|
||||||
|
|
||||||
pop edi
|
|
||||||
pop esi
|
|
||||||
pop ebx
|
|
||||||
pop ebp
|
|
||||||
|
|
||||||
ret
|
|
||||||
|
|
||||||
global limine_term_write_entry
|
|
||||||
limine_term_write_entry:
|
|
||||||
bits 64
|
|
||||||
push rbx
|
|
||||||
push rbp
|
|
||||||
push r12
|
|
||||||
push r13
|
|
||||||
push r14
|
|
||||||
push r15
|
|
||||||
|
|
||||||
mov [user_stack], rsp
|
|
||||||
mov esp, [limine_rt_stack]
|
|
||||||
|
|
||||||
mov word [user_cs], cs
|
|
||||||
mov word [user_ds], ds
|
|
||||||
mov word [user_es], es
|
|
||||||
mov word [user_ss], ss
|
|
||||||
|
|
||||||
push rdx
|
|
||||||
push rsi
|
|
||||||
push rdi
|
|
||||||
|
|
||||||
push 0x18
|
|
||||||
push .mode32
|
|
||||||
retfq
|
|
||||||
bits 32
|
|
||||||
.mode32:
|
|
||||||
mov eax, 0x20
|
|
||||||
mov ds, ax
|
|
||||||
mov es, ax
|
|
||||||
mov ss, ax
|
|
||||||
|
|
||||||
call [limine_term_write_ptr]
|
|
||||||
add esp, 24
|
|
||||||
|
|
||||||
push dword [user_cs]
|
|
||||||
push .mode64
|
|
||||||
retfd
|
|
||||||
bits 64
|
|
||||||
.mode64:
|
|
||||||
mov ds, word [user_ds]
|
|
||||||
mov es, word [user_es]
|
|
||||||
mov ss, word [user_ss]
|
|
||||||
mov rsp, [user_stack]
|
|
||||||
|
|
||||||
pop r15
|
|
||||||
pop r14
|
|
||||||
pop r13
|
|
||||||
pop r12
|
|
||||||
pop rbp
|
|
||||||
pop rbx
|
|
||||||
|
|
||||||
ret
|
|
|
@ -1,140 +0,0 @@
|
||||||
extern _GLOBAL_OFFSET_TABLE_
|
|
||||||
|
|
||||||
section .bss
|
|
||||||
|
|
||||||
user_stack:
|
|
||||||
resq 1
|
|
||||||
|
|
||||||
user_cs: resq 1
|
|
||||||
user_ds: resq 1
|
|
||||||
user_es: resq 1
|
|
||||||
user_ss: resq 1
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
extern term_write
|
|
||||||
extern limine_rt_stack
|
|
||||||
extern limine_term_callback_ptr
|
|
||||||
extern limine_term_write_ptr
|
|
||||||
|
|
||||||
global limine_term_callback
|
|
||||||
limine_term_callback:
|
|
||||||
bits 32
|
|
||||||
push ebp
|
|
||||||
mov ebp, esp
|
|
||||||
|
|
||||||
push ebx
|
|
||||||
push esi
|
|
||||||
push edi
|
|
||||||
|
|
||||||
; Go 64
|
|
||||||
push 0x28
|
|
||||||
call .p1
|
|
||||||
.p1:
|
|
||||||
add dword [esp], .mode64 - .p1
|
|
||||||
retfd
|
|
||||||
bits 64
|
|
||||||
.mode64:
|
|
||||||
mov eax, 0x30
|
|
||||||
mov ds, ax
|
|
||||||
mov es, ax
|
|
||||||
mov ss, ax
|
|
||||||
|
|
||||||
mov rdi, [rbp + 8]
|
|
||||||
mov rsi, [rbp + 16]
|
|
||||||
mov rdx, [rbp + 24]
|
|
||||||
mov rcx, [rbp + 32]
|
|
||||||
mov r8, [rbp + 40]
|
|
||||||
|
|
||||||
call .get_got
|
|
||||||
.get_got:
|
|
||||||
pop rax
|
|
||||||
add rax, _GLOBAL_OFFSET_TABLE_ + $$ - .get_got wrt ..gotpc
|
|
||||||
|
|
||||||
mov rbx, rsp
|
|
||||||
mov rsp, [rax + user_stack wrt ..gotoff]
|
|
||||||
call [rax + limine_term_callback_ptr wrt ..gotoff]
|
|
||||||
mov rsp, rbx
|
|
||||||
|
|
||||||
; Go 32
|
|
||||||
push 0x18
|
|
||||||
call .p2
|
|
||||||
.p2:
|
|
||||||
add qword [rsp], .mode32 - .p2
|
|
||||||
retfq
|
|
||||||
bits 32
|
|
||||||
.mode32:
|
|
||||||
mov eax, 0x20
|
|
||||||
mov ds, ax
|
|
||||||
mov es, ax
|
|
||||||
mov ss, ax
|
|
||||||
|
|
||||||
pop edi
|
|
||||||
pop esi
|
|
||||||
pop ebx
|
|
||||||
pop ebp
|
|
||||||
|
|
||||||
ret
|
|
||||||
|
|
||||||
bits 64
|
|
||||||
global limine_term_write_entry
|
|
||||||
limine_term_write_entry:
|
|
||||||
push rbx
|
|
||||||
push rbp
|
|
||||||
push r12
|
|
||||||
push r13
|
|
||||||
push r14
|
|
||||||
push r15
|
|
||||||
|
|
||||||
call .get_got
|
|
||||||
.get_got:
|
|
||||||
pop rbx
|
|
||||||
add ebx, _GLOBAL_OFFSET_TABLE_ + $$ - .get_got wrt ..gotpc
|
|
||||||
|
|
||||||
mov [rbx + user_stack wrt ..gotoff], rsp
|
|
||||||
mov esp, [rbx + limine_rt_stack wrt ..gotoff]
|
|
||||||
|
|
||||||
mov word [rbx + user_cs wrt ..gotoff], cs
|
|
||||||
mov word [rbx + user_ds wrt ..gotoff], ds
|
|
||||||
mov word [rbx + user_es wrt ..gotoff], es
|
|
||||||
mov word [rbx + user_ss wrt ..gotoff], ss
|
|
||||||
|
|
||||||
push rdx
|
|
||||||
push rsi
|
|
||||||
push rdi
|
|
||||||
|
|
||||||
push 0x18
|
|
||||||
call .p1
|
|
||||||
.p1:
|
|
||||||
add qword [rsp], .mode32 - .p1
|
|
||||||
retfq
|
|
||||||
bits 32
|
|
||||||
.mode32:
|
|
||||||
mov eax, 0x20
|
|
||||||
mov ds, ax
|
|
||||||
mov es, ax
|
|
||||||
mov ss, ax
|
|
||||||
|
|
||||||
call [ebx + limine_term_write_ptr wrt ..gotoff]
|
|
||||||
add esp, 24
|
|
||||||
|
|
||||||
push dword [ebx + user_cs wrt ..gotoff]
|
|
||||||
call .p2
|
|
||||||
.p2:
|
|
||||||
add dword [esp], .mode64 - .p2
|
|
||||||
retfd
|
|
||||||
bits 64
|
|
||||||
.mode64:
|
|
||||||
mov ds, word [rbx + user_ds wrt ..gotoff]
|
|
||||||
mov es, word [rbx + user_es wrt ..gotoff]
|
|
||||||
mov ss, word [rbx + user_ss wrt ..gotoff]
|
|
||||||
mov rsp, [rbx + user_stack wrt ..gotoff]
|
|
||||||
|
|
||||||
pop r15
|
|
||||||
pop r14
|
|
||||||
pop r13
|
|
||||||
pop r12
|
|
||||||
pop rbp
|
|
||||||
pop rbx
|
|
||||||
|
|
||||||
ret
|
|
|
@ -2,13 +2,9 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void (*limine_print)(const char *buf, size_t size) = NULL;
|
|
||||||
|
|
||||||
static const char CONVERSION_TABLE[] = "0123456789abcdef";
|
static const char CONVERSION_TABLE[] = "0123456789abcdef";
|
||||||
|
|
||||||
void e9_putc(char c) {
|
void e9_putc(char c) {
|
||||||
if (limine_print != NULL)
|
|
||||||
limine_print(&c, 1);
|
|
||||||
#if defined (__x86_64__) || defined (__i386__)
|
#if defined (__x86_64__) || defined (__i386__)
|
||||||
__asm__ __volatile__ ("outb %0, %1" :: "a" (c), "Nd" (0xe9) : "memory");
|
__asm__ __volatile__ ("outb %0, %1" :: "a" (c), "Nd" (0xe9) : "memory");
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
extern void (*limine_print)(const char *buf, size_t size);
|
|
||||||
|
|
||||||
void e9_putc(char c);
|
void e9_putc(char c);
|
||||||
void e9_print(const char *msg);
|
void e9_print(const char *msg);
|
||||||
void e9_puts(const char *msg);
|
void e9_puts(const char *msg);
|
||||||
|
|
|
@ -135,14 +135,6 @@ struct limine_smp_request _smp_request = {
|
||||||
__attribute__((section(".limine_reqs")))
|
__attribute__((section(".limine_reqs")))
|
||||||
void *smp_req = &_smp_request;
|
void *smp_req = &_smp_request;
|
||||||
|
|
||||||
struct limine_terminal_request _terminal_request = {
|
|
||||||
.id = LIMINE_TERMINAL_REQUEST,
|
|
||||||
.revision = 0, .response = NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
__attribute__((section(".limine_reqs")))
|
|
||||||
void *terminal_req = &_terminal_request;
|
|
||||||
|
|
||||||
struct limine_dtb_request _dtb_request = {
|
struct limine_dtb_request _dtb_request = {
|
||||||
.id = LIMINE_DTB_REQUEST,
|
.id = LIMINE_DTB_REQUEST,
|
||||||
.revision = 0, .response = NULL
|
.revision = 0, .response = NULL
|
||||||
|
@ -240,17 +232,7 @@ void ap_entry(struct limine_smp_info *info) {
|
||||||
|
|
||||||
extern char kernel_start[];
|
extern char kernel_start[];
|
||||||
|
|
||||||
static void write_shim(const char *s, uint64_t l) {
|
|
||||||
struct limine_terminal *terminal = _terminal_request.response->terminals[0];
|
|
||||||
|
|
||||||
_terminal_request.response->write(terminal, s, l);
|
|
||||||
}
|
|
||||||
|
|
||||||
void limine_main(void) {
|
void limine_main(void) {
|
||||||
if (_terminal_request.response) {
|
|
||||||
limine_print = write_shim;
|
|
||||||
}
|
|
||||||
|
|
||||||
e9_printf("\nWe're alive");
|
e9_printf("\nWe're alive");
|
||||||
|
|
||||||
uint64_t kernel_slide = (uint64_t)kernel_start - 0xffffffff80000000;
|
uint64_t kernel_slide = (uint64_t)kernel_start - 0xffffffff80000000;
|
||||||
|
@ -458,24 +440,6 @@ FEAT_START
|
||||||
}
|
}
|
||||||
FEAT_END
|
FEAT_END
|
||||||
|
|
||||||
FEAT_START
|
|
||||||
e9_printf("");
|
|
||||||
if (_terminal_request.response == NULL) {
|
|
||||||
e9_printf("Terminal not passed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
struct limine_terminal_response *term_response = _terminal_request.response;
|
|
||||||
e9_printf("Terminal feature, revision %d", term_response->revision);
|
|
||||||
e9_printf("%d terminal(s)", term_response->terminal_count);
|
|
||||||
for (size_t i = 0; i < term_response->terminal_count; i++) {
|
|
||||||
struct limine_terminal *terminal = term_response->terminals[i];
|
|
||||||
e9_printf("Columns: %d", terminal->columns);
|
|
||||||
e9_printf("Rows: %d", terminal->rows);
|
|
||||||
e9_printf("Using framebuffer: %x", terminal->framebuffer);
|
|
||||||
}
|
|
||||||
e9_printf("Write function at: %x", term_response->write);
|
|
||||||
FEAT_END
|
|
||||||
|
|
||||||
FEAT_START
|
FEAT_START
|
||||||
e9_printf("");
|
e9_printf("");
|
||||||
if (_dtb_request.response == NULL) {
|
if (_dtb_request.response == NULL) {
|
||||||
|
|
Loading…
Reference in New Issue