/*
 * Velleman K8048 Programmer for FreeBSD and others.
 *
 * Copyright (c) 2005-2007 Darron Broad
 * All rights reserved.
 *
 * Licensed under the terms of the BSD license, see file LICENSE
 * for details.
 *
 * $Id: k8048.c,v 1.61 2008/02/20 04:40:40 darron Exp $
 *
 * Contrib:
 *  Increased device name length for MACOS/X by Tobias Braun
 */

#include "k8048.h"

/*
 * usage
 */
void
usage(struct k8048 *k, char *execname, char *errormsg)
{
    printf( "\nk8048: [%s]\n\n", execname );

    printf( "Usage:\n\n"

            "   Command        Help\n"
            "   -------        ----\n"
            "   k8048       -  Usage\n"
            "   kdebug      -  Debug PIC16F627\n"
            "   ktest n     -  Test I/O with n seconds between each test\n"
            "   k14 a [p]   -  Use 14 bit word architecture (see below for params, and (1) a)\n"
            "   k16 a [p]   -  Use 16 bit word architecture (see below for params, and (1) b)\n\n"

            "   Action(a)      Parameter(p)   Help\n"
            "   ---------      ------------   ----\n"
            "   (i)d                        - Dump device identification\n"
            "   (c)onfig    -  [config]     - Dump or write config word (see (2) below)\n"
            "   (o)sccal    -  [osccal]     - Dump or write oscillator calibration word (see (2) )\n"
            "   (f)lash     -  [size]       - Dump %d or size bytes of program flash\n"
            "   (e)eeprom   -  [size]       - Dump %d or size bytes of data eeprom\n"
            "   (p)rogram   -  filename     - Program intel hex32 file into flash\n" 
            "   (v)erify    -  filename     - Verify intel hex32 file in flash\n"
            "   (u)nprotect                 - Disable program/data protection\n"
            "   (b)lank                     - Bulk erase device\n"
            "   (s)elect    -  device       - Force selection of device rather than probe (see (3) )\n\n"
            
            "   (1) a. Some PIC12 and most PIC16 are 14 bit word achitecture (k14).\n"
            "       b. PIC18 are 16 bit word architecture (k16).\n\n"

            "   (2) 12F/16F 14 bit word devices with OSCCAL and BG only.\n"
            "       The Device is blanked prior to writing either calibration or bandgap bits.\n\n"

            "   (3) The selection action must precede one other action.\n"
            "       For older PIC16F84 devices force selection with PIC16F84\n"
            "       Eg. k14 s PIC16F84 p mycode.hex\n\n",

            DEFAULT_SIZE, DEFAULT_SIZE);

    printf( "Examples:\n\n"

            "   ktest 10                 - Test I/O\n"
            "   k14 c                    - Dump config\n"
            "   k14 c 0x11ff             - Write config\n"
            "   k16 p mycode.hex         - Program hex32 file\n"
            "   k16 v mycode.hex         - Verify hex32 file\n\n");

    printf( "Configuration:\n\n"
            "   %s\n\n"

            "   # Serial device\n"
            "   DEVICE=%s\n\n"

            "   # Bit rules for I/O\n"
            "   # + 0x01 FLIPDTR      (PGD out)    (needed for k8048, not needed for k8076)\n"
            "   # + 0x02 FLIPRTS      (PGC out)    (needed for k8048, not needed for k8076)\n"
            "   # + 0x04 FLIPTX       (VPP out)    (needed for k8048, not needed for k8076)\n"
            "   # + 0x08 FLIPCTS      (PGD in)     (needed for k8048, not needed for k8076)\n"
            "   # + 0x10 ENABLEPULLUP (PGD pullup) (needed for k8048, may be needed for k8076)\n\n"

            "   # k8048: +FLIPDTR +FLIPRTS +FLIPTX +FLIPCTS +ENABLEPULLUP\n"
            "   # k8076: +ENABLEPULLUP\n"
            "   BITRULES=0x%02x\n\n",

            k->dotfile, k->device, k->flags);
    
    if(errormsg!=NULL)
    {
        printf( "Error:\n\n"
                "   %s: %s\n\n",
                execname, errormsg);
        exit(-1);
    }
    exit(0);
}

/*
 * open serial port and exec user command
 */
int main(int argc, char **argv)
{
    struct k8048 k;
    char *execdup, *execname;
    char *filename;
    unsigned short word;

    /* get exec name */
    execdup = strdup(argv[0]);
    execname= basename(execdup);

    /* get configuration */
    getconf(&k);
    
    /* open device */
    if((k.fd=io_open(&k)) < 0)
    {
        usage(&k, execname, "Unable to access serial device\n\n"
            "If you have specified the correct serial"
            "device then either run this application\n"
            "as root, make the application setuid root"
            "or change the permissions of the device.");
    }

    /* perform command */
    if(mystrcasestr(execname,"k8048")==execname)
        usage(&k, execname, NULL);

    if(mystrcasestr(execname,"ktest")==execname)
    {
        if(argc < 2)
            usage(&k, execname, "Missing arg");
        else if(argc > 2)
            usage(&k, execname, "Too many args");
        
        int seconds= atoi(argv[1]);
        if(seconds > 0)
            io_test1(&k, seconds);
        else
            io_test2(&k, seconds);
        io_close(&k);
        exit(0);
    }

    if(mystrcasestr(execname,"kdebug")==execname)
    {
        if(argc > 1)
            usage(&k, execname, "Too many args");

        io_debug(&k);
        io_close(&k);
        exit(0);
    }
    
    if(mystrcasestr(execname,"k14")==execname)
        k.arch= ARCH14BIT;  /* some PIC12 and most PIC16 */
    else if(mystrcasestr(execname,"k16")==execname)
        k.arch= ARCH16BIT;  /* PIC18 */
    else
        usage(&k, execname, "Unknown command");

    if(argc < 2)
        usage(&k, execname, "Missing arg(s)");

    if(argv[1][0] == 's')   /* Override device probe */
    {
        if(argc < 3)
            usage(&k, execname, "Missing args [s]");
        strncpy(k.devicename, argv[2], STRLEN);
        argc-=2;
        argv+=2;
        if(argc < 2)
            usage(&k, execname, "Missing arg(s)");
    }

    switch(argv[1][0])
    {
        case 'i':   if(argc > 2)
                        usage(&k, execname, "Too many args [i]");
                    pic_dumpdeviceid(&k);
                    break;
        case 'c':   if(argc > 3)
                        usage(&k, execname, "Too many args [c]");
                    if(argc == 2)
                        pic_dumpconfig(&k);
                    else
                        pic_writeconfig(&k, strtoul(argv[2], NULL, 0));
                    break;
        case 'o':   if(argc > 3)
                        usage(&k, execname, "Too many args [o]");
                    if(argc == 2)
                        pic_dumposccal(&k);
                    else
                        pic_writeosccal(&k, strtoul(argv[2], NULL, 0));
                    break;

        case 'f':   
        case 'e':   if(argc > 3)
                        usage(&k, execname, "Too many args [f/e]");
                    if(argc == 2)
                        word= DEFAULT_SIZE;
                    else
                        word= strtoul(argv[2], NULL, 0);
                    if(argv[1][0]=='f')
                        pic_dumpflash(&k, word);
                    else
                        pic_dumpeeprom(&k, word);
                    break;

        case 'p': 
        case 'v':   if(argc < 3)
                        usage(&k, execname, "Missing arg [p/v]");
                    else if(argc > 3)
                        usage(&k, execname, "Too many args [p/v]");

                    filename= getarg(argv[2]);
                    if(filename != NULL)
                    {
                        if(argv[1][0]=='p')
                            pic_program_verify(&k, filename, PROGRAM);
                        else
                            pic_program_verify(&k, filename, VERIFY);
                        free(filename);
                    }
                    else
                        usage(&k, execname, "Missing filename [p/v]");
                    break;

        case 'u':   if(argc > 2)
                        usage(&k, execname, "Too many args [u]");
                    pic_unprotect(&k);
                    break;

        case 'b':   if(argc > 2)
                        usage(&k, execname, "Too many args [b]");
                    pic_blank(&k);
                    break;

        default:    usage(&k, execname, "Unknown action");
                    break;
    }

    io_close(&k);
    exit(0);
}
