commit
229152ff8f
28
Makefile
28
Makefile
@ -7,7 +7,15 @@ CFLAGS=-Wall
|
||||
|
||||
OBJS=\
|
||||
$(OBJDIR)/p1load.o \
|
||||
$(OBJDIR)/ploader.o
|
||||
$(OBJDIR)/port.o \
|
||||
$(OBJDIR)/ploader.o \
|
||||
$(OBJDIR)/packet.o
|
||||
|
||||
EEPROM_OBJS=\
|
||||
$(OBJDIR)/eeprom.o \
|
||||
$(OBJDIR)/port.o \
|
||||
$(OBJDIR)/ploader.o \
|
||||
$(OBJDIR)/packet.o
|
||||
|
||||
OS?=macosx
|
||||
|
||||
@ -29,22 +37,22 @@ ifeq ($(OS),raspberrypi)
|
||||
OS=linux
|
||||
CFLAGS+=-DLINUX -DRASPBERRY_PI
|
||||
EXT=
|
||||
OSINT=osint_linux
|
||||
OSINT=osint_linux.o
|
||||
LIBS=
|
||||
OBJS+=$(OBJDIR)/gpio_sysfs.o
|
||||
OSINT+=gpio_sysfs.o
|
||||
endif
|
||||
|
||||
ifeq ($(OS),msys)
|
||||
CFLAGS+=-DMINGW
|
||||
EXT=.exe
|
||||
OSINT=osint_mingw enumcom
|
||||
OSINT=osint_mingw.o enumcom.o
|
||||
LIBS=-lsetupapi
|
||||
endif
|
||||
|
||||
ifeq ($(OS),macosx)
|
||||
CFLAGS+=-DMACOSX
|
||||
EXT=
|
||||
OSINT=osint_linux
|
||||
OSINT=osint_linux.o
|
||||
LIBS=
|
||||
endif
|
||||
|
||||
@ -58,22 +66,28 @@ BINDIR=bin/$(OS)
|
||||
|
||||
TARGET=$(BINDIR)/p1load$(EXT)
|
||||
|
||||
EEPROM_TARGET=$(BINDIR)/eeprom$(EXT)
|
||||
|
||||
HDRS=\
|
||||
ploader.h
|
||||
|
||||
OBJS+=$(foreach x, $(OSINT), $(OBJDIR)/$(x).o)
|
||||
OBJS+=$(foreach x, $(OSINT), $(OBJDIR)/$(x))
|
||||
EEPROM_OBJS+=$(foreach x, $(OSINT), $(OBJDIR)/$(x))
|
||||
|
||||
CFLAGS+=-Wall
|
||||
LDFLAGS=$(CFLAGS)
|
||||
|
||||
.PHONY: default
|
||||
default: $(TARGET)
|
||||
default: $(TARGET) $(EEPROM_TARGET)
|
||||
|
||||
DIRS=$(OBJDIR) $(BINDIR)
|
||||
|
||||
$(TARGET): $(BINDIR) $(OBJDIR) $(OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
|
||||
|
||||
$(EEPROM_TARGET): $(BINDIR) $(OBJDIR) $(EEPROM_OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(EEPROM_OBJS) $(LIBS)
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c $(HDRS) $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
|
285
eeprom.c
Normal file
285
eeprom.c
Normal file
@ -0,0 +1,285 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include "port.h"
|
||||
#include "ploader.h"
|
||||
#include "packet.h"
|
||||
#include "osint.h"
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
/* port prefix */
|
||||
#if defined(CYGWIN) || defined(WIN32) || defined(MINGW)
|
||||
#define PORT_PREFIX ""
|
||||
#elif defined(LINUX)
|
||||
#ifdef RASPBERRY_PI
|
||||
#define PORT_PREFIX "ttyAMA"
|
||||
#else
|
||||
#define PORT_PREFIX "ttyUSB"
|
||||
#endif
|
||||
#elif defined(MACOSX)
|
||||
#define PORT_PREFIX "cu.usbserial-"
|
||||
#else
|
||||
#define PORT_PREFIX ""
|
||||
#endif
|
||||
|
||||
/* defaults */
|
||||
#define BAUD_RATE 115200
|
||||
|
||||
/* constants */
|
||||
#define HUB_MEMORY_SIZE 32768
|
||||
|
||||
static PL_state state;
|
||||
|
||||
static void Usage(void);
|
||||
static uint8_t *ReadEntireFile(char *name, long *pSize);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char actualPort[PATH_MAX], *var, *val, *port, *p;
|
||||
int baudRate, baudRate2, verbose, i;
|
||||
char *file = NULL;
|
||||
long imageSize;
|
||||
uint8_t *image;
|
||||
|
||||
/* initialize */
|
||||
baudRate = baudRate2 = BAUD_RATE;
|
||||
verbose = FALSE;
|
||||
port = NULL;
|
||||
|
||||
/* initialize the loader port */
|
||||
InitPortState(&state);
|
||||
|
||||
/* process the position-independent arguments */
|
||||
for (i = 1; i < argc; ++i) {
|
||||
|
||||
/* handle switches */
|
||||
if (argv[i][0] == '-') {
|
||||
switch (argv[i][1]) {
|
||||
case 'b':
|
||||
if (argv[i][2])
|
||||
p = &argv[i][2];
|
||||
else if (++i < argc)
|
||||
p = argv[i];
|
||||
else
|
||||
Usage();
|
||||
if (*p != ':')
|
||||
baudRate = baudRate2 = atoi(p);
|
||||
if ((p = strchr(p, ':')) != NULL) {
|
||||
if (*++p != ':' && *p != '\0')
|
||||
baudRate2 = atoi(p);
|
||||
}
|
||||
break;
|
||||
case 'D':
|
||||
if (argv[i][2])
|
||||
p = &argv[i][2];
|
||||
else if (++i < argc)
|
||||
p = argv[i];
|
||||
else
|
||||
Usage();
|
||||
if ((var = strtok(p, "=")) != NULL) {
|
||||
if ((val = strtok(NULL, "")) != NULL) {
|
||||
if (strcmp(var, "reset") == 0)
|
||||
use_reset_method(val);
|
||||
else
|
||||
Usage();
|
||||
}
|
||||
else
|
||||
Usage();
|
||||
}
|
||||
else
|
||||
Usage();
|
||||
break;
|
||||
case 'p':
|
||||
if (argv[i][2])
|
||||
port = &argv[i][2];
|
||||
else if (++i < argc)
|
||||
port = argv[i];
|
||||
else
|
||||
Usage();
|
||||
#if defined(CYGWIN) || defined(WIN32) || defined(LINUX)
|
||||
if (isdigit((int)port[0])) {
|
||||
#if defined(CYGWIN) || defined(WIN32)
|
||||
static char buf[10];
|
||||
sprintf(buf, "COM%d", atoi(port));
|
||||
port = buf;
|
||||
#endif
|
||||
#if defined(LINUX)
|
||||
static char buf[64];
|
||||
sprintf(buf, "/dev/%s%d", PORT_PREFIX, atoi(port));
|
||||
port = buf;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if defined(MACOSX)
|
||||
if (port[0] != '/') {
|
||||
static char buf[64];
|
||||
sprintf(buf, "/dev/%s-%s", PORT_PREFIX, port);
|
||||
port = buf;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case 'P':
|
||||
ShowPorts(&state, PORT_PREFIX);
|
||||
break;
|
||||
case 'r':
|
||||
break;
|
||||
case 'v':
|
||||
verbose = TRUE;
|
||||
break;
|
||||
case '?':
|
||||
/* fall through */
|
||||
default:
|
||||
Usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle the input filename */
|
||||
else {
|
||||
if (file)
|
||||
Usage();
|
||||
file = argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
switch (InitPort(&state, PORT_PREFIX, port, baudRate, verbose, actualPort)) {
|
||||
case CHECK_PORT_OK:
|
||||
printf("Found propeller version %d on %s\n", state.version, actualPort);
|
||||
break;
|
||||
case CHECK_PORT_OPEN_FAILED:
|
||||
printf("error: opening serial port '%s'\n", port);
|
||||
perror("Error is ");
|
||||
return 1;
|
||||
case CHECK_PORT_NO_PROPELLER:
|
||||
if (port)
|
||||
printf("error: no propeller chip on port '%s'\n", port);
|
||||
else
|
||||
printf("error: can't find a port with a propeller chip\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define HELPER "pkt_test.binary"
|
||||
|
||||
/* read the entire file into a buffer */
|
||||
if (!(image = ReadEntireFile(HELPER, &imageSize))) {
|
||||
printf("error: reading '%s'\n", HELPER);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* make sure the file isn't too big for hub memory */
|
||||
if (imageSize > HUB_MEMORY_SIZE) {
|
||||
printf("error: image too big for hub memory\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* load the file from the memory buffer */
|
||||
printf("Loading '%s' (%ld bytes)\n", HELPER, imageSize);
|
||||
switch (PL_LoadSpinBinary(&state, LOAD_TYPE_RUN, image, imageSize)) {
|
||||
case LOAD_STS_OK:
|
||||
printf("OK\n");
|
||||
break;
|
||||
case LOAD_STS_ERROR:
|
||||
printf("Error\n");
|
||||
return 1;
|
||||
case LOAD_STS_TIMEOUT:
|
||||
printf("Timeout\n");
|
||||
return 1;
|
||||
default:
|
||||
printf("Internal error\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
{
|
||||
enum {
|
||||
OP_RESULT,
|
||||
OP_ADD,
|
||||
OP_SUB
|
||||
};
|
||||
int32_t type, sts;
|
||||
int32_t buf[2];
|
||||
while (1) {
|
||||
printf("type and two arguments: ");
|
||||
scanf("%d %d %d", &type, &buf[0], &buf[1]);
|
||||
printf("%d %d %d\n", type, buf[0], buf[1]);
|
||||
printf("sizeof(buf) = %lu\n", sizeof(buf));
|
||||
sts = SendPacket(type, (void *)buf, sizeof(buf));
|
||||
printf("SendPacket returned %d\n", sts);
|
||||
if (sts == 0) {
|
||||
sts = ReceivePacket(&type, (void *)buf, sizeof(buf));
|
||||
printf("ReceivePacket returned %d\n", sts);
|
||||
if (sts >= 0)
|
||||
printf("%d %d\n", type, buf[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Usage - display a usage message and exit */
|
||||
static void Usage(void)
|
||||
{
|
||||
printf("\
|
||||
p1load - a simple loader for the propeller - %s, %s\n\
|
||||
usage: p1load\n\
|
||||
[ -b baud ] baud rate (default is %d)\n\
|
||||
[ -D var=val ] set variable value\n\
|
||||
[ -p port ] serial port (default is to auto-detect the port)\n\
|
||||
[ -P ] list available serial ports\n\
|
||||
[ -r addr:len ] read from eeprom\n\
|
||||
[ -v ] verbose output\n\
|
||||
[ -w addr write to eeprom\n\
|
||||
[ -? ] display a usage message and exit\n\
|
||||
file file to load\n", VERSION, __DATE__, BAUD_RATE);
|
||||
#ifdef RASPBERRY_PI
|
||||
printf("\
|
||||
\n\
|
||||
Supports resetting the Propeller with a GPIO pin with option: -Dreset=gpio,pin,level\n\
|
||||
where \"pin\" is the GPIO number to use and \"level\" is the logic level, 0 or 1. This\n\
|
||||
defaults to GPIO 17 and level 0.\n\
|
||||
");
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* ReadEntireFile - read an entire file into an allocated buffer */
|
||||
static uint8_t *ReadEntireFile(char *name, long *pSize)
|
||||
{
|
||||
uint8_t *buf;
|
||||
long size;
|
||||
FILE *fp;
|
||||
|
||||
/* open the file in binary mode */
|
||||
if (!(fp = fopen(name, "rb")))
|
||||
return NULL;
|
||||
|
||||
/* get the file size */
|
||||
fseek(fp, 0L, SEEK_END);
|
||||
size = ftell(fp);
|
||||
fseek(fp, 0L, SEEK_SET);
|
||||
|
||||
/* allocate a buffer for the file contents */
|
||||
if (!(buf = (uint8_t *)malloc(size))) {
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* read the contents of the file into the buffer */
|
||||
if (fread(buf, 1, size, fp) != size) {
|
||||
free(buf);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* close the file ad return the buffer containing the file contents */
|
||||
*pSize = size;
|
||||
fclose(fp);
|
||||
return buf;
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
*
|
||||
* Copyright (c) 2009 by John Steven Denson
|
||||
* Modified in 2011 by David Michael Betz
|
||||
* GPIO reset code by Michael Rychlik
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
@ -88,7 +89,7 @@ int use_reset_method(char* method)
|
||||
}
|
||||
}
|
||||
|
||||
printf ("Using GPIO pin %d as Propeller reset ", propellerResetGpioPin);
|
||||
//printf ("Using GPIO pin %d as Propeller reset ", propellerResetGpioPin);
|
||||
if (propellerResetGpioLevel)
|
||||
{
|
||||
printf ("(HIGH).\n");
|
||||
|
240
p1load.c
240
p1load.c
@ -4,6 +4,7 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include "port.h"
|
||||
#include "ploader.h"
|
||||
#include "osint.h"
|
||||
|
||||
@ -28,84 +29,23 @@
|
||||
#endif
|
||||
|
||||
/* defaults */
|
||||
#define BAUD_RATE 115200
|
||||
#define BAUD_RATE 115200
|
||||
|
||||
/* CheckPort state structure */
|
||||
typedef struct {
|
||||
int baud;
|
||||
int verbose;
|
||||
char *actualport;
|
||||
} CheckPortInfo;
|
||||
|
||||
/* CheckPort result codes */
|
||||
enum {
|
||||
CHECK_PORT_OK,
|
||||
CHECK_PORT_OPEN_FAILED,
|
||||
CHECK_PORT_NO_PROPELLER
|
||||
};
|
||||
|
||||
static void cb_reset(void *data)
|
||||
{
|
||||
hwreset();
|
||||
}
|
||||
|
||||
static int cb_tx(void *data, uint8_t* buf, int n)
|
||||
{
|
||||
return tx(buf, n);
|
||||
}
|
||||
|
||||
static int cb_rx_timeout(void *data, uint8_t* buf, int n, int timeout)
|
||||
{
|
||||
return rx_timeout(buf, n, timeout);
|
||||
}
|
||||
|
||||
static void cb_progress(void *data, int phase, int current)
|
||||
{
|
||||
switch (phase) {
|
||||
case LOAD_PHASE_HANDSHAKE:
|
||||
break;
|
||||
case LOAD_PHASE_RESPONSE:
|
||||
break;
|
||||
case LOAD_PHASE_VERSION:
|
||||
break;
|
||||
case LOAD_PHASE_HANDSHAKE_DONE:
|
||||
break;
|
||||
case LOAD_PHASE_PROGRAM:
|
||||
printf("\rLoading hub memory ... %d", current);
|
||||
fflush(stdout);
|
||||
break;
|
||||
case LOAD_PHASE_EEPROM_WRITE:
|
||||
printf(" -- OK\nWriting EEPROM ... ");
|
||||
fflush(stdout);
|
||||
break;
|
||||
case LOAD_PHASE_EEPROM_VERIFY:
|
||||
printf("OK\nVerifying EEPROM ... ");
|
||||
fflush(stdout);
|
||||
break;
|
||||
case LOAD_PHASE_DONE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* constants */
|
||||
#define HUB_MEMORY_SIZE 32768
|
||||
|
||||
static PL_state state;
|
||||
static int version;
|
||||
|
||||
static void Usage(void);
|
||||
static uint8_t *ReadEntireFile(char *name, long *pSize);
|
||||
static int ShowPort(const char* port, void* data);
|
||||
static void ShowPorts(char *prefix);
|
||||
static int CheckPort(const char* port, void* data);
|
||||
static int InitPort(char *prefix, char *port, int baud, int verbose, char *actualport);
|
||||
static int OpenPort(const char* port, int baud);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char actualPort[PATH_MAX], *var, *val, *port, *p;
|
||||
int baudRate, baudRate2, verbose, terminalMode, pstMode, i;
|
||||
int loadType = LOAD_TYPE_RUN;
|
||||
int loadTypeOptionSeen = FALSE;
|
||||
char *file = NULL;
|
||||
int loadType = 0;
|
||||
long imageSize;
|
||||
uint8_t *image;
|
||||
|
||||
@ -114,12 +54,8 @@ int main(int argc, char *argv[])
|
||||
verbose = terminalMode = pstMode = FALSE;
|
||||
port = NULL;
|
||||
|
||||
/* initialize the loader */
|
||||
PL_Init(&state);
|
||||
state.reset = cb_reset;
|
||||
state.tx = cb_tx;
|
||||
state.rx_timeout = cb_rx_timeout;
|
||||
state.progress = cb_progress;
|
||||
/* initialize the loader port */
|
||||
InitPortState(&state);
|
||||
|
||||
/* process the position-independent arguments */
|
||||
for (i = 1; i < argc; ++i) {
|
||||
@ -162,6 +98,10 @@ int main(int argc, char *argv[])
|
||||
Usage();
|
||||
break;
|
||||
case 'e':
|
||||
if (!loadTypeOptionSeen) {
|
||||
loadTypeOptionSeen = TRUE;
|
||||
loadType = 0;
|
||||
}
|
||||
loadType |= LOAD_TYPE_EEPROM;
|
||||
break;
|
||||
case 'p':
|
||||
@ -194,9 +134,13 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
break;
|
||||
case 'P':
|
||||
ShowPorts(PORT_PREFIX);
|
||||
ShowPorts(&state, PORT_PREFIX);
|
||||
break;
|
||||
case 'r':
|
||||
if (!loadTypeOptionSeen) {
|
||||
loadTypeOptionSeen = TRUE;
|
||||
loadType = 0;
|
||||
}
|
||||
loadType |= LOAD_TYPE_RUN;
|
||||
break;
|
||||
case 'T':
|
||||
@ -224,39 +168,54 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
switch (InitPort(PORT_PREFIX, port, baudRate, verbose, actualPort)) {
|
||||
case CHECK_PORT_OK:
|
||||
printf("Found propeller version %d on %s\n", version, actualPort);
|
||||
break;
|
||||
case CHECK_PORT_OPEN_FAILED:
|
||||
printf("error: opening serial port '%s'\n", port);
|
||||
perror("Error is ");
|
||||
return 1;
|
||||
case CHECK_PORT_NO_PROPELLER:
|
||||
if (port)
|
||||
printf("error: no propeller chip on port '%s'\n", port);
|
||||
else
|
||||
printf("error: can't find a port with a propeller chip\n");
|
||||
return 1;
|
||||
if (file || terminalMode) {
|
||||
switch (InitPort(&state, PORT_PREFIX, port, baudRate, verbose, actualPort)) {
|
||||
case CHECK_PORT_OK:
|
||||
printf("Found propeller version %d on %s\n", state.version, actualPort);
|
||||
break;
|
||||
case CHECK_PORT_OPEN_FAILED:
|
||||
printf("error: opening serial port '%s'\n", port);
|
||||
perror("Error is ");
|
||||
return 1;
|
||||
case CHECK_PORT_NO_PROPELLER:
|
||||
if (port)
|
||||
printf("error: no propeller chip on port '%s'\n", port);
|
||||
else
|
||||
printf("error: can't find a port with a propeller chip\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* check for a file to load */
|
||||
if (file) {
|
||||
if ((image = ReadEntireFile(file, &imageSize)) != NULL) {
|
||||
printf("Loading '%s' (%ld bytes)\n", file, imageSize);
|
||||
switch (PL_LoadSpinBinary(&state, loadType, image, imageSize)) {
|
||||
case LOAD_STS_OK:
|
||||
printf(" -- OK\n");
|
||||
break;
|
||||
case LOAD_STS_ERROR:
|
||||
printf(" -- Error\n");
|
||||
break;
|
||||
case LOAD_STS_TIMEOUT:
|
||||
printf(" -- Timeout\n");
|
||||
break;
|
||||
default:
|
||||
printf(" -- Internal error\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* read the entire file into a buffer */
|
||||
if (!(image = ReadEntireFile(file, &imageSize))) {
|
||||
printf("error: reading '%s'\n", file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* make sure the file isn't too big for hub memory */
|
||||
if (imageSize > HUB_MEMORY_SIZE) {
|
||||
printf("error: image too big for hub memory\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* load the file from the memory buffer */
|
||||
printf("Loading '%s' (%ld bytes)\n", file, imageSize);
|
||||
switch (PL_LoadSpinBinary(&state, loadType, image, imageSize)) {
|
||||
case LOAD_STS_OK:
|
||||
printf("OK\n");
|
||||
break;
|
||||
case LOAD_STS_ERROR:
|
||||
printf("Error\n");
|
||||
return 1;
|
||||
case LOAD_STS_TIMEOUT:
|
||||
printf("Timeout\n");
|
||||
return 1;
|
||||
default:
|
||||
printf("Internal error\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -276,14 +235,14 @@ int main(int argc, char *argv[])
|
||||
static void Usage(void)
|
||||
{
|
||||
printf("\
|
||||
p1load - a simple loader for the propeller - v%s, %s\n\
|
||||
p1load - a simple loader for the propeller - %s, %s\n\
|
||||
usage: p1load\n\
|
||||
[ -b baud ] baud rate (default is %d)\n\
|
||||
[ -D var=val ] set variable value\n\
|
||||
[ -e ] write a bootable image to EEPROM\n\
|
||||
[ -p port ] serial port (default is to auto-detect the port)\n\
|
||||
[ -P ] list available serial ports\n\
|
||||
[ -r ] run the program after loading\n\
|
||||
[ -r ] run the program after loading (default)\n\
|
||||
[ -t ] enter terminal mode after running the program\n\
|
||||
[ -T ] enter PST-compatible terminal mode\n\
|
||||
[ -v ] verbose output\n\
|
||||
@ -293,7 +252,8 @@ usage: p1load\n\
|
||||
printf("\
|
||||
\n\
|
||||
This version supports resetting the Propeller with a GPIO pin with option: -Dreset=gpio,pin,level\n\
|
||||
where \"pin\" is the GPIO number to use and \"level\" is the logic level, 0 or 1. \n\
|
||||
where \"pin\" is the GPIO number to use and \"level\" is the logic level, 0 or 1. This defaults to\n\
|
||||
GPIO 17 and level 0.\n\
|
||||
");
|
||||
#endif
|
||||
exit(1);
|
||||
@ -333,69 +293,3 @@ static uint8_t *ReadEntireFile(char *name, long *pSize)
|
||||
fclose(fp);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int ShowPort(const char* port, void* data)
|
||||
{
|
||||
printf("%s\n", port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ShowPorts(char *prefix)
|
||||
{
|
||||
serial_find(prefix, ShowPort, NULL);
|
||||
}
|
||||
|
||||
static int CheckPort(const char* port, void* data)
|
||||
{
|
||||
CheckPortInfo* info = (CheckPortInfo*)data;
|
||||
int rc;
|
||||
if (info->verbose)
|
||||
printf("Trying %s \n", port); fflush(stdout);
|
||||
if ((rc = OpenPort(port, info->baud)) != CHECK_PORT_OK)
|
||||
return rc;
|
||||
if (info->actualport) {
|
||||
strncpy(info->actualport, port, PATH_MAX - 1);
|
||||
info->actualport[PATH_MAX - 1] = '\0';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int InitPort(char *prefix, char *port, int baud, int verbose, char *actualport)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (port) {
|
||||
if (actualport) {
|
||||
strncpy(actualport, port, PATH_MAX - 1);
|
||||
actualport[PATH_MAX - 1] = '\0';
|
||||
}
|
||||
result = OpenPort(port, baud);
|
||||
}
|
||||
else {
|
||||
CheckPortInfo info;
|
||||
info.baud = baud;
|
||||
info.verbose = verbose;
|
||||
info.actualport = actualport;
|
||||
if (serial_find(prefix, CheckPort, &info) == 0)
|
||||
result = CHECK_PORT_OK;
|
||||
else
|
||||
result = CHECK_PORT_NO_PROPELLER;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int OpenPort(const char* port, int baud)
|
||||
{
|
||||
/* open the port */
|
||||
if (serial_init(port, baud) == 0)
|
||||
return CHECK_PORT_OPEN_FAILED;
|
||||
|
||||
/* check for a propeller on this port */
|
||||
if (PL_HardwareFound(&state, &version) != LOAD_STS_OK) {
|
||||
serial_done();
|
||||
return CHECK_PORT_NO_PROPELLER;
|
||||
}
|
||||
|
||||
return CHECK_PORT_OK;
|
||||
}
|
||||
|
173
packet.c
Normal file
173
packet.c
Normal file
@ -0,0 +1,173 @@
|
||||
/* packet.c - an elf and spin binary loader for the Parallax Propeller microcontroller
|
||||
Copyright (c) 2011 David Michael Betz
|
||||
See license at the end of the file
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "packet.h"
|
||||
#include "osint.h"
|
||||
|
||||
/* packet format: SOH pkt# type length-lo length-hi hdrchk length*data crc1 crc2 */
|
||||
#define HDR_SOH 0
|
||||
#define HDR_TYPE 1
|
||||
#define HDR_LEN_HI 2
|
||||
#define HDR_LEN_LO 3
|
||||
#define HDR_CHK 4
|
||||
|
||||
/* packet header and crc lengths */
|
||||
#define PKTHDRLEN 5
|
||||
#define PKTCRCLEN 2
|
||||
|
||||
/* maximum length of a frame */
|
||||
#define FRAMELEN (PKTHDRLEN + PKTMAXLEN + PKTCRCLEN)
|
||||
|
||||
/* protocol characters */
|
||||
#define SOH 0x01 /* start of a packet */
|
||||
|
||||
#define updcrc(crc, ch) (crctab[((crc) >> 8) & 0xff] ^ ((crc) << 8) ^ (ch))
|
||||
|
||||
static const uint16_t crctab[256] = {
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
|
||||
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
|
||||
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
|
||||
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
|
||||
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
|
||||
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
|
||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
|
||||
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
|
||||
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
|
||||
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
|
||||
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
|
||||
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
|
||||
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
|
||||
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
|
||||
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
|
||||
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
|
||||
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
|
||||
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
|
||||
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
|
||||
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
||||
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
|
||||
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
|
||||
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
|
||||
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
|
||||
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
|
||||
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
|
||||
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
|
||||
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
|
||||
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
|
||||
};
|
||||
|
||||
int SendPacket(int type, uint8_t *buf, int len)
|
||||
{
|
||||
uint8_t hdr[PKTHDRLEN], crc[PKTCRCLEN], *p;
|
||||
uint16_t crc16 = 0;
|
||||
int cnt;
|
||||
|
||||
/* setup the frame header */
|
||||
hdr[HDR_SOH] = SOH; /* SOH */
|
||||
hdr[HDR_TYPE] = type; /* type type */
|
||||
hdr[HDR_LEN_HI] = (uint8_t)(len >> 8); /* data length - high byte */
|
||||
hdr[HDR_LEN_LO] = (uint8_t)len; /* data length - low byte */
|
||||
hdr[HDR_CHK] = hdr[1] + hdr[2] + hdr[3]; /* header checksum */
|
||||
printf("tx length %d\n", len);
|
||||
printf("%02x %02x %02x\n", hdr[1], hdr[2], hdr[3]);
|
||||
|
||||
/* compute the crc */
|
||||
for (p = buf, cnt = len; --cnt >= 0; ++p)
|
||||
crc16 = updcrc(crc16, *p);
|
||||
crc16 = updcrc(crc16, '\0');
|
||||
crc16 = updcrc(crc16, '\0');
|
||||
|
||||
/* add the crc to the frame */
|
||||
crc[0] = (uint8_t)(crc16 >> 8);
|
||||
crc[1] = (uint8_t)crc16;
|
||||
|
||||
/* send the packet */
|
||||
tx(hdr, PKTHDRLEN);
|
||||
if (len > 0)
|
||||
tx(buf, len);
|
||||
tx(crc, PKTCRCLEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReceivePacket(int *pType, uint8_t *buf, int len)
|
||||
{
|
||||
uint8_t hdr[PKTHDRLEN], crc[PKTCRCLEN];
|
||||
int actual_len, chk;
|
||||
uint16_t crc16 = 0;
|
||||
int retries = 10;
|
||||
printf("Looking for SOH\n");
|
||||
|
||||
/* look for start of packet */
|
||||
do {
|
||||
rx_timeout(&hdr[HDR_SOH], 1, 100);
|
||||
} while (hdr[HDR_SOH] != SOH && --retries >= 0);
|
||||
printf(" retries %d\n", retries);
|
||||
|
||||
/* check for a timeout waiting for the packet */
|
||||
if (retries < 0)
|
||||
return -1;
|
||||
|
||||
/* receive the rest of the header */
|
||||
rx(&hdr[HDR_TYPE], PKTHDRLEN - 1);
|
||||
printf("%02x %02x %02x %02x %02x\n", hdr[0], hdr[1], hdr[2], hdr[3], hdr[4]);
|
||||
|
||||
/* check the header checksum */
|
||||
chk = (hdr[1] + hdr[2] + hdr[3]) & 0xff;
|
||||
if (hdr[HDR_CHK] != chk)
|
||||
return -1;
|
||||
printf("checksum okay\n");
|
||||
|
||||
/* make sure the buffer is big enough for the payload */
|
||||
actual_len = (hdr[HDR_LEN_HI] << 8) | hdr[HDR_LEN_LO];
|
||||
printf("rx length %d\n", actual_len);
|
||||
if (actual_len > len)
|
||||
return -1;
|
||||
printf("length okay\n");
|
||||
|
||||
/* receive the packet payload */
|
||||
rx(buf, actual_len);
|
||||
|
||||
/* compute the crc */
|
||||
for (len = actual_len; --len >= 0; )
|
||||
crc16 = updcrc(crc16, *buf++);
|
||||
|
||||
/* receive the crc */
|
||||
rx(crc, PKTCRCLEN);
|
||||
|
||||
/* check the crc */
|
||||
crc16 = updcrc(crc16, crc[0]);
|
||||
crc16 = updcrc(crc16, crc[1]);
|
||||
if (crc16 != 0)
|
||||
return -1;
|
||||
printf("crc okay\n");
|
||||
|
||||
/* return packet type and the length of the payload */
|
||||
*pType = hdr[HDR_TYPE];
|
||||
return actual_len;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
32
packet.h
Normal file
32
packet.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* packet.h - an elf and spin binary loader for the Parallax Propeller microcontroller
|
||||
|
||||
Copyright (c) 2011 David Michael Betz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __PACKET_H__
|
||||
#define __PACKET_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define PKTMAXLEN 1024
|
||||
|
||||
int SendPacket(int type, uint8_t *buf, int len);
|
||||
int ReceivePacket(int *pType, uint8_t *buf, int len);
|
||||
|
||||
#endif
|
409
packet_driver.spin
Normal file
409
packet_driver.spin
Normal file
@ -0,0 +1,409 @@
|
||||
''*********************************************
|
||||
''* Bidirectional Packet Driver *
|
||||
''* (C) 2011-2015 David Betz *
|
||||
''* Based on: *
|
||||
''* Full-Duplex Serial Driver v1.1 Extended *
|
||||
''* (C) 2006 Parallax, Inc. *
|
||||
''*********************************************
|
||||
|
||||
CON
|
||||
|
||||
' command codes
|
||||
#0
|
||||
CMD_IDLE ' must be zero
|
||||
CMD_RXPACKET
|
||||
CMD_TXPACKET
|
||||
|
||||
' status codes
|
||||
#0
|
||||
STATUS_OK
|
||||
STATUS_ERROR
|
||||
|
||||
' init offsets
|
||||
#0
|
||||
INIT_MBOX ' zeroed by driver after init is done
|
||||
INIT_RXPIN
|
||||
INIT_TXPIN
|
||||
INIT_BAUDRATE
|
||||
_INIT_SIZE
|
||||
|
||||
' mailbox offsets
|
||||
#0
|
||||
MBOX_CMD
|
||||
MBOX_TYPE ' or pointer to type on rx
|
||||
MBOX_BUFFER
|
||||
MBOX_LENGTH ' or pointer to length on rx
|
||||
MBOX_STATUS
|
||||
MBOX_COG ' not really part of the mailbox
|
||||
_MBOX_SIZE
|
||||
|
||||
' character codes
|
||||
SOH = $01 ' start of a packet
|
||||
|
||||
VAR
|
||||
long mbox[_MBOX_SIZE]
|
||||
|
||||
PUB start(rxpin, txpin, baudrate) | init[_INIT_SIZE], cogn
|
||||
|
||||
'' Start packet driver - starts a cog
|
||||
'' returns zero on success and non-zero if no cog is available
|
||||
''
|
||||
|
||||
' stop the driver if it's already running
|
||||
stop
|
||||
|
||||
' start the driver cog
|
||||
init[INIT_MBOX] := @mbox
|
||||
init[INIT_RXPIN] := rxpin
|
||||
init[INIT_TXPIN] := txpin
|
||||
init[INIT_BAUDRATE] := clkfreq / baudrate
|
||||
cogn := mbox[MBOX_COG] := cognew(@entry, @init) + 1
|
||||
|
||||
' if the cog started okay wait for it to finish initializing
|
||||
if cogn
|
||||
repeat while init[INIT_MBOX] <> 0
|
||||
|
||||
return !cogn
|
||||
|
||||
PUB stop
|
||||
if mbox[MBOX_COG]
|
||||
cogstop(mbox[MBOX_COG]~ - 1)
|
||||
|
||||
PUB rx(ptype, buffer, plength)
|
||||
|
||||
mbox[MBOX_BUFFER] := buffer
|
||||
mbox[MBOX_LENGTH] := long[plength]
|
||||
|
||||
mbox[MBOX_CMD] := CMD_RXPACKET
|
||||
repeat while mbox[MBOX_CMD] <> CMD_IDLE
|
||||
|
||||
long[ptype] := mbox[MBOX_TYPE]
|
||||
long[plength] := mbox[MBOX_LENGTH]
|
||||
|
||||
return mbox[MBOX_STATUS]
|
||||
|
||||
PUB tx(type, buffer, length)
|
||||
|
||||
mbox[MBOX_TYPE] := type
|
||||
mbox[MBOX_BUFFER] := buffer
|
||||
mbox[MBOX_LENGTH] := length
|
||||
|
||||
mbox[MBOX_CMD] := CMD_TXPACKET
|
||||
repeat while mbox[MBOX_CMD] <> CMD_IDLE
|
||||
|
||||
return mbox[MBOX_STATUS]
|
||||
|
||||
DAT
|
||||
|
||||
'***********************************
|
||||
'* Assembly language serial driver *
|
||||
'***********************************
|
||||
|
||||
org
|
||||
'
|
||||
'
|
||||
' Entry
|
||||
'
|
||||
entry mov t1, par 'get init structure address
|
||||
|
||||
rdlong t2, t1 'get the mailbox address
|
||||
mov cmd_ptr, t2 'offset 0 - cmd
|
||||
add t2, #4
|
||||
mov pkt_type_ptr, t2 'offset 1 - type
|
||||
add t2, #4
|
||||
mov pkt_buffer_ptr, t2 'offset 2 - buffer
|
||||
add t2, #4
|
||||
mov pkt_length_ptr, t2 'offset 3 - length
|
||||
add t2, #4
|
||||
mov pkt_status_ptr, t2 'offset 4 - status
|
||||
|
||||
add t1, #4 'get rx_pin
|
||||
rdlong t2, t1
|
||||
mov rxmask, #1
|
||||
shl rxmask, t2
|
||||
|
||||
add t1, #4 'get tx_pin
|
||||
rdlong t2, t1
|
||||
mov txmask, #1
|
||||
shl txmask, t2
|
||||
|
||||
add t1, #4 'get bit_ticks
|
||||
rdlong bitticks, t1
|
||||
|
||||
or outa, txmask 'initialize the tx pin
|
||||
or dira, txmask
|
||||
andn dira, rxmask 'initialize the rx pin
|
||||
|
||||
rdlong t1, #0
|
||||
add t1, cnt
|
||||
waitcnt t1, #0
|
||||
|
||||
mov t1, #CMD_IDLE 'no command in progress
|
||||
wrlong t1, cmd_ptr
|
||||
|
||||
mov t1, #0 'signal end of initialization
|
||||
wrlong t1, par
|
||||
|
||||
next_cmd mov t1, #CMD_IDLE 'no command in progress
|
||||
wrlong t1, cmd_ptr
|
||||
:wait rdlong t1, cmd_ptr wz 'wait for a command
|
||||
if_z jmp #:wait
|
||||
add t1, #dispatch
|
||||
jmp t1
|
||||
|
||||
dispatch jmp #next_cmd 'should never happen
|
||||
jmp #do_rxpacket
|
||||
jmp #do_txpacket
|
||||
|
||||
' receive a packet
|
||||
do_rxpacket rdlong rcv_ptr, pkt_buffer_ptr
|
||||
rdlong t1, pkt_length_ptr
|
||||
rdlong rcv_max, t1
|
||||
call #rxpacket
|
||||
if_c wrlong ok_status, pkt_status_ptr
|
||||
if_c wrlong rcv_type, pkt_type_ptr
|
||||
if_c wrlong rcv_length, pkt_length_ptr
|
||||
if_nc wrlong error_status, pkt_status_ptr
|
||||
jmp #next_cmd
|
||||
|
||||
' send a packet
|
||||
do_txpacket rdlong xmt_type, pkt_type_ptr
|
||||
rdlong xmt_ptr, pkt_buffer_ptr
|
||||
rdlong xmt_length, pkt_length_ptr
|
||||
call #txpacket
|
||||
if_c wrlong ok_status, pkt_status_ptr
|
||||
if_nc wrlong error_status, pkt_status_ptr
|
||||
jmp #next_cmd
|
||||
|
||||
ok_status long STATUS_OK
|
||||
error_status long STATUS_ERROR
|
||||
|
||||
' receive a packet
|
||||
' input:
|
||||
' rcv_ptr points to the receive buffer
|
||||
' rcv_max is the maximum number of bytes to receive
|
||||
' output:
|
||||
' rcv_type is the packet type
|
||||
' rcv_length is the length of the packet received
|
||||
' C is set on return if the packet was received successfully
|
||||
' C is clear on return if there was an error
|
||||
rxpacket call #rxbyte
|
||||
cmp rxdata, #SOH wz
|
||||
if_nz jmp #rxpacket
|
||||
call #rxbyte ' receive packet type
|
||||
mov rcv_type, rxdata
|
||||
mov rcv_chk, rxdata
|
||||
call #rxbyte ' receive hi byte of packet length
|
||||
mov rcv_length, rxdata
|
||||
shl rcv_length, #8
|
||||
add rcv_chk, rxdata
|
||||
call #rxbyte ' receive lo byte of packet length
|
||||
or rcv_length, rxdata
|
||||
add rcv_chk, rxdata
|
||||
call #rxbyte ' receive header checksum
|
||||
and rcv_chk, #$ff
|
||||
cmp rxdata, rcv_chk wz
|
||||
if_nz jmp #rxerror
|
||||
cmp rcv_length, rcv_max wz, wc
|
||||
if_a jmp #rxerror
|
||||
mov crc, #0
|
||||
mov rcv_cnt, rcv_length wz
|
||||
if_z jmp #:crc
|
||||
:next call #rxbyte ' receive the next data byte
|
||||
mov crcdata, rxdata
|
||||
call #updcrc ' update the crc
|
||||
wrbyte rxdata, rcv_ptr
|
||||
add rcv_ptr, #1
|
||||
djnz rcv_cnt, #:next
|
||||
:crc call #rxbyte
|
||||
mov crcdata, rxdata
|
||||
call #updcrc ' update the crc
|
||||
call #rxbyte
|
||||
mov crcdata, rxdata
|
||||
call #updcrc ' update the crc
|
||||
cmp crc, #0 wz ' check the crc
|
||||
if_nz jmp #rxerror
|
||||
test $, #1 wc ' set c to indicate success
|
||||
rxpacket_ret ret
|
||||
|
||||
rxerror test $, #0 wc ' clear c to indicate failure
|
||||
jmp rxpacket_ret
|
||||
|
||||
' transmit a packet
|
||||
' input:
|
||||
' xmt_type is the packet type
|
||||
' xmt_ptr points to the packet data
|
||||
' xmt_length is the number of bytes of data
|
||||
' output:
|
||||
' C is set on return if the packet was received successfully
|
||||
' C is clear on return if there was an error
|
||||
txpacket mov txdata, #SOH
|
||||
call #txbyte
|
||||
mov txdata, xmt_type
|
||||
mov xmt_chk, txdata
|
||||
call #txbyte
|
||||
mov txdata, xmt_length
|
||||
shr txdata, #8
|
||||
add xmt_chk, txdata
|
||||
call #txbyte
|
||||
mov txdata, xmt_length
|
||||
and txdata, #$ff
|
||||
add xmt_chk, txdata
|
||||
call #txbyte
|
||||
mov txdata, xmt_chk
|
||||
and txdata, #$ff
|
||||
call #txbyte
|
||||
mov crc, #0
|
||||
mov xmt_cnt, xmt_length wz
|
||||
if_z jmp #:crc
|
||||
:next rdbyte txdata, xmt_ptr
|
||||
add xmt_ptr, #1
|
||||
mov crcdata, txdata
|
||||
call #updcrc
|
||||
call #txbyte
|
||||
djnz xmt_cnt, #:next
|
||||
:crc mov crcdata, #0
|
||||
call #updcrc
|
||||
call #updcrc
|
||||
mov txdata, crc
|
||||
shr txdata, #8
|
||||
call #txbyte
|
||||
mov txdata, crc
|
||||
and txdata, #$ff
|
||||
call #txbyte
|
||||
test $, #1 wc ' set c to indicate success
|
||||
txpacket_ret ret
|
||||
|
||||
' receive a byte
|
||||
' output:
|
||||
' rxdata is the byte received
|
||||
rxbyte waitpne rxmask, rxmask ' wait for a start bit
|
||||
mov rxcnt, bitticks ' wait until half way through the bit time
|
||||
shr rxcnt, #1
|
||||
add rxcnt, bitticks
|
||||
add rxcnt, cnt
|
||||
mov rxbits, #9 ' receive 8 data bits and the stop bit
|
||||
:next waitcnt rxcnt, bitticks ' wait until the center of the next bit
|
||||
test rxmask, ina wc ' sample the rx pin
|
||||
rcr rxdata, #1 ' add to the byte being received
|
||||
djnz rxbits, #:next
|
||||
shr rxdata, #32-9 ' shift the received data to the low bits
|
||||
and rxdata, #$ff ' mask off the stop bit
|
||||
waitpeq rxmask, rxmask ' wait for the end of the stop bit
|
||||
rxbyte_ret ret
|
||||
|
||||
' transmit a byte
|
||||
' input:
|
||||
' txdata is the byte to send (destroyed on return)
|
||||
txbyte or txdata, #$100 ' or in a stop bit
|
||||
shl txdata, #1 ' shift in a start bit
|
||||
mov txcnt, cnt
|
||||
add txcnt, bitticks
|
||||
mov txbits, #10
|
||||
:next shr txdata, #1 wc
|
||||
muxc outa, txmask
|
||||
waitcnt txcnt, bitticks
|
||||
djnz txbits, #:next
|
||||
waitcnt txcnt, bitticks
|
||||
txbyte_ret ret
|
||||
|
||||
bitticks long 0
|
||||
rxmask long 0
|
||||
rxdata long 0
|
||||
rxbits long 0
|
||||
rxcnt long 0
|
||||
txmask long 0
|
||||
txdata long 0
|
||||
txbits long 0
|
||||
txcnt long 0
|
||||
|
||||
' update the crc
|
||||
' input:
|
||||
' crc the current crc
|
||||
' crcdata the data to add to the crc
|
||||
' output:
|
||||
' crc the updated crc
|
||||
updcrc mov t1, crc
|
||||
test t1, #$100 wz
|
||||
shr t1, #9
|
||||
add t1, #crctab
|
||||
movs :load, t1
|
||||
shl crc, #8
|
||||
:load mov t1, 0-0
|
||||
if_nz shr t1, #16
|
||||
xor crc, t1
|
||||
xor crc, crcdata
|
||||
and crc, word_mask
|
||||
updcrc_ret ret
|
||||
|
||||
crc long 0
|
||||
crcdata long 0
|
||||
|
||||
'
|
||||
'
|
||||
' Initialized data
|
||||
'
|
||||
'
|
||||
zero long 0
|
||||
word_mask long $ffff
|
||||
|
||||
crctab
|
||||
word $0000, $1021, $2042, $3063, $4084, $50a5, $60c6, $70e7
|
||||
word $8108, $9129, $a14a, $b16b, $c18c, $d1ad, $e1ce, $f1ef
|
||||
word $1231, $0210, $3273, $2252, $52b5, $4294, $72f7, $62d6
|
||||
word $9339, $8318, $b37b, $a35a, $d3bd, $c39c, $f3ff, $e3de
|
||||
word $2462, $3443, $0420, $1401, $64e6, $74c7, $44a4, $5485
|
||||
word $a56a, $b54b, $8528, $9509, $e5ee, $f5cf, $c5ac, $d58d
|
||||
word $3653, $2672, $1611, $0630, $76d7, $66f6, $5695, $46b4
|
||||
word $b75b, $a77a, $9719, $8738, $f7df, $e7fe, $d79d, $c7bc
|
||||
word $48c4, $58e5, $6886, $78a7, $0840, $1861, $2802, $3823
|
||||
word $c9cc, $d9ed, $e98e, $f9af, $8948, $9969, $a90a, $b92b
|
||||
word $5af5, $4ad4, $7ab7, $6a96, $1a71, $0a50, $3a33, $2a12
|
||||
word $dbfd, $cbdc, $fbbf, $eb9e, $9b79, $8b58, $bb3b, $ab1a
|
||||
word $6ca6, $7c87, $4ce4, $5cc5, $2c22, $3c03, $0c60, $1c41
|
||||
word $edae, $fd8f, $cdec, $ddcd, $ad2a, $bd0b, $8d68, $9d49
|
||||
word $7e97, $6eb6, $5ed5, $4ef4, $3e13, $2e32, $1e51, $0e70
|
||||
word $ff9f, $efbe, $dfdd, $cffc, $bf1b, $af3a, $9f59, $8f78
|
||||
word $9188, $81a9, $b1ca, $a1eb, $d10c, $c12d, $f14e, $e16f
|
||||
word $1080, $00a1, $30c2, $20e3, $5004, $4025, $7046, $6067
|
||||
word $83b9, $9398, $a3fb, $b3da, $c33d, $d31c, $e37f, $f35e
|
||||
word $02b1, $1290, $22f3, $32d2, $4235, $5214, $6277, $7256
|
||||
word $b5ea, $a5cb, $95a8, $8589, $f56e, $e54f, $d52c, $c50d
|
||||
word $34e2, $24c3, $14a0, $0481, $7466, $6447, $5424, $4405
|
||||
word $a7db, $b7fa, $8799, $97b8, $e75f, $f77e, $c71d, $d73c
|
||||
word $26d3, $36f2, $0691, $16b0, $6657, $7676, $4615, $5634
|
||||
word $d94c, $c96d, $f90e, $e92f, $99c8, $89e9, $b98a, $a9ab
|
||||
word $5844, $4865, $7806, $6827, $18c0, $08e1, $3882, $28a3
|
||||
word $cb7d, $db5c, $eb3f, $fb1e, $8bf9, $9bd8, $abbb, $bb9a
|
||||
word $4a75, $5a54, $6a37, $7a16, $0af1, $1ad0, $2ab3, $3a92
|
||||
word $fd2e, $ed0f, $dd6c, $cd4d, $bdaa, $ad8b, $9de8, $8dc9
|
||||
word $7c26, $6c07, $5c64, $4c45, $3ca2, $2c83, $1ce0, $0cc1
|
||||
word $ef1f, $ff3e, $cf5d, $df7c, $af9b, $bfba, $8fd9, $9ff8
|
||||
word $6e17, $7e36, $4e55, $5e74, $2e93, $3eb2, $0ed1, $1ef0
|
||||
|
||||
'
|
||||
' Uninitialized data
|
||||
'
|
||||
t1 res 1
|
||||
t2 res 1
|
||||
|
||||
xmt_type res 1
|
||||
xmt_length res 1
|
||||
xmt_chk res 1
|
||||
xmt_ptr res 1
|
||||
xmt_cnt res 1
|
||||
|
||||
rcv_type res 1
|
||||
rcv_length res 1 'packet length
|
||||
rcv_chk res 1 'header checksum
|
||||
rcv_max res 1 'maximum packet data length
|
||||
rcv_ptr res 1 'data buffer pointer
|
||||
rcv_cnt res 1 'data buffer count
|
||||
|
||||
cmd_ptr res 1
|
||||
pkt_type_ptr res 1
|
||||
pkt_buffer_ptr res 1
|
||||
pkt_length_ptr res 1
|
||||
pkt_status_ptr res 1
|
||||
|
||||
fit 496
|
29
ploader.c
29
ploader.c
@ -41,26 +41,20 @@ int PL_LoadSpinBinary(PL_state *state, int loadType, uint8_t *image, int size)
|
||||
{
|
||||
int i, sts;
|
||||
|
||||
/* report the start of program loading */
|
||||
if (state->progress)
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_PROGRAM);
|
||||
|
||||
TLong(state, loadType);
|
||||
TLong(state, size / sizeof(uint32_t));
|
||||
|
||||
/* download the spin binary */
|
||||
for (i = 0; i < size; i += 4) {
|
||||
|
||||
/* report load progress */
|
||||
if (state->progress && (i % 1024) == 0)
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_PROGRAM, i);
|
||||
|
||||
/* transmit the next long */
|
||||
uint32_t data = image[i] | (image[i + 1] << 8) | (image[i + 2] << 16) | (image[i + 3] << 24);
|
||||
TLong(state, data);
|
||||
}
|
||||
TComm(state);
|
||||
|
||||
/* report load of program code finished */
|
||||
if (state->progress)
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_PROGRAM, size);
|
||||
|
||||
/* wait for an ACK indicating a successful load */
|
||||
if ((sts = WaitForAck(state, CHECKSUM_RETRIES)) < 0)
|
||||
return LOAD_STS_TIMEOUT;
|
||||
@ -72,7 +66,7 @@ int PL_LoadSpinBinary(PL_state *state, int loadType, uint8_t *image, int size)
|
||||
|
||||
/* report the start of the eeprom writing phase */
|
||||
if (state->progress)
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_EEPROM_WRITE, 0);
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_EEPROM_WRITE);
|
||||
|
||||
/* wait for an ACK indicating a successful EEPROM programming */
|
||||
if ((sts = WaitForAck(state, EEPROM_PROGRAMMING_RETRIES)) < 0)
|
||||
@ -82,7 +76,7 @@ int PL_LoadSpinBinary(PL_state *state, int loadType, uint8_t *image, int size)
|
||||
|
||||
/* report the start of the eeprom verification phase */
|
||||
if (state->progress)
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_EEPROM_VERIFY, 0);
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_EEPROM_VERIFY);
|
||||
|
||||
/* wait for an ACK indicating a successful EEPROM verification */
|
||||
if ((sts = WaitForAck(state, EEPROM_VERIFICATION_RETRIES)) < 0)
|
||||
@ -93,7 +87,7 @@ int PL_LoadSpinBinary(PL_state *state, int loadType, uint8_t *image, int size)
|
||||
|
||||
/* report load completion */
|
||||
if (state->progress)
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_DONE, 0);
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_DONE);
|
||||
|
||||
/* load completed successfully */
|
||||
return LOAD_STS_OK;
|
||||
@ -103,6 +97,7 @@ static int WaitForAck(PL_state *state, int retries)
|
||||
{
|
||||
uint8_t buf[1];
|
||||
while (--retries >= 0) {
|
||||
(*state->msleep)(state->serialData, 20);
|
||||
TByte(state, 0xf9);
|
||||
TComm(state);
|
||||
if ((*state->rx_timeout)(state->serialData, buf, 1, ACK_TIMEOUT) > 0)
|
||||
@ -122,7 +117,7 @@ int PL_HardwareFound(PL_state *state, int *pVersion)
|
||||
|
||||
/* report the start of the handshake phase */
|
||||
if (state->progress)
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_HANDSHAKE, 0);
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_HANDSHAKE);
|
||||
|
||||
/* reset the propeller (includes post-reset delay of 100ms) */
|
||||
(*state->reset)(state->serialData);
|
||||
@ -144,7 +139,7 @@ int PL_HardwareFound(PL_state *state, int *pVersion)
|
||||
|
||||
/* report the start of the handshake response phase */
|
||||
if (state->progress)
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_RESPONSE, 0);
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_RESPONSE);
|
||||
|
||||
/* receive the connection response */
|
||||
for (i = 0; i < 250; ++i) {
|
||||
@ -157,7 +152,7 @@ int PL_HardwareFound(PL_state *state, int *pVersion)
|
||||
|
||||
/* report the start of the version phase */
|
||||
if (state->progress)
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_VERSION, 0);
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_VERSION);
|
||||
|
||||
/* receive the chip version */
|
||||
for (version = i = 0; i < 8; ++i) {
|
||||
@ -170,7 +165,7 @@ int PL_HardwareFound(PL_state *state, int *pVersion)
|
||||
|
||||
/* report handshake completion */
|
||||
if (state->progress)
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_HANDSHAKE_DONE, 0);
|
||||
(*state->progress)(state->progressData, LOAD_PHASE_HANDSHAKE_DONE);
|
||||
|
||||
/* return successfully */
|
||||
return LOAD_STS_OK;
|
||||
|
22
ploader.h
22
ploader.h
@ -26,6 +26,18 @@ extern "C"
|
||||
#define LOAD_TYPE_EEPROM 2
|
||||
#define LOAD_TYPE_EEPROM_RUN 3
|
||||
|
||||
/* constants taken from the Propeller Tool loader */
|
||||
#define BaudRate 115200 // Likely Baud rate of Propeller communication
|
||||
#define ResetPulsePeriod 25 // Reset pulse size (in ms)
|
||||
#define MinResetDelay 60 // Minimum post-reset delay
|
||||
#define MaxResetDelay 500 // Maximum post-reset delay
|
||||
|
||||
/* Transmit buffer size is 32K / 4 = 8K longs * 11 bytes per long plus two longs for the command and size */
|
||||
#define TxBufSize ((((1024 * 32) / 4) + 2) * 11)
|
||||
|
||||
/* Receive buffer is large enough to receive max possible bytes during reset + 250 bytes for handshake response */
|
||||
#define RxBufSize (((BaudRate / 10 * (ResetPulsePeriod + MaxResetDelay) / 1000) & 0xFFFFFFFE) + 258)
|
||||
|
||||
/* loader state structure - filled in by the loader functions */
|
||||
typedef struct {
|
||||
|
||||
@ -33,16 +45,20 @@ typedef struct {
|
||||
void (*reset)(void *data);
|
||||
int (*tx)(void *data, uint8_t* buf, int n);
|
||||
int (*rx_timeout)(void *data, uint8_t* buf, int n, int timeout);
|
||||
void (*msleep)(void *data, int msecs);
|
||||
void *serialData;
|
||||
|
||||
/* propeller version */
|
||||
int version;
|
||||
|
||||
/* load progress interface */
|
||||
void (*progress)(void *data, int phase, int current);
|
||||
void (*progress)(void *data, int phase);
|
||||
void *progressData;
|
||||
|
||||
/* internal variables */
|
||||
uint8_t txbuf[1024];
|
||||
uint8_t txbuf[TxBufSize];
|
||||
int txcnt;
|
||||
uint8_t rxbuf[1024];
|
||||
uint8_t rxbuf[RxBufSize];
|
||||
int rxnext;
|
||||
int rxcnt;
|
||||
uint8_t lfsr;
|
||||
|
160
port.c
Normal file
160
port.c
Normal file
@ -0,0 +1,160 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include "port.h"
|
||||
#include "ploader.h"
|
||||
#include "osint.h"
|
||||
|
||||
/* CheckPort state structure */
|
||||
typedef struct {
|
||||
PL_state *state;
|
||||
int baud;
|
||||
int verbose;
|
||||
char *actualport;
|
||||
} CheckPortInfo;
|
||||
|
||||
void ShowPorts(PL_state *state, char *prefix);
|
||||
int InitPort(PL_state *state, char *prefix, char *port, int baud, int verbose, char *actualport);
|
||||
|
||||
static int ShowPort(const char *port, void *data);
|
||||
static int CheckPort(const char *port, void *data);
|
||||
static int OpenPort(PL_state *state, const char *port, int baud);
|
||||
static void cb_reset(void *data);
|
||||
static int cb_tx(void *data, uint8_t* buf, int n);
|
||||
static int cb_rx_timeout(void *data, uint8_t* buf, int n, int timeout);
|
||||
static void cb_msleep(void *data, int msecs);
|
||||
static void cb_progress(void *data, int phase);
|
||||
|
||||
void InitPortState(PL_state *state)
|
||||
{
|
||||
PL_Init(state);
|
||||
state->reset = cb_reset;
|
||||
state->tx = cb_tx;
|
||||
state->rx_timeout = cb_rx_timeout;
|
||||
state->progress = cb_progress;
|
||||
state->msleep = cb_msleep;
|
||||
#ifdef RASPBERRY_PI
|
||||
{
|
||||
char cmd[20] = "gpio,17,0";
|
||||
// use_reset_method uses strtok to parse the string so it can't be a constant
|
||||
use_reset_method(cmd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ShowPorts(PL_state *state, char *prefix)
|
||||
{
|
||||
serial_find(prefix, ShowPort, state);
|
||||
}
|
||||
|
||||
int InitPort(PL_state *state, char *prefix, char *port, int baud, int verbose, char *actualport)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (port) {
|
||||
if (actualport) {
|
||||
strncpy(actualport, port, PATH_MAX - 1);
|
||||
actualport[PATH_MAX - 1] = '\0';
|
||||
}
|
||||
result = OpenPort(state, port, baud);
|
||||
}
|
||||
else {
|
||||
CheckPortInfo info;
|
||||
info.state = state;
|
||||
info.baud = baud;
|
||||
info.verbose = verbose;
|
||||
info.actualport = actualport;
|
||||
if (serial_find(prefix, CheckPort, &info) == 0)
|
||||
result = CHECK_PORT_OK;
|
||||
else
|
||||
result = CHECK_PORT_NO_PROPELLER;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ShowPort(const char *port, void *data)
|
||||
{
|
||||
printf("%s\n", port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int CheckPort(const char *port, void *data)
|
||||
{
|
||||
CheckPortInfo *info = (CheckPortInfo*)data;
|
||||
int rc;
|
||||
if (info->verbose)
|
||||
printf("Trying %s \n", port); fflush(stdout);
|
||||
if ((rc = OpenPort(info->state, port, info->baud)) != CHECK_PORT_OK)
|
||||
return rc;
|
||||
if (info->actualport) {
|
||||
strncpy(info->actualport, port, PATH_MAX - 1);
|
||||
info->actualport[PATH_MAX - 1] = '\0';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int OpenPort(PL_state *state, const char *port, int baud)
|
||||
{
|
||||
/* open the port */
|
||||
if (serial_init(port, baud) == 0)
|
||||
return CHECK_PORT_OPEN_FAILED;
|
||||
|
||||
/* check for a propeller on this port */
|
||||
if (PL_HardwareFound(state, &state->version) != LOAD_STS_OK) {
|
||||
serial_done();
|
||||
return CHECK_PORT_NO_PROPELLER;
|
||||
}
|
||||
|
||||
return CHECK_PORT_OK;
|
||||
}
|
||||
|
||||
static void cb_reset(void *data)
|
||||
{
|
||||
hwreset();
|
||||
}
|
||||
|
||||
static int cb_tx(void *data, uint8_t* buf, int n)
|
||||
{
|
||||
return tx(buf, n);
|
||||
}
|
||||
|
||||
static int cb_rx_timeout(void *data, uint8_t* buf, int n, int timeout)
|
||||
{
|
||||
return rx_timeout(buf, n, timeout);
|
||||
}
|
||||
|
||||
static void cb_msleep(void *data, int msecs)
|
||||
{
|
||||
msleep(msecs);
|
||||
}
|
||||
|
||||
static void cb_progress(void *data, int phase)
|
||||
{
|
||||
switch (phase) {
|
||||
case LOAD_PHASE_HANDSHAKE:
|
||||
break;
|
||||
case LOAD_PHASE_RESPONSE:
|
||||
break;
|
||||
case LOAD_PHASE_VERSION:
|
||||
break;
|
||||
case LOAD_PHASE_HANDSHAKE_DONE:
|
||||
break;
|
||||
case LOAD_PHASE_PROGRAM:
|
||||
printf("Loading hub memory ... ");
|
||||
fflush(stdout);
|
||||
break;
|
||||
case LOAD_PHASE_EEPROM_WRITE:
|
||||
printf("OK\nWriting EEPROM ... ");
|
||||
fflush(stdout);
|
||||
break;
|
||||
case LOAD_PHASE_EEPROM_VERIFY:
|
||||
printf("OK\nVerifying EEPROM ... ");
|
||||
fflush(stdout);
|
||||
break;
|
||||
case LOAD_PHASE_DONE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
18
port.h
Normal file
18
port.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef __PORT_H__
|
||||
#define __PORT_H__
|
||||
|
||||
#include "ploader.h"
|
||||
|
||||
/* CheckPort result codes */
|
||||
enum {
|
||||
CHECK_PORT_OK,
|
||||
CHECK_PORT_OPEN_FAILED,
|
||||
CHECK_PORT_NO_PROPELLER
|
||||
};
|
||||
|
||||
/* prototypes */
|
||||
void InitPortState(PL_state *state);
|
||||
void ShowPorts(PL_state *state, char *prefix);
|
||||
int InitPort(PL_state *state, char *prefix, char *port, int baud, int verbose, char *actualport);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user