Files
utun2/lib/packet_link.c
T

130 lines
5.8 KiB
C

#include "packet_link_backend.h"
#include "u_async.h"
#include "ll_queue.h"
#include "mem.h"
#include "debug_config.h"
#include <stdlib.h>
struct packet_link {
struct UASYNC* ua;
struct packet_link_ops ops;
struct packet_link_callbacks callbacks;
struct packet_link_state state;
void *impl, *arg, *notification;
unsigned active;
int closing, rx_enabled, sending;
};
static void destroy_deferred(void* arg) {
struct packet_link* link = arg;
link->ops.destroy(link->impl);
u_free(link);
}
static void release(struct packet_link* link) {
if (--link->active || !link->closing) return;
/* Backend мог вызвать receive из своего I/O callback: его стек тоже должен завершиться. */
if (!uasync_call_soon(link->ua, link, destroy_deferred)) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: cannot defer destruction"); abort();
}
}
static void notify(void* arg) {
struct packet_link* link = arg;
link->notification = NULL;
link->active++;
if (link->callbacks.state_changed) link->callbacks.state_changed(link, link->arg);
release(link);
}
static int schedule(struct packet_link* link) {
if (link->notification || link->closing) return 0;
link->notification = uasync_call_soon(link->ua, link, notify);
if (link->notification) return 0;
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: cannot schedule state notification");
return -1;
}
static int valid_state(const struct packet_link_state* s) {
return s && s->phase >= PACKET_LINK_CONNECTING && s->phase <= PACKET_LINK_TERMINATED &&
s->max_packet && s->max_packet <= UINT16_MAX && (!s->tx_ready || s->phase == PACKET_LINK_OPEN);
}
struct packet_link* packet_link_create(struct UASYNC* ua, const struct packet_link_ops* ops, void* impl,
const struct packet_link_state* initial, const struct packet_link_callbacks* callbacks, void* arg) {
if (!ua || !ops || !ops->send || !ops->stop || !ops->destroy || !ops->set_rx_enabled || !valid_state(initial) ||
!callbacks || !callbacks->received) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: invalid create arguments"); return NULL;
}
struct packet_link* link = u_calloc(1, sizeof(*link));
if (!link) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: allocation failed"); return NULL; }
link->ua = ua; link->ops = *ops; link->impl = impl; link->state = *initial; link->callbacks = *callbacks; link->arg = arg;
if (schedule(link)) { u_free(link); return NULL; }
DEBUG_DEBUG(DEBUG_CATEGORY_SYS, "packet link=%p created phase=%d max=%zu ready=%d",
(void*)link, initial->phase, initial->max_packet, initial->tx_ready);
return link;
}
struct packet_link_state packet_link_get_state(const struct packet_link* link) {
if (!link || link->closing) return (struct packet_link_state){PACKET_LINK_TERMINATED, 0, 0, PACKET_LINK_CLOSED};
return link->state;
}
void packet_link_backend_set_state(struct packet_link* link, const struct packet_link_state* state) {
if (!link || link->closing) return;
if (!valid_state(state) || (link->state.phase == PACKET_LINK_TERMINATED && state->phase != PACKET_LINK_TERMINATED)) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: invalid state transition"); return;
}
struct packet_link_state old = link->state;
if (old.phase == state->phase && old.max_packet == state->max_packet && old.tx_ready == state->tx_ready && old.error == state->error) return;
link->state = *state;
DEBUG_DEBUG(DEBUG_CATEGORY_SYS, "packet link=%p phase=%d ready=%d max=%zu error=%d",
(void*)link, state->phase, state->tx_ready, state->max_packet, state->error);
/* Потерять обязательное событие нельзя; синхронный callback нарушил бы контракт send. */
if (schedule(link)) abort();
}
int packet_link_send(struct packet_link* link, struct ll_entry* packet) {
if (!link || link->closing || link->state.phase == PACKET_LINK_TERMINATED) return PACKET_LINK_CLOSED;
if (!packet || packet->owner || packet->next || packet->prev || !packet->dgram || !packet->len) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: invalid TX packet"); return PACKET_LINK_INVALID;
}
if (packet->len > link->state.max_packet) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: oversized TX size=%u max=%zu", packet->len, link->state.max_packet);
return PACKET_LINK_TOO_LARGE;
}
if (!link->state.tx_ready || link->sending) return PACKET_LINK_WOULD_BLOCK;
link->active++; link->sending = 1;
int result = link->ops.send(link->impl, packet);
link->sending = 0;
release(link);
return result;
}
void packet_link_set_rx_enabled(struct packet_link* link, int enabled) {
if (!link || link->closing || link->rx_enabled == !!enabled) return;
link->active++; link->rx_enabled = !!enabled;
link->ops.set_rx_enabled(link->impl, !!enabled);
release(link);
}
int packet_link_backend_receive(struct packet_link* link, struct ll_entry* packet) {
if (!link || link->closing || link->state.phase == PACKET_LINK_TERMINATED) return PACKET_LINK_CLOSED;
if (!packet || packet->owner || !packet->dgram || !packet->len || packet->len > link->state.max_packet) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: invalid RX packet"); return PACKET_LINK_INVALID;
}
if (!link->rx_enabled || link->sending || link->state.phase != PACKET_LINK_OPEN) return PACKET_LINK_WOULD_BLOCK;
link->active++;
link->callbacks.received(link, packet, link->arg);
release(link);
return PACKET_LINK_OK;
}
void packet_link_close(struct packet_link* link) {
if (!link || link->closing) return;
link->active++; link->closing = 1;
if (link->notification) { uasync_call_soon_cancel(link->ua, link->notification); link->notification = NULL; }
link->ops.stop(link->impl);
release(link);
}