Skip to content
Snippets Groups Projects
mensactrl.c 7.36 KiB
Newer Older
daniel's avatar
daniel committed
/*
daniel's avatar
daniel committed
 * Control Mensadisplay connected to the LCDIF
daniel's avatar
daniel committed
 *
daniel's avatar
daniel committed
 * (C) 2014 Daniel Willmann <daniel@totalueberwachung.de>
daniel's avatar
daniel committed

 * 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 <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <math.h>

#include <zmq.h>

daniel's avatar
daniel committed
#include "common.h"
daniel's avatar
daniel committed
#define ROWS_PER_LINE 7
#define LINES_PER_MODULE 2
#define COLS_PER_MODULE 40

#define POWER_TIMEOUT 180000 /* milliseconds */
#define POWER_GPIO_PATH "/sys/class/gpio/gpio91/value"

daniel's avatar
daniel committed
struct mensa_fb {
daniel's avatar
daniel committed
	int x_res, y_res;
daniel's avatar
daniel committed
	int hmodules, vmodules;
daniel's avatar
daniel committed
	int fd;
	uint8_t *inputfb;
	uint16_t *fbmem;
daniel's avatar
daniel committed
	ssize_t size;
daniel's avatar
daniel committed
int verbose = 0;

daniel's avatar
daniel committed
static int is_inbounds(struct mensa_fb *mensafb, int x, int y, int width, int height)
{
    if (y < 0 || height < 0 || y + height > mensafb->y_res ||
		    x < 0 || width < 0 || x + width > mensafb->x_res)
	    return 0;
    return 1;
}

static int blit_area(struct mensa_fb *mensafb, const int col, const int row,
daniel's avatar
daniel committed
		const int width, const int height)
{
    int r, c;
    int vmpos, vpos, hmpos, hpos, pos;

daniel's avatar
daniel committed
    if (!is_inbounds(mensafb, col, row, width, height))
	    return -1;
daniel's avatar
daniel committed

    for (r = row; r < row + height; r++) {
Drahflow's avatar
Drahflow committed
	for (c = col; c < col + width; c++) {
	    /* Calculate module and position inside the module */
	    vmpos = r/(LINES_PER_MODULE*ROWS_PER_LINE);
	    vpos = r%(LINES_PER_MODULE*ROWS_PER_LINE);
	    hmpos = c/(COLS_PER_MODULE);
	    hpos = c%(COLS_PER_MODULE);
daniel's avatar
daniel committed

Drahflow's avatar
Drahflow committed
	    pos = hpos + hmpos*COLS_PER_MODULE*LINES_PER_MODULE;
daniel's avatar
daniel committed

Drahflow's avatar
Drahflow committed
	    if (vpos >= ROWS_PER_LINE) {
		vpos -= ROWS_PER_LINE;
		pos += COLS_PER_MODULE;
	    }
daniel's avatar
daniel committed

daniel's avatar
daniel committed
	    /* Invert order */
Drahflow's avatar
Drahflow committed
	    pos = (LINES_PER_MODULE * COLS_PER_MODULE * mensafb->hmodules - 1) - pos;
daniel's avatar
daniel committed

	    /* Add in row offset */
Drahflow's avatar
Drahflow committed
	    pos = pos + vpos*(mensafb->hmodules*COLS_PER_MODULE*LINES_PER_MODULE);
daniel's avatar
daniel committed

daniel's avatar
daniel committed

	    if (mensafb->inputfb[c + r * mensafb->x_res] == 0) {
Drahflow's avatar
Drahflow committed
		/* clear bit */
		mensafb->fbmem[pos] &= ~(1<<(mensafb->vmodules - 1 - vmpos));
	    } else {
		/* set bit */
		mensafb->fbmem[pos] |= (1<<(mensafb->vmodules - 1 - vmpos));
	    }
	}
daniel's avatar
daniel committed
    }
daniel's avatar
daniel committed
    return 0;
daniel's avatar
daniel committed
}

daniel's avatar
daniel committed
void setPixel(struct mensa_fb *mensafb, int col, int row, uint8_t bright)
{
	int idx = row * mensafb->x_res + col;

daniel's avatar
daniel committed
	if (!is_inbounds(mensafb, col, row, 1, 1))
	    return;
daniel's avatar
daniel committed

	mensafb->inputfb[idx] = bright;
daniel's avatar
daniel committed
static struct mensa_fb *setup_fb(const char *devname, int hmodules, int vmodules)
daniel's avatar
daniel committed
{
daniel's avatar
daniel committed
	struct mensa_fb *mensafb = malloc(sizeof(struct mensa_fb));
daniel's avatar
daniel committed
	int i;

daniel's avatar
daniel committed
	mensafb->x_res = COLS_PER_MODULE * hmodules;
	mensafb->y_res = ROWS_PER_LINE * LINES_PER_MODULE * vmodules;
	mensafb->size = mensafb->x_res * ROWS_PER_LINE * LINES_PER_MODULE;
daniel's avatar
daniel committed
	mensafb->fd = open(devname, O_RDWR);
	if (mensafb->fd < 0) {
daniel's avatar
daniel committed
		perror("opening fb");
daniel's avatar
daniel committed
		free(mensafb);
daniel's avatar
daniel committed
		exit(1);
	}
daniel's avatar
daniel committed
	mensafb->inputfb = malloc(mensafb->x_res * mensafb->y_res);
daniel's avatar
daniel committed
	if (!mensafb->inputfb) {
		free(mensafb);
daniel's avatar
daniel committed
		exit(1);
	}
daniel's avatar
daniel committed
	memset(mensafb->inputfb, 0, mensafb->x_res * mensafb->y_res);
	mensafb->fbmem=mmap(NULL, mensafb->size * 2 + mensafb->x_res * LINES_PER_MODULE * 2,
daniel's avatar
daniel committed
			PROT_READ|PROT_WRITE, MAP_SHARED, mensafb->fd, 0);
daniel's avatar
daniel committed
	if (mensafb->fbmem==NULL) {
daniel's avatar
daniel committed
		perror("mmap'ing fb");
daniel's avatar
daniel committed
		free(mensafb->inputfb);
		free(mensafb);
daniel's avatar
daniel committed
		exit(1);
	}
	for (i = 0; i < mensafb->size + mensafb->x_res * LINES_PER_MODULE; i++)
daniel's avatar
daniel committed
		mensafb->fbmem[i] = ((6 + i / (mensafb->x_res * LINES_PER_MODULE)) % 7)<<5;
daniel's avatar
daniel committed
	mensafb->hmodules = hmodules;
	mensafb->vmodules = vmodules;
daniel's avatar
daniel committed
	return mensafb;
daniel's avatar
daniel committed
int copyArea(struct mensa_fb *mensafb, int x, int y, int w, int h, uint8_t *data, size_t len)
{
	int row;
	if (!is_inbounds(mensafb, x, y, w, h)) {
		if (verbose)
			printf("Geometry overflow: %ix%i+%i+%i\n", x, y, w, h);
		return -1;
	}

	if (len != (size_t)w*h) {
		if (verbose)
Jan Luebbe's avatar
Jan Luebbe committed
			printf("Blit size mismatch: want %i, have %zi\n", w*h, len);
daniel's avatar
daniel committed
		return -1;
	}

	for (row = 0; row < h; row++) {
		int idx = (row + y) * mensafb->x_res + x;
daniel's avatar
daniel committed
		memcpy(&mensafb->inputfb[idx], &data[row*w], w);
	}
	return 0;
}

int handlePixel(struct mensa_fb *mensafb, struct pixel *pix, size_t len) {
	if (len != sizeof(struct pixel))
		return -1;

	if (!is_inbounds(mensafb, pix->x, pix->y, 1, 1)) {
		return -1;
	}

Drahflow's avatar
Drahflow committed
	setPixel(mensafb, pix->x, pix->y, pix->bright);
daniel's avatar
daniel committed
	return blit_area(mensafb, pix->x, pix->y, 1, 1);
}

int handleBlit(struct mensa_fb *mensafb, struct blit *blit, size_t len)
{
	int rc;

	if (len < sizeof(struct blit))
		return -1;

	if (!is_inbounds(mensafb, blit->x, blit->y, blit->width, blit->height)) {
		return -1;
	}

	rc = copyArea(mensafb, blit->x, blit->y, blit->width, blit->height, blit->data, len - sizeof(struct blit));
	if (rc < 0)
		return rc;

	return blit_area(mensafb, blit->x, blit->y, blit->width, blit->height);
}

int handleFill(struct mensa_fb *mensafb, struct fill *fill, size_t len) {
	int row;

	if (len != sizeof(struct fill))
		return -1;

	if (!is_inbounds(mensafb, fill->x, fill->y, fill->width, fill->height)) {
		return -1;
	}

	for (row = fill->y; row < fill->y + fill->height; row++) {
		int idx = row * mensafb->x_res + fill->x;
		memset(&mensafb->inputfb[idx], fill->bright, fill->width);
	}

	return blit_area(mensafb, fill->x, fill->y, fill->width, fill->height);
Drahflow's avatar
Drahflow committed
}

daniel's avatar
daniel committed
void handleCommand(struct mensa_fb *mensafb, struct packet *p, size_t len) {
	int rc = 0;
Drahflow's avatar
Drahflow committed
	switch(p->cmd) {
daniel's avatar
daniel committed
		case CMD_PIXEL: rc = handlePixel(mensafb, &p->pixel, len - 1); break;
		case CMD_BLIT: rc = handleBlit(mensafb, &p->blit, len - 1); break;
		case CMD_FILL: rc = handleFill(mensafb, &p->fill, len - 1); break;
	default:
	       if (verbose)
		       printf("Unknown command: 0x%02x\n", p->cmd);
Drahflow's avatar
Drahflow committed
	}
daniel's avatar
daniel committed
	if ((rc < 0) && (verbose))
		printf("Error handling command 0x%02x\n", p->cmd);
Drahflow's avatar
Drahflow committed
}

int setPower(int enabled) {
	int fd;

	if (power == enabled)
	    return 0;

	fd = open(POWER_GPIO_PATH, O_WRONLY);
	if  (fd < 0) {
		perror("open power gpio");
		return fd;
	}

	write(fd, enabled ? "1" : "0", 1);

	power = enabled;

	close(fd);
	return 0;
}

daniel's avatar
daniel committed
int main(int argc, char *argv[]) {
daniel's avatar
daniel committed
	struct mensa_fb *mensafb;
daniel's avatar
daniel committed
	void *context = zmq_ctx_new ();
daniel's avatar
daniel committed
	void *responder = zmq_socket (context, ZMQ_REP);
	int rc, timeout = POWER_TIMEOUT;
daniel's avatar
daniel committed

	if (argc != 2)
		exit(1);

	rc = zmq_setsockopt (responder, ZMQ_RCVTIMEO, &timeout, sizeof(timeout));
	if (rc < 0)
		perror("zmq_setsockopt");

daniel's avatar
daniel committed
	rc = zmq_bind (responder, "tcp://*:5556");
daniel's avatar
daniel committed
	if (rc < 0)
daniel's avatar
daniel committed
		perror("zmq_bind");
daniel's avatar
daniel committed
	mensafb = setup_fb(argv[1], 12, 5);
daniel's avatar
daniel committed

	while (1) {
                while (1) {
Drahflow's avatar
Drahflow committed
			zmq_msg_t message;
			zmq_msg_init (&message);
			rc = zmq_msg_recv (&message, responder, 0);
			if (rc < 0 && errno == EAGAIN)
				setPower(0);
Drahflow's avatar
Drahflow committed

			if(zmq_msg_size(&message)) {
				setPower(1);
daniel's avatar
daniel committed
				handleCommand(mensafb, (struct packet *)zmq_msg_data(&message), zmq_msg_size(&message));
Drahflow's avatar
Drahflow committed
			}

			if (!zmq_msg_more(&message)) {
				zmq_msg_close(&message);
				break;
			}
Drahflow's avatar
Drahflow committed

			zmq_msg_close(&message);
		}
Drahflow's avatar
Drahflow committed

		zmq_send(responder, NULL, 0, 0);
daniel's avatar
daniel committed
	}

	return 0;
}