Commit f9c418f6 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] kill soundmodem

It was dead before 2.4 (replaced by better userspace versions)
parent a994c542
......@@ -185,120 +185,6 @@ config BAYCOM_EPP
say M here and read <file:Documentation/modules.txt>. This is
recommended. The module will be called baycom_par.o.
config SOUNDMODEM
tristate "Soundcard modem driver"
depends on PARPORT && AX25
---help---
This experimental driver allows a standard Sound Blaster or
WindowsSoundSystem compatible sound card to be used as a packet
radio modem (NOT as a telephone modem!), to send digital traffic
over amateur radio.
To configure the driver, use the sethdlc, smdiag and smmixer
utilities available in the standard ax25 utilities package. For
information on how to key the transmitter, see
<http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html> and
<file:Documentation/networking/soundmodem.txt>.
If you want to compile this driver as a module ( = code which can be
inserted in and removed from the running kernel whenever you want),
say M here and read <file:Documentation/modules.txt>. This is
recommended. The module will be called soundmodem.o.
config SOUNDMODEM_SBC
bool "soundmodem support for Soundblaster and compatible cards"
depends on SOUNDMODEM
help
This option enables the soundmodem driver to use Sound Blaster and
compatible cards. If you have a dual mode card (i.e. a WSS cards
with a Sound Blaster emulation) you should say N here and Y to
"Sound card modem support for WSS and Crystal cards", below, because
this usually results in better performance. This option also
supports SB16/32/64 in full-duplex mode.
config SOUNDMODEM_WSS
bool "soundmodem support for WSS and Crystal cards"
depends on SOUNDMODEM
help
This option enables the soundmodem driver to use WindowsSoundSystem
compatible cards. These cards feature a codec chip from either
Analog Devices (such as AD1848, AD1845, AD1812) or Crystal
Semiconductors (such as CS4248, CS423x). This option also supports
the WSS full-duplex operation which currently works with Crystal
CS423x chips. If you don't need full-duplex operation, do not enable
it to save performance.
config SOUNDMODEM_AFSK1200
bool "soundmodem support for 1200 baud AFSK modulation"
depends on SOUNDMODEM
help
This option enables the soundmodem driver 1200 baud AFSK modem,
compatible to popular modems using TCM3105 or AM7911. The
demodulator requires about 12% of the CPU power of a Pentium 75 CPU
per channel.
config SOUNDMODEM_AFSK2400_7
bool "soundmodem support for 2400 baud AFSK modulation (7.3728MHz crystal)"
depends on SOUNDMODEM
help
This option enables the soundmodem driver 2400 baud AFSK modem,
compatible to TCM3105 modems (over-)clocked with a 7.3728MHz
crystal. Note that the availability of this driver does _not_ imply
that I recommend building such links. It is only here since users
especially in eastern Europe have asked me to do so. In fact this
modulation scheme has many disadvantages, mainly its incompatibility
with many transceiver designs and the fact that the TCM3105 (if
used) is operated widely outside its specifications.
config SOUNDMODEM_AFSK2400_8
bool "soundmodem support for 2400 baud AFSK modulation (8MHz crystal)"
depends on SOUNDMODEM
help
This option enables the soundmodem driver 2400 baud AFSK modem,
compatible to TCM3105 modems (over-)clocked with an 8MHz crystal.
Note that the availability of this driver does _not_ imply that I
recommend building such links. It is only here since users
especially in eastern Europe have asked me to do so. In fact this
modulation scheme has many disadvantages, mainly its incompatibility
with many transceiver designs and the fact that the TCM3105 (if
used) is operated widely outside its specifications.
config SOUNDMODEM_AFSK2666
bool "soundmodem support for 2666 baud AFSK modulation"
depends on SOUNDMODEM
help
This option enables the soundmodem driver 2666 baud AFSK modem.
This modem is experimental, and not compatible to anything
else I know of.
config SOUNDMODEM_HAPN4800
bool "soundmodem support for 4800 baud HAPN-1 modulation"
depends on SOUNDMODEM
help
This option enables the soundmodem driver 4800 baud HAPN-1
compatible modem. This modulation seems to be widely used 'down
under' and in the Netherlands. Here, nobody uses it, so I could not
test if it works. It is compatible to itself, however :-)
config SOUNDMODEM_PSK4800
bool "soundmodem support for 4800 baud PSK modulation"
depends on SOUNDMODEM
help
This option enables the soundmodem driver 4800 baud 8PSK modem.
This modem is experimental, and not compatible to anything
else I know of.
config SOUNDMODEM_FSK9600
bool "soundmodem support for 9600 baud FSK G3RUH modulation"
depends on SOUNDMODEM
help
This option enables the soundmodem driver 9600 baud FSK modem,
compatible to the G3RUH standard. The demodulator requires about 4%
of the CPU power of a Pentium 75 CPU per channel. You can say Y to
both 1200 baud AFSK and 9600 baud FSK if you want (but obviously you
can only use one protocol at a time, depending on what the other end
can understand).
config YAM
tristate "YAM driver for AX.25"
depends on AX25
......
#
# Makefile for the soundmodem device driver.
#
obj-$(CONFIG_SOUNDMODEM) += soundmodem.o
soundmodem-y := sm.o
soundmodem-$(CONFIG_SOUNDMODEM_SBC) += sm_sbc.o
soundmodem-$(CONFIG_SOUNDMODEM_WSS) += sm_wss.o
soundmodem-$(CONFIG_SOUNDMODEM_AFSK1200) += sm_afsk1200.o
soundmodem-$(CONFIG_SOUNDMODEM_AFSK2400_7) += sm_afsk2400_7.o
soundmodem-$(CONFIG_SOUNDMODEM_AFSK2400_8) += sm_afsk2400_8.o
soundmodem-$(CONFIG_SOUNDMODEM_AFSK2666) += sm_afsk2666.o
soundmodem-$(CONFIG_SOUNDMODEM_HAPN4800) += sm_hapn4800.o
soundmodem-$(CONFIG_SOUNDMODEM_PSK4800) += sm_psk4800.o
soundmodem-$(CONFIG_SOUNDMODEM_FSK9600) += sm_fsk9600.o
soundmodem-objs := $(soundmodem-y)
host-progs := gentbl
HOST_LOADLIBES := -lm
# Files generated that shall be removed upon make clean
clean-files := sm_tbl_afsk1200.h sm_tbl_afsk2400_7.h \
sm_tbl_afsk2400_8.h sm_tbl_afsk2666.h \
sm_tbl_psk4800.h sm_tbl_hapn4800.h \
sm_tbl_fsk9600.h
include $(TOPDIR)/Rules.make
# Dependencies on generates files need to be listed explicitly
$(obj)/sm_afsk1200.o: $(obj)/sm_tbl_afsk1200.h
$(obj)/sm_afsk2400_7.o: $(obj)/sm_tbl_afsk2400_7.h
$(obj)/sm_afsk2400_8.o: $(obj)/sm_tbl_afsk2400_8.h
$(obj)/sm_afsk2666.o: $(obj)/sm_tbl_afsk2666.h
$(obj)/sm_psk4800.o: $(obj)/sm_tbl_psk4800.h
$(obj)/sm_hapn4800.o: $(obj)/sm_tbl_hapn4800.h
$(obj)/sm_fsk9600.o: $(obj)/sm_tbl_fsk9600.h
$(obj)/sm_tbl_%: $(obj)/gentbl
cd $(obj) && ./gentbl
/*****************************************************************************/
/*
* gentbl.c -- soundcard radio modem driver table generator.
*
* Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Please note that the GPL allows you to use the driver, NOT the radio.
* In order to use the radio, you need a license from the communications
* authority of your country.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
/* -------------------------------------------------------------------- */
static void gentbl_offscostab(FILE *f, unsigned int nbits)
{
int i;
fprintf(f, "\n/*\n * small cosine table in U8 format\n */\n"
"#define OFFSCOSTABBITS %u\n"
"#define OFFSCOSTABSIZE (1<<OFFSCOSTABBITS)\n\n",
nbits);
fprintf(f, "static unsigned char offscostab[OFFSCOSTABSIZE] = {\n\t");
for (i = 0; i < (1<<nbits); i++) {
fprintf(f, "%4u", (int)
(128+127.0*cos(i*2.0*M_PI/(1<<nbits))));
if (i < (1<<nbits)-1)
fprintf(f, "%s", (i & 7) == 7 ? ",\n\t" : ",");
}
fprintf(f, "\n};\n\n"
"#define OFFSCOS(x) offscostab[((x)>>%d)&0x%x]\n\n",
16-nbits, (1<<nbits)-1);
}
/* -------------------------------------------------------------------- */
static void gentbl_costab(FILE *f, unsigned int nbits)
{
int i;
fprintf(f, "\n/*\n * more accurate cosine table\n */\n\n"
"static const short costab[%d] = {", (1<<nbits));
for (i = 0; i < (1<<nbits); i++) {
if (!(i & 7))
fprintf(f, "\n\t");
fprintf(f, "%6d", (int)(32767.0*cos(i*2.0*M_PI/(1<<nbits))));
if (i != ((1<<nbits)-1))
fprintf(f, ", ");
}
fprintf(f, "\n};\n\n#define COS(x) costab[((x)>>%d)&0x%x]\n"
"#define SIN(x) COS((x)+0xc000)\n\n", 16-nbits,
(1<<nbits)-1);
}
/* -------------------------------------------------------------------- */
#define AFSK12_SAMPLE_RATE 9600
#define AFSK12_TX_FREQ_LO 1200
#define AFSK12_TX_FREQ_HI 2200
#define AFSK12_CORRLEN 8
static void gentbl_afsk1200(FILE *f)
{
int i, v, sum;
#define ARGLO(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_LO/(double)AFSK12_SAMPLE_RATE
#define ARGHI(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_HI/(double)AFSK12_SAMPLE_RATE
fprintf(f, "\n/*\n * afsk1200 specific tables\n */\n"
"#define AFSK12_SAMPLE_RATE %u\n"
"#define AFSK12_TX_FREQ_LO %u\n"
"#define AFSK12_TX_FREQ_HI %u\n"
"#define AFSK12_CORRLEN %u\n\n",
AFSK12_SAMPLE_RATE, AFSK12_TX_FREQ_LO,
AFSK12_TX_FREQ_HI, AFSK12_CORRLEN);
fprintf(f, "static const int afsk12_tx_lo_i[] = {\n\t");
for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
sum += (v = 127.0*cos(ARGLO(i)));
fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
}
fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_I %d\n\n"
"static const int afsk12_tx_lo_q[] = {\n\t", sum);
for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
sum += (v = 127.0*sin(ARGLO(i)));
fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
}
fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_Q %d\n\n"
"static const int afsk12_tx_hi_i[] = {\n\t", sum);
for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
sum += (v = 127.0*cos(ARGHI(i)));
fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
}
fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_I %d\n\n"
"static const int afsk12_tx_hi_q[] = {\n\t", sum);
for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
sum += (v = 127.0*sin(ARGHI(i)));
fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
}
fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %d\n\n", sum);
#undef ARGLO
#undef ARGHI
}
/* -------------------------------------------------------------------- */
static const float fsk96_tx_coeff_4[32] = {
-0.001152, 0.000554, 0.002698, 0.002753,
-0.002033, -0.008861, -0.008002, 0.006607,
0.023727, 0.018905, -0.018056, -0.057957,
-0.044368, 0.055683, 0.207667, 0.322048,
0.322048, 0.207667, 0.055683, -0.044368,
-0.057957, -0.018056, 0.018905, 0.023727,
0.006607, -0.008002, -0.008861, -0.002033,
0.002753, 0.002698, 0.000554, -0.001152
};
static const float fsk96_tx_coeff_5[40] = {
-0.001009, -0.000048, 0.001376, 0.002547,
0.002061, -0.001103, -0.005795, -0.008170,
-0.004017, 0.006924, 0.018225, 0.019238,
0.002925, -0.025777, -0.048064, -0.039683,
0.013760, 0.104144, 0.200355, 0.262346,
0.262346, 0.200355, 0.104144, 0.013760,
-0.039683, -0.048064, -0.025777, 0.002925,
0.019238, 0.018225, 0.006924, -0.004017,
-0.008170, -0.005795, -0.001103, 0.002061,
0.002547, 0.001376, -0.000048, -0.001009
};
#define HAMMING(x) (0.54-0.46*cos(2*M_PI*(x)));
static inline float hamming(float x)
{
return 0.54-0.46*cos(2*M_PI*x);
}
static inline float sinc(float x)
{
if (x == 0)
return 1;
x *= M_PI;
return sin(x)/x;
}
static void gentbl_fsk9600(FILE *f)
{
int i, j, k, l, m;
float s;
float c[44];
float min, max;
fprintf(f, "\n/*\n * fsk9600 specific tables\n */\n");
min = max = 0;
memset(c, 0, sizeof(c));
#if 0
memcpy(c+2, fsk96_tx_coeff_4, sizeof(fsk96_tx_coeff_4));
#else
for (i = 0; i < 29; i++)
c[3+i] = sinc(1.2*((i-14.0)/4.0))*hamming(i/28.0)/3.5;
#endif
fprintf(f, "static unsigned char fsk96_txfilt_4[] = {\n\t");
for (i = 0; i < 4; i++) {
for (j = 0; j < 256; j++) {
for (k = 1, s = 0, l = i; k < 256; k <<= 1) {
if (j & k) {
for (m = 0; m < 4; m++, l++)
s += c[l];
} else {
for (m = 0; m < 4; m++, l++)
s -= c[l];
}
}
s *= 0.75;
if (s > max)
max = s;
if (s < min)
min = s;
fprintf(f, "%4d", (int)(128+127*s));
if (i < 3 || j < 255)
fprintf(f, ",%s", (j & 7) == 7
? "\n\t" : "");
}
}
#ifdef VERBOSE
fprintf(stderr, "fsk9600: txfilt4: min = %f; max = %f\n", min, max);
#endif
fprintf(f, "\n};\n\n");
min = max = 0;
memset(c, 0, sizeof(c));
#if 0
memcpy(c+2, fsk96_tx_coeff_5, sizeof(fsk96_tx_coeff_5));
#else
for (i = 0; i < 36; i++)
c[4+i] = sinc(1.2*((i-17.5)/5.0))*hamming(i/35.0)/4.5;
#endif
fprintf(f, "static unsigned char fsk96_txfilt_5[] = {\n\t");
for (i = 0; i < 5; i++) {
for (j = 0; j < 256; j++) {
for (k = 1, s = 0, l = i; k < 256; k <<= 1) {
if (j & k) {
for (m = 0; m < 5; m++, l++)
s += c[l];
} else {
for (m = 0; m < 5; m++, l++)
s -= c[l];
}
}
s *= 0.75;
if (s > max)
max = s;
if (s < min)
min = s;
fprintf(f, "%4d", (int)(128+127*s));
if (i < 4 || j < 255)
fprintf(f, ",%s", (j & 7) == 7
? "\n\t" : "");
}
}
#ifdef VERBOSE
fprintf(stderr, "fsk9600: txfilt5: min = %f; max = %f\n", min, max);
#endif
fprintf(f, "\n};\n\n");
}
/* -------------------------------------------------------------------- */
#define AFSK26_SAMPLERATE 16000
#define AFSK26_NUMCAR 2
#define AFSK26_FIRSTCAR 2000
#define AFSK26_MSK_LEN 6
#define AFSK26_RXOVER 2
#define AFSK26_DEMCORRLEN (2*AFSK26_MSK_LEN)
#define AFSK26_WINDOW(x) ((1-cos(2.0*M_PI*(x)))/2.0)
#define AFSK26_AMPL(x) (((x)?1.0:0.7))
#undef AFSK26_AMPL
#define AFSK26_AMPL(x) 1
static void gentbl_afsk2666(FILE *f)
{
int i, j, k, l, o, v, sumi, sumq;
float window[AFSK26_DEMCORRLEN*AFSK26_RXOVER];
int cfreq[AFSK26_NUMCAR];
fprintf(f, "\n/*\n * afsk2666 specific tables\n */\n"
"#define AFSK26_DEMCORRLEN %d\n"
"#define AFSK26_SAMPLERATE %d\n\n", AFSK26_DEMCORRLEN,
AFSK26_SAMPLERATE);
fprintf(f, "static const unsigned int afsk26_carfreq[%d] = { ",
AFSK26_NUMCAR);
for (i = 0; i < AFSK26_NUMCAR; i++) {
cfreq[i] = 0x10000*AFSK26_FIRSTCAR/AFSK26_SAMPLERATE+
0x10000*i/AFSK26_MSK_LEN/2;
fprintf(f, "0x%x", cfreq[i]);
if (i < AFSK26_NUMCAR-1)
fprintf(f, ", ");
}
fprintf(f, " };\n\n");
for (i = 0; i < AFSK26_DEMCORRLEN*AFSK26_RXOVER; i++)
window[i] = AFSK26_WINDOW(((float)i)/(AFSK26_DEMCORRLEN*
AFSK26_RXOVER)) * 127.0;
fprintf(f, "\nstatic const struct {\n\t"
"int i[%d];\n\tint q[%d];\n} afsk26_dem_tables[%d][%d] = {\n",
AFSK26_DEMCORRLEN, AFSK26_DEMCORRLEN, AFSK26_RXOVER, AFSK26_NUMCAR);
for (o = AFSK26_RXOVER-1; o >= 0; o--) {
fprintf(f, "\t{\n");
for (i = 0; i < AFSK26_NUMCAR; i++) {
j = cfreq[i];
fprintf(f, "\t\t{{ ");
for (l = AFSK26_DEMCORRLEN-1, k = (j * o)/AFSK26_RXOVER, sumi = 0; l >= 0;
l--, k = (k+j)&0xffffu) {
sumi += (v = AFSK26_AMPL(i)*window[l*AFSK26_RXOVER+o]*
cos(M_PI*k/32768.0));
fprintf(f, "%6d%s", v, l ? ", " : " }, { ");
}
for (l = AFSK26_DEMCORRLEN-1, k = (j * o)/AFSK26_RXOVER, sumq = 0; l >= 0;
l--, k = (k+j)&0xffffu) {
sumq += (v = AFSK26_AMPL(i)*window[l*AFSK26_RXOVER+o]*
sin(M_PI*k/32768.0));
fprintf(f, "%6d%s", v, l ? ", " : " }}");
}
if (i < 1)
fprintf(f, ",");
fprintf(f, "\n#define AFSK26_DEM_SUM_I_%d_%d %d\n"
"#define AFSK26_DEM_SUM_Q_%d_%d %d\n",
AFSK26_RXOVER-1-o, i, sumi, AFSK26_RXOVER-1-o, i, sumq);
}
fprintf(f, "\t}%s\n", o ? "," : "");
}
fprintf(f, "};\n\n");
}
/* -------------------------------------------------------------------- */
#define ATAN_TABLEN 1024
static void gentbl_atantab(FILE *f)
{
int i;
short x;
fprintf(f, "\n/*\n"
" * arctan table (indexed by i/q; should really be indexed by i/(i+q)\n"
" */\n""#define ATAN_TABLEN %d\n\n"
"static const unsigned short atan_tab[ATAN_TABLEN+2] = {",
ATAN_TABLEN);
for (i = 0; i <= ATAN_TABLEN; i++) {
if (!(i & 7))
fprintf(f, "\n\t");
x = atan(i / (float)ATAN_TABLEN) / M_PI * 0x8000;
fprintf(f, "%6d, ", x);
}
fprintf(f, "%6d\n};\n\n", x);
}
/* -------------------------------------------------------------------- */
#define PSK48_TXF_OVERSAMPLING 5
#define PSK48_TXF_NUMSAMPLES 16
#define PSK48_RXF_LEN 64
static const float psk48_tx_coeff[80] = {
-0.000379, -0.000640, -0.000000, 0.000772,
0.000543, -0.000629, -0.001187, -0.000000,
0.001634, 0.001183, -0.001382, -0.002603,
-0.000000, 0.003481, 0.002472, -0.002828,
-0.005215, -0.000000, 0.006705, 0.004678,
-0.005269, -0.009584, -0.000000, 0.012065,
0.008360, -0.009375, -0.017028, -0.000000,
0.021603, 0.015123, -0.017229, -0.032012,
-0.000000, 0.043774, 0.032544, -0.040365,
-0.084963, -0.000000, 0.201161, 0.374060,
0.374060, 0.201161, -0.000000, -0.084963,
-0.040365, 0.032544, 0.043774, -0.000000,
-0.032012, -0.017229, 0.015123, 0.021603,
-0.000000, -0.017028, -0.009375, 0.008360,
0.012065, -0.000000, -0.009584, -0.005269,
0.004678, 0.006705, -0.000000, -0.005215,
-0.002828, 0.002472, 0.003481, -0.000000,
-0.002603, -0.001382, 0.001183, 0.001634,
-0.000000, -0.001187, -0.000629, 0.000543,
0.000772, -0.000000, -0.000640, -0.000379
};
static const float psk48_rx_coeff[PSK48_RXF_LEN] = {
-0.000219, 0.000360, 0.000873, 0.001080,
0.000747, -0.000192, -0.001466, -0.002436,
-0.002328, -0.000699, 0.002101, 0.004809,
0.005696, 0.003492, -0.001633, -0.007660,
-0.011316, -0.009627, -0.001780, 0.009712,
0.019426, 0.021199, 0.011342, -0.008583,
-0.030955, -0.044093, -0.036634, -0.002651,
0.054742, 0.123101, 0.184198, 0.220219,
0.220219, 0.184198, 0.123101, 0.054742,
-0.002651, -0.036634, -0.044093, -0.030955,
-0.008583, 0.011342, 0.021199, 0.019426,
0.009712, -0.001780, -0.009627, -0.011316,
-0.007660, -0.001633, 0.003492, 0.005696,
0.004809, 0.002101, -0.000699, -0.002328,
-0.002436, -0.001466, -0.000192, 0.000747,
0.001080, 0.000873, 0.000360, -0.000219
};
static void gentbl_psk4800(FILE *f)
{
int i, j, k;
short x;
fprintf(f, "\n/*\n * psk4800 specific tables\n */\n"
"#define PSK48_TXF_OVERSAMPLING %d\n"
"#define PSK48_TXF_NUMSAMPLES %d\n\n"
"#define PSK48_SAMPLERATE 8000\n"
"#define PSK48_CAR_FREQ 2000\n"
"#define PSK48_PSK_LEN 5\n"
"#define PSK48_RXF_LEN %u\n"
"#define PSK48_PHASEINC (0x10000*PSK48_CAR_FREQ/PSK48_SAMPLERATE)\n"
"#define PSK48_SPHASEINC (0x10000/(2*PSK48_PSK_LEN))\n\n"
"static const short psk48_tx_table[PSK48_TXF_OVERSAMPLING*"
"PSK48_TXF_NUMSAMPLES*8*2] = {",
PSK48_TXF_OVERSAMPLING, PSK48_TXF_NUMSAMPLES, PSK48_RXF_LEN);
for (i = 0; i < PSK48_TXF_OVERSAMPLING; i++) {
for (j = 0; j < PSK48_TXF_NUMSAMPLES; j++) {
fprintf(f, "\n\t");
for (k = 0; k < 8; k++) {
x = 32767.0 * cos(k*M_PI/4.0) *
psk48_tx_coeff[j * PSK48_TXF_OVERSAMPLING + i];
fprintf(f, "%6d, ", x);
}
fprintf(f, "\n\t");
for (k = 0; k < 8; k++) {
x = 32767.0 * sin(k*M_PI/4.0) *
psk48_tx_coeff[j * PSK48_TXF_OVERSAMPLING + i];
fprintf(f, "%6d", x);
if (k != 7 || j != PSK48_TXF_NUMSAMPLES-1 ||
i != PSK48_TXF_OVERSAMPLING-1)
fprintf(f, ", ");
}
}
}
fprintf(f, "\n};\n\n");
fprintf(f, "static const short psk48_rx_coeff[PSK48_RXF_LEN] = {\n\t");
for (i = 0; i < PSK48_RXF_LEN; i++) {
fprintf(f, "%6d", (int)(psk48_rx_coeff[i]*32767.0));
if (i < PSK48_RXF_LEN-1)
fprintf(f, ",%s", (i & 7) == 7 ? "\n\t" : "");
}
fprintf(f, "\n};\n\n");
}
/* -------------------------------------------------------------------- */
static void gentbl_hapn4800(FILE *f)
{
int i, j, k, l;
float s;
float c[44];
float min, max;
fprintf(f, "\n/*\n * hapn4800 specific tables\n */\n\n");
/*
* firstly generate tables for the FM transmitter modulator
*/
min = max = 0;
memset(c, 0, sizeof(c));
for (i = 0; i < 24; i++)
c[8+i] = sinc(1.5*((i-11.5)/8.0))*hamming(i/23.0)/2.4;
for (i = 0; i < 24; i++)
c[i] -= c[i+8];
fprintf(f, "static unsigned char hapn48_txfilt_8[] = {\n\t");
for (i = 0; i < 8; i++) {
for (j = 0; j < 16; j++) {
for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 8) {
if (j & k)
s += c[l];
else
s -= c[l];
}
if (s > max)
max = s;
if (s < min)
min = s;
fprintf(f, "%4d", (int)(128+127*s));
if (i < 7 || j < 15)
fprintf(f, ",%s", (j & 7) == 7
? "\n\t" : "");
}
}
#ifdef VERBOSE
fprintf(stderr, "hapn4800: txfilt8: min = %f; max = %f\n", min, max);
#endif
fprintf(f, "\n};\n\n");
min = max = 0;
memset(c, 0, sizeof(c));
for (i = 0; i < 30; i++)
c[10+i] = sinc(1.5*((i-14.5)/10.0))*hamming(i/29.0)/2.4;
for (i = 0; i < 30; i++)
c[i] -= c[i+10];
fprintf(f, "static unsigned char hapn48_txfilt_10[] = {\n\t");
for (i = 0; i < 10; i++) {
for (j = 0; j < 16; j++) {
for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 10) {
if (j & k)
s += c[l];
else
s -= c[l];
}
if (s > max)
max = s;
if (s < min)
min = s;
fprintf(f, "%4d", (int)(128+127*s));
if (i < 9 || j < 15)
fprintf(f, ",%s", (j & 7) == 7
? "\n\t" : "");
}
}
#ifdef VERBOSE
fprintf(stderr, "hapn4800: txfilt10: min = %f; max = %f\n", min, max);
#endif
fprintf(f, "\n};\n\n");
/*
* secondly generate tables for the PM transmitter modulator
*/
min = max = 0;
memset(c, 0, sizeof(c));
for (i = 0; i < 25; i++)
c[i] = sinc(1.4*((i-12.0)/8.0))*hamming(i/24.0)/6.3;
for (i = 0; i < 25; i++)
for (j = 1; j < 8; j++)
c[i] += c[i+j];
fprintf(f, "static unsigned char hapn48_txfilt_pm8[] = {\n\t");
for (i = 0; i < 8; i++) {
for (j = 0; j < 16; j++) {
for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 8) {
if (j & k)
s += c[l];
else
s -= c[l];
}
if (s > max)
max = s;
if (s < min)
min = s;
fprintf(f, "%4d", (int)(128+127*s));
if (i < 7 || j < 15)
fprintf(f, ",%s", (j & 7) == 7
? "\n\t" : "");
}
}
#ifdef VERBOSE
fprintf(stderr, "hapn4800: txfiltpm8: min = %f; max = %f\n", min, max);
#endif
fprintf(f, "\n};\n\n");
min = max = 0;
memset(c, 0, sizeof(c));
for (i = 0; i < 31; i++)
c[10+i] = sinc(1.4*((i-15.0)/10.0))*hamming(i/30.0)/7.9;
for (i = 0; i < 31; i++)
for (j = 1; j < 10; j++)
c[i] += c[i+j];
fprintf(f, "static unsigned char hapn48_txfilt_pm10[] = {\n\t");
for (i = 0; i < 10; i++) {
for (j = 0; j < 16; j++) {
for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 10) {
if (j & k)
s += c[l];
else
s -= c[l];
}
if (s > max)
max = s;
if (s < min)
min = s;
fprintf(f, "%4d", (int)(128+127*s));
if (i < 9 || j < 15)
fprintf(f, ",%s", (j & 7) == 7
? "\n\t" : "");
}
}
#ifdef VERBOSE
fprintf(stderr, "hapn4800: txfiltpm10: min = %f; max = %f\n", min, max);
#endif
fprintf(f, "\n};\n\n");
}
/* -------------------------------------------------------------------- */
#define AFSK24_SAMPLERATE 16000
#define AFSK24_CORRLEN 14
static void gentbl_afsk2400(FILE *f, float tcm3105clk)
{
int i, sum, v;
fprintf(f, "\n/*\n * afsk2400 specific tables (tcm3105 clk %7fHz)\n */\n"
"#define AFSK24_TX_FREQ_LO %d\n"
"#define AFSK24_TX_FREQ_HI %d\n"
"#define AFSK24_BITPLL_INC %d\n"
"#define AFSK24_SAMPLERATE %d\n\n", tcm3105clk,
(int)(tcm3105clk/3694.0), (int)(tcm3105clk/2015.0),
0x10000*2400/AFSK24_SAMPLERATE, AFSK24_SAMPLERATE);
#define ARGLO(x) 2.0*M_PI*(double)x*(tcm3105clk/3694.0)/(double)AFSK24_SAMPLERATE
#define ARGHI(x) 2.0*M_PI*(double)x*(tcm3105clk/2015.0)/(double)AFSK24_SAMPLERATE
#define WINDOW(x) hamming((float)(x)/(AFSK24_CORRLEN-1.0))
fprintf(f, "static const int afsk24_tx_lo_i[] = {\n\t");
for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
sum += (v = 127.0*cos(ARGLO(i))*WINDOW(i));
fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
}
fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_I %d\n\n"
"static const int afsk24_tx_lo_q[] = {\n\t", sum);
for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
sum += (v = 127.0*sin(ARGLO(i))*WINDOW(i));
fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
}
fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_Q %d\n\n"
"static const int afsk24_tx_hi_i[] = {\n\t", sum);
for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
sum += (v = 127.0*cos(ARGHI(i))*WINDOW(i));
fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
}
fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_I %d\n\n"
"static const int afsk24_tx_hi_q[] = {\n\t", sum);
for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
sum += (v = 127.0*sin(ARGHI(i))*WINDOW(i));
fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
}
fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %d\n\n", sum);
#undef ARGLO
#undef ARGHI
#undef WINDOW
}
/* -------------------------------------------------------------------- */
static char *progname;
static void gentbl_banner(FILE *f)
{
fprintf(f, "/*\n * THIS FILE IS GENERATED AUTOMATICALLY BY %s, "
"DO NOT EDIT!\n */\n\n", progname);
}
/* -------------------------------------------------------------------- */
int main(int argc, char *argv[])
{
FILE *f;
progname = argv[0];
if (!(f = fopen("sm_tbl_afsk1200.h", "w")))
exit(1);
gentbl_banner(f);
gentbl_offscostab(f, 6);
gentbl_costab(f, 6);
gentbl_afsk1200(f);
fclose(f);
if (!(f = fopen("sm_tbl_afsk2666.h", "w")))
exit(1);
gentbl_banner(f);
gentbl_offscostab(f, 6);
gentbl_costab(f, 6);
gentbl_afsk2666(f);
fclose(f);
if (!(f = fopen("sm_tbl_psk4800.h", "w")))
exit(1);
gentbl_banner(f);
gentbl_psk4800(f);
gentbl_costab(f, 8);
gentbl_atantab(f);
fclose(f);
if (!(f = fopen("sm_tbl_hapn4800.h", "w")))
exit(1);
gentbl_banner(f);
gentbl_hapn4800(f);
fclose(f);
if (!(f = fopen("sm_tbl_fsk9600.h", "w")))
exit(1);
gentbl_banner(f);
gentbl_fsk9600(f);
fclose(f);
if (!(f = fopen("sm_tbl_afsk2400_8.h", "w")))
exit(1);
gentbl_banner(f);
gentbl_offscostab(f, 6);
gentbl_costab(f, 6);
gentbl_afsk2400(f, 8000000);
fclose(f);
if (!(f = fopen("sm_tbl_afsk2400_7.h", "w")))
exit(1);
gentbl_banner(f);
gentbl_offscostab(f, 6);
gentbl_costab(f, 6);
gentbl_afsk2400(f, 7372800);
fclose(f);
exit(0);
}
/* -------------------------------------------------------------------- */
/*****************************************************************************/
/*
* sm.c -- soundcard radio modem driver.
*
* Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Please note that the GPL allows you to use the driver, NOT the radio.
* In order to use the radio, you need a license from the communications
* authority of your country.
*
*
* Command line options (insmod command line)
*
* mode mode string; eg. "wss:afsk1200"
* iobase base address of the soundcard; common values are 0x220 for sbc,
* 0x530 for wss
* irq interrupt number; common values are 7 or 5 for sbc, 11 for wss
* dma dma number; common values are 0 or 1
*
*
* History:
* 0.1 21.09.1996 Started
* 18.10.1996 Changed to new user space access routines (copy_{to,from}_user)
* 0.4 21.01.1997 Separately compileable soundcard/modem modules
* 0.5 03.03.1997 fixed LPT probing (check_lpt result was interpreted the wrong way round)
* 0.6 16.04.1997 init code/data tagged
* 0.7 30.07.1997 fixed halfduplex interrupt handlers/hotfix for CS423X
* 0.8 14.04.1998 cleanups
* 0.9 03.08.1999 adapt to Linus' new __setup/__initcall
* use parport lowlevel drivers instead of directly writing to a parallel port
* removed some pre-2.2 kernel compatibility cruft
* 0.10 10.08.1999 Check if parport can do SPP and is safe to access during interrupt contexts
* 0.11 12.02.2000 adapted to softnet driver interface
* 0.12 03.07.2000 fix interface name handling
*/
/*****************************************************************************/
#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/parport.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "sm.h"
/* --------------------------------------------------------------------- */
/*static*/ const char sm_drvname[] = "soundmodem";
static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
KERN_INFO "soundmodem: version 0.12 compiled " __TIME__ " " __DATE__ "\n";
/* --------------------------------------------------------------------- */
/*static*/ const struct modem_tx_info *sm_modem_tx_table[] = {
#ifdef CONFIG_SOUNDMODEM_AFSK1200
&sm_afsk1200_tx,
#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
#ifdef CONFIG_SOUNDMODEM_AFSK2400_7
&sm_afsk2400_7_tx,
#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */
#ifdef CONFIG_SOUNDMODEM_AFSK2400_8
&sm_afsk2400_8_tx,
#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */
#ifdef CONFIG_SOUNDMODEM_AFSK2666
&sm_afsk2666_tx,
#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
#ifdef CONFIG_SOUNDMODEM_PSK4800
&sm_psk4800_tx,
#endif /* CONFIG_SOUNDMODEM_PSK4800 */
#ifdef CONFIG_SOUNDMODEM_HAPN4800
&sm_hapn4800_8_tx,
&sm_hapn4800_10_tx,
&sm_hapn4800_pm8_tx,
&sm_hapn4800_pm10_tx,
#endif /* CONFIG_SOUNDMODEM_HAPN4800 */
#ifdef CONFIG_SOUNDMODEM_FSK9600
&sm_fsk9600_4_tx,
&sm_fsk9600_5_tx,
#endif /* CONFIG_SOUNDMODEM_FSK9600 */
NULL
};
/*static*/ const struct modem_rx_info *sm_modem_rx_table[] = {
#ifdef CONFIG_SOUNDMODEM_AFSK1200
&sm_afsk1200_rx,
#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
#ifdef CONFIG_SOUNDMODEM_AFSK2400_7
&sm_afsk2400_7_rx,
#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */
#ifdef CONFIG_SOUNDMODEM_AFSK2400_8
&sm_afsk2400_8_rx,
#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */
#ifdef CONFIG_SOUNDMODEM_AFSK2666
&sm_afsk2666_rx,
#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
#ifdef CONFIG_SOUNDMODEM_PSK4800
&sm_psk4800_rx,
#endif /* CONFIG_SOUNDMODEM_PSK4800 */
#ifdef CONFIG_SOUNDMODEM_HAPN4800
&sm_hapn4800_8_rx,
&sm_hapn4800_10_rx,
&sm_hapn4800_pm8_rx,
&sm_hapn4800_pm10_rx,
#endif /* CONFIG_SOUNDMODEM_HAPN4800 */
#ifdef CONFIG_SOUNDMODEM_FSK9600
&sm_fsk9600_4_rx,
&sm_fsk9600_5_rx,
#endif /* CONFIG_SOUNDMODEM_FSK9600 */
NULL
};
static const struct hardware_info *sm_hardware_table[] = {
#ifdef CONFIG_SOUNDMODEM_SBC
&sm_hw_sbc,
&sm_hw_sbcfdx,
#endif /* CONFIG_SOUNDMODEM_SBC */
#ifdef CONFIG_SOUNDMODEM_WSS
&sm_hw_wss,
&sm_hw_wssfdx,
#endif /* CONFIG_SOUNDMODEM_WSS */
NULL
};
/* --------------------------------------------------------------------- */
#define NR_PORTS 4
static struct net_device sm_device[NR_PORTS];
/* --------------------------------------------------------------------- */
#define UART_RBR(iobase) (iobase+0)
#define UART_THR(iobase) (iobase+0)
#define UART_IER(iobase) (iobase+1)
#define UART_IIR(iobase) (iobase+2)
#define UART_FCR(iobase) (iobase+2)
#define UART_LCR(iobase) (iobase+3)
#define UART_MCR(iobase) (iobase+4)
#define UART_LSR(iobase) (iobase+5)
#define UART_MSR(iobase) (iobase+6)
#define UART_SCR(iobase) (iobase+7)
#define UART_DLL(iobase) (iobase+0)
#define UART_DLM(iobase) (iobase+1)
#define SER_EXTENT 8
#define MIDI_DATA(iobase) (iobase)
#define MIDI_STATUS(iobase) (iobase+1)
#define MIDI_READ_FULL 0x80 /* attention: negative logic!! */
#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */
#define MIDI_EXTENT 2
/* ---------------------------------------------------------------------- */
#define PARAM_TXDELAY 1
#define PARAM_PERSIST 2
#define PARAM_SLOTTIME 3
#define PARAM_TXTAIL 4
#define PARAM_FULLDUP 5
#define PARAM_HARDWARE 6
#define PARAM_RETURN 255
#define SP_SER 1
#define SP_PAR 2
#define SP_MIDI 4
/*
* ===================== port checking routines ========================
*/
enum uart { c_uart_unknown, c_uart_8250,
c_uart_16450, c_uart_16550, c_uart_16550A};
static const char *uart_str[] =
{ "unknown", "8250", "16450", "16550", "16550A" };
static enum uart check_uart(unsigned int iobase)
{
unsigned char b1,b2,b3;
enum uart u;
enum uart uart_tab[] =
{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
if (iobase <= 0 || iobase > 0x1000-SER_EXTENT)
return c_uart_unknown;
if (check_region(iobase, SER_EXTENT))
return c_uart_unknown;
b1 = inb(UART_MCR(iobase));
outb(b1 | 0x10, UART_MCR(iobase)); /* loopback mode */
b2 = inb(UART_MSR(iobase));
outb(0x1a, UART_MCR(iobase));
b3 = inb(UART_MSR(iobase)) & 0xf0;
outb(b1, UART_MCR(iobase)); /* restore old values */
outb(b2, UART_MSR(iobase));
if (b3 != 0x90)
return c_uart_unknown;
inb(UART_RBR(iobase));
inb(UART_RBR(iobase));
outb(0x01, UART_FCR(iobase)); /* enable FIFOs */
u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3];
if (u == c_uart_16450) {
outb(0x5a, UART_SCR(iobase));
b1 = inb(UART_SCR(iobase));
outb(0xa5, UART_SCR(iobase));
b2 = inb(UART_SCR(iobase));
if ((b1 != 0x5a) || (b2 != 0xa5))
u = c_uart_8250;
}
return u;
}
/* --------------------------------------------------------------------- */
static int check_midi(unsigned int iobase)
{
unsigned long timeout;
unsigned long flags;
unsigned char b;
if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT)
return 0;
if (check_region(iobase, MIDI_EXTENT))
return 0;
timeout = jiffies + (HZ / 100);
while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
if ((signed)(jiffies - timeout) > 0)
return 0;
save_flags(flags);
cli();
outb(0xff, MIDI_DATA(iobase));
b = inb(MIDI_STATUS(iobase));
restore_flags(flags);
if (!(b & MIDI_WRITE_EMPTY))
return 0;
while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
if ((signed)(jiffies - timeout) > 0)
return 0;
return 1;
}
/* --------------------------------------------------------------------- */
void sm_output_status(struct sm_state *sm)
{
int invert_dcd = 0;
int invert_ptt = 0;
int ptt = /*hdlcdrv_ptt(&sm->hdrv)*/(sm->dma.ptt_cnt > 0) ^ invert_ptt;
int dcd = (!!sm->hdrv.hdlcrx.dcd) ^ invert_dcd;
if (sm->hdrv.ptt_out.flags & SP_SER) {
outb(dcd | (ptt << 1), UART_MCR(sm->hdrv.ptt_out.seriobase));
outb(0x40 & (-ptt), UART_LCR(sm->hdrv.ptt_out.seriobase));
}
if (sm->hdrv.ptt_out.flags & SP_PAR && sm->pardev && sm->pardev->port)
parport_write_data(sm->pardev->port, ptt | (dcd << 1));
if (sm->hdrv.ptt_out.flags & SP_MIDI && hdlcdrv_ptt(&sm->hdrv))
outb(0, MIDI_DATA(sm->hdrv.ptt_out.midiiobase));
}
/* --------------------------------------------------------------------- */
static void sm_output_open(struct sm_state *sm, const char *ifname)
{
enum uart u = c_uart_unknown;
struct parport *pp = NULL;
sm->hdrv.ptt_out.flags = 0;
if (sm->hdrv.ptt_out.seriobase > 0 &&
sm->hdrv.ptt_out.seriobase <= 0x1000-SER_EXTENT &&
((u = check_uart(sm->hdrv.ptt_out.seriobase))) != c_uart_unknown) {
sm->hdrv.ptt_out.flags |= SP_SER;
request_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT, "sm ser ptt");
outb(0, UART_IER(sm->hdrv.ptt_out.seriobase));
/* 5 bits, 1 stop, no parity, no break, Div latch access */
outb(0x80, UART_LCR(sm->hdrv.ptt_out.seriobase));
outb(0, UART_DLM(sm->hdrv.ptt_out.seriobase));
outb(1, UART_DLL(sm->hdrv.ptt_out.seriobase)); /* as fast as possible */
/* LCR and MCR set by output_status */
}
sm->pardev = NULL;
if (sm->hdrv.ptt_out.pariobase > 0) {
pp = parport_enumerate();
while (pp && pp->base != sm->hdrv.ptt_out.pariobase)
pp = pp->next;
if (!pp)
printk(KERN_WARNING "%s: parport at address 0x%x not found\n", sm_drvname, sm->hdrv.ptt_out.pariobase);
else if ((~pp->modes) & (PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT))
printk(KERN_WARNING "%s: parport at address 0x%x cannot be used\n", sm_drvname, sm->hdrv.ptt_out.pariobase);
else {
sm->pardev = parport_register_device(pp, ifname, NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
if (!sm->pardev) {
pp = NULL;
printk(KERN_WARNING "%s: cannot register parport device (address 0x%x)\n", sm_drvname, sm->hdrv.ptt_out.pariobase);
} else {
if (parport_claim(sm->pardev)) {
parport_unregister_device(sm->pardev);
sm->pardev = NULL;
printk(KERN_WARNING "%s: cannot claim parport at address 0x%x\n", sm_drvname, sm->hdrv.ptt_out.pariobase);
} else
sm->hdrv.ptt_out.flags |= SP_PAR;
}
}
}
if (sm->hdrv.ptt_out.midiiobase > 0 &&
sm->hdrv.ptt_out.midiiobase <= 0x1000-MIDI_EXTENT &&
check_midi(sm->hdrv.ptt_out.midiiobase)) {
sm->hdrv.ptt_out.flags |= SP_MIDI;
request_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT,
"sm midi ptt");
}
sm_output_status(sm);
printk(KERN_INFO "%s: ptt output:", sm_drvname);
if (sm->hdrv.ptt_out.flags & SP_SER)
printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase,
uart_str[u]);
if (sm->hdrv.ptt_out.flags & SP_PAR)
printk(" parallel interface at 0x%x", sm->hdrv.ptt_out.pariobase);
if (sm->hdrv.ptt_out.flags & SP_MIDI)
printk(" mpu401 (midi) interface at 0x%x", sm->hdrv.ptt_out.midiiobase);
if (!sm->hdrv.ptt_out.flags)
printk(" none");
printk("\n");
}
/* --------------------------------------------------------------------- */
static void sm_output_close(struct sm_state *sm)
{
/* release regions used for PTT output */
sm->hdrv.hdlctx.ptt = sm->hdrv.hdlctx.calibrate = 0;
sm_output_status(sm);
if (sm->hdrv.ptt_out.flags & SP_SER)
release_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT);
if (sm->hdrv.ptt_out.flags & SP_PAR && sm->pardev) {
parport_release(sm->pardev);
parport_unregister_device(sm->pardev);
}
if (sm->hdrv.ptt_out.flags & SP_MIDI)
release_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT);
sm->hdrv.ptt_out.flags = 0;
}
/* --------------------------------------------------------------------- */
static int sm_open(struct net_device *dev);
static int sm_close(struct net_device *dev);
static int sm_ioctl(struct net_device *dev, struct ifreq *ifr,
struct hdlcdrv_ioctl *hi, int cmd);
/* --------------------------------------------------------------------- */
static const struct hdlcdrv_ops sm_ops = {
sm_drvname, sm_drvinfo, sm_open, sm_close, sm_ioctl
};
/* --------------------------------------------------------------------- */
static int sm_open(struct net_device *dev)
{
struct sm_state *sm;
int err;
if (!dev || !dev->priv ||
((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
printk(KERN_ERR "sm_open: invalid device struct\n");
return -EINVAL;
}
sm = (struct sm_state *)dev->priv;
if (!sm->mode_tx || !sm->mode_rx || !sm->hwdrv || !sm->hwdrv->open)
return -ENODEV;
sm->hdrv.par.bitrate = sm->mode_rx->bitrate;
err = sm->hwdrv->open(dev, sm);
if (err)
return err;
sm_output_open(sm, dev->name);
MOD_INC_USE_COUNT;
printk(KERN_INFO "%s: %s mode %s.%s at iobase 0x%lx irq %u dma %u dma2 %u\n",
sm_drvname, sm->hwdrv->hw_name, sm->mode_tx->name,
sm->mode_rx->name, dev->base_addr, dev->irq, dev->dma, sm->hdrv.ptt_out.dma2);
return 0;
}
/* --------------------------------------------------------------------- */
static int sm_close(struct net_device *dev)
{
struct sm_state *sm;
int err = -ENODEV;
if (!dev || !dev->priv ||
((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
printk(KERN_ERR "sm_close: invalid device struct\n");
return -EINVAL;
}
sm = (struct sm_state *)dev->priv;
if (sm->hwdrv && sm->hwdrv->close)
err = sm->hwdrv && sm->hwdrv->close(dev, sm);
sm_output_close(sm);
MOD_DEC_USE_COUNT;
printk(KERN_INFO "%s: close %s at iobase 0x%lx irq %u dma %u\n",
sm_drvname, sm->hwdrv->hw_name, dev->base_addr, dev->irq, dev->dma);
return err;
}
/* --------------------------------------------------------------------- */
static int sethw(struct net_device *dev, struct sm_state *sm, char *mode)
{
char *cp = strchr(mode, ':');
const struct hardware_info **hwp = sm_hardware_table;
if (!cp)
cp = mode;
else {
*cp++ = '\0';
while (hwp && (*hwp) && (*hwp)->hw_name && strcmp((*hwp)->hw_name, mode))
hwp++;
if (!hwp || !*hwp || !(*hwp)->hw_name)
return -EINVAL;
if ((*hwp)->loc_storage > sizeof(sm->hw)) {
printk(KERN_ERR "%s: insufficient storage for hw driver %s (%d)\n",
sm_drvname, (*hwp)->hw_name, (*hwp)->loc_storage);
return -EINVAL;
}
sm->hwdrv = *hwp;
}
if (!*cp)
return 0;
if (sm->hwdrv && sm->hwdrv->sethw)
return sm->hwdrv->sethw(dev, sm, cp);
return -EINVAL;
}
/* --------------------------------------------------------------------- */
static int sm_ioctl(struct net_device *dev, struct ifreq *ifr,
struct hdlcdrv_ioctl *hi, int cmd)
{
struct sm_state *sm;
struct sm_ioctl bi;
unsigned long flags;
unsigned int newdiagmode;
unsigned int newdiagflags;
char *cp;
const struct modem_tx_info **mtp = sm_modem_tx_table;
const struct modem_rx_info **mrp = sm_modem_rx_table;
const struct hardware_info **hwp = sm_hardware_table;
if (!dev || !dev->priv ||
((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
printk(KERN_ERR "sm_ioctl: invalid device struct\n");
return -EINVAL;
}
sm = (struct sm_state *)dev->priv;
if (cmd != SIOCDEVPRIVATE) {
if (!sm->hwdrv || !sm->hwdrv->ioctl)
return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
return -ENOIOCTLCMD;
}
switch (hi->cmd) {
default:
if (sm->hwdrv && sm->hwdrv->ioctl)
return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
return -ENOIOCTLCMD;
case HDLCDRVCTL_GETMODE:
cp = hi->data.modename;
if (sm->hwdrv && sm->hwdrv->hw_name)
cp += sprintf(cp, "%s:", sm->hwdrv->hw_name);
else
cp += sprintf(cp, "<unspec>:");
if (sm->mode_tx && sm->mode_tx->name)
cp += sprintf(cp, "%s", sm->mode_tx->name);
else
cp += sprintf(cp, "<unspec>");
if (!sm->mode_rx || !sm->mode_rx ||
strcmp(sm->mode_rx->name, sm->mode_tx->name)) {
if (sm->mode_rx && sm->mode_rx->name)
cp += sprintf(cp, ",%s", sm->mode_rx->name);
else
cp += sprintf(cp, ",<unspec>");
}
if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
return -EFAULT;
return 0;
case HDLCDRVCTL_SETMODE:
if (netif_running(dev) || !capable(CAP_NET_ADMIN))
return -EACCES;
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
return sethw(dev, sm, hi->data.modename);
case HDLCDRVCTL_MODELIST:
cp = hi->data.modename;
while (*hwp) {
if ((*hwp)->hw_name)
cp += sprintf(cp, "%s:,", (*hwp)->hw_name);
hwp++;
}
while (*mtp) {
if ((*mtp)->name)
cp += sprintf(cp, ">%s,", (*mtp)->name);
mtp++;
}
while (*mrp) {
if ((*mrp)->name)
cp += sprintf(cp, "<%s,", (*mrp)->name);
mrp++;
}
cp[-1] = '\0';
if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
return -EFAULT;
return 0;
#ifdef SM_DEBUG
case SMCTL_GETDEBUG:
if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
return -EFAULT;
bi.data.dbg.int_rate = sm->debug_vals.last_intcnt;
bi.data.dbg.mod_cycles = sm->debug_vals.mod_cyc;
bi.data.dbg.demod_cycles = sm->debug_vals.demod_cyc;
bi.data.dbg.dma_residue = sm->debug_vals.dma_residue;
sm->debug_vals.mod_cyc = sm->debug_vals.demod_cyc =
sm->debug_vals.dma_residue = 0;
if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
return -EFAULT;
return 0;
#endif /* SM_DEBUG */
case SMCTL_DIAGNOSE:
if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
return -EFAULT;
newdiagmode = bi.data.diag.mode;
newdiagflags = bi.data.diag.flags;
if (newdiagmode > SM_DIAGMODE_CONSTELLATION)
return -EINVAL;
bi.data.diag.mode = sm->diag.mode;
bi.data.diag.flags = sm->diag.flags;
bi.data.diag.samplesperbit = sm->mode_rx->sperbit;
if (sm->diag.mode != newdiagmode) {
save_flags(flags);
cli();
sm->diag.ptr = -1;
sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
sm->diag.mode = newdiagmode;
restore_flags(flags);
if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
return -EFAULT;
return 0;
}
if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF) {
if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
return -EFAULT;
return 0;
}
if (bi.data.diag.datalen > DIAGDATALEN)
bi.data.diag.datalen = DIAGDATALEN;
if (sm->diag.ptr < bi.data.diag.datalen) {
if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
return -EFAULT;
return 0;
}
if (copy_to_user(bi.data.diag.data, sm->diag.data,
bi.data.diag.datalen * sizeof(short)))
return -EFAULT;
bi.data.diag.flags |= SM_DIAGFLAG_VALID;
save_flags(flags);
cli();
sm->diag.ptr = -1;
sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
sm->diag.mode = newdiagmode;
restore_flags(flags);
if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
return -EFAULT;
return 0;
}
}
/* --------------------------------------------------------------------- */
/*
* command line settable parameters
*/
static char *mode[NR_PORTS] = { [0 ... NR_PORTS-1] = NULL };
static int iobase[NR_PORTS] = { [0 ... NR_PORTS-1] = -1 };
static int irq[NR_PORTS] = { [0 ... NR_PORTS-1] = -1 };
static int dma[NR_PORTS] = { [0 ... NR_PORTS-1] = -1 };
static int dma2[NR_PORTS] = { [0 ... NR_PORTS-1] = -1 };
static int serio[NR_PORTS] = { [0 ... NR_PORTS-1] = 0 };
static int pario[NR_PORTS] = { [0 ... NR_PORTS-1] = 0 };
static int midiio[NR_PORTS] = { [0 ... NR_PORTS-1] = 0 };
MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s");
MODULE_PARM_DESC(mode, "soundmodem operating mode; eg. sbc:afsk1200 or wss:fsk9600");
MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i");
MODULE_PARM_DESC(iobase, "soundmodem base address");
MODULE_PARM(irq, "1-" __MODULE_STRING(NR_PORTS) "i");
MODULE_PARM_DESC(irq, "soundmodem interrupt");
MODULE_PARM(dma, "1-" __MODULE_STRING(NR_PORTS) "i");
MODULE_PARM_DESC(dma, "soundmodem dma channel");
MODULE_PARM(dma2, "1-" __MODULE_STRING(NR_PORTS) "i");
MODULE_PARM_DESC(dma2, "soundmodem 2nd dma channel; full duplex only");
MODULE_PARM(serio, "1-" __MODULE_STRING(NR_PORTS) "i");
MODULE_PARM_DESC(serio, "soundmodem PTT output on serial port");
MODULE_PARM(pario, "1-" __MODULE_STRING(NR_PORTS) "i");
MODULE_PARM_DESC(pario, "soundmodem PTT output on parallel port");
MODULE_PARM(midiio, "1-" __MODULE_STRING(NR_PORTS) "i");
MODULE_PARM_DESC(midiio, "soundmodem PTT output on midi port");
MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
MODULE_DESCRIPTION("Soundcard amateur radio modem driver");
/* --------------------------------------------------------------------- */
static int __init init_soundmodem(void)
{
int i, j, found = 0;
char set_hw = 1;
struct sm_state *sm;
printk(sm_drvinfo);
/*
* register net devices
*/
for (i = 0; i < NR_PORTS; i++) {
struct net_device *dev = sm_device+i;
char ifname[IFNAMSIZ];
sprintf(ifname, "sm%d", i);
if (!mode[i])
set_hw = 0;
else {
if (!strncmp(mode[i], "sbc", 3)) {
if (iobase[i] == -1)
iobase[i] = 0x220;
if (irq[i] == -1)
irq[i] = 5;
if (dma[i] == -1)
dma[i] = 1;
} else {
if (iobase[i] == -1)
iobase[i] = 0x530;
if (irq[i] == -1)
irq[i] = 11;
if (dma[i] == -1)
dma[i] = 1;
}
}
if (!set_hw)
iobase[i] = irq[i] = 0;
j = hdlcdrv_register_hdlcdrv(dev, &sm_ops, sizeof(struct sm_state), ifname, iobase[i], irq[i], dma[i]);
if (!j) {
sm = (struct sm_state *)dev->priv;
sm->hdrv.ptt_out.dma2 = dma2[i];
sm->hdrv.ptt_out.seriobase = serio[i];
sm->hdrv.ptt_out.pariobase = pario[i];
sm->hdrv.ptt_out.midiiobase = midiio[i];
if (set_hw && sethw(dev, sm, mode[i]))
set_hw = 0;
found++;
} else {
printk(KERN_WARNING "%s: cannot register net device\n", sm_drvname);
}
}
if (!found)
return -ENXIO;
return 0;
}
static void __exit cleanup_soundmodem(void)
{
int i;
printk(KERN_INFO "sm: cleanup_module called\n");
for(i = 0; i < NR_PORTS; i++) {
struct net_device *dev = sm_device+i;
struct sm_state *sm = (struct sm_state *)dev->priv;
if (sm) {
if (sm->hdrv.magic != HDLCDRV_MAGIC)
printk(KERN_ERR "sm: invalid magic in "
"cleanup_module\n");
else
hdlcdrv_unregister_hdlcdrv(dev);
}
}
}
module_init(init_soundmodem);
module_exit(cleanup_soundmodem);
/* --------------------------------------------------------------------- */
#ifndef MODULE
/*
* format: soundmodem=io,irq,dma[,dma2[,serio[,pario]]],mode
* mode: hw:modem
* hw: sbc, wss, wssfdx
* modem: afsk1200, fsk9600
*/
static int __init sm_setup(char *str)
{
static unsigned nr_dev;
int ints[8];
if (nr_dev >= NR_PORTS)
return 0;
str = get_options(str, 8, ints);
mode[nr_dev] = str;
if (ints[0] >= 1)
iobase[nr_dev] = ints[1];
if (ints[0] >= 2)
irq[nr_dev] = ints[2];
if (ints[0] >= 3)
dma[nr_dev] = ints[3];
if (ints[0] >= 4)
dma2[nr_dev] = ints[4];
if (ints[0] >= 5)
serio[nr_dev] = ints[5];
if (ints[0] >= 6)
pario[nr_dev] = ints[6];
if (ints[0] >= 7)
midiio[nr_dev] = ints[7];
nr_dev++;
return 1;
}
__setup("soundmodem=", sm_setup);
#endif /* MODULE */
/* --------------------------------------------------------------------- */
/*****************************************************************************/
/*
* sm.h -- soundcard radio modem driver internal header.
*
* Copyright (C) 1996-1999 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Please note that the GPL allows you to use the driver, NOT the radio.
* In order to use the radio, you need a license from the communications
* authority of your country.
*
*/
#ifndef _SM_H
#define _SM_H
/* ---------------------------------------------------------------------- */
#include <linux/hdlcdrv.h>
#include <linux/soundmodem.h>
#include <asm/processor.h>
#include <linux/bitops.h>
#include <linux/parport.h>
#define SM_DEBUG
/* ---------------------------------------------------------------------- */
/*
* Information that need to be kept for each board.
*/
struct sm_state {
struct hdlcdrv_state hdrv;
const struct modem_tx_info *mode_tx;
const struct modem_rx_info *mode_rx;
const struct hardware_info *hwdrv;
struct pardevice *pardev;
/*
* Hardware (soundcard) access routines state
*/
struct {
void *ibuf;
unsigned int ifragsz;
unsigned int ifragptr;
unsigned int i16bit;
void *obuf;
unsigned int ofragsz;
unsigned int ofragptr;
unsigned int o16bit;
int ptt_cnt;
} dma;
union {
long hw[32/sizeof(long)];
} hw;
/*
* state of the modem code
*/
union {
long m[48/sizeof(long)];
} m;
union {
long d[256/sizeof(long)];
} d;
#define DIAGDATALEN 64
struct diag_data {
unsigned int mode;
unsigned int flags;
volatile int ptr;
short data[DIAGDATALEN];
} diag;
#ifdef SM_DEBUG
struct debug_vals {
unsigned long last_jiffies;
unsigned cur_intcnt;
unsigned last_intcnt;
unsigned mod_cyc;
unsigned demod_cyc;
unsigned dma_residue;
} debug_vals;
#endif /* SM_DEBUG */
};
/* ---------------------------------------------------------------------- */
/*
* Mode definition structure
*/
struct modem_tx_info {
const char *name;
unsigned int loc_storage;
int srate;
int bitrate;
void (*modulator_u8)(struct sm_state *, unsigned char *, unsigned int);
void (*modulator_s16)(struct sm_state *, short *, unsigned int);
void (*init)(struct sm_state *);
};
struct modem_rx_info {
const char *name;
unsigned int loc_storage;
int srate;
int bitrate;
unsigned int overlap;
unsigned int sperbit;
void (*demodulator_u8)(struct sm_state *, const unsigned char *, unsigned int);
void (*demodulator_s16)(struct sm_state *, const short *, unsigned int);
void (*init)(struct sm_state *);
};
/* ---------------------------------------------------------------------- */
/*
* Soundcard driver definition structure
*/
struct hardware_info {
char *hw_name; /* used for request_{region,irq,dma} */
unsigned int loc_storage;
/*
* mode specific open/close
*/
int (*open)(struct net_device *, struct sm_state *);
int (*close)(struct net_device *, struct sm_state *);
int (*ioctl)(struct net_device *, struct sm_state *, struct ifreq *,
struct hdlcdrv_ioctl *, int);
int (*sethw)(struct net_device *, struct sm_state *, char *);
};
/* --------------------------------------------------------------------- */
extern const char sm_drvname[];
extern const char sm_drvinfo[];
/* --------------------------------------------------------------------- */
/*
* ===================== diagnostics stuff ===============================
*/
static inline void diag_trigger(struct sm_state *sm)
{
if (sm->diag.ptr < 0)
if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd)
sm->diag.ptr = 0;
}
/* --------------------------------------------------------------------- */
#define SHRT_MAX ((short)(((unsigned short)(~0U))>>1))
#define SHRT_MIN (-SHRT_MAX-1)
static inline void diag_add(struct sm_state *sm, int valinp, int valdemod)
{
int val;
if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
sm->diag.mode != SM_DIAGMODE_DEMOD) ||
sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
return;
val = (sm->diag.mode == SM_DIAGMODE_DEMOD) ? valdemod : valinp;
/* clip */
if (val > SHRT_MAX)
val = SHRT_MAX;
if (val < SHRT_MIN)
val = SHRT_MIN;
sm->diag.data[sm->diag.ptr++] = val;
}
/* --------------------------------------------------------------------- */
static inline void diag_add_one(struct sm_state *sm, int val)
{
if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
sm->diag.mode != SM_DIAGMODE_DEMOD) ||
sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
return;
/* clip */
if (val > SHRT_MAX)
val = SHRT_MAX;
if (val < SHRT_MIN)
val = SHRT_MIN;
sm->diag.data[sm->diag.ptr++] = val;
}
/* --------------------------------------------------------------------- */
static inline void diag_add_constellation(struct sm_state *sm, int vali, int valq)
{
if ((sm->diag.mode != SM_DIAGMODE_CONSTELLATION) ||
sm->diag.ptr >= DIAGDATALEN-1 || sm->diag.ptr < 0)
return;
/* clip */
if (vali > SHRT_MAX)
vali = SHRT_MAX;
if (vali < SHRT_MIN)
vali = SHRT_MIN;
if (valq > SHRT_MAX)
valq = SHRT_MAX;
if (valq < SHRT_MIN)
valq = SHRT_MIN;
sm->diag.data[sm->diag.ptr++] = vali;
sm->diag.data[sm->diag.ptr++] = valq;
}
/* --------------------------------------------------------------------- */
/*
* ===================== utility functions ===============================
*/
#if 0
static inline unsigned int hweight32(unsigned int w)
__attribute__ ((unused));
static inline unsigned int hweight16(unsigned short w)
__attribute__ ((unused));
static inline unsigned int hweight8(unsigned char w)
__attribute__ ((unused));
static inline unsigned int hweight32(unsigned int w)
{
unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
}
static inline unsigned int hweight16(unsigned short w)
{
unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
res = (res & 0x3333) + ((res >> 2) & 0x3333);
res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
return (res & 0x00FF) + ((res >> 8) & 0x00FF);
}
static inline unsigned int hweight8(unsigned char w)
{
unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
res = (res & 0x33) + ((res >> 2) & 0x33);
return (res & 0x0F) + ((res >> 4) & 0x0F);
}
#endif
static inline unsigned int gcd(unsigned int x, unsigned int y)
__attribute__ ((unused));
static inline unsigned int lcm(unsigned int x, unsigned int y)
__attribute__ ((unused));
static inline unsigned int gcd(unsigned int x, unsigned int y)
{
for (;;) {
if (!x)
return y;
if (!y)
return x;
if (x > y)
x %= y;
else
y %= x;
}
}
static inline unsigned int lcm(unsigned int x, unsigned int y)
{
return x * y / gcd(x, y);
}
/* --------------------------------------------------------------------- */
/*
* ===================== profiling =======================================
*/
#ifdef __i386__
#include <asm/msr.h>
/*
* only do 32bit cycle counter arithmetic; we hope we won't overflow.
* in fact, overflowing modems would require over 2THz CPU clock speeds :-)
*/
#define time_exec(var,cmd) \
({ \
if (cpu_has_tsc) { \
unsigned int cnt1, cnt2; \
rdtscl(cnt1); \
cmd; \
rdtscl(cnt2); \
var = cnt2-cnt1; \
} else { \
cmd; \
} \
})
#else /* __i386__ */
#define time_exec(var,cmd) cmd
#endif /* __i386__ */
/* --------------------------------------------------------------------- */
extern const struct modem_tx_info sm_afsk1200_tx;
extern const struct modem_tx_info sm_afsk2400_7_tx;
extern const struct modem_tx_info sm_afsk2400_8_tx;
extern const struct modem_tx_info sm_afsk2666_tx;
extern const struct modem_tx_info sm_psk4800_tx;
extern const struct modem_tx_info sm_hapn4800_8_tx;
extern const struct modem_tx_info sm_hapn4800_10_tx;
extern const struct modem_tx_info sm_hapn4800_pm8_tx;
extern const struct modem_tx_info sm_hapn4800_pm10_tx;
extern const struct modem_tx_info sm_fsk9600_4_tx;
extern const struct modem_tx_info sm_fsk9600_5_tx;
extern const struct modem_rx_info sm_afsk1200_rx;
extern const struct modem_rx_info sm_afsk2400_7_rx;
extern const struct modem_rx_info sm_afsk2400_8_rx;
extern const struct modem_rx_info sm_afsk2666_rx;
extern const struct modem_rx_info sm_psk4800_rx;
extern const struct modem_rx_info sm_hapn4800_8_rx;
extern const struct modem_rx_info sm_hapn4800_10_rx;
extern const struct modem_rx_info sm_hapn4800_pm8_rx;
extern const struct modem_rx_info sm_hapn4800_pm10_rx;
extern const struct modem_rx_info sm_fsk9600_4_rx;
extern const struct modem_rx_info sm_fsk9600_5_rx;
extern const struct hardware_info sm_hw_sbc;
extern const struct hardware_info sm_hw_sbcfdx;
extern const struct hardware_info sm_hw_wss;
extern const struct hardware_info sm_hw_wssfdx;
extern const struct modem_tx_info *sm_modem_tx_table[];
extern const struct modem_rx_info *sm_modem_rx_table[];
extern const struct hardware_info *sm_hardware_table[];
/* --------------------------------------------------------------------- */
void sm_output_status(struct sm_state *sm);
/*void sm_output_open(struct sm_state *sm);*/
/*void sm_output_close(struct sm_state *sm);*/
/* --------------------------------------------------------------------- */
extern void inline sm_int_freq(struct sm_state *sm)
{
#ifdef SM_DEBUG
unsigned long cur_jiffies = jiffies;
/*
* measure the interrupt frequency
*/
sm->debug_vals.cur_intcnt++;
if ((cur_jiffies - sm->debug_vals.last_jiffies) >= HZ) {
sm->debug_vals.last_jiffies = cur_jiffies;
sm->debug_vals.last_intcnt = sm->debug_vals.cur_intcnt;
sm->debug_vals.cur_intcnt = 0;
}
#endif /* SM_DEBUG */
}
/* --------------------------------------------------------------------- */
#endif /* _SM_H */
/*****************************************************************************/
/*
* sm_afsk1200.c -- soundcard radio modem driver, 1200 baud AFSK modem
*
* Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Please note that the GPL allows you to use the driver, NOT the radio.
* In order to use the radio, you need a license from the communications
* authority of your country.
*
*/
#include "sm.h"
#include "sm_tbl_afsk1200.h"
/* --------------------------------------------------------------------- */
struct demod_state_afsk12 {
unsigned int shreg;
unsigned int bit_pll;
unsigned char last_sample;
unsigned int dcd_shreg;
int dcd_sum0, dcd_sum1, dcd_sum2;
unsigned int dcd_time;
unsigned char last_rxbit;
};
struct mod_state_afsk12 {
unsigned int shreg;
unsigned char tx_bit;
unsigned int bit_pll;
unsigned int dds_inc;
unsigned int txphase;
};
/* --------------------------------------------------------------------- */
static const int dds_inc[2] = {
AFSK12_TX_FREQ_LO*0x10000/AFSK12_SAMPLE_RATE,
AFSK12_TX_FREQ_HI*0x10000/AFSK12_SAMPLE_RATE
};
static void modulator_1200_u8(struct sm_state *sm, unsigned char *buf,
unsigned int buflen)
{
struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m);
for (; buflen > 0; buflen--) {
if (!((st->txphase++) & 7)) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
st->shreg >>= 1;
}
st->dds_inc = dds_inc[st->tx_bit & 1];
*buf++ = OFFSCOS(st->bit_pll);
st->bit_pll += st->dds_inc;
}
}
/* --------------------------------------------------------------------- */
static void modulator_1200_s16(struct sm_state *sm, short *buf, unsigned int buflen)
{
struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m);
for (; buflen > 0; buflen--) {
if (!((st->txphase++) & 7)) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
st->shreg >>= 1;
}
st->dds_inc = dds_inc[st->tx_bit & 1];
*buf++ = COS(st->bit_pll);
st->bit_pll += st->dds_inc;
}
}
/* --------------------------------------------------------------------- */
static __inline__ int convolution8_u8(const unsigned char *st, const int *coeff, int csum)
{
int sum = -0x80 * csum;
sum += (st[0] * coeff[0]);
sum += (st[-1] * coeff[1]);
sum += (st[-2] * coeff[2]);
sum += (st[-3] * coeff[3]);
sum += (st[-4] * coeff[4]);
sum += (st[-5] * coeff[5]);
sum += (st[-6] * coeff[6]);
sum += (st[-7] * coeff[7]);
sum >>= 7;
return sum * sum;
}
static __inline__ int convolution8_s16(const short *st, const int *coeff, int csum)
{
int sum = 0;
sum += (st[0] * coeff[0]);
sum += (st[-1] * coeff[1]);
sum += (st[-2] * coeff[2]);
sum += (st[-3] * coeff[3]);
sum += (st[-4] * coeff[4]);
sum += (st[-5] * coeff[5]);
sum += (st[-6] * coeff[6]);
sum += (st[-7] * coeff[7]);
sum >>= 15;
return sum * sum;
}
static __inline__ int do_filter_1200_u8(const unsigned char *buf)
{
int sum = convolution8_u8(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I);
sum += convolution8_u8(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q);
sum -= convolution8_u8(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I);
sum -= convolution8_u8(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q);
return sum;
}
static __inline__ int do_filter_1200_s16(const short *buf)
{
int sum = convolution8_s16(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I);
sum += convolution8_s16(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q);
sum -= convolution8_s16(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I);
sum -= convolution8_s16(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q);
return sum;
}
/* --------------------------------------------------------------------- */
static const int pll_corr[2] = { -0x1000, 0x1000 };
static void demodulator_1200_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
{
struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
int j;
int sum;
unsigned char newsample;
for (; buflen > 0; buflen--, buf++) {
sum = do_filter_1200_u8(buf);
st->dcd_shreg <<= 1;
st->bit_pll += 0x2000;
newsample = (sum > 0);
if (st->last_sample ^ newsample) {
st->last_sample = newsample;
st->dcd_shreg |= 1;
st->bit_pll += pll_corr
[st->bit_pll < 0x9000];
j = 4 * hweight8(st->dcd_shreg & 0x38)
- hweight16(st->dcd_shreg & 0x7c0);
st->dcd_sum0 += j;
}
hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 120;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffff;
st->shreg >>= 1;
st->shreg |= (!(st->last_rxbit ^
st->last_sample)) << 16;
st->last_rxbit = st->last_sample;
diag_trigger(sm);
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
}
diag_add(sm, (((int)*buf)-0x80) << 8, sum);
}
}
/* --------------------------------------------------------------------- */
static void demodulator_1200_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
{
struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
int j;
int sum;
unsigned char newsample;
for (; buflen > 0; buflen--, buf++) {
sum = do_filter_1200_s16(buf);
st->dcd_shreg <<= 1;
st->bit_pll += 0x2000;
newsample = (sum > 0);
if (st->last_sample ^ newsample) {
st->last_sample = newsample;
st->dcd_shreg |= 1;
st->bit_pll += pll_corr
[st->bit_pll < 0x9000];
j = 4 * hweight8(st->dcd_shreg & 0x38)
- hweight16(st->dcd_shreg & 0x7c0);
st->dcd_sum0 += j;
}
hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 120;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffff;
st->shreg >>= 1;
st->shreg |= (!(st->last_rxbit ^
st->last_sample)) << 16;
st->last_rxbit = st->last_sample;
diag_trigger(sm);
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
}
diag_add(sm, *buf, sum);
}
}
/* --------------------------------------------------------------------- */
static void demod_init_1200(struct sm_state *sm)
{
struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
st->dcd_time = 120;
st->dcd_sum0 = 2;
}
/* --------------------------------------------------------------------- */
const struct modem_tx_info sm_afsk1200_tx = {
"afsk1200", sizeof(struct mod_state_afsk12),
AFSK12_SAMPLE_RATE, 1200, modulator_1200_u8, modulator_1200_s16, NULL
};
const struct modem_rx_info sm_afsk1200_rx = {
"afsk1200", sizeof(struct demod_state_afsk12),
AFSK12_SAMPLE_RATE, 1200, 8, AFSK12_SAMPLE_RATE/1200,
demodulator_1200_u8, demodulator_1200_s16, demod_init_1200
};
/* --------------------------------------------------------------------- */
/*****************************************************************************/
/*
* sm_afsk2400_7.c -- soundcard radio modem driver, 2400 baud AFSK modem
*
* Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Please note that the GPL allows you to use the driver, NOT the radio.
* In order to use the radio, you need a license from the communications
* authority of your country.
*
*/
/*
* This driver is intended to be compatible with TCM3105 modems
* overclocked to 7.3728MHz. The mark and space frequencies therefore
* lie at 3658 and 1996 Hz.
* Note that I do _not_ recommend the building of such links, I provide
* this only for the users who live in the coverage area of such
* a "legacy" link.
*/
#include "sm.h"
#include "sm_tbl_afsk2400_7.h"
/* --------------------------------------------------------------------- */
struct demod_state_afsk24 {
unsigned int shreg;
unsigned int bit_pll;
unsigned char last_sample;
unsigned int dcd_shreg;
int dcd_sum0, dcd_sum1, dcd_sum2;
unsigned int dcd_time;
unsigned char last_rxbit;
};
struct mod_state_afsk24 {
unsigned int shreg;
unsigned char tx_bit;
unsigned int bit_pll;
unsigned int tx_seq;
unsigned int phinc;
};
/* --------------------------------------------------------------------- */
static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE,
AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE };
static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
{
struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
for (; buflen > 0; buflen--, buf++) {
if (st->tx_seq < 0x5555) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
st->shreg >>= 1;
st->phinc = dds_inc[st->tx_bit & 1];
}
st->tx_seq += 0x5555;
st->tx_seq &= 0xffff;
*buf = OFFSCOS(st->bit_pll);
st->bit_pll += st->phinc;
}
}
/* --------------------------------------------------------------------- */
static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen)
{
struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
for (; buflen > 0; buflen--, buf++) {
if (st->tx_seq < 0x5555) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
st->shreg >>= 1;
st->phinc = dds_inc[st->tx_bit & 1];
}
st->tx_seq += 0x5555;
st->tx_seq &= 0xffff;
*buf = COS(st->bit_pll);
st->bit_pll += st->phinc;
}
}
/* --------------------------------------------------------------------- */
static __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum)
{
int sum = -0x80 * csum;
sum += (st[0] * coeff[0]);
sum += (st[-1] * coeff[1]);
sum += (st[-2] * coeff[2]);
sum += (st[-3] * coeff[3]);
sum += (st[-4] * coeff[4]);
sum += (st[-5] * coeff[5]);
sum += (st[-6] * coeff[6]);
sum += (st[-7] * coeff[7]);
sum += (st[-8] * coeff[8]);
sum += (st[-9] * coeff[9]);
sum += (st[-10] * coeff[10]);
sum += (st[-11] * coeff[11]);
sum += (st[-12] * coeff[12]);
sum += (st[-13] * coeff[13]);
sum >>= 7;
return sum * sum;
}
static __inline__ int convolution14_s16(const short *st, const int *coeff, int csum)
{
int sum = 0;
sum += (st[0] * coeff[0]);
sum += (st[-1] * coeff[1]);
sum += (st[-2] * coeff[2]);
sum += (st[-3] * coeff[3]);
sum += (st[-4] * coeff[4]);
sum += (st[-5] * coeff[5]);
sum += (st[-6] * coeff[6]);
sum += (st[-7] * coeff[7]);
sum += (st[-8] * coeff[8]);
sum += (st[-9] * coeff[9]);
sum += (st[-10] * coeff[10]);
sum += (st[-11] * coeff[11]);
sum += (st[-12] * coeff[12]);
sum += (st[-13] * coeff[13]);
sum >>= 15;
return sum * sum;
}
static __inline__ int do_filter_2400_u8(const unsigned char *buf)
{
int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
return sum;
}
static __inline__ int do_filter_2400_s16(const short *buf)
{
int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
return sum;
}
/* --------------------------------------------------------------------- */
static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
{
struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
int j;
int sum;
unsigned char newsample;
for (; buflen > 0; buflen--, buf++) {
sum = do_filter_2400_u8(buf);
st->dcd_shreg <<= 1;
st->bit_pll += AFSK24_BITPLL_INC;
newsample = (sum > 0);
if (st->last_sample ^ newsample) {
st->last_sample = newsample;
st->dcd_shreg |= 1;
if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
st->bit_pll += AFSK24_BITPLL_INC/2;
else
st->bit_pll -= AFSK24_BITPLL_INC/2;
j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
- hweight16(st->dcd_shreg & 0x1e0);
st->dcd_sum0 += j;
}
hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 120;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffff;
st->shreg >>= 1;
st->shreg |= (!(st->last_rxbit ^
st->last_sample)) << 16;
st->last_rxbit = st->last_sample;
diag_trigger(sm);
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
}
diag_add(sm, (((int)*buf)-0x80) << 8, sum);
}
}
/* --------------------------------------------------------------------- */
static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
{
struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
int j;
int sum;
unsigned char newsample;
for (; buflen > 0; buflen--, buf++) {
sum = do_filter_2400_s16(buf);
st->dcd_shreg <<= 1;
st->bit_pll += AFSK24_BITPLL_INC;
newsample = (sum > 0);
if (st->last_sample ^ newsample) {
st->last_sample = newsample;
st->dcd_shreg |= 1;
if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
st->bit_pll += AFSK24_BITPLL_INC/2;
else
st->bit_pll -= AFSK24_BITPLL_INC/2;
j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
- hweight16(st->dcd_shreg & 0x1e0);
st->dcd_sum0 += j;
}
hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 120;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffff;
st->shreg >>= 1;
st->shreg |= (!(st->last_rxbit ^
st->last_sample)) << 16;
st->last_rxbit = st->last_sample;
diag_trigger(sm);
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
}
diag_add(sm, *buf, sum);
}
}
/* --------------------------------------------------------------------- */
static void demod_init_2400(struct sm_state *sm)
{
struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
st->dcd_time = 120;
st->dcd_sum0 = 2;
}
/* --------------------------------------------------------------------- */
const struct modem_tx_info sm_afsk2400_7_tx = {
"afsk2400_7", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400,
modulator_2400_u8, modulator_2400_s16, NULL
};
const struct modem_rx_info sm_afsk2400_7_rx = {
"afsk2400_7", sizeof(struct demod_state_afsk24),
AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400,
demodulator_2400_u8, demodulator_2400_s16, demod_init_2400
};
/* --------------------------------------------------------------------- */
/*****************************************************************************/
/*
* sm_afsk2400_8.c -- soundcard radio modem driver, 2400 baud AFSK modem
*
* Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Please note that the GPL allows you to use the driver, NOT the radio.
* In order to use the radio, you need a license from the communications
* authority of your country.
*
*/
/*
* This driver is intended to be compatible with TCM3105 modems
* overclocked to 8MHz. The mark and space frequencies therefore
* lie at 3970 and 2165 Hz.
* Note that I do _not_ recommend the building of such links, I provide
* this only for the users who live in the coverage area of such
* a "legacy" link.
*/
#include "sm.h"
#include "sm_tbl_afsk2400_8.h"
/* --------------------------------------------------------------------- */
struct demod_state_afsk24 {
unsigned int shreg;
unsigned int bit_pll;
unsigned char last_sample;
unsigned int dcd_shreg;
int dcd_sum0, dcd_sum1, dcd_sum2;
unsigned int dcd_time;
unsigned char last_rxbit;
};
struct mod_state_afsk24 {
unsigned int shreg;
unsigned char tx_bit;
unsigned int bit_pll;
unsigned int tx_seq;
unsigned int phinc;
};
/* --------------------------------------------------------------------- */
static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE,
AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE };
static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
{
struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
for (; buflen > 0; buflen--, buf++) {
if (st->tx_seq < 0x5555) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
st->shreg >>= 1;
st->phinc = dds_inc[st->tx_bit & 1];
}
st->tx_seq += 0x5555;
st->tx_seq &= 0xffff;
*buf = OFFSCOS(st->bit_pll);
st->bit_pll += st->phinc;
}
}
/* --------------------------------------------------------------------- */
static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen)
{
struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
for (; buflen > 0; buflen--, buf++) {
if (st->tx_seq < 0x5555) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
st->shreg >>= 1;
st->phinc = dds_inc[st->tx_bit & 1];
}
st->tx_seq += 0x5555;
st->tx_seq &= 0xffff;
*buf = COS(st->bit_pll);
st->bit_pll += st->phinc;
}
}
/* --------------------------------------------------------------------- */
static __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum)
{
int sum = -0x80 * csum;
sum += (st[0] * coeff[0]);
sum += (st[-1] * coeff[1]);
sum += (st[-2] * coeff[2]);
sum += (st[-3] * coeff[3]);
sum += (st[-4] * coeff[4]);
sum += (st[-5] * coeff[5]);
sum += (st[-6] * coeff[6]);
sum += (st[-7] * coeff[7]);
sum += (st[-8] * coeff[8]);
sum += (st[-9] * coeff[9]);
sum += (st[-10] * coeff[10]);
sum += (st[-11] * coeff[11]);
sum += (st[-12] * coeff[12]);
sum += (st[-13] * coeff[13]);
sum >>= 7;
return sum * sum;
}
static __inline__ int convolution14_s16(const short *st, const int *coeff, int csum)
{
int sum = 0;
sum += (st[0] * coeff[0]);
sum += (st[-1] * coeff[1]);
sum += (st[-2] * coeff[2]);
sum += (st[-3] * coeff[3]);
sum += (st[-4] * coeff[4]);
sum += (st[-5] * coeff[5]);
sum += (st[-6] * coeff[6]);
sum += (st[-7] * coeff[7]);
sum += (st[-8] * coeff[8]);
sum += (st[-9] * coeff[9]);
sum += (st[-10] * coeff[10]);
sum += (st[-11] * coeff[11]);
sum += (st[-12] * coeff[12]);
sum += (st[-13] * coeff[13]);
sum >>= 15;
return sum * sum;
}
static __inline__ int do_filter_2400_u8(const unsigned char *buf)
{
int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
return sum;
}
static __inline__ int do_filter_2400_s16(const short *buf)
{
int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
return sum;
}
/* --------------------------------------------------------------------- */
static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
{
struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
int j;
int sum;
unsigned char newsample;
for (; buflen > 0; buflen--, buf++) {
sum = do_filter_2400_u8(buf);
st->dcd_shreg <<= 1;
st->bit_pll += AFSK24_BITPLL_INC;
newsample = (sum > 0);
if (st->last_sample ^ newsample) {
st->last_sample = newsample;
st->dcd_shreg |= 1;
if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
st->bit_pll += AFSK24_BITPLL_INC/2;
else
st->bit_pll -= AFSK24_BITPLL_INC/2;
j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
- hweight16(st->dcd_shreg & 0x1e0);
st->dcd_sum0 += j;
}
hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 120;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffff;
st->shreg >>= 1;
st->shreg |= (!(st->last_rxbit ^
st->last_sample)) << 16;
st->last_rxbit = st->last_sample;
diag_trigger(sm);
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
}
diag_add(sm, (((int)*buf)-0x80) << 8, sum);
}
}
/* --------------------------------------------------------------------- */
static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
{
struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
int j;
int sum;
unsigned char newsample;
for (; buflen > 0; buflen--, buf++) {
sum = do_filter_2400_s16(buf);
st->dcd_shreg <<= 1;
st->bit_pll += AFSK24_BITPLL_INC;
newsample = (sum > 0);
if (st->last_sample ^ newsample) {
st->last_sample = newsample;
st->dcd_shreg |= 1;
if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
st->bit_pll += AFSK24_BITPLL_INC/2;
else
st->bit_pll -= AFSK24_BITPLL_INC/2;
j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
- hweight16(st->dcd_shreg & 0x1e0);
st->dcd_sum0 += j;
}
hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 120;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffff;
st->shreg >>= 1;
st->shreg |= (!(st->last_rxbit ^
st->last_sample)) << 16;
st->last_rxbit = st->last_sample;
diag_trigger(sm);
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
}
diag_add(sm, *buf, sum);
}
}
/* --------------------------------------------------------------------- */
static void demod_init_2400(struct sm_state *sm)
{
struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
st->dcd_time = 120;
st->dcd_sum0 = 2;
}
/* --------------------------------------------------------------------- */
const struct modem_tx_info sm_afsk2400_8_tx = {
"afsk2400_8", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400,
modulator_2400_u8, modulator_2400_s16, NULL
};
const struct modem_rx_info sm_afsk2400_8_rx = {
"afsk2400_8", sizeof(struct demod_state_afsk24),
AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400,
demodulator_2400_u8, demodulator_2400_s16, demod_init_2400
};
/* --------------------------------------------------------------------- */
/*****************************************************************************/
/*
* sm_afsk2666.c -- soundcard radio modem driver, 2666 baud AFSK modem
*
* Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Please note that the GPL allows you to use the driver, NOT the radio.
* In order to use the radio, you need a license from the communications
* authority of your country.
*
*/
#include "sm.h"
#include "sm_tbl_afsk2666.h"
/* --------------------------------------------------------------------- */
struct demod_state_afsk26 {
unsigned int shreg;
unsigned long descram;
int dem_sum[8];
int dem_sum_mean;
int dem_cnt;
unsigned int bit_pll;
unsigned char last_sample;
unsigned int dcd_shreg;
int dcd_sum0, dcd_sum1, dcd_sum2;
unsigned int dcd_time;
};
struct mod_state_afsk26 {
unsigned int shreg;
unsigned long scram;
unsigned int bit_pll;
unsigned int phinc;
unsigned int tx_seq;
};
/* --------------------------------------------------------------------- */
#define DESCRAM_TAP1 0x20000
#define DESCRAM_TAP2 0x01000
#define DESCRAM_TAP3 0x00001
#define DESCRAM_TAPSH1 17
#define DESCRAM_TAPSH2 12
#define DESCRAM_TAPSH3 0
#define SCRAM_TAP1 0x20000 /* X^17 */
#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
/* --------------------------------------------------------------------- */
static void modulator_2666_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
{
struct mod_state_afsk26 *st = (struct mod_state_afsk26 *)(&sm->m);
for (; buflen > 0; buflen--, buf++) {
if (!st->tx_seq++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->scram = ((st->scram << 1) | (st->scram & 1));
st->scram ^= (!(st->shreg & 1));
st->shreg >>= 1;
if (st->scram & (SCRAM_TAP1 << 1))
st->scram ^= SCRAM_TAPN << 1;
st->phinc = afsk26_carfreq[!(st->scram & (SCRAM_TAP1 << 2))];
}
if (st->tx_seq >= 6)
st->tx_seq = 0;
*buf = OFFSCOS(st->bit_pll);
st->bit_pll += st->phinc;
}
}
/* --------------------------------------------------------------------- */
static void modulator_2666_s16(struct sm_state *sm, short *buf, unsigned int buflen)
{
struct mod_state_afsk26 *st = (struct mod_state_afsk26 *)(&sm->m);
for (; buflen > 0; buflen--, buf++) {
if (!st->tx_seq++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->scram = ((st->scram << 1) | (st->scram & 1));
st->scram ^= (!(st->shreg & 1));
st->shreg >>= 1;
if (st->scram & (SCRAM_TAP1 << 1))
st->scram ^= SCRAM_TAPN << 1;
st->phinc = afsk26_carfreq[!(st->scram & (SCRAM_TAP1 << 2))];
}
if (st->tx_seq >= 6)
st->tx_seq = 0;
*buf = COS(st->bit_pll);
st->bit_pll += st->phinc;
}
}
/* --------------------------------------------------------------------- */
static __inline__ int convolution12_u8(const unsigned char *st, const int *coeff, int csum)
{
int sum = -0x80 * csum;
sum += (st[0] * coeff[0]);
sum += (st[-1] * coeff[1]);
sum += (st[-2] * coeff[2]);
sum += (st[-3] * coeff[3]);
sum += (st[-4] * coeff[4]);
sum += (st[-5] * coeff[5]);
sum += (st[-6] * coeff[6]);
sum += (st[-7] * coeff[7]);
sum += (st[-8] * coeff[8]);
sum += (st[-9] * coeff[9]);
sum += (st[-10] * coeff[10]);
sum += (st[-11] * coeff[11]);
return sum;
}
static __inline__ int convolution12_s16(const short *st, const int *coeff, int csum)
{
int sum = 0;
sum += (st[0] * coeff[0]);
sum += (st[-1] * coeff[1]);
sum += (st[-2] * coeff[2]);
sum += (st[-3] * coeff[3]);
sum += (st[-4] * coeff[4]);
sum += (st[-5] * coeff[5]);
sum += (st[-6] * coeff[6]);
sum += (st[-7] * coeff[7]);
sum += (st[-8] * coeff[8]);
sum += (st[-9] * coeff[9]);
sum += (st[-10] * coeff[10]);
sum += (st[-11] * coeff[11]);
sum >>= 8;
return sum;
}
/* ---------------------------------------------------------------------- */
#if 0
static int binexp(unsigned int i)
{
int ret = 31;
if (!i)
return 0;
if (i < 0x10000LU) {
i <<= 16;
ret -= 16;
}
if (i < 0x1000000LU) {
i <<= 8;
ret -= 8;
}
if (i < 0x10000000LU) {
i <<= 4;
ret -= 4;
}
if (i < 0x40000000LU) {
i <<= 2;
ret -= 2;
}
if (i < 0x80000000LU)
ret -= 1;
return ret;
}
static const sqrt_tab[16] = {
00000, 16384, 23170, 28378, 32768, 36636, 40132, 43348,
46341, 49152, 51811, 54340, 56756, 59073, 61303, 63455
};
static unsigned int int_sqrt_approx(unsigned int i)
{
unsigned int j;
if (i < 16)
return sqrt_tab[i] >> 14;
j = binexp(i) >> 1;
i >>= (j * 2 - 2);
return (sqrt_tab[i & 0xf] << j) >> 15;
}
#endif
/* --------------------------------------------------------------------- */
extern unsigned int est_pwr(int i, int q)
{
unsigned int ui = abs(i);
unsigned int uq = abs(q);
if (uq > ui) {
unsigned int tmp;
tmp = ui;
ui = uq;
uq = tmp;
}
if (uq > (ui >> 1))
return 7*(ui>>3) + 9*(uq>>4);
else
return ui + (uq>>2);
}
/* --------------------------------------------------------------------- */
static void demod_one_sample(struct sm_state *sm, struct demod_state_afsk26 *st, int curval,
int loi, int loq, int hii, int hiq)
{
static const int pll_corr[2] = { -0xa00, 0xa00 };
unsigned char curbit;
unsigned int descx;
int val;
/*
* estimate power
*/
val = est_pwr(hii, hiq) - est_pwr(loi, loq);
/*
* estimate center value
*/
st->dem_sum[0] += val >> 8;
if ((++st->dem_cnt) >= 256) {
st->dem_cnt = 0;
st->dem_sum_mean = (st->dem_sum[0]+st->dem_sum[1]+
st->dem_sum[2]+st->dem_sum[3]+
st->dem_sum[4]+st->dem_sum[5]+
st->dem_sum[6]+st->dem_sum[7]) >> 3;
memmove(st->dem_sum+1, st->dem_sum,
sizeof(st->dem_sum)-sizeof(st->dem_sum[0]));
st->dem_sum[0] = 0;
}
/*
* decision and bit clock regen
*/
val -= st->dem_sum_mean;
diag_add(sm, curval, val);
st->dcd_shreg <<= 1;
st->bit_pll += 0x1555;
curbit = (val > 0);
if (st->last_sample ^ curbit) {
st->dcd_shreg |= 1;
st->bit_pll += pll_corr[st->bit_pll < (0x8000+0x1555)];
st->dcd_sum0 += 4*hweight8(st->dcd_shreg & 0x1e) -
hweight16(st->dcd_shreg & 0xfe00);
}
st->last_sample = curbit;
hdlcdrv_channelbit(&sm->hdrv, curbit);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 400;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffffu;
st->descram = (st->descram << 1) | curbit;
descx = st->descram ^ (st->descram >> 1);
descx ^= ((descx >> DESCRAM_TAPSH1) ^
(descx >> DESCRAM_TAPSH2));
st->shreg >>= 1;
st->shreg |= (!(descx & 1)) << 16;
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
diag_trigger(sm);
}
}
/* --------------------------------------------------------------------- */
static void demodulator_2666_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
{
struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d);
for (; buflen > 0; buflen--, buf++) {
demod_one_sample(sm, st, (*buf-0x80)<<8,
convolution12_u8(buf, afsk26_dem_tables[0][0].i, AFSK26_DEM_SUM_I_0_0),
convolution12_u8(buf, afsk26_dem_tables[0][0].q, AFSK26_DEM_SUM_Q_0_0),
convolution12_u8(buf, afsk26_dem_tables[0][1].i, AFSK26_DEM_SUM_I_0_1),
convolution12_u8(buf, afsk26_dem_tables[0][1].q, AFSK26_DEM_SUM_Q_0_1));
demod_one_sample(sm, st, (*buf-0x80)<<8,
convolution12_u8(buf, afsk26_dem_tables[1][0].i, AFSK26_DEM_SUM_I_1_0),
convolution12_u8(buf, afsk26_dem_tables[1][0].q, AFSK26_DEM_SUM_Q_1_0),
convolution12_u8(buf, afsk26_dem_tables[1][1].i, AFSK26_DEM_SUM_I_1_1),
convolution12_u8(buf, afsk26_dem_tables[1][1].q, AFSK26_DEM_SUM_Q_1_1));
}
}
/* --------------------------------------------------------------------- */
static void demodulator_2666_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
{
struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d);
for (; buflen > 0; buflen--, buf++) {
demod_one_sample(sm, st, *buf,
convolution12_s16(buf, afsk26_dem_tables[0][0].i, AFSK26_DEM_SUM_I_0_0),
convolution12_s16(buf, afsk26_dem_tables[0][0].q, AFSK26_DEM_SUM_Q_0_0),
convolution12_s16(buf, afsk26_dem_tables[0][1].i, AFSK26_DEM_SUM_I_0_1),
convolution12_s16(buf, afsk26_dem_tables[0][1].q, AFSK26_DEM_SUM_Q_0_1));
demod_one_sample(sm, st, *buf,
convolution12_s16(buf, afsk26_dem_tables[1][0].i, AFSK26_DEM_SUM_I_1_0),
convolution12_s16(buf, afsk26_dem_tables[1][0].q, AFSK26_DEM_SUM_Q_1_0),
convolution12_s16(buf, afsk26_dem_tables[1][1].i, AFSK26_DEM_SUM_I_1_1),
convolution12_s16(buf, afsk26_dem_tables[1][1].q, AFSK26_DEM_SUM_Q_1_1));
}
}
/* --------------------------------------------------------------------- */
static void demod_init_2666(struct sm_state *sm)
{
struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d);
st->dcd_time = 400;
st->dcd_sum0 = 2;
}
/* --------------------------------------------------------------------- */
const struct modem_tx_info sm_afsk2666_tx = {
"afsk2666", sizeof(struct mod_state_afsk26), AFSK26_SAMPLERATE, 2666,
modulator_2666_u8, modulator_2666_s16, NULL
};
const struct modem_rx_info sm_afsk2666_rx = {
"afsk2666", sizeof(struct demod_state_afsk26), AFSK26_SAMPLERATE, 2666, 12, 6,
demodulator_2666_u8, demodulator_2666_s16, demod_init_2666
};
/* --------------------------------------------------------------------- */
/*****************************************************************************/
/*
* sm_fsk9600.c -- soundcard radio modem driver,
* 9600 baud G3RUH compatible FSK modem
*
* Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Please note that the GPL allows you to use the driver, NOT the radio.
* In order to use the radio, you need a license from the communications
* authority of your country.
*
*/
#include "sm.h"
#include "sm_tbl_fsk9600.h"
/* --------------------------------------------------------------------- */
struct demod_state_fsk96 {
unsigned int shreg;
unsigned long descram;
unsigned int bit_pll;
unsigned char last_sample;
unsigned int dcd_shreg;
int dcd_sum0, dcd_sum1, dcd_sum2;
unsigned int dcd_time;
};
struct mod_state_fsk96 {
unsigned int shreg;
unsigned long scram;
unsigned char tx_bit;
unsigned char *txtbl;
unsigned int txphase;
};
/* --------------------------------------------------------------------- */
#define DESCRAM_TAP1 0x20000
#define DESCRAM_TAP2 0x01000
#define DESCRAM_TAP3 0x00001
#define DESCRAM_TAPSH1 17
#define DESCRAM_TAPSH2 12
#define DESCRAM_TAPSH3 0
#define SCRAM_TAP1 0x20000 /* X^17 */
#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
/* --------------------------------------------------------------------- */
static void modulator_9600_4_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
{
struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
for (; buflen > 0; buflen--) {
if (!st->txphase++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->scram = (st->scram << 1) | (st->scram & 1);
st->scram ^= !(st->shreg & 1);
st->shreg >>= 1;
if (st->scram & (SCRAM_TAP1 << 1))
st->scram ^= SCRAM_TAPN << 1;
st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff);
}
if (st->txphase >= 4)
st->txphase = 0;
*buf++ = *st->txtbl;
st->txtbl += 0x100;
}
}
/* --------------------------------------------------------------------- */
static void modulator_9600_4_s16(struct sm_state *sm, short *buf, unsigned int buflen)
{
struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
for (; buflen > 0; buflen--) {
if (!st->txphase++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->scram = (st->scram << 1) | (st->scram & 1);
st->scram ^= !(st->shreg & 1);
st->shreg >>= 1;
if (st->scram & (SCRAM_TAP1 << 1))
st->scram ^= SCRAM_TAPN << 1;
st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff);
}
if (st->txphase >= 4)
st->txphase = 0;
*buf++ = ((*st->txtbl)-0x80) << 8;
st->txtbl += 0x100;
}
}
/* --------------------------------------------------------------------- */
static void demodulator_9600_4_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
{
struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
static const int pll_corr[2] = { -0x1000, 0x1000 };
unsigned char curbit;
unsigned int descx;
for (; buflen > 0; buflen--, buf++) {
st->dcd_shreg <<= 1;
st->bit_pll += 0x4000;
curbit = (*buf >= 0x80);
if (st->last_sample ^ curbit) {
st->dcd_shreg |= 1;
st->bit_pll += pll_corr[st->bit_pll < 0xa000];
st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) -
!!(st->dcd_shreg & 0x10);
}
st->last_sample = curbit;
hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 240;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffff;
st->descram = (st->descram << 1) | curbit;
descx = st->descram ^ (st->descram >> 1);
descx ^= ((descx >> DESCRAM_TAPSH1) ^
(descx >> DESCRAM_TAPSH2));
st->shreg >>= 1;
st->shreg |= (!(descx & 1)) << 16;
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
diag_trigger(sm);
}
diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
}
}
/* --------------------------------------------------------------------- */
static void demodulator_9600_4_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
{
struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
static const int pll_corr[2] = { -0x1000, 0x1000 };
unsigned char curbit;
unsigned int descx;
for (; buflen > 0; buflen--, buf++) {
st->dcd_shreg <<= 1;
st->bit_pll += 0x4000;
curbit = (*buf >= 0);
if (st->last_sample ^ curbit) {
st->dcd_shreg |= 1;
st->bit_pll += pll_corr[st->bit_pll < 0xa000];
st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) -
!!(st->dcd_shreg & 0x10);
}
st->last_sample = curbit;
hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 240;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffff;
st->descram = (st->descram << 1) | curbit;
descx = st->descram ^ (st->descram >> 1);
descx ^= ((descx >> DESCRAM_TAPSH1) ^
(descx >> DESCRAM_TAPSH2));
st->shreg >>= 1;
st->shreg |= (!(descx & 1)) << 16;
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
diag_trigger(sm);
}
diag_add_one(sm, *buf);
}
}
/* --------------------------------------------------------------------- */
static void modulator_9600_5_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
{
struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
for (; buflen > 0; buflen--) {
if (!st->txphase++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->scram = (st->scram << 1) | (st->scram & 1);
st->scram ^= !(st->shreg & 1);
st->shreg >>= 1;
if (st->scram & (SCRAM_TAP1 << 1))
st->scram ^= SCRAM_TAPN << 1;
st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff);
}
if (st->txphase >= 5)
st->txphase = 0;
*buf++ = *st->txtbl;
st->txtbl += 0x100;
}
}
/* --------------------------------------------------------------------- */
static void modulator_9600_5_s16(struct sm_state *sm, short *buf, unsigned int buflen)
{
struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
for (; buflen > 0; buflen--) {
if (!st->txphase++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->scram = (st->scram << 1) | (st->scram & 1);
st->scram ^= !(st->shreg & 1);
st->shreg >>= 1;
if (st->scram & (SCRAM_TAP1 << 1))
st->scram ^= SCRAM_TAPN << 1;
st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff);
}
if (st->txphase >= 5)
st->txphase = 0;
*buf++ = ((*st->txtbl)-0x80)<<8;
st->txtbl += 0x100;
}
}
/* --------------------------------------------------------------------- */
static void demodulator_9600_5_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
{
struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
static const int pll_corr[2] = { -0x1000, 0x1000 };
unsigned char curbit;
unsigned int descx;
for (; buflen > 0; buflen--, buf++) {
st->dcd_shreg <<= 1;
st->bit_pll += 0x3333;
curbit = (*buf >= 0x80);
if (st->last_sample ^ curbit) {
st->dcd_shreg |= 1;
st->bit_pll += pll_corr[st->bit_pll < 0x9999];
st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) -
hweight8(st->dcd_shreg & 0x70);
}
st->last_sample = curbit;
hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 240;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffff;
st->descram = (st->descram << 1) | curbit;
descx = st->descram ^ (st->descram >> 1);
descx ^= ((descx >> DESCRAM_TAPSH1) ^
(descx >> DESCRAM_TAPSH2));
st->shreg >>= 1;
st->shreg |= (!(descx & 1)) << 16;
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
diag_trigger(sm);
}
diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
}
}
/* --------------------------------------------------------------------- */
static void demodulator_9600_5_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
{
struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
static const int pll_corr[2] = { -0x1000, 0x1000 };
unsigned char curbit;
unsigned int descx;
for (; buflen > 0; buflen--, buf++) {
st->dcd_shreg <<= 1;
st->bit_pll += 0x3333;
curbit = (*buf >= 0);
if (st->last_sample ^ curbit) {
st->dcd_shreg |= 1;
st->bit_pll += pll_corr[st->bit_pll < 0x9999];
st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) -
hweight8(st->dcd_shreg & 0x70);
}
st->last_sample = curbit;
hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 240;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffff;
st->descram = (st->descram << 1) | curbit;
descx = st->descram ^ (st->descram >> 1);
descx ^= ((descx >> DESCRAM_TAPSH1) ^
(descx >> DESCRAM_TAPSH2));
st->shreg >>= 1;
st->shreg |= (!(descx & 1)) << 16;
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
diag_trigger(sm);
}
diag_add_one(sm, *buf);
}
}
/* --------------------------------------------------------------------- */
static void demod_init_9600(struct sm_state *sm)
{
struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
st->dcd_time = 240;
st->dcd_sum0 = 2;
}
/* --------------------------------------------------------------------- */
const struct modem_tx_info sm_fsk9600_4_tx = {
"fsk9600", sizeof(struct mod_state_fsk96), 38400, 9600,
modulator_9600_4_u8, modulator_9600_4_s16, NULL
};
const struct modem_rx_info sm_fsk9600_4_rx = {
"fsk9600", sizeof(struct demod_state_fsk96), 38400, 9600, 1, 4,
demodulator_9600_4_u8, demodulator_9600_4_s16, demod_init_9600
};
/* --------------------------------------------------------------------- */
const struct modem_tx_info sm_fsk9600_5_tx = {
"fsk9600", sizeof(struct mod_state_fsk96), 48000, 9600,
modulator_9600_5_u8, modulator_9600_5_s16, NULL
};
const struct modem_rx_info sm_fsk9600_5_rx = {
"fsk9600", sizeof(struct demod_state_fsk96), 48000, 9600, 1, 5,
demodulator_9600_5_u8, demodulator_9600_5_s16, demod_init_9600
};
/* --------------------------------------------------------------------- */
/*****************************************************************************/
/*
* sm_hapn4800.c -- soundcard radio modem driver, 4800 baud HAPN modem
*
* Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Please note that the GPL allows you to use the driver, NOT the radio.
* In order to use the radio, you need a license from the communications
* authority of your country.
*
*
* This module implements a (hopefully) HAPN (Hamilton Area Packet
* Network) compatible 4800 baud modem.
* The HAPN modem uses kind of "duobinary signalling" (not really,
* duobinary signalling gives ... 0 0 -1 0 1 0 0 ... at the sampling
* instants, whereas HAPN signalling gives ... 0 0 -1 1 0 0 ..., see
* Proakis, Digital Communications).
* The code is untested. It is compatible with itself (i.e. it can decode
* the packets it sent), but I could not test if it is compatible with
* any "real" HAPN modem, since noone uses it in my region of the world.
* Feedback therefore welcome.
*/
#include "sm.h"
#include "sm_tbl_hapn4800.h"
/* --------------------------------------------------------------------- */
struct demod_state_hapn48 {
unsigned int shreg;
unsigned int bit_pll;
unsigned char last_bit;
unsigned char last_bit2;
unsigned int dcd_shreg;
int dcd_sum0, dcd_sum1, dcd_sum2;
unsigned int dcd_time;
int lvlhi, lvllo;
};
struct mod_state_hapn48 {
unsigned int shreg;
unsigned char tx_bit;
unsigned int tx_seq;
const unsigned char *tbl;
};
/* --------------------------------------------------------------------- */
static void modulator_hapn4800_10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
{
struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
for (; buflen > 0; buflen--, buf++) {
if (!st->tx_seq++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->tx_bit = ((st->tx_bit << 1) |
(st->tx_bit & 1));
st->tx_bit ^= (!(st->shreg & 1));
st->shreg >>= 1;
st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf);
}
if (st->tx_seq >= 10)
st->tx_seq = 0;
*buf = *st->tbl;
st->tbl += 0x10;
}
}
/* --------------------------------------------------------------------- */
static void modulator_hapn4800_10_s16(struct sm_state *sm, short *buf, unsigned int buflen)
{
struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
for (; buflen > 0; buflen--, buf++) {
if (!st->tx_seq++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->tx_bit = ((st->tx_bit << 1) |
(st->tx_bit & 1));
st->tx_bit ^= (!(st->shreg & 1));
st->shreg >>= 1;
st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf);
}
if (st->tx_seq >= 10)
st->tx_seq = 0;
*buf = ((*st->tbl)-0x80)<<8;
st->tbl += 0x10;
}
}
/* --------------------------------------------------------------------- */
static void modulator_hapn4800_8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
{
struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
for (; buflen > 0; buflen--, buf++) {
if (!st->tx_seq++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
st->tx_bit ^= !(st->shreg & 1);
st->shreg >>= 1;
st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf);
}
if (st->tx_seq >= 8)
st->tx_seq = 0;
*buf = *st->tbl;
st->tbl += 0x10;
}
}
/* --------------------------------------------------------------------- */
static void modulator_hapn4800_8_s16(struct sm_state *sm, short *buf, unsigned int buflen)
{
struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
for (; buflen > 0; buflen--, buf++) {
if (!st->tx_seq++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
st->tx_bit ^= !(st->shreg & 1);
st->shreg >>= 1;
st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf);
}
if (st->tx_seq >= 8)
st->tx_seq = 0;
*buf = ((*st->tbl)-0x80)<<8;
st->tbl += 0x10;
}
}
/* --------------------------------------------------------------------- */
static void modulator_hapn4800_pm10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
{
struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
for (; buflen > 0; buflen--, buf++) {
if (!st->tx_seq++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->tx_bit = ((st->tx_bit << 1) |
(st->tx_bit & 1));
st->tx_bit ^= (!(st->shreg & 1));
st->shreg >>= 1;
st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf);
}
if (st->tx_seq >= 10)
st->tx_seq = 0;
*buf = *st->tbl;
st->tbl += 0x10;
}
}
/* --------------------------------------------------------------------- */
static void modulator_hapn4800_pm10_s16(struct sm_state *sm, short *buf, unsigned int buflen)
{
struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
for (; buflen > 0; buflen--, buf++) {
if (!st->tx_seq++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->tx_bit = ((st->tx_bit << 1) |
(st->tx_bit & 1));
st->tx_bit ^= (!(st->shreg & 1));
st->shreg >>= 1;
st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf);
}
if (st->tx_seq >= 10)
st->tx_seq = 0;
*buf = ((*st->tbl)-0x80)<<8;
st->tbl += 0x10;
}
}
/* --------------------------------------------------------------------- */
static void modulator_hapn4800_pm8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
{
struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
for (; buflen > 0; buflen--, buf++) {
if (!st->tx_seq++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
st->tx_bit ^= !(st->shreg & 1);
st->shreg >>= 1;
st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf);
}
if (st->tx_seq >= 8)
st->tx_seq = 0;
*buf = *st->tbl;
st->tbl += 0x10;
}
}
/* --------------------------------------------------------------------- */
static void modulator_hapn4800_pm8_s16(struct sm_state *sm, short *buf, unsigned int buflen)
{
struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
for (; buflen > 0; buflen--, buf++) {
if (!st->tx_seq++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
st->tx_bit ^= !(st->shreg & 1);
st->shreg >>= 1;
st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf);
}
if (st->tx_seq >= 8)
st->tx_seq = 0;
*buf = ((*st->tbl)-0x80)<<8;
st->tbl += 0x10;
}
}
/* --------------------------------------------------------------------- */
static void demodulator_hapn4800_10_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
{
struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
static const int pll_corr[2] = { -0x800, 0x800 };
int curst, cursync;
int inv;
for (; buflen > 0; buflen--, buf++) {
inv = ((int)(buf[-2])-0x80) << 8;
st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
if (inv > st->lvlhi)
st->lvlhi = inv;
if (inv < st->lvllo)
st->lvllo = inv;
if (buflen & 1)
st->dcd_shreg <<= 1;
st->bit_pll += 0x199a;
curst = cursync = 0;
if (inv > st->lvlhi >> 1) {
curst = 1;
cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
buf[-2] > buf[-0] && buf[-2] > buf[-4]);
} else if (inv < st->lvllo >> 1) {
curst = -1;
cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
buf[-2] < buf[-0] && buf[-2] < buf[-4]);
}
if (cursync) {
st->dcd_shreg |= cursync;
st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu];
st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) -
hweight32(st->dcd_shreg & 0xe739ce70);
}
hdlcdrv_channelbit(&sm->hdrv, cursync);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 240;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffff;
st->last_bit2 = st->last_bit;
if (curst < 0)
st->last_bit = 0;
else if (curst > 0)
st->last_bit = 1;
st->shreg >>= 1;
st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
diag_trigger(sm);
}
diag_add_one(sm, inv);
}
}
/* --------------------------------------------------------------------- */
static void demodulator_hapn4800_10_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
{
struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
static const int pll_corr[2] = { -0x800, 0x800 };
int curst, cursync;
int inv;
for (; buflen > 0; buflen--, buf++) {
inv = buf[-2];
st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
if (inv > st->lvlhi)
st->lvlhi = inv;
if (inv < st->lvllo)
st->lvllo = inv;
if (buflen & 1)
st->dcd_shreg <<= 1;
st->bit_pll += 0x199a;
curst = cursync = 0;
if (inv > st->lvlhi >> 1) {
curst = 1;
cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
buf[-2] > buf[-0] && buf[-2] > buf[-4]);
} else if (inv < st->lvllo >> 1) {
curst = -1;
cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
buf[-2] < buf[-0] && buf[-2] < buf[-4]);
}
if (cursync) {
st->dcd_shreg |= cursync;
st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu];
st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) -
hweight32(st->dcd_shreg & 0xe739ce70);
}
hdlcdrv_channelbit(&sm->hdrv, cursync);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 240;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffff;
st->last_bit2 = st->last_bit;
if (curst < 0)
st->last_bit = 0;
else if (curst > 0)
st->last_bit = 1;
st->shreg >>= 1;
st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
diag_trigger(sm);
}
diag_add_one(sm, inv);
}
}
/* --------------------------------------------------------------------- */
static void demodulator_hapn4800_8_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
{
struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
static const int pll_corr[2] = { -0x800, 0x800 };
int curst, cursync;
int inv;
for (; buflen > 0; buflen--, buf++) {
inv = ((int)(buf[-2])-0x80) << 8;
st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
if (inv > st->lvlhi)
st->lvlhi = inv;
if (inv < st->lvllo)
st->lvllo = inv;
if (buflen & 1)
st->dcd_shreg <<= 1;
st->bit_pll += 0x2000;
curst = cursync = 0;
if (inv > st->lvlhi >> 1) {
curst = 1;
cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
buf[-2] > buf[-0] && buf[-2] > buf[-4]);
} else if (inv < st->lvllo >> 1) {
curst = -1;
cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
buf[-2] < buf[-0] && buf[-2] < buf[-4]);
}
if (cursync) {
st->dcd_shreg |= cursync;
st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u];
st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) -
hweight32(st->dcd_shreg & 0xbbbbbbbb);
}
hdlcdrv_channelbit(&sm->hdrv, cursync);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 240;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffff;
st->last_bit2 = st->last_bit;
if (curst < 0)
st->last_bit = 0;
else if (curst > 0)
st->last_bit = 1;
st->shreg >>= 1;
st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
diag_trigger(sm);
}
diag_add_one(sm, inv);
}
}
/* --------------------------------------------------------------------- */
static void demodulator_hapn4800_8_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
{
struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
static const int pll_corr[2] = { -0x800, 0x800 };
int curst, cursync;
int inv;
for (; buflen > 0; buflen--, buf++) {
inv = buf[-2];
st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
if (inv > st->lvlhi)
st->lvlhi = inv;
if (inv < st->lvllo)
st->lvllo = inv;
if (buflen & 1)
st->dcd_shreg <<= 1;
st->bit_pll += 0x2000;
curst = cursync = 0;
if (inv > st->lvlhi >> 1) {
curst = 1;
cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
buf[-2] > buf[-0] && buf[-2] > buf[-4]);
} else if (inv < st->lvllo >> 1) {
curst = -1;
cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
buf[-2] < buf[-0] && buf[-2] < buf[-4]);
}
if (cursync) {
st->dcd_shreg |= cursync;
st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u];
st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) -
hweight32(st->dcd_shreg & 0xbbbbbbbb);
}
hdlcdrv_channelbit(&sm->hdrv, cursync);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 240;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffff;
st->last_bit2 = st->last_bit;
if (curst < 0)
st->last_bit = 0;
else if (curst > 0)
st->last_bit = 1;
st->shreg >>= 1;
st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
diag_trigger(sm);
}
diag_add_one(sm, inv);
}
}
/* --------------------------------------------------------------------- */
static void demod_init_hapn4800(struct sm_state *sm)
{
struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
st->dcd_time = 120;
st->dcd_sum0 = 2;
}
/* --------------------------------------------------------------------- */
const struct modem_tx_info sm_hapn4800_8_tx = {
"hapn4800", sizeof(struct mod_state_hapn48), 38400, 4800,
modulator_hapn4800_8_u8, modulator_hapn4800_8_s16, NULL
};
const struct modem_rx_info sm_hapn4800_8_rx = {
"hapn4800", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8,
demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800
};
/* --------------------------------------------------------------------- */
const struct modem_tx_info sm_hapn4800_10_tx = {
"hapn4800", sizeof(struct mod_state_hapn48), 48000, 4800,
modulator_hapn4800_10_u8, modulator_hapn4800_10_s16, NULL
};
const struct modem_rx_info sm_hapn4800_10_rx = {
"hapn4800", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10,
demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800
};
/* --------------------------------------------------------------------- */
const struct modem_tx_info sm_hapn4800_pm8_tx = {
"hapn4800pm", sizeof(struct mod_state_hapn48), 38400, 4800,
modulator_hapn4800_pm8_u8, modulator_hapn4800_pm8_s16, NULL
};
const struct modem_rx_info sm_hapn4800_pm8_rx = {
"hapn4800pm", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8,
demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800
};
/* --------------------------------------------------------------------- */
const struct modem_tx_info sm_hapn4800_pm10_tx = {
"hapn4800pm", sizeof(struct mod_state_hapn48), 48000, 4800,
modulator_hapn4800_pm10_u8, modulator_hapn4800_pm10_s16, NULL
};
const struct modem_rx_info sm_hapn4800_pm10_rx = {
"hapn4800pm", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10,
demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800
};
/* --------------------------------------------------------------------- */
/*****************************************************************************/
/*
* sm_psk4800.c -- soundcard radio modem driver, 4800 baud 8PSK modem
*
* Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Please note that the GPL allows you to use the driver, NOT the radio.
* In order to use the radio, you need a license from the communications
* authority of your country.
*
*/
#include "sm.h"
#include "sm_tbl_psk4800.h"
/* --------------------------------------------------------------------- */
#define DESCRAM_TAP1 0x20000
#define DESCRAM_TAP2 0x01000
#define DESCRAM_TAP3 0x00001
#define DESCRAM_TAPSH1 17
#define DESCRAM_TAPSH2 12
#define DESCRAM_TAPSH3 0
#define SCRAM_TAP1 0x20000 /* X^17 */
#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
#define SCRAM_SHIFT 17
/* --------------------------------------------------------------------- */
struct demod_state_psk48 {
/*
* input mixer and lowpass
*/
short infi[PSK48_RXF_LEN/2], infq[PSK48_RXF_LEN/2];
unsigned int downmixer;
int ovrphase;
short magi, magq;
/*
* sampling instant recovery
*/
int pwrhist[5];
unsigned int s_phase;
int cur_sync;
/*
* phase recovery
*/
short cur_phase_dev;
short last_ph_err;
unsigned short pskph;
unsigned int phase;
unsigned short last_pskph;
unsigned char cur_raw, last_raw, rawbits;
/*
* decoding
*/
unsigned int shreg;
unsigned long descram;
unsigned int bit_pll;
unsigned char last_sample;
unsigned int dcd_shreg;
int dcd_sum0, dcd_sum1, dcd_sum2;
unsigned int dcd_time;
};
struct mod_state_psk48 {
unsigned char txbits[PSK48_TXF_NUMSAMPLES];
unsigned short txphase;
unsigned int shreg;
unsigned long scram;
const short *tbl;
unsigned int txseq;
};
/* --------------------------------------------------------------------- */
static void modulator_4800_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
{
struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m);
int i, j;
int si, sq;
for (; buflen > 0; buflen--, buf++) {
if (!st->txseq++) {
memmove(st->txbits+1, st->txbits,
sizeof(st->txbits)-sizeof(st->txbits[0]));
for (i = 0; i < 3; i++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->scram = (st->scram << 1) |
(st->shreg & 1);
st->shreg >>= 1;
if (st->scram & SCRAM_TAP1)
st->scram ^= SCRAM_TAPN;
}
j = (st->scram >> (SCRAM_SHIFT+3)) & 7;
st->txbits[0] -= (j ^ (j >> 1));
st->txbits[0] &= 7;
st->tbl = psk48_tx_table;
}
if (st->txseq >= PSK48_TXF_OVERSAMPLING)
st->txseq = 0;
for (j = si = sq = 0; j < PSK48_TXF_NUMSAMPLES; j++, st->tbl += 16) {
si += st->tbl[st->txbits[j]];
sq += st->tbl[st->txbits[j]+8];
}
*buf = ((si*COS(st->txphase)+ sq*SIN(st->txphase)) >> 23) + 0x80;
st->txphase = (st->txphase + PSK48_PHASEINC) & 0xffffu;
}
}
/* --------------------------------------------------------------------- */
static void modulator_4800_s16(struct sm_state *sm, short *buf, unsigned int buflen)
{
struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m);
int i, j;
int si, sq;
for (; buflen > 0; buflen--, buf++) {
if (!st->txseq++) {
memmove(st->txbits+1, st->txbits,
sizeof(st->txbits)-sizeof(st->txbits[0]));
for (i = 0; i < 3; i++) {
if (st->shreg <= 1)
st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
st->scram = (st->scram << 1) |
(st->shreg & 1);
st->shreg >>= 1;
if (st->scram & SCRAM_TAP1)
st->scram ^= SCRAM_TAPN;
}
j = (st->scram >> (SCRAM_SHIFT+3)) & 7;
st->txbits[0] -= (j ^ (j >> 1));
st->txbits[0] &= 7;
st->tbl = psk48_tx_table;
}
if (st->txseq >= PSK48_TXF_OVERSAMPLING)
st->txseq = 0;
for (j = si = sq = 0; j < PSK48_TXF_NUMSAMPLES; j++, st->tbl += 16) {
si += st->tbl[st->txbits[j]];
sq += st->tbl[st->txbits[j]+8];
}
*buf = (si*COS(st->txphase)+ sq*SIN(st->txphase)) >> 15;
st->txphase = (st->txphase + PSK48_PHASEINC) & 0xffffu;
}
}
/* --------------------------------------------------------------------- */
static __inline__ unsigned short tbl_atan(short q, short i)
{
short tmp;
unsigned short argoffs = 0;
if (i == 0 && q == 0)
return 0;
switch (((q < 0) << 1) | (i < 0)) {
case 0:
break;
case 1:
tmp = q;
q = -i;
i = tmp;
argoffs = 0x4000;
break;
case 3:
q = -q;
i = -i;
argoffs = 0x8000;
break;
case 2:
tmp = -q;
q = i;
i = tmp;
argoffs = 0xc000;
break;
}
if (q > i) {
tmp = i / q * ATAN_TABLEN;
return (argoffs+0x4000-atan_tab[((i<<15)/q*ATAN_TABLEN>>15)])
&0xffffu;
}
return (argoffs+atan_tab[((q<<15)/i*ATAN_TABLEN)>>15])&0xffffu;
}
#define ATAN(q,i) tbl_atan(q, i)
/* --------------------------------------------------------------------- */
static void demod_psk48_baseband(struct sm_state *sm, struct demod_state_psk48 *st,
short vali, short valq)
{
int i, j;
st->magi = vali;
st->magq = valq;
memmove(st->pwrhist+1, st->pwrhist,
sizeof(st->pwrhist)-sizeof(st->pwrhist[0]));
st->pwrhist[0] = st->magi * st->magi +
st->magq * st->magq;
st->cur_sync = ((st->pwrhist[4] >> 2) > st->pwrhist[2] &&
(st->pwrhist[0] >> 2) > st->pwrhist[2] &&
st-> pwrhist[3] > st->pwrhist[2] &&
st->pwrhist[1] > st->pwrhist[2]);
st->s_phase &= 0xffff;
st->s_phase += PSK48_SPHASEINC;
st->dcd_shreg <<= 1;
if (st->cur_sync) {
if (st->s_phase >= (0x8000 + 5*PSK48_SPHASEINC/2))
st->s_phase -= PSK48_SPHASEINC/6;
else
st->s_phase += PSK48_SPHASEINC/6;
st->dcd_sum0 = 4*hweight8(st->dcd_shreg & 0xf8)-
hweight16(st->dcd_shreg & 0x1f00);
}
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 240;
}
if (st->s_phase < 0x10000)
return;
/*
* sample one constellation
*/
st->last_pskph = st->pskph;
st->pskph = (ATAN(st->magq, st->magi)-
st->phase) & 0xffffu;
st->last_ph_err = (st->pskph & 0x1fffu) - 0x1000;
st->phase += st->last_ph_err/16;
st->last_raw = st->cur_raw;
st->cur_raw = ((st->pskph >> 13) & 7);
i = (st->cur_raw - st->last_raw) & 7;
st->rawbits = i ^ (i >> 1) ^ (i >> 2);
st->descram = (st->descram << 3) | (st->rawbits);
hdlcdrv_channelbit(&sm->hdrv, st->descram & 4);
hdlcdrv_channelbit(&sm->hdrv, st->descram & 2);
hdlcdrv_channelbit(&sm->hdrv, st->descram & 1);
i = (((st->descram >> DESCRAM_TAPSH1) & 7) ^
((st->descram >> DESCRAM_TAPSH2) & 7) ^
((st->descram >> DESCRAM_TAPSH3) & 7));
for (j = 4; j; j >>= 1) {
st->shreg >>= 1;
st->shreg |= (!!(i & j)) << 16;
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
}
#if 0
st->dcd_shreg <<= 1;
st->bit_pll += 0x4000;
curbit = (*buf >= 0x80);
if (st->last_sample ^ curbit) {
st->dcd_shreg |= 1;
st->bit_pll += pll_corr
[st->bit_pll < 0xa000];
st->dcd_sum0 += 8 *
hweight8(st->dcd_shreg & 0x0c) -
!!(st->dcd_shreg & 0x10);
}
st->last_sample = curbit;
hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
if ((--st->dcd_time) <= 0) {
hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
st->dcd_sum1 +
st->dcd_sum2) < 0);
st->dcd_sum2 = st->dcd_sum1;
st->dcd_sum1 = st->dcd_sum0;
st->dcd_sum0 = 2; /* slight bias */
st->dcd_time = 240;
}
if (st->bit_pll >= 0x10000) {
st->bit_pll &= 0xffffu;
st->descram = (st->descram << 1) | curbit;
descx = st->descram ^ (st->descram >> 1);
descx ^= ((descx >> DESCRAM_TAPSH1) ^
(descx >> DESCRAM_TAPSH2));
st->shreg >>= 1;
st->shreg |= (!(descx & 1)) << 16;
if (st->shreg & 1) {
hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
st->shreg = 0x10000;
}
diag_trigger(sm);
}
diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
#endif
diag_trigger(sm);
diag_add_constellation(sm, (vali*COS(st->phase)+ valq*SIN(st->phase)) >> 13,
(valq*COS(st->phase) - vali*SIN(st->phase)) >> 13);
}
/* --------------------------------------------------------------------- */
static void demodulator_4800_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
{
struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d);
int i, si, sq;
const short *coeff;
for (; buflen > 0; buflen--, buf++) {
memmove(st->infi+1, st->infi,
sizeof(st->infi)-sizeof(st->infi[0]));
memmove(st->infq+1, st->infq,
sizeof(st->infq)-sizeof(st->infq[0]));
si = *buf;
si &= 0xff;
si -= 128;
diag_add_one(sm, si << 8);
st->infi[0] = (si * COS(st->downmixer))>>7;
st->infq[0] = (si * SIN(st->downmixer))>>7;
st->downmixer = (st->downmixer-PSK48_PHASEINC)&0xffffu;
for (i = si = sq = 0, coeff = psk48_rx_coeff; i < (PSK48_RXF_LEN/2);
i++, coeff += 2) {
si += st->infi[i] * (*coeff);
sq += st->infq[i] * (*coeff);
}
demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
for (i = si = sq = 0, coeff = psk48_rx_coeff + 1; i < (PSK48_RXF_LEN/2);
i++, coeff += 2) {
si += st->infi[i] * (*coeff);
sq += st->infq[i] * (*coeff);
}
demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
}
}
/* --------------------------------------------------------------------- */
static void demodulator_4800_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
{
struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d);
int i, si, sq;
const short *coeff;
for (; buflen > 0; buflen--, buf++) {
memmove(st->infi+1, st->infi,
sizeof(st->infi)-sizeof(st->infi[0]));
memmove(st->infq+1, st->infq,
sizeof(st->infq)-sizeof(st->infq[0]));
si = *buf;
diag_add_one(sm, si);
st->infi[0] = (si * COS(st->downmixer))>>15;
st->infq[0] = (si * SIN(st->downmixer))>>15;
st->downmixer = (st->downmixer-PSK48_PHASEINC)&0xffffu;
for (i = si = sq = 0, coeff = psk48_rx_coeff; i < (PSK48_RXF_LEN/2);
i++, coeff += 2) {
si += st->infi[i] * (*coeff);
sq += st->infq[i] * (*coeff);
}
demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
for (i = si = sq = 0, coeff = psk48_rx_coeff + 1; i < (PSK48_RXF_LEN/2);
i++, coeff += 2) {
si += st->infi[i] * (*coeff);
sq += st->infq[i] * (*coeff);
}
demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
}
}
/* --------------------------------------------------------------------- */
static void mod_init_4800(struct sm_state *sm)
{
struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m);
st->scram = 1;
}
/* --------------------------------------------------------------------- */
static void demod_init_4800(struct sm_state *sm)
{
struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d);
st->dcd_time = 120;
st->dcd_sum0 = 2;
}
/* --------------------------------------------------------------------- */
const struct modem_tx_info sm_psk4800_tx = {
"psk4800", sizeof(struct mod_state_psk48),
PSK48_SAMPLERATE, 4800,
modulator_4800_u8, modulator_4800_s16, mod_init_4800
};
const struct modem_rx_info sm_psk4800_rx = {
"psk4800", sizeof(struct demod_state_psk48),
PSK48_SAMPLERATE, 4800, 1, PSK48_TXF_OVERSAMPLING,
demodulator_4800_u8, demodulator_4800_s16, demod_init_4800
};
/* --------------------------------------------------------------------- */
/*
* sm_sbc.c -- soundcard radio modem driver soundblaster hardware driver
*
* Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Please note that the GPL allows you to use the driver, NOT the radio.
* In order to use the radio, you need a license from the communications
* authority of your country.
*
*/
#include <linux/ptrace.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/soundmodem.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/dma.h>
#include "sm.h"
#include "smdma.h"
/* --------------------------------------------------------------------- */
/*
* currently this module is supposed to support both module styles, i.e.
* the old one present up to about 2.1.9, and the new one functioning
* starting with 2.1.21. The reason is I have a kit allowing to compile
* this module also under 2.0.x which was requested by several people.
* This will go in 2.2
*/
#include <linux/version.h>
#if LINUX_VERSION_CODE >= 0x20100
#include <asm/uaccess.h>
#else
#include <asm/segment.h>
#include <linux/mm.h>
#undef put_user
#undef get_user
#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
static inline int copy_from_user(void *to, const void *from, unsigned long n)
{
int i = verify_area(VERIFY_READ, from, n);
if (i)
return i;
memcpy_fromfs(to, from, n);
return 0;
}
static inline int copy_to_user(void *to, const void *from, unsigned long n)
{
int i = verify_area(VERIFY_WRITE, to, n);
if (i)
return i;
memcpy_tofs(to, from, n);
return 0;
}
#endif
/* --------------------------------------------------------------------- */
struct sc_state_sbc {
unsigned char revhi, revlo;
unsigned char fmt[2];
unsigned int sr[2];
};
#define SCSTATE ((struct sc_state_sbc *)(&sm->hw))
/* --------------------------------------------------------------------- */
/*
* the sbc converter's registers
*/
#define DSP_RESET(iobase) (iobase+0x6)
#define DSP_READ_DATA(iobase) (iobase+0xa)
#define DSP_WRITE_DATA(iobase) (iobase+0xc)
#define DSP_WRITE_STATUS(iobase) (iobase+0xc)
#define DSP_DATA_AVAIL(iobase) (iobase+0xe)
#define DSP_MIXER_ADDR(iobase) (iobase+0x4)
#define DSP_MIXER_DATA(iobase) (iobase+0x5)
#define DSP_INTACK_16BIT(iobase) (iobase+0xf)
#define SBC_EXTENT 16
/* --------------------------------------------------------------------- */
/*
* SBC commands
*/
#define SBC_OUTPUT 0x14
#define SBC_INPUT 0x24
#define SBC_BLOCKSIZE 0x48
#define SBC_HI_OUTPUT 0x91
#define SBC_HI_INPUT 0x99
#define SBC_LO_OUTPUT_AUTOINIT 0x1c
#define SBC_LO_INPUT_AUTOINIT 0x2c
#define SBC_HI_OUTPUT_AUTOINIT 0x90
#define SBC_HI_INPUT_AUTOINIT 0x98
#define SBC_IMMED_INT 0xf2
#define SBC_GET_REVISION 0xe1
#define ESS_GET_REVISION 0xe7
#define SBC_SPEAKER_ON 0xd1
#define SBC_SPEAKER_OFF 0xd3
#define SBC_DMA_ON 0xd0
#define SBC_DMA_OFF 0xd4
#define SBC_SAMPLE_RATE 0x40
#define SBC_SAMPLE_RATE_OUT 0x41
#define SBC_SAMPLE_RATE_IN 0x42
#define SBC_MONO_8BIT 0xa0
#define SBC_MONO_16BIT 0xa4
#define SBC_STEREO_8BIT 0xa8
#define SBC_STEREO_16BIT 0xac
#define SBC4_OUT8_AI 0xc6
#define SBC4_IN8_AI 0xce
#define SBC4_MODE_UNS_MONO 0x00
#define SBC4_MODE_SIGN_MONO 0x10
#define SBC4_OUT16_AI 0xb6
#define SBC4_IN16_AI 0xbe
/* --------------------------------------------------------------------- */
static int inline reset_dsp(struct net_device *dev)
{
int i;
outb(1, DSP_RESET(dev->base_addr));
udelay(300);
outb(0, DSP_RESET(dev->base_addr));
for (i = 0; i < 0xffff; i++)
if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80)
if (inb(DSP_READ_DATA(dev->base_addr)) == 0xaa)
return 1;
return 0;
}
/* --------------------------------------------------------------------- */
static void inline write_dsp(struct net_device *dev, unsigned char data)
{
int i;
for (i = 0; i < 0xffff; i++)
if (!(inb(DSP_WRITE_STATUS(dev->base_addr)) & 0x80)) {
outb(data, DSP_WRITE_DATA(dev->base_addr));
return;
}
}
/* --------------------------------------------------------------------- */
static int inline read_dsp(struct net_device *dev, unsigned char *data)
{
int i;
if (!data)
return 0;
for (i = 0; i < 0xffff; i++)
if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) {
*data = inb(DSP_READ_DATA(dev->base_addr));
return 1;
}
return 0;
}
/* --------------------------------------------------------------------- */
static int config_resources(struct net_device *dev, struct sm_state *sm, int fdx)
{
unsigned char irqreg = 0, dmareg = 0, realirq, realdma;
unsigned long flags;
switch (dev->irq) {
case 2:
case 9:
irqreg |= 0x01;
break;
case 5:
irqreg |= 0x02;
break;
case 7:
irqreg |= 0x04;
break;
case 10:
irqreg |= 0x08;
break;
default:
return -ENODEV;
}
switch (dev->dma) {
case 0:
dmareg |= 0x01;
break;
case 1:
dmareg |= 0x02;
break;
case 3:
dmareg |= 0x08;
break;
default:
return -ENODEV;
}
if (fdx) {
switch (sm->hdrv.ptt_out.dma2) {
case 5:
dmareg |= 0x20;
break;
case 6:
dmareg |= 0x40;
break;
case 7:
dmareg |= 0x80;
break;
default:
return -ENODEV;
}
}
save_flags(flags);
cli();
outb(0x80, DSP_MIXER_ADDR(dev->base_addr));
outb(irqreg, DSP_MIXER_DATA(dev->base_addr));
realirq = inb(DSP_MIXER_DATA(dev->base_addr));
outb(0x81, DSP_MIXER_ADDR(dev->base_addr));
outb(dmareg, DSP_MIXER_DATA(dev->base_addr));
realdma = inb(DSP_MIXER_DATA(dev->base_addr));
restore_flags(flags);
if ((~realirq) & irqreg || (~realdma) & dmareg) {
printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device "
"and IRQ/DMA specified wrongly?\n", sm_drvname);
return -EINVAL;
}
return 0;
}
/* --------------------------------------------------------------------- */
static void inline sbc_int_ack_8bit(struct net_device *dev)
{
inb(DSP_DATA_AVAIL(dev->base_addr));
}
/* --------------------------------------------------------------------- */
static void inline sbc_int_ack_16bit(struct net_device *dev)
{
inb(DSP_INTACK_16BIT(dev->base_addr));
}
/* --------------------------------------------------------------------- */
static void setup_dma_dsp(struct net_device *dev, struct sm_state *sm, int send)
{
unsigned long flags;
static const unsigned char sbcmode[2][2] = {
{ SBC_LO_INPUT_AUTOINIT, SBC_LO_OUTPUT_AUTOINIT },
{ SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT }
};
static const unsigned char sbc4mode[2] = { SBC4_IN8_AI, SBC4_OUT8_AI };
static const unsigned char sbcskr[2] = { SBC_SPEAKER_OFF, SBC_SPEAKER_ON };
unsigned int nsamps;
send = !!send;
if (!reset_dsp(dev)) {
printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
return;
}
save_flags(flags);
cli();
sbc_int_ack_8bit(dev);
write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */
write_dsp(dev, SCSTATE->fmt[send]);
write_dsp(dev, sbcskr[send]);
nsamps = dma_setup(sm, send, dev->dma) - 1;
sbc_int_ack_8bit(dev);
if (SCSTATE->revhi >= 4) {
write_dsp(dev, sbc4mode[send]);
write_dsp(dev, SBC4_MODE_UNS_MONO);
write_dsp(dev, nsamps & 0xff);
write_dsp(dev, nsamps >> 8);
} else {
write_dsp(dev, SBC_BLOCKSIZE);
write_dsp(dev, nsamps & 0xff);
write_dsp(dev, nsamps >> 8);
write_dsp(dev, sbcmode[SCSTATE->fmt[send] >= 180][send]);
/* hispeed mode if sample rate > 13kHz */
}
restore_flags(flags);
}
/* --------------------------------------------------------------------- */
static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct net_device *dev = (struct net_device *)dev_id;
struct sm_state *sm = (struct sm_state *)dev->priv;
unsigned int curfrag;
if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
return;
cli();
sbc_int_ack_8bit(dev);
disable_dma(dev->dma);
clear_dma_ff(dev->dma);
dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag);
enable_dma(dev->dma);
sm_int_freq(sm);
sti();
if (sm->dma.ptt_cnt <= 0) {
dma_receive(sm, curfrag);
hdlcdrv_arbitrate(dev, &sm->hdrv);
if (hdlcdrv_ptt(&sm->hdrv)) {
/* starting to transmit */
disable_dma(dev->dma);
hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
dma_start_transmit(sm);
setup_dma_dsp(dev, sm, 1);
dma_transmit(sm);
}
} else if (dma_end_transmit(sm, curfrag)) {
/* stopping transmission */
disable_dma(dev->dma);
sti();
dma_init_receive(sm);
setup_dma_dsp(dev, sm, 0);
} else
dma_transmit(sm);
sm_output_status(sm);
hdlcdrv_transmitter(dev, &sm->hdrv);
hdlcdrv_receiver(dev, &sm->hdrv);
}
/* --------------------------------------------------------------------- */
static int sbc_open(struct net_device *dev, struct sm_state *sm)
{
int err;
unsigned int dmasz, u;
if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n",
sizeof(struct sc_state_sbc), sizeof(sm->m));
return -ENODEV;
}
if (!dev || !sm)
return -ENXIO;
if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT ||
dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
return -ENXIO;
if (check_region(dev->base_addr, SBC_EXTENT))
return -EACCES;
/*
* check if a card is available
*/
if (!reset_dsp(dev)) {
printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n",
sm_drvname, dev->base_addr);
return -ENODEV;
}
write_dsp(dev, SBC_GET_REVISION);
if (!read_dsp(dev, &SCSTATE->revhi) ||
!read_dsp(dev, &SCSTATE->revlo))
return -ENODEV;
printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname,
SCSTATE->revhi, SCSTATE->revlo);
if (SCSTATE->revhi < 2) {
printk(KERN_ERR "%s: your card is an antiquity, at least DSP "
"rev 2.00 required\n", sm_drvname);
return -ENODEV;
}
if (SCSTATE->revhi < 3 &&
(SCSTATE->fmt[0] >= 180 || SCSTATE->fmt[1] >= 180)) {
printk(KERN_ERR "%s: sbc io 0x%lx: DSP rev %d.%02d too "
"old, at least 3.00 required\n", sm_drvname,
dev->base_addr, SCSTATE->revhi, SCSTATE->revlo);
return -ENODEV;
}
if (SCSTATE->revhi >= 4 &&
(err = config_resources(dev, sm, 0))) {
printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname);
return err;
}
/*
* initialize some variables
*/
dma_init_receive(sm);
dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
u = NUM_FRAGMENTS * sm->dma.ofragsz;
if (u > dmasz)
dmasz = u;
if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
return -ENOMEM;
dma_init_transmit(sm);
dma_init_receive(sm);
memset(&sm->m, 0, sizeof(sm->m));
memset(&sm->d, 0, sizeof(sm->d));
if (sm->mode_tx->init)
sm->mode_tx->init(sm);
if (sm->mode_rx->init)
sm->mode_rx->init(sm);
if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
kfree(sm->dma.obuf);
return -EBUSY;
}
if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT,
sm->hwdrv->hw_name, dev)) {
free_dma(dev->dma);
kfree(sm->dma.obuf);
return -EBUSY;
}
request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
setup_dma_dsp(dev, sm, 0);
return 0;
}
/* --------------------------------------------------------------------- */
static int sbc_close(struct net_device *dev, struct sm_state *sm)
{
if (!dev || !sm)
return -EINVAL;
/*
* disable interrupts
*/
disable_dma(dev->dma);
reset_dsp(dev);
free_irq(dev->irq, dev);
free_dma(dev->dma);
release_region(dev->base_addr, SBC_EXTENT);
kfree(sm->dma.obuf);
return 0;
}
/* --------------------------------------------------------------------- */
static int sbc_sethw(struct net_device *dev, struct sm_state *sm, char *mode)
{
char *cp = strchr(mode, '.');
const struct modem_tx_info **mtp = sm_modem_tx_table;
const struct modem_rx_info **mrp;
if (!strcmp(mode, "off")) {
sm->mode_tx = NULL;
sm->mode_rx = NULL;
return 0;
}
if (cp)
*cp++ = '\0';
else
cp = mode;
for (; *mtp; mtp++) {
if ((*mtp)->loc_storage > sizeof(sm->m)) {
printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
continue;
}
if (!(*mtp)->name || strcmp((*mtp)->name, mode))
continue;
if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100)
continue;
if (!(*mtp)->modulator_u8)
continue;
for (mrp = sm_modem_rx_table; *mrp; mrp++) {
if ((*mrp)->loc_storage > sizeof(sm->d)) {
printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
continue;
}
if (!(*mrp)->demodulator_u8)
continue;
if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
(*mrp)->srate >= 5000 && (*mrp)->srate <= 44100) {
sm->mode_tx = *mtp;
sm->mode_rx = *mrp;
SCSTATE->fmt[0] = 256-((1000000L+sm->mode_rx->srate/2)/
sm->mode_rx->srate);
SCSTATE->fmt[1] = 256-((1000000L+sm->mode_tx->srate/2)/
sm->mode_tx->srate);
sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
if (sm->dma.ifragsz < sm->mode_rx->overlap)
sm->dma.ifragsz = sm->mode_rx->overlap;
sm->dma.i16bit = sm->dma.o16bit = 0;
return 0;
}
}
}
return -EINVAL;
}
/* --------------------------------------------------------------------- */
static int sbc_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *ifr,
struct hdlcdrv_ioctl *hi, int cmd)
{
struct sm_ioctl bi;
unsigned long flags;
int i;
if (cmd != SIOCDEVPRIVATE)
return -ENOIOCTLCMD;
if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE |
HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
return -EFAULT;
switch (bi.cmd) {
default:
return -ENOIOCTLCMD;
case SMCTL_GETMIXER:
i = 0;
bi.data.mix.sample_rate = sm->mode_rx->srate;
bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
bi.data.mix.mixer_type = SM_MIXER_INVALID;
switch (SCSTATE->revhi) {
case 2:
bi.data.mix.mixer_type = SM_MIXER_CT1335;
break;
case 3:
bi.data.mix.mixer_type = SM_MIXER_CT1345;
break;
case 4:
bi.data.mix.mixer_type = SM_MIXER_CT1745;
break;
}
if (bi.data.mix.mixer_type != SM_MIXER_INVALID &&
bi.data.mix.reg < 0x80) {
save_flags(flags);
cli();
outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
bi.data.mix.data = inb(DSP_MIXER_DATA(dev->base_addr));
restore_flags(flags);
i = 1;
}
if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
return -EFAULT;
return i;
case SMCTL_SETMIXER:
if (!capable(CAP_SYS_RAWIO))
return -EACCES;
switch (SCSTATE->revhi) {
case 2:
if (bi.data.mix.mixer_type != SM_MIXER_CT1335)
return -EINVAL;
break;
case 3:
if (bi.data.mix.mixer_type != SM_MIXER_CT1345)
return -EINVAL;
break;
case 4:
if (bi.data.mix.mixer_type != SM_MIXER_CT1745)
return -EINVAL;
break;
default:
return -ENODEV;
}
if (bi.data.mix.reg >= 0x80)
return -EACCES;
save_flags(flags);
cli();
outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
outb(bi.data.mix.data, DSP_MIXER_DATA(dev->base_addr));
restore_flags(flags);
return 0;
}
if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
return -EFAULT;
return 0;
}
/* --------------------------------------------------------------------- */
const struct hardware_info sm_hw_sbc = {
"sbc", sizeof(struct sc_state_sbc),
sbc_open, sbc_close, sbc_ioctl, sbc_sethw
};
/* --------------------------------------------------------------------- */
static void setup_dma_fdx_dsp(struct net_device *dev, struct sm_state *sm)
{
unsigned long flags;
unsigned int isamps, osamps;
if (!reset_dsp(dev)) {
printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
return;
}
save_flags(flags);
cli();
sbc_int_ack_8bit(dev);
sbc_int_ack_16bit(dev);
/* should eventually change to set rates individually by SBC_SAMPLE_RATE_{IN/OUT} */
write_dsp(dev, SBC_SAMPLE_RATE_IN);
write_dsp(dev, SCSTATE->sr[0] >> 8);
write_dsp(dev, SCSTATE->sr[0] & 0xff);
write_dsp(dev, SBC_SAMPLE_RATE_OUT);
write_dsp(dev, SCSTATE->sr[1] >> 8);
write_dsp(dev, SCSTATE->sr[1] & 0xff);
write_dsp(dev, SBC_SPEAKER_ON);
if (sm->dma.o16bit) {
/*
* DMA channel 1 (8bit) does input (capture),
* DMA channel 2 (16bit) does output (playback)
*/
isamps = dma_setup(sm, 0, dev->dma) - 1;
osamps = dma_setup(sm, 1, sm->hdrv.ptt_out.dma2) - 1;
sbc_int_ack_8bit(dev);
sbc_int_ack_16bit(dev);
write_dsp(dev, SBC4_IN8_AI);
write_dsp(dev, SBC4_MODE_UNS_MONO);
write_dsp(dev, isamps & 0xff);
write_dsp(dev, isamps >> 8);
write_dsp(dev, SBC4_OUT16_AI);
write_dsp(dev, SBC4_MODE_SIGN_MONO);
write_dsp(dev, osamps & 0xff);
write_dsp(dev, osamps >> 8);
} else {
/*
* DMA channel 1 (8bit) does output (playback),
* DMA channel 2 (16bit) does input (capture)
*/
isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1;
osamps = dma_setup(sm, 1, dev->dma) - 1;
sbc_int_ack_8bit(dev);
sbc_int_ack_16bit(dev);
write_dsp(dev, SBC4_OUT8_AI);
write_dsp(dev, SBC4_MODE_UNS_MONO);
write_dsp(dev, osamps & 0xff);
write_dsp(dev, osamps >> 8);
write_dsp(dev, SBC4_IN16_AI);
write_dsp(dev, SBC4_MODE_SIGN_MONO);
write_dsp(dev, isamps & 0xff);
write_dsp(dev, isamps >> 8);
}
dma_init_receive(sm);
dma_init_transmit(sm);
restore_flags(flags);
}
/* --------------------------------------------------------------------- */
static void sbcfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct net_device *dev = (struct net_device *)dev_id;
struct sm_state *sm = (struct sm_state *)dev->priv;
unsigned char intsrc, pbint = 0, captint = 0;
unsigned int ocfrag, icfrag;
unsigned long flags;
if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
return;
save_flags(flags);
cli();
outb(0x82, DSP_MIXER_ADDR(dev->base_addr));
intsrc = inb(DSP_MIXER_DATA(dev->base_addr));
if (intsrc & 0x01) {
sbc_int_ack_8bit(dev);
if (sm->dma.o16bit) {
captint = 1;
disable_dma(dev->dma);
clear_dma_ff(dev->dma);
dma_ptr(sm, 0, dev->dma, &icfrag);
enable_dma(dev->dma);
} else {
pbint = 1;
disable_dma(dev->dma);
clear_dma_ff(dev->dma);
dma_ptr(sm, 1, dev->dma, &ocfrag);
enable_dma(dev->dma);
}
}
if (intsrc & 0x02) {
sbc_int_ack_16bit(dev);
if (sm->dma.o16bit) {
pbint = 1;
disable_dma(sm->hdrv.ptt_out.dma2);
clear_dma_ff(sm->hdrv.ptt_out.dma2);
dma_ptr(sm, 1, sm->hdrv.ptt_out.dma2, &ocfrag);
enable_dma(sm->hdrv.ptt_out.dma2);
} else {
captint = 1;
disable_dma(sm->hdrv.ptt_out.dma2);
clear_dma_ff(sm->hdrv.ptt_out.dma2);
dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag);
enable_dma(sm->hdrv.ptt_out.dma2);
}
}
restore_flags(flags);
sm_int_freq(sm);
sti();
if (pbint) {
if (dma_end_transmit(sm, ocfrag))
dma_clear_transmit(sm);
dma_transmit(sm);
}
if (captint) {
dma_receive(sm, icfrag);
hdlcdrv_arbitrate(dev, &sm->hdrv);
}
sm_output_status(sm);
hdlcdrv_transmitter(dev, &sm->hdrv);
hdlcdrv_receiver(dev, &sm->hdrv);
}
/* --------------------------------------------------------------------- */
static int sbcfdx_open(struct net_device *dev, struct sm_state *sm)
{
int err;
if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n",
sizeof(struct sc_state_sbc), sizeof(sm->m));
return -ENODEV;
}
if (!dev || !sm)
return -ENXIO;
if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT ||
dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
return -ENXIO;
if (check_region(dev->base_addr, SBC_EXTENT))
return -EACCES;
/*
* check if a card is available
*/
if (!reset_dsp(dev)) {
printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n",
sm_drvname, dev->base_addr);
return -ENODEV;
}
write_dsp(dev, SBC_GET_REVISION);
if (!read_dsp(dev, &SCSTATE->revhi) ||
!read_dsp(dev, &SCSTATE->revlo))
return -ENODEV;
printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname,
SCSTATE->revhi, SCSTATE->revlo);
if (SCSTATE->revhi < 4) {
printk(KERN_ERR "%s: at least DSP rev 4.00 required\n", sm_drvname);
return -ENODEV;
}
if ((err = config_resources(dev, sm, 1))) {
printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname);
return err;
}
/*
* initialize some variables
*/
if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA)))
return -ENOMEM;
if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) {
kfree(sm->dma.ibuf);
return -ENOMEM;
}
dma_init_transmit(sm);
dma_init_receive(sm);
memset(&sm->m, 0, sizeof(sm->m));
memset(&sm->d, 0, sizeof(sm->d));
if (sm->mode_tx->init)
sm->mode_tx->init(sm);
if (sm->mode_rx->init)
sm->mode_rx->init(sm);
if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
kfree(sm->dma.ibuf);
kfree(sm->dma.obuf);
return -EBUSY;
}
if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
kfree(sm->dma.ibuf);
kfree(sm->dma.obuf);
free_dma(dev->dma);
return -EBUSY;
}
if (request_irq(dev->irq, sbcfdx_interrupt, SA_INTERRUPT,
sm->hwdrv->hw_name, dev)) {
kfree(sm->dma.ibuf);
kfree(sm->dma.obuf);
free_dma(dev->dma);
free_dma(sm->hdrv.ptt_out.dma2);
return -EBUSY;
}
request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
setup_dma_fdx_dsp(dev, sm);
return 0;
}
/* --------------------------------------------------------------------- */
static int sbcfdx_close(struct net_device *dev, struct sm_state *sm)
{
if (!dev || !sm)
return -EINVAL;
/*
* disable interrupts
*/
disable_dma(dev->dma);
disable_dma(sm->hdrv.ptt_out.dma2);
reset_dsp(dev);
free_irq(dev->irq, dev);
free_dma(dev->dma);
free_dma(sm->hdrv.ptt_out.dma2);
release_region(dev->base_addr, SBC_EXTENT);
kfree(sm->dma.ibuf);
kfree(sm->dma.obuf);
return 0;
}
/* --------------------------------------------------------------------- */
static int sbcfdx_sethw(struct net_device *dev, struct sm_state *sm, char *mode)
{
char *cp = strchr(mode, '.');
const struct modem_tx_info **mtp = sm_modem_tx_table;
const struct modem_rx_info **mrp;
if (!strcmp(mode, "off")) {
sm->mode_tx = NULL;
sm->mode_rx = NULL;
return 0;
}
if (cp)
*cp++ = '\0';
else
cp = mode;
for (; *mtp; mtp++) {
if ((*mtp)->loc_storage > sizeof(sm->m)) {
printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
continue;
}
if (!(*mtp)->name || strcmp((*mtp)->name, mode))
continue;
if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100)
continue;
for (mrp = sm_modem_rx_table; *mrp; mrp++) {
if ((*mrp)->loc_storage > sizeof(sm->d)) {
printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
continue;
}
if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
(*mtp)->srate >= 5000 && (*mtp)->srate <= 44100 &&
(*mrp)->srate == (*mtp)->srate) {
sm->mode_tx = *mtp;
sm->mode_rx = *mrp;
SCSTATE->sr[0] = sm->mode_rx->srate;
SCSTATE->sr[1] = sm->mode_tx->srate;
sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
if (sm->dma.ifragsz < sm->mode_rx->overlap)
sm->dma.ifragsz = sm->mode_rx->overlap;
if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_u8) {
sm->dma.i16bit = 1;
sm->dma.o16bit = 0;
sm->dma.ifragsz <<= 1;
} else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_s16) {
sm->dma.i16bit = 0;
sm->dma.o16bit = 1;
sm->dma.ofragsz <<= 1;
} else {
printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname,
sm->mode_rx->name, sm->mode_tx->name);
sm->mode_tx = NULL;
sm->mode_rx = NULL;
return -EINVAL;
}
return 0;
}
}
}
return -EINVAL;
}
/* --------------------------------------------------------------------- */
static int sbcfdx_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *ifr,
struct hdlcdrv_ioctl *hi, int cmd)
{
if (cmd != SIOCDEVPRIVATE)
return -ENOIOCTLCMD;
if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 | HDLCDRV_PARMASK_SERIOBASE |
HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
return sbc_ioctl(dev, sm, ifr, hi, cmd);
}
/* --------------------------------------------------------------------- */
const struct hardware_info sm_hw_sbcfdx = {
"sbcfdx", sizeof(struct sc_state_sbc),
sbcfdx_open, sbcfdx_close, sbcfdx_ioctl, sbcfdx_sethw
};
/* --------------------------------------------------------------------- */
/*****************************************************************************/
/*
* sm_wss.c -- soundcard radio modem driver, WSS (half duplex) driver
*
* Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Please note that the GPL allows you to use the driver, NOT the radio.
* In order to use the radio, you need a license from the communications
* authority of your country.
*
*/
#include <linux/ptrace.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/soundmodem.h>
#include <asm/io.h>
#include <asm/dma.h>
#include "sm.h"
#include "smdma.h"
/* --------------------------------------------------------------------- */
/*
* currently this module is supposed to support both module styles, i.e.
* the old one present up to about 2.1.9, and the new one functioning
* starting with 2.1.21. The reason is I have a kit allowing to compile
* this module also under 2.0.x which was requested by several people.
* This will go in 2.2
*/
#include <linux/version.h>
#if LINUX_VERSION_CODE >= 0x20100
#include <asm/uaccess.h>
#else
#include <asm/segment.h>
#include <linux/mm.h>
#undef put_user
#undef get_user
#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
static inline int copy_from_user(void *to, const void *from, unsigned long n)
{
int i = verify_area(VERIFY_READ, from, n);
if (i)
return i;
memcpy_fromfs(to, from, n);
return 0;
}
static inline int copy_to_user(void *to, const void *from, unsigned long n)
{
int i = verify_area(VERIFY_WRITE, to, n);
if (i)
return i;
memcpy_tofs(to, from, n);
return 0;
}
#endif
/* --------------------------------------------------------------------- */
struct sc_state_wss {
unsigned char revwss, revid, revv, revcid;
unsigned char fmt[2];
unsigned char crystal;
};
#define SCSTATE ((struct sc_state_wss *)(&sm->hw))
/* --------------------------------------------------------------------- */
#define WSS_CONFIG(iobase) (iobase+0)
#define WSS_STATUS(iobase) (iobase+3)
#define WSS_CODEC_IA(iobase) (iobase+4)
#define WSS_CODEC_ID(iobase) (iobase+5)
#define WSS_CODEC_STATUS(iobase) (iobase+6)
#define WSS_CODEC_DATA(iobase) (iobase+7)
#define WSS_EXTENT 8
#define CS423X_HOTFIX
/* --------------------------------------------------------------------- */
static void write_codec(struct net_device *dev, unsigned char idx,
unsigned char data)
{
int timeout = 900000;
/* wait until codec ready */
while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
timeout--;
outb(idx, WSS_CODEC_IA(dev->base_addr));
outb(data, WSS_CODEC_ID(dev->base_addr));
}
/* --------------------------------------------------------------------- */
static unsigned char read_codec(struct net_device *dev, unsigned char idx)
{
int timeout = 900000;
/* wait until codec ready */
while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
timeout--;
outb(idx & 0x1f, WSS_CODEC_IA(dev->base_addr));
return inb(WSS_CODEC_ID(dev->base_addr));
}
/* --------------------------------------------------------------------- */
extern void inline wss_ack_int(struct net_device *dev)
{
outb(0, WSS_CODEC_STATUS(dev->base_addr));
}
/* --------------------------------------------------------------------- */
static int wss_srate_tab[16] = {
8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050,
-1, 37800, -1, 44100, 48000, 33075, 9600, 6620
};
static int wss_srate_index(int srate)
{
int i;
for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++)
if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0)
return i;
return -1;
}
/* --------------------------------------------------------------------- */
static int wss_set_codec_fmt(struct net_device *dev, struct sm_state *sm, unsigned char fmt,
unsigned char fmt2, char fdx, char fullcalib)
{
unsigned long time;
unsigned long flags;
save_flags(flags);
cli();
/* Clock and data format register */
write_codec(dev, 0x48, fmt);
if (SCSTATE->crystal) {
write_codec(dev, 0x5c, fmt2 & 0xf0);
/* MCE and interface config reg */
write_codec(dev, 0x49, (fdx ? 0 : 0x4) | (fullcalib ? 0x18 : 0));
} else
/* MCE and interface config reg */
write_codec(dev, 0x49, fdx ? 0x8 : 0xc);
outb(0xb, WSS_CODEC_IA(dev->base_addr)); /* leave MCE */
if (SCSTATE->crystal && !fullcalib) {
restore_flags(flags);
return 0;
}
/*
* wait for ACI start
*/
time = 1000;
while (!(read_codec(dev, 0x0b) & 0x20))
if (!(--time)) {
printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n",
sm_drvname);
restore_flags(flags);
return -1;
}
/*
* wait for ACI end
*/
sti();
time = jiffies + HZ/4;
while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0));
restore_flags(flags);
if ((signed)(jiffies - time) >= 0) {
printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n",
sm_drvname);
return -1;
}
return 0;
}
/* --------------------------------------------------------------------- */
static int wss_init_codec(struct net_device *dev, struct sm_state *sm, char fdx,
unsigned char src_l, unsigned char src_r,
int igain_l, int igain_r,
int ogain_l, int ogain_r)
{
unsigned char tmp, reg0, reg1, reg6, reg7;
static const signed char irqtab[16] =
{ -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1,
-1, -1 };
static const signed char dmatab[4] = { 1, 2, -1, 3 };
tmp = inb(WSS_STATUS(dev->base_addr));
if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 &&
(tmp & 0x3f) != 0x0f) {
printk(KERN_WARNING "sm: WSS card id register not found, "
"address 0x%lx, ID register 0x%02x\n",
dev->base_addr, (int)tmp);
/* return -1; */
SCSTATE->revwss = 0;
} else {
if ((tmp & 0x80) && ((dev->dma == 0) ||
((dev->irq >= 8) && (dev->irq != 9)))) {
printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 "
"(except IRQ9) cannot be used on an 8bit "
"card\n", sm_drvname);
return -1;
}
if (dev->irq > 15 || irqtab[dev->irq] == -1) {
printk(KERN_ERR "%s: WSS: invalid interrupt %d\n",
sm_drvname, (int)dev->irq);
return -1;
}
if (dev->dma > 3 || dmatab[dev->dma] == -1) {
printk(KERN_ERR "%s: WSS: invalid dma channel %d\n",
sm_drvname, (int)dev->dma);
return -1;
}
tmp = irqtab[dev->irq] | dmatab[dev->dma];
/* irq probe */
outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr));
if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) {
outb(0, WSS_CONFIG(dev->base_addr));
printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n",
sm_drvname, dev->irq);
}
outb(tmp, WSS_CONFIG(dev->base_addr));
SCSTATE->revwss = inb(WSS_STATUS(dev->base_addr)) & 0x3f;
}
/*
* initialize the codec
*/
if (igain_l < 0)
igain_l = 0;
if (igain_r < 0)
igain_r = 0;
if (ogain_l > 0)
ogain_l = 0;
if (ogain_r > 0)
ogain_r = 0;
reg0 = (src_l << 6) & 0xc0;
reg1 = (src_r << 6) & 0xc0;
if (reg0 == 0x80 && igain_l >= 20) {
reg0 |= 0x20;
igain_l -= 20;
}
if (reg1 == 0x80 && igain_r >= 20) {
reg1 |= 0x20;
igain_r -= 20;
}
if (igain_l > 23)
igain_l = 23;
if (igain_r > 23)
igain_r = 23;
reg0 |= igain_l * 2 / 3;
reg1 |= igain_r * 2 / 3;
reg6 = (ogain_l < -95) ? 0x80 : (ogain_l * (-2) / 3);
reg7 = (ogain_r < -95) ? 0x80 : (ogain_r * (-2) / 3);
write_codec(dev, 9, 0);
write_codec(dev, 0, 0x45);
if (read_codec(dev, 0) != 0x45)
goto codec_err;
write_codec(dev, 0, 0xaa);
if (read_codec(dev, 0) != 0xaa)
goto codec_err;
write_codec(dev, 12, 0x40); /* enable MODE2 */
write_codec(dev, 16, 0);
write_codec(dev, 0, 0x45);
SCSTATE->crystal = (read_codec(dev, 16) != 0x45);
write_codec(dev, 0, 0xaa);
SCSTATE->crystal &= (read_codec(dev, 16) != 0xaa);
if (SCSTATE->crystal) {
SCSTATE->revcid = read_codec(dev, 0x19);
SCSTATE->revv = (SCSTATE->revcid >> 5) & 7;
SCSTATE->revcid &= 7;
write_codec(dev, 0x10, 0x80); /* maximum output level */
write_codec(dev, 0x11, 0x02); /* xtal enable and no HPF */
write_codec(dev, 0x12, 0x80); /* left line input control */
write_codec(dev, 0x13, 0x80); /* right line input control */
write_codec(dev, 0x16, 0); /* disable alternative freq sel */
write_codec(dev, 0x1a, 0xe0); /* mono IO disable */
write_codec(dev, 0x1b, 0x00); /* left out no att */
write_codec(dev, 0x1d, 0x00); /* right out no att */
}
if (wss_set_codec_fmt(dev, sm, SCSTATE->fmt[0], SCSTATE->fmt[0], fdx, 1))
goto codec_err;
write_codec(dev, 0, reg0); /* left input control */
write_codec(dev, 1, reg1); /* right input control */
write_codec(dev, 2, 0x80); /* left aux#1 input control */
write_codec(dev, 3, 0x80); /* right aux#1 input control */
write_codec(dev, 4, 0x80); /* left aux#2 input control */
write_codec(dev, 5, 0x80); /* right aux#2 input control */
write_codec(dev, 6, reg6); /* left dac control */
write_codec(dev, 7, reg7); /* right dac control */
write_codec(dev, 0xa, 0x2); /* pin control register */
write_codec(dev, 0xd, 0x0); /* digital mix control */
SCSTATE->revid = read_codec(dev, 0xc) & 0xf;
/*
* print revisions
*/
if (SCSTATE->crystal)
printk(KERN_INFO "%s: Crystal CODEC ID %d, Chip revision %d, "
" Chip ID %d\n", sm_drvname, (int)SCSTATE->revid,
(int)SCSTATE->revv, (int)SCSTATE->revcid);
else
printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n",
sm_drvname, (int)SCSTATE->revwss,
(int)SCSTATE->revid);
return 0;
codec_err:
outb(0, WSS_CONFIG(dev->base_addr));
printk(KERN_ERR "%s: no WSS soundcard found at address 0x%lx\n",
sm_drvname, dev->base_addr);
return -1;
}
/* --------------------------------------------------------------------- */
static void setup_dma_wss(struct net_device *dev, struct sm_state *sm, int send)
{
unsigned long flags;
static const unsigned char codecmode[2] = { 0x0e, 0x0d };
unsigned char oldcodecmode;
long abrt;
unsigned char fmt;
unsigned int numsamps;
send = !!send;
fmt = SCSTATE->fmt[send];
save_flags(flags);
cli();
/*
* perform the final DMA sequence to disable the codec request
*/
oldcodecmode = read_codec(dev, 9);
write_codec(dev, 9, 0xc); /* disable codec */
wss_ack_int(dev);
if (read_codec(dev, 11) & 0x10) {
dma_setup(sm, oldcodecmode & 1, dev->dma);
abrt = 0;
while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000));
}
#ifdef CS423X_HOTFIX
if (read_codec(dev, 0x8) != fmt || SCSTATE->crystal)
wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
#else /* CS423X_HOTFIX */
if (read_codec(dev, 0x8) != fmt)
wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
#endif /* CS423X_HOTFIX */
numsamps = dma_setup(sm, send, dev->dma) - 1;
write_codec(dev, 15, numsamps & 0xff);
write_codec(dev, 14, numsamps >> 8);
write_codec(dev, 9, codecmode[send]);
restore_flags(flags);
}
/* --------------------------------------------------------------------- */
static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct net_device *dev = (struct net_device *)dev_id;
struct sm_state *sm = (struct sm_state *)dev->priv;
unsigned int curfrag;
unsigned int nums;
if (!dev || !sm || !sm->mode_rx || !sm->mode_tx ||
sm->hdrv.magic != HDLCDRV_MAGIC)
return;
cli();
wss_ack_int(dev);
disable_dma(dev->dma);
clear_dma_ff(dev->dma);
nums = dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag) - 1;
write_codec(dev, 15, nums & 0xff);
write_codec(dev, 14, nums >> 8);
enable_dma(dev->dma);
sm_int_freq(sm);
sti();
if (sm->dma.ptt_cnt <= 0) {
dma_receive(sm, curfrag);
hdlcdrv_arbitrate(dev, &sm->hdrv);
if (hdlcdrv_ptt(&sm->hdrv)) {
/* starting to transmit */
disable_dma(dev->dma);
hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
dma_start_transmit(sm);
setup_dma_wss(dev, sm, 1);
dma_transmit(sm);
}
} else if (dma_end_transmit(sm, curfrag)) {
/* stopping transmission */
disable_dma(dev->dma);
dma_init_receive(sm);
setup_dma_wss(dev, sm, 0);
} else
dma_transmit(sm);
sm_output_status(sm);
hdlcdrv_transmitter(dev, &sm->hdrv);
hdlcdrv_receiver(dev, &sm->hdrv);
}
/* --------------------------------------------------------------------- */
static int wss_open(struct net_device *dev, struct sm_state *sm)
{
unsigned int dmasz, u;
if (sizeof(sm->m) < sizeof(struct sc_state_wss)) {
printk(KERN_ERR "sm wss: wss state too big: %d > %d\n",
sizeof(struct sc_state_wss), sizeof(sm->m));
return -ENODEV;
}
if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
return -ENXIO;
if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT ||
dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
return -ENXIO;
if (check_region(dev->base_addr, WSS_EXTENT))
return -EACCES;
/*
* check if a card is available
*/
if (wss_init_codec(dev, sm, 0, 1, 1, 0, 0, -45, -45))
return -ENODEV;
/*
* initialize some variables
*/
dma_init_receive(sm);
dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
u = NUM_FRAGMENTS * sm->dma.ofragsz;
if (u > dmasz)
dmasz = u;
if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
return -ENOMEM;
dma_init_transmit(sm);
dma_init_receive(sm);
memset(&sm->m, 0, sizeof(sm->m));
memset(&sm->d, 0, sizeof(sm->d));
if (sm->mode_tx->init)
sm->mode_tx->init(sm);
if (sm->mode_rx->init)
sm->mode_rx->init(sm);
if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
kfree(sm->dma.obuf);
return -EBUSY;
}
if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT,
sm->hwdrv->hw_name, dev)) {
free_dma(dev->dma);
kfree(sm->dma.obuf);
return -EBUSY;
}
request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
setup_dma_wss(dev, sm, 0);
return 0;
}
/* --------------------------------------------------------------------- */
static int wss_close(struct net_device *dev, struct sm_state *sm)
{
if (!dev || !sm)
return -EINVAL;
/*
* disable interrupts
*/
disable_dma(dev->dma);
write_codec(dev, 9, 0xc); /* disable codec */
free_irq(dev->irq, dev);
free_dma(dev->dma);
release_region(dev->base_addr, WSS_EXTENT);
kfree(sm->dma.obuf);
return 0;
}
/* --------------------------------------------------------------------- */
static int wss_sethw(struct net_device *dev, struct sm_state *sm, char *mode)
{
char *cp = strchr(mode, '.');
const struct modem_tx_info **mtp = sm_modem_tx_table;
const struct modem_rx_info **mrp;
int i, j;
if (!strcmp(mode, "off")) {
sm->mode_tx = NULL;
sm->mode_rx = NULL;
return 0;
}
if (cp)
*cp++ = '\0';
else
cp = mode;
for (; *mtp; mtp++) {
if ((*mtp)->loc_storage > sizeof(sm->m)) {
printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
continue;
}
if (!(*mtp)->name || strcmp((*mtp)->name, mode))
continue;
if ((i = wss_srate_index((*mtp)->srate)) < 0)
continue;
for (mrp = sm_modem_rx_table; *mrp; mrp++) {
if ((*mrp)->loc_storage > sizeof(sm->d)) {
printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
continue;
}
if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
((j = wss_srate_index((*mrp)->srate)) >= 0)) {
sm->mode_tx = *mtp;
sm->mode_rx = *mrp;
SCSTATE->fmt[0] = j;
SCSTATE->fmt[1] = i;
sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
if (sm->dma.ifragsz < sm->mode_rx->overlap)
sm->dma.ifragsz = sm->mode_rx->overlap;
/* prefer same data format if possible to minimize switching times */
sm->dma.i16bit = sm->dma.o16bit = 2;
if (sm->mode_rx->srate == sm->mode_tx->srate) {
if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_s16)
sm->dma.i16bit = sm->dma.o16bit = 1;
else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_u8)
sm->dma.i16bit = sm->dma.o16bit = 0;
}
if (sm->dma.i16bit == 2) {
if (sm->mode_rx->demodulator_s16)
sm->dma.i16bit = 1;
else if (sm->mode_rx->demodulator_u8)
sm->dma.i16bit = 0;
}
if (sm->dma.o16bit == 2) {
if (sm->mode_tx->modulator_s16)
sm->dma.o16bit = 1;
else if (sm->mode_tx->modulator_u8)
sm->dma.o16bit = 0;
}
if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) {
printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname,
sm->mode_rx->name, sm->mode_tx->name);
sm->mode_tx = NULL;
sm->mode_rx = NULL;
return -EINVAL;
}
#ifdef __BIG_ENDIAN
/* big endian 16bit only works on crystal cards... */
if (sm->dma.i16bit) {
SCSTATE->fmt[0] |= 0xc0;
sm->dma.ifragsz <<= 1;
}
if (sm->dma.o16bit) {
SCSTATE->fmt[1] |= 0xc0;
sm->dma.ofragsz <<= 1;
}
#else /* __BIG_ENDIAN */
if (sm->dma.i16bit) {
SCSTATE->fmt[0] |= 0x40;
sm->dma.ifragsz <<= 1;
}
if (sm->dma.o16bit) {
SCSTATE->fmt[1] |= 0x40;
sm->dma.ofragsz <<= 1;
}
#endif /* __BIG_ENDIAN */
return 0;
}
}
}
return -EINVAL;
}
/* --------------------------------------------------------------------- */
static int wss_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *ifr,
struct hdlcdrv_ioctl *hi, int cmd)
{
struct sm_ioctl bi;
int i;
if (cmd != SIOCDEVPRIVATE)
return -ENOIOCTLCMD;
if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE |
HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
return -EFAULT;
switch (bi.cmd) {
default:
return -ENOIOCTLCMD;
case SMCTL_GETMIXER:
i = 0;
bi.data.mix.sample_rate = sm->mode_rx->srate;
bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
bi.data.mix.mixer_type = SCSTATE->crystal ?
SM_MIXER_CRYSTAL : SM_MIXER_AD1848;
if (((SCSTATE->crystal ? 0x2c0c20fflu: 0x20fflu)
>> bi.data.mix.reg) & 1) {
bi.data.mix.data = read_codec(dev, bi.data.mix.reg);
i = 1;
}
if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
return -EFAULT;
return i;
case SMCTL_SETMIXER:
if (!capable(CAP_SYS_RAWIO))
return -EACCES;
if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL ||
!SCSTATE->crystal) &&
(bi.data.mix.mixer_type != SM_MIXER_AD1848 ||
bi.data.mix.reg >= 0x10))
return -EINVAL;
if (!((0x2c0c20fflu >> bi.data.mix.reg) & 1))
return -EACCES;
write_codec(dev, bi.data.mix.reg, bi.data.mix.data);
return 0;
}
if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
return -EFAULT;
return 0;
}
/* --------------------------------------------------------------------- */
const struct hardware_info sm_hw_wss = {
"wss", sizeof(struct sc_state_wss),
wss_open, wss_close, wss_ioctl, wss_sethw
};
/* --------------------------------------------------------------------- */
static void setup_fdx_dma_wss(struct net_device *dev, struct sm_state *sm)
{
unsigned long flags;
unsigned char oldcodecmode, codecdma;
long abrt;
unsigned int osamps, isamps;
save_flags(flags);
cli();
/*
* perform the final DMA sequence to disable the codec request
*/
oldcodecmode = read_codec(dev, 9);
write_codec(dev, 9, 0); /* disable codec DMA */
wss_ack_int(dev);
if ((codecdma = read_codec(dev, 11)) & 0x10) {
dma_setup(sm, 1, dev->dma);
dma_setup(sm, 0, sm->hdrv.ptt_out.dma2);
abrt = 0;
while (((codecdma = read_codec(dev, 11)) & 0x10) || ((++abrt) >= 0x10000));
}
wss_set_codec_fmt(dev, sm, SCSTATE->fmt[1], SCSTATE->fmt[0], 1, 1);
osamps = dma_setup(sm, 1, dev->dma) - 1;
isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1;
write_codec(dev, 15, osamps & 0xff);
write_codec(dev, 14, osamps >> 8);
if (SCSTATE->crystal) {
write_codec(dev, 31, isamps & 0xff);
write_codec(dev, 30, isamps >> 8);
}
write_codec(dev, 9, 3);
restore_flags(flags);
}
/* --------------------------------------------------------------------- */
static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct net_device *dev = (struct net_device *)dev_id;
struct sm_state *sm = (struct sm_state *)dev->priv;
unsigned long flags;
unsigned char cry_int_src;
unsigned icfrag, ocfrag, isamps, osamps;
if (!dev || !sm || !sm->mode_rx || !sm->mode_tx ||
sm->hdrv.magic != HDLCDRV_MAGIC)
return;
save_flags(flags);
cli();
if (SCSTATE->crystal) {
/* Crystal has an essentially different interrupt handler! */
cry_int_src = read_codec(dev, 0x18);
wss_ack_int(dev);
if (cry_int_src & 0x10) { /* playback interrupt */
disable_dma(dev->dma);
clear_dma_ff(dev->dma);
osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1;
write_codec(dev, 15, osamps & 0xff);
write_codec(dev, 14, osamps >> 8);
enable_dma(dev->dma);
}
if (cry_int_src & 0x20) { /* capture interrupt */
disable_dma(sm->hdrv.ptt_out.dma2);
clear_dma_ff(sm->hdrv.ptt_out.dma2);
isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1;
write_codec(dev, 31, isamps & 0xff);
write_codec(dev, 30, isamps >> 8);
enable_dma(sm->hdrv.ptt_out.dma2);
}
restore_flags(flags);
sm_int_freq(sm);
sti();
if (cry_int_src & 0x10) {
if (dma_end_transmit(sm, ocfrag))
dma_clear_transmit(sm);
dma_transmit(sm);
}
if (cry_int_src & 0x20) {
dma_receive(sm, icfrag);
hdlcdrv_arbitrate(dev, &sm->hdrv);
}
sm_output_status(sm);
hdlcdrv_transmitter(dev, &sm->hdrv);
hdlcdrv_receiver(dev, &sm->hdrv);
return;
}
wss_ack_int(dev);
disable_dma(dev->dma);
disable_dma(sm->hdrv.ptt_out.dma2);
clear_dma_ff(dev->dma);
clear_dma_ff(sm->hdrv.ptt_out.dma2);
osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1;
isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1;
write_codec(dev, 15, osamps & 0xff);
write_codec(dev, 14, osamps >> 8);
if (SCSTATE->crystal) {
write_codec(dev, 31, isamps & 0xff);
write_codec(dev, 30, isamps >> 8);
}
enable_dma(dev->dma);
enable_dma(sm->hdrv.ptt_out.dma2);
restore_flags(flags);
sm_int_freq(sm);
sti();
if (dma_end_transmit(sm, ocfrag))
dma_clear_transmit(sm);
dma_transmit(sm);
dma_receive(sm, icfrag);
hdlcdrv_arbitrate(dev, &sm->hdrv);
sm_output_status(sm);
hdlcdrv_transmitter(dev, &sm->hdrv);
hdlcdrv_receiver(dev, &sm->hdrv);
}
/* --------------------------------------------------------------------- */
static int wssfdx_open(struct net_device *dev, struct sm_state *sm)
{
if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
return -ENXIO;
if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT ||
dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
return -ENXIO;
if (check_region(dev->base_addr, WSS_EXTENT))
return -EACCES;
/*
* check if a card is available
*/
if (wss_init_codec(dev, sm, 1, 1, 1, 0, 0, -45, -45))
return -ENODEV;
/*
* initialize some variables
*/
if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA)))
return -ENOMEM;
if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) {
kfree(sm->dma.ibuf);
return -ENOMEM;
}
dma_init_transmit(sm);
dma_init_receive(sm);
memset(&sm->m, 0, sizeof(sm->m));
memset(&sm->d, 0, sizeof(sm->d));
if (sm->mode_tx->init)
sm->mode_tx->init(sm);
if (sm->mode_rx->init)
sm->mode_rx->init(sm);
if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
kfree(sm->dma.ibuf);
kfree(sm->dma.obuf);
return -EBUSY;
}
if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
kfree(sm->dma.ibuf);
kfree(sm->dma.obuf);
free_dma(dev->dma);
return -EBUSY;
}
if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT,
sm->hwdrv->hw_name, dev)) {
kfree(sm->dma.ibuf);
kfree(sm->dma.obuf);
free_dma(dev->dma);
free_dma(sm->hdrv.ptt_out.dma2);
return -EBUSY;
}
request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
setup_fdx_dma_wss(dev, sm);
return 0;
}
/* --------------------------------------------------------------------- */
static int wssfdx_close(struct net_device *dev, struct sm_state *sm)
{
if (!dev || !sm)
return -EINVAL;
/*
* disable interrupts
*/
disable_dma(dev->dma);
disable_dma(sm->hdrv.ptt_out.dma2);
write_codec(dev, 9, 0xc); /* disable codec */
free_irq(dev->irq, dev);
free_dma(dev->dma);
free_dma(sm->hdrv.ptt_out.dma2);
release_region(dev->base_addr, WSS_EXTENT);
kfree(sm->dma.ibuf);
kfree(sm->dma.obuf);
return 0;
}
/* --------------------------------------------------------------------- */
static int wssfdx_sethw(struct net_device *dev, struct sm_state *sm, char *mode)
{
char *cp = strchr(mode, '.');
const struct modem_tx_info **mtp = sm_modem_tx_table;
const struct modem_rx_info **mrp;
int i;
if (!strcmp(mode, "off")) {
sm->mode_tx = NULL;
sm->mode_rx = NULL;
return 0;
}
if (cp)
*cp++ = '\0';
else
cp = mode;
for (; *mtp; mtp++) {
if ((*mtp)->loc_storage > sizeof(sm->m)) {
printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
continue;
}
if (!(*mtp)->name || strcmp((*mtp)->name, mode))
continue;
if ((i = wss_srate_index((*mtp)->srate)) < 0)
continue;
for (mrp = sm_modem_rx_table; *mrp; mrp++) {
if ((*mrp)->loc_storage > sizeof(sm->d)) {
printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
continue;
}
if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
(*mtp)->srate == (*mrp)->srate) {
sm->mode_tx = *mtp;
sm->mode_rx = *mrp;
SCSTATE->fmt[0] = SCSTATE->fmt[1] = i;
sm->dma.ifragsz = sm->dma.ofragsz = (sm->mode_rx->srate + 50)/100;
if (sm->dma.ifragsz < sm->mode_rx->overlap)
sm->dma.ifragsz = sm->mode_rx->overlap;
sm->dma.i16bit = sm->dma.o16bit = 2;
if (sm->mode_rx->demodulator_s16) {
sm->dma.i16bit = 1;
sm->dma.ifragsz <<= 1;
#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */
SCSTATE->fmt[0] |= 0xc0;
#else /* __BIG_ENDIAN */
SCSTATE->fmt[0] |= 0x40;
#endif /* __BIG_ENDIAN */
} else if (sm->mode_rx->demodulator_u8)
sm->dma.i16bit = 0;
if (sm->mode_tx->modulator_s16) {
sm->dma.o16bit = 1;
sm->dma.ofragsz <<= 1;
#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */
SCSTATE->fmt[1] |= 0xc0;
#else /* __BIG_ENDIAN */
SCSTATE->fmt[1] |= 0x40;
#endif /* __BIG_ENDIAN */
} else if (sm->mode_tx->modulator_u8)
sm->dma.o16bit = 0;
if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) {
printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname,
sm->mode_rx->name, sm->mode_tx->name);
sm->mode_tx = NULL;
sm->mode_rx = NULL;
return -EINVAL;
}
return 0;
}
}
}
return -EINVAL;
}
/* --------------------------------------------------------------------- */
static int wssfdx_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *ifr,
struct hdlcdrv_ioctl *hi, int cmd)
{
if (cmd != SIOCDEVPRIVATE)
return -ENOIOCTLCMD;
if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 |
HDLCDRV_PARMASK_SERIOBASE | HDLCDRV_PARMASK_PARIOBASE |
HDLCDRV_PARMASK_MIDIIOBASE;
return wss_ioctl(dev, sm, ifr, hi, cmd);
}
/* --------------------------------------------------------------------- */
const struct hardware_info sm_hw_wssfdx = {
"wssfdx", sizeof(struct sc_state_wss),
wssfdx_open, wssfdx_close, wssfdx_ioctl, wssfdx_sethw
};
/* --------------------------------------------------------------------- */
#undef SCSTATE
/*****************************************************************************/
/*
* smdma.h -- soundcard radio modem driver dma buffer routines.
*
* Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Please note that the GPL allows you to use the driver, NOT the radio.
* In order to use the radio, you need a license from the communications
* authority of your country.
*
*/
#ifndef _SMDMA_H
#define _SMDMA_H
/* ---------------------------------------------------------------------- */
#include "sm.h"
/* ---------------------------------------------------------------------- */
#define DMA_MODE_AUTOINIT 0x10
#define NUM_FRAGMENTS 4
/*
* NOTE: make sure that hdlcdrv_hdlcbuffer contains enough space
* for the modulator to fill the whole DMA buffer without underrun
* at the highest possible baud rate, otherwise the TX state machine will
* not work correctly. That is (9k6 FSK): HDLCDRV_HDLCBUFFER > 6*NUM_FRAGMENTS
*/
/* --------------------------------------------------------------------- */
/*
* ===================== DMA buffer management ===========================
*/
/*
* returns the number of samples per fragment
*/
static __inline__ unsigned int dma_setup(struct sm_state *sm, int send, unsigned int dmanr)
{
if (send) {
disable_dma(dmanr);
clear_dma_ff(dmanr);
set_dma_mode(dmanr, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
set_dma_addr(dmanr, virt_to_bus(sm->dma.obuf));
set_dma_count(dmanr, sm->dma.ofragsz * NUM_FRAGMENTS);
enable_dma(dmanr);
if (sm->dma.o16bit)
return sm->dma.ofragsz/2;
return sm->dma.ofragsz;
} else {
disable_dma(dmanr);
clear_dma_ff(dmanr);
set_dma_mode(dmanr, DMA_MODE_READ | DMA_MODE_AUTOINIT);
set_dma_addr(dmanr, virt_to_bus(sm->dma.ibuf));
set_dma_count(dmanr, sm->dma.ifragsz * NUM_FRAGMENTS);
enable_dma(dmanr);
if (sm->dma.i16bit)
return sm->dma.ifragsz/2;
return sm->dma.ifragsz;
}
}
/* --------------------------------------------------------------------- */
static __inline__ unsigned int dma_ptr(struct sm_state *sm, int send, unsigned int dmanr,
unsigned int *curfrag)
{
unsigned int dmaptr, sz, frg, offs;
dmaptr = get_dma_residue(dmanr);
if (send) {
sz = sm->dma.ofragsz * NUM_FRAGMENTS;
if (dmaptr == 0 || dmaptr > sz)
dmaptr = sz;
dmaptr--;
frg = dmaptr / sm->dma.ofragsz;
offs = (dmaptr % sm->dma.ofragsz) + 1;
*curfrag = NUM_FRAGMENTS - 1 - frg;
#ifdef SM_DEBUG
if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue)
sm->debug_vals.dma_residue = offs;
#endif /* SM_DEBUG */
if (sm->dma.o16bit)
return offs/2;
return offs;
} else {
sz = sm->dma.ifragsz * NUM_FRAGMENTS;
if (dmaptr == 0 || dmaptr > sz)
dmaptr = sz;
dmaptr--;
frg = dmaptr / sm->dma.ifragsz;
offs = (dmaptr % sm->dma.ifragsz) + 1;
*curfrag = NUM_FRAGMENTS - 1 - frg;
#ifdef SM_DEBUG
if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue)
sm->debug_vals.dma_residue = offs;
#endif /* SM_DEBUG */
if (sm->dma.i16bit)
return offs/2;
return offs;
}
}
/* --------------------------------------------------------------------- */
static __inline__ int dma_end_transmit(struct sm_state *sm, unsigned int curfrag)
{
unsigned int diff = (NUM_FRAGMENTS + curfrag - sm->dma.ofragptr) % NUM_FRAGMENTS;
sm->dma.ofragptr = curfrag;
if (sm->dma.ptt_cnt <= 0) {
sm->dma.ptt_cnt = 0;
return 0;
}
sm->dma.ptt_cnt -= diff;
if (sm->dma.ptt_cnt <= 0) {
sm->dma.ptt_cnt = 0;
return -1;
}
return 0;
}
static __inline__ void dma_transmit(struct sm_state *sm)
{
void *p;
while (sm->dma.ptt_cnt < NUM_FRAGMENTS && hdlcdrv_ptt(&sm->hdrv)) {
p = (unsigned char *)sm->dma.obuf + sm->dma.ofragsz *
((sm->dma.ofragptr + sm->dma.ptt_cnt) % NUM_FRAGMENTS);
if (sm->dma.o16bit) {
time_exec(sm->debug_vals.mod_cyc,
sm->mode_tx->modulator_s16(sm, p, sm->dma.ofragsz/2));
} else {
time_exec(sm->debug_vals.mod_cyc,
sm->mode_tx->modulator_u8(sm, p, sm->dma.ofragsz));
}
sm->dma.ptt_cnt++;
}
}
static __inline__ void dma_init_transmit(struct sm_state *sm)
{
sm->dma.ofragptr = 0;
sm->dma.ptt_cnt = 0;
}
static __inline__ void dma_start_transmit(struct sm_state *sm)
{
sm->dma.ofragptr = 0;
if (sm->dma.o16bit) {
time_exec(sm->debug_vals.mod_cyc,
sm->mode_tx->modulator_s16(sm, sm->dma.obuf, sm->dma.ofragsz/2));
} else {
time_exec(sm->debug_vals.mod_cyc,
sm->mode_tx->modulator_u8(sm, sm->dma.obuf, sm->dma.ofragsz));
}
sm->dma.ptt_cnt = 1;
}
static __inline__ void dma_clear_transmit(struct sm_state *sm)
{
sm->dma.ptt_cnt = 0;
memset(sm->dma.obuf, (sm->dma.o16bit) ? 0 : 0x80, sm->dma.ofragsz * NUM_FRAGMENTS);
}
/* --------------------------------------------------------------------- */
static __inline__ void dma_receive(struct sm_state *sm, unsigned int curfrag)
{
void *p;
while (sm->dma.ifragptr != curfrag) {
if (sm->dma.ifragptr)
p = (unsigned char *)sm->dma.ibuf +
sm->dma.ifragsz * sm->dma.ifragptr;
else {
p = (unsigned char *)sm->dma.ibuf + NUM_FRAGMENTS * sm->dma.ifragsz;
memcpy(p, sm->dma.ibuf, sm->dma.ifragsz);
}
if (sm->dma.o16bit) {
time_exec(sm->debug_vals.demod_cyc,
sm->mode_rx->demodulator_s16(sm, p, sm->dma.ifragsz/2));
} else {
time_exec(sm->debug_vals.demod_cyc,
sm->mode_rx->demodulator_u8(sm, p, sm->dma.ifragsz));
}
sm->dma.ifragptr = (sm->dma.ifragptr + 1) % NUM_FRAGMENTS;
}
}
static __inline__ void dma_init_receive(struct sm_state *sm)
{
sm->dma.ifragptr = 0;
}
/* --------------------------------------------------------------------- */
#endif /* _SMDMA_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment