From: Ruslan Ruslichenko <[email protected]> Implement the session management for Remote Port protocol. Key features implemented are following:
- Hello packet handling and capabilities negotiation - Dynamic packet management routines - rp_peer_state struct definition to hold negotiated capabilities. Signed-off-by: Edgar E. Iglesias <[email protected]> Signed-off-by: Takahiro Nakata <[email protected]> Signed-off-by: Ruslan Ruslichenko <[email protected]> --- hw/core/remote-port-proto.c | 134 +++++++++++++++++++++++++++- include/hw/core/remote-port-proto.h | 72 +++++++++++++++ 2 files changed, 205 insertions(+), 1 deletion(-) diff --git a/hw/core/remote-port-proto.c b/hw/core/remote-port-proto.c index 1653fe7b78..969dfc3cb2 100644 --- a/hw/core/remote-port-proto.c +++ b/hw/core/remote-port-proto.c @@ -64,7 +64,43 @@ int rp_decode_payload(struct rp_pkt *pkt) int used = 0; switch (pkt->hdr.cmd) { - /* TBD */ + case RP_CMD_hello: + assert(pkt->hdr.len >= sizeof pkt->hello.version); + pkt->hello.version.major = be16_to_cpu(pkt->hello.version.major); + pkt->hello.version.minor = be16_to_cpu(pkt->hello.version.minor); + used += sizeof pkt->hello.version; + + if ((pkt->hdr.len - used) >= sizeof pkt->hello.caps) { + void *offset; + int i; + + pkt->hello.caps.offset = be32_to_cpu(pkt->hello.caps.offset); + pkt->hello.caps.len = be16_to_cpu(pkt->hello.caps.len); + + offset = (char *)pkt + pkt->hello.caps.offset; + for (i = 0; i < pkt->hello.caps.len; i++) { + uint32_t cap; + + /* + * We don't know if offset is 32bit aligned so use + * memcpy to do the endian conversion. + */ + memcpy(&cap, offset + i * sizeof cap, sizeof cap); + cap = be32_to_cpu(cap); + memcpy(offset + i * sizeof cap, &cap, sizeof cap); + } + used += sizeof pkt->hello.caps; + } else { + pkt->hello.caps.offset = 0; + pkt->hello.caps.len = 0; + } + + /* + * Consume everything ignoring additional headers we do not yet + * know about. + */ + used = pkt->hdr.len; + break; default: break; } @@ -80,3 +116,99 @@ void rp_encode_hdr(struct rp_pkt_hdr *hdr, uint32_t cmd, uint32_t id, hdr->dev = cpu_to_be32(dev); hdr->flags = cpu_to_be32(flags); } + +size_t rp_encode_hello_caps(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt, + uint16_t version_major, uint16_t version_minor, + uint32_t *caps, uint32_t *caps_out, + uint32_t caps_len) +{ + size_t psize = sizeof *pkt + sizeof caps[0] * caps_len; + unsigned int i; + + rp_encode_hdr(&pkt->hdr, RP_CMD_hello, id, dev, + psize - sizeof pkt->hdr, 0); + pkt->version.major = cpu_to_be16(version_major); + pkt->version.minor = cpu_to_be16(version_minor); + + /* Feature list is appeneded right after the hello packet. */ + pkt->caps.offset = cpu_to_be32(sizeof *pkt); + pkt->caps.len = cpu_to_be16(caps_len); + + for (i = 0; i < caps_len; i++) { + uint32_t cap; + + cap = caps[i]; + caps_out[i] = cpu_to_be32(cap); + } + return sizeof *pkt; +} + +void rp_process_caps(struct rp_peer_state *peer, + void *caps, size_t caps_len) +{ + int i; + + assert(peer->caps.busaccess_ext_base == false); + + for (i = 0; i < caps_len; i++) { + uint32_t cap; + + memcpy(&cap, caps + i * sizeof cap, sizeof cap); + + switch (cap) { + case CAP_BUSACCESS_EXT_BASE: + peer->caps.busaccess_ext_base = true; + break; + case CAP_BUSACCESS_EXT_BYTE_EN: + peer->caps.busaccess_ext_byte_en = true; + break; + case CAP_WIRE_POSTED_UPDATES: + peer->caps.wire_posted_updates = true; + break; + case CAP_ATS: + peer->caps.ats = true; + break; + } + } +} + +void rp_dpkt_alloc(RemotePortDynPkt *dpkt, size_t size) +{ + if (dpkt->size < size) { + char *u8; + dpkt->pkt = realloc(dpkt->pkt, size); + u8 = (void *) dpkt->pkt; + memset(u8 + dpkt->size, 0, size - dpkt->size); + dpkt->size = size; + } +} + +void rp_dpkt_swap(RemotePortDynPkt *a, RemotePortDynPkt *b) +{ + struct rp_pkt *tmp_pkt; + size_t tmp_size; + + tmp_pkt = a->pkt; + tmp_size = a->size; + a->pkt = b->pkt; + a->size = b->size; + b->pkt = tmp_pkt; + b->size = tmp_size; +} + +bool rp_dpkt_is_valid(RemotePortDynPkt *dpkt) +{ + return dpkt->size > 0 && dpkt->pkt->hdr.len; +} + +void rp_dpkt_invalidate(RemotePortDynPkt *dpkt) +{ + assert(rp_dpkt_is_valid(dpkt)); + dpkt->pkt->hdr.len = 0; +} + +inline void rp_dpkt_free(RemotePortDynPkt *dpkt) +{ + dpkt->size = 0; + free(dpkt->pkt); +} diff --git a/include/hw/core/remote-port-proto.h b/include/hw/core/remote-port-proto.h index 1d8e1b25da..ca1ab2f1d3 100644 --- a/include/hw/core/remote-port-proto.h +++ b/include/hw/core/remote-port-proto.h @@ -302,6 +302,28 @@ struct rp_pkt { }; }; +struct rp_peer_state { + void *opaque; + + struct rp_pkt pkt; + bool hdr_used; + + struct rp_version version; + + struct { + bool busaccess_ext_base; + bool busaccess_ext_byte_en; + bool wire_posted_updates; + bool ats; + } caps; + + /* Used to normalize our clk. */ + int64_t clk_base; + + struct rp_cfg_state local_cfg; + struct rp_cfg_state peer_cfg; +}; + const char *rp_cmd_to_string(enum rp_cmd cmd); int rp_decode_hdr(struct rp_pkt *pkt); int rp_decode_payload(struct rp_pkt *pkt); @@ -310,4 +332,54 @@ void rp_encode_hdr(struct rp_pkt_hdr *hdr, uint32_t cmd, uint32_t id, uint32_t dev, uint32_t len, uint32_t flags); +/* + * caps is a an array of supported capabilities by the implementor. + * caps_out is the encoded (network byte order) version of the + * same array. It should be sent to the peer after the hello packet. + */ +size_t rp_encode_hello_caps(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt, + uint16_t version_major, uint16_t version_minor, + uint32_t *caps, uint32_t *features_out, + uint32_t features_len); + +/* rp_encode_hello is deprecated in favor of hello_caps. */ +static inline size_t __attribute__ ((deprecated)) +rp_encode_hello(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt, + uint16_t version_major, uint16_t version_minor) { + return rp_encode_hello_caps(id, dev, pkt, version_major, version_minor, + NULL, NULL, 0); +} + +void rp_process_caps(struct rp_peer_state *peer, + void *caps, size_t caps_len); + +/* Dynamically resizable remote port pkt. */ + +typedef struct RemotePortDynPkt { + struct rp_pkt *pkt; + size_t size; +} RemotePortDynPkt; + +/* + * Make sure dpkt is allocated and has enough room. + */ + +void rp_dpkt_alloc(RemotePortDynPkt *dpkt, size_t size); + +void rp_dpkt_swap(RemotePortDynPkt *a, RemotePortDynPkt *b); + +/* + * Check if the dpkt is valid. Used for debugging purposes. + */ + +bool rp_dpkt_is_valid(RemotePortDynPkt *dpkt); + +/* + * Invalidate the dpkt. Used for debugging purposes. + */ + +void rp_dpkt_invalidate(RemotePortDynPkt *dpkt); + +void rp_dpkt_free(RemotePortDynPkt *dpkt); + #endif -- 2.43.0
