mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 09:08:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
1039
drivers/net/hamradio/6pack.c
Normal file
1039
drivers/net/hamradio/6pack.c
Normal file
File diff suppressed because it is too large
Load diff
193
drivers/net/hamradio/Kconfig
Normal file
193
drivers/net/hamradio/Kconfig
Normal file
|
@ -0,0 +1,193 @@
|
|||
config MKISS
|
||||
tristate "Serial port KISS driver"
|
||||
depends on AX25 && TTY
|
||||
select CRC16
|
||||
---help---
|
||||
KISS is a protocol used for the exchange of data between a computer
|
||||
and a Terminal Node Controller (a small embedded system commonly
|
||||
used for networking over AX.25 amateur radio connections; it
|
||||
connects the computer's serial port with the radio's microphone
|
||||
input and speaker output).
|
||||
|
||||
Although KISS is less advanced than the 6pack protocol, it has
|
||||
the advantage that it is already supported by most modern TNCs
|
||||
without the need for a firmware upgrade.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mkiss.
|
||||
|
||||
config 6PACK
|
||||
tristate "Serial port 6PACK driver"
|
||||
depends on AX25 && TTY
|
||||
---help---
|
||||
6pack is a transmission protocol for the data exchange between your
|
||||
PC and your TNC (the Terminal Node Controller acts as a kind of
|
||||
modem connecting your computer's serial port to your radio's
|
||||
microphone input and speaker output). This protocol can be used as
|
||||
an alternative to KISS for networking over AX.25 amateur radio
|
||||
connections, but it has some extended functionality.
|
||||
|
||||
Note that this driver is still experimental and might cause
|
||||
problems. For details about the features and the usage of the
|
||||
driver, read <file:Documentation/networking/6pack.txt>.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called 6pack.
|
||||
|
||||
config BPQETHER
|
||||
tristate "BPQ Ethernet driver"
|
||||
depends on AX25
|
||||
help
|
||||
AX.25 is the protocol used for computer communication over amateur
|
||||
radio. If you say Y here, you will be able to send and receive AX.25
|
||||
traffic over Ethernet (also called "BPQ AX.25"), which could be
|
||||
useful if some other computer on your local network has a direct
|
||||
amateur radio connection.
|
||||
|
||||
config DMASCC
|
||||
tristate "High-speed (DMA) SCC driver for AX.25"
|
||||
depends on ISA && AX25 && BROKEN_ON_SMP && ISA_DMA_API
|
||||
---help---
|
||||
This is a driver for high-speed SCC boards, i.e. those supporting
|
||||
DMA on one port. You usually use those boards to connect your
|
||||
computer to an amateur radio modem (such as the WA4DSY 56kbps
|
||||
modem), in order to send and receive AX.25 packet radio network
|
||||
traffic.
|
||||
|
||||
Currently, this driver supports Ottawa PI/PI2, Paccomm/Gracilis
|
||||
PackeTwin, and S5SCC/DMA boards. They are detected automatically.
|
||||
If you have one of these cards, say Y here and read the AX25-HOWTO,
|
||||
available from <http://www.tldp.org/docs.html#howto>.
|
||||
|
||||
This driver can operate multiple boards simultaneously. If you
|
||||
compile it as a module (by saying M instead of Y), it will be called
|
||||
dmascc. If you don't pass any parameter to the driver, all
|
||||
possible I/O addresses are probed. This could irritate other devices
|
||||
that are currently not in use. You may specify the list of addresses
|
||||
to be probed by "dmascc.io=addr1,addr2,..." (when compiled into the
|
||||
kernel image) or "io=addr1,addr2,..." (when loaded as a module). The
|
||||
network interfaces will be called dmascc0 and dmascc1 for the board
|
||||
detected first, dmascc2 and dmascc3 for the second one, and so on.
|
||||
|
||||
Before you configure each interface with ifconfig, you MUST set
|
||||
certain parameters, such as channel access timing, clock mode, and
|
||||
DMA channel. This is accomplished with a small utility program,
|
||||
dmascc_cfg, available at
|
||||
<http://www.linux-ax25.org/wiki/Ax25-tools>. Please be sure to
|
||||
get at least version 1.27 of dmascc_cfg, as older versions will not
|
||||
work with the current driver.
|
||||
|
||||
config SCC
|
||||
tristate "Z8530 SCC driver"
|
||||
depends on ISA && AX25 && ISA_DMA_API
|
||||
---help---
|
||||
These cards are used to connect your Linux box to an amateur radio
|
||||
in order to communicate with other computers. If you want to use
|
||||
this, read <file:Documentation/networking/z8530drv.txt> and the
|
||||
AX25-HOWTO, available from
|
||||
<http://www.tldp.org/docs.html#howto>. Also make sure to say Y
|
||||
to "Amateur Radio AX.25 Level 2" support.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called scc.
|
||||
|
||||
config SCC_DELAY
|
||||
bool "additional delay for PA0HZP OptoSCC compatible boards"
|
||||
depends on SCC
|
||||
help
|
||||
Say Y here if you experience problems with the SCC driver not
|
||||
working properly; please read
|
||||
<file:Documentation/networking/z8530drv.txt> for details.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config SCC_TRXECHO
|
||||
bool "support for TRX that feedback the tx signal to rx"
|
||||
depends on SCC
|
||||
help
|
||||
Some transmitters feed the transmitted signal back to the receive
|
||||
line. Say Y here to foil this by explicitly disabling the receiver
|
||||
during data transmission.
|
||||
|
||||
If in doubt, say Y.
|
||||
|
||||
config BAYCOM_SER_FDX
|
||||
tristate "BAYCOM ser12 fullduplex driver for AX.25"
|
||||
depends on AX25 && !S390
|
||||
select CRC_CCITT
|
||||
---help---
|
||||
This is one of two drivers for Baycom style simple amateur radio
|
||||
modems that connect to a serial interface. The driver supports the
|
||||
ser12 design in full-duplex mode. In addition, it allows the
|
||||
baudrate to be set between 300 and 4800 baud (however not all modems
|
||||
support all baudrates). This is the preferred driver. The next
|
||||
driver, "BAYCOM ser12 half-duplex driver for AX.25" is the old
|
||||
driver and still provided in case this driver does not work with
|
||||
your serial interface chip. To configure the driver, use the sethdlc
|
||||
utility available in the standard ax25 utilities package. For
|
||||
information on the modems, see <http://www.baycom.de/> and
|
||||
<file:Documentation/networking/baycom.txt>.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called baycom_ser_fdx. This is recommended.
|
||||
|
||||
config BAYCOM_SER_HDX
|
||||
tristate "BAYCOM ser12 halfduplex driver for AX.25"
|
||||
depends on AX25 && !S390
|
||||
select CRC_CCITT
|
||||
---help---
|
||||
This is one of two drivers for Baycom style simple amateur radio
|
||||
modems that connect to a serial interface. The driver supports the
|
||||
ser12 design in half-duplex mode. This is the old driver. It is
|
||||
still provided in case your serial interface chip does not work with
|
||||
the full-duplex driver. This driver is deprecated. To configure
|
||||
the driver, use the sethdlc utility available in the standard ax25
|
||||
utilities package. For information on the modems, see
|
||||
<http://www.baycom.de/> and
|
||||
<file:Documentation/networking/baycom.txt>.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called baycom_ser_hdx. This is recommended.
|
||||
|
||||
config BAYCOM_PAR
|
||||
tristate "BAYCOM picpar and par96 driver for AX.25"
|
||||
depends on PARPORT && AX25
|
||||
select CRC_CCITT
|
||||
---help---
|
||||
This is a driver for Baycom style simple amateur radio modems that
|
||||
connect to a parallel interface. The driver supports the picpar and
|
||||
par96 designs. To configure the driver, use the sethdlc utility
|
||||
available in the standard ax25 utilities package. For information on
|
||||
the modems, see <http://www.baycom.de/> and the file
|
||||
<file:Documentation/networking/baycom.txt>.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called baycom_par. This is recommended.
|
||||
|
||||
config BAYCOM_EPP
|
||||
tristate "BAYCOM epp driver for AX.25"
|
||||
depends on PARPORT && AX25 && !64BIT
|
||||
select CRC_CCITT
|
||||
---help---
|
||||
This is a driver for Baycom style simple amateur radio modems that
|
||||
connect to a parallel interface. The driver supports the EPP
|
||||
designs. To configure the driver, use the sethdlc utility available
|
||||
in the standard ax25 utilities package. For information on the
|
||||
modems, see <http://www.baycom.de/> and the file
|
||||
<file:Documentation/networking/baycom.txt>.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called baycom_epp. This is recommended.
|
||||
|
||||
config YAM
|
||||
tristate "YAM driver for AX.25"
|
||||
depends on AX25 && !S390
|
||||
help
|
||||
The YAM is a modem for packet radio which connects to the serial
|
||||
port and includes some of the functions of a Terminal Node
|
||||
Controller. If you have one of those, say Y here.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called yam.
|
||||
|
||||
|
22
drivers/net/hamradio/Makefile
Normal file
22
drivers/net/hamradio/Makefile
Normal file
|
@ -0,0 +1,22 @@
|
|||
#
|
||||
# Makefile for the Linux AX.25 and HFMODEM device drivers.
|
||||
#
|
||||
#
|
||||
# 19971130 Moved the amateur radio related network drivers from
|
||||
# drivers/net/ to drivers/hamradio for easier maintenance.
|
||||
# Joerg Reuter DL1BKE <jreuter@yaina.de>
|
||||
#
|
||||
# 20000806 Rewritten to use lists instead of if-statements.
|
||||
# Christoph Hellwig <hch@infradead.org>
|
||||
#
|
||||
|
||||
obj-$(CONFIG_DMASCC) += dmascc.o
|
||||
obj-$(CONFIG_SCC) += scc.o
|
||||
obj-$(CONFIG_MKISS) += mkiss.o
|
||||
obj-$(CONFIG_6PACK) += 6pack.o
|
||||
obj-$(CONFIG_YAM) += yam.o
|
||||
obj-$(CONFIG_BPQETHER) += bpqether.o
|
||||
obj-$(CONFIG_BAYCOM_SER_FDX) += baycom_ser_fdx.o hdlcdrv.o
|
||||
obj-$(CONFIG_BAYCOM_SER_HDX) += baycom_ser_hdx.o hdlcdrv.o
|
||||
obj-$(CONFIG_BAYCOM_PAR) += baycom_par.o hdlcdrv.o
|
||||
obj-$(CONFIG_BAYCOM_EPP) += baycom_epp.o hdlcdrv.o
|
1287
drivers/net/hamradio/baycom_epp.c
Normal file
1287
drivers/net/hamradio/baycom_epp.c
Normal file
File diff suppressed because it is too large
Load diff
575
drivers/net/hamradio/baycom_par.c
Normal file
575
drivers/net/hamradio/baycom_par.c
Normal file
|
@ -0,0 +1,575 @@
|
|||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* baycom_par.c -- baycom par96 and picpar radio modem driver.
|
||||
*
|
||||
* Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Please note that the GPL allows you to use the driver, NOT the radio.
|
||||
* In order to use the radio, you need a license from the communications
|
||||
* authority of your country.
|
||||
*
|
||||
*
|
||||
* Supported modems
|
||||
*
|
||||
* par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard.
|
||||
* The modem does all the filtering and regenerates the receiver clock.
|
||||
* Data is transferred from and to the PC via a shift register.
|
||||
* The shift register is filled with 16 bits and an interrupt is
|
||||
* signalled. The PC then empties the shift register in a burst. This
|
||||
* modem connects to the parallel port, hence the name. The modem
|
||||
* leaves the implementation of the HDLC protocol and the scrambler
|
||||
* polynomial to the PC. This modem is no longer available (at least
|
||||
* from Baycom) and has been replaced by the PICPAR modem (see below).
|
||||
* You may however still build one from the schematics published in
|
||||
* cq-DL :-).
|
||||
*
|
||||
* picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The
|
||||
* modem is protocol compatible to par96, but uses only three low
|
||||
* power ICs and can therefore be fed from the parallel port and
|
||||
* does not require an additional power supply. It features
|
||||
* built in DCD circuitry. The driver should therefore be configured
|
||||
* for hardware DCD.
|
||||
*
|
||||
*
|
||||
* Command line options (insmod command line)
|
||||
*
|
||||
* mode driver mode string. Valid choices are par96 and picpar.
|
||||
* iobase base address of the port; common values are 0x378, 0x278, 0x3bc
|
||||
*
|
||||
*
|
||||
* History:
|
||||
* 0.1 26.06.1996 Adapted from baycom.c and made network driver interface
|
||||
* 18.10.1996 Changed to new user space access routines (copy_{to,from}_user)
|
||||
* 0.3 26.04.1997 init code/data tagged
|
||||
* 0.4 08.07.1997 alternative ser12 decoding algorithm (uses delta CTS ints)
|
||||
* 0.5 11.11.1997 split into separate files for ser12/par96
|
||||
* 0.6 03.08.1999 adapt to Linus' new __setup/__initcall
|
||||
* removed some pre-2.2 kernel compatibility cruft
|
||||
* 0.7 10.08.1999 Check if parport can do SPP and is safe to access during interrupt contexts
|
||||
* 0.8 12.02.2000 adapted to softnet driver interface
|
||||
* removed direct parport access, uses parport driver methods
|
||||
* 0.9 03.07.2000 fix interface name handling
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/hdlcdrv.h>
|
||||
#include <linux/baycom.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#define BAYCOM_DEBUG
|
||||
|
||||
/*
|
||||
* modem options; bit mask
|
||||
*/
|
||||
#define BAYCOM_OPTIONS_SOFTDCD 1
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static const char bc_drvname[] = "baycom_par";
|
||||
static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
|
||||
"baycom_par: version 0.9\n";
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#define NR_PORTS 4
|
||||
|
||||
static struct net_device *baycom_device[NR_PORTS];
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#define PAR96_BURSTBITS 16
|
||||
#define PAR96_BURST 4
|
||||
#define PAR96_PTT 2
|
||||
#define PAR96_TXBIT 1
|
||||
#define PAR96_ACK 0x40
|
||||
#define PAR96_RXBIT 0x20
|
||||
#define PAR96_DCD 0x10
|
||||
#define PAR97_POWER 0xf8
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/*
|
||||
* Information that need to be kept for each board.
|
||||
*/
|
||||
|
||||
struct baycom_state {
|
||||
struct hdlcdrv_state hdrv;
|
||||
|
||||
struct pardevice *pdev;
|
||||
unsigned int options;
|
||||
|
||||
struct modem_state {
|
||||
short arb_divider;
|
||||
unsigned char flags;
|
||||
unsigned int shreg;
|
||||
struct modem_state_par96 {
|
||||
int dcd_count;
|
||||
unsigned int dcd_shreg;
|
||||
unsigned long descram;
|
||||
unsigned long scram;
|
||||
} par96;
|
||||
} modem;
|
||||
|
||||
#ifdef BAYCOM_DEBUG
|
||||
struct debug_vals {
|
||||
unsigned long last_jiffies;
|
||||
unsigned cur_intcnt;
|
||||
unsigned last_intcnt;
|
||||
int cur_pllcorr;
|
||||
int last_pllcorr;
|
||||
} debug_vals;
|
||||
#endif /* BAYCOM_DEBUG */
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static void __inline__ baycom_int_freq(struct baycom_state *bc)
|
||||
{
|
||||
#ifdef BAYCOM_DEBUG
|
||||
unsigned long cur_jiffies = jiffies;
|
||||
/*
|
||||
* measure the interrupt frequency
|
||||
*/
|
||||
bc->debug_vals.cur_intcnt++;
|
||||
if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) {
|
||||
bc->debug_vals.last_jiffies = cur_jiffies;
|
||||
bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
|
||||
bc->debug_vals.cur_intcnt = 0;
|
||||
bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
|
||||
bc->debug_vals.cur_pllcorr = 0;
|
||||
}
|
||||
#endif /* BAYCOM_DEBUG */
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/*
|
||||
* ===================== PAR96 specific routines =========================
|
||||
*/
|
||||
|
||||
#define PAR96_DESCRAM_TAP1 0x20000
|
||||
#define PAR96_DESCRAM_TAP2 0x01000
|
||||
#define PAR96_DESCRAM_TAP3 0x00001
|
||||
|
||||
#define PAR96_DESCRAM_TAPSH1 17
|
||||
#define PAR96_DESCRAM_TAPSH2 12
|
||||
#define PAR96_DESCRAM_TAPSH3 0
|
||||
|
||||
#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */
|
||||
#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static __inline__ void par96_tx(struct net_device *dev, struct baycom_state *bc)
|
||||
{
|
||||
int i;
|
||||
unsigned int data = hdlcdrv_getbits(&bc->hdrv);
|
||||
struct parport *pp = bc->pdev->port;
|
||||
|
||||
for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) {
|
||||
unsigned char val = PAR97_POWER;
|
||||
bc->modem.par96.scram = ((bc->modem.par96.scram << 1) |
|
||||
(bc->modem.par96.scram & 1));
|
||||
if (!(data & 1))
|
||||
bc->modem.par96.scram ^= 1;
|
||||
if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))
|
||||
bc->modem.par96.scram ^=
|
||||
(PAR96_SCRAM_TAPN << 1);
|
||||
if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2))
|
||||
val |= PAR96_TXBIT;
|
||||
pp->ops->write_data(pp, val);
|
||||
pp->ops->write_data(pp, val | PAR96_BURST);
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static __inline__ void par96_rx(struct net_device *dev, struct baycom_state *bc)
|
||||
{
|
||||
int i;
|
||||
unsigned int data, mask, mask2, descx;
|
||||
struct parport *pp = bc->pdev->port;
|
||||
|
||||
/*
|
||||
* do receiver; differential decode and descramble on the fly
|
||||
*/
|
||||
for(data = i = 0; i < PAR96_BURSTBITS; i++) {
|
||||
bc->modem.par96.descram = (bc->modem.par96.descram << 1);
|
||||
if (pp->ops->read_status(pp) & PAR96_RXBIT)
|
||||
bc->modem.par96.descram |= 1;
|
||||
descx = bc->modem.par96.descram ^
|
||||
(bc->modem.par96.descram >> 1);
|
||||
/* now the diff decoded data is inverted in descram */
|
||||
pp->ops->write_data(pp, PAR97_POWER | PAR96_PTT);
|
||||
descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^
|
||||
(descx >> PAR96_DESCRAM_TAPSH2));
|
||||
data >>= 1;
|
||||
if (!(descx & 1))
|
||||
data |= 0x8000;
|
||||
pp->ops->write_data(pp, PAR97_POWER | PAR96_PTT | PAR96_BURST);
|
||||
}
|
||||
hdlcdrv_putbits(&bc->hdrv, data);
|
||||
/*
|
||||
* do DCD algorithm
|
||||
*/
|
||||
if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
|
||||
bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16)
|
||||
| (data << 16);
|
||||
/* search for flags and set the dcd counter appropriately */
|
||||
for(mask = 0x1fe00, mask2 = 0xfc00, i = 0;
|
||||
i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
|
||||
if ((bc->modem.par96.dcd_shreg & mask) == mask2)
|
||||
bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4;
|
||||
/* check for abort/noise sequences */
|
||||
for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0;
|
||||
i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
|
||||
if (((bc->modem.par96.dcd_shreg & mask) == mask2) &&
|
||||
(bc->modem.par96.dcd_count >= 0))
|
||||
bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10;
|
||||
/* decrement and set the dcd variable */
|
||||
if (bc->modem.par96.dcd_count >= 0)
|
||||
bc->modem.par96.dcd_count -= 2;
|
||||
hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0);
|
||||
} else {
|
||||
hdlcdrv_setdcd(&bc->hdrv, !!(pp->ops->read_status(pp) & PAR96_DCD));
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static void par96_interrupt(void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct baycom_state *bc = netdev_priv(dev);
|
||||
|
||||
baycom_int_freq(bc);
|
||||
/*
|
||||
* check if transmitter active
|
||||
*/
|
||||
if (hdlcdrv_ptt(&bc->hdrv))
|
||||
par96_tx(dev, bc);
|
||||
else {
|
||||
par96_rx(dev, bc);
|
||||
if (--bc->modem.arb_divider <= 0) {
|
||||
bc->modem.arb_divider = 6;
|
||||
local_irq_enable();
|
||||
hdlcdrv_arbitrate(dev, &bc->hdrv);
|
||||
}
|
||||
}
|
||||
local_irq_enable();
|
||||
hdlcdrv_transmitter(dev, &bc->hdrv);
|
||||
hdlcdrv_receiver(dev, &bc->hdrv);
|
||||
local_irq_disable();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static void par96_wakeup(void *handle)
|
||||
{
|
||||
struct net_device *dev = (struct net_device *)handle;
|
||||
struct baycom_state *bc = netdev_priv(dev);
|
||||
|
||||
printk(KERN_DEBUG "baycom_par: %s: why am I being woken up?\n", dev->name);
|
||||
if (!parport_claim(bc->pdev))
|
||||
printk(KERN_DEBUG "baycom_par: %s: I'm broken.\n", dev->name);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int par96_open(struct net_device *dev)
|
||||
{
|
||||
struct baycom_state *bc = netdev_priv(dev);
|
||||
struct parport *pp;
|
||||
|
||||
if (!dev || !bc)
|
||||
return -ENXIO;
|
||||
pp = parport_find_base(dev->base_addr);
|
||||
if (!pp) {
|
||||
printk(KERN_ERR "baycom_par: parport at 0x%lx unknown\n", dev->base_addr);
|
||||
return -ENXIO;
|
||||
}
|
||||
if (pp->irq < 0) {
|
||||
printk(KERN_ERR "baycom_par: parport at 0x%lx has no irq\n", pp->base);
|
||||
parport_put_port(pp);
|
||||
return -ENXIO;
|
||||
}
|
||||
if ((~pp->modes) & (PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT)) {
|
||||
printk(KERN_ERR "baycom_par: parport at 0x%lx cannot be used\n", pp->base);
|
||||
parport_put_port(pp);
|
||||
return -ENXIO;
|
||||
}
|
||||
memset(&bc->modem, 0, sizeof(bc->modem));
|
||||
bc->hdrv.par.bitrate = 9600;
|
||||
bc->pdev = parport_register_device(pp, dev->name, NULL, par96_wakeup,
|
||||
par96_interrupt, PARPORT_DEV_EXCL, dev);
|
||||
parport_put_port(pp);
|
||||
if (!bc->pdev) {
|
||||
printk(KERN_ERR "baycom_par: cannot register parport at 0x%lx\n", dev->base_addr);
|
||||
return -ENXIO;
|
||||
}
|
||||
if (parport_claim(bc->pdev)) {
|
||||
printk(KERN_ERR "baycom_par: parport at 0x%lx busy\n", pp->base);
|
||||
parport_unregister_device(bc->pdev);
|
||||
return -EBUSY;
|
||||
}
|
||||
pp = bc->pdev->port;
|
||||
dev->irq = pp->irq;
|
||||
pp->ops->data_forward(pp);
|
||||
bc->hdrv.par.bitrate = 9600;
|
||||
pp->ops->write_data(pp, PAR96_PTT | PAR97_POWER); /* switch off PTT */
|
||||
pp->ops->enable_irq(pp);
|
||||
printk(KERN_INFO "%s: par96 at iobase 0x%lx irq %u options 0x%x\n",
|
||||
bc_drvname, dev->base_addr, dev->irq, bc->options);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int par96_close(struct net_device *dev)
|
||||
{
|
||||
struct baycom_state *bc = netdev_priv(dev);
|
||||
struct parport *pp;
|
||||
|
||||
if (!dev || !bc)
|
||||
return -EINVAL;
|
||||
pp = bc->pdev->port;
|
||||
/* disable interrupt */
|
||||
pp->ops->disable_irq(pp);
|
||||
/* switch off PTT */
|
||||
pp->ops->write_data(pp, PAR96_PTT | PAR97_POWER);
|
||||
parport_release(bc->pdev);
|
||||
parport_unregister_device(bc->pdev);
|
||||
printk(KERN_INFO "%s: close par96 at iobase 0x%lx irq %u\n",
|
||||
bc_drvname, dev->base_addr, dev->irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/*
|
||||
* ===================== hdlcdrv driver interface =========================
|
||||
*/
|
||||
|
||||
static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
|
||||
struct hdlcdrv_ioctl *hi, int cmd);
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static struct hdlcdrv_ops par96_ops = {
|
||||
.drvname = bc_drvname,
|
||||
.drvinfo = bc_drvinfo,
|
||||
.open = par96_open,
|
||||
.close = par96_close,
|
||||
.ioctl = baycom_ioctl
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int baycom_setmode(struct baycom_state *bc, const char *modestr)
|
||||
{
|
||||
if (!strncmp(modestr, "picpar", 6))
|
||||
bc->options = 0;
|
||||
else if (!strncmp(modestr, "par96", 5))
|
||||
bc->options = BAYCOM_OPTIONS_SOFTDCD;
|
||||
else
|
||||
bc->options = !!strchr(modestr, '*');
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
|
||||
struct hdlcdrv_ioctl *hi, int cmd)
|
||||
{
|
||||
struct baycom_state *bc;
|
||||
struct baycom_ioctl bi;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
bc = netdev_priv(dev);
|
||||
BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC);
|
||||
|
||||
if (cmd != SIOCDEVPRIVATE)
|
||||
return -ENOIOCTLCMD;
|
||||
switch (hi->cmd) {
|
||||
default:
|
||||
break;
|
||||
|
||||
case HDLCDRVCTL_GETMODE:
|
||||
strcpy(hi->data.modename, bc->options ? "par96" : "picpar");
|
||||
if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case HDLCDRVCTL_SETMODE:
|
||||
if (netif_running(dev) || !capable(CAP_NET_ADMIN))
|
||||
return -EACCES;
|
||||
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
|
||||
return baycom_setmode(bc, hi->data.modename);
|
||||
|
||||
case HDLCDRVCTL_MODELIST:
|
||||
strcpy(hi->data.modename, "par96,picpar");
|
||||
if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case HDLCDRVCTL_MODEMPARMASK:
|
||||
return HDLCDRV_PARMASK_IOBASE;
|
||||
|
||||
}
|
||||
|
||||
if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
|
||||
return -EFAULT;
|
||||
switch (bi.cmd) {
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
|
||||
#ifdef BAYCOM_DEBUG
|
||||
case BAYCOMCTL_GETDEBUG:
|
||||
bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
|
||||
bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
|
||||
bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
|
||||
break;
|
||||
#endif /* BAYCOM_DEBUG */
|
||||
|
||||
}
|
||||
if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* command line settable parameters
|
||||
*/
|
||||
static char *mode[NR_PORTS] = { "picpar", };
|
||||
static int iobase[NR_PORTS] = { 0x378, };
|
||||
|
||||
module_param_array(mode, charp, NULL, 0);
|
||||
MODULE_PARM_DESC(mode, "baycom operating mode; eg. par96 or picpar");
|
||||
module_param_array(iobase, int, NULL, 0);
|
||||
MODULE_PARM_DESC(iobase, "baycom io base address");
|
||||
|
||||
MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
|
||||
MODULE_DESCRIPTION("Baycom par96 and picpar amateur radio modem driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int __init init_baycompar(void)
|
||||
{
|
||||
int i, found = 0;
|
||||
char set_hw = 1;
|
||||
|
||||
printk(bc_drvinfo);
|
||||
/*
|
||||
* register net devices
|
||||
*/
|
||||
for (i = 0; i < NR_PORTS; i++) {
|
||||
struct net_device *dev;
|
||||
struct baycom_state *bc;
|
||||
char ifname[IFNAMSIZ];
|
||||
|
||||
sprintf(ifname, "bcp%d", i);
|
||||
|
||||
if (!mode[i])
|
||||
set_hw = 0;
|
||||
if (!set_hw)
|
||||
iobase[i] = 0;
|
||||
|
||||
dev = hdlcdrv_register(&par96_ops,
|
||||
sizeof(struct baycom_state),
|
||||
ifname, iobase[i], 0, 0);
|
||||
if (IS_ERR(dev))
|
||||
break;
|
||||
|
||||
bc = netdev_priv(dev);
|
||||
if (set_hw && baycom_setmode(bc, mode[i]))
|
||||
set_hw = 0;
|
||||
found++;
|
||||
baycom_device[i] = dev;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return -ENXIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_baycompar(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < NR_PORTS; i++) {
|
||||
struct net_device *dev = baycom_device[i];
|
||||
|
||||
if (dev)
|
||||
hdlcdrv_unregister(dev);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(init_baycompar);
|
||||
module_exit(cleanup_baycompar);
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#ifndef MODULE
|
||||
|
||||
/*
|
||||
* format: baycom_par=io,mode
|
||||
* mode: par96,picpar
|
||||
*/
|
||||
|
||||
static int __init baycom_par_setup(char *str)
|
||||
{
|
||||
static unsigned nr_dev;
|
||||
int ints[2];
|
||||
|
||||
if (nr_dev >= NR_PORTS)
|
||||
return 0;
|
||||
str = get_options(str, 2, ints);
|
||||
if (ints[0] < 1)
|
||||
return 0;
|
||||
mode[nr_dev] = str;
|
||||
iobase[nr_dev] = ints[1];
|
||||
nr_dev++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("baycom_par=", baycom_par_setup);
|
||||
|
||||
#endif /* MODULE */
|
||||
/* --------------------------------------------------------------------- */
|
716
drivers/net/hamradio/baycom_ser_fdx.c
Normal file
716
drivers/net/hamradio/baycom_ser_fdx.c
Normal file
|
@ -0,0 +1,716 @@
|
|||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* baycom_ser_fdx.c -- baycom ser12 fullduplex radio modem driver.
|
||||
*
|
||||
* Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Please note that the GPL allows you to use the driver, NOT the radio.
|
||||
* In order to use the radio, you need a license from the communications
|
||||
* authority of your country.
|
||||
*
|
||||
*
|
||||
* Supported modems
|
||||
*
|
||||
* ser12: This is a very simple 1200 baud AFSK modem. The modem consists only
|
||||
* of a modulator/demodulator chip, usually a TI TCM3105. The computer
|
||||
* is responsible for regenerating the receiver bit clock, as well as
|
||||
* for handling the HDLC protocol. The modem connects to a serial port,
|
||||
* hence the name. Since the serial port is not used as an async serial
|
||||
* port, the kernel driver for serial ports cannot be used, and this
|
||||
* driver only supports standard serial hardware (8250, 16450, 16550A)
|
||||
*
|
||||
* This modem usually draws its supply current out of the otherwise unused
|
||||
* TXD pin of the serial port. Thus a contiguous stream of 0x00-bytes
|
||||
* is transmitted to achieve a positive supply voltage.
|
||||
*
|
||||
* hsk: This is a 4800 baud FSK modem, designed for TNC use. It works fine
|
||||
* in 'baycom-mode' :-) In contrast to the TCM3105 modem, power is
|
||||
* externally supplied. So there's no need to provide the 0x00-byte-stream
|
||||
* when receiving or idle, which drastically reduces interrupt load.
|
||||
*
|
||||
* Command line options (insmod command line)
|
||||
*
|
||||
* mode ser# hardware DCD
|
||||
* ser#* software DCD
|
||||
* ser#+ hardware DCD, inverted signal at DCD pin
|
||||
* '#' denotes the baud rate / 100, eg. ser12* is '1200 baud, soft DCD'
|
||||
* iobase base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
|
||||
* baud baud rate (between 300 and 4800)
|
||||
* irq interrupt line of the port; common values are 4,3
|
||||
*
|
||||
*
|
||||
* History:
|
||||
* 0.1 26.06.1996 Adapted from baycom.c and made network driver interface
|
||||
* 18.10.1996 Changed to new user space access routines (copy_{to,from}_user)
|
||||
* 0.3 26.04.1997 init code/data tagged
|
||||
* 0.4 08.07.1997 alternative ser12 decoding algorithm (uses delta CTS ints)
|
||||
* 0.5 11.11.1997 ser12/par96 split into separate files
|
||||
* 0.6 24.01.1998 Thorsten Kranzkowski, dl8bcu and Thomas Sailer:
|
||||
* reduced interrupt load in transmit case
|
||||
* reworked receiver
|
||||
* 0.7 03.08.1999 adapt to Linus' new __setup/__initcall
|
||||
* 0.8 10.08.1999 use module_init/module_exit
|
||||
* 0.9 12.02.2000 adapted to softnet driver interface
|
||||
* 0.10 03.07.2000 fix interface name handling
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#include <linux/capability.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/hdlcdrv.h>
|
||||
#include <linux/baycom.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#define BAYCOM_DEBUG
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static const char bc_drvname[] = "baycom_ser_fdx";
|
||||
static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
|
||||
"baycom_ser_fdx: version 0.10\n";
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#define NR_PORTS 4
|
||||
|
||||
static struct net_device *baycom_device[NR_PORTS];
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#define RBR(iobase) (iobase+0)
|
||||
#define THR(iobase) (iobase+0)
|
||||
#define IER(iobase) (iobase+1)
|
||||
#define IIR(iobase) (iobase+2)
|
||||
#define FCR(iobase) (iobase+2)
|
||||
#define LCR(iobase) (iobase+3)
|
||||
#define MCR(iobase) (iobase+4)
|
||||
#define LSR(iobase) (iobase+5)
|
||||
#define MSR(iobase) (iobase+6)
|
||||
#define SCR(iobase) (iobase+7)
|
||||
#define DLL(iobase) (iobase+0)
|
||||
#define DLM(iobase) (iobase+1)
|
||||
|
||||
#define SER12_EXTENT 8
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/*
|
||||
* Information that need to be kept for each board.
|
||||
*/
|
||||
|
||||
struct baycom_state {
|
||||
struct hdlcdrv_state hdrv;
|
||||
|
||||
unsigned int baud, baud_us, baud_arbdiv, baud_uartdiv, baud_dcdtimeout;
|
||||
int opt_dcd;
|
||||
|
||||
struct modem_state {
|
||||
unsigned char flags;
|
||||
unsigned char ptt;
|
||||
unsigned int shreg;
|
||||
struct modem_state_ser12 {
|
||||
unsigned char tx_bit;
|
||||
unsigned char last_rxbit;
|
||||
int dcd_sum0, dcd_sum1, dcd_sum2;
|
||||
int dcd_time;
|
||||
unsigned int pll_time;
|
||||
unsigned int txshreg;
|
||||
} ser12;
|
||||
} modem;
|
||||
|
||||
#ifdef BAYCOM_DEBUG
|
||||
struct debug_vals {
|
||||
unsigned long last_jiffies;
|
||||
unsigned cur_intcnt;
|
||||
unsigned last_intcnt;
|
||||
int cur_pllcorr;
|
||||
int last_pllcorr;
|
||||
} debug_vals;
|
||||
#endif /* BAYCOM_DEBUG */
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static inline void baycom_int_freq(struct baycom_state *bc)
|
||||
{
|
||||
#ifdef BAYCOM_DEBUG
|
||||
unsigned long cur_jiffies = jiffies;
|
||||
/*
|
||||
* measure the interrupt frequency
|
||||
*/
|
||||
bc->debug_vals.cur_intcnt++;
|
||||
if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) {
|
||||
bc->debug_vals.last_jiffies = cur_jiffies;
|
||||
bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
|
||||
bc->debug_vals.cur_intcnt = 0;
|
||||
bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
|
||||
bc->debug_vals.cur_pllcorr = 0;
|
||||
}
|
||||
#endif /* BAYCOM_DEBUG */
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/*
|
||||
* ===================== SER12 specific routines =========================
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static inline void ser12_set_divisor(struct net_device *dev,
|
||||
unsigned int divisor)
|
||||
{
|
||||
outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */
|
||||
outb(divisor, DLL(dev->base_addr));
|
||||
outb(divisor >> 8, DLM(dev->base_addr));
|
||||
outb(0x01, LCR(dev->base_addr)); /* word length = 6 */
|
||||
/*
|
||||
* make sure the next interrupt is generated;
|
||||
* 0 must be used to power the modem; the modem draws its
|
||||
* power from the TxD line
|
||||
*/
|
||||
outb(0x00, THR(dev->base_addr));
|
||||
/*
|
||||
* it is important not to set the divider while transmitting;
|
||||
* this reportedly makes some UARTs generating interrupts
|
||||
* in the hundredthousands per second region
|
||||
* Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
|
||||
*/
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#if 0
|
||||
static inline unsigned int hweight16(unsigned int w)
|
||||
__attribute__ ((unused));
|
||||
static inline unsigned int hweight8(unsigned int w)
|
||||
__attribute__ ((unused));
|
||||
|
||||
static inline unsigned int hweight16(unsigned int w)
|
||||
{
|
||||
unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
|
||||
res = (res & 0x3333) + ((res >> 2) & 0x3333);
|
||||
res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
|
||||
return (res & 0x00FF) + ((res >> 8) & 0x00FF);
|
||||
}
|
||||
|
||||
static inline unsigned int hweight8(unsigned int w)
|
||||
{
|
||||
unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
|
||||
res = (res & 0x33) + ((res >> 2) & 0x33);
|
||||
return (res & 0x0F) + ((res >> 4) & 0x0F);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static __inline__ void ser12_rx(struct net_device *dev, struct baycom_state *bc, struct timeval *tv, unsigned char curs)
|
||||
{
|
||||
int timediff;
|
||||
int bdus8 = bc->baud_us >> 3;
|
||||
int bdus4 = bc->baud_us >> 2;
|
||||
int bdus2 = bc->baud_us >> 1;
|
||||
|
||||
timediff = 1000000 + tv->tv_usec - bc->modem.ser12.pll_time;
|
||||
while (timediff >= 500000)
|
||||
timediff -= 1000000;
|
||||
while (timediff >= bdus2) {
|
||||
timediff -= bc->baud_us;
|
||||
bc->modem.ser12.pll_time += bc->baud_us;
|
||||
bc->modem.ser12.dcd_time--;
|
||||
/* first check if there is room to add a bit */
|
||||
if (bc->modem.shreg & 1) {
|
||||
hdlcdrv_putbits(&bc->hdrv, (bc->modem.shreg >> 1) ^ 0xffff);
|
||||
bc->modem.shreg = 0x10000;
|
||||
}
|
||||
/* add a one bit */
|
||||
bc->modem.shreg >>= 1;
|
||||
}
|
||||
if (bc->modem.ser12.dcd_time <= 0) {
|
||||
if (!bc->opt_dcd)
|
||||
hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
|
||||
bc->modem.ser12.dcd_sum1 +
|
||||
bc->modem.ser12.dcd_sum2) < 0);
|
||||
bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
|
||||
bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
|
||||
bc->modem.ser12.dcd_sum0 = 2; /* slight bias */
|
||||
bc->modem.ser12.dcd_time += 120;
|
||||
}
|
||||
if (bc->modem.ser12.last_rxbit != curs) {
|
||||
bc->modem.ser12.last_rxbit = curs;
|
||||
bc->modem.shreg |= 0x10000;
|
||||
/* adjust the PLL */
|
||||
if (timediff > 0)
|
||||
bc->modem.ser12.pll_time += bdus8;
|
||||
else
|
||||
bc->modem.ser12.pll_time += 1000000 - bdus8;
|
||||
/* update DCD */
|
||||
if (abs(timediff) > bdus4)
|
||||
bc->modem.ser12.dcd_sum0 += 4;
|
||||
else
|
||||
bc->modem.ser12.dcd_sum0--;
|
||||
#ifdef BAYCOM_DEBUG
|
||||
bc->debug_vals.cur_pllcorr = timediff;
|
||||
#endif /* BAYCOM_DEBUG */
|
||||
}
|
||||
while (bc->modem.ser12.pll_time >= 1000000)
|
||||
bc->modem.ser12.pll_time -= 1000000;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static irqreturn_t ser12_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = (struct net_device *)dev_id;
|
||||
struct baycom_state *bc = netdev_priv(dev);
|
||||
struct timeval tv;
|
||||
unsigned char iir, msr;
|
||||
unsigned int txcount = 0;
|
||||
|
||||
if (!bc || bc->hdrv.magic != HDLCDRV_MAGIC)
|
||||
return IRQ_NONE;
|
||||
/* fast way out for shared irq */
|
||||
if ((iir = inb(IIR(dev->base_addr))) & 1)
|
||||
return IRQ_NONE;
|
||||
/* get current time */
|
||||
do_gettimeofday(&tv);
|
||||
msr = inb(MSR(dev->base_addr));
|
||||
/* delta DCD */
|
||||
if ((msr & 8) && bc->opt_dcd)
|
||||
hdlcdrv_setdcd(&bc->hdrv, !((msr ^ bc->opt_dcd) & 0x80));
|
||||
do {
|
||||
switch (iir & 6) {
|
||||
case 6:
|
||||
inb(LSR(dev->base_addr));
|
||||
break;
|
||||
|
||||
case 4:
|
||||
inb(RBR(dev->base_addr));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/*
|
||||
* make sure the next interrupt is generated;
|
||||
* 0 must be used to power the modem; the modem draws its
|
||||
* power from the TxD line
|
||||
*/
|
||||
outb(0x00, THR(dev->base_addr));
|
||||
baycom_int_freq(bc);
|
||||
txcount++;
|
||||
/*
|
||||
* first output the last bit (!) then call HDLC transmitter,
|
||||
* since this may take quite long
|
||||
*/
|
||||
if (bc->modem.ptt)
|
||||
outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
|
||||
else
|
||||
outb(0x0d, MCR(dev->base_addr)); /* transmitter off */
|
||||
break;
|
||||
|
||||
default:
|
||||
msr = inb(MSR(dev->base_addr));
|
||||
/* delta DCD */
|
||||
if ((msr & 8) && bc->opt_dcd)
|
||||
hdlcdrv_setdcd(&bc->hdrv, !((msr ^ bc->opt_dcd) & 0x80));
|
||||
break;
|
||||
}
|
||||
iir = inb(IIR(dev->base_addr));
|
||||
} while (!(iir & 1));
|
||||
ser12_rx(dev, bc, &tv, msr & 0x10); /* CTS */
|
||||
if (bc->modem.ptt && txcount) {
|
||||
if (bc->modem.ser12.txshreg <= 1) {
|
||||
bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
|
||||
if (!hdlcdrv_ptt(&bc->hdrv)) {
|
||||
ser12_set_divisor(dev, 115200/100/8);
|
||||
bc->modem.ptt = 0;
|
||||
goto end_transmit;
|
||||
}
|
||||
}
|
||||
bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1));
|
||||
bc->modem.ser12.txshreg >>= 1;
|
||||
}
|
||||
end_transmit:
|
||||
local_irq_enable();
|
||||
if (!bc->modem.ptt && txcount) {
|
||||
hdlcdrv_arbitrate(dev, &bc->hdrv);
|
||||
if (hdlcdrv_ptt(&bc->hdrv)) {
|
||||
ser12_set_divisor(dev, bc->baud_uartdiv);
|
||||
bc->modem.ser12.txshreg = 1;
|
||||
bc->modem.ptt = 1;
|
||||
}
|
||||
}
|
||||
hdlcdrv_transmitter(dev, &bc->hdrv);
|
||||
hdlcdrv_receiver(dev, &bc->hdrv);
|
||||
local_irq_disable();
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
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 ser12_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 };
|
||||
|
||||
b1 = inb(MCR(iobase));
|
||||
outb(b1 | 0x10, MCR(iobase)); /* loopback mode */
|
||||
b2 = inb(MSR(iobase));
|
||||
outb(0x1a, MCR(iobase));
|
||||
b3 = inb(MSR(iobase)) & 0xf0;
|
||||
outb(b1, MCR(iobase)); /* restore old values */
|
||||
outb(b2, MSR(iobase));
|
||||
if (b3 != 0x90)
|
||||
return c_uart_unknown;
|
||||
inb(RBR(iobase));
|
||||
inb(RBR(iobase));
|
||||
outb(0x01, FCR(iobase)); /* enable FIFOs */
|
||||
u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
|
||||
if (u == c_uart_16450) {
|
||||
outb(0x5a, SCR(iobase));
|
||||
b1 = inb(SCR(iobase));
|
||||
outb(0xa5, SCR(iobase));
|
||||
b2 = inb(SCR(iobase));
|
||||
if ((b1 != 0x5a) || (b2 != 0xa5))
|
||||
u = c_uart_8250;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int ser12_open(struct net_device *dev)
|
||||
{
|
||||
struct baycom_state *bc = netdev_priv(dev);
|
||||
enum uart u;
|
||||
|
||||
if (!dev || !bc)
|
||||
return -ENXIO;
|
||||
if (!dev->base_addr || dev->base_addr > 0xffff-SER12_EXTENT ||
|
||||
dev->irq < 2 || dev->irq > nr_irqs) {
|
||||
printk(KERN_INFO "baycom_ser_fdx: invalid portnumber (max %u) "
|
||||
"or irq (2 <= irq <= %d)\n",
|
||||
0xffff-SER12_EXTENT, nr_irqs);
|
||||
return -ENXIO;
|
||||
}
|
||||
if (bc->baud < 300 || bc->baud > 4800) {
|
||||
printk(KERN_INFO "baycom_ser_fdx: invalid baudrate "
|
||||
"(300...4800)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx")) {
|
||||
printk(KERN_WARNING "BAYCOM_SER_FSX: I/O port 0x%04lx busy\n",
|
||||
dev->base_addr);
|
||||
return -EACCES;
|
||||
}
|
||||
memset(&bc->modem, 0, sizeof(bc->modem));
|
||||
bc->hdrv.par.bitrate = bc->baud;
|
||||
bc->baud_us = 1000000/bc->baud;
|
||||
bc->baud_uartdiv = (115200/8)/bc->baud;
|
||||
if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown){
|
||||
release_region(dev->base_addr, SER12_EXTENT);
|
||||
return -EIO;
|
||||
}
|
||||
outb(0, FCR(dev->base_addr)); /* disable FIFOs */
|
||||
outb(0x0d, MCR(dev->base_addr));
|
||||
outb(0, IER(dev->base_addr));
|
||||
if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED,
|
||||
"baycom_ser_fdx", dev)) {
|
||||
release_region(dev->base_addr, SER12_EXTENT);
|
||||
return -EBUSY;
|
||||
}
|
||||
/*
|
||||
* set the SIO to 6 Bits/character; during receive,
|
||||
* the baud rate is set to produce 100 ints/sec
|
||||
* to feed the channel arbitration process,
|
||||
* during transmit to baud ints/sec to run
|
||||
* the transmitter
|
||||
*/
|
||||
ser12_set_divisor(dev, 115200/100/8);
|
||||
/*
|
||||
* enable transmitter empty interrupt and modem status interrupt
|
||||
*/
|
||||
outb(0x0a, IER(dev->base_addr));
|
||||
/*
|
||||
* make sure the next interrupt is generated;
|
||||
* 0 must be used to power the modem; the modem draws its
|
||||
* power from the TxD line
|
||||
*/
|
||||
outb(0x00, THR(dev->base_addr));
|
||||
hdlcdrv_setdcd(&bc->hdrv, 0);
|
||||
printk(KERN_INFO "%s: ser_fdx at iobase 0x%lx irq %u baud %u uart %s\n",
|
||||
bc_drvname, dev->base_addr, dev->irq, bc->baud, uart_str[u]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int ser12_close(struct net_device *dev)
|
||||
{
|
||||
struct baycom_state *bc = netdev_priv(dev);
|
||||
|
||||
if (!dev || !bc)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* disable interrupts
|
||||
*/
|
||||
outb(0, IER(dev->base_addr));
|
||||
outb(1, MCR(dev->base_addr));
|
||||
free_irq(dev->irq, dev);
|
||||
release_region(dev->base_addr, SER12_EXTENT);
|
||||
printk(KERN_INFO "%s: close ser_fdx at iobase 0x%lx irq %u\n",
|
||||
bc_drvname, dev->base_addr, dev->irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/*
|
||||
* ===================== hdlcdrv driver interface =========================
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
|
||||
struct hdlcdrv_ioctl *hi, int cmd);
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static struct hdlcdrv_ops ser12_ops = {
|
||||
.drvname = bc_drvname,
|
||||
.drvinfo = bc_drvinfo,
|
||||
.open = ser12_open,
|
||||
.close = ser12_close,
|
||||
.ioctl = baycom_ioctl,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int baycom_setmode(struct baycom_state *bc, const char *modestr)
|
||||
{
|
||||
unsigned int baud;
|
||||
|
||||
if (!strncmp(modestr, "ser", 3)) {
|
||||
baud = simple_strtoul(modestr+3, NULL, 10);
|
||||
if (baud >= 3 && baud <= 48)
|
||||
bc->baud = baud*100;
|
||||
}
|
||||
if (strchr(modestr, '*'))
|
||||
bc->opt_dcd = 0;
|
||||
else if (strchr(modestr, '+'))
|
||||
bc->opt_dcd = -1;
|
||||
else
|
||||
bc->opt_dcd = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
|
||||
struct hdlcdrv_ioctl *hi, int cmd)
|
||||
{
|
||||
struct baycom_state *bc;
|
||||
struct baycom_ioctl bi;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
bc = netdev_priv(dev);
|
||||
BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC);
|
||||
|
||||
if (cmd != SIOCDEVPRIVATE)
|
||||
return -ENOIOCTLCMD;
|
||||
switch (hi->cmd) {
|
||||
default:
|
||||
break;
|
||||
|
||||
case HDLCDRVCTL_GETMODE:
|
||||
sprintf(hi->data.modename, "ser%u", bc->baud / 100);
|
||||
if (bc->opt_dcd <= 0)
|
||||
strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : "+");
|
||||
if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case HDLCDRVCTL_SETMODE:
|
||||
if (netif_running(dev) || !capable(CAP_NET_ADMIN))
|
||||
return -EACCES;
|
||||
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
|
||||
return baycom_setmode(bc, hi->data.modename);
|
||||
|
||||
case HDLCDRVCTL_MODELIST:
|
||||
strcpy(hi->data.modename, "ser12,ser3,ser24");
|
||||
if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case HDLCDRVCTL_MODEMPARMASK:
|
||||
return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
|
||||
|
||||
}
|
||||
|
||||
if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
|
||||
return -EFAULT;
|
||||
switch (bi.cmd) {
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
|
||||
#ifdef BAYCOM_DEBUG
|
||||
case BAYCOMCTL_GETDEBUG:
|
||||
bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
|
||||
bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
|
||||
bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
|
||||
break;
|
||||
#endif /* BAYCOM_DEBUG */
|
||||
|
||||
}
|
||||
if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* command line settable parameters
|
||||
*/
|
||||
static char *mode[NR_PORTS] = { "ser12*", };
|
||||
static int iobase[NR_PORTS] = { 0x3f8, };
|
||||
static int irq[NR_PORTS] = { 4, };
|
||||
static int baud[NR_PORTS] = { [0 ... NR_PORTS-1] = 1200 };
|
||||
|
||||
module_param_array(mode, charp, NULL, 0);
|
||||
MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
|
||||
module_param_array(iobase, int, NULL, 0);
|
||||
MODULE_PARM_DESC(iobase, "baycom io base address");
|
||||
module_param_array(irq, int, NULL, 0);
|
||||
MODULE_PARM_DESC(irq, "baycom irq number");
|
||||
module_param_array(baud, int, NULL, 0);
|
||||
MODULE_PARM_DESC(baud, "baycom baud rate (300 to 4800)");
|
||||
|
||||
MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
|
||||
MODULE_DESCRIPTION("Baycom ser12 full duplex amateur radio modem driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int __init init_baycomserfdx(void)
|
||||
{
|
||||
int i, found = 0;
|
||||
char set_hw = 1;
|
||||
|
||||
printk(bc_drvinfo);
|
||||
/*
|
||||
* register net devices
|
||||
*/
|
||||
for (i = 0; i < NR_PORTS; i++) {
|
||||
struct net_device *dev;
|
||||
struct baycom_state *bc;
|
||||
char ifname[IFNAMSIZ];
|
||||
|
||||
sprintf(ifname, "bcsf%d", i);
|
||||
|
||||
if (!mode[i])
|
||||
set_hw = 0;
|
||||
if (!set_hw)
|
||||
iobase[i] = irq[i] = 0;
|
||||
|
||||
dev = hdlcdrv_register(&ser12_ops,
|
||||
sizeof(struct baycom_state),
|
||||
ifname, iobase[i], irq[i], 0);
|
||||
if (IS_ERR(dev))
|
||||
break;
|
||||
|
||||
bc = netdev_priv(dev);
|
||||
if (set_hw && baycom_setmode(bc, mode[i]))
|
||||
set_hw = 0;
|
||||
bc->baud = baud[i];
|
||||
found++;
|
||||
baycom_device[i] = dev;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return -ENXIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_baycomserfdx(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < NR_PORTS; i++) {
|
||||
struct net_device *dev = baycom_device[i];
|
||||
if (dev)
|
||||
hdlcdrv_unregister(dev);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(init_baycomserfdx);
|
||||
module_exit(cleanup_baycomserfdx);
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#ifndef MODULE
|
||||
|
||||
/*
|
||||
* format: baycom_ser_fdx=io,irq,mode
|
||||
* mode: ser# hardware DCD
|
||||
* ser#* software DCD
|
||||
* ser#+ hardware DCD, inverted signal at DCD pin
|
||||
* '#' denotes the baud rate / 100, eg. ser12* is '1200 baud, soft DCD'
|
||||
*/
|
||||
|
||||
static int __init baycom_ser_fdx_setup(char *str)
|
||||
{
|
||||
static unsigned nr_dev;
|
||||
int ints[4];
|
||||
|
||||
if (nr_dev >= NR_PORTS)
|
||||
return 0;
|
||||
str = get_options(str, 4, ints);
|
||||
if (ints[0] < 2)
|
||||
return 0;
|
||||
mode[nr_dev] = str;
|
||||
iobase[nr_dev] = ints[1];
|
||||
irq[nr_dev] = ints[2];
|
||||
if (ints[0] >= 3)
|
||||
baud[nr_dev] = ints[3];
|
||||
nr_dev++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("baycom_ser_fdx=", baycom_ser_fdx_setup);
|
||||
|
||||
#endif /* MODULE */
|
||||
/* --------------------------------------------------------------------- */
|
743
drivers/net/hamradio/baycom_ser_hdx.c
Normal file
743
drivers/net/hamradio/baycom_ser_hdx.c
Normal file
|
@ -0,0 +1,743 @@
|
|||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* baycom_ser_hdx.c -- baycom ser12 halfduplex radio modem driver.
|
||||
*
|
||||
* Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Please note that the GPL allows you to use the driver, NOT the radio.
|
||||
* In order to use the radio, you need a license from the communications
|
||||
* authority of your country.
|
||||
*
|
||||
*
|
||||
* Supported modems
|
||||
*
|
||||
* ser12: This is a very simple 1200 baud AFSK modem. The modem consists only
|
||||
* of a modulator/demodulator chip, usually a TI TCM3105. The computer
|
||||
* is responsible for regenerating the receiver bit clock, as well as
|
||||
* for handling the HDLC protocol. The modem connects to a serial port,
|
||||
* hence the name. Since the serial port is not used as an async serial
|
||||
* port, the kernel driver for serial ports cannot be used, and this
|
||||
* driver only supports standard serial hardware (8250, 16450, 16550A)
|
||||
*
|
||||
*
|
||||
* Command line options (insmod command line)
|
||||
*
|
||||
* mode ser12 hardware DCD
|
||||
* ser12* software DCD
|
||||
* ser12@ hardware/software DCD, i.e. no explicit DCD signal but hardware
|
||||
* mutes audio input to the modem
|
||||
* ser12+ hardware DCD, inverted signal at DCD pin
|
||||
* iobase base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
|
||||
* irq interrupt line of the port; common values are 4,3
|
||||
*
|
||||
*
|
||||
* History:
|
||||
* 0.1 26.06.1996 Adapted from baycom.c and made network driver interface
|
||||
* 18.10.1996 Changed to new user space access routines (copy_{to,from}_user)
|
||||
* 0.3 26.04.1997 init code/data tagged
|
||||
* 0.4 08.07.1997 alternative ser12 decoding algorithm (uses delta CTS ints)
|
||||
* 0.5 11.11.1997 ser12/par96 split into separate files
|
||||
* 0.6 14.04.1998 cleanups
|
||||
* 0.7 03.08.1999 adapt to Linus' new __setup/__initcall
|
||||
* 0.8 10.08.1999 use module_init/module_exit
|
||||
* 0.9 12.02.2000 adapted to softnet driver interface
|
||||
* 0.10 03.07.2000 fix interface name handling
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#include <linux/capability.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/hdlcdrv.h>
|
||||
#include <linux/baycom.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#define BAYCOM_DEBUG
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static const char bc_drvname[] = "baycom_ser_hdx";
|
||||
static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
|
||||
"baycom_ser_hdx: version 0.10\n";
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#define NR_PORTS 4
|
||||
|
||||
static struct net_device *baycom_device[NR_PORTS];
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#define RBR(iobase) (iobase+0)
|
||||
#define THR(iobase) (iobase+0)
|
||||
#define IER(iobase) (iobase+1)
|
||||
#define IIR(iobase) (iobase+2)
|
||||
#define FCR(iobase) (iobase+2)
|
||||
#define LCR(iobase) (iobase+3)
|
||||
#define MCR(iobase) (iobase+4)
|
||||
#define LSR(iobase) (iobase+5)
|
||||
#define MSR(iobase) (iobase+6)
|
||||
#define SCR(iobase) (iobase+7)
|
||||
#define DLL(iobase) (iobase+0)
|
||||
#define DLM(iobase) (iobase+1)
|
||||
|
||||
#define SER12_EXTENT 8
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/*
|
||||
* Information that need to be kept for each board.
|
||||
*/
|
||||
|
||||
struct baycom_state {
|
||||
struct hdlcdrv_state hdrv;
|
||||
|
||||
int opt_dcd;
|
||||
|
||||
struct modem_state {
|
||||
short arb_divider;
|
||||
unsigned char flags;
|
||||
unsigned int shreg;
|
||||
struct modem_state_ser12 {
|
||||
unsigned char tx_bit;
|
||||
int dcd_sum0, dcd_sum1, dcd_sum2;
|
||||
unsigned char last_sample;
|
||||
unsigned char last_rxbit;
|
||||
unsigned int dcd_shreg;
|
||||
unsigned int dcd_time;
|
||||
unsigned int bit_pll;
|
||||
unsigned char interm_sample;
|
||||
} ser12;
|
||||
} modem;
|
||||
|
||||
#ifdef BAYCOM_DEBUG
|
||||
struct debug_vals {
|
||||
unsigned long last_jiffies;
|
||||
unsigned cur_intcnt;
|
||||
unsigned last_intcnt;
|
||||
int cur_pllcorr;
|
||||
int last_pllcorr;
|
||||
} debug_vals;
|
||||
#endif /* BAYCOM_DEBUG */
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static inline void baycom_int_freq(struct baycom_state *bc)
|
||||
{
|
||||
#ifdef BAYCOM_DEBUG
|
||||
unsigned long cur_jiffies = jiffies;
|
||||
/*
|
||||
* measure the interrupt frequency
|
||||
*/
|
||||
bc->debug_vals.cur_intcnt++;
|
||||
if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) {
|
||||
bc->debug_vals.last_jiffies = cur_jiffies;
|
||||
bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
|
||||
bc->debug_vals.cur_intcnt = 0;
|
||||
bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
|
||||
bc->debug_vals.cur_pllcorr = 0;
|
||||
}
|
||||
#endif /* BAYCOM_DEBUG */
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/*
|
||||
* ===================== SER12 specific routines =========================
|
||||
*/
|
||||
|
||||
static inline void ser12_set_divisor(struct net_device *dev,
|
||||
unsigned char divisor)
|
||||
{
|
||||
outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */
|
||||
outb(divisor, DLL(dev->base_addr));
|
||||
outb(0, DLM(dev->base_addr));
|
||||
outb(0x01, LCR(dev->base_addr)); /* word length = 6 */
|
||||
/*
|
||||
* make sure the next interrupt is generated;
|
||||
* 0 must be used to power the modem; the modem draws its
|
||||
* power from the TxD line
|
||||
*/
|
||||
outb(0x00, THR(dev->base_addr));
|
||||
/*
|
||||
* it is important not to set the divider while transmitting;
|
||||
* this reportedly makes some UARTs generating interrupts
|
||||
* in the hundredthousands per second region
|
||||
* Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
|
||||
*/
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* must call the TX arbitrator every 10ms
|
||||
*/
|
||||
#define SER12_ARB_DIVIDER(bc) (bc->opt_dcd ? 24 : 36)
|
||||
|
||||
#define SER12_DCD_INTERVAL(bc) (bc->opt_dcd ? 12 : 240)
|
||||
|
||||
static inline void ser12_tx(struct net_device *dev, struct baycom_state *bc)
|
||||
{
|
||||
/* one interrupt per channel bit */
|
||||
ser12_set_divisor(dev, 12);
|
||||
/*
|
||||
* first output the last bit (!) then call HDLC transmitter,
|
||||
* since this may take quite long
|
||||
*/
|
||||
outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
|
||||
if (bc->modem.shreg <= 1)
|
||||
bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
|
||||
bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^
|
||||
(bc->modem.shreg & 1));
|
||||
bc->modem.shreg >>= 1;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static inline void ser12_rx(struct net_device *dev, struct baycom_state *bc)
|
||||
{
|
||||
unsigned char cur_s;
|
||||
/*
|
||||
* do demodulator
|
||||
*/
|
||||
cur_s = inb(MSR(dev->base_addr)) & 0x10; /* the CTS line */
|
||||
hdlcdrv_channelbit(&bc->hdrv, cur_s);
|
||||
bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) |
|
||||
(cur_s != bc->modem.ser12.last_sample);
|
||||
bc->modem.ser12.last_sample = cur_s;
|
||||
if(bc->modem.ser12.dcd_shreg & 1) {
|
||||
if (!bc->opt_dcd) {
|
||||
unsigned int dcdspos, dcdsneg;
|
||||
|
||||
dcdspos = dcdsneg = 0;
|
||||
dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
|
||||
if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
|
||||
dcdspos += 2;
|
||||
dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
|
||||
dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
|
||||
dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
|
||||
|
||||
bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
|
||||
} else
|
||||
bc->modem.ser12.dcd_sum0--;
|
||||
}
|
||||
if(!bc->modem.ser12.dcd_time) {
|
||||
hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
|
||||
bc->modem.ser12.dcd_sum1 +
|
||||
bc->modem.ser12.dcd_sum2) < 0);
|
||||
bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
|
||||
bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
|
||||
/* offset to ensure DCD off on silent input */
|
||||
bc->modem.ser12.dcd_sum0 = 2;
|
||||
bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
|
||||
}
|
||||
bc->modem.ser12.dcd_time--;
|
||||
if (!bc->opt_dcd) {
|
||||
/*
|
||||
* PLL code for the improved software DCD algorithm
|
||||
*/
|
||||
if (bc->modem.ser12.interm_sample) {
|
||||
/*
|
||||
* intermediate sample; set timing correction to normal
|
||||
*/
|
||||
ser12_set_divisor(dev, 4);
|
||||
} else {
|
||||
/*
|
||||
* do PLL correction and call HDLC receiver
|
||||
*/
|
||||
switch (bc->modem.ser12.dcd_shreg & 7) {
|
||||
case 1: /* transition too late */
|
||||
ser12_set_divisor(dev, 5);
|
||||
#ifdef BAYCOM_DEBUG
|
||||
bc->debug_vals.cur_pllcorr++;
|
||||
#endif /* BAYCOM_DEBUG */
|
||||
break;
|
||||
case 4: /* transition too early */
|
||||
ser12_set_divisor(dev, 3);
|
||||
#ifdef BAYCOM_DEBUG
|
||||
bc->debug_vals.cur_pllcorr--;
|
||||
#endif /* BAYCOM_DEBUG */
|
||||
break;
|
||||
default:
|
||||
ser12_set_divisor(dev, 4);
|
||||
break;
|
||||
}
|
||||
bc->modem.shreg >>= 1;
|
||||
if (bc->modem.ser12.last_sample ==
|
||||
bc->modem.ser12.last_rxbit)
|
||||
bc->modem.shreg |= 0x10000;
|
||||
bc->modem.ser12.last_rxbit =
|
||||
bc->modem.ser12.last_sample;
|
||||
}
|
||||
if (++bc->modem.ser12.interm_sample >= 3)
|
||||
bc->modem.ser12.interm_sample = 0;
|
||||
/*
|
||||
* DCD stuff
|
||||
*/
|
||||
if (bc->modem.ser12.dcd_shreg & 1) {
|
||||
unsigned int dcdspos, dcdsneg;
|
||||
|
||||
dcdspos = dcdsneg = 0;
|
||||
dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
|
||||
dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
|
||||
<< 1;
|
||||
dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
|
||||
dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
|
||||
dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
|
||||
|
||||
bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* PLL algorithm for the hardware squelch DCD algorithm
|
||||
*/
|
||||
if (bc->modem.ser12.interm_sample) {
|
||||
/*
|
||||
* intermediate sample; set timing correction to normal
|
||||
*/
|
||||
ser12_set_divisor(dev, 6);
|
||||
} else {
|
||||
/*
|
||||
* do PLL correction and call HDLC receiver
|
||||
*/
|
||||
switch (bc->modem.ser12.dcd_shreg & 3) {
|
||||
case 1: /* transition too late */
|
||||
ser12_set_divisor(dev, 7);
|
||||
#ifdef BAYCOM_DEBUG
|
||||
bc->debug_vals.cur_pllcorr++;
|
||||
#endif /* BAYCOM_DEBUG */
|
||||
break;
|
||||
case 2: /* transition too early */
|
||||
ser12_set_divisor(dev, 5);
|
||||
#ifdef BAYCOM_DEBUG
|
||||
bc->debug_vals.cur_pllcorr--;
|
||||
#endif /* BAYCOM_DEBUG */
|
||||
break;
|
||||
default:
|
||||
ser12_set_divisor(dev, 6);
|
||||
break;
|
||||
}
|
||||
bc->modem.shreg >>= 1;
|
||||
if (bc->modem.ser12.last_sample ==
|
||||
bc->modem.ser12.last_rxbit)
|
||||
bc->modem.shreg |= 0x10000;
|
||||
bc->modem.ser12.last_rxbit =
|
||||
bc->modem.ser12.last_sample;
|
||||
}
|
||||
bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
|
||||
/*
|
||||
* DCD stuff
|
||||
*/
|
||||
bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1);
|
||||
}
|
||||
outb(0x0d, MCR(dev->base_addr)); /* transmitter off */
|
||||
if (bc->modem.shreg & 1) {
|
||||
hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
|
||||
bc->modem.shreg = 0x10000;
|
||||
}
|
||||
if(!bc->modem.ser12.dcd_time) {
|
||||
if (bc->opt_dcd & 1)
|
||||
hdlcdrv_setdcd(&bc->hdrv, !((inb(MSR(dev->base_addr)) ^ bc->opt_dcd) & 0x80));
|
||||
else
|
||||
hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
|
||||
bc->modem.ser12.dcd_sum1 +
|
||||
bc->modem.ser12.dcd_sum2) < 0);
|
||||
bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
|
||||
bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
|
||||
/* offset to ensure DCD off on silent input */
|
||||
bc->modem.ser12.dcd_sum0 = 2;
|
||||
bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
|
||||
}
|
||||
bc->modem.ser12.dcd_time--;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static irqreturn_t ser12_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = (struct net_device *)dev_id;
|
||||
struct baycom_state *bc = netdev_priv(dev);
|
||||
unsigned char iir;
|
||||
|
||||
if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
|
||||
return IRQ_NONE;
|
||||
/* fast way out */
|
||||
if ((iir = inb(IIR(dev->base_addr))) & 1)
|
||||
return IRQ_NONE;
|
||||
baycom_int_freq(bc);
|
||||
do {
|
||||
switch (iir & 6) {
|
||||
case 6:
|
||||
inb(LSR(dev->base_addr));
|
||||
break;
|
||||
|
||||
case 4:
|
||||
inb(RBR(dev->base_addr));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/*
|
||||
* check if transmitter active
|
||||
*/
|
||||
if (hdlcdrv_ptt(&bc->hdrv))
|
||||
ser12_tx(dev, bc);
|
||||
else {
|
||||
ser12_rx(dev, bc);
|
||||
bc->modem.arb_divider--;
|
||||
}
|
||||
outb(0x00, THR(dev->base_addr));
|
||||
break;
|
||||
|
||||
default:
|
||||
inb(MSR(dev->base_addr));
|
||||
break;
|
||||
}
|
||||
iir = inb(IIR(dev->base_addr));
|
||||
} while (!(iir & 1));
|
||||
if (bc->modem.arb_divider <= 0) {
|
||||
bc->modem.arb_divider = SER12_ARB_DIVIDER(bc);
|
||||
local_irq_enable();
|
||||
hdlcdrv_arbitrate(dev, &bc->hdrv);
|
||||
}
|
||||
local_irq_enable();
|
||||
hdlcdrv_transmitter(dev, &bc->hdrv);
|
||||
hdlcdrv_receiver(dev, &bc->hdrv);
|
||||
local_irq_disable();
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
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 ser12_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 };
|
||||
|
||||
b1 = inb(MCR(iobase));
|
||||
outb(b1 | 0x10, MCR(iobase)); /* loopback mode */
|
||||
b2 = inb(MSR(iobase));
|
||||
outb(0x1a, MCR(iobase));
|
||||
b3 = inb(MSR(iobase)) & 0xf0;
|
||||
outb(b1, MCR(iobase)); /* restore old values */
|
||||
outb(b2, MSR(iobase));
|
||||
if (b3 != 0x90)
|
||||
return c_uart_unknown;
|
||||
inb(RBR(iobase));
|
||||
inb(RBR(iobase));
|
||||
outb(0x01, FCR(iobase)); /* enable FIFOs */
|
||||
u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
|
||||
if (u == c_uart_16450) {
|
||||
outb(0x5a, SCR(iobase));
|
||||
b1 = inb(SCR(iobase));
|
||||
outb(0xa5, SCR(iobase));
|
||||
b2 = inb(SCR(iobase));
|
||||
if ((b1 != 0x5a) || (b2 != 0xa5))
|
||||
u = c_uart_8250;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int ser12_open(struct net_device *dev)
|
||||
{
|
||||
struct baycom_state *bc = netdev_priv(dev);
|
||||
enum uart u;
|
||||
|
||||
if (!dev || !bc)
|
||||
return -ENXIO;
|
||||
if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
|
||||
dev->irq < 2 || dev->irq > 15)
|
||||
return -ENXIO;
|
||||
if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12"))
|
||||
return -EACCES;
|
||||
memset(&bc->modem, 0, sizeof(bc->modem));
|
||||
bc->hdrv.par.bitrate = 1200;
|
||||
if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown) {
|
||||
release_region(dev->base_addr, SER12_EXTENT);
|
||||
return -EIO;
|
||||
}
|
||||
outb(0, FCR(dev->base_addr)); /* disable FIFOs */
|
||||
outb(0x0d, MCR(dev->base_addr));
|
||||
outb(0, IER(dev->base_addr));
|
||||
if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED,
|
||||
"baycom_ser12", dev)) {
|
||||
release_region(dev->base_addr, SER12_EXTENT);
|
||||
return -EBUSY;
|
||||
}
|
||||
/*
|
||||
* enable transmitter empty interrupt
|
||||
*/
|
||||
outb(2, IER(dev->base_addr));
|
||||
/*
|
||||
* set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
|
||||
* we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
|
||||
* depending on the usage of the software DCD routine
|
||||
*/
|
||||
ser12_set_divisor(dev, bc->opt_dcd ? 6 : 4);
|
||||
printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u uart %s\n",
|
||||
bc_drvname, dev->base_addr, dev->irq, uart_str[u]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int ser12_close(struct net_device *dev)
|
||||
{
|
||||
struct baycom_state *bc = netdev_priv(dev);
|
||||
|
||||
if (!dev || !bc)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* disable interrupts
|
||||
*/
|
||||
outb(0, IER(dev->base_addr));
|
||||
outb(1, MCR(dev->base_addr));
|
||||
free_irq(dev->irq, dev);
|
||||
release_region(dev->base_addr, SER12_EXTENT);
|
||||
printk(KERN_INFO "%s: close ser12 at iobase 0x%lx irq %u\n",
|
||||
bc_drvname, dev->base_addr, dev->irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/*
|
||||
* ===================== hdlcdrv driver interface =========================
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
|
||||
struct hdlcdrv_ioctl *hi, int cmd);
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static struct hdlcdrv_ops ser12_ops = {
|
||||
.drvname = bc_drvname,
|
||||
.drvinfo = bc_drvinfo,
|
||||
.open = ser12_open,
|
||||
.close = ser12_close,
|
||||
.ioctl = baycom_ioctl,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int baycom_setmode(struct baycom_state *bc, const char *modestr)
|
||||
{
|
||||
if (strchr(modestr, '*'))
|
||||
bc->opt_dcd = 0;
|
||||
else if (strchr(modestr, '+'))
|
||||
bc->opt_dcd = -1;
|
||||
else if (strchr(modestr, '@'))
|
||||
bc->opt_dcd = -2;
|
||||
else
|
||||
bc->opt_dcd = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
|
||||
struct hdlcdrv_ioctl *hi, int cmd)
|
||||
{
|
||||
struct baycom_state *bc;
|
||||
struct baycom_ioctl bi;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
bc = netdev_priv(dev);
|
||||
BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC);
|
||||
|
||||
if (cmd != SIOCDEVPRIVATE)
|
||||
return -ENOIOCTLCMD;
|
||||
switch (hi->cmd) {
|
||||
default:
|
||||
break;
|
||||
|
||||
case HDLCDRVCTL_GETMODE:
|
||||
strcpy(hi->data.modename, "ser12");
|
||||
if (bc->opt_dcd <= 0)
|
||||
strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : (bc->opt_dcd == -2) ? "@" : "+");
|
||||
if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case HDLCDRVCTL_SETMODE:
|
||||
if (netif_running(dev) || !capable(CAP_NET_ADMIN))
|
||||
return -EACCES;
|
||||
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
|
||||
return baycom_setmode(bc, hi->data.modename);
|
||||
|
||||
case HDLCDRVCTL_MODELIST:
|
||||
strcpy(hi->data.modename, "ser12");
|
||||
if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case HDLCDRVCTL_MODEMPARMASK:
|
||||
return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
|
||||
|
||||
}
|
||||
|
||||
if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
|
||||
return -EFAULT;
|
||||
switch (bi.cmd) {
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
|
||||
#ifdef BAYCOM_DEBUG
|
||||
case BAYCOMCTL_GETDEBUG:
|
||||
bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
|
||||
bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
|
||||
bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
|
||||
break;
|
||||
#endif /* BAYCOM_DEBUG */
|
||||
|
||||
}
|
||||
if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* command line settable parameters
|
||||
*/
|
||||
static char *mode[NR_PORTS] = { "ser12*", };
|
||||
static int iobase[NR_PORTS] = { 0x3f8, };
|
||||
static int irq[NR_PORTS] = { 4, };
|
||||
|
||||
module_param_array(mode, charp, NULL, 0);
|
||||
MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
|
||||
module_param_array(iobase, int, NULL, 0);
|
||||
MODULE_PARM_DESC(iobase, "baycom io base address");
|
||||
module_param_array(irq, int, NULL, 0);
|
||||
MODULE_PARM_DESC(irq, "baycom irq number");
|
||||
|
||||
MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
|
||||
MODULE_DESCRIPTION("Baycom ser12 half duplex amateur radio modem driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int __init init_baycomserhdx(void)
|
||||
{
|
||||
int i, found = 0;
|
||||
char set_hw = 1;
|
||||
|
||||
printk(bc_drvinfo);
|
||||
/*
|
||||
* register net devices
|
||||
*/
|
||||
for (i = 0; i < NR_PORTS; i++) {
|
||||
struct net_device *dev;
|
||||
struct baycom_state *bc;
|
||||
char ifname[IFNAMSIZ];
|
||||
|
||||
sprintf(ifname, "bcsh%d", i);
|
||||
|
||||
if (!mode[i])
|
||||
set_hw = 0;
|
||||
if (!set_hw)
|
||||
iobase[i] = irq[i] = 0;
|
||||
|
||||
dev = hdlcdrv_register(&ser12_ops,
|
||||
sizeof(struct baycom_state),
|
||||
ifname, iobase[i], irq[i], 0);
|
||||
if (IS_ERR(dev))
|
||||
break;
|
||||
|
||||
bc = netdev_priv(dev);
|
||||
if (set_hw && baycom_setmode(bc, mode[i]))
|
||||
set_hw = 0;
|
||||
found++;
|
||||
baycom_device[i] = dev;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return -ENXIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_baycomserhdx(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < NR_PORTS; i++) {
|
||||
struct net_device *dev = baycom_device[i];
|
||||
|
||||
if (dev)
|
||||
hdlcdrv_unregister(dev);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(init_baycomserhdx);
|
||||
module_exit(cleanup_baycomserhdx);
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#ifndef MODULE
|
||||
|
||||
/*
|
||||
* format: baycom_ser_hdx=io,irq,mode
|
||||
* mode: ser12 hardware DCD
|
||||
* ser12* software DCD
|
||||
* ser12@ hardware/software DCD, i.e. no explicit DCD signal but hardware
|
||||
* mutes audio input to the modem
|
||||
* ser12+ hardware DCD, inverted signal at DCD pin
|
||||
*/
|
||||
|
||||
static int __init baycom_ser_hdx_setup(char *str)
|
||||
{
|
||||
static unsigned nr_dev;
|
||||
int ints[3];
|
||||
|
||||
if (nr_dev >= NR_PORTS)
|
||||
return 0;
|
||||
str = get_options(str, 3, ints);
|
||||
if (ints[0] < 2)
|
||||
return 0;
|
||||
mode[nr_dev] = str;
|
||||
iobase[nr_dev] = ints[1];
|
||||
irq[nr_dev] = ints[2];
|
||||
nr_dev++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("baycom_ser_hdx=", baycom_ser_hdx_setup);
|
||||
|
||||
#endif /* MODULE */
|
||||
/* --------------------------------------------------------------------- */
|
629
drivers/net/hamradio/bpqether.c
Normal file
629
drivers/net/hamradio/bpqether.c
Normal file
|
@ -0,0 +1,629 @@
|
|||
/*
|
||||
* G8BPQ compatible "AX.25 via ethernet" driver release 004
|
||||
*
|
||||
* This code REQUIRES 2.0.0 or higher/ NET3.029
|
||||
*
|
||||
* This module:
|
||||
* This module 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 is a "pseudo" network driver to allow AX.25 over Ethernet
|
||||
* using G8BPQ encapsulation. It has been extracted from the protocol
|
||||
* implementation because
|
||||
*
|
||||
* - things got unreadable within the protocol stack
|
||||
* - to cure the protocol stack from "feature-ism"
|
||||
* - a protocol implementation shouldn't need to know on
|
||||
* which hardware it is running
|
||||
* - user-level programs like the AX.25 utilities shouldn't
|
||||
* need to know about the hardware.
|
||||
* - IP over ethernet encapsulated AX.25 was impossible
|
||||
* - rxecho.c did not work
|
||||
* - to have room for extensions
|
||||
* - it just deserves to "live" as an own driver
|
||||
*
|
||||
* This driver can use any ethernet destination address, and can be
|
||||
* limited to accept frames from one dedicated ethernet card only.
|
||||
*
|
||||
* Note that the driver sets up the BPQ devices automagically on
|
||||
* startup or (if started before the "insmod" of an ethernet device)
|
||||
* on "ifconfig up". It hopefully will remove the BPQ on "rmmod"ing
|
||||
* the ethernet device (in fact: as soon as another ethernet or bpq
|
||||
* device gets "ifconfig"ured).
|
||||
*
|
||||
* I have heard that several people are thinking of experiments
|
||||
* with highspeed packet radio using existing ethernet cards.
|
||||
* Well, this driver is prepared for this purpose, just add
|
||||
* your tx key control and a txdelay / tailtime algorithm,
|
||||
* probably some buffering, and /voila/...
|
||||
*
|
||||
* History
|
||||
* BPQ 001 Joerg(DL1BKE) Extracted BPQ code from AX.25
|
||||
* protocol stack and added my own
|
||||
* yet existing patches
|
||||
* BPQ 002 Joerg(DL1BKE) Scan network device list on
|
||||
* startup.
|
||||
* BPQ 003 Joerg(DL1BKE) Ethernet destination address
|
||||
* and accepted source address
|
||||
* can be configured by an ioctl()
|
||||
* call.
|
||||
* Fixed to match Linux networking
|
||||
* changes - 2.1.15.
|
||||
* BPQ 004 Joerg(DL1BKE) Fixed to not lock up on ifconfig.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/ax25.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include <net/ip.h>
|
||||
#include <net/arp.h>
|
||||
#include <net/net_namespace.h>
|
||||
|
||||
#include <linux/bpqether.h>
|
||||
|
||||
static const char banner[] __initconst = KERN_INFO \
|
||||
"AX.25: bpqether driver version 004\n";
|
||||
|
||||
static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
|
||||
|
||||
static char bpq_eth_addr[6];
|
||||
|
||||
static int bpq_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *);
|
||||
static int bpq_device_event(struct notifier_block *, unsigned long, void *);
|
||||
|
||||
static struct packet_type bpq_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_BPQ),
|
||||
.func = bpq_rcv,
|
||||
};
|
||||
|
||||
static struct notifier_block bpq_dev_notifier = {
|
||||
.notifier_call = bpq_device_event,
|
||||
};
|
||||
|
||||
|
||||
struct bpqdev {
|
||||
struct list_head bpq_list; /* list of bpq devices chain */
|
||||
struct net_device *ethdev; /* link to ethernet device */
|
||||
struct net_device *axdev; /* bpq device (bpq#) */
|
||||
char dest_addr[6]; /* ether destination address */
|
||||
char acpt_addr[6]; /* accept ether frames from this address only */
|
||||
};
|
||||
|
||||
static LIST_HEAD(bpq_devices);
|
||||
|
||||
/*
|
||||
* bpqether network devices are paired with ethernet devices below them, so
|
||||
* form a special "super class" of normal ethernet devices; split their locks
|
||||
* off into a separate class since they always nest.
|
||||
*/
|
||||
static struct lock_class_key bpq_netdev_xmit_lock_key;
|
||||
static struct lock_class_key bpq_netdev_addr_lock_key;
|
||||
|
||||
static void bpq_set_lockdep_class_one(struct net_device *dev,
|
||||
struct netdev_queue *txq,
|
||||
void *_unused)
|
||||
{
|
||||
lockdep_set_class(&txq->_xmit_lock, &bpq_netdev_xmit_lock_key);
|
||||
}
|
||||
|
||||
static void bpq_set_lockdep_class(struct net_device *dev)
|
||||
{
|
||||
lockdep_set_class(&dev->addr_list_lock, &bpq_netdev_addr_lock_key);
|
||||
netdev_for_each_tx_queue(dev, bpq_set_lockdep_class_one, NULL);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
|
||||
/*
|
||||
* Get the ethernet device for a BPQ device
|
||||
*/
|
||||
static inline struct net_device *bpq_get_ether_dev(struct net_device *dev)
|
||||
{
|
||||
struct bpqdev *bpq = netdev_priv(dev);
|
||||
|
||||
return bpq ? bpq->ethdev : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the BPQ device for the ethernet device
|
||||
*/
|
||||
static inline struct net_device *bpq_get_ax25_dev(struct net_device *dev)
|
||||
{
|
||||
struct bpqdev *bpq;
|
||||
|
||||
list_for_each_entry_rcu(bpq, &bpq_devices, bpq_list) {
|
||||
if (bpq->ethdev == dev)
|
||||
return bpq->axdev;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int dev_is_ethdev(struct net_device *dev)
|
||||
{
|
||||
return dev->type == ARPHRD_ETHER && strncmp(dev->name, "dummy", 5);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
|
||||
/*
|
||||
* Receive an AX.25 frame via an ethernet interface.
|
||||
*/
|
||||
static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev)
|
||||
{
|
||||
int len;
|
||||
char * ptr;
|
||||
struct ethhdr *eth;
|
||||
struct bpqdev *bpq;
|
||||
|
||||
if (!net_eq(dev_net(dev), &init_net))
|
||||
goto drop;
|
||||
|
||||
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
|
||||
return NET_RX_DROP;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(struct ethhdr)))
|
||||
goto drop;
|
||||
|
||||
rcu_read_lock();
|
||||
dev = bpq_get_ax25_dev(dev);
|
||||
|
||||
if (dev == NULL || !netif_running(dev))
|
||||
goto drop_unlock;
|
||||
|
||||
/*
|
||||
* if we want to accept frames from just one ethernet device
|
||||
* we check the source address of the sender.
|
||||
*/
|
||||
|
||||
bpq = netdev_priv(dev);
|
||||
|
||||
eth = eth_hdr(skb);
|
||||
|
||||
if (!(bpq->acpt_addr[0] & 0x01) &&
|
||||
!ether_addr_equal(eth->h_source, bpq->acpt_addr))
|
||||
goto drop_unlock;
|
||||
|
||||
if (skb_cow(skb, sizeof(struct ethhdr)))
|
||||
goto drop_unlock;
|
||||
|
||||
len = skb->data[0] + skb->data[1] * 256 - 5;
|
||||
|
||||
skb_pull(skb, 2); /* Remove the length bytes */
|
||||
skb_trim(skb, len); /* Set the length of the data */
|
||||
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += len;
|
||||
|
||||
ptr = skb_push(skb, 1);
|
||||
*ptr = 0;
|
||||
|
||||
skb->protocol = ax25_type_trans(skb, dev);
|
||||
netif_rx(skb);
|
||||
unlock:
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return 0;
|
||||
drop_unlock:
|
||||
kfree_skb(skb);
|
||||
goto unlock;
|
||||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an AX.25 frame via an ethernet interface
|
||||
*/
|
||||
static netdev_tx_t bpq_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
unsigned char *ptr;
|
||||
struct bpqdev *bpq;
|
||||
struct net_device *orig_dev;
|
||||
int size;
|
||||
|
||||
/*
|
||||
* Just to be *really* sure not to send anything if the interface
|
||||
* is down, the ethernet device may have gone.
|
||||
*/
|
||||
if (!netif_running(dev)) {
|
||||
kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
skb_pull(skb, 1); /* Drop KISS byte */
|
||||
size = skb->len;
|
||||
|
||||
/*
|
||||
* We're about to mess with the skb which may still shared with the
|
||||
* generic networking code so unshare and ensure it's got enough
|
||||
* space for the BPQ headers.
|
||||
*/
|
||||
if (skb_cow(skb, AX25_BPQ_HEADER_LEN)) {
|
||||
if (net_ratelimit())
|
||||
pr_err("bpqether: out of memory\n");
|
||||
kfree_skb(skb);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
ptr = skb_push(skb, 2); /* Make space for length */
|
||||
|
||||
*ptr++ = (size + 5) % 256;
|
||||
*ptr++ = (size + 5) / 256;
|
||||
|
||||
bpq = netdev_priv(dev);
|
||||
|
||||
orig_dev = dev;
|
||||
if ((dev = bpq_get_ether_dev(dev)) == NULL) {
|
||||
orig_dev->stats.tx_dropped++;
|
||||
kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
skb->protocol = ax25_type_trans(skb, dev);
|
||||
skb_reset_network_header(skb);
|
||||
dev_hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0);
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes+=skb->len;
|
||||
|
||||
dev_queue_xmit(skb);
|
||||
netif_wake_queue(dev);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set AX.25 callsign
|
||||
*/
|
||||
static int bpq_set_mac_address(struct net_device *dev, void *addr)
|
||||
{
|
||||
struct sockaddr *sa = (struct sockaddr *)addr;
|
||||
|
||||
memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ioctl commands
|
||||
*
|
||||
* SIOCSBPQETHOPT reserved for enhancements
|
||||
* SIOCSBPQETHADDR set the destination and accepted
|
||||
* source ethernet address (broadcast
|
||||
* or multicast: accept all)
|
||||
*/
|
||||
static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
struct bpq_ethaddr __user *ethaddr = ifr->ifr_data;
|
||||
struct bpqdev *bpq = netdev_priv(dev);
|
||||
struct bpq_req req;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
switch (cmd) {
|
||||
case SIOCSBPQETHOPT:
|
||||
if (copy_from_user(&req, ifr->ifr_data, sizeof(struct bpq_req)))
|
||||
return -EFAULT;
|
||||
switch (req.cmd) {
|
||||
case SIOCGBPQETHPARAM:
|
||||
case SIOCSBPQETHPARAM:
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SIOCSBPQETHADDR:
|
||||
if (copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN))
|
||||
return -EFAULT;
|
||||
if (copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN))
|
||||
return -EFAULT;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* open/close a device
|
||||
*/
|
||||
static int bpq_open(struct net_device *dev)
|
||||
{
|
||||
netif_start_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bpq_close(struct net_device *dev)
|
||||
{
|
||||
netif_stop_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
|
||||
/*
|
||||
* Proc filesystem
|
||||
*/
|
||||
static void *bpq_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
__acquires(RCU)
|
||||
{
|
||||
int i = 1;
|
||||
struct bpqdev *bpqdev;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
if (*pos == 0)
|
||||
return SEQ_START_TOKEN;
|
||||
|
||||
list_for_each_entry_rcu(bpqdev, &bpq_devices, bpq_list) {
|
||||
if (i == *pos)
|
||||
return bpqdev;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *bpq_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
struct list_head *p;
|
||||
struct bpqdev *bpqdev = v;
|
||||
|
||||
++*pos;
|
||||
|
||||
if (v == SEQ_START_TOKEN)
|
||||
p = rcu_dereference(list_next_rcu(&bpq_devices));
|
||||
else
|
||||
p = rcu_dereference(list_next_rcu(&bpqdev->bpq_list));
|
||||
|
||||
return (p == &bpq_devices) ? NULL
|
||||
: list_entry(p, struct bpqdev, bpq_list);
|
||||
}
|
||||
|
||||
static void bpq_seq_stop(struct seq_file *seq, void *v)
|
||||
__releases(RCU)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
|
||||
static int bpq_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
if (v == SEQ_START_TOKEN)
|
||||
seq_puts(seq,
|
||||
"dev ether destination accept from\n");
|
||||
else {
|
||||
const struct bpqdev *bpqdev = v;
|
||||
|
||||
seq_printf(seq, "%-5s %-10s %pM ",
|
||||
bpqdev->axdev->name, bpqdev->ethdev->name,
|
||||
bpqdev->dest_addr);
|
||||
|
||||
if (is_multicast_ether_addr(bpqdev->acpt_addr))
|
||||
seq_printf(seq, "*\n");
|
||||
else
|
||||
seq_printf(seq, "%pM\n", bpqdev->acpt_addr);
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations bpq_seqops = {
|
||||
.start = bpq_seq_start,
|
||||
.next = bpq_seq_next,
|
||||
.stop = bpq_seq_stop,
|
||||
.show = bpq_seq_show,
|
||||
};
|
||||
|
||||
static int bpq_info_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &bpq_seqops);
|
||||
}
|
||||
|
||||
static const struct file_operations bpq_info_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = bpq_info_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static const struct net_device_ops bpq_netdev_ops = {
|
||||
.ndo_open = bpq_open,
|
||||
.ndo_stop = bpq_close,
|
||||
.ndo_start_xmit = bpq_xmit,
|
||||
.ndo_set_mac_address = bpq_set_mac_address,
|
||||
.ndo_do_ioctl = bpq_ioctl,
|
||||
};
|
||||
|
||||
static void bpq_setup(struct net_device *dev)
|
||||
{
|
||||
dev->netdev_ops = &bpq_netdev_ops;
|
||||
dev->destructor = free_netdev;
|
||||
|
||||
memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN);
|
||||
memcpy(dev->dev_addr, &ax25_defaddr, AX25_ADDR_LEN);
|
||||
|
||||
dev->flags = 0;
|
||||
|
||||
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
|
||||
dev->header_ops = &ax25_header_ops;
|
||||
#endif
|
||||
|
||||
dev->type = ARPHRD_AX25;
|
||||
dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
|
||||
dev->mtu = AX25_DEF_PACLEN;
|
||||
dev->addr_len = AX25_ADDR_LEN;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup a new device.
|
||||
*/
|
||||
static int bpq_new_device(struct net_device *edev)
|
||||
{
|
||||
int err;
|
||||
struct net_device *ndev;
|
||||
struct bpqdev *bpq;
|
||||
|
||||
ndev = alloc_netdev(sizeof(struct bpqdev), "bpq%d", NET_NAME_UNKNOWN,
|
||||
bpq_setup);
|
||||
if (!ndev)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
bpq = netdev_priv(ndev);
|
||||
dev_hold(edev);
|
||||
bpq->ethdev = edev;
|
||||
bpq->axdev = ndev;
|
||||
|
||||
memcpy(bpq->dest_addr, bcast_addr, sizeof(bpq_eth_addr));
|
||||
memcpy(bpq->acpt_addr, bcast_addr, sizeof(bpq_eth_addr));
|
||||
|
||||
err = register_netdevice(ndev);
|
||||
if (err)
|
||||
goto error;
|
||||
bpq_set_lockdep_class(ndev);
|
||||
|
||||
/* List protected by RTNL */
|
||||
list_add_rcu(&bpq->bpq_list, &bpq_devices);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
dev_put(edev);
|
||||
free_netdev(ndev);
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
static void bpq_free_device(struct net_device *ndev)
|
||||
{
|
||||
struct bpqdev *bpq = netdev_priv(ndev);
|
||||
|
||||
dev_put(bpq->ethdev);
|
||||
list_del_rcu(&bpq->bpq_list);
|
||||
|
||||
unregister_netdevice(ndev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle device status changes.
|
||||
*/
|
||||
static int bpq_device_event(struct notifier_block *this,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
|
||||
if (!net_eq(dev_net(dev), &init_net))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (!dev_is_ethdev(dev))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
switch (event) {
|
||||
case NETDEV_UP: /* new ethernet device -> new BPQ interface */
|
||||
if (bpq_get_ax25_dev(dev) == NULL)
|
||||
bpq_new_device(dev);
|
||||
break;
|
||||
|
||||
case NETDEV_DOWN: /* ethernet device closed -> close BPQ interface */
|
||||
if ((dev = bpq_get_ax25_dev(dev)) != NULL)
|
||||
dev_close(dev);
|
||||
break;
|
||||
|
||||
case NETDEV_UNREGISTER: /* ethernet device removed -> free BPQ interface */
|
||||
if ((dev = bpq_get_ax25_dev(dev)) != NULL)
|
||||
bpq_free_device(dev);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/*
|
||||
* Initialize driver. To be called from af_ax25 if not compiled as a
|
||||
* module
|
||||
*/
|
||||
static int __init bpq_init_driver(void)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
if (!proc_create("bpqether", S_IRUGO, init_net.proc_net,
|
||||
&bpq_info_fops)) {
|
||||
printk(KERN_ERR
|
||||
"bpq: cannot create /proc/net/bpqether entry.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
dev_add_pack(&bpq_packet_type);
|
||||
|
||||
register_netdevice_notifier(&bpq_dev_notifier);
|
||||
|
||||
printk(banner);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit bpq_cleanup_driver(void)
|
||||
{
|
||||
struct bpqdev *bpq;
|
||||
|
||||
dev_remove_pack(&bpq_packet_type);
|
||||
|
||||
unregister_netdevice_notifier(&bpq_dev_notifier);
|
||||
|
||||
remove_proc_entry("bpqether", init_net.proc_net);
|
||||
|
||||
rtnl_lock();
|
||||
while (!list_empty(&bpq_devices)) {
|
||||
bpq = list_entry(bpq_devices.next, struct bpqdev, bpq_list);
|
||||
bpq_free_device(bpq->axdev);
|
||||
}
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Joerg Reuter DL1BKE <jreuter@yaina.de>");
|
||||
MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet");
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(bpq_init_driver);
|
||||
module_exit(bpq_cleanup_driver);
|
1455
drivers/net/hamradio/dmascc.c
Normal file
1455
drivers/net/hamradio/dmascc.c
Normal file
File diff suppressed because it is too large
Load diff
773
drivers/net/hamradio/hdlcdrv.c
Normal file
773
drivers/net/hamradio/hdlcdrv.c
Normal file
|
@ -0,0 +1,773 @@
|
|||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* hdlcdrv.c -- HDLC packet radio network driver.
|
||||
*
|
||||
* Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Please note that the GPL allows you to use the driver, NOT the radio.
|
||||
* In order to use the radio, you need a license from the communications
|
||||
* authority of your country.
|
||||
*
|
||||
* The driver was derived from Donald Beckers skeleton.c
|
||||
* Written 1993-94 by Donald Becker.
|
||||
*
|
||||
* History:
|
||||
* 0.1 21.09.1996 Started
|
||||
* 18.10.1996 Changed to new user space access routines
|
||||
* (copy_{to,from}_user)
|
||||
* 0.2 21.11.1996 various small changes
|
||||
* 0.3 03.03.1997 fixed (hopefully) IP not working with ax.25 as a module
|
||||
* 0.4 16.04.1997 init code/data tagged
|
||||
* 0.5 30.07.1997 made HDLC buffers bigger (solves a problem with the
|
||||
* soundmodem driver)
|
||||
* 0.6 05.04.1998 add spinlocks
|
||||
* 0.7 03.08.1999 removed some old compatibility cruft
|
||||
* 0.8 12.02.2000 adapted to softnet driver interface
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#include <linux/capability.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/hdlcdrv.h>
|
||||
#include <linux/random.h>
|
||||
#include <net/ax25.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include <linux/crc-ccitt.h>
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#define KISS_VERBOSE
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#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
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/*
|
||||
* the CRC routines are stolen from WAMPES
|
||||
* by Dieter Deyke
|
||||
*/
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
static inline void append_crc_ccitt(unsigned char *buffer, int len)
|
||||
{
|
||||
unsigned int crc = crc_ccitt(0xffff, buffer, len) ^ 0xffff;
|
||||
buffer += len;
|
||||
*buffer++ = crc;
|
||||
*buffer++ = crc >> 8;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
static inline int check_crc_ccitt(const unsigned char *buf, int cnt)
|
||||
{
|
||||
return (crc_ccitt(0xffff, buf, cnt) & 0xffff) == 0xf0b8;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
#if 0
|
||||
static int calc_crc_ccitt(const unsigned char *buf, int cnt)
|
||||
{
|
||||
unsigned int crc = 0xffff;
|
||||
|
||||
for (; cnt > 0; cnt--)
|
||||
crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
|
||||
crc ^= 0xffff;
|
||||
return crc & 0xffff;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
#define tenms_to_2flags(s,tenms) ((tenms * s->par.bitrate) / 100 / 16)
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/*
|
||||
* The HDLC routines
|
||||
*/
|
||||
|
||||
static int hdlc_rx_add_bytes(struct hdlcdrv_state *s, unsigned int bits,
|
||||
int num)
|
||||
{
|
||||
int added = 0;
|
||||
|
||||
while (s->hdlcrx.rx_state && num >= 8) {
|
||||
if (s->hdlcrx.len >= sizeof(s->hdlcrx.buffer)) {
|
||||
s->hdlcrx.rx_state = 0;
|
||||
return 0;
|
||||
}
|
||||
*s->hdlcrx.bp++ = bits >> (32-num);
|
||||
s->hdlcrx.len++;
|
||||
num -= 8;
|
||||
added += 8;
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
static void hdlc_rx_flag(struct net_device *dev, struct hdlcdrv_state *s)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int pkt_len;
|
||||
unsigned char *cp;
|
||||
|
||||
if (s->hdlcrx.len < 4)
|
||||
return;
|
||||
if (!check_crc_ccitt(s->hdlcrx.buffer, s->hdlcrx.len))
|
||||
return;
|
||||
pkt_len = s->hdlcrx.len - 2 + 1; /* KISS kludge */
|
||||
if (!(skb = dev_alloc_skb(pkt_len))) {
|
||||
printk("%s: memory squeeze, dropping packet\n", dev->name);
|
||||
dev->stats.rx_dropped++;
|
||||
return;
|
||||
}
|
||||
cp = skb_put(skb, pkt_len);
|
||||
*cp++ = 0; /* KISS kludge */
|
||||
memcpy(cp, s->hdlcrx.buffer, pkt_len - 1);
|
||||
skb->protocol = ax25_type_trans(skb, dev);
|
||||
netif_rx(skb);
|
||||
dev->stats.rx_packets++;
|
||||
}
|
||||
|
||||
void hdlcdrv_receiver(struct net_device *dev, struct hdlcdrv_state *s)
|
||||
{
|
||||
int i;
|
||||
unsigned int mask1, mask2, mask3, mask4, mask5, mask6, word;
|
||||
|
||||
if (!s || s->magic != HDLCDRV_MAGIC)
|
||||
return;
|
||||
if (test_and_set_bit(0, &s->hdlcrx.in_hdlc_rx))
|
||||
return;
|
||||
|
||||
while (!hdlcdrv_hbuf_empty(&s->hdlcrx.hbuf)) {
|
||||
word = hdlcdrv_hbuf_get(&s->hdlcrx.hbuf);
|
||||
|
||||
#ifdef HDLCDRV_DEBUG
|
||||
hdlcdrv_add_bitbuffer_word(&s->bitbuf_hdlc, word);
|
||||
#endif /* HDLCDRV_DEBUG */
|
||||
s->hdlcrx.bitstream >>= 16;
|
||||
s->hdlcrx.bitstream |= word << 16;
|
||||
s->hdlcrx.bitbuf >>= 16;
|
||||
s->hdlcrx.bitbuf |= word << 16;
|
||||
s->hdlcrx.numbits += 16;
|
||||
for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00,
|
||||
mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff;
|
||||
i >= 0;
|
||||
i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1,
|
||||
mask5 <<= 1, mask6 = (mask6 << 1) | 1) {
|
||||
if ((s->hdlcrx.bitstream & mask1) == mask1)
|
||||
s->hdlcrx.rx_state = 0; /* abort received */
|
||||
else if ((s->hdlcrx.bitstream & mask2) == mask3) {
|
||||
/* flag received */
|
||||
if (s->hdlcrx.rx_state) {
|
||||
hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf
|
||||
<< (8+i),
|
||||
s->hdlcrx.numbits
|
||||
-8-i);
|
||||
hdlc_rx_flag(dev, s);
|
||||
}
|
||||
s->hdlcrx.len = 0;
|
||||
s->hdlcrx.bp = s->hdlcrx.buffer;
|
||||
s->hdlcrx.rx_state = 1;
|
||||
s->hdlcrx.numbits = i;
|
||||
} else if ((s->hdlcrx.bitstream & mask4) == mask5) {
|
||||
/* stuffed bit */
|
||||
s->hdlcrx.numbits--;
|
||||
s->hdlcrx.bitbuf = (s->hdlcrx.bitbuf & (~mask6)) |
|
||||
((s->hdlcrx.bitbuf & mask6) << 1);
|
||||
}
|
||||
}
|
||||
s->hdlcrx.numbits -= hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf,
|
||||
s->hdlcrx.numbits);
|
||||
}
|
||||
clear_bit(0, &s->hdlcrx.in_hdlc_rx);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static inline void do_kiss_params(struct hdlcdrv_state *s,
|
||||
unsigned char *data, unsigned long len)
|
||||
{
|
||||
|
||||
#ifdef KISS_VERBOSE
|
||||
#define PKP(a,b) printk(KERN_INFO "hdlcdrv.c: channel params: " a "\n", b)
|
||||
#else /* KISS_VERBOSE */
|
||||
#define PKP(a,b)
|
||||
#endif /* KISS_VERBOSE */
|
||||
|
||||
if (len < 2)
|
||||
return;
|
||||
switch(data[0]) {
|
||||
case PARAM_TXDELAY:
|
||||
s->ch_params.tx_delay = data[1];
|
||||
PKP("TX delay = %ums", 10 * s->ch_params.tx_delay);
|
||||
break;
|
||||
case PARAM_PERSIST:
|
||||
s->ch_params.ppersist = data[1];
|
||||
PKP("p persistence = %u", s->ch_params.ppersist);
|
||||
break;
|
||||
case PARAM_SLOTTIME:
|
||||
s->ch_params.slottime = data[1];
|
||||
PKP("slot time = %ums", s->ch_params.slottime);
|
||||
break;
|
||||
case PARAM_TXTAIL:
|
||||
s->ch_params.tx_tail = data[1];
|
||||
PKP("TX tail = %ums", s->ch_params.tx_tail);
|
||||
break;
|
||||
case PARAM_FULLDUP:
|
||||
s->ch_params.fulldup = !!data[1];
|
||||
PKP("%s duplex", s->ch_params.fulldup ? "full" : "half");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#undef PKP
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
void hdlcdrv_transmitter(struct net_device *dev, struct hdlcdrv_state *s)
|
||||
{
|
||||
unsigned int mask1, mask2, mask3;
|
||||
int i;
|
||||
struct sk_buff *skb;
|
||||
int pkt_len;
|
||||
|
||||
if (!s || s->magic != HDLCDRV_MAGIC)
|
||||
return;
|
||||
if (test_and_set_bit(0, &s->hdlctx.in_hdlc_tx))
|
||||
return;
|
||||
for (;;) {
|
||||
if (s->hdlctx.numbits >= 16) {
|
||||
if (hdlcdrv_hbuf_full(&s->hdlctx.hbuf)) {
|
||||
clear_bit(0, &s->hdlctx.in_hdlc_tx);
|
||||
return;
|
||||
}
|
||||
hdlcdrv_hbuf_put(&s->hdlctx.hbuf, s->hdlctx.bitbuf);
|
||||
s->hdlctx.bitbuf >>= 16;
|
||||
s->hdlctx.numbits -= 16;
|
||||
}
|
||||
switch (s->hdlctx.tx_state) {
|
||||
default:
|
||||
clear_bit(0, &s->hdlctx.in_hdlc_tx);
|
||||
return;
|
||||
case 0:
|
||||
case 1:
|
||||
if (s->hdlctx.numflags) {
|
||||
s->hdlctx.numflags--;
|
||||
s->hdlctx.bitbuf |=
|
||||
0x7e7e << s->hdlctx.numbits;
|
||||
s->hdlctx.numbits += 16;
|
||||
break;
|
||||
}
|
||||
if (s->hdlctx.tx_state == 1) {
|
||||
clear_bit(0, &s->hdlctx.in_hdlc_tx);
|
||||
return;
|
||||
}
|
||||
if (!(skb = s->skb)) {
|
||||
int flgs = tenms_to_2flags(s, s->ch_params.tx_tail);
|
||||
if (flgs < 2)
|
||||
flgs = 2;
|
||||
s->hdlctx.tx_state = 1;
|
||||
s->hdlctx.numflags = flgs;
|
||||
break;
|
||||
}
|
||||
s->skb = NULL;
|
||||
netif_wake_queue(dev);
|
||||
pkt_len = skb->len-1; /* strip KISS byte */
|
||||
if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) {
|
||||
s->hdlctx.tx_state = 0;
|
||||
s->hdlctx.numflags = 1;
|
||||
dev_kfree_skb_irq(skb);
|
||||
break;
|
||||
}
|
||||
skb_copy_from_linear_data_offset(skb, 1,
|
||||
s->hdlctx.buffer,
|
||||
pkt_len);
|
||||
dev_kfree_skb_irq(skb);
|
||||
s->hdlctx.bp = s->hdlctx.buffer;
|
||||
append_crc_ccitt(s->hdlctx.buffer, pkt_len);
|
||||
s->hdlctx.len = pkt_len+2; /* the appended CRC */
|
||||
s->hdlctx.tx_state = 2;
|
||||
s->hdlctx.bitstream = 0;
|
||||
dev->stats.tx_packets++;
|
||||
break;
|
||||
case 2:
|
||||
if (!s->hdlctx.len) {
|
||||
s->hdlctx.tx_state = 0;
|
||||
s->hdlctx.numflags = 1;
|
||||
break;
|
||||
}
|
||||
s->hdlctx.len--;
|
||||
s->hdlctx.bitbuf |= *s->hdlctx.bp <<
|
||||
s->hdlctx.numbits;
|
||||
s->hdlctx.bitstream >>= 8;
|
||||
s->hdlctx.bitstream |= (*s->hdlctx.bp++) << 16;
|
||||
mask1 = 0x1f000;
|
||||
mask2 = 0x10000;
|
||||
mask3 = 0xffffffff >> (31-s->hdlctx.numbits);
|
||||
s->hdlctx.numbits += 8;
|
||||
for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1,
|
||||
mask3 = (mask3 << 1) | 1) {
|
||||
if ((s->hdlctx.bitstream & mask1) != mask1)
|
||||
continue;
|
||||
s->hdlctx.bitstream &= ~mask2;
|
||||
s->hdlctx.bitbuf =
|
||||
(s->hdlctx.bitbuf & mask3) |
|
||||
((s->hdlctx.bitbuf &
|
||||
(~mask3)) << 1);
|
||||
s->hdlctx.numbits++;
|
||||
mask3 = (mask3 << 1) | 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static void start_tx(struct net_device *dev, struct hdlcdrv_state *s)
|
||||
{
|
||||
s->hdlctx.tx_state = 0;
|
||||
s->hdlctx.numflags = tenms_to_2flags(s, s->ch_params.tx_delay);
|
||||
s->hdlctx.bitbuf = s->hdlctx.bitstream = s->hdlctx.numbits = 0;
|
||||
hdlcdrv_transmitter(dev, s);
|
||||
s->hdlctx.ptt = 1;
|
||||
s->ptt_keyed++;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
void hdlcdrv_arbitrate(struct net_device *dev, struct hdlcdrv_state *s)
|
||||
{
|
||||
if (!s || s->magic != HDLCDRV_MAGIC || s->hdlctx.ptt || !s->skb)
|
||||
return;
|
||||
if (s->ch_params.fulldup) {
|
||||
start_tx(dev, s);
|
||||
return;
|
||||
}
|
||||
if (s->hdlcrx.dcd) {
|
||||
s->hdlctx.slotcnt = s->ch_params.slottime;
|
||||
return;
|
||||
}
|
||||
if ((--s->hdlctx.slotcnt) > 0)
|
||||
return;
|
||||
s->hdlctx.slotcnt = s->ch_params.slottime;
|
||||
if ((prandom_u32() % 256) > s->ch_params.ppersist)
|
||||
return;
|
||||
start_tx(dev, s);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/*
|
||||
* ===================== network driver interface =========================
|
||||
*/
|
||||
|
||||
static netdev_tx_t hdlcdrv_send_packet(struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct hdlcdrv_state *sm = netdev_priv(dev);
|
||||
|
||||
if (skb->data[0] != 0) {
|
||||
do_kiss_params(sm, skb->data, skb->len);
|
||||
dev_kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
if (sm->skb)
|
||||
return NETDEV_TX_LOCKED;
|
||||
netif_stop_queue(dev);
|
||||
sm->skb = skb;
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int hdlcdrv_set_mac_address(struct net_device *dev, void *addr)
|
||||
{
|
||||
struct sockaddr *sa = (struct sockaddr *)addr;
|
||||
|
||||
/* addr is an AX.25 shifted ASCII mac address */
|
||||
memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/*
|
||||
* Open/initialize the board. This is called (in the current kernel)
|
||||
* sometime after booting when the 'ifconfig' program is run.
|
||||
*
|
||||
* This routine should set everything up anew at each open, even
|
||||
* registers that "should" only need to be set once at boot, so that
|
||||
* there is non-reboot way to recover if something goes wrong.
|
||||
*/
|
||||
|
||||
static int hdlcdrv_open(struct net_device *dev)
|
||||
{
|
||||
struct hdlcdrv_state *s = netdev_priv(dev);
|
||||
int i;
|
||||
|
||||
if (!s->ops || !s->ops->open)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* initialise some variables
|
||||
*/
|
||||
s->opened = 1;
|
||||
s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
|
||||
s->hdlcrx.in_hdlc_rx = 0;
|
||||
s->hdlcrx.rx_state = 0;
|
||||
|
||||
s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
|
||||
s->hdlctx.in_hdlc_tx = 0;
|
||||
s->hdlctx.tx_state = 1;
|
||||
s->hdlctx.numflags = 0;
|
||||
s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
|
||||
s->hdlctx.ptt = 0;
|
||||
s->hdlctx.slotcnt = s->ch_params.slottime;
|
||||
s->hdlctx.calibrate = 0;
|
||||
|
||||
i = s->ops->open(dev);
|
||||
if (i)
|
||||
return i;
|
||||
netif_start_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/*
|
||||
* The inverse routine to hdlcdrv_open().
|
||||
*/
|
||||
|
||||
static int hdlcdrv_close(struct net_device *dev)
|
||||
{
|
||||
struct hdlcdrv_state *s = netdev_priv(dev);
|
||||
int i = 0;
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
if (s->ops && s->ops->close)
|
||||
i = s->ops->close(dev);
|
||||
if (s->skb)
|
||||
dev_kfree_skb(s->skb);
|
||||
s->skb = NULL;
|
||||
s->opened = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
struct hdlcdrv_state *s = netdev_priv(dev);
|
||||
struct hdlcdrv_ioctl bi;
|
||||
|
||||
if (cmd != SIOCDEVPRIVATE) {
|
||||
if (s->ops && s->ops->ioctl)
|
||||
return s->ops->ioctl(dev, ifr, &bi, cmd);
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
|
||||
return -EFAULT;
|
||||
|
||||
switch (bi.cmd) {
|
||||
default:
|
||||
if (s->ops && s->ops->ioctl)
|
||||
return s->ops->ioctl(dev, ifr, &bi, cmd);
|
||||
return -ENOIOCTLCMD;
|
||||
|
||||
case HDLCDRVCTL_GETCHANNELPAR:
|
||||
bi.data.cp.tx_delay = s->ch_params.tx_delay;
|
||||
bi.data.cp.tx_tail = s->ch_params.tx_tail;
|
||||
bi.data.cp.slottime = s->ch_params.slottime;
|
||||
bi.data.cp.ppersist = s->ch_params.ppersist;
|
||||
bi.data.cp.fulldup = s->ch_params.fulldup;
|
||||
break;
|
||||
|
||||
case HDLCDRVCTL_SETCHANNELPAR:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EACCES;
|
||||
s->ch_params.tx_delay = bi.data.cp.tx_delay;
|
||||
s->ch_params.tx_tail = bi.data.cp.tx_tail;
|
||||
s->ch_params.slottime = bi.data.cp.slottime;
|
||||
s->ch_params.ppersist = bi.data.cp.ppersist;
|
||||
s->ch_params.fulldup = bi.data.cp.fulldup;
|
||||
s->hdlctx.slotcnt = 1;
|
||||
return 0;
|
||||
|
||||
case HDLCDRVCTL_GETMODEMPAR:
|
||||
bi.data.mp.iobase = dev->base_addr;
|
||||
bi.data.mp.irq = dev->irq;
|
||||
bi.data.mp.dma = dev->dma;
|
||||
bi.data.mp.dma2 = s->ptt_out.dma2;
|
||||
bi.data.mp.seriobase = s->ptt_out.seriobase;
|
||||
bi.data.mp.pariobase = s->ptt_out.pariobase;
|
||||
bi.data.mp.midiiobase = s->ptt_out.midiiobase;
|
||||
break;
|
||||
|
||||
case HDLCDRVCTL_SETMODEMPAR:
|
||||
if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev))
|
||||
return -EACCES;
|
||||
dev->base_addr = bi.data.mp.iobase;
|
||||
dev->irq = bi.data.mp.irq;
|
||||
dev->dma = bi.data.mp.dma;
|
||||
s->ptt_out.dma2 = bi.data.mp.dma2;
|
||||
s->ptt_out.seriobase = bi.data.mp.seriobase;
|
||||
s->ptt_out.pariobase = bi.data.mp.pariobase;
|
||||
s->ptt_out.midiiobase = bi.data.mp.midiiobase;
|
||||
return 0;
|
||||
|
||||
case HDLCDRVCTL_GETSTAT:
|
||||
bi.data.cs.ptt = hdlcdrv_ptt(s);
|
||||
bi.data.cs.dcd = s->hdlcrx.dcd;
|
||||
bi.data.cs.ptt_keyed = s->ptt_keyed;
|
||||
bi.data.cs.tx_packets = dev->stats.tx_packets;
|
||||
bi.data.cs.tx_errors = dev->stats.tx_errors;
|
||||
bi.data.cs.rx_packets = dev->stats.rx_packets;
|
||||
bi.data.cs.rx_errors = dev->stats.rx_errors;
|
||||
break;
|
||||
|
||||
case HDLCDRVCTL_OLDGETSTAT:
|
||||
bi.data.ocs.ptt = hdlcdrv_ptt(s);
|
||||
bi.data.ocs.dcd = s->hdlcrx.dcd;
|
||||
bi.data.ocs.ptt_keyed = s->ptt_keyed;
|
||||
break;
|
||||
|
||||
case HDLCDRVCTL_CALIBRATE:
|
||||
if(!capable(CAP_SYS_RAWIO))
|
||||
return -EPERM;
|
||||
if (bi.data.calibrate > INT_MAX / s->par.bitrate)
|
||||
return -EINVAL;
|
||||
s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16;
|
||||
return 0;
|
||||
|
||||
case HDLCDRVCTL_GETSAMPLES:
|
||||
#ifndef HDLCDRV_DEBUG
|
||||
return -EPERM;
|
||||
#else /* HDLCDRV_DEBUG */
|
||||
if (s->bitbuf_channel.rd == s->bitbuf_channel.wr)
|
||||
return -EAGAIN;
|
||||
bi.data.bits =
|
||||
s->bitbuf_channel.buffer[s->bitbuf_channel.rd];
|
||||
s->bitbuf_channel.rd = (s->bitbuf_channel.rd+1) %
|
||||
sizeof(s->bitbuf_channel.buffer);
|
||||
break;
|
||||
#endif /* HDLCDRV_DEBUG */
|
||||
|
||||
case HDLCDRVCTL_GETBITS:
|
||||
#ifndef HDLCDRV_DEBUG
|
||||
return -EPERM;
|
||||
#else /* HDLCDRV_DEBUG */
|
||||
if (s->bitbuf_hdlc.rd == s->bitbuf_hdlc.wr)
|
||||
return -EAGAIN;
|
||||
bi.data.bits =
|
||||
s->bitbuf_hdlc.buffer[s->bitbuf_hdlc.rd];
|
||||
s->bitbuf_hdlc.rd = (s->bitbuf_hdlc.rd+1) %
|
||||
sizeof(s->bitbuf_hdlc.buffer);
|
||||
break;
|
||||
#endif /* HDLCDRV_DEBUG */
|
||||
|
||||
case HDLCDRVCTL_DRIVERNAME:
|
||||
if (s->ops && s->ops->drvname) {
|
||||
strncpy(bi.data.drivername, s->ops->drvname,
|
||||
sizeof(bi.data.drivername));
|
||||
break;
|
||||
}
|
||||
bi.data.drivername[0] = '\0';
|
||||
break;
|
||||
|
||||
}
|
||||
if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static const struct net_device_ops hdlcdrv_netdev = {
|
||||
.ndo_open = hdlcdrv_open,
|
||||
.ndo_stop = hdlcdrv_close,
|
||||
.ndo_start_xmit = hdlcdrv_send_packet,
|
||||
.ndo_do_ioctl = hdlcdrv_ioctl,
|
||||
.ndo_set_mac_address = hdlcdrv_set_mac_address,
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize fields in hdlcdrv
|
||||
*/
|
||||
static void hdlcdrv_setup(struct net_device *dev)
|
||||
{
|
||||
static const struct hdlcdrv_channel_params dflt_ch_params = {
|
||||
20, 2, 10, 40, 0
|
||||
};
|
||||
struct hdlcdrv_state *s = netdev_priv(dev);
|
||||
|
||||
/*
|
||||
* initialize the hdlcdrv_state struct
|
||||
*/
|
||||
s->ch_params = dflt_ch_params;
|
||||
s->ptt_keyed = 0;
|
||||
|
||||
spin_lock_init(&s->hdlcrx.hbuf.lock);
|
||||
s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
|
||||
s->hdlcrx.in_hdlc_rx = 0;
|
||||
s->hdlcrx.rx_state = 0;
|
||||
|
||||
spin_lock_init(&s->hdlctx.hbuf.lock);
|
||||
s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
|
||||
s->hdlctx.in_hdlc_tx = 0;
|
||||
s->hdlctx.tx_state = 1;
|
||||
s->hdlctx.numflags = 0;
|
||||
s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
|
||||
s->hdlctx.ptt = 0;
|
||||
s->hdlctx.slotcnt = s->ch_params.slottime;
|
||||
s->hdlctx.calibrate = 0;
|
||||
|
||||
#ifdef HDLCDRV_DEBUG
|
||||
s->bitbuf_channel.rd = s->bitbuf_channel.wr = 0;
|
||||
s->bitbuf_channel.shreg = 0x80;
|
||||
|
||||
s->bitbuf_hdlc.rd = s->bitbuf_hdlc.wr = 0;
|
||||
s->bitbuf_hdlc.shreg = 0x80;
|
||||
#endif /* HDLCDRV_DEBUG */
|
||||
|
||||
|
||||
/* Fill in the fields of the device structure */
|
||||
|
||||
s->skb = NULL;
|
||||
|
||||
dev->netdev_ops = &hdlcdrv_netdev;
|
||||
dev->header_ops = &ax25_header_ops;
|
||||
|
||||
dev->type = ARPHRD_AX25; /* AF_AX25 device */
|
||||
dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
|
||||
dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */
|
||||
dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */
|
||||
memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN);
|
||||
memcpy(dev->dev_addr, &ax25_defaddr, AX25_ADDR_LEN);
|
||||
dev->tx_queue_len = 16;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
struct net_device *hdlcdrv_register(const struct hdlcdrv_ops *ops,
|
||||
unsigned int privsize, const char *ifname,
|
||||
unsigned int baseaddr, unsigned int irq,
|
||||
unsigned int dma)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct hdlcdrv_state *s;
|
||||
int err;
|
||||
|
||||
BUG_ON(ops == NULL);
|
||||
|
||||
if (privsize < sizeof(struct hdlcdrv_state))
|
||||
privsize = sizeof(struct hdlcdrv_state);
|
||||
|
||||
dev = alloc_netdev(privsize, ifname, NET_NAME_UNKNOWN, hdlcdrv_setup);
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/*
|
||||
* initialize part of the hdlcdrv_state struct
|
||||
*/
|
||||
s = netdev_priv(dev);
|
||||
s->magic = HDLCDRV_MAGIC;
|
||||
s->ops = ops;
|
||||
dev->base_addr = baseaddr;
|
||||
dev->irq = irq;
|
||||
dev->dma = dma;
|
||||
|
||||
err = register_netdev(dev);
|
||||
if (err < 0) {
|
||||
printk(KERN_WARNING "hdlcdrv: cannot register net "
|
||||
"device %s\n", dev->name);
|
||||
free_netdev(dev);
|
||||
dev = ERR_PTR(err);
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
void hdlcdrv_unregister(struct net_device *dev)
|
||||
{
|
||||
struct hdlcdrv_state *s = netdev_priv(dev);
|
||||
|
||||
BUG_ON(s->magic != HDLCDRV_MAGIC);
|
||||
|
||||
if (s->opened && s->ops->close)
|
||||
s->ops->close(dev);
|
||||
unregister_netdev(dev);
|
||||
|
||||
free_netdev(dev);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
EXPORT_SYMBOL(hdlcdrv_receiver);
|
||||
EXPORT_SYMBOL(hdlcdrv_transmitter);
|
||||
EXPORT_SYMBOL(hdlcdrv_arbitrate);
|
||||
EXPORT_SYMBOL(hdlcdrv_register);
|
||||
EXPORT_SYMBOL(hdlcdrv_unregister);
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int __init hdlcdrv_init_driver(void)
|
||||
{
|
||||
printk(KERN_INFO "hdlcdrv: (C) 1996-2000 Thomas Sailer HB9JNX/AE4WA\n");
|
||||
printk(KERN_INFO "hdlcdrv: version 0.8\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static void __exit hdlcdrv_cleanup_driver(void)
|
||||
{
|
||||
printk(KERN_INFO "hdlcdrv: cleanup\n");
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
|
||||
MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder");
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(hdlcdrv_init_driver);
|
||||
module_exit(hdlcdrv_cleanup_driver);
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
1037
drivers/net/hamradio/mkiss.c
Normal file
1037
drivers/net/hamradio/mkiss.c
Normal file
File diff suppressed because it is too large
Load diff
2184
drivers/net/hamradio/scc.c
Normal file
2184
drivers/net/hamradio/scc.c
Normal file
File diff suppressed because it is too large
Load diff
1217
drivers/net/hamradio/yam.c
Normal file
1217
drivers/net/hamradio/yam.c
Normal file
File diff suppressed because it is too large
Load diff
245
drivers/net/hamradio/z8530.h
Normal file
245
drivers/net/hamradio/z8530.h
Normal file
|
@ -0,0 +1,245 @@
|
|||
|
||||
/* 8530 Serial Communications Controller Register definitions */
|
||||
#define FLAG 0x7e
|
||||
|
||||
/* Write Register 0 */
|
||||
#define R0 0 /* Register selects */
|
||||
#define R1 1
|
||||
#define R2 2
|
||||
#define R3 3
|
||||
#define R4 4
|
||||
#define R5 5
|
||||
#define R6 6
|
||||
#define R7 7
|
||||
#define R8 8
|
||||
#define R9 9
|
||||
#define R10 10
|
||||
#define R11 11
|
||||
#define R12 12
|
||||
#define R13 13
|
||||
#define R14 14
|
||||
#define R15 15
|
||||
|
||||
#define NULLCODE 0 /* Null Code */
|
||||
#define POINT_HIGH 0x8 /* Select upper half of registers */
|
||||
#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
|
||||
#define SEND_ABORT 0x18 /* HDLC Abort */
|
||||
#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
|
||||
#define RES_Tx_P 0x28 /* Reset TxINT Pending */
|
||||
#define ERR_RES 0x30 /* Error Reset */
|
||||
#define RES_H_IUS 0x38 /* Reset highest IUS */
|
||||
|
||||
#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
|
||||
#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
|
||||
#define RES_EOM_L 0xC0 /* Reset EOM latch */
|
||||
|
||||
/* Write Register 1 */
|
||||
|
||||
#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
|
||||
#define TxINT_ENAB 0x2 /* Tx Int Enable */
|
||||
#define PAR_SPEC 0x4 /* Parity is special condition */
|
||||
|
||||
#define RxINT_DISAB 0 /* Rx Int Disable */
|
||||
#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
|
||||
#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
|
||||
#define INT_ERR_Rx 0x18 /* Int on error only */
|
||||
|
||||
#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
|
||||
#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
|
||||
#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
|
||||
|
||||
/* Write Register #2 (Interrupt Vector) */
|
||||
|
||||
/* Write Register 3 */
|
||||
|
||||
#define RxENABLE 0x1 /* Rx Enable */
|
||||
#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
|
||||
#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
|
||||
#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
|
||||
#define ENT_HM 0x10 /* Enter Hunt Mode */
|
||||
#define AUTO_ENAB 0x20 /* Auto Enables */
|
||||
#define Rx5 0x0 /* Rx 5 Bits/Character */
|
||||
#define Rx7 0x40 /* Rx 7 Bits/Character */
|
||||
#define Rx6 0x80 /* Rx 6 Bits/Character */
|
||||
#define Rx8 0xc0 /* Rx 8 Bits/Character */
|
||||
|
||||
/* Write Register 4 */
|
||||
|
||||
#define PAR_ENA 0x1 /* Parity Enable */
|
||||
#define PAR_EVEN 0x2 /* Parity Even/Odd* */
|
||||
|
||||
#define SYNC_ENAB 0 /* Sync Modes Enable */
|
||||
#define SB1 0x4 /* 1 stop bit/char */
|
||||
#define SB15 0x8 /* 1.5 stop bits/char */
|
||||
#define SB2 0xc /* 2 stop bits/char */
|
||||
|
||||
#define MONSYNC 0 /* 8 Bit Sync character */
|
||||
#define BISYNC 0x10 /* 16 bit sync character */
|
||||
#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
|
||||
#define EXTSYNC 0x30 /* External Sync Mode */
|
||||
|
||||
#define X1CLK 0x0 /* x1 clock mode */
|
||||
#define X16CLK 0x40 /* x16 clock mode */
|
||||
#define X32CLK 0x80 /* x32 clock mode */
|
||||
#define X64CLK 0xC0 /* x64 clock mode */
|
||||
|
||||
/* Write Register 5 */
|
||||
|
||||
#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
|
||||
#define RTS 0x2 /* RTS */
|
||||
#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
|
||||
#define TxENAB 0x8 /* Tx Enable */
|
||||
#define SND_BRK 0x10 /* Send Break */
|
||||
#define Tx5 0x0 /* Tx 5 bits (or less)/character */
|
||||
#define Tx7 0x20 /* Tx 7 bits/character */
|
||||
#define Tx6 0x40 /* Tx 6 bits/character */
|
||||
#define Tx8 0x60 /* Tx 8 bits/character */
|
||||
#define DTR 0x80 /* DTR */
|
||||
|
||||
/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
|
||||
|
||||
/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
|
||||
|
||||
/* Write Register 8 (transmit buffer) */
|
||||
|
||||
/* Write Register 9 (Master interrupt control) */
|
||||
#define VIS 1 /* Vector Includes Status */
|
||||
#define NV 2 /* No Vector */
|
||||
#define DLC 4 /* Disable Lower Chain */
|
||||
#define MIE 8 /* Master Interrupt Enable */
|
||||
#define STATHI 0x10 /* Status high */
|
||||
#define NORESET 0 /* No reset on write to R9 */
|
||||
#define CHRB 0x40 /* Reset channel B */
|
||||
#define CHRA 0x80 /* Reset channel A */
|
||||
#define FHWRES 0xc0 /* Force hardware reset */
|
||||
|
||||
/* Write Register 10 (misc control bits) */
|
||||
#define BIT6 1 /* 6 bit/8bit sync */
|
||||
#define LOOPMODE 2 /* SDLC Loop mode */
|
||||
#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
|
||||
#define MARKIDLE 8 /* Mark/flag on idle */
|
||||
#define GAOP 0x10 /* Go active on poll */
|
||||
#define NRZ 0 /* NRZ mode */
|
||||
#define NRZI 0x20 /* NRZI mode */
|
||||
#define FM1 0x40 /* FM1 (transition = 1) */
|
||||
#define FM0 0x60 /* FM0 (transition = 0) */
|
||||
#define CRCPS 0x80 /* CRC Preset I/O */
|
||||
|
||||
/* Write Register 11 (Clock Mode control) */
|
||||
#define TRxCXT 0 /* TRxC = Xtal output */
|
||||
#define TRxCTC 1 /* TRxC = Transmit clock */
|
||||
#define TRxCBR 2 /* TRxC = BR Generator Output */
|
||||
#define TRxCDP 3 /* TRxC = DPLL output */
|
||||
#define TRxCOI 4 /* TRxC O/I */
|
||||
#define TCRTxCP 0 /* Transmit clock = RTxC pin */
|
||||
#define TCTRxCP 8 /* Transmit clock = TRxC pin */
|
||||
#define TCBR 0x10 /* Transmit clock = BR Generator output */
|
||||
#define TCDPLL 0x18 /* Transmit clock = DPLL output */
|
||||
#define RCRTxCP 0 /* Receive clock = RTxC pin */
|
||||
#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
|
||||
#define RCBR 0x40 /* Receive clock = BR Generator output */
|
||||
#define RCDPLL 0x60 /* Receive clock = DPLL output */
|
||||
#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
|
||||
|
||||
/* Write Register 12 (lower byte of baud rate generator time constant) */
|
||||
|
||||
/* Write Register 13 (upper byte of baud rate generator time constant) */
|
||||
|
||||
/* Write Register 14 (Misc control bits) */
|
||||
#define BRENABL 1 /* Baud rate generator enable */
|
||||
#define BRSRC 2 /* Baud rate generator source */
|
||||
#define DTRREQ 4 /* DTR/Request function */
|
||||
#define AUTOECHO 8 /* Auto Echo */
|
||||
#define LOOPBAK 0x10 /* Local loopback */
|
||||
#define SEARCH 0x20 /* Enter search mode */
|
||||
#define RMC 0x40 /* Reset missing clock */
|
||||
#define DISDPLL 0x60 /* Disable DPLL */
|
||||
#define SSBR 0x80 /* Set DPLL source = BR generator */
|
||||
#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
|
||||
#define SFMM 0xc0 /* Set FM mode */
|
||||
#define SNRZI 0xe0 /* Set NRZI mode */
|
||||
|
||||
/* Write Register 15 (external/status interrupt control) */
|
||||
#define ZCIE 2 /* Zero count IE */
|
||||
#define DCDIE 8 /* DCD IE */
|
||||
#define SYNCIE 0x10 /* Sync/hunt IE */
|
||||
#define CTSIE 0x20 /* CTS IE */
|
||||
#define TxUIE 0x40 /* Tx Underrun/EOM IE */
|
||||
#define BRKIE 0x80 /* Break/Abort IE */
|
||||
|
||||
|
||||
/* Read Register 0 */
|
||||
#define Rx_CH_AV 0x1 /* Rx Character Available */
|
||||
#define ZCOUNT 0x2 /* Zero count */
|
||||
#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
|
||||
#define DCD 0x8 /* DCD */
|
||||
#define SYNC_HUNT 0x10 /* Sync/hunt */
|
||||
#define CTS 0x20 /* CTS */
|
||||
#define TxEOM 0x40 /* Tx underrun */
|
||||
#define BRK_ABRT 0x80 /* Break/Abort */
|
||||
|
||||
/* Read Register 1 */
|
||||
#define ALL_SNT 0x1 /* All sent */
|
||||
/* Residue Data for 8 Rx bits/char programmed */
|
||||
#define RES3 0x8 /* 0/3 */
|
||||
#define RES4 0x4 /* 0/4 */
|
||||
#define RES5 0xc /* 0/5 */
|
||||
#define RES6 0x2 /* 0/6 */
|
||||
#define RES7 0xa /* 0/7 */
|
||||
#define RES8 0x6 /* 0/8 */
|
||||
#define RES18 0xe /* 1/8 */
|
||||
#define RES28 0x0 /* 2/8 */
|
||||
/* Special Rx Condition Interrupts */
|
||||
#define PAR_ERR 0x10 /* Parity error */
|
||||
#define Rx_OVR 0x20 /* Rx Overrun Error */
|
||||
#define CRC_ERR 0x40 /* CRC/Framing Error */
|
||||
#define END_FR 0x80 /* End of Frame (SDLC) */
|
||||
|
||||
/* Read Register 2 (channel b only) - Interrupt vector */
|
||||
|
||||
/* Read Register 3 (interrupt pending register) ch a only */
|
||||
#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
|
||||
#define CHBTxIP 0x2 /* Channel B Tx IP */
|
||||
#define CHBRxIP 0x4 /* Channel B Rx IP */
|
||||
#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
|
||||
#define CHATxIP 0x10 /* Channel A Tx IP */
|
||||
#define CHARxIP 0x20 /* Channel A Rx IP */
|
||||
|
||||
/* Read Register 8 (receive data register) */
|
||||
|
||||
/* Read Register 10 (misc status bits) */
|
||||
#define ONLOOP 2 /* On loop */
|
||||
#define LOOPSEND 0x10 /* Loop sending */
|
||||
#define CLK2MIS 0x40 /* Two clocks missing */
|
||||
#define CLK1MIS 0x80 /* One clock missing */
|
||||
|
||||
/* Read Register 12 (lower byte of baud rate generator constant) */
|
||||
|
||||
/* Read Register 13 (upper byte of baud rate generator constant) */
|
||||
|
||||
/* Read Register 15 (value of WR 15) */
|
||||
|
||||
/* Z85C30/Z85230 Enhanced SCC register definitions */
|
||||
|
||||
/* Write Register 7' (SDLC/HDLC Programmable Enhancements) */
|
||||
#define AUTOTXF 0x01 /* Auto Tx Flag */
|
||||
#define AUTOEOM 0x02 /* Auto EOM Latch Reset */
|
||||
#define AUTORTS 0x04 /* Auto RTS */
|
||||
#define TXDNRZI 0x08 /* TxD Pulled High in SDLC NRZI mode */
|
||||
#define RXFIFOH 0x08 /* Z85230: Int on RX FIFO half full */
|
||||
#define FASTDTR 0x10 /* Fast DTR/REQ Mode */
|
||||
#define CRCCBCR 0x20 /* CRC Check Bytes Completely Received */
|
||||
#define TXFIFOE 0x20 /* Z85230: Int on TX FIFO completely empty */
|
||||
#define EXTRDEN 0x40 /* Extended Read Enabled */
|
||||
|
||||
/* Write Register 15 (external/status interrupt control) */
|
||||
#define SHDLCE 1 /* SDLC/HDLC Enhancements Enable */
|
||||
#define FIFOE 4 /* FIFO Enable */
|
||||
|
||||
/* Read Register 6 (frame status FIFO) */
|
||||
#define BCLSB 0xff /* LSB of 14 bits count */
|
||||
|
||||
/* Read Register 7 (frame status FIFO) */
|
||||
#define BCMSB 0x3f /* MSB of 14 bits count */
|
||||
#define FDA 0x40 /* FIFO Data Available Status */
|
||||
#define FOS 0x80 /* FIFO Overflow Status */
|
Loading…
Add table
Add a link
Reference in a new issue