Add initial plugin framework
This commit is contained in:
parent
07eebcd58b
commit
972b501455
13 changed files with 313 additions and 23 deletions
2
Makefile
2
Makefile
|
@ -1,7 +1,7 @@
|
|||
-include ../config.mak
|
||||
|
||||
SRCS = main.c dispatch.c channels.c transports.c teletext.c psi.c \
|
||||
subscriptions.c tsmux.c tsdemux.c pes.c buffer.c tcp.c
|
||||
subscriptions.c tsmux.c tsdemux.c pes.c buffer.c tcp.c plugin.c
|
||||
|
||||
SRCS += http.c htmlui.c
|
||||
|
||||
|
|
15
channels.c
15
channels.c
|
@ -34,7 +34,7 @@
|
|||
#include "dvb.h"
|
||||
#include "v4l.h"
|
||||
#include "iptv_input.h"
|
||||
|
||||
#include "psi.h"
|
||||
#include "channels.h"
|
||||
#include "transports.h"
|
||||
|
||||
|
@ -100,7 +100,7 @@ transport_set_channel(th_transport_t *t, th_channel_t *ch)
|
|||
{
|
||||
th_stream_t *st;
|
||||
char *chname;
|
||||
|
||||
const char *n;
|
||||
t->tht_channel = ch;
|
||||
LIST_INSERT_SORTED(&ch->ch_transports, t, tht_channel_link, transportcmp);
|
||||
|
||||
|
@ -110,9 +110,14 @@ transport_set_channel(th_transport_t *t, th_channel_t *ch)
|
|||
t->tht_name, chname);
|
||||
free(chname);
|
||||
|
||||
LIST_FOREACH(st, &t->tht_streams, st_link)
|
||||
syslog(LOG_DEBUG, " Stream [%s] - pid %d",
|
||||
htstvstreamtype2txt(st->st_type), st->st_pid);
|
||||
LIST_FOREACH(st, &t->tht_streams, st_link) {
|
||||
if(st->st_caid != 0) {
|
||||
n = psi_caid2name(st->st_caid);
|
||||
} else {
|
||||
n = htstvstreamtype2txt(st->st_type);
|
||||
}
|
||||
syslog(LOG_DEBUG, " Stream [%s] - pid %d", n, st->st_pid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
3
dvb.c
3
dvb.c
|
@ -799,8 +799,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
|
|||
len -= dlen; ptr += dlen; dllen -= dlen;
|
||||
}
|
||||
|
||||
if(stype == 1 &&
|
||||
free_ca_mode == 0 /* We dont have any CA-support (yet) */) {
|
||||
if(stype == 1) {
|
||||
t = dvb_find_transport(tdmi, transport_stream_id, service_id, 0);
|
||||
|
||||
if(t == NULL)
|
||||
|
|
|
@ -74,7 +74,7 @@ dvb_dvr_process_packets(th_dvb_adapter_t *tda, uint8_t *tsb, int r)
|
|||
while(r >= 188) {
|
||||
pid = (tsb[1] & 0x1f) << 8 | tsb[2];
|
||||
LIST_FOREACH(t, &tda->tda_transports, tht_adapter_link)
|
||||
ts_recv_packet(t, pid, tsb);
|
||||
ts_recv_packet(t, pid, tsb, 1);
|
||||
r -= 188;
|
||||
tsb += 188;
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ iptv_fd_callback(int events, void *opaque, int fd)
|
|||
|
||||
while(r >= 188) {
|
||||
pid = (tsb[1] & 0x1f) << 8 | tsb[2];
|
||||
ts_recv_packet(t, pid, tsb);
|
||||
ts_recv_packet(t, pid, tsb, 1);
|
||||
r -= 188;
|
||||
tsb += 188;
|
||||
}
|
||||
|
@ -225,6 +225,7 @@ iptv_configure_transport(th_transport_t *t, const char *iptv_type,
|
|||
|
||||
st = transport_add_stream(t, 0, HTSTV_TABLE);
|
||||
st->st_got_section = iptv_parse_pat;
|
||||
st->st_section_docrc = 1;
|
||||
|
||||
t->tht_channel = channel_find(channel_name, 1);
|
||||
LIST_INSERT_HEAD(&iptv_probing_transports, t, tht_adapter_link);
|
||||
|
|
90
plugin.c
Normal file
90
plugin.c
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* tvheadend, Plugin framework
|
||||
* Copyright (C) 2007 Andreas Öman
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "plugin.h"
|
||||
|
||||
struct th_plugin_list th_plugins;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void *
|
||||
plugin_aux_create(struct pluginaux_list *h, struct th_plugin *p, size_t siz)
|
||||
{
|
||||
pluginaux_t *pa;
|
||||
|
||||
pa = calloc(1, siz);
|
||||
pa->pa_plugin = p;
|
||||
LIST_INSERT_HEAD(h, pa, pa_link);
|
||||
return pa;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void *
|
||||
plugin_aux_find(struct pluginaux_list *h, struct th_plugin *p)
|
||||
{
|
||||
pluginaux_t *pa;
|
||||
|
||||
LIST_FOREACH(pa, h, pa_link)
|
||||
if(pa->pa_plugin == p)
|
||||
break;
|
||||
|
||||
return pa;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
plugin_aux_destroy(void *pa)
|
||||
{
|
||||
pluginaux_t *pa0 = pa;
|
||||
LIST_REMOVE(pa0, pa_link);
|
||||
free(pa);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
th_plugin_t *
|
||||
plugin_alloc(const char *name, void *opaque, size_t minsiz)
|
||||
{
|
||||
th_plugin_t *p;
|
||||
|
||||
if(sizeof(th_plugin_t) < minsiz)
|
||||
return NULL;
|
||||
|
||||
p = calloc(1, sizeof(th_plugin_t));
|
||||
|
||||
p->thp_name = strdup(name);
|
||||
p->thp_opaque = opaque;
|
||||
|
||||
LIST_INSERT_HEAD(&th_plugins, p, thp_link);
|
||||
|
||||
return p;
|
||||
}
|
61
plugin.h
Normal file
61
plugin.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* tvheadend, Plugin framework
|
||||
* Copyright (C) 2007 Andreas Öman
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PLUGIN_H_
|
||||
#define PLUGIN_H_
|
||||
|
||||
#include "tvhead.h"
|
||||
|
||||
LIST_HEAD(th_plugin_list, th_plugin);
|
||||
|
||||
extern struct th_plugin_list th_plugins;
|
||||
struct th_plugin;
|
||||
|
||||
typedef struct th_plugin {
|
||||
|
||||
LIST_ENTRY(th_plugin) thp_link;
|
||||
|
||||
void *thp_opaque;
|
||||
|
||||
const char *thp_name;
|
||||
|
||||
void (*thp_transport_start)(struct th_plugin *p, struct pluginaux_list *h,
|
||||
th_transport_t *t);
|
||||
|
||||
void (*thp_transport_stop)(struct th_plugin *p, struct pluginaux_list *h,
|
||||
th_transport_t *t);
|
||||
|
||||
void (*thp_psi_table)(struct th_plugin *p, struct pluginaux_list *h,
|
||||
th_transport_t *t, struct th_stream *st,
|
||||
const uint8_t *data, size_t len);
|
||||
|
||||
int (*thp_tsb_process)(pluginaux_t *pa, th_transport_t *t,
|
||||
struct th_stream *st, uint8_t *tsb);
|
||||
|
||||
} th_plugin_t;
|
||||
|
||||
void *plugin_aux_create(struct pluginaux_list *h, struct th_plugin *p,
|
||||
size_t siz);
|
||||
|
||||
void *plugin_aux_find(struct pluginaux_list *h, struct th_plugin *p);
|
||||
|
||||
void plugin_aux_destroy(void *pa);
|
||||
|
||||
th_plugin_t *plugin_alloc(const char *name, void *opaque, size_t minsiz);
|
||||
|
||||
#endif /* PLUGIN_H_ */
|
63
psi.c
63
psi.c
|
@ -29,6 +29,7 @@
|
|||
#include "transports.h"
|
||||
#include "dvb_support.h"
|
||||
#include "tsdemux.h"
|
||||
#include "strtab.h"
|
||||
|
||||
int
|
||||
psi_section_reassemble(psi_section_t *ps, uint8_t *data, int len,
|
||||
|
@ -89,6 +90,7 @@ psi_parse_pat(th_transport_t *t, uint8_t *ptr, int len,
|
|||
|
||||
if(prognum != 0) {
|
||||
st = transport_add_stream(t, pid, HTSTV_TABLE);
|
||||
st->st_section_docrc = 1;
|
||||
st->st_got_section = pmt_callback;
|
||||
|
||||
}
|
||||
|
@ -173,6 +175,7 @@ psi_parse_pmt(th_transport_t *t, uint8_t *ptr, int len, int chksvcid)
|
|||
uint8_t dtag, dlen;
|
||||
uint16_t sid;
|
||||
tv_streamtype_t hts_stream_type;
|
||||
th_stream_t *st;
|
||||
|
||||
if(len < 9)
|
||||
return -1;
|
||||
|
@ -198,6 +201,17 @@ psi_parse_pmt(th_transport_t *t, uint8_t *ptr, int len, int chksvcid)
|
|||
if(dlen > len)
|
||||
break;
|
||||
|
||||
switch(dtag) {
|
||||
case DVB_DESC_CA:
|
||||
st = transport_add_stream(t, (ptr[2] & 0x1f) << 8 | ptr[3], HTSTV_TABLE);
|
||||
st->st_caid = (ptr[0] << 8) | ptr[1];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
len -= dlen; ptr += dlen; dllen -= dlen;
|
||||
}
|
||||
|
||||
|
@ -435,3 +449,52 @@ psi_crc32(uint8_t *data, size_t datalen)
|
|||
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
||||
static struct strtab caidnametab[] = {
|
||||
{ "Seca", 0x0100 },
|
||||
{ "CCETT", 0x0200 },
|
||||
{ "Deutsche Telecom", 0x0300 },
|
||||
{ "Eurodec", 0x0400 },
|
||||
{ "Viaccess", 0x0500 },
|
||||
{ "Irdeto", 0x0600 },
|
||||
{ "Irdeto", 0x0602 },
|
||||
{ "Irdeto", 0x0604 },
|
||||
{ "Jerroldgi", 0x0700 },
|
||||
{ "Matra", 0x0800 },
|
||||
{ "NDS", 0x0900 },
|
||||
{ "Nokia", 0x0A00 },
|
||||
{ "Conax", 0x0B00 },
|
||||
{ "NTL", 0x0C00 },
|
||||
{ "CryptoWorks", 0x0D00 },
|
||||
{ "PowerVu", 0x0E00 },
|
||||
{ "Sony", 0x0F00 },
|
||||
{ "Tandberg", 0x1000 },
|
||||
{ "Thompson", 0x1100 },
|
||||
{ "TV-Com", 0x1200 },
|
||||
{ "HPT", 0x1300 },
|
||||
{ "HRT", 0x1400 },
|
||||
{ "IBM", 0x1500 },
|
||||
{ "Nera", 0x1600 },
|
||||
{ "BetaCrypt", 0x1700 },
|
||||
{ "BetaCrypt", 0x1702 },
|
||||
{ "BetaCrypt", 0x1722 },
|
||||
{ "BetaCrypt", 0x1762 },
|
||||
{ "NagraVision", 0x1800 },
|
||||
{ "Titan", 0x1900 },
|
||||
{ "Telefonica", 0x2000 },
|
||||
{ "Stentor", 0x2100 },
|
||||
{ "Tadiran Scopus", 0x2200 },
|
||||
{ "BARCO AS", 0x2300 },
|
||||
{ "StarGuide", 0x2400 },
|
||||
{ "Mentor", 0x2500 },
|
||||
{ "EBU", 0x2600 },
|
||||
{ "GI", 0x4700 },
|
||||
{ "Telemann", 0x4800 }
|
||||
};
|
||||
|
||||
const char *
|
||||
psi_caid2name(uint16_t caid)
|
||||
{
|
||||
return val2str(caid, caidnametab);
|
||||
}
|
||||
|
|
2
psi.h
2
psi.h
|
@ -41,4 +41,6 @@ int psi_build_pat(th_transport_t *t, uint8_t *buf, int maxlen);
|
|||
|
||||
int psi_build_pmt(th_muxer_t *tm, uint8_t *buf0, int maxlen);
|
||||
|
||||
const char *psi_caid2name(uint16_t caid);
|
||||
|
||||
#endif /* PSI_H_ */
|
||||
|
|
13
transports.c
13
transports.c
|
@ -52,6 +52,7 @@
|
|||
#include "psi.h"
|
||||
#include "pes.h"
|
||||
#include "buffer.h"
|
||||
#include "plugin.h"
|
||||
|
||||
static dtimer_t transport_monitor_timer;
|
||||
|
||||
|
@ -63,6 +64,7 @@ transport_stop(th_transport_t *t, int flush_subscriptions)
|
|||
th_subscription_t *s;
|
||||
th_stream_t *st;
|
||||
th_pkt_t *pkt;
|
||||
th_plugin_t *p;
|
||||
|
||||
if(flush_subscriptions) {
|
||||
while((s = LIST_FIRST(&t->tht_subscriptions)) != NULL)
|
||||
|
@ -72,6 +74,10 @@ transport_stop(th_transport_t *t, int flush_subscriptions)
|
|||
return;
|
||||
}
|
||||
|
||||
LIST_FOREACH(p, &th_plugins, thp_link)
|
||||
if(p->thp_transport_stop != NULL)
|
||||
p->thp_transport_stop(p, &t->tht_plugin_aux, t);
|
||||
|
||||
t->tht_stop_feed(t);
|
||||
|
||||
t->tht_tt_commercial_advice = COMMERCIAL_UNKNOWN;
|
||||
|
@ -144,6 +150,7 @@ transport_start(th_transport_t *t, unsigned int weight)
|
|||
th_stream_t *st;
|
||||
AVCodec *c;
|
||||
enum CodecID id;
|
||||
th_plugin_t *p;
|
||||
|
||||
assert(t->tht_status != TRANSPORT_RUNNING);
|
||||
|
||||
|
@ -181,6 +188,12 @@ transport_start(th_transport_t *t, unsigned int weight)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
LIST_FOREACH(p, &th_plugins, thp_link)
|
||||
if(p->thp_transport_start != NULL)
|
||||
p->thp_transport_start(p, &t->tht_plugin_aux, t);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
54
tsdemux.c
54
tsdemux.c
|
@ -48,6 +48,7 @@
|
|||
#include "psi.h"
|
||||
#include "buffer.h"
|
||||
#include "tsdemux.h"
|
||||
#include "plugin.h"
|
||||
|
||||
static int
|
||||
ts_reassembly_pes(th_transport_t *t, th_stream_t *st, uint8_t *data, int len)
|
||||
|
@ -105,17 +106,38 @@ ts_reassembly(th_transport_t *t, th_stream_t *st, uint8_t *data, int len,
|
|||
st->st_buffer_ptr += len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Code for dealing with a complete section
|
||||
*/
|
||||
|
||||
static void
|
||||
got_section(th_transport_t *t, th_stream_t *st)
|
||||
{
|
||||
th_plugin_t *p;
|
||||
LIST_FOREACH(p, &th_plugins, thp_link)
|
||||
if(p->thp_psi_table != NULL)
|
||||
p->thp_psi_table(p, &t->tht_plugin_aux, t, st,
|
||||
st->st_section->ps_data, st->st_section->ps_offset);
|
||||
|
||||
if(st->st_got_section != NULL)
|
||||
st->st_got_section(t, st, st->st_section->ps_data,
|
||||
st->st_section->ps_offset);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Process transport stream packets
|
||||
*/
|
||||
void
|
||||
ts_recv_packet(th_transport_t *t, int pid, uint8_t *tsb)
|
||||
ts_recv_packet(th_transport_t *t, int pid, uint8_t *tsb, int doplugin)
|
||||
{
|
||||
th_stream_t *st = NULL;
|
||||
th_subscription_t *s;
|
||||
int cc, err = 0, afc, afl = 0;
|
||||
int len, pusi;
|
||||
pluginaux_t *pa;
|
||||
th_plugin_t *p;
|
||||
|
||||
LIST_FOREACH(st, &t->tht_streams, st_link)
|
||||
if(st->st_pid == pid)
|
||||
|
@ -124,12 +146,24 @@ ts_recv_packet(th_transport_t *t, int pid, uint8_t *tsb)
|
|||
if(st == NULL)
|
||||
return;
|
||||
|
||||
if(doplugin) {
|
||||
LIST_FOREACH(pa, &t->tht_plugin_aux, pa_link) {
|
||||
p = pa->pa_plugin;
|
||||
if(p->thp_tsb_process != NULL)
|
||||
if(p->thp_tsb_process(pa, t, st, tsb))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
avgstat_add(&t->tht_rate, 188, dispatch_clock);
|
||||
if((tsb[3] >> 6) & 3)
|
||||
return; /* channel is encrypted */
|
||||
|
||||
|
||||
LIST_FOREACH(s, &t->tht_subscriptions, ths_transport_link)
|
||||
if(s->ths_raw_input != NULL)
|
||||
s->ths_raw_input(s, tsb, 188, st, s->ths_opaque);
|
||||
|
||||
avgstat_add(&t->tht_rate, 188, dispatch_clock);
|
||||
|
||||
afc = (tsb[3] >> 4) & 3;
|
||||
|
||||
if(afc & 1) {
|
||||
|
@ -144,6 +178,7 @@ ts_recv_packet(th_transport_t *t, int pid, uint8_t *tsb)
|
|||
st->st_cc = (cc + 1) & 0xf;
|
||||
}
|
||||
|
||||
|
||||
if(afc & 2)
|
||||
afl = tsb[4] + 1;
|
||||
|
||||
|
@ -167,17 +202,16 @@ ts_recv_packet(th_transport_t *t, int pid, uint8_t *tsb)
|
|||
if(len > 0) {
|
||||
if(len > 188 - afl)
|
||||
break;
|
||||
if(!psi_section_reassemble(st->st_section, tsb + afl, len, 0, 1))
|
||||
st->st_got_section(t, st, st->st_section->ps_data,
|
||||
st->st_section->ps_offset);
|
||||
|
||||
if(!psi_section_reassemble(st->st_section, tsb + afl, len, 0,
|
||||
st->st_section_docrc))
|
||||
got_section(t, st);
|
||||
afl += len;
|
||||
}
|
||||
}
|
||||
|
||||
if(!psi_section_reassemble(st->st_section, tsb + afl, 188 - afl, pusi, 1))
|
||||
st->st_got_section(t, st, st->st_section->ps_data,
|
||||
st->st_section->ps_offset);
|
||||
if(!psi_section_reassemble(st->st_section, tsb + afl, 188 - afl, pusi,
|
||||
st->st_section_docrc))
|
||||
got_section(t, st);
|
||||
break;
|
||||
|
||||
case HTSTV_TELETEXT:
|
||||
|
|
|
@ -19,6 +19,6 @@
|
|||
#ifndef TSDEMUX_H
|
||||
#define TSDEMUX_H
|
||||
|
||||
void ts_recv_packet(th_transport_t *t, int pid, uint8_t *tsb);
|
||||
void ts_recv_packet(th_transport_t *t, int pid, uint8_t *tsb, int doplugin);
|
||||
|
||||
#endif /* TSDEMUX_H */
|
||||
|
|
26
tvhead.h
26
tvhead.h
|
@ -38,6 +38,17 @@ typedef enum {
|
|||
} th_commercial_advice_t;
|
||||
|
||||
|
||||
/**
|
||||
* Auxiliary data for plugins
|
||||
*/
|
||||
typedef struct pluginaux {
|
||||
LIST_ENTRY(pluginaux) pa_link;
|
||||
struct th_plugin *pa_plugin;
|
||||
} pluginaux_t;
|
||||
|
||||
LIST_HEAD(pluginaux_list, pluginaux);
|
||||
|
||||
|
||||
/*
|
||||
* Dispatch timer
|
||||
*/
|
||||
|
@ -255,7 +266,9 @@ typedef struct th_stream {
|
|||
int st_peak_presentation_delay; /* Max seen diff. of DTS and PTS */
|
||||
|
||||
struct psi_section *st_section;
|
||||
int st_section_docrc; /* Set if we should verify CRC on tables */
|
||||
pid_section_callback_t *st_got_section;
|
||||
void *st_got_section_opaque;
|
||||
|
||||
/* For transport stream packet reassembly */
|
||||
|
||||
|
@ -300,9 +313,14 @@ typedef struct th_stream {
|
|||
|
||||
struct th_pkt_queue st_pktq;
|
||||
|
||||
/* ca id for this stream */
|
||||
|
||||
uint16_t st_caid;
|
||||
|
||||
} th_stream_t;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* A Transport (or in MPEG TS terms: a 'service')
|
||||
*/
|
||||
|
@ -328,8 +346,7 @@ typedef struct th_transport {
|
|||
th_commercial_advice_t tht_tt_commercial_advice;
|
||||
|
||||
int tht_tt_rundown_content_length;
|
||||
time_t tht_tt_clock; /* Network clock as determined by teletext
|
||||
decoder */
|
||||
time_t tht_tt_clock; /* Network clock as determined by teletext decoder */
|
||||
int tht_prio;
|
||||
|
||||
struct th_stream_list tht_streams;
|
||||
|
@ -365,6 +382,8 @@ typedef struct th_transport {
|
|||
|
||||
struct th_muxer_list tht_muxers; /* muxers */
|
||||
|
||||
struct pluginaux_list tht_plugin_aux;
|
||||
|
||||
/*
|
||||
* Per source type structs
|
||||
*/
|
||||
|
@ -397,6 +416,9 @@ typedef struct th_transport {
|
|||
struct {
|
||||
struct avgen *avgen;
|
||||
} avgen;
|
||||
|
||||
char pad[256]; /* for api stability */
|
||||
|
||||
} u;
|
||||
|
||||
} th_transport_t;
|
||||
|
|
Loading…
Add table
Reference in a new issue