OpenCores
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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.