summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgpoirier <gpoirier@b3059339-0415-0410-9bf9-f77b7e298cf2>2006-05-17 20:56:49 +0000
committergpoirier <gpoirier@b3059339-0415-0410-9bf9-f77b7e298cf2>2006-05-17 20:56:49 +0000
commit47e525833dafe0b3761cf5dbf62a4a39b9057d99 (patch)
treeb421a236f8df574ee6bb742e98d2a152cc533156
parentfb1abd6b3bbd89f9d7d37c0421ebdec8bee1d1f7 (diff)
downloadmpv-47e525833dafe0b3761cf5dbf62a4a39b9057d99.tar.bz2
mpv-47e525833dafe0b3761cf5dbf62a4a39b9057d99.tar.xz
Add YUY2 and back end scaling on S3 Virge chips in combination with fbdev.
Patch by Mark Sanderson < mmp AH kiora POIS ath POIS cx> git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@18536 b3059339-0415-0410-9bf9-f77b7e298cf2
-rw-r--r--AUTHORS3
-rw-r--r--DOCS/xml/en/video.xml7
-rwxr-xr-xconfigure16
-rw-r--r--libvo/video_out.c4
-rw-r--r--libvo/vo_s3fb.c515
5 files changed, 545 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
index e747adea5b..ef67821fa7 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -668,6 +668,9 @@ Sabbi, Nico <nsabbi@tiscali.it>
Sandell, Björn <biorn@dce.chalmers.se>
* various *BSD fixes
+Sanderson, Mark <mmp@kiora.ath.cx>
+ * -vo s3fb driver
+
Sauerbeck, Tilman <tsauerbeck@users.sourceforge.net>
* TGA decoder (RLE/uncomp), -mf tga support
diff --git a/DOCS/xml/en/video.xml b/DOCS/xml/en/video.xml
index c13b8f5877..8e9d6c6a45 100644
--- a/DOCS/xml/en/video.xml
+++ b/DOCS/xml/en/video.xml
@@ -190,6 +190,13 @@ S3 Savage3D's should work fine, but for Savage4, use XFree86 version 4.0.3
or greater (in case of image problems, try 16bpp). As for S3 Virge: there is
xv support, but the card itself is very slow, so you better sell it.
</para>
+<para>
+There is now a native framebuffer driver for S3 Virge cards similiar to
+tdfxfb. Set up your framebuffer (eg append
+"<option>vga=792 video=vesa:mtrr</option>" to your kernel) and use
+<option>-vo s3fb</option> (<option>-vf yuy2</option> and <option>-dr</option>
+will also help).
+</para>
<note>
<para>
diff --git a/configure b/configure
index 801f7dc3b5..d857b45333 100755
--- a/configure
+++ b/configure
@@ -329,6 +329,7 @@ Video output:
--enable-mlib build with mediaLib support (Solaris only) [disable]
--enable-3dfx build with obsolete /dev/3dfx support [disable]
--enable-tdfxfb build with tdfxfb (Voodoo 3/banshee) support [disable]
+ --enable-s3fb build with s3fb (S3 ViRGE) support [disable]
--enable-directfb build with DirectFB support [autodetect]
--enable-zr build with ZR360[56]7/ZR36060 support [autodetect]
--enable-bl build with Blinkenlights support [disable]
@@ -1660,6 +1661,7 @@ _gtk1=no
_termcap=auto
_termios=auto
_3dfx=no
+_s3fb=no
_tdfxfb=no
_tdfxvid=no
_tga=yes
@@ -1937,6 +1939,8 @@ for ac_option do
--disable-termios) _termios=no ;;
--enable-3dfx) _3dfx=yes ;;
--disable-3dfx) _3dfx=no ;;
+ --enable-s3fb) _s3fb=yes ;;
+ --disable-s3fb) _s3fb=no ;;
--enable-tdfxfb) _tdfxfb=yes ;;
--disable-tdfxvid) _tdfxvid=no ;;
--enable-tdfxvid) _tdfxvid=yes ;;
@@ -3594,6 +3598,17 @@ else
fi
echores "$_tdfxfb"
+echocheck "s3fb"
+if test "$_s3fb" = yes ; then
+ _def_s3fb='#define HAVE_S3FB 1'
+ _vosrc="$_vosrc vo_s3fb.c"
+ _vomodules="s3fb $_vomodules"
+else
+ _def_s3fb='#undef HAVE_S3FB'
+ _novomodules="s3fb $_novomodules"
+fi
+echores "$_s3fb"
+
echocheck "tdfxvid"
if test "$_tdfxvid" = yes ; then
_def_tdfxvid='#define HAVE_TDFX_VID 1'
@@ -8212,6 +8227,7 @@ $_def_directx
$_def_ggi
$_def_ggiwmh
$_def_3dfx
+$_def_s3fb
$_def_tdfxfb
$_def_tdfxvid
$_def_directfb
diff --git a/libvo/video_out.c b/libvo/video_out.c
index e4fa21f5ba..f4ff125d51 100644
--- a/libvo/video_out.c
+++ b/libvo/video_out.c
@@ -77,6 +77,7 @@ extern vo_functions_t video_out_fsdga;
extern vo_functions_t video_out_sdl;
extern vo_functions_t video_out_3dfx;
extern vo_functions_t video_out_tdfxfb;
+extern vo_functions_t video_out_s3fb;
extern vo_functions_t video_out_null;
//extern vo_functions_t video_out_odivx;
extern vo_functions_t video_out_zr;
@@ -164,6 +165,9 @@ vo_functions_t* video_out_drivers[] =
#ifdef HAVE_TDFXFB
&video_out_tdfxfb,
#endif
+#ifdef HAVE_S3FB
+ &video_out_s3fb,
+#endif
#ifdef HAVE_3DFX
&video_out_3dfx,
#endif
diff --git a/libvo/vo_s3fb.c b/libvo/vo_s3fb.c
new file mode 100644
index 0000000000..8355b1ad9f
--- /dev/null
+++ b/libvo/vo_s3fb.c
@@ -0,0 +1,515 @@
+/* Copyright (C) Mark Sanderson, 2006, <mmp@kiora.ath.cx>.
+ * Released under the terms and conditions of the GPL.
+ *
+ * 30-Mar-2006 Modified from tdfxfb.c by Mark Zealey
+ *
+ * Hints and tricks:
+ * - Use -dr to get direct rendering
+ * - Use -vf yuy2 to get yuy2 rendering, *MUCH* faster than yv12
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <linux/fb.h>
+#include <asm/io.h>
+
+#include "config.h"
+#include "fastmemcpy.h"
+#include "video_out.h"
+#include "video_out_internal.h"
+#include "aspect.h"
+#include "sub.h"
+
+static vo_info_t info =
+ {
+ "S3 Virge over fbdev",
+ "s3fb",
+ "Mark Sanderson <mmp@kiora.ath.cx>",
+ ""
+ };
+
+LIBVO_EXTERN(s3fb)
+
+ static int fd = -1;
+ static struct fb_fix_screeninfo fb_finfo;
+ static struct fb_var_screeninfo fb_vinfo;
+ static uint32_t in_width, in_height, in_format, in_depth, in_s3_format,
+ screenwidth, screenheight, screendepth, screenstride,
+ vidwidth, vidheight, vidx, vidy, page, offset, sreg;
+ static char *inpage, *inpage0, *smem = NULL;
+ static void (*alpha_func)();
+
+static void clear_screen();
+
+/* streams registers */
+#define PSTREAM_CONTROL_REG 0x8180
+#define COL_CHROMA_KEY_CONTROL_REG 0x8184
+#define SSTREAM_CONTROL_REG 0x8190
+#define CHROMA_KEY_UPPER_BOUND_REG 0x8194
+#define SSTREAM_STRETCH_REG 0x8198
+#define BLEND_CONTROL_REG 0x81A0
+#define PSTREAM_FBADDR0_REG 0x81C0
+#define PSTREAM_FBADDR1_REG 0x81C4
+#define PSTREAM_STRIDE_REG 0x81C8
+#define DOUBLE_BUFFER_REG 0x81CC
+#define SSTREAM_FBADDR0_REG 0x81D0
+#define SSTREAM_FBADDR1_REG 0x81D4
+#define SSTREAM_STRIDE_REG 0x81D8
+#define OPAQUE_OVERLAY_CONTROL_REG 0x81DC
+#define K1_VSCALE_REG 0x81E0
+#define K2_VSCALE_REG 0x81E4
+#define DDA_VERT_REG 0x81E8
+#define STREAMS_FIFO_REG 0x81EC
+#define PSTREAM_START_REG 0x81F0
+#define PSTREAM_WINDOW_SIZE_REG 0x81F4
+#define SSTREAM_START_REG 0x81F8
+#define SSTREAM_WINDOW_SIZE_REG 0x81FC
+
+#define S3_MEMBASE sreg
+#define S3_NEWMMIO_REGBASE 0x1000000 /* 16MB */
+#define S3_NEWMMIO_REGSIZE 0x10000 /* 64KB */
+#define S3V_MMIO_REGSIZE 0x8000 /* 32KB */
+#define S3_NEWMMIO_VGABASE (S3_NEWMMIO_REGBASE + 0x8000)
+
+#define OUTREG(mmreg, value) *(unsigned int *)(&v.mmio[mmreg]) = value
+
+typedef struct vga_type {
+ int cr38, cr39, cr53;
+ unsigned char *mmio;
+} vga_t;
+
+int readcrtc(int reg) {
+ outb(reg, 0x3d4);
+ return inb(0x3d5);
+}
+
+void writecrtc(int reg, int value) {
+ outb(reg, 0x3d4);
+ outb(value, 0x3d5);
+}
+
+int enable(vga_t *v) {
+ int fd;
+
+ // enable registers
+ if (iopl(3) != 0)
+ return 0;
+ v->cr38 = readcrtc(0x38);
+ v->cr39 = readcrtc(0x39);
+ v->cr53 = readcrtc(0x53);
+ writecrtc(0x38, 0x48);
+ writecrtc(0x39, 0xa5);
+ writecrtc(0x53, 0x08);
+ fd = open("/dev/mem", O_RDWR);
+ v->mmio = mmap(0, S3_NEWMMIO_REGSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd,
+ S3_MEMBASE + S3_NEWMMIO_REGBASE);
+ close(fd);
+ return 1;
+}
+
+void disable(vga_t *v) {
+ writecrtc(0x53, v->cr53);
+ writecrtc(0x39, v->cr39);
+ writecrtc(0x38, v->cr38);
+ iopl(0);
+ munmap(v->mmio, S3_NEWMMIO_REGSIZE);
+}
+
+int yuv_on(int format, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h, int crop, int xres, int yres, int line_length, int offset) {
+ int tmp, pitch, start, src_wc, src_hc, bpp;
+ vga_t v;
+
+ if (format == 0 || format == 7)
+ bpp = 4;
+ else if (format == 6)
+ bpp = 3;
+ else
+ bpp = 2;
+
+ src_wc = src_w - crop * 2;
+ src_hc = src_h - crop * 2;
+ pitch = src_w * bpp;
+
+ // video card memory layout:
+ // 0-n: visable screen memory, n = width * height * bytes per pixel
+ // n-m: scaler source memory, n is aligned to a page boundary
+ // m+: scaler source memory for multiple buffers
+
+ // offset is the first aligned byte after the screen memory, where the scaler input buffer is
+ tmp = (yres * line_length + 4095) & ~4095;
+ offset += tmp;
+
+ // start is the top left viewable scaler input pixel
+ start = offset + crop * pitch + crop * bpp;
+
+ if (!enable(&v))
+ return 0;
+
+ OUTREG(COL_CHROMA_KEY_CONTROL_REG, 0x47000000);
+ OUTREG(CHROMA_KEY_UPPER_BOUND_REG, 0x0);
+ OUTREG(BLEND_CONTROL_REG, 0x00000020);
+ OUTREG(DOUBLE_BUFFER_REG, 0x0); /* Choose fbaddr0 as stream source. */
+ OUTREG(OPAQUE_OVERLAY_CONTROL_REG, 0x0);
+
+ OUTREG(PSTREAM_CONTROL_REG, 0x06000000);
+ OUTREG(PSTREAM_FBADDR0_REG, 0x0);
+ OUTREG(PSTREAM_FBADDR1_REG, 0x0);
+ OUTREG(PSTREAM_STRIDE_REG, line_length);
+ OUTREG(PSTREAM_START_REG, 0x00010001);
+ OUTREG(PSTREAM_WINDOW_SIZE_REG, 0x00010001);
+ //OUTREG(SSTREAM_WINDOW_SIZE_REG, ( ((xres-1) << 16) | yres) & 0x7ff07ff);
+
+ if (dst_w == src_w)
+ tmp = 0;
+ else
+ tmp = 2;
+ /* format 1=YCbCr-16 2=YUV-16 3=BGR15 4=YUV-16/32(mixed 2/4byte stride) 5=BGR16 6=BGR24 0,7=BGR32 */
+ /* The YUV format pixel has a range of value from 0 to 255, while the YCbCr format pixel values are in the range of 16 to 240. */
+ OUTREG(SSTREAM_CONTROL_REG, tmp << 28 | (format << 24) |
+ ((((src_wc-1)<<1)-(dst_w-1)) & 0xfff));
+ OUTREG(SSTREAM_STRETCH_REG,
+ ((src_wc - 1) & 0x7ff) | (((src_wc - dst_w-1) & 0x7ff) << 16));
+ OUTREG(SSTREAM_FBADDR0_REG, start & 0x3fffff );
+ OUTREG(SSTREAM_STRIDE_REG, pitch & 0xfff );
+ OUTREG(SSTREAM_START_REG, ((dst_x + 1) << 16) | (dst_y + 1));
+ OUTREG(SSTREAM_WINDOW_SIZE_REG, ( ((dst_w-1) << 16) | (dst_h ) ) & 0x7ff07ff);
+ OUTREG(K1_VSCALE_REG, src_hc - 1 );
+ OUTREG(K2_VSCALE_REG, (src_hc - dst_h) & 0x7ff );
+ /* 0xc000 = bw & vert interp */
+ /* 0x8000 = no bw save */
+ OUTREG(DDA_VERT_REG, (((~dst_h)-1) & 0xfff ) | 0xc000);
+ writecrtc(0x92, (((pitch + 7) / 8) >> 8) | 0x80);
+ writecrtc(0x93, (pitch + 7) / 8);
+
+ writecrtc(0x67, readcrtc(0x67) | 0x4);
+
+ disable(&v);
+
+ return offset;
+}
+
+void yuv_off() {
+ vga_t v;
+
+ enable(&v);
+
+ writecrtc(0x67, readcrtc(0x67) & ~0xc);
+ memset(v.mmio + 0x8180, 0, 0x80);
+ OUTREG(0x81b8, 0x900);
+ OUTREG(0x81bc, 0x900);
+ OUTREG(0x81c8, 0x900);
+ OUTREG(0x81cc, 0x900);
+ OUTREG(0x81d8, 0x1);
+ OUTREG(0x81f8, 0x07ff07ff);
+ OUTREG(0x81fc, 0x00010001);
+ writecrtc(0x92, 0);
+ writecrtc(0x93, 0);
+ disable(&v);
+}
+
+static int preinit(const char *arg)
+{
+ char *name;
+
+ if(arg)
+ name = (char*)arg;
+ else if(!(name = getenv("FRAMEBUFFER")))
+ name = "/dev/fb0";
+
+ if((fd = open(name, O_RDWR)) == -1) {
+ printf("s3fb: can't open %s: %s\n", name, strerror(errno));
+ return -1;
+ }
+
+ if(ioctl(fd, FBIOGET_FSCREENINFO, &fb_finfo)) {
+ printf("s3fb: problem with FBITGET_FSCREENINFO ioctl: %s\n",
+ strerror(errno));
+ close(fd);
+ fd = -1;
+ return -1;
+ }
+
+ if(ioctl(fd, FBIOGET_VSCREENINFO, &fb_vinfo)) {
+ printf("s3fb: problem with FBITGET_VSCREENINFO ioctl: %s\n",
+ strerror(errno));
+ close(fd);
+ fd = -1;
+ return -1;
+ }
+
+ // Check the depth now as config() musn't fail
+ switch(fb_vinfo.bits_per_pixel) {
+ case 16:
+ case 24:
+ case 32:
+ break; // Ok
+ default:
+ printf("s3fb: %d bpp output is not supported\n", fb_vinfo.bits_per_pixel);
+ close(fd);
+ fd = -1;
+ return -1;
+ }
+
+ /* Open up a window to the hardware */
+ smem = mmap(0, fb_finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ sreg = fb_finfo.smem_start;
+
+ if((long)smem == -1) {
+ printf("s3fb: Couldn't map memory areas: %s\n", strerror(errno));
+ if((long)smem != -1)
+ munmap(smem, fb_finfo.smem_len);
+ smem = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+static void uninit(void)
+{
+ if (inpage0) {
+ clear_screen();
+ yuv_off();
+ inpage0 = NULL;
+ }
+
+ /* And close our mess */
+ if(smem) {
+ munmap(smem, fb_finfo.smem_len);
+ smem = NULL;
+ }
+
+ if(fd != -1) {
+ close(fd);
+ fd = -1;
+ }
+}
+
+static void clear_screen()
+{
+ if (inpage0) {
+ int n;
+
+ memset(smem, 0, screenheight * screenstride);
+
+ if (in_format == IMGFMT_YUY2) {
+ unsigned short *ptr;
+ int i;
+
+ ptr = (unsigned short *)inpage0;
+ n = in_width * in_height;
+ if (vo_doublebuffering)
+ n *= 2;
+ for(i=0; i<n; i++)
+ *ptr++ = 0x8000;
+
+ } else {
+ n = in_depth * in_width * in_height;
+ if (vo_doublebuffering)
+ n *= 2;
+ memset(inpage0, 0, n);
+ }
+ }
+}
+
+/* Setup output screen dimensions etc */
+static void setup_screen(uint32_t full)
+{
+ int inpageoffset;
+
+ aspect(&vidwidth, &vidheight, full ? A_ZOOM : A_NOZOOM);
+
+ // center picture
+ vidx = (screenwidth - vidwidth) / 2;
+ vidy = (screenheight - vidheight) / 2;
+
+ geometry(&vidx, &vidy, &vidwidth, &vidheight, screenwidth, screenheight);
+ vo_fs = full;
+
+ inpageoffset = yuv_on(in_s3_format, in_width, in_height, vidx, vidy, vidwidth, vidheight, 0, screenwidth, screenheight, screenstride, 0);
+ inpage0 = smem + inpageoffset;
+ inpage = inpage0;
+ printf("s3fb: output is at %dx%d +%dx%d\n", vidx, vidy, vidwidth, vidheight);
+
+ clear_screen();
+}
+
+static int config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height,
+ uint32_t flags, char *title, uint32_t format)
+{
+ screenwidth = fb_vinfo.xres;
+ screenheight = fb_vinfo.yres;
+ screenstride = fb_finfo.line_length;
+ aspect_save_screenres(fb_vinfo.xres,fb_vinfo.yres);
+
+ in_width = width;
+ in_height = height;
+ in_format = format;
+ aspect_save_orig(width,height);
+
+ aspect_save_prescale(d_width,d_height);
+
+ /* Setup the screen for rendering to */
+ screendepth = fb_vinfo.bits_per_pixel / 8;
+
+ switch(in_format) {
+
+ case IMGFMT_YUY2:
+ in_depth = 2;
+ in_s3_format = 1;
+ alpha_func = vo_draw_alpha_yuy2;
+ break;
+
+ case IMGFMT_BGR15:
+ in_depth = 2;
+ in_s3_format = 3;
+ alpha_func = vo_draw_alpha_rgb16;
+ break;
+
+ case IMGFMT_BGR16:
+ in_depth = 2;
+ in_s3_format = 5;
+ alpha_func = vo_draw_alpha_rgb16;
+ break;
+
+ case IMGFMT_BGR24:
+ in_depth = 3;
+ in_s3_format = 6;
+ alpha_func = vo_draw_alpha_rgb24;
+ break;
+
+ case IMGFMT_BGR32:
+ in_depth = 4;
+ in_s3_format = 7;
+ alpha_func = vo_draw_alpha_rgb32;
+ break;
+
+ default:
+ printf("s3fb: Eik! Something's wrong with control().\n");
+ return -1;
+ }
+
+ offset = in_width * in_depth * in_height;
+ if (vo_doublebuffering)
+ page = offset;
+ else
+ page = 0;
+
+ if(screenheight * screenstride + page + offset > fb_finfo.smem_len) {
+ printf("s3fb: Not enough video memory to play this movie. Try at a lower resolution\n");
+ return -1;
+ }
+
+ setup_screen(flags & VOFLAG_FULLSCREEN);
+ if (vo_doublebuffering)
+ inpage = inpage0 + page;
+
+ printf("s3fb: screen is %dx%d at %d bpp, in is %dx%d at %d bpp, norm is %dx%d\n",
+ screenwidth, screenheight, screendepth * 8,
+ in_width, in_height, in_depth * 8,
+ d_width, d_height);
+
+ return 0;
+}
+
+static void draw_alpha(int x, int y, int w, int h, unsigned char *src,
+ unsigned char *srca, int stride)
+{
+ char *dst = inpage + (y * in_width + x) * in_depth;
+ alpha_func(w, h, src, srca, stride, dst, in_width * in_depth);
+}
+
+static void draw_osd(void)
+{
+ if (!vo_doublebuffering)
+ vo_draw_text(in_width, in_height, draw_alpha);
+}
+
+/* Render onto the screen */
+static void flip_page(void)
+{
+ if(vo_doublebuffering) {
+ vo_draw_text(in_width, in_height, draw_alpha);
+ yuv_on(in_s3_format, in_width, in_height, vidx, vidy, vidwidth, vidheight, 0, screenwidth, screenheight, screenstride, page);
+ page ^= offset;
+ inpage = inpage0 + page;
+ }
+}
+
+static int draw_frame(uint8_t *src[])
+{
+ mem2agpcpy(inpage, src[0], in_width * in_depth * in_height);
+ return 0;
+}
+
+static int draw_slice(uint8_t *i[], int s[], int w, int h, int x, int y)
+{
+ return 1;
+}
+
+/* Attempt to start doing DR */
+static uint32_t get_image(mp_image_t *mpi)
+{
+
+ if(mpi->flags & MP_IMGFLAG_READABLE)
+ return VO_FALSE;
+ if(mpi->type == MP_IMGTYPE_STATIC && vo_doublebuffering)
+ return VO_FALSE;
+ if(mpi->type > MP_IMGTYPE_TEMP)
+ return VO_FALSE; // TODO ??
+
+ switch(in_format) {
+ case IMGFMT_BGR15:
+ case IMGFMT_BGR16:
+ case IMGFMT_BGR24:
+ case IMGFMT_BGR32:
+ case IMGFMT_YUY2:
+ mpi->planes[0] = inpage;
+ mpi->stride[0] = in_width * in_depth;
+ break;
+
+ default:
+ return VO_FALSE;
+ }
+
+ mpi->width = in_width;
+ mpi->flags |= MP_IMGFLAG_DIRECT;
+
+ return VO_TRUE;
+}
+
+static int control(uint32_t request, void *data, ...)
+{
+ switch(request) {
+ case VOCTRL_GET_IMAGE:
+ return get_image(data);
+
+ case VOCTRL_QUERY_FORMAT:
+ switch(*((uint32_t*)data)) {
+ case IMGFMT_BGR15:
+ case IMGFMT_BGR16:
+ case IMGFMT_BGR24:
+ case IMGFMT_BGR32:
+ case IMGFMT_YUY2:
+ return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW |
+ VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN;
+ }
+
+ return 0; /* Not supported */
+
+ case VOCTRL_FULLSCREEN:
+ setup_screen(!vo_fs);
+ return 0;
+ }
+
+ return VO_NOTIMPL;
+}
+
+/* Dummy funcs */
+static void check_events(void) {}