Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
501
drivers/net/wan/Kconfig
Normal file
501
drivers/net/wan/Kconfig
Normal file
@@ -0,0 +1,501 @@
|
||||
#
|
||||
# wan devices configuration
|
||||
#
|
||||
|
||||
menu "Wan interfaces"
|
||||
depends on NETDEVICES
|
||||
|
||||
config WAN
|
||||
bool "Wan interfaces support"
|
||||
---help---
|
||||
Wide Area Networks (WANs), such as X.25, Frame Relay and leased
|
||||
lines, are used to interconnect Local Area Networks (LANs) over vast
|
||||
distances with data transfer rates significantly higher than those
|
||||
achievable with commonly used asynchronous modem connections.
|
||||
|
||||
Usually, a quite expensive external device called a `WAN router' is
|
||||
needed to connect to a WAN. As an alternative, a relatively
|
||||
inexpensive WAN interface card can allow your Linux box to directly
|
||||
connect to a WAN.
|
||||
|
||||
If you have one of those cards and wish to use it under Linux,
|
||||
say Y here and also to the WAN driver for your card.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
# There is no way to detect a comtrol sv11 - force it modular for now.
|
||||
config HOSTESS_SV11
|
||||
tristate "Comtrol Hostess SV-11 support"
|
||||
depends on WAN && ISA && m && ISA_DMA_API && INET
|
||||
help
|
||||
Driver for Comtrol Hostess SV-11 network card which
|
||||
operates on low speed synchronous serial links at up to
|
||||
256Kbps, supporting PPP and Cisco HDLC.
|
||||
|
||||
The driver will be compiled as a module: the
|
||||
module will be called hostess_sv11.
|
||||
|
||||
# The COSA/SRP driver has not been tested as non-modular yet.
|
||||
config COSA
|
||||
tristate "COSA/SRP sync serial boards support"
|
||||
depends on WAN && ISA && m && ISA_DMA_API
|
||||
---help---
|
||||
Driver for COSA and SRP synchronous serial boards.
|
||||
|
||||
These boards allow to connect synchronous serial devices (for example
|
||||
base-band modems, or any other device with the X.21, V.24, V.35 or
|
||||
V.36 interface) to your Linux box. The cards can work as the
|
||||
character device, synchronous PPP network device, or the Cisco HDLC
|
||||
network device.
|
||||
|
||||
You will need user-space utilities COSA or SRP boards for downloading
|
||||
the firmware to the cards and to set them up. Look at the
|
||||
<http://www.fi.muni.cz/~kas/cosa/> for more information. You can also
|
||||
read the comment at the top of the <file:drivers/net/wan/cosa.c> for
|
||||
details about the cards and the driver itself.
|
||||
|
||||
The driver will be compiled as a module: the
|
||||
module will be called cosa.
|
||||
|
||||
#
|
||||
# Lan Media's board. Currently 1000, 1200, 5200, 5245
|
||||
#
|
||||
config LANMEDIA
|
||||
tristate "LanMedia Corp. SSI/V.35, T1/E1, HSSI, T3 boards"
|
||||
depends on WAN && PCI
|
||||
---help---
|
||||
Driver for the following Lan Media family of serial boards:
|
||||
|
||||
- LMC 1000 board allows you to connect synchronous serial devices
|
||||
(for example base-band modems, or any other device with the X.21,
|
||||
V.24, V.35 or V.36 interface) to your Linux box.
|
||||
|
||||
- LMC 1200 with on board DSU board allows you to connect your Linux
|
||||
box directly to a T1 or E1 circuit.
|
||||
|
||||
- LMC 5200 board provides a HSSI interface capable of running up to
|
||||
52 Mbits per second.
|
||||
|
||||
- LMC 5245 board connects directly to a T3 circuit saving the
|
||||
additional external hardware.
|
||||
|
||||
To change setting such as syncPPP vs Cisco HDLC or clock source you
|
||||
will need lmcctl. It is available at <ftp://ftp.lanmedia.com/>
|
||||
(broken link).
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called lmc.
|
||||
|
||||
# There is no way to detect a Sealevel board. Force it modular
|
||||
config SEALEVEL_4021
|
||||
tristate "Sealevel Systems 4021 support"
|
||||
depends on WAN && ISA && m && ISA_DMA_API && INET
|
||||
help
|
||||
This is a driver for the Sealevel Systems ACB 56 serial I/O adapter.
|
||||
|
||||
The driver will be compiled as a module: the
|
||||
module will be called sealevel.
|
||||
|
||||
# Generic HDLC
|
||||
config HDLC
|
||||
tristate "Generic HDLC layer"
|
||||
depends on WAN
|
||||
help
|
||||
Say Y to this option if your Linux box contains a WAN (Wide Area
|
||||
Network) card supported by this driver and you are planning to
|
||||
connect the box to a WAN.
|
||||
|
||||
You will need supporting software from
|
||||
<http://www.kernel.org/pub/linux/utils/net/hdlc/>.
|
||||
Generic HDLC driver currently supports raw HDLC, Cisco HDLC, Frame
|
||||
Relay, synchronous Point-to-Point Protocol (PPP) and X.25.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called hdlc.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config HDLC_RAW
|
||||
tristate "Raw HDLC support"
|
||||
depends on HDLC
|
||||
help
|
||||
Generic HDLC driver supporting raw HDLC over WAN connections.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config HDLC_RAW_ETH
|
||||
tristate "Raw HDLC Ethernet device support"
|
||||
depends on HDLC
|
||||
help
|
||||
Generic HDLC driver supporting raw HDLC Ethernet device emulation
|
||||
over WAN connections.
|
||||
|
||||
You will need it for Ethernet over HDLC bridges.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config HDLC_CISCO
|
||||
tristate "Cisco HDLC support"
|
||||
depends on HDLC
|
||||
help
|
||||
Generic HDLC driver supporting Cisco HDLC over WAN connections.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config HDLC_FR
|
||||
tristate "Frame Relay support"
|
||||
depends on HDLC
|
||||
help
|
||||
Generic HDLC driver supporting Frame Relay over WAN connections.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config HDLC_PPP
|
||||
tristate "Synchronous Point-to-Point Protocol (PPP) support"
|
||||
depends on HDLC
|
||||
help
|
||||
Generic HDLC driver supporting PPP over WAN connections.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config HDLC_X25
|
||||
tristate "X.25 protocol support"
|
||||
depends on HDLC && (LAPB=m && HDLC=m || LAPB=y)
|
||||
help
|
||||
Generic HDLC driver supporting X.25 over WAN connections.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
comment "X.25/LAPB support is disabled"
|
||||
depends on WAN && HDLC && (LAPB!=m || HDLC!=m) && LAPB!=y
|
||||
|
||||
config PCI200SYN
|
||||
tristate "Goramo PCI200SYN support"
|
||||
depends on HDLC && PCI
|
||||
help
|
||||
Driver for PCI200SYN cards by Goramo sp. j.
|
||||
|
||||
If you have such a card, say Y here and see
|
||||
<http://www.kernel.org/pub/linux/utils/net/hdlc/>.
|
||||
|
||||
To compile this as a module, choose M here: the
|
||||
module will be called pci200syn.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config WANXL
|
||||
tristate "SBE Inc. wanXL support"
|
||||
depends on HDLC && PCI
|
||||
help
|
||||
Driver for wanXL PCI cards by SBE Inc.
|
||||
|
||||
If you have such a card, say Y here and see
|
||||
<http://www.kernel.org/pub/linux/utils/net/hdlc/>.
|
||||
|
||||
To compile this as a module, choose M here: the
|
||||
module will be called wanxl.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config WANXL_BUILD_FIRMWARE
|
||||
bool "rebuild wanXL firmware"
|
||||
depends on WANXL && !PREVENT_FIRMWARE_BUILD
|
||||
help
|
||||
Allows you to rebuild firmware run by the QUICC processor.
|
||||
It requires as68k, ld68k and hexdump programs.
|
||||
|
||||
You should never need this option, say N.
|
||||
|
||||
config PC300
|
||||
tristate "Cyclades-PC300 support (RS-232/V.35, X.21, T1/E1 boards)"
|
||||
depends on HDLC && PCI
|
||||
---help---
|
||||
Driver for the Cyclades-PC300 synchronous communication boards.
|
||||
|
||||
These boards provide synchronous serial interfaces to your
|
||||
Linux box (interfaces currently available are RS-232/V.35, X.21 and
|
||||
T1/E1). If you wish to support Multilink PPP, please select the
|
||||
option later and read the file README.mlppp provided by PC300
|
||||
package.
|
||||
|
||||
To compile this as a module, choose M here: the module
|
||||
will be called pc300.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config PC300_MLPPP
|
||||
bool "Cyclades-PC300 MLPPP support"
|
||||
depends on PC300 && PPP_MULTILINK && PPP_SYNC_TTY && HDLC_PPP
|
||||
help
|
||||
Multilink PPP over the PC300 synchronous communication boards.
|
||||
|
||||
comment "Cyclades-PC300 MLPPP support is disabled."
|
||||
depends on WAN && HDLC && PC300 && (PPP=n || !PPP_MULTILINK || PPP_SYNC_TTY=n || !HDLC_PPP)
|
||||
|
||||
comment "Refer to the file README.mlppp, provided by PC300 package."
|
||||
depends on WAN && HDLC && PC300 && (PPP=n || !PPP_MULTILINK || PPP_SYNC_TTY=n || !HDLC_PPP)
|
||||
|
||||
config PC300TOO
|
||||
tristate "Cyclades PC300 RSV/X21 alternative support"
|
||||
depends on HDLC && PCI
|
||||
help
|
||||
Alternative driver for PC300 RSV/X21 PCI cards made by
|
||||
Cyclades, Inc. If you have such a card, say Y here and see
|
||||
<http://www.kernel.org/pub/linux/utils/net/hdlc/>.
|
||||
|
||||
To compile this as a module, choose M here: the module
|
||||
will be called pc300too.
|
||||
|
||||
If unsure, say N here.
|
||||
|
||||
config N2
|
||||
tristate "SDL RISCom/N2 support"
|
||||
depends on HDLC && ISA
|
||||
help
|
||||
Driver for RISCom/N2 single or dual channel ISA cards by
|
||||
SDL Communications Inc.
|
||||
|
||||
If you have such a card, say Y here and see
|
||||
<http://www.kernel.org/pub/linux/utils/net/hdlc/>.
|
||||
|
||||
Note that N2csu and N2dds cards are not supported by this driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called n2.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config C101
|
||||
tristate "Moxa C101 support"
|
||||
depends on HDLC && ISA
|
||||
help
|
||||
Driver for C101 SuperSync ISA cards by Moxa Technologies Co., Ltd.
|
||||
|
||||
If you have such a card, say Y here and see
|
||||
<http://www.kernel.org/pub/linux/utils/net/hdlc/>.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called c101.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config FARSYNC
|
||||
tristate "FarSync T-Series support"
|
||||
depends on HDLC && PCI
|
||||
---help---
|
||||
Support for the FarSync T-Series X.21 (and V.35/V.24) cards by
|
||||
FarSite Communications Ltd.
|
||||
|
||||
Synchronous communication is supported on all ports at speeds up to
|
||||
8Mb/s (128K on V.24) using synchronous PPP, Cisco HDLC, raw HDLC,
|
||||
Frame Relay or X.25/LAPB.
|
||||
|
||||
If you want the module to be automatically loaded when the interface
|
||||
is referenced then you should add "alias hdlcX farsync" to
|
||||
/etc/modprobe.conf for each interface, where X is 0, 1, 2, ..., or
|
||||
simply use "alias hdlc* farsync" to indicate all of them.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called farsync.
|
||||
|
||||
config DSCC4
|
||||
tristate "Etinc PCISYNC serial board support"
|
||||
depends on HDLC && PCI && m
|
||||
help
|
||||
Driver for Etinc PCISYNC boards based on the Infineon (ex. Siemens)
|
||||
DSCC4 chipset.
|
||||
|
||||
This is supposed to work with the four port card. Take a look at
|
||||
<http://www.cogenit.fr/dscc4/> for further information about the
|
||||
driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called dscc4.
|
||||
|
||||
config DSCC4_PCISYNC
|
||||
bool "Etinc PCISYNC features"
|
||||
depends on DSCC4
|
||||
help
|
||||
Due to Etinc's design choice for its PCISYNC cards, some operations
|
||||
are only allowed on specific ports of the DSCC4. This option is the
|
||||
only way for the driver to know that it shouldn't return a success
|
||||
code for these operations.
|
||||
|
||||
Please say Y if your card is an Etinc's PCISYNC.
|
||||
|
||||
config DSCC4_PCI_RST
|
||||
bool "Hard reset support"
|
||||
depends on DSCC4
|
||||
help
|
||||
Various DSCC4 bugs forbid any reliable software reset of the ASIC.
|
||||
As a replacement, some vendors provide a way to assert the PCI #RST
|
||||
pin of DSCC4 through the GPIO port of the card. If you choose Y,
|
||||
the driver will make use of this feature before module removal
|
||||
(i.e. rmmod). The feature is known to be available on Commtech's
|
||||
cards. Contact your manufacturer for details.
|
||||
|
||||
Say Y if your card supports this feature.
|
||||
|
||||
config DLCI
|
||||
tristate "Frame Relay DLCI support"
|
||||
depends on WAN
|
||||
---help---
|
||||
Support for the Frame Relay protocol.
|
||||
|
||||
Frame Relay is a fast low-cost way to connect to a remote Internet
|
||||
access provider or to form a private wide area network. The one
|
||||
physical line from your box to the local "switch" (i.e. the entry
|
||||
point to the Frame Relay network, usually at the phone company) can
|
||||
carry several logical point-to-point connections to other computers
|
||||
connected to the Frame Relay network. For a general explanation of
|
||||
the protocol, check out <http://www.mplsforum.org/>.
|
||||
|
||||
To use frame relay, you need supporting hardware (called FRAD) and
|
||||
certain programs from the net-tools package as explained in
|
||||
<file:Documentation/networking/framerelay.txt>.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called dlci.
|
||||
|
||||
config DLCI_MAX
|
||||
int "Max DLCI per device"
|
||||
depends on DLCI
|
||||
default "8"
|
||||
help
|
||||
How many logical point-to-point frame relay connections (the
|
||||
identifiers of which are called DCLIs) should be handled by each
|
||||
of your hardware frame relay access devices.
|
||||
|
||||
Go with the default.
|
||||
|
||||
config SDLA
|
||||
tristate "SDLA (Sangoma S502/S508) support"
|
||||
depends on DLCI && ISA
|
||||
help
|
||||
Driver for the Sangoma S502A, S502E, and S508 Frame Relay Access
|
||||
Devices.
|
||||
|
||||
These are multi-protocol cards, but only Frame Relay is supported
|
||||
by the driver at this time. Please read
|
||||
<file:Documentation/networking/framerelay.txt>.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called sdla.
|
||||
|
||||
# Wan router core.
|
||||
config WAN_ROUTER_DRIVERS
|
||||
tristate "WAN router drivers"
|
||||
depends on WAN && WAN_ROUTER
|
||||
---help---
|
||||
Connect LAN to WAN via Linux box.
|
||||
|
||||
Select driver your card and remember to say Y to "Wan Router."
|
||||
You will need the wan-tools package which is available from
|
||||
<ftp://ftp.sangoma.com/>. For more information read:
|
||||
<file:Documentation/networking/wan-router.txt>.
|
||||
|
||||
Note that the answer to this question won't directly affect the
|
||||
kernel except for how subordinate drivers may be built:
|
||||
saying N will just cause the configurator to skip all
|
||||
the questions about WAN router drivers.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config CYCLADES_SYNC
|
||||
tristate "Cyclom 2X(tm) cards (EXPERIMENTAL)"
|
||||
depends on WAN_ROUTER_DRIVERS && (PCI || ISA)
|
||||
---help---
|
||||
Cyclom 2X from Cyclades Corporation <http://www.cyclades.com/> is an
|
||||
intelligent multiprotocol WAN adapter with data transfer rates up to
|
||||
512 Kbps. These cards support the X.25 and SNA related protocols.
|
||||
|
||||
While no documentation is available at this time please grab the
|
||||
wanconfig tarball in
|
||||
<http://www.conectiva.com.br/~acme/cycsyn-devel/> (with minor changes
|
||||
to make it compile with the current wanrouter include files; efforts
|
||||
are being made to use the original package available at
|
||||
<ftp://ftp.sangoma.com/>).
|
||||
|
||||
Feel free to contact me or the cycsyn-devel mailing list at
|
||||
<acme@conectiva.com.br> and <cycsyn-devel@bazar.conectiva.com.br> for
|
||||
additional details, I hope to have documentation available as soon as
|
||||
possible. (Cyclades Brazil is writing the Documentation).
|
||||
|
||||
The next questions will ask you about the protocols you want the
|
||||
driver to support (for now only X.25 is supported).
|
||||
|
||||
If you have one or more of these cards, say Y to this option.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cyclomx.
|
||||
|
||||
config CYCLOMX_X25
|
||||
bool "Cyclom 2X X.25 support (EXPERIMENTAL)"
|
||||
depends on CYCLADES_SYNC
|
||||
help
|
||||
Connect a Cyclom 2X card to an X.25 network.
|
||||
|
||||
Enabling X.25 support will enlarge your kernel by about 11 kB.
|
||||
|
||||
# X.25 network drivers
|
||||
config LAPBETHER
|
||||
tristate "LAPB over Ethernet driver (EXPERIMENTAL)"
|
||||
depends on WAN && LAPB && X25
|
||||
---help---
|
||||
Driver for a pseudo device (typically called /dev/lapb0) which allows
|
||||
you to open an LAPB point-to-point connection to some other computer
|
||||
on your Ethernet network.
|
||||
|
||||
In order to do this, you need to say Y or M to the driver for your
|
||||
Ethernet card as well as to "LAPB Data Link Driver".
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called lapbether.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config X25_ASY
|
||||
tristate "X.25 async driver (EXPERIMENTAL)"
|
||||
depends on WAN && LAPB && X25
|
||||
---help---
|
||||
Send and receive X.25 frames over regular asynchronous serial
|
||||
lines such as telephone lines equipped with ordinary modems.
|
||||
|
||||
Experts should note that this driver doesn't currently comply with
|
||||
the asynchronous HDLS framing protocols in CCITT recommendation X.25.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called x25_asy.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config SBNI
|
||||
tristate "Granch SBNI12 Leased Line adapter support"
|
||||
depends on WAN && X86
|
||||
---help---
|
||||
Driver for ISA SBNI12-xx cards which are low cost alternatives to
|
||||
leased line modems.
|
||||
|
||||
You can find more information and last versions of drivers and
|
||||
utilities at <http://www.granch.ru/>. If you have any question you
|
||||
can send email to <sbni@granch.ru>.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called sbni.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config SBNI_MULTILINE
|
||||
bool "Multiple line feature support"
|
||||
depends on SBNI
|
||||
help
|
||||
Schedule traffic for some parallel lines, via SBNI12 adapters.
|
||||
|
||||
If you have two computers connected with two parallel lines it's
|
||||
possible to increase transfer rate nearly twice. You should have
|
||||
a program named 'sbniconfig' to configure adapters.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
endmenu
|
||||
|
||||
68
drivers/net/wan/Makefile
Normal file
68
drivers/net/wan/Makefile
Normal file
@@ -0,0 +1,68 @@
|
||||
#
|
||||
# Makefile for the Linux network (wan) device drivers.
|
||||
#
|
||||
# 3 Aug 2000, Christoph Hellwig <hch@infradead.org>
|
||||
# Rewritten to use lists instead of if-statements.
|
||||
#
|
||||
|
||||
cyclomx-y := cycx_main.o
|
||||
cyclomx-$(CONFIG_CYCLOMX_X25) += cycx_x25.o
|
||||
cyclomx-objs := $(cyclomx-y)
|
||||
|
||||
obj-$(CONFIG_HDLC) += hdlc.o
|
||||
obj-$(CONFIG_HDLC_RAW) += hdlc_raw.o
|
||||
obj-$(CONFIG_HDLC_RAW_ETH) += hdlc_raw_eth.o
|
||||
obj-$(CONFIG_HDLC_CISCO) += hdlc_cisco.o
|
||||
obj-$(CONFIG_HDLC_FR) += hdlc_fr.o
|
||||
obj-$(CONFIG_HDLC_PPP) += hdlc_ppp.o syncppp.o
|
||||
obj-$(CONFIG_HDLC_X25) += hdlc_x25.o
|
||||
|
||||
pc300-y := pc300_drv.o
|
||||
pc300-$(CONFIG_PC300_MLPPP) += pc300_tty.o
|
||||
pc300-objs := $(pc300-y)
|
||||
|
||||
obj-$(CONFIG_HOSTESS_SV11) += z85230.o syncppp.o hostess_sv11.o
|
||||
obj-$(CONFIG_SEALEVEL_4021) += z85230.o syncppp.o sealevel.o
|
||||
obj-$(CONFIG_COSA) += syncppp.o cosa.o
|
||||
obj-$(CONFIG_FARSYNC) += syncppp.o farsync.o
|
||||
obj-$(CONFIG_DSCC4) += dscc4.o
|
||||
obj-$(CONFIG_LANMEDIA) += syncppp.o
|
||||
obj-$(CONFIG_X25_ASY) += x25_asy.o
|
||||
|
||||
obj-$(CONFIG_LANMEDIA) += lmc/
|
||||
|
||||
obj-$(CONFIG_DLCI) += dlci.o
|
||||
obj-$(CONFIG_SDLA) += sdla.o
|
||||
obj-$(CONFIG_CYCLADES_SYNC) += cycx_drv.o cyclomx.o
|
||||
obj-$(CONFIG_LAPBETHER) += lapbether.o
|
||||
obj-$(CONFIG_SBNI) += sbni.o
|
||||
obj-$(CONFIG_PC300) += pc300.o
|
||||
obj-$(CONFIG_N2) += n2.o
|
||||
obj-$(CONFIG_C101) += c101.o
|
||||
obj-$(CONFIG_WANXL) += wanxl.o
|
||||
obj-$(CONFIG_PCI200SYN) += pci200syn.o
|
||||
obj-$(CONFIG_PC300TOO) += pc300too.o
|
||||
|
||||
clean-files := wanxlfw.inc
|
||||
$(obj)/wanxl.o: $(obj)/wanxlfw.inc
|
||||
|
||||
ifeq ($(CONFIG_WANXL_BUILD_FIRMWARE),y)
|
||||
ifeq ($(ARCH),m68k)
|
||||
AS68K = $(AS)
|
||||
LD68K = $(LD)
|
||||
else
|
||||
AS68K = as68k
|
||||
LD68K = ld68k
|
||||
endif
|
||||
|
||||
quiet_cmd_build_wanxlfw = BLD FW $@
|
||||
cmd_build_wanxlfw = \
|
||||
$(CPP) -Wp,-MD,$(depfile) -I$(srctree)/include $< | $(AS68K) -m68360 -o $(obj)/wanxlfw.o; \
|
||||
$(LD68K) --oformat binary -Ttext 0x1000 $(obj)/wanxlfw.o -o $(obj)/wanxlfw.bin; \
|
||||
hexdump -ve '"\n" 16/1 "0x%02X,"' $(obj)/wanxlfw.bin | sed 's/0x ,//g;1s/^/static u8 firmware[]={/;$$s/,$$/\n};\n/' >$(obj)/wanxlfw.inc; \
|
||||
rm -f $(obj)/wanxlfw.bin $(obj)/wanxlfw.o
|
||||
|
||||
$(obj)/wanxlfw.inc: $(src)/wanxlfw.S
|
||||
$(call if_changed_dep,build_wanxlfw)
|
||||
targets += wanxlfw.inc
|
||||
endif
|
||||
453
drivers/net/wan/c101.c
Normal file
453
drivers/net/wan/c101.c
Normal file
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* Moxa C101 synchronous serial card driver for Linux
|
||||
*
|
||||
* Copyright (C) 2000-2003 Krzysztof Halasa <khc@pm.waw.pl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* For information see <http://www.kernel.org/pub/linux/utils/net/hdlc/>
|
||||
*
|
||||
* Sources of information:
|
||||
* Hitachi HD64570 SCA User's Manual
|
||||
* Moxa C101 User's Manual
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/hdlc.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "hd64570.h"
|
||||
|
||||
|
||||
static const char* version = "Moxa C101 driver version: 1.15";
|
||||
static const char* devname = "C101";
|
||||
|
||||
#undef DEBUG_PKT
|
||||
#define DEBUG_RINGS
|
||||
|
||||
#define C101_PAGE 0x1D00
|
||||
#define C101_DTR 0x1E00
|
||||
#define C101_SCA 0x1F00
|
||||
#define C101_WINDOW_SIZE 0x2000
|
||||
#define C101_MAPPED_RAM_SIZE 0x4000
|
||||
|
||||
#define RAM_SIZE (256 * 1024)
|
||||
#define TX_RING_BUFFERS 10
|
||||
#define RX_RING_BUFFERS ((RAM_SIZE - C101_WINDOW_SIZE) / \
|
||||
(sizeof(pkt_desc) + HDLC_MAX_MRU) - TX_RING_BUFFERS)
|
||||
|
||||
#define CLOCK_BASE 9830400 /* 9.8304 MHz */
|
||||
#define PAGE0_ALWAYS_MAPPED
|
||||
|
||||
static char *hw; /* pointer to hw=xxx command line string */
|
||||
|
||||
|
||||
typedef struct card_s {
|
||||
struct net_device *dev;
|
||||
spinlock_t lock; /* TX lock */
|
||||
u8 __iomem *win0base; /* ISA window base address */
|
||||
u32 phy_winbase; /* ISA physical base address */
|
||||
sync_serial_settings settings;
|
||||
int rxpart; /* partial frame received, next frame invalid*/
|
||||
unsigned short encoding;
|
||||
unsigned short parity;
|
||||
u16 rx_ring_buffers; /* number of buffers in a ring */
|
||||
u16 tx_ring_buffers;
|
||||
u16 buff_offset; /* offset of first buffer of first channel */
|
||||
u16 rxin; /* rx ring buffer 'in' pointer */
|
||||
u16 txin; /* tx ring buffer 'in' and 'last' pointers */
|
||||
u16 txlast;
|
||||
u8 rxs, txs, tmc; /* SCA registers */
|
||||
u8 irq; /* IRQ (3-15) */
|
||||
u8 page;
|
||||
|
||||
struct card_s *next_card;
|
||||
}card_t;
|
||||
|
||||
typedef card_t port_t;
|
||||
|
||||
static card_t *first_card;
|
||||
static card_t **new_card = &first_card;
|
||||
|
||||
|
||||
#define sca_in(reg, card) readb((card)->win0base + C101_SCA + (reg))
|
||||
#define sca_out(value, reg, card) writeb(value, (card)->win0base + C101_SCA + (reg))
|
||||
#define sca_inw(reg, card) readw((card)->win0base + C101_SCA + (reg))
|
||||
|
||||
/* EDA address register must be set in EDAL, EDAH order - 8 bit ISA bus */
|
||||
#define sca_outw(value, reg, card) do { \
|
||||
writeb(value & 0xFF, (card)->win0base + C101_SCA + (reg)); \
|
||||
writeb((value >> 8 ) & 0xFF, (card)->win0base + C101_SCA + (reg+1));\
|
||||
} while(0)
|
||||
|
||||
#define port_to_card(port) (port)
|
||||
#define log_node(port) (0)
|
||||
#define phy_node(port) (0)
|
||||
#define winsize(card) (C101_WINDOW_SIZE)
|
||||
#define win0base(card) ((card)->win0base)
|
||||
#define winbase(card) ((card)->win0base + 0x2000)
|
||||
#define get_port(card, port) (card)
|
||||
static void sca_msci_intr(port_t *port);
|
||||
|
||||
|
||||
static inline u8 sca_get_page(card_t *card)
|
||||
{
|
||||
return card->page;
|
||||
}
|
||||
|
||||
static inline void openwin(card_t *card, u8 page)
|
||||
{
|
||||
card->page = page;
|
||||
writeb(page, card->win0base + C101_PAGE);
|
||||
}
|
||||
|
||||
|
||||
#include "hd6457x.c"
|
||||
|
||||
|
||||
static inline void set_carrier(port_t *port)
|
||||
{
|
||||
if (!(sca_in(MSCI1_OFFSET + ST3, port) & ST3_DCD))
|
||||
netif_carrier_on(port_to_dev(port));
|
||||
else
|
||||
netif_carrier_off(port_to_dev(port));
|
||||
}
|
||||
|
||||
|
||||
static void sca_msci_intr(port_t *port)
|
||||
{
|
||||
u8 stat = sca_in(MSCI0_OFFSET + ST1, port); /* read MSCI ST1 status */
|
||||
|
||||
/* Reset MSCI TX underrun and CDCD (ignored) status bit */
|
||||
sca_out(stat & (ST1_UDRN | ST1_CDCD), MSCI0_OFFSET + ST1, port);
|
||||
|
||||
if (stat & ST1_UDRN) {
|
||||
struct net_device_stats *stats = hdlc_stats(port_to_dev(port));
|
||||
stats->tx_errors++; /* TX Underrun error detected */
|
||||
stats->tx_fifo_errors++;
|
||||
}
|
||||
|
||||
stat = sca_in(MSCI1_OFFSET + ST1, port); /* read MSCI1 ST1 status */
|
||||
/* Reset MSCI CDCD status bit - uses ch#2 DCD input */
|
||||
sca_out(stat & ST1_CDCD, MSCI1_OFFSET + ST1, port);
|
||||
|
||||
if (stat & ST1_CDCD)
|
||||
set_carrier(port);
|
||||
}
|
||||
|
||||
|
||||
static void c101_set_iface(port_t *port)
|
||||
{
|
||||
u8 rxs = port->rxs & CLK_BRG_MASK;
|
||||
u8 txs = port->txs & CLK_BRG_MASK;
|
||||
|
||||
switch(port->settings.clock_type) {
|
||||
case CLOCK_INT:
|
||||
rxs |= CLK_BRG_RX; /* TX clock */
|
||||
txs |= CLK_RXCLK_TX; /* BRG output */
|
||||
break;
|
||||
|
||||
case CLOCK_TXINT:
|
||||
rxs |= CLK_LINE_RX; /* RXC input */
|
||||
txs |= CLK_BRG_TX; /* BRG output */
|
||||
break;
|
||||
|
||||
case CLOCK_TXFROMRX:
|
||||
rxs |= CLK_LINE_RX; /* RXC input */
|
||||
txs |= CLK_RXCLK_TX; /* RX clock */
|
||||
break;
|
||||
|
||||
default: /* EXTernal clock */
|
||||
rxs |= CLK_LINE_RX; /* RXC input */
|
||||
txs |= CLK_LINE_TX; /* TXC input */
|
||||
}
|
||||
|
||||
port->rxs = rxs;
|
||||
port->txs = txs;
|
||||
sca_out(rxs, MSCI1_OFFSET + RXS, port);
|
||||
sca_out(txs, MSCI1_OFFSET + TXS, port);
|
||||
sca_set_port(port);
|
||||
}
|
||||
|
||||
|
||||
static int c101_open(struct net_device *dev)
|
||||
{
|
||||
port_t *port = dev_to_port(dev);
|
||||
int result;
|
||||
|
||||
result = hdlc_open(dev);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
writeb(1, port->win0base + C101_DTR);
|
||||
sca_out(0, MSCI1_OFFSET + CTL, port); /* RTS uses ch#2 output */
|
||||
sca_open(dev);
|
||||
/* DCD is connected to port 2 !@#$%^& - disable MSCI0 CDCD interrupt */
|
||||
sca_out(IE1_UDRN, MSCI0_OFFSET + IE1, port);
|
||||
sca_out(IE0_TXINT, MSCI0_OFFSET + IE0, port);
|
||||
|
||||
set_carrier(port);
|
||||
|
||||
/* enable MSCI1 CDCD interrupt */
|
||||
sca_out(IE1_CDCD, MSCI1_OFFSET + IE1, port);
|
||||
sca_out(IE0_RXINTA, MSCI1_OFFSET + IE0, port);
|
||||
sca_out(0x48, IER0, port); /* TXINT #0 and RXINT #1 */
|
||||
c101_set_iface(port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int c101_close(struct net_device *dev)
|
||||
{
|
||||
port_t *port = dev_to_port(dev);
|
||||
|
||||
sca_close(dev);
|
||||
writeb(0, port->win0base + C101_DTR);
|
||||
sca_out(CTL_NORTS, MSCI1_OFFSET + CTL, port);
|
||||
hdlc_close(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int c101_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
const size_t size = sizeof(sync_serial_settings);
|
||||
sync_serial_settings new_line;
|
||||
sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
|
||||
port_t *port = dev_to_port(dev);
|
||||
|
||||
#ifdef DEBUG_RINGS
|
||||
if (cmd == SIOCDEVPRIVATE) {
|
||||
sca_dump_rings(dev);
|
||||
printk(KERN_DEBUG "MSCI1: ST: %02x %02x %02x %02x\n",
|
||||
sca_in(MSCI1_OFFSET + ST0, port),
|
||||
sca_in(MSCI1_OFFSET + ST1, port),
|
||||
sca_in(MSCI1_OFFSET + ST2, port),
|
||||
sca_in(MSCI1_OFFSET + ST3, port));
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (cmd != SIOCWANDEV)
|
||||
return hdlc_ioctl(dev, ifr, cmd);
|
||||
|
||||
switch(ifr->ifr_settings.type) {
|
||||
case IF_GET_IFACE:
|
||||
ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
|
||||
if (ifr->ifr_settings.size < size) {
|
||||
ifr->ifr_settings.size = size; /* data size wanted */
|
||||
return -ENOBUFS;
|
||||
}
|
||||
if (copy_to_user(line, &port->settings, size))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case IF_IFACE_SYNC_SERIAL:
|
||||
if(!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&new_line, line, size))
|
||||
return -EFAULT;
|
||||
|
||||
if (new_line.clock_type != CLOCK_EXT &&
|
||||
new_line.clock_type != CLOCK_TXFROMRX &&
|
||||
new_line.clock_type != CLOCK_INT &&
|
||||
new_line.clock_type != CLOCK_TXINT)
|
||||
return -EINVAL; /* No such clock setting */
|
||||
|
||||
if (new_line.loopback != 0 && new_line.loopback != 1)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&port->settings, &new_line, size); /* Update settings */
|
||||
c101_set_iface(port);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return hdlc_ioctl(dev, ifr, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void c101_destroy_card(card_t *card)
|
||||
{
|
||||
readb(card->win0base + C101_PAGE); /* Resets SCA? */
|
||||
|
||||
if (card->irq)
|
||||
free_irq(card->irq, card);
|
||||
|
||||
if (card->win0base) {
|
||||
iounmap(card->win0base);
|
||||
release_mem_region(card->phy_winbase, C101_MAPPED_RAM_SIZE);
|
||||
}
|
||||
|
||||
free_netdev(card->dev);
|
||||
|
||||
kfree(card);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int __init c101_run(unsigned long irq, unsigned long winbase)
|
||||
{
|
||||
struct net_device *dev;
|
||||
hdlc_device *hdlc;
|
||||
card_t *card;
|
||||
int result;
|
||||
|
||||
if (irq<3 || irq>15 || irq == 6) /* FIXME */ {
|
||||
printk(KERN_ERR "c101: invalid IRQ value\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (winbase < 0xC0000 || winbase > 0xDFFFF || (winbase & 0x3FFF) !=0) {
|
||||
printk(KERN_ERR "c101: invalid RAM value\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
card = kmalloc(sizeof(card_t), GFP_KERNEL);
|
||||
if (card == NULL) {
|
||||
printk(KERN_ERR "c101: unable to allocate memory\n");
|
||||
return -ENOBUFS;
|
||||
}
|
||||
memset(card, 0, sizeof(card_t));
|
||||
|
||||
card->dev = alloc_hdlcdev(card);
|
||||
if (!card->dev) {
|
||||
printk(KERN_ERR "c101: unable to allocate memory\n");
|
||||
kfree(card);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
if (request_irq(irq, sca_intr, 0, devname, card)) {
|
||||
printk(KERN_ERR "c101: could not allocate IRQ\n");
|
||||
c101_destroy_card(card);
|
||||
return -EBUSY;
|
||||
}
|
||||
card->irq = irq;
|
||||
|
||||
if (!request_mem_region(winbase, C101_MAPPED_RAM_SIZE, devname)) {
|
||||
printk(KERN_ERR "c101: could not request RAM window\n");
|
||||
c101_destroy_card(card);
|
||||
return -EBUSY;
|
||||
}
|
||||
card->phy_winbase = winbase;
|
||||
card->win0base = ioremap(winbase, C101_MAPPED_RAM_SIZE);
|
||||
if (!card->win0base) {
|
||||
printk(KERN_ERR "c101: could not map I/O address\n");
|
||||
c101_destroy_card(card);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
card->tx_ring_buffers = TX_RING_BUFFERS;
|
||||
card->rx_ring_buffers = RX_RING_BUFFERS;
|
||||
card->buff_offset = C101_WINDOW_SIZE; /* Bytes 1D00-1FFF reserved */
|
||||
|
||||
readb(card->win0base + C101_PAGE); /* Resets SCA? */
|
||||
udelay(100);
|
||||
writeb(0, card->win0base + C101_PAGE);
|
||||
writeb(0, card->win0base + C101_DTR); /* Power-up for RAM? */
|
||||
|
||||
sca_init(card, 0);
|
||||
|
||||
dev = port_to_dev(card);
|
||||
hdlc = dev_to_hdlc(dev);
|
||||
|
||||
spin_lock_init(&card->lock);
|
||||
SET_MODULE_OWNER(dev);
|
||||
dev->irq = irq;
|
||||
dev->mem_start = winbase;
|
||||
dev->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1;
|
||||
dev->tx_queue_len = 50;
|
||||
dev->do_ioctl = c101_ioctl;
|
||||
dev->open = c101_open;
|
||||
dev->stop = c101_close;
|
||||
hdlc->attach = sca_attach;
|
||||
hdlc->xmit = sca_xmit;
|
||||
card->settings.clock_type = CLOCK_EXT;
|
||||
|
||||
result = register_hdlc_device(dev);
|
||||
if (result) {
|
||||
printk(KERN_WARNING "c101: unable to register hdlc device\n");
|
||||
c101_destroy_card(card);
|
||||
return result;
|
||||
}
|
||||
|
||||
sca_init_sync_port(card); /* Set up C101 memory */
|
||||
set_carrier(card);
|
||||
|
||||
printk(KERN_INFO "%s: Moxa C101 on IRQ%u,"
|
||||
" using %u TX + %u RX packets rings\n",
|
||||
dev->name, card->irq,
|
||||
card->tx_ring_buffers, card->rx_ring_buffers);
|
||||
|
||||
*new_card = card;
|
||||
new_card = &card->next_card;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int __init c101_init(void)
|
||||
{
|
||||
if (hw == NULL) {
|
||||
#ifdef MODULE
|
||||
printk(KERN_INFO "c101: no card initialized\n");
|
||||
#endif
|
||||
return -ENOSYS; /* no parameters specified, abort */
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s\n", version);
|
||||
|
||||
do {
|
||||
unsigned long irq, ram;
|
||||
|
||||
irq = simple_strtoul(hw, &hw, 0);
|
||||
|
||||
if (*hw++ != ',')
|
||||
break;
|
||||
ram = simple_strtoul(hw, &hw, 0);
|
||||
|
||||
if (*hw == ':' || *hw == '\x0')
|
||||
c101_run(irq, ram);
|
||||
|
||||
if (*hw == '\x0')
|
||||
return first_card ? 0 : -ENOSYS;
|
||||
}while(*hw++ == ':');
|
||||
|
||||
printk(KERN_ERR "c101: invalid hardware parameters\n");
|
||||
return first_card ? 0 : -ENOSYS;
|
||||
}
|
||||
|
||||
|
||||
static void __exit c101_cleanup(void)
|
||||
{
|
||||
card_t *card = first_card;
|
||||
|
||||
while (card) {
|
||||
card_t *ptr = card;
|
||||
card = card->next_card;
|
||||
unregister_hdlc_device(port_to_dev(ptr));
|
||||
c101_destroy_card(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module_init(c101_init);
|
||||
module_exit(c101_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
|
||||
MODULE_DESCRIPTION("Moxa C101 serial port driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
module_param(hw, charp, 0444);
|
||||
MODULE_PARM_DESC(hw, "irq,ram:irq,...");
|
||||
2084
drivers/net/wan/cosa.c
Normal file
2084
drivers/net/wan/cosa.c
Normal file
File diff suppressed because it is too large
Load Diff
117
drivers/net/wan/cosa.h
Normal file
117
drivers/net/wan/cosa.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/* $Id: cosa.h,v 1.1.1.1 2007/06/12 07:27:11 eyryu Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (C) 1995-1997 Jan "Yenya" Kasprzak <kas@fi.muni.cz>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef COSA_H__
|
||||
#define COSA_H__
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/* status register - output bits */
|
||||
#define SR_RX_DMA_ENA 0x04 /* receiver DMA enable bit */
|
||||
#define SR_TX_DMA_ENA 0x08 /* transmitter DMA enable bit */
|
||||
#define SR_RST 0x10 /* SRP reset */
|
||||
#define SR_USR_INT_ENA 0x20 /* user interrupt enable bit */
|
||||
#define SR_TX_INT_ENA 0x40 /* transmitter interrupt enable bit */
|
||||
#define SR_RX_INT_ENA 0x80 /* receiver interrupt enable bit */
|
||||
|
||||
/* status register - input bits */
|
||||
#define SR_USR_RQ 0x20 /* user interrupt request pending */
|
||||
#define SR_TX_RDY 0x40 /* transmitter empty (ready) */
|
||||
#define SR_RX_RDY 0x80 /* receiver data ready */
|
||||
|
||||
#define SR_UP_REQUEST 0x02 /* request from SRP to transfer data
|
||||
up to PC */
|
||||
#define SR_DOWN_REQUEST 0x01 /* SRP is able to transfer data down
|
||||
from PC to SRP */
|
||||
#define SR_END_OF_TRANSFER 0x03 /* SRP signalize end of
|
||||
transfer (up or down) */
|
||||
|
||||
#define SR_CMD_FROM_SRP_MASK 0x03 /* mask to get SRP command */
|
||||
|
||||
/* bits in driver status byte definitions : */
|
||||
#define SR_RDY_RCV 0x01 /* ready to receive packet */
|
||||
#define SR_RDY_SND 0x02 /* ready to send packet */
|
||||
#define SR_CMD_PND 0x04 /* command pending */ /* not currently used */
|
||||
|
||||
/* ???? */
|
||||
#define SR_PKT_UP 0x01 /* transfer of packet up in progress */
|
||||
#define SR_PKT_DOWN 0x02 /* transfer of packet down in progress */
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#define SR_LOAD_ADDR 0x4400 /* SRP microcode load address */
|
||||
#define SR_START_ADDR 0x4400 /* SRP microcode start address */
|
||||
|
||||
#define COSA_LOAD_ADDR 0x400 /* SRP microcode load address */
|
||||
#define COSA_MAX_FIRMWARE_SIZE 0x10000
|
||||
|
||||
/* ioctls */
|
||||
struct cosa_download {
|
||||
int addr, len;
|
||||
char __user *code;
|
||||
};
|
||||
|
||||
/* Reset the device */
|
||||
#define COSAIORSET _IO('C',0xf0)
|
||||
|
||||
/* Start microcode at given address */
|
||||
#define COSAIOSTRT _IOW('C',0xf1, int)
|
||||
|
||||
/* Read the block from the device memory */
|
||||
#define COSAIORMEM _IOWR('C',0xf2, struct cosa_download *)
|
||||
/* actually the struct cosa_download itself; this is to keep
|
||||
* the ioctl number same as in 2.4 in order to keep the user-space
|
||||
* utils compatible. */
|
||||
|
||||
/* Write the block to the device memory (i.e. download the microcode) */
|
||||
#define COSAIODOWNLD _IOW('C',0xf2, struct cosa_download *)
|
||||
/* actually the struct cosa_download itself; this is to keep
|
||||
* the ioctl number same as in 2.4 in order to keep the user-space
|
||||
* utils compatible. */
|
||||
|
||||
/* Read the device type (one of "srp", "cosa", and "cosa8" for now) */
|
||||
#define COSAIORTYPE _IOR('C',0xf3, char *)
|
||||
|
||||
/* Read the device identification string */
|
||||
#define COSAIORIDSTR _IOR('C',0xf4, char *)
|
||||
/* Maximum length of the identification string. */
|
||||
#define COSA_MAX_ID_STRING 128
|
||||
|
||||
/* Increment/decrement the module usage count :-) */
|
||||
/* #define COSAIOMINC _IO('C',0xf5) */
|
||||
/* #define COSAIOMDEC _IO('C',0xf6) */
|
||||
|
||||
/* Get the total number of cards installed */
|
||||
#define COSAIONRCARDS _IO('C',0xf7)
|
||||
|
||||
/* Get the number of channels on this card */
|
||||
#define COSAIONRCHANS _IO('C',0xf8)
|
||||
|
||||
/* Set the driver for the bus-master operations */
|
||||
#define COSAIOBMSET _IOW('C', 0xf9, unsigned short)
|
||||
|
||||
#define COSA_BM_OFF 0 /* Bus-mastering off - use ISA DMA (default) */
|
||||
#define COSA_BM_ON 1 /* Bus-mastering on - faster but untested */
|
||||
|
||||
/* Gets the busmaster status */
|
||||
#define COSAIOBMGET _IO('C', 0xfa)
|
||||
|
||||
#endif /* !COSA_H__ */
|
||||
576
drivers/net/wan/cycx_drv.c
Normal file
576
drivers/net/wan/cycx_drv.c
Normal file
@@ -0,0 +1,576 @@
|
||||
/*
|
||||
* cycx_drv.c Cyclom 2X Support Module.
|
||||
*
|
||||
* This module is a library of common hardware specific
|
||||
* functions used by the Cyclades Cyclom 2X sync card.
|
||||
*
|
||||
* Author: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
|
||||
*
|
||||
* Copyright: (c) 1998-2003 Arnaldo Carvalho de Melo
|
||||
*
|
||||
* Based on sdladrv.c by Gene Kozin <genek@compuserve.com>
|
||||
*
|
||||
* 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.
|
||||
* ============================================================================
|
||||
* 1999/11/11 acme set_current_state(TASK_INTERRUPTIBLE), code
|
||||
* cleanup
|
||||
* 1999/11/08 acme init_cyc2x deleted, doing nothing
|
||||
* 1999/11/06 acme back to read[bw], write[bw] and memcpy_to and
|
||||
* fromio to use dpmbase ioremaped
|
||||
* 1999/10/26 acme use isa_read[bw], isa_write[bw] & isa_memcpy_to
|
||||
* & fromio
|
||||
* 1999/10/23 acme cleanup to only supports cyclom2x: all the other
|
||||
* boards are no longer manufactured by cyclades,
|
||||
* if someone wants to support them... be my guest!
|
||||
* 1999/05/28 acme cycx_intack & cycx_intde gone for good
|
||||
* 1999/05/18 acme lots of unlogged work, submitting to Linus...
|
||||
* 1999/01/03 acme more judicious use of data types
|
||||
* 1999/01/03 acme judicious use of data types :>
|
||||
* cycx_inten trying to reset pending interrupts
|
||||
* from cyclom 2x - I think this isn't the way to
|
||||
* go, but for now...
|
||||
* 1999/01/02 acme cycx_intack ok, I think there's nothing to do
|
||||
* to ack an int in cycx_drv.c, only handle it in
|
||||
* cyx_isr (or in the other protocols: cyp_isr,
|
||||
* cyf_isr, when they get implemented.
|
||||
* Dec 31, 1998 acme cycx_data_boot & cycx_code_boot fixed, crossing
|
||||
* fingers to see x25_configure in cycx_x25.c
|
||||
* work... :)
|
||||
* Dec 26, 1998 acme load implementation fixed, seems to work! :)
|
||||
* cycx_2x_dpmbase_options with all the possible
|
||||
* DPM addresses (20).
|
||||
* cycx_intr implemented (test this!)
|
||||
* general code cleanup
|
||||
* Dec 8, 1998 Ivan Passos Cyclom-2X firmware load implementation.
|
||||
* Aug 8, 1998 acme Initial version.
|
||||
*/
|
||||
|
||||
#include <linux/init.h> /* __init */
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h> /* printk(), and other useful stuff */
|
||||
#include <linux/stddef.h> /* offsetof(), etc. */
|
||||
#include <linux/errno.h> /* return codes */
|
||||
#include <linux/cycx_drv.h> /* API definitions */
|
||||
#include <linux/cycx_cfm.h> /* CYCX firmware module definitions */
|
||||
#include <linux/delay.h> /* udelay, msleep_interruptible */
|
||||
#include <asm/io.h> /* read[wl], write[wl], ioremap, iounmap */
|
||||
|
||||
#define MOD_VERSION 0
|
||||
#define MOD_RELEASE 6
|
||||
|
||||
MODULE_AUTHOR("Arnaldo Carvalho de Melo");
|
||||
MODULE_DESCRIPTION("Cyclom 2x Sync Card Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* Hardware-specific functions */
|
||||
static int load_cyc2x(struct cycx_hw *hw, struct cycx_firmware *cfm, u32 len);
|
||||
static void cycx_bootcfg(struct cycx_hw *hw);
|
||||
|
||||
static int reset_cyc2x(void __iomem *addr);
|
||||
static int detect_cyc2x(void __iomem *addr);
|
||||
|
||||
/* Miscellaneous functions */
|
||||
static int get_option_index(long *optlist, long optval);
|
||||
static u16 checksum(u8 *buf, u32 len);
|
||||
|
||||
#define wait_cyc(addr) cycx_exec(addr + CMD_OFFSET)
|
||||
|
||||
/* Global Data */
|
||||
|
||||
/* private data */
|
||||
static char modname[] = "cycx_drv";
|
||||
static char fullname[] = "Cyclom 2X Support Module";
|
||||
static char copyright[] = "(c) 1998-2003 Arnaldo Carvalho de Melo "
|
||||
"<acme@conectiva.com.br>";
|
||||
|
||||
/* Hardware configuration options.
|
||||
* These are arrays of configuration options used by verification routines.
|
||||
* The first element of each array is its size (i.e. number of options).
|
||||
*/
|
||||
static long cyc2x_dpmbase_options[] = {
|
||||
20,
|
||||
0xA0000, 0xA4000, 0xA8000, 0xAC000, 0xB0000, 0xB4000, 0xB8000,
|
||||
0xBC000, 0xC0000, 0xC4000, 0xC8000, 0xCC000, 0xD0000, 0xD4000,
|
||||
0xD8000, 0xDC000, 0xE0000, 0xE4000, 0xE8000, 0xEC000
|
||||
};
|
||||
|
||||
static long cycx_2x_irq_options[] = { 7, 3, 5, 9, 10, 11, 12, 15 };
|
||||
|
||||
/* Kernel Loadable Module Entry Points */
|
||||
/* Module 'insert' entry point.
|
||||
* o print announcement
|
||||
* o initialize static data
|
||||
*
|
||||
* Return: 0 Ok
|
||||
* < 0 error.
|
||||
* Context: process */
|
||||
|
||||
static int __init cycx_drv_init(void)
|
||||
{
|
||||
printk(KERN_INFO "%s v%u.%u %s\n", fullname, MOD_VERSION, MOD_RELEASE,
|
||||
copyright);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Module 'remove' entry point.
|
||||
* o release all remaining system resources */
|
||||
static void cycx_drv_cleanup(void)
|
||||
{
|
||||
}
|
||||
|
||||
/* Kernel APIs */
|
||||
/* Set up adapter.
|
||||
* o detect adapter type
|
||||
* o verify hardware configuration options
|
||||
* o check for hardware conflicts
|
||||
* o set up adapter shared memory
|
||||
* o test adapter memory
|
||||
* o load firmware
|
||||
* Return: 0 ok.
|
||||
* < 0 error */
|
||||
EXPORT_SYMBOL(cycx_setup);
|
||||
int cycx_setup(struct cycx_hw *hw, void *cfm, u32 len, unsigned long dpmbase)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Verify IRQ configuration options */
|
||||
if (!get_option_index(cycx_2x_irq_options, hw->irq)) {
|
||||
printk(KERN_ERR "%s: IRQ %d is invalid!\n", modname, hw->irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Setup adapter dual-port memory window and test memory */
|
||||
if (!dpmbase) {
|
||||
printk(KERN_ERR "%s: you must specify the dpm address!\n",
|
||||
modname);
|
||||
return -EINVAL;
|
||||
} else if (!get_option_index(cyc2x_dpmbase_options, dpmbase)) {
|
||||
printk(KERN_ERR "%s: memory address 0x%lX is invalid!\n",
|
||||
modname, dpmbase);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hw->dpmbase = ioremap(dpmbase, CYCX_WINDOWSIZE);
|
||||
hw->dpmsize = CYCX_WINDOWSIZE;
|
||||
|
||||
if (!detect_cyc2x(hw->dpmbase)) {
|
||||
printk(KERN_ERR "%s: adapter Cyclom 2X not found at "
|
||||
"address 0x%lX!\n", modname, dpmbase);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s: found Cyclom 2X card at address 0x%lX.\n",
|
||||
modname, dpmbase);
|
||||
|
||||
/* Load firmware. If loader fails then shut down adapter */
|
||||
err = load_cyc2x(hw, cfm, len);
|
||||
|
||||
if (err)
|
||||
cycx_down(hw); /* shutdown adapter */
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(cycx_down);
|
||||
int cycx_down(struct cycx_hw *hw)
|
||||
{
|
||||
iounmap(hw->dpmbase);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Enable interrupt generation. */
|
||||
static void cycx_inten(struct cycx_hw *hw)
|
||||
{
|
||||
writeb(0, hw->dpmbase);
|
||||
}
|
||||
|
||||
/* Generate an interrupt to adapter's CPU. */
|
||||
EXPORT_SYMBOL(cycx_intr);
|
||||
void cycx_intr(struct cycx_hw *hw)
|
||||
{
|
||||
writew(0, hw->dpmbase + GEN_CYCX_INTR);
|
||||
}
|
||||
|
||||
/* Execute Adapter Command.
|
||||
* o Set exec flag.
|
||||
* o Busy-wait until flag is reset. */
|
||||
EXPORT_SYMBOL(cycx_exec);
|
||||
int cycx_exec(void __iomem *addr)
|
||||
{
|
||||
u16 i = 0;
|
||||
/* wait till addr content is zeroed */
|
||||
|
||||
while (readw(addr)) {
|
||||
udelay(1000);
|
||||
|
||||
if (++i > 50)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read absolute adapter memory.
|
||||
* Transfer data from adapter's memory to data buffer. */
|
||||
EXPORT_SYMBOL(cycx_peek);
|
||||
int cycx_peek(struct cycx_hw *hw, u32 addr, void *buf, u32 len)
|
||||
{
|
||||
if (len == 1)
|
||||
*(u8*)buf = readb(hw->dpmbase + addr);
|
||||
else
|
||||
memcpy_fromio(buf, hw->dpmbase + addr, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write Absolute Adapter Memory.
|
||||
* Transfer data from data buffer to adapter's memory. */
|
||||
EXPORT_SYMBOL(cycx_poke);
|
||||
int cycx_poke(struct cycx_hw *hw, u32 addr, void *buf, u32 len)
|
||||
{
|
||||
if (len == 1)
|
||||
writeb(*(u8*)buf, hw->dpmbase + addr);
|
||||
else
|
||||
memcpy_toio(hw->dpmbase + addr, buf, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Hardware-Specific Functions */
|
||||
|
||||
/* Load Aux Routines */
|
||||
/* Reset board hardware.
|
||||
return 1 if memory exists at addr and 0 if not. */
|
||||
static int memory_exists(void __iomem *addr)
|
||||
{
|
||||
int tries = 0;
|
||||
|
||||
for (; tries < 3 ; tries++) {
|
||||
writew(TEST_PATTERN, addr + 0x10);
|
||||
|
||||
if (readw(addr + 0x10) == TEST_PATTERN)
|
||||
if (readw(addr + 0x10) == TEST_PATTERN)
|
||||
return 1;
|
||||
|
||||
msleep_interruptible(1 * 1000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Load reset code. */
|
||||
static void reset_load(void __iomem *addr, u8 *buffer, u32 cnt)
|
||||
{
|
||||
void __iomem *pt_code = addr + RESET_OFFSET;
|
||||
u16 i; /*, j; */
|
||||
|
||||
for (i = 0 ; i < cnt ; i++) {
|
||||
/* for (j = 0 ; j < 50 ; j++); Delay - FIXME busy waiting... */
|
||||
writeb(*buffer++, pt_code++);
|
||||
}
|
||||
}
|
||||
|
||||
/* Load buffer using boot interface.
|
||||
* o copy data from buffer to Cyclom-X memory
|
||||
* o wait for reset code to copy it to right portion of memory */
|
||||
static int buffer_load(void __iomem *addr, u8 *buffer, u32 cnt)
|
||||
{
|
||||
memcpy_toio(addr + DATA_OFFSET, buffer, cnt);
|
||||
writew(GEN_BOOT_DAT, addr + CMD_OFFSET);
|
||||
|
||||
return wait_cyc(addr);
|
||||
}
|
||||
|
||||
/* Set up entry point and kick start Cyclom-X CPU. */
|
||||
static void cycx_start(void __iomem *addr)
|
||||
{
|
||||
/* put in 0x30 offset the jump instruction to the code entry point */
|
||||
writeb(0xea, addr + 0x30);
|
||||
writeb(0x00, addr + 0x31);
|
||||
writeb(0xc4, addr + 0x32);
|
||||
writeb(0x00, addr + 0x33);
|
||||
writeb(0x00, addr + 0x34);
|
||||
|
||||
/* cmd to start executing code */
|
||||
writew(GEN_START, addr + CMD_OFFSET);
|
||||
}
|
||||
|
||||
/* Load and boot reset code. */
|
||||
static void cycx_reset_boot(void __iomem *addr, u8 *code, u32 len)
|
||||
{
|
||||
void __iomem *pt_start = addr + START_OFFSET;
|
||||
|
||||
writeb(0xea, pt_start++); /* jmp to f000:3f00 */
|
||||
writeb(0x00, pt_start++);
|
||||
writeb(0xfc, pt_start++);
|
||||
writeb(0x00, pt_start++);
|
||||
writeb(0xf0, pt_start);
|
||||
reset_load(addr, code, len);
|
||||
|
||||
/* 80186 was in hold, go */
|
||||
writeb(0, addr + START_CPU);
|
||||
msleep_interruptible(1 * 1000);
|
||||
}
|
||||
|
||||
/* Load data.bin file through boot (reset) interface. */
|
||||
static int cycx_data_boot(void __iomem *addr, u8 *code, u32 len)
|
||||
{
|
||||
void __iomem *pt_boot_cmd = addr + CMD_OFFSET;
|
||||
u32 i;
|
||||
|
||||
/* boot buffer lenght */
|
||||
writew(CFM_LOAD_BUFSZ, pt_boot_cmd + sizeof(u16));
|
||||
writew(GEN_DEFPAR, pt_boot_cmd);
|
||||
|
||||
if (wait_cyc(addr) < 0)
|
||||
return -1;
|
||||
|
||||
writew(0, pt_boot_cmd + sizeof(u16));
|
||||
writew(0x4000, pt_boot_cmd + 2 * sizeof(u16));
|
||||
writew(GEN_SET_SEG, pt_boot_cmd);
|
||||
|
||||
if (wait_cyc(addr) < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ)
|
||||
if (buffer_load(addr, code + i,
|
||||
min_t(u32, CFM_LOAD_BUFSZ, (len - i))) < 0) {
|
||||
printk(KERN_ERR "%s: Error !!\n", modname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Load code.bin file through boot (reset) interface. */
|
||||
static int cycx_code_boot(void __iomem *addr, u8 *code, u32 len)
|
||||
{
|
||||
void __iomem *pt_boot_cmd = addr + CMD_OFFSET;
|
||||
u32 i;
|
||||
|
||||
/* boot buffer lenght */
|
||||
writew(CFM_LOAD_BUFSZ, pt_boot_cmd + sizeof(u16));
|
||||
writew(GEN_DEFPAR, pt_boot_cmd);
|
||||
|
||||
if (wait_cyc(addr) < 0)
|
||||
return -1;
|
||||
|
||||
writew(0x0000, pt_boot_cmd + sizeof(u16));
|
||||
writew(0xc400, pt_boot_cmd + 2 * sizeof(u16));
|
||||
writew(GEN_SET_SEG, pt_boot_cmd);
|
||||
|
||||
if (wait_cyc(addr) < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ)
|
||||
if (buffer_load(addr, code + i,
|
||||
min_t(u32, CFM_LOAD_BUFSZ, (len - i)))) {
|
||||
printk(KERN_ERR "%s: Error !!\n", modname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Load adapter from the memory image of the CYCX firmware module.
|
||||
* o verify firmware integrity and compatibility
|
||||
* o start adapter up */
|
||||
static int load_cyc2x(struct cycx_hw *hw, struct cycx_firmware *cfm, u32 len)
|
||||
{
|
||||
int i, j;
|
||||
struct cycx_fw_header *img_hdr;
|
||||
u8 *reset_image,
|
||||
*data_image,
|
||||
*code_image;
|
||||
void __iomem *pt_cycld = hw->dpmbase + 0x400;
|
||||
u16 cksum;
|
||||
|
||||
/* Announce */
|
||||
printk(KERN_INFO "%s: firmware signature=\"%s\"\n", modname,
|
||||
cfm->signature);
|
||||
|
||||
/* Verify firmware signature */
|
||||
if (strcmp(cfm->signature, CFM_SIGNATURE)) {
|
||||
printk(KERN_ERR "%s:load_cyc2x: not Cyclom-2X firmware!\n",
|
||||
modname);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s: firmware version=%u\n", modname, cfm->version);
|
||||
|
||||
/* Verify firmware module format version */
|
||||
if (cfm->version != CFM_VERSION) {
|
||||
printk(KERN_ERR "%s:%s: firmware format %u rejected! "
|
||||
"Expecting %u.\n",
|
||||
modname, __FUNCTION__, cfm->version, CFM_VERSION);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Verify firmware module length and checksum */
|
||||
cksum = checksum((u8*)&cfm->info, sizeof(struct cycx_fw_info) +
|
||||
cfm->info.codesize);
|
||||
/*
|
||||
FIXME cfm->info.codesize is off by 2
|
||||
if (((len - sizeof(struct cycx_firmware) - 1) != cfm->info.codesize) ||
|
||||
*/
|
||||
if (cksum != cfm->checksum) {
|
||||
printk(KERN_ERR "%s:%s: firmware corrupted!\n",
|
||||
modname, __FUNCTION__);
|
||||
printk(KERN_ERR " cdsize = 0x%x (expected 0x%lx)\n",
|
||||
len - (int)sizeof(struct cycx_firmware) - 1,
|
||||
cfm->info.codesize);
|
||||
printk(KERN_ERR " chksum = 0x%x (expected 0x%x)\n",
|
||||
cksum, cfm->checksum);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* If everything is ok, set reset, data and code pointers */
|
||||
img_hdr = (struct cycx_fw_header *)&cfm->image;
|
||||
#ifdef FIRMWARE_DEBUG
|
||||
printk(KERN_INFO "%s:%s: image sizes\n", __FUNCTION__, modname);
|
||||
printk(KERN_INFO " reset=%lu\n", img_hdr->reset_size);
|
||||
printk(KERN_INFO " data=%lu\n", img_hdr->data_size);
|
||||
printk(KERN_INFO " code=%lu\n", img_hdr->code_size);
|
||||
#endif
|
||||
reset_image = ((u8 *)img_hdr) + sizeof(struct cycx_fw_header);
|
||||
data_image = reset_image + img_hdr->reset_size;
|
||||
code_image = data_image + img_hdr->data_size;
|
||||
|
||||
/*---- Start load ----*/
|
||||
/* Announce */
|
||||
printk(KERN_INFO "%s: loading firmware %s (ID=%u)...\n", modname,
|
||||
cfm->descr[0] ? cfm->descr : "unknown firmware",
|
||||
cfm->info.codeid);
|
||||
|
||||
for (i = 0 ; i < 5 ; i++) {
|
||||
/* Reset Cyclom hardware */
|
||||
if (!reset_cyc2x(hw->dpmbase)) {
|
||||
printk(KERN_ERR "%s: dpm problem or board not found\n",
|
||||
modname);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Load reset.bin */
|
||||
cycx_reset_boot(hw->dpmbase, reset_image, img_hdr->reset_size);
|
||||
/* reset is waiting for boot */
|
||||
writew(GEN_POWER_ON, pt_cycld);
|
||||
msleep_interruptible(1 * 1000);
|
||||
|
||||
for (j = 0 ; j < 3 ; j++)
|
||||
if (!readw(pt_cycld))
|
||||
goto reset_loaded;
|
||||
else
|
||||
msleep_interruptible(1 * 1000);
|
||||
}
|
||||
|
||||
printk(KERN_ERR "%s: reset not started.\n", modname);
|
||||
return -EINVAL;
|
||||
|
||||
reset_loaded:
|
||||
/* Load data.bin */
|
||||
if (cycx_data_boot(hw->dpmbase, data_image, img_hdr->data_size)) {
|
||||
printk(KERN_ERR "%s: cannot load data file.\n", modname);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Load code.bin */
|
||||
if (cycx_code_boot(hw->dpmbase, code_image, img_hdr->code_size)) {
|
||||
printk(KERN_ERR "%s: cannot load code file.\n", modname);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Prepare boot-time configuration data */
|
||||
cycx_bootcfg(hw);
|
||||
|
||||
/* kick-off CPU */
|
||||
cycx_start(hw->dpmbase);
|
||||
|
||||
/* Arthur Ganzert's tip: wait a while after the firmware loading...
|
||||
seg abr 26 17:17:12 EST 1999 - acme */
|
||||
msleep_interruptible(7 * 1000);
|
||||
printk(KERN_INFO "%s: firmware loaded!\n", modname);
|
||||
|
||||
/* enable interrupts */
|
||||
cycx_inten(hw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prepare boot-time firmware configuration data.
|
||||
* o initialize configuration data area
|
||||
From async.doc - V_3.4.0 - 07/18/1994
|
||||
- As of now, only static buffers are available to the user.
|
||||
So, the bit VD_RXDIRC must be set in 'valid'. That means that user
|
||||
wants to use the static transmission and reception buffers. */
|
||||
static void cycx_bootcfg(struct cycx_hw *hw)
|
||||
{
|
||||
/* use fixed buffers */
|
||||
writeb(FIXED_BUFFERS, hw->dpmbase + CONF_OFFSET);
|
||||
}
|
||||
|
||||
/* Detect Cyclom 2x adapter.
|
||||
* Following tests are used to detect Cyclom 2x adapter:
|
||||
* to be completed based on the tests done below
|
||||
* Return 1 if detected o.k. or 0 if failed.
|
||||
* Note: This test is destructive! Adapter will be left in shutdown
|
||||
* state after the test. */
|
||||
static int detect_cyc2x(void __iomem *addr)
|
||||
{
|
||||
reset_cyc2x(addr);
|
||||
|
||||
return memory_exists(addr);
|
||||
}
|
||||
|
||||
/* Miscellaneous */
|
||||
/* Get option's index into the options list.
|
||||
* Return option's index (1 .. N) or zero if option is invalid. */
|
||||
static int get_option_index(long *optlist, long optval)
|
||||
{
|
||||
int i = 1;
|
||||
|
||||
for (; i <= optlist[0]; ++i)
|
||||
if (optlist[i] == optval)
|
||||
return i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reset adapter's CPU. */
|
||||
static int reset_cyc2x(void __iomem *addr)
|
||||
{
|
||||
writeb(0, addr + RST_ENABLE);
|
||||
msleep_interruptible(2 * 1000);
|
||||
writeb(0, addr + RST_DISABLE);
|
||||
msleep_interruptible(2 * 1000);
|
||||
|
||||
return memory_exists(addr);
|
||||
}
|
||||
|
||||
/* Calculate 16-bit CRC using CCITT polynomial. */
|
||||
static u16 checksum(u8 *buf, u32 len)
|
||||
{
|
||||
u16 crc = 0;
|
||||
u16 mask, flag;
|
||||
|
||||
for (; len; --len, ++buf)
|
||||
for (mask = 0x80; mask; mask >>= 1) {
|
||||
flag = (crc & 0x8000);
|
||||
crc <<= 1;
|
||||
crc |= ((*buf & mask) ? 1 : 0);
|
||||
|
||||
if (flag)
|
||||
crc ^= 0x1021;
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
module_init(cycx_drv_init);
|
||||
module_exit(cycx_drv_cleanup);
|
||||
|
||||
/* End */
|
||||
350
drivers/net/wan/cycx_main.c
Normal file
350
drivers/net/wan/cycx_main.c
Normal file
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* cycx_main.c Cyclades Cyclom 2X WAN Link Driver. Main module.
|
||||
*
|
||||
* Author: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
|
||||
*
|
||||
* Copyright: (c) 1998-2003 Arnaldo Carvalho de Melo
|
||||
*
|
||||
* Based on sdlamain.c by Gene Kozin <genek@compuserve.com> &
|
||||
* Jaspreet Singh <jaspreet@sangoma.com>
|
||||
*
|
||||
* 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.
|
||||
* ============================================================================
|
||||
* Please look at the bitkeeper changelog (or any other scm tool that ends up
|
||||
* importing bitkeeper changelog or that replaces bitkeeper in the future as
|
||||
* main tool for linux development).
|
||||
*
|
||||
* 2001/05/09 acme Fix MODULE_DESC for debug, .bss nitpicks,
|
||||
* some cleanups
|
||||
* 2000/07/13 acme remove useless #ifdef MODULE and crap
|
||||
* #if KERNEL_VERSION > blah
|
||||
* 2000/07/06 acme __exit at cyclomx_cleanup
|
||||
* 2000/04/02 acme dprintk and cycx_debug
|
||||
* module_init/module_exit
|
||||
* 2000/01/21 acme rename cyclomx_open to cyclomx_mod_inc_use_count
|
||||
* and cyclomx_close to cyclomx_mod_dec_use_count
|
||||
* 2000/01/08 acme cleanup
|
||||
* 1999/11/06 acme cycx_down back to life (it needs to be
|
||||
* called to iounmap the dpmbase)
|
||||
* 1999/08/09 acme removed references to enable_tx_int
|
||||
* use spinlocks instead of cli/sti in
|
||||
* cyclomx_set_state
|
||||
* 1999/05/19 acme works directly linked into the kernel
|
||||
* init_waitqueue_head for 2.3.* kernel
|
||||
* 1999/05/18 acme major cleanup (polling not needed), etc
|
||||
* 1998/08/28 acme minor cleanup (ioctls for firmware deleted)
|
||||
* queue_task activated
|
||||
* 1998/08/08 acme Initial version.
|
||||
*/
|
||||
|
||||
#include <linux/stddef.h> /* offsetof(), etc. */
|
||||
#include <linux/errno.h> /* return codes */
|
||||
#include <linux/string.h> /* inline memset(), etc. */
|
||||
#include <linux/slab.h> /* kmalloc(), kfree() */
|
||||
#include <linux/kernel.h> /* printk(), and other useful stuff */
|
||||
#include <linux/module.h> /* support for loadable modules */
|
||||
#include <linux/ioport.h> /* request_region(), release_region() */
|
||||
#include <linux/wanrouter.h> /* WAN router definitions */
|
||||
#include <linux/cyclomx.h> /* cyclomx common user API definitions */
|
||||
#include <linux/init.h> /* __init (when not using as a module) */
|
||||
|
||||
unsigned int cycx_debug;
|
||||
|
||||
MODULE_AUTHOR("Arnaldo Carvalho de Melo");
|
||||
MODULE_DESCRIPTION("Cyclom 2X Sync Card Driver.");
|
||||
MODULE_LICENSE("GPL");
|
||||
module_param(cycx_debug, int, 0);
|
||||
MODULE_PARM_DESC(cycx_debug, "cyclomx debug level");
|
||||
|
||||
/* Defines & Macros */
|
||||
|
||||
#define CYCX_DRV_VERSION 0 /* version number */
|
||||
#define CYCX_DRV_RELEASE 11 /* release (minor version) number */
|
||||
#define CYCX_MAX_CARDS 1 /* max number of adapters */
|
||||
|
||||
#define CONFIG_CYCX_CARDS 1
|
||||
|
||||
/* Function Prototypes */
|
||||
|
||||
/* WAN link driver entry points */
|
||||
static int cycx_wan_setup(struct wan_device *wandev, wandev_conf_t *conf);
|
||||
static int cycx_wan_shutdown(struct wan_device *wandev);
|
||||
|
||||
/* Miscellaneous functions */
|
||||
static irqreturn_t cycx_isr(int irq, void *dev_id);
|
||||
|
||||
/* Global Data
|
||||
* Note: All data must be explicitly initialized!!!
|
||||
*/
|
||||
|
||||
/* private data */
|
||||
static char cycx_drvname[] = "cyclomx";
|
||||
static char cycx_fullname[] = "CYCLOM 2X(tm) Sync Card Driver";
|
||||
static char cycx_copyright[] = "(c) 1998-2003 Arnaldo Carvalho de Melo "
|
||||
"<acme@conectiva.com.br>";
|
||||
static int cycx_ncards = CONFIG_CYCX_CARDS;
|
||||
static struct cycx_device *cycx_card_array; /* adapter data space */
|
||||
|
||||
/* Kernel Loadable Module Entry Points */
|
||||
|
||||
/*
|
||||
* Module 'insert' entry point.
|
||||
* o print announcement
|
||||
* o allocate adapter data space
|
||||
* o initialize static data
|
||||
* o register all cards with WAN router
|
||||
* o calibrate Cyclom 2X shared memory access delay.
|
||||
*
|
||||
* Return: 0 Ok
|
||||
* < 0 error.
|
||||
* Context: process
|
||||
*/
|
||||
static int __init cycx_init(void)
|
||||
{
|
||||
int cnt, err = -ENOMEM;
|
||||
|
||||
printk(KERN_INFO "%s v%u.%u %s\n",
|
||||
cycx_fullname, CYCX_DRV_VERSION, CYCX_DRV_RELEASE,
|
||||
cycx_copyright);
|
||||
|
||||
/* Verify number of cards and allocate adapter data space */
|
||||
cycx_ncards = min_t(int, cycx_ncards, CYCX_MAX_CARDS);
|
||||
cycx_ncards = max_t(int, cycx_ncards, 1);
|
||||
cycx_card_array = kmalloc(sizeof(struct cycx_device) * cycx_ncards,
|
||||
GFP_KERNEL);
|
||||
if (!cycx_card_array)
|
||||
goto out;
|
||||
|
||||
memset(cycx_card_array, 0, sizeof(struct cycx_device) * cycx_ncards);
|
||||
|
||||
/* Register adapters with WAN router */
|
||||
for (cnt = 0; cnt < cycx_ncards; ++cnt) {
|
||||
struct cycx_device *card = &cycx_card_array[cnt];
|
||||
struct wan_device *wandev = &card->wandev;
|
||||
|
||||
sprintf(card->devname, "%s%d", cycx_drvname, cnt + 1);
|
||||
wandev->magic = ROUTER_MAGIC;
|
||||
wandev->name = card->devname;
|
||||
wandev->private = card;
|
||||
wandev->setup = cycx_wan_setup;
|
||||
wandev->shutdown = cycx_wan_shutdown;
|
||||
err = register_wan_device(wandev);
|
||||
|
||||
if (err) {
|
||||
printk(KERN_ERR "%s: %s registration failed with "
|
||||
"error %d!\n",
|
||||
cycx_drvname, card->devname, err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
err = -ENODEV;
|
||||
if (!cnt) {
|
||||
kfree(cycx_card_array);
|
||||
goto out;
|
||||
}
|
||||
err = 0;
|
||||
cycx_ncards = cnt; /* adjust actual number of cards */
|
||||
out: return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module 'remove' entry point.
|
||||
* o unregister all adapters from the WAN router
|
||||
* o release all remaining system resources
|
||||
*/
|
||||
static void __exit cycx_exit(void)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (; i < cycx_ncards; ++i) {
|
||||
struct cycx_device *card = &cycx_card_array[i];
|
||||
unregister_wan_device(card->devname);
|
||||
}
|
||||
|
||||
kfree(cycx_card_array);
|
||||
}
|
||||
|
||||
/* WAN Device Driver Entry Points */
|
||||
/*
|
||||
* Setup/configure WAN link driver.
|
||||
* o check adapter state
|
||||
* o make sure firmware is present in configuration
|
||||
* o allocate interrupt vector
|
||||
* o setup Cyclom 2X hardware
|
||||
* o call appropriate routine to perform protocol-specific initialization
|
||||
*
|
||||
* This function is called when router handles ROUTER_SETUP IOCTL. The
|
||||
* configuration structure is in kernel memory (including extended data, if
|
||||
* any).
|
||||
*/
|
||||
static int cycx_wan_setup(struct wan_device *wandev, wandev_conf_t *conf)
|
||||
{
|
||||
int rc = -EFAULT;
|
||||
struct cycx_device *card;
|
||||
int irq;
|
||||
|
||||
/* Sanity checks */
|
||||
|
||||
if (!wandev || !wandev->private || !conf)
|
||||
goto out;
|
||||
|
||||
card = wandev->private;
|
||||
rc = -EBUSY;
|
||||
if (wandev->state != WAN_UNCONFIGURED)
|
||||
goto out;
|
||||
|
||||
rc = -EINVAL;
|
||||
if (!conf->data_size || !conf->data) {
|
||||
printk(KERN_ERR "%s: firmware not found in configuration "
|
||||
"data!\n", wandev->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (conf->irq <= 0) {
|
||||
printk(KERN_ERR "%s: can't configure without IRQ!\n",
|
||||
wandev->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Allocate IRQ */
|
||||
irq = conf->irq == 2 ? 9 : conf->irq; /* IRQ2 -> IRQ9 */
|
||||
|
||||
if (request_irq(irq, cycx_isr, 0, wandev->name, card)) {
|
||||
printk(KERN_ERR "%s: can't reserve IRQ %d!\n",
|
||||
wandev->name, irq);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Configure hardware, load firmware, etc. */
|
||||
memset(&card->hw, 0, sizeof(card->hw));
|
||||
card->hw.irq = irq;
|
||||
card->hw.dpmsize = CYCX_WINDOWSIZE;
|
||||
card->hw.fwid = CFID_X25_2X;
|
||||
spin_lock_init(&card->lock);
|
||||
init_waitqueue_head(&card->wait_stats);
|
||||
|
||||
rc = cycx_setup(&card->hw, conf->data, conf->data_size, conf->maddr);
|
||||
if (rc)
|
||||
goto out_irq;
|
||||
|
||||
/* Initialize WAN device data space */
|
||||
wandev->irq = irq;
|
||||
wandev->dma = wandev->ioport = 0;
|
||||
wandev->maddr = (unsigned long)card->hw.dpmbase;
|
||||
wandev->msize = card->hw.dpmsize;
|
||||
wandev->hw_opt[2] = 0;
|
||||
wandev->hw_opt[3] = card->hw.fwid;
|
||||
|
||||
/* Protocol-specific initialization */
|
||||
switch (card->hw.fwid) {
|
||||
#ifdef CONFIG_CYCLOMX_X25
|
||||
case CFID_X25_2X:
|
||||
rc = cycx_x25_wan_init(card, conf);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
printk(KERN_ERR "%s: this firmware is not supported!\n",
|
||||
wandev->name);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
cycx_down(&card->hw);
|
||||
goto out_irq;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
out:
|
||||
return rc;
|
||||
out_irq:
|
||||
free_irq(irq, card);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shut down WAN link driver.
|
||||
* o shut down adapter hardware
|
||||
* o release system resources.
|
||||
*
|
||||
* This function is called by the router when device is being unregistered or
|
||||
* when it handles ROUTER_DOWN IOCTL.
|
||||
*/
|
||||
static int cycx_wan_shutdown(struct wan_device *wandev)
|
||||
{
|
||||
int ret = -EFAULT;
|
||||
struct cycx_device *card;
|
||||
|
||||
/* sanity checks */
|
||||
if (!wandev || !wandev->private)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
if (wandev->state == WAN_UNCONFIGURED)
|
||||
goto out;
|
||||
|
||||
card = wandev->private;
|
||||
wandev->state = WAN_UNCONFIGURED;
|
||||
cycx_down(&card->hw);
|
||||
printk(KERN_INFO "%s: irq %d being freed!\n", wandev->name,
|
||||
wandev->irq);
|
||||
free_irq(wandev->irq, card);
|
||||
out: return ret;
|
||||
}
|
||||
|
||||
/* Miscellaneous */
|
||||
/*
|
||||
* Cyclom 2X Interrupt Service Routine.
|
||||
* o acknowledge Cyclom 2X hardware interrupt.
|
||||
* o call protocol-specific interrupt service routine, if any.
|
||||
*/
|
||||
static irqreturn_t cycx_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct cycx_device *card = dev_id;
|
||||
|
||||
if (card->wandev.state == WAN_UNCONFIGURED)
|
||||
goto out;
|
||||
|
||||
if (card->in_isr) {
|
||||
printk(KERN_WARNING "%s: interrupt re-entrancy on IRQ %d!\n",
|
||||
card->devname, card->wandev.irq);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (card->isr)
|
||||
card->isr(card);
|
||||
return IRQ_HANDLED;
|
||||
out:
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Set WAN device state. */
|
||||
void cycx_set_state(struct cycx_device *card, int state)
|
||||
{
|
||||
unsigned long flags;
|
||||
char *string_state = NULL;
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
|
||||
if (card->wandev.state != state) {
|
||||
switch (state) {
|
||||
case WAN_CONNECTED:
|
||||
string_state = "connected!";
|
||||
break;
|
||||
case WAN_DISCONNECTED:
|
||||
string_state = "disconnected!";
|
||||
break;
|
||||
}
|
||||
printk(KERN_INFO "%s: link %s\n", card->devname, string_state);
|
||||
card->wandev.state = state;
|
||||
}
|
||||
|
||||
card->state_tick = jiffies;
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
}
|
||||
|
||||
module_init(cycx_init);
|
||||
module_exit(cycx_exit);
|
||||
1606
drivers/net/wan/cycx_x25.c
Normal file
1606
drivers/net/wan/cycx_x25.c
Normal file
File diff suppressed because it is too large
Load Diff
565
drivers/net/wan/dlci.c
Normal file
565
drivers/net/wan/dlci.c
Normal file
@@ -0,0 +1,565 @@
|
||||
/*
|
||||
* DLCI Implementation of Frame Relay protocol for Linux, according to
|
||||
* RFC 1490. This generic device provides en/decapsulation for an
|
||||
* underlying hardware driver. Routes & IPs are assigned to these
|
||||
* interfaces. Requires 'dlcicfg' program to create usable
|
||||
* interfaces, the initial one, 'dlci' is for IOCTL use only.
|
||||
*
|
||||
* Version: @(#)dlci.c 0.35 4 Jan 1997
|
||||
*
|
||||
* Author: Mike McLagan <mike.mclagan@linux.org>
|
||||
*
|
||||
* Changes:
|
||||
*
|
||||
* 0.15 Mike Mclagan Packet freeing, bug in kmalloc call
|
||||
* DLCI_RET handling
|
||||
* 0.20 Mike McLagan More conservative on which packets
|
||||
* are returned for retry and which are
|
||||
* are dropped. If DLCI_RET_DROP is
|
||||
* returned from the FRAD, the packet is
|
||||
* sent back to Linux for re-transmission
|
||||
* 0.25 Mike McLagan Converted to use SIOC IOCTL calls
|
||||
* 0.30 Jim Freeman Fixed to allow IPX traffic
|
||||
* 0.35 Michael Elizabeth Fixed incorrect memcpy_fromfs
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_frad.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
static const char version[] = "DLCI driver v0.35, 4 Jan 1997, mike.mclagan@linux.org";
|
||||
|
||||
static LIST_HEAD(dlci_devs);
|
||||
|
||||
static void dlci_setup(struct net_device *);
|
||||
|
||||
/*
|
||||
* these encapsulate the RFC 1490 requirements as well as
|
||||
* deal with packet transmission and reception, working with
|
||||
* the upper network layers
|
||||
*/
|
||||
|
||||
static int dlci_header(struct sk_buff *skb, struct net_device *dev,
|
||||
unsigned short type, void *daddr, void *saddr,
|
||||
unsigned len)
|
||||
{
|
||||
struct frhdr hdr;
|
||||
struct dlci_local *dlp;
|
||||
unsigned int hlen;
|
||||
char *dest;
|
||||
|
||||
dlp = dev->priv;
|
||||
|
||||
hdr.control = FRAD_I_UI;
|
||||
switch(type)
|
||||
{
|
||||
case ETH_P_IP:
|
||||
hdr.IP_NLPID = FRAD_P_IP;
|
||||
hlen = sizeof(hdr.control) + sizeof(hdr.IP_NLPID);
|
||||
break;
|
||||
|
||||
/* feel free to add other types, if necessary */
|
||||
|
||||
default:
|
||||
hdr.pad = FRAD_P_PADDING;
|
||||
hdr.NLPID = FRAD_P_SNAP;
|
||||
memset(hdr.OUI, 0, sizeof(hdr.OUI));
|
||||
hdr.PID = htons(type);
|
||||
hlen = sizeof(hdr);
|
||||
break;
|
||||
}
|
||||
|
||||
dest = skb_push(skb, hlen);
|
||||
if (!dest)
|
||||
return(0);
|
||||
|
||||
memcpy(dest, &hdr, hlen);
|
||||
|
||||
return(hlen);
|
||||
}
|
||||
|
||||
static void dlci_receive(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct dlci_local *dlp;
|
||||
struct frhdr *hdr;
|
||||
int process, header;
|
||||
|
||||
dlp = dev->priv;
|
||||
if (!pskb_may_pull(skb, sizeof(*hdr))) {
|
||||
printk(KERN_NOTICE "%s: invalid data no header\n",
|
||||
dev->name);
|
||||
dlp->stats.rx_errors++;
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
hdr = (struct frhdr *) skb->data;
|
||||
process = 0;
|
||||
header = 0;
|
||||
skb->dev = dev;
|
||||
|
||||
if (hdr->control != FRAD_I_UI)
|
||||
{
|
||||
printk(KERN_NOTICE "%s: Invalid header flag 0x%02X.\n", dev->name, hdr->control);
|
||||
dlp->stats.rx_errors++;
|
||||
}
|
||||
else
|
||||
switch(hdr->IP_NLPID)
|
||||
{
|
||||
case FRAD_P_PADDING:
|
||||
if (hdr->NLPID != FRAD_P_SNAP)
|
||||
{
|
||||
printk(KERN_NOTICE "%s: Unsupported NLPID 0x%02X.\n", dev->name, hdr->NLPID);
|
||||
dlp->stats.rx_errors++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hdr->OUI[0] + hdr->OUI[1] + hdr->OUI[2] != 0)
|
||||
{
|
||||
printk(KERN_NOTICE "%s: Unsupported organizationally unique identifier 0x%02X-%02X-%02X.\n", dev->name, hdr->OUI[0], hdr->OUI[1], hdr->OUI[2]);
|
||||
dlp->stats.rx_errors++;
|
||||
break;
|
||||
}
|
||||
|
||||
/* at this point, it's an EtherType frame */
|
||||
header = sizeof(struct frhdr);
|
||||
/* Already in network order ! */
|
||||
skb->protocol = hdr->PID;
|
||||
process = 1;
|
||||
break;
|
||||
|
||||
case FRAD_P_IP:
|
||||
header = sizeof(hdr->control) + sizeof(hdr->IP_NLPID);
|
||||
skb->protocol = htons(ETH_P_IP);
|
||||
process = 1;
|
||||
break;
|
||||
|
||||
case FRAD_P_SNAP:
|
||||
case FRAD_P_Q933:
|
||||
case FRAD_P_CLNP:
|
||||
printk(KERN_NOTICE "%s: Unsupported NLPID 0x%02X.\n", dev->name, hdr->pad);
|
||||
dlp->stats.rx_errors++;
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_NOTICE "%s: Invalid pad byte 0x%02X.\n", dev->name, hdr->pad);
|
||||
dlp->stats.rx_errors++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (process)
|
||||
{
|
||||
/* we've set up the protocol, so discard the header */
|
||||
skb->mac.raw = skb->data;
|
||||
skb_pull(skb, header);
|
||||
dlp->stats.rx_bytes += skb->len;
|
||||
netif_rx(skb);
|
||||
dlp->stats.rx_packets++;
|
||||
dev->last_rx = jiffies;
|
||||
}
|
||||
else
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
static int dlci_transmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct dlci_local *dlp;
|
||||
int ret;
|
||||
|
||||
ret = 0;
|
||||
|
||||
if (!skb || !dev)
|
||||
return(0);
|
||||
|
||||
dlp = dev->priv;
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
ret = dlp->slave->hard_start_xmit(skb, dlp->slave);
|
||||
switch (ret)
|
||||
{
|
||||
case DLCI_RET_OK:
|
||||
dlp->stats.tx_packets++;
|
||||
ret = 0;
|
||||
break;
|
||||
case DLCI_RET_ERR:
|
||||
dlp->stats.tx_errors++;
|
||||
ret = 0;
|
||||
break;
|
||||
case DLCI_RET_DROP:
|
||||
dlp->stats.tx_dropped++;
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
/* Alan Cox recommends always returning 0, and always freeing the packet */
|
||||
/* experience suggest a slightly more conservative approach */
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
dev_kfree_skb(skb);
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static int dlci_config(struct net_device *dev, struct dlci_conf __user *conf, int get)
|
||||
{
|
||||
struct dlci_conf config;
|
||||
struct dlci_local *dlp;
|
||||
struct frad_local *flp;
|
||||
int err;
|
||||
|
||||
dlp = dev->priv;
|
||||
|
||||
flp = dlp->slave->priv;
|
||||
|
||||
if (!get)
|
||||
{
|
||||
if(copy_from_user(&config, conf, sizeof(struct dlci_conf)))
|
||||
return -EFAULT;
|
||||
if (config.flags & ~DLCI_VALID_FLAGS)
|
||||
return(-EINVAL);
|
||||
memcpy(&dlp->config, &config, sizeof(struct dlci_conf));
|
||||
dlp->configured = 1;
|
||||
}
|
||||
|
||||
err = (*flp->dlci_conf)(dlp->slave, dev, get);
|
||||
if (err)
|
||||
return(err);
|
||||
|
||||
if (get)
|
||||
{
|
||||
if(copy_to_user(conf, &dlp->config, sizeof(struct dlci_conf)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int dlci_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
struct dlci_local *dlp;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return(-EPERM);
|
||||
|
||||
dlp = dev->priv;
|
||||
|
||||
switch(cmd)
|
||||
{
|
||||
case DLCI_GET_SLAVE:
|
||||
if (!*(short *)(dev->dev_addr))
|
||||
return(-EINVAL);
|
||||
|
||||
strncpy(ifr->ifr_slave, dlp->slave->name, sizeof(ifr->ifr_slave));
|
||||
break;
|
||||
|
||||
case DLCI_GET_CONF:
|
||||
case DLCI_SET_CONF:
|
||||
if (!*(short *)(dev->dev_addr))
|
||||
return(-EINVAL);
|
||||
|
||||
return(dlci_config(dev, ifr->ifr_data, cmd == DLCI_GET_CONF));
|
||||
break;
|
||||
|
||||
default:
|
||||
return(-EOPNOTSUPP);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int dlci_change_mtu(struct net_device *dev, int new_mtu)
|
||||
{
|
||||
struct dlci_local *dlp;
|
||||
|
||||
dlp = dev->priv;
|
||||
|
||||
return((*dlp->slave->change_mtu)(dlp->slave, new_mtu));
|
||||
}
|
||||
|
||||
static int dlci_open(struct net_device *dev)
|
||||
{
|
||||
struct dlci_local *dlp;
|
||||
struct frad_local *flp;
|
||||
int err;
|
||||
|
||||
dlp = dev->priv;
|
||||
|
||||
if (!*(short *)(dev->dev_addr))
|
||||
return(-EINVAL);
|
||||
|
||||
if (!netif_running(dlp->slave))
|
||||
return(-ENOTCONN);
|
||||
|
||||
flp = dlp->slave->priv;
|
||||
err = (*flp->activate)(dlp->slave, dev);
|
||||
if (err)
|
||||
return(err);
|
||||
|
||||
netif_start_queue(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dlci_close(struct net_device *dev)
|
||||
{
|
||||
struct dlci_local *dlp;
|
||||
struct frad_local *flp;
|
||||
int err;
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
dlp = dev->priv;
|
||||
|
||||
flp = dlp->slave->priv;
|
||||
err = (*flp->deactivate)(dlp->slave, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct net_device_stats *dlci_get_stats(struct net_device *dev)
|
||||
{
|
||||
struct dlci_local *dlp;
|
||||
|
||||
dlp = dev->priv;
|
||||
|
||||
return(&dlp->stats);
|
||||
}
|
||||
|
||||
static int dlci_add(struct dlci_add *dlci)
|
||||
{
|
||||
struct net_device *master, *slave;
|
||||
struct dlci_local *dlp;
|
||||
struct frad_local *flp;
|
||||
int err = -EINVAL;
|
||||
|
||||
|
||||
/* validate slave device */
|
||||
slave = dev_get_by_name(dlci->devname);
|
||||
if (!slave)
|
||||
return -ENODEV;
|
||||
|
||||
if (slave->type != ARPHRD_FRAD || slave->priv == NULL)
|
||||
goto err1;
|
||||
|
||||
/* create device name */
|
||||
master = alloc_netdev( sizeof(struct dlci_local), "dlci%d",
|
||||
dlci_setup);
|
||||
if (!master) {
|
||||
err = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
/* make sure same slave not already registered */
|
||||
rtnl_lock();
|
||||
list_for_each_entry(dlp, &dlci_devs, list) {
|
||||
if (dlp->slave == slave) {
|
||||
err = -EBUSY;
|
||||
goto err2;
|
||||
}
|
||||
}
|
||||
|
||||
err = dev_alloc_name(master, master->name);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
|
||||
*(short *)(master->dev_addr) = dlci->dlci;
|
||||
|
||||
dlp = (struct dlci_local *) master->priv;
|
||||
dlp->slave = slave;
|
||||
dlp->master = master;
|
||||
|
||||
flp = slave->priv;
|
||||
err = (*flp->assoc)(slave, master);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
|
||||
err = register_netdevice(master);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
|
||||
strcpy(dlci->devname, master->name);
|
||||
|
||||
list_add(&dlp->list, &dlci_devs);
|
||||
rtnl_unlock();
|
||||
|
||||
return(0);
|
||||
|
||||
err2:
|
||||
rtnl_unlock();
|
||||
free_netdev(master);
|
||||
err1:
|
||||
dev_put(slave);
|
||||
return(err);
|
||||
}
|
||||
|
||||
static int dlci_del(struct dlci_add *dlci)
|
||||
{
|
||||
struct dlci_local *dlp;
|
||||
struct frad_local *flp;
|
||||
struct net_device *master, *slave;
|
||||
int err;
|
||||
|
||||
/* validate slave device */
|
||||
master = __dev_get_by_name(dlci->devname);
|
||||
if (!master)
|
||||
return(-ENODEV);
|
||||
|
||||
if (netif_running(master)) {
|
||||
return(-EBUSY);
|
||||
}
|
||||
|
||||
dlp = master->priv;
|
||||
slave = dlp->slave;
|
||||
flp = slave->priv;
|
||||
|
||||
rtnl_lock();
|
||||
err = (*flp->deassoc)(slave, master);
|
||||
if (!err) {
|
||||
list_del(&dlp->list);
|
||||
|
||||
unregister_netdevice(master);
|
||||
|
||||
dev_put(slave);
|
||||
}
|
||||
rtnl_unlock();
|
||||
|
||||
return(err);
|
||||
}
|
||||
|
||||
static int dlci_ioctl(unsigned int cmd, void __user *arg)
|
||||
{
|
||||
struct dlci_add add;
|
||||
int err;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return(-EPERM);
|
||||
|
||||
if(copy_from_user(&add, arg, sizeof(struct dlci_add)))
|
||||
return -EFAULT;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case SIOCADDDLCI:
|
||||
err = dlci_add(&add);
|
||||
|
||||
if (!err)
|
||||
if(copy_to_user(arg, &add, sizeof(struct dlci_add)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
|
||||
case SIOCDELDLCI:
|
||||
err = dlci_del(&add);
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return(err);
|
||||
}
|
||||
|
||||
static void dlci_setup(struct net_device *dev)
|
||||
{
|
||||
struct dlci_local *dlp = dev->priv;
|
||||
|
||||
dev->flags = 0;
|
||||
dev->open = dlci_open;
|
||||
dev->stop = dlci_close;
|
||||
dev->do_ioctl = dlci_dev_ioctl;
|
||||
dev->hard_start_xmit = dlci_transmit;
|
||||
dev->hard_header = dlci_header;
|
||||
dev->get_stats = dlci_get_stats;
|
||||
dev->change_mtu = dlci_change_mtu;
|
||||
dev->destructor = free_netdev;
|
||||
|
||||
dlp->receive = dlci_receive;
|
||||
|
||||
dev->type = ARPHRD_DLCI;
|
||||
dev->hard_header_len = sizeof(struct frhdr);
|
||||
dev->addr_len = sizeof(short);
|
||||
|
||||
}
|
||||
|
||||
/* if slave is unregistering, then cleanup master */
|
||||
static int dlci_dev_event(struct notifier_block *unused,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct net_device *dev = (struct net_device *) ptr;
|
||||
|
||||
if (event == NETDEV_UNREGISTER) {
|
||||
struct dlci_local *dlp;
|
||||
|
||||
list_for_each_entry(dlp, &dlci_devs, list) {
|
||||
if (dlp->slave == dev) {
|
||||
list_del(&dlp->list);
|
||||
unregister_netdevice(dlp->master);
|
||||
dev_put(dlp->slave);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block dlci_notifier = {
|
||||
.notifier_call = dlci_dev_event,
|
||||
};
|
||||
|
||||
static int __init init_dlci(void)
|
||||
{
|
||||
dlci_ioctl_set(dlci_ioctl);
|
||||
register_netdevice_notifier(&dlci_notifier);
|
||||
|
||||
printk("%s.\n", version);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit dlci_exit(void)
|
||||
{
|
||||
struct dlci_local *dlp, *nxt;
|
||||
|
||||
dlci_ioctl_set(NULL);
|
||||
unregister_netdevice_notifier(&dlci_notifier);
|
||||
|
||||
rtnl_lock();
|
||||
list_for_each_entry_safe(dlp, nxt, &dlci_devs, list) {
|
||||
unregister_netdevice(dlp->master);
|
||||
dev_put(dlp->slave);
|
||||
}
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
module_init(init_dlci);
|
||||
module_exit(dlci_exit);
|
||||
|
||||
MODULE_AUTHOR("Mike McLagan");
|
||||
MODULE_DESCRIPTION("Frame Relay DLCI layer");
|
||||
MODULE_LICENSE("GPL");
|
||||
2074
drivers/net/wan/dscc4.c
Normal file
2074
drivers/net/wan/dscc4.c
Normal file
File diff suppressed because it is too large
Load Diff
2711
drivers/net/wan/farsync.c
Normal file
2711
drivers/net/wan/farsync.c
Normal file
File diff suppressed because it is too large
Load Diff
357
drivers/net/wan/farsync.h
Normal file
357
drivers/net/wan/farsync.h
Normal file
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* FarSync X21 driver for Linux
|
||||
*
|
||||
* Actually sync driver for X.21, V.35 and V.24 on FarSync T-series cards
|
||||
*
|
||||
* Copyright (C) 2001 FarSite Communications Ltd.
|
||||
* www.farsite.co.uk
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author: R.J.Dunlop <bob.dunlop@farsite.co.uk>
|
||||
*
|
||||
* For the most part this file only contains structures and information
|
||||
* that is visible to applications outside the driver. Shared memory
|
||||
* layout etc is internal to the driver and described within farsync.c.
|
||||
* Overlap exists in that the values used for some fields within the
|
||||
* ioctl interface extend into the cards firmware interface so values in
|
||||
* this file may not be changed arbitrarily.
|
||||
*/
|
||||
|
||||
/* What's in a name
|
||||
*
|
||||
* The project name for this driver is Oscar. The driver is intended to be
|
||||
* used with the FarSite T-Series cards (T2P & T4P) running in the high
|
||||
* speed frame shifter mode. This is sometimes referred to as X.21 mode
|
||||
* which is a complete misnomer as the card continues to support V.24 and
|
||||
* V.35 as well as X.21.
|
||||
*
|
||||
* A short common prefix is useful for routines within the driver to avoid
|
||||
* conflict with other similar drivers and I chosen to use "fst_" for this
|
||||
* purpose (FarSite T-series).
|
||||
*
|
||||
* Finally the device driver needs a short network interface name. Since
|
||||
* "hdlc" is already in use I've chosen the even less informative "sync"
|
||||
* for the present.
|
||||
*/
|
||||
#define FST_NAME "fst" /* In debug/info etc */
|
||||
#define FST_NDEV_NAME "sync" /* For net interface */
|
||||
#define FST_DEV_NAME "farsync" /* For misc interfaces */
|
||||
|
||||
|
||||
/* User version number
|
||||
*
|
||||
* This version number is incremented with each official release of the
|
||||
* package and is a simplified number for normal user reference.
|
||||
* Individual files are tracked by the version control system and may
|
||||
* have individual versions (or IDs) that move much faster than the
|
||||
* the release version as individual updates are tracked.
|
||||
*/
|
||||
#define FST_USER_VERSION "1.04"
|
||||
|
||||
|
||||
/* Ioctl call command values
|
||||
*
|
||||
* The first three private ioctls are used by the sync-PPP module,
|
||||
* allowing a little room for expansion we start our numbering at 10.
|
||||
*/
|
||||
#define FSTWRITE (SIOCDEVPRIVATE+10)
|
||||
#define FSTCPURESET (SIOCDEVPRIVATE+11)
|
||||
#define FSTCPURELEASE (SIOCDEVPRIVATE+12)
|
||||
#define FSTGETCONF (SIOCDEVPRIVATE+13)
|
||||
#define FSTSETCONF (SIOCDEVPRIVATE+14)
|
||||
|
||||
|
||||
/* FSTWRITE
|
||||
*
|
||||
* Used to write a block of data (firmware etc) before the card is running
|
||||
*/
|
||||
struct fstioc_write {
|
||||
unsigned int size;
|
||||
unsigned int offset;
|
||||
unsigned char data[0];
|
||||
};
|
||||
|
||||
|
||||
/* FSTCPURESET and FSTCPURELEASE
|
||||
*
|
||||
* These take no additional data.
|
||||
* FSTCPURESET forces the cards CPU into a reset state and holds it there.
|
||||
* FSTCPURELEASE releases the CPU from this reset state allowing it to run,
|
||||
* the reset vector should be setup before this ioctl is run.
|
||||
*/
|
||||
|
||||
/* FSTGETCONF and FSTSETCONF
|
||||
*
|
||||
* Get and set a card/ports configuration.
|
||||
* In order to allow selective setting of items and for the kernel to
|
||||
* indicate a partial status response the first field "valid" is a bitmask
|
||||
* indicating which other fields in the structure are valid.
|
||||
* Many of the field names in this structure match those used in the
|
||||
* firmware shared memory configuration interface and come originally from
|
||||
* the NT header file Smc.h
|
||||
*
|
||||
* When used with FSTGETCONF this structure should be zeroed before use.
|
||||
* This is to allow for possible future expansion when some of the fields
|
||||
* might be used to indicate a different (expanded) structure.
|
||||
*/
|
||||
struct fstioc_info {
|
||||
unsigned int valid; /* Bits of structure that are valid */
|
||||
unsigned int nports; /* Number of serial ports */
|
||||
unsigned int type; /* Type index of card */
|
||||
unsigned int state; /* State of card */
|
||||
unsigned int index; /* Index of port ioctl was issued on */
|
||||
unsigned int smcFirmwareVersion;
|
||||
unsigned long kernelVersion; /* What Kernel version we are working with */
|
||||
unsigned short lineInterface; /* Physical interface type */
|
||||
unsigned char proto; /* Line protocol */
|
||||
unsigned char internalClock; /* 1 => internal clock, 0 => external */
|
||||
unsigned int lineSpeed; /* Speed in bps */
|
||||
unsigned int v24IpSts; /* V.24 control input status */
|
||||
unsigned int v24OpSts; /* V.24 control output status */
|
||||
unsigned short clockStatus; /* lsb: 0=> present, 1=> absent */
|
||||
unsigned short cableStatus; /* lsb: 0=> present, 1=> absent */
|
||||
unsigned short cardMode; /* lsb: LED id mode */
|
||||
unsigned short debug; /* Debug flags */
|
||||
unsigned char transparentMode; /* Not used always 0 */
|
||||
unsigned char invertClock; /* Invert clock feature for syncing */
|
||||
unsigned char startingSlot; /* Time slot to use for start of tx */
|
||||
unsigned char clockSource; /* External or internal */
|
||||
unsigned char framing; /* E1, T1 or J1 */
|
||||
unsigned char structure; /* unframed, double, crc4, f4, f12, */
|
||||
/* f24 f72 */
|
||||
unsigned char interface; /* rj48c or bnc */
|
||||
unsigned char coding; /* hdb3 b8zs */
|
||||
unsigned char lineBuildOut; /* 0, -7.5, -15, -22 */
|
||||
unsigned char equalizer; /* short or lon haul settings */
|
||||
unsigned char loopMode; /* various loopbacks */
|
||||
unsigned char range; /* cable lengths */
|
||||
unsigned char txBufferMode; /* tx elastic buffer depth */
|
||||
unsigned char rxBufferMode; /* rx elastic buffer depth */
|
||||
unsigned char losThreshold; /* Attenuation on LOS signal */
|
||||
unsigned char idleCode; /* Value to send as idle timeslot */
|
||||
unsigned int receiveBufferDelay; /* delay thro rx buffer timeslots */
|
||||
unsigned int framingErrorCount; /* framing errors */
|
||||
unsigned int codeViolationCount; /* code violations */
|
||||
unsigned int crcErrorCount; /* CRC errors */
|
||||
int lineAttenuation; /* in dB*/
|
||||
unsigned short lossOfSignal;
|
||||
unsigned short receiveRemoteAlarm;
|
||||
unsigned short alarmIndicationSignal;
|
||||
};
|
||||
|
||||
/* "valid" bitmask */
|
||||
#define FSTVAL_NONE 0x00000000 /* Nothing valid (firmware not running).
|
||||
* Slight misnomer. In fact nports,
|
||||
* type, state and index will be set
|
||||
* based on hardware detected.
|
||||
*/
|
||||
#define FSTVAL_OMODEM 0x0000001F /* First 5 bits correspond to the
|
||||
* output status bits defined for
|
||||
* v24OpSts
|
||||
*/
|
||||
#define FSTVAL_SPEED 0x00000020 /* internalClock, lineSpeed, clockStatus
|
||||
*/
|
||||
#define FSTVAL_CABLE 0x00000040 /* lineInterface, cableStatus */
|
||||
#define FSTVAL_IMODEM 0x00000080 /* v24IpSts */
|
||||
#define FSTVAL_CARD 0x00000100 /* nports, type, state, index,
|
||||
* smcFirmwareVersion
|
||||
*/
|
||||
#define FSTVAL_PROTO 0x00000200 /* proto */
|
||||
#define FSTVAL_MODE 0x00000400 /* cardMode */
|
||||
#define FSTVAL_PHASE 0x00000800 /* Clock phase */
|
||||
#define FSTVAL_TE1 0x00001000 /* T1E1 Configuration */
|
||||
#define FSTVAL_DEBUG 0x80000000 /* debug */
|
||||
#define FSTVAL_ALL 0x00001FFF /* Note: does not include DEBUG flag */
|
||||
|
||||
/* "type" */
|
||||
#define FST_TYPE_NONE 0 /* Probably should never happen */
|
||||
#define FST_TYPE_T2P 1 /* T2P X21 2 port card */
|
||||
#define FST_TYPE_T4P 2 /* T4P X21 4 port card */
|
||||
#define FST_TYPE_T1U 3 /* T1U X21 1 port card */
|
||||
#define FST_TYPE_T2U 4 /* T2U X21 2 port card */
|
||||
#define FST_TYPE_T4U 5 /* T4U X21 4 port card */
|
||||
#define FST_TYPE_TE1 6 /* T1E1 X21 1 port card */
|
||||
|
||||
/* "family" */
|
||||
#define FST_FAMILY_TXP 0 /* T2P or T4P */
|
||||
#define FST_FAMILY_TXU 1 /* T1U or T2U or T4U */
|
||||
|
||||
/* "state" */
|
||||
#define FST_UNINIT 0 /* Raw uninitialised state following
|
||||
* system startup */
|
||||
#define FST_RESET 1 /* Processor held in reset state */
|
||||
#define FST_DOWNLOAD 2 /* Card being downloaded */
|
||||
#define FST_STARTING 3 /* Released following download */
|
||||
#define FST_RUNNING 4 /* Processor running */
|
||||
#define FST_BADVERSION 5 /* Bad shared memory version detected */
|
||||
#define FST_HALTED 6 /* Processor flagged a halt */
|
||||
#define FST_IFAILED 7 /* Firmware issued initialisation failed
|
||||
* interrupt
|
||||
*/
|
||||
/* "lineInterface" */
|
||||
#define V24 1
|
||||
#define X21 2
|
||||
#define V35 3
|
||||
#define X21D 4
|
||||
#define T1 5
|
||||
#define E1 6
|
||||
#define J1 7
|
||||
|
||||
/* "proto" */
|
||||
#define FST_HDLC 1 /* Cisco compatible HDLC */
|
||||
#define FST_PPP 2 /* Sync PPP */
|
||||
#define FST_MONITOR 3 /* Monitor only (raw packet reception) */
|
||||
#define FST_RAW 4 /* Two way raw packets */
|
||||
#define FST_GEN_HDLC 5 /* Using "Generic HDLC" module */
|
||||
|
||||
/* "internalClock" */
|
||||
#define INTCLK 1
|
||||
#define EXTCLK 0
|
||||
|
||||
/* "v24IpSts" bitmask */
|
||||
#define IPSTS_CTS 0x00000001 /* Clear To Send (Indicate for X.21) */
|
||||
#define IPSTS_INDICATE IPSTS_CTS
|
||||
#define IPSTS_DSR 0x00000002 /* Data Set Ready (T2P Port A) */
|
||||
#define IPSTS_DCD 0x00000004 /* Data Carrier Detect */
|
||||
#define IPSTS_RI 0x00000008 /* Ring Indicator (T2P Port A) */
|
||||
#define IPSTS_TMI 0x00000010 /* Test Mode Indicator (Not Supported)*/
|
||||
|
||||
/* "v24OpSts" bitmask */
|
||||
#define OPSTS_RTS 0x00000001 /* Request To Send (Control for X.21) */
|
||||
#define OPSTS_CONTROL OPSTS_RTS
|
||||
#define OPSTS_DTR 0x00000002 /* Data Terminal Ready */
|
||||
#define OPSTS_DSRS 0x00000004 /* Data Signalling Rate Select (Not
|
||||
* Supported) */
|
||||
#define OPSTS_SS 0x00000008 /* Select Standby (Not Supported) */
|
||||
#define OPSTS_LL 0x00000010 /* Maintenance Test (Not Supported) */
|
||||
|
||||
/* "cardMode" bitmask */
|
||||
#define CARD_MODE_IDENTIFY 0x0001
|
||||
|
||||
/*
|
||||
* Constants for T1/E1 configuration
|
||||
*/
|
||||
|
||||
/*
|
||||
* Clock source
|
||||
*/
|
||||
#define CLOCKING_SLAVE 0
|
||||
#define CLOCKING_MASTER 1
|
||||
|
||||
/*
|
||||
* Framing
|
||||
*/
|
||||
#define FRAMING_E1 0
|
||||
#define FRAMING_J1 1
|
||||
#define FRAMING_T1 2
|
||||
|
||||
/*
|
||||
* Structure
|
||||
*/
|
||||
#define STRUCTURE_UNFRAMED 0
|
||||
#define STRUCTURE_E1_DOUBLE 1
|
||||
#define STRUCTURE_E1_CRC4 2
|
||||
#define STRUCTURE_E1_CRC4M 3
|
||||
#define STRUCTURE_T1_4 4
|
||||
#define STRUCTURE_T1_12 5
|
||||
#define STRUCTURE_T1_24 6
|
||||
#define STRUCTURE_T1_72 7
|
||||
|
||||
/*
|
||||
* Interface
|
||||
*/
|
||||
#define INTERFACE_RJ48C 0
|
||||
#define INTERFACE_BNC 1
|
||||
|
||||
/*
|
||||
* Coding
|
||||
*/
|
||||
|
||||
#define CODING_HDB3 0
|
||||
#define CODING_NRZ 1
|
||||
#define CODING_CMI 2
|
||||
#define CODING_CMI_HDB3 3
|
||||
#define CODING_CMI_B8ZS 4
|
||||
#define CODING_AMI 5
|
||||
#define CODING_AMI_ZCS 6
|
||||
#define CODING_B8ZS 7
|
||||
|
||||
/*
|
||||
* Line Build Out
|
||||
*/
|
||||
#define LBO_0dB 0
|
||||
#define LBO_7dB5 1
|
||||
#define LBO_15dB 2
|
||||
#define LBO_22dB5 3
|
||||
|
||||
/*
|
||||
* Range for long haul t1 > 655ft
|
||||
*/
|
||||
#define RANGE_0_133_FT 0
|
||||
#define RANGE_0_40_M RANGE_0_133_FT
|
||||
#define RANGE_133_266_FT 1
|
||||
#define RANGE_40_81_M RANGE_133_266_FT
|
||||
#define RANGE_266_399_FT 2
|
||||
#define RANGE_81_122_M RANGE_266_399_FT
|
||||
#define RANGE_399_533_FT 3
|
||||
#define RANGE_122_162_M RANGE_399_533_FT
|
||||
#define RANGE_533_655_FT 4
|
||||
#define RANGE_162_200_M RANGE_533_655_FT
|
||||
/*
|
||||
* Receive Equaliser
|
||||
*/
|
||||
#define EQUALIZER_SHORT 0
|
||||
#define EQUALIZER_LONG 1
|
||||
|
||||
/*
|
||||
* Loop modes
|
||||
*/
|
||||
#define LOOP_NONE 0
|
||||
#define LOOP_LOCAL 1
|
||||
#define LOOP_PAYLOAD_EXC_TS0 2
|
||||
#define LOOP_PAYLOAD_INC_TS0 3
|
||||
#define LOOP_REMOTE 4
|
||||
|
||||
/*
|
||||
* Buffer modes
|
||||
*/
|
||||
#define BUFFER_2_FRAME 0
|
||||
#define BUFFER_1_FRAME 1
|
||||
#define BUFFER_96_BIT 2
|
||||
#define BUFFER_NONE 3
|
||||
|
||||
/* Debug support
|
||||
*
|
||||
* These should only be enabled for development kernels, production code
|
||||
* should define FST_DEBUG=0 in order to exclude the code.
|
||||
* Setting FST_DEBUG=1 will include all the debug code but in a disabled
|
||||
* state, use the FSTSETCONF ioctl to enable specific debug actions, or
|
||||
* FST_DEBUG can be set to prime the debug selection.
|
||||
*/
|
||||
#define FST_DEBUG 0x0000
|
||||
#if FST_DEBUG
|
||||
|
||||
extern int fst_debug_mask; /* Bit mask of actions to debug, bits
|
||||
* listed below. Note: Bit 0 is used
|
||||
* to trigger the inclusion of this
|
||||
* code, without enabling any actions.
|
||||
*/
|
||||
#define DBG_INIT 0x0002 /* Card detection and initialisation */
|
||||
#define DBG_OPEN 0x0004 /* Open and close sequences */
|
||||
#define DBG_PCI 0x0008 /* PCI config operations */
|
||||
#define DBG_IOCTL 0x0010 /* Ioctls and other config */
|
||||
#define DBG_INTR 0x0020 /* Interrupt routines (be careful) */
|
||||
#define DBG_TX 0x0040 /* Packet transmission */
|
||||
#define DBG_RX 0x0080 /* Packet reception */
|
||||
#define DBG_CMD 0x0100 /* Port command issuing */
|
||||
|
||||
#define DBG_ASS 0xFFFF /* Assert like statements. Code that
|
||||
* should never be reached, if you see
|
||||
* one of these then I've been an ass
|
||||
*/
|
||||
#endif /* FST_DEBUG */
|
||||
|
||||
241
drivers/net/wan/hd64570.h
Normal file
241
drivers/net/wan/hd64570.h
Normal file
@@ -0,0 +1,241 @@
|
||||
#ifndef __HD64570_H
|
||||
#define __HD64570_H
|
||||
|
||||
/* SCA HD64570 register definitions - all addresses for mode 0 (8086 MPU)
|
||||
and 1 (64180 MPU). For modes 2 and 3, XOR the address with 0x01.
|
||||
|
||||
Source: HD64570 SCA User's Manual
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* SCA Control Registers */
|
||||
#define LPR 0x00 /* Low Power */
|
||||
|
||||
/* Wait controller registers */
|
||||
#define PABR0 0x02 /* Physical Address Boundary 0 */
|
||||
#define PABR1 0x03 /* Physical Address Boundary 1 */
|
||||
#define WCRL 0x04 /* Wait Control L */
|
||||
#define WCRM 0x05 /* Wait Control M */
|
||||
#define WCRH 0x06 /* Wait Control H */
|
||||
|
||||
#define PCR 0x08 /* DMA Priority Control */
|
||||
#define DMER 0x09 /* DMA Master Enable */
|
||||
|
||||
|
||||
/* Interrupt registers */
|
||||
#define ISR0 0x10 /* Interrupt Status 0 */
|
||||
#define ISR1 0x11 /* Interrupt Status 1 */
|
||||
#define ISR2 0x12 /* Interrupt Status 2 */
|
||||
|
||||
#define IER0 0x14 /* Interrupt Enable 0 */
|
||||
#define IER1 0x15 /* Interrupt Enable 1 */
|
||||
#define IER2 0x16 /* Interrupt Enable 2 */
|
||||
|
||||
#define ITCR 0x18 /* Interrupt Control */
|
||||
#define IVR 0x1A /* Interrupt Vector */
|
||||
#define IMVR 0x1C /* Interrupt Modified Vector */
|
||||
|
||||
|
||||
|
||||
/* MSCI channel (port) 0 registers - offset 0x20
|
||||
MSCI channel (port) 1 registers - offset 0x40 */
|
||||
|
||||
#define MSCI0_OFFSET 0x20
|
||||
#define MSCI1_OFFSET 0x40
|
||||
|
||||
#define TRBL 0x00 /* TX/RX buffer L */
|
||||
#define TRBH 0x01 /* TX/RX buffer H */
|
||||
#define ST0 0x02 /* Status 0 */
|
||||
#define ST1 0x03 /* Status 1 */
|
||||
#define ST2 0x04 /* Status 2 */
|
||||
#define ST3 0x05 /* Status 3 */
|
||||
#define FST 0x06 /* Frame Status */
|
||||
#define IE0 0x08 /* Interrupt Enable 0 */
|
||||
#define IE1 0x09 /* Interrupt Enable 1 */
|
||||
#define IE2 0x0A /* Interrupt Enable 2 */
|
||||
#define FIE 0x0B /* Frame Interrupt Enable */
|
||||
#define CMD 0x0C /* Command */
|
||||
#define MD0 0x0E /* Mode 0 */
|
||||
#define MD1 0x0F /* Mode 1 */
|
||||
#define MD2 0x10 /* Mode 2 */
|
||||
#define CTL 0x11 /* Control */
|
||||
#define SA0 0x12 /* Sync/Address 0 */
|
||||
#define SA1 0x13 /* Sync/Address 1 */
|
||||
#define IDL 0x14 /* Idle Pattern */
|
||||
#define TMC 0x15 /* Time Constant */
|
||||
#define RXS 0x16 /* RX Clock Source */
|
||||
#define TXS 0x17 /* TX Clock Source */
|
||||
#define TRC0 0x18 /* TX Ready Control 0 */
|
||||
#define TRC1 0x19 /* TX Ready Control 1 */
|
||||
#define RRC 0x1A /* RX Ready Control */
|
||||
#define CST0 0x1C /* Current Status 0 */
|
||||
#define CST1 0x1D /* Current Status 1 */
|
||||
|
||||
|
||||
/* Timer channel 0 (port 0 RX) registers - offset 0x60
|
||||
Timer channel 1 (port 0 TX) registers - offset 0x68
|
||||
Timer channel 2 (port 1 RX) registers - offset 0x70
|
||||
Timer channel 3 (port 1 TX) registers - offset 0x78
|
||||
*/
|
||||
|
||||
#define TIMER0RX_OFFSET 0x60
|
||||
#define TIMER0TX_OFFSET 0x68
|
||||
#define TIMER1RX_OFFSET 0x70
|
||||
#define TIMER1TX_OFFSET 0x78
|
||||
|
||||
#define TCNTL 0x00 /* Up-counter L */
|
||||
#define TCNTH 0x01 /* Up-counter H */
|
||||
#define TCONRL 0x02 /* Constant L */
|
||||
#define TCONRH 0x03 /* Constant H */
|
||||
#define TCSR 0x04 /* Control/Status */
|
||||
#define TEPR 0x05 /* Expand Prescale */
|
||||
|
||||
|
||||
|
||||
/* DMA channel 0 (port 0 RX) registers - offset 0x80
|
||||
DMA channel 1 (port 0 TX) registers - offset 0xA0
|
||||
DMA channel 2 (port 1 RX) registers - offset 0xC0
|
||||
DMA channel 3 (port 1 TX) registers - offset 0xE0
|
||||
*/
|
||||
|
||||
#define DMAC0RX_OFFSET 0x80
|
||||
#define DMAC0TX_OFFSET 0xA0
|
||||
#define DMAC1RX_OFFSET 0xC0
|
||||
#define DMAC1TX_OFFSET 0xE0
|
||||
|
||||
#define BARL 0x00 /* Buffer Address L (chained block) */
|
||||
#define BARH 0x01 /* Buffer Address H (chained block) */
|
||||
#define BARB 0x02 /* Buffer Address B (chained block) */
|
||||
|
||||
#define DARL 0x00 /* RX Destination Addr L (single block) */
|
||||
#define DARH 0x01 /* RX Destination Addr H (single block) */
|
||||
#define DARB 0x02 /* RX Destination Addr B (single block) */
|
||||
|
||||
#define SARL 0x04 /* TX Source Address L (single block) */
|
||||
#define SARH 0x05 /* TX Source Address H (single block) */
|
||||
#define SARB 0x06 /* TX Source Address B (single block) */
|
||||
|
||||
#define CPB 0x06 /* Chain Pointer Base (chained block) */
|
||||
|
||||
#define CDAL 0x08 /* Current Descriptor Addr L (chained block) */
|
||||
#define CDAH 0x09 /* Current Descriptor Addr H (chained block) */
|
||||
#define EDAL 0x0A /* Error Descriptor Addr L (chained block) */
|
||||
#define EDAH 0x0B /* Error Descriptor Addr H (chained block) */
|
||||
#define BFLL 0x0C /* RX Receive Buffer Length L (chained block)*/
|
||||
#define BFLH 0x0D /* RX Receive Buffer Length H (chained block)*/
|
||||
#define BCRL 0x0E /* Byte Count L */
|
||||
#define BCRH 0x0F /* Byte Count H */
|
||||
#define DSR 0x10 /* DMA Status */
|
||||
#define DSR_RX(node) (DSR + (node ? DMAC1RX_OFFSET : DMAC0RX_OFFSET))
|
||||
#define DSR_TX(node) (DSR + (node ? DMAC1TX_OFFSET : DMAC0TX_OFFSET))
|
||||
#define DMR 0x11 /* DMA Mode */
|
||||
#define DMR_RX(node) (DMR + (node ? DMAC1RX_OFFSET : DMAC0RX_OFFSET))
|
||||
#define DMR_TX(node) (DMR + (node ? DMAC1TX_OFFSET : DMAC0TX_OFFSET))
|
||||
#define FCT 0x13 /* Frame End Interrupt Counter */
|
||||
#define FCT_RX(node) (FCT + (node ? DMAC1RX_OFFSET : DMAC0RX_OFFSET))
|
||||
#define FCT_TX(node) (FCT + (node ? DMAC1TX_OFFSET : DMAC0TX_OFFSET))
|
||||
#define DIR 0x14 /* DMA Interrupt Enable */
|
||||
#define DIR_RX(node) (DIR + (node ? DMAC1RX_OFFSET : DMAC0RX_OFFSET))
|
||||
#define DIR_TX(node) (DIR + (node ? DMAC1TX_OFFSET : DMAC0TX_OFFSET))
|
||||
#define DCR 0x15 /* DMA Command */
|
||||
#define DCR_RX(node) (DCR + (node ? DMAC1RX_OFFSET : DMAC0RX_OFFSET))
|
||||
#define DCR_TX(node) (DCR + (node ? DMAC1TX_OFFSET : DMAC0TX_OFFSET))
|
||||
|
||||
|
||||
|
||||
|
||||
/* Descriptor Structure */
|
||||
|
||||
typedef struct {
|
||||
u16 cp; /* Chain Pointer */
|
||||
u32 bp; /* Buffer Pointer (24 bits) */
|
||||
u16 len; /* Data Length */
|
||||
u8 stat; /* Status */
|
||||
u8 unused; /* pads to 2-byte boundary */
|
||||
}__attribute__ ((packed)) pkt_desc;
|
||||
|
||||
|
||||
/* Packet Descriptor Status bits */
|
||||
|
||||
#define ST_TX_EOM 0x80 /* End of frame */
|
||||
#define ST_TX_EOT 0x01 /* End of transmition */
|
||||
|
||||
#define ST_RX_EOM 0x80 /* End of frame */
|
||||
#define ST_RX_SHORT 0x40 /* Short frame */
|
||||
#define ST_RX_ABORT 0x20 /* Abort */
|
||||
#define ST_RX_RESBIT 0x10 /* Residual bit */
|
||||
#define ST_RX_OVERRUN 0x08 /* Overrun */
|
||||
#define ST_RX_CRC 0x04 /* CRC */
|
||||
|
||||
#define ST_ERROR_MASK 0x7C
|
||||
|
||||
#define DIR_EOTE 0x80 /* Transfer completed */
|
||||
#define DIR_EOME 0x40 /* Frame Transfer Completed (chained-block) */
|
||||
#define DIR_BOFE 0x20 /* Buffer Overflow/Underflow (chained-block)*/
|
||||
#define DIR_COFE 0x10 /* Counter Overflow (chained-block) */
|
||||
|
||||
|
||||
#define DSR_EOT 0x80 /* Transfer completed */
|
||||
#define DSR_EOM 0x40 /* Frame Transfer Completed (chained-block) */
|
||||
#define DSR_BOF 0x20 /* Buffer Overflow/Underflow (chained-block)*/
|
||||
#define DSR_COF 0x10 /* Counter Overflow (chained-block) */
|
||||
#define DSR_DE 0x02 /* DMA Enable */
|
||||
#define DSR_DWE 0x01 /* DMA Write Disable */
|
||||
|
||||
/* DMA Master Enable Register (DMER) bits */
|
||||
#define DMER_DME 0x80 /* DMA Master Enable */
|
||||
|
||||
|
||||
#define CMD_RESET 0x21 /* Reset Channel */
|
||||
#define CMD_TX_ENABLE 0x02 /* Start transmitter */
|
||||
#define CMD_RX_ENABLE 0x12 /* Start receiver */
|
||||
|
||||
#define MD0_HDLC 0x80 /* Bit-sync HDLC mode */
|
||||
#define MD0_CRC_ENA 0x04 /* Enable CRC code calculation */
|
||||
#define MD0_CRC_CCITT 0x02 /* CCITT CRC instead of CRC-16 */
|
||||
#define MD0_CRC_PR1 0x01 /* Initial all-ones instead of all-zeros */
|
||||
|
||||
#define MD0_CRC_NONE 0x00
|
||||
#define MD0_CRC_16_0 0x04
|
||||
#define MD0_CRC_16 0x05
|
||||
#define MD0_CRC_ITU_0 0x06
|
||||
#define MD0_CRC_ITU 0x07
|
||||
|
||||
#define MD2_NRZ 0x00
|
||||
#define MD2_NRZI 0x20
|
||||
#define MD2_MANCHESTER 0x80
|
||||
#define MD2_FM_MARK 0xA0
|
||||
#define MD2_FM_SPACE 0xC0
|
||||
#define MD2_LOOPBACK 0x03 /* Local data Loopback */
|
||||
|
||||
#define CTL_NORTS 0x01
|
||||
#define CTL_IDLE 0x10 /* Transmit an idle pattern */
|
||||
#define CTL_UDRNC 0x20 /* Idle after CRC or FCS+flag transmition */
|
||||
|
||||
#define ST0_TXRDY 0x02 /* TX ready */
|
||||
#define ST0_RXRDY 0x01 /* RX ready */
|
||||
|
||||
#define ST1_UDRN 0x80 /* MSCI TX underrun */
|
||||
#define ST1_CDCD 0x04 /* DCD level changed */
|
||||
|
||||
#define ST3_CTS 0x08 /* modem input - /CTS */
|
||||
#define ST3_DCD 0x04 /* modem input - /DCD */
|
||||
|
||||
#define IE0_TXINT 0x80 /* TX INT MSCI interrupt enable */
|
||||
#define IE0_RXINTA 0x40 /* RX INT A MSCI interrupt enable */
|
||||
#define IE1_UDRN 0x80 /* TX underrun MSCI interrupt enable */
|
||||
#define IE1_CDCD 0x04 /* DCD level changed */
|
||||
|
||||
#define DCR_ABORT 0x01 /* Software abort command */
|
||||
#define DCR_CLEAR_EOF 0x02 /* Clear EOF interrupt */
|
||||
|
||||
/* TX and RX Clock Source - RXS and TXS */
|
||||
#define CLK_BRG_MASK 0x0F
|
||||
#define CLK_LINE_RX 0x00 /* TX/RX clock line input */
|
||||
#define CLK_LINE_TX 0x00 /* TX/RX line input */
|
||||
#define CLK_BRG_RX 0x40 /* internal baud rate generator */
|
||||
#define CLK_BRG_TX 0x40 /* internal baud rate generator */
|
||||
#define CLK_RXCLK_TX 0x60 /* TX clock from RX clock */
|
||||
|
||||
#endif
|
||||
530
drivers/net/wan/hd64572.h
Normal file
530
drivers/net/wan/hd64572.h
Normal file
@@ -0,0 +1,530 @@
|
||||
/*
|
||||
* hd64572.h Description of the Hitachi HD64572 (SCA-II), valid for
|
||||
* CPU modes 0 & 2.
|
||||
*
|
||||
* Author: Ivan Passos <ivan@cyclades.com>
|
||||
*
|
||||
* Copyright: (c) 2000-2001 Cyclades Corp.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $Log: hd64572.h,v $
|
||||
* Revision 1.1.1.1 2007/06/12 07:27:11 eyryu
|
||||
* s3c-linux-2.6.21.5
|
||||
*
|
||||
* Revision 3.1 2001/06/15 12:41:10 regina
|
||||
* upping major version number
|
||||
*
|
||||
* Revision 1.1.1.1 2001/06/13 20:24:49 daniela
|
||||
* PC300 initial CVS version (3.4.0-pre1)
|
||||
*
|
||||
* Revision 1.0 2000/01/25 ivan
|
||||
* Initial version.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __HD64572_H
|
||||
#define __HD64572_H
|
||||
|
||||
/* Illegal Access Register */
|
||||
#define ILAR 0x00
|
||||
|
||||
/* Wait Controller Registers */
|
||||
#define PABR0L 0x20 /* Physical Addr Boundary Register 0 L */
|
||||
#define PABR0H 0x21 /* Physical Addr Boundary Register 0 H */
|
||||
#define PABR1L 0x22 /* Physical Addr Boundary Register 1 L */
|
||||
#define PABR1H 0x23 /* Physical Addr Boundary Register 1 H */
|
||||
#define WCRL 0x24 /* Wait Control Register L */
|
||||
#define WCRM 0x25 /* Wait Control Register M */
|
||||
#define WCRH 0x26 /* Wait Control Register H */
|
||||
|
||||
/* Interrupt Registers */
|
||||
#define IVR 0x60 /* Interrupt Vector Register */
|
||||
#define IMVR 0x64 /* Interrupt Modified Vector Register */
|
||||
#define ITCR 0x68 /* Interrupt Control Register */
|
||||
#define ISR0 0x6c /* Interrupt Status Register 0 */
|
||||
#define ISR1 0x70 /* Interrupt Status Register 1 */
|
||||
#define IER0 0x74 /* Interrupt Enable Register 0 */
|
||||
#define IER1 0x78 /* Interrupt Enable Register 1 */
|
||||
|
||||
/* Register Access Macros (chan is 0 or 1 in _any_ case) */
|
||||
#define M_REG(reg, chan) (reg + 0x80*chan) /* MSCI */
|
||||
#define DRX_REG(reg, chan) (reg + 0x40*chan) /* DMA Rx */
|
||||
#define DTX_REG(reg, chan) (reg + 0x20*(2*chan + 1)) /* DMA Tx */
|
||||
#define TRX_REG(reg, chan) (reg + 0x20*chan) /* Timer Rx */
|
||||
#define TTX_REG(reg, chan) (reg + 0x10*(2*chan + 1)) /* Timer Tx */
|
||||
#define ST_REG(reg, chan) (reg + 0x80*chan) /* Status Cnt */
|
||||
#define IR0_DRX(val, chan) ((val)<<(8*(chan))) /* Int DMA Rx */
|
||||
#define IR0_DTX(val, chan) ((val)<<(4*(2*chan + 1))) /* Int DMA Tx */
|
||||
#define IR0_M(val, chan) ((val)<<(8*(chan))) /* Int MSCI */
|
||||
|
||||
/* MSCI Channel Registers */
|
||||
#define MSCI0_OFFSET 0x00
|
||||
#define MSCI1_OFFSET 0x80
|
||||
|
||||
#define MD0 0x138 /* Mode reg 0 */
|
||||
#define MD1 0x139 /* Mode reg 1 */
|
||||
#define MD2 0x13a /* Mode reg 2 */
|
||||
#define MD3 0x13b /* Mode reg 3 */
|
||||
#define CTL 0x130 /* Control reg */
|
||||
#define RXS 0x13c /* RX clock source */
|
||||
#define TXS 0x13d /* TX clock source */
|
||||
#define EXS 0x13e /* External clock input selection */
|
||||
#define TMCT 0x144 /* Time constant (Tx) */
|
||||
#define TMCR 0x145 /* Time constant (Rx) */
|
||||
#define CMD 0x128 /* Command reg */
|
||||
#define ST0 0x118 /* Status reg 0 */
|
||||
#define ST1 0x119 /* Status reg 1 */
|
||||
#define ST2 0x11a /* Status reg 2 */
|
||||
#define ST3 0x11b /* Status reg 3 */
|
||||
#define ST4 0x11c /* Status reg 4 */
|
||||
#define FST 0x11d /* frame Status reg */
|
||||
#define IE0 0x120 /* Interrupt enable reg 0 */
|
||||
#define IE1 0x121 /* Interrupt enable reg 1 */
|
||||
#define IE2 0x122 /* Interrupt enable reg 2 */
|
||||
#define IE4 0x124 /* Interrupt enable reg 4 */
|
||||
#define FIE 0x125 /* Frame Interrupt enable reg */
|
||||
#define SA0 0x140 /* Syn Address reg 0 */
|
||||
#define SA1 0x141 /* Syn Address reg 1 */
|
||||
#define IDL 0x142 /* Idle register */
|
||||
#define TRBL 0x100 /* TX/RX buffer reg L */
|
||||
#define TRBK 0x101 /* TX/RX buffer reg K */
|
||||
#define TRBJ 0x102 /* TX/RX buffer reg J */
|
||||
#define TRBH 0x103 /* TX/RX buffer reg H */
|
||||
#define TRC0 0x148 /* TX Ready control reg 0 */
|
||||
#define TRC1 0x149 /* TX Ready control reg 1 */
|
||||
#define RRC 0x14a /* RX Ready control reg */
|
||||
#define CST0 0x108 /* Current Status Register 0 */
|
||||
#define CST1 0x109 /* Current Status Register 1 */
|
||||
#define CST2 0x10a /* Current Status Register 2 */
|
||||
#define CST3 0x10b /* Current Status Register 3 */
|
||||
#define GPO 0x131 /* General Purpose Output Pin Ctl Reg */
|
||||
#define TFS 0x14b /* Tx Start Threshold Ctl Reg */
|
||||
#define TFN 0x143 /* Inter-transmit-frame Time Fill Ctl Reg */
|
||||
#define TBN 0x110 /* Tx Buffer Number Reg */
|
||||
#define RBN 0x111 /* Rx Buffer Number Reg */
|
||||
#define TNR0 0x150 /* Tx DMA Request Ctl Reg 0 */
|
||||
#define TNR1 0x151 /* Tx DMA Request Ctl Reg 1 */
|
||||
#define TCR 0x152 /* Tx DMA Critical Request Reg */
|
||||
#define RNR 0x154 /* Rx DMA Request Ctl Reg */
|
||||
#define RCR 0x156 /* Rx DMA Critical Request Reg */
|
||||
|
||||
/* Timer Registers */
|
||||
#define TIMER0RX_OFFSET 0x00
|
||||
#define TIMER0TX_OFFSET 0x10
|
||||
#define TIMER1RX_OFFSET 0x20
|
||||
#define TIMER1TX_OFFSET 0x30
|
||||
|
||||
#define TCNTL 0x200 /* Timer Upcounter L */
|
||||
#define TCNTH 0x201 /* Timer Upcounter H */
|
||||
#define TCONRL 0x204 /* Timer Constant Register L */
|
||||
#define TCONRH 0x205 /* Timer Constant Register H */
|
||||
#define TCSR 0x206 /* Timer Control/Status Register */
|
||||
#define TEPR 0x207 /* Timer Expand Prescale Register */
|
||||
|
||||
/* DMA registers */
|
||||
#define PCR 0x40 /* DMA priority control reg */
|
||||
#define DRR 0x44 /* DMA reset reg */
|
||||
#define DMER 0x07 /* DMA Master Enable reg */
|
||||
#define BTCR 0x08 /* Burst Tx Ctl Reg */
|
||||
#define BOLR 0x0c /* Back-off Length Reg */
|
||||
#define DSR_RX(chan) (0x48 + 2*chan) /* DMA Status Reg (Rx) */
|
||||
#define DSR_TX(chan) (0x49 + 2*chan) /* DMA Status Reg (Tx) */
|
||||
#define DIR_RX(chan) (0x4c + 2*chan) /* DMA Interrupt Enable Reg (Rx) */
|
||||
#define DIR_TX(chan) (0x4d + 2*chan) /* DMA Interrupt Enable Reg (Tx) */
|
||||
#define FCT_RX(chan) (0x50 + 2*chan) /* Frame End Interrupt Counter (Rx) */
|
||||
#define FCT_TX(chan) (0x51 + 2*chan) /* Frame End Interrupt Counter (Tx) */
|
||||
#define DMR_RX(chan) (0x54 + 2*chan) /* DMA Mode Reg (Rx) */
|
||||
#define DMR_TX(chan) (0x55 + 2*chan) /* DMA Mode Reg (Tx) */
|
||||
#define DCR_RX(chan) (0x58 + 2*chan) /* DMA Command Reg (Rx) */
|
||||
#define DCR_TX(chan) (0x59 + 2*chan) /* DMA Command Reg (Tx) */
|
||||
|
||||
/* DMA Channel Registers */
|
||||
#define DMAC0RX_OFFSET 0x00
|
||||
#define DMAC0TX_OFFSET 0x20
|
||||
#define DMAC1RX_OFFSET 0x40
|
||||
#define DMAC1TX_OFFSET 0x60
|
||||
|
||||
#define DARL 0x80 /* Dest Addr Register L (single-block, RX only) */
|
||||
#define DARH 0x81 /* Dest Addr Register H (single-block, RX only) */
|
||||
#define DARB 0x82 /* Dest Addr Register B (single-block, RX only) */
|
||||
#define DARBH 0x83 /* Dest Addr Register BH (single-block, RX only) */
|
||||
#define SARL 0x80 /* Source Addr Register L (single-block, TX only) */
|
||||
#define SARH 0x81 /* Source Addr Register H (single-block, TX only) */
|
||||
#define SARB 0x82 /* Source Addr Register B (single-block, TX only) */
|
||||
#define DARBH 0x83 /* Source Addr Register BH (single-block, TX only) */
|
||||
#define BARL 0x80 /* Buffer Addr Register L (chained-block) */
|
||||
#define BARH 0x81 /* Buffer Addr Register H (chained-block) */
|
||||
#define BARB 0x82 /* Buffer Addr Register B (chained-block) */
|
||||
#define BARBH 0x83 /* Buffer Addr Register BH (chained-block) */
|
||||
#define CDAL 0x84 /* Current Descriptor Addr Register L */
|
||||
#define CDAH 0x85 /* Current Descriptor Addr Register H */
|
||||
#define CDAB 0x86 /* Current Descriptor Addr Register B */
|
||||
#define CDABH 0x87 /* Current Descriptor Addr Register BH */
|
||||
#define EDAL 0x88 /* Error Descriptor Addr Register L */
|
||||
#define EDAH 0x89 /* Error Descriptor Addr Register H */
|
||||
#define EDAB 0x8a /* Error Descriptor Addr Register B */
|
||||
#define EDABH 0x8b /* Error Descriptor Addr Register BH */
|
||||
#define BFLL 0x90 /* RX Buffer Length L (only RX) */
|
||||
#define BFLH 0x91 /* RX Buffer Length H (only RX) */
|
||||
#define BCRL 0x8c /* Byte Count Register L */
|
||||
#define BCRH 0x8d /* Byte Count Register H */
|
||||
|
||||
/* Block Descriptor Structure */
|
||||
typedef struct {
|
||||
unsigned long next; /* pointer to next block descriptor */
|
||||
unsigned long ptbuf; /* buffer pointer */
|
||||
unsigned short len; /* data length */
|
||||
unsigned char status; /* status */
|
||||
unsigned char filler[5]; /* alignment filler (16 bytes) */
|
||||
} pcsca_bd_t;
|
||||
|
||||
/* Block Descriptor Structure */
|
||||
typedef struct {
|
||||
u32 cp; /* pointer to next block descriptor */
|
||||
u32 bp; /* buffer pointer */
|
||||
u16 len; /* data length */
|
||||
u8 stat; /* status */
|
||||
u8 unused; /* pads to 4-byte boundary */
|
||||
}pkt_desc;
|
||||
|
||||
|
||||
/*
|
||||
Descriptor Status definitions:
|
||||
|
||||
Bit Transmission Reception
|
||||
|
||||
7 EOM EOM
|
||||
6 - Short Frame
|
||||
5 - Abort
|
||||
4 - Residual bit
|
||||
3 Underrun Overrun
|
||||
2 - CRC
|
||||
1 Ownership Ownership
|
||||
0 EOT -
|
||||
*/
|
||||
#define DST_EOT 0x01 /* End of transmit command */
|
||||
#define DST_OSB 0x02 /* Ownership bit */
|
||||
#define DST_CRC 0x04 /* CRC Error */
|
||||
#define DST_OVR 0x08 /* Overrun */
|
||||
#define DST_UDR 0x08 /* Underrun */
|
||||
#define DST_RBIT 0x10 /* Residual bit */
|
||||
#define DST_ABT 0x20 /* Abort */
|
||||
#define DST_SHRT 0x40 /* Short Frame */
|
||||
#define DST_EOM 0x80 /* End of Message */
|
||||
|
||||
/* Packet Descriptor Status bits */
|
||||
|
||||
#define ST_TX_EOM 0x80 /* End of frame */
|
||||
#define ST_TX_UNDRRUN 0x08
|
||||
#define ST_TX_OWNRSHP 0x02
|
||||
#define ST_TX_EOT 0x01 /* End of transmition */
|
||||
|
||||
#define ST_RX_EOM 0x80 /* End of frame */
|
||||
#define ST_RX_SHORT 0x40 /* Short frame */
|
||||
#define ST_RX_ABORT 0x20 /* Abort */
|
||||
#define ST_RX_RESBIT 0x10 /* Residual bit */
|
||||
#define ST_RX_OVERRUN 0x08 /* Overrun */
|
||||
#define ST_RX_CRC 0x04 /* CRC */
|
||||
#define ST_RX_OWNRSHP 0x02
|
||||
|
||||
#define ST_ERROR_MASK 0x7C
|
||||
|
||||
/* Status Counter Registers */
|
||||
#define CMCR 0x158 /* Counter Master Ctl Reg */
|
||||
#define TECNTL 0x160 /* Tx EOM Counter L */
|
||||
#define TECNTM 0x161 /* Tx EOM Counter M */
|
||||
#define TECNTH 0x162 /* Tx EOM Counter H */
|
||||
#define TECCR 0x163 /* Tx EOM Counter Ctl Reg */
|
||||
#define URCNTL 0x164 /* Underrun Counter L */
|
||||
#define URCNTH 0x165 /* Underrun Counter H */
|
||||
#define URCCR 0x167 /* Underrun Counter Ctl Reg */
|
||||
#define RECNTL 0x168 /* Rx EOM Counter L */
|
||||
#define RECNTM 0x169 /* Rx EOM Counter M */
|
||||
#define RECNTH 0x16a /* Rx EOM Counter H */
|
||||
#define RECCR 0x16b /* Rx EOM Counter Ctl Reg */
|
||||
#define ORCNTL 0x16c /* Overrun Counter L */
|
||||
#define ORCNTH 0x16d /* Overrun Counter H */
|
||||
#define ORCCR 0x16f /* Overrun Counter Ctl Reg */
|
||||
#define CECNTL 0x170 /* CRC Counter L */
|
||||
#define CECNTH 0x171 /* CRC Counter H */
|
||||
#define CECCR 0x173 /* CRC Counter Ctl Reg */
|
||||
#define ABCNTL 0x174 /* Abort frame Counter L */
|
||||
#define ABCNTH 0x175 /* Abort frame Counter H */
|
||||
#define ABCCR 0x177 /* Abort frame Counter Ctl Reg */
|
||||
#define SHCNTL 0x178 /* Short frame Counter L */
|
||||
#define SHCNTH 0x179 /* Short frame Counter H */
|
||||
#define SHCCR 0x17b /* Short frame Counter Ctl Reg */
|
||||
#define RSCNTL 0x17c /* Residual bit Counter L */
|
||||
#define RSCNTH 0x17d /* Residual bit Counter H */
|
||||
#define RSCCR 0x17f /* Residual bit Counter Ctl Reg */
|
||||
|
||||
/* Register Programming Constants */
|
||||
|
||||
#define IR0_DMIC 0x00000001
|
||||
#define IR0_DMIB 0x00000002
|
||||
#define IR0_DMIA 0x00000004
|
||||
#define IR0_EFT 0x00000008
|
||||
#define IR0_DMAREQ 0x00010000
|
||||
#define IR0_TXINT 0x00020000
|
||||
#define IR0_RXINTB 0x00040000
|
||||
#define IR0_RXINTA 0x00080000
|
||||
#define IR0_TXRDY 0x00100000
|
||||
#define IR0_RXRDY 0x00200000
|
||||
|
||||
#define MD0_CRC16_0 0x00
|
||||
#define MD0_CRC16_1 0x01
|
||||
#define MD0_CRC32 0x02
|
||||
#define MD0_CRC_CCITT 0x03
|
||||
#define MD0_CRCC0 0x04
|
||||
#define MD0_CRCC1 0x08
|
||||
#define MD0_AUTO_ENA 0x10
|
||||
#define MD0_ASYNC 0x00
|
||||
#define MD0_BY_MSYNC 0x20
|
||||
#define MD0_BY_BISYNC 0x40
|
||||
#define MD0_BY_EXT 0x60
|
||||
#define MD0_BIT_SYNC 0x80
|
||||
#define MD0_TRANSP 0xc0
|
||||
|
||||
#define MD0_HDLC 0x80 /* Bit-sync HDLC mode */
|
||||
|
||||
#define MD0_CRC_NONE 0x00
|
||||
#define MD0_CRC_16_0 0x04
|
||||
#define MD0_CRC_16 0x05
|
||||
#define MD0_CRC_ITU32 0x06
|
||||
#define MD0_CRC_ITU 0x07
|
||||
|
||||
#define MD1_NOADDR 0x00
|
||||
#define MD1_SADDR1 0x40
|
||||
#define MD1_SADDR2 0x80
|
||||
#define MD1_DADDR 0xc0
|
||||
|
||||
#define MD2_NRZI_IEEE 0x40
|
||||
#define MD2_MANCHESTER 0x80
|
||||
#define MD2_FM_MARK 0xA0
|
||||
#define MD2_FM_SPACE 0xC0
|
||||
#define MD2_LOOPBACK 0x03 /* Local data Loopback */
|
||||
|
||||
#define MD2_F_DUPLEX 0x00
|
||||
#define MD2_AUTO_ECHO 0x01
|
||||
#define MD2_LOOP_HI_Z 0x02
|
||||
#define MD2_LOOP_MIR 0x03
|
||||
#define MD2_ADPLL_X8 0x00
|
||||
#define MD2_ADPLL_X16 0x08
|
||||
#define MD2_ADPLL_X32 0x10
|
||||
#define MD2_NRZ 0x00
|
||||
#define MD2_NRZI 0x20
|
||||
#define MD2_NRZ_IEEE 0x40
|
||||
#define MD2_MANCH 0x00
|
||||
#define MD2_FM1 0x20
|
||||
#define MD2_FM0 0x40
|
||||
#define MD2_FM 0x80
|
||||
|
||||
#define CTL_RTS 0x01
|
||||
#define CTL_DTR 0x02
|
||||
#define CTL_SYN 0x04
|
||||
#define CTL_IDLC 0x10
|
||||
#define CTL_UDRNC 0x20
|
||||
#define CTL_URSKP 0x40
|
||||
#define CTL_URCT 0x80
|
||||
|
||||
#define CTL_NORTS 0x01
|
||||
#define CTL_NODTR 0x02
|
||||
#define CTL_IDLE 0x10
|
||||
|
||||
#define RXS_BR0 0x01
|
||||
#define RXS_BR1 0x02
|
||||
#define RXS_BR2 0x04
|
||||
#define RXS_BR3 0x08
|
||||
#define RXS_ECLK 0x00
|
||||
#define RXS_ECLK_NS 0x20
|
||||
#define RXS_IBRG 0x40
|
||||
#define RXS_PLL1 0x50
|
||||
#define RXS_PLL2 0x60
|
||||
#define RXS_PLL3 0x70
|
||||
#define RXS_DRTXC 0x80
|
||||
|
||||
#define TXS_BR0 0x01
|
||||
#define TXS_BR1 0x02
|
||||
#define TXS_BR2 0x04
|
||||
#define TXS_BR3 0x08
|
||||
#define TXS_ECLK 0x00
|
||||
#define TXS_IBRG 0x40
|
||||
#define TXS_RCLK 0x60
|
||||
#define TXS_DTRXC 0x80
|
||||
|
||||
#define EXS_RES0 0x01
|
||||
#define EXS_RES1 0x02
|
||||
#define EXS_RES2 0x04
|
||||
#define EXS_TES0 0x10
|
||||
#define EXS_TES1 0x20
|
||||
#define EXS_TES2 0x40
|
||||
|
||||
#define CLK_BRG_MASK 0x0F
|
||||
#define CLK_PIN_OUT 0x80
|
||||
#define CLK_LINE 0x00 /* clock line input */
|
||||
#define CLK_BRG 0x40 /* internal baud rate generator */
|
||||
#define CLK_TX_RXCLK 0x60 /* TX clock from RX clock */
|
||||
|
||||
#define CMD_RX_RST 0x11
|
||||
#define CMD_RX_ENA 0x12
|
||||
#define CMD_RX_DIS 0x13
|
||||
#define CMD_RX_CRC_INIT 0x14
|
||||
#define CMD_RX_MSG_REJ 0x15
|
||||
#define CMD_RX_MP_SRCH 0x16
|
||||
#define CMD_RX_CRC_EXC 0x17
|
||||
#define CMD_RX_CRC_FRC 0x18
|
||||
#define CMD_TX_RST 0x01
|
||||
#define CMD_TX_ENA 0x02
|
||||
#define CMD_TX_DISA 0x03
|
||||
#define CMD_TX_CRC_INIT 0x04
|
||||
#define CMD_TX_CRC_EXC 0x05
|
||||
#define CMD_TX_EOM 0x06
|
||||
#define CMD_TX_ABORT 0x07
|
||||
#define CMD_TX_MP_ON 0x08
|
||||
#define CMD_TX_BUF_CLR 0x09
|
||||
#define CMD_TX_DISB 0x0b
|
||||
#define CMD_CH_RST 0x21
|
||||
#define CMD_SRCH_MODE 0x31
|
||||
#define CMD_NOP 0x00
|
||||
|
||||
#define CMD_RESET 0x21
|
||||
#define CMD_TX_ENABLE 0x02
|
||||
#define CMD_RX_ENABLE 0x12
|
||||
|
||||
#define ST0_RXRDY 0x01
|
||||
#define ST0_TXRDY 0x02
|
||||
#define ST0_RXINTB 0x20
|
||||
#define ST0_RXINTA 0x40
|
||||
#define ST0_TXINT 0x80
|
||||
|
||||
#define ST1_IDLE 0x01
|
||||
#define ST1_ABORT 0x02
|
||||
#define ST1_CDCD 0x04
|
||||
#define ST1_CCTS 0x08
|
||||
#define ST1_SYN_FLAG 0x10
|
||||
#define ST1_CLMD 0x20
|
||||
#define ST1_TXIDLE 0x40
|
||||
#define ST1_UDRN 0x80
|
||||
|
||||
#define ST2_CRCE 0x04
|
||||
#define ST2_ONRN 0x08
|
||||
#define ST2_RBIT 0x10
|
||||
#define ST2_ABORT 0x20
|
||||
#define ST2_SHORT 0x40
|
||||
#define ST2_EOM 0x80
|
||||
|
||||
#define ST3_RX_ENA 0x01
|
||||
#define ST3_TX_ENA 0x02
|
||||
#define ST3_DCD 0x04
|
||||
#define ST3_CTS 0x08
|
||||
#define ST3_SRCH_MODE 0x10
|
||||
#define ST3_SLOOP 0x20
|
||||
#define ST3_GPI 0x80
|
||||
|
||||
#define ST4_RDNR 0x01
|
||||
#define ST4_RDCR 0x02
|
||||
#define ST4_TDNR 0x04
|
||||
#define ST4_TDCR 0x08
|
||||
#define ST4_OCLM 0x20
|
||||
#define ST4_CFT 0x40
|
||||
#define ST4_CGPI 0x80
|
||||
|
||||
#define FST_CRCEF 0x04
|
||||
#define FST_OVRNF 0x08
|
||||
#define FST_RBIF 0x10
|
||||
#define FST_ABTF 0x20
|
||||
#define FST_SHRTF 0x40
|
||||
#define FST_EOMF 0x80
|
||||
|
||||
#define IE0_RXRDY 0x01
|
||||
#define IE0_TXRDY 0x02
|
||||
#define IE0_RXINTB 0x20
|
||||
#define IE0_RXINTA 0x40
|
||||
#define IE0_TXINT 0x80
|
||||
#define IE0_UDRN 0x00008000 /* TX underrun MSCI interrupt enable */
|
||||
#define IE0_CDCD 0x00000400 /* CD level change interrupt enable */
|
||||
|
||||
#define IE1_IDLD 0x01
|
||||
#define IE1_ABTD 0x02
|
||||
#define IE1_CDCD 0x04
|
||||
#define IE1_CCTS 0x08
|
||||
#define IE1_SYNCD 0x10
|
||||
#define IE1_CLMD 0x20
|
||||
#define IE1_IDL 0x40
|
||||
#define IE1_UDRN 0x80
|
||||
|
||||
#define IE2_CRCE 0x04
|
||||
#define IE2_OVRN 0x08
|
||||
#define IE2_RBIT 0x10
|
||||
#define IE2_ABT 0x20
|
||||
#define IE2_SHRT 0x40
|
||||
#define IE2_EOM 0x80
|
||||
|
||||
#define IE4_RDNR 0x01
|
||||
#define IE4_RDCR 0x02
|
||||
#define IE4_TDNR 0x04
|
||||
#define IE4_TDCR 0x08
|
||||
#define IE4_OCLM 0x20
|
||||
#define IE4_CFT 0x40
|
||||
#define IE4_CGPI 0x80
|
||||
|
||||
#define FIE_CRCEF 0x04
|
||||
#define FIE_OVRNF 0x08
|
||||
#define FIE_RBIF 0x10
|
||||
#define FIE_ABTF 0x20
|
||||
#define FIE_SHRTF 0x40
|
||||
#define FIE_EOMF 0x80
|
||||
|
||||
#define DSR_DWE 0x01
|
||||
#define DSR_DE 0x02
|
||||
#define DSR_REF 0x04
|
||||
#define DSR_UDRF 0x04
|
||||
#define DSR_COA 0x08
|
||||
#define DSR_COF 0x10
|
||||
#define DSR_BOF 0x20
|
||||
#define DSR_EOM 0x40
|
||||
#define DSR_EOT 0x80
|
||||
|
||||
#define DIR_REF 0x04
|
||||
#define DIR_UDRF 0x04
|
||||
#define DIR_COA 0x08
|
||||
#define DIR_COF 0x10
|
||||
#define DIR_BOF 0x20
|
||||
#define DIR_EOM 0x40
|
||||
#define DIR_EOT 0x80
|
||||
|
||||
#define DIR_REFE 0x04
|
||||
#define DIR_UDRFE 0x04
|
||||
#define DIR_COAE 0x08
|
||||
#define DIR_COFE 0x10
|
||||
#define DIR_BOFE 0x20
|
||||
#define DIR_EOME 0x40
|
||||
#define DIR_EOTE 0x80
|
||||
|
||||
#define DMR_CNTE 0x02
|
||||
#define DMR_NF 0x04
|
||||
#define DMR_SEOME 0x08
|
||||
#define DMR_TMOD 0x10
|
||||
|
||||
#define DMER_DME 0x80 /* DMA Master Enable */
|
||||
|
||||
#define DCR_SW_ABT 0x01
|
||||
#define DCR_FCT_CLR 0x02
|
||||
|
||||
#define DCR_ABORT 0x01
|
||||
#define DCR_CLEAR_EOF 0x02
|
||||
|
||||
#define PCR_COTE 0x80
|
||||
#define PCR_PR0 0x01
|
||||
#define PCR_PR1 0x02
|
||||
#define PCR_PR2 0x04
|
||||
#define PCR_CCC 0x08
|
||||
#define PCR_BRC 0x10
|
||||
#define PCR_OSB 0x40
|
||||
#define PCR_BURST 0x80
|
||||
|
||||
#endif /* (__HD64572_H) */
|
||||
867
drivers/net/wan/hd6457x.c
Normal file
867
drivers/net/wan/hd6457x.c
Normal file
@@ -0,0 +1,867 @@
|
||||
/*
|
||||
* Hitachi SCA HD64570 and HD64572 common driver for Linux
|
||||
*
|
||||
* Copyright (C) 1998-2003 Krzysztof Halasa <khc@pm.waw.pl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* Sources of information:
|
||||
* Hitachi HD64570 SCA User's Manual
|
||||
* Hitachi HD64572 SCA-II User's Manual
|
||||
*
|
||||
* We use the following SCA memory map:
|
||||
*
|
||||
* Packet buffer descriptor rings - starting from winbase or win0base:
|
||||
* rx_ring_buffers * sizeof(pkt_desc) = logical channel #0 RX ring
|
||||
* tx_ring_buffers * sizeof(pkt_desc) = logical channel #0 TX ring
|
||||
* rx_ring_buffers * sizeof(pkt_desc) = logical channel #1 RX ring (if used)
|
||||
* tx_ring_buffers * sizeof(pkt_desc) = logical channel #1 TX ring (if used)
|
||||
*
|
||||
* Packet data buffers - starting from winbase + buff_offset:
|
||||
* rx_ring_buffers * HDLC_MAX_MRU = logical channel #0 RX buffers
|
||||
* tx_ring_buffers * HDLC_MAX_MRU = logical channel #0 TX buffers
|
||||
* rx_ring_buffers * HDLC_MAX_MRU = logical channel #0 RX buffers (if used)
|
||||
* tx_ring_buffers * HDLC_MAX_MRU = logical channel #0 TX buffers (if used)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <linux/hdlc.h>
|
||||
|
||||
#if (!defined (__HD64570_H) && !defined (__HD64572_H)) || \
|
||||
(defined (__HD64570_H) && defined (__HD64572_H))
|
||||
#error Either hd64570.h or hd64572.h must be included
|
||||
#endif
|
||||
|
||||
#define get_msci(port) (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET)
|
||||
#define get_dmac_rx(port) (phy_node(port) ? DMAC1RX_OFFSET : DMAC0RX_OFFSET)
|
||||
#define get_dmac_tx(port) (phy_node(port) ? DMAC1TX_OFFSET : DMAC0TX_OFFSET)
|
||||
|
||||
#define SCA_INTR_MSCI(node) (node ? 0x10 : 0x01)
|
||||
#define SCA_INTR_DMAC_RX(node) (node ? 0x20 : 0x02)
|
||||
#define SCA_INTR_DMAC_TX(node) (node ? 0x40 : 0x04)
|
||||
|
||||
#ifdef __HD64570_H /* HD64570 */
|
||||
#define sca_outa(value, reg, card) sca_outw(value, reg, card)
|
||||
#define sca_ina(reg, card) sca_inw(reg, card)
|
||||
#define writea(value, ptr) writew(value, ptr)
|
||||
|
||||
#else /* HD64572 */
|
||||
#define sca_outa(value, reg, card) sca_outl(value, reg, card)
|
||||
#define sca_ina(reg, card) sca_inl(reg, card)
|
||||
#define writea(value, ptr) writel(value, ptr)
|
||||
#endif
|
||||
|
||||
static inline struct net_device *port_to_dev(port_t *port)
|
||||
{
|
||||
return port->dev;
|
||||
}
|
||||
|
||||
static inline int sca_intr_status(card_t *card)
|
||||
{
|
||||
u8 result = 0;
|
||||
|
||||
#ifdef __HD64570_H /* HD64570 */
|
||||
u8 isr0 = sca_in(ISR0, card);
|
||||
u8 isr1 = sca_in(ISR1, card);
|
||||
|
||||
if (isr1 & 0x03) result |= SCA_INTR_DMAC_RX(0);
|
||||
if (isr1 & 0x0C) result |= SCA_INTR_DMAC_TX(0);
|
||||
if (isr1 & 0x30) result |= SCA_INTR_DMAC_RX(1);
|
||||
if (isr1 & 0xC0) result |= SCA_INTR_DMAC_TX(1);
|
||||
if (isr0 & 0x0F) result |= SCA_INTR_MSCI(0);
|
||||
if (isr0 & 0xF0) result |= SCA_INTR_MSCI(1);
|
||||
|
||||
#else /* HD64572 */
|
||||
u32 isr0 = sca_inl(ISR0, card);
|
||||
|
||||
if (isr0 & 0x0000000F) result |= SCA_INTR_DMAC_RX(0);
|
||||
if (isr0 & 0x000000F0) result |= SCA_INTR_DMAC_TX(0);
|
||||
if (isr0 & 0x00000F00) result |= SCA_INTR_DMAC_RX(1);
|
||||
if (isr0 & 0x0000F000) result |= SCA_INTR_DMAC_TX(1);
|
||||
if (isr0 & 0x003E0000) result |= SCA_INTR_MSCI(0);
|
||||
if (isr0 & 0x3E000000) result |= SCA_INTR_MSCI(1);
|
||||
|
||||
#endif /* HD64570 vs HD64572 */
|
||||
|
||||
if (!(result & SCA_INTR_DMAC_TX(0)))
|
||||
if (sca_in(DSR_TX(0), card) & DSR_EOM)
|
||||
result |= SCA_INTR_DMAC_TX(0);
|
||||
if (!(result & SCA_INTR_DMAC_TX(1)))
|
||||
if (sca_in(DSR_TX(1), card) & DSR_EOM)
|
||||
result |= SCA_INTR_DMAC_TX(1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline port_t* dev_to_port(struct net_device *dev)
|
||||
{
|
||||
return dev_to_hdlc(dev)->priv;
|
||||
}
|
||||
|
||||
static inline u16 next_desc(port_t *port, u16 desc, int transmit)
|
||||
{
|
||||
return (desc + 1) % (transmit ? port_to_card(port)->tx_ring_buffers
|
||||
: port_to_card(port)->rx_ring_buffers);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline u16 desc_abs_number(port_t *port, u16 desc, int transmit)
|
||||
{
|
||||
u16 rx_buffs = port_to_card(port)->rx_ring_buffers;
|
||||
u16 tx_buffs = port_to_card(port)->tx_ring_buffers;
|
||||
|
||||
desc %= (transmit ? tx_buffs : rx_buffs); // called with "X + 1" etc.
|
||||
return log_node(port) * (rx_buffs + tx_buffs) +
|
||||
transmit * rx_buffs + desc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline u16 desc_offset(port_t *port, u16 desc, int transmit)
|
||||
{
|
||||
/* Descriptor offset always fits in 16 bytes */
|
||||
return desc_abs_number(port, desc, transmit) * sizeof(pkt_desc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline pkt_desc __iomem *desc_address(port_t *port, u16 desc, int transmit)
|
||||
{
|
||||
#ifdef PAGE0_ALWAYS_MAPPED
|
||||
return (pkt_desc __iomem *)(win0base(port_to_card(port))
|
||||
+ desc_offset(port, desc, transmit));
|
||||
#else
|
||||
return (pkt_desc __iomem *)(winbase(port_to_card(port))
|
||||
+ desc_offset(port, desc, transmit));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline u32 buffer_offset(port_t *port, u16 desc, int transmit)
|
||||
{
|
||||
return port_to_card(port)->buff_offset +
|
||||
desc_abs_number(port, desc, transmit) * (u32)HDLC_MAX_MRU;
|
||||
}
|
||||
|
||||
|
||||
static inline void sca_set_carrier(port_t *port)
|
||||
{
|
||||
if (!(sca_in(get_msci(port) + ST3, port_to_card(port)) & ST3_DCD)) {
|
||||
#ifdef DEBUG_LINK
|
||||
printk(KERN_DEBUG "%s: sca_set_carrier on\n",
|
||||
port_to_dev(port)->name);
|
||||
#endif
|
||||
netif_carrier_on(port_to_dev(port));
|
||||
} else {
|
||||
#ifdef DEBUG_LINK
|
||||
printk(KERN_DEBUG "%s: sca_set_carrier off\n",
|
||||
port_to_dev(port)->name);
|
||||
#endif
|
||||
netif_carrier_off(port_to_dev(port));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void sca_init_sync_port(port_t *port)
|
||||
{
|
||||
card_t *card = port_to_card(port);
|
||||
int transmit, i;
|
||||
|
||||
port->rxin = 0;
|
||||
port->txin = 0;
|
||||
port->txlast = 0;
|
||||
|
||||
#if !defined(PAGE0_ALWAYS_MAPPED) && !defined(ALL_PAGES_ALWAYS_MAPPED)
|
||||
openwin(card, 0);
|
||||
#endif
|
||||
|
||||
for (transmit = 0; transmit < 2; transmit++) {
|
||||
u16 dmac = transmit ? get_dmac_tx(port) : get_dmac_rx(port);
|
||||
u16 buffs = transmit ? card->tx_ring_buffers
|
||||
: card->rx_ring_buffers;
|
||||
|
||||
for (i = 0; i < buffs; i++) {
|
||||
pkt_desc __iomem *desc = desc_address(port, i, transmit);
|
||||
u16 chain_off = desc_offset(port, i + 1, transmit);
|
||||
u32 buff_off = buffer_offset(port, i, transmit);
|
||||
|
||||
writea(chain_off, &desc->cp);
|
||||
writel(buff_off, &desc->bp);
|
||||
writew(0, &desc->len);
|
||||
writeb(0, &desc->stat);
|
||||
}
|
||||
|
||||
/* DMA disable - to halt state */
|
||||
sca_out(0, transmit ? DSR_TX(phy_node(port)) :
|
||||
DSR_RX(phy_node(port)), card);
|
||||
/* software ABORT - to initial state */
|
||||
sca_out(DCR_ABORT, transmit ? DCR_TX(phy_node(port)) :
|
||||
DCR_RX(phy_node(port)), card);
|
||||
|
||||
#ifdef __HD64570_H
|
||||
sca_out(0, dmac + CPB, card); /* pointer base */
|
||||
#endif
|
||||
/* current desc addr */
|
||||
sca_outa(desc_offset(port, 0, transmit), dmac + CDAL, card);
|
||||
if (!transmit)
|
||||
sca_outa(desc_offset(port, buffs - 1, transmit),
|
||||
dmac + EDAL, card);
|
||||
else
|
||||
sca_outa(desc_offset(port, 0, transmit), dmac + EDAL,
|
||||
card);
|
||||
|
||||
/* clear frame end interrupt counter */
|
||||
sca_out(DCR_CLEAR_EOF, transmit ? DCR_TX(phy_node(port)) :
|
||||
DCR_RX(phy_node(port)), card);
|
||||
|
||||
if (!transmit) { /* Receive */
|
||||
/* set buffer length */
|
||||
sca_outw(HDLC_MAX_MRU, dmac + BFLL, card);
|
||||
/* Chain mode, Multi-frame */
|
||||
sca_out(0x14, DMR_RX(phy_node(port)), card);
|
||||
sca_out(DIR_EOME | DIR_BOFE, DIR_RX(phy_node(port)),
|
||||
card);
|
||||
/* DMA enable */
|
||||
sca_out(DSR_DE, DSR_RX(phy_node(port)), card);
|
||||
} else { /* Transmit */
|
||||
/* Chain mode, Multi-frame */
|
||||
sca_out(0x14, DMR_TX(phy_node(port)), card);
|
||||
/* enable underflow interrupts */
|
||||
sca_out(DIR_BOFE, DIR_TX(phy_node(port)), card);
|
||||
}
|
||||
}
|
||||
sca_set_carrier(port);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef NEED_SCA_MSCI_INTR
|
||||
/* MSCI interrupt service */
|
||||
static inline void sca_msci_intr(port_t *port)
|
||||
{
|
||||
u16 msci = get_msci(port);
|
||||
card_t* card = port_to_card(port);
|
||||
u8 stat = sca_in(msci + ST1, card); /* read MSCI ST1 status */
|
||||
|
||||
/* Reset MSCI TX underrun and CDCD status bit */
|
||||
sca_out(stat & (ST1_UDRN | ST1_CDCD), msci + ST1, card);
|
||||
|
||||
if (stat & ST1_UDRN) {
|
||||
struct net_device_stats *stats = hdlc_stats(port_to_dev(port));
|
||||
stats->tx_errors++; /* TX Underrun error detected */
|
||||
stats->tx_fifo_errors++;
|
||||
}
|
||||
|
||||
if (stat & ST1_CDCD)
|
||||
sca_set_carrier(port);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static inline void sca_rx(card_t *card, port_t *port, pkt_desc __iomem *desc, u16 rxin)
|
||||
{
|
||||
struct net_device *dev = port_to_dev(port);
|
||||
struct net_device_stats *stats = hdlc_stats(dev);
|
||||
struct sk_buff *skb;
|
||||
u16 len;
|
||||
u32 buff;
|
||||
#ifndef ALL_PAGES_ALWAYS_MAPPED
|
||||
u32 maxlen;
|
||||
u8 page;
|
||||
#endif
|
||||
|
||||
len = readw(&desc->len);
|
||||
skb = dev_alloc_skb(len);
|
||||
if (!skb) {
|
||||
stats->rx_dropped++;
|
||||
return;
|
||||
}
|
||||
|
||||
buff = buffer_offset(port, rxin, 0);
|
||||
#ifndef ALL_PAGES_ALWAYS_MAPPED
|
||||
page = buff / winsize(card);
|
||||
buff = buff % winsize(card);
|
||||
maxlen = winsize(card) - buff;
|
||||
|
||||
openwin(card, page);
|
||||
|
||||
if (len > maxlen) {
|
||||
memcpy_fromio(skb->data, winbase(card) + buff, maxlen);
|
||||
openwin(card, page + 1);
|
||||
memcpy_fromio(skb->data + maxlen, winbase(card), len - maxlen);
|
||||
} else
|
||||
#endif
|
||||
memcpy_fromio(skb->data, winbase(card) + buff, len);
|
||||
|
||||
#if !defined(PAGE0_ALWAYS_MAPPED) && !defined(ALL_PAGES_ALWAYS_MAPPED)
|
||||
/* select pkt_desc table page back */
|
||||
openwin(card, 0);
|
||||
#endif
|
||||
skb_put(skb, len);
|
||||
#ifdef DEBUG_PKT
|
||||
printk(KERN_DEBUG "%s RX(%i):", dev->name, skb->len);
|
||||
debug_frame(skb);
|
||||
#endif
|
||||
stats->rx_packets++;
|
||||
stats->rx_bytes += skb->len;
|
||||
dev->last_rx = jiffies;
|
||||
skb->protocol = hdlc_type_trans(skb, dev);
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Receive DMA interrupt service */
|
||||
static inline void sca_rx_intr(port_t *port)
|
||||
{
|
||||
u16 dmac = get_dmac_rx(port);
|
||||
card_t *card = port_to_card(port);
|
||||
u8 stat = sca_in(DSR_RX(phy_node(port)), card); /* read DMA Status */
|
||||
struct net_device_stats *stats = hdlc_stats(port_to_dev(port));
|
||||
|
||||
/* Reset DSR status bits */
|
||||
sca_out((stat & (DSR_EOT | DSR_EOM | DSR_BOF | DSR_COF)) | DSR_DWE,
|
||||
DSR_RX(phy_node(port)), card);
|
||||
|
||||
if (stat & DSR_BOF)
|
||||
stats->rx_over_errors++; /* Dropped one or more frames */
|
||||
|
||||
while (1) {
|
||||
u32 desc_off = desc_offset(port, port->rxin, 0);
|
||||
pkt_desc __iomem *desc;
|
||||
u32 cda = sca_ina(dmac + CDAL, card);
|
||||
|
||||
if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc)))
|
||||
break; /* No frame received */
|
||||
|
||||
desc = desc_address(port, port->rxin, 0);
|
||||
stat = readb(&desc->stat);
|
||||
if (!(stat & ST_RX_EOM))
|
||||
port->rxpart = 1; /* partial frame received */
|
||||
else if ((stat & ST_ERROR_MASK) || port->rxpart) {
|
||||
stats->rx_errors++;
|
||||
if (stat & ST_RX_OVERRUN) stats->rx_fifo_errors++;
|
||||
else if ((stat & (ST_RX_SHORT | ST_RX_ABORT |
|
||||
ST_RX_RESBIT)) || port->rxpart)
|
||||
stats->rx_frame_errors++;
|
||||
else if (stat & ST_RX_CRC) stats->rx_crc_errors++;
|
||||
if (stat & ST_RX_EOM)
|
||||
port->rxpart = 0; /* received last fragment */
|
||||
} else
|
||||
sca_rx(card, port, desc, port->rxin);
|
||||
|
||||
/* Set new error descriptor address */
|
||||
sca_outa(desc_off, dmac + EDAL, card);
|
||||
port->rxin = next_desc(port, port->rxin, 0);
|
||||
}
|
||||
|
||||
/* make sure RX DMA is enabled */
|
||||
sca_out(DSR_DE, DSR_RX(phy_node(port)), card);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Transmit DMA interrupt service */
|
||||
static inline void sca_tx_intr(port_t *port)
|
||||
{
|
||||
struct net_device *dev = port_to_dev(port);
|
||||
struct net_device_stats *stats = hdlc_stats(dev);
|
||||
u16 dmac = get_dmac_tx(port);
|
||||
card_t* card = port_to_card(port);
|
||||
u8 stat;
|
||||
|
||||
spin_lock(&port->lock);
|
||||
|
||||
stat = sca_in(DSR_TX(phy_node(port)), card); /* read DMA Status */
|
||||
|
||||
/* Reset DSR status bits */
|
||||
sca_out((stat & (DSR_EOT | DSR_EOM | DSR_BOF | DSR_COF)) | DSR_DWE,
|
||||
DSR_TX(phy_node(port)), card);
|
||||
|
||||
while (1) {
|
||||
pkt_desc __iomem *desc;
|
||||
|
||||
u32 desc_off = desc_offset(port, port->txlast, 1);
|
||||
u32 cda = sca_ina(dmac + CDAL, card);
|
||||
if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc)))
|
||||
break; /* Transmitter is/will_be sending this frame */
|
||||
|
||||
desc = desc_address(port, port->txlast, 1);
|
||||
stats->tx_packets++;
|
||||
stats->tx_bytes += readw(&desc->len);
|
||||
writeb(0, &desc->stat); /* Free descriptor */
|
||||
port->txlast = next_desc(port, port->txlast, 1);
|
||||
}
|
||||
|
||||
netif_wake_queue(dev);
|
||||
spin_unlock(&port->lock);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static irqreturn_t sca_intr(int irq, void* dev_id)
|
||||
{
|
||||
card_t *card = dev_id;
|
||||
int i;
|
||||
u8 stat;
|
||||
int handled = 0;
|
||||
|
||||
#ifndef ALL_PAGES_ALWAYS_MAPPED
|
||||
u8 page = sca_get_page(card);
|
||||
#endif
|
||||
|
||||
while((stat = sca_intr_status(card)) != 0) {
|
||||
handled = 1;
|
||||
for (i = 0; i < 2; i++) {
|
||||
port_t *port = get_port(card, i);
|
||||
if (port) {
|
||||
if (stat & SCA_INTR_MSCI(i))
|
||||
sca_msci_intr(port);
|
||||
|
||||
if (stat & SCA_INTR_DMAC_RX(i))
|
||||
sca_rx_intr(port);
|
||||
|
||||
if (stat & SCA_INTR_DMAC_TX(i))
|
||||
sca_tx_intr(port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ALL_PAGES_ALWAYS_MAPPED
|
||||
openwin(card, page); /* Restore original page */
|
||||
#endif
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void sca_set_port(port_t *port)
|
||||
{
|
||||
card_t* card = port_to_card(port);
|
||||
u16 msci = get_msci(port);
|
||||
u8 md2 = sca_in(msci + MD2, card);
|
||||
unsigned int tmc, br = 10, brv = 1024;
|
||||
|
||||
|
||||
if (port->settings.clock_rate > 0) {
|
||||
/* Try lower br for better accuracy*/
|
||||
do {
|
||||
br--;
|
||||
brv >>= 1; /* brv = 2^9 = 512 max in specs */
|
||||
|
||||
/* Baud Rate = CLOCK_BASE / TMC / 2^BR */
|
||||
tmc = CLOCK_BASE / brv / port->settings.clock_rate;
|
||||
}while (br > 1 && tmc <= 128);
|
||||
|
||||
if (tmc < 1) {
|
||||
tmc = 1;
|
||||
br = 0; /* For baud=CLOCK_BASE we use tmc=1 br=0 */
|
||||
brv = 1;
|
||||
} else if (tmc > 255)
|
||||
tmc = 256; /* tmc=0 means 256 - low baud rates */
|
||||
|
||||
port->settings.clock_rate = CLOCK_BASE / brv / tmc;
|
||||
} else {
|
||||
br = 9; /* Minimum clock rate */
|
||||
tmc = 256; /* 8bit = 0 */
|
||||
port->settings.clock_rate = CLOCK_BASE / (256 * 512);
|
||||
}
|
||||
|
||||
port->rxs = (port->rxs & ~CLK_BRG_MASK) | br;
|
||||
port->txs = (port->txs & ~CLK_BRG_MASK) | br;
|
||||
port->tmc = tmc;
|
||||
|
||||
/* baud divisor - time constant*/
|
||||
#ifdef __HD64570_H
|
||||
sca_out(port->tmc, msci + TMC, card);
|
||||
#else
|
||||
sca_out(port->tmc, msci + TMCR, card);
|
||||
sca_out(port->tmc, msci + TMCT, card);
|
||||
#endif
|
||||
|
||||
/* Set BRG bits */
|
||||
sca_out(port->rxs, msci + RXS, card);
|
||||
sca_out(port->txs, msci + TXS, card);
|
||||
|
||||
if (port->settings.loopback)
|
||||
md2 |= MD2_LOOPBACK;
|
||||
else
|
||||
md2 &= ~MD2_LOOPBACK;
|
||||
|
||||
sca_out(md2, msci + MD2, card);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void sca_open(struct net_device *dev)
|
||||
{
|
||||
port_t *port = dev_to_port(dev);
|
||||
card_t* card = port_to_card(port);
|
||||
u16 msci = get_msci(port);
|
||||
u8 md0, md2;
|
||||
|
||||
switch(port->encoding) {
|
||||
case ENCODING_NRZ: md2 = MD2_NRZ; break;
|
||||
case ENCODING_NRZI: md2 = MD2_NRZI; break;
|
||||
case ENCODING_FM_MARK: md2 = MD2_FM_MARK; break;
|
||||
case ENCODING_FM_SPACE: md2 = MD2_FM_SPACE; break;
|
||||
default: md2 = MD2_MANCHESTER;
|
||||
}
|
||||
|
||||
if (port->settings.loopback)
|
||||
md2 |= MD2_LOOPBACK;
|
||||
|
||||
switch(port->parity) {
|
||||
case PARITY_CRC16_PR0: md0 = MD0_HDLC | MD0_CRC_16_0; break;
|
||||
case PARITY_CRC16_PR1: md0 = MD0_HDLC | MD0_CRC_16; break;
|
||||
#ifdef __HD64570_H
|
||||
case PARITY_CRC16_PR0_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU_0; break;
|
||||
#else
|
||||
case PARITY_CRC32_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU32; break;
|
||||
#endif
|
||||
case PARITY_CRC16_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU; break;
|
||||
default: md0 = MD0_HDLC | MD0_CRC_NONE;
|
||||
}
|
||||
|
||||
sca_out(CMD_RESET, msci + CMD, card);
|
||||
sca_out(md0, msci + MD0, card);
|
||||
sca_out(0x00, msci + MD1, card); /* no address field check */
|
||||
sca_out(md2, msci + MD2, card);
|
||||
sca_out(0x7E, msci + IDL, card); /* flag character 0x7E */
|
||||
#ifdef __HD64570_H
|
||||
sca_out(CTL_IDLE, msci + CTL, card);
|
||||
#else
|
||||
/* Skip the rest of underrun frame */
|
||||
sca_out(CTL_IDLE | CTL_URCT | CTL_URSKP, msci + CTL, card);
|
||||
#endif
|
||||
|
||||
#ifdef __HD64570_H
|
||||
/* Allow at least 8 bytes before requesting RX DMA operation */
|
||||
/* TX with higher priority and possibly with shorter transfers */
|
||||
sca_out(0x07, msci + RRC, card); /* +1=RXRDY/DMA activation condition*/
|
||||
sca_out(0x10, msci + TRC0, card); /* = TXRDY/DMA activation condition*/
|
||||
sca_out(0x14, msci + TRC1, card); /* +1=TXRDY/DMA deactiv condition */
|
||||
#else
|
||||
sca_out(0x0F, msci + RNR, card); /* +1=RX DMA activation condition */
|
||||
sca_out(0x3C, msci + TFS, card); /* +1 = TX start */
|
||||
sca_out(0x38, msci + TCR, card); /* =Critical TX DMA activ condition */
|
||||
sca_out(0x38, msci + TNR0, card); /* =TX DMA activation condition */
|
||||
sca_out(0x3F, msci + TNR1, card); /* +1=TX DMA deactivation condition*/
|
||||
#endif
|
||||
|
||||
/* We're using the following interrupts:
|
||||
- TXINT (DMAC completed all transmisions, underrun or DCD change)
|
||||
- all DMA interrupts
|
||||
*/
|
||||
|
||||
sca_set_carrier(port);
|
||||
|
||||
#ifdef __HD64570_H
|
||||
/* MSCI TX INT and RX INT A IRQ enable */
|
||||
sca_out(IE0_TXINT | IE0_RXINTA, msci + IE0, card);
|
||||
sca_out(IE1_UDRN | IE1_CDCD, msci + IE1, card);
|
||||
sca_out(sca_in(IER0, card) | (phy_node(port) ? 0xC0 : 0x0C),
|
||||
IER0, card); /* TXINT and RXINT */
|
||||
/* enable DMA IRQ */
|
||||
sca_out(sca_in(IER1, card) | (phy_node(port) ? 0xF0 : 0x0F),
|
||||
IER1, card);
|
||||
#else
|
||||
/* MSCI TXINT and RXINTA interrupt enable */
|
||||
sca_outl(IE0_TXINT | IE0_RXINTA | IE0_UDRN | IE0_CDCD, msci + IE0,
|
||||
card);
|
||||
/* DMA & MSCI IRQ enable */
|
||||
sca_outl(sca_inl(IER0, card) |
|
||||
(phy_node(port) ? 0x0A006600 : 0x000A0066), IER0, card);
|
||||
#endif
|
||||
|
||||
#ifdef __HD64570_H
|
||||
sca_out(port->tmc, msci + TMC, card); /* Restore registers */
|
||||
#else
|
||||
sca_out(port->tmc, msci + TMCR, card);
|
||||
sca_out(port->tmc, msci + TMCT, card);
|
||||
#endif
|
||||
sca_out(port->rxs, msci + RXS, card);
|
||||
sca_out(port->txs, msci + TXS, card);
|
||||
sca_out(CMD_TX_ENABLE, msci + CMD, card);
|
||||
sca_out(CMD_RX_ENABLE, msci + CMD, card);
|
||||
|
||||
netif_start_queue(dev);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void sca_close(struct net_device *dev)
|
||||
{
|
||||
port_t *port = dev_to_port(dev);
|
||||
card_t* card = port_to_card(port);
|
||||
|
||||
/* reset channel */
|
||||
sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port));
|
||||
#ifdef __HD64570_H
|
||||
/* disable MSCI interrupts */
|
||||
sca_out(sca_in(IER0, card) & (phy_node(port) ? 0x0F : 0xF0),
|
||||
IER0, card);
|
||||
/* disable DMA interrupts */
|
||||
sca_out(sca_in(IER1, card) & (phy_node(port) ? 0x0F : 0xF0),
|
||||
IER1, card);
|
||||
#else
|
||||
/* disable DMA & MSCI IRQ */
|
||||
sca_outl(sca_inl(IER0, card) &
|
||||
(phy_node(port) ? 0x00FF00FF : 0xFF00FF00), IER0, card);
|
||||
#endif
|
||||
netif_stop_queue(dev);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int sca_attach(struct net_device *dev, unsigned short encoding,
|
||||
unsigned short parity)
|
||||
{
|
||||
if (encoding != ENCODING_NRZ &&
|
||||
encoding != ENCODING_NRZI &&
|
||||
encoding != ENCODING_FM_MARK &&
|
||||
encoding != ENCODING_FM_SPACE &&
|
||||
encoding != ENCODING_MANCHESTER)
|
||||
return -EINVAL;
|
||||
|
||||
if (parity != PARITY_NONE &&
|
||||
parity != PARITY_CRC16_PR0 &&
|
||||
parity != PARITY_CRC16_PR1 &&
|
||||
#ifdef __HD64570_H
|
||||
parity != PARITY_CRC16_PR0_CCITT &&
|
||||
#else
|
||||
parity != PARITY_CRC32_PR1_CCITT &&
|
||||
#endif
|
||||
parity != PARITY_CRC16_PR1_CCITT)
|
||||
return -EINVAL;
|
||||
|
||||
dev_to_port(dev)->encoding = encoding;
|
||||
dev_to_port(dev)->parity = parity;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef DEBUG_RINGS
|
||||
static void sca_dump_rings(struct net_device *dev)
|
||||
{
|
||||
port_t *port = dev_to_port(dev);
|
||||
card_t *card = port_to_card(port);
|
||||
u16 cnt;
|
||||
#if !defined(PAGE0_ALWAYS_MAPPED) && !defined(ALL_PAGES_ALWAYS_MAPPED)
|
||||
u8 page;
|
||||
#endif
|
||||
|
||||
#if !defined(PAGE0_ALWAYS_MAPPED) && !defined(ALL_PAGES_ALWAYS_MAPPED)
|
||||
page = sca_get_page(card);
|
||||
openwin(card, 0);
|
||||
#endif
|
||||
|
||||
printk(KERN_DEBUG "RX ring: CDA=%u EDA=%u DSR=%02X in=%u %sactive",
|
||||
sca_ina(get_dmac_rx(port) + CDAL, card),
|
||||
sca_ina(get_dmac_rx(port) + EDAL, card),
|
||||
sca_in(DSR_RX(phy_node(port)), card), port->rxin,
|
||||
sca_in(DSR_RX(phy_node(port)), card) & DSR_DE?"":"in");
|
||||
for (cnt = 0; cnt < port_to_card(port)->rx_ring_buffers; cnt++)
|
||||
printk(" %02X", readb(&(desc_address(port, cnt, 0)->stat)));
|
||||
|
||||
printk("\n" KERN_DEBUG "TX ring: CDA=%u EDA=%u DSR=%02X in=%u "
|
||||
"last=%u %sactive",
|
||||
sca_ina(get_dmac_tx(port) + CDAL, card),
|
||||
sca_ina(get_dmac_tx(port) + EDAL, card),
|
||||
sca_in(DSR_TX(phy_node(port)), card), port->txin, port->txlast,
|
||||
sca_in(DSR_TX(phy_node(port)), card) & DSR_DE ? "" : "in");
|
||||
|
||||
for (cnt = 0; cnt < port_to_card(port)->tx_ring_buffers; cnt++)
|
||||
printk(" %02X", readb(&(desc_address(port, cnt, 1)->stat)));
|
||||
printk("\n");
|
||||
|
||||
printk(KERN_DEBUG "MSCI: MD: %02x %02x %02x, "
|
||||
"ST: %02x %02x %02x %02x"
|
||||
#ifdef __HD64572_H
|
||||
" %02x"
|
||||
#endif
|
||||
", FST: %02x CST: %02x %02x\n",
|
||||
sca_in(get_msci(port) + MD0, card),
|
||||
sca_in(get_msci(port) + MD1, card),
|
||||
sca_in(get_msci(port) + MD2, card),
|
||||
sca_in(get_msci(port) + ST0, card),
|
||||
sca_in(get_msci(port) + ST1, card),
|
||||
sca_in(get_msci(port) + ST2, card),
|
||||
sca_in(get_msci(port) + ST3, card),
|
||||
#ifdef __HD64572_H
|
||||
sca_in(get_msci(port) + ST4, card),
|
||||
#endif
|
||||
sca_in(get_msci(port) + FST, card),
|
||||
sca_in(get_msci(port) + CST0, card),
|
||||
sca_in(get_msci(port) + CST1, card));
|
||||
|
||||
#ifdef __HD64572_H
|
||||
printk(KERN_DEBUG "ILAR: %02x ISR: %08x %08x\n", sca_in(ILAR, card),
|
||||
sca_inl(ISR0, card), sca_inl(ISR1, card));
|
||||
#else
|
||||
printk(KERN_DEBUG "ISR: %02x %02x %02x\n", sca_in(ISR0, card),
|
||||
sca_in(ISR1, card), sca_in(ISR2, card));
|
||||
#endif
|
||||
|
||||
#if !defined(PAGE0_ALWAYS_MAPPED) && !defined(ALL_PAGES_ALWAYS_MAPPED)
|
||||
openwin(card, page); /* Restore original page */
|
||||
#endif
|
||||
}
|
||||
#endif /* DEBUG_RINGS */
|
||||
|
||||
|
||||
|
||||
static int sca_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
port_t *port = dev_to_port(dev);
|
||||
card_t *card = port_to_card(port);
|
||||
pkt_desc __iomem *desc;
|
||||
u32 buff, len;
|
||||
#ifndef ALL_PAGES_ALWAYS_MAPPED
|
||||
u8 page;
|
||||
u32 maxlen;
|
||||
#endif
|
||||
|
||||
spin_lock_irq(&port->lock);
|
||||
|
||||
desc = desc_address(port, port->txin + 1, 1);
|
||||
if (readb(&desc->stat)) { /* allow 1 packet gap */
|
||||
/* should never happen - previous xmit should stop queue */
|
||||
#ifdef DEBUG_PKT
|
||||
printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name);
|
||||
#endif
|
||||
netif_stop_queue(dev);
|
||||
spin_unlock_irq(&port->lock);
|
||||
return 1; /* request packet to be queued */
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PKT
|
||||
printk(KERN_DEBUG "%s TX(%i):", dev->name, skb->len);
|
||||
debug_frame(skb);
|
||||
#endif
|
||||
|
||||
desc = desc_address(port, port->txin, 1);
|
||||
buff = buffer_offset(port, port->txin, 1);
|
||||
len = skb->len;
|
||||
#ifndef ALL_PAGES_ALWAYS_MAPPED
|
||||
page = buff / winsize(card);
|
||||
buff = buff % winsize(card);
|
||||
maxlen = winsize(card) - buff;
|
||||
|
||||
openwin(card, page);
|
||||
if (len > maxlen) {
|
||||
memcpy_toio(winbase(card) + buff, skb->data, maxlen);
|
||||
openwin(card, page + 1);
|
||||
memcpy_toio(winbase(card), skb->data + maxlen, len - maxlen);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
memcpy_toio(winbase(card) + buff, skb->data, len);
|
||||
|
||||
#if !defined(PAGE0_ALWAYS_MAPPED) && !defined(ALL_PAGES_ALWAYS_MAPPED)
|
||||
openwin(card, 0); /* select pkt_desc table page back */
|
||||
#endif
|
||||
writew(len, &desc->len);
|
||||
writeb(ST_TX_EOM, &desc->stat);
|
||||
dev->trans_start = jiffies;
|
||||
|
||||
port->txin = next_desc(port, port->txin, 1);
|
||||
sca_outa(desc_offset(port, port->txin, 1),
|
||||
get_dmac_tx(port) + EDAL, card);
|
||||
|
||||
sca_out(DSR_DE, DSR_TX(phy_node(port)), card); /* Enable TX DMA */
|
||||
|
||||
desc = desc_address(port, port->txin + 1, 1);
|
||||
if (readb(&desc->stat)) /* allow 1 packet gap */
|
||||
netif_stop_queue(dev);
|
||||
|
||||
spin_unlock_irq(&port->lock);
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef NEED_DETECT_RAM
|
||||
static u32 __devinit sca_detect_ram(card_t *card, u8 __iomem *rambase, u32 ramsize)
|
||||
{
|
||||
/* Round RAM size to 32 bits, fill from end to start */
|
||||
u32 i = ramsize &= ~3;
|
||||
|
||||
#ifndef ALL_PAGES_ALWAYS_MAPPED
|
||||
u32 size = winsize(card);
|
||||
|
||||
openwin(card, (i - 4) / size); /* select last window */
|
||||
#endif
|
||||
do {
|
||||
i -= 4;
|
||||
#ifndef ALL_PAGES_ALWAYS_MAPPED
|
||||
if ((i + 4) % size == 0)
|
||||
openwin(card, i / size);
|
||||
writel(i ^ 0x12345678, rambase + i % size);
|
||||
#else
|
||||
writel(i ^ 0x12345678, rambase + i);
|
||||
#endif
|
||||
}while (i > 0);
|
||||
|
||||
for (i = 0; i < ramsize ; i += 4) {
|
||||
#ifndef ALL_PAGES_ALWAYS_MAPPED
|
||||
if (i % size == 0)
|
||||
openwin(card, i / size);
|
||||
|
||||
if (readl(rambase + i % size) != (i ^ 0x12345678))
|
||||
break;
|
||||
#else
|
||||
if (readl(rambase + i) != (i ^ 0x12345678))
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
#endif /* NEED_DETECT_RAM */
|
||||
|
||||
|
||||
|
||||
static void __devinit sca_init(card_t *card, int wait_states)
|
||||
{
|
||||
sca_out(wait_states, WCRL, card); /* Wait Control */
|
||||
sca_out(wait_states, WCRM, card);
|
||||
sca_out(wait_states, WCRH, card);
|
||||
|
||||
sca_out(0, DMER, card); /* DMA Master disable */
|
||||
sca_out(0x03, PCR, card); /* DMA priority */
|
||||
sca_out(0, DSR_RX(0), card); /* DMA disable - to halt state */
|
||||
sca_out(0, DSR_TX(0), card);
|
||||
sca_out(0, DSR_RX(1), card);
|
||||
sca_out(0, DSR_TX(1), card);
|
||||
sca_out(DMER_DME, DMER, card); /* DMA Master enable */
|
||||
}
|
||||
380
drivers/net/wan/hdlc.c
Normal file
380
drivers/net/wan/hdlc.c
Normal file
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* Generic HDLC support routines for Linux
|
||||
*
|
||||
* Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* Currently supported:
|
||||
* * raw IP-in-HDLC
|
||||
* * Cisco HDLC
|
||||
* * Frame Relay with ANSI or CCITT LMI (both user and network side)
|
||||
* * PPP
|
||||
* * X.25
|
||||
*
|
||||
* Use sethdlc utility to set line parameters, protocol and PVCs
|
||||
*
|
||||
* How does it work:
|
||||
* - proto->open(), close(), start(), stop() calls are serialized.
|
||||
* The order is: open, [ start, stop ... ] close ...
|
||||
* - proto->start() and stop() are called with spin_lock_irq held.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/lapb.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/hdlc.h>
|
||||
|
||||
|
||||
static const char* version = "HDLC support module revision 1.21";
|
||||
|
||||
#undef DEBUG_LINK
|
||||
|
||||
static struct hdlc_proto *first_proto = NULL;
|
||||
|
||||
|
||||
static int hdlc_change_mtu(struct net_device *dev, int new_mtu)
|
||||
{
|
||||
if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))
|
||||
return -EINVAL;
|
||||
dev->mtu = new_mtu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct net_device_stats *hdlc_get_stats(struct net_device *dev)
|
||||
{
|
||||
return hdlc_stats(dev);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *p, struct net_device *orig_dev)
|
||||
{
|
||||
struct hdlc_device_desc *desc = dev_to_desc(dev);
|
||||
if (desc->netif_rx)
|
||||
return desc->netif_rx(skb);
|
||||
|
||||
desc->stats.rx_dropped++; /* Shouldn't happen */
|
||||
dev_kfree_skb(skb);
|
||||
return NET_RX_DROP;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline void hdlc_proto_start(struct net_device *dev)
|
||||
{
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
if (hdlc->proto->start)
|
||||
return hdlc->proto->start(dev);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline void hdlc_proto_stop(struct net_device *dev)
|
||||
{
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
if (hdlc->proto->stop)
|
||||
return hdlc->proto->stop(dev);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int hdlc_device_event(struct notifier_block *this, unsigned long event,
|
||||
void *ptr)
|
||||
{
|
||||
struct net_device *dev = ptr;
|
||||
hdlc_device *hdlc;
|
||||
unsigned long flags;
|
||||
int on;
|
||||
|
||||
if (dev->get_stats != hdlc_get_stats)
|
||||
return NOTIFY_DONE; /* not an HDLC device */
|
||||
|
||||
if (event != NETDEV_CHANGE)
|
||||
return NOTIFY_DONE; /* Only interrested in carrier changes */
|
||||
|
||||
on = netif_carrier_ok(dev);
|
||||
|
||||
#ifdef DEBUG_LINK
|
||||
printk(KERN_DEBUG "%s: hdlc_device_event NETDEV_CHANGE, carrier %i\n",
|
||||
dev->name, on);
|
||||
#endif
|
||||
|
||||
hdlc = dev_to_hdlc(dev);
|
||||
spin_lock_irqsave(&hdlc->state_lock, flags);
|
||||
|
||||
if (hdlc->carrier == on)
|
||||
goto carrier_exit; /* no change in DCD line level */
|
||||
|
||||
hdlc->carrier = on;
|
||||
|
||||
if (!hdlc->open)
|
||||
goto carrier_exit;
|
||||
|
||||
if (hdlc->carrier) {
|
||||
printk(KERN_INFO "%s: Carrier detected\n", dev->name);
|
||||
hdlc_proto_start(dev);
|
||||
} else {
|
||||
printk(KERN_INFO "%s: Carrier lost\n", dev->name);
|
||||
hdlc_proto_stop(dev);
|
||||
}
|
||||
|
||||
carrier_exit:
|
||||
spin_unlock_irqrestore(&hdlc->state_lock, flags);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Must be called by hardware driver when HDLC device is being opened */
|
||||
int hdlc_open(struct net_device *dev)
|
||||
{
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
#ifdef DEBUG_LINK
|
||||
printk(KERN_DEBUG "%s: hdlc_open() carrier %i open %i\n", dev->name,
|
||||
hdlc->carrier, hdlc->open);
|
||||
#endif
|
||||
|
||||
if (hdlc->proto == NULL)
|
||||
return -ENOSYS; /* no protocol attached */
|
||||
|
||||
if (hdlc->proto->open) {
|
||||
int result = hdlc->proto->open(dev);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
|
||||
spin_lock_irq(&hdlc->state_lock);
|
||||
|
||||
if (hdlc->carrier) {
|
||||
printk(KERN_INFO "%s: Carrier detected\n", dev->name);
|
||||
hdlc_proto_start(dev);
|
||||
} else
|
||||
printk(KERN_INFO "%s: No carrier\n", dev->name);
|
||||
|
||||
hdlc->open = 1;
|
||||
|
||||
spin_unlock_irq(&hdlc->state_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Must be called by hardware driver when HDLC device is being closed */
|
||||
void hdlc_close(struct net_device *dev)
|
||||
{
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
#ifdef DEBUG_LINK
|
||||
printk(KERN_DEBUG "%s: hdlc_close() carrier %i open %i\n", dev->name,
|
||||
hdlc->carrier, hdlc->open);
|
||||
#endif
|
||||
|
||||
spin_lock_irq(&hdlc->state_lock);
|
||||
|
||||
hdlc->open = 0;
|
||||
if (hdlc->carrier)
|
||||
hdlc_proto_stop(dev);
|
||||
|
||||
spin_unlock_irq(&hdlc->state_lock);
|
||||
|
||||
if (hdlc->proto->close)
|
||||
hdlc->proto->close(dev);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
struct hdlc_proto *proto = first_proto;
|
||||
int result;
|
||||
|
||||
if (cmd != SIOCWANDEV)
|
||||
return -EINVAL;
|
||||
|
||||
if (dev_to_hdlc(dev)->proto) {
|
||||
result = dev_to_hdlc(dev)->proto->ioctl(dev, ifr);
|
||||
if (result != -EINVAL)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Not handled by currently attached protocol (if any) */
|
||||
|
||||
while (proto) {
|
||||
if ((result = proto->ioctl(dev, ifr)) != -EINVAL)
|
||||
return result;
|
||||
proto = proto->next;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void hdlc_setup_dev(struct net_device *dev)
|
||||
{
|
||||
/* Re-init all variables changed by HDLC protocol drivers,
|
||||
* including ether_setup() called from hdlc_raw_eth.c.
|
||||
*/
|
||||
dev->get_stats = hdlc_get_stats;
|
||||
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
|
||||
dev->mtu = HDLC_MAX_MTU;
|
||||
dev->type = ARPHRD_RAWHDLC;
|
||||
dev->hard_header_len = 16;
|
||||
dev->addr_len = 0;
|
||||
dev->hard_header = NULL;
|
||||
dev->rebuild_header = NULL;
|
||||
dev->set_mac_address = NULL;
|
||||
dev->hard_header_cache = NULL;
|
||||
dev->header_cache_update = NULL;
|
||||
dev->change_mtu = hdlc_change_mtu;
|
||||
dev->hard_header_parse = NULL;
|
||||
}
|
||||
|
||||
static void hdlc_setup(struct net_device *dev)
|
||||
{
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
|
||||
hdlc_setup_dev(dev);
|
||||
hdlc->carrier = 1;
|
||||
hdlc->open = 0;
|
||||
spin_lock_init(&hdlc->state_lock);
|
||||
}
|
||||
|
||||
struct net_device *alloc_hdlcdev(void *priv)
|
||||
{
|
||||
struct net_device *dev;
|
||||
dev = alloc_netdev(sizeof(struct hdlc_device_desc) +
|
||||
sizeof(hdlc_device), "hdlc%d", hdlc_setup);
|
||||
if (dev)
|
||||
dev_to_hdlc(dev)->priv = priv;
|
||||
return dev;
|
||||
}
|
||||
|
||||
void unregister_hdlc_device(struct net_device *dev)
|
||||
{
|
||||
rtnl_lock();
|
||||
unregister_netdevice(dev);
|
||||
detach_hdlc_protocol(dev);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
|
||||
int (*rx)(struct sk_buff *skb), size_t size)
|
||||
{
|
||||
detach_hdlc_protocol(dev);
|
||||
|
||||
if (!try_module_get(proto->module))
|
||||
return -ENOSYS;
|
||||
|
||||
if (size)
|
||||
if ((dev_to_hdlc(dev)->state = kmalloc(size,
|
||||
GFP_KERNEL)) == NULL) {
|
||||
printk(KERN_WARNING "Memory squeeze on"
|
||||
" hdlc_proto_attach()\n");
|
||||
module_put(proto->module);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
dev_to_hdlc(dev)->proto = proto;
|
||||
dev_to_desc(dev)->netif_rx = rx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void detach_hdlc_protocol(struct net_device *dev)
|
||||
{
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
|
||||
if (hdlc->proto) {
|
||||
if (hdlc->proto->detach)
|
||||
hdlc->proto->detach(dev);
|
||||
module_put(hdlc->proto->module);
|
||||
hdlc->proto = NULL;
|
||||
}
|
||||
kfree(hdlc->state);
|
||||
hdlc->state = NULL;
|
||||
hdlc_setup_dev(dev);
|
||||
}
|
||||
|
||||
|
||||
void register_hdlc_protocol(struct hdlc_proto *proto)
|
||||
{
|
||||
proto->next = first_proto;
|
||||
first_proto = proto;
|
||||
}
|
||||
|
||||
|
||||
void unregister_hdlc_protocol(struct hdlc_proto *proto)
|
||||
{
|
||||
struct hdlc_proto **p = &first_proto;
|
||||
while (*p) {
|
||||
if (*p == proto) {
|
||||
*p = proto->next;
|
||||
return;
|
||||
}
|
||||
p = &((*p)->next);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
|
||||
MODULE_DESCRIPTION("HDLC support module");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
EXPORT_SYMBOL(hdlc_open);
|
||||
EXPORT_SYMBOL(hdlc_close);
|
||||
EXPORT_SYMBOL(hdlc_ioctl);
|
||||
EXPORT_SYMBOL(alloc_hdlcdev);
|
||||
EXPORT_SYMBOL(unregister_hdlc_device);
|
||||
EXPORT_SYMBOL(register_hdlc_protocol);
|
||||
EXPORT_SYMBOL(unregister_hdlc_protocol);
|
||||
EXPORT_SYMBOL(attach_hdlc_protocol);
|
||||
EXPORT_SYMBOL(detach_hdlc_protocol);
|
||||
|
||||
static struct packet_type hdlc_packet_type = {
|
||||
.type = __constant_htons(ETH_P_HDLC),
|
||||
.func = hdlc_rcv,
|
||||
};
|
||||
|
||||
|
||||
static struct notifier_block hdlc_notifier = {
|
||||
.notifier_call = hdlc_device_event,
|
||||
};
|
||||
|
||||
|
||||
static int __init hdlc_module_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
printk(KERN_INFO "%s\n", version);
|
||||
if ((result = register_netdevice_notifier(&hdlc_notifier)) != 0)
|
||||
return result;
|
||||
dev_add_pack(&hdlc_packet_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void __exit hdlc_module_exit(void)
|
||||
{
|
||||
dev_remove_pack(&hdlc_packet_type);
|
||||
unregister_netdevice_notifier(&hdlc_notifier);
|
||||
}
|
||||
|
||||
|
||||
module_init(hdlc_module_init);
|
||||
module_exit(hdlc_module_exit);
|
||||
396
drivers/net/wan/hdlc_cisco.c
Normal file
396
drivers/net/wan/hdlc_cisco.c
Normal file
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* Generic HDLC support routines for Linux
|
||||
* Cisco HDLC support
|
||||
*
|
||||
* Copyright (C) 2000 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/lapb.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/hdlc.h>
|
||||
|
||||
#undef DEBUG_HARD_HEADER
|
||||
|
||||
#define CISCO_MULTICAST 0x8F /* Cisco multicast address */
|
||||
#define CISCO_UNICAST 0x0F /* Cisco unicast address */
|
||||
#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */
|
||||
#define CISCO_SYS_INFO 0x2000 /* Cisco interface/system info */
|
||||
#define CISCO_ADDR_REQ 0 /* Cisco address request */
|
||||
#define CISCO_ADDR_REPLY 1 /* Cisco address reply */
|
||||
#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */
|
||||
|
||||
|
||||
struct hdlc_header {
|
||||
u8 address;
|
||||
u8 control;
|
||||
u16 protocol;
|
||||
}__attribute__ ((packed));
|
||||
|
||||
|
||||
struct cisco_packet {
|
||||
u32 type; /* code */
|
||||
u32 par1;
|
||||
u32 par2;
|
||||
u16 rel; /* reliability */
|
||||
u32 time;
|
||||
}__attribute__ ((packed));
|
||||
#define CISCO_PACKET_LEN 18
|
||||
#define CISCO_BIG_PACKET_LEN 20
|
||||
|
||||
|
||||
struct cisco_state {
|
||||
cisco_proto settings;
|
||||
|
||||
struct timer_list timer;
|
||||
unsigned long last_poll;
|
||||
int up;
|
||||
int request_sent;
|
||||
u32 txseq; /* TX sequence number */
|
||||
u32 rxseq; /* RX sequence number */
|
||||
};
|
||||
|
||||
|
||||
static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr);
|
||||
|
||||
|
||||
static inline struct cisco_state * state(hdlc_device *hdlc)
|
||||
{
|
||||
return(struct cisco_state *)(hdlc->state);
|
||||
}
|
||||
|
||||
|
||||
static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev,
|
||||
u16 type, void *daddr, void *saddr,
|
||||
unsigned int len)
|
||||
{
|
||||
struct hdlc_header *data;
|
||||
#ifdef DEBUG_HARD_HEADER
|
||||
printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name);
|
||||
#endif
|
||||
|
||||
skb_push(skb, sizeof(struct hdlc_header));
|
||||
data = (struct hdlc_header*)skb->data;
|
||||
if (type == CISCO_KEEPALIVE)
|
||||
data->address = CISCO_MULTICAST;
|
||||
else
|
||||
data->address = CISCO_UNICAST;
|
||||
data->control = 0;
|
||||
data->protocol = htons(type);
|
||||
|
||||
return sizeof(struct hdlc_header);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void cisco_keepalive_send(struct net_device *dev, u32 type,
|
||||
u32 par1, u32 par2)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct cisco_packet *data;
|
||||
|
||||
skb = dev_alloc_skb(sizeof(struct hdlc_header) +
|
||||
sizeof(struct cisco_packet));
|
||||
if (!skb) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Memory squeeze on cisco_keepalive_send()\n",
|
||||
dev->name);
|
||||
return;
|
||||
}
|
||||
skb_reserve(skb, 4);
|
||||
cisco_hard_header(skb, dev, CISCO_KEEPALIVE, NULL, NULL, 0);
|
||||
data = (struct cisco_packet*)(skb->data + 4);
|
||||
|
||||
data->type = htonl(type);
|
||||
data->par1 = htonl(par1);
|
||||
data->par2 = htonl(par2);
|
||||
data->rel = 0xFFFF;
|
||||
/* we will need do_div here if 1000 % HZ != 0 */
|
||||
data->time = htonl((jiffies - INITIAL_JIFFIES) * (1000 / HZ));
|
||||
|
||||
skb_put(skb, sizeof(struct cisco_packet));
|
||||
skb->priority = TC_PRIO_CONTROL;
|
||||
skb->dev = dev;
|
||||
skb->nh.raw = skb->data;
|
||||
|
||||
dev_queue_xmit(skb);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static __be16 cisco_type_trans(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct hdlc_header *data = (struct hdlc_header*)skb->data;
|
||||
|
||||
if (skb->len < sizeof(struct hdlc_header))
|
||||
return __constant_htons(ETH_P_HDLC);
|
||||
|
||||
if (data->address != CISCO_MULTICAST &&
|
||||
data->address != CISCO_UNICAST)
|
||||
return __constant_htons(ETH_P_HDLC);
|
||||
|
||||
switch(data->protocol) {
|
||||
case __constant_htons(ETH_P_IP):
|
||||
case __constant_htons(ETH_P_IPX):
|
||||
case __constant_htons(ETH_P_IPV6):
|
||||
skb_pull(skb, sizeof(struct hdlc_header));
|
||||
return data->protocol;
|
||||
default:
|
||||
return __constant_htons(ETH_P_HDLC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int cisco_rx(struct sk_buff *skb)
|
||||
{
|
||||
struct net_device *dev = skb->dev;
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
struct hdlc_header *data = (struct hdlc_header*)skb->data;
|
||||
struct cisco_packet *cisco_data;
|
||||
struct in_device *in_dev;
|
||||
__be32 addr, mask;
|
||||
|
||||
if (skb->len < sizeof(struct hdlc_header))
|
||||
goto rx_error;
|
||||
|
||||
if (data->address != CISCO_MULTICAST &&
|
||||
data->address != CISCO_UNICAST)
|
||||
goto rx_error;
|
||||
|
||||
switch(ntohs(data->protocol)) {
|
||||
case CISCO_SYS_INFO:
|
||||
/* Packet is not needed, drop it. */
|
||||
dev_kfree_skb_any(skb);
|
||||
return NET_RX_SUCCESS;
|
||||
|
||||
case CISCO_KEEPALIVE:
|
||||
if ((skb->len != sizeof(struct hdlc_header) +
|
||||
CISCO_PACKET_LEN) &&
|
||||
(skb->len != sizeof(struct hdlc_header) +
|
||||
CISCO_BIG_PACKET_LEN)) {
|
||||
printk(KERN_INFO "%s: Invalid length of Cisco control"
|
||||
" packet (%d bytes)\n", dev->name, skb->len);
|
||||
goto rx_error;
|
||||
}
|
||||
|
||||
cisco_data = (struct cisco_packet*)(skb->data + sizeof
|
||||
(struct hdlc_header));
|
||||
|
||||
switch(ntohl (cisco_data->type)) {
|
||||
case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */
|
||||
in_dev = dev->ip_ptr;
|
||||
addr = 0;
|
||||
mask = ~0; /* is the mask correct? */
|
||||
|
||||
if (in_dev != NULL) {
|
||||
struct in_ifaddr **ifap = &in_dev->ifa_list;
|
||||
|
||||
while (*ifap != NULL) {
|
||||
if (strcmp(dev->name,
|
||||
(*ifap)->ifa_label) == 0) {
|
||||
addr = (*ifap)->ifa_local;
|
||||
mask = (*ifap)->ifa_mask;
|
||||
break;
|
||||
}
|
||||
ifap = &(*ifap)->ifa_next;
|
||||
}
|
||||
|
||||
cisco_keepalive_send(dev, CISCO_ADDR_REPLY,
|
||||
addr, mask);
|
||||
}
|
||||
dev_kfree_skb_any(skb);
|
||||
return NET_RX_SUCCESS;
|
||||
|
||||
case CISCO_ADDR_REPLY:
|
||||
printk(KERN_INFO "%s: Unexpected Cisco IP address "
|
||||
"reply\n", dev->name);
|
||||
goto rx_error;
|
||||
|
||||
case CISCO_KEEPALIVE_REQ:
|
||||
state(hdlc)->rxseq = ntohl(cisco_data->par1);
|
||||
if (state(hdlc)->request_sent &&
|
||||
ntohl(cisco_data->par2) == state(hdlc)->txseq) {
|
||||
state(hdlc)->last_poll = jiffies;
|
||||
if (!state(hdlc)->up) {
|
||||
u32 sec, min, hrs, days;
|
||||
sec = ntohl(cisco_data->time) / 1000;
|
||||
min = sec / 60; sec -= min * 60;
|
||||
hrs = min / 60; min -= hrs * 60;
|
||||
days = hrs / 24; hrs -= days * 24;
|
||||
printk(KERN_INFO "%s: Link up (peer "
|
||||
"uptime %ud%uh%um%us)\n",
|
||||
dev->name, days, hrs,
|
||||
min, sec);
|
||||
netif_dormant_off(dev);
|
||||
state(hdlc)->up = 1;
|
||||
}
|
||||
}
|
||||
|
||||
dev_kfree_skb_any(skb);
|
||||
return NET_RX_SUCCESS;
|
||||
} /* switch(keepalive type) */
|
||||
} /* switch(protocol) */
|
||||
|
||||
printk(KERN_INFO "%s: Unsupported protocol %x\n", dev->name,
|
||||
data->protocol);
|
||||
dev_kfree_skb_any(skb);
|
||||
return NET_RX_DROP;
|
||||
|
||||
rx_error:
|
||||
dev_to_desc(dev)->stats.rx_errors++; /* Mark error */
|
||||
dev_kfree_skb_any(skb);
|
||||
return NET_RX_DROP;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void cisco_timer(unsigned long arg)
|
||||
{
|
||||
struct net_device *dev = (struct net_device *)arg;
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
|
||||
if (state(hdlc)->up &&
|
||||
time_after(jiffies, state(hdlc)->last_poll +
|
||||
state(hdlc)->settings.timeout * HZ)) {
|
||||
state(hdlc)->up = 0;
|
||||
printk(KERN_INFO "%s: Link down\n", dev->name);
|
||||
netif_dormant_on(dev);
|
||||
}
|
||||
|
||||
cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ, ++state(hdlc)->txseq,
|
||||
state(hdlc)->rxseq);
|
||||
state(hdlc)->request_sent = 1;
|
||||
state(hdlc)->timer.expires = jiffies +
|
||||
state(hdlc)->settings.interval * HZ;
|
||||
state(hdlc)->timer.function = cisco_timer;
|
||||
state(hdlc)->timer.data = arg;
|
||||
add_timer(&state(hdlc)->timer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void cisco_start(struct net_device *dev)
|
||||
{
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
state(hdlc)->up = 0;
|
||||
state(hdlc)->request_sent = 0;
|
||||
state(hdlc)->txseq = state(hdlc)->rxseq = 0;
|
||||
|
||||
init_timer(&state(hdlc)->timer);
|
||||
state(hdlc)->timer.expires = jiffies + HZ; /*First poll after 1s*/
|
||||
state(hdlc)->timer.function = cisco_timer;
|
||||
state(hdlc)->timer.data = (unsigned long)dev;
|
||||
add_timer(&state(hdlc)->timer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void cisco_stop(struct net_device *dev)
|
||||
{
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
del_timer_sync(&state(hdlc)->timer);
|
||||
netif_dormant_on(dev);
|
||||
state(hdlc)->up = 0;
|
||||
state(hdlc)->request_sent = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct hdlc_proto proto = {
|
||||
.start = cisco_start,
|
||||
.stop = cisco_stop,
|
||||
.type_trans = cisco_type_trans,
|
||||
.ioctl = cisco_ioctl,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
|
||||
{
|
||||
cisco_proto __user *cisco_s = ifr->ifr_settings.ifs_ifsu.cisco;
|
||||
const size_t size = sizeof(cisco_proto);
|
||||
cisco_proto new_settings;
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
int result;
|
||||
|
||||
switch (ifr->ifr_settings.type) {
|
||||
case IF_GET_PROTO:
|
||||
if (dev_to_hdlc(dev)->proto != &proto)
|
||||
return -EINVAL;
|
||||
ifr->ifr_settings.type = IF_PROTO_CISCO;
|
||||
if (ifr->ifr_settings.size < size) {
|
||||
ifr->ifr_settings.size = size; /* data size wanted */
|
||||
return -ENOBUFS;
|
||||
}
|
||||
if (copy_to_user(cisco_s, &state(hdlc)->settings, size))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case IF_PROTO_CISCO:
|
||||
if(!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if(dev->flags & IFF_UP)
|
||||
return -EBUSY;
|
||||
|
||||
if (copy_from_user(&new_settings, cisco_s, size))
|
||||
return -EFAULT;
|
||||
|
||||
if (new_settings.interval < 1 ||
|
||||
new_settings.timeout < 2)
|
||||
return -EINVAL;
|
||||
|
||||
result=hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = attach_hdlc_protocol(dev, &proto, cisco_rx,
|
||||
sizeof(struct cisco_state));
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
memcpy(&state(hdlc)->settings, &new_settings, size);
|
||||
dev->hard_start_xmit = hdlc->xmit;
|
||||
dev->hard_header = cisco_hard_header;
|
||||
dev->type = ARPHRD_CISCO;
|
||||
netif_dormant_on(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static int __init mod_init(void)
|
||||
{
|
||||
register_hdlc_protocol(&proto);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void __exit mod_exit(void)
|
||||
{
|
||||
unregister_hdlc_protocol(&proto);
|
||||
}
|
||||
|
||||
|
||||
module_init(mod_init);
|
||||
module_exit(mod_exit);
|
||||
|
||||
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
|
||||
MODULE_DESCRIPTION("Cisco HDLC protocol support for generic HDLC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
1347
drivers/net/wan/hdlc_fr.c
Normal file
1347
drivers/net/wan/hdlc_fr.c
Normal file
File diff suppressed because it is too large
Load Diff
158
drivers/net/wan/hdlc_ppp.c
Normal file
158
drivers/net/wan/hdlc_ppp.c
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Generic HDLC support routines for Linux
|
||||
* Point-to-point protocol support
|
||||
*
|
||||
* Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/lapb.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/hdlc.h>
|
||||
#include <net/syncppp.h>
|
||||
|
||||
struct ppp_state {
|
||||
struct ppp_device pppdev;
|
||||
struct ppp_device *syncppp_ptr;
|
||||
int (*old_change_mtu)(struct net_device *dev, int new_mtu);
|
||||
};
|
||||
|
||||
static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr);
|
||||
|
||||
|
||||
static inline struct ppp_state* state(hdlc_device *hdlc)
|
||||
{
|
||||
return(struct ppp_state *)(hdlc->state);
|
||||
}
|
||||
|
||||
|
||||
static int ppp_open(struct net_device *dev)
|
||||
{
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
void *old_ioctl;
|
||||
int result;
|
||||
|
||||
dev->priv = &state(hdlc)->syncppp_ptr;
|
||||
state(hdlc)->syncppp_ptr = &state(hdlc)->pppdev;
|
||||
state(hdlc)->pppdev.dev = dev;
|
||||
|
||||
old_ioctl = dev->do_ioctl;
|
||||
state(hdlc)->old_change_mtu = dev->change_mtu;
|
||||
sppp_attach(&state(hdlc)->pppdev);
|
||||
/* sppp_attach nukes them. We don't need syncppp's ioctl */
|
||||
dev->do_ioctl = old_ioctl;
|
||||
state(hdlc)->pppdev.sppp.pp_flags &= ~PP_CISCO;
|
||||
dev->type = ARPHRD_PPP;
|
||||
result = sppp_open(dev);
|
||||
if (result) {
|
||||
sppp_detach(dev);
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void ppp_close(struct net_device *dev)
|
||||
{
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
|
||||
sppp_close(dev);
|
||||
sppp_detach(dev);
|
||||
dev->rebuild_header = NULL;
|
||||
dev->change_mtu = state(hdlc)->old_change_mtu;
|
||||
dev->mtu = HDLC_MAX_MTU;
|
||||
dev->hard_header_len = 16;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static __be16 ppp_type_trans(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
return __constant_htons(ETH_P_WAN_PPP);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct hdlc_proto proto = {
|
||||
.open = ppp_open,
|
||||
.close = ppp_close,
|
||||
.type_trans = ppp_type_trans,
|
||||
.ioctl = ppp_ioctl,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
|
||||
{
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
int result;
|
||||
|
||||
switch (ifr->ifr_settings.type) {
|
||||
case IF_GET_PROTO:
|
||||
if (dev_to_hdlc(dev)->proto != &proto)
|
||||
return -EINVAL;
|
||||
ifr->ifr_settings.type = IF_PROTO_PPP;
|
||||
return 0; /* return protocol only, no settable parameters */
|
||||
|
||||
case IF_PROTO_PPP:
|
||||
if(!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if(dev->flags & IFF_UP)
|
||||
return -EBUSY;
|
||||
|
||||
/* no settable parameters */
|
||||
|
||||
result=hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = attach_hdlc_protocol(dev, &proto, NULL,
|
||||
sizeof(struct ppp_state));
|
||||
if (result)
|
||||
return result;
|
||||
dev->hard_start_xmit = hdlc->xmit;
|
||||
dev->type = ARPHRD_PPP;
|
||||
netif_dormant_off(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static int __init mod_init(void)
|
||||
{
|
||||
register_hdlc_protocol(&proto);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void __exit mod_exit(void)
|
||||
{
|
||||
unregister_hdlc_protocol(&proto);
|
||||
}
|
||||
|
||||
|
||||
module_init(mod_init);
|
||||
module_exit(mod_exit);
|
||||
|
||||
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
|
||||
MODULE_DESCRIPTION("PPP protocol support for generic HDLC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
119
drivers/net/wan/hdlc_raw.c
Normal file
119
drivers/net/wan/hdlc_raw.c
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Generic HDLC support routines for Linux
|
||||
* HDLC support
|
||||
*
|
||||
* Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/lapb.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/hdlc.h>
|
||||
|
||||
|
||||
static int raw_ioctl(struct net_device *dev, struct ifreq *ifr);
|
||||
|
||||
static __be16 raw_type_trans(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
return __constant_htons(ETH_P_IP);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct hdlc_proto proto = {
|
||||
.type_trans = raw_type_trans,
|
||||
.ioctl = raw_ioctl,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static int raw_ioctl(struct net_device *dev, struct ifreq *ifr)
|
||||
{
|
||||
raw_hdlc_proto __user *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc;
|
||||
const size_t size = sizeof(raw_hdlc_proto);
|
||||
raw_hdlc_proto new_settings;
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
int result;
|
||||
|
||||
switch (ifr->ifr_settings.type) {
|
||||
case IF_GET_PROTO:
|
||||
if (dev_to_hdlc(dev)->proto != &proto)
|
||||
return -EINVAL;
|
||||
ifr->ifr_settings.type = IF_PROTO_HDLC;
|
||||
if (ifr->ifr_settings.size < size) {
|
||||
ifr->ifr_settings.size = size; /* data size wanted */
|
||||
return -ENOBUFS;
|
||||
}
|
||||
if (copy_to_user(raw_s, hdlc->state, size))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case IF_PROTO_HDLC:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (dev->flags & IFF_UP)
|
||||
return -EBUSY;
|
||||
|
||||
if (copy_from_user(&new_settings, raw_s, size))
|
||||
return -EFAULT;
|
||||
|
||||
if (new_settings.encoding == ENCODING_DEFAULT)
|
||||
new_settings.encoding = ENCODING_NRZ;
|
||||
|
||||
if (new_settings.parity == PARITY_DEFAULT)
|
||||
new_settings.parity = PARITY_CRC16_PR1_CCITT;
|
||||
|
||||
result = hdlc->attach(dev, new_settings.encoding,
|
||||
new_settings.parity);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = attach_hdlc_protocol(dev, &proto, NULL,
|
||||
sizeof(raw_hdlc_proto));
|
||||
if (result)
|
||||
return result;
|
||||
memcpy(hdlc->state, &new_settings, size);
|
||||
dev->hard_start_xmit = hdlc->xmit;
|
||||
dev->type = ARPHRD_RAWHDLC;
|
||||
netif_dormant_off(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static int __init mod_init(void)
|
||||
{
|
||||
register_hdlc_protocol(&proto);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void __exit mod_exit(void)
|
||||
{
|
||||
unregister_hdlc_protocol(&proto);
|
||||
}
|
||||
|
||||
|
||||
module_init(mod_init);
|
||||
module_exit(mod_exit);
|
||||
|
||||
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
|
||||
MODULE_DESCRIPTION("Raw HDLC protocol support for generic HDLC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
139
drivers/net/wan/hdlc_raw_eth.c
Normal file
139
drivers/net/wan/hdlc_raw_eth.c
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Generic HDLC support routines for Linux
|
||||
* HDLC Ethernet emulation support
|
||||
*
|
||||
* Copyright (C) 2002-2006 Krzysztof Halasa <khc@pm.waw.pl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/lapb.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/hdlc.h>
|
||||
|
||||
static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr);
|
||||
|
||||
static int eth_tx(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
int pad = ETH_ZLEN - skb->len;
|
||||
if (pad > 0) { /* Pad the frame with zeros */
|
||||
int len = skb->len;
|
||||
if (skb_tailroom(skb) < pad)
|
||||
if (pskb_expand_head(skb, 0, pad, GFP_ATOMIC)) {
|
||||
hdlc_stats(dev)->tx_dropped++;
|
||||
dev_kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
skb_put(skb, pad);
|
||||
memset(skb->data + len, 0, pad);
|
||||
}
|
||||
return dev_to_hdlc(dev)->xmit(skb, dev);
|
||||
}
|
||||
|
||||
|
||||
static struct hdlc_proto proto = {
|
||||
.type_trans = eth_type_trans,
|
||||
.ioctl = raw_eth_ioctl,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
|
||||
{
|
||||
raw_hdlc_proto __user *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc;
|
||||
const size_t size = sizeof(raw_hdlc_proto);
|
||||
raw_hdlc_proto new_settings;
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
int result;
|
||||
void *old_ch_mtu;
|
||||
int old_qlen;
|
||||
|
||||
switch (ifr->ifr_settings.type) {
|
||||
case IF_GET_PROTO:
|
||||
if (dev_to_hdlc(dev)->proto != &proto)
|
||||
return -EINVAL;
|
||||
ifr->ifr_settings.type = IF_PROTO_HDLC_ETH;
|
||||
if (ifr->ifr_settings.size < size) {
|
||||
ifr->ifr_settings.size = size; /* data size wanted */
|
||||
return -ENOBUFS;
|
||||
}
|
||||
if (copy_to_user(raw_s, hdlc->state, size))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case IF_PROTO_HDLC_ETH:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (dev->flags & IFF_UP)
|
||||
return -EBUSY;
|
||||
|
||||
if (copy_from_user(&new_settings, raw_s, size))
|
||||
return -EFAULT;
|
||||
|
||||
if (new_settings.encoding == ENCODING_DEFAULT)
|
||||
new_settings.encoding = ENCODING_NRZ;
|
||||
|
||||
if (new_settings.parity == PARITY_DEFAULT)
|
||||
new_settings.parity = PARITY_CRC16_PR1_CCITT;
|
||||
|
||||
result = hdlc->attach(dev, new_settings.encoding,
|
||||
new_settings.parity);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = attach_hdlc_protocol(dev, &proto, NULL,
|
||||
sizeof(raw_hdlc_proto));
|
||||
if (result)
|
||||
return result;
|
||||
memcpy(hdlc->state, &new_settings, size);
|
||||
dev->hard_start_xmit = eth_tx;
|
||||
old_ch_mtu = dev->change_mtu;
|
||||
old_qlen = dev->tx_queue_len;
|
||||
ether_setup(dev);
|
||||
dev->change_mtu = old_ch_mtu;
|
||||
dev->tx_queue_len = old_qlen;
|
||||
memcpy(dev->dev_addr, "\x00\x01", 2);
|
||||
get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2);
|
||||
netif_dormant_off(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static int __init mod_init(void)
|
||||
{
|
||||
register_hdlc_protocol(&proto);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void __exit mod_exit(void)
|
||||
{
|
||||
unregister_hdlc_protocol(&proto);
|
||||
}
|
||||
|
||||
|
||||
module_init(mod_init);
|
||||
module_exit(mod_exit);
|
||||
|
||||
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
|
||||
MODULE_DESCRIPTION("Ethernet encapsulation support for generic HDLC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
246
drivers/net/wan/hdlc_x25.c
Normal file
246
drivers/net/wan/hdlc_x25.c
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Generic HDLC support routines for Linux
|
||||
* X.25 support
|
||||
*
|
||||
* Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/lapb.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/hdlc.h>
|
||||
|
||||
#include <net/x25device.h>
|
||||
|
||||
static int x25_ioctl(struct net_device *dev, struct ifreq *ifr);
|
||||
|
||||
/* These functions are callbacks called by LAPB layer */
|
||||
|
||||
static void x25_connect_disconnect(struct net_device *dev, int reason, int code)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned char *ptr;
|
||||
|
||||
if ((skb = dev_alloc_skb(1)) == NULL) {
|
||||
printk(KERN_ERR "%s: out of memory\n", dev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = skb_put(skb, 1);
|
||||
*ptr = code;
|
||||
|
||||
skb->protocol = x25_type_trans(skb, dev);
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void x25_connected(struct net_device *dev, int reason)
|
||||
{
|
||||
x25_connect_disconnect(dev, reason, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void x25_disconnected(struct net_device *dev, int reason)
|
||||
{
|
||||
x25_connect_disconnect(dev, reason, 2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int x25_data_indication(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
unsigned char *ptr;
|
||||
|
||||
skb_push(skb, 1);
|
||||
|
||||
if (skb_cow(skb, 1))
|
||||
return NET_RX_DROP;
|
||||
|
||||
ptr = skb->data;
|
||||
*ptr = 0;
|
||||
|
||||
skb->protocol = x25_type_trans(skb, dev);
|
||||
return netif_rx(skb);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void x25_data_transmit(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
hdlc->xmit(skb, dev); /* Ignore return value :-( */
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int x25_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
int result;
|
||||
|
||||
|
||||
/* X.25 to LAPB */
|
||||
switch (skb->data[0]) {
|
||||
case 0: /* Data to be transmitted */
|
||||
skb_pull(skb, 1);
|
||||
if ((result = lapb_data_request(dev, skb)) != LAPB_OK)
|
||||
dev_kfree_skb(skb);
|
||||
return 0;
|
||||
|
||||
case 1:
|
||||
if ((result = lapb_connect_request(dev))!= LAPB_OK) {
|
||||
if (result == LAPB_CONNECTED)
|
||||
/* Send connect confirm. msg to level 3 */
|
||||
x25_connected(dev, 0);
|
||||
else
|
||||
printk(KERN_ERR "%s: LAPB connect request "
|
||||
"failed, error code = %i\n",
|
||||
dev->name, result);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if ((result = lapb_disconnect_request(dev)) != LAPB_OK) {
|
||||
if (result == LAPB_NOTCONNECTED)
|
||||
/* Send disconnect confirm. msg to level 3 */
|
||||
x25_disconnected(dev, 0);
|
||||
else
|
||||
printk(KERN_ERR "%s: LAPB disconnect request "
|
||||
"failed, error code = %i\n",
|
||||
dev->name, result);
|
||||
}
|
||||
break;
|
||||
|
||||
default: /* to be defined */
|
||||
break;
|
||||
}
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int x25_open(struct net_device *dev)
|
||||
{
|
||||
struct lapb_register_struct cb;
|
||||
int result;
|
||||
|
||||
cb.connect_confirmation = x25_connected;
|
||||
cb.connect_indication = x25_connected;
|
||||
cb.disconnect_confirmation = x25_disconnected;
|
||||
cb.disconnect_indication = x25_disconnected;
|
||||
cb.data_indication = x25_data_indication;
|
||||
cb.data_transmit = x25_data_transmit;
|
||||
|
||||
result = lapb_register(dev, &cb);
|
||||
if (result != LAPB_OK)
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void x25_close(struct net_device *dev)
|
||||
{
|
||||
lapb_unregister(dev);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int x25_rx(struct sk_buff *skb)
|
||||
{
|
||||
struct hdlc_device_desc *desc = dev_to_desc(skb->dev);
|
||||
|
||||
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
|
||||
desc->stats.rx_dropped++;
|
||||
return NET_RX_DROP;
|
||||
}
|
||||
|
||||
if (lapb_data_received(skb->dev, skb) == LAPB_OK)
|
||||
return NET_RX_SUCCESS;
|
||||
|
||||
desc->stats.rx_errors++;
|
||||
dev_kfree_skb_any(skb);
|
||||
return NET_RX_DROP;
|
||||
}
|
||||
|
||||
|
||||
static struct hdlc_proto proto = {
|
||||
.open = x25_open,
|
||||
.close = x25_close,
|
||||
.ioctl = x25_ioctl,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static int x25_ioctl(struct net_device *dev, struct ifreq *ifr)
|
||||
{
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
int result;
|
||||
|
||||
switch (ifr->ifr_settings.type) {
|
||||
case IF_GET_PROTO:
|
||||
if (dev_to_hdlc(dev)->proto != &proto)
|
||||
return -EINVAL;
|
||||
ifr->ifr_settings.type = IF_PROTO_X25;
|
||||
return 0; /* return protocol only, no settable parameters */
|
||||
|
||||
case IF_PROTO_X25:
|
||||
if(!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if(dev->flags & IFF_UP)
|
||||
return -EBUSY;
|
||||
|
||||
result=hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if ((result = attach_hdlc_protocol(dev, &proto,
|
||||
x25_rx, 0)) != 0)
|
||||
return result;
|
||||
dev->hard_start_xmit = x25_xmit;
|
||||
dev->type = ARPHRD_X25;
|
||||
netif_dormant_off(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static int __init mod_init(void)
|
||||
{
|
||||
register_hdlc_protocol(&proto);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void __exit mod_exit(void)
|
||||
{
|
||||
unregister_hdlc_protocol(&proto);
|
||||
}
|
||||
|
||||
|
||||
module_init(mod_init);
|
||||
module_exit(mod_exit);
|
||||
|
||||
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
|
||||
MODULE_DESCRIPTION("X.25 protocol support for generic HDLC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
421
drivers/net/wan/hostess_sv11.c
Normal file
421
drivers/net/wan/hostess_sv11.c
Normal file
@@ -0,0 +1,421 @@
|
||||
/*
|
||||
* Comtrol SV11 card driver
|
||||
*
|
||||
* This is a slightly odd Z85230 synchronous driver. All you need to
|
||||
* know basically is
|
||||
*
|
||||
* Its a genuine Z85230
|
||||
*
|
||||
* It supports DMA using two DMA channels in SYNC mode. The driver doesn't
|
||||
* use these facilities
|
||||
*
|
||||
* The control port is at io+1, the data at io+3 and turning off the DMA
|
||||
* is done by writing 0 to io+4
|
||||
*
|
||||
* The hardware does the bus handling to avoid the need for delays between
|
||||
* touching control registers.
|
||||
*
|
||||
* Port B isnt wired (why - beats me)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <net/arp.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <net/syncppp.h>
|
||||
#include "z85230.h"
|
||||
|
||||
static int dma;
|
||||
|
||||
struct sv11_device
|
||||
{
|
||||
void *if_ptr; /* General purpose pointer (used by SPPP) */
|
||||
struct z8530_dev sync;
|
||||
struct ppp_device netdev;
|
||||
};
|
||||
|
||||
/*
|
||||
* Network driver support routines
|
||||
*/
|
||||
|
||||
/*
|
||||
* Frame receive. Simple for our card as we do sync ppp and there
|
||||
* is no funny garbage involved
|
||||
*/
|
||||
|
||||
static void hostess_input(struct z8530_channel *c, struct sk_buff *skb)
|
||||
{
|
||||
/* Drop the CRC - it's not a good idea to try and negotiate it ;) */
|
||||
skb_trim(skb, skb->len-2);
|
||||
skb->protocol=__constant_htons(ETH_P_WAN_PPP);
|
||||
skb->mac.raw=skb->data;
|
||||
skb->dev=c->netdevice;
|
||||
/*
|
||||
* Send it to the PPP layer. We don't have time to process
|
||||
* it right now.
|
||||
*/
|
||||
netif_rx(skb);
|
||||
c->netdevice->last_rx = jiffies;
|
||||
}
|
||||
|
||||
/*
|
||||
* We've been placed in the UP state
|
||||
*/
|
||||
|
||||
static int hostess_open(struct net_device *d)
|
||||
{
|
||||
struct sv11_device *sv11=d->priv;
|
||||
int err = -1;
|
||||
|
||||
/*
|
||||
* Link layer up
|
||||
*/
|
||||
switch(dma)
|
||||
{
|
||||
case 0:
|
||||
err=z8530_sync_open(d, &sv11->sync.chanA);
|
||||
break;
|
||||
case 1:
|
||||
err=z8530_sync_dma_open(d, &sv11->sync.chanA);
|
||||
break;
|
||||
case 2:
|
||||
err=z8530_sync_txdma_open(d, &sv11->sync.chanA);
|
||||
break;
|
||||
}
|
||||
|
||||
if(err)
|
||||
return err;
|
||||
/*
|
||||
* Begin PPP
|
||||
*/
|
||||
err=sppp_open(d);
|
||||
if(err)
|
||||
{
|
||||
switch(dma)
|
||||
{
|
||||
case 0:
|
||||
z8530_sync_close(d, &sv11->sync.chanA);
|
||||
break;
|
||||
case 1:
|
||||
z8530_sync_dma_close(d, &sv11->sync.chanA);
|
||||
break;
|
||||
case 2:
|
||||
z8530_sync_txdma_close(d, &sv11->sync.chanA);
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
sv11->sync.chanA.rx_function=hostess_input;
|
||||
|
||||
/*
|
||||
* Go go go
|
||||
*/
|
||||
|
||||
netif_start_queue(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hostess_close(struct net_device *d)
|
||||
{
|
||||
struct sv11_device *sv11=d->priv;
|
||||
/*
|
||||
* Discard new frames
|
||||
*/
|
||||
sv11->sync.chanA.rx_function=z8530_null_rx;
|
||||
/*
|
||||
* PPP off
|
||||
*/
|
||||
sppp_close(d);
|
||||
/*
|
||||
* Link layer down
|
||||
*/
|
||||
netif_stop_queue(d);
|
||||
|
||||
switch(dma)
|
||||
{
|
||||
case 0:
|
||||
z8530_sync_close(d, &sv11->sync.chanA);
|
||||
break;
|
||||
case 1:
|
||||
z8530_sync_dma_close(d, &sv11->sync.chanA);
|
||||
break;
|
||||
case 2:
|
||||
z8530_sync_txdma_close(d, &sv11->sync.chanA);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hostess_ioctl(struct net_device *d, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
/* struct sv11_device *sv11=d->priv;
|
||||
z8530_ioctl(d,&sv11->sync.chanA,ifr,cmd) */
|
||||
return sppp_do_ioctl(d, ifr,cmd);
|
||||
}
|
||||
|
||||
static struct net_device_stats *hostess_get_stats(struct net_device *d)
|
||||
{
|
||||
struct sv11_device *sv11=d->priv;
|
||||
if(sv11)
|
||||
return z8530_get_stats(&sv11->sync.chanA);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Passed PPP frames, fire them downwind.
|
||||
*/
|
||||
|
||||
static int hostess_queue_xmit(struct sk_buff *skb, struct net_device *d)
|
||||
{
|
||||
struct sv11_device *sv11=d->priv;
|
||||
return z8530_queue_xmit(&sv11->sync.chanA, skb);
|
||||
}
|
||||
|
||||
static int hostess_neigh_setup(struct neighbour *n)
|
||||
{
|
||||
if (n->nud_state == NUD_NONE) {
|
||||
n->ops = &arp_broken_ops;
|
||||
n->output = n->ops->output;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hostess_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
|
||||
{
|
||||
if (p->tbl->family == AF_INET) {
|
||||
p->neigh_setup = hostess_neigh_setup;
|
||||
p->ucast_probes = 0;
|
||||
p->mcast_probes = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sv11_setup(struct net_device *dev)
|
||||
{
|
||||
dev->open = hostess_open;
|
||||
dev->stop = hostess_close;
|
||||
dev->hard_start_xmit = hostess_queue_xmit;
|
||||
dev->get_stats = hostess_get_stats;
|
||||
dev->do_ioctl = hostess_ioctl;
|
||||
dev->neigh_setup = hostess_neigh_setup_dev;
|
||||
}
|
||||
|
||||
/*
|
||||
* Description block for a Comtrol Hostess SV11 card
|
||||
*/
|
||||
|
||||
static struct sv11_device *sv11_init(int iobase, int irq)
|
||||
{
|
||||
struct z8530_dev *dev;
|
||||
struct sv11_device *sv;
|
||||
|
||||
/*
|
||||
* Get the needed I/O space
|
||||
*/
|
||||
|
||||
if(!request_region(iobase, 8, "Comtrol SV11"))
|
||||
{
|
||||
printk(KERN_WARNING "hostess: I/O 0x%X already in use.\n", iobase);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sv = kmalloc(sizeof(struct sv11_device), GFP_KERNEL);
|
||||
if(!sv)
|
||||
goto fail3;
|
||||
|
||||
memset(sv, 0, sizeof(*sv));
|
||||
sv->if_ptr=&sv->netdev;
|
||||
|
||||
sv->netdev.dev = alloc_netdev(0, "hdlc%d", sv11_setup);
|
||||
if(!sv->netdev.dev)
|
||||
goto fail2;
|
||||
|
||||
SET_MODULE_OWNER(sv->netdev.dev);
|
||||
|
||||
dev=&sv->sync;
|
||||
|
||||
/*
|
||||
* Stuff in the I/O addressing
|
||||
*/
|
||||
|
||||
dev->active = 0;
|
||||
|
||||
dev->chanA.ctrlio=iobase+1;
|
||||
dev->chanA.dataio=iobase+3;
|
||||
dev->chanB.ctrlio=-1;
|
||||
dev->chanB.dataio=-1;
|
||||
dev->chanA.irqs=&z8530_nop;
|
||||
dev->chanB.irqs=&z8530_nop;
|
||||
|
||||
outb(0, iobase+4); /* DMA off */
|
||||
|
||||
/* We want a fast IRQ for this device. Actually we'd like an even faster
|
||||
IRQ ;) - This is one driver RtLinux is made for */
|
||||
|
||||
if(request_irq(irq, &z8530_interrupt, IRQF_DISABLED, "Hostess SV11", dev)<0)
|
||||
{
|
||||
printk(KERN_WARNING "hostess: IRQ %d already in use.\n", irq);
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
dev->irq=irq;
|
||||
dev->chanA.private=sv;
|
||||
dev->chanA.netdevice=sv->netdev.dev;
|
||||
dev->chanA.dev=dev;
|
||||
dev->chanB.dev=dev;
|
||||
|
||||
if(dma)
|
||||
{
|
||||
/*
|
||||
* You can have DMA off or 1 and 3 thats the lot
|
||||
* on the Comtrol.
|
||||
*/
|
||||
dev->chanA.txdma=3;
|
||||
dev->chanA.rxdma=1;
|
||||
outb(0x03|0x08, iobase+4); /* DMA on */
|
||||
if(request_dma(dev->chanA.txdma, "Hostess SV/11 (TX)")!=0)
|
||||
goto fail;
|
||||
|
||||
if(dma==1)
|
||||
{
|
||||
if(request_dma(dev->chanA.rxdma, "Hostess SV/11 (RX)")!=0)
|
||||
goto dmafail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Kill our private IRQ line the hostess can end up chattering
|
||||
until the configuration is set */
|
||||
disable_irq(irq);
|
||||
|
||||
/*
|
||||
* Begin normal initialise
|
||||
*/
|
||||
|
||||
if(z8530_init(dev)!=0)
|
||||
{
|
||||
printk(KERN_ERR "Z8530 series device not found.\n");
|
||||
enable_irq(irq);
|
||||
goto dmafail2;
|
||||
}
|
||||
z8530_channel_load(&dev->chanB, z8530_dead_port);
|
||||
if(dev->type==Z85C30)
|
||||
z8530_channel_load(&dev->chanA, z8530_hdlc_kilostream);
|
||||
else
|
||||
z8530_channel_load(&dev->chanA, z8530_hdlc_kilostream_85230);
|
||||
|
||||
enable_irq(irq);
|
||||
|
||||
|
||||
/*
|
||||
* Now we can take the IRQ
|
||||
*/
|
||||
if(dev_alloc_name(dev->chanA.netdevice,"hdlc%d")>=0)
|
||||
{
|
||||
struct net_device *d=dev->chanA.netdevice;
|
||||
|
||||
/*
|
||||
* Initialise the PPP components
|
||||
*/
|
||||
sppp_attach(&sv->netdev);
|
||||
|
||||
/*
|
||||
* Local fields
|
||||
*/
|
||||
|
||||
d->base_addr = iobase;
|
||||
d->irq = irq;
|
||||
d->priv = sv;
|
||||
|
||||
if(register_netdev(d))
|
||||
{
|
||||
printk(KERN_ERR "%s: unable to register device.\n",
|
||||
d->name);
|
||||
sppp_detach(d);
|
||||
goto dmafail2;
|
||||
}
|
||||
|
||||
z8530_describe(dev, "I/O", iobase);
|
||||
dev->active=1;
|
||||
return sv;
|
||||
}
|
||||
dmafail2:
|
||||
if(dma==1)
|
||||
free_dma(dev->chanA.rxdma);
|
||||
dmafail:
|
||||
if(dma)
|
||||
free_dma(dev->chanA.txdma);
|
||||
fail:
|
||||
free_irq(irq, dev);
|
||||
fail1:
|
||||
free_netdev(sv->netdev.dev);
|
||||
fail2:
|
||||
kfree(sv);
|
||||
fail3:
|
||||
release_region(iobase,8);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void sv11_shutdown(struct sv11_device *dev)
|
||||
{
|
||||
sppp_detach(dev->netdev.dev);
|
||||
unregister_netdev(dev->netdev.dev);
|
||||
z8530_shutdown(&dev->sync);
|
||||
free_irq(dev->sync.irq, dev);
|
||||
if(dma)
|
||||
{
|
||||
if(dma==1)
|
||||
free_dma(dev->sync.chanA.rxdma);
|
||||
free_dma(dev->sync.chanA.txdma);
|
||||
}
|
||||
release_region(dev->sync.chanA.ctrlio-1, 8);
|
||||
free_netdev(dev->netdev.dev);
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
|
||||
static int io=0x200;
|
||||
static int irq=9;
|
||||
|
||||
module_param(io, int, 0);
|
||||
MODULE_PARM_DESC(io, "The I/O base of the Comtrol Hostess SV11 card");
|
||||
module_param(dma, int, 0);
|
||||
MODULE_PARM_DESC(dma, "Set this to 1 to use DMA1/DMA3 for TX/RX");
|
||||
module_param(irq, int, 0);
|
||||
MODULE_PARM_DESC(irq, "The interrupt line setting for the Comtrol Hostess SV11 card");
|
||||
|
||||
MODULE_AUTHOR("Alan Cox");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Modular driver for the Comtrol Hostess SV11");
|
||||
|
||||
static struct sv11_device *sv11_unit;
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
printk(KERN_INFO "SV-11 Z85230 Synchronous Driver v 0.03.\n");
|
||||
printk(KERN_INFO "(c) Copyright 2001, Red Hat Inc.\n");
|
||||
if((sv11_unit=sv11_init(io,irq))==NULL)
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cleanup_module(void)
|
||||
{
|
||||
if(sv11_unit)
|
||||
sv11_shutdown(sv11_unit);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
465
drivers/net/wan/lapbether.c
Normal file
465
drivers/net/wan/lapbether.c
Normal file
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
* "LAPB via ethernet" driver release 001
|
||||
*
|
||||
* This code REQUIRES 2.1.15 or higher/ NET3.038
|
||||
*
|
||||
* 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 LAPB over Ethernet.
|
||||
*
|
||||
* This driver can use any ethernet destination address, and can be
|
||||
* limited to accept frames from one dedicated ethernet card only.
|
||||
*
|
||||
* History
|
||||
* LAPBETH 001 Jonathan Naylor Cloned from bpqether.c
|
||||
* 2000-10-29 Henner Eisen lapb_data_indication() return status.
|
||||
* 2000-11-14 Henner Eisen dev_hold/put, NETDEV_GOING_DOWN support
|
||||
*/
|
||||
|
||||
#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/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/lapb.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <net/x25device.h>
|
||||
|
||||
static char bcast_addr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
|
||||
/* If this number is made larger, check that the temporary string buffer
|
||||
* in lapbeth_new_device is large enough to store the probe device name.*/
|
||||
#define MAXLAPBDEV 100
|
||||
|
||||
struct lapbethdev {
|
||||
struct list_head node;
|
||||
struct net_device *ethdev; /* link to ethernet device */
|
||||
struct net_device *axdev; /* lapbeth device (lapb#) */
|
||||
struct net_device_stats stats; /* some statistics */
|
||||
};
|
||||
|
||||
static struct list_head lapbeth_devices = LIST_HEAD_INIT(lapbeth_devices);
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/*
|
||||
* Get the LAPB device for the ethernet device
|
||||
*/
|
||||
static struct lapbethdev *lapbeth_get_x25_dev(struct net_device *dev)
|
||||
{
|
||||
struct lapbethdev *lapbeth;
|
||||
|
||||
list_for_each_entry_rcu(lapbeth, &lapbeth_devices, node) {
|
||||
if (lapbeth->ethdev == dev)
|
||||
return lapbeth;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static __inline__ int dev_is_ethdev(struct net_device *dev)
|
||||
{
|
||||
return dev->type == ARPHRD_ETHER && strncmp(dev->name, "dummy", 5);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/*
|
||||
* Receive a LAPB frame via an ethernet interface.
|
||||
*/
|
||||
static int lapbeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev)
|
||||
{
|
||||
int len, err;
|
||||
struct lapbethdev *lapbeth;
|
||||
|
||||
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
|
||||
return NET_RX_DROP;
|
||||
|
||||
if (!pskb_may_pull(skb, 2))
|
||||
goto drop;
|
||||
|
||||
rcu_read_lock();
|
||||
lapbeth = lapbeth_get_x25_dev(dev);
|
||||
if (!lapbeth)
|
||||
goto drop_unlock;
|
||||
if (!netif_running(lapbeth->axdev))
|
||||
goto drop_unlock;
|
||||
|
||||
lapbeth->stats.rx_packets++;
|
||||
|
||||
len = skb->data[0] + skb->data[1] * 256;
|
||||
lapbeth->stats.rx_bytes += len;
|
||||
|
||||
skb_pull(skb, 2); /* Remove the length bytes */
|
||||
skb_trim(skb, len); /* Set the length of the data */
|
||||
|
||||
if ((err = lapb_data_received(lapbeth->axdev, skb)) != LAPB_OK) {
|
||||
printk(KERN_DEBUG "lapbether: lapb_data_received err - %d\n", err);
|
||||
goto drop_unlock;
|
||||
}
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
drop_unlock:
|
||||
kfree_skb(skb);
|
||||
goto out;
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lapbeth_data_indication(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
unsigned char *ptr;
|
||||
|
||||
skb_push(skb, 1);
|
||||
|
||||
if (skb_cow(skb, 1))
|
||||
return NET_RX_DROP;
|
||||
|
||||
ptr = skb->data;
|
||||
*ptr = 0x00;
|
||||
|
||||
skb->protocol = x25_type_trans(skb, dev);
|
||||
skb->dev->last_rx = jiffies;
|
||||
return netif_rx(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a LAPB frame via an ethernet interface
|
||||
*/
|
||||
static int lapbeth_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
int err = -ENODEV;
|
||||
|
||||
/*
|
||||
* Just to be *really* sure not to send anything if the interface
|
||||
* is down, the ethernet device may have gone.
|
||||
*/
|
||||
if (!netif_running(dev)) {
|
||||
goto drop;
|
||||
}
|
||||
|
||||
switch (skb->data[0]) {
|
||||
case 0x00:
|
||||
err = 0;
|
||||
break;
|
||||
case 0x01:
|
||||
if ((err = lapb_connect_request(dev)) != LAPB_OK)
|
||||
printk(KERN_ERR "lapbeth: lapb_connect_request "
|
||||
"error: %d\n", err);
|
||||
goto drop_ok;
|
||||
case 0x02:
|
||||
if ((err = lapb_disconnect_request(dev)) != LAPB_OK)
|
||||
printk(KERN_ERR "lapbeth: lapb_disconnect_request "
|
||||
"err: %d\n", err);
|
||||
/* Fall thru */
|
||||
default:
|
||||
goto drop_ok;
|
||||
}
|
||||
|
||||
skb_pull(skb, 1);
|
||||
|
||||
if ((err = lapb_data_request(dev, skb)) != LAPB_OK) {
|
||||
printk(KERN_ERR "lapbeth: lapb_data_request error - %d\n", err);
|
||||
err = -ENOMEM;
|
||||
goto drop;
|
||||
}
|
||||
err = 0;
|
||||
out:
|
||||
return err;
|
||||
drop_ok:
|
||||
err = 0;
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void lapbeth_data_transmit(struct net_device *ndev, struct sk_buff *skb)
|
||||
{
|
||||
struct lapbethdev *lapbeth = netdev_priv(ndev);
|
||||
unsigned char *ptr;
|
||||
struct net_device *dev;
|
||||
int size = skb->len;
|
||||
|
||||
skb->protocol = htons(ETH_P_X25);
|
||||
|
||||
ptr = skb_push(skb, 2);
|
||||
|
||||
*ptr++ = size % 256;
|
||||
*ptr++ = size / 256;
|
||||
|
||||
lapbeth->stats.tx_packets++;
|
||||
lapbeth->stats.tx_bytes += size;
|
||||
|
||||
skb->dev = dev = lapbeth->ethdev;
|
||||
|
||||
dev->hard_header(skb, dev, ETH_P_DEC, bcast_addr, NULL, 0);
|
||||
|
||||
dev_queue_xmit(skb);
|
||||
}
|
||||
|
||||
static void lapbeth_connected(struct net_device *dev, int reason)
|
||||
{
|
||||
unsigned char *ptr;
|
||||
struct sk_buff *skb = dev_alloc_skb(1);
|
||||
|
||||
if (!skb) {
|
||||
printk(KERN_ERR "lapbeth: out of memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = skb_put(skb, 1);
|
||||
*ptr = 0x01;
|
||||
|
||||
skb->protocol = x25_type_trans(skb, dev);
|
||||
skb->dev->last_rx = jiffies;
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
||||
static void lapbeth_disconnected(struct net_device *dev, int reason)
|
||||
{
|
||||
unsigned char *ptr;
|
||||
struct sk_buff *skb = dev_alloc_skb(1);
|
||||
|
||||
if (!skb) {
|
||||
printk(KERN_ERR "lapbeth: out of memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = skb_put(skb, 1);
|
||||
*ptr = 0x02;
|
||||
|
||||
skb->protocol = x25_type_trans(skb, dev);
|
||||
skb->dev->last_rx = jiffies;
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Statistics
|
||||
*/
|
||||
static struct net_device_stats *lapbeth_get_stats(struct net_device *dev)
|
||||
{
|
||||
struct lapbethdev *lapbeth = netdev_priv(dev);
|
||||
return &lapbeth->stats;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set AX.25 callsign
|
||||
*/
|
||||
static int lapbeth_set_mac_address(struct net_device *dev, void *addr)
|
||||
{
|
||||
struct sockaddr *sa = addr;
|
||||
memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct lapb_register_struct lapbeth_callbacks = {
|
||||
.connect_confirmation = lapbeth_connected,
|
||||
.connect_indication = lapbeth_connected,
|
||||
.disconnect_confirmation = lapbeth_disconnected,
|
||||
.disconnect_indication = lapbeth_disconnected,
|
||||
.data_indication = lapbeth_data_indication,
|
||||
.data_transmit = lapbeth_data_transmit,
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* open/close a device
|
||||
*/
|
||||
static int lapbeth_open(struct net_device *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = lapb_register(dev, &lapbeth_callbacks)) != LAPB_OK) {
|
||||
printk(KERN_ERR "lapbeth: lapb_register error - %d\n", err);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
netif_start_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lapbeth_close(struct net_device *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
if ((err = lapb_unregister(dev)) != LAPB_OK)
|
||||
printk(KERN_ERR "lapbeth: lapb_unregister error - %d\n", err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void lapbeth_setup(struct net_device *dev)
|
||||
{
|
||||
dev->hard_start_xmit = lapbeth_xmit;
|
||||
dev->open = lapbeth_open;
|
||||
dev->stop = lapbeth_close;
|
||||
dev->destructor = free_netdev;
|
||||
dev->set_mac_address = lapbeth_set_mac_address;
|
||||
dev->get_stats = lapbeth_get_stats;
|
||||
dev->type = ARPHRD_X25;
|
||||
dev->hard_header_len = 3;
|
||||
dev->mtu = 1000;
|
||||
dev->addr_len = 0;
|
||||
SET_MODULE_OWNER(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup a new device.
|
||||
*/
|
||||
static int lapbeth_new_device(struct net_device *dev)
|
||||
{
|
||||
struct net_device *ndev;
|
||||
struct lapbethdev *lapbeth;
|
||||
int rc = -ENOMEM;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
ndev = alloc_netdev(sizeof(*lapbeth), "lapb%d",
|
||||
lapbeth_setup);
|
||||
if (!ndev)
|
||||
goto out;
|
||||
|
||||
lapbeth = netdev_priv(ndev);
|
||||
lapbeth->axdev = ndev;
|
||||
|
||||
dev_hold(dev);
|
||||
lapbeth->ethdev = dev;
|
||||
|
||||
rc = dev_alloc_name(ndev, ndev->name);
|
||||
if (rc < 0)
|
||||
goto fail;
|
||||
|
||||
rc = -EIO;
|
||||
if (register_netdevice(ndev))
|
||||
goto fail;
|
||||
|
||||
list_add_rcu(&lapbeth->node, &lapbeth_devices);
|
||||
rc = 0;
|
||||
out:
|
||||
return rc;
|
||||
fail:
|
||||
dev_put(dev);
|
||||
free_netdev(ndev);
|
||||
kfree(lapbeth);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free a lapb network device.
|
||||
*/
|
||||
static void lapbeth_free_device(struct lapbethdev *lapbeth)
|
||||
{
|
||||
dev_put(lapbeth->ethdev);
|
||||
list_del_rcu(&lapbeth->node);
|
||||
unregister_netdevice(lapbeth->axdev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle device status changes.
|
||||
*
|
||||
* Called from notifier with RTNL held.
|
||||
*/
|
||||
static int lapbeth_device_event(struct notifier_block *this,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct lapbethdev *lapbeth;
|
||||
struct net_device *dev = ptr;
|
||||
|
||||
if (!dev_is_ethdev(dev))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
switch (event) {
|
||||
case NETDEV_UP:
|
||||
/* New ethernet device -> new LAPB interface */
|
||||
if (lapbeth_get_x25_dev(dev) == NULL)
|
||||
lapbeth_new_device(dev);
|
||||
break;
|
||||
case NETDEV_DOWN:
|
||||
/* ethernet device closed -> close LAPB interface */
|
||||
lapbeth = lapbeth_get_x25_dev(dev);
|
||||
if (lapbeth)
|
||||
dev_close(lapbeth->axdev);
|
||||
break;
|
||||
case NETDEV_UNREGISTER:
|
||||
/* ethernet device disappears -> remove LAPB interface */
|
||||
lapbeth = lapbeth_get_x25_dev(dev);
|
||||
if (lapbeth)
|
||||
lapbeth_free_device(lapbeth);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static struct packet_type lapbeth_packet_type = {
|
||||
.type = __constant_htons(ETH_P_DEC),
|
||||
.func = lapbeth_rcv,
|
||||
};
|
||||
|
||||
static struct notifier_block lapbeth_dev_notifier = {
|
||||
.notifier_call = lapbeth_device_event,
|
||||
};
|
||||
|
||||
static char banner[] __initdata = KERN_INFO "LAPB Ethernet driver version 0.02\n";
|
||||
|
||||
static int __init lapbeth_init_driver(void)
|
||||
{
|
||||
dev_add_pack(&lapbeth_packet_type);
|
||||
|
||||
register_netdevice_notifier(&lapbeth_dev_notifier);
|
||||
|
||||
printk(banner);
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(lapbeth_init_driver);
|
||||
|
||||
static void __exit lapbeth_cleanup_driver(void)
|
||||
{
|
||||
struct lapbethdev *lapbeth;
|
||||
struct list_head *entry, *tmp;
|
||||
|
||||
dev_remove_pack(&lapbeth_packet_type);
|
||||
unregister_netdevice_notifier(&lapbeth_dev_notifier);
|
||||
|
||||
rtnl_lock();
|
||||
list_for_each_safe(entry, tmp, &lapbeth_devices) {
|
||||
lapbeth = list_entry(entry, struct lapbethdev, node);
|
||||
|
||||
unregister_netdevice(lapbeth->axdev);
|
||||
}
|
||||
rtnl_unlock();
|
||||
}
|
||||
module_exit(lapbeth_cleanup_driver);
|
||||
|
||||
MODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>");
|
||||
MODULE_DESCRIPTION("The unofficial LAPB over Ethernet driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
17
drivers/net/wan/lmc/Makefile
Normal file
17
drivers/net/wan/lmc/Makefile
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# Makefile for the Lan Media 21140 based WAN cards
|
||||
# Specifically the 1000,1200,5200,5245
|
||||
#
|
||||
|
||||
obj-$(CONFIG_LANMEDIA) += lmc.o
|
||||
|
||||
lmc-objs := lmc_debug.o lmc_media.o lmc_main.o lmc_proto.o
|
||||
|
||||
# Like above except every packet gets echoed to KERN_DEBUG
|
||||
# in hex
|
||||
#
|
||||
# DBDEF = \
|
||||
# -DDEBUG \
|
||||
# -DLMC_PACKET_LOG
|
||||
|
||||
EXTRA_CFLAGS += -I. $(DBGDEF)
|
||||
33
drivers/net/wan/lmc/lmc.h
Normal file
33
drivers/net/wan/lmc/lmc.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef _LMC_H_
|
||||
#define _LMC_H_
|
||||
|
||||
#include "lmc_var.h"
|
||||
|
||||
/*
|
||||
* prototypes for everyone
|
||||
*/
|
||||
int lmc_probe(struct net_device * dev);
|
||||
unsigned lmc_mii_readreg(lmc_softc_t * const sc, unsigned
|
||||
devaddr, unsigned regno);
|
||||
void lmc_mii_writereg(lmc_softc_t * const sc, unsigned devaddr,
|
||||
unsigned regno, unsigned data);
|
||||
void lmc_led_on(lmc_softc_t * const, u_int32_t);
|
||||
void lmc_led_off(lmc_softc_t * const, u_int32_t);
|
||||
unsigned lmc_mii_readreg(lmc_softc_t * const, unsigned, unsigned);
|
||||
void lmc_mii_writereg(lmc_softc_t * const, unsigned, unsigned, unsigned);
|
||||
void lmc_gpio_mkinput(lmc_softc_t * const sc, u_int32_t bits);
|
||||
void lmc_gpio_mkoutput(lmc_softc_t * const sc, u_int32_t bits);
|
||||
|
||||
int lmc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
|
||||
|
||||
extern lmc_media_t lmc_ds3_media;
|
||||
extern lmc_media_t lmc_ssi_media;
|
||||
extern lmc_media_t lmc_t1_media;
|
||||
extern lmc_media_t lmc_hssi_media;
|
||||
|
||||
#ifdef _DBG_EVENTLOG
|
||||
static void lmcEventLog( u_int32_t EventNum, u_int32_t arg2, u_int32_t arg3 );
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
83
drivers/net/wan/lmc/lmc_debug.c
Normal file
83
drivers/net/wan/lmc/lmc_debug.c
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "lmc_debug.h"
|
||||
|
||||
/*
|
||||
* Prints out len, max to 80 octets using printk, 20 per line
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
#ifdef LMC_PACKET_LOG
|
||||
void lmcConsoleLog(char *type, unsigned char *ucData, int iLen)
|
||||
{
|
||||
int iNewLine = 1;
|
||||
char str[80], *pstr;
|
||||
|
||||
sprintf(str, KERN_DEBUG "lmc: %s: ", type);
|
||||
pstr = str+strlen(str);
|
||||
|
||||
if(iLen > 240){
|
||||
printk(KERN_DEBUG "lmc: Printing 240 chars... out of: %d\n", iLen);
|
||||
iLen = 240;
|
||||
}
|
||||
else{
|
||||
printk(KERN_DEBUG "lmc: Printing %d chars\n", iLen);
|
||||
}
|
||||
|
||||
while(iLen > 0)
|
||||
{
|
||||
sprintf(pstr, "%02x ", *ucData);
|
||||
pstr+=3;
|
||||
ucData++;
|
||||
if( !(iNewLine % 20))
|
||||
{
|
||||
sprintf(pstr, "\n");
|
||||
printk(str);
|
||||
sprintf(str, KERN_DEBUG "lmc: %s: ", type);
|
||||
pstr=str+strlen(str);
|
||||
}
|
||||
iNewLine++;
|
||||
iLen--;
|
||||
}
|
||||
sprintf(pstr, "\n");
|
||||
printk(str);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
u_int32_t lmcEventLogIndex = 0;
|
||||
u_int32_t lmcEventLogBuf[LMC_EVENTLOGSIZE * LMC_EVENTLOGARGS];
|
||||
|
||||
void lmcEventLog (u_int32_t EventNum, u_int32_t arg2, u_int32_t arg3)
|
||||
{
|
||||
lmcEventLogBuf[lmcEventLogIndex++] = EventNum;
|
||||
lmcEventLogBuf[lmcEventLogIndex++] = arg2;
|
||||
lmcEventLogBuf[lmcEventLogIndex++] = arg3;
|
||||
lmcEventLogBuf[lmcEventLogIndex++] = jiffies;
|
||||
|
||||
lmcEventLogIndex &= (LMC_EVENTLOGSIZE * LMC_EVENTLOGARGS) - 1;
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
void lmc_trace(struct net_device *dev, char *msg){
|
||||
#ifdef LMC_TRACE
|
||||
unsigned long j = jiffies + 3; /* Wait for 50 ms */
|
||||
|
||||
if(in_interrupt()){
|
||||
printk("%s: * %s\n", dev->name, msg);
|
||||
// while(time_before(jiffies, j+10))
|
||||
// ;
|
||||
}
|
||||
else {
|
||||
printk("%s: %s\n", dev->name, msg);
|
||||
while(time_before(jiffies, j))
|
||||
schedule();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------- end if_lmc_linux.c ------------------------ */
|
||||
52
drivers/net/wan/lmc/lmc_debug.h
Normal file
52
drivers/net/wan/lmc/lmc_debug.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef _LMC_DEBUG_H_
|
||||
#define _LMC_DEBUG_H_
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef LMC_PACKET_LOG
|
||||
#define LMC_CONSOLE_LOG(x,y,z) lmcConsoleLog((x), (y), (z))
|
||||
#else
|
||||
#define LMC_CONSOLE_LOG(x,y,z)
|
||||
#endif
|
||||
#else
|
||||
#define LMC_CONSOLE_LOG(x,y,z)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Debug --- Event log definitions --- */
|
||||
/* EVENTLOGSIZE*EVENTLOGARGS needs to be a power of 2 */
|
||||
#define LMC_EVENTLOGSIZE 1024 /* number of events in eventlog */
|
||||
#define LMC_EVENTLOGARGS 4 /* number of args for each event */
|
||||
|
||||
/* event indicators */
|
||||
#define LMC_EVENT_XMT 1
|
||||
#define LMC_EVENT_XMTEND 2
|
||||
#define LMC_EVENT_XMTINT 3
|
||||
#define LMC_EVENT_RCVINT 4
|
||||
#define LMC_EVENT_RCVEND 5
|
||||
#define LMC_EVENT_INT 6
|
||||
#define LMC_EVENT_XMTINTTMO 7
|
||||
#define LMC_EVENT_XMTPRCTMO 8
|
||||
#define LMC_EVENT_INTEND 9
|
||||
#define LMC_EVENT_RESET1 10
|
||||
#define LMC_EVENT_RESET2 11
|
||||
#define LMC_EVENT_FORCEDRESET 12
|
||||
#define LMC_EVENT_WATCHDOG 13
|
||||
#define LMC_EVENT_BADPKTSURGE 14
|
||||
#define LMC_EVENT_TBUSY0 15
|
||||
#define LMC_EVENT_TBUSY1 16
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
extern u_int32_t lmcEventLogIndex;
|
||||
extern u_int32_t lmcEventLogBuf[LMC_EVENTLOGSIZE * LMC_EVENTLOGARGS];
|
||||
#define LMC_EVENT_LOG(x, y, z) lmcEventLog((x), (y), (z))
|
||||
#else
|
||||
#define LMC_EVENT_LOG(x,y,z)
|
||||
#endif /* end ifdef _DBG_EVENTLOG */
|
||||
|
||||
void lmcConsoleLog(char *type, unsigned char *ucData, int iLen);
|
||||
void lmcEventLog (u_int32_t EventNum, u_int32_t arg2, u_int32_t arg3);
|
||||
void lmc_trace(struct net_device *dev, char *msg);
|
||||
|
||||
#endif
|
||||
257
drivers/net/wan/lmc/lmc_ioctl.h
Normal file
257
drivers/net/wan/lmc/lmc_ioctl.h
Normal file
@@ -0,0 +1,257 @@
|
||||
#ifndef _LMC_IOCTL_H_
|
||||
#define _LMC_IOCTL_H_
|
||||
/* $Id: lmc_ioctl.h,v 1.1.1.1 2007/06/12 07:27:11 eyryu Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1997-2000 LAN Media Corporation (LMC)
|
||||
* All rights reserved. www.lanmedia.com
|
||||
*
|
||||
* This code is written by:
|
||||
* Andrew Stanley-Jones (asj@cban.com)
|
||||
* Rob Braun (bbraun@vix.com),
|
||||
* Michael Graff (explorer@vix.com) and
|
||||
* Matt Thomas (matt@3am-software.com).
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License version 2, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#define LMCIOCGINFO SIOCDEVPRIVATE+3 /* get current state */
|
||||
#define LMCIOCSINFO SIOCDEVPRIVATE+4 /* set state to user values */
|
||||
#define LMCIOCGETLMCSTATS SIOCDEVPRIVATE+5
|
||||
#define LMCIOCCLEARLMCSTATS SIOCDEVPRIVATE+6
|
||||
#define LMCIOCDUMPEVENTLOG SIOCDEVPRIVATE+7
|
||||
#define LMCIOCGETXINFO SIOCDEVPRIVATE+8
|
||||
#define LMCIOCSETCIRCUIT SIOCDEVPRIVATE+9
|
||||
#define LMCIOCUNUSEDATM SIOCDEVPRIVATE+10
|
||||
#define LMCIOCRESET SIOCDEVPRIVATE+11
|
||||
#define LMCIOCT1CONTROL SIOCDEVPRIVATE+12
|
||||
#define LMCIOCIFTYPE SIOCDEVPRIVATE+13
|
||||
#define LMCIOCXILINX SIOCDEVPRIVATE+14
|
||||
|
||||
#define LMC_CARDTYPE_UNKNOWN -1
|
||||
#define LMC_CARDTYPE_HSSI 1 /* probed card is a HSSI card */
|
||||
#define LMC_CARDTYPE_DS3 2 /* probed card is a DS3 card */
|
||||
#define LMC_CARDTYPE_SSI 3 /* probed card is a SSI card */
|
||||
#define LMC_CARDTYPE_T1 4 /* probed card is a T1 card */
|
||||
|
||||
#define LMC_CTL_CARDTYPE_LMC5200 0 /* HSSI */
|
||||
#define LMC_CTL_CARDTYPE_LMC5245 1 /* DS3 */
|
||||
#define LMC_CTL_CARDTYPE_LMC1000 2 /* SSI, V.35 */
|
||||
#define LMC_CTL_CARDTYPE_LMC1200 3 /* DS1 */
|
||||
|
||||
#define LMC_CTL_OFF 0 /* generic OFF value */
|
||||
#define LMC_CTL_ON 1 /* generic ON value */
|
||||
|
||||
#define LMC_CTL_CLOCK_SOURCE_EXT 0 /* clock off line */
|
||||
#define LMC_CTL_CLOCK_SOURCE_INT 1 /* internal clock */
|
||||
|
||||
#define LMC_CTL_CRC_LENGTH_16 16
|
||||
#define LMC_CTL_CRC_LENGTH_32 32
|
||||
#define LMC_CTL_CRC_BYTESIZE_2 2
|
||||
#define LMC_CTL_CRC_BYTESIZE_4 4
|
||||
|
||||
|
||||
#define LMC_CTL_CABLE_LENGTH_LT_100FT 0 /* DS3 cable < 100 feet */
|
||||
#define LMC_CTL_CABLE_LENGTH_GT_100FT 1 /* DS3 cable >= 100 feet */
|
||||
|
||||
#define LMC_CTL_CIRCUIT_TYPE_E1 0
|
||||
#define LMC_CTL_CIRCUIT_TYPE_T1 1
|
||||
|
||||
/*
|
||||
* IFTYPE defines
|
||||
*/
|
||||
#define LMC_PPP 1 /* use sppp interface */
|
||||
#define LMC_NET 2 /* use direct net interface */
|
||||
#define LMC_RAW 3 /* use direct net interface */
|
||||
|
||||
/*
|
||||
* These are not in the least IOCTL related, but I want them common.
|
||||
*/
|
||||
/*
|
||||
* assignments for the GPIO register on the DEC chip (common)
|
||||
*/
|
||||
#define LMC_GEP_INIT 0x01 /* 0: */
|
||||
#define LMC_GEP_RESET 0x02 /* 1: */
|
||||
#define LMC_GEP_MODE 0x10 /* 4: */
|
||||
#define LMC_GEP_DP 0x20 /* 5: */
|
||||
#define LMC_GEP_DATA 0x40 /* 6: serial out */
|
||||
#define LMC_GEP_CLK 0x80 /* 7: serial clock */
|
||||
|
||||
/*
|
||||
* HSSI GPIO assignments
|
||||
*/
|
||||
#define LMC_GEP_HSSI_ST 0x04 /* 2: receive timing sense (deprecated) */
|
||||
#define LMC_GEP_HSSI_CLOCK 0x08 /* 3: clock source */
|
||||
|
||||
/*
|
||||
* T1 GPIO assignments
|
||||
*/
|
||||
#define LMC_GEP_SSI_GENERATOR 0x04 /* 2: enable prog freq gen serial i/f */
|
||||
#define LMC_GEP_SSI_TXCLOCK 0x08 /* 3: provide clock on TXCLOCK output */
|
||||
|
||||
/*
|
||||
* Common MII16 bits
|
||||
*/
|
||||
#define LMC_MII16_LED0 0x0080
|
||||
#define LMC_MII16_LED1 0x0100
|
||||
#define LMC_MII16_LED2 0x0200
|
||||
#define LMC_MII16_LED3 0x0400 /* Error, and the red one */
|
||||
#define LMC_MII16_LED_ALL 0x0780 /* LED bit mask */
|
||||
#define LMC_MII16_FIFO_RESET 0x0800
|
||||
|
||||
/*
|
||||
* definitions for HSSI
|
||||
*/
|
||||
#define LMC_MII16_HSSI_TA 0x0001
|
||||
#define LMC_MII16_HSSI_CA 0x0002
|
||||
#define LMC_MII16_HSSI_LA 0x0004
|
||||
#define LMC_MII16_HSSI_LB 0x0008
|
||||
#define LMC_MII16_HSSI_LC 0x0010
|
||||
#define LMC_MII16_HSSI_TM 0x0020
|
||||
#define LMC_MII16_HSSI_CRC 0x0040
|
||||
|
||||
/*
|
||||
* assignments for the MII register 16 (DS3)
|
||||
*/
|
||||
#define LMC_MII16_DS3_ZERO 0x0001
|
||||
#define LMC_MII16_DS3_TRLBK 0x0002
|
||||
#define LMC_MII16_DS3_LNLBK 0x0004
|
||||
#define LMC_MII16_DS3_RAIS 0x0008
|
||||
#define LMC_MII16_DS3_TAIS 0x0010
|
||||
#define LMC_MII16_DS3_BIST 0x0020
|
||||
#define LMC_MII16_DS3_DLOS 0x0040
|
||||
#define LMC_MII16_DS3_CRC 0x1000
|
||||
#define LMC_MII16_DS3_SCRAM 0x2000
|
||||
#define LMC_MII16_DS3_SCRAM_LARS 0x4000
|
||||
|
||||
/* Note: 2 pairs of LEDs where swapped by mistake
|
||||
* in Xilinx code for DS3 & DS1 adapters */
|
||||
#define LMC_DS3_LED0 0x0100 /* bit 08 yellow */
|
||||
#define LMC_DS3_LED1 0x0080 /* bit 07 blue */
|
||||
#define LMC_DS3_LED2 0x0400 /* bit 10 green */
|
||||
#define LMC_DS3_LED3 0x0200 /* bit 09 red */
|
||||
|
||||
/*
|
||||
* framer register 0 and 7 (7 is latched and reset on read)
|
||||
*/
|
||||
#define LMC_FRAMER_REG0_DLOS 0x80 /* digital loss of service */
|
||||
#define LMC_FRAMER_REG0_OOFS 0x40 /* out of frame sync */
|
||||
#define LMC_FRAMER_REG0_AIS 0x20 /* alarm indication signal */
|
||||
#define LMC_FRAMER_REG0_CIS 0x10 /* channel idle */
|
||||
#define LMC_FRAMER_REG0_LOC 0x08 /* loss of clock */
|
||||
|
||||
/*
|
||||
* Framer register 9 contains the blue alarm signal
|
||||
*/
|
||||
#define LMC_FRAMER_REG9_RBLUE 0x02 /* Blue alarm failure */
|
||||
|
||||
/*
|
||||
* Framer register 0x10 contains xbit error
|
||||
*/
|
||||
#define LMC_FRAMER_REG10_XBIT 0x01 /* X bit error alarm failure */
|
||||
|
||||
/*
|
||||
* And SSI, LMC1000
|
||||
*/
|
||||
#define LMC_MII16_SSI_DTR 0x0001 /* DTR output RW */
|
||||
#define LMC_MII16_SSI_DSR 0x0002 /* DSR input RO */
|
||||
#define LMC_MII16_SSI_RTS 0x0004 /* RTS output RW */
|
||||
#define LMC_MII16_SSI_CTS 0x0008 /* CTS input RO */
|
||||
#define LMC_MII16_SSI_DCD 0x0010 /* DCD input RO */
|
||||
#define LMC_MII16_SSI_RI 0x0020 /* RI input RO */
|
||||
#define LMC_MII16_SSI_CRC 0x1000 /* CRC select - RW */
|
||||
|
||||
/*
|
||||
* bits 0x0080 through 0x0800 are generic, and described
|
||||
* above with LMC_MII16_LED[0123] _LED_ALL, and _FIFO_RESET
|
||||
*/
|
||||
#define LMC_MII16_SSI_LL 0x1000 /* LL output RW */
|
||||
#define LMC_MII16_SSI_RL 0x2000 /* RL output RW */
|
||||
#define LMC_MII16_SSI_TM 0x4000 /* TM input RO */
|
||||
#define LMC_MII16_SSI_LOOP 0x8000 /* loopback enable RW */
|
||||
|
||||
/*
|
||||
* Some of the MII16 bits are mirrored in the MII17 register as well,
|
||||
* but let's keep thing separate for now, and get only the cable from
|
||||
* the MII17.
|
||||
*/
|
||||
#define LMC_MII17_SSI_CABLE_MASK 0x0038 /* mask to extract the cable type */
|
||||
#define LMC_MII17_SSI_CABLE_SHIFT 3 /* shift to extract the cable type */
|
||||
|
||||
/*
|
||||
* And T1, LMC1200
|
||||
*/
|
||||
#define LMC_MII16_T1_UNUSED1 0x0003
|
||||
#define LMC_MII16_T1_XOE 0x0004
|
||||
#define LMC_MII16_T1_RST 0x0008 /* T1 chip reset - RW */
|
||||
#define LMC_MII16_T1_Z 0x0010 /* output impedance T1=1, E1=0 output - RW */
|
||||
#define LMC_MII16_T1_INTR 0x0020 /* interrupt from 8370 - RO */
|
||||
#define LMC_MII16_T1_ONESEC 0x0040 /* one second square wave - ro */
|
||||
|
||||
#define LMC_MII16_T1_LED0 0x0100
|
||||
#define LMC_MII16_T1_LED1 0x0080
|
||||
#define LMC_MII16_T1_LED2 0x0400
|
||||
#define LMC_MII16_T1_LED3 0x0200
|
||||
#define LMC_MII16_T1_FIFO_RESET 0x0800
|
||||
|
||||
#define LMC_MII16_T1_CRC 0x1000 /* CRC select - RW */
|
||||
#define LMC_MII16_T1_UNUSED2 0xe000
|
||||
|
||||
|
||||
/* 8370 framer registers */
|
||||
|
||||
#define T1FRAMER_ALARM1_STATUS 0x47
|
||||
#define T1FRAMER_ALARM2_STATUS 0x48
|
||||
#define T1FRAMER_FERR_LSB 0x50
|
||||
#define T1FRAMER_FERR_MSB 0x51 /* framing bit error counter */
|
||||
#define T1FRAMER_LCV_LSB 0x54
|
||||
#define T1FRAMER_LCV_MSB 0x55 /* line code violation counter */
|
||||
#define T1FRAMER_AERR 0x5A
|
||||
|
||||
/* mask for the above AERR register */
|
||||
#define T1FRAMER_LOF_MASK (0x0f0) /* receive loss of frame */
|
||||
#define T1FRAMER_COFA_MASK (0x0c0) /* change of frame alignment */
|
||||
#define T1FRAMER_SEF_MASK (0x03) /* severely errored frame */
|
||||
|
||||
/* 8370 framer register ALM1 (0x47) values
|
||||
* used to determine link status
|
||||
*/
|
||||
|
||||
#define T1F_SIGFRZ 0x01 /* signaling freeze */
|
||||
#define T1F_RLOF 0x02 /* receive loss of frame alignment */
|
||||
#define T1F_RLOS 0x04 /* receive loss of signal */
|
||||
#define T1F_RALOS 0x08 /* receive analog loss of signal or RCKI loss of clock */
|
||||
#define T1F_RAIS 0x10 /* receive alarm indication signal */
|
||||
#define T1F_UNUSED 0x20
|
||||
#define T1F_RYEL 0x40 /* receive yellow alarm */
|
||||
#define T1F_RMYEL 0x80 /* receive multiframe yellow alarm */
|
||||
|
||||
#define LMC_T1F_WRITE 0
|
||||
#define LMC_T1F_READ 1
|
||||
|
||||
typedef struct lmc_st1f_control {
|
||||
int command;
|
||||
int address;
|
||||
int value;
|
||||
char __user *data;
|
||||
} lmc_t1f_control;
|
||||
|
||||
enum lmc_xilinx_c {
|
||||
lmc_xilinx_reset = 1,
|
||||
lmc_xilinx_load_prom = 2,
|
||||
lmc_xilinx_load = 3
|
||||
};
|
||||
|
||||
struct lmc_xilinx_control {
|
||||
enum lmc_xilinx_c command;
|
||||
int len;
|
||||
char __user *data;
|
||||
};
|
||||
|
||||
/* ------------------ end T1 defs ------------------- */
|
||||
|
||||
#define LMC_MII_LedMask 0x0780
|
||||
#define LMC_MII_LedBitPos 7
|
||||
|
||||
#endif
|
||||
2197
drivers/net/wan/lmc/lmc_main.c
Normal file
2197
drivers/net/wan/lmc/lmc_main.c
Normal file
File diff suppressed because it is too large
Load Diff
1237
drivers/net/wan/lmc/lmc_media.c
Normal file
1237
drivers/net/wan/lmc/lmc_media.c
Normal file
File diff suppressed because it is too large
Load Diff
249
drivers/net/wan/lmc/lmc_proto.c
Normal file
249
drivers/net/wan/lmc/lmc_proto.c
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright (c) 1997-2000 LAN Media Corporation (LMC)
|
||||
* All rights reserved. www.lanmedia.com
|
||||
*
|
||||
* This code is written by:
|
||||
* Andrew Stanley-Jones (asj@cban.com)
|
||||
* Rob Braun (bbraun@vix.com),
|
||||
* Michael Graff (explorer@vix.com) and
|
||||
* Matt Thomas (matt@3am-software.com).
|
||||
*
|
||||
* With Help By:
|
||||
* David Boggs
|
||||
* Ron Crane
|
||||
* Allan Cox
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License version 2, incorporated herein by reference.
|
||||
*
|
||||
* Driver for the LanMedia LMC5200, LMC5245, LMC1000, LMC1200 cards.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <net/syncppp.h>
|
||||
|
||||
#include <asm/processor.h> /* Processor type for cache alignment. */
|
||||
#include <asm/io.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
#include "lmc.h"
|
||||
#include "lmc_var.h"
|
||||
#include "lmc_debug.h"
|
||||
#include "lmc_ioctl.h"
|
||||
#include "lmc_proto.h"
|
||||
|
||||
/*
|
||||
* The compile-time variable SPPPSTUP causes the module to be
|
||||
* compiled without referencing any of the sync ppp routines.
|
||||
*/
|
||||
#ifdef SPPPSTUB
|
||||
#define SPPP_detach(d) (void)0
|
||||
#define SPPP_open(d) 0
|
||||
#define SPPP_reopen(d) (void)0
|
||||
#define SPPP_close(d) (void)0
|
||||
#define SPPP_attach(d) (void)0
|
||||
#define SPPP_do_ioctl(d,i,c) -EOPNOTSUPP
|
||||
#else
|
||||
#define SPPP_attach(x) sppp_attach((x)->pd)
|
||||
#define SPPP_detach(x) sppp_detach((x)->pd->dev)
|
||||
#define SPPP_open(x) sppp_open((x)->pd->dev)
|
||||
#define SPPP_reopen(x) sppp_reopen((x)->pd->dev)
|
||||
#define SPPP_close(x) sppp_close((x)->pd->dev)
|
||||
#define SPPP_do_ioctl(x, y, z) sppp_do_ioctl((x)->pd->dev, (y), (z))
|
||||
#endif
|
||||
|
||||
// init
|
||||
void lmc_proto_init(lmc_softc_t *sc) /*FOLD00*/
|
||||
{
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_init in");
|
||||
switch(sc->if_type){
|
||||
case LMC_PPP:
|
||||
sc->pd = kmalloc(sizeof(struct ppp_device), GFP_KERNEL);
|
||||
if (!sc->pd) {
|
||||
printk("lmc_proto_init(): kmalloc failure!\n");
|
||||
return;
|
||||
}
|
||||
sc->pd->dev = sc->lmc_device;
|
||||
sc->if_ptr = sc->pd;
|
||||
break;
|
||||
case LMC_RAW:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_init out");
|
||||
}
|
||||
|
||||
// attach
|
||||
void lmc_proto_attach(lmc_softc_t *sc) /*FOLD00*/
|
||||
{
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_attach in");
|
||||
switch(sc->if_type){
|
||||
case LMC_PPP:
|
||||
{
|
||||
struct net_device *dev = sc->lmc_device;
|
||||
SPPP_attach(sc);
|
||||
dev->do_ioctl = lmc_ioctl;
|
||||
}
|
||||
break;
|
||||
case LMC_NET:
|
||||
{
|
||||
struct net_device *dev = sc->lmc_device;
|
||||
/*
|
||||
* They set a few basics because they don't use sync_ppp
|
||||
*/
|
||||
dev->flags |= IFF_POINTOPOINT;
|
||||
dev->hard_header = NULL;
|
||||
dev->hard_header_len = 0;
|
||||
dev->addr_len = 0;
|
||||
}
|
||||
case LMC_RAW: /* Setup the task queue, maybe we should notify someone? */
|
||||
{
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_attach out");
|
||||
}
|
||||
|
||||
// detach
|
||||
void lmc_proto_detach(lmc_softc_t *sc) /*FOLD00*/
|
||||
{
|
||||
switch(sc->if_type){
|
||||
case LMC_PPP:
|
||||
SPPP_detach(sc);
|
||||
break;
|
||||
case LMC_RAW: /* Tell someone we're detaching? */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// reopen
|
||||
void lmc_proto_reopen(lmc_softc_t *sc) /*FOLD00*/
|
||||
{
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_reopen in");
|
||||
switch(sc->if_type){
|
||||
case LMC_PPP:
|
||||
SPPP_reopen(sc);
|
||||
break;
|
||||
case LMC_RAW: /* Reset the interface after being down, prerape to receive packets again */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_reopen out");
|
||||
}
|
||||
|
||||
|
||||
// ioctl
|
||||
int lmc_proto_ioctl(lmc_softc_t *sc, struct ifreq *ifr, int cmd) /*FOLD00*/
|
||||
{
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_ioctl out");
|
||||
switch(sc->if_type){
|
||||
case LMC_PPP:
|
||||
return SPPP_do_ioctl (sc, ifr, cmd);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_ioctl out");
|
||||
}
|
||||
|
||||
// open
|
||||
void lmc_proto_open(lmc_softc_t *sc) /*FOLD00*/
|
||||
{
|
||||
int ret;
|
||||
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_open in");
|
||||
switch(sc->if_type){
|
||||
case LMC_PPP:
|
||||
ret = SPPP_open(sc);
|
||||
if(ret < 0)
|
||||
printk("%s: syncPPP open failed: %d\n", sc->name, ret);
|
||||
break;
|
||||
case LMC_RAW: /* We're about to start getting packets! */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_open out");
|
||||
}
|
||||
|
||||
// close
|
||||
|
||||
void lmc_proto_close(lmc_softc_t *sc) /*FOLD00*/
|
||||
{
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_close in");
|
||||
switch(sc->if_type){
|
||||
case LMC_PPP:
|
||||
SPPP_close(sc);
|
||||
break;
|
||||
case LMC_RAW: /* Interface going down */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_close out");
|
||||
}
|
||||
|
||||
unsigned short lmc_proto_type(lmc_softc_t *sc, struct sk_buff *skb) /*FOLD00*/
|
||||
{
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_type in");
|
||||
switch(sc->if_type){
|
||||
case LMC_PPP:
|
||||
return htons(ETH_P_WAN_PPP);
|
||||
break;
|
||||
case LMC_NET:
|
||||
return htons(ETH_P_802_2);
|
||||
break;
|
||||
case LMC_RAW: /* Packet type for skbuff kind of useless */
|
||||
return htons(ETH_P_802_2);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "%s: No protocol set for this interface, assuming 802.2 (which is wrong!!)\n", sc->name);
|
||||
return htons(ETH_P_802_2);
|
||||
break;
|
||||
}
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_tye out");
|
||||
|
||||
}
|
||||
|
||||
void lmc_proto_netif(lmc_softc_t *sc, struct sk_buff *skb) /*FOLD00*/
|
||||
{
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_netif in");
|
||||
switch(sc->if_type){
|
||||
case LMC_PPP:
|
||||
case LMC_NET:
|
||||
default:
|
||||
skb->dev->last_rx = jiffies;
|
||||
netif_rx(skb);
|
||||
break;
|
||||
case LMC_RAW:
|
||||
break;
|
||||
}
|
||||
lmc_trace(sc->lmc_device, "lmc_proto_netif out");
|
||||
}
|
||||
|
||||
16
drivers/net/wan/lmc/lmc_proto.h
Normal file
16
drivers/net/wan/lmc/lmc_proto.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef _LMC_PROTO_H_
|
||||
#define _LMC_PROTO_H_
|
||||
|
||||
void lmc_proto_init(lmc_softc_t *sc);
|
||||
void lmc_proto_attach(lmc_softc_t *sc);
|
||||
void lmc_proto_detach(lmc_softc_t *sc);
|
||||
void lmc_proto_reopen(lmc_softc_t *sc);
|
||||
int lmc_proto_ioctl(lmc_softc_t *sc, struct ifreq *ifr, int cmd);
|
||||
void lmc_proto_open(lmc_softc_t *sc);
|
||||
void lmc_proto_close(lmc_softc_t *sc);
|
||||
unsigned short lmc_proto_type(lmc_softc_t *sc, struct sk_buff *skb);
|
||||
void lmc_proto_netif(lmc_softc_t *sc, struct sk_buff *skb);
|
||||
int lmc_skb_rawpackets(char *buf, char **start, off_t offset, int len, int unused);
|
||||
|
||||
#endif
|
||||
|
||||
570
drivers/net/wan/lmc/lmc_var.h
Normal file
570
drivers/net/wan/lmc/lmc_var.h
Normal file
@@ -0,0 +1,570 @@
|
||||
#ifndef _LMC_VAR_H_
|
||||
#define _LMC_VAR_H_
|
||||
|
||||
/* $Id: lmc_var.h,v 1.1.1.1 2007/06/12 07:27:11 eyryu Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1997-2000 LAN Media Corporation (LMC)
|
||||
* All rights reserved. www.lanmedia.com
|
||||
*
|
||||
* This code is written by:
|
||||
* Andrew Stanley-Jones (asj@cban.com)
|
||||
* Rob Braun (bbraun@vix.com),
|
||||
* Michael Graff (explorer@vix.com) and
|
||||
* Matt Thomas (matt@3am-software.com).
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License version 2, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/timer.h>
|
||||
|
||||
#ifndef __KERNEL__
|
||||
typedef signed char s8;
|
||||
typedef unsigned char u8;
|
||||
|
||||
typedef signed short s16;
|
||||
typedef unsigned short u16;
|
||||
|
||||
typedef signed int s32;
|
||||
typedef unsigned int u32;
|
||||
|
||||
typedef signed long long s64;
|
||||
typedef unsigned long long u64;
|
||||
|
||||
#define BITS_PER_LONG 32
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* basic definitions used in lmc include files
|
||||
*/
|
||||
|
||||
typedef struct lmc___softc lmc_softc_t;
|
||||
typedef struct lmc___media lmc_media_t;
|
||||
typedef struct lmc___ctl lmc_ctl_t;
|
||||
|
||||
#define lmc_csrptr_t unsigned long
|
||||
#define u_int16_t u16
|
||||
#define u_int8_t u8
|
||||
#define tulip_uint32_t u32
|
||||
|
||||
#define LMC_REG_RANGE 0x80
|
||||
|
||||
#define LMC_PRINTF_FMT "%s"
|
||||
#define LMC_PRINTF_ARGS (sc->lmc_device->name)
|
||||
|
||||
#define TX_TIMEOUT (2*HZ)
|
||||
|
||||
#define LMC_TXDESCS 32
|
||||
#define LMC_RXDESCS 32
|
||||
|
||||
#define LMC_LINK_UP 1
|
||||
#define LMC_LINK_DOWN 0
|
||||
|
||||
/* These macros for generic read and write to and from the dec chip */
|
||||
#define LMC_CSR_READ(sc, csr) \
|
||||
inl((sc)->lmc_csrs.csr)
|
||||
#define LMC_CSR_WRITE(sc, reg, val) \
|
||||
outl((val), (sc)->lmc_csrs.reg)
|
||||
|
||||
//#ifdef _LINUX_DELAY_H
|
||||
// #define SLOW_DOWN_IO udelay(2);
|
||||
// #undef __SLOW_DOWN_IO
|
||||
// #define __SLOW_DOWN_IO udelay(2);
|
||||
//#endif
|
||||
|
||||
#define DELAY(n) SLOW_DOWN_IO
|
||||
|
||||
#define lmc_delay() inl(sc->lmc_csrs.csr_9)
|
||||
|
||||
/* This macro sync's up with the mii so that reads and writes can take place */
|
||||
#define LMC_MII_SYNC(sc) do {int n=32; while( n >= 0 ) { \
|
||||
LMC_CSR_WRITE((sc), csr_9, 0x20000); \
|
||||
lmc_delay(); \
|
||||
LMC_CSR_WRITE((sc), csr_9, 0x30000); \
|
||||
lmc_delay(); \
|
||||
n--; }} while(0)
|
||||
|
||||
struct lmc_regfile_t {
|
||||
lmc_csrptr_t csr_busmode; /* CSR0 */
|
||||
lmc_csrptr_t csr_txpoll; /* CSR1 */
|
||||
lmc_csrptr_t csr_rxpoll; /* CSR2 */
|
||||
lmc_csrptr_t csr_rxlist; /* CSR3 */
|
||||
lmc_csrptr_t csr_txlist; /* CSR4 */
|
||||
lmc_csrptr_t csr_status; /* CSR5 */
|
||||
lmc_csrptr_t csr_command; /* CSR6 */
|
||||
lmc_csrptr_t csr_intr; /* CSR7 */
|
||||
lmc_csrptr_t csr_missed_frames; /* CSR8 */
|
||||
lmc_csrptr_t csr_9; /* CSR9 */
|
||||
lmc_csrptr_t csr_10; /* CSR10 */
|
||||
lmc_csrptr_t csr_11; /* CSR11 */
|
||||
lmc_csrptr_t csr_12; /* CSR12 */
|
||||
lmc_csrptr_t csr_13; /* CSR13 */
|
||||
lmc_csrptr_t csr_14; /* CSR14 */
|
||||
lmc_csrptr_t csr_15; /* CSR15 */
|
||||
};
|
||||
|
||||
#define csr_enetrom csr_9 /* 21040 */
|
||||
#define csr_reserved csr_10 /* 21040 */
|
||||
#define csr_full_duplex csr_11 /* 21040 */
|
||||
#define csr_bootrom csr_10 /* 21041/21140A/?? */
|
||||
#define csr_gp csr_12 /* 21140* */
|
||||
#define csr_watchdog csr_15 /* 21140* */
|
||||
#define csr_gp_timer csr_11 /* 21041/21140* */
|
||||
#define csr_srom_mii csr_9 /* 21041/21140* */
|
||||
#define csr_sia_status csr_12 /* 2104x */
|
||||
#define csr_sia_connectivity csr_13 /* 2104x */
|
||||
#define csr_sia_tx_rx csr_14 /* 2104x */
|
||||
#define csr_sia_general csr_15 /* 2104x */
|
||||
|
||||
/* tulip length/control transmit descriptor definitions
|
||||
* used to define bits in the second tulip_desc_t field (length)
|
||||
* for the transmit descriptor -baz */
|
||||
|
||||
#define LMC_TDES_FIRST_BUFFER_SIZE ((u_int32_t)(0x000007FF))
|
||||
#define LMC_TDES_SECOND_BUFFER_SIZE ((u_int32_t)(0x003FF800))
|
||||
#define LMC_TDES_HASH_FILTERING ((u_int32_t)(0x00400000))
|
||||
#define LMC_TDES_DISABLE_PADDING ((u_int32_t)(0x00800000))
|
||||
#define LMC_TDES_SECOND_ADDR_CHAINED ((u_int32_t)(0x01000000))
|
||||
#define LMC_TDES_END_OF_RING ((u_int32_t)(0x02000000))
|
||||
#define LMC_TDES_ADD_CRC_DISABLE ((u_int32_t)(0x04000000))
|
||||
#define LMC_TDES_SETUP_PACKET ((u_int32_t)(0x08000000))
|
||||
#define LMC_TDES_INVERSE_FILTERING ((u_int32_t)(0x10000000))
|
||||
#define LMC_TDES_FIRST_SEGMENT ((u_int32_t)(0x20000000))
|
||||
#define LMC_TDES_LAST_SEGMENT ((u_int32_t)(0x40000000))
|
||||
#define LMC_TDES_INTERRUPT_ON_COMPLETION ((u_int32_t)(0x80000000))
|
||||
|
||||
#define TDES_SECOND_BUFFER_SIZE_BIT_NUMBER 11
|
||||
#define TDES_COLLISION_COUNT_BIT_NUMBER 3
|
||||
|
||||
/* Constants for the RCV descriptor RDES */
|
||||
|
||||
#define LMC_RDES_OVERFLOW ((u_int32_t)(0x00000001))
|
||||
#define LMC_RDES_CRC_ERROR ((u_int32_t)(0x00000002))
|
||||
#define LMC_RDES_DRIBBLING_BIT ((u_int32_t)(0x00000004))
|
||||
#define LMC_RDES_REPORT_ON_MII_ERR ((u_int32_t)(0x00000008))
|
||||
#define LMC_RDES_RCV_WATCHDOG_TIMEOUT ((u_int32_t)(0x00000010))
|
||||
#define LMC_RDES_FRAME_TYPE ((u_int32_t)(0x00000020))
|
||||
#define LMC_RDES_COLLISION_SEEN ((u_int32_t)(0x00000040))
|
||||
#define LMC_RDES_FRAME_TOO_LONG ((u_int32_t)(0x00000080))
|
||||
#define LMC_RDES_LAST_DESCRIPTOR ((u_int32_t)(0x00000100))
|
||||
#define LMC_RDES_FIRST_DESCRIPTOR ((u_int32_t)(0x00000200))
|
||||
#define LMC_RDES_MULTICAST_FRAME ((u_int32_t)(0x00000400))
|
||||
#define LMC_RDES_RUNT_FRAME ((u_int32_t)(0x00000800))
|
||||
#define LMC_RDES_DATA_TYPE ((u_int32_t)(0x00003000))
|
||||
#define LMC_RDES_LENGTH_ERROR ((u_int32_t)(0x00004000))
|
||||
#define LMC_RDES_ERROR_SUMMARY ((u_int32_t)(0x00008000))
|
||||
#define LMC_RDES_FRAME_LENGTH ((u_int32_t)(0x3FFF0000))
|
||||
#define LMC_RDES_OWN_BIT ((u_int32_t)(0x80000000))
|
||||
|
||||
#define RDES_FRAME_LENGTH_BIT_NUMBER 16
|
||||
|
||||
#define LMC_RDES_ERROR_MASK ( (u_int32_t)( \
|
||||
LMC_RDES_OVERFLOW \
|
||||
| LMC_RDES_DRIBBLING_BIT \
|
||||
| LMC_RDES_REPORT_ON_MII_ERR \
|
||||
| LMC_RDES_COLLISION_SEEN ) )
|
||||
|
||||
|
||||
/*
|
||||
* Ioctl info
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
u_int32_t n;
|
||||
u_int32_t m;
|
||||
u_int32_t v;
|
||||
u_int32_t x;
|
||||
u_int32_t r;
|
||||
u_int32_t f;
|
||||
u_int32_t exact;
|
||||
} lmc_av9110_t;
|
||||
|
||||
/*
|
||||
* Common structure passed to the ioctl code.
|
||||
*/
|
||||
struct lmc___ctl {
|
||||
u_int32_t cardtype;
|
||||
u_int32_t clock_source; /* HSSI, T1 */
|
||||
u_int32_t clock_rate; /* T1 */
|
||||
u_int32_t crc_length;
|
||||
u_int32_t cable_length; /* DS3 */
|
||||
u_int32_t scrambler_onoff; /* DS3 */
|
||||
u_int32_t cable_type; /* T1 */
|
||||
u_int32_t keepalive_onoff; /* protocol */
|
||||
u_int32_t ticks; /* ticks/sec */
|
||||
union {
|
||||
lmc_av9110_t ssi;
|
||||
} cardspec;
|
||||
u_int32_t circuit_type; /* T1 or E1 */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Carefull, look at the data sheet, there's more to this
|
||||
* structure than meets the eye. It should probably be:
|
||||
*
|
||||
* struct tulip_desc_t {
|
||||
* u8 own:1;
|
||||
* u32 status:31;
|
||||
* u32 control:10;
|
||||
* u32 buffer1;
|
||||
* u32 buffer2;
|
||||
* };
|
||||
* You could also expand status control to provide more bit information
|
||||
*/
|
||||
|
||||
struct tulip_desc_t {
|
||||
s32 status;
|
||||
s32 length;
|
||||
u32 buffer1;
|
||||
u32 buffer2;
|
||||
};
|
||||
|
||||
/*
|
||||
* media independent methods to check on media status, link, light LEDs,
|
||||
* etc.
|
||||
*/
|
||||
struct lmc___media {
|
||||
void (* init)(lmc_softc_t * const);
|
||||
void (* defaults)(lmc_softc_t * const);
|
||||
void (* set_status)(lmc_softc_t * const, lmc_ctl_t *);
|
||||
void (* set_clock_source)(lmc_softc_t * const, int);
|
||||
void (* set_speed)(lmc_softc_t * const, lmc_ctl_t *);
|
||||
void (* set_cable_length)(lmc_softc_t * const, int);
|
||||
void (* set_scrambler)(lmc_softc_t * const, int);
|
||||
int (* get_link_status)(lmc_softc_t * const);
|
||||
void (* set_link_status)(lmc_softc_t * const, int);
|
||||
void (* set_crc_length)(lmc_softc_t * const, int);
|
||||
void (* set_circuit_type)(lmc_softc_t * const, int);
|
||||
void (* watchdog)(lmc_softc_t * const);
|
||||
};
|
||||
|
||||
|
||||
#define STATCHECK 0xBEEFCAFE
|
||||
|
||||
/* Included in this structure are first
|
||||
* - standard net_device_stats
|
||||
* - some other counters used for debug and driver performance
|
||||
* evaluation -baz
|
||||
*/
|
||||
struct lmc_statistics
|
||||
{
|
||||
unsigned long rx_packets; /* total packets received */
|
||||
unsigned long tx_packets; /* total packets transmitted */
|
||||
unsigned long rx_bytes;
|
||||
unsigned long tx_bytes;
|
||||
|
||||
unsigned long rx_errors; /* bad packets received */
|
||||
unsigned long tx_errors; /* packet transmit problems */
|
||||
unsigned long rx_dropped; /* no space in linux buffers */
|
||||
unsigned long tx_dropped; /* no space available in linux */
|
||||
unsigned long multicast; /* multicast packets received */
|
||||
unsigned long collisions;
|
||||
|
||||
/* detailed rx_errors: */
|
||||
unsigned long rx_length_errors;
|
||||
unsigned long rx_over_errors; /* receiver ring buff overflow */
|
||||
unsigned long rx_crc_errors; /* recved pkt with crc error */
|
||||
unsigned long rx_frame_errors; /* recv'd frame alignment error */
|
||||
unsigned long rx_fifo_errors; /* recv'r fifo overrun */
|
||||
unsigned long rx_missed_errors; /* receiver missed packet */
|
||||
|
||||
/* detailed tx_errors */
|
||||
unsigned long tx_aborted_errors;
|
||||
unsigned long tx_carrier_errors;
|
||||
unsigned long tx_fifo_errors;
|
||||
unsigned long tx_heartbeat_errors;
|
||||
unsigned long tx_window_errors;
|
||||
|
||||
/* for cslip etc */
|
||||
unsigned long rx_compressed;
|
||||
unsigned long tx_compressed;
|
||||
|
||||
/* -------------------------------------
|
||||
* Custom stats & counters follow -baz */
|
||||
u_int32_t version_size;
|
||||
u_int32_t lmc_cardtype;
|
||||
|
||||
u_int32_t tx_ProcTimeout;
|
||||
u_int32_t tx_IntTimeout;
|
||||
u_int32_t tx_NoCompleteCnt;
|
||||
u_int32_t tx_MaxXmtsB4Int;
|
||||
u_int32_t tx_TimeoutCnt;
|
||||
u_int32_t tx_OutOfSyncPtr;
|
||||
u_int32_t tx_tbusy0;
|
||||
u_int32_t tx_tbusy1;
|
||||
u_int32_t tx_tbusy_calls;
|
||||
u_int32_t resetCount;
|
||||
u_int32_t lmc_txfull;
|
||||
u_int32_t tbusy;
|
||||
u_int32_t dirtyTx;
|
||||
u_int32_t lmc_next_tx;
|
||||
u_int32_t otherTypeCnt;
|
||||
u_int32_t lastType;
|
||||
u_int32_t lastTypeOK;
|
||||
u_int32_t txLoopCnt;
|
||||
u_int32_t usedXmtDescripCnt;
|
||||
u_int32_t txIndexCnt;
|
||||
u_int32_t rxIntLoopCnt;
|
||||
|
||||
u_int32_t rx_SmallPktCnt;
|
||||
u_int32_t rx_BadPktSurgeCnt;
|
||||
u_int32_t rx_BuffAllocErr;
|
||||
u_int32_t tx_lossOfClockCnt;
|
||||
|
||||
/* T1 error counters */
|
||||
u_int32_t framingBitErrorCount;
|
||||
u_int32_t lineCodeViolationCount;
|
||||
|
||||
u_int32_t lossOfFrameCount;
|
||||
u_int32_t changeOfFrameAlignmentCount;
|
||||
u_int32_t severelyErroredFrameCount;
|
||||
|
||||
u_int32_t check;
|
||||
};
|
||||
|
||||
|
||||
typedef struct lmc_xinfo {
|
||||
u_int32_t Magic0; /* BEEFCAFE */
|
||||
|
||||
u_int32_t PciCardType;
|
||||
u_int32_t PciSlotNumber; /* PCI slot number */
|
||||
|
||||
u_int16_t DriverMajorVersion;
|
||||
u_int16_t DriverMinorVersion;
|
||||
u_int16_t DriverSubVersion;
|
||||
|
||||
u_int16_t XilinxRevisionNumber;
|
||||
u_int16_t MaxFrameSize;
|
||||
|
||||
u_int16_t t1_alarm1_status;
|
||||
u_int16_t t1_alarm2_status;
|
||||
|
||||
int link_status;
|
||||
u_int32_t mii_reg16;
|
||||
|
||||
u_int32_t Magic1; /* DEADBEEF */
|
||||
} LMC_XINFO;
|
||||
|
||||
|
||||
/*
|
||||
* forward decl
|
||||
*/
|
||||
struct lmc___softc {
|
||||
void *if_ptr; /* General purpose pointer (used by SPPP) */
|
||||
char *name;
|
||||
u8 board_idx;
|
||||
struct lmc_statistics stats;
|
||||
struct net_device *lmc_device;
|
||||
|
||||
int hang, rxdesc, bad_packet, some_counter;
|
||||
u_int32_t txgo;
|
||||
struct lmc_regfile_t lmc_csrs;
|
||||
volatile u_int32_t lmc_txtick;
|
||||
volatile u_int32_t lmc_rxtick;
|
||||
u_int32_t lmc_flags;
|
||||
u_int32_t lmc_intrmask; /* our copy of csr_intr */
|
||||
u_int32_t lmc_cmdmode; /* our copy of csr_cmdmode */
|
||||
u_int32_t lmc_busmode; /* our copy of csr_busmode */
|
||||
u_int32_t lmc_gpio_io; /* state of in/out settings */
|
||||
u_int32_t lmc_gpio; /* state of outputs */
|
||||
struct sk_buff* lmc_txq[LMC_TXDESCS];
|
||||
struct sk_buff* lmc_rxq[LMC_RXDESCS];
|
||||
volatile
|
||||
struct tulip_desc_t lmc_rxring[LMC_RXDESCS];
|
||||
volatile
|
||||
struct tulip_desc_t lmc_txring[LMC_TXDESCS];
|
||||
unsigned int lmc_next_rx, lmc_next_tx;
|
||||
volatile
|
||||
unsigned int lmc_taint_tx, lmc_taint_rx;
|
||||
int lmc_tx_start, lmc_txfull;
|
||||
int lmc_txbusy;
|
||||
u_int16_t lmc_miireg16;
|
||||
int lmc_ok;
|
||||
int last_link_status;
|
||||
int lmc_cardtype;
|
||||
u_int32_t last_frameerr;
|
||||
lmc_media_t *lmc_media;
|
||||
struct timer_list timer;
|
||||
lmc_ctl_t ictl;
|
||||
u_int32_t TxDescriptControlInit;
|
||||
|
||||
int tx_TimeoutInd; /* additional driver state */
|
||||
int tx_TimeoutDisplay;
|
||||
unsigned int lastlmc_taint_tx;
|
||||
int lasttx_packets;
|
||||
u_int32_t tx_clockState;
|
||||
u_int32_t lmc_crcSize;
|
||||
LMC_XINFO lmc_xinfo;
|
||||
char lmc_yel, lmc_blue, lmc_red; /* for T1 and DS3 */
|
||||
char lmc_timing; /* for HSSI and SSI */
|
||||
int got_irq;
|
||||
|
||||
char last_led_err[4];
|
||||
|
||||
u32 last_int;
|
||||
u32 num_int;
|
||||
|
||||
spinlock_t lmc_lock;
|
||||
u_int16_t if_type; /* PPP or NET */
|
||||
struct ppp_device *pd;
|
||||
|
||||
/* Failure cases */
|
||||
u8 failed_ring;
|
||||
u8 failed_recv_alloc;
|
||||
|
||||
/* Structure check */
|
||||
u32 check;
|
||||
};
|
||||
|
||||
#define LMC_PCI_TIME 1
|
||||
#define LMC_EXT_TIME 0
|
||||
|
||||
#define PKT_BUF_SZ 1542 /* was 1536 */
|
||||
|
||||
/* CSR5 settings */
|
||||
#define TIMER_INT 0x00000800
|
||||
#define TP_LINK_FAIL 0x00001000
|
||||
#define TP_LINK_PASS 0x00000010
|
||||
#define NORMAL_INT 0x00010000
|
||||
#define ABNORMAL_INT 0x00008000
|
||||
#define RX_JABBER_INT 0x00000200
|
||||
#define RX_DIED 0x00000100
|
||||
#define RX_NOBUFF 0x00000080
|
||||
#define RX_INT 0x00000040
|
||||
#define TX_FIFO_UNDER 0x00000020
|
||||
#define TX_JABBER 0x00000008
|
||||
#define TX_NOBUFF 0x00000004
|
||||
#define TX_DIED 0x00000002
|
||||
#define TX_INT 0x00000001
|
||||
|
||||
/* CSR6 settings */
|
||||
#define OPERATION_MODE 0x00000200 /* Full Duplex */
|
||||
#define PROMISC_MODE 0x00000040 /* Promiscuous Mode */
|
||||
#define RECIEVE_ALL 0x40000000 /* Recieve All */
|
||||
#define PASS_BAD_FRAMES 0x00000008 /* Pass Bad Frames */
|
||||
|
||||
/* Dec control registers CSR6 as well */
|
||||
#define LMC_DEC_ST 0x00002000
|
||||
#define LMC_DEC_SR 0x00000002
|
||||
|
||||
/* CSR15 settings */
|
||||
#define RECV_WATCHDOG_DISABLE 0x00000010
|
||||
#define JABBER_DISABLE 0x00000001
|
||||
|
||||
/* More settings */
|
||||
/*
|
||||
* aSR6 -- Command (Operation Mode) Register
|
||||
*/
|
||||
#define TULIP_CMD_RECEIVEALL 0x40000000L /* (RW) Receivel all frames? */
|
||||
#define TULIP_CMD_MUSTBEONE 0x02000000L /* (RW) Must Be One (21140) */
|
||||
#define TULIP_CMD_TXTHRSHLDCTL 0x00400000L /* (RW) Transmit Threshold Mode (21140) */
|
||||
#define TULIP_CMD_STOREFWD 0x00200000L /* (RW) Store and Foward (21140) */
|
||||
#define TULIP_CMD_NOHEARTBEAT 0x00080000L /* (RW) No Heartbeat (21140) */
|
||||
#define TULIP_CMD_PORTSELECT 0x00040000L /* (RW) Post Select (100Mb) (21140) */
|
||||
#define TULIP_CMD_FULLDUPLEX 0x00000200L /* (RW) Full Duplex Mode */
|
||||
#define TULIP_CMD_OPERMODE 0x00000C00L /* (RW) Operating Mode */
|
||||
#define TULIP_CMD_PROMISCUOUS 0x00000041L /* (RW) Promiscuous Mode */
|
||||
#define TULIP_CMD_PASSBADPKT 0x00000008L /* (RW) Pass Bad Frames */
|
||||
#define TULIP_CMD_THRESHOLDCTL 0x0000C000L /* (RW) Threshold Control */
|
||||
|
||||
#define TULIP_GP_PINSET 0x00000100L
|
||||
#define TULIP_BUSMODE_SWRESET 0x00000001L
|
||||
#define TULIP_WATCHDOG_TXDISABLE 0x00000001L
|
||||
#define TULIP_WATCHDOG_RXDISABLE 0x00000010L
|
||||
|
||||
#define TULIP_STS_NORMALINTR 0x00010000L /* (RW) Normal Interrupt */
|
||||
#define TULIP_STS_ABNRMLINTR 0x00008000L /* (RW) Abnormal Interrupt */
|
||||
#define TULIP_STS_ERI 0x00004000L /* (RW) Early Receive Interrupt */
|
||||
#define TULIP_STS_SYSERROR 0x00002000L /* (RW) System Error */
|
||||
#define TULIP_STS_GTE 0x00000800L /* (RW) General Pupose Timer Exp */
|
||||
#define TULIP_STS_ETI 0x00000400L /* (RW) Early Transmit Interrupt */
|
||||
#define TULIP_STS_RXWT 0x00000200L /* (RW) Receiver Watchdog Timeout */
|
||||
#define TULIP_STS_RXSTOPPED 0x00000100L /* (RW) Receiver Process Stopped */
|
||||
#define TULIP_STS_RXNOBUF 0x00000080L /* (RW) Receive Buf Unavail */
|
||||
#define TULIP_STS_RXINTR 0x00000040L /* (RW) Receive Interrupt */
|
||||
#define TULIP_STS_TXUNDERFLOW 0x00000020L /* (RW) Transmit Underflow */
|
||||
#define TULIP_STS_TXJABER 0x00000008L /* (RW) Jabber timeout */
|
||||
#define TULIP_STS_TXNOBUF 0x00000004L
|
||||
#define TULIP_STS_TXSTOPPED 0x00000002L /* (RW) Transmit Process Stopped */
|
||||
#define TULIP_STS_TXINTR 0x00000001L /* (RW) Transmit Interrupt */
|
||||
|
||||
#define TULIP_STS_RXS_STOPPED 0x00000000L /* 000 - Stopped */
|
||||
|
||||
#define TULIP_STS_RXSTOPPED 0x00000100L /* (RW) Receive Process Stopped */
|
||||
#define TULIP_STS_RXNOBUF 0x00000080L
|
||||
|
||||
#define TULIP_CMD_TXRUN 0x00002000L /* (RW) Start/Stop Transmitter */
|
||||
#define TULIP_CMD_RXRUN 0x00000002L /* (RW) Start/Stop Receive Filtering */
|
||||
#define TULIP_DSTS_TxDEFERRED 0x00000001 /* Initially Deferred */
|
||||
#define TULIP_DSTS_OWNER 0x80000000 /* Owner (1 = 21040) */
|
||||
#define TULIP_DSTS_RxMIIERR 0x00000008
|
||||
#define LMC_DSTS_ERRSUM (TULIP_DSTS_RxMIIERR)
|
||||
|
||||
#define TULIP_DEFAULT_INTR_MASK (TULIP_STS_NORMALINTR \
|
||||
| TULIP_STS_RXINTR \
|
||||
| TULIP_STS_TXINTR \
|
||||
| TULIP_STS_ABNRMLINTR \
|
||||
| TULIP_STS_SYSERROR \
|
||||
| TULIP_STS_TXSTOPPED \
|
||||
| TULIP_STS_TXUNDERFLOW\
|
||||
| TULIP_STS_RXSTOPPED )
|
||||
|
||||
#define DESC_OWNED_BY_SYSTEM ((u_int32_t)(0x00000000))
|
||||
#define DESC_OWNED_BY_DC21X4 ((u_int32_t)(0x80000000))
|
||||
|
||||
#ifndef TULIP_CMD_RECEIVEALL
|
||||
#define TULIP_CMD_RECEIVEALL 0x40000000L
|
||||
#endif
|
||||
|
||||
/* Adapter module number */
|
||||
#define LMC_ADAP_HSSI 2
|
||||
#define LMC_ADAP_DS3 3
|
||||
#define LMC_ADAP_SSI 4
|
||||
#define LMC_ADAP_T1 5
|
||||
|
||||
#define HDLC_HDR_LEN 4
|
||||
#define HDLC_ADDR_LEN 1
|
||||
#define HDLC_SLARP 0x8035
|
||||
#define LMC_MTU 1500
|
||||
#define SLARP_LINECHECK 2
|
||||
|
||||
#define LMC_CRC_LEN_16 2 /* 16-bit CRC */
|
||||
#define LMC_CRC_LEN_32 4
|
||||
|
||||
#ifdef LMC_HDLC
|
||||
/* definition of an hdlc header. */
|
||||
struct hdlc_hdr
|
||||
{
|
||||
u8 address;
|
||||
u8 control;
|
||||
u16 type;
|
||||
};
|
||||
|
||||
/* definition of a slarp header. */
|
||||
struct slarp
|
||||
{
|
||||
long code;
|
||||
union sl
|
||||
{
|
||||
struct
|
||||
{
|
||||
ulong address;
|
||||
ulong mask;
|
||||
ushort unused;
|
||||
} add;
|
||||
struct
|
||||
{
|
||||
ulong mysequence;
|
||||
ulong yoursequence;
|
||||
ushort reliability;
|
||||
ulong time;
|
||||
} chk;
|
||||
} t;
|
||||
};
|
||||
#endif /* LMC_HDLC */
|
||||
|
||||
|
||||
#endif /* _LMC_VAR_H_ */
|
||||
568
drivers/net/wan/n2.c
Normal file
568
drivers/net/wan/n2.c
Normal file
@@ -0,0 +1,568 @@
|
||||
/*
|
||||
* SDL Inc. RISCom/N2 synchronous serial card driver for Linux
|
||||
*
|
||||
* Copyright (C) 1998-2003 Krzysztof Halasa <khc@pm.waw.pl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* For information see <http://www.kernel.org/pub/linux/utils/net/hdlc/>
|
||||
*
|
||||
* Note: integrated CSU/DSU/DDS are not supported by this driver
|
||||
*
|
||||
* Sources of information:
|
||||
* Hitachi HD64570 SCA User's Manual
|
||||
* SDL Inc. PPP/HDLC/CISCO driver
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/hdlc.h>
|
||||
#include <asm/io.h>
|
||||
#include "hd64570.h"
|
||||
|
||||
|
||||
static const char* version = "SDL RISCom/N2 driver version: 1.15";
|
||||
static const char* devname = "RISCom/N2";
|
||||
|
||||
#undef DEBUG_PKT
|
||||
#define DEBUG_RINGS
|
||||
|
||||
#define USE_WINDOWSIZE 16384
|
||||
#define USE_BUS16BITS 1
|
||||
#define CLOCK_BASE 9830400 /* 9.8304 MHz */
|
||||
#define MAX_PAGES 16 /* 16 RAM pages at max */
|
||||
#define MAX_RAM_SIZE 0x80000 /* 512 KB */
|
||||
#if MAX_RAM_SIZE > MAX_PAGES * USE_WINDOWSIZE
|
||||
#undef MAX_RAM_SIZE
|
||||
#define MAX_RAM_SIZE (MAX_PAGES * USE_WINDOWSIZE)
|
||||
#endif
|
||||
#define N2_IOPORTS 0x10
|
||||
#define NEED_DETECT_RAM
|
||||
#define NEED_SCA_MSCI_INTR
|
||||
#define MAX_TX_BUFFERS 10
|
||||
|
||||
static char *hw = NULL; /* pointer to hw=xxx command line string */
|
||||
|
||||
/* RISCom/N2 Board Registers */
|
||||
|
||||
/* PC Control Register */
|
||||
#define N2_PCR 0
|
||||
#define PCR_RUNSCA 1 /* Run 64570 */
|
||||
#define PCR_VPM 2 /* Enable VPM - needed if using RAM above 1 MB */
|
||||
#define PCR_ENWIN 4 /* Open window */
|
||||
#define PCR_BUS16 8 /* 16-bit bus */
|
||||
|
||||
|
||||
/* Memory Base Address Register */
|
||||
#define N2_BAR 2
|
||||
|
||||
|
||||
/* Page Scan Register */
|
||||
#define N2_PSR 4
|
||||
#define WIN16K 0x00
|
||||
#define WIN32K 0x20
|
||||
#define WIN64K 0x40
|
||||
#define PSR_WINBITS 0x60
|
||||
#define PSR_DMAEN 0x80
|
||||
#define PSR_PAGEBITS 0x0F
|
||||
|
||||
|
||||
/* Modem Control Reg */
|
||||
#define N2_MCR 6
|
||||
#define CLOCK_OUT_PORT1 0x80
|
||||
#define CLOCK_OUT_PORT0 0x40
|
||||
#define TX422_PORT1 0x20
|
||||
#define TX422_PORT0 0x10
|
||||
#define DSR_PORT1 0x08
|
||||
#define DSR_PORT0 0x04
|
||||
#define DTR_PORT1 0x02
|
||||
#define DTR_PORT0 0x01
|
||||
|
||||
|
||||
typedef struct port_s {
|
||||
struct net_device *dev;
|
||||
struct card_s *card;
|
||||
spinlock_t lock; /* TX lock */
|
||||
sync_serial_settings settings;
|
||||
int valid; /* port enabled */
|
||||
int rxpart; /* partial frame received, next frame invalid*/
|
||||
unsigned short encoding;
|
||||
unsigned short parity;
|
||||
u16 rxin; /* rx ring buffer 'in' pointer */
|
||||
u16 txin; /* tx ring buffer 'in' and 'last' pointers */
|
||||
u16 txlast;
|
||||
u8 rxs, txs, tmc; /* SCA registers */
|
||||
u8 phy_node; /* physical port # - 0 or 1 */
|
||||
u8 log_node; /* logical port # */
|
||||
}port_t;
|
||||
|
||||
|
||||
|
||||
typedef struct card_s {
|
||||
u8 __iomem *winbase; /* ISA window base address */
|
||||
u32 phy_winbase; /* ISA physical base address */
|
||||
u32 ram_size; /* number of bytes */
|
||||
u16 io; /* IO Base address */
|
||||
u16 buff_offset; /* offset of first buffer of first channel */
|
||||
u16 rx_ring_buffers; /* number of buffers in a ring */
|
||||
u16 tx_ring_buffers;
|
||||
u8 irq; /* IRQ (3-15) */
|
||||
|
||||
port_t ports[2];
|
||||
struct card_s *next_card;
|
||||
}card_t;
|
||||
|
||||
|
||||
static card_t *first_card;
|
||||
static card_t **new_card = &first_card;
|
||||
|
||||
|
||||
#define sca_reg(reg, card) (0x8000 | (card)->io | \
|
||||
((reg) & 0x0F) | (((reg) & 0xF0) << 6))
|
||||
#define sca_in(reg, card) inb(sca_reg(reg, card))
|
||||
#define sca_out(value, reg, card) outb(value, sca_reg(reg, card))
|
||||
#define sca_inw(reg, card) inw(sca_reg(reg, card))
|
||||
#define sca_outw(value, reg, card) outw(value, sca_reg(reg, card))
|
||||
|
||||
#define port_to_card(port) ((port)->card)
|
||||
#define log_node(port) ((port)->log_node)
|
||||
#define phy_node(port) ((port)->phy_node)
|
||||
#define winsize(card) (USE_WINDOWSIZE)
|
||||
#define winbase(card) ((card)->winbase)
|
||||
#define get_port(card, port) ((card)->ports[port].valid ? \
|
||||
&(card)->ports[port] : NULL)
|
||||
|
||||
|
||||
|
||||
static __inline__ u8 sca_get_page(card_t *card)
|
||||
{
|
||||
return inb(card->io + N2_PSR) & PSR_PAGEBITS;
|
||||
}
|
||||
|
||||
|
||||
static __inline__ void openwin(card_t *card, u8 page)
|
||||
{
|
||||
u8 psr = inb(card->io + N2_PSR);
|
||||
outb((psr & ~PSR_PAGEBITS) | page, card->io + N2_PSR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#include "hd6457x.c"
|
||||
|
||||
|
||||
|
||||
static void n2_set_iface(port_t *port)
|
||||
{
|
||||
card_t *card = port->card;
|
||||
int io = card->io;
|
||||
u8 mcr = inb(io + N2_MCR);
|
||||
u8 msci = get_msci(port);
|
||||
u8 rxs = port->rxs & CLK_BRG_MASK;
|
||||
u8 txs = port->txs & CLK_BRG_MASK;
|
||||
|
||||
switch(port->settings.clock_type) {
|
||||
case CLOCK_INT:
|
||||
mcr |= port->phy_node ? CLOCK_OUT_PORT1 : CLOCK_OUT_PORT0;
|
||||
rxs |= CLK_BRG_RX; /* BRG output */
|
||||
txs |= CLK_RXCLK_TX; /* RX clock */
|
||||
break;
|
||||
|
||||
case CLOCK_TXINT:
|
||||
mcr |= port->phy_node ? CLOCK_OUT_PORT1 : CLOCK_OUT_PORT0;
|
||||
rxs |= CLK_LINE_RX; /* RXC input */
|
||||
txs |= CLK_BRG_TX; /* BRG output */
|
||||
break;
|
||||
|
||||
case CLOCK_TXFROMRX:
|
||||
mcr |= port->phy_node ? CLOCK_OUT_PORT1 : CLOCK_OUT_PORT0;
|
||||
rxs |= CLK_LINE_RX; /* RXC input */
|
||||
txs |= CLK_RXCLK_TX; /* RX clock */
|
||||
break;
|
||||
|
||||
default: /* Clock EXTernal */
|
||||
mcr &= port->phy_node ? ~CLOCK_OUT_PORT1 : ~CLOCK_OUT_PORT0;
|
||||
rxs |= CLK_LINE_RX; /* RXC input */
|
||||
txs |= CLK_LINE_TX; /* TXC input */
|
||||
}
|
||||
|
||||
outb(mcr, io + N2_MCR);
|
||||
port->rxs = rxs;
|
||||
port->txs = txs;
|
||||
sca_out(rxs, msci + RXS, card);
|
||||
sca_out(txs, msci + TXS, card);
|
||||
sca_set_port(port);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int n2_open(struct net_device *dev)
|
||||
{
|
||||
port_t *port = dev_to_port(dev);
|
||||
int io = port->card->io;
|
||||
u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1:TX422_PORT0);
|
||||
int result;
|
||||
|
||||
result = hdlc_open(dev);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
mcr &= port->phy_node ? ~DTR_PORT1 : ~DTR_PORT0; /* set DTR ON */
|
||||
outb(mcr, io + N2_MCR);
|
||||
|
||||
outb(inb(io + N2_PCR) | PCR_ENWIN, io + N2_PCR); /* open window */
|
||||
outb(inb(io + N2_PSR) | PSR_DMAEN, io + N2_PSR); /* enable dma */
|
||||
sca_open(dev);
|
||||
n2_set_iface(port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int n2_close(struct net_device *dev)
|
||||
{
|
||||
port_t *port = dev_to_port(dev);
|
||||
int io = port->card->io;
|
||||
u8 mcr = inb(io+N2_MCR) | (port->phy_node ? TX422_PORT1 : TX422_PORT0);
|
||||
|
||||
sca_close(dev);
|
||||
mcr |= port->phy_node ? DTR_PORT1 : DTR_PORT0; /* set DTR OFF */
|
||||
outb(mcr, io + N2_MCR);
|
||||
hdlc_close(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int n2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
const size_t size = sizeof(sync_serial_settings);
|
||||
sync_serial_settings new_line;
|
||||
sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
|
||||
port_t *port = dev_to_port(dev);
|
||||
|
||||
#ifdef DEBUG_RINGS
|
||||
if (cmd == SIOCDEVPRIVATE) {
|
||||
sca_dump_rings(dev);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (cmd != SIOCWANDEV)
|
||||
return hdlc_ioctl(dev, ifr, cmd);
|
||||
|
||||
switch(ifr->ifr_settings.type) {
|
||||
case IF_GET_IFACE:
|
||||
ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
|
||||
if (ifr->ifr_settings.size < size) {
|
||||
ifr->ifr_settings.size = size; /* data size wanted */
|
||||
return -ENOBUFS;
|
||||
}
|
||||
if (copy_to_user(line, &port->settings, size))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case IF_IFACE_SYNC_SERIAL:
|
||||
if(!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&new_line, line, size))
|
||||
return -EFAULT;
|
||||
|
||||
if (new_line.clock_type != CLOCK_EXT &&
|
||||
new_line.clock_type != CLOCK_TXFROMRX &&
|
||||
new_line.clock_type != CLOCK_INT &&
|
||||
new_line.clock_type != CLOCK_TXINT)
|
||||
return -EINVAL; /* No such clock setting */
|
||||
|
||||
if (new_line.loopback != 0 && new_line.loopback != 1)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&port->settings, &new_line, size); /* Update settings */
|
||||
n2_set_iface(port);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return hdlc_ioctl(dev, ifr, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void n2_destroy_card(card_t *card)
|
||||
{
|
||||
int cnt;
|
||||
|
||||
for (cnt = 0; cnt < 2; cnt++)
|
||||
if (card->ports[cnt].card) {
|
||||
struct net_device *dev = port_to_dev(&card->ports[cnt]);
|
||||
unregister_hdlc_device(dev);
|
||||
}
|
||||
|
||||
if (card->irq)
|
||||
free_irq(card->irq, card);
|
||||
|
||||
if (card->winbase) {
|
||||
iounmap(card->winbase);
|
||||
release_mem_region(card->phy_winbase, USE_WINDOWSIZE);
|
||||
}
|
||||
|
||||
if (card->io)
|
||||
release_region(card->io, N2_IOPORTS);
|
||||
if (card->ports[0].dev)
|
||||
free_netdev(card->ports[0].dev);
|
||||
if (card->ports[1].dev)
|
||||
free_netdev(card->ports[1].dev);
|
||||
kfree(card);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int __init n2_run(unsigned long io, unsigned long irq,
|
||||
unsigned long winbase, long valid0, long valid1)
|
||||
{
|
||||
card_t *card;
|
||||
u8 cnt, pcr;
|
||||
int i;
|
||||
|
||||
if (io < 0x200 || io > 0x3FF || (io % N2_IOPORTS) != 0) {
|
||||
printk(KERN_ERR "n2: invalid I/O port value\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (irq < 3 || irq > 15 || irq == 6) /* FIXME */ {
|
||||
printk(KERN_ERR "n2: invalid IRQ value\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (winbase < 0xA0000 || winbase > 0xFFFFF || (winbase & 0xFFF) != 0) {
|
||||
printk(KERN_ERR "n2: invalid RAM value\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
card = kmalloc(sizeof(card_t), GFP_KERNEL);
|
||||
if (card == NULL) {
|
||||
printk(KERN_ERR "n2: unable to allocate memory\n");
|
||||
return -ENOBUFS;
|
||||
}
|
||||
memset(card, 0, sizeof(card_t));
|
||||
|
||||
card->ports[0].dev = alloc_hdlcdev(&card->ports[0]);
|
||||
card->ports[1].dev = alloc_hdlcdev(&card->ports[1]);
|
||||
if (!card->ports[0].dev || !card->ports[1].dev) {
|
||||
printk(KERN_ERR "n2: unable to allocate memory\n");
|
||||
n2_destroy_card(card);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!request_region(io, N2_IOPORTS, devname)) {
|
||||
printk(KERN_ERR "n2: I/O port region in use\n");
|
||||
n2_destroy_card(card);
|
||||
return -EBUSY;
|
||||
}
|
||||
card->io = io;
|
||||
|
||||
if (request_irq(irq, &sca_intr, 0, devname, card)) {
|
||||
printk(KERN_ERR "n2: could not allocate IRQ\n");
|
||||
n2_destroy_card(card);
|
||||
return(-EBUSY);
|
||||
}
|
||||
card->irq = irq;
|
||||
|
||||
if (!request_mem_region(winbase, USE_WINDOWSIZE, devname)) {
|
||||
printk(KERN_ERR "n2: could not request RAM window\n");
|
||||
n2_destroy_card(card);
|
||||
return(-EBUSY);
|
||||
}
|
||||
card->phy_winbase = winbase;
|
||||
card->winbase = ioremap(winbase, USE_WINDOWSIZE);
|
||||
if (!card->winbase) {
|
||||
printk(KERN_ERR "n2: ioremap() failed\n");
|
||||
n2_destroy_card(card);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
outb(0, io + N2_PCR);
|
||||
outb(winbase >> 12, io + N2_BAR);
|
||||
|
||||
switch (USE_WINDOWSIZE) {
|
||||
case 16384:
|
||||
outb(WIN16K, io + N2_PSR);
|
||||
break;
|
||||
|
||||
case 32768:
|
||||
outb(WIN32K, io + N2_PSR);
|
||||
break;
|
||||
|
||||
case 65536:
|
||||
outb(WIN64K, io + N2_PSR);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_ERR "n2: invalid window size\n");
|
||||
n2_destroy_card(card);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pcr = PCR_ENWIN | PCR_VPM | (USE_BUS16BITS ? PCR_BUS16 : 0);
|
||||
outb(pcr, io + N2_PCR);
|
||||
|
||||
card->ram_size = sca_detect_ram(card, card->winbase, MAX_RAM_SIZE);
|
||||
|
||||
/* number of TX + RX buffers for one port */
|
||||
i = card->ram_size / ((valid0 + valid1) * (sizeof(pkt_desc) +
|
||||
HDLC_MAX_MRU));
|
||||
|
||||
card->tx_ring_buffers = min(i / 2, MAX_TX_BUFFERS);
|
||||
card->rx_ring_buffers = i - card->tx_ring_buffers;
|
||||
|
||||
card->buff_offset = (valid0 + valid1) * sizeof(pkt_desc) *
|
||||
(card->tx_ring_buffers + card->rx_ring_buffers);
|
||||
|
||||
printk(KERN_INFO "n2: RISCom/N2 %u KB RAM, IRQ%u, "
|
||||
"using %u TX + %u RX packets rings\n", card->ram_size / 1024,
|
||||
card->irq, card->tx_ring_buffers, card->rx_ring_buffers);
|
||||
|
||||
if (card->tx_ring_buffers < 1) {
|
||||
printk(KERN_ERR "n2: RAM test failed\n");
|
||||
n2_destroy_card(card);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
pcr |= PCR_RUNSCA; /* run SCA */
|
||||
outb(pcr, io + N2_PCR);
|
||||
outb(0, io + N2_MCR);
|
||||
|
||||
sca_init(card, 0);
|
||||
for (cnt = 0; cnt < 2; cnt++) {
|
||||
port_t *port = &card->ports[cnt];
|
||||
struct net_device *dev = port_to_dev(port);
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
|
||||
if ((cnt == 0 && !valid0) || (cnt == 1 && !valid1))
|
||||
continue;
|
||||
|
||||
port->phy_node = cnt;
|
||||
port->valid = 1;
|
||||
|
||||
if ((cnt == 1) && valid0)
|
||||
port->log_node = 1;
|
||||
|
||||
spin_lock_init(&port->lock);
|
||||
SET_MODULE_OWNER(dev);
|
||||
dev->irq = irq;
|
||||
dev->mem_start = winbase;
|
||||
dev->mem_end = winbase + USE_WINDOWSIZE - 1;
|
||||
dev->tx_queue_len = 50;
|
||||
dev->do_ioctl = n2_ioctl;
|
||||
dev->open = n2_open;
|
||||
dev->stop = n2_close;
|
||||
hdlc->attach = sca_attach;
|
||||
hdlc->xmit = sca_xmit;
|
||||
port->settings.clock_type = CLOCK_EXT;
|
||||
port->card = card;
|
||||
|
||||
if (register_hdlc_device(dev)) {
|
||||
printk(KERN_WARNING "n2: unable to register hdlc "
|
||||
"device\n");
|
||||
port->card = NULL;
|
||||
n2_destroy_card(card);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
sca_init_sync_port(port); /* Set up SCA memory */
|
||||
|
||||
printk(KERN_INFO "%s: RISCom/N2 node %d\n",
|
||||
dev->name, port->phy_node);
|
||||
}
|
||||
|
||||
*new_card = card;
|
||||
new_card = &card->next_card;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int __init n2_init(void)
|
||||
{
|
||||
if (hw==NULL) {
|
||||
#ifdef MODULE
|
||||
printk(KERN_INFO "n2: no card initialized\n");
|
||||
#endif
|
||||
return -EINVAL; /* no parameters specified, abort */
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s\n", version);
|
||||
|
||||
do {
|
||||
unsigned long io, irq, ram;
|
||||
long valid[2] = { 0, 0 }; /* Default = both ports disabled */
|
||||
|
||||
io = simple_strtoul(hw, &hw, 0);
|
||||
|
||||
if (*hw++ != ',')
|
||||
break;
|
||||
irq = simple_strtoul(hw, &hw, 0);
|
||||
|
||||
if (*hw++ != ',')
|
||||
break;
|
||||
ram = simple_strtoul(hw, &hw, 0);
|
||||
|
||||
if (*hw++ != ',')
|
||||
break;
|
||||
while(1) {
|
||||
if (*hw == '0' && !valid[0])
|
||||
valid[0] = 1; /* Port 0 enabled */
|
||||
else if (*hw == '1' && !valid[1])
|
||||
valid[1] = 1; /* Port 1 enabled */
|
||||
else
|
||||
break;
|
||||
hw++;
|
||||
}
|
||||
|
||||
if (!valid[0] && !valid[1])
|
||||
break; /* at least one port must be used */
|
||||
|
||||
if (*hw == ':' || *hw == '\x0')
|
||||
n2_run(io, irq, ram, valid[0], valid[1]);
|
||||
|
||||
if (*hw == '\x0')
|
||||
return first_card ? 0 : -EINVAL;
|
||||
}while(*hw++ == ':');
|
||||
|
||||
printk(KERN_ERR "n2: invalid hardware parameters\n");
|
||||
return first_card ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static void __exit n2_cleanup(void)
|
||||
{
|
||||
card_t *card = first_card;
|
||||
|
||||
while (card) {
|
||||
card_t *ptr = card;
|
||||
card = card->next_card;
|
||||
n2_destroy_card(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module_init(n2_init);
|
||||
module_exit(n2_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
|
||||
MODULE_DESCRIPTION("RISCom/N2 serial port driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
module_param(hw, charp, 0444);
|
||||
MODULE_PARM_DESC(hw, "io,irq,ram,ports:io,irq,...");
|
||||
1241
drivers/net/wan/pc300-falc-lh.h
Normal file
1241
drivers/net/wan/pc300-falc-lh.h
Normal file
File diff suppressed because it is too large
Load Diff
485
drivers/net/wan/pc300.h
Normal file
485
drivers/net/wan/pc300.h
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* pc300.h Cyclades-PC300(tm) Kernel API Definitions.
|
||||
*
|
||||
* Author: Ivan Passos <ivan@cyclades.com>
|
||||
*
|
||||
* Copyright: (c) 1999-2002 Cyclades Corp.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $Log: pc300.h,v $
|
||||
* Revision 1.1.1.1 2007/06/12 07:27:11 eyryu
|
||||
* s3c-linux-2.6.21.5
|
||||
*
|
||||
* Revision 3.12 2002/03/07 14:17:09 henrique
|
||||
* License data fixed
|
||||
*
|
||||
* Revision 3.11 2002/01/28 21:09:39 daniela
|
||||
* Included ';' after pc300hw.bus.
|
||||
*
|
||||
* Revision 3.10 2002/01/17 17:58:52 ivan
|
||||
* Support for PC300-TE/M (PMC).
|
||||
*
|
||||
* Revision 3.9 2001/09/28 13:30:53 daniela
|
||||
* Renamed dma_start routine to rx_dma_start.
|
||||
*
|
||||
* Revision 3.8 2001/09/24 13:03:45 daniela
|
||||
* Fixed BOF interrupt treatment. Created dma_start routine.
|
||||
*
|
||||
* Revision 3.7 2001/08/10 17:19:58 daniela
|
||||
* Fixed IOCTLs defines.
|
||||
*
|
||||
* Revision 3.6 2001/07/18 19:24:42 daniela
|
||||
* Included kernel version.
|
||||
*
|
||||
* Revision 3.5 2001/07/05 18:38:08 daniela
|
||||
* DMA transmission bug fix.
|
||||
*
|
||||
* Revision 3.4 2001/06/26 17:10:40 daniela
|
||||
* New configuration parameters (line code, CRC calculation and clock).
|
||||
*
|
||||
* Revision 3.3 2001/06/22 13:13:02 regina
|
||||
* MLPPP implementation
|
||||
*
|
||||
* Revision 3.2 2001/06/18 17:56:09 daniela
|
||||
* Increased DEF_MTU and TX_QUEUE_LEN.
|
||||
*
|
||||
* Revision 3.1 2001/06/15 12:41:10 regina
|
||||
* upping major version number
|
||||
*
|
||||
* Revision 1.1.1.1 2001/06/13 20:25:06 daniela
|
||||
* PC300 initial CVS version (3.4.0-pre1)
|
||||
*
|
||||
* Revision 2.3 2001/03/05 daniela
|
||||
* Created struct pc300conf, to provide the hardware information to pc300util.
|
||||
* Inclusion of 'alloc_ramsize' field on structure 'pc300hw'.
|
||||
*
|
||||
* Revision 2.2 2000/12/22 daniela
|
||||
* Structures and defines to support pc300util: statistics, status,
|
||||
* loopback tests, trace.
|
||||
*
|
||||
* Revision 2.1 2000/09/28 ivan
|
||||
* Inclusion of 'iophys' and 'iosize' fields on structure 'pc300hw', to
|
||||
* allow release of I/O region at module unload.
|
||||
* Changed location of include files.
|
||||
*
|
||||
* Revision 2.0 2000/03/27 ivan
|
||||
* Added support for the PC300/TE cards.
|
||||
*
|
||||
* Revision 1.1 2000/01/31 ivan
|
||||
* Replaced 'pc300[drv|sca].h' former PC300 driver include files.
|
||||
*
|
||||
* Revision 1.0 1999/12/16 ivan
|
||||
* First official release.
|
||||
* Inclusion of 'nchan' field on structure 'pc300hw', to allow variable
|
||||
* number of ports per card.
|
||||
* Inclusion of 'if_ptr' field on structure 'pc300dev'.
|
||||
*
|
||||
* Revision 0.6 1999/11/17 ivan
|
||||
* Changed X.25-specific function names to comply with adopted convention.
|
||||
*
|
||||
* Revision 0.5 1999/11/16 Daniela Squassoni
|
||||
* X.25 support.
|
||||
*
|
||||
* Revision 0.4 1999/11/15 ivan
|
||||
* Inclusion of 'clock' field on structure 'pc300hw'.
|
||||
*
|
||||
* Revision 0.3 1999/11/10 ivan
|
||||
* IOCTL name changing.
|
||||
* Inclusion of driver function prototypes.
|
||||
*
|
||||
* Revision 0.2 1999/11/03 ivan
|
||||
* Inclusion of 'tx_skb' and union 'ifu' on structure 'pc300dev'.
|
||||
*
|
||||
* Revision 0.1 1999/01/15 ivan
|
||||
* Initial version.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PC300_H
|
||||
#define _PC300_H
|
||||
|
||||
#include <linux/hdlc.h>
|
||||
#include <net/syncppp.h>
|
||||
#include "hd64572.h"
|
||||
#include "pc300-falc-lh.h"
|
||||
|
||||
#ifndef CY_TYPES
|
||||
#define CY_TYPES
|
||||
typedef __u64 ucdouble; /* 64 bits, unsigned */
|
||||
typedef __u32 uclong; /* 32 bits, unsigned */
|
||||
typedef __u16 ucshort; /* 16 bits, unsigned */
|
||||
typedef __u8 ucchar; /* 8 bits, unsigned */
|
||||
#endif /* CY_TYPES */
|
||||
|
||||
#define PC300_PROTO_MLPPP 1
|
||||
|
||||
#define PC300_KERNEL "2.4.x" /* Kernel supported by this driver */
|
||||
|
||||
#define PC300_DEVNAME "hdlc" /* Dev. name base (for hdlc0, hdlc1, etc.) */
|
||||
#define PC300_MAXINDEX 100 /* Max dev. name index (the '0' in hdlc0) */
|
||||
|
||||
#define PC300_MAXCARDS 4 /* Max number of cards per system */
|
||||
#define PC300_MAXCHAN 2 /* Number of channels per card */
|
||||
|
||||
#define PC300_PLX_WIN 0x80 /* PLX control window size (128b) */
|
||||
#define PC300_RAMSIZE 0x40000 /* RAM window size (256Kb) */
|
||||
#define PC300_SCASIZE 0x400 /* SCA window size (1Kb) */
|
||||
#define PC300_FALCSIZE 0x400 /* FALC window size (1Kb) */
|
||||
|
||||
#define PC300_OSC_CLOCK 24576000
|
||||
#define PC300_PCI_CLOCK 33000000
|
||||
|
||||
#define BD_DEF_LEN 0x0800 /* DMA buffer length (2KB) */
|
||||
#define DMA_TX_MEMSZ 0x8000 /* Total DMA Tx memory size (32KB/ch) */
|
||||
#define DMA_RX_MEMSZ 0x10000 /* Total DMA Rx memory size (64KB/ch) */
|
||||
|
||||
#define N_DMA_TX_BUF (DMA_TX_MEMSZ / BD_DEF_LEN) /* DMA Tx buffers */
|
||||
#define N_DMA_RX_BUF (DMA_RX_MEMSZ / BD_DEF_LEN) /* DMA Rx buffers */
|
||||
|
||||
/* DMA Buffer Offsets */
|
||||
#define DMA_TX_BASE ((N_DMA_TX_BUF + N_DMA_RX_BUF) * \
|
||||
PC300_MAXCHAN * sizeof(pcsca_bd_t))
|
||||
#define DMA_RX_BASE (DMA_TX_BASE + PC300_MAXCHAN*DMA_TX_MEMSZ)
|
||||
|
||||
/* DMA Descriptor Offsets */
|
||||
#define DMA_TX_BD_BASE 0x0000
|
||||
#define DMA_RX_BD_BASE (DMA_TX_BD_BASE + ((PC300_MAXCHAN*DMA_TX_MEMSZ / \
|
||||
BD_DEF_LEN) * sizeof(pcsca_bd_t)))
|
||||
|
||||
/* DMA Descriptor Macros */
|
||||
#define TX_BD_ADDR(chan, n) (DMA_TX_BD_BASE + \
|
||||
((N_DMA_TX_BUF*chan) + n) * sizeof(pcsca_bd_t))
|
||||
#define RX_BD_ADDR(chan, n) (DMA_RX_BD_BASE + \
|
||||
((N_DMA_RX_BUF*chan) + n) * sizeof(pcsca_bd_t))
|
||||
|
||||
/* Macro to access the FALC registers (TE only) */
|
||||
#define F_REG(reg, chan) (0x200*(chan) + ((reg)<<2))
|
||||
|
||||
/***************************************
|
||||
* Memory access functions/macros *
|
||||
* (required to support Alpha systems) *
|
||||
***************************************/
|
||||
#ifdef __KERNEL__
|
||||
#define cpc_writeb(port,val) {writeb((ucchar)(val),(port)); mb();}
|
||||
#define cpc_writew(port,val) {writew((ushort)(val),(port)); mb();}
|
||||
#define cpc_writel(port,val) {writel((uclong)(val),(port)); mb();}
|
||||
|
||||
#define cpc_readb(port) readb(port)
|
||||
#define cpc_readw(port) readw(port)
|
||||
#define cpc_readl(port) readl(port)
|
||||
|
||||
#else /* __KERNEL__ */
|
||||
#define cpc_writeb(port,val) (*(volatile ucchar *)(port) = (ucchar)(val))
|
||||
#define cpc_writew(port,val) (*(volatile ucshort *)(port) = (ucshort)(val))
|
||||
#define cpc_writel(port,val) (*(volatile uclong *)(port) = (uclong)(val))
|
||||
|
||||
#define cpc_readb(port) (*(volatile ucchar *)(port))
|
||||
#define cpc_readw(port) (*(volatile ucshort *)(port))
|
||||
#define cpc_readl(port) (*(volatile uclong *)(port))
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
/****** Data Structures *****************************************************/
|
||||
|
||||
/*
|
||||
* RUNTIME_9050 - PLX PCI9050-1 local configuration and shared runtime
|
||||
* registers. This structure can be used to access the 9050 registers
|
||||
* (memory mapped).
|
||||
*/
|
||||
struct RUNTIME_9050 {
|
||||
uclong loc_addr_range[4]; /* 00-0Ch : Local Address Ranges */
|
||||
uclong loc_rom_range; /* 10h : Local ROM Range */
|
||||
uclong loc_addr_base[4]; /* 14-20h : Local Address Base Addrs */
|
||||
uclong loc_rom_base; /* 24h : Local ROM Base */
|
||||
uclong loc_bus_descr[4]; /* 28-34h : Local Bus Descriptors */
|
||||
uclong rom_bus_descr; /* 38h : ROM Bus Descriptor */
|
||||
uclong cs_base[4]; /* 3C-48h : Chip Select Base Addrs */
|
||||
uclong intr_ctrl_stat; /* 4Ch : Interrupt Control/Status */
|
||||
uclong init_ctrl; /* 50h : EEPROM ctrl, Init Ctrl, etc */
|
||||
};
|
||||
|
||||
#define PLX_9050_LINT1_ENABLE 0x01
|
||||
#define PLX_9050_LINT1_POL 0x02
|
||||
#define PLX_9050_LINT1_STATUS 0x04
|
||||
#define PLX_9050_LINT2_ENABLE 0x08
|
||||
#define PLX_9050_LINT2_POL 0x10
|
||||
#define PLX_9050_LINT2_STATUS 0x20
|
||||
#define PLX_9050_INTR_ENABLE 0x40
|
||||
#define PLX_9050_SW_INTR 0x80
|
||||
|
||||
/* Masks to access the init_ctrl PLX register */
|
||||
#define PC300_CLKSEL_MASK (0x00000004UL)
|
||||
#define PC300_CHMEDIA_MASK(chan) (0x00000020UL<<(chan*3))
|
||||
#define PC300_CTYPE_MASK (0x00000800UL)
|
||||
|
||||
/* CPLD Registers (base addr = falcbase, TE only) */
|
||||
/* CPLD v. 0 */
|
||||
#define CPLD_REG1 0x140 /* Chip resets, DCD/CTS status */
|
||||
#define CPLD_REG2 0x144 /* Clock enable , LED control */
|
||||
/* CPLD v. 2 or higher */
|
||||
#define CPLD_V2_REG1 0x100 /* Chip resets, DCD/CTS status */
|
||||
#define CPLD_V2_REG2 0x104 /* Clock enable , LED control */
|
||||
#define CPLD_ID_REG 0x108 /* CPLD version */
|
||||
|
||||
/* CPLD Register bit description: for the FALC bits, they should always be
|
||||
set based on the channel (use (bit<<(2*ch)) to access the correct bit for
|
||||
that channel) */
|
||||
#define CPLD_REG1_FALC_RESET 0x01
|
||||
#define CPLD_REG1_SCA_RESET 0x02
|
||||
#define CPLD_REG1_GLOBAL_CLK 0x08
|
||||
#define CPLD_REG1_FALC_DCD 0x10
|
||||
#define CPLD_REG1_FALC_CTS 0x20
|
||||
|
||||
#define CPLD_REG2_FALC_TX_CLK 0x01
|
||||
#define CPLD_REG2_FALC_RX_CLK 0x02
|
||||
#define CPLD_REG2_FALC_LED1 0x10
|
||||
#define CPLD_REG2_FALC_LED2 0x20
|
||||
|
||||
/* Structure with FALC-related fields (TE only) */
|
||||
#define PC300_FALC_MAXLOOP 0x0000ffff /* for falc_issue_cmd() */
|
||||
|
||||
typedef struct falc {
|
||||
ucchar sync; /* If true FALC is synchronized */
|
||||
ucchar active; /* if TRUE then already active */
|
||||
ucchar loop_active; /* if TRUE a line loopback UP was received */
|
||||
ucchar loop_gen; /* if TRUE a line loopback UP was issued */
|
||||
|
||||
ucchar num_channels;
|
||||
ucchar offset; /* 1 for T1, 0 for E1 */
|
||||
ucchar full_bandwidth;
|
||||
|
||||
ucchar xmb_cause;
|
||||
ucchar multiframe_mode;
|
||||
|
||||
/* Statistics */
|
||||
ucshort pden; /* Pulse Density violation count */
|
||||
ucshort los; /* Loss of Signal count */
|
||||
ucshort losr; /* Loss of Signal recovery count */
|
||||
ucshort lfa; /* Loss of frame alignment count */
|
||||
ucshort farec; /* Frame Alignment Recovery count */
|
||||
ucshort lmfa; /* Loss of multiframe alignment count */
|
||||
ucshort ais; /* Remote Alarm indication Signal count */
|
||||
ucshort sec; /* One-second timer */
|
||||
ucshort es; /* Errored second */
|
||||
ucshort rai; /* remote alarm received */
|
||||
ucshort bec;
|
||||
ucshort fec;
|
||||
ucshort cvc;
|
||||
ucshort cec;
|
||||
ucshort ebc;
|
||||
|
||||
/* Status */
|
||||
ucchar red_alarm;
|
||||
ucchar blue_alarm;
|
||||
ucchar loss_fa;
|
||||
ucchar yellow_alarm;
|
||||
ucchar loss_mfa;
|
||||
ucchar prbs;
|
||||
} falc_t;
|
||||
|
||||
typedef struct falc_status {
|
||||
ucchar sync; /* If true FALC is synchronized */
|
||||
ucchar red_alarm;
|
||||
ucchar blue_alarm;
|
||||
ucchar loss_fa;
|
||||
ucchar yellow_alarm;
|
||||
ucchar loss_mfa;
|
||||
ucchar prbs;
|
||||
} falc_status_t;
|
||||
|
||||
typedef struct rsv_x21_status {
|
||||
ucchar dcd;
|
||||
ucchar dsr;
|
||||
ucchar cts;
|
||||
ucchar rts;
|
||||
ucchar dtr;
|
||||
} rsv_x21_status_t;
|
||||
|
||||
typedef struct pc300stats {
|
||||
int hw_type;
|
||||
uclong line_on;
|
||||
uclong line_off;
|
||||
struct net_device_stats gen_stats;
|
||||
falc_t te_stats;
|
||||
} pc300stats_t;
|
||||
|
||||
typedef struct pc300status {
|
||||
int hw_type;
|
||||
rsv_x21_status_t gen_status;
|
||||
falc_status_t te_status;
|
||||
} pc300status_t;
|
||||
|
||||
typedef struct pc300loopback {
|
||||
char loop_type;
|
||||
char loop_on;
|
||||
} pc300loopback_t;
|
||||
|
||||
typedef struct pc300patterntst {
|
||||
char patrntst_on; /* 0 - off; 1 - on; 2 - read num_errors */
|
||||
ucshort num_errors;
|
||||
} pc300patterntst_t;
|
||||
|
||||
typedef struct pc300dev {
|
||||
void *if_ptr; /* General purpose pointer */
|
||||
struct pc300ch *chan;
|
||||
ucchar trace_on;
|
||||
uclong line_on; /* DCD(X.21, RSV) / sync(TE) change counters */
|
||||
uclong line_off;
|
||||
#ifdef __KERNEL__
|
||||
char name[16];
|
||||
struct net_device *dev;
|
||||
|
||||
void *private;
|
||||
struct sk_buff *tx_skb;
|
||||
union { /* This union has all the protocol-specific structures */
|
||||
struct ppp_device pppdev;
|
||||
}ifu;
|
||||
#ifdef CONFIG_PC300_MLPPP
|
||||
void *cpc_tty; /* information to PC300 TTY driver */
|
||||
#endif
|
||||
#endif /* __KERNEL__ */
|
||||
}pc300dev_t;
|
||||
|
||||
typedef struct pc300hw {
|
||||
int type; /* RSV, X21, etc. */
|
||||
int bus; /* Bus (PCI, PMC, etc.) */
|
||||
int nchan; /* number of channels */
|
||||
int irq; /* interrupt request level */
|
||||
uclong clock; /* Board clock */
|
||||
ucchar cpld_id; /* CPLD ID (TE only) */
|
||||
ucshort cpld_reg1; /* CPLD reg 1 (TE only) */
|
||||
ucshort cpld_reg2; /* CPLD reg 2 (TE only) */
|
||||
ucshort gpioc_reg; /* PLX GPIOC reg */
|
||||
ucshort intctl_reg; /* PLX Int Ctrl/Status reg */
|
||||
uclong iophys; /* PLX registers I/O base */
|
||||
uclong iosize; /* PLX registers I/O size */
|
||||
uclong plxphys; /* PLX registers MMIO base (physical) */
|
||||
void __iomem * plxbase; /* PLX registers MMIO base (virtual) */
|
||||
uclong plxsize; /* PLX registers MMIO size */
|
||||
uclong scaphys; /* SCA registers MMIO base (physical) */
|
||||
void __iomem * scabase; /* SCA registers MMIO base (virtual) */
|
||||
uclong scasize; /* SCA registers MMIO size */
|
||||
uclong ramphys; /* On-board RAM MMIO base (physical) */
|
||||
void __iomem * rambase; /* On-board RAM MMIO base (virtual) */
|
||||
uclong alloc_ramsize; /* RAM MMIO size allocated by the PCI bridge */
|
||||
uclong ramsize; /* On-board RAM MMIO size */
|
||||
uclong falcphys; /* FALC registers MMIO base (physical) */
|
||||
void __iomem * falcbase;/* FALC registers MMIO base (virtual) */
|
||||
uclong falcsize; /* FALC registers MMIO size */
|
||||
} pc300hw_t;
|
||||
|
||||
typedef struct pc300chconf {
|
||||
sync_serial_settings phys_settings; /* Clock type/rate (in bps),
|
||||
loopback mode */
|
||||
raw_hdlc_proto proto_settings; /* Encoding, parity (CRC) */
|
||||
uclong media; /* HW media (RS232, V.35, etc.) */
|
||||
uclong proto; /* Protocol (PPP, X.25, etc.) */
|
||||
ucchar monitor; /* Monitor mode (0 = off, !0 = on) */
|
||||
|
||||
/* TE-specific parameters */
|
||||
ucchar lcode; /* Line Code (AMI, B8ZS, etc.) */
|
||||
ucchar fr_mode; /* Frame Mode (ESF, D4, etc.) */
|
||||
ucchar lbo; /* Line Build Out */
|
||||
ucchar rx_sens; /* Rx Sensitivity (long- or short-haul) */
|
||||
uclong tslot_bitmap; /* bit[i]=1 => timeslot _i_ is active */
|
||||
} pc300chconf_t;
|
||||
|
||||
typedef struct pc300ch {
|
||||
struct pc300 *card;
|
||||
int channel;
|
||||
pc300dev_t d;
|
||||
pc300chconf_t conf;
|
||||
ucchar tx_first_bd; /* First TX DMA block descr. w/ data */
|
||||
ucchar tx_next_bd; /* Next free TX DMA block descriptor */
|
||||
ucchar rx_first_bd; /* First free RX DMA block descriptor */
|
||||
ucchar rx_last_bd; /* Last free RX DMA block descriptor */
|
||||
ucchar nfree_tx_bd; /* Number of free TX DMA block descriptors */
|
||||
falc_t falc; /* FALC structure (TE only) */
|
||||
} pc300ch_t;
|
||||
|
||||
typedef struct pc300 {
|
||||
pc300hw_t hw; /* hardware config. */
|
||||
pc300ch_t chan[PC300_MAXCHAN];
|
||||
#ifdef __KERNEL__
|
||||
spinlock_t card_lock;
|
||||
#endif /* __KERNEL__ */
|
||||
} pc300_t;
|
||||
|
||||
typedef struct pc300conf {
|
||||
pc300hw_t hw;
|
||||
pc300chconf_t conf;
|
||||
} pc300conf_t;
|
||||
|
||||
/* DEV ioctl() commands */
|
||||
#define N_SPPP_IOCTLS 2
|
||||
|
||||
enum pc300_ioctl_cmds {
|
||||
SIOCCPCRESERVED = (SIOCDEVPRIVATE + N_SPPP_IOCTLS),
|
||||
SIOCGPC300CONF,
|
||||
SIOCSPC300CONF,
|
||||
SIOCGPC300STATUS,
|
||||
SIOCGPC300FALCSTATUS,
|
||||
SIOCGPC300UTILSTATS,
|
||||
SIOCGPC300UTILSTATUS,
|
||||
SIOCSPC300TRACE,
|
||||
SIOCSPC300LOOPBACK,
|
||||
SIOCSPC300PATTERNTEST,
|
||||
};
|
||||
|
||||
/* Loopback types - PC300/TE boards */
|
||||
enum pc300_loopback_cmds {
|
||||
PC300LOCLOOP = 1,
|
||||
PC300REMLOOP,
|
||||
PC300PAYLOADLOOP,
|
||||
PC300GENLOOPUP,
|
||||
PC300GENLOOPDOWN,
|
||||
};
|
||||
|
||||
/* Control Constant Definitions */
|
||||
#define PC300_RSV 0x01
|
||||
#define PC300_X21 0x02
|
||||
#define PC300_TE 0x03
|
||||
|
||||
#define PC300_PCI 0x00
|
||||
#define PC300_PMC 0x01
|
||||
|
||||
#define PC300_LC_AMI 0x01
|
||||
#define PC300_LC_B8ZS 0x02
|
||||
#define PC300_LC_NRZ 0x03
|
||||
#define PC300_LC_HDB3 0x04
|
||||
|
||||
/* Framing (T1) */
|
||||
#define PC300_FR_ESF 0x01
|
||||
#define PC300_FR_D4 0x02
|
||||
#define PC300_FR_ESF_JAPAN 0x03
|
||||
|
||||
/* Framing (E1) */
|
||||
#define PC300_FR_MF_CRC4 0x04
|
||||
#define PC300_FR_MF_NON_CRC4 0x05
|
||||
#define PC300_FR_UNFRAMED 0x06
|
||||
|
||||
#define PC300_LBO_0_DB 0x00
|
||||
#define PC300_LBO_7_5_DB 0x01
|
||||
#define PC300_LBO_15_DB 0x02
|
||||
#define PC300_LBO_22_5_DB 0x03
|
||||
|
||||
#define PC300_RX_SENS_SH 0x01
|
||||
#define PC300_RX_SENS_LH 0x02
|
||||
|
||||
#define PC300_TX_TIMEOUT (2*HZ)
|
||||
#define PC300_TX_QUEUE_LEN 100
|
||||
#define PC300_DEF_MTU 1600
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/* Function Prototypes */
|
||||
void tx_dma_start(pc300_t *, int);
|
||||
int cpc_open(struct net_device *dev);
|
||||
int cpc_set_media(hdlc_device *, int);
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _PC300_H */
|
||||
|
||||
3710
drivers/net/wan/pc300_drv.c
Normal file
3710
drivers/net/wan/pc300_drv.c
Normal file
File diff suppressed because it is too large
Load Diff
1096
drivers/net/wan/pc300_tty.c
Normal file
1096
drivers/net/wan/pc300_tty.c
Normal file
File diff suppressed because it is too large
Load Diff
565
drivers/net/wan/pc300too.c
Normal file
565
drivers/net/wan/pc300too.c
Normal file
@@ -0,0 +1,565 @@
|
||||
/*
|
||||
* Cyclades PC300 synchronous serial card driver for Linux
|
||||
*
|
||||
* Copyright (C) 2000-2007 Krzysztof Halasa <khc@pm.waw.pl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* For information see <http://www.kernel.org/pub/linux/utils/net/hdlc/>.
|
||||
*
|
||||
* Sources of information:
|
||||
* Hitachi HD64572 SCA-II User's Manual
|
||||
* Cyclades PC300 Linux driver
|
||||
*
|
||||
* This driver currently supports only PC300/RSV (V.24/V.35) and
|
||||
* PC300/X21 cards.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/hdlc.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "hd64572.h"
|
||||
|
||||
static const char* version = "Cyclades PC300 driver version: 1.17";
|
||||
static const char* devname = "PC300";
|
||||
|
||||
#undef DEBUG_PKT
|
||||
#define DEBUG_RINGS
|
||||
|
||||
#define PC300_PLX_SIZE 0x80 /* PLX control window size (128 B) */
|
||||
#define PC300_SCA_SIZE 0x400 /* SCA window size (1 KB) */
|
||||
#define ALL_PAGES_ALWAYS_MAPPED
|
||||
#define NEED_DETECT_RAM
|
||||
#define NEED_SCA_MSCI_INTR
|
||||
#define MAX_TX_BUFFERS 10
|
||||
|
||||
static int pci_clock_freq = 33000000;
|
||||
static int use_crystal_clock = 0;
|
||||
static unsigned int CLOCK_BASE;
|
||||
|
||||
/* Masks to access the init_ctrl PLX register */
|
||||
#define PC300_CLKSEL_MASK (0x00000004UL)
|
||||
#define PC300_CHMEDIA_MASK(port) (0x00000020UL << ((port) * 3))
|
||||
#define PC300_CTYPE_MASK (0x00000800UL)
|
||||
|
||||
|
||||
enum { PC300_RSV = 1, PC300_X21, PC300_TE }; /* card types */
|
||||
|
||||
/*
|
||||
* PLX PCI9050-1 local configuration and shared runtime registers.
|
||||
* This structure can be used to access 9050 registers (memory mapped).
|
||||
*/
|
||||
typedef struct {
|
||||
u32 loc_addr_range[4]; /* 00-0Ch : Local Address Ranges */
|
||||
u32 loc_rom_range; /* 10h : Local ROM Range */
|
||||
u32 loc_addr_base[4]; /* 14-20h : Local Address Base Addrs */
|
||||
u32 loc_rom_base; /* 24h : Local ROM Base */
|
||||
u32 loc_bus_descr[4]; /* 28-34h : Local Bus Descriptors */
|
||||
u32 rom_bus_descr; /* 38h : ROM Bus Descriptor */
|
||||
u32 cs_base[4]; /* 3C-48h : Chip Select Base Addrs */
|
||||
u32 intr_ctrl_stat; /* 4Ch : Interrupt Control/Status */
|
||||
u32 init_ctrl; /* 50h : EEPROM ctrl, Init Ctrl, etc */
|
||||
}plx9050;
|
||||
|
||||
|
||||
|
||||
typedef struct port_s {
|
||||
struct net_device *dev;
|
||||
struct card_s *card;
|
||||
spinlock_t lock; /* TX lock */
|
||||
sync_serial_settings settings;
|
||||
int rxpart; /* partial frame received, next frame invalid*/
|
||||
unsigned short encoding;
|
||||
unsigned short parity;
|
||||
unsigned int iface;
|
||||
u16 rxin; /* rx ring buffer 'in' pointer */
|
||||
u16 txin; /* tx ring buffer 'in' and 'last' pointers */
|
||||
u16 txlast;
|
||||
u8 rxs, txs, tmc; /* SCA registers */
|
||||
u8 phy_node; /* physical port # - 0 or 1 */
|
||||
}port_t;
|
||||
|
||||
|
||||
|
||||
typedef struct card_s {
|
||||
int type; /* RSV, X21, etc. */
|
||||
int n_ports; /* 1 or 2 ports */
|
||||
u8 __iomem *rambase; /* buffer memory base (virtual) */
|
||||
u8 __iomem *scabase; /* SCA memory base (virtual) */
|
||||
plx9050 __iomem *plxbase; /* PLX registers memory base (virtual) */
|
||||
u32 init_ctrl_value; /* Saved value - 9050 bug workaround */
|
||||
u16 rx_ring_buffers; /* number of buffers in a ring */
|
||||
u16 tx_ring_buffers;
|
||||
u16 buff_offset; /* offset of first buffer of first channel */
|
||||
u8 irq; /* interrupt request level */
|
||||
|
||||
port_t ports[2];
|
||||
}card_t;
|
||||
|
||||
|
||||
#define sca_in(reg, card) readb(card->scabase + (reg))
|
||||
#define sca_out(value, reg, card) writeb(value, card->scabase + (reg))
|
||||
#define sca_inw(reg, card) readw(card->scabase + (reg))
|
||||
#define sca_outw(value, reg, card) writew(value, card->scabase + (reg))
|
||||
#define sca_inl(reg, card) readl(card->scabase + (reg))
|
||||
#define sca_outl(value, reg, card) writel(value, card->scabase + (reg))
|
||||
|
||||
#define port_to_card(port) (port->card)
|
||||
#define log_node(port) (port->phy_node)
|
||||
#define phy_node(port) (port->phy_node)
|
||||
#define winbase(card) (card->rambase)
|
||||
#define get_port(card, port) ((port) < (card)->n_ports ? \
|
||||
(&(card)->ports[port]) : (NULL))
|
||||
|
||||
#include "hd6457x.c"
|
||||
|
||||
|
||||
static void pc300_set_iface(port_t *port)
|
||||
{
|
||||
card_t *card = port->card;
|
||||
u32 __iomem * init_ctrl = &card->plxbase->init_ctrl;
|
||||
u16 msci = get_msci(port);
|
||||
u8 rxs = port->rxs & CLK_BRG_MASK;
|
||||
u8 txs = port->txs & CLK_BRG_MASK;
|
||||
|
||||
sca_out(EXS_TES1, (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET) + EXS,
|
||||
port_to_card(port));
|
||||
switch(port->settings.clock_type) {
|
||||
case CLOCK_INT:
|
||||
rxs |= CLK_BRG; /* BRG output */
|
||||
txs |= CLK_PIN_OUT | CLK_TX_RXCLK; /* RX clock */
|
||||
break;
|
||||
|
||||
case CLOCK_TXINT:
|
||||
rxs |= CLK_LINE; /* RXC input */
|
||||
txs |= CLK_PIN_OUT | CLK_BRG; /* BRG output */
|
||||
break;
|
||||
|
||||
case CLOCK_TXFROMRX:
|
||||
rxs |= CLK_LINE; /* RXC input */
|
||||
txs |= CLK_PIN_OUT | CLK_TX_RXCLK; /* RX clock */
|
||||
break;
|
||||
|
||||
default: /* EXTernal clock */
|
||||
rxs |= CLK_LINE; /* RXC input */
|
||||
txs |= CLK_PIN_OUT | CLK_LINE; /* TXC input */
|
||||
break;
|
||||
}
|
||||
|
||||
port->rxs = rxs;
|
||||
port->txs = txs;
|
||||
sca_out(rxs, msci + RXS, card);
|
||||
sca_out(txs, msci + TXS, card);
|
||||
sca_set_port(port);
|
||||
|
||||
if (port->card->type == PC300_RSV) {
|
||||
if (port->iface == IF_IFACE_V35)
|
||||
writel(card->init_ctrl_value |
|
||||
PC300_CHMEDIA_MASK(port->phy_node), init_ctrl);
|
||||
else
|
||||
writel(card->init_ctrl_value &
|
||||
~PC300_CHMEDIA_MASK(port->phy_node), init_ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int pc300_open(struct net_device *dev)
|
||||
{
|
||||
port_t *port = dev_to_port(dev);
|
||||
|
||||
int result = hdlc_open(dev);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
sca_open(dev);
|
||||
pc300_set_iface(port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int pc300_close(struct net_device *dev)
|
||||
{
|
||||
sca_close(dev);
|
||||
hdlc_close(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int pc300_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
const size_t size = sizeof(sync_serial_settings);
|
||||
sync_serial_settings new_line;
|
||||
sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
|
||||
int new_type;
|
||||
port_t *port = dev_to_port(dev);
|
||||
|
||||
#ifdef DEBUG_RINGS
|
||||
if (cmd == SIOCDEVPRIVATE) {
|
||||
sca_dump_rings(dev);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (cmd != SIOCWANDEV)
|
||||
return hdlc_ioctl(dev, ifr, cmd);
|
||||
|
||||
if (ifr->ifr_settings.type == IF_GET_IFACE) {
|
||||
ifr->ifr_settings.type = port->iface;
|
||||
if (ifr->ifr_settings.size < size) {
|
||||
ifr->ifr_settings.size = size; /* data size wanted */
|
||||
return -ENOBUFS;
|
||||
}
|
||||
if (copy_to_user(line, &port->settings, size))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
if (port->card->type == PC300_X21 &&
|
||||
(ifr->ifr_settings.type == IF_IFACE_SYNC_SERIAL ||
|
||||
ifr->ifr_settings.type == IF_IFACE_X21))
|
||||
new_type = IF_IFACE_X21;
|
||||
|
||||
else if (port->card->type == PC300_RSV &&
|
||||
(ifr->ifr_settings.type == IF_IFACE_SYNC_SERIAL ||
|
||||
ifr->ifr_settings.type == IF_IFACE_V35))
|
||||
new_type = IF_IFACE_V35;
|
||||
|
||||
else if (port->card->type == PC300_RSV &&
|
||||
ifr->ifr_settings.type == IF_IFACE_V24)
|
||||
new_type = IF_IFACE_V24;
|
||||
|
||||
else
|
||||
return hdlc_ioctl(dev, ifr, cmd);
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&new_line, line, size))
|
||||
return -EFAULT;
|
||||
|
||||
if (new_line.clock_type != CLOCK_EXT &&
|
||||
new_line.clock_type != CLOCK_TXFROMRX &&
|
||||
new_line.clock_type != CLOCK_INT &&
|
||||
new_line.clock_type != CLOCK_TXINT)
|
||||
return -EINVAL; /* No such clock setting */
|
||||
|
||||
if (new_line.loopback != 0 && new_line.loopback != 1)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&port->settings, &new_line, size); /* Update settings */
|
||||
port->iface = new_type;
|
||||
pc300_set_iface(port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void pc300_pci_remove_one(struct pci_dev *pdev)
|
||||
{
|
||||
int i;
|
||||
card_t *card = pci_get_drvdata(pdev);
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
if (card->ports[i].card) {
|
||||
struct net_device *dev = port_to_dev(&card->ports[i]);
|
||||
unregister_hdlc_device(dev);
|
||||
}
|
||||
|
||||
if (card->irq)
|
||||
free_irq(card->irq, card);
|
||||
|
||||
if (card->rambase)
|
||||
iounmap(card->rambase);
|
||||
if (card->scabase)
|
||||
iounmap(card->scabase);
|
||||
if (card->plxbase)
|
||||
iounmap(card->plxbase);
|
||||
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
if (card->ports[0].dev)
|
||||
free_netdev(card->ports[0].dev);
|
||||
if (card->ports[1].dev)
|
||||
free_netdev(card->ports[1].dev);
|
||||
kfree(card);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
card_t *card;
|
||||
u8 rev_id;
|
||||
u32 __iomem *p;
|
||||
int i;
|
||||
u32 ramsize;
|
||||
u32 ramphys; /* buffer memory base */
|
||||
u32 scaphys; /* SCA memory base */
|
||||
u32 plxphys; /* PLX registers memory base */
|
||||
|
||||
#ifndef MODULE
|
||||
static int printed_version;
|
||||
if (!printed_version++)
|
||||
printk(KERN_INFO "%s\n", version);
|
||||
#endif
|
||||
|
||||
i = pci_enable_device(pdev);
|
||||
if (i)
|
||||
return i;
|
||||
|
||||
i = pci_request_regions(pdev, "PC300");
|
||||
if (i) {
|
||||
pci_disable_device(pdev);
|
||||
return i;
|
||||
}
|
||||
|
||||
card = kmalloc(sizeof(card_t), GFP_KERNEL);
|
||||
if (card == NULL) {
|
||||
printk(KERN_ERR "pc300: unable to allocate memory\n");
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
memset(card, 0, sizeof(card_t));
|
||||
pci_set_drvdata(pdev, card);
|
||||
|
||||
if (pdev->device == PCI_DEVICE_ID_PC300_TE_1 ||
|
||||
pdev->device == PCI_DEVICE_ID_PC300_TE_2)
|
||||
card->type = PC300_TE; /* not fully supported */
|
||||
else if (card->init_ctrl_value & PC300_CTYPE_MASK)
|
||||
card->type = PC300_X21;
|
||||
else
|
||||
card->type = PC300_RSV;
|
||||
|
||||
if (pdev->device == PCI_DEVICE_ID_PC300_RX_1 ||
|
||||
pdev->device == PCI_DEVICE_ID_PC300_TE_1)
|
||||
card->n_ports = 1;
|
||||
else
|
||||
card->n_ports = 2;
|
||||
|
||||
for (i = 0; i < card->n_ports; i++)
|
||||
if (!(card->ports[i].dev = alloc_hdlcdev(&card->ports[i]))) {
|
||||
printk(KERN_ERR "pc300: unable to allocate memory\n");
|
||||
pc300_pci_remove_one(pdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id);
|
||||
if (pci_resource_len(pdev, 0) != PC300_PLX_SIZE ||
|
||||
pci_resource_len(pdev, 2) != PC300_SCA_SIZE ||
|
||||
pci_resource_len(pdev, 3) < 16384) {
|
||||
printk(KERN_ERR "pc300: invalid card EEPROM parameters\n");
|
||||
pc300_pci_remove_one(pdev);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
plxphys = pci_resource_start(pdev,0) & PCI_BASE_ADDRESS_MEM_MASK;
|
||||
card->plxbase = ioremap(plxphys, PC300_PLX_SIZE);
|
||||
|
||||
scaphys = pci_resource_start(pdev,2) & PCI_BASE_ADDRESS_MEM_MASK;
|
||||
card->scabase = ioremap(scaphys, PC300_SCA_SIZE);
|
||||
|
||||
ramphys = pci_resource_start(pdev,3) & PCI_BASE_ADDRESS_MEM_MASK;
|
||||
card->rambase = ioremap(ramphys, pci_resource_len(pdev,3));
|
||||
|
||||
if (card->plxbase == NULL ||
|
||||
card->scabase == NULL ||
|
||||
card->rambase == NULL) {
|
||||
printk(KERN_ERR "pc300: ioremap() failed\n");
|
||||
pc300_pci_remove_one(pdev);
|
||||
}
|
||||
|
||||
/* PLX PCI 9050 workaround for local configuration register read bug */
|
||||
pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, scaphys);
|
||||
card->init_ctrl_value = readl(&((plx9050 __iomem *)card->scabase)->init_ctrl);
|
||||
pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, plxphys);
|
||||
|
||||
/* Reset PLX */
|
||||
p = &card->plxbase->init_ctrl;
|
||||
writel(card->init_ctrl_value | 0x40000000, p);
|
||||
readl(p); /* Flush the write - do not use sca_flush */
|
||||
udelay(1);
|
||||
|
||||
writel(card->init_ctrl_value, p);
|
||||
readl(p); /* Flush the write - do not use sca_flush */
|
||||
udelay(1);
|
||||
|
||||
/* Reload Config. Registers from EEPROM */
|
||||
writel(card->init_ctrl_value | 0x20000000, p);
|
||||
readl(p); /* Flush the write - do not use sca_flush */
|
||||
udelay(1);
|
||||
|
||||
writel(card->init_ctrl_value, p);
|
||||
readl(p); /* Flush the write - do not use sca_flush */
|
||||
udelay(1);
|
||||
|
||||
ramsize = sca_detect_ram(card, card->rambase,
|
||||
pci_resource_len(pdev, 3));
|
||||
|
||||
if (use_crystal_clock)
|
||||
card->init_ctrl_value &= ~PC300_CLKSEL_MASK;
|
||||
else
|
||||
card->init_ctrl_value |= PC300_CLKSEL_MASK;
|
||||
|
||||
writel(card->init_ctrl_value, &card->plxbase->init_ctrl);
|
||||
/* number of TX + RX buffers for one port */
|
||||
i = ramsize / (card->n_ports * (sizeof(pkt_desc) + HDLC_MAX_MRU));
|
||||
card->tx_ring_buffers = min(i / 2, MAX_TX_BUFFERS);
|
||||
card->rx_ring_buffers = i - card->tx_ring_buffers;
|
||||
|
||||
card->buff_offset = card->n_ports * sizeof(pkt_desc) *
|
||||
(card->tx_ring_buffers + card->rx_ring_buffers);
|
||||
|
||||
printk(KERN_INFO "pc300: PC300/%s, %u KB RAM at 0x%x, IRQ%u, "
|
||||
"using %u TX + %u RX packets rings\n",
|
||||
card->type == PC300_X21 ? "X21" :
|
||||
card->type == PC300_TE ? "TE" : "RSV",
|
||||
ramsize / 1024, ramphys, pdev->irq,
|
||||
card->tx_ring_buffers, card->rx_ring_buffers);
|
||||
|
||||
if (card->tx_ring_buffers < 1) {
|
||||
printk(KERN_ERR "pc300: RAM test failed\n");
|
||||
pc300_pci_remove_one(pdev);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Enable interrupts on the PCI bridge, LINTi1 active low */
|
||||
writew(0x0041, &card->plxbase->intr_ctrl_stat);
|
||||
|
||||
/* Allocate IRQ */
|
||||
if (request_irq(pdev->irq, sca_intr, IRQF_SHARED, devname, card)) {
|
||||
printk(KERN_WARNING "pc300: could not allocate IRQ%d.\n",
|
||||
pdev->irq);
|
||||
pc300_pci_remove_one(pdev);
|
||||
return -EBUSY;
|
||||
}
|
||||
card->irq = pdev->irq;
|
||||
|
||||
sca_init(card, 0);
|
||||
|
||||
// COTE not set - allows better TX DMA settings
|
||||
// sca_out(sca_in(PCR, card) | PCR_COTE, PCR, card);
|
||||
|
||||
sca_out(0x10, BTCR, card);
|
||||
|
||||
for (i = 0; i < card->n_ports; i++) {
|
||||
port_t *port = &card->ports[i];
|
||||
struct net_device *dev = port_to_dev(port);
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
port->phy_node = i;
|
||||
|
||||
spin_lock_init(&port->lock);
|
||||
SET_MODULE_OWNER(dev);
|
||||
dev->irq = card->irq;
|
||||
dev->mem_start = ramphys;
|
||||
dev->mem_end = ramphys + ramsize - 1;
|
||||
dev->tx_queue_len = 50;
|
||||
dev->do_ioctl = pc300_ioctl;
|
||||
dev->open = pc300_open;
|
||||
dev->stop = pc300_close;
|
||||
hdlc->attach = sca_attach;
|
||||
hdlc->xmit = sca_xmit;
|
||||
port->settings.clock_type = CLOCK_EXT;
|
||||
port->card = card;
|
||||
if (card->type == PC300_X21)
|
||||
port->iface = IF_IFACE_X21;
|
||||
else
|
||||
port->iface = IF_IFACE_V35;
|
||||
|
||||
if (register_hdlc_device(dev)) {
|
||||
printk(KERN_ERR "pc300: unable to register hdlc "
|
||||
"device\n");
|
||||
port->card = NULL;
|
||||
pc300_pci_remove_one(pdev);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
sca_init_sync_port(port); /* Set up SCA memory */
|
||||
|
||||
printk(KERN_INFO "%s: PC300 node %d\n",
|
||||
dev->name, port->phy_node);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct pci_device_id pc300_pci_tbl[] __devinitdata = {
|
||||
{ PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_PC300_RX_1, PCI_ANY_ID,
|
||||
PCI_ANY_ID, 0, 0, 0 },
|
||||
{ PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_PC300_RX_2, PCI_ANY_ID,
|
||||
PCI_ANY_ID, 0, 0, 0 },
|
||||
{ PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_PC300_TE_1, PCI_ANY_ID,
|
||||
PCI_ANY_ID, 0, 0, 0 },
|
||||
{ PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_PC300_TE_2, PCI_ANY_ID,
|
||||
PCI_ANY_ID, 0, 0, 0 },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
|
||||
static struct pci_driver pc300_pci_driver = {
|
||||
.name = "PC300",
|
||||
.id_table = pc300_pci_tbl,
|
||||
.probe = pc300_pci_init_one,
|
||||
.remove = pc300_pci_remove_one,
|
||||
};
|
||||
|
||||
|
||||
static int __init pc300_init_module(void)
|
||||
{
|
||||
#ifdef MODULE
|
||||
printk(KERN_INFO "%s\n", version);
|
||||
#endif
|
||||
if (pci_clock_freq < 1000000 || pci_clock_freq > 80000000) {
|
||||
printk(KERN_ERR "pc300: Invalid PCI clock frequency\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (use_crystal_clock != 0 && use_crystal_clock != 1) {
|
||||
printk(KERN_ERR "pc300: Invalid 'use_crystal_clock' value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
CLOCK_BASE = use_crystal_clock ? 24576000 : pci_clock_freq;
|
||||
|
||||
return pci_register_driver(&pc300_pci_driver);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void __exit pc300_cleanup_module(void)
|
||||
{
|
||||
pci_unregister_driver(&pc300_pci_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
|
||||
MODULE_DESCRIPTION("Cyclades PC300 serial port driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DEVICE_TABLE(pci, pc300_pci_tbl);
|
||||
module_param(pci_clock_freq, int, 0444);
|
||||
MODULE_PARM_DESC(pci_clock_freq, "System PCI clock frequency in Hz");
|
||||
module_param(use_crystal_clock, int, 0444);
|
||||
MODULE_PARM_DESC(use_crystal_clock,
|
||||
"Use 24.576 MHz clock instead of PCI clock");
|
||||
module_init(pc300_init_module);
|
||||
module_exit(pc300_cleanup_module);
|
||||
495
drivers/net/wan/pci200syn.c
Normal file
495
drivers/net/wan/pci200syn.c
Normal file
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
* Goramo PCI200SYN synchronous serial card driver for Linux
|
||||
*
|
||||
* Copyright (C) 2002-2003 Krzysztof Halasa <khc@pm.waw.pl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* For information see <http://www.kernel.org/pub/linux/utils/net/hdlc/>
|
||||
*
|
||||
* Sources of information:
|
||||
* Hitachi HD64572 SCA-II User's Manual
|
||||
* PLX Technology Inc. PCI9052 Data Book
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/hdlc.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "hd64572.h"
|
||||
|
||||
static const char* version = "Goramo PCI200SYN driver version: 1.16";
|
||||
static const char* devname = "PCI200SYN";
|
||||
|
||||
#undef DEBUG_PKT
|
||||
#define DEBUG_RINGS
|
||||
|
||||
#define PCI200SYN_PLX_SIZE 0x80 /* PLX control window size (128b) */
|
||||
#define PCI200SYN_SCA_SIZE 0x400 /* SCA window size (1Kb) */
|
||||
#define ALL_PAGES_ALWAYS_MAPPED
|
||||
#define NEED_DETECT_RAM
|
||||
#define NEED_SCA_MSCI_INTR
|
||||
#define MAX_TX_BUFFERS 10
|
||||
|
||||
static int pci_clock_freq = 33000000;
|
||||
#define CLOCK_BASE pci_clock_freq
|
||||
|
||||
/*
|
||||
* PLX PCI9052 local configuration and shared runtime registers.
|
||||
* This structure can be used to access 9052 registers (memory mapped).
|
||||
*/
|
||||
typedef struct {
|
||||
u32 loc_addr_range[4]; /* 00-0Ch : Local Address Ranges */
|
||||
u32 loc_rom_range; /* 10h : Local ROM Range */
|
||||
u32 loc_addr_base[4]; /* 14-20h : Local Address Base Addrs */
|
||||
u32 loc_rom_base; /* 24h : Local ROM Base */
|
||||
u32 loc_bus_descr[4]; /* 28-34h : Local Bus Descriptors */
|
||||
u32 rom_bus_descr; /* 38h : ROM Bus Descriptor */
|
||||
u32 cs_base[4]; /* 3C-48h : Chip Select Base Addrs */
|
||||
u32 intr_ctrl_stat; /* 4Ch : Interrupt Control/Status */
|
||||
u32 init_ctrl; /* 50h : EEPROM ctrl, Init Ctrl, etc */
|
||||
}plx9052;
|
||||
|
||||
|
||||
|
||||
typedef struct port_s {
|
||||
struct net_device *dev;
|
||||
struct card_s *card;
|
||||
spinlock_t lock; /* TX lock */
|
||||
sync_serial_settings settings;
|
||||
int rxpart; /* partial frame received, next frame invalid*/
|
||||
unsigned short encoding;
|
||||
unsigned short parity;
|
||||
u16 rxin; /* rx ring buffer 'in' pointer */
|
||||
u16 txin; /* tx ring buffer 'in' and 'last' pointers */
|
||||
u16 txlast;
|
||||
u8 rxs, txs, tmc; /* SCA registers */
|
||||
u8 phy_node; /* physical port # - 0 or 1 */
|
||||
}port_t;
|
||||
|
||||
|
||||
|
||||
typedef struct card_s {
|
||||
u8 __iomem *rambase; /* buffer memory base (virtual) */
|
||||
u8 __iomem *scabase; /* SCA memory base (virtual) */
|
||||
plx9052 __iomem *plxbase;/* PLX registers memory base (virtual) */
|
||||
u16 rx_ring_buffers; /* number of buffers in a ring */
|
||||
u16 tx_ring_buffers;
|
||||
u16 buff_offset; /* offset of first buffer of first channel */
|
||||
u8 irq; /* interrupt request level */
|
||||
|
||||
port_t ports[2];
|
||||
}card_t;
|
||||
|
||||
|
||||
#define sca_in(reg, card) readb(card->scabase + (reg))
|
||||
#define sca_out(value, reg, card) writeb(value, card->scabase + (reg))
|
||||
#define sca_inw(reg, card) readw(card->scabase + (reg))
|
||||
#define sca_outw(value, reg, card) writew(value, card->scabase + (reg))
|
||||
#define sca_inl(reg, card) readl(card->scabase + (reg))
|
||||
#define sca_outl(value, reg, card) writel(value, card->scabase + (reg))
|
||||
|
||||
#define port_to_card(port) (port->card)
|
||||
#define log_node(port) (port->phy_node)
|
||||
#define phy_node(port) (port->phy_node)
|
||||
#define winbase(card) (card->rambase)
|
||||
#define get_port(card, port) (&card->ports[port])
|
||||
#define sca_flush(card) (sca_in(IER0, card));
|
||||
|
||||
static inline void new_memcpy_toio(char __iomem *dest, char *src, int length)
|
||||
{
|
||||
int len;
|
||||
do {
|
||||
len = length > 256 ? 256 : length;
|
||||
memcpy_toio(dest, src, len);
|
||||
dest += len;
|
||||
src += len;
|
||||
length -= len;
|
||||
readb(dest);
|
||||
} while (len);
|
||||
}
|
||||
|
||||
#undef memcpy_toio
|
||||
#define memcpy_toio new_memcpy_toio
|
||||
|
||||
#include "hd6457x.c"
|
||||
|
||||
|
||||
static void pci200_set_iface(port_t *port)
|
||||
{
|
||||
card_t *card = port->card;
|
||||
u16 msci = get_msci(port);
|
||||
u8 rxs = port->rxs & CLK_BRG_MASK;
|
||||
u8 txs = port->txs & CLK_BRG_MASK;
|
||||
|
||||
sca_out(EXS_TES1, (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET) + EXS,
|
||||
port_to_card(port));
|
||||
switch(port->settings.clock_type) {
|
||||
case CLOCK_INT:
|
||||
rxs |= CLK_BRG; /* BRG output */
|
||||
txs |= CLK_PIN_OUT | CLK_TX_RXCLK; /* RX clock */
|
||||
break;
|
||||
|
||||
case CLOCK_TXINT:
|
||||
rxs |= CLK_LINE; /* RXC input */
|
||||
txs |= CLK_PIN_OUT | CLK_BRG; /* BRG output */
|
||||
break;
|
||||
|
||||
case CLOCK_TXFROMRX:
|
||||
rxs |= CLK_LINE; /* RXC input */
|
||||
txs |= CLK_PIN_OUT | CLK_TX_RXCLK; /* RX clock */
|
||||
break;
|
||||
|
||||
default: /* EXTernal clock */
|
||||
rxs |= CLK_LINE; /* RXC input */
|
||||
txs |= CLK_PIN_OUT | CLK_LINE; /* TXC input */
|
||||
break;
|
||||
}
|
||||
|
||||
port->rxs = rxs;
|
||||
port->txs = txs;
|
||||
sca_out(rxs, msci + RXS, card);
|
||||
sca_out(txs, msci + TXS, card);
|
||||
sca_set_port(port);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int pci200_open(struct net_device *dev)
|
||||
{
|
||||
port_t *port = dev_to_port(dev);
|
||||
|
||||
int result = hdlc_open(dev);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
sca_open(dev);
|
||||
pci200_set_iface(port);
|
||||
sca_flush(port_to_card(port));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int pci200_close(struct net_device *dev)
|
||||
{
|
||||
sca_close(dev);
|
||||
sca_flush(port_to_card(dev_to_port(dev)));
|
||||
hdlc_close(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int pci200_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
const size_t size = sizeof(sync_serial_settings);
|
||||
sync_serial_settings new_line;
|
||||
sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
|
||||
port_t *port = dev_to_port(dev);
|
||||
|
||||
#ifdef DEBUG_RINGS
|
||||
if (cmd == SIOCDEVPRIVATE) {
|
||||
sca_dump_rings(dev);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (cmd != SIOCWANDEV)
|
||||
return hdlc_ioctl(dev, ifr, cmd);
|
||||
|
||||
switch(ifr->ifr_settings.type) {
|
||||
case IF_GET_IFACE:
|
||||
ifr->ifr_settings.type = IF_IFACE_V35;
|
||||
if (ifr->ifr_settings.size < size) {
|
||||
ifr->ifr_settings.size = size; /* data size wanted */
|
||||
return -ENOBUFS;
|
||||
}
|
||||
if (copy_to_user(line, &port->settings, size))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case IF_IFACE_V35:
|
||||
case IF_IFACE_SYNC_SERIAL:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&new_line, line, size))
|
||||
return -EFAULT;
|
||||
|
||||
if (new_line.clock_type != CLOCK_EXT &&
|
||||
new_line.clock_type != CLOCK_TXFROMRX &&
|
||||
new_line.clock_type != CLOCK_INT &&
|
||||
new_line.clock_type != CLOCK_TXINT)
|
||||
return -EINVAL; /* No such clock setting */
|
||||
|
||||
if (new_line.loopback != 0 && new_line.loopback != 1)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&port->settings, &new_line, size); /* Update settings */
|
||||
pci200_set_iface(port);
|
||||
sca_flush(port_to_card(port));
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return hdlc_ioctl(dev, ifr, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void pci200_pci_remove_one(struct pci_dev *pdev)
|
||||
{
|
||||
int i;
|
||||
card_t *card = pci_get_drvdata(pdev);
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
if (card->ports[i].card) {
|
||||
struct net_device *dev = port_to_dev(&card->ports[i]);
|
||||
unregister_hdlc_device(dev);
|
||||
}
|
||||
|
||||
if (card->irq)
|
||||
free_irq(card->irq, card);
|
||||
|
||||
if (card->rambase)
|
||||
iounmap(card->rambase);
|
||||
if (card->scabase)
|
||||
iounmap(card->scabase);
|
||||
if (card->plxbase)
|
||||
iounmap(card->plxbase);
|
||||
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
if (card->ports[0].dev)
|
||||
free_netdev(card->ports[0].dev);
|
||||
if (card->ports[1].dev)
|
||||
free_netdev(card->ports[1].dev);
|
||||
kfree(card);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
card_t *card;
|
||||
u8 rev_id;
|
||||
u32 __iomem *p;
|
||||
int i;
|
||||
u32 ramsize;
|
||||
u32 ramphys; /* buffer memory base */
|
||||
u32 scaphys; /* SCA memory base */
|
||||
u32 plxphys; /* PLX registers memory base */
|
||||
|
||||
#ifndef MODULE
|
||||
static int printed_version;
|
||||
if (!printed_version++)
|
||||
printk(KERN_INFO "%s\n", version);
|
||||
#endif
|
||||
|
||||
i = pci_enable_device(pdev);
|
||||
if (i)
|
||||
return i;
|
||||
|
||||
i = pci_request_regions(pdev, "PCI200SYN");
|
||||
if (i) {
|
||||
pci_disable_device(pdev);
|
||||
return i;
|
||||
}
|
||||
|
||||
card = kmalloc(sizeof(card_t), GFP_KERNEL);
|
||||
if (card == NULL) {
|
||||
printk(KERN_ERR "pci200syn: unable to allocate memory\n");
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
memset(card, 0, sizeof(card_t));
|
||||
pci_set_drvdata(pdev, card);
|
||||
card->ports[0].dev = alloc_hdlcdev(&card->ports[0]);
|
||||
card->ports[1].dev = alloc_hdlcdev(&card->ports[1]);
|
||||
if (!card->ports[0].dev || !card->ports[1].dev) {
|
||||
printk(KERN_ERR "pci200syn: unable to allocate memory\n");
|
||||
pci200_pci_remove_one(pdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id);
|
||||
if (pci_resource_len(pdev, 0) != PCI200SYN_PLX_SIZE ||
|
||||
pci_resource_len(pdev, 2) != PCI200SYN_SCA_SIZE ||
|
||||
pci_resource_len(pdev, 3) < 16384) {
|
||||
printk(KERN_ERR "pci200syn: invalid card EEPROM parameters\n");
|
||||
pci200_pci_remove_one(pdev);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
plxphys = pci_resource_start(pdev,0) & PCI_BASE_ADDRESS_MEM_MASK;
|
||||
card->plxbase = ioremap(plxphys, PCI200SYN_PLX_SIZE);
|
||||
|
||||
scaphys = pci_resource_start(pdev,2) & PCI_BASE_ADDRESS_MEM_MASK;
|
||||
card->scabase = ioremap(scaphys, PCI200SYN_SCA_SIZE);
|
||||
|
||||
ramphys = pci_resource_start(pdev,3) & PCI_BASE_ADDRESS_MEM_MASK;
|
||||
card->rambase = ioremap(ramphys, pci_resource_len(pdev,3));
|
||||
|
||||
if (card->plxbase == NULL ||
|
||||
card->scabase == NULL ||
|
||||
card->rambase == NULL) {
|
||||
printk(KERN_ERR "pci200syn: ioremap() failed\n");
|
||||
pci200_pci_remove_one(pdev);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Reset PLX */
|
||||
p = &card->plxbase->init_ctrl;
|
||||
writel(readl(p) | 0x40000000, p);
|
||||
readl(p); /* Flush the write - do not use sca_flush */
|
||||
udelay(1);
|
||||
|
||||
writel(readl(p) & ~0x40000000, p);
|
||||
readl(p); /* Flush the write - do not use sca_flush */
|
||||
udelay(1);
|
||||
|
||||
ramsize = sca_detect_ram(card, card->rambase,
|
||||
pci_resource_len(pdev, 3));
|
||||
|
||||
/* number of TX + RX buffers for one port - this is dual port card */
|
||||
i = ramsize / (2 * (sizeof(pkt_desc) + HDLC_MAX_MRU));
|
||||
card->tx_ring_buffers = min(i / 2, MAX_TX_BUFFERS);
|
||||
card->rx_ring_buffers = i - card->tx_ring_buffers;
|
||||
|
||||
card->buff_offset = 2 * sizeof(pkt_desc) * (card->tx_ring_buffers +
|
||||
card->rx_ring_buffers);
|
||||
|
||||
printk(KERN_INFO "pci200syn: %u KB RAM at 0x%x, IRQ%u, using %u TX +"
|
||||
" %u RX packets rings\n", ramsize / 1024, ramphys,
|
||||
pdev->irq, card->tx_ring_buffers, card->rx_ring_buffers);
|
||||
|
||||
if (pdev->subsystem_device == PCI_DEVICE_ID_PLX_9050) {
|
||||
printk(KERN_ERR "Detected PCI200SYN card with old "
|
||||
"configuration data.\n");
|
||||
printk(KERN_ERR "See <http://www.kernel.org/pub/"
|
||||
"linux/utils/net/hdlc/pci200syn/> for update.\n");
|
||||
printk(KERN_ERR "The card will stop working with"
|
||||
" future versions of Linux if not updated.\n");
|
||||
}
|
||||
|
||||
if (card->tx_ring_buffers < 1) {
|
||||
printk(KERN_ERR "pci200syn: RAM test failed\n");
|
||||
pci200_pci_remove_one(pdev);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Enable interrupts on the PCI bridge */
|
||||
p = &card->plxbase->intr_ctrl_stat;
|
||||
writew(readw(p) | 0x0040, p);
|
||||
|
||||
/* Allocate IRQ */
|
||||
if (request_irq(pdev->irq, sca_intr, IRQF_SHARED, devname, card)) {
|
||||
printk(KERN_WARNING "pci200syn: could not allocate IRQ%d.\n",
|
||||
pdev->irq);
|
||||
pci200_pci_remove_one(pdev);
|
||||
return -EBUSY;
|
||||
}
|
||||
card->irq = pdev->irq;
|
||||
|
||||
sca_init(card, 0);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
port_t *port = &card->ports[i];
|
||||
struct net_device *dev = port_to_dev(port);
|
||||
hdlc_device *hdlc = dev_to_hdlc(dev);
|
||||
port->phy_node = i;
|
||||
|
||||
spin_lock_init(&port->lock);
|
||||
SET_MODULE_OWNER(dev);
|
||||
dev->irq = card->irq;
|
||||
dev->mem_start = ramphys;
|
||||
dev->mem_end = ramphys + ramsize - 1;
|
||||
dev->tx_queue_len = 50;
|
||||
dev->do_ioctl = pci200_ioctl;
|
||||
dev->open = pci200_open;
|
||||
dev->stop = pci200_close;
|
||||
hdlc->attach = sca_attach;
|
||||
hdlc->xmit = sca_xmit;
|
||||
port->settings.clock_type = CLOCK_EXT;
|
||||
port->card = card;
|
||||
if (register_hdlc_device(dev)) {
|
||||
printk(KERN_ERR "pci200syn: unable to register hdlc "
|
||||
"device\n");
|
||||
port->card = NULL;
|
||||
pci200_pci_remove_one(pdev);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
sca_init_sync_port(port); /* Set up SCA memory */
|
||||
|
||||
printk(KERN_INFO "%s: PCI200SYN node %d\n",
|
||||
dev->name, port->phy_node);
|
||||
}
|
||||
|
||||
sca_flush(card);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct pci_device_id pci200_pci_tbl[] __devinitdata = {
|
||||
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX,
|
||||
PCI_DEVICE_ID_PLX_9050, 0, 0, 0 },
|
||||
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX,
|
||||
PCI_DEVICE_ID_PLX_PCI200SYN, 0, 0, 0 },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
|
||||
static struct pci_driver pci200_pci_driver = {
|
||||
.name = "PCI200SYN",
|
||||
.id_table = pci200_pci_tbl,
|
||||
.probe = pci200_pci_init_one,
|
||||
.remove = pci200_pci_remove_one,
|
||||
};
|
||||
|
||||
|
||||
static int __init pci200_init_module(void)
|
||||
{
|
||||
#ifdef MODULE
|
||||
printk(KERN_INFO "%s\n", version);
|
||||
#endif
|
||||
if (pci_clock_freq < 1000000 || pci_clock_freq > 80000000) {
|
||||
printk(KERN_ERR "pci200syn: Invalid PCI clock frequency\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return pci_register_driver(&pci200_pci_driver);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void __exit pci200_cleanup_module(void)
|
||||
{
|
||||
pci_unregister_driver(&pci200_pci_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
|
||||
MODULE_DESCRIPTION("Goramo PCI200SYN serial port driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DEVICE_TABLE(pci, pci200_pci_tbl);
|
||||
module_param(pci_clock_freq, int, 0444);
|
||||
MODULE_PARM_DESC(pci_clock_freq, "System PCI clock frequency in Hz");
|
||||
module_init(pci200_init_module);
|
||||
module_exit(pci200_cleanup_module);
|
||||
1733
drivers/net/wan/sbni.c
Normal file
1733
drivers/net/wan/sbni.c
Normal file
File diff suppressed because it is too large
Load Diff
141
drivers/net/wan/sbni.h
Normal file
141
drivers/net/wan/sbni.h
Normal file
@@ -0,0 +1,141 @@
|
||||
/* sbni.h: definitions for a Granch SBNI12 driver, version 5.0.0
|
||||
* Written 2001 Denis I.Timofeev (timofeev@granch.ru)
|
||||
* This file is distributed under the GNU GPL
|
||||
*/
|
||||
|
||||
#ifndef SBNI_H
|
||||
#define SBNI_H
|
||||
|
||||
#ifdef SBNI_DEBUG
|
||||
#define DP( A ) A
|
||||
#else
|
||||
#define DP( A )
|
||||
#endif
|
||||
|
||||
|
||||
/* We don't have official vendor id yet... */
|
||||
#define SBNI_PCI_VENDOR 0x55
|
||||
#define SBNI_PCI_DEVICE 0x9f
|
||||
|
||||
#define ISA_MODE 0x00
|
||||
#define PCI_MODE 0x01
|
||||
|
||||
#define SBNI_IO_EXTENT 4
|
||||
|
||||
enum sbni_reg {
|
||||
CSR0 = 0,
|
||||
CSR1 = 1,
|
||||
DAT = 2
|
||||
};
|
||||
|
||||
/* CSR0 mapping */
|
||||
enum {
|
||||
BU_EMP = 0x02,
|
||||
RC_CHK = 0x04,
|
||||
CT_ZER = 0x08,
|
||||
TR_REQ = 0x10,
|
||||
TR_RDY = 0x20,
|
||||
EN_INT = 0x40,
|
||||
RC_RDY = 0x80
|
||||
};
|
||||
|
||||
|
||||
/* CSR1 mapping */
|
||||
#define PR_RES 0x80
|
||||
|
||||
struct sbni_csr1 {
|
||||
unsigned rxl : 5;
|
||||
unsigned rate : 2;
|
||||
unsigned : 1;
|
||||
};
|
||||
|
||||
/* fields in frame header */
|
||||
#define FRAME_ACK_MASK (unsigned short)0x7000
|
||||
#define FRAME_LEN_MASK (unsigned short)0x03FF
|
||||
#define FRAME_FIRST (unsigned short)0x8000
|
||||
#define FRAME_RETRY (unsigned short)0x0800
|
||||
|
||||
#define FRAME_SENT_BAD (unsigned short)0x4000
|
||||
#define FRAME_SENT_OK (unsigned short)0x3000
|
||||
|
||||
|
||||
/* state flags */
|
||||
enum {
|
||||
FL_WAIT_ACK = 0x01,
|
||||
FL_NEED_RESEND = 0x02,
|
||||
FL_PREV_OK = 0x04,
|
||||
FL_SLOW_MODE = 0x08,
|
||||
FL_SECONDARY = 0x10,
|
||||
#ifdef CONFIG_SBNI_MULTILINE
|
||||
FL_SLAVE = 0x20,
|
||||
#endif
|
||||
FL_LINE_DOWN = 0x40
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
DEFAULT_IOBASEADDR = 0x210,
|
||||
DEFAULT_INTERRUPTNUMBER = 5,
|
||||
DEFAULT_RATE = 0,
|
||||
DEFAULT_FRAME_LEN = 1012
|
||||
};
|
||||
|
||||
#define DEF_RXL_DELTA -1
|
||||
#define DEF_RXL 0xf
|
||||
|
||||
#define SBNI_SIG 0x5a
|
||||
|
||||
#define SBNI_MIN_LEN 60 /* Shortest Ethernet frame without FCS */
|
||||
#define SBNI_MAX_FRAME 1023
|
||||
#define ETHER_MAX_LEN 1518
|
||||
|
||||
#define SBNI_TIMEOUT (HZ/10)
|
||||
|
||||
#define TR_ERROR_COUNT 32
|
||||
#define CHANGE_LEVEL_START_TICKS 4
|
||||
|
||||
#define SBNI_MAX_NUM_CARDS 16
|
||||
|
||||
/* internal SBNI-specific statistics */
|
||||
struct sbni_in_stats {
|
||||
u32 all_rx_number;
|
||||
u32 bad_rx_number;
|
||||
u32 timeout_number;
|
||||
u32 all_tx_number;
|
||||
u32 resend_tx_number;
|
||||
};
|
||||
|
||||
/* SBNI ioctl params */
|
||||
#define SIOCDEVGETINSTATS SIOCDEVPRIVATE
|
||||
#define SIOCDEVRESINSTATS SIOCDEVPRIVATE+1
|
||||
#define SIOCDEVGHWSTATE SIOCDEVPRIVATE+2
|
||||
#define SIOCDEVSHWSTATE SIOCDEVPRIVATE+3
|
||||
#define SIOCDEVENSLAVE SIOCDEVPRIVATE+4
|
||||
#define SIOCDEVEMANSIPATE SIOCDEVPRIVATE+5
|
||||
|
||||
|
||||
/* data packet for SIOCDEVGHWSTATE/SIOCDEVSHWSTATE ioctl requests */
|
||||
struct sbni_flags {
|
||||
u32 rxl : 4;
|
||||
u32 rate : 2;
|
||||
u32 fixed_rxl : 1;
|
||||
u32 slow_mode : 1;
|
||||
u32 mac_addr : 24;
|
||||
};
|
||||
|
||||
/*
|
||||
* CRC-32 stuff
|
||||
*/
|
||||
#define CRC32(c,crc) (crc32tab[((size_t)(crc) ^ (c)) & 0xff] ^ (((crc) >> 8) & 0x00FFFFFF))
|
||||
/* CRC generator 0xEDB88320 */
|
||||
/* CRC remainder 0x2144DF1C */
|
||||
/* CRC initial value 0x00000000 */
|
||||
#define CRC32_REMAINDER 0x2144DF1C
|
||||
#define CRC32_INITIAL 0x00000000
|
||||
|
||||
#ifndef __initdata
|
||||
#define __initdata
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
1669
drivers/net/wan/sdla.c
Normal file
1669
drivers/net/wan/sdla.c
Normal file
File diff suppressed because it is too large
Load Diff
470
drivers/net/wan/sealevel.c
Normal file
470
drivers/net/wan/sealevel.c
Normal file
@@ -0,0 +1,470 @@
|
||||
/*
|
||||
* Sealevel Systems 4021 driver.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* (c) Copyright 1999, 2001 Alan Cox
|
||||
* (c) Copyright 2001 Red Hat Inc.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/arp.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <net/syncppp.h>
|
||||
#include "z85230.h"
|
||||
|
||||
|
||||
struct slvl_device
|
||||
{
|
||||
void *if_ptr; /* General purpose pointer (used by SPPP) */
|
||||
struct z8530_channel *chan;
|
||||
struct ppp_device pppdev;
|
||||
int channel;
|
||||
};
|
||||
|
||||
|
||||
struct slvl_board
|
||||
{
|
||||
struct slvl_device *dev[2];
|
||||
struct z8530_dev board;
|
||||
int iobase;
|
||||
};
|
||||
|
||||
/*
|
||||
* Network driver support routines
|
||||
*/
|
||||
|
||||
/*
|
||||
* Frame receive. Simple for our card as we do sync ppp and there
|
||||
* is no funny garbage involved
|
||||
*/
|
||||
|
||||
static void sealevel_input(struct z8530_channel *c, struct sk_buff *skb)
|
||||
{
|
||||
/* Drop the CRC - it's not a good idea to try and negotiate it ;) */
|
||||
skb_trim(skb, skb->len-2);
|
||||
skb->protocol=htons(ETH_P_WAN_PPP);
|
||||
skb->mac.raw=skb->data;
|
||||
skb->dev=c->netdevice;
|
||||
/*
|
||||
* Send it to the PPP layer. We don't have time to process
|
||||
* it right now.
|
||||
*/
|
||||
netif_rx(skb);
|
||||
c->netdevice->last_rx = jiffies;
|
||||
}
|
||||
|
||||
/*
|
||||
* We've been placed in the UP state
|
||||
*/
|
||||
|
||||
static int sealevel_open(struct net_device *d)
|
||||
{
|
||||
struct slvl_device *slvl=d->priv;
|
||||
int err = -1;
|
||||
int unit = slvl->channel;
|
||||
|
||||
/*
|
||||
* Link layer up.
|
||||
*/
|
||||
|
||||
switch(unit)
|
||||
{
|
||||
case 0:
|
||||
err=z8530_sync_dma_open(d, slvl->chan);
|
||||
break;
|
||||
case 1:
|
||||
err=z8530_sync_open(d, slvl->chan);
|
||||
break;
|
||||
}
|
||||
|
||||
if(err)
|
||||
return err;
|
||||
/*
|
||||
* Begin PPP
|
||||
*/
|
||||
err=sppp_open(d);
|
||||
if(err)
|
||||
{
|
||||
switch(unit)
|
||||
{
|
||||
case 0:
|
||||
z8530_sync_dma_close(d, slvl->chan);
|
||||
break;
|
||||
case 1:
|
||||
z8530_sync_close(d, slvl->chan);
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
slvl->chan->rx_function=sealevel_input;
|
||||
|
||||
/*
|
||||
* Go go go
|
||||
*/
|
||||
netif_start_queue(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sealevel_close(struct net_device *d)
|
||||
{
|
||||
struct slvl_device *slvl=d->priv;
|
||||
int unit = slvl->channel;
|
||||
|
||||
/*
|
||||
* Discard new frames
|
||||
*/
|
||||
|
||||
slvl->chan->rx_function=z8530_null_rx;
|
||||
|
||||
/*
|
||||
* PPP off
|
||||
*/
|
||||
sppp_close(d);
|
||||
/*
|
||||
* Link layer down
|
||||
*/
|
||||
|
||||
netif_stop_queue(d);
|
||||
|
||||
switch(unit)
|
||||
{
|
||||
case 0:
|
||||
z8530_sync_dma_close(d, slvl->chan);
|
||||
break;
|
||||
case 1:
|
||||
z8530_sync_close(d, slvl->chan);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sealevel_ioctl(struct net_device *d, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
/* struct slvl_device *slvl=d->priv;
|
||||
z8530_ioctl(d,&slvl->sync.chanA,ifr,cmd) */
|
||||
return sppp_do_ioctl(d, ifr,cmd);
|
||||
}
|
||||
|
||||
static struct net_device_stats *sealevel_get_stats(struct net_device *d)
|
||||
{
|
||||
struct slvl_device *slvl=d->priv;
|
||||
if(slvl)
|
||||
return z8530_get_stats(slvl->chan);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Passed PPP frames, fire them downwind.
|
||||
*/
|
||||
|
||||
static int sealevel_queue_xmit(struct sk_buff *skb, struct net_device *d)
|
||||
{
|
||||
struct slvl_device *slvl=d->priv;
|
||||
return z8530_queue_xmit(slvl->chan, skb);
|
||||
}
|
||||
|
||||
static int sealevel_neigh_setup(struct neighbour *n)
|
||||
{
|
||||
if (n->nud_state == NUD_NONE) {
|
||||
n->ops = &arp_broken_ops;
|
||||
n->output = n->ops->output;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sealevel_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
|
||||
{
|
||||
if (p->tbl->family == AF_INET) {
|
||||
p->neigh_setup = sealevel_neigh_setup;
|
||||
p->ucast_probes = 0;
|
||||
p->mcast_probes = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sealevel_attach(struct net_device *dev)
|
||||
{
|
||||
struct slvl_device *sv = dev->priv;
|
||||
sppp_attach(&sv->pppdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sealevel_detach(struct net_device *dev)
|
||||
{
|
||||
sppp_detach(dev);
|
||||
}
|
||||
|
||||
static void slvl_setup(struct net_device *d)
|
||||
{
|
||||
d->open = sealevel_open;
|
||||
d->stop = sealevel_close;
|
||||
d->init = sealevel_attach;
|
||||
d->uninit = sealevel_detach;
|
||||
d->hard_start_xmit = sealevel_queue_xmit;
|
||||
d->get_stats = sealevel_get_stats;
|
||||
d->set_multicast_list = NULL;
|
||||
d->do_ioctl = sealevel_ioctl;
|
||||
d->neigh_setup = sealevel_neigh_setup_dev;
|
||||
d->set_mac_address = NULL;
|
||||
|
||||
}
|
||||
|
||||
static inline struct slvl_device *slvl_alloc(int iobase, int irq)
|
||||
{
|
||||
struct net_device *d;
|
||||
struct slvl_device *sv;
|
||||
|
||||
d = alloc_netdev(sizeof(struct slvl_device), "hdlc%d",
|
||||
slvl_setup);
|
||||
|
||||
if (!d)
|
||||
return NULL;
|
||||
|
||||
sv = d->priv;
|
||||
sv->if_ptr = &sv->pppdev;
|
||||
sv->pppdev.dev = d;
|
||||
d->base_addr = iobase;
|
||||
d->irq = irq;
|
||||
|
||||
return sv;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Allocate and setup Sealevel board.
|
||||
*/
|
||||
|
||||
static __init struct slvl_board *slvl_init(int iobase, int irq,
|
||||
int txdma, int rxdma, int slow)
|
||||
{
|
||||
struct z8530_dev *dev;
|
||||
struct slvl_board *b;
|
||||
|
||||
/*
|
||||
* Get the needed I/O space
|
||||
*/
|
||||
|
||||
if(!request_region(iobase, 8, "Sealevel 4021"))
|
||||
{
|
||||
printk(KERN_WARNING "sealevel: I/O 0x%X already in use.\n", iobase);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b = kmalloc(sizeof(struct slvl_board), GFP_KERNEL);
|
||||
if(!b)
|
||||
goto fail3;
|
||||
|
||||
memset(b, 0, sizeof(*b));
|
||||
if (!(b->dev[0]= slvl_alloc(iobase, irq)))
|
||||
goto fail2;
|
||||
|
||||
b->dev[0]->chan = &b->board.chanA;
|
||||
b->dev[0]->channel = 0;
|
||||
|
||||
if (!(b->dev[1] = slvl_alloc(iobase, irq)))
|
||||
goto fail1_0;
|
||||
|
||||
b->dev[1]->chan = &b->board.chanB;
|
||||
b->dev[1]->channel = 1;
|
||||
|
||||
dev = &b->board;
|
||||
|
||||
/*
|
||||
* Stuff in the I/O addressing
|
||||
*/
|
||||
|
||||
dev->active = 0;
|
||||
|
||||
b->iobase = iobase;
|
||||
|
||||
/*
|
||||
* Select 8530 delays for the old board
|
||||
*/
|
||||
|
||||
if(slow)
|
||||
iobase |= Z8530_PORT_SLEEP;
|
||||
|
||||
dev->chanA.ctrlio=iobase+1;
|
||||
dev->chanA.dataio=iobase;
|
||||
dev->chanB.ctrlio=iobase+3;
|
||||
dev->chanB.dataio=iobase+2;
|
||||
|
||||
dev->chanA.irqs=&z8530_nop;
|
||||
dev->chanB.irqs=&z8530_nop;
|
||||
|
||||
/*
|
||||
* Assert DTR enable DMA
|
||||
*/
|
||||
|
||||
outb(3|(1<<7), b->iobase+4);
|
||||
|
||||
|
||||
/* We want a fast IRQ for this device. Actually we'd like an even faster
|
||||
IRQ ;) - This is one driver RtLinux is made for */
|
||||
|
||||
if(request_irq(irq, &z8530_interrupt, IRQF_DISABLED, "SeaLevel", dev)<0)
|
||||
{
|
||||
printk(KERN_WARNING "sealevel: IRQ %d already in use.\n", irq);
|
||||
goto fail1_1;
|
||||
}
|
||||
|
||||
dev->irq=irq;
|
||||
dev->chanA.private=&b->dev[0];
|
||||
dev->chanB.private=&b->dev[1];
|
||||
dev->chanA.netdevice=b->dev[0]->pppdev.dev;
|
||||
dev->chanB.netdevice=b->dev[1]->pppdev.dev;
|
||||
dev->chanA.dev=dev;
|
||||
dev->chanB.dev=dev;
|
||||
|
||||
dev->chanA.txdma=3;
|
||||
dev->chanA.rxdma=1;
|
||||
if(request_dma(dev->chanA.txdma, "SeaLevel (TX)")!=0)
|
||||
goto fail;
|
||||
|
||||
if(request_dma(dev->chanA.rxdma, "SeaLevel (RX)")!=0)
|
||||
goto dmafail;
|
||||
|
||||
disable_irq(irq);
|
||||
|
||||
/*
|
||||
* Begin normal initialise
|
||||
*/
|
||||
|
||||
if(z8530_init(dev)!=0)
|
||||
{
|
||||
printk(KERN_ERR "Z8530 series device not found.\n");
|
||||
enable_irq(irq);
|
||||
goto dmafail2;
|
||||
}
|
||||
if(dev->type==Z85C30)
|
||||
{
|
||||
z8530_channel_load(&dev->chanA, z8530_hdlc_kilostream);
|
||||
z8530_channel_load(&dev->chanB, z8530_hdlc_kilostream);
|
||||
}
|
||||
else
|
||||
{
|
||||
z8530_channel_load(&dev->chanA, z8530_hdlc_kilostream_85230);
|
||||
z8530_channel_load(&dev->chanB, z8530_hdlc_kilostream_85230);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we can take the IRQ
|
||||
*/
|
||||
|
||||
enable_irq(irq);
|
||||
|
||||
if (register_netdev(b->dev[0]->pppdev.dev))
|
||||
goto dmafail2;
|
||||
|
||||
if (register_netdev(b->dev[1]->pppdev.dev))
|
||||
goto fail_unit;
|
||||
|
||||
z8530_describe(dev, "I/O", iobase);
|
||||
dev->active=1;
|
||||
return b;
|
||||
|
||||
fail_unit:
|
||||
unregister_netdev(b->dev[0]->pppdev.dev);
|
||||
|
||||
dmafail2:
|
||||
free_dma(dev->chanA.rxdma);
|
||||
dmafail:
|
||||
free_dma(dev->chanA.txdma);
|
||||
fail:
|
||||
free_irq(irq, dev);
|
||||
fail1_1:
|
||||
free_netdev(b->dev[1]->pppdev.dev);
|
||||
fail1_0:
|
||||
free_netdev(b->dev[0]->pppdev.dev);
|
||||
fail2:
|
||||
kfree(b);
|
||||
fail3:
|
||||
release_region(iobase,8);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void __exit slvl_shutdown(struct slvl_board *b)
|
||||
{
|
||||
int u;
|
||||
|
||||
z8530_shutdown(&b->board);
|
||||
|
||||
for(u=0; u<2; u++)
|
||||
{
|
||||
struct net_device *d = b->dev[u]->pppdev.dev;
|
||||
unregister_netdev(d);
|
||||
free_netdev(d);
|
||||
}
|
||||
|
||||
free_irq(b->board.irq, &b->board);
|
||||
free_dma(b->board.chanA.rxdma);
|
||||
free_dma(b->board.chanA.txdma);
|
||||
/* DMA off on the card, drop DTR */
|
||||
outb(0, b->iobase);
|
||||
release_region(b->iobase, 8);
|
||||
kfree(b);
|
||||
}
|
||||
|
||||
|
||||
static int io=0x238;
|
||||
static int txdma=1;
|
||||
static int rxdma=3;
|
||||
static int irq=5;
|
||||
static int slow=0;
|
||||
|
||||
module_param(io, int, 0);
|
||||
MODULE_PARM_DESC(io, "The I/O base of the Sealevel card");
|
||||
module_param(txdma, int, 0);
|
||||
MODULE_PARM_DESC(txdma, "Transmit DMA channel");
|
||||
module_param(rxdma, int, 0);
|
||||
MODULE_PARM_DESC(rxdma, "Receive DMA channel");
|
||||
module_param(irq, int, 0);
|
||||
MODULE_PARM_DESC(irq, "The interrupt line setting for the SeaLevel card");
|
||||
module_param(slow, bool, 0);
|
||||
MODULE_PARM_DESC(slow, "Set this for an older Sealevel card such as the 4012");
|
||||
|
||||
MODULE_AUTHOR("Alan Cox");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Modular driver for the SeaLevel 4021");
|
||||
|
||||
static struct slvl_board *slvl_unit;
|
||||
|
||||
static int __init slvl_init_module(void)
|
||||
{
|
||||
#ifdef MODULE
|
||||
printk(KERN_INFO "SeaLevel Z85230 Synchronous Driver v 0.02.\n");
|
||||
printk(KERN_INFO "(c) Copyright 1998, Building Number Three Ltd.\n");
|
||||
#endif
|
||||
slvl_unit = slvl_init(io, irq, txdma, rxdma, slow);
|
||||
|
||||
return slvl_unit ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
static void __exit slvl_cleanup_module(void)
|
||||
{
|
||||
if(slvl_unit)
|
||||
slvl_shutdown(slvl_unit);
|
||||
}
|
||||
|
||||
module_init(slvl_init_module);
|
||||
module_exit(slvl_cleanup_module);
|
||||
1484
drivers/net/wan/syncppp.c
Normal file
1484
drivers/net/wan/syncppp.c
Normal file
File diff suppressed because it is too large
Load Diff
855
drivers/net/wan/wanxl.c
Normal file
855
drivers/net/wan/wanxl.c
Normal file
@@ -0,0 +1,855 @@
|
||||
/*
|
||||
* wanXL serial card driver for Linux
|
||||
* host part
|
||||
*
|
||||
* Copyright (C) 2003 Krzysztof Halasa <khc@pm.waw.pl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* Status:
|
||||
* - Only DTE (external clock) support with NRZ and NRZI encodings
|
||||
* - wanXL100 will require minor driver modifications, no access to hw
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/hdlc.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "wanxl.h"
|
||||
|
||||
static const char* version = "wanXL serial card driver version: 0.48";
|
||||
|
||||
#define PLX_CTL_RESET 0x40000000 /* adapter reset */
|
||||
|
||||
#undef DEBUG_PKT
|
||||
#undef DEBUG_PCI
|
||||
|
||||
/* MAILBOX #1 - PUTS COMMANDS */
|
||||
#define MBX1_CMD_ABORTJ 0x85000000 /* Abort and Jump */
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
#define MBX1_CMD_BSWAP 0x8C000001 /* little-endian Byte Swap Mode */
|
||||
#else
|
||||
#define MBX1_CMD_BSWAP 0x8C000000 /* big-endian Byte Swap Mode */
|
||||
#endif
|
||||
|
||||
/* MAILBOX #2 - DRAM SIZE */
|
||||
#define MBX2_MEMSZ_MASK 0xFFFF0000 /* PUTS Memory Size Register mask */
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct net_device *dev;
|
||||
struct card_t *card;
|
||||
spinlock_t lock; /* for wanxl_xmit */
|
||||
int node; /* physical port #0 - 3 */
|
||||
unsigned int clock_type;
|
||||
int tx_in, tx_out;
|
||||
struct sk_buff *tx_skbs[TX_BUFFERS];
|
||||
}port_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
desc_t rx_descs[RX_QUEUE_LENGTH];
|
||||
port_status_t port_status[4];
|
||||
}card_status_t;
|
||||
|
||||
|
||||
typedef struct card_t {
|
||||
int n_ports; /* 1, 2 or 4 ports */
|
||||
u8 irq;
|
||||
|
||||
u8 __iomem *plx; /* PLX PCI9060 virtual base address */
|
||||
struct pci_dev *pdev; /* for pci_name(pdev) */
|
||||
int rx_in;
|
||||
struct sk_buff *rx_skbs[RX_QUEUE_LENGTH];
|
||||
card_status_t *status; /* shared between host and card */
|
||||
dma_addr_t status_address;
|
||||
port_t ports[0]; /* 1 - 4 port_t structures follow */
|
||||
}card_t;
|
||||
|
||||
|
||||
|
||||
static inline port_t* dev_to_port(struct net_device *dev)
|
||||
{
|
||||
return (port_t *)dev_to_hdlc(dev)->priv;
|
||||
}
|
||||
|
||||
|
||||
static inline port_status_t* get_status(port_t *port)
|
||||
{
|
||||
return &port->card->status->port_status[port->node];
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG_PCI
|
||||
static inline dma_addr_t pci_map_single_debug(struct pci_dev *pdev, void *ptr,
|
||||
size_t size, int direction)
|
||||
{
|
||||
dma_addr_t addr = pci_map_single(pdev, ptr, size, direction);
|
||||
if (addr + size > 0x100000000LL)
|
||||
printk(KERN_CRIT "wanXL %s: pci_map_single() returned memory"
|
||||
" at 0x%LX!\n", pci_name(pdev),
|
||||
(unsigned long long)addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
#undef pci_map_single
|
||||
#define pci_map_single pci_map_single_debug
|
||||
#endif
|
||||
|
||||
|
||||
/* Cable and/or personality module change interrupt service */
|
||||
static inline void wanxl_cable_intr(port_t *port)
|
||||
{
|
||||
u32 value = get_status(port)->cable;
|
||||
int valid = 1;
|
||||
const char *cable, *pm, *dte = "", *dsr = "", *dcd = "";
|
||||
|
||||
switch(value & 0x7) {
|
||||
case STATUS_CABLE_V35: cable = "V.35"; break;
|
||||
case STATUS_CABLE_X21: cable = "X.21"; break;
|
||||
case STATUS_CABLE_V24: cable = "V.24"; break;
|
||||
case STATUS_CABLE_EIA530: cable = "EIA530"; break;
|
||||
case STATUS_CABLE_NONE: cable = "no"; break;
|
||||
default: cable = "invalid";
|
||||
}
|
||||
|
||||
switch((value >> STATUS_CABLE_PM_SHIFT) & 0x7) {
|
||||
case STATUS_CABLE_V35: pm = "V.35"; break;
|
||||
case STATUS_CABLE_X21: pm = "X.21"; break;
|
||||
case STATUS_CABLE_V24: pm = "V.24"; break;
|
||||
case STATUS_CABLE_EIA530: pm = "EIA530"; break;
|
||||
case STATUS_CABLE_NONE: pm = "no personality"; valid = 0; break;
|
||||
default: pm = "invalid personality"; valid = 0;
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
if ((value & 7) == ((value >> STATUS_CABLE_PM_SHIFT) & 7)) {
|
||||
dsr = (value & STATUS_CABLE_DSR) ? ", DSR ON" :
|
||||
", DSR off";
|
||||
dcd = (value & STATUS_CABLE_DCD) ? ", carrier ON" :
|
||||
", carrier off";
|
||||
}
|
||||
dte = (value & STATUS_CABLE_DCE) ? " DCE" : " DTE";
|
||||
}
|
||||
printk(KERN_INFO "%s: %s%s module, %s cable%s%s\n",
|
||||
port->dev->name, pm, dte, cable, dsr, dcd);
|
||||
|
||||
if (value & STATUS_CABLE_DCD)
|
||||
netif_carrier_on(port->dev);
|
||||
else
|
||||
netif_carrier_off(port->dev);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Transmit complete interrupt service */
|
||||
static inline void wanxl_tx_intr(port_t *port)
|
||||
{
|
||||
struct net_device *dev = port->dev;
|
||||
struct net_device_stats *stats = hdlc_stats(dev);
|
||||
while (1) {
|
||||
desc_t *desc = &get_status(port)->tx_descs[port->tx_in];
|
||||
struct sk_buff *skb = port->tx_skbs[port->tx_in];
|
||||
|
||||
switch (desc->stat) {
|
||||
case PACKET_FULL:
|
||||
case PACKET_EMPTY:
|
||||
netif_wake_queue(dev);
|
||||
return;
|
||||
|
||||
case PACKET_UNDERRUN:
|
||||
stats->tx_errors++;
|
||||
stats->tx_fifo_errors++;
|
||||
break;
|
||||
|
||||
default:
|
||||
stats->tx_packets++;
|
||||
stats->tx_bytes += skb->len;
|
||||
}
|
||||
desc->stat = PACKET_EMPTY; /* Free descriptor */
|
||||
pci_unmap_single(port->card->pdev, desc->address, skb->len,
|
||||
PCI_DMA_TODEVICE);
|
||||
dev_kfree_skb_irq(skb);
|
||||
port->tx_in = (port->tx_in + 1) % TX_BUFFERS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Receive complete interrupt service */
|
||||
static inline void wanxl_rx_intr(card_t *card)
|
||||
{
|
||||
desc_t *desc;
|
||||
while (desc = &card->status->rx_descs[card->rx_in],
|
||||
desc->stat != PACKET_EMPTY) {
|
||||
if ((desc->stat & PACKET_PORT_MASK) > card->n_ports)
|
||||
printk(KERN_CRIT "wanXL %s: received packet for"
|
||||
" nonexistent port\n", pci_name(card->pdev));
|
||||
else {
|
||||
struct sk_buff *skb = card->rx_skbs[card->rx_in];
|
||||
port_t *port = &card->ports[desc->stat &
|
||||
PACKET_PORT_MASK];
|
||||
struct net_device *dev = port->dev;
|
||||
struct net_device_stats *stats = hdlc_stats(dev);
|
||||
|
||||
if (!skb)
|
||||
stats->rx_dropped++;
|
||||
else {
|
||||
pci_unmap_single(card->pdev, desc->address,
|
||||
BUFFER_LENGTH,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
skb_put(skb, desc->length);
|
||||
|
||||
#ifdef DEBUG_PKT
|
||||
printk(KERN_DEBUG "%s RX(%i):", dev->name,
|
||||
skb->len);
|
||||
debug_frame(skb);
|
||||
#endif
|
||||
stats->rx_packets++;
|
||||
stats->rx_bytes += skb->len;
|
||||
dev->last_rx = jiffies;
|
||||
skb->protocol = hdlc_type_trans(skb, dev);
|
||||
netif_rx(skb);
|
||||
skb = NULL;
|
||||
}
|
||||
|
||||
if (!skb) {
|
||||
skb = dev_alloc_skb(BUFFER_LENGTH);
|
||||
desc->address = skb ?
|
||||
pci_map_single(card->pdev, skb->data,
|
||||
BUFFER_LENGTH,
|
||||
PCI_DMA_FROMDEVICE) : 0;
|
||||
card->rx_skbs[card->rx_in] = skb;
|
||||
}
|
||||
}
|
||||
desc->stat = PACKET_EMPTY; /* Free descriptor */
|
||||
card->rx_in = (card->rx_in + 1) % RX_QUEUE_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static irqreturn_t wanxl_intr(int irq, void* dev_id)
|
||||
{
|
||||
card_t *card = dev_id;
|
||||
int i;
|
||||
u32 stat;
|
||||
int handled = 0;
|
||||
|
||||
|
||||
while((stat = readl(card->plx + PLX_DOORBELL_FROM_CARD)) != 0) {
|
||||
handled = 1;
|
||||
writel(stat, card->plx + PLX_DOORBELL_FROM_CARD);
|
||||
|
||||
for (i = 0; i < card->n_ports; i++) {
|
||||
if (stat & (1 << (DOORBELL_FROM_CARD_TX_0 + i)))
|
||||
wanxl_tx_intr(&card->ports[i]);
|
||||
if (stat & (1 << (DOORBELL_FROM_CARD_CABLE_0 + i)))
|
||||
wanxl_cable_intr(&card->ports[i]);
|
||||
}
|
||||
if (stat & (1 << DOORBELL_FROM_CARD_RX))
|
||||
wanxl_rx_intr(card);
|
||||
}
|
||||
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int wanxl_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
port_t *port = dev_to_port(dev);
|
||||
desc_t *desc;
|
||||
|
||||
spin_lock(&port->lock);
|
||||
|
||||
desc = &get_status(port)->tx_descs[port->tx_out];
|
||||
if (desc->stat != PACKET_EMPTY) {
|
||||
/* should never happen - previous xmit should stop queue */
|
||||
#ifdef DEBUG_PKT
|
||||
printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name);
|
||||
#endif
|
||||
netif_stop_queue(dev);
|
||||
spin_unlock_irq(&port->lock);
|
||||
return 1; /* request packet to be queued */
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PKT
|
||||
printk(KERN_DEBUG "%s TX(%i):", dev->name, skb->len);
|
||||
debug_frame(skb);
|
||||
#endif
|
||||
|
||||
port->tx_skbs[port->tx_out] = skb;
|
||||
desc->address = pci_map_single(port->card->pdev, skb->data, skb->len,
|
||||
PCI_DMA_TODEVICE);
|
||||
desc->length = skb->len;
|
||||
desc->stat = PACKET_FULL;
|
||||
writel(1 << (DOORBELL_TO_CARD_TX_0 + port->node),
|
||||
port->card->plx + PLX_DOORBELL_TO_CARD);
|
||||
dev->trans_start = jiffies;
|
||||
|
||||
port->tx_out = (port->tx_out + 1) % TX_BUFFERS;
|
||||
|
||||
if (get_status(port)->tx_descs[port->tx_out].stat != PACKET_EMPTY) {
|
||||
netif_stop_queue(dev);
|
||||
#ifdef DEBUG_PKT
|
||||
printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name);
|
||||
#endif
|
||||
}
|
||||
|
||||
spin_unlock(&port->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int wanxl_attach(struct net_device *dev, unsigned short encoding,
|
||||
unsigned short parity)
|
||||
{
|
||||
port_t *port = dev_to_port(dev);
|
||||
|
||||
if (encoding != ENCODING_NRZ &&
|
||||
encoding != ENCODING_NRZI)
|
||||
return -EINVAL;
|
||||
|
||||
if (parity != PARITY_NONE &&
|
||||
parity != PARITY_CRC32_PR1_CCITT &&
|
||||
parity != PARITY_CRC16_PR1_CCITT &&
|
||||
parity != PARITY_CRC32_PR0_CCITT &&
|
||||
parity != PARITY_CRC16_PR0_CCITT)
|
||||
return -EINVAL;
|
||||
|
||||
get_status(port)->encoding = encoding;
|
||||
get_status(port)->parity = parity;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int wanxl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
const size_t size = sizeof(sync_serial_settings);
|
||||
sync_serial_settings line;
|
||||
port_t *port = dev_to_port(dev);
|
||||
|
||||
if (cmd != SIOCWANDEV)
|
||||
return hdlc_ioctl(dev, ifr, cmd);
|
||||
|
||||
switch (ifr->ifr_settings.type) {
|
||||
case IF_GET_IFACE:
|
||||
ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
|
||||
if (ifr->ifr_settings.size < size) {
|
||||
ifr->ifr_settings.size = size; /* data size wanted */
|
||||
return -ENOBUFS;
|
||||
}
|
||||
line.clock_type = get_status(port)->clocking;
|
||||
line.clock_rate = 0;
|
||||
line.loopback = 0;
|
||||
|
||||
if (copy_to_user(ifr->ifr_settings.ifs_ifsu.sync, &line, size))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case IF_IFACE_SYNC_SERIAL:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
if (dev->flags & IFF_UP)
|
||||
return -EBUSY;
|
||||
|
||||
if (copy_from_user(&line, ifr->ifr_settings.ifs_ifsu.sync,
|
||||
size))
|
||||
return -EFAULT;
|
||||
|
||||
if (line.clock_type != CLOCK_EXT &&
|
||||
line.clock_type != CLOCK_TXFROMRX)
|
||||
return -EINVAL; /* No such clock setting */
|
||||
|
||||
if (line.loopback != 0)
|
||||
return -EINVAL;
|
||||
|
||||
get_status(port)->clocking = line.clock_type;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return hdlc_ioctl(dev, ifr, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int wanxl_open(struct net_device *dev)
|
||||
{
|
||||
port_t *port = dev_to_port(dev);
|
||||
u8 __iomem *dbr = port->card->plx + PLX_DOORBELL_TO_CARD;
|
||||
unsigned long timeout;
|
||||
int i;
|
||||
|
||||
if (get_status(port)->open) {
|
||||
printk(KERN_ERR "%s: port already open\n", dev->name);
|
||||
return -EIO;
|
||||
}
|
||||
if ((i = hdlc_open(dev)) != 0)
|
||||
return i;
|
||||
|
||||
port->tx_in = port->tx_out = 0;
|
||||
for (i = 0; i < TX_BUFFERS; i++)
|
||||
get_status(port)->tx_descs[i].stat = PACKET_EMPTY;
|
||||
/* signal the card */
|
||||
writel(1 << (DOORBELL_TO_CARD_OPEN_0 + port->node), dbr);
|
||||
|
||||
timeout = jiffies + HZ;
|
||||
do
|
||||
if (get_status(port)->open) {
|
||||
netif_start_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
while (time_after(timeout, jiffies));
|
||||
|
||||
printk(KERN_ERR "%s: unable to open port\n", dev->name);
|
||||
/* ask the card to close the port, should it be still alive */
|
||||
writel(1 << (DOORBELL_TO_CARD_CLOSE_0 + port->node), dbr);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int wanxl_close(struct net_device *dev)
|
||||
{
|
||||
port_t *port = dev_to_port(dev);
|
||||
unsigned long timeout;
|
||||
int i;
|
||||
|
||||
hdlc_close(dev);
|
||||
/* signal the card */
|
||||
writel(1 << (DOORBELL_TO_CARD_CLOSE_0 + port->node),
|
||||
port->card->plx + PLX_DOORBELL_TO_CARD);
|
||||
|
||||
timeout = jiffies + HZ;
|
||||
do
|
||||
if (!get_status(port)->open)
|
||||
break;
|
||||
while (time_after(timeout, jiffies));
|
||||
|
||||
if (get_status(port)->open)
|
||||
printk(KERN_ERR "%s: unable to close port\n", dev->name);
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
for (i = 0; i < TX_BUFFERS; i++) {
|
||||
desc_t *desc = &get_status(port)->tx_descs[i];
|
||||
|
||||
if (desc->stat != PACKET_EMPTY) {
|
||||
desc->stat = PACKET_EMPTY;
|
||||
pci_unmap_single(port->card->pdev, desc->address,
|
||||
port->tx_skbs[i]->len,
|
||||
PCI_DMA_TODEVICE);
|
||||
dev_kfree_skb(port->tx_skbs[i]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct net_device_stats *wanxl_get_stats(struct net_device *dev)
|
||||
{
|
||||
struct net_device_stats *stats = hdlc_stats(dev);
|
||||
port_t *port = dev_to_port(dev);
|
||||
|
||||
stats->rx_over_errors = get_status(port)->rx_overruns;
|
||||
stats->rx_frame_errors = get_status(port)->rx_frame_errors;
|
||||
stats->rx_errors = stats->rx_over_errors + stats->rx_frame_errors;
|
||||
return stats;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int wanxl_puts_command(card_t *card, u32 cmd)
|
||||
{
|
||||
unsigned long timeout = jiffies + 5 * HZ;
|
||||
|
||||
writel(cmd, card->plx + PLX_MAILBOX_1);
|
||||
do {
|
||||
if (readl(card->plx + PLX_MAILBOX_1) == 0)
|
||||
return 0;
|
||||
|
||||
schedule();
|
||||
}while (time_after(timeout, jiffies));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void wanxl_reset(card_t *card)
|
||||
{
|
||||
u32 old_value = readl(card->plx + PLX_CONTROL) & ~PLX_CTL_RESET;
|
||||
|
||||
writel(0x80, card->plx + PLX_MAILBOX_0);
|
||||
writel(old_value | PLX_CTL_RESET, card->plx + PLX_CONTROL);
|
||||
readl(card->plx + PLX_CONTROL); /* wait for posted write */
|
||||
udelay(1);
|
||||
writel(old_value, card->plx + PLX_CONTROL);
|
||||
readl(card->plx + PLX_CONTROL); /* wait for posted write */
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void wanxl_pci_remove_one(struct pci_dev *pdev)
|
||||
{
|
||||
card_t *card = pci_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < card->n_ports; i++) {
|
||||
unregister_hdlc_device(card->ports[i].dev);
|
||||
free_netdev(card->ports[i].dev);
|
||||
}
|
||||
|
||||
/* unregister and free all host resources */
|
||||
if (card->irq)
|
||||
free_irq(card->irq, card);
|
||||
|
||||
wanxl_reset(card);
|
||||
|
||||
for (i = 0; i < RX_QUEUE_LENGTH; i++)
|
||||
if (card->rx_skbs[i]) {
|
||||
pci_unmap_single(card->pdev,
|
||||
card->status->rx_descs[i].address,
|
||||
BUFFER_LENGTH, PCI_DMA_FROMDEVICE);
|
||||
dev_kfree_skb(card->rx_skbs[i]);
|
||||
}
|
||||
|
||||
if (card->plx)
|
||||
iounmap(card->plx);
|
||||
|
||||
if (card->status)
|
||||
pci_free_consistent(pdev, sizeof(card_status_t),
|
||||
card->status, card->status_address);
|
||||
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
kfree(card);
|
||||
}
|
||||
|
||||
|
||||
#include "wanxlfw.inc"
|
||||
|
||||
static int __devinit wanxl_pci_init_one(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
card_t *card;
|
||||
u32 ramsize, stat;
|
||||
unsigned long timeout;
|
||||
u32 plx_phy; /* PLX PCI base address */
|
||||
u32 mem_phy; /* memory PCI base addr */
|
||||
u8 __iomem *mem; /* memory virtual base addr */
|
||||
int i, ports, alloc_size;
|
||||
|
||||
#ifndef MODULE
|
||||
static int printed_version;
|
||||
if (!printed_version) {
|
||||
printed_version++;
|
||||
printk(KERN_INFO "%s\n", version);
|
||||
}
|
||||
#endif
|
||||
|
||||
i = pci_enable_device(pdev);
|
||||
if (i)
|
||||
return i;
|
||||
|
||||
/* QUICC can only access first 256 MB of host RAM directly,
|
||||
but PLX9060 DMA does 32-bits for actual packet data transfers */
|
||||
|
||||
/* FIXME when PCI/DMA subsystems are fixed.
|
||||
We set both dma_mask and consistent_dma_mask to 28 bits
|
||||
and pray pci_alloc_consistent() will use this info. It should
|
||||
work on most platforms */
|
||||
if (pci_set_consistent_dma_mask(pdev, DMA_28BIT_MASK) ||
|
||||
pci_set_dma_mask(pdev, DMA_28BIT_MASK)) {
|
||||
printk(KERN_ERR "wanXL: No usable DMA configuration\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
i = pci_request_regions(pdev, "wanXL");
|
||||
if (i) {
|
||||
pci_disable_device(pdev);
|
||||
return i;
|
||||
}
|
||||
|
||||
switch (pdev->device) {
|
||||
case PCI_DEVICE_ID_SBE_WANXL100: ports = 1; break;
|
||||
case PCI_DEVICE_ID_SBE_WANXL200: ports = 2; break;
|
||||
default: ports = 4;
|
||||
}
|
||||
|
||||
alloc_size = sizeof(card_t) + ports * sizeof(port_t);
|
||||
card = kmalloc(alloc_size, GFP_KERNEL);
|
||||
if (card == NULL) {
|
||||
printk(KERN_ERR "wanXL %s: unable to allocate memory\n",
|
||||
pci_name(pdev));
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
memset(card, 0, alloc_size);
|
||||
|
||||
pci_set_drvdata(pdev, card);
|
||||
card->pdev = pdev;
|
||||
|
||||
card->status = pci_alloc_consistent(pdev, sizeof(card_status_t),
|
||||
&card->status_address);
|
||||
if (card->status == NULL) {
|
||||
wanxl_pci_remove_one(pdev);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PCI
|
||||
printk(KERN_DEBUG "wanXL %s: pci_alloc_consistent() returned memory"
|
||||
" at 0x%LX\n", pci_name(pdev),
|
||||
(unsigned long long)card->status_address);
|
||||
#endif
|
||||
|
||||
/* FIXME when PCI/DMA subsystems are fixed.
|
||||
We set both dma_mask and consistent_dma_mask back to 32 bits
|
||||
to indicate the card can do 32-bit DMA addressing */
|
||||
if (pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK) ||
|
||||
pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
|
||||
printk(KERN_ERR "wanXL: No usable DMA configuration\n");
|
||||
wanxl_pci_remove_one(pdev);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* set up PLX mapping */
|
||||
plx_phy = pci_resource_start(pdev, 0);
|
||||
|
||||
card->plx = ioremap_nocache(plx_phy, 0x70);
|
||||
if (!card->plx) {
|
||||
printk(KERN_ERR "wanxl: ioremap() failed\n");
|
||||
wanxl_pci_remove_one(pdev);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
#if RESET_WHILE_LOADING
|
||||
wanxl_reset(card);
|
||||
#endif
|
||||
|
||||
timeout = jiffies + 20 * HZ;
|
||||
while ((stat = readl(card->plx + PLX_MAILBOX_0)) != 0) {
|
||||
if (time_before(timeout, jiffies)) {
|
||||
printk(KERN_WARNING "wanXL %s: timeout waiting for"
|
||||
" PUTS to complete\n", pci_name(pdev));
|
||||
wanxl_pci_remove_one(pdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
switch(stat & 0xC0) {
|
||||
case 0x00: /* hmm - PUTS completed with non-zero code? */
|
||||
case 0x80: /* PUTS still testing the hardware */
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_WARNING "wanXL %s: PUTS test 0x%X"
|
||||
" failed\n", pci_name(pdev), stat & 0x30);
|
||||
wanxl_pci_remove_one(pdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
schedule();
|
||||
}
|
||||
|
||||
/* get on-board memory size (PUTS detects no more than 4 MB) */
|
||||
ramsize = readl(card->plx + PLX_MAILBOX_2) & MBX2_MEMSZ_MASK;
|
||||
|
||||
/* set up on-board RAM mapping */
|
||||
mem_phy = pci_resource_start(pdev, 2);
|
||||
|
||||
|
||||
/* sanity check the board's reported memory size */
|
||||
if (ramsize < BUFFERS_ADDR +
|
||||
(TX_BUFFERS + RX_BUFFERS) * BUFFER_LENGTH * ports) {
|
||||
printk(KERN_WARNING "wanXL %s: no enough on-board RAM"
|
||||
" (%u bytes detected, %u bytes required)\n",
|
||||
pci_name(pdev), ramsize, BUFFERS_ADDR +
|
||||
(TX_BUFFERS + RX_BUFFERS) * BUFFER_LENGTH * ports);
|
||||
wanxl_pci_remove_one(pdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (wanxl_puts_command(card, MBX1_CMD_BSWAP)) {
|
||||
printk(KERN_WARNING "wanXL %s: unable to Set Byte Swap"
|
||||
" Mode\n", pci_name(pdev));
|
||||
wanxl_pci_remove_one(pdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (i = 0; i < RX_QUEUE_LENGTH; i++) {
|
||||
struct sk_buff *skb = dev_alloc_skb(BUFFER_LENGTH);
|
||||
card->rx_skbs[i] = skb;
|
||||
if (skb)
|
||||
card->status->rx_descs[i].address =
|
||||
pci_map_single(card->pdev, skb->data,
|
||||
BUFFER_LENGTH,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
}
|
||||
|
||||
mem = ioremap_nocache(mem_phy, PDM_OFFSET + sizeof(firmware));
|
||||
if (!mem) {
|
||||
printk(KERN_ERR "wanxl: ioremap() failed\n");
|
||||
wanxl_pci_remove_one(pdev);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(firmware); i += 4)
|
||||
writel(htonl(*(u32*)(firmware + i)), mem + PDM_OFFSET + i);
|
||||
|
||||
for (i = 0; i < ports; i++)
|
||||
writel(card->status_address +
|
||||
(void *)&card->status->port_status[i] -
|
||||
(void *)card->status, mem + PDM_OFFSET + 4 + i * 4);
|
||||
writel(card->status_address, mem + PDM_OFFSET + 20);
|
||||
writel(PDM_OFFSET, mem);
|
||||
iounmap(mem);
|
||||
|
||||
writel(0, card->plx + PLX_MAILBOX_5);
|
||||
|
||||
if (wanxl_puts_command(card, MBX1_CMD_ABORTJ)) {
|
||||
printk(KERN_WARNING "wanXL %s: unable to Abort and Jump\n",
|
||||
pci_name(pdev));
|
||||
wanxl_pci_remove_one(pdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
stat = 0;
|
||||
timeout = jiffies + 5 * HZ;
|
||||
do {
|
||||
if ((stat = readl(card->plx + PLX_MAILBOX_5)) != 0)
|
||||
break;
|
||||
schedule();
|
||||
}while (time_after(timeout, jiffies));
|
||||
|
||||
if (!stat) {
|
||||
printk(KERN_WARNING "wanXL %s: timeout while initializing card"
|
||||
"firmware\n", pci_name(pdev));
|
||||
wanxl_pci_remove_one(pdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#if DETECT_RAM
|
||||
ramsize = stat;
|
||||
#endif
|
||||
|
||||
printk(KERN_INFO "wanXL %s: at 0x%X, %u KB of RAM at 0x%X, irq %u\n",
|
||||
pci_name(pdev), plx_phy, ramsize / 1024, mem_phy, pdev->irq);
|
||||
|
||||
/* Allocate IRQ */
|
||||
if (request_irq(pdev->irq, wanxl_intr, IRQF_SHARED, "wanXL", card)) {
|
||||
printk(KERN_WARNING "wanXL %s: could not allocate IRQ%i.\n",
|
||||
pci_name(pdev), pdev->irq);
|
||||
wanxl_pci_remove_one(pdev);
|
||||
return -EBUSY;
|
||||
}
|
||||
card->irq = pdev->irq;
|
||||
|
||||
for (i = 0; i < ports; i++) {
|
||||
hdlc_device *hdlc;
|
||||
port_t *port = &card->ports[i];
|
||||
struct net_device *dev = alloc_hdlcdev(port);
|
||||
if (!dev) {
|
||||
printk(KERN_ERR "wanXL %s: unable to allocate"
|
||||
" memory\n", pci_name(pdev));
|
||||
wanxl_pci_remove_one(pdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
port->dev = dev;
|
||||
hdlc = dev_to_hdlc(dev);
|
||||
spin_lock_init(&port->lock);
|
||||
SET_MODULE_OWNER(dev);
|
||||
dev->tx_queue_len = 50;
|
||||
dev->do_ioctl = wanxl_ioctl;
|
||||
dev->open = wanxl_open;
|
||||
dev->stop = wanxl_close;
|
||||
hdlc->attach = wanxl_attach;
|
||||
hdlc->xmit = wanxl_xmit;
|
||||
dev->get_stats = wanxl_get_stats;
|
||||
port->card = card;
|
||||
port->node = i;
|
||||
get_status(port)->clocking = CLOCK_EXT;
|
||||
if (register_hdlc_device(dev)) {
|
||||
printk(KERN_ERR "wanXL %s: unable to register hdlc"
|
||||
" device\n", pci_name(pdev));
|
||||
free_netdev(dev);
|
||||
wanxl_pci_remove_one(pdev);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
card->n_ports++;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "wanXL %s: port", pci_name(pdev));
|
||||
for (i = 0; i < ports; i++)
|
||||
printk("%s #%i: %s", i ? "," : "", i,
|
||||
card->ports[i].dev->name);
|
||||
printk("\n");
|
||||
|
||||
for (i = 0; i < ports; i++)
|
||||
wanxl_cable_intr(&card->ports[i]); /* get carrier status etc.*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pci_device_id wanxl_pci_tbl[] __devinitdata = {
|
||||
{ PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_SBE_WANXL100, PCI_ANY_ID,
|
||||
PCI_ANY_ID, 0, 0, 0 },
|
||||
{ PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_SBE_WANXL200, PCI_ANY_ID,
|
||||
PCI_ANY_ID, 0, 0, 0 },
|
||||
{ PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_SBE_WANXL400, PCI_ANY_ID,
|
||||
PCI_ANY_ID, 0, 0, 0 },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
|
||||
static struct pci_driver wanxl_pci_driver = {
|
||||
.name = "wanXL",
|
||||
.id_table = wanxl_pci_tbl,
|
||||
.probe = wanxl_pci_init_one,
|
||||
.remove = wanxl_pci_remove_one,
|
||||
};
|
||||
|
||||
|
||||
static int __init wanxl_init_module(void)
|
||||
{
|
||||
#ifdef MODULE
|
||||
printk(KERN_INFO "%s\n", version);
|
||||
#endif
|
||||
return pci_register_driver(&wanxl_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit wanxl_cleanup_module(void)
|
||||
{
|
||||
pci_unregister_driver(&wanxl_pci_driver);
|
||||
}
|
||||
|
||||
|
||||
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
|
||||
MODULE_DESCRIPTION("SBE Inc. wanXL serial port driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DEVICE_TABLE(pci, wanxl_pci_tbl);
|
||||
|
||||
module_init(wanxl_init_module);
|
||||
module_exit(wanxl_cleanup_module);
|
||||
152
drivers/net/wan/wanxl.h
Normal file
152
drivers/net/wan/wanxl.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* wanXL serial card driver for Linux
|
||||
* definitions common to host driver and card firmware
|
||||
*
|
||||
* Copyright (C) 2003 Krzysztof Halasa <khc@pm.waw.pl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define RESET_WHILE_LOADING 0
|
||||
|
||||
/* you must rebuild the firmware if any of the following is changed */
|
||||
#define DETECT_RAM 0 /* needed for > 4MB RAM, 16 MB maximum */
|
||||
#define QUICC_MEMCPY_USES_PLX 1 /* must be used if the host has > 256 MB RAM */
|
||||
|
||||
|
||||
#define STATUS_CABLE_V35 2
|
||||
#define STATUS_CABLE_X21 3
|
||||
#define STATUS_CABLE_V24 4
|
||||
#define STATUS_CABLE_EIA530 5
|
||||
#define STATUS_CABLE_INVALID 6
|
||||
#define STATUS_CABLE_NONE 7
|
||||
|
||||
#define STATUS_CABLE_DCE 0x8000
|
||||
#define STATUS_CABLE_DSR 0x0010
|
||||
#define STATUS_CABLE_DCD 0x0008
|
||||
#define STATUS_CABLE_PM_SHIFT 5
|
||||
|
||||
#define PDM_OFFSET 0x1000
|
||||
|
||||
#define TX_BUFFERS 10 /* per port */
|
||||
#define RX_BUFFERS 30
|
||||
#define RX_QUEUE_LENGTH 40 /* card->host queue length - per card */
|
||||
|
||||
#define PACKET_EMPTY 0x00
|
||||
#define PACKET_FULL 0x10
|
||||
#define PACKET_SENT 0x20 /* TX only */
|
||||
#define PACKET_UNDERRUN 0x30 /* TX only */
|
||||
#define PACKET_PORT_MASK 0x03 /* RX only */
|
||||
|
||||
/* bit numbers in PLX9060 doorbell registers */
|
||||
#define DOORBELL_FROM_CARD_TX_0 0 /* packet sent by the card */
|
||||
#define DOORBELL_FROM_CARD_TX_1 1
|
||||
#define DOORBELL_FROM_CARD_TX_2 2
|
||||
#define DOORBELL_FROM_CARD_TX_3 3
|
||||
#define DOORBELL_FROM_CARD_RX 4
|
||||
#define DOORBELL_FROM_CARD_CABLE_0 5 /* cable/PM/etc. changed */
|
||||
#define DOORBELL_FROM_CARD_CABLE_1 6
|
||||
#define DOORBELL_FROM_CARD_CABLE_2 7
|
||||
#define DOORBELL_FROM_CARD_CABLE_3 8
|
||||
|
||||
#define DOORBELL_TO_CARD_OPEN_0 0
|
||||
#define DOORBELL_TO_CARD_OPEN_1 1
|
||||
#define DOORBELL_TO_CARD_OPEN_2 2
|
||||
#define DOORBELL_TO_CARD_OPEN_3 3
|
||||
#define DOORBELL_TO_CARD_CLOSE_0 4
|
||||
#define DOORBELL_TO_CARD_CLOSE_1 5
|
||||
#define DOORBELL_TO_CARD_CLOSE_2 6
|
||||
#define DOORBELL_TO_CARD_CLOSE_3 7
|
||||
#define DOORBELL_TO_CARD_TX_0 8 /* outbound packet queued */
|
||||
#define DOORBELL_TO_CARD_TX_1 9
|
||||
#define DOORBELL_TO_CARD_TX_2 10
|
||||
#define DOORBELL_TO_CARD_TX_3 11
|
||||
|
||||
/* firmware-only status bits, starting from last DOORBELL_TO_CARD + 1 */
|
||||
#define TASK_SCC_0 12
|
||||
#define TASK_SCC_1 13
|
||||
#define TASK_SCC_2 14
|
||||
#define TASK_SCC_3 15
|
||||
|
||||
#define ALIGN32(x) (((x) + 3) & 0xFFFFFFFC)
|
||||
#define BUFFER_LENGTH ALIGN32(HDLC_MAX_MRU + 4) /* 4 bytes for 32-bit CRC */
|
||||
|
||||
/* Address of TX and RX buffers in 68360 address space */
|
||||
#define BUFFERS_ADDR 0x4000 /* 16 KB */
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
#define PLX_OFFSET 0
|
||||
#else
|
||||
#define PLX_OFFSET PLX + 0x80
|
||||
#endif
|
||||
|
||||
#define PLX_MAILBOX_0 (PLX_OFFSET + 0x40)
|
||||
#define PLX_MAILBOX_1 (PLX_OFFSET + 0x44)
|
||||
#define PLX_MAILBOX_2 (PLX_OFFSET + 0x48)
|
||||
#define PLX_MAILBOX_3 (PLX_OFFSET + 0x4C)
|
||||
#define PLX_MAILBOX_4 (PLX_OFFSET + 0x50)
|
||||
#define PLX_MAILBOX_5 (PLX_OFFSET + 0x54)
|
||||
#define PLX_MAILBOX_6 (PLX_OFFSET + 0x58)
|
||||
#define PLX_MAILBOX_7 (PLX_OFFSET + 0x5C)
|
||||
#define PLX_DOORBELL_TO_CARD (PLX_OFFSET + 0x60)
|
||||
#define PLX_DOORBELL_FROM_CARD (PLX_OFFSET + 0x64)
|
||||
#define PLX_INTERRUPT_CS (PLX_OFFSET + 0x68)
|
||||
#define PLX_CONTROL (PLX_OFFSET + 0x6C)
|
||||
|
||||
#ifdef __ASSEMBLER__
|
||||
#define PLX_DMA_0_MODE (PLX + 0x100)
|
||||
#define PLX_DMA_0_PCI (PLX + 0x104)
|
||||
#define PLX_DMA_0_LOCAL (PLX + 0x108)
|
||||
#define PLX_DMA_0_LENGTH (PLX + 0x10C)
|
||||
#define PLX_DMA_0_DESC (PLX + 0x110)
|
||||
#define PLX_DMA_1_MODE (PLX + 0x114)
|
||||
#define PLX_DMA_1_PCI (PLX + 0x118)
|
||||
#define PLX_DMA_1_LOCAL (PLX + 0x11C)
|
||||
#define PLX_DMA_1_LENGTH (PLX + 0x120)
|
||||
#define PLX_DMA_1_DESC (PLX + 0x124)
|
||||
#define PLX_DMA_CMD_STS (PLX + 0x128)
|
||||
#define PLX_DMA_ARBITR_0 (PLX + 0x12C)
|
||||
#define PLX_DMA_ARBITR_1 (PLX + 0x130)
|
||||
#endif
|
||||
|
||||
#define DESC_LENGTH 12
|
||||
|
||||
/* offsets from start of status_t */
|
||||
/* card to host */
|
||||
#define STATUS_OPEN 0
|
||||
#define STATUS_CABLE (STATUS_OPEN + 4)
|
||||
#define STATUS_RX_OVERRUNS (STATUS_CABLE + 4)
|
||||
#define STATUS_RX_FRAME_ERRORS (STATUS_RX_OVERRUNS + 4)
|
||||
|
||||
/* host to card */
|
||||
#define STATUS_PARITY (STATUS_RX_FRAME_ERRORS + 4)
|
||||
#define STATUS_ENCODING (STATUS_PARITY + 4)
|
||||
#define STATUS_CLOCKING (STATUS_ENCODING + 4)
|
||||
#define STATUS_TX_DESCS (STATUS_CLOCKING + 4)
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
typedef struct {
|
||||
volatile u32 stat;
|
||||
u32 address; /* PCI address */
|
||||
volatile u32 length;
|
||||
}desc_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
// Card to host
|
||||
volatile u32 open;
|
||||
volatile u32 cable;
|
||||
volatile u32 rx_overruns;
|
||||
volatile u32 rx_frame_errors;
|
||||
|
||||
// Host to card
|
||||
u32 parity;
|
||||
u32 encoding;
|
||||
u32 clocking;
|
||||
desc_t tx_descs[TX_BUFFERS];
|
||||
}port_status_t;
|
||||
|
||||
#endif /* __ASSEMBLER__ */
|
||||
895
drivers/net/wan/wanxlfw.S
Normal file
895
drivers/net/wan/wanxlfw.S
Normal file
@@ -0,0 +1,895 @@
|
||||
.psize 0
|
||||
/*
|
||||
wanXL serial card driver for Linux
|
||||
card firmware part
|
||||
|
||||
Copyright (C) 2003 Krzysztof Halasa <khc@pm.waw.pl>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of version 2 of the GNU General Public License
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
|
||||
|
||||
|
||||
DPRAM BDs:
|
||||
0x000 - 0x050 TX#0 0x050 - 0x140 RX#0
|
||||
0x140 - 0x190 TX#1 0x190 - 0x280 RX#1
|
||||
0x280 - 0x2D0 TX#2 0x2D0 - 0x3C0 RX#2
|
||||
0x3C0 - 0x410 TX#3 0x410 - 0x500 RX#3
|
||||
|
||||
|
||||
000 5FF 1536 Bytes Dual-Port RAM User Data / BDs
|
||||
600 6FF 256 Bytes Dual-Port RAM User Data / BDs
|
||||
700 7FF 256 Bytes Dual-Port RAM User Data / BDs
|
||||
C00 CBF 192 Bytes Dual-Port RAM Parameter RAM Page 1
|
||||
D00 DBF 192 Bytes Dual-Port RAM Parameter RAM Page 2
|
||||
E00 EBF 192 Bytes Dual-Port RAM Parameter RAM Page 3
|
||||
F00 FBF 192 Bytes Dual-Port RAM Parameter RAM Page 4
|
||||
|
||||
local interrupts level
|
||||
NMI 7
|
||||
PIT timer, CPM (RX/TX complete) 4
|
||||
PCI9060 DMA and PCI doorbells 3
|
||||
Cable - not used 1
|
||||
*/
|
||||
|
||||
#include <linux/hdlc.h>
|
||||
#include "wanxl.h"
|
||||
|
||||
/* memory addresses and offsets */
|
||||
|
||||
MAX_RAM_SIZE = 16 * 1024 * 1024 // max RAM supported by hardware
|
||||
|
||||
PCI9060_VECTOR = 0x0000006C
|
||||
CPM_IRQ_BASE = 0x40
|
||||
ERROR_VECTOR = CPM_IRQ_BASE * 4
|
||||
SCC1_VECTOR = (CPM_IRQ_BASE + 0x1E) * 4
|
||||
SCC2_VECTOR = (CPM_IRQ_BASE + 0x1D) * 4
|
||||
SCC3_VECTOR = (CPM_IRQ_BASE + 0x1C) * 4
|
||||
SCC4_VECTOR = (CPM_IRQ_BASE + 0x1B) * 4
|
||||
CPM_IRQ_LEVEL = 4
|
||||
TIMER_IRQ = 128
|
||||
TIMER_IRQ_LEVEL = 4
|
||||
PITR_CONST = 0x100 + 16 // 1 Hz timer
|
||||
|
||||
MBAR = 0x0003FF00
|
||||
|
||||
VALUE_WINDOW = 0x40000000
|
||||
ORDER_WINDOW = 0xC0000000
|
||||
|
||||
PLX = 0xFFF90000
|
||||
|
||||
CSRA = 0xFFFB0000
|
||||
CSRB = 0xFFFB0002
|
||||
CSRC = 0xFFFB0004
|
||||
CSRD = 0xFFFB0006
|
||||
STATUS_CABLE_LL = 0x2000
|
||||
STATUS_CABLE_DTR = 0x1000
|
||||
|
||||
DPRBASE = 0xFFFC0000
|
||||
|
||||
SCC1_BASE = DPRBASE + 0xC00
|
||||
MISC_BASE = DPRBASE + 0xCB0
|
||||
SCC2_BASE = DPRBASE + 0xD00
|
||||
SCC3_BASE = DPRBASE + 0xE00
|
||||
SCC4_BASE = DPRBASE + 0xF00
|
||||
|
||||
// offset from SCCx_BASE
|
||||
// SCC_xBASE contain offsets from DPRBASE and must be divisible by 8
|
||||
SCC_RBASE = 0 // 16-bit RxBD base address
|
||||
SCC_TBASE = 2 // 16-bit TxBD base address
|
||||
SCC_RFCR = 4 // 8-bit Rx function code
|
||||
SCC_TFCR = 5 // 8-bit Tx function code
|
||||
SCC_MRBLR = 6 // 16-bit maximum Rx buffer length
|
||||
SCC_C_MASK = 0x34 // 32-bit CRC constant
|
||||
SCC_C_PRES = 0x38 // 32-bit CRC preset
|
||||
SCC_MFLR = 0x46 // 16-bit max Rx frame length (without flags)
|
||||
|
||||
REGBASE = DPRBASE + 0x1000
|
||||
PICR = REGBASE + 0x026 // 16-bit periodic irq control
|
||||
PITR = REGBASE + 0x02A // 16-bit periodic irq timing
|
||||
OR1 = REGBASE + 0x064 // 32-bit RAM bank #1 options
|
||||
CICR = REGBASE + 0x540 // 32(24)-bit CP interrupt config
|
||||
CIMR = REGBASE + 0x548 // 32-bit CP interrupt mask
|
||||
CISR = REGBASE + 0x54C // 32-bit CP interrupts in-service
|
||||
PADIR = REGBASE + 0x550 // 16-bit PortA data direction bitmap
|
||||
PAPAR = REGBASE + 0x552 // 16-bit PortA pin assignment bitmap
|
||||
PAODR = REGBASE + 0x554 // 16-bit PortA open drain bitmap
|
||||
PADAT = REGBASE + 0x556 // 16-bit PortA data register
|
||||
|
||||
PCDIR = REGBASE + 0x560 // 16-bit PortC data direction bitmap
|
||||
PCPAR = REGBASE + 0x562 // 16-bit PortC pin assignment bitmap
|
||||
PCSO = REGBASE + 0x564 // 16-bit PortC special options
|
||||
PCDAT = REGBASE + 0x566 // 16-bit PortC data register
|
||||
PCINT = REGBASE + 0x568 // 16-bit PortC interrupt control
|
||||
CR = REGBASE + 0x5C0 // 16-bit Command register
|
||||
|
||||
SCC1_REGS = REGBASE + 0x600
|
||||
SCC2_REGS = REGBASE + 0x620
|
||||
SCC3_REGS = REGBASE + 0x640
|
||||
SCC4_REGS = REGBASE + 0x660
|
||||
SICR = REGBASE + 0x6EC // 32-bit SI clock route
|
||||
|
||||
// offset from SCCx_REGS
|
||||
SCC_GSMR_L = 0x00 // 32 bits
|
||||
SCC_GSMR_H = 0x04 // 32 bits
|
||||
SCC_PSMR = 0x08 // 16 bits
|
||||
SCC_TODR = 0x0C // 16 bits
|
||||
SCC_DSR = 0x0E // 16 bits
|
||||
SCC_SCCE = 0x10 // 16 bits
|
||||
SCC_SCCM = 0x14 // 16 bits
|
||||
SCC_SCCS = 0x17 // 8 bits
|
||||
|
||||
#if QUICC_MEMCPY_USES_PLX
|
||||
.macro memcpy_from_pci src, dest, len // len must be < 8 MB
|
||||
addl #3, \len
|
||||
andl #0xFFFFFFFC, \len // always copy n * 4 bytes
|
||||
movel \src, PLX_DMA_0_PCI
|
||||
movel \dest, PLX_DMA_0_LOCAL
|
||||
movel \len, PLX_DMA_0_LENGTH
|
||||
movel #0x0103, PLX_DMA_CMD_STS // start channel 0 transfer
|
||||
bsr memcpy_from_pci_run
|
||||
.endm
|
||||
|
||||
.macro memcpy_to_pci src, dest, len
|
||||
addl #3, \len
|
||||
andl #0xFFFFFFFC, \len // always copy n * 4 bytes
|
||||
movel \src, PLX_DMA_1_LOCAL
|
||||
movel \dest, PLX_DMA_1_PCI
|
||||
movel \len, PLX_DMA_1_LENGTH
|
||||
movel #0x0301, PLX_DMA_CMD_STS // start channel 1 transfer
|
||||
bsr memcpy_to_pci_run
|
||||
.endm
|
||||
|
||||
#else
|
||||
|
||||
.macro memcpy src, dest, len // len must be < 65536 bytes
|
||||
movel %d7, -(%sp) // src and dest must be < 256 MB
|
||||
movel \len, %d7 // bits 0 and 1
|
||||
lsrl #2, \len
|
||||
andl \len, \len
|
||||
beq 99f // only 0 - 3 bytes
|
||||
subl #1, \len // for dbf
|
||||
98: movel (\src)+, (\dest)+
|
||||
dbfw \len, 98b
|
||||
99: movel %d7, \len
|
||||
btstl #1, \len
|
||||
beq 99f
|
||||
movew (\src)+, (\dest)+
|
||||
99: btstl #0, \len
|
||||
beq 99f
|
||||
moveb (\src)+, (\dest)+
|
||||
99:
|
||||
movel (%sp)+, %d7
|
||||
.endm
|
||||
|
||||
.macro memcpy_from_pci src, dest, len
|
||||
addl #VALUE_WINDOW, \src
|
||||
memcpy \src, \dest, \len
|
||||
.endm
|
||||
|
||||
.macro memcpy_to_pci src, dest, len
|
||||
addl #VALUE_WINDOW, \dest
|
||||
memcpy \src, \dest, \len
|
||||
.endm
|
||||
#endif
|
||||
|
||||
|
||||
.macro wait_for_command
|
||||
99: btstl #0, CR
|
||||
bne 99b
|
||||
.endm
|
||||
|
||||
|
||||
|
||||
|
||||
/****************************** card initialization *******************/
|
||||
.text
|
||||
.global _start
|
||||
_start: bra init
|
||||
|
||||
.org _start + 4
|
||||
ch_status_addr: .long 0, 0, 0, 0
|
||||
rx_descs_addr: .long 0
|
||||
|
||||
init:
|
||||
#if DETECT_RAM
|
||||
movel OR1, %d0
|
||||
andl #0xF00007FF, %d0 // mask AMxx bits
|
||||
orl #0xFFFF800 & ~(MAX_RAM_SIZE - 1), %d0 // update RAM bank size
|
||||
movel %d0, OR1
|
||||
#endif
|
||||
|
||||
addl #VALUE_WINDOW, rx_descs_addr // PCI addresses of shared data
|
||||
clrl %d0 // D0 = 4 * port
|
||||
init_1: tstl ch_status_addr(%d0)
|
||||
beq init_2
|
||||
addl #VALUE_WINDOW, ch_status_addr(%d0)
|
||||
init_2: addl #4, %d0
|
||||
cmpl #4 * 4, %d0
|
||||
bne init_1
|
||||
|
||||
movel #pci9060_interrupt, PCI9060_VECTOR
|
||||
movel #error_interrupt, ERROR_VECTOR
|
||||
movel #port_interrupt_1, SCC1_VECTOR
|
||||
movel #port_interrupt_2, SCC2_VECTOR
|
||||
movel #port_interrupt_3, SCC3_VECTOR
|
||||
movel #port_interrupt_4, SCC4_VECTOR
|
||||
movel #timer_interrupt, TIMER_IRQ * 4
|
||||
|
||||
movel #0x78000000, CIMR // only SCCx IRQs from CPM
|
||||
movew #(TIMER_IRQ_LEVEL << 8) + TIMER_IRQ, PICR // interrupt from PIT
|
||||
movew #PITR_CONST, PITR
|
||||
|
||||
// SCC1=SCCa SCC2=SCCb SCC3=SCCc SCC4=SCCd prio=4 HP=-1 IRQ=64-79
|
||||
movel #0xD41F40 + (CPM_IRQ_LEVEL << 13), CICR
|
||||
movel #0x543, PLX_DMA_0_MODE // 32-bit, Ready, Burst, IRQ
|
||||
movel #0x543, PLX_DMA_1_MODE
|
||||
movel #0x0, PLX_DMA_0_DESC // from PCI to local
|
||||
movel #0x8, PLX_DMA_1_DESC // from local to PCI
|
||||
movel #0x101, PLX_DMA_CMD_STS // enable both DMA channels
|
||||
// enable local IRQ, DMA, doorbells and PCI IRQ
|
||||
orl #0x000F0300, PLX_INTERRUPT_CS
|
||||
|
||||
#if DETECT_RAM
|
||||
bsr ram_test
|
||||
#else
|
||||
movel #1, PLX_MAILBOX_5 // non-zero value = init complete
|
||||
#endif
|
||||
bsr check_csr
|
||||
|
||||
movew #0xFFFF, PAPAR // all pins are clocks/data
|
||||
clrw PADIR // first function
|
||||
clrw PCSO // CD and CTS always active
|
||||
|
||||
|
||||
/****************************** main loop *****************************/
|
||||
|
||||
main: movel channel_stats, %d7 // D7 = doorbell + irq status
|
||||
clrl channel_stats
|
||||
|
||||
tstl %d7
|
||||
bne main_1
|
||||
// nothing to do - wait for next event
|
||||
stop #0x2200 // supervisor + IRQ level 2
|
||||
movew #0x2700, %sr // disable IRQs again
|
||||
bra main
|
||||
|
||||
main_1: clrl %d0 // D0 = 4 * port
|
||||
clrl %d6 // D6 = doorbell to host value
|
||||
|
||||
main_l: btstl #DOORBELL_TO_CARD_CLOSE_0, %d7
|
||||
beq main_op
|
||||
bclrl #DOORBELL_TO_CARD_OPEN_0, %d7 // in case both bits are set
|
||||
bsr close_port
|
||||
main_op:
|
||||
btstl #DOORBELL_TO_CARD_OPEN_0, %d7
|
||||
beq main_cl
|
||||
bsr open_port
|
||||
main_cl:
|
||||
btstl #DOORBELL_TO_CARD_TX_0, %d7
|
||||
beq main_txend
|
||||
bsr tx
|
||||
main_txend:
|
||||
btstl #TASK_SCC_0, %d7
|
||||
beq main_next
|
||||
bsr tx_end
|
||||
bsr rx
|
||||
|
||||
main_next:
|
||||
lsrl #1, %d7 // port status for next port
|
||||
addl #4, %d0 // D0 = 4 * next port
|
||||
cmpl #4 * 4, %d0
|
||||
bne main_l
|
||||
movel %d6, PLX_DOORBELL_FROM_CARD // signal the host
|
||||
bra main
|
||||
|
||||
|
||||
/****************************** open port *****************************/
|
||||
|
||||
open_port: // D0 = 4 * port, D6 = doorbell to host
|
||||
movel ch_status_addr(%d0), %a0 // A0 = port status address
|
||||
tstl STATUS_OPEN(%a0)
|
||||
bne open_port_ret // port already open
|
||||
movel #1, STATUS_OPEN(%a0) // confirm the port is open
|
||||
// setup BDs
|
||||
clrl tx_in(%d0)
|
||||
clrl tx_out(%d0)
|
||||
clrl tx_count(%d0)
|
||||
clrl rx_in(%d0)
|
||||
|
||||
movel SICR, %d1 // D1 = clock settings in SICR
|
||||
andl clocking_mask(%d0), %d1
|
||||
cmpl #CLOCK_TXFROMRX, STATUS_CLOCKING(%a0)
|
||||
bne open_port_clock_ext
|
||||
orl clocking_txfromrx(%d0), %d1
|
||||
bra open_port_set_clock
|
||||
|
||||
open_port_clock_ext:
|
||||
orl clocking_ext(%d0), %d1
|
||||
open_port_set_clock:
|
||||
movel %d1, SICR // update clock settings in SICR
|
||||
|
||||
orw #STATUS_CABLE_DTR, csr_output(%d0) // DTR on
|
||||
bsr check_csr // call with disabled timer interrupt
|
||||
|
||||
// Setup TX descriptors
|
||||
movel first_buffer(%d0), %d1 // D1 = starting buffer address
|
||||
movel tx_first_bd(%d0), %a1 // A1 = starting TX BD address
|
||||
movel #TX_BUFFERS - 2, %d2 // D2 = TX_BUFFERS - 1 counter
|
||||
movel #0x18000000, %d3 // D3 = initial TX BD flags: Int + Last
|
||||
cmpl #PARITY_NONE, STATUS_PARITY(%a0)
|
||||
beq open_port_tx_loop
|
||||
bsetl #26, %d3 // TX BD flag: Transmit CRC
|
||||
open_port_tx_loop:
|
||||
movel %d3, (%a1)+ // TX flags + length
|
||||
movel %d1, (%a1)+ // buffer address
|
||||
addl #BUFFER_LENGTH, %d1
|
||||
dbfw %d2, open_port_tx_loop
|
||||
|
||||
bsetl #29, %d3 // TX BD flag: Wrap (last BD)
|
||||
movel %d3, (%a1)+ // Final TX flags + length
|
||||
movel %d1, (%a1)+ // buffer address
|
||||
|
||||
// Setup RX descriptors // A1 = starting RX BD address
|
||||
movel #RX_BUFFERS - 2, %d2 // D2 = RX_BUFFERS - 1 counter
|
||||
open_port_rx_loop:
|
||||
movel #0x90000000, (%a1)+ // RX flags + length
|
||||
movel %d1, (%a1)+ // buffer address
|
||||
addl #BUFFER_LENGTH, %d1
|
||||
dbfw %d2, open_port_rx_loop
|
||||
|
||||
movel #0xB0000000, (%a1)+ // Final RX flags + length
|
||||
movel %d1, (%a1)+ // buffer address
|
||||
|
||||
// Setup port parameters
|
||||
movel scc_base_addr(%d0), %a1 // A1 = SCC_BASE address
|
||||
movel scc_reg_addr(%d0), %a2 // A2 = SCC_REGS address
|
||||
|
||||
movel #0xFFFF, SCC_SCCE(%a2) // clear status bits
|
||||
movel #0x0000, SCC_SCCM(%a2) // interrupt mask
|
||||
|
||||
movel tx_first_bd(%d0), %d1
|
||||
movew %d1, SCC_TBASE(%a1) // D1 = offset of first TxBD
|
||||
addl #TX_BUFFERS * 8, %d1
|
||||
movew %d1, SCC_RBASE(%a1) // D1 = offset of first RxBD
|
||||
moveb #0x8, SCC_RFCR(%a1) // Intel mode, 1000
|
||||
moveb #0x8, SCC_TFCR(%a1)
|
||||
|
||||
// Parity settings
|
||||
cmpl #PARITY_CRC16_PR1_CCITT, STATUS_PARITY(%a0)
|
||||
bne open_port_parity_1
|
||||
clrw SCC_PSMR(%a2) // CRC16-CCITT
|
||||
movel #0xF0B8, SCC_C_MASK(%a1)
|
||||
movel #0xFFFF, SCC_C_PRES(%a1)
|
||||
movew #HDLC_MAX_MRU + 2, SCC_MFLR(%a1) // 2 bytes for CRC
|
||||
movew #2, parity_bytes(%d0)
|
||||
bra open_port_2
|
||||
|
||||
open_port_parity_1:
|
||||
cmpl #PARITY_CRC32_PR1_CCITT, STATUS_PARITY(%a0)
|
||||
bne open_port_parity_2
|
||||
movew #0x0800, SCC_PSMR(%a2) // CRC32-CCITT
|
||||
movel #0xDEBB20E3, SCC_C_MASK(%a1)
|
||||
movel #0xFFFFFFFF, SCC_C_PRES(%a1)
|
||||
movew #HDLC_MAX_MRU + 4, SCC_MFLR(%a1) // 4 bytes for CRC
|
||||
movew #4, parity_bytes(%d0)
|
||||
bra open_port_2
|
||||
|
||||
open_port_parity_2:
|
||||
cmpl #PARITY_CRC16_PR0_CCITT, STATUS_PARITY(%a0)
|
||||
bne open_port_parity_3
|
||||
clrw SCC_PSMR(%a2) // CRC16-CCITT preset 0
|
||||
movel #0xF0B8, SCC_C_MASK(%a1)
|
||||
clrl SCC_C_PRES(%a1)
|
||||
movew #HDLC_MAX_MRU + 2, SCC_MFLR(%a1) // 2 bytes for CRC
|
||||
movew #2, parity_bytes(%d0)
|
||||
bra open_port_2
|
||||
|
||||
open_port_parity_3:
|
||||
cmpl #PARITY_CRC32_PR0_CCITT, STATUS_PARITY(%a0)
|
||||
bne open_port_parity_4
|
||||
movew #0x0800, SCC_PSMR(%a2) // CRC32-CCITT preset 0
|
||||
movel #0xDEBB20E3, SCC_C_MASK(%a1)
|
||||
clrl SCC_C_PRES(%a1)
|
||||
movew #HDLC_MAX_MRU + 4, SCC_MFLR(%a1) // 4 bytes for CRC
|
||||
movew #4, parity_bytes(%d0)
|
||||
bra open_port_2
|
||||
|
||||
open_port_parity_4:
|
||||
clrw SCC_PSMR(%a2) // no parity
|
||||
movel #0xF0B8, SCC_C_MASK(%a1)
|
||||
movel #0xFFFF, SCC_C_PRES(%a1)
|
||||
movew #HDLC_MAX_MRU, SCC_MFLR(%a1) // 0 bytes for CRC
|
||||
clrw parity_bytes(%d0)
|
||||
|
||||
open_port_2:
|
||||
movel #0x00000003, SCC_GSMR_H(%a2) // RTSM
|
||||
cmpl #ENCODING_NRZI, STATUS_ENCODING(%a0)
|
||||
bne open_port_nrz
|
||||
movel #0x10040900, SCC_GSMR_L(%a2) // NRZI: TCI Tend RECN+TENC=1
|
||||
bra open_port_3
|
||||
|
||||
open_port_nrz:
|
||||
movel #0x10040000, SCC_GSMR_L(%a2) // NRZ: TCI Tend RECN+TENC=0
|
||||
open_port_3:
|
||||
movew #BUFFER_LENGTH, SCC_MRBLR(%a1)
|
||||
movel %d0, %d1
|
||||
lsll #4, %d1 // D1 bits 7 and 6 = port
|
||||
orl #1, %d1
|
||||
movew %d1, CR // Init SCC RX and TX params
|
||||
wait_for_command
|
||||
|
||||
// TCI Tend ENR ENT
|
||||
movew #0x001F, SCC_SCCM(%a2) // TXE RXF BSY TXB RXB interrupts
|
||||
orl #0x00000030, SCC_GSMR_L(%a2) // enable SCC
|
||||
open_port_ret:
|
||||
rts
|
||||
|
||||
|
||||
/****************************** close port ****************************/
|
||||
|
||||
close_port: // D0 = 4 * port, D6 = doorbell to host
|
||||
movel scc_reg_addr(%d0), %a0 // A0 = SCC_REGS address
|
||||
clrw SCC_SCCM(%a0) // no SCC interrupts
|
||||
andl #0xFFFFFFCF, SCC_GSMR_L(%a0) // Disable ENT and ENR
|
||||
|
||||
andw #~STATUS_CABLE_DTR, csr_output(%d0) // DTR off
|
||||
bsr check_csr // call with disabled timer interrupt
|
||||
|
||||
movel ch_status_addr(%d0), %d1
|
||||
clrl STATUS_OPEN(%d1) // confirm the port is closed
|
||||
rts
|
||||
|
||||
|
||||
/****************************** transmit packet ***********************/
|
||||
// queue packets for transmission
|
||||
tx: // D0 = 4 * port, D6 = doorbell to host
|
||||
cmpl #TX_BUFFERS, tx_count(%d0)
|
||||
beq tx_ret // all DB's = descs in use
|
||||
|
||||
movel tx_out(%d0), %d1
|
||||
movel %d1, %d2 // D1 = D2 = tx_out BD# = desc#
|
||||
mulul #DESC_LENGTH, %d2 // D2 = TX desc offset
|
||||
addl ch_status_addr(%d0), %d2
|
||||
addl #STATUS_TX_DESCS, %d2 // D2 = TX desc address
|
||||
cmpl #PACKET_FULL, (%d2) // desc status
|
||||
bne tx_ret
|
||||
|
||||
// queue it
|
||||
movel 4(%d2), %a0 // PCI address
|
||||
lsll #3, %d1 // BD is 8-bytes long
|
||||
addl tx_first_bd(%d0), %d1 // D1 = current tx_out BD addr
|
||||
|
||||
movel 4(%d1), %a1 // A1 = dest address
|
||||
movel 8(%d2), %d2 // D2 = length
|
||||
movew %d2, 2(%d1) // length into BD
|
||||
memcpy_from_pci %a0, %a1, %d2
|
||||
bsetl #31, (%d1) // CP go ahead
|
||||
|
||||
// update tx_out and tx_count
|
||||
movel tx_out(%d0), %d1
|
||||
addl #1, %d1
|
||||
cmpl #TX_BUFFERS, %d1
|
||||
bne tx_1
|
||||
clrl %d1
|
||||
tx_1: movel %d1, tx_out(%d0)
|
||||
|
||||
addl #1, tx_count(%d0)
|
||||
bra tx
|
||||
|
||||
tx_ret: rts
|
||||
|
||||
|
||||
/****************************** packet received ***********************/
|
||||
|
||||
// Service receive buffers // D0 = 4 * port, D6 = doorbell to host
|
||||
rx: movel rx_in(%d0), %d1 // D1 = rx_in BD#
|
||||
lsll #3, %d1 // BD is 8-bytes long
|
||||
addl rx_first_bd(%d0), %d1 // D1 = current rx_in BD address
|
||||
movew (%d1), %d2 // D2 = RX BD flags
|
||||
btstl #15, %d2
|
||||
bne rx_ret // BD still empty
|
||||
|
||||
btstl #1, %d2
|
||||
bne rx_overrun
|
||||
|
||||
tstw parity_bytes(%d0)
|
||||
bne rx_parity
|
||||
bclrl #2, %d2 // do not test for CRC errors
|
||||
rx_parity:
|
||||
andw #0x0CBC, %d2 // mask status bits
|
||||
cmpw #0x0C00, %d2 // correct frame
|
||||
bne rx_bad_frame
|
||||
clrl %d3
|
||||
movew 2(%d1), %d3
|
||||
subw parity_bytes(%d0), %d3 // D3 = packet length
|
||||
cmpw #HDLC_MAX_MRU, %d3
|
||||
bgt rx_bad_frame
|
||||
|
||||
rx_good_frame:
|
||||
movel rx_out, %d2
|
||||
mulul #DESC_LENGTH, %d2
|
||||
addl rx_descs_addr, %d2 // D2 = RX desc address
|
||||
cmpl #PACKET_EMPTY, (%d2) // desc stat
|
||||
bne rx_overrun
|
||||
|
||||
movel %d3, 8(%d2)
|
||||
movel 4(%d1), %a0 // A0 = source address
|
||||
movel 4(%d2), %a1
|
||||
tstl %a1
|
||||
beq rx_ignore_data
|
||||
memcpy_to_pci %a0, %a1, %d3
|
||||
rx_ignore_data:
|
||||
movel packet_full(%d0), (%d2) // update desc stat
|
||||
|
||||
// update D6 and rx_out
|
||||
bsetl #DOORBELL_FROM_CARD_RX, %d6 // signal host that RX completed
|
||||
movel rx_out, %d2
|
||||
addl #1, %d2
|
||||
cmpl #RX_QUEUE_LENGTH, %d2
|
||||
bne rx_1
|
||||
clrl %d2
|
||||
rx_1: movel %d2, rx_out
|
||||
|
||||
rx_free_bd:
|
||||
andw #0xF000, (%d1) // clear CM and error bits
|
||||
bsetl #31, (%d1) // free BD
|
||||
// update rx_in
|
||||
movel rx_in(%d0), %d1
|
||||
addl #1, %d1
|
||||
cmpl #RX_BUFFERS, %d1
|
||||
bne rx_2
|
||||
clrl %d1
|
||||
rx_2: movel %d1, rx_in(%d0)
|
||||
bra rx
|
||||
|
||||
rx_overrun:
|
||||
movel ch_status_addr(%d0), %d2
|
||||
addl #1, STATUS_RX_OVERRUNS(%d2)
|
||||
bra rx_free_bd
|
||||
|
||||
rx_bad_frame:
|
||||
movel ch_status_addr(%d0), %d2
|
||||
addl #1, STATUS_RX_FRAME_ERRORS(%d2)
|
||||
bra rx_free_bd
|
||||
|
||||
rx_ret: rts
|
||||
|
||||
|
||||
/****************************** packet transmitted ********************/
|
||||
|
||||
// Service transmit buffers // D0 = 4 * port, D6 = doorbell to host
|
||||
tx_end: tstl tx_count(%d0)
|
||||
beq tx_end_ret // TX buffers already empty
|
||||
|
||||
movel tx_in(%d0), %d1
|
||||
movel %d1, %d2 // D1 = D2 = tx_in BD# = desc#
|
||||
lsll #3, %d1 // BD is 8-bytes long
|
||||
addl tx_first_bd(%d0), %d1 // D1 = current tx_in BD address
|
||||
movew (%d1), %d3 // D3 = TX BD flags
|
||||
btstl #15, %d3
|
||||
bne tx_end_ret // BD still being transmitted
|
||||
|
||||
// update D6, tx_in and tx_count
|
||||
orl bell_tx(%d0), %d6 // signal host that TX desc freed
|
||||
subl #1, tx_count(%d0)
|
||||
movel tx_in(%d0), %d1
|
||||
addl #1, %d1
|
||||
cmpl #TX_BUFFERS, %d1
|
||||
bne tx_end_1
|
||||
clrl %d1
|
||||
tx_end_1:
|
||||
movel %d1, tx_in(%d0)
|
||||
|
||||
// free host's descriptor
|
||||
mulul #DESC_LENGTH, %d2 // D2 = TX desc offset
|
||||
addl ch_status_addr(%d0), %d2
|
||||
addl #STATUS_TX_DESCS, %d2 // D2 = TX desc address
|
||||
btstl #1, %d3
|
||||
bne tx_end_underrun
|
||||
movel #PACKET_SENT, (%d2)
|
||||
bra tx_end
|
||||
|
||||
tx_end_underrun:
|
||||
movel #PACKET_UNDERRUN, (%d2)
|
||||
bra tx_end
|
||||
|
||||
tx_end_ret: rts
|
||||
|
||||
|
||||
/****************************** PLX PCI9060 DMA memcpy ****************/
|
||||
|
||||
#if QUICC_MEMCPY_USES_PLX
|
||||
// called with interrupts disabled
|
||||
memcpy_from_pci_run:
|
||||
movel %d0, -(%sp)
|
||||
movew %sr, -(%sp)
|
||||
memcpy_1:
|
||||
movel PLX_DMA_CMD_STS, %d0 // do not btst PLX register directly
|
||||
btstl #4, %d0 // transfer done?
|
||||
bne memcpy_end
|
||||
stop #0x2200 // enable PCI9060 interrupts
|
||||
movew #0x2700, %sr // disable interrupts again
|
||||
bra memcpy_1
|
||||
|
||||
memcpy_to_pci_run:
|
||||
movel %d0, -(%sp)
|
||||
movew %sr, -(%sp)
|
||||
memcpy_2:
|
||||
movel PLX_DMA_CMD_STS, %d0 // do not btst PLX register directly
|
||||
btstl #12, %d0 // transfer done?
|
||||
bne memcpy_end
|
||||
stop #0x2200 // enable PCI9060 interrupts
|
||||
movew #0x2700, %sr // disable interrupts again
|
||||
bra memcpy_2
|
||||
|
||||
memcpy_end:
|
||||
movew (%sp)+, %sr
|
||||
movel (%sp)+, %d0
|
||||
rts
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/****************************** PLX PCI9060 interrupt *****************/
|
||||
|
||||
pci9060_interrupt:
|
||||
movel %d0, -(%sp)
|
||||
|
||||
movel PLX_DOORBELL_TO_CARD, %d0
|
||||
movel %d0, PLX_DOORBELL_TO_CARD // confirm all requests
|
||||
orl %d0, channel_stats
|
||||
|
||||
movel #0x0909, PLX_DMA_CMD_STS // clear DMA ch #0 and #1 interrupts
|
||||
|
||||
movel (%sp)+, %d0
|
||||
rte
|
||||
|
||||
/****************************** SCC interrupts ************************/
|
||||
|
||||
port_interrupt_1:
|
||||
orl #0, SCC1_REGS + SCC_SCCE; // confirm SCC events
|
||||
orl #1 << TASK_SCC_0, channel_stats
|
||||
movel #0x40000000, CISR
|
||||
rte
|
||||
|
||||
port_interrupt_2:
|
||||
orl #0, SCC2_REGS + SCC_SCCE; // confirm SCC events
|
||||
orl #1 << TASK_SCC_1, channel_stats
|
||||
movel #0x20000000, CISR
|
||||
rte
|
||||
|
||||
port_interrupt_3:
|
||||
orl #0, SCC3_REGS + SCC_SCCE; // confirm SCC events
|
||||
orl #1 << TASK_SCC_2, channel_stats
|
||||
movel #0x10000000, CISR
|
||||
rte
|
||||
|
||||
port_interrupt_4:
|
||||
orl #0, SCC4_REGS + SCC_SCCE; // confirm SCC events
|
||||
orl #1 << TASK_SCC_3, channel_stats
|
||||
movel #0x08000000, CISR
|
||||
rte
|
||||
|
||||
error_interrupt:
|
||||
rte
|
||||
|
||||
|
||||
/****************************** cable and PM routine ******************/
|
||||
// modified registers: none
|
||||
check_csr:
|
||||
movel %d0, -(%sp)
|
||||
movel %d1, -(%sp)
|
||||
movel %d2, -(%sp)
|
||||
movel %a0, -(%sp)
|
||||
movel %a1, -(%sp)
|
||||
|
||||
clrl %d0 // D0 = 4 * port
|
||||
movel #CSRA, %a0 // A0 = CSR address
|
||||
|
||||
check_csr_loop:
|
||||
movew (%a0), %d1 // D1 = CSR input bits
|
||||
andl #0xE7, %d1 // PM and cable sense bits (no DCE bit)
|
||||
cmpw #STATUS_CABLE_V35 * (1 + 1 << STATUS_CABLE_PM_SHIFT), %d1
|
||||
bne check_csr_1
|
||||
movew #0x0E08, %d1
|
||||
bra check_csr_valid
|
||||
|
||||
check_csr_1:
|
||||
cmpw #STATUS_CABLE_X21 * (1 + 1 << STATUS_CABLE_PM_SHIFT), %d1
|
||||
bne check_csr_2
|
||||
movew #0x0408, %d1
|
||||
bra check_csr_valid
|
||||
|
||||
check_csr_2:
|
||||
cmpw #STATUS_CABLE_V24 * (1 + 1 << STATUS_CABLE_PM_SHIFT), %d1
|
||||
bne check_csr_3
|
||||
movew #0x0208, %d1
|
||||
bra check_csr_valid
|
||||
|
||||
check_csr_3:
|
||||
cmpw #STATUS_CABLE_EIA530 * (1 + 1 << STATUS_CABLE_PM_SHIFT), %d1
|
||||
bne check_csr_disable
|
||||
movew #0x0D08, %d1
|
||||
bra check_csr_valid
|
||||
|
||||
check_csr_disable:
|
||||
movew #0x0008, %d1 // D1 = disable everything
|
||||
movew #0x80E7, %d2 // D2 = input mask: ignore DSR
|
||||
bra check_csr_write
|
||||
|
||||
check_csr_valid: // D1 = mode and IRQ bits
|
||||
movew csr_output(%d0), %d2
|
||||
andw #0x3000, %d2 // D2 = requested LL and DTR bits
|
||||
orw %d2, %d1 // D1 = all requested output bits
|
||||
movew #0x80FF, %d2 // D2 = input mask: include DSR
|
||||
|
||||
check_csr_write:
|
||||
cmpw old_csr_output(%d0), %d1
|
||||
beq check_csr_input
|
||||
movew %d1, old_csr_output(%d0)
|
||||
movew %d1, (%a0) // Write CSR output bits
|
||||
|
||||
check_csr_input:
|
||||
movew (PCDAT), %d1
|
||||
andw dcd_mask(%d0), %d1
|
||||
beq check_csr_dcd_on // DCD and CTS signals are negated
|
||||
movew (%a0), %d1 // D1 = CSR input bits
|
||||
andw #~STATUS_CABLE_DCD, %d1 // DCD off
|
||||
bra check_csr_previous
|
||||
|
||||
check_csr_dcd_on:
|
||||
movew (%a0), %d1 // D1 = CSR input bits
|
||||
orw #STATUS_CABLE_DCD, %d1 // DCD on
|
||||
check_csr_previous:
|
||||
andw %d2, %d1 // input mask
|
||||
movel ch_status_addr(%d0), %a1
|
||||
cmpl STATUS_CABLE(%a1), %d1 // check for change
|
||||
beq check_csr_next
|
||||
movel %d1, STATUS_CABLE(%a1) // update status
|
||||
movel bell_cable(%d0), PLX_DOORBELL_FROM_CARD // signal the host
|
||||
|
||||
check_csr_next:
|
||||
addl #2, %a0 // next CSR register
|
||||
addl #4, %d0 // D0 = 4 * next port
|
||||
cmpl #4 * 4, %d0
|
||||
bne check_csr_loop
|
||||
|
||||
movel (%sp)+, %a1
|
||||
movel (%sp)+, %a0
|
||||
movel (%sp)+, %d2
|
||||
movel (%sp)+, %d1
|
||||
movel (%sp)+, %d0
|
||||
rts
|
||||
|
||||
|
||||
/****************************** timer interrupt ***********************/
|
||||
|
||||
timer_interrupt:
|
||||
bsr check_csr
|
||||
rte
|
||||
|
||||
|
||||
/****************************** RAM sizing and test *******************/
|
||||
#if DETECT_RAM
|
||||
ram_test:
|
||||
movel #0x12345678, %d1 // D1 = test value
|
||||
movel %d1, (128 * 1024 - 4)
|
||||
movel #128 * 1024, %d0 // D0 = RAM size tested
|
||||
ram_test_size:
|
||||
cmpl #MAX_RAM_SIZE, %d0
|
||||
beq ram_test_size_found
|
||||
movel %d0, %a0
|
||||
addl #128 * 1024 - 4, %a0
|
||||
cmpl (%a0), %d1
|
||||
beq ram_test_size_check
|
||||
ram_test_next_size:
|
||||
lsll #1, %d0
|
||||
bra ram_test_size
|
||||
|
||||
ram_test_size_check:
|
||||
eorl #0xFFFFFFFF, %d1
|
||||
movel %d1, (128 * 1024 - 4)
|
||||
cmpl (%a0), %d1
|
||||
bne ram_test_next_size
|
||||
|
||||
ram_test_size_found: // D0 = RAM size
|
||||
movel %d0, %a0 // A0 = fill ptr
|
||||
subl #firmware_end + 4, %d0
|
||||
lsrl #2, %d0
|
||||
movel %d0, %d1 // D1 = DBf counter
|
||||
ram_test_fill:
|
||||
movel %a0, -(%a0)
|
||||
dbfw %d1, ram_test_fill
|
||||
subl #0x10000, %d1
|
||||
cmpl #0xFFFFFFFF, %d1
|
||||
bne ram_test_fill
|
||||
|
||||
ram_test_loop: // D0 = DBf counter
|
||||
cmpl (%a0)+, %a0
|
||||
dbnew %d0, ram_test_loop
|
||||
bne ram_test_found_bad
|
||||
subl #0x10000, %d0
|
||||
cmpl #0xFFFFFFFF, %d0
|
||||
bne ram_test_loop
|
||||
bra ram_test_all_ok
|
||||
|
||||
ram_test_found_bad:
|
||||
subl #4, %a0
|
||||
ram_test_all_ok:
|
||||
movel %a0, PLX_MAILBOX_5
|
||||
rts
|
||||
#endif
|
||||
|
||||
|
||||
/****************************** constants *****************************/
|
||||
|
||||
scc_reg_addr:
|
||||
.long SCC1_REGS, SCC2_REGS, SCC3_REGS, SCC4_REGS
|
||||
scc_base_addr:
|
||||
.long SCC1_BASE, SCC2_BASE, SCC3_BASE, SCC4_BASE
|
||||
|
||||
tx_first_bd:
|
||||
.long DPRBASE
|
||||
.long DPRBASE + (TX_BUFFERS + RX_BUFFERS) * 8
|
||||
.long DPRBASE + (TX_BUFFERS + RX_BUFFERS) * 8 * 2
|
||||
.long DPRBASE + (TX_BUFFERS + RX_BUFFERS) * 8 * 3
|
||||
|
||||
rx_first_bd:
|
||||
.long DPRBASE + TX_BUFFERS * 8
|
||||
.long DPRBASE + TX_BUFFERS * 8 + (TX_BUFFERS + RX_BUFFERS) * 8
|
||||
.long DPRBASE + TX_BUFFERS * 8 + (TX_BUFFERS + RX_BUFFERS) * 8 * 2
|
||||
.long DPRBASE + TX_BUFFERS * 8 + (TX_BUFFERS + RX_BUFFERS) * 8 * 3
|
||||
|
||||
first_buffer:
|
||||
.long BUFFERS_ADDR
|
||||
.long BUFFERS_ADDR + (TX_BUFFERS + RX_BUFFERS) * BUFFER_LENGTH
|
||||
.long BUFFERS_ADDR + (TX_BUFFERS + RX_BUFFERS) * BUFFER_LENGTH * 2
|
||||
.long BUFFERS_ADDR + (TX_BUFFERS + RX_BUFFERS) * BUFFER_LENGTH * 3
|
||||
|
||||
bell_tx:
|
||||
.long 1 << DOORBELL_FROM_CARD_TX_0, 1 << DOORBELL_FROM_CARD_TX_1
|
||||
.long 1 << DOORBELL_FROM_CARD_TX_2, 1 << DOORBELL_FROM_CARD_TX_3
|
||||
|
||||
bell_cable:
|
||||
.long 1 << DOORBELL_FROM_CARD_CABLE_0, 1 << DOORBELL_FROM_CARD_CABLE_1
|
||||
.long 1 << DOORBELL_FROM_CARD_CABLE_2, 1 << DOORBELL_FROM_CARD_CABLE_3
|
||||
|
||||
packet_full:
|
||||
.long PACKET_FULL, PACKET_FULL + 1, PACKET_FULL + 2, PACKET_FULL + 3
|
||||
|
||||
clocking_ext:
|
||||
.long 0x0000002C, 0x00003E00, 0x002C0000, 0x3E000000
|
||||
clocking_txfromrx:
|
||||
.long 0x0000002D, 0x00003F00, 0x002D0000, 0x3F000000
|
||||
clocking_mask:
|
||||
.long 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000
|
||||
dcd_mask:
|
||||
.word 0x020, 0, 0x080, 0, 0x200, 0, 0x800
|
||||
|
||||
.ascii "wanXL firmware\n"
|
||||
.asciz "Copyright (C) 2003 Krzysztof Halasa <khc@pm.waw.pl>\n"
|
||||
|
||||
|
||||
/****************************** variables *****************************/
|
||||
|
||||
.align 4
|
||||
channel_stats: .long 0
|
||||
|
||||
tx_in: .long 0, 0, 0, 0 // transmitted
|
||||
tx_out: .long 0, 0, 0, 0 // received from host for transmission
|
||||
tx_count: .long 0, 0, 0, 0 // currently in transmit queue
|
||||
|
||||
rx_in: .long 0, 0, 0, 0 // received from port
|
||||
rx_out: .long 0 // transmitted to host
|
||||
parity_bytes: .word 0, 0, 0, 0, 0, 0, 0 // only 4 words are used
|
||||
|
||||
csr_output: .word 0
|
||||
old_csr_output: .word 0, 0, 0, 0, 0, 0, 0
|
||||
.align 4
|
||||
firmware_end: // must be dword-aligned
|
||||
158
drivers/net/wan/wanxlfw.inc_shipped
Normal file
158
drivers/net/wan/wanxlfw.inc_shipped
Normal file
@@ -0,0 +1,158 @@
|
||||
static u8 firmware[]={
|
||||
0x60,0x00,0x00,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0xB9,0x40,0x00,0x00,0x00,0x00,0x00,
|
||||
0x10,0x14,0x42,0x80,0x4A,0xB0,0x09,0xB0,0x00,0x00,0x10,0x04,0x67,0x00,0x00,0x0E,
|
||||
0x06,0xB0,0x40,0x00,0x00,0x00,0x09,0xB0,0x00,0x00,0x10,0x04,0x58,0x80,0x0C,0x80,
|
||||
0x00,0x00,0x00,0x10,0x66,0x00,0xFF,0xDE,0x21,0xFC,0x00,0x00,0x16,0xBC,0x00,0x6C,
|
||||
0x21,0xFC,0x00,0x00,0x17,0x5E,0x01,0x00,0x21,0xFC,0x00,0x00,0x16,0xDE,0x01,0x78,
|
||||
0x21,0xFC,0x00,0x00,0x16,0xFE,0x01,0x74,0x21,0xFC,0x00,0x00,0x17,0x1E,0x01,0x70,
|
||||
0x21,0xFC,0x00,0x00,0x17,0x3E,0x01,0x6C,0x21,0xFC,0x00,0x00,0x18,0x4C,0x02,0x00,
|
||||
0x23,0xFC,0x78,0x00,0x00,0x00,0xFF,0xFC,0x15,0x48,0x33,0xFC,0x04,0x80,0xFF,0xFC,
|
||||
0x10,0x26,0x33,0xFC,0x01,0x10,0xFF,0xFC,0x10,0x2A,0x23,0xFC,0x00,0xD4,0x9F,0x40,
|
||||
0xFF,0xFC,0x15,0x40,0x23,0xFC,0x00,0x00,0x05,0x43,0xFF,0xF9,0x01,0x00,0x23,0xFC,
|
||||
0x00,0x00,0x05,0x43,0xFF,0xF9,0x01,0x14,0x23,0xFC,0x00,0x00,0x00,0x00,0xFF,0xF9,
|
||||
0x01,0x10,0x23,0xFC,0x00,0x00,0x00,0x08,0xFF,0xF9,0x01,0x24,0x23,0xFC,0x00,0x00,
|
||||
0x01,0x01,0xFF,0xF9,0x01,0x28,0x00,0xB9,0x00,0x0F,0x03,0x00,0xFF,0xF9,0x00,0xE8,
|
||||
0x23,0xFC,0x00,0x00,0x00,0x01,0xFF,0xF9,0x00,0xD4,0x61,0x00,0x06,0x74,0x33,0xFC,
|
||||
0xFF,0xFF,0xFF,0xFC,0x15,0x52,0x42,0x79,0xFF,0xFC,0x15,0x50,0x42,0x79,0xFF,0xFC,
|
||||
0x15,0x64,0x2E,0x3A,0x08,0x50,0x42,0xB9,0x00,0x00,0x19,0x54,0x4A,0x87,0x66,0x00,
|
||||
0x00,0x0E,0x4E,0x72,0x22,0x00,0x46,0xFC,0x27,0x00,0x60,0x00,0xFF,0xE6,0x42,0x80,
|
||||
0x42,0x86,0x08,0x07,0x00,0x04,0x67,0x00,0x00,0x0A,0x08,0x87,0x00,0x00,0x61,0x00,
|
||||
0x02,0xA0,0x08,0x07,0x00,0x00,0x67,0x00,0x00,0x06,0x61,0x00,0x00,0x36,0x08,0x07,
|
||||
0x00,0x08,0x67,0x00,0x00,0x06,0x61,0x00,0x02,0xB8,0x08,0x07,0x00,0x0C,0x67,0x00,
|
||||
0x00,0x0A,0x61,0x00,0x04,0x94,0x61,0x00,0x03,0x60,0xE2,0x8F,0x58,0x80,0x0C,0x80,
|
||||
0x00,0x00,0x00,0x10,0x66,0x00,0xFF,0xBC,0x23,0xC6,0xFF,0xF9,0x00,0xE4,0x60,0x00,
|
||||
0xFF,0x92,0x20,0x70,0x09,0xB0,0x00,0x00,0x10,0x04,0x4A,0xA8,0x00,0x00,0x66,0x00,
|
||||
0x02,0x4E,0x21,0x7C,0x00,0x00,0x00,0x01,0x00,0x00,0x42,0xB0,0x09,0xB0,0x00,0x00,
|
||||
0x19,0x58,0x42,0xB0,0x09,0xB0,0x00,0x00,0x19,0x68,0x42,0xB0,0x09,0xB0,0x00,0x00,
|
||||
0x19,0x78,0x42,0xB0,0x09,0xB0,0x00,0x00,0x19,0x88,0x22,0x39,0xFF,0xFC,0x16,0xEC,
|
||||
0xC2,0xB0,0x09,0xB0,0x00,0x00,0x18,0xF2,0x0C,0xA8,0x00,0x00,0x00,0x04,0x00,0x18,
|
||||
0x66,0x00,0x00,0x0E,0x82,0xB0,0x09,0xB0,0x00,0x00,0x18,0xE2,0x60,0x00,0x00,0x0A,
|
||||
0x82,0xB0,0x09,0xB0,0x00,0x00,0x18,0xD2,0x23,0xC1,0xFF,0xFC,0x16,0xEC,0x00,0x70,
|
||||
0x10,0x00,0x09,0xB0,0x00,0x00,0x19,0xAA,0x61,0x00,0x05,0x76,0x22,0x30,0x09,0xB0,
|
||||
0x00,0x00,0x18,0x92,0x22,0x70,0x09,0xB0,0x00,0x00,0x18,0x72,0x74,0x08,0x26,0x3C,
|
||||
0x18,0x00,0x00,0x00,0x0C,0xA8,0x00,0x00,0x00,0x01,0x00,0x10,0x67,0x00,0x00,0x06,
|
||||
0x08,0xC3,0x00,0x1A,0x22,0xC3,0x22,0xC1,0x06,0x81,0x00,0x00,0x05,0xFC,0x51,0xCA,
|
||||
0xFF,0xF4,0x08,0xC3,0x00,0x1D,0x22,0xC3,0x22,0xC1,0x74,0x1C,0x22,0xFC,0x90,0x00,
|
||||
0x00,0x00,0x22,0xC1,0x06,0x81,0x00,0x00,0x05,0xFC,0x51,0xCA,0xFF,0xF0,0x22,0xFC,
|
||||
0xB0,0x00,0x00,0x00,0x22,0xC1,0x22,0x70,0x09,0xB0,0x00,0x00,0x18,0x62,0x24,0x70,
|
||||
0x09,0xB0,0x00,0x00,0x18,0x52,0x25,0x7C,0x00,0x00,0xFF,0xFF,0x00,0x10,0x25,0x7C,
|
||||
0x00,0x00,0x00,0x00,0x00,0x14,0x22,0x30,0x09,0xB0,0x00,0x00,0x18,0x72,0x33,0x41,
|
||||
0x00,0x02,0x06,0x81,0x00,0x00,0x00,0x50,0x33,0x41,0x00,0x00,0x13,0x7C,0x00,0x08,
|
||||
0x00,0x04,0x13,0x7C,0x00,0x08,0x00,0x05,0x0C,0xA8,0x00,0x00,0x00,0x05,0x00,0x10,
|
||||
0x66,0x00,0x00,0x2A,0x42,0x6A,0x00,0x08,0x23,0x7C,0x00,0x00,0xF0,0xB8,0x00,0x34,
|
||||
0x23,0x7C,0x00,0x00,0xFF,0xFF,0x00,0x38,0x33,0x7C,0x05,0xFA,0x00,0x46,0x31,0xBC,
|
||||
0x00,0x02,0x09,0xB0,0x00,0x00,0x19,0x9C,0x60,0x00,0x00,0xBC,0x0C,0xA8,0x00,0x00,
|
||||
0x00,0x07,0x00,0x10,0x66,0x00,0x00,0x2C,0x35,0x7C,0x08,0x00,0x00,0x08,0x23,0x7C,
|
||||
0xDE,0xBB,0x20,0xE3,0x00,0x34,0x23,0x7C,0xFF,0xFF,0xFF,0xFF,0x00,0x38,0x33,0x7C,
|
||||
0x05,0xFC,0x00,0x46,0x31,0xBC,0x00,0x04,0x09,0xB0,0x00,0x00,0x19,0x9C,0x60,0x00,
|
||||
0x00,0x86,0x0C,0xA8,0x00,0x00,0x00,0x04,0x00,0x10,0x66,0x00,0x00,0x26,0x42,0x6A,
|
||||
0x00,0x08,0x23,0x7C,0x00,0x00,0xF0,0xB8,0x00,0x34,0x42,0xA9,0x00,0x38,0x33,0x7C,
|
||||
0x05,0xFA,0x00,0x46,0x31,0xBC,0x00,0x02,0x09,0xB0,0x00,0x00,0x19,0x9C,0x60,0x00,
|
||||
0x00,0x56,0x0C,0xA8,0x00,0x00,0x00,0x06,0x00,0x10,0x66,0x00,0x00,0x28,0x35,0x7C,
|
||||
0x08,0x00,0x00,0x08,0x23,0x7C,0xDE,0xBB,0x20,0xE3,0x00,0x34,0x42,0xA9,0x00,0x38,
|
||||
0x33,0x7C,0x05,0xFC,0x00,0x46,0x31,0xBC,0x00,0x04,0x09,0xB0,0x00,0x00,0x19,0x9C,
|
||||
0x60,0x00,0x00,0x24,0x42,0x6A,0x00,0x08,0x23,0x7C,0x00,0x00,0xF0,0xB8,0x00,0x34,
|
||||
0x23,0x7C,0x00,0x00,0xFF,0xFF,0x00,0x38,0x33,0x7C,0x05,0xF8,0x00,0x46,0x42,0x70,
|
||||
0x09,0xB0,0x00,0x00,0x19,0x9C,0x25,0x7C,0x00,0x00,0x00,0x03,0x00,0x04,0x0C,0xA8,
|
||||
0x00,0x00,0x00,0x02,0x00,0x14,0x66,0x00,0x00,0x0E,0x25,0x7C,0x10,0x04,0x09,0x00,
|
||||
0x00,0x00,0x60,0x00,0x00,0x0A,0x25,0x7C,0x10,0x04,0x00,0x00,0x00,0x00,0x33,0x7C,
|
||||
0x05,0xFC,0x00,0x06,0x22,0x00,0xE9,0x89,0x00,0x81,0x00,0x00,0x00,0x01,0x33,0xC1,
|
||||
0xFF,0xFC,0x15,0xC0,0x08,0x39,0x00,0x00,0xFF,0xFC,0x15,0xC0,0x66,0x00,0xFF,0xF6,
|
||||
0x35,0x7C,0x00,0x1F,0x00,0x14,0x00,0xAA,0x00,0x00,0x00,0x30,0x00,0x00,0x4E,0x75,
|
||||
0x20,0x70,0x09,0xB0,0x00,0x00,0x18,0x52,0x42,0x68,0x00,0x14,0x02,0xA8,0xFF,0xFF,
|
||||
0xFF,0xCF,0x00,0x00,0x02,0x70,0xEF,0xFF,0x09,0xB0,0x00,0x00,0x19,0xAA,0x61,0x00,
|
||||
0x03,0x70,0x22,0x30,0x09,0xB0,0x00,0x00,0x10,0x04,0x42,0xB0,0x19,0x90,0x4E,0x75,
|
||||
0x0C,0xB0,0x00,0x00,0x00,0x0A,0x09,0xB0,0x00,0x00,0x19,0x78,0x67,0x00,0x00,0xA8,
|
||||
0x22,0x30,0x09,0xB0,0x00,0x00,0x19,0x68,0x24,0x01,0x4C,0x3C,0x20,0x00,0x00,0x00,
|
||||
0x00,0x0C,0xD4,0xB0,0x09,0xB0,0x00,0x00,0x10,0x04,0x06,0x82,0x00,0x00,0x00,0x1C,
|
||||
0x0C,0xB0,0x00,0x00,0x00,0x10,0x29,0x90,0x66,0x00,0x00,0x7C,0x20,0x70,0x29,0xA0,
|
||||
0x00,0x04,0xE7,0x89,0xD2,0xB0,0x09,0xB0,0x00,0x00,0x18,0x72,0x22,0x70,0x19,0xA0,
|
||||
0x00,0x04,0x24,0x30,0x29,0xA0,0x00,0x08,0x31,0x82,0x19,0xA0,0x00,0x02,0x56,0x82,
|
||||
0x02,0x82,0xFF,0xFF,0xFF,0xFC,0x23,0xC8,0xFF,0xF9,0x01,0x04,0x23,0xC9,0xFF,0xF9,
|
||||
0x01,0x08,0x23,0xC2,0xFF,0xF9,0x01,0x0C,0x23,0xFC,0x00,0x00,0x01,0x03,0xFF,0xF9,
|
||||
0x01,0x28,0x61,0x00,0x01,0xF6,0x08,0xF0,0x00,0x1F,0x19,0x90,0x22,0x30,0x09,0xB0,
|
||||
0x00,0x00,0x19,0x68,0x52,0x81,0x0C,0x81,0x00,0x00,0x00,0x0A,0x66,0x00,0x00,0x04,
|
||||
0x42,0x81,0x21,0x81,0x09,0xB0,0x00,0x00,0x19,0x68,0x52,0xB0,0x09,0xB0,0x00,0x00,
|
||||
0x19,0x78,0x60,0x00,0xFF,0x4C,0x4E,0x75,0x22,0x30,0x09,0xB0,0x00,0x00,0x19,0x88,
|
||||
0xE7,0x89,0xD2,0xB0,0x09,0xB0,0x00,0x00,0x18,0x82,0x34,0x30,0x19,0x90,0x08,0x02,
|
||||
0x00,0x0F,0x66,0x00,0x01,0x12,0x08,0x02,0x00,0x01,0x66,0x00,0x00,0xE6,0x4A,0x70,
|
||||
0x09,0xB0,0x00,0x00,0x19,0x9C,0x66,0x00,0x00,0x06,0x08,0x82,0x00,0x02,0x02,0x42,
|
||||
0x0C,0xBC,0x0C,0x42,0x0C,0x00,0x66,0x00,0x00,0xDC,0x42,0x83,0x36,0x30,0x19,0xA0,
|
||||
0x00,0x02,0x96,0x70,0x09,0xB0,0x00,0x00,0x19,0x9C,0x0C,0x43,0x05,0xF8,0x6E,0x00,
|
||||
0x00,0xC4,0x24,0x3A,0x04,0x84,0x4C,0x3C,0x20,0x00,0x00,0x00,0x00,0x0C,0xD4,0xBA,
|
||||
0xFA,0xF4,0x0C,0xB0,0x00,0x00,0x00,0x00,0x29,0x90,0x66,0x00,0x00,0x96,0x21,0x83,
|
||||
0x29,0xA0,0x00,0x08,0x20,0x70,0x19,0xA0,0x00,0x04,0x22,0x70,0x29,0xA0,0x00,0x04,
|
||||
0x4A,0x89,0x67,0x00,0x00,0x2A,0x56,0x83,0x02,0x83,0xFF,0xFF,0xFF,0xFC,0x23,0xC8,
|
||||
0xFF,0xF9,0x01,0x1C,0x23,0xC9,0xFF,0xF9,0x01,0x18,0x23,0xC3,0xFF,0xF9,0x01,0x20,
|
||||
0x23,0xFC,0x00,0x00,0x03,0x01,0xFF,0xF9,0x01,0x28,0x61,0x00,0x01,0x2C,0x21,0xB0,
|
||||
0x09,0xB0,0x00,0x00,0x18,0xC2,0x29,0x90,0x08,0xC6,0x00,0x04,0x24,0x3A,0x04,0x1A,
|
||||
0x52,0x82,0x0C,0x82,0x00,0x00,0x00,0x28,0x66,0x00,0x00,0x04,0x42,0x82,0x23,0xC2,
|
||||
0x00,0x00,0x19,0x98,0x02,0x70,0xF0,0x00,0x19,0x90,0x08,0xF0,0x00,0x1F,0x19,0x90,
|
||||
0x22,0x30,0x09,0xB0,0x00,0x00,0x19,0x88,0x52,0x81,0x0C,0x81,0x00,0x00,0x00,0x1E,
|
||||
0x66,0x00,0x00,0x04,0x42,0x81,0x21,0x81,0x09,0xB0,0x00,0x00,0x19,0x88,0x60,0x00,
|
||||
0xFE,0xF8,0x24,0x30,0x09,0xB0,0x00,0x00,0x10,0x04,0x52,0xB0,0x29,0xA0,0x00,0x08,
|
||||
0x60,0x00,0xFF,0xC2,0x24,0x30,0x09,0xB0,0x00,0x00,0x10,0x04,0x52,0xB0,0x29,0xA0,
|
||||
0x00,0x0C,0x60,0x00,0xFF,0xB0,0x4E,0x75,0x4A,0xB0,0x09,0xB0,0x00,0x00,0x19,0x78,
|
||||
0x67,0x00,0x00,0x86,0x22,0x30,0x09,0xB0,0x00,0x00,0x19,0x58,0x24,0x01,0xE7,0x89,
|
||||
0xD2,0xB0,0x09,0xB0,0x00,0x00,0x18,0x72,0x36,0x30,0x19,0x90,0x08,0x03,0x00,0x0F,
|
||||
0x66,0x00,0x00,0x66,0x8C,0xB0,0x09,0xB0,0x00,0x00,0x18,0xA2,0x53,0xB0,0x09,0xB0,
|
||||
0x00,0x00,0x19,0x78,0x22,0x30,0x09,0xB0,0x00,0x00,0x19,0x58,0x52,0x81,0x0C,0x81,
|
||||
0x00,0x00,0x00,0x0A,0x66,0x00,0x00,0x04,0x42,0x81,0x21,0x81,0x09,0xB0,0x00,0x00,
|
||||
0x19,0x58,0x4C,0x3C,0x20,0x00,0x00,0x00,0x00,0x0C,0xD4,0xB0,0x09,0xB0,0x00,0x00,
|
||||
0x10,0x04,0x06,0x82,0x00,0x00,0x00,0x1C,0x08,0x03,0x00,0x01,0x66,0x00,0x00,0x0E,
|
||||
0x21,0xBC,0x00,0x00,0x00,0x20,0x29,0x90,0x60,0x00,0xFF,0x7E,0x21,0xBC,0x00,0x00,
|
||||
0x00,0x30,0x29,0x90,0x60,0x00,0xFF,0x72,0x4E,0x75,0x2F,0x00,0x40,0xE7,0x20,0x39,
|
||||
0xFF,0xF9,0x01,0x28,0x08,0x00,0x00,0x04,0x66,0x00,0x00,0x2C,0x4E,0x72,0x22,0x00,
|
||||
0x46,0xFC,0x27,0x00,0x60,0x00,0xFF,0xE8,0x2F,0x00,0x40,0xE7,0x20,0x39,0xFF,0xF9,
|
||||
0x01,0x28,0x08,0x00,0x00,0x0C,0x66,0x00,0x00,0x0E,0x4E,0x72,0x22,0x00,0x46,0xFC,
|
||||
0x27,0x00,0x60,0x00,0xFF,0xE8,0x46,0xDF,0x20,0x1F,0x4E,0x75,0x2F,0x00,0x20,0x39,
|
||||
0xFF,0xF9,0x00,0xE0,0x23,0xC0,0xFF,0xF9,0x00,0xE0,0x81,0xB9,0x00,0x00,0x19,0x54,
|
||||
0x23,0xFC,0x00,0x00,0x09,0x09,0xFF,0xF9,0x01,0x28,0x20,0x1F,0x4E,0x73,0x00,0xB9,
|
||||
0x00,0x00,0x00,0x00,0xFF,0xFC,0x16,0x10,0x00,0xB9,0x00,0x00,0x10,0x00,0x00,0x00,
|
||||
0x19,0x54,0x23,0xFC,0x40,0x00,0x00,0x00,0xFF,0xFC,0x15,0x4C,0x4E,0x73,0x00,0xB9,
|
||||
0x00,0x00,0x00,0x00,0xFF,0xFC,0x16,0x30,0x00,0xB9,0x00,0x00,0x20,0x00,0x00,0x00,
|
||||
0x19,0x54,0x23,0xFC,0x20,0x00,0x00,0x00,0xFF,0xFC,0x15,0x4C,0x4E,0x73,0x00,0xB9,
|
||||
0x00,0x00,0x00,0x00,0xFF,0xFC,0x16,0x50,0x00,0xB9,0x00,0x00,0x40,0x00,0x00,0x00,
|
||||
0x19,0x54,0x23,0xFC,0x10,0x00,0x00,0x00,0xFF,0xFC,0x15,0x4C,0x4E,0x73,0x00,0xB9,
|
||||
0x00,0x00,0x00,0x00,0xFF,0xFC,0x16,0x70,0x00,0xB9,0x00,0x00,0x80,0x00,0x00,0x00,
|
||||
0x19,0x54,0x23,0xFC,0x08,0x00,0x00,0x00,0xFF,0xFC,0x15,0x4C,0x4E,0x73,0x4E,0x73,
|
||||
0x2F,0x00,0x2F,0x01,0x2F,0x02,0x2F,0x08,0x2F,0x09,0x42,0x80,0x20,0x7C,0xFF,0xFB,
|
||||
0x00,0x00,0x32,0x10,0x02,0x81,0x00,0x00,0x00,0xE7,0x0C,0x41,0x00,0x42,0x66,0x00,
|
||||
0x00,0x0A,0x32,0x3C,0x0E,0x08,0x60,0x00,0x00,0x3E,0x0C,0x41,0x00,0x63,0x66,0x00,
|
||||
0x00,0x0A,0x32,0x3C,0x04,0x08,0x60,0x00,0x00,0x2E,0x0C,0x41,0x00,0x84,0x66,0x00,
|
||||
0x00,0x0A,0x32,0x3C,0x02,0x08,0x60,0x00,0x00,0x1E,0x0C,0x41,0x00,0xA5,0x66,0x00,
|
||||
0x00,0x0A,0x32,0x3C,0x0D,0x08,0x60,0x00,0x00,0x0E,0x32,0x3C,0x00,0x08,0x34,0x3C,
|
||||
0x80,0xE7,0x60,0x00,0x00,0x14,0x34,0x30,0x09,0xB0,0x00,0x00,0x19,0xAA,0x02,0x42,
|
||||
0x30,0x00,0x82,0x42,0x34,0x3C,0x80,0xFF,0xB2,0x70,0x09,0xB0,0x00,0x00,0x19,0xAC,
|
||||
0x67,0x00,0x00,0x0C,0x31,0x81,0x09,0xB0,0x00,0x00,0x19,0xAC,0x30,0x81,0x32,0x39,
|
||||
0xFF,0xFC,0x15,0x66,0xC2,0x70,0x09,0xB0,0x00,0x00,0x19,0x02,0x67,0x00,0x00,0x0C,
|
||||
0x32,0x10,0x02,0x41,0xFF,0xF7,0x60,0x00,0x00,0x08,0x32,0x10,0x00,0x41,0x00,0x08,
|
||||
0xC2,0x42,0x22,0x70,0x09,0xB0,0x00,0x00,0x10,0x04,0xB2,0xA9,0x00,0x04,0x67,0x00,
|
||||
0x00,0x12,0x23,0x41,0x00,0x04,0x23,0xF0,0x09,0xB0,0x00,0x00,0x18,0xB2,0xFF,0xF9,
|
||||
0x00,0xE4,0x54,0x88,0x58,0x80,0x0C,0x80,0x00,0x00,0x00,0x10,0x66,0x00,0xFF,0x34,
|
||||
0x22,0x5F,0x20,0x5F,0x24,0x1F,0x22,0x1F,0x20,0x1F,0x4E,0x75,0x61,0x00,0xFF,0x12,
|
||||
0x4E,0x73,0xFF,0xFC,0x16,0x00,0xFF,0xFC,0x16,0x20,0xFF,0xFC,0x16,0x40,0xFF,0xFC,
|
||||
0x16,0x60,0xFF,0xFC,0x0C,0x00,0xFF,0xFC,0x0D,0x00,0xFF,0xFC,0x0E,0x00,0xFF,0xFC,
|
||||
0x0F,0x00,0xFF,0xFC,0x00,0x00,0xFF,0xFC,0x01,0x40,0xFF,0xFC,0x02,0x80,0xFF,0xFC,
|
||||
0x03,0xC0,0xFF,0xFC,0x00,0x50,0xFF,0xFC,0x01,0x90,0xFF,0xFC,0x02,0xD0,0xFF,0xFC,
|
||||
0x04,0x10,0x00,0x00,0x40,0x00,0x00,0x01,0x2F,0x60,0x00,0x02,0x1E,0xC0,0x00,0x03,
|
||||
0x0E,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,
|
||||
0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x80,0x00,0x00,
|
||||
0x01,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x12,0x00,0x00,
|
||||
0x00,0x13,0x00,0x00,0x00,0x2C,0x00,0x00,0x3E,0x00,0x00,0x2C,0x00,0x00,0x3E,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x2D,0x00,0x00,0x3F,0x00,0x00,0x2D,0x00,0x00,0x3F,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,
|
||||
0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x80,0x00,0x00,0x02,0x00,0x00,0x00,0x08,0x00,
|
||||
0x77,0x61,0x6E,0x58,0x4C,0x20,0x66,0x69,0x72,0x6D,0x77,0x61,0x72,0x65,0x0A,0x43,
|
||||
0x6F,0x70,0x79,0x72,0x69,0x67,0x68,0x74,0x20,0x28,0x43,0x29,0x20,0x32,0x30,0x30,
|
||||
0x33,0x20,0x4B,0x72,0x7A,0x79,0x73,0x7A,0x74,0x6F,0x66,0x20,0x48,0x61,0x6C,0x61,
|
||||
0x73,0x61,0x20,0x3C,0x6B,0x68,0x63,0x40,0x70,0x6D,0x2E,0x77,0x61,0x77,0x2E,0x70,
|
||||
0x6C,0x3E,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
};
|
||||
831
drivers/net/wan/x25_asy.c
Normal file
831
drivers/net/wan/x25_asy.c
Normal file
@@ -0,0 +1,831 @@
|
||||
/*
|
||||
* Things to sort out:
|
||||
*
|
||||
* o tbusy handling
|
||||
* o allow users to set the parameters
|
||||
* o sync/async switching ?
|
||||
*
|
||||
* Note: This does _not_ implement CCITT X.25 asynchronous framing
|
||||
* recommendations. Its primarily for testing purposes. If you wanted
|
||||
* to do CCITT then in theory all you need is to nick the HDLC async
|
||||
* checksum routines from ppp.c
|
||||
* Changes:
|
||||
*
|
||||
* 2000-10-29 Henner Eisen lapb_data_indication() return status.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/x25.h>
|
||||
#include <linux/lapb.h>
|
||||
#include <linux/init.h>
|
||||
#include "x25_asy.h"
|
||||
|
||||
#include <net/x25device.h>
|
||||
|
||||
static struct net_device **x25_asy_devs;
|
||||
static int x25_asy_maxdev = SL_NRUNIT;
|
||||
|
||||
module_param(x25_asy_maxdev, int, 0);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int x25_asy_esc(unsigned char *p, unsigned char *d, int len);
|
||||
static void x25_asy_unesc(struct x25_asy *sl, unsigned char c);
|
||||
static void x25_asy_setup(struct net_device *dev);
|
||||
|
||||
/* Find a free X.25 channel, and link in this `tty' line. */
|
||||
static struct x25_asy *x25_asy_alloc(void)
|
||||
{
|
||||
struct net_device *dev = NULL;
|
||||
struct x25_asy *sl;
|
||||
int i;
|
||||
|
||||
if (x25_asy_devs == NULL)
|
||||
return NULL; /* Master array missing ! */
|
||||
|
||||
for (i = 0; i < x25_asy_maxdev; i++) {
|
||||
dev = x25_asy_devs[i];
|
||||
|
||||
/* Not allocated ? */
|
||||
if (dev == NULL)
|
||||
break;
|
||||
|
||||
sl = dev->priv;
|
||||
/* Not in use ? */
|
||||
if (!test_and_set_bit(SLF_INUSE, &sl->flags))
|
||||
return sl;
|
||||
}
|
||||
|
||||
|
||||
/* Sorry, too many, all slots in use */
|
||||
if (i >= x25_asy_maxdev)
|
||||
return NULL;
|
||||
|
||||
/* If no channels are available, allocate one */
|
||||
if (!dev) {
|
||||
char name[IFNAMSIZ];
|
||||
sprintf(name, "x25asy%d", i);
|
||||
|
||||
dev = alloc_netdev(sizeof(struct x25_asy),
|
||||
name, x25_asy_setup);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
/* Initialize channel control data */
|
||||
sl = dev->priv;
|
||||
dev->base_addr = i;
|
||||
|
||||
/* register device so that it can be ifconfig'ed */
|
||||
if (register_netdev(dev) == 0) {
|
||||
/* (Re-)Set the INUSE bit. Very Important! */
|
||||
set_bit(SLF_INUSE, &sl->flags);
|
||||
x25_asy_devs[i] = dev;
|
||||
return sl;
|
||||
} else {
|
||||
printk("x25_asy_alloc() - register_netdev() failure.\n");
|
||||
free_netdev(dev);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Free an X.25 channel. */
|
||||
static void x25_asy_free(struct x25_asy *sl)
|
||||
{
|
||||
/* Free all X.25 frame buffers. */
|
||||
kfree(sl->rbuff);
|
||||
sl->rbuff = NULL;
|
||||
kfree(sl->xbuff);
|
||||
sl->xbuff = NULL;
|
||||
|
||||
if (!test_and_clear_bit(SLF_INUSE, &sl->flags)) {
|
||||
printk("%s: x25_asy_free for already free unit.\n", sl->dev->name);
|
||||
}
|
||||
}
|
||||
|
||||
static int x25_asy_change_mtu(struct net_device *dev, int newmtu)
|
||||
{
|
||||
struct x25_asy *sl = dev->priv;
|
||||
unsigned char *xbuff, *rbuff;
|
||||
int len = 2* newmtu;
|
||||
|
||||
xbuff = kmalloc(len + 4, GFP_ATOMIC);
|
||||
rbuff = kmalloc(len + 4, GFP_ATOMIC);
|
||||
|
||||
if (xbuff == NULL || rbuff == NULL)
|
||||
{
|
||||
printk("%s: unable to grow X.25 buffers, MTU change cancelled.\n",
|
||||
dev->name);
|
||||
kfree(xbuff);
|
||||
kfree(rbuff);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_bh(&sl->lock);
|
||||
xbuff = xchg(&sl->xbuff, xbuff);
|
||||
if (sl->xleft) {
|
||||
if (sl->xleft <= len) {
|
||||
memcpy(sl->xbuff, sl->xhead, sl->xleft);
|
||||
} else {
|
||||
sl->xleft = 0;
|
||||
sl->stats.tx_dropped++;
|
||||
}
|
||||
}
|
||||
sl->xhead = sl->xbuff;
|
||||
|
||||
rbuff = xchg(&sl->rbuff, rbuff);
|
||||
if (sl->rcount) {
|
||||
if (sl->rcount <= len) {
|
||||
memcpy(sl->rbuff, rbuff, sl->rcount);
|
||||
} else {
|
||||
sl->rcount = 0;
|
||||
sl->stats.rx_over_errors++;
|
||||
set_bit(SLF_ERROR, &sl->flags);
|
||||
}
|
||||
}
|
||||
|
||||
dev->mtu = newmtu;
|
||||
sl->buffsize = len;
|
||||
|
||||
spin_unlock_bh(&sl->lock);
|
||||
|
||||
kfree(xbuff);
|
||||
kfree(rbuff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Set the "sending" flag. This must be atomic, hence the ASM. */
|
||||
|
||||
static inline void x25_asy_lock(struct x25_asy *sl)
|
||||
{
|
||||
netif_stop_queue(sl->dev);
|
||||
}
|
||||
|
||||
|
||||
/* Clear the "sending" flag. This must be atomic, hence the ASM. */
|
||||
|
||||
static inline void x25_asy_unlock(struct x25_asy *sl)
|
||||
{
|
||||
netif_wake_queue(sl->dev);
|
||||
}
|
||||
|
||||
/* Send one completely decapsulated IP datagram to the IP layer. */
|
||||
|
||||
static void x25_asy_bump(struct x25_asy *sl)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int count;
|
||||
int err;
|
||||
|
||||
count = sl->rcount;
|
||||
sl->stats.rx_bytes+=count;
|
||||
|
||||
skb = dev_alloc_skb(count+1);
|
||||
if (skb == NULL)
|
||||
{
|
||||
printk("%s: memory squeeze, dropping packet.\n", sl->dev->name);
|
||||
sl->stats.rx_dropped++;
|
||||
return;
|
||||
}
|
||||
skb_push(skb,1); /* LAPB internal control */
|
||||
memcpy(skb_put(skb,count), sl->rbuff, count);
|
||||
skb->protocol = x25_type_trans(skb, sl->dev);
|
||||
if((err=lapb_data_received(skb->dev, skb))!=LAPB_OK)
|
||||
{
|
||||
kfree_skb(skb);
|
||||
printk(KERN_DEBUG "x25_asy: data received err - %d\n",err);
|
||||
}
|
||||
else
|
||||
{
|
||||
netif_rx(skb);
|
||||
sl->dev->last_rx = jiffies;
|
||||
sl->stats.rx_packets++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Encapsulate one IP datagram and stuff into a TTY queue. */
|
||||
static void x25_asy_encaps(struct x25_asy *sl, unsigned char *icp, int len)
|
||||
{
|
||||
unsigned char *p;
|
||||
int actual, count, mtu = sl->dev->mtu;
|
||||
|
||||
if (len > mtu)
|
||||
{ /* Sigh, shouldn't occur BUT ... */
|
||||
len = mtu;
|
||||
printk ("%s: truncating oversized transmit packet!\n", sl->dev->name);
|
||||
sl->stats.tx_dropped++;
|
||||
x25_asy_unlock(sl);
|
||||
return;
|
||||
}
|
||||
|
||||
p = icp;
|
||||
count = x25_asy_esc(p, (unsigned char *) sl->xbuff, len);
|
||||
|
||||
/* Order of next two lines is *very* important.
|
||||
* When we are sending a little amount of data,
|
||||
* the transfer may be completed inside driver.write()
|
||||
* routine, because it's running with interrupts enabled.
|
||||
* In this case we *never* got WRITE_WAKEUP event,
|
||||
* if we did not request it before write operation.
|
||||
* 14 Oct 1994 Dmitry Gorodchanin.
|
||||
*/
|
||||
sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
|
||||
actual = sl->tty->driver->write(sl->tty, sl->xbuff, count);
|
||||
sl->xleft = count - actual;
|
||||
sl->xhead = sl->xbuff + actual;
|
||||
/* VSV */
|
||||
clear_bit(SLF_OUTWAIT, &sl->flags); /* reset outfill flag */
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the driver when there's room for more data. If we have
|
||||
* more packets to send, we send them here.
|
||||
*/
|
||||
static void x25_asy_write_wakeup(struct tty_struct *tty)
|
||||
{
|
||||
int actual;
|
||||
struct x25_asy *sl = (struct x25_asy *) tty->disc_data;
|
||||
|
||||
/* First make sure we're connected. */
|
||||
if (!sl || sl->magic != X25_ASY_MAGIC || !netif_running(sl->dev))
|
||||
return;
|
||||
|
||||
if (sl->xleft <= 0)
|
||||
{
|
||||
/* Now serial buffer is almost free & we can start
|
||||
* transmission of another packet */
|
||||
sl->stats.tx_packets++;
|
||||
tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
|
||||
x25_asy_unlock(sl);
|
||||
return;
|
||||
}
|
||||
|
||||
actual = tty->driver->write(tty, sl->xhead, sl->xleft);
|
||||
sl->xleft -= actual;
|
||||
sl->xhead += actual;
|
||||
}
|
||||
|
||||
static void x25_asy_timeout(struct net_device *dev)
|
||||
{
|
||||
struct x25_asy *sl = (struct x25_asy*)(dev->priv);
|
||||
|
||||
spin_lock(&sl->lock);
|
||||
if (netif_queue_stopped(dev)) {
|
||||
/* May be we must check transmitter timeout here ?
|
||||
* 14 Oct 1994 Dmitry Gorodchanin.
|
||||
*/
|
||||
printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
|
||||
(sl->tty->driver->chars_in_buffer(sl->tty) || sl->xleft) ?
|
||||
"bad line quality" : "driver error");
|
||||
sl->xleft = 0;
|
||||
sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
|
||||
x25_asy_unlock(sl);
|
||||
}
|
||||
spin_unlock(&sl->lock);
|
||||
}
|
||||
|
||||
/* Encapsulate an IP datagram and kick it into a TTY queue. */
|
||||
|
||||
static int x25_asy_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct x25_asy *sl = (struct x25_asy*)(dev->priv);
|
||||
int err;
|
||||
|
||||
if (!netif_running(sl->dev)) {
|
||||
printk("%s: xmit call when iface is down\n", dev->name);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch(skb->data[0])
|
||||
{
|
||||
case 0x00:break;
|
||||
case 0x01: /* Connection request .. do nothing */
|
||||
if((err=lapb_connect_request(dev))!=LAPB_OK)
|
||||
printk(KERN_ERR "x25_asy: lapb_connect_request error - %d\n", err);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
case 0x02: /* Disconnect request .. do nothing - hang up ?? */
|
||||
if((err=lapb_disconnect_request(dev))!=LAPB_OK)
|
||||
printk(KERN_ERR "x25_asy: lapb_disconnect_request error - %d\n", err);
|
||||
default:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
skb_pull(skb,1); /* Remove control byte */
|
||||
/*
|
||||
* If we are busy already- too bad. We ought to be able
|
||||
* to queue things at this point, to allow for a little
|
||||
* frame buffer. Oh well...
|
||||
* -----------------------------------------------------
|
||||
* I hate queues in X.25 driver. May be it's efficient,
|
||||
* but for me latency is more important. ;)
|
||||
* So, no queues !
|
||||
* 14 Oct 1994 Dmitry Gorodchanin.
|
||||
*/
|
||||
|
||||
if((err=lapb_data_request(dev,skb))!=LAPB_OK)
|
||||
{
|
||||
printk(KERN_ERR "lapbeth: lapb_data_request error - %d\n", err);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* LAPB interface boilerplate
|
||||
*/
|
||||
|
||||
/*
|
||||
* Called when I frame data arrives. We did the work above - throw it
|
||||
* at the net layer.
|
||||
*/
|
||||
|
||||
static int x25_asy_data_indication(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
skb->dev->last_rx = jiffies;
|
||||
return netif_rx(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Data has emerged from the LAPB protocol machine. We don't handle
|
||||
* busy cases too well. Its tricky to see how to do this nicely -
|
||||
* perhaps lapb should allow us to bounce this ?
|
||||
*/
|
||||
|
||||
static void x25_asy_data_transmit(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct x25_asy *sl=dev->priv;
|
||||
|
||||
spin_lock(&sl->lock);
|
||||
if (netif_queue_stopped(sl->dev) || sl->tty == NULL)
|
||||
{
|
||||
spin_unlock(&sl->lock);
|
||||
printk(KERN_ERR "x25_asy: tbusy drop\n");
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
/* We were not busy, so we are now... :-) */
|
||||
if (skb != NULL)
|
||||
{
|
||||
x25_asy_lock(sl);
|
||||
sl->stats.tx_bytes+=skb->len;
|
||||
x25_asy_encaps(sl, skb->data, skb->len);
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
spin_unlock(&sl->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* LAPB connection establish/down information.
|
||||
*/
|
||||
|
||||
static void x25_asy_connected(struct net_device *dev, int reason)
|
||||
{
|
||||
struct x25_asy *sl = dev->priv;
|
||||
struct sk_buff *skb;
|
||||
unsigned char *ptr;
|
||||
|
||||
if ((skb = dev_alloc_skb(1)) == NULL) {
|
||||
printk(KERN_ERR "lapbeth: out of memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = skb_put(skb, 1);
|
||||
*ptr = 0x01;
|
||||
|
||||
skb->protocol = x25_type_trans(skb, sl->dev);
|
||||
netif_rx(skb);
|
||||
sl->dev->last_rx = jiffies;
|
||||
}
|
||||
|
||||
static void x25_asy_disconnected(struct net_device *dev, int reason)
|
||||
{
|
||||
struct x25_asy *sl = dev->priv;
|
||||
struct sk_buff *skb;
|
||||
unsigned char *ptr;
|
||||
|
||||
if ((skb = dev_alloc_skb(1)) == NULL) {
|
||||
printk(KERN_ERR "x25_asy: out of memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = skb_put(skb, 1);
|
||||
*ptr = 0x02;
|
||||
|
||||
skb->protocol = x25_type_trans(skb, sl->dev);
|
||||
netif_rx(skb);
|
||||
sl->dev->last_rx = jiffies;
|
||||
}
|
||||
|
||||
static struct lapb_register_struct x25_asy_callbacks = {
|
||||
.connect_confirmation = x25_asy_connected,
|
||||
.connect_indication = x25_asy_connected,
|
||||
.disconnect_confirmation = x25_asy_disconnected,
|
||||
.disconnect_indication = x25_asy_disconnected,
|
||||
.data_indication = x25_asy_data_indication,
|
||||
.data_transmit = x25_asy_data_transmit,
|
||||
|
||||
};
|
||||
|
||||
|
||||
/* Open the low-level part of the X.25 channel. Easy! */
|
||||
static int x25_asy_open(struct net_device *dev)
|
||||
{
|
||||
struct x25_asy *sl = (struct x25_asy*)(dev->priv);
|
||||
unsigned long len;
|
||||
int err;
|
||||
|
||||
if (sl->tty == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Allocate the X.25 frame buffers:
|
||||
*
|
||||
* rbuff Receive buffer.
|
||||
* xbuff Transmit buffer.
|
||||
*/
|
||||
|
||||
len = dev->mtu * 2;
|
||||
|
||||
sl->rbuff = kmalloc(len + 4, GFP_KERNEL);
|
||||
if (sl->rbuff == NULL) {
|
||||
goto norbuff;
|
||||
}
|
||||
sl->xbuff = kmalloc(len + 4, GFP_KERNEL);
|
||||
if (sl->xbuff == NULL) {
|
||||
goto noxbuff;
|
||||
}
|
||||
|
||||
sl->buffsize = len;
|
||||
sl->rcount = 0;
|
||||
sl->xleft = 0;
|
||||
sl->flags &= (1 << SLF_INUSE); /* Clear ESCAPE & ERROR flags */
|
||||
|
||||
netif_start_queue(dev);
|
||||
|
||||
/*
|
||||
* Now attach LAPB
|
||||
*/
|
||||
if((err=lapb_register(dev, &x25_asy_callbacks))==LAPB_OK)
|
||||
return 0;
|
||||
|
||||
/* Cleanup */
|
||||
kfree(sl->xbuff);
|
||||
noxbuff:
|
||||
kfree(sl->rbuff);
|
||||
norbuff:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
||||
/* Close the low-level part of the X.25 channel. Easy! */
|
||||
static int x25_asy_close(struct net_device *dev)
|
||||
{
|
||||
struct x25_asy *sl = (struct x25_asy*)(dev->priv);
|
||||
int err;
|
||||
|
||||
spin_lock(&sl->lock);
|
||||
if (sl->tty)
|
||||
sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
|
||||
|
||||
netif_stop_queue(dev);
|
||||
sl->rcount = 0;
|
||||
sl->xleft = 0;
|
||||
if((err=lapb_unregister(dev))!=LAPB_OK)
|
||||
printk(KERN_ERR "x25_asy_close: lapb_unregister error -%d\n",err);
|
||||
spin_unlock(&sl->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the 'receiver data ready' interrupt.
|
||||
* This function is called by the 'tty_io' module in the kernel when
|
||||
* a block of X.25 data has been received, which can now be decapsulated
|
||||
* and sent on to some IP layer for further processing.
|
||||
*/
|
||||
|
||||
static void x25_asy_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
|
||||
{
|
||||
struct x25_asy *sl = (struct x25_asy *) tty->disc_data;
|
||||
|
||||
if (!sl || sl->magic != X25_ASY_MAGIC || !netif_running(sl->dev))
|
||||
return;
|
||||
|
||||
|
||||
/* Read the characters out of the buffer */
|
||||
while (count--) {
|
||||
if (fp && *fp++) {
|
||||
if (!test_and_set_bit(SLF_ERROR, &sl->flags)) {
|
||||
sl->stats.rx_errors++;
|
||||
}
|
||||
cp++;
|
||||
continue;
|
||||
}
|
||||
x25_asy_unesc(sl, *cp++);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the high-level part of the X.25 channel.
|
||||
* This function is called by the TTY module when the
|
||||
* X.25 line discipline is called for. Because we are
|
||||
* sure the tty line exists, we only have to link it to
|
||||
* a free X.25 channel...
|
||||
*/
|
||||
|
||||
static int x25_asy_open_tty(struct tty_struct *tty)
|
||||
{
|
||||
struct x25_asy *sl = (struct x25_asy *) tty->disc_data;
|
||||
int err;
|
||||
|
||||
/* First make sure we're not already connected. */
|
||||
if (sl && sl->magic == X25_ASY_MAGIC) {
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
/* OK. Find a free X.25 channel to use. */
|
||||
if ((sl = x25_asy_alloc()) == NULL) {
|
||||
return -ENFILE;
|
||||
}
|
||||
|
||||
sl->tty = tty;
|
||||
tty->disc_data = sl;
|
||||
tty->receive_room = 65536;
|
||||
if (tty->driver->flush_buffer) {
|
||||
tty->driver->flush_buffer(tty);
|
||||
}
|
||||
if (tty->ldisc.flush_buffer) {
|
||||
tty->ldisc.flush_buffer(tty);
|
||||
}
|
||||
|
||||
/* Restore default settings */
|
||||
sl->dev->type = ARPHRD_X25;
|
||||
|
||||
/* Perform the low-level X.25 async init */
|
||||
if ((err = x25_asy_open(sl->dev)))
|
||||
return err;
|
||||
|
||||
/* Done. We have linked the TTY line to a channel. */
|
||||
return sl->dev->base_addr;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Close down an X.25 channel.
|
||||
* This means flushing out any pending queues, and then restoring the
|
||||
* TTY line discipline to what it was before it got hooked to X.25
|
||||
* (which usually is TTY again).
|
||||
*/
|
||||
static void x25_asy_close_tty(struct tty_struct *tty)
|
||||
{
|
||||
struct x25_asy *sl = (struct x25_asy *) tty->disc_data;
|
||||
|
||||
/* First make sure we're connected. */
|
||||
if (!sl || sl->magic != X25_ASY_MAGIC)
|
||||
return;
|
||||
|
||||
if (sl->dev->flags & IFF_UP)
|
||||
{
|
||||
(void) dev_close(sl->dev);
|
||||
}
|
||||
|
||||
tty->disc_data = NULL;
|
||||
sl->tty = NULL;
|
||||
x25_asy_free(sl);
|
||||
}
|
||||
|
||||
|
||||
static struct net_device_stats *x25_asy_get_stats(struct net_device *dev)
|
||||
{
|
||||
struct x25_asy *sl = (struct x25_asy*)(dev->priv);
|
||||
|
||||
return &sl->stats;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* STANDARD X.25 ENCAPSULATION *
|
||||
************************************************************************/
|
||||
|
||||
int x25_asy_esc(unsigned char *s, unsigned char *d, int len)
|
||||
{
|
||||
unsigned char *ptr = d;
|
||||
unsigned char c;
|
||||
|
||||
/*
|
||||
* Send an initial END character to flush out any
|
||||
* data that may have accumulated in the receiver
|
||||
* due to line noise.
|
||||
*/
|
||||
|
||||
*ptr++ = X25_END; /* Send 10111110 bit seq */
|
||||
|
||||
/*
|
||||
* For each byte in the packet, send the appropriate
|
||||
* character sequence, according to the X.25 protocol.
|
||||
*/
|
||||
|
||||
while (len-- > 0)
|
||||
{
|
||||
switch(c = *s++)
|
||||
{
|
||||
case X25_END:
|
||||
*ptr++ = X25_ESC;
|
||||
*ptr++ = X25_ESCAPE(X25_END);
|
||||
break;
|
||||
case X25_ESC:
|
||||
*ptr++ = X25_ESC;
|
||||
*ptr++ = X25_ESCAPE(X25_ESC);
|
||||
break;
|
||||
default:
|
||||
*ptr++ = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*ptr++ = X25_END;
|
||||
return (ptr - d);
|
||||
}
|
||||
|
||||
static void x25_asy_unesc(struct x25_asy *sl, unsigned char s)
|
||||
{
|
||||
|
||||
switch(s)
|
||||
{
|
||||
case X25_END:
|
||||
if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && (sl->rcount > 2))
|
||||
{
|
||||
x25_asy_bump(sl);
|
||||
}
|
||||
clear_bit(SLF_ESCAPE, &sl->flags);
|
||||
sl->rcount = 0;
|
||||
return;
|
||||
|
||||
case X25_ESC:
|
||||
set_bit(SLF_ESCAPE, &sl->flags);
|
||||
return;
|
||||
|
||||
case X25_ESCAPE(X25_ESC):
|
||||
case X25_ESCAPE(X25_END):
|
||||
if (test_and_clear_bit(SLF_ESCAPE, &sl->flags))
|
||||
s = X25_UNESCAPE(s);
|
||||
break;
|
||||
}
|
||||
if (!test_bit(SLF_ERROR, &sl->flags))
|
||||
{
|
||||
if (sl->rcount < sl->buffsize)
|
||||
{
|
||||
sl->rbuff[sl->rcount++] = s;
|
||||
return;
|
||||
}
|
||||
sl->stats.rx_over_errors++;
|
||||
set_bit(SLF_ERROR, &sl->flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Perform I/O control on an active X.25 channel. */
|
||||
static int x25_asy_ioctl(struct tty_struct *tty, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct x25_asy *sl = (struct x25_asy *) tty->disc_data;
|
||||
|
||||
/* First make sure we're connected. */
|
||||
if (!sl || sl->magic != X25_ASY_MAGIC)
|
||||
return -EINVAL;
|
||||
|
||||
switch(cmd) {
|
||||
case SIOCGIFNAME:
|
||||
if (copy_to_user((void __user *)arg, sl->dev->name,
|
||||
strlen(sl->dev->name) + 1))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
case SIOCSIFHWADDR:
|
||||
return -EINVAL;
|
||||
/* Allow stty to read, but not set, the serial port */
|
||||
case TCGETS:
|
||||
case TCGETA:
|
||||
return n_tty_ioctl(tty, file, cmd, arg);
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
|
||||
static int x25_asy_open_dev(struct net_device *dev)
|
||||
{
|
||||
struct x25_asy *sl = (struct x25_asy*)(dev->priv);
|
||||
if(sl->tty==NULL)
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialise the X.25 driver. Called by the device init code */
|
||||
static void x25_asy_setup(struct net_device *dev)
|
||||
{
|
||||
struct x25_asy *sl = dev->priv;
|
||||
|
||||
sl->magic = X25_ASY_MAGIC;
|
||||
sl->dev = dev;
|
||||
spin_lock_init(&sl->lock);
|
||||
set_bit(SLF_INUSE, &sl->flags);
|
||||
|
||||
/*
|
||||
* Finish setting up the DEVICE info.
|
||||
*/
|
||||
|
||||
dev->mtu = SL_MTU;
|
||||
dev->hard_start_xmit = x25_asy_xmit;
|
||||
dev->tx_timeout = x25_asy_timeout;
|
||||
dev->watchdog_timeo = HZ*20;
|
||||
dev->open = x25_asy_open_dev;
|
||||
dev->stop = x25_asy_close;
|
||||
dev->get_stats = x25_asy_get_stats;
|
||||
dev->change_mtu = x25_asy_change_mtu;
|
||||
dev->hard_header_len = 0;
|
||||
dev->addr_len = 0;
|
||||
dev->type = ARPHRD_X25;
|
||||
dev->tx_queue_len = 10;
|
||||
|
||||
/* New-style flags. */
|
||||
dev->flags = IFF_NOARP;
|
||||
}
|
||||
|
||||
static struct tty_ldisc x25_ldisc = {
|
||||
.owner = THIS_MODULE,
|
||||
.magic = TTY_LDISC_MAGIC,
|
||||
.name = "X.25",
|
||||
.open = x25_asy_open_tty,
|
||||
.close = x25_asy_close_tty,
|
||||
.ioctl = x25_asy_ioctl,
|
||||
.receive_buf = x25_asy_receive_buf,
|
||||
.write_wakeup = x25_asy_write_wakeup,
|
||||
};
|
||||
|
||||
static int __init init_x25_asy(void)
|
||||
{
|
||||
if (x25_asy_maxdev < 4)
|
||||
x25_asy_maxdev = 4; /* Sanity */
|
||||
|
||||
printk(KERN_INFO "X.25 async: version 0.00 ALPHA "
|
||||
"(dynamic channels, max=%d).\n", x25_asy_maxdev );
|
||||
|
||||
x25_asy_devs = kmalloc(sizeof(struct net_device *)*x25_asy_maxdev,
|
||||
GFP_KERNEL);
|
||||
if (!x25_asy_devs) {
|
||||
printk(KERN_WARNING "X25 async: Can't allocate x25_asy_ctrls[] "
|
||||
"array! Uaargh! (-> No X.25 available)\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(x25_asy_devs, 0, sizeof(struct net_device *)*x25_asy_maxdev);
|
||||
|
||||
return tty_register_ldisc(N_X25, &x25_ldisc);
|
||||
}
|
||||
|
||||
|
||||
static void __exit exit_x25_asy(void)
|
||||
{
|
||||
struct net_device *dev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < x25_asy_maxdev; i++) {
|
||||
dev = x25_asy_devs[i];
|
||||
if (dev) {
|
||||
struct x25_asy *sl = dev->priv;
|
||||
|
||||
spin_lock_bh(&sl->lock);
|
||||
if (sl->tty)
|
||||
tty_hangup(sl->tty);
|
||||
|
||||
spin_unlock_bh(&sl->lock);
|
||||
/*
|
||||
* VSV = if dev->start==0, then device
|
||||
* unregistered while close proc.
|
||||
*/
|
||||
unregister_netdev(dev);
|
||||
free_netdev(dev);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(x25_asy_devs);
|
||||
tty_unregister_ldisc(N_X25);
|
||||
}
|
||||
|
||||
module_init(init_x25_asy);
|
||||
module_exit(exit_x25_asy);
|
||||
50
drivers/net/wan/x25_asy.h
Normal file
50
drivers/net/wan/x25_asy.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef _LINUX_X25_ASY_H
|
||||
#define _LINUX_X25_ASY_H
|
||||
|
||||
/* X.25 asy configuration. */
|
||||
#define SL_NRUNIT 256 /* MAX number of X.25 channels;
|
||||
This can be overridden with
|
||||
insmod -ox25_asy_maxdev=nnn */
|
||||
#define SL_MTU 256
|
||||
|
||||
/* X25 async protocol characters. */
|
||||
#define X25_END 0x7E /* indicates end of frame */
|
||||
#define X25_ESC 0x7D /* indicates byte stuffing */
|
||||
#define X25_ESCAPE(x) ((x)^0x20)
|
||||
#define X25_UNESCAPE(x) ((x)^0x20)
|
||||
|
||||
|
||||
struct x25_asy {
|
||||
int magic;
|
||||
|
||||
/* Various fields. */
|
||||
spinlock_t lock;
|
||||
struct tty_struct *tty; /* ptr to TTY structure */
|
||||
struct net_device *dev; /* easy for intr handling */
|
||||
|
||||
/* These are pointers to the malloc()ed frame buffers. */
|
||||
unsigned char *rbuff; /* receiver buffer */
|
||||
int rcount; /* received chars counter */
|
||||
unsigned char *xbuff; /* transmitter buffer */
|
||||
unsigned char *xhead; /* pointer to next byte to XMIT */
|
||||
int xleft; /* bytes left in XMIT queue */
|
||||
|
||||
/* X.25 interface statistics. */
|
||||
struct net_device_stats stats;
|
||||
|
||||
int buffsize; /* Max buffers sizes */
|
||||
|
||||
unsigned long flags; /* Flag values/ mode etc */
|
||||
#define SLF_INUSE 0 /* Channel in use */
|
||||
#define SLF_ESCAPE 1 /* ESC received */
|
||||
#define SLF_ERROR 2 /* Parity, etc. error */
|
||||
#define SLF_OUTWAIT 4 /* Waiting for output */
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define X25_ASY_MAGIC 0x5303
|
||||
|
||||
extern int x25_asy_init(struct net_device *dev);
|
||||
|
||||
#endif /* _LINUX_X25_ASY.H */
|
||||
1839
drivers/net/wan/z85230.c
Normal file
1839
drivers/net/wan/z85230.c
Normal file
File diff suppressed because it is too large
Load Diff
449
drivers/net/wan/z85230.h
Normal file
449
drivers/net/wan/z85230.h
Normal file
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* Description of Z8530 Z85C30 and Z85230 communications chips
|
||||
*
|
||||
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
|
||||
* Copyright (C) 1998 Alan Cox <alan@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef _Z8530_H
|
||||
#define _Z8530_H
|
||||
|
||||
#include <linux/tty.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
/* Conversion routines to/from brg time constants from/to bits
|
||||
* per second.
|
||||
*/
|
||||
#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
|
||||
#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
|
||||
|
||||
/* The Zilog register set */
|
||||
|
||||
#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 RPRIME 16 /* Indicate a prime register access on 230 */
|
||||
|
||||
#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 PRIME 1 /* R5' etc register access (Z85C30/230 only) */
|
||||
#define ZCIE 2 /* Zero count IE */
|
||||
#define FIFOE 4 /* Z85230 only */
|
||||
#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) */
|
||||
|
||||
|
||||
/*
|
||||
* Interrupt handling functions for this SCC
|
||||
*/
|
||||
|
||||
struct z8530_channel;
|
||||
|
||||
struct z8530_irqhandler
|
||||
{
|
||||
void (*rx)(struct z8530_channel *);
|
||||
void (*tx)(struct z8530_channel *);
|
||||
void (*status)(struct z8530_channel *);
|
||||
};
|
||||
|
||||
/*
|
||||
* A channel of the Z8530
|
||||
*/
|
||||
|
||||
struct z8530_channel
|
||||
{
|
||||
struct z8530_irqhandler *irqs; /* IRQ handlers */
|
||||
/*
|
||||
* Synchronous
|
||||
*/
|
||||
u16 count; /* Buyes received */
|
||||
u16 max; /* Most we can receive this frame */
|
||||
u16 mtu; /* MTU of the device */
|
||||
u8 *dptr; /* Pointer into rx buffer */
|
||||
struct sk_buff *skb; /* Buffer dptr points into */
|
||||
struct sk_buff *skb2; /* Pending buffer */
|
||||
u8 status; /* Current DCD */
|
||||
u8 dcdcheck; /* which bit to check for line */
|
||||
u8 sync; /* Set if in sync mode */
|
||||
|
||||
u8 regs[32]; /* Register map for the chip */
|
||||
u8 pendregs[32]; /* Pending register values */
|
||||
|
||||
struct sk_buff *tx_skb; /* Buffer being transmitted */
|
||||
struct sk_buff *tx_next_skb; /* Next transmit buffer */
|
||||
u8 *tx_ptr; /* Byte pointer into the buffer */
|
||||
u8 *tx_next_ptr; /* Next pointer to use */
|
||||
u8 *tx_dma_buf[2]; /* TX flip buffers for DMA */
|
||||
u8 tx_dma_used; /* Flip buffer usage toggler */
|
||||
u16 txcount; /* Count of bytes to transmit */
|
||||
|
||||
void (*rx_function)(struct z8530_channel *, struct sk_buff *);
|
||||
|
||||
/*
|
||||
* Sync DMA
|
||||
*/
|
||||
|
||||
u8 rxdma; /* DMA channels */
|
||||
u8 txdma;
|
||||
u8 rxdma_on; /* DMA active if flag set */
|
||||
u8 txdma_on;
|
||||
u8 dma_num; /* Buffer we are DMAing into */
|
||||
u8 dma_ready; /* Is the other buffer free */
|
||||
u8 dma_tx; /* TX is to use DMA */
|
||||
u8 *rx_buf[2]; /* The flip buffers */
|
||||
|
||||
/*
|
||||
* System
|
||||
*/
|
||||
|
||||
struct z8530_dev *dev; /* Z85230 chip instance we are from */
|
||||
unsigned long ctrlio; /* I/O ports */
|
||||
unsigned long dataio;
|
||||
|
||||
/*
|
||||
* For PC we encode this way.
|
||||
*/
|
||||
#define Z8530_PORT_SLEEP 0x80000000
|
||||
#define Z8530_PORT_OF(x) ((x)&0xFFFF)
|
||||
|
||||
u32 rx_overrun; /* Overruns - not done yet */
|
||||
u32 rx_crc_err;
|
||||
|
||||
/*
|
||||
* Bound device pointers
|
||||
*/
|
||||
|
||||
void *private; /* For our owner */
|
||||
struct net_device *netdevice; /* Network layer device */
|
||||
struct net_device_stats stats; /* Network layer statistics */
|
||||
|
||||
/*
|
||||
* Async features
|
||||
*/
|
||||
|
||||
struct tty_struct *tty; /* Attached terminal */
|
||||
int line; /* Minor number */
|
||||
wait_queue_head_t open_wait; /* Tasks waiting to open */
|
||||
wait_queue_head_t close_wait; /* and for close to end */
|
||||
unsigned long event; /* Pending events */
|
||||
int fdcount; /* # of fd on device */
|
||||
int blocked_open; /* # of blocked opens */
|
||||
int x_char; /* XON/XOF char */
|
||||
unsigned char *xmit_buf; /* Transmit pointer */
|
||||
int xmit_head; /* Transmit ring */
|
||||
int xmit_tail;
|
||||
int xmit_cnt;
|
||||
int flags;
|
||||
int timeout;
|
||||
int xmit_fifo_size; /* Transmit FIFO info */
|
||||
|
||||
int close_delay; /* Do we wait for drain on close ? */
|
||||
unsigned short closing_wait;
|
||||
|
||||
/* We need to know the current clock divisor
|
||||
* to read the bps rate the chip has currently
|
||||
* loaded.
|
||||
*/
|
||||
|
||||
unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */
|
||||
int zs_baud;
|
||||
|
||||
int magic;
|
||||
int baud_base; /* Baud parameters */
|
||||
int custom_divisor;
|
||||
|
||||
|
||||
unsigned char tx_active; /* character is being xmitted */
|
||||
unsigned char tx_stopped; /* output is suspended */
|
||||
|
||||
spinlock_t *lock; /* Devicr lock */
|
||||
};
|
||||
|
||||
/*
|
||||
* Each Z853x0 device.
|
||||
*/
|
||||
|
||||
struct z8530_dev
|
||||
{
|
||||
char *name; /* Device instance name */
|
||||
struct z8530_channel chanA; /* SCC channel A */
|
||||
struct z8530_channel chanB; /* SCC channel B */
|
||||
int type;
|
||||
#define Z8530 0 /* NMOS dinosaur */
|
||||
#define Z85C30 1 /* CMOS - better */
|
||||
#define Z85230 2 /* CMOS with real FIFO */
|
||||
int irq; /* Interrupt for the device */
|
||||
int active; /* Soft interrupt enable - the Mac doesn't
|
||||
always have a hard disable on its 8530s... */
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Functions
|
||||
*/
|
||||
|
||||
extern u8 z8530_dead_port[];
|
||||
extern u8 z8530_hdlc_kilostream_85230[];
|
||||
extern u8 z8530_hdlc_kilostream[];
|
||||
extern irqreturn_t z8530_interrupt(int, void *);
|
||||
extern void z8530_describe(struct z8530_dev *, char *mapping, unsigned long io);
|
||||
extern int z8530_init(struct z8530_dev *);
|
||||
extern int z8530_shutdown(struct z8530_dev *);
|
||||
extern int z8530_sync_open(struct net_device *, struct z8530_channel *);
|
||||
extern int z8530_sync_close(struct net_device *, struct z8530_channel *);
|
||||
extern int z8530_sync_dma_open(struct net_device *, struct z8530_channel *);
|
||||
extern int z8530_sync_dma_close(struct net_device *, struct z8530_channel *);
|
||||
extern int z8530_sync_txdma_open(struct net_device *, struct z8530_channel *);
|
||||
extern int z8530_sync_txdma_close(struct net_device *, struct z8530_channel *);
|
||||
extern int z8530_channel_load(struct z8530_channel *, u8 *);
|
||||
extern int z8530_queue_xmit(struct z8530_channel *c, struct sk_buff *skb);
|
||||
extern struct net_device_stats *z8530_get_stats(struct z8530_channel *c);
|
||||
extern void z8530_null_rx(struct z8530_channel *c, struct sk_buff *skb);
|
||||
|
||||
|
||||
/*
|
||||
* Standard interrupt vector sets
|
||||
*/
|
||||
|
||||
extern struct z8530_irqhandler z8530_sync, z8530_async, z8530_nop;
|
||||
|
||||
/*
|
||||
* Asynchronous Interfacing
|
||||
*/
|
||||
|
||||
#define SERIAL_MAGIC 0x5301
|
||||
|
||||
/*
|
||||
* The size of the serial xmit buffer is 1 page, or 4096 bytes
|
||||
*/
|
||||
|
||||
#define SERIAL_XMIT_SIZE 4096
|
||||
#define WAKEUP_CHARS 256
|
||||
|
||||
/*
|
||||
* Events are used to schedule things to happen at timer-interrupt
|
||||
* time, instead of at rs interrupt time.
|
||||
*/
|
||||
#define RS_EVENT_WRITE_WAKEUP 0
|
||||
|
||||
/* Internal flags used only by kernel/chr_drv/serial.c */
|
||||
#define ZILOG_INITIALIZED 0x80000000 /* Serial port was initialized */
|
||||
#define ZILOG_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
|
||||
#define ZILOG_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
|
||||
#define ZILOG_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
|
||||
#define ZILOG_CLOSING 0x08000000 /* Serial port is closing */
|
||||
#define ZILOG_CTS_FLOW 0x04000000 /* Do CTS flow control */
|
||||
#define ZILOG_CHECK_CD 0x02000000 /* i.e., CLOCAL */
|
||||
|
||||
#endif /* !(_Z8530_H) */
|
||||
Reference in New Issue
Block a user