Radioberry-2.x/SBC/rpi-4/device_driver/firmware/radioberry.c

607 wiersze
18 KiB
C

/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
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 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.
For more information, please refer to <http://unlicense.org/>
* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*
* This programs is the c part of the firmware for the RadioBerry.
*
* The belonging gateware can be found at: https://github.com/softerhardware/Hermes-Lite2
*
* **** Device Driver version. ****
*
* By using this program you have the possibility to connect to SDR programs like:
* - pihpsdr (including TCP)
* - Quisk
* - PSDR
* - SPARK
* - SDRConsole
*
* Using the 'HPSDR UDP protocol'; also called protocol-1
*
*
* http://www.pa3gsb.nl
*
* Johan PA3GSB
*
*/
#define NR 0x04
#define HERMESLITE 0x06
#include "radioberry_ioctl.h"
#include "radioberry.h"
#include "filters.h"
#include "register.h"
#include "bias.h"
#include "measure.h"
#include <pthread.h>
#include <sys/socket.h>
#include <unistd.h>
int main(int argc, char **argv)
{
printIntroScreen();
if (initRadioberry() < 0){
fprintf(stderr,"Radioberry; could not be initialized. \n");
exit(-1);
}
signal(SIGINT, handle_sigint);
runRadioberry();
closeRadioberry();
}
static void start_rb_control_thread(void);
static void start_rb_measure_thread(void);
static void start_rb_register_thread(void);
static void start_timer_thread(void);
static int initRadioberry(void) {
sem_init(&mutex, 0, 1);
sem_init(&tx_empty, 0, TX_MAX);
sem_init(&tx_full, 0, 0);
sem_init(&spi_msg, 0, 0);
sem_init(&i2c_meas, 0, 0);
gettimeofday(&t20, 0);
memset(commands,0,256); // initialise the commands.
if((fd_rb = open("/dev/radioberry", O_RDWR)) < 0){
perror("Failed to open /dev/radioberry");
exit(-1);
}
int init = 0;
do {
rb_info.command_data = 0x0;
//required to retrieve gateware information.
if (ioctl(fd_rb, RADIOBERRY_IOC_COMMAND, &rb_info) == -1) {
fprintf(stderr, "RADIOBERRY_IOC_COMMAND Error.");
exit(-1);
}
if (rb_info.major == 0) sleep(1);
init++;
} while (rb_info.major == 0 && init < 10);
gateware_major_version = rb_info.major;
gateware_minor_version = rb_info.minor;
fprintf(stderr, "Radioberry gateware version %d-%d.\n", rb_info.major, rb_info.minor);
gateware_fpga_type = rb_info.fpga;
driver_version = rb_info.version;
//***********************************************
// Filters switching initialization
//***********************************************
initFilters();
//***********************************************
init_I2C_bias();
openI2C_measure();
pthread_t pid1, pid2;
pthread_create(&pid1, NULL, packetreader, NULL);
pthread_create(&pid2, NULL, txWriter, NULL);
start_rb_control_thread();
start_rb_measure_thread();
/* create a UDP socket */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("cannot create socket\n");
return 0;
}
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = TIMEOUT_MS;
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO,(char*)&timeout,sizeof(timeout)) < 0)
perror("setsockopt failed\n");
int optval = 7; // high priority.
if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &optval, sizeof(optval))<0) {
perror("fd socket: SO_PRIORITY");
}
/* bind the socket to any valid IP address and a specific port */
memset((char *)&myaddr, 0, sizeof(myaddr));
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
myaddr.sin_port = htons(SERVICE_PORT);
if (bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
perror("bind failed");
return 0;
}
if ((sock_TCP_Server = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket tcp");
return -1;
}
int tcpmaxseg = 1032;
int yes = 1;
setsockopt(sock_TCP_Server, IPPROTO_TCP, TCP_MAXSEG, (const char *)&tcpmaxseg, sizeof(int));
int sndbufsize = 65535;
int rcvbufsize = 65535;
setsockopt(sock_TCP_Server, SOL_SOCKET, SO_SNDBUF, (const char *)&sndbufsize, sizeof(int));
setsockopt(sock_TCP_Server, SOL_SOCKET, SO_RCVBUF, (const char *)&rcvbufsize, sizeof(int));
setsockopt(sock_TCP_Server, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout, sizeof(timeout)); //added
setsockopt(sock_TCP_Server, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
if (setsockopt(sock_TCP_Server, SOL_SOCKET, SO_PRIORITY, &optval, sizeof(optval))<0) {
perror("sock_TCP_Server socket: SO_PRIORITY");
}
if (bind(sock_TCP_Server, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0)
{
perror("bind tcp");
return -1;
}
listen(sock_TCP_Server, 1024);
int flags = fcntl(sock_TCP_Server, F_GETFL, 0);
fcntl(sock_TCP_Server, F_SETFL, flags | O_NONBLOCK);
start_rb_register_thread();
return 0;
}
static void closeRadioberry(void) {
if (fd_rb != 0) close(fd_rb);
if (sock_TCP_Client >= 0) close(sock_TCP_Client);
if (sock_TCP_Server >= 0) close(sock_TCP_Server);
close_I2C_bias();
close_I2C_measure();
}
static void runRadioberry(void) {
fprintf(stderr, "Radioberry, Starting packet control part. \n");
start_timer_thread();
fprintf(stderr, "Radioberry, Starting packet tx part. \n");
while(!(running==0 && closerb == 1)) {
if (running) {
active = 1;
sendPacket();
} else {active = 0; usleep(20000); }
}
}
static void *packetreader(void *arg) {
int size, bytes_read, bytes_left;
unsigned char buffer[2048];
uint32_t *code0 = (uint32_t *) buffer;
fprintf(stderr, "Radioberry, Starting packet rx part. \n");
while(1) {
if (sock_TCP_Client >= 0) {
// handle TCP protocol.
bytes_read=0;
bytes_left=1032;
while (bytes_left > 0) {
size = recvfrom(sock_TCP_Client, buffer+bytes_read, (size_t) bytes_left, 0, NULL, 0);
if (size < 0 && errno == EAGAIN) continue;
if (size < 0) break;
bytes_read += size;
bytes_left -= size;
}
if (bytes_read == 1032) handlePacket(buffer); else fprintf(stderr, "tcp packet received; wrong length %d \n", bytes_read);
}
else {
// handle UDP protocol.
int recvlen = recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr *)&remaddr, &addrlen);
if (recvlen > 0) { udp_retries = 0; handlePacket(buffer); } else udp_retries++;
// If nothing has arrived via UDP for some time (defined by socket timeout * 10) , try to open TCP connection.
if (sock_TCP_Client < 0 && udp_retries > 10)
{
if((sock_TCP_Client = accept(sock_TCP_Server, NULL, NULL)) > -1)
{
fprintf(stderr, "sock_TCP_Client: %d connected to sock_TCP_Server: %d\n", sock_TCP_Client, sock_TCP_Server);
}
udp_retries=0;
}
}
}
}
static void handlePacket(char* buffer){
uint32_t code;
memcpy(&code, buffer, 4);
switch (code)
{
default:
fprintf(stderr, "Received packages not for me! \n");
break;
case 0x0002feef:
fprintf(stderr, "Discovery packet received \n");
fprintf(stderr,"SDR Program IP-address %s \n", inet_ntoa(remaddr.sin_addr));
fprintf(stderr, "Discovery Port %d \n", ntohs(remaddr.sin_port));
memset(broadcastReply, 0, 60);
unsigned char reply[22] = {0xEF, 0xFE, 0x02, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, gateware_major_version, HERMESLITE, 0, 0, 0, 0, 0, 0, 0, 0, NR, 0, gateware_minor_version };
memcpy(broadcastReply, reply, 22);
if (sock_TCP_Client > -1) {
send(sock_TCP_Client, broadcastReply, 60, 0);
close(sock_TCP_Client);
sock_TCP_Client = -1;
}
else if (sendto(fd, broadcastReply, sizeof(broadcastReply), 0, (struct sockaddr *)&remaddr, addrlen) < 0) fprintf(stderr, "broadcast reply error");
break;
case 0x0004feef:
fprintf(stderr, "SDR Program sends Stop command \n");
running = 0;
while (active) usleep(1000);
last_sequence_number = 0;
if (sock_TCP_Client > -1)
{
close(sock_TCP_Client);
sock_TCP_Client = -1;
fprintf(stderr, "SDR Program sends TCP Stop command \n");
} else fprintf(stderr, "SDR Program sends UDP Stop command \n");
break;
case 0x0104feef:
case 0x0304feef:
fprintf(stderr, "Start Port %d \n", ntohs(remaddr.sin_port));
running = 1;
if (sock_TCP_Client > -1)
fprintf(stderr, "SDR Program sends TCP Start command \n");
else
fprintf(stderr, "SDR Program sends UDP Start command \n");
break;
case 0x1104feef:
fprintf(stderr, "Connect the TCP client to the server\n");
if (sock_TCP_Client < 0)
{
if((sock_TCP_Client = accept(sock_TCP_Server, NULL, NULL)) < 0)
{
fprintf(stderr, "*** ERROR TCP accept ***\n");
perror("accept");
return;
}
fprintf(stderr, "sock_TCP_Client: %d connected to sock_TCP_Server: %d\n", sock_TCP_Client, sock_TCP_Server);
running = 1;
fprintf(stderr, "SDR Program sends TCP Start command \n");
}
break;
case 0x0201feef:
processPacket(buffer);
break;
}
}
#define assign_change(a,b,c) if ((a) != b) { b = (a); fprintf(stderr, "%20s= %08lx (%10ld)\n", c, (long) b, (long) b ); }
static void handleCommand(int base_index, char* buffer) {
command = buffer[base_index];
command_data=((buffer[base_index+1]&0xFF)<<24)+((buffer[base_index+2]&0xFF)<<16)+((buffer[base_index+3]&0xFF)<<8)+(buffer[base_index+4]&0xFF);
// switch off the attached power amplifier if the current and temp could not be measured.
if (((command >> 1)&0x09) == 0x09 ) if (!i2c_measure_module_active) command_data = command_data & 0xFFF7FFFF;
// pa bias setting
if (((command >> 1)&0x3D) == 0x3D) write_I2C_bias(((command_data>>8)&0xFF), command_data & 0xFF);
if (commands[command] != command_data) {
commands[command] = command_data;
if ((command & 0x1E) == 0x1E) CWX = (command_data & 0x01000000) ? 0x01:0x00;
push(command);
sem_post(&spi_msg);
}
}
static void handleCommands(char* buffer) {
handleCommand(11, buffer);
handleCommand(523, buffer);
}
static void processPacket(char* buffer)
{
MOX = ((buffer[11] & 0x01)==0x01) ? 0x01:0x00;
handleCommands(buffer);
int frame = 0;
for (frame; frame < 2; frame++)
{
int coarse_pointer = frame * 512 + 8;
int j = 8;
for (j; j < 512; j += 8)
{
int k = coarse_pointer + j;
if (MOX || CWX) {
sem_wait(&tx_empty);
int i = 0;
for (i; i < 8; i++){
put_tx_buffer(buffer[k + i]);
}
sem_post(&tx_full);
}
}
}
//nrx for internal use required.
if ((buffer[ 11] & 0xFE) == 0x00) {
assign_change((((buffer[ 15] & 0x38) >> 3) + 1), nrx, "Receivers");
}
if ((buffer[523] & 0xFE) == 0x00) {
assign_change((((buffer[527] & 0x38) >> 3) + 1), nrx, "Receivers");
}
//**************************************************
// Handling filter change
//**************************************************
handleFilters(buffer, CWX);
//**************************************************
}
static void sendPacket(void) {
fillPacketToSend();
if (sock_TCP_Client >= 0) {
if (sendto(sock_TCP_Client, hpsdrdata, sizeof(hpsdrdata), 0, NULL, 0) != 1032) fprintf(stderr, "TCP send error");
} else {
if (sendto(fd, hpsdrdata, sizeof(hpsdrdata), 0, (struct sockaddr *)&remaddr, addrlen) != 1032) fprintf(stderr, "UDP send error");
}
}
static void read_temperature_raspberryPi(void) {
FILE *thermal;
thermal = fopen("/sys/class/thermal/thermal_zone0/temp","r");
float systemp, millideg;
if (fscanf(thermal,"%f",&millideg) == 1) {
systemp = millideg / 1000;
//fprintf(stderr, "CPU temperature is %f degrees C\n",systemp);
sys_temp = (int) (4096/3.26) * ((systemp/ 100) + 0.5);
//fprintf(stderr, "CPU temperature in protocol has value %x\n",sys_temp);
}
fclose(thermal);
}
static void fillPacketToSend(void) {
memset(hpsdrdata,0,1032);
memcpy(hpsdrdata, header_hpsdrdata, 4);
hpsdrdata[4] = ((last_sequence_number >> 24) & 0xFF);
hpsdrdata[5] = ((last_sequence_number >> 16) & 0xFF);
hpsdrdata[6] = ((last_sequence_number >> 8) & 0xFF);
hpsdrdata[7] = (last_sequence_number & 0xFF);
last_sequence_number++;
memcpy(hpsdrdata + 8, sync_hpsdrdata, 8);
memcpy(hpsdrdata + 520, sync_hpsdrdata, 8);
if (lnrx != nrx) usleep(1000);
lnrx = nrx;
int factor = (lnrx - 1) * 6;
for (int frame = 0; frame < 2; frame++) {
int coarse_pointer = frame * 512; // 512 bytes total in each frame
int nr_samples = (nrx == 1)? 63 : (nrx == 2)? 72: (nrx ==3)? 75: 76;
if (read(fd_rb , rx_buffer , nr_samples) < 0) {
fprintf(stderr, "Error %d reading frame from radioberry device\n", errno);
break;
}
rb_sample = 0;
for (int i=0; i< (504 / (8 + factor)); i++) {
int index = 16 + coarse_pointer + (i * (8 + factor));
//NR must be read from gateware.
for (int r = 0; r < MIN(lnrx, NR); r++) {
memcpy(hpsdrdata + index + (r * 6), rx_buffer + rb_sample, 6);
rb_sample+=6;
}
}
// inform the SDR about the radioberry control status.
// https://github.com/softerhardware/Hermes-Lite2/wiki/Protocol
if ( !i2c_measure_module_active & last_sequence_number % 500 == 0) read_temperature_raspberryPi();
if ( last_sequence_number % 2 == 0) {
// if i2c_measure_module_active; the temperature of module is used, otherwise the RPI temp.
hpsdrdata[11 + coarse_pointer] = 0x08 | (rb_control & 0x07);
if (i2c_measure_module_active) {
hpsdrdata[12 + coarse_pointer] = ((pa_temp >> 8) & 0xFF);
hpsdrdata[13 + coarse_pointer] = (pa_temp & 0xFF);
} else {
hpsdrdata[12 + coarse_pointer] = ((sys_temp >> 8) & 0xFF);
hpsdrdata[13 + coarse_pointer] = (sys_temp & 0xFF);
}
} else {
hpsdrdata[11 + coarse_pointer] = 0x10 | (rb_control & 0x07);
hpsdrdata[14 + coarse_pointer] = ((pa_current >> 8) & 0xFF);
hpsdrdata[15 + coarse_pointer] = (pa_current & 0xFF);
}
}
}
static void send_control(unsigned char command) {
unsigned char data[6];
uint32_t command_data = commands[command];
// if temperature could not be measured the pa is disabled
rb_info.rb_command = ( (pa_temp_ok ? 0x04 : 0x00) | ((CWX << 1) & 0x02) | (running & 0x01) );
rb_info.command = command;
rb_info.command_data = command_data;
//fprintf(stderr, "RB-Command = %02X Command = %02X command_data = %08X\n", rb_info.rb_command, command, command_data);
if (ioctl(fd_rb, RADIOBERRY_IOC_COMMAND, &rb_info) == -1) {
fprintf(stderr, "Could not sent commando to radioberry device.");
}
//fprintf(stderr, "RB Gateware control = %02X \n", rb_info.rb_command);
rb_control = rb_info.rb_command;
}
static void *rb_control_thread(void *arg) {
while(1) {
sem_wait(&spi_msg);
if (empty()) send_control(MOX); else send_control(pop());
}
fprintf(stderr,"rb_control_thread: exiting\n");
return NULL;
}
static void start_rb_control_thread(void) {
pthread_t pid1;
pthread_create(&pid1, NULL, rb_control_thread, NULL);
}
static void *rb_measure_thread(void *arg) {
// temperature == (((T*.01)+.5)/3.26)*4096 if pa temperature > 50C (=1256) switch pa off! (pa_temp_ok)
int measured_temp_ok_count = 0;
while(1) {
sem_wait(&i2c_meas);
if (i2c_measure_module_active) read_I2C_measure(&pa_current, &pa_temp);
if (pa_temp_ok && (pa_temp >= 1256)) {
fprintf(stderr, "ALERT: temperature of PA is higher than 50ºC; PA will be switched off! \n");
pa_temp_ok = 0;
}
// PA recovery after high temperature; switch PA on again if PA temp is in range for 10 seconds.
if (!pa_temp_ok && (pa_temp < 1256)) measured_temp_ok_count++;
if (measured_temp_ok_count == 100) {
measured_temp_ok_count = 0;
pa_temp_ok = 1;
fprintf(stderr, "PA temperature is ok; PA can be used! \n");
}
}
fprintf(stderr,"rb_measure_thread: exiting\n");
return NULL;
}
static void start_rb_measure_thread(void) {
pthread_t pid1;
pthread_create(&pid1, NULL, rb_measure_thread, NULL);
}
static void *rb_register_thread(void *arg) {
sleep(60);
sprintf(gatewareversion,"%d.%d", gateware_major_version, gateware_minor_version);
sprintf(firmwareversion,"%s", FIRMWAREVERSION);
sprintf(driverversion,"%.2f", driver_version/100.0);
gateware_fpga_type == 0 ? sprintf(fpgatype,"%s", "-") : gateware_fpga_type == 1 ? sprintf(fpgatype,"%s", "CL016") : sprintf(fpgatype,"%s", "CL025");
registerRadioberry();
return NULL;
}
static void start_rb_register_thread(void) {
pthread_t pid1;
pthread_create(&pid1, NULL, rb_register_thread, NULL);
}
static void *timer_thread(void *arg) {
while(1) {
usleep(100000);
if (running) sem_post(&spi_msg);
if (running) sem_post(&i2c_meas);
}
fprintf(stderr,"timer_thread: exiting\n");
return NULL;
}
static void start_timer_thread(void ) {
pthread_t pid1;
pthread_create(&pid1, NULL, timer_thread, NULL);
}
static void *txWriter(void *arg) {
gettimeofday(&t20, 0);
while(1) {
sem_wait(&tx_full);
int i = 0;
for (i; i < 4; i++){
get_tx_buffer(); //EER first 4 bytes followed by 4 bytes TX IQ.
}
for (i=0; i < 4; i++){
tx_iqdata[i] = get_tx_buffer(); //EER first 4 bytes followed by 4 bytes TX IQ.
}
//first setup without EER
if (MOX || CWX) {
//fprintf(stderr, "I = %2X - %2X Q= %2X - %2X \n", tx_iqdata[0], tx_iqdata[1], tx_iqdata[2], tx_iqdata[3]);
if (write(fd_rb , tx_iqdata , sizeof(tx_iqdata))<0)
fprintf(stderr, "Error %d writing to radioberry device\n", errno);
}
sem_post(&tx_empty);
tx_count ++;
if (tx_count == 48000) {
tx_count = 0;
gettimeofday(&t21, 0);
float elapsed = timedifference_msec(t20, t21);
fprintf(stderr, "Code tx write executed in %f milliseconds.\n", elapsed);
//fprintf(stderr, "tx_iqdata = %02X - %02X - %02X - %02X\n", tx_iqdata[4], tx_iqdata[5], tx_iqdata[6], tx_iqdata[7]);
gettimeofday(&t20, 0);
}
}
}
static void put_tx_buffer(unsigned char value) {
tx_buffer[fill_tx] = value;
fill_tx = (fill_tx + 1) % TX_MAX_BUFFER;
}
static unsigned char get_tx_buffer() {
unsigned char tmp = tx_buffer[use_tx];
use_tx = (use_tx + 1) % TX_MAX_BUFFER;
return tmp;
}
//end of source.