URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/uclinux/uClinux-2.0.x/drivers/net/soundmodem
- from Rev 199 to Rev 1765
- ↔ Reverse comparison
Rev 199 → Rev 1765
/sm.c
0,0 → 1,899
/*****************************************************************************/ |
|
/* |
* sm.c -- soundcard radio modem driver. |
* |
* Copyright (C) 1996-1998 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.96 Started |
* 18.10.96 Changed to new user space access routines (copy_{to,from}_user) |
* 0.4 21.01.97 Separately compileable soundcard/modem modules |
* 0.5 03.03.97 fixed LPT probing (check_lpt result was interpreted the wrong way round) |
* 0.6 16.04.97 init code/data tagged |
* 0.7 30.07.97 fixed halfduplex interrupt handlers/hotfix for CS423X |
* 0.8 14.04.98 cleanups |
*/ |
|
/*****************************************************************************/ |
|
#include <linux/config.h> |
#include <linux/module.h> |
#include <linux/ptrace.h> |
#include <linux/types.h> |
#include <linux/fcntl.h> |
#include <linux/ioport.h> |
#include <linux/net.h> |
#include <linux/in.h> |
#include <linux/string.h> |
#include <asm/system.h> |
#include <asm/io.h> |
#include <asm/bitops.h> |
#include <linux/delay.h> |
#include <linux/errno.h> |
#include "sm.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; }) |
|
extern 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; |
} |
|
extern 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 |
|
#if LINUX_VERSION_CODE >= 0x20123 |
#include <linux/init.h> |
#else |
#define __init |
#define __initdata |
#define __initfunc(x) x |
#endif |
|
/* --------------------------------------------------------------------- */ |
|
/*static*/ const char sm_drvname[] = "soundmodem"; |
static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-1998 Thomas Sailer, HB9JNX/AE4WA\n" |
KERN_INFO "soundmodem: version 0.8 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 device sm_device[NR_PORTS]; |
|
static struct { |
char *mode; |
int iobase, irq, dma, dma2, seriobase, pariobase, midiiobase; |
} sm_ports[NR_PORTS] = { |
{ NULL, -1, 0, 0, 0, -1, -1, -1 }, |
}; |
|
/* --------------------------------------------------------------------- */ |
|
#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 LPT_DATA(iobase) (iobase+0) |
#define LPT_STATUS(iobase) (iobase+1) |
#define LPT_CONTROL(iobase) (iobase+2) |
#define LPT_IRQ_ENABLE 0x10 |
|
#define LPT_EXTENT 3 |
|
#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 ======================== |
*/ |
|
/* |
* returns 0 if ok and != 0 on error; |
* the same behaviour as par96_check_lpt in baycom.c |
*/ |
|
/* |
* returns 0 if ok and != 0 on error; |
* the same behaviour as par96_check_lpt in baycom.c |
*/ |
|
static int check_lpt(unsigned int iobase) |
{ |
unsigned char b1,b2; |
int i; |
|
if (iobase <= 0 || iobase > 0x1000-LPT_EXTENT) |
return 0; |
if (check_region(iobase, LPT_EXTENT)) |
return 0; |
b1 = inb(LPT_DATA(iobase)); |
b2 = inb(LPT_CONTROL(iobase)); |
outb(0xaa, LPT_DATA(iobase)); |
i = inb(LPT_DATA(iobase)) == 0xaa; |
outb(0x55, LPT_DATA(iobase)); |
i &= inb(LPT_DATA(iobase)) == 0x55; |
outb(0x0a, LPT_CONTROL(iobase)); |
i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a; |
outb(0x05, LPT_CONTROL(iobase)); |
i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05; |
outb(b1, LPT_DATA(iobase)); |
outb(b2, LPT_CONTROL(iobase)); |
return !i; |
} |
|
/* --------------------------------------------------------------------- */ |
|
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) { |
outb(ptt | (dcd << 1), LPT_DATA(sm->hdrv.ptt_out.pariobase)); |
} |
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) |
{ |
enum uart u = c_uart_unknown; |
|
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 */ |
} |
if (sm->hdrv.ptt_out.pariobase > 0 && |
sm->hdrv.ptt_out.pariobase <= 0x1000-LPT_EXTENT && |
!check_lpt(sm->hdrv.ptt_out.pariobase)) { |
sm->hdrv.ptt_out.flags |= SP_PAR; |
request_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT, "sm par ptt"); |
} |
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) |
release_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT); |
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 device *dev); |
static int sm_close(struct device *dev); |
static int sm_ioctl(struct 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 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); |
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 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 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 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 (dev->start || !suser()) |
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("%s:,", (*hwp)->hw_name); |
hwp++; |
} |
while (*mtp) { |
if ((*mtp)->name) |
cp += sprintf(">%s,", (*mtp)->name); |
mtp++; |
} |
while (*mrp) { |
if ((*mrp)->name) |
cp += sprintf("<%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; |
} |
} |
|
/* --------------------------------------------------------------------- */ |
|
#ifdef __i386__ |
|
int sm_x86_capability = 0; |
|
__initfunc(static void i386_capability(void)) |
{ |
unsigned long flags; |
unsigned long fl1; |
union { |
struct { |
unsigned int ebx, edx, ecx; |
} r; |
unsigned char s[13]; |
} id; |
unsigned int eax; |
|
save_flags(flags); |
flags |= 0x200000; |
restore_flags(flags); |
save_flags(flags); |
fl1 = flags; |
flags &= ~0x200000; |
restore_flags(flags); |
save_flags(flags); |
if (!(fl1 & 0x200000) || (flags & 0x200000)) { |
printk(KERN_WARNING "%s: cpu does not support CPUID\n", sm_drvname); |
return; |
} |
__asm__ ("cpuid" : "=a" (eax), "=b" (id.r.ebx), "=c" (id.r.ecx), "=d" (id.r.edx) : |
"0" (0)); |
id.s[12] = 0; |
if (eax < 1) { |
printk(KERN_WARNING "%s: cpu (vendor string %s) does not support capability " |
"list\n", sm_drvname, id.s); |
return; |
} |
printk(KERN_INFO "%s: cpu: vendor string %s ", sm_drvname, id.s); |
__asm__ ("cpuid" : "=a" (eax), "=d" (sm_x86_capability) : "0" (1) : "ebx", "ecx"); |
printk("fam %d mdl %d step %d cap 0x%x\n", (eax >> 8) & 15, (eax >> 4) & 15, |
eax & 15, sm_x86_capability); |
} |
#endif /* __i386__ */ |
|
/* --------------------------------------------------------------------- */ |
|
#ifdef MODULE |
__initfunc(static int sm_init(void)) |
#else /* MODULE */ |
__initfunc(int sm_init(void)) |
#endif /* MODULE */ |
{ |
int i, j, found = 0; |
char set_hw = 1; |
struct sm_state *sm; |
char ifname[HDLCDRV_IFNAMELEN]; |
|
printk(sm_drvinfo); |
#ifdef __i386__ |
i386_capability(); |
#endif /* __i386__ */ |
/* |
* register net devices |
*/ |
for (i = 0; i < NR_PORTS; i++) { |
struct device *dev = sm_device+i; |
sprintf(ifname, "sm%d", i); |
|
if (!sm_ports[i].mode) |
set_hw = 0; |
if (!set_hw) |
sm_ports[i].iobase = sm_ports[i].irq = 0; |
j = hdlcdrv_register_hdlcdrv(dev, &sm_ops, sizeof(struct sm_state), |
ifname, sm_ports[i].iobase, |
sm_ports[i].irq, sm_ports[i].dma); |
if (!j) { |
sm = (struct sm_state *)dev->priv; |
sm->hdrv.ptt_out.dma2 = sm_ports[i].dma2; |
sm->hdrv.ptt_out.seriobase = sm_ports[i].seriobase; |
sm->hdrv.ptt_out.pariobase = sm_ports[i].pariobase; |
sm->hdrv.ptt_out.midiiobase = sm_ports[i].midiiobase; |
if (set_hw && sethw(dev, sm, sm_ports[i].mode)) |
set_hw = 0; |
found++; |
} else { |
printk(KERN_WARNING "%s: cannot register net device\n", |
sm_drvname); |
} |
} |
if (!found) |
return -ENXIO; |
return 0; |
} |
|
/* --------------------------------------------------------------------- */ |
|
#ifdef MODULE |
|
/* |
* command line settable parameters |
*/ |
static char *mode = NULL; |
static int iobase = -1; |
static int irq = -1; |
static int dma = -1; |
static int dma2 = -1; |
static int serio = 0; |
static int pario = 0; |
static int midiio = 0; |
|
#if LINUX_VERSION_CODE >= 0x20115 |
|
MODULE_PARM(mode, "s"); |
MODULE_PARM_DESC(mode, "soundmodem operating mode; eg. sbc:afsk1200 or wss:fsk9600"); |
MODULE_PARM(iobase, "i"); |
MODULE_PARM_DESC(iobase, "soundmodem base address"); |
MODULE_PARM(irq, "i"); |
MODULE_PARM_DESC(irq, "soundmodem interrupt"); |
MODULE_PARM(dma, "i"); |
MODULE_PARM_DESC(dma, "soundmodem dma channel"); |
MODULE_PARM(dma2, "i"); |
MODULE_PARM_DESC(dma2, "soundmodem 2nd dma channel; full duplex only"); |
MODULE_PARM(serio, "i"); |
MODULE_PARM_DESC(serio, "soundmodem PTT output on serial port"); |
MODULE_PARM(pario, "i"); |
MODULE_PARM_DESC(pario, "soundmodem PTT output on parallel port"); |
MODULE_PARM(midiio, "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"); |
|
#endif |
|
__initfunc(int init_module(void)) |
{ |
if (mode) { |
if (iobase == -1) |
iobase = (!strncmp(mode, "sbc", 3)) ? 0x220 : 0x530; |
if (irq == -1) |
irq = (!strncmp(mode, "sbc", 3)) ? 5 : 11; |
if (dma == -1) |
dma = 1; |
} |
sm_ports[0].mode = mode; |
sm_ports[0].iobase = iobase; |
sm_ports[0].irq = irq; |
sm_ports[0].dma = dma; |
sm_ports[0].dma2 = dma2; |
sm_ports[0].seriobase = serio; |
sm_ports[0].pariobase = pario; |
sm_ports[0].midiiobase = midiio; |
sm_ports[1].mode = NULL; |
|
return sm_init(); |
} |
|
/* --------------------------------------------------------------------- */ |
|
void cleanup_module(void) |
{ |
int i; |
|
printk(KERN_INFO "sm: cleanup_module called\n"); |
|
for(i = 0; i < NR_PORTS; i++) { |
struct 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); |
} |
} |
} |
|
#else /* MODULE */ |
/* --------------------------------------------------------------------- */ |
/* |
* format: sm=io,irq,dma[,dma2[,serio[,pario]]],mode |
* mode: hw:modem |
* hw: sbc, wss, wssfdx |
* modem: afsk1200, fsk9600 |
*/ |
|
__initfunc(void sm_setup(char *str, int *ints)) |
{ |
int i; |
|
for (i = 0; (i < NR_PORTS) && (sm_ports[i].mode); i++); |
if ((i >= NR_PORTS) || (ints[0] < 3)) { |
printk(KERN_INFO "%s: too many or invalid interface " |
"specifications\n", sm_drvname); |
return; |
} |
sm_ports[i].mode = str; |
sm_ports[i].iobase = ints[1]; |
sm_ports[i].irq = ints[2]; |
sm_ports[i].dma = ints[3]; |
sm_ports[i].dma2 = (ints[0] >= 4) ? ints[4] : 0; |
sm_ports[i].seriobase = (ints[0] >= 5) ? ints[5] : 0; |
sm_ports[i].pariobase = (ints[0] >= 6) ? ints[6] : 0; |
sm_ports[i].midiiobase = (ints[0] >= 7) ? ints[7] : 0; |
if (i < NR_PORTS-1) |
sm_ports[i+1].mode = NULL; |
} |
|
#endif /* MODULE */ |
/* --------------------------------------------------------------------- */ |
/sm_afsk2400_7.c
0,0 → 1,296
/*****************************************************************************/ |
|
/* |
* 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; |
} |
} |
|
/* --------------------------------------------------------------------- */ |
|
extern __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; |
} |
|
extern __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; |
} |
|
extern __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; |
} |
|
extern __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
0,0 → 1,296
/*****************************************************************************/ |
|
/* |
* 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; |
} |
} |
|
/* --------------------------------------------------------------------- */ |
|
extern __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; |
} |
|
extern __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; |
} |
|
extern __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; |
} |
|
extern __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_fsk9600.c
0,0 → 1,391
/*****************************************************************************/ |
|
/* |
* 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
0,0 → 1,560
/*****************************************************************************/ |
|
/* |
* 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.h
0,0 → 1,382
/*****************************************************************************/ |
|
/* |
* sm.h -- soundcard radio modem driver internal header. |
* |
* Copyright (C) 1996-1998 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> |
|
#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; |
|
/* |
* 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 device *, struct sm_state *); |
int (*close)(struct device *, struct sm_state *); |
int (*ioctl)(struct device *, struct sm_state *, struct ifreq *, |
struct hdlcdrv_ioctl *, int); |
int (*sethw)(struct device *, struct sm_state *, char *); |
}; |
|
/* --------------------------------------------------------------------- */ |
|
#define min(a, b) (((a) < (b)) ? (a) : (b)) |
#define max(a, b) (((a) > (b)) ? (a) : (b)) |
|
/* --------------------------------------------------------------------- */ |
|
extern const char sm_drvname[]; |
extern const char sm_drvinfo[]; |
|
/* --------------------------------------------------------------------- */ |
/* |
* ===================== diagnostics stuff =============================== |
*/ |
|
extern 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) |
|
extern 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; |
} |
|
/* --------------------------------------------------------------------- */ |
|
extern 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 =============================== |
*/ |
|
extern inline unsigned int hweight32(unsigned int w) |
__attribute__ ((unused)); |
extern inline unsigned int hweight16(unsigned short w) |
__attribute__ ((unused)); |
extern inline unsigned int hweight8(unsigned char w) |
__attribute__ ((unused)); |
|
extern 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); |
} |
|
extern 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); |
} |
|
extern 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); |
} |
|
extern inline unsigned int gcd(unsigned int x, unsigned int y) |
__attribute__ ((unused)); |
extern inline unsigned int lcm(unsigned int x, unsigned int y) |
__attribute__ ((unused)); |
|
extern 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; |
} |
} |
|
extern inline unsigned int lcm(unsigned int x, unsigned int y) |
{ |
return x * y / gcd(x, y); |
} |
|
/* --------------------------------------------------------------------- */ |
/* |
* ===================== profiling ======================================= |
*/ |
|
|
#ifdef __i386__ |
|
extern int sm_x86_capability; |
|
#define HAS_RDTSC (sm_x86_capability & 0x10) |
|
/* |
* 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 (HAS_RDTSC) { \ |
unsigned int cnt1, cnt2, cnt3; \ |
__asm__(".byte 0x0f,0x31" : "=a" (cnt1), "=d" (cnt3)); \ |
cmd; \ |
__asm__(".byte 0x0f,0x31" : "=a" (cnt2), "=d" (cnt3)); \ |
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
0,0 → 1,272
/*****************************************************************************/ |
|
/* |
* 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; |
} |
} |
|
/* --------------------------------------------------------------------- */ |
|
extern __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; |
} |
|
extern __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; |
} |
|
extern __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; |
} |
|
extern __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_sbc.c
0,0 → 1,941
/*****************************************************************************/ |
|
/* |
* 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/sched.h> |
#include <linux/interrupt.h> |
#include <asm/io.h> |
#include <asm/dma.h> |
#include <linux/ioport.h> |
#include <linux/soundmodem.h> |
#include <linux/delay.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; }) |
|
extern 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; |
} |
|
extern 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 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 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 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 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 device *dev) |
{ |
inb(DSP_DATA_AVAIL(dev->base_addr)); |
} |
|
/* --------------------------------------------------------------------- */ |
|
static void inline sbc_int_ack_16bit(struct device *dev) |
{ |
inb(DSP_INTACK_16BIT(dev->base_addr)); |
} |
|
/* --------------------------------------------------------------------- */ |
|
static void setup_dma_dsp(struct 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 device *dev = (struct 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 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_s(sm->dma.obuf, dmasz); |
return -EBUSY; |
} |
if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT, |
sm->hwdrv->hw_name, dev)) { |
free_dma(dev->dma); |
kfree_s(sm->dma.obuf, dmasz); |
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 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 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 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 (!suser()) |
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 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 device *dev = (struct 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 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 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 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 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 |
}; |
|
/* --------------------------------------------------------------------- */ |
/smdma.h
0,0 → 1,217
/*****************************************************************************/ |
|
/* |
* 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 |
*/ |
extern __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; |
} |
} |
|
/* --------------------------------------------------------------------- */ |
|
extern __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; |
} |
} |
|
/* --------------------------------------------------------------------- */ |
|
extern __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; |
} |
|
extern __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++; |
} |
} |
|
extern __inline__ void dma_init_transmit(struct sm_state *sm) |
{ |
sm->dma.ofragptr = 0; |
sm->dma.ptt_cnt = 0; |
} |
|
extern __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; |
} |
|
extern __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); |
} |
|
/* --------------------------------------------------------------------- */ |
|
extern __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; |
} |
} |
|
extern __inline__ void dma_init_receive(struct sm_state *sm) |
{ |
sm->dma.ifragptr = 0; |
} |
|
/* --------------------------------------------------------------------- */ |
#endif /* _SMDMA_H */ |
|
|
|
/sm_afsk2666.c
0,0 → 1,356
/*****************************************************************************/ |
|
/* |
* 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; |
} |
} |
|
/* --------------------------------------------------------------------- */ |
|
extern __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; |
} |
|
extern __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_psk4800.c
0,0 → 1,418
/*****************************************************************************/ |
|
/* |
* 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 |
}; |
|
/* --------------------------------------------------------------------- */ |
/gentbl.c
0,0 → 1,688
/*****************************************************************************/ |
|
/* |
* 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 <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[40]; |
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_wss.c
0,0 → 1,965
/*****************************************************************************/ |
|
/* |
* 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/sched.h> |
#include <linux/interrupt.h> |
#include <asm/io.h> |
#include <asm/dma.h> |
#include <linux/ioport.h> |
#include <linux/soundmodem.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; }) |
|
extern 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; |
} |
|
extern 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 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 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 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 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) |
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 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 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 device *dev = (struct 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 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_s(sm->dma.obuf, dmasz); |
return -EBUSY; |
} |
if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT, |
sm->hwdrv->hw_name, dev)) { |
free_dma(dev->dma); |
kfree_s(sm->dma.obuf, dmasz); |
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 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 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 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 (!suser()) |
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 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 device *dev = (struct 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 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 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 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 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 |
/Makefile
0,0 → 1,60
# |
# Makefile for the soundmodem device driver. |
# |
# Note! Dependencies are done automagically by 'make dep', which also |
# removes any old dependencies. DON'T put your own dependencies here |
# unless it's something special (ie not a .c file). |
# |
# Note 2! The CFLAGS definitions are now inherited from the |
# parent makes.. |
# |
|
O_TARGET := soundmodem.o |
|
O_OBJS := sm.o |
ifeq ($(CONFIG_SOUNDMODEM_SBC),y) |
O_OBJS += sm_sbc.o |
endif |
ifeq ($(CONFIG_SOUNDMODEM_WSS),y) |
O_OBJS += sm_wss.o |
endif |
ifeq ($(CONFIG_SOUNDMODEM_AFSK1200),y) |
O_OBJS += sm_afsk1200.o |
endif |
ifeq ($(CONFIG_SOUNDMODEM_AFSK2400_7),y) |
O_OBJS += sm_afsk2400_7.o |
endif |
ifeq ($(CONFIG_SOUNDMODEM_AFSK2400_8),y) |
O_OBJS += sm_afsk2400_8.o |
endif |
ifeq ($(CONFIG_SOUNDMODEM_AFSK2666),y) |
O_OBJS += sm_afsk2666.o |
endif |
ifeq ($(CONFIG_SOUNDMODEM_HAPN4800),y) |
O_OBJS += sm_hapn4800.o |
endif |
ifeq ($(CONFIG_SOUNDMODEM_PSK4800),y) |
O_OBJS += sm_psk4800.o |
endif |
ifeq ($(CONFIG_SOUNDMODEM_FSK9600),y) |
O_OBJS += sm_fsk9600.o |
endif |
|
M_OBJS := $(O_TARGET) |
|
all: all_targets |
.PHONY: all |
|
gentbl: gentbl.c |
$(HOSTCC) -Wall $< -o $@ -lm |
|
TBLHDR := sm_tbl_afsk1200.h sm_tbl_afsk2400_8.h |
TBLHDR += sm_tbl_afsk2666.h sm_tbl_psk4800.h |
TBLHDR += sm_tbl_hapn4800.h sm_tbl_fsk9600.h |
|
$(TBLHDR): gentbl |
./gentbl |
|
fastdep: $(TBLHDR) |
|
include $(TOPDIR)/Rules.make |