/*
 *	User side initialisation of the sonix card. Done here to avoid
 *	kernel bloat.
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <net/if.h>
#include "sonix_cfg.h"

void sonix_timeout()
{
	#define MSG "sonix: board not responding.\n"
	/* stdio isn't defined to be re-entrant in ANSI C */
	write(2,MSG,strlen(MSG));
	exit(1);
}

static int sonix_sendexpect(int fd, char *in, char *out, int nook)
{	
	char data[64];
	int r;
	signal(SIGALRM, sonix_timeout);
	alarm(3);
/*	printf(">%s\n",in);*/
	write(fd,in,strlen(in));
	r=read(fd,out, 64);
/*	printf("<%s\n",out);*/
	if(!nook && strstr(out,"OK")==NULL)
	{
		do
		{
			read(fd,data,64);
/*			printf("<%s\n",data);*/
		}
		while(strstr(data,"OK")==NULL);
	}
	alarm(0);
	return r;
}

static int hexbits(unsigned char *bp)
{
	unsigned char b[3];
	int v;
	b[0]=*bp++;
	b[1]=*bp;
	b[2]=0;
	sscanf(b,"%X",&v);
	return v;
}
	
static void sonix_setmacaddr(int fd, unsigned char *bp)
{
	unsigned char buf[64];
	unsigned char *p;
	struct ifreq ifr;
	int sfd=socket(AF_INET, SOCK_DGRAM,0);
	if(sfd==-1)
	{
		perror("socket");
		exit(1);
	}
	sonix_sendexpect(fd,"ATI3\r",buf,0);
	/* buf now holds 6 hex digits */
	strcpy(ifr.ifr_name,"sonix0");
	memcpy(ifr.ifr_hwaddr.sa_data,"\x00\x20\x8A",3);
	p=buf;
	while(*p<'0'|| *p>'9')
		p++;
	ifr.ifr_hwaddr.sa_data[3]=hexbits(p);
	ifr.ifr_hwaddr.sa_data[4]=hexbits(p+2);
	ifr.ifr_hwaddr.sa_data[5]=hexbits(p+4);
	ifr.ifr_hwaddr.sa_family=ARPHRD_ETHER;
	memcpy(bp, ifr.ifr_hwaddr.sa_data,6);
/*	printf("%02X.%02X.%02X.%02X.%02X.%02X",
		(int)bp[0],(int)bp[1],(int)bp[2],
		(int)bp[3],(int)bp[4],(int)bp[5]);*/
		
	if(ioctl(sfd,SIOCSIFHWADDR,&ifr)==-1)
	{
		perror("sonix0: hardware address");
		exit(1);
	}
	close(sfd);
}

static void sonix_setparams(int fd)
{	
	char buf[64];
	sonix_sendexpect(fd,"ATE0\r",buf,0);
	sonix_sendexpect(fd,"ATV1\r",buf,0);
}

static void sonix_checkver(int fd)
{
	int ver;
	char buf[64];
	char *tp;
	sonix_sendexpect(fd,"ATI6\r",buf,1);
	tp=strchr(buf,'/');
	if(tp)
	{
		tp=strchr(tp+1,'-');
		if(tp)
		{
			while(++*tp=='0');
			sscanf(tp-1,"%d",&ver);
			if(ver<7)
				fprintf(stderr,"sonix0: You need to get a flash uprade for your board.\n");
		}
		return;
	}
	fprintf(stderr,"sonix0: Strange version response.\n");
}

static void sonix_netmode(int fd)
{
	char buf[64];
	sonix_sendexpect(fd,"ATI71\r",buf,0);
}


void main(int argc, char *argv[])
{
	static struct
	{
		char zero __attribute__((packed));
		char cfg __attribute__((packed));
		char zero2 __attribute__((packed));
		char cmd __attribute__((packed));
		char zero3 __attribute__((packed));
		char len __attribute__((packed));
		char scramble __attribute__((packed));
		char compress __attribute__((packed));
		char mac[6] __attribute__((packed));
		char nodename[12] __attribute__((packed));
		char zero4[14] __attribute__((packed));
	} ctrl;
	
	int fd;
	
	ctrl.cfg=L_LAPB;
	ctrl.cmd=L_INIT_PARAMS;
	ctrl.len=40;
	
	
	fd=open("/dev/sonix0modem",O_RDWR);
	if(fd==-1)
	{
		perror("sonix0");
		exit(1);
	}
	sonix_setparams(fd);			
	sonix_checkver(fd);
	sonix_setmacaddr(fd, ctrl.mac);
	sonix_netmode(fd);
	close(fd);
	
	fd=open("/dev/sonix0cfg", O_RDWR);
	if(fd==-1)
	{
		perror("config");
		exit(1);
	}

	while(argv[1]&&argv[1][0]=='-')
	{
		if(strcmp(argv[1],"-fastcomp")==0)
			ctrl.compress=2;
		else if(strcmp(argv[1],"-bestcomp")==0)
			ctrl.compress=1;
		else if(strcmp(argv[1],"-scramble")==0)
			ctrl.scramble=1;
		else
		{
			fprintf(stderr,"Unknown option '%s'.\n",
				argv[1]);
		}
	}
	if(argv[1]==NULL)
	{
		char buf[64];
		gethostname(buf,64);
		memcpy(ctrl.nodename,buf,sizeof(ctrl.nodename));
	}
	else
	{
		memcpy(ctrl.nodename,argv[1],sizeof(ctrl.nodename));
		if(argv[2])
			fprintf(stderr,"Excess arguments ignored.\n");
	}

	write(fd,&ctrl,46);
	close(fd);
				
	printf("sonix0: Activated with node name of %s.\n",ctrl.nodename);
}
