Commit 42bb9c91 authored by Peter Crosthwaite's avatar Peter Crosthwaite Committed by Edgar E. Iglesias
Browse files

stream: Remove app argument hack



The uint32_t *app argument doesn't exist in real hardware. It was a hack in
xilinx_axidma/enet to fake the (secondary) control stream connection. Removed
the argument and added the second stream to axienet/dma.

Signed-off-by: default avatarPeter Crosthwaite <peter.crosthwaite@xilinx.com>
Signed-off-by: default avatarEdgar E. Iglesias <edgar.iglesias@gmail.com>
parent 3630ae95
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
#include "hw/stream.h"

size_t
stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app)
stream_push(StreamSlave *sink, uint8_t *buf, size_t len)
{
    StreamSlaveClass *k =  STREAM_SLAVE_GET_CLASS(sink);

    return k->push(sink, buf, len, app);
    return k->push(sink, buf, len);
}

bool
+70 −29
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@

#define TYPE_XILINX_AXI_DMA "xlnx.axi-dma"
#define TYPE_XILINX_AXI_DMA_DATA_STREAM "xilinx-axi-dma-data-stream"
#define TYPE_XILINX_AXI_DMA_CONTROL_STREAM "xilinx-axi-dma-control-stream"

#define XILINX_AXI_DMA(obj) \
     OBJECT_CHECK(XilinxAXIDMA, (obj), TYPE_XILINX_AXI_DMA)
@@ -43,12 +44,19 @@
     OBJECT_CHECK(XilinxAXIDMAStreamSlave, (obj),\
     TYPE_XILINX_AXI_DMA_DATA_STREAM)

#define XILINX_AXI_DMA_CONTROL_STREAM(obj) \
     OBJECT_CHECK(XilinxAXIDMAStreamSlave, (obj),\
     TYPE_XILINX_AXI_DMA_CONTROL_STREAM)

#define R_DMACR             (0x00 / 4)
#define R_DMASR             (0x04 / 4)
#define R_CURDESC           (0x08 / 4)
#define R_TAILDESC          (0x10 / 4)
#define R_MAX               (0x30 / 4)

#define CONTROL_PAYLOAD_WORDS 5
#define CONTROL_PAYLOAD_SIZE (CONTROL_PAYLOAD_WORDS * (sizeof(uint32_t)))

typedef struct XilinxAXIDMA XilinxAXIDMA;
typedef struct XilinxAXIDMAStreamSlave XilinxAXIDMAStreamSlave;

@@ -73,7 +81,7 @@ struct SDesc {
    uint64_t reserved;
    uint32_t control;
    uint32_t status;
    uint32_t app[6];
    uint8_t app[CONTROL_PAYLOAD_SIZE];
};

enum {
@@ -101,6 +109,7 @@ struct Stream {
    int pos;
    unsigned int complete_cnt;
    uint32_t regs[R_MAX];
    uint8_t app[20];
};

struct XilinxAXIDMAStreamSlave {
@@ -113,8 +122,10 @@ struct XilinxAXIDMA {
    SysBusDevice busdev;
    MemoryRegion iomem;
    uint32_t freqhz;
    StreamSlave *tx_dev;
    StreamSlave *tx_data_dev;
    StreamSlave *tx_control_dev;
    XilinxAXIDMAStreamSlave rx_data_dev;
    XilinxAXIDMAStreamSlave rx_control_dev;

    struct Stream streams[2];

@@ -185,7 +196,6 @@ static void stream_desc_show(struct SDesc *d)
static void stream_desc_load(struct Stream *s, hwaddr addr)
{
    struct SDesc *d = &s->desc;
    int i;

    cpu_physical_memory_read(addr, (void *) d, sizeof *d);

@@ -194,24 +204,17 @@ static void stream_desc_load(struct Stream *s, hwaddr addr)
    d->nxtdesc = le64_to_cpu(d->nxtdesc);
    d->control = le32_to_cpu(d->control);
    d->status = le32_to_cpu(d->status);
    for (i = 0; i < ARRAY_SIZE(d->app); i++) {
        d->app[i] = le32_to_cpu(d->app[i]);
    }
}

static void stream_desc_store(struct Stream *s, hwaddr addr)
{
    struct SDesc *d = &s->desc;
    int i;

    /* Convert from host endianness into LE.  */
    d->buffer_address = cpu_to_le64(d->buffer_address);
    d->nxtdesc = cpu_to_le64(d->nxtdesc);
    d->control = cpu_to_le32(d->control);
    d->status = cpu_to_le32(d->status);
    for (i = 0; i < ARRAY_SIZE(d->app); i++) {
        d->app[i] = cpu_to_le32(d->app[i]);
    }
    cpu_physical_memory_write(addr, (void *) d, sizeof *d);
}

@@ -263,13 +266,12 @@ static void stream_complete(struct Stream *s)
    }
}

static void stream_process_mem2s(struct Stream *s,
                                 StreamSlave *tx_dev)
static void stream_process_mem2s(struct Stream *s, StreamSlave *tx_data_dev,
                                 StreamSlave *tx_control_dev)
{
    uint32_t prev_d;
    unsigned char txbuf[16 * 1024];
    unsigned int txlen;
    uint32_t app[6];

    if (!stream_running(s) || stream_idle(s)) {
        return;
@@ -285,7 +287,7 @@ static void stream_process_mem2s(struct Stream *s,

        if (stream_desc_sof(&s->desc)) {
            s->pos = 0;
            memcpy(app, s->desc.app, sizeof app);
            stream_push(tx_control_dev, s->desc.app, sizeof(s->desc.app));
        }

        txlen = s->desc.control & SDESC_CTRL_LEN_MASK;
@@ -299,7 +301,7 @@ static void stream_process_mem2s(struct Stream *s,
        s->pos += txlen;

        if (stream_desc_eof(&s->desc)) {
            stream_push(tx_dev, txbuf, s->pos, app);
            stream_push(tx_data_dev, txbuf, s->pos);
            s->pos = 0;
            stream_complete(s);
        }
@@ -319,7 +321,7 @@ static void stream_process_mem2s(struct Stream *s,
}

static size_t stream_process_s2mem(struct Stream *s, unsigned char *buf,
                                   size_t len, uint32_t *app)
                                   size_t len)
{
    uint32_t prev_d;
    unsigned int rxlen;
@@ -350,12 +352,8 @@ static size_t stream_process_s2mem(struct Stream *s, unsigned char *buf,

        /* Update the descriptor.  */
        if (!len) {
            int i;

            stream_complete(s);
            for (i = 0; i < 5; i++) {
                s->desc.app[i] = app[i];
            }
            memcpy(s->desc.app, s->app, sizeof(s->desc.app));
            s->desc.status |= SDESC_STATUS_EOF;
        }

@@ -386,6 +384,22 @@ static void xilinx_axidma_reset(DeviceState *dev)
    }
}

static size_t
xilinx_axidma_control_stream_push(StreamSlave *obj, unsigned char *buf,
                                  size_t len)
{
    XilinxAXIDMAStreamSlave *cs = XILINX_AXI_DMA_CONTROL_STREAM(obj);
    struct Stream *s = &cs->dma->streams[1];

    if (len != CONTROL_PAYLOAD_SIZE) {
        hw_error("AXI DMA requires %d byte control stream payload\n",
                 (int)CONTROL_PAYLOAD_SIZE);
    }

    memcpy(s->app, buf, len);
    return len;
}

static bool
xilinx_axidma_data_stream_can_push(StreamSlave *obj,
                                   StreamCanPushNotifyFn notify,
@@ -404,17 +418,13 @@ xilinx_axidma_data_stream_can_push(StreamSlave *obj,
}

static size_t
xilinx_axidma_data_stream_push(StreamSlave *obj, unsigned char *buf, size_t len,
                               uint32_t *app)
xilinx_axidma_data_stream_push(StreamSlave *obj, unsigned char *buf, size_t len)
{
    XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(obj);
    struct Stream *s = &ds->dma->streams[1];
    size_t ret;

    if (!app) {
        hw_error("No stream app data!\n");
    }
    ret = stream_process_s2mem(s, buf, len, app);
    ret = stream_process_s2mem(s, buf, len);
    stream_update_irq(s);
    return ret;
}
@@ -495,7 +505,7 @@ static void axidma_write(void *opaque, hwaddr addr,
            s->regs[addr] = value;
            s->regs[R_DMASR] &= ~DMASR_IDLE; /* Not idle.  */
            if (!sid) {
                stream_process_mem2s(s, d->tx_dev);
                stream_process_mem2s(s, d->tx_data_dev, d->tx_control_dev);
            }
            break;
        default:
@@ -521,14 +531,19 @@ static void xilinx_axidma_realize(DeviceState *dev, Error **errp)
{
    XilinxAXIDMA *s = XILINX_AXI_DMA(dev);
    XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(&s->rx_data_dev);
    XilinxAXIDMAStreamSlave *cs = XILINX_AXI_DMA_CONTROL_STREAM(
                                                            &s->rx_control_dev);
    Error *local_errp = NULL;

    object_property_add_link(OBJECT(ds), "dma", TYPE_XILINX_AXI_DMA,
                             (Object **)&ds->dma, &local_errp);
    object_property_add_link(OBJECT(cs), "dma", TYPE_XILINX_AXI_DMA,
                             (Object **)&cs->dma, &local_errp);
    if (local_errp) {
        goto xilinx_axidma_realize_fail;
    }
    object_property_set_link(OBJECT(ds), OBJECT(s), "dma", &local_errp);
    object_property_set_link(OBJECT(cs), OBJECT(s), "dma", &local_errp);
    if (local_errp) {
        goto xilinx_axidma_realize_fail;
    }
@@ -556,12 +571,21 @@ static void xilinx_axidma_init(Object *obj)
    Error *errp = NULL;

    object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
                             (Object **) &s->tx_dev, NULL);
                             (Object **) &s->tx_data_dev, &errp);
    assert_no_error(errp);
    object_property_add_link(obj, "axistream-control-connected",
                             TYPE_STREAM_SLAVE,
                             (Object **) &s->tx_control_dev, &errp);
    assert_no_error(errp);

    object_initialize(&s->rx_data_dev, TYPE_XILINX_AXI_DMA_DATA_STREAM);
    object_initialize(&s->rx_control_dev, TYPE_XILINX_AXI_DMA_CONTROL_STREAM);
    object_property_add_child(OBJECT(s), "axistream-connected-target",
                              (Object *)&s->rx_data_dev, &errp);
    assert_no_error(errp);
    object_property_add_child(OBJECT(s), "axistream-control-connected-target",
                              (Object *)&s->rx_control_dev, &errp);
    assert_no_error(errp);

    sysbus_init_irq(sbd, &s->streams[0].irq);
    sysbus_init_irq(sbd, &s->streams[1].irq);
@@ -590,6 +614,10 @@ static StreamSlaveClass xilinx_axidma_data_stream_class = {
    .can_push = xilinx_axidma_data_stream_can_push,
};

static StreamSlaveClass xilinx_axidma_control_stream_class = {
    .push = xilinx_axidma_control_stream_push,
};

static void xilinx_axidma_stream_class_init(ObjectClass *klass, void *data)
{
    StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
@@ -618,10 +646,23 @@ static const TypeInfo xilinx_axidma_data_stream_info = {
    }
};

static const TypeInfo xilinx_axidma_control_stream_info = {
    .name          = TYPE_XILINX_AXI_DMA_CONTROL_STREAM,
    .parent        = TYPE_OBJECT,
    .instance_size = sizeof(struct XilinxAXIDMAStreamSlave),
    .class_init    = xilinx_axidma_stream_class_init,
    .class_data    = &xilinx_axidma_control_stream_class,
    .interfaces = (InterfaceInfo[]) {
        { TYPE_STREAM_SLAVE },
        { }
    }
};

static void xilinx_axidma_register_types(void)
{
    type_register_static(&axidma_info);
    type_register_static(&xilinx_axidma_data_stream_info);
    type_register_static(&xilinx_axidma_control_stream_info);
}

type_init(xilinx_axidma_register_types)
+15 −10
Original line number Diff line number Diff line
@@ -79,7 +79,7 @@ petalogix_ml605_init(QEMUMachineInitArgs *args)
    const char *cpu_model = args->cpu_model;
    MemoryRegion *address_space_mem = get_system_memory();
    DeviceState *dev, *dma, *eth0;
    Object *peer;
    Object *ds, *cs;
    MicroBlazeCPU *cpu;
    SysBusDevice *busdev;
    CPUMBState *env;
@@ -140,15 +140,20 @@ petalogix_ml605_init(QEMUMachineInitArgs *args)
    object_property_add_child(qdev_get_machine(), "xilinx-dma", OBJECT(dma),
                              NULL);

    peer = object_property_get_link(OBJECT(dma),
    ds = object_property_get_link(OBJECT(dma),
                                  "axistream-connected-target", NULL);
    xilinx_axiethernet_init(eth0, &nd_table[0], STREAM_SLAVE(peer),
                            0x82780000, irq[3], 0x1000, 0x1000);
    cs = object_property_get_link(OBJECT(dma),
                                  "axistream-control-connected-target", NULL);
    xilinx_axiethernet_init(eth0, &nd_table[0], STREAM_SLAVE(ds),
                            STREAM_SLAVE(cs), 0x82780000, irq[3], 0x1000,
                            0x1000);

    peer = object_property_get_link(OBJECT(eth0),
    ds = object_property_get_link(OBJECT(eth0),
                                  "axistream-connected-target", NULL);
    xilinx_axidma_init(dma, STREAM_SLAVE(peer), 0x84600000, irq[1], irq[0],
                       100 * 1000000);
    cs = object_property_get_link(OBJECT(eth0),
                                  "axistream-control-connected-target", NULL);
    xilinx_axidma_init(dma, STREAM_SLAVE(ds), STREAM_SLAVE(cs), 0x84600000,
                       irq[1], irq[0], 100 * 1000000);

    {
        SSIBus *spi;
+85 −15
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@

#define TYPE_XILINX_AXI_ENET "xlnx.axi-ethernet"
#define TYPE_XILINX_AXI_ENET_DATA_STREAM "xilinx-axienet-data-stream"
#define TYPE_XILINX_AXI_ENET_CONTROL_STREAM "xilinx-axienet-control-stream"

#define XILINX_AXI_ENET(obj) \
     OBJECT_CHECK(XilinxAXIEnet, (obj), TYPE_XILINX_AXI_ENET)
@@ -42,12 +43,19 @@
     OBJECT_CHECK(XilinxAXIEnetStreamSlave, (obj),\
     TYPE_XILINX_AXI_ENET_DATA_STREAM)

#define XILINX_AXI_ENET_CONTROL_STREAM(obj) \
     OBJECT_CHECK(XilinxAXIEnetStreamSlave, (obj),\
     TYPE_XILINX_AXI_ENET_CONTROL_STREAM)

/* Advertisement control register. */
#define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
#define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
#define ADVERTISE_100HALF       0x0080  /* Try for 100mbps half-duplex */
#define ADVERTISE_100FULL       0x0100  /* Try for 100mbps full-duplex */

#define CONTROL_PAYLOAD_WORDS 5
#define CONTROL_PAYLOAD_SIZE (CONTROL_PAYLOAD_WORDS * (sizeof(uint32_t)))

struct PHY {
    uint32_t regs[32];

@@ -329,8 +337,10 @@ struct XilinxAXIEnet {
    SysBusDevice busdev;
    MemoryRegion iomem;
    qemu_irq irq;
    StreamSlave *tx_dev;
    StreamSlave *tx_data_dev;
    StreamSlave *tx_control_dev;
    XilinxAXIEnetStreamSlave rx_data_dev;
    XilinxAXIEnetStreamSlave rx_control_dev;
    NICState *nic;
    NICConf conf;

@@ -381,11 +391,14 @@ struct XilinxAXIEnet {
    /* 32K x 1 lookup filter.  */
    uint32_t ext_mtable[1024];

    uint32_t hdr[CONTROL_PAYLOAD_WORDS];

    uint8_t *rxmem;
    uint32_t *rxapp;
    uint32_t rxsize;
    uint32_t rxpos;

    uint8_t rxapp[CONTROL_PAYLOAD_SIZE];
    uint32_t rxappsize;
};

static void axienet_rx_reset(XilinxAXIEnet *s)
@@ -670,14 +683,22 @@ static void axienet_eth_rx_notify(void *opaque)
{
    XilinxAXIEnet *s = XILINX_AXI_ENET(opaque);

    while (s->rxsize && stream_can_push(s->tx_dev, axienet_eth_rx_notify, s)) {
        size_t ret = stream_push(s->tx_dev, (void *)s->rxmem + s->rxpos,
                                 s->rxsize, s->rxapp);
    while (s->rxappsize && stream_can_push(s->tx_control_dev,
                                           axienet_eth_rx_notify, s)) {
        size_t ret = stream_push(s->tx_control_dev,
                                 (void *)s->rxapp + CONTROL_PAYLOAD_SIZE
                                 - s->rxappsize, s->rxappsize);
        s->rxappsize -= ret;
    }

    while (s->rxsize && stream_can_push(s->tx_data_dev,
                                        axienet_eth_rx_notify, s)) {
        size_t ret = stream_push(s->tx_data_dev, (void *)s->rxmem + s->rxpos,
                                 s->rxsize);
        s->rxsize -= ret;
        s->rxpos += ret;
        if (!s->rxsize) {
            s->regs[R_IS] |= IS_RX_COMPLETE;
            g_free(s->rxapp);
        }
    }
    enet_update_irq(s);
@@ -689,7 +710,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
    static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
                                              0xff, 0xff, 0xff};
    static const unsigned char sa_ipmcast[3] = {0x01, 0x00, 0x52};
    uint32_t app[6] = {0};
    uint32_t app[CONTROL_PAYLOAD_WORDS] = {0};
    int promisc = s->fmi & (1 << 31);
    int unicast, broadcast, multicast, ip_multicast = 0;
    uint32_t csum32;
@@ -822,7 +843,11 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)

    s->rxsize = size;
    s->rxpos = 0;
    s->rxapp = g_memdup(app, sizeof(app));
    for (i = 0; i < ARRAY_SIZE(app); ++i) {
        app[i] = cpu_to_le32(app[i]);
    }
    s->rxappsize = CONTROL_PAYLOAD_SIZE;
    memcpy(s->rxapp, app, s->rxappsize);
    axienet_eth_rx_notify(s);

    enet_update_irq(s);
@@ -838,8 +863,27 @@ static void eth_cleanup(NetClientState *nc)
}

static size_t
xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size,
                                uint32_t *hdr)
xilinx_axienet_control_stream_push(StreamSlave *obj, uint8_t *buf, size_t len)
{
    int i;
    XilinxAXIEnetStreamSlave *cs = XILINX_AXI_ENET_CONTROL_STREAM(obj);
    XilinxAXIEnet *s = cs->enet;

    if (len != CONTROL_PAYLOAD_SIZE) {
        hw_error("AXI Enet requires %d byte control stream payload\n",
                 (int)CONTROL_PAYLOAD_SIZE);
    }

    memcpy(s->hdr, buf, len);

    for (i = 0; i < ARRAY_SIZE(s->hdr); ++i) {
        s->hdr[i] = le32_to_cpu(s->hdr[i]);
    }
    return len;
}

static size_t
xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size)
{
    XilinxAXIEnetStreamSlave *ds = XILINX_AXI_ENET_DATA_STREAM(obj);
    XilinxAXIEnet *s = ds->enet;
@@ -856,16 +900,16 @@ xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size,
        }
    }

    if (hdr[0] & 1) {
        unsigned int start_off = hdr[1] >> 16;
        unsigned int write_off = hdr[1] & 0xffff;
    if (s->hdr[0] & 1) {
        unsigned int start_off = s->hdr[1] >> 16;
        unsigned int write_off = s->hdr[1] & 0xffff;
        uint32_t tmp_csum;
        uint16_t csum;

        tmp_csum = net_checksum_add(size - start_off,
                                    (uint8_t *)buf + start_off);
        /* Accumulate the seed.  */
        tmp_csum += hdr[2] & 0xffff;
        tmp_csum += s->hdr[2] & 0xffff;

        /* Fold the 32bit partial checksum.  */
        csum = net_checksum_finish(tmp_csum);
@@ -896,14 +940,19 @@ static void xilinx_enet_realize(DeviceState *dev, Error **errp)
{
    XilinxAXIEnet *s = XILINX_AXI_ENET(dev);
    XilinxAXIEnetStreamSlave *ds = XILINX_AXI_ENET_DATA_STREAM(&s->rx_data_dev);
    XilinxAXIEnetStreamSlave *cs = XILINX_AXI_ENET_CONTROL_STREAM(
                                                            &s->rx_control_dev);
    Error *local_errp = NULL;

    object_property_add_link(OBJECT(ds), "enet", "xlnx.axi-ethernet",
                             (Object **) &ds->enet, &local_errp);
    object_property_add_link(OBJECT(cs), "enet", "xlnx.axi-ethernet",
                             (Object **) &cs->enet, &local_errp);
    if (local_errp) {
        goto xilinx_enet_realize_fail;
    }
    object_property_set_link(OBJECT(ds), OBJECT(s), "enet", &local_errp);
    object_property_set_link(OBJECT(cs), OBJECT(s), "enet", &local_errp);
    if (local_errp) {
        goto xilinx_enet_realize_fail;
    }
@@ -934,13 +983,21 @@ static void xilinx_enet_init(Object *obj)
    Error *errp = NULL;

    object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
                             (Object **) &s->tx_dev, &errp);
                             (Object **) &s->tx_data_dev, &errp);
    assert_no_error(errp);
    object_property_add_link(obj, "axistream-control-connected",
                             TYPE_STREAM_SLAVE,
                             (Object **) &s->tx_control_dev, &errp);
    assert_no_error(errp);

    object_initialize(&s->rx_data_dev, TYPE_XILINX_AXI_ENET_DATA_STREAM);
    object_initialize(&s->rx_control_dev, TYPE_XILINX_AXI_ENET_CONTROL_STREAM);
    object_property_add_child(OBJECT(s), "axistream-connected-target",
                              (Object *)&s->rx_data_dev, &errp);
    assert_no_error(errp);
    object_property_add_child(OBJECT(s), "axistream-control-connected-target",
                              (Object *)&s->rx_control_dev, &errp);
    assert_no_error(errp);

    sysbus_init_irq(sbd, &s->irq);

@@ -992,10 +1049,23 @@ static const TypeInfo xilinx_enet_data_stream_info = {
    }
};

static const TypeInfo xilinx_enet_control_stream_info = {
    .name          = TYPE_XILINX_AXI_ENET_CONTROL_STREAM,
    .parent        = TYPE_OBJECT,
    .instance_size = sizeof(struct XilinxAXIEnetStreamSlave),
    .class_init    = xilinx_enet_stream_class_init,
    .class_data    = xilinx_axienet_control_stream_push,
    .interfaces = (InterfaceInfo[]) {
            { TYPE_STREAM_SLAVE },
            { }
    }
};

static void xilinx_enet_register_types(void)
{
    type_register_static(&xilinx_enet_info);
    type_register_static(&xilinx_enet_data_stream_info);
    type_register_static(&xilinx_enet_control_stream_info);
}

type_init(xilinx_enet_register_types)
+2 −3
Original line number Diff line number Diff line
@@ -43,12 +43,11 @@ typedef struct StreamSlaveClass {
     * @buf: Data to write
     * @len: Maximum number of bytes to write
     */
    size_t (*push)(StreamSlave *obj, unsigned char *buf, size_t len,
                                                    uint32_t *app);
    size_t (*push)(StreamSlave *obj, unsigned char *buf, size_t len);
} StreamSlaveClass;

size_t
stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app);
stream_push(StreamSlave *sink, uint8_t *buf, size_t len);

bool
stream_can_push(StreamSlave *sink, StreamCanPushNotifyFn notify,
Loading