mirror of
https://github.com/AsahiLinux/m1n1
synced 2025-03-07 00:37:14 +00:00
WIP: dcp/dptx: Add support for dcp endpoint dptx
Supposedly required for HDMI out initilization on M2 (Pro)? Mac minis.
This commit is contained in:
parent
cdb6d087b2
commit
32431d8ba1
12 changed files with 1183 additions and 28 deletions
8
Makefile
8
Makefile
|
@ -73,6 +73,9 @@ LIBFDT_OBJECTS := $(patsubst %,libfdt/%, \
|
|||
fdt_addresses.o fdt_empty_tree.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_sw.o \
|
||||
fdt_wip.o fdt.o)
|
||||
|
||||
DCP_OBJECTS := $(patsubst %,dcp/%, \
|
||||
dptxep.o parser.o)
|
||||
|
||||
OBJECTS := \
|
||||
adt.o \
|
||||
afk.o \
|
||||
|
@ -128,6 +131,7 @@ OBJECTS := \
|
|||
utils.o utils_asm.o \
|
||||
vsprintf.o \
|
||||
wdt.o \
|
||||
$(DCP_OBJECTS) \
|
||||
$(MINILZLIB_OBJECTS) $(TINF_OBJECTS) $(DLMALLOC_OBJECTS) $(LIBFDT_OBJECTS) $(RUST_LIBS)
|
||||
|
||||
FP_OBJECTS := \
|
||||
|
@ -149,9 +153,9 @@ all: update_tag update_cfg build/$(TARGET) build/$(TARGET_RAW)
|
|||
clean:
|
||||
rm -rf build/*
|
||||
format:
|
||||
$(CLANG_FORMAT) -i src/*.c src/math/*.c src/*.h src/math/*.h sysinc/*.h
|
||||
$(CLANG_FORMAT) -i src/*.c src/dcp/*.c src/math/*.c src/*.h src/dcp/*.h src/math/*.h sysinc/*.h
|
||||
format-check:
|
||||
$(CLANG_FORMAT) --dry-run --Werror src/*.c src/*.h sysinc/*.h
|
||||
$(CLANG_FORMAT) --dry-run --Werror src/*.c src/*.h src/dcp/*.c src/dcp/*.h sysinc/*.h
|
||||
rustfmt:
|
||||
cd rust && cargo fmt
|
||||
rustfmt-check:
|
||||
|
|
188
src/afk.c
188
src/afk.c
|
@ -38,10 +38,6 @@ enum EPICCategory {
|
|||
CAT_COMMAND = 0x30,
|
||||
};
|
||||
|
||||
enum EPICMessage {
|
||||
CODE_ANNOUNCE = 0x30,
|
||||
};
|
||||
|
||||
struct afk_qe {
|
||||
u32 magic;
|
||||
u32 size;
|
||||
|
@ -62,11 +58,12 @@ struct epic_sub_hdr {
|
|||
u32 length;
|
||||
u8 version;
|
||||
u8 category;
|
||||
u16 code;
|
||||
u16 type;
|
||||
u64 timestamp;
|
||||
u16 seq;
|
||||
u16 unk;
|
||||
u32 unk2;
|
||||
u8 unk;
|
||||
u8 flags;
|
||||
u32 inline_len;
|
||||
} PACKED;
|
||||
|
||||
struct epic_announce {
|
||||
|
@ -80,8 +77,11 @@ struct epic_cmd {
|
|||
u64 txbuf;
|
||||
u32 rxlen;
|
||||
u32 txlen;
|
||||
u16 pad;
|
||||
} PACKED;
|
||||
|
||||
#define AFK_MAX_CHANNEL 16
|
||||
|
||||
struct afk_epic_ep {
|
||||
int ep;
|
||||
rtkit_dev_t *rtk;
|
||||
|
@ -96,6 +96,10 @@ struct afk_epic_ep {
|
|||
struct rtkit_buffer rxbuf;
|
||||
|
||||
bool started;
|
||||
u16 seq;
|
||||
|
||||
const afk_epic_service_ops_t *ops;
|
||||
afk_epic_service_t services[AFK_MAX_CHANNEL];
|
||||
};
|
||||
|
||||
enum RBEP_MSG {
|
||||
|
@ -345,8 +349,118 @@ static void afk_epic_rx_ack(afk_epic_ep_t *epic)
|
|||
rb->hdr->rptr = rptr;
|
||||
}
|
||||
|
||||
int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 code, void *txbuf, size_t txsize,
|
||||
void *rxbuf, size_t *rxsize)
|
||||
struct epic_std_service_ap_call {
|
||||
u32 unk0;
|
||||
u32 unk1;
|
||||
u32 type;
|
||||
u32 len;
|
||||
u32 magic;
|
||||
u8 _unk[48];
|
||||
} PACKED;
|
||||
|
||||
static int afk_epic_handle_std_service(afk_epic_ep_t *epic, int channel, struct epic_hdr *hdr,
|
||||
struct epic_sub_hdr *sub, void *payload, size_t payload_size)
|
||||
{
|
||||
afk_epic_service_t *service = &epic->services[channel];
|
||||
|
||||
if (sub->category == CAT_NOTIFY) {
|
||||
struct epic_std_service_ap_call *call = payload;
|
||||
size_t call_size;
|
||||
void *reply;
|
||||
int ret;
|
||||
|
||||
if (payload_size < sizeof(*call))
|
||||
return -1;
|
||||
|
||||
call_size = call->len;
|
||||
if (payload_size < sizeof(*call) + call_size)
|
||||
return -1;
|
||||
|
||||
if (!service->ops->call)
|
||||
return 0;
|
||||
reply = calloc(payload_size, 1);
|
||||
if (!reply)
|
||||
return -1;
|
||||
|
||||
ret = service->ops->call(service, call->type, payload + sizeof(*call), call_size,
|
||||
reply + sizeof(*call), call_size);
|
||||
if (ret) {
|
||||
free(reply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
memcpy(reply, call, sizeof(*call));
|
||||
|
||||
size_t tx_size = sizeof(struct epic_hdr) + sizeof(struct epic_sub_hdr) + payload_size;
|
||||
void *msg = calloc(tx_size, 1);
|
||||
|
||||
u32 sub_seq = sub->seq;
|
||||
|
||||
struct epic_hdr *hdr = msg;
|
||||
struct epic_sub_hdr *sub = msg + sizeof(struct epic_hdr);
|
||||
|
||||
hdr->version = 2;
|
||||
hdr->seq = epic->seq++;
|
||||
|
||||
sub->length = payload_size;
|
||||
sub->version = 4;
|
||||
sub->category = CAT_REPLY;
|
||||
sub->type = CODE_STD_SERVICE;
|
||||
sub->seq = sub_seq; // service->seq++;
|
||||
sub->flags = 0x08;
|
||||
sub->inline_len = payload_size;
|
||||
|
||||
memcpy(msg + sizeof(struct epic_hdr) + sizeof(struct epic_sub_hdr), reply, payload_size);
|
||||
|
||||
afk_epic_tx(epic, channel, TYPE_NOTIFY_ACK, msg, tx_size);
|
||||
free(reply);
|
||||
free(msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("AFK: channel %d received unhandled standard service message: %x\n", channel,
|
||||
sub->category);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int afk_epic_report(afk_epic_ep_t *epic, int channel, u32 type, u16 sub_seq, void *payload,
|
||||
size_t payload_size)
|
||||
{
|
||||
struct epic_hdr *hdr;
|
||||
struct epic_sub_hdr *sub;
|
||||
size_t bfr_size = sizeof(*hdr) + sizeof(*sub) + payload_size;
|
||||
void *bfr = calloc(bfr_size, 1);
|
||||
if (!bfr)
|
||||
return -1;
|
||||
|
||||
memset(bfr, 0, bfr_size);
|
||||
hdr = bfr;
|
||||
sub = bfr + sizeof(*hdr);
|
||||
|
||||
hdr->version = 2;
|
||||
hdr->seq = epic->seq++;
|
||||
sub->length = payload_size;
|
||||
sub->version = 4;
|
||||
sub->category = CAT_REPORT;
|
||||
sub->type = type;
|
||||
sub->seq = sub_seq;
|
||||
|
||||
memcpy(bfr + sizeof(*hdr) + sizeof(*sub), payload, payload_size);
|
||||
|
||||
int ret = afk_epic_tx(epic, channel, TYPE_NOTIFY, bfr, bfr_size);
|
||||
free(bfr);
|
||||
if (ret < 0) {
|
||||
printf("EPIC: failed to transmit notify\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 sub_seq, u16 code, void *txbuf,
|
||||
size_t txsize, void *rxbuf, size_t *rxsize)
|
||||
{
|
||||
struct {
|
||||
struct epic_hdr hdr;
|
||||
|
@ -359,13 +473,15 @@ int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 code, void *txbuf, si
|
|||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
u32 tag = epic->seq;
|
||||
|
||||
msg.hdr.version = 2;
|
||||
msg.hdr.seq = 0;
|
||||
msg.hdr.seq = epic->seq++;
|
||||
msg.sub.length = sizeof(msg.cmd);
|
||||
msg.sub.version = 3;
|
||||
msg.sub.version = 4;
|
||||
msg.sub.category = CAT_COMMAND;
|
||||
msg.sub.code = code;
|
||||
msg.sub.seq = 0;
|
||||
msg.sub.type = code;
|
||||
msg.sub.seq = sub_seq;
|
||||
msg.cmd.txbuf = epic->txbuf.dva;
|
||||
msg.cmd.txlen = txsize;
|
||||
msg.cmd.rxbuf = epic->rxbuf.dva;
|
||||
|
@ -396,9 +512,16 @@ int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 code, void *txbuf, si
|
|||
struct epic_hdr *hdr = (void *)(rmsg + 1);
|
||||
struct epic_sub_hdr *sub = (void *)(hdr + 1);
|
||||
|
||||
if (sub->category != CAT_REPLY || sub->code != code) {
|
||||
if (sub->category == CAT_NOTIFY && sub->type == CODE_STD_SERVICE) {
|
||||
void *payload = rmsg->data + sizeof(struct epic_hdr) + sizeof(struct epic_sub_hdr);
|
||||
size_t payload_size =
|
||||
rmsg->size - sizeof(struct epic_hdr) - sizeof(struct epic_sub_hdr);
|
||||
afk_epic_rx_ack(epic);
|
||||
afk_epic_handle_std_service(epic, channel, hdr, sub, payload, payload_size);
|
||||
continue;
|
||||
} else if (sub->category != CAT_REPLY || sub->type != code) {
|
||||
printf("EPIC: got unexpected message %02x:%04x during command\n", sub->category,
|
||||
sub->code);
|
||||
sub->type);
|
||||
afk_epic_rx_ack(epic);
|
||||
continue;
|
||||
}
|
||||
|
@ -482,7 +605,8 @@ int afk_epic_shutdown(afk_epic_ep_t *epic)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int afk_epic_start_interface(afk_epic_ep_t *epic, char *name, size_t txsize, size_t rxsize)
|
||||
int afk_epic_start_channel(afk_epic_ep_t *epic, const afk_epic_service_ops_t *ops, void *intf,
|
||||
const char *name)
|
||||
{
|
||||
int channel = -1;
|
||||
struct afk_qe *msg;
|
||||
|
@ -503,9 +627,9 @@ int afk_epic_start_interface(afk_epic_ep_t *epic, char *name, size_t txsize, siz
|
|||
struct epic_hdr *hdr = (void *)(msg + 1);
|
||||
struct epic_sub_hdr *sub = (void *)(hdr + 1);
|
||||
|
||||
if (sub->category != CAT_REPORT || sub->code != CODE_ANNOUNCE) {
|
||||
if (sub->category != CAT_REPORT || sub->type != CODE_ANNOUNCE) {
|
||||
printf("EPIC: got unexpected message %02x:%04x during iface start\n", sub->category,
|
||||
sub->code);
|
||||
sub->type);
|
||||
afk_epic_rx_ack(epic);
|
||||
continue;
|
||||
}
|
||||
|
@ -518,10 +642,34 @@ int afk_epic_start_interface(afk_epic_ep_t *epic, char *name, size_t txsize, siz
|
|||
continue;
|
||||
}
|
||||
|
||||
size_t props_size = sub->length - offsetof(struct epic_announce, props);
|
||||
afk_epic_service_t *service = &epic->services[msg->channel];
|
||||
service->ops = ops;
|
||||
service->epic = epic;
|
||||
service->intf = intf;
|
||||
service->channel = msg->channel;
|
||||
|
||||
if (ops && ops->init && !ops->init(service, announce->props, props_size)) {
|
||||
printf("EPIC: ignoring channel %d: %s\n", msg->channel, announce->name);
|
||||
afk_epic_rx_ack(epic);
|
||||
memset(service, 0, sizeof(*service));
|
||||
continue;
|
||||
}
|
||||
|
||||
channel = msg->channel;
|
||||
break;
|
||||
}
|
||||
|
||||
afk_epic_rx_ack(epic);
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
int afk_epic_start_interface(afk_epic_ep_t *epic, const afk_epic_service_ops_t *ops, void *intf,
|
||||
const char *name, size_t txsize, size_t rxsize)
|
||||
{
|
||||
int channel = afk_epic_start_channel(epic, ops, intf, name);
|
||||
|
||||
if (channel == -1) {
|
||||
printf("EPIC: too many unexpected messages, giving up\n");
|
||||
return -1;
|
||||
|
@ -537,9 +685,7 @@ int afk_epic_start_interface(afk_epic_ep_t *epic, char *name, size_t txsize, siz
|
|||
return -1;
|
||||
}
|
||||
|
||||
printf("EPIC: started interface %d (%s)\n", msg->channel, announce->name);
|
||||
|
||||
afk_epic_rx_ack(epic);
|
||||
printf("EPIC: started interface %d (%s)\n", channel, name);
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
|
62
src/afk.h
62
src/afk.h
|
@ -3,15 +3,71 @@
|
|||
#ifndef DCP_AFK_H
|
||||
#define DCP_AFK_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "rtkit.h"
|
||||
|
||||
#define MAX_PENDING_CMDS 8
|
||||
|
||||
typedef struct afk_epic_ep afk_epic_ep_t;
|
||||
|
||||
typedef struct afk_epic_service_ops afk_epic_service_ops_t;
|
||||
|
||||
enum EPICMessage {
|
||||
CODE_ANNOUNCE = 0x30,
|
||||
CODE_STD_SERVICE = 0xc0,
|
||||
};
|
||||
|
||||
struct epic_cmd_info {
|
||||
u16 code;
|
||||
|
||||
void *rxbuf;
|
||||
void *txbuf;
|
||||
u64 rxbuf_dma;
|
||||
u64 txbuf_dma;
|
||||
size_t rxlen;
|
||||
size_t txlen;
|
||||
|
||||
u32 retcode;
|
||||
bool done;
|
||||
bool free_on_ack;
|
||||
};
|
||||
|
||||
typedef struct afk_epic_service {
|
||||
const afk_epic_service_ops_t *ops;
|
||||
afk_epic_ep_t *epic;
|
||||
void *intf;
|
||||
|
||||
struct epic_cmd_info cmds[MAX_PENDING_CMDS];
|
||||
u8 cmd_tag;
|
||||
|
||||
u32 channel;
|
||||
bool enabled;
|
||||
|
||||
u16 seq;
|
||||
|
||||
void *cookie;
|
||||
} afk_epic_service_t;
|
||||
|
||||
typedef struct afk_epic_service_ops {
|
||||
const char name[32];
|
||||
|
||||
bool (*init)(afk_epic_service_t *service, u8 *props, size_t props_size);
|
||||
int (*call)(afk_epic_service_t *service, u32 idx, const void *data, size_t data_size,
|
||||
void *reply, size_t reply_size);
|
||||
} afk_epic_service_ops_t;
|
||||
|
||||
afk_epic_ep_t *afk_epic_init(rtkit_dev_t *rtkit, int endpoint);
|
||||
int afk_epic_shutdown(afk_epic_ep_t *epic);
|
||||
|
||||
int afk_epic_start_interface(afk_epic_ep_t *epic, char *name, size_t insize, size_t outsize);
|
||||
int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 code, void *txbuf, size_t txsize,
|
||||
void *rxbuf, size_t *rxsize);
|
||||
int afk_epic_start_channel(afk_epic_ep_t *epic, const afk_epic_service_ops_t *ops, void *intf,
|
||||
const char *name);
|
||||
int afk_epic_start_interface(afk_epic_ep_t *epic, const afk_epic_service_ops_t *ops, void *intf,
|
||||
const char *name, size_t insize, size_t outsize);
|
||||
int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 sub_seq, u16 code, void *txbuf,
|
||||
size_t txsize, void *rxbuf, size_t *rxsize);
|
||||
|
||||
int afk_epic_report(afk_epic_ep_t *epic, int channel, u32 type, u16 sub_seq, void *payload,
|
||||
size_t payload_size);
|
||||
|
||||
#endif
|
||||
|
|
21
src/dcp.h
21
src/dcp.h
|
@ -7,12 +7,33 @@
|
|||
#include "dart.h"
|
||||
#include "rtkit.h"
|
||||
|
||||
typedef struct dp_phy_configure_opts {
|
||||
u32 link_rate;
|
||||
u32 lanes;
|
||||
bool set_rate;
|
||||
bool set_lanes;
|
||||
} dp_phy_configure_opts_t;
|
||||
|
||||
typedef struct dptx_phy dptx_phy_t;
|
||||
typedef struct afk_epic_service afk_epic_service_t;
|
||||
|
||||
typedef struct dptx_port {
|
||||
bool enabled;
|
||||
u32 unit;
|
||||
afk_epic_service_t *service;
|
||||
dp_phy_configure_opts_t phy_opts;
|
||||
dptx_phy_t *phy;
|
||||
u32 link_rate, pending_link_rate;
|
||||
} dptx_port_t;
|
||||
|
||||
typedef struct {
|
||||
dart_dev_t *dart_dcp;
|
||||
dart_dev_t *dart_disp;
|
||||
iova_domain_t *iovad_dcp;
|
||||
asc_dev_t *asc;
|
||||
rtkit_dev_t *rtkit;
|
||||
|
||||
dptx_port_t *port;
|
||||
} dcp_dev_t;
|
||||
|
||||
dcp_dev_t *dcp_init(const char *dcp_path, const char *dcp_dart_path, const char *disp_dart_path);
|
||||
|
|
546
src/dcp/dptxep.c
Normal file
546
src/dcp/dptxep.c
Normal file
|
@ -0,0 +1,546 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2022 Sven Peter <sven@svenpeter.dev> */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dptxep.h"
|
||||
#include "malloc.h"
|
||||
#include "parser.h"
|
||||
|
||||
#include "../afk.h"
|
||||
#include "../dcp.h"
|
||||
#include "../types.h"
|
||||
#include "../utils.h"
|
||||
|
||||
#define DCP_DPTX_ENDPOINT 0x2a
|
||||
#define TXBUF_LEN 0x4000
|
||||
#define RXBUF_LEN 0x4000
|
||||
|
||||
struct dcpdptx_connection_cmd {
|
||||
u32 unk;
|
||||
u32 target;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dcpdptx_hotplug_cmd {
|
||||
u8 _pad0[16];
|
||||
u32 unk;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dptxport_apcall_link_rate {
|
||||
u32 retcode;
|
||||
u8 _unk0[12];
|
||||
u32 link_rate;
|
||||
u8 _unk1[12];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dptxport_apcall_get_support {
|
||||
u32 retcode;
|
||||
u8 _unk0[12];
|
||||
u32 supported;
|
||||
u8 _unk1[12];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dptxport_apcall_max_drive_settings {
|
||||
u32 retcode;
|
||||
u8 _unk0[12];
|
||||
u32 max_drive_settings[2];
|
||||
u8 _unk1[8];
|
||||
};
|
||||
|
||||
struct dptxport_apcall_set_tiled {
|
||||
u32 retcode;
|
||||
};
|
||||
|
||||
struct epic_service_call {
|
||||
u8 _pad0[2];
|
||||
u16 group;
|
||||
u32 command;
|
||||
u32 data_len;
|
||||
#define EPIC_SERVICE_CALL_MAGIC 0x69706378
|
||||
u32 magic;
|
||||
u8 _pad1[48];
|
||||
} __attribute__((packed));
|
||||
|
||||
typedef struct dcp_dptx_if {
|
||||
afk_epic_ep_t *epic; // ensure an afk_epic_ep_t pointer can be used as dcp_dptx_if_t
|
||||
dcp_dev_t *dcp;
|
||||
|
||||
int channel;
|
||||
struct dptx_port port[2];
|
||||
} dcp_dptx_if_t;
|
||||
|
||||
static int afk_service_call(afk_epic_service_t *service, u16 group, u32 command, const void *data,
|
||||
size_t data_len, size_t data_pad, void *output, size_t output_len,
|
||||
size_t output_pad)
|
||||
{
|
||||
struct epic_service_call *call;
|
||||
void *bfr;
|
||||
size_t bfr_len = max(data_len + data_pad, output_len + output_pad) + sizeof(*call);
|
||||
int ret;
|
||||
u32 retlen;
|
||||
size_t rx_len = bfr_len;
|
||||
|
||||
bfr = calloc(bfr_len, 1);
|
||||
if (!bfr)
|
||||
return -1;
|
||||
|
||||
call = bfr;
|
||||
call->group = group;
|
||||
call->command = command;
|
||||
call->data_len = data_len + data_pad;
|
||||
call->magic = EPIC_SERVICE_CALL_MAGIC;
|
||||
|
||||
memcpy(bfr + sizeof(*call), data, data_len);
|
||||
|
||||
ret = afk_epic_command(service->epic, service->channel, service->seq++, CODE_STD_SERVICE, bfr,
|
||||
bfr_len, bfr, &rx_len);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (call->magic != EPIC_SERVICE_CALL_MAGIC || call->group != group ||
|
||||
call->command != command) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retlen = call->data_len;
|
||||
if (output_len < retlen)
|
||||
retlen = output_len;
|
||||
if (output && output_len) {
|
||||
memset(output, 0, output_len);
|
||||
memcpy(output, bfr + sizeof(*call), retlen);
|
||||
}
|
||||
|
||||
out:
|
||||
free(bfr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dptxport_validate_connection(afk_epic_service_t *service, u8 core, u8 atc, u8 die)
|
||||
{
|
||||
dptx_port_t *dptx = service->cookie;
|
||||
struct dcpdptx_connection_cmd cmd, resp;
|
||||
int ret;
|
||||
u32 target = FIELD_PREP(DCPDPTX_REMOTE_PORT_CORE, core) |
|
||||
FIELD_PREP(DCPDPTX_REMOTE_PORT_DFP, atc) |
|
||||
FIELD_PREP(DCPDPTX_REMOTE_PORT_DIE, die) | DCPDPTX_REMOTE_PORT_CONNECTED;
|
||||
|
||||
cmd.target = target;
|
||||
cmd.unk = 0x100;
|
||||
ret = afk_service_call(service, 0, 14, &cmd, sizeof(cmd), 40, &resp, sizeof(resp), 40);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (resp.target != target)
|
||||
return -1;
|
||||
if (resp.unk != 0x100)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dptxport_connect(afk_epic_service_t *service, u8 core, u8 atc, u8 die)
|
||||
{
|
||||
dptx_port_t *dptx = service->cookie;
|
||||
struct dcpdptx_connection_cmd cmd = {0}, resp = {0};
|
||||
int ret;
|
||||
u32 target = FIELD_PREP(DCPDPTX_REMOTE_PORT_CORE, core) |
|
||||
FIELD_PREP(DCPDPTX_REMOTE_PORT_DFP, atc) |
|
||||
FIELD_PREP(DCPDPTX_REMOTE_PORT_DIE, die) | DCPDPTX_REMOTE_PORT_CONNECTED;
|
||||
|
||||
cmd.target = target;
|
||||
// cmd.unk = 0x100;
|
||||
ret = afk_service_call(service, 0, 13, &cmd, sizeof(cmd), 24, &resp, sizeof(resp), 24);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (resp.target != target)
|
||||
return -1;
|
||||
if (resp.unk != 0x100)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dptxport_request_display(afk_epic_service_t *service)
|
||||
{
|
||||
return afk_service_call(service, 0, 8, NULL, 0, 16, NULL, 0, 16);
|
||||
}
|
||||
|
||||
int dptxport_release_display(afk_epic_service_t *service)
|
||||
{
|
||||
return afk_service_call(service, 0, 9, NULL, 0, 16, NULL, 0, 16);
|
||||
}
|
||||
|
||||
int dptxport_set_hpd(afk_epic_service_t *service, bool hpd)
|
||||
{
|
||||
struct dcpdptx_hotplug_cmd cmd, resp;
|
||||
int ret;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
|
||||
if (hpd)
|
||||
cmd.unk = 1;
|
||||
|
||||
ret = afk_service_call(service, 8, 10, &cmd, sizeof(cmd), 12, &resp, sizeof(resp), 12);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (resp.unk != 1)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call_get_max_drive_settings(afk_epic_service_t *service, void *reply_,
|
||||
size_t reply_size)
|
||||
{
|
||||
struct dptxport_apcall_max_drive_settings *reply = reply_;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -1;
|
||||
|
||||
reply->retcode = 0;
|
||||
reply->max_drive_settings[0] = 0x3;
|
||||
reply->max_drive_settings[1] = 0x3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call_get_max_link_rate(afk_epic_service_t *service, void *reply_,
|
||||
size_t reply_size)
|
||||
{
|
||||
struct dptxport_apcall_link_rate *reply = reply_;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -1;
|
||||
|
||||
reply->retcode = 0;
|
||||
reply->link_rate = LINK_RATE_HBR3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call_get_link_rate(afk_epic_service_t *service, void *reply_, size_t reply_size)
|
||||
{
|
||||
dptx_port_t *dptx = service->cookie;
|
||||
struct dptxport_apcall_link_rate *reply = reply_;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -1;
|
||||
|
||||
reply->retcode = 0;
|
||||
reply->link_rate = dptx->link_rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call_will_change_link_config(afk_epic_service_t *service)
|
||||
{
|
||||
dptx_port_t *dptx = service->cookie;
|
||||
|
||||
dptx->phy_opts.set_lanes = 0;
|
||||
dptx->phy_opts.set_rate = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call_did_change_link_config(afk_epic_service_t *service)
|
||||
{
|
||||
// struct dptx_port *dptx = service->cookie;
|
||||
// int ret = 0;
|
||||
|
||||
mdelay(1000);
|
||||
// dispext0,0 -> atcph1,dpphy
|
||||
// mux_control_select(dptx->mux, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call_set_link_rate(afk_epic_service_t *service, const void *data,
|
||||
size_t data_size, void *reply_, size_t reply_size)
|
||||
{
|
||||
dptx_port_t *dptx = service->cookie;
|
||||
const struct dptxport_apcall_link_rate *request = data;
|
||||
struct dptxport_apcall_link_rate *reply = reply_;
|
||||
u32 link_rate, phy_link_rate;
|
||||
bool phy_set_rate = false;
|
||||
int ret;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -1;
|
||||
if (data_size < sizeof(*request))
|
||||
return -1;
|
||||
|
||||
link_rate = request->link_rate;
|
||||
|
||||
switch (link_rate) {
|
||||
case LINK_RATE_RBR:
|
||||
phy_link_rate = 1620;
|
||||
phy_set_rate = true;
|
||||
break;
|
||||
case LINK_RATE_HBR:
|
||||
phy_link_rate = 2700;
|
||||
phy_set_rate = true;
|
||||
break;
|
||||
case LINK_RATE_HBR2:
|
||||
phy_link_rate = 5400;
|
||||
phy_set_rate = true;
|
||||
break;
|
||||
case LINK_RATE_HBR3:
|
||||
phy_link_rate = 8100;
|
||||
phy_set_rate = true;
|
||||
break;
|
||||
case 0:
|
||||
phy_link_rate = 0;
|
||||
phy_set_rate = true;
|
||||
break;
|
||||
default:
|
||||
printf("DPTXPort: Unsupported link rate 0x%x requested\n", link_rate);
|
||||
link_rate = 0;
|
||||
phy_set_rate = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (phy_set_rate) {
|
||||
dptx->phy_opts.link_rate = phy_link_rate;
|
||||
dptx->phy_opts.set_rate = 1;
|
||||
|
||||
if (dptx->phy) {
|
||||
ret = 0; // phy_configure(dptx->atcphy, &dptx->phy_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// if (dptx->phy_ops.dp.set_rate)
|
||||
dptx->link_rate = dptx->pending_link_rate = link_rate;
|
||||
}
|
||||
|
||||
// dptx->pending_link_rate = link_rate;
|
||||
reply->retcode = 0;
|
||||
reply->link_rate = link_rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call_get_supports_hpd(afk_epic_service_t *service, void *reply_,
|
||||
size_t reply_size)
|
||||
{
|
||||
struct dptxport_apcall_get_support *reply = reply_;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -1;
|
||||
|
||||
reply->retcode = 0;
|
||||
reply->supported = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call_get_supports_downspread(afk_epic_service_t *service, void *reply_,
|
||||
size_t reply_size)
|
||||
{
|
||||
struct dptxport_apcall_get_support *reply = reply_;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -1;
|
||||
|
||||
reply->retcode = 0;
|
||||
reply->supported = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call_set_tiled_display_hint(afk_epic_service_t *service, void *reply_,
|
||||
size_t reply_size)
|
||||
{
|
||||
struct dptxport_apcall_set_tiled *reply = reply_;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -1;
|
||||
|
||||
reply->retcode = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call(afk_epic_service_t *service, u32 idx, const void *data, size_t data_size,
|
||||
void *reply, size_t reply_size)
|
||||
{
|
||||
switch (idx) {
|
||||
case DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG:
|
||||
return dptxport_call_will_change_link_config(service);
|
||||
case DPTX_APCALL_DID_CHANGE_LINK_CONFIG:
|
||||
return dptxport_call_did_change_link_config(service);
|
||||
case DPTX_APCALL_GET_MAX_LINK_RATE:
|
||||
return dptxport_call_get_max_link_rate(service, reply, reply_size);
|
||||
case DPTX_APCALL_GET_LINK_RATE:
|
||||
return dptxport_call_get_link_rate(service, reply, reply_size);
|
||||
case DPTX_APCALL_SET_LINK_RATE:
|
||||
return dptxport_call_set_link_rate(service, data, data_size, reply, reply_size);
|
||||
case DPTX_APCALL_GET_SUPPORTS_HPD:
|
||||
return dptxport_call_get_supports_hpd(service, reply, reply_size);
|
||||
case DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD:
|
||||
return dptxport_call_get_supports_downspread(service, reply, reply_size);
|
||||
case DPTX_APCALL_GET_MAX_DRIVE_SETTINGS:
|
||||
return dptxport_call_get_max_drive_settings(service, reply, reply_size);
|
||||
case DPTX_APCALL_SET_TILED_DISPLAY_HINTS:
|
||||
memcpy(reply, data, min(reply_size, data_size));
|
||||
return dptxport_call_set_tiled_display_hint(service, reply, reply_size);
|
||||
default:
|
||||
/* just try to ACK and hope for the best... */
|
||||
printf("DPTXPort: unhandled call %d\n", idx);
|
||||
// fallthrough
|
||||
/* we can silently ignore and just ACK these calls */
|
||||
case DPTX_APCALL_ACTIVATE:
|
||||
case DPTX_APCALL_DEACTIVATE:
|
||||
case DPTX_APCALL_SET_DRIVE_SETTINGS:
|
||||
case DPTX_APCALL_GET_DRIVE_SETTINGS:
|
||||
case DPTX_APCALL_SET_ACTIVE_LANE_COUNT:
|
||||
memcpy(reply, data, min(reply_size, data_size));
|
||||
if (reply_size > 4)
|
||||
memset(reply, 0, 4);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool dptxport_init(afk_epic_service_t *service, u8 *props, size_t props_size)
|
||||
{
|
||||
s64 unit;
|
||||
const char *name = NULL, *class = NULL;
|
||||
struct dcp_parse_ctx ctx;
|
||||
dcp_dptx_if_t *dptx = (dcp_dptx_if_t *)service->epic;
|
||||
int ret;
|
||||
dptx = service->intf;
|
||||
|
||||
ret = parse(props, props_size, &ctx);
|
||||
if (ret) {
|
||||
printf("DPTXPort: failed to parse init props: %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
ret = parse_epic_service_init(&ctx, &name, &class, &unit);
|
||||
if (ret) {
|
||||
printf("DPTXPort: failed to extract init props: %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
printf("DPTXPort: parsed: name:'%s' class:'%s' unit:'%ld'\n", name, class, unit);
|
||||
|
||||
if (strcmp(name, "dcpdptx-port-epic"))
|
||||
goto free;
|
||||
if (strcmp(class, "AppleDCPDPTXRemotePort"))
|
||||
goto free;
|
||||
free((void *)name);
|
||||
free((void *)class);
|
||||
|
||||
switch (unit) {
|
||||
case 0:
|
||||
case 1:
|
||||
if (dptx->port[unit].enabled) {
|
||||
printf("DPTXPort: unit %ld already exists\n", unit);
|
||||
return false;
|
||||
}
|
||||
dptx->port[unit].unit = unit;
|
||||
dptx->port[unit].service = service;
|
||||
dptx->port[unit].enabled = true;
|
||||
service->cookie = (void *)&dptx->port[unit];
|
||||
printf("%s:%d port %ld enabled service:%p\n", __func__, __LINE__, unit, service);
|
||||
break;
|
||||
default:
|
||||
printf("DPTXPort: invalid unit %ld\n", unit);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
free:
|
||||
free((void *)name);
|
||||
free((void *)class);
|
||||
return false;
|
||||
}
|
||||
|
||||
static const afk_epic_service_ops_t dcp_dptx_ops = {
|
||||
.name = "AppleDCPDPTXRemotePort",
|
||||
|
||||
.init = dptxport_init,
|
||||
.call = dptxport_call,
|
||||
};
|
||||
|
||||
int dcp_dptx_connect(dcp_dptx_if_t *dptx, u32 port)
|
||||
{
|
||||
if (!dptx->port[port].service)
|
||||
return -1;
|
||||
|
||||
// dptx->port[port].atcphy = phy;
|
||||
// dptxport_validate_connection(dptx->port[port].service, 0, 5, 0);
|
||||
dptxport_connect(dptx->port[port].service, 0, 5, 0);
|
||||
dptxport_request_display(dptx->port[port].service);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dcp_dptx_hpd(dcp_dptx_if_t *dptx, u32 port, bool hpd)
|
||||
{
|
||||
if (!dptx->port[port].service)
|
||||
return -1;
|
||||
|
||||
dptxport_set_hpd(dptx->port[port].service, hpd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dcp_dptx_disconnect(dcp_dptx_if_t *dptx, u32 port)
|
||||
{
|
||||
dptxport_release_display(dptx->port[port].service);
|
||||
dptxport_set_hpd(dptx->port[port].service, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
dcp_dptx_if_t *dcp_dptx_init(dcp_dev_t *dcp)
|
||||
{
|
||||
int channel;
|
||||
|
||||
dcp_dptx_if_t *dptx = malloc(sizeof(dcp_dptx_if_t));
|
||||
if (!dptx)
|
||||
return NULL;
|
||||
|
||||
dptx->dcp = dcp;
|
||||
dptx->epic = afk_epic_init(dcp->rtkit, DCP_DPTX_ENDPOINT);
|
||||
if (!dptx->epic) {
|
||||
printf("dcp-dptx: failed to initialize EPIC\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
channel = afk_epic_start_interface(dptx->epic, &dcp_dptx_ops, dptx,
|
||||
"dispext4294967295:dcpdptx-port-", TXBUF_LEN, RXBUF_LEN);
|
||||
|
||||
if (channel < 0) {
|
||||
printf("dcp-dptx: failed to initialize DPTXRemotePort service\n");
|
||||
goto err_shutdown;
|
||||
}
|
||||
|
||||
channel =
|
||||
afk_epic_start_channel(dptx->epic, &dcp_dptx_ops, dptx, "dispext4294967295:dcpdptx-port-");
|
||||
|
||||
if (channel < 0) {
|
||||
printf("dcp-dptx: failed to initialize DPTXRemotePort service\n");
|
||||
goto err_shutdown;
|
||||
}
|
||||
|
||||
u8 report_1[8] = {0x62, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00};
|
||||
afk_epic_report(dptx->epic, 1, 0x12, 0, report_1, sizeof(report_1));
|
||||
|
||||
u8 report_3[8] = {0x63, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00};
|
||||
afk_epic_report(dptx->epic, 3, 0x12, 0, report_3, sizeof(report_3));
|
||||
|
||||
return dptx;
|
||||
|
||||
err_shutdown:
|
||||
afk_epic_shutdown(dptx->epic);
|
||||
err_free:
|
||||
free(dptx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dcp_dptx_shutdown(dcp_dptx_if_t *dptx)
|
||||
{
|
||||
afk_epic_shutdown(dptx->epic);
|
||||
|
||||
free(dptx);
|
||||
return 0;
|
||||
}
|
58
src/dcp/dptxep.h
Normal file
58
src/dcp/dptxep.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2022 Sven Peter <sven@svenpeter.dev> */
|
||||
|
||||
#ifndef __APPLE_DCP_DPTXEP_H__
|
||||
#define __APPLE_DCP_DPTXEP_H__
|
||||
|
||||
#include "../dcp.h"
|
||||
#include "../types.h"
|
||||
|
||||
typedef struct dcp_dptx_if dcp_dptx_if_t;
|
||||
|
||||
enum dptx_apcall {
|
||||
DPTX_APCALL_ACTIVATE = 0,
|
||||
DPTX_APCALL_DEACTIVATE = 1,
|
||||
DPTX_APCALL_GET_MAX_DRIVE_SETTINGS = 2,
|
||||
DPTX_APCALL_SET_DRIVE_SETTINGS = 3,
|
||||
DPTX_APCALL_GET_DRIVE_SETTINGS = 4,
|
||||
DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG = 5,
|
||||
DPTX_APCALL_DID_CHANGE_LINK_CONFIG = 6,
|
||||
DPTX_APCALL_GET_MAX_LINK_RATE = 7,
|
||||
DPTX_APCALL_GET_LINK_RATE = 8,
|
||||
DPTX_APCALL_SET_LINK_RATE = 9,
|
||||
DPTX_APCALL_GET_ACTIVE_LANE_COUNT = 10,
|
||||
DPTX_APCALL_SET_ACTIVE_LANE_COUNT = 11,
|
||||
DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD = 12,
|
||||
DPTX_APCALL_GET_DOWN_SPREAD = 13,
|
||||
DPTX_APCALL_SET_DOWN_SPREAD = 14,
|
||||
DPTX_APCALL_GET_SUPPORTS_LANE_MAPPING = 15,
|
||||
DPTX_APCALL_SET_LANE_MAP = 16,
|
||||
DPTX_APCALL_GET_SUPPORTS_HPD = 17,
|
||||
DPTX_APCALL_FORCE_HOTPLUG_DETECT = 18,
|
||||
DPTX_APCALL_INACTIVE_SINK_DETECTED = 19,
|
||||
DPTX_APCALL_SET_TILED_DISPLAY_HINTS = 20,
|
||||
DPTX_APCALL_DEVICE_NOT_RESPONDING = 21,
|
||||
DPTX_APCALL_DEVICE_BUSY_TIMEOUT = 22,
|
||||
DPTX_APCALL_DEVICE_NOT_STARTED = 23,
|
||||
};
|
||||
|
||||
#define DCPDPTX_REMOTE_PORT_CORE GENMASK(3, 0)
|
||||
#define DCPDPTX_REMOTE_PORT_DFP GENMASK(7, 4)
|
||||
#define DCPDPTX_REMOTE_PORT_DIE GENMASK(11, 8)
|
||||
#define DCPDPTX_REMOTE_PORT_CONNECTED BIT(15)
|
||||
|
||||
enum dptx_link_rate {
|
||||
LINK_RATE_RBR = 0x06,
|
||||
LINK_RATE_HBR = 0x0a,
|
||||
LINK_RATE_HBR2 = 0x14,
|
||||
LINK_RATE_HBR3 = 0x1e,
|
||||
};
|
||||
|
||||
dcp_dptx_if_t *dcp_dptx_init(dcp_dev_t *dcp);
|
||||
int dcp_dptx_shutdown(dcp_dptx_if_t *dptx);
|
||||
|
||||
int dcp_dptx_connect(dcp_dptx_if_t *dptx, u32 port);
|
||||
int dcp_dptx_disconnect(dcp_dptx_if_t *dptx, u32 port);
|
||||
int dcp_dptx_hpd(dcp_dptx_if_t *dptx, u32 port, bool hpd);
|
||||
|
||||
#endif
|
271
src/dcp/parser.c
Normal file
271
src/dcp/parser.c
Normal file
|
@ -0,0 +1,271 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */
|
||||
|
||||
#include "malloc.h"
|
||||
#include "parser.h"
|
||||
#include "string.h"
|
||||
|
||||
#include "../utils.h"
|
||||
|
||||
#define DCP_PARSE_HEADER 0xd3
|
||||
|
||||
enum dcp_parse_type {
|
||||
DCP_TYPE_DICTIONARY = 1,
|
||||
DCP_TYPE_ARRAY = 2,
|
||||
DCP_TYPE_INT64 = 4,
|
||||
DCP_TYPE_STRING = 9,
|
||||
DCP_TYPE_BLOB = 10,
|
||||
DCP_TYPE_BOOL = 11
|
||||
};
|
||||
|
||||
struct dcp_parse_tag {
|
||||
unsigned int size : 24;
|
||||
enum dcp_parse_type type : 5;
|
||||
unsigned int padding : 2;
|
||||
bool last : 1;
|
||||
} __packed;
|
||||
|
||||
static void *parse_bytes(struct dcp_parse_ctx *ctx, size_t count)
|
||||
{
|
||||
void *ptr = ctx->blob + ctx->pos;
|
||||
|
||||
if (ctx->pos + count > ctx->len)
|
||||
return NULL;
|
||||
|
||||
ctx->pos += count;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static u32 *parse_u32(struct dcp_parse_ctx *ctx)
|
||||
{
|
||||
return parse_bytes(ctx, sizeof(u32));
|
||||
}
|
||||
|
||||
static struct dcp_parse_tag *parse_tag(struct dcp_parse_ctx *ctx)
|
||||
{
|
||||
struct dcp_parse_tag *tag;
|
||||
|
||||
/* Align to 32-bits */
|
||||
ctx->pos = ALIGN_UP(ctx->pos, 4);
|
||||
|
||||
tag = parse_bytes(ctx, sizeof(struct dcp_parse_tag));
|
||||
|
||||
if (!tag)
|
||||
return NULL;
|
||||
|
||||
if (tag->padding)
|
||||
return NULL;
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
static struct dcp_parse_tag *parse_tag_of_type(struct dcp_parse_ctx *ctx, enum dcp_parse_type type)
|
||||
{
|
||||
struct dcp_parse_tag *tag = parse_tag(ctx);
|
||||
|
||||
if (!tag)
|
||||
return NULL;
|
||||
|
||||
if (tag->type != type)
|
||||
return NULL;
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
static int skip(struct dcp_parse_ctx *handle)
|
||||
{
|
||||
struct dcp_parse_tag *tag = parse_tag(handle);
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
if (!tag)
|
||||
return -1;
|
||||
|
||||
switch (tag->type) {
|
||||
case DCP_TYPE_DICTIONARY:
|
||||
for (i = 0; i < tag->size; ++i) {
|
||||
ret |= skip(handle); /* key */
|
||||
ret |= skip(handle); /* value */
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
case DCP_TYPE_ARRAY:
|
||||
for (i = 0; i < tag->size; ++i)
|
||||
ret |= skip(handle);
|
||||
|
||||
return ret;
|
||||
|
||||
case DCP_TYPE_INT64:
|
||||
handle->pos += sizeof(s64);
|
||||
return 0;
|
||||
|
||||
case DCP_TYPE_STRING:
|
||||
case DCP_TYPE_BLOB:
|
||||
handle->pos += tag->size;
|
||||
return 0;
|
||||
|
||||
case DCP_TYPE_BOOL:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Caller must free the result */
|
||||
static char *parse_string(struct dcp_parse_ctx *handle)
|
||||
{
|
||||
struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_STRING);
|
||||
const char *in;
|
||||
char *out;
|
||||
|
||||
if (!tag)
|
||||
return NULL;
|
||||
|
||||
in = parse_bytes(handle, tag->size);
|
||||
if (!in)
|
||||
return NULL;
|
||||
|
||||
out = malloc(tag->size + 1);
|
||||
|
||||
memcpy(out, in, tag->size);
|
||||
out[tag->size] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
static int parse_int(struct dcp_parse_ctx *handle, s64 *value)
|
||||
{
|
||||
void *tag = parse_tag_of_type(handle, DCP_TYPE_INT64);
|
||||
s64 *in;
|
||||
|
||||
if (!tag)
|
||||
return -1;
|
||||
|
||||
in = parse_bytes(handle, sizeof(s64));
|
||||
|
||||
if (!in)
|
||||
return -1;
|
||||
|
||||
memcpy(value, in, sizeof(*value));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_bool(struct dcp_parse_ctx *handle, bool *b)
|
||||
{
|
||||
struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_BOOL);
|
||||
|
||||
if (!tag)
|
||||
return -1;
|
||||
|
||||
*b = !!tag->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct iterator {
|
||||
struct dcp_parse_ctx *handle;
|
||||
u32 idx, len;
|
||||
};
|
||||
|
||||
static int iterator_begin(struct dcp_parse_ctx *handle, struct iterator *it, bool dict)
|
||||
{
|
||||
struct dcp_parse_tag *tag;
|
||||
enum dcp_parse_type type = dict ? DCP_TYPE_DICTIONARY : DCP_TYPE_ARRAY;
|
||||
|
||||
*it = (struct iterator){.handle = handle, .idx = 0};
|
||||
|
||||
tag = parse_tag_of_type(it->handle, type);
|
||||
if (!tag)
|
||||
return -1;
|
||||
|
||||
it->len = tag->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define dcp_parse_foreach_in_array(handle, it) \
|
||||
for (iterator_begin(handle, &it, false); it.idx < it.len; ++it.idx)
|
||||
#define dcp_parse_foreach_in_dict(handle, it) \
|
||||
for (iterator_begin(handle, &it, true); it.idx < it.len; ++it.idx)
|
||||
|
||||
int parse(void *blob, size_t size, struct dcp_parse_ctx *ctx)
|
||||
{
|
||||
u32 *header;
|
||||
|
||||
*ctx = (struct dcp_parse_ctx){
|
||||
.blob = blob,
|
||||
.len = size,
|
||||
.pos = 0,
|
||||
};
|
||||
|
||||
header = parse_u32(ctx);
|
||||
if (!header)
|
||||
return -1;
|
||||
|
||||
if (*header != DCP_PARSE_HEADER)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_epic_service_init(struct dcp_parse_ctx *handle, const char **name, const char **class,
|
||||
s64 *unit)
|
||||
{
|
||||
int ret = 0;
|
||||
struct iterator it;
|
||||
bool parsed_unit = false;
|
||||
bool parsed_name = false;
|
||||
bool parsed_class = false;
|
||||
|
||||
*name = NULL;
|
||||
*class = NULL;
|
||||
|
||||
dcp_parse_foreach_in_dict(handle, it)
|
||||
{
|
||||
char *key = parse_string(it.handle);
|
||||
|
||||
if (!key) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcmp(key, "EPICName")) {
|
||||
*name = parse_string(it.handle);
|
||||
if (!*name)
|
||||
ret = -1;
|
||||
else
|
||||
parsed_name = true;
|
||||
} else if (!strcmp(key, "EPICProviderClass")) {
|
||||
*class = parse_string(it.handle);
|
||||
if (!*class)
|
||||
ret = -1;
|
||||
else
|
||||
parsed_class = true;
|
||||
} else if (!strcmp(key, "EPICUnit")) {
|
||||
ret = parse_int(it.handle, unit);
|
||||
if (!ret)
|
||||
parsed_unit = true;
|
||||
} else {
|
||||
skip(it.handle);
|
||||
}
|
||||
|
||||
free(key);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!parsed_unit || !parsed_name || !parsed_class)
|
||||
ret = -1;
|
||||
|
||||
if (ret) {
|
||||
if (*name) {
|
||||
free(*name);
|
||||
*name = NULL;
|
||||
}
|
||||
if (*class) {
|
||||
free(*class);
|
||||
*class = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
19
src/dcp/parser.h
Normal file
19
src/dcp/parser.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */
|
||||
|
||||
#ifndef __APPLE_DCP_PARSER_H__
|
||||
#define __APPLE_DCP_PARSER_H__
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
struct dcp_parse_ctx {
|
||||
void *blob;
|
||||
u32 pos, len;
|
||||
};
|
||||
|
||||
int parse(void *blob, size_t size, struct dcp_parse_ctx *ctx);
|
||||
|
||||
int parse_epic_service_init(struct dcp_parse_ctx *handle, const char **name, const char **class,
|
||||
s64 *unit);
|
||||
|
||||
#endif
|
|
@ -98,7 +98,8 @@ dcp_iboot_if_t *dcp_ib_init(dcp_dev_t *dcp)
|
|||
goto err_free;
|
||||
}
|
||||
|
||||
iboot->channel = afk_epic_start_interface(iboot->epic, "disp0-service", TXBUF_LEN, RXBUF_LEN);
|
||||
iboot->channel =
|
||||
afk_epic_start_interface(iboot->epic, NULL, NULL, "disp0-service", TXBUF_LEN, RXBUF_LEN);
|
||||
|
||||
if (iboot->channel < 0) {
|
||||
printf("dcp-iboot: failed to initialize disp0 service\n");
|
||||
|
@ -130,7 +131,7 @@ static int dcp_ib_cmd(dcp_iboot_if_t *iboot, int op, size_t in_size)
|
|||
iboot->txcmd.op = op;
|
||||
iboot->txcmd.len = sizeof(struct txcmd) + in_size;
|
||||
|
||||
return afk_epic_command(iboot->epic, iboot->channel, 0xc0, iboot->txbuf,
|
||||
return afk_epic_command(iboot->epic, iboot->channel, 0, 0xc0, iboot->txbuf,
|
||||
sizeof(struct txcmd) + in_size, iboot->rxbuf, &rxsize);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include "utils.h"
|
||||
#include "xnuboot.h"
|
||||
|
||||
#include "dcp/dptxep.h"
|
||||
|
||||
#define DISPLAY_STATUS_DELAY 100
|
||||
#define DISPLAY_STATUS_RETRIES 20
|
||||
|
||||
|
@ -24,6 +26,7 @@
|
|||
|
||||
static dcp_dev_t *dcp;
|
||||
static dcp_iboot_if_t *iboot;
|
||||
static dcp_dptx_if_t *dptx;
|
||||
static u64 fb_dva;
|
||||
static u64 fb_size;
|
||||
bool display_is_external;
|
||||
|
@ -199,6 +202,24 @@ int display_start_dcp(void)
|
|||
return -1;
|
||||
}
|
||||
|
||||
// Power on
|
||||
int ret;
|
||||
if ((ret = dcp_ib_set_power(iboot, true)) < 0) {
|
||||
printf("display: failed to set power\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dptx = dcp_dptx_init(dcp);
|
||||
if (!dptx) {
|
||||
printf("display: failed to initialize DCP iBoot interface\n");
|
||||
dcp_ib_shutdown(iboot);
|
||||
iboot = NULL;
|
||||
dcp_shutdown(dcp, false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dcp_dptx_connect(dptx, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -491,6 +512,8 @@ int display_init(void)
|
|||
void display_shutdown(dcp_shutdown_mode mode)
|
||||
{
|
||||
if (iboot) {
|
||||
if (dptx)
|
||||
dcp_dptx_shutdown(dptx);
|
||||
dcp_ib_shutdown(iboot);
|
||||
switch (mode) {
|
||||
case DCP_QUIESCED:
|
||||
|
|
|
@ -345,6 +345,14 @@ static void rtkit_crashed(rtkit_dev_t *rtk)
|
|||
}
|
||||
}
|
||||
|
||||
bool rtkit_can_recv(rtkit_dev_t *rtk)
|
||||
{
|
||||
if (rtk->crashed)
|
||||
return false;
|
||||
|
||||
return asc_can_recv(rtk->asc);
|
||||
}
|
||||
|
||||
int rtkit_recv(rtkit_dev_t *rtk, struct rtkit_message *msg)
|
||||
{
|
||||
struct asc_message asc_msg;
|
||||
|
|
|
@ -31,6 +31,8 @@ void rtkit_free(rtkit_dev_t *rtk);
|
|||
bool rtkit_start_ep(rtkit_dev_t *rtk, u8 ep);
|
||||
bool rtkit_boot(rtkit_dev_t *rtk);
|
||||
|
||||
bool rtkit_can_recv(rtkit_dev_t *rtk);
|
||||
|
||||
int rtkit_recv(rtkit_dev_t *rtk, struct rtkit_message *msg);
|
||||
bool rtkit_send(rtkit_dev_t *rtk, const struct rtkit_message *msg);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue