staging: irda: remove the irda network stack and drivers
No one has publicly stepped up to maintain this broken codebase for devices that no one uses anymore, so let's just drop the whole thing. If someone really wants/needs it, we can revert this and they can fix the code up to work properly. Cc: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
c72654356d
commit
d64c2a7612
|
@ -1,10 +0,0 @@
|
|||
To use the IrDA protocols within Linux you will need to get a suitable copy
|
||||
of the IrDA Utilities. More detailed information about these and associated
|
||||
programs can be found on http://irda.sourceforge.net/
|
||||
|
||||
For more information about how to use the IrDA protocol stack, see the
|
||||
Linux Infrared HOWTO by Werner Heuser <wehe@tuxmobil.org>:
|
||||
<http://www.tuxmobil.org/Infrared-HOWTO/Infrared-HOWTO.html>
|
||||
|
||||
There is an active mailing list for discussing Linux-IrDA matters called
|
||||
irda-users@lists.sourceforge.net
|
|
@ -24,8 +24,6 @@ menuconfig STAGING
|
|||
|
||||
if STAGING
|
||||
|
||||
source "drivers/staging/irda/net/Kconfig"
|
||||
|
||||
source "drivers/staging/ipx/Kconfig"
|
||||
|
||||
source "drivers/staging/ncpfs/Kconfig"
|
||||
|
|
|
@ -5,8 +5,6 @@ obj-y += media/
|
|||
obj-y += typec/
|
||||
obj-$(CONFIG_IPX) += ipx/
|
||||
obj-$(CONFIG_NCP_FS) += ncpfs/
|
||||
obj-$(CONFIG_IRDA) += irda/net/
|
||||
obj-$(CONFIG_IRDA) += irda/drivers/
|
||||
obj-$(CONFIG_PRISM2_USB) += wlan-ng/
|
||||
obj-$(CONFIG_COMEDI) += comedi/
|
||||
obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
The irda code will be removed soon from the kernel tree as it is old and
|
||||
obsolete and broken.
|
||||
|
||||
Don't worry about fixing up anything here, it's not needed.
|
|
@ -1,398 +0,0 @@
|
|||
menu "Infrared-port device drivers"
|
||||
depends on IRDA!=n
|
||||
|
||||
comment "SIR device drivers"
|
||||
|
||||
config IRTTY_SIR
|
||||
tristate "IrTTY (uses Linux serial driver)"
|
||||
depends on IRDA && TTY
|
||||
help
|
||||
Say Y here if you want to build support for the IrTTY line
|
||||
discipline. To compile it as a module, choose M here: the module
|
||||
will be called irtty-sir. IrTTY makes it possible to use Linux's
|
||||
own serial driver for all IrDA ports that are 16550 compatible.
|
||||
Most IrDA chips are 16550 compatible so you should probably say Y
|
||||
to this option. Using IrTTY will however limit the speed of the
|
||||
connection to 115200 bps (IrDA SIR mode).
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config BFIN_SIR
|
||||
tristate "Blackfin SIR on UART"
|
||||
depends on BLACKFIN && IRDA
|
||||
default n
|
||||
help
|
||||
Say Y here if your want to enable SIR function on Blackfin UART
|
||||
devices.
|
||||
|
||||
To activate this driver you can start irattach like:
|
||||
"irattach irda0 -s"
|
||||
|
||||
Saying M, it will be built as a module named bfin_sir.
|
||||
|
||||
Note that you need to turn off one of the serial drivers for SIR
|
||||
to use that UART.
|
||||
|
||||
config BFIN_SIR0
|
||||
bool "Blackfin SIR on UART0"
|
||||
depends on BFIN_SIR && !SERIAL_BFIN_UART0
|
||||
|
||||
config BFIN_SIR1
|
||||
bool "Blackfin SIR on UART1"
|
||||
depends on BFIN_SIR && !SERIAL_BFIN_UART1 && (!BF531 && !BF532 && !BF533 && !BF561)
|
||||
|
||||
config BFIN_SIR2
|
||||
bool "Blackfin SIR on UART2"
|
||||
depends on BFIN_SIR && !SERIAL_BFIN_UART2 && (BF54x || BF538 || BF539)
|
||||
|
||||
config BFIN_SIR3
|
||||
bool "Blackfin SIR on UART3"
|
||||
depends on BFIN_SIR && !SERIAL_BFIN_UART3 && (BF54x)
|
||||
|
||||
choice
|
||||
prompt "SIR Mode"
|
||||
depends on BFIN_SIR
|
||||
default SIR_BFIN_DMA
|
||||
|
||||
config SIR_BFIN_DMA
|
||||
bool "DMA mode"
|
||||
depends on !DMA_UNCACHED_NONE
|
||||
|
||||
config SIR_BFIN_PIO
|
||||
bool "PIO mode"
|
||||
endchoice
|
||||
|
||||
config SH_SIR
|
||||
tristate "SuperH SIR on UART"
|
||||
depends on IRDA && SUPERH && \
|
||||
(CPU_SUBTYPE_SH7722 || CPU_SUBTYPE_SH7723 || \
|
||||
CPU_SUBTYPE_SH7724)
|
||||
default n
|
||||
help
|
||||
Say Y here if your want to enable SIR function on SuperH UART
|
||||
devices.
|
||||
|
||||
comment "Dongle support"
|
||||
|
||||
config DONGLE
|
||||
bool "Serial dongle support"
|
||||
depends on IRTTY_SIR
|
||||
help
|
||||
Say Y here if you have an infrared device that connects to your
|
||||
computer's serial port. These devices are called dongles. Then say Y
|
||||
or M to the driver for your particular dongle below.
|
||||
|
||||
Note that the answer to this question won't directly affect the
|
||||
kernel: saying N will just cause the configurator to skip all
|
||||
the questions about serial dongles.
|
||||
|
||||
config ESI_DONGLE
|
||||
tristate "ESI JetEye PC dongle"
|
||||
depends on IRTTY_SIR && DONGLE && IRDA
|
||||
help
|
||||
Say Y here if you want to build support for the Extended Systems
|
||||
JetEye PC dongle. To compile it as a module, choose M here. The ESI
|
||||
dongle attaches to the normal 9-pin serial port connector, and can
|
||||
currently only be used by IrTTY. To activate support for ESI
|
||||
dongles you will have to start irattach like this:
|
||||
"irattach -d esi".
|
||||
|
||||
config ACTISYS_DONGLE
|
||||
tristate "ACTiSYS IR-220L and IR220L+ dongle"
|
||||
depends on IRTTY_SIR && DONGLE && IRDA
|
||||
help
|
||||
Say Y here if you want to build support for the ACTiSYS IR-220L and
|
||||
IR220L+ dongles. To compile it as a module, choose M here. The
|
||||
ACTiSYS dongles attaches to the normal 9-pin serial port connector,
|
||||
and can currently only be used by IrTTY. To activate support for
|
||||
ACTiSYS dongles you will have to start irattach like this:
|
||||
"irattach -d actisys" or "irattach -d actisys+".
|
||||
|
||||
config TEKRAM_DONGLE
|
||||
tristate "Tekram IrMate 210B dongle"
|
||||
depends on IRTTY_SIR && DONGLE && IRDA
|
||||
help
|
||||
Say Y here if you want to build support for the Tekram IrMate 210B
|
||||
dongle. To compile it as a module, choose M here. The Tekram dongle
|
||||
attaches to the normal 9-pin serial port connector, and can
|
||||
currently only be used by IrTTY. To activate support for Tekram
|
||||
dongles you will have to start irattach like this:
|
||||
"irattach -d tekram".
|
||||
|
||||
config TOIM3232_DONGLE
|
||||
tristate "TOIM3232 IrDa dongle"
|
||||
depends on IRTTY_SIR && DONGLE && IRDA
|
||||
help
|
||||
Say Y here if you want to build support for the Vishay/Temic
|
||||
TOIM3232 and TOIM4232 based dongles.
|
||||
To compile it as a module, choose M here.
|
||||
|
||||
config LITELINK_DONGLE
|
||||
tristate "Parallax LiteLink dongle"
|
||||
depends on IRTTY_SIR && DONGLE && IRDA
|
||||
help
|
||||
Say Y here if you want to build support for the Parallax Litelink
|
||||
dongle. To compile it as a module, choose M here. The Parallax
|
||||
dongle attaches to the normal 9-pin serial port connector, and can
|
||||
currently only be used by IrTTY. To activate support for Parallax
|
||||
dongles you will have to start irattach like this:
|
||||
"irattach -d litelink".
|
||||
|
||||
config MA600_DONGLE
|
||||
tristate "Mobile Action MA600 dongle"
|
||||
depends on IRTTY_SIR && DONGLE && IRDA
|
||||
help
|
||||
Say Y here if you want to build support for the Mobile Action MA600
|
||||
dongle. To compile it as a module, choose M here. The MA600 dongle
|
||||
attaches to the normal 9-pin serial port connector, and can
|
||||
currently only be used by IrTTY. The driver should also support
|
||||
the MA620 USB version of the dongle, if the integrated USB-to-RS232
|
||||
converter is supported by usbserial. To activate support for
|
||||
MA600 dongle you will have to start irattach like this:
|
||||
"irattach -d ma600".
|
||||
|
||||
config GIRBIL_DONGLE
|
||||
tristate "Greenwich GIrBIL dongle"
|
||||
depends on IRTTY_SIR && DONGLE && IRDA
|
||||
help
|
||||
Say Y here if you want to build support for the Greenwich GIrBIL
|
||||
dongle. If you want to compile it as a module, choose M here.
|
||||
The Greenwich dongle attaches to the normal 9-pin serial port
|
||||
connector, and can currently only be used by IrTTY. To activate
|
||||
support for Greenwich dongles you will have to start irattach
|
||||
like this: "irattach -d girbil".
|
||||
|
||||
config MCP2120_DONGLE
|
||||
tristate "Microchip MCP2120"
|
||||
depends on IRTTY_SIR && DONGLE && IRDA
|
||||
help
|
||||
Say Y here if you want to build support for the Microchip MCP2120
|
||||
dongle. If you want to compile it as a module, choose M here.
|
||||
The MCP2120 dongle attaches to the normal 9-pin serial port
|
||||
connector, and can currently only be used by IrTTY. To activate
|
||||
support for MCP2120 dongles you will have to start irattach
|
||||
like this: "irattach -d mcp2120".
|
||||
|
||||
You must build this dongle yourself. For more information see:
|
||||
<http://www.eyetap.org/~tangf/irda_sir_linux.html>
|
||||
|
||||
config OLD_BELKIN_DONGLE
|
||||
tristate "Old Belkin dongle"
|
||||
depends on IRTTY_SIR && DONGLE && IRDA
|
||||
help
|
||||
Say Y here if you want to build support for the Adaptec Airport 1000
|
||||
and 2000 dongles. If you want to compile it as a module, choose
|
||||
M here. Some information is contained in the comments
|
||||
at the top of <file:drivers/net/irda/old_belkin-sir.c>.
|
||||
|
||||
config ACT200L_DONGLE
|
||||
tristate "ACTiSYS IR-200L dongle"
|
||||
depends on IRTTY_SIR && DONGLE && IRDA
|
||||
help
|
||||
Say Y here if you want to build support for the ACTiSYS IR-200L
|
||||
dongle. If you want to compile it as a module, choose M here.
|
||||
The ACTiSYS IR-200L dongle attaches to the normal 9-pin serial
|
||||
port connector, and can currently only be used by IrTTY.
|
||||
To activate support for ACTiSYS IR-200L dongle you will have to
|
||||
start irattach like this: "irattach -d act200l".
|
||||
|
||||
config KINGSUN_DONGLE
|
||||
tristate "KingSun/DonShine DS-620 IrDA-USB dongle"
|
||||
depends on IRDA && USB
|
||||
help
|
||||
Say Y or M here if you want to build support for the KingSun/DonShine
|
||||
DS-620 IrDA-USB bridge device driver.
|
||||
|
||||
This USB bridge does not conform to the IrDA-USB device class
|
||||
specification, and therefore needs its own specific driver. This
|
||||
dongle supports SIR speed only (9600 bps).
|
||||
|
||||
To compile it as a module, choose M here: the module will be called
|
||||
kingsun-sir.
|
||||
|
||||
config KSDAZZLE_DONGLE
|
||||
tristate "KingSun Dazzle IrDA-USB dongle"
|
||||
depends on IRDA && USB
|
||||
help
|
||||
Say Y or M here if you want to build support for the KingSun Dazzle
|
||||
IrDA-USB bridge device driver.
|
||||
|
||||
This USB bridge does not conform to the IrDA-USB device class
|
||||
specification, and therefore needs its own specific driver. This
|
||||
dongle supports SIR speeds only (9600 through 115200 bps).
|
||||
|
||||
To compile it as a module, choose M here: the module will be called
|
||||
ksdazzle-sir.
|
||||
|
||||
config KS959_DONGLE
|
||||
tristate "KingSun KS-959 IrDA-USB dongle"
|
||||
depends on IRDA && USB
|
||||
help
|
||||
Say Y or M here if you want to build support for the KingSun KS-959
|
||||
IrDA-USB bridge device driver.
|
||||
|
||||
This USB bridge does not conform to the IrDA-USB device class
|
||||
specification, and therefore needs its own specific driver. This
|
||||
dongle supports SIR speeds only (9600 through 57600 bps).
|
||||
|
||||
To compile it as a module, choose M here: the module will be called
|
||||
ks959-sir.
|
||||
|
||||
comment "FIR device drivers"
|
||||
|
||||
config USB_IRDA
|
||||
tristate "IrDA USB dongles"
|
||||
depends on IRDA && USB
|
||||
select FW_LOADER
|
||||
---help---
|
||||
Say Y here if you want to build support for the USB IrDA FIR Dongle
|
||||
device driver. To compile it as a module, choose M here: the module
|
||||
will be called irda-usb. IrDA-USB support the various IrDA USB
|
||||
dongles available and most of their peculiarities. Those dongles
|
||||
plug in the USB port of your computer, are plug and play, and
|
||||
support SIR and FIR (4Mbps) speeds. On the other hand, those
|
||||
dongles tend to be less efficient than a FIR chipset.
|
||||
|
||||
Please note that the driver is still experimental. And of course,
|
||||
you will need both USB and IrDA support in your kernel...
|
||||
|
||||
config SIGMATEL_FIR
|
||||
tristate "SigmaTel STIr4200 bridge"
|
||||
depends on IRDA && USB
|
||||
select CRC32
|
||||
---help---
|
||||
Say Y here if you want to build support for the SigmaTel STIr4200
|
||||
USB IrDA FIR bridge device driver.
|
||||
|
||||
USB bridge based on the SigmaTel STIr4200 don't conform to the
|
||||
IrDA-USB device class specification, and therefore need their
|
||||
own specific driver. Those dongles support SIR and FIR (4Mbps)
|
||||
speeds.
|
||||
|
||||
To compile it as a module, choose M here: the module will be called
|
||||
stir4200.
|
||||
|
||||
config NSC_FIR
|
||||
tristate "NSC PC87108/PC87338"
|
||||
depends on IRDA && ISA_DMA_API
|
||||
help
|
||||
Say Y here if you want to build support for the NSC PC87108 and
|
||||
PC87338 IrDA chipsets. This driver supports SIR,
|
||||
MIR and FIR (4Mbps) speeds.
|
||||
|
||||
To compile it as a module, choose M here: the module will be called
|
||||
nsc-ircc.
|
||||
|
||||
config WINBOND_FIR
|
||||
tristate "Winbond W83977AF (IR)"
|
||||
depends on IRDA && ISA_DMA_API
|
||||
help
|
||||
Say Y here if you want to build IrDA support for the Winbond
|
||||
W83977AF super-io chipset. This driver should be used for the IrDA
|
||||
chipset in the Corel NetWinder. The driver supports SIR, MIR and
|
||||
FIR (4Mbps) speeds.
|
||||
|
||||
To compile it as a module, choose M here: the module will be called
|
||||
w83977af_ir.
|
||||
|
||||
config TOSHIBA_FIR
|
||||
tristate "Toshiba Type-O IR Port"
|
||||
depends on IRDA && PCI && !64BIT && VIRT_TO_BUS
|
||||
help
|
||||
Say Y here if you want to build support for the Toshiba Type-O IR
|
||||
and Donau oboe chipsets. These chipsets are used by the Toshiba
|
||||
Libretto 100/110CT, Tecra 8100, Portege 7020 and many more laptops.
|
||||
To compile it as a module, choose M here: the module will be called
|
||||
donauboe.
|
||||
|
||||
config AU1000_FIR
|
||||
tristate "Alchemy IrDA SIR/FIR"
|
||||
depends on IRDA && MIPS_ALCHEMY
|
||||
help
|
||||
Say Y/M here to build support the IrDA peripheral on the
|
||||
Alchemy Au1000 and Au1100 SoCs.
|
||||
Say M to build a module; it will be called au1k_ir.ko
|
||||
|
||||
config SMC_IRCC_FIR
|
||||
tristate "SMSC IrCC"
|
||||
depends on IRDA && ISA_DMA_API
|
||||
help
|
||||
Say Y here if you want to build support for the SMC Infrared
|
||||
Communications Controller. It is used in a wide variety of
|
||||
laptops (Fujitsu, Sony, Compaq and some Toshiba).
|
||||
To compile it as a module, choose M here: the module will be called
|
||||
smsc-ircc2.o.
|
||||
|
||||
config ALI_FIR
|
||||
tristate "ALi M5123 FIR"
|
||||
depends on IRDA && ISA_DMA_API
|
||||
help
|
||||
Say Y here if you want to build support for the ALi M5123 FIR
|
||||
Controller. The ALi M5123 FIR Controller is embedded in ALi M1543C,
|
||||
M1535, M1535D, M1535+, M1535D South Bridge. This driver supports
|
||||
SIR, MIR and FIR (4Mbps) speeds.
|
||||
|
||||
To compile it as a module, choose M here: the module will be called
|
||||
ali-ircc.
|
||||
|
||||
config VLSI_FIR
|
||||
tristate "VLSI 82C147 SIR/MIR/FIR"
|
||||
depends on IRDA && PCI
|
||||
help
|
||||
Say Y here if you want to build support for the VLSI 82C147
|
||||
PCI-IrDA Controller. This controller is used by the HP OmniBook 800
|
||||
and 5500 notebooks. The driver provides support for SIR, MIR and
|
||||
FIR (4Mbps) speeds.
|
||||
|
||||
To compile it as a module, choose M here: the module will be called
|
||||
vlsi_ir.
|
||||
|
||||
config SA1100_FIR
|
||||
tristate "SA1100 Internal IR"
|
||||
depends on ARCH_SA1100 && IRDA && DMA_SA11X0
|
||||
|
||||
config VIA_FIR
|
||||
tristate "VIA VT8231/VT1211 SIR/MIR/FIR"
|
||||
depends on IRDA && ISA_DMA_API
|
||||
help
|
||||
Say Y here if you want to build support for the VIA VT8231
|
||||
and VIA VT1211 IrDA controllers, found on the motherboards using
|
||||
those VIA chipsets. To use this controller, you will need
|
||||
to plug a specific 5 pins FIR IrDA dongle in the specific
|
||||
motherboard connector. The driver provides support for SIR, MIR
|
||||
and FIR (4Mbps) speeds.
|
||||
|
||||
You will need to specify the 'dongle_id' module parameter to
|
||||
indicate the FIR dongle attached to the controller.
|
||||
|
||||
To compile it as a module, choose M here: the module will be called
|
||||
via-ircc.
|
||||
|
||||
config PXA_FICP
|
||||
tristate "Intel PXA2xx Internal FICP"
|
||||
depends on ARCH_PXA && IRDA
|
||||
help
|
||||
Say Y or M here if you want to build support for the PXA2xx
|
||||
built-in IRDA interface which can support both SIR and FIR.
|
||||
This driver relies on platform specific helper routines so
|
||||
available capabilities may vary from one PXA2xx target to
|
||||
another.
|
||||
|
||||
config MCS_FIR
|
||||
tristate "MosChip MCS7780 IrDA-USB dongle"
|
||||
depends on IRDA && USB
|
||||
select CRC32
|
||||
help
|
||||
Say Y or M here if you want to build support for the MosChip
|
||||
MCS7780 IrDA-USB bridge device driver.
|
||||
|
||||
USB bridge based on the MosChip MCS7780 don't conform to the
|
||||
IrDA-USB device class specification, and therefore need their
|
||||
own specific driver. Those dongles support SIR and FIR (4Mbps)
|
||||
speeds.
|
||||
|
||||
To compile it as a module, choose M here: the module will be called
|
||||
mcs7780.
|
||||
|
||||
endmenu
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
#
|
||||
# Makefile for the Linux IrDA infrared port device drivers.
|
||||
#
|
||||
# 9 Aug 2000, Christoph Hellwig <hch@infradead.org>
|
||||
# Rewritten to use lists instead of if-statements.
|
||||
#
|
||||
|
||||
subdir-ccflags-y += -I$(srctree)/drivers/staging/irda/include
|
||||
|
||||
# FIR drivers
|
||||
obj-$(CONFIG_USB_IRDA) += irda-usb.o
|
||||
obj-$(CONFIG_SIGMATEL_FIR) += stir4200.o
|
||||
obj-$(CONFIG_NSC_FIR) += nsc-ircc.o
|
||||
obj-$(CONFIG_WINBOND_FIR) += w83977af_ir.o
|
||||
obj-$(CONFIG_SA1100_FIR) += sa1100_ir.o
|
||||
obj-$(CONFIG_TOSHIBA_FIR) += donauboe.o
|
||||
obj-$(CONFIG_SMC_IRCC_FIR) += smsc-ircc2.o
|
||||
obj-$(CONFIG_ALI_FIR) += ali-ircc.o
|
||||
obj-$(CONFIG_VLSI_FIR) += vlsi_ir.o
|
||||
obj-$(CONFIG_VIA_FIR) += via-ircc.o
|
||||
obj-$(CONFIG_PXA_FICP) += pxaficp_ir.o
|
||||
obj-$(CONFIG_MCS_FIR) += mcs7780.o
|
||||
obj-$(CONFIG_AU1000_FIR) += au1k_ir.o
|
||||
# SIR drivers
|
||||
obj-$(CONFIG_IRTTY_SIR) += irtty-sir.o sir-dev.o
|
||||
obj-$(CONFIG_BFIN_SIR) += bfin_sir.o
|
||||
obj-$(CONFIG_SH_SIR) += sh_sir.o
|
||||
# dongle drivers for SIR drivers
|
||||
obj-$(CONFIG_ESI_DONGLE) += esi-sir.o
|
||||
obj-$(CONFIG_TEKRAM_DONGLE) += tekram-sir.o
|
||||
obj-$(CONFIG_ACTISYS_DONGLE) += actisys-sir.o
|
||||
obj-$(CONFIG_LITELINK_DONGLE) += litelink-sir.o
|
||||
obj-$(CONFIG_GIRBIL_DONGLE) += girbil-sir.o
|
||||
obj-$(CONFIG_OLD_BELKIN_DONGLE) += old_belkin-sir.o
|
||||
obj-$(CONFIG_MCP2120_DONGLE) += mcp2120-sir.o
|
||||
obj-$(CONFIG_ACT200L_DONGLE) += act200l-sir.o
|
||||
obj-$(CONFIG_MA600_DONGLE) += ma600-sir.o
|
||||
obj-$(CONFIG_TOIM3232_DONGLE) += toim3232-sir.o
|
||||
obj-$(CONFIG_KINGSUN_DONGLE) += kingsun-sir.o
|
||||
obj-$(CONFIG_KSDAZZLE_DONGLE) += ksdazzle-sir.o
|
||||
obj-$(CONFIG_KS959_DONGLE) += ks959-sir.o
|
||||
|
||||
# The SIR helper module
|
||||
sir-dev-objs := sir_dev.o sir_dongle.o
|
|
@ -1,250 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: act200l.c
|
||||
* Version: 0.8
|
||||
* Description: Implementation for the ACTiSYS ACT-IR200L dongle
|
||||
* Status: Experimental.
|
||||
* Author: SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp>
|
||||
* Created at: Fri Aug 3 17:35:42 2001
|
||||
* Modified at: Fri Aug 17 10:22:40 2001
|
||||
* Modified by: SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp>
|
||||
*
|
||||
* Copyright (c) 2001 SHIMIZU Takuya, All Rights Reserved.
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
|
||||
#include "sir-dev.h"
|
||||
|
||||
static int act200l_reset(struct sir_dev *dev);
|
||||
static int act200l_open(struct sir_dev *dev);
|
||||
static int act200l_close(struct sir_dev *dev);
|
||||
static int act200l_change_speed(struct sir_dev *dev, unsigned speed);
|
||||
|
||||
/* Regsiter 0: Control register #1 */
|
||||
#define ACT200L_REG0 0x00
|
||||
#define ACT200L_TXEN 0x01 /* Enable transmitter */
|
||||
#define ACT200L_RXEN 0x02 /* Enable receiver */
|
||||
|
||||
/* Register 1: Control register #2 */
|
||||
#define ACT200L_REG1 0x10
|
||||
#define ACT200L_LODB 0x01 /* Load new baud rate count value */
|
||||
#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */
|
||||
|
||||
/* Register 4: Output Power register */
|
||||
#define ACT200L_REG4 0x40
|
||||
#define ACT200L_OP0 0x01 /* Enable LED1C output */
|
||||
#define ACT200L_OP1 0x02 /* Enable LED2C output */
|
||||
#define ACT200L_BLKR 0x04
|
||||
|
||||
/* Register 5: Receive Mode register */
|
||||
#define ACT200L_REG5 0x50
|
||||
#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */
|
||||
|
||||
/* Register 6: Receive Sensitivity register #1 */
|
||||
#define ACT200L_REG6 0x60
|
||||
#define ACT200L_RS0 0x01 /* receive threshold bit 0 */
|
||||
#define ACT200L_RS1 0x02 /* receive threshold bit 1 */
|
||||
|
||||
/* Register 7: Receive Sensitivity register #2 */
|
||||
#define ACT200L_REG7 0x70
|
||||
#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */
|
||||
|
||||
/* Register 8,9: Baud Rate Dvider register #1,#2 */
|
||||
#define ACT200L_REG8 0x80
|
||||
#define ACT200L_REG9 0x90
|
||||
|
||||
#define ACT200L_2400 0x5f
|
||||
#define ACT200L_9600 0x17
|
||||
#define ACT200L_19200 0x0b
|
||||
#define ACT200L_38400 0x05
|
||||
#define ACT200L_57600 0x03
|
||||
#define ACT200L_115200 0x01
|
||||
|
||||
/* Register 13: Control register #3 */
|
||||
#define ACT200L_REG13 0xd0
|
||||
#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */
|
||||
|
||||
/* Register 15: Status register */
|
||||
#define ACT200L_REG15 0xf0
|
||||
|
||||
/* Register 21: Control register #4 */
|
||||
#define ACT200L_REG21 0x50
|
||||
#define ACT200L_EXCK 0x02 /* Disable clock output driver */
|
||||
#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */
|
||||
|
||||
static struct dongle_driver act200l = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "ACTiSYS ACT-IR200L",
|
||||
.type = IRDA_ACT200L_DONGLE,
|
||||
.open = act200l_open,
|
||||
.close = act200l_close,
|
||||
.reset = act200l_reset,
|
||||
.set_speed = act200l_change_speed,
|
||||
};
|
||||
|
||||
static int __init act200l_sir_init(void)
|
||||
{
|
||||
return irda_register_dongle(&act200l);
|
||||
}
|
||||
|
||||
static void __exit act200l_sir_cleanup(void)
|
||||
{
|
||||
irda_unregister_dongle(&act200l);
|
||||
}
|
||||
|
||||
static int act200l_open(struct sir_dev *dev)
|
||||
{
|
||||
struct qos_info *qos = &dev->qos;
|
||||
|
||||
/* Power on the dongle */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
/* Set the speeds we can accept */
|
||||
qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
|
||||
qos->min_turn_time.bits = 0x03;
|
||||
irda_qos_bits_to_value(qos);
|
||||
|
||||
/* irda thread waits 50 msec for power settling */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int act200l_close(struct sir_dev *dev)
|
||||
{
|
||||
/* Power off the dongle */
|
||||
sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function act200l_change_speed (dev, speed)
|
||||
*
|
||||
* Set the speed for the ACTiSYS ACT-IR200L type dongle.
|
||||
*
|
||||
*/
|
||||
static int act200l_change_speed(struct sir_dev *dev, unsigned speed)
|
||||
{
|
||||
u8 control[3];
|
||||
int ret = 0;
|
||||
|
||||
/* Clear DTR and set RTS to enter command mode */
|
||||
sirdev_set_dtr_rts(dev, FALSE, TRUE);
|
||||
|
||||
switch (speed) {
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
/* fall through */
|
||||
case 9600:
|
||||
control[0] = ACT200L_REG8 | (ACT200L_9600 & 0x0f);
|
||||
control[1] = ACT200L_REG9 | ((ACT200L_9600 >> 4) & 0x0f);
|
||||
break;
|
||||
case 19200:
|
||||
control[0] = ACT200L_REG8 | (ACT200L_19200 & 0x0f);
|
||||
control[1] = ACT200L_REG9 | ((ACT200L_19200 >> 4) & 0x0f);
|
||||
break;
|
||||
case 38400:
|
||||
control[0] = ACT200L_REG8 | (ACT200L_38400 & 0x0f);
|
||||
control[1] = ACT200L_REG9 | ((ACT200L_38400 >> 4) & 0x0f);
|
||||
break;
|
||||
case 57600:
|
||||
control[0] = ACT200L_REG8 | (ACT200L_57600 & 0x0f);
|
||||
control[1] = ACT200L_REG9 | ((ACT200L_57600 >> 4) & 0x0f);
|
||||
break;
|
||||
case 115200:
|
||||
control[0] = ACT200L_REG8 | (ACT200L_115200 & 0x0f);
|
||||
control[1] = ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f);
|
||||
break;
|
||||
}
|
||||
control[2] = ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE;
|
||||
|
||||
/* Write control bytes */
|
||||
sirdev_raw_write(dev, control, 3);
|
||||
msleep(5);
|
||||
|
||||
/* Go back to normal mode */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
dev->speed = speed;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function act200l_reset (driver)
|
||||
*
|
||||
* Reset the ACTiSYS ACT-IR200L type dongle.
|
||||
*/
|
||||
|
||||
#define ACT200L_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET+1)
|
||||
#define ACT200L_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET+2)
|
||||
|
||||
static int act200l_reset(struct sir_dev *dev)
|
||||
{
|
||||
unsigned state = dev->fsm.substate;
|
||||
unsigned delay = 0;
|
||||
static const u8 control[9] = {
|
||||
ACT200L_REG15,
|
||||
ACT200L_REG13 | ACT200L_SHDW,
|
||||
ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL,
|
||||
ACT200L_REG13,
|
||||
ACT200L_REG7 | ACT200L_ENPOS,
|
||||
ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1,
|
||||
ACT200L_REG5 | ACT200L_RWIDL,
|
||||
ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR,
|
||||
ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN
|
||||
};
|
||||
int ret = 0;
|
||||
|
||||
switch (state) {
|
||||
case SIRDEV_STATE_DONGLE_RESET:
|
||||
/* Reset the dongle : set RTS low for 25 ms */
|
||||
sirdev_set_dtr_rts(dev, TRUE, FALSE);
|
||||
state = ACT200L_STATE_WAIT1_RESET;
|
||||
delay = 50;
|
||||
break;
|
||||
|
||||
case ACT200L_STATE_WAIT1_RESET:
|
||||
/* Clear DTR and set RTS to enter command mode */
|
||||
sirdev_set_dtr_rts(dev, FALSE, TRUE);
|
||||
|
||||
udelay(25); /* better wait for some short while */
|
||||
|
||||
/* Write control bytes */
|
||||
sirdev_raw_write(dev, control, sizeof(control));
|
||||
state = ACT200L_STATE_WAIT2_RESET;
|
||||
delay = 15;
|
||||
break;
|
||||
|
||||
case ACT200L_STATE_WAIT2_RESET:
|
||||
/* Go back to normal mode */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
dev->speed = 9600;
|
||||
break;
|
||||
default:
|
||||
net_err_ratelimited("%s(), unknown state %d\n",
|
||||
__func__, state);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
dev->fsm.substate = state;
|
||||
return (delay > 0) ? delay : ret;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp>");
|
||||
MODULE_DESCRIPTION("ACTiSYS ACT-IR200L dongle driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("irda-dongle-10"); /* IRDA_ACT200L_DONGLE */
|
||||
|
||||
module_init(act200l_sir_init);
|
||||
module_exit(act200l_sir_cleanup);
|
|
@ -1,245 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: actisys.c
|
||||
* Version: 1.1
|
||||
* Description: Implementation for the ACTiSYS IR-220L and IR-220L+
|
||||
* dongles
|
||||
* Status: Beta.
|
||||
* Authors: Dag Brattli <dagb@cs.uit.no> (initially)
|
||||
* Jean Tourrilhes <jt@hpl.hp.com> (new version)
|
||||
* Martin Diehl <mad@mdiehl.de> (new version for sir_dev)
|
||||
* Created at: Wed Oct 21 20:02:35 1998
|
||||
* Modified at: Sun Oct 27 22:02:13 2002
|
||||
* Modified by: Martin Diehl <mad@mdiehl.de>
|
||||
*
|
||||
* Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
|
||||
* Copyright (c) 1999 Jean Tourrilhes
|
||||
* Copyright (c) 2002 Martin Diehl
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
/*
|
||||
* Changelog
|
||||
*
|
||||
* 0.8 -> 0.9999 - Jean
|
||||
* o New initialisation procedure : much safer and correct
|
||||
* o New procedure the change speed : much faster and simpler
|
||||
* o Other cleanups & comments
|
||||
* Thanks to Lichen Wang @ Actisys for his excellent help...
|
||||
*
|
||||
* 1.0 -> 1.1 - Martin Diehl
|
||||
* modified for new sir infrastructure
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
|
||||
#include "sir-dev.h"
|
||||
|
||||
/*
|
||||
* Define the timing of the pulses we send to the dongle (to reset it, and
|
||||
* to toggle speeds). Basically, the limit here is the propagation speed of
|
||||
* the signals through the serial port, the dongle being much faster. Any
|
||||
* serial port support 115 kb/s, so we are sure that pulses 8.5 us wide can
|
||||
* go through cleanly . If you are on the wild side, you can try to lower
|
||||
* this value (Actisys recommended me 2 us, and 0 us work for me on a P233!)
|
||||
*/
|
||||
#define MIN_DELAY 10 /* 10 us to be on the conservative side */
|
||||
|
||||
static int actisys_open(struct sir_dev *);
|
||||
static int actisys_close(struct sir_dev *);
|
||||
static int actisys_change_speed(struct sir_dev *, unsigned);
|
||||
static int actisys_reset(struct sir_dev *);
|
||||
|
||||
/* These are the baudrates supported, in the order available */
|
||||
/* Note : the 220L doesn't support 38400, but we will fix that below */
|
||||
static unsigned baud_rates[] = { 9600, 19200, 57600, 115200, 38400 };
|
||||
|
||||
#define MAX_SPEEDS ARRAY_SIZE(baud_rates)
|
||||
|
||||
static struct dongle_driver act220l = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "Actisys ACT-220L",
|
||||
.type = IRDA_ACTISYS_DONGLE,
|
||||
.open = actisys_open,
|
||||
.close = actisys_close,
|
||||
.reset = actisys_reset,
|
||||
.set_speed = actisys_change_speed,
|
||||
};
|
||||
|
||||
static struct dongle_driver act220l_plus = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "Actisys ACT-220L+",
|
||||
.type = IRDA_ACTISYS_PLUS_DONGLE,
|
||||
.open = actisys_open,
|
||||
.close = actisys_close,
|
||||
.reset = actisys_reset,
|
||||
.set_speed = actisys_change_speed,
|
||||
};
|
||||
|
||||
static int __init actisys_sir_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* First, register an Actisys 220L dongle */
|
||||
ret = irda_register_dongle(&act220l);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Now, register an Actisys 220L+ dongle */
|
||||
ret = irda_register_dongle(&act220l_plus);
|
||||
if (ret < 0) {
|
||||
irda_unregister_dongle(&act220l);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit actisys_sir_cleanup(void)
|
||||
{
|
||||
/* We have to remove both dongles */
|
||||
irda_unregister_dongle(&act220l_plus);
|
||||
irda_unregister_dongle(&act220l);
|
||||
}
|
||||
|
||||
static int actisys_open(struct sir_dev *dev)
|
||||
{
|
||||
struct qos_info *qos = &dev->qos;
|
||||
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
/* Set the speeds we can accept */
|
||||
qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
|
||||
|
||||
/* Remove support for 38400 if this is not a 220L+ dongle */
|
||||
if (dev->dongle_drv->type == IRDA_ACTISYS_DONGLE)
|
||||
qos->baud_rate.bits &= ~IR_38400;
|
||||
|
||||
qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */
|
||||
irda_qos_bits_to_value(qos);
|
||||
|
||||
/* irda thread waits 50 msec for power settling */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int actisys_close(struct sir_dev *dev)
|
||||
{
|
||||
/* Power off the dongle */
|
||||
sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function actisys_change_speed (task)
|
||||
*
|
||||
* Change speed of the ACTiSYS IR-220L and IR-220L+ type IrDA dongles.
|
||||
* To cycle through the available baud rates, pulse RTS low for a few us.
|
||||
*
|
||||
* First, we reset the dongle to always start from a known state.
|
||||
* Then, we cycle through the speeds by pulsing RTS low and then up.
|
||||
* The dongle allow us to pulse quite fast, se we can set speed in one go,
|
||||
* which is must faster ( < 100 us) and less complex than what is found
|
||||
* in some other dongle drivers...
|
||||
* Note that even if the new speed is the same as the current speed,
|
||||
* we reassert the speed. This make sure that things are all right,
|
||||
* and it's fast anyway...
|
||||
* By the way, this function will work for both type of dongles,
|
||||
* because the additional speed is at the end of the sequence...
|
||||
*/
|
||||
static int actisys_change_speed(struct sir_dev *dev, unsigned speed)
|
||||
{
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
|
||||
pr_debug("%s(), speed=%d (was %d)\n", __func__, speed, dev->speed);
|
||||
|
||||
/* dongle was already resetted from irda_request state machine,
|
||||
* we are in known state (dongle default)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Now, we can set the speed requested. Send RTS pulses until we
|
||||
* reach the target speed
|
||||
*/
|
||||
for (i = 0; i < MAX_SPEEDS; i++) {
|
||||
if (speed == baud_rates[i]) {
|
||||
dev->speed = speed;
|
||||
break;
|
||||
}
|
||||
/* Set RTS low for 10 us */
|
||||
sirdev_set_dtr_rts(dev, TRUE, FALSE);
|
||||
udelay(MIN_DELAY);
|
||||
|
||||
/* Set RTS high for 10 us */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
udelay(MIN_DELAY);
|
||||
}
|
||||
|
||||
/* Check if life is sweet... */
|
||||
if (i >= MAX_SPEEDS) {
|
||||
actisys_reset(dev);
|
||||
ret = -EINVAL; /* This should not happen */
|
||||
}
|
||||
|
||||
/* Basta lavoro, on se casse d'ici... */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function actisys_reset (task)
|
||||
*
|
||||
* Reset the Actisys type dongle. Warning, this function must only be
|
||||
* called with a process context!
|
||||
*
|
||||
* We need to do two things in this function :
|
||||
* o first make sure that the dongle is in a state where it can operate
|
||||
* o second put the dongle in a know state
|
||||
*
|
||||
* The dongle is powered of the RTS and DTR lines. In the dongle, there
|
||||
* is a big capacitor to accommodate the current spikes. This capacitor
|
||||
* takes a least 50 ms to be charged. In theory, the Bios set those lines
|
||||
* up, so by the time we arrive here we should be set. It doesn't hurt
|
||||
* to be on the conservative side, so we will wait...
|
||||
* <Martin : move above comment to irda_config_fsm>
|
||||
* Then, we set the speed to 9600 b/s to get in a known state (see in
|
||||
* change_speed for details). It is needed because the IrDA stack
|
||||
* has tried to set the speed immediately after our first return,
|
||||
* so before we can be sure the dongle is up and running.
|
||||
*/
|
||||
|
||||
static int actisys_reset(struct sir_dev *dev)
|
||||
{
|
||||
/* Reset the dongle : set DTR low for 10 us */
|
||||
sirdev_set_dtr_rts(dev, FALSE, TRUE);
|
||||
udelay(MIN_DELAY);
|
||||
|
||||
/* Go back to normal mode */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
dev->speed = 9600; /* That's the default */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no> - Jean Tourrilhes <jt@hpl.hp.com>");
|
||||
MODULE_DESCRIPTION("ACTiSYS IR-220L and IR-220L+ dongle driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("irda-dongle-2"); /* IRDA_ACTISYS_DONGLE */
|
||||
MODULE_ALIAS("irda-dongle-3"); /* IRDA_ACTISYS_PLUS_DONGLE */
|
||||
|
||||
module_init(actisys_sir_init);
|
||||
module_exit(actisys_sir_cleanup);
|
File diff suppressed because it is too large
Load Diff
|
@ -1,227 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ali-ircc.h
|
||||
* Version: 0.5
|
||||
* Description: Driver for the ALI M1535D and M1543C FIR Controller
|
||||
* Status: Experimental.
|
||||
* Author: Benjamin Kong <benjamin_kong@ali.com.tw>
|
||||
* Created at: 2000/10/16 03:46PM
|
||||
* Modified at: 2001/1/3 02:56PM
|
||||
* Modified by: Benjamin Kong <benjamin_kong@ali.com.tw>
|
||||
*
|
||||
* Copyright (c) 2000 Benjamin Kong <benjamin_kong@ali.com.tw>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef ALI_IRCC_H
|
||||
#define ALI_IRCC_H
|
||||
|
||||
#include <linux/ktime.h>
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/* SIR Register */
|
||||
/* Usr definition of linux/serial_reg.h */
|
||||
|
||||
/* FIR Register */
|
||||
#define BANK0 0x20
|
||||
#define BANK1 0x21
|
||||
#define BANK2 0x22
|
||||
#define BANK3 0x23
|
||||
|
||||
#define FIR_MCR 0x07 /* Master Control Register */
|
||||
|
||||
/* Bank 0 */
|
||||
#define FIR_DR 0x00 /* Alias 0, FIR Data Register (R/W) */
|
||||
#define FIR_IER 0x01 /* Alias 1, FIR Interrupt Enable Register (R/W) */
|
||||
#define FIR_IIR 0x02 /* Alias 2, FIR Interrupt Identification Register (Read only) */
|
||||
#define FIR_LCR_A 0x03 /* Alias 3, FIR Line Control Register A (R/W) */
|
||||
#define FIR_LCR_B 0x04 /* Alias 4, FIR Line Control Register B (R/W) */
|
||||
#define FIR_LSR 0x05 /* Alias 5, FIR Line Status Register (R/W) */
|
||||
#define FIR_BSR 0x06 /* Alias 6, FIR Bus Status Register (Read only) */
|
||||
|
||||
|
||||
/* Alias 1 */
|
||||
#define IER_FIFO 0x10 /* FIR FIFO Interrupt Enable */
|
||||
#define IER_TIMER 0x20 /* Timer Interrupt Enable */
|
||||
#define IER_EOM 0x40 /* End of Message Interrupt Enable */
|
||||
#define IER_ACT 0x80 /* Active Frame Interrupt Enable */
|
||||
|
||||
/* Alias 2 */
|
||||
#define IIR_FIFO 0x10 /* FIR FIFO Interrupt */
|
||||
#define IIR_TIMER 0x20 /* Timer Interrupt */
|
||||
#define IIR_EOM 0x40 /* End of Message Interrupt */
|
||||
#define IIR_ACT 0x80 /* Active Frame Interrupt */
|
||||
|
||||
/* Alias 3 */
|
||||
#define LCR_A_FIFO_RESET 0x80 /* FIFO Reset */
|
||||
|
||||
/* Alias 4 */
|
||||
#define LCR_B_BW 0x10 /* Brick Wall */
|
||||
#define LCR_B_SIP 0x20 /* SIP Enable */
|
||||
#define LCR_B_TX_MODE 0x40 /* Transmit Mode */
|
||||
#define LCR_B_RX_MODE 0x80 /* Receive Mode */
|
||||
|
||||
/* Alias 5 */
|
||||
#define LSR_FIR_LSA 0x00 /* FIR Line Status Address */
|
||||
#define LSR_FRAME_ABORT 0x08 /* Frame Abort */
|
||||
#define LSR_CRC_ERROR 0x10 /* CRC Error */
|
||||
#define LSR_SIZE_ERROR 0x20 /* Size Error */
|
||||
#define LSR_FRAME_ERROR 0x40 /* Frame Error */
|
||||
#define LSR_FIFO_UR 0x80 /* FIFO Underrun */
|
||||
#define LSR_FIFO_OR 0x80 /* FIFO Overrun */
|
||||
|
||||
/* Alias 6 */
|
||||
#define BSR_FIFO_NOT_EMPTY 0x80 /* FIFO Not Empty */
|
||||
|
||||
/* Bank 1 */
|
||||
#define FIR_CR 0x00 /* Alias 0, FIR Configuration Register (R/W) */
|
||||
#define FIR_FIFO_TR 0x01 /* Alias 1, FIR FIFO Threshold Register (R/W) */
|
||||
#define FIR_DMA_TR 0x02 /* Alias 2, FIR DMA Threshold Register (R/W) */
|
||||
#define FIR_TIMER_IIR 0x03 /* Alias 3, FIR Timer interrupt interval register (W/O) */
|
||||
#define FIR_FIFO_FR 0x03 /* Alias 3, FIR FIFO Flag register (R/O) */
|
||||
#define FIR_FIFO_RAR 0x04 /* Alias 4, FIR FIFO Read Address register (R/O) */
|
||||
#define FIR_FIFO_WAR 0x05 /* Alias 5, FIR FIFO Write Address register (R/O) */
|
||||
#define FIR_TR 0x06 /* Alias 6, Test REgister (W/O) */
|
||||
|
||||
/* Alias 0 */
|
||||
#define CR_DMA_EN 0x01 /* DMA Enable */
|
||||
#define CR_DMA_BURST 0x02 /* DMA Burst Mode */
|
||||
#define CR_TIMER_EN 0x08 /* Timer Enable */
|
||||
|
||||
/* Alias 3 */
|
||||
#define TIMER_IIR_500 0x00 /* 500 us */
|
||||
#define TIMER_IIR_1ms 0x01 /* 1 ms */
|
||||
#define TIMER_IIR_2ms 0x02 /* 2 ms */
|
||||
#define TIMER_IIR_4ms 0x03 /* 4 ms */
|
||||
|
||||
/* Bank 2 */
|
||||
#define FIR_IRDA_CR 0x00 /* Alias 0, IrDA Control Register (R/W) */
|
||||
#define FIR_BOF_CR 0x01 /* Alias 1, BOF Count Register (R/W) */
|
||||
#define FIR_BW_CR 0x02 /* Alias 2, Brick Wall Count Register (R/W) */
|
||||
#define FIR_TX_DSR_HI 0x03 /* Alias 3, TX Data Size Register (high) (R/W) */
|
||||
#define FIR_TX_DSR_LO 0x04 /* Alias 4, TX Data Size Register (low) (R/W) */
|
||||
#define FIR_RX_DSR_HI 0x05 /* Alias 5, RX Data Size Register (high) (R/W) */
|
||||
#define FIR_RX_DSR_LO 0x06 /* Alias 6, RX Data Size Register (low) (R/W) */
|
||||
|
||||
/* Alias 0 */
|
||||
#define IRDA_CR_HDLC1152 0x80 /* 1.152Mbps HDLC Select */
|
||||
#define IRDA_CR_CRC 0X40 /* CRC Select. */
|
||||
#define IRDA_CR_HDLC 0x20 /* HDLC select. */
|
||||
#define IRDA_CR_HP_MODE 0x10 /* HP mode (read only) */
|
||||
#define IRDA_CR_SD_ST 0x08 /* SD/MODE State. */
|
||||
#define IRDA_CR_FIR_SIN 0x04 /* FIR SIN Select. */
|
||||
#define IRDA_CR_ITTX_0 0x02 /* SOUT State. IRTX force to 0 */
|
||||
#define IRDA_CR_ITTX_1 0x03 /* SOUT State. IRTX force to 1 */
|
||||
|
||||
/* Bank 3 */
|
||||
#define FIR_ID_VR 0x00 /* Alias 0, FIR ID Version Register (R/O) */
|
||||
#define FIR_MODULE_CR 0x01 /* Alias 1, FIR Module Control Register (R/W) */
|
||||
#define FIR_IO_BASE_HI 0x02 /* Alias 2, FIR Higher I/O Base Address Register (R/O) */
|
||||
#define FIR_IO_BASE_LO 0x03 /* Alias 3, FIR Lower I/O Base Address Register (R/O) */
|
||||
#define FIR_IRQ_CR 0x04 /* Alias 4, FIR IRQ Channel Register (R/O) */
|
||||
#define FIR_DMA_CR 0x05 /* Alias 5, FIR DMA Channel Register (R/O) */
|
||||
|
||||
struct ali_chip {
|
||||
char *name;
|
||||
int cfg[2];
|
||||
unsigned char entr1;
|
||||
unsigned char entr2;
|
||||
unsigned char cid_index;
|
||||
unsigned char cid_value;
|
||||
int (*probe)(struct ali_chip *chip, chipio_t *info);
|
||||
int (*init)(struct ali_chip *chip, chipio_t *info);
|
||||
};
|
||||
typedef struct ali_chip ali_chip_t;
|
||||
|
||||
|
||||
/* DMA modes needed */
|
||||
#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */
|
||||
#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */
|
||||
|
||||
#define MAX_TX_WINDOW 7
|
||||
#define MAX_RX_WINDOW 7
|
||||
|
||||
#define TX_FIFO_Threshold 8
|
||||
#define RX_FIFO_Threshold 1
|
||||
#define TX_DMA_Threshold 1
|
||||
#define RX_DMA_Threshold 1
|
||||
|
||||
/* For storing entries in the status FIFO */
|
||||
|
||||
struct st_fifo_entry {
|
||||
int status;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct st_fifo {
|
||||
struct st_fifo_entry entries[MAX_RX_WINDOW];
|
||||
int pending_bytes;
|
||||
int head;
|
||||
int tail;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct frame_cb {
|
||||
void *start; /* Start of frame in DMA mem */
|
||||
int len; /* Length of frame in DMA mem */
|
||||
};
|
||||
|
||||
struct tx_fifo {
|
||||
struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */
|
||||
int ptr; /* Currently being sent */
|
||||
int len; /* Length of queue */
|
||||
int free; /* Next free slot */
|
||||
void *tail; /* Next free start in DMA mem */
|
||||
};
|
||||
|
||||
/* Private data for each instance */
|
||||
struct ali_ircc_cb {
|
||||
|
||||
struct st_fifo st_fifo; /* Info about received frames */
|
||||
struct tx_fifo tx_fifo; /* Info about frames to be transmitted */
|
||||
|
||||
struct net_device *netdev; /* Yes! we are some kind of netdevice */
|
||||
|
||||
struct irlap_cb *irlap; /* The link layer we are binded to */
|
||||
struct qos_info qos; /* QoS capabilities for this device */
|
||||
|
||||
chipio_t io; /* IrDA controller information */
|
||||
iobuff_t tx_buff; /* Transmit buffer */
|
||||
iobuff_t rx_buff; /* Receive buffer */
|
||||
dma_addr_t tx_buff_dma;
|
||||
dma_addr_t rx_buff_dma;
|
||||
|
||||
__u8 ier; /* Interrupt enable register */
|
||||
|
||||
__u8 InterruptID; /* Interrupt ID */
|
||||
__u8 BusStatus; /* Bus Status */
|
||||
__u8 LineStatus; /* Line Status */
|
||||
|
||||
unsigned char rcvFramesOverflow;
|
||||
|
||||
ktime_t stamp;
|
||||
|
||||
spinlock_t lock; /* For serializing operations */
|
||||
|
||||
__u32 new_speed;
|
||||
int index; /* Instance index */
|
||||
|
||||
unsigned char fifo_opti_buf;
|
||||
};
|
||||
|
||||
static inline void switch_bank(int iobase, int bank)
|
||||
{
|
||||
outb(bank, iobase+FIR_MCR);
|
||||
}
|
||||
|
||||
#endif /* ALI_IRCC_H */
|
|
@ -1,985 +0,0 @@
|
|||
/*
|
||||
* Alchemy Semi Au1000 IrDA driver
|
||||
*
|
||||
* Copyright 2001 MontaVista Software Inc.
|
||||
* Author: MontaVista Software, Inc.
|
||||
* ppopov@mvista.com or source@mvista.com
|
||||
*
|
||||
* This program is free software; you can distribute it and/or modify it
|
||||
* under the terms of the GNU General Public License (Version 2) as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irmod.h>
|
||||
#include <net/irda/wrapper.h>
|
||||
#include <net/irda/irda_device.h>
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
|
||||
/* registers */
|
||||
#define IR_RING_PTR_STATUS 0x00
|
||||
#define IR_RING_BASE_ADDR_H 0x04
|
||||
#define IR_RING_BASE_ADDR_L 0x08
|
||||
#define IR_RING_SIZE 0x0C
|
||||
#define IR_RING_PROMPT 0x10
|
||||
#define IR_RING_ADDR_CMPR 0x14
|
||||
#define IR_INT_CLEAR 0x18
|
||||
#define IR_CONFIG_1 0x20
|
||||
#define IR_SIR_FLAGS 0x24
|
||||
#define IR_STATUS 0x28
|
||||
#define IR_READ_PHY_CONFIG 0x2C
|
||||
#define IR_WRITE_PHY_CONFIG 0x30
|
||||
#define IR_MAX_PKT_LEN 0x34
|
||||
#define IR_RX_BYTE_CNT 0x38
|
||||
#define IR_CONFIG_2 0x3C
|
||||
#define IR_ENABLE 0x40
|
||||
|
||||
/* Config1 */
|
||||
#define IR_RX_INVERT_LED (1 << 0)
|
||||
#define IR_TX_INVERT_LED (1 << 1)
|
||||
#define IR_ST (1 << 2)
|
||||
#define IR_SF (1 << 3)
|
||||
#define IR_SIR (1 << 4)
|
||||
#define IR_MIR (1 << 5)
|
||||
#define IR_FIR (1 << 6)
|
||||
#define IR_16CRC (1 << 7)
|
||||
#define IR_TD (1 << 8)
|
||||
#define IR_RX_ALL (1 << 9)
|
||||
#define IR_DMA_ENABLE (1 << 10)
|
||||
#define IR_RX_ENABLE (1 << 11)
|
||||
#define IR_TX_ENABLE (1 << 12)
|
||||
#define IR_LOOPBACK (1 << 14)
|
||||
#define IR_SIR_MODE (IR_SIR | IR_DMA_ENABLE | \
|
||||
IR_RX_ALL | IR_RX_ENABLE | IR_SF | \
|
||||
IR_16CRC)
|
||||
|
||||
/* ir_status */
|
||||
#define IR_RX_STATUS (1 << 9)
|
||||
#define IR_TX_STATUS (1 << 10)
|
||||
#define IR_PHYEN (1 << 15)
|
||||
|
||||
/* ir_write_phy_config */
|
||||
#define IR_BR(x) (((x) & 0x3f) << 10) /* baud rate */
|
||||
#define IR_PW(x) (((x) & 0x1f) << 5) /* pulse width */
|
||||
#define IR_P(x) ((x) & 0x1f) /* preamble bits */
|
||||
|
||||
/* Config2 */
|
||||
#define IR_MODE_INV (1 << 0)
|
||||
#define IR_ONE_PIN (1 << 1)
|
||||
#define IR_PHYCLK_40MHZ (0 << 2)
|
||||
#define IR_PHYCLK_48MHZ (1 << 2)
|
||||
#define IR_PHYCLK_56MHZ (2 << 2)
|
||||
#define IR_PHYCLK_64MHZ (3 << 2)
|
||||
#define IR_DP (1 << 4)
|
||||
#define IR_DA (1 << 5)
|
||||
#define IR_FLT_HIGH (0 << 6)
|
||||
#define IR_FLT_MEDHI (1 << 6)
|
||||
#define IR_FLT_MEDLO (2 << 6)
|
||||
#define IR_FLT_LO (3 << 6)
|
||||
#define IR_IEN (1 << 8)
|
||||
|
||||
/* ir_enable */
|
||||
#define IR_HC (1 << 3) /* divide SBUS clock by 2 */
|
||||
#define IR_CE (1 << 2) /* clock enable */
|
||||
#define IR_C (1 << 1) /* coherency bit */
|
||||
#define IR_BE (1 << 0) /* set in big endian mode */
|
||||
|
||||
#define NUM_IR_DESC 64
|
||||
#define RING_SIZE_4 0x0
|
||||
#define RING_SIZE_16 0x3
|
||||
#define RING_SIZE_64 0xF
|
||||
#define MAX_NUM_IR_DESC 64
|
||||
#define MAX_BUF_SIZE 2048
|
||||
|
||||
/* Ring descriptor flags */
|
||||
#define AU_OWN (1 << 7) /* tx,rx */
|
||||
#define IR_DIS_CRC (1 << 6) /* tx */
|
||||
#define IR_BAD_CRC (1 << 5) /* tx */
|
||||
#define IR_NEED_PULSE (1 << 4) /* tx */
|
||||
#define IR_FORCE_UNDER (1 << 3) /* tx */
|
||||
#define IR_DISABLE_TX (1 << 2) /* tx */
|
||||
#define IR_HW_UNDER (1 << 0) /* tx */
|
||||
#define IR_TX_ERROR (IR_DIS_CRC | IR_BAD_CRC | IR_HW_UNDER)
|
||||
|
||||
#define IR_PHY_ERROR (1 << 6) /* rx */
|
||||
#define IR_CRC_ERROR (1 << 5) /* rx */
|
||||
#define IR_MAX_LEN (1 << 4) /* rx */
|
||||
#define IR_FIFO_OVER (1 << 3) /* rx */
|
||||
#define IR_SIR_ERROR (1 << 2) /* rx */
|
||||
#define IR_RX_ERROR (IR_PHY_ERROR | IR_CRC_ERROR | \
|
||||
IR_MAX_LEN | IR_FIFO_OVER | IR_SIR_ERROR)
|
||||
|
||||
struct db_dest {
|
||||
struct db_dest *pnext;
|
||||
volatile u32 *vaddr;
|
||||
dma_addr_t dma_addr;
|
||||
};
|
||||
|
||||
struct ring_dest {
|
||||
u8 count_0; /* 7:0 */
|
||||
u8 count_1; /* 12:8 */
|
||||
u8 reserved;
|
||||
u8 flags;
|
||||
u8 addr_0; /* 7:0 */
|
||||
u8 addr_1; /* 15:8 */
|
||||
u8 addr_2; /* 23:16 */
|
||||
u8 addr_3; /* 31:24 */
|
||||
};
|
||||
|
||||
/* Private data for each instance */
|
||||
struct au1k_private {
|
||||
void __iomem *iobase;
|
||||
int irq_rx, irq_tx;
|
||||
|
||||
struct db_dest *pDBfree;
|
||||
struct db_dest db[2 * NUM_IR_DESC];
|
||||
volatile struct ring_dest *rx_ring[NUM_IR_DESC];
|
||||
volatile struct ring_dest *tx_ring[NUM_IR_DESC];
|
||||
struct db_dest *rx_db_inuse[NUM_IR_DESC];
|
||||
struct db_dest *tx_db_inuse[NUM_IR_DESC];
|
||||
u32 rx_head;
|
||||
u32 tx_head;
|
||||
u32 tx_tail;
|
||||
u32 tx_full;
|
||||
|
||||
iobuff_t rx_buff;
|
||||
|
||||
struct net_device *netdev;
|
||||
struct qos_info qos;
|
||||
struct irlap_cb *irlap;
|
||||
|
||||
u8 open;
|
||||
u32 speed;
|
||||
u32 newspeed;
|
||||
|
||||
struct resource *ioarea;
|
||||
struct au1k_irda_platform_data *platdata;
|
||||
struct clk *irda_clk;
|
||||
};
|
||||
|
||||
static int qos_mtt_bits = 0x07; /* 1 ms or more */
|
||||
|
||||
static void au1k_irda_plat_set_phy_mode(struct au1k_private *p, int mode)
|
||||
{
|
||||
if (p->platdata && p->platdata->set_phy_mode)
|
||||
p->platdata->set_phy_mode(mode);
|
||||
}
|
||||
|
||||
static inline unsigned long irda_read(struct au1k_private *p,
|
||||
unsigned long ofs)
|
||||
{
|
||||
/*
|
||||
* IrDA peripheral bug. You have to read the register
|
||||
* twice to get the right value.
|
||||
*/
|
||||
(void)__raw_readl(p->iobase + ofs);
|
||||
return __raw_readl(p->iobase + ofs);
|
||||
}
|
||||
|
||||
static inline void irda_write(struct au1k_private *p, unsigned long ofs,
|
||||
unsigned long val)
|
||||
{
|
||||
__raw_writel(val, p->iobase + ofs);
|
||||
wmb();
|
||||
}
|
||||
|
||||
/*
|
||||
* Buffer allocation/deallocation routines. The buffer descriptor returned
|
||||
* has the virtual and dma address of a buffer suitable for
|
||||
* both, receive and transmit operations.
|
||||
*/
|
||||
static struct db_dest *GetFreeDB(struct au1k_private *aup)
|
||||
{
|
||||
struct db_dest *db;
|
||||
db = aup->pDBfree;
|
||||
|
||||
if (db)
|
||||
aup->pDBfree = db->pnext;
|
||||
return db;
|
||||
}
|
||||
|
||||
/*
|
||||
DMA memory allocation, derived from pci_alloc_consistent.
|
||||
However, the Au1000 data cache is coherent (when programmed
|
||||
so), therefore we return KSEG0 address, not KSEG1.
|
||||
*/
|
||||
static void *dma_alloc(size_t size, dma_addr_t *dma_handle)
|
||||
{
|
||||
void *ret;
|
||||
int gfp = GFP_ATOMIC | GFP_DMA;
|
||||
|
||||
ret = (void *)__get_free_pages(gfp, get_order(size));
|
||||
|
||||
if (ret != NULL) {
|
||||
memset(ret, 0, size);
|
||||
*dma_handle = virt_to_bus(ret);
|
||||
ret = (void *)KSEG0ADDR(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dma_free(void *vaddr, size_t size)
|
||||
{
|
||||
vaddr = (void *)KSEG0ADDR(vaddr);
|
||||
free_pages((unsigned long) vaddr, get_order(size));
|
||||
}
|
||||
|
||||
|
||||
static void setup_hw_rings(struct au1k_private *aup, u32 rx_base, u32 tx_base)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < NUM_IR_DESC; i++) {
|
||||
aup->rx_ring[i] = (volatile struct ring_dest *)
|
||||
(rx_base + sizeof(struct ring_dest) * i);
|
||||
}
|
||||
for (i = 0; i < NUM_IR_DESC; i++) {
|
||||
aup->tx_ring[i] = (volatile struct ring_dest *)
|
||||
(tx_base + sizeof(struct ring_dest) * i);
|
||||
}
|
||||
}
|
||||
|
||||
static int au1k_irda_init_iobuf(iobuff_t *io, int size)
|
||||
{
|
||||
io->head = kmalloc(size, GFP_KERNEL);
|
||||
if (io->head != NULL) {
|
||||
io->truesize = size;
|
||||
io->in_frame = FALSE;
|
||||
io->state = OUTSIDE_FRAME;
|
||||
io->data = io->head;
|
||||
}
|
||||
return io->head ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the IrDA communications speed.
|
||||
*/
|
||||
static int au1k_irda_set_speed(struct net_device *dev, int speed)
|
||||
{
|
||||
struct au1k_private *aup = netdev_priv(dev);
|
||||
volatile struct ring_dest *ptxd;
|
||||
unsigned long control;
|
||||
int ret = 0, timeout = 10, i;
|
||||
|
||||
if (speed == aup->speed)
|
||||
return ret;
|
||||
|
||||
/* disable PHY first */
|
||||
au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF);
|
||||
irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) & ~IR_PHYEN);
|
||||
|
||||
/* disable RX/TX */
|
||||
irda_write(aup, IR_CONFIG_1,
|
||||
irda_read(aup, IR_CONFIG_1) & ~(IR_RX_ENABLE | IR_TX_ENABLE));
|
||||
msleep(20);
|
||||
while (irda_read(aup, IR_STATUS) & (IR_RX_STATUS | IR_TX_STATUS)) {
|
||||
msleep(20);
|
||||
if (!timeout--) {
|
||||
netdev_err(dev, "rx/tx disable timeout\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* disable DMA */
|
||||
irda_write(aup, IR_CONFIG_1,
|
||||
irda_read(aup, IR_CONFIG_1) & ~IR_DMA_ENABLE);
|
||||
msleep(20);
|
||||
|
||||
/* After we disable tx/rx. the index pointers go back to zero. */
|
||||
aup->tx_head = aup->tx_tail = aup->rx_head = 0;
|
||||
for (i = 0; i < NUM_IR_DESC; i++) {
|
||||
ptxd = aup->tx_ring[i];
|
||||
ptxd->flags = 0;
|
||||
ptxd->count_0 = 0;
|
||||
ptxd->count_1 = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_IR_DESC; i++) {
|
||||
ptxd = aup->rx_ring[i];
|
||||
ptxd->count_0 = 0;
|
||||
ptxd->count_1 = 0;
|
||||
ptxd->flags = AU_OWN;
|
||||
}
|
||||
|
||||
if (speed == 4000000)
|
||||
au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_FIR);
|
||||
else
|
||||
au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_SIR);
|
||||
|
||||
switch (speed) {
|
||||
case 9600:
|
||||
irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(11) | IR_PW(12));
|
||||
irda_write(aup, IR_CONFIG_1, IR_SIR_MODE);
|
||||
break;
|
||||
case 19200:
|
||||
irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(5) | IR_PW(12));
|
||||
irda_write(aup, IR_CONFIG_1, IR_SIR_MODE);
|
||||
break;
|
||||
case 38400:
|
||||
irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(2) | IR_PW(12));
|
||||
irda_write(aup, IR_CONFIG_1, IR_SIR_MODE);
|
||||
break;
|
||||
case 57600:
|
||||
irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(1) | IR_PW(12));
|
||||
irda_write(aup, IR_CONFIG_1, IR_SIR_MODE);
|
||||
break;
|
||||
case 115200:
|
||||
irda_write(aup, IR_WRITE_PHY_CONFIG, IR_PW(12));
|
||||
irda_write(aup, IR_CONFIG_1, IR_SIR_MODE);
|
||||
break;
|
||||
case 4000000:
|
||||
irda_write(aup, IR_WRITE_PHY_CONFIG, IR_P(15));
|
||||
irda_write(aup, IR_CONFIG_1, IR_FIR | IR_DMA_ENABLE |
|
||||
IR_RX_ENABLE);
|
||||
break;
|
||||
default:
|
||||
netdev_err(dev, "unsupported speed %x\n", speed);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
aup->speed = speed;
|
||||
irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) | IR_PHYEN);
|
||||
|
||||
control = irda_read(aup, IR_STATUS);
|
||||
irda_write(aup, IR_RING_PROMPT, 0);
|
||||
|
||||
if (control & (1 << 14)) {
|
||||
netdev_err(dev, "configuration error\n");
|
||||
} else {
|
||||
if (control & (1 << 11))
|
||||
netdev_debug(dev, "Valid SIR config\n");
|
||||
if (control & (1 << 12))
|
||||
netdev_debug(dev, "Valid MIR config\n");
|
||||
if (control & (1 << 13))
|
||||
netdev_debug(dev, "Valid FIR config\n");
|
||||
if (control & (1 << 10))
|
||||
netdev_debug(dev, "TX enabled\n");
|
||||
if (control & (1 << 9))
|
||||
netdev_debug(dev, "RX enabled\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void update_rx_stats(struct net_device *dev, u32 status, u32 count)
|
||||
{
|
||||
struct net_device_stats *ps = &dev->stats;
|
||||
|
||||
ps->rx_packets++;
|
||||
|
||||
if (status & IR_RX_ERROR) {
|
||||
ps->rx_errors++;
|
||||
if (status & (IR_PHY_ERROR | IR_FIFO_OVER))
|
||||
ps->rx_missed_errors++;
|
||||
if (status & IR_MAX_LEN)
|
||||
ps->rx_length_errors++;
|
||||
if (status & IR_CRC_ERROR)
|
||||
ps->rx_crc_errors++;
|
||||
} else
|
||||
ps->rx_bytes += count;
|
||||
}
|
||||
|
||||
static void update_tx_stats(struct net_device *dev, u32 status, u32 pkt_len)
|
||||
{
|
||||
struct net_device_stats *ps = &dev->stats;
|
||||
|
||||
ps->tx_packets++;
|
||||
ps->tx_bytes += pkt_len;
|
||||
|
||||
if (status & IR_TX_ERROR) {
|
||||
ps->tx_errors++;
|
||||
ps->tx_aborted_errors++;
|
||||
}
|
||||
}
|
||||
|
||||
static void au1k_tx_ack(struct net_device *dev)
|
||||
{
|
||||
struct au1k_private *aup = netdev_priv(dev);
|
||||
volatile struct ring_dest *ptxd;
|
||||
|
||||
ptxd = aup->tx_ring[aup->tx_tail];
|
||||
while (!(ptxd->flags & AU_OWN) && (aup->tx_tail != aup->tx_head)) {
|
||||
update_tx_stats(dev, ptxd->flags,
|
||||
(ptxd->count_1 << 8) | ptxd->count_0);
|
||||
ptxd->count_0 = 0;
|
||||
ptxd->count_1 = 0;
|
||||
wmb();
|
||||
aup->tx_tail = (aup->tx_tail + 1) & (NUM_IR_DESC - 1);
|
||||
ptxd = aup->tx_ring[aup->tx_tail];
|
||||
|
||||
if (aup->tx_full) {
|
||||
aup->tx_full = 0;
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
}
|
||||
|
||||
if (aup->tx_tail == aup->tx_head) {
|
||||
if (aup->newspeed) {
|
||||
au1k_irda_set_speed(dev, aup->newspeed);
|
||||
aup->newspeed = 0;
|
||||
} else {
|
||||
irda_write(aup, IR_CONFIG_1,
|
||||
irda_read(aup, IR_CONFIG_1) & ~IR_TX_ENABLE);
|
||||
irda_write(aup, IR_CONFIG_1,
|
||||
irda_read(aup, IR_CONFIG_1) | IR_RX_ENABLE);
|
||||
irda_write(aup, IR_RING_PROMPT, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int au1k_irda_rx(struct net_device *dev)
|
||||
{
|
||||
struct au1k_private *aup = netdev_priv(dev);
|
||||
volatile struct ring_dest *prxd;
|
||||
struct sk_buff *skb;
|
||||
struct db_dest *pDB;
|
||||
u32 flags, count;
|
||||
|
||||
prxd = aup->rx_ring[aup->rx_head];
|
||||
flags = prxd->flags;
|
||||
|
||||
while (!(flags & AU_OWN)) {
|
||||
pDB = aup->rx_db_inuse[aup->rx_head];
|
||||
count = (prxd->count_1 << 8) | prxd->count_0;
|
||||
if (!(flags & IR_RX_ERROR)) {
|
||||
/* good frame */
|
||||
update_rx_stats(dev, flags, count);
|
||||
skb = alloc_skb(count + 1, GFP_ATOMIC);
|
||||
if (skb == NULL) {
|
||||
dev->stats.rx_dropped++;
|
||||
continue;
|
||||
}
|
||||
skb_reserve(skb, 1);
|
||||
if (aup->speed == 4000000)
|
||||
skb_put(skb, count);
|
||||
else
|
||||
skb_put(skb, count - 2);
|
||||
skb_copy_to_linear_data(skb, (void *)pDB->vaddr,
|
||||
count - 2);
|
||||
skb->dev = dev;
|
||||
skb_reset_mac_header(skb);
|
||||
skb->protocol = htons(ETH_P_IRDA);
|
||||
netif_rx(skb);
|
||||
prxd->count_0 = 0;
|
||||
prxd->count_1 = 0;
|
||||
}
|
||||
prxd->flags |= AU_OWN;
|
||||
aup->rx_head = (aup->rx_head + 1) & (NUM_IR_DESC - 1);
|
||||
irda_write(aup, IR_RING_PROMPT, 0);
|
||||
|
||||
/* next descriptor */
|
||||
prxd = aup->rx_ring[aup->rx_head];
|
||||
flags = prxd->flags;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t au1k_irda_interrupt(int dummy, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct au1k_private *aup = netdev_priv(dev);
|
||||
|
||||
irda_write(aup, IR_INT_CLEAR, 0); /* ack irda interrupts */
|
||||
|
||||
au1k_irda_rx(dev);
|
||||
au1k_tx_ack(dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int au1k_init(struct net_device *dev)
|
||||
{
|
||||
struct au1k_private *aup = netdev_priv(dev);
|
||||
u32 enable, ring_address, phyck;
|
||||
struct clk *c;
|
||||
int i;
|
||||
|
||||
c = clk_get(NULL, "irda_clk");
|
||||
if (IS_ERR(c))
|
||||
return PTR_ERR(c);
|
||||
i = clk_prepare_enable(c);
|
||||
if (i) {
|
||||
clk_put(c);
|
||||
return i;
|
||||
}
|
||||
|
||||
switch (clk_get_rate(c)) {
|
||||
case 40000000:
|
||||
phyck = IR_PHYCLK_40MHZ;
|
||||
break;
|
||||
case 48000000:
|
||||
phyck = IR_PHYCLK_48MHZ;
|
||||
break;
|
||||
case 56000000:
|
||||
phyck = IR_PHYCLK_56MHZ;
|
||||
break;
|
||||
case 64000000:
|
||||
phyck = IR_PHYCLK_64MHZ;
|
||||
break;
|
||||
default:
|
||||
clk_disable_unprepare(c);
|
||||
clk_put(c);
|
||||
return -EINVAL;
|
||||
}
|
||||
aup->irda_clk = c;
|
||||
|
||||
enable = IR_HC | IR_CE | IR_C;
|
||||
#ifndef CONFIG_CPU_LITTLE_ENDIAN
|
||||
enable |= IR_BE;
|
||||
#endif
|
||||
aup->tx_head = 0;
|
||||
aup->tx_tail = 0;
|
||||
aup->rx_head = 0;
|
||||
|
||||
for (i = 0; i < NUM_IR_DESC; i++)
|
||||
aup->rx_ring[i]->flags = AU_OWN;
|
||||
|
||||
irda_write(aup, IR_ENABLE, enable);
|
||||
msleep(20);
|
||||
|
||||
/* disable PHY */
|
||||
au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF);
|
||||
irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) & ~IR_PHYEN);
|
||||
msleep(20);
|
||||
|
||||
irda_write(aup, IR_MAX_PKT_LEN, MAX_BUF_SIZE);
|
||||
|
||||
ring_address = (u32)virt_to_phys((void *)aup->rx_ring[0]);
|
||||
irda_write(aup, IR_RING_BASE_ADDR_H, ring_address >> 26);
|
||||
irda_write(aup, IR_RING_BASE_ADDR_L, (ring_address >> 10) & 0xffff);
|
||||
|
||||
irda_write(aup, IR_RING_SIZE,
|
||||
(RING_SIZE_64 << 8) | (RING_SIZE_64 << 12));
|
||||
|
||||
irda_write(aup, IR_CONFIG_2, phyck | IR_ONE_PIN);
|
||||
irda_write(aup, IR_RING_ADDR_CMPR, 0);
|
||||
|
||||
au1k_irda_set_speed(dev, 9600);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au1k_irda_start(struct net_device *dev)
|
||||
{
|
||||
struct au1k_private *aup = netdev_priv(dev);
|
||||
char hwname[32];
|
||||
int retval;
|
||||
|
||||
retval = au1k_init(dev);
|
||||
if (retval) {
|
||||
netdev_err(dev, "error in au1k_init\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = request_irq(aup->irq_tx, &au1k_irda_interrupt, 0,
|
||||
dev->name, dev);
|
||||
if (retval) {
|
||||
netdev_err(dev, "unable to get IRQ %d\n", dev->irq);
|
||||
return retval;
|
||||
}
|
||||
retval = request_irq(aup->irq_rx, &au1k_irda_interrupt, 0,
|
||||
dev->name, dev);
|
||||
if (retval) {
|
||||
free_irq(aup->irq_tx, dev);
|
||||
netdev_err(dev, "unable to get IRQ %d\n", dev->irq);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Give self a hardware name */
|
||||
sprintf(hwname, "Au1000 SIR/FIR");
|
||||
aup->irlap = irlap_open(dev, &aup->qos, hwname);
|
||||
netif_start_queue(dev);
|
||||
|
||||
/* int enable */
|
||||
irda_write(aup, IR_CONFIG_2, irda_read(aup, IR_CONFIG_2) | IR_IEN);
|
||||
|
||||
/* power up */
|
||||
au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_SIR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au1k_irda_stop(struct net_device *dev)
|
||||
{
|
||||
struct au1k_private *aup = netdev_priv(dev);
|
||||
|
||||
au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF);
|
||||
|
||||
/* disable interrupts */
|
||||
irda_write(aup, IR_CONFIG_2, irda_read(aup, IR_CONFIG_2) & ~IR_IEN);
|
||||
irda_write(aup, IR_CONFIG_1, 0);
|
||||
irda_write(aup, IR_ENABLE, 0); /* disable clock */
|
||||
|
||||
if (aup->irlap) {
|
||||
irlap_close(aup->irlap);
|
||||
aup->irlap = NULL;
|
||||
}
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
/* disable the interrupt */
|
||||
free_irq(aup->irq_tx, dev);
|
||||
free_irq(aup->irq_rx, dev);
|
||||
|
||||
clk_disable_unprepare(aup->irda_clk);
|
||||
clk_put(aup->irda_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Au1000 transmit routine.
|
||||
*/
|
||||
static int au1k_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct au1k_private *aup = netdev_priv(dev);
|
||||
int speed = irda_get_next_speed(skb);
|
||||
volatile struct ring_dest *ptxd;
|
||||
struct db_dest *pDB;
|
||||
u32 len, flags;
|
||||
|
||||
if (speed != aup->speed && speed != -1)
|
||||
aup->newspeed = speed;
|
||||
|
||||
if ((skb->len == 0) && (aup->newspeed)) {
|
||||
if (aup->tx_tail == aup->tx_head) {
|
||||
au1k_irda_set_speed(dev, speed);
|
||||
aup->newspeed = 0;
|
||||
}
|
||||
dev_kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
ptxd = aup->tx_ring[aup->tx_head];
|
||||
flags = ptxd->flags;
|
||||
|
||||
if (flags & AU_OWN) {
|
||||
netdev_debug(dev, "tx_full\n");
|
||||
netif_stop_queue(dev);
|
||||
aup->tx_full = 1;
|
||||
return 1;
|
||||
} else if (((aup->tx_head + 1) & (NUM_IR_DESC - 1)) == aup->tx_tail) {
|
||||
netdev_debug(dev, "tx_full\n");
|
||||
netif_stop_queue(dev);
|
||||
aup->tx_full = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
pDB = aup->tx_db_inuse[aup->tx_head];
|
||||
|
||||
#if 0
|
||||
if (irda_read(aup, IR_RX_BYTE_CNT) != 0) {
|
||||
netdev_debug(dev, "tx warning: rx byte cnt %x\n",
|
||||
irda_read(aup, IR_RX_BYTE_CNT));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (aup->speed == 4000000) {
|
||||
/* FIR */
|
||||
skb_copy_from_linear_data(skb, (void *)pDB->vaddr, skb->len);
|
||||
ptxd->count_0 = skb->len & 0xff;
|
||||
ptxd->count_1 = (skb->len >> 8) & 0xff;
|
||||
} else {
|
||||
/* SIR */
|
||||
len = async_wrap_skb(skb, (u8 *)pDB->vaddr, MAX_BUF_SIZE);
|
||||
ptxd->count_0 = len & 0xff;
|
||||
ptxd->count_1 = (len >> 8) & 0xff;
|
||||
ptxd->flags |= IR_DIS_CRC;
|
||||
}
|
||||
ptxd->flags |= AU_OWN;
|
||||
wmb();
|
||||
|
||||
irda_write(aup, IR_CONFIG_1,
|
||||
irda_read(aup, IR_CONFIG_1) | IR_TX_ENABLE);
|
||||
irda_write(aup, IR_RING_PROMPT, 0);
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
aup->tx_head = (aup->tx_head + 1) & (NUM_IR_DESC - 1);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* The Tx ring has been full longer than the watchdog timeout
|
||||
* value. The transmitter must be hung?
|
||||
*/
|
||||
static void au1k_tx_timeout(struct net_device *dev)
|
||||
{
|
||||
u32 speed;
|
||||
struct au1k_private *aup = netdev_priv(dev);
|
||||
|
||||
netdev_err(dev, "tx timeout\n");
|
||||
speed = aup->speed;
|
||||
aup->speed = 0;
|
||||
au1k_irda_set_speed(dev, speed);
|
||||
aup->tx_full = 0;
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
static int au1k_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
|
||||
{
|
||||
struct if_irda_req *rq = (struct if_irda_req *)ifreq;
|
||||
struct au1k_private *aup = netdev_priv(dev);
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
switch (cmd) {
|
||||
case SIOCSBANDWIDTH:
|
||||
if (capable(CAP_NET_ADMIN)) {
|
||||
/*
|
||||
* We are unable to set the speed if the
|
||||
* device is not running.
|
||||
*/
|
||||
if (aup->open)
|
||||
ret = au1k_irda_set_speed(dev,
|
||||
rq->ifr_baudrate);
|
||||
else {
|
||||
netdev_err(dev, "ioctl: !netif_running\n");
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SIOCSMEDIABUSY:
|
||||
ret = -EPERM;
|
||||
if (capable(CAP_NET_ADMIN)) {
|
||||
irda_device_set_media_busy(dev, TRUE);
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIOCGRECEIVING:
|
||||
rq->ifr_receiving = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct net_device_ops au1k_irda_netdev_ops = {
|
||||
.ndo_open = au1k_irda_start,
|
||||
.ndo_stop = au1k_irda_stop,
|
||||
.ndo_start_xmit = au1k_irda_hard_xmit,
|
||||
.ndo_tx_timeout = au1k_tx_timeout,
|
||||
.ndo_do_ioctl = au1k_irda_ioctl,
|
||||
};
|
||||
|
||||
static int au1k_irda_net_init(struct net_device *dev)
|
||||
{
|
||||
struct au1k_private *aup = netdev_priv(dev);
|
||||
struct db_dest *pDB, *pDBfree;
|
||||
int i, err, retval = 0;
|
||||
dma_addr_t temp;
|
||||
|
||||
err = au1k_irda_init_iobuf(&aup->rx_buff, 14384);
|
||||
if (err)
|
||||
goto out1;
|
||||
|
||||
dev->netdev_ops = &au1k_irda_netdev_ops;
|
||||
|
||||
irda_init_max_qos_capabilies(&aup->qos);
|
||||
|
||||
/* The only value we must override it the baudrate */
|
||||
aup->qos.baud_rate.bits = IR_9600 | IR_19200 | IR_38400 |
|
||||
IR_57600 | IR_115200 | IR_576000 | (IR_4000000 << 8);
|
||||
|
||||
aup->qos.min_turn_time.bits = qos_mtt_bits;
|
||||
irda_qos_bits_to_value(&aup->qos);
|
||||
|
||||
retval = -ENOMEM;
|
||||
|
||||
/* Tx ring follows rx ring + 512 bytes */
|
||||
/* we need a 1k aligned buffer */
|
||||
aup->rx_ring[0] = (struct ring_dest *)
|
||||
dma_alloc(2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest)),
|
||||
&temp);
|
||||
if (!aup->rx_ring[0])
|
||||
goto out2;
|
||||
|
||||
/* allocate the data buffers */
|
||||
aup->db[0].vaddr =
|
||||
dma_alloc(MAX_BUF_SIZE * 2 * NUM_IR_DESC, &temp);
|
||||
if (!aup->db[0].vaddr)
|
||||
goto out3;
|
||||
|
||||
setup_hw_rings(aup, (u32)aup->rx_ring[0], (u32)aup->rx_ring[0] + 512);
|
||||
|
||||
pDBfree = NULL;
|
||||
pDB = aup->db;
|
||||
for (i = 0; i < (2 * NUM_IR_DESC); i++) {
|
||||
pDB->pnext = pDBfree;
|
||||
pDBfree = pDB;
|
||||
pDB->vaddr =
|
||||
(u32 *)((unsigned)aup->db[0].vaddr + (MAX_BUF_SIZE * i));
|
||||
pDB->dma_addr = (dma_addr_t)virt_to_bus(pDB->vaddr);
|
||||
pDB++;
|
||||
}
|
||||
aup->pDBfree = pDBfree;
|
||||
|
||||
/* attach a data buffer to each descriptor */
|
||||
for (i = 0; i < NUM_IR_DESC; i++) {
|
||||
pDB = GetFreeDB(aup);
|
||||
if (!pDB)
|
||||
goto out3;
|
||||
aup->rx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff);
|
||||
aup->rx_ring[i]->addr_1 = (u8)((pDB->dma_addr >> 8) & 0xff);
|
||||
aup->rx_ring[i]->addr_2 = (u8)((pDB->dma_addr >> 16) & 0xff);
|
||||
aup->rx_ring[i]->addr_3 = (u8)((pDB->dma_addr >> 24) & 0xff);
|
||||
aup->rx_db_inuse[i] = pDB;
|
||||
}
|
||||
for (i = 0; i < NUM_IR_DESC; i++) {
|
||||
pDB = GetFreeDB(aup);
|
||||
if (!pDB)
|
||||
goto out3;
|
||||
aup->tx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff);
|
||||
aup->tx_ring[i]->addr_1 = (u8)((pDB->dma_addr >> 8) & 0xff);
|
||||
aup->tx_ring[i]->addr_2 = (u8)((pDB->dma_addr >> 16) & 0xff);
|
||||
aup->tx_ring[i]->addr_3 = (u8)((pDB->dma_addr >> 24) & 0xff);
|
||||
aup->tx_ring[i]->count_0 = 0;
|
||||
aup->tx_ring[i]->count_1 = 0;
|
||||
aup->tx_ring[i]->flags = 0;
|
||||
aup->tx_db_inuse[i] = pDB;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out3:
|
||||
dma_free((void *)aup->rx_ring[0],
|
||||
2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest)));
|
||||
out2:
|
||||
kfree(aup->rx_buff.head);
|
||||
out1:
|
||||
netdev_err(dev, "au1k_irda_net_init() failed. Returns %d\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int au1k_irda_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct au1k_private *aup;
|
||||
struct net_device *dev;
|
||||
struct resource *r;
|
||||
struct clk *c;
|
||||
int err;
|
||||
|
||||
dev = alloc_irdadev(sizeof(struct au1k_private));
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
aup = netdev_priv(dev);
|
||||
|
||||
aup->platdata = pdev->dev.platform_data;
|
||||
|
||||
err = -EINVAL;
|
||||
r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!r)
|
||||
goto out;
|
||||
|
||||
aup->irq_tx = r->start;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
|
||||
if (!r)
|
||||
goto out;
|
||||
|
||||
aup->irq_rx = r->start;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r)
|
||||
goto out;
|
||||
|
||||
err = -EBUSY;
|
||||
aup->ioarea = request_mem_region(r->start, resource_size(r),
|
||||
pdev->name);
|
||||
if (!aup->ioarea)
|
||||
goto out;
|
||||
|
||||
/* bail out early if clock doesn't exist */
|
||||
c = clk_get(NULL, "irda_clk");
|
||||
if (IS_ERR(c)) {
|
||||
err = PTR_ERR(c);
|
||||
goto out;
|
||||
}
|
||||
clk_put(c);
|
||||
|
||||
aup->iobase = ioremap_nocache(r->start, resource_size(r));
|
||||
if (!aup->iobase)
|
||||
goto out2;
|
||||
|
||||
dev->irq = aup->irq_rx;
|
||||
|
||||
err = au1k_irda_net_init(dev);
|
||||
if (err)
|
||||
goto out3;
|
||||
err = register_netdev(dev);
|
||||
if (err)
|
||||
goto out4;
|
||||
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
netdev_info(dev, "IrDA: Registered device\n");
|
||||
return 0;
|
||||
|
||||
out4:
|
||||
dma_free((void *)aup->db[0].vaddr,
|
||||
MAX_BUF_SIZE * 2 * NUM_IR_DESC);
|
||||
dma_free((void *)aup->rx_ring[0],
|
||||
2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest)));
|
||||
kfree(aup->rx_buff.head);
|
||||
out3:
|
||||
iounmap(aup->iobase);
|
||||
out2:
|
||||
release_resource(aup->ioarea);
|
||||
kfree(aup->ioarea);
|
||||
out:
|
||||
free_netdev(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int au1k_irda_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
struct au1k_private *aup = netdev_priv(dev);
|
||||
|
||||
unregister_netdev(dev);
|
||||
|
||||
dma_free((void *)aup->db[0].vaddr,
|
||||
MAX_BUF_SIZE * 2 * NUM_IR_DESC);
|
||||
dma_free((void *)aup->rx_ring[0],
|
||||
2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest)));
|
||||
kfree(aup->rx_buff.head);
|
||||
|
||||
iounmap(aup->iobase);
|
||||
release_resource(aup->ioarea);
|
||||
kfree(aup->ioarea);
|
||||
|
||||
free_netdev(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver au1k_irda_driver = {
|
||||
.driver = {
|
||||
.name = "au1000-irda",
|
||||
},
|
||||
.probe = au1k_irda_probe,
|
||||
.remove = au1k_irda_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(au1k_irda_driver);
|
||||
|
||||
MODULE_AUTHOR("Pete Popov <ppopov@mvista.com>");
|
||||
MODULE_DESCRIPTION("Au1000 IrDA Device Driver");
|
|
@ -1,819 +0,0 @@
|
|||
/*
|
||||
* Blackfin Infra-red Driver
|
||||
*
|
||||
* Copyright 2006-2009 Analog Devices Inc.
|
||||
*
|
||||
* Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*
|
||||
*/
|
||||
#include "bfin_sir.h"
|
||||
|
||||
#ifdef CONFIG_SIR_BFIN_DMA
|
||||
#define DMA_SIR_RX_XCNT 10
|
||||
#define DMA_SIR_RX_YCNT (PAGE_SIZE / DMA_SIR_RX_XCNT)
|
||||
#define DMA_SIR_RX_FLUSH_JIFS (HZ * 4 / 250)
|
||||
#endif
|
||||
|
||||
#if ANOMALY_05000447
|
||||
static int max_rate = 57600;
|
||||
#else
|
||||
static int max_rate = 115200;
|
||||
#endif
|
||||
|
||||
static void bfin_sir_rx_dma_timeout(struct timer_list *t);
|
||||
|
||||
static void turnaround_delay(int mtt)
|
||||
{
|
||||
long ticks;
|
||||
|
||||
mtt = mtt < 10000 ? 10000 : mtt;
|
||||
ticks = 1 + mtt / (USEC_PER_SEC / HZ);
|
||||
schedule_timeout_uninterruptible(ticks);
|
||||
}
|
||||
|
||||
static void bfin_sir_init_ports(struct bfin_sir_port *sp, struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
struct resource *res;
|
||||
|
||||
for (i = 0; i < pdev->num_resources; i++) {
|
||||
res = &pdev->resource[i];
|
||||
switch (res->flags) {
|
||||
case IORESOURCE_MEM:
|
||||
sp->membase = (void __iomem *)res->start;
|
||||
break;
|
||||
case IORESOURCE_IRQ:
|
||||
sp->irq = res->start;
|
||||
break;
|
||||
case IORESOURCE_DMA:
|
||||
sp->rx_dma_channel = res->start;
|
||||
sp->tx_dma_channel = res->end;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sp->clk = get_sclk();
|
||||
#ifdef CONFIG_SIR_BFIN_DMA
|
||||
sp->tx_done = 1;
|
||||
timer_setup(&sp->rx_dma_timer, bfin_sir_rx_dma_timeout, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void bfin_sir_stop_tx(struct bfin_sir_port *port)
|
||||
{
|
||||
#ifdef CONFIG_SIR_BFIN_DMA
|
||||
disable_dma(port->tx_dma_channel);
|
||||
#endif
|
||||
|
||||
while (!(UART_GET_LSR(port) & THRE)) {
|
||||
cpu_relax();
|
||||
continue;
|
||||
}
|
||||
|
||||
UART_CLEAR_IER(port, ETBEI);
|
||||
}
|
||||
|
||||
static void bfin_sir_enable_tx(struct bfin_sir_port *port)
|
||||
{
|
||||
UART_SET_IER(port, ETBEI);
|
||||
}
|
||||
|
||||
static void bfin_sir_stop_rx(struct bfin_sir_port *port)
|
||||
{
|
||||
UART_CLEAR_IER(port, ERBFI);
|
||||
}
|
||||
|
||||
static void bfin_sir_enable_rx(struct bfin_sir_port *port)
|
||||
{
|
||||
UART_SET_IER(port, ERBFI);
|
||||
}
|
||||
|
||||
static int bfin_sir_set_speed(struct bfin_sir_port *port, int speed)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
unsigned int quot;
|
||||
unsigned short val, lsr, lcr;
|
||||
static int utime;
|
||||
int count = 10;
|
||||
|
||||
lcr = WLS(8);
|
||||
|
||||
switch (speed) {
|
||||
case 9600:
|
||||
case 19200:
|
||||
case 38400:
|
||||
case 57600:
|
||||
case 115200:
|
||||
|
||||
/*
|
||||
* IRDA is not affected by anomaly 05000230, so there is no
|
||||
* need to tweak the divisor like he UART driver (which will
|
||||
* slightly speed up the baud rate on us).
|
||||
*/
|
||||
quot = (port->clk + (8 * speed)) / (16 * speed);
|
||||
|
||||
do {
|
||||
udelay(utime);
|
||||
lsr = UART_GET_LSR(port);
|
||||
} while (!(lsr & TEMT) && count--);
|
||||
|
||||
/* The useconds for 1 bits to transmit */
|
||||
utime = 1000000 / speed + 1;
|
||||
|
||||
/* Clear UCEN bit to reset the UART state machine
|
||||
* and control registers
|
||||
*/
|
||||
val = UART_GET_GCTL(port);
|
||||
val &= ~UCEN;
|
||||
UART_PUT_GCTL(port, val);
|
||||
|
||||
/* Set DLAB in LCR to Access THR RBR IER */
|
||||
UART_SET_DLAB(port);
|
||||
SSYNC();
|
||||
|
||||
UART_PUT_DLL(port, quot & 0xFF);
|
||||
UART_PUT_DLH(port, (quot >> 8) & 0xFF);
|
||||
SSYNC();
|
||||
|
||||
/* Clear DLAB in LCR */
|
||||
UART_CLEAR_DLAB(port);
|
||||
SSYNC();
|
||||
|
||||
UART_PUT_LCR(port, lcr);
|
||||
|
||||
val = UART_GET_GCTL(port);
|
||||
val |= UCEN;
|
||||
UART_PUT_GCTL(port, val);
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "bfin_sir: Invalid speed %d\n", speed);
|
||||
break;
|
||||
}
|
||||
|
||||
val = UART_GET_GCTL(port);
|
||||
/* If not add the 'RPOLC', we can't catch the receive interrupt.
|
||||
* It's related with the HW layout and the IR transiver.
|
||||
*/
|
||||
val |= UMOD_IRDA | RPOLC;
|
||||
UART_PUT_GCTL(port, val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bfin_sir_is_receiving(struct net_device *dev)
|
||||
{
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
struct bfin_sir_port *port = self->sir_port;
|
||||
|
||||
if (!(UART_GET_IER(port) & ERBFI))
|
||||
return 0;
|
||||
return self->rx_buff.state != OUTSIDE_FRAME;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SIR_BFIN_PIO
|
||||
static void bfin_sir_tx_chars(struct net_device *dev)
|
||||
{
|
||||
unsigned int chr;
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
struct bfin_sir_port *port = self->sir_port;
|
||||
|
||||
if (self->tx_buff.len != 0) {
|
||||
chr = *(self->tx_buff.data);
|
||||
UART_PUT_CHAR(port, chr);
|
||||
self->tx_buff.data++;
|
||||
self->tx_buff.len--;
|
||||
} else {
|
||||
self->stats.tx_packets++;
|
||||
self->stats.tx_bytes += self->tx_buff.data - self->tx_buff.head;
|
||||
if (self->newspeed) {
|
||||
bfin_sir_set_speed(port, self->newspeed);
|
||||
self->speed = self->newspeed;
|
||||
self->newspeed = 0;
|
||||
}
|
||||
bfin_sir_stop_tx(port);
|
||||
bfin_sir_enable_rx(port);
|
||||
/* I'm hungry! */
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void bfin_sir_rx_chars(struct net_device *dev)
|
||||
{
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
struct bfin_sir_port *port = self->sir_port;
|
||||
unsigned char ch;
|
||||
|
||||
UART_CLEAR_LSR(port);
|
||||
ch = UART_GET_CHAR(port);
|
||||
async_unwrap_char(dev, &self->stats, &self->rx_buff, ch);
|
||||
}
|
||||
|
||||
static irqreturn_t bfin_sir_rx_int(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
struct bfin_sir_port *port = self->sir_port;
|
||||
|
||||
spin_lock(&self->lock);
|
||||
while ((UART_GET_LSR(port) & DR))
|
||||
bfin_sir_rx_chars(dev);
|
||||
spin_unlock(&self->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t bfin_sir_tx_int(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
struct bfin_sir_port *port = self->sir_port;
|
||||
|
||||
spin_lock(&self->lock);
|
||||
if (UART_GET_LSR(port) & THRE)
|
||||
bfin_sir_tx_chars(dev);
|
||||
spin_unlock(&self->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
#endif /* CONFIG_SIR_BFIN_PIO */
|
||||
|
||||
#ifdef CONFIG_SIR_BFIN_DMA
|
||||
static void bfin_sir_dma_tx_chars(struct net_device *dev)
|
||||
{
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
struct bfin_sir_port *port = self->sir_port;
|
||||
|
||||
if (!port->tx_done)
|
||||
return;
|
||||
port->tx_done = 0;
|
||||
|
||||
if (self->tx_buff.len == 0) {
|
||||
self->stats.tx_packets++;
|
||||
if (self->newspeed) {
|
||||
bfin_sir_set_speed(port, self->newspeed);
|
||||
self->speed = self->newspeed;
|
||||
self->newspeed = 0;
|
||||
}
|
||||
bfin_sir_enable_rx(port);
|
||||
port->tx_done = 1;
|
||||
netif_wake_queue(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
blackfin_dcache_flush_range((unsigned long)(self->tx_buff.data),
|
||||
(unsigned long)(self->tx_buff.data+self->tx_buff.len));
|
||||
set_dma_config(port->tx_dma_channel,
|
||||
set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP,
|
||||
INTR_ON_BUF, DIMENSION_LINEAR, DATA_SIZE_8,
|
||||
DMA_SYNC_RESTART));
|
||||
set_dma_start_addr(port->tx_dma_channel,
|
||||
(unsigned long)(self->tx_buff.data));
|
||||
set_dma_x_count(port->tx_dma_channel, self->tx_buff.len);
|
||||
set_dma_x_modify(port->tx_dma_channel, 1);
|
||||
enable_dma(port->tx_dma_channel);
|
||||
}
|
||||
|
||||
static irqreturn_t bfin_sir_dma_tx_int(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
struct bfin_sir_port *port = self->sir_port;
|
||||
|
||||
spin_lock(&self->lock);
|
||||
if (!(get_dma_curr_irqstat(port->tx_dma_channel) & DMA_RUN)) {
|
||||
clear_dma_irqstat(port->tx_dma_channel);
|
||||
bfin_sir_stop_tx(port);
|
||||
|
||||
self->stats.tx_packets++;
|
||||
self->stats.tx_bytes += self->tx_buff.len;
|
||||
self->tx_buff.len = 0;
|
||||
if (self->newspeed) {
|
||||
bfin_sir_set_speed(port, self->newspeed);
|
||||
self->speed = self->newspeed;
|
||||
self->newspeed = 0;
|
||||
}
|
||||
bfin_sir_enable_rx(port);
|
||||
/* I'm hungry! */
|
||||
netif_wake_queue(dev);
|
||||
port->tx_done = 1;
|
||||
}
|
||||
spin_unlock(&self->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void bfin_sir_dma_rx_chars(struct net_device *dev)
|
||||
{
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
struct bfin_sir_port *port = self->sir_port;
|
||||
int i;
|
||||
|
||||
UART_CLEAR_LSR(port);
|
||||
|
||||
for (i = port->rx_dma_buf.head; i < port->rx_dma_buf.tail; i++)
|
||||
async_unwrap_char(dev, &self->stats, &self->rx_buff, port->rx_dma_buf.buf[i]);
|
||||
}
|
||||
|
||||
static void bfin_sir_rx_dma_timeout(struct timer_list *t)
|
||||
{
|
||||
struct bfin_sir_port *port = from_timer(port, t, rx_dma_timer);
|
||||
struct net_device *dev = port->dev;
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
|
||||
int x_pos, pos;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&self->lock, flags);
|
||||
x_pos = DMA_SIR_RX_XCNT - get_dma_curr_xcount(port->rx_dma_channel);
|
||||
if (x_pos == DMA_SIR_RX_XCNT)
|
||||
x_pos = 0;
|
||||
|
||||
pos = port->rx_dma_nrows * DMA_SIR_RX_XCNT + x_pos;
|
||||
|
||||
if (pos > port->rx_dma_buf.tail) {
|
||||
port->rx_dma_buf.tail = pos;
|
||||
bfin_sir_dma_rx_chars(dev);
|
||||
port->rx_dma_buf.head = port->rx_dma_buf.tail;
|
||||
}
|
||||
spin_unlock_irqrestore(&self->lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t bfin_sir_dma_rx_int(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
struct bfin_sir_port *port = self->sir_port;
|
||||
unsigned short irqstat;
|
||||
|
||||
spin_lock(&self->lock);
|
||||
|
||||
port->rx_dma_nrows++;
|
||||
port->rx_dma_buf.tail = DMA_SIR_RX_XCNT * port->rx_dma_nrows;
|
||||
bfin_sir_dma_rx_chars(dev);
|
||||
if (port->rx_dma_nrows >= DMA_SIR_RX_YCNT) {
|
||||
port->rx_dma_nrows = 0;
|
||||
port->rx_dma_buf.tail = 0;
|
||||
}
|
||||
port->rx_dma_buf.head = port->rx_dma_buf.tail;
|
||||
|
||||
irqstat = get_dma_curr_irqstat(port->rx_dma_channel);
|
||||
clear_dma_irqstat(port->rx_dma_channel);
|
||||
spin_unlock(&self->lock);
|
||||
|
||||
mod_timer(&port->rx_dma_timer, jiffies + DMA_SIR_RX_FLUSH_JIFS);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
#endif /* CONFIG_SIR_BFIN_DMA */
|
||||
|
||||
static int bfin_sir_startup(struct bfin_sir_port *port, struct net_device *dev)
|
||||
{
|
||||
#ifdef CONFIG_SIR_BFIN_DMA
|
||||
dma_addr_t dma_handle;
|
||||
#endif /* CONFIG_SIR_BFIN_DMA */
|
||||
|
||||
if (request_dma(port->rx_dma_channel, "BFIN_UART_RX") < 0) {
|
||||
dev_warn(&dev->dev, "Unable to attach SIR RX DMA channel\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (request_dma(port->tx_dma_channel, "BFIN_UART_TX") < 0) {
|
||||
dev_warn(&dev->dev, "Unable to attach SIR TX DMA channel\n");
|
||||
free_dma(port->rx_dma_channel);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SIR_BFIN_DMA
|
||||
|
||||
set_dma_callback(port->rx_dma_channel, bfin_sir_dma_rx_int, dev);
|
||||
set_dma_callback(port->tx_dma_channel, bfin_sir_dma_tx_int, dev);
|
||||
|
||||
port->rx_dma_buf.buf = dma_alloc_coherent(NULL, PAGE_SIZE,
|
||||
&dma_handle, GFP_DMA);
|
||||
port->rx_dma_buf.head = 0;
|
||||
port->rx_dma_buf.tail = 0;
|
||||
port->rx_dma_nrows = 0;
|
||||
|
||||
set_dma_config(port->rx_dma_channel,
|
||||
set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO,
|
||||
INTR_ON_ROW, DIMENSION_2D,
|
||||
DATA_SIZE_8, DMA_SYNC_RESTART));
|
||||
set_dma_x_count(port->rx_dma_channel, DMA_SIR_RX_XCNT);
|
||||
set_dma_x_modify(port->rx_dma_channel, 1);
|
||||
set_dma_y_count(port->rx_dma_channel, DMA_SIR_RX_YCNT);
|
||||
set_dma_y_modify(port->rx_dma_channel, 1);
|
||||
set_dma_start_addr(port->rx_dma_channel, (unsigned long)port->rx_dma_buf.buf);
|
||||
enable_dma(port->rx_dma_channel);
|
||||
|
||||
|
||||
#else
|
||||
|
||||
if (request_irq(port->irq, bfin_sir_rx_int, 0, "BFIN_SIR_RX", dev)) {
|
||||
dev_warn(&dev->dev, "Unable to attach SIR RX interrupt\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (request_irq(port->irq+1, bfin_sir_tx_int, 0, "BFIN_SIR_TX", dev)) {
|
||||
dev_warn(&dev->dev, "Unable to attach SIR TX interrupt\n");
|
||||
free_irq(port->irq, dev);
|
||||
return -EBUSY;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bfin_sir_shutdown(struct bfin_sir_port *port, struct net_device *dev)
|
||||
{
|
||||
unsigned short val;
|
||||
|
||||
bfin_sir_stop_rx(port);
|
||||
|
||||
val = UART_GET_GCTL(port);
|
||||
val &= ~(UCEN | UMOD_MASK | RPOLC);
|
||||
UART_PUT_GCTL(port, val);
|
||||
|
||||
#ifdef CONFIG_SIR_BFIN_DMA
|
||||
disable_dma(port->tx_dma_channel);
|
||||
disable_dma(port->rx_dma_channel);
|
||||
del_timer(&(port->rx_dma_timer));
|
||||
dma_free_coherent(NULL, PAGE_SIZE, port->rx_dma_buf.buf, 0);
|
||||
#else
|
||||
free_irq(port->irq+1, dev);
|
||||
free_irq(port->irq, dev);
|
||||
#endif
|
||||
free_dma(port->tx_dma_channel);
|
||||
free_dma(port->rx_dma_channel);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int bfin_sir_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct bfin_sir_port *sir_port;
|
||||
struct net_device *dev;
|
||||
struct bfin_sir_self *self;
|
||||
|
||||
sir_port = platform_get_drvdata(pdev);
|
||||
if (!sir_port)
|
||||
return 0;
|
||||
|
||||
dev = sir_port->dev;
|
||||
self = netdev_priv(dev);
|
||||
if (self->open) {
|
||||
flush_work(&self->work);
|
||||
bfin_sir_shutdown(self->sir_port, dev);
|
||||
netif_device_detach(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int bfin_sir_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct bfin_sir_port *sir_port;
|
||||
struct net_device *dev;
|
||||
struct bfin_sir_self *self;
|
||||
struct bfin_sir_port *port;
|
||||
|
||||
sir_port = platform_get_drvdata(pdev);
|
||||
if (!sir_port)
|
||||
return 0;
|
||||
|
||||
dev = sir_port->dev;
|
||||
self = netdev_priv(dev);
|
||||
port = self->sir_port;
|
||||
if (self->open) {
|
||||
if (self->newspeed) {
|
||||
self->speed = self->newspeed;
|
||||
self->newspeed = 0;
|
||||
}
|
||||
bfin_sir_startup(port, dev);
|
||||
bfin_sir_set_speed(port, 9600);
|
||||
bfin_sir_enable_rx(port);
|
||||
netif_device_attach(dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define bfin_sir_suspend NULL
|
||||
#define bfin_sir_resume NULL
|
||||
#endif
|
||||
|
||||
static void bfin_sir_send_work(struct work_struct *work)
|
||||
{
|
||||
struct bfin_sir_self *self = container_of(work, struct bfin_sir_self, work);
|
||||
struct net_device *dev = self->sir_port->dev;
|
||||
struct bfin_sir_port *port = self->sir_port;
|
||||
unsigned short val;
|
||||
int tx_cnt = 10;
|
||||
|
||||
while (bfin_sir_is_receiving(dev) && --tx_cnt)
|
||||
turnaround_delay(self->mtt);
|
||||
|
||||
bfin_sir_stop_rx(port);
|
||||
|
||||
/* To avoid losting RX interrupt, we reset IR function before
|
||||
* sending data. We also can set the speed, which will
|
||||
* reset all the UART.
|
||||
*/
|
||||
val = UART_GET_GCTL(port);
|
||||
val &= ~(UMOD_MASK | RPOLC);
|
||||
UART_PUT_GCTL(port, val);
|
||||
SSYNC();
|
||||
val |= UMOD_IRDA | RPOLC;
|
||||
UART_PUT_GCTL(port, val);
|
||||
SSYNC();
|
||||
/* bfin_sir_set_speed(port, self->speed); */
|
||||
|
||||
#ifdef CONFIG_SIR_BFIN_DMA
|
||||
bfin_sir_dma_tx_chars(dev);
|
||||
#endif
|
||||
bfin_sir_enable_tx(port);
|
||||
netif_trans_update(dev);
|
||||
}
|
||||
|
||||
static int bfin_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
int speed = irda_get_next_speed(skb);
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
self->mtt = irda_get_mtt(skb);
|
||||
|
||||
if (speed != self->speed && speed != -1)
|
||||
self->newspeed = speed;
|
||||
|
||||
self->tx_buff.data = self->tx_buff.head;
|
||||
if (skb->len == 0)
|
||||
self->tx_buff.len = 0;
|
||||
else
|
||||
self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, self->tx_buff.truesize);
|
||||
|
||||
schedule_work(&self->work);
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bfin_sir_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
|
||||
{
|
||||
struct if_irda_req *rq = (struct if_irda_req *)ifreq;
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
struct bfin_sir_port *port = self->sir_port;
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SIOCSBANDWIDTH:
|
||||
if (capable(CAP_NET_ADMIN)) {
|
||||
if (self->open) {
|
||||
ret = bfin_sir_set_speed(port, rq->ifr_baudrate);
|
||||
bfin_sir_enable_rx(port);
|
||||
} else {
|
||||
dev_warn(&dev->dev, "SIOCSBANDWIDTH: !netif_running\n");
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SIOCSMEDIABUSY:
|
||||
ret = -EPERM;
|
||||
if (capable(CAP_NET_ADMIN)) {
|
||||
irda_device_set_media_busy(dev, TRUE);
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIOCGRECEIVING:
|
||||
rq->ifr_receiving = bfin_sir_is_receiving(dev);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct net_device_stats *bfin_sir_stats(struct net_device *dev)
|
||||
{
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
|
||||
return &self->stats;
|
||||
}
|
||||
|
||||
static int bfin_sir_open(struct net_device *dev)
|
||||
{
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
struct bfin_sir_port *port = self->sir_port;
|
||||
int err;
|
||||
|
||||
self->newspeed = 0;
|
||||
self->speed = 9600;
|
||||
|
||||
spin_lock_init(&self->lock);
|
||||
|
||||
err = bfin_sir_startup(port, dev);
|
||||
if (err)
|
||||
goto err_startup;
|
||||
|
||||
bfin_sir_set_speed(port, 9600);
|
||||
|
||||
self->irlap = irlap_open(dev, &self->qos, DRIVER_NAME);
|
||||
if (!self->irlap) {
|
||||
err = -ENOMEM;
|
||||
goto err_irlap;
|
||||
}
|
||||
|
||||
INIT_WORK(&self->work, bfin_sir_send_work);
|
||||
|
||||
/*
|
||||
* Now enable the interrupt then start the queue
|
||||
*/
|
||||
self->open = 1;
|
||||
bfin_sir_enable_rx(port);
|
||||
|
||||
netif_start_queue(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_irlap:
|
||||
self->open = 0;
|
||||
bfin_sir_shutdown(port, dev);
|
||||
err_startup:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bfin_sir_stop(struct net_device *dev)
|
||||
{
|
||||
struct bfin_sir_self *self = netdev_priv(dev);
|
||||
|
||||
flush_work(&self->work);
|
||||
bfin_sir_shutdown(self->sir_port, dev);
|
||||
|
||||
if (self->rxskb) {
|
||||
dev_kfree_skb(self->rxskb);
|
||||
self->rxskb = NULL;
|
||||
}
|
||||
|
||||
/* Stop IrLAP */
|
||||
if (self->irlap) {
|
||||
irlap_close(self->irlap);
|
||||
self->irlap = NULL;
|
||||
}
|
||||
|
||||
netif_stop_queue(dev);
|
||||
self->open = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bfin_sir_init_iobuf(iobuff_t *io, int size)
|
||||
{
|
||||
io->head = kmalloc(size, GFP_KERNEL);
|
||||
if (!io->head)
|
||||
return -ENOMEM;
|
||||
io->truesize = size;
|
||||
io->in_frame = FALSE;
|
||||
io->state = OUTSIDE_FRAME;
|
||||
io->data = io->head;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct net_device_ops bfin_sir_ndo = {
|
||||
.ndo_open = bfin_sir_open,
|
||||
.ndo_stop = bfin_sir_stop,
|
||||
.ndo_start_xmit = bfin_sir_hard_xmit,
|
||||
.ndo_do_ioctl = bfin_sir_ioctl,
|
||||
.ndo_get_stats = bfin_sir_stats,
|
||||
};
|
||||
|
||||
static int bfin_sir_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct bfin_sir_self *self;
|
||||
unsigned int baudrate_mask;
|
||||
struct bfin_sir_port *sir_port;
|
||||
int err;
|
||||
|
||||
if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(per) && \
|
||||
per[pdev->id][3] == pdev->id) {
|
||||
err = peripheral_request_list(per[pdev->id], DRIVER_NAME);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "Invalid pdev id, please check board file\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = -ENOMEM;
|
||||
sir_port = kmalloc(sizeof(*sir_port), GFP_KERNEL);
|
||||
if (!sir_port)
|
||||
goto err_mem_0;
|
||||
|
||||
bfin_sir_init_ports(sir_port, pdev);
|
||||
|
||||
dev = alloc_irdadev(sizeof(*self));
|
||||
if (!dev)
|
||||
goto err_mem_1;
|
||||
|
||||
self = netdev_priv(dev);
|
||||
self->dev = &pdev->dev;
|
||||
self->sir_port = sir_port;
|
||||
sir_port->dev = dev;
|
||||
|
||||
err = bfin_sir_init_iobuf(&self->rx_buff, IRDA_SKB_MAX_MTU);
|
||||
if (err)
|
||||
goto err_mem_2;
|
||||
err = bfin_sir_init_iobuf(&self->tx_buff, IRDA_SIR_MAX_FRAME);
|
||||
if (err)
|
||||
goto err_mem_3;
|
||||
|
||||
dev->netdev_ops = &bfin_sir_ndo;
|
||||
dev->irq = sir_port->irq;
|
||||
|
||||
irda_init_max_qos_capabilies(&self->qos);
|
||||
|
||||
baudrate_mask = IR_9600;
|
||||
|
||||
switch (max_rate) {
|
||||
case 115200:
|
||||
baudrate_mask |= IR_115200;
|
||||
case 57600:
|
||||
baudrate_mask |= IR_57600;
|
||||
case 38400:
|
||||
baudrate_mask |= IR_38400;
|
||||
case 19200:
|
||||
baudrate_mask |= IR_19200;
|
||||
case 9600:
|
||||
break;
|
||||
default:
|
||||
dev_warn(&pdev->dev, "Invalid maximum baud rate, using 9600\n");
|
||||
}
|
||||
|
||||
self->qos.baud_rate.bits &= baudrate_mask;
|
||||
|
||||
self->qos.min_turn_time.bits = 1; /* 10 ms or more */
|
||||
|
||||
irda_qos_bits_to_value(&self->qos);
|
||||
|
||||
err = register_netdev(dev);
|
||||
|
||||
if (err) {
|
||||
kfree(self->tx_buff.head);
|
||||
err_mem_3:
|
||||
kfree(self->rx_buff.head);
|
||||
err_mem_2:
|
||||
free_netdev(dev);
|
||||
err_mem_1:
|
||||
kfree(sir_port);
|
||||
err_mem_0:
|
||||
peripheral_free_list(per[pdev->id]);
|
||||
} else
|
||||
platform_set_drvdata(pdev, sir_port);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bfin_sir_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bfin_sir_port *sir_port;
|
||||
struct net_device *dev = NULL;
|
||||
struct bfin_sir_self *self;
|
||||
|
||||
sir_port = platform_get_drvdata(pdev);
|
||||
if (!sir_port)
|
||||
return 0;
|
||||
dev = sir_port->dev;
|
||||
self = netdev_priv(dev);
|
||||
unregister_netdev(dev);
|
||||
kfree(self->tx_buff.head);
|
||||
kfree(self->rx_buff.head);
|
||||
free_netdev(dev);
|
||||
kfree(sir_port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver bfin_ir_driver = {
|
||||
.probe = bfin_sir_probe,
|
||||
.remove = bfin_sir_remove,
|
||||
.suspend = bfin_sir_suspend,
|
||||
.resume = bfin_sir_resume,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(bfin_ir_driver);
|
||||
|
||||
module_param(max_rate, int, 0);
|
||||
MODULE_PARM_DESC(max_rate, "Maximum baud rate (115200, 57600, 38400, 19200, 9600)");
|
||||
|
||||
MODULE_AUTHOR("Graf Yang <graf.yang@analog.com>");
|
||||
MODULE_DESCRIPTION("Blackfin IrDA driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
* Blackfin Infra-red Driver
|
||||
*
|
||||
* Copyright 2006-2009 Analog Devices Inc.
|
||||
*
|
||||
* Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/serial.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/wrapper.h>
|
||||
#include <net/irda/irda_device.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/portmux.h>
|
||||
#undef DRIVER_NAME
|
||||
|
||||
#ifdef CONFIG_SIR_BFIN_DMA
|
||||
struct dma_rx_buf {
|
||||
char *buf;
|
||||
int head;
|
||||
int tail;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct bfin_sir_port {
|
||||
unsigned char __iomem *membase;
|
||||
unsigned int irq;
|
||||
unsigned int lsr;
|
||||
unsigned long clk;
|
||||
struct net_device *dev;
|
||||
#ifdef CONFIG_SIR_BFIN_DMA
|
||||
int tx_done;
|
||||
struct dma_rx_buf rx_dma_buf;
|
||||
struct timer_list rx_dma_timer;
|
||||
int rx_dma_nrows;
|
||||
#endif
|
||||
unsigned int tx_dma_channel;
|
||||
unsigned int rx_dma_channel;
|
||||
};
|
||||
|
||||
struct bfin_sir_port_res {
|
||||
unsigned long base_addr;
|
||||
int irq;
|
||||
unsigned int rx_dma_channel;
|
||||
unsigned int tx_dma_channel;
|
||||
};
|
||||
|
||||
struct bfin_sir_self {
|
||||
struct bfin_sir_port *sir_port;
|
||||
spinlock_t lock;
|
||||
unsigned int open;
|
||||
int speed;
|
||||
int newspeed;
|
||||
|
||||
struct sk_buff *txskb;
|
||||
struct sk_buff *rxskb;
|
||||
struct net_device_stats stats;
|
||||
struct device *dev;
|
||||
struct irlap_cb *irlap;
|
||||
struct qos_info qos;
|
||||
|
||||
iobuff_t tx_buff;
|
||||
iobuff_t rx_buff;
|
||||
|
||||
struct work_struct work;
|
||||
int mtt;
|
||||
};
|
||||
|
||||
#define DRIVER_NAME "bfin_sir"
|
||||
|
||||
#include <asm/bfin_serial.h>
|
||||
|
||||
static const unsigned short per[][4] = {
|
||||
/* rx pin tx pin NULL uart_number */
|
||||
{P_UART0_RX, P_UART0_TX, 0, 0},
|
||||
{P_UART1_RX, P_UART1_TX, 0, 1},
|
||||
{P_UART2_RX, P_UART2_TX, 0, 2},
|
||||
{P_UART3_RX, P_UART3_TX, 0, 3},
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,362 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: toshoboe.h
|
||||
* Version: 2.16
|
||||
* Description: Driver for the Toshiba OBOE (or type-O or 701)
|
||||
* FIR Chipset, also supports the DONAUOBOE (type-DO
|
||||
* or d01) FIR chipset which as far as I know is
|
||||
* register compatible.
|
||||
* Status: Experimental.
|
||||
* Author: James McKenzie <james@fishsoup.dhs.org>
|
||||
* Created at: Sat May 8 12:35:27 1999
|
||||
* Modified: 2.16 Martin Lucina <mato@kotelna.sk>
|
||||
* Modified: 2.16 Sat Jun 22 18:54:29 2002 (sync headers)
|
||||
* Modified: 2.17 Christian Gennerat <christian.gennerat@polytechnique.org>
|
||||
* Modified: 2.17 jeu sep 12 08:50:20 2002 (add lock to be used by spinlocks)
|
||||
*
|
||||
* Copyright (c) 1999 James McKenzie, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither James McKenzie nor Cambridge University admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
* Applicable Models : Libretto 100/110CT and many more.
|
||||
* Toshiba refers to this chip as the type-O IR port,
|
||||
* or the type-DO IR port.
|
||||
*
|
||||
* IrDA chip set list from Toshiba Computer Engineering Corp.
|
||||
* model method maker controller Version
|
||||
* Portege 320CT FIR,SIR Toshiba Oboe(Triangle)
|
||||
* Portege 3010CT FIR,SIR Toshiba Oboe(Sydney)
|
||||
* Portege 3015CT FIR,SIR Toshiba Oboe(Sydney)
|
||||
* Portege 3020CT FIR,SIR Toshiba Oboe(Sydney)
|
||||
* Portege 7020CT FIR,SIR ? ?
|
||||
*
|
||||
* Satell. 4090XCDT FIR,SIR ? ?
|
||||
*
|
||||
* Libretto 100CT FIR,SIR Toshiba Oboe
|
||||
* Libretto 1000CT FIR,SIR Toshiba Oboe
|
||||
*
|
||||
* TECRA750DVD FIR,SIR Toshiba Oboe(Triangle) REV ID=14h
|
||||
* TECRA780 FIR,SIR Toshiba Oboe(Sandlot) REV ID=32h,33h
|
||||
* TECRA750CDT FIR,SIR Toshiba Oboe(Triangle) REV ID=13h,14h
|
||||
* TECRA8000 FIR,SIR Toshiba Oboe(ISKUR) REV ID=23h
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
/* The documentation for this chip is allegedly released */
|
||||
/* However I have not seen it, not have I managed to contact */
|
||||
/* anyone who has. HOWEVER the chip bears a striking resemblance */
|
||||
/* to the IrDA controller in the Toshiba RISC TMPR3922 chip */
|
||||
/* the documentation for this is freely available at */
|
||||
/* http://www.madingley.org/james/resources/toshoboe/TMPR3922.pdf */
|
||||
/* The mapping between the registers in that document and the */
|
||||
/* Registers in the 701 oboe chip are as follows */
|
||||
|
||||
|
||||
/* 3922 reg 701 regs, by bit numbers */
|
||||
/* 7- 0 15- 8 24-16 31-25 */
|
||||
/* $28 0x0 0x1 */
|
||||
/* $2c SEE NOTE 1 */
|
||||
/* $30 0x6 0x7 */
|
||||
/* $34 0x8 0x9 SEE NOTE 2 */
|
||||
/* $38 0x10 0x11 */
|
||||
/* $3C 0xe SEE NOTE 3 */
|
||||
/* $40 0x12 0x13 */
|
||||
/* $44 0x14 0x15 */
|
||||
/* $48 0x16 0x17 */
|
||||
/* $4c 0x18 0x19 */
|
||||
/* $50 0x1a 0x1b */
|
||||
|
||||
/* FIXME: could be 0x1b 0x1a here */
|
||||
|
||||
/* $54 0x1d 0x1c */
|
||||
/* $5C 0xf SEE NOTE 4 */
|
||||
/* $130 SEE NOTE 5 */
|
||||
/* $134 SEE NOTE 6 */
|
||||
/* */
|
||||
/* NOTES: */
|
||||
/* 1. The pointer to ring is packed in most unceremoniusly */
|
||||
/* 701 Register Address bits (A9-A0 must be zero) */
|
||||
/* 0x4: A17 A16 A15 A14 A13 A12 A11 A10 */
|
||||
/* 0x5: A25 A24 A23 A22 A21 A20 A19 A18 */
|
||||
/* 0x2: 0 0 A31 A30 A29 A28 A27 A26 */
|
||||
/* */
|
||||
/* 2. The M$ drivers do a write 0x1 to 0x9, however the 3922 */
|
||||
/* documentation would suggest that a write of 0x1 to 0x8 */
|
||||
/* would be more appropriate. */
|
||||
/* */
|
||||
/* 3. This assignment is tenuous at best, register 0xe seems to */
|
||||
/* have bits arranged 0 0 0 R/W R/W R/W R/W R/W */
|
||||
/* if either of the lower two bits are set the chip seems to */
|
||||
/* switch off */
|
||||
/* */
|
||||
/* 4. Bits 7-4 seem to be different 4 seems just to be generic */
|
||||
/* receiver busy flag */
|
||||
/* */
|
||||
/* 5. and 6. The IER and ISR have a different bit assignment */
|
||||
/* The lower three bits of both read back as ones */
|
||||
/* ISR is register 0xc, IER is register 0xd */
|
||||
/* 7 6 5 4 3 2 1 0 */
|
||||
/* 0xc: TxDone RxDone TxUndr RxOver SipRcv 1 1 1 */
|
||||
/* 0xd: TxDone RxDone TxUndr RxOver SipRcv 1 1 1 */
|
||||
/* TxDone xmitt done (generated only if generate interrupt bit */
|
||||
/* is set in the ring) */
|
||||
/* RxDone recv completed (or other recv condition if you set it */
|
||||
/* up */
|
||||
/* TxUnder underflow in Transmit FIFO */
|
||||
/* RxOver overflow in Recv FIFO */
|
||||
/* SipRcv received serial gap (or other condition you set) */
|
||||
/* Interrupts are enabled by writing a one to the IER register */
|
||||
/* Interrupts are cleared by writing a one to the ISR register */
|
||||
/* */
|
||||
/* 6. The remaining registers: 0x6 and 0x3 appear to be */
|
||||
/* reserved parts of 16 or 32 bit registersthe remainder */
|
||||
/* 0xa 0xb 0x1e 0x1f could possibly be (by their behaviour) */
|
||||
/* the Unicast Filter register at $58. */
|
||||
/* */
|
||||
/* 7. While the core obviously expects 32 bit accesses all the */
|
||||
/* M$ drivers do 8 bit accesses, infact the Miniport ones */
|
||||
/* write and read back the byte serveral times (why?) */
|
||||
|
||||
|
||||
#ifndef TOSHOBOE_H
|
||||
#define TOSHOBOE_H
|
||||
|
||||
/* Registers */
|
||||
|
||||
#define OBOE_IO_EXTENT 0x1f
|
||||
|
||||
/*Receive and transmit slot pointers */
|
||||
#define OBOE_REG(i) (i+(self->base))
|
||||
#define OBOE_RXSLOT OBOE_REG(0x0)
|
||||
#define OBOE_TXSLOT OBOE_REG(0x1)
|
||||
#define OBOE_SLOT_MASK 0x3f
|
||||
|
||||
#define OBOE_TXRING_OFFSET 0x200
|
||||
#define OBOE_TXRING_OFFSET_IN_SLOTS 0x40
|
||||
|
||||
/*pointer to the ring */
|
||||
#define OBOE_RING_BASE0 OBOE_REG(0x4)
|
||||
#define OBOE_RING_BASE1 OBOE_REG(0x5)
|
||||
#define OBOE_RING_BASE2 OBOE_REG(0x2)
|
||||
#define OBOE_RING_BASE3 OBOE_REG(0x3)
|
||||
|
||||
/*Number of slots in the ring */
|
||||
#define OBOE_RING_SIZE OBOE_REG(0x7)
|
||||
#define OBOE_RING_SIZE_RX4 0x00
|
||||
#define OBOE_RING_SIZE_RX8 0x01
|
||||
#define OBOE_RING_SIZE_RX16 0x03
|
||||
#define OBOE_RING_SIZE_RX32 0x07
|
||||
#define OBOE_RING_SIZE_RX64 0x0f
|
||||
#define OBOE_RING_SIZE_TX4 0x00
|
||||
#define OBOE_RING_SIZE_TX8 0x10
|
||||
#define OBOE_RING_SIZE_TX16 0x30
|
||||
#define OBOE_RING_SIZE_TX32 0x70
|
||||
#define OBOE_RING_SIZE_TX64 0xf0
|
||||
|
||||
#define OBOE_RING_MAX_SIZE 64
|
||||
|
||||
/*Causes the gubbins to re-examine the ring */
|
||||
#define OBOE_PROMPT OBOE_REG(0x9)
|
||||
#define OBOE_PROMPT_BIT 0x1
|
||||
|
||||
/* Interrupt Status Register */
|
||||
#define OBOE_ISR OBOE_REG(0xc)
|
||||
/* Interrupt Enable Register */
|
||||
#define OBOE_IER OBOE_REG(0xd)
|
||||
/* Interrupt bits for IER and ISR */
|
||||
#define OBOE_INT_TXDONE 0x80
|
||||
#define OBOE_INT_RXDONE 0x40
|
||||
#define OBOE_INT_TXUNDER 0x20
|
||||
#define OBOE_INT_RXOVER 0x10
|
||||
#define OBOE_INT_SIP 0x08
|
||||
#define OBOE_INT_MASK 0xf8
|
||||
|
||||
/*Reset Register */
|
||||
#define OBOE_CONFIG1 OBOE_REG(0xe)
|
||||
#define OBOE_CONFIG1_RST 0x01
|
||||
#define OBOE_CONFIG1_DISABLE 0x02
|
||||
#define OBOE_CONFIG1_4 0x08
|
||||
#define OBOE_CONFIG1_8 0x08
|
||||
|
||||
#define OBOE_CONFIG1_ON 0x8
|
||||
#define OBOE_CONFIG1_RESET 0xf
|
||||
#define OBOE_CONFIG1_OFF 0xe
|
||||
|
||||
#define OBOE_STATUS OBOE_REG(0xf)
|
||||
#define OBOE_STATUS_RXBUSY 0x10
|
||||
#define OBOE_STATUS_FIRRX 0x04
|
||||
#define OBOE_STATUS_MIRRX 0x02
|
||||
#define OBOE_STATUS_SIRRX 0x01
|
||||
|
||||
|
||||
/*Speed control registers */
|
||||
#define OBOE_CONFIG0L OBOE_REG(0x10)
|
||||
#define OBOE_CONFIG0H OBOE_REG(0x11)
|
||||
|
||||
#define OBOE_CONFIG0H_TXONLOOP 0x80 /*Transmit when looping (dangerous) */
|
||||
#define OBOE_CONFIG0H_LOOP 0x40 /*Loopback Tx->Rx */
|
||||
#define OBOE_CONFIG0H_ENTX 0x10 /*Enable Tx */
|
||||
#define OBOE_CONFIG0H_ENRX 0x08 /*Enable Rx */
|
||||
#define OBOE_CONFIG0H_ENDMAC 0x04 /*Enable/reset* the DMA controller */
|
||||
#define OBOE_CONFIG0H_RCVANY 0x02 /*DMA mode 1=bytes, 0=dwords */
|
||||
|
||||
#define OBOE_CONFIG0L_CRC16 0x80 /*CRC 1=16 bit 0=32 bit */
|
||||
#define OBOE_CONFIG0L_ENFIR 0x40 /*Enable FIR */
|
||||
#define OBOE_CONFIG0L_ENMIR 0x20 /*Enable MIR */
|
||||
#define OBOE_CONFIG0L_ENSIR 0x10 /*Enable SIR */
|
||||
#define OBOE_CONFIG0L_ENSIRF 0x08 /*Enable SIR framer */
|
||||
#define OBOE_CONFIG0L_SIRTEST 0x04 /*Enable SIR framer in MIR and FIR */
|
||||
#define OBOE_CONFIG0L_INVERTTX 0x02 /*Invert Tx Line */
|
||||
#define OBOE_CONFIG0L_INVERTRX 0x01 /*Invert Rx Line */
|
||||
|
||||
#define OBOE_BOF OBOE_REG(0x12)
|
||||
#define OBOE_EOF OBOE_REG(0x13)
|
||||
|
||||
#define OBOE_ENABLEL OBOE_REG(0x14)
|
||||
#define OBOE_ENABLEH OBOE_REG(0x15)
|
||||
|
||||
#define OBOE_ENABLEH_PHYANDCLOCK 0x80 /*Toggle low to copy config in */
|
||||
#define OBOE_ENABLEH_CONFIGERR 0x40
|
||||
#define OBOE_ENABLEH_FIRON 0x20
|
||||
#define OBOE_ENABLEH_MIRON 0x10
|
||||
#define OBOE_ENABLEH_SIRON 0x08
|
||||
#define OBOE_ENABLEH_ENTX 0x04
|
||||
#define OBOE_ENABLEH_ENRX 0x02
|
||||
#define OBOE_ENABLEH_CRC16 0x01
|
||||
|
||||
#define OBOE_ENABLEL_BROADCAST 0x01
|
||||
|
||||
#define OBOE_CURR_PCONFIGL OBOE_REG(0x16) /*Current config */
|
||||
#define OBOE_CURR_PCONFIGH OBOE_REG(0x17)
|
||||
|
||||
#define OBOE_NEW_PCONFIGL OBOE_REG(0x18)
|
||||
#define OBOE_NEW_PCONFIGH OBOE_REG(0x19)
|
||||
|
||||
#define OBOE_PCONFIGH_BAUDMASK 0xfc
|
||||
#define OBOE_PCONFIGH_WIDTHMASK 0x04
|
||||
#define OBOE_PCONFIGL_WIDTHMASK 0xe0
|
||||
#define OBOE_PCONFIGL_PREAMBLEMASK 0x1f
|
||||
|
||||
#define OBOE_PCONFIG_BAUDMASK 0xfc00
|
||||
#define OBOE_PCONFIG_BAUDSHIFT 10
|
||||
#define OBOE_PCONFIG_WIDTHMASK 0x04e0
|
||||
#define OBOE_PCONFIG_WIDTHSHIFT 5
|
||||
#define OBOE_PCONFIG_PREAMBLEMASK 0x001f
|
||||
#define OBOE_PCONFIG_PREAMBLESHIFT 0
|
||||
|
||||
#define OBOE_MAXLENL OBOE_REG(0x1a)
|
||||
#define OBOE_MAXLENH OBOE_REG(0x1b)
|
||||
|
||||
#define OBOE_RXCOUNTH OBOE_REG(0x1c) /*Reset on recipt */
|
||||
#define OBOE_RXCOUNTL OBOE_REG(0x1d) /*of whole packet */
|
||||
|
||||
/* The PCI ID of the OBOE chip */
|
||||
#ifndef PCI_DEVICE_ID_FIR701
|
||||
#define PCI_DEVICE_ID_FIR701 0x0701
|
||||
#endif
|
||||
|
||||
#ifndef PCI_DEVICE_ID_FIRD01
|
||||
#define PCI_DEVICE_ID_FIRD01 0x0d01
|
||||
#endif
|
||||
|
||||
struct OboeSlot
|
||||
{
|
||||
__u16 len; /*Tweleve bits of packet length */
|
||||
__u8 unused;
|
||||
__u8 control; /*Slot control/status see below */
|
||||
__u32 address; /*Slot buffer address */
|
||||
}
|
||||
__packed;
|
||||
|
||||
#define OBOE_NTASKS OBOE_TXRING_OFFSET_IN_SLOTS
|
||||
|
||||
struct OboeRing
|
||||
{
|
||||
struct OboeSlot rx[OBOE_NTASKS];
|
||||
struct OboeSlot tx[OBOE_NTASKS];
|
||||
};
|
||||
|
||||
#define OBOE_RING_LEN (sizeof(struct OboeRing))
|
||||
|
||||
|
||||
#define OBOE_CTL_TX_HW_OWNS 0x80 /*W/R This slot owned by the hardware */
|
||||
#define OBOE_CTL_TX_DISTX_CRC 0x40 /*W Disable CRC generation for [FM]IR */
|
||||
#define OBOE_CTL_TX_BAD_CRC 0x20 /*W Generate bad CRC */
|
||||
#define OBOE_CTL_TX_SIP 0x10 /*W Generate an SIP after xmittion */
|
||||
#define OBOE_CTL_TX_MKUNDER 0x08 /*W Generate an underrun error */
|
||||
#define OBOE_CTL_TX_RTCENTX 0x04 /*W Enable receiver and generate TXdone */
|
||||
/* After this slot is processed */
|
||||
#define OBOE_CTL_TX_UNDER 0x01 /*R Set by hardware to indicate underrun */
|
||||
|
||||
|
||||
#define OBOE_CTL_RX_HW_OWNS 0x80 /*W/R This slot owned by hardware */
|
||||
#define OBOE_CTL_RX_PHYERR 0x40 /*R Decoder error on receiption */
|
||||
#define OBOE_CTL_RX_CRCERR 0x20 /*R CRC error only set for [FM]IR */
|
||||
#define OBOE_CTL_RX_LENGTH 0x10 /*R Packet > max Rx length */
|
||||
#define OBOE_CTL_RX_OVER 0x08 /*R set to indicate an overflow */
|
||||
#define OBOE_CTL_RX_SIRBAD 0x04 /*R SIR had BOF in packet or ABORT sequence */
|
||||
#define OBOE_CTL_RX_RXEOF 0x02 /*R Finished receiving on this slot */
|
||||
|
||||
|
||||
struct toshoboe_cb
|
||||
{
|
||||
struct net_device *netdev; /* Yes! we are some kind of netdevice */
|
||||
struct tty_driver ttydev;
|
||||
|
||||
struct irlap_cb *irlap; /* The link layer we are binded to */
|
||||
|
||||
chipio_t io; /* IrDA controller information */
|
||||
struct qos_info qos; /* QoS capabilities for this device */
|
||||
|
||||
__u32 flags; /* Interface flags */
|
||||
|
||||
struct pci_dev *pdev; /*PCI device */
|
||||
int base; /*IO base */
|
||||
|
||||
|
||||
int txpending; /*how many tx's are pending */
|
||||
int txs, rxs; /*Which slots are we at */
|
||||
|
||||
int irdad; /*Driver under control of netdev end */
|
||||
int async; /*Driver under control of async end */
|
||||
|
||||
|
||||
int stopped; /*Stopped by some or other APM stuff */
|
||||
|
||||
int filter; /*In SIR mode do we want to receive
|
||||
frames or byte ranges */
|
||||
|
||||
void *ringbuf; /*The ring buffer */
|
||||
struct OboeRing *ring; /*The ring */
|
||||
|
||||
void *tx_bufs[OBOE_RING_MAX_SIZE]; /*The buffers */
|
||||
void *rx_bufs[OBOE_RING_MAX_SIZE];
|
||||
|
||||
|
||||
int speed; /*Current setting of the speed */
|
||||
int new_speed; /*Set to request a speed change */
|
||||
|
||||
/* The spinlock protect critical parts of the driver.
|
||||
* Locking is done like this :
|
||||
* spin_lock_irqsave(&self->spinlock, flags);
|
||||
* Releasing the lock :
|
||||
* spin_unlock_irqrestore(&self->spinlock, flags);
|
||||
*/
|
||||
spinlock_t spinlock;
|
||||
/* Used for the probe and diagnostics code */
|
||||
int int_rx;
|
||||
int int_tx;
|
||||
int int_txunder;
|
||||
int int_rxover;
|
||||
int int_sip;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -1,157 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: esi.c
|
||||
* Version: 1.6
|
||||
* Description: Driver for the Extended Systems JetEye PC dongle
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sat Feb 21 18:54:38 1998
|
||||
* Modified at: Sun Oct 27 22:01:04 2002
|
||||
* Modified by: Martin Diehl <mad@mdiehl.de>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, <dagb@cs.uit.no>,
|
||||
* Copyright (c) 1998 Thomas Davis, <ratbert@radiks.net>,
|
||||
* Copyright (c) 2002 Martin Diehl, <mad@mdiehl.de>,
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
|
||||
#include "sir-dev.h"
|
||||
|
||||
static int esi_open(struct sir_dev *);
|
||||
static int esi_close(struct sir_dev *);
|
||||
static int esi_change_speed(struct sir_dev *, unsigned);
|
||||
static int esi_reset(struct sir_dev *);
|
||||
|
||||
static struct dongle_driver esi = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "JetEye PC ESI-9680 PC",
|
||||
.type = IRDA_ESI_DONGLE,
|
||||
.open = esi_open,
|
||||
.close = esi_close,
|
||||
.reset = esi_reset,
|
||||
.set_speed = esi_change_speed,
|
||||
};
|
||||
|
||||
static int __init esi_sir_init(void)
|
||||
{
|
||||
return irda_register_dongle(&esi);
|
||||
}
|
||||
|
||||
static void __exit esi_sir_cleanup(void)
|
||||
{
|
||||
irda_unregister_dongle(&esi);
|
||||
}
|
||||
|
||||
static int esi_open(struct sir_dev *dev)
|
||||
{
|
||||
struct qos_info *qos = &dev->qos;
|
||||
|
||||
/* Power up and set dongle to 9600 baud */
|
||||
sirdev_set_dtr_rts(dev, FALSE, TRUE);
|
||||
|
||||
qos->baud_rate.bits &= IR_9600|IR_19200|IR_115200;
|
||||
qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */
|
||||
irda_qos_bits_to_value(qos);
|
||||
|
||||
/* irda thread waits 50 msec for power settling */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int esi_close(struct sir_dev *dev)
|
||||
{
|
||||
/* Power off dongle */
|
||||
sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function esi_change_speed (task)
|
||||
*
|
||||
* Set the speed for the Extended Systems JetEye PC ESI-9680 type dongle
|
||||
* Apparently (see old esi-driver) no delays are needed here...
|
||||
*
|
||||
*/
|
||||
static int esi_change_speed(struct sir_dev *dev, unsigned speed)
|
||||
{
|
||||
int ret = 0;
|
||||
int dtr, rts;
|
||||
|
||||
switch (speed) {
|
||||
case 19200:
|
||||
dtr = TRUE;
|
||||
rts = FALSE;
|
||||
break;
|
||||
case 115200:
|
||||
dtr = rts = TRUE;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
speed = 9600;
|
||||
/* fall through */
|
||||
case 9600:
|
||||
dtr = FALSE;
|
||||
rts = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Change speed of dongle */
|
||||
sirdev_set_dtr_rts(dev, dtr, rts);
|
||||
dev->speed = speed;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function esi_reset (task)
|
||||
*
|
||||
* Reset dongle;
|
||||
*
|
||||
*/
|
||||
static int esi_reset(struct sir_dev *dev)
|
||||
{
|
||||
sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
|
||||
/* Hm, the old esi-driver left the dongle unpowered relying on
|
||||
* the following speed change to repower. This might work for
|
||||
* the esi because we only need the modem lines. However, now the
|
||||
* general rule is reset must bring the dongle to some working
|
||||
* well-known state because speed change might write to registers.
|
||||
* The old esi-driver didn't any delay here - let's hope it' fine.
|
||||
*/
|
||||
|
||||
sirdev_set_dtr_rts(dev, FALSE, TRUE);
|
||||
dev->speed = 9600;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
|
||||
MODULE_DESCRIPTION("Extended Systems JetEye PC dongle driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("irda-dongle-1"); /* IRDA_ESI_DONGLE */
|
||||
|
||||
module_init(esi_sir_init);
|
||||
module_exit(esi_sir_cleanup);
|
||||
|
|
@ -1,252 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: girbil.c
|
||||
* Version: 1.2
|
||||
* Description: Implementation for the Greenwich GIrBIL dongle
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sat Feb 6 21:02:33 1999
|
||||
* Modified at: Fri Dec 17 09:13:20 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
|
||||
#include "sir-dev.h"
|
||||
|
||||
static int girbil_reset(struct sir_dev *dev);
|
||||
static int girbil_open(struct sir_dev *dev);
|
||||
static int girbil_close(struct sir_dev *dev);
|
||||
static int girbil_change_speed(struct sir_dev *dev, unsigned speed);
|
||||
|
||||
/* Control register 1 */
|
||||
#define GIRBIL_TXEN 0x01 /* Enable transmitter */
|
||||
#define GIRBIL_RXEN 0x02 /* Enable receiver */
|
||||
#define GIRBIL_ECAN 0x04 /* Cancel self emitted data */
|
||||
#define GIRBIL_ECHO 0x08 /* Echo control characters */
|
||||
|
||||
/* LED Current Register (0x2) */
|
||||
#define GIRBIL_HIGH 0x20
|
||||
#define GIRBIL_MEDIUM 0x21
|
||||
#define GIRBIL_LOW 0x22
|
||||
|
||||
/* Baud register (0x3) */
|
||||
#define GIRBIL_2400 0x30
|
||||
#define GIRBIL_4800 0x31
|
||||
#define GIRBIL_9600 0x32
|
||||
#define GIRBIL_19200 0x33
|
||||
#define GIRBIL_38400 0x34
|
||||
#define GIRBIL_57600 0x35
|
||||
#define GIRBIL_115200 0x36
|
||||
|
||||
/* Mode register (0x4) */
|
||||
#define GIRBIL_IRDA 0x40
|
||||
#define GIRBIL_ASK 0x41
|
||||
|
||||
/* Control register 2 (0x5) */
|
||||
#define GIRBIL_LOAD 0x51 /* Load the new baud rate value */
|
||||
|
||||
static struct dongle_driver girbil = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "Greenwich GIrBIL",
|
||||
.type = IRDA_GIRBIL_DONGLE,
|
||||
.open = girbil_open,
|
||||
.close = girbil_close,
|
||||
.reset = girbil_reset,
|
||||
.set_speed = girbil_change_speed,
|
||||
};
|
||||
|
||||
static int __init girbil_sir_init(void)
|
||||
{
|
||||
return irda_register_dongle(&girbil);
|
||||
}
|
||||
|
||||
static void __exit girbil_sir_cleanup(void)
|
||||
{
|
||||
irda_unregister_dongle(&girbil);
|
||||
}
|
||||
|
||||
static int girbil_open(struct sir_dev *dev)
|
||||
{
|
||||
struct qos_info *qos = &dev->qos;
|
||||
|
||||
/* Power on dongle */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
|
||||
qos->min_turn_time.bits = 0x03;
|
||||
irda_qos_bits_to_value(qos);
|
||||
|
||||
/* irda thread waits 50 msec for power settling */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int girbil_close(struct sir_dev *dev)
|
||||
{
|
||||
/* Power off dongle */
|
||||
sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function girbil_change_speed (dev, speed)
|
||||
*
|
||||
* Set the speed for the Girbil type dongle.
|
||||
*
|
||||
*/
|
||||
|
||||
#define GIRBIL_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1)
|
||||
|
||||
static int girbil_change_speed(struct sir_dev *dev, unsigned speed)
|
||||
{
|
||||
unsigned state = dev->fsm.substate;
|
||||
unsigned delay = 0;
|
||||
u8 control[2];
|
||||
static int ret = 0;
|
||||
|
||||
/* dongle alread reset - port and dongle at default speed */
|
||||
|
||||
switch(state) {
|
||||
|
||||
case SIRDEV_STATE_DONGLE_SPEED:
|
||||
|
||||
/* Set DTR and Clear RTS to enter command mode */
|
||||
sirdev_set_dtr_rts(dev, FALSE, TRUE);
|
||||
|
||||
udelay(25); /* better wait a little while */
|
||||
|
||||
ret = 0;
|
||||
switch (speed) {
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
/* fall through */
|
||||
case 9600:
|
||||
control[0] = GIRBIL_9600;
|
||||
break;
|
||||
case 19200:
|
||||
control[0] = GIRBIL_19200;
|
||||
break;
|
||||
case 34800:
|
||||
control[0] = GIRBIL_38400;
|
||||
break;
|
||||
case 57600:
|
||||
control[0] = GIRBIL_57600;
|
||||
break;
|
||||
case 115200:
|
||||
control[0] = GIRBIL_115200;
|
||||
break;
|
||||
}
|
||||
control[1] = GIRBIL_LOAD;
|
||||
|
||||
/* Write control bytes */
|
||||
sirdev_raw_write(dev, control, 2);
|
||||
|
||||
dev->speed = speed;
|
||||
|
||||
state = GIRBIL_STATE_WAIT_SPEED;
|
||||
delay = 100;
|
||||
break;
|
||||
|
||||
case GIRBIL_STATE_WAIT_SPEED:
|
||||
/* Go back to normal mode */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
udelay(25); /* better wait a little while */
|
||||
break;
|
||||
|
||||
default:
|
||||
net_err_ratelimited("%s - undefined state %d\n",
|
||||
__func__, state);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
dev->fsm.substate = state;
|
||||
return (delay > 0) ? delay : ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function girbil_reset (driver)
|
||||
*
|
||||
* This function resets the girbil dongle.
|
||||
*
|
||||
* Algorithm:
|
||||
* 0. set RTS, and wait at least 5 ms
|
||||
* 1. clear RTS
|
||||
*/
|
||||
|
||||
|
||||
#define GIRBIL_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET + 1)
|
||||
#define GIRBIL_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET + 2)
|
||||
#define GIRBIL_STATE_WAIT3_RESET (SIRDEV_STATE_DONGLE_RESET + 3)
|
||||
|
||||
static int girbil_reset(struct sir_dev *dev)
|
||||
{
|
||||
unsigned state = dev->fsm.substate;
|
||||
unsigned delay = 0;
|
||||
u8 control = GIRBIL_TXEN | GIRBIL_RXEN;
|
||||
int ret = 0;
|
||||
|
||||
switch (state) {
|
||||
case SIRDEV_STATE_DONGLE_RESET:
|
||||
/* Reset dongle */
|
||||
sirdev_set_dtr_rts(dev, TRUE, FALSE);
|
||||
/* Sleep at least 5 ms */
|
||||
delay = 20;
|
||||
state = GIRBIL_STATE_WAIT1_RESET;
|
||||
break;
|
||||
|
||||
case GIRBIL_STATE_WAIT1_RESET:
|
||||
/* Set DTR and clear RTS to enter command mode */
|
||||
sirdev_set_dtr_rts(dev, FALSE, TRUE);
|
||||
delay = 20;
|
||||
state = GIRBIL_STATE_WAIT2_RESET;
|
||||
break;
|
||||
|
||||
case GIRBIL_STATE_WAIT2_RESET:
|
||||
/* Write control byte */
|
||||
sirdev_raw_write(dev, &control, 1);
|
||||
delay = 20;
|
||||
state = GIRBIL_STATE_WAIT3_RESET;
|
||||
break;
|
||||
|
||||
case GIRBIL_STATE_WAIT3_RESET:
|
||||
/* Go back to normal mode */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
dev->speed = 9600;
|
||||
break;
|
||||
|
||||
default:
|
||||
net_err_ratelimited("%s(), undefined state %d\n",
|
||||
__func__, state);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
dev->fsm.substate = state;
|
||||
return (delay > 0) ? delay : ret;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
|
||||
MODULE_DESCRIPTION("Greenwich GIrBIL dongle driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("irda-dongle-4"); /* IRDA_GIRBIL_DONGLE */
|
||||
|
||||
module_init(girbil_sir_init);
|
||||
module_exit(girbil_sir_cleanup);
|
File diff suppressed because it is too large
Load Diff
|
@ -1,175 +0,0 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* Filename: irda-usb.h
|
||||
* Version: 0.10
|
||||
* Description: IrDA-USB Driver
|
||||
* Status: Experimental
|
||||
* Author: Dag Brattli <dag@brattli.net>
|
||||
*
|
||||
* Copyright (C) 2001, Roman Weissgaerber <weissg@vienna.at>
|
||||
* Copyright (C) 2000, Dag Brattli <dag@brattli.net>
|
||||
* Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.com>
|
||||
* Copyright (C) 2004, SigmaTel, Inc. <irquality@sigmatel.com>
|
||||
* Copyright (C) 2005, Milan Beno <beno@pobox.sk>
|
||||
* Copyright (C) 2006, Nick FEdchik <nick@fedchik.org.ua>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include <linux/ktime.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irda_device.h> /* struct irlap_cb */
|
||||
|
||||
#define RX_COPY_THRESHOLD 200
|
||||
#define IRDA_USB_MAX_MTU 2051
|
||||
#define IRDA_USB_SPEED_MTU 64 /* Weird, but work like this */
|
||||
|
||||
/* Maximum number of active URB on the Rx path
|
||||
* This is the amount of buffers the we keep between the USB harware and the
|
||||
* IrDA stack.
|
||||
*
|
||||
* Note : the network layer does also queue the packets between us and the
|
||||
* IrDA stack, and is actually pretty fast and efficient in doing that.
|
||||
* Therefore, we don't need to have a large number of URBs, and we can
|
||||
* perfectly live happy with only one. We certainly don't need to keep the
|
||||
* full IrTTP window around here...
|
||||
* I repeat for those who have trouble to understand : 1 URB is plenty
|
||||
* good enough to handle back-to-back (brickwalled) frames. I tried it,
|
||||
* it works (it's the hardware that has trouble doing it).
|
||||
*
|
||||
* Having 2 URBs would allow the USB stack to process one URB while we take
|
||||
* care of the other and then swap the URBs...
|
||||
* On the other hand, increasing the number of URB will have penalities
|
||||
* in term of latency and will interact with the link management in IrLAP...
|
||||
* Jean II */
|
||||
#define IU_MAX_ACTIVE_RX_URBS 1 /* Don't touch !!! */
|
||||
|
||||
/* When a Rx URB is passed back to us, we can't reuse it immediately,
|
||||
* because it may still be referenced by the USB layer. Therefore we
|
||||
* need to keep one extra URB in the Rx path.
|
||||
* Jean II */
|
||||
#define IU_MAX_RX_URBS (IU_MAX_ACTIVE_RX_URBS + 1)
|
||||
|
||||
/* Various ugly stuff to try to workaround generic problems */
|
||||
/* Send speed command in case of timeout, just for trying to get things sane */
|
||||
#define IU_BUG_KICK_TIMEOUT
|
||||
/* Show the USB class descriptor */
|
||||
#undef IU_DUMP_CLASS_DESC
|
||||
/* Assume a minimum round trip latency for USB transfer (in us)...
|
||||
* USB transfer are done in the next USB slot if there is no traffic
|
||||
* (1/19 msec) and is done at 12 Mb/s :
|
||||
* Waiting for slot + tx = (53us + 16us) * 2 = 137us minimum.
|
||||
* Rx notification will only be done at the end of the USB frame period :
|
||||
* OHCI : frame period = 1ms
|
||||
* UHCI : frame period = 1ms, but notification can take 2 or 3 ms :-(
|
||||
* EHCI : frame period = 125us */
|
||||
#define IU_USB_MIN_RTT 500 /* This should be safe in most cases */
|
||||
|
||||
/* Inbound header */
|
||||
#define MEDIA_BUSY 0x80
|
||||
|
||||
#define SPEED_2400 0x01
|
||||
#define SPEED_9600 0x02
|
||||
#define SPEED_19200 0x03
|
||||
#define SPEED_38400 0x04
|
||||
#define SPEED_57600 0x05
|
||||
#define SPEED_115200 0x06
|
||||
#define SPEED_576000 0x07
|
||||
#define SPEED_1152000 0x08
|
||||
#define SPEED_4000000 0x09
|
||||
#define SPEED_16000000 0x0a
|
||||
|
||||
/* Basic capabilities */
|
||||
#define IUC_DEFAULT 0x00 /* Basic device compliant with 1.0 spec */
|
||||
/* Main bugs */
|
||||
#define IUC_SPEED_BUG 0x01 /* Device doesn't set speed after the frame */
|
||||
#define IUC_NO_WINDOW 0x02 /* Device doesn't behave with big Rx window */
|
||||
#define IUC_NO_TURN 0x04 /* Device doesn't do turnaround by itself */
|
||||
/* Not currently used */
|
||||
#define IUC_SIR_ONLY 0x08 /* Device doesn't behave at FIR speeds */
|
||||
#define IUC_SMALL_PKT 0x10 /* Device doesn't behave with big Rx packets */
|
||||
#define IUC_MAX_WINDOW 0x20 /* Device underestimate the Rx window */
|
||||
#define IUC_MAX_XBOFS 0x40 /* Device need more xbofs than advertised */
|
||||
#define IUC_STIR421X 0x80 /* SigmaTel 4210/4220/4116 VFIR */
|
||||
|
||||
/* USB class definitions */
|
||||
#define USB_IRDA_HEADER 0x01
|
||||
#define USB_CLASS_IRDA 0x02 /* USB_CLASS_APP_SPEC subclass */
|
||||
#define USB_DT_IRDA 0x21
|
||||
#define USB_IRDA_STIR421X_HEADER 0x03
|
||||
#define IU_SIGMATEL_MAX_RX_URBS (IU_MAX_ACTIVE_RX_URBS + \
|
||||
USB_IRDA_STIR421X_HEADER)
|
||||
|
||||
struct irda_class_desc {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__le16 bcdSpecRevision;
|
||||
__u8 bmDataSize;
|
||||
__u8 bmWindowSize;
|
||||
__u8 bmMinTurnaroundTime;
|
||||
__le16 wBaudRate;
|
||||
__u8 bmAdditionalBOFs;
|
||||
__u8 bIrdaRateSniff;
|
||||
__u8 bMaxUnicastList;
|
||||
} __packed;
|
||||
|
||||
/* class specific interface request to get the IrDA-USB class descriptor
|
||||
* (6.2.5, USB-IrDA class spec 1.0) */
|
||||
|
||||
#define IU_REQ_GET_CLASS_DESC 0x06
|
||||
#define STIR421X_MAX_PATCH_DOWNLOAD_SIZE 1023
|
||||
|
||||
struct irda_usb_cb {
|
||||
struct irda_class_desc *irda_desc;
|
||||
struct usb_device *usbdev; /* init: probe_irda */
|
||||
struct usb_interface *usbintf; /* init: probe_irda */
|
||||
int netopen; /* Device is active for network */
|
||||
int present; /* Device is present on the bus */
|
||||
__u32 capability; /* Capability of the hardware */
|
||||
__u8 bulk_in_ep; /* Rx Endpoint assignments */
|
||||
__u8 bulk_out_ep; /* Tx Endpoint assignments */
|
||||
__u16 bulk_out_mtu; /* Max Tx packet size in bytes */
|
||||
__u8 bulk_int_ep; /* Interrupt Endpoint assignments */
|
||||
|
||||
__u8 max_rx_urb;
|
||||
struct urb **rx_urb; /* URBs used to receive data frames */
|
||||
struct urb *idle_rx_urb; /* Pointer to idle URB in Rx path */
|
||||
struct urb *tx_urb; /* URB used to send data frames */
|
||||
struct urb *speed_urb; /* URB used to send speed commands */
|
||||
|
||||
struct net_device *netdev; /* Yes! we are some kind of netdev. */
|
||||
struct irlap_cb *irlap; /* The link layer we are binded to */
|
||||
struct qos_info qos;
|
||||
char *speed_buff; /* Buffer for speed changes */
|
||||
char *tx_buff;
|
||||
|
||||
ktime_t stamp;
|
||||
|
||||
spinlock_t lock; /* For serializing Tx operations */
|
||||
|
||||
__u16 xbofs; /* Current xbofs setting */
|
||||
__s16 new_xbofs; /* xbofs we need to set */
|
||||
__u32 speed; /* Current speed */
|
||||
__s32 new_speed; /* speed we need to set */
|
||||
|
||||
__u8 header_length; /* USB-IrDA frame header size */
|
||||
int needspatch; /* device needs firmware patch */
|
||||
|
||||
struct timer_list rx_defer_timer; /* Wait for Rx error to clear */
|
||||
struct urb *rx_defer_timer_urb; /* URB attached to rx_defer_timer */
|
||||
};
|
||||
|
|
@ -1,570 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irtty-sir.c
|
||||
* Version: 2.0
|
||||
* Description: IrDA line discipline implementation
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Tue Dec 9 21:18:38 1997
|
||||
* Modified at: Sun Oct 27 22:13:30 2002
|
||||
* Modified by: Martin Diehl <mad@mdiehl.de>
|
||||
* Sources: slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
|
||||
* Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
|
||||
*
|
||||
* Copyright (c) 1998-2000 Dag Brattli,
|
||||
* Copyright (c) 2002 Martin Diehl,
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irda_device.h>
|
||||
|
||||
#include "sir-dev.h"
|
||||
#include "irtty-sir.h"
|
||||
|
||||
static int qos_mtt_bits = 0x03; /* 5 ms or more */
|
||||
|
||||
module_param(qos_mtt_bits, int, 0);
|
||||
MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");
|
||||
|
||||
/* ------------------------------------------------------- */
|
||||
|
||||
/* device configuration callbacks always invoked with irda-thread context */
|
||||
|
||||
/* find out, how many chars we have in buffers below us
|
||||
* this is allowed to lie, i.e. return less chars than we
|
||||
* actually have. The returned value is used to determine
|
||||
* how long the irdathread should wait before doing the
|
||||
* real blocking wait_until_sent()
|
||||
*/
|
||||
|
||||
static int irtty_chars_in_buffer(struct sir_dev *dev)
|
||||
{
|
||||
struct sirtty_cb *priv = dev->priv;
|
||||
|
||||
IRDA_ASSERT(priv != NULL, return -1;);
|
||||
IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;);
|
||||
|
||||
return tty_chars_in_buffer(priv->tty);
|
||||
}
|
||||
|
||||
/* Wait (sleep) until underlaying hardware finished transmission
|
||||
* i.e. hardware buffers are drained
|
||||
* this must block and not return before all characters are really sent
|
||||
*
|
||||
* If the tty sits on top of a 16550A-like uart, there are typically
|
||||
* up to 16 bytes in the fifo - f.e. 9600 bps 8N1 needs 16.7 msec
|
||||
*
|
||||
* With usbserial the uart-fifo is basically replaced by the converter's
|
||||
* outgoing endpoint buffer, which can usually hold 64 bytes (at least).
|
||||
* With pl2303 it appears we are safe with 60msec here.
|
||||
*
|
||||
* I really wish all serial drivers would provide
|
||||
* correct implementation of wait_until_sent()
|
||||
*/
|
||||
|
||||
#define USBSERIAL_TX_DONE_DELAY 60
|
||||
|
||||
static void irtty_wait_until_sent(struct sir_dev *dev)
|
||||
{
|
||||
struct sirtty_cb *priv = dev->priv;
|
||||
struct tty_struct *tty;
|
||||
|
||||
IRDA_ASSERT(priv != NULL, return;);
|
||||
IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;);
|
||||
|
||||
tty = priv->tty;
|
||||
if (tty->ops->wait_until_sent) {
|
||||
tty->ops->wait_until_sent(tty, msecs_to_jiffies(100));
|
||||
}
|
||||
else {
|
||||
msleep(USBSERIAL_TX_DONE_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function irtty_change_speed (dev, speed)
|
||||
*
|
||||
* Change the speed of the serial port.
|
||||
*
|
||||
* This may sleep in set_termios (usbserial driver f.e.) and must
|
||||
* not be called from interrupt/timer/tasklet therefore.
|
||||
* All such invocations are deferred to kIrDAd now so we can sleep there.
|
||||
*/
|
||||
|
||||
static int irtty_change_speed(struct sir_dev *dev, unsigned speed)
|
||||
{
|
||||
struct sirtty_cb *priv = dev->priv;
|
||||
struct tty_struct *tty;
|
||||
struct ktermios old_termios;
|
||||
int cflag;
|
||||
|
||||
IRDA_ASSERT(priv != NULL, return -1;);
|
||||
IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;);
|
||||
|
||||
tty = priv->tty;
|
||||
|
||||
down_write(&tty->termios_rwsem);
|
||||
old_termios = tty->termios;
|
||||
cflag = tty->termios.c_cflag;
|
||||
tty_encode_baud_rate(tty, speed, speed);
|
||||
if (tty->ops->set_termios)
|
||||
tty->ops->set_termios(tty, &old_termios);
|
||||
priv->io.speed = speed;
|
||||
up_write(&tty->termios_rwsem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function irtty_set_dtr_rts (dev, dtr, rts)
|
||||
*
|
||||
* This function can be used by dongles etc. to set or reset the status
|
||||
* of the dtr and rts lines
|
||||
*/
|
||||
|
||||
static int irtty_set_dtr_rts(struct sir_dev *dev, int dtr, int rts)
|
||||
{
|
||||
struct sirtty_cb *priv = dev->priv;
|
||||
int set = 0;
|
||||
int clear = 0;
|
||||
|
||||
IRDA_ASSERT(priv != NULL, return -1;);
|
||||
IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;);
|
||||
|
||||
if (rts)
|
||||
set |= TIOCM_RTS;
|
||||
else
|
||||
clear |= TIOCM_RTS;
|
||||
if (dtr)
|
||||
set |= TIOCM_DTR;
|
||||
else
|
||||
clear |= TIOCM_DTR;
|
||||
|
||||
/*
|
||||
* We can't use ioctl() because it expects a non-null file structure,
|
||||
* and we don't have that here.
|
||||
* This function is not yet defined for all tty driver, so
|
||||
* let's be careful... Jean II
|
||||
*/
|
||||
IRDA_ASSERT(priv->tty->ops->tiocmset != NULL, return -1;);
|
||||
priv->tty->ops->tiocmset(priv->tty, set, clear);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------- */
|
||||
|
||||
/* called from sir_dev when there is more data to send
|
||||
* context is either netdev->hard_xmit or some transmit-completion bh
|
||||
* i.e. we are under spinlock here and must not sleep.
|
||||
*/
|
||||
|
||||
static int irtty_do_write(struct sir_dev *dev, const unsigned char *ptr, size_t len)
|
||||
{
|
||||
struct sirtty_cb *priv = dev->priv;
|
||||
struct tty_struct *tty;
|
||||
int writelen;
|
||||
|
||||
IRDA_ASSERT(priv != NULL, return -1;);
|
||||
IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;);
|
||||
|
||||
tty = priv->tty;
|
||||
if (!tty->ops->write)
|
||||
return 0;
|
||||
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
writelen = tty_write_room(tty);
|
||||
if (writelen > len)
|
||||
writelen = len;
|
||||
return tty->ops->write(tty, ptr, writelen);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------- */
|
||||
|
||||
/* irda line discipline callbacks */
|
||||
|
||||
/*
|
||||
* Function irtty_receive_buf( tty, cp, count)
|
||||
*
|
||||
* Handle the 'receiver data ready' interrupt. This function is called
|
||||
* by the 'tty_io' module in the kernel when a block of IrDA data has
|
||||
* been received, which can now be decapsulated and delivered for
|
||||
* further processing
|
||||
*
|
||||
* calling context depends on underlying driver and tty->port->low_latency!
|
||||
* for example (low_latency: 1 / 0):
|
||||
* serial.c: uart-interrupt / softint
|
||||
* usbserial: urb-complete-interrupt / softint
|
||||
*/
|
||||
|
||||
static void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
|
||||
char *fp, int count)
|
||||
{
|
||||
struct sir_dev *dev;
|
||||
struct sirtty_cb *priv = tty->disc_data;
|
||||
int i;
|
||||
|
||||
IRDA_ASSERT(priv != NULL, return;);
|
||||
IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;);
|
||||
|
||||
if (unlikely(count==0)) /* yes, this happens */
|
||||
return;
|
||||
|
||||
dev = priv->dev;
|
||||
if (!dev) {
|
||||
net_warn_ratelimited("%s(), not ready yet!\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
/*
|
||||
* Characters received with a parity error, etc?
|
||||
*/
|
||||
if (fp && *fp++) {
|
||||
pr_debug("Framing or parity error!\n");
|
||||
sirdev_receive(dev, NULL, 0); /* notify sir_dev (updating stats) */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sirdev_receive(dev, cp, count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function irtty_write_wakeup (tty)
|
||||
*
|
||||
* Called by the driver when there's room for more data. If we have
|
||||
* more packets to send, we send them here.
|
||||
*
|
||||
*/
|
||||
static void irtty_write_wakeup(struct tty_struct *tty)
|
||||
{
|
||||
struct sirtty_cb *priv = tty->disc_data;
|
||||
|
||||
IRDA_ASSERT(priv != NULL, return;);
|
||||
IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;);
|
||||
|
||||
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
if (priv->dev)
|
||||
sirdev_write_complete(priv->dev);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Function irtty_stop_receiver (tty, stop)
|
||||
*
|
||||
*/
|
||||
|
||||
static inline void irtty_stop_receiver(struct tty_struct *tty, int stop)
|
||||
{
|
||||
struct ktermios old_termios;
|
||||
int cflag;
|
||||
|
||||
down_write(&tty->termios_rwsem);
|
||||
old_termios = tty->termios;
|
||||
cflag = tty->termios.c_cflag;
|
||||
|
||||
if (stop)
|
||||
cflag &= ~CREAD;
|
||||
else
|
||||
cflag |= CREAD;
|
||||
|
||||
tty->termios.c_cflag = cflag;
|
||||
if (tty->ops->set_termios)
|
||||
tty->ops->set_termios(tty, &old_termios);
|
||||
up_write(&tty->termios_rwsem);
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/* serialize ldisc open/close with sir_dev */
|
||||
static DEFINE_MUTEX(irtty_mutex);
|
||||
|
||||
/* notifier from sir_dev when irda% device gets opened (ifup) */
|
||||
|
||||
static int irtty_start_dev(struct sir_dev *dev)
|
||||
{
|
||||
struct sirtty_cb *priv;
|
||||
struct tty_struct *tty;
|
||||
|
||||
/* serialize with ldisc open/close */
|
||||
mutex_lock(&irtty_mutex);
|
||||
|
||||
priv = dev->priv;
|
||||
if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) {
|
||||
mutex_unlock(&irtty_mutex);
|
||||
return -ESTALE;
|
||||
}
|
||||
|
||||
tty = priv->tty;
|
||||
|
||||
if (tty->ops->start)
|
||||
tty->ops->start(tty);
|
||||
/* Make sure we can receive more data */
|
||||
irtty_stop_receiver(tty, FALSE);
|
||||
|
||||
mutex_unlock(&irtty_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* notifier from sir_dev when irda% device gets closed (ifdown) */
|
||||
|
||||
static int irtty_stop_dev(struct sir_dev *dev)
|
||||
{
|
||||
struct sirtty_cb *priv;
|
||||
struct tty_struct *tty;
|
||||
|
||||
/* serialize with ldisc open/close */
|
||||
mutex_lock(&irtty_mutex);
|
||||
|
||||
priv = dev->priv;
|
||||
if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) {
|
||||
mutex_unlock(&irtty_mutex);
|
||||
return -ESTALE;
|
||||
}
|
||||
|
||||
tty = priv->tty;
|
||||
|
||||
/* Make sure we don't receive more data */
|
||||
irtty_stop_receiver(tty, TRUE);
|
||||
if (tty->ops->stop)
|
||||
tty->ops->stop(tty);
|
||||
|
||||
mutex_unlock(&irtty_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------- */
|
||||
|
||||
static struct sir_driver sir_tty_drv = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "sir_tty",
|
||||
.start_dev = irtty_start_dev,
|
||||
.stop_dev = irtty_stop_dev,
|
||||
.do_write = irtty_do_write,
|
||||
.chars_in_buffer = irtty_chars_in_buffer,
|
||||
.wait_until_sent = irtty_wait_until_sent,
|
||||
.set_speed = irtty_change_speed,
|
||||
.set_dtr_rts = irtty_set_dtr_rts,
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Function irtty_ioctl (tty, file, cmd, arg)
|
||||
*
|
||||
* The Swiss army knife of system calls :-)
|
||||
*
|
||||
*/
|
||||
static int irtty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct irtty_info { char name[6]; } info;
|
||||
struct sir_dev *dev;
|
||||
struct sirtty_cb *priv = tty->disc_data;
|
||||
int err = 0;
|
||||
|
||||
IRDA_ASSERT(priv != NULL, return -ENODEV;);
|
||||
IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -EBADR;);
|
||||
|
||||
pr_debug("%s(cmd=0x%X)\n", __func__, cmd);
|
||||
|
||||
dev = priv->dev;
|
||||
IRDA_ASSERT(dev != NULL, return -1;);
|
||||
|
||||
switch (cmd) {
|
||||
case IRTTY_IOCTDONGLE:
|
||||
/* this call blocks for completion */
|
||||
err = sirdev_set_dongle(dev, (IRDA_DONGLE) arg);
|
||||
break;
|
||||
|
||||
case IRTTY_IOCGET:
|
||||
IRDA_ASSERT(dev->netdev != NULL, return -1;);
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
strncpy(info.name, dev->netdev->name, sizeof(info.name)-1);
|
||||
|
||||
if (copy_to_user((void __user *)arg, &info, sizeof(info)))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
default:
|
||||
err = tty_mode_ioctl(tty, file, cmd, arg);
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Function irtty_open(tty)
|
||||
*
|
||||
* This function is called by the TTY module when the IrDA line
|
||||
* discipline is called for. Because we are sure the tty line exists,
|
||||
* we only have to link it to a free IrDA channel.
|
||||
*/
|
||||
static int irtty_open(struct tty_struct *tty)
|
||||
{
|
||||
struct sir_dev *dev;
|
||||
struct sirtty_cb *priv;
|
||||
int ret = 0;
|
||||
|
||||
/* Module stuff handled via irda_ldisc.owner - Jean II */
|
||||
|
||||
/* stop the underlying driver */
|
||||
irtty_stop_receiver(tty, TRUE);
|
||||
if (tty->ops->stop)
|
||||
tty->ops->stop(tty);
|
||||
|
||||
tty_driver_flush_buffer(tty);
|
||||
|
||||
/* apply mtt override */
|
||||
sir_tty_drv.qos_mtt_bits = qos_mtt_bits;
|
||||
|
||||
/* get a sir device instance for this driver */
|
||||
dev = sirdev_get_instance(&sir_tty_drv, tty->name);
|
||||
if (!dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* allocate private device info block */
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
priv->magic = IRTTY_MAGIC;
|
||||
priv->tty = tty;
|
||||
priv->dev = dev;
|
||||
|
||||
/* serialize with start_dev - in case we were racing with ifup */
|
||||
mutex_lock(&irtty_mutex);
|
||||
|
||||
dev->priv = priv;
|
||||
tty->disc_data = priv;
|
||||
tty->receive_room = 65536;
|
||||
|
||||
mutex_unlock(&irtty_mutex);
|
||||
|
||||
pr_debug("%s - %s: irda line discipline opened\n", __func__, tty->name);
|
||||
|
||||
return 0;
|
||||
|
||||
out_put:
|
||||
sirdev_put_instance(dev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function irtty_close (tty)
|
||||
*
|
||||
* Close down a IrDA channel. This means flushing out any pending queues,
|
||||
* and then restoring the TTY line discipline to what it was before it got
|
||||
* hooked to IrDA (which usually is TTY again).
|
||||
*/
|
||||
static void irtty_close(struct tty_struct *tty)
|
||||
{
|
||||
struct sirtty_cb *priv = tty->disc_data;
|
||||
|
||||
IRDA_ASSERT(priv != NULL, return;);
|
||||
IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;);
|
||||
|
||||
/* Hm, with a dongle attached the dongle driver wants
|
||||
* to close the dongle - which requires the use of
|
||||
* some tty write and/or termios or ioctl operations.
|
||||
* Are we allowed to call those when already requested
|
||||
* to shutdown the ldisc?
|
||||
* If not, we should somehow mark the dev being staled.
|
||||
* Question remains, how to close the dongle in this case...
|
||||
* For now let's assume we are granted to issue tty driver calls
|
||||
* until we return here from the ldisc close. I'm just wondering
|
||||
* how this behaves with hotpluggable serial hardware like
|
||||
* rs232-pcmcia card or usb-serial...
|
||||
*
|
||||
* priv->tty = NULL?;
|
||||
*/
|
||||
|
||||
/* we are dead now */
|
||||
tty->disc_data = NULL;
|
||||
|
||||
sirdev_put_instance(priv->dev);
|
||||
|
||||
/* Stop tty */
|
||||
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
if (tty->ops->stop)
|
||||
tty->ops->stop(tty);
|
||||
|
||||
kfree(priv);
|
||||
|
||||
pr_debug("%s - %s: irda line discipline closed\n", __func__, tty->name);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------- */
|
||||
|
||||
static struct tty_ldisc_ops irda_ldisc = {
|
||||
.magic = TTY_LDISC_MAGIC,
|
||||
.name = "irda",
|
||||
.flags = 0,
|
||||
.open = irtty_open,
|
||||
.close = irtty_close,
|
||||
.read = NULL,
|
||||
.write = NULL,
|
||||
.ioctl = irtty_ioctl,
|
||||
.poll = NULL,
|
||||
.receive_buf = irtty_receive_buf,
|
||||
.write_wakeup = irtty_write_wakeup,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------- */
|
||||
|
||||
static int __init irtty_sir_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = tty_register_ldisc(N_IRDA, &irda_ldisc)) != 0)
|
||||
net_err_ratelimited("IrDA: can't register line discipline (err = %d)\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit irtty_sir_cleanup(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = tty_unregister_ldisc(N_IRDA))) {
|
||||
net_err_ratelimited("%s(), can't unregister line discipline (err = %d)\n",
|
||||
__func__, err);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(irtty_sir_init);
|
||||
module_exit(irtty_sir_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
|
||||
MODULE_DESCRIPTION("IrDA TTY device driver");
|
||||
MODULE_ALIAS_LDISC(N_IRDA);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* sir_tty.h: definitions for the irtty_sir client driver (former irtty)
|
||||
*
|
||||
* Copyright (c) 2002 Martin Diehl
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRTTYSIR_H
|
||||
#define IRTTYSIR_H
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irda_device.h> // chipio_t
|
||||
|
||||
#define IRTTY_IOC_MAGIC 'e'
|
||||
#define IRTTY_IOCTDONGLE _IO(IRTTY_IOC_MAGIC, 1)
|
||||
#define IRTTY_IOCGET _IOR(IRTTY_IOC_MAGIC, 2, struct irtty_info)
|
||||
#define IRTTY_IOC_MAXNR 2
|
||||
|
||||
struct sirtty_cb {
|
||||
magic_t magic;
|
||||
|
||||
struct sir_dev *dev;
|
||||
struct tty_struct *tty;
|
||||
|
||||
chipio_t io; /* IrDA controller information */
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,634 +0,0 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* Filename: kingsun-sir.c
|
||||
* Version: 0.1.1
|
||||
* Description: Irda KingSun/DonShine USB Dongle
|
||||
* Status: Experimental
|
||||
* Author: Alex Villacís Lasso <a_villacis@palosanto.com>
|
||||
*
|
||||
* Based on stir4200 and mcs7780 drivers, with (strange?) differences
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* This is my current (2007-04-25) understanding of how this dongle is supposed
|
||||
* to work. This is based on reverse-engineering and examination of the packet
|
||||
* data sent and received by the WinXP driver using USBSnoopy. Feel free to
|
||||
* update here as more of this dongle is known:
|
||||
*
|
||||
* General: Unlike the other USB IrDA dongles, this particular dongle exposes,
|
||||
* not two bulk (in and out) endpoints, but two *interrupt* ones. This dongle,
|
||||
* like the bulk based ones (stir4200.c and mcs7780.c), requires polling in
|
||||
* order to receive data.
|
||||
* Transmission: Just like stir4200, this dongle uses a raw stream of data,
|
||||
* which needs to be wrapped and escaped in a similar way as in stir4200.c.
|
||||
* Reception: Poll-based, as in stir4200. Each read returns the contents of a
|
||||
* 8-byte buffer, of which the first byte (LSB) indicates the number of bytes
|
||||
* (1-7) of valid data contained within the remaining 7 bytes. For example, if
|
||||
* the buffer had the following contents:
|
||||
* 06 ff ff ff c0 01 04 aa
|
||||
* This means that (06) there are 6 bytes of valid data. The byte 0xaa at the
|
||||
* end is garbage (left over from a previous reception) and is discarded.
|
||||
* If a read returns an "impossible" value as the length of valid data (such as
|
||||
* 0x36) in the first byte, then the buffer is uninitialized (as is the case of
|
||||
* first plug-in) and its contents should be discarded. There is currently no
|
||||
* evidence that the top 5 bits of the 1st byte of the buffer can have values
|
||||
* other than 0 once reception begins.
|
||||
* Once valid bytes are collected, the assembled stream is a sequence of
|
||||
* wrapped IrDA frames that is unwrapped and unescaped as in stir4200.c.
|
||||
* BIG FAT WARNING: the dongle does *not* reset the RX buffer in any way after
|
||||
* a successful read from the host, which means that in absence of further
|
||||
* reception, repeated reads from the dongle will return the exact same
|
||||
* contents repeatedly. Attempts to be smart and cache a previous read seem
|
||||
* to result in corrupted packets, so this driver depends on the unwrap logic
|
||||
* to sort out any repeated reads.
|
||||
* Speed change: no commands observed so far to change speed, assumed fixed
|
||||
* 9600bps (SIR).
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/wrapper.h>
|
||||
#include <net/irda/crc.h>
|
||||
|
||||
/*
|
||||
* According to lsusb, 0x07c0 is assigned to
|
||||
* "Code Mercenaries Hard- und Software GmbH"
|
||||
*/
|
||||
#define KING_VENDOR_ID 0x07c0
|
||||
#define KING_PRODUCT_ID 0x4200
|
||||
|
||||
/* These are the currently known USB ids */
|
||||
static const struct usb_device_id dongles[] = {
|
||||
/* KingSun Co,Ltd IrDA/USB Bridge */
|
||||
{ USB_DEVICE(KING_VENDOR_ID, KING_PRODUCT_ID) },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, dongles);
|
||||
|
||||
#define KINGSUN_MTT 0x07
|
||||
|
||||
#define KINGSUN_FIFO_SIZE 4096
|
||||
#define KINGSUN_EP_IN 0
|
||||
#define KINGSUN_EP_OUT 1
|
||||
|
||||
struct kingsun_cb {
|
||||
struct usb_device *usbdev; /* init: probe_irda */
|
||||
struct net_device *netdev; /* network layer */
|
||||
struct irlap_cb *irlap; /* The link layer we are binded to */
|
||||
|
||||
struct qos_info qos;
|
||||
|
||||
__u8 *in_buf; /* receive buffer */
|
||||
__u8 *out_buf; /* transmit buffer */
|
||||
__u8 max_rx; /* max. atomic read from dongle
|
||||
(usually 8), also size of in_buf */
|
||||
__u8 max_tx; /* max. atomic write to dongle
|
||||
(usually 8) */
|
||||
|
||||
iobuff_t rx_buff; /* receive unwrap state machine */
|
||||
spinlock_t lock;
|
||||
int receiving;
|
||||
|
||||
__u8 ep_in;
|
||||
__u8 ep_out;
|
||||
|
||||
struct urb *tx_urb;
|
||||
struct urb *rx_urb;
|
||||
};
|
||||
|
||||
/* Callback transmission routine */
|
||||
static void kingsun_send_irq(struct urb *urb)
|
||||
{
|
||||
struct kingsun_cb *kingsun = urb->context;
|
||||
struct net_device *netdev = kingsun->netdev;
|
||||
|
||||
/* in process of stopping, just drop data */
|
||||
if (!netif_running(kingsun->netdev)) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"kingsun_send_irq: Network not running!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* unlink, shutdown, unplug, other nasties */
|
||||
if (urb->status != 0) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"kingsun_send_irq: urb asynchronously failed - %d\n",
|
||||
urb->status);
|
||||
}
|
||||
netif_wake_queue(netdev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from net/core when new frame is available.
|
||||
*/
|
||||
static netdev_tx_t kingsun_hard_xmit(struct sk_buff *skb,
|
||||
struct net_device *netdev)
|
||||
{
|
||||
struct kingsun_cb *kingsun;
|
||||
int wraplen;
|
||||
int ret = 0;
|
||||
|
||||
netif_stop_queue(netdev);
|
||||
|
||||
/* the IRDA wrapping routines don't deal with non linear skb */
|
||||
SKB_LINEAR_ASSERT(skb);
|
||||
|
||||
kingsun = netdev_priv(netdev);
|
||||
|
||||
spin_lock(&kingsun->lock);
|
||||
|
||||
/* Append data to the end of whatever data remains to be transmitted */
|
||||
wraplen = async_wrap_skb(skb,
|
||||
kingsun->out_buf,
|
||||
KINGSUN_FIFO_SIZE);
|
||||
|
||||
/* Calculate how much data can be transmitted in this urb */
|
||||
usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev,
|
||||
usb_sndintpipe(kingsun->usbdev, kingsun->ep_out),
|
||||
kingsun->out_buf, wraplen, kingsun_send_irq,
|
||||
kingsun, 1);
|
||||
|
||||
if ((ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC))) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"kingsun_hard_xmit: failed tx_urb submit: %d\n", ret);
|
||||
switch (ret) {
|
||||
case -ENODEV:
|
||||
case -EPIPE:
|
||||
break;
|
||||
default:
|
||||
netdev->stats.tx_errors++;
|
||||
netif_start_queue(netdev);
|
||||
}
|
||||
} else {
|
||||
netdev->stats.tx_packets++;
|
||||
netdev->stats.tx_bytes += skb->len;
|
||||
}
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
spin_unlock(&kingsun->lock);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* Receive callback function */
|
||||
static void kingsun_rcv_irq(struct urb *urb)
|
||||
{
|
||||
struct kingsun_cb *kingsun = urb->context;
|
||||
int ret;
|
||||
|
||||
/* in process of stopping, just drop data */
|
||||
if (!netif_running(kingsun->netdev)) {
|
||||
kingsun->receiving = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* unlink, shutdown, unplug, other nasties */
|
||||
if (urb->status != 0) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"kingsun_rcv_irq: urb asynchronously failed - %d\n",
|
||||
urb->status);
|
||||
kingsun->receiving = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (urb->actual_length == kingsun->max_rx) {
|
||||
__u8 *bytes = urb->transfer_buffer;
|
||||
int i;
|
||||
|
||||
/* The very first byte in the buffer indicates the length of
|
||||
valid data in the read. This byte must be in the range
|
||||
1..kingsun->max_rx -1 . Values outside this range indicate
|
||||
an uninitialized Rx buffer when the dongle has just been
|
||||
plugged in. */
|
||||
if (bytes[0] >= 1 && bytes[0] < kingsun->max_rx) {
|
||||
for (i = 1; i <= bytes[0]; i++) {
|
||||
async_unwrap_char(kingsun->netdev,
|
||||
&kingsun->netdev->stats,
|
||||
&kingsun->rx_buff, bytes[i]);
|
||||
}
|
||||
kingsun->receiving =
|
||||
(kingsun->rx_buff.state != OUTSIDE_FRAME)
|
||||
? 1 : 0;
|
||||
}
|
||||
} else if (urb->actual_length > 0) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"%s(): Unexpected response length, expected %d got %d\n",
|
||||
__func__, kingsun->max_rx, urb->actual_length);
|
||||
}
|
||||
/* This urb has already been filled in kingsun_net_open */
|
||||
ret = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function kingsun_net_open (dev)
|
||||
*
|
||||
* Network device is taken up. Usually this is done by "ifconfig irda0 up"
|
||||
*/
|
||||
static int kingsun_net_open(struct net_device *netdev)
|
||||
{
|
||||
struct kingsun_cb *kingsun = netdev_priv(netdev);
|
||||
int err = -ENOMEM;
|
||||
char hwname[16];
|
||||
|
||||
/* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */
|
||||
kingsun->receiving = 0;
|
||||
|
||||
/* Initialize for SIR to copy data directly into skb. */
|
||||
kingsun->rx_buff.in_frame = FALSE;
|
||||
kingsun->rx_buff.state = OUTSIDE_FRAME;
|
||||
kingsun->rx_buff.truesize = IRDA_SKB_MAX_MTU;
|
||||
kingsun->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
|
||||
if (!kingsun->rx_buff.skb)
|
||||
goto free_mem;
|
||||
|
||||
skb_reserve(kingsun->rx_buff.skb, 1);
|
||||
kingsun->rx_buff.head = kingsun->rx_buff.skb->data;
|
||||
|
||||
kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!kingsun->rx_urb)
|
||||
goto free_mem;
|
||||
|
||||
kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!kingsun->tx_urb)
|
||||
goto free_mem;
|
||||
|
||||
/*
|
||||
* Now that everything should be initialized properly,
|
||||
* Open new IrLAP layer instance to take care of us...
|
||||
*/
|
||||
sprintf(hwname, "usb#%d", kingsun->usbdev->devnum);
|
||||
kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname);
|
||||
if (!kingsun->irlap) {
|
||||
dev_err(&kingsun->usbdev->dev, "irlap_open failed\n");
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
/* Start first reception */
|
||||
usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev,
|
||||
usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in),
|
||||
kingsun->in_buf, kingsun->max_rx,
|
||||
kingsun_rcv_irq, kingsun, 1);
|
||||
kingsun->rx_urb->status = 0;
|
||||
err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
|
||||
if (err) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"first urb-submit failed: %d\n", err);
|
||||
goto close_irlap;
|
||||
}
|
||||
|
||||
netif_start_queue(netdev);
|
||||
|
||||
/* Situation at this point:
|
||||
- all work buffers allocated
|
||||
- urbs allocated and ready to fill
|
||||
- max rx packet known (in max_rx)
|
||||
- unwrap state machine initialized, in state outside of any frame
|
||||
- receive request in progress
|
||||
- IrLAP layer started, about to hand over packets to send
|
||||
*/
|
||||
|
||||
return 0;
|
||||
|
||||
close_irlap:
|
||||
irlap_close(kingsun->irlap);
|
||||
free_mem:
|
||||
if (kingsun->tx_urb) {
|
||||
usb_free_urb(kingsun->tx_urb);
|
||||
kingsun->tx_urb = NULL;
|
||||
}
|
||||
if (kingsun->rx_urb) {
|
||||
usb_free_urb(kingsun->rx_urb);
|
||||
kingsun->rx_urb = NULL;
|
||||
}
|
||||
if (kingsun->rx_buff.skb) {
|
||||
kfree_skb(kingsun->rx_buff.skb);
|
||||
kingsun->rx_buff.skb = NULL;
|
||||
kingsun->rx_buff.head = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function kingsun_net_close (kingsun)
|
||||
*
|
||||
* Network device is taken down. Usually this is done by
|
||||
* "ifconfig irda0 down"
|
||||
*/
|
||||
static int kingsun_net_close(struct net_device *netdev)
|
||||
{
|
||||
struct kingsun_cb *kingsun = netdev_priv(netdev);
|
||||
|
||||
/* Stop transmit processing */
|
||||
netif_stop_queue(netdev);
|
||||
|
||||
/* Mop up receive && transmit urb's */
|
||||
usb_kill_urb(kingsun->tx_urb);
|
||||
usb_kill_urb(kingsun->rx_urb);
|
||||
|
||||
usb_free_urb(kingsun->tx_urb);
|
||||
usb_free_urb(kingsun->rx_urb);
|
||||
|
||||
kingsun->tx_urb = NULL;
|
||||
kingsun->rx_urb = NULL;
|
||||
|
||||
kfree_skb(kingsun->rx_buff.skb);
|
||||
kingsun->rx_buff.skb = NULL;
|
||||
kingsun->rx_buff.head = NULL;
|
||||
kingsun->rx_buff.in_frame = FALSE;
|
||||
kingsun->rx_buff.state = OUTSIDE_FRAME;
|
||||
kingsun->receiving = 0;
|
||||
|
||||
/* Stop and remove instance of IrLAP */
|
||||
if (kingsun->irlap)
|
||||
irlap_close(kingsun->irlap);
|
||||
|
||||
kingsun->irlap = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* IOCTLs : Extra out-of-band network commands...
|
||||
*/
|
||||
static int kingsun_net_ioctl(struct net_device *netdev, struct ifreq *rq,
|
||||
int cmd)
|
||||
{
|
||||
struct if_irda_req *irq = (struct if_irda_req *) rq;
|
||||
struct kingsun_cb *kingsun = netdev_priv(netdev);
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SIOCSBANDWIDTH: /* Set bandwidth */
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/* Check if the device is still there */
|
||||
if (netif_device_present(kingsun->netdev))
|
||||
/* No observed commands for speed change */
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
|
||||
case SIOCSMEDIABUSY: /* Set media busy */
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/* Check if the IrDA stack is still there */
|
||||
if (netif_running(kingsun->netdev))
|
||||
irda_device_set_media_busy(kingsun->netdev, TRUE);
|
||||
break;
|
||||
|
||||
case SIOCGRECEIVING:
|
||||
/* Only approximately true */
|
||||
irq->ifr_receiving = kingsun->receiving;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct net_device_ops kingsun_ops = {
|
||||
.ndo_start_xmit = kingsun_hard_xmit,
|
||||
.ndo_open = kingsun_net_open,
|
||||
.ndo_stop = kingsun_net_close,
|
||||
.ndo_do_ioctl = kingsun_net_ioctl,
|
||||
};
|
||||
|
||||
/*
|
||||
* This routine is called by the USB subsystem for each new device
|
||||
* in the system. We need to check if the device is ours, and in
|
||||
* this case start handling it.
|
||||
*/
|
||||
static int kingsun_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct kingsun_cb *kingsun = NULL;
|
||||
struct net_device *net = NULL;
|
||||
int ret = -ENOMEM;
|
||||
int pipe, maxp_in, maxp_out;
|
||||
__u8 ep_in;
|
||||
__u8 ep_out;
|
||||
|
||||
/* Check that there really are two interrupt endpoints.
|
||||
Check based on the one in drivers/usb/input/usbmouse.c
|
||||
*/
|
||||
interface = intf->cur_altsetting;
|
||||
if (interface->desc.bNumEndpoints != 2) {
|
||||
dev_err(&intf->dev,
|
||||
"kingsun-sir: expected 2 endpoints, found %d\n",
|
||||
interface->desc.bNumEndpoints);
|
||||
return -ENODEV;
|
||||
}
|
||||
endpoint = &interface->endpoint[KINGSUN_EP_IN].desc;
|
||||
if (!usb_endpoint_is_int_in(endpoint)) {
|
||||
dev_err(&intf->dev,
|
||||
"kingsun-sir: endpoint 0 is not interrupt IN\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ep_in = endpoint->bEndpointAddress;
|
||||
pipe = usb_rcvintpipe(dev, ep_in);
|
||||
maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
|
||||
if (maxp_in > 255 || maxp_in <= 1) {
|
||||
dev_err(&intf->dev,
|
||||
"endpoint 0 has max packet size %d not in range\n",
|
||||
maxp_in);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc;
|
||||
if (!usb_endpoint_is_int_out(endpoint)) {
|
||||
dev_err(&intf->dev,
|
||||
"kingsun-sir: endpoint 1 is not interrupt OUT\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ep_out = endpoint->bEndpointAddress;
|
||||
pipe = usb_sndintpipe(dev, ep_out);
|
||||
maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
|
||||
|
||||
/* Allocate network device container. */
|
||||
net = alloc_irdadev(sizeof(*kingsun));
|
||||
if(!net)
|
||||
goto err_out1;
|
||||
|
||||
SET_NETDEV_DEV(net, &intf->dev);
|
||||
kingsun = netdev_priv(net);
|
||||
kingsun->irlap = NULL;
|
||||
kingsun->tx_urb = NULL;
|
||||
kingsun->rx_urb = NULL;
|
||||
kingsun->ep_in = ep_in;
|
||||
kingsun->ep_out = ep_out;
|
||||
kingsun->in_buf = NULL;
|
||||
kingsun->out_buf = NULL;
|
||||
kingsun->max_rx = (__u8)maxp_in;
|
||||
kingsun->max_tx = (__u8)maxp_out;
|
||||
kingsun->netdev = net;
|
||||
kingsun->usbdev = dev;
|
||||
kingsun->rx_buff.in_frame = FALSE;
|
||||
kingsun->rx_buff.state = OUTSIDE_FRAME;
|
||||
kingsun->rx_buff.skb = NULL;
|
||||
kingsun->receiving = 0;
|
||||
spin_lock_init(&kingsun->lock);
|
||||
|
||||
/* Allocate input buffer */
|
||||
kingsun->in_buf = kmalloc(kingsun->max_rx, GFP_KERNEL);
|
||||
if (!kingsun->in_buf)
|
||||
goto free_mem;
|
||||
|
||||
/* Allocate output buffer */
|
||||
kingsun->out_buf = kmalloc(KINGSUN_FIFO_SIZE, GFP_KERNEL);
|
||||
if (!kingsun->out_buf)
|
||||
goto free_mem;
|
||||
|
||||
printk(KERN_INFO "KingSun/DonShine IRDA/USB found at address %d, "
|
||||
"Vendor: %x, Product: %x\n",
|
||||
dev->devnum, le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
|
||||
/* Initialize QoS for this device */
|
||||
irda_init_max_qos_capabilies(&kingsun->qos);
|
||||
|
||||
/* That's the Rx capability. */
|
||||
kingsun->qos.baud_rate.bits &= IR_9600;
|
||||
kingsun->qos.min_turn_time.bits &= KINGSUN_MTT;
|
||||
irda_qos_bits_to_value(&kingsun->qos);
|
||||
|
||||
/* Override the network functions we need to use */
|
||||
net->netdev_ops = &kingsun_ops;
|
||||
|
||||
ret = register_netdev(net);
|
||||
if (ret != 0)
|
||||
goto free_mem;
|
||||
|
||||
dev_info(&net->dev, "IrDA: Registered KingSun/DonShine device %s\n",
|
||||
net->name);
|
||||
|
||||
usb_set_intfdata(intf, kingsun);
|
||||
|
||||
/* Situation at this point:
|
||||
- all work buffers allocated
|
||||
- urbs not allocated, set to NULL
|
||||
- max rx packet known (in max_rx)
|
||||
- unwrap state machine (partially) initialized, but skb == NULL
|
||||
*/
|
||||
|
||||
return 0;
|
||||
|
||||
free_mem:
|
||||
kfree(kingsun->out_buf);
|
||||
kfree(kingsun->in_buf);
|
||||
free_netdev(net);
|
||||
err_out1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The current device is removed, the USB layer tell us to shut it down...
|
||||
*/
|
||||
static void kingsun_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct kingsun_cb *kingsun = usb_get_intfdata(intf);
|
||||
|
||||
if (!kingsun)
|
||||
return;
|
||||
|
||||
unregister_netdev(kingsun->netdev);
|
||||
|
||||
/* Mop up receive && transmit urb's */
|
||||
if (kingsun->tx_urb != NULL) {
|
||||
usb_kill_urb(kingsun->tx_urb);
|
||||
usb_free_urb(kingsun->tx_urb);
|
||||
kingsun->tx_urb = NULL;
|
||||
}
|
||||
if (kingsun->rx_urb != NULL) {
|
||||
usb_kill_urb(kingsun->rx_urb);
|
||||
usb_free_urb(kingsun->rx_urb);
|
||||
kingsun->rx_urb = NULL;
|
||||
}
|
||||
|
||||
kfree(kingsun->out_buf);
|
||||
kfree(kingsun->in_buf);
|
||||
free_netdev(kingsun->netdev);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/* USB suspend, so power off the transmitter/receiver */
|
||||
static int kingsun_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct kingsun_cb *kingsun = usb_get_intfdata(intf);
|
||||
|
||||
netif_device_detach(kingsun->netdev);
|
||||
if (kingsun->tx_urb != NULL) usb_kill_urb(kingsun->tx_urb);
|
||||
if (kingsun->rx_urb != NULL) usb_kill_urb(kingsun->rx_urb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Coming out of suspend, so reset hardware */
|
||||
static int kingsun_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct kingsun_cb *kingsun = usb_get_intfdata(intf);
|
||||
|
||||
if (kingsun->rx_urb != NULL)
|
||||
usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
|
||||
netif_device_attach(kingsun->netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* USB device callbacks
|
||||
*/
|
||||
static struct usb_driver irda_driver = {
|
||||
.name = "kingsun-sir",
|
||||
.probe = kingsun_probe,
|
||||
.disconnect = kingsun_disconnect,
|
||||
.id_table = dongles,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = kingsun_suspend,
|
||||
.resume = kingsun_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
module_usb_driver(irda_driver);
|
||||
|
||||
MODULE_AUTHOR("Alex Villacís Lasso <a_villacis@palosanto.com>");
|
||||
MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun/DonShine");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,912 +0,0 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* Filename: ks959-sir.c
|
||||
* Version: 0.1.2
|
||||
* Description: Irda KingSun KS-959 USB Dongle
|
||||
* Status: Experimental
|
||||
* Author: Alex Villacís Lasso <a_villacis@palosanto.com>
|
||||
* with help from Domen Puncer <domen@coderock.org>
|
||||
*
|
||||
* Based on stir4200, mcs7780, kingsun-sir drivers.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* Following is my most current (2007-07-17) understanding of how the Kingsun
|
||||
* KS-959 dongle is supposed to work. This information was deduced by
|
||||
* reverse-engineering and examining the USB traffic captured with USBSnoopy
|
||||
* from the WinXP driver. Feel free to update here as more of the dongle is
|
||||
* known.
|
||||
*
|
||||
* My most sincere thanks must go to Domen Puncer <domen@coderock.org> for
|
||||
* invaluable help in cracking the obfuscation and padding required for this
|
||||
* dongle.
|
||||
*
|
||||
* General: This dongle exposes one interface with one interrupt IN endpoint.
|
||||
* However, the interrupt endpoint is NOT used at all for this dongle. Instead,
|
||||
* this dongle uses control transfers for everything, including sending and
|
||||
* receiving the IrDA frame data. Apparently the interrupt endpoint is just a
|
||||
* dummy to ensure the dongle has a valid interface to present to the PC.And I
|
||||
* thought the DonShine dongle was weird... In addition, this dongle uses
|
||||
* obfuscation (?!?!), applied at the USB level, to hide the traffic, both sent
|
||||
* and received, from the dongle. I call it obfuscation because the XOR keying
|
||||
* and padding required to produce an USB traffic acceptable for the dongle can
|
||||
* not be explained by any other technical requirement.
|
||||
*
|
||||
* Transmission: To transmit an IrDA frame, the driver must prepare a control
|
||||
* URB with the following as a setup packet:
|
||||
* bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE
|
||||
* bRequest 0x09
|
||||
* wValue <length of valid data before padding, little endian>
|
||||
* wIndex 0x0000
|
||||
* wLength <length of padded data>
|
||||
* The payload packet must be manually wrapped and escaped (as in stir4200.c),
|
||||
* then padded and obfuscated before being sent. Both padding and obfuscation
|
||||
* are implemented in the procedure obfuscate_tx_buffer(). Suffice to say, the
|
||||
* designer/programmer of the dongle used his name as a source for the
|
||||
* obfuscation. WTF?!
|
||||
* Apparently the dongle cannot handle payloads larger than 256 bytes. The
|
||||
* driver has to perform fragmentation in order to send anything larger than
|
||||
* this limit.
|
||||
*
|
||||
* Reception: To receive data, the driver must poll the dongle regularly (like
|
||||
* kingsun-sir.c) with control URBs and the following as a setup packet:
|
||||
* bRequestType USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE
|
||||
* bRequest 0x01
|
||||
* wValue 0x0200
|
||||
* wIndex 0x0000
|
||||
* wLength 0x0800 (size of available buffer)
|
||||
* If there is data to be read, it will be returned as the response payload.
|
||||
* This data is (apparently) not padded, but it is obfuscated. To de-obfuscate
|
||||
* it, the driver must XOR every byte, in sequence, with a value that starts at
|
||||
* 1 and is incremented with each byte processed, and then with 0x55. The value
|
||||
* incremented with each byte processed overflows as an unsigned char. The
|
||||
* resulting bytes form a wrapped SIR frame that is unwrapped and unescaped
|
||||
* as in stir4200.c The incremented value is NOT reset with each frame, but is
|
||||
* kept across the entire session with the dongle. Also, the dongle inserts an
|
||||
* extra garbage byte with value 0x95 (after decoding) every 0xff bytes, which
|
||||
* must be skipped.
|
||||
*
|
||||
* Speed change: To change the speed of the dongle, the driver prepares a
|
||||
* control URB with the following as a setup packet:
|
||||
* bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE
|
||||
* bRequest 0x09
|
||||
* wValue 0x0200
|
||||
* wIndex 0x0001
|
||||
* wLength 0x0008 (length of the payload)
|
||||
* The payload is a 8-byte record, apparently identical to the one used in
|
||||
* drivers/usb/serial/cypress_m8.c to change speed:
|
||||
* __u32 baudSpeed;
|
||||
* unsigned int dataBits : 2; // 0 - 5 bits 3 - 8 bits
|
||||
* unsigned int : 1;
|
||||
* unsigned int stopBits : 1;
|
||||
* unsigned int parityEnable : 1;
|
||||
* unsigned int parityType : 1;
|
||||
* unsigned int : 1;
|
||||
* unsigned int reset : 1;
|
||||
* unsigned char reserved[3]; // set to 0
|
||||
*
|
||||
* For now only SIR speeds have been observed with this dongle. Therefore,
|
||||
* nothing is known on what changes (if any) must be done to frame wrapping /
|
||||
* unwrapping for higher than SIR speeds. This driver assumes no change is
|
||||
* necessary and announces support for all the way to 57600 bps. Although the
|
||||
* package announces support for up to 4MBps, tests with a Sony Ericcson K300
|
||||
* phone show corruption when receiving large frames at 115200 bps, the highest
|
||||
* speed announced by the phone. However, transmission at 115200 bps is OK. Go
|
||||
* figure. Since I don't know whether the phone or the dongle is at fault, max
|
||||
* announced speed is 57600 bps until someone produces a device that can run
|
||||
* at higher speeds with this dongle.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/wrapper.h>
|
||||
#include <net/irda/crc.h>
|
||||
|
||||
#define KS959_VENDOR_ID 0x07d0
|
||||
#define KS959_PRODUCT_ID 0x4959
|
||||
|
||||
/* These are the currently known USB ids */
|
||||
static const struct usb_device_id dongles[] = {
|
||||
/* KingSun Co,Ltd IrDA/USB Bridge */
|
||||
{USB_DEVICE(KS959_VENDOR_ID, KS959_PRODUCT_ID)},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, dongles);
|
||||
|
||||
#define KINGSUN_MTT 0x07
|
||||
#define KINGSUN_REQ_RECV 0x01
|
||||
#define KINGSUN_REQ_SEND 0x09
|
||||
|
||||
#define KINGSUN_RCV_FIFO_SIZE 2048 /* Max length we can receive */
|
||||
#define KINGSUN_SND_FIFO_SIZE 2048 /* Max packet we can send */
|
||||
#define KINGSUN_SND_PACKET_SIZE 256 /* Max packet dongle can handle */
|
||||
|
||||
struct ks959_speedparams {
|
||||
__le32 baudrate; /* baud rate, little endian */
|
||||
__u8 flags;
|
||||
__u8 reserved[3];
|
||||
} __packed;
|
||||
|
||||
#define KS_DATA_5_BITS 0x00
|
||||
#define KS_DATA_6_BITS 0x01
|
||||
#define KS_DATA_7_BITS 0x02
|
||||
#define KS_DATA_8_BITS 0x03
|
||||
|
||||
#define KS_STOP_BITS_1 0x00
|
||||
#define KS_STOP_BITS_2 0x08
|
||||
|
||||
#define KS_PAR_DISABLE 0x00
|
||||
#define KS_PAR_EVEN 0x10
|
||||
#define KS_PAR_ODD 0x30
|
||||
#define KS_RESET 0x80
|
||||
|
||||
struct ks959_cb {
|
||||
struct usb_device *usbdev; /* init: probe_irda */
|
||||
struct net_device *netdev; /* network layer */
|
||||
struct irlap_cb *irlap; /* The link layer we are binded to */
|
||||
|
||||
struct qos_info qos;
|
||||
|
||||
struct usb_ctrlrequest *tx_setuprequest;
|
||||
struct urb *tx_urb;
|
||||
__u8 *tx_buf_clear;
|
||||
unsigned int tx_buf_clear_used;
|
||||
unsigned int tx_buf_clear_sent;
|
||||
__u8 *tx_buf_xored;
|
||||
|
||||
struct usb_ctrlrequest *rx_setuprequest;
|
||||
struct urb *rx_urb;
|
||||
__u8 *rx_buf;
|
||||
__u8 rx_variable_xormask;
|
||||
iobuff_t rx_unwrap_buff;
|
||||
|
||||
struct usb_ctrlrequest *speed_setuprequest;
|
||||
struct urb *speed_urb;
|
||||
struct ks959_speedparams speedparams;
|
||||
unsigned int new_speed;
|
||||
|
||||
spinlock_t lock;
|
||||
int receiving;
|
||||
};
|
||||
|
||||
/* Procedure to perform the obfuscation/padding expected by the dongle
|
||||
*
|
||||
* buf_cleartext (IN) Cleartext version of the IrDA frame to transmit
|
||||
* len_cleartext (IN) Length of the cleartext version of IrDA frame
|
||||
* buf_xoredtext (OUT) Obfuscated version of frame built by proc
|
||||
* len_maxbuf (OUT) Maximum space available at buf_xoredtext
|
||||
*
|
||||
* (return) length of obfuscated frame with padding
|
||||
*
|
||||
* If not enough space (as indicated by len_maxbuf vs. required padding),
|
||||
* zero is returned
|
||||
*
|
||||
* The value of lookup_string is actually a required portion of the algorithm.
|
||||
* Seems the designer of the dongle wanted to state who exactly is responsible
|
||||
* for implementing obfuscation. Send your best (or other) wishes to him ]:-)
|
||||
*/
|
||||
static unsigned int obfuscate_tx_buffer(const __u8 * buf_cleartext,
|
||||
unsigned int len_cleartext,
|
||||
__u8 * buf_xoredtext,
|
||||
unsigned int len_maxbuf)
|
||||
{
|
||||
unsigned int len_xoredtext;
|
||||
|
||||
/* Calculate required length with padding, check for necessary space */
|
||||
len_xoredtext = ((len_cleartext + 7) & ~0x7) + 0x10;
|
||||
if (len_xoredtext <= len_maxbuf) {
|
||||
static const __u8 lookup_string[] = "wangshuofei19710";
|
||||
__u8 xor_mask;
|
||||
|
||||
/* Unlike the WinXP driver, we *do* clear out the padding */
|
||||
memset(buf_xoredtext, 0, len_xoredtext);
|
||||
|
||||
xor_mask = lookup_string[(len_cleartext & 0x0f) ^ 0x06] ^ 0x55;
|
||||
|
||||
while (len_cleartext-- > 0) {
|
||||
*buf_xoredtext++ = *buf_cleartext++ ^ xor_mask;
|
||||
}
|
||||
} else {
|
||||
len_xoredtext = 0;
|
||||
}
|
||||
return len_xoredtext;
|
||||
}
|
||||
|
||||
/* Callback transmission routine */
|
||||
static void ks959_speed_irq(struct urb *urb)
|
||||
{
|
||||
/* unlink, shutdown, unplug, other nasties */
|
||||
if (urb->status != 0) {
|
||||
dev_err(&urb->dev->dev,
|
||||
"ks959_speed_irq: urb asynchronously failed - %d\n",
|
||||
urb->status);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send a control request to change speed of the dongle */
|
||||
static int ks959_change_speed(struct ks959_cb *kingsun, unsigned speed)
|
||||
{
|
||||
static unsigned int supported_speeds[] = { 2400, 9600, 19200, 38400,
|
||||
57600, 115200, 576000, 1152000, 4000000, 0
|
||||
};
|
||||
int err;
|
||||
unsigned int i;
|
||||
|
||||
if (kingsun->speed_setuprequest == NULL || kingsun->speed_urb == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Check that requested speed is among the supported ones */
|
||||
for (i = 0; supported_speeds[i] && supported_speeds[i] != speed; i++) ;
|
||||
if (supported_speeds[i] == 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
memset(&(kingsun->speedparams), 0, sizeof(struct ks959_speedparams));
|
||||
kingsun->speedparams.baudrate = cpu_to_le32(speed);
|
||||
kingsun->speedparams.flags = KS_DATA_8_BITS;
|
||||
|
||||
/* speed_setuprequest pre-filled in ks959_probe */
|
||||
usb_fill_control_urb(kingsun->speed_urb, kingsun->usbdev,
|
||||
usb_sndctrlpipe(kingsun->usbdev, 0),
|
||||
(unsigned char *)kingsun->speed_setuprequest,
|
||||
&(kingsun->speedparams),
|
||||
sizeof(struct ks959_speedparams), ks959_speed_irq,
|
||||
kingsun);
|
||||
kingsun->speed_urb->status = 0;
|
||||
err = usb_submit_urb(kingsun->speed_urb, GFP_ATOMIC);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Submit one fragment of an IrDA frame to the dongle */
|
||||
static void ks959_send_irq(struct urb *urb);
|
||||
static int ks959_submit_tx_fragment(struct ks959_cb *kingsun)
|
||||
{
|
||||
unsigned int padlen;
|
||||
unsigned int wraplen;
|
||||
int ret;
|
||||
|
||||
/* Check whether current plaintext can produce a padded buffer that fits
|
||||
within the range handled by the dongle */
|
||||
wraplen = (KINGSUN_SND_PACKET_SIZE & ~0x7) - 0x10;
|
||||
if (wraplen > kingsun->tx_buf_clear_used)
|
||||
wraplen = kingsun->tx_buf_clear_used;
|
||||
|
||||
/* Perform dongle obfuscation. Also remove the portion of the frame that
|
||||
was just obfuscated and will now be sent to the dongle. */
|
||||
padlen = obfuscate_tx_buffer(kingsun->tx_buf_clear, wraplen,
|
||||
kingsun->tx_buf_xored,
|
||||
KINGSUN_SND_PACKET_SIZE);
|
||||
|
||||
/* Calculate how much data can be transmitted in this urb */
|
||||
kingsun->tx_setuprequest->wValue = cpu_to_le16(wraplen);
|
||||
kingsun->tx_setuprequest->wLength = cpu_to_le16(padlen);
|
||||
/* Rest of the fields were filled in ks959_probe */
|
||||
usb_fill_control_urb(kingsun->tx_urb, kingsun->usbdev,
|
||||
usb_sndctrlpipe(kingsun->usbdev, 0),
|
||||
(unsigned char *)kingsun->tx_setuprequest,
|
||||
kingsun->tx_buf_xored, padlen,
|
||||
ks959_send_irq, kingsun);
|
||||
kingsun->tx_urb->status = 0;
|
||||
ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC);
|
||||
|
||||
/* Remember how much data was sent, in order to update at callback */
|
||||
kingsun->tx_buf_clear_sent = (ret == 0) ? wraplen : 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Callback transmission routine */
|
||||
static void ks959_send_irq(struct urb *urb)
|
||||
{
|
||||
struct ks959_cb *kingsun = urb->context;
|
||||
struct net_device *netdev = kingsun->netdev;
|
||||
int ret = 0;
|
||||
|
||||
/* in process of stopping, just drop data */
|
||||
if (!netif_running(kingsun->netdev)) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"ks959_send_irq: Network not running!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* unlink, shutdown, unplug, other nasties */
|
||||
if (urb->status != 0) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"ks959_send_irq: urb asynchronously failed - %d\n",
|
||||
urb->status);
|
||||
return;
|
||||
}
|
||||
|
||||
if (kingsun->tx_buf_clear_used > 0) {
|
||||
/* Update data remaining to be sent */
|
||||
if (kingsun->tx_buf_clear_sent < kingsun->tx_buf_clear_used) {
|
||||
memmove(kingsun->tx_buf_clear,
|
||||
kingsun->tx_buf_clear +
|
||||
kingsun->tx_buf_clear_sent,
|
||||
kingsun->tx_buf_clear_used -
|
||||
kingsun->tx_buf_clear_sent);
|
||||
}
|
||||
kingsun->tx_buf_clear_used -= kingsun->tx_buf_clear_sent;
|
||||
kingsun->tx_buf_clear_sent = 0;
|
||||
|
||||
if (kingsun->tx_buf_clear_used > 0) {
|
||||
/* There is more data to be sent */
|
||||
if ((ret = ks959_submit_tx_fragment(kingsun)) != 0) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"ks959_send_irq: failed tx_urb submit: %d\n",
|
||||
ret);
|
||||
switch (ret) {
|
||||
case -ENODEV:
|
||||
case -EPIPE:
|
||||
break;
|
||||
default:
|
||||
netdev->stats.tx_errors++;
|
||||
netif_start_queue(netdev);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* All data sent, send next speed && wake network queue */
|
||||
if (kingsun->new_speed != -1 &&
|
||||
cpu_to_le32(kingsun->new_speed) !=
|
||||
kingsun->speedparams.baudrate)
|
||||
ks959_change_speed(kingsun, kingsun->new_speed);
|
||||
|
||||
netif_wake_queue(netdev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from net/core when new frame is available.
|
||||
*/
|
||||
static netdev_tx_t ks959_hard_xmit(struct sk_buff *skb,
|
||||
struct net_device *netdev)
|
||||
{
|
||||
struct ks959_cb *kingsun;
|
||||
unsigned int wraplen;
|
||||
int ret = 0;
|
||||
|
||||
netif_stop_queue(netdev);
|
||||
|
||||
/* the IRDA wrapping routines don't deal with non linear skb */
|
||||
SKB_LINEAR_ASSERT(skb);
|
||||
|
||||
kingsun = netdev_priv(netdev);
|
||||
|
||||
spin_lock(&kingsun->lock);
|
||||
kingsun->new_speed = irda_get_next_speed(skb);
|
||||
|
||||
/* Append data to the end of whatever data remains to be transmitted */
|
||||
wraplen =
|
||||
async_wrap_skb(skb, kingsun->tx_buf_clear, KINGSUN_SND_FIFO_SIZE);
|
||||
kingsun->tx_buf_clear_used = wraplen;
|
||||
|
||||
if ((ret = ks959_submit_tx_fragment(kingsun)) != 0) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"ks959_hard_xmit: failed tx_urb submit: %d\n", ret);
|
||||
switch (ret) {
|
||||
case -ENODEV:
|
||||
case -EPIPE:
|
||||
break;
|
||||
default:
|
||||
netdev->stats.tx_errors++;
|
||||
netif_start_queue(netdev);
|
||||
}
|
||||
} else {
|
||||
netdev->stats.tx_packets++;
|
||||
netdev->stats.tx_bytes += skb->len;
|
||||
|
||||
}
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
spin_unlock(&kingsun->lock);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* Receive callback function */
|
||||
static void ks959_rcv_irq(struct urb *urb)
|
||||
{
|
||||
struct ks959_cb *kingsun = urb->context;
|
||||
int ret;
|
||||
|
||||
/* in process of stopping, just drop data */
|
||||
if (!netif_running(kingsun->netdev)) {
|
||||
kingsun->receiving = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* unlink, shutdown, unplug, other nasties */
|
||||
if (urb->status != 0) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"kingsun_rcv_irq: urb asynchronously failed - %d\n",
|
||||
urb->status);
|
||||
kingsun->receiving = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (urb->actual_length > 0) {
|
||||
__u8 *bytes = urb->transfer_buffer;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < urb->actual_length; i++) {
|
||||
/* De-obfuscation implemented here: variable portion of
|
||||
xormask is incremented, and then used with the encoded
|
||||
byte for the XOR. The result of the operation is used
|
||||
to unwrap the SIR frame. */
|
||||
kingsun->rx_variable_xormask++;
|
||||
bytes[i] =
|
||||
bytes[i] ^ kingsun->rx_variable_xormask ^ 0x55u;
|
||||
|
||||
/* rx_variable_xormask doubles as an index counter so we
|
||||
can skip the byte at 0xff (wrapped around to 0).
|
||||
*/
|
||||
if (kingsun->rx_variable_xormask != 0) {
|
||||
async_unwrap_char(kingsun->netdev,
|
||||
&kingsun->netdev->stats,
|
||||
&kingsun->rx_unwrap_buff,
|
||||
bytes[i]);
|
||||
}
|
||||
}
|
||||
kingsun->receiving =
|
||||
(kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* This urb has already been filled in kingsun_net_open. Setup
|
||||
packet must be re-filled, but it is assumed that urb keeps the
|
||||
pointer to the initial setup packet, as well as the payload buffer.
|
||||
Setup packet is already pre-filled at ks959_probe.
|
||||
*/
|
||||
urb->status = 0;
|
||||
ret = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function kingsun_net_open (dev)
|
||||
*
|
||||
* Network device is taken up. Usually this is done by "ifconfig irda0 up"
|
||||
*/
|
||||
static int ks959_net_open(struct net_device *netdev)
|
||||
{
|
||||
struct ks959_cb *kingsun = netdev_priv(netdev);
|
||||
int err = -ENOMEM;
|
||||
char hwname[16];
|
||||
|
||||
/* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */
|
||||
kingsun->receiving = 0;
|
||||
|
||||
/* Initialize for SIR to copy data directly into skb. */
|
||||
kingsun->rx_unwrap_buff.in_frame = FALSE;
|
||||
kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
|
||||
kingsun->rx_unwrap_buff.truesize = IRDA_SKB_MAX_MTU;
|
||||
kingsun->rx_unwrap_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
|
||||
if (!kingsun->rx_unwrap_buff.skb)
|
||||
goto free_mem;
|
||||
|
||||
skb_reserve(kingsun->rx_unwrap_buff.skb, 1);
|
||||
kingsun->rx_unwrap_buff.head = kingsun->rx_unwrap_buff.skb->data;
|
||||
|
||||
kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!kingsun->rx_urb)
|
||||
goto free_mem;
|
||||
|
||||
kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!kingsun->tx_urb)
|
||||
goto free_mem;
|
||||
|
||||
kingsun->speed_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!kingsun->speed_urb)
|
||||
goto free_mem;
|
||||
|
||||
/* Initialize speed for dongle */
|
||||
kingsun->new_speed = 9600;
|
||||
err = ks959_change_speed(kingsun, 9600);
|
||||
if (err < 0)
|
||||
goto free_mem;
|
||||
|
||||
/*
|
||||
* Now that everything should be initialized properly,
|
||||
* Open new IrLAP layer instance to take care of us...
|
||||
*/
|
||||
sprintf(hwname, "usb#%d", kingsun->usbdev->devnum);
|
||||
kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname);
|
||||
if (!kingsun->irlap) {
|
||||
err = -ENOMEM;
|
||||
dev_err(&kingsun->usbdev->dev, "irlap_open failed\n");
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
/* Start reception. Setup request already pre-filled in ks959_probe */
|
||||
usb_fill_control_urb(kingsun->rx_urb, kingsun->usbdev,
|
||||
usb_rcvctrlpipe(kingsun->usbdev, 0),
|
||||
(unsigned char *)kingsun->rx_setuprequest,
|
||||
kingsun->rx_buf, KINGSUN_RCV_FIFO_SIZE,
|
||||
ks959_rcv_irq, kingsun);
|
||||
kingsun->rx_urb->status = 0;
|
||||
err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
|
||||
if (err) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"first urb-submit failed: %d\n", err);
|
||||
goto close_irlap;
|
||||
}
|
||||
|
||||
netif_start_queue(netdev);
|
||||
|
||||
/* Situation at this point:
|
||||
- all work buffers allocated
|
||||
- urbs allocated and ready to fill
|
||||
- max rx packet known (in max_rx)
|
||||
- unwrap state machine initialized, in state outside of any frame
|
||||
- receive request in progress
|
||||
- IrLAP layer started, about to hand over packets to send
|
||||
*/
|
||||
|
||||
return 0;
|
||||
|
||||
close_irlap:
|
||||
irlap_close(kingsun->irlap);
|
||||
free_mem:
|
||||
usb_free_urb(kingsun->speed_urb);
|
||||
kingsun->speed_urb = NULL;
|
||||
usb_free_urb(kingsun->tx_urb);
|
||||
kingsun->tx_urb = NULL;
|
||||
usb_free_urb(kingsun->rx_urb);
|
||||
kingsun->rx_urb = NULL;
|
||||
if (kingsun->rx_unwrap_buff.skb) {
|
||||
kfree_skb(kingsun->rx_unwrap_buff.skb);
|
||||
kingsun->rx_unwrap_buff.skb = NULL;
|
||||
kingsun->rx_unwrap_buff.head = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function kingsun_net_close (kingsun)
|
||||
*
|
||||
* Network device is taken down. Usually this is done by
|
||||
* "ifconfig irda0 down"
|
||||
*/
|
||||
static int ks959_net_close(struct net_device *netdev)
|
||||
{
|
||||
struct ks959_cb *kingsun = netdev_priv(netdev);
|
||||
|
||||
/* Stop transmit processing */
|
||||
netif_stop_queue(netdev);
|
||||
|
||||
/* Mop up receive && transmit urb's */
|
||||
usb_kill_urb(kingsun->tx_urb);
|
||||
usb_free_urb(kingsun->tx_urb);
|
||||
kingsun->tx_urb = NULL;
|
||||
|
||||
usb_kill_urb(kingsun->speed_urb);
|
||||
usb_free_urb(kingsun->speed_urb);
|
||||
kingsun->speed_urb = NULL;
|
||||
|
||||
usb_kill_urb(kingsun->rx_urb);
|
||||
usb_free_urb(kingsun->rx_urb);
|
||||
kingsun->rx_urb = NULL;
|
||||
|
||||
kfree_skb(kingsun->rx_unwrap_buff.skb);
|
||||
kingsun->rx_unwrap_buff.skb = NULL;
|
||||
kingsun->rx_unwrap_buff.head = NULL;
|
||||
kingsun->rx_unwrap_buff.in_frame = FALSE;
|
||||
kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
|
||||
kingsun->receiving = 0;
|
||||
|
||||
/* Stop and remove instance of IrLAP */
|
||||
if (kingsun->irlap)
|
||||
irlap_close(kingsun->irlap);
|
||||
|
||||
kingsun->irlap = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* IOCTLs : Extra out-of-band network commands...
|
||||
*/
|
||||
static int ks959_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
|
||||
{
|
||||
struct if_irda_req *irq = (struct if_irda_req *)rq;
|
||||
struct ks959_cb *kingsun = netdev_priv(netdev);
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SIOCSBANDWIDTH: /* Set bandwidth */
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/* Check if the device is still there */
|
||||
if (netif_device_present(kingsun->netdev))
|
||||
return ks959_change_speed(kingsun, irq->ifr_baudrate);
|
||||
break;
|
||||
|
||||
case SIOCSMEDIABUSY: /* Set media busy */
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/* Check if the IrDA stack is still there */
|
||||
if (netif_running(kingsun->netdev))
|
||||
irda_device_set_media_busy(kingsun->netdev, TRUE);
|
||||
break;
|
||||
|
||||
case SIOCGRECEIVING:
|
||||
/* Only approximately true */
|
||||
irq->ifr_receiving = kingsun->receiving;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct net_device_ops ks959_ops = {
|
||||
.ndo_start_xmit = ks959_hard_xmit,
|
||||
.ndo_open = ks959_net_open,
|
||||
.ndo_stop = ks959_net_close,
|
||||
.ndo_do_ioctl = ks959_net_ioctl,
|
||||
};
|
||||
/*
|
||||
* This routine is called by the USB subsystem for each new device
|
||||
* in the system. We need to check if the device is ours, and in
|
||||
* this case start handling it.
|
||||
*/
|
||||
static int ks959_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct ks959_cb *kingsun = NULL;
|
||||
struct net_device *net = NULL;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
/* Allocate network device container. */
|
||||
net = alloc_irdadev(sizeof(*kingsun));
|
||||
if (!net)
|
||||
goto err_out1;
|
||||
|
||||
SET_NETDEV_DEV(net, &intf->dev);
|
||||
kingsun = netdev_priv(net);
|
||||
kingsun->netdev = net;
|
||||
kingsun->usbdev = dev;
|
||||
kingsun->irlap = NULL;
|
||||
kingsun->tx_setuprequest = NULL;
|
||||
kingsun->tx_urb = NULL;
|
||||
kingsun->tx_buf_clear = NULL;
|
||||
kingsun->tx_buf_xored = NULL;
|
||||
kingsun->tx_buf_clear_used = 0;
|
||||
kingsun->tx_buf_clear_sent = 0;
|
||||
|
||||
kingsun->rx_setuprequest = NULL;
|
||||
kingsun->rx_urb = NULL;
|
||||
kingsun->rx_buf = NULL;
|
||||
kingsun->rx_variable_xormask = 0;
|
||||
kingsun->rx_unwrap_buff.in_frame = FALSE;
|
||||
kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
|
||||
kingsun->rx_unwrap_buff.skb = NULL;
|
||||
kingsun->receiving = 0;
|
||||
spin_lock_init(&kingsun->lock);
|
||||
|
||||
kingsun->speed_setuprequest = NULL;
|
||||
kingsun->speed_urb = NULL;
|
||||
kingsun->speedparams.baudrate = 0;
|
||||
|
||||
/* Allocate input buffer */
|
||||
kingsun->rx_buf = kmalloc(KINGSUN_RCV_FIFO_SIZE, GFP_KERNEL);
|
||||
if (!kingsun->rx_buf)
|
||||
goto free_mem;
|
||||
|
||||
/* Allocate input setup packet */
|
||||
kingsun->rx_setuprequest =
|
||||
kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
|
||||
if (!kingsun->rx_setuprequest)
|
||||
goto free_mem;
|
||||
kingsun->rx_setuprequest->bRequestType =
|
||||
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
|
||||
kingsun->rx_setuprequest->bRequest = KINGSUN_REQ_RECV;
|
||||
kingsun->rx_setuprequest->wValue = cpu_to_le16(0x0200);
|
||||
kingsun->rx_setuprequest->wIndex = 0;
|
||||
kingsun->rx_setuprequest->wLength = cpu_to_le16(KINGSUN_RCV_FIFO_SIZE);
|
||||
|
||||
/* Allocate output buffer */
|
||||
kingsun->tx_buf_clear = kmalloc(KINGSUN_SND_FIFO_SIZE, GFP_KERNEL);
|
||||
if (!kingsun->tx_buf_clear)
|
||||
goto free_mem;
|
||||
kingsun->tx_buf_xored = kmalloc(KINGSUN_SND_PACKET_SIZE, GFP_KERNEL);
|
||||
if (!kingsun->tx_buf_xored)
|
||||
goto free_mem;
|
||||
|
||||
/* Allocate and initialize output setup packet */
|
||||
kingsun->tx_setuprequest =
|
||||
kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
|
||||
if (!kingsun->tx_setuprequest)
|
||||
goto free_mem;
|
||||
kingsun->tx_setuprequest->bRequestType =
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
|
||||
kingsun->tx_setuprequest->bRequest = KINGSUN_REQ_SEND;
|
||||
kingsun->tx_setuprequest->wValue = 0;
|
||||
kingsun->tx_setuprequest->wIndex = 0;
|
||||
kingsun->tx_setuprequest->wLength = 0;
|
||||
|
||||
/* Allocate and initialize speed setup packet */
|
||||
kingsun->speed_setuprequest =
|
||||
kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
|
||||
if (!kingsun->speed_setuprequest)
|
||||
goto free_mem;
|
||||
kingsun->speed_setuprequest->bRequestType =
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
|
||||
kingsun->speed_setuprequest->bRequest = KINGSUN_REQ_SEND;
|
||||
kingsun->speed_setuprequest->wValue = cpu_to_le16(0x0200);
|
||||
kingsun->speed_setuprequest->wIndex = cpu_to_le16(0x0001);
|
||||
kingsun->speed_setuprequest->wLength =
|
||||
cpu_to_le16(sizeof(struct ks959_speedparams));
|
||||
|
||||
printk(KERN_INFO "KingSun KS-959 IRDA/USB found at address %d, "
|
||||
"Vendor: %x, Product: %x\n",
|
||||
dev->devnum, le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
|
||||
/* Initialize QoS for this device */
|
||||
irda_init_max_qos_capabilies(&kingsun->qos);
|
||||
|
||||
/* Baud rates known to be supported. Please uncomment if devices (other
|
||||
than a SonyEriccson K300 phone) can be shown to support higher speed
|
||||
with this dongle.
|
||||
*/
|
||||
kingsun->qos.baud_rate.bits =
|
||||
IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600;
|
||||
kingsun->qos.min_turn_time.bits &= KINGSUN_MTT;
|
||||
irda_qos_bits_to_value(&kingsun->qos);
|
||||
|
||||
/* Override the network functions we need to use */
|
||||
net->netdev_ops = &ks959_ops;
|
||||
|
||||
ret = register_netdev(net);
|
||||
if (ret != 0)
|
||||
goto free_mem;
|
||||
|
||||
dev_info(&net->dev, "IrDA: Registered KingSun KS-959 device %s\n",
|
||||
net->name);
|
||||
|
||||
usb_set_intfdata(intf, kingsun);
|
||||
|
||||
/* Situation at this point:
|
||||
- all work buffers allocated
|
||||
- setup requests pre-filled
|
||||
- urbs not allocated, set to NULL
|
||||
- max rx packet known (is KINGSUN_FIFO_SIZE)
|
||||
- unwrap state machine (partially) initialized, but skb == NULL
|
||||
*/
|
||||
|
||||
return 0;
|
||||
|
||||
free_mem:
|
||||
kfree(kingsun->speed_setuprequest);
|
||||
kfree(kingsun->tx_setuprequest);
|
||||
kfree(kingsun->tx_buf_xored);
|
||||
kfree(kingsun->tx_buf_clear);
|
||||
kfree(kingsun->rx_setuprequest);
|
||||
kfree(kingsun->rx_buf);
|
||||
free_netdev(net);
|
||||
err_out1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The current device is removed, the USB layer tell us to shut it down...
|
||||
*/
|
||||
static void ks959_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct ks959_cb *kingsun = usb_get_intfdata(intf);
|
||||
|
||||
if (!kingsun)
|
||||
return;
|
||||
|
||||
unregister_netdev(kingsun->netdev);
|
||||
|
||||
/* Mop up receive && transmit urb's */
|
||||
if (kingsun->speed_urb != NULL) {
|
||||
usb_kill_urb(kingsun->speed_urb);
|
||||
usb_free_urb(kingsun->speed_urb);
|
||||
kingsun->speed_urb = NULL;
|
||||
}
|
||||
if (kingsun->tx_urb != NULL) {
|
||||
usb_kill_urb(kingsun->tx_urb);
|
||||
usb_free_urb(kingsun->tx_urb);
|
||||
kingsun->tx_urb = NULL;
|
||||
}
|
||||
if (kingsun->rx_urb != NULL) {
|
||||
usb_kill_urb(kingsun->rx_urb);
|
||||
usb_free_urb(kingsun->rx_urb);
|
||||
kingsun->rx_urb = NULL;
|
||||
}
|
||||
|
||||
kfree(kingsun->speed_setuprequest);
|
||||
kfree(kingsun->tx_setuprequest);
|
||||
kfree(kingsun->tx_buf_xored);
|
||||
kfree(kingsun->tx_buf_clear);
|
||||
kfree(kingsun->rx_setuprequest);
|
||||
kfree(kingsun->rx_buf);
|
||||
free_netdev(kingsun->netdev);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/* USB suspend, so power off the transmitter/receiver */
|
||||
static int ks959_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct ks959_cb *kingsun = usb_get_intfdata(intf);
|
||||
|
||||
netif_device_detach(kingsun->netdev);
|
||||
if (kingsun->speed_urb != NULL)
|
||||
usb_kill_urb(kingsun->speed_urb);
|
||||
if (kingsun->tx_urb != NULL)
|
||||
usb_kill_urb(kingsun->tx_urb);
|
||||
if (kingsun->rx_urb != NULL)
|
||||
usb_kill_urb(kingsun->rx_urb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Coming out of suspend, so reset hardware */
|
||||
static int ks959_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct ks959_cb *kingsun = usb_get_intfdata(intf);
|
||||
|
||||
if (kingsun->rx_urb != NULL) {
|
||||
/* Setup request already filled in ks959_probe */
|
||||
usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
|
||||
}
|
||||
netif_device_attach(kingsun->netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* USB device callbacks
|
||||
*/
|
||||
static struct usb_driver irda_driver = {
|
||||
.name = "ks959-sir",
|
||||
.probe = ks959_probe,
|
||||
.disconnect = ks959_disconnect,
|
||||
.id_table = dongles,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = ks959_suspend,
|
||||
.resume = ks959_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
module_usb_driver(irda_driver);
|
||||
|
||||
MODULE_AUTHOR("Alex Villacís Lasso <a_villacis@palosanto.com>");
|
||||
MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun KS-959");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,813 +0,0 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* Filename: ksdazzle.c
|
||||
* Version: 0.1.2
|
||||
* Description: Irda KingSun Dazzle USB Dongle
|
||||
* Status: Experimental
|
||||
* Author: Alex Villacís Lasso <a_villacis@palosanto.com>
|
||||
*
|
||||
* Based on stir4200, mcs7780, kingsun-sir drivers.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* Following is my most current (2007-07-26) understanding of how the Kingsun
|
||||
* 07D0:4100 dongle (sometimes known as the MA-660) is supposed to work. This
|
||||
* information was deduced by examining the USB traffic captured with USBSnoopy
|
||||
* from the WinXP driver. Feel free to update here as more of the dongle is
|
||||
* known.
|
||||
*
|
||||
* General: This dongle exposes one interface with two interrupt endpoints, one
|
||||
* IN and one OUT. In this regard, it is similar to what the Kingsun/Donshine
|
||||
* dongle (07c0:4200) exposes. Traffic is raw and needs to be wrapped and
|
||||
* unwrapped manually as in stir4200, kingsun-sir, and ks959-sir.
|
||||
*
|
||||
* Transmission: To transmit an IrDA frame, it is necessary to wrap it, then
|
||||
* split it into multiple segments of up to 7 bytes each, and transmit each in
|
||||
* sequence. It seems that sending a single big block (like kingsun-sir does)
|
||||
* won't work with this dongle. Each segment needs to be prefixed with a value
|
||||
* equal to (unsigned char)0xF8 + <number of bytes in segment>, inside a payload
|
||||
* of exactly 8 bytes. For example, a segment of 1 byte gets prefixed by 0xF9,
|
||||
* and one of 7 bytes gets prefixed by 0xFF. The bytes at the end of the
|
||||
* payload, not considered by the prefix, are ignored (set to 0 by this
|
||||
* implementation).
|
||||
*
|
||||
* Reception: To receive data, the driver must poll the dongle regularly (like
|
||||
* kingsun-sir.c) with interrupt URBs. If data is available, it will be returned
|
||||
* in payloads from 0 to 8 bytes long. When concatenated, these payloads form
|
||||
* a raw IrDA stream that needs to be unwrapped as in stir4200 and kingsun-sir
|
||||
*
|
||||
* Speed change: To change the speed of the dongle, the driver prepares a
|
||||
* control URB with the following as a setup packet:
|
||||
* bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE
|
||||
* bRequest 0x09
|
||||
* wValue 0x0200
|
||||
* wIndex 0x0001
|
||||
* wLength 0x0008 (length of the payload)
|
||||
* The payload is a 8-byte record, apparently identical to the one used in
|
||||
* drivers/usb/serial/cypress_m8.c to change speed:
|
||||
* __u32 baudSpeed;
|
||||
* unsigned int dataBits : 2; // 0 - 5 bits 3 - 8 bits
|
||||
* unsigned int : 1;
|
||||
* unsigned int stopBits : 1;
|
||||
* unsigned int parityEnable : 1;
|
||||
* unsigned int parityType : 1;
|
||||
* unsigned int : 1;
|
||||
* unsigned int reset : 1;
|
||||
* unsigned char reserved[3]; // set to 0
|
||||
*
|
||||
* For now only SIR speeds have been observed with this dongle. Therefore,
|
||||
* nothing is known on what changes (if any) must be done to frame wrapping /
|
||||
* unwrapping for higher than SIR speeds. This driver assumes no change is
|
||||
* necessary and announces support for all the way to 115200 bps.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/wrapper.h>
|
||||
#include <net/irda/crc.h>
|
||||
|
||||
#define KSDAZZLE_VENDOR_ID 0x07d0
|
||||
#define KSDAZZLE_PRODUCT_ID 0x4100
|
||||
|
||||
/* These are the currently known USB ids */
|
||||
static const struct usb_device_id dongles[] = {
|
||||
/* KingSun Co,Ltd IrDA/USB Bridge */
|
||||
{USB_DEVICE(KSDAZZLE_VENDOR_ID, KSDAZZLE_PRODUCT_ID)},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, dongles);
|
||||
|
||||
#define KINGSUN_MTT 0x07
|
||||
#define KINGSUN_REQ_RECV 0x01
|
||||
#define KINGSUN_REQ_SEND 0x09
|
||||
|
||||
#define KINGSUN_SND_FIFO_SIZE 2048 /* Max packet we can send */
|
||||
#define KINGSUN_RCV_MAX 2048 /* Max transfer we can receive */
|
||||
|
||||
struct ksdazzle_speedparams {
|
||||
__le32 baudrate; /* baud rate, little endian */
|
||||
__u8 flags;
|
||||
__u8 reserved[3];
|
||||
} __packed;
|
||||
|
||||
#define KS_DATA_5_BITS 0x00
|
||||
#define KS_DATA_6_BITS 0x01
|
||||
#define KS_DATA_7_BITS 0x02
|
||||
#define KS_DATA_8_BITS 0x03
|
||||
|
||||
#define KS_STOP_BITS_1 0x00
|
||||
#define KS_STOP_BITS_2 0x08
|
||||
|
||||
#define KS_PAR_DISABLE 0x00
|
||||
#define KS_PAR_EVEN 0x10
|
||||
#define KS_PAR_ODD 0x30
|
||||
#define KS_RESET 0x80
|
||||
|
||||
#define KINGSUN_EP_IN 0
|
||||
#define KINGSUN_EP_OUT 1
|
||||
|
||||
struct ksdazzle_cb {
|
||||
struct usb_device *usbdev; /* init: probe_irda */
|
||||
struct net_device *netdev; /* network layer */
|
||||
struct irlap_cb *irlap; /* The link layer we are binded to */
|
||||
|
||||
struct qos_info qos;
|
||||
|
||||
struct urb *tx_urb;
|
||||
__u8 *tx_buf_clear;
|
||||
unsigned int tx_buf_clear_used;
|
||||
unsigned int tx_buf_clear_sent;
|
||||
__u8 tx_payload[8];
|
||||
|
||||
struct urb *rx_urb;
|
||||
__u8 *rx_buf;
|
||||
iobuff_t rx_unwrap_buff;
|
||||
|
||||
struct usb_ctrlrequest *speed_setuprequest;
|
||||
struct urb *speed_urb;
|
||||
struct ksdazzle_speedparams speedparams;
|
||||
unsigned int new_speed;
|
||||
|
||||
__u8 ep_in;
|
||||
__u8 ep_out;
|
||||
|
||||
spinlock_t lock;
|
||||
int receiving;
|
||||
};
|
||||
|
||||
/* Callback transmission routine */
|
||||
static void ksdazzle_speed_irq(struct urb *urb)
|
||||
{
|
||||
/* unlink, shutdown, unplug, other nasties */
|
||||
if (urb->status != 0)
|
||||
dev_err(&urb->dev->dev,
|
||||
"ksdazzle_speed_irq: urb asynchronously failed - %d\n",
|
||||
urb->status);
|
||||
}
|
||||
|
||||
/* Send a control request to change speed of the dongle */
|
||||
static int ksdazzle_change_speed(struct ksdazzle_cb *kingsun, unsigned speed)
|
||||
{
|
||||
static unsigned int supported_speeds[] = { 2400, 9600, 19200, 38400,
|
||||
57600, 115200, 576000, 1152000, 4000000, 0
|
||||
};
|
||||
int err;
|
||||
unsigned int i;
|
||||
|
||||
if (kingsun->speed_setuprequest == NULL || kingsun->speed_urb == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Check that requested speed is among the supported ones */
|
||||
for (i = 0; supported_speeds[i] && supported_speeds[i] != speed; i++) ;
|
||||
if (supported_speeds[i] == 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
memset(&(kingsun->speedparams), 0, sizeof(struct ksdazzle_speedparams));
|
||||
kingsun->speedparams.baudrate = cpu_to_le32(speed);
|
||||
kingsun->speedparams.flags = KS_DATA_8_BITS;
|
||||
|
||||
/* speed_setuprequest pre-filled in ksdazzle_probe */
|
||||
usb_fill_control_urb(kingsun->speed_urb, kingsun->usbdev,
|
||||
usb_sndctrlpipe(kingsun->usbdev, 0),
|
||||
(unsigned char *)kingsun->speed_setuprequest,
|
||||
&(kingsun->speedparams),
|
||||
sizeof(struct ksdazzle_speedparams),
|
||||
ksdazzle_speed_irq, kingsun);
|
||||
kingsun->speed_urb->status = 0;
|
||||
err = usb_submit_urb(kingsun->speed_urb, GFP_ATOMIC);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Submit one fragment of an IrDA frame to the dongle */
|
||||
static void ksdazzle_send_irq(struct urb *urb);
|
||||
static int ksdazzle_submit_tx_fragment(struct ksdazzle_cb *kingsun)
|
||||
{
|
||||
unsigned int wraplen;
|
||||
int ret;
|
||||
|
||||
/* We can send at most 7 bytes of payload at a time */
|
||||
wraplen = 7;
|
||||
if (wraplen > kingsun->tx_buf_clear_used)
|
||||
wraplen = kingsun->tx_buf_clear_used;
|
||||
|
||||
/* Prepare payload prefix with used length */
|
||||
memset(kingsun->tx_payload, 0, 8);
|
||||
kingsun->tx_payload[0] = (unsigned char)0xf8 + wraplen;
|
||||
memcpy(kingsun->tx_payload + 1, kingsun->tx_buf_clear, wraplen);
|
||||
|
||||
usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev,
|
||||
usb_sndintpipe(kingsun->usbdev, kingsun->ep_out),
|
||||
kingsun->tx_payload, 8, ksdazzle_send_irq, kingsun, 1);
|
||||
kingsun->tx_urb->status = 0;
|
||||
ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC);
|
||||
|
||||
/* Remember how much data was sent, in order to update at callback */
|
||||
kingsun->tx_buf_clear_sent = (ret == 0) ? wraplen : 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Callback transmission routine */
|
||||
static void ksdazzle_send_irq(struct urb *urb)
|
||||
{
|
||||
struct ksdazzle_cb *kingsun = urb->context;
|
||||
struct net_device *netdev = kingsun->netdev;
|
||||
int ret = 0;
|
||||
|
||||
/* in process of stopping, just drop data */
|
||||
if (!netif_running(kingsun->netdev)) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"ksdazzle_send_irq: Network not running!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* unlink, shutdown, unplug, other nasties */
|
||||
if (urb->status != 0) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"ksdazzle_send_irq: urb asynchronously failed - %d\n",
|
||||
urb->status);
|
||||
return;
|
||||
}
|
||||
|
||||
if (kingsun->tx_buf_clear_used > 0) {
|
||||
/* Update data remaining to be sent */
|
||||
if (kingsun->tx_buf_clear_sent < kingsun->tx_buf_clear_used) {
|
||||
memmove(kingsun->tx_buf_clear,
|
||||
kingsun->tx_buf_clear +
|
||||
kingsun->tx_buf_clear_sent,
|
||||
kingsun->tx_buf_clear_used -
|
||||
kingsun->tx_buf_clear_sent);
|
||||
}
|
||||
kingsun->tx_buf_clear_used -= kingsun->tx_buf_clear_sent;
|
||||
kingsun->tx_buf_clear_sent = 0;
|
||||
|
||||
if (kingsun->tx_buf_clear_used > 0) {
|
||||
/* There is more data to be sent */
|
||||
if ((ret = ksdazzle_submit_tx_fragment(kingsun)) != 0) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"ksdazzle_send_irq: failed tx_urb submit: %d\n",
|
||||
ret);
|
||||
switch (ret) {
|
||||
case -ENODEV:
|
||||
case -EPIPE:
|
||||
break;
|
||||
default:
|
||||
netdev->stats.tx_errors++;
|
||||
netif_start_queue(netdev);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* All data sent, send next speed && wake network queue */
|
||||
if (kingsun->new_speed != -1 &&
|
||||
cpu_to_le32(kingsun->new_speed) !=
|
||||
kingsun->speedparams.baudrate)
|
||||
ksdazzle_change_speed(kingsun,
|
||||
kingsun->new_speed);
|
||||
|
||||
netif_wake_queue(netdev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from net/core when new frame is available.
|
||||
*/
|
||||
static netdev_tx_t ksdazzle_hard_xmit(struct sk_buff *skb,
|
||||
struct net_device *netdev)
|
||||
{
|
||||
struct ksdazzle_cb *kingsun;
|
||||
unsigned int wraplen;
|
||||
int ret = 0;
|
||||
|
||||
netif_stop_queue(netdev);
|
||||
|
||||
/* the IRDA wrapping routines don't deal with non linear skb */
|
||||
SKB_LINEAR_ASSERT(skb);
|
||||
|
||||
kingsun = netdev_priv(netdev);
|
||||
|
||||
spin_lock(&kingsun->lock);
|
||||
kingsun->new_speed = irda_get_next_speed(skb);
|
||||
|
||||
/* Append data to the end of whatever data remains to be transmitted */
|
||||
wraplen =
|
||||
async_wrap_skb(skb, kingsun->tx_buf_clear, KINGSUN_SND_FIFO_SIZE);
|
||||
kingsun->tx_buf_clear_used = wraplen;
|
||||
|
||||
if ((ret = ksdazzle_submit_tx_fragment(kingsun)) != 0) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"ksdazzle_hard_xmit: failed tx_urb submit: %d\n", ret);
|
||||
switch (ret) {
|
||||
case -ENODEV:
|
||||
case -EPIPE:
|
||||
break;
|
||||
default:
|
||||
netdev->stats.tx_errors++;
|
||||
netif_start_queue(netdev);
|
||||
}
|
||||
} else {
|
||||
netdev->stats.tx_packets++;
|
||||
netdev->stats.tx_bytes += skb->len;
|
||||
|
||||
}
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
spin_unlock(&kingsun->lock);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* Receive callback function */
|
||||
static void ksdazzle_rcv_irq(struct urb *urb)
|
||||
{
|
||||
struct ksdazzle_cb *kingsun = urb->context;
|
||||
struct net_device *netdev = kingsun->netdev;
|
||||
|
||||
/* in process of stopping, just drop data */
|
||||
if (!netif_running(netdev)) {
|
||||
kingsun->receiving = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* unlink, shutdown, unplug, other nasties */
|
||||
if (urb->status != 0) {
|
||||
dev_err(&kingsun->usbdev->dev,
|
||||
"ksdazzle_rcv_irq: urb asynchronously failed - %d\n",
|
||||
urb->status);
|
||||
kingsun->receiving = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (urb->actual_length > 0) {
|
||||
__u8 *bytes = urb->transfer_buffer;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < urb->actual_length; i++) {
|
||||
async_unwrap_char(netdev, &netdev->stats,
|
||||
&kingsun->rx_unwrap_buff, bytes[i]);
|
||||
}
|
||||
kingsun->receiving =
|
||||
(kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* This urb has already been filled in ksdazzle_net_open. It is assumed that
|
||||
urb keeps the pointer to the payload buffer.
|
||||
*/
|
||||
urb->status = 0;
|
||||
usb_submit_urb(urb, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ksdazzle_net_open (dev)
|
||||
*
|
||||
* Network device is taken up. Usually this is done by "ifconfig irda0 up"
|
||||
*/
|
||||
static int ksdazzle_net_open(struct net_device *netdev)
|
||||
{
|
||||
struct ksdazzle_cb *kingsun = netdev_priv(netdev);
|
||||
int err = -ENOMEM;
|
||||
char hwname[16];
|
||||
|
||||
/* At this point, urbs are NULL, and skb is NULL (see ksdazzle_probe) */
|
||||
kingsun->receiving = 0;
|
||||
|
||||
/* Initialize for SIR to copy data directly into skb. */
|
||||
kingsun->rx_unwrap_buff.in_frame = FALSE;
|
||||
kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
|
||||
kingsun->rx_unwrap_buff.truesize = IRDA_SKB_MAX_MTU;
|
||||
kingsun->rx_unwrap_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
|
||||
if (!kingsun->rx_unwrap_buff.skb)
|
||||
goto free_mem;
|
||||
|
||||
skb_reserve(kingsun->rx_unwrap_buff.skb, 1);
|
||||
kingsun->rx_unwrap_buff.head = kingsun->rx_unwrap_buff.skb->data;
|
||||
|
||||
kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!kingsun->rx_urb)
|
||||
goto free_mem;
|
||||
|
||||
kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!kingsun->tx_urb)
|
||||
goto free_mem;
|
||||
|
||||
kingsun->speed_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!kingsun->speed_urb)
|
||||
goto free_mem;
|
||||
|
||||
/* Initialize speed for dongle */
|
||||
kingsun->new_speed = 9600;
|
||||
err = ksdazzle_change_speed(kingsun, 9600);
|
||||
if (err < 0)
|
||||
goto free_mem;
|
||||
|
||||
/*
|
||||
* Now that everything should be initialized properly,
|
||||
* Open new IrLAP layer instance to take care of us...
|
||||
*/
|
||||
sprintf(hwname, "usb#%d", kingsun->usbdev->devnum);
|
||||
kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname);
|
||||
if (!kingsun->irlap) {
|
||||
err = -ENOMEM;
|
||||
dev_err(&kingsun->usbdev->dev, "irlap_open failed\n");
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
/* Start reception. */
|
||||
usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev,
|
||||
usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in),
|
||||
kingsun->rx_buf, KINGSUN_RCV_MAX, ksdazzle_rcv_irq,
|
||||
kingsun, 1);
|
||||
kingsun->rx_urb->status = 0;
|
||||
err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
|
||||
if (err) {
|
||||
dev_err(&kingsun->usbdev->dev, "first urb-submit failed: %d\n", err);
|
||||
goto close_irlap;
|
||||
}
|
||||
|
||||
netif_start_queue(netdev);
|
||||
|
||||
/* Situation at this point:
|
||||
- all work buffers allocated
|
||||
- urbs allocated and ready to fill
|
||||
- max rx packet known (in max_rx)
|
||||
- unwrap state machine initialized, in state outside of any frame
|
||||
- receive request in progress
|
||||
- IrLAP layer started, about to hand over packets to send
|
||||
*/
|
||||
|
||||
return 0;
|
||||
|
||||
close_irlap:
|
||||
irlap_close(kingsun->irlap);
|
||||
free_mem:
|
||||
usb_free_urb(kingsun->speed_urb);
|
||||
kingsun->speed_urb = NULL;
|
||||
usb_free_urb(kingsun->tx_urb);
|
||||
kingsun->tx_urb = NULL;
|
||||
usb_free_urb(kingsun->rx_urb);
|
||||
kingsun->rx_urb = NULL;
|
||||
if (kingsun->rx_unwrap_buff.skb) {
|
||||
kfree_skb(kingsun->rx_unwrap_buff.skb);
|
||||
kingsun->rx_unwrap_buff.skb = NULL;
|
||||
kingsun->rx_unwrap_buff.head = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ksdazzle_net_close (dev)
|
||||
*
|
||||
* Network device is taken down. Usually this is done by
|
||||
* "ifconfig irda0 down"
|
||||
*/
|
||||
static int ksdazzle_net_close(struct net_device *netdev)
|
||||
{
|
||||
struct ksdazzle_cb *kingsun = netdev_priv(netdev);
|
||||
|
||||
/* Stop transmit processing */
|
||||
netif_stop_queue(netdev);
|
||||
|
||||
/* Mop up receive && transmit urb's */
|
||||
usb_kill_urb(kingsun->tx_urb);
|
||||
usb_free_urb(kingsun->tx_urb);
|
||||
kingsun->tx_urb = NULL;
|
||||
|
||||
usb_kill_urb(kingsun->speed_urb);
|
||||
usb_free_urb(kingsun->speed_urb);
|
||||
kingsun->speed_urb = NULL;
|
||||
|
||||
usb_kill_urb(kingsun->rx_urb);
|
||||
usb_free_urb(kingsun->rx_urb);
|
||||
kingsun->rx_urb = NULL;
|
||||
|
||||
kfree_skb(kingsun->rx_unwrap_buff.skb);
|
||||
kingsun->rx_unwrap_buff.skb = NULL;
|
||||
kingsun->rx_unwrap_buff.head = NULL;
|
||||
kingsun->rx_unwrap_buff.in_frame = FALSE;
|
||||
kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
|
||||
kingsun->receiving = 0;
|
||||
|
||||
/* Stop and remove instance of IrLAP */
|
||||
irlap_close(kingsun->irlap);
|
||||
|
||||
kingsun->irlap = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* IOCTLs : Extra out-of-band network commands...
|
||||
*/
|
||||
static int ksdazzle_net_ioctl(struct net_device *netdev, struct ifreq *rq,
|
||||
int cmd)
|
||||
{
|
||||
struct if_irda_req *irq = (struct if_irda_req *)rq;
|
||||
struct ksdazzle_cb *kingsun = netdev_priv(netdev);
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SIOCSBANDWIDTH: /* Set bandwidth */
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/* Check if the device is still there */
|
||||
if (netif_device_present(kingsun->netdev))
|
||||
return ksdazzle_change_speed(kingsun,
|
||||
irq->ifr_baudrate);
|
||||
break;
|
||||
|
||||
case SIOCSMEDIABUSY: /* Set media busy */
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/* Check if the IrDA stack is still there */
|
||||
if (netif_running(kingsun->netdev))
|
||||
irda_device_set_media_busy(kingsun->netdev, TRUE);
|
||||
break;
|
||||
|
||||
case SIOCGRECEIVING:
|
||||
/* Only approximately true */
|
||||
irq->ifr_receiving = kingsun->receiving;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct net_device_ops ksdazzle_ops = {
|
||||
.ndo_start_xmit = ksdazzle_hard_xmit,
|
||||
.ndo_open = ksdazzle_net_open,
|
||||
.ndo_stop = ksdazzle_net_close,
|
||||
.ndo_do_ioctl = ksdazzle_net_ioctl,
|
||||
};
|
||||
|
||||
/*
|
||||
* This routine is called by the USB subsystem for each new device
|
||||
* in the system. We need to check if the device is ours, and in
|
||||
* this case start handling it.
|
||||
*/
|
||||
static int ksdazzle_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct ksdazzle_cb *kingsun = NULL;
|
||||
struct net_device *net = NULL;
|
||||
int ret = -ENOMEM;
|
||||
int pipe, maxp_in, maxp_out;
|
||||
__u8 ep_in;
|
||||
__u8 ep_out;
|
||||
|
||||
/* Check that there really are two interrupt endpoints. Check based on the
|
||||
one in drivers/usb/input/usbmouse.c
|
||||
*/
|
||||
interface = intf->cur_altsetting;
|
||||
if (interface->desc.bNumEndpoints != 2) {
|
||||
dev_err(&intf->dev, "ksdazzle: expected 2 endpoints, found %d\n",
|
||||
interface->desc.bNumEndpoints);
|
||||
return -ENODEV;
|
||||
}
|
||||
endpoint = &interface->endpoint[KINGSUN_EP_IN].desc;
|
||||
if (!usb_endpoint_is_int_in(endpoint)) {
|
||||
dev_err(&intf->dev,
|
||||
"ksdazzle: endpoint 0 is not interrupt IN\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ep_in = endpoint->bEndpointAddress;
|
||||
pipe = usb_rcvintpipe(dev, ep_in);
|
||||
maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
|
||||
if (maxp_in > 255 || maxp_in <= 1) {
|
||||
dev_err(&intf->dev,
|
||||
"ksdazzle: endpoint 0 has max packet size %d not in range [2..255]\n",
|
||||
maxp_in);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc;
|
||||
if (!usb_endpoint_is_int_out(endpoint)) {
|
||||
dev_err(&intf->dev,
|
||||
"ksdazzle: endpoint 1 is not interrupt OUT\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ep_out = endpoint->bEndpointAddress;
|
||||
pipe = usb_sndintpipe(dev, ep_out);
|
||||
maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
|
||||
|
||||
/* Allocate network device container. */
|
||||
net = alloc_irdadev(sizeof(*kingsun));
|
||||
if (!net)
|
||||
goto err_out1;
|
||||
|
||||
SET_NETDEV_DEV(net, &intf->dev);
|
||||
kingsun = netdev_priv(net);
|
||||
kingsun->netdev = net;
|
||||
kingsun->usbdev = dev;
|
||||
kingsun->ep_in = ep_in;
|
||||
kingsun->ep_out = ep_out;
|
||||
kingsun->irlap = NULL;
|
||||
kingsun->tx_urb = NULL;
|
||||
kingsun->tx_buf_clear = NULL;
|
||||
kingsun->tx_buf_clear_used = 0;
|
||||
kingsun->tx_buf_clear_sent = 0;
|
||||
|
||||
kingsun->rx_urb = NULL;
|
||||
kingsun->rx_buf = NULL;
|
||||
kingsun->rx_unwrap_buff.in_frame = FALSE;
|
||||
kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
|
||||
kingsun->rx_unwrap_buff.skb = NULL;
|
||||
kingsun->receiving = 0;
|
||||
spin_lock_init(&kingsun->lock);
|
||||
|
||||
kingsun->speed_setuprequest = NULL;
|
||||
kingsun->speed_urb = NULL;
|
||||
kingsun->speedparams.baudrate = 0;
|
||||
|
||||
/* Allocate input buffer */
|
||||
kingsun->rx_buf = kmalloc(KINGSUN_RCV_MAX, GFP_KERNEL);
|
||||
if (!kingsun->rx_buf)
|
||||
goto free_mem;
|
||||
|
||||
/* Allocate output buffer */
|
||||
kingsun->tx_buf_clear = kmalloc(KINGSUN_SND_FIFO_SIZE, GFP_KERNEL);
|
||||
if (!kingsun->tx_buf_clear)
|
||||
goto free_mem;
|
||||
|
||||
/* Allocate and initialize speed setup packet */
|
||||
kingsun->speed_setuprequest =
|
||||
kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
|
||||
if (!kingsun->speed_setuprequest)
|
||||
goto free_mem;
|
||||
kingsun->speed_setuprequest->bRequestType =
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
|
||||
kingsun->speed_setuprequest->bRequest = KINGSUN_REQ_SEND;
|
||||
kingsun->speed_setuprequest->wValue = cpu_to_le16(0x0200);
|
||||
kingsun->speed_setuprequest->wIndex = cpu_to_le16(0x0001);
|
||||
kingsun->speed_setuprequest->wLength =
|
||||
cpu_to_le16(sizeof(struct ksdazzle_speedparams));
|
||||
|
||||
printk(KERN_INFO "KingSun/Dazzle IRDA/USB found at address %d, "
|
||||
"Vendor: %x, Product: %x\n",
|
||||
dev->devnum, le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
|
||||
/* Initialize QoS for this device */
|
||||
irda_init_max_qos_capabilies(&kingsun->qos);
|
||||
|
||||
/* Baud rates known to be supported. Please uncomment if devices (other
|
||||
than a SonyEriccson K300 phone) can be shown to support higher speeds
|
||||
with this dongle.
|
||||
*/
|
||||
kingsun->qos.baud_rate.bits =
|
||||
IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200;
|
||||
kingsun->qos.min_turn_time.bits &= KINGSUN_MTT;
|
||||
irda_qos_bits_to_value(&kingsun->qos);
|
||||
|
||||
/* Override the network functions we need to use */
|
||||
net->netdev_ops = &ksdazzle_ops;
|
||||
|
||||
ret = register_netdev(net);
|
||||
if (ret != 0)
|
||||
goto free_mem;
|
||||
|
||||
dev_info(&net->dev, "IrDA: Registered KingSun/Dazzle device %s\n",
|
||||
net->name);
|
||||
|
||||
usb_set_intfdata(intf, kingsun);
|
||||
|
||||
/* Situation at this point:
|
||||
- all work buffers allocated
|
||||
- setup requests pre-filled
|
||||
- urbs not allocated, set to NULL
|
||||
- max rx packet known (is KINGSUN_FIFO_SIZE)
|
||||
- unwrap state machine (partially) initialized, but skb == NULL
|
||||
*/
|
||||
|
||||
return 0;
|
||||
|
||||
free_mem:
|
||||
kfree(kingsun->speed_setuprequest);
|
||||
kfree(kingsun->tx_buf_clear);
|
||||
kfree(kingsun->rx_buf);
|
||||
free_netdev(net);
|
||||
err_out1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The current device is removed, the USB layer tell us to shut it down...
|
||||
*/
|
||||
static void ksdazzle_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct ksdazzle_cb *kingsun = usb_get_intfdata(intf);
|
||||
|
||||
if (!kingsun)
|
||||
return;
|
||||
|
||||
unregister_netdev(kingsun->netdev);
|
||||
|
||||
/* Mop up receive && transmit urb's */
|
||||
usb_kill_urb(kingsun->speed_urb);
|
||||
usb_free_urb(kingsun->speed_urb);
|
||||
kingsun->speed_urb = NULL;
|
||||
|
||||
usb_kill_urb(kingsun->tx_urb);
|
||||
usb_free_urb(kingsun->tx_urb);
|
||||
kingsun->tx_urb = NULL;
|
||||
|
||||
usb_kill_urb(kingsun->rx_urb);
|
||||
usb_free_urb(kingsun->rx_urb);
|
||||
kingsun->rx_urb = NULL;
|
||||
|
||||
kfree(kingsun->speed_setuprequest);
|
||||
kfree(kingsun->tx_buf_clear);
|
||||
kfree(kingsun->rx_buf);
|
||||
free_netdev(kingsun->netdev);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/* USB suspend, so power off the transmitter/receiver */
|
||||
static int ksdazzle_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct ksdazzle_cb *kingsun = usb_get_intfdata(intf);
|
||||
|
||||
netif_device_detach(kingsun->netdev);
|
||||
if (kingsun->speed_urb != NULL)
|
||||
usb_kill_urb(kingsun->speed_urb);
|
||||
if (kingsun->tx_urb != NULL)
|
||||
usb_kill_urb(kingsun->tx_urb);
|
||||
if (kingsun->rx_urb != NULL)
|
||||
usb_kill_urb(kingsun->rx_urb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Coming out of suspend, so reset hardware */
|
||||
static int ksdazzle_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct ksdazzle_cb *kingsun = usb_get_intfdata(intf);
|
||||
|
||||
if (kingsun->rx_urb != NULL) {
|
||||
/* Setup request already filled in ksdazzle_probe */
|
||||
usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
|
||||
}
|
||||
netif_device_attach(kingsun->netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* USB device callbacks
|
||||
*/
|
||||
static struct usb_driver irda_driver = {
|
||||
.name = "ksdazzle-sir",
|
||||
.probe = ksdazzle_probe,
|
||||
.disconnect = ksdazzle_disconnect,
|
||||
.id_table = dongles,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = ksdazzle_suspend,
|
||||
.resume = ksdazzle_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
module_usb_driver(irda_driver);
|
||||
|
||||
MODULE_AUTHOR("Alex Villacís Lasso <a_villacis@palosanto.com>");
|
||||
MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun Dazzle");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,199 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: litelink.c
|
||||
* Version: 1.1
|
||||
* Description: Driver for the Parallax LiteLink dongle
|
||||
* Status: Stable
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Fri May 7 12:50:33 1999
|
||||
* Modified at: Fri Dec 17 09:14:23 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
/*
|
||||
* Modified at: Thu Jan 15 2003
|
||||
* Modified by: Eugene Crosser <crosser@average.org>
|
||||
*
|
||||
* Convert to "new" IRDA infrastructure for kernel 2.6
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
|
||||
#include "sir-dev.h"
|
||||
|
||||
#define MIN_DELAY 25 /* 15 us, but wait a little more to be sure */
|
||||
#define MAX_DELAY 10000 /* 1 ms */
|
||||
|
||||
static int litelink_open(struct sir_dev *dev);
|
||||
static int litelink_close(struct sir_dev *dev);
|
||||
static int litelink_change_speed(struct sir_dev *dev, unsigned speed);
|
||||
static int litelink_reset(struct sir_dev *dev);
|
||||
|
||||
/* These are the baudrates supported - 9600 must be last one! */
|
||||
static unsigned baud_rates[] = { 115200, 57600, 38400, 19200, 9600 };
|
||||
|
||||
static struct dongle_driver litelink = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "Parallax LiteLink",
|
||||
.type = IRDA_LITELINK_DONGLE,
|
||||
.open = litelink_open,
|
||||
.close = litelink_close,
|
||||
.reset = litelink_reset,
|
||||
.set_speed = litelink_change_speed,
|
||||
};
|
||||
|
||||
static int __init litelink_sir_init(void)
|
||||
{
|
||||
return irda_register_dongle(&litelink);
|
||||
}
|
||||
|
||||
static void __exit litelink_sir_cleanup(void)
|
||||
{
|
||||
irda_unregister_dongle(&litelink);
|
||||
}
|
||||
|
||||
static int litelink_open(struct sir_dev *dev)
|
||||
{
|
||||
struct qos_info *qos = &dev->qos;
|
||||
|
||||
/* Power up dongle */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
/* Set the speeds we can accept */
|
||||
qos->baud_rate.bits &= IR_115200|IR_57600|IR_38400|IR_19200|IR_9600;
|
||||
qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */
|
||||
irda_qos_bits_to_value(qos);
|
||||
|
||||
/* irda thread waits 50 msec for power settling */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int litelink_close(struct sir_dev *dev)
|
||||
{
|
||||
/* Power off dongle */
|
||||
sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function litelink_change_speed (task)
|
||||
*
|
||||
* Change speed of the Litelink dongle. To cycle through the available
|
||||
* baud rates, pulse RTS low for a few ms.
|
||||
*/
|
||||
static int litelink_change_speed(struct sir_dev *dev, unsigned speed)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* dongle already reset by irda-thread - current speed (dongle and
|
||||
* port) is the default speed (115200 for litelink!)
|
||||
*/
|
||||
|
||||
/* Cycle through avaiable baudrates until we reach the correct one */
|
||||
for (i = 0; baud_rates[i] != speed; i++) {
|
||||
|
||||
/* end-of-list reached due to invalid speed request */
|
||||
if (baud_rates[i] == 9600)
|
||||
break;
|
||||
|
||||
/* Set DTR, clear RTS */
|
||||
sirdev_set_dtr_rts(dev, FALSE, TRUE);
|
||||
|
||||
/* Sleep a minimum of 15 us */
|
||||
udelay(MIN_DELAY);
|
||||
|
||||
/* Set DTR, Set RTS */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
/* Sleep a minimum of 15 us */
|
||||
udelay(MIN_DELAY);
|
||||
}
|
||||
|
||||
dev->speed = baud_rates[i];
|
||||
|
||||
/* invalid baudrate should not happen - but if, we return -EINVAL and
|
||||
* the dongle configured for 9600 so the stack has a chance to recover
|
||||
*/
|
||||
|
||||
return (dev->speed == speed) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function litelink_reset (task)
|
||||
*
|
||||
* Reset the Litelink type dongle.
|
||||
*
|
||||
*/
|
||||
static int litelink_reset(struct sir_dev *dev)
|
||||
{
|
||||
/* probably the power-up can be dropped here, but with only
|
||||
* 15 usec delay it's not worth the risk unless somebody with
|
||||
* the hardware confirms it doesn't break anything...
|
||||
*/
|
||||
|
||||
/* Power on dongle */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
/* Sleep a minimum of 15 us */
|
||||
udelay(MIN_DELAY);
|
||||
|
||||
/* Clear RTS to reset dongle */
|
||||
sirdev_set_dtr_rts(dev, TRUE, FALSE);
|
||||
|
||||
/* Sleep a minimum of 15 us */
|
||||
udelay(MIN_DELAY);
|
||||
|
||||
/* Go back to normal mode */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
/* Sleep a minimum of 15 us */
|
||||
udelay(MIN_DELAY);
|
||||
|
||||
/* This dongles speed defaults to 115200 bps */
|
||||
dev->speed = 115200;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
|
||||
MODULE_DESCRIPTION("Parallax Litelink dongle driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("irda-dongle-5"); /* IRDA_LITELINK_DONGLE */
|
||||
|
||||
/*
|
||||
* Function init_module (void)
|
||||
*
|
||||
* Initialize Litelink module
|
||||
*
|
||||
*/
|
||||
module_init(litelink_sir_init);
|
||||
|
||||
/*
|
||||
* Function cleanup_module (void)
|
||||
*
|
||||
* Cleanup Litelink module
|
||||
*
|
||||
*/
|
||||
module_exit(litelink_sir_cleanup);
|
|
@ -1,253 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ma600.c
|
||||
* Version: 0.1
|
||||
* Description: Implementation of the MA600 dongle
|
||||
* Status: Experimental.
|
||||
* Author: Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95
|
||||
* Created at: Sat Jun 10 20:02:35 2000
|
||||
* Modified at: Sat Aug 16 09:34:13 2003
|
||||
* Modified by: Martin Diehl <mad@mdiehl.de> (modified for new sir_dev)
|
||||
*
|
||||
* Note: very thanks to Mr. Maru Wang <maru@mobileaction.com.tw> for providing
|
||||
* information on the MA600 dongle
|
||||
*
|
||||
* Copyright (c) 2000 Leung, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
|
||||
#include "sir-dev.h"
|
||||
|
||||
static int ma600_open(struct sir_dev *);
|
||||
static int ma600_close(struct sir_dev *);
|
||||
static int ma600_change_speed(struct sir_dev *, unsigned);
|
||||
static int ma600_reset(struct sir_dev *);
|
||||
|
||||
/* control byte for MA600 */
|
||||
#define MA600_9600 0x00
|
||||
#define MA600_19200 0x01
|
||||
#define MA600_38400 0x02
|
||||
#define MA600_57600 0x03
|
||||
#define MA600_115200 0x04
|
||||
#define MA600_DEV_ID1 0x05
|
||||
#define MA600_DEV_ID2 0x06
|
||||
#define MA600_2400 0x08
|
||||
|
||||
static struct dongle_driver ma600 = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "MA600",
|
||||
.type = IRDA_MA600_DONGLE,
|
||||
.open = ma600_open,
|
||||
.close = ma600_close,
|
||||
.reset = ma600_reset,
|
||||
.set_speed = ma600_change_speed,
|
||||
};
|
||||
|
||||
|
||||
static int __init ma600_sir_init(void)
|
||||
{
|
||||
return irda_register_dongle(&ma600);
|
||||
}
|
||||
|
||||
static void __exit ma600_sir_cleanup(void)
|
||||
{
|
||||
irda_unregister_dongle(&ma600);
|
||||
}
|
||||
|
||||
/*
|
||||
Power on:
|
||||
(0) Clear RTS and DTR for 1 second
|
||||
(1) Set RTS and DTR for 1 second
|
||||
(2) 9600 bps now
|
||||
Note: assume RTS, DTR are clear before
|
||||
*/
|
||||
static int ma600_open(struct sir_dev *dev)
|
||||
{
|
||||
struct qos_info *qos = &dev->qos;
|
||||
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
/* Explicitly set the speeds we can accept */
|
||||
qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400
|
||||
|IR_57600|IR_115200;
|
||||
/* Hm, 0x01 means 10ms - for >= 1ms we would need 0x07 */
|
||||
qos->min_turn_time.bits = 0x01; /* Needs at least 1 ms */
|
||||
irda_qos_bits_to_value(qos);
|
||||
|
||||
/* irda thread waits 50 msec for power settling */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ma600_close(struct sir_dev *dev)
|
||||
{
|
||||
/* Power off dongle */
|
||||
sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __u8 get_control_byte(__u32 speed)
|
||||
{
|
||||
__u8 byte;
|
||||
|
||||
switch (speed) {
|
||||
default:
|
||||
case 115200:
|
||||
byte = MA600_115200;
|
||||
break;
|
||||
case 57600:
|
||||
byte = MA600_57600;
|
||||
break;
|
||||
case 38400:
|
||||
byte = MA600_38400;
|
||||
break;
|
||||
case 19200:
|
||||
byte = MA600_19200;
|
||||
break;
|
||||
case 9600:
|
||||
byte = MA600_9600;
|
||||
break;
|
||||
case 2400:
|
||||
byte = MA600_2400;
|
||||
break;
|
||||
}
|
||||
|
||||
return byte;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ma600_change_speed (dev, speed)
|
||||
*
|
||||
* Set the speed for the MA600 type dongle.
|
||||
*
|
||||
* The dongle has already been reset to a known state (dongle default)
|
||||
* We cycle through speeds by pulsing RTS low and then high.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Function ma600_change_speed (dev, speed)
|
||||
*
|
||||
* Set the speed for the MA600 type dongle.
|
||||
*
|
||||
* Algorithm
|
||||
* 1. Reset (already done by irda thread state machine)
|
||||
* 2. clear RTS, set DTR and wait for 1ms
|
||||
* 3. send Control Byte to the MA600 through TXD to set new baud rate
|
||||
* wait until the stop bit of Control Byte is sent (for 9600 baud rate,
|
||||
* it takes about 10 msec)
|
||||
* 4. set RTS, set DTR (return to NORMAL Operation)
|
||||
* 5. wait at least 10 ms, new setting (baud rate, etc) takes effect here
|
||||
* after
|
||||
*/
|
||||
|
||||
/* total delays are only about 20ms - let's just sleep for now to
|
||||
* avoid the state machine complexity before we get things working
|
||||
*/
|
||||
|
||||
static int ma600_change_speed(struct sir_dev *dev, unsigned speed)
|
||||
{
|
||||
u8 byte;
|
||||
|
||||
pr_debug("%s(), speed=%d (was %d)\n", __func__,
|
||||
speed, dev->speed);
|
||||
|
||||
/* dongle already reset, dongle and port at default speed (9600) */
|
||||
|
||||
/* Set RTS low for 1 ms */
|
||||
sirdev_set_dtr_rts(dev, TRUE, FALSE);
|
||||
mdelay(1);
|
||||
|
||||
/* Write control byte */
|
||||
byte = get_control_byte(speed);
|
||||
sirdev_raw_write(dev, &byte, sizeof(byte));
|
||||
|
||||
/* Wait at least 10ms: fake wait_until_sent - 10 bits at 9600 baud*/
|
||||
msleep(15); /* old ma600 uses 15ms */
|
||||
|
||||
#if 1
|
||||
/* read-back of the control byte. ma600 is the first dongle driver
|
||||
* which uses this so there might be some unidentified issues.
|
||||
* Disable this in case of problems with readback.
|
||||
*/
|
||||
|
||||
sirdev_raw_read(dev, &byte, sizeof(byte));
|
||||
if (byte != get_control_byte(speed)) {
|
||||
net_warn_ratelimited("%s(): bad control byte read-back %02x != %02x\n",
|
||||
__func__, (unsigned)byte,
|
||||
(unsigned)get_control_byte(speed));
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
pr_debug("%s() control byte write read OK\n", __func__);
|
||||
#endif
|
||||
|
||||
/* Set DTR, Set RTS */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
/* Wait at least 10ms */
|
||||
msleep(10);
|
||||
|
||||
/* dongle is now switched to the new speed */
|
||||
dev->speed = speed;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ma600_reset (dev)
|
||||
*
|
||||
* This function resets the ma600 dongle.
|
||||
*
|
||||
* Algorithm:
|
||||
* 0. DTR=0, RTS=1 and wait 10 ms
|
||||
* 1. DTR=1, RTS=1 and wait 10 ms
|
||||
* 2. 9600 bps now
|
||||
*/
|
||||
|
||||
/* total delays are only about 20ms - let's just sleep for now to
|
||||
* avoid the state machine complexity before we get things working
|
||||
*/
|
||||
|
||||
static int ma600_reset(struct sir_dev *dev)
|
||||
{
|
||||
/* Reset the dongle : set DTR low for 10 ms */
|
||||
sirdev_set_dtr_rts(dev, FALSE, TRUE);
|
||||
msleep(10);
|
||||
|
||||
/* Go back to normal mode */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
msleep(10);
|
||||
|
||||
dev->speed = 9600; /* That's the dongle-default */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95");
|
||||
MODULE_DESCRIPTION("MA600 dongle driver version 0.1");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("irda-dongle-11"); /* IRDA_MA600_DONGLE */
|
||||
|
||||
module_init(ma600_sir_init);
|
||||
module_exit(ma600_sir_cleanup);
|
||||
|
|
@ -1,224 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
*
|
||||
* Filename: mcp2120.c
|
||||
* Version: 1.0
|
||||
* Description: Implementation for the MCP2120 (Microchip)
|
||||
* Status: Experimental.
|
||||
* Author: Felix Tang (tangf@eyetap.org)
|
||||
* Created at: Sun Mar 31 19:32:12 EST 2002
|
||||
* Based on code by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 2002 Felix Tang, All Rights Reserved.
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
|
||||
#include "sir-dev.h"
|
||||
|
||||
static int mcp2120_reset(struct sir_dev *dev);
|
||||
static int mcp2120_open(struct sir_dev *dev);
|
||||
static int mcp2120_close(struct sir_dev *dev);
|
||||
static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed);
|
||||
|
||||
#define MCP2120_9600 0x87
|
||||
#define MCP2120_19200 0x8B
|
||||
#define MCP2120_38400 0x85
|
||||
#define MCP2120_57600 0x83
|
||||
#define MCP2120_115200 0x81
|
||||
|
||||
#define MCP2120_COMMIT 0x11
|
||||
|
||||
static struct dongle_driver mcp2120 = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "Microchip MCP2120",
|
||||
.type = IRDA_MCP2120_DONGLE,
|
||||
.open = mcp2120_open,
|
||||
.close = mcp2120_close,
|
||||
.reset = mcp2120_reset,
|
||||
.set_speed = mcp2120_change_speed,
|
||||
};
|
||||
|
||||
static int __init mcp2120_sir_init(void)
|
||||
{
|
||||
return irda_register_dongle(&mcp2120);
|
||||
}
|
||||
|
||||
static void __exit mcp2120_sir_cleanup(void)
|
||||
{
|
||||
irda_unregister_dongle(&mcp2120);
|
||||
}
|
||||
|
||||
static int mcp2120_open(struct sir_dev *dev)
|
||||
{
|
||||
struct qos_info *qos = &dev->qos;
|
||||
|
||||
/* seems no explicit power-on required here and reset switching it on anyway */
|
||||
|
||||
qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
|
||||
qos->min_turn_time.bits = 0x01;
|
||||
irda_qos_bits_to_value(qos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp2120_close(struct sir_dev *dev)
|
||||
{
|
||||
/* Power off dongle */
|
||||
/* reset and inhibit mcp2120 */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
// sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function mcp2120_change_speed (dev, speed)
|
||||
*
|
||||
* Set the speed for the MCP2120.
|
||||
*
|
||||
*/
|
||||
|
||||
#define MCP2120_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED+1)
|
||||
|
||||
static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed)
|
||||
{
|
||||
unsigned state = dev->fsm.substate;
|
||||
unsigned delay = 0;
|
||||
u8 control[2];
|
||||
static int ret = 0;
|
||||
|
||||
switch (state) {
|
||||
case SIRDEV_STATE_DONGLE_SPEED:
|
||||
/* Set DTR to enter command mode */
|
||||
sirdev_set_dtr_rts(dev, TRUE, FALSE);
|
||||
udelay(500);
|
||||
|
||||
ret = 0;
|
||||
switch (speed) {
|
||||
default:
|
||||
speed = 9600;
|
||||
ret = -EINVAL;
|
||||
/* fall through */
|
||||
case 9600:
|
||||
control[0] = MCP2120_9600;
|
||||
//printk("mcp2120 9600\n");
|
||||
break;
|
||||
case 19200:
|
||||
control[0] = MCP2120_19200;
|
||||
//printk("mcp2120 19200\n");
|
||||
break;
|
||||
case 34800:
|
||||
control[0] = MCP2120_38400;
|
||||
//printk("mcp2120 38400\n");
|
||||
break;
|
||||
case 57600:
|
||||
control[0] = MCP2120_57600;
|
||||
//printk("mcp2120 57600\n");
|
||||
break;
|
||||
case 115200:
|
||||
control[0] = MCP2120_115200;
|
||||
//printk("mcp2120 115200\n");
|
||||
break;
|
||||
}
|
||||
control[1] = MCP2120_COMMIT;
|
||||
|
||||
/* Write control bytes */
|
||||
sirdev_raw_write(dev, control, 2);
|
||||
dev->speed = speed;
|
||||
|
||||
state = MCP2120_STATE_WAIT_SPEED;
|
||||
delay = 100;
|
||||
//printk("mcp2120_change_speed: dongle_speed\n");
|
||||
break;
|
||||
|
||||
case MCP2120_STATE_WAIT_SPEED:
|
||||
/* Go back to normal mode */
|
||||
sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
//printk("mcp2120_change_speed: mcp_wait\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
net_err_ratelimited("%s(), undefine state %d\n",
|
||||
__func__, state);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
dev->fsm.substate = state;
|
||||
return (delay > 0) ? delay : ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function mcp2120_reset (driver)
|
||||
*
|
||||
* This function resets the mcp2120 dongle.
|
||||
*
|
||||
* Info: -set RTS to reset mcp2120
|
||||
* -set DTR to set mcp2120 software command mode
|
||||
* -mcp2120 defaults to 9600 baud after reset
|
||||
*
|
||||
* Algorithm:
|
||||
* 0. Set RTS to reset mcp2120.
|
||||
* 1. Clear RTS and wait for device reset timer of 30 ms (max).
|
||||
*
|
||||
*/
|
||||
|
||||
#define MCP2120_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET+1)
|
||||
#define MCP2120_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET+2)
|
||||
|
||||
static int mcp2120_reset(struct sir_dev *dev)
|
||||
{
|
||||
unsigned state = dev->fsm.substate;
|
||||
unsigned delay = 0;
|
||||
int ret = 0;
|
||||
|
||||
switch (state) {
|
||||
case SIRDEV_STATE_DONGLE_RESET:
|
||||
//printk("mcp2120_reset: dongle_reset\n");
|
||||
/* Reset dongle by setting RTS*/
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
state = MCP2120_STATE_WAIT1_RESET;
|
||||
delay = 50;
|
||||
break;
|
||||
|
||||
case MCP2120_STATE_WAIT1_RESET:
|
||||
//printk("mcp2120_reset: mcp2120_wait1\n");
|
||||
/* clear RTS and wait for at least 30 ms. */
|
||||
sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
state = MCP2120_STATE_WAIT2_RESET;
|
||||
delay = 50;
|
||||
break;
|
||||
|
||||
case MCP2120_STATE_WAIT2_RESET:
|
||||
//printk("mcp2120_reset mcp2120_wait2\n");
|
||||
/* Go back to normal mode */
|
||||
sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
break;
|
||||
|
||||
default:
|
||||
net_err_ratelimited("%s(), undefined state %d\n",
|
||||
__func__, state);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
dev->fsm.substate = state;
|
||||
return (delay > 0) ? delay : ret;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Felix Tang <tangf@eyetap.org>");
|
||||
MODULE_DESCRIPTION("Microchip MCP2120");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("irda-dongle-9"); /* IRDA_MCP2120_DONGLE */
|
||||
|
||||
module_init(mcp2120_sir_init);
|
||||
module_exit(mcp2120_sir_cleanup);
|
|
@ -1,990 +0,0 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* Filename: mcs7780.c
|
||||
* Version: 0.4-alpha
|
||||
* Description: Irda MosChip USB Dongle Driver
|
||||
* Authors: Lukasz Stelmach <stlman@poczta.fm>
|
||||
* Brian Pugh <bpugh@cs.pdx.edu>
|
||||
* Judy Fischbach <jfisch@cs.pdx.edu>
|
||||
*
|
||||
* Based on stir4200 driver, but some things done differently.
|
||||
* Based on earlier driver by Paul Stewart <stewart@parc.com>
|
||||
*
|
||||
* Copyright (C) 2000, Roman Weissgaerber <weissg@vienna.at>
|
||||
* Copyright (C) 2001, Dag Brattli <dag@brattli.net>
|
||||
* Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.com>
|
||||
* Copyright (C) 2004, Stephen Hemminger <shemminger@osdl.org>
|
||||
* Copyright (C) 2005, Lukasz Stelmach <stlman@poczta.fm>
|
||||
* Copyright (C) 2005, Brian Pugh <bpugh@cs.pdx.edu>
|
||||
* Copyright (C) 2005, Judy Fischbach <jfisch@cs.pdx.edu>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* MCS7780 is a simple USB to IrDA bridge by MosChip. It is neither
|
||||
* compatibile with irda-usb nor with stir4200. Although it is quite
|
||||
* similar to the later as far as general idea of operation is concerned.
|
||||
* That is it requires the software to do all the framing job at SIR speeds.
|
||||
* The hardware does take care of the framing at MIR and FIR speeds.
|
||||
* It supports all speeds from 2400 through 4Mbps
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/wrapper.h>
|
||||
#include <net/irda/crc.h>
|
||||
|
||||
#include "mcs7780.h"
|
||||
|
||||
#define MCS_VENDOR_ID 0x9710
|
||||
#define MCS_PRODUCT_ID 0x7780
|
||||
|
||||
static const struct usb_device_id mcs_table[] = {
|
||||
/* MosChip Corp., MCS7780 FIR-USB Adapter */
|
||||
{USB_DEVICE(MCS_VENDOR_ID, MCS_PRODUCT_ID)},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_AUTHOR("Brian Pugh <bpugh@cs.pdx.edu>");
|
||||
MODULE_DESCRIPTION("IrDA-USB Dongle Driver for MosChip MCS7780");
|
||||
MODULE_VERSION("0.3alpha");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, mcs_table);
|
||||
|
||||
static int qos_mtt_bits = 0x07 /* > 1ms */ ;
|
||||
module_param(qos_mtt_bits, int, 0);
|
||||
MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");
|
||||
|
||||
static int receive_mode = 0x1;
|
||||
module_param(receive_mode, int, 0);
|
||||
MODULE_PARM_DESC(receive_mode,
|
||||
"Receive mode of the device (1:fast, 0:slow, default:1)");
|
||||
|
||||
static int sir_tweak = 1;
|
||||
module_param(sir_tweak, int, 0444);
|
||||
MODULE_PARM_DESC(sir_tweak,
|
||||
"Default pulse width (1:1.6us, 0:3/16 bit, default:1).");
|
||||
|
||||
static int transceiver_type = MCS_TSC_VISHAY;
|
||||
module_param(transceiver_type, int, 0444);
|
||||
MODULE_PARM_DESC(transceiver_type, "IR transceiver type, see mcs7780.h.");
|
||||
|
||||
static struct usb_driver mcs_driver = {
|
||||
.name = "mcs7780",
|
||||
.probe = mcs_probe,
|
||||
.disconnect = mcs_disconnect,
|
||||
.id_table = mcs_table,
|
||||
};
|
||||
|
||||
/* speed flag selection by direct addressing.
|
||||
addr = (speed >> 8) & 0x0f
|
||||
|
||||
0x1 57600 0x2 115200 0x4 1152000 0x5 9600
|
||||
0x6 38400 0x9 2400 0xa 576000 0xb 19200
|
||||
|
||||
4Mbps (or 2400) must be checked separately. Since it also has
|
||||
to be programmed in a different manner that is not a big problem.
|
||||
*/
|
||||
static __u16 mcs_speed_set[16] = { 0,
|
||||
MCS_SPEED_57600,
|
||||
MCS_SPEED_115200,
|
||||
0,
|
||||
MCS_SPEED_1152000,
|
||||
MCS_SPEED_9600,
|
||||
MCS_SPEED_38400,
|
||||
0, 0,
|
||||
MCS_SPEED_2400,
|
||||
MCS_SPEED_576000,
|
||||
MCS_SPEED_19200,
|
||||
0, 0, 0,
|
||||
};
|
||||
|
||||
/* Set given 16 bit register with a 16 bit value. Send control message
|
||||
* to set dongle register. */
|
||||
static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val)
|
||||
{
|
||||
struct usb_device *dev = mcs->usbdev;
|
||||
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
|
||||
MCS_WR_RTYPE, val, reg, NULL, 0,
|
||||
msecs_to_jiffies(MCS_CTRL_TIMEOUT));
|
||||
}
|
||||
|
||||
/* Get 16 bit register value. Send contol message to read dongle register. */
|
||||
static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val)
|
||||
{
|
||||
struct usb_device *dev = mcs->usbdev;
|
||||
void *dmabuf;
|
||||
int ret;
|
||||
|
||||
dmabuf = kmalloc(sizeof(__u16), GFP_KERNEL);
|
||||
if (!dmabuf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
|
||||
MCS_RD_RTYPE, 0, reg, dmabuf, 2,
|
||||
msecs_to_jiffies(MCS_CTRL_TIMEOUT));
|
||||
|
||||
memcpy(val, dmabuf, sizeof(__u16));
|
||||
kfree(dmabuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Setup a communication between mcs7780 and TFDU chips. It is described
|
||||
* in more detail in the data sheet. The setup sequence puts the the
|
||||
* vishay tranceiver into high speed mode. It will also receive SIR speed
|
||||
* packets but at reduced sensitivity.
|
||||
*/
|
||||
|
||||
/* 0: OK 1:ERROR */
|
||||
static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs)
|
||||
{
|
||||
int ret = 0;
|
||||
__u16 rval;
|
||||
|
||||
/* mcs_get_reg should read exactly two bytes from the dongle */
|
||||
ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval);
|
||||
if (unlikely(ret != 2)) {
|
||||
ret = -EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* The MCS_XCVR_CONF bit puts the transceiver into configuration
|
||||
* mode. The MCS_MODE0 bit must start out high (1) and then
|
||||
* transition to low and the MCS_STFIR and MCS_MODE1 bits must
|
||||
* be low.
|
||||
*/
|
||||
rval |= (MCS_MODE0 | MCS_XCVR_CONF);
|
||||
rval &= ~MCS_STFIR;
|
||||
rval &= ~MCS_MODE1;
|
||||
ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval);
|
||||
if (unlikely(ret))
|
||||
goto error;
|
||||
|
||||
rval &= ~MCS_MODE0;
|
||||
ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval);
|
||||
if (unlikely(ret))
|
||||
goto error;
|
||||
|
||||
rval &= ~MCS_XCVR_CONF;
|
||||
ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval);
|
||||
if (unlikely(ret))
|
||||
goto error;
|
||||
|
||||
ret = 0;
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Setup a communication between mcs7780 and agilent chip. */
|
||||
static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs)
|
||||
{
|
||||
net_warn_ratelimited("This transceiver type is not supported yet\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Setup a communication between mcs7780 and sharp chip. */
|
||||
static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs)
|
||||
{
|
||||
net_warn_ratelimited("This transceiver type is not supported yet\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Common setup for all transceivers */
|
||||
static inline int mcs_setup_transceiver(struct mcs_cb *mcs)
|
||||
{
|
||||
int ret = 0;
|
||||
__u16 rval;
|
||||
const char *msg;
|
||||
|
||||
msg = "Basic transceiver setup error";
|
||||
|
||||
/* read value of MODE Register, set the DRIVER and RESET bits
|
||||
* and write value back out to MODE Register
|
||||
*/
|
||||
ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval);
|
||||
if(unlikely(ret != 2))
|
||||
goto error;
|
||||
rval |= MCS_DRIVER; /* put the mcs7780 into configuration mode. */
|
||||
ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
|
||||
if(unlikely(ret))
|
||||
goto error;
|
||||
|
||||
rval = 0; /* set min pulse width to 0 initially. */
|
||||
ret = mcs_set_reg(mcs, MCS_MINRXPW_REG, rval);
|
||||
if(unlikely(ret))
|
||||
goto error;
|
||||
|
||||
ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval);
|
||||
if(unlikely(ret != 2))
|
||||
goto error;
|
||||
|
||||
rval &= ~MCS_FIR; /* turn off fir mode. */
|
||||
if(mcs->sir_tweak)
|
||||
rval |= MCS_SIR16US; /* 1.6us pulse width */
|
||||
else
|
||||
rval &= ~MCS_SIR16US; /* 3/16 bit time pulse width */
|
||||
|
||||
/* make sure ask mode and back to back packets are off. */
|
||||
rval &= ~(MCS_BBTG | MCS_ASK);
|
||||
|
||||
rval &= ~MCS_SPEED_MASK;
|
||||
rval |= MCS_SPEED_9600; /* make sure initial speed is 9600. */
|
||||
mcs->speed = 9600;
|
||||
mcs->new_speed = 0; /* new_speed is set to 0 */
|
||||
rval &= ~MCS_PLLPWDN; /* disable power down. */
|
||||
|
||||
/* make sure device determines direction and that the auto send sip
|
||||
* pulse are on.
|
||||
*/
|
||||
rval |= MCS_DTD | MCS_SIPEN;
|
||||
|
||||
ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
|
||||
if(unlikely(ret))
|
||||
goto error;
|
||||
|
||||
msg = "transceiver model specific setup error";
|
||||
switch (mcs->transceiver_type) {
|
||||
case MCS_TSC_VISHAY:
|
||||
ret = mcs_setup_transceiver_vishay(mcs);
|
||||
break;
|
||||
|
||||
case MCS_TSC_SHARP:
|
||||
ret = mcs_setup_transceiver_sharp(mcs);
|
||||
break;
|
||||
|
||||
case MCS_TSC_AGILENT:
|
||||
ret = mcs_setup_transceiver_agilent(mcs);
|
||||
break;
|
||||
|
||||
default:
|
||||
net_warn_ratelimited("Unknown transceiver type: %d\n",
|
||||
mcs->transceiver_type);
|
||||
ret = 1;
|
||||
}
|
||||
if (unlikely(ret))
|
||||
goto error;
|
||||
|
||||
/* If transceiver is not SHARP, then if receive mode set
|
||||
* on the RXFAST bit in the XCVR Register otherwise unset it
|
||||
*/
|
||||
if (mcs->transceiver_type != MCS_TSC_SHARP) {
|
||||
|
||||
ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval);
|
||||
if (unlikely(ret != 2))
|
||||
goto error;
|
||||
if (mcs->receive_mode)
|
||||
rval |= MCS_RXFAST;
|
||||
else
|
||||
rval &= ~MCS_RXFAST;
|
||||
ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval);
|
||||
if (unlikely(ret))
|
||||
goto error;
|
||||
}
|
||||
|
||||
msg = "transceiver reset";
|
||||
|
||||
ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval);
|
||||
if (unlikely(ret != 2))
|
||||
goto error;
|
||||
|
||||
/* reset the mcs7780 so all changes take effect. */
|
||||
rval &= ~MCS_RESET;
|
||||
ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
|
||||
if (unlikely(ret))
|
||||
goto error;
|
||||
else
|
||||
return ret;
|
||||
|
||||
error:
|
||||
net_err_ratelimited("%s\n", msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wraps the data in format for SIR */
|
||||
static inline int mcs_wrap_sir_skb(struct sk_buff *skb, __u8 * buf)
|
||||
{
|
||||
int wraplen;
|
||||
|
||||
/* 2: full frame length, including "the length" */
|
||||
wraplen = async_wrap_skb(skb, buf + 2, 4094);
|
||||
|
||||
wraplen += 2;
|
||||
buf[0] = wraplen & 0xff;
|
||||
buf[1] = (wraplen >> 8) & 0xff;
|
||||
|
||||
return wraplen;
|
||||
}
|
||||
|
||||
/* Wraps the data in format for FIR */
|
||||
static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf)
|
||||
{
|
||||
unsigned int len = 0;
|
||||
__u32 fcs = ~(crc32_le(~0, skb->data, skb->len));
|
||||
|
||||
/* add 2 bytes for length value and 4 bytes for fcs. */
|
||||
len = skb->len + 6;
|
||||
|
||||
/* The mcs7780 requires that the first two bytes are the packet
|
||||
* length in little endian order. Note: the length value includes
|
||||
* the two bytes for the length value itself.
|
||||
*/
|
||||
buf[0] = len & 0xff;
|
||||
buf[1] = (len >> 8) & 0xff;
|
||||
/* copy the data into the tx buffer. */
|
||||
skb_copy_from_linear_data(skb, buf + 2, skb->len);
|
||||
/* put the fcs in the last four bytes in little endian order. */
|
||||
buf[len - 4] = fcs & 0xff;
|
||||
buf[len - 3] = (fcs >> 8) & 0xff;
|
||||
buf[len - 2] = (fcs >> 16) & 0xff;
|
||||
buf[len - 1] = (fcs >> 24) & 0xff;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Wraps the data in format for MIR */
|
||||
static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf)
|
||||
{
|
||||
__u16 fcs = 0;
|
||||
int len = skb->len + 4;
|
||||
|
||||
fcs = ~(irda_calc_crc16(~fcs, skb->data, skb->len));
|
||||
/* put the total packet length in first. Note: packet length
|
||||
* value includes the two bytes that hold the packet length
|
||||
* itself.
|
||||
*/
|
||||
buf[0] = len & 0xff;
|
||||
buf[1] = (len >> 8) & 0xff;
|
||||
/* copy the data */
|
||||
skb_copy_from_linear_data(skb, buf + 2, skb->len);
|
||||
/* put the fcs in last two bytes in little endian order. */
|
||||
buf[len - 2] = fcs & 0xff;
|
||||
buf[len - 1] = (fcs >> 8) & 0xff;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Unwrap received packets at MIR speed. A 16 bit crc_ccitt checksum is
|
||||
* used for the fcs. When performed over the entire packet the result
|
||||
* should be GOOD_FCS = 0xf0b8. Hands the unwrapped data off to the IrDA
|
||||
* layer via a sk_buff.
|
||||
*/
|
||||
static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len)
|
||||
{
|
||||
__u16 fcs;
|
||||
int new_len;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Assume that the frames are going to fill a single packet
|
||||
* rather than span multiple packets.
|
||||
*/
|
||||
|
||||
new_len = len - 2;
|
||||
if(unlikely(new_len <= 0)) {
|
||||
net_err_ratelimited("%s short frame length %d\n",
|
||||
mcs->netdev->name, new_len);
|
||||
++mcs->netdev->stats.rx_errors;
|
||||
++mcs->netdev->stats.rx_length_errors;
|
||||
return;
|
||||
}
|
||||
fcs = 0;
|
||||
fcs = irda_calc_crc16(~fcs, buf, len);
|
||||
|
||||
if(fcs != GOOD_FCS) {
|
||||
net_err_ratelimited("crc error calc 0x%x len %d\n",
|
||||
fcs, new_len);
|
||||
mcs->netdev->stats.rx_errors++;
|
||||
mcs->netdev->stats.rx_crc_errors++;
|
||||
return;
|
||||
}
|
||||
|
||||
skb = dev_alloc_skb(new_len + 1);
|
||||
if(unlikely(!skb)) {
|
||||
++mcs->netdev->stats.rx_dropped;
|
||||
return;
|
||||
}
|
||||
|
||||
skb_reserve(skb, 1);
|
||||
skb_copy_to_linear_data(skb, buf, new_len);
|
||||
skb_put(skb, new_len);
|
||||
skb_reset_mac_header(skb);
|
||||
skb->protocol = htons(ETH_P_IRDA);
|
||||
skb->dev = mcs->netdev;
|
||||
|
||||
netif_rx(skb);
|
||||
|
||||
mcs->netdev->stats.rx_packets++;
|
||||
mcs->netdev->stats.rx_bytes += new_len;
|
||||
}
|
||||
|
||||
/* Unwrap received packets at FIR speed. A 32 bit crc_ccitt checksum is
|
||||
* used for the fcs. Hands the unwrapped data off to the IrDA
|
||||
* layer via a sk_buff.
|
||||
*/
|
||||
static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len)
|
||||
{
|
||||
__u32 fcs;
|
||||
int new_len;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Assume that the frames are going to fill a single packet
|
||||
* rather than span multiple packets. This is most likely a false
|
||||
* assumption.
|
||||
*/
|
||||
|
||||
new_len = len - 4;
|
||||
if(unlikely(new_len <= 0)) {
|
||||
net_err_ratelimited("%s short frame length %d\n",
|
||||
mcs->netdev->name, new_len);
|
||||
++mcs->netdev->stats.rx_errors;
|
||||
++mcs->netdev->stats.rx_length_errors;
|
||||
return;
|
||||
}
|
||||
|
||||
fcs = ~(crc32_le(~0, buf, new_len));
|
||||
if(fcs != get_unaligned_le32(buf + new_len)) {
|
||||
net_err_ratelimited("crc error calc 0x%x len %d\n",
|
||||
fcs, new_len);
|
||||
mcs->netdev->stats.rx_errors++;
|
||||
mcs->netdev->stats.rx_crc_errors++;
|
||||
return;
|
||||
}
|
||||
|
||||
skb = dev_alloc_skb(new_len + 1);
|
||||
if(unlikely(!skb)) {
|
||||
++mcs->netdev->stats.rx_dropped;
|
||||
return;
|
||||
}
|
||||
|
||||
skb_reserve(skb, 1);
|
||||
skb_copy_to_linear_data(skb, buf, new_len);
|
||||
skb_put(skb, new_len);
|
||||
skb_reset_mac_header(skb);
|
||||
skb->protocol = htons(ETH_P_IRDA);
|
||||
skb->dev = mcs->netdev;
|
||||
|
||||
netif_rx(skb);
|
||||
|
||||
mcs->netdev->stats.rx_packets++;
|
||||
mcs->netdev->stats.rx_bytes += new_len;
|
||||
}
|
||||
|
||||
|
||||
/* Allocates urbs for both receive and transmit.
|
||||
* If alloc fails return error code 0 (fail) otherwise
|
||||
* return error code 1 (success).
|
||||
*/
|
||||
static inline int mcs_setup_urbs(struct mcs_cb *mcs)
|
||||
{
|
||||
mcs->rx_urb = NULL;
|
||||
|
||||
mcs->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!mcs->tx_urb)
|
||||
return 0;
|
||||
|
||||
mcs->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!mcs->rx_urb) {
|
||||
usb_free_urb(mcs->tx_urb);
|
||||
mcs->tx_urb = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Sets up state to be initially outside frame, gets receive urb,
|
||||
* sets status to successful and then submits the urb to start
|
||||
* receiving the data.
|
||||
*/
|
||||
static inline int mcs_receive_start(struct mcs_cb *mcs)
|
||||
{
|
||||
mcs->rx_buff.in_frame = FALSE;
|
||||
mcs->rx_buff.state = OUTSIDE_FRAME;
|
||||
|
||||
usb_fill_bulk_urb(mcs->rx_urb, mcs->usbdev,
|
||||
usb_rcvbulkpipe(mcs->usbdev, mcs->ep_in),
|
||||
mcs->in_buf, 4096, mcs_receive_irq, mcs);
|
||||
|
||||
mcs->rx_urb->status = 0;
|
||||
return usb_submit_urb(mcs->rx_urb, GFP_KERNEL);
|
||||
}
|
||||
|
||||
/* Finds the in and out endpoints for the mcs control block */
|
||||
static inline int mcs_find_endpoints(struct mcs_cb *mcs,
|
||||
struct usb_host_endpoint *ep, int epnum)
|
||||
{
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
/* If no place to store the endpoints just return */
|
||||
if (!ep)
|
||||
return ret;
|
||||
|
||||
/* cycle through all endpoints, find the first two that are DIR_IN */
|
||||
for (i = 0; i < epnum; i++) {
|
||||
if (ep[i].desc.bEndpointAddress & USB_DIR_IN)
|
||||
mcs->ep_in = ep[i].desc.bEndpointAddress;
|
||||
else
|
||||
mcs->ep_out = ep[i].desc.bEndpointAddress;
|
||||
|
||||
/* MosChip says that the chip has only two bulk
|
||||
* endpoints. Find one for each direction and move on.
|
||||
*/
|
||||
if ((mcs->ep_in != 0) && (mcs->ep_out != 0)) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mcs_speed_work(struct work_struct *work)
|
||||
{
|
||||
struct mcs_cb *mcs = container_of(work, struct mcs_cb, work);
|
||||
struct net_device *netdev = mcs->netdev;
|
||||
|
||||
mcs_speed_change(mcs);
|
||||
netif_wake_queue(netdev);
|
||||
}
|
||||
|
||||
/* Function to change the speed of the mcs7780. Fully supports SIR,
|
||||
* MIR, and FIR speeds.
|
||||
*/
|
||||
static int mcs_speed_change(struct mcs_cb *mcs)
|
||||
{
|
||||
int ret = 0;
|
||||
int rst = 0;
|
||||
int cnt = 0;
|
||||
__u16 nspeed;
|
||||
__u16 rval;
|
||||
|
||||
nspeed = mcs_speed_set[(mcs->new_speed >> 8) & 0x0f];
|
||||
|
||||
do {
|
||||
mcs_get_reg(mcs, MCS_RESV_REG, &rval);
|
||||
} while(cnt++ < 100 && (rval & MCS_IRINTX));
|
||||
|
||||
if (cnt > 100) {
|
||||
net_err_ratelimited("unable to change speed\n");
|
||||
ret = -EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
mcs_get_reg(mcs, MCS_MODE_REG, &rval);
|
||||
|
||||
/* MINRXPW values recommended by MosChip */
|
||||
if (mcs->new_speed <= 115200) {
|
||||
rval &= ~MCS_FIR;
|
||||
|
||||
rst = mcs->speed > 115200;
|
||||
if (rst)
|
||||
mcs_set_reg(mcs, MCS_MINRXPW_REG, 0);
|
||||
|
||||
} else if (mcs->new_speed <= 1152000) {
|
||||
rval &= ~MCS_FIR;
|
||||
|
||||
rst = !(mcs->speed == 576000 || mcs->speed == 1152000);
|
||||
if (rst)
|
||||
mcs_set_reg(mcs, MCS_MINRXPW_REG, 5);
|
||||
|
||||
} else {
|
||||
rval |= MCS_FIR;
|
||||
|
||||
rst = mcs->speed != 4000000;
|
||||
if (rst)
|
||||
mcs_set_reg(mcs, MCS_MINRXPW_REG, 5);
|
||||
|
||||
}
|
||||
|
||||
rval &= ~MCS_SPEED_MASK;
|
||||
rval |= nspeed;
|
||||
|
||||
ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
|
||||
if (unlikely(ret))
|
||||
goto error;
|
||||
|
||||
if (rst)
|
||||
switch (mcs->transceiver_type) {
|
||||
case MCS_TSC_VISHAY:
|
||||
ret = mcs_setup_transceiver_vishay(mcs);
|
||||
break;
|
||||
|
||||
case MCS_TSC_SHARP:
|
||||
ret = mcs_setup_transceiver_sharp(mcs);
|
||||
break;
|
||||
|
||||
case MCS_TSC_AGILENT:
|
||||
ret = mcs_setup_transceiver_agilent(mcs);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = 1;
|
||||
net_warn_ratelimited("Unknown transceiver type: %d\n",
|
||||
mcs->transceiver_type);
|
||||
}
|
||||
if (unlikely(ret))
|
||||
goto error;
|
||||
|
||||
mcs_get_reg(mcs, MCS_MODE_REG, &rval);
|
||||
rval &= ~MCS_RESET;
|
||||
ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
|
||||
|
||||
mcs->speed = mcs->new_speed;
|
||||
error:
|
||||
mcs->new_speed = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Ioctl calls not supported at this time. Can be an area of future work. */
|
||||
static int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
|
||||
{
|
||||
/* struct if_irda_req *irq = (struct if_irda_req *)rq; */
|
||||
/* struct mcs_cb *mcs = netdev_priv(netdev); */
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Network device is taken down, done by "ifconfig irda0 down" */
|
||||
static int mcs_net_close(struct net_device *netdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mcs_cb *mcs = netdev_priv(netdev);
|
||||
|
||||
/* Stop transmit processing */
|
||||
netif_stop_queue(netdev);
|
||||
|
||||
kfree_skb(mcs->rx_buff.skb);
|
||||
|
||||
/* kill and free the receive and transmit URBs */
|
||||
usb_kill_urb(mcs->rx_urb);
|
||||
usb_free_urb(mcs->rx_urb);
|
||||
usb_kill_urb(mcs->tx_urb);
|
||||
usb_free_urb(mcs->tx_urb);
|
||||
|
||||
/* Stop and remove instance of IrLAP */
|
||||
if (mcs->irlap)
|
||||
irlap_close(mcs->irlap);
|
||||
|
||||
mcs->irlap = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Network device is taken up, done by "ifconfig irda0 up" */
|
||||
static int mcs_net_open(struct net_device *netdev)
|
||||
{
|
||||
struct mcs_cb *mcs = netdev_priv(netdev);
|
||||
char hwname[16];
|
||||
int ret = 0;
|
||||
|
||||
ret = usb_clear_halt(mcs->usbdev,
|
||||
usb_sndbulkpipe(mcs->usbdev, mcs->ep_in));
|
||||
if (ret)
|
||||
goto error1;
|
||||
ret = usb_clear_halt(mcs->usbdev,
|
||||
usb_rcvbulkpipe(mcs->usbdev, mcs->ep_out));
|
||||
if (ret)
|
||||
goto error1;
|
||||
|
||||
ret = mcs_setup_transceiver(mcs);
|
||||
if (ret)
|
||||
goto error1;
|
||||
|
||||
ret = -ENOMEM;
|
||||
|
||||
/* Initialize for SIR/FIR to copy data directly into skb. */
|
||||
mcs->receiving = 0;
|
||||
mcs->rx_buff.truesize = IRDA_SKB_MAX_MTU;
|
||||
mcs->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
|
||||
if (!mcs->rx_buff.skb)
|
||||
goto error1;
|
||||
|
||||
skb_reserve(mcs->rx_buff.skb, 1);
|
||||
mcs->rx_buff.head = mcs->rx_buff.skb->data;
|
||||
|
||||
/*
|
||||
* Now that everything should be initialized properly,
|
||||
* Open new IrLAP layer instance to take care of us...
|
||||
* Note : will send immediately a speed change...
|
||||
*/
|
||||
sprintf(hwname, "usb#%d", mcs->usbdev->devnum);
|
||||
mcs->irlap = irlap_open(netdev, &mcs->qos, hwname);
|
||||
if (!mcs->irlap) {
|
||||
net_err_ratelimited("mcs7780: irlap_open failed\n");
|
||||
goto error2;
|
||||
}
|
||||
|
||||
if (!mcs_setup_urbs(mcs))
|
||||
goto error3;
|
||||
|
||||
ret = mcs_receive_start(mcs);
|
||||
if (ret)
|
||||
goto error4;
|
||||
|
||||
netif_start_queue(netdev);
|
||||
return 0;
|
||||
|
||||
error4:
|
||||
usb_free_urb(mcs->rx_urb);
|
||||
usb_free_urb(mcs->tx_urb);
|
||||
error3:
|
||||
irlap_close(mcs->irlap);
|
||||
error2:
|
||||
kfree_skb(mcs->rx_buff.skb);
|
||||
error1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Receive callback function. */
|
||||
static void mcs_receive_irq(struct urb *urb)
|
||||
{
|
||||
__u8 *bytes;
|
||||
struct mcs_cb *mcs = urb->context;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (!netif_running(mcs->netdev))
|
||||
return;
|
||||
|
||||
if (urb->status)
|
||||
return;
|
||||
|
||||
if (urb->actual_length > 0) {
|
||||
bytes = urb->transfer_buffer;
|
||||
|
||||
/* MCS returns frames without BOF and EOF
|
||||
* I assume it returns whole frames.
|
||||
*/
|
||||
/* SIR speed */
|
||||
if(mcs->speed < 576000) {
|
||||
async_unwrap_char(mcs->netdev, &mcs->netdev->stats,
|
||||
&mcs->rx_buff, 0xc0);
|
||||
|
||||
for (i = 0; i < urb->actual_length; i++)
|
||||
async_unwrap_char(mcs->netdev, &mcs->netdev->stats,
|
||||
&mcs->rx_buff, bytes[i]);
|
||||
|
||||
async_unwrap_char(mcs->netdev, &mcs->netdev->stats,
|
||||
&mcs->rx_buff, 0xc1);
|
||||
}
|
||||
/* MIR speed */
|
||||
else if(mcs->speed == 576000 || mcs->speed == 1152000) {
|
||||
mcs_unwrap_mir(mcs, urb->transfer_buffer,
|
||||
urb->actual_length);
|
||||
}
|
||||
/* FIR speed */
|
||||
else {
|
||||
mcs_unwrap_fir(mcs, urb->transfer_buffer,
|
||||
urb->actual_length);
|
||||
}
|
||||
}
|
||||
|
||||
ret = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
/* Transmit callback function. */
|
||||
static void mcs_send_irq(struct urb *urb)
|
||||
{
|
||||
struct mcs_cb *mcs = urb->context;
|
||||
struct net_device *ndev = mcs->netdev;
|
||||
|
||||
if (unlikely(mcs->new_speed))
|
||||
schedule_work(&mcs->work);
|
||||
else
|
||||
netif_wake_queue(ndev);
|
||||
}
|
||||
|
||||
/* Transmit callback function. */
|
||||
static netdev_tx_t mcs_hard_xmit(struct sk_buff *skb,
|
||||
struct net_device *ndev)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct mcs_cb *mcs;
|
||||
int wraplen;
|
||||
int ret = 0;
|
||||
|
||||
netif_stop_queue(ndev);
|
||||
mcs = netdev_priv(ndev);
|
||||
|
||||
spin_lock_irqsave(&mcs->lock, flags);
|
||||
|
||||
mcs->new_speed = irda_get_next_speed(skb);
|
||||
if (likely(mcs->new_speed == mcs->speed))
|
||||
mcs->new_speed = 0;
|
||||
|
||||
/* SIR speed */
|
||||
if(mcs->speed < 576000) {
|
||||
wraplen = mcs_wrap_sir_skb(skb, mcs->out_buf);
|
||||
}
|
||||
/* MIR speed */
|
||||
else if(mcs->speed == 576000 || mcs->speed == 1152000) {
|
||||
wraplen = mcs_wrap_mir_skb(skb, mcs->out_buf);
|
||||
}
|
||||
/* FIR speed */
|
||||
else {
|
||||
wraplen = mcs_wrap_fir_skb(skb, mcs->out_buf);
|
||||
}
|
||||
usb_fill_bulk_urb(mcs->tx_urb, mcs->usbdev,
|
||||
usb_sndbulkpipe(mcs->usbdev, mcs->ep_out),
|
||||
mcs->out_buf, wraplen, mcs_send_irq, mcs);
|
||||
|
||||
if ((ret = usb_submit_urb(mcs->tx_urb, GFP_ATOMIC))) {
|
||||
net_err_ratelimited("failed tx_urb: %d\n", ret);
|
||||
switch (ret) {
|
||||
case -ENODEV:
|
||||
case -EPIPE:
|
||||
break;
|
||||
default:
|
||||
mcs->netdev->stats.tx_errors++;
|
||||
netif_start_queue(ndev);
|
||||
}
|
||||
} else {
|
||||
mcs->netdev->stats.tx_packets++;
|
||||
mcs->netdev->stats.tx_bytes += skb->len;
|
||||
}
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
spin_unlock_irqrestore(&mcs->lock, flags);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static const struct net_device_ops mcs_netdev_ops = {
|
||||
.ndo_open = mcs_net_open,
|
||||
.ndo_stop = mcs_net_close,
|
||||
.ndo_start_xmit = mcs_hard_xmit,
|
||||
.ndo_do_ioctl = mcs_net_ioctl,
|
||||
};
|
||||
|
||||
/*
|
||||
* This function is called by the USB subsystem for each new device in the
|
||||
* system. Need to verify the device and if it is, then start handling it.
|
||||
*/
|
||||
static int mcs_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
struct net_device *ndev = NULL;
|
||||
struct mcs_cb *mcs;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
ndev = alloc_irdadev(sizeof(*mcs));
|
||||
if (!ndev)
|
||||
goto error1;
|
||||
|
||||
pr_debug("MCS7780 USB-IrDA bridge found at %d.\n", udev->devnum);
|
||||
|
||||
SET_NETDEV_DEV(ndev, &intf->dev);
|
||||
|
||||
ret = usb_reset_configuration(udev);
|
||||
if (ret != 0) {
|
||||
net_err_ratelimited("mcs7780: usb reset configuration failed\n");
|
||||
goto error2;
|
||||
}
|
||||
|
||||
mcs = netdev_priv(ndev);
|
||||
mcs->usbdev = udev;
|
||||
mcs->netdev = ndev;
|
||||
spin_lock_init(&mcs->lock);
|
||||
|
||||
/* Initialize QoS for this device */
|
||||
irda_init_max_qos_capabilies(&mcs->qos);
|
||||
|
||||
/* That's the Rx capability. */
|
||||
mcs->qos.baud_rate.bits &=
|
||||
IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200
|
||||
| IR_576000 | IR_1152000 | (IR_4000000 << 8);
|
||||
|
||||
|
||||
mcs->qos.min_turn_time.bits &= qos_mtt_bits;
|
||||
irda_qos_bits_to_value(&mcs->qos);
|
||||
|
||||
/* Speed change work initialisation*/
|
||||
INIT_WORK(&mcs->work, mcs_speed_work);
|
||||
|
||||
ndev->netdev_ops = &mcs_netdev_ops;
|
||||
|
||||
if (!intf->cur_altsetting) {
|
||||
ret = -ENOMEM;
|
||||
goto error2;
|
||||
}
|
||||
|
||||
ret = mcs_find_endpoints(mcs, intf->cur_altsetting->endpoint,
|
||||
intf->cur_altsetting->desc.bNumEndpoints);
|
||||
if (!ret) {
|
||||
ret = -ENODEV;
|
||||
goto error2;
|
||||
}
|
||||
|
||||
ret = register_netdev(ndev);
|
||||
if (ret != 0)
|
||||
goto error2;
|
||||
|
||||
pr_debug("IrDA: Registered MosChip MCS7780 device as %s\n",
|
||||
ndev->name);
|
||||
|
||||
mcs->transceiver_type = transceiver_type;
|
||||
mcs->sir_tweak = sir_tweak;
|
||||
mcs->receive_mode = receive_mode;
|
||||
|
||||
usb_set_intfdata(intf, mcs);
|
||||
return 0;
|
||||
|
||||
error2:
|
||||
free_netdev(ndev);
|
||||
|
||||
error1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The current device is removed, the USB layer tells us to shut down. */
|
||||
static void mcs_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct mcs_cb *mcs = usb_get_intfdata(intf);
|
||||
|
||||
if (!mcs)
|
||||
return;
|
||||
|
||||
cancel_work_sync(&mcs->work);
|
||||
|
||||
unregister_netdev(mcs->netdev);
|
||||
free_netdev(mcs->netdev);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
pr_debug("MCS7780 now disconnected.\n");
|
||||
}
|
||||
|
||||
module_usb_driver(mcs_driver);
|
|
@ -1,165 +0,0 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* Filename: mcs7780.h
|
||||
* Version: 0.2-alpha
|
||||
* Description: Irda MosChip USB Dongle
|
||||
* Status: Experimental
|
||||
* Authors: Lukasz Stelmach <stlman@poczta.fm>
|
||||
* Brian Pugh <bpugh@cs.pdx.edu>
|
||||
*
|
||||
* Copyright (C) 2005, Lukasz Stelmach <stlman@poczta.fm>
|
||||
* Copyright (C) 2005, Brian Pugh <bpugh@cs.pdx.edu>
|
||||
*
|
||||
* 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 _MCS7780_H
|
||||
#define _MCS7780_H
|
||||
|
||||
#define MCS_MODE_SIR 0
|
||||
#define MCS_MODE_MIR 1
|
||||
#define MCS_MODE_FIR 2
|
||||
|
||||
#define MCS_CTRL_TIMEOUT 500
|
||||
#define MCS_XMIT_TIMEOUT 500
|
||||
/* Possible transceiver types */
|
||||
#define MCS_TSC_VISHAY 0 /* Vishay TFD, default choice */
|
||||
#define MCS_TSC_AGILENT 1 /* Agilent 3602/3600 */
|
||||
#define MCS_TSC_SHARP 2 /* Sharp GP2W1000YP */
|
||||
|
||||
/* Requests */
|
||||
#define MCS_RD_RTYPE 0xC0
|
||||
#define MCS_WR_RTYPE 0x40
|
||||
#define MCS_RDREQ 0x0F
|
||||
#define MCS_WRREQ 0x0E
|
||||
|
||||
/* Register 0x00 */
|
||||
#define MCS_MODE_REG 0
|
||||
#define MCS_FIR ((__u16)0x0001)
|
||||
#define MCS_SIR16US ((__u16)0x0002)
|
||||
#define MCS_BBTG ((__u16)0x0004)
|
||||
#define MCS_ASK ((__u16)0x0008)
|
||||
#define MCS_PARITY ((__u16)0x0010)
|
||||
|
||||
/* SIR/MIR speed constants */
|
||||
#define MCS_SPEED_SHIFT 5
|
||||
#define MCS_SPEED_MASK ((__u16)0x00E0)
|
||||
#define MCS_SPEED(x) ((x & MCS_SPEED_MASK) >> MCS_SPEED_SHIFT)
|
||||
#define MCS_SPEED_2400 ((0 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
|
||||
#define MCS_SPEED_9600 ((1 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
|
||||
#define MCS_SPEED_19200 ((2 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
|
||||
#define MCS_SPEED_38400 ((3 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
|
||||
#define MCS_SPEED_57600 ((4 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
|
||||
#define MCS_SPEED_115200 ((5 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
|
||||
#define MCS_SPEED_576000 ((6 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
|
||||
#define MCS_SPEED_1152000 ((7 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
|
||||
|
||||
#define MCS_PLLPWDN ((__u16)0x0100)
|
||||
#define MCS_DRIVER ((__u16)0x0200)
|
||||
#define MCS_DTD ((__u16)0x0400)
|
||||
#define MCS_DIR ((__u16)0x0800)
|
||||
#define MCS_SIPEN ((__u16)0x1000)
|
||||
#define MCS_SENDSIP ((__u16)0x2000)
|
||||
#define MCS_CHGDIR ((__u16)0x4000)
|
||||
#define MCS_RESET ((__u16)0x8000)
|
||||
|
||||
/* Register 0x02 */
|
||||
#define MCS_XCVR_REG 2
|
||||
#define MCS_MODE0 ((__u16)0x0001)
|
||||
#define MCS_STFIR ((__u16)0x0002)
|
||||
#define MCS_XCVR_CONF ((__u16)0x0004)
|
||||
#define MCS_RXFAST ((__u16)0x0008)
|
||||
/* TXCUR [6:4] */
|
||||
#define MCS_TXCUR_SHIFT 4
|
||||
#define MCS_TXCUR_MASK ((__u16)0x0070)
|
||||
#define MCS_TXCUR(x) ((x & MCS_TXCUR_MASK) >> MCS_TXCUR_SHIFT)
|
||||
#define MCS_SETTXCUR(x,y) \
|
||||
((x & ~MCS_TXCUR_MASK) | (y << MCS_TXCUR_SHIFT) & MCS_TXCUR_MASK)
|
||||
|
||||
#define MCS_MODE1 ((__u16)0x0080)
|
||||
#define MCS_SMODE0 ((__u16)0x0100)
|
||||
#define MCS_SMODE1 ((__u16)0x0200)
|
||||
#define MCS_INVTX ((__u16)0x0400)
|
||||
#define MCS_INVRX ((__u16)0x0800)
|
||||
|
||||
#define MCS_MINRXPW_REG 4
|
||||
|
||||
#define MCS_RESV_REG 7
|
||||
#define MCS_IRINTX ((__u16)0x0001)
|
||||
#define MCS_IRINRX ((__u16)0x0002)
|
||||
|
||||
struct mcs_cb {
|
||||
struct usb_device *usbdev; /* init: probe_irda */
|
||||
struct net_device *netdev; /* network layer */
|
||||
struct irlap_cb *irlap; /* The link layer we are binded to */
|
||||
struct qos_info qos;
|
||||
unsigned int speed; /* Current speed */
|
||||
unsigned int new_speed; /* new speed */
|
||||
|
||||
struct work_struct work; /* Change speed work */
|
||||
|
||||
struct sk_buff *tx_pending;
|
||||
char in_buf[4096]; /* transmit/receive buffer */
|
||||
char out_buf[4096]; /* transmit/receive buffer */
|
||||
__u8 *fifo_status;
|
||||
|
||||
iobuff_t rx_buff; /* receive unwrap state machine */
|
||||
spinlock_t lock;
|
||||
int receiving;
|
||||
|
||||
__u8 ep_in;
|
||||
__u8 ep_out;
|
||||
|
||||
struct urb *rx_urb;
|
||||
struct urb *tx_urb;
|
||||
|
||||
int transceiver_type;
|
||||
int sir_tweak;
|
||||
int receive_mode;
|
||||
};
|
||||
|
||||
static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val);
|
||||
static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val);
|
||||
|
||||
static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs);
|
||||
static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs);
|
||||
static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs);
|
||||
static inline int mcs_setup_transceiver(struct mcs_cb *mcs);
|
||||
static inline int mcs_wrap_sir_skb(struct sk_buff *skb, __u8 * buf);
|
||||
static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf);
|
||||
static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf);
|
||||
static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len);
|
||||
static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len);
|
||||
static inline int mcs_setup_urbs(struct mcs_cb *mcs);
|
||||
static inline int mcs_receive_start(struct mcs_cb *mcs);
|
||||
static inline int mcs_find_endpoints(struct mcs_cb *mcs,
|
||||
struct usb_host_endpoint *ep, int epnum);
|
||||
|
||||
static int mcs_speed_change(struct mcs_cb *mcs);
|
||||
|
||||
static int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd);
|
||||
static int mcs_net_close(struct net_device *netdev);
|
||||
static int mcs_net_open(struct net_device *netdev);
|
||||
|
||||
static void mcs_receive_irq(struct urb *urb);
|
||||
static void mcs_send_irq(struct urb *urb);
|
||||
static netdev_tx_t mcs_hard_xmit(struct sk_buff *skb,
|
||||
struct net_device *netdev);
|
||||
|
||||
static int mcs_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id);
|
||||
static void mcs_disconnect(struct usb_interface *intf);
|
||||
|
||||
#endif /* _MCS7780_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,281 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: nsc-ircc.h
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Fri Nov 13 14:37:40 1998
|
||||
* Modified at: Sun Jan 23 17:47:00 2000
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>
|
||||
* Copyright (c) 1998 Lichen Wang, <lwang@actisys.com>
|
||||
* Copyright (c) 1998 Actisys Corp., www.actisys.com
|
||||
* All Rights Reserved
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef NSC_IRCC_H
|
||||
#define NSC_IRCC_H
|
||||
|
||||
#include <linux/ktime.h>
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/* Features for chips (set in driver_data) */
|
||||
#define NSC_FORCE_DONGLE_TYPE9 0x00000001
|
||||
|
||||
/* DMA modes needed */
|
||||
#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */
|
||||
#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */
|
||||
|
||||
/* Config registers for the '108 */
|
||||
#define CFG_108_BAIC 0x00
|
||||
#define CFG_108_CSRT 0x01
|
||||
#define CFG_108_MCTL 0x02
|
||||
|
||||
/* Config registers for the '338 */
|
||||
#define CFG_338_FER 0x00
|
||||
#define CFG_338_FAR 0x01
|
||||
#define CFG_338_PTR 0x02
|
||||
#define CFG_338_PNP0 0x1b
|
||||
#define CFG_338_PNP1 0x1c
|
||||
#define CFG_338_PNP3 0x4f
|
||||
|
||||
/* Config registers for the '39x (in the logical device bank) */
|
||||
#define CFG_39X_LDN 0x07 /* Logical device number (Super I/O bank) */
|
||||
#define CFG_39X_SIOCF1 0x21 /* SuperI/O Config */
|
||||
#define CFG_39X_ACT 0x30 /* Device activation */
|
||||
#define CFG_39X_BASEH 0x60 /* Device base address (high bits) */
|
||||
#define CFG_39X_BASEL 0x61 /* Device base address (low bits) */
|
||||
#define CFG_39X_IRQNUM 0x70 /* Interrupt number & wake up enable */
|
||||
#define CFG_39X_IRQSEL 0x71 /* Interrupt select (edge/level + polarity) */
|
||||
#define CFG_39X_DMA0 0x74 /* DMA 0 configuration */
|
||||
#define CFG_39X_DMA1 0x75 /* DMA 1 configuration */
|
||||
#define CFG_39X_SPC 0xF0 /* Serial port configuration register */
|
||||
|
||||
/* Flags for configuration register CRF0 */
|
||||
#define APEDCRC 0x02
|
||||
#define ENBNKSEL 0x01
|
||||
|
||||
/* Set 0 */
|
||||
#define TXD 0x00 /* Transmit data port */
|
||||
#define RXD 0x00 /* Receive data port */
|
||||
|
||||
/* Register 1 */
|
||||
#define IER 0x01 /* Interrupt Enable Register*/
|
||||
#define IER_RXHDL_IE 0x01 /* Receiver high data level interrupt */
|
||||
#define IER_TXLDL_IE 0x02 /* Transeiver low data level interrupt */
|
||||
#define IER_LS_IE 0x04//* Link Status Interrupt */
|
||||
#define IER_ETXURI 0x04 /* Tx underrun */
|
||||
#define IER_DMA_IE 0x10 /* DMA finished interrupt */
|
||||
#define IER_TXEMP_IE 0x20
|
||||
#define IER_SFIF_IE 0x40 /* Frame status FIFO intr */
|
||||
#define IER_TMR_IE 0x80 /* Timer event */
|
||||
|
||||
#define FCR 0x02 /* (write only) */
|
||||
#define FCR_FIFO_EN 0x01 /* Enable FIFO's */
|
||||
#define FCR_RXSR 0x02 /* Rx FIFO soft reset */
|
||||
#define FCR_TXSR 0x04 /* Tx FIFO soft reset */
|
||||
#define FCR_RXTH 0x40 /* Rx FIFO threshold (set to 16) */
|
||||
#define FCR_TXTH 0x20 /* Tx FIFO threshold (set to 17) */
|
||||
|
||||
#define EIR 0x02 /* (read only) */
|
||||
#define EIR_RXHDL_EV 0x01
|
||||
#define EIR_TXLDL_EV 0x02
|
||||
#define EIR_LS_EV 0x04
|
||||
#define EIR_DMA_EV 0x10
|
||||
#define EIR_TXEMP_EV 0x20
|
||||
#define EIR_SFIF_EV 0x40
|
||||
#define EIR_TMR_EV 0x80
|
||||
|
||||
#define LCR 0x03 /* Link control register */
|
||||
#define LCR_WLS_8 0x03 /* 8 bits */
|
||||
|
||||
#define BSR 0x03 /* Bank select register */
|
||||
#define BSR_BKSE 0x80
|
||||
#define BANK0 LCR_WLS_8 /* Must make sure that we set 8N1 */
|
||||
#define BANK1 0x80
|
||||
#define BANK2 0xe0
|
||||
#define BANK3 0xe4
|
||||
#define BANK4 0xe8
|
||||
#define BANK5 0xec
|
||||
#define BANK6 0xf0
|
||||
#define BANK7 0xf4
|
||||
|
||||
#define MCR 0x04 /* Mode Control Register */
|
||||
#define MCR_MODE_MASK ~(0xd0)
|
||||
#define MCR_UART 0x00
|
||||
#define MCR_RESERVED 0x20
|
||||
#define MCR_SHARP_IR 0x40
|
||||
#define MCR_SIR 0x60
|
||||
#define MCR_MIR 0x80
|
||||
#define MCR_FIR 0xa0
|
||||
#define MCR_CEIR 0xb0
|
||||
#define MCR_IR_PLS 0x10
|
||||
#define MCR_DMA_EN 0x04
|
||||
#define MCR_EN_IRQ 0x08
|
||||
#define MCR_TX_DFR 0x08
|
||||
|
||||
#define LSR 0x05 /* Link status register */
|
||||
#define LSR_RXDA 0x01 /* Receiver data available */
|
||||
#define LSR_TXRDY 0x20 /* Transmitter ready */
|
||||
#define LSR_TXEMP 0x40 /* Transmitter empty */
|
||||
|
||||
#define ASCR 0x07 /* Auxiliary Status and Control Register */
|
||||
#define ASCR_RXF_TOUT 0x01 /* Rx FIFO timeout */
|
||||
#define ASCR_FEND_INF 0x02 /* Frame end bytes in rx FIFO */
|
||||
#define ASCR_S_EOT 0x04 /* Set end of transmission */
|
||||
#define ASCT_RXBSY 0x20 /* Rx busy */
|
||||
#define ASCR_TXUR 0x40 /* Transeiver underrun */
|
||||
#define ASCR_CTE 0x80 /* Clear timer event */
|
||||
|
||||
/* Bank 2 */
|
||||
#define BGDL 0x00 /* Baud Generator Divisor Port (Low Byte) */
|
||||
#define BGDH 0x01 /* Baud Generator Divisor Port (High Byte) */
|
||||
|
||||
#define ECR1 0x02 /* Extended Control Register 1 */
|
||||
#define ECR1_EXT_SL 0x01 /* Extended Mode Select */
|
||||
#define ECR1_DMANF 0x02 /* DMA Fairness */
|
||||
#define ECR1_DMATH 0x04 /* DMA Threshold */
|
||||
#define ECR1_DMASWP 0x08 /* DMA Swap */
|
||||
|
||||
#define EXCR2 0x04
|
||||
#define EXCR2_TFSIZ 0x01 /* Rx FIFO size = 32 */
|
||||
#define EXCR2_RFSIZ 0x04 /* Tx FIFO size = 32 */
|
||||
|
||||
#define TXFLV 0x06 /* Tx FIFO level */
|
||||
#define RXFLV 0x07 /* Rx FIFO level */
|
||||
|
||||
/* Bank 3 */
|
||||
#define MID 0x00
|
||||
|
||||
/* Bank 4 */
|
||||
#define TMRL 0x00 /* Timer low byte */
|
||||
#define TMRH 0x01 /* Timer high byte */
|
||||
#define IRCR1 0x02 /* Infrared control register 1 */
|
||||
#define IRCR1_TMR_EN 0x01 /* Timer enable */
|
||||
|
||||
#define TFRLL 0x04
|
||||
#define TFRLH 0x05
|
||||
#define RFRLL 0x06
|
||||
#define RFRLH 0x07
|
||||
|
||||
/* Bank 5 */
|
||||
#define IRCR2 0x04 /* Infrared control register 2 */
|
||||
#define IRCR2_MDRS 0x04 /* MIR data rate select */
|
||||
#define IRCR2_FEND_MD 0x20 /* */
|
||||
|
||||
#define FRM_ST 0x05 /* Frame status FIFO */
|
||||
#define FRM_ST_VLD 0x80 /* Frame status FIFO data valid */
|
||||
#define FRM_ST_ERR_MSK 0x5f
|
||||
#define FRM_ST_LOST_FR 0x40 /* Frame lost */
|
||||
#define FRM_ST_MAX_LEN 0x10 /* Max frame len exceeded */
|
||||
#define FRM_ST_PHY_ERR 0x08 /* Physical layer error */
|
||||
#define FRM_ST_BAD_CRC 0x04
|
||||
#define FRM_ST_OVR1 0x02 /* Rx FIFO overrun */
|
||||
#define FRM_ST_OVR2 0x01 /* Frame status FIFO overrun */
|
||||
|
||||
#define RFLFL 0x06
|
||||
#define RFLFH 0x07
|
||||
|
||||
/* Bank 6 */
|
||||
#define IR_CFG2 0x00
|
||||
#define IR_CFG2_DIS_CRC 0x02
|
||||
|
||||
/* Bank 7 */
|
||||
#define IRM_CR 0x07 /* Infrared module control register */
|
||||
#define IRM_CR_IRX_MSL 0x40
|
||||
#define IRM_CR_AF_MNT 0x80 /* Automatic format */
|
||||
|
||||
/* NSC chip information */
|
||||
struct nsc_chip {
|
||||
char *name; /* Name of chipset */
|
||||
int cfg[3]; /* Config registers */
|
||||
u_int8_t cid_index; /* Chip identification index reg */
|
||||
u_int8_t cid_value; /* Chip identification expected value */
|
||||
u_int8_t cid_mask; /* Chip identification revision mask */
|
||||
|
||||
/* Functions for probing and initializing the specific chip */
|
||||
int (*probe)(struct nsc_chip *chip, chipio_t *info);
|
||||
int (*init)(struct nsc_chip *chip, chipio_t *info);
|
||||
};
|
||||
typedef struct nsc_chip nsc_chip_t;
|
||||
|
||||
/* For storing entries in the status FIFO */
|
||||
struct st_fifo_entry {
|
||||
int status;
|
||||
int len;
|
||||
};
|
||||
|
||||
#define MAX_TX_WINDOW 7
|
||||
#define MAX_RX_WINDOW 7
|
||||
|
||||
struct st_fifo {
|
||||
struct st_fifo_entry entries[MAX_RX_WINDOW];
|
||||
int pending_bytes;
|
||||
int head;
|
||||
int tail;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct frame_cb {
|
||||
void *start; /* Start of frame in DMA mem */
|
||||
int len; /* Length of frame in DMA mem */
|
||||
};
|
||||
|
||||
struct tx_fifo {
|
||||
struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */
|
||||
int ptr; /* Currently being sent */
|
||||
int len; /* Length of queue */
|
||||
int free; /* Next free slot */
|
||||
void *tail; /* Next free start in DMA mem */
|
||||
};
|
||||
|
||||
/* Private data for each instance */
|
||||
struct nsc_ircc_cb {
|
||||
struct st_fifo st_fifo; /* Info about received frames */
|
||||
struct tx_fifo tx_fifo; /* Info about frames to be transmitted */
|
||||
|
||||
struct net_device *netdev; /* Yes! we are some kind of netdevice */
|
||||
|
||||
struct irlap_cb *irlap; /* The link layer we are binded to */
|
||||
struct qos_info qos; /* QoS capabilities for this device */
|
||||
|
||||
chipio_t io; /* IrDA controller information */
|
||||
iobuff_t tx_buff; /* Transmit buffer */
|
||||
iobuff_t rx_buff; /* Receive buffer */
|
||||
dma_addr_t tx_buff_dma;
|
||||
dma_addr_t rx_buff_dma;
|
||||
|
||||
__u8 ier; /* Interrupt enable register */
|
||||
|
||||
ktime_t stamp;
|
||||
|
||||
spinlock_t lock; /* For serializing operations */
|
||||
|
||||
__u32 new_speed;
|
||||
int index; /* Instance index */
|
||||
|
||||
struct platform_device *pldev;
|
||||
};
|
||||
|
||||
static inline void switch_bank(int iobase, int bank)
|
||||
{
|
||||
outb(bank, iobase+BSR);
|
||||
}
|
||||
|
||||
#endif /* NSC_IRCC_H */
|
|
@ -1,146 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: old_belkin.c
|
||||
* Version: 1.1
|
||||
* Description: Driver for the Belkin (old) SmartBeam dongle
|
||||
* Status: Experimental...
|
||||
* Author: Jean Tourrilhes <jt@hpl.hp.com>
|
||||
* Created at: 22/11/99
|
||||
* Modified at: Fri Dec 17 09:13:32 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999 Jean Tourrilhes, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
// #include <net/irda/irda_device.h>
|
||||
|
||||
#include "sir-dev.h"
|
||||
|
||||
/*
|
||||
* Belkin is selling a dongle called the SmartBeam.
|
||||
* In fact, there is two hardware version of this dongle, of course with
|
||||
* the same name and looking the exactly same (grrr...).
|
||||
* I guess that I've got the old one, because inside I don't have
|
||||
* a jumper for IrDA/ASK...
|
||||
*
|
||||
* As far as I can make it from info on their web site, the old dongle
|
||||
* support only 9600 b/s, which make our life much simpler as far as
|
||||
* the driver is concerned, but you might not like it very much ;-)
|
||||
* The new SmartBeam does 115 kb/s, and I've not tested it...
|
||||
*
|
||||
* Belkin claim that the correct driver for the old dongle (in Windows)
|
||||
* is the generic Parallax 9500a driver, but the Linux LiteLink driver
|
||||
* fails for me (probably because Linux-IrDA doesn't rate fallback),
|
||||
* so I created this really dumb driver...
|
||||
*
|
||||
* In fact, this driver doesn't do much. The only thing it does is to
|
||||
* prevent Linux-IrDA to use any other speed than 9600 b/s ;-) This
|
||||
* driver is called "old_belkin" so that when the new SmartBeam is supported
|
||||
* its driver can be called "belkin" instead of "new_belkin".
|
||||
*
|
||||
* Note : this driver was written without any info/help from Belkin,
|
||||
* so a lot of info here might be totally wrong. Blame me ;-)
|
||||
*/
|
||||
|
||||
static int old_belkin_open(struct sir_dev *dev);
|
||||
static int old_belkin_close(struct sir_dev *dev);
|
||||
static int old_belkin_change_speed(struct sir_dev *dev, unsigned speed);
|
||||
static int old_belkin_reset(struct sir_dev *dev);
|
||||
|
||||
static struct dongle_driver old_belkin = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "Old Belkin SmartBeam",
|
||||
.type = IRDA_OLD_BELKIN_DONGLE,
|
||||
.open = old_belkin_open,
|
||||
.close = old_belkin_close,
|
||||
.reset = old_belkin_reset,
|
||||
.set_speed = old_belkin_change_speed,
|
||||
};
|
||||
|
||||
static int __init old_belkin_sir_init(void)
|
||||
{
|
||||
return irda_register_dongle(&old_belkin);
|
||||
}
|
||||
|
||||
static void __exit old_belkin_sir_cleanup(void)
|
||||
{
|
||||
irda_unregister_dongle(&old_belkin);
|
||||
}
|
||||
|
||||
static int old_belkin_open(struct sir_dev *dev)
|
||||
{
|
||||
struct qos_info *qos = &dev->qos;
|
||||
|
||||
/* Power on dongle */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
/* Not too fast, please... */
|
||||
qos->baud_rate.bits &= IR_9600;
|
||||
/* Needs at least 10 ms (totally wild guess, can do probably better) */
|
||||
qos->min_turn_time.bits = 0x01;
|
||||
irda_qos_bits_to_value(qos);
|
||||
|
||||
/* irda thread waits 50 msec for power settling */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int old_belkin_close(struct sir_dev *dev)
|
||||
{
|
||||
/* Power off dongle */
|
||||
sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function old_belkin_change_speed (task)
|
||||
*
|
||||
* With only one speed available, not much to do...
|
||||
*/
|
||||
static int old_belkin_change_speed(struct sir_dev *dev, unsigned speed)
|
||||
{
|
||||
dev->speed = 9600;
|
||||
return (speed==dev->speed) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function old_belkin_reset (task)
|
||||
*
|
||||
* Reset the Old-Belkin type dongle.
|
||||
*
|
||||
*/
|
||||
static int old_belkin_reset(struct sir_dev *dev)
|
||||
{
|
||||
/* This dongles speed "defaults" to 9600 bps ;-) */
|
||||
dev->speed = 9600;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Jean Tourrilhes <jt@hpl.hp.com>");
|
||||
MODULE_DESCRIPTION("Belkin (old) SmartBeam dongle driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("irda-dongle-7"); /* IRDA_OLD_BELKIN_DONGLE */
|
||||
|
||||
module_init(old_belkin_sir_init);
|
||||
module_exit(old_belkin_sir_cleanup);
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,810 +0,0 @@
|
|||
/*
|
||||
* SuperH IrDA Driver
|
||||
*
|
||||
* Copyright (C) 2009 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <morimoto.kuninori@renesas.com>
|
||||
*
|
||||
* Based on bfin_sir.c
|
||||
* Copyright 2006-2009 Analog Devices Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/irda/wrapper.h>
|
||||
#include <net/irda/irda_device.h>
|
||||
#include <asm/clock.h>
|
||||
|
||||
#define DRIVER_NAME "sh_sir"
|
||||
|
||||
#define RX_PHASE (1 << 0)
|
||||
#define TX_PHASE (1 << 1)
|
||||
#define TX_COMP_PHASE (1 << 2) /* tx complete */
|
||||
#define NONE_PHASE (1 << 31)
|
||||
|
||||
#define IRIF_RINTCLR 0x0016 /* DMA rx interrupt source clear */
|
||||
#define IRIF_TINTCLR 0x0018 /* DMA tx interrupt source clear */
|
||||
#define IRIF_SIR0 0x0020 /* IrDA-SIR10 control */
|
||||
#define IRIF_SIR1 0x0022 /* IrDA-SIR10 baudrate error correction */
|
||||
#define IRIF_SIR2 0x0024 /* IrDA-SIR10 baudrate count */
|
||||
#define IRIF_SIR3 0x0026 /* IrDA-SIR10 status */
|
||||
#define IRIF_SIR_FRM 0x0028 /* Hardware frame processing set */
|
||||
#define IRIF_SIR_EOF 0x002A /* EOF value */
|
||||
#define IRIF_SIR_FLG 0x002C /* Flag clear */
|
||||
#define IRIF_UART_STS2 0x002E /* UART status 2 */
|
||||
#define IRIF_UART0 0x0030 /* UART control */
|
||||
#define IRIF_UART1 0x0032 /* UART status */
|
||||
#define IRIF_UART2 0x0034 /* UART mode */
|
||||
#define IRIF_UART3 0x0036 /* UART transmit data */
|
||||
#define IRIF_UART4 0x0038 /* UART receive data */
|
||||
#define IRIF_UART5 0x003A /* UART interrupt mask */
|
||||
#define IRIF_UART6 0x003C /* UART baud rate error correction */
|
||||
#define IRIF_UART7 0x003E /* UART baud rate count set */
|
||||
#define IRIF_CRC0 0x0040 /* CRC engine control */
|
||||
#define IRIF_CRC1 0x0042 /* CRC engine input data */
|
||||
#define IRIF_CRC2 0x0044 /* CRC engine calculation */
|
||||
#define IRIF_CRC3 0x0046 /* CRC engine output data 1 */
|
||||
#define IRIF_CRC4 0x0048 /* CRC engine output data 2 */
|
||||
|
||||
/* IRIF_SIR0 */
|
||||
#define IRTPW (1 << 1) /* transmit pulse width select */
|
||||
#define IRERRC (1 << 0) /* Clear receive pulse width error */
|
||||
|
||||
/* IRIF_SIR3 */
|
||||
#define IRERR (1 << 0) /* received pulse width Error */
|
||||
|
||||
/* IRIF_SIR_FRM */
|
||||
#define EOFD (1 << 9) /* EOF detection flag */
|
||||
#define FRER (1 << 8) /* Frame Error bit */
|
||||
#define FRP (1 << 0) /* Frame processing set */
|
||||
|
||||
/* IRIF_UART_STS2 */
|
||||
#define IRSME (1 << 6) /* Receive Sum Error flag */
|
||||
#define IROVE (1 << 5) /* Receive Overrun Error flag */
|
||||
#define IRFRE (1 << 4) /* Receive Framing Error flag */
|
||||
#define IRPRE (1 << 3) /* Receive Parity Error flag */
|
||||
|
||||
/* IRIF_UART0_*/
|
||||
#define TBEC (1 << 2) /* Transmit Data Clear */
|
||||
#define RIE (1 << 1) /* Receive Enable */
|
||||
#define TIE (1 << 0) /* Transmit Enable */
|
||||
|
||||
/* IRIF_UART1 */
|
||||
#define URSME (1 << 6) /* Receive Sum Error Flag */
|
||||
#define UROVE (1 << 5) /* Receive Overrun Error Flag */
|
||||
#define URFRE (1 << 4) /* Receive Framing Error Flag */
|
||||
#define URPRE (1 << 3) /* Receive Parity Error Flag */
|
||||
#define RBF (1 << 2) /* Receive Buffer Full Flag */
|
||||
#define TSBE (1 << 1) /* Transmit Shift Buffer Empty Flag */
|
||||
#define TBE (1 << 0) /* Transmit Buffer Empty flag */
|
||||
#define TBCOMP (TSBE | TBE)
|
||||
|
||||
/* IRIF_UART5 */
|
||||
#define RSEIM (1 << 6) /* Receive Sum Error Flag IRQ Mask */
|
||||
#define RBFIM (1 << 2) /* Receive Buffer Full Flag IRQ Mask */
|
||||
#define TSBEIM (1 << 1) /* Transmit Shift Buffer Empty Flag IRQ Mask */
|
||||
#define TBEIM (1 << 0) /* Transmit Buffer Empty Flag IRQ Mask */
|
||||
#define RX_MASK (RSEIM | RBFIM)
|
||||
|
||||
/* IRIF_CRC0 */
|
||||
#define CRC_RST (1 << 15) /* CRC Engine Reset */
|
||||
#define CRC_CT_MASK 0x0FFF
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
structure
|
||||
|
||||
|
||||
************************************************************************/
|
||||
struct sh_sir_self {
|
||||
void __iomem *membase;
|
||||
unsigned int irq;
|
||||
struct clk *clk;
|
||||
|
||||
struct net_device *ndev;
|
||||
|
||||
struct irlap_cb *irlap;
|
||||
struct qos_info qos;
|
||||
|
||||
iobuff_t tx_buff;
|
||||
iobuff_t rx_buff;
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
common function
|
||||
|
||||
|
||||
************************************************************************/
|
||||
static void sh_sir_write(struct sh_sir_self *self, u32 offset, u16 data)
|
||||
{
|
||||
iowrite16(data, self->membase + offset);
|
||||
}
|
||||
|
||||
static u16 sh_sir_read(struct sh_sir_self *self, u32 offset)
|
||||
{
|
||||
return ioread16(self->membase + offset);
|
||||
}
|
||||
|
||||
static void sh_sir_update_bits(struct sh_sir_self *self, u32 offset,
|
||||
u16 mask, u16 data)
|
||||
{
|
||||
u16 old, new;
|
||||
|
||||
old = sh_sir_read(self, offset);
|
||||
new = (old & ~mask) | data;
|
||||
if (old != new)
|
||||
sh_sir_write(self, offset, new);
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
CRC function
|
||||
|
||||
|
||||
************************************************************************/
|
||||
static void sh_sir_crc_reset(struct sh_sir_self *self)
|
||||
{
|
||||
sh_sir_write(self, IRIF_CRC0, CRC_RST);
|
||||
}
|
||||
|
||||
static void sh_sir_crc_add(struct sh_sir_self *self, u8 data)
|
||||
{
|
||||
sh_sir_write(self, IRIF_CRC1, (u16)data);
|
||||
}
|
||||
|
||||
static u16 sh_sir_crc_cnt(struct sh_sir_self *self)
|
||||
{
|
||||
return CRC_CT_MASK & sh_sir_read(self, IRIF_CRC0);
|
||||
}
|
||||
|
||||
static u16 sh_sir_crc_out(struct sh_sir_self *self)
|
||||
{
|
||||
return sh_sir_read(self, IRIF_CRC4);
|
||||
}
|
||||
|
||||
static int sh_sir_crc_init(struct sh_sir_self *self)
|
||||
{
|
||||
struct device *dev = &self->ndev->dev;
|
||||
int ret = -EIO;
|
||||
u16 val;
|
||||
|
||||
sh_sir_crc_reset(self);
|
||||
|
||||
sh_sir_crc_add(self, 0xCC);
|
||||
sh_sir_crc_add(self, 0xF5);
|
||||
sh_sir_crc_add(self, 0xF1);
|
||||
sh_sir_crc_add(self, 0xA7);
|
||||
|
||||
val = sh_sir_crc_cnt(self);
|
||||
if (4 != val) {
|
||||
dev_err(dev, "CRC count error %x\n", val);
|
||||
goto crc_init_out;
|
||||
}
|
||||
|
||||
val = sh_sir_crc_out(self);
|
||||
if (0x51DF != val) {
|
||||
dev_err(dev, "CRC result error%x\n", val);
|
||||
goto crc_init_out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
crc_init_out:
|
||||
|
||||
sh_sir_crc_reset(self);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
baud rate functions
|
||||
|
||||
|
||||
************************************************************************/
|
||||
#define SCLK_BASE 1843200 /* 1.8432MHz */
|
||||
|
||||
static u32 sh_sir_find_sclk(struct clk *irda_clk)
|
||||
{
|
||||
struct cpufreq_frequency_table *freq_table = irda_clk->freq_table;
|
||||
struct cpufreq_frequency_table *pos;
|
||||
struct clk *pclk = clk_get(NULL, "peripheral_clk");
|
||||
u32 limit, min = 0xffffffff, tmp;
|
||||
int index = 0;
|
||||
|
||||
limit = clk_get_rate(pclk);
|
||||
clk_put(pclk);
|
||||
|
||||
/* IrDA can not set over peripheral_clk */
|
||||
cpufreq_for_each_valid_entry_idx(pos, freq_table, index) {
|
||||
u32 freq = pos->frequency;
|
||||
|
||||
/* IrDA should not over peripheral_clk */
|
||||
if (freq > limit)
|
||||
continue;
|
||||
|
||||
tmp = freq % SCLK_BASE;
|
||||
if (tmp < min) {
|
||||
min = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return freq_table[index].frequency;
|
||||
}
|
||||
|
||||
#define ERR_ROUNDING(a) ((a + 5000) / 10000)
|
||||
static int sh_sir_set_baudrate(struct sh_sir_self *self, u32 baudrate)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct device *dev = &self->ndev->dev;
|
||||
u32 rate;
|
||||
u16 uabca, uabc;
|
||||
u16 irbca, irbc;
|
||||
u32 min, rerr, tmp;
|
||||
int i;
|
||||
|
||||
/* Baud Rate Error Correction x 10000 */
|
||||
u32 rate_err_array[] = {
|
||||
0, 625, 1250, 1875,
|
||||
2500, 3125, 3750, 4375,
|
||||
5000, 5625, 6250, 6875,
|
||||
7500, 8125, 8750, 9375,
|
||||
};
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
*
|
||||
* it support 9600 only now
|
||||
*/
|
||||
switch (baudrate) {
|
||||
case 9600:
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "un-supported baudrate %d\n", baudrate);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
clk = clk_get(NULL, "irda_clk");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "can not get irda_clk\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
clk_set_rate(clk, sh_sir_find_sclk(clk));
|
||||
rate = clk_get_rate(clk);
|
||||
clk_put(clk);
|
||||
|
||||
dev_dbg(dev, "selected sclk = %d\n", rate);
|
||||
|
||||
/*
|
||||
* CALCULATION
|
||||
*
|
||||
* 1843200 = system rate / (irbca + (irbc + 1))
|
||||
*/
|
||||
|
||||
irbc = rate / SCLK_BASE;
|
||||
|
||||
tmp = rate - (SCLK_BASE * irbc);
|
||||
tmp *= 10000;
|
||||
|
||||
rerr = tmp / SCLK_BASE;
|
||||
|
||||
min = 0xffffffff;
|
||||
irbca = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(rate_err_array); i++) {
|
||||
tmp = abs(rate_err_array[i] - rerr);
|
||||
if (min > tmp) {
|
||||
min = tmp;
|
||||
irbca = i;
|
||||
}
|
||||
}
|
||||
|
||||
tmp = rate / (irbc + ERR_ROUNDING(rate_err_array[irbca]));
|
||||
if ((SCLK_BASE / 100) < abs(tmp - SCLK_BASE))
|
||||
dev_warn(dev, "IrDA freq error margin over %d\n", tmp);
|
||||
|
||||
dev_dbg(dev, "target = %d, result = %d, infrared = %d.%d\n",
|
||||
SCLK_BASE, tmp, irbc, rate_err_array[irbca]);
|
||||
|
||||
irbca = (irbca & 0xF) << 4;
|
||||
irbc = (irbc - 1) & 0xF;
|
||||
|
||||
if (!irbc) {
|
||||
dev_err(dev, "sh_sir can not set 0 in IRIF_SIR2\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
sh_sir_write(self, IRIF_SIR0, IRTPW | IRERRC);
|
||||
sh_sir_write(self, IRIF_SIR1, irbca);
|
||||
sh_sir_write(self, IRIF_SIR2, irbc);
|
||||
|
||||
/*
|
||||
* CALCULATION
|
||||
*
|
||||
* BaudRate[bps] = system rate / (uabca + (uabc + 1) x 16)
|
||||
*/
|
||||
|
||||
uabc = rate / baudrate;
|
||||
uabc = (uabc / 16) - 1;
|
||||
uabc = (uabc + 1) * 16;
|
||||
|
||||
tmp = rate - (uabc * baudrate);
|
||||
tmp *= 10000;
|
||||
|
||||
rerr = tmp / baudrate;
|
||||
|
||||
min = 0xffffffff;
|
||||
uabca = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(rate_err_array); i++) {
|
||||
tmp = abs(rate_err_array[i] - rerr);
|
||||
if (min > tmp) {
|
||||
min = tmp;
|
||||
uabca = i;
|
||||
}
|
||||
}
|
||||
|
||||
tmp = rate / (uabc + ERR_ROUNDING(rate_err_array[uabca]));
|
||||
if ((baudrate / 100) < abs(tmp - baudrate))
|
||||
dev_warn(dev, "UART freq error margin over %d\n", tmp);
|
||||
|
||||
dev_dbg(dev, "target = %d, result = %d, uart = %d.%d\n",
|
||||
baudrate, tmp,
|
||||
uabc, rate_err_array[uabca]);
|
||||
|
||||
uabca = (uabca & 0xF) << 4;
|
||||
uabc = (uabc / 16) - 1;
|
||||
|
||||
sh_sir_write(self, IRIF_UART6, uabca);
|
||||
sh_sir_write(self, IRIF_UART7, uabc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
iobuf function
|
||||
|
||||
|
||||
************************************************************************/
|
||||
static int __sh_sir_init_iobuf(iobuff_t *io, int size)
|
||||
{
|
||||
io->head = kmalloc(size, GFP_KERNEL);
|
||||
if (!io->head)
|
||||
return -ENOMEM;
|
||||
|
||||
io->truesize = size;
|
||||
io->in_frame = FALSE;
|
||||
io->state = OUTSIDE_FRAME;
|
||||
io->data = io->head;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sh_sir_remove_iobuf(struct sh_sir_self *self)
|
||||
{
|
||||
kfree(self->rx_buff.head);
|
||||
kfree(self->tx_buff.head);
|
||||
|
||||
self->rx_buff.head = NULL;
|
||||
self->tx_buff.head = NULL;
|
||||
}
|
||||
|
||||
static int sh_sir_init_iobuf(struct sh_sir_self *self, int rxsize, int txsize)
|
||||
{
|
||||
int err = -ENOMEM;
|
||||
|
||||
if (self->rx_buff.head ||
|
||||
self->tx_buff.head) {
|
||||
dev_err(&self->ndev->dev, "iobuff has already existed.");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = __sh_sir_init_iobuf(&self->rx_buff, rxsize);
|
||||
if (err)
|
||||
goto iobuf_err;
|
||||
|
||||
err = __sh_sir_init_iobuf(&self->tx_buff, txsize);
|
||||
|
||||
iobuf_err:
|
||||
if (err)
|
||||
sh_sir_remove_iobuf(self);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
status function
|
||||
|
||||
|
||||
************************************************************************/
|
||||
static void sh_sir_clear_all_err(struct sh_sir_self *self)
|
||||
{
|
||||
/* Clear error flag for receive pulse width */
|
||||
sh_sir_update_bits(self, IRIF_SIR0, IRERRC, IRERRC);
|
||||
|
||||
/* Clear frame / EOF error flag */
|
||||
sh_sir_write(self, IRIF_SIR_FLG, 0xffff);
|
||||
|
||||
/* Clear all status error */
|
||||
sh_sir_write(self, IRIF_UART_STS2, 0);
|
||||
}
|
||||
|
||||
static void sh_sir_set_phase(struct sh_sir_self *self, int phase)
|
||||
{
|
||||
u16 uart5 = 0;
|
||||
u16 uart0 = 0;
|
||||
|
||||
switch (phase) {
|
||||
case TX_PHASE:
|
||||
uart5 = TBEIM;
|
||||
uart0 = TBEC | TIE;
|
||||
break;
|
||||
case TX_COMP_PHASE:
|
||||
uart5 = TSBEIM;
|
||||
uart0 = TIE;
|
||||
break;
|
||||
case RX_PHASE:
|
||||
uart5 = RX_MASK;
|
||||
uart0 = RIE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sh_sir_write(self, IRIF_UART5, uart5);
|
||||
sh_sir_write(self, IRIF_UART0, uart0);
|
||||
}
|
||||
|
||||
static int sh_sir_is_which_phase(struct sh_sir_self *self)
|
||||
{
|
||||
u16 val = sh_sir_read(self, IRIF_UART5);
|
||||
|
||||
if (val & TBEIM)
|
||||
return TX_PHASE;
|
||||
|
||||
if (val & TSBEIM)
|
||||
return TX_COMP_PHASE;
|
||||
|
||||
if (val & RX_MASK)
|
||||
return RX_PHASE;
|
||||
|
||||
return NONE_PHASE;
|
||||
}
|
||||
|
||||
static void sh_sir_tx(struct sh_sir_self *self, int phase)
|
||||
{
|
||||
switch (phase) {
|
||||
case TX_PHASE:
|
||||
if (0 >= self->tx_buff.len) {
|
||||
sh_sir_set_phase(self, TX_COMP_PHASE);
|
||||
} else {
|
||||
sh_sir_write(self, IRIF_UART3, self->tx_buff.data[0]);
|
||||
self->tx_buff.len--;
|
||||
self->tx_buff.data++;
|
||||
}
|
||||
break;
|
||||
case TX_COMP_PHASE:
|
||||
sh_sir_set_phase(self, RX_PHASE);
|
||||
netif_wake_queue(self->ndev);
|
||||
break;
|
||||
default:
|
||||
dev_err(&self->ndev->dev, "should not happen\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int sh_sir_read_data(struct sh_sir_self *self)
|
||||
{
|
||||
u16 val = 0;
|
||||
int timeout = 1024;
|
||||
|
||||
while (timeout--) {
|
||||
val = sh_sir_read(self, IRIF_UART1);
|
||||
|
||||
/* data get */
|
||||
if (val & RBF) {
|
||||
if (val & (URSME | UROVE | URFRE | URPRE))
|
||||
break;
|
||||
|
||||
return (int)sh_sir_read(self, IRIF_UART4);
|
||||
}
|
||||
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
dev_err(&self->ndev->dev, "UART1 %04x : STATUS %04x\n",
|
||||
val, sh_sir_read(self, IRIF_UART_STS2));
|
||||
|
||||
/* read data register for clear error */
|
||||
sh_sir_read(self, IRIF_UART4);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void sh_sir_rx(struct sh_sir_self *self)
|
||||
{
|
||||
int timeout = 1024;
|
||||
int data;
|
||||
|
||||
while (timeout--) {
|
||||
data = sh_sir_read_data(self);
|
||||
if (data < 0)
|
||||
break;
|
||||
|
||||
async_unwrap_char(self->ndev, &self->ndev->stats,
|
||||
&self->rx_buff, (u8)data);
|
||||
|
||||
if (EOFD & sh_sir_read(self, IRIF_SIR_FRM))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t sh_sir_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct sh_sir_self *self = dev_id;
|
||||
struct device *dev = &self->ndev->dev;
|
||||
int phase = sh_sir_is_which_phase(self);
|
||||
|
||||
switch (phase) {
|
||||
case TX_COMP_PHASE:
|
||||
case TX_PHASE:
|
||||
sh_sir_tx(self, phase);
|
||||
break;
|
||||
case RX_PHASE:
|
||||
if (sh_sir_read(self, IRIF_SIR3))
|
||||
dev_err(dev, "rcv pulse width error occurred\n");
|
||||
|
||||
sh_sir_rx(self);
|
||||
sh_sir_clear_all_err(self);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "unknown interrupt\n");
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
net_device_ops function
|
||||
|
||||
|
||||
************************************************************************/
|
||||
static int sh_sir_hard_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||
{
|
||||
struct sh_sir_self *self = netdev_priv(ndev);
|
||||
int speed = irda_get_next_speed(skb);
|
||||
|
||||
if ((0 < speed) &&
|
||||
(9600 != speed)) {
|
||||
dev_err(&ndev->dev, "support 9600 only (%d)\n", speed);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
netif_stop_queue(ndev);
|
||||
|
||||
self->tx_buff.data = self->tx_buff.head;
|
||||
self->tx_buff.len = 0;
|
||||
if (skb->len)
|
||||
self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data,
|
||||
self->tx_buff.truesize);
|
||||
|
||||
sh_sir_set_phase(self, TX_PHASE);
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_sir_ioctl(struct net_device *ndev, struct ifreq *ifreq, int cmd)
|
||||
{
|
||||
/*
|
||||
* FIXME
|
||||
*
|
||||
* This function is needed for irda framework.
|
||||
* But nothing to do now
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct net_device_stats *sh_sir_stats(struct net_device *ndev)
|
||||
{
|
||||
struct sh_sir_self *self = netdev_priv(ndev);
|
||||
|
||||
return &self->ndev->stats;
|
||||
}
|
||||
|
||||
static int sh_sir_open(struct net_device *ndev)
|
||||
{
|
||||
struct sh_sir_self *self = netdev_priv(ndev);
|
||||
int err;
|
||||
|
||||
clk_enable(self->clk);
|
||||
err = sh_sir_crc_init(self);
|
||||
if (err)
|
||||
goto open_err;
|
||||
|
||||
sh_sir_set_baudrate(self, 9600);
|
||||
|
||||
self->irlap = irlap_open(ndev, &self->qos, DRIVER_NAME);
|
||||
if (!self->irlap) {
|
||||
err = -ENODEV;
|
||||
goto open_err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now enable the interrupt then start the queue
|
||||
*/
|
||||
sh_sir_update_bits(self, IRIF_SIR_FRM, FRP, FRP);
|
||||
sh_sir_read(self, IRIF_UART1); /* flag clear */
|
||||
sh_sir_read(self, IRIF_UART4); /* flag clear */
|
||||
sh_sir_set_phase(self, RX_PHASE);
|
||||
|
||||
netif_start_queue(ndev);
|
||||
|
||||
dev_info(&self->ndev->dev, "opened\n");
|
||||
|
||||
return 0;
|
||||
|
||||
open_err:
|
||||
clk_disable(self->clk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sh_sir_stop(struct net_device *ndev)
|
||||
{
|
||||
struct sh_sir_self *self = netdev_priv(ndev);
|
||||
|
||||
/* Stop IrLAP */
|
||||
if (self->irlap) {
|
||||
irlap_close(self->irlap);
|
||||
self->irlap = NULL;
|
||||
}
|
||||
|
||||
netif_stop_queue(ndev);
|
||||
|
||||
dev_info(&ndev->dev, "stopped\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct net_device_ops sh_sir_ndo = {
|
||||
.ndo_open = sh_sir_open,
|
||||
.ndo_stop = sh_sir_stop,
|
||||
.ndo_start_xmit = sh_sir_hard_xmit,
|
||||
.ndo_do_ioctl = sh_sir_ioctl,
|
||||
.ndo_get_stats = sh_sir_stats,
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
platform_driver function
|
||||
|
||||
|
||||
************************************************************************/
|
||||
static int sh_sir_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *ndev;
|
||||
struct sh_sir_self *self;
|
||||
struct resource *res;
|
||||
char clk_name[8];
|
||||
int irq;
|
||||
int err = -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (!res || irq < 0) {
|
||||
dev_err(&pdev->dev, "Not enough platform resources.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ndev = alloc_irdadev(sizeof(*self));
|
||||
if (!ndev)
|
||||
goto exit;
|
||||
|
||||
self = netdev_priv(ndev);
|
||||
self->membase = ioremap_nocache(res->start, resource_size(res));
|
||||
if (!self->membase) {
|
||||
err = -ENXIO;
|
||||
dev_err(&pdev->dev, "Unable to ioremap.\n");
|
||||
goto err_mem_1;
|
||||
}
|
||||
|
||||
err = sh_sir_init_iobuf(self, IRDA_SKB_MAX_MTU, IRDA_SIR_MAX_FRAME);
|
||||
if (err)
|
||||
goto err_mem_2;
|
||||
|
||||
snprintf(clk_name, sizeof(clk_name), "irda%d", pdev->id);
|
||||
self->clk = clk_get(&pdev->dev, clk_name);
|
||||
if (IS_ERR(self->clk)) {
|
||||
dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
|
||||
err = -ENODEV;
|
||||
goto err_mem_3;
|
||||
}
|
||||
|
||||
irda_init_max_qos_capabilies(&self->qos);
|
||||
|
||||
ndev->netdev_ops = &sh_sir_ndo;
|
||||
ndev->irq = irq;
|
||||
|
||||
self->ndev = ndev;
|
||||
self->qos.baud_rate.bits &= IR_9600; /* FIXME */
|
||||
self->qos.min_turn_time.bits = 1; /* 10 ms or more */
|
||||
|
||||
irda_qos_bits_to_value(&self->qos);
|
||||
|
||||
err = register_netdev(ndev);
|
||||
if (err)
|
||||
goto err_mem_4;
|
||||
|
||||
platform_set_drvdata(pdev, ndev);
|
||||
err = devm_request_irq(&pdev->dev, irq, sh_sir_irq, 0, "sh_sir", self);
|
||||
if (err) {
|
||||
dev_warn(&pdev->dev, "Unable to attach sh_sir interrupt\n");
|
||||
goto err_mem_4;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "SuperH IrDA probed\n");
|
||||
|
||||
goto exit;
|
||||
|
||||
err_mem_4:
|
||||
clk_put(self->clk);
|
||||
err_mem_3:
|
||||
sh_sir_remove_iobuf(self);
|
||||
err_mem_2:
|
||||
iounmap(self->membase);
|
||||
err_mem_1:
|
||||
free_netdev(ndev);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sh_sir_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *ndev = platform_get_drvdata(pdev);
|
||||
struct sh_sir_self *self = netdev_priv(ndev);
|
||||
|
||||
if (!self)
|
||||
return 0;
|
||||
|
||||
unregister_netdev(ndev);
|
||||
clk_put(self->clk);
|
||||
sh_sir_remove_iobuf(self);
|
||||
iounmap(self->membase);
|
||||
free_netdev(ndev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sh_sir_driver = {
|
||||
.probe = sh_sir_probe,
|
||||
.remove = sh_sir_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(sh_sir_driver);
|
||||
|
||||
MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
|
||||
MODULE_DESCRIPTION("SuperH IrDA driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,191 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* sir.h: include file for irda-sir device abstraction layer
|
||||
*
|
||||
* Copyright (c) 2002 Martin Diehl
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRDA_SIR_H
|
||||
#define IRDA_SIR_H
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irda_device.h> // iobuff_t
|
||||
|
||||
struct sir_fsm {
|
||||
struct semaphore sem;
|
||||
struct delayed_work work;
|
||||
unsigned state, substate;
|
||||
int param;
|
||||
int result;
|
||||
};
|
||||
|
||||
#define SIRDEV_STATE_WAIT_TX_COMPLETE 0x0100
|
||||
|
||||
/* substates for wait_tx_complete */
|
||||
#define SIRDEV_STATE_WAIT_XMIT 0x0101
|
||||
#define SIRDEV_STATE_WAIT_UNTIL_SENT 0x0102
|
||||
#define SIRDEV_STATE_TX_DONE 0x0103
|
||||
|
||||
#define SIRDEV_STATE_DONGLE_OPEN 0x0300
|
||||
|
||||
/* 0x0301-0x03ff reserved for individual dongle substates */
|
||||
|
||||
#define SIRDEV_STATE_DONGLE_CLOSE 0x0400
|
||||
|
||||
/* 0x0401-0x04ff reserved for individual dongle substates */
|
||||
|
||||
#define SIRDEV_STATE_SET_DTR_RTS 0x0500
|
||||
|
||||
#define SIRDEV_STATE_SET_SPEED 0x0700
|
||||
#define SIRDEV_STATE_DONGLE_CHECK 0x0800
|
||||
#define SIRDEV_STATE_DONGLE_RESET 0x0900
|
||||
|
||||
/* 0x0901-0x09ff reserved for individual dongle substates */
|
||||
|
||||
#define SIRDEV_STATE_DONGLE_SPEED 0x0a00
|
||||
/* 0x0a01-0x0aff reserved for individual dongle substates */
|
||||
|
||||
#define SIRDEV_STATE_PORT_SPEED 0x0b00
|
||||
#define SIRDEV_STATE_DONE 0x0c00
|
||||
#define SIRDEV_STATE_ERROR 0x0d00
|
||||
#define SIRDEV_STATE_COMPLETE 0x0e00
|
||||
|
||||
#define SIRDEV_STATE_DEAD 0xffff
|
||||
|
||||
|
||||
struct sir_dev;
|
||||
|
||||
struct dongle_driver {
|
||||
|
||||
struct module *owner;
|
||||
|
||||
const char *driver_name;
|
||||
|
||||
IRDA_DONGLE type;
|
||||
|
||||
int (*open)(struct sir_dev *dev);
|
||||
int (*close)(struct sir_dev *dev);
|
||||
int (*reset)(struct sir_dev *dev);
|
||||
int (*set_speed)(struct sir_dev *dev, unsigned speed);
|
||||
|
||||
struct list_head dongle_list;
|
||||
};
|
||||
|
||||
struct sir_driver {
|
||||
|
||||
struct module *owner;
|
||||
|
||||
const char *driver_name;
|
||||
|
||||
int qos_mtt_bits;
|
||||
|
||||
int (*chars_in_buffer)(struct sir_dev *dev);
|
||||
void (*wait_until_sent)(struct sir_dev *dev);
|
||||
int (*set_speed)(struct sir_dev *dev, unsigned speed);
|
||||
int (*set_dtr_rts)(struct sir_dev *dev, int dtr, int rts);
|
||||
|
||||
int (*do_write)(struct sir_dev *dev, const unsigned char *ptr, size_t len);
|
||||
|
||||
int (*start_dev)(struct sir_dev *dev);
|
||||
int (*stop_dev)(struct sir_dev *dev);
|
||||
};
|
||||
|
||||
|
||||
/* exported */
|
||||
|
||||
int irda_register_dongle(struct dongle_driver *new);
|
||||
int irda_unregister_dongle(struct dongle_driver *drv);
|
||||
|
||||
struct sir_dev *sirdev_get_instance(const struct sir_driver *drv,
|
||||
const char *name);
|
||||
int sirdev_put_instance(struct sir_dev *self);
|
||||
|
||||
int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type);
|
||||
void sirdev_write_complete(struct sir_dev *dev);
|
||||
int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count);
|
||||
|
||||
/* low level helpers for SIR device/dongle setup */
|
||||
int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len);
|
||||
int sirdev_raw_read(struct sir_dev *dev, char *buf, int len);
|
||||
int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts);
|
||||
|
||||
/* not exported */
|
||||
|
||||
int sirdev_get_dongle(struct sir_dev *self, IRDA_DONGLE type);
|
||||
int sirdev_put_dongle(struct sir_dev *self);
|
||||
|
||||
void sirdev_enable_rx(struct sir_dev *dev);
|
||||
int sirdev_schedule_request(struct sir_dev *dev, int state, unsigned param);
|
||||
|
||||
/* inline helpers */
|
||||
|
||||
static inline int sirdev_schedule_speed(struct sir_dev *dev, unsigned speed)
|
||||
{
|
||||
return sirdev_schedule_request(dev, SIRDEV_STATE_SET_SPEED, speed);
|
||||
}
|
||||
|
||||
static inline int sirdev_schedule_dongle_open(struct sir_dev *dev, int dongle_id)
|
||||
{
|
||||
return sirdev_schedule_request(dev, SIRDEV_STATE_DONGLE_OPEN, dongle_id);
|
||||
}
|
||||
|
||||
static inline int sirdev_schedule_dongle_close(struct sir_dev *dev)
|
||||
{
|
||||
return sirdev_schedule_request(dev, SIRDEV_STATE_DONGLE_CLOSE, 0);
|
||||
}
|
||||
|
||||
static inline int sirdev_schedule_dtr_rts(struct sir_dev *dev, int dtr, int rts)
|
||||
{
|
||||
int dtrrts;
|
||||
|
||||
dtrrts = ((dtr) ? 0x02 : 0x00) | ((rts) ? 0x01 : 0x00);
|
||||
return sirdev_schedule_request(dev, SIRDEV_STATE_SET_DTR_RTS, dtrrts);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static inline int sirdev_schedule_mode(struct sir_dev *dev, int mode)
|
||||
{
|
||||
return sirdev_schedule_request(dev, SIRDEV_STATE_SET_MODE, mode);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
struct sir_dev {
|
||||
struct net_device *netdev;
|
||||
|
||||
struct irlap_cb *irlap;
|
||||
|
||||
struct qos_info qos;
|
||||
|
||||
char hwname[32];
|
||||
|
||||
struct sir_fsm fsm;
|
||||
atomic_t enable_rx;
|
||||
int raw_tx;
|
||||
spinlock_t tx_lock;
|
||||
|
||||
u32 new_speed;
|
||||
u32 flags;
|
||||
|
||||
unsigned speed;
|
||||
|
||||
iobuff_t tx_buff; /* Transmit buffer */
|
||||
iobuff_t rx_buff; /* Receive buffer */
|
||||
struct sk_buff *tx_skb;
|
||||
|
||||
const struct dongle_driver * dongle_drv;
|
||||
const struct sir_driver * drv;
|
||||
void *priv;
|
||||
|
||||
};
|
||||
|
||||
#endif /* IRDA_SIR_H */
|
|
@ -1,987 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* sir_dev.c: irda sir network device
|
||||
*
|
||||
* Copyright (c) 2002 Martin Diehl
|
||||
*
|
||||
* 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/hardirq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/wrapper.h>
|
||||
#include <net/irda/irda_device.h>
|
||||
|
||||
#include "sir-dev.h"
|
||||
|
||||
|
||||
static struct workqueue_struct *irda_sir_wq;
|
||||
|
||||
/* STATE MACHINE */
|
||||
|
||||
/* substate handler of the config-fsm to handle the cases where we want
|
||||
* to wait for transmit completion before changing the port configuration
|
||||
*/
|
||||
|
||||
static int sirdev_tx_complete_fsm(struct sir_dev *dev)
|
||||
{
|
||||
struct sir_fsm *fsm = &dev->fsm;
|
||||
unsigned next_state, delay;
|
||||
unsigned bytes_left;
|
||||
|
||||
do {
|
||||
next_state = fsm->substate; /* default: stay in current substate */
|
||||
delay = 0;
|
||||
|
||||
switch(fsm->substate) {
|
||||
|
||||
case SIRDEV_STATE_WAIT_XMIT:
|
||||
if (dev->drv->chars_in_buffer)
|
||||
bytes_left = dev->drv->chars_in_buffer(dev);
|
||||
else
|
||||
bytes_left = 0;
|
||||
if (!bytes_left) {
|
||||
next_state = SIRDEV_STATE_WAIT_UNTIL_SENT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dev->speed > 115200)
|
||||
delay = (bytes_left*8*10000) / (dev->speed/100);
|
||||
else if (dev->speed > 0)
|
||||
delay = (bytes_left*10*10000) / (dev->speed/100);
|
||||
else
|
||||
delay = 0;
|
||||
/* expected delay (usec) until remaining bytes are sent */
|
||||
if (delay < 100) {
|
||||
udelay(delay);
|
||||
delay = 0;
|
||||
break;
|
||||
}
|
||||
/* sleep some longer delay (msec) */
|
||||
delay = (delay+999) / 1000;
|
||||
break;
|
||||
|
||||
case SIRDEV_STATE_WAIT_UNTIL_SENT:
|
||||
/* block until underlaying hardware buffer are empty */
|
||||
if (dev->drv->wait_until_sent)
|
||||
dev->drv->wait_until_sent(dev);
|
||||
next_state = SIRDEV_STATE_TX_DONE;
|
||||
break;
|
||||
|
||||
case SIRDEV_STATE_TX_DONE:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
net_err_ratelimited("%s - undefined state\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
fsm->substate = next_state;
|
||||
} while (delay == 0);
|
||||
return delay;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function sirdev_config_fsm
|
||||
*
|
||||
* State machine to handle the configuration of the device (and attached dongle, if any).
|
||||
* This handler is scheduled for execution in kIrDAd context, so we can sleep.
|
||||
* however, kIrDAd is shared by all sir_dev devices so we better don't sleep there too
|
||||
* long. Instead, for longer delays we start a timer to reschedule us later.
|
||||
* On entry, fsm->sem is always locked and the netdev xmit queue stopped.
|
||||
* Both must be unlocked/restarted on completion - but only on final exit.
|
||||
*/
|
||||
|
||||
static void sirdev_config_fsm(struct work_struct *work)
|
||||
{
|
||||
struct sir_dev *dev = container_of(work, struct sir_dev, fsm.work.work);
|
||||
struct sir_fsm *fsm = &dev->fsm;
|
||||
int next_state;
|
||||
int ret = -1;
|
||||
unsigned delay;
|
||||
|
||||
pr_debug("%s(), <%ld>\n", __func__, jiffies);
|
||||
|
||||
do {
|
||||
pr_debug("%s - state=0x%04x / substate=0x%04x\n",
|
||||
__func__, fsm->state, fsm->substate);
|
||||
|
||||
next_state = fsm->state;
|
||||
delay = 0;
|
||||
|
||||
switch(fsm->state) {
|
||||
|
||||
case SIRDEV_STATE_DONGLE_OPEN:
|
||||
if (dev->dongle_drv != NULL) {
|
||||
ret = sirdev_put_dongle(dev);
|
||||
if (ret) {
|
||||
fsm->result = -EINVAL;
|
||||
next_state = SIRDEV_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize dongle */
|
||||
ret = sirdev_get_dongle(dev, fsm->param);
|
||||
if (ret) {
|
||||
fsm->result = ret;
|
||||
next_state = SIRDEV_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Dongles are powered through the modem control lines which
|
||||
* were just set during open. Before resetting, let's wait for
|
||||
* the power to stabilize. This is what some dongle drivers did
|
||||
* in open before, while others didn't - should be safe anyway.
|
||||
*/
|
||||
|
||||
delay = 50;
|
||||
fsm->substate = SIRDEV_STATE_DONGLE_RESET;
|
||||
next_state = SIRDEV_STATE_DONGLE_RESET;
|
||||
|
||||
fsm->param = 9600;
|
||||
|
||||
break;
|
||||
|
||||
case SIRDEV_STATE_DONGLE_CLOSE:
|
||||
/* shouldn't we just treat this as success=? */
|
||||
if (dev->dongle_drv == NULL) {
|
||||
fsm->result = -EINVAL;
|
||||
next_state = SIRDEV_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = sirdev_put_dongle(dev);
|
||||
if (ret) {
|
||||
fsm->result = ret;
|
||||
next_state = SIRDEV_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
next_state = SIRDEV_STATE_DONE;
|
||||
break;
|
||||
|
||||
case SIRDEV_STATE_SET_DTR_RTS:
|
||||
ret = sirdev_set_dtr_rts(dev,
|
||||
(fsm->param&0x02) ? TRUE : FALSE,
|
||||
(fsm->param&0x01) ? TRUE : FALSE);
|
||||
next_state = SIRDEV_STATE_DONE;
|
||||
break;
|
||||
|
||||
case SIRDEV_STATE_SET_SPEED:
|
||||
fsm->substate = SIRDEV_STATE_WAIT_XMIT;
|
||||
next_state = SIRDEV_STATE_DONGLE_CHECK;
|
||||
break;
|
||||
|
||||
case SIRDEV_STATE_DONGLE_CHECK:
|
||||
ret = sirdev_tx_complete_fsm(dev);
|
||||
if (ret < 0) {
|
||||
fsm->result = ret;
|
||||
next_state = SIRDEV_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
if ((delay=ret) != 0)
|
||||
break;
|
||||
|
||||
if (dev->dongle_drv) {
|
||||
fsm->substate = SIRDEV_STATE_DONGLE_RESET;
|
||||
next_state = SIRDEV_STATE_DONGLE_RESET;
|
||||
}
|
||||
else {
|
||||
dev->speed = fsm->param;
|
||||
next_state = SIRDEV_STATE_PORT_SPEED;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIRDEV_STATE_DONGLE_RESET:
|
||||
if (dev->dongle_drv->reset) {
|
||||
ret = dev->dongle_drv->reset(dev);
|
||||
if (ret < 0) {
|
||||
fsm->result = ret;
|
||||
next_state = SIRDEV_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
ret = 0;
|
||||
if ((delay=ret) == 0) {
|
||||
/* set serial port according to dongle default speed */
|
||||
if (dev->drv->set_speed)
|
||||
dev->drv->set_speed(dev, dev->speed);
|
||||
fsm->substate = SIRDEV_STATE_DONGLE_SPEED;
|
||||
next_state = SIRDEV_STATE_DONGLE_SPEED;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIRDEV_STATE_DONGLE_SPEED:
|
||||
if (dev->dongle_drv->set_speed) {
|
||||
ret = dev->dongle_drv->set_speed(dev, fsm->param);
|
||||
if (ret < 0) {
|
||||
fsm->result = ret;
|
||||
next_state = SIRDEV_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
ret = 0;
|
||||
if ((delay=ret) == 0)
|
||||
next_state = SIRDEV_STATE_PORT_SPEED;
|
||||
break;
|
||||
|
||||
case SIRDEV_STATE_PORT_SPEED:
|
||||
/* Finally we are ready to change the serial port speed */
|
||||
if (dev->drv->set_speed)
|
||||
dev->drv->set_speed(dev, dev->speed);
|
||||
dev->new_speed = 0;
|
||||
next_state = SIRDEV_STATE_DONE;
|
||||
break;
|
||||
|
||||
case SIRDEV_STATE_DONE:
|
||||
/* Signal network layer so it can send more frames */
|
||||
netif_wake_queue(dev->netdev);
|
||||
next_state = SIRDEV_STATE_COMPLETE;
|
||||
break;
|
||||
|
||||
default:
|
||||
net_err_ratelimited("%s - undefined state\n", __func__);
|
||||
fsm->result = -EINVAL;
|
||||
/* fall thru */
|
||||
|
||||
case SIRDEV_STATE_ERROR:
|
||||
net_err_ratelimited("%s - error: %d\n",
|
||||
__func__, fsm->result);
|
||||
|
||||
#if 0 /* don't enable this before we have netdev->tx_timeout to recover */
|
||||
netif_stop_queue(dev->netdev);
|
||||
#else
|
||||
netif_wake_queue(dev->netdev);
|
||||
#endif
|
||||
/* fall thru */
|
||||
|
||||
case SIRDEV_STATE_COMPLETE:
|
||||
/* config change finished, so we are not busy any longer */
|
||||
sirdev_enable_rx(dev);
|
||||
up(&fsm->sem);
|
||||
return;
|
||||
}
|
||||
fsm->state = next_state;
|
||||
} while(!delay);
|
||||
|
||||
queue_delayed_work(irda_sir_wq, &fsm->work, msecs_to_jiffies(delay));
|
||||
}
|
||||
|
||||
/* schedule some device configuration task for execution by kIrDAd
|
||||
* on behalf of the above state machine.
|
||||
* can be called from process or interrupt/tasklet context.
|
||||
*/
|
||||
|
||||
int sirdev_schedule_request(struct sir_dev *dev, int initial_state, unsigned param)
|
||||
{
|
||||
struct sir_fsm *fsm = &dev->fsm;
|
||||
|
||||
pr_debug("%s - state=0x%04x / param=%u\n", __func__,
|
||||
initial_state, param);
|
||||
|
||||
if (down_trylock(&fsm->sem)) {
|
||||
if (in_interrupt() || in_atomic() || irqs_disabled()) {
|
||||
pr_debug("%s(), state machine busy!\n", __func__);
|
||||
return -EWOULDBLOCK;
|
||||
} else
|
||||
down(&fsm->sem);
|
||||
}
|
||||
|
||||
if (fsm->state == SIRDEV_STATE_DEAD) {
|
||||
/* race with sirdev_close should never happen */
|
||||
net_err_ratelimited("%s(), instance staled!\n", __func__);
|
||||
up(&fsm->sem);
|
||||
return -ESTALE; /* or better EPIPE? */
|
||||
}
|
||||
|
||||
netif_stop_queue(dev->netdev);
|
||||
atomic_set(&dev->enable_rx, 0);
|
||||
|
||||
fsm->state = initial_state;
|
||||
fsm->param = param;
|
||||
fsm->result = 0;
|
||||
|
||||
INIT_DELAYED_WORK(&fsm->work, sirdev_config_fsm);
|
||||
queue_delayed_work(irda_sir_wq, &fsm->work, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
void sirdev_enable_rx(struct sir_dev *dev)
|
||||
{
|
||||
if (unlikely(atomic_read(&dev->enable_rx)))
|
||||
return;
|
||||
|
||||
/* flush rx-buffer - should also help in case of problems with echo cancelation */
|
||||
dev->rx_buff.data = dev->rx_buff.head;
|
||||
dev->rx_buff.len = 0;
|
||||
dev->rx_buff.in_frame = FALSE;
|
||||
dev->rx_buff.state = OUTSIDE_FRAME;
|
||||
atomic_set(&dev->enable_rx, 1);
|
||||
}
|
||||
|
||||
static int sirdev_is_receiving(struct sir_dev *dev)
|
||||
{
|
||||
if (!atomic_read(&dev->enable_rx))
|
||||
return 0;
|
||||
|
||||
return dev->rx_buff.state != OUTSIDE_FRAME;
|
||||
}
|
||||
|
||||
int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type)
|
||||
{
|
||||
int err;
|
||||
|
||||
pr_debug("%s : requesting dongle %d.\n", __func__, type);
|
||||
|
||||
err = sirdev_schedule_dongle_open(dev, type);
|
||||
if (unlikely(err))
|
||||
return err;
|
||||
down(&dev->fsm.sem); /* block until config change completed */
|
||||
err = dev->fsm.result;
|
||||
up(&dev->fsm.sem);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(sirdev_set_dongle);
|
||||
|
||||
/* used by dongle drivers for dongle programming */
|
||||
|
||||
int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (unlikely(len > dev->tx_buff.truesize))
|
||||
return -ENOSPC;
|
||||
|
||||
spin_lock_irqsave(&dev->tx_lock, flags); /* serialize with other tx operations */
|
||||
while (dev->tx_buff.len > 0) { /* wait until tx idle */
|
||||
spin_unlock_irqrestore(&dev->tx_lock, flags);
|
||||
msleep(10);
|
||||
spin_lock_irqsave(&dev->tx_lock, flags);
|
||||
}
|
||||
|
||||
dev->tx_buff.data = dev->tx_buff.head;
|
||||
memcpy(dev->tx_buff.data, buf, len);
|
||||
dev->tx_buff.len = len;
|
||||
|
||||
ret = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len);
|
||||
if (ret > 0) {
|
||||
pr_debug("%s(), raw-tx started\n", __func__);
|
||||
|
||||
dev->tx_buff.data += ret;
|
||||
dev->tx_buff.len -= ret;
|
||||
dev->raw_tx = 1;
|
||||
ret = len; /* all data is going to be sent */
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->tx_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(sirdev_raw_write);
|
||||
|
||||
/* seems some dongle drivers may need this */
|
||||
|
||||
int sirdev_raw_read(struct sir_dev *dev, char *buf, int len)
|
||||
{
|
||||
int count;
|
||||
|
||||
if (atomic_read(&dev->enable_rx))
|
||||
return -EIO; /* fail if we expect irda-frames */
|
||||
|
||||
count = (len < dev->rx_buff.len) ? len : dev->rx_buff.len;
|
||||
|
||||
if (count > 0) {
|
||||
memcpy(buf, dev->rx_buff.data, count);
|
||||
dev->rx_buff.data += count;
|
||||
dev->rx_buff.len -= count;
|
||||
}
|
||||
|
||||
/* remaining stuff gets flushed when re-enabling normal rx */
|
||||
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL(sirdev_raw_read);
|
||||
|
||||
int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts)
|
||||
{
|
||||
int ret = -ENXIO;
|
||||
if (dev->drv->set_dtr_rts)
|
||||
ret = dev->drv->set_dtr_rts(dev, dtr, rts);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(sirdev_set_dtr_rts);
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
/* called from client driver - likely with bh-context - to indicate
|
||||
* it made some progress with transmission. Hence we send the next
|
||||
* chunk, if any, or complete the skb otherwise
|
||||
*/
|
||||
|
||||
void sirdev_write_complete(struct sir_dev *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct sk_buff *skb;
|
||||
int actual = 0;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&dev->tx_lock, flags);
|
||||
|
||||
pr_debug("%s() - dev->tx_buff.len = %d\n",
|
||||
__func__, dev->tx_buff.len);
|
||||
|
||||
if (likely(dev->tx_buff.len > 0)) {
|
||||
/* Write data left in transmit buffer */
|
||||
actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len);
|
||||
|
||||
if (likely(actual>0)) {
|
||||
dev->tx_buff.data += actual;
|
||||
dev->tx_buff.len -= actual;
|
||||
}
|
||||
else if (unlikely(actual<0)) {
|
||||
/* could be dropped later when we have tx_timeout to recover */
|
||||
net_err_ratelimited("%s: drv->do_write failed (%d)\n",
|
||||
__func__, actual);
|
||||
if ((skb=dev->tx_skb) != NULL) {
|
||||
dev->tx_skb = NULL;
|
||||
dev_kfree_skb_any(skb);
|
||||
dev->netdev->stats.tx_errors++;
|
||||
dev->netdev->stats.tx_dropped++;
|
||||
}
|
||||
dev->tx_buff.len = 0;
|
||||
}
|
||||
if (dev->tx_buff.len > 0)
|
||||
goto done; /* more data to send later */
|
||||
}
|
||||
|
||||
if (unlikely(dev->raw_tx != 0)) {
|
||||
/* in raw mode we are just done now after the buffer was sent
|
||||
* completely. Since this was requested by some dongle driver
|
||||
* running under the control of the irda-thread we must take
|
||||
* care here not to re-enable the queue. The queue will be
|
||||
* restarted when the irda-thread has completed the request.
|
||||
*/
|
||||
|
||||
pr_debug("%s(), raw-tx done\n", __func__);
|
||||
dev->raw_tx = 0;
|
||||
goto done; /* no post-frame handling in raw mode */
|
||||
}
|
||||
|
||||
/* we have finished now sending this skb.
|
||||
* update statistics and free the skb.
|
||||
* finally we check and trigger a pending speed change, if any.
|
||||
* if not we switch to rx mode and wake the queue for further
|
||||
* packets.
|
||||
* note the scheduled speed request blocks until the lower
|
||||
* client driver and the corresponding hardware has really
|
||||
* finished sending all data (xmit fifo drained f.e.)
|
||||
* before the speed change gets finally done and the queue
|
||||
* re-activated.
|
||||
*/
|
||||
|
||||
pr_debug("%s(), finished with frame!\n", __func__);
|
||||
|
||||
if ((skb=dev->tx_skb) != NULL) {
|
||||
dev->tx_skb = NULL;
|
||||
dev->netdev->stats.tx_packets++;
|
||||
dev->netdev->stats.tx_bytes += skb->len;
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
|
||||
if (unlikely(dev->new_speed > 0)) {
|
||||
pr_debug("%s(), Changing speed!\n", __func__);
|
||||
err = sirdev_schedule_speed(dev, dev->new_speed);
|
||||
if (unlikely(err)) {
|
||||
/* should never happen
|
||||
* forget the speed change and hope the stack recovers
|
||||
*/
|
||||
net_err_ratelimited("%s - schedule speed change failed: %d\n",
|
||||
__func__, err);
|
||||
netif_wake_queue(dev->netdev);
|
||||
}
|
||||
/* else: success
|
||||
* speed change in progress now
|
||||
* on completion dev->new_speed gets cleared,
|
||||
* rx-reenabled and the queue restarted
|
||||
*/
|
||||
}
|
||||
else {
|
||||
sirdev_enable_rx(dev);
|
||||
netif_wake_queue(dev->netdev);
|
||||
}
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&dev->tx_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(sirdev_write_complete);
|
||||
|
||||
/* called from client driver - likely with bh-context - to give us
|
||||
* some more received bytes. We put them into the rx-buffer,
|
||||
* normally unwrapping and building LAP-skb's (unless rx disabled)
|
||||
*/
|
||||
|
||||
int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count)
|
||||
{
|
||||
if (!dev || !dev->netdev) {
|
||||
net_warn_ratelimited("%s(), not ready yet!\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!dev->irlap) {
|
||||
net_warn_ratelimited("%s - too early: %p / %zd!\n",
|
||||
__func__, cp, count);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cp==NULL) {
|
||||
/* error already at lower level receive
|
||||
* just update stats and set media busy
|
||||
*/
|
||||
irda_device_set_media_busy(dev->netdev, TRUE);
|
||||
dev->netdev->stats.rx_dropped++;
|
||||
pr_debug("%s; rx-drop: %zd\n", __func__, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read the characters into the buffer */
|
||||
if (likely(atomic_read(&dev->enable_rx))) {
|
||||
while (count--)
|
||||
/* Unwrap and destuff one byte */
|
||||
async_unwrap_char(dev->netdev, &dev->netdev->stats,
|
||||
&dev->rx_buff, *cp++);
|
||||
} else {
|
||||
while (count--) {
|
||||
/* rx not enabled: save the raw bytes and never
|
||||
* trigger any netif_rx. The received bytes are flushed
|
||||
* later when we re-enable rx but might be read meanwhile
|
||||
* by the dongle driver.
|
||||
*/
|
||||
dev->rx_buff.data[dev->rx_buff.len++] = *cp++;
|
||||
|
||||
/* What should we do when the buffer is full? */
|
||||
if (unlikely(dev->rx_buff.len == dev->rx_buff.truesize))
|
||||
dev->rx_buff.len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(sirdev_receive);
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
/* callbacks from network layer */
|
||||
|
||||
static netdev_tx_t sirdev_hard_xmit(struct sk_buff *skb,
|
||||
struct net_device *ndev)
|
||||
{
|
||||
struct sir_dev *dev = netdev_priv(ndev);
|
||||
unsigned long flags;
|
||||
int actual = 0;
|
||||
int err;
|
||||
s32 speed;
|
||||
|
||||
IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;);
|
||||
|
||||
netif_stop_queue(ndev);
|
||||
|
||||
pr_debug("%s(), skb->len = %d\n", __func__, skb->len);
|
||||
|
||||
speed = irda_get_next_speed(skb);
|
||||
if ((speed != dev->speed) && (speed != -1)) {
|
||||
if (!skb->len) {
|
||||
err = sirdev_schedule_speed(dev, speed);
|
||||
if (unlikely(err == -EWOULDBLOCK)) {
|
||||
/* Failed to initiate the speed change, likely the fsm
|
||||
* is still busy (pretty unlikely, but...)
|
||||
* We refuse to accept the skb and return with the queue
|
||||
* stopped so the network layer will retry after the
|
||||
* fsm completes and wakes the queue.
|
||||
*/
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
else if (unlikely(err)) {
|
||||
/* other fatal error - forget the speed change and
|
||||
* hope the stack will recover somehow
|
||||
*/
|
||||
netif_start_queue(ndev);
|
||||
}
|
||||
/* else: success
|
||||
* speed change in progress now
|
||||
* on completion the queue gets restarted
|
||||
*/
|
||||
|
||||
dev_kfree_skb_any(skb);
|
||||
return NETDEV_TX_OK;
|
||||
} else
|
||||
dev->new_speed = speed;
|
||||
}
|
||||
|
||||
/* Init tx buffer*/
|
||||
dev->tx_buff.data = dev->tx_buff.head;
|
||||
|
||||
/* Check problems */
|
||||
if(spin_is_locked(&dev->tx_lock)) {
|
||||
pr_debug("%s(), write not completed\n", __func__);
|
||||
}
|
||||
|
||||
/* serialize with write completion */
|
||||
spin_lock_irqsave(&dev->tx_lock, flags);
|
||||
|
||||
/* Copy skb to tx_buff while wrapping, stuffing and making CRC */
|
||||
dev->tx_buff.len = async_wrap_skb(skb, dev->tx_buff.data, dev->tx_buff.truesize);
|
||||
|
||||
/* transmission will start now - disable receive.
|
||||
* if we are just in the middle of an incoming frame,
|
||||
* treat it as collision. probably it's a good idea to
|
||||
* reset the rx_buf OUTSIDE_FRAME in this case too?
|
||||
*/
|
||||
atomic_set(&dev->enable_rx, 0);
|
||||
if (unlikely(sirdev_is_receiving(dev)))
|
||||
dev->netdev->stats.collisions++;
|
||||
|
||||
actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len);
|
||||
|
||||
if (likely(actual > 0)) {
|
||||
dev->tx_skb = skb;
|
||||
dev->tx_buff.data += actual;
|
||||
dev->tx_buff.len -= actual;
|
||||
}
|
||||
else if (unlikely(actual < 0)) {
|
||||
/* could be dropped later when we have tx_timeout to recover */
|
||||
net_err_ratelimited("%s: drv->do_write failed (%d)\n",
|
||||
__func__, actual);
|
||||
dev_kfree_skb_any(skb);
|
||||
dev->netdev->stats.tx_errors++;
|
||||
dev->netdev->stats.tx_dropped++;
|
||||
netif_wake_queue(ndev);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->tx_lock, flags);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* called from network layer with rtnl hold */
|
||||
|
||||
static int sirdev_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
|
||||
{
|
||||
struct if_irda_req *irq = (struct if_irda_req *) rq;
|
||||
struct sir_dev *dev = netdev_priv(ndev);
|
||||
int ret = 0;
|
||||
|
||||
IRDA_ASSERT(dev != NULL, return -1;);
|
||||
|
||||
pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, ndev->name, cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case SIOCSBANDWIDTH: /* Set bandwidth */
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
ret = -EPERM;
|
||||
else
|
||||
ret = sirdev_schedule_speed(dev, irq->ifr_baudrate);
|
||||
/* cannot sleep here for completion
|
||||
* we are called from network layer with rtnl hold
|
||||
*/
|
||||
break;
|
||||
|
||||
case SIOCSDONGLE: /* Set dongle */
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
ret = -EPERM;
|
||||
else
|
||||
ret = sirdev_schedule_dongle_open(dev, irq->ifr_dongle);
|
||||
/* cannot sleep here for completion
|
||||
* we are called from network layer with rtnl hold
|
||||
*/
|
||||
break;
|
||||
|
||||
case SIOCSMEDIABUSY: /* Set media busy */
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
ret = -EPERM;
|
||||
else
|
||||
irda_device_set_media_busy(dev->netdev, TRUE);
|
||||
break;
|
||||
|
||||
case SIOCGRECEIVING: /* Check if we are receiving right now */
|
||||
irq->ifr_receiving = sirdev_is_receiving(dev);
|
||||
break;
|
||||
|
||||
case SIOCSDTRRTS:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
ret = -EPERM;
|
||||
else
|
||||
ret = sirdev_schedule_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts);
|
||||
/* cannot sleep here for completion
|
||||
* we are called from network layer with rtnl hold
|
||||
*/
|
||||
break;
|
||||
|
||||
case SIOCSMODE:
|
||||
#if 0
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
ret = -EPERM;
|
||||
else
|
||||
ret = sirdev_schedule_mode(dev, irq->ifr_mode);
|
||||
/* cannot sleep here for completion
|
||||
* we are called from network layer with rtnl hold
|
||||
*/
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
|
||||
#define SIRBUF_ALLOCSIZE 4269 /* worst case size of a wrapped IrLAP frame */
|
||||
|
||||
static int sirdev_alloc_buffers(struct sir_dev *dev)
|
||||
{
|
||||
dev->tx_buff.truesize = SIRBUF_ALLOCSIZE;
|
||||
dev->rx_buff.truesize = IRDA_SKB_MAX_MTU;
|
||||
|
||||
/* Bootstrap ZeroCopy Rx */
|
||||
dev->rx_buff.skb = __netdev_alloc_skb(dev->netdev, dev->rx_buff.truesize,
|
||||
GFP_KERNEL);
|
||||
if (dev->rx_buff.skb == NULL)
|
||||
return -ENOMEM;
|
||||
skb_reserve(dev->rx_buff.skb, 1);
|
||||
dev->rx_buff.head = dev->rx_buff.skb->data;
|
||||
|
||||
dev->tx_buff.head = kmalloc(dev->tx_buff.truesize, GFP_KERNEL);
|
||||
if (dev->tx_buff.head == NULL) {
|
||||
kfree_skb(dev->rx_buff.skb);
|
||||
dev->rx_buff.skb = NULL;
|
||||
dev->rx_buff.head = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev->tx_buff.data = dev->tx_buff.head;
|
||||
dev->rx_buff.data = dev->rx_buff.head;
|
||||
dev->tx_buff.len = 0;
|
||||
dev->rx_buff.len = 0;
|
||||
|
||||
dev->rx_buff.in_frame = FALSE;
|
||||
dev->rx_buff.state = OUTSIDE_FRAME;
|
||||
return 0;
|
||||
};
|
||||
|
||||
static void sirdev_free_buffers(struct sir_dev *dev)
|
||||
{
|
||||
kfree_skb(dev->rx_buff.skb);
|
||||
kfree(dev->tx_buff.head);
|
||||
dev->rx_buff.head = dev->tx_buff.head = NULL;
|
||||
dev->rx_buff.skb = NULL;
|
||||
}
|
||||
|
||||
static int sirdev_open(struct net_device *ndev)
|
||||
{
|
||||
struct sir_dev *dev = netdev_priv(ndev);
|
||||
const struct sir_driver *drv = dev->drv;
|
||||
|
||||
if (!drv)
|
||||
return -ENODEV;
|
||||
|
||||
/* increase the reference count of the driver module before doing serious stuff */
|
||||
if (!try_module_get(drv->owner))
|
||||
return -ESTALE;
|
||||
|
||||
if (sirdev_alloc_buffers(dev))
|
||||
goto errout_dec;
|
||||
|
||||
if (!dev->drv->start_dev || dev->drv->start_dev(dev))
|
||||
goto errout_free;
|
||||
|
||||
sirdev_enable_rx(dev);
|
||||
dev->raw_tx = 0;
|
||||
|
||||
netif_start_queue(ndev);
|
||||
dev->irlap = irlap_open(ndev, &dev->qos, dev->hwname);
|
||||
if (!dev->irlap)
|
||||
goto errout_stop;
|
||||
|
||||
netif_wake_queue(ndev);
|
||||
|
||||
pr_debug("%s - done, speed = %d\n", __func__, dev->speed);
|
||||
|
||||
return 0;
|
||||
|
||||
errout_stop:
|
||||
atomic_set(&dev->enable_rx, 0);
|
||||
if (dev->drv->stop_dev)
|
||||
dev->drv->stop_dev(dev);
|
||||
errout_free:
|
||||
sirdev_free_buffers(dev);
|
||||
errout_dec:
|
||||
module_put(drv->owner);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int sirdev_close(struct net_device *ndev)
|
||||
{
|
||||
struct sir_dev *dev = netdev_priv(ndev);
|
||||
const struct sir_driver *drv;
|
||||
|
||||
/* pr_debug("%s\n", __func__); */
|
||||
|
||||
netif_stop_queue(ndev);
|
||||
|
||||
down(&dev->fsm.sem); /* block on pending config completion */
|
||||
|
||||
atomic_set(&dev->enable_rx, 0);
|
||||
|
||||
if (unlikely(!dev->irlap))
|
||||
goto out;
|
||||
irlap_close(dev->irlap);
|
||||
dev->irlap = NULL;
|
||||
|
||||
drv = dev->drv;
|
||||
if (unlikely(!drv || !dev->priv))
|
||||
goto out;
|
||||
|
||||
if (drv->stop_dev)
|
||||
drv->stop_dev(dev);
|
||||
|
||||
sirdev_free_buffers(dev);
|
||||
module_put(drv->owner);
|
||||
|
||||
out:
|
||||
dev->speed = 0;
|
||||
up(&dev->fsm.sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct net_device_ops sirdev_ops = {
|
||||
.ndo_start_xmit = sirdev_hard_xmit,
|
||||
.ndo_open = sirdev_open,
|
||||
.ndo_stop = sirdev_close,
|
||||
.ndo_do_ioctl = sirdev_ioctl,
|
||||
};
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
|
||||
struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *name)
|
||||
{
|
||||
struct net_device *ndev;
|
||||
struct sir_dev *dev;
|
||||
|
||||
pr_debug("%s - %s\n", __func__, name);
|
||||
|
||||
/* instead of adding tests to protect against drv->do_write==NULL
|
||||
* at several places we refuse to create a sir_dev instance for
|
||||
* drivers which don't implement do_write.
|
||||
*/
|
||||
if (!drv || !drv->do_write)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Allocate new instance of the device
|
||||
*/
|
||||
ndev = alloc_irdadev(sizeof(*dev));
|
||||
if (ndev == NULL) {
|
||||
net_err_ratelimited("%s - Can't allocate memory for IrDA control block!\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
dev = netdev_priv(ndev);
|
||||
|
||||
irda_init_max_qos_capabilies(&dev->qos);
|
||||
dev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
|
||||
dev->qos.min_turn_time.bits = drv->qos_mtt_bits;
|
||||
irda_qos_bits_to_value(&dev->qos);
|
||||
|
||||
strncpy(dev->hwname, name, sizeof(dev->hwname)-1);
|
||||
|
||||
atomic_set(&dev->enable_rx, 0);
|
||||
dev->tx_skb = NULL;
|
||||
|
||||
spin_lock_init(&dev->tx_lock);
|
||||
sema_init(&dev->fsm.sem, 1);
|
||||
|
||||
dev->drv = drv;
|
||||
dev->netdev = ndev;
|
||||
|
||||
/* Override the network functions we need to use */
|
||||
ndev->netdev_ops = &sirdev_ops;
|
||||
|
||||
if (register_netdev(ndev)) {
|
||||
net_err_ratelimited("%s(), register_netdev() failed!\n",
|
||||
__func__);
|
||||
goto out_freenetdev;
|
||||
}
|
||||
|
||||
return dev;
|
||||
|
||||
out_freenetdev:
|
||||
free_netdev(ndev);
|
||||
out:
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(sirdev_get_instance);
|
||||
|
||||
int sirdev_put_instance(struct sir_dev *dev)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
atomic_set(&dev->enable_rx, 0);
|
||||
|
||||
netif_carrier_off(dev->netdev);
|
||||
netif_device_detach(dev->netdev);
|
||||
|
||||
if (dev->dongle_drv)
|
||||
err = sirdev_schedule_dongle_close(dev);
|
||||
if (err)
|
||||
net_err_ratelimited("%s - error %d\n", __func__, err);
|
||||
|
||||
sirdev_close(dev->netdev);
|
||||
|
||||
down(&dev->fsm.sem);
|
||||
dev->fsm.state = SIRDEV_STATE_DEAD; /* mark staled */
|
||||
dev->dongle_drv = NULL;
|
||||
dev->priv = NULL;
|
||||
up(&dev->fsm.sem);
|
||||
|
||||
/* Remove netdevice */
|
||||
unregister_netdev(dev->netdev);
|
||||
|
||||
free_netdev(dev->netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(sirdev_put_instance);
|
||||
|
||||
static int __init sir_wq_init(void)
|
||||
{
|
||||
irda_sir_wq = create_singlethread_workqueue("irda_sir_wq");
|
||||
if (!irda_sir_wq)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit sir_wq_exit(void)
|
||||
{
|
||||
destroy_workqueue(irda_sir_wq);
|
||||
}
|
||||
|
||||
module_init(sir_wq_init);
|
||||
module_exit(sir_wq_exit);
|
||||
|
||||
MODULE_AUTHOR("Martin Diehl <info@mdiehl.de>");
|
||||
MODULE_DESCRIPTION("IrDA SIR core");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,133 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* sir_dongle.c: manager for serial dongle protocol drivers
|
||||
*
|
||||
* Copyright (c) 2002 Martin Diehl
|
||||
*
|
||||
* 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/kmod.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
|
||||
#include "sir-dev.h"
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* dongle registration and attachment
|
||||
*
|
||||
*/
|
||||
|
||||
static LIST_HEAD(dongle_list); /* list of registered dongle drivers */
|
||||
static DEFINE_MUTEX(dongle_list_lock); /* protects the list */
|
||||
|
||||
int irda_register_dongle(struct dongle_driver *new)
|
||||
{
|
||||
struct list_head *entry;
|
||||
struct dongle_driver *drv;
|
||||
|
||||
pr_debug("%s : registering dongle \"%s\" (%d).\n",
|
||||
__func__, new->driver_name, new->type);
|
||||
|
||||
mutex_lock(&dongle_list_lock);
|
||||
list_for_each(entry, &dongle_list) {
|
||||
drv = list_entry(entry, struct dongle_driver, dongle_list);
|
||||
if (new->type == drv->type) {
|
||||
mutex_unlock(&dongle_list_lock);
|
||||
return -EEXIST;
|
||||
}
|
||||
}
|
||||
list_add(&new->dongle_list, &dongle_list);
|
||||
mutex_unlock(&dongle_list_lock);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(irda_register_dongle);
|
||||
|
||||
int irda_unregister_dongle(struct dongle_driver *drv)
|
||||
{
|
||||
mutex_lock(&dongle_list_lock);
|
||||
list_del(&drv->dongle_list);
|
||||
mutex_unlock(&dongle_list_lock);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(irda_unregister_dongle);
|
||||
|
||||
int sirdev_get_dongle(struct sir_dev *dev, IRDA_DONGLE type)
|
||||
{
|
||||
struct list_head *entry;
|
||||
const struct dongle_driver *drv = NULL;
|
||||
int err = -EINVAL;
|
||||
|
||||
request_module("irda-dongle-%d", type);
|
||||
|
||||
if (dev->dongle_drv != NULL)
|
||||
return -EBUSY;
|
||||
|
||||
/* serialize access to the list of registered dongles */
|
||||
mutex_lock(&dongle_list_lock);
|
||||
|
||||
list_for_each(entry, &dongle_list) {
|
||||
drv = list_entry(entry, struct dongle_driver, dongle_list);
|
||||
if (drv->type == type)
|
||||
break;
|
||||
else
|
||||
drv = NULL;
|
||||
}
|
||||
|
||||
if (!drv) {
|
||||
err = -ENODEV;
|
||||
goto out_unlock; /* no such dongle */
|
||||
}
|
||||
|
||||
/* handling of SMP races with dongle module removal - three cases:
|
||||
* 1) dongle driver was already unregistered - then we haven't found the
|
||||
* requested dongle above and are already out here
|
||||
* 2) the module is already marked deleted but the driver is still
|
||||
* registered - then the try_module_get() below will fail
|
||||
* 3) the try_module_get() below succeeds before the module is marked
|
||||
* deleted - then sys_delete_module() fails and prevents the removal
|
||||
* because the module is in use.
|
||||
*/
|
||||
|
||||
if (!try_module_get(drv->owner)) {
|
||||
err = -ESTALE;
|
||||
goto out_unlock; /* rmmod already pending */
|
||||
}
|
||||
dev->dongle_drv = drv;
|
||||
|
||||
if (!drv->open || (err=drv->open(dev))!=0)
|
||||
goto out_reject; /* failed to open driver */
|
||||
|
||||
mutex_unlock(&dongle_list_lock);
|
||||
return 0;
|
||||
|
||||
out_reject:
|
||||
dev->dongle_drv = NULL;
|
||||
module_put(drv->owner);
|
||||
out_unlock:
|
||||
mutex_unlock(&dongle_list_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
int sirdev_put_dongle(struct sir_dev *dev)
|
||||
{
|
||||
const struct dongle_driver *drv = dev->dongle_drv;
|
||||
|
||||
if (drv) {
|
||||
if (drv->close)
|
||||
drv->close(dev); /* close this dongle instance */
|
||||
|
||||
dev->dongle_drv = NULL; /* unlink the dongle driver */
|
||||
module_put(drv->owner);/* decrement driver's module refcount */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,191 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Description: Definitions for the SMC IrCC chipset
|
||||
* Status: Experimental.
|
||||
* Author: Daniele Peri (peri@csai.unipa.it)
|
||||
*
|
||||
* Copyright (c) 2002 Daniele Peri
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Based on smc-ircc.h:
|
||||
*
|
||||
* Copyright (c) 1999-2000, Dag Brattli <dagb@cs.uit.no>
|
||||
* Copyright (c) 1998-1999, Thomas Davis (tadavis@jps.net>
|
||||
* All Rights Reserved
|
||||
*
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef SMSC_IRCC2_H
|
||||
#define SMSC_IRCC2_H
|
||||
|
||||
/* DMA modes needed */
|
||||
#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */
|
||||
#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */
|
||||
|
||||
/* Master Control Register */
|
||||
#define IRCC_MASTER 0x07
|
||||
#define IRCC_MASTER_POWERDOWN 0x80
|
||||
#define IRCC_MASTER_RESET 0x40
|
||||
#define IRCC_MASTER_INT_EN 0x20
|
||||
#define IRCC_MASTER_ERROR_RESET 0x10
|
||||
|
||||
/* Register block 0 */
|
||||
|
||||
/* Interrupt Identification */
|
||||
#define IRCC_IIR 0x01
|
||||
#define IRCC_IIR_ACTIVE_FRAME 0x80
|
||||
#define IRCC_IIR_EOM 0x40
|
||||
#define IRCC_IIR_RAW_MODE 0x20
|
||||
#define IRCC_IIR_FIFO 0x10
|
||||
|
||||
/* Interrupt Enable */
|
||||
#define IRCC_IER 0x02
|
||||
#define IRCC_IER_ACTIVE_FRAME 0x80
|
||||
#define IRCC_IER_EOM 0x40
|
||||
#define IRCC_IER_RAW_MODE 0x20
|
||||
#define IRCC_IER_FIFO 0x10
|
||||
|
||||
/* Line Status Register */
|
||||
#define IRCC_LSR 0x03
|
||||
#define IRCC_LSR_UNDERRUN 0x80
|
||||
#define IRCC_LSR_OVERRUN 0x40
|
||||
#define IRCC_LSR_FRAME_ERROR 0x20
|
||||
#define IRCC_LSR_SIZE_ERROR 0x10
|
||||
#define IRCC_LSR_CRC_ERROR 0x80
|
||||
#define IRCC_LSR_FRAME_ABORT 0x40
|
||||
|
||||
/* Line Status Address Register */
|
||||
#define IRCC_LSAR 0x03
|
||||
#define IRCC_LSAR_ADDRESS_MASK 0x07
|
||||
|
||||
/* Line Control Register A */
|
||||
#define IRCC_LCR_A 0x04
|
||||
#define IRCC_LCR_A_FIFO_RESET 0x80
|
||||
#define IRCC_LCR_A_FAST 0x40
|
||||
#define IRCC_LCR_A_GP_DATA 0x20
|
||||
#define IRCC_LCR_A_RAW_TX 0x10
|
||||
#define IRCC_LCR_A_RAW_RX 0x08
|
||||
#define IRCC_LCR_A_ABORT 0x04
|
||||
#define IRCC_LCR_A_DATA_DONE 0x02
|
||||
|
||||
/* Line Control Register B */
|
||||
#define IRCC_LCR_B 0x05
|
||||
#define IRCC_LCR_B_SCE_DISABLED 0x00
|
||||
#define IRCC_LCR_B_SCE_TRANSMIT 0x40
|
||||
#define IRCC_LCR_B_SCE_RECEIVE 0x80
|
||||
#define IRCC_LCR_B_SCE_UNDEFINED 0xc0
|
||||
#define IRCC_LCR_B_SIP_ENABLE 0x20
|
||||
#define IRCC_LCR_B_BRICK_WALL 0x10
|
||||
|
||||
/* Bus Status Register */
|
||||
#define IRCC_BSR 0x06
|
||||
#define IRCC_BSR_NOT_EMPTY 0x80
|
||||
#define IRCC_BSR_FIFO_FULL 0x40
|
||||
#define IRCC_BSR_TIMEOUT 0x20
|
||||
|
||||
/* Register block 1 */
|
||||
|
||||
#define IRCC_FIFO_THRESHOLD 0x02
|
||||
|
||||
#define IRCC_SCE_CFGA 0x00
|
||||
#define IRCC_CFGA_AUX_IR 0x80
|
||||
#define IRCC_CFGA_HALF_DUPLEX 0x04
|
||||
#define IRCC_CFGA_TX_POLARITY 0x02
|
||||
#define IRCC_CFGA_RX_POLARITY 0x01
|
||||
|
||||
#define IRCC_CFGA_COM 0x00
|
||||
#define IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK 0x87
|
||||
#define IRCC_CFGA_IRDA_SIR_A 0x08
|
||||
#define IRCC_CFGA_ASK_SIR 0x10
|
||||
#define IRCC_CFGA_IRDA_SIR_B 0x18
|
||||
#define IRCC_CFGA_IRDA_HDLC 0x20
|
||||
#define IRCC_CFGA_IRDA_4PPM 0x28
|
||||
#define IRCC_CFGA_CONSUMER 0x30
|
||||
#define IRCC_CFGA_RAW_IR 0x38
|
||||
#define IRCC_CFGA_OTHER 0x40
|
||||
|
||||
#define IRCC_IR_HDLC 0x04
|
||||
#define IRCC_IR_4PPM 0x01
|
||||
#define IRCC_IR_CONSUMER 0x02
|
||||
|
||||
#define IRCC_SCE_CFGB 0x01
|
||||
#define IRCC_CFGB_LOOPBACK 0x20
|
||||
#define IRCC_CFGB_LPBCK_TX_CRC 0x10
|
||||
#define IRCC_CFGB_NOWAIT 0x08
|
||||
#define IRCC_CFGB_STRING_MOVE 0x04
|
||||
#define IRCC_CFGB_DMA_BURST 0x02
|
||||
#define IRCC_CFGB_DMA_ENABLE 0x01
|
||||
|
||||
#define IRCC_CFGB_MUX_COM 0x00
|
||||
#define IRCC_CFGB_MUX_IR 0x40
|
||||
#define IRCC_CFGB_MUX_AUX 0x80
|
||||
#define IRCC_CFGB_MUX_INACTIVE 0xc0
|
||||
|
||||
/* Register block 3 - Identification Registers! */
|
||||
#define IRCC_ID_HIGH 0x00 /* 0x10 */
|
||||
#define IRCC_ID_LOW 0x01 /* 0xB8 */
|
||||
#define IRCC_CHIP_ID 0x02 /* 0xF1 */
|
||||
#define IRCC_VERSION 0x03 /* 0x01 */
|
||||
#define IRCC_INTERFACE 0x04 /* low 4 = DMA, high 4 = IRQ */
|
||||
#define IRCC_INTERFACE_DMA_MASK 0x0F /* low 4 = DMA, high 4 = IRQ */
|
||||
#define IRCC_INTERFACE_IRQ_MASK 0xF0 /* low 4 = DMA, high 4 = IRQ */
|
||||
|
||||
/* Register block 4 - IrDA */
|
||||
#define IRCC_CONTROL 0x00
|
||||
#define IRCC_BOF_COUNT_LO 0x01 /* Low byte */
|
||||
#define IRCC_BOF_COUNT_HI 0x00 /* High nibble (bit 0-3) */
|
||||
#define IRCC_BRICKWALL_CNT_LO 0x02 /* Low byte */
|
||||
#define IRCC_BRICKWALL_CNT_HI 0x03 /* High nibble (bit 4-7) */
|
||||
#define IRCC_TX_SIZE_LO 0x04 /* Low byte */
|
||||
#define IRCC_TX_SIZE_HI 0x03 /* High nibble (bit 0-3) */
|
||||
#define IRCC_RX_SIZE_HI 0x05 /* High nibble (bit 0-3) */
|
||||
#define IRCC_RX_SIZE_LO 0x06 /* Low byte */
|
||||
|
||||
#define IRCC_1152 0x80
|
||||
#define IRCC_CRC 0x40
|
||||
|
||||
/* Register block 5 - IrDA */
|
||||
#define IRCC_ATC 0x00
|
||||
#define IRCC_ATC_nPROGREADY 0x80
|
||||
#define IRCC_ATC_SPEED 0x40
|
||||
#define IRCC_ATC_ENABLE 0x20
|
||||
#define IRCC_ATC_MASK 0xE0
|
||||
|
||||
|
||||
#define IRCC_IRHALFDUPLEX_TIMEOUT 0x01
|
||||
|
||||
#define IRCC_SCE_TX_DELAY_TIMER 0x02
|
||||
|
||||
/*
|
||||
* Other definitions
|
||||
*/
|
||||
|
||||
#define SMSC_IRCC2_MAX_SIR_SPEED 115200
|
||||
#define SMSC_IRCC2_FIR_CHIP_IO_EXTENT 8
|
||||
#define SMSC_IRCC2_SIR_CHIP_IO_EXTENT 8
|
||||
#define SMSC_IRCC2_FIFO_SIZE 16
|
||||
#define SMSC_IRCC2_FIFO_THRESHOLD 64
|
||||
/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
|
||||
#define SMSC_IRCC2_RX_BUFF_TRUESIZE 14384
|
||||
#define SMSC_IRCC2_TX_BUFF_TRUESIZE 14384
|
||||
#define SMSC_IRCC2_MIN_TURN_TIME 0x07
|
||||
#define SMSC_IRCC2_WINDOW_SIZE 0x07
|
||||
/* Maximum wait for hw transmitter to finish */
|
||||
#define SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US 1000 /* 1 ms */
|
||||
/* Maximum wait for ATC transceiver programming to finish */
|
||||
#define SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES 1
|
||||
#endif /* SMSC_IRCC2_H */
|
|
@ -1,100 +0,0 @@
|
|||
#ifndef SMSC_SIO_H
|
||||
#define SMSC_SIO_H
|
||||
|
||||
/******************************************
|
||||
Keys. They should work with every SMsC SIO
|
||||
******************************************/
|
||||
|
||||
#define SMSCSIO_CFGACCESSKEY 0x55
|
||||
#define SMSCSIO_CFGEXITKEY 0xaa
|
||||
|
||||
/*****************************
|
||||
* Generic SIO Flat (!?) *
|
||||
*****************************/
|
||||
|
||||
/* Register 0x0d */
|
||||
#define SMSCSIOFLAT_DEVICEID_REG 0x0d
|
||||
|
||||
/* Register 0x0c */
|
||||
#define SMSCSIOFLAT_UARTMODE0C_REG 0x0c
|
||||
#define SMSCSIOFLAT_UART2MODE_MASK 0x38
|
||||
#define SMSCSIOFLAT_UART2MODE_VAL_COM 0x00
|
||||
#define SMSCSIOFLAT_UART2MODE_VAL_IRDA 0x08
|
||||
#define SMSCSIOFLAT_UART2MODE_VAL_ASKIR 0x10
|
||||
|
||||
/* Register 0x25 */
|
||||
#define SMSCSIOFLAT_UART2BASEADDR_REG 0x25
|
||||
|
||||
/* Register 0x2b */
|
||||
#define SMSCSIOFLAT_FIRBASEADDR_REG 0x2b
|
||||
|
||||
/* Register 0x2c */
|
||||
#define SMSCSIOFLAT_FIRDMASELECT_REG 0x2c
|
||||
#define SMSCSIOFLAT_FIRDMASELECT_MASK 0x0f
|
||||
|
||||
/* Register 0x28 */
|
||||
#define SMSCSIOFLAT_UARTIRQSELECT_REG 0x28
|
||||
#define SMSCSIOFLAT_UART2IRQSELECT_MASK 0x0f
|
||||
#define SMSCSIOFLAT_UART1IRQSELECT_MASK 0xf0
|
||||
#define SMSCSIOFLAT_UARTIRQSELECT_VAL_NONE 0x00
|
||||
|
||||
|
||||
/*********************
|
||||
* LPC47N227 *
|
||||
*********************/
|
||||
|
||||
#define LPC47N227_CFGACCESSKEY 0x55
|
||||
#define LPC47N227_CFGEXITKEY 0xaa
|
||||
|
||||
/* Register 0x00 */
|
||||
#define LPC47N227_FDCPOWERVALIDCONF_REG 0x00
|
||||
#define LPC47N227_FDCPOWER_MASK 0x08
|
||||
#define LPC47N227_VALID_MASK 0x80
|
||||
|
||||
/* Register 0x02 */
|
||||
#define LPC47N227_UART12POWER_REG 0x02
|
||||
#define LPC47N227_UART1POWERDOWN_MASK 0x08
|
||||
#define LPC47N227_UART2POWERDOWN_MASK 0x80
|
||||
|
||||
/* Register 0x07 */
|
||||
#define LPC47N227_APMBOOTDRIVE_REG 0x07
|
||||
#define LPC47N227_PARPORT2AUTOPWRDOWN_MASK 0x10 /* auto power down on if set */
|
||||
#define LPC47N227_UART2AUTOPWRDOWN_MASK 0x20 /* auto power down on if set */
|
||||
#define LPC47N227_UART1AUTOPWRDOWN_MASK 0x40 /* auto power down on if set */
|
||||
|
||||
/* Register 0x0c */
|
||||
#define LPC47N227_UARTMODE0C_REG 0x0c
|
||||
#define LPC47N227_UART2MODE_MASK 0x38
|
||||
#define LPC47N227_UART2MODE_VAL_COM 0x00
|
||||
#define LPC47N227_UART2MODE_VAL_IRDA 0x08
|
||||
#define LPC47N227_UART2MODE_VAL_ASKIR 0x10
|
||||
|
||||
/* Register 0x0d */
|
||||
#define LPC47N227_DEVICEID_REG 0x0d
|
||||
#define LPC47N227_DEVICEID_DEFVAL 0x5a
|
||||
|
||||
/* Register 0x0e */
|
||||
#define LPC47N227_REVISIONID_REG 0x0e
|
||||
|
||||
/* Register 0x25 */
|
||||
#define LPC47N227_UART2BASEADDR_REG 0x25
|
||||
|
||||
/* Register 0x28 */
|
||||
#define LPC47N227_UARTIRQSELECT_REG 0x28
|
||||
#define LPC47N227_UART2IRQSELECT_MASK 0x0f
|
||||
#define LPC47N227_UART1IRQSELECT_MASK 0xf0
|
||||
#define LPC47N227_UARTIRQSELECT_VAL_NONE 0x00
|
||||
|
||||
/* Register 0x2b */
|
||||
#define LPC47N227_FIRBASEADDR_REG 0x2b
|
||||
|
||||
/* Register 0x2c */
|
||||
#define LPC47N227_FIRDMASELECT_REG 0x2c
|
||||
#define LPC47N227_FIRDMASELECT_MASK 0x0f
|
||||
#define LPC47N227_FIRDMASELECT_VAL_DMA1 0x01 /* 47n227 has three dma channels */
|
||||
#define LPC47N227_FIRDMASELECT_VAL_DMA2 0x02
|
||||
#define LPC47N227_FIRDMASELECT_VAL_DMA3 0x03
|
||||
#define LPC47N227_FIRDMASELECT_VAL_NONE 0x0f
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -1,225 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: tekram.c
|
||||
* Version: 1.3
|
||||
* Description: Implementation of the Tekram IrMate IR-210B dongle
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Wed Oct 21 20:02:35 1998
|
||||
* Modified at: Sun Oct 27 22:02:38 2002
|
||||
* Modified by: Martin Diehl <mad@mdiehl.de>
|
||||
*
|
||||
* Copyright (c) 1998-1999 Dag Brattli,
|
||||
* Copyright (c) 2002 Martin Diehl,
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
|
||||
#include "sir-dev.h"
|
||||
|
||||
static int tekram_delay = 150; /* default is 150 ms */
|
||||
module_param(tekram_delay, int, 0);
|
||||
MODULE_PARM_DESC(tekram_delay, "tekram dongle write complete delay");
|
||||
|
||||
static int tekram_open(struct sir_dev *);
|
||||
static int tekram_close(struct sir_dev *);
|
||||
static int tekram_change_speed(struct sir_dev *, unsigned);
|
||||
static int tekram_reset(struct sir_dev *);
|
||||
|
||||
#define TEKRAM_115200 0x00
|
||||
#define TEKRAM_57600 0x01
|
||||
#define TEKRAM_38400 0x02
|
||||
#define TEKRAM_19200 0x03
|
||||
#define TEKRAM_9600 0x04
|
||||
|
||||
#define TEKRAM_PW 0x10 /* Pulse select bit */
|
||||
|
||||
static struct dongle_driver tekram = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "Tekram IR-210B",
|
||||
.type = IRDA_TEKRAM_DONGLE,
|
||||
.open = tekram_open,
|
||||
.close = tekram_close,
|
||||
.reset = tekram_reset,
|
||||
.set_speed = tekram_change_speed,
|
||||
};
|
||||
|
||||
static int __init tekram_sir_init(void)
|
||||
{
|
||||
if (tekram_delay < 1 || tekram_delay > 500)
|
||||
tekram_delay = 200;
|
||||
pr_debug("%s - using %d ms delay\n",
|
||||
tekram.driver_name, tekram_delay);
|
||||
return irda_register_dongle(&tekram);
|
||||
}
|
||||
|
||||
static void __exit tekram_sir_cleanup(void)
|
||||
{
|
||||
irda_unregister_dongle(&tekram);
|
||||
}
|
||||
|
||||
static int tekram_open(struct sir_dev *dev)
|
||||
{
|
||||
struct qos_info *qos = &dev->qos;
|
||||
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
|
||||
qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */
|
||||
irda_qos_bits_to_value(qos);
|
||||
|
||||
/* irda thread waits 50 msec for power settling */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tekram_close(struct sir_dev *dev)
|
||||
{
|
||||
/* Power off dongle */
|
||||
sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function tekram_change_speed (dev, state, speed)
|
||||
*
|
||||
* Set the speed for the Tekram IRMate 210 type dongle. Warning, this
|
||||
* function must be called with a process context!
|
||||
*
|
||||
* Algorithm
|
||||
* 1. clear DTR
|
||||
* 2. set RTS, and wait at least 7 us
|
||||
* 3. send Control Byte to the IR-210 through TXD to set new baud rate
|
||||
* wait until the stop bit of Control Byte is sent (for 9600 baud rate,
|
||||
* it takes about 100 msec)
|
||||
*
|
||||
* [oops, why 100 msec? sending 1 byte (10 bits) takes 1.05 msec
|
||||
* - is this probably to compensate for delays in tty layer?]
|
||||
*
|
||||
* 5. clear RTS (return to NORMAL Operation)
|
||||
* 6. wait at least 50 us, new setting (baud rate, etc) takes effect here
|
||||
* after
|
||||
*/
|
||||
|
||||
#define TEKRAM_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1)
|
||||
|
||||
static int tekram_change_speed(struct sir_dev *dev, unsigned speed)
|
||||
{
|
||||
unsigned state = dev->fsm.substate;
|
||||
unsigned delay = 0;
|
||||
u8 byte;
|
||||
static int ret = 0;
|
||||
|
||||
switch(state) {
|
||||
case SIRDEV_STATE_DONGLE_SPEED:
|
||||
|
||||
switch (speed) {
|
||||
default:
|
||||
speed = 9600;
|
||||
ret = -EINVAL;
|
||||
/* fall thru */
|
||||
case 9600:
|
||||
byte = TEKRAM_PW|TEKRAM_9600;
|
||||
break;
|
||||
case 19200:
|
||||
byte = TEKRAM_PW|TEKRAM_19200;
|
||||
break;
|
||||
case 38400:
|
||||
byte = TEKRAM_PW|TEKRAM_38400;
|
||||
break;
|
||||
case 57600:
|
||||
byte = TEKRAM_PW|TEKRAM_57600;
|
||||
break;
|
||||
case 115200:
|
||||
byte = TEKRAM_115200;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set DTR, Clear RTS */
|
||||
sirdev_set_dtr_rts(dev, TRUE, FALSE);
|
||||
|
||||
/* Wait at least 7us */
|
||||
udelay(14);
|
||||
|
||||
/* Write control byte */
|
||||
sirdev_raw_write(dev, &byte, 1);
|
||||
|
||||
dev->speed = speed;
|
||||
|
||||
state = TEKRAM_STATE_WAIT_SPEED;
|
||||
delay = tekram_delay;
|
||||
break;
|
||||
|
||||
case TEKRAM_STATE_WAIT_SPEED:
|
||||
/* Set DTR, Set RTS */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
udelay(50);
|
||||
break;
|
||||
|
||||
default:
|
||||
net_err_ratelimited("%s - undefined state %d\n",
|
||||
__func__, state);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
dev->fsm.substate = state;
|
||||
return (delay > 0) ? delay : ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function tekram_reset (driver)
|
||||
*
|
||||
* This function resets the tekram dongle. Warning, this function
|
||||
* must be called with a process context!!
|
||||
*
|
||||
* Algorithm:
|
||||
* 0. Clear RTS and DTR, and wait 50 ms (power off the IR-210 )
|
||||
* 1. clear RTS
|
||||
* 2. set DTR, and wait at least 1 ms
|
||||
* 3. clear DTR to SPACE state, wait at least 50 us for further
|
||||
* operation
|
||||
*/
|
||||
|
||||
static int tekram_reset(struct sir_dev *dev)
|
||||
{
|
||||
/* Clear DTR, Set RTS */
|
||||
sirdev_set_dtr_rts(dev, FALSE, TRUE);
|
||||
|
||||
/* Should sleep 1 ms */
|
||||
msleep(1);
|
||||
|
||||
/* Set DTR, Set RTS */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
/* Wait at least 50 us */
|
||||
udelay(75);
|
||||
|
||||
dev->speed = 9600;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
|
||||
MODULE_DESCRIPTION("Tekram IrMate IR-210B dongle driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("irda-dongle-0"); /* IRDA_TEKRAM_DONGLE */
|
||||
|
||||
module_init(tekram_sir_init);
|
||||
module_exit(tekram_sir_cleanup);
|
|
@ -1,358 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: toim3232-sir.c
|
||||
* Version: 1.0
|
||||
* Description: Implementation of dongles based on the Vishay/Temic
|
||||
* TOIM3232 SIR Endec chipset. Currently only the
|
||||
* IRWave IR320ST-2 is tested, although it should work
|
||||
* with any TOIM3232 or TOIM4232 chipset based RS232
|
||||
* dongle with minimal modification.
|
||||
* Based heavily on the Tekram driver (tekram.c),
|
||||
* with thanks to Dag Brattli and Martin Diehl.
|
||||
* Status: Experimental.
|
||||
* Author: David Basden <davidb-irda@rcpt.to>
|
||||
* Created at: Thu Feb 09 23:47:32 2006
|
||||
*
|
||||
* Copyright (c) 2006 David Basden.
|
||||
* Copyright (c) 1998-1999 Dag Brattli,
|
||||
* Copyright (c) 2002 Martin Diehl,
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
/*
|
||||
* This driver has currently only been tested on the IRWave IR320ST-2
|
||||
*
|
||||
* PROTOCOL:
|
||||
*
|
||||
* The protocol for talking to the TOIM3232 is quite easy, and is
|
||||
* designed to interface with RS232 with only level convertors. The
|
||||
* BR/~D line on the chip is brought high to signal 'command mode',
|
||||
* where a command byte is sent to select the baudrate of the RS232
|
||||
* interface and the pulse length of the IRDA output. When BR/~D
|
||||
* is brought low, the dongle then changes to the selected baudrate,
|
||||
* and the RS232 interface is used for data until BR/~D is brought
|
||||
* high again. The initial speed for the TOIMx323 after RESET is
|
||||
* 9600 baud. The baudrate for command-mode is the last selected
|
||||
* baud-rate, or 9600 after a RESET.
|
||||
*
|
||||
* The dongle I have (below) adds some extra hardware on the front end,
|
||||
* but this is mostly directed towards pariasitic power from the RS232
|
||||
* line rather than changing very much about how to communicate with
|
||||
* the TOIM3232.
|
||||
*
|
||||
* The protocol to talk to the TOIM4232 chipset seems to be almost
|
||||
* identical to the TOIM3232 (and the 4232 datasheet is more detailed)
|
||||
* so this code will probably work on that as well, although I haven't
|
||||
* tested it on that hardware.
|
||||
*
|
||||
* Target dongle variations that might be common:
|
||||
*
|
||||
* DTR and RTS function:
|
||||
* The data sheet for the 4232 has a sample implementation that hooks the
|
||||
* DTR and RTS lines to the RESET and BaudRate/~Data lines of the
|
||||
* chip (through line-converters). Given both DTR and RTS would have to
|
||||
* be held low in normal operation, and the TOIMx232 requires +5V to
|
||||
* signal ground, most dongle designers would almost certainly choose
|
||||
* an implementation that kept at least one of DTR or RTS high in
|
||||
* normal operation to provide power to the dongle, but will likely
|
||||
* vary between designs.
|
||||
*
|
||||
* User specified command bits:
|
||||
* There are two user-controllable output lines from the TOIMx232 that
|
||||
* can be set low or high by setting the appropriate bits in the
|
||||
* high-nibble of the command byte (when setting speed and pulse length).
|
||||
* These might be used to switch on and off added hardware or extra
|
||||
* dongle features.
|
||||
*
|
||||
*
|
||||
* Target hardware: IRWave IR320ST-2
|
||||
*
|
||||
* The IRWave IR320ST-2 is a simple dongle based on the Vishay/Temic
|
||||
* TOIM3232 SIR Endec and the Vishay/Temic TFDS4500 SIR IRDA transceiver.
|
||||
* It uses a hex inverter and some discrete components to buffer and
|
||||
* line convert the RS232 down to 5V.
|
||||
*
|
||||
* The dongle is powered through a voltage regulator, fed by a large
|
||||
* capacitor. To switch the dongle on, DTR is brought high to charge
|
||||
* the capacitor and drive the voltage regulator. DTR isn't associated
|
||||
* with any control lines on the TOIM3232. Parisitic power is also taken
|
||||
* from the RTS, TD and RD lines when brought high, but through resistors.
|
||||
* When DTR is low, the circuit might lose power even with RTS high.
|
||||
*
|
||||
* RTS is inverted and attached to the BR/~D input pin. When RTS
|
||||
* is high, BR/~D is low, and the TOIM3232 is in the normal 'data' mode.
|
||||
* RTS is brought low, BR/~D is high, and the TOIM3232 is in 'command
|
||||
* mode'.
|
||||
*
|
||||
* For some unknown reason, the RESET line isn't actually connected
|
||||
* to anything. This means to reset the dongle to get it to a known
|
||||
* state (9600 baud) you must drop DTR and RTS low, wait for the power
|
||||
* capacitor to discharge, and then bring DTR (and RTS for data mode)
|
||||
* high again, and wait for the capacitor to charge, the power supply
|
||||
* to stabilise, and the oscillator clock to stabilise.
|
||||
*
|
||||
* Fortunately, if the current baudrate is known, the chipset can
|
||||
* easily change speed by entering command mode without having to
|
||||
* reset the dongle first.
|
||||
*
|
||||
* Major Components:
|
||||
*
|
||||
* - Vishay/Temic TOIM3232 SIR Endec to change RS232 pulse timings
|
||||
* to IRDA pulse timings
|
||||
* - 3.6864MHz crystal to drive TOIM3232 clock oscillator
|
||||
* - DM74lS04M Inverting Hex line buffer for RS232 input buffering
|
||||
* and level conversion
|
||||
* - PJ2951AC 150mA voltage regulator
|
||||
* - Vishay/Temic TFDS4500 SIR IRDA front-end transceiver
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
|
||||
#include "sir-dev.h"
|
||||
|
||||
static int toim3232delay = 150; /* default is 150 ms */
|
||||
module_param(toim3232delay, int, 0);
|
||||
MODULE_PARM_DESC(toim3232delay, "toim3232 dongle write complete delay");
|
||||
|
||||
static int toim3232_open(struct sir_dev *);
|
||||
static int toim3232_close(struct sir_dev *);
|
||||
static int toim3232_change_speed(struct sir_dev *, unsigned);
|
||||
static int toim3232_reset(struct sir_dev *);
|
||||
|
||||
#define TOIM3232_115200 0x00
|
||||
#define TOIM3232_57600 0x01
|
||||
#define TOIM3232_38400 0x02
|
||||
#define TOIM3232_19200 0x03
|
||||
#define TOIM3232_9600 0x06
|
||||
#define TOIM3232_2400 0x0A
|
||||
|
||||
#define TOIM3232_PW 0x10 /* Pulse select bit */
|
||||
|
||||
static struct dongle_driver toim3232 = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "Vishay TOIM3232",
|
||||
.type = IRDA_TOIM3232_DONGLE,
|
||||
.open = toim3232_open,
|
||||
.close = toim3232_close,
|
||||
.reset = toim3232_reset,
|
||||
.set_speed = toim3232_change_speed,
|
||||
};
|
||||
|
||||
static int __init toim3232_sir_init(void)
|
||||
{
|
||||
if (toim3232delay < 1 || toim3232delay > 500)
|
||||
toim3232delay = 200;
|
||||
pr_debug("%s - using %d ms delay\n",
|
||||
toim3232.driver_name, toim3232delay);
|
||||
return irda_register_dongle(&toim3232);
|
||||
}
|
||||
|
||||
static void __exit toim3232_sir_cleanup(void)
|
||||
{
|
||||
irda_unregister_dongle(&toim3232);
|
||||
}
|
||||
|
||||
static int toim3232_open(struct sir_dev *dev)
|
||||
{
|
||||
struct qos_info *qos = &dev->qos;
|
||||
|
||||
/* Pull the lines high to start with.
|
||||
*
|
||||
* For the IR320ST-2, we need to charge the main supply capacitor to
|
||||
* switch the device on. We keep DTR high throughout to do this.
|
||||
* When RTS, TD and RD are high, they will also trickle-charge the
|
||||
* cap. RTS is high for data transmission, and low for baud rate select.
|
||||
* -- DGB
|
||||
*/
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
/* The TOI3232 supports many speeds between 1200bps and 115000bps.
|
||||
* We really only care about those supported by the IRDA spec, but
|
||||
* 38400 seems to be implemented in many places */
|
||||
qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
|
||||
|
||||
/* From the tekram driver. Not sure what a reasonable value is -- DGB */
|
||||
qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */
|
||||
irda_qos_bits_to_value(qos);
|
||||
|
||||
/* irda thread waits 50 msec for power settling */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int toim3232_close(struct sir_dev *dev)
|
||||
{
|
||||
/* Power off dongle */
|
||||
sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function toim3232change_speed (dev, state, speed)
|
||||
*
|
||||
* Set the speed for the TOIM3232 based dongle. Warning, this
|
||||
* function must be called with a process context!
|
||||
*
|
||||
* Algorithm
|
||||
* 1. keep DTR high but clear RTS to bring into baud programming mode
|
||||
* 2. wait at least 7us to enter programming mode
|
||||
* 3. send control word to set baud rate and timing
|
||||
* 4. wait at least 1us
|
||||
* 5. bring RTS high to enter DATA mode (RS232 is passed through to transceiver)
|
||||
* 6. should take effect immediately (although probably worth waiting)
|
||||
*/
|
||||
|
||||
#define TOIM3232_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1)
|
||||
|
||||
static int toim3232_change_speed(struct sir_dev *dev, unsigned speed)
|
||||
{
|
||||
unsigned state = dev->fsm.substate;
|
||||
unsigned delay = 0;
|
||||
u8 byte;
|
||||
static int ret = 0;
|
||||
|
||||
switch(state) {
|
||||
case SIRDEV_STATE_DONGLE_SPEED:
|
||||
|
||||
/* Figure out what we are going to send as a control byte */
|
||||
switch (speed) {
|
||||
case 2400:
|
||||
byte = TOIM3232_PW|TOIM3232_2400;
|
||||
break;
|
||||
default:
|
||||
speed = 9600;
|
||||
ret = -EINVAL;
|
||||
/* fall thru */
|
||||
case 9600:
|
||||
byte = TOIM3232_PW|TOIM3232_9600;
|
||||
break;
|
||||
case 19200:
|
||||
byte = TOIM3232_PW|TOIM3232_19200;
|
||||
break;
|
||||
case 38400:
|
||||
byte = TOIM3232_PW|TOIM3232_38400;
|
||||
break;
|
||||
case 57600:
|
||||
byte = TOIM3232_PW|TOIM3232_57600;
|
||||
break;
|
||||
case 115200:
|
||||
byte = TOIM3232_115200;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set DTR, Clear RTS: Go into baud programming mode */
|
||||
sirdev_set_dtr_rts(dev, TRUE, FALSE);
|
||||
|
||||
/* Wait at least 7us */
|
||||
udelay(14);
|
||||
|
||||
/* Write control byte */
|
||||
sirdev_raw_write(dev, &byte, 1);
|
||||
|
||||
dev->speed = speed;
|
||||
|
||||
state = TOIM3232_STATE_WAIT_SPEED;
|
||||
delay = toim3232delay;
|
||||
break;
|
||||
|
||||
case TOIM3232_STATE_WAIT_SPEED:
|
||||
/* Have transmitted control byte * Wait for 'at least 1us' */
|
||||
udelay(14);
|
||||
|
||||
/* Set DTR, Set RTS: Go into normal data mode */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
/* Wait (TODO: check this is needed) */
|
||||
udelay(50);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_ERR "%s - undefined state %d\n", __func__, state);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
dev->fsm.substate = state;
|
||||
return (delay > 0) ? delay : ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function toim3232reset (driver)
|
||||
*
|
||||
* This function resets the toim3232 dongle. Warning, this function
|
||||
* must be called with a process context!!
|
||||
*
|
||||
* What we should do is:
|
||||
* 0. Pull RESET high
|
||||
* 1. Wait for at least 7us
|
||||
* 2. Pull RESET low
|
||||
* 3. Wait for at least 7us
|
||||
* 4. Pull BR/~D high
|
||||
* 5. Wait for at least 7us
|
||||
* 6. Send control byte to set baud rate
|
||||
* 7. Wait at least 1us after stop bit
|
||||
* 8. Pull BR/~D low
|
||||
* 9. Should then be in data mode
|
||||
*
|
||||
* Because the IR320ST-2 doesn't have the RESET line connected for some reason,
|
||||
* we'll have to do something else.
|
||||
*
|
||||
* The default speed after a RESET is 9600, so lets try just bringing it up in
|
||||
* data mode after switching it off, waiting for the supply capacitor to
|
||||
* discharge, and then switch it back on. This isn't actually pulling RESET
|
||||
* high, but it seems to have the same effect.
|
||||
*
|
||||
* This behaviour will probably work on dongles that have the RESET line connected,
|
||||
* but if not, add a flag for the IR320ST-2, and implment the above-listed proper
|
||||
* behaviour.
|
||||
*
|
||||
* RTS is inverted and then fed to BR/~D, so to put it in programming mode, we
|
||||
* need to have pull RTS low
|
||||
*/
|
||||
|
||||
static int toim3232_reset(struct sir_dev *dev)
|
||||
{
|
||||
/* Switch off both DTR and RTS to switch off dongle */
|
||||
sirdev_set_dtr_rts(dev, FALSE, FALSE);
|
||||
|
||||
/* Should sleep a while. This might be evil doing it this way.*/
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
schedule_timeout(msecs_to_jiffies(50));
|
||||
|
||||
/* Set DTR, Set RTS (data mode) */
|
||||
sirdev_set_dtr_rts(dev, TRUE, TRUE);
|
||||
|
||||
/* Wait at least 10 ms for power to stabilize again */
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
schedule_timeout(msecs_to_jiffies(10));
|
||||
|
||||
/* Speed should now be 9600 */
|
||||
dev->speed = 9600;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("David Basden <davidb-linux@rcpt.to>");
|
||||
MODULE_DESCRIPTION("Vishay/Temic TOIM3232 based dongle driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("irda-dongle-12"); /* IRDA_TOIM3232_DONGLE */
|
||||
|
||||
module_init(toim3232_sir_init);
|
||||
module_exit(toim3232_sir_cleanup);
|
File diff suppressed because it is too large
Load Diff
|
@ -1,846 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: via-ircc.h
|
||||
* Version: 1.0
|
||||
* Description: Driver for the VIA VT8231/VT8233 IrDA chipsets
|
||||
* Author: VIA Technologies, inc
|
||||
* Date : 08/06/2003
|
||||
|
||||
Copyright (c) 1998-2003 VIA Technologies, Inc.
|
||||
|
||||
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, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTIES OR REPRESENTATIONS; 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
* Comment:
|
||||
* jul/08/2002 : Rx buffer length should use Rx ring ptr.
|
||||
* Oct/28/2002 : Add SB id for 3147 and 3177.
|
||||
* jul/09/2002 : only implement two kind of dongle currently.
|
||||
* Oct/02/2002 : work on VT8231 and VT8233 .
|
||||
* Aug/06/2003 : change driver format to pci driver .
|
||||
********************************************************************/
|
||||
#ifndef via_IRCC_H
|
||||
#define via_IRCC_H
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define MAX_TX_WINDOW 7
|
||||
#define MAX_RX_WINDOW 7
|
||||
|
||||
struct st_fifo_entry {
|
||||
int status;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct st_fifo {
|
||||
struct st_fifo_entry entries[MAX_RX_WINDOW + 2];
|
||||
int pending_bytes;
|
||||
int head;
|
||||
int tail;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct frame_cb {
|
||||
void *start; /* Start of frame in DMA mem */
|
||||
int len; /* Length of frame in DMA mem */
|
||||
};
|
||||
|
||||
struct tx_fifo {
|
||||
struct frame_cb queue[MAX_TX_WINDOW + 2]; /* Info about frames in queue */
|
||||
int ptr; /* Currently being sent */
|
||||
int len; /* Length of queue */
|
||||
int free; /* Next free slot */
|
||||
void *tail; /* Next free start in DMA mem */
|
||||
};
|
||||
|
||||
|
||||
struct eventflag // for keeping track of Interrupt Events
|
||||
{
|
||||
//--------tx part
|
||||
unsigned char TxFIFOUnderRun;
|
||||
unsigned char EOMessage;
|
||||
unsigned char TxFIFOReady;
|
||||
unsigned char EarlyEOM;
|
||||
//--------rx part
|
||||
unsigned char PHYErr;
|
||||
unsigned char CRCErr;
|
||||
unsigned char RxFIFOOverRun;
|
||||
unsigned char EOPacket;
|
||||
unsigned char RxAvail;
|
||||
unsigned char TooLargePacket;
|
||||
unsigned char SIRBad;
|
||||
//--------unknown
|
||||
unsigned char Unknown;
|
||||
//----------
|
||||
unsigned char TimeOut;
|
||||
unsigned char RxDMATC;
|
||||
unsigned char TxDMATC;
|
||||
};
|
||||
|
||||
/* Private data for each instance */
|
||||
struct via_ircc_cb {
|
||||
struct st_fifo st_fifo; /* Info about received frames */
|
||||
struct tx_fifo tx_fifo; /* Info about frames to be transmitted */
|
||||
|
||||
struct net_device *netdev; /* Yes! we are some kind of netdevice */
|
||||
|
||||
struct irlap_cb *irlap; /* The link layer we are binded to */
|
||||
struct qos_info qos; /* QoS capabilities for this device */
|
||||
|
||||
chipio_t io; /* IrDA controller information */
|
||||
iobuff_t tx_buff; /* Transmit buffer */
|
||||
iobuff_t rx_buff; /* Receive buffer */
|
||||
dma_addr_t tx_buff_dma;
|
||||
dma_addr_t rx_buff_dma;
|
||||
|
||||
__u8 ier; /* Interrupt enable register */
|
||||
|
||||
spinlock_t lock; /* For serializing operations */
|
||||
|
||||
__u32 flags; /* Interface flags */
|
||||
__u32 new_speed;
|
||||
int index; /* Instance index */
|
||||
|
||||
struct eventflag EventFlag;
|
||||
unsigned int chip_id; /* to remember chip id */
|
||||
unsigned int RetryCount;
|
||||
unsigned int RxDataReady;
|
||||
unsigned int RxLastCount;
|
||||
};
|
||||
|
||||
|
||||
//---------I=Infrared, H=Host, M=Misc, T=Tx, R=Rx, ST=Status,
|
||||
// CF=Config, CT=Control, L=Low, H=High, C=Count
|
||||
#define I_CF_L_0 0x10
|
||||
#define I_CF_H_0 0x11
|
||||
#define I_SIR_BOF 0x12
|
||||
#define I_SIR_EOF 0x13
|
||||
#define I_ST_CT_0 0x15
|
||||
#define I_ST_L_1 0x16
|
||||
#define I_ST_H_1 0x17
|
||||
#define I_CF_L_1 0x18
|
||||
#define I_CF_H_1 0x19
|
||||
#define I_CF_L_2 0x1a
|
||||
#define I_CF_H_2 0x1b
|
||||
#define I_CF_3 0x1e
|
||||
#define H_CT 0x20
|
||||
#define H_ST 0x21
|
||||
#define M_CT 0x22
|
||||
#define TX_CT_1 0x23
|
||||
#define TX_CT_2 0x24
|
||||
#define TX_ST 0x25
|
||||
#define RX_CT 0x26
|
||||
#define RX_ST 0x27
|
||||
#define RESET 0x28
|
||||
#define P_ADDR 0x29
|
||||
#define RX_C_L 0x2a
|
||||
#define RX_C_H 0x2b
|
||||
#define RX_P_L 0x2c
|
||||
#define RX_P_H 0x2d
|
||||
#define TX_C_L 0x2e
|
||||
#define TX_C_H 0x2f
|
||||
#define TIMER 0x32
|
||||
#define I_CF_4 0x33
|
||||
#define I_T_C_L 0x34
|
||||
#define I_T_C_H 0x35
|
||||
#define VERSION 0x3f
|
||||
//-------------------------------
|
||||
#define StartAddr 0x10 // the first register address
|
||||
#define EndAddr 0x3f // the last register address
|
||||
#define GetBit(val,bit) val = (unsigned char) ((val>>bit) & 0x1)
|
||||
// Returns the bit
|
||||
#define SetBit(val,bit) val= (unsigned char ) (val | (0x1 << bit))
|
||||
// Sets bit to 1
|
||||
#define ResetBit(val,bit) val= (unsigned char ) (val & ~(0x1 << bit))
|
||||
// Sets bit to 0
|
||||
|
||||
#define OFF 0
|
||||
#define ON 1
|
||||
#define DMA_TX_MODE 0x08
|
||||
#define DMA_RX_MODE 0x04
|
||||
|
||||
#define DMA1 0
|
||||
#define DMA2 0xc0
|
||||
#define MASK1 DMA1+0x0a
|
||||
#define MASK2 DMA2+0x14
|
||||
|
||||
#define Clk_bit 0x40
|
||||
#define Tx_bit 0x01
|
||||
#define Rd_Valid 0x08
|
||||
#define RxBit 0x08
|
||||
|
||||
static void DisableDmaChannel(unsigned int channel)
|
||||
{
|
||||
switch (channel) { // 8 Bit DMA channels DMAC1
|
||||
case 0:
|
||||
outb(4, MASK1); //mask channel 0
|
||||
break;
|
||||
case 1:
|
||||
outb(5, MASK1); //Mask channel 1
|
||||
break;
|
||||
case 2:
|
||||
outb(6, MASK1); //Mask channel 2
|
||||
break;
|
||||
case 3:
|
||||
outb(7, MASK1); //Mask channel 3
|
||||
break;
|
||||
case 5:
|
||||
outb(5, MASK2); //Mask channel 5
|
||||
break;
|
||||
case 6:
|
||||
outb(6, MASK2); //Mask channel 6
|
||||
break;
|
||||
case 7:
|
||||
outb(7, MASK2); //Mask channel 7
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char ReadLPCReg(int iRegNum)
|
||||
{
|
||||
unsigned char iVal;
|
||||
|
||||
outb(0x87, 0x2e);
|
||||
outb(0x87, 0x2e);
|
||||
outb(iRegNum, 0x2e);
|
||||
iVal = inb(0x2f);
|
||||
outb(0xaa, 0x2e);
|
||||
|
||||
return iVal;
|
||||
}
|
||||
|
||||
static void WriteLPCReg(int iRegNum, unsigned char iVal)
|
||||
{
|
||||
|
||||
outb(0x87, 0x2e);
|
||||
outb(0x87, 0x2e);
|
||||
outb(iRegNum, 0x2e);
|
||||
outb(iVal, 0x2f);
|
||||
outb(0xAA, 0x2e);
|
||||
}
|
||||
|
||||
static __u8 ReadReg(unsigned int BaseAddr, int iRegNum)
|
||||
{
|
||||
return (__u8) inb(BaseAddr + iRegNum);
|
||||
}
|
||||
|
||||
static void WriteReg(unsigned int BaseAddr, int iRegNum, unsigned char iVal)
|
||||
{
|
||||
outb(iVal, BaseAddr + iRegNum);
|
||||
}
|
||||
|
||||
static int WriteRegBit(unsigned int BaseAddr, unsigned char RegNum,
|
||||
unsigned char BitPos, unsigned char value)
|
||||
{
|
||||
__u8 Rtemp, Wtemp;
|
||||
|
||||
if (BitPos > 7) {
|
||||
return -1;
|
||||
}
|
||||
if ((RegNum < StartAddr) || (RegNum > EndAddr))
|
||||
return -1;
|
||||
Rtemp = ReadReg(BaseAddr, RegNum);
|
||||
if (value == 0)
|
||||
Wtemp = ResetBit(Rtemp, BitPos);
|
||||
else {
|
||||
if (value == 1)
|
||||
Wtemp = SetBit(Rtemp, BitPos);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
WriteReg(BaseAddr, RegNum, Wtemp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __u8 CheckRegBit(unsigned int BaseAddr, unsigned char RegNum,
|
||||
unsigned char BitPos)
|
||||
{
|
||||
__u8 temp;
|
||||
|
||||
if (BitPos > 7)
|
||||
return 0xff;
|
||||
if ((RegNum < StartAddr) || (RegNum > EndAddr)) {
|
||||
// printf("what is the register %x!\n",RegNum);
|
||||
}
|
||||
temp = ReadReg(BaseAddr, RegNum);
|
||||
return GetBit(temp, BitPos);
|
||||
}
|
||||
|
||||
static void SetMaxRxPacketSize(__u16 iobase, __u16 size)
|
||||
{
|
||||
__u16 low, high;
|
||||
if ((size & 0xe000) == 0) {
|
||||
low = size & 0x00ff;
|
||||
high = (size & 0x1f00) >> 8;
|
||||
WriteReg(iobase, I_CF_L_2, low);
|
||||
WriteReg(iobase, I_CF_H_2, high);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//for both Rx and Tx
|
||||
|
||||
static void SetFIFO(__u16 iobase, __u16 value)
|
||||
{
|
||||
switch (value) {
|
||||
case 128:
|
||||
WriteRegBit(iobase, 0x11, 0, 0);
|
||||
WriteRegBit(iobase, 0x11, 7, 1);
|
||||
break;
|
||||
case 64:
|
||||
WriteRegBit(iobase, 0x11, 0, 0);
|
||||
WriteRegBit(iobase, 0x11, 7, 0);
|
||||
break;
|
||||
case 32:
|
||||
WriteRegBit(iobase, 0x11, 0, 1);
|
||||
WriteRegBit(iobase, 0x11, 7, 0);
|
||||
break;
|
||||
default:
|
||||
WriteRegBit(iobase, 0x11, 0, 0);
|
||||
WriteRegBit(iobase, 0x11, 7, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#define CRC16(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,7,val) //0 for 32 CRC
|
||||
/*
|
||||
#define SetVFIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,5,val)
|
||||
#define SetFIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,6,val)
|
||||
#define SetMIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,5,val)
|
||||
#define SetSIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,4,val)
|
||||
*/
|
||||
#define SIRFilter(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,3,val)
|
||||
#define Filter(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,2,val)
|
||||
#define InvertTX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,1,val)
|
||||
#define InvertRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,0,val)
|
||||
//****************************I_CF_H_0
|
||||
#define EnableTX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,4,val)
|
||||
#define EnableRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,3,val)
|
||||
#define EnableDMA(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,2,val)
|
||||
#define SIRRecvAny(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,1,val)
|
||||
#define DiableTrans(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,0,val)
|
||||
//***************************I_SIR_BOF,I_SIR_EOF
|
||||
#define SetSIRBOF(BaseAddr,val) WriteReg(BaseAddr,I_SIR_BOF,val)
|
||||
#define SetSIREOF(BaseAddr,val) WriteReg(BaseAddr,I_SIR_EOF,val)
|
||||
#define GetSIRBOF(BaseAddr) ReadReg(BaseAddr,I_SIR_BOF)
|
||||
#define GetSIREOF(BaseAddr) ReadReg(BaseAddr,I_SIR_EOF)
|
||||
//*******************I_ST_CT_0
|
||||
#define EnPhys(BaseAddr,val) WriteRegBit(BaseAddr,I_ST_CT_0,7,val)
|
||||
#define IsModeError(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,6) //RO
|
||||
#define IsVFIROn(BaseAddr) CheckRegBit(BaseAddr,0x14,0) //RO for VT1211 only
|
||||
#define IsFIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,5) //RO
|
||||
#define IsMIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,4) //RO
|
||||
#define IsSIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,3) //RO
|
||||
#define IsEnableTX(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,2) //RO
|
||||
#define IsEnableRX(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,1) //RO
|
||||
#define Is16CRC(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,0) //RO
|
||||
//***************************I_CF_3
|
||||
#define DisableAdjacentPulseWidth(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,5,val) //1 disable
|
||||
#define DisablePulseWidthAdjust(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,4,val) //1 disable
|
||||
#define UseOneRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,1,val) //0 use two RX
|
||||
#define SlowIRRXLowActive(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,0,val) //0 show RX high=1 in SIR
|
||||
//***************************H_CT
|
||||
#define EnAllInt(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,7,val)
|
||||
#define TXStart(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,6,val)
|
||||
#define RXStart(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,5,val)
|
||||
#define ClearRXInt(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,4,val) // 1 clear
|
||||
//*****************H_ST
|
||||
#define IsRXInt(BaseAddr) CheckRegBit(BaseAddr,H_ST,4)
|
||||
#define GetIntIndentify(BaseAddr) ((ReadReg(BaseAddr,H_ST)&0xf1) >>1)
|
||||
#define IsHostBusy(BaseAddr) CheckRegBit(BaseAddr,H_ST,0)
|
||||
#define GetHostStatus(BaseAddr) ReadReg(BaseAddr,H_ST) //RO
|
||||
//**************************M_CT
|
||||
#define EnTXDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,7,val)
|
||||
#define EnRXDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,6,val)
|
||||
#define SwapDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,5,val)
|
||||
#define EnInternalLoop(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,4,val)
|
||||
#define EnExternalLoop(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,3,val)
|
||||
//**************************TX_CT_1
|
||||
#define EnTXFIFOHalfLevelInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,4,val) //half empty int (1 half)
|
||||
#define EnTXFIFOUnderrunEOMInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,5,val)
|
||||
#define EnTXFIFOReadyInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,6,val) //int when reach it threshold (setting by bit 4)
|
||||
//**************************TX_CT_2
|
||||
#define ForceUnderrun(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,7,val) // force an underrun int
|
||||
#define EnTXCRC(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,6,val) //1 for FIR,MIR...0 (not SIR)
|
||||
#define ForceBADCRC(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,5,val) //force an bad CRC
|
||||
#define SendSIP(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,4,val) //send indication pulse for prevent SIR disturb
|
||||
#define ClearEnTX(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,3,val) // opposite to EnTX
|
||||
//*****************TX_ST
|
||||
#define GetTXStatus(BaseAddr) ReadReg(BaseAddr,TX_ST) //RO
|
||||
//**************************RX_CT
|
||||
#define EnRXSpecInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,0,val)
|
||||
#define EnRXFIFOReadyInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,1,val) //enable int when reach it threshold (setting by bit 7)
|
||||
#define EnRXFIFOHalfLevelInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,7,val) //enable int when (1) half full...or (0) just not full
|
||||
//*****************RX_ST
|
||||
#define GetRXStatus(BaseAddr) ReadReg(BaseAddr,RX_ST) //RO
|
||||
//***********************P_ADDR
|
||||
#define SetPacketAddr(BaseAddr,addr) WriteReg(BaseAddr,P_ADDR,addr)
|
||||
//***********************I_CF_4
|
||||
#define EnGPIOtoRX2(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,7,val)
|
||||
#define EnTimerInt(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,1,val)
|
||||
#define ClearTimerInt(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,0,val)
|
||||
//***********************I_T_C_L
|
||||
#define WriteGIO(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_L,7,val)
|
||||
#define ReadGIO(BaseAddr) CheckRegBit(BaseAddr,I_T_C_L,7)
|
||||
#define ReadRX(BaseAddr) CheckRegBit(BaseAddr,I_T_C_L,3) //RO
|
||||
#define WriteTX(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_L,0,val)
|
||||
//***********************I_T_C_H
|
||||
#define EnRX2(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_H,7,val)
|
||||
#define ReadRX2(BaseAddr) CheckRegBit(BaseAddr,I_T_C_H,7)
|
||||
//**********************Version
|
||||
#define GetFIRVersion(BaseAddr) ReadReg(BaseAddr,VERSION)
|
||||
|
||||
|
||||
static void SetTimer(__u16 iobase, __u8 count)
|
||||
{
|
||||
EnTimerInt(iobase, OFF);
|
||||
WriteReg(iobase, TIMER, count);
|
||||
EnTimerInt(iobase, ON);
|
||||
}
|
||||
|
||||
|
||||
static void SetSendByte(__u16 iobase, __u32 count)
|
||||
{
|
||||
__u32 low, high;
|
||||
|
||||
if ((count & 0xf000) == 0) {
|
||||
low = count & 0x00ff;
|
||||
high = (count & 0x0f00) >> 8;
|
||||
WriteReg(iobase, TX_C_L, low);
|
||||
WriteReg(iobase, TX_C_H, high);
|
||||
}
|
||||
}
|
||||
|
||||
static void ResetChip(__u16 iobase, __u8 type)
|
||||
{
|
||||
__u8 value;
|
||||
|
||||
value = (type + 2) << 4;
|
||||
WriteReg(iobase, RESET, type);
|
||||
}
|
||||
|
||||
static int CkRxRecv(__u16 iobase, struct via_ircc_cb *self)
|
||||
{
|
||||
__u8 low, high;
|
||||
__u16 wTmp = 0, wTmp1 = 0, wTmp_new = 0;
|
||||
|
||||
low = ReadReg(iobase, RX_C_L);
|
||||
high = ReadReg(iobase, RX_C_H);
|
||||
wTmp1 = high;
|
||||
wTmp = (wTmp1 << 8) | low;
|
||||
udelay(10);
|
||||
low = ReadReg(iobase, RX_C_L);
|
||||
high = ReadReg(iobase, RX_C_H);
|
||||
wTmp1 = high;
|
||||
wTmp_new = (wTmp1 << 8) | low;
|
||||
if (wTmp_new != wTmp)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static __u16 RxCurCount(__u16 iobase, struct via_ircc_cb * self)
|
||||
{
|
||||
__u8 low, high;
|
||||
__u16 wTmp = 0, wTmp1 = 0;
|
||||
|
||||
low = ReadReg(iobase, RX_P_L);
|
||||
high = ReadReg(iobase, RX_P_H);
|
||||
wTmp1 = high;
|
||||
wTmp = (wTmp1 << 8) | low;
|
||||
return wTmp;
|
||||
}
|
||||
|
||||
/* This Routine can only use in recevie_complete
|
||||
* for it will update last count.
|
||||
*/
|
||||
|
||||
static __u16 GetRecvByte(__u16 iobase, struct via_ircc_cb * self)
|
||||
{
|
||||
__u8 low, high;
|
||||
__u16 wTmp, wTmp1, ret;
|
||||
|
||||
low = ReadReg(iobase, RX_P_L);
|
||||
high = ReadReg(iobase, RX_P_H);
|
||||
wTmp1 = high;
|
||||
wTmp = (wTmp1 << 8) | low;
|
||||
|
||||
|
||||
if (wTmp >= self->RxLastCount)
|
||||
ret = wTmp - self->RxLastCount;
|
||||
else
|
||||
ret = (0x8000 - self->RxLastCount) + wTmp;
|
||||
self->RxLastCount = wTmp;
|
||||
|
||||
/* RX_P is more actually the RX_C
|
||||
low=ReadReg(iobase,RX_C_L);
|
||||
high=ReadReg(iobase,RX_C_H);
|
||||
|
||||
if(!(high&0xe000)) {
|
||||
temp=(high<<8)+low;
|
||||
return temp;
|
||||
}
|
||||
else return 0;
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void Sdelay(__u16 scale)
|
||||
{
|
||||
__u8 bTmp;
|
||||
int i, j;
|
||||
|
||||
for (j = 0; j < scale; j++) {
|
||||
for (i = 0; i < 0x20; i++) {
|
||||
bTmp = inb(0xeb);
|
||||
outb(bTmp, 0xeb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Tdelay(__u16 scale)
|
||||
{
|
||||
__u8 bTmp;
|
||||
int i, j;
|
||||
|
||||
for (j = 0; j < scale; j++) {
|
||||
for (i = 0; i < 0x50; i++) {
|
||||
bTmp = inb(0xeb);
|
||||
outb(bTmp, 0xeb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ActClk(__u16 iobase, __u8 value)
|
||||
{
|
||||
__u8 bTmp;
|
||||
bTmp = ReadReg(iobase, 0x34);
|
||||
if (value)
|
||||
WriteReg(iobase, 0x34, bTmp | Clk_bit);
|
||||
else
|
||||
WriteReg(iobase, 0x34, bTmp & ~Clk_bit);
|
||||
}
|
||||
|
||||
static void ClkTx(__u16 iobase, __u8 Clk, __u8 Tx)
|
||||
{
|
||||
__u8 bTmp;
|
||||
|
||||
bTmp = ReadReg(iobase, 0x34);
|
||||
if (Clk == 0)
|
||||
bTmp &= ~Clk_bit;
|
||||
else {
|
||||
if (Clk == 1)
|
||||
bTmp |= Clk_bit;
|
||||
}
|
||||
WriteReg(iobase, 0x34, bTmp);
|
||||
Sdelay(1);
|
||||
if (Tx == 0)
|
||||
bTmp &= ~Tx_bit;
|
||||
else {
|
||||
if (Tx == 1)
|
||||
bTmp |= Tx_bit;
|
||||
}
|
||||
WriteReg(iobase, 0x34, bTmp);
|
||||
}
|
||||
|
||||
static void Wr_Byte(__u16 iobase, __u8 data)
|
||||
{
|
||||
__u8 bData = data;
|
||||
// __u8 btmp;
|
||||
int i;
|
||||
|
||||
ClkTx(iobase, 0, 1);
|
||||
|
||||
Tdelay(2);
|
||||
ActClk(iobase, 1);
|
||||
Tdelay(1);
|
||||
|
||||
for (i = 0; i < 8; i++) { //LDN
|
||||
|
||||
if ((bData >> i) & 0x01) {
|
||||
ClkTx(iobase, 0, 1); //bit data = 1;
|
||||
} else {
|
||||
ClkTx(iobase, 0, 0); //bit data = 1;
|
||||
}
|
||||
Tdelay(2);
|
||||
Sdelay(1);
|
||||
ActClk(iobase, 1); //clk hi
|
||||
Tdelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
static __u8 Rd_Indx(__u16 iobase, __u8 addr, __u8 index)
|
||||
{
|
||||
__u8 data = 0, bTmp, data_bit;
|
||||
int i;
|
||||
|
||||
bTmp = addr | (index << 1) | 0;
|
||||
ClkTx(iobase, 0, 0);
|
||||
Tdelay(2);
|
||||
ActClk(iobase, 1);
|
||||
udelay(1);
|
||||
Wr_Byte(iobase, bTmp);
|
||||
Sdelay(1);
|
||||
ClkTx(iobase, 0, 0);
|
||||
Tdelay(2);
|
||||
for (i = 0; i < 10; i++) {
|
||||
ActClk(iobase, 1);
|
||||
Tdelay(1);
|
||||
ActClk(iobase, 0);
|
||||
Tdelay(1);
|
||||
ClkTx(iobase, 0, 1);
|
||||
Tdelay(1);
|
||||
bTmp = ReadReg(iobase, 0x34);
|
||||
if (!(bTmp & Rd_Valid))
|
||||
break;
|
||||
}
|
||||
if (!(bTmp & Rd_Valid)) {
|
||||
for (i = 0; i < 8; i++) {
|
||||
ActClk(iobase, 1);
|
||||
Tdelay(1);
|
||||
ActClk(iobase, 0);
|
||||
bTmp = ReadReg(iobase, 0x34);
|
||||
data_bit = 1 << i;
|
||||
if (bTmp & RxBit)
|
||||
data |= data_bit;
|
||||
else
|
||||
data &= ~data_bit;
|
||||
Tdelay(2);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < 2; i++) {
|
||||
ActClk(iobase, 1);
|
||||
Tdelay(1);
|
||||
ActClk(iobase, 0);
|
||||
Tdelay(2);
|
||||
}
|
||||
bTmp = ReadReg(iobase, 0x34);
|
||||
}
|
||||
for (i = 0; i < 1; i++) {
|
||||
ActClk(iobase, 1);
|
||||
Tdelay(1);
|
||||
ActClk(iobase, 0);
|
||||
Tdelay(2);
|
||||
}
|
||||
ClkTx(iobase, 0, 0);
|
||||
Tdelay(1);
|
||||
for (i = 0; i < 3; i++) {
|
||||
ActClk(iobase, 1);
|
||||
Tdelay(1);
|
||||
ActClk(iobase, 0);
|
||||
Tdelay(2);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static void Wr_Indx(__u16 iobase, __u8 addr, __u8 index, __u8 data)
|
||||
{
|
||||
int i;
|
||||
__u8 bTmp;
|
||||
|
||||
ClkTx(iobase, 0, 0);
|
||||
udelay(2);
|
||||
ActClk(iobase, 1);
|
||||
udelay(1);
|
||||
bTmp = addr | (index << 1) | 1;
|
||||
Wr_Byte(iobase, bTmp);
|
||||
Wr_Byte(iobase, data);
|
||||
for (i = 0; i < 2; i++) {
|
||||
ClkTx(iobase, 0, 0);
|
||||
Tdelay(2);
|
||||
ActClk(iobase, 1);
|
||||
Tdelay(1);
|
||||
}
|
||||
ActClk(iobase, 0);
|
||||
}
|
||||
|
||||
static void ResetDongle(__u16 iobase)
|
||||
{
|
||||
int i;
|
||||
ClkTx(iobase, 0, 0);
|
||||
Tdelay(1);
|
||||
for (i = 0; i < 30; i++) {
|
||||
ActClk(iobase, 1);
|
||||
Tdelay(1);
|
||||
ActClk(iobase, 0);
|
||||
Tdelay(1);
|
||||
}
|
||||
ActClk(iobase, 0);
|
||||
}
|
||||
|
||||
static void SetSITmode(__u16 iobase)
|
||||
{
|
||||
|
||||
__u8 bTmp;
|
||||
|
||||
bTmp = ReadLPCReg(0x28);
|
||||
WriteLPCReg(0x28, bTmp | 0x10); //select ITMOFF
|
||||
bTmp = ReadReg(iobase, 0x35);
|
||||
WriteReg(iobase, 0x35, bTmp | 0x40); // Driver ITMOFF
|
||||
WriteReg(iobase, 0x28, bTmp | 0x80); // enable All interrupt
|
||||
}
|
||||
|
||||
static void SI_SetMode(__u16 iobase, int mode)
|
||||
{
|
||||
//__u32 dTmp;
|
||||
__u8 bTmp;
|
||||
|
||||
WriteLPCReg(0x28, 0x70); // S/W Reset
|
||||
SetSITmode(iobase);
|
||||
ResetDongle(iobase);
|
||||
udelay(10);
|
||||
Wr_Indx(iobase, 0x40, 0x0, 0x17); //RX ,APEN enable,Normal power
|
||||
Wr_Indx(iobase, 0x40, 0x1, mode); //Set Mode
|
||||
Wr_Indx(iobase, 0x40, 0x2, 0xff); //Set power to FIR VFIR > 1m
|
||||
bTmp = Rd_Indx(iobase, 0x40, 1);
|
||||
}
|
||||
|
||||
static void InitCard(__u16 iobase)
|
||||
{
|
||||
ResetChip(iobase, 5);
|
||||
WriteReg(iobase, I_ST_CT_0, 0x00); // open CHIP on
|
||||
SetSIRBOF(iobase, 0xc0); // hardware default value
|
||||
SetSIREOF(iobase, 0xc1);
|
||||
}
|
||||
|
||||
static void CommonInit(__u16 iobase)
|
||||
{
|
||||
// EnTXCRC(iobase,0);
|
||||
SwapDMA(iobase, OFF);
|
||||
SetMaxRxPacketSize(iobase, 0x0fff); //set to max:4095
|
||||
EnRXFIFOReadyInt(iobase, OFF);
|
||||
EnRXFIFOHalfLevelInt(iobase, OFF);
|
||||
EnTXFIFOHalfLevelInt(iobase, OFF);
|
||||
EnTXFIFOUnderrunEOMInt(iobase, ON);
|
||||
// EnTXFIFOReadyInt(iobase,ON);
|
||||
InvertTX(iobase, OFF);
|
||||
InvertRX(iobase, OFF);
|
||||
// WriteLPCReg(0xF0,0); //(if VT1211 then do this)
|
||||
if (IsSIROn(iobase)) {
|
||||
SIRFilter(iobase, ON);
|
||||
SIRRecvAny(iobase, ON);
|
||||
} else {
|
||||
SIRFilter(iobase, OFF);
|
||||
SIRRecvAny(iobase, OFF);
|
||||
}
|
||||
EnRXSpecInt(iobase, ON);
|
||||
WriteReg(iobase, I_ST_CT_0, 0x80);
|
||||
EnableDMA(iobase, ON);
|
||||
}
|
||||
|
||||
static void SetBaudRate(__u16 iobase, __u32 rate)
|
||||
{
|
||||
__u8 value = 11, temp;
|
||||
|
||||
if (IsSIROn(iobase)) {
|
||||
switch (rate) {
|
||||
case (__u32) (2400L):
|
||||
value = 47;
|
||||
break;
|
||||
case (__u32) (9600L):
|
||||
value = 11;
|
||||
break;
|
||||
case (__u32) (19200L):
|
||||
value = 5;
|
||||
break;
|
||||
case (__u32) (38400L):
|
||||
value = 2;
|
||||
break;
|
||||
case (__u32) (57600L):
|
||||
value = 1;
|
||||
break;
|
||||
case (__u32) (115200L):
|
||||
value = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (IsMIROn(iobase)) {
|
||||
value = 0; // will automatically be fixed in 1.152M
|
||||
} else if (IsFIROn(iobase)) {
|
||||
value = 0; // will automatically be fixed in 4M
|
||||
}
|
||||
temp = (ReadReg(iobase, I_CF_H_1) & 0x03);
|
||||
temp |= value << 2;
|
||||
WriteReg(iobase, I_CF_H_1, temp);
|
||||
}
|
||||
|
||||
static void SetPulseWidth(__u16 iobase, __u8 width)
|
||||
{
|
||||
__u8 temp, temp1, temp2;
|
||||
|
||||
temp = (ReadReg(iobase, I_CF_L_1) & 0x1f);
|
||||
temp1 = (ReadReg(iobase, I_CF_H_1) & 0xfc);
|
||||
temp2 = (width & 0x07) << 5;
|
||||
temp |= temp2;
|
||||
temp2 = (width & 0x18) >> 3;
|
||||
temp1 |= temp2;
|
||||
WriteReg(iobase, I_CF_L_1, temp);
|
||||
WriteReg(iobase, I_CF_H_1, temp1);
|
||||
}
|
||||
|
||||
static void SetSendPreambleCount(__u16 iobase, __u8 count)
|
||||
{
|
||||
__u8 temp;
|
||||
|
||||
temp = ReadReg(iobase, I_CF_L_1) & 0xe0;
|
||||
temp |= count;
|
||||
WriteReg(iobase, I_CF_L_1, temp);
|
||||
|
||||
}
|
||||
|
||||
static void SetVFIR(__u16 BaseAddr, __u8 val)
|
||||
{
|
||||
__u8 tmp;
|
||||
|
||||
tmp = ReadReg(BaseAddr, I_CF_L_0);
|
||||
WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f);
|
||||
WriteRegBit(BaseAddr, I_CF_H_0, 5, val);
|
||||
}
|
||||
|
||||
static void SetFIR(__u16 BaseAddr, __u8 val)
|
||||
{
|
||||
__u8 tmp;
|
||||
|
||||
WriteRegBit(BaseAddr, I_CF_H_0, 5, 0);
|
||||
tmp = ReadReg(BaseAddr, I_CF_L_0);
|
||||
WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f);
|
||||
WriteRegBit(BaseAddr, I_CF_L_0, 6, val);
|
||||
}
|
||||
|
||||
static void SetMIR(__u16 BaseAddr, __u8 val)
|
||||
{
|
||||
__u8 tmp;
|
||||
|
||||
WriteRegBit(BaseAddr, I_CF_H_0, 5, 0);
|
||||
tmp = ReadReg(BaseAddr, I_CF_L_0);
|
||||
WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f);
|
||||
WriteRegBit(BaseAddr, I_CF_L_0, 5, val);
|
||||
}
|
||||
|
||||
static void SetSIR(__u16 BaseAddr, __u8 val)
|
||||
{
|
||||
__u8 tmp;
|
||||
|
||||
WriteRegBit(BaseAddr, I_CF_H_0, 5, 0);
|
||||
tmp = ReadReg(BaseAddr, I_CF_L_0);
|
||||
WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f);
|
||||
WriteRegBit(BaseAddr, I_CF_L_0, 4, val);
|
||||
}
|
||||
|
||||
#endif /* via_IRCC_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,757 +0,0 @@
|
|||
|
||||
/*********************************************************************
|
||||
*
|
||||
* vlsi_ir.h: VLSI82C147 PCI IrDA controller driver for Linux
|
||||
*
|
||||
* Version: 0.5
|
||||
*
|
||||
* Copyright (c) 2001-2003 Martin Diehl
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRDA_VLSI_FIR_H
|
||||
#define IRDA_VLSI_FIR_H
|
||||
|
||||
/* ================================================================
|
||||
* compatibility stuff
|
||||
*/
|
||||
|
||||
/* definitions not present in pci_ids.h */
|
||||
|
||||
#ifndef PCI_CLASS_WIRELESS_IRDA
|
||||
#define PCI_CLASS_WIRELESS_IRDA 0x0d00
|
||||
#endif
|
||||
|
||||
#ifndef PCI_CLASS_SUBCLASS_MASK
|
||||
#define PCI_CLASS_SUBCLASS_MASK 0xffff
|
||||
#endif
|
||||
|
||||
/* ================================================================ */
|
||||
|
||||
/* non-standard PCI registers */
|
||||
|
||||
enum vlsi_pci_regs {
|
||||
VLSI_PCI_CLKCTL = 0x40, /* chip clock input control */
|
||||
VLSI_PCI_MSTRPAGE = 0x41, /* addr [31:24] for all busmaster cycles */
|
||||
VLSI_PCI_IRMISC = 0x42 /* mainly legacy UART related */
|
||||
};
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* VLSI_PCI_CLKCTL: Clock Control Register (u8, rw) */
|
||||
|
||||
/* Three possible clock sources: either on-chip 48MHz PLL or
|
||||
* external clock applied to EXTCLK pin. External clock may
|
||||
* be either 48MHz or 40MHz, which is indicated by XCKSEL.
|
||||
* CLKSTP controls whether the selected clock source gets
|
||||
* connected to the IrDA block.
|
||||
*
|
||||
* On my HP OB-800 the BIOS sets external 40MHz clock as source
|
||||
* when IrDA enabled and I've never detected any PLL lock success.
|
||||
* Apparently the 14.3...MHz OSC input required for the PLL to work
|
||||
* is not connected and the 40MHz EXTCLK is provided externally.
|
||||
* At least this is what makes the driver working for me.
|
||||
*/
|
||||
|
||||
enum vlsi_pci_clkctl {
|
||||
|
||||
/* PLL control */
|
||||
|
||||
CLKCTL_PD_INV = 0x04, /* PD#: inverted power down signal,
|
||||
* i.e. PLL is powered, if PD_INV set */
|
||||
CLKCTL_LOCK = 0x40, /* (ro) set, if PLL is locked */
|
||||
|
||||
/* clock source selection */
|
||||
|
||||
CLKCTL_EXTCLK = 0x20, /* set to select external clock input, not PLL */
|
||||
CLKCTL_XCKSEL = 0x10, /* set to indicate EXTCLK is 40MHz, not 48MHz */
|
||||
|
||||
/* IrDA block control */
|
||||
|
||||
CLKCTL_CLKSTP = 0x80, /* set to disconnect from selected clock source */
|
||||
CLKCTL_WAKE = 0x08 /* set to enable wakeup feature: whenever IR activity
|
||||
* is detected, PD_INV gets set(?) and CLKSTP cleared */
|
||||
};
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* VLSI_PCI_MSTRPAGE: Master Page Register (u8, rw) and busmastering stuff */
|
||||
|
||||
#define DMA_MASK_USED_BY_HW 0xffffffff
|
||||
#define DMA_MASK_MSTRPAGE 0x00ffffff
|
||||
#define MSTRPAGE_VALUE (DMA_MASK_MSTRPAGE >> 24)
|
||||
|
||||
/* PCI busmastering is somewhat special for this guy - in short:
|
||||
*
|
||||
* We select to operate using fixed MSTRPAGE=0, use ISA DMA
|
||||
* address restrictions to make the PCI BM api aware of this,
|
||||
* but ensure the hardware is dealing with real 32bit access.
|
||||
*
|
||||
* In detail:
|
||||
* The chip executes normal 32bit busmaster cycles, i.e.
|
||||
* drives all 32 address lines. These addresses however are
|
||||
* composed of [0:23] taken from various busaddr-pointers
|
||||
* and [24:31] taken from the MSTRPAGE register in the VLSI82C147
|
||||
* config space. Therefore _all_ busmastering must be
|
||||
* targeted to/from one single 16MB (busaddr-) superpage!
|
||||
* The point is to make sure all the allocations for memory
|
||||
* locations with busmaster access (ring descriptors, buffers)
|
||||
* are indeed bus-mappable to the same 16MB range (for x86 this
|
||||
* means they must reside in the same 16MB physical memory address
|
||||
* range). The only constraint we have which supports "several objects
|
||||
* mappable to common 16MB range" paradigma, is the old ISA DMA
|
||||
* restriction to the first 16MB of physical address range.
|
||||
* Hence the approach here is to enable PCI busmaster support using
|
||||
* the correct 32bit dma-mask used by the chip. Afterwards the device's
|
||||
* dma-mask gets restricted to 24bit, which must be honoured somehow by
|
||||
* all allocations for memory areas to be exposed to the chip ...
|
||||
*
|
||||
* Note:
|
||||
* Don't be surprised to get "Setting latency timer..." messages every
|
||||
* time when PCI busmastering is enabled for the chip.
|
||||
* The chip has its PCI latency timer RO fixed at 0 - which is not a
|
||||
* problem here, because it is never requesting _burst_ transactions.
|
||||
*/
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* VLSI_PCIIRMISC: IR Miscellaneous Register (u8, rw) */
|
||||
|
||||
/* legacy UART emulation - not used by this driver - would require:
|
||||
* (see below for some register-value definitions)
|
||||
*
|
||||
* - IRMISC_UARTEN must be set to enable UART address decoding
|
||||
* - IRMISC_UARTSEL configured
|
||||
* - IRCFG_MASTER must be cleared
|
||||
* - IRCFG_SIR must be set
|
||||
* - IRENABLE_PHYANDCLOCK must be asserted 0->1 (and hence IRENABLE_SIR_ON)
|
||||
*/
|
||||
|
||||
enum vlsi_pci_irmisc {
|
||||
|
||||
/* IR transceiver control */
|
||||
|
||||
IRMISC_IRRAIL = 0x40, /* (ro?) IR rail power indication (and control?)
|
||||
* 0=3.3V / 1=5V. Probably set during power-on?
|
||||
* unclear - not touched by driver */
|
||||
IRMISC_IRPD = 0x08, /* transceiver power down, if set */
|
||||
|
||||
/* legacy UART control */
|
||||
|
||||
IRMISC_UARTTST = 0x80, /* UART test mode - "always write 0" */
|
||||
IRMISC_UARTEN = 0x04, /* enable UART address decoding */
|
||||
|
||||
/* bits [1:0] IRMISC_UARTSEL to select legacy UART address */
|
||||
|
||||
IRMISC_UARTSEL_3f8 = 0x00,
|
||||
IRMISC_UARTSEL_2f8 = 0x01,
|
||||
IRMISC_UARTSEL_3e8 = 0x02,
|
||||
IRMISC_UARTSEL_2e8 = 0x03
|
||||
};
|
||||
|
||||
/* ================================================================ */
|
||||
|
||||
/* registers mapped to 32 byte PCI IO space */
|
||||
|
||||
/* note: better access all registers at the indicated u8/u16 size
|
||||
* although some of them contain only 1 byte of information.
|
||||
* some of them (particaluarly PROMPT and IRCFG) ignore
|
||||
* access when using the wrong addressing mode!
|
||||
*/
|
||||
|
||||
enum vlsi_pio_regs {
|
||||
VLSI_PIO_IRINTR = 0x00, /* interrupt enable/request (u8, rw) */
|
||||
VLSI_PIO_RINGPTR = 0x02, /* rx/tx ring pointer (u16, ro) */
|
||||
VLSI_PIO_RINGBASE = 0x04, /* [23:10] of ring address (u16, rw) */
|
||||
VLSI_PIO_RINGSIZE = 0x06, /* rx/tx ring size (u16, rw) */
|
||||
VLSI_PIO_PROMPT = 0x08, /* triggers ring processing (u16, wo) */
|
||||
/* 0x0a-0x0f: reserved / duplicated UART regs */
|
||||
VLSI_PIO_IRCFG = 0x10, /* configuration select (u16, rw) */
|
||||
VLSI_PIO_SIRFLAG = 0x12, /* BOF/EOF for filtered SIR (u16, ro) */
|
||||
VLSI_PIO_IRENABLE = 0x14, /* enable and status register (u16, rw/ro) */
|
||||
VLSI_PIO_PHYCTL = 0x16, /* physical layer current status (u16, ro) */
|
||||
VLSI_PIO_NPHYCTL = 0x18, /* next physical layer select (u16, rw) */
|
||||
VLSI_PIO_MAXPKT = 0x1a, /* [11:0] max len for packet receive (u16, rw) */
|
||||
VLSI_PIO_RCVBCNT = 0x1c /* current receive-FIFO byte count (u16, ro) */
|
||||
/* 0x1e-0x1f: reserved / duplicated UART regs */
|
||||
};
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* VLSI_PIO_IRINTR: Interrupt Register (u8, rw) */
|
||||
|
||||
/* enable-bits:
|
||||
* 1 = enable / 0 = disable
|
||||
* interrupt condition bits:
|
||||
* set according to corresponding interrupt source
|
||||
* (regardless of the state of the enable bits)
|
||||
* enable bit status indicates whether interrupt gets raised
|
||||
* write-to-clear
|
||||
* note: RPKTINT and TPKTINT behave different in legacy UART mode (which we don't use :-)
|
||||
*/
|
||||
|
||||
enum vlsi_pio_irintr {
|
||||
IRINTR_ACTEN = 0x80, /* activity interrupt enable */
|
||||
IRINTR_ACTIVITY = 0x40, /* activity monitor (traffic detected) */
|
||||
IRINTR_RPKTEN = 0x20, /* receive packet interrupt enable*/
|
||||
IRINTR_RPKTINT = 0x10, /* rx-packet transferred from fifo to memory finished */
|
||||
IRINTR_TPKTEN = 0x08, /* transmit packet interrupt enable */
|
||||
IRINTR_TPKTINT = 0x04, /* last bit of tx-packet+crc shifted to ir-pulser */
|
||||
IRINTR_OE_EN = 0x02, /* UART rx fifo overrun error interrupt enable */
|
||||
IRINTR_OE_INT = 0x01 /* UART rx fifo overrun error (read LSR to clear) */
|
||||
};
|
||||
|
||||
/* we use this mask to check whether the (shared PCI) interrupt is ours */
|
||||
|
||||
#define IRINTR_INT_MASK (IRINTR_ACTIVITY|IRINTR_RPKTINT|IRINTR_TPKTINT)
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* VLSI_PIO_RINGPTR: Ring Pointer Read-Back Register (u16, ro) */
|
||||
|
||||
/* _both_ ring pointers are indices relative to the _entire_ rx,tx-ring!
|
||||
* i.e. the referenced descriptor is located
|
||||
* at RINGBASE + PTR * sizeof(descr) for rx and tx
|
||||
* therefore, the tx-pointer has offset MAX_RING_DESCR
|
||||
*/
|
||||
|
||||
#define MAX_RING_DESCR 64 /* tx, rx rings may contain up to 64 descr each */
|
||||
|
||||
#define RINGPTR_RX_MASK (MAX_RING_DESCR-1)
|
||||
#define RINGPTR_TX_MASK ((MAX_RING_DESCR-1)<<8)
|
||||
|
||||
#define RINGPTR_GET_RX(p) ((p)&RINGPTR_RX_MASK)
|
||||
#define RINGPTR_GET_TX(p) (((p)&RINGPTR_TX_MASK)>>8)
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* VLSI_PIO_RINGBASE: Ring Pointer Base Address Register (u16, ro) */
|
||||
|
||||
/* Contains [23:10] part of the ring base (bus-) address
|
||||
* which must be 1k-alinged. [31:24] is taken from
|
||||
* VLSI_PCI_MSTRPAGE above.
|
||||
* The controller initiates non-burst PCI BM cycles to
|
||||
* fetch and update the descriptors in the ring.
|
||||
* Once fetched, the descriptor remains cached onchip
|
||||
* until it gets closed and updated due to the ring
|
||||
* processing state machine.
|
||||
* The entire ring area is split in rx and tx areas with each
|
||||
* area consisting of 64 descriptors of 8 bytes each.
|
||||
* The rx(tx) ring is located at ringbase+0 (ringbase+64*8).
|
||||
*/
|
||||
|
||||
#define BUS_TO_RINGBASE(p) (((p)>>10)&0x3fff)
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* VLSI_PIO_RINGSIZE: Ring Size Register (u16, rw) */
|
||||
|
||||
/* bit mask to indicate the ring size to be used for rx and tx.
|
||||
* possible values encoded bits
|
||||
* 4 0000
|
||||
* 8 0001
|
||||
* 16 0011
|
||||
* 32 0111
|
||||
* 64 1111
|
||||
* located at [15:12] for tx and [11:8] for rx ([7:0] unused)
|
||||
*
|
||||
* note: probably a good idea to have IRCFG_MSTR cleared when writing
|
||||
* this so the state machines are stopped and the RINGPTR is reset!
|
||||
*/
|
||||
|
||||
#define SIZE_TO_BITS(num) ((((num)-1)>>2)&0x0f)
|
||||
#define TX_RX_TO_RINGSIZE(tx,rx) ((SIZE_TO_BITS(tx)<<12)|(SIZE_TO_BITS(rx)<<8))
|
||||
#define RINGSIZE_TO_RXSIZE(rs) ((((rs)&0x0f00)>>6)+4)
|
||||
#define RINGSIZE_TO_TXSIZE(rs) ((((rs)&0xf000)>>10)+4)
|
||||
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* VLSI_PIO_PROMPT: Ring Prompting Register (u16, write-to-start) */
|
||||
|
||||
/* writing any value kicks the ring processing state machines
|
||||
* for both tx, rx rings as follows:
|
||||
* - active rings (currently owning an active descriptor)
|
||||
* ignore the prompt and continue
|
||||
* - idle rings fetch the next descr from the ring and start
|
||||
* their processing
|
||||
*/
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* VLSI_PIO_IRCFG: IR Config Register (u16, rw) */
|
||||
|
||||
/* notes:
|
||||
* - not more than one SIR/MIR/FIR bit must be set at any time
|
||||
* - SIR, MIR, FIR and CRC16 select the configuration which will
|
||||
* be applied on next 0->1 transition of IRENABLE_PHYANDCLOCK (see below).
|
||||
* - besides allowing the PCI interface to execute busmaster cycles
|
||||
* and therefore the ring SM to operate, the MSTR bit has side-effects:
|
||||
* when MSTR is cleared, the RINGPTR's get reset and the legacy UART mode
|
||||
* (in contrast to busmaster access mode) gets enabled.
|
||||
* - clearing ENRX or setting ENTX while data is received may stall the
|
||||
* receive fifo until ENRX reenabled _and_ another packet arrives
|
||||
* - SIRFILT means the chip performs the required unwrapping of hardware
|
||||
* headers (XBOF's, BOF/EOF) and un-escaping in the _receive_ direction.
|
||||
* Only the resulting IrLAP payload is copied to the receive buffers -
|
||||
* but with the 16bit FCS still encluded. Question remains, whether it
|
||||
* was already checked or we should do it before passing the packet to IrLAP?
|
||||
*/
|
||||
|
||||
enum vlsi_pio_ircfg {
|
||||
IRCFG_LOOP = 0x4000, /* enable loopback test mode */
|
||||
IRCFG_ENTX = 0x1000, /* transmit enable */
|
||||
IRCFG_ENRX = 0x0800, /* receive enable */
|
||||
IRCFG_MSTR = 0x0400, /* master enable */
|
||||
IRCFG_RXANY = 0x0200, /* receive any packet */
|
||||
IRCFG_CRC16 = 0x0080, /* 16bit (not 32bit) CRC select for MIR/FIR */
|
||||
IRCFG_FIR = 0x0040, /* FIR 4PPM encoding mode enable */
|
||||
IRCFG_MIR = 0x0020, /* MIR HDLC encoding mode enable */
|
||||
IRCFG_SIR = 0x0010, /* SIR encoding mode enable */
|
||||
IRCFG_SIRFILT = 0x0008, /* enable SIR decode filter (receiver unwrapping) */
|
||||
IRCFG_SIRTEST = 0x0004, /* allow SIR decode filter when not in SIR mode */
|
||||
IRCFG_TXPOL = 0x0002, /* invert tx polarity when set */
|
||||
IRCFG_RXPOL = 0x0001 /* invert rx polarity when set */
|
||||
};
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* VLSI_PIO_SIRFLAG: SIR Flag Register (u16, ro) */
|
||||
|
||||
/* register contains hardcoded BOF=0xc0 at [7:0] and EOF=0xc1 at [15:8]
|
||||
* which is used for unwrapping received frames in SIR decode-filter mode
|
||||
*/
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* VLSI_PIO_IRENABLE: IR Enable Register (u16, rw/ro) */
|
||||
|
||||
/* notes:
|
||||
* - IREN acts as gate for latching the configured IR mode information
|
||||
* from IRCFG and IRPHYCTL when IREN=reset and applying them when
|
||||
* IREN gets set afterwards.
|
||||
* - ENTXST reflects IRCFG_ENTX
|
||||
* - ENRXST = IRCFG_ENRX && (!IRCFG_ENTX || IRCFG_LOOP)
|
||||
*/
|
||||
|
||||
enum vlsi_pio_irenable {
|
||||
IRENABLE_PHYANDCLOCK = 0x8000, /* enable IR phy and gate the mode config (rw) */
|
||||
IRENABLE_CFGER = 0x4000, /* mode configuration error (ro) */
|
||||
IRENABLE_FIR_ON = 0x2000, /* FIR on status (ro) */
|
||||
IRENABLE_MIR_ON = 0x1000, /* MIR on status (ro) */
|
||||
IRENABLE_SIR_ON = 0x0800, /* SIR on status (ro) */
|
||||
IRENABLE_ENTXST = 0x0400, /* transmit enable status (ro) */
|
||||
IRENABLE_ENRXST = 0x0200, /* Receive enable status (ro) */
|
||||
IRENABLE_CRC16_ON = 0x0100 /* 16bit (not 32bit) CRC enabled status (ro) */
|
||||
};
|
||||
|
||||
#define IRENABLE_MASK 0xff00 /* Read mask */
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* VLSI_PIO_PHYCTL: IR Physical Layer Current Control Register (u16, ro) */
|
||||
|
||||
/* read-back of the currently applied physical layer status.
|
||||
* applied from VLSI_PIO_NPHYCTL at rising edge of IRENABLE_PHYANDCLOCK
|
||||
* contents identical to VLSI_PIO_NPHYCTL (see below)
|
||||
*/
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* VLSI_PIO_NPHYCTL: IR Physical Layer Next Control Register (u16, rw) */
|
||||
|
||||
/* latched during IRENABLE_PHYANDCLOCK=0 and applied at 0-1 transition
|
||||
*
|
||||
* consists of BAUD[15:10], PLSWID[9:5] and PREAMB[4:0] bits defined as follows:
|
||||
*
|
||||
* SIR-mode: BAUD = (115.2kHz / baudrate) - 1
|
||||
* PLSWID = (pulsetime * freq / (BAUD+1)) - 1
|
||||
* where pulsetime is the requested IrPHY pulse width
|
||||
* and freq is 8(16)MHz for 40(48)MHz primary input clock
|
||||
* PREAMB: don't care for SIR
|
||||
*
|
||||
* The nominal SIR pulse width is 3/16 bit time so we have PLSWID=12
|
||||
* fixed for all SIR speeds at 40MHz input clock (PLSWID=24 at 48MHz).
|
||||
* IrPHY also allows shorter pulses down to the nominal pulse duration
|
||||
* at 115.2kbaud (minus some tolerance) which is 1.41 usec.
|
||||
* Using the expression PLSWID = 12/(BAUD+1)-1 (multiplied by two for 48MHz)
|
||||
* we get the minimum acceptable PLSWID values according to the VLSI
|
||||
* specification, which provides 1.5 usec pulse width for all speeds (except
|
||||
* for 2.4kbaud getting 6usec). This is fine with IrPHY v1.3 specs and
|
||||
* reduces the transceiver power which drains the battery. At 9.6kbaud for
|
||||
* example this amounts to more than 90% battery power saving!
|
||||
*
|
||||
* MIR-mode: BAUD = 0
|
||||
* PLSWID = 9(10) for 40(48) MHz input clock
|
||||
* to get nominal MIR pulse width
|
||||
* PREAMB = 1
|
||||
*
|
||||
* FIR-mode: BAUD = 0
|
||||
* PLSWID: don't care
|
||||
* PREAMB = 15
|
||||
*/
|
||||
|
||||
#define PHYCTL_BAUD_SHIFT 10
|
||||
#define PHYCTL_BAUD_MASK 0xfc00
|
||||
#define PHYCTL_PLSWID_SHIFT 5
|
||||
#define PHYCTL_PLSWID_MASK 0x03e0
|
||||
#define PHYCTL_PREAMB_SHIFT 0
|
||||
#define PHYCTL_PREAMB_MASK 0x001f
|
||||
|
||||
#define PHYCTL_TO_BAUD(bwp) (((bwp)&PHYCTL_BAUD_MASK)>>PHYCTL_BAUD_SHIFT)
|
||||
#define PHYCTL_TO_PLSWID(bwp) (((bwp)&PHYCTL_PLSWID_MASK)>>PHYCTL_PLSWID_SHIFT)
|
||||
#define PHYCTL_TO_PREAMB(bwp) (((bwp)&PHYCTL_PREAMB_MASK)>>PHYCTL_PREAMB_SHIFT)
|
||||
|
||||
#define BWP_TO_PHYCTL(b,w,p) ((((b)<<PHYCTL_BAUD_SHIFT)&PHYCTL_BAUD_MASK) \
|
||||
| (((w)<<PHYCTL_PLSWID_SHIFT)&PHYCTL_PLSWID_MASK) \
|
||||
| (((p)<<PHYCTL_PREAMB_SHIFT)&PHYCTL_PREAMB_MASK))
|
||||
|
||||
#define BAUD_BITS(br) ((115200/(br))-1)
|
||||
|
||||
static inline unsigned
|
||||
calc_width_bits(unsigned baudrate, unsigned widthselect, unsigned clockselect)
|
||||
{
|
||||
unsigned tmp;
|
||||
|
||||
if (widthselect) /* nominal 3/16 puls width */
|
||||
return (clockselect) ? 12 : 24;
|
||||
|
||||
tmp = ((clockselect) ? 12 : 24) / (BAUD_BITS(baudrate)+1);
|
||||
|
||||
/* intermediate result of integer division needed here */
|
||||
|
||||
return (tmp>0) ? (tmp-1) : 0;
|
||||
}
|
||||
|
||||
#define PHYCTL_SIR(br,ws,cs) BWP_TO_PHYCTL(BAUD_BITS(br),calc_width_bits((br),(ws),(cs)),0)
|
||||
#define PHYCTL_MIR(cs) BWP_TO_PHYCTL(0,((cs)?9:10),1)
|
||||
#define PHYCTL_FIR BWP_TO_PHYCTL(0,0,15)
|
||||
|
||||
/* quite ugly, I know. But implementing these calculations here avoids
|
||||
* having magic numbers in the code and allows some playing with pulsewidths
|
||||
* without risk to violate the standards.
|
||||
* FWIW, here is the table for reference:
|
||||
*
|
||||
* baudrate BAUD min-PLSWID nom-PLSWID PREAMB
|
||||
* 2400 47 0(0) 12(24) 0
|
||||
* 9600 11 0(0) 12(24) 0
|
||||
* 19200 5 1(2) 12(24) 0
|
||||
* 38400 2 3(6) 12(24) 0
|
||||
* 57600 1 5(10) 12(24) 0
|
||||
* 115200 0 11(22) 12(24) 0
|
||||
* MIR 0 - 9(10) 1
|
||||
* FIR 0 - 0 15
|
||||
*
|
||||
* note: x(y) means x-value for 40MHz / y-value for 48MHz primary input clock
|
||||
*/
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
|
||||
/* VLSI_PIO_MAXPKT: Maximum Packet Length register (u16, rw) */
|
||||
|
||||
/* maximum acceptable length for received packets */
|
||||
|
||||
/* hw imposed limitation - register uses only [11:0] */
|
||||
#define MAX_PACKET_LENGTH 0x0fff
|
||||
|
||||
/* IrLAP I-field (apparently not defined elsewhere) */
|
||||
#define IRDA_MTU 2048
|
||||
|
||||
/* complete packet consists of A(1)+C(1)+I(<=IRDA_MTU) */
|
||||
#define IRLAP_SKB_ALLOCSIZE (1+1+IRDA_MTU)
|
||||
|
||||
/* the buffers we use to exchange frames with the hardware need to be
|
||||
* larger than IRLAP_SKB_ALLOCSIZE because we may have up to 4 bytes FCS
|
||||
* appended and, in SIR mode, a lot of frame wrapping bytes. The worst
|
||||
* case appears to be a SIR packet with I-size==IRDA_MTU and all bytes
|
||||
* requiring to be escaped to provide transparency. Furthermore, the peer
|
||||
* might ask for quite a number of additional XBOFs:
|
||||
* up to 115+48 XBOFS 163
|
||||
* regular BOF 1
|
||||
* A-field 1
|
||||
* C-field 1
|
||||
* I-field, IRDA_MTU, all escaped 4096
|
||||
* FCS (16 bit at SIR, escaped) 4
|
||||
* EOF 1
|
||||
* AFAICS nothing in IrLAP guarantees A/C field not to need escaping
|
||||
* (f.e. 0xc0/0xc1 - i.e. BOF/EOF - are legal values there) so in the
|
||||
* worst case we have 4269 bytes total frame size.
|
||||
* However, the VLSI uses 12 bits only for all buffer length values,
|
||||
* which limits the maximum useable buffer size <= 4095.
|
||||
* Note this is not a limitation in the receive case because we use
|
||||
* the SIR filtering mode where the hw unwraps the frame and only the
|
||||
* bare packet+fcs is stored into the buffer - in contrast to the SIR
|
||||
* tx case where we have to pass frame-wrapped packets to the hw.
|
||||
* If this would ever become an issue in real life, the only workaround
|
||||
* I see would be using the legacy UART emulation in SIR mode.
|
||||
*/
|
||||
|
||||
#define XFER_BUF_SIZE MAX_PACKET_LENGTH
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* VLSI_PIO_RCVBCNT: Receive Byte Count Register (u16, ro) */
|
||||
|
||||
/* receive packet counter gets incremented on every non-filtered
|
||||
* byte which was put in the receive fifo and reset for each
|
||||
* new packet. Used to decide whether we are just in the middle
|
||||
* of receiving
|
||||
*/
|
||||
|
||||
/* better apply the [11:0] mask when reading, as some docs say the
|
||||
* reserved [15:12] would return 1 when reading - which is wrong AFAICS
|
||||
*/
|
||||
#define RCVBCNT_MASK 0x0fff
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/* descriptors for rx/tx ring
|
||||
*
|
||||
* accessed by hardware - don't change!
|
||||
*
|
||||
* the descriptor is owned by hardware, when the ACTIVE status bit
|
||||
* is set and nothing (besides reading status to test the bit)
|
||||
* shall be done. The bit gets cleared by hw, when the descriptor
|
||||
* gets closed. Premature reaping of descriptors owned be the chip
|
||||
* can be achieved by disabling IRCFG_MSTR
|
||||
*
|
||||
* Attention: Writing addr overwrites status!
|
||||
*
|
||||
* ### FIXME: depends on endianess (but there ain't no non-i586 ob800 ;-)
|
||||
*/
|
||||
|
||||
struct ring_descr_hw {
|
||||
volatile __le16 rd_count; /* tx/rx count [11:0] */
|
||||
__le16 reserved;
|
||||
union {
|
||||
__le32 addr; /* [23:0] of the buffer's busaddress */
|
||||
struct {
|
||||
u8 addr_res[3];
|
||||
volatile u8 status; /* descriptor status */
|
||||
} __packed rd_s;
|
||||
} __packed rd_u;
|
||||
} __packed;
|
||||
|
||||
#define rd_addr rd_u.addr
|
||||
#define rd_status rd_u.rd_s.status
|
||||
|
||||
/* ring descriptor status bits */
|
||||
|
||||
#define RD_ACTIVE 0x80 /* descriptor owned by hw (both TX,RX) */
|
||||
|
||||
/* TX ring descriptor status */
|
||||
|
||||
#define RD_TX_DISCRC 0x40 /* do not send CRC (for SIR) */
|
||||
#define RD_TX_BADCRC 0x20 /* force a bad CRC */
|
||||
#define RD_TX_PULSE 0x10 /* send indication pulse after this frame (MIR/FIR) */
|
||||
#define RD_TX_FRCEUND 0x08 /* force underrun */
|
||||
#define RD_TX_CLRENTX 0x04 /* clear ENTX after this frame */
|
||||
#define RD_TX_UNDRN 0x01 /* TX fifo underrun (probably PCI problem) */
|
||||
|
||||
/* RX ring descriptor status */
|
||||
|
||||
#define RD_RX_PHYERR 0x40 /* physical encoding error */
|
||||
#define RD_RX_CRCERR 0x20 /* CRC error (MIR/FIR) */
|
||||
#define RD_RX_LENGTH 0x10 /* frame exceeds buffer length */
|
||||
#define RD_RX_OVER 0x08 /* RX fifo overrun (probably PCI problem) */
|
||||
#define RD_RX_SIRBAD 0x04 /* EOF missing: BOF follows BOF (SIR, filtered) */
|
||||
|
||||
#define RD_RX_ERROR 0x7c /* any error in received frame */
|
||||
|
||||
/* the memory required to hold the 2 descriptor rings */
|
||||
#define HW_RING_AREA_SIZE (2 * MAX_RING_DESCR * sizeof(struct ring_descr_hw))
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/* sw-ring descriptors consists of a bus-mapped transfer buffer with
|
||||
* associated skb and a pointer to the hw entry descriptor
|
||||
*/
|
||||
|
||||
struct ring_descr {
|
||||
struct ring_descr_hw *hw;
|
||||
struct sk_buff *skb;
|
||||
void *buf;
|
||||
};
|
||||
|
||||
/* wrappers for operations on hw-exposed ring descriptors
|
||||
* access to the hw-part of the descriptors must use these.
|
||||
*/
|
||||
|
||||
static inline int rd_is_active(struct ring_descr *rd)
|
||||
{
|
||||
return (rd->hw->rd_status & RD_ACTIVE) != 0;
|
||||
}
|
||||
|
||||
static inline void rd_activate(struct ring_descr *rd)
|
||||
{
|
||||
rd->hw->rd_status |= RD_ACTIVE;
|
||||
}
|
||||
|
||||
static inline void rd_set_status(struct ring_descr *rd, u8 s)
|
||||
{
|
||||
rd->hw->rd_status = s; /* may pass ownership to the hardware */
|
||||
}
|
||||
|
||||
static inline void rd_set_addr_status(struct ring_descr *rd, dma_addr_t a, u8 s)
|
||||
{
|
||||
/* order is important for two reasons:
|
||||
* - overlayed: writing addr overwrites status
|
||||
* - we want to write status last so we have valid address in
|
||||
* case status has RD_ACTIVE set
|
||||
*/
|
||||
|
||||
if ((a & ~DMA_MASK_MSTRPAGE)>>24 != MSTRPAGE_VALUE) {
|
||||
net_err_ratelimited("%s: pci busaddr inconsistency!\n",
|
||||
__func__);
|
||||
dump_stack();
|
||||
return;
|
||||
}
|
||||
|
||||
a &= DMA_MASK_MSTRPAGE; /* clear highbyte to make sure we won't write
|
||||
* to status - just in case MSTRPAGE_VALUE!=0
|
||||
*/
|
||||
rd->hw->rd_addr = cpu_to_le32(a);
|
||||
wmb();
|
||||
rd_set_status(rd, s); /* may pass ownership to the hardware */
|
||||
}
|
||||
|
||||
static inline void rd_set_count(struct ring_descr *rd, u16 c)
|
||||
{
|
||||
rd->hw->rd_count = cpu_to_le16(c);
|
||||
}
|
||||
|
||||
static inline u8 rd_get_status(struct ring_descr *rd)
|
||||
{
|
||||
return rd->hw->rd_status;
|
||||
}
|
||||
|
||||
static inline dma_addr_t rd_get_addr(struct ring_descr *rd)
|
||||
{
|
||||
dma_addr_t a;
|
||||
|
||||
a = le32_to_cpu(rd->hw->rd_addr);
|
||||
return (a & DMA_MASK_MSTRPAGE) | (MSTRPAGE_VALUE << 24);
|
||||
}
|
||||
|
||||
static inline u16 rd_get_count(struct ring_descr *rd)
|
||||
{
|
||||
return le16_to_cpu(rd->hw->rd_count);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/* sw descriptor rings for rx, tx:
|
||||
*
|
||||
* operations follow producer-consumer paradigm, with the hw
|
||||
* in the middle doing the processing.
|
||||
* ring size must be power of two.
|
||||
*
|
||||
* producer advances r->tail after inserting for processing
|
||||
* consumer advances r->head after removing processed rd
|
||||
* ring is empty if head==tail / full if (tail+1)==head
|
||||
*/
|
||||
|
||||
struct vlsi_ring {
|
||||
struct pci_dev *pdev;
|
||||
int dir;
|
||||
unsigned len;
|
||||
unsigned size;
|
||||
unsigned mask;
|
||||
atomic_t head, tail;
|
||||
struct ring_descr *rd;
|
||||
};
|
||||
|
||||
/* ring processing helpers */
|
||||
|
||||
static inline struct ring_descr *ring_last(struct vlsi_ring *r)
|
||||
{
|
||||
int t;
|
||||
|
||||
t = atomic_read(&r->tail) & r->mask;
|
||||
return (((t+1) & r->mask) == (atomic_read(&r->head) & r->mask)) ? NULL : &r->rd[t];
|
||||
}
|
||||
|
||||
static inline struct ring_descr *ring_put(struct vlsi_ring *r)
|
||||
{
|
||||
atomic_inc(&r->tail);
|
||||
return ring_last(r);
|
||||
}
|
||||
|
||||
static inline struct ring_descr *ring_first(struct vlsi_ring *r)
|
||||
{
|
||||
int h;
|
||||
|
||||
h = atomic_read(&r->head) & r->mask;
|
||||
return (h == (atomic_read(&r->tail) & r->mask)) ? NULL : &r->rd[h];
|
||||
}
|
||||
|
||||
static inline struct ring_descr *ring_get(struct vlsi_ring *r)
|
||||
{
|
||||
atomic_inc(&r->head);
|
||||
return ring_first(r);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/* our private compound VLSI-PCI-IRDA device information */
|
||||
|
||||
typedef struct vlsi_irda_dev {
|
||||
struct pci_dev *pdev;
|
||||
|
||||
struct irlap_cb *irlap;
|
||||
|
||||
struct qos_info qos;
|
||||
|
||||
unsigned mode;
|
||||
int baud, new_baud;
|
||||
|
||||
dma_addr_t busaddr;
|
||||
void *virtaddr;
|
||||
struct vlsi_ring *tx_ring, *rx_ring;
|
||||
|
||||
ktime_t last_rx;
|
||||
|
||||
spinlock_t lock;
|
||||
struct mutex mtx;
|
||||
|
||||
u8 resume_ok;
|
||||
struct proc_dir_entry *proc_entry;
|
||||
|
||||
} vlsi_irda_dev_t;
|
||||
|
||||
/********************************************************/
|
||||
|
||||
/* the remapped error flags we use for returning from frame
|
||||
* post-processing in vlsi_process_tx/rx() after it was completed
|
||||
* by the hardware. These functions either return the >=0 number
|
||||
* of transferred bytes in case of success or the negative (-)
|
||||
* of the or'ed error flags.
|
||||
*/
|
||||
|
||||
#define VLSI_TX_DROP 0x0001
|
||||
#define VLSI_TX_FIFO 0x0002
|
||||
|
||||
#define VLSI_RX_DROP 0x0100
|
||||
#define VLSI_RX_OVER 0x0200
|
||||
#define VLSI_RX_LENGTH 0x0400
|
||||
#define VLSI_RX_FRAME 0x0800
|
||||
#define VLSI_RX_CRC 0x1000
|
||||
|
||||
/********************************************************/
|
||||
|
||||
#endif /* IRDA_VLSI_FIR_H */
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
#ifndef W83977AF_H
|
||||
#define W83977AF_H
|
||||
|
||||
#define W977_EFIO_BASE 0x370
|
||||
#define W977_EFIO2_BASE 0x3f0
|
||||
#define W977_DEVICE_IR 0x06
|
||||
|
||||
|
||||
/*
|
||||
* Enter extended function mode
|
||||
*/
|
||||
static inline void w977_efm_enter(unsigned int efio)
|
||||
{
|
||||
outb(0x87, efio);
|
||||
outb(0x87, efio);
|
||||
}
|
||||
|
||||
/*
|
||||
* Select a device to configure
|
||||
*/
|
||||
|
||||
static inline void w977_select_device(__u8 devnum, unsigned int efio)
|
||||
{
|
||||
outb(0x07, efio);
|
||||
outb(devnum, efio+1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a byte to a register
|
||||
*/
|
||||
static inline void w977_write_reg(__u8 reg, __u8 value, unsigned int efio)
|
||||
{
|
||||
outb(reg, efio);
|
||||
outb(value, efio+1);
|
||||
}
|
||||
|
||||
/*
|
||||
* read a byte from a register
|
||||
*/
|
||||
static inline __u8 w977_read_reg(__u8 reg, unsigned int efio)
|
||||
{
|
||||
outb(reg, efio);
|
||||
return inb(efio+1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exit extended function mode
|
||||
*/
|
||||
static inline void w977_efm_exit(unsigned int efio)
|
||||
{
|
||||
outb(0xAA, efio);
|
||||
}
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -1,198 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: w83977af_ir.h
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Paul VanderSpek
|
||||
* Created at: Thu Nov 19 13:55:34 1998
|
||||
* Modified at: Tue Jan 11 13:08:19 2000
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef W83977AF_IR_H
|
||||
#define W83977AF_IR_H
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Flags for configuration register CRF0 */
|
||||
#define ENBNKSEL 0x01
|
||||
#define APEDCRC 0x02
|
||||
#define TXW4C 0x04
|
||||
#define RXW4C 0x08
|
||||
|
||||
/* Bank 0 */
|
||||
#define RBR 0x00 /* Receiver buffer register */
|
||||
#define TBR 0x00 /* Transmitter buffer register */
|
||||
|
||||
#define ICR 0x01 /* Interrupt configuration register */
|
||||
#define ICR_ERBRI 0x01 /* Receiver buffer register interrupt */
|
||||
#define ICR_ETBREI 0x02 /* Transeiver empty interrupt */
|
||||
#define ICR_EUSRI 0x04//* IR status interrupt */
|
||||
#define ICR_EHSRI 0x04
|
||||
#define ICR_ETXURI 0x04 /* Tx underrun */
|
||||
#define ICR_EDMAI 0x10 /* DMA interrupt */
|
||||
#define ICR_ETXTHI 0x20 /* Transmitter threshold interrupt */
|
||||
#define ICR_EFSFI 0x40 /* Frame status FIFO interrupt */
|
||||
#define ICR_ETMRI 0x80 /* Timer interrupt */
|
||||
|
||||
#define UFR 0x02 /* FIFO control register */
|
||||
#define UFR_EN_FIFO 0x01 /* Enable FIFO's */
|
||||
#define UFR_RXF_RST 0x02 /* Reset Rx FIFO */
|
||||
#define UFR_TXF_RST 0x04 /* Reset Tx FIFO */
|
||||
#define UFR_RXTL 0x80 /* Rx FIFO threshold (set to 16) */
|
||||
#define UFR_TXTL 0x20 /* Tx FIFO threshold (set to 17) */
|
||||
|
||||
#define ISR 0x02 /* Interrupt status register */
|
||||
#define ISR_RXTH_I 0x01 /* Receive threshold interrupt */
|
||||
#define ISR_TXEMP_I 0x02 /* Transmitter empty interrupt */
|
||||
#define ISR_FEND_I 0x04
|
||||
#define ISR_DMA_I 0x10
|
||||
#define ISR_TXTH_I 0x20 /* Transmitter threshold interrupt */
|
||||
#define ISR_FSF_I 0x40
|
||||
#define ISR_TMR_I 0x80 /* Timer interrupt */
|
||||
|
||||
#define UCR 0x03 /* Uart control register */
|
||||
#define UCR_DLS8 0x03 /* 8N1 */
|
||||
|
||||
#define SSR 0x03 /* Sets select register */
|
||||
#define SET0 UCR_DLS8 /* Make sure we keep 8N1 */
|
||||
#define SET1 (0x80|UCR_DLS8) /* Make sure we keep 8N1 */
|
||||
#define SET2 0xE0
|
||||
#define SET3 0xE4
|
||||
#define SET4 0xE8
|
||||
#define SET5 0xEC
|
||||
#define SET6 0xF0
|
||||
#define SET7 0xF4
|
||||
|
||||
#define HCR 0x04
|
||||
#define HCR_MODE_MASK ~(0xD0)
|
||||
#define HCR_SIR 0x60
|
||||
#define HCR_MIR_576 0x20
|
||||
#define HCR_MIR_1152 0x80
|
||||
#define HCR_FIR 0xA0
|
||||
#define HCR_EN_DMA 0x04
|
||||
#define HCR_EN_IRQ 0x08
|
||||
#define HCR_TX_WT 0x08
|
||||
|
||||
#define USR 0x05 /* IR status register */
|
||||
#define USR_RDR 0x01 /* Receive data ready */
|
||||
#define USR_TSRE 0x40 /* Transmitter empty? */
|
||||
|
||||
#define AUDR 0x07
|
||||
#define AUDR_SFEND 0x08 /* Set a frame end */
|
||||
#define AUDR_RXBSY 0x20 /* Rx busy */
|
||||
#define AUDR_UNDR 0x40 /* Transeiver underrun */
|
||||
|
||||
/* Set 2 */
|
||||
#define ABLL 0x00 /* Advanced baud rate divisor latch (low byte) */
|
||||
#define ABHL 0x01 /* Advanced baud rate divisor latch (high byte) */
|
||||
|
||||
#define ADCR1 0x02
|
||||
#define ADCR1_ADV_SL 0x01
|
||||
#define ADCR1_D_CHSW 0x08 /* the specs are wrong. its bit 3, not 4 */
|
||||
#define ADCR1_DMA_F 0x02
|
||||
|
||||
#define ADCR2 0x04
|
||||
#define ADCR2_TXFS32 0x01
|
||||
#define ADCR2_RXFS32 0x04
|
||||
|
||||
#define RXFDTH 0x07
|
||||
|
||||
/* Set 3 */
|
||||
#define AUID 0x00
|
||||
|
||||
/* Set 4 */
|
||||
#define TMRL 0x00 /* Timer value register (low byte) */
|
||||
#define TMRH 0x01 /* Timer value register (high byte) */
|
||||
|
||||
#define IR_MSL 0x02 /* Infrared mode select */
|
||||
#define IR_MSL_EN_TMR 0x01 /* Enable timer */
|
||||
|
||||
#define TFRLL 0x04 /* Transmitter frame length (low byte) */
|
||||
#define TFRLH 0x05 /* Transmitter frame length (high byte) */
|
||||
#define RFRLL 0x06 /* Receiver frame length (low byte) */
|
||||
#define RFRLH 0x07 /* Receiver frame length (high byte) */
|
||||
|
||||
/* Set 5 */
|
||||
|
||||
#define FS_FO 0x05 /* Frame status FIFO */
|
||||
#define FS_FO_FSFDR 0x80 /* Frame status FIFO data ready */
|
||||
#define FS_FO_LST_FR 0x40 /* Frame lost */
|
||||
#define FS_FO_MX_LEX 0x10 /* Max frame len exceeded */
|
||||
#define FS_FO_PHY_ERR 0x08 /* Physical layer error */
|
||||
#define FS_FO_CRC_ERR 0x04
|
||||
#define FS_FO_RX_OV 0x02 /* Receive overrun */
|
||||
#define FS_FO_FSF_OV 0x01 /* Frame status FIFO overrun */
|
||||
#define FS_FO_ERR_MSK 0x5f /* Error mask */
|
||||
|
||||
#define RFLFL 0x06
|
||||
#define RFLFH 0x07
|
||||
|
||||
/* Set 6 */
|
||||
#define IR_CFG2 0x00
|
||||
#define IR_CFG2_DIS_CRC 0x02
|
||||
|
||||
/* Set 7 */
|
||||
#define IRM_CR 0x07 /* Infrared module control register */
|
||||
#define IRM_CR_IRX_MSL 0x40
|
||||
#define IRM_CR_AF_MNT 0x80 /* Automatic format */
|
||||
|
||||
/* For storing entries in the status FIFO */
|
||||
struct st_fifo_entry {
|
||||
int status;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct st_fifo {
|
||||
struct st_fifo_entry entries[10];
|
||||
int head;
|
||||
int tail;
|
||||
int len;
|
||||
};
|
||||
|
||||
/* Private data for each instance */
|
||||
struct w83977af_ir {
|
||||
struct st_fifo st_fifo;
|
||||
|
||||
int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */
|
||||
int tx_len; /* Number of frames in tx_buff */
|
||||
|
||||
struct net_device *netdev; /* Yes! we are some kind of netdevice */
|
||||
|
||||
struct irlap_cb *irlap; /* The link layer we are binded to */
|
||||
struct qos_info qos; /* QoS capabilities for this device */
|
||||
|
||||
chipio_t io; /* IrDA controller information */
|
||||
iobuff_t tx_buff; /* Transmit buffer */
|
||||
iobuff_t rx_buff; /* Receive buffer */
|
||||
dma_addr_t tx_buff_dma;
|
||||
dma_addr_t rx_buff_dma;
|
||||
|
||||
/* Note : currently locking is *very* incomplete, but this
|
||||
* will get you started. Check in nsc-ircc.c for a proper
|
||||
* locking strategy. - Jean II */
|
||||
spinlock_t lock; /* For serializing operations */
|
||||
|
||||
__u32 new_speed;
|
||||
};
|
||||
|
||||
static inline void switch_bank( int iobase, int set)
|
||||
{
|
||||
outb(set, iobase+SSR);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,87 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: af_irda.h
|
||||
* Version: 1.0
|
||||
* Description: IrDA sockets declarations
|
||||
* Status: Stable
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Tue Dec 9 21:13:12 1997
|
||||
* Modified at: Fri Jan 28 13:16:32 2000
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved.
|
||||
* Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef AF_IRDA_H
|
||||
#define AF_IRDA_H
|
||||
|
||||
#include <linux/irda.h>
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/iriap.h> /* struct iriap_cb */
|
||||
#include <net/irda/irias_object.h> /* struct ias_value */
|
||||
#include <net/irda/irlmp.h> /* struct lsap_cb */
|
||||
#include <net/irda/irttp.h> /* struct tsap_cb */
|
||||
#include <net/irda/discovery.h> /* struct discovery_t */
|
||||
#include <net/sock.h>
|
||||
|
||||
/* IrDA Socket */
|
||||
struct irda_sock {
|
||||
/* struct sock has to be the first member of irda_sock */
|
||||
struct sock sk;
|
||||
__u32 saddr; /* my local address */
|
||||
__u32 daddr; /* peer address */
|
||||
|
||||
struct lsap_cb *lsap; /* LSAP used by Ultra */
|
||||
__u8 pid; /* Protocol IP (PID) used by Ultra */
|
||||
|
||||
struct tsap_cb *tsap; /* TSAP used by this connection */
|
||||
__u8 dtsap_sel; /* remote TSAP address */
|
||||
__u8 stsap_sel; /* local TSAP address */
|
||||
|
||||
__u32 max_sdu_size_rx;
|
||||
__u32 max_sdu_size_tx;
|
||||
__u32 max_data_size;
|
||||
__u8 max_header_size;
|
||||
struct qos_info qos_tx;
|
||||
|
||||
__u16_host_order mask; /* Hint bits mask */
|
||||
__u16_host_order hints; /* Hint bits */
|
||||
|
||||
void *ckey; /* IrLMP client handle */
|
||||
void *skey; /* IrLMP service handle */
|
||||
|
||||
struct ias_object *ias_obj; /* Our service name + lsap in IAS */
|
||||
struct iriap_cb *iriap; /* Used to query remote IAS */
|
||||
struct ias_value *ias_result; /* Result of remote IAS query */
|
||||
|
||||
hashbin_t *cachelog; /* Result of discovery query */
|
||||
__u32 cachedaddr; /* Result of selective discovery query */
|
||||
|
||||
int nslots; /* Number of slots to use for discovery */
|
||||
|
||||
int errno; /* status of the IAS query */
|
||||
|
||||
wait_queue_head_t query_wait; /* Wait for the answer to a query */
|
||||
struct timer_list watchdog; /* Timeout for discovery */
|
||||
|
||||
LOCAL_FLOW tx_flow;
|
||||
LOCAL_FLOW rx_flow;
|
||||
};
|
||||
|
||||
static inline struct irda_sock *irda_sk(struct sock *sk)
|
||||
{
|
||||
return (struct irda_sock *)sk;
|
||||
}
|
||||
|
||||
#endif /* AF_IRDA_H */
|
|
@ -1,29 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: crc.h
|
||||
* Version:
|
||||
* Description: CRC routines
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Mon Aug 4 20:40:53 1997
|
||||
* Modified at: Sun May 2 20:25:23 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRDA_CRC_H
|
||||
#define IRDA_CRC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/crc-ccitt.h>
|
||||
|
||||
#define INIT_FCS 0xffff /* Initial FCS value */
|
||||
#define GOOD_FCS 0xf0b8 /* Good final FCS value */
|
||||
|
||||
/* Recompute the FCS with one more character appended. */
|
||||
#define irda_fcs(fcs, c) crc_ccitt_byte(fcs, c)
|
||||
|
||||
/* Recompute the FCS with len bytes appended. */
|
||||
#define irda_calc_crc16(fcs, buf, len) crc_ccitt(fcs, buf, len)
|
||||
|
||||
#endif
|
|
@ -1,95 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: discovery.h
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Tue Apr 6 16:53:53 1999
|
||||
* Modified at: Tue Oct 5 10:05:10 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
* Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef DISCOVERY_H
|
||||
#define DISCOVERY_H
|
||||
|
||||
#include <asm/param.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irqueue.h> /* irda_queue_t */
|
||||
#include <net/irda/irlap_event.h> /* LAP_REASON */
|
||||
|
||||
#define DISCOVERY_EXPIRE_TIMEOUT (2*sysctl_discovery_timeout*HZ)
|
||||
#define DISCOVERY_DEFAULT_SLOTS 0
|
||||
|
||||
/*
|
||||
* This type is used by the protocols that transmit 16 bits words in
|
||||
* little endian format. A little endian machine stores MSB of word in
|
||||
* byte[1] and LSB in byte[0]. A big endian machine stores MSB in byte[0]
|
||||
* and LSB in byte[1].
|
||||
*
|
||||
* This structure is used in the code for things that are endian neutral
|
||||
* but that fit in a word so that we can manipulate them efficiently.
|
||||
* By endian neutral, I mean things that are really an array of bytes,
|
||||
* and always used as such, for example the hint bits. Jean II
|
||||
*/
|
||||
typedef union {
|
||||
__u16 word;
|
||||
__u8 byte[2];
|
||||
} __u16_host_order;
|
||||
|
||||
/* Types of discovery */
|
||||
typedef enum {
|
||||
DISCOVERY_LOG, /* What's in our discovery log */
|
||||
DISCOVERY_ACTIVE, /* Doing our own discovery on the medium */
|
||||
DISCOVERY_PASSIVE, /* Peer doing discovery on the medium */
|
||||
EXPIRY_TIMEOUT, /* Entry expired due to timeout */
|
||||
} DISCOVERY_MODE;
|
||||
|
||||
#define NICKNAME_MAX_LEN 21
|
||||
|
||||
/* Basic discovery information about a peer */
|
||||
typedef struct irda_device_info discinfo_t; /* linux/irda.h */
|
||||
|
||||
/*
|
||||
* The DISCOVERY structure is used for both discovery requests and responses
|
||||
*/
|
||||
typedef struct discovery_t {
|
||||
irda_queue_t q; /* Must be first! */
|
||||
|
||||
discinfo_t data; /* Basic discovery information */
|
||||
int name_len; /* Length of nickname */
|
||||
|
||||
LAP_REASON condition; /* More info about the discovery */
|
||||
int gen_addr_bit; /* Need to generate a new device
|
||||
* address? */
|
||||
int nslots; /* Number of slots to use when
|
||||
* discovering */
|
||||
unsigned long timestamp; /* Last time discovered */
|
||||
unsigned long firststamp; /* First time discovered */
|
||||
} discovery_t;
|
||||
|
||||
void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *discovery);
|
||||
void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log);
|
||||
void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force);
|
||||
struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn,
|
||||
__u16 mask, int old_entries);
|
||||
|
||||
#endif
|
|
@ -1,106 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ircomm_core.h
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Wed Jun 9 08:58:43 1999
|
||||
* Modified at: Mon Dec 13 11:52:29 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRCOMM_CORE_H
|
||||
#define IRCOMM_CORE_H
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irqueue.h>
|
||||
#include <net/irda/ircomm_event.h>
|
||||
|
||||
#define IRCOMM_MAGIC 0x98347298
|
||||
#define IRCOMM_HEADER_SIZE 1
|
||||
|
||||
struct ircomm_cb; /* Forward decl. */
|
||||
|
||||
/*
|
||||
* A small call-table, so we don't have to check the service-type whenever
|
||||
* we want to do something
|
||||
*/
|
||||
typedef struct {
|
||||
int (*data_request)(struct ircomm_cb *, struct sk_buff *, int clen);
|
||||
int (*connect_request)(struct ircomm_cb *, struct sk_buff *,
|
||||
struct ircomm_info *);
|
||||
int (*connect_response)(struct ircomm_cb *, struct sk_buff *);
|
||||
int (*disconnect_request)(struct ircomm_cb *, struct sk_buff *,
|
||||
struct ircomm_info *);
|
||||
} call_t;
|
||||
|
||||
struct ircomm_cb {
|
||||
irda_queue_t queue;
|
||||
magic_t magic;
|
||||
|
||||
notify_t notify;
|
||||
call_t issue;
|
||||
|
||||
int state;
|
||||
int line; /* Which TTY line we are using */
|
||||
|
||||
struct tsap_cb *tsap;
|
||||
struct lsap_cb *lsap;
|
||||
|
||||
__u8 dlsap_sel; /* Destination LSAP/TSAP selector */
|
||||
__u8 slsap_sel; /* Source LSAP/TSAP selector */
|
||||
|
||||
__u32 saddr; /* Source device address (link we are using) */
|
||||
__u32 daddr; /* Destination device address */
|
||||
|
||||
int max_header_size; /* Header space we must reserve for each frame */
|
||||
int max_data_size; /* The amount of data we can fill in each frame */
|
||||
|
||||
LOCAL_FLOW flow_status; /* Used by ircomm_lmp */
|
||||
int pkt_count; /* Number of frames we have sent to IrLAP */
|
||||
|
||||
__u8 service_type;
|
||||
};
|
||||
|
||||
extern hashbin_t *ircomm;
|
||||
|
||||
struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line);
|
||||
int ircomm_close(struct ircomm_cb *self);
|
||||
|
||||
int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb);
|
||||
void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb);
|
||||
void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb);
|
||||
int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb);
|
||||
int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel,
|
||||
__u32 saddr, __u32 daddr, struct sk_buff *skb,
|
||||
__u8 service_type);
|
||||
void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb,
|
||||
struct ircomm_info *info);
|
||||
void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb,
|
||||
struct ircomm_info *info);
|
||||
int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata);
|
||||
int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata);
|
||||
void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb,
|
||||
struct ircomm_info *info);
|
||||
void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow);
|
||||
|
||||
#define ircomm_is_connected(self) (self->state == IRCOMM_CONN)
|
||||
|
||||
#endif
|
|
@ -1,83 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ircomm_event.h
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sun Jun 6 23:51:13 1999
|
||||
* Modified at: Thu Jun 10 08:36:25 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRCOMM_EVENT_H
|
||||
#define IRCOMM_EVENT_H
|
||||
|
||||
#include <net/irda/irmod.h>
|
||||
|
||||
typedef enum {
|
||||
IRCOMM_IDLE,
|
||||
IRCOMM_WAITI,
|
||||
IRCOMM_WAITR,
|
||||
IRCOMM_CONN,
|
||||
} IRCOMM_STATE;
|
||||
|
||||
/* IrCOMM Events */
|
||||
typedef enum {
|
||||
IRCOMM_CONNECT_REQUEST,
|
||||
IRCOMM_CONNECT_RESPONSE,
|
||||
IRCOMM_TTP_CONNECT_INDICATION,
|
||||
IRCOMM_LMP_CONNECT_INDICATION,
|
||||
IRCOMM_TTP_CONNECT_CONFIRM,
|
||||
IRCOMM_LMP_CONNECT_CONFIRM,
|
||||
|
||||
IRCOMM_LMP_DISCONNECT_INDICATION,
|
||||
IRCOMM_TTP_DISCONNECT_INDICATION,
|
||||
IRCOMM_DISCONNECT_REQUEST,
|
||||
|
||||
IRCOMM_TTP_DATA_INDICATION,
|
||||
IRCOMM_LMP_DATA_INDICATION,
|
||||
IRCOMM_DATA_REQUEST,
|
||||
IRCOMM_CONTROL_REQUEST,
|
||||
IRCOMM_CONTROL_INDICATION,
|
||||
} IRCOMM_EVENT;
|
||||
|
||||
/*
|
||||
* Used for passing information through the state-machine
|
||||
*/
|
||||
struct ircomm_info {
|
||||
__u32 saddr; /* Source device address */
|
||||
__u32 daddr; /* Destination device address */
|
||||
__u8 dlsap_sel;
|
||||
LM_REASON reason; /* Reason for disconnect */
|
||||
__u32 max_data_size;
|
||||
__u32 max_header_size;
|
||||
|
||||
struct qos_info *qos;
|
||||
};
|
||||
|
||||
extern const char *const ircomm_state[];
|
||||
|
||||
struct ircomm_cb; /* Forward decl. */
|
||||
|
||||
int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_info *info);
|
||||
void ircomm_next_state(struct ircomm_cb *self, IRCOMM_STATE state);
|
||||
|
||||
#endif
|
|
@ -1,36 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ircomm_lmp.h
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Wed Jun 9 10:06:07 1999
|
||||
* Modified at: Fri Aug 13 07:32:32 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRCOMM_LMP_H
|
||||
#define IRCOMM_LMP_H
|
||||
|
||||
#include <net/irda/ircomm_core.h>
|
||||
|
||||
int ircomm_open_lsap(struct ircomm_cb *self);
|
||||
|
||||
#endif
|
|
@ -1,147 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ircomm_param.h
|
||||
* Version: 1.0
|
||||
* Description: Parameter handling for the IrCOMM protocol
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Mon Jun 7 08:47:28 1999
|
||||
* Modified at: Wed Aug 25 13:46:33 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRCOMM_PARAMS_H
|
||||
#define IRCOMM_PARAMS_H
|
||||
|
||||
#include <net/irda/parameters.h>
|
||||
|
||||
/* Parameters common to all service types */
|
||||
#define IRCOMM_SERVICE_TYPE 0x00
|
||||
#define IRCOMM_PORT_TYPE 0x01 /* Only used in LM-IAS */
|
||||
#define IRCOMM_PORT_NAME 0x02 /* Only used in LM-IAS */
|
||||
|
||||
/* Parameters for both 3 wire and 9 wire */
|
||||
#define IRCOMM_DATA_RATE 0x10
|
||||
#define IRCOMM_DATA_FORMAT 0x11
|
||||
#define IRCOMM_FLOW_CONTROL 0x12
|
||||
#define IRCOMM_XON_XOFF 0x13
|
||||
#define IRCOMM_ENQ_ACK 0x14
|
||||
#define IRCOMM_LINE_STATUS 0x15
|
||||
#define IRCOMM_BREAK 0x16
|
||||
|
||||
/* Parameters for 9 wire */
|
||||
#define IRCOMM_DTE 0x20
|
||||
#define IRCOMM_DCE 0x21
|
||||
#define IRCOMM_POLL 0x22
|
||||
|
||||
/* Service type (details) */
|
||||
#define IRCOMM_3_WIRE_RAW 0x01
|
||||
#define IRCOMM_3_WIRE 0x02
|
||||
#define IRCOMM_9_WIRE 0x04
|
||||
#define IRCOMM_CENTRONICS 0x08
|
||||
|
||||
/* Port type (details) */
|
||||
#define IRCOMM_SERIAL 0x00
|
||||
#define IRCOMM_PARALLEL 0x01
|
||||
|
||||
/* Data format (details) */
|
||||
#define IRCOMM_WSIZE_5 0x00
|
||||
#define IRCOMM_WSIZE_6 0x01
|
||||
#define IRCOMM_WSIZE_7 0x02
|
||||
#define IRCOMM_WSIZE_8 0x03
|
||||
|
||||
#define IRCOMM_1_STOP_BIT 0x00
|
||||
#define IRCOMM_2_STOP_BIT 0x04 /* 1.5 if char len 5 */
|
||||
|
||||
#define IRCOMM_PARITY_DISABLE 0x00
|
||||
#define IRCOMM_PARITY_ENABLE 0x08
|
||||
|
||||
#define IRCOMM_PARITY_ODD 0x00
|
||||
#define IRCOMM_PARITY_EVEN 0x10
|
||||
#define IRCOMM_PARITY_MARK 0x20
|
||||
#define IRCOMM_PARITY_SPACE 0x30
|
||||
|
||||
/* Flow control */
|
||||
#define IRCOMM_XON_XOFF_IN 0x01
|
||||
#define IRCOMM_XON_XOFF_OUT 0x02
|
||||
#define IRCOMM_RTS_CTS_IN 0x04
|
||||
#define IRCOMM_RTS_CTS_OUT 0x08
|
||||
#define IRCOMM_DSR_DTR_IN 0x10
|
||||
#define IRCOMM_DSR_DTR_OUT 0x20
|
||||
#define IRCOMM_ENQ_ACK_IN 0x40
|
||||
#define IRCOMM_ENQ_ACK_OUT 0x80
|
||||
|
||||
/* Line status */
|
||||
#define IRCOMM_OVERRUN_ERROR 0x02
|
||||
#define IRCOMM_PARITY_ERROR 0x04
|
||||
#define IRCOMM_FRAMING_ERROR 0x08
|
||||
|
||||
/* DTE (Data terminal equipment) line settings */
|
||||
#define IRCOMM_DELTA_DTR 0x01
|
||||
#define IRCOMM_DELTA_RTS 0x02
|
||||
#define IRCOMM_DTR 0x04
|
||||
#define IRCOMM_RTS 0x08
|
||||
|
||||
/* DCE (Data communications equipment) line settings */
|
||||
#define IRCOMM_DELTA_CTS 0x01 /* Clear to send has changed */
|
||||
#define IRCOMM_DELTA_DSR 0x02 /* Data set ready has changed */
|
||||
#define IRCOMM_DELTA_RI 0x04 /* Ring indicator has changed */
|
||||
#define IRCOMM_DELTA_CD 0x08 /* Carrier detect has changed */
|
||||
#define IRCOMM_CTS 0x10 /* Clear to send is high */
|
||||
#define IRCOMM_DSR 0x20 /* Data set ready is high */
|
||||
#define IRCOMM_RI 0x40 /* Ring indicator is high */
|
||||
#define IRCOMM_CD 0x80 /* Carrier detect is high */
|
||||
#define IRCOMM_DCE_DELTA_ANY 0x0f
|
||||
|
||||
/*
|
||||
* Parameter state
|
||||
*/
|
||||
struct ircomm_params {
|
||||
/* General control params */
|
||||
__u8 service_type;
|
||||
__u8 port_type;
|
||||
char port_name[32];
|
||||
|
||||
/* Control params for 3- and 9-wire service type */
|
||||
__u32 data_rate; /* Data rate in bps */
|
||||
__u8 data_format;
|
||||
__u8 flow_control;
|
||||
char xonxoff[2];
|
||||
char enqack[2];
|
||||
__u8 line_status;
|
||||
__u8 _break;
|
||||
|
||||
__u8 null_modem;
|
||||
|
||||
/* Control params for 9-wire service type */
|
||||
__u8 dte;
|
||||
__u8 dce;
|
||||
__u8 poll;
|
||||
|
||||
/* Control params for Centronics service type */
|
||||
};
|
||||
|
||||
struct ircomm_tty_cb; /* Forward decl. */
|
||||
|
||||
int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush);
|
||||
|
||||
extern pi_param_info_t ircomm_param_info;
|
||||
|
||||
#endif /* IRCOMM_PARAMS_H */
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ircomm_ttp.h
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Wed Jun 9 10:06:07 1999
|
||||
* Modified at: Fri Aug 13 07:32:22 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRCOMM_TTP_H
|
||||
#define IRCOMM_TTP_H
|
||||
|
||||
#include <net/irda/ircomm_core.h>
|
||||
|
||||
int ircomm_open_tsap(struct ircomm_cb *self);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ircomm_tty.h
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sun Jun 6 23:24:22 1999
|
||||
* Modified at: Fri Jan 28 13:16:57 2000
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRCOMM_TTY_H
|
||||
#define IRCOMM_TTY_H
|
||||
|
||||
#include <linux/serial.h>
|
||||
#include <linux/termios.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/tty.h> /* struct tty_struct */
|
||||
|
||||
#include <net/irda/irias_object.h>
|
||||
#include <net/irda/ircomm_core.h>
|
||||
#include <net/irda/ircomm_param.h>
|
||||
|
||||
#define IRCOMM_TTY_PORTS 32
|
||||
#define IRCOMM_TTY_MAGIC 0x3432
|
||||
#define IRCOMM_TTY_MAJOR 161
|
||||
#define IRCOMM_TTY_MINOR 0
|
||||
|
||||
/* This is used as an initial value to max_header_size before the proper
|
||||
* value is filled in (5 for ttp, 4 for lmp). This allow us to detect
|
||||
* the state of the underlying connection. - Jean II */
|
||||
#define IRCOMM_TTY_HDR_UNINITIALISED 16
|
||||
/* Same for payload size. See qos.c for the smallest max data size */
|
||||
#define IRCOMM_TTY_DATA_UNINITIALISED (64 - IRCOMM_TTY_HDR_UNINITIALISED)
|
||||
|
||||
/*
|
||||
* IrCOMM TTY driver state
|
||||
*/
|
||||
struct ircomm_tty_cb {
|
||||
irda_queue_t queue; /* Must be first */
|
||||
struct tty_port port;
|
||||
magic_t magic;
|
||||
|
||||
int state; /* Connect state */
|
||||
|
||||
struct ircomm_cb *ircomm; /* IrCOMM layer instance */
|
||||
|
||||
struct sk_buff *tx_skb; /* Transmit buffer */
|
||||
struct sk_buff *ctrl_skb; /* Control data buffer */
|
||||
|
||||
/* Parameters */
|
||||
struct ircomm_params settings;
|
||||
|
||||
__u8 service_type; /* The service that we support */
|
||||
int client; /* True if we are a client */
|
||||
LOCAL_FLOW flow; /* IrTTP flow status */
|
||||
|
||||
int line;
|
||||
|
||||
__u8 dlsap_sel;
|
||||
__u8 slsap_sel;
|
||||
|
||||
__u32 saddr;
|
||||
__u32 daddr;
|
||||
|
||||
__u32 max_data_size; /* Max data we can transmit in one packet */
|
||||
__u32 max_header_size; /* The amount of header space we must reserve */
|
||||
__u32 tx_data_size; /* Max data size of current tx_skb */
|
||||
|
||||
struct iriap_cb *iriap; /* Instance used for querying remote IAS */
|
||||
struct ias_object* obj;
|
||||
void *skey;
|
||||
void *ckey;
|
||||
|
||||
struct timer_list watchdog_timer;
|
||||
struct work_struct tqueue;
|
||||
|
||||
/* Protect concurent access to :
|
||||
* o self->ctrl_skb
|
||||
* o self->tx_skb
|
||||
* Maybe other things may gain to be protected as well...
|
||||
* Jean II */
|
||||
spinlock_t spinlock;
|
||||
};
|
||||
|
||||
void ircomm_tty_start(struct tty_struct *tty);
|
||||
void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self);
|
||||
|
||||
int ircomm_tty_tiocmget(struct tty_struct *tty);
|
||||
int ircomm_tty_tiocmset(struct tty_struct *tty, unsigned int set,
|
||||
unsigned int clear);
|
||||
int ircomm_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
void ircomm_tty_set_termios(struct tty_struct *tty,
|
||||
struct ktermios *old_termios);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ircomm_tty_attach.h
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Wed Jun 9 15:55:18 1999
|
||||
* Modified at: Fri Dec 10 21:04:55 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRCOMM_TTY_ATTACH_H
|
||||
#define IRCOMM_TTY_ATTACH_H
|
||||
|
||||
#include <net/irda/ircomm_tty.h>
|
||||
|
||||
typedef enum {
|
||||
IRCOMM_TTY_IDLE,
|
||||
IRCOMM_TTY_SEARCH,
|
||||
IRCOMM_TTY_QUERY_PARAMETERS,
|
||||
IRCOMM_TTY_QUERY_LSAP_SEL,
|
||||
IRCOMM_TTY_SETUP,
|
||||
IRCOMM_TTY_READY,
|
||||
} IRCOMM_TTY_STATE;
|
||||
|
||||
/* IrCOMM TTY Events */
|
||||
typedef enum {
|
||||
IRCOMM_TTY_ATTACH_CABLE,
|
||||
IRCOMM_TTY_DETACH_CABLE,
|
||||
IRCOMM_TTY_DATA_REQUEST,
|
||||
IRCOMM_TTY_DATA_INDICATION,
|
||||
IRCOMM_TTY_DISCOVERY_REQUEST,
|
||||
IRCOMM_TTY_DISCOVERY_INDICATION,
|
||||
IRCOMM_TTY_CONNECT_CONFIRM,
|
||||
IRCOMM_TTY_CONNECT_INDICATION,
|
||||
IRCOMM_TTY_DISCONNECT_REQUEST,
|
||||
IRCOMM_TTY_DISCONNECT_INDICATION,
|
||||
IRCOMM_TTY_WD_TIMER_EXPIRED,
|
||||
IRCOMM_TTY_GOT_PARAMETERS,
|
||||
IRCOMM_TTY_GOT_LSAPSEL,
|
||||
} IRCOMM_TTY_EVENT;
|
||||
|
||||
/* Used for passing information through the state-machine */
|
||||
struct ircomm_tty_info {
|
||||
__u32 saddr; /* Source device address */
|
||||
__u32 daddr; /* Destination device address */
|
||||
__u8 dlsap_sel;
|
||||
};
|
||||
|
||||
extern const char *const ircomm_state[];
|
||||
extern const char *const ircomm_tty_state[];
|
||||
|
||||
int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_tty_info *info);
|
||||
|
||||
|
||||
int ircomm_tty_attach_cable(struct ircomm_tty_cb *self);
|
||||
void ircomm_tty_detach_cable(struct ircomm_tty_cb *self);
|
||||
void ircomm_tty_connect_confirm(void *instance, void *sap,
|
||||
struct qos_info *qos,
|
||||
__u32 max_sdu_size,
|
||||
__u8 max_header_size,
|
||||
struct sk_buff *skb);
|
||||
void ircomm_tty_disconnect_indication(void *instance, void *sap,
|
||||
LM_REASON reason,
|
||||
struct sk_buff *skb);
|
||||
void ircomm_tty_connect_indication(void *instance, void *sap,
|
||||
struct qos_info *qos,
|
||||
__u32 max_sdu_size,
|
||||
__u8 max_header_size,
|
||||
struct sk_buff *skb);
|
||||
int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self);
|
||||
void ircomm_tty_link_established(struct ircomm_tty_cb *self);
|
||||
|
||||
#endif /* IRCOMM_TTY_ATTACH_H */
|
|
@ -1,115 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irda.h
|
||||
* Version: 1.0
|
||||
* Description: IrDA common include file for kernel internal use
|
||||
* Status: Stable
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Tue Dec 9 21:13:12 1997
|
||||
* Modified at: Fri Jan 28 13:16:32 2000
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved.
|
||||
* Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef NET_IRDA_H
|
||||
#define NET_IRDA_H
|
||||
|
||||
#include <linux/skbuff.h> /* struct sk_buff */
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/if.h> /* sa_family_t in <linux/irda.h> */
|
||||
#include <linux/irda.h>
|
||||
|
||||
typedef __u32 magic_t;
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
/* Hack to do small backoff when setting media busy in IrLAP */
|
||||
#ifndef SMALL
|
||||
#define SMALL 5
|
||||
#endif
|
||||
|
||||
#ifndef IRDA_MIN /* Lets not mix this MIN with other header files */
|
||||
#define IRDA_MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef IRDA_ALIGN
|
||||
# define IRDA_ALIGN __attribute__((aligned))
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IRDA_DEBUG
|
||||
#define IRDA_ASSERT(expr, func) \
|
||||
do { if(!(expr)) { \
|
||||
printk( "Assertion failed! %s:%s:%d %s\n", \
|
||||
__FILE__,__func__,__LINE__,(#expr) ); \
|
||||
func } } while (0)
|
||||
#define IRDA_ASSERT_LABEL(label) label
|
||||
#else
|
||||
#define IRDA_ASSERT(expr, func) do { (void)(expr); } while (0)
|
||||
#define IRDA_ASSERT_LABEL(label)
|
||||
#endif /* CONFIG_IRDA_DEBUG */
|
||||
|
||||
/*
|
||||
* Magic numbers used by Linux-IrDA. Random numbers which must be unique to
|
||||
* give the best protection
|
||||
*/
|
||||
|
||||
#define IRTTY_MAGIC 0x2357
|
||||
#define LAP_MAGIC 0x1357
|
||||
#define LMP_MAGIC 0x4321
|
||||
#define LMP_LSAP_MAGIC 0x69333
|
||||
#define LMP_LAP_MAGIC 0x3432
|
||||
#define IRDA_DEVICE_MAGIC 0x63454
|
||||
#define IAS_MAGIC 0x007
|
||||
#define TTP_MAGIC 0x241169
|
||||
#define TTP_TSAP_MAGIC 0x4345
|
||||
#define IROBEX_MAGIC 0x341324
|
||||
#define HB_MAGIC 0x64534
|
||||
#define IRLAN_MAGIC 0x754
|
||||
#define IAS_OBJECT_MAGIC 0x34234
|
||||
#define IAS_ATTRIB_MAGIC 0x45232
|
||||
#define IRDA_TASK_MAGIC 0x38423
|
||||
|
||||
#define IAS_DEVICE_ID 0x0000 /* Defined by IrDA, IrLMP section 4.1 (page 68) */
|
||||
#define IAS_PNP_ID 0xd342
|
||||
#define IAS_OBEX_ID 0x34323
|
||||
#define IAS_IRLAN_ID 0x34234
|
||||
#define IAS_IRCOMM_ID 0x2343
|
||||
#define IAS_IRLPT_ID 0x9876
|
||||
|
||||
struct net_device;
|
||||
struct packet_type;
|
||||
|
||||
void irda_proc_register(void);
|
||||
void irda_proc_unregister(void);
|
||||
|
||||
int irda_sysctl_register(void);
|
||||
void irda_sysctl_unregister(void);
|
||||
|
||||
int irsock_init(void);
|
||||
void irsock_cleanup(void);
|
||||
|
||||
int irda_nl_register(void);
|
||||
void irda_nl_unregister(void);
|
||||
|
||||
int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *ptype, struct net_device *orig_dev);
|
||||
|
||||
#endif /* NET_IRDA_H */
|
|
@ -1,285 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irda_device.h
|
||||
* Version: 0.9
|
||||
* Description: Contains various declarations used by the drivers
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Tue Apr 14 12:41:42 1998
|
||||
* Modified at: Mon Mar 20 09:08:57 2000
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
|
||||
* Copyright (c) 1998 Thomas Davis, <ratbert@radiks.net>,
|
||||
* Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
/*
|
||||
* This header contains all the IrDA definitions a driver really
|
||||
* needs, and therefore the driver should not need to include
|
||||
* any other IrDA headers - Jean II
|
||||
*/
|
||||
|
||||
#ifndef IRDA_DEVICE_H
|
||||
#define IRDA_DEVICE_H
|
||||
|
||||
#include <linux/tty.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/skbuff.h> /* struct sk_buff */
|
||||
#include <linux/irda.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <net/pkt_sched.h>
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/qos.h> /* struct qos_info */
|
||||
#include <net/irda/irqueue.h> /* irda_queue_t */
|
||||
|
||||
/* A few forward declarations (to make compiler happy) */
|
||||
struct irlap_cb;
|
||||
|
||||
/* Some non-standard interface flags (should not conflict with any in if.h) */
|
||||
#define IFF_SIR 0x0001 /* Supports SIR speeds */
|
||||
#define IFF_MIR 0x0002 /* Supports MIR speeds */
|
||||
#define IFF_FIR 0x0004 /* Supports FIR speeds */
|
||||
#define IFF_VFIR 0x0008 /* Supports VFIR speeds */
|
||||
#define IFF_PIO 0x0010 /* Supports PIO transfer of data */
|
||||
#define IFF_DMA 0x0020 /* Supports DMA transfer of data */
|
||||
#define IFF_SHM 0x0040 /* Supports shared memory data transfers */
|
||||
#define IFF_DONGLE 0x0080 /* Interface has a dongle attached */
|
||||
#define IFF_AIR 0x0100 /* Supports Advanced IR (AIR) standards */
|
||||
|
||||
#define IO_XMIT 0x01
|
||||
#define IO_RECV 0x02
|
||||
|
||||
typedef enum {
|
||||
IRDA_IRLAP, /* IrDA mode, and deliver to IrLAP */
|
||||
IRDA_RAW, /* IrDA mode */
|
||||
SHARP_ASK,
|
||||
TV_REMOTE, /* Also known as Consumer Electronics IR */
|
||||
} INFRARED_MODE;
|
||||
|
||||
typedef enum {
|
||||
IRDA_TASK_INIT, /* All tasks are initialized with this state */
|
||||
IRDA_TASK_DONE, /* Signals that the task is finished */
|
||||
IRDA_TASK_WAIT,
|
||||
IRDA_TASK_WAIT1,
|
||||
IRDA_TASK_WAIT2,
|
||||
IRDA_TASK_WAIT3,
|
||||
IRDA_TASK_CHILD_INIT, /* Initializing child task */
|
||||
IRDA_TASK_CHILD_WAIT, /* Waiting for child task to finish */
|
||||
IRDA_TASK_CHILD_DONE /* Child task is finished */
|
||||
} IRDA_TASK_STATE;
|
||||
|
||||
struct irda_task;
|
||||
typedef int (*IRDA_TASK_CALLBACK) (struct irda_task *task);
|
||||
|
||||
struct irda_task {
|
||||
irda_queue_t q;
|
||||
magic_t magic;
|
||||
|
||||
IRDA_TASK_STATE state;
|
||||
IRDA_TASK_CALLBACK function;
|
||||
IRDA_TASK_CALLBACK finished;
|
||||
|
||||
struct irda_task *parent;
|
||||
struct timer_list timer;
|
||||
|
||||
void *instance; /* Instance being called */
|
||||
void *param; /* Parameter to be used by instance */
|
||||
};
|
||||
|
||||
/* Dongle info */
|
||||
struct dongle_reg;
|
||||
typedef struct {
|
||||
struct dongle_reg *issue; /* Registration info */
|
||||
struct net_device *dev; /* Device we are attached to */
|
||||
struct irda_task *speed_task; /* Task handling speed change */
|
||||
struct irda_task *reset_task; /* Task handling reset */
|
||||
__u32 speed; /* Current speed */
|
||||
|
||||
/* Callbacks to the IrDA device driver */
|
||||
int (*set_mode)(struct net_device *, int mode);
|
||||
int (*read)(struct net_device *dev, __u8 *buf, int len);
|
||||
int (*write)(struct net_device *dev, __u8 *buf, int len);
|
||||
int (*set_dtr_rts)(struct net_device *dev, int dtr, int rts);
|
||||
} dongle_t;
|
||||
|
||||
/* Dongle registration info */
|
||||
struct dongle_reg {
|
||||
irda_queue_t q; /* Must be first */
|
||||
IRDA_DONGLE type;
|
||||
|
||||
void (*open)(dongle_t *dongle, struct qos_info *qos);
|
||||
void (*close)(dongle_t *dongle);
|
||||
int (*reset)(struct irda_task *task);
|
||||
int (*change_speed)(struct irda_task *task);
|
||||
struct module *owner;
|
||||
};
|
||||
|
||||
/*
|
||||
* Per-packet information we need to hide inside sk_buff
|
||||
* (must not exceed 48 bytes, check with struct sk_buff)
|
||||
* The default_qdisc_pad field is a temporary hack.
|
||||
*/
|
||||
struct irda_skb_cb {
|
||||
unsigned int default_qdisc_pad;
|
||||
magic_t magic; /* Be sure that we can trust the information */
|
||||
__u32 next_speed; /* The Speed to be set *after* this frame */
|
||||
__u16 mtt; /* Minimum turn around time */
|
||||
__u16 xbofs; /* Number of xbofs required, used by SIR mode */
|
||||
__u16 next_xbofs; /* Number of xbofs required *after* this frame */
|
||||
void *context; /* May be used by drivers */
|
||||
void (*destructor)(struct sk_buff *skb); /* Used for flow control */
|
||||
__u16 xbofs_delay; /* Number of xbofs used for generating the mtt */
|
||||
__u8 line; /* Used by IrCOMM in IrLPT mode */
|
||||
};
|
||||
|
||||
/* Chip specific info */
|
||||
typedef struct {
|
||||
int cfg_base; /* Config register IO base */
|
||||
int sir_base; /* SIR IO base */
|
||||
int fir_base; /* FIR IO base */
|
||||
int mem_base; /* Shared memory base */
|
||||
int sir_ext; /* Length of SIR iobase */
|
||||
int fir_ext; /* Length of FIR iobase */
|
||||
int irq, irq2; /* Interrupts used */
|
||||
int dma, dma2; /* DMA channel(s) used */
|
||||
int fifo_size; /* FIFO size */
|
||||
int irqflags; /* interrupt flags (ie, IRQF_SHARED) */
|
||||
int direction; /* Link direction, used by some FIR drivers */
|
||||
int enabled; /* Powered on? */
|
||||
int suspended; /* Suspended by APM */
|
||||
__u32 speed; /* Currently used speed */
|
||||
__u32 new_speed; /* Speed we must change to when Tx is finished */
|
||||
int dongle_id; /* Dongle or transceiver currently used */
|
||||
} chipio_t;
|
||||
|
||||
/* IO buffer specific info (inspired by struct sk_buff) */
|
||||
typedef struct {
|
||||
int state; /* Receiving state (transmit state not used) */
|
||||
int in_frame; /* True if receiving frame */
|
||||
|
||||
__u8 *head; /* start of buffer */
|
||||
__u8 *data; /* start of data in buffer */
|
||||
|
||||
int len; /* current length of data */
|
||||
int truesize; /* total allocated size of buffer */
|
||||
__u16 fcs;
|
||||
|
||||
struct sk_buff *skb; /* ZeroCopy Rx in async_unwrap_char() */
|
||||
} iobuff_t;
|
||||
|
||||
/* Maximum SIR frame (skb) that we expect to receive *unwrapped*.
|
||||
* Max LAP MTU (I field) is 2048 bytes max (IrLAP 1.1, chapt 6.6.5, p40).
|
||||
* Max LAP header is 2 bytes (for now).
|
||||
* Max CRC is 2 bytes at SIR, 4 bytes at FIR.
|
||||
* Need 1 byte for skb_reserve() to align IP header for IrLAN.
|
||||
* Add a few extra bytes just to be safe (buffer is power of two anyway)
|
||||
* Jean II */
|
||||
#define IRDA_SKB_MAX_MTU 2064
|
||||
/* Maximum SIR frame that we expect to send, wrapped (i.e. with XBOFS
|
||||
* and escaped characters on top of above). */
|
||||
#define IRDA_SIR_MAX_FRAME 4269
|
||||
|
||||
/* The SIR unwrapper async_unwrap_char() will use a Rx-copy-break mechanism
|
||||
* when using the optional ZeroCopy Rx, where only small frames are memcpy
|
||||
* to a smaller skb to save memory. This is the threshold under which copy
|
||||
* will happen (and over which it won't happen).
|
||||
* Some FIR drivers may use this #define as well...
|
||||
* This is the same value as various Ethernet drivers. - Jean II */
|
||||
#define IRDA_RX_COPY_THRESHOLD 256
|
||||
|
||||
/* Function prototypes */
|
||||
int irda_device_init(void);
|
||||
void irda_device_cleanup(void);
|
||||
|
||||
/* IrLAP entry points used by the drivers.
|
||||
* We declare them here to avoid the driver pulling a whole bunch stack
|
||||
* headers they don't really need - Jean II */
|
||||
struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos,
|
||||
const char *hw_name);
|
||||
void irlap_close(struct irlap_cb *self);
|
||||
|
||||
/* Interface to be uses by IrLAP */
|
||||
void irda_device_set_media_busy(struct net_device *dev, int status);
|
||||
int irda_device_is_media_busy(struct net_device *dev);
|
||||
int irda_device_is_receiving(struct net_device *dev);
|
||||
|
||||
/* Interface for internal use */
|
||||
static inline int irda_device_txqueue_empty(const struct net_device *dev)
|
||||
{
|
||||
return qdisc_all_tx_empty(dev);
|
||||
}
|
||||
int irda_device_set_raw_mode(struct net_device* self, int status);
|
||||
struct net_device *alloc_irdadev(int sizeof_priv);
|
||||
|
||||
void irda_setup_dma(int channel, dma_addr_t buffer, int count, int mode);
|
||||
|
||||
/*
|
||||
* Function irda_get_mtt (skb)
|
||||
*
|
||||
* Utility function for getting the minimum turnaround time out of
|
||||
* the skb, where it has been hidden in the cb field.
|
||||
*/
|
||||
static inline __u16 irda_get_mtt(const struct sk_buff *skb)
|
||||
{
|
||||
const struct irda_skb_cb *cb = (const struct irda_skb_cb *) skb->cb;
|
||||
return (cb->magic == LAP_MAGIC) ? cb->mtt : 10000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function irda_get_next_speed (skb)
|
||||
*
|
||||
* Extract the speed that should be set *after* this frame from the skb
|
||||
*
|
||||
* Note : return -1 for user space frames
|
||||
*/
|
||||
static inline __u32 irda_get_next_speed(const struct sk_buff *skb)
|
||||
{
|
||||
const struct irda_skb_cb *cb = (const struct irda_skb_cb *) skb->cb;
|
||||
return (cb->magic == LAP_MAGIC) ? cb->next_speed : -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function irda_get_next_xbofs (skb)
|
||||
*
|
||||
* Extract the xbofs that should be set for this frame from the skb
|
||||
*
|
||||
* Note : default to 10 for user space frames
|
||||
*/
|
||||
static inline __u16 irda_get_xbofs(const struct sk_buff *skb)
|
||||
{
|
||||
const struct irda_skb_cb *cb = (const struct irda_skb_cb *) skb->cb;
|
||||
return (cb->magic == LAP_MAGIC) ? cb->xbofs : 10;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function irda_get_next_xbofs (skb)
|
||||
*
|
||||
* Extract the xbofs that should be set *after* this frame from the skb
|
||||
*
|
||||
* Note : return -1 for user space frames
|
||||
*/
|
||||
static inline __u16 irda_get_next_xbofs(const struct sk_buff *skb)
|
||||
{
|
||||
const struct irda_skb_cb *cb = (const struct irda_skb_cb *) skb->cb;
|
||||
return (cb->magic == LAP_MAGIC) ? cb->next_xbofs : -1;
|
||||
}
|
||||
#endif /* IRDA_DEVICE_H */
|
||||
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: iriap.h
|
||||
* Version: 0.5
|
||||
* Description: Information Access Protocol (IAP)
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Thu Aug 21 00:02:07 1997
|
||||
* Modified at: Sat Dec 25 16:42:09 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1997-1999 Dag Brattli <dagb@cs.uit.no>,
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRIAP_H
|
||||
#define IRIAP_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <net/irda/iriap_event.h>
|
||||
#include <net/irda/irias_object.h>
|
||||
#include <net/irda/irqueue.h> /* irda_queue_t */
|
||||
#include <net/irda/timer.h> /* struct timer_list */
|
||||
|
||||
#define IAP_LST 0x80
|
||||
#define IAP_ACK 0x40
|
||||
|
||||
#define IAS_SERVER 0
|
||||
#define IAS_CLIENT 1
|
||||
|
||||
/* IrIAP Op-codes */
|
||||
#define GET_INFO_BASE 0x01
|
||||
#define GET_OBJECTS 0x02
|
||||
#define GET_VALUE 0x03
|
||||
#define GET_VALUE_BY_CLASS 0x04
|
||||
#define GET_OBJECT_INFO 0x05
|
||||
#define GET_ATTRIB_NAMES 0x06
|
||||
|
||||
#define IAS_SUCCESS 0
|
||||
#define IAS_CLASS_UNKNOWN 1
|
||||
#define IAS_ATTRIB_UNKNOWN 2
|
||||
#define IAS_DISCONNECT 10
|
||||
|
||||
typedef void (*CONFIRM_CALLBACK)(int result, __u16 obj_id,
|
||||
struct ias_value *value, void *priv);
|
||||
|
||||
struct iriap_cb {
|
||||
irda_queue_t q; /* Must be first */
|
||||
magic_t magic; /* Magic cookie */
|
||||
|
||||
int mode; /* Client or server */
|
||||
|
||||
__u32 saddr;
|
||||
__u32 daddr;
|
||||
__u8 operation;
|
||||
|
||||
struct sk_buff *request_skb;
|
||||
struct lsap_cb *lsap;
|
||||
__u8 slsap_sel;
|
||||
|
||||
/* Client states */
|
||||
IRIAP_STATE client_state;
|
||||
IRIAP_STATE call_state;
|
||||
|
||||
/* Server states */
|
||||
IRIAP_STATE server_state;
|
||||
IRIAP_STATE r_connect_state;
|
||||
|
||||
CONFIRM_CALLBACK confirm;
|
||||
void *priv; /* Used to identify client */
|
||||
|
||||
__u8 max_header_size;
|
||||
__u32 max_data_size;
|
||||
|
||||
struct timer_list watchdog_timer;
|
||||
};
|
||||
|
||||
int iriap_init(void);
|
||||
void iriap_cleanup(void);
|
||||
|
||||
struct iriap_cb *iriap_open(__u8 slsap_sel, int mode, void *priv,
|
||||
CONFIRM_CALLBACK callback);
|
||||
void iriap_close(struct iriap_cb *self);
|
||||
|
||||
int iriap_getvaluebyclass_request(struct iriap_cb *self,
|
||||
__u32 saddr, __u32 daddr,
|
||||
char *name, char *attr);
|
||||
void iriap_connect_request(struct iriap_cb *self);
|
||||
void iriap_send_ack( struct iriap_cb *self);
|
||||
void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb);
|
||||
|
||||
void iriap_register_server(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: iriap_event.h
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Mon Aug 4 20:40:53 1997
|
||||
* Modified at: Sun Oct 31 22:02:54 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRIAP_FSM_H
|
||||
#define IRIAP_FSM_H
|
||||
|
||||
/* Forward because of circular include dependecies */
|
||||
struct iriap_cb;
|
||||
|
||||
/* IrIAP states */
|
||||
typedef enum {
|
||||
/* Client */
|
||||
S_DISCONNECT,
|
||||
S_CONNECTING,
|
||||
S_CALL,
|
||||
|
||||
/* S-Call */
|
||||
S_MAKE_CALL,
|
||||
S_CALLING,
|
||||
S_OUTSTANDING,
|
||||
S_REPLYING,
|
||||
S_WAIT_FOR_CALL,
|
||||
S_WAIT_ACTIVE,
|
||||
|
||||
/* Server */
|
||||
R_DISCONNECT,
|
||||
R_CALL,
|
||||
|
||||
/* R-Connect */
|
||||
R_WAITING,
|
||||
R_WAIT_ACTIVE,
|
||||
R_RECEIVING,
|
||||
R_EXECUTE,
|
||||
R_RETURNING,
|
||||
} IRIAP_STATE;
|
||||
|
||||
typedef enum {
|
||||
IAP_CALL_REQUEST,
|
||||
IAP_CALL_REQUEST_GVBC,
|
||||
IAP_CALL_RESPONSE,
|
||||
IAP_RECV_F_LST,
|
||||
IAP_LM_DISCONNECT_INDICATION,
|
||||
IAP_LM_CONNECT_INDICATION,
|
||||
IAP_LM_CONNECT_CONFIRM,
|
||||
} IRIAP_EVENT;
|
||||
|
||||
void iriap_next_client_state (struct iriap_cb *self, IRIAP_STATE state);
|
||||
void iriap_next_call_state (struct iriap_cb *self, IRIAP_STATE state);
|
||||
void iriap_next_server_state (struct iriap_cb *self, IRIAP_STATE state);
|
||||
void iriap_next_r_connect_state(struct iriap_cb *self, IRIAP_STATE state);
|
||||
|
||||
|
||||
void iriap_do_client_event(struct iriap_cb *self, IRIAP_EVENT event,
|
||||
struct sk_buff *skb);
|
||||
void iriap_do_call_event (struct iriap_cb *self, IRIAP_EVENT event,
|
||||
struct sk_buff *skb);
|
||||
|
||||
void iriap_do_server_event (struct iriap_cb *self, IRIAP_EVENT event,
|
||||
struct sk_buff *skb);
|
||||
void iriap_do_r_connect_event(struct iriap_cb *self, IRIAP_EVENT event,
|
||||
struct sk_buff *skb);
|
||||
|
||||
#endif /* IRIAP_FSM_H */
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irias_object.h
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Thu Oct 1 22:49:50 1998
|
||||
* Modified at: Wed Dec 15 11:20:57 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef LM_IAS_OBJECT_H
|
||||
#define LM_IAS_OBJECT_H
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irqueue.h>
|
||||
|
||||
/* LM-IAS Attribute types */
|
||||
#define IAS_MISSING 0
|
||||
#define IAS_INTEGER 1
|
||||
#define IAS_OCT_SEQ 2
|
||||
#define IAS_STRING 3
|
||||
|
||||
/* Object ownership of attributes (user or kernel) */
|
||||
#define IAS_KERNEL_ATTR 0
|
||||
#define IAS_USER_ATTR 1
|
||||
|
||||
/*
|
||||
* LM-IAS Object
|
||||
*/
|
||||
struct ias_object {
|
||||
irda_queue_t q; /* Must be first! */
|
||||
magic_t magic;
|
||||
|
||||
char *name;
|
||||
int id;
|
||||
hashbin_t *attribs;
|
||||
};
|
||||
|
||||
/*
|
||||
* Values used by LM-IAS attributes
|
||||
*/
|
||||
struct ias_value {
|
||||
__u8 type; /* Value description */
|
||||
__u8 owner; /* Managed from user/kernel space */
|
||||
int charset; /* Only used by string type */
|
||||
int len;
|
||||
|
||||
/* Value */
|
||||
union {
|
||||
int integer;
|
||||
char *string;
|
||||
__u8 *oct_seq;
|
||||
} t;
|
||||
};
|
||||
|
||||
/*
|
||||
* Attributes used by LM-IAS objects
|
||||
*/
|
||||
struct ias_attrib {
|
||||
irda_queue_t q; /* Must be first! */
|
||||
int magic;
|
||||
|
||||
char *name; /* Attribute name */
|
||||
struct ias_value *value; /* Attribute value */
|
||||
};
|
||||
|
||||
struct ias_object *irias_new_object(char *name, int id);
|
||||
void irias_insert_object(struct ias_object *obj);
|
||||
int irias_delete_object(struct ias_object *obj);
|
||||
int irias_delete_attrib(struct ias_object *obj, struct ias_attrib *attrib,
|
||||
int cleanobject);
|
||||
void __irias_delete_object(struct ias_object *obj);
|
||||
|
||||
void irias_add_integer_attrib(struct ias_object *obj, char *name, int value,
|
||||
int user);
|
||||
void irias_add_string_attrib(struct ias_object *obj, char *name, char *value,
|
||||
int user);
|
||||
void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets,
|
||||
int len, int user);
|
||||
int irias_object_change_attribute(char *obj_name, char *attrib_name,
|
||||
struct ias_value *new_value);
|
||||
struct ias_object *irias_find_object(char *name);
|
||||
struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name);
|
||||
|
||||
struct ias_value *irias_new_string_value(char *string);
|
||||
struct ias_value *irias_new_integer_value(int integer);
|
||||
struct ias_value *irias_new_octseq_value(__u8 *octseq , int len);
|
||||
struct ias_value *irias_new_missing_value(void);
|
||||
void irias_delete_value(struct ias_value *value);
|
||||
|
||||
extern struct ias_value irias_missing;
|
||||
extern hashbin_t *irias_objects;
|
||||
|
||||
#endif
|
|
@ -1,42 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irlan_client.h
|
||||
* Version: 0.3
|
||||
* Description: IrDA LAN access layer
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sun Aug 31 20:14:37 1997
|
||||
* Modified at: Thu Apr 22 14:13:34 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRLAN_CLIENT_H
|
||||
#define IRLAN_CLIENT_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include <net/irda/irias_object.h>
|
||||
#include <net/irda/irlan_event.h>
|
||||
|
||||
void irlan_client_discovery_indication(discinfo_t *, DISCOVERY_MODE, void *);
|
||||
void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr);
|
||||
|
||||
void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb);
|
||||
void irlan_client_get_value_confirm(int result, __u16 obj_id,
|
||||
struct ias_value *value, void *priv);
|
||||
#endif
|
|
@ -1,230 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irlan_common.h
|
||||
* Version: 0.8
|
||||
* Description: IrDA LAN access layer
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sun Aug 31 20:14:37 1997
|
||||
* Modified at: Sun Oct 31 19:41:24 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRLAN_H
|
||||
#define IRLAN_H
|
||||
|
||||
#include <asm/param.h> /* for HZ */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_ether.h>
|
||||
|
||||
#include <net/irda/irttp.h>
|
||||
|
||||
#define IRLAN_MTU 1518
|
||||
#define IRLAN_TIMEOUT 10*HZ /* 10 seconds */
|
||||
|
||||
/* Command packet types */
|
||||
#define CMD_GET_PROVIDER_INFO 0
|
||||
#define CMD_GET_MEDIA_CHAR 1
|
||||
#define CMD_OPEN_DATA_CHANNEL 2
|
||||
#define CMD_CLOSE_DATA_CHAN 3
|
||||
#define CMD_RECONNECT_DATA_CHAN 4
|
||||
#define CMD_FILTER_OPERATION 5
|
||||
|
||||
/* Some responses */
|
||||
#define RSP_SUCCESS 0
|
||||
#define RSP_INSUFFICIENT_RESOURCES 1
|
||||
#define RSP_INVALID_COMMAND_FORMAT 2
|
||||
#define RSP_COMMAND_NOT_SUPPORTED 3
|
||||
#define RSP_PARAM_NOT_SUPPORTED 4
|
||||
#define RSP_VALUE_NOT_SUPPORTED 5
|
||||
#define RSP_NOT_OPEN 6
|
||||
#define RSP_AUTHENTICATION_REQUIRED 7
|
||||
#define RSP_INVALID_PASSWORD 8
|
||||
#define RSP_PROTOCOL_ERROR 9
|
||||
#define RSP_ASYNCHRONOUS_ERROR 255
|
||||
|
||||
/* Media types */
|
||||
#define MEDIA_802_3 1
|
||||
#define MEDIA_802_5 2
|
||||
|
||||
/* Filter parameters */
|
||||
#define DATA_CHAN 1
|
||||
#define FILTER_TYPE 2
|
||||
#define FILTER_MODE 3
|
||||
|
||||
/* Filter types */
|
||||
#define IRLAN_DIRECTED 0x01
|
||||
#define IRLAN_FUNCTIONAL 0x02
|
||||
#define IRLAN_GROUP 0x04
|
||||
#define IRLAN_MAC_FRAME 0x08
|
||||
#define IRLAN_MULTICAST 0x10
|
||||
#define IRLAN_BROADCAST 0x20
|
||||
#define IRLAN_IPX_SOCKET 0x40
|
||||
|
||||
/* Filter modes */
|
||||
#define ALL 1
|
||||
#define FILTER 2
|
||||
#define NONE 3
|
||||
|
||||
/* Filter operations */
|
||||
#define GET 1
|
||||
#define CLEAR 2
|
||||
#define ADD 3
|
||||
#define REMOVE 4
|
||||
#define DYNAMIC 5
|
||||
|
||||
/* Access types */
|
||||
#define ACCESS_DIRECT 1
|
||||
#define ACCESS_PEER 2
|
||||
#define ACCESS_HOSTED 3
|
||||
|
||||
#define IRLAN_BYTE 0
|
||||
#define IRLAN_SHORT 1
|
||||
#define IRLAN_ARRAY 2
|
||||
|
||||
/* IrLAN sits on top if IrTTP */
|
||||
#define IRLAN_MAX_HEADER (TTP_HEADER+LMP_HEADER)
|
||||
/* 1 byte for the command code and 1 byte for the parameter count */
|
||||
#define IRLAN_CMD_HEADER 2
|
||||
|
||||
#define IRLAN_STRING_PARAMETER_LEN(name, value) (1 + strlen((name)) + 2 \
|
||||
+ strlen ((value)))
|
||||
#define IRLAN_BYTE_PARAMETER_LEN(name) (1 + strlen((name)) + 2 + 1)
|
||||
#define IRLAN_SHORT_PARAMETER_LEN(name) (1 + strlen((name)) + 2 + 2)
|
||||
|
||||
/*
|
||||
* IrLAN client
|
||||
*/
|
||||
struct irlan_client_cb {
|
||||
int state;
|
||||
|
||||
int open_retries;
|
||||
|
||||
struct tsap_cb *tsap_ctrl;
|
||||
__u32 max_sdu_size;
|
||||
__u8 max_header_size;
|
||||
|
||||
int access_type; /* Access type of provider */
|
||||
__u8 reconnect_key[255];
|
||||
__u8 key_len;
|
||||
|
||||
__u16 recv_arb_val;
|
||||
__u16 max_frame;
|
||||
int filter_type;
|
||||
|
||||
int unicast_open;
|
||||
int broadcast_open;
|
||||
|
||||
int tx_busy;
|
||||
struct sk_buff_head txq; /* Transmit control queue */
|
||||
|
||||
struct iriap_cb *iriap;
|
||||
|
||||
struct timer_list kick_timer;
|
||||
};
|
||||
|
||||
/*
|
||||
* IrLAN provider
|
||||
*/
|
||||
struct irlan_provider_cb {
|
||||
int state;
|
||||
|
||||
struct tsap_cb *tsap_ctrl;
|
||||
__u32 max_sdu_size;
|
||||
__u8 max_header_size;
|
||||
|
||||
/*
|
||||
* Store some values here which are used by the provider to parse
|
||||
* the filter operations
|
||||
*/
|
||||
int data_chan;
|
||||
int filter_type;
|
||||
int filter_mode;
|
||||
int filter_operation;
|
||||
int filter_entry;
|
||||
int access_type; /* Access type */
|
||||
__u16 send_arb_val;
|
||||
|
||||
__u8 mac_address[ETH_ALEN]; /* Generated MAC address for peer device */
|
||||
};
|
||||
|
||||
/*
|
||||
* IrLAN control block
|
||||
*/
|
||||
struct irlan_cb {
|
||||
int magic;
|
||||
struct list_head dev_list;
|
||||
struct net_device *dev; /* Ethernet device structure*/
|
||||
|
||||
__u32 saddr; /* Source device address */
|
||||
__u32 daddr; /* Destination device address */
|
||||
int disconnect_reason; /* Why we got disconnected */
|
||||
|
||||
int media; /* Media type */
|
||||
__u8 version[2]; /* IrLAN version */
|
||||
|
||||
struct tsap_cb *tsap_data; /* Data TSAP */
|
||||
|
||||
int use_udata; /* Use Unit Data transfers */
|
||||
|
||||
__u8 stsap_sel_data; /* Source data TSAP selector */
|
||||
__u8 dtsap_sel_data; /* Destination data TSAP selector */
|
||||
__u8 dtsap_sel_ctrl; /* Destination ctrl TSAP selector */
|
||||
|
||||
struct irlan_client_cb client; /* Client specific fields */
|
||||
struct irlan_provider_cb provider; /* Provider specific fields */
|
||||
|
||||
__u32 max_sdu_size;
|
||||
__u8 max_header_size;
|
||||
|
||||
wait_queue_head_t open_wait;
|
||||
struct timer_list watchdog_timer;
|
||||
};
|
||||
|
||||
void irlan_close(struct irlan_cb *self);
|
||||
void irlan_close_tsaps(struct irlan_cb *self);
|
||||
|
||||
int irlan_register_netdev(struct irlan_cb *self);
|
||||
void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel);
|
||||
void irlan_start_watchdog_timer(struct irlan_cb *self, int timeout);
|
||||
|
||||
void irlan_open_data_tsap(struct irlan_cb *self);
|
||||
|
||||
int irlan_run_ctrl_tx_queue(struct irlan_cb *self);
|
||||
|
||||
struct irlan_cb *irlan_get_any(void);
|
||||
void irlan_get_provider_info(struct irlan_cb *self);
|
||||
void irlan_get_media_char(struct irlan_cb *self);
|
||||
void irlan_open_data_channel(struct irlan_cb *self);
|
||||
void irlan_close_data_channel(struct irlan_cb *self);
|
||||
void irlan_set_multicast_filter(struct irlan_cb *self, int status);
|
||||
void irlan_set_broadcast_filter(struct irlan_cb *self, int status);
|
||||
|
||||
int irlan_insert_byte_param(struct sk_buff *skb, char *param, __u8 value);
|
||||
int irlan_insert_short_param(struct sk_buff *skb, char *param, __u16 value);
|
||||
int irlan_insert_string_param(struct sk_buff *skb, char *param, char *value);
|
||||
int irlan_insert_array_param(struct sk_buff *skb, char *name, __u8 *value,
|
||||
__u16 value_len);
|
||||
|
||||
int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irlan_eth.h
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Thu Oct 15 08:36:58 1998
|
||||
* Modified at: Fri May 14 23:29:00 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRLAN_ETH_H
|
||||
#define IRLAN_ETH_H
|
||||
|
||||
struct net_device *alloc_irlandev(const char *name);
|
||||
int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb);
|
||||
|
||||
void irlan_eth_flow_indication( void *instance, void *sap, LOCAL_FLOW flow);
|
||||
#endif
|
|
@ -1,81 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irlan_event.h
|
||||
* Version:
|
||||
* Description: LAN access
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sun Aug 31 20:14:37 1997
|
||||
* Modified at: Tue Feb 2 09:45:17 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1997 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRLAN_EVENT_H
|
||||
#define IRLAN_EVENT_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <net/irda/irlan_common.h>
|
||||
|
||||
typedef enum {
|
||||
IRLAN_IDLE,
|
||||
IRLAN_QUERY,
|
||||
IRLAN_CONN,
|
||||
IRLAN_INFO,
|
||||
IRLAN_MEDIA,
|
||||
IRLAN_OPEN,
|
||||
IRLAN_WAIT,
|
||||
IRLAN_ARB,
|
||||
IRLAN_DATA,
|
||||
IRLAN_CLOSE,
|
||||
IRLAN_SYNC
|
||||
} IRLAN_STATE;
|
||||
|
||||
typedef enum {
|
||||
IRLAN_DISCOVERY_INDICATION,
|
||||
IRLAN_IAS_PROVIDER_AVAIL,
|
||||
IRLAN_IAS_PROVIDER_NOT_AVAIL,
|
||||
IRLAN_LAP_DISCONNECT,
|
||||
IRLAN_LMP_DISCONNECT,
|
||||
IRLAN_CONNECT_COMPLETE,
|
||||
IRLAN_DATA_INDICATION,
|
||||
IRLAN_DATA_CONNECT_INDICATION,
|
||||
IRLAN_RETRY_CONNECT,
|
||||
|
||||
IRLAN_CONNECT_INDICATION,
|
||||
IRLAN_GET_INFO_CMD,
|
||||
IRLAN_GET_MEDIA_CMD,
|
||||
IRLAN_OPEN_DATA_CMD,
|
||||
IRLAN_FILTER_CONFIG_CMD,
|
||||
|
||||
IRLAN_CHECK_CON_ARB,
|
||||
IRLAN_PROVIDER_SIGNAL,
|
||||
|
||||
IRLAN_WATCHDOG_TIMEOUT,
|
||||
} IRLAN_EVENT;
|
||||
|
||||
extern const char * const irlan_state[];
|
||||
|
||||
void irlan_do_client_event(struct irlan_cb *self, IRLAN_EVENT event,
|
||||
struct sk_buff *skb);
|
||||
|
||||
void irlan_do_provider_event(struct irlan_cb *self, IRLAN_EVENT event,
|
||||
struct sk_buff *skb);
|
||||
|
||||
void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state);
|
||||
void irlan_next_provider_state(struct irlan_cb *self, IRLAN_STATE state);
|
||||
|
||||
#endif
|
|
@ -1,35 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irlan_filter.h
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Fri Jan 29 15:24:08 1999
|
||||
* Modified at: Sun Feb 7 23:35:31 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRLAN_FILTER_H
|
||||
#define IRLAN_FILTER_H
|
||||
|
||||
void irlan_check_command_param(struct irlan_cb *self, char *param,
|
||||
char *value);
|
||||
void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb);
|
||||
#ifdef CONFIG_PROC_FS
|
||||
void irlan_print_filter(struct seq_file *seq, int filter_type);
|
||||
#endif
|
||||
|
||||
#endif /* IRLAN_FILTER_H */
|
|
@ -1,52 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irlan_provider.h
|
||||
* Version: 0.1
|
||||
* Description: IrDA LAN access layer
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sun Aug 31 20:14:37 1997
|
||||
* Modified at: Sun May 9 12:26:11 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRLAN_SERVER_H
|
||||
#define IRLAN_SERVER_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include <net/irda/irlan_common.h>
|
||||
|
||||
void irlan_provider_ctrl_disconnect_indication(void *instance, void *sap,
|
||||
LM_REASON reason,
|
||||
struct sk_buff *skb);
|
||||
|
||||
|
||||
void irlan_provider_connect_response(struct irlan_cb *, struct tsap_cb *);
|
||||
|
||||
int irlan_parse_open_data_cmd(struct irlan_cb *self, struct sk_buff *skb);
|
||||
int irlan_provider_parse_command(struct irlan_cb *self, int cmd,
|
||||
struct sk_buff *skb);
|
||||
|
||||
void irlan_provider_send_reply(struct irlan_cb *self, int command,
|
||||
int ret_code);
|
||||
int irlan_provider_open_ctrl_tsap(struct irlan_cb *self);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,311 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irlap.h
|
||||
* Version: 0.8
|
||||
* Description: An IrDA LAP driver for Linux
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Mon Aug 4 20:40:53 1997
|
||||
* Modified at: Fri Dec 10 13:21:17 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
|
||||
* All Rights Reserved.
|
||||
* Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRLAP_H
|
||||
#define IRLAP_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
#include <net/irda/irqueue.h> /* irda_queue_t */
|
||||
#include <net/irda/qos.h> /* struct qos_info */
|
||||
#include <net/irda/discovery.h> /* discovery_t */
|
||||
#include <net/irda/irlap_event.h> /* IRLAP_STATE, ... */
|
||||
#include <net/irda/irmod.h> /* struct notify_t */
|
||||
|
||||
#define CONFIG_IRDA_DYNAMIC_WINDOW 1
|
||||
|
||||
#define LAP_RELIABLE 1
|
||||
#define LAP_UNRELIABLE 0
|
||||
|
||||
#define LAP_ADDR_HEADER 1 /* IrLAP Address Header */
|
||||
#define LAP_CTRL_HEADER 1 /* IrLAP Control Header */
|
||||
|
||||
/* May be different when we get VFIR */
|
||||
#define LAP_MAX_HEADER (LAP_ADDR_HEADER + LAP_CTRL_HEADER)
|
||||
|
||||
/* Each IrDA device gets a random 32 bits IRLAP device address */
|
||||
#define LAP_ALEN 4
|
||||
|
||||
#define BROADCAST 0xffffffff /* Broadcast device address */
|
||||
#define CBROADCAST 0xfe /* Connection broadcast address */
|
||||
#define XID_FORMAT 0x01 /* Discovery XID format */
|
||||
|
||||
/* Nobody seems to use this constant. */
|
||||
#define LAP_WINDOW_SIZE 8
|
||||
/* We keep the LAP queue very small to minimise the amount of buffering.
|
||||
* this improve latency and reduce resource consumption.
|
||||
* This work only because we have synchronous refilling of IrLAP through
|
||||
* the flow control mechanism (via scheduler and IrTTP).
|
||||
* 2 buffers is the minimum we can work with, one that we send while polling
|
||||
* IrTTP, and another to know that we should not send the pf bit.
|
||||
* Jean II */
|
||||
#define LAP_HIGH_THRESHOLD 2
|
||||
/* Some rare non TTP clients don't implement flow control, and
|
||||
* so don't comply with the above limit (and neither with this one).
|
||||
* For IAP and management, it doesn't matter, because they never transmit much.
|
||||
*.For IrLPT, this should be fixed.
|
||||
* - Jean II */
|
||||
#define LAP_MAX_QUEUE 10
|
||||
/* Please note that all IrDA management frames (LMP/TTP conn req/disc and
|
||||
* IAS queries) fall in the second category and are sent to LAP even if TTP
|
||||
* is stopped. This means that those frames will wait only a maximum of
|
||||
* two (2) data frames before beeing sent on the "wire", which speed up
|
||||
* new socket setup when the link is saturated.
|
||||
* Same story for two sockets competing for the medium : if one saturates
|
||||
* the LAP, when the other want to transmit it only has to wait for
|
||||
* maximum three (3) packets (2 + one scheduling), which improve performance
|
||||
* of delay sensitive applications.
|
||||
* Jean II */
|
||||
|
||||
#define NR_EXPECTED 1
|
||||
#define NR_UNEXPECTED 0
|
||||
#define NR_INVALID -1
|
||||
|
||||
#define NS_EXPECTED 1
|
||||
#define NS_UNEXPECTED 0
|
||||
#define NS_INVALID -1
|
||||
|
||||
/*
|
||||
* Meta information passed within the IrLAP state machine
|
||||
*/
|
||||
struct irlap_info {
|
||||
__u8 caddr; /* Connection address */
|
||||
__u8 control; /* Frame type */
|
||||
__u8 cmd;
|
||||
|
||||
__u32 saddr;
|
||||
__u32 daddr;
|
||||
|
||||
int pf; /* Poll/final bit set */
|
||||
|
||||
__u8 nr; /* Sequence number of next frame expected */
|
||||
__u8 ns; /* Sequence number of frame sent */
|
||||
|
||||
int S; /* Number of slots */
|
||||
int slot; /* Random chosen slot */
|
||||
int s; /* Current slot */
|
||||
|
||||
discovery_t *discovery; /* Discovery information */
|
||||
};
|
||||
|
||||
/* Main structure of IrLAP */
|
||||
struct irlap_cb {
|
||||
irda_queue_t q; /* Must be first */
|
||||
magic_t magic;
|
||||
|
||||
/* Device we are attached to */
|
||||
struct net_device *netdev;
|
||||
char hw_name[2*IFNAMSIZ + 1];
|
||||
|
||||
/* Connection state */
|
||||
volatile IRLAP_STATE state; /* Current state */
|
||||
|
||||
/* Timers used by IrLAP */
|
||||
struct timer_list query_timer;
|
||||
struct timer_list slot_timer;
|
||||
struct timer_list discovery_timer;
|
||||
struct timer_list final_timer;
|
||||
struct timer_list poll_timer;
|
||||
struct timer_list wd_timer;
|
||||
struct timer_list backoff_timer;
|
||||
|
||||
/* Media busy stuff */
|
||||
struct timer_list media_busy_timer;
|
||||
int media_busy;
|
||||
|
||||
/* Timeouts which will be different with different turn time */
|
||||
int slot_timeout;
|
||||
int poll_timeout;
|
||||
int final_timeout;
|
||||
int wd_timeout;
|
||||
|
||||
struct sk_buff_head txq; /* Frames to be transmitted */
|
||||
struct sk_buff_head txq_ultra;
|
||||
|
||||
__u8 caddr; /* Connection address */
|
||||
__u32 saddr; /* Source device address */
|
||||
__u32 daddr; /* Destination device address */
|
||||
|
||||
int retry_count; /* Times tried to establish connection */
|
||||
int add_wait; /* True if we are waiting for frame */
|
||||
|
||||
__u8 connect_pending;
|
||||
__u8 disconnect_pending;
|
||||
|
||||
/* To send a faster RR if tx queue empty */
|
||||
#ifdef CONFIG_IRDA_FAST_RR
|
||||
int fast_RR_timeout;
|
||||
int fast_RR;
|
||||
#endif /* CONFIG_IRDA_FAST_RR */
|
||||
|
||||
int N1; /* N1 * F-timer = Negitiated link disconnect warning threshold */
|
||||
int N2; /* N2 * F-timer = Negitiated link disconnect time */
|
||||
int N3; /* Connection retry count */
|
||||
|
||||
int local_busy;
|
||||
int remote_busy;
|
||||
int xmitflag;
|
||||
|
||||
__u8 vs; /* Next frame to be sent */
|
||||
__u8 vr; /* Next frame to be received */
|
||||
__u8 va; /* Last frame acked */
|
||||
int window; /* Nr of I-frames allowed to send */
|
||||
int window_size; /* Current negotiated window size */
|
||||
|
||||
#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
|
||||
__u32 line_capacity; /* Number of bytes allowed to send */
|
||||
__u32 bytes_left; /* Number of bytes still allowed to transmit */
|
||||
#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
|
||||
|
||||
struct sk_buff_head wx_list;
|
||||
|
||||
__u8 ack_required;
|
||||
|
||||
/* XID parameters */
|
||||
__u8 S; /* Number of slots */
|
||||
__u8 slot; /* Random chosen slot */
|
||||
__u8 s; /* Current slot */
|
||||
int frame_sent; /* Have we sent reply? */
|
||||
|
||||
hashbin_t *discovery_log;
|
||||
discovery_t *discovery_cmd;
|
||||
|
||||
__u32 speed; /* Link speed */
|
||||
|
||||
struct qos_info qos_tx; /* QoS requested by peer */
|
||||
struct qos_info qos_rx; /* QoS requested by self */
|
||||
struct qos_info *qos_dev; /* QoS supported by device */
|
||||
|
||||
notify_t notify; /* Callbacks to IrLMP */
|
||||
|
||||
int mtt_required; /* Minimum turnaround time required */
|
||||
int xbofs_delay; /* Nr of XBOF's used to MTT */
|
||||
int bofs_count; /* Negotiated extra BOFs */
|
||||
int next_bofs; /* Negotiated extra BOFs after next frame */
|
||||
|
||||
int mode; /* IrLAP mode (primary, secondary or monitor) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Function prototypes
|
||||
*/
|
||||
int irlap_init(void);
|
||||
void irlap_cleanup(void);
|
||||
|
||||
struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos,
|
||||
const char *hw_name);
|
||||
void irlap_close(struct irlap_cb *self);
|
||||
|
||||
void irlap_connect_request(struct irlap_cb *self, __u32 daddr,
|
||||
struct qos_info *qos, int sniff);
|
||||
void irlap_connect_response(struct irlap_cb *self, struct sk_buff *skb);
|
||||
void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb);
|
||||
void irlap_connect_confirm(struct irlap_cb *, struct sk_buff *skb);
|
||||
|
||||
void irlap_data_indication(struct irlap_cb *, struct sk_buff *, int unreliable);
|
||||
void irlap_data_request(struct irlap_cb *, struct sk_buff *, int unreliable);
|
||||
|
||||
#ifdef CONFIG_IRDA_ULTRA
|
||||
void irlap_unitdata_request(struct irlap_cb *, struct sk_buff *);
|
||||
void irlap_unitdata_indication(struct irlap_cb *, struct sk_buff *);
|
||||
#endif /* CONFIG_IRDA_ULTRA */
|
||||
|
||||
void irlap_disconnect_request(struct irlap_cb *);
|
||||
void irlap_disconnect_indication(struct irlap_cb *, LAP_REASON reason);
|
||||
|
||||
void irlap_status_indication(struct irlap_cb *, int quality_of_link);
|
||||
|
||||
void irlap_test_request(__u8 *info, int len);
|
||||
|
||||
void irlap_discovery_request(struct irlap_cb *, discovery_t *discovery);
|
||||
void irlap_discovery_confirm(struct irlap_cb *, hashbin_t *discovery_log);
|
||||
void irlap_discovery_indication(struct irlap_cb *, discovery_t *discovery);
|
||||
|
||||
void irlap_reset_indication(struct irlap_cb *self);
|
||||
void irlap_reset_confirm(void);
|
||||
|
||||
void irlap_update_nr_received(struct irlap_cb *, int nr);
|
||||
int irlap_validate_nr_received(struct irlap_cb *, int nr);
|
||||
int irlap_validate_ns_received(struct irlap_cb *, int ns);
|
||||
|
||||
int irlap_generate_rand_time_slot(int S, int s);
|
||||
void irlap_initiate_connection_state(struct irlap_cb *);
|
||||
void irlap_flush_all_queues(struct irlap_cb *);
|
||||
void irlap_wait_min_turn_around(struct irlap_cb *, struct qos_info *);
|
||||
|
||||
void irlap_apply_default_connection_parameters(struct irlap_cb *self);
|
||||
void irlap_apply_connection_parameters(struct irlap_cb *self, int now);
|
||||
|
||||
#define IRLAP_GET_HEADER_SIZE(self) (LAP_MAX_HEADER)
|
||||
#define IRLAP_GET_TX_QUEUE_LEN(self) skb_queue_len(&self->txq)
|
||||
|
||||
/* Return TRUE if the node is in primary mode (i.e. master)
|
||||
* - Jean II */
|
||||
static inline int irlap_is_primary(struct irlap_cb *self)
|
||||
{
|
||||
int ret;
|
||||
switch(self->state) {
|
||||
case LAP_XMIT_P:
|
||||
case LAP_NRM_P:
|
||||
ret = 1;
|
||||
break;
|
||||
case LAP_XMIT_S:
|
||||
case LAP_NRM_S:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Clear a pending IrLAP disconnect. - Jean II */
|
||||
static inline void irlap_clear_disconnect(struct irlap_cb *self)
|
||||
{
|
||||
self->disconnect_pending = FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function irlap_next_state (self, state)
|
||||
*
|
||||
* Switches state and provides debug information
|
||||
*
|
||||
*/
|
||||
static inline void irlap_next_state(struct irlap_cb *self, IRLAP_STATE state)
|
||||
{
|
||||
/*
|
||||
if (!self || self->magic != LAP_MAGIC)
|
||||
return;
|
||||
|
||||
pr_debug("next LAP state = %s\n", irlap_state[state]);
|
||||
*/
|
||||
self->state = state;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,129 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
*
|
||||
* Filename: irlap_event.h
|
||||
* Version: 0.1
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sat Aug 16 00:59:29 1997
|
||||
* Modified at: Tue Dec 21 11:20:30 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
|
||||
* All Rights Reserved.
|
||||
* Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRLAP_EVENT_H
|
||||
#define IRLAP_EVENT_H
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
|
||||
/* A few forward declarations (to make compiler happy) */
|
||||
struct irlap_cb;
|
||||
struct irlap_info;
|
||||
|
||||
/* IrLAP States */
|
||||
typedef enum {
|
||||
LAP_NDM, /* Normal disconnected mode */
|
||||
LAP_QUERY,
|
||||
LAP_REPLY,
|
||||
LAP_CONN, /* Connect indication */
|
||||
LAP_SETUP, /* Setting up connection */
|
||||
LAP_OFFLINE, /* A really boring state */
|
||||
LAP_XMIT_P,
|
||||
LAP_PCLOSE,
|
||||
LAP_NRM_P, /* Normal response mode as primary */
|
||||
LAP_RESET_WAIT,
|
||||
LAP_RESET,
|
||||
LAP_NRM_S, /* Normal response mode as secondary */
|
||||
LAP_XMIT_S,
|
||||
LAP_SCLOSE,
|
||||
LAP_RESET_CHECK,
|
||||
} IRLAP_STATE;
|
||||
|
||||
/* IrLAP Events */
|
||||
typedef enum {
|
||||
/* Services events */
|
||||
DISCOVERY_REQUEST,
|
||||
CONNECT_REQUEST,
|
||||
CONNECT_RESPONSE,
|
||||
DISCONNECT_REQUEST,
|
||||
DATA_REQUEST,
|
||||
RESET_REQUEST,
|
||||
RESET_RESPONSE,
|
||||
|
||||
/* Send events */
|
||||
SEND_I_CMD,
|
||||
SEND_UI_FRAME,
|
||||
|
||||
/* Receive events */
|
||||
RECV_DISCOVERY_XID_CMD,
|
||||
RECV_DISCOVERY_XID_RSP,
|
||||
RECV_SNRM_CMD,
|
||||
RECV_TEST_CMD,
|
||||
RECV_TEST_RSP,
|
||||
RECV_UA_RSP,
|
||||
RECV_DM_RSP,
|
||||
RECV_RD_RSP,
|
||||
RECV_I_CMD,
|
||||
RECV_I_RSP,
|
||||
RECV_UI_FRAME,
|
||||
RECV_FRMR_RSP,
|
||||
RECV_RR_CMD,
|
||||
RECV_RR_RSP,
|
||||
RECV_RNR_CMD,
|
||||
RECV_RNR_RSP,
|
||||
RECV_REJ_CMD,
|
||||
RECV_REJ_RSP,
|
||||
RECV_SREJ_CMD,
|
||||
RECV_SREJ_RSP,
|
||||
RECV_DISC_CMD,
|
||||
|
||||
/* Timer events */
|
||||
SLOT_TIMER_EXPIRED,
|
||||
QUERY_TIMER_EXPIRED,
|
||||
FINAL_TIMER_EXPIRED,
|
||||
POLL_TIMER_EXPIRED,
|
||||
DISCOVERY_TIMER_EXPIRED,
|
||||
WD_TIMER_EXPIRED,
|
||||
BACKOFF_TIMER_EXPIRED,
|
||||
MEDIA_BUSY_TIMER_EXPIRED,
|
||||
} IRLAP_EVENT;
|
||||
|
||||
/*
|
||||
* Disconnect reason code
|
||||
*/
|
||||
typedef enum { /* FIXME check the two first reason codes */
|
||||
LAP_DISC_INDICATION=1, /* Received a disconnect request from peer */
|
||||
LAP_NO_RESPONSE, /* To many retransmits without response */
|
||||
LAP_RESET_INDICATION, /* To many retransmits, or invalid nr/ns */
|
||||
LAP_FOUND_NONE, /* No devices were discovered */
|
||||
LAP_MEDIA_BUSY,
|
||||
LAP_PRIMARY_CONFLICT,
|
||||
} LAP_REASON;
|
||||
|
||||
extern const char *const irlap_state[];
|
||||
|
||||
void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event,
|
||||
struct sk_buff *skb, struct irlap_info *info);
|
||||
void irlap_print_event(IRLAP_EVENT event);
|
||||
|
||||
int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb);
|
||||
|
||||
#endif
|
|
@ -1,167 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irlap_frame.h
|
||||
* Version: 0.9
|
||||
* Description: IrLAP frame declarations
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Tue Aug 19 10:27:26 1997
|
||||
* Modified at: Sat Dec 25 21:07:26 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1997-1999 Dag Brattli <dagb@cs.uit.no>,
|
||||
* All Rights Reserved.
|
||||
* Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRLAP_FRAME_H
|
||||
#define IRLAP_FRAME_H
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
|
||||
/* A few forward declarations (to make compiler happy) */
|
||||
struct irlap_cb;
|
||||
struct discovery_t;
|
||||
|
||||
/* Frame types and templates */
|
||||
#define INVALID 0xff
|
||||
|
||||
/* Unnumbered (U) commands */
|
||||
#define SNRM_CMD 0x83 /* Set Normal Response Mode */
|
||||
#define DISC_CMD 0x43 /* Disconnect */
|
||||
#define XID_CMD 0x2f /* Exchange Station Identification */
|
||||
#define TEST_CMD 0xe3 /* Test */
|
||||
|
||||
/* Unnumbered responses */
|
||||
#define RNRM_RSP 0x83 /* Request Normal Response Mode */
|
||||
#define UA_RSP 0x63 /* Unnumbered Acknowledgement */
|
||||
#define FRMR_RSP 0x87 /* Frame Reject */
|
||||
#define DM_RSP 0x0f /* Disconnect Mode */
|
||||
#define RD_RSP 0x43 /* Request Disconnection */
|
||||
#define XID_RSP 0xaf /* Exchange Station Identification */
|
||||
#define TEST_RSP 0xe3 /* Test frame */
|
||||
|
||||
/* Supervisory (S) */
|
||||
#define RR 0x01 /* Receive Ready */
|
||||
#define REJ 0x09 /* Reject */
|
||||
#define RNR 0x05 /* Receive Not Ready */
|
||||
#define SREJ 0x0d /* Selective Reject */
|
||||
|
||||
/* Information (I) */
|
||||
#define I_FRAME 0x00 /* Information Format */
|
||||
#define UI_FRAME 0x03 /* Unnumbered Information */
|
||||
|
||||
#define CMD_FRAME 0x01
|
||||
#define RSP_FRAME 0x00
|
||||
|
||||
#define PF_BIT 0x10 /* Poll/final bit */
|
||||
|
||||
/* Some IrLAP field lengths */
|
||||
/*
|
||||
* Only baud rate triplet is 4 bytes (PV can be 2 bytes).
|
||||
* All others params (7) are 3 bytes, so that's 7*3 + 1*4 bytes.
|
||||
*/
|
||||
#define IRLAP_NEGOCIATION_PARAMS_LEN 25
|
||||
#define IRLAP_DISCOVERY_INFO_LEN 32
|
||||
|
||||
struct disc_frame {
|
||||
__u8 caddr; /* Connection address */
|
||||
__u8 control;
|
||||
} __packed;
|
||||
|
||||
struct xid_frame {
|
||||
__u8 caddr; /* Connection address */
|
||||
__u8 control;
|
||||
__u8 ident; /* Should always be XID_FORMAT */
|
||||
__le32 saddr; /* Source device address */
|
||||
__le32 daddr; /* Destination device address */
|
||||
__u8 flags; /* Discovery flags */
|
||||
__u8 slotnr;
|
||||
__u8 version;
|
||||
} __packed;
|
||||
|
||||
struct test_frame {
|
||||
__u8 caddr; /* Connection address */
|
||||
__u8 control;
|
||||
__le32 saddr; /* Source device address */
|
||||
__le32 daddr; /* Destination device address */
|
||||
} __packed;
|
||||
|
||||
struct ua_frame {
|
||||
__u8 caddr;
|
||||
__u8 control;
|
||||
__le32 saddr; /* Source device address */
|
||||
__le32 daddr; /* Dest device address */
|
||||
} __packed;
|
||||
|
||||
struct dm_frame {
|
||||
__u8 caddr; /* Connection address */
|
||||
__u8 control;
|
||||
} __packed;
|
||||
|
||||
struct rd_frame {
|
||||
__u8 caddr; /* Connection address */
|
||||
__u8 control;
|
||||
} __packed;
|
||||
|
||||
struct rr_frame {
|
||||
__u8 caddr; /* Connection address */
|
||||
__u8 control;
|
||||
} __packed;
|
||||
|
||||
struct i_frame {
|
||||
__u8 caddr;
|
||||
__u8 control;
|
||||
} __packed;
|
||||
|
||||
struct snrm_frame {
|
||||
__u8 caddr;
|
||||
__u8 control;
|
||||
__le32 saddr;
|
||||
__le32 daddr;
|
||||
__u8 ncaddr;
|
||||
} __packed;
|
||||
|
||||
void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb);
|
||||
void irlap_send_discovery_xid_frame(struct irlap_cb *, int S, __u8 s,
|
||||
__u8 command,
|
||||
struct discovery_t *discovery);
|
||||
void irlap_send_snrm_frame(struct irlap_cb *, struct qos_info *);
|
||||
void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr,
|
||||
struct sk_buff *cmd);
|
||||
void irlap_send_ua_response_frame(struct irlap_cb *, struct qos_info *);
|
||||
void irlap_send_dm_frame(struct irlap_cb *self);
|
||||
void irlap_send_rd_frame(struct irlap_cb *self);
|
||||
void irlap_send_disc_frame(struct irlap_cb *self);
|
||||
void irlap_send_rr_frame(struct irlap_cb *self, int command);
|
||||
|
||||
void irlap_send_data_primary(struct irlap_cb *, struct sk_buff *);
|
||||
void irlap_send_data_primary_poll(struct irlap_cb *, struct sk_buff *);
|
||||
void irlap_send_data_secondary(struct irlap_cb *, struct sk_buff *);
|
||||
void irlap_send_data_secondary_final(struct irlap_cb *, struct sk_buff *);
|
||||
void irlap_resend_rejected_frames(struct irlap_cb *, int command);
|
||||
void irlap_resend_rejected_frame(struct irlap_cb *self, int command);
|
||||
|
||||
void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb,
|
||||
__u8 caddr, int command);
|
||||
|
||||
int irlap_insert_qos_negotiation_params(struct irlap_cb *self,
|
||||
struct sk_buff *skb);
|
||||
|
||||
#endif
|
|
@ -1,295 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irlmp.h
|
||||
* Version: 0.9
|
||||
* Description: IrDA Link Management Protocol (LMP) layer
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sun Aug 17 20:54:32 1997
|
||||
* Modified at: Fri Dec 10 13:23:01 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
|
||||
* All Rights Reserved.
|
||||
* Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRLMP_H
|
||||
#define IRLMP_H
|
||||
|
||||
#include <asm/param.h> /* for HZ */
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/qos.h>
|
||||
#include <net/irda/irlap.h> /* LAP_MAX_HEADER, ... */
|
||||
#include <net/irda/irlmp_event.h>
|
||||
#include <net/irda/irqueue.h>
|
||||
#include <net/irda/discovery.h>
|
||||
|
||||
/* LSAP-SEL's */
|
||||
#define LSAP_MASK 0x7f
|
||||
#define LSAP_IAS 0x00
|
||||
#define LSAP_ANY 0xff
|
||||
#define LSAP_MAX 0x6f /* 0x70-0x7f are reserved */
|
||||
#define LSAP_CONNLESS 0x70 /* Connectionless LSAP, mostly used for Ultra */
|
||||
|
||||
#define DEV_ADDR_ANY 0xffffffff
|
||||
|
||||
#define LMP_HEADER 2 /* Dest LSAP + Source LSAP */
|
||||
#define LMP_CONTROL_HEADER 4 /* LMP_HEADER + opcode + parameter */
|
||||
#define LMP_PID_HEADER 1 /* Used by Ultra */
|
||||
#define LMP_MAX_HEADER (LMP_CONTROL_HEADER+LAP_MAX_HEADER)
|
||||
|
||||
#define LM_MAX_CONNECTIONS 10
|
||||
|
||||
#define LM_IDLE_TIMEOUT 2*HZ /* 2 seconds for now */
|
||||
|
||||
typedef enum {
|
||||
S_PNP = 0,
|
||||
S_PDA,
|
||||
S_COMPUTER,
|
||||
S_PRINTER,
|
||||
S_MODEM,
|
||||
S_FAX,
|
||||
S_LAN,
|
||||
S_TELEPHONY,
|
||||
S_COMM,
|
||||
S_OBEX,
|
||||
S_ANY,
|
||||
S_END,
|
||||
} SERVICE;
|
||||
|
||||
/* For selective discovery */
|
||||
typedef void (*DISCOVERY_CALLBACK1) (discinfo_t *, DISCOVERY_MODE, void *);
|
||||
/* For expiry (the same) */
|
||||
typedef void (*DISCOVERY_CALLBACK2) (discinfo_t *, DISCOVERY_MODE, void *);
|
||||
|
||||
typedef struct {
|
||||
irda_queue_t queue; /* Must be first */
|
||||
|
||||
__u16_host_order hints; /* Hint bits */
|
||||
} irlmp_service_t;
|
||||
|
||||
typedef struct {
|
||||
irda_queue_t queue; /* Must be first */
|
||||
|
||||
__u16_host_order hint_mask;
|
||||
|
||||
DISCOVERY_CALLBACK1 disco_callback; /* Selective discovery */
|
||||
DISCOVERY_CALLBACK2 expir_callback; /* Selective expiration */
|
||||
void *priv; /* Used to identify client */
|
||||
} irlmp_client_t;
|
||||
|
||||
/*
|
||||
* Information about each logical LSAP connection
|
||||
*/
|
||||
struct lsap_cb {
|
||||
irda_queue_t queue; /* Must be first */
|
||||
magic_t magic;
|
||||
|
||||
unsigned long connected; /* set_bit used on this */
|
||||
int persistent;
|
||||
|
||||
__u8 slsap_sel; /* Source (this) LSAP address */
|
||||
__u8 dlsap_sel; /* Destination LSAP address (if connected) */
|
||||
#ifdef CONFIG_IRDA_ULTRA
|
||||
__u8 pid; /* Used by connectionless LSAP */
|
||||
#endif /* CONFIG_IRDA_ULTRA */
|
||||
struct sk_buff *conn_skb; /* Store skb here while connecting */
|
||||
|
||||
struct timer_list watchdog_timer;
|
||||
|
||||
LSAP_STATE lsap_state; /* Connection state */
|
||||
notify_t notify; /* Indication/Confirm entry points */
|
||||
struct qos_info qos; /* QoS for this connection */
|
||||
|
||||
struct lap_cb *lap; /* Pointer to LAP connection structure */
|
||||
};
|
||||
|
||||
/*
|
||||
* Used for caching the last slsap->dlsap->handle mapping
|
||||
*
|
||||
* We don't need to keep/match the remote address in the cache because
|
||||
* we are associated with a specific LAP (which implies it).
|
||||
* Jean II
|
||||
*/
|
||||
typedef struct {
|
||||
int valid;
|
||||
|
||||
__u8 slsap_sel;
|
||||
__u8 dlsap_sel;
|
||||
struct lsap_cb *lsap;
|
||||
} CACHE_ENTRY;
|
||||
|
||||
/*
|
||||
* Information about each registered IrLAP layer
|
||||
*/
|
||||
struct lap_cb {
|
||||
irda_queue_t queue; /* Must be first */
|
||||
magic_t magic;
|
||||
|
||||
int reason; /* LAP disconnect reason */
|
||||
|
||||
IRLMP_STATE lap_state;
|
||||
|
||||
struct irlap_cb *irlap; /* Instance of IrLAP layer */
|
||||
hashbin_t *lsaps; /* LSAP associated with this link */
|
||||
struct lsap_cb *flow_next; /* Next lsap to be polled for Tx */
|
||||
|
||||
__u8 caddr; /* Connection address */
|
||||
__u32 saddr; /* Source device address */
|
||||
__u32 daddr; /* Destination device address */
|
||||
|
||||
struct qos_info *qos; /* LAP QoS for this session */
|
||||
struct timer_list idle_timer;
|
||||
|
||||
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
|
||||
/* The lsap cache was moved from struct irlmp_cb to here because
|
||||
* it must be associated with the specific LAP. Also, this
|
||||
* improves performance. - Jean II */
|
||||
CACHE_ENTRY cache; /* Caching last slsap->dlsap->handle mapping */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Main structure for IrLMP
|
||||
*/
|
||||
struct irlmp_cb {
|
||||
magic_t magic;
|
||||
|
||||
__u8 conflict_flag;
|
||||
|
||||
discovery_t discovery_cmd; /* Discovery command to use by IrLAP */
|
||||
discovery_t discovery_rsp; /* Discovery response to use by IrLAP */
|
||||
|
||||
/* Last lsap picked automatically by irlmp_find_free_slsap() */
|
||||
int last_lsap_sel;
|
||||
|
||||
struct timer_list discovery_timer;
|
||||
|
||||
hashbin_t *links; /* IrLAP connection table */
|
||||
hashbin_t *unconnected_lsaps;
|
||||
hashbin_t *clients;
|
||||
hashbin_t *services;
|
||||
|
||||
hashbin_t *cachelog; /* Current discovery log */
|
||||
|
||||
int running;
|
||||
|
||||
__u16_host_order hints; /* Hint bits */
|
||||
};
|
||||
|
||||
/* Prototype declarations */
|
||||
int irlmp_init(void);
|
||||
void irlmp_cleanup(void);
|
||||
struct lsap_cb *irlmp_open_lsap(__u8 slsap, notify_t *notify, __u8 pid);
|
||||
void irlmp_close_lsap( struct lsap_cb *self);
|
||||
|
||||
__u16 irlmp_service_to_hint(int service);
|
||||
void *irlmp_register_service(__u16 hints);
|
||||
int irlmp_unregister_service(void *handle);
|
||||
void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb,
|
||||
DISCOVERY_CALLBACK2 expir_clb, void *priv);
|
||||
int irlmp_unregister_client(void *handle);
|
||||
int irlmp_update_client(void *handle, __u16 hint_mask,
|
||||
DISCOVERY_CALLBACK1 disco_clb,
|
||||
DISCOVERY_CALLBACK2 expir_clb, void *priv);
|
||||
|
||||
void irlmp_register_link(struct irlap_cb *, __u32 saddr, notify_t *);
|
||||
void irlmp_unregister_link(__u32 saddr);
|
||||
|
||||
int irlmp_connect_request(struct lsap_cb *, __u8 dlsap_sel,
|
||||
__u32 saddr, __u32 daddr,
|
||||
struct qos_info *, struct sk_buff *);
|
||||
void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb);
|
||||
int irlmp_connect_response(struct lsap_cb *, struct sk_buff *);
|
||||
void irlmp_connect_confirm(struct lsap_cb *, struct sk_buff *);
|
||||
struct lsap_cb *irlmp_dup(struct lsap_cb *self, void *instance);
|
||||
|
||||
void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason,
|
||||
struct sk_buff *userdata);
|
||||
int irlmp_disconnect_request(struct lsap_cb *, struct sk_buff *userdata);
|
||||
|
||||
void irlmp_discovery_confirm(hashbin_t *discovery_log, DISCOVERY_MODE mode);
|
||||
void irlmp_discovery_request(int nslots);
|
||||
discinfo_t *irlmp_get_discoveries(int *pn, __u16 mask, int nslots);
|
||||
void irlmp_do_expiry(void);
|
||||
void irlmp_do_discovery(int nslots);
|
||||
discovery_t *irlmp_get_discovery_response(void);
|
||||
void irlmp_discovery_expiry(discinfo_t *expiry, int number);
|
||||
|
||||
int irlmp_data_request(struct lsap_cb *, struct sk_buff *);
|
||||
void irlmp_data_indication(struct lsap_cb *, struct sk_buff *);
|
||||
|
||||
int irlmp_udata_request(struct lsap_cb *, struct sk_buff *);
|
||||
void irlmp_udata_indication(struct lsap_cb *, struct sk_buff *);
|
||||
|
||||
#ifdef CONFIG_IRDA_ULTRA
|
||||
int irlmp_connless_data_request(struct lsap_cb *, struct sk_buff *, __u8);
|
||||
void irlmp_connless_data_indication(struct lsap_cb *, struct sk_buff *);
|
||||
#endif /* CONFIG_IRDA_ULTRA */
|
||||
|
||||
void irlmp_status_indication(struct lap_cb *, LINK_STATUS link, LOCK_STATUS lock);
|
||||
void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow);
|
||||
|
||||
LM_REASON irlmp_convert_lap_reason(LAP_REASON);
|
||||
|
||||
static inline __u32 irlmp_get_saddr(const struct lsap_cb *self)
|
||||
{
|
||||
return (self && self->lap) ? self->lap->saddr : 0;
|
||||
}
|
||||
|
||||
static inline __u32 irlmp_get_daddr(const struct lsap_cb *self)
|
||||
{
|
||||
return (self && self->lap) ? self->lap->daddr : 0;
|
||||
}
|
||||
|
||||
const char *irlmp_reason_str(LM_REASON reason);
|
||||
|
||||
extern int sysctl_discovery_timeout;
|
||||
extern int sysctl_discovery_slots;
|
||||
extern int sysctl_discovery;
|
||||
extern int sysctl_lap_keepalive_time; /* in ms, default is LM_IDLE_TIMEOUT */
|
||||
extern struct irlmp_cb *irlmp;
|
||||
|
||||
/* Check if LAP queue is full.
|
||||
* Used by IrTTP for low control, see comments in irlap.h - Jean II */
|
||||
static inline int irlmp_lap_tx_queue_full(struct lsap_cb *self)
|
||||
{
|
||||
if (self == NULL)
|
||||
return 0;
|
||||
if (self->lap == NULL)
|
||||
return 0;
|
||||
if (self->lap->irlap == NULL)
|
||||
return 0;
|
||||
|
||||
return IRLAP_GET_TX_QUEUE_LEN(self->lap->irlap) >= LAP_HIGH_THRESHOLD;
|
||||
}
|
||||
|
||||
/* After doing a irlmp_dup(), this get one of the two socket back into
|
||||
* a state where it's waiting incoming connections.
|
||||
* Note : this can be used *only* if the socket is not yet connected
|
||||
* (i.e. NO irlmp_connect_response() done on this socket).
|
||||
* - Jean II */
|
||||
static inline void irlmp_listen(struct lsap_cb *self)
|
||||
{
|
||||
self->dlsap_sel = LSAP_ANY;
|
||||
self->lap = NULL;
|
||||
self->lsap_state = LSAP_DISCONNECTED;
|
||||
/* Started when we received the LM_CONNECT_INDICATION */
|
||||
del_timer(&self->watchdog_timer);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,98 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irlmp_event.h
|
||||
* Version: 0.1
|
||||
* Description: IrDA-LMP event handling
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Mon Aug 4 20:40:53 1997
|
||||
* Modified at: Thu Jul 8 12:18:54 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>,
|
||||
* All Rights Reserved.
|
||||
* Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRLMP_EVENT_H
|
||||
#define IRLMP_EVENT_H
|
||||
|
||||
/* A few forward declarations (to make compiler happy) */
|
||||
struct irlmp_cb;
|
||||
struct lsap_cb;
|
||||
struct lap_cb;
|
||||
struct discovery_t;
|
||||
|
||||
/* LAP states */
|
||||
typedef enum {
|
||||
/* IrLAP connection control states */
|
||||
LAP_STANDBY, /* No LAP connection */
|
||||
LAP_U_CONNECT, /* Starting LAP connection */
|
||||
LAP_ACTIVE, /* LAP connection is active */
|
||||
} IRLMP_STATE;
|
||||
|
||||
/* LSAP connection control states */
|
||||
typedef enum {
|
||||
LSAP_DISCONNECTED, /* No LSAP connection */
|
||||
LSAP_CONNECT, /* Connect indication from peer */
|
||||
LSAP_CONNECT_PEND, /* Connect request from service user */
|
||||
LSAP_DATA_TRANSFER_READY, /* LSAP connection established */
|
||||
LSAP_SETUP, /* Trying to set up LSAP connection */
|
||||
LSAP_SETUP_PEND, /* Request to start LAP connection */
|
||||
} LSAP_STATE;
|
||||
|
||||
typedef enum {
|
||||
/* LSAP events */
|
||||
LM_CONNECT_REQUEST,
|
||||
LM_CONNECT_CONFIRM,
|
||||
LM_CONNECT_RESPONSE,
|
||||
LM_CONNECT_INDICATION,
|
||||
|
||||
LM_DISCONNECT_INDICATION,
|
||||
LM_DISCONNECT_REQUEST,
|
||||
|
||||
LM_DATA_REQUEST,
|
||||
LM_UDATA_REQUEST,
|
||||
LM_DATA_INDICATION,
|
||||
LM_UDATA_INDICATION,
|
||||
|
||||
LM_WATCHDOG_TIMEOUT,
|
||||
|
||||
/* IrLAP events */
|
||||
LM_LAP_CONNECT_REQUEST,
|
||||
LM_LAP_CONNECT_INDICATION,
|
||||
LM_LAP_CONNECT_CONFIRM,
|
||||
LM_LAP_DISCONNECT_INDICATION,
|
||||
LM_LAP_DISCONNECT_REQUEST,
|
||||
LM_LAP_DISCOVERY_REQUEST,
|
||||
LM_LAP_DISCOVERY_CONFIRM,
|
||||
LM_LAP_IDLE_TIMEOUT,
|
||||
} IRLMP_EVENT;
|
||||
|
||||
extern const char *const irlmp_state[];
|
||||
extern const char *const irlsap_state[];
|
||||
|
||||
void irlmp_watchdog_timer_expired(struct timer_list *t);
|
||||
void irlmp_discovery_timer_expired(struct timer_list *t);
|
||||
void irlmp_idle_timer_expired(struct timer_list *t);
|
||||
|
||||
void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event,
|
||||
struct sk_buff *skb);
|
||||
int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event,
|
||||
struct sk_buff *skb);
|
||||
|
||||
#endif /* IRLMP_EVENT_H */
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irlmp_frame.h
|
||||
* Version: 0.9
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Tue Aug 19 02:09:59 1997
|
||||
* Modified at: Fri Dec 10 13:21:53 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>,
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRMLP_FRAME_H
|
||||
#define IRMLP_FRAME_H
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <net/irda/discovery.h>
|
||||
|
||||
/* IrLMP frame opcodes */
|
||||
#define CONNECT_CMD 0x01
|
||||
#define CONNECT_CNF 0x81
|
||||
#define DISCONNECT 0x02
|
||||
#define ACCESSMODE_CMD 0x03
|
||||
#define ACCESSMODE_CNF 0x83
|
||||
|
||||
#define CONTROL_BIT 0x80
|
||||
|
||||
void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
|
||||
int expedited, struct sk_buff *skb);
|
||||
void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
|
||||
__u8 opcode, struct sk_buff *skb);
|
||||
void irlmp_link_data_indication(struct lap_cb *, struct sk_buff *,
|
||||
int unreliable);
|
||||
#ifdef CONFIG_IRDA_ULTRA
|
||||
void irlmp_link_unitdata_indication(struct lap_cb *, struct sk_buff *);
|
||||
#endif /* CONFIG_IRDA_ULTRA */
|
||||
|
||||
void irlmp_link_connect_indication(struct lap_cb *, __u32 saddr, __u32 daddr,
|
||||
struct qos_info *qos, struct sk_buff *skb);
|
||||
void irlmp_link_connect_request(__u32 daddr);
|
||||
void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos,
|
||||
struct sk_buff *skb);
|
||||
void irlmp_link_disconnect_indication(struct lap_cb *, struct irlap_cb *,
|
||||
LAP_REASON reason, struct sk_buff *);
|
||||
void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log);
|
||||
void irlmp_link_discovery_indication(struct lap_cb *, discovery_t *discovery);
|
||||
|
||||
#endif
|
|
@ -1,109 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irmod.h
|
||||
* Version: 0.3
|
||||
* Description: IrDA module and utilities functions
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Mon Dec 15 13:58:52 1997
|
||||
* Modified at: Fri Jan 28 13:15:24 2000
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved.
|
||||
* Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charg.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRMOD_H
|
||||
#define IRMOD_H
|
||||
|
||||
/* Misc status information */
|
||||
typedef enum {
|
||||
STATUS_OK,
|
||||
STATUS_ABORTED,
|
||||
STATUS_NO_ACTIVITY,
|
||||
STATUS_NOISY,
|
||||
STATUS_REMOTE,
|
||||
} LINK_STATUS;
|
||||
|
||||
typedef enum {
|
||||
LOCK_NO_CHANGE,
|
||||
LOCK_LOCKED,
|
||||
LOCK_UNLOCKED,
|
||||
} LOCK_STATUS;
|
||||
|
||||
typedef enum { FLOW_STOP, FLOW_START } LOCAL_FLOW;
|
||||
|
||||
/*
|
||||
* IrLMP disconnect reasons. The order is very important, since they
|
||||
* correspond to disconnect reasons sent in IrLMP disconnect frames, so
|
||||
* please do not touch :-)
|
||||
*/
|
||||
typedef enum {
|
||||
LM_USER_REQUEST = 1, /* User request */
|
||||
LM_LAP_DISCONNECT, /* Unexpected IrLAP disconnect */
|
||||
LM_CONNECT_FAILURE, /* Failed to establish IrLAP connection */
|
||||
LM_LAP_RESET, /* IrLAP reset */
|
||||
LM_INIT_DISCONNECT, /* Link Management initiated disconnect */
|
||||
LM_LSAP_NOTCONN, /* Data delivered on unconnected LSAP */
|
||||
LM_NON_RESP_CLIENT, /* Non responsive LM-MUX client */
|
||||
LM_NO_AVAIL_CLIENT, /* No available LM-MUX client */
|
||||
LM_CONN_HALF_OPEN, /* Connection is half open */
|
||||
LM_BAD_SOURCE_ADDR, /* Illegal source address (i.e 0x00) */
|
||||
} LM_REASON;
|
||||
#define LM_UNKNOWN 0xff /* Unspecified disconnect reason */
|
||||
|
||||
/* A few forward declarations (to make compiler happy) */
|
||||
struct qos_info; /* in <net/irda/qos.h> */
|
||||
|
||||
/*
|
||||
* Notify structure used between transport and link management layers
|
||||
*/
|
||||
typedef struct {
|
||||
int (*data_indication)(void *priv, void *sap, struct sk_buff *skb);
|
||||
int (*udata_indication)(void *priv, void *sap, struct sk_buff *skb);
|
||||
void (*connect_confirm)(void *instance, void *sap,
|
||||
struct qos_info *qos, __u32 max_sdu_size,
|
||||
__u8 max_header_size, struct sk_buff *skb);
|
||||
void (*connect_indication)(void *instance, void *sap,
|
||||
struct qos_info *qos, __u32 max_sdu_size,
|
||||
__u8 max_header_size, struct sk_buff *skb);
|
||||
void (*disconnect_indication)(void *instance, void *sap,
|
||||
LM_REASON reason, struct sk_buff *);
|
||||
void (*flow_indication)(void *instance, void *sap, LOCAL_FLOW flow);
|
||||
void (*status_indication)(void *instance,
|
||||
LINK_STATUS link, LOCK_STATUS lock);
|
||||
void *instance; /* Layer instance pointer */
|
||||
char name[16]; /* Name of layer */
|
||||
} notify_t;
|
||||
|
||||
#define NOTIFY_MAX_NAME 16
|
||||
|
||||
/* Zero the notify structure */
|
||||
void irda_notify_init(notify_t *notify);
|
||||
|
||||
/* Locking wrapper - Note the inverted logic on irda_lock().
|
||||
* Those function basically return false if the lock is already in the
|
||||
* position you want to set it. - Jean II */
|
||||
#define irda_lock(lock) (! test_and_set_bit(0, (void *) (lock)))
|
||||
#define irda_unlock(lock) (test_and_clear_bit(0, (void *) (lock)))
|
||||
|
||||
#endif /* IRMOD_H */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irqueue.h
|
||||
* Version: 0.3
|
||||
* Description: General queue implementation
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Tue Jun 9 13:26:50 1998
|
||||
* Modified at: Thu Oct 7 13:25:16 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (C) 1998-1999, Aage Kvalnes <aage@cs.uit.no>
|
||||
* Copyright (c) 1998, Dag Brattli
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This code is taken from the Vortex Operating System written by Aage
|
||||
* Kvalnes and has been ported to Linux and Linux/IR by Dag Brattli
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#ifndef IRDA_QUEUE_H
|
||||
#define IRDA_QUEUE_H
|
||||
|
||||
#define NAME_SIZE 32
|
||||
|
||||
/*
|
||||
* Hash types (some flags can be xored)
|
||||
* See comments in irqueue.c for which one to use...
|
||||
*/
|
||||
#define HB_NOLOCK 0 /* No concurent access prevention */
|
||||
#define HB_LOCK 1 /* Prevent concurent write with global lock */
|
||||
|
||||
/*
|
||||
* Hash defines
|
||||
*/
|
||||
#define HASHBIN_SIZE 8
|
||||
#define HASHBIN_MASK 0x7
|
||||
|
||||
#ifndef IRDA_ALIGN
|
||||
#define IRDA_ALIGN __attribute__((aligned))
|
||||
#endif
|
||||
|
||||
#define Q_NULL { NULL, NULL, "", 0 }
|
||||
|
||||
typedef void (*FREE_FUNC)(void *arg);
|
||||
|
||||
struct irda_queue {
|
||||
struct irda_queue *q_next;
|
||||
struct irda_queue *q_prev;
|
||||
|
||||
char q_name[NAME_SIZE];
|
||||
long q_hash; /* Must be able to cast a (void *) */
|
||||
};
|
||||
typedef struct irda_queue irda_queue_t;
|
||||
|
||||
typedef struct hashbin_t {
|
||||
__u32 magic;
|
||||
int hb_type;
|
||||
int hb_size;
|
||||
spinlock_t hb_spinlock; /* HB_LOCK - Can be used by the user */
|
||||
|
||||
irda_queue_t* hb_queue[HASHBIN_SIZE] IRDA_ALIGN;
|
||||
|
||||
irda_queue_t* hb_current;
|
||||
} hashbin_t;
|
||||
|
||||
hashbin_t *hashbin_new(int type);
|
||||
int hashbin_delete(hashbin_t* hashbin, FREE_FUNC func);
|
||||
int hashbin_clear(hashbin_t* hashbin, FREE_FUNC free_func);
|
||||
void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv,
|
||||
const char* name);
|
||||
void* hashbin_remove(hashbin_t* hashbin, long hashv, const char* name);
|
||||
void* hashbin_remove_first(hashbin_t *hashbin);
|
||||
void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry);
|
||||
void* hashbin_find(hashbin_t* hashbin, long hashv, const char* name);
|
||||
void* hashbin_lock_find(hashbin_t* hashbin, long hashv, const char* name);
|
||||
void* hashbin_find_next(hashbin_t* hashbin, long hashv, const char* name,
|
||||
void ** pnext);
|
||||
irda_queue_t *hashbin_get_first(hashbin_t *hashbin);
|
||||
irda_queue_t *hashbin_get_next(hashbin_t *hashbin);
|
||||
|
||||
#define HASHBIN_GET_SIZE(hashbin) hashbin->hb_size
|
||||
|
||||
#endif
|
|
@ -1,210 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irttp.h
|
||||
* Version: 1.0
|
||||
* Description: Tiny Transport Protocol (TTP) definitions
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sun Aug 31 20:14:31 1997
|
||||
* Modified at: Sun Dec 12 13:09:07 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
|
||||
* All Rights Reserved.
|
||||
* Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRTTP_H
|
||||
#define IRTTP_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irlmp.h> /* struct lsap_cb */
|
||||
#include <net/irda/qos.h> /* struct qos_info */
|
||||
#include <net/irda/irqueue.h>
|
||||
|
||||
#define TTP_MAX_CONNECTIONS LM_MAX_CONNECTIONS
|
||||
#define TTP_HEADER 1
|
||||
#define TTP_MAX_HEADER (TTP_HEADER + LMP_MAX_HEADER)
|
||||
#define TTP_SAR_HEADER 5
|
||||
#define TTP_PARAMETERS 0x80
|
||||
#define TTP_MORE 0x80
|
||||
|
||||
/* Transmission queue sizes */
|
||||
/* Worst case scenario, two window of data - Jean II */
|
||||
#define TTP_TX_MAX_QUEUE 14
|
||||
/* We need to keep at least 5 frames to make sure that we can refill
|
||||
* appropriately the LAP layer. LAP keeps only two buffers, and we need
|
||||
* to have 7 to make a full window - Jean II */
|
||||
#define TTP_TX_LOW_THRESHOLD 5
|
||||
/* Most clients are synchronous with respect to flow control, so we can
|
||||
* keep a low number of Tx buffers in TTP - Jean II */
|
||||
#define TTP_TX_HIGH_THRESHOLD 7
|
||||
|
||||
/* Receive queue sizes */
|
||||
/* Minimum of credit that the peer should hold.
|
||||
* If the peer has less credits than 9 frames, we will explicitly send
|
||||
* him some credits (through irttp_give_credit() and a specific frame).
|
||||
* Note that when we give credits it's likely that it won't be sent in
|
||||
* this LAP window, but in the next one. So, we make sure that the peer
|
||||
* has something to send while waiting for credits (one LAP window == 7
|
||||
* + 1 frames while he process the credits). - Jean II */
|
||||
#define TTP_RX_MIN_CREDIT 8
|
||||
/* This is the default maximum number of credits held by the peer, so the
|
||||
* default maximum number of frames he can send us before needing flow
|
||||
* control answer from us (this may be negociated differently at TSAP setup).
|
||||
* We want to minimise the number of times we have to explicitly send some
|
||||
* credit to the peer, hoping we can piggyback it on the return data. In
|
||||
* particular, it doesn't make sense for us to send credit more than once
|
||||
* per LAP window.
|
||||
* Moreover, giving credits has some latency, so we need strictly more than
|
||||
* a LAP window, otherwise we may already have credits in our Tx queue.
|
||||
* But on the other hand, we don't want to keep too many Rx buffer here
|
||||
* before starting to flow control the other end, so make it exactly one
|
||||
* LAP window + 1 + MIN_CREDITS. - Jean II */
|
||||
#define TTP_RX_DEFAULT_CREDIT 16
|
||||
/* Maximum number of credits we can allow the peer to have, and therefore
|
||||
* maximum Rx queue size.
|
||||
* Note that we try to deliver packets to the higher layer every time we
|
||||
* receive something, so in normal mode the Rx queue will never contains
|
||||
* more than one or two packets. - Jean II */
|
||||
#define TTP_RX_MAX_CREDIT 21
|
||||
|
||||
/* What clients should use when calling ttp_open_tsap() */
|
||||
#define DEFAULT_INITIAL_CREDIT TTP_RX_DEFAULT_CREDIT
|
||||
|
||||
/* Some priorities for disconnect requests */
|
||||
#define P_NORMAL 0
|
||||
#define P_HIGH 1
|
||||
|
||||
#define TTP_SAR_DISABLE 0
|
||||
#define TTP_SAR_UNBOUND 0xffffffff
|
||||
|
||||
/* Parameters */
|
||||
#define TTP_MAX_SDU_SIZE 0x01
|
||||
|
||||
/*
|
||||
* This structure contains all data associated with one instance of a TTP
|
||||
* connection.
|
||||
*/
|
||||
struct tsap_cb {
|
||||
irda_queue_t q; /* Must be first */
|
||||
magic_t magic; /* Just in case */
|
||||
|
||||
__u8 stsap_sel; /* Source TSAP */
|
||||
__u8 dtsap_sel; /* Destination TSAP */
|
||||
|
||||
struct lsap_cb *lsap; /* Corresponding LSAP to this TSAP */
|
||||
|
||||
__u8 connected; /* TSAP connected */
|
||||
|
||||
__u8 initial_credit; /* Initial credit to give peer */
|
||||
|
||||
int avail_credit; /* Available credit to return to peer */
|
||||
int remote_credit; /* Credit held by peer TTP entity */
|
||||
int send_credit; /* Credit held by local TTP entity */
|
||||
|
||||
struct sk_buff_head tx_queue; /* Frames to be transmitted */
|
||||
struct sk_buff_head rx_queue; /* Received frames */
|
||||
struct sk_buff_head rx_fragments;
|
||||
int tx_queue_lock;
|
||||
int rx_queue_lock;
|
||||
spinlock_t lock;
|
||||
|
||||
notify_t notify; /* Callbacks to client layer */
|
||||
|
||||
struct net_device_stats stats;
|
||||
struct timer_list todo_timer;
|
||||
|
||||
__u32 max_seg_size; /* Max data that fit into an IrLAP frame */
|
||||
__u8 max_header_size;
|
||||
|
||||
int rx_sdu_busy; /* RxSdu.busy */
|
||||
__u32 rx_sdu_size; /* Current size of a partially received frame */
|
||||
__u32 rx_max_sdu_size; /* Max receive user data size */
|
||||
|
||||
int tx_sdu_busy; /* TxSdu.busy */
|
||||
__u32 tx_max_sdu_size; /* Max transmit user data size */
|
||||
|
||||
int close_pend; /* Close, but disconnect_pend */
|
||||
unsigned long disconnect_pend; /* Disconnect, but still data to send */
|
||||
struct sk_buff *disconnect_skb;
|
||||
};
|
||||
|
||||
struct irttp_cb {
|
||||
magic_t magic;
|
||||
hashbin_t *tsaps;
|
||||
};
|
||||
|
||||
int irttp_init(void);
|
||||
void irttp_cleanup(void);
|
||||
|
||||
struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify);
|
||||
int irttp_close_tsap(struct tsap_cb *self);
|
||||
|
||||
int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb);
|
||||
int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb);
|
||||
|
||||
int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel,
|
||||
__u32 saddr, __u32 daddr,
|
||||
struct qos_info *qos, __u32 max_sdu_size,
|
||||
struct sk_buff *userdata);
|
||||
int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size,
|
||||
struct sk_buff *userdata);
|
||||
int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *skb,
|
||||
int priority);
|
||||
void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow);
|
||||
struct tsap_cb *irttp_dup(struct tsap_cb *self, void *instance);
|
||||
|
||||
static inline __u32 irttp_get_saddr(struct tsap_cb *self)
|
||||
{
|
||||
return irlmp_get_saddr(self->lsap);
|
||||
}
|
||||
|
||||
static inline __u32 irttp_get_daddr(struct tsap_cb *self)
|
||||
{
|
||||
return irlmp_get_daddr(self->lsap);
|
||||
}
|
||||
|
||||
static inline __u32 irttp_get_max_seg_size(struct tsap_cb *self)
|
||||
{
|
||||
return self->max_seg_size;
|
||||
}
|
||||
|
||||
/* After doing a irttp_dup(), this get one of the two socket back into
|
||||
* a state where it's waiting incoming connections.
|
||||
* Note : this can be used *only* if the socket is not yet connected
|
||||
* (i.e. NO irttp_connect_response() done on this socket).
|
||||
* - Jean II */
|
||||
static inline void irttp_listen(struct tsap_cb *self)
|
||||
{
|
||||
irlmp_listen(self->lsap);
|
||||
self->dtsap_sel = LSAP_ANY;
|
||||
}
|
||||
|
||||
/* Return TRUE if the node is in primary mode (i.e. master)
|
||||
* - Jean II */
|
||||
static inline int irttp_is_primary(struct tsap_cb *self)
|
||||
{
|
||||
if ((self == NULL) ||
|
||||
(self->lsap == NULL) ||
|
||||
(self->lsap->lap == NULL) ||
|
||||
(self->lsap->lap->irlap == NULL))
|
||||
return -2;
|
||||
return irlap_is_primary(self->lsap->lap->irlap);
|
||||
}
|
||||
|
||||
#endif /* IRTTP_H */
|
|
@ -1,100 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: parameters.h
|
||||
* Version: 1.0
|
||||
* Description: A more general way to handle (pi,pl,pv) parameters
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Mon Jun 7 08:47:28 1999
|
||||
* Modified at: Sun Jan 30 14:05:14 2000
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Michel Dänzer <daenzer@debian.org>, 10/2001
|
||||
* - simplify irda_pv_t to avoid endianness issues
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRDA_PARAMS_H
|
||||
#define IRDA_PARAMS_H
|
||||
|
||||
/*
|
||||
* The currently supported types. Beware not to change the sequence since
|
||||
* it a good reason why the sized integers has a value equal to their size
|
||||
*/
|
||||
typedef enum {
|
||||
PV_INTEGER, /* Integer of any (pl) length */
|
||||
PV_INT_8_BITS, /* Integer of 8 bits in length */
|
||||
PV_INT_16_BITS, /* Integer of 16 bits in length */
|
||||
PV_STRING, /* \0 terminated string */
|
||||
PV_INT_32_BITS, /* Integer of 32 bits in length */
|
||||
PV_OCT_SEQ, /* Octet sequence */
|
||||
PV_NO_VALUE /* Does not contain any value (pl=0) */
|
||||
} PV_TYPE;
|
||||
|
||||
/* Bit 7 of type field */
|
||||
#define PV_BIG_ENDIAN 0x80
|
||||
#define PV_LITTLE_ENDIAN 0x00
|
||||
#define PV_MASK 0x7f /* To mask away endian bit */
|
||||
|
||||
#define PV_PUT 0
|
||||
#define PV_GET 1
|
||||
|
||||
typedef union {
|
||||
char *c;
|
||||
__u32 i;
|
||||
__u32 *ip;
|
||||
} irda_pv_t;
|
||||
|
||||
typedef struct {
|
||||
__u8 pi;
|
||||
__u8 pl;
|
||||
irda_pv_t pv;
|
||||
} irda_param_t;
|
||||
|
||||
typedef int (*PI_HANDLER)(void *self, irda_param_t *param, int get);
|
||||
typedef int (*PV_HANDLER)(void *self, __u8 *buf, int len, __u8 pi,
|
||||
PV_TYPE type, PI_HANDLER func);
|
||||
|
||||
typedef struct {
|
||||
const PI_HANDLER func; /* Handler for this parameter identifier */
|
||||
PV_TYPE type; /* Data type for this parameter */
|
||||
} pi_minor_info_t;
|
||||
|
||||
typedef struct {
|
||||
const pi_minor_info_t *pi_minor_call_table;
|
||||
int len;
|
||||
} pi_major_info_t;
|
||||
|
||||
typedef struct {
|
||||
const pi_major_info_t *tables;
|
||||
int len;
|
||||
__u8 pi_mask;
|
||||
int pi_major_offset;
|
||||
} pi_param_info_t;
|
||||
|
||||
int irda_param_pack(__u8 *buf, char *fmt, ...);
|
||||
|
||||
int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len,
|
||||
pi_param_info_t *info);
|
||||
int irda_param_extract_all(void *self, __u8 *buf, int len,
|
||||
pi_param_info_t *info);
|
||||
|
||||
#define irda_param_insert_byte(buf,pi,pv) irda_param_pack(buf,"bbb",pi,1,pv)
|
||||
|
||||
#endif /* IRDA_PARAMS_H */
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: qos.h
|
||||
* Version: 1.0
|
||||
* Description: Quality of Service definitions
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Fri Sep 19 23:21:09 1997
|
||||
* Modified at: Thu Dec 2 13:51:54 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef IRDA_QOS_H
|
||||
#define IRDA_QOS_H
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <net/irda/parameters.h>
|
||||
|
||||
#define PI_BAUD_RATE 0x01
|
||||
#define PI_MAX_TURN_TIME 0x82
|
||||
#define PI_DATA_SIZE 0x83
|
||||
#define PI_WINDOW_SIZE 0x84
|
||||
#define PI_ADD_BOFS 0x85
|
||||
#define PI_MIN_TURN_TIME 0x86
|
||||
#define PI_LINK_DISC 0x08
|
||||
|
||||
#define IR_115200_MAX 0x3f
|
||||
|
||||
/* Baud rates (first byte) */
|
||||
#define IR_2400 0x01
|
||||
#define IR_9600 0x02
|
||||
#define IR_19200 0x04
|
||||
#define IR_38400 0x08
|
||||
#define IR_57600 0x10
|
||||
#define IR_115200 0x20
|
||||
#define IR_576000 0x40
|
||||
#define IR_1152000 0x80
|
||||
|
||||
/* Baud rates (second byte) */
|
||||
#define IR_4000000 0x01
|
||||
#define IR_16000000 0x02
|
||||
|
||||
/* Quality of Service information */
|
||||
struct qos_value {
|
||||
__u32 value;
|
||||
__u16 bits; /* LSB is first byte, MSB is second byte */
|
||||
};
|
||||
|
||||
struct qos_info {
|
||||
magic_t magic;
|
||||
|
||||
struct qos_value baud_rate; /* IR_11520O | ... */
|
||||
struct qos_value max_turn_time;
|
||||
struct qos_value data_size;
|
||||
struct qos_value window_size;
|
||||
struct qos_value additional_bofs;
|
||||
struct qos_value min_turn_time;
|
||||
struct qos_value link_disc_time;
|
||||
|
||||
struct qos_value power;
|
||||
};
|
||||
|
||||
extern int sysctl_max_baud_rate;
|
||||
extern int sysctl_max_inactive_time;
|
||||
|
||||
void irda_init_max_qos_capabilies(struct qos_info *qos);
|
||||
void irda_qos_compute_intersection(struct qos_info *, struct qos_info *);
|
||||
|
||||
__u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time);
|
||||
|
||||
void irda_qos_bits_to_value(struct qos_info *qos);
|
||||
|
||||
/* So simple, how could we not inline those two ?
|
||||
* Note : one byte is 10 bits if you include start and stop bits
|
||||
* Jean II */
|
||||
#define irlap_min_turn_time_in_bytes(speed, min_turn_time) ( \
|
||||
speed * min_turn_time / 10000000 \
|
||||
)
|
||||
#define irlap_xbofs_in_usec(speed, xbofs) ( \
|
||||
xbofs * 10000000 / speed \
|
||||
)
|
||||
|
||||
#endif
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: timer.h
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sat Aug 16 00:59:29 1997
|
||||
* Modified at: Thu Oct 7 12:25:24 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1997, 1998-1999 Dag Brattli <dagb@cs.uit.no>,
|
||||
* All Rights Reserved.
|
||||
* Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef TIMER_H
|
||||
#define TIMER_H
|
||||
|
||||
#include <linux/timer.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
#include <asm/param.h> /* for HZ */
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
|
||||
/* A few forward declarations (to make compiler happy) */
|
||||
struct irlmp_cb;
|
||||
struct irlap_cb;
|
||||
struct lsap_cb;
|
||||
struct lap_cb;
|
||||
|
||||
/*
|
||||
* Timeout definitions, some defined in IrLAP 6.13.5 - p. 92
|
||||
*/
|
||||
#define POLL_TIMEOUT (450*HZ/1000) /* Must never exceed 500 ms */
|
||||
#define FINAL_TIMEOUT (500*HZ/1000) /* Must never exceed 500 ms */
|
||||
|
||||
/*
|
||||
* Normally twice of p-timer. Note 3, IrLAP 6.3.11.2 - p. 60 suggests
|
||||
* at least twice duration of the P-timer.
|
||||
*/
|
||||
#define WD_TIMEOUT (POLL_TIMEOUT*2)
|
||||
|
||||
#define MEDIABUSY_TIMEOUT (500*HZ/1000) /* 500 msec */
|
||||
#define SMALLBUSY_TIMEOUT (100*HZ/1000) /* 100 msec - IrLAP 6.13.4 */
|
||||
|
||||
/*
|
||||
* Slot timer must never exceed 85 ms, and must always be at least 25 ms,
|
||||
* suggested to 75-85 msec by IrDA lite. This doesn't work with a lot of
|
||||
* devices, and other stackes uses a lot more, so it's best we do it as well
|
||||
* (Note : this is the default value and sysctl overrides it - Jean II)
|
||||
*/
|
||||
#define SLOT_TIMEOUT (90*HZ/1000)
|
||||
|
||||
/*
|
||||
* The latest discovery frame (XID) is longer due to the extra discovery
|
||||
* information (hints, device name...). This is its extra length.
|
||||
* We use that when setting the query timeout. Jean II
|
||||
*/
|
||||
#define XIDEXTRA_TIMEOUT (34*HZ/1000) /* 34 msec */
|
||||
|
||||
#define WATCHDOG_TIMEOUT (20*HZ) /* 20 sec */
|
||||
|
||||
static inline void irda_start_timer(struct timer_list *ptimer, int timeout,
|
||||
void (*callback)(struct timer_list *))
|
||||
{
|
||||
ptimer->function = callback;
|
||||
|
||||
/* Set new value for timer (update or add timer).
|
||||
* We use mod_timer() because it's more efficient and also
|
||||
* safer with respect to race conditions - Jean II */
|
||||
mod_timer(ptimer, jiffies + timeout);
|
||||
}
|
||||
|
||||
|
||||
void irlap_start_slot_timer(struct irlap_cb *self, int timeout);
|
||||
void irlap_start_query_timer(struct irlap_cb *self, int S, int s);
|
||||
void irlap_start_final_timer(struct irlap_cb *self, int timeout);
|
||||
void irlap_start_wd_timer(struct irlap_cb *self, int timeout);
|
||||
void irlap_start_backoff_timer(struct irlap_cb *self, int timeout);
|
||||
|
||||
void irlap_start_mbusy_timer(struct irlap_cb *self, int timeout);
|
||||
void irlap_stop_mbusy_timer(struct irlap_cb *);
|
||||
|
||||
void irlmp_start_watchdog_timer(struct lsap_cb *, int timeout);
|
||||
void irlmp_start_discovery_timer(struct irlmp_cb *, int timeout);
|
||||
void irlmp_start_idle_timer(struct lap_cb *, int timeout);
|
||||
void irlmp_stop_idle_timer(struct lap_cb *self);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: wrapper.h
|
||||
* Version: 1.2
|
||||
* Description: IrDA SIR async wrapper layer
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Mon Aug 4 20:40:53 1997
|
||||
* Modified at: Tue Jan 11 12:37:29 2000
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither Dag Brattli nor University of Tromsø admit liability nor
|
||||
* provide warranty for any of this software. This material is
|
||||
* provided "AS-IS" and at no charge.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef WRAPPER_H
|
||||
#define WRAPPER_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include <net/irda/irda_device.h> /* iobuff_t */
|
||||
|
||||
#define BOF 0xc0 /* Beginning of frame */
|
||||
#define XBOF 0xff
|
||||
#define EOF 0xc1 /* End of frame */
|
||||
#define CE 0x7d /* Control escape */
|
||||
|
||||
#define STA BOF /* Start flag */
|
||||
#define STO EOF /* End flag */
|
||||
|
||||
#define IRDA_TRANS 0x20 /* Asynchronous transparency modifier */
|
||||
|
||||
/* States for receiving a frame in async mode */
|
||||
enum {
|
||||
OUTSIDE_FRAME,
|
||||
BEGIN_FRAME,
|
||||
LINK_ESCAPE,
|
||||
INSIDE_FRAME
|
||||
};
|
||||
|
||||
/* Proto definitions */
|
||||
int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize);
|
||||
void async_unwrap_char(struct net_device *dev, struct net_device_stats *stats,
|
||||
iobuff_t *buf, __u8 byte);
|
||||
|
||||
#endif
|
|
@ -1,96 +0,0 @@
|
|||
#
|
||||
# IrDA protocol configuration
|
||||
#
|
||||
|
||||
menuconfig IRDA
|
||||
depends on NET && !S390
|
||||
tristate "IrDA (infrared) subsystem support"
|
||||
select CRC_CCITT
|
||||
---help---
|
||||
Say Y here if you want to build support for the IrDA (TM) protocols.
|
||||
The Infrared Data Associations (tm) specifies standards for wireless
|
||||
infrared communication and is supported by most laptops and PDA's.
|
||||
|
||||
To use Linux support for the IrDA (tm) protocols, you will also need
|
||||
some user-space utilities like irattach. For more information, see
|
||||
the file <file:Documentation/networking/irda.txt>. You also want to
|
||||
read the IR-HOWTO, available at
|
||||
<http://www.tldp.org/docs.html#howto>.
|
||||
|
||||
If you want to exchange bits of data (vCal, vCard) with a PDA, you
|
||||
will need to install some OBEX application, such as OpenObex :
|
||||
<http://sourceforge.net/projects/openobex/>
|
||||
|
||||
To compile this support as a module, choose M here: the module will
|
||||
be called irda.
|
||||
|
||||
comment "IrDA protocols"
|
||||
depends on IRDA
|
||||
|
||||
source "drivers/staging/irda/net/irlan/Kconfig"
|
||||
|
||||
source "drivers/staging/irda/net/irnet/Kconfig"
|
||||
|
||||
source "drivers/staging/irda/net/ircomm/Kconfig"
|
||||
|
||||
config IRDA_ULTRA
|
||||
bool "Ultra (connectionless) protocol"
|
||||
depends on IRDA
|
||||
help
|
||||
Say Y here to support the connectionless Ultra IRDA protocol.
|
||||
Ultra allows to exchange data over IrDA with really simple devices
|
||||
(watch, beacon) without the overhead of the IrDA protocol (no handshaking,
|
||||
no management frames, simple fixed header).
|
||||
Ultra is available as a special socket : socket(AF_IRDA, SOCK_DGRAM, 1);
|
||||
|
||||
comment "IrDA options"
|
||||
depends on IRDA
|
||||
|
||||
config IRDA_CACHE_LAST_LSAP
|
||||
bool "Cache last LSAP"
|
||||
depends on IRDA
|
||||
help
|
||||
Say Y here if you want IrLMP to cache the last LSAP used. This
|
||||
makes sense since most frames will be sent/received on the same
|
||||
connection. Enabling this option will save a hash-lookup per frame.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config IRDA_FAST_RR
|
||||
bool "Fast RRs (low latency)"
|
||||
depends on IRDA
|
||||
---help---
|
||||
Say Y here is you want IrLAP to send fast RR (Receive Ready) frames
|
||||
when acting as a primary station.
|
||||
Disabling this option will make latency over IrDA very bad. Enabling
|
||||
this option will make the IrDA stack send more packet than strictly
|
||||
necessary, thus reduce your battery life (but not that much).
|
||||
|
||||
Fast RR will make IrLAP send out a RR frame immediately when
|
||||
receiving a frame if its own transmit queue is currently empty. This
|
||||
will give a lot of speed improvement when receiving much data since
|
||||
the secondary station will not have to wait the max. turn around
|
||||
time (usually 500ms) before it is allowed to transmit the next time.
|
||||
If the transmit queue of the secondary is also empty, the primary will
|
||||
start backing-off before sending another RR frame, waiting longer
|
||||
each time until the back-off reaches the max. turn around time.
|
||||
This back-off increase in controlled via
|
||||
/proc/sys/net/irda/fast_poll_increase
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config IRDA_DEBUG
|
||||
bool "Debug information"
|
||||
depends on IRDA
|
||||
help
|
||||
Say Y here if you want the IrDA subsystem to write debug information
|
||||
to your syslog. You can change the debug level in
|
||||
/proc/sys/net/irda/debug .
|
||||
When this option is enabled, the IrDA also perform many extra internal
|
||||
verifications which will usually prevent the kernel to crash in case of
|
||||
bugs.
|
||||
|
||||
If unsure, say Y (since it makes it easier to find the bugs).
|
||||
|
||||
source "drivers/staging/irda/drivers/Kconfig"
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#
|
||||
# Makefile for the Linux IrDA protocol layer.
|
||||
#
|
||||
|
||||
subdir-ccflags-y += -I$(srctree)/drivers/staging/irda/include
|
||||
|
||||
obj-$(CONFIG_IRDA) += irda.o
|
||||
obj-$(CONFIG_IRLAN) += irlan/
|
||||
obj-$(CONFIG_IRNET) += irnet/
|
||||
obj-$(CONFIG_IRCOMM) += ircomm/
|
||||
|
||||
irda-y := iriap.o iriap_event.o irlmp.o irlmp_event.o irlmp_frame.o \
|
||||
irlap.o irlap_event.o irlap_frame.o timer.o qos.o irqueue.o \
|
||||
irttp.o irda_device.o irias_object.o wrapper.o af_irda.o \
|
||||
discovery.o parameters.o irnetlink.o irmod.o
|
||||
irda-$(CONFIG_PROC_FS) += irproc.o
|
||||
irda-$(CONFIG_SYSCTL) += irsysctl.o
|
File diff suppressed because it is too large
Load Diff
|
@ -1,417 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: discovery.c
|
||||
* Version: 0.1
|
||||
* Description: Routines for handling discoveries at the IrLMP layer
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Tue Apr 6 15:33:50 1999
|
||||
* Modified at: Sat Oct 9 17:11:31 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
* Modified at: Fri May 28 3:11 CST 1999
|
||||
* Modified by: Horst von Brand <vonbrand@sleipnir.valparaiso.cl>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irlmp.h>
|
||||
|
||||
#include <net/irda/discovery.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/*
|
||||
* Function irlmp_add_discovery (cachelog, discovery)
|
||||
*
|
||||
* Add a new discovery to the cachelog, and remove any old discoveries
|
||||
* from the same device
|
||||
*
|
||||
* Note : we try to preserve the time this device was *first* discovered
|
||||
* (as opposed to the time of last discovery used for cleanup). This is
|
||||
* used by clients waiting for discovery events to tell if the device
|
||||
* discovered is "new" or just the same old one. They can't rely there
|
||||
* on a binary flag (new/old), because not all discovery events are
|
||||
* propagated to them, and they might not always listen, so they would
|
||||
* miss some new devices popping up...
|
||||
* Jean II
|
||||
*/
|
||||
void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new)
|
||||
{
|
||||
discovery_t *discovery, *node;
|
||||
unsigned long flags;
|
||||
|
||||
/* Set time of first discovery if node is new (see below) */
|
||||
new->firststamp = new->timestamp;
|
||||
|
||||
spin_lock_irqsave(&cachelog->hb_spinlock, flags);
|
||||
|
||||
/*
|
||||
* Remove all discoveries of devices that has previously been
|
||||
* discovered on the same link with the same name (info), or the
|
||||
* same daddr. We do this since some devices (mostly PDAs) change
|
||||
* their device address between every discovery.
|
||||
*/
|
||||
discovery = (discovery_t *) hashbin_get_first(cachelog);
|
||||
while (discovery != NULL ) {
|
||||
node = discovery;
|
||||
|
||||
/* Be sure to stay one item ahead */
|
||||
discovery = (discovery_t *) hashbin_get_next(cachelog);
|
||||
|
||||
if ((node->data.saddr == new->data.saddr) &&
|
||||
((node->data.daddr == new->data.daddr) ||
|
||||
(strcmp(node->data.info, new->data.info) == 0)))
|
||||
{
|
||||
/* This discovery is a previous discovery
|
||||
* from the same device, so just remove it
|
||||
*/
|
||||
hashbin_remove_this(cachelog, (irda_queue_t *) node);
|
||||
/* Check if hints bits are unchanged */
|
||||
if (get_unaligned((__u16 *)node->data.hints) == get_unaligned((__u16 *)new->data.hints))
|
||||
/* Set time of first discovery for this node */
|
||||
new->firststamp = node->firststamp;
|
||||
kfree(node);
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert the new and updated version */
|
||||
hashbin_insert(cachelog, (irda_queue_t *) new, new->data.daddr, NULL);
|
||||
|
||||
spin_unlock_irqrestore(&cachelog->hb_spinlock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function irlmp_add_discovery_log (cachelog, log)
|
||||
*
|
||||
* Merge a disovery log into the cachelog.
|
||||
*
|
||||
*/
|
||||
void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log)
|
||||
{
|
||||
discovery_t *discovery;
|
||||
|
||||
/*
|
||||
* If log is missing this means that IrLAP was unable to perform the
|
||||
* discovery, so restart discovery again with just the half timeout
|
||||
* of the normal one.
|
||||
*/
|
||||
/* Well... It means that there was nobody out there - Jean II */
|
||||
if (log == NULL) {
|
||||
/* irlmp_start_discovery_timer(irlmp, 150); */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Locking : we are the only owner of this discovery log, so
|
||||
* no need to lock it.
|
||||
* We just need to lock the global log in irlmp_add_discovery().
|
||||
*/
|
||||
discovery = (discovery_t *) hashbin_remove_first(log);
|
||||
while (discovery != NULL) {
|
||||
irlmp_add_discovery(cachelog, discovery);
|
||||
|
||||
discovery = (discovery_t *) hashbin_remove_first(log);
|
||||
}
|
||||
|
||||
/* Delete the now empty log */
|
||||
hashbin_delete(log, (FREE_FUNC) kfree);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function irlmp_expire_discoveries (log, saddr, force)
|
||||
*
|
||||
* Go through all discoveries and expire all that has stayed too long
|
||||
*
|
||||
* Note : this assume that IrLAP won't change its saddr, which
|
||||
* currently is a valid assumption...
|
||||
*/
|
||||
void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force)
|
||||
{
|
||||
discovery_t * discovery;
|
||||
discovery_t * curr;
|
||||
unsigned long flags;
|
||||
discinfo_t * buffer = NULL;
|
||||
int n; /* Size of the full log */
|
||||
int i = 0; /* How many we expired */
|
||||
|
||||
IRDA_ASSERT(log != NULL, return;);
|
||||
spin_lock_irqsave(&log->hb_spinlock, flags);
|
||||
|
||||
discovery = (discovery_t *) hashbin_get_first(log);
|
||||
while (discovery != NULL) {
|
||||
/* Be sure to be one item ahead */
|
||||
curr = discovery;
|
||||
discovery = (discovery_t *) hashbin_get_next(log);
|
||||
|
||||
/* Test if it's time to expire this discovery */
|
||||
if ((curr->data.saddr == saddr) &&
|
||||
(force ||
|
||||
((jiffies - curr->timestamp) > DISCOVERY_EXPIRE_TIMEOUT)))
|
||||
{
|
||||
/* Create buffer as needed.
|
||||
* As this function get called a lot and most time
|
||||
* we don't have anything to put in the log (we are
|
||||
* quite picky), we can save a lot of overhead
|
||||
* by not calling kmalloc. Jean II */
|
||||
if(buffer == NULL) {
|
||||
/* Create the client specific buffer */
|
||||
n = HASHBIN_GET_SIZE(log);
|
||||
buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC);
|
||||
if (!buffer) {
|
||||
spin_unlock_irqrestore(&log->hb_spinlock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Copy discovery information */
|
||||
memcpy(&(buffer[i]), &(curr->data),
|
||||
sizeof(discinfo_t));
|
||||
i++;
|
||||
|
||||
/* Remove it from the log */
|
||||
curr = hashbin_remove_this(log, (irda_queue_t *) curr);
|
||||
kfree(curr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Drop the spinlock before calling the higher layers, as
|
||||
* we can't guarantee they won't call us back and create a
|
||||
* deadlock. We will work on our own private data, so we
|
||||
* don't care to be interrupted. - Jean II */
|
||||
spin_unlock_irqrestore(&log->hb_spinlock, flags);
|
||||
|
||||
if(buffer == NULL)
|
||||
return;
|
||||
|
||||
/* Tell IrLMP and registered clients about it */
|
||||
irlmp_discovery_expiry(buffer, i);
|
||||
|
||||
/* Free up our buffer */
|
||||
kfree(buffer);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Function irlmp_dump_discoveries (log)
|
||||
*
|
||||
* Print out all discoveries in log
|
||||
*
|
||||
*/
|
||||
void irlmp_dump_discoveries(hashbin_t *log)
|
||||
{
|
||||
discovery_t *discovery;
|
||||
|
||||
IRDA_ASSERT(log != NULL, return;);
|
||||
|
||||
discovery = (discovery_t *) hashbin_get_first(log);
|
||||
while (discovery != NULL) {
|
||||
pr_debug("Discovery:\n");
|
||||
pr_debug(" daddr=%08x\n", discovery->data.daddr);
|
||||
pr_debug(" saddr=%08x\n", discovery->data.saddr);
|
||||
pr_debug(" nickname=%s\n", discovery->data.info);
|
||||
|
||||
discovery = (discovery_t *) hashbin_get_next(log);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Function irlmp_copy_discoveries (log, pn, mask)
|
||||
*
|
||||
* Copy all discoveries in a buffer
|
||||
*
|
||||
* This function implement a safe way for lmp clients to access the
|
||||
* discovery log. The basic problem is that we don't want the log
|
||||
* to change (add/remove) while the client is reading it. If the
|
||||
* lmp client manipulate directly the hashbin, he is sure to get
|
||||
* into troubles...
|
||||
* The idea is that we copy all the current discovery log in a buffer
|
||||
* which is specific to the client and pass this copy to him. As we
|
||||
* do this operation with the spinlock grabbed, we are safe...
|
||||
* Note : we don't want those clients to grab the spinlock, because
|
||||
* we have no control on how long they will hold it...
|
||||
* Note : we choose to copy the log in "struct irda_device_info" to
|
||||
* save space...
|
||||
* Note : the client must kfree himself() the log...
|
||||
* Jean II
|
||||
*/
|
||||
struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn,
|
||||
__u16 mask, int old_entries)
|
||||
{
|
||||
discovery_t * discovery;
|
||||
unsigned long flags;
|
||||
discinfo_t * buffer = NULL;
|
||||
int j_timeout = (sysctl_discovery_timeout * HZ);
|
||||
int n; /* Size of the full log */
|
||||
int i = 0; /* How many we picked */
|
||||
|
||||
IRDA_ASSERT(pn != NULL, return NULL;);
|
||||
IRDA_ASSERT(log != NULL, return NULL;);
|
||||
|
||||
/* Save spin lock */
|
||||
spin_lock_irqsave(&log->hb_spinlock, flags);
|
||||
|
||||
discovery = (discovery_t *) hashbin_get_first(log);
|
||||
while (discovery != NULL) {
|
||||
/* Mask out the ones we don't want :
|
||||
* We want to match the discovery mask, and to get only
|
||||
* the most recent one (unless we want old ones) */
|
||||
if ((get_unaligned((__u16 *)discovery->data.hints) & mask) &&
|
||||
((old_entries) ||
|
||||
((jiffies - discovery->firststamp) < j_timeout))) {
|
||||
/* Create buffer as needed.
|
||||
* As this function get called a lot and most time
|
||||
* we don't have anything to put in the log (we are
|
||||
* quite picky), we can save a lot of overhead
|
||||
* by not calling kmalloc. Jean II */
|
||||
if(buffer == NULL) {
|
||||
/* Create the client specific buffer */
|
||||
n = HASHBIN_GET_SIZE(log);
|
||||
buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC);
|
||||
if (!buffer) {
|
||||
spin_unlock_irqrestore(&log->hb_spinlock, flags);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Copy discovery information */
|
||||
memcpy(&(buffer[i]), &(discovery->data),
|
||||
sizeof(discinfo_t));
|
||||
i++;
|
||||
}
|
||||
discovery = (discovery_t *) hashbin_get_next(log);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&log->hb_spinlock, flags);
|
||||
|
||||
/* Get the actual number of device in the buffer and return */
|
||||
*pn = i;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static inline discovery_t *discovery_seq_idx(loff_t pos)
|
||||
|
||||
{
|
||||
discovery_t *discovery;
|
||||
|
||||
for (discovery = (discovery_t *) hashbin_get_first(irlmp->cachelog);
|
||||
discovery != NULL;
|
||||
discovery = (discovery_t *) hashbin_get_next(irlmp->cachelog)) {
|
||||
if (pos-- == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return discovery;
|
||||
}
|
||||
|
||||
static void *discovery_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
spin_lock_irq(&irlmp->cachelog->hb_spinlock);
|
||||
return *pos ? discovery_seq_idx(*pos - 1) : SEQ_START_TOKEN;
|
||||
}
|
||||
|
||||
static void *discovery_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
++*pos;
|
||||
return (v == SEQ_START_TOKEN)
|
||||
? (void *) hashbin_get_first(irlmp->cachelog)
|
||||
: (void *) hashbin_get_next(irlmp->cachelog);
|
||||
}
|
||||
|
||||
static void discovery_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
spin_unlock_irq(&irlmp->cachelog->hb_spinlock);
|
||||
}
|
||||
|
||||
static int discovery_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
if (v == SEQ_START_TOKEN)
|
||||
seq_puts(seq, "IrLMP: Discovery log:\n\n");
|
||||
else {
|
||||
const discovery_t *discovery = v;
|
||||
|
||||
seq_printf(seq, "nickname: %s, hint: 0x%02x%02x",
|
||||
discovery->data.info,
|
||||
discovery->data.hints[0],
|
||||
discovery->data.hints[1]);
|
||||
#if 0
|
||||
if ( discovery->data.hints[0] & HINT_PNP)
|
||||
seq_puts(seq, "PnP Compatible ");
|
||||
if ( discovery->data.hints[0] & HINT_PDA)
|
||||
seq_puts(seq, "PDA/Palmtop ");
|
||||
if ( discovery->data.hints[0] & HINT_COMPUTER)
|
||||
seq_puts(seq, "Computer ");
|
||||
if ( discovery->data.hints[0] & HINT_PRINTER)
|
||||
seq_puts(seq, "Printer ");
|
||||
if ( discovery->data.hints[0] & HINT_MODEM)
|
||||
seq_puts(seq, "Modem ");
|
||||
if ( discovery->data.hints[0] & HINT_FAX)
|
||||
seq_puts(seq, "Fax ");
|
||||
if ( discovery->data.hints[0] & HINT_LAN)
|
||||
seq_puts(seq, "LAN Access ");
|
||||
|
||||
if ( discovery->data.hints[1] & HINT_TELEPHONY)
|
||||
seq_puts(seq, "Telephony ");
|
||||
if ( discovery->data.hints[1] & HINT_FILE_SERVER)
|
||||
seq_puts(seq, "File Server ");
|
||||
if ( discovery->data.hints[1] & HINT_COMM)
|
||||
seq_puts(seq, "IrCOMM ");
|
||||
if ( discovery->data.hints[1] & HINT_OBEX)
|
||||
seq_puts(seq, "IrOBEX ");
|
||||
#endif
|
||||
seq_printf(seq,", saddr: 0x%08x, daddr: 0x%08x\n\n",
|
||||
discovery->data.saddr,
|
||||
discovery->data.daddr);
|
||||
|
||||
seq_putc(seq, '\n');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations discovery_seq_ops = {
|
||||
.start = discovery_seq_start,
|
||||
.next = discovery_seq_next,
|
||||
.stop = discovery_seq_stop,
|
||||
.show = discovery_seq_show,
|
||||
};
|
||||
|
||||
static int discovery_seq_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
IRDA_ASSERT(irlmp != NULL, return -EINVAL;);
|
||||
|
||||
return seq_open(file, &discovery_seq_ops);
|
||||
}
|
||||
|
||||
const struct file_operations discovery_seq_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = discovery_seq_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
#endif
|
|
@ -1,12 +0,0 @@
|
|||
config IRCOMM
|
||||
tristate "IrCOMM protocol"
|
||||
depends on IRDA && TTY
|
||||
help
|
||||
Say Y here if you want to build support for the IrCOMM protocol.
|
||||
To compile it as modules, choose M here: the modules will be
|
||||
called ircomm and ircomm_tty.
|
||||
IrCOMM implements serial port emulation, and makes it possible to
|
||||
use all existing applications that understands TTY's with an
|
||||
infrared link. Thus you should be able to use application like PPP,
|
||||
minicom and others.
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#
|
||||
# Makefile for the Linux IrDA IrCOMM protocol layer.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IRCOMM) += ircomm.o ircomm-tty.o
|
||||
|
||||
ircomm-y := ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o
|
||||
ircomm-tty-y := ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o ircomm_param.o
|
|
@ -1,563 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ircomm_core.c
|
||||
* Version: 1.0
|
||||
* Description: IrCOMM service interface
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sun Jun 6 20:37:34 1999
|
||||
* Modified at: Tue Dec 21 13:26:41 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
* Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irmod.h>
|
||||
#include <net/irda/irlmp.h>
|
||||
#include <net/irda/iriap.h>
|
||||
#include <net/irda/irttp.h>
|
||||
#include <net/irda/irias_object.h>
|
||||
|
||||
#include <net/irda/ircomm_event.h>
|
||||
#include <net/irda/ircomm_lmp.h>
|
||||
#include <net/irda/ircomm_ttp.h>
|
||||
#include <net/irda/ircomm_param.h>
|
||||
#include <net/irda/ircomm_core.h>
|
||||
|
||||
static int __ircomm_close(struct ircomm_cb *self);
|
||||
static void ircomm_control_indication(struct ircomm_cb *self,
|
||||
struct sk_buff *skb, int clen);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
extern struct proc_dir_entry *proc_irda;
|
||||
static int ircomm_seq_open(struct inode *, struct file *);
|
||||
|
||||
static const struct file_operations ircomm_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ircomm_seq_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
hashbin_t *ircomm = NULL;
|
||||
|
||||
static int __init ircomm_init(void)
|
||||
{
|
||||
ircomm = hashbin_new(HB_LOCK);
|
||||
if (ircomm == NULL) {
|
||||
net_err_ratelimited("%s(), can't allocate hashbin!\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
{ struct proc_dir_entry *ent;
|
||||
ent = proc_create("ircomm", 0, proc_irda, &ircomm_proc_fops);
|
||||
if (!ent) {
|
||||
printk(KERN_ERR "ircomm_init: can't create /proc entry!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
net_info_ratelimited("IrCOMM protocol (Dag Brattli)\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit ircomm_cleanup(void)
|
||||
{
|
||||
hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
remove_proc_entry("ircomm", proc_irda);
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_open (client_notify)
|
||||
*
|
||||
* Start a new IrCOMM instance
|
||||
*
|
||||
*/
|
||||
struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line)
|
||||
{
|
||||
struct ircomm_cb *self = NULL;
|
||||
int ret;
|
||||
|
||||
pr_debug("%s(), service_type=0x%02x\n", __func__ ,
|
||||
service_type);
|
||||
|
||||
IRDA_ASSERT(ircomm != NULL, return NULL;);
|
||||
|
||||
self = kzalloc(sizeof(struct ircomm_cb), GFP_KERNEL);
|
||||
if (self == NULL)
|
||||
return NULL;
|
||||
|
||||
self->notify = *notify;
|
||||
self->magic = IRCOMM_MAGIC;
|
||||
|
||||
/* Check if we should use IrLMP or IrTTP */
|
||||
if (service_type & IRCOMM_3_WIRE_RAW) {
|
||||
self->flow_status = FLOW_START;
|
||||
ret = ircomm_open_lsap(self);
|
||||
} else
|
||||
ret = ircomm_open_tsap(self);
|
||||
|
||||
if (ret < 0) {
|
||||
kfree(self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->service_type = service_type;
|
||||
self->line = line;
|
||||
|
||||
hashbin_insert(ircomm, (irda_queue_t *) self, line, NULL);
|
||||
|
||||
ircomm_next_state(self, IRCOMM_IDLE);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_open);
|
||||
|
||||
/*
|
||||
* Function ircomm_close_instance (self)
|
||||
*
|
||||
* Remove IrCOMM instance
|
||||
*
|
||||
*/
|
||||
static int __ircomm_close(struct ircomm_cb *self)
|
||||
{
|
||||
/* Disconnect link if any */
|
||||
ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, NULL, NULL);
|
||||
|
||||
/* Remove TSAP */
|
||||
if (self->tsap) {
|
||||
irttp_close_tsap(self->tsap);
|
||||
self->tsap = NULL;
|
||||
}
|
||||
|
||||
/* Remove LSAP */
|
||||
if (self->lsap) {
|
||||
irlmp_close_lsap(self->lsap);
|
||||
self->lsap = NULL;
|
||||
}
|
||||
self->magic = 0;
|
||||
|
||||
kfree(self);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_close (self)
|
||||
*
|
||||
* Closes and removes the specified IrCOMM instance
|
||||
*
|
||||
*/
|
||||
int ircomm_close(struct ircomm_cb *self)
|
||||
{
|
||||
struct ircomm_cb *entry;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -EIO;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;);
|
||||
|
||||
entry = hashbin_remove(ircomm, self->line, NULL);
|
||||
|
||||
IRDA_ASSERT(entry == self, return -1;);
|
||||
|
||||
return __ircomm_close(self);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_close);
|
||||
|
||||
/*
|
||||
* Function ircomm_connect_request (self, service_type)
|
||||
*
|
||||
* Impl. of this function is differ from one of the reference. This
|
||||
* function does discovery as well as sending connect request
|
||||
*
|
||||
*/
|
||||
int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel,
|
||||
__u32 saddr, __u32 daddr, struct sk_buff *skb,
|
||||
__u8 service_type)
|
||||
{
|
||||
struct ircomm_info info;
|
||||
int ret;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
|
||||
|
||||
self->service_type= service_type;
|
||||
|
||||
info.dlsap_sel = dlsap_sel;
|
||||
info.saddr = saddr;
|
||||
info.daddr = daddr;
|
||||
|
||||
ret = ircomm_do_event(self, IRCOMM_CONNECT_REQUEST, skb, &info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_connect_request);
|
||||
|
||||
/*
|
||||
* Function ircomm_connect_indication (self, qos, skb)
|
||||
*
|
||||
* Notify user layer about the incoming connection
|
||||
*
|
||||
*/
|
||||
void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb,
|
||||
struct ircomm_info *info)
|
||||
{
|
||||
/*
|
||||
* If there are any data hiding in the control channel, we must
|
||||
* deliver it first. The side effect is that the control channel
|
||||
* will be removed from the skb
|
||||
*/
|
||||
if (self->notify.connect_indication)
|
||||
self->notify.connect_indication(self->notify.instance, self,
|
||||
info->qos, info->max_data_size,
|
||||
info->max_header_size, skb);
|
||||
else {
|
||||
pr_debug("%s(), missing handler\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_connect_response (self, userdata, max_sdu_size)
|
||||
*
|
||||
* User accepts connection
|
||||
*
|
||||
*/
|
||||
int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
|
||||
|
||||
ret = ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_connect_response);
|
||||
|
||||
/*
|
||||
* Function connect_confirm (self, skb)
|
||||
*
|
||||
* Notify user layer that the link is now connected
|
||||
*
|
||||
*/
|
||||
void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb,
|
||||
struct ircomm_info *info)
|
||||
{
|
||||
if (self->notify.connect_confirm )
|
||||
self->notify.connect_confirm(self->notify.instance,
|
||||
self, info->qos,
|
||||
info->max_data_size,
|
||||
info->max_header_size, skb);
|
||||
else {
|
||||
pr_debug("%s(), missing handler\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_data_request (self, userdata)
|
||||
*
|
||||
* Send IrCOMM data to peer device
|
||||
*
|
||||
*/
|
||||
int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -EFAULT;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
|
||||
IRDA_ASSERT(skb != NULL, return -EFAULT;);
|
||||
|
||||
ret = ircomm_do_event(self, IRCOMM_DATA_REQUEST, skb, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_data_request);
|
||||
|
||||
/*
|
||||
* Function ircomm_data_indication (self, skb)
|
||||
*
|
||||
* Data arrived, so deliver it to user
|
||||
*
|
||||
*/
|
||||
void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb)
|
||||
{
|
||||
IRDA_ASSERT(skb->len > 0, return;);
|
||||
|
||||
if (self->notify.data_indication)
|
||||
self->notify.data_indication(self->notify.instance, self, skb);
|
||||
else {
|
||||
pr_debug("%s(), missing handler\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_process_data (self, skb)
|
||||
*
|
||||
* Data arrived which may contain control channel data
|
||||
*
|
||||
*/
|
||||
void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb)
|
||||
{
|
||||
int clen;
|
||||
|
||||
IRDA_ASSERT(skb->len > 0, return;);
|
||||
|
||||
clen = skb->data[0];
|
||||
|
||||
/*
|
||||
* Input validation check: a stir4200/mcp2150 combinations sometimes
|
||||
* results in frames with clen > remaining packet size. These are
|
||||
* illegal; if we throw away just this frame then it seems to carry on
|
||||
* fine
|
||||
*/
|
||||
if (unlikely(skb->len < (clen + 1))) {
|
||||
pr_debug("%s() throwing away illegal frame\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there are any data hiding in the control channel, we must
|
||||
* deliver it first. The side effect is that the control channel
|
||||
* will be removed from the skb
|
||||
*/
|
||||
if (clen > 0)
|
||||
ircomm_control_indication(self, skb, clen);
|
||||
|
||||
/* Remove control channel from data channel */
|
||||
skb_pull(skb, clen+1);
|
||||
|
||||
if (skb->len)
|
||||
ircomm_data_indication(self, skb);
|
||||
else {
|
||||
pr_debug("%s(), data was control info only!\n",
|
||||
__func__);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_control_request (self, params)
|
||||
*
|
||||
* Send control data to peer device
|
||||
*
|
||||
*/
|
||||
int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -EFAULT;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
|
||||
IRDA_ASSERT(skb != NULL, return -EFAULT;);
|
||||
|
||||
ret = ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_control_request);
|
||||
|
||||
/*
|
||||
* Function ircomm_control_indication (self, skb)
|
||||
*
|
||||
* Data has arrived on the control channel
|
||||
*
|
||||
*/
|
||||
static void ircomm_control_indication(struct ircomm_cb *self,
|
||||
struct sk_buff *skb, int clen)
|
||||
{
|
||||
/* Use udata for delivering data on the control channel */
|
||||
if (self->notify.udata_indication) {
|
||||
struct sk_buff *ctrl_skb;
|
||||
|
||||
/* We don't own the skb, so clone it */
|
||||
ctrl_skb = skb_clone(skb, GFP_ATOMIC);
|
||||
if (!ctrl_skb)
|
||||
return;
|
||||
|
||||
/* Remove data channel from control channel */
|
||||
skb_trim(ctrl_skb, clen+1);
|
||||
|
||||
self->notify.udata_indication(self->notify.instance, self,
|
||||
ctrl_skb);
|
||||
|
||||
/* Drop reference count -
|
||||
* see ircomm_tty_control_indication(). */
|
||||
dev_kfree_skb(ctrl_skb);
|
||||
} else {
|
||||
pr_debug("%s(), missing handler\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_disconnect_request (self, userdata, priority)
|
||||
*
|
||||
* User layer wants to disconnect the IrCOMM connection
|
||||
*
|
||||
*/
|
||||
int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata)
|
||||
{
|
||||
struct ircomm_info info;
|
||||
int ret;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
|
||||
|
||||
ret = ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, userdata,
|
||||
&info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_disconnect_request);
|
||||
|
||||
/*
|
||||
* Function disconnect_indication (self, skb)
|
||||
*
|
||||
* Tell user that the link has been disconnected
|
||||
*
|
||||
*/
|
||||
void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb,
|
||||
struct ircomm_info *info)
|
||||
{
|
||||
IRDA_ASSERT(info != NULL, return;);
|
||||
|
||||
if (self->notify.disconnect_indication) {
|
||||
self->notify.disconnect_indication(self->notify.instance, self,
|
||||
info->reason, skb);
|
||||
} else {
|
||||
pr_debug("%s(), missing handler\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_flow_request (self, flow)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow)
|
||||
{
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
|
||||
if (self->service_type == IRCOMM_3_WIRE_RAW)
|
||||
return;
|
||||
|
||||
irttp_flow_request(self->tsap, flow);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_flow_request);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static void *ircomm_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
struct ircomm_cb *self;
|
||||
loff_t off = 0;
|
||||
|
||||
spin_lock_irq(&ircomm->hb_spinlock);
|
||||
|
||||
for (self = (struct ircomm_cb *) hashbin_get_first(ircomm);
|
||||
self != NULL;
|
||||
self = (struct ircomm_cb *) hashbin_get_next(ircomm)) {
|
||||
if (off++ == *pos)
|
||||
break;
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
static void *ircomm_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
++*pos;
|
||||
|
||||
return (void *) hashbin_get_next(ircomm);
|
||||
}
|
||||
|
||||
static void ircomm_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
spin_unlock_irq(&ircomm->hb_spinlock);
|
||||
}
|
||||
|
||||
static int ircomm_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
const struct ircomm_cb *self = v;
|
||||
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EINVAL; );
|
||||
|
||||
if(self->line < 0x10)
|
||||
seq_printf(seq, "ircomm%d", self->line);
|
||||
else
|
||||
seq_printf(seq, "irlpt%d", self->line - 0x10);
|
||||
|
||||
seq_printf(seq,
|
||||
" state: %s, slsap_sel: %#02x, dlsap_sel: %#02x, mode:",
|
||||
ircomm_state[ self->state],
|
||||
self->slsap_sel, self->dlsap_sel);
|
||||
|
||||
if(self->service_type & IRCOMM_3_WIRE_RAW)
|
||||
seq_printf(seq, " 3-wire-raw");
|
||||
if(self->service_type & IRCOMM_3_WIRE)
|
||||
seq_printf(seq, " 3-wire");
|
||||
if(self->service_type & IRCOMM_9_WIRE)
|
||||
seq_printf(seq, " 9-wire");
|
||||
if(self->service_type & IRCOMM_CENTRONICS)
|
||||
seq_printf(seq, " Centronics");
|
||||
seq_putc(seq, '\n');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations ircomm_seq_ops = {
|
||||
.start = ircomm_seq_start,
|
||||
.next = ircomm_seq_next,
|
||||
.stop = ircomm_seq_stop,
|
||||
.show = ircomm_seq_show,
|
||||
};
|
||||
|
||||
static int ircomm_seq_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &ircomm_seq_ops);
|
||||
}
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
MODULE_AUTHOR("Dag Brattli <dag@brattli.net>");
|
||||
MODULE_DESCRIPTION("IrCOMM protocol");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(ircomm_init);
|
||||
module_exit(ircomm_cleanup);
|
|
@ -1,246 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ircomm_event.c
|
||||
* Version: 1.0
|
||||
* Description: IrCOMM layer state machine
|
||||
* Status: Stable
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sun Jun 6 20:33:11 1999
|
||||
* Modified at: Sun Dec 12 13:44:32 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irlmp.h>
|
||||
#include <net/irda/iriap.h>
|
||||
#include <net/irda/irttp.h>
|
||||
#include <net/irda/irias_object.h>
|
||||
|
||||
#include <net/irda/ircomm_core.h>
|
||||
#include <net/irda/ircomm_event.h>
|
||||
|
||||
static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_info *info);
|
||||
static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_info *info);
|
||||
static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_info *info);
|
||||
static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_info *info);
|
||||
|
||||
const char *const ircomm_state[] = {
|
||||
"IRCOMM_IDLE",
|
||||
"IRCOMM_WAITI",
|
||||
"IRCOMM_WAITR",
|
||||
"IRCOMM_CONN",
|
||||
};
|
||||
|
||||
static const char *const ircomm_event[] __maybe_unused = {
|
||||
"IRCOMM_CONNECT_REQUEST",
|
||||
"IRCOMM_CONNECT_RESPONSE",
|
||||
"IRCOMM_TTP_CONNECT_INDICATION",
|
||||
"IRCOMM_LMP_CONNECT_INDICATION",
|
||||
"IRCOMM_TTP_CONNECT_CONFIRM",
|
||||
"IRCOMM_LMP_CONNECT_CONFIRM",
|
||||
|
||||
"IRCOMM_LMP_DISCONNECT_INDICATION",
|
||||
"IRCOMM_TTP_DISCONNECT_INDICATION",
|
||||
"IRCOMM_DISCONNECT_REQUEST",
|
||||
|
||||
"IRCOMM_TTP_DATA_INDICATION",
|
||||
"IRCOMM_LMP_DATA_INDICATION",
|
||||
"IRCOMM_DATA_REQUEST",
|
||||
"IRCOMM_CONTROL_REQUEST",
|
||||
"IRCOMM_CONTROL_INDICATION",
|
||||
};
|
||||
|
||||
static int (*state[])(struct ircomm_cb *self, IRCOMM_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_info *info) =
|
||||
{
|
||||
ircomm_state_idle,
|
||||
ircomm_state_waiti,
|
||||
ircomm_state_waitr,
|
||||
ircomm_state_conn,
|
||||
};
|
||||
|
||||
/*
|
||||
* Function ircomm_state_idle (self, event, skb)
|
||||
*
|
||||
* IrCOMM is currently idle
|
||||
*
|
||||
*/
|
||||
static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
case IRCOMM_CONNECT_REQUEST:
|
||||
ircomm_next_state(self, IRCOMM_WAITI);
|
||||
ret = self->issue.connect_request(self, skb, info);
|
||||
break;
|
||||
case IRCOMM_TTP_CONNECT_INDICATION:
|
||||
case IRCOMM_LMP_CONNECT_INDICATION:
|
||||
ircomm_next_state(self, IRCOMM_WAITR);
|
||||
ircomm_connect_indication(self, skb, info);
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s(), unknown event: %s\n", __func__ ,
|
||||
ircomm_event[event]);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_state_waiti (self, event, skb)
|
||||
*
|
||||
* The IrCOMM user has requested an IrCOMM connection to the remote
|
||||
* device and is awaiting confirmation
|
||||
*/
|
||||
static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
case IRCOMM_TTP_CONNECT_CONFIRM:
|
||||
case IRCOMM_LMP_CONNECT_CONFIRM:
|
||||
ircomm_next_state(self, IRCOMM_CONN);
|
||||
ircomm_connect_confirm(self, skb, info);
|
||||
break;
|
||||
case IRCOMM_TTP_DISCONNECT_INDICATION:
|
||||
case IRCOMM_LMP_DISCONNECT_INDICATION:
|
||||
ircomm_next_state(self, IRCOMM_IDLE);
|
||||
ircomm_disconnect_indication(self, skb, info);
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s(), unknown event: %s\n", __func__ ,
|
||||
ircomm_event[event]);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_state_waitr (self, event, skb)
|
||||
*
|
||||
* IrCOMM has received an incoming connection request and is awaiting
|
||||
* response from the user
|
||||
*/
|
||||
static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
case IRCOMM_CONNECT_RESPONSE:
|
||||
ircomm_next_state(self, IRCOMM_CONN);
|
||||
ret = self->issue.connect_response(self, skb);
|
||||
break;
|
||||
case IRCOMM_DISCONNECT_REQUEST:
|
||||
ircomm_next_state(self, IRCOMM_IDLE);
|
||||
ret = self->issue.disconnect_request(self, skb, info);
|
||||
break;
|
||||
case IRCOMM_TTP_DISCONNECT_INDICATION:
|
||||
case IRCOMM_LMP_DISCONNECT_INDICATION:
|
||||
ircomm_next_state(self, IRCOMM_IDLE);
|
||||
ircomm_disconnect_indication(self, skb, info);
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s(), unknown event = %s\n", __func__ ,
|
||||
ircomm_event[event]);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_state_conn (self, event, skb)
|
||||
*
|
||||
* IrCOMM is connected to the peer IrCOMM device
|
||||
*
|
||||
*/
|
||||
static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
case IRCOMM_DATA_REQUEST:
|
||||
ret = self->issue.data_request(self, skb, 0);
|
||||
break;
|
||||
case IRCOMM_TTP_DATA_INDICATION:
|
||||
ircomm_process_data(self, skb);
|
||||
break;
|
||||
case IRCOMM_LMP_DATA_INDICATION:
|
||||
ircomm_data_indication(self, skb);
|
||||
break;
|
||||
case IRCOMM_CONTROL_REQUEST:
|
||||
/* Just send a separate frame for now */
|
||||
ret = self->issue.data_request(self, skb, skb->len);
|
||||
break;
|
||||
case IRCOMM_TTP_DISCONNECT_INDICATION:
|
||||
case IRCOMM_LMP_DISCONNECT_INDICATION:
|
||||
ircomm_next_state(self, IRCOMM_IDLE);
|
||||
ircomm_disconnect_indication(self, skb, info);
|
||||
break;
|
||||
case IRCOMM_DISCONNECT_REQUEST:
|
||||
ircomm_next_state(self, IRCOMM_IDLE);
|
||||
ret = self->issue.disconnect_request(self, skb, info);
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s(), unknown event = %s\n", __func__ ,
|
||||
ircomm_event[event]);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_do_event (self, event, skb)
|
||||
*
|
||||
* Process event
|
||||
*
|
||||
*/
|
||||
int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_info *info)
|
||||
{
|
||||
pr_debug("%s: state=%s, event=%s\n", __func__ ,
|
||||
ircomm_state[self->state], ircomm_event[event]);
|
||||
|
||||
return (*state[self->state])(self, event, skb, info);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_next_state (self, state)
|
||||
*
|
||||
* Switch state
|
||||
*
|
||||
*/
|
||||
void ircomm_next_state(struct ircomm_cb *self, IRCOMM_STATE state)
|
||||
{
|
||||
self->state = state;
|
||||
|
||||
pr_debug("%s: next state=%s, service type=%d\n", __func__ ,
|
||||
ircomm_state[self->state], self->service_type);
|
||||
}
|
|
@ -1,350 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ircomm_lmp.c
|
||||
* Version: 1.0
|
||||
* Description: Interface between IrCOMM and IrLMP
|
||||
* Status: Stable
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sun Jun 6 20:48:27 1999
|
||||
* Modified at: Sun Dec 12 13:44:17 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
* Sources: Previous IrLPT work by Thomas Davis
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
* Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/gfp.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irlmp.h>
|
||||
#include <net/irda/iriap.h>
|
||||
#include <net/irda/irda_device.h> /* struct irda_skb_cb */
|
||||
|
||||
#include <net/irda/ircomm_event.h>
|
||||
#include <net/irda/ircomm_lmp.h>
|
||||
|
||||
|
||||
/*
|
||||
* Function ircomm_lmp_connect_request (self, userdata)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int ircomm_lmp_connect_request(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata,
|
||||
struct ircomm_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Don't forget to refcount it - should be NULL anyway */
|
||||
if(userdata)
|
||||
skb_get(userdata);
|
||||
|
||||
ret = irlmp_connect_request(self->lsap, info->dlsap_sel,
|
||||
info->saddr, info->daddr, NULL, userdata);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_lmp_connect_response (self, skb)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int ircomm_lmp_connect_response(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata)
|
||||
{
|
||||
struct sk_buff *tx_skb;
|
||||
|
||||
/* Any userdata supplied? */
|
||||
if (userdata == NULL) {
|
||||
tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
|
||||
if (!tx_skb)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Reserve space for MUX and LAP header */
|
||||
skb_reserve(tx_skb, LMP_MAX_HEADER);
|
||||
} else {
|
||||
/*
|
||||
* Check that the client has reserved enough space for
|
||||
* headers
|
||||
*/
|
||||
IRDA_ASSERT(skb_headroom(userdata) >= LMP_MAX_HEADER,
|
||||
return -1;);
|
||||
|
||||
/* Don't forget to refcount it - should be NULL anyway */
|
||||
skb_get(userdata);
|
||||
tx_skb = userdata;
|
||||
}
|
||||
|
||||
return irlmp_connect_response(self->lsap, tx_skb);
|
||||
}
|
||||
|
||||
static int ircomm_lmp_disconnect_request(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata,
|
||||
struct ircomm_info *info)
|
||||
{
|
||||
struct sk_buff *tx_skb;
|
||||
int ret;
|
||||
|
||||
if (!userdata) {
|
||||
tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
|
||||
if (!tx_skb)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Reserve space for MUX and LAP header */
|
||||
skb_reserve(tx_skb, LMP_MAX_HEADER);
|
||||
userdata = tx_skb;
|
||||
} else {
|
||||
/* Don't forget to refcount it - should be NULL anyway */
|
||||
skb_get(userdata);
|
||||
}
|
||||
|
||||
ret = irlmp_disconnect_request(self->lsap, userdata);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_lmp_flow_control (skb)
|
||||
*
|
||||
* This function is called when a data frame we have sent to IrLAP has
|
||||
* been deallocated. We do this to make sure we don't flood IrLAP with
|
||||
* frames, since we are not using the IrTTP flow control mechanism
|
||||
*/
|
||||
static void ircomm_lmp_flow_control(struct sk_buff *skb)
|
||||
{
|
||||
struct irda_skb_cb *cb;
|
||||
struct ircomm_cb *self;
|
||||
int line;
|
||||
|
||||
IRDA_ASSERT(skb != NULL, return;);
|
||||
|
||||
cb = (struct irda_skb_cb *) skb->cb;
|
||||
|
||||
line = cb->line;
|
||||
|
||||
self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL);
|
||||
if (!self) {
|
||||
pr_debug("%s(), didn't find myself\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
|
||||
self->pkt_count--;
|
||||
|
||||
if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) {
|
||||
pr_debug("%s(), asking TTY to start again!\n", __func__);
|
||||
self->flow_status = FLOW_START;
|
||||
if (self->notify.flow_indication)
|
||||
self->notify.flow_indication(self->notify.instance,
|
||||
self, FLOW_START);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_lmp_data_request (self, userdata)
|
||||
*
|
||||
* Send data frame to peer device
|
||||
*
|
||||
*/
|
||||
static int ircomm_lmp_data_request(struct ircomm_cb *self,
|
||||
struct sk_buff *skb,
|
||||
int not_used)
|
||||
{
|
||||
struct irda_skb_cb *cb;
|
||||
int ret;
|
||||
|
||||
IRDA_ASSERT(skb != NULL, return -1;);
|
||||
|
||||
cb = (struct irda_skb_cb *) skb->cb;
|
||||
|
||||
cb->line = self->line;
|
||||
|
||||
pr_debug("%s(), sending frame\n", __func__);
|
||||
|
||||
/* Don't forget to refcount it - see ircomm_tty_do_softint() */
|
||||
skb_get(skb);
|
||||
|
||||
skb_orphan(skb);
|
||||
skb->destructor = ircomm_lmp_flow_control;
|
||||
|
||||
if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) {
|
||||
pr_debug("%s(), asking TTY to slow down!\n", __func__);
|
||||
self->flow_status = FLOW_STOP;
|
||||
if (self->notify.flow_indication)
|
||||
self->notify.flow_indication(self->notify.instance,
|
||||
self, FLOW_STOP);
|
||||
}
|
||||
ret = irlmp_data_request(self->lsap, skb);
|
||||
if (ret) {
|
||||
net_err_ratelimited("%s(), failed\n", __func__);
|
||||
/* irlmp_data_request already free the packet */
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_lmp_data_indication (instance, sap, skb)
|
||||
*
|
||||
* Incoming data which we must deliver to the state machine, to check
|
||||
* we are still connected.
|
||||
*/
|
||||
static int ircomm_lmp_data_indication(void *instance, void *sap,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ircomm_cb *self = (struct ircomm_cb *) instance;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
|
||||
IRDA_ASSERT(skb != NULL, return -1;);
|
||||
|
||||
ircomm_do_event(self, IRCOMM_LMP_DATA_INDICATION, skb, NULL);
|
||||
|
||||
/* Drop reference count - see ircomm_tty_data_indication(). */
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_lmp_connect_confirm (instance, sap, qos, max_sdu_size,
|
||||
* max_header_size, skb)
|
||||
*
|
||||
* Connection has been confirmed by peer device
|
||||
*
|
||||
*/
|
||||
static void ircomm_lmp_connect_confirm(void *instance, void *sap,
|
||||
struct qos_info *qos,
|
||||
__u32 max_seg_size,
|
||||
__u8 max_header_size,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ircomm_cb *self = (struct ircomm_cb *) instance;
|
||||
struct ircomm_info info;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
IRDA_ASSERT(skb != NULL, return;);
|
||||
IRDA_ASSERT(qos != NULL, return;);
|
||||
|
||||
info.max_data_size = max_seg_size;
|
||||
info.max_header_size = max_header_size;
|
||||
info.qos = qos;
|
||||
|
||||
ircomm_do_event(self, IRCOMM_LMP_CONNECT_CONFIRM, skb, &info);
|
||||
|
||||
/* Drop reference count - see ircomm_tty_connect_confirm(). */
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_lmp_connect_indication (instance, sap, qos, max_sdu_size,
|
||||
* max_header_size, skb)
|
||||
*
|
||||
* Peer device wants to make a connection with us
|
||||
*
|
||||
*/
|
||||
static void ircomm_lmp_connect_indication(void *instance, void *sap,
|
||||
struct qos_info *qos,
|
||||
__u32 max_seg_size,
|
||||
__u8 max_header_size,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ircomm_cb *self = (struct ircomm_cb *)instance;
|
||||
struct ircomm_info info;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
IRDA_ASSERT(skb != NULL, return;);
|
||||
IRDA_ASSERT(qos != NULL, return;);
|
||||
|
||||
info.max_data_size = max_seg_size;
|
||||
info.max_header_size = max_header_size;
|
||||
info.qos = qos;
|
||||
|
||||
ircomm_do_event(self, IRCOMM_LMP_CONNECT_INDICATION, skb, &info);
|
||||
|
||||
/* Drop reference count - see ircomm_tty_connect_indication(). */
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_lmp_disconnect_indication (instance, sap, reason, skb)
|
||||
*
|
||||
* Peer device has closed the connection, or the link went down for some
|
||||
* other reason
|
||||
*/
|
||||
static void ircomm_lmp_disconnect_indication(void *instance, void *sap,
|
||||
LM_REASON reason,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ircomm_cb *self = (struct ircomm_cb *) instance;
|
||||
struct ircomm_info info;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
|
||||
info.reason = reason;
|
||||
|
||||
ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info);
|
||||
|
||||
/* Drop reference count - see ircomm_tty_disconnect_indication(). */
|
||||
if(skb)
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
/*
|
||||
* Function ircomm_open_lsap (self)
|
||||
*
|
||||
* Open LSAP. This function will only be used when using "raw" services
|
||||
*
|
||||
*/
|
||||
int ircomm_open_lsap(struct ircomm_cb *self)
|
||||
{
|
||||
notify_t notify;
|
||||
|
||||
/* Register callbacks */
|
||||
irda_notify_init(¬ify);
|
||||
notify.data_indication = ircomm_lmp_data_indication;
|
||||
notify.connect_confirm = ircomm_lmp_connect_confirm;
|
||||
notify.connect_indication = ircomm_lmp_connect_indication;
|
||||
notify.disconnect_indication = ircomm_lmp_disconnect_indication;
|
||||
notify.instance = self;
|
||||
strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
|
||||
|
||||
self->lsap = irlmp_open_lsap(LSAP_ANY, ¬ify, 0);
|
||||
if (!self->lsap) {
|
||||
pr_debug("%sfailed to allocate tsap\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
self->slsap_sel = self->lsap->slsap_sel;
|
||||
|
||||
/*
|
||||
* Initialize the call-table for issuing commands
|
||||
*/
|
||||
self->issue.data_request = ircomm_lmp_data_request;
|
||||
self->issue.connect_request = ircomm_lmp_connect_request;
|
||||
self->issue.connect_response = ircomm_lmp_connect_response;
|
||||
self->issue.disconnect_request = ircomm_lmp_disconnect_request;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,501 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ircomm_param.c
|
||||
* Version: 1.0
|
||||
* Description: Parameter handling for the IrCOMM protocol
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Mon Jun 7 10:25:11 1999
|
||||
* Modified at: Sun Jan 30 14:32:03 2000
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/parameters.h>
|
||||
|
||||
#include <net/irda/ircomm_core.h>
|
||||
#include <net/irda/ircomm_tty_attach.h>
|
||||
#include <net/irda/ircomm_tty.h>
|
||||
|
||||
#include <net/irda/ircomm_param.h>
|
||||
|
||||
static int ircomm_param_service_type(void *instance, irda_param_t *param,
|
||||
int get);
|
||||
static int ircomm_param_port_type(void *instance, irda_param_t *param,
|
||||
int get);
|
||||
static int ircomm_param_port_name(void *instance, irda_param_t *param,
|
||||
int get);
|
||||
static int ircomm_param_service_type(void *instance, irda_param_t *param,
|
||||
int get);
|
||||
static int ircomm_param_data_rate(void *instance, irda_param_t *param,
|
||||
int get);
|
||||
static int ircomm_param_data_format(void *instance, irda_param_t *param,
|
||||
int get);
|
||||
static int ircomm_param_flow_control(void *instance, irda_param_t *param,
|
||||
int get);
|
||||
static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get);
|
||||
static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get);
|
||||
static int ircomm_param_line_status(void *instance, irda_param_t *param,
|
||||
int get);
|
||||
static int ircomm_param_dte(void *instance, irda_param_t *param, int get);
|
||||
static int ircomm_param_dce(void *instance, irda_param_t *param, int get);
|
||||
static int ircomm_param_poll(void *instance, irda_param_t *param, int get);
|
||||
|
||||
static const pi_minor_info_t pi_minor_call_table_common[] = {
|
||||
{ ircomm_param_service_type, PV_INT_8_BITS },
|
||||
{ ircomm_param_port_type, PV_INT_8_BITS },
|
||||
{ ircomm_param_port_name, PV_STRING }
|
||||
};
|
||||
static const pi_minor_info_t pi_minor_call_table_non_raw[] = {
|
||||
{ ircomm_param_data_rate, PV_INT_32_BITS | PV_BIG_ENDIAN },
|
||||
{ ircomm_param_data_format, PV_INT_8_BITS },
|
||||
{ ircomm_param_flow_control, PV_INT_8_BITS },
|
||||
{ ircomm_param_xon_xoff, PV_INT_16_BITS },
|
||||
{ ircomm_param_enq_ack, PV_INT_16_BITS },
|
||||
{ ircomm_param_line_status, PV_INT_8_BITS }
|
||||
};
|
||||
static const pi_minor_info_t pi_minor_call_table_9_wire[] = {
|
||||
{ ircomm_param_dte, PV_INT_8_BITS },
|
||||
{ ircomm_param_dce, PV_INT_8_BITS },
|
||||
{ ircomm_param_poll, PV_NO_VALUE },
|
||||
};
|
||||
|
||||
static const pi_major_info_t pi_major_call_table[] = {
|
||||
{ pi_minor_call_table_common, 3 },
|
||||
{ pi_minor_call_table_non_raw, 6 },
|
||||
{ pi_minor_call_table_9_wire, 3 }
|
||||
/* { pi_minor_call_table_centronics } */
|
||||
};
|
||||
|
||||
pi_param_info_t ircomm_param_info = { pi_major_call_table, 3, 0x0f, 4 };
|
||||
|
||||
/*
|
||||
* Function ircomm_param_request (self, pi, flush)
|
||||
*
|
||||
* Queue a parameter for the control channel
|
||||
*
|
||||
*/
|
||||
int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct sk_buff *skb;
|
||||
int count;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
/* Make sure we don't send parameters for raw mode */
|
||||
if (self->service_type == IRCOMM_3_WIRE_RAW)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&self->spinlock, flags);
|
||||
|
||||
skb = self->ctrl_skb;
|
||||
if (!skb) {
|
||||
skb = alloc_skb(256, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
spin_unlock_irqrestore(&self->spinlock, flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
skb_reserve(skb, self->max_header_size);
|
||||
self->ctrl_skb = skb;
|
||||
}
|
||||
/*
|
||||
* Inserting is a little bit tricky since we don't know how much
|
||||
* room we will need. But this should hopefully work OK
|
||||
*/
|
||||
count = irda_param_insert(self, pi, skb_tail_pointer(skb),
|
||||
skb_tailroom(skb), &ircomm_param_info);
|
||||
if (count < 0) {
|
||||
net_warn_ratelimited("%s(), no room for parameter!\n",
|
||||
__func__);
|
||||
spin_unlock_irqrestore(&self->spinlock, flags);
|
||||
return -1;
|
||||
}
|
||||
skb_put(skb, count);
|
||||
pr_debug("%s(), skb->len=%d\n", __func__, skb->len);
|
||||
|
||||
spin_unlock_irqrestore(&self->spinlock, flags);
|
||||
|
||||
if (flush) {
|
||||
/* ircomm_tty_do_softint will take care of the rest */
|
||||
schedule_work(&self->tqueue);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_param_service_type (self, buf, len)
|
||||
*
|
||||
* Handle service type, this function will both be called after the LM-IAS
|
||||
* query and then the remote device sends its initial parameters
|
||||
*
|
||||
*/
|
||||
static int ircomm_param_service_type(void *instance, irda_param_t *param,
|
||||
int get)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
|
||||
__u8 service_type = (__u8) param->pv.i;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
if (get) {
|
||||
param->pv.i = self->settings.service_type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find all common service types */
|
||||
service_type &= self->service_type;
|
||||
if (!service_type) {
|
||||
pr_debug("%s(), No common service type to use!\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
pr_debug("%s(), services in common=%02x\n", __func__ ,
|
||||
service_type);
|
||||
|
||||
/*
|
||||
* Now choose a preferred service type of those available
|
||||
*/
|
||||
if (service_type & IRCOMM_CENTRONICS)
|
||||
self->settings.service_type = IRCOMM_CENTRONICS;
|
||||
else if (service_type & IRCOMM_9_WIRE)
|
||||
self->settings.service_type = IRCOMM_9_WIRE;
|
||||
else if (service_type & IRCOMM_3_WIRE)
|
||||
self->settings.service_type = IRCOMM_3_WIRE;
|
||||
else if (service_type & IRCOMM_3_WIRE_RAW)
|
||||
self->settings.service_type = IRCOMM_3_WIRE_RAW;
|
||||
|
||||
pr_debug("%s(), resulting service type=0x%02x\n", __func__ ,
|
||||
self->settings.service_type);
|
||||
|
||||
/*
|
||||
* Now the line is ready for some communication. Check if we are a
|
||||
* server, and send over some initial parameters.
|
||||
* Client do it in ircomm_tty_state_setup().
|
||||
* Note : we may get called from ircomm_tty_getvalue_confirm(),
|
||||
* therefore before we even have open any socket. And self->client
|
||||
* is initialised to TRUE only later. So, we check if the link is
|
||||
* really initialised. - Jean II
|
||||
*/
|
||||
if ((self->max_header_size != IRCOMM_TTY_HDR_UNINITIALISED) &&
|
||||
(!self->client) &&
|
||||
(self->settings.service_type != IRCOMM_3_WIRE_RAW))
|
||||
{
|
||||
/* Init connection */
|
||||
ircomm_tty_send_initial_parameters(self);
|
||||
ircomm_tty_link_established(self);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_param_port_type (self, param)
|
||||
*
|
||||
* The port type parameter tells if the devices are serial or parallel.
|
||||
* Since we only advertise serial service, this parameter should only
|
||||
* be equal to IRCOMM_SERIAL.
|
||||
*/
|
||||
static int ircomm_param_port_type(void *instance, irda_param_t *param, int get)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
if (get)
|
||||
param->pv.i = IRCOMM_SERIAL;
|
||||
else {
|
||||
self->settings.port_type = (__u8) param->pv.i;
|
||||
|
||||
pr_debug("%s(), port type=%d\n", __func__ ,
|
||||
self->settings.port_type);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_param_port_name (self, param)
|
||||
*
|
||||
* Exchange port name
|
||||
*
|
||||
*/
|
||||
static int ircomm_param_port_name(void *instance, irda_param_t *param, int get)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
if (get) {
|
||||
pr_debug("%s(), not imp!\n", __func__);
|
||||
} else {
|
||||
pr_debug("%s(), port-name=%s\n", __func__ , param->pv.c);
|
||||
strncpy(self->settings.port_name, param->pv.c, 32);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_param_data_rate (self, param)
|
||||
*
|
||||
* Exchange data rate to be used in this settings
|
||||
*
|
||||
*/
|
||||
static int ircomm_param_data_rate(void *instance, irda_param_t *param, int get)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
if (get)
|
||||
param->pv.i = self->settings.data_rate;
|
||||
else
|
||||
self->settings.data_rate = param->pv.i;
|
||||
|
||||
pr_debug("%s(), data rate = %d\n", __func__ , param->pv.i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_param_data_format (self, param)
|
||||
*
|
||||
* Exchange data format to be used in this settings
|
||||
*
|
||||
*/
|
||||
static int ircomm_param_data_format(void *instance, irda_param_t *param,
|
||||
int get)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
if (get)
|
||||
param->pv.i = self->settings.data_format;
|
||||
else
|
||||
self->settings.data_format = (__u8) param->pv.i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_param_flow_control (self, param)
|
||||
*
|
||||
* Exchange flow control settings to be used in this settings
|
||||
*
|
||||
*/
|
||||
static int ircomm_param_flow_control(void *instance, irda_param_t *param,
|
||||
int get)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
if (get)
|
||||
param->pv.i = self->settings.flow_control;
|
||||
else
|
||||
self->settings.flow_control = (__u8) param->pv.i;
|
||||
|
||||
pr_debug("%s(), flow control = 0x%02x\n", __func__ , (__u8)param->pv.i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_param_xon_xoff (self, param)
|
||||
*
|
||||
* Exchange XON/XOFF characters
|
||||
*
|
||||
*/
|
||||
static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
if (get) {
|
||||
param->pv.i = self->settings.xonxoff[0];
|
||||
param->pv.i |= self->settings.xonxoff[1] << 8;
|
||||
} else {
|
||||
self->settings.xonxoff[0] = (__u16) param->pv.i & 0xff;
|
||||
self->settings.xonxoff[1] = (__u16) param->pv.i >> 8;
|
||||
}
|
||||
|
||||
pr_debug("%s(), XON/XOFF = 0x%02x,0x%02x\n", __func__ ,
|
||||
param->pv.i & 0xff, param->pv.i >> 8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_param_enq_ack (self, param)
|
||||
*
|
||||
* Exchange ENQ/ACK characters
|
||||
*
|
||||
*/
|
||||
static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
if (get) {
|
||||
param->pv.i = self->settings.enqack[0];
|
||||
param->pv.i |= self->settings.enqack[1] << 8;
|
||||
} else {
|
||||
self->settings.enqack[0] = (__u16) param->pv.i & 0xff;
|
||||
self->settings.enqack[1] = (__u16) param->pv.i >> 8;
|
||||
}
|
||||
|
||||
pr_debug("%s(), ENQ/ACK = 0x%02x,0x%02x\n", __func__ ,
|
||||
param->pv.i & 0xff, param->pv.i >> 8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_param_line_status (self, param)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int ircomm_param_line_status(void *instance, irda_param_t *param,
|
||||
int get)
|
||||
{
|
||||
pr_debug("%s(), not impl.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_param_dte (instance, param)
|
||||
*
|
||||
* If we get here, there must be some sort of null-modem connection, and
|
||||
* we are probably working in server mode as well.
|
||||
*/
|
||||
static int ircomm_param_dte(void *instance, irda_param_t *param, int get)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
|
||||
__u8 dte;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
if (get)
|
||||
param->pv.i = self->settings.dte;
|
||||
else {
|
||||
dte = (__u8) param->pv.i;
|
||||
|
||||
self->settings.dce = 0;
|
||||
|
||||
if (dte & IRCOMM_DELTA_DTR)
|
||||
self->settings.dce |= (IRCOMM_DELTA_DSR|
|
||||
IRCOMM_DELTA_RI |
|
||||
IRCOMM_DELTA_CD);
|
||||
if (dte & IRCOMM_DTR)
|
||||
self->settings.dce |= (IRCOMM_DSR|
|
||||
IRCOMM_RI |
|
||||
IRCOMM_CD);
|
||||
|
||||
if (dte & IRCOMM_DELTA_RTS)
|
||||
self->settings.dce |= IRCOMM_DELTA_CTS;
|
||||
if (dte & IRCOMM_RTS)
|
||||
self->settings.dce |= IRCOMM_CTS;
|
||||
|
||||
/* Take appropriate actions */
|
||||
ircomm_tty_check_modem_status(self);
|
||||
|
||||
/* Null modem cable emulator */
|
||||
self->settings.null_modem = TRUE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_param_dce (instance, param)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int ircomm_param_dce(void *instance, irda_param_t *param, int get)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
|
||||
__u8 dce;
|
||||
|
||||
pr_debug("%s(), dce = 0x%02x\n", __func__ , (__u8)param->pv.i);
|
||||
|
||||
dce = (__u8) param->pv.i;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
self->settings.dce = dce;
|
||||
|
||||
/* Check if any of the settings have changed */
|
||||
if (dce & 0x0f) {
|
||||
if (dce & IRCOMM_DELTA_CTS) {
|
||||
pr_debug("%s(), CTS\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
ircomm_tty_check_modem_status(self);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_param_poll (instance, param)
|
||||
*
|
||||
* Called when the peer device is polling for the line settings
|
||||
*
|
||||
*/
|
||||
static int ircomm_param_poll(void *instance, irda_param_t *param, int get)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
/* Poll parameters are always of length 0 (just a signal) */
|
||||
if (!get) {
|
||||
/* Respond with DTE line settings */
|
||||
ircomm_param_request(self, IRCOMM_DTE, TRUE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,350 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ircomm_ttp.c
|
||||
* Version: 1.0
|
||||
* Description: Interface between IrCOMM and IrTTP
|
||||
* Status: Stable
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sun Jun 6 20:48:27 1999
|
||||
* Modified at: Mon Dec 13 11:35:13 1999
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
|
||||
* Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irlmp.h>
|
||||
#include <net/irda/iriap.h>
|
||||
#include <net/irda/irttp.h>
|
||||
|
||||
#include <net/irda/ircomm_event.h>
|
||||
#include <net/irda/ircomm_ttp.h>
|
||||
|
||||
static int ircomm_ttp_data_indication(void *instance, void *sap,
|
||||
struct sk_buff *skb);
|
||||
static void ircomm_ttp_connect_confirm(void *instance, void *sap,
|
||||
struct qos_info *qos,
|
||||
__u32 max_sdu_size,
|
||||
__u8 max_header_size,
|
||||
struct sk_buff *skb);
|
||||
static void ircomm_ttp_connect_indication(void *instance, void *sap,
|
||||
struct qos_info *qos,
|
||||
__u32 max_sdu_size,
|
||||
__u8 max_header_size,
|
||||
struct sk_buff *skb);
|
||||
static void ircomm_ttp_flow_indication(void *instance, void *sap,
|
||||
LOCAL_FLOW cmd);
|
||||
static void ircomm_ttp_disconnect_indication(void *instance, void *sap,
|
||||
LM_REASON reason,
|
||||
struct sk_buff *skb);
|
||||
static int ircomm_ttp_data_request(struct ircomm_cb *self,
|
||||
struct sk_buff *skb,
|
||||
int clen);
|
||||
static int ircomm_ttp_connect_request(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata,
|
||||
struct ircomm_info *info);
|
||||
static int ircomm_ttp_connect_response(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata);
|
||||
static int ircomm_ttp_disconnect_request(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata,
|
||||
struct ircomm_info *info);
|
||||
|
||||
/*
|
||||
* Function ircomm_open_tsap (self)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
int ircomm_open_tsap(struct ircomm_cb *self)
|
||||
{
|
||||
notify_t notify;
|
||||
|
||||
/* Register callbacks */
|
||||
irda_notify_init(¬ify);
|
||||
notify.data_indication = ircomm_ttp_data_indication;
|
||||
notify.connect_confirm = ircomm_ttp_connect_confirm;
|
||||
notify.connect_indication = ircomm_ttp_connect_indication;
|
||||
notify.flow_indication = ircomm_ttp_flow_indication;
|
||||
notify.disconnect_indication = ircomm_ttp_disconnect_indication;
|
||||
notify.instance = self;
|
||||
strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
|
||||
|
||||
self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
|
||||
¬ify);
|
||||
if (!self->tsap) {
|
||||
pr_debug("%sfailed to allocate tsap\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
self->slsap_sel = self->tsap->stsap_sel;
|
||||
|
||||
/*
|
||||
* Initialize the call-table for issuing commands
|
||||
*/
|
||||
self->issue.data_request = ircomm_ttp_data_request;
|
||||
self->issue.connect_request = ircomm_ttp_connect_request;
|
||||
self->issue.connect_response = ircomm_ttp_connect_response;
|
||||
self->issue.disconnect_request = ircomm_ttp_disconnect_request;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_ttp_connect_request (self, userdata)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int ircomm_ttp_connect_request(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata,
|
||||
struct ircomm_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Don't forget to refcount it - should be NULL anyway */
|
||||
if(userdata)
|
||||
skb_get(userdata);
|
||||
|
||||
ret = irttp_connect_request(self->tsap, info->dlsap_sel,
|
||||
info->saddr, info->daddr, NULL,
|
||||
TTP_SAR_DISABLE, userdata);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_ttp_connect_response (self, skb)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int ircomm_ttp_connect_response(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Don't forget to refcount it - should be NULL anyway */
|
||||
if(userdata)
|
||||
skb_get(userdata);
|
||||
|
||||
ret = irttp_connect_response(self->tsap, TTP_SAR_DISABLE, userdata);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_ttp_data_request (self, userdata)
|
||||
*
|
||||
* Send IrCOMM data to IrTTP layer. Currently we do not try to combine
|
||||
* control data with pure data, so they will be sent as separate frames.
|
||||
* Should not be a big problem though, since control frames are rare. But
|
||||
* some of them are sent after connection establishment, so this can
|
||||
* increase the latency a bit.
|
||||
*/
|
||||
static int ircomm_ttp_data_request(struct ircomm_cb *self,
|
||||
struct sk_buff *skb,
|
||||
int clen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
IRDA_ASSERT(skb != NULL, return -1;);
|
||||
|
||||
pr_debug("%s(), clen=%d\n", __func__ , clen);
|
||||
|
||||
/*
|
||||
* Insert clen field, currently we either send data only, or control
|
||||
* only frames, to make things easier and avoid queueing
|
||||
*/
|
||||
IRDA_ASSERT(skb_headroom(skb) >= IRCOMM_HEADER_SIZE, return -1;);
|
||||
|
||||
/* Don't forget to refcount it - see ircomm_tty_do_softint() */
|
||||
skb_get(skb);
|
||||
|
||||
skb_push(skb, IRCOMM_HEADER_SIZE);
|
||||
|
||||
skb->data[0] = clen;
|
||||
|
||||
ret = irttp_data_request(self->tsap, skb);
|
||||
if (ret) {
|
||||
net_err_ratelimited("%s(), failed\n", __func__);
|
||||
/* irttp_data_request already free the packet */
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_ttp_data_indication (instance, sap, skb)
|
||||
*
|
||||
* Incoming data
|
||||
*
|
||||
*/
|
||||
static int ircomm_ttp_data_indication(void *instance, void *sap,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ircomm_cb *self = (struct ircomm_cb *) instance;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
|
||||
IRDA_ASSERT(skb != NULL, return -1;);
|
||||
|
||||
ircomm_do_event(self, IRCOMM_TTP_DATA_INDICATION, skb, NULL);
|
||||
|
||||
/* Drop reference count - see ircomm_tty_data_indication(). */
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ircomm_ttp_connect_confirm(void *instance, void *sap,
|
||||
struct qos_info *qos,
|
||||
__u32 max_sdu_size,
|
||||
__u8 max_header_size,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ircomm_cb *self = (struct ircomm_cb *) instance;
|
||||
struct ircomm_info info;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
IRDA_ASSERT(skb != NULL, return;);
|
||||
IRDA_ASSERT(qos != NULL, goto out;);
|
||||
|
||||
if (max_sdu_size != TTP_SAR_DISABLE) {
|
||||
net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
info.max_data_size = irttp_get_max_seg_size(self->tsap)
|
||||
- IRCOMM_HEADER_SIZE;
|
||||
info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE;
|
||||
info.qos = qos;
|
||||
|
||||
ircomm_do_event(self, IRCOMM_TTP_CONNECT_CONFIRM, skb, &info);
|
||||
|
||||
out:
|
||||
/* Drop reference count - see ircomm_tty_connect_confirm(). */
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_ttp_connect_indication (instance, sap, qos, max_sdu_size,
|
||||
* max_header_size, skb)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static void ircomm_ttp_connect_indication(void *instance, void *sap,
|
||||
struct qos_info *qos,
|
||||
__u32 max_sdu_size,
|
||||
__u8 max_header_size,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ircomm_cb *self = (struct ircomm_cb *)instance;
|
||||
struct ircomm_info info;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
IRDA_ASSERT(skb != NULL, return;);
|
||||
IRDA_ASSERT(qos != NULL, goto out;);
|
||||
|
||||
if (max_sdu_size != TTP_SAR_DISABLE) {
|
||||
net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
info.max_data_size = irttp_get_max_seg_size(self->tsap)
|
||||
- IRCOMM_HEADER_SIZE;
|
||||
info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE;
|
||||
info.qos = qos;
|
||||
|
||||
ircomm_do_event(self, IRCOMM_TTP_CONNECT_INDICATION, skb, &info);
|
||||
|
||||
out:
|
||||
/* Drop reference count - see ircomm_tty_connect_indication(). */
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_ttp_disconnect_request (self, userdata, info)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int ircomm_ttp_disconnect_request(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata,
|
||||
struct ircomm_info *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Don't forget to refcount it - should be NULL anyway */
|
||||
if(userdata)
|
||||
skb_get(userdata);
|
||||
|
||||
ret = irttp_disconnect_request(self->tsap, userdata, P_NORMAL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_ttp_disconnect_indication (instance, sap, reason, skb)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static void ircomm_ttp_disconnect_indication(void *instance, void *sap,
|
||||
LM_REASON reason,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ircomm_cb *self = (struct ircomm_cb *) instance;
|
||||
struct ircomm_info info;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
|
||||
info.reason = reason;
|
||||
|
||||
ircomm_do_event(self, IRCOMM_TTP_DISCONNECT_INDICATION, skb, &info);
|
||||
|
||||
/* Drop reference count - see ircomm_tty_disconnect_indication(). */
|
||||
if(skb)
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_ttp_flow_indication (instance, sap, cmd)
|
||||
*
|
||||
* Layer below is telling us to start or stop the flow of data
|
||||
*
|
||||
*/
|
||||
static void ircomm_ttp_flow_indication(void *instance, void *sap,
|
||||
LOCAL_FLOW cmd)
|
||||
{
|
||||
struct ircomm_cb *self = (struct ircomm_cb *) instance;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
|
||||
if (self->notify.flow_indication)
|
||||
self->notify.flow_indication(self->notify.instance, self, cmd);
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,987 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ircomm_tty_attach.c
|
||||
* Version:
|
||||
* Description: Code for attaching the serial driver to IrCOMM
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sat Jun 5 17:42:00 1999
|
||||
* Modified at: Tue Jan 4 14:20:49 2000
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
|
||||
* Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irlmp.h>
|
||||
#include <net/irda/iriap.h>
|
||||
#include <net/irda/irttp.h>
|
||||
#include <net/irda/irias_object.h>
|
||||
#include <net/irda/parameters.h>
|
||||
|
||||
#include <net/irda/ircomm_core.h>
|
||||
#include <net/irda/ircomm_param.h>
|
||||
#include <net/irda/ircomm_event.h>
|
||||
|
||||
#include <net/irda/ircomm_tty.h>
|
||||
#include <net/irda/ircomm_tty_attach.h>
|
||||
|
||||
static void ircomm_tty_ias_register(struct ircomm_tty_cb *self);
|
||||
static void ircomm_tty_discovery_indication(discinfo_t *discovery,
|
||||
DISCOVERY_MODE mode,
|
||||
void *priv);
|
||||
static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
|
||||
struct ias_value *value, void *priv);
|
||||
static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self,
|
||||
int timeout);
|
||||
static void ircomm_tty_watchdog_timer_expired(struct timer_list *timer);
|
||||
|
||||
static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
|
||||
IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb,
|
||||
struct ircomm_tty_info *info);
|
||||
static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
|
||||
IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb,
|
||||
struct ircomm_tty_info *info);
|
||||
static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
|
||||
IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb,
|
||||
struct ircomm_tty_info *info);
|
||||
static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
|
||||
IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb,
|
||||
struct ircomm_tty_info *info);
|
||||
static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
|
||||
IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb,
|
||||
struct ircomm_tty_info *info);
|
||||
static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
|
||||
IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb,
|
||||
struct ircomm_tty_info *info);
|
||||
|
||||
const char *const ircomm_tty_state[] = {
|
||||
"IRCOMM_TTY_IDLE",
|
||||
"IRCOMM_TTY_SEARCH",
|
||||
"IRCOMM_TTY_QUERY_PARAMETERS",
|
||||
"IRCOMM_TTY_QUERY_LSAP_SEL",
|
||||
"IRCOMM_TTY_SETUP",
|
||||
"IRCOMM_TTY_READY",
|
||||
"*** ERROR *** ",
|
||||
};
|
||||
|
||||
static const char *const ircomm_tty_event[] __maybe_unused = {
|
||||
"IRCOMM_TTY_ATTACH_CABLE",
|
||||
"IRCOMM_TTY_DETACH_CABLE",
|
||||
"IRCOMM_TTY_DATA_REQUEST",
|
||||
"IRCOMM_TTY_DATA_INDICATION",
|
||||
"IRCOMM_TTY_DISCOVERY_REQUEST",
|
||||
"IRCOMM_TTY_DISCOVERY_INDICATION",
|
||||
"IRCOMM_TTY_CONNECT_CONFIRM",
|
||||
"IRCOMM_TTY_CONNECT_INDICATION",
|
||||
"IRCOMM_TTY_DISCONNECT_REQUEST",
|
||||
"IRCOMM_TTY_DISCONNECT_INDICATION",
|
||||
"IRCOMM_TTY_WD_TIMER_EXPIRED",
|
||||
"IRCOMM_TTY_GOT_PARAMETERS",
|
||||
"IRCOMM_TTY_GOT_LSAPSEL",
|
||||
"*** ERROR ****",
|
||||
};
|
||||
|
||||
static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_tty_info *info) =
|
||||
{
|
||||
ircomm_tty_state_idle,
|
||||
ircomm_tty_state_search,
|
||||
ircomm_tty_state_query_parameters,
|
||||
ircomm_tty_state_query_lsap_sel,
|
||||
ircomm_tty_state_setup,
|
||||
ircomm_tty_state_ready,
|
||||
};
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_attach_cable (driver)
|
||||
*
|
||||
* Try to attach cable (IrCOMM link). This function will only return
|
||||
* when the link has been connected, or if an error condition occurs.
|
||||
* If success, the return value is the resulting service type.
|
||||
*/
|
||||
int ircomm_tty_attach_cable(struct ircomm_tty_cb *self)
|
||||
{
|
||||
struct tty_struct *tty;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
/* Check if somebody has already connected to us */
|
||||
if (ircomm_is_connected(self->ircomm)) {
|
||||
pr_debug("%s(), already connected!\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Make sure nobody tries to write before the link is up */
|
||||
tty = tty_port_tty_get(&self->port);
|
||||
if (tty) {
|
||||
tty->hw_stopped = 1;
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
|
||||
ircomm_tty_ias_register(self);
|
||||
|
||||
ircomm_tty_do_event(self, IRCOMM_TTY_ATTACH_CABLE, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_detach_cable (driver)
|
||||
*
|
||||
* Detach cable, or cable has been detached by peer
|
||||
*
|
||||
*/
|
||||
void ircomm_tty_detach_cable(struct ircomm_tty_cb *self)
|
||||
{
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
|
||||
|
||||
del_timer(&self->watchdog_timer);
|
||||
|
||||
/* Remove discovery handler */
|
||||
if (self->ckey) {
|
||||
irlmp_unregister_client(self->ckey);
|
||||
self->ckey = NULL;
|
||||
}
|
||||
/* Remove IrCOMM hint bits */
|
||||
if (self->skey) {
|
||||
irlmp_unregister_service(self->skey);
|
||||
self->skey = NULL;
|
||||
}
|
||||
|
||||
if (self->iriap) {
|
||||
iriap_close(self->iriap);
|
||||
self->iriap = NULL;
|
||||
}
|
||||
|
||||
/* Remove LM-IAS object */
|
||||
if (self->obj) {
|
||||
irias_delete_object(self->obj);
|
||||
self->obj = NULL;
|
||||
}
|
||||
|
||||
ircomm_tty_do_event(self, IRCOMM_TTY_DETACH_CABLE, NULL, NULL);
|
||||
|
||||
/* Reset some values */
|
||||
self->daddr = self->saddr = 0;
|
||||
self->dlsap_sel = self->slsap_sel = 0;
|
||||
|
||||
memset(&self->settings, 0, sizeof(struct ircomm_params));
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_ias_register (self)
|
||||
*
|
||||
* Register with LM-IAS depending on which service type we are
|
||||
*
|
||||
*/
|
||||
static void ircomm_tty_ias_register(struct ircomm_tty_cb *self)
|
||||
{
|
||||
__u8 oct_seq[6];
|
||||
__u16 hints;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
|
||||
|
||||
/* Compute hint bits based on service */
|
||||
hints = irlmp_service_to_hint(S_COMM);
|
||||
if (self->service_type & IRCOMM_3_WIRE_RAW)
|
||||
hints |= irlmp_service_to_hint(S_PRINTER);
|
||||
|
||||
/* Advertise IrCOMM hint bit in discovery */
|
||||
if (!self->skey)
|
||||
self->skey = irlmp_register_service(hints);
|
||||
/* Set up a discovery handler */
|
||||
if (!self->ckey)
|
||||
self->ckey = irlmp_register_client(hints,
|
||||
ircomm_tty_discovery_indication,
|
||||
NULL, (void *) self);
|
||||
|
||||
/* If already done, no need to do it again */
|
||||
if (self->obj)
|
||||
return;
|
||||
|
||||
if (self->service_type & IRCOMM_3_WIRE_RAW) {
|
||||
/* Register IrLPT with LM-IAS */
|
||||
self->obj = irias_new_object("IrLPT", IAS_IRLPT_ID);
|
||||
irias_add_integer_attrib(self->obj, "IrDA:IrLMP:LsapSel",
|
||||
self->slsap_sel, IAS_KERNEL_ATTR);
|
||||
} else {
|
||||
/* Register IrCOMM with LM-IAS */
|
||||
self->obj = irias_new_object("IrDA:IrCOMM", IAS_IRCOMM_ID);
|
||||
irias_add_integer_attrib(self->obj, "IrDA:TinyTP:LsapSel",
|
||||
self->slsap_sel, IAS_KERNEL_ATTR);
|
||||
|
||||
/* Code the parameters into the buffer */
|
||||
irda_param_pack(oct_seq, "bbbbbb",
|
||||
IRCOMM_SERVICE_TYPE, 1, self->service_type,
|
||||
IRCOMM_PORT_TYPE, 1, IRCOMM_SERIAL);
|
||||
|
||||
/* Register parameters with LM-IAS */
|
||||
irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6,
|
||||
IAS_KERNEL_ATTR);
|
||||
}
|
||||
irias_insert_object(self->obj);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_ias_unregister (self)
|
||||
*
|
||||
* Remove our IAS object and client hook while connected.
|
||||
*
|
||||
*/
|
||||
static void ircomm_tty_ias_unregister(struct ircomm_tty_cb *self)
|
||||
{
|
||||
/* Remove LM-IAS object now so it is not reused.
|
||||
* IrCOMM deals very poorly with multiple incoming connections.
|
||||
* It should looks a lot more like IrNET, and "dup" a server TSAP
|
||||
* to the application TSAP (based on various rules).
|
||||
* This is a cheap workaround allowing multiple clients to
|
||||
* connect to us. It will not always work.
|
||||
* Each IrCOMM socket has an IAS entry. Incoming connection will
|
||||
* pick the first one found. So, when we are fully connected,
|
||||
* we remove our IAS entries so that the next IAS entry is used.
|
||||
* We do that for *both* client and server, because a server
|
||||
* can also create client instances.
|
||||
* Jean II */
|
||||
if (self->obj) {
|
||||
irias_delete_object(self->obj);
|
||||
self->obj = NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Remove discovery handler.
|
||||
* While we are connected, we no longer need to receive
|
||||
* discovery events. This would be the case if there is
|
||||
* multiple IrLAP interfaces. Jean II */
|
||||
if (self->ckey) {
|
||||
irlmp_unregister_client(self->ckey);
|
||||
self->ckey = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_send_initial_parameters (self)
|
||||
*
|
||||
* Send initial parameters to the remote IrCOMM device. These parameters
|
||||
* must be sent before any data.
|
||||
*/
|
||||
int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self)
|
||||
{
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
if (self->service_type & IRCOMM_3_WIRE_RAW)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Set default values, but only if the application for some reason
|
||||
* haven't set them already
|
||||
*/
|
||||
pr_debug("%s(), data-rate = %d\n", __func__ ,
|
||||
self->settings.data_rate);
|
||||
if (!self->settings.data_rate)
|
||||
self->settings.data_rate = 9600;
|
||||
pr_debug("%s(), data-format = %d\n", __func__ ,
|
||||
self->settings.data_format);
|
||||
if (!self->settings.data_format)
|
||||
self->settings.data_format = IRCOMM_WSIZE_8; /* 8N1 */
|
||||
|
||||
pr_debug("%s(), flow-control = %d\n", __func__ ,
|
||||
self->settings.flow_control);
|
||||
/*self->settings.flow_control = IRCOMM_RTS_CTS_IN|IRCOMM_RTS_CTS_OUT;*/
|
||||
|
||||
/* Do not set delta values for the initial parameters */
|
||||
self->settings.dte = IRCOMM_DTR | IRCOMM_RTS;
|
||||
|
||||
/* Only send service type parameter when we are the client */
|
||||
if (self->client)
|
||||
ircomm_param_request(self, IRCOMM_SERVICE_TYPE, FALSE);
|
||||
ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE);
|
||||
ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
|
||||
|
||||
/* For a 3 wire service, we just flush the last parameter and return */
|
||||
if (self->settings.service_type == IRCOMM_3_WIRE) {
|
||||
ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Only 9-wire service types continue here */
|
||||
ircomm_param_request(self, IRCOMM_FLOW_CONTROL, FALSE);
|
||||
#if 0
|
||||
ircomm_param_request(self, IRCOMM_XON_XOFF, FALSE);
|
||||
ircomm_param_request(self, IRCOMM_ENQ_ACK, FALSE);
|
||||
#endif
|
||||
/* Notify peer that we are ready to receive data */
|
||||
ircomm_param_request(self, IRCOMM_DTE, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_discovery_indication (discovery)
|
||||
*
|
||||
* Remote device is discovered, try query the remote IAS to see which
|
||||
* device it is, and which services it has.
|
||||
*
|
||||
*/
|
||||
static void ircomm_tty_discovery_indication(discinfo_t *discovery,
|
||||
DISCOVERY_MODE mode,
|
||||
void *priv)
|
||||
{
|
||||
struct ircomm_tty_cb *self;
|
||||
struct ircomm_tty_info info;
|
||||
|
||||
/* Important note :
|
||||
* We need to drop all passive discoveries.
|
||||
* The LSAP management of IrComm is deficient and doesn't deal
|
||||
* with the case of two instance connecting to each other
|
||||
* simultaneously (it will deadlock in LMP).
|
||||
* The proper fix would be to use the same technique as in IrNET,
|
||||
* to have one server socket and separate instances for the
|
||||
* connecting/connected socket.
|
||||
* The workaround is to drop passive discovery, which drastically
|
||||
* reduce the probability of this happening.
|
||||
* Jean II */
|
||||
if(mode == DISCOVERY_PASSIVE)
|
||||
return;
|
||||
|
||||
info.daddr = discovery->daddr;
|
||||
info.saddr = discovery->saddr;
|
||||
|
||||
self = priv;
|
||||
ircomm_tty_do_event(self, IRCOMM_TTY_DISCOVERY_INDICATION,
|
||||
NULL, &info);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_disconnect_indication (instance, sap, reason, skb)
|
||||
*
|
||||
* Link disconnected
|
||||
*
|
||||
*/
|
||||
void ircomm_tty_disconnect_indication(void *instance, void *sap,
|
||||
LM_REASON reason,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
|
||||
struct tty_struct *tty;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
|
||||
|
||||
tty = tty_port_tty_get(&self->port);
|
||||
if (!tty)
|
||||
return;
|
||||
|
||||
/* This will stop control data transfers */
|
||||
self->flow = FLOW_STOP;
|
||||
|
||||
/* Stop data transfers */
|
||||
tty->hw_stopped = 1;
|
||||
|
||||
ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL,
|
||||
NULL);
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_getvalue_confirm (result, obj_id, value, priv)
|
||||
*
|
||||
* Got result from the IAS query we make
|
||||
*
|
||||
*/
|
||||
static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
|
||||
struct ias_value *value,
|
||||
void *priv)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
|
||||
|
||||
/* We probably don't need to make any more queries */
|
||||
iriap_close(self->iriap);
|
||||
self->iriap = NULL;
|
||||
|
||||
/* Check if request succeeded */
|
||||
if (result != IAS_SUCCESS) {
|
||||
pr_debug("%s(), got NULL value!\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (value->type) {
|
||||
case IAS_OCT_SEQ:
|
||||
pr_debug("%s(), got octet sequence\n", __func__);
|
||||
|
||||
irda_param_extract_all(self, value->t.oct_seq, value->len,
|
||||
&ircomm_param_info);
|
||||
|
||||
ircomm_tty_do_event(self, IRCOMM_TTY_GOT_PARAMETERS, NULL,
|
||||
NULL);
|
||||
break;
|
||||
case IAS_INTEGER:
|
||||
/* Got LSAP selector */
|
||||
pr_debug("%s(), got lsapsel = %d\n", __func__ ,
|
||||
value->t.integer);
|
||||
|
||||
if (value->t.integer == -1) {
|
||||
pr_debug("%s(), invalid value!\n", __func__);
|
||||
} else
|
||||
self->dlsap_sel = value->t.integer;
|
||||
|
||||
ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL);
|
||||
break;
|
||||
case IAS_MISSING:
|
||||
pr_debug("%s(), got IAS_MISSING\n", __func__);
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s(), got unknown type!\n", __func__);
|
||||
break;
|
||||
}
|
||||
irias_delete_value(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_connect_confirm (instance, sap, qos, max_sdu_size, skb)
|
||||
*
|
||||
* Connection confirmed
|
||||
*
|
||||
*/
|
||||
void ircomm_tty_connect_confirm(void *instance, void *sap,
|
||||
struct qos_info *qos,
|
||||
__u32 max_data_size,
|
||||
__u8 max_header_size,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
|
||||
|
||||
self->client = TRUE;
|
||||
self->max_data_size = max_data_size;
|
||||
self->max_header_size = max_header_size;
|
||||
self->flow = FLOW_START;
|
||||
|
||||
ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL);
|
||||
|
||||
/* No need to kfree_skb - see ircomm_ttp_connect_confirm() */
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_connect_indication (instance, sap, qos, max_sdu_size,
|
||||
* skb)
|
||||
*
|
||||
* we are discovered and being requested to connect by remote device !
|
||||
*
|
||||
*/
|
||||
void ircomm_tty_connect_indication(void *instance, void *sap,
|
||||
struct qos_info *qos,
|
||||
__u32 max_data_size,
|
||||
__u8 max_header_size,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
|
||||
int clen;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
|
||||
|
||||
self->client = FALSE;
|
||||
self->max_data_size = max_data_size;
|
||||
self->max_header_size = max_header_size;
|
||||
self->flow = FLOW_START;
|
||||
|
||||
clen = skb->data[0];
|
||||
if (clen)
|
||||
irda_param_extract_all(self, skb->data+1,
|
||||
IRDA_MIN(skb->len, clen),
|
||||
&ircomm_param_info);
|
||||
|
||||
ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL);
|
||||
|
||||
/* No need to kfree_skb - see ircomm_ttp_connect_indication() */
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_link_established (self)
|
||||
*
|
||||
* Called when the IrCOMM link is established
|
||||
*
|
||||
*/
|
||||
void ircomm_tty_link_established(struct ircomm_tty_cb *self)
|
||||
{
|
||||
struct tty_struct *tty;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
|
||||
|
||||
tty = tty_port_tty_get(&self->port);
|
||||
if (!tty)
|
||||
return;
|
||||
|
||||
del_timer(&self->watchdog_timer);
|
||||
|
||||
/*
|
||||
* IrCOMM link is now up, and if we are not using hardware
|
||||
* flow-control, then declare the hardware as running. Otherwise we
|
||||
* will have to wait for the peer device (DCE) to raise the CTS
|
||||
* line.
|
||||
*/
|
||||
if (tty_port_cts_enabled(&self->port) &&
|
||||
((self->settings.dce & IRCOMM_CTS) == 0)) {
|
||||
pr_debug("%s(), waiting for CTS ...\n", __func__);
|
||||
goto put;
|
||||
} else {
|
||||
pr_debug("%s(), starting hardware!\n", __func__);
|
||||
|
||||
tty->hw_stopped = 0;
|
||||
|
||||
/* Wake up processes blocked on open */
|
||||
wake_up_interruptible(&self->port.open_wait);
|
||||
}
|
||||
|
||||
schedule_work(&self->tqueue);
|
||||
put:
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_start_watchdog_timer (self, timeout)
|
||||
*
|
||||
* Start the watchdog timer. This timer is used to make sure that any
|
||||
* connection attempt is successful, and if not, we will retry after
|
||||
* the timeout
|
||||
*/
|
||||
static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self,
|
||||
int timeout)
|
||||
{
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
|
||||
|
||||
irda_start_timer(&self->watchdog_timer, timeout,
|
||||
ircomm_tty_watchdog_timer_expired);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_watchdog_timer_expired (data)
|
||||
*
|
||||
* Called when the connect procedure have taken to much time.
|
||||
*
|
||||
*/
|
||||
static void ircomm_tty_watchdog_timer_expired(struct timer_list *t)
|
||||
{
|
||||
struct ircomm_tty_cb *self = from_timer(self, t, watchdog_timer);
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
|
||||
|
||||
ircomm_tty_do_event(self, IRCOMM_TTY_WD_TIMER_EXPIRED, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_do_event (self, event, skb)
|
||||
*
|
||||
* Process event
|
||||
*
|
||||
*/
|
||||
int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_tty_info *info)
|
||||
{
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
pr_debug("%s: state=%s, event=%s\n", __func__ ,
|
||||
ircomm_tty_state[self->state], ircomm_tty_event[event]);
|
||||
|
||||
return (*state[self->state])(self, event, skb, info);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_next_state (self, state)
|
||||
*
|
||||
* Switch state
|
||||
*
|
||||
*/
|
||||
static inline void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_STATE state)
|
||||
{
|
||||
/*
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
|
||||
|
||||
pr_debug("%s: next state=%s, service type=%d\n", __func__ ,
|
||||
ircomm_tty_state[self->state], self->service_type);
|
||||
*/
|
||||
self->state = state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_state_idle (self, event, skb, info)
|
||||
*
|
||||
* Just hanging around
|
||||
*
|
||||
*/
|
||||
static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
|
||||
IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb,
|
||||
struct ircomm_tty_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s: state=%s, event=%s\n", __func__ ,
|
||||
ircomm_tty_state[self->state], ircomm_tty_event[event]);
|
||||
switch (event) {
|
||||
case IRCOMM_TTY_ATTACH_CABLE:
|
||||
/* Try to discover any remote devices */
|
||||
ircomm_tty_start_watchdog_timer(self, 3*HZ);
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
|
||||
|
||||
irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
|
||||
break;
|
||||
case IRCOMM_TTY_DISCOVERY_INDICATION:
|
||||
self->daddr = info->daddr;
|
||||
self->saddr = info->saddr;
|
||||
|
||||
if (self->iriap) {
|
||||
net_warn_ratelimited("%s(), busy with a previous query\n",
|
||||
__func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
|
||||
ircomm_tty_getvalue_confirm);
|
||||
|
||||
iriap_getvaluebyclass_request(self->iriap,
|
||||
self->saddr, self->daddr,
|
||||
"IrDA:IrCOMM", "Parameters");
|
||||
|
||||
ircomm_tty_start_watchdog_timer(self, 3*HZ);
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
|
||||
break;
|
||||
case IRCOMM_TTY_CONNECT_INDICATION:
|
||||
del_timer(&self->watchdog_timer);
|
||||
|
||||
/* Accept connection */
|
||||
ircomm_connect_response(self->ircomm, NULL);
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
|
||||
break;
|
||||
case IRCOMM_TTY_WD_TIMER_EXPIRED:
|
||||
/* Just stay idle */
|
||||
break;
|
||||
case IRCOMM_TTY_DETACH_CABLE:
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s(), unknown event: %s\n", __func__ ,
|
||||
ircomm_tty_event[event]);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_state_search (self, event, skb, info)
|
||||
*
|
||||
* Trying to discover an IrCOMM device
|
||||
*
|
||||
*/
|
||||
static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
|
||||
IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb,
|
||||
struct ircomm_tty_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s: state=%s, event=%s\n", __func__ ,
|
||||
ircomm_tty_state[self->state], ircomm_tty_event[event]);
|
||||
|
||||
switch (event) {
|
||||
case IRCOMM_TTY_DISCOVERY_INDICATION:
|
||||
self->daddr = info->daddr;
|
||||
self->saddr = info->saddr;
|
||||
|
||||
if (self->iriap) {
|
||||
net_warn_ratelimited("%s(), busy with a previous query\n",
|
||||
__func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
|
||||
ircomm_tty_getvalue_confirm);
|
||||
|
||||
if (self->service_type == IRCOMM_3_WIRE_RAW) {
|
||||
iriap_getvaluebyclass_request(self->iriap, self->saddr,
|
||||
self->daddr, "IrLPT",
|
||||
"IrDA:IrLMP:LsapSel");
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
|
||||
} else {
|
||||
iriap_getvaluebyclass_request(self->iriap, self->saddr,
|
||||
self->daddr,
|
||||
"IrDA:IrCOMM",
|
||||
"Parameters");
|
||||
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
|
||||
}
|
||||
ircomm_tty_start_watchdog_timer(self, 3*HZ);
|
||||
break;
|
||||
case IRCOMM_TTY_CONNECT_INDICATION:
|
||||
del_timer(&self->watchdog_timer);
|
||||
ircomm_tty_ias_unregister(self);
|
||||
|
||||
/* Accept connection */
|
||||
ircomm_connect_response(self->ircomm, NULL);
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
|
||||
break;
|
||||
case IRCOMM_TTY_WD_TIMER_EXPIRED:
|
||||
#if 1
|
||||
/* Give up */
|
||||
#else
|
||||
/* Try to discover any remote devices */
|
||||
ircomm_tty_start_watchdog_timer(self, 3*HZ);
|
||||
irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
|
||||
#endif
|
||||
break;
|
||||
case IRCOMM_TTY_DETACH_CABLE:
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s(), unknown event: %s\n", __func__ ,
|
||||
ircomm_tty_event[event]);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_state_query (self, event, skb, info)
|
||||
*
|
||||
* Querying the remote LM-IAS for IrCOMM parameters
|
||||
*
|
||||
*/
|
||||
static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
|
||||
IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb,
|
||||
struct ircomm_tty_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s: state=%s, event=%s\n", __func__ ,
|
||||
ircomm_tty_state[self->state], ircomm_tty_event[event]);
|
||||
|
||||
switch (event) {
|
||||
case IRCOMM_TTY_GOT_PARAMETERS:
|
||||
if (self->iriap) {
|
||||
net_warn_ratelimited("%s(), busy with a previous query\n",
|
||||
__func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
|
||||
ircomm_tty_getvalue_confirm);
|
||||
|
||||
iriap_getvaluebyclass_request(self->iriap, self->saddr,
|
||||
self->daddr, "IrDA:IrCOMM",
|
||||
"IrDA:TinyTP:LsapSel");
|
||||
|
||||
ircomm_tty_start_watchdog_timer(self, 3*HZ);
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
|
||||
break;
|
||||
case IRCOMM_TTY_WD_TIMER_EXPIRED:
|
||||
/* Go back to search mode */
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
|
||||
ircomm_tty_start_watchdog_timer(self, 3*HZ);
|
||||
break;
|
||||
case IRCOMM_TTY_CONNECT_INDICATION:
|
||||
del_timer(&self->watchdog_timer);
|
||||
ircomm_tty_ias_unregister(self);
|
||||
|
||||
/* Accept connection */
|
||||
ircomm_connect_response(self->ircomm, NULL);
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
|
||||
break;
|
||||
case IRCOMM_TTY_DETACH_CABLE:
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s(), unknown event: %s\n", __func__ ,
|
||||
ircomm_tty_event[event]);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_state_query_lsap_sel (self, event, skb, info)
|
||||
*
|
||||
* Query remote LM-IAS for the LSAP selector which we can connect to
|
||||
*
|
||||
*/
|
||||
static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
|
||||
IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb,
|
||||
struct ircomm_tty_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s: state=%s, event=%s\n", __func__ ,
|
||||
ircomm_tty_state[self->state], ircomm_tty_event[event]);
|
||||
|
||||
switch (event) {
|
||||
case IRCOMM_TTY_GOT_LSAPSEL:
|
||||
/* Connect to remote device */
|
||||
ret = ircomm_connect_request(self->ircomm, self->dlsap_sel,
|
||||
self->saddr, self->daddr,
|
||||
NULL, self->service_type);
|
||||
ircomm_tty_start_watchdog_timer(self, 3*HZ);
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_SETUP);
|
||||
break;
|
||||
case IRCOMM_TTY_WD_TIMER_EXPIRED:
|
||||
/* Go back to search mode */
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
|
||||
ircomm_tty_start_watchdog_timer(self, 3*HZ);
|
||||
break;
|
||||
case IRCOMM_TTY_CONNECT_INDICATION:
|
||||
del_timer(&self->watchdog_timer);
|
||||
ircomm_tty_ias_unregister(self);
|
||||
|
||||
/* Accept connection */
|
||||
ircomm_connect_response(self->ircomm, NULL);
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
|
||||
break;
|
||||
case IRCOMM_TTY_DETACH_CABLE:
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s(), unknown event: %s\n", __func__ ,
|
||||
ircomm_tty_event[event]);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_state_setup (self, event, skb, info)
|
||||
*
|
||||
* Trying to connect
|
||||
*
|
||||
*/
|
||||
static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
|
||||
IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb,
|
||||
struct ircomm_tty_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s: state=%s, event=%s\n", __func__ ,
|
||||
ircomm_tty_state[self->state], ircomm_tty_event[event]);
|
||||
|
||||
switch (event) {
|
||||
case IRCOMM_TTY_CONNECT_CONFIRM:
|
||||
del_timer(&self->watchdog_timer);
|
||||
ircomm_tty_ias_unregister(self);
|
||||
|
||||
/*
|
||||
* Send initial parameters. This will also send out queued
|
||||
* parameters waiting for the connection to come up
|
||||
*/
|
||||
ircomm_tty_send_initial_parameters(self);
|
||||
ircomm_tty_link_established(self);
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
|
||||
break;
|
||||
case IRCOMM_TTY_CONNECT_INDICATION:
|
||||
del_timer(&self->watchdog_timer);
|
||||
ircomm_tty_ias_unregister(self);
|
||||
|
||||
/* Accept connection */
|
||||
ircomm_connect_response(self->ircomm, NULL);
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
|
||||
break;
|
||||
case IRCOMM_TTY_WD_TIMER_EXPIRED:
|
||||
/* Go back to search mode */
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
|
||||
ircomm_tty_start_watchdog_timer(self, 3*HZ);
|
||||
break;
|
||||
case IRCOMM_TTY_DETACH_CABLE:
|
||||
/* ircomm_disconnect_request(self->ircomm, NULL); */
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s(), unknown event: %s\n", __func__ ,
|
||||
ircomm_tty_event[event]);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_state_ready (self, event, skb, info)
|
||||
*
|
||||
* IrCOMM is now connected
|
||||
*
|
||||
*/
|
||||
static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
|
||||
IRCOMM_TTY_EVENT event,
|
||||
struct sk_buff *skb,
|
||||
struct ircomm_tty_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
case IRCOMM_TTY_DATA_REQUEST:
|
||||
ret = ircomm_data_request(self->ircomm, skb);
|
||||
break;
|
||||
case IRCOMM_TTY_DETACH_CABLE:
|
||||
ircomm_disconnect_request(self->ircomm, NULL);
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
|
||||
break;
|
||||
case IRCOMM_TTY_DISCONNECT_INDICATION:
|
||||
ircomm_tty_ias_register(self);
|
||||
ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
|
||||
ircomm_tty_start_watchdog_timer(self, 3*HZ);
|
||||
|
||||
if (tty_port_check_carrier(&self->port)) {
|
||||
/* Drop carrier */
|
||||
self->settings.dce = IRCOMM_DELTA_CD;
|
||||
ircomm_tty_check_modem_status(self);
|
||||
} else {
|
||||
pr_debug("%s(), hanging up!\n", __func__);
|
||||
tty_port_tty_hangup(&self->port, false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s(), unknown event: %s\n", __func__ ,
|
||||
ircomm_tty_event[event]);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1,291 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: ircomm_tty_ioctl.c
|
||||
* Version:
|
||||
* Description:
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Thu Jun 10 14:39:09 1999
|
||||
* Modified at: Wed Jan 5 14:45:43 2000
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/termios.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/serial.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irmod.h>
|
||||
|
||||
#include <net/irda/ircomm_core.h>
|
||||
#include <net/irda/ircomm_param.h>
|
||||
#include <net/irda/ircomm_tty_attach.h>
|
||||
#include <net/irda/ircomm_tty.h>
|
||||
|
||||
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_change_speed (driver)
|
||||
*
|
||||
* Change speed of the driver. If the remote device is a DCE, then this
|
||||
* should make it change the speed of its serial port
|
||||
*/
|
||||
static void ircomm_tty_change_speed(struct ircomm_tty_cb *self,
|
||||
struct tty_struct *tty)
|
||||
{
|
||||
unsigned int cflag, cval;
|
||||
int baud;
|
||||
|
||||
if (!self->ircomm)
|
||||
return;
|
||||
|
||||
cflag = tty->termios.c_cflag;
|
||||
|
||||
/* byte size and parity */
|
||||
switch (cflag & CSIZE) {
|
||||
case CS5: cval = IRCOMM_WSIZE_5; break;
|
||||
case CS6: cval = IRCOMM_WSIZE_6; break;
|
||||
case CS7: cval = IRCOMM_WSIZE_7; break;
|
||||
case CS8: cval = IRCOMM_WSIZE_8; break;
|
||||
default: cval = IRCOMM_WSIZE_5; break;
|
||||
}
|
||||
if (cflag & CSTOPB)
|
||||
cval |= IRCOMM_2_STOP_BIT;
|
||||
|
||||
if (cflag & PARENB)
|
||||
cval |= IRCOMM_PARITY_ENABLE;
|
||||
if (!(cflag & PARODD))
|
||||
cval |= IRCOMM_PARITY_EVEN;
|
||||
|
||||
/* Determine divisor based on baud rate */
|
||||
baud = tty_get_baud_rate(tty);
|
||||
if (!baud)
|
||||
baud = 9600; /* B0 transition handled in rs_set_termios */
|
||||
|
||||
self->settings.data_rate = baud;
|
||||
ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE);
|
||||
|
||||
/* CTS flow control flag and modem status interrupts */
|
||||
tty_port_set_cts_flow(&self->port, cflag & CRTSCTS);
|
||||
if (cflag & CRTSCTS) {
|
||||
self->settings.flow_control |= IRCOMM_RTS_CTS_IN;
|
||||
/* This got me. Bummer. Jean II */
|
||||
if (self->service_type == IRCOMM_3_WIRE_RAW)
|
||||
net_warn_ratelimited("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n",
|
||||
__func__);
|
||||
} else {
|
||||
self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN;
|
||||
}
|
||||
tty_port_set_check_carrier(&self->port, ~cflag & CLOCAL);
|
||||
|
||||
self->settings.data_format = cval;
|
||||
|
||||
ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
|
||||
ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_set_termios (tty, old_termios)
|
||||
*
|
||||
* This routine allows the tty driver to be notified when device's
|
||||
* termios settings have changed. Note that a well-designed tty driver
|
||||
* should be prepared to accept the case where old == NULL, and try to
|
||||
* do something rational.
|
||||
*/
|
||||
void ircomm_tty_set_termios(struct tty_struct *tty,
|
||||
struct ktermios *old_termios)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
|
||||
unsigned int cflag = tty->termios.c_cflag;
|
||||
|
||||
if ((cflag == old_termios->c_cflag) &&
|
||||
(RELEVANT_IFLAG(tty->termios.c_iflag) ==
|
||||
RELEVANT_IFLAG(old_termios->c_iflag)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ircomm_tty_change_speed(self, tty);
|
||||
|
||||
/* Handle transition to B0 status */
|
||||
if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) {
|
||||
self->settings.dte &= ~(IRCOMM_DTR|IRCOMM_RTS);
|
||||
ircomm_param_request(self, IRCOMM_DTE, TRUE);
|
||||
}
|
||||
|
||||
/* Handle transition away from B0 status */
|
||||
if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
|
||||
self->settings.dte |= IRCOMM_DTR;
|
||||
if (!C_CRTSCTS(tty) || !tty_throttled(tty))
|
||||
self->settings.dte |= IRCOMM_RTS;
|
||||
ircomm_param_request(self, IRCOMM_DTE, TRUE);
|
||||
}
|
||||
|
||||
/* Handle turning off CRTSCTS */
|
||||
if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty))
|
||||
{
|
||||
tty->hw_stopped = 0;
|
||||
ircomm_tty_start(tty);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_tiocmget (tty)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
int ircomm_tty_tiocmget(struct tty_struct *tty)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
|
||||
unsigned int result;
|
||||
|
||||
if (tty_io_error(tty))
|
||||
return -EIO;
|
||||
|
||||
result = ((self->settings.dte & IRCOMM_RTS) ? TIOCM_RTS : 0)
|
||||
| ((self->settings.dte & IRCOMM_DTR) ? TIOCM_DTR : 0)
|
||||
| ((self->settings.dce & IRCOMM_CD) ? TIOCM_CAR : 0)
|
||||
| ((self->settings.dce & IRCOMM_RI) ? TIOCM_RNG : 0)
|
||||
| ((self->settings.dce & IRCOMM_DSR) ? TIOCM_DSR : 0)
|
||||
| ((self->settings.dce & IRCOMM_CTS) ? TIOCM_CTS : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_tiocmset (tty, set, clear)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
int ircomm_tty_tiocmset(struct tty_struct *tty,
|
||||
unsigned int set, unsigned int clear)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
|
||||
|
||||
if (tty_io_error(tty))
|
||||
return -EIO;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
if (set & TIOCM_RTS)
|
||||
self->settings.dte |= IRCOMM_RTS;
|
||||
if (set & TIOCM_DTR)
|
||||
self->settings.dte |= IRCOMM_DTR;
|
||||
|
||||
if (clear & TIOCM_RTS)
|
||||
self->settings.dte &= ~IRCOMM_RTS;
|
||||
if (clear & TIOCM_DTR)
|
||||
self->settings.dte &= ~IRCOMM_DTR;
|
||||
|
||||
if ((set|clear) & TIOCM_RTS)
|
||||
self->settings.dte |= IRCOMM_DELTA_RTS;
|
||||
if ((set|clear) & TIOCM_DTR)
|
||||
self->settings.dte |= IRCOMM_DELTA_DTR;
|
||||
|
||||
ircomm_param_request(self, IRCOMM_DTE, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function get_serial_info (driver, retinfo)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self,
|
||||
struct serial_struct __user *retinfo)
|
||||
{
|
||||
struct serial_struct info;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.line = self->line;
|
||||
info.flags = self->port.flags;
|
||||
info.baud_base = self->settings.data_rate;
|
||||
info.close_delay = self->port.close_delay;
|
||||
info.closing_wait = self->port.closing_wait;
|
||||
|
||||
/* For compatibility */
|
||||
info.type = PORT_16550A;
|
||||
|
||||
if (copy_to_user(retinfo, &info, sizeof(*retinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function set_serial_info (driver, new_info)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int ircomm_tty_set_serial_info(struct ircomm_tty_cb *self,
|
||||
struct serial_struct __user *new_info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_ioctl (tty, cmd, arg)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
int ircomm_tty_ioctl(struct tty_struct *tty,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
|
||||
int ret = 0;
|
||||
|
||||
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
|
||||
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
|
||||
(cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
|
||||
if (tty_io_error(tty))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case TIOCGSERIAL:
|
||||
ret = ircomm_tty_get_serial_info(self, (struct serial_struct __user *) arg);
|
||||
break;
|
||||
case TIOCSSERIAL:
|
||||
ret = ircomm_tty_set_serial_info(self, (struct serial_struct __user *) arg);
|
||||
break;
|
||||
case TIOCMIWAIT:
|
||||
pr_debug("(), TIOCMIWAIT, not impl!\n");
|
||||
break;
|
||||
|
||||
case TIOCGICOUNT:
|
||||
pr_debug("%s(), TIOCGICOUNT not impl!\n", __func__);
|
||||
return 0;
|
||||
default:
|
||||
ret = -ENOIOCTLCMD; /* ioctls which we must ignore */
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,316 +0,0 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* Filename: irda_device.c
|
||||
* Version: 0.9
|
||||
* Description: Utility functions used by the device drivers
|
||||
* Status: Experimental.
|
||||
* Author: Dag Brattli <dagb@cs.uit.no>
|
||||
* Created at: Sat Oct 9 09:22:27 1999
|
||||
* Modified at: Sun Jan 23 17:41:24 2000
|
||||
* Modified by: Dag Brattli <dagb@cs.uit.no>
|
||||
*
|
||||
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
|
||||
* Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.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.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <asm/ioctls.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <net/irda/irda_device.h>
|
||||
#include <net/irda/irlap.h>
|
||||
#include <net/irda/timer.h>
|
||||
#include <net/irda/wrapper.h>
|
||||
|
||||
static void __irda_task_delete(struct irda_task *task);
|
||||
|
||||
static hashbin_t *dongles;
|
||||
static hashbin_t *tasks;
|
||||
|
||||
static void irda_task_timer_expired(struct timer_list *timer);
|
||||
|
||||
int __init irda_device_init(void)
|
||||
{
|
||||
dongles = hashbin_new(HB_NOLOCK);
|
||||
if (!dongles) {
|
||||
net_warn_ratelimited("IrDA: Can't allocate dongles hashbin!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
spin_lock_init(&dongles->hb_spinlock);
|
||||
|
||||
tasks = hashbin_new(HB_LOCK);
|
||||
if (!tasks) {
|
||||
net_warn_ratelimited("IrDA: Can't allocate tasks hashbin!\n");
|
||||
hashbin_delete(dongles, NULL);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* We no longer initialise the driver ourselves here, we let
|
||||
* the system do it for us... - Jean II
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void leftover_dongle(void *arg)
|
||||
{
|
||||
struct dongle_reg *reg = arg;
|
||||
|
||||
net_warn_ratelimited("IrDA: Dongle type %x not unregistered\n",
|
||||
reg->type);
|
||||
}
|
||||
|
||||
void irda_device_cleanup(void)
|
||||
{
|
||||
hashbin_delete(tasks, (FREE_FUNC) __irda_task_delete);
|
||||
|
||||
hashbin_delete(dongles, leftover_dongle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function irda_device_set_media_busy (self, status)
|
||||
*
|
||||
* Called when we have detected that another station is transmitting
|
||||
* in contention mode.
|
||||
*/
|
||||
void irda_device_set_media_busy(struct net_device *dev, int status)
|
||||
{
|
||||
struct irlap_cb *self;
|
||||
|
||||
pr_debug("%s(%s)\n", __func__, status ? "TRUE" : "FALSE");
|
||||
|
||||
self = (struct irlap_cb *)dev->atalk_ptr;
|
||||
|
||||
/* Some drivers may enable the receive interrupt before calling
|
||||
* irlap_open(), or they may disable the receive interrupt
|
||||
* after calling irlap_close().
|
||||
* The IrDA stack is protected from this in irlap_driver_rcv().
|
||||
* However, the driver calls directly the wrapper, that calls
|
||||
* us directly. Make sure we protect ourselves.
|
||||
* Jean II
|
||||
*/
|
||||
if (!self || self->magic != LAP_MAGIC)
|
||||
return;
|
||||
|
||||
if (status) {
|
||||
self->media_busy = TRUE;
|
||||
if (status == SMALL)
|
||||
irlap_start_mbusy_timer(self, SMALLBUSY_TIMEOUT);
|
||||
else
|
||||
irlap_start_mbusy_timer(self, MEDIABUSY_TIMEOUT);
|
||||
pr_debug("Media busy!\n");
|
||||
} else {
|
||||
self->media_busy = FALSE;
|
||||
irlap_stop_mbusy_timer(self);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(irda_device_set_media_busy);
|
||||
|
||||
/*
|
||||
* Function irda_device_is_receiving (dev)
|
||||
*
|
||||
* Check if the device driver is currently receiving data
|
||||
*
|
||||
*/
|
||||
int irda_device_is_receiving(struct net_device *dev)
|
||||
{
|
||||
struct if_irda_req req;
|
||||
int ret;
|
||||
|
||||
if (!dev->netdev_ops->ndo_do_ioctl) {
|
||||
net_err_ratelimited("%s: do_ioctl not impl. by device driver\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = (dev->netdev_ops->ndo_do_ioctl)(dev, (struct ifreq *) &req,
|
||||
SIOCGRECEIVING);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return req.ifr_receiving;
|
||||
}
|
||||
|
||||
static void __irda_task_delete(struct irda_task *task)
|
||||
{
|
||||
del_timer(&task->timer);
|
||||
|
||||
kfree(task);
|
||||
}
|
||||
|
||||
static void irda_task_delete(struct irda_task *task)
|
||||
{
|
||||
/* Unregister task */
|
||||
hashbin_remove(tasks, (long)task, NULL);
|
||||
|
||||
__irda_task_delete(task);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function irda_task_kick (task)
|
||||
*
|
||||
* Tries to execute a task possible multiple times until the task is either
|
||||
* finished, or askes for a timeout. When a task is finished, we do post
|
||||
* processing, and notify the parent task, that is waiting for this task
|
||||
* to complete.
|
||||
*/
|
||||
static int irda_task_kick(struct irda_task *task)
|
||||
{
|
||||
int finished = TRUE;
|
||||
int count = 0;
|
||||
int timeout;
|
||||
|
||||
IRDA_ASSERT(task != NULL, return -1;);
|
||||
IRDA_ASSERT(task->magic == IRDA_TASK_MAGIC, return -1;);
|
||||
|
||||
/* Execute task until it's finished, or askes for a timeout */
|
||||
do {
|
||||
timeout = task->function(task);
|
||||
if (count++ > 100) {
|
||||
net_err_ratelimited("%s: error in task handler!\n",
|
||||
__func__);
|
||||
irda_task_delete(task);
|
||||
return TRUE;
|
||||
}
|
||||
} while ((timeout == 0) && (task->state != IRDA_TASK_DONE));
|
||||
|
||||
if (timeout < 0) {
|
||||
net_err_ratelimited("%s: Error executing task!\n", __func__);
|
||||
irda_task_delete(task);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Check if we are finished */
|
||||
if (task->state == IRDA_TASK_DONE) {
|
||||
del_timer(&task->timer);
|
||||
|
||||
/* Do post processing */
|
||||
if (task->finished)
|
||||
task->finished(task);
|
||||
|
||||
/* Notify parent */
|
||||
if (task->parent) {
|
||||
/* Check if parent is waiting for us to complete */
|
||||
if (task->parent->state == IRDA_TASK_CHILD_WAIT) {
|
||||
task->parent->state = IRDA_TASK_CHILD_DONE;
|
||||
|
||||
/* Stop timer now that we are here */
|
||||
del_timer(&task->parent->timer);
|
||||
|
||||
/* Kick parent task */
|
||||
irda_task_kick(task->parent);
|
||||
}
|
||||
}
|
||||
irda_task_delete(task);
|
||||
} else if (timeout > 0) {
|
||||
irda_start_timer(&task->timer, timeout,
|
||||
irda_task_timer_expired);
|
||||
finished = FALSE;
|
||||
} else {
|
||||
pr_debug("%s(), not finished, and no timeout!\n",
|
||||
__func__);
|
||||
finished = FALSE;
|
||||
}
|
||||
|
||||
return finished;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function irda_task_timer_expired (data)
|
||||
*
|
||||
* Task time has expired. We now try to execute task (again), and restart
|
||||
* the timer if the task has not finished yet
|
||||
*/
|
||||
static void irda_task_timer_expired(struct timer_list *t)
|
||||
{
|
||||
struct irda_task *task = from_timer(task, t, timer);
|
||||
|
||||
irda_task_kick(task);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function irda_device_setup (dev)
|
||||
*
|
||||
* This function should be used by low level device drivers in a similar way
|
||||
* as ether_setup() is used by normal network device drivers
|
||||
*/
|
||||
static void irda_device_setup(struct net_device *dev)
|
||||
{
|
||||
dev->hard_header_len = 0;
|
||||
dev->addr_len = LAP_ALEN;
|
||||
|
||||
dev->type = ARPHRD_IRDA;
|
||||
dev->tx_queue_len = 8; /* Window size + 1 s-frame */
|
||||
|
||||
memset(dev->broadcast, 0xff, LAP_ALEN);
|
||||
|
||||
dev->mtu = 2048;
|
||||
dev->flags = IFF_NOARP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Funciton alloc_irdadev
|
||||
* Allocates and sets up an IRDA device in a manner similar to
|
||||
* alloc_etherdev.
|
||||
*/
|
||||
struct net_device *alloc_irdadev(int sizeof_priv)
|
||||
{
|
||||
return alloc_netdev(sizeof_priv, "irda%d", NET_NAME_UNKNOWN,
|
||||
irda_device_setup);
|
||||
}
|
||||
EXPORT_SYMBOL(alloc_irdadev);
|
||||
|
||||
#ifdef CONFIG_ISA_DMA_API
|
||||
/*
|
||||
* Function setup_dma (idev, buffer, count, mode)
|
||||
*
|
||||
* Setup the DMA channel. Commonly used by LPC FIR drivers
|
||||
*
|
||||
*/
|
||||
void irda_setup_dma(int channel, dma_addr_t buffer, int count, int mode)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
flags = claim_dma_lock();
|
||||
|
||||
disable_dma(channel);
|
||||
clear_dma_ff(channel);
|
||||
set_dma_mode(channel, mode);
|
||||
set_dma_addr(channel, buffer);
|
||||
set_dma_count(channel, count);
|
||||
enable_dma(channel);
|
||||
|
||||
release_dma_lock(flags);
|
||||
}
|
||||
EXPORT_SYMBOL(irda_setup_dma);
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue