/*
    Tucnak - VHF contest log
    Copyright (C) 2002-2020  Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/

#include "header.h"

#include "cwdaemon.h"
#include "fifo.h"
#include "language2.h"
#include "ppdev.h" 
#include "rc.h"

#ifdef HAVE_LINUX_PPDEV_H

int parport_init(struct cwdaemon *cwda){
    cwda->fd = -1;
    parport_open(cwda, 1);
    return 0;
}

int parport_open(struct cwdaemon *cwda, int verbose){
	int mode;
	struct ppdev_frob_struct frob;
    char errbuf[1024];

    if (cwda->freeing) return -1;
    if (cwda->fd >=0 ) return 0;


    cwda->fd = open(cfg->cwda_device, O_RDWR|O_NONBLOCK);
    if (cwda->fd<0){
        if (verbose) log_addf(VTEXT(T_CANT_OPEN), cfg->cwda_device, strerror_r(errno, errbuf, sizeof(errbuf)));
        if (errno==ENOENT) {
            int len, devno;
            len=strcspn(cfg->cwda_device, "01234567890");
            if (len<strlen(cfg->cwda_device))
                devno=atoi(cfg->cwda_device+len);
            else
                devno=0;
            if (verbose) log_addf(VTEXT(T_TIP_MKNOD), cfg->cwda_device, devno, cfg->cwda_device); 
        }
        if (errno==EACCES && verbose) {
            log_addf(VTEXT(T_TIP_CHMOD), cfg->cwda_device); 
        }
        close(cwda->fd);
        cwda->fd=-1;
        return -1;
    }

    mode=PARPORT_MODE_PCSPP;
    if (ioctl(cwda->fd, PPSETMODE, &mode)<0){
        if (verbose) log_addf(VTEXT(T_CANT_SET_EPP), cfg->cwda_device, cfg->cwda_device, errno, strerror_r(errno, errbuf, sizeof(errbuf)));
        close(cwda->fd);
        cwda->fd=-1;
    return -1;
    }

    if (ioctl(cwda->fd, PPEXCL, NULL)<0){
        if (verbose) log_addf(VTEXT(T_CANT_EXCLUSIVE_ACCESS), cfg->cwda_device, cfg->cwda_device, errno, strerror_r(errno, errbuf, sizeof(errbuf)));
        close(cwda->fd);
        cwda->fd=-1;
        return -1;
    }

    if (ioctl(cwda->fd, PPCLAIM, NULL)<0){
        if (verbose) log_addf(VTEXT(T_CANT_CLAIM), cfg->cwda_device, strerror_r(errno, errbuf, sizeof(errbuf)));
        if (errno==ENXIO && verbose){
            log_addf(VTEXT(T_TIP_RMMOD));
            log_addf(VTEXT(T_TIP_KILL_LSOF), cfg->cwda_device);
        }
        close(cwda->fd);
        cwda->fd=-1;
        return -1;
    }
    
    
	frob.mask = PARPORT_CONTROL_STROBE;
	frob.val = PARPORT_CONTROL_STROBE;
	if (ioctl(cwda->fd, PPFCONTROL, &frob)<0){
        if (verbose) log_addf(VTEXT(T_CANT_SET_STROBE), strerror_r(errno, errbuf, sizeof(errbuf))); 
        close(cwda->fd);
        cwda->fd=-1;
        return 1;
    }
    parport_reset(cwda);
    return 0;
}

/* linux ppdev */
int parport_free(struct cwdaemon *cwda){
	struct ppdev_frob_struct frob;
    char errbuf[1024];
    
    if (!cwda) return 0;
	
    if (cwda->fd>=0){
        frob.mask = PARPORT_CONTROL_STROBE;
        frob.val = 0;
        if (ioctl(cwda->fd, PPFCONTROL, &frob)<0){
            dbg("Can't clear parport_STROBE %s\n", strerror_r(errno, errbuf, sizeof(errbuf))); 
        }
        close(cwda->fd);
        cwda->fd=-1;
    }
	return 0;
}

/* linux ppdev */
int parport_reset(struct cwdaemon *cwda){
    parport_ptt(cwda, 0);
    parport_cw(cwda, 0);
    parport_ssbway(cwda, 0);
	return 0;
}

/* linux ppdev */
int parport_cw(struct cwdaemon *cwda, int onoff){
	struct ppdev_frob_struct frob;
    char errbuf[1024];
    
    if (!cwda || cwda->fd<0) return 1;
    
	frob.mask = PARPORT_CONTROL_SELECT;
	frob.val = !onoff?PARPORT_CONTROL_SELECT:0;
	if (ioctl(cwda->fd, PPFCONTROL, &frob)<0){
        dbg("Can't %s parport_SELECT %s\n", onoff?"set":"clear", strerror_r(errno, errbuf, sizeof(errbuf))); 
        close(cwda->fd);
        cwda->fd=-1;
        return 1;
    }
    return 0;
}

/* linux ppdev */
int parport_ptt(struct cwdaemon *cwda, int onoff){
	struct ppdev_frob_struct frob;
    char errbuf[1024];
    
    if (!cwda) return 1;
    if (cwda->fd<0 && parport_open(cwda, 0)) return 1;
    
	frob.mask = PARPORT_CONTROL_INIT;
	frob.val = onoff?PARPORT_CONTROL_INIT:0;
	if (ioctl(cwda->fd, PPFCONTROL, &frob)<0){
        dbg("Can't %s parport_INIT %s\n", onoff?"set":"clear", strerror_r(errno, errbuf, sizeof(errbuf))); 
        close(cwda->fd);
        cwda->fd=-1;
        return 1;
    }
    return 0;
}

/* linux ppdev */
int parport_ssbway(struct cwdaemon *cwda, int onoff){
	struct ppdev_frob_struct frob;
    char errbuf[1024];
    
    /*dbg("ssbway(%d)\n", onoff);*/

    if (!cwda) return 1;
    if (cwda->fd<0 && parport_open(cwda, 0)) return 1;
    
	frob.mask = PARPORT_CONTROL_AUTOFD;
	frob.val = onoff?PARPORT_CONTROL_AUTOFD:0;
	if (ioctl(cwda->fd, PPFCONTROL, &frob)<0){
        dbg("Can't %s parport_AUTOFD %s\n", onoff?"set":"clear", strerror_r(errno, errbuf, sizeof(errbuf))); 
        close(cwda->fd);
        cwda->fd=-1;
        return 1;
    }
    return 0;
}

int parport_band(struct cwdaemon *cwda, int bandsw){
    if (!cwda) return 1;
    if (cwda->fd<0 && parport_open(cwda, 0)) return 1;

    cwda->data &= ~0xe1;
    if (bandsw & 0x01) cwda->data |= 0x01;
    if (bandsw & 0x02) cwda->data |= 0x20;
    if (bandsw & 0x04) cwda->data |= 0x40;
    if (bandsw & 0x08) cwda->data |= 0x80;

    if (ioctl(cwda->fd, PPWDATA, &cwda->data)<0){ 
        dbg("Can't write data to parport\n"); 
        close(cwda->fd);
        cwda->fd=-1;
        return 1;
    }
    return 0;
}

void parport_info(GString *gs){
    int port;
    int fd;
    char s[1024];
    unsigned int modes;
    int mode;

    g_string_append_printf(gs, "\n  parport_info:\n");
    for (port=0;port<4;port++){
        sprintf(s, "/dev/parport%d", port);
        fd=open(s, O_RDWR|O_NONBLOCK);
        if (fd<0){
            if (errno==ENOENT) continue;
            char *c = z_strdup_strerror(errno);
            g_string_append_printf(gs, "Found /dev/parport%d but cannot open it: %s\n", port, c);
            g_free(c);
            continue;
        }
        if (ioctl(fd, PPGETMODES, &modes)<0){
            if (errno==ENODEV) continue;
            char *c = z_strdup_strerror(errno);
            g_string_append_printf(gs, "Can't get modes for /dev/parport%d: %s\n", port, c);
            g_free(c);
            close(fd);
            continue;
        }
        g_string_append_printf(gs, "Found /dev/parport%d\n", port);
        
        g_string_append_printf(gs, " modes: 0x%x ", modes);
        if (modes&PARPORT_MODE_PCSPP)     g_string_append_printf(gs, "SPP ");
        if (modes&PARPORT_MODE_TRISTATE)  g_string_append_printf(gs, "TRISTATE ");
        if (modes&PARPORT_MODE_EPP)       g_string_append_printf(gs, "EPP ");
        if (modes&PARPORT_MODE_ECP)       g_string_append_printf(gs, "ECP ");
        if (modes&PARPORT_MODE_COMPAT)    g_string_append_printf(gs, "COMPAT ");
        if (modes&PARPORT_MODE_DMA)       g_string_append_printf(gs, "DMA ");
        if (modes&PARPORT_MODE_SAFEININT) g_string_append_printf(gs, "SAFEINIT ");
        g_string_append_printf(gs, "\n");
        
        if (ioctl(fd, PPGETMODE, &mode)<0){
            close(fd);
            continue;
        }
        g_string_append_printf(gs, " mode: 0x%x ", mode);
        if (mode==0) g_string_append_printf(gs, "NIBBLE ");
        if (mode&IEEE1284_MODE_BYTE)   g_string_append_printf(gs, "BYTE ");
        if (mode&IEEE1284_MODE_COMPAT) g_string_append_printf(gs, "COMPAT ");
        if (mode&IEEE1284_MODE_BECP)   g_string_append_printf(gs, "Bounded_ECP ");
        if (mode&IEEE1284_MODE_ECP)    g_string_append_printf(gs, "ECP ");
        if (mode&IEEE1284_MODE_ECPRLE) g_string_append_printf(gs, "ECP_RLE ");
        if (mode&IEEE1284_MODE_ECPSWE) g_string_append_printf(gs, "Software_ECP ");
        if (mode&IEEE1284_MODE_EPP)    g_string_append_printf(gs, "EPP ");
        if (mode&IEEE1284_MODE_EPPSL)  g_string_append_printf(gs, "EPP_1.7 ");
        if (mode&IEEE1284_MODE_EPPSWE) g_string_append_printf(gs, "Software_EPP ");
        if (mode&IEEE1284_DEVICEID)    g_string_append_printf(gs, "DEVICEID ");
        g_string_append_printf(gs, "\n");
        close(fd);
    }
    g_string_append_printf(gs, "\n");
}
#endif
